UNPKG

@aws-cdk-testing/cli-integ

Version:

Integration tests for the AWS CDK CLI

112 lines (99 loc) 3.69 kB
import { AtmosphereClient } from '@cdklabs/cdk-atmosphere-client'; import { AwsClients } from './aws'; import { TestContext } from './integ-test'; import { ResourcePool } from './resource-pool'; import { DisableBootstrapContext } from './with-cdk-app'; export function atmosphereEnabled(): boolean { const enabled = process.env.CDK_INTEG_ATMOSPHERE_ENABLED; return enabled === 'true' || enabled === '1'; } export function atmosphereEndpoint(): string { const value = process.env.CDK_INTEG_ATMOSPHERE_ENDPOINT; if (!value) { throw new Error('CDK_INTEG_ATMOSPHERE_ENDPOINT is not defined'); } return value; } export function atmospherePool() { const value = process.env.CDK_INTEG_ATMOSPHERE_POOL; if (!value) { throw new Error('CDK_INTEG_ATMOSPHERE_POOL is not defined'); } return value; } export type AwsContext = { readonly aws: AwsClients }; /** * Higher order function to execute a block with an AWS client setup * * Allocate the next region from the REGION pool and dispose it afterwards. */ export function withAws<A extends TestContext>( block: (context: A & AwsContext & DisableBootstrapContext) => Promise<void>, disableBootstrap: boolean = false, ): (context: A) => Promise<void> { return async (context: A) => { if (atmosphereEnabled()) { const atmosphere = new AtmosphereClient(atmosphereEndpoint()); const allocation = await atmosphere.acquire({ pool: atmospherePool(), requester: context.name, timeoutSeconds: 60 * 30 }); const aws = await AwsClients.forIdentity(allocation.environment.region, { accessKeyId: allocation.credentials.accessKeyId, secretAccessKey: allocation.credentials.secretAccessKey, sessionToken: allocation.credentials.sessionToken, accountId: allocation.environment.account, }, context.output); await sanityCheck(aws); let outcome = 'success'; try { return await block({ ...context, disableBootstrap, aws }); } catch (e: any) { outcome = 'failure'; throw e; } finally { await atmosphere.release(allocation.id, outcome); } } else { return regionPool().using(async (region) => { const aws = await AwsClients.forRegion(region, context.output); await sanityCheck(aws); return block({ ...context, disableBootstrap, aws }); }); } }; } let _regionPool: undefined | ResourcePool; export function regionPool(): ResourcePool { if (_regionPool !== undefined) { return _regionPool; } const REGIONS = process.env.AWS_REGIONS ? process.env.AWS_REGIONS.split(',') : [process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION ?? 'us-east-1']; _regionPool = ResourcePool.withResources('aws_regions', REGIONS); return _regionPool; } /** * Perform a one-time quick sanity check that the AWS clients have properly configured credentials * * If we don't do this, calls are going to fail and they'll be retried and everything will take * forever before the user notices a simple misconfiguration. * * We can't check for the presence of environment variables since credentials could come from * anywhere, so do simple account retrieval. * * Only do it once per process. */ async function sanityCheck(aws: AwsClients) { if (sanityChecked === undefined) { try { await aws.account(); sanityChecked = true; } catch (e: any) { sanityChecked = false; throw new Error(`AWS credentials probably not configured, got error: ${e.message}`); } } if (!sanityChecked) { throw new Error('AWS credentials probably not configured, see previous error'); } } let sanityChecked: boolean | undefined;