ようへいの日々精進XP

よかろうもん

Elixir で AWS を操作するチュートリアル(S3 編)

この記事は...

qiita.com

参加者の少ない, 初老丸 Advent Calendar 2017 17 日目の記事です.

Elixir で AWS を操作する選択肢

  • 公式パッケージは提供されていません(2017/12/17 時点)
  • 以下のようなパッケージがオープンソースで提供されていました(2017/12/17 時点)
Package 名 GitHub URL GitHub Star 備考
ex_aws https://github.com/ex-aws/ex_aws 553
aws https://github.com/jkakar/aws-elixir 165
erlcloud https://github.com/erlcloud/erlcloud 523 AWS APIs library for Erlang

ざっくり検索したので, 他にもあれば教えて下さい.

ということで, Github Star 数基準に選定して ex_aws で試してみます.

ex_aws について

github.com

  • 2.0 以降では, 各 AWS サービス毎にパッケージが提供されている(ex: S3 を操作する場合, ex_aws_s3 とか)
  • 21 の AWS サービスをサポート(2017/12/17 時点)
  • HTTP クライアントや JSON Codec を利用者側で選ぶことが出来る
  • リトライ処理は Exponential Backoff アルゴリズムで実装している
  • 自動的なりトライ処理とページネート(大量のリソースをよしなに分割して出力してくれるという理解)

チュートリアル(S3 バケット一覧を取得する)

ゴール

  • ex_aws_s3 を利用して S3 のバケット一覧 JSON で取得する

docker run

オフィシャルリポジトリを利用します.

docker run --name elixir_aws_sample -t -i -v ${HOME}:/root -v $(pwd):/elixir elixir /bin/bash

mix new

mix new s3_ope

以下のように出力されます.

root@65afa8ba92a5:/elixir# mix new s3_ope
* creating README.md
* creating .gitignore
* creating mix.exs
* creating config
* creating config/config.exs
* creating lib
* creating lib/aws_demo.ex
* creating test
* creating test/test_helper.exs
* creating test/aws_demo_test.exs

Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:

    cd s3_ope
    mix test

Run "mix help" for more commands.

mix.exs からの mix deps.get

以下のように依存パッケージを記載します.

  defp deps do
    [
      {:ex_aws, "~> 2.0"},
      {:ex_aws_s3, "~> 2.0"},
      {:hackney, "~> 1.9"},
      {:sweet_xml, "~> 0.6"},
      {:configparser_ex, "~> 2.0"}
    ]
  end

mix deps.get を実行する.

mix deps.get

以下のように出力される.

root@65afa8ba92a5:/elixir/s3_ope # mix deps.get
Resolving Hex dependencies...
Dependency resolution completed:
  certifi 2.0.0
  configparser_ex 2.0.1
  ex_aws 2.0.2
  ex_aws_s3 2.0.0
  hackney 1.10.1
...
* Getting unicode_util_compat (Hex package)
  Checking package (https://repo.hex.pm/tarballs/unicode_util_compat-0.3.1.tar)
  Fetched package

config.exs

config.exs に Shared Credentials のプロファイル名(watashino-profile)を以下のように設定します.

...
config :ex_aws,
  debug_requests: false,
  access_key_id: [{:awscli, "watashino-profile", 30}, :instance_role],
  secret_access_key: [{:awscli, "watashino-profile", 30}, :instance_role],
  region: "ap-northeast-1"
...

これだけです. 簡単ですな.

iex を起動

iex -S mix

初回の場合, 以下のように出力されます.

root@65afa8ba92a5:/elixir/s3_ope# iex -S mix
Erlang/OTP 20 [erts-9.1.3] [source] [64-bit] [smp:2:2] [ds:2:2:10] [async-threads:10] [hipe] [kernel-poll:false]

===> Compiling mimerl
===> Compiling metrics
=
...
==> ex_aws_s3
Compiling 7 files (.ex)
Generated ex_aws_s3 app
==> s3_ope
Compiling 1 file (.ex)
Generated s3_ope app
Interactive Elixir (1.5.2) - press Ctrl+C to exit (type h() ENTER for help)

list_buckets を実行

list_buckets/0 を実行すると以下のように出力されます.(出力はサンプルです)

iex(1)> ExAws.S3.list_buckets
%ExAws.Operation.S3{body: "", bucket: "", headers: %{}, http_method: :get,
 params: [], parser: &ExAws.S3.Parsers.parse_all_my_buckets_result/1, path: "/",
 resource: "", service: :s3, stream_builder: nil}

この状態では, まだ S3 にリクエストは送信されていない状態です. list_buckets を呼び出す為の準備をする感じでしょうか.

request! でリクエストを送信

ExAws の request!/2 をパイプライン演算子でつなげてあげることで, 初めて S3 にリクエストが送信されました.(出力はサンプルです)

iex(4)> ExAws.S3.list_buckets |> ExAws.request!
%{body: %{buckets: [%{creation_date: "2017-12-17T05:30:42.000Z",
       name: "oreno-bucket-1"}, 
    %{creation_date: "2017-12-17T05:30:43.000Z", name: "oreno-bucket-2"},
    %{creation_date: "2017-12-17T05:30:44.000Z", name: "oreno-bucket-3"},
...
  headers: [{"x-amz-id-2",
    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"},
   {"x-amz-request-id", "0F9E0E78F451D1C1"},
   {"Date", "Sun, 17 Dec 2017 04:56:41 GMT"},
   {"Content-Type", "application/xml"}, {"Transfer-Encoding", "chunked"},
   {"Server", "AmazonS3"}], status_code: 200}

上記のようにレスポンスが Map で返ってきます.

レスポンスがちょっとアレなので...

Map の get/3 で必要な部分だけをよしなに取得して...(出力はサンプルです)

iex(8)> ExAws.S3.list_buckets |> ExAws.request! |> Map.get(:body) |> Map.get(:buckets)
[%{creation_date: "2017-12-17T05:30:42.000Z", name: "oreno-bucket-1"},
 %{creation_date: "2017-12-17T05:30:43.000Z", name: "oreno-bucket-2"},
 %{creation_date: "2017-12-17T05:30:44.000Z", name: "oreno-bucket-3"}]

最後は Poison で JSON に...

Poison の encode!/2 を使えば JSON に.(出力はサンプルです)

iex(9)> ExAws.S3.list_buckets |> ExAws.request! |> Map.get(:body) |> Map.get(:buckets) |> Poison.encode!
"[{\"name\":\"oreno-bucket-1\",\"creation_date\":\"2017-12-17T05:30:42.000Z\"},{\"name\":\"oreno-bucket-2\",\"creation_date\":\"2017-12-17T05:30:43.000Z\"},{\"name\":\"oreno-bucket-3\",\"creation_date\":\"2017-12-17T05:30:44.000Z\"}]"

いい感じ.

以上

ex_aws で S3 のバケット一覧を取得するまでのチュートリアルでした.

ex_aws は開発も活発で, パッケージのダウンロード数についても ex_awsaws と比較しても圧倒的なので, 事実上のスタンダードパッケージだと思いますので, 今後のアップデートに注目していきたいと思います.