示例#1
0
class TestProviderInteractiveMode(unittest.TestCase):
    """Tests for runway.cfngin.providers.aws.default interactive mode."""

    def setUp(self) -> None:
        """Run before tests."""
        region = "us-east-1"
        self.session = get_session(region=region)
        self.provider = Provider(self.session, interactive=True, recreate_failed=True)
        self.stubber = Stubber(self.provider.cloudformation)

    @patch("runway.cfngin.ui.get_raw_input")
    def test_interactive_destroy_stack(self, patched_input: MagicMock) -> None:
        """Test interactive_destroy_stack."""
        stack_name = "fake-stack"
        stack = {"StackName": stack_name}
        patched_input.return_value = "y"

        self.stubber.add_response("delete_stack", {}, stack)

        with self.stubber:
            self.assertIsNone(self.provider.interactive_destroy_stack(stack_name))
            self.stubber.assert_no_pending_responses()

    @patch("runway.cfngin.providers.aws.default.Provider.update_termination_protection")
    @patch("runway.cfngin.ui.get_raw_input")
    def test_interactive_destroy_stack_termination_protected(
        self, patched_input: MagicMock, patched_update_term: MagicMock
    ) -> None:
        """Test interactive_destroy_stack with termination protection."""
        stack_name = "fake-stack"
        stack = {"StackName": stack_name}
        patched_input.return_value = "y"

        self.stubber.add_client_error(
            "delete_stack", service_message="TerminationProtection"
        )
        self.stubber.add_response("delete_stack", {}, stack)

        with self.stubber:
            self.provider.interactive_destroy_stack(stack_name, approval="y")
        self.stubber.assert_no_pending_responses()
        patched_input.assert_called_once()
        patched_update_term.assert_called_once_with(stack_name, False)

    @patch("runway.cfngin.ui.get_raw_input")
    def test_destroy_stack_canceled(self, patched_input: MagicMock) -> None:
        """Test destroy stack canceled."""
        patched_input.return_value = "n"

        with self.assertRaises(exceptions.CancelExecution):
            stack = {"StackName": "MockStack"}
            self.provider.destroy_stack(stack)  # type: ignore

    def test_successful_init(self) -> None:
        """Test successful init."""
        replacements = True
        provider = Provider(
            self.session, interactive=True, replacements_only=replacements
        )
        self.assertEqual(provider.replacements_only, replacements)

    @patch("runway.cfngin.providers.aws.default.Provider.update_termination_protection")
    @patch("runway.cfngin.providers.aws.default.ask_for_approval")
    def test_update_stack_execute_success_no_stack_policy(
        self, patched_approval: MagicMock, patched_update_term: MagicMock
    ) -> None:
        """Test update stack execute success no stack policy."""
        stack_name = "my-fake-stack"

        self.stubber.add_response(
            "create_change_set", {"Id": "CHANGESETID", "StackId": "STACKID"}
        )
        changes = [generate_change()]

        self.stubber.add_response(
            "describe_change_set",
            generate_change_set_response(
                status="CREATE_COMPLETE", execution_status="AVAILABLE", changes=changes
            ),
        )

        self.stubber.add_response("execute_change_set", {})

        with self.stubber:
            self.provider.update_stack(
                fqn=stack_name,
                template=Template(url="http://fake.template.url.com/"),
                old_parameters=[],
                parameters=[],
                tags=[],
            )

        patched_approval.assert_called_once_with(
            full_changeset=changes, params_diff=[], include_verbose=True, fqn=stack_name
        )
        patched_update_term.assert_called_once_with(stack_name, False)

    @patch("runway.cfngin.providers.aws.default.Provider.update_termination_protection")
    @patch("runway.cfngin.providers.aws.default.ask_for_approval")
    def test_update_stack_execute_success_with_stack_policy(
        self, patched_approval: MagicMock, patched_update_term: MagicMock
    ) -> None:
        """Test update stack execute success with stack policy."""
        stack_name = "my-fake-stack"

        self.stubber.add_response(
            "create_change_set", {"Id": "CHANGESETID", "StackId": "STACKID"}
        )
        changes = [generate_change()]

        self.stubber.add_response(
            "describe_change_set",
            generate_change_set_response(
                status="CREATE_COMPLETE", execution_status="AVAILABLE", changes=changes
            ),
        )

        self.stubber.add_response("set_stack_policy", {})

        self.stubber.add_response("execute_change_set", {})

        with self.stubber:
            self.provider.update_stack(
                fqn=stack_name,
                template=Template(url="http://fake.template.url.com/"),
                old_parameters=[],
                parameters=[],
                tags=[],
                stack_policy=Template(body="{}"),
            )

        patched_approval.assert_called_once_with(
            full_changeset=changes, params_diff=[], include_verbose=True, fqn=stack_name
        )
        patched_update_term.assert_called_once_with(stack_name, False)

    def test_select_destroy_method(self) -> None:
        """Test select destroy method."""
        for i in [
            [{"force_interactive": False}, self.provider.interactive_destroy_stack],
            [{"force_interactive": True}, self.provider.interactive_destroy_stack],
        ]:
            self.assertEqual(
                self.provider.select_destroy_method(**i[0]), i[1]  # type: ignore
            )

    def test_select_update_method(self) -> None:
        """Test select update method."""
        for i in [
            [
                {"force_interactive": False, "force_change_set": False},
                self.provider.interactive_update_stack,
            ],
            [
                {"force_interactive": True, "force_change_set": False},
                self.provider.interactive_update_stack,
            ],
            [
                {"force_interactive": False, "force_change_set": True},
                self.provider.interactive_update_stack,
            ],
            [
                {"force_interactive": True, "force_change_set": True},
                self.provider.interactive_update_stack,
            ],
        ]:
            self.assertEqual(
                self.provider.select_update_method(**i[0]), i[1]  # type: ignore
            )

    @patch("runway.cfngin.providers.aws.default.output_full_changeset")
    @patch("runway.cfngin.providers.aws.default.output_summary")
    def test_get_stack_changes_interactive(
        self, mock_output_summary: MagicMock, mock_output_full_cs: MagicMock
    ) -> None:
        """Test get stack changes interactive."""
        stack_name = "MockStack"
        mock_stack = generate_stack_object(stack_name)

        self.stubber.add_response(
            "describe_stacks", {"Stacks": [generate_describe_stacks_stack(stack_name)]}
        )
        self.stubber.add_response(
            "get_template", generate_get_template("cfn_template.yaml")
        )
        self.stubber.add_response(
            "create_change_set", {"Id": "CHANGESETID", "StackId": stack_name}
        )
        changes = [generate_change()]
        self.stubber.add_response(
            "describe_change_set",
            generate_change_set_response(
                status="CREATE_COMPLETE", execution_status="AVAILABLE", changes=changes
            ),
        )
        self.stubber.add_response("delete_change_set", {})
        self.stubber.add_response(
            "describe_stacks", {"Stacks": [generate_describe_stacks_stack(stack_name)]}
        )

        with self.stubber:
            self.provider.get_stack_changes(
                stack=mock_stack,
                template=Template(url="http://fake.template.url.com/"),
                parameters=[],
                tags=[],
            )

        mock_output_summary.assert_called_with(
            stack_name, "changes", changes, [], replacements_only=False
        )
        mock_output_full_cs.assert_called_with(
            full_changeset=changes, params_diff=[], fqn=stack_name
        )
示例#2
0
class TestProviderInteractiveMode(unittest.TestCase):
    """Tests for runway.cfngin.providers.aws.default interactive mode."""
    def setUp(self):
        """Run before tests."""
        region = "us-east-1"
        self.session = get_session(region=region)
        self.provider = Provider(self.session,
                                 interactive=True,
                                 recreate_failed=True)
        self.stubber = Stubber(self.provider.cloudformation)

    @patch('runway.cfngin.ui.get_raw_input')
    def test_interactive_destroy_stack(self, patched_input):
        """Test interactive_destroy_stack."""
        stack_name = 'fake-stack'
        stack = {'StackName': stack_name}
        patched_input.return_value = 'y'

        self.stubber.add_response('delete_stack', {}, stack)

        with self.stubber:
            self.assertIsNone(
                self.provider.interactive_destroy_stack(stack_name))
            self.stubber.assert_no_pending_responses()

    @patch(
        'runway.cfngin.providers.aws.default.Provider.update_termination_protection'
    )
    @patch('runway.cfngin.ui.get_raw_input')
    def test_interactive_destroy_stack_termination_protected(
            self, patched_input, patched_update_term):
        """Test interactive_destroy_stack with termination protection."""
        stack_name = 'fake-stack'
        stack = {'StackName': stack_name}
        patched_input.return_value = 'y'

        self.stubber.add_client_error('delete_stack',
                                      service_message='TerminationProtection')
        self.stubber.add_response('delete_stack', {}, stack)

        with self.stubber:
            self.provider.interactive_destroy_stack(stack_name, approval='y')
        self.stubber.assert_no_pending_responses()
        patched_input.assert_called_once()
        patched_update_term.assert_called_once_with(stack_name, False)

    @patch('runway.cfngin.ui.get_raw_input')
    def test_destroy_stack_canceled(self, patched_input):
        """Test destroy stack canceled."""
        stack = {'StackName': 'MockStack'}
        patched_input.return_value = 'n'

        with self.assertRaises(exceptions.CancelExecution):
            self.provider.destroy_stack(stack)

    def test_successful_init(self):
        """Test successful init."""
        replacements = True
        provider = Provider(self.session,
                            interactive=True,
                            replacements_only=replacements)
        self.assertEqual(provider.replacements_only, replacements)

    @patch(
        'runway.cfngin.providers.aws.default.Provider.update_termination_protection'
    )
    @patch("runway.cfngin.providers.aws.default.ask_for_approval")
    def test_update_stack_execute_success_no_stack_policy(
            self, patched_approval, patched_update_term):
        """Test update stack execute success no stack policy."""
        stack_name = "my-fake-stack"

        self.stubber.add_response("create_change_set", {
            'Id': 'CHANGESETID',
            'StackId': 'STACKID'
        })
        changes = [generate_change()]

        self.stubber.add_response(
            "describe_change_set",
            generate_change_set_response(
                status="CREATE_COMPLETE",
                execution_status="AVAILABLE",
                changes=changes,
            ))

        self.stubber.add_response("execute_change_set", {})

        with self.stubber:
            self.provider.update_stack(
                fqn=stack_name,
                template=Template(url="http://fake.template.url.com/"),
                old_parameters=[],
                parameters=[],
                tags=[])

        patched_approval.assert_called_once_with(full_changeset=changes,
                                                 params_diff=[],
                                                 include_verbose=True,
                                                 fqn=stack_name)
        patched_update_term.assert_called_once_with(stack_name, False)

    @patch(
        'runway.cfngin.providers.aws.default.Provider.update_termination_protection'
    )
    @patch("runway.cfngin.providers.aws.default.ask_for_approval")
    def test_update_stack_execute_success_with_stack_policy(
            self, patched_approval, patched_update_term):
        """Test update stack execute success with stack policy."""
        stack_name = "my-fake-stack"

        self.stubber.add_response("create_change_set", {
            'Id': 'CHANGESETID',
            'StackId': 'STACKID'
        })
        changes = [generate_change()]

        self.stubber.add_response(
            "describe_change_set",
            generate_change_set_response(
                status="CREATE_COMPLETE",
                execution_status="AVAILABLE",
                changes=changes,
            ))

        self.stubber.add_response("set_stack_policy", {})

        self.stubber.add_response("execute_change_set", {})

        with self.stubber:
            self.provider.update_stack(
                fqn=stack_name,
                template=Template(url="http://fake.template.url.com/"),
                old_parameters=[],
                parameters=[],
                tags=[],
                stack_policy=Template(body="{}"),
            )

        patched_approval.assert_called_once_with(full_changeset=changes,
                                                 params_diff=[],
                                                 include_verbose=True,
                                                 fqn=stack_name)
        patched_update_term.assert_called_once_with(stack_name, False)

    def test_select_destroy_method(self):
        """Test select destroy method."""
        for i in [[{
                'force_interactive': False
        }, self.provider.interactive_destroy_stack],
                  [{
                      'force_interactive': True
                  }, self.provider.interactive_destroy_stack]]:
            self.assertEqual(self.provider.select_destroy_method(**i[0]), i[1])

    def test_select_update_method(self):
        """Test select update method."""
        for i in [[{
                'force_interactive': False,
                'force_change_set': False
        }, self.provider.interactive_update_stack],
                  [{
                      'force_interactive': True,
                      'force_change_set': False
                  }, self.provider.interactive_update_stack],
                  [{
                      'force_interactive': False,
                      'force_change_set': True
                  }, self.provider.interactive_update_stack],
                  [{
                      'force_interactive': True,
                      'force_change_set': True
                  }, self.provider.interactive_update_stack]]:
            self.assertEqual(self.provider.select_update_method(**i[0]), i[1])

    @patch('runway.cfngin.providers.aws.default.output_full_changeset')
    @patch('runway.cfngin.providers.aws.default.output_summary')
    def test_get_stack_changes_interactive(self, mock_output_summary,
                                           mock_output_full_cs):
        """Test get stack changes interactive."""
        stack_name = "MockStack"
        mock_stack = generate_stack_object(stack_name)

        self.stubber.add_response(
            'describe_stacks',
            {'Stacks': [generate_describe_stacks_stack(stack_name)]})
        self.stubber.add_response('get_template',
                                  generate_get_template('cfn_template.yaml'))
        self.stubber.add_response("create_change_set", {
            'Id': 'CHANGESETID',
            'StackId': stack_name
        })
        changes = []
        changes.append(generate_change())

        self.stubber.add_response(
            "describe_change_set",
            generate_change_set_response(
                status="CREATE_COMPLETE",
                execution_status="AVAILABLE",
                changes=changes,
            ))
        self.stubber.add_response("delete_change_set", {})
        self.stubber.add_response(
            'describe_stacks',
            {'Stacks': [generate_describe_stacks_stack(stack_name)]})

        with self.stubber:
            self.provider.get_stack_changes(
                stack=mock_stack,
                template=Template(url="http://fake.template.url.com/"),
                parameters=[],
                tags=[])

        mock_output_summary.assert_called_with(stack_name,
                                               'changes',
                                               changes, [],
                                               replacements_only=False)
        mock_output_full_cs.assert_called_with(full_changeset=changes,
                                               params_diff=[],
                                               fqn=stack_name)