def validate_template(req_data): LOGGER.debug(req_data) response_content = """ <Capabilities></Capabilities> <CapabilitiesReason></CapabilitiesReason> <DeclaredTransforms></DeclaredTransforms> <Description></Description> <Parameters> </Parameters> """ try: template_deployer.template_to_json(req_data.get('TemplateBody')[0]) response = make_response('ValidateTemplate', response_content) return response except Exception as err: response = error_response('Template Validation Error: %s' % err) return response
def return_response(self, method, path, data, headers, response): req_data = None if method == 'POST' and path == '/': req_data = urlparse.parse_qs(to_str(data)) action = req_data.get('Action')[0] if req_data: if action == 'DescribeStackResources': if response.status_code < 300: response_dict = xmltodict.parse(response.content)['DescribeStackResourcesResponse'] resources = response_dict['DescribeStackResourcesResult']['StackResources'] if not resources: # Check if stack exists stack_name = req_data.get('StackName')[0] cloudformation_client = aws_stack.connect_to_service('cloudformation') try: cloudformation_client.describe_stacks(StackName=stack_name) except Exception: return error_response('Stack with id %s does not exist' % stack_name, code=404) if action == 'DescribeStackResource': if response.status_code >= 500: # fix an error in moto where it fails with 500 if the stack does not exist return error_response('Stack resource does not exist', code=404) if action == 'ListStackResources': response_dict = xmltodict.parse(response.content, force_list=('member'))['ListStackResourcesResponse'] resources = response_dict['ListStackResourcesResult']['StackResourceSummaries'] if resources: sqs_client = aws_stack.connect_to_service('sqs') content_str = content_str_original = to_str(response.content) new_response = Response() new_response.status_code = response.status_code new_response.headers = response.headers for resource in resources['member']: if resource['ResourceType'] == 'AWS::SQS::Queue': try: queue_name = resource['PhysicalResourceId'] queue_url = sqs_client.get_queue_url(QueueName=queue_name)['QueueUrl'] except Exception: stack_name = req_data.get('StackName')[0] return error_response('Stack with id %s does not exist' % stack_name, code=404) content_str = re.sub(resource['PhysicalResourceId'], queue_url, content_str) new_response._content = content_str if content_str_original != new_response._content: # if changes have been made, return patched response new_response.headers['content-length'] = len(new_response._content) return new_response elif action in ('CreateStack', 'UpdateStack'): if response.status_code >= 400: return response # run the actual deployment template = template_deployer.template_to_json(req_data.get('TemplateBody')[0]) template_deployer.deploy_template(template, req_data.get('StackName')[0])
def test_list_stack_resources_returns_queue_urls(self): cloudformation = aws_stack.connect_to_resource('cloudformation') template = template_deployer.template_to_json(load_file(TEST_TEMPLATE_2)) cloudformation.create_stack(StackName=TEST_STACK_NAME_2, TemplateBody=template) def check_stack(): stack = get_stack_details(TEST_STACK_NAME_2) assert stack['StackStatus'] == 'CREATE_COMPLETE' retry(check_stack, retries=3, sleep=2) list_stack_summaries = list_stack_resources(TEST_STACK_NAME_2) queue_urls = get_queue_urls() for resource in list_stack_summaries: assert resource['PhysicalResourceId'] in queue_urls
def test_apply_template(self): cloudformation = aws_stack.connect_to_resource('cloudformation') template = template_deployer.template_to_json(load_file(TEST_TEMPLATE_1)) # deploy template cloudformation.create_stack(StackName=TEST_STACK_NAME, TemplateBody=template) # wait for deployment to finish def check_stack(): stack = get_stack_details(TEST_STACK_NAME) assert stack['StackStatus'] == 'CREATE_COMPLETE' retry(check_stack, retries=3, sleep=2) # assert that bucket has been created assert bucket_exists('cf-test-bucket-1') # assert that queue has been created assert queue_exists('cf-test-queue-1') # assert that stream has been created assert stream_exists('cf-test-stream-1') # assert that queue has been created resource = describe_stack_resource(TEST_STACK_NAME, 'SQSQueueNoNameProperty') assert queue_exists(resource['PhysicalResourceId'])
def execute_change_set(req_data): cs_arn = req_data.get('ChangeSetName')[0] stack_name = req_data.get('StackName')[0] cs_details = CHANGE_SETS.get(cs_arn) if not cs_details: return error_response('Change Set %s does not exist' % cs_arn, 404, 'ChangeSetNotFound') # convert to JSON (might have been YAML, and update_stack/create_stack seem to only work with JSON) template = template_deployer.template_to_json(cs_details.get('TemplateBody')[0]) # update stack information cloudformation_service = aws_stack.connect_to_service('cloudformation') if stack_exists(stack_name): cloudformation_service.update_stack(StackName=stack_name, TemplateBody=template) else: cloudformation_service.create_stack(StackName=stack_name, TemplateBody=template) # now run the actual deployment template_deployer.deploy_template(template, stack_name) response = make_response('ExecuteChangeSet') return response
def test_validate_template(self): cloudformation = aws_stack.connect_to_service('cloudformation') template = template_deployer.template_to_json( load_file(TEST_TEMPLATE_1)) response = cloudformation.validate_template(TemplateBody=template) self.assertEqual(response['ResponseMetadata']['HTTPStatusCode'], 200)
def test_create_delete_stack(self): cloudformation = aws_stack.connect_to_resource('cloudformation') cf_client = aws_stack.connect_to_service('cloudformation') s3 = aws_stack.connect_to_service('s3') sns = aws_stack.connect_to_service('sns') sqs = aws_stack.connect_to_service('sqs') apigateway = aws_stack.connect_to_service('apigateway') template = template_deployer.template_to_json( load_file(TEST_TEMPLATE_1)) # deploy template stack_name = 'stack-%s' % short_uid() cloudformation.create_stack(StackName=stack_name, TemplateBody=template) # wait for deployment to finish def check_stack(): stack = get_stack_details(stack_name) self.assertEqual(stack['StackStatus'], 'CREATE_COMPLETE') retry(check_stack, retries=3, sleep=2) # assert that resources have been created assert bucket_exists('cf-test-bucket-1') queue_url = queue_exists('cf-test-queue-1') assert queue_url topic_arn = topic_exists('%s-test-topic-1-1' % stack_name) assert topic_arn assert stream_exists('cf-test-stream-1') resource = describe_stack_resource(stack_name, 'SQSQueueNoNameProperty') assert queue_exists(resource['PhysicalResourceId']) assert ssm_param_exists('cf-test-param-1') # assert that tags have been created tags = s3.get_bucket_tagging(Bucket='cf-test-bucket-1')['TagSet'] self.assertEqual( tags, [{ 'Key': 'foobar', 'Value': aws_stack.get_sqs_queue_url('cf-test-queue-1') }]) tags = sns.list_tags_for_resource(ResourceArn=topic_arn)['Tags'] self.assertEqual( tags, [{ 'Key': 'foo', 'Value': 'cf-test-bucket-1' }, { 'Key': 'bar', 'Value': aws_stack.s3_bucket_arn('cf-test-bucket-1') }]) queue_tags = sqs.list_queue_tags(QueueUrl=queue_url) self.assertIn('Tags', queue_tags) self.assertEqual(queue_tags['Tags'], { 'key1': 'value1', 'key2': 'value2' }) # assert that bucket notifications have been created notifications = s3.get_bucket_notification_configuration( Bucket='cf-test-bucket-1') self.assertIn('QueueConfigurations', notifications) self.assertIn('LambdaFunctionConfigurations', notifications) self.assertEqual(notifications['QueueConfigurations'][0]['QueueArn'], 'aws:arn:sqs:test:testqueue') self.assertEqual(notifications['QueueConfigurations'][0]['Events'], ['s3:ObjectDeleted:*']) self.assertEqual( notifications['LambdaFunctionConfigurations'][0] ['LambdaFunctionArn'], 'aws:arn:lambda:test:testfunc') self.assertEqual( notifications['LambdaFunctionConfigurations'][0]['Events'], ['s3:ObjectCreated:*']) # assert that subscriptions have been created subs = sns.list_subscriptions()['Subscriptions'] subs = [ s for s in subs if (':%s:cf-test-queue-1' % TEST_AWS_ACCOUNT_ID) in s['Endpoint'] ] self.assertEqual(len(subs), 1) self.assertIn( ':%s:%s-test-topic-1-1' % (TEST_AWS_ACCOUNT_ID, stack_name), subs[0]['TopicArn']) # assert that subscription attributes are added properly attrs = sns.get_subscription_attributes( SubscriptionArn=subs[0]['SubscriptionArn'])['Attributes'] self.assertEqual( attrs, { 'Endpoint': subs[0]['Endpoint'], 'Protocol': 'sqs', 'SubscriptionArn': subs[0]['SubscriptionArn'], 'TopicArn': subs[0]['TopicArn'], 'FilterPolicy': json.dumps({'eventType': ['created']}) }) # assert that Gateway responses have been created test_api_name = 'test-api' api = [ a for a in apigateway.get_rest_apis()['items'] if a['name'] == test_api_name ][0] responses = apigateway.get_gateway_responses( restApiId=api['id'])['items'] self.assertEqual(len(responses), 2) types = [r['responseType'] for r in responses] self.assertEqual(set(types), set(['UNAUTHORIZED', 'DEFAULT_5XX'])) # delete the stack cf_client.delete_stack(StackName=stack_name) # assert that resources have been deleted assert not bucket_exists('cf-test-bucket-1') assert not queue_exists('cf-test-queue-1') assert not topic_exists('%s-test-topic-1-1' % stack_name) retry(lambda: self.assertFalse(stream_exists('cf-test-stream-1')))
def test_validate_template(self): cloudformation = aws_stack.connect_to_service('cloudformation') template = template_deployer.template_to_json(load_file(TEST_TEMPLATE_1)) response = cloudformation.validate_template(TemplateBody=template) assert response['ResponseMetadata']['HTTPStatusCode'] == 200
def test_create_delete_stack(self): cloudformation = aws_stack.connect_to_resource('cloudformation') cf_client = aws_stack.connect_to_service('cloudformation') s3 = aws_stack.connect_to_service('s3') sns = aws_stack.connect_to_service('sns') apigateway = aws_stack.connect_to_service('apigateway') template = template_deployer.template_to_json( load_file(TEST_TEMPLATE_1)) # deploy template stack_name = 'stack-%s' % short_uid() cloudformation.create_stack(StackName=stack_name, TemplateBody=template) # wait for deployment to finish def check_stack(): stack = get_stack_details(stack_name) self.assertEqual(stack['StackStatus'], 'CREATE_COMPLETE') retry(check_stack, retries=3, sleep=2) # assert that resources have been created assert bucket_exists('cf-test-bucket-1') assert queue_exists('cf-test-queue-1') topic_arn = topic_exists('%s-test-topic-1-1' % stack_name) assert topic_arn assert stream_exists('cf-test-stream-1') resource = describe_stack_resource(stack_name, 'SQSQueueNoNameProperty') assert queue_exists(resource['PhysicalResourceId']) # assert that tags have been created tags = s3.get_bucket_tagging(Bucket='cf-test-bucket-1')['TagSet'] self.assertEqual( tags, [{ 'Key': 'foobar', 'Value': aws_stack.get_sqs_queue_url('cf-test-queue-1') }]) tags = sns.list_tags_for_resource(ResourceArn=topic_arn)['Tags'] self.assertEqual( tags, [{ 'Key': 'foo', 'Value': 'cf-test-bucket-1' }, { 'Key': 'bar', 'Value': aws_stack.s3_bucket_arn('cf-test-bucket-1') }]) # assert that subscriptions have been created subs = sns.list_subscriptions()['Subscriptions'] subs = [ s for s in subs if (':%s:cf-test-queue-1' % TEST_AWS_ACCOUNT_ID) in s['Endpoint'] ] self.assertEqual(len(subs), 1) self.assertIn( ':%s:%s-test-topic-1-1' % (TEST_AWS_ACCOUNT_ID, stack_name), subs[0]['TopicArn']) # assert that Gateway responses have been created test_api_name = 'test-api' api = [ a for a in apigateway.get_rest_apis()['items'] if a['name'] == test_api_name ][0] responses = apigateway.get_gateway_responses( restApiId=api['id'])['items'] self.assertEqual(len(responses), 2) types = [r['responseType'] for r in responses] self.assertEqual(set(types), set(['UNAUTHORIZED', 'DEFAULT_5XX'])) # delete the stack cf_client.delete_stack(StackName=stack_name) # assert that resources have been deleted assert not bucket_exists('cf-test-bucket-1') assert not queue_exists('cf-test-queue-1') assert not topic_exists('%s-test-topic-1-1' % stack_name) retry(lambda: self.assertFalse(stream_exists('cf-test-stream-1')))
def return_response(self, method, path, data, headers, response): req_data = None if method == 'POST' and path == '/': req_data = urlparse.parse_qs(to_str(data)) action = req_data.get('Action')[0] if req_data: if action == 'DescribeStackResources': if response.status_code < 300: response_dict = xmltodict.parse( response.content)['DescribeStackResourcesResponse'] resources = response_dict['DescribeStackResourcesResult'][ 'StackResources'] if not resources: # Check if stack exists stack_name = req_data.get('StackName')[0] cloudformation_client = aws_stack.connect_to_service( 'cloudformation') try: cloudformation_client.describe_stacks( StackName=stack_name) except Exception: return error_response( 'Stack with name %s does not exist' % stack_name, code=404) if action == 'DescribeStackResource': if response.status_code >= 500: # fix an error in moto where it fails with 500 if the stack does not exist return error_response('Stack resource does not exist', code=404) if action == 'ListStackResources': response_dict = xmltodict.parse( response.content, force_list=('member'))['ListStackResourcesResponse'] resources = response_dict['ListStackResourcesResult'][ 'StackResourceSummaries'] if resources: sqs_client = aws_stack.connect_to_service('sqs') content_str = content_str_original = to_str( response.content) new_response = Response() new_response.status_code = response.status_code new_response.headers = response.headers for resource in resources['member']: if resource['ResourceType'] == 'AWS::SQS::Queue': try: queue_name = resource['PhysicalResourceId'] queue_url = sqs_client.get_queue_url( QueueName=queue_name)['QueueUrl'] except Exception: stack_name = req_data.get('StackName')[0] return error_response( 'Stack with name %s does not exist' % stack_name, code=404) content_str = re.sub( resource['PhysicalResourceId'], queue_url, content_str) new_response._content = content_str if content_str_original != new_response._content: # if changes have been made, return patched response new_response.headers['content-length'] = len( new_response._content) return new_response elif action in ('CreateStack', 'UpdateStack'): if response.status_code >= 400: return response # run the actual deployment template = template_deployer.template_to_json( req_data.get('TemplateBody')[0]) template_deployer.deploy_template(template, req_data.get('StackName')[0])
def update_cloudformation(method, path, data, headers, response=None, return_forward_info=False): req_data = None if method == 'POST' and path == '/': req_data = urlparse.parse_qs(data) action = req_data.get('Action')[0] if return_forward_info: if req_data: if action == 'CreateChangeSet': return create_change_set(req_data) elif action == 'DescribeChangeSet': return describe_change_set(req_data) elif action == 'ExecuteChangeSet': return execute_change_set(req_data) elif action == 'UpdateStack' and req_data.get('TemplateURL'): # Temporary fix until the moto CF backend can handle TemplateURL (currently fails) url = re.sub(r'https?://s3\.amazonaws\.com', aws_stack.get_local_service_url('s3'), req_data.get('TemplateURL')[0]) req_data['TemplateBody'] = requests.get(url).content modified_data = urlparse.urlencode(req_data, doseq=True) return Request(data=modified_data, headers=headers, method=method) return True if req_data: if action == 'DescribeStackResources': if response.status_code < 300: response_dict = xmltodict.parse( response.content)['DescribeStackResourcesResponse'] resources = response_dict['DescribeStackResourcesResult'][ 'StackResources'] if not resources: # Check if stack exists stack_name = req_data.get('StackName')[0] cloudformation_client = aws_stack.connect_to_service( 'cloudformation') try: cloudformation_client.describe_stacks( StackName=stack_name) except Exception as e: return error_response( 'Stack with id %s does not exist' % stack_name, code=404) if action == 'DescribeStackResource': if response.status_code >= 500: # fix an error in moto where it fails with 500 if the stack does not exist return error_response('Stack resource does not exist', code=404) elif action == 'CreateStack' or action == 'UpdateStack': # run the actual deployment template = template_deployer.template_to_json( req_data.get('TemplateBody')[0]) template_deployer.deploy_template(template, req_data.get('StackName')[0]) if response.status_code >= 400: return make_response(action)