SENSY株式会社のwasnotです。
さて、Adventカレンダー、みんな埋めてくれないので作成者として埋めていきます。
ネタがあまりないので、先日のDatastoreのExport機能でバックアップの自動化の続きとして、
Datastoreのバックアップが完了した時にGAE/SEアプリに通知を送りたいと思います。
GAE/SEを使うのは単にこのために外部サービスやサーバを立てたくないからです。
先日のDatastoreバックアップのフックもSEでしたね。
GCSの変更通知
先日はDatastoreのExport機能を使ってGoogle Cloud Storage(GCS)のバケットに保存しました。
このイベントはGCSについている変更通知機能を使って取得できます。
この機能にも先日と同じように既存の通知機能とベータ版のpub/sub連携版があります。
今回は新しそうなpub/sub連携版を試したいと思います。
既存のオブジェクト変更通知
現在のGA機能にもGCS上の、アップロードや削除などのファイル変更の通知を
webhookとして受け取ることのできる機能が備わっています。
これは以下のようなgsutilコマンドで設定できます。
> gsutil notification watchbucket [-i ChannelId] [-t ClientToken] ApplicationUrl gs://BucketName
これで終わりだったら簡単なんですが、
webmasterツールでwebhook先アプリのドメインをホワイトリストに追加したりする必要があるようです。
詳しいことはこちらでもわかりやすく解説されています。
Object Change NotificationをApp Engineで受け取る設定
Cloud Pub/Sub Notifications for Cloud Storage
できることはほぼ同等なのですが、最近はGCSからの変更通知をpub/subにpushする機能も備わっています。
今まで自分の指定したアプリへのwebhookを直接していたところをpub/sub経由で行うようになった、
という感じだと思っています。(違ったらすみません)
こちらはpub/subを経由することでコンソール上でpub/subのtopicを見たり設定できます。
また、一つのtopicから複数のwebhookへpushもできるのでより柔軟になった、と考えることができそうです。
さらに後述しますが、pub/subを経由することで、同一のプロジェクトなら、AppEngine/SEのドメインはホワイトリストに追加不要でpushできます!
これは手間が減るのでとてもいいかな、と思います。
設定
変更通知をpub/subへ登録
まず、オブジェクトの変更を通知する機能を登録します。
これは既存の登録コマンドととても似ていて、gsutil経由で行います。
> gsutil notification create -t [TOPIC_NAME] -f json gs://[BUCKET_NAME]
watchbucket
を設定する、ではなくnotification
を作成する、という感じでしょうか。
-f json
は通知の中身をjsonで受け取るかどうかを指定するものです。
json|none
なので指定しないと、bodyが何も送られてこないと思います。
projectの指定が必要な場合がある。
上記の記事の説明だと-tのオプションはpub/subのトピック名だけを指定すればいいのかと思っていました。
しかし、下記のように怒られてしまいました。
今回はmy-datastore-backup
バケットにdatastore-backup
という名前のトピックを作成した例です。
> gsutil notification create -t datastore-backup -f json gs://my-datastore-backup
You are attempting to perform an operation that requires a project id, with none configured. Please re-run gsutil config and make sure to follow the instructions for finding and entering your default project id.
今回は自分のアカウントで叩いていましたが、projectを複数持っていて、gcloudコマンドにデフォルトproject idを設定していないせいかもしれません。
他のgsutilのように-p
オプションはproject_id指定ではなく、prefix指定になっていたので個別には指定できませんでした。
gsutil help notification
の例にあるように、topicsの名前を絶対パスで指定すると設定できました。
プロジェクトIDがmy-project
だとすると以下のようにしたら作成されます。
> gsutil notification create -f json -t projects/my-project/topics/datastore-backup gs://my-datastore-backup
Created Cloud Pub/Sub topic projects/my-project/topics/datastore-backup
Created notification config projects/_/buckets/my-datastore-backup/notificationConfigs/2
> gsutil notification list gs://my-datastore-backup
projects/_/buckets/my-datastore-backup/notificationConfigs/2
Cloud Pub/Sub topic: projects/my-project/topics/datastore-backup
この状態でコンソールのPub/sub画面を見るときちんと追加されています。
サブスクリプションの登録
次はGCSではなくPub/Subの話題です。
Pub/Subの機能については特に書きませんが、topicへの通知をsubscriptionを設定して他のendpointにpushする方法はこちらに書かれています。
このページのApp Engine スタンダード環境のエンドポイントの項目にGAE/SEで受け取る方法についてヒントが書いてあります。
ここにあるように、同一プロジェクトであればpush先のドメインをホワイトリストに登録するのが不要になります!
なのでpushのサブスクリプションを作ってappengineのエンドポイントを設定すれば完了になります。
「サブスクリプションを作成」をクリック
サブスクリプション名を設定(ここではfinish-datastore-backup
)、
配信タイプを「エンドポイントURLにpush」を選択しpush先のendpointを登録します。
これで変更時に設定したエンドポイントに向けて通知が届きます。
GCSからの通知を受け取る
基本的に先ほどの公式ページに通知形式について記載があります。
前の項で設定したendpointにPOSTで通知が来ます。
例を示すとこのような感じのjsonが届きます。
Accept-Charset: UTF-8
Content-Length: xxx
Content-Type: application/json
Host: my-project.appspot.com
User-Agent: CloudPubSub-Google
X-Appengine-Country: ZZ
X-Cloud-Trace-Context: xxx/xxx;o=1
{
"message": {
"data": "***",
"attributes": {
"objectGeneration": "1512372633478813",
"bucketId": "my-datastore-backup",
"eventType": "OBJECT_FINALIZE",
"notificationConfig": "projects/_/buckets/my-datastore-backup/notificationConfigs/1",
"payloadFormat": "JSON_API_V1",
"objectId": "test.txt"
},
"message_id": "178388778619664",
"messageId": "178388778619664",
"publish_time": "2017-12-04T07:30:33.720Z",
"publishTime": "2017-12-04T07:30:33.720Z"
},
"subscription": "projects/my-project/subscriptions/finish-datastore-backup"
}
よく使うのはobjectId
やeventType
あたりですね。
eventTypeはイベントの種類に記載されていますが、4種類あるようです。
- OBJECT_FINALIZE: アップロード完了イベント。失敗は含まず。
- OBJECT_METADATA_CHANGE: メタデータ変更時。
- OBJECT_DELETE: 削除時。上書き時にも送信される。バージョニング有効時のバージョン削除はARCHIVEが送信される。
- OBJECT_ARCHIVE: バージョニング有効時のみ、上書き等で送信される。
注意点: admin権限について
pub/subとGAE/SEのヒントでは、pub/sub用のendpointがcurl等で叩かれないために、
app.yamlでadmin権限を付与することを推奨しています。
handlers:
- url: /_ah/push-handlers/.*
script: main.APPLICATION
login: admin
そして、その際に/_ah/push-handlers/
プレフィックスが必須であると書いてあります。
最初読んだときはこのプレフィックス自体が必須ではなく、admin制限さえやっていればいいのかな、と思いましたが、このプレフィックスは必須でした。
具体的には別のprefixやちょっとでもtypoしたりするとredirect loopが起きてしまいました。
typoで/ah/push-handlers/
にしてしまったり、
ちなみにlogin:admin
を外せば普通に動きます。
でもあまり良くないので、決め打ちのprefixに従っておいた方が良さそうです。
taskqueue/cronのように、どんなendpointに対してもadmin権限が付与されて送信されるわけではないので注意が必要でした。
まとめ
今回はGCSのpub/sub連携とpub/sub->appengineの連携機能を触ってみました。
特にpub/sub->appengineなどは今更感ありますが、taskqueueとの違いなどもわかってよかった、と思っています。
この記事ではappengine/SE側のコードを書かなかったですね。
endpointがappengineなだけなので、FEとかでもホワイトリスト設定なしに動くと思います。
サンプルプロジェクトもとても参考になりました。
以上です。