From 3a6fe702a7350afd62dbdc8506390ccec7695c4d Mon Sep 17 00:00:00 2001 From: Dmitry Meyerson Date: Fri, 31 Jan 2020 15:31:16 -0500 Subject: [PATCH 1/8] add restapi blueprint --- stacker_blueprints/rest_gateway.py | 198 +++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 stacker_blueprints/rest_gateway.py diff --git a/stacker_blueprints/rest_gateway.py b/stacker_blueprints/rest_gateway.py new file mode 100644 index 00000000..951084dd --- /dev/null +++ b/stacker_blueprints/rest_gateway.py @@ -0,0 +1,198 @@ +from troposphere import Ref, Template, Output +from troposphere.apigateway import RestApi, Method +from troposphere.apigateway import Resource, MethodResponse +from troposphere.apigateway import Integration, IntegrationResponse +from troposphere.apigateway import Deployment, Stage, ApiStage +from troposphere.apigateway import UsagePlan, QuotaSettings, ThrottleSettings +from troposphere.apigateway import ApiKey, StageKey, UsagePlanKey +from troposphere.iam import Role, Policy +from troposphere.awslambda import Function, Code +from troposphere import GetAtt, Join + +from stacker.blueprints.base import Blueprint +from stacker.blueprints.variables.types import TroposphereType + + +# adapted from https://github.com/cloudtools/troposphere/blob/master/examples/ApiGateway.py + +class RestGateway(Blueprint): + VARIABLES = {} + + def create_rest_gateway(self): + t = self.template + + # Create the Api Gateway + rest_api = t.add_resource(RestApi( + "ExampleApi", + Name="ExampleApi" + )) + + t.add_output(Output("RestGatewayId", Value=Ref(ExampleApi))) + + def create_template(self): + self.create_rest_gateway() +# def lambda_function(self): +# # Create a Lambda function that will be mapped +# code = [ +# "var response = require('cfn-response');", +# "exports.handler = function(event, context) {", +# " context.succeed('foobar!');", +# " return 'foobar!';", +# "};", +# ] + +# # Create a role for the lambda function +# t.add_resource(Role( +# "LambdaExecutionRole", +# Path="/", +# Policies=[Policy( +# PolicyName="root", +# PolicyDocument={ +# "Version": "2012-10-17", +# "Statement": [{ +# "Action": ["logs:*"], +# "Resource": "arn:aws:logs:*:*:*", +# "Effect": "Allow" +# }, { +# "Action": ["lambda:*"], +# "Resource": "*", +# "Effect": "Allow" +# }] +# })], +# AssumeRolePolicyDocument={"Version": "2012-10-17", "Statement": [ +# { +# "Action": ["sts:AssumeRole"], +# "Effect": "Allow", +# "Principal": { +# "Service": [ +# "lambda.amazonaws.com", +# "apigateway.amazonaws.com" +# ] +# } +# } +# ]}, +# )) + +# # Create the Lambda function +# foobar_function = t.add_resource(Function( +# "FoobarFunction", +# Code=Code( +# ZipFile=Join("", code) +# ), +# Handler="index.handler", +# Role=GetAtt("LambdaExecutionRole", "Arn"), +# Runtime="nodejs4.3", +# )) + +# # Create a resource to map the lambda function to +# resource = t.add_resource(Resource( +# "FoobarResource", +# RestApiId=Ref(rest_api), +# PathPart="foobar", +# ParentId=GetAtt("ExampleApi", "RootResourceId"), +# )) + +# # Create a Lambda API method for the Lambda resource +# method = t.add_resource(Method( +# "LambdaMethod", +# DependsOn='FoobarFunction', +# RestApiId=Ref(rest_api), +# AuthorizationType="NONE", +# ResourceId=Ref(resource), +# HttpMethod="GET", +# Integration=Integration( +# Credentials=GetAtt("LambdaExecutionRole", "Arn"), +# Type="AWS", +# IntegrationHttpMethod='POST', +# IntegrationResponses=[ +# IntegrationResponse( +# StatusCode='200' +# ) +# ], +# Uri=Join("", [ +# "arn:aws:apigateway:eu-west-1:lambda:path/2015-03-31/functions/", +# GetAtt("FoobarFunction", "Arn"), +# "/invocations" +# ]) +# ), +# MethodResponses=[ +# MethodResponse( +# "CatResponse", +# StatusCode='200' +# ) +# ] +# )) + +# # Create a deployment +# stage_name = 'v1' + +# deployment = t.add_resource(Deployment( +# "%sDeployment" % stage_name, +# DependsOn="LambdaMethod", +# RestApiId=Ref(rest_api), +# )) + +# stage = t.add_resource(Stage( +# '%sStage' % stage_name, +# StageName=stage_name, +# RestApiId=Ref(rest_api), +# DeploymentId=Ref(deployment) +# )) + +# key = t.add_resource(ApiKey( +# "ApiKey", +# StageKeys=[StageKey( +# RestApiId=Ref(rest_api), +# StageName=Ref(stage) +# )] +# )) + +# # Create an API usage plan +# usagePlan = t.add_resource(UsagePlan( +# "ExampleUsagePlan", +# UsagePlanName="ExampleUsagePlan", +# Description="Example usage plan", +# Quota=QuotaSettings( +# Limit=50000, +# Period="MONTH" +# ), +# Throttle=ThrottleSettings( +# BurstLimit=500, +# RateLimit=5000 +# ), +# ApiStages=[ +# ApiStage( +# ApiId=Ref(rest_api), +# Stage=Ref(stage) +# )] +# )) + +# # tie the usage plan and key together +# usagePlanKey = t.add_resource(UsagePlanKey( +# "ExampleUsagePlanKey", +# KeyId=Ref(key), +# KeyType="API_KEY", +# UsagePlanId=Ref(usagePlan) +# )) + +# # Add the deployment endpoint as an output +# t.add_output([ +# Output( +# "ApiEndpoint", +# Value=Join("", [ +# "https://", +# Ref(rest_api), +# ".execute-api.eu-west-1.amazonaws.com/", +# stage_name +# ]), +# Description="Endpoint for this stage of the api" +# ), +# Output( +# "ApiKey", +# Value=Ref(key), +# Description="API key" +# ), +# ]) + + +# print(t.to_json()) \ No newline at end of file From f63cf86b8e7044c1a57012bffa2dea4755d7c7f3 Mon Sep 17 00:00:00 2001 From: Dmitry Meyerson Date: Fri, 31 Jan 2020 15:41:50 -0500 Subject: [PATCH 2/8] add restapi blueprint --- stacker_blueprints/rest_gateway.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stacker_blueprints/rest_gateway.py b/stacker_blueprints/rest_gateway.py index 951084dd..78b15256 100644 --- a/stacker_blueprints/rest_gateway.py +++ b/stacker_blueprints/rest_gateway.py @@ -27,7 +27,7 @@ def create_rest_gateway(self): Name="ExampleApi" )) - t.add_output(Output("RestGatewayId", Value=Ref(ExampleApi))) + t.add_output(Output("RestGatewayId", Value=Ref('ExampleApi'))) def create_template(self): self.create_rest_gateway() From 70c8e19507ed1bb92bd45dd8ac5e63a9c2a9886c Mon Sep 17 00:00:00 2001 From: Dmitry Meyerson Date: Fri, 31 Jan 2020 15:58:24 -0500 Subject: [PATCH 3/8] add restapi blueprint --- stacker_blueprints/rest_gateway.py | 115 ++++++++++++++--------------- 1 file changed, 56 insertions(+), 59 deletions(-) diff --git a/stacker_blueprints/rest_gateway.py b/stacker_blueprints/rest_gateway.py index 78b15256..5590c03d 100644 --- a/stacker_blueprints/rest_gateway.py +++ b/stacker_blueprints/rest_gateway.py @@ -31,66 +31,63 @@ def create_rest_gateway(self): def create_template(self): self.create_rest_gateway() -# def lambda_function(self): -# # Create a Lambda function that will be mapped -# code = [ -# "var response = require('cfn-response');", -# "exports.handler = function(event, context) {", -# " context.succeed('foobar!');", -# " return 'foobar!';", -# "};", -# ] - -# # Create a role for the lambda function -# t.add_resource(Role( -# "LambdaExecutionRole", -# Path="/", -# Policies=[Policy( -# PolicyName="root", -# PolicyDocument={ -# "Version": "2012-10-17", -# "Statement": [{ -# "Action": ["logs:*"], -# "Resource": "arn:aws:logs:*:*:*", -# "Effect": "Allow" -# }, { -# "Action": ["lambda:*"], -# "Resource": "*", -# "Effect": "Allow" -# }] -# })], -# AssumeRolePolicyDocument={"Version": "2012-10-17", "Statement": [ -# { -# "Action": ["sts:AssumeRole"], -# "Effect": "Allow", -# "Principal": { -# "Service": [ -# "lambda.amazonaws.com", -# "apigateway.amazonaws.com" -# ] -# } -# } -# ]}, -# )) - -# # Create the Lambda function -# foobar_function = t.add_resource(Function( -# "FoobarFunction", -# Code=Code( -# ZipFile=Join("", code) -# ), -# Handler="index.handler", -# Role=GetAtt("LambdaExecutionRole", "Arn"), -# Runtime="nodejs4.3", -# )) + self.create_lambda_function() + + def create_lambda_function(self): + # Create a Lambda function that will be mapped + t = self.template + code = ["print('ima a lambda!')"] + + # Create a role for the lambda function + t.add_resource(Role( + "LambdaExecutionRole", + Path="/", + Policies=[Policy( + PolicyName="root", + PolicyDocument={ + "Version": "2012-10-17", + "Statement": [{ + "Action": ["logs:*"], + "Resource": "arn:aws:logs:*:*:*", + "Effect": "Allow" + }, { + "Action": ["lambda:*"], + "Resource": "*", + "Effect": "Allow" + }] + })], + AssumeRolePolicyDocument={"Version": "2012-10-17", "Statement": [ + { + "Action": ["sts:AssumeRole"], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com", + "apigateway.amazonaws.com" + ] + } + } + ]}, + )) -# # Create a resource to map the lambda function to -# resource = t.add_resource(Resource( -# "FoobarResource", -# RestApiId=Ref(rest_api), -# PathPart="foobar", -# ParentId=GetAtt("ExampleApi", "RootResourceId"), -# )) + # Create the Lambda function + foobar_function = t.add_resource(Function( + "FoobarFunction", + Code=Code( + ZipFile=Join("", code) + ), + Handler="index.handler", + Role=GetAtt("LambdaExecutionRole", "Arn"), + Runtime="python3.7", + )) + + # Create a resource to map the lambda function to + resource = t.add_resource(Resource( + "FoobarResource", + RestApiId=Ref(rest_api), + PathPart="foobar", + ParentId=GetAtt("ExampleApi", "RootResourceId"), + )) # # Create a Lambda API method for the Lambda resource # method = t.add_resource(Method( From 7af155bf8f8746f359b250c2fa19c0b7d269166b Mon Sep 17 00:00:00 2001 From: Dmitry Meyerson Date: Fri, 31 Jan 2020 16:01:27 -0500 Subject: [PATCH 4/8] add restapi blueprint --- stacker_blueprints/rest_gateway.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stacker_blueprints/rest_gateway.py b/stacker_blueprints/rest_gateway.py index 5590c03d..d1de2ef0 100644 --- a/stacker_blueprints/rest_gateway.py +++ b/stacker_blueprints/rest_gateway.py @@ -84,7 +84,7 @@ def create_lambda_function(self): # Create a resource to map the lambda function to resource = t.add_resource(Resource( "FoobarResource", - RestApiId=Ref(rest_api), + RestApiId=Ref('ExampleApi'), PathPart="foobar", ParentId=GetAtt("ExampleApi", "RootResourceId"), )) From e4eda1f53108f1b471fd18b44191f37c18e331ca Mon Sep 17 00:00:00 2001 From: Dmitry Meyerson Date: Mon, 3 Feb 2020 12:27:03 -0500 Subject: [PATCH 5/8] use vars --- stacker_blueprints/rest_gateway.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stacker_blueprints/rest_gateway.py b/stacker_blueprints/rest_gateway.py index d1de2ef0..4182a244 100644 --- a/stacker_blueprints/rest_gateway.py +++ b/stacker_blueprints/rest_gateway.py @@ -34,6 +34,7 @@ def create_template(self): self.create_lambda_function() def create_lambda_function(self): + variables = self.get_variables() # Create a Lambda function that will be mapped t = self.template code = ["print('ima a lambda!')"] @@ -72,7 +73,7 @@ def create_lambda_function(self): # Create the Lambda function foobar_function = t.add_resource(Function( - "FoobarFunction", + variables["function_name"], Code=Code( ZipFile=Join("", code) ), From beab0aff3d8bcea3c91bffcec925a9ed0cbcc6b1 Mon Sep 17 00:00:00 2001 From: Dmitry Meyerson Date: Mon, 3 Feb 2020 14:55:26 -0500 Subject: [PATCH 6/8] working example --- stacker_blueprints/rest_gateway.py | 247 ++++++++++++++++------------- 1 file changed, 133 insertions(+), 114 deletions(-) diff --git a/stacker_blueprints/rest_gateway.py b/stacker_blueprints/rest_gateway.py index 4182a244..eadcc19b 100644 --- a/stacker_blueprints/rest_gateway.py +++ b/stacker_blueprints/rest_gateway.py @@ -16,25 +16,44 @@ # adapted from https://github.com/cloudtools/troposphere/blob/master/examples/ApiGateway.py class RestGateway(Blueprint): - VARIABLES = {} + + # where we set variable defaults - overriden the the yml + + VARIABLES = { + 'ApiName': + {"type": str, "description": "API gateaway name"}, + 'FunctionName': + {"type":str, "default":"FunctionName"}, + 'runtime': + {"type":str, "default":"python3.7"}, + 'handler': + {"type":str, "default": "index.handler"}, + # 'paths': + # {"type": list, "description": ""} + } def create_rest_gateway(self): t = self.template - + variables = self.get_variables() + print("restapi variables: {}".format(self.get_variables())) # Create the Api Gateway + rest_api = t.add_resource(RestApi( - "ExampleApi", - Name="ExampleApi" + "ApiGateway", + Name=variables['ApiName'] )) - t.add_output(Output("RestGatewayId", Value=Ref('ExampleApi'))) + t.add_output(Output("RestGatewayId", Value=Ref('ApiGateway'))) def create_template(self): self.create_rest_gateway() self.create_lambda_function() + self.create_deployment() + print(self.template.to_json()) def create_lambda_function(self): variables = self.get_variables() + print("lambda variables: {}".format(variables)) # Create a Lambda function that will be mapped t = self.template code = ["print('ima a lambda!')"] @@ -73,124 +92,124 @@ def create_lambda_function(self): # Create the Lambda function foobar_function = t.add_resource(Function( - variables["function_name"], + variables['FunctionName'], Code=Code( ZipFile=Join("", code) ), - Handler="index.handler", + Handler=variables['handler'], Role=GetAtt("LambdaExecutionRole", "Arn"), - Runtime="python3.7", + Runtime=variables['runtime'], )) # Create a resource to map the lambda function to resource = t.add_resource(Resource( "FoobarResource", - RestApiId=Ref('ExampleApi'), + RestApiId=Ref('ApiGateway'), PathPart="foobar", - ParentId=GetAtt("ExampleApi", "RootResourceId"), + ParentId=GetAtt("ApiGateway", "RootResourceId"), + )) + + # Create a Lambda API method for the Lambda resource + method = t.add_resource(Method( + "LambdaMethod", + DependsOn=variables['FunctionName'], + RestApiId=Ref('ApiGateway'), + AuthorizationType="NONE", + ResourceId=Ref(resource), + HttpMethod="GET", + Integration=Integration( + Credentials=GetAtt("LambdaExecutionRole", "Arn"), + Type="AWS", + IntegrationHttpMethod='POST', + IntegrationResponses=[ + IntegrationResponse( + StatusCode='200' + ) + ], + Uri=Join("", [ + "arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/", + GetAtt(variables["FunctionName"], "Arn"), + "/invocations" + ]) + ), + MethodResponses=[ + MethodResponse( + "CatResponse", + StatusCode='200' + ) + ] + )) + +# Create a deployment + def create_deployment(self): + t = self.template + variables = self.get_variables() + stage_name = 'v1' + + deployment = t.add_resource(Deployment( + "%sDeployment" % stage_name, + DependsOn="LambdaMethod", + RestApiId=Ref("ApiGateway"), )) -# # Create a Lambda API method for the Lambda resource -# method = t.add_resource(Method( -# "LambdaMethod", -# DependsOn='FoobarFunction', -# RestApiId=Ref(rest_api), -# AuthorizationType="NONE", -# ResourceId=Ref(resource), -# HttpMethod="GET", -# Integration=Integration( -# Credentials=GetAtt("LambdaExecutionRole", "Arn"), -# Type="AWS", -# IntegrationHttpMethod='POST', -# IntegrationResponses=[ -# IntegrationResponse( -# StatusCode='200' -# ) -# ], -# Uri=Join("", [ -# "arn:aws:apigateway:eu-west-1:lambda:path/2015-03-31/functions/", -# GetAtt("FoobarFunction", "Arn"), -# "/invocations" -# ]) -# ), -# MethodResponses=[ -# MethodResponse( -# "CatResponse", -# StatusCode='200' -# ) -# ] -# )) - -# # Create a deployment -# stage_name = 'v1' - -# deployment = t.add_resource(Deployment( -# "%sDeployment" % stage_name, -# DependsOn="LambdaMethod", -# RestApiId=Ref(rest_api), -# )) - -# stage = t.add_resource(Stage( -# '%sStage' % stage_name, -# StageName=stage_name, -# RestApiId=Ref(rest_api), -# DeploymentId=Ref(deployment) -# )) - -# key = t.add_resource(ApiKey( -# "ApiKey", -# StageKeys=[StageKey( -# RestApiId=Ref(rest_api), -# StageName=Ref(stage) -# )] -# )) - -# # Create an API usage plan -# usagePlan = t.add_resource(UsagePlan( -# "ExampleUsagePlan", -# UsagePlanName="ExampleUsagePlan", -# Description="Example usage plan", -# Quota=QuotaSettings( -# Limit=50000, -# Period="MONTH" -# ), -# Throttle=ThrottleSettings( -# BurstLimit=500, -# RateLimit=5000 -# ), -# ApiStages=[ -# ApiStage( -# ApiId=Ref(rest_api), -# Stage=Ref(stage) -# )] -# )) - -# # tie the usage plan and key together -# usagePlanKey = t.add_resource(UsagePlanKey( -# "ExampleUsagePlanKey", -# KeyId=Ref(key), -# KeyType="API_KEY", -# UsagePlanId=Ref(usagePlan) -# )) - -# # Add the deployment endpoint as an output -# t.add_output([ -# Output( -# "ApiEndpoint", -# Value=Join("", [ -# "https://", -# Ref(rest_api), -# ".execute-api.eu-west-1.amazonaws.com/", -# stage_name -# ]), -# Description="Endpoint for this stage of the api" -# ), -# Output( -# "ApiKey", -# Value=Ref(key), -# Description="API key" -# ), -# ]) - - -# print(t.to_json()) \ No newline at end of file + stage = t.add_resource(Stage( + '%sStage' % stage_name, + StageName=stage_name, + RestApiId=Ref("ApiGateway"), + DeploymentId=Ref(deployment) + )) + + key = t.add_resource(ApiKey( + "ApiKey", + StageKeys=[StageKey( + RestApiId=Ref("ApiGateway"), + StageName=Ref(stage) + )] + )) + + # Create an API usage plan + usagePlan = t.add_resource(UsagePlan( + "ExampleUsagePlan", + UsagePlanName="ExampleUsagePlan", + Description="Example usage plan", + Quota=QuotaSettings( + Limit=50000, + Period="MONTH" + ), + Throttle=ThrottleSettings( + BurstLimit=500, + RateLimit=5000 + ), + ApiStages=[ + ApiStage( + ApiId=Ref("ApiGateway"), + Stage=Ref(stage) + )] + )) + + # tie the usage plan and key together + usagePlanKey = t.add_resource(UsagePlanKey( + "ExampleUsagePlanKey", + KeyId=Ref(key), + KeyType="API_KEY", + UsagePlanId=Ref(usagePlan) + )) + + # Add the deployment endpoint as an output + t.add_output([ + Output( + "ApiEndpoint", + Value=Join("", [ + "https://", + Ref("ApiGateway"), + ".execute-api.us-east-1.amazonaws.com/", + stage_name + ]), + Description="Endpoint for this stage of the api" + ), + Output( + "ApiKey", + Value=Ref(key), + Description="API key" + ), + ]) \ No newline at end of file From a707144fbbf85e57214aebcdaaf848e9bbd4b224 Mon Sep 17 00:00:00 2001 From: Dmitry Meyerson Date: Mon, 3 Feb 2020 15:17:54 -0500 Subject: [PATCH 7/8] working code --- stacker_blueprints/rest_gateway.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/stacker_blueprints/rest_gateway.py b/stacker_blueprints/rest_gateway.py index eadcc19b..65e23646 100644 --- a/stacker_blueprints/rest_gateway.py +++ b/stacker_blueprints/rest_gateway.py @@ -28,6 +28,8 @@ class RestGateway(Blueprint): {"type":str, "default":"python3.7"}, 'handler': {"type":str, "default": "index.handler"}, + 'pathpart': + {"type":str, "default": "stuff"} # 'paths': # {"type": list, "description": ""} } @@ -56,7 +58,7 @@ def create_lambda_function(self): print("lambda variables: {}".format(variables)) # Create a Lambda function that will be mapped t = self.template - code = ["print('ima a lambda!')"] + code = ["def handler(event, context):\n print('im a lambda')"] # Create a role for the lambda function t.add_resource(Role( @@ -105,11 +107,12 @@ def create_lambda_function(self): resource = t.add_resource(Resource( "FoobarResource", RestApiId=Ref('ApiGateway'), - PathPart="foobar", + PathPart=variables["pathpart"], ParentId=GetAtt("ApiGateway", "RootResourceId"), )) # Create a Lambda API method for the Lambda resource + # this is what associates a pathpart with a lambda method = t.add_resource(Method( "LambdaMethod", DependsOn=variables['FunctionName'], From 89abe86ff4c8ba7f7a5ed2aacc5adc5683814a7e Mon Sep 17 00:00:00 2001 From: meyerson Date: Fri, 1 May 2020 13:25:06 -0500 Subject: [PATCH 8/8] Update README.rst --- README.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.rst b/README.rst index 81df07b1..e5f084fa 100644 --- a/README.rst +++ b/README.rst @@ -14,3 +14,5 @@ stacker_blueprints An attempt at a common Blueprint library for use with `stacker `_. If you're new to stacker you may use `stacker_cookiecutter `_ to setup your project. + +trigger something