search
LoginSignup
0
Help us understand the problem. What are the problem?

posted at

TypeError: Cannot read properties of undefined (reading 'byteLength') AWS SDK for JavaScript v3で少しハマった話

はじめに

ローカルでは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')というエラーになる

①の解決のために、クレデンシャルを渡せばいいと思い、以下の修正を行った。
image.png

がしかし、今度はTypeError: Cannot read properties of undefined (reading 'byteLength')というエラーが出た…。エラーの内容をそのまま素直に読んでも??になり、ググると同じような事で躓いている人がいたので見てみると、以下のようなサイトがあった。

どうやら解決方法としては、キーをcamelCaseにすればいいという事らしい。

before after
image.png image.png

この修正をして再度CIを回してみると確かにエラーは解消された(他のエラーは出ているが)。

が、一体全体ケースが違うだけの単語は全く同じキーがあるのは何なのか…?

次の章でその正体についてみていく。

accessKeyId/secretAccessKey vs AccessKeyId/SecretAccessKey 2つの違いは何?

今回はDynamoDBのクライアントを生成しようとしてたが、そのクライアントの生成時にコンストラクタの引数に渡すconfigについてはClass DynamoDBClientからDynamoDBClientConfigである事が分かり、さらにそのconfigにはcredentialsというキーがある事が分かる。

このcredentials(Namespace Credentials)を見てみると、以下の画像のように問題になっているケースが違うだけの同じ名前のキーがある事が分かる。
image.png

今回問題になっているのは上記の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の一時的なセキュリティ認証情報を使用するためにAssumeRoleAPIなどを呼んだ際に返ってくる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' }
		});

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
0
Help us understand the problem. What are the problem?