Skip to content

Commit

Permalink
feat: add go sample for API Gateway
Browse files Browse the repository at this point in the history
  • Loading branch information
retgits committed Mar 11, 2020
1 parent 8e6afac commit ae95527
Show file tree
Hide file tree
Showing 6 changed files with 347 additions and 0 deletions.
42 changes: 42 additions & 0 deletions aws-go-lambda-gateway/Gopkg.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Gopkg.toml example
#
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true


[[constraint]]
name = "github.com/pulumi/pulumi"
version = "1.12.0"

[[constraint]]
name = "github.com/pulumi/pulumi-aws"
version = "1.25.0"

[[constraint]]
name = "github.com/aws/aws-lambda-go"
version = "1.15.0"

[prune]
go-tests = true
unused-packages = true
3 changes: 3 additions & 0 deletions aws-go-lambda-gateway/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
build::
GOOS=linux GOARCH=amd64 go build -o ./handler/handler ./handler/handler.go
zip -j ./handler/handler.zip ./handler/handler
13 changes: 13 additions & 0 deletions aws-go-lambda-gateway/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: go-lambda
runtime: go
description: Basic example of an AWS lambda With API Gateway
template:
config:
aws:region:
description: The AWS region to deploy into
default: us-east-1
go-lambda:accountid:
description: The AWS account ID to use
go-lambda:gateway-region:
description: The AWS region to deploy the AWS API Gateway to
default: us-east-1
124 changes: 124 additions & 0 deletions aws-go-lambda-gateway/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# AWS Golang Lambda With API Gateway

This example creates a lambda that does a simple `ToUpper` on the path input of an API request and returns it.

## Deploying the App

To deploy your infrastructure, follow the below steps.

### Prerequisites

1. [Install Pulumi](https://www.pulumi.com/docs/get-started/install/)
2. [Configure AWS Credentials](https://www.pulumi.com/docs/intro/cloud-providers/aws/setup/)
3. [Clone aws-go-lambda](https://github.com/aws/aws-lambda-go)

### Steps

After cloning this repo, run these commands from the working directory:

1. Restore your Go dependencies. This example currently uses [Dep](https://github.com/golang/dep) to do so:

```bash
$ dep ensure
```

1. Build the handler:

- For developers on Linux and macOS:

```bash
make build
```
- For developers on Windows:
- Get the `build-lambda-zip` tool:
```bash
set GO111MODULE=on
go.exe get -u github.com/aws/aws-lambda-go/cmd/build-lambda-zip
```
- Use the tool from your GOPATH:
```bash
set GOOS=linux
set GOARCH=amd64
set CGO_ENABLED=0
go build -o handler\handler handler\handler.go
%USERPROFILE%\Go\bin\build-lambda-zip.exe -o handler\handler.zip handler\handler
```

2. Create a new Pulumi stack, which is an isolated deployment target for this example:

```bash
pulumi stack init
```

3. Set the required configuration variables for this program:
```bash
$ pulumi config set aws:region us-west-2
$ pulumi config set accountid <your AWS account ID>
$ pulumi config set gateway-region <the region where you want to deploy your API gateway>
```

4. Execute the Pulumi program to create our lambda:

```bash
$ pulumi up
Previewing update (dev):
Type Name Plan
+ pulumi:pulumi:Stack go-lambda-dev create
+ ├─ aws:apigateway:RestApi UpperCaseGateway create
+ ├─ aws:iam:Role task-exec-role create
+ ├─ aws:apigateway:Resource UpperAPI create
+ ├─ aws:iam:RolePolicy lambda-log-policy create
+ ├─ aws:apigateway:Method AnyMethod create
+ ├─ aws:lambda:Function basicLambda create
+ ├─ aws:lambda:Permission APIPermission create
+ ├─ aws:apigateway:Integration LambdaIntegration create
+ └─ aws:apigateway:Deployment APIDeployment create

Resources:
+ 10 to create

Do you want to perform this update? yes
Updating (dev):
Type Name Status
+ pulumi:pulumi:Stack go-lambda-dev created
+ ├─ aws:apigateway:RestApi UpperCaseGateway created
+ ├─ aws:iam:Role task-exec-role created
+ ├─ aws:apigateway:Resource UpperAPI created
+ ├─ aws:iam:RolePolicy lambda-log-policy created
+ ├─ aws:apigateway:Method AnyMethod created
+ ├─ aws:lambda:Function basicLambda created
+ ├─ aws:apigateway:Integration LambdaIntegration created
+ ├─ aws:lambda:Permission APIPermission created
+ └─ aws:apigateway:Deployment APIDeployment created

Outputs:
invocation URL: "https://<gateway-id>.execute-api.us-west-2.amazonaws.com/prod/{message}"
lambda : "arn:aws:lambda:us-west-2:ACCOUNTID:function:basicLambda-75711af"

Resources:
+ 10 created

Duration: 29s
```

5. Call our lambda function from the cli:

```bash
curl https://<gateway-id>.execute-api.us-west-2.amazonaws.com/prod/helloworld
HELLOWORLD%
```

6. From there, feel free to experiment. Simply making edits, rebuilding your handler, and running `pulumi up` will update your lambda.

7. Afterwards, destroy your stack and remove it:

```bash
pulumi destroy --yes
pulumi stack rm --yes
```
20 changes: 20 additions & 0 deletions aws-go-lambda-gateway/handler/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package main

import (
"strings"

"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
)

// handler is a simple function that takes a string and does a ToUpper.
func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
return events.APIGatewayProxyResponse{
StatusCode: 200,
Body: strings.ToUpper(request.Path[1:]),
}, nil
}

func main() {
lambda.Start(handler)
}
145 changes: 145 additions & 0 deletions aws-go-lambda-gateway/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package main

import (
"github.com/pulumi/pulumi-aws/sdk/go/aws/apigateway"
"github.com/pulumi/pulumi-aws/sdk/go/aws/iam"
"github.com/pulumi/pulumi-aws/sdk/go/aws/lambda"
"github.com/pulumi/pulumi/sdk/go/pulumi"
)

func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
// Read the configuration data from Pulumi.<stack>.yaml
gatewayRegion, _ := ctx.GetConfig("go-lambda:gateway-region")
accountID, _ := ctx.GetConfig("go-lambda:accountid")

// Create an IAM role.
role, err := iam.NewRole(ctx, "task-exec-role", &iam.RoleArgs{
AssumeRolePolicy: pulumi.String(`{
"Version": "2012-10-17",
"Statement": [{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}]
}`),
})
if err != nil {
return err
}

// Attach a policy to allow writing logs to CloudWatch
logPolicy, err := iam.NewRolePolicy(ctx, "lambda-log-policy", &iam.RolePolicyArgs{
Role: role.Name,
Policy: pulumi.String(`{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
}]
}`),
})

// Set arguments for constructing the function resource.
args := &lambda.FunctionArgs{
Handler: pulumi.String("handler"),
Role: role.Arn,
Runtime: pulumi.String("go1.x"),
Code: pulumi.NewFileArchive("./handler/handler.zip"),
}

// Create the lambda using the args.
function, err := lambda.NewFunction(
ctx,
"basicLambda",
args,
pulumi.DependsOn([]pulumi.Resource{logPolicy}),
)
if err != nil {
return err
}

// Create a new API Gateway.
gateway, err := apigateway.NewRestApi(ctx, "UpperCaseGateway", &apigateway.RestApiArgs{
Name: pulumi.String("UpperCaseGateway"),
Description: pulumi.String("An API Gateway for the UpperCase function"),
Policy: pulumi.String(`{ "Version": "2012-10-17", "Statement": [ { "Action": "sts:AssumeRole", "Principal": { "Service": "lambda.amazonaws.com" }, "Effect": "Allow", "Sid": "" },{ "Action": "execute-api:Invoke", "Resource":"execute-api:/*", "Principal": "*", "Effect": "Allow", "Sid": "" } ] }`),
})
if err != nil {
return err
}

// Add a resource to the API Gateway.
// This makes the API Gateway accept requests on "/{message}".
apiresource, err := apigateway.NewResource(ctx, "UpperAPI", &apigateway.ResourceArgs{
RestApi: gateway.ID(),
PathPart: pulumi.String("{proxy+}"),
ParentId: gateway.RootResourceId,
}, pulumi.DependsOn([]pulumi.Resource{gateway}))
if err != nil {
return err
}

// Add a method to the API Gateway.
_, err = apigateway.NewMethod(ctx, "GetMethod", &apigateway.MethodArgs{
HttpMethod: pulumi.String("ANY"),
Authorization: pulumi.String("NONE"),
RestApi: gateway.ID(),
ResourceId: apiresource.ID(),
}, pulumi.DependsOn([]pulumi.Resource{gateway, apiresource}))
if err != nil {
return err
}

// Add an integration to the API Gateway.
// This makes communication between the API Gateway and the Lambda function work
_, err = apigateway.NewIntegration(ctx, "LambdaIntegration", &apigateway.IntegrationArgs{
HttpMethod: pulumi.String("ANY"),
IntegrationHttpMethod: pulumi.String("POST"),
ResourceId: apiresource.ID(),
RestApi: gateway.ID(),
Type: pulumi.String("AWS_PROXY"),
Uri: function.InvokeArn,
}, pulumi.DependsOn([]pulumi.Resource{gateway, apiresource, function}))
if err != nil {
return err
}

// Add a resource based policy to the Lambda function.
// This is the final step and allows AWS API Gateway to communicate with the AWS Lambda function
permission, err := lambda.NewPermission(ctx, "APIPermission", &lambda.PermissionArgs{
Action: pulumi.String("lambda:InvokeFunction"),
Function: function.Name,
Principal: pulumi.String("apigateway.amazonaws.com"),
SourceArn: pulumi.Sprintf("arn:aws:execute-api:%s:%s:%s/*/*/*", gatewayRegion, accountID, gateway.ID()),
}, pulumi.DependsOn([]pulumi.Resource{gateway, apiresource, function}))
if err != nil {
return err
}

// Create a new deployment
_, err = apigateway.NewDeployment(ctx, "APIDeployment", &apigateway.DeploymentArgs{
Description: pulumi.String("UpperCase API deployment"),
RestApi: gateway.ID(),
StageDescription: pulumi.String("Production"),
StageName: pulumi.String("prod"),
}, pulumi.DependsOn([]pulumi.Resource{gateway, apiresource, function, permission}))
if err != nil {
return err
}

// Export the lambda ARN and API Gateway URL
ctx.Export("lambda", function.Arn)
ctx.Export("invocation URL", pulumi.Sprintf("https://%s.execute-api.%s.amazonaws.com/prod/{message}", gateway.ID(), gatewayRegion))

return nil
})
}

0 comments on commit ae95527

Please sign in to comment.