227 lines
8.4 KiB
Python
227 lines
8.4 KiB
Python
# Copyright 2018 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 time
|
|
|
|
from kubernetes.client.rest import ApiException
|
|
from oslo_log import log as logging
|
|
|
|
from armada import const
|
|
from armada.exceptions import armada_exceptions
|
|
from armada.handlers import metrics
|
|
from armada.handlers import helm
|
|
from armada.handlers.release_diff import ReleaseDiff
|
|
from armada.handlers.chart_delete import ChartDelete
|
|
import armada.utils.release as r
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class ChartDeploy(object):
|
|
def __init__(
|
|
self, manifest, disable_update_pre, disable_update_post,
|
|
k8s_wait_attempts, k8s_wait_attempt_sleep, timeout, helm):
|
|
self.manifest = manifest
|
|
self.disable_update_pre = disable_update_pre
|
|
self.disable_update_post = disable_update_post
|
|
self.k8s_wait_attempts = k8s_wait_attempts
|
|
self.k8s_wait_attempt_sleep = k8s_wait_attempt_sleep
|
|
self.timeout = timeout
|
|
self.k8s = helm.k8s
|
|
|
|
def execute(self, ch, cg_test_all_charts, prefix, concurrency):
|
|
chart_name = ch['metadata']['name']
|
|
manifest_name = self.manifest['metadata']['name']
|
|
with metrics.CHART_HANDLE.get_context(concurrency, manifest_name,
|
|
chart_name):
|
|
return self._execute(ch, cg_test_all_charts, prefix)
|
|
|
|
def _execute(self, ch, cg_test_all_charts, prefix):
|
|
manifest_name = self.manifest['metadata']['name']
|
|
chart = ch[const.KEYWORD_DATA]
|
|
chart_name = ch['metadata']['name']
|
|
namespace = chart.get('namespace')
|
|
release = chart.get('release')
|
|
release_name = r.release_prefixer(prefix, release)
|
|
release_id = helm.HelmReleaseId(namespace, release_name)
|
|
LOG.info('Processing Chart, release=%s', release_id)
|
|
|
|
result = {}
|
|
|
|
wait_timeout = self._get_timeout(ch)
|
|
# Begin Chart timeout deadline
|
|
deadline = time.time() + wait_timeout
|
|
|
|
def noop():
|
|
pass
|
|
|
|
deploy = noop
|
|
|
|
# Resolve action
|
|
values = chart.get('values', {})
|
|
self._convert_min_ready(chart)
|
|
|
|
try:
|
|
self.k8s.read_custom_resource(
|
|
group=const.CHART_GROUP,
|
|
version=const.CHART_VERSION,
|
|
namespace=namespace,
|
|
plural=const.CHART_PLURAL,
|
|
name=release_name)
|
|
|
|
# indicate to the end user what path we are taking
|
|
LOG.info("Existing release %s found", release_name)
|
|
|
|
def upgrade():
|
|
# do actual update
|
|
timer = int(round(deadline - time.time()))
|
|
|
|
LOG.info(
|
|
"Upgrading (replacing) release=%s, "
|
|
"timeout=%ss", release_name, timer)
|
|
|
|
self.k8s.replace_custom_resource(
|
|
"armada.airshipit.io",
|
|
"v1",
|
|
namespace,
|
|
"armadacharts",
|
|
release_name,
|
|
self._gen_crd_template(chart_name, release_id,
|
|
chart['source']['location'],
|
|
values, chart.get('wait', {}),
|
|
chart.get('upgrade', {}).
|
|
get('pre', {}).get('delete', []),
|
|
chart.get('test', {}).
|
|
get('enabled', True))
|
|
)
|
|
|
|
LOG.info('Upgrade completed')
|
|
result['upgrade'] = release_id
|
|
|
|
action = metrics.ChartDeployAction.UPGRADE
|
|
deploy = upgrade
|
|
|
|
except ApiException as err:
|
|
if err.status == 404:
|
|
def install():
|
|
timer = int(round(deadline - time.time()))
|
|
LOG.info(
|
|
"Installing release=%s, "
|
|
"timeout=%ss", release_name, timer)
|
|
|
|
self.k8s.create_custom_resource(
|
|
const.CHART_GROUP,
|
|
const.CHART_VERSION,
|
|
namespace,
|
|
const.CHART_PLURAL,
|
|
self._gen_crd_template(chart_name, release_id,
|
|
chart['source']['location'],
|
|
values, chart.get('wait', {}),
|
|
chart.get('upgrade', {}).
|
|
get('pre', {}).get('delete', []),
|
|
chart.get('test', {}).
|
|
get('enabled', True)
|
|
)
|
|
)
|
|
|
|
LOG.info('Install completed')
|
|
result['install'] = release_id
|
|
|
|
action = metrics.ChartDeployAction.INSTALL
|
|
deploy = install
|
|
else:
|
|
raise
|
|
|
|
# Deploy
|
|
with metrics.CHART_DEPLOY.get_context(wait_timeout, manifest_name,
|
|
chart_name,
|
|
action.get_label_value()):
|
|
deploy()
|
|
|
|
# Wait
|
|
timer = int(round(deadline - time.time()))
|
|
LOG.info('Starting to wait')
|
|
self.k8s.wait_custom_resource(const.CHART_GROUP,
|
|
const.CHART_VERSION,
|
|
namespace,
|
|
const.CHART_PLURAL,
|
|
"{}={}".format(const.CHART_LABEL,
|
|
release_id.name),
|
|
timer)
|
|
|
|
return result
|
|
|
|
def _get_timeout(self, ch):
|
|
chart_data = ch[const.KEYWORD_DATA]
|
|
wait_config = chart_data.get('wait', {})
|
|
|
|
# Calculate timeout
|
|
wait_timeout = self.timeout
|
|
if wait_timeout is None:
|
|
wait_timeout = wait_config.get('timeout')
|
|
|
|
# TODO: Remove when v1 doc support is removed.
|
|
deprecated_timeout = chart_data.get('timeout')
|
|
if deprecated_timeout is not None:
|
|
LOG.warn(
|
|
'The `timeout` key is deprecated and support '
|
|
'for this will be removed soon. Use '
|
|
'`wait.timeout` instead.')
|
|
if wait_timeout is None:
|
|
wait_timeout = deprecated_timeout
|
|
|
|
if wait_timeout is None:
|
|
LOG.info(
|
|
'No Chart timeout specified, using default: %ss',
|
|
const.DEFAULT_CHART_TIMEOUT)
|
|
wait_timeout = const.DEFAULT_CHART_TIMEOUT
|
|
|
|
return wait_timeout
|
|
|
|
@staticmethod
|
|
def _convert_min_ready(chart):
|
|
wait_opts = chart.get('wait', {})
|
|
if len(wait_opts) > 0:
|
|
res_opts = wait_opts.get('resources', [])
|
|
for opt in res_opts:
|
|
if 'min_ready' in opt:
|
|
opt['min_ready'] = str(opt['min_ready'])
|
|
|
|
@staticmethod
|
|
def _gen_crd_template(chart_name, release_id, location,
|
|
values, wait_opts, pre_opts=[], test_enabled=True):
|
|
return {
|
|
'apiVersion': const.CHART_APIVERSION,
|
|
'kind': const.CHART_KIND,
|
|
'metadata': {
|
|
'labels': {
|
|
const.CHART_LABEL: release_id.name
|
|
},
|
|
'name': release_id.name,
|
|
'namespace': release_id.namespace,
|
|
},
|
|
'data': {
|
|
'chart_name': chart_name,
|
|
'namespace': release_id.namespace,
|
|
'release': release_id.name,
|
|
'source': {
|
|
'location': location,
|
|
},
|
|
'values': values,
|
|
'test': {'enabled': test_enabled},
|
|
'delete': pre_opts,
|
|
'wait': wait_opts,
|
|
}
|
|
}
|