SwaggerでLambdaのデバッグ環境を作る(7)：AWS S3トリガをデバッグする

久しぶりの、SwaggerでLambdaのデバッグ環境を作る の拡張です。
　第一回の投稿はこちらから：[SwaggerでLambdaのデバッグ環境を作る(1)](https://qiita.com/poruruba/items/e3a46787c79517a815f9)

AWS S3にファイルがアップロードされたときにLambdaを起動するスクリプトを書こうと思っても、Lambda上でのデバッグは大変なので、ローカルでデバッグ(実行中のブレイクを入れたり、変数を参照したり)できるようにします。

S3は、本物ではなく、S3互換のMinIOを使います。

今回のローカルデバッグ環境は以下のGitHubに反映済みです。

poruruba/swagger_template
　https://github.com/poruruba/swagger_template

#MinIOのセットアップ

ほぼこちらに書いてある通りに進めます。

MinIO Quickstart Guide
　https://docs.min.io/

今回は、Distributed MinIO Quickstart Guideを採用しています。

まずは、wgetでバイナリファイルをダウンロードして、/usr/local/binにコピーします。

```
> wget https://dl.min.io/server/minio/release/linux-amd64/minio
> chmod +x minio
> cp minio /usr/local/bin

> wget https://dl.min.io/client/mc/release/linux-amd64/mc
> chmod +x mc
> cp mc /usr/local/bin
```

minioがMinIO本体です。mcはMinIO Clientと呼ばれるもので、MinIOサーバの設定をしたり、ファイルをアップしたりするクライアントツールです。

次にMinIO用のフォルダを作成します。

```
> mkdir /opt/minio
> mkdir /opt/minio/conf
> mkdir /opt/minio/data
```

次に、自動起動用の設定をします。systemdを使います。

``` vi /etc/systemd/system/minio.service ```

```/etc/systemd/system/minio.service 
[Unit]
Description=minio Object Storage Service
After=network.target

[Service]
Type=simple
EnvironmentFile=/opt/minio/conf/env.conf
ExecStart=/usr/local/bin/minio server /opt/minio/data
Restart=on-abort
WorkingDirectory=/opt/minio/

[Install]
WantedBy=multi-user.target
```

MinIOを起動するには、以下の環境変数を指定する必要があります。

```vi /opt/minio/conf/env.conf```

```/opt/minio/conf/env.conf
MINIO_ACCESS_KEY=admin
MINIO_SECRET_KEY=[ブラウザ用パスワード]
```

上記の[ブラウザ用パスワード]を秘匿の値に書き換えてください。後で使います。

ちなみに、また、もしいったん上記で稼働させた後変更したい場合には以下のように書き換えて、MinIOを再起動します。

```vi /opt/minio/conf/env.conf```

```/opt/minio/conf/env.conf
MINIO_ACCESS_KEY=admin
MINIO_SECRET_KEY=[新ブラウザ用パスワード]
MINIO_ACCESS_KEY_OLD=admin
MINIO_SECRET_KEY_OLD=[旧ブラウザ用パスワード]
```

これで準備ができました。以下の通りに実行します。

```
> systemctl daemon-reload
> systemctl start minio
```

daemon-reloadは、minio.serviceを変更した場合にのみ必要です。
起動できたかどうかは以下で確認します。

```
> systemctl status minio
● minio.service - minio Object Storage Service
   Loaded: loaded (/etc/systemd/system/minio.service; enabled; vendor preset: enabled)
   Active: active (running) since Tue 2020-07-28 00:16:07 JST; 4s ago
 Main PID: 29514 (minio)
    Tasks: 13 (limit: 4264)
   CGroup: /system.slice/minio.service
           mq29514 /usr/local/bin/minio server /opt/minio/data
・・・
```

PC再起動でも自動起動させたい場合は以下を実行します。

```
> systemctl enable minio
```

最後に、mc(MinIO用クライアントツール)から操作できるように、このMinIOサーバの情報を登録します。

```
> mc config host add [MinIOの名前] http://[MinIOのホスト名]:9000 admin [ブラウザ用パスワード]
> mc admin config set [MinIOの名前] region name="[リージョン名]"
> systemctl restart minio
```

[MinIOの名前]は覚えやすい適当な名前を付けてください。FQDNである必要はありません。
リージョン名も適当に入れてください。本物の場合の「ap-northeast-1」に相当します。

#触ってみる

まずは、ブラウザで覗いてみましょう。ポート番号は9000です。

　http://[MinIOのホスト名]:9000

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/80d0fd91-5c32-4025-5ba4-cb242adf9c61.png)

Access KeyにMINIO_ACCESS_KEYに指定したadminを、Secret KeyにMINIO_SECRET_KEYに指定した[ブラウザ用パスワード]を指定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/a060a541-e6dd-439f-ee44-66c43c4fc7ec.png)

さっそく、バケットを作ってみます。
右下の＋ボタン→ Create bucket を押して出てくる小さなダイアログボックスで、任意のバケット名を入れると、バケットが出来上がります。
適当に、「testbucket」という名前のバケットを作っておきます。

今度は、Node.jsで、AWS SDKを使ってファイルをアップロードしたりダウンロードしたりしてみましょう。

```
> mkdir s3_test
> cd s3_test
> npm init -y
> npm install aws-sdk
```

```vi index.js```

```js:index.js
var AWS = require('aws-sdk');
var s3  = new AWS.S3({
          accessKeyId: 'admin' ,
          secretAccessKey: '[ブラウザ用パスワード]',
          endpoint: 'http://[MinIOのホスト名]:9000',
          s3ForcePathStyle: true, // needed with minio?
          signatureVersion: 'v4'
});

const BucketName = '[バケット名]';
const FileName = 'test.txt';

// putObject operation.

var put_params = {
	Bucket: BucketName,
	Key: FileName,
	Body: 'Hello from MinIO!!'
};

s3.putObject(put_params, (err, data) => {
	if (err){
		console.log(err)
		return;
	}

	console.log('Successfully uploaded data to ' + BucketName + '/' + FileName);

// getObject operation

	const fs = require('fs');

	var fileStream = fs.createWriteStream(FileName);
	var get_params = {
		Bucket: BucketName,
		Key: FileName
	};

	var s3Stream = s3.getObject(get_params).createReadStream();
	s3Stream.on('error', function(err) {
		console.error("s3Stream Error:",  err);
	});
	s3Stream.pipe(fileStream).on('error', (err) => {
	    console.error('fileStream Error:', err);
	}).on('close', () => {
	    console.log('Done.');
	});
});
```

実行します。

```
> node index.js
Successfully uploaded data to testbucket/test.txt
Done.
```

ブラウザを覗くと、test.txtがアップロードされているのがわかるかと思います。

#ローカルデバッグ環境の準備

今度は、MinIOにファイルがアップされたらローカルデバッグ環境のNode.jsが起動するようにします。要は、AWSのS3にファイルがアップされたらLambdaを起動させることのエミュレートです。

```api\controllers\``` に以下のファイルを配置します。

```js:head.js
'use strict';

/*
  append to swagger.yaml

    head:
      x-swagger-router-controller: head
      operationId: s3
      responses:
        200:
          description: Success
*/

function s3(req, res) {
  console.log("[HEAD] head_s3 called");
  res.json({});
}

module.exports = {
  s3: s3
};
```

次に、通知を受けたいエンドポイントのHEADメソッドに、このファイルが呼ばれるように```api\swagger\swagger.yaml```を編集します。
以下は、/test-s3 という[エンドポイント名]に通知が来るようにする例です。

```yaml:api\swagger\swagger.yaml
・・・
  /test-s3:
    head:
      x-swagger-router-controller: head
      operationId: s3
      responses:
        200:
          description: Success
・・・
```

ローカルのデバッグ環境を立ち上げます。
次に、この通知を受けるサーバのエンドポイントをMinIOサーバに登録します。

```
> mc admin config set [MinIOの名前] notify_webhook:1 queue_limit="0" endpoint="[通知を受けるサーバのURL]/[エンドポイント名]" queue_dir=""
> systemctl restart minio
> mc event add [MinIOの名前]/[バケット名] arn:minio:sqs::1:webhook --event put --suffix .[拡張子名]
```

今回は拡張子として、.txtとします。
上記を設定すると、通知を受け付けるサーバに、以下のようにHEADメソッドが飛んできているのがわかります。

```
[HEAD] head_s3 called
[HEAD] head_s3 called
```

次に、実際にトリガを受け付けるLambdaに相当するNode.jsを作成します。

まずは、swagger.yamlを以下のように編集します。

```api\swagger\swagger.yaml
・・・
  /test-s3:
    post:
      x-swagger-router-controller: routing
      operationId: test-s3
      parameters:
        - in: body
          name: body
          schema:
            $ref: "#/definitions/CommonRequest"
      responses:
        200:
          description: Success
          schema:
            $ref: "#/definitions/CommonResponse"
    head:
      x-swagger-router-controller: head
      operationId: s3
      responses:
        200:
          description: Success
・・・
```

headはさきほど追加したので、今回は実際のPOSTメソッドを宣言しています。
Node.jsのソースは以下の感じです。これが今回デバッグしたかった主役のソースファイルです。

```js:api/controllers/test-s3/index.js
'use strict';

exports.handler = async (event, context, callback) => {
	console.log(event);
};
```

function.jsに宣言も追記しておきます。

```js:api\controllers\functions.js
・・・
const alexa_table = {
//  "test-alexa" : require('./lambda/test_alexa').handler,
//  "test-clova": require('./test-clova').handler,
//  "test-s3": require('./test-s3').handler,
"test-s3": require('./test-s3').handler,
};
```

ローカル実行環境を再起動して、もう一度ファイルをアップロードしてみましょう

```
> node index.js
```

そうすると、こんな感じで、トリガを受け取れました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/259bed89-00d8-6a60-8efc-b09081bb8dab.png)

もちろん、Node.jsのソースファイルにブレイクを入れることもできます。

#よく使うMCコマンド

```
mc config host list
mc config host add [ホスト名] http://[ホストのFQDN]:9000 admin [ブラウザ用パスワード]

mc admin config get [ホスト名] region
mc admin config set [ホスト名] region="[リージョン名]"

mc admin config get [ホスト名] notify_webhook
mc admin config set [ホスト名] notify_webhook:[イベント名] endpoint="[トリガ先URL]" queue_limit="0"

mc event list [ホスト名]/[バケット名]
mc event add [ホスト名]/[バケット名] ARN --event put --prefix [フォルダ名]/ --suffix .[拡張子]

ARN
arn:minio:sqs:[リージョン名]:[イベント名]:webhook
```

以上