【GCP】Google Cloud Storage を curl コマンドで利用する

#はじめに

curl コマンドのみで Google Cloud Storage の API を利用する手順を確立させたのでメモ。

#手順

###前提

- Google Cloud Platform
- Curl

###Google Cloud Storage JSON API の有効化

Google Cloud Platform にログインし、任意のプロジェクトで設定を行っていきます。

まず、[API ライブラリ](https://console.cloud.google.com/apis/library/storage-api.googleapis.com)で Google Cloud Storage JSON API を有効にします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/258416/d81e61ab-ee01-8fa4-c008-0f97f5989217.png)

###OAuth 同意画面作成

[認証情報](https://console.cloud.google.com/apis/credentials/consent?createClient)ページで、クライアントIDに Google Cloud Storage JSON API の利用権限を付与するためのOAuth同意画面を新規作成します。
[アプリケーション名]に任意のアプリ名を指定します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/258416/63771d19-8bfe-c07d-d3e2-13ffdfb617e4.png)

[スコープの追加]をクリックし、スコープ一覧から`Google Cloud Storage JSON API`で検索して、[../auth/devstorage.read_write]にチェックを入れ、[追加]をクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/258416/979fc3e4-3f6e-5667-dada-2bb49529115d.png)

スコープ一覧に「../auth/devstorage.read_write」が追加されたら、[保存]をクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/258416/8c149907-0723-81ab-a5ec-d387dfce1d9a.png)

###クライアントID作成
OAuth同意画面の作成を保存すると、次にクライアントID作成画面が表示されます。

[アプリケーションの種類]から[ウェブアプリケーション]を選択し、任意のクライアントID名を指定して、[承認済みのリダイレクト URI]に`http://localhost`を指定し、[作成]をクリックします。
![スクリーンショット 2019-03-19 16.27.40.png](https://qiita-image-store.s3.amazonaws.com/0/258416/4e2449b2-9e08-1018-b3f4-4ff43df4fa12.png)

クライアントIDとクライアントシークレットがダイアログ表示されるので控えておきます。

###認証コードの取得

|クエリ名|値|説明|
|---|---|---|
|scope|*https\://www\.googleapis.com/auth/devstorage.read_write*|[スコープ一覧](https://developers.google.com/identity/protocols/googlescopes)に記載されている利用したいAPIのスコープURI|
|redirect_uri|*http\://localhost*|クライアントID作成時に指定した[承認済みのリダイレクト URI]の値|
|client_id|先程控えたクライアントID|クライアントID|
|response_type|*code*|固定値|
|access_type|*offline*|固定値|

`https://accounts.google.com/o/oauth2/v2/auth`に上記のクエリを指定してWebブラウザからアクセスします。

URI例

```shell
https://accounts.google.com/o/oauth2/v2/auth?redirect_uri=http://localhost&client_id=XXXXXXX.apps.googleusercontent.com&scope=https://www.googleapis.com/auth/devstorage.read_write&response_type=code&access_type=offline
```

表示されるOAuth同意画面に従い、Google アカウントでのログインおよびAPIスコープへの権限付与の許可を行います。

`http://localhost`画面にリダイレクトされたら、URL欄に表示されているリダイレクト先URIの*code*クエリの値が認証コードなので控えます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/258416/12017f0b-b548-38ba-88e6-a726db09a928.png)

リダイレクト先URI例

```shell
http://localhost/?code=4/EgEanhtbO...&scope=https://www.googleapis.com/auth/devstorage.read_write
```

###アクセストークン/リフレッシュトークンの取得

|クエリ名|値|説明|
|---|---|---|
|redirect_uri|*http\://localhost*|クライアントID作成時に指定した[承認済みのリダイレクト URI]の値|
|client_id|先程控えたクライアントID|クライアントID|
|client_secret|先程控えたクライアントシークレット|クライアントシークレット|
|code|先程控えた認証コード|認証コード|
|grant_type|*authorization_code*|固定値|
|access_type|*offline*|固定値|

上記のクエリを指定して下記例の通りリクエストを実行します。

実行例

```shell
$ REDIRECT_URI=http://localhost
$ CLIENT_ID=...
$ CLIENT_SECRET=...
$ AUTHORIZATION_CODE=...
$ curl --data "redirect_uri=${REDIRECT_URI}" \
     --data "client_id=${CLIENT_ID}" \
     --data "client_secret=${CLIENT_SECRET}" \
     --data "code=${AUTHORIZATION_CODE}" \
     --data "grant_type=authorization_code" \
     --data "access_type=offline" \
     https://www.googleapis.com/oauth2/v4/token

{
  "access_token": "ya29....",
  "expires_in": 3600,
  "refresh_token": "1/Z...",
  "scope": "https://www.googleapis.com/auth/devstorage.read_write",
  "token_type": "Bearer"
}
```

アクセストークン`access_token`とリフレッシュトークン`refresh_token`が取得できました。

###Google Cloud Storage JSON API の利用（例：ファイルアップロード）

バケットが未作成の場合は[Google クラウド ストレージ](https://console.cloud.google.com/storage/browser)にバケットを作成します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/258416/cc3351ce-ef75-36e2-750e-c9026c627b43.png)

API Reference： https://cloud.google.com/storage/docs/json_api/v1/
アップロードコマンド：　https://cloud.google.com/storage/docs/uploading-objects?hl=ja

|必要なパラメータ（アップロード時）|説明|
|---|---|
|OBJECT|アップロードしたいローカルファイルのパス|
|OAUTH2_TOKEN|先程取得したアクセストークン|
|OBJECT_CONTENT_TYPE|アップロードしたいファイルのMIMEタイプ|
|BUCKET_NAME|アップロード先のバケット名|
|OBJECT_NAME|アップロード後のファイル名|

例えば、バケットにファイルをアップロードするには、上記のパラメータを指定して下記例の通りリクエストを実行します。

アップロード実行例

```shell
$ ls
file1.txt
$ OBJECT=file1.txt
$ AUTH2_TOKEN=ya29....
$ OBJECT_CONTENT_TYPE=text/plain
$ BUCKET_NAME=test...
$ OBJECT_NAME=file1.txt
$ curl -X POST --data-binary @${OBJECT} \
    -H "Authorization: Bearer ${OAUTH2_TOKEN}" \
    -H "Content-Type: ${OBJECT_CONTENT_TYPE}" \
    "https://www.googleapis.com/upload/storage/v1/b/${BUCKET_NAME}/o?uploadType=media&name=${OBJECT_NAME}"

```

ファイルがバケット内にアップロードされました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/258416/b04b0813-5bc0-a9b3-68c3-5e51fb57919f.png)

###アクセストークンの更新

アクセストークンの有効期限は3600秒です。期限切れの場合はリフレッシュトークンを利用してアクセストークンを更新する必要があります。

|クエリ名|値|説明|
|---|---|---|
|client_id|先程控えたクライアントID|クライアントID|
|client_secret|先程控えたクライアントシークレット|クライアントシークレット|
|refresh_token|先程認証コードを利用して取得したリフレッシュトークン|リフレッシュトークン|
|grant_type|*refresh_token*|固定値|

新しいアクセストークンを取得する場合は、上記のクエリを指定して下記例の通りリクエストを実行します。

```shell
$ CLIENT_ID=...
$ CLIENT_SECRET=...
$ REFRESH_TOKEN=...
$ curl --data "client_id=${CLIENT_ID}"  \
    --data "client_secret=${CLIENT_SECRET}"  \
    --data "refresh_token=${REFRESH_TOKEN}"  \
    --data "grant_type=refresh_token" \
    https://www.googleapis.com/oauth2/v4/token

{
  "access_token": "ya29....",
  "expires_in": 3600,
  "scope": "https://www.googleapis.com/auth/devstorage.read_write",
  "token_type": "Bearer"
}
```

これで次回期限（3600秒間）まで有効なアクセストークン`access_token`を再度取得することができました。
リフレッシュトークンは次回のアクセストークン更新でも継続して利用することが可能です。
実際の利用では、「アクセストークンの更新」→「Google Cloud Storage JSON API の利用」を毎回セットで実施することになるかと思います。

#おわりに

アタッチやボリュームマウントができない Docker コンテナ内で生成したファイルを回収するために、gsutil の導入や json キーの環境変数への埋込みを行わずに Google Cloud Storage にファイルアップロードをしたかったので、これで何とかなりました。