Cloud Build上でgoapp testを実行する

# tl;dr
- GAE/GoのCI/CDでCloud Buildを使う
- `aetest`を使ったtestを実行するため、`goapp test`をCloud Build上で実行したい
- 最新のGCP SDKではgoappを簡単には使えなくなっているので、`gcr.io/cloud-builders/gcloud`を使う場合ひと工夫必要である

# goapp test

GAE/Goで`appengine.NewContext`に依存したtestを実行するためには、[aetest](https://cloud.google.com/appengine/docs/standard/go/tools/localunittesting/reference)が必要になります。
`aetest`を使ったtestを実行するためには[goapp testが必要](https://cloud.google.com/appengine/docs/standard/go/tools/localunittesting/)となります。

# Cloud Build上のGCP SDK

CI/CDを[Cloud Build](https://cloud.google.com/cloud-build/)で行います。
ほぼGithubのprivate repositoryであってもほぼ無料で使用できるのと、GAEへのdeployなどIAMで権限管理できるのでGCP上にアプリケーションを構築するのであれば使わない理由はないといえます。

Cloud BuildでGCP SDKを使ったbuild pipelineを構築する場合、[gcr.io/cloud-builders/gcloud](https://github.com/GoogleCloudPlatform/cloud-builders/tree/master/gcloud)を使うことになると思います。
これは割と最新のGCP SDKがinstallされたimageになります。
ただ最近のSDKだと`app-engine-go` componentをinstallしていても、`goapp`は簡単には使えないようになっています。
これは`gcr.io/cloud-builders/gcloud`であっても同様で、`goapp`自体存在はしているものの、`PATH`が通ってなかったり、実行フラグが立っていないので使うにはひと手間必要となります。

# goappのpathを調べる
`gcr.io/cloud-builders/gcloud`上でgoappがどこに存在しているのかを調べるために、`gcr.io/cloud-builders/gcloud`を手元にpullしてきて入ってみます。

```sh
$ docker run --rm --name gcloud --entrypoint bash -it gcr.io/cloud-builders/gcloud
root@99f4e01f618f:/# which gcloud
/builder/google-cloud-sdk/bin//gcloud
root@99f4e01f618f:/# ls -l /builder/google-cloud-sdk/platform/google_appengine/ |grep goapp
-rw-r--r--  1 root root    4798 Jul 14 03:17 goapp
```

ありました、`/builder/google-cloud-sdk/platform/google_appengine/goapp`です。
ただ実行権限はついてませんね。
まあ`goapp`自体はただのpythonスクリプトなので、python経由で叩けば問題なしです。

# cloudbuild.yamlに記述

これまで調べた内容を元に以下[^1]のような感じになります。

```yaml
steps:
  - name: gcr.io/cloud-builders/gcloud
    env:
      - 'GOPATH=/workspace/gopath'
    entrypoint: python
    args: ['/builder/google-cloud-sdk/platform/google_appengine/goapp', 'test', './app']
    id: goapp-test
```

[^1]: 別途`goapp`に実行権限をつけて、`PATH`も通したimageを用意してGCRにpushしておけば簡単に使えますが、`gcr.io/cloud-builders`はCloud Build上でcacheされていて高速に使えるので、`gcr.io/cloud-builders/gcloud`を使います。

`GOPATH`を設定しているのは、`gcloud deploy`でGAEのdeployをするために`GOPATH`をいじる[^2]からです。
[^2]: [GAE/Go \(\+dep\) で gcloud app deploy する為の構成](https://qiita.com/su_aska/items/81b983ac662691cc97b5)を参考にしてます。事前にsymlinkを作ってあげる必要があります。

またいまやっているプロジェクトではGAEからFirestoreとCloud Storageにアクセスしており、testでもこれらのAPIを使う[^3]ようにしています。そのため、`serviceAccountKey.json`を渡してあげる必要があります。
[^3]: [google\-cloud\-go\-testing](https://github.com/GoogleCloudPlatform/google-cloud-go-testing)にFirestoreのmockはないのと、Cloud Storageのmockも使いにくかったので。。。

KMSで暗号化した`serviceAccountKey.json`を事前に`serviceAccountKey.json.enc`などとしてrepositoryにaddしておきます。
これをCloud Build上でdecryptしてtestで使うようにします。

```yaml
steps:
  - name: gcr.io/cloud-builders/gcloud
    args:
      - kms
      - decrypt
      - --ciphertext-file=secrets/serviceAccountKey.json.enc
      - --plaintext-file=secrets/serviceAccountKey.json
      - --location=global
      - --keyring=demo-keyring
      - --key=demo-key
    id: decrypt-service-account
    waitFor: ['-']
  - name: gcr.io/cloud-builders/gcloud
    env:
      - 'GOPATH=/workspace/gopath'
      - 'GOOGLE_APPLICATION_CREDENTIALS=/workspace/secrets/serviceAccountKey.json'
    entrypoint: python
    args: ['/builder/google-cloud-sdk/platform/google_appengine/goapp', 'test', './app']
    waitFor: ['decrypt-service-account']
    id: goapp-test
```

事前に`gcloud kms`[^4]でrepository上の`serviceAccountKey.json.enc`をdecrypt[^5]しておきます。
[^4]: `keyring`, `key`は適当です
[^5]: 事前にIAMでCloud Build用のサービスアカウントにKMSの権限をつけておく必要があります

decryptした`serviceAccountKey.json`は`GOOGLE_APPLICATION_CREDENTIALS`で指定することで[cloud.google.com/go](https://godoc.org/cloud.google.com/go)[^6]から透過的に使えるようになります。
[^6]: [Getting Started with Authentication](https://cloud.google.com/docs/authentication/getting-started)を参照