dockerでDynamoDB LocalとAWS SDK for C++の開発環境を動かす

[前々回](http://qiita.com/d9magai@github/items/7c2631fd859c989fec89)はAWS SDK for C++を使ってS3をいじってみました。今回はDynamoDBで試してみます。せっかくなのでDynamoDB Localをdockerで動かし、それを操作します。

#準備

まずはdockerでDynamoDB Localを動かします。

##dockerでDynamoDB Localを動かす

丁度よさげなイメージが[こちら](https://hub.docker.com/r/tray/dynamodb-local/)にありましたので、これを使って動かします。その際に-sharedDbを指定し、リージョン区分のエミュレート機能を無効にします。個人的にはこの機能はハマりポイントだと思うので(というかハマりました)無効にして単一のDBが作られるようにします。

```bash
$ sudo docker run -d \
                  -p 8000:8000 \
                  -v /var/dynamodblocal:/var/dynamodblocal \
                  --name localdynamodb \
                  -t tray/dynamodb-local -sharedDb -dbPath /var/dynamodblocal
```

## DynamoDB Localにテーブルを作成

動かしたら次は`aws cli`でテーブルを作ります。
[開発者ガイド](https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Tools.CLI.html)をパクって、Music という名前のテーブルを作成します。パーティションキーは Artist で、ソートキーは SongTitle です。
DynamoDB Localに作るので`--endpoint-url http://localhost:8000`を指定しています。

```bash
$ aws dynamodb create-table --endpoint-url http://localhost:8000 \
                            --table-name Music \
                            --attribute-definitions AttributeName=Artist,AttributeType=S \
                                                    AttributeName=SongTitle,AttributeType=S \
                            --key-schema AttributeName=Artist,KeyType=HASH AttributeName=SongTitle,KeyType=RANGE \
                            --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1 
```

## DynamoDB JavaScript Shellを使って見てみる

http://localhost:8000/shell/ を開いてDynamoDB JavaScript ShellでDBにテーブルが作られたことを確認します。
こちらがテーブルの一覧を表示させるAPIです。

```js
dynamodb.listTables(params, function(err, data) {
    if (err) console.log(err, err.stack); // an error occurred
    else     console.log(data);           // successful response
});
```

実行するとこのように表示されて、Musicテーブルが作られたことが確認できます。

```json
{"TableNames":["Music"]}
```

また、こちらのAPIを実行するとMusicテーブルの中身を覗けます。

```js
var params = {
    TableName: 'Music',
};
dynamodb.scan(params, function(err, data) {
    if (err) print(err); // an error occurred
    else print(data); // successful response
});
```

まだ何もありません。

```json
{
  "Items": [],
  "Count": 0,
  "ScannedCount": 0
}
```

# AWS SDK for C++を使ってDynamoDB Localにputする

さて、AWS SDK for C++を使ってputしようと思いますが、そのための実行環境として[拙作のこちらのイメージ](https://hub.docker.com/r/d9magai/awssdkcpp/)を使おうと思います。
※余談
このイメージをDocker Hubでビルドしますと現状、必ず失敗します。ライブラリをmakeするときにメモリを使い過ぎるのが原因かな、と思ってます。1ヶ月くらい前の時点では失敗したり成功したりでしたが、今は何回ビルドしても成功しません。なんとかして解決したいものです。

## コンテナを起動してプログラムを実行

まずはコンテナを起動します。

```bash
$ sudo docker run -i --link localdynamodb:localdynamodb -t d9magai/awssdkcpp bash
```

コンテナを起動したら`prog.cpp`を書きます。Musicテーブルに歌手名と曲名と再生回数をputするプログラムです。

```cpp:prog.cpp
#include <aws/core/Aws.h>
#include <aws/dynamodb/DynamoDBClient.h>
#include <aws/dynamodb/model/PutItemRequest.h>
#include <aws/dynamodb/model/AttributeValueValue.h>
#include <aws/core/auth/AWSCredentialsProvider.h>
#include <aws/core/utils/Outcome.h>

const Aws::String AWS_ACCESS_KEY_ID = "XXXXXXXXXXXXXXXXXXXX";
const Aws::String AWS_SECRET_ACCESS_KEY = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";

int main(int argc, char** argv) {

    try {
        Aws::SDKOptions  options;
        Aws::InitAPI(options);

        Aws::Client::ClientConfiguration config;
        config.scheme = Aws::Http::Scheme::HTTP;
        config.connectTimeoutMs = 1000;
        config.requestTimeoutMs = 1000;
        config.endpointOverride = "localdynamodb:8000";
        config.region = Aws::Region::AP_NORTHEAST_1;
        auto dynamoDbClient = Aws::DynamoDB::DynamoDBClient(Aws::Auth::AWSCredentials(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY), config);

        Aws::DynamoDB::Model::PutItemRequest putItemRequest = Aws::DynamoDB::Model::PutItemRequest()
        .WithTableName("Music")
        .AddItem("Artist", Aws::DynamoDB::Model::AttributeValue().SetS("aiko"))
        .AddItem("SongTitle", Aws::DynamoDB::Model::AttributeValue().SetS("アンドロメダ"))
        .AddItem("Views", Aws::DynamoDB::Model::AttributeValue().SetN("3002"));

        auto putItemOutcome = dynamoDbClient.PutItem(putItemRequest);
        if(!putItemOutcome.IsSuccess()) {
            std::stringstream ss;
            ss << "PutItem failed with error: " << putItemOutcome.GetError().GetMessage() << std::endl;
            throw std::runtime_error(ss.str());
        }
        std::cout << "PutItem Success" << std::endl;
        Aws::ShutdownAPI(options);
    } catch (const std::exception &e) {
        std::cerr << e.what();
        return 1;
    }

    return 0;
}

```

接続先がDynamoDB Localなので、アクセスキーもシークレットキーもダミーで構いません。
コンパイルして実行して`PutItem Success`と表示されれば成功です。

```bash
$ g++ -DAWS_CUSTOM_MEMORY_MANAGEMENT -std=c++11 prog.cpp -I/opt/aws-sdk-cpp/include -L/opt/aws-sdk-cpp/lib/linux/intel64/ -laws-cpp-sdk-core -laws-cpp-sdk-dynamodb
$ ./a.out
PutItem Success
```
## DynamoDB JavaScript ShellでMusicテーブルの中を見てみる

Musicテーブルの中身を確認し、ちゃんとデータがputされてるか見てみます。
http://localhost:8000/shell/ をもう一度開き、こちらのAPIでMusicテーブルをscanします。

```js
var params = {
    TableName: 'Music',
};
dynamodb.scan(params, function(err, data) {
    if (err) print(err); // an error occurred
    else print(data); // successful response
});
```

ちゃんと登録されていたら、こんな感じで表示されます。

```json
{
  "Items": [
    {
      "Artist": "aiko",
      "SongTitle": "アンドロメダ",
      "Views": 3002
    }
  ],
  "Count": 1,
  "ScannedCount": 1
}
```

# DynamoDB LocalとAWS SDK for C++をdockerで動かせた

無事に動かせました。
ちなみにアクセスキー、シークレットキーと`Aws::Client::ClientConfiguration config;`の値を正しく設定すればLocalではないほうのDynamoDBにも接続できます。今のところ私がAWS SDK for C++で使ったことがあるのはS3,DynamoDBくらいなので、もっと勉強していろいろ使ってみようと思います。


参考
https://aws.amazon.com/jp/blogs/aws/introducing-the-aws-sdk-for-c/