はじめに
ローカルではAWS CLIのconfig設定(設定の基本を参照)をしていたので問題にならなかったが、CIでテスト実行しようとした時に、SDKのクライアントのコンストラクタでクレデンシャルを設定しようとしてハマったので、気を付けたいポイントについて備忘録を残す。
※以下ではCI上でSDKのクライアントのインスタンスをmock化しないで利用しているが、CI環境でテストするのにそもそもSDKをそのまま動かしているのはおかしいのでは…?という突っ込みが当然あると思う。結合でテストしたい場面ではないのであれば、単体テストとして実装するので本来はmockにすべきだが、今回は敢えてmockにしないとどういう事が起きるのか?mockにしないでテスト=単体テストではなく結合テストをCI環境で簡単に実行できるのか?を検証してみたかったので、モックではなく、SDKをそのまま使うという事をした。
結論 どうすればいいか?
以下のように、accessKeyId/secretAccessKey
を指定する。
new DynamoDBClient({
// 省略
credentials: { accessKeyId: 'dumy', secretAccessKey: 'dumy' }
});
※エラーが出たconfig設定は以下。
new DynamoDBClient({
// 省略
credentials: { AccessKeyId: 'dumy', SecretAccessKey: 'dumy' }
});
以下で解決に至る経緯を見ていく。
経緯
①CredentialsProviderError: Could not load credentials from any providers
というエラーになる
まずCredentialsProviderError: Could not load credentials from any providers
というエラーが出た。
これは単純にローカルではAWS CLIを利用していた関係でNode.js での認証情報の設定にあるように、~/.aws/credentials
からクレデンシャルを読み込んで動いていたが、CIではそもそもその設定がないのでクレデンシャルがなく、エラーになったというだけ。
というわけでクライアントのインスタンスを生成する際に、引数にクレデンシャルを渡す事にした。
※実際にエラーが出たGitHub ActionsのJobは以下。
②TypeError: Cannot read properties of undefined (reading 'byteLength')
というエラーになる
①の解決のために、クレデンシャルを渡せばいいと思い、以下の修正を行った。
がしかし、今度はTypeError: Cannot read properties of undefined (reading 'byteLength')
というエラーが出た…。エラーの内容をそのまま素直に読んでも??になり、ググると同じような事で躓いている人がいたので見てみると、以下のようなサイトがあった。
どうやら解決方法としては、キーをcamelCaseにすればいいという事らしい。
before | after |
---|---|
この修正をして再度CIを回してみると確かにエラーは解消された(他のエラーは出ているが)。
が、一体全体ケースが違うだけの単語は全く同じキーがあるのは何なのか…?
次の章でその正体についてみていく。
accessKeyId/secretAccessKey vs AccessKeyId/SecretAccessKey 2つの違いは何?
今回はDynamoDBのクライアントを生成しようとしてたが、そのクライアントの生成時にコンストラクタの引数に渡すconfigについてはClass DynamoDBClientからDynamoDBClientConfigである事が分かり、さらにそのconfigにはcredentialsというキーがある事が分かる。
このcredentials(Namespace Credentials)を見てみると、以下の画像のように問題になっているケースが違うだけの同じ名前のキーがある事が分かる。
今回問題になっているのは上記の2つがそれぞれ何者なのか?という事。リファレンスの説明を読むと、
- AccessKeyId
The access key ID that identifies the temporary security credentials.(一時的なセキュリティ認証情報を識別するためのアクセスキーID)
Defined in clients/client-sts/dist-types/models/models_0.d.ts:283 - SecretAccessKey
The secret access key that can be used to sign requests.(リクエストに署名するために使用できる秘密のアクセスキー)
Defined in clients/client-sts/dist-types/models/models_0.d.ts:287
と書かれており、一時的なセキュリティ認証情報
やclient-sts
と書かれているではないか?!という事に気づく。
ここまで行くと察しがつき、AccessKeyId/SecretAccessKey
はAWS STSの一時的なセキュリティ認証情報を使用するためにAssumeRole
APIなどを呼んだ際に返ってくるCredentialsのキーという事が分かる。
公式で言うとAWS SDK での一時的セキュリティ認証情報の使用やAssumeRole Response Elementsに書かれている 。
assumeRoleResult = AssumeRole(role-arn);
tempCredentials = new SessionAWSCredentials(
assumeRoleResult.AccessKeyId, // <- ここで出てくる`AccessKeyId`の事
assumeRoleResult.SecretAccessKey, // <- ここで出てくる`SecretAccessKey`の事
assumeRoleResult.SessionToken);
s3Request = CreateAmazonS3Client(tempCredentials);
つまり、AccessKeyId/SecretAccessKey
の2つはSDKにクレデンシャル情報を渡すためのキーではなく、AWS STSから返されるCredentialsのキーを表している。なので、SDKのクライアントを生成するために、コンストラクタに渡すべきクレデンシャル情報は以下のように、accessKeyId/secretAccessKey
で書く必要がある。
"credentials": {
"accessKeyId": "dumy",
"secretAccessKey": "dumy"
}
まとめとして
AWSさん、ドキュメントは分かりやすくして頂けると嬉しいです…(´;ω;`)と思ったのでした。少なくともNamespace Credentialsのページに、AccessKeyId/SecretAccessKey
が書いてあるのは混乱を招くかと…。
※ちなみに、今回の記事ではSDKのクライアントのコンストラクタにクレデンシャルを渡していたが、別にそんな事をせず、環境変数でAWSクレデンシャルを設定すればそれで済むという話でもある(Node.js での認証情報の設定を参照)。
// 以下のように"credentials"を渡さずとも、環境変数で設定する方法もある
client.dynamodb = new DynamoDBClient({
region: 'local-env',
endpoint: 'http://localhost:8000',
credentials: { accessKeyId: 'dumy', secretAccessKey: 'dumy' }
});