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_applied_schema
|
||||||
- [ ] upgrade_published_schema
|
- [ ] upgrade_published_schema
|
||||||
|
|
||||||
## cloudformation - 21% implemented
|
## cloudformation - 56% implemented
|
||||||
- [ ] cancel_update_stack
|
- [ ] cancel_update_stack
|
||||||
- [ ] continue_update_rollback
|
- [ ] continue_update_rollback
|
||||||
- [X] create_change_set
|
- [X] create_change_set
|
||||||
@ -488,7 +488,7 @@
|
|||||||
- [ ] describe_stack_resource
|
- [ ] describe_stack_resource
|
||||||
- [ ] describe_stack_resources
|
- [ ] describe_stack_resources
|
||||||
- [X] describe_stack_set
|
- [X] describe_stack_set
|
||||||
- [ ] describe_stack_set_operation
|
- [X] describe_stack_set_operation
|
||||||
- [X] describe_stacks
|
- [X] describe_stacks
|
||||||
- [ ] estimate_template_cost
|
- [ ] estimate_template_cost
|
||||||
- [X] execute_change_set
|
- [X] execute_change_set
|
||||||
@ -500,13 +500,13 @@
|
|||||||
- [ ] list_imports
|
- [ ] list_imports
|
||||||
- [X] list_stack_instances
|
- [X] list_stack_instances
|
||||||
- [X] list_stack_resources
|
- [X] list_stack_resources
|
||||||
- [ ] list_stack_set_operation_results
|
- [X] list_stack_set_operation_results
|
||||||
- [X] list_stack_set_operations
|
- [X] list_stack_set_operations
|
||||||
- [X] list_stack_sets
|
- [X] list_stack_sets
|
||||||
- [X] list_stacks
|
- [X] list_stacks
|
||||||
- [ ] set_stack_policy
|
- [ ] set_stack_policy
|
||||||
- [ ] signal_resource
|
- [ ] signal_resource
|
||||||
- [ ] stop_stack_set_operation
|
- [X] stop_stack_set_operation
|
||||||
- [X] update_stack
|
- [X] update_stack
|
||||||
- [X] update_stack_instances
|
- [X] update_stack_instances
|
||||||
- [X] update_stack_set
|
- [X] update_stack_set
|
||||||
|
@ -22,7 +22,10 @@ from .exceptions import ValidationError
|
|||||||
|
|
||||||
class FakeStackSet(BaseModel):
|
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.id = stackset_id
|
||||||
self.arn = generate_stackset_arn(stackset_id, region)
|
self.arn = generate_stackset_arn(stackset_id, region)
|
||||||
self.name = name
|
self.name = name
|
||||||
@ -34,24 +37,33 @@ class FakeStackSet(BaseModel):
|
|||||||
self.execution_role = execution_role
|
self.execution_role = execution_role
|
||||||
self.status = status
|
self.status = status
|
||||||
self.instances = FakeStackInstances(parameters, self.id, self.name)
|
self.instances = FakeStackInstances(parameters, self.id, self.name)
|
||||||
|
self.stack_instances = self.instances.stack_instances
|
||||||
self.operations = []
|
self.operations = []
|
||||||
|
|
||||||
@property
|
def _create_operation(self, operation_id, action, status, accounts=[], regions=[]):
|
||||||
def stack_instances(self):
|
|
||||||
return self.instances.stack_instances
|
|
||||||
|
|
||||||
def _create_operation(self, operation_id, action, status):
|
|
||||||
operation = {
|
operation = {
|
||||||
'OperationId': str(operation_id),
|
'OperationId': str(operation_id),
|
||||||
'Action': action,
|
'Action': action,
|
||||||
'Status': status,
|
'Status': status,
|
||||||
'CreationTimestamp': datetime.now(),
|
'CreationTimestamp': datetime.now(),
|
||||||
'EndTimestamp': datetime.now() + timedelta(minutes=2),
|
'EndTimestamp': datetime.now() + timedelta(minutes=2),
|
||||||
|
'Instances': [{account: region} for account in accounts for region in regions],
|
||||||
}
|
}
|
||||||
|
|
||||||
self.operations += [operation]
|
self.operations += [operation]
|
||||||
return 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):
|
def delete(self):
|
||||||
self.status = 'DELETED'
|
self.status = 'DELETED'
|
||||||
|
|
||||||
@ -70,7 +82,9 @@ class FakeStackSet(BaseModel):
|
|||||||
if accounts and regions:
|
if accounts and regions:
|
||||||
self.update_instances(accounts, regions, self.parameters)
|
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
|
return operation
|
||||||
|
|
||||||
def create_stack_instances(self, accounts, regions, parameters, operation_id=None):
|
def create_stack_instances(self, accounts, regions, parameters, operation_id=None):
|
||||||
@ -80,7 +94,8 @@ class FakeStackSet(BaseModel):
|
|||||||
parameters = self.parameters
|
parameters = self.parameters
|
||||||
|
|
||||||
self.instances.create_instances(accounts, regions, parameters, operation_id)
|
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):
|
def delete_stack_instances(self, accounts, regions, operation_id=None):
|
||||||
if not operation_id:
|
if not operation_id:
|
||||||
@ -88,14 +103,17 @@ class FakeStackSet(BaseModel):
|
|||||||
|
|
||||||
self.instances.delete(accounts, regions)
|
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):
|
def update_instances(self, accounts, regions, parameters, operation_id=None):
|
||||||
if not operation_id:
|
if not operation_id:
|
||||||
operation_id = uuid.uuid4()
|
operation_id = uuid.uuid4()
|
||||||
|
|
||||||
self.instances.update(accounts, regions, parameters)
|
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
|
return operation
|
||||||
|
|
||||||
|
|
||||||
|
@ -412,6 +412,30 @@ class CloudFormationResponse(BaseResponse):
|
|||||||
template = self.response_template(LIST_STACK_SET_OPERATIONS_RESPONSE_TEMPLATE)
|
template = self.response_template(LIST_STACK_SET_OPERATIONS_RESPONSE_TEMPLATE)
|
||||||
return template.render(stackset=stackset)
|
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):
|
def update_stack_set(self):
|
||||||
stackset_name = self._get_param('StackSetName')
|
stackset_name = self._get_param('StackSetName')
|
||||||
operation_id = self._get_param('OperationId')
|
operation_id = self._get_param('OperationId')
|
||||||
@ -878,3 +902,56 @@ LIST_STACK_SET_OPERATIONS_RESPONSE_TEMPLATE = """<ListStackSetOperationsResponse
|
|||||||
</ResponseMetadata>
|
</ResponseMetadata>
|
||||||
</ListStackSetOperationsResponse>
|
</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')
|
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
|
@mock_cloudformation
|
||||||
def test_boto3_update_stack_instances():
|
def test_boto3_update_stack_instances():
|
||||||
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
|
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user