Operations (#4)
Added stop, list operation results, and describe operation
This commit is contained in:
parent
4207a8e182
commit
5fb43ee7b6
@ -470,7 +470,7 @@
|
||||
- [ ] upgrade_applied_schema
|
||||
- [ ] upgrade_published_schema
|
||||
|
||||
## cloudformation - 21% implemented
|
||||
## cloudformation - 56% implemented
|
||||
- [ ] cancel_update_stack
|
||||
- [ ] continue_update_rollback
|
||||
- [X] create_change_set
|
||||
@ -488,7 +488,7 @@
|
||||
- [ ] describe_stack_resource
|
||||
- [ ] describe_stack_resources
|
||||
- [X] describe_stack_set
|
||||
- [ ] describe_stack_set_operation
|
||||
- [X] describe_stack_set_operation
|
||||
- [X] describe_stacks
|
||||
- [ ] estimate_template_cost
|
||||
- [X] execute_change_set
|
||||
@ -500,13 +500,13 @@
|
||||
- [ ] list_imports
|
||||
- [X] list_stack_instances
|
||||
- [X] list_stack_resources
|
||||
- [ ] list_stack_set_operation_results
|
||||
- [X] list_stack_set_operation_results
|
||||
- [X] list_stack_set_operations
|
||||
- [X] list_stack_sets
|
||||
- [X] list_stacks
|
||||
- [ ] set_stack_policy
|
||||
- [ ] signal_resource
|
||||
- [ ] stop_stack_set_operation
|
||||
- [X] stop_stack_set_operation
|
||||
- [X] update_stack
|
||||
- [X] update_stack_instances
|
||||
- [X] update_stack_set
|
||||
|
@ -22,7 +22,10 @@ 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):
|
||||
def __init__(self, stackset_id, name, template, region='us-east-1',
|
||||
status='ACTIVE', description=None, parameters=None, tags=None,
|
||||
admin_role='AWSCloudFormationStackSetAdministrationRole',
|
||||
execution_role='AWSCloudFormationStackSetExecutionRole'):
|
||||
self.id = stackset_id
|
||||
self.arn = generate_stackset_arn(stackset_id, region)
|
||||
self.name = name
|
||||
@ -34,24 +37,33 @@ class FakeStackSet(BaseModel):
|
||||
self.execution_role = execution_role
|
||||
self.status = status
|
||||
self.instances = FakeStackInstances(parameters, self.id, self.name)
|
||||
self.stack_instances = self.instances.stack_instances
|
||||
self.operations = []
|
||||
|
||||
@property
|
||||
def stack_instances(self):
|
||||
return self.instances.stack_instances
|
||||
|
||||
def _create_operation(self, operation_id, action, status):
|
||||
def _create_operation(self, operation_id, action, status, accounts=[], regions=[]):
|
||||
operation = {
|
||||
'OperationId': str(operation_id),
|
||||
'Action': action,
|
||||
'Status': status,
|
||||
'CreationTimestamp': datetime.now(),
|
||||
'EndTimestamp': datetime.now() + timedelta(minutes=2),
|
||||
'Instances': [{account: region} for account in accounts for region in regions],
|
||||
}
|
||||
|
||||
self.operations += [operation]
|
||||
return operation
|
||||
|
||||
def get_operation(self, operation_id):
|
||||
for operation in self.operations:
|
||||
if operation_id == operation['OperationId']:
|
||||
return operation
|
||||
raise ValidationError(operation_id)
|
||||
|
||||
def update_operation(self, operation_id, status):
|
||||
operation = self.get_operation(operation_id)
|
||||
operation['Status'] = status
|
||||
return operation_id
|
||||
|
||||
def delete(self):
|
||||
self.status = 'DELETED'
|
||||
|
||||
@ -70,7 +82,9 @@ class FakeStackSet(BaseModel):
|
||||
if accounts and regions:
|
||||
self.update_instances(accounts, regions, self.parameters)
|
||||
|
||||
operation = self._create_operation(operation_id=operation_id, action='UPDATE', status='SUCCEEDED')
|
||||
operation = self._create_operation(operation_id=operation_id,
|
||||
action='UPDATE', status='SUCCEEDED', accounts=accounts,
|
||||
regions=regions)
|
||||
return operation
|
||||
|
||||
def create_stack_instances(self, accounts, regions, parameters, operation_id=None):
|
||||
@ -80,7 +94,8 @@ class FakeStackSet(BaseModel):
|
||||
parameters = self.parameters
|
||||
|
||||
self.instances.create_instances(accounts, regions, parameters, operation_id)
|
||||
self._create_operation(operation_id=operation_id, action='CREATE', status='SUCCEEDED')
|
||||
self._create_operation(operation_id=operation_id, action='CREATE',
|
||||
status='SUCCEEDED', accounts=accounts, regions=regions)
|
||||
|
||||
def delete_stack_instances(self, accounts, regions, operation_id=None):
|
||||
if not operation_id:
|
||||
@ -88,14 +103,17 @@ class FakeStackSet(BaseModel):
|
||||
|
||||
self.instances.delete(accounts, regions)
|
||||
|
||||
self._create_operation(operation_id=operation_id, action='DELETE', status='SUCCEEDED')
|
||||
self._create_operation(operation_id=operation_id, action='DELETE',
|
||||
status='SUCCEEDED', accounts=accounts, regions=regions)
|
||||
|
||||
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')
|
||||
operation = self._create_operation(operation_id=operation_id,
|
||||
action='UPDATE', status='SUCCEEDED', accounts=accounts,
|
||||
regions=regions)
|
||||
return operation
|
||||
|
||||
|
||||
|
@ -412,6 +412,30 @@ class CloudFormationResponse(BaseResponse):
|
||||
template = self.response_template(LIST_STACK_SET_OPERATIONS_RESPONSE_TEMPLATE)
|
||||
return template.render(stackset=stackset)
|
||||
|
||||
def stop_stack_set_operation(self):
|
||||
stackset_name = self._get_param('StackSetName')
|
||||
operation_id = self._get_param('OperationId')
|
||||
stackset = self.cloudformation_backend.get_stack_set(stackset_name)
|
||||
stackset.update_operation(operation_id, 'STOPPED')
|
||||
template = self.response_template(STOP_STACK_SET_OPERATION_RESPONSE_TEMPLATE)
|
||||
return template.render()
|
||||
|
||||
def describe_stack_set_operation(self):
|
||||
stackset_name = self._get_param('StackSetName')
|
||||
operation_id = self._get_param('OperationId')
|
||||
stackset = self.cloudformation_backend.get_stack_set(stackset_name)
|
||||
operation = stackset.get_operation(operation_id)
|
||||
template = self.response_template(DESCRIBE_STACKSET_OPERATION_RESPONSE_TEMPLATE)
|
||||
return template.render(stackset=stackset, operation=operation)
|
||||
|
||||
def list_stack_set_operation_results(self):
|
||||
stackset_name = self._get_param('StackSetName')
|
||||
operation_id = self._get_param('OperationId')
|
||||
stackset = self.cloudformation_backend.get_stack_set(stackset_name)
|
||||
operation = stackset.get_operation(operation_id)
|
||||
template = self.response_template(LIST_STACK_SET_OPERATION_RESULTS_RESPONSE_TEMPLATE)
|
||||
return template.render(operation=operation)
|
||||
|
||||
def update_stack_set(self):
|
||||
stackset_name = self._get_param('StackSetName')
|
||||
operation_id = self._get_param('OperationId')
|
||||
@ -878,3 +902,56 @@ LIST_STACK_SET_OPERATIONS_RESPONSE_TEMPLATE = """<ListStackSetOperationsResponse
|
||||
</ResponseMetadata>
|
||||
</ListStackSetOperationsResponse>
|
||||
"""
|
||||
|
||||
STOP_STACK_SET_OPERATION_RESPONSE_TEMPLATE = """<StopStackSetOperationResponse xmlns="http://internal.amazon.com/coral/com.amazonaws.maestro.service.v20160713/">
|
||||
<StopStackSetOperationResult/>
|
||||
<ResponseMetadata>
|
||||
<RequestId>2188554a-07c6-4396-b2c5-example</RequestId>
|
||||
</ResponseMetadata> </StopStackSetOperationResponse>
|
||||
"""
|
||||
|
||||
DESCRIBE_STACKSET_OPERATION_RESPONSE_TEMPLATE = """<DescribeStackSetOperationResponse xmlns="http://internal.amazon.com/coral/com.amazonaws.maestro.service.v20160713/">
|
||||
<DescribeStackSetOperationResult>
|
||||
<StackSetOperation>
|
||||
<ExecutionRoleName>{{ stackset.execution_role }}</ExecutionRoleName>
|
||||
<AdministrationRoleARN>arn:aws:iam::123456789012:role/{{ stackset.admin_role }}</AdministrationRoleARN>
|
||||
<StackSetId>{{ stackset.id }}</StackSetId>
|
||||
<CreationTimestamp>{{ operation.CreationTimestamp }}</CreationTimestamp>
|
||||
<OperationId>{{ operation.OperationId }}</OperationId>
|
||||
<Action>{{ operation.Action }}</Action>
|
||||
<OperationPreferences>
|
||||
<RegionOrder/>
|
||||
</OperationPreferences>
|
||||
<EndTimestamp>{{ operation.EndTimestamp }}</EndTimestamp>
|
||||
<Status>{{ operation.Status }}</Status>
|
||||
</StackSetOperation>
|
||||
</DescribeStackSetOperationResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>2edc27b6-9ce2-486a-a192-example</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeStackSetOperationResponse>
|
||||
"""
|
||||
|
||||
LIST_STACK_SET_OPERATION_RESULTS_RESPONSE_TEMPLATE = """<ListStackSetOperationResultsResponse xmlns="http://internal.amazon.com/coral/com.amazonaws.maestro.service.v20160713/">
|
||||
<ListStackSetOperationResultsResult>
|
||||
<Summaries>
|
||||
{% for instance in operation.Instances %}
|
||||
{% for account, region in instance.items() %}
|
||||
<member>
|
||||
<AccountGateResult>
|
||||
<StatusReason>Function not found: arn:aws:lambda:us-west-2:123456789012:function:AWSCloudFormationStackSetAccountGate</StatusReason>
|
||||
<Status>SKIPPED</Status>
|
||||
</AccountGateResult>
|
||||
<Region>{{ region }}</Region>
|
||||
<Account>{{ account }}</Account>
|
||||
<Status>{{ operation.Status }}</Status>
|
||||
</member>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</Summaries>
|
||||
</ListStackSetOperationResultsResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>ac05a9ce-5f98-4197-a29b-example</RequestId>
|
||||
</ResponseMetadata>
|
||||
</ListStackSetOperationResultsResponse>
|
||||
"""
|
||||
|
@ -240,6 +240,86 @@ def test_boto3_list_stacksets_contents():
|
||||
stacksets['Summaries'][0].should.have.key('Status').which.should.equal('ACTIVE')
|
||||
|
||||
|
||||
@mock_cloudformation
|
||||
def test_boto3_stop_stack_set_operation():
|
||||
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-1', 'us-west-2'],
|
||||
)
|
||||
operation_id = cf_conn.list_stack_set_operations(
|
||||
StackSetName="test_stack_set")['Summaries'][-1]['OperationId']
|
||||
cf_conn.stop_stack_set_operation(
|
||||
StackSetName="test_stack_set",
|
||||
OperationId=operation_id
|
||||
)
|
||||
list_operation = cf_conn.list_stack_set_operations(
|
||||
StackSetName="test_stack_set"
|
||||
)
|
||||
list_operation['Summaries'][-1]['Status'].should.equal('STOPPED')
|
||||
|
||||
|
||||
@mock_cloudformation
|
||||
def test_boto3_describe_stack_set_operation():
|
||||
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-1', 'us-west-2'],
|
||||
)
|
||||
operation_id = cf_conn.list_stack_set_operations(
|
||||
StackSetName="test_stack_set")['Summaries'][-1]['OperationId']
|
||||
cf_conn.stop_stack_set_operation(
|
||||
StackSetName="test_stack_set",
|
||||
OperationId=operation_id
|
||||
)
|
||||
response = cf_conn.describe_stack_set_operation(
|
||||
StackSetName="test_stack_set",
|
||||
OperationId=operation_id,
|
||||
)
|
||||
|
||||
response['StackSetOperation']['Status'].should.equal('STOPPED')
|
||||
response['StackSetOperation']['Action'].should.equal('CREATE')
|
||||
|
||||
|
||||
@mock_cloudformation
|
||||
def test_boto3_list_stack_set_operation_results():
|
||||
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-1', 'us-west-2'],
|
||||
)
|
||||
operation_id = cf_conn.list_stack_set_operations(
|
||||
StackSetName="test_stack_set")['Summaries'][-1]['OperationId']
|
||||
|
||||
cf_conn.stop_stack_set_operation(
|
||||
StackSetName="test_stack_set",
|
||||
OperationId=operation_id
|
||||
)
|
||||
response = cf_conn.list_stack_set_operation_results(
|
||||
StackSetName="test_stack_set",
|
||||
OperationId=operation_id,
|
||||
)
|
||||
|
||||
response['Summaries'].should.have.length_of(3)
|
||||
response['Summaries'][0].should.have.key('Account').which.should.equal('123456789012')
|
||||
response['Summaries'][1].should.have.key('Status').which.should.equal('STOPPED')
|
||||
|
||||
|
||||
@mock_cloudformation
|
||||
def test_boto3_update_stack_instances():
|
||||
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
|
||||
|
Loading…
Reference in New Issue
Block a user