Skip to content

Commit 4cea583

Browse files
authored
fix: revert "feat(credential-provider-node): use dynamic import for credential providers (#5677)"
This reverts commit 7841411.
1 parent 7841411 commit 4cea583

File tree

36 files changed

+436
-827
lines changed

36 files changed

+436
-827
lines changed

Diff for: Makefile

-3
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,6 @@ unlink-smithy:
2020
rm ./node_modules/\@smithy
2121
yarn --check-files
2222

23-
copy-smithy:
24-
node ./scripts/copy-smithy-dist-files
25-
2623
# Runs build for all packages using Turborepo
2724
turbo-build:
2825
(cd scripts/remote-cache && yarn)

Diff for: package.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
"@tsconfig/recommended": "1.0.1",
7070
"@types/chai-as-promised": "^7.1.2",
7171
"@types/fs-extra": "^8.0.1",
72-
"@types/jest": "29.5.11",
72+
"@types/jest": "29.5.2",
7373
"@typescript-eslint/eslint-plugin": "5.55.0",
7474
"@typescript-eslint/parser": "5.55.0",
7575
"async": "3.2.4",
@@ -93,8 +93,8 @@
9393
"glob": "7.1.6",
9494
"husky": "^4.2.3",
9595
"jasmine-core": "^3.5.0",
96-
"jest": "29.7.0",
97-
"jest-environment-jsdom": "29.7.0",
96+
"jest": "29.5.0",
97+
"jest-environment-jsdom": "29.5.0",
9898
"jmespath": "^0.15.0",
9999
"json5": "^2.2.0",
100100
"karma": "6.4.0",
@@ -113,7 +113,7 @@
113113
"mocha": "10.0.0",
114114
"prettier": "2.8.5",
115115
"rimraf": "3.0.2",
116-
"ts-jest": "29.1.1",
116+
"ts-jest": "29.1.0",
117117
"ts-loader": "9.4.2",
118118
"ts-mocha": "10.0.0",
119119
"ts-node": "10.9.1",

Diff for: packages/credential-provider-node/src/defaultProvider.spec.ts

+73-53
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import { fromIni } from "@aws-sdk/credential-provider-ini";
33
import { fromProcess } from "@aws-sdk/credential-provider-process";
44
import { fromSSO } from "@aws-sdk/credential-provider-sso";
55
import { fromTokenFile } from "@aws-sdk/credential-provider-web-identity";
6-
import { CredentialsProviderError } from "@smithy/property-provider";
6+
import { chain, CredentialsProviderError, memoize } from "@smithy/property-provider";
77
import { ENV_PROFILE, loadSharedConfigFiles } from "@smithy/shared-ini-file-loader";
88

9-
import { credentialsTreatedAsExpired, credentialsWillNeedRefresh, defaultProvider } from "./defaultProvider";
9+
import { defaultProvider } from "./defaultProvider";
1010
import { remoteProvider } from "./remoteProvider";
1111

1212
jest.mock("@aws-sdk/credential-provider-env");
@@ -15,6 +15,7 @@ jest.mock("@aws-sdk/credential-provider-ini");
1515
jest.mock("@aws-sdk/credential-provider-process");
1616
jest.mock("@aws-sdk/credential-provider-sso");
1717
jest.mock("@aws-sdk/credential-provider-web-identity");
18+
jest.mock("@smithy/property-provider");
1819
jest.mock("@smithy/shared-ini-file-loader");
1920
jest.mock("./remoteProvider");
2021

@@ -24,24 +25,19 @@ describe(defaultProvider.name, () => {
2425
secretAccessKey: "mockSecretAccessKey",
2526
};
2627

27-
const credentials = () => {
28-
throw new CredentialsProviderError("test", true);
29-
};
30-
31-
const finalCredentials = () => {
32-
return mockCreds;
33-
};
34-
3528
const mockInit = {
3629
profile: "mockProfile",
3730
};
3831

39-
const mockEnvFn = jest.fn().mockImplementation(() => credentials());
40-
const mockSsoFn = jest.fn().mockImplementation(() => credentials());
41-
const mockIniFn = jest.fn().mockImplementation(() => credentials());
42-
const mockProcessFn = jest.fn().mockImplementation(() => credentials());
43-
const mockTokenFileFn = jest.fn().mockImplementation(() => credentials());
44-
const mockRemoteProviderFn = jest.fn().mockImplementation(() => finalCredentials());
32+
const mockEnvFn = jest.fn();
33+
const mockSsoFn = jest.fn();
34+
const mockIniFn = jest.fn();
35+
const mockProcessFn = jest.fn();
36+
const mockTokenFileFn = jest.fn();
37+
const mockRemoteProviderFn = jest.fn();
38+
39+
const mockChainFn = jest.fn();
40+
const mockMemoizeFn = jest.fn().mockResolvedValue(mockCreds);
4541

4642
beforeEach(() => {
4743
[
@@ -51,48 +47,51 @@ describe(defaultProvider.name, () => {
5147
[fromProcess, mockProcessFn],
5248
[fromTokenFile, mockTokenFileFn],
5349
[remoteProvider, mockRemoteProviderFn],
50+
[chain, mockChainFn],
51+
[memoize, mockMemoizeFn],
5452
].forEach(([fromFn, mockFn]) => {
5553
(fromFn as jest.Mock).mockReturnValue(mockFn);
5654
});
5755
});
5856

5957
afterEach(async () => {
58+
const errorFnIndex = (chain as jest.Mock).mock.calls[0].length;
59+
const errorFn = (chain as jest.Mock).mock.calls[0][errorFnIndex - 1];
60+
const expectedError = new CredentialsProviderError("Could not load credentials from any providers", false);
61+
try {
62+
await errorFn();
63+
fail(`expected ${expectedError}`);
64+
} catch (error) {
65+
expect(error.toString()).toStrictEqual(expectedError.toString());
66+
}
67+
68+
expect(memoize).toHaveBeenCalledWith(mockChainFn, expect.any(Function), expect.any(Function));
69+
6070
jest.clearAllMocks();
6171
});
6272

6373
describe("without fromEnv", () => {
64-
it("creates provider chain and memoizes it", async () => {
65-
const provider = defaultProvider(mockInit);
66-
67-
// initial call proceeds through the chain.
68-
{
69-
const receivedCreds = await provider();
70-
expect(receivedCreds).toEqual(mockCreds);
74+
afterEach(() => {
75+
expect(chain).toHaveBeenCalledWith(
76+
mockSsoFn,
77+
mockIniFn,
78+
mockProcessFn,
79+
mockTokenFileFn,
80+
mockRemoteProviderFn,
81+
expect.any(Function)
82+
);
83+
});
7184

72-
expect(fromEnv).not.toHaveBeenCalled();
73-
expect(fromSSO).toHaveBeenCalledWith(mockInit);
74-
expect(fromIni).toHaveBeenCalledWith(mockInit);
75-
expect(fromProcess).toHaveBeenCalledWith(mockInit);
76-
expect(fromTokenFile).toHaveBeenCalledWith(mockInit);
77-
expect(remoteProvider).toHaveBeenCalledWith(mockInit);
85+
it("creates provider chain and memoizes it", async () => {
86+
const receivedCreds = await defaultProvider(mockInit)();
87+
expect(receivedCreds).toStrictEqual(mockCreds);
7888

79-
expect(loadSharedConfigFiles).not.toHaveBeenCalled();
89+
expect(fromEnv).not.toHaveBeenCalled();
90+
for (const fromFn of [fromSSO, fromIni, fromProcess, fromTokenFile, remoteProvider]) {
91+
expect(fromFn).toHaveBeenCalledWith(mockInit);
8092
}
8193

82-
jest.clearAllMocks();
83-
84-
// subsequent call does not enter the chain.
85-
{
86-
const receivedCreds = await provider();
87-
expect(receivedCreds).toEqual(mockCreds);
88-
89-
expect(fromEnv).not.toHaveBeenCalled();
90-
expect(fromSSO).not.toHaveBeenCalledWith(mockInit);
91-
expect(fromIni).not.toHaveBeenCalledWith(mockInit);
92-
expect(fromProcess).not.toHaveBeenCalledWith(mockInit);
93-
expect(fromTokenFile).not.toHaveBeenCalledWith(mockInit);
94-
expect(remoteProvider).not.toHaveBeenCalledWith(mockInit);
95-
}
94+
expect(loadSharedConfigFiles).not.toHaveBeenCalled();
9695
});
9796

9897
it(`if env['${ENV_PROFILE}'] is set`, async () => {
@@ -124,42 +123,63 @@ describe(defaultProvider.name, () => {
124123
for (const fromFn of [fromSSO, fromIni, fromProcess, fromTokenFile, remoteProvider]) {
125124
expect(fromFn).toHaveBeenCalledWith(mockInitWithoutProfile);
126125
}
126+
127+
expect(chain).toHaveBeenCalledWith(
128+
mockEnvFn,
129+
mockSsoFn,
130+
mockIniFn,
131+
mockProcessFn,
132+
mockTokenFileFn,
133+
mockRemoteProviderFn,
134+
expect.any(Function)
135+
);
127136
});
128137

129-
describe(credentialsTreatedAsExpired.name, () => {
138+
describe("memoize isExpired", () => {
130139
const mockDateNow = Date.now();
131140
beforeEach(async () => {
132141
jest.spyOn(Date, "now").mockReturnValueOnce(mockDateNow);
142+
await defaultProvider(mockInit)();
133143
});
134144

135145
it("returns true if expiration is defined, and creds have expired", () => {
146+
const memoizeExpiredFn = (memoize as jest.Mock).mock.calls[0][1];
136147
const expiration = new Date(mockDateNow - 24 * 60 * 60 * 1000);
137-
expect(credentialsTreatedAsExpired({ ...mockCreds, expiration })).toEqual(true);
148+
expect(memoizeExpiredFn({ expiration })).toEqual(true);
138149
});
139150

140151
it("returns true if expiration is defined, and creds expire in <5 mins", () => {
152+
const memoizeExpiredFn = (memoize as jest.Mock).mock.calls[0][1];
141153
const expiration = new Date(mockDateNow + 299 * 1000);
142-
expect(credentialsTreatedAsExpired({ ...mockCreds, expiration })).toEqual(true);
154+
expect(memoizeExpiredFn({ expiration })).toEqual(true);
143155
});
144156

145157
it("returns false if expiration is defined, but creds expire in >5 mins", () => {
158+
const memoizeExpiredFn = (memoize as jest.Mock).mock.calls[0][1];
146159
const expiration = new Date(mockDateNow + 301 * 1000);
147-
expect(credentialsTreatedAsExpired({ ...mockCreds, expiration })).toEqual(false);
160+
expect(memoizeExpiredFn({ expiration })).toEqual(false);
148161
});
149162

150163
it("returns false if expiration is not defined", () => {
151-
expect(credentialsTreatedAsExpired({ ...mockCreds })).toEqual(false);
164+
const memoizeExpiredFn = (memoize as jest.Mock).mock.calls[0][1];
165+
expect(memoizeExpiredFn({})).toEqual(false);
152166
});
153167
});
154168

155-
describe(credentialsWillNeedRefresh.name, () => {
169+
describe("memoize requiresRefresh", () => {
170+
beforeEach(async () => {
171+
await defaultProvider(mockInit)();
172+
});
173+
156174
it("returns true if expiration is not defined", () => {
157-
const expiration = new Date();
158-
expect(credentialsWillNeedRefresh({ ...mockCreds, expiration })).toEqual(true);
175+
const memoizeRefreshFn = (memoize as jest.Mock).mock.calls[0][2];
176+
const expiration = Date.now();
177+
expect(memoizeRefreshFn({ expiration })).toEqual(true);
159178
});
160179

161180
it("returns false if expiration is not defined", () => {
162-
expect(credentialsWillNeedRefresh({ ...mockCreds })).toEqual(false);
181+
const memoizeRefreshFn = (memoize as jest.Mock).mock.calls[0][2];
182+
expect(memoizeRefreshFn({})).toEqual(false);
163183
});
164184
});
165185
});
+14-49
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import type { FromIniInit } from "@aws-sdk/credential-provider-ini";
2-
import type { FromProcessInit } from "@aws-sdk/credential-provider-process";
3-
import type { FromSSOInit } from "@aws-sdk/credential-provider-sso";
4-
import type { FromTokenFileInit } from "@aws-sdk/credential-provider-web-identity";
5-
import type { RemoteProviderInit } from "@smithy/credential-provider-imds";
1+
import { fromEnv } from "@aws-sdk/credential-provider-env";
2+
import { fromIni, FromIniInit } from "@aws-sdk/credential-provider-ini";
3+
import { fromProcess, FromProcessInit } from "@aws-sdk/credential-provider-process";
4+
import { fromSSO, FromSSOInit } from "@aws-sdk/credential-provider-sso";
5+
import { fromTokenFile, FromTokenFileInit } from "@aws-sdk/credential-provider-web-identity";
6+
import { RemoteProviderInit } from "@smithy/credential-provider-imds";
67
import { chain, CredentialsProviderError, memoize } from "@smithy/property-provider";
78
import { ENV_PROFILE } from "@smithy/shared-ini-file-loader";
89
import { AwsCredentialIdentity, MemoizedProvider } from "@smithy/types";
@@ -48,52 +49,16 @@ export type DefaultProviderInit = FromIniInit & RemoteProviderInit & FromProcess
4849
export const defaultProvider = (init: DefaultProviderInit = {}): MemoizedProvider<AwsCredentialIdentity> =>
4950
memoize(
5051
chain(
51-
...(init.profile || process.env[ENV_PROFILE]
52-
? []
53-
: [
54-
async () => {
55-
const { fromEnv } = await import("@aws-sdk/credential-provider-env");
56-
return fromEnv()();
57-
},
58-
]),
59-
async () => {
60-
const { fromSSO } = await import("@aws-sdk/credential-provider-sso");
61-
return fromSSO(init)();
62-
},
63-
async () => {
64-
const { fromIni } = await import("@aws-sdk/credential-provider-ini");
65-
return fromIni(init)();
66-
},
67-
async () => {
68-
const { fromProcess } = await import("@aws-sdk/credential-provider-process");
69-
return fromProcess(init)();
70-
},
71-
async () => {
72-
const { fromTokenFile } = await import("@aws-sdk/credential-provider-web-identity");
73-
return fromTokenFile(init)();
74-
},
75-
async () => {
76-
return (await remoteProvider(init))();
77-
},
52+
...(init.profile || process.env[ENV_PROFILE] ? [] : [fromEnv()]),
53+
fromSSO(init),
54+
fromIni(init),
55+
fromProcess(init),
56+
fromTokenFile(init),
57+
remoteProvider(init),
7858
async () => {
7959
throw new CredentialsProviderError("Could not load credentials from any providers", false);
8060
}
8161
),
82-
credentialsTreatedAsExpired,
83-
credentialsWillNeedRefresh
62+
(credentials) => credentials.expiration !== undefined && credentials.expiration.getTime() - Date.now() < 300000,
63+
(credentials) => credentials.expiration !== undefined
8464
);
85-
86-
/**
87-
* @internal
88-
*
89-
* @returns credentials have expiration.
90-
*/
91-
export const credentialsWillNeedRefresh = (credentials: AwsCredentialIdentity) => credentials?.expiration !== undefined;
92-
93-
/**
94-
* @internal
95-
*
96-
* @returns credentials with less than 5 minutes left.
97-
*/
98-
export const credentialsTreatedAsExpired = (credentials: AwsCredentialIdentity) =>
99-
credentials?.expiration !== undefined && credentials.expiration.getTime() - Date.now() < 300000;

Diff for: packages/credential-provider-node/src/remoteProvider.spec.ts

+3-5
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ describe(remoteProvider.name, () => {
4242
"returns fromContainerMetadata if env['%s'] is set",
4343
async (key) => {
4444
process.env[key] = "defined";
45-
const receivedCreds = await (await remoteProvider(mockInit))();
45+
const receivedCreds = await remoteProvider(mockInit)();
4646
expect(receivedCreds).toStrictEqual(mockCredsFromContainer);
4747
expect(fromContainerMetadata).toHaveBeenCalledWith(mockInit);
4848
expect(fromInstanceMetadata).not.toHaveBeenCalled();
@@ -53,9 +53,7 @@ describe(remoteProvider.name, () => {
5353
process.env[ENV_IMDS_DISABLED] = "1";
5454
const expectedError = new CredentialsProviderError("EC2 Instance Metadata Service access disabled");
5555
try {
56-
await (
57-
await remoteProvider(mockInit)
58-
)();
56+
await remoteProvider(mockInit)();
5957
fail(`expectedError ${expectedError}`);
6058
} catch (error) {
6159
expect(error).toStrictEqual(expectedError);
@@ -65,7 +63,7 @@ describe(remoteProvider.name, () => {
6563
});
6664

6765
it("returns fromInstanceMetadata if environment variables are not set", async () => {
68-
const receivedCreds = await (await remoteProvider(mockInit))();
66+
const receivedCreds = await remoteProvider(mockInit)();
6967
expect(receivedCreds).toStrictEqual(mockSourceCredsFromInstanceMetadata);
7068
expect(fromInstanceMetadata).toHaveBeenCalledWith(mockInit);
7169
expect(fromContainerMetadata).not.toHaveBeenCalled();

Diff for: packages/credential-provider-node/src/remoteProvider.ts

+9-10
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
1-
import type { RemoteProviderInit } from "@smithy/credential-provider-imds";
1+
import {
2+
ENV_CMDS_FULL_URI,
3+
ENV_CMDS_RELATIVE_URI,
4+
fromContainerMetadata,
5+
fromInstanceMetadata,
6+
RemoteProviderInit,
7+
} from "@smithy/credential-provider-imds";
28
import { CredentialsProviderError } from "@smithy/property-provider";
3-
import type { AwsCredentialIdentityProvider } from "@smithy/types";
9+
import { AwsCredentialIdentityProvider } from "@smithy/types";
410

511
export const ENV_IMDS_DISABLED = "AWS_EC2_METADATA_DISABLED";
612

7-
/**
8-
* @internal
9-
*/
10-
export const remoteProvider = async (init: RemoteProviderInit): Promise<AwsCredentialIdentityProvider> => {
11-
const { ENV_CMDS_FULL_URI, ENV_CMDS_RELATIVE_URI, fromContainerMetadata, fromInstanceMetadata } = await import(
12-
"@smithy/credential-provider-imds"
13-
);
14-
13+
export const remoteProvider = (init: RemoteProviderInit): AwsCredentialIdentityProvider => {
1514
if (process.env[ENV_CMDS_RELATIVE_URI] || process.env[ENV_CMDS_FULL_URI]) {
1615
return fromContainerMetadata(init);
1716
}

Diff for: packages/middleware-endpoint-discovery/src/middleware-endpoint-discovery.integ.spec.ts

-9
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,3 @@
1-
jest.mock("@aws-sdk/credential-provider-node", () => ({
2-
defaultProvider: async () => {
3-
return {
4-
secretAccessKey: "integration-test",
5-
accessKeyId: "integration-test",
6-
sessionToken: "integration-test",
7-
};
8-
},
9-
}));
101
import { TimestreamQuery } from "@aws-sdk/client-timestream-query";
112
import { TimestreamWrite } from "@aws-sdk/client-timestream-write";
123
import { EndpointCache } from "@aws-sdk/endpoint-cache";

0 commit comments

Comments
 (0)