diff options
-rw-r--r-- | k8s/.coveragerc | 21 | ||||
-rw-r--r-- | k8s/LICENSE.txt | 2 | ||||
-rw-r--r-- | k8s/k8s-node-type.yaml | 8 | ||||
-rw-r--r-- | k8s/k8sclient/k8sclient.py | 6 | ||||
-rw-r--r-- | k8s/pom.xml | 4 | ||||
-rw-r--r-- | k8s/setup.py | 2 | ||||
-rw-r--r-- | k8s/tests/test_k8sclient.py | 125 |
7 files changed, 154 insertions, 14 deletions
diff --git a/k8s/.coveragerc b/k8s/.coveragerc new file mode 100644 index 0000000..088c2da --- /dev/null +++ b/k8s/.coveragerc @@ -0,0 +1,21 @@ +# .coveragerc to control coverage.py +[run] +branch = True + +[report] +# Regexes for lines to exclude from consideration +exclude_lines = + # Have to re-enable the standard pragma + pragma: no cover + + # Don't complain about missing debug-only code: + def __repr__ + if self\.debug + + # Don't complain if tests don't hit defensive assertion code: + raise AssertionError + raise NotImplementedError + + # Don't complain if non-runnable code isn't run: + if 0: + if __name__ == .__main__.: diff --git a/k8s/LICENSE.txt b/k8s/LICENSE.txt index e266d0a..43098d1 100644 --- a/k8s/LICENSE.txt +++ b/k8s/LICENSE.txt @@ -1,7 +1,7 @@ ============LICENSE_START======================================================= org.onap.dcae ================================================================================ -Copyright (c) 2017-2018 AT&T Intellectual Property. All rights reserved. +Copyright (c) 2017-2019 AT&T Intellectual Property. All rights reserved. ================================================================================ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/k8s/k8s-node-type.yaml b/k8s/k8s-node-type.yaml index f63f822..166e430 100644 --- a/k8s/k8s-node-type.yaml +++ b/k8s/k8s-node-type.yaml @@ -19,13 +19,13 @@ tosca_definitions_version: cloudify_dsl_1_3 imports: - - http://www.getcloudify.org/spec/cloudify/4.2/types.yaml + - http://www.getcloudify.org/spec/cloudify/3.4/types.yaml plugins: k8s: executor: 'central_deployment_agent' package_name: k8splugin - package_version: 1.4.6 + package_version: 1.4.7 data_types: @@ -123,9 +123,9 @@ node_types: default: {} description: > This is used to specify the cpu and memory request and limit for container. - Please specify "requests" property and/or a "limits" property, with subproproperties + Please specify "requests" property and/or a "limits" property, with subproproperties for cpu and memory. (https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/) - + log_info: type: dcae.types.LoggingInfo diff --git a/k8s/k8sclient/k8sclient.py b/k8s/k8sclient/k8sclient.py index 84ca84f..31631ad 100644 --- a/k8s/k8sclient/k8sclient.py +++ b/k8s/k8sclient/k8sclient.py @@ -38,7 +38,7 @@ FACTORS = {None: 1, "s": 1, "m": 60, "h": 3600} # group 3: protocol # group 4: host port PORTS = re.compile("^([0-9]+)(/(udp|UDP|tcp|TCP))?:([0-9]+)$") - + def _create_deployment_name(component_name): return "dep-{0}".format(component_name) @@ -126,7 +126,7 @@ def _create_resources(resources=None): resources_obj = client.V1ResourceRequirements( limits = resources.get("limits"), requests = resources.get("requests") - ) + ) return resources_obj else: return None @@ -398,9 +398,9 @@ def deploy(namespace, component_name, image, replicas, always_pull, k8sconfig, r } try: - _configure_api() # Get API handles + _configure_api() core = client.CoreV1Api() ext = client.ExtensionsV1beta1Api() diff --git a/k8s/pom.xml b/k8s/pom.xml index 844f214..45c5a39 100644 --- a/k8s/pom.xml +++ b/k8s/pom.xml @@ -1,7 +1,7 @@ <?xml version="1.0"?> <!-- ================================================================================ -Copyright (c) 2017-2018 AT&T Intellectual Property. All rights reserved. +Copyright (c) 2017-2019 AT&T Intellectual Property. All rights reserved. ================================================================================ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ ECOMP is a trademark and service mark of AT&T Intellectual Property. <groupId>org.onap.dcaegen2.platform.plugins</groupId> <artifactId>k8s</artifactId> <name>k8s-plugin</name> - <version>1.4.5-SNAPSHOT</version> + <version>1.4.7-SNAPSHOT</version> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> diff --git a/k8s/setup.py b/k8s/setup.py index 36fbe05..9ad35bf 100644 --- a/k8s/setup.py +++ b/k8s/setup.py @@ -23,7 +23,7 @@ from setuptools import setup setup( name='k8splugin', description='Cloudify plugin for containerized components deployed using Kubernetes', - version="1.4.6", + version="1.4.7", author='J. F. Lucas, Michael Hwang, Tommy Carpenter', packages=['k8splugin','k8sclient','msb','configure'], zip_safe=False, diff --git a/k8s/tests/test_k8sclient.py b/k8s/tests/test_k8sclient.py index e985def..2511239 100644 --- a/k8s/tests/test_k8sclient.py +++ b/k8s/tests/test_k8sclient.py @@ -102,7 +102,7 @@ def test_parse_ports(): ("9101/udp:31043", (9101, 31043, "UDP")) ] ] - + bad_ports = [ "9101", "9101:", @@ -130,10 +130,10 @@ def test_parse_ports(): (9661,"TCP") : 19661, (9661,"UDP") : 19661, (8080,"TCP") : 8080 - } + } for test_case in good_ports: - container_ports, port_map = _parse_ports([test_case["in"]]) + container_ports, port_map = _parse_ports([test_case["in"]]) (cport, hport, proto) = test_case["ex"] assert container_ports == [(cport, proto)] assert port_map == {(cport, proto) : hport} @@ -153,3 +153,122 @@ def test_create_container(): assert container.ports[0].container_port == 80 and container.ports[0].protocol == "TCP" assert container.ports[1].container_port == 53 and container.ports[1].protocol == "UDP" + +def test_create_probe(): + from k8sclient.k8sclient import _create_probe + from kubernetes import client + + http_checks = [ + {"type" : "http", "endpoint" : "/example/health"} + ] + + script_checks = [ + {"type" : "docker", "script": "/opt/app/health_check.sh"} + ] + + for hc in http_checks: + probe = _create_probe(hc, 13131) + assert probe.http_get.path == hc["endpoint"] + assert probe.http_get.scheme == hc["type"].upper() + + for hc in script_checks: + probe = _create_probe(hc, 13131) + assert probe._exec.command[0] == hc["script"] + +def test_deploy(monkeypatch): + import k8sclient.k8sclient + from kubernetes import client + + # We need to patch the kubernetes 'client' module + # Awkward because of the way it requires a function call + # to get an API object + core = client.CoreV1Api() + ext = client.ExtensionsV1beta1Api() + + def pseudo_deploy(namespace, dep): + return dep + + def pseudo_service(namespace, svc): + return svc + + # patched_core returns a CoreV1Api object with the + # create_namespaced_service method stubbed out so that there + # is no attempt to call the k8s API server + def patched_core(): + monkeypatch.setattr(core, "create_namespaced_service", pseudo_service) + return core + + # patched_ext returns an ExtensionsV1beta1Api object with the + # create_namespaced_deployment method stubbed out so that there + # is no attempt to call the k8s API server + def patched_ext(): + monkeypatch.setattr(ext,"create_namespaced_deployment", pseudo_deploy) + return ext + + def pseudo_configure(): + pass + + monkeypatch.setattr(k8sclient.k8sclient,"_configure_api", pseudo_configure) + monkeypatch.setattr(client, "CoreV1Api", patched_core) + monkeypatch.setattr(client,"ExtensionsV1beta1Api", patched_ext) + + k8s_test_config = { + "image_pull_secrets" : ["secret0", "secret1"], + "filebeat" : { + "log_path": "/var/log/onap", + "data_path": "/usr/share/filebeat/data", + "config_path": "/usr/share/filebeat/filebeat.yml", + "config_subpath": "filebeat.yml", + "image" : "filebeat-repo/filebeat:latest", + "config_map" : "dcae-filebeat-configmap" + }, + "tls" : { + "cert_path": "/opt/certs", + "image": "tlsrepo/tls-init-container:1.2.3" + } + } + + resources = { + "limits": { + "cpu" : 0.5, + "memory" : "2Gi" + }, + "requests": { + "cpu" : 0.5, + "memory" : "2Gi" + } + } + + kwargs = { + "volumes": [ + {"host":{"path": "/path/on/host"}, "container":{"bind":"/path/on/container","mode":"rw"}} + ], + "ports": ["80:0", "443:0"], + "env": {"name0": "value0", "name1": "value1"}, + "log_info": {"log_directory": "/path/to/container/log/directory"}, + "tls_info": {"use_tls": True, "cert_directory": "/path/to/container/cert/directory" }, + "readiness": {"type": "http", "endpoint" : "/ready"} + } + dep, deployment_description = k8sclient.k8sclient.deploy("k8stest","testcomponent","example.com/testcomponent:1.4.3",1,False, k8s_test_config, resources, **kwargs) + + assert deployment_description["deployment"] == "dep-testcomponent" + assert deployment_description["namespace"] == "k8stest" + assert deployment_description["services"][0] == "testcomponent" + + # For unit test purposes, we want to make sure that the deployment object + # we're passing to the k8s API is correct + app_container = dep.spec.template.spec.containers[0] + assert app_container.image == "example.com/testcomponent:1.4.3" + assert app_container.image_pull_policy == "IfNotPresent" + assert len(app_container.ports) == 2 + assert app_container.ports[0].container_port == 80 + assert app_container.ports[1].container_port == 443 + assert app_container.readiness_probe.http_get.path == "/ready" + assert app_container.readiness_probe.http_get.scheme == "HTTP" + assert len(app_container.volume_mounts) == 3 + assert app_container.volume_mounts[0].mount_path == "/path/on/container" + assert app_container.volume_mounts[1].mount_path == "/path/to/container/log/directory" + assert app_container.volume_mounts[2].mount_path == "/path/to/container/cert/directory" + + # Needs to be correctly labeled so that the Service can find it + assert dep.spec.template.metadata.labels["app"] == "testcomponent" |