AWS CDKとKotlinでHelloWorld

AWS CDKで単純なlambdaを作成し、処理をKotlinで実装、AWS SAMでローカル実行を行うまでの作業メモ。
AWS CDKはTypescriptを利用します。
（諸々理由はありますがCDKはJava（Kotlin）だと記述が煩雑になる感じがしたので）
また、各種ツールのインストールは長くなるので割愛します。

## 環境
- macOS Mojave 10.14.6
- AWS CDK関連
  - AWS CDK 1.8.0
  - nodenv 1.3.0
  - Node.js 10.15.3
- AWS SAM関連
  - AWS SAM CLI 0.21.0
  - docker desktop 2.1.0.2 
  - pyenv 1.2.13
  - Python 3.7.4
- Kotlin関連
  - Kotlin 1.3.50
  - jenv 0.5.2
  - Java Corretto-8.222.10.1
  - Gradle 5.6.2

## 流れ
1. AWS CDKプロジェクト作成
1. Kotlinプロジェクト作成
1. Lambda 作成
1. CDK設定
1. ローカル起動

### AWS CDKプロジェクト作成
```bash
$ mkdir aws-lambda-kotlin && cd $_
$ nodenv local 10.15.3
$ cdk init --language typescript
Applying project template app for typescript
Initializing a new git repository...
Executing npm install...
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN aws-lambda-kotlin@0.1.0 No repository field.
npm WARN aws-lambda-kotlin@0.1.0 No license field.

# Useful commands

 * `npm run build`   compile typescript to js
 * `npm run watch`   watch for changes and compile
 * `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
```

### Kotlinプロジェクト作成
```bash
$ mkdir hello-world && cd $_
$ jenv local 1.8
$ gradle init --type kotlin-application --dsl kotlin --project-name hello-world --package foo.bar
```
kotlinのバージョンを1.3.50に変更

```kotlin:hello-world/build.gradle.kts
id("org.jetbrains.kotlin.jvm") version "1.3.50"
```

ライブラリ追加

```kotlin:hello-world/build.gradle.kts
implementation("com.amazonaws:aws-lambda-java-core:1.2.0")
implementation("com.amazonaws:aws-lambda-java-events:2.2.7")
implementation("com.amazonaws:aws-lambda-java-log4j2:1.0.0")
```

mainClassName変更（これは不要かも？）

```kotlin:hello-world/build.gradle.kts
mainClassName = "foo.bar.Handler"
```

デプロイパッケージ作成設定の追加

```kotlin:hello-world/build.gradle.kts
tasks {
    val buildZip by registering(Zip::class) {
        from(compileKotlin)
        from(processResources)
        into("lib") {
            from(configurations.compileClasspath)
        }
    }
    named("build") {
        dependsOn(buildZip)
    }
}
```

### Lambda 作成

Handlerクラス作成

```kotlin:hello-world/src/main/kotlin/foo/bar/Handler.kt
package foo.bar

import com.amazonaws.services.lambda.runtime.Context
import com.amazonaws.services.lambda.runtime.RequestHandler
import org.apache.logging.log4j.LogManager

data class Response(val body: String)

class Handler : RequestHandler<Map<String, Any>, Response> {
    companion object {
        private val log = LogManager.getLogger()
    }

    override fun handleRequest(input: Map<String, Any>, context: Context): Response {
        log.info("received : $input")
        return Response(input["param"]?.toString() ?: "empty")
    }
}
```

### CDK設定
依存追加

```bash
$ npm i @aws-cdk/aws-lambda @aws-cdk/aws-lambda-event-sources
```

### Stack修正

```typescript:lib/aws-lambda-kotlin-stack.ts
import cdk = require('@aws-cdk/core');
import { Function, Runtime, Code } from '@aws-cdk/aws-lambda';
import { Duration } from '@aws-cdk/core';

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

    const myFunction = new Function(this, 'hello-world-function', {
      functionName: 'hello-function',
      runtime: Runtime.JAVA_8,
      code: Code.asset('hello-world/build/distributions/hello-world.zip'),
      handler: 'foo.bar.Handler',
      timeout: Duration.seconds(10),
    });
  }
}
```

### ローカル起動

```
$ ./hello-world/gradlew -b ./hello-world/build.gradle.kts clean build
$ cdk synth --no-staging > template.yaml
$ sam local invoke `grep -B1 "AWS::Lambda::Function" template.yaml | head -n1 | sed -e 's/^ *//' -e 's/:$//'` --no-event
Invoking foo.bar.Handler (java8)
2019-09-11 17:29:33 Found credentials in shared credentials file: ~/.aws/credentials
Decompressing /Users/hoge/work/aws-lambda-kotlin/hello-world/build/distributions/hello-world.zip

Fetching lambci/lambda:java8 Docker container image...........................................
Mounting /private/var/folders/t7/mb1gz5z55y1dvsgkmyh22q580000gn/T/tmp1yczekpk as /var/task:ro,delegated inside runtime container
START RequestId: 23f83fd0-b396-439a-ac97-368e0fc4b353 Version: $LATEST
END RequestId: 23f83fd0-b396-439a-ac97-368e0fc4b353
REPORT RequestId: 23f83fd0-b396-439a-ac97-368e0fc4b353  Duration: 96.06 ms      Billed Duration: 100 ms Memory Size: 128 MB     Max Memory Used: 5 MB
2019-09-11 08:30:19 23f83fd0-b396-439a-ac97-368e0fc4b353 INFO  Handler:15 - received : {}


{"body":"empty"}
```