UNPKG

@aws-cdk-testing/cli-integ

Version:

Integration tests for the AWS CDK CLI

379 lines (303 loc) 11.1 kB
const path = require('path'); var constructs = require('constructs'); if (process.env.PACKAGE_LAYOUT_VERSION === '1') { var cdk = require('@aws-cdk/core'); var ec2 = require('@aws-cdk/aws-ec2'); var ssm = require('@aws-cdk/aws-ssm'); var iam = require('@aws-cdk/aws-iam'); var sns = require('@aws-cdk/aws-sns'); var lambda = require('@aws-cdk/aws-lambda'); var docker = require('@aws-cdk/aws-ecr-assets'); } else { var cdk = require('aws-cdk-lib'); var { aws_ec2: ec2, aws_ssm: ssm, aws_iam: iam, aws_sns: sns, aws_lambda: lambda, aws_ecr_assets: docker } = require('aws-cdk-lib'); } const { Annotations } = cdk; const { StackWithNestedStack, StackWithNestedStackUsingParameters } = require('./nested-stack'); const stackPrefix = process.env.STACK_NAME_PREFIX; if (!stackPrefix) { throw new Error(`the STACK_NAME_PREFIX environment variable is required`); } class MyStack extends cdk.Stack { constructor(parent, id, props) { super(parent, id, props); new sns.Topic(this, 'topic'); if (cdk.AvailabilityZoneProvider) { // <= 0.34.0 new cdk.AvailabilityZoneProvider(this).availabilityZones; } else if (cdk.Context) { // <= 0.35.0 cdk.Context.getAvailabilityZones(this); } else { this.availabilityZones; } const parameterName = '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2'; getSsmParameterValue(this, parameterName); } } function getSsmParameterValue(scope, parameterName) { return ssm.StringParameter.valueFromLookup(scope, parameterName); } class YourStack extends cdk.Stack { constructor(parent, id, props) { super(parent, id, props); new sns.Topic(this, 'topic1'); new sns.Topic(this, 'topic2'); } } class StackUsingContext extends cdk.Stack { constructor(parent, id, props) { super(parent, id, props); new cdk.CfnResource(this, 'Handle', { type: 'AWS::CloudFormation::WaitConditionHandle' }); new cdk.CfnOutput(this, 'Output', { value: this.availabilityZones, }); } } class ParameterStack extends cdk.Stack { constructor(parent, id, props) { super(parent, id, props); new sns.Topic(this, 'TopicParameter', { topicName: new cdk.CfnParameter(this, 'TopicNameParam').valueAsString }); } } class OtherParameterStack extends cdk.Stack { constructor(parent, id, props) { super(parent, id, props); new sns.Topic(this, 'TopicParameter', { topicName: new cdk.CfnParameter(this, 'OtherTopicNameParam').valueAsString }); } } class MultiParameterStack extends cdk.Stack { constructor(parent, id, props) { super(parent, id, props); new sns.Topic(this, 'TopicParameter', { displayName: new cdk.CfnParameter(this, 'DisplayNameParam').valueAsString }); new sns.Topic(this, 'OtherTopicParameter', { displayName: new cdk.CfnParameter(this, 'OtherDisplayNameParam').valueAsString }); } } class OutputsStack extends cdk.Stack { constructor(parent, id, props) { super(parent, id, props); const topic = new sns.Topic(this, 'MyOutput', { topicName: `${cdk.Stack.of(this).stackName}MyTopic` }); new cdk.CfnOutput(this, 'TopicName', { value: topic.topicName }) } } class AnotherOutputsStack extends cdk.Stack { constructor(parent, id, props) { super(parent, id, props); const topic = new sns.Topic(this, 'MyOtherOutput', { topicName: `${cdk.Stack.of(this).stackName}MyOtherTopic` }); new cdk.CfnOutput(this, 'TopicName', { value: topic.topicName }); } } class IamStack extends cdk.Stack { constructor(parent, id, props) { super(parent, id, props); new iam.Role(this, 'SomeRole', { assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com') }); } } class ProvidingStack extends cdk.Stack { constructor(parent, id, props) { super(parent, id, props); this.topic = new sns.Topic(this, 'BogusTopic'); // Some filler } } class StackWithError extends cdk.Stack { constructor(parent, id, props) { super(parent, id, props); this.topic = new sns.Topic(this, 'BogusTopic'); // Some filler Annotations.of(this).addError('This is an error'); } } class StageWithError extends cdk.Stage { constructor(parent, id, props) { super(parent, id, props); new StackWithError(this, 'Stack'); } } class ConsumingStack extends cdk.Stack { constructor(parent, id, props) { super(parent, id, props); new sns.Topic(this, 'BogusTopic'); // Some filler new cdk.CfnOutput(this, 'IConsumedSomething', { value: props.providingStack.topic.topicArn }); } } class MissingSSMParameterStack extends cdk.Stack { constructor(parent, id, props) { super(parent, id, props); const parameterName = constructs.Node.of(this).tryGetContext('test:ssm-parameter-name'); if (parameterName) { const param = getSsmParameterValue(this, parameterName); new iam.Role(this, 'PhonyRole', { assumedBy: new iam.AccountPrincipal(param) }); } } } class LambdaStack extends cdk.Stack { constructor(parent, id, props) { super(parent, id, props); const fn = new lambda.Function(this, 'my-function', { code: lambda.Code.asset(path.join(__dirname, 'lambda')), runtime: lambda.Runtime.NODEJS_LATEST, handler: 'index.handler' }); new cdk.CfnOutput(this, 'FunctionArn', { value: fn.functionArn }); } } class DockerStack extends cdk.Stack { constructor(parent, id, props) { super(parent, id, props); new docker.DockerImageAsset(this, 'image', { directory: path.join(__dirname, 'docker') }); // Add at least a single resource (WaitConditionHandle), otherwise this stack will never // be deployed (and its assets never built) new cdk.CfnResource(this, 'Handle', { type: 'AWS::CloudFormation::WaitConditionHandle' }); } } class DockerStackWithCustomFile extends cdk.Stack { constructor(parent, id, props) { super(parent, id, props); new docker.DockerImageAsset(this, 'image', { directory: path.join(__dirname, 'docker'), file: 'Dockerfile.Custom' }); // Add at least a single resource (WaitConditionHandle), otherwise this stack will never // be deployed (and its assets never built) new cdk.CfnResource(this, 'Handle', { type: 'AWS::CloudFormation::WaitConditionHandle' }); } } class FailedStack extends cdk.Stack { constructor(parent, id, props) { super(parent, id, props); // fails on 'Property PolicyDocument cannot be empty'. new cdk.CfnResource(this, 'EmptyPolicy', { type: 'AWS::IAM::Policy' }) } } const VPC_TAG_NAME = 'custom-tag'; const VPC_TAG_VALUE = `${stackPrefix}-bazinga!`; class DefineVpcStack extends cdk.Stack { constructor(parent, id, props) { super(parent, id, props); const vpc = new ec2.Vpc(this, 'VPC', { maxAzs: 1, }) cdk.Aspects.of(vpc).add(new cdk.Tag(VPC_TAG_NAME, VPC_TAG_VALUE)); } } class ImportVpcStack extends cdk.Stack { constructor(parent, id, props) { super(parent, id, props); ec2.Vpc.fromLookup(this, 'DefaultVPC', { isDefault: true }); ec2.Vpc.fromLookup(this, 'ByTag', { tags: { [VPC_TAG_NAME]: VPC_TAG_VALUE } }); } } class ConditionalResourceStack extends cdk.Stack { constructor(parent, id, props) { super(parent, id, props); if (!process.env.NO_RESOURCE) { new iam.User(this, 'User'); } } } class SomeStage extends cdk.Stage { constructor(parent, id, props) { super(parent, id, props); new YourStack(this, 'StackInStage'); } } class StageUsingContext extends cdk.Stage { constructor(parent, id, props) { super(parent, id, props); new StackUsingContext(this, 'StackInStage'); } } const app = new cdk.App(); const defaultEnv = { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }; // Sometimes we don't want to synthesize all stacks because it will impact the results const stackSet = process.env.INTEG_STACK_SET || 'default'; switch (stackSet) { case 'default': // Deploy all does a wildcard ${stackPrefix}-test-* new MyStack(app, `${stackPrefix}-test-1`, { env: defaultEnv }); new YourStack(app, `${stackPrefix}-test-2`); // Deploy wildcard with parameters does ${stackPrefix}-param-test-* new ParameterStack(app, `${stackPrefix}-param-test-1`); new OtherParameterStack(app, `${stackPrefix}-param-test-2`); // Deploy stack with multiple parameters new MultiParameterStack(app, `${stackPrefix}-param-test-3`); // Deploy stack with outputs does ${stackPrefix}-outputs-test-* new OutputsStack(app, `${stackPrefix}-outputs-test-1`); new AnotherOutputsStack(app, `${stackPrefix}-outputs-test-2`); // Not included in wildcard new IamStack(app, `${stackPrefix}-iam-test`, { env: defaultEnv }); const providing = new ProvidingStack(app, `${stackPrefix}-order-providing`); new ConsumingStack(app, `${stackPrefix}-order-consuming`, { providingStack: providing }); new MissingSSMParameterStack(app, `${stackPrefix}-missing-ssm-parameter`, { env: defaultEnv }); new LambdaStack(app, `${stackPrefix}-lambda`); new DockerStack(app, `${stackPrefix}-docker`); new DockerStackWithCustomFile(app, `${stackPrefix}-docker-with-custom-file`); new FailedStack(app, `${stackPrefix}-failed`) if (process.env.ENABLE_VPC_TESTING) { // Gating so we don't do context fetching unless that's what we are here for const env = { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }; if (process.env.ENABLE_VPC_TESTING === 'DEFINE') new DefineVpcStack(app, `${stackPrefix}-define-vpc`, { env }); if (process.env.ENABLE_VPC_TESTING === 'IMPORT') new ImportVpcStack(app, `${stackPrefix}-import-vpc`, { env }); } new ConditionalResourceStack(app, `${stackPrefix}-conditional-resource`) new StackWithNestedStack(app, `${stackPrefix}-with-nested-stack`); new StackWithNestedStackUsingParameters(app, `${stackPrefix}-with-nested-stack-using-parameters`); new YourStack(app, `${stackPrefix}-termination-protection`, { terminationProtection: process.env.TERMINATION_PROTECTION !== 'FALSE' ? true : false, }); new SomeStage(app, `${stackPrefix}-stage`); break; case 'stage-using-context': // Cannot be combined with other test stacks, because we use this to test // that stage context is propagated up and causes synth to fail when combined // with '--no-lookups'. // Needs a dummy stack at the top level because the CLI will fail otherwise new YourStack(app, `${stackPrefix}-toplevel`, { env: defaultEnv }); new StageUsingContext(app, `${stackPrefix}-stage-using-context`, { env: defaultEnv, }); break; case 'stage-with-errors': const stage = new StageWithError(app, `${stackPrefix}-stage-with-errors`); stage.synth({ validateOnSynthesis: true }); break; default: throw new Error(`Unrecognized INTEG_STACK_SET: '${stackSet}'`); } app.synth();