From d936d778dad8add39fcdd06199a90e3cd1b8d36f Mon Sep 17 00:00:00 2001 From: Sam Chow Date: Wed, 22 Aug 2018 16:32:14 -0400 Subject: [PATCH] Add Rollout status to kube config tool --- config_app/config_endpoints/api/kubeconfig.py | 7 +++ config_app/config_util/k8saccessor.py | 61 +++++++++++++++++++ .../config-tool-servicetoken-role.yml | 1 + .../kube-deploy-modal.component.html | 4 ++ .../kube-deploy-modal.component.ts | 50 +++++++++++++-- 5 files changed, 119 insertions(+), 4 deletions(-) diff --git a/config_app/config_endpoints/api/kubeconfig.py b/config_app/config_endpoints/api/kubeconfig.py index a2f9219c8..e06169c1b 100644 --- a/config_app/config_endpoints/api/kubeconfig.py +++ b/config_app/config_endpoints/api/kubeconfig.py @@ -37,6 +37,13 @@ class SuperUserKubernetesDeployment(ApiResource): deployment_names = request.get_json()['deploymentNames'] return KubernetesAccessorSingleton.get_instance().cycle_qe_deployments(deployment_names) +@resource('/v1/kubernetes/deployment//status') +class QEDeploymentRolloutStatus(ApiResource): + @kubernetes_only + @nickname('scGetDeploymentRolloutStatus') + def get(self, deployment): + return KubernetesAccessorSingleton.get_instance().get_deployment_rollout_status(deployment) + @resource('/v1/superuser/config/kubernetes') class SuperUserKubernetesConfiguration(ApiResource): diff --git a/config_app/config_util/k8saccessor.py b/config_app/config_util/k8saccessor.py index dd7f596e2..df9796c1f 100644 --- a/config_app/config_util/k8saccessor.py +++ b/config_app/config_util/k8saccessor.py @@ -96,6 +96,65 @@ class KubernetesAccessorSingleton(object): self._assert_success(self._execute_k8s_api('PUT', secret_url, secret)) + def get_deployment_rollout_status(self, deployment_name): + """" + Returns the status of a rollout of a given deployment in the form: + { + 'status': 'failed' | 'progressing' | 'available' + 'message': + } + """ + deployment_selector_url = 'namespaces/%s/deployments/%s' % ( + self.kube_config.qe_namespace, deployment_name + ) + + response = self._execute_k8s_api('GET', deployment_selector_url, api_prefix='apis/apps/v1') + if response.status_code != 200: + return None + deployment = json.loads(response.text) + + # Logic for rollout status pulled from the `kubectl rollout status` command: + # https://github.com/kubernetes/kubernetes/blob/d9ba19c751709c8608e09a0537eea98973f3a796/pkg/kubectl/rollout_status.go#L62 + if deployment['metadata']['generation'] <= deployment['status']['observedGeneration']: + for cond in deployment['status']['conditions']: + if cond['type'] == 'Progressing' and cond['reason'] == 'ProgressDeadlineExceeded': + return { + 'status': 'failed', + 'message': 'Deployment %s\'s rollout failed. Please try again later.' % deployment_name + } + + desired_replicas = deployment['spec']['replicas'] + current_replicas = deployment['status']['replicas'] + # Some fields are optional in the spec, so if they're omitted, replace with defaults that won't indicate a wrong status + available_replicas = deployment['status'].get('availableReplicas', 0) + updated_replicas = deployment['status'].get('updatedReplicas', 0) + + if updated_replicas < desired_replicas: + return { + 'status': 'progressing', + 'message': 'Waiting for rollout to finish: %d out of %d new replicas have been updated...' % (updated_replicas, desired_replicas) + } + if current_replicas > updated_replicas: + return { + 'status': 'progressing', + 'message': 'Waiting for rollout to finish: %d old replicas are pending termination...' % (current_replicas - updated_replicas) + } + if available_replicas < updated_replicas: + return { + 'status': 'progressing', + 'message': 'Waiting for rollout to finish: %d of %d updated replicas are available...' % (available_replicas, updated_replicas) + } + + return { + 'status': 'available', + 'message': 'Deployment %s successfully rolled out.' % deployment_name + } + + return { + 'status': 'progressing', + 'message': 'Waiting for deployment spec to be updated...' + } + def get_qe_deployments(self): """" Returns all deployments matching the label selector provided in the KubeConfig @@ -126,6 +185,8 @@ class KubernetesAccessorSingleton(object): 'template': { 'spec': { 'containers': [{ + # Note: this name MUST match the deployment template's pod template + # (e.g.