diff --git a/config_app/config_endpoints/api/kubeconfig.py b/config_app/config_endpoints/api/kubeconfig.py index e06169c1b..e63c3d99e 100644 --- a/config_app/config_endpoints/api/kubeconfig.py +++ b/config_app/config_endpoints/api/kubeconfig.py @@ -42,7 +42,7 @@ class QEDeploymentRolloutStatus(ApiResource): @kubernetes_only @nickname('scGetDeploymentRolloutStatus') def get(self, deployment): - return KubernetesAccessorSingleton.get_instance().get_deployment_rollout_status(deployment) + return KubernetesAccessorSingleton.get_instance().get_deployment_rollout_status(deployment).to_dict() @resource('/v1/superuser/config/kubernetes') diff --git a/config_app/config_util/k8saccessor.py b/config_app/config_util/k8saccessor.py index df9796c1f..0ce67953d 100644 --- a/config_app/config_util/k8saccessor.py +++ b/config_app/config_util/k8saccessor.py @@ -13,6 +13,22 @@ logger = logging.getLogger(__name__) QE_DEPLOYMENT_LABEL = 'quay-enterprise-component' +class _DeploymentRolloutStatus: + """ + Class containing response of the deployment rollout status method. + status is one of: 'failed' | 'progressing' | 'available' + message is any string describing the state. + """ + def __init__(self, status, message): + self.status = status + self.message = message + + def to_dict(self): + return { + 'status': self.status, + 'message': self.message + } + class KubernetesAccessorSingleton(object): """ Singleton allowing access to kubernetes operations """ @@ -98,11 +114,8 @@ class KubernetesAccessorSingleton(object): 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': - } + Returns the status of a rollout of a given deployment + :return _DeploymentRolloutStatus """ deployment_selector_url = 'namespaces/%s/deployments/%s' % ( self.kube_config.qe_namespace, deployment_name @@ -110,7 +123,7 @@ class KubernetesAccessorSingleton(object): response = self._execute_k8s_api('GET', deployment_selector_url, api_prefix='apis/apps/v1') if response.status_code != 200: - return None + return _DeploymentRolloutStatus('failed', 'Could not get deployment. Please check that the deployment exists') deployment = json.loads(response.text) # Logic for rollout status pulled from the `kubectl rollout status` command: @@ -118,42 +131,47 @@ class KubernetesAccessorSingleton(object): 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 - } + return _DeploymentRolloutStatus( + 'failed', + 'Deployment %s\'s rollout failed. Please try again later.' % deployment_name + ) desired_replicas = deployment['spec']['replicas'] - current_replicas = deployment['status']['replicas'] + current_replicas = deployment['status'].get('replicas', 0) + if current_replicas == 0: + return _DeploymentRolloutStatus( + 'available', + 'Deployment %s updated (no replicas, so nothing to roll out)' + ) # 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) - } + return _DeploymentRolloutStatus( + 'progressing', + '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) - } + return _DeploymentRolloutStatus( + 'progressing', + '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 _DeploymentRolloutStatus( + 'progressing', + '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 _DeploymentRolloutStatus( + 'available', + 'Deployment %s successfully rolled out.' % deployment_name + ) - return { - 'status': 'progressing', - 'message': 'Waiting for deployment spec to be updated...' - } + return _DeploymentRolloutStatus( + 'progressing', + 'Waiting for deployment spec to be updated...' + ) def get_qe_deployments(self): """" @@ -187,10 +205,11 @@ class KubernetesAccessorSingleton(object): 'containers': [{ # Note: this name MUST match the deployment template's pod template # (e.g.