Run wait/test even if chart not updated

Previously if a chart is not updated, it would simply be skipped over.
Now, the wait/tests are run in this case to ensure the chart success
criteria is/was actually satisfied. It does still skip tests if there
is a last test result recorded as successful already, as an
optimization.

Change-Id: I5dc95fe0f16fe0989761e771c77d2c4fa8f6e7ea
This commit is contained in:
Sean Eagan 2018-10-26 16:00:29 -05:00
parent 36a21b86a7
commit 69b43983e9
4 changed files with 119 additions and 44 deletions

View File

@ -23,7 +23,7 @@ from armada.handlers.test import test_release_for_success
from armada.handlers.release_diff import ReleaseDiff
from armada.handlers.wait import ChartWait
from armada.exceptions import tiller_exceptions
from armada.utils.release import release_prefixer, get_release_status
import armada.utils.release as r
LOG = logging.getLogger(__name__)
@ -43,7 +43,7 @@ class ChartDeploy(object):
def execute(self, chart, cg_test_all_charts, prefix, known_releases):
namespace = chart.get('namespace')
release = chart.get('release')
release_name = release_prefixer(prefix, release)
release_name = r.release_prefixer(prefix, release)
LOG.info('Processing Chart, release=%s', release_name)
values = chart.get('values', {})
@ -59,7 +59,7 @@ class ChartDeploy(object):
status = None
if old_release:
status = get_release_status(old_release)
status = r.get_release_status(old_release)
if status not in [const.STATUS_FAILED, const.STATUS_DEPLOYED]:
raise armada_exceptions.UnexpectedReleaseStatusException(
@ -147,39 +147,36 @@ class ChartDeploy(object):
if not diff:
LOG.info("Found no updates to chart release inputs")
return result
else:
LOG.info("Found updates to chart release inputs")
LOG.debug("%s", diff)
result['diff'] = {chart['release']: str(diff)}
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
# 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)
# 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
# process install
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(
@ -198,26 +195,33 @@ class ChartDeploy(object):
tiller_result.__dict__)
result['install'] = release_name
# Wait
timer = int(round(deadline - time.time()))
chart_wait.wait(timer)
# Test
test_chart_override = chart.get('test')
# Use old default value when not using newer `test` key
test_cleanup = True
if test_chart_override is None:
test_this_chart = cg_test_all_charts
test_enabled = cg_test_all_charts
elif isinstance(test_chart_override, bool):
LOG.warn('Boolean value for chart `test` key is'
' deprecated and support for this will'
' be removed. Use `test.enabled` '
'instead.')
test_this_chart = test_chart_override
test_enabled = test_chart_override
else:
# NOTE: helm tests are enabled by default
test_this_chart = test_chart_override.get('enabled', True)
test_enabled = test_chart_override.get('enabled', True)
test_cleanup = test_chart_override.get('options', {}).get(
'cleanup', False)
if test_this_chart:
just_deployed = ('install' in result) or ('upgrade' in result)
last_test_passed = old_release and r.get_last_test_result(old_release)
run_test = 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_cleanup)

View File

@ -26,6 +26,9 @@ def test_release_for_success(tiller,
cleanup=False):
test_suite_run = tiller.test_release(
release, timeout=timeout, cleanup=cleanup)
results = getattr(test_suite_run, 'results', [])
failed_results = [r for r in results if r.status != TESTRUN_STATUS_SUCCESS]
return len(failed_results) == 0
return get_test_suite_run_success(test_suite_run)
def get_test_suite_run_success(test_suite_run):
return all(
r.status == TESTRUN_STATUS_SUCCESS for r in test_suite_run.results)

View File

@ -18,6 +18,7 @@ import yaml
from armada import const
from armada.handlers import armada
from armada.handlers import chart_deploy
from armada.handlers.test import TESTRUN_STATUS_SUCCESS, TESTRUN_STATUS_FAILURE
from armada.tests.unit import base
from armada.tests.test_utils import AttrDict, makeMockThreadSafe
from armada.utils.release import release_prefixer, get_release_status
@ -334,6 +335,7 @@ class ArmadaHandlerTestCase(base.ArmadaTestCase):
known_releases,
test_success=True,
test_failure_to_run=False,
expected_last_test_result=None,
diff={'some_key': {'some diff'}}):
"""Test install functionality from the sync() method."""
@ -476,7 +478,8 @@ class ArmadaHandlerTestCase(base.ArmadaTestCase):
test_cleanup = test_chart_override.get('options', {}).get(
'cleanup', False)
if test_this_chart and expected_apply:
if test_this_chart and (expected_apply or
not expected_last_test_result):
expected_test_release_for_success_calls.append(
mock.call(
m_tiller,
@ -527,16 +530,35 @@ class ArmadaHandlerTestCase(base.ArmadaTestCase):
c for c in yaml_documents if c['data'].get('chart_name') == name
][0]
def get_mock_release(self, name, status):
def get_mock_release(self, name, status, last_test_results=None):
status_mock = mock.Mock()
status_mock.return_value = status
chart = self._get_chart_by_name(name)
def get_test_result(success):
status = (TESTRUN_STATUS_SUCCESS
if success else TESTRUN_STATUS_FAILURE)
return mock.Mock(status=status)
last_test_suite_run = None
if last_test_results is not None:
results = [get_test_result(r) for r in last_test_results]
last_test_suite_run = mock.Mock(results=results)
def has_last_test(name):
if name == 'last_test_suite_run':
return last_test_results is not None
self.fail('Called HasField() with unexpected field')
mock_release = mock.Mock(
version=1,
chart=chart,
config=mock.Mock(raw="{}"),
info=mock.Mock(
status=mock.Mock(Code=mock.MagicMock(Name=status_mock))))
status=mock.Mock(
Code=mock.MagicMock(Name=status_mock),
HasField=mock.MagicMock(side_effect=has_last_test),
last_test_suite_run=last_test_suite_run)))
mock_release.name = name
return mock_release
@ -556,6 +578,36 @@ class ArmadaHandlerTestCase(base.ArmadaTestCase):
known_releases = [self.get_mock_release(c1, const.STATUS_DEPLOYED)]
self._test_sync(known_releases, diff=set())
def test_armada_sync_with_failed_test_result(self):
c1 = 'armada-test_chart_1'
known_releases = [
self.get_mock_release(
c1, const.STATUS_DEPLOYED, last_test_results=[False])
]
self._test_sync(
known_releases, diff=set(), expected_last_test_result=False)
def test_armada_sync_with_success_test_result(self):
c1 = 'armada-test_chart_1'
known_releases = [
self.get_mock_release(
c1, const.STATUS_DEPLOYED, last_test_results=[True])
]
self._test_sync(
known_releases, diff=set(), expected_last_test_result=True)
def test_armada_sync_with_success_test_result_no_tests(self):
c1 = 'armada-test_chart_1'
known_releases = [
self.get_mock_release(
c1, const.STATUS_DEPLOYED, last_test_results=[])
]
self._test_sync(
known_releases, diff=set(), expected_last_test_result=True)
def test_armada_sync_with_both_deployed_releases(self):
c1 = 'armada-test_chart_1'
c2 = 'armada-test_chart_2'

View File

@ -12,6 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from armada.handlers.test import get_test_suite_run_success
def release_prefixer(prefix, release):
'''
@ -36,4 +38,18 @@ def get_release_status(release):
:return: status name of release
"""
return release.info.status.Code.Name(release.info.status.code)
status = release.info.status
return status.Code.Name(status.code)
def get_last_test_result(release):
"""
:param release: protobuf release object
:return: status name of release
"""
status = release.info.status
if not status.HasField('last_test_suite_run'):
return None
return get_test_suite_run_success(status.last_test_suite_run)