def test_noninteractive_changeset_update_with_stack_policy(self): """Test noninteractive changeset update with stack policy.""" stack_name = "MockStack" self.stubber.add_response("create_change_set", { 'Id': 'CHANGESETID', 'StackId': 'STACKID' }) 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("set_stack_policy", {}) self.stubber.add_response("execute_change_set", {}) with self.stubber: self.provider.noninteractive_changeset_update( fqn=stack_name, template=Template(url="http://fake.template.url.com/"), old_parameters=[], parameters=[], stack_policy=Template(body="{}"), tags=[], )
def test_generate_cloudformation_args(self): """Test generate cloudformation args.""" stack_name = "mystack" template_url = "http://fake.s3url.com/blah.json" template_body = '{"fake_body": "woot"}' std_args = { "stack_name": stack_name, "parameters": [], "tags": [], "template": Template(url=template_url) } std_return = { "StackName": stack_name, "Parameters": [], "Tags": [], "Capabilities": DEFAULT_CAPABILITIES, "TemplateURL": template_url } result = generate_cloudformation_args(**std_args) self.assertEqual(result, std_return) result = generate_cloudformation_args(service_role="FakeRole", **std_args) service_role_result = copy.deepcopy(std_return) service_role_result["RoleARN"] = "FakeRole" self.assertEqual(result, service_role_result) result = generate_cloudformation_args(change_set_name="MyChanges", **std_args) change_set_result = copy.deepcopy(std_return) change_set_result["ChangeSetName"] = "MyChanges" self.assertEqual(result, change_set_result) # Check stack policy stack_policy = Template(body="{}") result = generate_cloudformation_args(stack_policy=stack_policy, **std_args) stack_policy_result = copy.deepcopy(std_return) stack_policy_result["StackPolicyBody"] = "{}" self.assertEqual(result, stack_policy_result) # If not TemplateURL is provided, use TemplateBody std_args["template"] = Template(body=template_body) template_body_result = copy.deepcopy(std_return) del template_body_result["TemplateURL"] template_body_result["TemplateBody"] = template_body result = generate_cloudformation_args(**std_args) self.assertEqual(result, template_body_result)
def test_create_change_set_stack_did_not_change(self) -> None: """Test create change set stack did not change.""" self.stubber.add_response( "create_change_set", {"Id": "CHANGESETID", "StackId": "STACKID"} ) self.stubber.add_response( "describe_change_set", generate_change_set_response( "FAILED", status_reason="Stack didn't contain changes." ), ) self.stubber.add_response( "delete_change_set", {}, expected_params={"ChangeSetName": "CHANGESETID"} ) with self.stubber: with self.assertRaises(exceptions.StackDidNotChange): create_change_set( cfn_client=self.cfn, fqn="my-fake-stack", template=Template(url="http://fake.template.url.com/"), parameters=[], tags=[], )
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)
def test_noninteractive_changeset_update_no_stack_policy(self) -> None: """Test noninteractive changeset update no stack policy.""" 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: stack_name = "MockStack" self.provider.noninteractive_changeset_update( fqn=stack_name, template=Template(url="http://fake.template.url.com/"), old_parameters=[], parameters=[], stack_policy=None, tags=[], )
def test_get_stack_changes_create(self, mock_output_full_cs: MagicMock) -> None: """Test get stack changes create.""" stack_name = "MockStack" mock_stack = generate_stack_object(stack_name) self.stubber.add_response( "describe_stacks", { "Stacks": [ generate_describe_stacks_stack( stack_name, stack_status="REVIEW_IN_PROGRESS" ) ] }, ) 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, stack_status="REVIEW_IN_PROGRESS" ) ] }, ) self.stubber.add_response( "describe_stacks", { "Stacks": [ generate_describe_stacks_stack( stack_name, stack_status="REVIEW_IN_PROGRESS" ) ] }, ) self.stubber.add_response("delete_stack", {}) with self.stubber: self.provider.get_stack_changes( stack=mock_stack, template=Template(url="http://fake.template.url.com/"), parameters=[], tags=[], ) mock_output_full_cs.assert_called_with( full_changeset=changes, params_diff=[], fqn=stack_name, answer="y" )
def test_create_stack_with_changeset(self, patched_create_change_set, patched_update_term): """Test create_stack, force changeset, termination protection.""" stack_name = 'fake_stack' template_path = Path('./tests/cfngin/fixtures/cfn_template.yaml') template = Template(body=template_path.read_text()) parameters = [] tags = [] changeset_id = 'CHANGESETID' patched_create_change_set.return_value = ([], changeset_id) self.stubber.add_response('execute_change_set', {}, {'ChangeSetName': changeset_id}) with self.stubber: self.provider.create_stack(stack_name, template, parameters, tags, force_change_set=True, termination_protection=True) self.stubber.assert_no_pending_responses() patched_create_change_set.assert_called_once_with( self.provider.cloudformation, stack_name, template, parameters, tags, 'CREATE', service_role=self.provider.service_role) patched_update_term.assert_called_once_with(stack_name, True)
def test_get_stack_changes_create(self, mock_output_full_cs): """Test get stack changes create.""" stack_name = "MockStack" mock_stack = generate_stack_object(stack_name) self.stubber.add_response( 'describe_stacks', { 'Stacks': [ generate_describe_stacks_stack( stack_name, stack_status='REVIEW_IN_PROGRESS') ] }) 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, stack_status='REVIEW_IN_PROGRESS') ] }) self.stubber.add_response( 'describe_stacks', { 'Stacks': [ generate_describe_stacks_stack( stack_name, stack_status='REVIEW_IN_PROGRESS') ] }) self.stubber.add_response("delete_stack", {}) with self.stubber: self.provider.get_stack_changes( stack=mock_stack, template=Template(url="http://fake.template.url.com/"), parameters=[], tags=[]) mock_output_full_cs.assert_called_with(full_changeset=changes, params_diff=[], fqn=stack_name, answer='y')
def test_update_stack_execute_success_with_stack_policy( self, patched_approval): """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 = [] 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("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_with(full_changeset=changes, params_diff=[], include_verbose=True, fqn=stack_name) self.assertEqual(patched_approval.call_count, 1)
def test_get_stack_changes_update(self, mock_output_full_cs): """Test get stack changes update.""" 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: result = self.provider.get_stack_changes( stack=mock_stack, template=Template(url="http://fake.template.url.com/"), parameters=[], tags=[]) mock_output_full_cs.assert_called_with(full_changeset=changes, params_diff=[], fqn=stack_name, answer='y') expected_outputs = { 'FakeOutput': '<inferred-change: MockStack.FakeOutput={}>'.format( str({"Ref": "FakeResource"})) } self.assertEqual(self.provider.get_outputs(stack_name), expected_outputs) self.assertEqual(result, expected_outputs)
def test_create_stack_no_changeset(self): """Test create_stack, no changeset, template url.""" stack_name = "fake_stack" template = Template(url="http://fake.template.url.com/") parameters = [] tags = [] expected_args = generate_cloudformation_args(stack_name, parameters, tags, template) expected_args["EnableTerminationProtection"] = False self.stubber.add_response("create_stack", {"StackId": stack_name}, expected_args) with self.stubber: self.provider.create_stack(stack_name, template, parameters, tags) self.stubber.assert_no_pending_responses()
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)
def test_get_stack_changes_update(self, mock_output_full_cs: MagicMock) -> None: """Test get stack changes update.""" 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: result = self.provider.get_stack_changes( stack=mock_stack, template=Template(url="http://fake.template.url.com/"), parameters=[], tags=[], ) mock_output_full_cs.assert_called_with( full_changeset=changes, params_diff=[], fqn=stack_name, answer="y" ) expected_outputs = { "FakeOutput": "<inferred-change: MockStack.FakeOutput={}>".format( str({"Ref": "FakeResource"}) ) } self.assertEqual(self.provider.get_outputs(stack_name), expected_outputs) self.assertEqual(result, expected_outputs)
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 )
def test_create_change_set_unhandled_failed_status(self): """Test create change set unhandled failed status.""" self.stubber.add_response("create_change_set", { 'Id': 'CHANGESETID', 'StackId': 'STACKID' }) self.stubber.add_response( "describe_change_set", generate_change_set_response( "FAILED", status_reason="Some random bad thing.")) with self.stubber: with self.assertRaises(exceptions.UnhandledChangeSetStatus): create_change_set( cfn_client=self.cfn, fqn="my-fake-stack", template=Template(url="http://fake.template.url.com/"), parameters=[], tags=[])
def test_create_stack_with_changeset( self, patched_create_change_set: MagicMock, patched_update_term: MagicMock ) -> None: """Test create_stack, force changeset, termination protection.""" stack_name = "fake_stack" template_path = Path("./tests/unit/cfngin/fixtures/cfn_template.yaml") template = Template(body=template_path.read_text()) parameters: List[Any] = [] tags: List[Any] = [] changeset_id = "CHANGESETID" patched_create_change_set.return_value = ([], changeset_id) self.stubber.add_response( "execute_change_set", {}, {"ChangeSetName": changeset_id} ) with self.stubber: self.provider.create_stack( stack_name, template, parameters, tags, force_change_set=True, termination_protection=True, ) self.stubber.assert_no_pending_responses() patched_create_change_set.assert_called_once_with( self.provider.cloudformation, stack_name, template, parameters, tags, "CREATE", service_role=self.provider.service_role, ) patched_update_term.assert_called_once_with(stack_name, True)
def test_create_change_set_bad_execution_status(self): """Test create change set bad execution status.""" self.stubber.add_response("create_change_set", { 'Id': 'CHANGESETID', 'StackId': 'STACKID' }) self.stubber.add_response( "describe_change_set", generate_change_set_response( status="CREATE_COMPLETE", execution_status="UNAVAILABLE", )) with self.stubber: with self.assertRaises(exceptions.UnableToExecuteChangeSet): create_change_set( cfn_client=self.cfn, fqn="my-fake-stack", template=Template(url="http://fake.template.url.com/"), parameters=[], tags=[])