CLOVER🍀

That was when it all began.

PythonのAWSクライアントライブラリ、Boto 3の接続情報を設定する

これは、なにをしたくて書いたもの?

PythonAWSクライアントライブラリといえば、Boto 3というものだそうです。

AWS SDK for Python | AWS

GitHub - boto/boto3: AWS SDK for Python

ドキュメントは、こちら。

Boto 3 Documentation — Boto 3 Docs 1.9.134 documentation

AWSの代わりにLocalStackを使っていろいろと遊びたいのですが、LocalStackに接続する際にはエンドポイントを変えたり
しないといけないわけで、そのあたりを調べてみました。

お題

Boto 3を使って、LocalStack上のS3にアクセスしてみたいと思います。

Boto 3には高レベルAPI(Resource)と低レベルAPI(Client)があるようなので、その両方でやってみます。

環境とライブラリのインストール

LocalStackは、Docker Composeで起動。LocalStackは、「192.168.0.3」というホストで動作しているものとします。
docker-compose.yml

version: "3"
services:
  localstack:
    image: localstack/localstack:0.9.1
    ports:
      - "4567-4593:4567-4593"
      - "8080:8080"
    environment:
      - SERVICES=${SERVICES- }
      - DEBUG=${DEBUG- }
      - DATA_DIR=${DATA_DIR- }
      - PORT_WEB_UI=${PORT_WEB_UI- }
      - LAMBDA_EXECUTOR=docker-reuse
      - KINESIS_ERROR_PROBABILITY=${KINESIS_ERROR_PROBABILITY- }
      - DOCKER_HOST=unix:///var/run/docker.sock
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"

今回の環境は、こちら。

$ python -V
Python 3.6.7

仮想環境の作成。

$ python3 -m venv venv
$ . venv/bin/activate

Boto 3のインストール。

$ pip3 install boto3

バージョン。

$ pip3 freeze
boto3==1.9.134
...

これで、準備は完了です。

サンプルプログラム

Bucketの作成、削除、オブジェクトのPut/Getを行う簡単なプログラムを作成。
app.py

import boto3.session
from botocore.config import Config

session = boto3.session.Session(
    aws_access_key_id = 'my-access-key',
    aws_secret_access_key = 'my-secret-access-key'
)


### for Client
s3client = session.client(
    's3',
    endpoint_url = 'http://192.168.0.3:4572',
    config = Config()
)

print(s3client.list_buckets())

bucket = s3client.create_bucket(Bucket = 'my-bucket1')

print(s3client.list_buckets())

s3client.put_object(Bucket = 'my-bucket1', Key = 'sample-object1', Body = b'Hello LocalStack S3!! from Client')

print('body = ' + s3client.get_object(Bucket = 'my-bucket1', Key = 'sample-object1')['Body'].read().decode())

s3client.delete_object(Bucket = 'my-bucket1', Key = 'sample-object1')

s3client.delete_bucket(Bucket = 'my-bucket1')


#### for Resource
s3resource = session.resource(
    's3',
    endpoint_url = 'http://192.168.0.3:4572',
    config = Config()
)

bucket = s3resource.Bucket('my-bucket2')

bucket.create()

my_object = bucket.Object('sample-object2')
my_object.put(Body = b'Hello LocalStack S3!! from Resource')

print('body = ' + my_object.get()['Body'].read().decode())

my_object.delete()

bucket.delete()

サンプルは、Exampleを参考にしつつ

Amazon S3 Examples — Boto 3 Docs 1.9.134 documentation

S3用のAPIリファレンスを眺めながら作成。

S3

アクセスキーやシークレットアクセスキーは、AWS CLIで設定してもいいのですが、今回はプログラム内で指定。

session = boto3.session.Session(
    aws_access_key_id = 'my-access-key',
    aws_secret_access_key = 'my-secret-access-key'
)

デフォルトの情報以外でClientやResourceを作成するには、Sessionというものをカスタマイズすることになる、と。

Session — Boto 3 Docs 1.9.134 documentation

低レベルAPI(Client)を使った場合。

### for Client
s3client = session.client(
    's3',
    endpoint_url = 'http://192.168.0.3:4572',
    config = Config()
)

print(s3client.list_buckets())

bucket = s3client.create_bucket(Bucket = 'my-bucket1')

print(s3client.list_buckets())

s3client.put_object(Bucket = 'my-bucket1', Key = 'sample-object1', Body = b'Hello LocalStack S3!! from Client')

print('body = ' + s3client.get_object(Bucket = 'my-bucket1', Key = 'sample-object1')['Body'].read().decode())

s3client.delete_object(Bucket = 'my-bucket1', Key = 'sample-object1')

s3client.delete_bucket(Bucket = 'my-bucket1')

Sessionから、Clientを作成します。この時、エンドポイントを指定したりできます。

s3client = session.client(
    's3',
    endpoint_url = 'http://192.168.0.3:4572',
    config = Config()
)

リファレンスは、こちら。

boto3.session.Session

ここで指定しているConfigというのは、Boto 3より更に低レベルなライブラリである、Botocoreというライブラリの設定です。
今回は特になにも指定していませんが、カスタマイズする場合は以下を参照しましょう。

Config Reference — botocore 1.12.134 documentation

続いて、高レベルAPI(Resource)の場合。

#### for Resource
s3resource = session.resource(
    's3',
    endpoint_url = 'http://192.168.0.3:4572',
    config = Config()
)

bucket = s3resource.Bucket('my-bucket2')

bucket.create()

my_object = bucket.Object('sample-object2')
my_object.put(Body = b'Hello LocalStack S3!! from Resource')

print('body = ' + my_object.get()['Body'].read().decode())

my_object.delete()

bucket.delete()

Resourceも、やはりSessionから作成します。

s3resource = session.resource(
    's3',
    endpoint_url = 'http://192.168.0.3:4572',
    config = Config()
)

リファレンスは、こちら。

resource

Clientの時と同様、ConfigというのはBotocoreの設定になります。

確認

最後に、実行して確認です。

$ python3 app.py 
{'ResponseMetadata': {'HTTPStatusCode': 200, 'HTTPHeaders': {'server': 'BaseHTTP/0.3 Python/2.7.15', 'date': 'Sat, 20 Apr 2019 13:20:59 GMT', 'content-type': 'application/xml; charset=utf-8', 'content-length': '205', 'access-control-allow-origin': '*', 'access-control-allow-methods': 'HEAD,GET,PUT,POST,DELETE,OPTIONS,PATCH', 'access-control-allow-headers': 'authorization,content-type,content-md5,cache-control,x-amz-content-sha256,x-amz-date,x-amz-security-token,x-amz-user-agent'}, 'RetryAttempts': 0}, 'Buckets': [], 'Owner': {'DisplayName': 'webfile', 'ID': 'bcaf1ffd86f41161ca5fb16fd081034f'}}
{'ResponseMetadata': {'HTTPStatusCode': 200, 'HTTPHeaders': {'server': 'BaseHTTP/0.3 Python/2.7.15', 'date': 'Sat, 20 Apr 2019 13:20:59 GMT', 'content-type': 'application/xml; charset=utf-8', 'content-length': '298', 'access-control-allow-origin': '*', 'access-control-allow-methods': 'HEAD,GET,PUT,POST,DELETE,OPTIONS,PATCH', 'access-control-allow-headers': 'authorization,content-type,content-md5,cache-control,x-amz-content-sha256,x-amz-date,x-amz-security-token,x-amz-user-agent'}, 'RetryAttempts': 0}, 'Buckets': [{'Name': 'my-bucket1', 'CreationDate': datetime.datetime(2006, 2, 3, 16, 45, 9, tzinfo=tzutc())}], 'Owner': {'DisplayName': 'webfile', 'ID': 'bcaf1ffd86f41161ca5fb16fd081034f'}}
body = Hello LocalStack S3!! from Client
body = Hello LocalStack S3!! from Resource