Rustに入門する〜１日目　SAMを使ってAWS Lambda上で動作させる〜

# 初めに


今年の正月くらいにRustの公式の入門みたいなページを読んだはいいけど何も作らず今日まで来てしまったので、
いい加減何か作ろうと思ったらちょうどいい題材があったのでそれで入門しようという感じ。
家のサーバで動かしてもいいですが `AWS Lambda` がコスト的にも管理的にも楽なのでこれでやります。
アプリはあんま作らないので割と初心気味

最終的に作るものですが、
このコロナのご時世で会社全体でリモートとなり、今まで雑にやってきたけどSlackのステータスはしっかり変更しようね！
...とはなったけどめんどくさいとか忘れてたとなるのでその辺りをいい感じにしてくれるものを作ろうと思います。

# 方針

ステータスの方針についてはまだ決まってないのもあるのでとりあえずですが、
１日一回業務開始時間に実行させ、
今日が土日か自分の終日予定に休み系のワードがあればステータスを休暇に変え、なければステータスなしに変更するものを作ります。
もしくは業務終了時間に固定で業務終了に切り替えるもの。

まず ` AWS Lambda` でRustで動かします。

# 公式のサンプルをAWS Lambda上で動かす

cargoはドキュメント読みながらちょっとだけ動かすときにインストールしてるので省略

https://docs.rs/lambda_runtime/0.2.1/lambda_runtime/

rustのlambda用のランタイムは上記っぽいので`cargo init`をして上記をコピペで動かしてみます。
コピペして出たエラーから必要そうなパッケージをざっくり調べて追加したらとりあえず以下でなんとかなりました。

```toml
[dependencies]
lambda_runtime = "*"
simple-error = "*"
serde_derive = "*"
serde = { version = "*", features = ["derive"] }
```


とりあえずこれで`cargo build`が成功したので`cargo run` をしてみます。

```
thread 'main' panicked at 'Could not find runtime API env var: environment variable not found', /Users/***/.cargo/registry/src/github.com-1ecc6299db9ec823/lambda_runtime_core-0.1.2/src/runtime.rs:79:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
```

ダメでした。
環境変数ガー問題なのでとりあえずLambda上なら動くだろうと信じて `AWS Lambda` にあげてみます。

AWSのブログ参考に...と思ってみたら動かすコードも含め色々必要なのが全部載ってました...

https://aws.amazon.com/jp/blogs/opensource/rust-runtime-for-aws-lambda/

とりあえずコードの内容はそのままに`cargo.toml` に上記ページの`[[bin]]`から３行を追加し、
その他 bashの内容を黙々と実行していきます。
じっくり読みはしてませんがとりあえず雰囲気とコマンドだけ動かせばいけるやろうの精神です。

どうやらカスタムランタイムでもSAMが利用できるみたいですが、
一旦動くかを確認したいので以下の部分まで進め、zipにしたものをlambdaにあげます。

```bash
 zip -j rust.zip ./target/x86_64-unknown-linux-musl/release/bootstrap
```
テストイベントをコピペしたらエラーを吐きましたが
単純にAWS側のサンプルとdocs.rsのサンプルの変数の違いなので以下に変更して実行

```json
{
  "first_name": "Fizz",
  "last_name": "Buzz"
}
```

動きました。
ちなみにこの状態でもローカルで実行したら環境変数の問題は起きて実行できませんでした。

```json
{
  "message": "Hello, Foo!"
}
```

# SAMで実行・デプロイする


この状態では環境変数をなんとかしないとローカルでは実行できず
毎回buildしてzipで固めてGUIでアップロードをとらないと確認できません。

環境変数をどうにかするのもいいですが、デプロイで楽をするためにもSAMでなんとかできるようにしましょう。

AWS CLIやSAMのインストールは今回できている環境なので省略します。

## テンプレートを作る

テンプレートについてですが、
少し調べたところカスタムランタイムは`Runtime: provided`と指定してやれば良いみたいです。

ここまでわかればあとは公式ドキュメントと睨めっこするだけです。
https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/sam-resource-function.html
後で足せばいいのでとりあえず以下で作りました。

```yaml:template.yaml
AWSTemplateFormatVersion : '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Resources:
  ChangeSlackStatusProject:
    Type: "AWS::Serverless::Function"
    Properties:
      FunctionName: ChangeSlackStatus
      Handler: main
      Runtime: provided
      CodeUri: ./rust.zip
```


## ビルドする

### 失敗

とりあえずbuildしてみましたが、providedはサポートしていないって言われました。
上の方で参考にしたAWSのブログにはSAMテンプレート使って `sam build` できるよ！って書いてあったので、
おそらく何か方法はあるはずです。
最悪 `zip` で固めればできるのと、一旦実行デプロイまでをみたいので後日もしくはこの記事の投稿後に頑張ろうと思います。

```
$ sam build
Building resource 'ChangeSlackStatus'

Build Failed
Error: 'provided' runtime is not supported
```

# デプロイする

とりあえずソースコードは先ほど手動で作ったものがあるのでそれを使ってデプロイします。
お手軽に`--guided`を使ってデプロイします。

```
$ sam deploy --guided

Configuring SAM deploy
======================

	Looking for samconfig.toml :  Not found

	Setting default arguments for 'sam deploy'
	=========================================
	Stack Name [sam-app]: ChangeSlackStatusProject
	AWS Region [us-east-1]: ap-northeast-1
	#Shows you resources changes to be deployed and require a 'Y' to initiate deploy
	Confirm changes before deploy [y/N]: y
	#SAM needs permission to be able to create roles to connect to the resources in your template
	Allow SAM CLI IAM role creation [Y/n]: Y
	Save arguments to samconfig.toml [Y/n]: Y

（中略）

CloudFormation events from changeset
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ResourceStatus                                                      ResourceType                                                        LogicalResourceId                                                   ResourceStatusReason
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
CREATE_IN_PROGRESS                                                  AWS::IAM::Role                                                      ChangeSlackStatusProjectRole                                        -
CREATE_IN_PROGRESS                                                  AWS::IAM::Role                                                      ChangeSlackStatusProjectRole                                        Resource creation Initiated
CREATE_COMPLETE                                                     AWS::IAM::Role                                                      ChangeSlackStatusProjectRole                                        -
CREATE_IN_PROGRESS                                                  AWS::Lambda::Function                                               ChangeSlackStatusProject                                            -
CREATE_IN_PROGRESS                                                  AWS::Lambda::Function                                               ChangeSlackStatusProject                                            Resource creation Initiated
CREATE_COMPLETE                                                     AWS::Lambda::Function                                               ChangeSlackStatusProject                                            -
CREATE_COMPLETE                                                     AWS::CloudFormation::Stack                                          ChangeSlackStatusProject                                            -
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Successfully created/updated stack - ChangeSlackStatusProject in ap-northeast-1
```

無事できました。
テスト用のイベントを作り忘れたので(そもそもSAMでできるのか？）
最初の手動デプロイと同じ方法でテストイベントを作り実行しましたが、無事に実行することができました。


## ローカルで実行する


さて、順番が逆になってしまいましたが、ローカルで実行します。


とりあえずこのまま`sam local invoke`をしてもイベントがないので当然のようにエラーが出ます。

```
{"errorType":"JsonError","errorMessage":"JsonError: missing field `first_name` at line 1 column 2"}
```

なので、event用のjsonを作成しましょう。

```event.json
{
    "first_name": "Fizz",
    "last_name": "Buzz"
}
```

あとは`-e`で作ったイベントファイルを指定して実行するだけです。

```bash
$ sam local invoke -e event.json
Invoking main (provided)
Decompressing /xxxxx/StatusByClaneder/rust.zip
う
Fetching lambci/lambda:provided Docker container image......
Mounting /private/var/folders/lj/tt21hqgj7s3f0t3bgghmllkm0000gn/T/tmp0ob6vo6n as /var/task:ro,delegated inside runtime container
START RequestId: fe10b830-cbfa-1b9d-6b74-464b57a38f44 Version: $LATEST
END RequestId: fe10b830-cbfa-1b9d-6b74-464b57a38f44
REPORT RequestId: fe10b830-cbfa-1b9d-6b74-464b57a38f44	Init Duration: 97.80 ms	Duration: 4.03 ms	Billed Duration: 100 ms	Memory Size: 128 MB	Max Memory Used: 10 MB

{"message":"Hello, Fizz!"}
```

無事にローカルでも実行できました。


# １日目を終えて

書き終えて気づきましたが
今日は実行基盤を整えるところで終わりとなり、肝心のRustコードはコピペだけとなりました。

とはいえ`Cargo.toml`を少し書いたり、コードを少し読んだり、
(Lambdaの話ですが)カスタムランタイムのデプロイができたり学びはそれなりにありました。

終わったものを後で書いているわけではなく、なんだったら記事を書きながら試していたので、
２日目はいつになることなのか...それどころかやる気が消滅し２日目なんてなかったことになるかもしれません。

こうやって書いていくこと脅迫観念を作っているのでなんとか頑張って完成まで目指そうと思います。

２日目〜はGoogleカレンダーから情報を取るところまでなんとかする予定です。
実際は切りのいいところまで行ったら投稿に予定です。