@aws-sdk/ec2-metadata-service 3.952.0 → 3.954.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-cjs/index.js +93 -42
- package/dist-es/MetadataService.js +93 -42
- package/dist-types/MetadataService.d.ts +7 -0
- package/dist-types/MetadataServiceOptions.d.ts +10 -0
- package/dist-types/ts3.4/MetadataService.d.ts +7 -0
- package/dist-types/ts3.4/MetadataServiceOptions.d.ts +2 -0
- package/package.json +9 -9
package/dist-cjs/index.js
CHANGED
|
@@ -40,6 +40,8 @@ const IMDSv1_DISABLED_SELECTORS = {
|
|
|
40
40
|
class MetadataService {
|
|
41
41
|
disableFetchToken;
|
|
42
42
|
config;
|
|
43
|
+
retries;
|
|
44
|
+
backoffFn;
|
|
43
45
|
constructor(options = {}) {
|
|
44
46
|
this.config = (async () => {
|
|
45
47
|
const profile = options?.profile || process.env.AWS_PROFILE;
|
|
@@ -52,58 +54,106 @@ class MetadataService {
|
|
|
52
54
|
};
|
|
53
55
|
})();
|
|
54
56
|
this.disableFetchToken = options?.disableFetchToken || false;
|
|
57
|
+
this.retries = options?.retries ?? 3;
|
|
58
|
+
this.backoffFn = this.createBackoffFunction(options?.backoff);
|
|
55
59
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
});
|
|
63
|
-
const endpointUrl = new URL(endpoint);
|
|
64
|
-
const headers = options.headers || {};
|
|
65
|
-
if (this.disableFetchToken && ec2MetadataV1Disabled) {
|
|
66
|
-
throw new Error("IMDSv1 is disabled and fetching token is disabled, cannot make the request.");
|
|
60
|
+
createBackoffFunction(backoff) {
|
|
61
|
+
if (typeof backoff === "function") {
|
|
62
|
+
return backoff;
|
|
63
|
+
}
|
|
64
|
+
if (typeof backoff === "number") {
|
|
65
|
+
return () => backoff;
|
|
67
66
|
}
|
|
68
|
-
|
|
67
|
+
return (numFailures) => Math.pow(1.2, numFailures);
|
|
68
|
+
}
|
|
69
|
+
sleep(ms) {
|
|
70
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
71
|
+
}
|
|
72
|
+
async retryWithBackoff(operation) {
|
|
73
|
+
let lastError;
|
|
74
|
+
for (let attempt = 0; attempt <= this.retries; attempt++) {
|
|
69
75
|
try {
|
|
70
|
-
|
|
76
|
+
return await operation();
|
|
71
77
|
}
|
|
72
|
-
catch (
|
|
73
|
-
|
|
74
|
-
|
|
78
|
+
catch (error) {
|
|
79
|
+
lastError = error;
|
|
80
|
+
if (attempt === this.retries) {
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
if (this.shouldNotRetry(error)) {
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
const backoffResult = this.backoffFn(attempt);
|
|
87
|
+
if (typeof backoffResult === "number") {
|
|
88
|
+
await this.sleep(backoffResult * 1000);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
await backoffResult;
|
|
75
92
|
}
|
|
76
93
|
}
|
|
77
94
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
95
|
+
throw lastError;
|
|
96
|
+
}
|
|
97
|
+
shouldNotRetry(error) {
|
|
98
|
+
const statusCode = error.statusCode || error.$metadata?.httpStatusCode;
|
|
99
|
+
return statusCode === 400 || statusCode === 403 || statusCode === 404;
|
|
100
|
+
}
|
|
101
|
+
async request(path, options) {
|
|
102
|
+
return this.retryWithBackoff(async () => {
|
|
103
|
+
const { endpoint, ec2MetadataV1Disabled, httpOptions } = await this.config;
|
|
104
|
+
const handler = new nodeHttpHandler.NodeHttpHandler({
|
|
105
|
+
requestTimeout: httpOptions?.timeout,
|
|
106
|
+
throwOnRequestTimeout: true,
|
|
107
|
+
connectionTimeout: httpOptions?.timeout,
|
|
108
|
+
});
|
|
109
|
+
const endpointUrl = new URL(endpoint);
|
|
110
|
+
const headers = options.headers || {};
|
|
111
|
+
if (this.disableFetchToken && ec2MetadataV1Disabled) {
|
|
112
|
+
throw new Error("IMDSv1 is disabled and fetching token is disabled, cannot make the request.");
|
|
90
113
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
114
|
+
if (!this.disableFetchToken) {
|
|
115
|
+
try {
|
|
116
|
+
headers["x-aws-ec2-metadata-token"] = await this.fetchMetadataTokenInternal();
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
if (ec2MetadataV1Disabled) {
|
|
120
|
+
throw err;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
95
123
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
124
|
+
const request = new protocolHttp.HttpRequest({
|
|
125
|
+
method: options.method || "GET",
|
|
126
|
+
headers: headers,
|
|
127
|
+
hostname: endpointUrl.hostname,
|
|
128
|
+
path: endpointUrl.pathname + path,
|
|
129
|
+
protocol: endpointUrl.protocol,
|
|
130
|
+
port: endpointUrl.port ? parseInt(endpointUrl.port) : undefined,
|
|
131
|
+
});
|
|
132
|
+
try {
|
|
133
|
+
const { response } = await handler.handle(request, {});
|
|
134
|
+
if (response.statusCode === 200 && response.body) {
|
|
135
|
+
return utilStream.sdkStreamMixin(response.body).transformToString();
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
throw Object.assign(new Error(`Request failed with status code ${response.statusCode}`), {
|
|
139
|
+
$metadata: { httpStatusCode: response.statusCode },
|
|
140
|
+
});
|
|
141
|
+
}
|
|
102
142
|
}
|
|
103
|
-
|
|
104
|
-
|
|
143
|
+
catch (error) {
|
|
144
|
+
const wrappedError = new Error(`Error making request to the metadata service: ${error}`);
|
|
145
|
+
const { $metadata } = error;
|
|
146
|
+
if ($metadata?.httpStatusCode !== undefined) {
|
|
147
|
+
Object.assign(wrappedError, { $metadata });
|
|
148
|
+
}
|
|
149
|
+
throw wrappedError;
|
|
150
|
+
}
|
|
151
|
+
});
|
|
105
152
|
}
|
|
106
153
|
async fetchMetadataToken() {
|
|
154
|
+
return this.retryWithBackoff(() => this.fetchMetadataTokenInternal());
|
|
155
|
+
}
|
|
156
|
+
async fetchMetadataTokenInternal() {
|
|
107
157
|
const { endpoint, httpOptions } = await this.config;
|
|
108
158
|
const handler = new nodeHttpHandler.NodeHttpHandler({
|
|
109
159
|
requestTimeout: httpOptions?.timeout,
|
|
@@ -132,12 +182,13 @@ class MetadataService {
|
|
|
132
182
|
}
|
|
133
183
|
else {
|
|
134
184
|
throw Object.assign(new Error(`Failed to fetch metadata token with status code ${response.statusCode}`), {
|
|
135
|
-
|
|
185
|
+
$metadata: { httpStatusCode: response.statusCode },
|
|
136
186
|
});
|
|
137
187
|
}
|
|
138
188
|
}
|
|
139
189
|
catch (error) {
|
|
140
|
-
if (error.message === "TimeoutError" ||
|
|
190
|
+
if (error.message === "TimeoutError" ||
|
|
191
|
+
[403, 404, 405].includes(error.statusCode || error.$metadata?.httpStatusCode)) {
|
|
141
192
|
this.disableFetchToken = true;
|
|
142
193
|
throw new Error(`Error fetching metadata token: ${error}. [disableFetchToken] is now set to true.`);
|
|
143
194
|
}
|
|
@@ -6,6 +6,8 @@ import { ENDPOINT_SELECTORS, IMDSv1_DISABLED_SELECTORS } from "./ConfigLoaders";
|
|
|
6
6
|
export class MetadataService {
|
|
7
7
|
disableFetchToken;
|
|
8
8
|
config;
|
|
9
|
+
retries;
|
|
10
|
+
backoffFn;
|
|
9
11
|
constructor(options = {}) {
|
|
10
12
|
this.config = (async () => {
|
|
11
13
|
const profile = options?.profile || process.env.AWS_PROFILE;
|
|
@@ -18,58 +20,106 @@ export class MetadataService {
|
|
|
18
20
|
};
|
|
19
21
|
})();
|
|
20
22
|
this.disableFetchToken = options?.disableFetchToken || false;
|
|
23
|
+
this.retries = options?.retries ?? 3;
|
|
24
|
+
this.backoffFn = this.createBackoffFunction(options?.backoff);
|
|
21
25
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
});
|
|
29
|
-
const endpointUrl = new URL(endpoint);
|
|
30
|
-
const headers = options.headers || {};
|
|
31
|
-
if (this.disableFetchToken && ec2MetadataV1Disabled) {
|
|
32
|
-
throw new Error("IMDSv1 is disabled and fetching token is disabled, cannot make the request.");
|
|
26
|
+
createBackoffFunction(backoff) {
|
|
27
|
+
if (typeof backoff === "function") {
|
|
28
|
+
return backoff;
|
|
29
|
+
}
|
|
30
|
+
if (typeof backoff === "number") {
|
|
31
|
+
return () => backoff;
|
|
33
32
|
}
|
|
34
|
-
|
|
33
|
+
return (numFailures) => Math.pow(1.2, numFailures);
|
|
34
|
+
}
|
|
35
|
+
sleep(ms) {
|
|
36
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
37
|
+
}
|
|
38
|
+
async retryWithBackoff(operation) {
|
|
39
|
+
let lastError;
|
|
40
|
+
for (let attempt = 0; attempt <= this.retries; attempt++) {
|
|
35
41
|
try {
|
|
36
|
-
|
|
42
|
+
return await operation();
|
|
37
43
|
}
|
|
38
|
-
catch (
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
catch (error) {
|
|
45
|
+
lastError = error;
|
|
46
|
+
if (attempt === this.retries) {
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
if (this.shouldNotRetry(error)) {
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
const backoffResult = this.backoffFn(attempt);
|
|
53
|
+
if (typeof backoffResult === "number") {
|
|
54
|
+
await this.sleep(backoffResult * 1000);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
await backoffResult;
|
|
41
58
|
}
|
|
42
59
|
}
|
|
43
60
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
61
|
+
throw lastError;
|
|
62
|
+
}
|
|
63
|
+
shouldNotRetry(error) {
|
|
64
|
+
const statusCode = error.statusCode || error.$metadata?.httpStatusCode;
|
|
65
|
+
return statusCode === 400 || statusCode === 403 || statusCode === 404;
|
|
66
|
+
}
|
|
67
|
+
async request(path, options) {
|
|
68
|
+
return this.retryWithBackoff(async () => {
|
|
69
|
+
const { endpoint, ec2MetadataV1Disabled, httpOptions } = await this.config;
|
|
70
|
+
const handler = new NodeHttpHandler({
|
|
71
|
+
requestTimeout: httpOptions?.timeout,
|
|
72
|
+
throwOnRequestTimeout: true,
|
|
73
|
+
connectionTimeout: httpOptions?.timeout,
|
|
74
|
+
});
|
|
75
|
+
const endpointUrl = new URL(endpoint);
|
|
76
|
+
const headers = options.headers || {};
|
|
77
|
+
if (this.disableFetchToken && ec2MetadataV1Disabled) {
|
|
78
|
+
throw new Error("IMDSv1 is disabled and fetching token is disabled, cannot make the request.");
|
|
56
79
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
80
|
+
if (!this.disableFetchToken) {
|
|
81
|
+
try {
|
|
82
|
+
headers["x-aws-ec2-metadata-token"] = await this.fetchMetadataTokenInternal();
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
if (ec2MetadataV1Disabled) {
|
|
86
|
+
throw err;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
61
89
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
90
|
+
const request = new HttpRequest({
|
|
91
|
+
method: options.method || "GET",
|
|
92
|
+
headers: headers,
|
|
93
|
+
hostname: endpointUrl.hostname,
|
|
94
|
+
path: endpointUrl.pathname + path,
|
|
95
|
+
protocol: endpointUrl.protocol,
|
|
96
|
+
port: endpointUrl.port ? parseInt(endpointUrl.port) : undefined,
|
|
97
|
+
});
|
|
98
|
+
try {
|
|
99
|
+
const { response } = await handler.handle(request, {});
|
|
100
|
+
if (response.statusCode === 200 && response.body) {
|
|
101
|
+
return sdkStreamMixin(response.body).transformToString();
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
throw Object.assign(new Error(`Request failed with status code ${response.statusCode}`), {
|
|
105
|
+
$metadata: { httpStatusCode: response.statusCode },
|
|
106
|
+
});
|
|
107
|
+
}
|
|
68
108
|
}
|
|
69
|
-
|
|
70
|
-
|
|
109
|
+
catch (error) {
|
|
110
|
+
const wrappedError = new Error(`Error making request to the metadata service: ${error}`);
|
|
111
|
+
const { $metadata } = error;
|
|
112
|
+
if ($metadata?.httpStatusCode !== undefined) {
|
|
113
|
+
Object.assign(wrappedError, { $metadata });
|
|
114
|
+
}
|
|
115
|
+
throw wrappedError;
|
|
116
|
+
}
|
|
117
|
+
});
|
|
71
118
|
}
|
|
72
119
|
async fetchMetadataToken() {
|
|
120
|
+
return this.retryWithBackoff(() => this.fetchMetadataTokenInternal());
|
|
121
|
+
}
|
|
122
|
+
async fetchMetadataTokenInternal() {
|
|
73
123
|
const { endpoint, httpOptions } = await this.config;
|
|
74
124
|
const handler = new NodeHttpHandler({
|
|
75
125
|
requestTimeout: httpOptions?.timeout,
|
|
@@ -98,12 +148,13 @@ export class MetadataService {
|
|
|
98
148
|
}
|
|
99
149
|
else {
|
|
100
150
|
throw Object.assign(new Error(`Failed to fetch metadata token with status code ${response.statusCode}`), {
|
|
101
|
-
|
|
151
|
+
$metadata: { httpStatusCode: response.statusCode },
|
|
102
152
|
});
|
|
103
153
|
}
|
|
104
154
|
}
|
|
105
155
|
catch (error) {
|
|
106
|
-
if (error.message === "TimeoutError" ||
|
|
156
|
+
if (error.message === "TimeoutError" ||
|
|
157
|
+
[403, 404, 405].includes(error.statusCode || error.$metadata?.httpStatusCode)) {
|
|
107
158
|
this.disableFetchToken = true;
|
|
108
159
|
throw new Error(`Error fetching metadata token: ${error}. [disableFetchToken] is now set to true.`);
|
|
109
160
|
}
|
|
@@ -5,13 +5,20 @@ import { MetadataServiceOptions } from "./MetadataServiceOptions";
|
|
|
5
5
|
export declare class MetadataService {
|
|
6
6
|
private disableFetchToken;
|
|
7
7
|
private config;
|
|
8
|
+
private retries;
|
|
9
|
+
private backoffFn;
|
|
8
10
|
/**
|
|
9
11
|
* Creates a new MetadataService object with a given set of options.
|
|
10
12
|
*/
|
|
11
13
|
constructor(options?: MetadataServiceOptions);
|
|
14
|
+
private createBackoffFunction;
|
|
15
|
+
private sleep;
|
|
16
|
+
private retryWithBackoff;
|
|
17
|
+
private shouldNotRetry;
|
|
12
18
|
request(path: string, options: {
|
|
13
19
|
method?: string;
|
|
14
20
|
headers?: Record<string, string>;
|
|
15
21
|
}): Promise<string>;
|
|
16
22
|
fetchMetadataToken(): Promise<string>;
|
|
23
|
+
private fetchMetadataTokenInternal;
|
|
17
24
|
}
|
|
@@ -27,4 +27,14 @@ export interface MetadataServiceOptions {
|
|
|
27
27
|
* when true, metadata service will not fetch token, which indicates usage of IMDSv1
|
|
28
28
|
*/
|
|
29
29
|
disableFetchToken?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* the number of retry attempts for any failed request, defaulting to 3.
|
|
32
|
+
*/
|
|
33
|
+
retries?: number;
|
|
34
|
+
/**
|
|
35
|
+
* the number of seconds to sleep in-between retries and/or a customer provided backoff function to call.
|
|
36
|
+
* if the function returns a promise, it will be awaited and its resolved value ignored.
|
|
37
|
+
* if the function returns a number, the number will be used as seconds duration to wait before the following retry attempt.
|
|
38
|
+
*/
|
|
39
|
+
backoff?: number | ((numFailures: number) => Promise<void> | number);
|
|
30
40
|
}
|
|
@@ -2,7 +2,13 @@ import { MetadataServiceOptions } from "./MetadataServiceOptions";
|
|
|
2
2
|
export declare class MetadataService {
|
|
3
3
|
private disableFetchToken;
|
|
4
4
|
private config;
|
|
5
|
+
private retries;
|
|
6
|
+
private backoffFn;
|
|
5
7
|
constructor(options?: MetadataServiceOptions);
|
|
8
|
+
private createBackoffFunction;
|
|
9
|
+
private sleep;
|
|
10
|
+
private retryWithBackoff;
|
|
11
|
+
private shouldNotRetry;
|
|
6
12
|
request(
|
|
7
13
|
path: string,
|
|
8
14
|
options: {
|
|
@@ -11,4 +17,5 @@ export declare class MetadataService {
|
|
|
11
17
|
}
|
|
12
18
|
): Promise<string>;
|
|
13
19
|
fetchMetadataToken(): Promise<string>;
|
|
20
|
+
private fetchMetadataTokenInternal;
|
|
14
21
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aws-sdk/ec2-metadata-service",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.954.0",
|
|
4
4
|
"scripts": {
|
|
5
|
-
"build": "concurrently 'yarn:build:
|
|
5
|
+
"build": "concurrently 'yarn:build:types' 'yarn:build:es' && yarn build:cjs",
|
|
6
6
|
"build:cjs": "node ../../scripts/compilation/inline ec2-metadata-service",
|
|
7
7
|
"build:es": "tsc -p tsconfig.es.json",
|
|
8
8
|
"build:include:deps": "lerna run --scope $npm_package_name --include-dependencies build",
|
|
@@ -24,16 +24,16 @@
|
|
|
24
24
|
"module": "./dist-es/index.js",
|
|
25
25
|
"types": "./dist-types/index.d.ts",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@aws-sdk/types": "3.
|
|
28
|
-
"@smithy/node-config-provider": "^4.3.
|
|
29
|
-
"@smithy/node-http-handler": "^4.4.
|
|
30
|
-
"@smithy/protocol-http": "^5.3.
|
|
31
|
-
"@smithy/types": "^4.
|
|
32
|
-
"@smithy/util-stream": "^4.5.
|
|
27
|
+
"@aws-sdk/types": "3.953.0",
|
|
28
|
+
"@smithy/node-config-provider": "^4.3.6",
|
|
29
|
+
"@smithy/node-http-handler": "^4.4.6",
|
|
30
|
+
"@smithy/protocol-http": "^5.3.6",
|
|
31
|
+
"@smithy/types": "^4.10.0",
|
|
32
|
+
"@smithy/util-stream": "^4.5.7",
|
|
33
33
|
"tslib": "^2.6.2"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@aws-sdk/credential-providers": "3.
|
|
36
|
+
"@aws-sdk/credential-providers": "3.954.0",
|
|
37
37
|
"@tsconfig/recommended": "1.0.1",
|
|
38
38
|
"@types/node": "^18.19.69",
|
|
39
39
|
"concurrently": "7.0.0",
|