예제 #1
0
    def test_handle_stack_event_returns_expected_event(self, _):
        event = {
            'PhysicalResourceId':
            'arn:aws:cloudformation:eu-west-1:1234567890:stack/my-stack/my-stack-id',
            'StackName':
            'my-stack',
            'LogicalResourceId':
            'my-stack',
            'StackId':
            'arn:aws:cloudformation:eu-west-1:1234567890:stack/my-stack/my-stack-id',
            'ResourceType':
            'AWS::CloudFormation::Stack',
            'Timestamp':
            datetime.datetime(2016, 4, 1, 8, 3, 27, 548000, tzinfo=tzutc()),
            'EventId':
            'my-event-id',
            'ResourceStatus':
            'CREATE_COMPLETE'
        }
        valid_from_timestamp = datetime.datetime(2016,
                                                 4,
                                                 1,
                                                 8,
                                                 3,
                                                 25,
                                                 548000,
                                                 tzinfo=tzutc())
        cfn = CloudFormation()

        result = cfn.handle_stack_event(event, valid_from_timestamp,
                                        "CREATE_COMPLETE", "my-stack")
        self.assertDictEqual(event, result)
예제 #2
0
 def __init__(self, config, dry_run=False):
     self.logger = get_logger(root=True)
     self.config = config
     self.cfn = CloudFormation(region=self.config.region, dry_run=dry_run)
     self.parameter_resolver = ParameterResolver(self.cfn,
                                                 region=self.config.region)
     self.cli_parameters = config.cli_params
예제 #3
0
    def test_update_stack_calls_cloudformation_api_properly_with_stack_policy(
            self, _, cloudformation_mock):
        stack = Mock(spec=CloudFormationStack)
        stack.name = "stack-name"
        stack.get_parameters_list.return_value = [('a', 'b')]
        stack.get_tags_list.return_value = [('any-tag', 'any-tag-value')]
        stack.parameters = {}
        stack.template = Mock(spec=CloudFormationTemplate)
        stack.template.name = "template-name"
        stack.template.get_template_json.return_value = {'key': 'value'}
        stack.service_role = None
        stack.stack_policy = "{foo:baa}"
        stack.timeout = 42

        cfn = CloudFormation()
        cfn.update_stack(stack)

        cloudformation_mock.return_value.set_stack_policy.assert_called_once_with(
            StackName='stack-name', StackPolicyBody='"{foo:baa}"')

        cloudformation_mock.return_value.update_stack.assert_called_once_with(
            Capabilities=['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM'],
            Parameters=[('a', 'b')],
            StackName='stack-name',
            Tags=[('any-tag', 'any-tag-value')],
            TemplateBody={'key': 'value'})
예제 #4
0
    def test_handle_stack_event_raises_exception_on_rollback_complete(self, _):
        event = {
            'PhysicalResourceId':
            'arn:aws:sns:eu-west-1:1234567890:my-topic',
            'StackName':
            'my-stack',
            'LogicalResourceId':
            'my-stack',
            'StackId':
            'arn:aws:cloudformation:eu-west-1:1234567890:stack/my-stack/my-stack-id',
            'ResourceType':
            'AWS::CloudFormation::Stack',
            'Timestamp':
            datetime.datetime(2016, 4, 1, 8, 3, 27, 548000, tzinfo=tzutc()),
            'EventId':
            'my-event-id',
            'ResourceStatus':
            'ROLLBACK_COMPLETE'
        }
        valid_from_timestamp = datetime.datetime(2016,
                                                 4,
                                                 1,
                                                 8,
                                                 3,
                                                 25,
                                                 548000,
                                                 tzinfo=tzutc())
        cfn = CloudFormation()

        with self.assertRaises(CfnStackActionFailedException):
            cfn.handle_stack_event(event, valid_from_timestamp,
                                   "CREATE_COMPLETE", "my-stack")
예제 #5
0
    def test_create_stack_calls_cloudformation_api_properly_with_stack_policy(self, _, cloudformation_mock):
        stack = Mock(spec=CloudFormationStack)
        stack.name = "stack-name"
        stack.get_parameters_list.return_value = [('a', 'b')]
        stack.get_tags_list.return_value = [('any-tag', 'any-tag-value')]
        stack.parameters = {}
        stack.template = Mock(spec=CloudFormationTemplate)
        stack.template.name = "template-name"
        stack.template.get_template_json.return_value = {'key': 'value'}
        stack.service_role = None
        stack.stack_policy = "{foo:baa}"
        stack.timeout = 42

        cfn = CloudFormation()
        cfn.create_stack(stack)

        cloudformation_mock.return_value.create_stack.assert_called_once_with(
            Capabilities=['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM'],
            OnFailure='ROLLBACK',
            Parameters=[('a', 'b')],
            StackName='stack-name',
            Tags=[('any-tag', 'any-tag-value')],
            TemplateBody={'key': 'value'},
            StackPolicyBody='"{foo:baa}"'
        )
예제 #6
0
    def test_create_stack_calls_cloudformation_api_properly_with_service_role(self, _, cloudformation_mock):
        stack = Mock(spec=CloudFormationStack)
        stack.name = "stack-name"
        stack.get_parameters_list.return_value = [('a', 'b')]
        stack.get_tags_list.return_value = [('any-tag', 'any-tag-value')]
        stack.parameters = {}
        stack.template = Mock(spec=CloudFormationTemplate)
        stack.template.name = "template-name"
        stack.template.get_template_json.return_value = {'key': 'value'}
        stack.service_role = "arn:aws:iam::1234567890:role/my-role"
        stack.stack_policy = None
        stack.failure_action = None
        stack.disable_rollback = False
        stack.timeout = 42

        cfn = CloudFormation()
        cfn.create_stack(stack)

        cloudformation_mock.return_value.create_stack.assert_called_once_with(
            Capabilities=['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM'],
            Parameters=[('a', 'b')],
            StackName='stack-name',
            Tags=[('any-tag', 'any-tag-value')],
            TemplateBody={'key': 'value'},
            RoleARN="arn:aws:iam::1234567890:role/my-role"
        )
예제 #7
0
    def test_create_stack_calls_cloudformation_api_properly_with_disable_rollback_true(self, _, cloudformation_mock):
        stack = Mock(spec=CloudFormationStack)
        stack.name = "stack-name"
        stack.get_parameters_list.return_value = [('a', 'b')]
        stack.get_tags_list.return_value = [('any-tag', 'any-tag-value')]
        stack.parameters = {}
        stack.template = Mock(spec=CloudFormationTemplate)
        stack.template.name = "template-name"
        stack.template.get_template_json.return_value = {'key': 'value'}
        stack.service_role = None
        stack.stack_policy = "{foo:baa}"
        stack.failure_action = "DO_NOTHING"
        stack.disable_rollback = "True"
        stack.timeout = 42

        cfn = CloudFormation()
        cfn.create_stack(stack)

        cloudformation_mock.return_value.create_stack.assert_called_once_with(
            Capabilities=['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM'],
            OnFailure='DO_NOTHING',
            DisableRollback=True,
            Parameters=[('a', 'b')],
            StackName='stack-name',
            Tags=[('any-tag', 'any-tag-value')],
            TemplateBody={'key': 'value'},
            StackPolicyBody='"{foo:baa}"'
        )
예제 #8
0
    def test_wait_for_stack_event_returns_on_start_event_with_valid_timestamp(
            self, cloudformation_mock):
        timestamp = datetime.datetime.utcnow()

        template_mock = Mock(spec=CloudFormationTemplate)
        template_mock.url = "foo.yml"
        template_mock.get_template_body_dict.return_value = {}

        event = StackEvent()
        event.resource_type = "AWS::CloudFormation::Stack"
        event.resource_status = "UPDATE_IN_PROGRESS"
        event.event_id = "123"
        event.timestamp = timestamp

        stack_events_mock = Mock()
        stack_events_mock.describe_stack_events.return_value = [event]

        cloudformation_mock.connect_to_region.return_value = stack_events_mock

        cfn = CloudFormation()
        event = cfn.wait_for_stack_events("foo",
                                          "UPDATE_IN_PROGRESS",
                                          timestamp - timedelta(seconds=10),
                                          timeout=10)

        self.assertEqual(timestamp, event.timestamp)
예제 #9
0
    def test_handle_stack_event_returns_none_on_rollback_in_progress_state(
            self, _):
        event = {
            'PhysicalResourceId':
            'arn:aws:sns:eu-west-1:1234567890:my-topic',
            'StackName':
            'my-stack',
            'LogicalResourceId':
            'my-stack',
            'StackId':
            'arn:aws:cloudformation:eu-west-1:1234567890:stack/my-stack/my-stack-id',
            'ResourceType':
            'AWS::CloudFormation::Stack',
            'Timestamp':
            datetime.datetime(2016, 4, 1, 8, 3, 27, 548000, tzinfo=tzutc()),
            'EventId':
            'my-event-id',
            'ResourceStatus':
            'ROLLBACK_IN_PROGRESS',
            'ResourceStatusReason':
            'Foo'
        }
        valid_from_timestamp = datetime.datetime(2016,
                                                 4,
                                                 1,
                                                 8,
                                                 3,
                                                 25,
                                                 548000,
                                                 tzinfo=tzutc())
        cfn = CloudFormation()

        result = cfn.handle_stack_event(event, valid_from_timestamp,
                                        "CREATE_COMPLETE", "my-stack")
        self.assertIsNone(result)
예제 #10
0
    def test_get_stack_parameters_dict_returns_empty_dict_for_empty_parameters(self, _, get_stack_mock):
        cfn = CloudFormation()

        stack_mock = Mock()
        stack_mock.parameters = []
        get_stack_mock.return_value = stack_mock

        result = cfn.get_stack_parameters_dict('foo')

        self.assertDictEqual({}, result)
예제 #11
0
    def test_validate_stack_is_ready_for_action_raises_proper_exception_on_boto_error(self, cloudformation_mock):
        cloudformation_mock.return_value.describe_stacks.side_effect = BotoServerError('400', 'Bad Request')

        stack = CloudFormationStack('', [], 'my-stack', 'my-region')

        cfn = CloudFormation()
        with self.assertRaises(CfnSphereBotoError):
            cfn.validate_stack_is_ready_for_action(stack)

        cloudformation_mock.return_value.describe_stacks.assert_called_once_with('my-stack')
예제 #12
0
    def test_get_stack_parameters_dict_returns_empty_dict_for_empty_parameters(self, _, get_stack_mock):
        cfn = CloudFormation()

        stack_mock = Mock()
        stack_mock.parameters = []
        get_stack_mock.return_value = stack_mock

        result = cfn.get_stack_parameters_dict('foo')

        self.assertDictEqual({}, result)
예제 #13
0
    def test_validate_stack_is_ready_for_action_passes_if_stack_is_in_rollback_complete_state(self, get_stack_mock):
        stack_mock = Mock()
        stack_mock.stack_name = "my-stack"
        stack_mock.stack_status = "ROLLBACK_COMPLETE"
        get_stack_mock.return_value = stack_mock

        stack = CloudFormationStack('', [], 'my-stack', 'my-region')

        cfn = CloudFormation()
        cfn.validate_stack_is_ready_for_action(stack)
예제 #14
0
    def test_validate_stack_is_ready_for_action_passes_if_stack_is_in_rollback_complete_state(self, get_stack_mock):
        stack_mock = Mock()
        stack_mock.stack_name = "my-stack"
        stack_mock.stack_status = "ROLLBACK_COMPLETE"
        get_stack_mock.return_value = stack_mock

        stack = CloudFormationStack('', [], 'my-stack', 'my-region')

        cfn = CloudFormation()
        cfn.validate_stack_is_ready_for_action(stack)
예제 #15
0
    def test_get_stacks_correctly_calls_aws_api(self, cloudformation_mock):
        stacks = [Mock(spec=Stack), Mock(spec=Stack)]

        result = ResultSet()
        result.extend(stacks)
        result.next_token = None
        cloudformation_mock.connect_to_region.return_value.describe_stacks.return_value = result

        cfn = CloudFormation()
        self.assertListEqual(stacks, cfn.get_stacks())
예제 #16
0
    def test_get_stacks_correctly_calls_aws_api(self, cloudformation_mock):
        stacks = [Mock(spec=Stack), Mock(spec=Stack)]

        result = ResultSet()
        result.extend(stacks)
        result.next_token = None
        cloudformation_mock.connect_to_region.return_value.describe_stacks.return_value = result

        cfn = CloudFormation()
        self.assertListEqual(stacks, cfn.get_stacks())
예제 #17
0
    def test_validate_stack_is_ready_for_action_raises_exception_on_unknown_stack_state(self, get_stack_mock):
        stack_mock = Mock()
        stack_mock.stack_name = "my-stack"
        stack_mock.stack_status = "FOO"
        get_stack_mock.return_value = stack_mock

        stack = CloudFormationStack('', [], 'my-stack', 'my-region')

        cfn = CloudFormation()
        with self.assertRaises(CfnStackActionFailedException):
            cfn.validate_stack_is_ready_for_action(stack)
예제 #18
0
    def test_get_stack_parameters_dict_returns_proper_dict(self, _, get_stack_mock):
        cfn = CloudFormation()

        stack_mock = Mock()
        stack_mock.parameters = [{"ParameterKey": "myKey1", "ParameterValue": "myValue1"},
                                 {"ParameterKey": "myKey2", "ParameterValue": "myValue2"}]
        get_stack_mock.return_value = stack_mock

        result = cfn.get_stack_parameters_dict('foo')

        self.assertDictEqual({'myKey1': 'myValue1', 'myKey2': 'myValue2'}, result)
예제 #19
0
    def test_validate_stack_is_ready_for_action_raises_exception_on_unknown_stack_state(self, get_stack_mock):
        stack_mock = Mock()
        stack_mock.stack_name = "my-stack"
        stack_mock.stack_status = "FOO"
        get_stack_mock.return_value = stack_mock

        stack = CloudFormationStack('', [], 'my-stack', 'my-region')

        cfn = CloudFormation()
        with self.assertRaises(CfnStackActionFailedException):
            cfn.validate_stack_is_ready_for_action(stack)
예제 #20
0
    def test_validate_stack_is_ready_for_action_raises_exception_on_create_in_progress(self, get_stack_mock):
        stack_mock = Mock()
        stack_mock.stack_name = "my-stack"
        stack_mock.stack_status = "CREATE_IN_PROGRESS"
        get_stack_mock.return_value = stack_mock

        stack = CloudFormationStack('', [], 'my-stack', 'my-region')

        cfn = CloudFormation()
        with self.assertRaises(CfnStackActionFailedException):
            cfn.validate_stack_is_ready_for_action(stack)
예제 #21
0
    def test_validate_stack_is_ready_for_action_raises_exception_on_create_in_progress(self, get_stack_mock):
        stack_mock = Mock()
        stack_mock.stack_name = "my-stack"
        stack_mock.stack_status = "CREATE_IN_PROGRESS"
        get_stack_mock.return_value = stack_mock

        stack = CloudFormationStack('', [], 'my-stack', 'my-region')

        cfn = CloudFormation()
        with self.assertRaises(CfnStackActionFailedException):
            cfn.validate_stack_is_ready_for_action(stack)
예제 #22
0
    def test_get_stack_parameters_dict_returns_proper_dict(self, _, get_stack_mock):
        cfn = CloudFormation()

        stack_mock = Mock()
        stack_mock.parameters = [{"ParameterKey": "myKey1", "ParameterValue": "myValue1"},
                                 {"ParameterKey": "myKey2", "ParameterValue": "myValue2"}]
        get_stack_mock.return_value = stack_mock

        result = cfn.get_stack_parameters_dict('foo')

        self.assertDictEqual({'myKey1': 'myValue1', 'myKey2': 'myValue2'}, result)
예제 #23
0
    def test_validate_stack_is_ready_for_action_passes_if_stack_is_in_good_state(self, cloudformation_mock):
        describe_stack_mock = Mock()
        describe_stack_mock.stack_status = "UPDATE_COMPLETE"
        describe_stack_mock.stack_name = "my-stack"

        cloudformation_mock.return_value.describe_stacks.return_value = [describe_stack_mock]

        stack = CloudFormationStack('', [], 'my-stack', 'my-region')

        cfn = CloudFormation()
        cfn.validate_stack_is_ready_for_action(stack)

        cloudformation_mock.return_value.describe_stacks.assert_called_once_with('my-stack')
예제 #24
0
    def test_validate_stack_is_ready_for_action_raises_proper_exception_on_boto_error(self, get_stack_mock):
        get_stack_mock.side_effect = CfnSphereBotoError(None)

        stack_mock = Mock()
        stack_mock.stack_name = "my-stack"
        stack_mock.stack_status = "UPDATE_COMPLETE"
        get_stack_mock.return_value = stack_mock

        stack = CloudFormationStack('', [], 'my-stack', 'my-region')

        cfn = CloudFormation()
        with self.assertRaises(CfnSphereBotoError):
            cfn.validate_stack_is_ready_for_action(stack)
예제 #25
0
    def test_validate_stack_is_ready_for_action_raises_proper_exception_on_boto_error(self, get_stack_mock):
        get_stack_mock.side_effect = CfnSphereBotoError(None)

        stack_mock = Mock()
        stack_mock.stack_name = "my-stack"
        stack_mock.stack_status = "UPDATE_COMPLETE"
        get_stack_mock.return_value = stack_mock

        stack = CloudFormationStack('', [], 'my-stack', 'my-region')

        cfn = CloudFormation()
        with self.assertRaises(CfnSphereBotoError):
            cfn.validate_stack_is_ready_for_action(stack)
예제 #26
0
    def test_validate_stack_is_ready_for_action_raises_proper_exception_on_boto_error(
            self, cloudformation_mock):
        cloudformation_mock.return_value.describe_stacks.side_effect = BotoServerError(
            '400', 'Bad Request')

        stack = CloudFormationStack('', [], 'my-stack', 'my-region')

        cfn = CloudFormation()
        with self.assertRaises(CfnSphereBotoError):
            cfn.validate_stack_is_ready_for_action(stack)

        cloudformation_mock.return_value.describe_stacks.assert_called_once_with(
            'my-stack')
예제 #27
0
    def test_delete_stack_calls_wait_properly(self, wait_mock, _a, _b):
        stack = Mock(spec=CloudFormationStack)
        stack.name = "stack-name"
        stack.get_parameters_list.return_value = []
        stack.parameters = {}
        stack.template = Mock(spec=CloudFormationTemplate)
        stack.template.name = "template-name"

        stack.timeout = 42

        cfn = CloudFormation()
        cfn.delete_stack(stack)

        wait_mock.assert_called_once_with(stack.name, 'delete', stack.timeout)
예제 #28
0
    def test_validate_stack_is_ready_for_action_raises_exception_on_bad_stack_state(self, cloudformation_mock):
        describe_stack_mock = Mock()
        describe_stack_mock.stack_status = "UPDATE_IN_PROGRESS"
        describe_stack_mock.stack_name = "my-stack"

        cloudformation_mock.return_value.describe_stacks.return_value = [describe_stack_mock]

        stack = CloudFormationStack('', [], 'my-stack', 'my-region')

        cfn = CloudFormation()
        with self.assertRaises(CfnStackActionFailedException):
            cfn.validate_stack_is_ready_for_action(stack)

        cloudformation_mock.return_value.describe_stacks.assert_called_once_with('my-stack')
예제 #29
0
    def test_get_stacks_correctly_aggregates_paged_results(self, cloudformation_mock):
        stacks_1 = [Mock(spec=Stack), Mock(spec=Stack)]
        stacks_2 = [Mock(spec=Stack), Mock(spec=Stack)]

        result_1 = ResultSet()
        result_1.extend(stacks_1)
        result_1.next_token = "my-next-token"

        result_2 = ResultSet()
        result_2.extend(stacks_2)
        result_2.next_token = None

        cloudformation_mock.connect_to_region.return_value.describe_stacks.side_effect = [result_1, result_2]

        cfn = CloudFormation()
        self.assertListEqual(stacks_1 + stacks_2, cfn.get_stacks())
예제 #30
0
 def test_stack_exists_returns_false_for_non_existing_stack(
         self, get_stack_mock):
     get_stack_mock.side_effect = ClientError(
         {"Error": {
             "Message": "Stack with id stack3 does not exist"
         }}, "Foo")
     self.assertFalse(CloudFormation().stack_exists("stack3"))
예제 #31
0
    def test_handle_stack_event_returns_none_if_event_is_no_stack_event(self, _):
        event = {
            'PhysicalResourceId': 'arn:aws:sns:eu-west-1:1234567890:my-topic',
            'StackName': 'my-stack',
            'LogicalResourceId': 'Topic',
            'StackId': 'arn:aws:cloudformation:eu-west-1:1234567890:stack/my-stack/my-stack-id',
            'ResourceType': 'AWS::SNS::Topic',
            'Timestamp': datetime.datetime(2016, 4, 1, 8, 3, 27, 548000, tzinfo=tzutc()),
            'EventId': 'my-event-id',
            'ResourceStatus': 'CREATE_COMPLETE'
        }
        valid_from_timestamp = datetime.datetime(2016, 4, 1, 8, 3, 25, 548000, tzinfo=tzutc())
        cfn = CloudFormation()

        result = cfn.handle_stack_event(event, valid_from_timestamp, "CREATE_COMPLETE", "my-stack")
        self.assertIsNone(result)
예제 #32
0
    def test_handle_stack_event_raises_exception_on_rollback_complete(self, _):
        event = {
            'PhysicalResourceId': 'arn:aws:sns:eu-west-1:1234567890:my-topic',
            'StackName': 'my-stack',
            'LogicalResourceId': 'VPC',
            'StackId': 'arn:aws:cloudformation:eu-west-1:1234567890:stack/my-stack/my-stack-id',
            'ResourceType': 'AWS::CloudFormation::Stack',
            'Timestamp': datetime.datetime(2016, 4, 1, 8, 3, 27, 548000, tzinfo=tzutc()),
            'EventId': 'my-event-id',
            'ResourceStatus': 'ROLLBACK_COMPLETE'
        }
        valid_from_timestamp = datetime.datetime(2016, 4, 1, 8, 3, 25, 548000, tzinfo=tzutc())
        cfn = CloudFormation()

        with self.assertRaises(CfnStackActionFailedException):
            cfn.handle_stack_event(event, valid_from_timestamp, "CREATE_COMPLETE")
예제 #33
0
    def test_handle_stack_event_returns_none_if_event_has_not_expected_state(self, _):
        event = {
            'PhysicalResourceId': 'arn:aws:cloudformation:eu-west-1:1234567890:stack/my-stack/my-stack-id',
            'StackName': 'my-stack',
            'LogicalResourceId': 'cfn-sphere-test-vpc',
            'StackId': 'arn:aws:cloudformation:eu-west-1:1234567890:stack/my-stack/my-stack-id',
            'ResourceType': 'AWS::CloudFormation::Stack',
            'Timestamp': datetime.datetime(2016, 4, 1, 8, 3, 27, 548000, tzinfo=tzutc()),
            'EventId': 'my-event-id',
            'ResourceStatus': 'CREATE_IN_PROGRESS'
        }
        valid_from_timestamp = datetime.datetime(2016, 4, 1, 8, 3, 25, 548000, tzinfo=tzutc())
        cfn = CloudFormation()

        result = cfn.handle_stack_event(event, valid_from_timestamp, "CREATE_COMPLETE")
        self.assertIsNone(result)
예제 #34
0
    def test_validate_stack_is_ready_for_action_passes_if_stack_is_in_good_state(
            self, cloudformation_mock):
        describe_stack_mock = Mock()
        describe_stack_mock.stack_status = "UPDATE_COMPLETE"
        describe_stack_mock.stack_name = "my-stack"

        cloudformation_mock.return_value.describe_stacks.return_value = [
            describe_stack_mock
        ]

        stack = CloudFormationStack('', [], 'my-stack', 'my-region')

        cfn = CloudFormation()
        cfn.validate_stack_is_ready_for_action(stack)

        cloudformation_mock.return_value.describe_stacks.assert_called_once_with(
            'my-stack')
예제 #35
0
    def test_handle_stack_event_returns_none_on_rollback_in_progress_state(self, _):
        event = {
            'PhysicalResourceId': 'arn:aws:sns:eu-west-1:1234567890:my-topic',
            'StackName': 'my-stack',
            'LogicalResourceId': 'VPC',
            'StackId': 'arn:aws:cloudformation:eu-west-1:1234567890:stack/my-stack/my-stack-id',
            'ResourceType': 'AWS::CloudFormation::Stack',
            'Timestamp': datetime.datetime(2016, 4, 1, 8, 3, 27, 548000, tzinfo=tzutc()),
            'EventId': 'my-event-id',
            'ResourceStatus': 'ROLLBACK_IN_PROGRESS',
            'ResourceStatusReason': 'Foo'
        }
        valid_from_timestamp = datetime.datetime(2016, 4, 1, 8, 3, 25, 548000, tzinfo=tzutc())
        cfn = CloudFormation()

        result = cfn.handle_stack_event(event, valid_from_timestamp, "CREATE_COMPLETE")
        self.assertIsNone(result)
예제 #36
0
    def test_get_stack_parameters_dict_returns_proper_dict(self, _, get_stack_mock):
        cfn = CloudFormation()

        parameter_1 = Mock()
        parameter_1.key = "myKey1"
        parameter_1.value = "myValue1"
        parameter_2 = Mock()
        parameter_2.key = "myKey2"
        parameter_2.value = "myValue2"

        stack_mock = Mock()
        stack_mock.parameters = [parameter_1, parameter_2]
        get_stack_mock.return_value = stack_mock

        result = cfn.get_stack_parameters_dict('foo')

        self.assertDictEqual({'myKey1': 'myValue1', 'myKey2': 'myValue2'}, result)
예제 #37
0
 def test_get_stacks_dict_always_returns_empty_list_parameters_and_outputs(
         self, get_stack_descriptions_mock):
     get_stack_descriptions_mock.return_value = [{"StackName": "Foo"}]
     self.assertEqual({'Foo': {
         'outputs': [],
         'parameters': []
     }},
                      CloudFormation().get_stacks_dict())
예제 #38
0
    def test_validate_stack_is_ready_for_action_raises_exception_on_bad_stack_state(
            self, cloudformation_mock):
        describe_stack_mock = Mock()
        describe_stack_mock.stack_status = "UPDATE_IN_PROGRESS"
        describe_stack_mock.stack_name = "my-stack"

        cloudformation_mock.return_value.describe_stacks.return_value = [
            describe_stack_mock
        ]

        stack = CloudFormationStack('', [], 'my-stack', 'my-region')

        cfn = CloudFormation()
        with self.assertRaises(CfnStackActionFailedException):
            cfn.validate_stack_is_ready_for_action(stack)

        cloudformation_mock.return_value.describe_stacks.assert_called_once_with(
            'my-stack')
예제 #39
0
    def test_get_stacks_correctly_aggregates_paged_results(
            self, cloudformation_mock):
        stacks_1 = [Mock(spec=Stack), Mock(spec=Stack)]
        stacks_2 = [Mock(spec=Stack), Mock(spec=Stack)]

        result_1 = ResultSet()
        result_1.extend(stacks_1)
        result_1.next_token = "my-next-token"

        result_2 = ResultSet()
        result_2.extend(stacks_2)
        result_2.next_token = None

        cloudformation_mock.connect_to_region.return_value.describe_stacks.side_effect = [
            result_1, result_2
        ]

        cfn = CloudFormation()
        self.assertListEqual(stacks_1 + stacks_2, cfn.get_stacks())
예제 #40
0
    def test_delete_stack_calls_cloudformation_api_properly(
            self, _, cloudformation_mock):
        stack = Mock(spec=CloudFormationStack)
        stack.name = "stack-name"
        stack.get_parameters_list.return_value = [('a', 'b')]
        stack.get_tags_list.return_value = [('any-tag', 'any-tag-value')]
        stack.parameters = {}
        stack.template = Mock(spec=CloudFormationTemplate)
        stack.template.name = "template-name"
        stack.template.get_template_json.return_value = {'key': 'value'}
        stack.service_role = None
        stack.stack_policy = "{foo:baa}"
        stack.timeout = 42

        cfn = CloudFormation()
        cfn.delete_stack(stack)

        cloudformation_mock.return_value.delete_stack.assert_called_once_with(
            StackName=stack.name)
예제 #41
0
 def test_is_boto_stack_does_not_exist_exception_returns_true_for_message2(
         self):
     exception = Mock(spec=ClientError)
     exception.response = {
         "Error": {
             "Message": "Stack with id foo does not exist"
         }
     }
     self.assertTrue(
         CloudFormation.is_boto_stack_does_not_exist_exception(exception))
예제 #42
0
    def test_update_stack_calls_cloudformation_api_properly(self, _, cloudformation_mock):
        stack = Mock(spec=CloudFormationStack)
        stack.name = "stack-name"
        stack.get_parameters_list.return_value = [('a', 'b')]
        stack.tags = [('any-tag', 'any-tag-value')]
        stack.parameters = {}
        stack.template = Mock(spec=CloudFormationTemplate)
        stack.template.name = "template-name"
        stack.template.get_template_json.return_value = {'key': 'value'}
        stack.timeout = 42

        cfn = CloudFormation()
        cfn.update_stack(stack)

        cloudformation_mock.return_value.update_stack.assert_called_once_with('stack-name',
                                                                              capabilities=['CAPABILITY_IAM'],
                                                                              parameters=[('a', 'b')],
                                                                              tags=[('any-tag', 'any-tag-value')],
                                                                              template_body={'key': 'value'})
예제 #43
0
 def test_is_boto_no_update_required_exception_returns_true_for_message(
         self):
     exception = Mock(spec=ClientError)
     exception.response = {
         "Error": {
             "Message": "No updates are to be performed."
         }
     }
     self.assertTrue(
         CloudFormation.is_boto_no_update_required_exception(exception))
예제 #44
0
    def test_wait_for_stack_event_returns_on_update_complete(self, cloudformation_mock):
        timestamp = datetime.datetime.utcnow()

        template_mock = Mock(spec=CloudFormationTemplate)
        template_mock.url = "foo.yml"
        template_mock.get_template_body_dict.return_value = {}

        event = StackEvent()
        event.resource_type = "AWS::CloudFormation::Stack"
        event.resource_status = "UPDATE_COMPLETE"
        event.event_id = "123"
        event.timestamp = timestamp

        stack_events_mock = Mock()
        stack_events_mock.describe_stack_events.return_value = [event]

        cloudformation_mock.connect_to_region.return_value = stack_events_mock

        cfn = CloudFormation()
        cfn.wait_for_stack_events("foo", "UPDATE_COMPLETE", timestamp - timedelta(seconds=10),
                                  timeout=10)
예제 #45
0
    def test_update_stack_calls_cloudformation_api_properly(
            self, _, cloudformation_mock):
        stack = Mock(spec=CloudFormationStack)
        stack.name = "stack-name"
        stack.get_parameters_list.return_value = [('a', 'b')]
        stack.tags = [('any-tag', 'any-tag-value')]
        stack.parameters = {}
        stack.template = Mock(spec=CloudFormationTemplate)
        stack.template.name = "template-name"
        stack.template.get_template_json.return_value = {'key': 'value'}
        stack.timeout = 42

        cfn = CloudFormation()
        cfn.update_stack(stack)

        cloudformation_mock.return_value.update_stack.assert_called_once_with(
            'stack-name',
            capabilities=['CAPABILITY_IAM'],
            parameters=[('a', 'b')],
            tags=[('any-tag', 'any-tag-value')],
            template_body={'key': 'value'})
예제 #46
0
    def test_get_stack_parameters_dict_returns_proper_dict(
            self, _, get_stack_mock):
        cfn = CloudFormation()

        parameter_1 = Mock()
        parameter_1.key = "myKey1"
        parameter_1.value = "myValue1"
        parameter_2 = Mock()
        parameter_2.key = "myKey2"
        parameter_2.value = "myValue2"

        stack_mock = Mock()
        stack_mock.parameters = [parameter_1, parameter_2]
        get_stack_mock.return_value = stack_mock

        result = cfn.get_stack_parameters_dict('foo')

        self.assertDictEqual({
            'myKey1': 'myValue1',
            'myKey2': 'myValue2'
        }, result)
예제 #47
0
 def test_get_stacks_dict_returns_stack_dict(self,
                                             get_stack_descriptions_mock):
     get_stack_descriptions_mock.return_value = [{
         "StackName": "Foo",
         "Parameters": [],
         "Outputs": []
     }]
     self.assertEqual({'Foo': {
         'outputs': [],
         'parameters': []
     }},
                      CloudFormation().get_stacks_dict())
예제 #48
0
    def test_wait_for_stack_event_raises_exception_on_rollback(self, cloudformation_mock):
        timestamp = datetime.datetime.utcnow()

        template_mock = Mock(spec=CloudFormationTemplate)
        template_mock.url = "foo.yml"
        template_mock.get_template_body_dict.return_value = {}

        event = StackEvent()
        event.resource_type = "AWS::CloudFormation::Stack"
        event.resource_status = "ROLLBACK_COMPLETE"
        event.event_id = "123"
        event.timestamp = timestamp

        stack_events_mock = Mock()
        stack_events_mock.describe_stack_events.return_value = [event]

        cloudformation_mock.connect_to_region.return_value = stack_events_mock

        cfn = CloudFormation()
        with self.assertRaises(Exception):
            cfn.wait_for_stack_events("foo", ["UPDATE_COMPLETE"], timestamp - timedelta(seconds=10),
                                      timeout=10)
예제 #49
0
    def test_set_stack_policy_calls_cloudformation_api_properly(
            self, cloudformation_mock):
        stack = Mock(spec=CloudFormationStack)
        stack.name = "stack-name"
        stack.get_parameters_list.return_value = [('a', 'b')]
        stack.get_tags_list.return_value = [('any-tag', 'any-tag-value')]
        stack.parameters = {}
        stack.template = Mock(spec=CloudFormationTemplate)
        stack.template.name = "template-name"
        stack.template.get_template_json.return_value = {'key': 'value'}
        stack.service_role = None
        stack.stack_policy = """{"Statement":[{"Effect":"Allow"}]}"""
        stack.failure_action = None
        stack.disable_rollback = False
        stack.timeout = 42

        cfn = CloudFormation()
        cfn._set_stack_policy(stack)

        cloudformation_mock.return_value.set_stack_policy.assert_called_once_with(
            StackName='stack-name',
            StackPolicyBody='"{\\"Statement\\":[{\\"Effect\\":\\"Allow\\"}]}"')
예제 #50
0
    def test_create_stack_calls_cloudformation_api_properly(self, _, cloudformation_mock):
        stack = Mock(spec=CloudFormationStack)
        stack.name = "stack-name"
        stack.get_parameters_list.return_value = [('a', 'b')]
        stack.get_tags_list.return_value = [('any-tag', 'any-tag-value')]
        stack.parameters = {}
        stack.template = Mock(spec=CloudFormationTemplate)
        stack.template.name = "template-name"
        stack.template.get_template_json.return_value = {'key': 'value'}
        stack.timeout = 42

        cfn = CloudFormation()
        cfn.create_stack(stack)

        cloudformation_mock.return_value.create_stack.assert_called_once_with(
            Capabilities=['CAPABILITY_IAM'],
            OnFailure='DELETE',
            Parameters=[('a', 'b')],
            StackName='stack-name',
            Tags=[('any-tag', 'any-tag-value')],
            TemplateBody={'key': 'value'},
            TimeoutInMinutes=42)
예제 #51
0
    def test_wait_for_stack_event_returns_on_start_event_with_valid_timestamp(self, cloudformation_mock):
        timestamp = datetime.datetime.utcnow()

        template_mock = Mock(spec=CloudFormationTemplate)
        template_mock.url = "foo.yml"
        template_mock.get_template_body_dict.return_value = {}

        event = StackEvent()
        event.resource_type = "AWS::CloudFormation::Stack"
        event.resource_status = "UPDATE_IN_PROGRESS"
        event.event_id = "123"
        event.timestamp = timestamp

        stack_events_mock = Mock()
        stack_events_mock.describe_stack_events.return_value = [event]

        cloudformation_mock.connect_to_region.return_value = stack_events_mock

        cfn = CloudFormation()
        event = cfn.wait_for_stack_events("foo", "UPDATE_IN_PROGRESS",
                                          timestamp - timedelta(seconds=10),
                                          timeout=10)

        self.assertEqual(timestamp, event.timestamp)
예제 #52
0
    def test_wait_for_stack_event_returns_on_update_complete(
            self, cloudformation_mock):
        timestamp = datetime.datetime.utcnow()

        template_mock = Mock(spec=CloudFormationTemplate)
        template_mock.url = "foo.yml"
        template_mock.get_template_body_dict.return_value = {}

        event = StackEvent()
        event.resource_type = "AWS::CloudFormation::Stack"
        event.resource_status = "UPDATE_COMPLETE"
        event.event_id = "123"
        event.timestamp = timestamp

        stack_events_mock = Mock()
        stack_events_mock.describe_stack_events.return_value = [event]

        cloudformation_mock.connect_to_region.return_value = stack_events_mock

        cfn = CloudFormation()
        cfn.wait_for_stack_events("foo",
                                  "UPDATE_COMPLETE",
                                  timestamp - timedelta(seconds=10),
                                  timeout=10)
예제 #53
0
 def __init__(self, config):
     self.logger = get_logger(root=True)
     self.config = config
     self.cfn = CloudFormation(region=self.config.region)
     self.parameter_resolver = ParameterResolver(region=self.config.region)
예제 #54
0
class StackActionHandler(object):
    def __init__(self, config):
        self.logger = get_logger(root=True)
        self.config = config
        self.cfn = CloudFormation(region=self.config.region)
        self.parameter_resolver = ParameterResolver(region=self.config.region)

    def create_or_update_stacks(self):
        existing_stacks = self.cfn.get_stack_names()
        desired_stacks = self.config.stacks
        stack_processing_order = DependencyResolver().get_stack_order(desired_stacks)

        if len(stack_processing_order) > 1:
            self.logger.info(
                "Will process stacks in the following order: {0}".format(", ".join(stack_processing_order)))

        for stack_name in stack_processing_order:
            stack_config = self.config.stacks.get(stack_name)

            raw_template = FileLoader.get_file_from_url(stack_config.template_url, stack_config.working_dir)
            template = CloudFormationTemplateTransformer.transform_template(raw_template)

            combined_tags = dict(self.config.tags)
            combined_tags.update(stack_config.tags)

            parameters = self.parameter_resolver.resolve_parameter_values(stack_config.parameters, stack_name)
            stack = CloudFormationStack(template=template, parameters=parameters, tags=combined_tags,
                                        name=stack_name, region=self.config.region, timeout=stack_config.timeout)

            if stack_name in existing_stacks:

                self.cfn.validate_stack_is_ready_for_action(stack)
                self.cfn.update_stack(stack)
            else:
                self.cfn.create_stack(stack)

            CustomResourceHandler.process_post_resources(stack)

    def delete_stacks(self):
        existing_stacks = self.cfn.get_stack_names()
        stacks = self.config.stacks

        stack_processing_order = DependencyResolver().get_stack_order(stacks)
        stack_processing_order.reverse()

        self.logger.info("Will delete stacks in the following order: {0}".format(", ".join(stack_processing_order)))

        for stack_name in stack_processing_order:
            if stack_name in existing_stacks:
                stack = CloudFormationStack(None, None, stack_name, None, None)
                self.cfn.validate_stack_is_ready_for_action(stack)
                self.cfn.delete_stack(stack)
            else:
                self.logger.info("Stack {0} is already deleted".format(stack_name))
예제 #55
0
 def test_is_boto_stack_does_not_exist_exception_returns_true_for_message2(self):
     exception = Mock(spec=ClientError)
     exception.response = {"Error": {"Message": "Stack with id foo does not exist"}}
     self.assertTrue(CloudFormation.is_boto_stack_does_not_exist_exception(exception))
예제 #56
0
 def test_is_boto_stack_does_not_exist_exception_returns_false_with_other_message(self):
     exception = Mock(spec=ClientError)
     exception.response = {"Error": {"Message": "Other error"}}
     self.assertFalse(CloudFormation.is_boto_stack_does_not_exist_exception(exception))
예제 #57
0
 def test_is_boto_stack_does_not_exist_exception_returns_false_with_other_exception(self):
     exception = Mock(spec=Exception)
     exception.message = "No updates are to be performed."
     self.assertFalse(CloudFormation.is_boto_stack_does_not_exist_exception(exception))
예제 #58
0
 def test_is_boto_no_update_required_exception_returns_true_for_message(self):
     exception = Mock(spec=ClientError)
     exception.response = {"Error": {"Message": "No updates are to be performed."}}
     self.assertTrue(CloudFormation.is_boto_no_update_required_exception(exception))
예제 #59
0
 def test_is_boto_no_update_required_exception_returns_false_without_message(self):
     exception = Mock(spec=ClientError)
     exception.response = {"Error": {"Message": "Something went wrong"}}
     self.assertFalse(CloudFormation.is_boto_no_update_required_exception(exception))