-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Kotlin Lambda (CDK) example (#50)
- Loading branch information
Showing
24 changed files
with
6,950 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
node_modules | ||
dist | ||
cdk.out |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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": {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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/build | ||
*.js | ||
*.d.ts | ||
*.class | ||
cdk.context.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"trailingComma": "all", | ||
"tabWidth": 2, | ||
"semi": true, | ||
"arrowParens": "always", | ||
"printWidth": 120 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
# Hello world - Kotlin Lambda (CDK) example | ||
|
||
Sample project deploying a Kotlin-based Restate service to AWS Lambda using the AWS Cloud Development Kit (CDK). This | ||
is functionally equivalent to the [`hello-world-lambda`](../hello-world-lambda) example but uses CDK to automate the deployment of the | ||
Lambda function to AWS, and to automate handler registration with a Restate. | ||
|
||
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-jvm-cdk.ts`](bin/lambda-jvm-cdk.ts) | ||
* [CDK stack consisting of a Lambda function and providing Restate service registration](cdk/lambda-jvm-cdk-stack.ts) | ||
* [Kotlin Lambda handler](lambda) - based on [`hello-world-lambda`](../hello-world-lambda) | ||
|
||
## Download the example | ||
|
||
```shell | ||
wget https://github.com/restatedev/examples/releases/latest/download/kotlin-hello-world-lambda-cdk.zip && unzip kotlin-hello-world-lambda-cdk.zip -d kotlin-hello-world-lambda-cdk && rm kotlin-hello-world-lambda-cdk.zip | ||
``` | ||
|
||
## Deploy | ||
|
||
**Pre-requisites:** | ||
|
||
* npm | ||
* gradle | ||
* JDK >= 11 | ||
* Restate Cloud access (cluster id + API token) | ||
* AWS account, bootstrapped for CDK use | ||
|
||
Create a secret in Secrets Manager to hold the authentication token. The secret name is up to you -- we suggest | ||
using `/restate/` and an appropriate prefix to avoid confusion: | ||
|
||
```shell | ||
export AUTH_TOKEN_ARN=$(aws secretsmanager create-secret \ | ||
--name /restate/${CLUSTER_ID}/auth-token --secret-string ${RESTATE_AUTH_TOKEN} \ | ||
--query ARN --output text | ||
) | ||
``` | ||
|
||
Once you have the ARN for the auth token secret, you can deploy the stack using: | ||
|
||
```shell | ||
npx cdk deploy \ | ||
--context clusterId=${CLUSTER_ID} \ | ||
--context authTokenSecretArn=${AUTH_TOKEN_ARN} | ||
``` | ||
|
||
Alternatively, you can save this information in the `cdk.context.json` file: | ||
|
||
```json | ||
{ | ||
"clusterId": "...", | ||
"authTokenSecretArn": "arn:aws:secretsmanager:us-east-1:123456789012:secret:/restate/.../auth-token-abc123" | ||
} | ||
``` | ||
|
||
In that case, you can simply run: | ||
|
||
```shell | ||
npm run deploy | ||
``` | ||
|
||
In this example, the Lambda handler function name is dynamically generated by CDK. You can see what it is from the `HandlerFunction` | ||
output of the CDK stack after a successful deployment. | ||
|
||
### Test | ||
|
||
You can send a test request to the Restate cluster ingress endpoint to call the newly deployed service: | ||
|
||
```shell | ||
curl --json '{}' -H "Authorization: Bearer ${RESTATE_API_TOKEN}" \ | ||
https://${CLUSTER_ID}.dev.restate.cloud:8080/greeter.Greeter/Greet | ||
``` | ||
|
||
### Useful commands | ||
|
||
* `npm run build` compile the Lambda handler and synthesize CDK deployment artifacts | ||
* `npm run deploy` perform a CDK deployment |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
#!/usr/bin/env node | ||
import "source-map-support/register"; | ||
import * as cdk from "aws-cdk-lib"; | ||
import { LambdaJvmCdkStack } from "../cdk/lambda-jvm-cdk-stack"; | ||
|
||
const app = new cdk.App(); | ||
new LambdaJvmCdkStack(app, "LambdaJvmCdkStack", { | ||
clusterId: app.node.getContext("clusterId"), | ||
authTokenSecretArn: app.node.getContext("authTokenSecretArn"), | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
{ | ||
"app": "npx ts-node --prefer-ts-exts bin/lambda-jvm-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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import * as cdk from "aws-cdk-lib"; | ||
import * as lambda from "aws-cdk-lib/aws-lambda"; | ||
import * as restate from "@restatedev/restate-cdk"; | ||
import { Construct } from "constructs"; | ||
|
||
export class LambdaJvmCdkStack extends cdk.Stack { | ||
constructor( | ||
scope: Construct, | ||
id: string, | ||
props: { | ||
clusterId: string; | ||
authTokenSecretArn: string; | ||
} & cdk.StackProps, | ||
) { | ||
super(scope, id, props); | ||
|
||
const greeter: lambda.Function = new lambda.Function(this, "RestateKotlin", { | ||
runtime: lambda.Runtime.JAVA_21, | ||
architecture: lambda.Architecture.ARM_64, | ||
code: lambda.Code.fromAsset("lambda/build/libs/lambda-all.jar"), | ||
handler: "dev.restate.sdk.examples.LambdaHandler", | ||
timeout: cdk.Duration.seconds(10), | ||
logFormat: lambda.LogFormat.JSON, | ||
applicationLogLevel: "DEBUG", | ||
systemLogLevel: "DEBUG", | ||
}); | ||
|
||
const restateInstance = new restate.RestateCloudEndpoint(this, "RestateCloud", { | ||
clusterId: props.clusterId, | ||
authTokenSecretArn: props.authTokenSecretArn, | ||
}); | ||
|
||
// Alternatively, you can deploy Restate on your own infrastructure like this. See the Restate CDK docs for more. | ||
// const restateInstance = new restate.SingleNodeRestateInstance(this, "Restate", { | ||
// logGroup: new logs.LogGroup(this, "RestateLogs", { | ||
// retention: logs.RetentionDays.THREE_MONTHS, | ||
// }), | ||
// }); | ||
|
||
const handlers = new restate.LambdaServiceRegistry(this, "RestateServices", { | ||
serviceHandlers: { | ||
"greeter.Greeter": greeter, | ||
}, | ||
restate: restateInstance, | ||
}); | ||
handlers.register({ | ||
metaEndpoint: restateInstance.metaEndpoint, | ||
invokerRoleArn: restateInstance.invokerRole.roleArn, | ||
authTokenSecretArn: restateInstance.authToken.secretArn, | ||
}); | ||
|
||
new cdk.CfnOutput(this, "HandlerFunction", { | ||
value: greeter.functionName, | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar | ||
import com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer | ||
import com.google.protobuf.gradle.id | ||
|
||
val restateVersion = "0.6.0" | ||
|
||
plugins { | ||
kotlin("jvm") version "1.9.10" | ||
|
||
id("com.google.protobuf") version "0.9.1" | ||
|
||
// To package the dependency for Lambda | ||
id("com.github.johnrengelman.shadow") version "8.1.1" | ||
} | ||
|
||
repositories { | ||
mavenCentral() | ||
} | ||
|
||
dependencies { | ||
// Restate SDK | ||
implementation("dev.restate:sdk-api-kotlin:$restateVersion") | ||
implementation("dev.restate:sdk-lambda:$restateVersion") | ||
// To use Jackson to read/write state entries (optional) | ||
implementation("dev.restate:sdk-serde-jackson:$restateVersion") | ||
|
||
// Protobuf and grpc dependencies (we need the Java dependencies as well because the Kotlin dependencies rely on Java) | ||
implementation("com.google.protobuf:protobuf-java:3.24.3") | ||
implementation("com.google.protobuf:protobuf-kotlin:3.24.3") | ||
implementation("io.grpc:grpc-stub:1.58.0") | ||
implementation("io.grpc:grpc-protobuf:1.58.0") | ||
implementation("io.grpc:grpc-kotlin-stub:1.4.0") { exclude("javax.annotation", "javax.annotation-api") } | ||
// This is needed to compile the @Generated annotation forced by the grpc compiler | ||
// See https://github.com/grpc/grpc-java/issues/9153 | ||
compileOnly("org.apache.tomcat:annotations-api:6.0.53") | ||
|
||
// To specify the coroutines dispatcher | ||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") | ||
|
||
// AWS Lambda-specific logging, see https://docs.aws.amazon.com/lambda/latest/dg/java-logging.html#java-logging-log4j2 | ||
val log4j2version = "2.22.0" | ||
implementation("org.apache.logging.log4j:log4j-core:$log4j2version") | ||
implementation("org.apache.logging.log4j:log4j-layout-template-json:$log4j2version") | ||
implementation("com.amazonaws:aws-lambda-java-log4j2:1.6.0") | ||
|
||
// Testing (optional) | ||
testImplementation("org.junit.jupiter:junit-jupiter:5.9.1") | ||
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3") | ||
testImplementation("dev.restate:sdk-testing:$restateVersion") | ||
} | ||
|
||
// Setup Java/Kotlin compiler target | ||
java { | ||
toolchain { | ||
languageVersion.set(JavaLanguageVersion.of(17)) | ||
} | ||
} | ||
|
||
// Configure protoc plugin | ||
protobuf { | ||
protoc { artifact = "com.google.protobuf:protoc:3.24.3" } | ||
|
||
plugins { | ||
id("grpc") { artifact = "io.grpc:protoc-gen-grpc-java:1.58.0" } | ||
id("grpckt") { artifact = "io.grpc:protoc-gen-grpc-kotlin:1.4.0:jdk8@jar" } | ||
} | ||
|
||
generateProtoTasks { | ||
all().forEach { | ||
// We need both java and kotlin codegen(s) because the kotlin protobuf/grpc codegen depends on the java ones | ||
it.plugins { | ||
id("grpc") | ||
id("grpckt") | ||
} | ||
it.builtins { | ||
java {} | ||
id("kotlin") | ||
} | ||
} | ||
} | ||
} | ||
|
||
// Configure shadowJar plugin to properly transform Log4j plugin configurations - needed for AWS Lambda logger | ||
tasks.withType<ShadowJar> { | ||
transform(Log4j2PluginsCacheFileTransformer::class.java) | ||
} | ||
|
||
// Configure test platform | ||
tasks.withType<Test> { | ||
useJUnitPlatform() | ||
} |
Binary file added
BIN
+61.9 KB
kotlin/hello-world-lambda-cdk/lambda/gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
7 changes: 7 additions & 0 deletions
7
kotlin/hello-world-lambda-cdk/lambda/gradle/wrapper/gradle-wrapper.properties
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
distributionBase=GRADLE_USER_HOME | ||
distributionPath=wrapper/dists | ||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip | ||
networkTimeout=10000 | ||
validateDistributionUrl=true | ||
zipStoreBase=GRADLE_USER_HOME | ||
zipStorePath=wrapper/dists |
Oops, something went wrong.