def _generate_restapi(self, resource, template): # type: (models.RestAPI, Dict[str, Any]) -> None resources = template['Resources'] resources['RestAPI'] = { 'Type': 'AWS::Serverless::Api', 'Properties': { 'StageName': resource.api_gateway_stage, 'DefinitionBody': resource.swagger_doc, } } handler_cfn_name = to_cfn_resource_name( resource.lambda_function.resource_name) api_handler = template['Resources'].pop(handler_cfn_name) template['Resources']['APIHandler'] = api_handler resources['APIHandlerInvokePermission'] = { 'Type': 'AWS::Lambda::Permission', 'Properties': { 'FunctionName': { 'Ref': 'APIHandler' }, 'Action': 'lambda:InvokeFunction', 'Principal': 'apigateway.amazonaws.com', 'SourceArn': { 'Fn::Sub': [ ('arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}' ':${RestAPIId}/*'), { 'RestAPIId': { 'Ref': 'RestAPI' } }, ] }, } } for auth in resource.authorizers: auth_cfn_name = to_cfn_resource_name(auth.resource_name) resources[auth_cfn_name + 'InvokePermission'] = { 'Type': 'AWS::Lambda::Permission', 'Properties': { 'FunctionName': { 'Fn::GetAtt': [auth_cfn_name, 'Arn'] }, 'Action': 'lambda:InvokeFunction', 'Principal': 'apigateway.amazonaws.com', 'SourceArn': { 'Fn::Sub': [ ('arn:aws:execute-api:${AWS::Region}:' '${AWS::AccountId}:${RestAPIId}/*'), { 'RestAPIId': { 'Ref': 'RestAPI' } }, ] }, } } self._inject_restapi_outputs(template)
def _generate_snslambdasubscription(self, resource, template): # type: (models.SNSLambdaSubscription, Dict[str, Any]) -> None function_cfn_name = to_cfn_resource_name( resource.lambda_function.resource_name) function_cfn = template['Resources'][function_cfn_name] sns_cfn_name = self._register_cfn_resource_name( resource.resource_name) if resource.topic.startswith('arn:aws:sns:'): topic_arn = resource.topic # type: Union[str, Dict[str, str]] else: topic_arn = { 'Fn::Sub': ( 'arn:aws:sns:${AWS::Region}:${AWS::AccountId}:%s' % resource.topic ) } function_cfn['Properties']['Events'] = { sns_cfn_name: { 'Type': 'SNS', 'Properties': { 'Topic': topic_arn, } } }
def _add_websocket_domain_name(self, resource, template): # type: (models.WebsocketAPI, Dict[str, Any]) -> None if resource.domain_name is None: return domain_name = resource.domain_name cfn_name = to_cfn_resource_name(domain_name.resource_name) properties = { 'DomainName': domain_name.domain_name, 'DomainNameConfigurations': [ {'CertificateArn': domain_name.certificate_arn, 'EndpointType': 'REGIONAL'}, ] } if domain_name.tags: properties['Tags'] = domain_name.tags template['Resources'][cfn_name] = { 'Type': 'AWS::ApiGatewayV2::DomainName', 'Properties': properties, } template['Resources'][cfn_name + 'Mapping'] = { 'Type': 'AWS::ApiGatewayV2::ApiMapping', 'Properties': { 'DomainName': {'Ref': cfn_name}, 'ApiId': {'Ref': 'WebsocketAPI'}, 'ApiMappingKey': domain_name.api_mapping.mount_path, 'Stage': {'Ref': 'WebsocketAPIStage'}, } }
def _generate_snslambdasubscription(self, resource, template): # type: (models.SNSLambdaSubscription, Dict[str, Any]) -> None function_cfn_name = to_cfn_resource_name( resource.lambda_function.resource_name) function_cfn = template['Resources'][function_cfn_name] sns_cfn_name = self._register_cfn_resource_name( resource.resource_name) if resource.topic.startswith('arn:aws:sns:'): topic_arn = resource.topic # type: Union[str, Dict[str, str]] else: topic_arn = { 'Fn::Sub': ( 'arn:aws:sns:${AWS::Region}:${AWS::AccountId}:%s' % resource.topic ) } function_cfn['Properties']['Events'] = { sns_cfn_name: { 'Type': 'SNS', 'Properties': { 'Topic': topic_arn, } } }
def _generate_restapi(self, resource, template): # type: (models.RestAPI, Dict[str, Any]) -> None resources = template['Resources'] resources['RestAPI'] = { 'Type': 'AWS::Serverless::Api', 'Properties': { 'StageName': resource.api_gateway_stage, 'DefinitionBody': resource.swagger_doc, } } handler_cfn_name = to_cfn_resource_name( resource.lambda_function.resource_name) api_handler = template['Resources'].pop(handler_cfn_name) template['Resources']['APIHandler'] = api_handler resources['APIHandlerInvokePermission'] = { 'Type': 'AWS::Lambda::Permission', 'Properties': { 'FunctionName': {'Ref': 'APIHandler'}, 'Action': 'lambda:InvokeFunction', 'Principal': 'apigateway.amazonaws.com', 'SourceArn': { 'Fn::Sub': [ ('arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}' ':${RestAPIId}/*'), {'RestAPIId': {'Ref': 'RestAPI'}}, ] }, } } for auth in resource.authorizers: auth_cfn_name = to_cfn_resource_name(auth.resource_name) resources[auth_cfn_name + 'InvokePermission'] = { 'Type': 'AWS::Lambda::Permission', 'Properties': { 'FunctionName': {'Fn::GetAtt': [auth_cfn_name, 'Arn']}, 'Action': 'lambda:InvokeFunction', 'Principal': 'apigateway.amazonaws.com', 'SourceArn': { 'Fn::Sub': [ ('arn:aws:execute-api:${AWS::Region}:' '${AWS::AccountId}:${RestAPIId}/*'), {'RestAPIId': {'Ref': 'RestAPI'}}, ] }, } } self._inject_restapi_outputs(template)
def _auth_uri(self, authorizer): # type: (ChaliceAuthorizer) -> Any return { 'Fn::Sub': ('arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31' '/functions/${%s.Arn}/invocations' % to_cfn_resource_name(authorizer.name)) }
def test_to_cfn_resource_name_properties(name): try: result = utils.to_cfn_resource_name(name) except ValueError: # This is acceptable, the function raises ValueError # on bad input. pass else: assert re.search('[^A-Za-z0-9]', result) is None
def _auth_uri(self, authorizer): # type: (ChaliceAuthorizer) -> Any return { 'Fn::Sub': ( 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31' '/functions/${%s.Arn}/invocations' % to_cfn_resource_name( authorizer.name) ) }
def test_to_cfn_resource_name_properties(name): try: result = utils.to_cfn_resource_name(name) except ValueError: # This is acceptable, the function raises ValueError # on bad input. pass else: assert re.search('[^A-Za-z0-9]', result) is None
def _register_cfn_resource_name(self, name): # type: (str) -> str cfn_name = to_cfn_resource_name(name) if cfn_name in self._seen_names: raise DuplicateResourceNameError( 'A duplicate resource name was generated for ' 'the SAM template: %s' % cfn_name, ) self._seen_names.add(cfn_name) return cfn_name
def _register_cfn_resource_name(self, name): # type: (str) -> str cfn_name = to_cfn_resource_name(name) if cfn_name in self._seen_names: raise DuplicateResourceNameError( 'A duplicate resource name was generated for ' 'the SAM template: %s' % cfn_name, ) self._seen_names.add(cfn_name) return cfn_name
def _add_auth_handlers(self, resources, config, code_uri): # type: (Dict[str, Any], Config, str) -> None for auth_config in config.chalice_app.builtin_auth_handlers: auth_resource_name = to_cfn_resource_name(auth_config.name) new_config = config.scope(chalice_stage=config.chalice_stage, function_name=auth_config.name) resources[auth_resource_name] = self._generate_serverless_function( new_config, code_uri, auth_config.handler_string, 'authorizer') resources[auth_resource_name + 'InvokePermission'] = \ self._generate_lambda_permission(auth_resource_name)
def _generate_deployment_alarms(self, resource, template): # type: (models.LambdaFunction, Dict[str, Any]) -> List[str] deploy_preference = resource.deployment_preference alarm_resources = [] if isinstance(deploy_preference, models.SimpleDeploymentPreference): resources = template['Resources'] function_name = to_cfn_resource_name(resource.resource_name) for alarm in deploy_preference.alarms: target = 'LatestVersion' if alarm.with_version else 'Alias' cfn_resource_name = to_cfn_resource_name( function_name + target + 'ErrorOccurredAlarm') dimensions = [{ 'Name': 'Resource', 'Value': { 'Fn::Sub': "${APIHandler}:" + deploy_preference.auto_publish_alias } }, { 'Name': 'FunctionName', 'Value': {'Ref': 'APIHandler'} }] if alarm.with_version: dimensions.append({ 'Name': 'ExecutedVersion', 'Value': {'Fn::GetAtt': ['APIHandler.Version', 'Version']} }) resources[cfn_resource_name] = { 'Type': 'AWS::CloudWatch::Alarm', 'Properties': { 'AlarmDescription': 'Lambda Function Error > 0', 'ComparisonOperator': 'GreaterThanThreshold', 'Dimensions': dimensions, 'EvaluationPeriods': 2, 'MetricName': 'Errors', 'Namespace': 'AWS/Lambda', 'Period': 60, 'Statistic': 'Sum', 'Threshold': 0 } } alarm_resources.append(cfn_resource_name) return alarm_resources
def _add_iam_role(self, resource, cfn_resource): # type: (models.LambdaFunction, Dict[str, Any]) -> None role = resource.role if isinstance(role, models.ManagedIAMRole): cfn_resource['Properties']['Role'] = { 'Fn::GetAtt': [to_cfn_resource_name(role.resource_name), 'Arn'], } else: # resource is a PreCreatedIAMRole cfn_resource['Properties']['Role'] = role.role_arn
def _add_iam_role(self, resource, cfn_resource): # type: (models.LambdaFunction, Dict[str, Any]) -> None role = resource.role if isinstance(role, models.ManagedIAMRole): cfn_resource['Properties']['Role'] = { 'Fn::GetAtt': [ to_cfn_resource_name(role.resource_name), 'Arn' ], } else: # resource is a PreCreatedIAMRole cfn_resource['Properties']['Role'] = role.role_arn
def _generate_scheduledevent(self, resource, template): # type: (models.ScheduledEvent, Dict[str, Any]) -> None function_cfn_name = to_cfn_resource_name( resource.lambda_function.resource_name) function_cfn = template['Resources'][function_cfn_name] event_cfn_name = self._register_cfn_resource_name( resource.resource_name) function_cfn['Properties']['Events'] = { event_cfn_name: { 'Type': 'Schedule', 'Properties': { 'Schedule': resource.schedule_expression, } } }
def _generate_scheduledevent(self, resource, template): # type: (models.ScheduledEvent, Dict[str, Any]) -> None function_cfn_name = to_cfn_resource_name( resource.lambda_function.resource_name) function_cfn = template['Resources'][function_cfn_name] event_cfn_name = self._register_cfn_resource_name( resource.resource_name) function_cfn['Properties']['Events'] = { event_cfn_name: { 'Type': 'Schedule', 'Properties': { 'Schedule': resource.schedule_expression, } } }
def _generate_cloudwatchevent(self, resource, template): # type: (models.CloudWatchEvent, Dict[str, Any]) -> None function_cfn_name = to_cfn_resource_name( resource.lambda_function.resource_name) function_cfn = template['Resources'][function_cfn_name] event_cfn_name = self._register_cfn_resource_name( resource.resource_name) function_cfn['Properties']['Events'] = { event_cfn_name: { 'Type': 'CloudWatchEvent', 'Properties': { # For api calls we need serialized string form, for # SAM Templates we need datastructures. 'Pattern': json.loads(resource.event_pattern) } } }
def _generate_api_function_events(self, app): # type: (Chalice) -> Dict[str, Any] events = {} for methods in app.routes.values(): for http_method, view in methods.items(): key_name = to_cfn_resource_name(view.view_name + http_method.lower()) events[key_name] = { 'Type': 'Api', 'Properties': { 'Path': view.uri_pattern, 'RestApiId': { 'Ref': 'RestAPI' }, 'Method': http_method.lower(), } } return events
def _generate_sqseventsource(self, resource, template): # type: (models.SQSEventSource, Dict[str, Any]) -> None function_cfn_name = to_cfn_resource_name( resource.lambda_function.resource_name) function_cfn = template['Resources'][function_cfn_name] sns_cfn_name = self._register_cfn_resource_name(resource.resource_name) function_cfn['Properties']['Events'] = { sns_cfn_name: { 'Type': 'SQS', 'Properties': { 'Queue': { 'Fn::Sub': ('arn:aws:sqs:${AWS::Region}:${AWS::AccountId}:%s' % resource.queue) }, 'BatchSize': resource.batch_size, } } }
def _generate_sqseventsource(self, resource, template): # type: (models.SQSEventSource, Dict[str, Any]) -> None function_cfn_name = to_cfn_resource_name( resource.lambda_function.resource_name) function_cfn = template['Resources'][function_cfn_name] sns_cfn_name = self._register_cfn_resource_name( resource.resource_name) function_cfn['Properties']['Events'] = { sns_cfn_name: { 'Type': 'SQS', 'Properties': { 'Queue': { 'Fn::Sub': ( 'arn:aws:sqs:${AWS::Region}:${AWS::AccountId}:%s' % resource.queue ) }, 'BatchSize': resource.batch_size, } } }
def _add_domain_name(self, resource, template): # type: (models.RestAPI, Dict[str, Any]) -> None if resource.domain_name is None: return domain_name = resource.domain_name endpoint_type = resource.endpoint_type cfn_name = to_cfn_resource_name(domain_name.resource_name) properties = { 'DomainName': domain_name.domain_name, 'EndpointConfiguration': { 'Types': [endpoint_type], } } # type: Dict[str, Any] if endpoint_type == 'EDGE': properties['CertificateArn'] = domain_name.certificate_arn else: properties['RegionalCertificateArn'] = domain_name.certificate_arn if domain_name.tls_version is not None: properties['SecurityPolicy'] = domain_name.tls_version.value if domain_name.tags: properties['Tags'] = [ {'Key': key, 'Value': value} for key, value in sorted(domain_name.tags.items()) ] template['Resources'][cfn_name] = { 'Type': 'AWS::ApiGateway::DomainName', 'Properties': properties } template['Resources'][cfn_name + 'Mapping'] = { 'Type': 'AWS::ApiGateway::BasePathMapping', 'Properties': { 'DomainName': {'Ref': 'ApiGatewayCustomDomain'}, 'RestApiId': {'Ref': 'RestAPI'}, 'BasePath': domain_name.api_mapping.mount_path, 'Stage': {'Ref': 'RestAPI.Stage'}, } }
def test_to_cfn_resource_name(name, cfn_name): assert utils.to_cfn_resource_name(name) == cfn_name
def test_to_cfn_resource_name(name, cfn_name): assert utils.to_cfn_resource_name(name) == cfn_name