RDSのDownloadCompleteDBLogFile(非クエリAPI)とDownloadDBLogFilePortion(クエリAPI)


#はじめに

RDSのログをダウンロードする権限つけたろーと思うと、何か2つありますね。

- `rds:DownloadCompleteDBLogFile`
- `rds:DownloadDBLogFilePortion`

まずはポリシージェネレータとかで見ますよね。

![Policy Generator - awspolicygen.s3.amazonaws.com.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155963/72d624ab-3ef7-3973-8247-2d03422dcfb5.png)

https://awspolicygen.s3.amazonaws.com/policygen.html
やっぱり2つありますね。

「よくわからんけど両方つけとくかー」が許されない場合があるので、両者の違いを確認します。

#DownloadDBLogFilePortionとは

APIリファレンスは以下です。
https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_DownloadDBLogFilePortion.html

このAPI(に相当するもの)をCLIを用いて実行する場合の説明については、以下の記載があります。

>データベースログファイルをダウンロードするには、AWS CLI の `download-db-log-file-portion` コマンドを使用します。デフォルトでは、このコマンドによってログファイルの最新部分のみがダウンロードされます。ただし、`--starting-token 0` パラメータを指定して、ファイル全体をダウンロードすることもできます。

[Amazon RDS データベースログファイル]
https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/USER_LogAccess.html#USER_LogAccess.Procedural.Downloading

「ファイル全体をダウンロードすることもできます。」という文言だけを素直に受け止めると痛い目に合うので、一応深掘りしておきます。せっかくなのでCLIリファレンスから。

[CLIリファレンス]
https://docs.aws.amazon.com/cli/latest/reference/rds/download-db-log-file-portion.html

ダウンロードする際のログのサイズに1MBの上限があります。RDSのログを、サイズ上限1MBでローテートするようになっていればよいですが、そういった設定ができないエンジンもあるので、考慮が必要です。

>`Description`
Downloads all or a portion of the specified log file, up to 1 MB in size.

1MBで達した時点で残りは切り捨てられて、「どこまでダウンロードしたか」というのがTokenとして返ってくる。次回コマンド発行時に「この続きからダウンロードして」とTokenを渡してあげれば、切り捨てられた分がダウンロードできる。という挙動だと思っていますが、自信ないです。

>`--starting-token (string)`
A token to specify where to start paginating. This is the NextToken from a previously truncated response.

#DownloadCompleteDBLogFileとは

APIリファレンスで確認できません。RDSのAPIリファレンスのActionsの一覧に載っていないのです。
https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_Operations.html

当然CLIリファレンスにもありません。
これは一体どういうことなのか。

#クエリAPIと非クエリAPI

とりあえずいろんな段階をすっとばして以下を読んでみます。

>この要素では、ウェブサービスを実行するアクション (例: DynamoDB CreateTable アクションまたは Amazon EC2 の DescribeInstances アクション) を指定します。指定されたアクションによって、リクエスト内で使用されるパラメータが決まります。 **クエリ API では、アクションは API 名です。非クエリ API (例: RESTful API)については、サービスドキュメントで正しいアクションを参照してください。**

[AWS 署名バージョン 4 のリクエストの要素]
https://docs.aws.amazon.com/ja_jp/general/latest/gr/sigv4_elements.html#sigv4_elements_action

皆さんは意味が分かりましたか？私は分かりませんでした。

##RDSのクエリAPI

クエリAPIという聞きなれないワードが出てきたので、「`クエリAPI　RDS`」でググります。ドキュメントがありました。

>HTTP クエリベースのリクエストとは、HTTP 動詞 (GET または POST) とクエリパラメータ Action で記述する HTTP リクエストです。各クエリリクエストに、アクションの認証と選択を処理するための一般的なパラメータがいくつか含まれている必要があります。

[クエリ API の使用]
https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/Using_the_Query_API.html

クエリパラメータとは、クエリ文字列、クエリストリングとも呼ばれるもので、簡単に言えば「ブラウザのアドレスバーでURLを見た時に`?`の後についているもの」です。

皆さんもよくQiitaで「**お金**」と検索すると思いますが、その時にもクエリパラメータで「`q=お金`」が付与されていることが分かります。

![クエリパラメータ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155963/90f6d1cf-f263-87fc-5604-2b3aee7c865b.png)


APIリファレンスからサンプルを引っ張ってくると、クエリパラメータ`Action`に`DownloadDBLogFilePortion`が指定されていることが分かります。「**クエリ API では、アクションは API 名です。**」というのはこういったことを意味していたのだとようやく理解します。

```text:SampleRequest
https://rds.us-west-2.amazonaws.com/
  ?Action=DownloadDBLogFilePortion
  &DBInstanceIdentifier=myexampledb
  &LogFileName=log%2FERROR
  &Marker=0
  &NumberOfLines=50
  &Version=2014-09-01
  &X-Amz-Algorithm=AWS4-HMAC-SHA256
  &X-Amz-Credential=AKIADQKE4SARGYLE/20140127/us-west-2/rds/aws4_request
  &X-Amz-Date=20140127T235259Z
  &X-Amz-SignedHeaders=content-type;host;user-agent;x-amz-content-sha256;x-amz-date
  &X-Amz-Signature=2171c5a8e91a70202e77de7e81df75787f3bbd6b4ea97f7a426205474fcc446f
```

##RDSの非クエリAPI

`DownloadCompleteDBLogFile`（っぽいもの）の記述は、例えば以下にあります。

[REST を用いたログファイルの内容の読み取り]
https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/USER_LogAccess.html#DownloadCompleteDBLogFile

>Amazon RDS では、DB インスタンスのログファイルへのアクセスを許可する REST エンドポイントを使用できます。これは、Amazon RDS ログファイルの内容を取り出すアプリケーションを作成される場合に有用です。

サンプルは以下の通りです。クエリパラメータとして`Action`を定義してAPIを書くようになって**いない**ことが分かります。`downloadCompleteLogFile`がURIの中のパスとして指定されています。

```text:downloadCompleteLogFile
GET /v13/downloadCompleteLogFile/sample-sql/log/ERROR.6 HTTP/1.1
host: rds.us-west-2.amazonaws.com
X-Amz-Security-Token: AQoDYXdzEIH//////////wEa0AIXLhngC5zp9CyB1R6abwKrXHVR5efnAVN3XvR7IwqKYalFSn6UyJuEFTft9nObglx4QJ+GXV9cpACkETq=
X-Amz-Date: 20140903T233749Z
X-Amz-Algorithm: AWS4-HMAC-SHA256
X-Amz-Credential: AKIADQKE4SARGYLE/20140903/us-west-2/rds/aws4_request
X-Amz-SignedHeaders: host
X-Amz-Content-SHA256: e3b0c44298fc1c229afbf4c8996fb92427ae41e4649b934de495991b7852b855
X-Amz-Expires: 86400
X-Amz-Signature: 353a4f14b3f250142d9afc34f9f9948154d46ce7d4ec091d0cdabbcf8b40c558
```

「downloadComplete`db`LogFile」でなく「downloadCompleteLogFile」なのが少し気になるところです。

###「RESTを用いたログファイル内容の読み取り」はDownloadCompleteDBLogFileのAPIなのか？

以下のページを見る限りは、過去には正式にRESTのAPIとしての`DownloadCompleteDBLogFile`のリファレンスが用意されていたように見受けられます。

- [downloading RDS logs; DownloadCompleteDBLogFile missing?](https://github.com/aws/aws-sdk-js/issues/1292)
- [[Tips] Amazon RDSのログファイルから全てのメッセージを取得する方法](https://dev.classmethod.jp/cloud/aws/amazon-rds-logs-download-tips/)

`ttp://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/RESTReference.DownloadCompleteDBLogFile.html`※現状該当ページは存在せず

当時のリファレンスの記載内容は分かりませんが、それを参考に実施している内容は「RESTを用いたログファイル内容の読み取り」と同一のようです。

`method="GET", url="https://rds.ap-northeast-1.amazonaws.com/v13/downloadCompleteLogFile/db/general/mysql-general.log"`

また、「RESTを用いたログファイル内容の読み取り」のドキュメントのURLにおいて、アンカー(`#`で付与されているもの)が「DownloadCompleteDBLogFile」となっています。

`https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/USER_LogAccess.html#DownloadCompleteDBLogFile`

上記を踏まえて、「RESTを用いたログファイル内容の読み取り」は「DownloadCompleteDBLogFile」のAPIを用いたものだと考えています。

#マネジメントコンソールからRDSログをダウンロードする時に使用されているのは？

実際にダウンロードを試したのち、Cloud Trailのイベントから確認してみました。

`"eventName": "DownloadCompleteDBLogFile",`が記録されていることを確認できました。

```json:Trailイベント
{
    "eventVersion": "1.05",
    "userIdentity": {
        "type": "IAMUser",
        "principalId": "AIDAxxxxxxxxxWIJWHT24",
        "arn": "arn:aws:iam::000000000000:user/xxuserxx",
        "accountId": "000000000000",
        "accessKeyId": "ASIxxxxxxxHUxxMZDISG",
        "userName": "xxuserxx",
        "sessionContext": {
            "attributes": {
                "mfaAuthenticated": "false",
                "creationDate": "2019-11-05T00:32:52Z"
            }
        },
        "invokedBy": "signin.amazonaws.com"
    },
    "eventTime": "2019-11-05T00:44:50Z",
    "eventSource": "rds.amazonaws.com",
    "eventName": "DownloadCompleteDBLogFile",
    "awsRegion": "ap-northeast-1",
    "sourceIPAddress": "xx0.1xx.x9.6x",
    "userAgent": "signin.amazonaws.com",
    "requestParameters": {
        "dBInstanceIdentifier": "temp-rds-logcheck",
        "logFileName": "audit/server_audit.log.1",
        "acceptEncoding": "gzip, deflate, br"
    },
    "responseElements": null,
    "requestID": "a3xxxxxb-7xx9-4xx3-axxc-3xxxxxxx7ad9",
    "eventID": "e4xxx404-7xxc-4xx8-bxxc-fxxxxxxx6cd2",
    "eventType": "AwsApiCall",
    "recipientAccountId": "000000000000"
}
```

##ちなみに「ビュー」や「見る」を選択した場合は

ログをダウンロードせずに、マネジメントコンソール上で表示させることもできます。
「ビュー」だと単純なログの表示、「見る」を選択した場合は5秒ごとに最新ログを読み込み直す挙動となります。

両者とも`"eventName": "DownloadDBLogFilePortion",`が記録されていました。

```json:ビュー
{
    "eventVersion": "1.05",
    "userIdentity": {
        "type": "IAMUser",
        "principalId": "AIXXXXXXXXXI7WIJXXX24",
        "arn": "arn:aws:iam::000000000000:user/testuser",
        "accountId": "000000000000",
        "accessKeyId": "ASXXXXXXXXXXDHXXXXXU",
        "userName": "testuser",
        "sessionContext": {
            "attributes": {
                "mfaAuthenticated": "false",
                "creationDate": "2019-12-02T10:32:57Z"
            }
        },
        "invokedBy": "signin.amazonaws.com"
    },
    "eventTime": "2019-12-02T11:23:52Z",
    "eventSource": "rds.amazonaws.com",
    "eventName": "DownloadDBLogFilePortion",
    "awsRegion": "ap-northeast-1",
    "sourceIPAddress": "xx0.xxx.x9.xx",
    "userAgent": "signin.amazonaws.com",
    "requestParameters": {
        "dBInstanceIdentifier": "testdbinstance",
        "logFileName": "audit/server_audit.log.1",
        "numberOfLines": 1000,
        "marker": "0"
    },
    "responseElements": null,
    "requestID": "4xxxxx41-7xx7-4xxx-axx8-exxxxxxxx568",
    "eventID": "axxxxx90-6xx2-4cxx-xx7c-85xxxxxxx113",
    "eventType": "AwsApiCall",
    "recipientAccountId": "000000000000"
}
```

「見る」の場合は、ほぼほぼ同じイベントが出力されていますが、`marker`が動的に渡されているように見えました。

```json:見る(一部抜粋)
    "requestParameters": {
        "dBInstanceIdentifier": "testdbinstance",
        "logFileName": "audit/server_audit.log.1",
        "numberOfLines": 1000,
        "marker": "12:1000029"
    },
```



#改めてDownloadCompleteDBLogFileとは

`DownloadDBLogFilePortion`と異なり、サイズの制限などが無く、全量を取得できるAPIです。（詳細な仕様等は、公式のリファレンスが見つけられませんでした。）
CLIやSDKなどでは該当するものが用意されていないため、RESTを用いたAPIを実行するか、マネジメントコンソールからの操作を行う必要があります。RESTを用いてAPIを実行する際には、AWS APIリクエストの署名などを意識する必要があります。

[AWSAPI リクエストの署名]
https://docs.aws.amazon.com/ja_jp/general/latest/gr/signing_aws_api_requests.html

>**リクエストを署名する必要がある場合**
AWS に HTTP リクエストを送るためのカスタムコードを書く場合、リクエストの署名にコードを含める必要があります。次の理由から、これが必要になる場合があります。
・AWS SDK がないプログラミング言語を使用しているためです。
・AWS にリクエストを送る方法を完全に管理する場合。

「CLIやSDKに用意されていないAPIを利用したい場合。」もありますね。

>AWS Command Line Interface (AWS CLI) または AWS SDK の 1 つを使う場合には、リクエストに署名する必要はありません。これらのツールは、署名の計算、リクエストの再試行処理、エラー処理など、接続の詳細を管理します。また多くの場合、SDK には、AWS とやりとりするアプリケーションの作成を開始するのに役立つ、サンプルコード、チュートリアルなどのリソースも含まれています。

普段は面倒なことを全部やってくれるCLIやAWS SDK、ありがとう…。

#おわりに

`rds:DownloadCompleteDBLogFile`はマネジメントコンソールからログをダウンロードしたり、REST APIでログを取得する際に必要。
`rds:DownloadDBLogFilePortion`はマネジメントコンソールからログを参照したり、CLIやSDKでログのダウンロードを実施する時に必要。ということが分かりました。

#おわりのおわりに

のっぴきならない背景があるのでしょうが、`rds:DownloadCompleteDBLogFile`がCLIやSDKに対応していると嬉しかったですね。

`DownloadCompleteDBLogFile`のAPIの実行を含むプログラムをLambda（ランタイムpython3.6）で実行し、署名まわりを外部ライブラリ(botocore)を使用して解決していた際に、痛い目に合いました。。botocoreの公式リファレンスに記載されているもの**以外**の内部クラスなどは、たまたま使うことができる状態だが、AWSがバージョンを担保するものではない。Lambdaの実行環境は不定期でアップデートされる可能性があり、ユーザの直接利用を想定していないものはアナウンスなしで行われる。バージョンアップにより一部モジュールが未サポートになり、そこをimportするコードを載せていたので、エラーが発生する。。という事象でした。デプロイパッケージやレイヤなどを使用して、必要なモジュールを固定化しておく対応が必要でした。（もしそれが許されないとするとどう対応する…？外部ライブラリをimportしないコードに改修するしかないですね。）

作りこみで対応するのではなく、公式が提供してくれるものにこちらが合わせるのが大抵の場合は正しいアプローチですね。。