Skip to content
This repository was archived by the owner on Aug 1, 2024. It is now read-only.

feat: add go workshop #636

Merged
merged 13 commits into from
Aug 29, 2022
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions code/go/main-workshop/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# go.sum should be committed
!go.sum

# CDK asset staging directory
.cdk.staging
cdk.out
12 changes: 12 additions & 0 deletions code/go/main-workshop/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Welcome to your CDK Go project!

This is a blank project for Go development with CDK.

The `cdk.json` file tells the CDK Toolkit how to execute your app.

## Useful commands

* `cdk deploy` deploy this stack to your default AWS account/region
* `cdk diff` compare deployed stack with current state
* `cdk synth` emits the synthesized CloudFormation template
* `go test` run unit tests
55 changes: 55 additions & 0 deletions code/go/main-workshop/cdk-workshop.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package main

import (
"github.com/aws/aws-cdk-go/awscdk/v2"
"github.com/aws/aws-cdk-go/awscdk/v2/awslambda"
"github.com/aws/aws-cdk-go/awscdk/v2/awsapigateway"
"github.com/aws/constructs-go/constructs/v10"
"github.com/aws/jsii-runtime-go"
"cdk-workshop/hitcounter"
"github.com/cdklabs/cdk-dynamo-table-viewer-go/dynamotableviewer"
)

type CdkWorkshopStackProps struct {
awscdk.StackProps
}

func NewCdkWorkshopStack(scope constructs.Construct, id string, props *CdkWorkshopStackProps) awscdk.Stack {
var sprops awscdk.StackProps
if props != nil {
sprops = props.StackProps
}
stack := awscdk.NewStack(scope, &id, &sprops)

helloHandler := awslambda.NewFunction(stack, jsii.String("HelloHandler"), &awslambda.FunctionProps{
Code: awslambda.Code_FromAsset(jsii.String("lambda"), nil),
Runtime: awslambda.Runtime_NODEJS_16_X(),
Handler: jsii.String("hello.handler"),
})

hitcounter := hitcounter.NewHitCounter(stack, "HelloHitCounter", &hitcounter.HitCounterProps{
Downstream: helloHandler,
ReadCapacity: 10,
})

awsapigateway.NewLambdaRestApi(stack, jsii.String("Endpoint"), &awsapigateway.LambdaRestApiProps{
Handler: hitcounter.Handler(),
})

dynamotableviewer.NewTableViewer(stack, jsii.String("ViewHitCounter"), &dynamotableviewer.TableViewerProps{
Title: jsii.String("Hello Hits"),
Table: hitcounter.Table(),
})

return stack
}

func main() {
defer jsii.Close()

app := awscdk.NewApp(nil)

NewCdkWorkshopStack(app, "CdkWorkshopStack", &CdkWorkshopStackProps{})

app.Synth(nil)
}
95 changes: 95 additions & 0 deletions code/go/main-workshop/cdk-workshop_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package main

import (
"testing"

"github.com/aws/aws-cdk-go/awscdk/v2"
"github.com/aws/aws-cdk-go/awscdk/v2/assertions"
"github.com/aws/aws-cdk-go/awscdk/v2/awslambda"
"github.com/aws/jsii-runtime-go"
"github.com/google/go-cmp/cmp"
"cdk-workshop/hitcounter"
)

func TestTableCreatedWithEncryption(t *testing.T) {
// GIVEN
stack := awscdk.NewStack(nil, nil, nil)

// WHEN
testFn := awslambda.NewFunction(stack, jsii.String("TestFunction"), &awslambda.FunctionProps{
Code: awslambda.Code_FromAsset(jsii.String("lambda"), nil),
Runtime: awslambda.Runtime_NODEJS_16_X(),
Handler: jsii.String("hello.handler"),
})
hitcounter.NewHitCounter(stack, "MyTestConstruct", &hitcounter.HitCounterProps{
Downstream: testFn,
ReadCapacity: 10,
})

// THEN
template := assertions.Template_FromStack(stack)
template.HasResourceProperties(jsii.String("AWS::DynamoDB::Table"), &map[string]any{
"SSESpecification": map[string]any{
"SSEEnabled": true,
},
})
}

func TestLambdaFunction(t *testing.T) {
// GIVEN
stack := awscdk.NewStack(nil, nil, nil)

// WHEN
testFn := awslambda.NewFunction(stack, jsii.String("TestFunction"), &awslambda.FunctionProps{
Code: awslambda.Code_FromAsset(jsii.String("lambda"), nil),
Runtime: awslambda.Runtime_NODEJS_16_X(),
Handler: jsii.String("hello.handler"),
})
hitcounter.NewHitCounter(stack, "MyTestConstruct", &hitcounter.HitCounterProps{
Downstream: testFn,
ReadCapacity: 10,
})

// THEN
template := assertions.Template_FromStack(stack)
envCapture := assertions.NewCapture(nil)
template.HasResourceProperties(jsii.String("AWS::Lambda::Function"), &map[string]any{
"Environment": envCapture,
"Handler": "hitcounter.handler",
})
expectedEnv := &map[string]any{
"Variables": map[string]any{
"DOWNSTREAM_FUNCTION_NAME": map[string]any{
"Ref": "TestFunction22AD90FC",
},
"HITS_TABLE_NAME": map[string]any{
"Ref": "MyTestConstructHits24A357F0",
},
},
}
if !cmp.Equal(envCapture.AsObject(), expectedEnv) {
t.Error(expectedEnv, envCapture.AsObject())
}
}

func TestCanPassReadCapacity(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Error("Did not throw ReadCapacity error")
}
}()

// GIVEN
stack := awscdk.NewStack(nil, nil, nil)

// WHEN
testFn := awslambda.NewFunction(stack, jsii.String("TestFunction"), &awslambda.FunctionProps{
Code: awslambda.Code_FromAsset(jsii.String("lambda"), nil),
Runtime: awslambda.Runtime_NODEJS_16_X(),
Handler: jsii.String("hello.handler"),
})
hitcounter.NewHitCounter(stack, "MyTestConstruct", &hitcounter.HitCounterProps{
Downstream: testFn,
ReadCapacity: 20,
})
}
36 changes: 36 additions & 0 deletions code/go/main-workshop/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"app": "go mod download && go run cdk-workshop.go",
"watch": {
"include": [
"**"
],
"exclude": [
"README.md",
"cdk*.json",
"go.mod",
"go.sum",
"**/*test.go"
]
},
"context": {
"@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true,
"@aws-cdk/core:stackRelativeExports": true,
"@aws-cdk/aws-rds:lowercaseDbIdentifier": true,
"@aws-cdk/aws-lambda:recognizeVersionProps": true,
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
"@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true,
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
"@aws-cdk/core:checkSecretUsage": true,
"@aws-cdk/aws-iam:minimizePolicies": true,
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
"@aws-cdk/core:target-partitions": [
"aws",
"aws-cn"
]
}
}
13 changes: 13 additions & 0 deletions code/go/main-workshop/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module cdk-workshop

go 1.18

require (
github.com/aws/aws-cdk-go/awscdk/v2 v2.38.1
github.com/aws/constructs-go/constructs/v10 v10.1.84
github.com/aws/jsii-runtime-go v1.65.0
github.com/cdklabs/cdk-dynamo-table-viewer-go/dynamotableviewer v0.2.248
github.com/google/go-cmp v0.5.8
)

require github.com/Masterminds/semver/v3 v3.1.1 // indirect
35 changes: 35 additions & 0 deletions code/go/main-workshop/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/aws/aws-cdk-go/awscdk/v2 v2.0.0/go.mod h1:2PrsMzp7CioaiAHHEr8zCVkbMIKp+sZs/S15kTkLUH0=
github.com/aws/aws-cdk-go/awscdk/v2 v2.38.1 h1:5Jz4KkHMa0MS4uLQO8X32cuyHxRY8YOOWyFCJRFpfWE=
github.com/aws/aws-cdk-go/awscdk/v2 v2.38.1/go.mod h1:rNrZ+WbqCuPfpUrMcDmrEOel9ZlMCy2+E0iyNCJjS+4=
github.com/aws/constructs-go/constructs/v10 v10.0.5/go.mod h1:l9g2pvi6/NDTGfjih3Zocwk3K4ASge77Pf5KZ2j2484=
github.com/aws/constructs-go/constructs/v10 v10.0.9/go.mod h1:RC6w8bOwxLmPX7Jfo9dkEZ9iVfgH4QnaVnfWvaNOHy0=
github.com/aws/constructs-go/constructs/v10 v10.1.71/go.mod h1:nJzXC/q76n0XkbL76eG0OvJQwbD/lWc7sjerJEARubA=
github.com/aws/constructs-go/constructs/v10 v10.1.84 h1:tB6ui58Bvr/mO+qGsghf4v+4NubnBBDjTUkTY/tvBOg=
github.com/aws/constructs-go/constructs/v10 v10.1.84/go.mod h1:7+5GjvX5buJY6vZZmMxd50Ke1+NXzjva8N2eL+3txQQ=
github.com/aws/jsii-runtime-go v1.28.0/go.mod h1:6tZnlstx8bAB3vnLFF9n8bbkI//LDblAek9zFyMXV3E=
github.com/aws/jsii-runtime-go v1.37.0/go.mod h1:6tZnlstx8bAB3vnLFF9n8bbkI//LDblAek9zFyMXV3E=
github.com/aws/jsii-runtime-go v1.46.0/go.mod h1:6tZnlstx8bAB3vnLFF9n8bbkI//LDblAek9zFyMXV3E=
github.com/aws/jsii-runtime-go v1.63.2/go.mod h1:Dq2QkYFSpiHGabsCBMmLnnGkyx3lnf5k6C6fq8RN/90=
github.com/aws/jsii-runtime-go v1.65.0 h1:A6o9DpZD0+IeFrXJ/qBPX7VJne5Vuk2KSfrG5Ez2dz8=
github.com/aws/jsii-runtime-go v1.65.0/go.mod h1:Dq2QkYFSpiHGabsCBMmLnnGkyx3lnf5k6C6fq8RN/90=
github.com/cdklabs/cdk-dynamo-table-viewer-go/dynamotableviewer v0.2.248 h1:WemEJJwFDVANkBm0u/wEIEgvCITfnX5L9Co0EQW763I=
github.com/cdklabs/cdk-dynamo-table-viewer-go/dynamotableviewer v0.2.248/go.mod h1:k4cRRJUP2rBTxriNTIlJqoXgsTVoYtoCZPi6OLRGKXY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
64 changes: 64 additions & 0 deletions code/go/main-workshop/hitcounter/hitcounter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package hitcounter

import (
"github.com/aws/aws-cdk-go/awscdk/v2"
"github.com/aws/aws-cdk-go/awscdk/v2/awsdynamodb"
"github.com/aws/aws-cdk-go/awscdk/v2/awslambda"
"github.com/aws/constructs-go/constructs/v10"
"github.com/aws/jsii-runtime-go"
)

type HitCounterProps struct {
Downstream awslambda.IFunction
ReadCapacity float64
}

type hitCounter struct {
constructs.Construct
handler awslambda.IFunction
table awsdynamodb.Table
}

type HitCounter interface {
constructs.Construct
Handler() awslambda.IFunction
Table() awsdynamodb.Table
}

func NewHitCounter(scope constructs.Construct, id string, props *HitCounterProps) HitCounter {
if props.ReadCapacity < 5 || props.ReadCapacity > 20 {
panic("ReadCapacity must be between 5 and 20")
}

this := constructs.NewConstruct(scope, &id)

table := awsdynamodb.NewTable(this, jsii.String("Hits"), &awsdynamodb.TableProps{
PartitionKey: &awsdynamodb.Attribute{Name: jsii.String("path"), Type: awsdynamodb.AttributeType_STRING},
RemovalPolicy: awscdk.RemovalPolicy_DESTROY,
Encryption: awsdynamodb.TableEncryption_AWS_MANAGED,
ReadCapacity: jsii.Number(props.ReadCapacity),
})

handler := awslambda.NewFunction(this, jsii.String("HitCounterHandler"), &awslambda.FunctionProps{
Runtime: awslambda.Runtime_NODEJS_16_X(),
Handler: jsii.String("hitcounter.handler"),
Code: awslambda.Code_FromAsset(jsii.String("lambda"), nil),
Environment: &map[string]*string{
"DOWNSTREAM_FUNCTION_NAME": (*props).Downstream.FunctionName(),
"HITS_TABLE_NAME": table.TableName(),
},
})

table.GrantReadWriteData(handler)
props.Downstream.GrantInvoke(handler)

return &hitCounter{this, handler, table}
}

func (h *hitCounter) Handler() awslambda.IFunction {
return h.handler
}

func (h *hitCounter) Table() awsdynamodb.Table {
return h.table
}
8 changes: 8 additions & 0 deletions code/go/main-workshop/lambda/hello.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
exports.handler = async function(event) {
console.log("request:", JSON.stringify(event, undefined, 2));
return {
statusCode: 200,
headers: { "Content-Type": "text/plain" },
body: `Hello, CDK! You've hit ${event.path}\n`
};
};
28 changes: 28 additions & 0 deletions code/go/main-workshop/lambda/hitcounter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const { DynamoDB, Lambda } = require('aws-sdk');

exports.handler = async function(event) {
console.log("request:", JSON.stringify(event, undefined, 2));

// create AWS SDK clients
const dynamo = new DynamoDB();
const lambda = new Lambda();

// update dynamo entry for "path" with hits++
await dynamo.updateItem({
TableName: process.env.HITS_TABLE_NAME,
Key: { path: { S: event.path } },
UpdateExpression: 'ADD hits :incr',
ExpressionAttributeValues: { ':incr': { N: '1' } }
}).promise();

// call downstream function and capture response
const resp = await lambda.invoke({
FunctionName: process.env.DOWNSTREAM_FUNCTION_NAME,
Payload: JSON.stringify(event)
}).promise();

console.log('downstream response:', JSON.stringify(resp, undefined, 2));

// return response back to upstream caller
return JSON.parse(resp.Payload);
};
19 changes: 19 additions & 0 deletions code/go/pipelines-workshop/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# go.sum should be committed
!go.sum

# CDK asset staging directory
.cdk.staging
cdk.out
Loading