GCP - Cloud Buildで、Cloud Source Repositoryへのプッシュをトリガーに、Cloud Functionsへデプロイする

Googleさんから、「4月20日までにCloud FunctionsのためにCloud Buildを有効化しろや」という
趣旨のメールが来ておりませしたので、この機会に手動デプロイから抜け出してみようと思います。

[参考にした情報](https://cloud.google.com/functions/docs/testing/test-cicd?hl=ja)

## ToDo
- 構築手順
  - プロジェクトを作る。
  - API有効化(Cloud FunctionsとBuildとRepositoryなど)
  - 権限設定
  - リポジトリの作成
  - トリガーの作成
  - コードおよびビルド構成ファイルのプッシュ
  - デプロイ結果の確認
  - プロジェクトを削除する。

- 確認
  - コード修正～自動デプロイ

## Procedure
### Create a new project
あとですぐに消せるように、一時的なプロジェクトを作成します。

1. 画面の上にある [Select a project] を選択する。
2. [NEW PROJECT] を選択する。
3. 「Project Name」に任意の名前を入れる。<br>  グローバルで一意になるように、IDには数字が付与される。
4. [Create]を選択する。

### Enable APIs
APIを有効化します。

1. ハンバーガーから、「API & Services - Library」を選択する。
1. 虫眼鏡欄に、「Cloud Build API」を入力する。
1. 結果から、「Cloud Build API」を選択する。
1. [ENABLE]を選択する。
1. 同様に「Cloud Source Repositories API」も有効化する。
1. 同様に「Cloud Resource Manager API」も有効化する。
1. 同様に「Cloud Functions API」も有効化する。


### Add roles for Service Account
Cloud Buildのサービスアカウントに役割を追加します。

1. Cloud SDKを起動する。
1. プロジェクトを設定する。

    ```
    $ gcloud config set project [上で作ったプロジェクトのID]
    ```

1. プロジェクトを情報を確認する。

    ```
    $ gcloud projects describe [上で作ったプロジェクトのID]
    ```

    ```
    createTime: 'yyyy-mm-ddTHH:MM:SS.000Z'
    lifecycleState: ACTIVE
    name: プロジェクトの名前
    projectId: プロジェクトのID
    projectNumber: 'プロジェクトの番号'
    ```

1. Cloud BuildのサービスアカウントをCloud Functionsのランタイムサービスアカウントとして機能させる。
([プロジェクトのID]と[プロジェクトの番号]を置き換えて実行する。)

    ```
    $ gcloud iam service-accounts add-iam-policy-binding [プロジェクトのID]@appspot.gserviceaccount.com \
      --member serviceAccount:[プロジェクトの番号]@cloudbuild.gserviceaccount.com \
      --role roles/iam.serviceAccountUser
    ```

1. Cloud BuildのサービスアカウントをCloud Functionsの開発者の役割を追加する。
([プロジェクトのID]と[プロジェクトの番号]を置き換えて実行する。)

    ```
    $ gcloud projects add-iam-policy-binding [プロジェクトのID] \
      --member serviceAccount:[プロジェクトの番号]@cloudbuild.gserviceaccount.com \
      --role roles/cloudfunctions.developer
    ```

### Create a repository
Cloud Source Repository上にリポジトリを作成します。
1. Cloud SDKを起動する。
1. プロジェクトを設定する。


    
    $ gcloud config set project [上で作ったプロジェクトのID]
    


1. リポジトリの作成
「課金されるで」的なWARNINGは無視でOK。

    ```
    $ gcloud source repos create sample-repository
    ```

    ```
    Created [sample-repository].
    ```

1. クローン
「空のリポジトリやで」的なWARNINGは無視でOK。

    ```
    $ gcloud source repos clone sample-repository
    ```

    ```
    Cloning into '/home/ユーザ名/sample-repository'...
    warning: You appear to have cloned an empty repository.
    Project [上で作ったプロジェクトのID] repository [sample-repository] was cloned to [/home/ユーザ名/sample-repository].
    ```


### Create a build trigger
1. ハンバーガーから「Cloud Build - Triggers」を選択する。
1. [CREATE TRIGGER]を選択する。


    |Item|Defult Value|Value|Comment|
    |:-:|:-:|:-:|:-:|
    |Name|-|**sample-trigger**|任意のトリガー名(プロジェクト内で一意)|
    |Description|-|←|説明文|
    |Event|Push to a branch|←|トリガーとなるイベント(今回はプッシュを対象とする)|
    |Repository|-|**sample-repository**|今回は上で作ったリポジトリを選択|
    |Branch|-|**^master$**|今回はmasterを対象とする|
    |Invert Regex|No|←||
    |File Type|Cloud Build configuration fle(yaml or json)|←||
    |Cloud Build configuration file location|/cloudbuild.yaml|←|ビルド構成ファイルを指定|

1. [CREATE]を選択する。


### Register a code and build configuration file
リポジトリにコードと、ビルド構成ファイルを登録します。

1. Cloud SDKを起動する。
1. プロジェクトを設定する。

    ```
    $ gcloud config set project [上で作ったプロジェクトのID]
    ```

1. リポジトリをクローンしたディレクトリへ移動

    ```
    $ cd ./sample-repository
    ```

1. Pythonコードを main.py という名前で作成。

    ```Python:
    def hello_world(request):
        return f'Hello World!'
    ```

1. ビルド構成ファイルを cloudbuild.yaml という名前で作成。

    ```Python:
    steps:
        - name: 'gcr.io/cloud-builders/gcloud'
            args: ['functions', 'deploy', 'sample-function', '--trigger-http', '--runtime', 'python37', '--entry-point', 'hello_world', '--region', 'asia-northeast1']
    ```

    |Item|Value|
    |:-:|:-:|
    |Function name|sample-function|
    |Trigger|HTTP|
    |Runtime|Python37|
    |Entry Point|hello_world|
    |Region|asia-northeast1|

1. .gcloudignore を作成する。

    ```
    .gcloudignore
    .git
    .gitignore
    node_modules
    ```

1. ファイルの追加

    ```
    $ git add .
    ```

1. コミット

    ```
    $ git commit -m "This is a first time of deploy python code."
    ```

    「*** Please tell me who you are.(つーかお前誰やねん)」が出たら、

    ```
    git config --local user.email "your_name@your_domain.com"
    git config --local user.name "your_name"
    ```


1. プッシュ

    ```Shell:
    $ git push origin master
    ```


### Confirm build result
ビルド結果を確認します。

1. ビルド結果の確認
STATUSがSUCCESSであれば、デプロイ成功です。

    ```Shell:
    $ gcloud builds list
    ```

    ```
    ID                                    CREATE_TIME                DURATION  SOURCE                    IMAGES  STATUS
    xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx  yyyy-mm-ddTHH:MM:SS+00:00  xxS       sample-repository@master  -       SUCCESS
    ```

1. Cloud Functionsの確認
以下のようにSTATUSがACTIVEで作成されていればOKです。

    ```
    $ gcloud functions list
    ```

    ```
    NAME             STATUS  TRIGGER       REGION
    sample-function  ACTIVE  HTTP Trigger  asia-northeast1
    ```

1. Cloud Functionsの動作確認
以下のように結果がかえってくればOKです。

    ```
    $ gcloud functions call sample-function --region=asia-northeast1
    ```

    ```
    executionId: xxxxxxxxxxxxx
    result: Hello World!
    ```

### Delete the project
無駄に課金されることを避けるため、プロジェクトを削除します。

1. ハンバーガーから、「Home」を選択する。
2. [Go to project settings]を選択する。
3. [SHUT DOWN]を選択する。<br>30日間は消えずに残っている。


## Confirmation

### Auto-build


1. main.py を修正します。

    ```Python:
    def hello_world(request):
        return f'Hello World! This is the world!'
    ```

1. ファイルの追加

    ```
    $ git add .
    ```

1. コミット

    ```
    $ git commit -m "This is a second time of deploy python code."
    ```

    「*** Please tell me who you are.」が出たら、

    ```
    git config --local user.email "your_name@your_domain.com"
    git config --local user.name "your_name"
    ```


1. プッシュ

    ```
    $ git push origin master
    ```

1. ビルド結果の確認
STATUSがSUCCESSであれば、デプロイ成功です。

    ```
    $ gcloud builds list
    ```

    ```
    ID                                    CREATE_TIME                DURATION  SOURCE                    IMAGES  STATUS
    xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx  yyyy-mm-ddTHH:MM:SS+00:00  xxS       sample-repository@master  -       SUCCESS
    ```


1. Cloud Functionsの動作確認
以下のように結果がかえってくればOKです。
変更した内容が、自動的にデプロイされましたー。

    ```
    $ gcloud functions call sample-function --region=asia-northeast1
    ```

    ```
    executionId: xxxxxxxxxxxxx
    result: Hello World! This is the world!
    ```