diff --git a/.changeset/late-glasses-jam.md b/.changeset/late-glasses-jam.md new file mode 100644 index 00000000000..1f45828f3f5 --- /dev/null +++ b/.changeset/late-glasses-jam.md @@ -0,0 +1,8 @@ +--- +"@smithy/credential-provider-imds": minor +"@smithy/shared-ini-file-loader": minor +"@smithy/node-config-provider": minor +"@smithy/property-provider": minor +--- + +new logging-compatible signature for CredentialsProviderError diff --git a/packages/credential-provider-imds/src/fromContainerMetadata.ts b/packages/credential-provider-imds/src/fromContainerMetadata.ts index 0336931c361..2cfdff55568 100644 --- a/packages/credential-provider-imds/src/fromContainerMetadata.ts +++ b/packages/credential-provider-imds/src/fromContainerMetadata.ts @@ -1,5 +1,5 @@ import { CredentialsProviderError } from "@smithy/property-provider"; -import { AwsCredentialIdentityProvider } from "@smithy/types"; +import { AwsCredentialIdentityProvider, Logger } from "@smithy/types"; import { RequestOptions } from "http"; import { parse } from "url"; @@ -31,10 +31,12 @@ export const fromContainerMetadata = (init: RemoteProviderInit = {}): AwsCredent const { timeout, maxRetries } = providerConfigFromInit(init); return () => retry(async () => { - const requestOptions = await getCmdsUri(); + const requestOptions = await getCmdsUri({ logger: init.logger }); const credsResponse = JSON.parse(await requestFromEcsImds(timeout, requestOptions)); if (!isImdsCredentials(credsResponse)) { - throw new CredentialsProviderError("Invalid response received from instance metadata service."); + throw new CredentialsProviderError("Invalid response received from instance metadata service.", { + logger: init.logger, + }); } return fromImdsCredentials(credsResponse); }, maxRetries); @@ -65,7 +67,7 @@ const GREENGRASS_PROTOCOLS = { "https:": true, }; -const getCmdsUri = async (): Promise => { +const getCmdsUri = async ({ logger }: { logger?: Logger }): Promise => { if (process.env[ENV_CMDS_RELATIVE_URI]) { return { hostname: CMDS_IP, @@ -76,17 +78,17 @@ const getCmdsUri = async (): Promise => { if (process.env[ENV_CMDS_FULL_URI]) { const parsed = parse(process.env[ENV_CMDS_FULL_URI]!); if (!parsed.hostname || !(parsed.hostname in GREENGRASS_HOSTS)) { - throw new CredentialsProviderError( - `${parsed.hostname} is not a valid container metadata service hostname`, - false - ); + throw new CredentialsProviderError(`${parsed.hostname} is not a valid container metadata service hostname`, { + tryNextLink: false, + logger, + }); } if (!parsed.protocol || !(parsed.protocol in GREENGRASS_PROTOCOLS)) { - throw new CredentialsProviderError( - `${parsed.protocol} is not a valid container metadata service protocol`, - false - ); + throw new CredentialsProviderError(`${parsed.protocol} is not a valid container metadata service protocol`, { + tryNextLink: false, + logger, + }); } return { @@ -99,6 +101,9 @@ const getCmdsUri = async (): Promise => { "The container metadata credential provider cannot be used unless" + ` the ${ENV_CMDS_RELATIVE_URI} or ${ENV_CMDS_FULL_URI} environment` + " variable is set", - false + { + tryNextLink: false, + logger, + } ); }; diff --git a/packages/credential-provider-imds/src/fromInstanceMetadata.ts b/packages/credential-provider-imds/src/fromInstanceMetadata.ts index 875d238b542..b1fca9f047e 100644 --- a/packages/credential-provider-imds/src/fromInstanceMetadata.ts +++ b/packages/credential-provider-imds/src/fromInstanceMetadata.ts @@ -25,9 +25,12 @@ const X_AWS_EC2_METADATA_TOKEN = "x-aws-ec2-metadata-token"; * Instance Metadata Service */ export const fromInstanceMetadata = (init: RemoteProviderInit = {}): Provider => - staticStabilityProvider(getInstanceImdsProvider(init), { logger: init.logger }); + staticStabilityProvider(getInstanceMetadataProvider(init), { logger: init.logger }); -const getInstanceImdsProvider = (init: RemoteProviderInit) => { +/** + * @internal + */ +const getInstanceMetadataProvider = (init: RemoteProviderInit = {}) => { // when set to true, metadata service will not fetch token let disableFetchToken = false; const { logger, profile } = init; @@ -47,7 +50,8 @@ const getInstanceImdsProvider = (init: RemoteProviderInit) => { fallbackBlockedFromProcessEnv = !!envValue && envValue !== "false"; if (envValue === undefined) { throw new CredentialsProviderError( - `${AWS_EC2_METADATA_V1_DISABLED} not set in env, checking config file next.` + `${AWS_EC2_METADATA_V1_DISABLED} not set in env, checking config file next.`, + { logger: init.logger } ); } return fallbackBlockedFromProcessEnv; @@ -98,7 +102,7 @@ const getInstanceImdsProvider = (init: RemoteProviderInit) => { return retry(async () => { let creds: AwsCredentialIdentity; try { - creds = await getCredentialsFromProfile(imdsProfile, options); + creds = await getCredentialsFromProfile(imdsProfile, options, init); } catch (err) { if (err.statusCode === 401) { disableFetchToken = false; @@ -152,8 +156,8 @@ const getMetadataToken = async (options: RequestOptions) => const getProfile = async (options: RequestOptions) => (await httpRequest({ ...options, path: IMDS_PATH })).toString(); -const getCredentialsFromProfile = async (profile: string, options: RequestOptions) => { - const credsResponse = JSON.parse( +const getCredentialsFromProfile = async (profile: string, options: RequestOptions, init: RemoteProviderInit) => { + const credentialsResponse = JSON.parse( ( await httpRequest({ ...options, @@ -162,9 +166,11 @@ const getCredentialsFromProfile = async (profile: string, options: RequestOption ).toString() ); - if (!isImdsCredentials(credsResponse)) { - throw new CredentialsProviderError("Invalid response received from instance metadata service."); + if (!isImdsCredentials(credentialsResponse)) { + throw new CredentialsProviderError("Invalid response received from instance metadata service.", { + logger: init.logger, + }); } - return fromImdsCredentials(credsResponse); + return fromImdsCredentials(credentialsResponse); }; diff --git a/packages/node-config-provider/src/fromEnv.spec.ts b/packages/node-config-provider/src/fromEnv.spec.ts index 9ead7ad07f3..2b1508396a9 100644 --- a/packages/node-config-provider/src/fromEnv.spec.ts +++ b/packages/node-config-provider/src/fromEnv.spec.ts @@ -4,26 +4,30 @@ import { fromEnv, GetterFromEnv } from "./fromEnv"; describe("fromEnv", () => { describe("with env var getter", () => { - const envVarName = "ENV_VAR_NAME"; + const ENV_VAR_NAME = "ENV_VAR_NAME"; // Using Record instead of NodeJS.ProcessEnv, in order to not get type errors in non node environments - const envVarGetter: GetterFromEnv = (env: Record) => env[envVarName]!; - const envVarValue = process.env[envVarName]; + const envVarGetter: GetterFromEnv = (env: Record) => env[ENV_VAR_NAME]!; + const envVarValue = process.env[ENV_VAR_NAME]; const mockEnvVarValue = "mockEnvVarValue"; - const getCredentialsProviderError = (getter: GetterFromEnv) => - new CredentialsProviderError(`Cannot load config from environment variables with getter: ${getter}`); - beforeEach(() => { - delete process.env[envVarName]; + delete process.env[ENV_VAR_NAME]; }); afterAll(() => { - process.env[envVarName] = envVarValue; + process.env[ENV_VAR_NAME] = envVarValue; + }); + + describe("CredentialsProviderError", () => { + it("is behaving as expected cross-package in jest", () => { + expect(new CredentialsProviderError("msg", {}).message).toEqual("msg"); + expect(new CredentialsProviderError("msg", {}).name).toEqual("CredentialsProviderError"); + }); }); - it(`returns string value in '${envVarName}' env var when set`, () => { - process.env[envVarName] = mockEnvVarValue; + it(`returns string value in '${ENV_VAR_NAME}' env var when set`, () => { + process.env[ENV_VAR_NAME] = mockEnvVarValue; return expect(fromEnv(envVarGetter)()).resolves.toBe(mockEnvVarValue); }); @@ -35,9 +39,10 @@ describe("fromEnv", () => { return expect(fromEnv(getter)()).resolves.toEqual(value); }); - it(`throws when '${envVarName}' env var is not set`, () => { + it(`throws when '${ENV_VAR_NAME}' env var is not set`, async () => { expect.assertions(1); - return expect(fromEnv(envVarGetter)()).rejects.toMatchObject(getCredentialsProviderError(envVarGetter)); + const error = await fromEnv(envVarGetter)().catch((_) => _); + return expect(error).toEqual(new CredentialsProviderError(`Not found in ENV: ENV_VAR_NAME`, {})); }); it("throws when the getter function throws", () => { diff --git a/packages/node-config-provider/src/fromEnv.ts b/packages/node-config-provider/src/fromEnv.ts index 7d28654b30b..33941b27fcc 100644 --- a/packages/node-config-provider/src/fromEnv.ts +++ b/packages/node-config-provider/src/fromEnv.ts @@ -1,5 +1,7 @@ import { CredentialsProviderError } from "@smithy/property-provider"; -import { Provider } from "@smithy/types"; +import { Logger, Provider } from "@smithy/types"; + +import { getSelectorName } from "./getSelectorName"; // Using Record instead of NodeJS.ProcessEnv, in order to not get type errors in non node environments export type GetterFromEnv = (env: Record) => T | undefined; @@ -9,7 +11,7 @@ export type GetterFromEnv = (env: Record) => T | * environment variable. */ export const fromEnv = - (envVarSelector: GetterFromEnv): Provider => + (envVarSelector: GetterFromEnv, logger?: Logger): Provider => async () => { try { const config = envVarSelector(process.env); @@ -19,7 +21,8 @@ export const fromEnv = return config as T; } catch (e) { throw new CredentialsProviderError( - e.message || `Cannot load config from environment variables with getter: ${envVarSelector}` + e.message || `Not found in ENV: ${getSelectorName(envVarSelector.toString())}`, + { logger } ); } }; diff --git a/packages/node-config-provider/src/fromSharedConfigFiles.spec.ts b/packages/node-config-provider/src/fromSharedConfigFiles.spec.ts index c2b3c2edb7f..3382dc9d947 100644 --- a/packages/node-config-provider/src/fromSharedConfigFiles.spec.ts +++ b/packages/node-config-provider/src/fromSharedConfigFiles.spec.ts @@ -10,13 +10,11 @@ jest.mock("@smithy/shared-ini-file-loader", () => ({ })); describe("fromSharedConfigFiles", () => { - const configKey = "config_key"; - const configGetter: GetterFromConfig = (profile: Profile) => profile[configKey]; + const CONFIG_KEY = "config_key"; + const configGetter: GetterFromConfig = (profile: Profile) => profile[CONFIG_KEY]; - const getCredentialsProviderError = (profile: string, getter: GetterFromConfig) => - new CredentialsProviderError( - `Cannot load config for profile ${profile} in SDK configuration files with getter: ${getter}` - ); + const getCredentialsProviderError = (profile: string) => + new CredentialsProviderError(`Not found in config files w/ profile [${profile}]: CONFIG_KEY`, {}); describe("loadedConfig", () => { const mockConfigAnswer = "mockConfigAnswer"; @@ -36,21 +34,21 @@ describe("fromSharedConfigFiles", () => { { message: "returns configValue from default profile", iniDataInConfig: { - default: { [configKey]: mockConfigAnswer }, + default: { [CONFIG_KEY]: mockConfigAnswer }, }, iniDataInCredentials: { - default: { [configKey]: mockCredentialsNotAnswer }, + default: { [CONFIG_KEY]: mockCredentialsNotAnswer }, }, configValueToVerify: mockConfigAnswer, }, { message: "returns configValue from designated profile", iniDataInConfig: { - default: { [configKey]: mockConfigNotAnswer }, - foo: { [configKey]: mockConfigAnswer }, + default: { [CONFIG_KEY]: mockConfigNotAnswer }, + foo: { [CONFIG_KEY]: mockConfigAnswer }, }, iniDataInCredentials: { - foo: { [configKey]: mockCredentialsNotAnswer }, + foo: { [CONFIG_KEY]: mockCredentialsNotAnswer }, }, profile: "foo", configValueToVerify: mockConfigAnswer, @@ -58,11 +56,11 @@ describe("fromSharedConfigFiles", () => { { message: "returns configValue from credentials file if preferred", iniDataInConfig: { - default: { [configKey]: mockConfigNotAnswer }, - foo: { [configKey]: mockConfigNotAnswer }, + default: { [CONFIG_KEY]: mockConfigNotAnswer }, + foo: { [CONFIG_KEY]: mockConfigNotAnswer }, }, iniDataInCredentials: { - foo: { [configKey]: mockCredentialsAnswer }, + foo: { [CONFIG_KEY]: mockCredentialsAnswer }, }, profile: "foo", preferredFile: "credentials", @@ -71,7 +69,7 @@ describe("fromSharedConfigFiles", () => { { message: "returns configValue from config file if preferred credentials file doesn't contain config", iniDataInConfig: { - foo: { [configKey]: mockConfigAnswer }, + foo: { [CONFIG_KEY]: mockConfigAnswer }, }, iniDataInCredentials: {}, configValueToVerify: mockConfigAnswer, @@ -82,7 +80,7 @@ describe("fromSharedConfigFiles", () => { message: "returns configValue from credential file if preferred config file doesn't contain config", iniDataInConfig: {}, iniDataInCredentials: { - foo: { [configKey]: mockCredentialsAnswer }, + foo: { [CONFIG_KEY]: mockCredentialsAnswer }, }, configValueToVerify: mockCredentialsAnswer, profile: "foo", @@ -93,14 +91,14 @@ describe("fromSharedConfigFiles", () => { { message: "rejects if default profile is not present and profile value is not passed", iniDataInConfig: { - foo: { [configKey]: mockConfigNotAnswer }, + foo: { [CONFIG_KEY]: mockConfigNotAnswer }, }, iniDataInCredentials: {}, }, { message: "rejects if designated profile is not present", iniDataInConfig: { - default: { [configKey]: mockConfigNotAnswer }, + default: { [CONFIG_KEY]: mockConfigNotAnswer }, }, iniDataInCredentials: {}, profile: "foo", @@ -129,8 +127,8 @@ describe("fromSharedConfigFiles", () => { credentialsFile: iniDataInCredentials, }); (getProfileName as jest.Mock).mockReturnValueOnce(profile ?? "default"); - return expect(fromSharedConfigFiles(configGetter, { profile, preferredFile })()).rejects.toMatchObject( - getCredentialsProviderError(profile ?? "default", configGetter) + return expect(fromSharedConfigFiles(configGetter, { profile, preferredFile })()).rejects.toEqual( + getCredentialsProviderError(profile ?? "default") ); }); }); @@ -144,18 +142,18 @@ describe("fromSharedConfigFiles", () => { configFile: {}, credentialsFile: {}, }); - return expect(fromSharedConfigFiles(failGetter)()).rejects.toMatchObject(new CredentialsProviderError(message)); + return expect(fromSharedConfigFiles(failGetter)()).rejects.toEqual(new CredentialsProviderError(message)); }); }); describe("profile", () => { const loadedConfigData = { configFile: { - default: { [configKey]: "configFileDefault" }, - foo: { [configKey]: "configFileFoo" }, + default: { [CONFIG_KEY]: "configFileDefault" }, + foo: { [CONFIG_KEY]: "configFileFoo" }, }, credentialsFile: { - default: { [configKey]: "credentialsFileDefault" }, + default: { [CONFIG_KEY]: "credentialsFileDefault" }, }, }; @@ -166,7 +164,7 @@ describe("fromSharedConfigFiles", () => { it.each(["foo", "default"])("returns config value from %s profile", (profile) => { (getProfileName as jest.Mock).mockReturnValueOnce(profile); return expect(fromSharedConfigFiles(configGetter)()).resolves.toBe( - loadedConfigData.configFile[profile][configKey] + loadedConfigData.configFile[profile][CONFIG_KEY] ); }); }); diff --git a/packages/node-config-provider/src/fromSharedConfigFiles.ts b/packages/node-config-provider/src/fromSharedConfigFiles.ts index 66a5e0ab511..4947aaaa118 100644 --- a/packages/node-config-provider/src/fromSharedConfigFiles.ts +++ b/packages/node-config-provider/src/fromSharedConfigFiles.ts @@ -2,6 +2,8 @@ import { CredentialsProviderError } from "@smithy/property-provider"; import { getProfileName, loadSharedConfigFiles, SourceProfileInit } from "@smithy/shared-ini-file-loader"; import { ParsedIniData, Profile, Provider } from "@smithy/types"; +import { getSelectorName } from "./getSelectorName"; + export interface SharedConfigInit extends SourceProfileInit { /** * The preferred shared ini file to load the config. "config" option refers to @@ -41,8 +43,8 @@ export const fromSharedConfigFiles = return configValue; } catch (e) { throw new CredentialsProviderError( - e.message || - `Cannot load config for profile ${profile} in SDK configuration files with getter: ${configSelector}` + e.message || `Not found in config files w/ profile [${profile}]: ${getSelectorName(configSelector.toString())}`, + { logger: init.logger } ); } }; diff --git a/packages/node-config-provider/src/getSelectorName.ts b/packages/node-config-provider/src/getSelectorName.ts new file mode 100644 index 00000000000..7acc4c9211b --- /dev/null +++ b/packages/node-config-provider/src/getSelectorName.ts @@ -0,0 +1,19 @@ +/** + * Attempts to extract the name of the variable that the functional selector is looking for. + * Improves readability over the raw Function.toString() value. + * @internal + * @param functionString - function's string representation. + * + * @returns constant value used within the function. + */ +export function getSelectorName(functionString: string): string { + try { + const constants = new Set(Array.from(functionString.match(/([A-Z_]){3,}/g) ?? [])); + constants.delete("CONFIG"); + constants.delete("CONFIG_PREFIX_SEPARATOR"); + constants.delete("ENV"); + return [...constants].join(", "); + } catch (e) { + return functionString; + } +} diff --git a/packages/property-provider/src/CredentialsProviderError.spec.ts b/packages/property-provider/src/CredentialsProviderError.spec.ts index db951afac61..f3deda45d8b 100644 --- a/packages/property-provider/src/CredentialsProviderError.spec.ts +++ b/packages/property-provider/src/CredentialsProviderError.spec.ts @@ -2,10 +2,44 @@ import { CredentialsProviderError } from "./CredentialsProviderError"; import { ProviderError } from "./ProviderError"; describe(CredentialsProviderError.name, () => { - it("should be named as CredentialsProviderError", () => { + it("should be named CredentialsProviderError", () => { expect(new CredentialsProviderError("PANIC").name).toBe("CredentialsProviderError"); }); + it("should have a non-enumerable message like the base Error class", () => { + expect(new CredentialsProviderError("PANIC", {}).message).toBe("PANIC"); + + expect( + { + ...new CredentialsProviderError("PANIC", {}), + }.message + ).toBe(undefined); + }); + + it("should have an enumerable tryNextLink and logger like the base Error class", () => { + expect(new CredentialsProviderError("PANIC", {}).tryNextLink).toBe(true); + + expect( + { + ...new CredentialsProviderError("PANIC", { tryNextLink: false }), + }.tryNextLink + ).toBe(false); + }); + + it("should use logger.debug if provided", () => { + const logger = { + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + debug: jest.fn(), + trace: jest.fn(), + }; + new CredentialsProviderError("PANIC", { logger }); + + expect(logger.debug).toHaveBeenCalled(); + expect(logger.trace).not.toHaveBeenCalled(); + }); + describe.each([Error, ProviderError, CredentialsProviderError])("should be instanceof %p", (classConstructor) => { it("when created using constructor", () => { expect(new CredentialsProviderError("PANIC")).toBeInstanceOf(classConstructor); diff --git a/packages/property-provider/src/CredentialsProviderError.ts b/packages/property-provider/src/CredentialsProviderError.ts index c5a62f12ef4..75067619f95 100644 --- a/packages/property-provider/src/CredentialsProviderError.ts +++ b/packages/property-provider/src/CredentialsProviderError.ts @@ -1,4 +1,4 @@ -import { ProviderError } from "./ProviderError"; +import { ProviderError, ProviderErrorOptionsType } from "./ProviderError"; /** * @public @@ -13,12 +13,27 @@ import { ProviderError } from "./ProviderError"; */ export class CredentialsProviderError extends ProviderError { name = "CredentialsProviderError"; - constructor( - message: string, - public readonly tryNextLink: boolean = true - ) { - super(message, tryNextLink); - // Remove once we stop targetting ES5. + + /** + * @override + * @deprecated constructor should be given a logger. + */ + public constructor(message: string); + /** + * @override + * @deprecated constructor should be given a logger. + */ + public constructor(message: string, tryNextLink: boolean | undefined); + /** + * @override + * This signature is preferred for logging capability. + */ + public constructor(message: string, options: ProviderErrorOptionsType); + /** + * @override + */ + public constructor(message: string, options: boolean | ProviderErrorOptionsType = true) { + super(message, options as ProviderErrorOptionsType); Object.setPrototypeOf(this, CredentialsProviderError.prototype); } } diff --git a/packages/property-provider/src/ProviderError.ts b/packages/property-provider/src/ProviderError.ts index 4f1603ebf79..f239daf5c90 100644 --- a/packages/property-provider/src/ProviderError.ts +++ b/packages/property-provider/src/ProviderError.ts @@ -1,3 +1,13 @@ +import { Logger } from "@smithy/types"; + +/** + * @public + */ +export type ProviderErrorOptionsType = { + tryNextLink?: boolean | undefined; + logger?: Logger; +}; + /** * @public * @@ -11,15 +21,41 @@ */ export class ProviderError extends Error { name = "ProviderError"; - constructor( - message: string, - public readonly tryNextLink: boolean = true - ) { + public readonly tryNextLink: boolean; + + /** + * @deprecated constructor should be given a logger. + */ + public constructor(message: string); + /** + * @deprecated constructor should be given a logger. + */ + public constructor(message: string, tryNextLink: boolean | undefined); + /** + * This signature is preferred for logging capability. + */ + public constructor(message: string, options: ProviderErrorOptionsType); + public constructor(message: string, options: boolean | ProviderErrorOptionsType = true) { + let logger: Logger | undefined; + let tryNextLink: boolean = true; + + if (typeof options === "boolean") { + logger = undefined; + tryNextLink = options; + } else if (options != null && typeof options === "object") { + logger = options.logger; + tryNextLink = options.tryNextLink ?? true; + } super(message); - // Remove once we stop targetting ES5. + this.tryNextLink = tryNextLink; Object.setPrototypeOf(this, ProviderError.prototype); + logger?.debug?.(`@smithy/property-provider ${tryNextLink ? "->" : "(!)"} ${message}`); } - static from(error: Error, tryNextLink = true): ProviderError { - return Object.assign(new this(error.message, tryNextLink), error); + + /** + * @deprecated use new operator. + */ + static from(error: Error, options: boolean | ProviderErrorOptionsType = true): ProviderError { + return Object.assign(new this(error.message, options as ProviderErrorOptionsType), error); } } diff --git a/packages/property-provider/src/TokenProviderError.ts b/packages/property-provider/src/TokenProviderError.ts index b0c461e85c8..6799830c7f0 100644 --- a/packages/property-provider/src/TokenProviderError.ts +++ b/packages/property-provider/src/TokenProviderError.ts @@ -1,4 +1,4 @@ -import { ProviderError } from "./ProviderError"; +import { ProviderError, ProviderErrorOptionsType } from "./ProviderError"; /** * @public @@ -13,12 +13,27 @@ import { ProviderError } from "./ProviderError"; */ export class TokenProviderError extends ProviderError { name = "TokenProviderError"; - constructor( - message: string, - public readonly tryNextLink: boolean = true - ) { - super(message, tryNextLink); - // Remove once we stop targetting ES5. + + /** + * @override + * @deprecated constructor should be given a logger. + */ + public constructor(message: string); + /** + * @override + * @deprecated constructor should be given a logger. + */ + public constructor(message: string, tryNextLink: boolean | undefined); + /** + * @override + * This signature is preferred for logging capability. + */ + public constructor(message: string, options: ProviderErrorOptionsType); + /** + * @override + */ + public constructor(message: string, options: boolean | ProviderErrorOptionsType = true) { + super(message, options as ProviderErrorOptionsType); Object.setPrototypeOf(this, TokenProviderError.prototype); } } diff --git a/packages/shared-ini-file-loader/src/loadSharedConfigFiles.ts b/packages/shared-ini-file-loader/src/loadSharedConfigFiles.ts index 3b16fcd408f..9bf6336e877 100644 --- a/packages/shared-ini-file-loader/src/loadSharedConfigFiles.ts +++ b/packages/shared-ini-file-loader/src/loadSharedConfigFiles.ts @@ -1,4 +1,4 @@ -import { SharedConfigFiles } from "@smithy/types"; +import { Logger, SharedConfigFiles } from "@smithy/types"; import { getConfigData } from "./getConfigData"; import { getConfigFilepath } from "./getConfigFilepath"; @@ -26,6 +26,11 @@ export interface SharedConfigInit { * property is set, the provider will always reload any configuration files loaded before. */ ignoreCache?: boolean; + + /** + * For credential resolution trace logging. + */ + logger?: Logger; } const swallowError = () => ({});