def _do_test(mock_test, mock_chartbuilder, mock_pre_flight, mock_post_flight): # Instantiate Armada object. yaml_documents = list(yaml.safe_load_all(TEST_YAML)) m_tiller = mock.MagicMock() m_tiller.list_releases.return_value = known_releases armada_obj = armada.Armada(yaml_documents, m_tiller) armada_obj.chart_deploy.get_diff = mock.Mock() chart_group = armada_obj.manifest['armada']['chart_groups'][0] charts = chart_group['chart_group'] cg_test_all_charts = chart_group.get('test_charts') mock_test_release = mock_test.return_value.test_release_for_success if test_failure_to_run: def fail(tiller, release, timeout=None, cleanup=False): status = AttrDict( **{'info': AttrDict(**{'Description': 'Failed'})}) raise tiller_exceptions.ReleaseException( release, status, 'Test') mock_test_release.side_effect = fail else: mock_test_release.return_value = test_success # Stub out irrelevant methods called by `armada.sync()`. mock_chartbuilder.get_source_path.return_value = None mock_chartbuilder.get_helm_chart.return_value = None # Simulate chart diff, upgrade should only happen if non-empty. armada_obj.chart_deploy.get_diff.return_value = diff armada_obj.sync() expected_install_release_calls = [] expected_update_release_calls = [] expected_uninstall_release_calls = [] expected_test_constructor_calls = [] for c in charts: chart = c['chart'] release = chart['release'] prefix = armada_obj.manifest['armada']['release_prefix'] release_name = release_prefixer(prefix, release) # Simplified check because the actual code uses logical-or's # multiple conditions, so this is enough. native_wait_enabled = (chart['wait'].get('native', {}).get( 'enabled', True)) if release_name not in [x.name for x in known_releases]: expected_install_release_calls.append( mock.call( mock_chartbuilder().get_helm_chart(), "{}-{}".format( armada_obj.manifest['armada'] ['release_prefix'], chart['release']), chart['namespace'], values=yaml.safe_dump(chart['values']), wait=native_wait_enabled, timeout=mock.ANY)) else: target_release = None for known_release in known_releases: if known_release.name == release_name: target_release = known_release break if target_release: status = get_release_status(target_release) if status == const.STATUS_FAILED: protected = chart.get('protected', {}) if not protected: expected_uninstall_release_calls.append( mock.call(release_name)) expected_install_release_calls.append( mock.call( mock_chartbuilder().get_helm_chart(), "{}-{}".format( armada_obj.manifest['armada'] ['release_prefix'], chart['release']), chart['namespace'], values=yaml.safe_dump(chart['values']), wait=native_wait_enabled, timeout=mock.ANY)) else: p_continue = protected.get( 'continue_processing', False) if p_continue: continue else: if chart_group['sequenced']: break if status == const.STATUS_DEPLOYED: if diff: upgrade = chart.get('upgrade', {}) disable_hooks = upgrade.get('no_hooks', False) force = upgrade.get('force', False) recreate_pods = upgrade.get( 'recreate_pods', False) expected_update_release_calls.append( mock.call( mock_chartbuilder().get_helm_chart(), "{}-{}".format( armada_obj.manifest['armada'] ['release_prefix'], chart['release']), chart['namespace'], pre_actions={}, post_actions={}, disable_hooks=disable_hooks, force=force, recreate_pods=recreate_pods, values=yaml.safe_dump(chart['values']), wait=native_wait_enabled, timeout=mock.ANY)) test_chart_override = chart.get('test') expected_test_constructor_calls.append( mock.call( release_name, m_tiller, cg_test_charts=cg_test_all_charts, test_values=test_chart_override)) any_order = not chart_group['sequenced'] # Verify that at least 1 release is either installed or updated. self.assertTrue( len(expected_install_release_calls) >= 1 or len(expected_update_release_calls) >= 1) # Verify that the expected number of non-deployed releases are # installed with expected arguments. self.assertEqual( len(expected_install_release_calls), m_tiller.install_release.call_count) m_tiller.install_release.assert_has_calls( expected_install_release_calls, any_order=any_order) # Verify that the expected number of deployed releases are # updated with expected arguments. self.assertEqual( len(expected_update_release_calls), m_tiller.update_release.call_count) m_tiller.update_release.assert_has_calls( expected_update_release_calls, any_order=any_order) # Verify that the expected number of deployed releases are # uninstalled with expected arguments. self.assertEqual( len(expected_uninstall_release_calls), m_tiller.uninstall_release.call_count) m_tiller.uninstall_release.assert_has_calls( expected_uninstall_release_calls, any_order=any_order) # Verify that the expected number of deployed releases are # tested with expected arguments. self.assertEqual( len(expected_test_constructor_calls), mock_test.call_count) mock_test.assert_has_calls( expected_test_constructor_calls, any_order=True)
def list_releases(self): ''' List Helm Releases ''' # TODO(MarshM possibly combine list_releases() with list_charts() # since they do the same thing, grouping output differently stub = ReleaseServiceStub(self.channel) # NOTE(seaneagan): Paging through releases to prevent hitting the # maximum message size limit that tiller sets for it's reponses. def get_results(): releases = [] done = False next_release_expected = "" initial_total = None while not done: req = ListReleasesRequest(offset=next_release_expected, limit=LIST_RELEASES_PAGE_SIZE, status_codes=const.STATUS_ALL) LOG.debug('Tiller ListReleases() with timeout=%s, request=%s', self.timeout, req) response = stub.ListReleases(req, self.timeout, metadata=self.metadata) found_message = False for message in response: found_message = True page = message.releases if initial_total: if message.total != initial_total: LOG.warning( 'Total releases changed between ' 'pages from (%s) to (%s)', initial_total, message.count) raise ex.TillerListReleasesPagingException() else: initial_total = message.total # Add page to results. releases.extend(page) if message.next: next_release_expected = message.next else: done = True # Ensure we break out was no message found which # is seen if there are no releases in tiller. if not found_message: done = True return releases for index in range(LIST_RELEASES_ATTEMPTS): attempt = index + 1 try: releases = get_results() except ex.TillerListReleasesPagingException: LOG.warning('List releases paging failed on attempt %s/%s', attempt, LIST_RELEASES_ATTEMPTS) if attempt == LIST_RELEASES_ATTEMPTS: raise else: # Filter out old releases, similar to helm cli: # https://github.com/helm/helm/blob/1e26b5300b5166fabb90002535aacd2f9cc7d787/cmd/helm/list.go#L196 latest_versions = {} for r in releases: max = latest_versions.get(r.name) if max is not None: if max > r.version: continue latest_versions[r.name] = r.version latest_releases = [] for r in releases: if latest_versions[r.name] == r.version: LOG.debug('Found release %s, version %s, status: %s', r.name, r.version, get_release_status(r)) latest_releases.append(r) return latest_releases
def execute(self, chart, cg_test_all_charts, prefix, known_releases): namespace = chart.get('namespace') release = chart.get('release') release_name = r.release_prefixer(prefix, release) LOG.info('Processing Chart, release=%s', release_name) values = chart.get('values', {}) pre_actions = {} post_actions = {} result = {} protected = chart.get('protected', {}) p_continue = protected.get('continue_processing', False) old_release = self.find_chart_release(known_releases, release_name) status = None if old_release: status = r.get_release_status(old_release) if status not in [const.STATUS_FAILED, const.STATUS_DEPLOYED]: raise armada_exceptions.UnexpectedReleaseStatusException( release_name, status) chart_wait = ChartWait( self.tiller.k8s, release_name, chart, namespace, k8s_wait_attempts=self.k8s_wait_attempts, k8s_wait_attempt_sleep=self.k8s_wait_attempt_sleep, timeout=self.timeout) native_wait_enabled = chart_wait.is_native_enabled() # Begin Chart timeout deadline deadline = time.time() + chart_wait.get_timeout() chartbuilder = ChartBuilder(chart) new_chart = chartbuilder.get_helm_chart() # Check for existing FAILED release, and purge if status == const.STATUS_FAILED: LOG.info('Purging FAILED release %s before deployment.', release_name) if protected: if p_continue: LOG.warn( 'Release %s is `protected`, ' 'continue_processing=True. Operator must ' 'handle FAILED release manually.', release_name) result['protected'] = release_name return result else: LOG.error( 'Release %s is `protected`, ' 'continue_processing=False.', release_name) raise armada_exceptions.ProtectedReleaseException( release_name) else: # Purge the release self.tiller.uninstall_release(release_name) result['purge'] = release_name # TODO(mark-burnett): It may be more robust to directly call # tiller status to decide whether to install/upgrade rather # than checking for list membership. if status == const.STATUS_DEPLOYED: # indicate to the end user what path we are taking LOG.info("Existing release %s found in namespace %s", release_name, namespace) # extract the installed chart and installed values from the # latest release so we can compare to the intended state old_chart = old_release.chart old_values_string = old_release.config.raw upgrade = chart.get('upgrade', {}) disable_hooks = upgrade.get('no_hooks', False) options = upgrade.get('options', {}) force = options.get('force', False) recreate_pods = options.get('recreate_pods', False) if upgrade: upgrade_pre = upgrade.get('pre', {}) upgrade_post = upgrade.get('post', {}) if not self.disable_update_pre and upgrade_pre: pre_actions = upgrade_pre if not self.disable_update_post and upgrade_post: LOG.warning('Post upgrade actions are ignored by Armada' 'and will not affect deployment.') post_actions = upgrade_post try: old_values = yaml.safe_load(old_values_string) except yaml.YAMLError: chart_desc = '{} (previously deployed)'.format( old_chart.metadata.name) raise armada_exceptions.\ InvalidOverrideValuesYamlException(chart_desc) LOG.info('Checking for updates to chart release inputs.') diff = self.get_diff(old_chart, old_values, new_chart, values) if not diff: LOG.info("Found no updates to chart release inputs") else: LOG.info("Found updates to chart release inputs") LOG.debug("%s", diff) result['diff'] = {chart['release']: str(diff)} # TODO(MarshM): Add tiller dry-run before upgrade and # consider deadline impacts # do actual update timer = int(round(deadline - time.time())) LOG.info( "Upgrading release %s in namespace %s, wait=%s, " "timeout=%ss", release_name, namespace, native_wait_enabled, timer) tiller_result = self.tiller.update_release( new_chart, release_name, namespace, pre_actions=pre_actions, post_actions=post_actions, disable_hooks=disable_hooks, values=yaml.safe_dump(values), wait=native_wait_enabled, timeout=timer, force=force, recreate_pods=recreate_pods) LOG.info('Upgrade completed with results from Tiller: %s', tiller_result.__dict__) result['upgrade'] = release_name else: timer = int(round(deadline - time.time())) LOG.info( "Installing release %s in namespace %s, wait=%s, " "timeout=%ss", release_name, namespace, native_wait_enabled, timer) tiller_result = self.tiller.install_release( new_chart, release_name, namespace, values=yaml.safe_dump(values), wait=native_wait_enabled, timeout=timer) LOG.info('Install completed with results from Tiller: %s', tiller_result.__dict__) result['install'] = release_name # Wait timer = int(round(deadline - time.time())) chart_wait.wait(timer) # Test just_deployed = ('install' in result) or ('upgrade' in result) last_test_passed = old_release and r.get_last_test_result(old_release) test_values = chart.get('test') test_handler = Test(release_name, self.tiller, cg_test_charts=cg_test_all_charts, test_values=test_values) run_test = test_handler.test_enabled and (just_deployed or not last_test_passed) if run_test: timer = int(round(deadline - time.time())) self._test_chart(release_name, timer, test_handler) return result
def _do_test(mock_test, mock_chartbuilder, MockChartDownload, mock_post_flight): MockChartDownload.return_value.get_chart.side_effect = \ set_source_dir # Instantiate Armada object. yaml_documents = list(yaml.safe_load_all(TEST_YAML)) m_helm = mock.MagicMock() armada_obj = armada.Armada(yaml_documents, m_helm) prefix = armada_obj.manifest['data']['release_prefix'] def release_metadata(release_id, **kwargs): try: return next(r for r in known_releases if release_id.name == r['name'] and release_id.namespace == r['namespace']) except StopIteration: return None m_helm.release_metadata.side_effect = release_metadata armada_obj.chart_deploy.get_diff = mock.Mock() cg = armada_obj.manifest['data']['chart_groups'][0] chart_group = cg['data'] charts = chart_group['chart_group'] cg_test_all_charts = chart_group.get('test_charts') mock_test_release = mock_test.return_value.test_release if test_failure_to_run: mock_test_release.side_effect = Exception('test failed to run') else: if not test_success: mock_test_release.side_effect = Exception('test failed') mock_test.return_value.timeout = const.DEFAULT_TEST_TIMEOUT # Stub out irrelevant methods called by `armada.sync()`. mock_chartbuilder.get_helm_chart.return_value = None # Simulate chart diff, upgrade should only happen if non-empty. armada_obj.chart_deploy.get_diff.return_value = diff armada_obj.sync() expected_install_release_calls = [] expected_upgrade_release_calls = [] expected_uninstall_release_calls = [] expected_test_constructor_calls = [] for c in charts: chart = c['data'] release = chart['release'] release_name = release_prefixer(prefix, release) release_id = helm.HelmReleaseId(chart['namespace'], release_name) source_dir = chart['source_dir'] source_directory = os.path.join(*source_dir) # Simplified check because the actual code uses logical-or's # multiple conditions, so this is enough. native_wait_enabled = (chart['wait'].get('native', {}).get( 'enabled', True)) if release_name not in [x['name'] for x in known_releases]: expected_install_release_calls.append( mock.call(source_directory, release_id, values=chart['values'], wait=native_wait_enabled, timeout=mock.ANY)) else: target_release = None for known_release in known_releases: if known_release['name'] == release_name: target_release = known_release break if target_release: status = get_release_status(target_release) if status == helm.STATUS_FAILED: protected = chart.get('protected', {}) if not protected: expected_uninstall_release_calls.append( mock.call( release_id, purge=True, timeout=const.DEFAULT_DELETE_TIMEOUT)) expected_install_release_calls.append( mock.call(source_directory, release_id, values=chart['values'], wait=native_wait_enabled, timeout=mock.ANY)) else: p_continue = protected.get( 'continue_processing', False) if p_continue: continue else: if chart_group['sequenced']: break if status == helm.STATUS_DEPLOYED: if diff: upgrade = chart.get('upgrade', {}) disable_hooks = upgrade.get('no_hooks', False) options = upgrade.get('options', {}) force = options.get('force', False) expected_upgrade_release_calls.append( mock.call(source_directory, release_id, disable_hooks=disable_hooks, force=force, values=chart['values'], wait=native_wait_enabled, timeout=mock.ANY)) expected_test_constructor_calls.append( mock.call(chart, release_id, m_helm, cg_test_charts=cg_test_all_charts)) any_order = not chart_group['sequenced'] # Verify that at least 1 release is either installed or updated. self.assertTrue( len(expected_install_release_calls) >= 1 or len(expected_upgrade_release_calls) >= 1) # Verify that the expected number of non-deployed releases are # installed with expected arguments. self.assertEqual(len(expected_install_release_calls), m_helm.install_release.call_count) m_helm.install_release.assert_has_calls( expected_install_release_calls, any_order=any_order) # Verify that the expected number of deployed releases are # updated with expected arguments. self.assertEqual(len(expected_upgrade_release_calls), m_helm.upgrade_release.call_count) m_helm.upgrade_release.assert_has_calls( expected_upgrade_release_calls, any_order=any_order) # Verify that the expected number of deployed releases are # uninstalled with expected arguments. self.assertEqual(len(expected_uninstall_release_calls), m_helm.uninstall_release.call_count) m_helm.uninstall_release.assert_has_calls( expected_uninstall_release_calls, any_order=any_order) # Verify that the expected number of deployed releases are # tested with expected arguments. self.assertEqual(len(expected_test_constructor_calls), mock_test.call_count) mock_test.assert_has_calls(expected_test_constructor_calls, any_order=True)
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) source_dir = chart['source_dir'] source_directory = os.path.join(*source_dir) LOG.info('Processing Chart, release=%s', release_id) result = {} chart_wait = ChartWait( self.helm.k8s, release_id, ch, k8s_wait_attempts=self.k8s_wait_attempts, k8s_wait_attempt_sleep=self.k8s_wait_attempt_sleep, timeout=self.timeout) wait_timeout = chart_wait.get_timeout() # Begin Chart timeout deadline deadline = time.time() + wait_timeout old_release = self.helm.release_metadata(release_id) action = metrics.ChartDeployAction.NOOP def noop(): pass deploy = noop # Resolve action values = chart.get('values', {}) pre_actions = {} status = None if old_release: status = r.get_release_status(old_release) native_wait_enabled = chart_wait.is_native_enabled() chartbuilder = ChartBuilder.from_chart_doc(ch, self.helm) if status == helm.STATUS_DEPLOYED: # indicate to the end user what path we are taking LOG.info("Existing release %s found", release_id) # extract the installed chart and installed values from the # latest release so we can compare to the intended state old_chart = old_release['chart'] old_values = old_release.get('config', {}) upgrade = chart.get('upgrade', {}) options = upgrade.get('options', {}) # TODO: Remove when v1 doc support is removed. schema_info = get_schema_info(ch['schema']) if schema_info.version < 2: no_hooks_location = upgrade else: no_hooks_location = options disable_hooks = no_hooks_location.get('no_hooks', False) force = options.get('force', False) if upgrade: upgrade_pre = upgrade.get('pre', {}) upgrade_post = upgrade.get('post', {}) if not self.disable_update_pre and upgrade_pre: pre_actions = upgrade_pre if not self.disable_update_post and upgrade_post: LOG.warning('Post upgrade actions are ignored by Armada' 'and will not affect deployment.') LOG.info('Checking for updates to chart release inputs.') new_chart = chartbuilder.get_helm_chart(release_id, values) diff = self.get_diff(old_chart, old_values, new_chart, values) if not diff: LOG.info("Found no updates to chart release inputs") else: action = metrics.ChartDeployAction.UPGRADE LOG.info("Found updates to chart release inputs") def upgrade(): # do actual update timer = int(round(deadline - time.time())) PreUpdateActions(self.helm.k8s).execute( pre_actions, release, namespace, chart, disable_hooks, values, timer) LOG.info("Upgrading release=%s, wait=%s, " "timeout=%ss", release_id, native_wait_enabled, timer) self.helm.upgrade_release(source_directory, release_id, disable_hooks=disable_hooks, values=values, wait=native_wait_enabled, timeout=timer, force=force) LOG.info('Upgrade completed') result['upgrade'] = release_id deploy = upgrade else: def install(): timer = int(round(deadline - time.time())) LOG.info("Installing release=%s, wait=%s, " "timeout=%ss", release_id, native_wait_enabled, timer) self.helm.install_release(source_directory, release_id, values=values, wait=native_wait_enabled, timeout=timer) LOG.info('Install completed') result['install'] = release_id # Check for release with status other than DEPLOYED if status: if status != helm.STATUS_FAILED: LOG.warn( 'Unexpected release status encountered ' 'release=%s, status=%s', release_id, status) # Make best effort to determine whether a deployment is # likely pending, by checking if the last deployment # was started within the timeout window of the chart. last_deployment_age = r.get_last_deployment_age( old_release) likely_pending = last_deployment_age <= wait_timeout if likely_pending: # We don't take any deploy action and wait for the # to get deployed. deploy = noop deadline = deadline - last_deployment_age else: # Release is likely stuck in an unintended # state. Log and continue on with remediation steps # below. LOG.info( 'Old release %s likely stuck in status %s, ' '(last deployment age=%ss) >= ' '(chart wait timeout=%ss)', release, status, last_deployment_age, wait_timeout) res = self.purge_release(chart, release_id, status, manifest_name, chart_name, result) if isinstance(res, dict): if 'protected' in res: return res action = metrics.ChartDeployAction.INSTALL deploy = install else: # The chart is in Failed state, hence we purge # the chart and attempt to install it again. res = self.purge_release(chart, release_id, status, manifest_name, chart_name, result) if isinstance(res, dict): if 'protected' in res: return res action = metrics.ChartDeployAction.INSTALL deploy = install if status is None: action = metrics.ChartDeployAction.INSTALL deploy = install # 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())) chart_wait.wait(timer) # Test just_deployed = ('install' in result) or ('upgrade' in result) last_test_passed = old_release and r.get_last_test_result(old_release) test_handler = Test(chart, release_id, self.helm, cg_test_charts=cg_test_all_charts) run_test = test_handler.test_enabled and (just_deployed or not last_test_passed) if run_test: with metrics.CHART_TEST.get_context(test_handler.timeout, manifest_name, chart_name): self._test_chart(test_handler) return result
def _execute(self, ch, cg_test_all_charts, prefix, known_releases): 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) LOG.info('Processing Chart, release=%s', release_name) result = {} chart_wait = ChartWait( self.tiller.k8s, release_name, ch, namespace, k8s_wait_attempts=self.k8s_wait_attempts, k8s_wait_attempt_sleep=self.k8s_wait_attempt_sleep, timeout=self.timeout) wait_timeout = chart_wait.get_timeout() # Begin Chart timeout deadline deadline = time.time() + wait_timeout old_release = self.find_chart_release(known_releases, release_name) action = metrics.ChartDeployAction.NOOP def noop(): pass deploy = noop # Resolve action values = chart.get('values', {}) pre_actions = {} post_actions = {} status = None if old_release: status = r.get_release_status(old_release) native_wait_enabled = chart_wait.is_native_enabled() chartbuilder = ChartBuilder.from_chart_doc(ch) new_chart = chartbuilder.get_helm_chart() if status == const.STATUS_DEPLOYED: # indicate to the end user what path we are taking LOG.info("Existing release %s found in namespace %s", release_name, namespace) # extract the installed chart and installed values from the # latest release so we can compare to the intended state old_chart = old_release.chart old_values_string = old_release.config.raw upgrade = chart.get('upgrade', {}) options = upgrade.get('options', {}) # TODO: Remove when v1 doc support is removed. schema_info = get_schema_info(ch['schema']) if schema_info.version < 2: no_hooks_location = upgrade else: no_hooks_location = options disable_hooks = no_hooks_location.get('no_hooks', False) force = options.get('force', False) recreate_pods = options.get('recreate_pods', False) if upgrade: upgrade_pre = upgrade.get('pre', {}) upgrade_post = upgrade.get('post', {}) if not self.disable_update_pre and upgrade_pre: pre_actions = upgrade_pre if not self.disable_update_post and upgrade_post: LOG.warning('Post upgrade actions are ignored by Armada' 'and will not affect deployment.') post_actions = upgrade_post try: old_values = yaml.safe_load(old_values_string) except yaml.YAMLError: chart_desc = '{} (previously deployed)'.format( old_chart.metadata.name) raise armada_exceptions.\ InvalidOverrideValuesYamlException(chart_desc) LOG.info('Checking for updates to chart release inputs.') diff = self.get_diff(old_chart, old_values, new_chart, values) if not diff: LOG.info("Found no updates to chart release inputs") else: action = metrics.ChartDeployAction.UPGRADE LOG.info("Found updates to chart release inputs") LOG.debug("%s", diff) result['diff'] = {chart['release']: str(diff)} def upgrade(): # do actual update timer = int(round(deadline - time.time())) LOG.info( "Upgrading release %s in namespace %s, wait=%s, " "timeout=%ss", release_name, namespace, native_wait_enabled, timer) tiller_result = self.tiller.update_release( new_chart, release_name, namespace, pre_actions=pre_actions, post_actions=post_actions, disable_hooks=disable_hooks, values=yaml.safe_dump(values), wait=native_wait_enabled, timeout=timer, force=force, recreate_pods=recreate_pods) LOG.info('Upgrade completed with results from Tiller: %s', tiller_result.__dict__) result['upgrade'] = release_name deploy = upgrade else: # Check for release with status other than DEPLOYED if status: if status != const.STATUS_FAILED: LOG.warn( 'Unexpected release status encountered ' 'release=%s, status=%s', release_name, status) # Make best effort to determine whether a deployment is # likely pending, by checking if the last deployment # was started within the timeout window of the chart. last_deployment_age = r.get_last_deployment_age( old_release) likely_pending = last_deployment_age <= wait_timeout if likely_pending: # Give up if a deployment is likely pending, we do not # want to have multiple operations going on for the # same release at the same time. raise armada_exceptions.\ DeploymentLikelyPendingException( release_name, status, last_deployment_age, wait_timeout) else: # Release is likely stuck in an unintended (by tiller) # state. Log and continue on with remediation steps # below. LOG.info( 'Old release %s likely stuck in status %s, ' '(last deployment age=%ss) >= ' '(chart wait timeout=%ss)', release, status, last_deployment_age, wait_timeout) protected = chart.get('protected', {}) if protected: p_continue = protected.get('continue_processing', False) if p_continue: LOG.warn( 'Release %s is `protected`, ' 'continue_processing=True. Operator must ' 'handle %s release manually.', release_name, status) result['protected'] = release_name return result else: LOG.error( 'Release %s is `protected`, ' 'continue_processing=False.', release_name) raise armada_exceptions.ProtectedReleaseException( release_name, status) else: # Purge the release with metrics.CHART_DELETE.get_context( manifest_name, chart_name): LOG.info('Purging release %s with status %s', release_name, status) chart_delete = ChartDelete(chart, release_name, self.tiller) chart_delete.delete() result['purge'] = release_name action = metrics.ChartDeployAction.INSTALL def install(): timer = int(round(deadline - time.time())) LOG.info( "Installing release %s in namespace %s, wait=%s, " "timeout=%ss", release_name, namespace, native_wait_enabled, timer) tiller_result = self.tiller.install_release( new_chart, release_name, namespace, values=yaml.safe_dump(values), wait=native_wait_enabled, timeout=timer) LOG.info('Install completed with results from Tiller: %s', tiller_result.__dict__) result['install'] = release_name deploy = install # 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())) chart_wait.wait(timer) # Test just_deployed = ('install' in result) or ('upgrade' in result) last_test_passed = old_release and r.get_last_test_result(old_release) test_handler = Test(chart, release_name, self.tiller, cg_test_charts=cg_test_all_charts) run_test = test_handler.test_enabled and (just_deployed or not last_test_passed) if run_test: with metrics.CHART_TEST.get_context(test_handler.timeout, manifest_name, chart_name): self._test_chart(release_name, test_handler) return result