From f7cfcee89997775192c15836e40445143133a66c Mon Sep 17 00:00:00 2001 From: sullis Date: Mon, 12 Oct 2020 01:50:51 -0700 Subject: [PATCH 1/8] chore(init/java): use latest exec-maven-plugin (#9219) Changelog: https://github.com/mojohaus/exec-maven-plugin/releases/tag/exec-maven-plugin-3.0.0 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/aws-cdk/lib/init-templates/app/java/pom.template.xml | 2 +- .../aws-cdk/lib/init-templates/sample-app/java/pom.template.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/aws-cdk/lib/init-templates/app/java/pom.template.xml b/packages/aws-cdk/lib/init-templates/app/java/pom.template.xml index 18a08d803562f..dcce5ea68a5ef 100644 --- a/packages/aws-cdk/lib/init-templates/app/java/pom.template.xml +++ b/packages/aws-cdk/lib/init-templates/app/java/pom.template.xml @@ -27,7 +27,7 @@ org.codehaus.mojo exec-maven-plugin - 1.6.0 + 3.0.0 com.myorg.%name.PascalCased%App diff --git a/packages/aws-cdk/lib/init-templates/sample-app/java/pom.template.xml b/packages/aws-cdk/lib/init-templates/sample-app/java/pom.template.xml index 7159e270096a4..98af8eb52d2ff 100644 --- a/packages/aws-cdk/lib/init-templates/sample-app/java/pom.template.xml +++ b/packages/aws-cdk/lib/init-templates/sample-app/java/pom.template.xml @@ -23,7 +23,7 @@ org.codehaus.mojo exec-maven-plugin - 1.6.0 + 3.0.0 com.myorg.%name.PascalCased%App From 733d72dd9b5d5e2655fae6118290fc9d774f7525 Mon Sep 17 00:00:00 2001 From: John Crnjanin Date: Mon, 12 Oct 2020 19:27:31 +1000 Subject: [PATCH 2/8] chore(dynamodb): fix nonKeyAttributes limit test description (#8096) Relates to #8186, #8095 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts index 990e9bcd42094..01fa03d83d1bd 100644 --- a/packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts +++ b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts @@ -1112,7 +1112,7 @@ test('error when adding a global secondary index with projection type KEYS_ONLY, })).toThrow(/non-key attributes should not be specified when not using INCLUDE projection type/); }); -test('error when adding a global secondary index with projection type INCLUDE, but with more than 20 non-key attributes', () => { +test('error when adding a global secondary index with projection type INCLUDE, but with more than 100 non-key attributes', () => { const stack = new Stack(); const table = new Table(stack, CONSTRUCT_NAME, { partitionKey: TABLE_PARTITION_KEY, sortKey: TABLE_SORT_KEY }); const gsiNonKeyAttributeGenerator = NON_KEY_ATTRIBUTE_GENERATOR(GSI_NON_KEY); From 3327b7faafbed98b2a8da2caf769206f5c004d56 Mon Sep 17 00:00:00 2001 From: Nick Lynch Date: Mon, 12 Oct 2020 10:57:48 +0100 Subject: [PATCH 3/8] fix(cloudfront): compression disabled by default for Distribution (#10794) Flip the default setting for compression from 'false' to 'true', as the recommended setting. Also updated the README to make it easier to understand Behaviors more up-front. BREAKING CHANGE: `Distribution` behaviors now enable compression by default ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../test/integ.http-origin.expected.json | 1 + .../test/integ.load-balancer-origin.expected.json | 1 + .../test/integ.origin-group.expected.json | 2 ++ .../test/integ.s3-origin-oai.expected.json | 1 + .../test/integ.s3-origin.expected.json | 1 + packages/@aws-cdk/aws-cloudfront/README.md | 9 ++++++--- packages/@aws-cdk/aws-cloudfront/lib/distribution.ts | 2 +- .../aws-cloudfront/lib/private/cache-behavior.ts | 2 +- .../@aws-cdk/aws-cloudfront/test/distribution.test.ts | 9 +++++++++ .../test/integ.distribution-basic.expected.json | 1 + .../test/integ.distribution-extensive.expected.json | 3 ++- .../test/integ.distribution-lambda.expected.json | 3 ++- .../test/integ.distribution-policies.expected.json | 1 + .../aws-cloudfront/test/private/cache-behavior.test.ts | 1 + 14 files changed, 30 insertions(+), 7 deletions(-) diff --git a/packages/@aws-cdk/aws-cloudfront-origins/test/integ.http-origin.expected.json b/packages/@aws-cdk/aws-cloudfront-origins/test/integ.http-origin.expected.json index 1b53322bce185..bf1cb5354e5d3 100644 --- a/packages/@aws-cdk/aws-cloudfront-origins/test/integ.http-origin.expected.json +++ b/packages/@aws-cdk/aws-cloudfront-origins/test/integ.http-origin.expected.json @@ -6,6 +6,7 @@ "DistributionConfig": { "DefaultCacheBehavior": { "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", + "Compress": true, "TargetOriginId": "cloudfronthttporiginDistributionOrigin162B02709", "ViewerProtocolPolicy": "allow-all" }, diff --git a/packages/@aws-cdk/aws-cloudfront-origins/test/integ.load-balancer-origin.expected.json b/packages/@aws-cdk/aws-cloudfront-origins/test/integ.load-balancer-origin.expected.json index 0a4f25d7164f8..3e2d0d1a33572 100644 --- a/packages/@aws-cdk/aws-cloudfront-origins/test/integ.load-balancer-origin.expected.json +++ b/packages/@aws-cdk/aws-cloudfront-origins/test/integ.load-balancer-origin.expected.json @@ -412,6 +412,7 @@ "DistributionConfig": { "DefaultCacheBehavior": { "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", + "Compress": true, "TargetOriginId": "cloudfrontloadbalanceroriginDistributionOrigin1BCC75186", "ViewerProtocolPolicy": "allow-all" }, diff --git a/packages/@aws-cdk/aws-cloudfront-origins/test/integ.origin-group.expected.json b/packages/@aws-cdk/aws-cloudfront-origins/test/integ.origin-group.expected.json index c4dfa53520caa..c65231c502f19 100644 --- a/packages/@aws-cdk/aws-cloudfront-origins/test/integ.origin-group.expected.json +++ b/packages/@aws-cdk/aws-cloudfront-origins/test/integ.origin-group.expected.json @@ -71,6 +71,7 @@ "CacheBehaviors": [ { "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", + "Compress": true, "PathPattern": "/api", "TargetOriginId": "cloudfrontorigingroupDistributionOriginGroup10B57F1D1", "ViewerProtocolPolicy": "allow-all" @@ -78,6 +79,7 @@ ], "DefaultCacheBehavior": { "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", + "Compress": true, "TargetOriginId": "cloudfrontorigingroupDistributionOriginGroup10B57F1D1", "ViewerProtocolPolicy": "allow-all" }, diff --git a/packages/@aws-cdk/aws-cloudfront-origins/test/integ.s3-origin-oai.expected.json b/packages/@aws-cdk/aws-cloudfront-origins/test/integ.s3-origin-oai.expected.json index 7683122c7870a..3d17b0944873e 100644 --- a/packages/@aws-cdk/aws-cloudfront-origins/test/integ.s3-origin-oai.expected.json +++ b/packages/@aws-cdk/aws-cloudfront-origins/test/integ.s3-origin-oai.expected.json @@ -19,6 +19,7 @@ "DistributionConfig": { "DefaultCacheBehavior": { "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", + "Compress": true, "TargetOriginId": "cloudfronts3originoaiDistributionOrigin1516C5A91", "ViewerProtocolPolicy": "allow-all" }, diff --git a/packages/@aws-cdk/aws-cloudfront-origins/test/integ.s3-origin.expected.json b/packages/@aws-cdk/aws-cloudfront-origins/test/integ.s3-origin.expected.json index d7451363a0101..5eb310b5b92d7 100644 --- a/packages/@aws-cdk/aws-cloudfront-origins/test/integ.s3-origin.expected.json +++ b/packages/@aws-cdk/aws-cloudfront-origins/test/integ.s3-origin.expected.json @@ -70,6 +70,7 @@ "DistributionConfig": { "DefaultCacheBehavior": { "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", + "Compress": true, "TargetOriginId": "cloudfronts3originDistributionOrigin1741C4E95", "ViewerProtocolPolicy": "allow-all" }, diff --git a/packages/@aws-cdk/aws-cloudfront/README.md b/packages/@aws-cdk/aws-cloudfront/README.md index b973e44da5aa3..65c8c13010aab 100644 --- a/packages/@aws-cdk/aws-cloudfront/README.md +++ b/packages/@aws-cdk/aws-cloudfront/README.md @@ -35,9 +35,12 @@ for more complex use cases. ### Creating a distribution CloudFront distributions deliver your content from one or more origins; an origin is the location where you store the original version of your -content. Origins can be created from S3 buckets or a custom origin (HTTP server). Each distribution has a default behavior which applies to all -requests to that distribution, and routes requests to a primary origin. Constructs to define origins are in the `@aws-cdk/aws-cloudfront-origins` -module. +content. Origins can be created from S3 buckets or a custom origin (HTTP server). Constructs to define origins are in the `@aws-cdk/aws-cloudfront-origins` module. + +Each distribution has a default behavior which applies to all requests to that distribution, and routes requests to a primary origin. +Additional behaviors may be specified for an origin with a given URL path pattern. Behaviors allow routing with multiple origins, +controlling which HTTP methods to support, whether to require users to use HTTPS, and what query strings or cookies to forward to your origin, +among other settings. #### From an S3 Bucket diff --git a/packages/@aws-cdk/aws-cloudfront/lib/distribution.ts b/packages/@aws-cdk/aws-cloudfront/lib/distribution.ts index 3cbf713f5cf6d..5d1d7cb720b94 100644 --- a/packages/@aws-cdk/aws-cloudfront/lib/distribution.ts +++ b/packages/@aws-cdk/aws-cloudfront/lib/distribution.ts @@ -673,7 +673,7 @@ export interface AddBehaviorOptions { * See https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/ServingCompressedFiles.html#compressed-content-cloudfront-file-types * for file types CloudFront will compress. * - * @default false + * @default true */ readonly compress?: boolean; diff --git a/packages/@aws-cdk/aws-cloudfront/lib/private/cache-behavior.ts b/packages/@aws-cdk/aws-cloudfront/lib/private/cache-behavior.ts index a165d5e7219bd..4b700e166cecc 100644 --- a/packages/@aws-cdk/aws-cloudfront/lib/private/cache-behavior.ts +++ b/packages/@aws-cdk/aws-cloudfront/lib/private/cache-behavior.ts @@ -46,7 +46,7 @@ export class CacheBehavior { allowedMethods: this.props.allowedMethods?.methods, cachedMethods: this.props.cachedMethods?.methods, cachePolicyId: (this.props.cachePolicy ?? CachePolicy.CACHING_OPTIMIZED).cachePolicyId, - compress: this.props.compress, + compress: this.props.compress ?? true, originRequestPolicyId: this.props.originRequestPolicy?.originRequestPolicyId, smoothStreaming: this.props.smoothStreaming, viewerProtocolPolicy: this.props.viewerProtocolPolicy ?? ViewerProtocolPolicy.ALLOW_ALL, diff --git a/packages/@aws-cdk/aws-cloudfront/test/distribution.test.ts b/packages/@aws-cdk/aws-cloudfront/test/distribution.test.ts index 49a3cd4adb5b5..bbe6b34ebae3a 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/distribution.test.ts +++ b/packages/@aws-cdk/aws-cloudfront/test/distribution.test.ts @@ -25,6 +25,7 @@ test('minimal example renders correctly', () => { DistributionConfig: { DefaultCacheBehavior: { CachePolicyId: '658327ea-f89d-4fab-a63d-7e88639e58f6', + Compress: true, TargetOriginId: 'StackMyDistOrigin1D6D5E535', ViewerProtocolPolicy: 'allow-all', }, @@ -68,6 +69,7 @@ test('exhaustive example of props renders correctly', () => { Aliases: ['example.com'], DefaultCacheBehavior: { CachePolicyId: '658327ea-f89d-4fab-a63d-7e88639e58f6', + Compress: true, TargetOriginId: 'StackMyDistOrigin1D6D5E535', ViewerProtocolPolicy: 'allow-all', }, @@ -133,11 +135,13 @@ describe('multiple behaviors', () => { DistributionConfig: { DefaultCacheBehavior: { CachePolicyId: '658327ea-f89d-4fab-a63d-7e88639e58f6', + Compress: true, TargetOriginId: 'StackMyDistOrigin1D6D5E535', ViewerProtocolPolicy: 'allow-all', }, CacheBehaviors: [{ CachePolicyId: '658327ea-f89d-4fab-a63d-7e88639e58f6', + Compress: true, PathPattern: 'api/*', TargetOriginId: 'StackMyDistOrigin1D6D5E535', ViewerProtocolPolicy: 'allow-all', @@ -170,11 +174,13 @@ describe('multiple behaviors', () => { DistributionConfig: { DefaultCacheBehavior: { CachePolicyId: '658327ea-f89d-4fab-a63d-7e88639e58f6', + Compress: true, TargetOriginId: 'StackMyDistOrigin1D6D5E535', ViewerProtocolPolicy: 'allow-all', }, CacheBehaviors: [{ CachePolicyId: '658327ea-f89d-4fab-a63d-7e88639e58f6', + Compress: true, PathPattern: 'api/*', TargetOriginId: 'StackMyDistOrigin20B96F3AD', ViewerProtocolPolicy: 'allow-all', @@ -215,17 +221,20 @@ describe('multiple behaviors', () => { DistributionConfig: { DefaultCacheBehavior: { CachePolicyId: '658327ea-f89d-4fab-a63d-7e88639e58f6', + Compress: true, TargetOriginId: 'StackMyDistOrigin1D6D5E535', ViewerProtocolPolicy: 'allow-all', }, CacheBehaviors: [{ CachePolicyId: '658327ea-f89d-4fab-a63d-7e88639e58f6', + Compress: true, PathPattern: 'api/1*', TargetOriginId: 'StackMyDistOrigin20B96F3AD', ViewerProtocolPolicy: 'allow-all', }, { CachePolicyId: '658327ea-f89d-4fab-a63d-7e88639e58f6', + Compress: true, PathPattern: 'api/2*', TargetOriginId: 'StackMyDistOrigin1D6D5E535', ViewerProtocolPolicy: 'allow-all', diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.expected.json b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.expected.json index 9103815e60fd2..504433198d635 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.expected.json +++ b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-basic.expected.json @@ -6,6 +6,7 @@ "DistributionConfig": { "DefaultCacheBehavior": { "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", + "Compress": true, "TargetOriginId": "integdistributionbasicDistOrigin151B53FF1", "ViewerProtocolPolicy": "allow-all" }, diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-extensive.expected.json b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-extensive.expected.json index 5ad5427cdd5fd..9ec492a9c730a 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-extensive.expected.json +++ b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-extensive.expected.json @@ -12,6 +12,7 @@ "Comment": "a test", "DefaultCacheBehavior": { "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", + "Compress": true, "TargetOriginId": "integdistributionextensiveMyDistOrigin185F089B3", "ViewerProtocolPolicy": "allow-all" }, @@ -52,4 +53,4 @@ } } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-lambda.expected.json b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-lambda.expected.json index dc186fbacab3e..fde47299f760e 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-lambda.expected.json +++ b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-lambda.expected.json @@ -71,6 +71,7 @@ "DistributionConfig": { "DefaultCacheBehavior": { "CachePolicyId": "4135ea2d-6df8-44a3-9df3-4b5a84be39ad", + "Compress": true, "LambdaFunctionAssociations": [ { "EventType": "origin-request", @@ -98,4 +99,4 @@ } } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-policies.expected.json b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-policies.expected.json index e65e4fd8a0c36..a3fcd0084202a 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-policies.expected.json +++ b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-policies.expected.json @@ -51,6 +51,7 @@ "CachePolicyId": { "Ref": "CachePolicy26D8A535" }, + "Compress": true, "OriginRequestPolicyId": { "Ref": "OriginRequestPolicy3EFDB4FA" }, diff --git a/packages/@aws-cdk/aws-cloudfront/test/private/cache-behavior.test.ts b/packages/@aws-cdk/aws-cloudfront/test/private/cache-behavior.test.ts index 4b201addd7e37..a1e315fdcdd96 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/private/cache-behavior.test.ts +++ b/packages/@aws-cdk/aws-cloudfront/test/private/cache-behavior.test.ts @@ -22,6 +22,7 @@ test('renders the minimum template with an origin and path specified', () => { expect(behavior._renderBehavior()).toEqual({ targetOriginId: 'origin_id', cachePolicyId: '658327ea-f89d-4fab-a63d-7e88639e58f6', + compress: true, pathPattern: '*', viewerProtocolPolicy: 'allow-all', }); From 46df4a7c51843542bc79d2c1b3f211548ac39ab5 Mon Sep 17 00:00:00 2001 From: Niranjan Jayakar Date: Mon, 12 Oct 2020 11:54:12 +0100 Subject: [PATCH 4/8] feat(apigateway): autodetermine the private integration uri (#10730) When VPC Link is configured as a private integration on a Method, the 'uri' field is required. Without this the CloudFormation deployment fails with the error - 'Invalid integration URI specified'. Instead of switching this to just a synth time error, use the NLB DNS name when available. closes #10435 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-apigateway/README.md | 5 + .../aws-apigateway/lib/integration.ts | 33 ++++- .../@aws-cdk/aws-apigateway/lib/vpc-link.ts | 16 +- .../aws-apigateway/test/test.integration.ts | 140 ++++++++++++++++++ 4 files changed, 186 insertions(+), 8 deletions(-) diff --git a/packages/@aws-cdk/aws-apigateway/README.md b/packages/@aws-cdk/aws-apigateway/README.md index 9c40aa83ae322..5b55d55f4c38a 100644 --- a/packages/@aws-cdk/aws-apigateway/README.md +++ b/packages/@aws-cdk/aws-apigateway/README.md @@ -941,6 +941,11 @@ const integration = new apigw.Integration({ }); ``` +The uri for the private integration, in the case of a VpcLink, will be set to the DNS name of +the VPC Link's NLB. If the VPC Link has multiple NLBs or the VPC Link is imported or the DNS +name cannot be determined for any other reason, the user is expected to specify the `uri` +property. + Any existing `VpcLink` resource can be imported into the CDK app via the `VpcLink.fromVpcLinkId()`. ```ts diff --git a/packages/@aws-cdk/aws-apigateway/lib/integration.ts b/packages/@aws-cdk/aws-apigateway/lib/integration.ts index 5b6a3040cb946..9d08a02b57bc2 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/integration.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/integration.ts @@ -1,6 +1,7 @@ import * as iam from '@aws-cdk/aws-iam'; +import { Lazy } from '@aws-cdk/core'; import { Method } from './method'; -import { IVpcLink } from './vpc-link'; +import { IVpcLink, VpcLink } from './vpc-link'; export interface IntegrationOptions { /** @@ -90,7 +91,7 @@ export interface IntegrationOptions { /** * The type of network connection to the integration endpoint. - * @default ConnectionType.Internet + * @default - ConnectionType.VPC_LINK if `vpcLink` property is configured; ConnectionType.Internet otherwise. */ readonly connectionType?: ConnectionType; @@ -199,10 +200,34 @@ export class Integration { * being integrated, access the REST API object, method ARNs, etc. */ public bind(_method: Method): IntegrationConfig { + let uri = this.props.uri; + const options = this.props.options; + + if (options?.connectionType === ConnectionType.VPC_LINK && uri === undefined) { + uri = Lazy.stringValue({ + // needs to be a lazy since the targets can be added to the VpcLink construct after initialization. + produce: () => { + const vpcLink = options.vpcLink; + if (vpcLink instanceof VpcLink) { + const targets = vpcLink._targetDnsNames; + if (targets.length > 1) { + throw new Error("'uri' is required when there are more than one NLBs in the VPC Link"); + } else { + return `http://${targets[0]}`; + } + } else { + throw new Error("'uri' is required when the 'connectionType' is VPC_LINK"); + } + }, + }); + } return { - options: this.props.options, + options: { + ...options, + connectionType: options?.vpcLink ? ConnectionType.VPC_LINK : options?.connectionType, + }, type: this.props.type, - uri: this.props.uri, + uri, integrationHttpMethod: this.props.integrationHttpMethod, }; } diff --git a/packages/@aws-cdk/aws-apigateway/lib/vpc-link.ts b/packages/@aws-cdk/aws-apigateway/lib/vpc-link.ts index 85bdf63d0022c..700d22c137cc1 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/vpc-link.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/vpc-link.ts @@ -61,7 +61,7 @@ export class VpcLink extends Resource implements IVpcLink { */ public readonly vpcLinkId: string; - private readonly targets = new Array(); + private readonly _targets = new Array(); constructor(scope: Construct, id: string, props: VpcLinkProps = {}) { super(scope, id, { @@ -83,17 +83,25 @@ export class VpcLink extends Resource implements IVpcLink { } public addTargets(...targets: elbv2.INetworkLoadBalancer[]) { - this.targets.push(...targets); + this._targets.push(...targets); + } + + /** + * Return the list of DNS names from the target NLBs. + * @internal + * */ + public get _targetDnsNames(): string[] { + return this._targets.map(t => t.loadBalancerDnsName); } protected validate(): string[] { - if (this.targets.length === 0) { + if (this._targets.length === 0) { return ['No targets added to vpc link']; } return []; } private renderTargets() { - return this.targets.map(nlb => nlb.loadBalancerArn); + return this._targets.map(nlb => nlb.loadBalancerArn); } } diff --git a/packages/@aws-cdk/aws-apigateway/test/test.integration.ts b/packages/@aws-cdk/aws-apigateway/test/test.integration.ts index 40047b53df745..e946ded961b29 100644 --- a/packages/@aws-cdk/aws-apigateway/test/test.integration.ts +++ b/packages/@aws-cdk/aws-apigateway/test/test.integration.ts @@ -1,3 +1,4 @@ +import { ABSENT, expect, haveResourceLike } from '@aws-cdk/assert'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as elbv2 from '@aws-cdk/aws-elasticloadbalancingv2'; import * as iam from '@aws-cdk/aws-iam'; @@ -33,6 +34,89 @@ export = { test.done(); }, + 'uri is self determined from the NLB'(test: Test) { + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + const nlb = new elbv2.NetworkLoadBalancer(stack, 'NLB', { vpc }); + const link = new apigw.VpcLink(stack, 'link', { + targets: [nlb], + }); + const api = new apigw.RestApi(stack, 'restapi'); + const integration = new apigw.Integration({ + type: apigw.IntegrationType.HTTP_PROXY, + integrationHttpMethod: 'ANY', + options: { + connectionType: apigw.ConnectionType.VPC_LINK, + vpcLink: link, + }, + }); + api.root.addMethod('GET', integration); + + expect(stack).to(haveResourceLike('AWS::ApiGateway::Method', { + Integration: { + Uri: { + 'Fn::Join': [ + '', + [ + 'http://', + { + 'Fn::GetAtt': [ + 'NLB55158F82', + 'DNSName', + ], + }, + ], + ], + }, + }, + })); + + test.done(); + }, + + 'uri must be set for VpcLink with multiple NLBs'(test: Test) { + const app = new cdk.App(); + const stack = new cdk.Stack(app); + const vpc = new ec2.Vpc(stack, 'VPC'); + const nlb1 = new elbv2.NetworkLoadBalancer(stack, 'NLB1', { vpc }); + const nlb2 = new elbv2.NetworkLoadBalancer(stack, 'NLB2', { vpc }); + const link = new apigw.VpcLink(stack, 'link', { + targets: [nlb1, nlb2], + }); + const api = new apigw.RestApi(stack, 'restapi'); + const integration = new apigw.Integration({ + type: apigw.IntegrationType.HTTP_PROXY, + integrationHttpMethod: 'ANY', + options: { + connectionType: apigw.ConnectionType.VPC_LINK, + vpcLink: link, + }, + }); + api.root.addMethod('GET', integration); + test.throws(() => app.synth(), /'uri' is required when there are more than one NLBs in the VPC Link/); + + test.done(); + }, + + 'uri must be set when using an imported VpcLink'(test: Test) { + const app = new cdk.App(); + const stack = new cdk.Stack(app); + const link = apigw.VpcLink.fromVpcLinkId(stack, 'link', 'vpclinkid'); + const api = new apigw.RestApi(stack, 'restapi'); + const integration = new apigw.Integration({ + type: apigw.IntegrationType.HTTP_PROXY, + integrationHttpMethod: 'ANY', + options: { + connectionType: apigw.ConnectionType.VPC_LINK, + vpcLink: link, + }, + }); + api.root.addMethod('GET', integration); + test.throws(() => app.synth(), /'uri' is required when the 'connectionType' is VPC_LINK/); + + test.done(); + }, + 'connectionType of INTERNET and vpcLink are mutually exclusive'(test: Test) { // GIVEN const stack = new cdk.Stack(); @@ -55,4 +139,60 @@ export = { }), /cannot set 'vpcLink' where 'connectionType' is INTERNET/); test.done(); }, + + 'connectionType is ABSENT when vpcLink is not specified'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const api = new apigw.RestApi(stack, 'restapi'); + + // WHEN + const integration = new apigw.Integration({ + type: apigw.IntegrationType.HTTP_PROXY, + integrationHttpMethod: 'ANY', + }); + api.root.addMethod('ANY', integration); + + // THEN + expect(stack).to(haveResourceLike('AWS::ApiGateway::Method', { + HttpMethod: 'ANY', + Integration: { + ConnectionType: ABSENT, + }, + })); + + test.done(); + }, + + 'connectionType defaults to VPC_LINK if vpcLink is configured'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + const nlb = new elbv2.NetworkLoadBalancer(stack, 'NLB', { + vpc, + }); + const link = new apigw.VpcLink(stack, 'link', { + targets: [nlb], + }); + const api = new apigw.RestApi(stack, 'restapi'); + + // WHEN + const integration = new apigw.Integration({ + type: apigw.IntegrationType.HTTP_PROXY, + integrationHttpMethod: 'ANY', + options: { + vpcLink: link, + }, + }); + api.root.addMethod('ANY', integration); + + // THEN + expect(stack).to(haveResourceLike('AWS::ApiGateway::Method', { + HttpMethod: 'ANY', + Integration: { + ConnectionType: 'VPC_LINK', + }, + })); + + test.done(); + }, }; \ No newline at end of file From 99111f72adc210f48e269db50f2b8e8b78d21252 Mon Sep 17 00:00:00 2001 From: Nick Lynch Date: Mon, 12 Oct 2020 12:51:44 +0100 Subject: [PATCH 5/8] fix(lambda): grantInvoke on imported function fails (#10622) PR #8828 added the ability to add permissions on imported functions, if the account on the stack and imported function matched. However, when this doesn't match, attempting to call `grantInvoke` on the imported function results in a `findChild` error. This change guards against calling `addPermission` when `canCreatePermissions` is false by checking if the `CfnPermission` was actually created, and adjusting the return from `addInvoke` appropriately. fixes #10607 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-lambda/lib/function-base.ts | 12 +- .../@aws-cdk/aws-lambda/test/test.lambda.ts | 267 +++++++++++------- .../test/lambda/lambda.test.ts | 16 ++ 3 files changed, 184 insertions(+), 111 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/function-base.ts b/packages/@aws-cdk/aws-lambda/lib/function-base.ts index 7e6c309a3853e..ab1012b6015bc 100644 --- a/packages/@aws-cdk/aws-lambda/lib/function-base.ts +++ b/packages/@aws-cdk/aws-lambda/lib/function-base.ts @@ -210,7 +210,7 @@ export abstract class FunctionBase extends Resource implements IFunction { */ public addPermission(id: string, permission: Permission) { if (!this.canCreatePermissions) { - // FIXME: Report metadata + // FIXME: @deprecated(v2) - throw an error if calling `addPermission` on a resource that doesn't support it. return; } @@ -299,7 +299,11 @@ export abstract class FunctionBase extends Resource implements IFunction { action: 'lambda:InvokeFunction', }); - return { statementAdded: true, policyDependable: this._functionNode().findChild(identifier) } as iam.AddToResourcePolicyResult; + const permissionNode = this._functionNode().tryFindChild(identifier); + if (!permissionNode) { + throw new Error('Cannot modify permission to lambda function. Function is either imported or $LATEST version.'); + } + return { statementAdded: true, policyDependable: permissionNode }; }, node: this.node, stack: this.stack, @@ -357,13 +361,13 @@ export abstract class FunctionBase extends Resource implements IFunction { * ..which means that in order to extract the `account-id` component from the ARN, we can * split the ARN using ":" and select the component in index 4. * - * @returns true if account id of function matches this account + * @returns true if account id of function matches this account, or the accounts are unresolved. * * @internal */ protected _isStackAccount(): boolean { if (Token.isUnresolved(this.stack.account) || Token.isUnresolved(this.functionArn)) { - return false; + return true; } return this.stack.parseArn(this.functionArn).account === this.stack.account; } diff --git a/packages/@aws-cdk/aws-lambda/test/test.lambda.ts b/packages/@aws-cdk/aws-lambda/test/test.lambda.ts index 3678ba37069e0..cad80e9d38931 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.lambda.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.lambda.ts @@ -276,68 +276,71 @@ export = { // WHEN const imported = lambda.Function.fromFunctionArn(stack2, 'Imported', 'arn:aws:lambda:us-east-1:123456789012:function:ProcessKinesisRecords'); - // Can call addPermission() but it won't do anything - imported.addPermission('Hello', { - principal: new iam.ServicePrincipal('harry'), - }); - // THEN test.deepEqual(imported.functionArn, 'arn:aws:lambda:us-east-1:123456789012:function:ProcessKinesisRecords'); test.deepEqual(imported.functionName, 'ProcessKinesisRecords'); - expect(stack2).notTo(haveResource('AWS::Lambda::Permission')); + test.done(); }, - 'imported Function w/ resolved account and function arn can addPermissions'(test: Test) { - // GIVEN - const app = new cdk.App(); - const stack = new cdk.Stack(app, 'Imports', { - env: { account: '123456789012', region: 'us-east-1' }, - }); - const stack2 = new cdk.Stack(app, 'imported', { - env: { account: '123456789012', region: 'us-east-1' }, - }); - new lambda.Function(stack, 'BaseFunction', { - code: new lambda.InlineCode('foo'), - handler: 'index.handler', - runtime: lambda.Runtime.NODEJS_10_X, - }); + 'addPermissions()': { + 'imported Function w/ resolved account and function arn'(test: Test) { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'Imports', { + env: { account: '123456789012', region: 'us-east-1' }, + }); - // WHEN - const iFunc = lambda.Function.fromFunctionAttributes(stack2, 'iFunc', { - functionArn: 'arn:aws:lambda:us-east-1:123456789012:function:BaseFunction', - }); - iFunc.addPermission('iFunc', { - principal: new iam.ServicePrincipal('cloudformation.amazonaws.com'), - }); + // WHEN + const iFunc = lambda.Function.fromFunctionAttributes(stack, 'iFunc', { + functionArn: 'arn:aws:lambda:us-east-1:123456789012:function:BaseFunction', + }); + iFunc.addPermission('iFunc', { + principal: new iam.ServicePrincipal('cloudformation.amazonaws.com'), + }); - // THEN - expect(stack2).to(haveResource('AWS::Lambda::Permission')); - test.done(); - }, + // THEN + expect(stack).to(haveResource('AWS::Lambda::Permission')); + test.done(); + }, - 'imported Function w/o account cannot addPermissions'(test: Test) { - // GIVEN - const app = new cdk.App(); - const stack = new cdk.Stack(app, 'Base'); - const importedStack = new cdk.Stack(app, 'Imported'); - new lambda.Function(stack, 'BaseFunction', { - code: new lambda.InlineCode('foo'), - handler: 'index.handler', - runtime: lambda.Runtime.NODEJS_10_X, - }); + 'imported Function w/ unresolved account'(test: Test) { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'Imports'); - // WHEN - const iFunc = lambda.Function.fromFunctionAttributes(importedStack, 'iFunc', { - functionArn: 'arn:aws:lambda:us-east-1:123456789012:function:BaseFunction', - }); - iFunc.addPermission('iFunc', { - principal: new iam.ServicePrincipal('cloudformation.amazonaws.com'), - }); + // WHEN + const iFunc = lambda.Function.fromFunctionAttributes(stack, 'iFunc', { + functionArn: 'arn:aws:lambda:us-east-1:123456789012:function:BaseFunction', + }); + iFunc.addPermission('iFunc', { + principal: new iam.ServicePrincipal('cloudformation.amazonaws.com'), + }); - // THEN - expect(importedStack).notTo(haveResource('AWS::Lambda::Permission')); - test.done(); + // THEN + expect(stack).to(haveResource('AWS::Lambda::Permission')); + test.done(); + }, + + 'imported Function w/different account'(test: Test) { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'Base', { + env: { account: '111111111111' }, + }); + + // WHEN + const iFunc = lambda.Function.fromFunctionAttributes(stack, 'iFunc', { + functionArn: 'arn:aws:lambda:us-east-1:123456789012:function:BaseFunction', + }); + iFunc.addPermission('iFunc', { + principal: new iam.ServicePrincipal('cloudformation.amazonaws.com'), + }); + + // THEN + expect(stack).notTo(haveResource('AWS::Lambda::Permission')); + test.done(); + }, }, 'Lambda code can be read from a local directory via an asset'(test: Test) { @@ -1220,66 +1223,118 @@ export = { test.done(); }, - }, - 'grantInvoke with an imported role (in the same account)'(test: Test) { - // GIVEN - const stack = new cdk.Stack(undefined, undefined, { - env: { account: '123456789012' }, - }); - const fn = new lambda.Function(stack, 'Function', { - code: lambda.Code.fromInline('xxx'), - handler: 'index.handler', - runtime: lambda.Runtime.NODEJS_10_X, - }); + 'with an imported role (in the same account)'(test: Test) { + // GIVEN + const stack = new cdk.Stack(undefined, undefined, { + env: { account: '123456789012' }, + }); + const fn = new lambda.Function(stack, 'Function', { + code: lambda.Code.fromInline('xxx'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_10_X, + }); - // WHEN - fn.grantInvoke(iam.Role.fromRoleArn(stack, 'ForeignRole', 'arn:aws:iam::123456789012:role/someRole')); + // WHEN + fn.grantInvoke(iam.Role.fromRoleArn(stack, 'ForeignRole', 'arn:aws:iam::123456789012:role/someRole')); - // THEN - expect(stack).to(haveResource('AWS::IAM::Policy', { - PolicyDocument: objectLike({ - Statement: arrayWith( - { - Action: 'lambda:InvokeFunction', - Effect: 'Allow', - Resource: { 'Fn::GetAtt': ['Function76856677', 'Arn'] }, - }, - ), - }), - Roles: ['someRole'], - })); + // THEN + expect(stack).to(haveResource('AWS::IAM::Policy', { + PolicyDocument: objectLike({ + Statement: arrayWith( + { + Action: 'lambda:InvokeFunction', + Effect: 'Allow', + Resource: { 'Fn::GetAtt': ['Function76856677', 'Arn'] }, + }, + ), + }), + Roles: ['someRole'], + })); - test.done(); - }, + test.done(); + }, - 'grantInvoke with an imported role (from a different account)'(test: Test) { - // GIVEN - const stack = new cdk.Stack(undefined, undefined, { - env: { account: '3333' }, - }); - const fn = new lambda.Function(stack, 'Function', { - code: lambda.Code.fromInline('xxx'), - handler: 'index.handler', - runtime: lambda.Runtime.NODEJS_10_X, - }); + 'with an imported role (from a different account)'(test: Test) { + // GIVEN + const stack = new cdk.Stack(undefined, undefined, { + env: { account: '3333' }, + }); + const fn = new lambda.Function(stack, 'Function', { + code: lambda.Code.fromInline('xxx'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_10_X, + }); - // WHEN - fn.grantInvoke(iam.Role.fromRoleArn(stack, 'ForeignRole', 'arn:aws:iam::123456789012:role/someRole')); + // WHEN + fn.grantInvoke(iam.Role.fromRoleArn(stack, 'ForeignRole', 'arn:aws:iam::123456789012:role/someRole')); - // THEN - expect(stack).to(haveResource('AWS::Lambda::Permission', { - Action: 'lambda:InvokeFunction', - FunctionName: { - 'Fn::GetAtt': [ - 'Function76856677', - 'Arn', - ], - }, - Principal: 'arn:aws:iam::123456789012:role/someRole', - })); + // THEN + expect(stack).to(haveResource('AWS::Lambda::Permission', { + Action: 'lambda:InvokeFunction', + FunctionName: { + 'Fn::GetAtt': [ + 'Function76856677', + 'Arn', + ], + }, + Principal: 'arn:aws:iam::123456789012:role/someRole', + })); - test.done(); + test.done(); + }, + + 'on an imported function (same account)'(test: Test) { + // GIVEN + const stack = new cdk.Stack(undefined, undefined, { + env: { account: '123456789012' }, + }); + const fn = lambda.Function.fromFunctionArn(stack, 'Function', 'arn:aws:lambda:us-east-1:123456789012:function:MyFn'); + + // WHEN + fn.grantInvoke(new iam.ServicePrincipal('elasticloadbalancing.amazonaws.com')); + + // THEN + expect(stack).to(haveResource('AWS::Lambda::Permission', { + Action: 'lambda:InvokeFunction', + FunctionName: 'arn:aws:lambda:us-east-1:123456789012:function:MyFn', + Principal: 'elasticloadbalancing.amazonaws.com', + })); + + test.done(); + }, + + 'on an imported function (unresolved account)'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const fn = lambda.Function.fromFunctionArn(stack, 'Function', 'arn:aws:lambda:us-east-1:123456789012:function:MyFn'); + + // WHEN + fn.grantInvoke(new iam.ServicePrincipal('elasticloadbalancing.amazonaws.com')); + + // THEN + expect(stack).to(haveResource('AWS::Lambda::Permission', { + Action: 'lambda:InvokeFunction', + FunctionName: 'arn:aws:lambda:us-east-1:123456789012:function:MyFn', + Principal: 'elasticloadbalancing.amazonaws.com', + })); + + test.done(); + }, + + 'on an imported function (different account)'(test: Test) { + // GIVEN + const stack = new cdk.Stack(undefined, undefined, { + env: { account: '111111111111' }, // Different account + }); + const fn = lambda.Function.fromFunctionArn(stack, 'Function', 'arn:aws:lambda:us-east-1:123456789012:function:MyFn'); + + // THEN + test.throws(() => { fn.grantInvoke(new iam.ServicePrincipal('elasticloadbalancing.amazonaws.com')); }, + /Cannot modify permission to lambda function/); + + test.done(); + }, }, 'Can use metricErrors on a lambda Function'(test: Test) { @@ -1342,8 +1397,7 @@ export = { runtime: lambda.Runtime.NODEJS_10_X, code: lambda.Code.fromInline('exports.main = function() { console.log("DONE"); }'), handler: 'index.main', - }), - /nodejs10.x is not in \[nodejs12.x\]/); + }), /nodejs10.x is not in \[nodejs12.x\]/); test.done(); }, @@ -1362,8 +1416,7 @@ export = { runtime: lambda.Runtime.NODEJS_10_X, code: lambda.Code.fromInline('exports.main = function() { console.log("DONE"); }'), handler: 'index.main', - }), - /Unable to add layer:/); + }), /Unable to add layer:/); test.done(); }, diff --git a/packages/@aws-cdk/aws-s3-notifications/test/lambda/lambda.test.ts b/packages/@aws-cdk/aws-s3-notifications/test/lambda/lambda.test.ts index 10de49b776d02..6bd225d17fd0c 100644 --- a/packages/@aws-cdk/aws-s3-notifications/test/lambda/lambda.test.ts +++ b/packages/@aws-cdk/aws-s3-notifications/test/lambda/lambda.test.ts @@ -102,6 +102,22 @@ test('lambda in a different stack as notification target', () => { }); }); +test('imported lambda in a different account as notification target', () => { + const app = new App(); + const stack = new Stack(app, 'stack', { + env: { account: '111111111111' }, + }); + + // Lambda account and stack account differ; no permissions should be created. + const lambdaFunction = lambda.Function.fromFunctionArn(stack, 'lambdaFunction', 'arn:aws:lambda:us-east-1:123456789012:function:BaseFunction'); + const bucket = new s3.Bucket(stack, 'bucket'); + + bucket.addObjectCreatedNotification(new s3n.LambdaDestination(lambdaFunction)); + + // no permissions created + expect(stack).not.toHaveResourceLike('AWS::Lambda::Permission'); +}); + test('lambda as notification target', () => { // GIVEN const stack = new Stack(); From c7e52ee850868f5b068ee8d2fda006668f83f899 Mon Sep 17 00:00:00 2001 From: Eli Polonsky Date: Mon, 12 Oct 2020 16:56:27 +0300 Subject: [PATCH 6/8] chore(cli): Use previous version for fetching regression tests (#10675) This PR rectifies a long standing logical breakdown in our regression tests. In order to run regression tests, we first need to fetch the tests themselves from the previous release, and run it against CLI code that is being built. Up until now, those tests were fetched from the **latest published** version, under the assumption that this version is always lower (or equal) than the version that is currently being built. Normally, this assumption is correct. For example, let's say the latest published version is 1.65.0. When the pipeline runs, the version in `lerna.json` is 1.65.0, which is indeed equal to the latest published version. However, if the master pipeline is executed before the merge back PR is merged to master, the `lerna.json` version in that case will have `1.64.0`. This means that the latest published version number is actually bigger. This effectively meant that the CLI we are testing has a lower version than the framework, which was not the intent. This in turn caused "fake" failures when we introduced changes to assets schemas that validated version mismatches. Another change this PR introduces is a "cognitive" simplification in how we use verdaccio to serve locally built tarballs. Since our `lerna.json` in the repo has a version that is also the version of a publicly available module as well, we have to be very careful in how we launch verdaccio with the NPM uplink because of potential conflicts and overrides. Instead of having to worry about that, we now perform a fake bump in our pipeline with a pre-release tag, making it so that local versions are never the same as public packages, avoiding the uplink problems. More details here: https://github.com/aws/aws-cdk/pull/8150#issuecomment-634903897 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .gitignore | 3 + buildspec.yaml | 3 + bump-candidate.sh | 23 ++ packages/aws-cdk/CONTRIBUTING.md | 51 ++- packages/aws-cdk/package.json | 8 +- .../cli-regression-patches/v1.67.0/NOTES.md | 2 + .../v1.67.0/cdk-helpers.js | 331 ++++++++++++++++++ .../aws-cdk/test/integ/cli/cdk-helpers.ts | 12 +- packages/aws-cdk/test/integ/cli/test.sh | 6 - packages/aws-cdk/test/integ/github-helpers.ts | 30 ++ packages/aws-cdk/test/integ/run-against-dist | 3 + .../aws-cdk/test/integ/run-against-dist.bash | 71 +--- .../aws-cdk/test/integ/run-against-release | 1 + packages/aws-cdk/test/integ/run-against-repo | 1 + .../aws-cdk/test/integ/run-wrappers/repo/npm | 8 +- ...est-cli-regression-against-current-code.sh | 88 +---- ...t-cli-regression-against-latest-release.sh | 11 +- .../test/integ/test-cli-regression.bash | 82 +++++ yarn.lock | 62 +++- 19 files changed, 627 insertions(+), 169 deletions(-) create mode 100755 bump-candidate.sh create mode 100644 packages/aws-cdk/test/integ/cli-regression-patches/v1.67.0/NOTES.md create mode 100644 packages/aws-cdk/test/integ/cli-regression-patches/v1.67.0/cdk-helpers.js create mode 100644 packages/aws-cdk/test/integ/github-helpers.ts create mode 100644 packages/aws-cdk/test/integ/test-cli-regression.bash diff --git a/.gitignore b/.gitignore index 055b85911775d..28ed33d0064b2 100644 --- a/.gitignore +++ b/.gitignore @@ -40,5 +40,8 @@ yarn-error.log # Parcel default cache directory .parcel-cache +# VSCode history plugin +.vscode/.history/ + # Cloud9 .c9 diff --git a/buildspec.yaml b/buildspec.yaml index 8196752082e93..b3dac131ac590 100644 --- a/buildspec.yaml +++ b/buildspec.yaml @@ -18,6 +18,9 @@ phases: - /bin/bash ./fetch-dotnet-snk.sh build: commands: + # we bump here so that our master version won't be identical to the latest published version during tests. + # otherwise this causes problems with verdaccio mirroring. + - '[ ${GIT_BRANCH} = ${REGRESSION_TESTS_BRANCH} ] && /bin/bash ./bump-candidate.sh' - /bin/bash ./scripts/align-version.sh - /bin/bash ./build.sh post_build: diff --git a/bump-candidate.sh b/bump-candidate.sh new file mode 100755 index 0000000000000..5958a9a151230 --- /dev/null +++ b/bump-candidate.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# -------------------------------------------------------------------------------------------------- +# +# This script is intended to be used in our master pipeline as a way of incrementing the version number +# so that it doesnt colide with any published version. This is needed because our integration tests launch +# a verdaccio instance that serves local tarballs, and if those tarballs have the same version as +# already published modules, it messes things up. +# +# It does so by using a pre-release rc tag, making it so that locally packed versions will always be +# suffixed with '-rc', distinguishing it from publisehd modules. +# +# If you need to run integration tests locally against the distribution tarballs, you should run this +# script locally as well before building and packing the repository. +# +# This script only increments the version number in the version files, it does not generate a changelog. +# +# -------------------------------------------------------------------------------------------------- +set -euo pipefail +version=${1:-minor} + +echo "Starting candidate ${version} version bump" + +npx standard-version --release-as ${version} --prerelease=rc --skip.commit --skip.changelog \ No newline at end of file diff --git a/packages/aws-cdk/CONTRIBUTING.md b/packages/aws-cdk/CONTRIBUTING.md index b5416406b7136..cbf6933dff920 100644 --- a/packages/aws-cdk/CONTRIBUTING.md +++ b/packages/aws-cdk/CONTRIBUTING.md @@ -88,7 +88,7 @@ yarn integ-cli-no-regression #### Regression -Validate that previously tested functionality still works in light of recent changes to the CLI. This is done by fetching the functional tests of the latest published release, and running them against the new CLI code. +Validate that previously tested functionality still works in light of recent changes to the CLI. This is done by fetching the functional tests of the previous published release, and running them against the new CLI code. These tests run in two variations: @@ -97,17 +97,62 @@ These tests run in two variations: Use your local framework code. This is important to make sure the new CLI version will work properly with the new framework version. -- **against latest release code** + > See a concrete failure [example](https://github.com/aws/aws-cdk-rfcs/blob/master/text/00110-cli-framework-compatibility-strategy.md#remove---target-from-docker-build-command) - Fetches the framework code from the latest release. This is important to make sure +- **against previously release code** + + Fetches the framework code from the previous release. This is important to make sure the new CLI version does not rely on new framework features to provide the same functionality. + > See a concrete failure [example](https://github.com/aws/aws-cdk-rfcs/blob/master/text/00110-cli-framework-compatibility-strategy.md#change-artifact-metadata-type-value) + You can also run just these tests by executing: ```console yarn integ-cli-regression ``` +Note that these tests can only be executed using the `run-against-dist` wrapper. Why? well, it doesn't really make sense to `run-against-repo` when testing the **previously released code**, since we obviously cannot use the repo. Granted, running **against local framework code** can somehow work, but it required a few too many hacks in the current codebase to make it seem worthwhile. + +##### Implementation + +The implemention of the regression suites is not trivial to reason about and follow. Even though the code includes inline comments, we break down the exact details to better serve us in maintaining it and regaining context. + +Before diving into it, we establish a few key concepts: + +- `CANDIDATE_VERSION` - This is the version of the code that is being built in the pipeline, and its value is stored in the `build.json` file of the packaged artifact of the repo. +- `PREVIOUS_VERSION` - This is the version previous to the `CANDIDATE_VERSION`. +- `CLI_VERSION` - This is the version of the CLI we are testing. It is **always** the same as the `CANDIDATE_VERSION` since we want to test the latest CLI code. +- `FRAMEWORK_VERSION` - This is the version of the framework we are testing. It varries between the two variation of the regression suites. +Its value can either be that of `CANDIDATE_VERSION` (for testing against the latest framework code), or `PREVIOUS_VERSION` (for testing against the previously published version of the framework code). + +Following are the steps invovled in running these tests: + +1. Run [`./bump-candidate.sh`](../../bump-candidate.sh) to differentiate between the local version and the published version. For example, if the version in `lerna.json` is `1.67.0`, this script will result in a version `1.67.0-rc.0`. This is needed so that we can launch a verdaccio instance serving local tarballs without worrying about conflicts with the public npm uplink. This will help us avoid version quirks that might happen during the *post-release-pre-merge-back* time window. + +2. Run [`./align-version.sh`](../../scripts/align-version.sh) to configure the above version in all our packages. + +3. Build and Pack the repository. The produced tarballs will be versioned with the above version. + +4. Run `test/integ/run-against-dist test/integ/test-cli-regression-against-latest-release.sh` (or `test/integ/test-cli-regression-against-latest-code.sh`) + +5. First, the `run-against-dist` wrapper will run and: + + - Read the `CANDIDATE_VERSION` from `build.json` and export it. + - [Launch verdaccio](./test/integ/run-against-dist#L29) to serve all local tarballs (serves the `CANDIDATE_VERSION` now) + - [Install the CLI](./test/integ/run-against-dist#L30) using the `CANDIDATE_VERSION` version `CANDIDATE_VERSION` env variable. + - Execute the given script. + +6. Both cli regression test scripts run the same [`run_regression_against_framework_version`](./test/integ/test-cli-regression.bash#L22) function. This function accepts which framework version should the regression run against, it can be either `CANDIDATE_VERSION` or `PREVIOUS_VERSION`. Note that the argument is not the actual value of the versio, but instead is just an [indirection indentifier](./test/integ/test-cli-regression.bash#L81). The function will: + + - Calculate the actual value of the previous version based on the candidate version. (fetches from github) + - Download the previous version tarball from npm and extract the integration tests. + - Export a `FRAMWORK_VERSION` env variable based on the caller, and execute the integration tests of the previous version. + +7. Our integration tests now run and have knowledge of which framework version they should [install](./test/integ/cli/cdk-helpers.ts#L74). + +That "basically" it, hope it makes sense... + ### Init template integration tests Init template tests will initialize and compile the init templates that the diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index d72fac19cd2ab..030b3d2325504 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -19,8 +19,8 @@ "build+test": "npm run build && npm test", "integ-cli": "npm run integ-cli-regression && npm run integ-cli-no-regression", "integ-cli-regression": "npm run integ-cli-regression-latest-release && npm run integ-cli-regression-latest-code", - "integ-cli-regression-latest-release": "test/integ/run-against-repo test/integ/test-cli-regression-against-latest-release.sh", - "integ-cli-regression-latest-code": "test/integ/run-against-repo test/integ/test-cli-regression-against-current-code.sh", + "integ-cli-regression-latest-release": "test/integ/run-against-dist test/integ/test-cli-regression-against-latest-release.sh", + "integ-cli-regression-latest-code": "test/integ/run-against-dist test/integ/test-cli-regression-against-current-code.sh", "integ-cli-no-regression": "test/integ/run-against-repo test/integ/cli/test.sh", "integ-init": "test/integ/run-against-dist test/integ/init/test-all.sh" }, @@ -62,7 +62,9 @@ "pkglint": "0.0.0", "sinon": "^9.1.0", "ts-jest": "^26.4.1", - "ts-mock-imports": "^1.3.0" + "ts-mock-imports": "^1.3.0", + "@octokit/rest": "^18.0.6", + "make-runnable": "^1.3.8" }, "dependencies": { "@aws-cdk/cloud-assembly-schema": "0.0.0", diff --git a/packages/aws-cdk/test/integ/cli-regression-patches/v1.67.0/NOTES.md b/packages/aws-cdk/test/integ/cli-regression-patches/v1.67.0/NOTES.md new file mode 100644 index 0000000000000..19bd85e22f5f2 --- /dev/null +++ b/packages/aws-cdk/test/integ/cli-regression-patches/v1.67.0/NOTES.md @@ -0,0 +1,2 @@ +Integration tests are now injected with environment variable that specifies which framework +version they should install and use. This patch includes the cdk-helpers.js file that is responsible for that. \ No newline at end of file diff --git a/packages/aws-cdk/test/integ/cli-regression-patches/v1.67.0/cdk-helpers.js b/packages/aws-cdk/test/integ/cli-regression-patches/v1.67.0/cdk-helpers.js new file mode 100644 index 0000000000000..7bb7790818e40 --- /dev/null +++ b/packages/aws-cdk/test/integ/cli-regression-patches/v1.67.0/cdk-helpers.js @@ -0,0 +1,331 @@ +"use strict"; +var _a, _b; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.randomString = exports.rimraf = exports.shell = exports.TestFixture = exports.cloneDirectory = exports.withDefaultFixture = exports.withCdkApp = exports.withAws = void 0; +const child_process = require("child_process"); +const fs = require("fs"); +const os = require("os"); +const path = require("path"); +const aws_helpers_1 = require("./aws-helpers"); +const resource_pool_1 = require("./resource-pool"); +const REGIONS = process.env.AWS_REGIONS + ? process.env.AWS_REGIONS.split(',') + : [(_b = (_a = process.env.AWS_REGION) !== null && _a !== void 0 ? _a : process.env.AWS_DEFAULT_REGION) !== null && _b !== void 0 ? _b : 'us-east-1']; +const FRAMEWORK_VERSION = process.env.FRAMEWORK_VERSION; +process.stdout.write(`Using regions: ${REGIONS}\n`); +process.stdout.write(`Using framework version: ${FRAMEWORK_VERSION}\n`); +const REGION_POOL = new resource_pool_1.ResourcePool(REGIONS); +/** + * Higher order function to execute a block with an AWS client setup + * + * Allocate the next region from the REGION pool and dispose it afterwards. + */ +function withAws(block) { + return (context) => REGION_POOL.using(async (region) => { + const aws = await aws_helpers_1.AwsClients.forRegion(region, context.output); + await sanityCheck(aws); + return block({ ...context, aws }); + }); +} +exports.withAws = withAws; +/** + * Higher order function to execute a block with a CDK app fixture + * + * Requires an AWS client to be passed in. + * + * For backwards compatibility with existing tests (so we don't have to change + * too much) the inner block is expected to take a `TestFixture` object. + */ +function withCdkApp(block) { + return async (context) => { + const randy = randomString(); + const stackNamePrefix = `cdktest-${randy}`; + const integTestDir = path.join(os.tmpdir(), `cdk-integ-${randy}`); + context.output.write(` Stack prefix: ${stackNamePrefix}\n`); + context.output.write(` Test directory: ${integTestDir}\n`); + context.output.write(` Region: ${context.aws.region}\n`); + await cloneDirectory(path.join(__dirname, 'app'), integTestDir, context.output); + const fixture = new TestFixture(integTestDir, stackNamePrefix, context.output, context.aws); + let success = true; + try { + let modules = [ + '@aws-cdk/core', + '@aws-cdk/aws-sns', + '@aws-cdk/aws-iam', + '@aws-cdk/aws-lambda', + '@aws-cdk/aws-ssm', + '@aws-cdk/aws-ecr-assets', + '@aws-cdk/aws-cloudformation', + '@aws-cdk/aws-ec2', + ]; + if (FRAMEWORK_VERSION) { + modules = modules.map(module => `${module}@${FRAMEWORK_VERSION}`); + } + await fixture.shell(['npm', 'install', ...modules]); + await ensureBootstrapped(fixture); + await block(fixture); + } + catch (e) { + success = false; + throw e; + } + finally { + await fixture.dispose(success); + } + }; +} +exports.withCdkApp = withCdkApp; +/** + * Default test fixture for most (all?) integ tests + * + * It's a composition of withAws/withCdkApp, expecting the test block to take a `TestFixture` + * object. + * + * We could have put `withAws(withCdkApp(fixture => { /... actual test here.../ }))` in every + * test declaration but centralizing it is going to make it convenient to modify in the future. + */ +function withDefaultFixture(block) { + return withAws(withCdkApp(block)); + // ^~~~~~ this is disappointing TypeScript! Feels like you should have been able to derive this. +} +exports.withDefaultFixture = withDefaultFixture; +/** + * Prepare a target dir byreplicating a source directory + */ +async function cloneDirectory(source, target, output) { + await shell(['rm', '-rf', target], { output }); + await shell(['mkdir', '-p', target], { output }); + await shell(['cp', '-R', source + '/*', target], { output }); +} +exports.cloneDirectory = cloneDirectory; +class TestFixture { + constructor(integTestDir, stackNamePrefix, output, aws) { + this.integTestDir = integTestDir; + this.stackNamePrefix = stackNamePrefix; + this.output = output; + this.aws = aws; + this.qualifier = randomString().substr(0, 10); + this.bucketsToDelete = new Array(); + } + log(s) { + this.output.write(`${s}\n`); + } + async shell(command, options = {}) { + return shell(command, { + output: this.output, + cwd: this.integTestDir, + ...options, + }); + } + async cdkDeploy(stackNames, options = {}) { + var _a, _b; + stackNames = typeof stackNames === 'string' ? [stackNames] : stackNames; + const neverRequireApproval = (_a = options.neverRequireApproval) !== null && _a !== void 0 ? _a : true; + return this.cdk(['deploy', + ...(neverRequireApproval ? ['--require-approval=never'] : []), // Default to no approval in an unattended test + ...((_b = options.options) !== null && _b !== void 0 ? _b : []), ...this.fullStackName(stackNames)], options); + } + async cdkDestroy(stackNames, options = {}) { + var _a; + stackNames = typeof stackNames === 'string' ? [stackNames] : stackNames; + return this.cdk(['destroy', + '-f', // We never want a prompt in an unattended test + ...((_a = options.options) !== null && _a !== void 0 ? _a : []), ...this.fullStackName(stackNames)], options); + } + async cdk(args, options = {}) { + var _a; + const verbose = (_a = options.verbose) !== null && _a !== void 0 ? _a : true; + return this.shell(['cdk', ...(verbose ? ['-v'] : []), ...args], { + ...options, + modEnv: { + AWS_REGION: this.aws.region, + AWS_DEFAULT_REGION: this.aws.region, + STACK_NAME_PREFIX: this.stackNamePrefix, + ...options.modEnv, + }, + }); + } + fullStackName(stackNames) { + if (typeof stackNames === 'string') { + return `${this.stackNamePrefix}-${stackNames}`; + } + else { + return stackNames.map(s => `${this.stackNamePrefix}-${s}`); + } + } + /** + * Append this to the list of buckets to potentially delete + * + * At the end of a test, we clean up buckets that may not have gotten destroyed + * (for whatever reason). + */ + rememberToDeleteBucket(bucketName) { + this.bucketsToDelete.push(bucketName); + } + /** + * Cleanup leftover stacks and buckets + */ + async dispose(success) { + const stacksToDelete = await this.deleteableStacks(this.stackNamePrefix); + // Bootstrap stacks have buckets that need to be cleaned + const bucketNames = stacksToDelete.map(stack => aws_helpers_1.outputFromStack('BucketName', stack)).filter(defined); + await Promise.all(bucketNames.map(b => this.aws.emptyBucket(b))); + // Bootstrap stacks have ECR repositories with images which should be deleted + const imageRepositoryNames = stacksToDelete.map(stack => aws_helpers_1.outputFromStack('ImageRepositoryName', stack)).filter(defined); + await Promise.all(imageRepositoryNames.map(r => this.aws.deleteImageRepository(r))); + await this.aws.deleteStacks(...stacksToDelete.map(s => s.StackName)); + // We might have leaked some buckets by upgrading the bootstrap stack. Be + // sure to clean everything. + for (const bucket of this.bucketsToDelete) { + await this.aws.deleteBucket(bucket); + } + // If the tests completed successfully, happily delete the fixture + // (otherwise leave it for humans to inspect) + if (success) { + rimraf(this.integTestDir); + } + } + /** + * Return the stacks starting with our testing prefix that should be deleted + */ + async deleteableStacks(prefix) { + var _a; + const statusFilter = [ + 'CREATE_IN_PROGRESS', 'CREATE_FAILED', 'CREATE_COMPLETE', + 'ROLLBACK_IN_PROGRESS', 'ROLLBACK_FAILED', 'ROLLBACK_COMPLETE', + 'DELETE_FAILED', + 'UPDATE_IN_PROGRESS', 'UPDATE_COMPLETE_CLEANUP_IN_PROGRESS', + 'UPDATE_COMPLETE', 'UPDATE_ROLLBACK_IN_PROGRESS', + 'UPDATE_ROLLBACK_FAILED', + 'UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS', + 'UPDATE_ROLLBACK_COMPLETE', 'REVIEW_IN_PROGRESS', + 'IMPORT_IN_PROGRESS', 'IMPORT_COMPLETE', + 'IMPORT_ROLLBACK_IN_PROGRESS', 'IMPORT_ROLLBACK_FAILED', + 'IMPORT_ROLLBACK_COMPLETE', + ]; + const response = await this.aws.cloudFormation('describeStacks', {}); + return ((_a = response.Stacks) !== null && _a !== void 0 ? _a : []) + .filter(s => s.StackName.startsWith(prefix)) + .filter(s => statusFilter.includes(s.StackStatus)) + .filter(s => s.RootId === undefined); // Only delete parent stacks. Nested stacks are deleted in the process + } +} +exports.TestFixture = TestFixture; +/** + * Perform a one-time quick sanity check that the AWS clients has 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) { + if (sanityChecked === undefined) { + try { + await aws.account(); + sanityChecked = true; + } + catch (e) { + 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; +/** + * Make sure that the given environment is bootstrapped + * + * Since we go striping across regions, it's going to suck doing this + * by hand so let's just mass-automate it. + */ +async function ensureBootstrapped(fixture) { + // Old-style bootstrap stack with default name + if (await fixture.aws.stackStatus('CDKToolkit') === undefined) { + await fixture.cdk(['bootstrap', `aws://${await fixture.aws.account()}/${fixture.aws.region}`]); + } +} +/** + * A shell command that does what you want + * + * Is platform-aware, handles errors nicely. + */ +async function shell(command, options = {}) { + var _a, _b; + if (options.modEnv && options.env) { + throw new Error('Use either env or modEnv but not both'); + } + (_a = options.output) === null || _a === void 0 ? void 0 : _a.write(`💻 ${command.join(' ')}\n`); + const env = (_b = options.env) !== null && _b !== void 0 ? _b : (options.modEnv ? { ...process.env, ...options.modEnv } : undefined); + const child = child_process.spawn(command[0], command.slice(1), { + ...options, + env, + // Need this for Windows where we want .cmd and .bat to be found as well. + shell: true, + stdio: ['ignore', 'pipe', 'pipe'], + }); + return new Promise((resolve, reject) => { + const stdout = new Array(); + const stderr = new Array(); + child.stdout.on('data', chunk => { + var _a; + (_a = options.output) === null || _a === void 0 ? void 0 : _a.write(chunk); + stdout.push(chunk); + }); + child.stderr.on('data', chunk => { + var _a, _b; + (_a = options.output) === null || _a === void 0 ? void 0 : _a.write(chunk); + if ((_b = options.captureStderr) !== null && _b !== void 0 ? _b : true) { + stderr.push(chunk); + } + }); + child.once('error', reject); + child.once('close', code => { + if (code === 0 || options.allowErrExit) { + resolve((Buffer.concat(stdout).toString('utf-8') + Buffer.concat(stderr).toString('utf-8')).trim()); + } + else { + reject(new Error(`'${command.join(' ')}' exited with error code ${code}`)); + } + }); + }); +} +exports.shell = shell; +function defined(x) { + return x !== undefined; +} +/** + * rm -rf reimplementation, don't want to depend on an NPM package for this + */ +function rimraf(fsPath) { + try { + const isDir = fs.lstatSync(fsPath).isDirectory(); + if (isDir) { + for (const file of fs.readdirSync(fsPath)) { + rimraf(path.join(fsPath, file)); + } + fs.rmdirSync(fsPath); + } + else { + fs.unlinkSync(fsPath); + } + } + catch (e) { + // We will survive ENOENT + if (e.code !== 'ENOENT') { + throw e; + } + } +} +exports.rimraf = rimraf; +function randomString() { + // Crazy + return Math.random().toString(36).replace(/[^a-z0-9]+/g, ''); +} +exports.randomString = randomString; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2RrLWhlbHBlcnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjZGstaGVscGVycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7O0FBQUEsK0NBQStDO0FBQy9DLHlCQUF5QjtBQUN6Qix5QkFBeUI7QUFDekIsNkJBQTZCO0FBQzdCLCtDQUE0RDtBQUM1RCxtREFBK0M7QUFHL0MsTUFBTSxPQUFPLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxXQUFXO0lBQ3JDLENBQUMsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLFdBQVcsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDO0lBQ3BDLENBQUMsQ0FBQyxhQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsVUFBVSxtQ0FBSSxPQUFPLENBQUMsR0FBRyxDQUFDLGtCQUFrQixtQ0FBSSxXQUFXLENBQUMsQ0FBQztBQUU5RSxNQUFNLGlCQUFpQixHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsaUJBQWlCLENBQUM7QUFFeEQsT0FBTyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsa0JBQWtCLE9BQU8sSUFBSSxDQUFDLENBQUM7QUFDcEQsT0FBTyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsNEJBQTRCLGlCQUFpQixJQUFJLENBQUMsQ0FBQztBQUV4RSxNQUFNLFdBQVcsR0FBRyxJQUFJLDRCQUFZLENBQUMsT0FBTyxDQUFDLENBQUM7QUFLOUM7Ozs7R0FJRztBQUNILFNBQWdCLE9BQU8sQ0FBd0IsS0FBaUQ7SUFDOUYsT0FBTyxDQUFDLE9BQVUsRUFBRSxFQUFFLENBQUMsV0FBVyxDQUFDLEtBQUssQ0FBQyxLQUFLLEVBQUUsTUFBTSxFQUFFLEVBQUU7UUFDeEQsTUFBTSxHQUFHLEdBQUcsTUFBTSx3QkFBVSxDQUFDLFNBQVMsQ0FBQyxNQUFNLEVBQUUsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQy9ELE1BQU0sV0FBVyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBRXZCLE9BQU8sS0FBSyxDQUFDLEVBQUUs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QUFDekIsQ0FBQztBQUVEOztHQUVHO0FBQ0gsU0FBZ0IsTUFBTSxDQUFDLE1BQWM7SUFDbkMsSUFBSTtRQUNGLE1BQU0sS0FBSyxHQUFHLEVBQUUsQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLENBQUMsV0FBVyxFQUFFLENBQUM7UUFFakQsSUFBSSxLQUFLLEVBQUU7WUFDVCxLQUFLLE1BQU0sSUFBSSxJQUFJLEVBQUUsQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLEVBQUU7Z0JBQ3pDLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUFDO2FBQ2pDO1lBQ0QsRUFBRSxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsQ0FBQztTQUN0QjthQUFNO1lBQ0wsRUFBRSxDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsQ0FBQztTQUN2QjtLQUNGO0lBQUMsT0FBTyxDQUFDLEVBQUU7UUFDVix5QkFBeUI7UUFDekIsSUFBSSxDQUFDLENBQUMsSUFBSSxLQUFLLFFBQVEsRUFBRTtZQUFFLE1BQU0sQ0FBQyxDQUFDO1NBQUU7S0FDdEM7QUFDSCxDQUFDO0FBaEJELHdCQWdCQztBQUVELFNBQWdCLFlBQVk7SUFDMUIsUUFBUTtJQUNSLE9BQU8sSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsQ0FBQyxPQUFPLENBQUMsYUFBYSxFQUFFLEVBQUUsQ0FBQyxDQUFDO0FBQy9ELENBQUM7QUFIRCxvQ0FHQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGNoaWxkX3Byb2Nlc3MgZnJvbSAnY2hpbGRfcHJvY2Vzcyc7XG5pbXBvcnQgKiBhcyBmcyBmcm9tICdmcyc7XG5pbXBvcnQgKiBhcyBvcyBmcm9tICdvcyc7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0IHsgb3V0cHV0RnJvbVN0YWNrLCBBd3NDbGllbnRzIH0gZnJvbSAnLi9hd3MtaGVscGVycyc7XG5pbXBvcnQgeyBSZXNvdXJjZVBvb2wgfSBmcm9tICcuL3Jlc291cmNlLXBvb2wnO1xuaW1wb3J0IHsgVGVzdENvbnRleHQgfSBmcm9tICcuL3Rlc3QtaGVscGVycyc7XG5cbmNvbnN0IFJFR0lPTlMgPSBwcm9jZXNzLmVudi5BV1NfUkVHSU9OU1xuICA/IHByb2Nlc3MuZW52LkFXU19SRUdJT05TLnNwbGl0KCcsJylcbiAgOiBbcHJvY2Vzcy5lbnYuQVdTX1JFR0lPTiA/PyBwcm9jZXNzLmVudi5BV1NfREVGQVVMVF9SRUdJT04gPz8gJ3VzLWVhc3QtMSddO1xuXG5jb25zdCBGUkFNRVdPUktfVkVSU0lPTiA9IHByb2Nlc3MuZW52LkZSQU1FV09SS19WRVJTSU9OO1xuXG5wcm9jZXNzLnN0ZG91dC53cml0ZShgVXNpbmcgcmVnaW9uczogJHtSRUdJT05TfVxcbmApO1xucHJvY2Vzcy5zdGRvdXQud3JpdGUoYFVzaW5nIGZyYW1ld29yayB2ZXJzaW9uOiAke0ZSQU1FV09SS19WRVJTSU9OfVxcbmApO1xuXG5jb25zdCBSRUdJT05fUE9PTCA9IG5ldyBSZXNvdXJjZVBvb2woUkVHSU9OUyk7XG5cblxuZXhwb3J0IHR5cGUgQXdzQ29udGV4dCA9IHsgcmVhZG9ubHkgYXdzOiBBd3NDbGllbnRzIH07XG5cbi8qKlxuICogSGlnaGVyIG9yZGVyIGZ1bmN0aW9uIHRvIGV4ZWN1dGUgYSBibG9jayB3aXRoIGFuIEFXUyBjbGllbnQgc2V0dXBcbiAqXG4gKiBBbGxvY2F0ZSB0aGUgbmV4dCByZWdpb24gZnJvbSB0aGUgUkVHSU9OIHBvb2wgYW5kIGRpc3Bvc2UgaXQgYWZ0ZXJ3YXJkcy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHdpdGhBd3M8QSBleHRlbmRzIFRlc3RDb250ZXh0PihibG9jazogKGNvbnRleHQ6IEEgJiBBd3NDb250ZXh0KSA9PiBQcm9taXNlPHZvaWQ+KSB7XG4gIHJldHVybiAoY29udGV4dDogQSkgPT4gUkVHSU9OX1BPT0wudXNpbmcoYXN5bmMgKHJlZ2lvbikgPT4ge1xuICAgIGNvbnN0IGF3cyA9IGF3YWl0IEF3c0NsaWVudHMuZm9yUmVnaW9uKHJlZ2lvbiwgY29udGV4dC5vdXRwdXQpO1xuICAgIGF3YWl0IHNhbml0eUNoZWNrKGF3cyk7XG5cbiAgICByZXR1cm4gYmxvY2soeyAuLi5jb250ZXh0LCBhd3MgfSk7XG4gIH0pO1xufVxuXG4vKipcbiAqIEhpZ2hlciBvcmRlciBmdW5jdGlvbiB0byBleGVjdXRlIGEgYmxvY2sgd2l0aCBhIENESyBhcHAgZml4dHVyZVxuICpcbiAqIFJlcXVpcmVzIGFuIEFXUyBjbGllbnQgdG8gYmUgcGFzc2VkIGluLlxuICpcbiAqIEZvciBiYWNrd2FyZHMgY29tcGF0aWJpbGl0eSB3aXRoIGV4aXN0aW5nIHRlc3RzIChzbyB3ZSBkb24ndCBoYXZlIHRvIGNoYW5nZVxuICogdG9vIG11Y2gpIHRoZSBpbm5lciBibG9jayBpcyBleHBlY3RlZCB0byB0YWtlIGEgYFRlc3RGaXh0dXJlYCBvYmplY3QuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB3aXRoQ2RrQXBwPEEgZXh0ZW5kcyBUZXN0Q29udGV4dCAmIEF3c0NvbnRleHQ+KGJsb2NrOiAoY29udGV4dDogVGVzdEZpeHR1cmUpID0+IFByb21pc2U8dm9pZD4pIHtcbiAgcmV0dXJuIGFzeW5jIChjb250ZXh0OiBBKSA9PiB7XG4gICAgY29uc3QgcmFuZHkgPSByYW5kb21TdHJpbmcoKTtcbiAgICBjb25zdCBzdGFja05hbWVQcmVmaXggPSBgY2RrdGVzdC0ke3JhbmR5fWA7XG4gICAgY29uc3QgaW50ZWdUZXN0RGlyID0gcGF0aC5qb2luKG9zLnRtcGRpcigpLCBgY2RrLWludGVnLSR7cmFuZHl9YCk7XG5cbiAgICBjb250ZXh0Lm91dHB1dC53cml0ZShgIFN0YWNrIHByZWZpeDogICAke3N0YWNrTmFtZVByZWZpeH1cXG5gKTtcbiAgICBjb250ZXh0Lm91dHB1dC53cml0ZShgIFRlc3QgZGlyZWN0b3J5OiAke2ludGVnVGVzdERpcn1cXG5gKTtcbiAgICBjb250ZXh0Lm91dHB1dC53cml0ZShgIFJlZ2lvbjogICAgICAgICAke2NvbnRleHQuYXdzLnJlZ2lvbn1cXG5gKTtcblxuICAgIGF3YWl0IGNsb25lRGlyZWN0b3J5KHBhdGguam9pbihfX2Rpcm5hbWUsICdhcHAnKSwgaW50ZWdUZXN0RGlyLCBjb250ZXh0Lm91dHB1dCk7XG4gICAgY29uc3QgZml4dHVyZSA9IG5ldyBUZXN0Rml4dHVyZShcbiAgICAgIGludGVnVGVzdERpcixcbiAgICAgIHN0YWNrTmFtZVByZWZpeCxcbiAgICAgIGNvbnRleHQub3V0cHV0LFxuICAgICAgY29udGV4dC5hd3MpO1xuXG4gICAgbGV0IHN1Y2Nlc3MgPSB0cnVlO1xuICAgIHRyeSB7XG4gICAgICBsZXQgbW9kdWxlcyA9IFtcbiAgICAgICAgJ0Bhd3MtY2RrL2NvcmUnLFxuICAgICAgICAnQGF3cy1jZGsvYXdzLXNucycsXG4gICAgICAgICdAYXdzLWNkay9hd3MtaWFtJyxcbiAgICAgICAgJ0Bhd3MtY2RrL2F3cy1sYW1iZGEnLFxuICAgICAgICAnQGF3cy1jZGsvYXdzLXNzbScsXG4gICAgICAgICdAYXdzLWNkay9hd3MtZWNyLWFzc2V0cycsXG4gICAgICAgICdAYXdzLWNkay9hd3MtY2xvdWRmb3JtYXRpb24nLFxuICAgICAgICAnQGF3cy1jZGsvYXdzLWVjMicsXG4gICAgICBdO1xuICAgICAgaWYgKEZSQU1FV09SS19WRVJTSU9OKSB7XG4gICAgICAgIG1vZHVsZXMgPSBtb2R1bGVzLm1hcChtb2R1bGUgPT4gYCR7bW9kdWxlfUAke0ZSQU1FV09SS19WRVJTSU9OfWApO1xuICAgICAgfVxuICAgICAgYXdhaXQgZml4dHVyZS5zaGVsbChbJ25wbScsICdpbnN0YWxsJywgLi4ubW9kdWxlc10pO1xuXG4gICAgICBhd2FpdCBlbnN1cmVCb290c3RyYXBwZWQoZml4dHVyZSk7XG5cbiAgICAgIGF3YWl0IGJsb2NrKGZpeHR1cmUpO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIHN1Y2Nlc3MgPSBmYWxzZTtcbiAgICAgIHRocm93IGU7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIGF3YWl0IGZpeHR1cmUuZGlzcG9zZShzdWNjZXNzKTtcbiAgICB9XG4gIH07XG59XG5cbi8qKlxuICogRGVmYXVsdCB0ZXN0IGZpeHR1cmUgZm9yIG1vc3QgKGFsbD8pIGludGVnIHRlc3RzXG4gKlxuICogSXQncyBhIGNvbXBvc2l0aW9uIG9mIHdpdGhBd3Mvd2l0aENka0FwcCwgZXhwZWN0aW5nIHRoZSB0ZXN0IGJsb2NrIHRvIHRha2UgYSBgVGVzdEZpeHR1cmVgXG4gKiBvYmplY3QuXG4gKlxuICogV2UgY291bGQgaGF2ZSBwdXQgYHdpdGhBd3Mod2l0aENka0FwcChmaXh0dXJlID0+IHsgLy4uLiBhY3R1YWwgdGVzdCBoZXJlLi4uLyB9KSlgIGluIGV2ZXJ5XG4gKiB0ZXN0IGRlY2xhcmF0aW9uIGJ1dCBjZW50cmFsaXppbmcgaXQgaXMgZ29pbmcgdG8gbWFrZSBpdCBjb252ZW5pZW50IHRvIG1vZGlmeSBpbiB0aGUgZnV0dXJlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gd2l0aERlZmF1bHRGaXh0dXJlKGJsb2NrOiAoY29udGV4dDogVGVzdEZpeHR1cmUpID0+IFByb21pc2U8dm9pZD4pIHtcbiAgcmV0dXJuIHdpdGhBd3M8VGVzdENvbnRleHQ+KHdpdGhDZGtBcHAoYmxvY2spKTtcbiAgLy8gICAgICAgICAgICAgIF5+fn5+fiB0aGlzIGlzIGRpc2FwcG9pbnRpbmcgVHlwZVNjcmlwdCEgRmVlbHMgbGlrZSB5b3Ugc2hvdWxkIGhhdmUgYmVlbiBhYmxlIHRvIGRlcml2ZSB0aGlzLlxufVxuXG5leHBvcnQgaW50ZXJmYWNlIFNoZWxsT3B0aW9ucyBleHRlbmRzIGNoaWxkX3Byb2Nlc3MuU3Bhd25PcHRpb25zIHtcbiAgLyoqXG4gICAqIFByb3BlcnRpZXMgdG8gYWRkIHRvICdlbnYnXG4gICAqL1xuICBtb2RFbnY/OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+O1xuXG4gIC8qKlxuICAgKiBEb24ndCBmYWlsIHdoZW4gZXhpdGluZyB3aXRoIGFuIGVycm9yXG4gICAqXG4gICAqIEBkZWZhdWx0IGZhbHNlXG4gICAqL1xuICBhbGxvd0VyckV4aXQ/OiBib29sZWFuO1xuXG4gIC8qKlxuICAgKiBXaGV0aGVyIHRvIGNhcHR1cmUgc3RkZXJyXG4gICAqXG4gICAqIEBkZWZhdWx0IHRydWVcbiAgICovXG4gIGNhcHR1cmVTdGRlcnI/OiBib29sZWFuO1xuXG4gIC8qKlxuICAgKiBQYXNzIG91dHB1dCBoZXJlXG4gICAqL1xuICBvdXRwdXQ/OiBOb2RlSlMuV3JpdGFibGVTdHJlYW07XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgQ2RrQ2xpT3B0aW9ucyBleHRlbmRzIFNoZWxsT3B0aW9ucyB7XG4gIG9wdGlvbnM/OiBzdHJpbmdbXTtcbiAgbmV2ZXJSZXF1aXJlQXBwcm92YWw/OiBib29sZWFuO1xuICB2ZXJib3NlPzogYm9vbGVhbjtcbn1cblxuLyoqXG4gKiBQcmVwYXJlIGEgdGFyZ2V0IGRpciBieXJlcGxpY2F0aW5nIGEgc291cmNlIGRpcmVjdG9yeVxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2xvbmVEaXJlY3Rvcnkoc291cmNlOiBzdHJpbmcsIHRhcmdldDogc3RyaW5nLCBvdXRwdXQ/OiBOb2RlSlMuV3JpdGFibGVTdHJlYW0pIHtcbiAgYXdhaXQgc2hlbGwoWydybScsICctcmYnLCB0YXJnZXRdLCB7IG91dHB1dCB9KTtcbiAgYXdhaXQgc2hlbGwoWydta2RpcicsICctcCcsIHRhcmdldF0sIHsgb3V0cHV0IH0pO1xuICBhd2FpdCBzaGVsbChbJ2NwJywgJy1SJywgc291cmNlICsgJy8qJywgdGFyZ2V0XSwgeyBvdXRwdXQgfSk7XG59XG5cbmV4cG9ydCBjbGFzcyBUZXN0Rml4dHVyZSB7XG4gIHB1YmxpYyByZWFkb25seSBxdWFsaWZpZXIgPSByYW5kb21TdHJpbmcoKS5zdWJzdHIoMCwgMTApO1xuICBwcml2YXRlIHJlYWRvbmx5IGJ1Y2tldHNUb0RlbGV0ZSA9IG5ldyBBcnJheTxzdHJpbmc+KCk7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHVibGljIHJlYWRvbmx5IGludGVnVGVzdERpcjogc3RyaW5nLFxuICAgIHB1YmxpYyByZWFkb25seSBzdGFja05hbWVQcmVmaXg6IHN0cmluZyxcbiAgICBwdWJsaWMgcmVhZG9ubHkgb3V0cHV0OiBOb2RlSlMuV3JpdGFibGVTdHJlYW0sXG4gICAgcHVibGljIHJlYWRvbmx5IGF3czogQXdzQ2xpZW50cykge1xuICB9XG5cbiAgcHVibGljIGxvZyhzOiBzdHJpbmcpIHtcbiAgICB0aGlzLm91dHB1dC53cml0ZShgJHtzfVxcbmApO1xuICB9XG5cbiAgcHVibGljIGFzeW5jIHNoZWxsKGNvbW1hbmQ6IHN0cmluZ1tdLCBvcHRpb25zOiBPbWl0PFNoZWxsT3B0aW9ucywgJ2N3ZCd8J291dHB1dCc+ID0ge30pOiBQcm9taXNlPHN0cmluZz4ge1xuICAgIHJldHVybiBzaGVsbChjb21tYW5kLCB7XG4gICAgICBvdXRwdXQ6IHRoaXMub3V0cHV0LFxuICAgICAgY3dkOiB0aGlzLmludGVnVGVzdERpcixcbiAgICAgIC4uLm9wdGlvbnMsXG4gICAgfSk7XG4gIH1cblxuICBwdWJsaWMgYXN5bmMgY2RrRGVwbG95KHN0YWNrTmFtZXM6IHN0cmluZyB8IHN0cmluZ1tdLCBvcHRpb25zOiBDZGtDbGlPcHRpb25zID0ge30pIHtcbiAgICBzdGFja05hbWVzID0gdHlwZW9mIHN0YWNrTmFtZXMgPT09ICdzdHJpbmcnID8gW3N0YWNrTmFtZXNdIDogc3RhY2tOYW1lcztcblxuICAgIGNvbnN0IG5ldmVyUmVxdWlyZUFwcHJvdmFsID0gb3B0aW9ucy5uZXZlclJlcXVpcmVBcHByb3ZhbCA/PyB0cnVlO1xuXG4gICAgcmV0dXJuIHRoaXMuY2RrKFsnZGVwbG95JyxcbiAgICAgIC4uLihuZXZlclJlcXVpcmVBcHByb3ZhbCA/IFsnLS1yZXF1aXJlLWFwcHJvdmFsPW5ldmVyJ10gOiBbXSksIC8vIERlZmF1bHQgdG8gbm8gYXBwcm92YWwgaW4gYW4gdW5hdHRlbmRlZCB0ZXN0XG4gICAgICAuLi4ob3B0aW9ucy5vcHRpb25zID8/IFtdKSxcbiAgICAgIC4uLnRoaXMuZnVsbFN0YWNrTmFtZShzdGFja05hbWVzKV0sIG9wdGlvbnMpO1xuICB9XG5cbiAgcHVibGljIGFzeW5jIGNka0Rlc3Ryb3koc3RhY2tOYW1lczogc3RyaW5nIHwgc3RyaW5nW10sIG9wdGlvbnM6IENka0NsaU9wdGlvbnMgPSB7fSkge1xuICAgIHN0YWNrTmFtZXMgPSB0eXBlb2Ygc3RhY2tOYW1lcyA9PT0gJ3N0cmluZycgPyBbc3RhY2tOYW1lc10gOiBzdGFja05hbWVzO1xuXG4gICAgcmV0dXJuIHRoaXMuY2RrKFsnZGVzdHJveScsXG4gICAgICAnLWYnLCAvLyBXZSBuZXZlciB3YW50IGEgcHJvbXB0IGluIGFuIHVuYXR0ZW5kZWQgdGVzdFxuICAgICAgLi4uKG9wdGlvbnMub3B0aW9ucyA/PyBbXSksXG4gICAgICAuLi50aGlzLmZ1bGxTdGFja05hbWUoc3RhY2tOYW1lcyldLCBvcHRpb25zKTtcbiAgfVxuXG4gIHB1YmxpYyBhc3luYyBjZGsoYXJnczogc3RyaW5nW10sIG9wdGlvbnM6IENka0NsaU9wdGlvbnMgPSB7fSkge1xuICAgIGNvbnN0IHZlcmJvc2UgPSBvcHRpb25zLnZlcmJvc2UgPz8gdHJ1ZTtcblxuICAgIHJldHVybiB0aGlzLnNoZWxsKFsnY2RrJywgLi4uKHZlcmJvc2UgPyBbJy12J10gOiBbXSksIC4uLmFyZ3NdLCB7XG4gICAgICAuLi5vcHRpb25zLFxuICAgICAgbW9kRW52OiB7XG4gICAgICAgIEFXU19SRUdJT046IHRoaXMuYXdzLnJlZ2lvbixcbiAgICAgICAgQVdTX0RFRkFVTFRfUkVHSU9OOiB0aGlzLmF3cy5yZWdpb24sXG4gICAgICAgIFNUQUNLX05BTUVfUFJFRklYOiB0aGlzLnN0YWNrTmFtZVByZWZpeCxcbiAgICAgICAgLi4ub3B0aW9ucy5tb2RFbnYsXG4gICAgICB9LFxuICAgIH0pO1xuICB9XG5cbiAgcHVibGljIGZ1bGxTdGFja05hbWUoc3RhY2tOYW1lOiBzdHJpbmcpOiBzdHJpbmc7XG4gIHB1YmxpYyBmdWxsU3RhY2tOYW1lKHN0YWNrTmFtZXM6IHN0cmluZ1tdKTogc3RyaW5nW107XG4gIHB1YmxpYyBmdWxsU3RhY2tOYW1lKHN0YWNrTmFtZXM6IHN0cmluZyB8IHN0cmluZ1tdKTogc3RyaW5nIHwgc3RyaW5nW10ge1xuICAgIGlmICh0eXBlb2Ygc3RhY2tOYW1lcyA9PT0gJ3N0cmluZycpIHtcbiAgICAgIHJldHVybiBgJHt0aGlzLnN0YWNrTmFtZVByZWZpeH0tJHtzdGFja05hbWVzfWA7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBzdGFja05hbWVzLm1hcChzID0+IGAke3RoaXMuc3RhY2tOYW1lUHJlZml4fS0ke3N9YCk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEFwcGVuZCB0aGlzIHRvIHRoZSBsaXN0IG9mIGJ1Y2tldHMgdG8gcG90ZW50aWFsbHkgZGVsZXRlXG4gICAqXG4gICAqIEF0IHRoZSBlbmQgb2YgYSB0ZXN0LCB3ZSBjbGVhbiB1cCBidWNrZXRzIHRoYXQgbWF5IG5vdCBoYXZlIGdvdHRlbiBkZXN0cm95ZWRcbiAgICogKGZvciB3aGF0ZXZlciByZWFzb24pLlxuICAgKi9cbiAgcHVibGljIHJlbWVtYmVyVG9EZWxldGVCdWNrZXQoYnVja2V0TmFtZTogc3RyaW5nKSB7XG4gICAgdGhpcy5idWNrZXRzVG9EZWxldGUucHVzaChidWNrZXROYW1lKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDbGVhbnVwIGxlZnRvdmVyIHN0YWNrcyBhbmQgYnVja2V0c1xuICAgKi9cbiAgcHVibGljIGFzeW5jIGRpc3Bvc2Uoc3VjY2VzczogYm9vbGVhbikge1xuICAgIGNvbnN0IHN0YWNrc1RvRGVsZXRlID0gYXdhaXQgdGhpcy5kZWxldGVhYmxlU3RhY2tzKHRoaXMuc3RhY2tOYW1lUHJlZml4KTtcblxuICAgIC8vIEJvb3RzdHJhcCBzdGFja3MgaGF2ZSBidWNrZXRzIHRoYXQgbmVlZCB0byBiZSBjbGVhbmVkXG4gICAgY29uc3QgYnVja2V0TmFtZXMgPSBzdGFja3NUb0RlbGV0ZS5tYXAoc3RhY2sgPT4gb3V0cHV0RnJvbVN0YWNrKCdCdWNrZXROYW1lJywgc3RhY2spKS5maWx0ZXIoZGVmaW5lZCk7XG4gICAgYXdhaXQgUHJvbWlzZS5hbGwoYnVja2V0TmFtZXMubWFwKGIgPT4gdGhpcy5hd3MuZW1wdHlCdWNrZXQoYikpKTtcblxuICAgIC8vIEJvb3RzdHJhcCBzdGFja3MgaGF2ZSBFQ1IgcmVwb3NpdG9yaWVzIHdpdGggaW1hZ2VzIHdoaWNoIHNob3VsZCBiZSBkZWxldGVkXG4gICAgY29uc3QgaW1hZ2VSZXBvc2l0b3J5TmFtZXMgPSBzdGFja3NUb0RlbGV0ZS5tYXAoc3RhY2sgPT4gb3V0cHV0RnJvbVN0YWNrKCdJbWFnZVJlcG9zaXRvcnlOYW1lJywgc3RhY2spKS5maWx0ZXIoZGVmaW5lZCk7XG4gICAgYXdhaXQgUHJvbWlzZS5hbGwoaW1hZ2VSZXBvc2l0b3J5TmFtZXMubWFwKHIgPT4gdGhpcy5hd3MuZGVsZXRlSW1hZ2VSZXBvc2l0b3J5KHIpKSk7XG5cbiAgICBhd2FpdCB0aGlzLmF3cy5kZWxldGVTdGFja3MoLi4uc3RhY2tzVG9EZWxldGUubWFwKHMgPT4gcy5TdGFja05hbWUpKTtcblxuICAgIC8vIFdlIG1pZ2h0IGhhdmUgbGVha2VkIHNvbWUgYnVja2V0cyBieSB1cGdyYWRpbmcgdGhlIGJvb3RzdHJhcCBzdGFjay4gQmVcbiAgICAvLyBzdXJlIHRvIGNsZWFuIGV2ZXJ5dGhpbmcuXG4gICAgZm9yIChjb25zdCBidWNrZXQgb2YgdGhpcy5idWNrZXRzVG9EZWxldGUpIHtcbiAgICAgIGF3YWl0IHRoaXMuYXdzLmRlbGV0ZUJ1Y2tldChidWNrZXQpO1xuICAgIH1cblxuICAgIC8vIElmIHRoZSB0ZXN0cyBjb21wbGV0ZWQgc3VjY2Vzc2Z1bGx5LCBoYXBwaWx5IGRlbGV0ZSB0aGUgZml4dHVyZVxuICAgIC8vIChvdGhlcndpc2UgbGVhdmUgaXQgZm9yIGh1bWFucyB0byBpbnNwZWN0KVxuICAgIGlmIChzdWNjZXNzKSB7XG4gICAgICByaW1yYWYodGhpcy5pbnRlZ1Rlc3REaXIpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm4gdGhlIHN0YWNrcyBzdGFydGluZyB3aXRoIG91ciB0ZXN0aW5nIHByZWZpeCB0aGF0IHNob3VsZCBiZSBkZWxldGVkXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGRlbGV0ZWFibGVTdGFja3MocHJlZml4OiBzdHJpbmcpOiBQcm9taXNlPEFXUy5DbG91ZEZvcm1hdGlvbi5TdGFja1tdPiB7XG4gICAgY29uc3Qgc3RhdHVzRmlsdGVyID0gW1xuICAgICAgJ0NSRUFURV9JTl9QUk9HUkVTUycsICdDUkVBVEVfRkFJTEVEJywgJ0NSRUFURV9DT01QTEVURScsXG4gICAgICAnUk9MTEJBQ0tfSU5fUFJPR1JFU1MnLCAnUk9MTEJBQ0tfRkFJTEVEJywgJ1JPTExCQUNLX0NPTVBMRVRFJyxcbiAgICAgICdERUxFVEVfRkFJTEVEJyxcbiAgICAgICdVUERBVEVfSU5fUFJPR1JFU1MnLCAnVVBEQVRFX0NPTVBMRVRFX0NMRUFOVVBfSU5fUFJPR1JFU1MnLFxuICAgICAgJ1VQREFURV9DT01QTEVURScsICdVUERBVEVfUk9MTEJBQ0tfSU5fUFJPR1JFU1MnLFxuICAgICAgJ1VQREFURV9ST0xMQkFDS19GQUlMRUQnLFxuICAgICAgJ1VQREFURV9ST0xMQkFDS19DT01QTEVURV9DTEVBTlVQX0lOX1BST0dSRVNTJyxcbiAgICAgICdVUERBVEVfUk9MTEJBQ0tfQ09NUExFVEUnLCAnUkVWSUVXX0lOX1BST0dSRVNTJyxcbiAgICAgICdJTVBPUlRfSU5fUFJPR1JFU1MnLCAnSU1QT1JUX0NPTVBMRVRFJyxcbiAgICAgICdJTVBPUlRfUk9MTEJBQ0tfSU5fUFJPR1JFU1MnLCAnSU1QT1JUX1JPTExCQUNLX0ZBSUxFRCcsXG4gICAgICAnSU1QT1JUX1JPTExCQUNLX0NPTVBMRVRFJyxcbiAgICBdO1xuXG4gICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCB0aGlzLmF3cy5jbG91ZEZvcm1hdGlvbignZGVzY3JpYmVTdGFja3MnLCB7fSk7XG5cbiAgICByZXR1cm4gKHJlc3BvbnNlLlN0YWNrcyA/PyBbXSlcbiAgICAgIC5maWx0ZXIocyA9PiBzLlN0YWNrTmFtZS5zdGFydHNXaXRoKHByZWZpeCkpXG4gICAgICAuZmlsdGVyKHMgPT4gc3RhdHVzRmlsdGVyLmluY2x1ZGVzKHMuU3RhY2tTdGF0dXMpKVxuICAgICAgLmZpbHRlcihzID0+IHMuUm9vdElkID09PSB1bmRlZmluZWQpOyAvLyBPbmx5IGRlbGV0ZSBwYXJlbnQgc3RhY2tzLiBOZXN0ZWQgc3RhY2tzIGFyZSBkZWxldGVkIGluIHRoZSBwcm9jZXNzXG4gIH1cbn1cblxuLyoqXG4gKiBQZXJmb3JtIGEgb25lLXRpbWUgcXVpY2sgc2FuaXR5IGNoZWNrIHRoYXQgdGhlIEFXUyBjbGllbnRzIGhhcyBwcm9wZXJseSBjb25maWd1cmVkIGNyZWRlbnRpYWxzXG4gKlxuICogSWYgd2UgZG9uJ3QgZG8gdGhpcywgY2FsbHMgYXJlIGdvaW5nIHRvIGZhaWwgYW5kIHRoZXknbGwgYmUgcmV0cmllZCBhbmQgZXZlcnl0aGluZyB3aWxsIHRha2VcbiAqIGZvcmV2ZXIgYmVmb3JlIHRoZSB1c2VyIG5vdGljZXMgYSBzaW1wbGUgbWlzY29uZmlndXJhdGlvbi5cbiAqXG4gKiBXZSBjYW4ndCBjaGVjayBmb3IgdGhlIHByZXNlbmNlIG9mIGVudmlyb25tZW50IHZhcmlhYmxlcyBzaW5jZSBjcmVkZW50aWFscyBjb3VsZCBjb21lIGZyb21cbiAqIGFueXdoZXJlLCBzbyBkbyBzaW1wbGUgYWNjb3VudCByZXRyaWV2YWwuXG4gKlxuICogT25seSBkbyBpdCBvbmNlIHBlciBwcm9jZXNzLlxuICovXG5hc3luYyBmdW5jdGlvbiBzYW5pdHlDaGVjayhhd3M6IEF3c0NsaWVudHMpIHtcbiAgaWYgKHNhbml0eUNoZWNrZWQgPT09IHVuZGVmaW5lZCkge1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCBhd3MuYWNjb3VudCgpO1xuICAgICAgc2FuaXR5Q2hlY2tlZCA9IHRydWU7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgc2FuaXR5Q2hlY2tlZCA9IGZhbHNlO1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBBV1MgY3JlZGVudGlhbHMgcHJvYmFibHkgbm90IGNvbmZpZ3VyZWQsIGdvdCBlcnJvcjogJHtlLm1lc3NhZ2V9YCk7XG4gICAgfVxuICB9XG4gIGlmICghc2FuaXR5Q2hlY2tlZCkge1xuICAgIHRocm93IG5ldyBFcnJvcignQVdTIGNyZWRlbnRpYWxzIHByb2JhYmx5IG5vdCBjb25maWd1cmVkLCBzZWUgcHJldmlvdXMgZXJyb3InKTtcbiAgfVxufVxubGV0IHNhbml0eUNoZWNrZWQ6IGJvb2xlYW4gfCB1bmRlZmluZWQ7XG5cbi8qKlxuICogTWFrZSBzdXJlIHRoYXQgdGhlIGdpdmVuIGVudmlyb25tZW50IGlzIGJvb3RzdHJhcHBlZFxuICpcbiAqIFNpbmNlIHdlIGdvIHN0cmlwaW5nIGFjcm9zcyByZWdpb25zLCBpdCdzIGdvaW5nIHRvIHN1Y2sgZG9pbmcgdGhpc1xuICogYnkgaGFuZCBzbyBsZXQncyBqdXN0IG1hc3MtYXV0b21hdGUgaXQuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGVuc3VyZUJvb3RzdHJhcHBlZChmaXh0dXJlOiBUZXN0Rml4dHVyZSkge1xuICAvLyBPbGQtc3R5bGUgYm9vdHN0cmFwIHN0YWNrIHdpdGggZGVmYXVsdCBuYW1lXG4gIGlmIChhd2FpdCBmaXh0dXJlLmF3cy5zdGFja1N0YXR1cygnQ0RLVG9vbGtpdCcpID09PSB1bmRlZmluZWQpIHtcbiAgICBhd2FpdCBmaXh0dXJlLmNkayhbJ2Jvb3RzdHJhcCcsIGBhd3M6Ly8ke2F3YWl0IGZpeHR1cmUuYXdzLmFjY291bnQoKX0vJHtmaXh0dXJlLmF3cy5yZWdpb259YF0pO1xuICB9XG59XG5cbi8qKlxuICogQSBzaGVsbCBjb21tYW5kIHRoYXQgZG9lcyB3aGF0IHlvdSB3YW50XG4gKlxuICogSXMgcGxhdGZvcm0tYXdhcmUsIGhhbmRsZXMgZXJyb3JzIG5pY2VseS5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHNoZWxsKGNvbW1hbmQ6IHN0cmluZ1tdLCBvcHRpb25zOiBTaGVsbE9wdGlvbnMgPSB7fSk6IFByb21pc2U8c3RyaW5nPiB7XG4gIGlmIChvcHRpb25zLm1vZEVudiAmJiBvcHRpb25zLmVudikge1xuICAgIHRocm93IG5ldyBFcnJvcignVXNlIGVpdGhlciBlbnYgb3IgbW9kRW52IGJ1dCBub3QgYm90aCcpO1xuICB9XG5cbiAgb3B0aW9ucy5vdXRwdXQ/LndyaXRlKGDwn5K7ICR7Y29tbWFuZC5qb2luKCcgJyl9XFxuYCk7XG5cbiAgY29uc3QgZW52ID0gb3B0aW9ucy5lbnYgPz8gKG9wdGlvbnMubW9kRW52ID8geyAuLi5wcm9jZXNzLmVudiwgLi4ub3B0aW9ucy5tb2RFbnYgfSA6IHVuZGVmaW5lZCk7XG5cbiAgY29uc3QgY2hpbGQgPSBjaGlsZF9wcm9jZXNzLnNwYXduKGNvbW1hbmRbMF0sIGNvbW1hbmQuc2xpY2UoMSksIHtcbiAgICAuLi5vcHRpb25zLFxuICAgIGVudixcbiAgICAvLyBOZWVkIHRoaXMgZm9yIFdpbmRvd3Mgd2hlcmUgd2Ugd2FudCAuY21kIGFuZCAuYmF0IHRvIGJlIGZvdW5kIGFzIHdlbGwuXG4gICAgc2hlbGw6IHRydWUsXG4gICAgc3RkaW86IFsnaWdub3JlJywgJ3BpcGUnLCAncGlwZSddLFxuICB9KTtcblxuICByZXR1cm4gbmV3IFByb21pc2U8c3RyaW5nPigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgY29uc3Qgc3Rkb3V0ID0gbmV3IEFycmF5PEJ1ZmZlcj4oKTtcbiAgICBjb25zdCBzdGRlcnIgPSBuZXcgQXJyYXk8QnVmZmVyPigpO1xuXG4gICAgY2hpbGQuc3Rkb3V0IS5vbignZGF0YScsIGNodW5rID0+IHtcbiAgICAgIG9wdGlvbnMub3V0cHV0Py53cml0ZShjaHVuayk7XG4gICAgICBzdGRvdXQucHVzaChjaHVuayk7XG4gICAgfSk7XG5cbiAgICBjaGlsZC5zdGRlcnIhLm9uKCdkYXRhJywgY2h1bmsgPT4ge1xuICAgICAgb3B0aW9ucy5vdXRwdXQ/LndyaXRlKGNodW5rKTtcbiAgICAgIGlmIChvcHRpb25zLmNhcHR1cmVTdGRlcnIgPz8gdHJ1ZSkge1xuICAgICAgICBzdGRlcnIucHVzaChjaHVuayk7XG4gICAgICB9XG4gICAgfSk7XG5cbiAgICBjaGlsZC5vbmNlKCdlcnJvcicsIHJlamVjdCk7XG5cbiAgICBjaGlsZC5vbmNlKCdjbG9zZScsIGNvZGUgPT4ge1xuICAgICAgaWYgKGNvZGUgPT09IDAgfHwgb3B0aW9ucy5hbGxvd0VyckV4aXQpIHtcbiAgICAgICAgcmVzb2x2ZSgoQnVmZmVyLmNvbmNhdChzdGRvdXQpLnRvU3RyaW5nKCd1dGYtOCcpICsgQnVmZmVyLmNvbmNhdChzdGRlcnIpLnRvU3RyaW5nKCd1dGYtOCcpKS50cmltKCkpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmVqZWN0KG5ldyBFcnJvcihgJyR7Y29tbWFuZC5qb2luKCcgJyl9JyBleGl0ZWQgd2l0aCBlcnJvciBjb2RlICR7Y29kZX1gKSk7XG4gICAgICB9XG4gICAgfSk7XG4gIH0pO1xufVxuXG5mdW5jdGlvbiBkZWZpbmVkPEE+KHg6IEEpOiB4IGlzIE5vbk51bGxhYmxlPEE+IHtcbiAgcmV0dXJuIHggIT09IHVuZGVmaW5lZDtcbn1cblxuLyoqXG4gKiBybSAtcmYgcmVpbXBsZW1lbnRhdGlvbiwgZG9uJ3Qgd2FudCB0byBkZXBlbmQgb24gYW4gTlBNIHBhY2thZ2UgZm9yIHRoaXNcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJpbXJhZihmc1BhdGg6IHN0cmluZykge1xuICB0cnkge1xuICAgIGNvbnN0IGlzRGlyID0gZnMubHN0YXRTeW5jKGZzUGF0aCkuaXNEaXJlY3RvcnkoKTtcblxuICAgIGlmIChpc0Rpcikge1xuICAgICAgZm9yIChjb25zdCBmaWxlIG9mIGZzLnJlYWRkaXJTeW5jKGZzUGF0aCkpIHtcbiAgICAgICAgcmltcmFmKHBhdGguam9pbihmc1BhdGgsIGZpbGUpKTtcbiAgICAgIH1cbiAgICAgIGZzLnJtZGlyU3luYyhmc1BhdGgpO1xuICAgIH0gZWxzZSB7XG4gICAgICBmcy51bmxpbmtTeW5jKGZzUGF0aCk7XG4gICAgfVxuICB9IGNhdGNoIChlKSB7XG4gICAgLy8gV2Ugd2lsbCBzdXJ2aXZlIEVOT0VOVFxuICAgIGlmIChlLmNvZGUgIT09ICdFTk9FTlQnKSB7IHRocm93IGU7IH1cbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gcmFuZG9tU3RyaW5nKCkge1xuICAvLyBDcmF6eVxuICByZXR1cm4gTWF0aC5yYW5kb20oKS50b1N0cmluZygzNikucmVwbGFjZSgvW15hLXowLTldKy9nLCAnJyk7XG59Il19 \ No newline at end of file diff --git a/packages/aws-cdk/test/integ/cli/cdk-helpers.ts b/packages/aws-cdk/test/integ/cli/cdk-helpers.ts index 01829ebff413f..c1049e330d77e 100644 --- a/packages/aws-cdk/test/integ/cli/cdk-helpers.ts +++ b/packages/aws-cdk/test/integ/cli/cdk-helpers.ts @@ -10,7 +10,10 @@ const REGIONS = process.env.AWS_REGIONS ? process.env.AWS_REGIONS.split(',') : [process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION ?? 'us-east-1']; +const FRAMEWORK_VERSION = process.env.FRAMEWORK_VERSION; + process.stdout.write(`Using regions: ${REGIONS}\n`); +process.stdout.write(`Using framework version: ${FRAMEWORK_VERSION}\n`); const REGION_POOL = new ResourcePool(REGIONS); @@ -58,7 +61,7 @@ export function withCdkApp(block: (context: let success = true; try { - await fixture.shell(['npm', 'install', + let modules = [ '@aws-cdk/core', '@aws-cdk/aws-sns', '@aws-cdk/aws-iam', @@ -66,7 +69,12 @@ export function withCdkApp(block: (context: '@aws-cdk/aws-ssm', '@aws-cdk/aws-ecr-assets', '@aws-cdk/aws-cloudformation', - '@aws-cdk/aws-ec2']); + '@aws-cdk/aws-ec2', + ]; + if (FRAMEWORK_VERSION) { + modules = modules.map(module => `${module}@${FRAMEWORK_VERSION}`); + } + await fixture.shell(['npm', 'install', ...modules]); await ensureBootstrapped(fixture); diff --git a/packages/aws-cdk/test/integ/cli/test.sh b/packages/aws-cdk/test/integ/cli/test.sh index 42b4994b72ce9..05be97e5312c3 100755 --- a/packages/aws-cdk/test/integ/cli/test.sh +++ b/packages/aws-cdk/test/integ/cli/test.sh @@ -6,12 +6,6 @@ echo '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' echo 'CLI Integration Tests' echo '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' -current_version=$(node -p "require('${scriptdir}/../../../package.json').version") - -# This allows injecting different versions, not just the current one. -# Useful when testing. -export VERSION_UNDER_TEST=${VERSION_UNDER_TEST:-${current_version}} - cd $scriptdir # Install these dependencies that the tests (written in Jest) need. diff --git a/packages/aws-cdk/test/integ/github-helpers.ts b/packages/aws-cdk/test/integ/github-helpers.ts new file mode 100644 index 0000000000000..3a0bc1fb84195 --- /dev/null +++ b/packages/aws-cdk/test/integ/github-helpers.ts @@ -0,0 +1,30 @@ +import { Octokit } from '@octokit/rest'; +import * as semver from 'semver'; + +module.exports.fetchPreviousVersion = async function(base: string) { + const token = process.env.GITHUB_TOKEN; + if (!token) { + throw new Error('GITHUB_TOKEN must be set'); + } + + const github = new Octokit({ auth: token }); + const releases = await github.repos.listReleases({ + owner: 'aws', + repo: 'aws-cdk', + }); + + // this returns a list in decsending order, newest releases first + for (const release of releases.data) { + const version = release.name.replace('v', ''); + if (semver.lt(version, base)) { + return version; + } + } + throw new Error(`Unable to find previous version of ${base}`); + +}; + +// eslint-disable-next-line @typescript-eslint/no-require-imports +require('make-runnable/custom')({ + printOutputFrame: false, +}); \ No newline at end of file diff --git a/packages/aws-cdk/test/integ/run-against-dist b/packages/aws-cdk/test/integ/run-against-dist index 3b314abc0e2c4..ed17ddee88243 100755 --- a/packages/aws-cdk/test/integ/run-against-dist +++ b/packages/aws-cdk/test/integ/run-against-dist @@ -23,8 +23,11 @@ if [[ ! -f $dist_root/build.json ]]; then exit 1 fi +export CANDIDATE_VERSION=$(node -p "require('${dist_root}/build.json').version") +export TEST_RUNNER=dist serve_npm_packages +install_cli prepare_java_packages prepare_nuget_packages diff --git a/packages/aws-cdk/test/integ/run-against-dist.bash b/packages/aws-cdk/test/integ/run-against-dist.bash index a77f564f1895f..83c29e82a5620 100644 --- a/packages/aws-cdk/test/integ/run-against-dist.bash +++ b/packages/aws-cdk/test/integ/run-against-dist.bash @@ -4,7 +4,6 @@ npmws=/tmp/cdk-rundist rm -rf $npmws mkdir -p $npmws - # This script must create 1 or 2 traps, and the 'trap' command will replace # the previous trap, so get some 'dynamic traps' mechanism in place TRAPS=() @@ -35,66 +34,24 @@ function serve_npm_packages() { return 1 fi - local_cli_version="$(node -e "console.log(require('${dist_root}/build.json').version)")" + #------------------------------------------------------------------------------ + # Start a mock npm repository from the given tarballs + #------------------------------------------------------------------------------ + header "Starting local NPM Repository (Serving version ${CANDIDATE_VERSION})" tarballs_glob="$dist_root/js/*.tgz" - if [ ! -z "${USE_PUBLISHED_FRAMEWORK_VERSION:-}" ]; then - - echo "Testing against latest published versions of the framework" - - header "Installing aws-cdk from local tarballs..." - # Need 'npm install --prefix' otherwise it goes to the wrong directory - (cd ${npmws} && npx serve-npm-tarballs --glob "${tarballs_glob}" -- npm install --prefix $npmws aws-cdk@${local_cli_version}) - export PATH=$npmws/node_modules/.bin:$PATH - - else - - echo "Testing against local versions of the framework" - - #------------------------------------------------------------------------------ - # Start a mock npm repository from the given tarballs - #------------------------------------------------------------------------------ - header "Starting local NPM Repository" - - # When using '--daemon', 'npm install' first so the files are permanent, or - # 'npx' will remove them too soon. - npm install serve-npm-tarballs - eval $(npx serve-npm-tarballs --glob "${tarballs_glob}" --daemon) - TRAPS+=("kill $SERVE_NPM_TARBALLS_PID") - - header "Installing aws-cdk from local tarballs..." - # Need 'npm install --prefix' otherwise it goes to the wrong directory - (cd ${npmws} && npm install --prefix $npmws aws-cdk@${local_cli_version}) - export PATH=$npmws/node_modules/.bin:$PATH - - fi - - # a bit silly, but it verifies the PATH exports and just makes sure - # that we run 'cdk' commands we use the version we just installed. - verify_installed_cli_version ${local_cli_version} - + # When using '--daemon', 'npm install' first so the files are permanent, or + # 'npx' will remove them too soon. + npm install serve-npm-tarballs + eval $(npx serve-npm-tarballs --glob "${tarballs_glob}" --daemon) + TRAPS+=("kill $SERVE_NPM_TARBALLS_PID") } -# Make sure that installed CLI matches the build version -function verify_installed_cli_version() { - - expected_version=$1 - - header "Expected CDK version: ${expected_version}" - - log "Found CDK: $(type -p cdk)" - - # Execute "cdk --version" as a validation that the toolkit is installed - local actual_version="$(cdk --version | cut -d" " -f1)" - - if [ "${expected_version}" != "${actual_version}" ]; then - log "Mismatched CDK version. Expected: ${expected_version}, actual: ${actual_version}" - cdk --version - exit 1 - else - log "Verified CDK version is: ${expected_version}" - fi +function install_cli() { + echo "Installing CLI aws-cdk@${CANDIDATE_VERSION}" + (cd ${npmws} && npm install --prefix $npmws aws-cdk@${CANDIDATE_VERSION}) + export PATH=$npmws/node_modules/.bin:$PATH } function prepare_java_packages() { @@ -180,4 +137,4 @@ function prepare_python_packages() { function pip() { pip_ "$@" -} +} \ No newline at end of file diff --git a/packages/aws-cdk/test/integ/run-against-release b/packages/aws-cdk/test/integ/run-against-release index 5ea9bf34ff7d4..0865bd331a25d 100755 --- a/packages/aws-cdk/test/integ/run-against-release +++ b/packages/aws-cdk/test/integ/run-against-release @@ -14,6 +14,7 @@ mkdir -p $npmws # Install the CLI and put it on the PATH (cd $npmws && npm install aws-cdk) export PATH=$npmws/node_modules/.bin:$PATH +export TEST_RUNNER=release # Run the inner script exec "$@" diff --git a/packages/aws-cdk/test/integ/run-against-repo b/packages/aws-cdk/test/integ/run-against-repo index 601097c880d18..420850f2cd951 100755 --- a/packages/aws-cdk/test/integ/run-against-repo +++ b/packages/aws-cdk/test/integ/run-against-repo @@ -15,6 +15,7 @@ fi export REPO_ROOT="$repo_root" export ORIGINAL_NPM="$(type -p npm)" +export TEST_RUNNER=repo export PATH="$cli_dir/bin:$cli_dir/test/integ/run-wrappers/repo:$PATH" hash -r diff --git a/packages/aws-cdk/test/integ/run-wrappers/repo/npm b/packages/aws-cdk/test/integ/run-wrappers/repo/npm index e90e296bbef1d..4a49bd85621b5 100755 --- a/packages/aws-cdk/test/integ/run-wrappers/repo/npm +++ b/packages/aws-cdk/test/integ/run-wrappers/repo/npm @@ -5,11 +5,7 @@ command=$1 lerna=$REPO_ROOT/node_modules/.bin/lerna -if [ ! -z "${USE_PUBLISHED_FRAMEWORK_VERSION:-}" ]; then - # simply pass through to npm and install the published package. - echo "Running original NPM command since we are testing against published framework version" - exec $ORIGINAL_NPM "$@" -elif [[ "$command" == "install" || "$command" == "i" ]]; then +if [[ "$command" == "install" || "$command" == "i" ]]; then npmargs="install" shift @@ -33,3 +29,5 @@ elif [[ "$command" == "install" || "$command" == "i" ]]; then exec $ORIGINAL_NPM $npmargs fi + +exec $ORIGINAL_NPM "$@" \ No newline at end of file diff --git a/packages/aws-cdk/test/integ/test-cli-regression-against-current-code.sh b/packages/aws-cdk/test/integ/test-cli-regression-against-current-code.sh index dc62bab8f698c..ff50aa5f69a2b 100755 --- a/packages/aws-cdk/test/integ/test-cli-regression-against-current-code.sh +++ b/packages/aws-cdk/test/integ/test-cli-regression-against-current-code.sh @@ -1,93 +1,11 @@ #!/bin/bash # # Run our integration tests in regression mode against the -# local code of the framework and CLI. -# -# 1. Download the latest released version of the aws-cdk repo. -# 2. Copy its integration tests directory ((test/integ/cli)) here. -# 3. Run the integration tests from the copied directory. +# candidate version of the framework, which is the one we just packed. # set -euo pipefail integdir=$(cd $(dirname $0) && pwd) -temp_dir=$(mktemp -d) - -function cleanup { - # keep junit file to allow report creation - cp ${integ_under_test}/coverage/junit.xml . - rm -rf ${temp_dir} - rm -rf ${integ_under_test} -} - - -function get_latest_published_version { - - # fetch the latest published version number. - # this is stored in the github releases under the 'latest' tag. - - github_headers="" - if [[ "${GITHUB_TOKEN:-}" != "" ]]; then - github_headers="Authorization: token ${GITHUB_TOKEN}" - fi - - out="${temp_dir}/aws-cdk-release.json" - - curl -Ss --dump-header /dev/null -H "$github_headers" https://api.github.com/repos/aws/aws-cdk/releases/latest > ${out} - latest_version=$(node -p "require('${out}').name") - echo ${latest_version} -} - -function download_repo { - - # we need to download the repo code from GitHub in order to extract - # the integration tests that were present in that version of the repo. - # - # Download just the CLI tarball, which contains the tests. We can't - # use 'npm pack' here to obtain the tarball, as 'npm' commands may - # be redirected to a local Verdaccio. - # - # Rather than introducing another level of indirection to work around - # that, just go to npmjs.com directly. - - # Strip off leading 'v' - version=${1#v} - - out="${temp_dir}/.repo.tar.gz" - - curl -Ssf -L -o ${out} "https://registry.npmjs.org/aws-cdk/-/aws-cdk-${version}.tgz" - tar -zxf ${out} -C ${temp_dir} -} - -# this allows injecting different versions to be treated as the baseline -# of the regression. usually this would just be the latest published version, -# but this can even be a branch name during development and testing. -VERSION_UNDER_TEST=${VERSION_UNDER_TEST:-$(get_latest_published_version)} - -trap cleanup INT EXIT - -echo "Downloading aws-cdk repo version ${VERSION_UNDER_TEST}" -download_repo ${VERSION_UNDER_TEST} - -# remove '/' that is prevelant in our branch names but causes -# bad behvaior when using it as directory names. -sanitized_version=$(sed 's/\//-/g' <<< "${VERSION_UNDER_TEST}") - -# Test must be created in the same directory here because the script files liberally -# include files from '..' and they have to exist. -integ_under_test=${integdir}/cli-backwards-tests-${sanitized_version} -rm -rf ${integ_under_test} -echo "Copying integration tests of version ${VERSION_UNDER_TEST} to ${integ_under_test} (dont worry, its gitignored)" -cp -r ${temp_dir}/package/test/integ/cli ${integ_under_test} - -patch_dir="${integdir}/cli-regression-patches/${VERSION_UNDER_TEST}" -# delete possibly stale junit.xml file -rm -f ${integ_under_test}/junit.xml -if [[ -d "$patch_dir" ]]; then - echo "Hotpatching the tests with files from $patch_dir" >&2 - cp -r "$patch_dir"/* ${integ_under_test} -fi - -echo "Running integration tests of version ${VERSION_UNDER_TEST} from ${integ_under_test}" -set -x +source ${integdir}/test-cli-regression.bash -VERSION_UNDER_TEST=${VERSION_UNDER_TEST} ${integ_under_test}/test.sh "$@" +run_regression_against_framework_version CANDIDATE_VERSION diff --git a/packages/aws-cdk/test/integ/test-cli-regression-against-latest-release.sh b/packages/aws-cdk/test/integ/test-cli-regression-against-latest-release.sh index 6d0133ca06108..8b670eb7b793d 100755 --- a/packages/aws-cdk/test/integ/test-cli-regression-against-latest-release.sh +++ b/packages/aws-cdk/test/integ/test-cli-regression-against-latest-release.sh @@ -1,8 +1,11 @@ #!/bin/bash +# +# Run our integration tests in regression mode against the +# previous version of the framework, relative to the version being packed now. +# set -euo pipefail integdir=$(cd $(dirname $0) && pwd) -# run the regular regression test but pass the env variable that will -# eventually instruct our runners and wrappers to install the framework -# from npmjs.org rather then using the local code. -USE_PUBLISHED_FRAMEWORK_VERSION=True ${integdir}/test-cli-regression-against-current-code.sh "$@" +source ${integdir}/test-cli-regression.bash + +run_regression_against_framework_version PREVIOUS_VERSION diff --git a/packages/aws-cdk/test/integ/test-cli-regression.bash b/packages/aws-cdk/test/integ/test-cli-regression.bash new file mode 100644 index 0000000000000..70ac6645dd61c --- /dev/null +++ b/packages/aws-cdk/test/integ/test-cli-regression.bash @@ -0,0 +1,82 @@ +#!/bin/bash +# +# Helper functions for CLI regression tests. +# +set -euo pipefail +integdir=$(cd $(dirname $0) && pwd) + +# Run our integration tests in regression mode. +# +# 1. Figure out what was the previous (relative to the current candidate) version we published. +# 2. Download the integration tests artifact from that version. +# 2. Copy its integration tests directory ((test/integ/cli)) here. +# 3. Run the integration tests from the copied directory. +# +# Positional Arugments: +# +# 1) Framework version identifier. Which version of the framework should the tests run against. Options are: +# +# - CANDIDATE_VERSION: Use the candidate code, i.e the one being built right now. +# - PREVIOUS_VERSION: Use the previous version code, i.e the published version prior to CANDIDATE_VERSION. +# +function run_regression_against_framework_version() { + + TEST_RUNNER=${TEST_RUNNER:-""} + CANDIDATE_VERSION=${CANDIDATE_VERSION:-""} + + if [ "${TEST_RUNNER}" != "dist" ]; then + echo "Unsupported runner: ${TEST_RUNNER}. Regression tests can only run with the 'dist' runner" + exit 1 + fi + + if [[ ! "${CANDIDATE_VERSION}" =~ "rc" ]]; then + echo "Unexpected CANDIDATE_VERSION: ${CANDIDATE_VERSION}. Must be set to a pre-release version. Did you forget to run './bump-candiate.sh' before packing?" + exit 1 + fi + + SUPPORTED_FRAMEWORK_VERSION_IDENTIFIERS=("CANDIDATE_VERSION PREVIOUS_VERSION") + FRAMEWORK_VERSION_IDENTIFIER=$1 + if [[ ! " ${SUPPORTED_FRAMEWORK_VERSION_IDENTIFIERS[@]} " =~ " ${FRAMEWORK_VERSION_IDENTIFIER} " ]]; then + echo "Unsupported framework version identifier. Should be one of ${SUPPORTED_FRAMEWORK_VERSION_IDENTIFIERS}" + exit 1 + fi + + echo "Fetching previous version for candidate: ${CANDIDATE_VERSION}" + + # we need to explicitly install these deps because this script is executed + # int the test phase, which means the cwd is the packaged dist directory, + # so it doesn't have the dependencies installed from the installation of the package.json + # in the build phase. maybe we should just run npm install on the package.json again? + npm install @octokit/rest@^18.0.6 semver@^7.3.2 make-runnable@^1.3.8 + PREVIOUS_VERSION=$(node ${integdir}/github-helpers.js fetchPreviousVersion ${CANDIDATE_VERSION}) + + echo "Previous version is: ${PREVIOUS_VERSION}" + + temp_dir=$(mktemp -d) + integ_under_test=${integdir}/cli-backwards-tests-${PREVIOUS_VERSION} + + pushd ${temp_dir} + + echo "Downloading aws-cdk ${PREVIOUS_VERSION} tarball from npm" + npm pack aws-cdk@${PREVIOUS_VERSION} + tar -zxvf aws-cdk-${PREVIOUS_VERSION}.tgz + + rm -rf ${integ_under_test} + + echo "Copying integration tests of version ${PREVIOUS_VERSION} to ${integ_under_test} (dont worry, its gitignored)" + cp -r ${temp_dir}/package/test/integ/cli "${integ_under_test}" + + patch_dir="${integdir}/cli-regression-patches/v${PREVIOUS_VERSION}" + # delete possibly stale junit.xml file + rm -f ${integ_under_test}/junit.xml + if [[ -d "$patch_dir" ]]; then + echo "Hotpatching the tests with files from $patch_dir" >&2 + cp -r "$patch_dir"/* ${integ_under_test} + fi + + popd + + # the framework version to use is determined by the caller as the first argument. + # its a variable name indirection. + FRAMEWORK_VERSION=${!FRAMEWORK_VERSION_IDENTIFIER} ${integ_under_test}/test.sh +} diff --git a/yarn.lock b/yarn.lock index affb10c957d4c..d6e7b9cf672d1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2156,6 +2156,18 @@ dependencies: "@octokit/types" "^5.0.0" +"@octokit/core@^3.0.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.1.2.tgz#c937d5f9621b764573068fcd2e5defcc872fd9cc" + integrity sha512-AInOFULmwOa7+NFi9F8DlDkm5qtZVmDQayi7TUgChE3yeIGPq0Y+6cAEXPexQ3Ea+uZy66hKEazR7DJyU+4wfw== + dependencies: + "@octokit/auth-token" "^2.4.0" + "@octokit/graphql" "^4.3.1" + "@octokit/request" "^5.4.0" + "@octokit/types" "^5.0.0" + before-after-hook "^2.1.0" + universal-user-agent "^6.0.0" + "@octokit/endpoint@^6.0.1": version "6.0.6" resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.6.tgz#4f09f2b468976b444742a1d5069f6fa45826d999" @@ -2165,6 +2177,15 @@ is-plain-object "^5.0.0" universal-user-agent "^6.0.0" +"@octokit/graphql@^4.3.1": + version "4.5.6" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.5.6.tgz#708143ba15cf7c1879ed6188266e7f270be805d4" + integrity sha512-Rry+unqKTa3svswT2ZAuqenpLrzJd+JTv89LTeVa5UM/5OX8o4KTkPL7/1ABq4f/ZkELb0XEK/2IEoYwykcLXg== + dependencies: + "@octokit/request" "^5.3.0" + "@octokit/types" "^5.0.0" + universal-user-agent "^6.0.0" + "@octokit/plugin-enterprise-rest@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz#e07896739618dab8da7d4077c658003775f95437" @@ -2177,6 +2198,13 @@ dependencies: "@octokit/types" "^2.0.1" +"@octokit/plugin-paginate-rest@^2.2.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.4.0.tgz#92f951ddc8a1cd505353fa07650752ca25ed7e93" + integrity sha512-YT6Klz3LLH6/nNgi0pheJnUmTFW4kVnxGft+v8Itc41IIcjl7y1C8TatmKQBbCSuTSNFXO5pCENnqg6sjwpJhg== + dependencies: + "@octokit/types" "^5.5.0" + "@octokit/plugin-request-log@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.0.tgz#eef87a431300f6148c39a7f75f8cfeb218b2547e" @@ -2190,6 +2218,14 @@ "@octokit/types" "^2.0.1" deprecation "^2.3.1" +"@octokit/plugin-rest-endpoint-methods@4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.2.0.tgz#c5a0691b3aba5d8b4ef5dffd6af3649608f167ba" + integrity sha512-1/qn1q1C1hGz6W/iEDm9DoyNoG/xdFDt78E3eZ5hHeUfJTLJgyAMdj9chL/cNBHjcjd+FH5aO1x0VCqR2RE0mw== + dependencies: + "@octokit/types" "^5.5.0" + deprecation "^2.3.1" + "@octokit/request-error@^1.0.2": version "1.2.1" resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-1.2.1.tgz#ede0714c773f32347576c25649dc013ae6b31801" @@ -2208,7 +2244,7 @@ deprecation "^2.0.0" once "^1.4.0" -"@octokit/request@^5.2.0": +"@octokit/request@^5.2.0", "@octokit/request@^5.3.0", "@octokit/request@^5.4.0": version "5.4.9" resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.9.tgz#0a46f11b82351b3416d3157261ad9b1558c43365" integrity sha512-CzwVvRyimIM1h2n9pLVYfTDmX9m+KHSgCpqPsY8F1NdEK8IaWqXhSBXsdjOBFZSpEcxNEeg4p0UO9cQ8EnOCLA== @@ -2244,6 +2280,16 @@ once "^1.4.0" universal-user-agent "^4.0.0" +"@octokit/rest@^18.0.6": + version "18.0.6" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.0.6.tgz#76c274f1a68f40741a131768ef483f041e7b98b6" + integrity sha512-ES4lZBKPJMX/yUoQjAZiyFjei9pJ4lTTfb9k7OtYoUzKPDLl/M8jiHqt6qeSauyU4eZGLw0sgP1WiQl9FYeM5w== + dependencies: + "@octokit/core" "^3.0.0" + "@octokit/plugin-paginate-rest" "^2.2.0" + "@octokit/plugin-request-log" "^1.0.0" + "@octokit/plugin-rest-endpoint-methods" "4.2.0" + "@octokit/types@^2.0.0", "@octokit/types@^2.0.1": version "2.16.2" resolved "https://registry.yarnpkg.com/@octokit/types/-/types-2.16.2.tgz#4c5f8da3c6fecf3da1811aef678fda03edac35d2" @@ -2251,7 +2297,7 @@ dependencies: "@types/node" ">= 8" -"@octokit/types@^5.0.0", "@octokit/types@^5.0.1": +"@octokit/types@^5.0.0", "@octokit/types@^5.0.1", "@octokit/types@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@octokit/types/-/types-5.5.0.tgz#e5f06e8db21246ca102aa28444cdb13ae17a139b" integrity sha512-UZ1pErDue6bZNjYOotCNveTXArOMZQFG6hKJfOnGnulVCMcVVi7YIIuuR4WfBhjo7zgpmzn/BkPDnUXtNx+PcQ== @@ -4054,7 +4100,7 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" -before-after-hook@^2.0.0: +before-after-hook@^2.0.0, before-after-hook@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.1.0.tgz#b6c03487f44e24200dd30ca5e6a1979c5d2fb635" integrity sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A== @@ -4073,7 +4119,7 @@ bl@^4.0.3: inherits "^2.0.4" readable-stream "^3.4.0" -bluebird@^3.5.1, bluebird@^3.5.3, bluebird@^3.5.5: +bluebird@^3.5.0, bluebird@^3.5.1, bluebird@^3.5.3, bluebird@^3.5.5: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== @@ -9759,6 +9805,14 @@ make-fetch-happen@^5.0.0: socks-proxy-agent "^4.0.0" ssri "^6.0.0" +make-runnable@^1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/make-runnable/-/make-runnable-1.3.8.tgz#ce547757d776c1f369028dbd937731db9514fe6a" + integrity sha512-8lXEwzB1eLl1pGFx5JZxFbWac6v6bHfRrCn/xDO4Qk9H6DN0QqIsJOquGxz7NkZxedhh7uki1txsbgTci1W/Vw== + dependencies: + bluebird "^3.5.0" + yargs "^16.0.3" + makeerror@1.0.x: version "1.0.11" resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" From fa8d4ed06e56fe3a48adb256e5f463175b6289fb Mon Sep 17 00:00:00 2001 From: Niranjan Jayakar Date: Mon, 12 Oct 2020 15:26:56 +0100 Subject: [PATCH 7/8] chore: enable gitpod prebuilds for PRs from forks (#10824) This enables GitPod pre-builds to be created for PRs that are created from forks. Docs: https://www.gitpod.io/docs/prebuilds/#configure-the-github-app ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .gitpod.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitpod.yml b/.gitpod.yml index 61bf069e34517..2e63da1c1cb98 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -1,4 +1,10 @@ +github: + prebuilds: + pullRequestsFromForks: true + addComment: true + image: jsii/superchain + tasks: - init: yarn build --skip-test --no-bail --skip-prereqs --skip-compat From e90a0a9065f48e09a3e4c8e39403c6beac7cd566 Mon Sep 17 00:00:00 2001 From: Pankaj Yadav Date: Mon, 12 Oct 2020 21:58:19 +0530 Subject: [PATCH 8/8] feat(aws-codepipeline-actions) allows custom role for code commit event role (#10807) With this PR you can use custom role that is assumed by "events.amazonaws.com" to start CodePipeline execution. Fixes #10069 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-codepipeline-actions/README.md | 13 +++++ .../lib/codecommit/source-action.ts | 12 +++- .../test.codecommit-source-action.ts | 57 +++++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-codepipeline-actions/README.md b/packages/@aws-cdk/aws-codepipeline-actions/README.md index 39874e001cb4b..6578110867b33 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/README.md +++ b/packages/@aws-cdk/aws-codepipeline-actions/README.md @@ -42,6 +42,19 @@ pipeline.addStage({ }); ``` +If you want to use existing role which can be used by on commit event rule. +You can specify the role object in eventRole property. + +```ts +const eventRole = iam.Role.fromRoleArn(this, 'Event-role', 'roleArn'); +const sourceAction = new codepipeline_actions.CodeCommitSourceAction({ + actionName: 'CodeCommit', + repository: repo, + output: new codepipeline.Artifact(), + eventRole, +}); +``` + The CodeCommit source action emits variables: ```typescript diff --git a/packages/@aws-cdk/aws-codepipeline-actions/lib/codecommit/source-action.ts b/packages/@aws-cdk/aws-codepipeline-actions/lib/codecommit/source-action.ts index 62e8b83ba3424..8abca963a1943 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/lib/codecommit/source-action.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/lib/codecommit/source-action.ts @@ -77,6 +77,14 @@ export interface CodeCommitSourceActionProps extends codepipeline.CommonAwsActio * The CodeCommit repository. */ readonly repository: codecommit.IRepository; + + /** + * Role to be used by on commit event rule. + * Used only when trigger value is CodeCommitTrigger.EVENTS. + * + * @default a new role will be created. + */ + readonly eventRole?: iam.IRole; } /** @@ -124,7 +132,9 @@ export class CodeCommitSourceAction extends Action { if (createEvent) { const eventId = this.generateEventId(stage); this.props.repository.onCommit(eventId, { - target: new targets.CodePipeline(stage.pipeline), + target: new targets.CodePipeline(stage.pipeline, { + eventRole: this.props.eventRole, + }), branches: [this.branch], }); } diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/codecommit/test.codecommit-source-action.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/codecommit/test.codecommit-source-action.ts index fda7c79dc1800..520af74ba8af2 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/codecommit/test.codecommit-source-action.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/codecommit/test.codecommit-source-action.ts @@ -2,6 +2,7 @@ import { countResources, expect, haveResourceLike, not } from '@aws-cdk/assert'; import * as codebuild from '@aws-cdk/aws-codebuild'; import * as codecommit from '@aws-cdk/aws-codecommit'; import * as codepipeline from '@aws-cdk/aws-codepipeline'; +import * as iam from '@aws-cdk/aws-iam'; import { Stack, Lazy } from '@aws-cdk/core'; import { Test } from 'nodeunit'; import * as cpactions from '../../lib'; @@ -267,6 +268,62 @@ export = { test.done(); }, + + 'uses the role when passed'(test: Test) { + const stack = new Stack(); + + const pipeline = new codepipeline.Pipeline(stack, 'P', { + pipelineName: 'MyPipeline', + }); + + const triggerEventTestRole = new iam.Role(stack, 'Trigger-test-role', { + assumedBy: new iam.ServicePrincipal('events.amazonaws.com'), + }); + triggerEventTestRole.addToPolicy(new iam.PolicyStatement({ + actions: ['codepipeline:StartPipelineExecution'], + resources: [pipeline.pipelineArn], + })); + + const sourceOutput = new codepipeline.Artifact(); + + const sourceAction = new cpactions.CodeCommitSourceAction({ + actionName: 'CodeCommit', + repository: new codecommit.Repository(stack, 'R', { + repositoryName: 'repository', + }), + branch: Lazy.stringValue({ produce: () => 'my-branch' }), + output: sourceOutput, + eventRole: triggerEventTestRole, + }); + + pipeline.addStage({ + stageName: 'Source', + actions: [sourceAction], + }); + + const buildAction = new cpactions.CodeBuildAction({ + actionName: 'Build', + project: new codebuild.PipelineProject(stack, 'CodeBuild'), + input: sourceOutput, + }); + + pipeline.addStage({ + stageName: 'build', + actions: [buildAction], + }); + + expect(stack).to(haveResourceLike('AWS::Events::Rule', { + Targets: [ + { + Arn: stack.resolve(pipeline.pipelineArn), + Id: 'Target0', + RoleArn: stack.resolve(triggerEventTestRole.roleArn), + }, + ], + })); + + test.done(); + }, }, };