Skip to content

Commit

Permalink
Add go lambda cdk example (#218)
Browse files Browse the repository at this point in the history
* Add go lambda cdk example

* Update templates/go-lambda-cdk/README.md

Co-authored-by: Pavel Tcholakov <[email protected]>

* go mod tidy

---------

Co-authored-by: Pavel Tcholakov <[email protected]>
  • Loading branch information
jackkleeman and pcholakov authored Dec 15, 2024
1 parent 4bec1ff commit 6821fb8
Show file tree
Hide file tree
Showing 16 changed files with 464 additions and 0 deletions.
3 changes: 3 additions & 0 deletions templates/go-lambda-cdk/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
dist
cdk.out
15 changes: 15 additions & 0 deletions templates/go-lambda-cdk/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"env": {
"browser": true,
"commonjs": true,
"es2021": true
},
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
"overrides": [],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest"
},
"plugins": ["@typescript-eslint"],
"rules": {}
}
10 changes: 10 additions & 0 deletions templates/go-lambda-cdk/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
!jest.config.js
node_modules
.cdk.staging
cdk.out
.gradle
lambda/go
*.js
*.d.ts
*.class
cdk.context.json
7 changes: 7 additions & 0 deletions templates/go-lambda-cdk/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"trailingComma": "all",
"tabWidth": 2,
"semi": true,
"arrowParens": "always",
"printWidth": 120
}
69 changes: 69 additions & 0 deletions templates/go-lambda-cdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Go Lambda (CDK) example

Sample project deploying a Go-based Restate service to AWS Lambda using the AWS Cloud Development Kit (CDK).
The stack uses the Restate CDK constructs library to register the service with a Restate Cloud environment.

For more information on CDK, please see [Getting started with the AWS CDK](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html).

* [CDK app entry point `lambda-go-cdk.ts`](bin/lambda-go-cdk.ts)
* [CDK stack consisting of a Lambda function and providing Restate service registration](cdk/lambda-go-cdk-stack.ts)
* [Go Lambda handler](lambda) - based on [`go` template](../go)

## Download the example

- Via the CLI:
```shell
restate example go-lambda-cdk && cd go-lambda-cdk
```

- Via git clone:
```shell
git clone [email protected]:restatedev/examples.git
cd examples/templates/go-lambda-cdk
```

- Via `wget`:
```shell
wget https://github.com/restatedev/examples/releases/latest/download/go-lambda-cdk.zip && unzip go-lambda-cdk.zip -d go-lambda-cdk && rm go-lambda-cdk.zip
```

## Deploy

**Pre-requisites:**

* npm
* Go >= 1.21
* AWS account, bootstrapped for CDK use
* valid AWS credentials with sufficient privileges to create the necessary resources
* an existing [Restate Cloud](https://restate.dev) environment (environment id + API key)

Install npm dependencies:

```shell
npm install
```

To deploy the stack, export the Restate Cloud environment id and admin API key, and run `cdk deploy`:

```shell
export RESTATE_ENV_ID=env_... RESTATE_API_KEY=key_...
npx cdk deploy
```

The stack output will print out the Restate server ingress URL.

### Test

You can send a test request to the Restate ingress endpoint to call the newly deployed service:

```shell
curl -k ${restateIngressUrl}/Greeter/Greet \
-H "Authorization: Bearer $RESTATE_API_KEY" \
-H 'content-type: application/json' -d '"Restate"'
```

### Useful commands

* `npm run build` compile the Lambda handler and synthesize CDK deployment artifacts
* `npm run deploy` perform a CDK deployment
* `npm run destroy` delete the stack and all its resources
24 changes: 24 additions & 0 deletions templates/go-lambda-cdk/bin/go-lambda-cdk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env node

/*
* Copyright (c) 2024 - Restate Software, Inc., Restate GmbH
*
* This file is part of the Restate examples,
* which is released under the MIT license.
*
* You can find a copy of the license in the file LICENSE
* in the root directory of this repository or package or at
* https://github.com/restatedev/examples/
*/

import "source-map-support/register";
import * as cdk from "aws-cdk-lib";
import { GoLambdaCdkStack } from "../cdk/go-lambda-cdk-stack";

const app = new cdk.App();
new GoLambdaCdkStack(app, "GoLambdaCdkStack", {
env: {
account: process.env.CDK_DEFAULT_ACCOUNT,
region: process.env.CDK_DEFAULT_REGION,
},
});
59 changes: 59 additions & 0 deletions templates/go-lambda-cdk/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"app": "npx ts-node --prefer-ts-exts bin/go-lambda-cdk.ts",
"watch": {
"include": ["**"],
"exclude": [
"README.md",
"cdk*.json",
"**/*.d.ts",
"**/*.js",
"tsconfig.json",
"package*.json",
"yarn.lock",
"node_modules",
"test"
]
},
"context": {
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
"@aws-cdk/core:checkSecretUsage": true,
"@aws-cdk/core:target-partitions": ["aws", "aws-cn"],
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
"@aws-cdk/aws-iam:minimizePolicies": 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/aws-apigateway:disableCloudWatchRole": true,
"@aws-cdk/core:enablePartitionLiterals": true,
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
"@aws-cdk/aws-iam:standardizedServicePrincipals": true,
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
"@aws-cdk/aws-route53-patters:useCertificate": true,
"@aws-cdk/customresources:installLatestAwsSdkDefault": false,
"@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
"@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
"@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
"@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
"@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
"@aws-cdk/aws-redshift:columnId": true,
"@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true,
"@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true,
"@aws-cdk/aws-apigateway:requestValidatorUniqueId": true,
"@aws-cdk/aws-kms:aliasNameRef": true,
"@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true,
"@aws-cdk/core:includePrefixInUniqueNameGeneration": true,
"@aws-cdk/aws-efs:denyAnonymousAccess": true,
"@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true,
"@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true,
"@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true,
"@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true,
"@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true,
"@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true,
"@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true
}
}
78 changes: 78 additions & 0 deletions templates/go-lambda-cdk/cdk/go-lambda-cdk-stack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright (c) 2024 - Restate Software, Inc., Restate GmbH
*
* This file is part of the Restate examples,
* which is released under the MIT license.
*
* You can find a copy of the license in the file LICENSE
* in the root directory of this repository or package or at
* https://github.com/restatedev/examples/
*/

import * as restate from "@restatedev/restate-cdk";
import * as cdk from "aws-cdk-lib";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as secrets from "aws-cdk-lib/aws-secretsmanager";
import * as go from "@aws-cdk/aws-lambda-go-alpha";
import { Construct } from "constructs";

export class GoLambdaCdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props: cdk.StackProps) {
super(scope, id, props);

const handler = new go.GoFunction(
this,
"GreeterService",
{
runtime: lambda.Runtime.PROVIDED_AL2023,
architecture: lambda.Architecture.ARM_64,
entry: "lambda",
timeout: cdk.Duration.seconds(10),
loggingFormat: lambda.LoggingFormat.JSON,
applicationLogLevelV2: lambda.ApplicationLogLevel.DEBUG,
systemLogLevelV2: lambda.SystemLogLevel.INFO,
},
);

// Set the RESTATE_ENV_ID and RESTATE_API_KEY environment variables to point to your Restate Cloud environment.
// This construct automatically creates an invoker role that Restate Cloud will be able to assume to invoke handlers
// on behalf of your environment. See https://docs.restate.dev/deploy/cloud for more information.
const restateEnvironment = new restate.RestateCloudEnvironment(
this,
"RestateCloud",
{
environmentId: process.env.RESTATE_ENV_ID! as restate.EnvironmentId,
// Warning: this will result in the API key being baked into the CloudFormation template!
// For improved security, pre-populate the secret and pass it to the construct as a reference.
// See: https://docs.aws.amazon.com/secretsmanager/latest/userguide/cdk.html
apiKey: new secrets.Secret(this, "RestateCloudApiKey", {
secretStringValue: cdk.SecretValue.unsafePlainText(
process.env.RESTATE_API_KEY!,
),
}),
},
);
const deployer = new restate.ServiceDeployer(this, "ServiceDeployer");

// Alternatively, you can deploy a standalone Restate server using the SingleNodeRestateDeployment construct.
// Please see https://docs.restate.dev/deploy/lambda/self-hosted and the construct documentation for more details.
// const vpc = ec2.Vpc.fromLookup(this, "Vpc", { vpcId: "..." });
// const restateEnvironment = new restate.SingleNodeRestateDeployment(this, "Restate", {
// vpc,
// networkConfiguration: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
// });
// const deployer = new restate.ServiceDeployer(this, "ServiceDeployer", {
// vpc,
// securityGroups: [restateEnvironment.adminSecurityGroup],
// });

deployer.deployService(
"Greeter",
handler.currentVersion,
restateEnvironment,
);
new cdk.CfnOutput(this, "restateIngressUrl", {
value: restateEnvironment.ingressUrl,
});
}
}
1 change: 1 addition & 0 deletions templates/go-lambda-cdk/lambda/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
go
22 changes: 22 additions & 0 deletions templates/go-lambda-cdk/lambda/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module github.com/restatedev/examples/templates/go-lambda-cdk/lambda

go 1.22.5

require (
github.com/aws/aws-lambda-go v1.47.0
github.com/restatedev/sdk-go v0.13.1
)

require (
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/mr-tron/base58 v1.2.0 // indirect
go.opentelemetry.io/otel v1.28.0 // indirect
go.opentelemetry.io/otel/metric v1.28.0 // indirect
go.opentelemetry.io/otel/trace v1.28.0 // indirect
golang.org/x/net v0.23.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/protobuf v1.35.2 // indirect
)
39 changes: 39 additions & 0 deletions templates/go-lambda-cdk/lambda/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
github.com/aws/aws-lambda-go v1.47.0 h1:0H8s0vumYx/YKs4sE7YM0ktwL2eWse+kfopsRI1sXVI=
github.com/aws/aws-lambda-go v1.47.0/go.mod h1:dpMpZgvWx5vuQJfBt0zqBha60q7Dd7RfgJv23DymV8A=
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/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
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/restatedev/sdk-go v0.13.1 h1:6DdbDaGI7mI8uXxpwBKjbGnByvYP18UHQ83bANi+wAM=
github.com/restatedev/sdk-go v0.13.1/go.mod h1:Xalv67a5uOgGcbz7U1BgZQydCrsmENq2RAeTwTGXHng=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
33 changes: 33 additions & 0 deletions templates/go-lambda-cdk/lambda/greeter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package main

import (
restate "github.com/restatedev/sdk-go"
"time"
)

// Greeter is a struct which represents a Restate service; reflection will turn exported methods into service handlers
type Greeter struct{}

func (Greeter) Greet(ctx restate.Context, name string) (string, error) {
// Durably execute a set of steps; resilient against failures
greetingId := restate.Rand(ctx).UUID().String()

if _, err := restate.Run(ctx, func(ctx restate.RunContext) (restate.Void, error) {
return restate.Void{}, SendNotification(greetingId, name)
}); err != nil {
return "", err
}

if err := restate.Sleep(ctx, 1*time.Second); err != nil {
return "", err
}

if _, err := restate.Run(ctx, func(ctx restate.RunContext) (restate.Void, error) {
return restate.Void{}, SendReminder(greetingId)
}); err != nil {
return "", err
}

// Respond to caller
return "You said hi to " + name + "!", nil
}
23 changes: 23 additions & 0 deletions templates/go-lambda-cdk/lambda/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package main

import (
"log/slog"
"os"

"github.com/aws/aws-lambda-go/lambda"
restate "github.com/restatedev/sdk-go"
"github.com/restatedev/sdk-go/server"
)

func main() {
server := server.NewRestate().
Bind(restate.Reflect(Greeter{}))

handler, err := server.LambdaHandler()
if err != nil {
slog.Error("failed to create lambda handler", "err", err.Error())
os.Exit(1)
}

lambda.Start(handler)
}
Loading

0 comments on commit 6821fb8

Please sign in to comment.