From 11c4a7b5ac00763139f130595a37e182d34d9a72 Mon Sep 17 00:00:00 2001 From: AllanZhengYP Date: Thu, 10 Mar 2022 11:31:31 -0800 Subject: [PATCH] feat(credential-provider-imds): accept custom logger (#3409) * fix(types): logger interface * feat(credential-provider-imds): accept custom logger * chore: remove unused imports --- .../src/fromInstanceMetadata.ts | 2 +- .../src/remoteProvider/RemoteProviderInit.ts | 6 +++++- ...tExtendedInstanceMetadataCredentials.spec.ts | 13 ++++++++----- .../getExtendedInstanceMetadataCredentials.ts | 9 +++++---- .../src/utils/staticStabilityProvider.spec.ts | 12 ++++++++++++ .../src/utils/staticStabilityProvider.ts | 17 +++++++++++------ packages/middleware-logger/package.json | 1 - .../middleware-logger/src/loggerMiddleware.ts | 1 - .../middleware-signing/src/configurations.ts | 12 ++++++++++-- packages/types/src/logger.ts | 8 ++++---- 10 files changed, 56 insertions(+), 25 deletions(-) diff --git a/packages/credential-provider-imds/src/fromInstanceMetadata.ts b/packages/credential-provider-imds/src/fromInstanceMetadata.ts index 5bb5b583b783..333dc8ed153d 100644 --- a/packages/credential-provider-imds/src/fromInstanceMetadata.ts +++ b/packages/credential-provider-imds/src/fromInstanceMetadata.ts @@ -18,7 +18,7 @@ const IMDS_TOKEN_PATH = "/latest/api/token"; * Instance Metadata Service */ export const fromInstanceMetadata = (init: RemoteProviderInit = {}): Provider => - staticStabilityProvider(getInstanceImdsProvider(init)); + staticStabilityProvider(getInstanceImdsProvider(init), { logger: init.logger }); const getInstanceImdsProvider = (init: RemoteProviderInit) => { // when set to true, metadata service will not fetch token diff --git a/packages/credential-provider-imds/src/remoteProvider/RemoteProviderInit.ts b/packages/credential-provider-imds/src/remoteProvider/RemoteProviderInit.ts index dd626152f6d8..aaf24c84978d 100644 --- a/packages/credential-provider-imds/src/remoteProvider/RemoteProviderInit.ts +++ b/packages/credential-provider-imds/src/remoteProvider/RemoteProviderInit.ts @@ -1,3 +1,5 @@ +import { Logger } from "@aws-sdk/types"; + export const DEFAULT_TIMEOUT = 1000; // The default in AWS SDK for Python and CLI (botocore) is no retry or one attempt @@ -16,7 +18,9 @@ export interface RemoteProviderConfig { maxRetries: number; } -export type RemoteProviderInit = Partial; +export interface RemoteProviderInit extends Partial { + logger?: Logger; +} export const providerConfigFromInit = ({ maxRetries = DEFAULT_MAX_RETRIES, diff --git a/packages/credential-provider-imds/src/utils/getExtendedInstanceMetadataCredentials.spec.ts b/packages/credential-provider-imds/src/utils/getExtendedInstanceMetadataCredentials.spec.ts index be6090f93c10..6b8f9f55af25 100644 --- a/packages/credential-provider-imds/src/utils/getExtendedInstanceMetadataCredentials.spec.ts +++ b/packages/credential-provider-imds/src/utils/getExtendedInstanceMetadataCredentials.spec.ts @@ -1,3 +1,5 @@ +import { Logger } from "@aws-sdk/types"; + import { getExtendedInstanceMetadataCredentials } from "./getExtendedInstanceMetadataCredentials"; describe("getExtendedInstanceMetadataCredentials()", () => { @@ -6,9 +8,11 @@ describe("getExtendedInstanceMetadataCredentials()", () => { accessKeyId: "key", secretAccessKey: "secret", }; + const logger: Logger = { + warn: jest.fn(), + } as any; beforeEach(() => { - jest.spyOn(global.console, "warn").mockImplementation(() => {}); jest.spyOn(global.Math, "random"); nowMock = jest.spyOn(Date, "now").mockReturnValueOnce(new Date("2022-02-22T00:00:00Z").getTime()); }); @@ -20,7 +24,7 @@ describe("getExtendedInstanceMetadataCredentials()", () => { it("should extend the expiration random time(~15 mins) from now", () => { const anyDate: Date = "any date" as unknown as Date; (Math.random as jest.Mock).mockReturnValue(0.5); - expect(getExtendedInstanceMetadataCredentials({ ...staticSecret, expiration: anyDate })).toEqual({ + expect(getExtendedInstanceMetadataCredentials({ ...staticSecret, expiration: anyDate }, logger)).toEqual({ ...staticSecret, originalExpiration: anyDate, expiration: new Date("2022-02-22T00:17:30Z"), @@ -30,8 +34,7 @@ describe("getExtendedInstanceMetadataCredentials()", () => { it("should print warning message when extending the credentials", () => { const anyDate: Date = "any date" as unknown as Date; - getExtendedInstanceMetadataCredentials({ ...staticSecret, expiration: anyDate }); - // TODO: fill the doc link - expect(console.warn).toBeCalledWith(expect.stringContaining("Attempting credential expiration extension")); + getExtendedInstanceMetadataCredentials({ ...staticSecret, expiration: anyDate }, logger); + expect(logger.warn).toBeCalledWith(expect.stringContaining("Attempting credential expiration extension")); }); }); diff --git a/packages/credential-provider-imds/src/utils/getExtendedInstanceMetadataCredentials.ts b/packages/credential-provider-imds/src/utils/getExtendedInstanceMetadataCredentials.ts index 709c49a6dd0f..a1a6612482d6 100644 --- a/packages/credential-provider-imds/src/utils/getExtendedInstanceMetadataCredentials.ts +++ b/packages/credential-provider-imds/src/utils/getExtendedInstanceMetadataCredentials.ts @@ -1,19 +1,20 @@ +import { Logger } from "@aws-sdk/types"; + import { InstanceMetadataCredentials } from "../types"; const STATIC_STABILITY_REFRESH_INTERVAL_SECONDS = 15 * 60; const STATIC_STABILITY_REFRESH_INTERVAL_JITTER_WINDOW_SECONDS = 5 * 60; -// TODO const STATIC_STABILITY_DOC_URL = "https://docs.aws.amazon.com/sdkref/latest/guide/feature-static-credentials.html"; export const getExtendedInstanceMetadataCredentials = ( - credentials: InstanceMetadataCredentials + credentials: InstanceMetadataCredentials, + logger: Logger ): InstanceMetadataCredentials => { const refreshInterval = STATIC_STABILITY_REFRESH_INTERVAL_SECONDS + Math.floor(Math.random() * STATIC_STABILITY_REFRESH_INTERVAL_JITTER_WINDOW_SECONDS); const newExpiration = new Date(Date.now() + refreshInterval * 1000); - // ToDo: Call warn function on logger from configuration - console.warn( + logger.warn( "Attempting credential expiration extension due to a credential service availability issue. A refresh of these " + "credentials will be attempted after ${new Date(newExpiration)}.\nFor more information, please visit: " + STATIC_STABILITY_DOC_URL diff --git a/packages/credential-provider-imds/src/utils/staticStabilityProvider.spec.ts b/packages/credential-provider-imds/src/utils/staticStabilityProvider.spec.ts index 1b7a11bb61f8..5b6292b7cab1 100644 --- a/packages/credential-provider-imds/src/utils/staticStabilityProvider.spec.ts +++ b/packages/credential-provider-imds/src/utils/staticStabilityProvider.spec.ts @@ -1,3 +1,5 @@ +import { Logger } from "@aws-sdk/types"; + import { getExtendedInstanceMetadataCredentials } from "./getExtendedInstanceMetadataCredentials"; import { staticStabilityProvider } from "./staticStabilityProvider"; @@ -84,4 +86,14 @@ describe("staticStabilityProvider", () => { expect(getExtendedInstanceMetadataCredentials).toBeCalledTimes(repeat); expect(console.warn).not.toBeCalled(); }); + + it("should allow custom logger to print warning messages", async () => { + const provider = jest.fn().mockResolvedValueOnce(mockCreds).mockRejectedValue("Error"); + const logger = { warn: jest.fn() } as unknown as Logger; + const stableProvider = staticStabilityProvider(provider, { logger }); + expect(await stableProvider()).toEqual(mockCreds); // load initial creds + await stableProvider(); + expect(logger.warn).toBeCalledTimes(1); + expect(console.warn).toBeCalledTimes(0); + }); }); diff --git a/packages/credential-provider-imds/src/utils/staticStabilityProvider.ts b/packages/credential-provider-imds/src/utils/staticStabilityProvider.ts index f9074c73a5ca..5b563ffca4a0 100644 --- a/packages/credential-provider-imds/src/utils/staticStabilityProvider.ts +++ b/packages/credential-provider-imds/src/utils/staticStabilityProvider.ts @@ -1,4 +1,4 @@ -import { Credentials, Provider } from "@aws-sdk/types"; +import { Credentials, Logger, Provider } from "@aws-sdk/types"; import { InstanceMetadataCredentials } from "../types"; import { getExtendedInstanceMetadataCredentials } from "./getExtendedInstanceMetadataCredentials"; @@ -13,21 +13,26 @@ import { getExtendedInstanceMetadataCredentials } from "./getExtendedInstanceMet * @returns A credential provider that supports static stability */ export const staticStabilityProvider = ( - provider: Provider + provider: Provider, + options: { + logger?: Logger; + } = {} ): Provider => { + // Unlike normal SDK logger message, the key extension message must be transparent to users. + // When customer doesn't supply a custom logger, we need to log the warnings to console. + const logger = options?.logger || console; let pastCredentials: InstanceMetadataCredentials; return async () => { let credentials: InstanceMetadataCredentials; try { credentials = await provider(); if (credentials.expiration && credentials.expiration.getTime() < Date.now()) { - credentials = getExtendedInstanceMetadataCredentials(credentials); + credentials = getExtendedInstanceMetadataCredentials(credentials, logger); } } catch (e) { if (pastCredentials) { - // ToDo: Call warn function on logger from configuration - console.warn("Credential renew failed: ", e); - credentials = getExtendedInstanceMetadataCredentials(pastCredentials); + logger.warn("Credential renew failed: ", e); + credentials = getExtendedInstanceMetadataCredentials(pastCredentials, logger); } else { throw e; } diff --git a/packages/middleware-logger/package.json b/packages/middleware-logger/package.json index 9b5993560a09..1ca4947c1e37 100644 --- a/packages/middleware-logger/package.json +++ b/packages/middleware-logger/package.json @@ -24,7 +24,6 @@ "tslib": "^2.3.0" }, "devDependencies": { - "@aws-sdk/protocol-http": "*", "@tsconfig/recommended": "1.0.1", "@types/node": "^10.0.0", "concurrently": "7.0.0", diff --git a/packages/middleware-logger/src/loggerMiddleware.ts b/packages/middleware-logger/src/loggerMiddleware.ts index 0abd5a3a9dc6..44396ce51311 100644 --- a/packages/middleware-logger/src/loggerMiddleware.ts +++ b/packages/middleware-logger/src/loggerMiddleware.ts @@ -1,4 +1,3 @@ -import { HttpResponse } from "@aws-sdk/protocol-http"; import { AbsoluteLocation, HandlerExecutionContext, diff --git a/packages/middleware-signing/src/configurations.ts b/packages/middleware-signing/src/configurations.ts index ca09d5ad8770..97fa42f7d861 100644 --- a/packages/middleware-signing/src/configurations.ts +++ b/packages/middleware-signing/src/configurations.ts @@ -1,7 +1,14 @@ import { memoize } from "@aws-sdk/property-provider"; import { SignatureV4, SignatureV4CryptoInit, SignatureV4Init } from "@aws-sdk/signature-v4"; -import { Credentials, HashConstructor, Provider, RegionInfo, RegionInfoProvider, RequestSigner } from "@aws-sdk/types"; -import { options } from "yargs"; +import { + Credentials, + HashConstructor, + Logger, + Provider, + RegionInfo, + RegionInfoProvider, + RequestSigner, +} from "@aws-sdk/types"; // 5 minutes buffer time the refresh the credential before it really expires const CREDENTIAL_EXPIRE_WINDOW = 300000; @@ -83,6 +90,7 @@ interface SigV4PreviouslyResolved { region: string | Provider; signingName: string; sha256: HashConstructor; + logger?: Logger; } export interface AwsAuthResolvedConfig { diff --git a/packages/types/src/logger.ts b/packages/types/src/logger.ts index e8f23cce332b..75e829bb8cf6 100644 --- a/packages/types/src/logger.ts +++ b/packages/types/src/logger.ts @@ -21,8 +21,8 @@ export interface LoggerOptions { * throughout the middleware stack. */ export interface Logger { - debug(content: object): void; - info(content: object): void; - warn(content: object): void; - error(content: object): void; + debug(...content: any[]): void; + info(...content: any[]): void; + warn(...content: any[]): void; + error(...content: any[]): void; }