From ecc38094077da21c7cb7c84597710945fbc01788 Mon Sep 17 00:00:00 2001 From: Jason Luo Date: Sun, 10 Mar 2019 00:36:25 +0000 Subject: add liveness probe, fix readiness prob exec liveness and readiness probes may run script with arguments, ports and volumes are optional instead of mandatory, test_tasks.py accept ports[] and volumes[] fix the issue of labels which are logner than 63 Issue-ID: DCAEGEN2-1126 Change-Id: Id2f893adc300bf508c0512a51b3665872d36f674 Signed-off-by: Jason Luo --- k8s/ChangeLog.md | 5 +++++ k8s/k8s-node-type.yaml | 2 +- k8s/k8sclient/k8sclient.py | 23 ++++++++++++++++++----- k8s/k8splugin/tasks.py | 26 ++++++++++++++++---------- k8s/setup.py | 2 +- k8s/tests/test_tasks.py | 4 ++-- 6 files changed, 43 insertions(+), 19 deletions(-) diff --git a/k8s/ChangeLog.md b/k8s/ChangeLog.md index 3402581..a59a016 100644 --- a/k8s/ChangeLog.md +++ b/k8s/ChangeLog.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [1.4.9] +* Support for liveness probes (https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/) +* fix the readiness probe to run script such as "/opt/app/snmptrap/bin/snmptrapd.sh status" +* change "ports" and the "mode" of volume to be optional instead of mandatory + ## [1.4.8] * If an installation step times out because a component does not become ready within the maximum wait time, delete the Kubernetes artifacts associated with the component. Previously, an installation step might time diff --git a/k8s/k8s-node-type.yaml b/k8s/k8s-node-type.yaml index 13798f4..c803b81 100644 --- a/k8s/k8s-node-type.yaml +++ b/k8s/k8s-node-type.yaml @@ -25,7 +25,7 @@ plugins: k8s: executor: 'central_deployment_agent' package_name: k8splugin - package_version: 1.4.8 + package_version: 1.4.9 data_types: diff --git a/k8s/k8sclient/k8sclient.py b/k8s/k8sclient/k8sclient.py index 31631ad..d3417a7 100644 --- a/k8s/k8sclient/k8sclient.py +++ b/k8s/k8sclient/k8sclient.py @@ -116,7 +116,7 @@ def _create_probe(hc, port, use_tls=False): period_seconds = period, timeout_seconds = timeout, _exec = client.V1ExecAction( - command = [hc['script']] + command = hc['script'].split( ) ) ) return probe @@ -131,7 +131,7 @@ def _create_resources(resources=None): else: return None -def _create_container_object(name, image, always_pull, use_tls=False, env={}, container_ports=[], volume_mounts = [], resources = None, readiness = None): +def _create_container_object(name, image, always_pull, use_tls=False, env={}, container_ports=[], volume_mounts = [], resources = None, readiness = None, liveness = None): # Set up environment variables # Copy any passed in environment variables env_vars = [client.V1EnvVar(name=k, value=env[k]) for k in env.keys()] @@ -139,15 +139,21 @@ def _create_container_object(name, image, always_pull, use_tls=False, env={}, co pod_ip = client.V1EnvVarSource(field_ref = client.V1ObjectFieldSelector(field_path="status.podIP")) env_vars.append(client.V1EnvVar(name="POD_IP",value_from=pod_ip)) - # If a health check is specified, create a readiness probe + # If a health check is specified, create a readiness/liveness probe # (For an HTTP-based check, we assume it's at the first container port) probe = None + live_probe = None if readiness: hc_port = None if len(container_ports) > 0: (hc_port, proto) = container_ports[0] probe = _create_probe(readiness, hc_port, use_tls) + if liveness: + hc_port = None + if len(container_ports) > 0: + (hc_port, proto) = container_ports[0] + live_probe = _create_probe(liveness, hc_port, use_tls) if resources: resources_obj = _create_resources(resources) @@ -162,7 +168,8 @@ def _create_container_object(name, image, always_pull, use_tls=False, env={}, co ports=[client.V1ContainerPort(container_port=p, protocol=proto) for (p, proto) in container_ports], volume_mounts = volume_mounts, resources = resources_obj, - readiness_probe = probe + readiness_probe = probe, + liveness_probe = live_probe ) def _create_deployment_object(component_name, @@ -386,6 +393,12 @@ def deploy(namespace, component_name, image, replicas, always_pull, k8sconfig, r - timeout: time (in seconds) to allow a probe to complete - endpoint: the path portion of the URL that points to the readiness endpoint for "http" and "https" types - path: the full path to the script to be executed in the container for "script" and "docker" types + - liveness: dict with health check info; if present, used to create a liveness probe for the main container. Includes: + - type: check is done by making http(s) request to an endpoint ("http", "https") or by exec'ing a script in the container ("script", "docker") + - interval: period (in seconds) between probes + - timeout: time (in seconds) to allow a probe to complete + - endpoint: the path portion of the URL that points to the liveness endpoint for "http" and "https" types + - path: the full path to the script to be executed in the container for "script" and "docker" types ''' @@ -460,7 +473,7 @@ def deploy(namespace, component_name, image, replicas, always_pull, k8sconfig, r # Create the container for the component # Make it the first container in the pod - containers.insert(0, _create_container_object(component_name, image, always_pull, use_tls, kwargs.get("env", {}), container_ports, volume_mounts, resources, kwargs["readiness"])) + containers.insert(0, _create_container_object(component_name, image, always_pull, use_tls, kwargs.get("env", {}), container_ports, volume_mounts, resources, kwargs["readiness"], kwargs.get("liveness"))) # Build the k8s Deployment object labels = kwargs.get("labels", {}) diff --git a/k8s/k8splugin/tasks.py b/k8s/k8splugin/tasks.py index 727be78..399bc9f 100644 --- a/k8s/k8splugin/tasks.py +++ b/k8s/k8splugin/tasks.py @@ -286,6 +286,7 @@ def _create_and_start_container(container_name, image, **kwargs): {"log_directory": "/path/to/container/log/directory", "alternate_fb_path" : "/alternate/sidecar/log/path"}" - replicas: number of replicas to be launched initially - readiness: object with information needed to create a readiness check + - liveness: object with information needed to create a liveness check ''' env = { "CONSUL_HOST": CONSUL_INTERNAL_NAME, "CONFIG_BINDING_SERVICE": "config-binding-service" } @@ -308,7 +309,8 @@ def _create_and_start_container(container_name, image, **kwargs): env = env, labels = kwargs.get("labels", {}), log_info=kwargs.get("log_info"), - readiness=kwargs.get("readiness")) + readiness=kwargs.get("readiness"), + liveness=kwargs.get("liveness")) # Capture the result of deployment for future use ctx.instance.runtime_properties[K8S_DEPLOYMENT] = dep @@ -327,8 +329,8 @@ def _parse_cloudify_context(**kwargs): # Set some labels for the Kubernetes pods kwargs["labels"] = { "cfydeployment" : ctx.deployment.id, - "cfynode": ctx.node.name, - "cfynodeinstance": ctx.instance.id + "cfynode": ctx.node.name[:63], + "cfynodeinstance": ctx.instance.id[:63] } # Pick up the centralized logging info @@ -349,14 +351,16 @@ def _parse_cloudify_context(**kwargs): def _enhance_docker_params(**kwargs): ''' - Set up Docker environment variables and readiness check info + Set up Docker environment variables and readiness/liveness check info and inject into kwargs. ''' - # Get info for setting up readiness probe, if present + # Get info for setting up readiness/liveness probe, if present docker_config = kwargs.get("docker_config", {}) if "healthcheck" in docker_config: kwargs["readiness"] = docker_config["healthcheck"] + if "livehealthcheck" in docker_config: + kwargs["liveness"] = docker_config["livehealthcheck"] envs = kwargs.get("envs", {}) @@ -371,8 +375,7 @@ def _enhance_docker_params(**kwargs): def combine_params(key, docker_config, kwargs): v = docker_config.get(key, []) + kwargs.get(key, []) - if v: - kwargs[key] = v + kwargs[key] = v return kwargs # Add the lists of ports and volumes unintelligently - meaning just add the @@ -398,7 +401,8 @@ def _create_and_start_component(**kwargs): "tls_info": kwargs.get("tls_info", {}), "labels": kwargs.get("labels", {}), "resource_config": kwargs.get("resource_config",{}), - "readiness": kwargs.get("readiness",{})} + "readiness": kwargs.get("readiness",{}), + "liveness": kwargs.get("liveness",{})} returned_args = _create_and_start_container(service_component_name, image, **sub_kwargs) kwargs[K8S_DEPLOYMENT] = returned_args[K8S_DEPLOYMENT] @@ -518,6 +522,8 @@ def create_and_start_container_for_platforms(**kwargs): kwargs["resource_config"] = resource_config if "healthcheck" in docker_config: kwargs["readiness"] = docker_config["healthcheck"] + if "livehealthcheck" in docker_config: + kwargs["liveness"] = docker_config["livehealthcheck"] if "dns_name" in ctx.node.properties: service_component_name = ctx.node.properties["dns_name"] else: @@ -526,8 +532,8 @@ def create_and_start_container_for_platforms(**kwargs): # Set some labels for the Kubernetes pods kwargs["labels"] = { "cfydeployment" : ctx.deployment.id, - "cfynode": ctx.node.name, - "cfynodeinstance": ctx.instance.id + "cfynode": ctx.node.name[:63], + "cfynodeinstance": ctx.instance.id[:63] } host_port = ctx.node.properties["host_port"] diff --git a/k8s/setup.py b/k8s/setup.py index 3d94c96..a64efc8 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.8", + version="1.4.9", author='J. F. Lucas, Michael Hwang, Tommy Carpenter', packages=['k8splugin','k8sclient','msb','configure'], zip_safe=False, diff --git a/k8s/tests/test_tasks.py b/k8s/tests/test_tasks.py index 948489a..cf78860 100644 --- a/k8s/tests/test_tasks.py +++ b/k8s/tests/test_tasks.py @@ -247,7 +247,7 @@ def test_enhance_docker_params(mockconfig): test_kwargs = { "docker_config": {}, "service_id": None } actual = tasks._enhance_docker_params(**test_kwargs) - assert actual == {'envs': {"SERVICE_TAGS": ""}, 'docker_config': {}, "service_id": None } + assert actual == {'envs': {"SERVICE_TAGS": ""}, 'docker_config': {}, 'ports': [], 'volumes': [], "service_id": None } # Good - Test just docker config ports and volumes @@ -289,4 +289,4 @@ def test_notify_container(mockconfig): from k8splugin import tasks test_input = { "docker_config": { "policy": { "trigger_type": "unknown" } } } - assert [] == tasks._notify_container(**test_input) \ No newline at end of file + assert [] == tasks._notify_container(**test_input) -- cgit 1.2.3-korg