From 3a0993210d7d08392d97903589cf8adba6fb92d2 Mon Sep 17 00:00:00 2001 From: Simon Pasquier Date: Thu, 16 Jan 2014 11:16:13 +0100 Subject: [PATCH] Support of ignoreErrors for commands cfn-init will now fail immediately if a command with the key ignoreErrors='false' or without that key fails (eg it returns an exit code other than 0). This is similar to what the AWS cfn-init script is doing. Change-Id: I41bfa36154fa8b16541a6abb489495739b772376 Closes-Bug: #1269476 --- heat_cfntools/cfntools/cfn_helper.py | 16 ++++--- heat_cfntools/tests/test_cfn_helper.py | 65 +++++++++++++++++++++----- 2 files changed, 62 insertions(+), 19 deletions(-) diff --git a/heat_cfntools/cfntools/cfn_helper.py b/heat_cfntools/cfntools/cfn_helper.py index 9579194..4c184cd 100644 --- a/heat_cfntools/cfntools/cfn_helper.py +++ b/heat_cfntools/cfntools/cfn_helper.py @@ -829,6 +829,10 @@ def metadata_server_port( return None +class CommandsHandlerRunError(Exception): + pass + + class CommandsHandler(object): def __init__(self, commands): @@ -887,14 +891,12 @@ class CommandsHandler(object): if command_status == 0: LOG.info("%s has been successfully executed" % command_label) else: - if "ignoreErrors" in properties: - if properties["ignoreErrors"] == "false": - LOG.error("%s has failed. Not ignoring" % command_label) - else: - LOG.info("%s has failed. Explicit ignoring" - % command_label) + if "ignoreErrors" in properties and \ + to_boolean(properties["ignoreErrors"]): + LOG.info("%s has failed (status=%d). Explicit ignoring" + % (command_label, command_status)) else: - LOG.error("%s has failed." % command_label) + raise CommandsHandlerRunError("%s has failed." % command_label) class GroupsHandler(object): diff --git a/heat_cfntools/tests/test_cfn_helper.py b/heat_cfntools/tests/test_cfn_helper.py index 9073d01..e81b9bf 100644 --- a/heat_cfntools/tests/test_cfn_helper.py +++ b/heat_cfntools/tests/test_cfn_helper.py @@ -800,18 +800,6 @@ class TestMetadataRetrieve(testtools.TestCase): finally: m.UnsetStubs() - def test_cfn_init(self): - - with tempfile.NamedTemporaryFile(mode='w+') as foo_file: - md_data = {"AWS::CloudFormation::Init": {"config": {"files": { - foo_file.name: {"content": "bar"}}}}} - - md = cfn_helper.Metadata('teststack', None) - self.assertTrue( - md.retrieve(meta_str=md_data, last_path=self.last_file)) - md.cfn_init() - self.assertThat(foo_file.name, ttm.FileContains('bar')) - def test_nova_meta_with_cache(self): meta_in = {"uuid": "f9431d18-d971-434d-9044-5b38f5b4646f", "availability_zone": "nova", @@ -960,6 +948,59 @@ class TestMetadataRetrieve(testtools.TestCase): self.m.VerifyAll() +class TestCfnInit(MockPopenTestCase): + + def setUp(self): + super(TestCfnInit, self).setUp() + self.tdir = self.useFixture(fixtures.TempDir()) + self.last_file = os.path.join(self.tdir.path, 'last_metadata') + + def test_cfn_init(self): + + with tempfile.NamedTemporaryFile(mode='w+') as foo_file: + md_data = {"AWS::CloudFormation::Init": {"config": {"files": { + foo_file.name: {"content": "bar"}}}}} + + md = cfn_helper.Metadata('teststack', None) + self.assertTrue( + md.retrieve(meta_str=md_data, last_path=self.last_file)) + md.cfn_init() + self.assertThat(foo_file.name, ttm.FileContains('bar')) + + def test_cfn_init_with_ignore_errors_false(self): + self.mock_cmd_run(['su', 'root', '-c', '/bin/command1']).AndReturn( + FakePOpen('Doing something', 'error', -1)) + self.m.ReplayAll() + + md_data = {"AWS::CloudFormation::Init": {"config": {"commands": { + "00_foo": {"command": "/bin/command1", + "ignoreErrors": "false"}}}}} + + md = cfn_helper.Metadata('teststack', None) + self.assertTrue( + md.retrieve(meta_str=md_data, last_path=self.last_file)) + self.assertRaises(cfn_helper.CommandsHandlerRunError, md.cfn_init) + + def test_cfn_init_with_ignore_errors_true(self): + self.mock_cmd_run(['su', 'root', '-c', '/bin/command1']).AndReturn( + FakePOpen('Doing something', 'error', -1)) + self.mock_cmd_run(['su', 'root', '-c', '/bin/command2']).AndReturn( + FakePOpen('All good')) + self.m.ReplayAll() + + md_data = {"AWS::CloudFormation::Init": {"config": {"commands": { + "00_foo": {"command": "/bin/command1", + "ignoreErrors": "true"}, + "01_bar": {"command": "/bin/command2", + "ignoreErrors": "false"} + }}}} + + md = cfn_helper.Metadata('teststack', None) + self.assertTrue( + md.retrieve(meta_str=md_data, last_path=self.last_file)) + md.cfn_init() + + class TestSourcesHandler(MockPopenTestCase): def test_apply_sources_empty(self): sh = cfn_helper.SourcesHandler({})