CircleCIでGo1.19とgcoudコマンドを使用できる環境を作成する方法

## 概要
GoのAPIテストをCircleCIと連携する際に詰まったので備忘録として投稿します。

### 要件
今回の用件として、GCPのサービスアカウントを使用すること、Goのバージョンを1.17以上にすることがあります。
また、GCPのサービスアカウントを使用する際に`gcloud`コマンドを使用するので、Go1.17以上 + gcoudコマンドを使用できる環境が必要です。（厳密には[こちらのコマンド](https://cloud.google.com/sdk/docs/authorizing?hl=ja#authorize_with_a_service_account)を使用したいと思っています）
筆者が調べた限りでは、Go1.17以上で`gcloud`コマンドを使用できるDocker imageは見つからなかったので自作で環境を構築する必要がありそうでした。

###### GCPのサービスアカウントを使用する理由
テスト実行時にGCPと連携する必要があるためです。
###### GOのバージョンが1.17以上である理由
実装の都合上なのでCircleCIとの連携に深く関係はしていません。

## 実装方法の検討
まず初めに、どの様にCircleCIと連携させるかを検討して以下の3つの方法を検討・調査しました。
① GCPの[Container Registry](https://cloud.google.com/container-registry?hl=ja)を使用して実行環境を作成する。
② Docker Hubを使用して実行環境を作成する。
③ GCPのSDK imageを使用して実行環境を作成して、その後のjobでGoをinstallする。

## ① GCPの[Container Registry](https://cloud.google.com/container-registry?hl=ja)を使用して実行環境を作成する

GCPのContainer Registry（以下からGCRと略す）からimageをpullして実行環境を作成する方法です。
この機能以外にもGCRを使用していたため一元管理できる点が魅力でした。
CircleCIのコードのイメージとしては以下の様になります。

```yml
version: 2.1

executors:
  defaults: &defaults
    working_directory: [任意のディレクトリ]
    docker:
      - image: [GCRのimage]
```
しかし、GCRのimageをpullして実行環境を作成するにはGCPのアカウントの認証情報が必要なようで以下の様なエラーになります。

```
Error response from daemon: unauthorized: You don't have the needed permissions to perform this operation, and you may have invalid credentials. To authenticate your request, follow the steps in: https://cloud.google.com/container-registry/docs/advanced-authentication
```
最初に別のimageをコンテナ化して、そのコンテナに認証情報を登録してimageをpullできればできそうでしたが、今回は別の方法を模索することにしました。

## ② Docker Hubを使用して実行環境を作成する
①とほぼ同様の方法で、Docker imageの格納先をDocker Hubに変更します。
しかし、他の機能でのimageの管理はGCRで行なっており、新たにDocker Hubを使用すると管理するコストや手間が増えるのでまた別の方法を模索することになりました。

## ③ GCPのSDK imageを使用して実行環境を作成して、その後job移行の過程でGoをinstallする。
結論としてこちらの対策を取りました。
[google/cloud-sdk](https://hub.docker.com/r/google/cloud-sdk)をpullして環境を構築し、その後Goの1.19をcurlコマンドでインストールします。
これで、今回の要件である「GCPのサービスアカウントを使用すること」「Goのバージョンを1.7以上にすること」を満たすことができます。

### 実装
それでは、どの様に実装していったのかを記載していきます。

executorsで実行環境を定義しています。
今回は複数のexecutorがある中で追加する形だったので`gcloud-golang`という名称の実行環境を追加しました。
```config.yml
executors:
  gcloud-golang:
    <<: *defaults
    docker:
      - image: google/cloud-sdk:412.0.0
    environment:
      - GOPATH: /go
      - PATH: /usr/local/go/bin:/go/bin:/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/google-cloud-sdk/bin/
```
Goをインストールするコマンドです。
```config.yml
commands:
  go-install:
    steps:
      - run:
          command: |
            curl -Lso go.tar.gz "https://dl.google.com/go/go1.19.linux-amd64.tar.gz"
            tar -C /usr/local -xzf go.tar.gz
            rm go.tar.gz
```
executorsで定義した実行環境を指定し、Goをインストールするコマンドを打ち込みます。
次に`checkout`します。

```config.yml
jobs:
  build:
    executor:
      name: gcloud-golang
    steps:
      - go-install
      - checkout
```
上記の手順で今回の要件を満たすことができました。

# おまけ
今回初めてGCRにimageを登録したので備忘録として手順を記載します。
私の環境はmacOS Montereyを使用しています。


### Docker build
[こちらの記事](https://qiita.com/tktcorporation/items/956f4afd68d491fb6894)を参考にして以下のようなDockerfileを作成します。

```dockerfile
FROM google/cloud-sdk:412.0.0

ENV GOPATH /go
ENV PATH $GOPATH/bin:/usr/local/bin:$PATH

ENV GOLANG_VERSION 1.19

RUN curl -Lso go.tar.gz "https://dl.google.com/go/go${GOLANG_VERSION}.linux-amd64.tar.gz" \
    && tar -C /usr/local -xzf go.tar.gz \
    && rm go.tar.gz

ENV PATH /usr/local/go/bin:$PATH
```

Dockerfileをbuildしてimageを作成します。
その後に作成したimageを起動してログインします。

```
docker run -it [image ID] /bin/bash
```

### コンテナ
コンテナにログイン後にgcloudコマンドでログインします。
ブラウザへのリンクが表示されるので、任意のブラウザリンクを打ち込んでGCPのアカウントで認証します。

```terminal
# gcloud auth login

Go to the following link in your browser:

    https://~省略~

Enter authorization code: ~ブラウザで確認したコードを入力する。

You are now logged in as [GCPのアカウントのメールアドレス].
Your current project is [None].  You can change this setting by running:
  $ gcloud config set project PROJECT_ID
```

`gcloud auth configure-docker`コマンドを実行します。
`gcloud auth configure-docker`コマンドに関しての詳細は[こちら](https://cloud.google.com/sdk/gcloud/reference/auth/configure-docker)です。

```terminal
# gcloud auth configure-docker
Adding credentials for all GCR repositories.
~ 省略 ~
Do you want to continue (Y/n)?  Y

Docker configuration file updated.
```

プロジェクト名を設定します。
```
# gcloud config set project [GCPのproject名]
Updated property [core/project].
```
コンテナでの設定は終了したのでコンテナを抜けます。
```terminal
exit
```

## ローカル環境
先ほど起動したコンテナの情報を確認してコンテナをイメージ化します。
GCRのレジストリの命名規則は[こちら](https://cloud.google.com/container-registry/docs/overview?hl=ja#registries)を参照ください。
毎回タグに`latest`を付与すると、以前のtagはnoneになります。

```
% docker commit [コンテナ] asia.gcr.io/[プロジェクト内]/[GCRのフォルダ名]/[格納するGCRのimage名]:latest
```

最後にiamgeをGCRにpushします。
```terminal
% docker push asia.gcr.io/[プロジェクト内]/[GCRのフォルダ名]/[格納するGCRのimage名]:latest
```

以上。

## 参考サイト
[Container Registry](https://cloud.google.com/container-registry/docs/overview?hl=ja#registry_name)
[GCRの使い方](https://rinoguchi.net/2020/11/gcr.html)
[Go+Google Cloud SDK imageの作り方](https://qiita.com/tktcorporation/items/956f4afd68d491fb6894)