[メモ] 開発環境でAWS Cloudwatch Syntheticsっぽいスクリプトを実行したい

# はじめに
フルスタックなJavaエンジニアでした。訳あってPHPerをやっています。

AWSでCIないのかなーと思っていたら、 `Cloudwatch Synthetics Canaries` というものがあったので、チャレンジ中です。

詳しいことを理解せず構築しているので、ふーん、位で見てもらえたら嬉しいです。
不定期に更新するかもです。

# 何がしたい？
Canariesで作って動かして、失敗して、を繰り返しすぎて嫌になってきた（特にお金とデプロイ時間が）ので、ローカルでなんとかならんかなーと試したメモです

# このメモが読めそうな人
- Dockerわかる
- Puppeteerに触れている

# 前提、環境
- Puppeteer `4.0`
- Node.js `18.16.0`
- Docker `Docker version 23.0.4, build f480fb1`

# 準備
## ディレクトリ構成
```
./
|- Dockerfile
|- scripts
|    |- modules
|    |    `- wrapper
|    |        `- aws
|    |            `- synthetics.js
|    |- package.json
|    |- sample.js
|    `- script.js
`- outputs
```

`modules` は、独自関数？的なものを置いていきます。
`Synthetics` の `executeStep` を ~~Wrapper~~ Mock として定義しています

`outputs` にスクショを出します。
実行ログとかも出せると思いますが、未トライなのでいずれメモを追加します。いずれ。たぶん、きっと。

## Dockerfile
```Dockerfile
FROM node:18.16.0-slim

WORKDIR /app
COPY ./scripts/package.json /app/package.json

WORKDIR /app
RUN apt-get update \
&&  apt-get upgrade \
&&  apt-get install -y \
        libappindicator1 \
        fonts-liberation \
        chromium \
&&  npm install --force

ENV NO_SANDBOX=true \
    PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true \
    PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium

CMD ["script.js"]
```

このまま `docker run` すると、`script.js` が実行されます

## package.json
AWSに合わせて？？ puppeteer `4.0` をインストールします。
間違ってる気がとてもとてもする・・・(2023/05/11時点で、puppeteerの最新が `20.1.2` なので。。。)

```json
{
  "dependencies": {
    "puppeteer": "4.0"
  }
}
```

## synthetics.js
```js
let page;
let output_dir = '/output/';

exports.setPage = async function (val) {
    page = val;
}

exports.setOutputDir = async function (val) {
    output_dir = val;
}

exports.executeStep = async function (label, callback) {
    await page.screenshot({ path: output_dir + label + "_" + (new Date()).getTime() + ".png", fullPath: true });

    await callback();

    await page.screenshot({ path: output_dir + label  + "_" + (new Date()).getTime() + ".png", fullPath: true });
}
```
スクリーンショットの保存先は、 `/output/` で、ホストの `./outputs/` に吐き出されます。
一応、ファイルが重複しないように、ファイル名の後ろに、ミリ秒をくっつけています。
重複したらごめんね。

## sample.js
ほかサイトに記載されていたサンプルをいじっています。著作権、ごめん。
```js
const puppeteer = require('puppeteer');
const synthetics = require('./modules/wrapper/aws/synthetics');

(async () => {
  const browser = await puppeteer.launch({
    args: [
      '--no-sandbox',
      '--disable-setuid-sandbox'
    ]
  });
  const page = await browser.newPage();
  synthetics.setPage(page);

  await synthetics.executeStep('jikken', async function (timeoutInMillis = 30000) {
    await page.goto('https://example.com');
    await page.screenshot({ path: '/output/example.png', fullPath: true });
  });
  
  browser.close();
})();
```

`synthetics.executeStep` を実行すると、スクリプト前後でスクリーンショットを撮ります。
サンプルの例だと、スクリプト内でも `example.png` のスクショを撮っているので、 `./outputs` 配下に3枚の画像が出てきますね。

AWSと異なるところは、処理の実行の仕方と、 `page` の作り方・設定の所です。
本物のスクリプトと比較して、いい感じに環境別に動くようにすると、開発環境でもCanaries上でも同じスクリプトで動かせるかなーと思います。

# 実行
```cmd
docker build -t puppeteer --no-cache ./
```

```cmd
docker run --rm -it \
-v ./scripts:/app \
-v ./outputs:/output \
-w /app \
puppeteer
```

`script.js` 以外を実行する時は
```
docker run --rm -it \
-v ./scripts:/app \
-v ./outputs:/output \
-w /app \
puppeteer \
sample.js
```
の様に、container namerの後ろに、work dir配下にある（多分、相対パスでもOKだと思いますが）スクリプト名をつければOKです