Add stacksets (#3)

Added most stack set responses
This commit is contained in:
John Corrales 2019-01-10 21:33:15 -08:00 committed by GitHub
parent fcc5a165ea
commit 4207a8e182
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 872 additions and 14 deletions

View File

@ -475,19 +475,19 @@
- [ ] continue_update_rollback
- [X] create_change_set
- [X] create_stack
- [ ] create_stack_instances
- [ ] create_stack_set
- [X] create_stack_instances
- [X] create_stack_set
- [ ] delete_change_set
- [X] delete_stack
- [ ] delete_stack_instances
- [ ] delete_stack_set
- [X] delete_stack_instances
- [X] delete_stack_set
- [ ] describe_account_limits
- [ ] describe_change_set
- [ ] describe_stack_events
- [ ] describe_stack_instance
- [X] describe_stack_instance
- [ ] describe_stack_resource
- [ ] describe_stack_resources
- [ ] describe_stack_set
- [X] describe_stack_set
- [ ] describe_stack_set_operation
- [X] describe_stacks
- [ ] estimate_template_cost
@ -498,18 +498,18 @@
- [ ] list_change_sets
- [X] list_exports
- [ ] list_imports
- [ ] list_stack_instances
- [X] list_stack_instances
- [X] list_stack_resources
- [ ] list_stack_set_operation_results
- [ ] list_stack_set_operations
- [ ] list_stack_sets
- [X] list_stack_set_operations
- [X] list_stack_sets
- [X] list_stacks
- [ ] set_stack_policy
- [ ] signal_resource
- [ ] stop_stack_set_operation
- [X] update_stack
- [ ] update_stack_instances
- [ ] update_stack_set
- [X] update_stack_instances
- [X] update_stack_set
- [ ] update_termination_protection
- [ ] validate_template

View File

@ -1,5 +1,5 @@
from __future__ import unicode_literals
from datetime import datetime
from datetime import datetime, timedelta
import json
import yaml
import uuid
@ -12,12 +12,137 @@ from .parsing import ResourceMap, OutputMap
from .utils import (
generate_changeset_id,
generate_stack_id,
generate_stackset_arn,
generate_stackset_id,
yaml_tag_constructor,
validate_template_cfn_lint,
)
from .exceptions import ValidationError
class FakeStackSet(BaseModel):
def __init__(self, stackset_id, name, template, region='us-east-1', status='ACTIVE', description=None, parameters=None, tags=None, admin_role=None, execution_role=None):
self.id = stackset_id
self.arn = generate_stackset_arn(stackset_id, region)
self.name = name
self.template = template
self.description = description
self.parameters = parameters
self.tags = tags
self.admin_role = admin_role
self.execution_role = execution_role
self.status = status
self.instances = FakeStackInstances(parameters, self.id, self.name)
self.operations = []
@property
def stack_instances(self):
return self.instances.stack_instances
def _create_operation(self, operation_id, action, status):
operation = {
'OperationId': str(operation_id),
'Action': action,
'Status': status,
'CreationTimestamp': datetime.now(),
'EndTimestamp': datetime.now() + timedelta(minutes=2),
}
self.operations += [operation]
return operation
def delete(self):
self.status = 'DELETED'
def update(self, template, description, parameters, tags, admin_role,
execution_role, accounts, regions, operation_id=None):
if not operation_id:
operation_id = uuid.uuid4()
self.template = template if template else self.template
self.description = description if description is not None else self.description
self.parameters = parameters if parameters else self.parameters
self.tags = tags if tags else self.tags
self.admin_role = admin_role if admin_role else self.admin_role
self.execution_role = execution_role if execution_role else self.execution_role
if accounts and regions:
self.update_instances(accounts, regions, self.parameters)
operation = self._create_operation(operation_id=operation_id, action='UPDATE', status='SUCCEEDED')
return operation
def create_stack_instances(self, accounts, regions, parameters, operation_id=None):
if not operation_id:
operation_id = uuid.uuid4()
if not parameters:
parameters = self.parameters
self.instances.create_instances(accounts, regions, parameters, operation_id)
self._create_operation(operation_id=operation_id, action='CREATE', status='SUCCEEDED')
def delete_stack_instances(self, accounts, regions, operation_id=None):
if not operation_id:
operation_id = uuid.uuid4()
self.instances.delete(accounts, regions)
self._create_operation(operation_id=operation_id, action='DELETE', status='SUCCEEDED')
def update_instances(self, accounts, regions, parameters, operation_id=None):
if not operation_id:
operation_id = uuid.uuid4()
self.instances.update(accounts, regions, parameters)
operation = self._create_operation(operation_id=operation_id, action='UPDATE', status='SUCCEEDED')
return operation
class FakeStackInstances(BaseModel):
def __init__(self, parameters, stackset_id, stackset_name):
self.parameters = parameters if parameters else {}
self.stackset_id = stackset_id
self.stack_name = "StackSet-{}".format(stackset_id)
self.stackset_name = stackset_name
self.stack_instances = []
def create_instances(self, accounts, regions, parameters, operation_id):
new_instances = []
for region in regions:
for account in accounts:
instance = {
'StackId': generate_stack_id(self.stack_name, region, account),
'StackSetId': self.stackset_id,
'Region': region,
'Account': account,
'Status': "CURRENT",
'ParameterOverrides': parameters if parameters else [],
}
new_instances.append(instance)
self.stack_instances += new_instances
return new_instances
def update(self, accounts, regions, parameters):
for account in accounts:
for region in regions:
instance = self.get_instance(account, region)
if parameters:
instance['ParameterOverrides'] = parameters
else:
instance['ParameterOverrides'] = []
def delete(self, accounts, regions):
for i, instance in enumerate(self.stack_instances):
if instance['Region'] in regions and instance['Account'] in accounts:
self.stack_instances.pop(i)
def get_instance(self, account, region):
for i, instance in enumerate(self.stack_instances):
if instance['Region'] == region and instance['Account'] == account:
return self.stack_instances[i]
class FakeStack(BaseModel):
def __init__(self, stack_id, name, template, parameters, region_name, notification_arns=None, tags=None, role_arn=None, cross_stack_resources=None, create_change_set=False):
@ -146,10 +271,72 @@ class CloudFormationBackend(BaseBackend):
def __init__(self):
self.stacks = OrderedDict()
self.stacksets = OrderedDict()
self.deleted_stacks = {}
self.exports = OrderedDict()
self.change_sets = OrderedDict()
def create_stack_set(self, name, template, parameters, tags=None, description=None, region='us-east-1', admin_role=None, execution_role=None):
stackset_id = generate_stackset_id(name)
new_stackset = FakeStackSet(
stackset_id=stackset_id,
name=name,
template=template,
parameters=parameters,
description=description,
tags=tags,
admin_role=admin_role,
execution_role=execution_role,
)
self.stacksets[stackset_id] = new_stackset
return new_stackset
def get_stack_set(self, name):
stacksets = self.stacksets.keys()
for stackset in stacksets:
if self.stacksets[stackset].name == name:
return self.stacksets[stackset]
raise ValidationError(name)
def delete_stack_set(self, name):
stacksets = self.stacksets.keys()
for stackset in stacksets:
if self.stacksets[stackset].name == name:
self.stacksets[stackset].delete()
def create_stack_instances(self, stackset_name, accounts, regions, parameters, operation_id=None):
stackset = self.get_stack_set(stackset_name)
stackset.create_stack_instances(
accounts=accounts,
regions=regions,
parameters=parameters,
operation_id=operation_id,
)
return stackset
def update_stack_set(self, stackset_name, template=None, description=None,
parameters=None, tags=None, admin_role=None, execution_role=None,
accounts=None, regions=None, operation_id=None):
stackset = self.get_stack_set(stackset_name)
update = stackset.update(
template=template,
description=description,
parameters=parameters,
tags=tags,
admin_role=admin_role,
execution_role=execution_role,
accounts=accounts,
regions=regions,
operation_id=operation_id
)
return update
def delete_stack_instances(self, stackset_name, accounts, regions, operation_id=None):
stackset = self.get_stack_set(stackset_name)
stackset.delete_stack_instances(accounts, regions, operation_id)
return stackset
def create_stack(self, name, template, parameters, region_name, notification_arns=None, tags=None, role_arn=None, create_change_set=False):
stack_id = generate_stack_id(name)
new_stack = FakeStack(

View File

@ -312,6 +312,151 @@ class CloudFormationResponse(BaseResponse):
template = self.response_template(VALIDATE_STACK_RESPONSE_TEMPLATE)
return template.render(description=description)
def create_stack_set(self):
stackset_name = self._get_param('StackSetName')
stack_body = self._get_param('TemplateBody')
template_url = self._get_param('TemplateURL')
# role_arn = self._get_param('RoleARN')
parameters_list = self._get_list_prefix("Parameters.member")
tags = dict((item['key'], item['value'])
for item in self._get_list_prefix("Tags.member"))
# Copy-Pasta - Hack dict-comprehension
parameters = dict([
(parameter['parameter_key'], parameter['parameter_value'])
for parameter
in parameters_list
])
if template_url:
stack_body = self._get_stack_from_s3_url(template_url)
stackset = self.cloudformation_backend.create_stack_set(
name=stackset_name,
template=stack_body,
parameters=parameters,
tags=tags,
# role_arn=role_arn,
)
if self.request_json:
return json.dumps({
'CreateStackSetResponse': {
'CreateStackSetResult': {
'StackSetId': stackset.stackset_id,
}
}
})
else:
template = self.response_template(CREATE_STACK_SET_RESPONSE_TEMPLATE)
return template.render(stackset=stackset)
def create_stack_instances(self):
stackset_name = self._get_param('StackSetName')
accounts = self._get_multi_param('Accounts.member')
regions = self._get_multi_param('Regions.member')
parameters = self._get_multi_param('ParameterOverrides.member')
self.cloudformation_backend.create_stack_instances(stackset_name, accounts, regions, parameters)
template = self.response_template(CREATE_STACK_INSTANCES_TEMPLATE)
return template.render()
def delete_stack_set(self):
stackset_name = self._get_param('StackSetName')
self.cloudformation_backend.delete_stack_set(stackset_name)
template = self.response_template(DELETE_STACK_SET_RESPONSE_TEMPLATE)
return template.render()
def delete_stack_instances(self):
stackset_name = self._get_param('StackSetName')
accounts = self._get_multi_param('Accounts.member')
regions = self._get_multi_param('Regions.member')
self.cloudformation_backend.delete_stack_instances(stackset_name, accounts, regions)
template = self.response_template(DELETE_STACK_INSTANCES_TEMPLATE)
return template.render()
def describe_stack_set(self):
stackset_name = self._get_param('StackSetName')
stackset = self.cloudformation_backend.get_stack_set(stackset_name)
if not stackset.admin_role:
stackset.admin_role = 'arn:aws:iam::123456789012:role/AWSCloudFormationStackSetAdministrationRole'
if not stackset.execution_role:
stackset.execution_role = 'AWSCloudFormationStackSetExecutionRole'
template = self.response_template(DESCRIBE_STACK_SET_RESPONSE_TEMPLATE)
return template.render(stackset=stackset)
def describe_stack_instance(self):
stackset_name = self._get_param('StackSetName')
account = self._get_param('StackInstanceAccount')
region = self._get_param('StackInstanceRegion')
instance = self.cloudformation_backend.get_stack_set(stackset_name).instances.get_instance(account, region)
template = self.response_template(DESCRIBE_STACK_INSTANCE_TEMPLATE)
rendered = template.render(instance=instance)
return rendered
def list_stack_sets(self):
stacksets = self.cloudformation_backend.stacksets
template = self.response_template(LIST_STACK_SETS_TEMPLATE)
return template.render(stacksets=stacksets)
def list_stack_instances(self):
stackset_name = self._get_param('StackSetName')
stackset = self.cloudformation_backend.get_stack_set(stackset_name)
template = self.response_template(LIST_STACK_INSTANCES_TEMPLATE)
return template.render(stackset=stackset)
def list_stack_set_operations(self):
stackset_name = self._get_param('StackSetName')
stackset = self.cloudformation_backend.get_stack_set(stackset_name)
template = self.response_template(LIST_STACK_SET_OPERATIONS_RESPONSE_TEMPLATE)
return template.render(stackset=stackset)
def update_stack_set(self):
stackset_name = self._get_param('StackSetName')
operation_id = self._get_param('OperationId')
description = self._get_param('Description')
execution_role = self._get_param('ExecutionRoleName')
admin_role = self._get_param('AdministrationRoleARN')
accounts = self._get_multi_param('Accounts.member')
regions = self._get_multi_param('Regions.member')
template_body = self._get_param('TemplateBody')
template_url = self._get_param('TemplateURL')
if template_url:
template_body = self._get_stack_from_s3_url(template_url)
tags = dict((item['key'], item['value'])
for item in self._get_list_prefix("Tags.member"))
parameters_list = self._get_list_prefix("Parameters.member")
parameters = dict([
(parameter['parameter_key'], parameter['parameter_value'])
for parameter
in parameters_list
])
operation = self.cloudformation_backend.update_stack_set(
stackset_name=stackset_name,
template=template_body,
description=description,
parameters=parameters,
tags=tags,
admin_role=admin_role,
execution_role=execution_role,
accounts=accounts,
regions=regions,
operation_id=operation_id
)
template = self.response_template(UPDATE_STACK_SET_RESPONSE_TEMPLATE)
return template.render(operation=operation)
def update_stack_instances(self):
stackset_name = self._get_param('StackSetName')
accounts = self._get_multi_param('Accounts.member')
regions = self._get_multi_param('Regions.member')
parameters = self._get_multi_param('ParameterOverrides.member')
operation = self.cloudformation_backend.get_stack_set(stackset_name).update_instances(accounts, regions, parameters)
template = self.response_template(UPDATE_STACK_INSTANCES_RESPONSE_TEMPLATE)
return template.render(operation=operation)
VALIDATE_STACK_RESPONSE_TEMPLATE = """<ValidateTemplateResponse>
<ValidateTemplateResult>
@ -553,3 +698,183 @@ LIST_EXPORTS_RESPONSE = """<ListExportsResponse xmlns="http://cloudformation.ama
<RequestId>5ccc7dcd-744c-11e5-be70-example</RequestId>
</ResponseMetadata>
</ListExportsResponse>"""
CREATE_STACK_SET_RESPONSE_TEMPLATE = """<CreateStackSetResponse xmlns="http://internal.amazon.com/coral/com.amazonaws.maestro.service.v20160713/">
<CreateStackSetResult>
<StackSetId>{{ stackset.stackset_id }}</StackSetId>
</CreateStackSetResult>
<ResponseMetadata>
<RequestId>f457258c-391d-41d1-861f-example</RequestId>
</ResponseMetadata>
</CreateStackSetResponse>
"""
DESCRIBE_STACK_SET_RESPONSE_TEMPLATE = """<DescribeStackSetResponse xmlns="http://internal.amazon.com/coral/com.amazonaws.maestro.service.v20160713/">
<DescribeStackSetResult>
<StackSet>
<Capabilities/>
<StackSetARN>{{ stackset.arn }}</StackSetARN>
<ExecutionRoleName>{{ stackset.execution_role }}</ExecutionRoleName>
<AdministrationRoleARN>{{ stackset.admin_role }}</AdministrationRoleARN>
<StackSetId>{{ stackset.id }}</StackSetId>
<TemplateBody>{{ stackset.template }}</TemplateBody>
<StackSetName>{{ stackset.name }}</StackSetName>
<Parameters>
{% for param_name, param_value in stackset.parameters.items() %}
<member>
<ParameterKey>{{ param_name }}</ParameterKey>
<ParameterValue>{{ param_value }}</ParameterValue>
</member>
{% endfor %}
</Parameters>
<Tags>
{% for tag_key, tag_value in stackset.tags.items() %}
<member>
<Key>{{ tag_key }}</Key>
<Value>{{ tag_value }}</Value>
</member>
{% endfor %}
</Tags>
<Status>{{ stackset.status }}</Status>
</StackSet>
</DescribeStackSetResult>
<ResponseMetadata>
<RequestId>d8b64e11-5332-46e1-9603-example</RequestId>
</ResponseMetadata>
</DescribeStackSetResponse>"""
DELETE_STACK_SET_RESPONSE_TEMPLATE = """<DeleteStackSetResponse xmlns="http://internal.amazon.com/coral/com.amazonaws.maestro.service.v20160713/">
<DeleteStackSetResult/>
<ResponseMetadata>
<RequestId>c35ec2d0-d69f-4c4d-9bd7-example</RequestId>
</ResponseMetadata>
</DeleteStackSetResponse>"""
CREATE_STACK_INSTANCES_TEMPLATE = """<CreateStackInstancesResponse xmlns="http://internal.amazon.com/coral/com.amazonaws.maestro.service.v20160713/">
<CreateStackInstancesResult>
<OperationId>1459ad6d-63cc-4c96-a73e-example</OperationId>
</CreateStackInstancesResult>
<ResponseMetadata>
<RequestId>6b29f7e3-69be-4d32-b374-example</RequestId>
</ResponseMetadata>
</CreateStackInstancesResponse>
"""
LIST_STACK_INSTANCES_TEMPLATE = """<ListStackInstancesResponse xmlns="http://internal.amazon.com/coral/com.amazonaws.maestro.service.v20160713/">
<ListStackInstancesResult>
<Summaries>
{% for instance in stackset.stack_instances %}
<member>
<StackId>{{ instance.StackId }}</StackId>
<StackSetId>{{ instance.StackSetId }}</StackSetId>
<Region>{{ instance.Region }}</Region>
<Account>{{ instance.Account }}</Account>
<Status>{{ instance.Status }}</Status>
</member>
{% endfor %}
</Summaries>
</ListStackInstancesResult>
<ResponseMetadata>
<RequestId>83c27e73-b498-410f-993c-example</RequestId>
</ResponseMetadata>
</ListStackInstancesResponse>
"""
DELETE_STACK_INSTANCES_TEMPLATE = """<DeleteStackInstancesResponse xmlns="http://internal.amazon.com/coral/com.amazonaws.maestro.service.v20160713/">
<DeleteStackInstancesResult>
<OperationId>d76a070d-279a-45f3-b9b4-example</OperationId>
</DeleteStackInstancesResult>
<ResponseMetadata>
<RequestId>e5325090-66f6-4ecd-a531-example</RequestId>
</ResponseMetadata>
</DeleteStackInstancesResponse>
"""
DESCRIBE_STACK_INSTANCE_TEMPLATE = """<DescribeStackInstanceResponse xmlns="http://internal.amazon.com/coral/com.amazonaws.maestro.service.v20160713/">
<DescribeStackInstanceResult>
<StackInstance>
<StackId>{{ instance.StackId }}</StackId>
<StackSetId>{{ instance.StackSetId }}</StackSetId>
{% if instance.ParameterOverrides %}
<ParameterOverrides>
{% for override in instance.ParameterOverrides %}
{% if override['ParameterKey'] or override['ParameterValue'] %}
<member>
<ParameterKey>{{ override.ParameterKey }}</ParameterKey>
<UsePreviousValue>false</UsePreviousValue>
<ParameterValue>{{ override.ParameterValue }}</ParameterValue>
</member>
{% endif %}
{% endfor %}
</ParameterOverrides>
{% else %}
<ParameterOverrides/>
{% endif %}
<Region>{{ instance.Region }}</Region>
<Account>{{ instance.Account }}</Account>
<Status>{{ instance.Status }}</Status>
</StackInstance>
</DescribeStackInstanceResult>
<ResponseMetadata>
<RequestId>c6c7be10-0343-4319-8a25-example</RequestId>
</ResponseMetadata>
</DescribeStackInstanceResponse>
"""
LIST_STACK_SETS_TEMPLATE = """<ListStackSetsResponse xmlns="http://internal.amazon.com/coral/com.amazonaws.maestro.service.v20160713/">
<ListStackSetsResult>
<Summaries>
{% for key, value in stacksets.items() %}
<member>
<StackSetName>{{ value.name }}</StackSetName>
<StackSetId>{{ value.id }}</StackSetId>
<Status>{{ value.status }}</Status>
</member>
{% endfor %}
</Summaries>
</ListStackSetsResult>
<ResponseMetadata>
<RequestId>4dcacb73-841e-4ed8-b335-example</RequestId>
</ResponseMetadata>
</ListStackSetsResponse>
"""
UPDATE_STACK_INSTANCES_RESPONSE_TEMPLATE = """<UpdateStackInstancesResponse xmlns="http://internal.amazon.com/coral/com.amazonaws.maestro.service.v20160713/">
<UpdateStackInstancesResult>
<OperationId>{{ operation }}</OperationId>
</UpdateStackInstancesResult>
<ResponseMetadata>
<RequestId>bdbf8e94-19b6-4ce4-af85-example</RequestId>
</ResponseMetadata>
</UpdateStackInstancesResponse>
"""
UPDATE_STACK_SET_RESPONSE_TEMPLATE = """<UpdateStackSetResponse xmlns="http://internal.amazon.com/coral/com.amazonaws.maestro.service.v20160713/">
<UpdateStackSetResult>
<OperationId>{{ operation.OperationId }}</OperationId>
</UpdateStackSetResult>
<ResponseMetadata>
<RequestId>adac907b-17e3-43e6-a254-example</RequestId>
</ResponseMetadata>
</UpdateStackSetResponse>
"""
LIST_STACK_SET_OPERATIONS_RESPONSE_TEMPLATE = """<ListStackSetOperationsResponse xmlns="http://internal.amazon.com/coral/com.amazonaws.maestro.service.v20160713/">
<ListStackSetOperationsResult>
<Summaries>
{% for operation in stackset.operations %}
<member>
<CreationTimestamp>{{ operation.CreationTimestamp }}</CreationTimestamp>
<OperationId>{{ operation.OperationId }}</OperationId>
<Action>{{ operation.Action }}</Action>
<EndTimestamp>{{ operation.EndTimestamp }}</EndTimestamp>
<Status>{{ operation.Status }}</Status>
</member>
{% endfor %}
</Summaries>
</ListStackSetOperationsResult>
<ResponseMetadata>
<RequestId>65b9d9be-08bb-4a43-9a21-example</RequestId>
</ResponseMetadata>
</ListStackSetOperationsResponse>
"""

View File

@ -8,9 +8,9 @@ import os
from cfnlint import decode, core
def generate_stack_id(stack_name):
def generate_stack_id(stack_name, region="us-east-1", account="123456789"):
random_id = uuid.uuid4()
return "arn:aws:cloudformation:us-east-1:123456789:stack/{0}/{1}".format(stack_name, random_id)
return "arn:aws:cloudformation:{}:{}:stack/{}/{}".format(region, account, stack_name, random_id)
def generate_changeset_id(changeset_name, region_name):
@ -18,6 +18,15 @@ def generate_changeset_id(changeset_name, region_name):
return 'arn:aws:cloudformation:{0}:123456789:changeSet/{1}/{2}'.format(region_name, changeset_name, random_id)
def generate_stackset_id(stackset_name):
random_id = uuid.uuid4()
return '{}:{}'.format(stackset_name, random_id)
def generate_stackset_arn(stackset_id, region_name):
return 'arn:aws:cloudformation:{}:123456789012:stackset/{}'.format(region_name, stackset_id)
def random_suffix():
size = 12
chars = list(range(10)) + ['A-Z']

View File

@ -184,6 +184,343 @@ dummy_import_template_json = json.dumps(dummy_import_template)
dummy_redrive_template_json = json.dumps(dummy_redrive_template)
@mock_cloudformation
def test_boto3_describe_stack_instances():
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
cf_conn.create_stack_set(
StackSetName="test_stack_set",
TemplateBody=dummy_template_json,
)
cf_conn.create_stack_instances(
StackSetName="test_stack_set",
Accounts=['123456789012'],
Regions=['us-east-1', 'us-west-2'],
)
usw2_instance = cf_conn.describe_stack_instance(
StackSetName="test_stack_set",
StackInstanceAccount='123456789012',
StackInstanceRegion='us-west-2',
)
use1_instance = cf_conn.describe_stack_instance(
StackSetName="test_stack_set",
StackInstanceAccount='123456789012',
StackInstanceRegion='us-east-1',
)
usw2_instance['StackInstance'].should.have.key('Region').which.should.equal('us-west-2')
usw2_instance['StackInstance'].should.have.key('Account').which.should.equal('123456789012')
use1_instance['StackInstance'].should.have.key('Region').which.should.equal('us-east-1')
use1_instance['StackInstance'].should.have.key('Account').which.should.equal('123456789012')
@mock_cloudformation
def test_boto3_list_stacksets_length():
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
cf_conn.create_stack_set(
StackSetName="test_stack_set",
TemplateBody=dummy_template_json,
)
cf_conn.create_stack_set(
StackSetName="test_stack_set2",
TemplateBody=dummy_template_yaml,
)
stacksets = cf_conn.list_stack_sets()
stacksets.should.have.length_of(2)
@mock_cloudformation
def test_boto3_list_stacksets_contents():
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
cf_conn.create_stack_set(
StackSetName="test_stack_set",
TemplateBody=dummy_template_json,
)
stacksets = cf_conn.list_stack_sets()
stacksets['Summaries'][0].should.have.key('StackSetName').which.should.equal('test_stack_set')
stacksets['Summaries'][0].should.have.key('Status').which.should.equal('ACTIVE')
@mock_cloudformation
def test_boto3_update_stack_instances():
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
param = [
{'ParameterKey': 'SomeParam', 'ParameterValue': 'StackSetValue'},
{'ParameterKey': 'AnotherParam', 'ParameterValue': 'StackSetValue2'},
]
param_overrides = [
{'ParameterKey': 'SomeParam', 'ParameterValue': 'OverrideValue'},
{'ParameterKey': 'AnotherParam', 'ParameterValue': 'OverrideValue2'}
]
cf_conn.create_stack_set(
StackSetName="test_stack_set",
TemplateBody=dummy_template_yaml_with_ref,
Parameters=param,
)
cf_conn.create_stack_instances(
StackSetName="test_stack_set",
Accounts=['123456789012'],
Regions=['us-east-1', 'us-west-1', 'us-west-2'],
)
cf_conn.update_stack_instances(
StackSetName="test_stack_set",
Accounts=['123456789012'],
Regions=['us-west-1', 'us-west-2'],
ParameterOverrides=param_overrides,
)
usw2_instance = cf_conn.describe_stack_instance(
StackSetName="test_stack_set",
StackInstanceAccount='123456789012',
StackInstanceRegion='us-west-2',
)
usw1_instance = cf_conn.describe_stack_instance(
StackSetName="test_stack_set",
StackInstanceAccount='123456789012',
StackInstanceRegion='us-west-1',
)
use1_instance = cf_conn.describe_stack_instance(
StackSetName="test_stack_set",
StackInstanceAccount='123456789012',
StackInstanceRegion='us-east-1',
)
usw2_instance['StackInstance']['ParameterOverrides'][0]['ParameterKey'].should.equal(param_overrides[0]['ParameterKey'])
usw2_instance['StackInstance']['ParameterOverrides'][0]['ParameterValue'].should.equal(param_overrides[0]['ParameterValue'])
usw2_instance['StackInstance']['ParameterOverrides'][1]['ParameterKey'].should.equal(param_overrides[1]['ParameterKey'])
usw2_instance['StackInstance']['ParameterOverrides'][1]['ParameterValue'].should.equal(param_overrides[1]['ParameterValue'])
usw1_instance['StackInstance']['ParameterOverrides'][0]['ParameterKey'].should.equal(param_overrides[0]['ParameterKey'])
usw1_instance['StackInstance']['ParameterOverrides'][0]['ParameterValue'].should.equal(param_overrides[0]['ParameterValue'])
usw1_instance['StackInstance']['ParameterOverrides'][1]['ParameterKey'].should.equal(param_overrides[1]['ParameterKey'])
usw1_instance['StackInstance']['ParameterOverrides'][1]['ParameterValue'].should.equal(param_overrides[1]['ParameterValue'])
use1_instance['StackInstance']['ParameterOverrides'].should.be.empty
@mock_cloudformation
def test_boto3_delete_stack_instances():
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
cf_conn.create_stack_set(
StackSetName="test_stack_set",
TemplateBody=dummy_template_json,
)
cf_conn.create_stack_instances(
StackSetName="test_stack_set",
Accounts=['123456789012'],
Regions=['us-east-1', 'us-west-2'],
)
cf_conn.delete_stack_instances(
StackSetName="test_stack_set",
Accounts=['123456789012'],
Regions=['us-east-1'],
RetainStacks=False,
)
cf_conn.list_stack_instances(StackSetName="test_stack_set")['Summaries'].should.have.length_of(1)
cf_conn.list_stack_instances(StackSetName="test_stack_set")['Summaries'][0]['Region'].should.equal(
'us-west-2')
@mock_cloudformation
def test_boto3_create_stack_instances():
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
cf_conn.create_stack_set(
StackSetName="test_stack_set",
TemplateBody=dummy_template_json,
)
cf_conn.create_stack_instances(
StackSetName="test_stack_set",
Accounts=['123456789012'],
Regions=['us-east-1', 'us-west-2'],
)
cf_conn.list_stack_instances(StackSetName="test_stack_set")['Summaries'].should.have.length_of(2)
cf_conn.list_stack_instances(StackSetName="test_stack_set")['Summaries'][0]['Account'].should.equal(
'123456789012')
@mock_cloudformation
def test_boto3_create_stack_instances_with_param_overrides():
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
param = [
{'ParameterKey': 'TagDescription', 'ParameterValue': 'StackSetValue'},
{'ParameterKey': 'TagName', 'ParameterValue': 'StackSetValue2'},
]
param_overrides = [
{'ParameterKey': 'TagDescription', 'ParameterValue': 'OverrideValue'},
{'ParameterKey': 'TagName', 'ParameterValue': 'OverrideValue2'}
]
cf_conn.create_stack_set(
StackSetName="test_stack_set",
TemplateBody=dummy_template_yaml_with_ref,
Parameters=param,
)
cf_conn.create_stack_instances(
StackSetName="test_stack_set",
Accounts=['123456789012'],
Regions=['us-east-1', 'us-west-2'],
ParameterOverrides=param_overrides,
)
usw2_instance = cf_conn.describe_stack_instance(
StackSetName="test_stack_set",
StackInstanceAccount='123456789012',
StackInstanceRegion='us-west-2',
)
usw2_instance['StackInstance']['ParameterOverrides'][0]['ParameterKey'].should.equal(param_overrides[0]['ParameterKey'])
usw2_instance['StackInstance']['ParameterOverrides'][1]['ParameterKey'].should.equal(param_overrides[1]['ParameterKey'])
usw2_instance['StackInstance']['ParameterOverrides'][0]['ParameterValue'].should.equal(param_overrides[0]['ParameterValue'])
usw2_instance['StackInstance']['ParameterOverrides'][1]['ParameterValue'].should.equal(param_overrides[1]['ParameterValue'])
@mock_cloudformation
def test_update_stack_set():
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
param = [
{'ParameterKey': 'TagDescription', 'ParameterValue': 'StackSetValue'},
{'ParameterKey': 'TagName', 'ParameterValue': 'StackSetValue2'},
]
param_overrides = [
{'ParameterKey': 'TagDescription', 'ParameterValue': 'OverrideValue'},
{'ParameterKey': 'TagName', 'ParameterValue': 'OverrideValue2'}
]
cf_conn.create_stack_set(
StackSetName="test_stack_set",
TemplateBody=dummy_template_yaml_with_ref,
Parameters=param,
)
cf_conn.update_stack_set(
StackSetName='test_stack_set',
TemplateBody=dummy_template_yaml_with_ref,
Parameters=param_overrides,
)
stackset = cf_conn.describe_stack_set(StackSetName='test_stack_set')
stackset['StackSet']['Parameters'][0]['ParameterValue'].should.equal(param_overrides[0]['ParameterValue'])
stackset['StackSet']['Parameters'][1]['ParameterValue'].should.equal(param_overrides[1]['ParameterValue'])
stackset['StackSet']['Parameters'][0]['ParameterKey'].should.equal(param_overrides[0]['ParameterKey'])
stackset['StackSet']['Parameters'][1]['ParameterKey'].should.equal(param_overrides[1]['ParameterKey'])
@mock_cloudformation
def test_boto3_list_stack_set_operations():
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
cf_conn.create_stack_set(
StackSetName="test_stack_set",
TemplateBody=dummy_template_json,
)
cf_conn.create_stack_instances(
StackSetName="test_stack_set",
Accounts=['123456789012'],
Regions=['us-east-1', 'us-west-2'],
)
cf_conn.update_stack_instances(
StackSetName="test_stack_set",
Accounts=['123456789012'],
Regions=['us-east-1', 'us-west-2'],
)
list_operation = cf_conn.list_stack_set_operations(StackSetName="test_stack_set")
list_operation['Summaries'].should.have.length_of(2)
list_operation['Summaries'][-1]['Action'].should.equal('UPDATE')
@mock_cloudformation
def test_boto3_delete_stack_set():
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
cf_conn.create_stack_set(
StackSetName="test_stack_set",
TemplateBody=dummy_template_json,
)
cf_conn.delete_stack_set(StackSetName='test_stack_set')
cf_conn.describe_stack_set(StackSetName="test_stack_set")['StackSet']['Status'].should.equal(
'DELETED')
@mock_cloudformation
def test_boto3_create_stack_set():
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
cf_conn.create_stack_set(
StackSetName="test_stack_set",
TemplateBody=dummy_template_json,
)
cf_conn.describe_stack_set(StackSetName="test_stack_set")['StackSet']['TemplateBody'].should.equal(
dummy_template_json)
@mock_cloudformation
def test_boto3_create_stack_set_with_yaml():
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
cf_conn.create_stack_set(
StackSetName="test_stack_set",
TemplateBody=dummy_template_yaml,
)
cf_conn.describe_stack_set(StackSetName="test_stack_set")['StackSet']['TemplateBody'].should.equal(
dummy_template_yaml)
@mock_cloudformation
@mock_s3
def test_create_stack_set_from_s3_url():
s3 = boto3.client('s3')
s3_conn = boto3.resource('s3')
bucket = s3_conn.create_bucket(Bucket="foobar")
key = s3_conn.Object(
'foobar', 'template-key').put(Body=dummy_template_json)
key_url = s3.generate_presigned_url(
ClientMethod='get_object',
Params={
'Bucket': 'foobar',
'Key': 'template-key'
}
)
cf_conn = boto3.client('cloudformation', region_name='us-west-1')
cf_conn.create_stack_set(
StackSetName='stack_from_url',
TemplateURL=key_url,
)
cf_conn.describe_stack_set(StackSetName="stack_from_url")['StackSet']['TemplateBody'].should.equal(
dummy_template_json)
@mock_cloudformation
def test_boto3_create_stack_set_with_ref_yaml():
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
params = [
{'ParameterKey': 'TagDescription', 'ParameterValue': 'desc_ref'},
{'ParameterKey': 'TagName', 'ParameterValue': 'name_ref'},
]
cf_conn.create_stack_set(
StackSetName="test_stack",
TemplateBody=dummy_template_yaml_with_ref,
Parameters=params
)
cf_conn.describe_stack_set(StackSetName="test_stack")['StackSet']['TemplateBody'].should.equal(
dummy_template_yaml_with_ref)
@mock_cloudformation
def test_boto3_describe_stack_set_params():
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
params = [
{'ParameterKey': 'TagDescription', 'ParameterValue': 'desc_ref'},
{'ParameterKey': 'TagName', 'ParameterValue': 'name_ref'},
]
cf_conn.create_stack_set(
StackSetName="test_stack",
TemplateBody=dummy_template_yaml_with_ref,
Parameters=params
)
cf_conn.describe_stack_set(StackSetName="test_stack")['StackSet']['Parameters'].should.equal(
params)
@mock_cloudformation
def test_boto3_create_stack():