From 3f75c442686cd1e0bb38ffbcf77d11e2f532687a Mon Sep 17 00:00:00 2001 From: Eli Polonsky Date: Wed, 3 Nov 2021 13:09:33 +0200 Subject: [PATCH] chore: regression suite patch for lambda runtime deprecation (#17297) Follow up on https://github.com/aws/aws-cdk/pull/17282 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../cli-regression-patches/v1.130.0/NOTES.md | 4 + .../v1.130.0/app/app.js | 378 ++++++++++++++++++ 2 files changed, 382 insertions(+) create mode 100644 packages/aws-cdk/test/integ/cli-regression-patches/v1.130.0/NOTES.md create mode 100644 packages/aws-cdk/test/integ/cli-regression-patches/v1.130.0/app/app.js diff --git a/packages/aws-cdk/test/integ/cli-regression-patches/v1.130.0/NOTES.md b/packages/aws-cdk/test/integ/cli-regression-patches/v1.130.0/NOTES.md new file mode 100644 index 0000000000000..9a9338f4d04fb --- /dev/null +++ b/packages/aws-cdk/test/integ/cli-regression-patches/v1.130.0/NOTES.md @@ -0,0 +1,4 @@ +On november 2nd 2021, lambda started deprecating the nodejs10.x runtime. This meant we can no longer create functions with this runtime. +Our integration tests use this runtime for one of its stacks. + +This patch brings https://github.com/aws/aws-cdk/pull/17282 into the regression suite. \ No newline at end of file diff --git a/packages/aws-cdk/test/integ/cli-regression-patches/v1.130.0/app/app.js b/packages/aws-cdk/test/integ/cli-regression-patches/v1.130.0/app/app.js new file mode 100644 index 0000000000000..f0e332eb351fa --- /dev/null +++ b/packages/aws-cdk/test/integ/cli-regression-patches/v1.130.0/app/app.js @@ -0,0 +1,378 @@ +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_12_X, + 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();