packages/@aws-cdk/toolkit-lib/lib/context-providers/index.ts (113 lines of code) (raw):

import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import * as cxapi from '@aws-cdk/cx-api'; import { AmiContextProviderPlugin } from './ami'; import { AZContextProviderPlugin } from './availability-zones'; import { CcApiContextProviderPlugin } from './cc-api-provider'; import { EndpointServiceAZContextProviderPlugin } from './endpoint-service-availability-zones'; import { HostedZoneContextProviderPlugin } from './hosted-zones'; import { KeyContextProviderPlugin } from './keys'; import { LoadBalancerContextProviderPlugin, LoadBalancerListenerContextProviderPlugin } from './load-balancers'; import { SecurityGroupContextProviderPlugin } from './security-groups'; import { SSMContextProviderPlugin } from './ssm-parameters'; import { VpcNetworkContextProviderPlugin } from './vpcs'; import type { SdkProvider } from '../api/aws-auth/private'; import type { Context } from '../api/context'; import { TRANSIENT_CONTEXT_KEY } from '../api/context'; import { replaceEnvPlaceholders } from '../api/environment'; import { IO } from '../api/io/private'; import type { IoHelper } from '../api/io/private'; import type { PluginHost, ContextProviderPlugin } from '../api/plugin'; import { ContextProviderError } from '../api/toolkit-error'; import { formatErrorMessage } from '../util'; type ContextProviderFactory = ((sdk: SdkProvider, io: IContextProviderMessages) => ContextProviderPlugin); type ProviderMap = {[name: string]: ContextProviderFactory}; const PLUGIN_PROVIDER_PREFIX = 'plugin'; export interface IContextProviderMessages { /** * A message that is presented to users in normal mode of operation. * * Should be used sparingly. The Context Provider framework already provides useful output by default. * This can be uses in exceptionally situations, e.g. if a lookup call is expected to take a long time. */ info(message: string): Promise<void>; /** * A message that helps users debugging the context provider. * * Should be used in most cases to note on current action. */ debug(message: string): Promise<void>; } class ContextProviderMessages implements IContextProviderMessages { private readonly ioHelper: IoHelper; private readonly providerName: string; public constructor(ioHelper: IoHelper, providerName: string) { this.ioHelper = ioHelper; this.providerName = providerName; } public async info(message: string): Promise<void> { return this.ioHelper.notify(IO.CDK_ASSEMBLY_I0300.msg(message, { provider: this.providerName, })); } public async debug(message: string): Promise<void> { return this.ioHelper.notify(IO.CDK_ASSEMBLY_I0301.msg(message, { provider: this.providerName, })); } } /** * Iterate over the list of missing context values and invoke the appropriate providers from the map to retrieve them */ export async function provideContextValues( missingValues: cxschema.MissingContext[], context: Context, sdk: SdkProvider, pluginHost: PluginHost, ioHelper: IoHelper, ) { for (const missingContext of missingValues) { const key = missingContext.key; const providerName = missingContext.provider === cxschema.ContextProvider.PLUGIN ? `${PLUGIN_PROVIDER_PREFIX}:${(missingContext.props as cxschema.PluginContextQuery).pluginName}` : missingContext.provider; let factory; if (providerName.startsWith(`${PLUGIN_PROVIDER_PREFIX}:`)) { const plugin = pluginHost.contextProviderPlugins[providerName.substring(PLUGIN_PROVIDER_PREFIX.length + 1)]; if (!plugin) { // eslint-disable-next-line max-len throw new ContextProviderError(`Unrecognized plugin context provider name: ${missingContext.provider}.`); } factory = () => plugin; } else { factory = availableContextProviders[providerName]; if (!factory) { // eslint-disable-next-line max-len throw new ContextProviderError(`Unrecognized context provider name: ${missingContext.provider}. You might need to update the toolkit to match the version of the construct library.`); } } const provider = factory(sdk, new ContextProviderMessages(ioHelper, providerName)); let value; try { const environment = missingContext.props.account && missingContext.props.region ? cxapi.EnvironmentUtils.make(missingContext.props.account, missingContext.props.region) : undefined; const resolvedEnvironment: cxapi.Environment = environment ? await sdk.resolveEnvironment(environment) : { account: '?', region: '?', name: '?' }; const arns = await replaceEnvPlaceholders({ lookupRoleArn: missingContext.props.lookupRoleArn, }, resolvedEnvironment, sdk); value = await provider.getValue({ ...missingContext.props, lookupRoleArn: arns.lookupRoleArn }); } catch (e: any) { // Set a specially formatted provider value which will be interpreted // as a lookup failure in the toolkit. value = { [cxapi.PROVIDER_ERROR_KEY]: formatErrorMessage(e), [TRANSIENT_CONTEXT_KEY]: true }; } context.set(key, value); await ioHelper.notify(IO.DEFAULT_ASSEMBLY_DEBUG.msg(`Setting "${key}" context to ${JSON.stringify(value)}`)); } } /** * Register a context provider * * A context provider cannot reuse the SDKs authentication mechanisms. */ export function registerContextProvider(name: string, provider: ContextProviderPlugin) { availableContextProviders[name] = () => provider; } /** * Register a plugin context provider * * A plugin provider cannot reuse the SDKs authentication mechanisms. */ export function registerPluginContextProvider(name: string, provider: ContextProviderPlugin) { registerContextProvider(`${PLUGIN_PROVIDER_PREFIX}:${name}`, provider); } /** * Register a context provider factory * * A context provider factory takes an SdkProvider and returns the context provider plugin. */ export function registerContextProviderFactory(name: string, provider: ContextProviderFactory) { availableContextProviders[name] = provider; } const availableContextProviders: ProviderMap = { [cxschema.ContextProvider.AVAILABILITY_ZONE_PROVIDER]: (s, io) => new AZContextProviderPlugin(s, io), [cxschema.ContextProvider.SSM_PARAMETER_PROVIDER]: (s, io) => new SSMContextProviderPlugin(s, io), [cxschema.ContextProvider.HOSTED_ZONE_PROVIDER]: (s, io) => new HostedZoneContextProviderPlugin(s, io), [cxschema.ContextProvider.VPC_PROVIDER]: (s, io) => new VpcNetworkContextProviderPlugin(s, io), [cxschema.ContextProvider.AMI_PROVIDER]: (s, io) => new AmiContextProviderPlugin(s, io), [cxschema.ContextProvider.ENDPOINT_SERVICE_AVAILABILITY_ZONE_PROVIDER]: (s, io) => new EndpointServiceAZContextProviderPlugin(s, io), [cxschema.ContextProvider.SECURITY_GROUP_PROVIDER]: (s) => new SecurityGroupContextProviderPlugin(s), [cxschema.ContextProvider.LOAD_BALANCER_PROVIDER]: (s) => new LoadBalancerContextProviderPlugin(s), [cxschema.ContextProvider.LOAD_BALANCER_LISTENER_PROVIDER]: (s) => new LoadBalancerListenerContextProviderPlugin(s), [cxschema.ContextProvider.KEY_PROVIDER]: (s, io) => new KeyContextProviderPlugin(s, io), [cxschema.ContextProvider.CC_API_PROVIDER]: (s) => new CcApiContextProviderPlugin(s), };