From f86779df673c2ce1629ec46f55da24396b5add72 Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Wed, 1 Jul 2020 16:52:07 +0000 Subject: [PATCH 01/12] test: refactor fromInstanceMetadata --- .../src/fromInstanceMetadata.spec.ts | 87 +++++++++++-------- 1 file changed, 53 insertions(+), 34 deletions(-) diff --git a/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts b/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts index 3893586a3efb..b7409fd28cc5 100644 --- a/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts +++ b/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts @@ -4,71 +4,89 @@ import { fromImdsCredentials, ImdsCredentials } from "./remoteProvider/ImdsCredentials"; +import { Credentials } from "@aws-sdk/types"; -const mockHttpGet = httpGet; jest.mock("./remoteProvider/httpGet", () => ({ httpGet: jest.fn() })); - -beforeEach(() => { - mockHttpGet.mockReset(); -}); +jest.mock("./remoteProvider/ImdsCredentials", () => ({ + fromImdsCredentials: jest.fn(), + isImdsCredentials: jest.fn().mockReturnValue(true) +})); describe("fromInstanceMetadata", () => { - const creds: ImdsCredentials = Object.freeze({ + const mockImdsCreds: ImdsCredentials = Object.freeze({ AccessKeyId: "foo", SecretAccessKey: "bar", Token: "baz", Expiration: new Date().toISOString() }); - it("should resolve credentials by fetching them from the container metadata service", async () => { - mockHttpGet.mockReturnValue(Promise.resolve(JSON.stringify(creds))); - expect(await fromInstanceMetadata()()).toEqual(fromImdsCredentials(creds)); + const mockCreds: Credentials = Object.freeze({ + accessKeyId: mockImdsCreds.AccessKeyId, + secretAccessKey: mockImdsCreds.SecretAccessKey, + sessionToken: mockImdsCreds.Token, + expiration: new Date(mockImdsCreds.Expiration) + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + it.only("should resolve credentials by fetching them from the container metadata service", async () => { + (httpGet as jest.Mock).mockResolvedValue(JSON.stringify(mockImdsCreds)); + (fromImdsCredentials as jest.Mock).mockReturnValue(mockCreds); + + await expect(fromInstanceMetadata()()).resolves.toEqual(mockCreds); + expect(httpGet).toHaveBeenCalledTimes(2); + expect(fromImdsCredentials).toHaveBeenCalledTimes(1); }); it("should retry the fetching operation up to maxRetries times", async () => { const maxRetries = 5; - mockHttpGet.mockReturnValueOnce(Promise.resolve("foo")); + (httpGet as jest.Mock).mockResolvedValueOnce("foo"); for (let i = 0; i < maxRetries - 1; i++) { - mockHttpGet.mockReturnValueOnce(Promise.reject("No!")); + (httpGet as jest.Mock).mockResolvedValueOnce("No!"); } - mockHttpGet.mockReturnValueOnce(Promise.resolve(JSON.stringify(creds))); + (httpGet as jest.Mock).mockResolvedValueOnce(JSON.stringify(mockImdsCreds)); - expect(await fromInstanceMetadata({ maxRetries })()).toEqual( - fromImdsCredentials(creds) + await expect(fromInstanceMetadata({ maxRetries })()).resolves.toEqual( + fromImdsCredentials(mockImdsCreds) ); - expect(mockHttpGet.mock.calls.length).toEqual(maxRetries + 1); + expect(httpGet).toHaveBeenCalledTimes(maxRetries + 1); }); it("should retry responses that receive invalid response values", async () => { - mockHttpGet.mockReturnValueOnce(Promise.resolve("foo")); - for (let key of Object.keys(creds)) { - const invalidCreds: any = { ...creds }; + (httpGet as jest.Mock).mockResolvedValueOnce("foo"); + for (let key of Object.keys(mockImdsCreds)) { + const invalidCreds: any = { ...mockImdsCreds }; delete invalidCreds[key]; - mockHttpGet.mockReturnValueOnce( - Promise.resolve(JSON.stringify(invalidCreds)) + (httpGet as jest.Mock).mockResolvedValueOnce( + JSON.stringify(invalidCreds) ); } - mockHttpGet.mockReturnValueOnce(Promise.resolve(JSON.stringify(creds))); + (httpGet as jest.Mock).mockResolvedValueOnce(JSON.stringify(mockImdsCreds)); await fromInstanceMetadata({ maxRetries: 100 })(); - expect(mockHttpGet.mock.calls.length).toEqual( - Object.keys(creds).length + 2 + expect(httpGet).toHaveBeenCalledTimes( + Object.keys(mockImdsCreds).length + 2 ); }); it("should pass relevant configuration to httpGet", async () => { const timeout = Math.ceil(Math.random() * 1000); const profile = "foo-profile"; - mockHttpGet.mockReturnValueOnce(Promise.resolve(profile)); - mockHttpGet.mockReturnValue(Promise.resolve(JSON.stringify(creds))); + + (httpGet as jest.Mock).mockResolvedValueOnce(profile); + (httpGet as jest.Mock).mockResolvedValue(JSON.stringify(mockImdsCreds)); + await fromInstanceMetadata({ timeout })(); - expect(mockHttpGet.mock.calls.length).toEqual(2); - expect(mockHttpGet.mock.calls[0][0]).toEqual({ + + expect(httpGet).toHaveBeenCalledTimes(2); + expect((httpGet as jest.Mock).mock.calls[0][0]).toEqual({ host: "169.254.169.254", path: "/latest/meta-data/iam/security-credentials/", timeout }); - expect(mockHttpGet.mock.calls[1][0]).toEqual({ + expect((httpGet as jest.Mock).mock.calls[1][0]).toEqual({ host: "169.254.169.254", path: `/latest/meta-data/iam/security-credentials/${profile}`, timeout @@ -78,19 +96,20 @@ describe("fromInstanceMetadata", () => { it("should retry the profile name fetch as necessary", async () => { const defaultTimeout = 1000; const profile = "foo-profile"; - mockHttpGet.mockReturnValueOnce(Promise.reject("Too busy")); - mockHttpGet.mockReturnValueOnce(Promise.resolve(profile)); - mockHttpGet.mockReturnValueOnce(Promise.resolve(JSON.stringify(creds))); + + (httpGet as jest.Mock).mockRejectedValueOnce("Too busy"); + (httpGet as jest.Mock).mockResolvedValueOnce(profile); + (httpGet as jest.Mock).mockResolvedValueOnce(JSON.stringify(mockImdsCreds)); await fromInstanceMetadata({ maxRetries: 1 })(); - expect(mockHttpGet.mock.calls.length).toEqual(3); - expect(mockHttpGet.mock.calls[2][0]).toEqual({ + expect(httpGet).toHaveBeenCalledTimes(3); + expect((httpGet as jest.Mock).mock.calls[2][0]).toEqual({ host: "169.254.169.254", path: `/latest/meta-data/iam/security-credentials/${profile}`, timeout: defaultTimeout }); for (let index of [0, 1]) { - expect(mockHttpGet.mock.calls[index][0]).toEqual({ + expect((httpGet as jest.Mock).mock.calls[index][0]).toEqual({ host: "169.254.169.254", path: "/latest/meta-data/iam/security-credentials/", timeout: defaultTimeout From 7378d14a169da12594ae44239c6178a522121e00 Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Thu, 2 Jul 2020 01:10:02 +0000 Subject: [PATCH 02/12] test: rewrite tests from scratch --- .../src/fromInstanceMetadata.spec.ts | 116 +++++++----------- .../src/fromInstanceMetadata.ts | 18 +-- 2 files changed, 52 insertions(+), 82 deletions(-) diff --git a/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts b/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts index b7409fd28cc5..cca25201585e 100644 --- a/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts +++ b/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts @@ -4,15 +4,33 @@ import { fromImdsCredentials, ImdsCredentials } from "./remoteProvider/ImdsCredentials"; +import { providerConfigFromInit } from "./remoteProvider/RemoteProviderInit"; import { Credentials } from "@aws-sdk/types"; +import { retry } from "./remoteProvider/retry"; jest.mock("./remoteProvider/httpGet", () => ({ httpGet: jest.fn() })); jest.mock("./remoteProvider/ImdsCredentials", () => ({ fromImdsCredentials: jest.fn(), isImdsCredentials: jest.fn().mockReturnValue(true) })); +jest.mock("./remoteProvider/retry", () => ({ + retry: jest.fn() +})); +jest.mock("./remoteProvider/RemoteProviderInit", () => ({ + providerConfigFromInit: jest.fn() +})); describe("fromInstanceMetadata", () => { + const mockTimeout = 1000; + const mockMaxRetries = 3; + const mockProfile = "foo"; + + const mockHttpGetOptions = { + host: "169.254.169.254", + path: "/latest/meta-data/iam/security-credentials/", + timeout: mockTimeout + }; + const mockImdsCreds: ImdsCredentials = Object.freeze({ AccessKeyId: "foo", SecretAccessKey: "bar", @@ -27,93 +45,41 @@ describe("fromInstanceMetadata", () => { expiration: new Date(mockImdsCreds.Expiration) }); + beforeEach(() => { + (providerConfigFromInit as jest.Mock).mockReturnValue({ + timeout: mockTimeout, + maxRetries: mockMaxRetries + }); + }); + afterEach(() => { jest.resetAllMocks(); }); - it.only("should resolve credentials by fetching them from the container metadata service", async () => { - (httpGet as jest.Mock).mockResolvedValue(JSON.stringify(mockImdsCreds)); + it("gets profile name from IMDS, and passes profile name to fetch credentials", async () => { + (httpGet as jest.Mock) + .mockResolvedValueOnce(mockProfile) + .mockResolvedValueOnce(JSON.stringify(mockImdsCreds)); + + (retry as jest.Mock).mockImplementation((fn: any) => fn()); (fromImdsCredentials as jest.Mock).mockReturnValue(mockCreds); await expect(fromInstanceMetadata()()).resolves.toEqual(mockCreds); expect(httpGet).toHaveBeenCalledTimes(2); - expect(fromImdsCredentials).toHaveBeenCalledTimes(1); - }); - - it("should retry the fetching operation up to maxRetries times", async () => { - const maxRetries = 5; - (httpGet as jest.Mock).mockResolvedValueOnce("foo"); - for (let i = 0; i < maxRetries - 1; i++) { - (httpGet as jest.Mock).mockResolvedValueOnce("No!"); - } - (httpGet as jest.Mock).mockResolvedValueOnce(JSON.stringify(mockImdsCreds)); - - await expect(fromInstanceMetadata({ maxRetries })()).resolves.toEqual( - fromImdsCredentials(mockImdsCreds) - ); - expect(httpGet).toHaveBeenCalledTimes(maxRetries + 1); - }); - - it("should retry responses that receive invalid response values", async () => { - (httpGet as jest.Mock).mockResolvedValueOnce("foo"); - for (let key of Object.keys(mockImdsCreds)) { - const invalidCreds: any = { ...mockImdsCreds }; - delete invalidCreds[key]; - (httpGet as jest.Mock).mockResolvedValueOnce( - JSON.stringify(invalidCreds) - ); - } - (httpGet as jest.Mock).mockResolvedValueOnce(JSON.stringify(mockImdsCreds)); - - await fromInstanceMetadata({ maxRetries: 100 })(); - expect(httpGet).toHaveBeenCalledTimes( - Object.keys(mockImdsCreds).length + 2 - ); + expect(httpGet).toHaveBeenNthCalledWith(1, mockHttpGetOptions); + expect(httpGet).toHaveBeenNthCalledWith(2, { + ...mockHttpGetOptions, + path: `${mockHttpGetOptions.path}${mockProfile}` + }); }); - it("should pass relevant configuration to httpGet", async () => { - const timeout = Math.ceil(Math.random() * 1000); - const profile = "foo-profile"; + it("passes maxRetries returned from providerConfigFromInit to retry", () => {}); - (httpGet as jest.Mock).mockResolvedValueOnce(profile); - (httpGet as jest.Mock).mockResolvedValue(JSON.stringify(mockImdsCreds)); + it("passes timeout returned from providerConfigFromInit to requestFromEc2Imds", () => {}); - await fromInstanceMetadata({ timeout })(); + it("throws ProviderError is credentials returns are incorrect", () => {}); - expect(httpGet).toHaveBeenCalledTimes(2); - expect((httpGet as jest.Mock).mock.calls[0][0]).toEqual({ - host: "169.254.169.254", - path: "/latest/meta-data/iam/security-credentials/", - timeout - }); - expect((httpGet as jest.Mock).mock.calls[1][0]).toEqual({ - host: "169.254.169.254", - path: `/latest/meta-data/iam/security-credentials/${profile}`, - timeout - }); - }); - - it("should retry the profile name fetch as necessary", async () => { - const defaultTimeout = 1000; - const profile = "foo-profile"; - - (httpGet as jest.Mock).mockRejectedValueOnce("Too busy"); - (httpGet as jest.Mock).mockResolvedValueOnce(profile); - (httpGet as jest.Mock).mockResolvedValueOnce(JSON.stringify(mockImdsCreds)); + it("throws Error if requestFromEc2Imds for profile fails", () => {}); - await fromInstanceMetadata({ maxRetries: 1 })(); - expect(httpGet).toHaveBeenCalledTimes(3); - expect((httpGet as jest.Mock).mock.calls[2][0]).toEqual({ - host: "169.254.169.254", - path: `/latest/meta-data/iam/security-credentials/${profile}`, - timeout: defaultTimeout - }); - for (let index of [0, 1]) { - expect((httpGet as jest.Mock).mock.calls[index][0]).toEqual({ - host: "169.254.169.254", - path: "/latest/meta-data/iam/security-credentials/", - timeout: defaultTimeout - }); - } - }); + it("throws Error if requestFromEc2Imds for credentials fails", () => {}); }); diff --git a/packages/credential-provider-imds/src/fromInstanceMetadata.ts b/packages/credential-provider-imds/src/fromInstanceMetadata.ts index 6c32e2149d7f..39f5b6f750c7 100644 --- a/packages/credential-provider-imds/src/fromInstanceMetadata.ts +++ b/packages/credential-provider-imds/src/fromInstanceMetadata.ts @@ -15,9 +15,9 @@ import { ProviderError } from "@aws-sdk/property-provider"; * Creates a credential provider that will source credentials from the EC2 * Instance Metadata Service */ -export function fromInstanceMetadata( +export const fromInstanceMetadata = ( init: RemoteProviderInit = {} -): CredentialProvider { +): CredentialProvider => { const { timeout, maxRetries } = providerConfigFromInit(init); return async () => { const profile = ( @@ -40,15 +40,19 @@ export function fromInstanceMetadata( return fromImdsCredentials(credsResponse); }, maxRetries); }; -} +}; const IMDS_IP = "169.254.169.254"; const IMDS_PATH = "latest/meta-data/iam/security-credentials"; -function requestFromEc2Imds(timeout: number, path?: string): Promise { - return httpGet({ +const requestFromEc2Imds = async ( + timeout: number, + path?: string +): Promise => { + const buffer = await httpGet({ host: IMDS_IP, path: `/${IMDS_PATH}/${path ? path : ""}`, timeout - }).then(buffer => buffer.toString()); -} + }); + return buffer.toString(); +}; From 665844af41ddd36211eac5df456d3c81f58a5638 Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Thu, 2 Jul 2020 02:20:00 +0000 Subject: [PATCH 03/12] test: passes maxRetries returned from providerConfigFromInit to retry --- .../src/fromInstanceMetadata.spec.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts b/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts index cca25201585e..9440666040fc 100644 --- a/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts +++ b/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts @@ -73,7 +73,16 @@ describe("fromInstanceMetadata", () => { }); }); - it("passes maxRetries returned from providerConfigFromInit to retry", () => {}); + it("passes maxRetries returned from providerConfigFromInit to retry", async () => { + (retry as jest.Mock) + .mockResolvedValueOnce(mockProfile) + .mockResolvedValueOnce(mockCreds); + + await expect(fromInstanceMetadata()()).resolves.toEqual(mockCreds); + expect(retry).toHaveBeenCalledTimes(2); + expect((retry as jest.Mock).mock.calls[0][1]).toBe(mockMaxRetries); + expect((retry as jest.Mock).mock.calls[1][1]).toBe(mockMaxRetries); + }); it("passes timeout returned from providerConfigFromInit to requestFromEc2Imds", () => {}); From 5b1638c0c1facd274073ba2d3834a2b3fb9054c1 Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Thu, 2 Jul 2020 02:24:14 +0000 Subject: [PATCH 04/12] test: passes init to providerConfigFromInit --- .../src/fromInstanceMetadata.spec.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts b/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts index 9440666040fc..711fab0e329c 100644 --- a/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts +++ b/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts @@ -73,6 +73,17 @@ describe("fromInstanceMetadata", () => { }); }); + it("passes init to providerConfigFromInit", async () => { + (retry as jest.Mock) + .mockResolvedValueOnce(mockProfile) + .mockResolvedValueOnce(mockCreds); + + const init = { maxRetries: 5, timeout: 1213 }; + await expect(fromInstanceMetadata(init)()).resolves.toEqual(mockCreds); + expect(providerConfigFromInit).toHaveBeenCalledTimes(1); + expect(providerConfigFromInit).toHaveBeenCalledWith(init); + }); + it("passes maxRetries returned from providerConfigFromInit to retry", async () => { (retry as jest.Mock) .mockResolvedValueOnce(mockProfile) @@ -84,8 +95,6 @@ describe("fromInstanceMetadata", () => { expect((retry as jest.Mock).mock.calls[1][1]).toBe(mockMaxRetries); }); - it("passes timeout returned from providerConfigFromInit to requestFromEc2Imds", () => {}); - it("throws ProviderError is credentials returns are incorrect", () => {}); it("throws Error if requestFromEc2Imds for profile fails", () => {}); From 6af02cd6ed47172c8bb9eb500420fd75690e87cd Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Thu, 2 Jul 2020 02:28:51 +0000 Subject: [PATCH 05/12] test: passes {} to providerConfigFromInit if init not defined --- .../src/fromInstanceMetadata.spec.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts b/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts index 711fab0e329c..ac93804d8254 100644 --- a/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts +++ b/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts @@ -73,6 +73,16 @@ describe("fromInstanceMetadata", () => { }); }); + it("passes {} to providerConfigFromInit if init not defined", async () => { + (retry as jest.Mock) + .mockResolvedValueOnce(mockProfile) + .mockResolvedValueOnce(mockCreds); + + await expect(fromInstanceMetadata()()).resolves.toEqual(mockCreds); + expect(providerConfigFromInit).toHaveBeenCalledTimes(1); + expect(providerConfigFromInit).toHaveBeenCalledWith({}); + }); + it("passes init to providerConfigFromInit", async () => { (retry as jest.Mock) .mockResolvedValueOnce(mockProfile) @@ -95,7 +105,7 @@ describe("fromInstanceMetadata", () => { expect((retry as jest.Mock).mock.calls[1][1]).toBe(mockMaxRetries); }); - it("throws ProviderError is credentials returns are incorrect", () => {}); + it("throws ProviderError is credentials returned are incorrect", () => {}); it("throws Error if requestFromEc2Imds for profile fails", () => {}); From 4417446e96aa9a3ef34e596e77a7a4262a56da5b Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Thu, 2 Jul 2020 02:34:32 +0000 Subject: [PATCH 06/12] test: throws ProviderError is credentials returned are incorrect --- .../src/fromInstanceMetadata.spec.ts | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts b/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts index ac93804d8254..0a73a21b154e 100644 --- a/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts +++ b/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts @@ -2,11 +2,12 @@ import { fromInstanceMetadata } from "./fromInstanceMetadata"; import { httpGet } from "./remoteProvider/httpGet"; import { fromImdsCredentials, - ImdsCredentials + isImdsCredentials } from "./remoteProvider/ImdsCredentials"; import { providerConfigFromInit } from "./remoteProvider/RemoteProviderInit"; import { Credentials } from "@aws-sdk/types"; import { retry } from "./remoteProvider/retry"; +import { ProviderError } from "@aws-sdk/property-provider"; jest.mock("./remoteProvider/httpGet", () => ({ httpGet: jest.fn() })); jest.mock("./remoteProvider/ImdsCredentials", () => ({ @@ -31,7 +32,7 @@ describe("fromInstanceMetadata", () => { timeout: mockTimeout }; - const mockImdsCreds: ImdsCredentials = Object.freeze({ + const mockImdsCreds = Object.freeze({ AccessKeyId: "foo", SecretAccessKey: "bar", Token: "baz", @@ -105,7 +106,20 @@ describe("fromInstanceMetadata", () => { expect((retry as jest.Mock).mock.calls[1][1]).toBe(mockMaxRetries); }); - it("throws ProviderError is credentials returned are incorrect", () => {}); + it("throws ProviderError is credentials returned are incorrect", async () => { + (httpGet as jest.Mock) + .mockResolvedValueOnce(mockProfile) + .mockResolvedValueOnce(JSON.stringify(mockImdsCreds)); + + (retry as jest.Mock).mockImplementation((fn: any) => fn()); + ((isImdsCredentials as unknown) as jest.Mock).mockReturnValueOnce(false); + + await expect(fromInstanceMetadata()()).rejects.toEqual( + new ProviderError( + "Invalid response received from instance metadata service." + ) + ); + }); it("throws Error if requestFromEc2Imds for profile fails", () => {}); From c3d929bf22941dba5b8e6af79eda2a4e97d90655 Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Thu, 2 Jul 2020 02:36:43 +0000 Subject: [PATCH 07/12] test: throws Error if requestFromEc2Imds for profile fails --- .../src/fromInstanceMetadata.spec.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts b/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts index 0a73a21b154e..382a566a43b5 100644 --- a/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts +++ b/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts @@ -121,7 +121,12 @@ describe("fromInstanceMetadata", () => { ); }); - it("throws Error if requestFromEc2Imds for profile fails", () => {}); + it("throws Error if requestFromEc2Imds for profile fails", async () => { + const mockError = new Error("profile not found"); + (httpGet as jest.Mock).mockRejectedValueOnce(mockError); + (retry as jest.Mock).mockImplementation((fn: any) => fn()); + await expect(fromInstanceMetadata()()).rejects.toEqual(mockError); + }); it("throws Error if requestFromEc2Imds for credentials fails", () => {}); }); From 53652601dcfa3ef849cf87abd797ba31d4ec0c2e Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Thu, 2 Jul 2020 02:37:41 +0000 Subject: [PATCH 08/12] test: throws Error if requestFromEc2Imds for credentials fails --- .../src/fromInstanceMetadata.spec.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts b/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts index 382a566a43b5..8e80eb242c9f 100644 --- a/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts +++ b/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts @@ -125,8 +125,17 @@ describe("fromInstanceMetadata", () => { const mockError = new Error("profile not found"); (httpGet as jest.Mock).mockRejectedValueOnce(mockError); (retry as jest.Mock).mockImplementation((fn: any) => fn()); + await expect(fromInstanceMetadata()()).rejects.toEqual(mockError); }); - it("throws Error if requestFromEc2Imds for credentials fails", () => {}); + it("throws Error if requestFromEc2Imds for credentials fails", async () => { + const mockError = new Error("creds not found"); + (httpGet as jest.Mock) + .mockResolvedValueOnce(mockProfile) + .mockRejectedValueOnce(mockError); + (retry as jest.Mock).mockImplementation((fn: any) => fn()); + + await expect(fromInstanceMetadata()()).rejects.toEqual(mockError); + }); }); From bc1d5d9b44e27c70cb5376d6b3c04557ccba42fd Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Thu, 2 Jul 2020 02:44:18 +0000 Subject: [PATCH 09/12] test: trims profile returned name from IMDS --- .../src/fromInstanceMetadata.spec.ts | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts b/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts index 8e80eb242c9f..9335bcfef7de 100644 --- a/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts +++ b/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts @@ -9,17 +9,10 @@ import { Credentials } from "@aws-sdk/types"; import { retry } from "./remoteProvider/retry"; import { ProviderError } from "@aws-sdk/property-provider"; -jest.mock("./remoteProvider/httpGet", () => ({ httpGet: jest.fn() })); -jest.mock("./remoteProvider/ImdsCredentials", () => ({ - fromImdsCredentials: jest.fn(), - isImdsCredentials: jest.fn().mockReturnValue(true) -})); -jest.mock("./remoteProvider/retry", () => ({ - retry: jest.fn() -})); -jest.mock("./remoteProvider/RemoteProviderInit", () => ({ - providerConfigFromInit: jest.fn() -})); +jest.mock("./remoteProvider/httpGet"); +jest.mock("./remoteProvider/ImdsCredentials"); +jest.mock("./remoteProvider/retry"); +jest.mock("./remoteProvider/RemoteProviderInit"); describe("fromInstanceMetadata", () => { const mockTimeout = 1000; @@ -47,6 +40,7 @@ describe("fromInstanceMetadata", () => { }); beforeEach(() => { + ((isImdsCredentials as unknown) as jest.Mock).mockReturnValue(true); (providerConfigFromInit as jest.Mock).mockReturnValue({ timeout: mockTimeout, maxRetries: mockMaxRetries @@ -74,6 +68,23 @@ describe("fromInstanceMetadata", () => { }); }); + it("trims profile returned name from IMDS", async () => { + (httpGet as jest.Mock) + .mockResolvedValueOnce(" " + mockProfile + " ") + .mockResolvedValueOnce(JSON.stringify(mockImdsCreds)); + + (retry as jest.Mock).mockImplementation((fn: any) => fn()); + (fromImdsCredentials as jest.Mock).mockReturnValue(mockCreds); + + await expect(fromInstanceMetadata()()).resolves.toEqual(mockCreds); + expect(httpGet).toHaveBeenCalledTimes(2); + expect(httpGet).toHaveBeenNthCalledWith(1, mockHttpGetOptions); + expect(httpGet).toHaveBeenNthCalledWith(2, { + ...mockHttpGetOptions, + path: `${mockHttpGetOptions.path}${mockProfile}` + }); + }); + it("passes {} to providerConfigFromInit if init not defined", async () => { (retry as jest.Mock) .mockResolvedValueOnce(mockProfile) From 5679a1e51f35c1b6e4a4de7139b1c60c58eee39e Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Thu, 2 Jul 2020 02:51:42 +0000 Subject: [PATCH 10/12] test: throws SyntaxError if requestFromEc2Imds returns unparseable creds --- .../src/fromInstanceMetadata.spec.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts b/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts index 9335bcfef7de..e270ea1f6066 100644 --- a/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts +++ b/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts @@ -149,4 +149,15 @@ describe("fromInstanceMetadata", () => { await expect(fromInstanceMetadata()()).rejects.toEqual(mockError); }); + + it("throws SyntaxError if requestFromEc2Imds returns unparseable creds", async () => { + (httpGet as jest.Mock) + .mockResolvedValueOnce(mockProfile) + .mockResolvedValueOnce("."); + (retry as jest.Mock).mockImplementation((fn: any) => fn()); + + await expect(fromInstanceMetadata()()).rejects.toEqual( + new SyntaxError("Unexpected token . in JSON at position 0") + ); + }); }); From c23e6c51f56e189a6c619c70c0887314c4a738e1 Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Thu, 2 Jul 2020 02:58:06 +0000 Subject: [PATCH 11/12] test: minor updates --- .../src/fromInstanceMetadata.spec.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts b/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts index e270ea1f6066..df791488078d 100644 --- a/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts +++ b/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts @@ -117,7 +117,7 @@ describe("fromInstanceMetadata", () => { expect((retry as jest.Mock).mock.calls[1][1]).toBe(mockMaxRetries); }); - it("throws ProviderError is credentials returned are incorrect", async () => { + it("throws ProviderError if credentials returned are incorrect", async () => { (httpGet as jest.Mock) .mockResolvedValueOnce(mockProfile) .mockResolvedValueOnce(JSON.stringify(mockImdsCreds)); @@ -130,6 +130,11 @@ describe("fromInstanceMetadata", () => { "Invalid response received from instance metadata service." ) ); + expect(retry).toHaveBeenCalledTimes(2); + expect(httpGet).toHaveBeenCalledTimes(2); + expect(isImdsCredentials).toHaveBeenCalledTimes(1); + expect(isImdsCredentials).toHaveBeenCalledWith(mockImdsCreds); + expect(fromImdsCredentials).not.toHaveBeenCalled(); }); it("throws Error if requestFromEc2Imds for profile fails", async () => { @@ -138,6 +143,8 @@ describe("fromInstanceMetadata", () => { (retry as jest.Mock).mockImplementation((fn: any) => fn()); await expect(fromInstanceMetadata()()).rejects.toEqual(mockError); + expect(retry).toHaveBeenCalledTimes(1); + expect(httpGet).toHaveBeenCalledTimes(1); }); it("throws Error if requestFromEc2Imds for credentials fails", async () => { @@ -148,6 +155,9 @@ describe("fromInstanceMetadata", () => { (retry as jest.Mock).mockImplementation((fn: any) => fn()); await expect(fromInstanceMetadata()()).rejects.toEqual(mockError); + expect(retry).toHaveBeenCalledTimes(2); + expect(httpGet).toHaveBeenCalledTimes(2); + expect(fromImdsCredentials).not.toHaveBeenCalled(); }); it("throws SyntaxError if requestFromEc2Imds returns unparseable creds", async () => { @@ -159,5 +169,8 @@ describe("fromInstanceMetadata", () => { await expect(fromInstanceMetadata()()).rejects.toEqual( new SyntaxError("Unexpected token . in JSON at position 0") ); + expect(retry).toHaveBeenCalledTimes(2); + expect(httpGet).toHaveBeenCalledTimes(2); + expect(fromImdsCredentials).not.toHaveBeenCalled(); }); }); From 3a6c6513fd15a058f83ab48f812ab74538db583e Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Thu, 2 Jul 2020 03:00:36 +0000 Subject: [PATCH 12/12] chore: remove redundant types --- .../credential-provider-imds/src/fromInstanceMetadata.spec.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts b/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts index df791488078d..84d1fbfa62ec 100644 --- a/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts +++ b/packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts @@ -5,7 +5,6 @@ import { isImdsCredentials } from "./remoteProvider/ImdsCredentials"; import { providerConfigFromInit } from "./remoteProvider/RemoteProviderInit"; -import { Credentials } from "@aws-sdk/types"; import { retry } from "./remoteProvider/retry"; import { ProviderError } from "@aws-sdk/property-provider"; @@ -32,7 +31,7 @@ describe("fromInstanceMetadata", () => { Expiration: new Date().toISOString() }); - const mockCreds: Credentials = Object.freeze({ + const mockCreds = Object.freeze({ accessKeyId: mockImdsCreds.AccessKeyId, secretAccessKey: mockImdsCreds.SecretAccessKey, sessionToken: mockImdsCreds.Token,