2018-08-29 18:29:15 +00:00
|
|
|
import logging
|
|
|
|
|
2018-08-27 21:05:53 +00:00
|
|
|
from flask import request, make_response
|
2018-08-20 17:47:10 +00:00
|
|
|
|
2018-08-27 21:05:53 +00:00
|
|
|
from config_app.config_util.config import get_config_as_kube_secret
|
2018-08-20 17:47:10 +00:00
|
|
|
from data.database import configure
|
|
|
|
|
|
|
|
from config_app.c_app import app, config_provider
|
2018-08-27 21:05:53 +00:00
|
|
|
from config_app.config_endpoints.api import resource, ApiResource, nickname, kubernetes_only, validate_json_request
|
|
|
|
from config_app.config_util.k8saccessor import KubernetesAccessorSingleton, K8sApiException
|
2018-08-20 17:47:10 +00:00
|
|
|
|
2018-08-29 18:29:15 +00:00
|
|
|
logger = logging.getLogger(__name__)
|
2018-08-20 17:47:10 +00:00
|
|
|
|
|
|
|
@resource('/v1/kubernetes/deployments/')
|
|
|
|
class SuperUserKubernetesDeployment(ApiResource):
|
|
|
|
""" Resource for the getting the status of Quay Enterprise deployments and cycling them """
|
|
|
|
schemas = {
|
|
|
|
'ValidateDeploymentNames': {
|
|
|
|
'type': 'object',
|
|
|
|
'description': 'Validates deployment names for cycling',
|
|
|
|
'required': [
|
|
|
|
'deploymentNames'
|
|
|
|
],
|
|
|
|
'properties': {
|
|
|
|
'deploymentNames': {
|
|
|
|
'type': 'array',
|
|
|
|
'description': 'The names of the deployments to cycle'
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@kubernetes_only
|
|
|
|
@nickname('scGetNumDeployments')
|
|
|
|
def get(self):
|
|
|
|
return KubernetesAccessorSingleton.get_instance().get_qe_deployments()
|
|
|
|
|
|
|
|
@kubernetes_only
|
2018-08-27 21:05:53 +00:00
|
|
|
@validate_json_request('ValidateDeploymentNames')
|
2018-08-20 17:47:10 +00:00
|
|
|
@nickname('scCycleQEDeployments')
|
|
|
|
def put(self):
|
|
|
|
deployment_names = request.get_json()['deploymentNames']
|
|
|
|
return KubernetesAccessorSingleton.get_instance().cycle_qe_deployments(deployment_names)
|
|
|
|
|
2018-08-27 21:05:53 +00:00
|
|
|
|
2018-08-22 20:32:14 +00:00
|
|
|
@resource('/v1/kubernetes/deployment/<deployment>/status')
|
|
|
|
class QEDeploymentRolloutStatus(ApiResource):
|
|
|
|
@kubernetes_only
|
|
|
|
@nickname('scGetDeploymentRolloutStatus')
|
|
|
|
def get(self, deployment):
|
2018-08-28 17:44:58 +00:00
|
|
|
deployment_rollout_status = KubernetesAccessorSingleton.get_instance().get_deployment_rollout_status(deployment)
|
|
|
|
return {
|
|
|
|
'status': deployment_rollout_status.status,
|
|
|
|
'message': deployment_rollout_status.message,
|
|
|
|
}
|
2018-08-22 20:32:14 +00:00
|
|
|
|
2018-08-20 17:47:10 +00:00
|
|
|
|
2018-08-27 21:05:53 +00:00
|
|
|
@resource('/v1/kubernetes/deployments/rollback')
|
|
|
|
class QEDeploymentRollback(ApiResource):
|
|
|
|
""" Resource for rolling back deployments """
|
|
|
|
schemas = {
|
|
|
|
'ValidateDeploymentNames': {
|
|
|
|
'type': 'object',
|
|
|
|
'description': 'Validates deployment names for rolling back',
|
|
|
|
'required': [
|
|
|
|
'deploymentNames'
|
|
|
|
],
|
|
|
|
'properties': {
|
|
|
|
'deploymentNames': {
|
|
|
|
'type': 'array',
|
|
|
|
'description': 'The names of the deployments to rollback'
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@kubernetes_only
|
|
|
|
@nickname('scRollbackDeployments')
|
|
|
|
@validate_json_request('ValidateDeploymentNames')
|
|
|
|
def post(self):
|
|
|
|
"""
|
|
|
|
Returns the config to its original state and rolls back deployments
|
|
|
|
:return:
|
|
|
|
"""
|
|
|
|
deployment_names = request.get_json()['deploymentNames']
|
|
|
|
|
|
|
|
# To roll back a deployment, we must do 2 things:
|
2018-08-29 18:29:15 +00:00
|
|
|
# 1. Roll back the config secret to its old value (discarding changes we made in this session)
|
2018-08-27 21:05:53 +00:00
|
|
|
# 2. Trigger a rollback to the previous revision, so that the pods will be restarted with
|
|
|
|
# the old config
|
|
|
|
old_secret = get_config_as_kube_secret(config_provider.get_old_config_dir())
|
|
|
|
kube_accessor = KubernetesAccessorSingleton.get_instance()
|
|
|
|
kube_accessor.replace_qe_secret(old_secret)
|
|
|
|
|
|
|
|
try:
|
|
|
|
for name in deployment_names:
|
|
|
|
kube_accessor.rollback_deployment(name)
|
|
|
|
except K8sApiException as e:
|
2018-08-29 18:29:15 +00:00
|
|
|
logger.exception('Failed to rollback deployment.')
|
|
|
|
return make_response(e.message, 503)
|
2018-08-27 21:05:53 +00:00
|
|
|
|
|
|
|
return make_response('Ok', 204)
|
|
|
|
|
|
|
|
|
|
|
|
@resource('/v1/kubernetes/config')
|
2018-08-20 17:47:10 +00:00
|
|
|
class SuperUserKubernetesConfiguration(ApiResource):
|
|
|
|
""" Resource for saving the config files to kubernetes secrets. """
|
|
|
|
|
|
|
|
@kubernetes_only
|
|
|
|
@nickname('scDeployConfiguration')
|
|
|
|
def post(self):
|
2018-08-27 21:05:53 +00:00
|
|
|
try:
|
|
|
|
new_secret = get_config_as_kube_secret(config_provider.get_config_dir_path())
|
|
|
|
KubernetesAccessorSingleton.get_instance().replace_qe_secret(new_secret)
|
|
|
|
except K8sApiException as e:
|
2018-08-29 18:29:15 +00:00
|
|
|
logger.exception('Failed to deploy qe config secret to kubernetes.')
|
|
|
|
return make_response(e.message, 503)
|
2018-08-27 21:05:53 +00:00
|
|
|
|
|
|
|
return make_response('Ok', 201)
|
2018-08-20 17:47:10 +00:00
|
|
|
|
|
|
|
|
|
|
|
@resource('/v1/kubernetes/config/populate')
|
|
|
|
class KubernetesConfigurationPopulator(ApiResource):
|
|
|
|
""" Resource for populating the local configuration from the cluster's kubernetes secrets. """
|
|
|
|
|
|
|
|
@kubernetes_only
|
|
|
|
@nickname('scKubePopulateConfig')
|
|
|
|
def post(self):
|
|
|
|
# Get a clean transient directory to write the config into
|
|
|
|
config_provider.new_config_dir()
|
2018-08-27 21:05:53 +00:00
|
|
|
|
|
|
|
kube_accessor = KubernetesAccessorSingleton.get_instance()
|
|
|
|
kube_accessor.save_secret_to_directory(config_provider.get_config_dir_path())
|
|
|
|
config_provider.create_copy_of_config_dir()
|
2018-08-20 17:47:10 +00:00
|
|
|
|
|
|
|
# We update the db configuration to connect to their specified one
|
|
|
|
# (Note, even if this DB isn't valid, it won't affect much in the config app, since we'll report an error,
|
|
|
|
# and all of the options create a new clean dir, so we'll never pollute configs)
|
|
|
|
combined = dict(**app.config)
|
|
|
|
combined.update(config_provider.get_config())
|
|
|
|
configure(combined)
|
|
|
|
|
|
|
|
return 200
|