def test_deploy_failed_only_existing_changes(client, config): config._config['ExistingChanges'] = 'FAILED_ONLY' runner = Runner(client, config) with pytest.raises( ValidationError, match= 'Creation of new ChangeSet not allowed when existing valid ChangeSets found' ): runner.deploy() client.list_change_sets.assert_called_once_with(StackName='TestStack')
def test_deploy_client_error(client, config): client.configure_mock(**{ 'create_change_set.side_effect': ClientError({}, 'create_change_set') }) runner = Runner(client, config) with pytest.raises( StackError, match= 'An error occurred \\(Unknown\\) when calling the create_change_set .*' ): runner.deploy()
def test_deploy_rollback_complete(client, config): describe_stacks = { 'Stacks': [{ 'StackName': 'TestStack', 'StackStatus': StackStatus.ROLLBACK_COMPLETE.name, 'CreationTime': '2019-12-31T18:30:11.12345+0000' }] } client.configure_mock(**{'describe_stacks.return_value': describe_stacks}) runner = Runner(client, config) runner.deploy() client.delete_stack.assert_called_once_with(StackName='TestStack', RetainResources=[])
def test_deploy_waiter_failed(client, config): # Configure waiter to throw WaiterError for some unknown other reasons waiter_error = WaiterError('change_set_create_complete', 'Other reason', { 'Status': 'FAILED', 'StatusReason': 'Some other reason' }) waiter_mock = MagicMock(**{'wait.side_effect': waiter_error}) client.configure_mock(**{'get_waiter.return_value': waiter_mock}) runner = Runner(client, config) with pytest.raises( StackError, match= 'ChangeSet creation failed - Status: FAILED, Reason: Some other reason' ): runner.deploy()
def test_deploy_invalid_status(client, config): describe_stacks = { 'Stacks': [{ 'StackName': 'TestStack', 'StackStatus': StackStatus.UPDATE_IN_PROGRESS.name, 'CreationTime': '2019-12-31T18:30:11.12345+0000' }] } client.configure_mock(**{'describe_stacks.return_value': describe_stacks}) runner = Runner(client, config) with pytest.raises( ValidationError, match= 'Stack TestStack is not in a deployable status: UPDATE_IN_PROGRESS' ): runner.deploy()
def test_deploy(client, config, capsys, monkeypatch): # Prevent differences in format depending upon where this runs monkeypatch.setenv('STACKMANAGER_TIMEZONE', 'UTC') runner = Runner(client, config) runner.deploy() client.list_change_sets.assert_called_once_with(StackName='TestStack') client.create_change_set.assert_called_once_with( StackName='TestStack', ChangeSetName='TestChangeSet', ChangeSetType='UPDATE', Parameters=[{ 'ParameterKey': 'Param1', 'ParameterValue': 'Value1' }, { 'ParameterKey': 'Param2', 'UsePreviousValue': True }], Tags=[{ 'Key': 'Tag1', 'Value': 'Value1' }], Capabilities=['CAPABILITY_IAM'], TemplateBody='AWSTemplateFormatVersion : "2010-09-09"') client.get_waiter.assert_called_once_with('change_set_create_complete') client.get_waiter().wait.assert_called_once_with( StackName='TestStack', ChangeSetName='TestChangeSet', WaiterConfig={ 'Delay': 5, 'MaxAttempts': 120 }) client.describe_change_set.assert_called_once_with( ChangeSetName='TestChangeSet', StackName='TestStack') captured = capsys.readouterr() assert 'Stack: TestStack, Status: CREATE_COMPLETE' in captured.out assert 'Existing ChangeSets:\n 2020-01-01 00:00:00: ExistingChangeSet (CREATE_COMPLETE)' in captured.out assert 'Creating ChangeSet TestChangeSet' in captured.out assert 'Action LogicalResourceId ResourceType Replacement' in captured.out assert 'Add Queue AWS::SQS::Queue -' in captured.out assert 'ChangeSet TestChangeSet is ready to run' in captured.out
def test_deploy_no_changes(client, config, capsys): # Configure waiter to throw WaiterError for FAILURE due to no changes waiter_error = WaiterError('change_set_create_complete', 'No Changes', { 'Status': 'FAILED', 'StatusReason': 'No updates are to be performed' }) waiter_mock = MagicMock(**{'wait.side_effect': waiter_error}) client.configure_mock(**{'get_waiter.return_value': waiter_mock}) runner = Runner(client, config) runner.deploy() client.list_change_sets.assert_called_once_with(StackName='TestStack') client.create_change_set.assert_called_once() client.get_waiter.assert_called_once_with('change_set_create_complete') client.describe_change_set.assert_not_called() client.update_termination_protection.assert_not_called() captured = capsys.readouterr() assert 'No changes to Stack TestStack' in captured.out
def test_deploy_no_changes_enable_termination_protection( client, config, capsys): describe_stacks = { 'Stacks': [{ 'StackName': 'TestStack', 'StackStatus': StackStatus.CREATE_COMPLETE.name, 'CreationTime': '2019-12-31T18:30:11.12345+0000', 'EnableTerminationProtection': False }] } # Configure waiter to throw WaiterError for FAILURE due to no changes waiter_error = WaiterError('change_set_create_complete', 'No Changes', { 'Status': 'FAILED', 'StatusReason': 'No updates are to be performed' }) waiter_mock = MagicMock(**{'wait.side_effect': waiter_error}) client.configure_mock( **{ 'get_waiter.return_value': waiter_mock, 'describe_stacks.return_value': describe_stacks }) runner = Runner(client, config) runner.deploy() client.list_change_sets.assert_called_once_with(StackName='TestStack') client.create_change_set.assert_called_once() client.get_waiter.assert_called_once_with('change_set_create_complete') client.describe_change_set.assert_not_called() client.update_termination_protection.assert_called_once_with( StackName='TestStack', EnableTerminationProtection=True) captured = capsys.readouterr() assert 'No changes to Stack TestStack' in captured.out assert 'Enabled Termination Protection' in captured.out