Skip to content

Commit

Permalink
use options object
Browse files Browse the repository at this point in the history
  • Loading branch information
kuhe committed May 24, 2024
1 parent 30b64b4 commit 1b16fa8
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 43 deletions.
8 changes: 8 additions & 0 deletions .changeset/late-glasses-jam.md
Original file line number Diff line number Diff line change
@@ -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
31 changes: 18 additions & 13 deletions packages/credential-provider-imds/src/fromContainerMetadata.ts
Original file line number Diff line number Diff line change
@@ -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";

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -65,7 +67,7 @@ const GREENGRASS_PROTOCOLS = {
"https:": true,
};

const getCmdsUri = async (): Promise<RequestOptions> => {
const getCmdsUri = async ({ logger }: { logger?: Logger }): Promise<RequestOptions> => {
if (process.env[ENV_CMDS_RELATIVE_URI]) {
return {
hostname: CMDS_IP,
Expand All @@ -76,17 +78,17 @@ const getCmdsUri = async (): Promise<RequestOptions> => {
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 {
Expand All @@ -99,6 +101,9 @@ const getCmdsUri = async (): Promise<RequestOptions> => {
"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,
}
);
};
11 changes: 7 additions & 4 deletions packages/credential-provider-imds/src/fromInstanceMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,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;
Expand Down Expand Up @@ -98,7 +99,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;
Expand Down Expand Up @@ -152,7 +153,7 @@ 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 getCredentialsFromProfile = async (profile: string, options: RequestOptions, init: RemoteProviderInit) => {
const credsResponse = JSON.parse(
(
await httpRequest({
Expand All @@ -163,7 +164,9 @@ const getCredentialsFromProfile = async (profile: string, options: RequestOption
);

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);
Expand Down
7 changes: 4 additions & 3 deletions packages/node-config-provider/src/fromEnv.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CredentialsProviderError } from "@smithy/property-provider";
import { Provider } from "@smithy/types";
import { Logger, Provider } from "@smithy/types";

import { getSelectorName } from "./getSelectorName";

Expand All @@ -11,7 +11,7 @@ export type GetterFromEnv<T> = (env: Record<string, string | undefined>) => T |
* environment variable.
*/
export const fromEnv =
<T = string>(envVarSelector: GetterFromEnv<T>): Provider<T> =>
<T = string>(envVarSelector: GetterFromEnv<T>, logger?: Logger): Provider<T> =>
async () => {
try {
const config = envVarSelector(process.env);
Expand All @@ -21,7 +21,8 @@ export const fromEnv =
return config as T;
} catch (e) {
throw new CredentialsProviderError(
e.message || `Not found in ENV: ${getSelectorName(envVarSelector.toString())}`
e.message || `Not found in ENV: ${getSelectorName(envVarSelector.toString())}`,
{ logger }
);
}
};
3 changes: 2 additions & 1 deletion packages/node-config-provider/src/fromSharedConfigFiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ export const fromSharedConfigFiles =
return configValue;
} catch (e) {
throw new CredentialsProviderError(
e.message || `Not found in config files w/ profile [${profile}]: ${getSelectorName(configSelector.toString())}`
e.message || `Not found in config files w/ profile [${profile}]: ${getSelectorName(configSelector.toString())}`,
{ logger: init.logger }
);
}
};
22 changes: 11 additions & 11 deletions packages/property-provider/src/CredentialsProviderError.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@ import { CredentialsProviderError } from "./CredentialsProviderError";
import { ProviderError } from "./ProviderError";

describe(CredentialsProviderError.name, () => {
afterAll(() => {
CredentialsProviderError.logLimit = 100;
});

it("should be named as CredentialsProviderError", () => {
expect(new CredentialsProviderError("PANIC").name).toBe("CredentialsProviderError");
});

it("should log up to its logLimit", () => {
CredentialsProviderError.logLimit = 5;

Array.from({ length: CredentialsProviderError.logLimit + 2 }).forEach(() => {
new CredentialsProviderError("PANIC");
});
it("should use logger.trace if provided", () => {
const logger = {
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
debug: jest.fn(),
trace: jest.fn(),
};
new CredentialsProviderError("PANIC", { logger });

expect(CredentialsProviderError.log.length).toEqual(CredentialsProviderError.logLimit);
expect(logger.debug).not.toHaveBeenCalled();
expect(logger.trace).toHaveBeenCalled();
});

describe.each([Error, ProviderError, CredentialsProviderError])("should be instanceof %p", (classConstructor) => {
Expand Down
47 changes: 37 additions & 10 deletions packages/property-provider/src/CredentialsProviderError.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import { Logger } from "@smithy/types";

import { ProviderError } from "./ProviderError";

/**
* @public
*/
export type CredentialsProviderErrorOptionsType = {
tryNextLink?: boolean | undefined;
logger?: Logger;
};

/**
* @public
*
Expand All @@ -12,18 +22,35 @@ import { ProviderError } from "./ProviderError";
* ensures the chain will stop if an entirely unexpected error is encountered.
*/
export class CredentialsProviderError extends ProviderError {
public static log: string[] = [];
public static logLimit = 100;
public name = "CredentialsProviderError";
public constructor(
message: string,
public readonly tryNextLink: boolean = true
) {
public readonly tryNextLink: boolean = true;

/**
* @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: CredentialsProviderErrorOptionsType);
public constructor(message: string, options: boolean | CredentialsProviderErrorOptionsType = 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, tryNextLink);

Object.setPrototypeOf(this, CredentialsProviderError.prototype);
CredentialsProviderError.log.push(`${new Date().toISOString()} ${tryNextLink ? "->" : "(!)"} ${message}`);
while (CredentialsProviderError.log.length > CredentialsProviderError.logLimit) {
CredentialsProviderError.log.shift();
}
logger?.trace?.(`${new Date().toISOString()} ${tryNextLink ? "->" : "(!)"} ${message}`);
}
}
7 changes: 6 additions & 1 deletion packages/shared-ini-file-loader/src/loadSharedConfigFiles.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SharedConfigFiles } from "@smithy/types";
import { Logger, SharedConfigFiles } from "@smithy/types";

import { getConfigData } from "./getConfigData";
import { getConfigFilepath } from "./getConfigFilepath";
Expand Down Expand Up @@ -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 = () => ({});
Expand Down

0 comments on commit 1b16fa8

Please sign in to comment.