Skip to content

Commit 006940c

Browse files
committed
add e2e test
1 parent 358e1bb commit 006940c

File tree

4 files changed

+144
-2
lines changed

4 files changed

+144
-2
lines changed

packages/credential-provider-imds/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
"lint": "eslint -c ../../.eslintrc.js \"src/**/*.ts\"",
1616
"format": "prettier --config ../../prettier.config.js --ignore-path ../../.prettierignore --write \"**/*.{ts,md,json}\"",
1717
"test": "yarn g:vitest run",
18-
"test:watch": "yarn g:vitest watch"
18+
"test:e2e": "yarn g:vitest run -c vitest.config.e2e.ts --mode development",
19+
"test:watch": "yarn g:vitest watch",
20+
"test:e2e:watch": "yarn g:vitest watch -c vitest.config.e2e.ts"
1921
},
2022
"keywords": [
2123
"aws",
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import { CredentialsProviderError } from "@smithy/property-provider";
2+
import { afterEach, beforeEach, describe, expect, test as it} from "vitest";
3+
4+
import { fromInstanceMetadata,getMetadataToken } from "./fromInstanceMetadata";
5+
6+
describe("fromInstanceMetadata (Live EC2 E2E Tests)", () => {
7+
const originalEnv = { ...process.env };
8+
let imdsAvailable = false;
9+
10+
beforeEach(async () => {
11+
process.env = { ...originalEnv };
12+
13+
// Check IMDS availability
14+
try {
15+
const testProvider = fromInstanceMetadata({ timeout: 1000, maxRetries: 0 });
16+
await testProvider();
17+
imdsAvailable = true;
18+
} catch (err) {
19+
imdsAvailable = false;
20+
}
21+
});
22+
23+
afterEach(() => {
24+
process.env = { ...originalEnv };
25+
});
26+
27+
it("should fetch metadata token successfully", async () => {
28+
29+
if (!imdsAvailable) {
30+
return;
31+
}
32+
const options = {
33+
path: "/latest/api/token",
34+
method: "PUT",
35+
timeout: 1000,
36+
headers: {
37+
"x-aws-ec2-metadata-token-ttl-seconds": "21600",
38+
},
39+
};
40+
const token = await getMetadataToken(options);
41+
expect(token).toBeDefined();
42+
expect(typeof token).toBe("string");
43+
expect(token.length).toBeGreaterThan(0);
44+
});
45+
46+
it("retrieves credentials with account ID on allowlisted instances only)", async () => {
47+
48+
if (!imdsAvailable)
49+
return;
50+
51+
const provider = fromInstanceMetadata({ timeout: 1000, maxRetries: 2 });
52+
const credentials = await provider();
53+
54+
expect(credentials).toHaveProperty("accessKeyId");
55+
expect(credentials).toHaveProperty("secretAccessKey");
56+
expect(typeof credentials.accessKeyId).toBe("string");
57+
expect(typeof credentials.secretAccessKey).toBe("string");
58+
59+
if (!credentials.accountId) {
60+
console.log("Skipping account ID test not an allowlisted instance");
61+
return;
62+
}
63+
64+
expect(credentials.accountId).toBeDefined();
65+
expect(typeof credentials.accountId).toBe("string");
66+
67+
console.log("IMDSv2 Credentials with Account ID:", {
68+
accessKeyId: credentials.accessKeyId,
69+
sessionToken: credentials.sessionToken?.slice(0, 10) + "...",
70+
accountId: credentials.accountId,
71+
});
72+
});
73+
74+
it("IMDS access disabled via AWS_EC2_METADATA_DISABLED", async () => {
75+
process.env.AWS_EC2_METADATA_DISABLED = "true";
76+
77+
const provider = fromInstanceMetadata({ timeout: 1000 });
78+
79+
await expect(provider()).rejects.toThrow("IMDS credential fetching is disabled");
80+
});
81+
82+
it("Empty configured profile name should throw error", async () => {
83+
process.env.AWS_EC2_INSTANCE_PROFILE_NAME = " ";
84+
85+
const provider = fromInstanceMetadata({ timeout: 1000 });
86+
87+
await expect(provider()).rejects.toThrow();
88+
});
89+
90+
it("Uses configured profile name from env", async () => {
91+
92+
if (!imdsAvailable)
93+
return;
94+
95+
process.env.AWS_EC2_INSTANCE_PROFILE_NAME = "foo-profile";
96+
const provider = fromInstanceMetadata({ timeout: 1000 });
97+
98+
try {
99+
const credentials = await provider();
100+
expect(credentials).toHaveProperty("accessKeyId");
101+
console.log("Used configured profile name from env.");
102+
} catch (error) {
103+
expect(error).toBeDefined();
104+
console.log("Profile test completed (profile may not exist).");
105+
}
106+
});
107+
108+
it("Multiple calls return stable results", async () => {
109+
110+
if (!imdsAvailable)
111+
return;
112+
113+
const provider = fromInstanceMetadata({ timeout: 1000 });
114+
const creds1 = await provider();
115+
const creds2 = await provider();
116+
117+
expect(creds1.accessKeyId).toBeTruthy();
118+
expect(creds2.accessKeyId).toBeTruthy();
119+
expect(creds1.accessKeyId).toBe(creds2.accessKeyId);
120+
121+
console.log("Stable credentials returned across calls.");
122+
});
123+
124+
it("should timeout as expected when a request exceeds the specified duration", async () => {
125+
if (!imdsAvailable)
126+
return;
127+
const provider = fromInstanceMetadata({ timeout: 1 });
128+
129+
await expect(provider()).rejects.toThrow(/timeout|timed out|TimeoutError/i);
130+
});
131+
132+
});

packages/credential-provider-imds/src/fromInstanceMetadata.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ export const getImdsProfile = async (
190190
}, maxRetries);
191191
};
192192

193-
const getMetadataToken = async (options: RequestOptions) =>
193+
export const getMetadataToken = async (options: RequestOptions) =>
194194
httpRequest({
195195
...options,
196196
path: IMDS_TOKEN_PATH,
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { defineConfig } from "vitest/config";
2+
3+
export default defineConfig({
4+
test: {
5+
include: ["**/*.e2e.spec.ts"],
6+
environment: "node",
7+
},
8+
});

0 commit comments

Comments
 (0)