Lambdaのcustom runtimeとALBのLambdaサポートが来たので組み合わせてみる

re:invent2018の発表みなさんご覧になりましたでしょうか。
個人的に面白い事出来そうだなーと思ったのがLambdaに関わる発表でした。

- [ALBのlambdaサポート](https://aws.amazon.com/jp/about-aws/whats-new/2018/11/alb-can-now-invoke-lambda-functions-to-serve-https-requests/)
- [lambdaのcustom runtime](https://aws.amazon.com/jp/about-aws/whats-new/2018/11/aws-lambda-now-supports-custom-runtimes-and-layers/)

---

### ALBのlambdaサポート

- http(s)リクエストをイベントとして動くわけで、じゃあハンドラーのevent objectに何が入ってくるんじゃという話ですが、Nodeで受け取って単純に表示してみると以下のようになります。

```json
{
   "requestContext":{
      "elb":{
         "targetGroupArn":"arn:aws:elasticloadbalancing:{himitsu}"
      }
   },
   "httpMethod":"GET",
   "path":"/{albのpath}",
   "queryStringParameters":{},
   "headers":{
      "accept":"*/*",
      "host":"{albのhost名}",
      "user-agent":"curl/7.54.0",
      "x-amzn-trace-id":"{id}",
      "x-forwarded-for":"{ip.ip.ip.ip}",
      "x-forwarded-port":"80",
      "x-forwarded-proto":"http"
   },
   "body":"",
   "isBase64Encoded":false
}
```

- なるほどヘッダー情報やquery stringまでいい感じにパースして渡してくれるようです。
- 以下のようにstatusCodeやstatusDescriptionを詰めてobjectを返せばそれがresponseとなります。

```
{
   statusCode: 200,
   statusDescription: "200 OK",
   isBase64Encoded: false,
   headers: {
       Content-Type: "application/json; charset=utf-8"
    },
    body:""
}
```

### lambdaのcustom runtime

- 任意のruntimeを作れるよという話です。シェルスクリプトでの実装例がドキュメントに上がっています([link](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/runtimes-walkthrough.html))。bootstrapという実行可能ファイルが必要なのですね。
- 今回はRustで実装したかったのですが、awslabにもうmacro化したものがリファレンス実装として上がっていました([link](https://github.com/awslabs/aws-lambda-rust-runtime/blob/d117272fc535265851c9eb028179475ef87e20cb/lambda-runtime/src/runtime.rs#L200))。コードを見て頂ければ分かるようにループでイベントを待ち受け、eventオブジェクトを登録されたコールバックファンクション(これがまさしくlambdaのhandlerにあたる物)に渡して実行しています。こちらを使わせてもらいましょう。

---

### ALBとcustom runtimeのlambdaを組み合わせる

- コード例

```rust
#[macro_use]
extern crate lambda_runtime as lambda;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate log;
extern crate simple_logger;

use lambda::error::HandlerError;
use std::error::Error;

#[derive(Deserialize, Clone)]
struct CustomEvent {
  #[serde(rename = "requestContext")]
  request_context: Elb,
  #[serde(rename = "httpMethod")]
  http_method: String,
  path: String,
  #[serde(rename = "queryStringParameters")]
  query_string_parameters:QueryStringParameters
}

#[derive(Serialize, Clone)]
struct Response {
  #[serde(rename = "statusCode")]
  status_code: i64,
  #[serde(rename = "statusDescription")]
  status_description: String,
  #[serde(rename = "isBase64Encoded")]
  is_base64encoded: bool,
  headers: ContentType,
  body: String,
}

#[derive(Deserialize, Clone)]
struct Elb { elb: TargetGroupArn }
#[derive(Deserialize, Clone)]
struct TargetGroupArn {
  #[serde(rename = "targetGroupArn")]
  target_group_arn: String
}
#[derive(Deserialize, Clone)]
struct QueryStringParameters {
  name: String
}
#[derive(Serialize, Clone)]
struct ContentType {
  content_type: String
}

fn main() -> Result<(), Box<dyn Error>> {
  simple_logger::init_with_level(log::Level::Info)?;
  lambda!(my_handler);

  Ok(())
}

fn my_handler(e: CustomEvent, c: lambda::Context) -> Result<Response, HandlerError> {
  if e.http_method == "" {
    error!("Empty http_method in request {}", c.aws_request_id);
    return Err(c.new_error("Empty"));
  }

  Ok(Response {
    status_code: 200,
    status_description: "200 OK".to_string(),
    is_base64encoded: false,
    headers: ContentType {
      content_type: "application/json".to_string()
    },
    body: format!("Rust function invoked! Hello, {}\n", e.query_string_parameters.name)
  })
}
```

- requestイベントのeventオブジェクトの形は最初に書いたとおりだろうなーという事で、それをマッピング出来るように構造体を作ってみました。
- 以下をcargo.tomlに追記すればbootstrapという名前で実行可能なバイナリーを作成出来ますので、zipで固めてlambda関数としてアップロードします。

```toml
[[bin]]
name = "bootstrap"
path = "src/main.rs"
```

- あとはALBのターゲット設定でこの関数を指せばOKですね。

![lambda2.png](https://qiita-image-store.s3.amazonaws.com/0/50797/6c320d35-aa02-7ef8-68f5-11c8337f06df.png)


![lambda1.png](https://qiita-image-store.s3.amazonaws.com/0/50797/fb3e68f8-7d70-e8e9-792c-72e5622384b7.png)

---

### リクエストしてみる

![lambda3.png](https://qiita-image-store.s3.amazonaws.com/0/50797/e8d3b922-2e57-6f4f-9250-9caaa95a8728.png)

＼(^o^)／

---

### 感想

- custom runtimeでも問題なくALB連携出来ますね！
- 安定したruntimeがライブラリ化されれば一気にユースケースが広がりそうで、面白くなりそうです。