armada/armada/api/controller/armada.py
Sean Eagan 9fad5cff0a Add chart API to wait on k8s resource types/labels
This adds a `wait.resources` key to chart documents which allows
waiting on a list of k8s type+labels configurations to wait on.
Initially supported types are pods, jobs, deployments, daemonsets, and
statefulsets. The behavior for controller types is similar to that of
`kubectl rollout status`.

If `wait.resources` is omitted, it waits on pods and jobs (if any exist)
as before.

The existing `wait.labels` key still have the same behavior, but if
`wait.resources` is also included, the labels are added to each resource
wait in that array. Thus they serve to specify base labels that apply
to all resources in the release, so as to not have to duplicate them.
This may also be useful later for example to use them as labels to wait
for when deleting a chart.

Controller types additionaly have a `min_ready` field which
represents the minimum amount of pods of the controller which must
be ready in order for the controller to be considered ready. The value
can either be an integer or a percent string e.g. "80%", similar to e.g.
`maxUnavailable` in k8s. Default is "100%".

This also wraps up moving the rest of the wait code into its own module.

Change-Id: If72881af0c74e8f765bbb57ac5ffc8d709cd3c16
2018-10-05 16:48:32 -05:00

106 lines
4.0 KiB
Python

# Copyright 2017 The Armada Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import json
import yaml
import falcon
from oslo_config import cfg
from armada import api
from armada.common import policy
from armada import exceptions
from armada.handlers.armada import Armada
from armada.handlers.document import ReferenceResolver
from armada.handlers.override import Override
CONF = cfg.CONF
class Apply(api.BaseResource):
"""Controller for installing and updating charts defined in an Armada
manifest file.
"""
@policy.enforce('armada:create_endpoints')
def on_post(self, req, resp):
# Load data from request and get options
if req.content_type == 'application/x-yaml':
data = list(self.req_yaml(req))
if type(data[0]) is list:
documents = list(data[0])
else:
documents = data
elif req.content_type == 'application/json':
self.logger.debug("Applying manifest based on reference.")
req_body = self.req_json(req)
doc_ref = req_body.get('hrefs', None)
if not doc_ref:
self.logger.info("Request did not contain 'hrefs'.")
resp.status = falcon.HTTP_400
return
data = ReferenceResolver.resolve_reference(doc_ref)
documents = list()
for d in data:
documents.extend(list(yaml.safe_load_all(d.decode())))
if req_body.get('overrides', None):
overrides = Override(
documents, overrides=req_body.get('overrides'))
documents = overrides.update_manifests()
else:
self.error(req.context,
"Unknown content-type %s" % req.content_type)
# TODO(fmontei): Use falcon.<Relevant API Exception Class> instead.
return self.return_error(
resp,
falcon.HTTP_415,
message="Request must be in application/x-yaml"
"or application/json")
try:
armada = Armada(
documents,
disable_update_pre=req.get_param_as_bool('disable_update_pre'),
disable_update_post=req.get_param_as_bool(
'disable_update_post'),
enable_chart_cleanup=req.get_param_as_bool(
'enable_chart_cleanup'),
dry_run=req.get_param_as_bool('dry_run'),
force_wait=req.get_param_as_bool('wait'),
timeout=req.get_param_as_int('timeout'),
tiller_host=req.get_param('tiller_host'),
tiller_port=req.get_param_as_int('tiller_port') or
CONF.tiller_port,
tiller_namespace=req.get_param(
'tiller_namespace', default=CONF.tiller_namespace),
target_manifest=req.get_param('target_manifest'))
msg = armada.sync()
resp.body = json.dumps({
'message': msg,
})
resp.content_type = 'application/json'
resp.status = falcon.HTTP_200
except exceptions.ManifestException as e:
self.return_error(resp, falcon.HTTP_400, message=str(e))
except Exception as e:
self.logger.exception('Caught unexpected exception')
err_message = 'Failed to apply manifest: {}'.format(e)
self.error(req.context, err_message)
self.return_error(resp, falcon.HTTP_500, message=err_message)