def register_pre_resources_template(self, template): """Register one UploadToS3 action into the pre_resources template, as well as several Outputs so subsequente templates can reference these files. Before registering these actions, we create the .zip file we'll upload to s3 on apply time. """ code_path = os.path.join(self.project.build_path, 'code') if not os.path.exists(code_path): os.makedirs(code_path) # We need to know to which bucket we are uploading these files. template.add_parameter( actions.Parameter( name="CodeBucket" ) ) filename = os.path.join(code_path, self.get_bucket_key()) with open(filename, 'wb') as f: f.write(self.get_zip_file().read()) context, context_key = {}, self.get_context_key() try: lambda_context = self.project.get_resource('contexts::{}'.format(context_key)) except exceptions.ResourceNotFoundError: if context_key != 'default': raise else: context = lambda_context.settings template.add( actions.InjectContextAndUploadToS3( name="{}-upload".format(self.name), bucket=actions.Ref(name='CodeBucket'), key=self.get_bucket_key(), filename=os.path.relpath(filename, self.project.build_path), context_to_inject=context, context_destinaton=self.get_context_destination() ) ) template.add_output( actions.Output( name=utils.valid_cloudformation_name(self.name, "s3url"), value=actions.GetAttr( action="{}-upload".format(self.name), attr="s3url", ) ) ) template.add_output( actions.Output( name=utils.valid_cloudformation_name(self.name, "s3version"), value=actions.GetAttr( action="{}-upload".format(self.name), attr="s3version", ) ) )
def register_resources_template(self, template): targets, target_lambdas = [], [] for name, target in six.iteritems(self.settings.get('targets', {})): target_lambdas.append(target['lambda']) targets.append( events.Target( Arn=self.get_destination_arn(target['lambda']), Id=self.get_function_name(target['lambda']), Input=target.get('input', ''), InputPath=target.get('input_path', ''), )) rule = events.Rule(utils.valid_cloudformation_name(self.name, "Rule"), Description=self.settings.get('description', ''), EventPattern=self.settings.get( 'event_pattern', troposphere.Ref(troposphere.AWS_NO_VALUE)), ScheduleExpression=self.settings.get( 'schedule_expression', troposphere.Ref(troposphere.AWS_NO_VALUE)), State=self.get_enabled(), Targets=targets) template.add_resource(rule) for lambda_ in target_lambdas: template.add_resource( troposphere.awslambda.Permission( utils.valid_cloudformation_name(self.name, 'rule', 'permission'), Action="lambda:InvokeFunction", FunctionName=self.get_destination_arn(lambda_), Principal="events.amazonaws.com", SourceArn=troposphere.GetAtt(rule, 'Arn'), ))
def _get_policies(self): """Returns a list of policies to attach to the IAM Role of this Lambda. Users can add more policies to this Role by defining policy documents in the settings of the lambda under the ``policies`` key.""" policies = [] if self._get_true_false('auto-run-policy', 't'): policies.append( iam.Policy(PolicyName=utils.valid_cloudformation_name( self.name, 'logs', 'policy'), PolicyDocument={ "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": ["lambda:InvokeFunction"], "Resource": ["*"] }, { "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "arn:aws:logs:*:*:*", }] })) if self.settings.get('vpc') and self._get_true_false( 'auto-vpc-policy', 't'): policies.append( iam.Policy(PolicyName=utils.valid_cloudformation_name( self.name, 'vpc'), PolicyDocument={ "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": [ "ec2:CreateNetworkInterface", "ec2:DescribeNetworkInterfaces", "ec2:DeleteNetworkInterface" ], "Resource": ["*"] }] })) for policy_nme, policy_document in six.iteritems( self.settings.get('policies', {})): policies.append( iam.Policy(PolicyName=utils.valid_cloudformation_name( self.name, policy_nme, 'policy'), PolicyDocument=policy_document)) return policies
def test_0001_project(self): self._test_project_step('0001_project') self.assert_stack_succeed('p') self.assert_stack_succeed('r') lambda_ = self.get_lambda(utils.valid_cloudformation_name('cron:example')) rule_ = self.get_rule(utils.valid_cloudformation_name('every_night_rule')) targets = self.get_rule_targets(rule_['Name']) self.assertEqual(rule_['ScheduleExpression'], 'cron(0 20 * * ? *)') self.assertEqual(len(targets), 1) self.assertEqual(targets[0]['Arn'], '{}:current'.format(lambda_['FunctionArn']))
def test_0001_project(self): self._test_project_step('0001_project') self.assert_stack_succeed('p') self.assert_stack_succeed('r') lambda_ = self.get_lambda( utils.valid_cloudformation_name('cron:example')) rule_ = self.get_rule( utils.valid_cloudformation_name('every_night_rule')) targets = self.get_rule_targets(rule_['Name']) self.assertEqual(rule_['ScheduleExpression'], 'cron(0 20 * * ? *)') self.assertEqual(len(targets), 1) self.assertEqual(targets[0]['Arn'], '{}:current'.format(lambda_['FunctionArn']))
def register_resources_template(self, template): """Register one ``EventSourceMapping`` into the resources template. Note: We preprend a 30s Sleep before the creation of this resource because the IAM role of the lambda is not propagated fast enough uppon creation, and CloudFormation checks if the referenced lambda has permission to consume this stream on creation time. Because the ``Lambda`` and the ``EventSourceMapping`` are created in the same stack we need to introduce this as palliative measure, sorry! """ sleep_lambda = 'lambda:contrib_helpers:sleep:current' sleep = Sleep.create_with( utils.valid_cloudformation_name(self.name, "Sleep"), DependsOn=[self.project.reference(sleep_lambda)], lambda_arn=troposphere.Ref(self.project.reference(sleep_lambda)), Time=30 ) template.add_resource(sleep) template.add_resource( awslambda.EventSourceMapping( self.in_project_cf_name, DependsOn=[sleep.name, self.get_function_name()], BatchSize=self.get_batch_size(), Enabled=self.get_enabled(), EventSourceArn=self.settings.get('stream'), FunctionName=troposphere.Ref(self.get_function_name()), StartingPosition=self.get_starting_position() ) )
def get_or_create_resource(self, path, api, template): """Returns the ID of the Resource ``path`` in ``api``. If the resorce doesn't exits, create a new one and add it to ``template``.""" # Add leading slash if path and path[0] != '/': path = '/{}'.format(path) # Remove trailing slash if path and path[-1] == '/': path = path[:-1] # Make / the root path if not path: path = '/' # Return API root resource if if path == '/': return troposphere.GetAtt(api, 'RootResourceId') if path in self._resources: return self._resources[path] parent_path, path_part = path.rsplit('/', 1) parent_id = self.get_or_create_resource(parent_path, api, template) resource = Resource(utils.valid_cloudformation_name( self.name, 'Resource', *path.split('/')), ParentId=parent_id, PathPart=path_part, RestApiId=troposphere.Ref(api)) template.add_resource(resource) self._resources[path] = troposphere.Ref(resource) return self._resources[path]
def register_resources_template(self, template): extra = defaultdict(list) for notification_id, notification in six.iteritems( self._notifications): notification.register_destination_publish_permission(template) extra[notification.api_property].append( NotificationConfiguration( Id=troposphere.Join('-', ['gordon', notification.id]), DestinationArn=notification.get_destination_arn(), Events=[e for e, _, _ in notification.events], KeyFilters=[ KeyFilter(Name=name, Value=value) for name, value in notification.filters ])) bucket_notification_configuration_lambda = 'lambda:contrib_s3:bucket_notification_configuration:current' template.add_resource( S3BucketNotificationConfiguration.create_with( utils.valid_cloudformation_name(self.name), DependsOn=[ self.project.reference( bucket_notification_configuration_lambda) ], lambda_arn=troposphere.Ref( self.project.reference( bucket_notification_configuration_lambda)), Bucket=self.get_bucket_name(), **dict([[k, v] for k, v in six.iteritems(extra) if v])))
def register_resources_template(self, template): extra = defaultdict(list) for notification_id, notification in six.iteritems(self._notifications): notification.register_destination_publish_permission(template) extra[notification.api_property].append( NotificationConfiguration( Id=troposphere.Join('-', ['gordon', notification.id]), DestinationArn=notification.get_destination_arn(), Events=[e for e, _, _ in notification.events], KeyFilters=[KeyFilter(Name=name, Value=value) for name, value in notification.filters] ) ) bucket_notification_configuration_lambda = 'lambda:contrib_s3:bucket_notification_configuration:current' template.add_resource( S3BucketNotificationConfiguration.create_with( utils.valid_cloudformation_name(self.name), DependsOn=[self.project.reference(bucket_notification_configuration_lambda)], lambda_arn=troposphere.Ref(self.project.reference(bucket_notification_configuration_lambda)), Bucket=self.get_bucket_name(), **dict([[k, v] for k, v in six.iteritems(extra) if v]) ) )
def register_destination_publish_permission(self, template): template.add_resource( sns.TopicPolicy( utils.valid_cloudformation_name( self.bucket_notification_configuration.name, self.id, 'permission'), Topics=[self.get_destination_arn()], PolicyDocument={ "Version": "2008-10-17", "Id": "PublicationPolicy", "Statement": [{ "Effect": "Allow", "Principal": { "AWS": "*" }, "Action": ["sns:Publish"], "Resource": self.get_destination_arn(), "Condition": { "ArnEquals": { "aws:SourceArn": self.bucket_notification_configuration. get_bucket_arn() } } }] }))
def get_role(self): """Returns the role this lambda function will use. Users can customize which role to apply by referencing the ARN of the role. If no Role is defined, gordon will create and assing one with the basic permissions suggested by AWS. Users can customize the policies attached to the role using ``policies`` in the lambda settings.""" role = self.settings.get('role') if isinstance(role, six.string_types) or isinstance(role, troposphere.Ref): return role elif role is None: pass else: raise exceptions.InvalidLambdaRoleError(self.name, role) return iam.Role( utils.valid_cloudformation_name(self.name, 'role'), AssumeRolePolicyDocument={ "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": { "Service": ["lambda.amazonaws.com"] }, "Action": ["sts:AssumeRole"] }] }, Policies=self._get_policies() )
def get_role(self): """Returns the role this lambda function will use. Users can customize which role to apply by referencing the ARN of the role. If no Role is defined, gordon will create and assing one with the basic permissions suggested by AWS. Users can customize the policies attached to the role using ``policies`` in the lambda settings.""" role = self.settings.get('role') if isinstance(role, six.string_types) or isinstance( role, troposphere.Ref): return role elif role is None: pass else: raise exceptions.InvalidLambdaRoleError(self.name, role) return iam.Role(utils.valid_cloudformation_name(self.name, 'role'), AssumeRolePolicyDocument={ "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": { "Service": ["lambda.amazonaws.com"] }, "Action": ["sts:AssumeRole"] }] }, Policies=self._get_policies())
def register_resources_template(self, template): """Register one ``EventSourceMapping`` into the resources template. Note: We preprend a 30s Sleep before the creation of this resource because the IAM role of the lambda is not propagated fast enough uppon creation, and CloudFormation checks if the referenced lambda has permission to consume this stream on creation time. Because the ``Lambda`` and the ``EventSourceMapping`` are created in the same stack we need to introduce this as palliative measure, sorry! """ sleep_lambda = 'lambda:contrib_helpers:sleep:current' sleep = Sleep.create_with( utils.valid_cloudformation_name(self.name, "Sleep"), DependsOn=[self.project.reference(sleep_lambda)], lambda_arn=troposphere.Ref(self.project.reference(sleep_lambda)), Time=30) template.add_resource(sleep) template.add_resource( awslambda.EventSourceMapping( self.in_project_cf_name, DependsOn=[sleep.name, self.get_function_name()], BatchSize=self.get_batch_size(), Enabled=self.get_enabled(), EventSourceArn=self.settings.get('stream'), FunctionName=troposphere.Ref(self.get_function_name()), StartingPosition=self.get_starting_position()))
def register_destination_publish_permission(self, template): template.add_resource( sns.TopicPolicy( utils.valid_cloudformation_name( self.bucket_notification_configuration.name, self.id, 'permission' ), Topics=[self.get_destination_arn()], PolicyDocument={ "Version": "2008-10-17", "Id": "PublicationPolicy", "Statement": [{ "Effect": "Allow", "Principal": { "AWS": "*" }, "Action": ["sns:Publish"], "Resource": self.get_destination_arn(), "Condition": { "ArnEquals": {"aws:SourceArn": self.bucket_notification_configuration.get_bucket_arn()} } }] } ) )
def __init__(self, *args, **kwargs): super(Lambda, self).__init__(*args, **kwargs) self.current_alias_project_name = '{}:current'.format( self.in_project_name) self.current_alias_cf_name = utils.valid_cloudformation_name( self.name, "CurrentAlias") self.project.register_resource_reference( self.current_alias_project_name, self.current_alias_cf_name, self)
def __init__(self, *args, **kwargs): super(Lambda, self).__init__(*args, **kwargs) self.current_alias_project_name = '{}:current'.format(self.in_project_name) self.current_alias_cf_name = utils.valid_cloudformation_name(self.name, "CurrentAlias") self.project.register_resource_reference( self.current_alias_project_name, self.current_alias_cf_name, self )
def test_0001_project(self): self._test_project_step('0001_project') self.assert_stack_succeed('p') self.assert_stack_succeed('r') lambda_ = self.get_lambda(valid_cloudformation_name('kinesisconsumer:consumer')) self.assertEqual(lambda_['Runtime'], 'python2.7') aliases = self.get_lambda_aliases(function_name=lambda_['FunctionName']) self.assertEqual(aliases.keys(), ['current'])
def register_destination_publish_permission(self, template): template.add_resource( awslambda.Permission( utils.valid_cloudformation_name( self.bucket_notification_configuration.name, self.id, 'permission'), Action="lambda:InvokeFunction", FunctionName=self.get_destination_arn(), Principal="s3.amazonaws.com", SourceAccount=troposphere.Ref(troposphere.AWS_ACCOUNT_ID), ))
def register_resources_template(self, template): targets, target_lambdas = [], [] for name, target in six.iteritems(self.settings.get('targets', {})): target_lambdas.append(target['lambda']) targets.append( events.Target( Arn=self.get_destination_arn(target['lambda']), Id=self.get_function_name(target['lambda']), Input=target.get('input', ''), InputPath=target.get('input_path', ''), ) ) rule = events.Rule( utils.valid_cloudformation_name(self.name, "Rule"), Description=self.settings.get('description', ''), EventPattern=self.settings.get('event_pattern', troposphere.Ref(troposphere.AWS_NO_VALUE)), ScheduleExpression=self.settings.get('schedule_expression', troposphere.Ref(troposphere.AWS_NO_VALUE)), State=self.get_enabled(), Targets=targets ) template.add_resource(rule) for lambda_ in target_lambdas: template.add_resource( troposphere.awslambda.Permission( utils.valid_cloudformation_name( self.name, 'rule', 'permission' ), Action="lambda:InvokeFunction", FunctionName=self.get_destination_arn(lambda_), Principal="events.amazonaws.com", SourceArn=troposphere.GetAtt( rule, 'Arn' ), ) )
def test_0001_project(self): self._test_project_step('0001_project') self.assert_stack_succeed('p') self.assert_stack_succeed('r') lambda_ = self.get_lambda( valid_cloudformation_name('kinesisconsumer:consumer')) self.assertEqual(lambda_['Runtime'], 'python2.7') aliases = self.get_lambda_aliases( function_name=lambda_['FunctionName']) self.assertEqual(list(aliases.keys()), ['current'])
def register_destination_publish_permission(self, template): template.add_resource( awslambda.Permission( utils.valid_cloudformation_name( self.bucket_notification_configuration.name, self.id, 'permission' ), Action="lambda:InvokeFunction", FunctionName=self.get_destination_arn(), Principal="s3.amazonaws.com", SourceAccount=troposphere.Ref(troposphere.AWS_ACCOUNT_ID), ) )
def __init__(self, name, settings, project=None, app=None): self.name = name self.app = app self.project = project or self.app.project self.settings = settings for key in self.required_settings: if key not in self.settings: raise exceptions.ResourceSettingRequiredError(self.name, key) self.in_project_name = self._get_in_project_name() self.in_project_cf_name = utils.valid_cloudformation_name( self.in_project_name.split(':', 1)[1]) self.project.register_resource_reference(self.in_project_name, self.in_project_cf_name, self)
def test_0001_project(self): self._test_project_step('0001_project') self.assert_stack_succeed('p') self.assert_stack_succeed('r') lambda_ = self.get_lambda( utils.valid_cloudformation_name('javaexample:javaexample')) self.assertEqual(lambda_['Runtime'], 'java8') self.assertEqual(lambda_['Description'], 'My description') self.assertEqual(lambda_['MemorySize'], 192) self.assertEqual(lambda_['Timeout'], 123) aliases = self.get_lambda_aliases( function_name=lambda_['FunctionName']) self.assertEqual(list(aliases.keys()), ['current']) response = self.invoke_lambda(function_name=lambda_['FunctionName'], payload={'key1': 'hello'}) self.assert_lambda_response(response, 'hello')
def test_0001_project(self): self._test_project_step('0001_project') self.assert_stack_succeed('p') self.assert_stack_succeed('r') lambda_ = self.get_lambda(utils.valid_cloudformation_name('pyexample:pyexample')) self.assertEqual(lambda_['Runtime'], 'python2.7') self.assertEqual(lambda_['Description'], 'My description') self.assertEqual(lambda_['MemorySize'], 192) self.assertEqual(lambda_['Timeout'], 123) aliases = self.get_lambda_aliases(function_name=lambda_['FunctionName']) self.assertEqual(aliases.keys(), ['current']) response = self.invoke_lambda( function_name=lambda_['FunctionName'], payload={'key1': 'hello'} ) self.assert_lambda_response(response, 'hello')
def __init__(self, name, settings, project=None, app=None): self.name = name self.app = app self.project = project or self.app.project self.settings = settings for key in self.required_settings: if key not in self.settings: raise exceptions.ResourceSettingRequiredError(self.name, key) self.in_project_name = self._get_in_project_name() self.in_project_cf_name = utils.valid_cloudformation_name( self.in_project_name.split(':', 1)[1] ) self.project.register_resource_reference( self.in_project_name, self.in_project_cf_name, self )
def test_0002_project(self): self._test_project_step('0002_project') self.assert_stack_succeed('p') self.assert_stack_succeed('r') lambda_ = self.get_lambda(utils.valid_cloudformation_name('pyexample:pyexample')) self.assertEqual(lambda_['Runtime'], 'python2.7') self.assertEqual(lambda_['Description'], 'My second description') self.assertEqual(lambda_['MemorySize'], 256) self.assertEqual(lambda_['Timeout'], 199) aliases = self.get_lambda_aliases(function_name=lambda_['FunctionName']) self.assertEqual(list(aliases.keys()), ['current']) response = self.invoke_lambda( function_name=lambda_['FunctionName'], payload={'key1': 'hello', 'key2': 'bye'} ) self.assert_lambda_response(response, 'bye')
def get_or_create_resource(self, path, api, template): """Returns the ID of the Resource ``path`` in ``api``. If the resorce doesn't exits, create a new one and add it to ``template``.""" # Add leading slash if path and path[0] != '/': path = '/{}'.format(path) # Remove trailing slash if path and path[-1] == '/': path = path[:-1] # Make / the root path if not path: path = '/' # Return API root resource if if path == '/': return troposphere.GetAtt(api, 'RootResourceId') if path in self._resources: return self._resources[path] parent_path, path_part = path.rsplit('/', 1) parent_id = self.get_or_create_resource(parent_path, api, template) resource = Resource( utils.valid_cloudformation_name(self.name, 'Resource', *path.split('/')), ParentId=parent_id, PathPart=path_part, RestApiId=troposphere.Ref(api) ) template.add_resource(resource) self._resources[path] = troposphere.Ref(resource) return self._resources[path]
def register_resources_template(self, template): """Register the lambda Function into the troposphere template. If this function requires a custom Role, register it too.""" role = self.get_role() depends_on = [] if isinstance(role, iam.Role): template.add_resource(role) depends_on.append(role.name) role = troposphere.GetAtt(role, 'Arn') template.add_parameter( troposphere.Parameter( utils.valid_cloudformation_name(self.name, "s3version"), Type="String", )) extra = {} if self.settings.get('vpc'): vpc = self.project.get_resource('vpc::{}'.format( self.settings.get('vpc'))) if isinstance(vpc.settings['security-groups'], troposphere.Ref): vpc.settings[ 'security-groups']._type = 'List<AWS::EC2::SecurityGroup::Id>' if isinstance(vpc.settings['subnet-ids'], troposphere.Ref): vpc.settings['subnet-ids']._type = 'List<AWS::EC2::Subnet::Id>' extra['VpcConfig'] = awslambda.VPCConfig( SecurityGroupIds=vpc.settings['security-groups'], SubnetIds=vpc.settings['subnet-ids']) function = template.add_resource( awslambda.Function(self.in_project_cf_name, DependsOn=depends_on, Code=awslambda.Code( S3Bucket=troposphere.Ref("CodeBucket"), S3Key=self.get_bucket_key(), S3ObjectVersion=troposphere.Ref( utils.valid_cloudformation_name( self.name, "s3version")), ), Description=self.settings.get( 'description', ''), Handler=self.get_handler(), MemorySize=self.get_memory(), Role=role, Runtime=self.get_runtime(), Timeout=self.get_timeout(), **extra)) lambda_version = 'lambda:contrib_lambdas:version' lambda_ref = troposphere.GetAtt(self.project.reference(lambda_version), 'Arn') if not self.in_project_name.startswith('lambda:contrib_lambdas:'): lambda_version = '{}:current'.format(lambda_version) lambda_ref = troposphere.Ref( self.project.reference(lambda_version)) version = template.add_resource( LambdaVersion.create_with( utils.valid_cloudformation_name(self.name, "Version"), DependsOn=[ self.project.reference(lambda_version), function.name ], lambda_arn=lambda_ref, FunctionName=troposphere.Ref(function), S3ObjectVersion=troposphere.Ref( utils.valid_cloudformation_name(self.name, "s3version")), )) alias = template.add_resource( awslambda.Alias( self.current_alias_cf_name, DependsOn=[version.name], FunctionName=troposphere.Ref(function), FunctionVersion=troposphere.GetAtt(version, "Version"), Name="current", )) if self._get_true_false('cli-output', 't'): template.add_output([ troposphere.Output( utils.valid_cloudformation_name("Clioutput", self.in_project_name), Value=troposphere.Ref(alias), ) ])
def register_resources_template(self, template): """Register the lambda Function into the troposphere template. If this function requires a custom Role, register it too.""" role = self.get_role() depends_on = [] if isinstance(role, iam.Role): template.add_resource(role) depends_on.append(role.name) role = troposphere.GetAtt(role, 'Arn') template.add_parameter( troposphere.Parameter( utils.valid_cloudformation_name(self.name, "s3version"), Type="String", ) ) extra = {} if self.settings.get('vpc'): vpc = self.project.get_resource('vpc::{}'.format(self.settings.get('vpc'))) extra['VpcConfig'] = awslambda.VPCConfig( SecurityGroupIds=vpc.settings['security-groups'], SubnetIds=vpc.settings['subnet-ids'] ) function = template.add_resource( awslambda.Function( self.in_project_cf_name, DependsOn=depends_on, Code=awslambda.Code( S3Bucket=troposphere.Ref("CodeBucket"), S3Key=self.get_bucket_key(), S3ObjectVersion=troposphere.Ref( utils.valid_cloudformation_name(self.name, "s3version") ), ), Description=self.settings.get('description', ''), Handler=self.get_handler(), MemorySize=self.get_memory(), Role=role, Runtime=self.get_runtime(), Timeout=self.get_timeout(), **extra ) ) lambda_version = 'lambda:contrib_lambdas:version' lambda_ref = troposphere.GetAtt(self.project.reference(lambda_version), 'Arn') if not self.in_project_name.startswith('lambda:contrib_lambdas:'): lambda_version = '{}:current'.format(lambda_version) lambda_ref = troposphere.Ref(self.project.reference(lambda_version)) version = template.add_resource( LambdaVersion.create_with( utils.valid_cloudformation_name(self.name, "Version"), DependsOn=[ self.project.reference(lambda_version), function.name ], lambda_arn=lambda_ref, FunctionName=troposphere.Ref( function ), S3ObjectVersion=troposphere.Ref( utils.valid_cloudformation_name(self.name, "s3version") ), ) ) alias = template.add_resource( awslambda.Alias( self.current_alias_cf_name, DependsOn=[ version.name ], FunctionName=troposphere.Ref( function ), FunctionVersion=troposphere.GetAtt( version, "Version" ), Name="current", ) ) if self._get_true_false('cli-output', 't'): template.add_output([ troposphere.Output( utils.valid_cloudformation_name("Clioutput", self.in_project_name), Value=troposphere.Ref(alias), ) ])
def test_0001_project(self): self._test_project_step('0001_project') self.assert_stack_succeed('p') self.assert_stack_succeed('r') lambda_ = self.get_lambda(utils.valid_cloudformation_name('pyexample:hellopy')) self.assertEqual(lambda_['Runtime'], 'python2.7') self.assertEqual(lambda_['Description'], 'My hello description') self.assertEqual(lambda_['MemorySize'], 192) self.assertEqual(lambda_['Timeout'], 123) aliases = self.get_lambda_aliases(function_name=lambda_['FunctionName']) self.assertEqual(list(aliases.keys()), ['current']) response = self.invoke_lambda( function_name=lambda_['FunctionName'], payload={} ) self.assert_lambda_response(response, 'hello') lambda_ = self.get_lambda(utils.valid_cloudformation_name('pyexample:byepy')) self.assertEqual(lambda_['Runtime'], 'python2.7') self.assertEqual(lambda_['Description'], 'My bye description') self.assertEqual(lambda_['MemorySize'], 192) self.assertEqual(lambda_['Timeout'], 123) aliases = self.get_lambda_aliases(function_name=lambda_['FunctionName']) self.assertEqual(list(aliases.keys()), ['current']) response = self.invoke_lambda( function_name=lambda_['FunctionName'], payload={} ) self.assert_lambda_response(response, 'bye') client = boto3.client('apigateway') api = [a for a in client.get_rest_apis()['items'] if a['name'] == 'helloapi-{}'.format(self.uid)][0] endpoint = 'https://{}.execute-api.{}.amazonaws.com/{}'.format(api['id'], os.environ['AWS_DEFAULT_REGION'], self.uid) response = requests.get(endpoint) self.assertEqual(response.status_code, 200) self.assertEqual(response.content.decode('utf-8'), '"hello"') response = requests.get('{}/404'.format(endpoint)) self.assertEqual(response.status_code, 404) self.assertEqual(response.content.decode('utf-8'), '"hello"') response = requests.get('{}/shop/2'.format(endpoint)) self.assertEqual(response.status_code, 200) self.assertEqual(response.content.decode('utf-8'), '"hello"') response = requests.get('{}/http'.format(endpoint)) self.assertEqual(response.status_code, 200) self.assertEqual(json.loads(response.content.decode('utf-8'))['args'], {'hello': 'world'}) response = requests.get('{}/complex'.format(endpoint)) self.assertEqual(response.status_code, 200) self.assertEqual(response.content.decode('utf-8'), '"hello"') response = requests.post('{}/complex'.format(endpoint)) self.assertEqual(response.status_code, 200) self.assertEqual(response.content.decode('utf-8'), '"bye"')
def _get_policies(self): """Returns a list of policies to attach to the IAM Role of this Lambda. Users can add more policies to this Role by defining policy documents in the settings of the lambda under the ``policies`` key.""" policies = [] if self._get_true_false('auto-run-policy', 't'): policies.append( iam.Policy( PolicyName=utils.valid_cloudformation_name(self.name, 'logs', 'policy'), PolicyDocument={ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "lambda:InvokeFunction" ], "Resource": [ "*" ] }, { "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "arn:aws:logs:*:*:*", } ] } ) ) if self.settings.get('vpc') and self._get_true_false('auto-vpc-policy', 't'): policies.append( iam.Policy( PolicyName=utils.valid_cloudformation_name(self.name, 'vpc'), PolicyDocument={ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ec2:CreateNetworkInterface" ], "Resource": [ "*" ] } ] } ) ) for policy_nme, policy_document in six.iteritems(self.settings.get('policies', {})): policies.append( iam.Policy( PolicyName=utils.valid_cloudformation_name(self.name, policy_nme, 'policy'), PolicyDocument=policy_document ) ) return policies
def register_resources_template(self, template): deployment_resources = [] api = RestApi( self.in_project_cf_name, Name=troposphere.Join("-", [self.name, troposphere.Ref('Stage')]), Description=self.settings.get('description', '') ) template.add_resource(api) deployment_resources.append(api) invoke_lambda_role = troposphere.iam.Role( utils.valid_cloudformation_name(self.name, 'Role'), AssumeRolePolicyDocument={ "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": { "Service": ["apigateway.amazonaws.com"] }, "Action": ["sts:AssumeRole"] }] }, Policies=[ troposphere.iam.Policy( PolicyName=utils.valid_cloudformation_name(self.name, 'Role', 'Policy'), PolicyDocument={ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "lambda:InvokeFunction" ], "Resource": [ "*" ] } ] } ) ] ) template.add_resource(invoke_lambda_role) deployment_resources.append(invoke_lambda_role) deployment_dependencies = [] for path, resource in six.iteritems(self.settings.get('resources', {})): resource_reference = self.get_or_create_resource(path, api, template) methods = resource['methods'] if isinstance(methods, six.string_types): methods = [methods] if not isinstance(methods, dict): method_properties = copy.deepcopy(resource) method_properties.pop('methods', None) methods = dict([[method, method_properties] for method in methods]) for method, configuration in six.iteritems(methods): method_name = [self.name] method_name.extend(path.split('/')) method_name.append(method) extra = {} if 'parameters' in configuration: extra['RequestParameters'] = configuration['parameters'] m = Method( utils.valid_cloudformation_name(*method_name), HttpMethod=method, AuthorizationType=self.get_authorization_type(configuration), ApiKeyRequired=self.get_api_key_required(configuration), Integration=self.get_integration(configuration, invoke_lambda_role), MethodResponses=self.get_method_responses(configuration), ResourceId=resource_reference, RestApiId=troposphere.Ref(api), **extra ) template.add_resource(m) deployment_dependencies.append(m.name) deployment_resources.append(m) deploy_hash = hashlib.sha1(six.text_type(uuid.uuid4()).encode('utf-8')).hexdigest() deploy = Deployment( utils.valid_cloudformation_name(self.name, "Deployment", deploy_hash[:8]), DependsOn=sorted(deployment_dependencies), StageName=troposphere.Ref('Stage'), RestApiId=troposphere.Ref(api) ) template.add_resource(deploy) if self._get_true_false('cli-output', 't'): template.add_output([ troposphere.Output( utils.valid_cloudformation_name("Clioutput", self.in_project_name), Value=troposphere.Join( "", [ "https://", troposphere.Ref(api), ".execute-api.", troposphere.Ref(troposphere.AWS_REGION), ".amazonaws.com/", troposphere.Ref('Stage') ] ), ) ])