デプロイ時のアプリケーションIDの指定について

アプリケーションIDを、仮に、APP_ID とする。

app.yaml
application: APP_ID

で、これをローカル開発サーバで実行させると、アプリケーションIDは、

dev~APP_ID

となる。これはどこでわかるかというと、ローカル開発サーバの管理コンソール(http://localhost:管理用ポート番号/instances)を開くと表示されているもので、例えば、bulkloaderなんかで、ローカル開発サーバのアプリケーションIDを指定するときなどにも使用するものだ(参考:http://road2win.plala.jp/wordpress/?p=6427)。

で、このアプリケーションIDであるが、チュートリアルなどを見ていると、たいていの場合、サーバ側のプロジェクトIDと、app.yamlで指定する "application:"の値と、appcfg.pyなどで指定する -A オプションの値がすべて同じ、という例が多いため、区別がつきにくいのだが、-Aオプションの値はサーバ側のプロジェクトIDと同値を指定しなくてはいけないが、app.yamlには、それとは異なる値を設定することができる。

例えば自分の場合、1つのアプリケーションに対して、ステージング用サーバと本番サーバの2つを用意することが多いので、1つのソースコードを、2つのサーバ(プロジェクト)に向けてデプロイしている。
アプリケーションIDが "hogeapp" 、ステージング用のプロジェクトIDが "hogeapp_s"、本番用プロジェクトIDが "hogeapp_r" だとすると、

app.yaml
application: hogeapp
ステージングへのデプロイコマンド
appcfg.py -A hogeapp_s update ディレクトリ名 …
本番へのデプロイコマンド
appcfg.py -A hogeapp_r update ディレクトリ名…

とする。app.yaml をいちいち書き換える必要はない。

Googleアカウントとの関係

上記のデプロイコマンドの例では、Googleアカウントを指定していない(--emailオプションを指定してない)。その場合、gcloud auth list コマンドで"active"と表示されるアカウントでデプロイを実行しようとする。
このとき、hogeapp_s や、hogeapp_r に、そのアカウントに対する権限が設定されていないと、

Error 404: --- begin server output ---
This application does not exist (app_id=u'hogeapp_r').
--- end server output ---

というエラーが発生してデプロイに失敗する。
自分の場合、認証や権限等の機能のテストのため、2つ以上のGoogleアカウントを使い回して開発することが多い。gcloud auth コマンドで、アクティブアカウントを切り替えることができるが、この切り替えを失念したまま、--emailオプションもつけずにデプロイしてエラーが出て「スペルミスか?」と悩む、ということを何度か繰り返してしまった。
appcfg.py update の際は、常に --emailオプションを付けるクセをつけるべきだろう。

bulkloaderの使い方

bulkloaderの使い方についてググると、古い情報ばかりヒットして混乱するので、まとめる。

やりたいこと

csvファイルを本番サーバのdatastoreにインポートする。

前準備

app.yaml に、次の2行を追加する。

builtins:
- remote_api: on

なお、ググると、次をapp.yamlに追加せよ、という記述が結構な確率でヒットする。

- url: /remote_api
  script: $PYTHON_LIB/google/appengine/ext/remote_api/handler.py
  login: admin

しかし、これは古い方法のようだ。なので追加する必要なし。
そもそもエラーになるし、この記述がなくても、builtins...の記述だけで実行できた。
で、これを本番環境にデプロイしておく。

自分は、現行バージョンに影響が出ないよう、app.yamlのバージョン番号を1つ上げてデプロイした。

(この項書きかけ)

Google Cloud Storage JSON API Client for JavaScript を試す(2)アクセス制御関係

http://d.hatena.ne.jp/noazoh/20150115/1421315583 の続き。

Googleアカウントによる認証をせずとも、対象バケットへのアクセスが出来るようにしてみる。

コード

次に、サンプルコードを次のように変更する。
531行目〜

    /**
     * Handle authorization.
     */
    function handleAuthResult(authResult) {
      var authorizeButton = document.getElementById('authorize-button');
      if (authResult && !authResult.error) {
        authorizeButton.style.visibility = 'hidden';
        //initializeApi(); この行をコメントアウト
        filePicker.onchange = insertObject;
      } else {
        authorizeButton.style.visibility = '';
        authorizeButton.onclick = handleAuthClick;
      }
    }

565行目〜

    /**
     * Driver for sample application.
     */
    $(window)
      .bind('load', function() {
        addSelectionSwitchingListeners();
        initializeApi();    //この行を追加
        handleClientLoad();
    });

何をやっているかというと、cloud storage APIの初期化を、アカウント認証後に行っていたのを、ページがロードされた時に行うように変更した。

テスト

ブラウザを立ち上げ、Googleアカウントがログインしていない状態にしたうえで、サンプルを実行する。
まず、プルダウンから、たとえば"list Objects" を選択する。
まず、バケットの権限を何もいじらない状態(バケットを作成した直後の状態)で実行すると、結果はこうなる。

API Response

{
 "code": 401,
 "data": [
  {
   "domain": "global",
   "reason": "required",
   "message": "Login Required",
   "locationType": "header",
   "location": "Authorization"
  }
 ],
 "message": "Login Required",
 "error": {
  "code": 401,
  "data": [
   {
    "domain": "global",
    "reason": "required",
    "message": "Login Required",
    "locationType": "header",
    "location": "Authorization"
   }
  ],
  "message": "Login Required"
 }
}

次に、バケットに対して、AllUsers にREAD権限を与えてみる。

gsutil acl set public-read gs://バケット名

そして、同じくプルダウンから、"list Objects" を選択すると、バケット内のオブジェクトの一覧が取得できるようになる。

API Response

{
 "kind": "storage#objects",
 "result": {
  "kind": "storage#objects"
   ...
 }
}

デフォルトバケットと通常バケットのACLの初期値

通常バケットの作成直後の状態

gsutil acl get gs://通常バケット名
[
  {
    "entity": "project-owners-プロジェクト番号",
    "projectTeam": {
      "projectNumber": "プロジェクト番号",
      "team": "owners"
    },
    "role": "OWNER"
  },
  {
    "entity": "project-editors-プロジェクト番号",
    "projectTeam": {
      "projectNumber": "プロジェクト番号",
      "team": "editors"
    },
    "role": "OWNER"
  },
  {
    "entity": "project-viewers-プロジェクト番号",
    "projectTeam": {
      "projectNumber": "プロジェクト番号",
      "team": "viewers"
    },
    "role": "READER"
  }
]

デフォルトバケット作成直後の状態

gsutil acl get gs://デフォルトバケット名
[
  {
    "entity": "project-owners-プロジェクト番号",
    "projectTeam": {
      "projectNumber": "プロジェクト番号",
      "team": "owners"
    },
    "role": "OWNER"
  },
  {
    "entity": "project-editors-プロジェクト番号",
    "projectTeam": {
      "projectNumber": "プロジェクト番号",
      "team": "editors"
    },
    "role": "OWNER"
  },
  {
    "entity": "project-viewers-プロジェクト番号",
    "projectTeam": {
      "projectNumber": "プロジェクト番号",
      "team": "viewers"
    },
    "role": "READER"
  },
  {
    "entity": "user-何かのID",
    "entityId": "何かのID",
    "role": "OWNER"
  }
]

エントリーが1つ多い。「何かのID」が何のIDなのか不明。(何の、というよりは、"user-"というプレフィクスが付いているので、誰かのアカウントなのだろうが、どのアカウントに相当するものなのかが不明)
少なくとも、Google APIs 管理画面(https://code.google.com/apis/console/b/1/?noredirect)の、Google Cloud Storage のタブに表示されているGoogle Cloud Storage IDs(You, Owners, Editors, Team)とは違うIDである。