Python(boto)からサービスアカウントとキーファイル(p12)を使ってGoogle Cloud Storageにアクセスする

  • 6
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

botoからGoogle Cloud Storageにアクセスする場合、s3との互換運用アクセスを有効にすることでアクセスキーとシークレットキーでアクセスすることが可能です。
http://qiita.com/itkr/items/d990e87a2540332ee0e5

ただしこの方法にはひとつ問題があります。互換運用アクセスで発行できるアクセスキーはGoogleユーザーアカウントにリンクされるため、複数人で行うプロジェクトでは不便です。そこでサービスアカウントとキーファイル(p12)でアクセスするという選択肢が出てきます。

依存ライブラリのインストール

ここではgcs-oauth2-boto-pluginというライブラリを使用します。

pip install gcs-oauth2-boto-plugin

設定ファイルの準備

サービスアカウントとキーファイルのパスを記述する設定ファイルを準備します。

ファイルの場所

デフォルトでは~/.botoを見ますが、環境変数で以下のどちらかを用意すると任意のパスを指定できます。ちなみにこれはgsutilと同じです。

  • BOTO_CONFIG (単一ファイルを指定する)
  • BOTO_PATH (:区切りで複数ファイルを指定する)

pythonで環境変数を書く場合は下のように書きます。

import os
os.environ['BOTO_CONFIG'] = '/path.to/boto_config'

必要な記述

いろいろ設定を書くことが出来ますが、今回重要なのは次の設定です

[Credentials]
gs_service_key_file = /path.to/sample-KEYFILE.p12
gs_service_client_id = sample-service-account@developer.gserviceaccount.com

[GSUtil]
default_project_id = sampleproject-994

参考:https://cloud.google.com/storage/docs/gsutil/commands/config#additional-configuration-controllable-features

実装

基本コード

import boto

bucket_name = 'bucket_name'
uri = boto.storage_uri(bucket_name, 'gs')
print uri.get_bucket()

基本的にはgcs-oauth2-boto-pluginをインストールしていればこれだけです。

解説

なぜこれだけで良いのか

botoはAuthConnectionを貼る際にPluginを導入できる仕組みを用意しています。詳細に書くとbotoの実装の多くを転載することになってしまうので省きますが、botoのauth関連のコードを見るとget_pluginという関数の呼び出しを見つけることが出来ます。

boto/plugin.py
def get_plugin(cls, requested_capability=None):
    if not requested_capability:
        requested_capability = []
    result = []
    for handler in cls.__subclasses__():
        if handler.is_capable(requested_capability):
            result.append(handler)
    return result

__subclasses__はサブクラスの一覧を表示しますが、この場合はAuthHandlerクラスを継承しているクラスの一覧になります。gcs-oauth2-boto-pluginの中のOAuth2ServiceAccountAuthというクラスがAuthHandlerを継承しているためプラグインとして認識されます。その中で設定ファイルからgs_service_key_fileなど必要な情報を取得しています。

ちなみに

自分でAuthHandlerを継承したクラスを実装し、中でconfigファイルのパスを無理矢理上書きして動的に変更することもできます。が、たぶんもっとスマートにできると思います。

from boto.auth_handler import AuthHandler
from boto.pyami.config import Config

boto_path = ''

class SpamAuth(AuthHandler):
    def __init__(self, path, config, provider):
        config = Config(path=boto_path)
        # ... 略 ...

def spam(path='/path.to/boto_config'):
    global boto_path
    boto_path = path
    bucket_name = 'bucket_name'
    uri = boto.storage_uri(bucket_name, 'gs')
    print uri.get_bucket()

なお、この際にURIクラス(この場合BucketStorageUri)は一度connectionを張るとprovider_poolというdictの中にconnectionを保持するので、connectionを切り替える場合はdel BucketStorageUri.provider_pool['gs']BucketStorageUri.provider_pool = {}のようにクリアしてあげないとダメみたいですね。