diff --git a/CHANGELOG.md b/CHANGELOG.md index 83cca9c58cb2a..c207fc6012f7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,25 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [1.119.0](https://github.com/aws/aws-cdk/compare/v1.118.0...v1.119.0) (2021-08-17) + + +### Features + +* **apigatewayv2:** http api - domain url for a stage ([#15973](https://github.com/aws/aws-cdk/issues/15973)) ([bb5d587](https://github.com/aws/aws-cdk/commit/bb5d5874098b4b1047a29b6697e5e3a65f349ff0)), closes [#15801](https://github.com/aws/aws-cdk/issues/15801) +* **assets:** exclude "cdk.out" from docker assets ([#16034](https://github.com/aws/aws-cdk/issues/16034)) ([84a831a](https://github.com/aws/aws-cdk/commit/84a831ab804244d426321504fc0971d74f6181fd)), closes [#14841](https://github.com/aws/aws-cdk/issues/14841) [#14842](https://github.com/aws/aws-cdk/issues/14842) +* **aws-apigateway:** import existing usage plan ([#15771](https://github.com/aws/aws-cdk/issues/15771)) ([97fc290](https://github.com/aws/aws-cdk/commit/97fc29032c05edb7914c48efee0124be0126a5c4)), closes [#12677](https://github.com/aws/aws-cdk/issues/12677) +* **cfnspec:** cloudformation spec v39.9.0 ([#15987](https://github.com/aws/aws-cdk/issues/15987)) ([e0d6181](https://github.com/aws/aws-cdk/commit/e0d61810ab78f7cab1af53bce82c60790a814f71)) + + +### Bug Fixes + +* **core:** asset bundling fails for non-existent user ([#15313](https://github.com/aws/aws-cdk/issues/15313)) ([bf5882f](https://github.com/aws/aws-cdk/commit/bf5882f8def0676bbfaee7c2ff4fab6bf39df281)), closes [#15415](https://github.com/aws/aws-cdk/issues/15415) +* **ec2:** opaque error when insufficient NAT EIPs are configured ([#16040](https://github.com/aws/aws-cdk/issues/16040)) ([a308cac](https://github.com/aws/aws-cdk/commit/a308cacf1fc48e24311caec246b768ffe6ae9153)), closes [#16039](https://github.com/aws/aws-cdk/issues/16039) +* **events:** cross-account event targets that have a Role are broken ([#15717](https://github.com/aws/aws-cdk/issues/15717)) ([f570c94](https://github.com/aws/aws-cdk/commit/f570c94a7bc99cd5bebc96ee388d152220f9f613)), closes [#15639](https://github.com/aws/aws-cdk/issues/15639) +* **pipelines:** repos with dashes cannot be used as additionalInputs ([#16017](https://github.com/aws/aws-cdk/issues/16017)) ([400a59d](https://github.com/aws/aws-cdk/commit/400a59d19ee63fbd9318da34760b4ed8c9ba99b9)), closes [#15753](https://github.com/aws/aws-cdk/issues/15753) +* **s3-deployment:** BucketDeployment doesn't validate that distribution paths start with "/" ([#15865](https://github.com/aws/aws-cdk/issues/15865)) ([f8d8795](https://github.com/aws/aws-cdk/commit/f8d8795a610c3f49e31967001695caa648730d6d)), closes [#9317](https://github.com/aws/aws-cdk/issues/9317) + ## [1.118.0](https://github.com/aws/aws-cdk/compare/v1.117.0...v1.118.0) (2021-08-10) diff --git a/packages/@aws-cdk/assert-internal/lib/assertions/match-template.ts b/packages/@aws-cdk/assert-internal/lib/assertions/match-template.ts index e668466d12416..b3787446f1aef 100644 --- a/packages/@aws-cdk/assert-internal/lib/assertions/match-template.ts +++ b/packages/@aws-cdk/assert-internal/lib/assertions/match-template.ts @@ -78,16 +78,15 @@ class StackMatchesTemplateAssertion extends Assertion { } for (const change of Object.values(diff.parameters.changes)) { - if (change.isAddition) { return false; } + if (!change.isAddition) { return false; } } for (const change of Object.values(diff.outputs.changes)) { - if (change.isAddition || change.isUpdate) { return false; } + if (!change.isAddition) { return false; } } return true; } - throw new Error(`Unsupported match style: ${this.matchStyle}`); } public get description(): string { diff --git a/packages/@aws-cdk/assert-internal/test/cloud-artifact.ts b/packages/@aws-cdk/assert-internal/test/cloud-artifact.ts new file mode 100644 index 0000000000000..3cdacf6af5134 --- /dev/null +++ b/packages/@aws-cdk/assert-internal/test/cloud-artifact.ts @@ -0,0 +1,29 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import * as cxschema from '@aws-cdk/cloud-assembly-schema'; +import * as cxapi from '@aws-cdk/cx-api'; + +export function mkStack(template: any): cxapi.CloudFormationStackArtifact { + const assembly = new cxapi.CloudAssemblyBuilder(); + assembly.addArtifact('test', { + type: cxschema.ArtifactType.AWS_CLOUDFORMATION_STACK, + environment: cxapi.EnvironmentUtils.format('123456789012', 'bermuda-triangle-1'), + properties: { + templateFile: 'template.json', + }, + }); + + fs.writeFileSync(path.join(assembly.outdir, 'template.json'), JSON.stringify(template)); + return assembly.buildAssembly().getStackByName('test'); +} + +export function mkResource(props: any): cxapi.CloudFormationStackArtifact { + return mkStack({ + Resources: { + SomeResource: { + Type: 'Some::Resource', + Properties: props, + }, + }, + }); +} \ No newline at end of file diff --git a/packages/@aws-cdk/assert-internal/test/have-output.test.ts b/packages/@aws-cdk/assert-internal/test/have-output.test.ts index a92664f4875a5..ffe6212588748 100644 --- a/packages/@aws-cdk/assert-internal/test/have-output.test.ts +++ b/packages/@aws-cdk/assert-internal/test/have-output.test.ts @@ -1,7 +1,6 @@ -import { unlink, writeFileSync } from 'fs'; -import { join } from 'path'; -import * as cxschema from '@aws-cdk/cloud-assembly-schema'; +import { unlink } from 'fs'; import * as cxapi from '@aws-cdk/cx-api'; +import { mkStack } from './cloud-artifact'; import '../jest'; let templateFilePath: string; @@ -180,23 +179,4 @@ afterEach(done => { } else { done(); } -}); - -function mkStack(template: any): cxapi.CloudFormationStackArtifact { - const templateFileName = 'test-have-output-template.json'; - const stackName = 'test-have-output'; - const assembly = new cxapi.CloudAssemblyBuilder(); - - assembly.addArtifact(stackName, { - type: cxschema.ArtifactType.AWS_CLOUDFORMATION_STACK, - environment: cxapi.EnvironmentUtils.format('123456789012', 'bermuda-triangle-1'), - properties: { - templateFile: templateFileName, - }, - }); - - templateFilePath = join(assembly.outdir, templateFileName); - writeFileSync(templateFilePath, JSON.stringify(template)); - - return assembly.buildAssembly().getStackByName(stackName); -} +}); \ No newline at end of file diff --git a/packages/@aws-cdk/assert-internal/test/have-resource.test.ts b/packages/@aws-cdk/assert-internal/test/have-resource.test.ts index 7772e5f7d193f..324e221114df0 100644 --- a/packages/@aws-cdk/assert-internal/test/have-resource.test.ts +++ b/packages/@aws-cdk/assert-internal/test/have-resource.test.ts @@ -1,8 +1,5 @@ -import { writeFileSync } from 'fs'; -import { join } from 'path'; -import * as cxschema from '@aws-cdk/cloud-assembly-schema'; -import * as cxapi from '@aws-cdk/cx-api'; import { ABSENT, arrayWith, exactValue, expect as cdkExpect, haveResource, haveResourceLike, Capture, anything } from '../lib/index'; +import { mkResource, mkStack } from './cloud-artifact'; test('support resource with no properties', () => { const synthStack = mkStack({ @@ -86,7 +83,7 @@ test('haveResource allows to opt in value extension', () => { describe('property absence', () => { test('pass on absence', () => { - const synthStack = mkSomeResource({ + const synthStack = mkResource({ Prop: 'somevalue', }); @@ -96,7 +93,7 @@ describe('property absence', () => { }); test('fail on presence', () => { - const synthStack = mkSomeResource({ + const synthStack = mkResource({ PropA: 3, }); @@ -108,7 +105,7 @@ describe('property absence', () => { }); test('pass on deep absence', () => { - const synthStack = mkSomeResource({ + const synthStack = mkResource({ Deep: { Prop: 'somevalue', }, @@ -123,7 +120,7 @@ describe('property absence', () => { }); test('fail on deep presence', () => { - const synthStack = mkSomeResource({ + const synthStack = mkResource({ Deep: { Prop: 'somevalue', }, @@ -139,7 +136,7 @@ describe('property absence', () => { }); test('can use matcher to test for list element', () => { - const synthStack = mkSomeResource({ + const synthStack = mkResource({ List: [ { Prop: 'distraction' }, { Prop: 'goal' }, @@ -160,7 +157,7 @@ describe('property absence', () => { }); test('arrayContaining must match all elements in any order', () => { - const synthStack = mkSomeResource({ + const synthStack = mkResource({ List: ['a', 'b'], }); @@ -178,7 +175,7 @@ describe('property absence', () => { }); test('exactValue escapes from deep fuzzy matching', () => { - const synthStack = mkSomeResource({ + const synthStack = mkResource({ Deep: { PropA: 'A', PropB: 'B', @@ -216,7 +213,7 @@ describe('property absence', () => { * it. */ test('objectContainingDeep has deep effect through lists', () => { - const synthStack = mkSomeResource({ + const synthStack = mkResource({ List: [ { PropA: 'A', @@ -240,7 +237,7 @@ describe('property absence', () => { }); test('test capturing', () => { - const synthStack = mkSomeResource({ + const synthStack = mkResource({ Prop: 'somevalue', }); @@ -252,28 +249,3 @@ describe('property absence', () => { expect(propValue.capturedValue).toEqual('somevalue'); }); }); - -function mkStack(template: any): cxapi.CloudFormationStackArtifact { - const assembly = new cxapi.CloudAssemblyBuilder(); - assembly.addArtifact('test', { - type: cxschema.ArtifactType.AWS_CLOUDFORMATION_STACK, - environment: cxapi.EnvironmentUtils.format('123456789', 'us-west-2'), - properties: { - templateFile: 'template.json', - }, - }); - - writeFileSync(join(assembly.outdir, 'template.json'), JSON.stringify(template)); - return assembly.buildAssembly().getStackByName('test'); -} - -function mkSomeResource(props: any): cxapi.CloudFormationStackArtifact { - return mkStack({ - Resources: { - SomeResource: { - Type: 'Some::Resource', - Properties: props, - }, - }, - }); -} \ No newline at end of file diff --git a/packages/@aws-cdk/assert-internal/test/match-template.test.ts b/packages/@aws-cdk/assert-internal/test/match-template.test.ts new file mode 100644 index 0000000000000..dc3a4bfe6fb26 --- /dev/null +++ b/packages/@aws-cdk/assert-internal/test/match-template.test.ts @@ -0,0 +1,144 @@ +import { MatchStyle } from '../lib'; +import { mkStack } from './cloud-artifact'; +import '../jest'; + +describe('matchTemplate', () => { + describe('exact match', () => { + test('match on resources', () => { + const stack = mkStack({ + Resources: { + FooResource: { Type: 'Foo::Bar' }, + }, + }); + + expect(stack).toMatchTemplate({ + Resources: { + FooResource: { Type: 'Foo::Bar' }, + }, + }, MatchStyle.EXACT); + + expect(stack).not.toMatchTemplate({ + Resources: { + FooResource: { Type: 'Foo::Baz' }, + }, + }, MatchStyle.EXACT); + }); + + test('match on parameters', () => { + const stack = mkStack({ + Parameters: { + FooParameter: { Type: 'String' }, + }, + }); + expect(stack).toMatchTemplate({ + Parameters: { + FooParameter: { Type: 'String' }, + }, + }, MatchStyle.EXACT); + + expect(stack).not.toMatchTemplate({ + Parameters: { + BarParameter: { Type: 'String' }, + }, + }, MatchStyle.EXACT); + }); + + test('match on outputs', () => { + const stack = mkStack({ + Outputs: { + FooOutput: { Value: 'Foo' }, + }, + }); + + expect(stack).toMatchTemplate({ + Outputs: { + FooOutput: { Value: 'Foo' }, + }, + }, MatchStyle.EXACT); + + expect(stack).not.toMatchTemplate({ + Outputs: { + BarOutput: { Value: 'Bar' }, + }, + }, MatchStyle.EXACT); + + expect(stack).not.toMatchTemplate({ + Outputs: { + FooOutput: { Value: 'Bar' }, + }, + }, MatchStyle.EXACT); + }); + }); + + describe('superset match', () => { + test('match on resources', () => { + const stack = mkStack({ + Resources: { + FooResource: { + Type: 'Foo::Bar', + }, + BazResource: { + Type: 'Foo::Baz', + }, + }, + }); + expect(stack).toMatchTemplate({ + Resources: { + FooResource: { + Type: 'Foo::Bar', + }, + }, + }, MatchStyle.SUPERSET); + }); + + test('match on parameters', () => { + const stack = mkStack({ + Parameters: { + FooParameter: { Type: 'String' }, + BarParameter: { Type: 'String' }, + }, + }); + expect(stack).toMatchTemplate({ + Parameters: { + FooParameter: { Type: 'String' }, + }, + }, MatchStyle.SUPERSET); + + expect(stack).not.toMatchTemplate({ + Parameters: { + FooParameter: { Type: 'String' }, + BarParameter: { Type: 'Number' }, + }, + }, MatchStyle.SUPERSET); + }); + + test('match on outputs', () => { + const stack = mkStack({ + Outputs: { + FooOutput: { Value: 'Foo' }, + BarOutput: { Value: 'Bar' }, + }, + }); + + expect(stack).toMatchTemplate({ + Outputs: { + FooOutput: { Value: 'Foo' }, + }, + }, MatchStyle.SUPERSET); + + expect(stack).not.toMatchTemplate({ + Outputs: { + FooOutput: { Value: 'Foo' }, + BarOutput: { Value: 'Baz' }, + }, + }, MatchStyle.SUPERSET); + + expect(stack).not.toMatchTemplate({ + Outputs: { + FooOutput: { Value: 'Bar' }, + BazOutput: { Value: 'Bar' }, + }, + }, MatchStyle.SUPERSET); + }); + }); +}); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigateway/README.md b/packages/@aws-cdk/aws-apigateway/README.md index 9e33ef2125810..620b728b91435 100644 --- a/packages/@aws-cdk/aws-apigateway/README.md +++ b/packages/@aws-cdk/aws-apigateway/README.md @@ -212,6 +212,12 @@ plan.addApiStage({ }); ``` +Existing usage plans can be imported into a CDK app using its id. + +```ts +const importedUsagePlan = UsagePlan.fromUsagePlanId(stack, 'imported-usage-plan', ''); +``` + The name and value of the API Key can be specified at creation; if not provided, a name and value will be automatically generated by API Gateway. diff --git a/packages/@aws-cdk/aws-apigateway/lib/usage-plan.ts b/packages/@aws-cdk/aws-apigateway/lib/usage-plan.ts index 82f45bc538db6..4db28eb936e80 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/usage-plan.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/usage-plan.ts @@ -1,4 +1,4 @@ -import { FeatureFlags, Lazy, Names, Resource, Token } from '@aws-cdk/core'; +import { FeatureFlags, IResource, Lazy, Names, Resource, Token } from '@aws-cdk/core'; import { APIGATEWAY_USAGEPLANKEY_ORDERINSENSITIVE_ID } from '@aws-cdk/cx-api'; import { Construct } from 'constructs'; import { IApiKey } from './api-key'; @@ -156,35 +156,32 @@ export interface AddApiKeyOptions { readonly overrideLogicalId?: string; } -export class UsagePlan extends Resource { +/** + * A UsagePlan, either managed by this CDK app, or imported. + */ +export interface IUsagePlan extends IResource { /** + * Id of the usage plan * @attribute */ - public readonly usagePlanId: string; - - private readonly apiStages = new Array(); + readonly usagePlanId: string; - constructor(scope: Construct, id: string, props: UsagePlanProps = { }) { - super(scope, id); - let resource: CfnUsagePlan; - - resource = new CfnUsagePlan(this, 'Resource', { - apiStages: Lazy.any({ produce: () => this.renderApiStages(this.apiStages) }), - description: props.description, - quota: this.renderQuota(props), - throttle: this.renderThrottle(props.throttle), - usagePlanName: props.name, - }); - - this.apiStages.push(...(props.apiStages || [])); + /** + * Adds an ApiKey. + * + * @param apiKey the api key to associate with this usage plan + * @param options options that control the behaviour of this method + */ + addApiKey(apiKey: IApiKey, options?: AddApiKeyOptions): void; - this.usagePlanId = resource.ref; +} - // Add ApiKey when - if (props.apiKey) { - this.addApiKey(props.apiKey); - } - } +abstract class UsagePlanBase extends Resource implements IUsagePlan { + /** + * Id of the usage plan + * @attribute + */ + public abstract readonly usagePlanId: string; /** * Adds an ApiKey. @@ -213,6 +210,57 @@ export class UsagePlan extends Resource { } } +} + +export class UsagePlan extends UsagePlanBase { + + /** + * Import an externally defined usage plan using its ARN. + * + * @param scope the construct that will "own" the imported usage plan. + * @param id the id of the imported usage plan in the construct tree. + * @param usagePlanId the id of an existing usage plan. + */ + public static fromUsagePlanId(scope: Construct, id: string, usagePlanId: string): IUsagePlan { + class Import extends UsagePlanBase { + public readonly usagePlanId = usagePlanId; + + constructor() { + super(scope, id); + } + } + return new Import(); + } + + /** + * @attribute + */ + public readonly usagePlanId: string; + + private readonly apiStages = new Array(); + + constructor(scope: Construct, id: string, props: UsagePlanProps = { }) { + super(scope, id); + let resource: CfnUsagePlan; + + resource = new CfnUsagePlan(this, 'Resource', { + apiStages: Lazy.any({ produce: () => this.renderApiStages(this.apiStages) }), + description: props.description, + quota: this.renderQuota(props), + throttle: this.renderThrottle(props.throttle), + usagePlanName: props.name, + }); + + this.apiStages.push(...(props.apiStages || [])); + + this.usagePlanId = resource.ref; + + // Add ApiKey when + if (props.apiKey) { + this.addApiKey(props.apiKey); + } + } + /** * Adds an apiStage. * @param apiStage diff --git a/packages/@aws-cdk/aws-apigateway/test/integ.usage-plan.sharing.expected.json b/packages/@aws-cdk/aws-apigateway/test/integ.usage-plan.sharing.expected.json new file mode 100644 index 0000000000000..9e663c4a1f2c8 --- /dev/null +++ b/packages/@aws-cdk/aws-apigateway/test/integ.usage-plan.sharing.expected.json @@ -0,0 +1,41 @@ +[ + { + "Resources": { + "myusageplan4B391740": { + "Type": "AWS::ApiGateway::UsagePlan" + } + }, + "Outputs": { + "ExportsOutputRefmyusageplan4B391740F6B819BA": { + "Value": { + "Ref": "myusageplan4B391740" + }, + "Export": { + "Name": "test-apigateway-usageplan-create:ExportsOutputRefmyusageplan4B391740F6B819BA" + } + } + } + }, + { + "Resources": { + "myusageplanUsagePlanKeyResourcetestapigatewayusageplanimportmyapikey14CF31667CCB4183": { + "Type": "AWS::ApiGateway::UsagePlanKey", + "Properties": { + "KeyId": { + "Ref": "myapikey5C116C09" + }, + "KeyType": "API_KEY", + "UsagePlanId": { + "Fn::ImportValue": "test-apigateway-usageplan-create:ExportsOutputRefmyusageplan4B391740F6B819BA" + } + } + }, + "myapikey5C116C09": { + "Type": "AWS::ApiGateway::ApiKey", + "Properties": { + "Enabled": true + } + } + } + } +] \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigateway/test/integ.usage-plan.sharing.ts b/packages/@aws-cdk/aws-apigateway/test/integ.usage-plan.sharing.ts new file mode 100644 index 0000000000000..58ffe84234367 --- /dev/null +++ b/packages/@aws-cdk/aws-apigateway/test/integ.usage-plan.sharing.ts @@ -0,0 +1,37 @@ +/// !cdk-integ * +import * as cdk from '@aws-cdk/core'; +import * as apigateway from '../lib'; +import { IUsagePlan } from '../lib'; + +class Create extends cdk.Stack { + public usagePlan: IUsagePlan; + + constructor(scope: cdk.App, id: string) { + super(scope, id); + + this.usagePlan = new apigateway.UsagePlan(this, 'myusageplan'); + } +} + +interface ImportStackProps extends cdk.StackProps { + usagePlan: apigateway.IUsagePlan; +} + +class Import extends cdk.Stack { + constructor(scope: cdk.App, id: string, props: ImportStackProps) { + super(scope, id); + + const usageplan = apigateway.UsagePlan.fromUsagePlanId(this, 'myusageplan', props.usagePlan.usagePlanId); + const apikey = new apigateway.ApiKey(this, 'myapikey'); + usageplan.addApiKey(apikey); + } +} + +const app = new cdk.App(); + +const test = new Create(app, 'test-apigateway-usageplan-create'); +new Import(app, 'test-apigateway-usageplan-import', { + usagePlan: test.usagePlan, +}); + +app.synth(); diff --git a/packages/@aws-cdk/aws-apigateway/test/usage-plan.test.ts b/packages/@aws-cdk/aws-apigateway/test/usage-plan.test.ts index 88df6f2a04506..2cb6e4ed48ed2 100644 --- a/packages/@aws-cdk/aws-apigateway/test/usage-plan.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/usage-plan.test.ts @@ -176,6 +176,26 @@ describe('usage plan', () => { }, ResourcePart.Properties); }); + + test('imported', () => { + // GIVEN + const stack = new cdk.Stack(); + const usagePlan: apigateway.IUsagePlan = apigateway.UsagePlan.fromUsagePlanId(stack, 'my-usage-plan', 'imported-id'); + const apiKey: apigateway.ApiKey = new apigateway.ApiKey(stack, 'my-api-key'); + + // WHEN + usagePlan.addApiKey(apiKey); + + // THEN + expect(stack).toHaveResource('AWS::ApiGateway::UsagePlanKey', { + KeyId: { + Ref: 'myapikey1B052F70', + }, + KeyType: 'API_KEY', + UsagePlanId: 'imported-id', + }, ResourcePart.Properties); + }); + test('multiple keys', () => { // GIVEN const stack = new cdk.Stack(); diff --git a/packages/@aws-cdk/aws-apigatewayv2/README.md b/packages/@aws-cdk/aws-apigatewayv2/README.md index 944e328fc8532..cf0a2ab0552c7 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/README.md +++ b/packages/@aws-cdk/aws-apigatewayv2/README.md @@ -224,6 +224,12 @@ with 3 API mapping resources across different APIs and Stages. | api | beta | `https://${domainName}/bar` | | apiDemo | $default | `https://${domainName}/demo` | +You can retrieve the full domain URL with mapping key using the `domainUrl` property as so - + +```ts +const demoDomainUrl = apiDemo.defaultStage.domainUrl; // returns "https://example.com/demo" +``` + ### Managing access API Gateway supports multiple mechanisms for [controlling and managing access to your HTTP @@ -316,7 +322,7 @@ To retrieve a websocket URL and a callback URL: ```ts const webSocketURL = webSocketStage.url; // wss://${this.api.apiId}.execute-api.${s.region}.${s.urlSuffix}/${urlPath} -const callbackURL = webSocketURL.callbackUrl; +const callbackURL = webSocketStage.callbackUrl; // https://${this.api.apiId}.execute-api.${s.region}.${s.urlSuffix}/${urlPath} ``` diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/common/api-mapping.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/common/api-mapping.ts index adbe3fe3efc2c..f9ebfc89b52ca 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/common/api-mapping.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/common/api-mapping.ts @@ -82,6 +82,11 @@ export class ApiMapping extends Resource implements IApiMapping { */ public readonly mappingKey?: string; + /** + * API domain name + */ + public readonly domainName: IDomainName; + constructor(scope: Construct, id: string, props: ApiMappingProps) { super(scope, id); @@ -121,5 +126,6 @@ export class ApiMapping extends Resource implements IApiMapping { this.apiMappingId = resource.ref; this.mappingKey = props.apiMappingKey; + this.domainName = props.domainName; } } diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/common/base.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/common/base.ts index cd88fceba46b8..006df3f83cd2b 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/common/base.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/common/base.ts @@ -36,6 +36,12 @@ export abstract class StageBase extends Resource implements IStage { public abstract readonly stageName: string; protected abstract readonly baseApi: IApi; + /** + * The created ApiMapping if domain mapping has been added + * @internal + */ + protected _apiMapping?: ApiMapping + /** * The URL to this stage. */ @@ -45,7 +51,10 @@ export abstract class StageBase extends Resource implements IStage { * @internal */ protected _addDomainMapping(domainMapping: DomainMappingOptions) { - new ApiMapping(this, `${domainMapping.domainName}${domainMapping.mappingKey}`, { + if (this._apiMapping) { + throw new Error('Only one ApiMapping allowed per Stage'); + } + this._apiMapping = new ApiMapping(this, `${domainMapping.domainName}${domainMapping.mappingKey}`, { api: this.baseApi, domainName: domainMapping.domainName, stage: this, diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts index e4b6ce929eb2a..094c0131872d8 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts @@ -84,7 +84,7 @@ export interface IHttpApi extends IApi { */ export interface HttpApiProps { /** - * Name for the HTTP API resoruce + * Name for the HTTP API resource * @default - id of the HttpApi construct. */ readonly apiName?: string; @@ -209,7 +209,7 @@ export interface CorsPreflightOptions { } /** - * Options for the Route with Integration resoruce + * Options for the Route with Integration resource */ export interface AddRoutesOptions extends BatchHttpRouteOptions { /** diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/stage.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/stage.ts index 3ad58eea4364b..709f207a04435 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/stage.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/stage.ts @@ -176,4 +176,15 @@ export class HttpStage extends HttpStageBase { const urlPath = this.stageName === DEFAULT_STAGE_NAME ? '' : this.stageName; return `https://${this.api.apiId}.execute-api.${s.region}.${s.urlSuffix}/${urlPath}`; } + + /** + * The custom domain URL to this stage + */ + public get domainUrl(): string { + if (!this._apiMapping) { + throw new Error('domainUrl is not available when no API mapping is associated with the Stage'); + } + + return `https://${this._apiMapping.domainName.name}/${this._apiMapping.mappingKey ?? ''}`; + } } diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/stage.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/stage.test.ts index c56c2ded5d48b..e7a05924719d3 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/stage.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/stage.test.ts @@ -1,8 +1,8 @@ import { Template } from '@aws-cdk/assertions'; +import { Certificate } from '@aws-cdk/aws-certificatemanager'; import { Metric } from '@aws-cdk/aws-cloudwatch'; import { Stack } from '@aws-cdk/core'; -import { HttpApi, HttpStage } from '../../lib'; - +import { DomainName, HttpApi, HttpStage } from '../../lib'; describe('HttpStage', () => { test('default', () => { @@ -116,4 +116,47 @@ describe('HttpStage', () => { const metricNames = metrics.map(m => m.metricName); expect(metricNames).toEqual(['4xx', '5xx', 'DataProcessed', 'Latency', 'IntegrationLatency', 'Count']); }); +}); + +describe('HttpStage with domain mapping', () => { + const domainName = 'example.com'; + const certArn = 'arn:aws:acm:us-east-1:111111111111:certificate'; + + test('domainUrl returns the correct path', () => { + const stack = new Stack(); + const api = new HttpApi(stack, 'Api', { + createDefaultStage: false, + }); + + const dn = new DomainName(stack, 'DN', { + domainName, + certificate: Certificate.fromCertificateArn(stack, 'cert', certArn), + }); + + const stage = new HttpStage(stack, 'DefaultStage', { + httpApi: api, + domainMapping: { + domainName: dn, + }, + }); + + expect(stage.domainUrl).toBe(`https://${domainName}/`); + }); + + test('domainUrl throws error if domainMapping is not configured', () => { + const stack = new Stack(); + const api = new HttpApi(stack, 'Api', { + createDefaultStage: false, + }); + + const stage = new HttpStage(stack, 'DefaultStage', { + httpApi: api, + }); + + const t = () => { + stage.domainUrl; + }; + + expect(t).toThrow(/domainUrl is not available when no API mapping is associated with the Stage/); + }); }); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.docker-asset.lit.expected.json b/packages/@aws-cdk/aws-codebuild/test/integ.docker-asset.lit.expected.json index 043ed7ee5642d..e8574d481b17f 100644 --- a/packages/@aws-cdk/aws-codebuild/test/integ.docker-asset.lit.expected.json +++ b/packages/@aws-cdk/aws-codebuild/test/integ.docker-asset.lit.expected.json @@ -146,7 +146,7 @@ { "Ref": "AWS::URLSuffix" }, - "/aws-cdk/assets:2564b8c8e3f9e82a6394872a4e555c4d0c06ab6f47b9aca360c22c9ed622487c" + "/aws-cdk/assets:4af07cfea2e112710555eb86325bfd4d7d4b97e4fa9f1bf6c053c72f992c7fe5" ] ] }, 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 8708410dfff18..136bf9bba9dc5 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 @@ -106,6 +106,15 @@ export interface CodeCommitSourceActionProps extends codepipeline.CommonAwsActio /** * CodePipeline Source that is provided by an AWS CodeCommit repository. + * + * If the CodeCommit repository is in a different account, you must use + * `CodeCommitTrigger.EVENTS` to trigger the pipeline. + * + * (That is because the Pipeline structure normally only has a `RepositoryName` + * field, and that is not enough for the pipeline to locate the repository's + * source account. However, if the pipeline is triggered via an EventBridge + * event, the event itself has the full repository ARN in there, allowing the + * pipeline to locate the repository). */ export class CodeCommitSourceAction extends Action { /** diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/codecommit/codecommit-source-action.test.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/codecommit/codecommit-source-action.test.ts index 8c668be7f47d8..da226dd37a74d 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/codecommit/codecommit-source-action.test.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/codecommit/codecommit-source-action.test.ts @@ -1,4 +1,4 @@ -import { arrayWith, countResources, expect, haveResourceLike, not, objectLike } from '@aws-cdk/assert-internal'; +import { ABSENT, arrayWith, countResources, expect, haveResourceLike, not, objectLike } from '@aws-cdk/assert-internal'; import * as codebuild from '@aws-cdk/aws-codebuild'; import * as codecommit from '@aws-cdk/aws-codecommit'; import * as codepipeline from '@aws-cdk/aws-codepipeline'; @@ -38,6 +38,91 @@ nodeunitShim({ test.done(); }, + 'cross-account CodeCommit Repository Source does not use target role in source stack'(test: Test) { + // Test for https://github.com/aws/aws-cdk/issues/15639 + const app = new App(); + const sourceStack = new Stack(app, 'SourceStack', { env: { account: '1234', region: 'north-pole' } }); + const targetStack = new Stack(app, 'TargetStack', { env: { account: '5678', region: 'north-pole' } }); + + const repo = new codecommit.Repository(sourceStack, 'MyRepo', { + repositoryName: 'my-repo', + }); + + const sourceOutput = new codepipeline.Artifact(); + new codepipeline.Pipeline(targetStack, 'MyPipeline', { + stages: [ + { + stageName: 'Source', + actions: [ + new cpactions.CodeCommitSourceAction({ actionName: 'Source', repository: repo, output: sourceOutput }), + ], + }, + { + stageName: 'Build', + actions: [ + new cpactions.CodeBuildAction({ actionName: 'Build', project: new codebuild.PipelineProject(targetStack, 'MyProject'), input: sourceOutput }), + ], + }, + ], + }); + + // THEN - creates a Rule in the source stack targeting the pipeline stack's event bus using a generated role + expect(sourceStack).to(haveResourceLike('AWS::Events::Rule', { + EventPattern: { + source: ['aws.codecommit'], + resources: [ + { 'Fn::GetAtt': ['MyRepoF4F48043', 'Arn'] }, + ], + }, + Targets: [{ + RoleARN: ABSENT, + Arn: { + 'Fn::Join': ['', [ + 'arn:', + { 'Ref': 'AWS::Partition' }, + ':events:north-pole:5678:event-bus/default', + ]], + }, + }], + })); + + // THEN - creates a Rule in the pipeline stack using the role to start the pipeline + expect(targetStack).to(haveResourceLike('AWS::Events::Rule', { + 'EventPattern': { + 'source': [ + 'aws.codecommit', + ], + 'resources': [ + { + 'Fn::Join': [ + '', + [ + 'arn:', + { 'Ref': 'AWS::Partition' }, + ':codecommit:north-pole:1234:my-repo', + ], + ], + }, + ], + }, + 'Targets': [ + { + 'Arn': { + 'Fn::Join': ['', [ + 'arn:', + { 'Ref': 'AWS::Partition' }, + ':codepipeline:north-pole:5678:', + { 'Ref': 'MyPipelineAED38ECF' }, + ]], + }, + 'RoleArn': { 'Fn::GetAtt': ['MyPipelineEventsRoleFAB99F32', 'Arn'] }, + }, + ], + })); + + test.done(); + }, + 'does not poll for source changes and uses Events for CodeCommitTrigger.EVENTS'(test: Test) { const stack = new Stack(); diff --git a/packages/@aws-cdk/aws-ec2/lib/nat.ts b/packages/@aws-cdk/aws-ec2/lib/nat.ts index e991e732f38b0..4743799762b51 100644 --- a/packages/@aws-cdk/aws-ec2/lib/nat.ts +++ b/packages/@aws-cdk/aws-ec2/lib/nat.ts @@ -222,6 +222,14 @@ class NatGatewayProvider extends NatProvider { } public configureNat(options: ConfigureNatOptions) { + if ( + this.props.eipAllocationIds != null + && !Token.isUnresolved(this.props.eipAllocationIds) + && this.props.eipAllocationIds.length < options.natSubnets.length + ) { + throw new Error(`Not enough NAT gateway EIP allocation IDs (${this.props.eipAllocationIds.length} provided) for the requested subnet count (${options.natSubnets.length} needed).`); + } + // Create the NAT gateways let i = 0; for (const sub of options.natSubnets) { @@ -413,4 +421,4 @@ function pickN(i: number, xs: string[]) { } return xs[i]; -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-ec2/test/client-vpn-authorization-rule.test.ts b/packages/@aws-cdk/aws-ec2/test/client-vpn-authorization-rule.test.ts index 6c8911d90a06d..5390eb43adb85 100644 --- a/packages/@aws-cdk/aws-ec2/test/client-vpn-authorization-rule.test.ts +++ b/packages/@aws-cdk/aws-ec2/test/client-vpn-authorization-rule.test.ts @@ -1,11 +1,16 @@ import '@aws-cdk/assert-internal/jest'; -import { Stack } from '@aws-cdk/core'; +import { App, Stack } from '@aws-cdk/core'; import { Connections, IClientVpnEndpoint } from '../lib'; import { ClientVpnAuthorizationRule } from '../lib/client-vpn-authorization-rule'; let stack: Stack; beforeEach(() => { - stack = new Stack(); + const app = new App({ + context: { + '@aws-cdk/core:newStyleStackSynthesis': false, + }, + }); + stack = new Stack(app); }); describe('ClientVpnAuthorizationRule constructor', () => { diff --git a/packages/@aws-cdk/aws-ec2/test/client-vpn-route.test.ts b/packages/@aws-cdk/aws-ec2/test/client-vpn-route.test.ts index 42f094ab4bb9e..fde37f49335ae 100644 --- a/packages/@aws-cdk/aws-ec2/test/client-vpn-route.test.ts +++ b/packages/@aws-cdk/aws-ec2/test/client-vpn-route.test.ts @@ -1,6 +1,6 @@ import '@aws-cdk/assert-internal/jest'; import { SamlMetadataDocument, SamlProvider } from '@aws-cdk/aws-iam'; -import { Stack } from '@aws-cdk/core'; +import { App, Stack } from '@aws-cdk/core'; import * as ec2 from '../lib'; import { ClientVpnRoute, @@ -11,7 +11,12 @@ import { let stack: Stack; let vpc: ec2.IVpc; beforeEach(() => { - stack = new Stack(); + const app = new App({ + context: { + '@aws-cdk/core:newStyleStackSynthesis': false, + }, + }); + stack = new Stack(app); vpc = new ec2.Vpc(stack, 'Vpc'); }); diff --git a/packages/@aws-cdk/aws-ec2/test/vpc.test.ts b/packages/@aws-cdk/aws-ec2/test/vpc.test.ts index dd22090584a25..d65bc016271be 100644 --- a/packages/@aws-cdk/aws-ec2/test/vpc.test.ts +++ b/packages/@aws-cdk/aws-ec2/test/vpc.test.ts @@ -35,7 +35,7 @@ nodeunitShim({ 'vpc.vpcId returns a token to the VPC ID'(test: Test) { const stack = getTestStack(); const vpc = new Vpc(stack, 'TheVPC'); - test.deepEqual(stack.resolve(vpc.vpcId), { Ref: 'TheVPC92636AB0' } ); + test.deepEqual(stack.resolve(vpc.vpcId), { Ref: 'TheVPC92636AB0' }); test.done(); }, @@ -55,11 +55,11 @@ nodeunitShim({ new Vpc(stack, 'TheVPC'); cdkExpect(stack).to( haveResource('AWS::EC2::VPC', - hasTags( [{ Key: 'Name', Value: 'TestStack/TheVPC' }])), + hasTags([{ Key: 'Name', Value: 'TestStack/TheVPC' }])), ); cdkExpect(stack).to( haveResource('AWS::EC2::InternetGateway', - hasTags( [{ Key: 'Name', Value: 'TestStack/TheVPC' }])), + hasTags([{ Key: 'Name', Value: 'TestStack/TheVPC' }])), ); test.done(); }, @@ -86,7 +86,7 @@ nodeunitShim({ 'dns getters correspond to CFN properties': (() => { - const tests: any = { }; + const tests: any = {}; const inputs = [ { dnsSupport: false, dnsHostnames: false }, @@ -173,8 +173,7 @@ nodeunitShim({ }, ], }); - cdkExpect(stack).to(countResources('AWS::EC2::InternetGateway', 1)) - ; + cdkExpect(stack).to(countResources('AWS::EC2::InternetGateway', 1)); cdkExpect(stack).notTo(haveResource('AWS::EC2::NatGateway')); test.done(); }, @@ -223,7 +222,7 @@ nodeunitShim({ 'with no subnets defined, the VPC should have an IGW, and a NAT Gateway per AZ'(test: Test) { const stack = getTestStack(); const zones = stack.availabilityZones.length; - new Vpc(stack, 'TheVPC', { }); + new Vpc(stack, 'TheVPC', {}); cdkExpect(stack).to(countResources('AWS::EC2::InternetGateway', 1)); cdkExpect(stack).to(countResources('AWS::EC2::NatGateway', zones)); test.done(); @@ -251,7 +250,7 @@ nodeunitShim({ cdkExpect(stack).to(haveResource('AWS::EC2::InternetGateway')); cdkExpect(stack).to(haveResourceLike('AWS::EC2::Route', { DestinationCidrBlock: '8.8.8.8/32', - GatewayId: { }, + GatewayId: {}, })); test.done(); }, @@ -457,7 +456,7 @@ nodeunitShim({ } cdkExpect(stack).to(haveResourceLike('AWS::EC2::Route', { DestinationCidrBlock: '0.0.0.0/0', - NatGatewayId: { }, + NatGatewayId: {}, })); test.done(); @@ -475,7 +474,7 @@ nodeunitShim({ } cdkExpect(stack).to(haveResourceLike('AWS::EC2::Route', { DestinationCidrBlock: '0.0.0.0/0', - NatGatewayId: { }, + NatGatewayId: {}, })); test.done(); }, @@ -489,7 +488,7 @@ nodeunitShim({ cdkExpect(stack).to(countResources('AWS::EC2::NatGateway', 1)); cdkExpect(stack).to(haveResourceLike('AWS::EC2::Route', { DestinationCidrBlock: '0.0.0.0/0', - NatGatewayId: { }, + NatGatewayId: {}, })); test.done(); }, @@ -873,6 +872,31 @@ nodeunitShim({ test.done(); }, + 'NAT gateway provider with insufficient EIP allocations'(test: Test) { + const stack = new Stack(); + const natGatewayProvider = NatProvider.gateway({ eipAllocationIds: ['a'] }); + expect(() => new Vpc(stack, 'VpcNetwork', { natGatewayProvider })) + .toThrow(/Not enough NAT gateway EIP allocation IDs \(1 provided\) for the requested subnet count \(\d+ needed\)/); + + test.done(); + }, + + 'NAT gateway provider with token EIP allocations'(test: Test) { + const stack = new Stack(); + const eipAllocationIds = Fn.split(',', Fn.importValue('myVpcId')); + const natGatewayProvider = NatProvider.gateway({ eipAllocationIds }); + new Vpc(stack, 'VpcNetwork', { natGatewayProvider }); + + cdkExpect(stack).to(haveResource('AWS::EC2::NatGateway', { + AllocationId: stack.resolve(Fn.select(0, eipAllocationIds)), + })); + cdkExpect(stack).to(haveResource('AWS::EC2::NatGateway', { + AllocationId: stack.resolve(Fn.select(1, eipAllocationIds)), + })); + + test.done(); + }, + 'Can add an IPv6 route'(test: Test) { // GIVEN const stack = getTestStack(); diff --git a/packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts b/packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts index 928fb286cb3a0..7003fc6931826 100644 --- a/packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts +++ b/packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts @@ -1,7 +1,7 @@ import * as fs from 'fs'; import * as path from 'path'; import * as ecr from '@aws-cdk/aws-ecr'; -import { Annotations, AssetStaging, FeatureFlags, FileFingerprintOptions, IgnoreMode, Stack, SymlinkFollowMode, Token } from '@aws-cdk/core'; +import { Annotations, AssetStaging, FeatureFlags, FileFingerprintOptions, IgnoreMode, Stack, SymlinkFollowMode, Token, Stage } from '@aws-cdk/core'; import * as cxapi from '@aws-cdk/cx-api'; import { Construct } from 'constructs'; @@ -62,6 +62,8 @@ export interface DockerImageAssetOptions extends FingerprintOptions, FileFingerp export interface DockerImageAssetProps extends DockerImageAssetOptions { /** * The directory where the Dockerfile is stored + * + * Any directory inside with a name that matches the CDK output folder (cdk.out by default) will be excluded from the asset */ readonly directory: string; } @@ -138,13 +140,16 @@ export class DockerImageAsset extends CoreConstruct implements IAsset { // Ensure the Dockerfile is included no matter what. exclude.push('!' + path.basename(file)); + // Ensure the cdk.out folder is not included to avoid infinite loops. + const cdkout = Stage.of(this)?.outdir ?? 'cdk.out'; + exclude.push(cdkout); if (props.repositoryName) { Annotations.of(this).addWarning('DockerImageAsset.repositoryName is deprecated. Override "core.Stack.addDockerImageAsset" to control asset locations'); } // include build context in "extra" so it will impact the hash - const extraHash: { [field: string]: any } = { }; + const extraHash: { [field: string]: any } = {}; if (props.extraHash) { extraHash.user = props.extraHash; } if (props.buildArgs) { extraHash.buildArgs = props.buildArgs; } if (props.target) { extraHash.target = props.target; } diff --git a/packages/@aws-cdk/aws-ecr-assets/test/image-asset.test.ts b/packages/@aws-cdk/aws-ecr-assets/test/image-asset.test.ts index 76dac04f66852..e156209458878 100644 --- a/packages/@aws-cdk/aws-ecr-assets/test/image-asset.test.ts +++ b/packages/@aws-cdk/aws-ecr-assets/test/image-asset.test.ts @@ -10,7 +10,7 @@ import { DockerImageAsset } from '../lib'; /* eslint-disable quote-props */ -const DEMO_IMAGE_ASSET_HASH = 'b2c69bfbfe983b634456574587443159b3b7258849856a118ad3d2772238f1a5'; +const DEMO_IMAGE_ASSET_HASH = '8c1d9ca9f5d37b1c4870c13a9f855301bb42c1848dbcdd5edc8fe2c6c7261d48'; const flags = { [cxapi.DOCKER_IGNORE_SUPPORT]: true }; @@ -29,11 +29,11 @@ describe('image asset', () => { expect(artifact.assets).toEqual([ { repositoryName: 'aws-cdk/assets', - imageTag: 'b2c69bfbfe983b634456574587443159b3b7258849856a118ad3d2772238f1a5', - id: 'b2c69bfbfe983b634456574587443159b3b7258849856a118ad3d2772238f1a5', + imageTag: '8c1d9ca9f5d37b1c4870c13a9f855301bb42c1848dbcdd5edc8fe2c6c7261d48', + id: '8c1d9ca9f5d37b1c4870c13a9f855301bb42c1848dbcdd5edc8fe2c6c7261d48', packaging: 'container-image', - path: 'asset.b2c69bfbfe983b634456574587443159b3b7258849856a118ad3d2772238f1a5', - sourceHash: 'b2c69bfbfe983b634456574587443159b3b7258849856a118ad3d2772238f1a5', + path: 'asset.8c1d9ca9f5d37b1c4870c13a9f855301bb42c1848dbcdd5edc8fe2c6c7261d48', + sourceHash: '8c1d9ca9f5d37b1c4870c13a9f855301bb42c1848dbcdd5edc8fe2c6c7261d48', }, ]); @@ -283,13 +283,13 @@ describe('image asset', () => { const asset6 = new DockerImageAsset(stack, 'Asset6', { directory, extraHash: 'random-extra' }); const asset7 = new DockerImageAsset(stack, 'Asset7', { directory, repositoryName: 'foo' }); - expect(asset1.assetHash).toEqual('ab01ecd4419f59e1ec0ac9e57a60dbb653be68a29af0223fa8cb24b4b747bc73'); - expect(asset2.assetHash).toEqual('7fb12f6148098e3f5c56c788a865d2af689125ead403b795fe6a262ec34384b3'); - expect(asset3.assetHash).toEqual('fc3b6d802ba198ba2ee55079dbef27682bcd1288d5849eb5bbd5cd69038359b3'); - expect(asset4.assetHash).toEqual('30439ea6dfeb4ddfd9175097286895c78393ef52a78c68f92db08abc4513cad6'); - expect(asset5.assetHash).toEqual('5775170880e26ba31799745241b90d4340c674bb3b1c01d758e416ee3f1c386f'); - expect(asset6.assetHash).toEqual('ba82fd351a4d3e3f5c5d948b9948e7e829badc3da90f97e00bb7724afbeacfd4'); - expect(asset7.assetHash).toEqual('26ec194928431cab6ec5af24ea9f01af2cf7b20e361128b07b2a7405d2951f95'); + expect(asset1.assetHash).toEqual('365b5d951fc5f725f78093a07e3e1cc7819b4cbe582ca71a4c344752c23bf409'); + expect(asset2.assetHash).toEqual('9560a36f786f317c5e1abb986b58269b2453ed1cab16c36fd9b76646c837078c'); + expect(asset3.assetHash).toEqual('4f4e16f5b0cfab21be4298a04b20f62f63cd91a649ef4620d6d3c948d29f3cb4'); + expect(asset4.assetHash).toEqual('72b961f96e358b8dad935719cfc2704c3d14a46434871825ac81e3b94caa4853'); + expect(asset5.assetHash).toEqual('c23d34b3a1dac5a80c42e8fa6c88a0ac697eb709a6f36ebdb6e36ee8c75edc75'); + expect(asset6.assetHash).toEqual('7e950a9b08c58d371c1658e04d377c0ec59d89a47fc245a86a50525b36a8949b'); + expect(asset7.assetHash).toEqual('313dd1f45a939b77fa8a4eb7780190aa7a20a40c95f503eca9e099186643d717'); }); }); diff --git a/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.expected.json b/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.expected.json index ad86ae9cd34da..e2aefa9185424 100644 --- a/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.expected.json +++ b/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.expected.json @@ -70,7 +70,7 @@ { "Ref": "AWS::URLSuffix" }, - "/aws-cdk/assets:b2c69bfbfe983b634456574587443159b3b7258849856a118ad3d2772238f1a5" + "/aws-cdk/assets:8c1d9ca9f5d37b1c4870c13a9f855301bb42c1848dbcdd5edc8fe2c6c7261d48" ] ] } diff --git a/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.expected.json b/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.expected.json index f86d0e5fea7ab..7e23b25796d4e 100644 --- a/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.expected.json +++ b/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.expected.json @@ -17,7 +17,7 @@ }, "/", { - "Ref": "AssetParameters41589ef1a760129e41441e85e58fe02db5f019ed532b8a4a20729f3245b0593bS3BucketFB15A731" + "Ref": "AssetParameters08bf71a5b9aa57c58cc7510137ed079260aac01394d01f4c29a9778ac890b816S3Bucket40925BAC" }, "/", { @@ -27,7 +27,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters41589ef1a760129e41441e85e58fe02db5f019ed532b8a4a20729f3245b0593bS3VersionKeyF38F9C9A" + "Ref": "AssetParameters08bf71a5b9aa57c58cc7510137ed079260aac01394d01f4c29a9778ac890b816S3VersionKey30F5671B" } ] } @@ -40,7 +40,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters41589ef1a760129e41441e85e58fe02db5f019ed532b8a4a20729f3245b0593bS3VersionKeyF38F9C9A" + "Ref": "AssetParameters08bf71a5b9aa57c58cc7510137ed079260aac01394d01f4c29a9778ac890b816S3VersionKey30F5671B" } ] } @@ -55,17 +55,17 @@ } }, "Parameters": { - "AssetParameters41589ef1a760129e41441e85e58fe02db5f019ed532b8a4a20729f3245b0593bS3BucketFB15A731": { + "AssetParameters08bf71a5b9aa57c58cc7510137ed079260aac01394d01f4c29a9778ac890b816S3Bucket40925BAC": { "Type": "String", - "Description": "S3 bucket for asset \"41589ef1a760129e41441e85e58fe02db5f019ed532b8a4a20729f3245b0593b\"" + "Description": "S3 bucket for asset \"08bf71a5b9aa57c58cc7510137ed079260aac01394d01f4c29a9778ac890b816\"" }, - "AssetParameters41589ef1a760129e41441e85e58fe02db5f019ed532b8a4a20729f3245b0593bS3VersionKeyF38F9C9A": { + "AssetParameters08bf71a5b9aa57c58cc7510137ed079260aac01394d01f4c29a9778ac890b816S3VersionKey30F5671B": { "Type": "String", - "Description": "S3 key for asset version \"41589ef1a760129e41441e85e58fe02db5f019ed532b8a4a20729f3245b0593b\"" + "Description": "S3 key for asset version \"08bf71a5b9aa57c58cc7510137ed079260aac01394d01f4c29a9778ac890b816\"" }, - "AssetParameters41589ef1a760129e41441e85e58fe02db5f019ed532b8a4a20729f3245b0593bArtifactHashEC633CC8": { + "AssetParameters08bf71a5b9aa57c58cc7510137ed079260aac01394d01f4c29a9778ac890b816ArtifactHash98F3F6F7": { "Type": "String", - "Description": "Artifact hash for asset \"41589ef1a760129e41441e85e58fe02db5f019ed532b8a4a20729f3245b0593b\"" + "Description": "Artifact hash for asset \"08bf71a5b9aa57c58cc7510137ed079260aac01394d01f4c29a9778ac890b816\"" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.multiple-application-load-balanced-ecs-service.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.multiple-application-load-balanced-ecs-service.expected.json index 0412778adb2e8..5ce81fe00ad59 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.multiple-application-load-balanced-ecs-service.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.multiple-application-load-balanced-ecs-service.expected.json @@ -95,15 +95,15 @@ "VpcPublicSubnet1NATGateway4D7517AA": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, "AllocationId": { "Fn::GetAtt": [ "VpcPublicSubnet1EIPD7E02669", "AllocationId" ] }, - "SubnetId": { - "Ref": "VpcPublicSubnet1Subnet5C2D37C4" - }, "Tags": [ { "Key": "Name", @@ -192,15 +192,15 @@ "VpcPublicSubnet2NATGateway9182C01D": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + }, "AllocationId": { "Fn::GetAtt": [ "VpcPublicSubnet2EIP3C605A87", "AllocationId" ] }, - "SubnetId": { - "Ref": "VpcPublicSubnet2Subnet691E08A3" - }, "Tags": [ { "Key": "Name", diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.scheduled-ecs-task.lit.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.scheduled-ecs-task.lit.expected.json index e3afaddbeb7c6..e96ad6a2bbad7 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.scheduled-ecs-task.lit.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.scheduled-ecs-task.lit.expected.json @@ -95,15 +95,15 @@ "VpcPublicSubnet1NATGateway4D7517AA": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, "AllocationId": { "Fn::GetAtt": [ "VpcPublicSubnet1EIPD7E02669", "AllocationId" ] }, - "SubnetId": { - "Ref": "VpcPublicSubnet1Subnet5C2D37C4" - }, "Tags": [ { "Key": "Name", diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.alb-fargate-service-https.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.alb-fargate-service-https.expected.json index 5a67a969707c8..76713f6d4ef64 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.alb-fargate-service-https.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.alb-fargate-service-https.expected.json @@ -95,15 +95,15 @@ "VpcPublicSubnet1NATGateway4D7517AA": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, "AllocationId": { "Fn::GetAtt": [ "VpcPublicSubnet1EIPD7E02669", "AllocationId" ] }, - "SubnetId": { - "Ref": "VpcPublicSubnet1Subnet5C2D37C4" - }, "Tags": [ { "Key": "Name", @@ -192,15 +192,15 @@ "VpcPublicSubnet2NATGateway9182C01D": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + }, "AllocationId": { "Fn::GetAtt": [ "VpcPublicSubnet2EIP3C605A87", "AllocationId" ] }, - "SubnetId": { - "Ref": "VpcPublicSubnet2Subnet691E08A3" - }, "Tags": [ { "Key": "Name", @@ -766,4 +766,4 @@ } } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.asset-image.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.asset-image.expected.json index 4fdde5753fdf6..dc589f5d6c48f 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.asset-image.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.asset-image.expected.json @@ -95,15 +95,15 @@ "VpcPublicSubnet1NATGateway4D7517AA": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, "AllocationId": { "Fn::GetAtt": [ "VpcPublicSubnet1EIPD7E02669", "AllocationId" ] }, - "SubnetId": { - "Ref": "VpcPublicSubnet1Subnet5C2D37C4" - }, "Tags": [ { "Key": "Name", @@ -192,15 +192,15 @@ "VpcPublicSubnet2NATGateway9182C01D": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + }, "AllocationId": { "Fn::GetAtt": [ "VpcPublicSubnet2EIP3C605A87", "AllocationId" ] }, - "SubnetId": { - "Ref": "VpcPublicSubnet2Subnet691E08A3" - }, "Tags": [ { "Key": "Name", @@ -497,7 +497,7 @@ { "Ref": "AWS::URLSuffix" }, - "/aws-cdk/assets:b2c69bfbfe983b634456574587443159b3b7258849856a118ad3d2772238f1a5" + "/aws-cdk/assets:8c1d9ca9f5d37b1c4870c13a9f855301bb42c1848dbcdd5edc8fe2c6c7261d48" ] ] }, diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.circuit-breaker-load-balanced-fargate-service.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.circuit-breaker-load-balanced-fargate-service.expected.json index 19af7964b18be..24b4bb012e384 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.circuit-breaker-load-balanced-fargate-service.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.circuit-breaker-load-balanced-fargate-service.expected.json @@ -95,15 +95,15 @@ "VpcPublicSubnet1NATGateway4D7517AA": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, "AllocationId": { "Fn::GetAtt": [ "VpcPublicSubnet1EIPD7E02669", "AllocationId" ] }, - "SubnetId": { - "Ref": "VpcPublicSubnet1Subnet5C2D37C4" - }, "Tags": [ { "Key": "Name", @@ -192,15 +192,15 @@ "VpcPublicSubnet2NATGateway9182C01D": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + }, "AllocationId": { "Fn::GetAtt": [ "VpcPublicSubnet2EIP3C605A87", "AllocationId" ] }, - "SubnetId": { - "Ref": "VpcPublicSubnet2Subnet691E08A3" - }, "Tags": [ { "Key": "Name", @@ -684,19 +684,19 @@ }, "myServiceServiceURL1258C56B": { "Value": { - "Fn::Join":[ - "", - [ - "http://", - { - "Fn::GetAtt": [ - "myServiceLB168895E1", - "DNSName" - ] - } - ] + "Fn::Join": [ + "", + [ + "http://", + { + "Fn::GetAtt": [ + "myServiceLB168895E1", + "DNSName" + ] + } + ] ] } } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.circuit-breaker-queue-processing-fargate-service.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.circuit-breaker-queue-processing-fargate-service.expected.json index 3b8fad89fcf97..74e53d75a507a 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.circuit-breaker-queue-processing-fargate-service.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.circuit-breaker-queue-processing-fargate-service.expected.json @@ -95,15 +95,15 @@ "VPCPublicSubnet1NATGatewayE0556630": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + }, "AllocationId": { "Fn::GetAtt": [ "VPCPublicSubnet1EIP6AD938E8", "AllocationId" ] }, - "SubnetId": { - "Ref": "VPCPublicSubnet1SubnetB4246D30" - }, "Tags": [ { "Key": "Name", @@ -192,15 +192,15 @@ "VPCPublicSubnet2NATGateway3C070193": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VPCPublicSubnet2Subnet74179F39" + }, "AllocationId": { "Fn::GetAtt": [ "VPCPublicSubnet2EIP4947BC00", "AllocationId" ] }, - "SubnetId": { - "Ref": "VPCPublicSubnet2Subnet74179F39" - }, "Tags": [ { "Key": "Name", @@ -460,7 +460,7 @@ { "Ref": "AWS::URLSuffix" }, - "/aws-cdk/assets:3a8ba3ad06ed212b075efa3157fb407649c5996812bc64eeb5209e220aab4be5" + "/aws-cdk/assets:d6b024485c22795b5f5379edcd5cd6485f5bec6eb80bd072b20526f8eb2e0c64" ] ] }, @@ -590,7 +590,7 @@ "Cluster": { "Ref": "EcsDefaultClusterMnL3mNNYNVPC9C1EC7A3" }, - "DeploymentConfiguration": { + "DeploymentConfiguration": { "DeploymentCircuitBreaker": { "Enable": true, "Rollback": true @@ -845,4 +845,4 @@ } } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.executionrole.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.executionrole.expected.json index 93c7a7307a6ee..b3921b410fabd 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.executionrole.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.executionrole.expected.json @@ -95,15 +95,15 @@ "VpcPublicSubnet1NATGateway4D7517AA": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, "AllocationId": { "Fn::GetAtt": [ "VpcPublicSubnet1EIPD7E02669", "AllocationId" ] }, - "SubnetId": { - "Ref": "VpcPublicSubnet1Subnet5C2D37C4" - }, "Tags": [ { "Key": "Name", @@ -192,15 +192,15 @@ "VpcPublicSubnet2NATGateway9182C01D": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + }, "AllocationId": { "Fn::GetAtt": [ "VpcPublicSubnet2EIP3C605A87", "AllocationId" ] }, - "SubnetId": { - "Ref": "VpcPublicSubnet2Subnet691E08A3" - }, "Tags": [ { "Key": "Name", diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-autocreate.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-autocreate.expected.json index 778523d6bc6df..61b34b57938cd 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-autocreate.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-autocreate.expected.json @@ -405,15 +405,15 @@ "EcsDefaultClusterMnL3mNNYNVpcPublicSubnet1NATGateway5E3732C1": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "EcsDefaultClusterMnL3mNNYNVpcPublicSubnet1Subnet3C273B99" + }, "AllocationId": { "Fn::GetAtt": [ "EcsDefaultClusterMnL3mNNYNVpcPublicSubnet1EIP8704DB2F", "AllocationId" ] }, - "SubnetId": { - "Ref": "EcsDefaultClusterMnL3mNNYNVpcPublicSubnet1Subnet3C273B99" - }, "Tags": [ { "Key": "Name", @@ -502,15 +502,15 @@ "EcsDefaultClusterMnL3mNNYNVpcPublicSubnet2NATGateway4C855E00": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "EcsDefaultClusterMnL3mNNYNVpcPublicSubnet2Subnet95FF715A" + }, "AllocationId": { "Fn::GetAtt": [ "EcsDefaultClusterMnL3mNNYNVpcPublicSubnet2EIPF0764873", "AllocationId" ] }, - "SubnetId": { - "Ref": "EcsDefaultClusterMnL3mNNYNVpcPublicSubnet2Subnet95FF715A" - }, "Tags": [ { "Key": "Name", diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-vpconly.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-vpconly.expected.json index f221c99ccf4fc..d63db295a39cc 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-vpconly.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-vpconly.expected.json @@ -95,15 +95,15 @@ "VpcPublicSubnet1NATGateway4D7517AA": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, "AllocationId": { "Fn::GetAtt": [ "VpcPublicSubnet1EIPD7E02669", "AllocationId" ] }, - "SubnetId": { - "Ref": "VpcPublicSubnet1Subnet5C2D37C4" - }, "Tags": [ { "Key": "Name", @@ -192,15 +192,15 @@ "VpcPublicSubnet2NATGateway9182C01D": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + }, "AllocationId": { "Fn::GetAtt": [ "VpcPublicSubnet2EIP3C605A87", "AllocationId" ] }, - "SubnetId": { - "Ref": "VpcPublicSubnet2Subnet691E08A3" - }, "Tags": [ { "Key": "Name", diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3.expected.json index efd340e79b5cf..886e4a35dcf63 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3.expected.json @@ -95,15 +95,15 @@ "VpcPublicSubnet1NATGateway4D7517AA": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, "AllocationId": { "Fn::GetAtt": [ "VpcPublicSubnet1EIPD7E02669", "AllocationId" ] }, - "SubnetId": { - "Ref": "VpcPublicSubnet1Subnet5C2D37C4" - }, "Tags": [ { "Key": "Name", @@ -192,15 +192,15 @@ "VpcPublicSubnet2NATGateway9182C01D": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + }, "AllocationId": { "Fn::GetAtt": [ "VpcPublicSubnet2EIP3C605A87", "AllocationId" ] }, - "SubnetId": { - "Ref": "VpcPublicSubnet2Subnet691E08A3" - }, "Tags": [ { "Key": "Name", diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.multiple-network-load-balanced-fargate-service.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.multiple-network-load-balanced-fargate-service.expected.json index 2cdd6991792e8..643ff38905d69 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.multiple-network-load-balanced-fargate-service.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.multiple-network-load-balanced-fargate-service.expected.json @@ -95,15 +95,15 @@ "VpcPublicSubnet1NATGateway4D7517AA": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, "AllocationId": { "Fn::GetAtt": [ "VpcPublicSubnet1EIPD7E02669", "AllocationId" ] }, - "SubnetId": { - "Ref": "VpcPublicSubnet1Subnet5C2D37C4" - }, "Tags": [ { "Key": "Name", @@ -192,15 +192,15 @@ "VpcPublicSubnet2NATGateway9182C01D": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + }, "AllocationId": { "Fn::GetAtt": [ "VpcPublicSubnet2EIP3C605A87", "AllocationId" ] }, - "SubnetId": { - "Ref": "VpcPublicSubnet2Subnet691E08A3" - }, "Tags": [ { "Key": "Name", diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service-isolated.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service-isolated.expected.json index 6124fba473584..2414ce426a716 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service-isolated.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service-isolated.expected.json @@ -769,7 +769,7 @@ { "Ref": "AWS::URLSuffix" }, - "/aws-cdk/assets:3a8ba3ad06ed212b075efa3157fb407649c5996812bc64eeb5209e220aab4be5" + "/aws-cdk/assets:d6b024485c22795b5f5379edcd5cd6485f5bec6eb80bd072b20526f8eb2e0c64" ] ] }, diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service-public.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service-public.expected.json index de873260aa209..ae18adac13bbf 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service-public.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service-public.expected.json @@ -95,15 +95,15 @@ "VPCPublicSubnet1NATGatewayE0556630": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + }, "AllocationId": { "Fn::GetAtt": [ "VPCPublicSubnet1EIP6AD938E8", "AllocationId" ] }, - "SubnetId": { - "Ref": "VPCPublicSubnet1SubnetB4246D30" - }, "Tags": [ { "Key": "Name", @@ -192,15 +192,15 @@ "VPCPublicSubnet2NATGateway3C070193": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VPCPublicSubnet2Subnet74179F39" + }, "AllocationId": { "Fn::GetAtt": [ "VPCPublicSubnet2EIP4947BC00", "AllocationId" ] }, - "SubnetId": { - "Ref": "VPCPublicSubnet2Subnet74179F39" - }, "Tags": [ { "Key": "Name", @@ -289,15 +289,15 @@ "VPCPublicSubnet3NATGatewayD3048F5C": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VPCPublicSubnet3Subnet631C5E25" + }, "AllocationId": { "Fn::GetAtt": [ "VPCPublicSubnet3EIPAD4BC883", "AllocationId" ] }, - "SubnetId": { - "Ref": "VPCPublicSubnet3Subnet631C5E25" - }, "Tags": [ { "Key": "Name", @@ -619,7 +619,7 @@ { "Ref": "AWS::URLSuffix" }, - "/aws-cdk/assets:3a8ba3ad06ed212b075efa3157fb407649c5996812bc64eeb5209e220aab4be5" + "/aws-cdk/assets:d6b024485c22795b5f5379edcd5cd6485f5bec6eb80bd072b20526f8eb2e0c64" ] ] }, diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service.expected.json index 6fbcb79ecfa00..f43e4e5aa4e0b 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service.expected.json @@ -95,15 +95,15 @@ "VPCPublicSubnet1NATGatewayE0556630": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + }, "AllocationId": { "Fn::GetAtt": [ "VPCPublicSubnet1EIP6AD938E8", "AllocationId" ] }, - "SubnetId": { - "Ref": "VPCPublicSubnet1SubnetB4246D30" - }, "Tags": [ { "Key": "Name", @@ -192,15 +192,15 @@ "VPCPublicSubnet2NATGateway3C070193": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VPCPublicSubnet2Subnet74179F39" + }, "AllocationId": { "Fn::GetAtt": [ "VPCPublicSubnet2EIP4947BC00", "AllocationId" ] }, - "SubnetId": { - "Ref": "VPCPublicSubnet2Subnet74179F39" - }, "Tags": [ { "Key": "Name", @@ -460,7 +460,7 @@ { "Ref": "AWS::URLSuffix" }, - "/aws-cdk/assets:3a8ba3ad06ed212b075efa3157fb407649c5996812bc64eeb5209e220aab4be5" + "/aws-cdk/assets:d6b024485c22795b5f5379edcd5cd6485f5bec6eb80bd072b20526f8eb2e0c64" ] ] }, diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.scheduled-fargate-task.lit.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.scheduled-fargate-task.lit.expected.json index 8ffdd12ce4046..e580c83502abd 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.scheduled-fargate-task.lit.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.scheduled-fargate-task.lit.expected.json @@ -95,15 +95,15 @@ "VpcPublicSubnet1NATGateway4D7517AA": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, "AllocationId": { "Fn::GetAtt": [ "VpcPublicSubnet1EIPD7E02669", "AllocationId" ] }, - "SubnetId": { - "Ref": "VpcPublicSubnet1Subnet5C2D37C4" - }, "Tags": [ { "Key": "Name", @@ -293,7 +293,7 @@ { "Ref": "AWS::URLSuffix" }, - "/aws-cdk/assets:b2c69bfbfe983b634456574587443159b3b7258849856a118ad3d2772238f1a5" + "/aws-cdk/assets:8c1d9ca9f5d37b1c4870c13a9f855301bb42c1848dbcdd5edc8fe2c6c7261d48" ] ] }, @@ -417,22 +417,6 @@ ] } }, - "ScheduledFargateTaskScheduledTaskDefSecurityGroupE075BC19": { - "Type": "AWS::EC2::SecurityGroup", - "Properties": { - "GroupDescription": "aws-fargate-integ/ScheduledFargateTask/ScheduledTaskDef/SecurityGroup", - "SecurityGroupEgress": [ - { - "CidrIp": "0.0.0.0/0", - "Description": "Allow all outbound traffic by default", - "IpProtocol": "-1" - } - ], - "VpcId": { - "Ref": "Vpc8378EB38" - } - } - }, "ScheduledFargateTaskScheduledTaskDefEventsRole6CE19522": { "Type": "AWS::IAM::Role", "Properties": { @@ -502,6 +486,22 @@ } ] } + }, + "ScheduledFargateTaskScheduledTaskDefSecurityGroupE075BC19": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "aws-fargate-integ/ScheduledFargateTask/ScheduledTaskDef/SecurityGroup", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.special-listener.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.special-listener.expected.json index 5075f6511573b..7fa930b16d66d 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.special-listener.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.special-listener.expected.json @@ -95,15 +95,15 @@ "VpcPublicSubnet1NATGateway4D7517AA": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, "AllocationId": { "Fn::GetAtt": [ "VpcPublicSubnet1EIPD7E02669", "AllocationId" ] }, - "SubnetId": { - "Ref": "VpcPublicSubnet1Subnet5C2D37C4" - }, "Tags": [ { "Key": "Name", @@ -192,15 +192,15 @@ "VpcPublicSubnet2NATGateway9182C01D": { "Type": "AWS::EC2::NatGateway", "Properties": { + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + }, "AllocationId": { "Fn::GetAtt": [ "VpcPublicSubnet2EIP3C605A87", "AllocationId" ] }, - "SubnetId": { - "Ref": "VpcPublicSubnet2Subnet691E08A3" - }, "Tags": [ { "Key": "Name", diff --git a/packages/@aws-cdk/aws-ecs/README.md b/packages/@aws-cdk/aws-ecs/README.md index 9e8170f86d826..180710a7e2cb3 100644 --- a/packages/@aws-cdk/aws-ecs/README.md +++ b/packages/@aws-cdk/aws-ecs/README.md @@ -141,15 +141,13 @@ purpose-built by AWS for running containers. You can launch Amazon ECS container The following example will create a capacity with self-managed Amazon EC2 capacity of 2 `c5.large` Linux instances running with `Bottlerocket` AMI. -Note that you must specify either a `machineImage` or `machineImageType`, at least one, not both. - The following example adds Bottlerocket capacity to the cluster: ```ts cluster.addCapacity('bottlerocket-asg', { minCapacity: 2, instanceType: new ec2.InstanceType('c5.large'), - machineImageType: ecs.MachineImageType.BOTTLEROCKET, + machineImage: new ecs.BottleRocketImage(), }); ``` @@ -214,7 +212,7 @@ some supporting containers which are used to support the main container, doings things like upload logs or metrics to monitoring services. To run a task or service with Amazon EC2 launch type, use the `Ec2TaskDefinition`. For AWS Fargate tasks/services, use the -`FargateTaskDefinition`. For AWS ECS Anywhere use the `ExternalTaskDefinition`. These classes +`FargateTaskDefinition`. For AWS ECS Anywhere use the `ExternalTaskDefinition`. These classes provide simplified APIs that only contain properties relevant for each specific launch type. For a `FargateTaskDefinition`, specify the task size (`memoryLimitMiB` and `cpu`): @@ -736,7 +734,7 @@ const service = new ecs.Ec2Service(stack, 'Service', { }); ``` -With `bridge` or `host` network modes, only `SRV` DNS record types are supported. +With `bridge` or `host` network modes, only `SRV` DNS record types are supported. By default, `SRV` DNS record types will target the default container and default port. However, you may target a different container and port on the same ECS task: @@ -893,7 +891,7 @@ const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', { To enable using the inference accelerators in the containers, add `inferenceAcceleratorResources` field and set it to a list of device names used for the inference accelerators. Each value in the -list should match a `DeviceName` for an `InferenceAccelerator` specified in the task definition. +list should match a `DeviceName` for an `InferenceAccelerator` specified in the task definition. ```ts const inferenceAcceleratorResources = ['device1']; @@ -909,7 +907,7 @@ taskDefinition.addContainer('cont', { Please note, ECS Exec leverages AWS Systems Manager (SSM). So as a prerequisite for the exec command to work, you need to have the SSM plugin for the AWS CLI installed locally. For more information, see -[Install Session Manager plugin for AWS CLI] (https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html). +[Install Session Manager plugin for AWS CLI](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html). To enable the ECS Exec feature for your containers, set the boolean flag `enableExecuteCommand` to `true` in your `Ec2Service` or `FargateService`. @@ -927,11 +925,11 @@ const service = new ecs.Ec2Service(stack, 'Service', { You can enable sending logs of your execute session commands to a CloudWatch log group or S3 bucket by configuring the `executeCommandConfiguration` property for your cluster. The default configuration will send the logs to the CloudWatch Logs using the `awslogs` log driver that is configured in your task definition. Please note, -when using your own `logConfiguration` the log group or S3 Bucket specified must already be created. +when using your own `logConfiguration` the log group or S3 Bucket specified must already be created. To encrypt data using your own KMS Customer Key (CMK), you must create a CMK and provide the key in the `kmsKey` field of the `executeCommandConfiguration`. To use this key for encrypting CloudWatch log data or S3 bucket, make sure to associate the key -to these resources on creation. +to these resources on creation. ```ts const kmsKey = new kms.Key(stack, 'KmsKey'); diff --git a/packages/@aws-cdk/aws-ecs/lib/cluster.ts b/packages/@aws-cdk/aws-ecs/lib/cluster.ts index 5b9da054a9fe2..1fce9b00d4500 100644 --- a/packages/@aws-cdk/aws-ecs/lib/cluster.ts +++ b/packages/@aws-cdk/aws-ecs/lib/cluster.ts @@ -294,12 +294,13 @@ export class Cluster extends Resource implements ICluster { * @deprecated Use {@link Cluster.addAsgCapacityProvider} instead. */ public addCapacity(id: string, options: AddCapacityOptions): autoscaling.AutoScalingGroup { - if (options.machineImage && options.machineImageType) { - throw new Error('You can only specify either machineImage or machineImageType, not both.'); - } + // Do 2-way defaulting here: if the machineImageType is BOTTLEROCKET, pick the right AMI. + // Otherwise, determine the machineImageType from the given AMI. + const machineImage = options.machineImage ?? + (options.machineImageType === MachineImageType.BOTTLEROCKET ? new BottleRocketImage() : new EcsOptimizedAmi()); - const machineImage = options.machineImage ?? options.machineImageType === MachineImageType.BOTTLEROCKET ? - new BottleRocketImage() : new EcsOptimizedAmi(); + const machineImageType = options.machineImageType ?? + (isBottleRocketImage(machineImage) ? MachineImageType.BOTTLEROCKET : MachineImageType.AMAZON_LINUX_2); const autoScalingGroup = new autoscaling.AutoScalingGroup(this, id, { vpc: this.vpc, @@ -309,7 +310,7 @@ export class Cluster extends Resource implements ICluster { }); this.addAutoScalingGroup(autoScalingGroup, { - machineImageType: options.machineImageType, + machineImageType: machineImageType, ...options, }); @@ -1016,11 +1017,18 @@ export interface AddAutoScalingGroupCapacityOptions { */ readonly topicEncryptionKey?: kms.IKey; - /** - * Specify the machine image type. + * What type of machine image this is + * + * Depending on the setting, different UserData will automatically be added + * to the `AutoScalingGroup` to configure it properly for use with ECS. * - * @default MachineImageType.AMAZON_LINUX_2 + * If you create an `AutoScalingGroup` yourself and are adding it via + * `addAutoScalingGroup()`, you must specify this value. If you are adding an + * `autoScalingGroup` via `addCapacity`, this value will be determined + * from the `machineImage` you pass. + * + * @default - Automatically determined from `machineImage`, if available, otherwise `MachineImageType.AMAZON_LINUX_2`. */ readonly machineImageType?: MachineImageType; } @@ -1356,3 +1364,8 @@ class MaybeCreateCapacityProviderAssociations implements IAspect { } } } + + +function isBottleRocketImage(image: ec2.IMachineImage) { + return image instanceof BottleRocketImage; +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecs/test/cluster.test.ts b/packages/@aws-cdk/aws-ecs/test/cluster.test.ts index cd144f87bfb12..8b6dd4626ddb6 100644 --- a/packages/@aws-cdk/aws-ecs/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/cluster.test.ts @@ -1589,7 +1589,7 @@ nodeunitShim({ test.done(); }, - 'cluster capacity with bottlerocket AMI'(test: Test) { + 'cluster capacity with bottlerocket AMI, by setting machineImageType'(test: Test) { // GIVEN const app = new cdk.App(); const stack = new cdk.Stack(app, 'test'); @@ -1682,20 +1682,34 @@ nodeunitShim({ test.done(); }, - 'throws when machineImage and machineImageType both specified'(test: Test) { + 'cluster capacity with bottlerocket AMI, by setting the machineImage'(test: Test) { // GIVEN const app = new cdk.App(); const stack = new cdk.Stack(app, 'test'); + const cluster = new ecs.Cluster(stack, 'EcsCluster'); + cluster.addCapacity('bottlerocket-asg', { + instanceType: new ec2.InstanceType('c5.large'), + machineImage: new ecs.BottleRocketImage(), + }); // THEN - test.throws(() => { - cluster.addCapacity('bottlerocket-asg', { - instanceType: new ec2.InstanceType('c5.large'), - machineImageType: ecs.MachineImageType.BOTTLEROCKET, - machineImage: new ecs.EcsOptimizedAmi(), - }); - }, /You can only specify either machineImage or machineImageType, not both./); + expect(stack).to(haveResourceLike('AWS::AutoScaling::LaunchConfiguration', { + UserData: { + 'Fn::Base64': { + 'Fn::Join': [ + '', + [ + '\n[settings.ecs]\ncluster = "', + { + Ref: 'EcsCluster97242B84', + }, + '"', + ], + ], + }, + }, + })); test.done(); }, diff --git a/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts b/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts index 3a8671cdf22f7..b5c60ae1c5c0c 100644 --- a/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts @@ -1892,7 +1892,7 @@ describe('container definition', () => { { Ref: 'AWS::Region' }, '.', { Ref: 'AWS::URLSuffix' }, - '/aws-cdk/assets:b2c69bfbfe983b634456574587443159b3b7258849856a118ad3d2772238f1a5', + '/aws-cdk/assets:8c1d9ca9f5d37b1c4870c13a9f855301bb42c1848dbcdd5edc8fe2c6c7261d48', ], ], }, @@ -1948,11 +1948,11 @@ describe('container definition', () => { const asm = app.synth(); expect(asm.getStackArtifact(stack.artifactId).assets[0]).toEqual({ repositoryName: 'aws-cdk/assets', - imageTag: 'ce3419d7c5d2d44e2789b13ccbd2d54ddf682557669f68bcee753231f5f1c0a5', - id: 'ce3419d7c5d2d44e2789b13ccbd2d54ddf682557669f68bcee753231f5f1c0a5', + imageTag: '9d913132f812bc1ad436aeb5a51f9216c5776b8079318c1883ad2f79f0ef1a4b', + id: '9d913132f812bc1ad436aeb5a51f9216c5776b8079318c1883ad2f79f0ef1a4b', packaging: 'container-image', - path: 'asset.ce3419d7c5d2d44e2789b13ccbd2d54ddf682557669f68bcee753231f5f1c0a5', - sourceHash: 'ce3419d7c5d2d44e2789b13ccbd2d54ddf682557669f68bcee753231f5f1c0a5', + path: 'asset.9d913132f812bc1ad436aeb5a51f9216c5776b8079318c1883ad2f79f0ef1a4b', + sourceHash: '9d913132f812bc1ad436aeb5a51f9216c5776b8079318c1883ad2f79f0ef1a4b', target: 'build-target', file: 'index.py', }); diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/ec2-task-definition.test.ts b/packages/@aws-cdk/aws-ecs/test/ec2/ec2-task-definition.test.ts index 06f891868feb8..793dff5c3b97b 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/ec2-task-definition.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/ec2/ec2-task-definition.test.ts @@ -759,7 +759,7 @@ describe('ec2 task definition', () => { { Ref: 'AWS::URLSuffix', }, - '/aws-cdk/assets:b2c69bfbfe983b634456574587443159b3b7258849856a118ad3d2772238f1a5', + '/aws-cdk/assets:8c1d9ca9f5d37b1c4870c13a9f855301bb42c1848dbcdd5edc8fe2c6c7261d48', ], ], }, diff --git a/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.expected.json b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.expected.json index dbdb415e57e63..a03a81e7255da 100644 --- a/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.expected.json +++ b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.expected.json @@ -714,7 +714,7 @@ { "Ref": "AWS::URLSuffix" }, - "/aws-cdk/assets:709fd91ba301f9b460ce1066dbc339f6a29bd4a07609ff98fb0e0faa475b650d" + "/aws-cdk/assets:3fc39b45c4fd074ceef5d0f8528b74fa7fe6e8fa0aa4a6ffe7fb5e016cf8dc04" ] ] }, diff --git a/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-fargate-task.expected.json b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-fargate-task.expected.json index 123578699510c..40e42600e5413 100644 --- a/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-fargate-task.expected.json +++ b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-fargate-task.expected.json @@ -237,7 +237,7 @@ { "Ref": "AWS::URLSuffix" }, - "/aws-cdk/assets:709fd91ba301f9b460ce1066dbc339f6a29bd4a07609ff98fb0e0faa475b650d" + "/aws-cdk/assets:3fc39b45c4fd074ceef5d0f8528b74fa7fe6e8fa0aa4a6ffe7fb5e016cf8dc04" ] ] }, diff --git a/packages/@aws-cdk/aws-events/lib/rule.ts b/packages/@aws-cdk/aws-events/lib/rule.ts index 08ecb18bbc1d7..47f5d05201a53 100644 --- a/packages/@aws-cdk/aws-events/lib/rule.ts +++ b/packages/@aws-cdk/aws-events/lib/rule.ts @@ -1,5 +1,5 @@ import { IRole, PolicyStatement, Role, ServicePrincipal } from '@aws-cdk/aws-iam'; -import { App, IConstruct, IResource, Lazy, Names, Resource, Stack, Token, PhysicalName } from '@aws-cdk/core'; +import { App, IResource, Lazy, Names, Resource, Stack, Token, PhysicalName } from '@aws-cdk/core'; import { Node, Construct } from 'constructs'; import { IEventBus } from './event-bus'; import { EventPattern } from './event-pattern'; @@ -9,10 +9,6 @@ import { Schedule } from './schedule'; import { IRuleTarget } from './target'; import { mergeEventPattern, renderEventPattern, sameEnvDimension } from './util'; -// keep this import separate from other imports to reduce chance for merge conflicts with v2-main -// eslint-disable-next-line no-duplicate-imports, import/order -import { Construct as CoreConstruct } from '@aws-cdk/core'; - /** * Properties for defining an EventBridge Rule */ @@ -118,7 +114,9 @@ export class Rule extends Resource implements IRule { private readonly eventPattern: EventPattern = { }; private readonly scheduleExpression?: string; private readonly description?: string; - private readonly targetAccounts: {[key: string]: Set} = {}; + + /** Set to keep track of what target accounts and regions we've already created event buses for */ + private readonly _xEnvTargetsAdded = new Set(); constructor(scope: Construct, id: string, props: RuleProps = { }) { super(scope, id, { @@ -194,63 +192,19 @@ export class Rule extends Resource implements IRule { // based on: // https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-cross-account.html - // for cross-account or cross-region events, we cannot create new components for an imported resource - // because we don't have the target stack - const isImportedResource = !sameEnvDimension(targetStack.account, targetAccount) || !sameEnvDimension(targetStack.region, targetRegion); //(targetAccount !== targetStack.account) || (targetRegion !== targetStack.region); - if (isImportedResource) { - throw new Error('Cannot create a cross-account or cross-region rule with an imported resource'); - } - - // for cross-account or cross-region events, we require concrete accounts + // for cross-account or cross-region events, we require a concrete target account and region if (!targetAccount || Token.isUnresolved(targetAccount)) { throw new Error('You need to provide a concrete account for the target stack when using cross-account or cross-region events'); } - if (Token.isUnresolved(sourceAccount)) { - throw new Error('You need to provide a concrete account for the source stack when using cross-account or cross-region events'); - } - // and the target region has to be concrete as well if (!targetRegion || Token.isUnresolved(targetRegion)) { throw new Error('You need to provide a concrete region for the target stack when using cross-account or cross-region events'); } - - // the _actual_ target is just the event bus of the target's account - // make sure we only add it once per account per region - let targetAccountExists = false; - const accountKey = Object.keys(this.targetAccounts).find(account => account === targetAccount); - if (accountKey) { - targetAccountExists = this.targetAccounts[accountKey].has(targetRegion); - } - - if (!targetAccountExists) { - // add the current account-region pair to tracking structure - const regionsSet = this.targetAccounts[targetAccount]; - if (!regionsSet) { - this.targetAccounts[targetAccount] = new Set(); - } - this.targetAccounts[targetAccount].add(targetRegion); - - const eventBusArn = targetStack.formatArn({ - service: 'events', - resource: 'event-bus', - resourceName: 'default', - region: targetRegion, - account: targetAccount, - }); - - this.targets.push({ - id, - arn: eventBusArn, - // for cross-region we now require a role with PutEvents permissions - roleArn: roleArn ?? this.singletonEventRole(this, [this.putEventStatement(eventBusArn)]).roleArn, - }); + if (Token.isUnresolved(sourceAccount)) { + throw new Error('You need to provide a concrete account for the source stack when using cross-account or cross-region events'); } - // Grant the source account in the source region permissions to publish events to the event bus of the target account in the target region. - // Do it in a separate stack instead of the target stack (which seems like the obvious place to put it), - // because it needs to be deployed before the rule containing the above event-bus target in the source stack - // (EventBridge verifies whether you have permissions to the targets on rule creation), - // but it's common for the target stack to depend on the source stack - // (that's the case with CodePipeline, for example) + // Don't exactly understand why this code was here (seems unlikely this rule would be violated), but + // let's leave it in nonetheless. const sourceApp = this.node.root; if (!sourceApp || !App.isApp(sourceApp)) { throw new Error('Event stack which uses cross-account or cross-region targets must be part of a CDK app'); @@ -263,60 +217,28 @@ export class Rule extends Resource implements IRule { throw new Error('Event stack and target stack must belong to the same CDK app'); } - // if different accounts, we need to add the permissions to the target eventbus - if (!sameEnvDimension(sourceAccount, targetAccount)) { - const stackId = `EventBusPolicy-${sourceAccount}-${targetRegion}-${targetAccount}`; - let eventBusPolicyStack: Stack = sourceApp.node.tryFindChild(stackId) as Stack; - if (!eventBusPolicyStack) { - eventBusPolicyStack = new Stack(sourceApp, stackId, { - env: { - account: targetAccount, - region: targetRegion, - }, - stackName: `${targetStack.stackName}-EventBusPolicy-support-${targetRegion}-${sourceAccount}`, - }); - new CfnEventBusPolicy(eventBusPolicyStack, 'GivePermToOtherAccount', { - action: 'events:PutEvents', - statementId: `Allow-account-${sourceAccount}`, - principal: sourceAccount, - }); - } - // deploy the event bus permissions before the source stack - sourceStack.addDependency(eventBusPolicyStack); - } - - // The actual rule lives in the target stack. - // Other than the account, it's identical to this one - - // eventPattern is mutable through addEventPattern(), so we need to lazy evaluate it - // but only Tokens can be lazy in the framework, so make a subclass instead - const self = this; - - class CopyRule extends Rule { - public _renderEventPattern(): any { - return self._renderEventPattern(); - } - - // we need to override validate(), as it uses the - // value of the eventPattern field, - // which might be empty in the case of the copied rule - // (as the patterns in the original might be added through addEventPattern(), - // not passed through the constructor). - // Anyway, even if the original rule is invalid, - // we would get duplicate errors if we didn't override this, - // which is probably a bad idea in and of itself - protected validate(): string[] { - return []; - } - - } - - new CopyRule(targetStack, `${Names.uniqueId(this)}-${id}`, { + // The target of this Rule will be the default event bus of the target environment + this.ensureXEnvTargetEventBus(targetStack, targetAccount, targetRegion, id); + + // The actual rule lives in the target stack. Other than the account, it's identical to this one, + // but only evaluated at render time (via a special subclass). + // + // FIXME: the MirrorRule is a bit silly, forwarding the exact same event to another event bus + // and trigger on it there (there will be issues with construct references, for example). Especially + // in the case of scheduled events, we will just trigger both rules in parallel in both environments. + // + // A better solution would be to have the source rule add a unique token to the the event, + // and have the mirror rule trigger on that token only (thereby properly separating triggering, which + // happens in the source env; and activating, which happens in the target env). + // + // Don't have time to do that right now. + const mirrorRuleScope = this.obtainMirrorRuleScope(targetStack, targetAccount, targetRegion); + new MirrorRule(mirrorRuleScope, `${Names.uniqueId(this)}-${id}`, { targets: [target], eventPattern: this.eventPattern, schedule: this.scheduleExpression ? Schedule.expression(this.scheduleExpression) : undefined, description: this.description, - }); + }, this); return; } @@ -413,32 +335,140 @@ export class Rule extends Resource implements IRule { } /** - * Obtain the Role for the EventBridge event - * - * If a role already exists, it will be returned. This ensures that if multiple - * events have the same target, they will share a role. - * @internal - */ - private singletonEventRole(scope: IConstruct, policyStatements: PolicyStatement[]): IRole { - const id = 'EventsRole'; - const existing = scope.node.tryFindChild(id) as IRole; - if (existing) { return existing; } + * Make sure we add the target environments event bus as a target, and the target has permissions set up to receive our events + * + * For cross-account rules, uses a support stack to set up a policy on the target event bus. + */ + private ensureXEnvTargetEventBus(targetStack: Stack, targetAccount: string, targetRegion: string, id: string) { + // the _actual_ target is just the event bus of the target's account + // make sure we only add it once per account per region + const key = `${targetAccount}:${targetRegion}`; + if (this._xEnvTargetsAdded.has(key)) { return; } + this._xEnvTargetsAdded.add(key); + + const eventBusArn = targetStack.formatArn({ + service: 'events', + resource: 'event-bus', + resourceName: 'default', + region: targetRegion, + account: targetAccount, + }); - const role = new Role(scope as CoreConstruct, id, { - roleName: PhysicalName.GENERATE_IF_NEEDED, - assumedBy: new ServicePrincipal('events.amazonaws.com'), + // For some reason, cross-region requires a Role (with `PutEvents` on the + // target event bus) while cross-account doesn't + const roleArn = !sameEnvDimension(targetRegion, Stack.of(this).region) + ? this.crossRegionPutEventsRole(eventBusArn).roleArn + : undefined; + + this.targets.push({ + id, + arn: eventBusArn, + roleArn, }); - policyStatements.forEach(role.addToPolicy.bind(role)); + // Add a policy to the target Event Bus to allow the source account/region to publish into it. + // + // Since this Event Bus permission needs to be deployed before the stack containing the Rule is deployed + // (as EventBridge verifies whether you have permissions to the targets on rule creation), this needs + // to be in a support stack. + + const sourceApp = this.node.root as App; + const sourceAccount = Stack.of(this).account; + + // If different accounts, we need to add the permissions to the target eventbus + // + // For different region, no need for a policy on the target event bus (but a need + // for a role). + if (!sameEnvDimension(sourceAccount, targetAccount)) { + const stackId = `EventBusPolicy-${sourceAccount}-${targetRegion}-${targetAccount}`; + let eventBusPolicyStack: Stack = sourceApp.node.tryFindChild(stackId) as Stack; + if (!eventBusPolicyStack) { + eventBusPolicyStack = new Stack(sourceApp, stackId, { + env: { + account: targetAccount, + region: targetRegion, + }, + // The region in the stack name is rather redundant (it will always be the target region) + // Leaving it in for backwards compatibility. + stackName: `${targetStack.stackName}-EventBusPolicy-support-${targetRegion}-${sourceAccount}`, + }); + new CfnEventBusPolicy(eventBusPolicyStack, 'GivePermToOtherAccount', { + action: 'events:PutEvents', + statementId: `Allow-account-${sourceAccount}`, + principal: sourceAccount, + }); + } + // deploy the event bus permissions before the source stack + Stack.of(this).addDependency(eventBusPolicyStack); + } + } - return role; + /** + * Return the scope where the mirror rule should be created for x-env event targets + * + * This is the target resource's containing stack if it shares the same region (owned + * resources), or should be a fresh support stack for imported resources. + * + * We don't implement the second yet, as I have to think long and hard on whether we + * can reuse the existing support stack or not, and I don't have time for that right now. + */ + private obtainMirrorRuleScope(targetStack: Stack, targetAccount: string, targetRegion: string): Construct { + // for cross-account or cross-region events, we cannot create new components for an imported resource + // because we don't have the target stack + if (sameEnvDimension(targetStack.account, targetAccount) && sameEnvDimension(targetStack.region, targetRegion)) { + return targetStack; + } + + // For now, we don't do the work for the support stack yet + throw new Error('Cannot create a cross-account or cross-region rule for an imported resource (create a stack with the right environment for the imported resource)'); } - private putEventStatement(eventBusArn: string) { - return new PolicyStatement({ + /** + * Obtain the Role for the EventBridge event + * + * If a role already exists, it will be returned. This ensures that if multiple + * events have the same target, they will share a role. + * @internal + */ + private crossRegionPutEventsRole(eventBusArn: string): IRole { + const id = 'EventsRole'; + let role = this.node.tryFindChild(id) as IRole; + if (!role) { + role = new Role(this, id, { + roleName: PhysicalName.GENERATE_IF_NEEDED, + assumedBy: new ServicePrincipal('events.amazonaws.com'), + }); + } + + role.addToPrincipalPolicy(new PolicyStatement({ actions: ['events:PutEvents'], resources: [eventBusArn], - }); + })); + + return role; } } +/** + * A rule that mirrors another rule + */ +class MirrorRule extends Rule { + constructor(scope: Construct, id: string, props: RuleProps, private readonly source: Rule) { + super(scope, id, props); + } + + public _renderEventPattern(): any { + return this.source._renderEventPattern(); + } + + /** + * Override validate to be a no-op + * + * The rules are never stored on this object so there's nothing to validate. + * + * Instead, we mirror the other rule at render time. + */ + protected validate(): string[] { + return []; + } +} diff --git a/packages/@aws-cdk/aws-events/test/test.rule.ts b/packages/@aws-cdk/aws-events/test/test.rule.ts index 352ee30132733..b6ff7b62d86d4 100644 --- a/packages/@aws-cdk/aws-events/test/test.rule.ts +++ b/packages/@aws-cdk/aws-events/test/test.rule.ts @@ -1,3 +1,4 @@ +/* eslint-disable object-curly-newline */ import { expect, haveResource, haveResourceLike } from '@aws-cdk/assert-internal'; import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; @@ -417,6 +418,55 @@ export = { test.done(); }, + 'in cross-account scenario, target role is only used in target account'(test: Test) { + // GIVEN + const app = new cdk.App(); + const ruleStack = new cdk.Stack(app, 'RuleStack', { env: { account: '1234', region: 'us-east-1' } }); + const targetStack = new cdk.Stack(app, 'TargeTStack', { env: { account: '5678', region: 'us-east-1' } }); + + const rule = new Rule(ruleStack, 'EventRule', { + schedule: Schedule.rate(cdk.Duration.minutes(1)), + }); + + const role = new iam.Role(targetStack, 'SomeRole', { + assumedBy: new iam.ServicePrincipal('nobody'), + }); + + // a plain string should just be stringified (i.e. double quotes added and escaped) + rule.addTarget({ + bind: () => ({ + id: '', + arn: 'ARN2', + role, + targetResource: role, // Not really but good enough + }), + }); + + // THEN + expect(ruleStack).to(haveResourceLike('AWS::Events::Rule', { + Targets: [ + { + Arn: { 'Fn::Join': ['', [ + 'arn:', + { 'Ref': 'AWS::Partition' }, + ':events:us-east-1:5678:event-bus/default', + ]] }, + }, + ], + })); + expect(targetStack).to(haveResourceLike('AWS::Events::Rule', { + 'Targets': [ + { + 'Arn': 'ARN2', + 'Id': 'Target0', + 'RoleArn': { 'Fn::GetAtt': ['SomeRole6DDC54DD', 'Arn'] }, + }, + ], + })); + + test.done(); + }, + 'asEventRuleTarget can use the ruleArn and a uniqueId of the rule'(test: Test) { const stack = new cdk.Stack(); @@ -618,7 +668,7 @@ export = { test.throws(() => { rule.addTarget(new SomeTarget('T', resource)); - }, /You need to provide a concrete account for the source stack when using cross-account or cross-region events/); + }, /You need to provide a concrete region/); test.done(); }, @@ -843,7 +893,7 @@ export = { const resource = EventBus.fromEventBusArn(sourceStack, 'TargetEventBus', `arn:aws:events:${targetRegion}:${targetAccount}:event-bus/default`); test.throws(() => { rule.addTarget(new SomeTarget('T', resource)); - }, /Cannot create a cross-account or cross-region rule with an imported resource/); + }, /Cannot create a cross-account or cross-region rule for an imported resource/); test.done(); }, diff --git a/packages/@aws-cdk/aws-lambda-python/lib/bundling.ts b/packages/@aws-cdk/aws-lambda-python/lib/bundling.ts index 5a3228a78fef7..0c2b4bf624786 100644 --- a/packages/@aws-cdk/aws-lambda-python/lib/bundling.ts +++ b/packages/@aws-cdk/aws-lambda-python/lib/bundling.ts @@ -100,7 +100,7 @@ export function bundle(options: BundlingOptions): lambda.Code { const image = cdk.DockerImage.fromBuild(stagedir, { buildArgs: { - IMAGE: runtime.bundlingDockerImage.image, + IMAGE: runtime.bundlingImage.image, }, file: dockerfile, }); diff --git a/packages/@aws-cdk/aws-lambda-python/test/function.test.ts b/packages/@aws-cdk/aws-lambda-python/test/function.test.ts index 0eda2a419e97c..c0636329b2ecc 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/function.test.ts +++ b/packages/@aws-cdk/aws-lambda-python/test/function.test.ts @@ -48,7 +48,7 @@ test('PythonFunction with defaults', () => { }); expect(bundle).toHaveBeenCalledWith(expect.objectContaining({ - entry: expect.stringMatching(/@aws-cdk\/aws-lambda-python\/test\/lambda-handler$/), + entry: expect.stringMatching(/aws-lambda-python\/test\/lambda-handler$/), outputPathSuffix: '.', })); @@ -65,7 +65,7 @@ test('PythonFunction with index in a subdirectory', () => { }); expect(bundle).toHaveBeenCalledWith(expect.objectContaining({ - entry: expect.stringMatching(/@aws-cdk\/aws-lambda-python\/test\/lambda-handler-sub$/), + entry: expect.stringMatching(/aws-lambda-python\/test\/lambda-handler-sub$/), outputPathSuffix: '.', })); @@ -142,4 +142,4 @@ test('allows specifying hash type', () => { S3Key: 'MY_CUSTOM_HASH', }, }); -}); \ No newline at end of file +}); diff --git a/packages/@aws-cdk/aws-lambda-python/test/integ.function.expected.json b/packages/@aws-cdk/aws-lambda-python/test/integ.function.expected.json index 6ed7f497e9ee0..e48b9c2e0ad92 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/integ.function.expected.json +++ b/packages/@aws-cdk/aws-lambda-python/test/integ.function.expected.json @@ -36,7 +36,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters5b8ff93384af5b488025e13b274c2dd894e474a810f1a406af1aeb4edbba6a3cS3Bucket5B146B0B" + "Ref": "AssetParametersfc7bfbf72c74b955f7bc25d2bb123c0eeec9557cda17481146d51672768907b2S3Bucket383ED51E" }, "S3Key": { "Fn::Join": [ @@ -49,7 +49,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters5b8ff93384af5b488025e13b274c2dd894e474a810f1a406af1aeb4edbba6a3cS3VersionKeyC0C8A627" + "Ref": "AssetParametersfc7bfbf72c74b955f7bc25d2bb123c0eeec9557cda17481146d51672768907b2S3VersionKeyA520554C" } ] } @@ -62,7 +62,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters5b8ff93384af5b488025e13b274c2dd894e474a810f1a406af1aeb4edbba6a3cS3VersionKeyC0C8A627" + "Ref": "AssetParametersfc7bfbf72c74b955f7bc25d2bb123c0eeec9557cda17481146d51672768907b2S3VersionKeyA520554C" } ] } @@ -87,17 +87,17 @@ } }, "Parameters": { - "AssetParameters5b8ff93384af5b488025e13b274c2dd894e474a810f1a406af1aeb4edbba6a3cS3Bucket5B146B0B": { + "AssetParametersfc7bfbf72c74b955f7bc25d2bb123c0eeec9557cda17481146d51672768907b2S3Bucket383ED51E": { "Type": "String", - "Description": "S3 bucket for asset \"5b8ff93384af5b488025e13b274c2dd894e474a810f1a406af1aeb4edbba6a3c\"" + "Description": "S3 bucket for asset \"fc7bfbf72c74b955f7bc25d2bb123c0eeec9557cda17481146d51672768907b2\"" }, - "AssetParameters5b8ff93384af5b488025e13b274c2dd894e474a810f1a406af1aeb4edbba6a3cS3VersionKeyC0C8A627": { + "AssetParametersfc7bfbf72c74b955f7bc25d2bb123c0eeec9557cda17481146d51672768907b2S3VersionKeyA520554C": { "Type": "String", - "Description": "S3 key for asset version \"5b8ff93384af5b488025e13b274c2dd894e474a810f1a406af1aeb4edbba6a3c\"" + "Description": "S3 key for asset version \"fc7bfbf72c74b955f7bc25d2bb123c0eeec9557cda17481146d51672768907b2\"" }, - "AssetParameters5b8ff93384af5b488025e13b274c2dd894e474a810f1a406af1aeb4edbba6a3cArtifactHashB6A7723E": { + "AssetParametersfc7bfbf72c74b955f7bc25d2bb123c0eeec9557cda17481146d51672768907b2ArtifactHashB863A6ED": { "Type": "String", - "Description": "Artifact hash for asset \"5b8ff93384af5b488025e13b274c2dd894e474a810f1a406af1aeb4edbba6a3c\"" + "Description": "Artifact hash for asset \"fc7bfbf72c74b955f7bc25d2bb123c0eeec9557cda17481146d51672768907b2\"" } }, "Outputs": { diff --git a/packages/@aws-cdk/aws-lambda-python/test/integ.function.pipenv.expected.json b/packages/@aws-cdk/aws-lambda-python/test/integ.function.pipenv.expected.json index 6766584717e3d..2b64c7f650529 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/integ.function.pipenv.expected.json +++ b/packages/@aws-cdk/aws-lambda-python/test/integ.function.pipenv.expected.json @@ -36,7 +36,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters1a697367cf267ba8a72f772b462c503dd5a6e7bcdd8cb353d89604e9d18802eeS3BucketADC4FFB3" + "Ref": "AssetParameters2f7cfa4e9b9d7c70c9af6f1820a340c116a1e139fcac4dde381f541331ba5e46S3Bucket84213B6F" }, "S3Key": { "Fn::Join": [ @@ -49,7 +49,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters1a697367cf267ba8a72f772b462c503dd5a6e7bcdd8cb353d89604e9d18802eeS3VersionKey784D7689" + "Ref": "AssetParameters2f7cfa4e9b9d7c70c9af6f1820a340c116a1e139fcac4dde381f541331ba5e46S3VersionKeyC495D2FD" } ] } @@ -62,7 +62,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters1a697367cf267ba8a72f772b462c503dd5a6e7bcdd8cb353d89604e9d18802eeS3VersionKey784D7689" + "Ref": "AssetParameters2f7cfa4e9b9d7c70c9af6f1820a340c116a1e139fcac4dde381f541331ba5e46S3VersionKeyC495D2FD" } ] } @@ -121,7 +121,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters8eb49c58869010ec84a5b407369006e9cb5fdf9667d8609638d6fa59265fc3ecS3Bucket5B706387" + "Ref": "AssetParameters7157876e6cb8bea4bc3b6e272edab8a4d202a0be6bdcecf33b95008fbacc2335S3Bucket1E236877" }, "S3Key": { "Fn::Join": [ @@ -134,7 +134,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters8eb49c58869010ec84a5b407369006e9cb5fdf9667d8609638d6fa59265fc3ecS3VersionKey48CF3C03" + "Ref": "AssetParameters7157876e6cb8bea4bc3b6e272edab8a4d202a0be6bdcecf33b95008fbacc2335S3VersionKeyD0D6DBBF" } ] } @@ -147,7 +147,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters8eb49c58869010ec84a5b407369006e9cb5fdf9667d8609638d6fa59265fc3ecS3VersionKey48CF3C03" + "Ref": "AssetParameters7157876e6cb8bea4bc3b6e272edab8a4d202a0be6bdcecf33b95008fbacc2335S3VersionKeyD0D6DBBF" } ] } @@ -206,7 +206,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters992750d379dbbe94426dbd352099e3344b9edcfd098a56691b53eafaeb227b10S3Bucket9E9FE80F" + "Ref": "AssetParameters1d8b9cdbaaf7c36dc94604966b3b1f6fb2a972fbddc5cfd62fef7a7a8efd5888S3BucketC4EB0B32" }, "S3Key": { "Fn::Join": [ @@ -219,7 +219,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters992750d379dbbe94426dbd352099e3344b9edcfd098a56691b53eafaeb227b10S3VersionKeyFFAE128A" + "Ref": "AssetParameters1d8b9cdbaaf7c36dc94604966b3b1f6fb2a972fbddc5cfd62fef7a7a8efd5888S3VersionKeyB8755D2B" } ] } @@ -232,7 +232,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters992750d379dbbe94426dbd352099e3344b9edcfd098a56691b53eafaeb227b10S3VersionKeyFFAE128A" + "Ref": "AssetParameters1d8b9cdbaaf7c36dc94604966b3b1f6fb2a972fbddc5cfd62fef7a7a8efd5888S3VersionKeyB8755D2B" } ] } @@ -257,41 +257,41 @@ } }, "Parameters": { - "AssetParameters1a697367cf267ba8a72f772b462c503dd5a6e7bcdd8cb353d89604e9d18802eeS3BucketADC4FFB3": { + "AssetParameters2f7cfa4e9b9d7c70c9af6f1820a340c116a1e139fcac4dde381f541331ba5e46S3Bucket84213B6F": { "Type": "String", - "Description": "S3 bucket for asset \"1a697367cf267ba8a72f772b462c503dd5a6e7bcdd8cb353d89604e9d18802ee\"" + "Description": "S3 bucket for asset \"2f7cfa4e9b9d7c70c9af6f1820a340c116a1e139fcac4dde381f541331ba5e46\"" }, - "AssetParameters1a697367cf267ba8a72f772b462c503dd5a6e7bcdd8cb353d89604e9d18802eeS3VersionKey784D7689": { + "AssetParameters2f7cfa4e9b9d7c70c9af6f1820a340c116a1e139fcac4dde381f541331ba5e46S3VersionKeyC495D2FD": { "Type": "String", - "Description": "S3 key for asset version \"1a697367cf267ba8a72f772b462c503dd5a6e7bcdd8cb353d89604e9d18802ee\"" + "Description": "S3 key for asset version \"2f7cfa4e9b9d7c70c9af6f1820a340c116a1e139fcac4dde381f541331ba5e46\"" }, - "AssetParameters1a697367cf267ba8a72f772b462c503dd5a6e7bcdd8cb353d89604e9d18802eeArtifactHash76BB0F66": { + "AssetParameters2f7cfa4e9b9d7c70c9af6f1820a340c116a1e139fcac4dde381f541331ba5e46ArtifactHashBBA8D438": { "Type": "String", - "Description": "Artifact hash for asset \"1a697367cf267ba8a72f772b462c503dd5a6e7bcdd8cb353d89604e9d18802ee\"" + "Description": "Artifact hash for asset \"2f7cfa4e9b9d7c70c9af6f1820a340c116a1e139fcac4dde381f541331ba5e46\"" }, - "AssetParameters8eb49c58869010ec84a5b407369006e9cb5fdf9667d8609638d6fa59265fc3ecS3Bucket5B706387": { + "AssetParameters7157876e6cb8bea4bc3b6e272edab8a4d202a0be6bdcecf33b95008fbacc2335S3Bucket1E236877": { "Type": "String", - "Description": "S3 bucket for asset \"8eb49c58869010ec84a5b407369006e9cb5fdf9667d8609638d6fa59265fc3ec\"" + "Description": "S3 bucket for asset \"7157876e6cb8bea4bc3b6e272edab8a4d202a0be6bdcecf33b95008fbacc2335\"" }, - "AssetParameters8eb49c58869010ec84a5b407369006e9cb5fdf9667d8609638d6fa59265fc3ecS3VersionKey48CF3C03": { + "AssetParameters7157876e6cb8bea4bc3b6e272edab8a4d202a0be6bdcecf33b95008fbacc2335S3VersionKeyD0D6DBBF": { "Type": "String", - "Description": "S3 key for asset version \"8eb49c58869010ec84a5b407369006e9cb5fdf9667d8609638d6fa59265fc3ec\"" + "Description": "S3 key for asset version \"7157876e6cb8bea4bc3b6e272edab8a4d202a0be6bdcecf33b95008fbacc2335\"" }, - "AssetParameters8eb49c58869010ec84a5b407369006e9cb5fdf9667d8609638d6fa59265fc3ecArtifactHashD17A32B8": { + "AssetParameters7157876e6cb8bea4bc3b6e272edab8a4d202a0be6bdcecf33b95008fbacc2335ArtifactHash3C95A517": { "Type": "String", - "Description": "Artifact hash for asset \"8eb49c58869010ec84a5b407369006e9cb5fdf9667d8609638d6fa59265fc3ec\"" + "Description": "Artifact hash for asset \"7157876e6cb8bea4bc3b6e272edab8a4d202a0be6bdcecf33b95008fbacc2335\"" }, - "AssetParameters992750d379dbbe94426dbd352099e3344b9edcfd098a56691b53eafaeb227b10S3Bucket9E9FE80F": { + "AssetParameters1d8b9cdbaaf7c36dc94604966b3b1f6fb2a972fbddc5cfd62fef7a7a8efd5888S3BucketC4EB0B32": { "Type": "String", - "Description": "S3 bucket for asset \"992750d379dbbe94426dbd352099e3344b9edcfd098a56691b53eafaeb227b10\"" + "Description": "S3 bucket for asset \"1d8b9cdbaaf7c36dc94604966b3b1f6fb2a972fbddc5cfd62fef7a7a8efd5888\"" }, - "AssetParameters992750d379dbbe94426dbd352099e3344b9edcfd098a56691b53eafaeb227b10S3VersionKeyFFAE128A": { + "AssetParameters1d8b9cdbaaf7c36dc94604966b3b1f6fb2a972fbddc5cfd62fef7a7a8efd5888S3VersionKeyB8755D2B": { "Type": "String", - "Description": "S3 key for asset version \"992750d379dbbe94426dbd352099e3344b9edcfd098a56691b53eafaeb227b10\"" + "Description": "S3 key for asset version \"1d8b9cdbaaf7c36dc94604966b3b1f6fb2a972fbddc5cfd62fef7a7a8efd5888\"" }, - "AssetParameters992750d379dbbe94426dbd352099e3344b9edcfd098a56691b53eafaeb227b10ArtifactHashCAE919EE": { + "AssetParameters1d8b9cdbaaf7c36dc94604966b3b1f6fb2a972fbddc5cfd62fef7a7a8efd5888ArtifactHash46327689": { "Type": "String", - "Description": "Artifact hash for asset \"992750d379dbbe94426dbd352099e3344b9edcfd098a56691b53eafaeb227b10\"" + "Description": "Artifact hash for asset \"1d8b9cdbaaf7c36dc94604966b3b1f6fb2a972fbddc5cfd62fef7a7a8efd5888\"" } }, "Outputs": { diff --git a/packages/@aws-cdk/aws-lambda-python/test/integ.function.poetry.expected.json b/packages/@aws-cdk/aws-lambda-python/test/integ.function.poetry.expected.json index 9b5e2aba2f075..6e305994bd801 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/integ.function.poetry.expected.json +++ b/packages/@aws-cdk/aws-lambda-python/test/integ.function.poetry.expected.json @@ -36,7 +36,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters75ddddb47479a218a15821c4afd818c51d26c8340cbbe49f6eca0b7b802e3923S3Bucket1A5FBFC5" + "Ref": "AssetParameters5e3cce416e15bd5ddb77e3ffc9fa3d8f5eac73b0db8c1db7ae3d7f6197c0ecb3S3Bucket666BD1AB" }, "S3Key": { "Fn::Join": [ @@ -49,7 +49,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters75ddddb47479a218a15821c4afd818c51d26c8340cbbe49f6eca0b7b802e3923S3VersionKey67EF2E81" + "Ref": "AssetParameters5e3cce416e15bd5ddb77e3ffc9fa3d8f5eac73b0db8c1db7ae3d7f6197c0ecb3S3VersionKeyA31B2B4A" } ] } @@ -62,7 +62,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters75ddddb47479a218a15821c4afd818c51d26c8340cbbe49f6eca0b7b802e3923S3VersionKey67EF2E81" + "Ref": "AssetParameters5e3cce416e15bd5ddb77e3ffc9fa3d8f5eac73b0db8c1db7ae3d7f6197c0ecb3S3VersionKeyA31B2B4A" } ] } @@ -121,7 +121,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters929810b8fc5030a232173abd44cd2d54f4735eece74ac8cf1c34e7b9dd9161a3S3Bucket0ECFE319" + "Ref": "AssetParameters12a01ed3f74f4bee61a4c67c4ca842472390f70c4b8bdd2cceb033abe16d7764S3BucketFA6FBCEA" }, "S3Key": { "Fn::Join": [ @@ -134,7 +134,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters929810b8fc5030a232173abd44cd2d54f4735eece74ac8cf1c34e7b9dd9161a3S3VersionKeyA373952D" + "Ref": "AssetParameters12a01ed3f74f4bee61a4c67c4ca842472390f70c4b8bdd2cceb033abe16d7764S3VersionKey55B7E38F" } ] } @@ -147,7 +147,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters929810b8fc5030a232173abd44cd2d54f4735eece74ac8cf1c34e7b9dd9161a3S3VersionKeyA373952D" + "Ref": "AssetParameters12a01ed3f74f4bee61a4c67c4ca842472390f70c4b8bdd2cceb033abe16d7764S3VersionKey55B7E38F" } ] } @@ -206,7 +206,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters5a08158e59eb223498febeed20bc4005c3e81534f6c47bd7d8a2079f256f25d0S3BucketDB10730C" + "Ref": "AssetParameters22d2bf270f7c8f776322a3bad39e8c690cbaa95a442ae2fec419b259df5632f0S3BucketC2F9D441" }, "S3Key": { "Fn::Join": [ @@ -219,7 +219,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters5a08158e59eb223498febeed20bc4005c3e81534f6c47bd7d8a2079f256f25d0S3VersionKeyE7AE1114" + "Ref": "AssetParameters22d2bf270f7c8f776322a3bad39e8c690cbaa95a442ae2fec419b259df5632f0S3VersionKeyB53188B8" } ] } @@ -232,7 +232,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters5a08158e59eb223498febeed20bc4005c3e81534f6c47bd7d8a2079f256f25d0S3VersionKeyE7AE1114" + "Ref": "AssetParameters22d2bf270f7c8f776322a3bad39e8c690cbaa95a442ae2fec419b259df5632f0S3VersionKeyB53188B8" } ] } @@ -257,41 +257,41 @@ } }, "Parameters": { - "AssetParameters75ddddb47479a218a15821c4afd818c51d26c8340cbbe49f6eca0b7b802e3923S3Bucket1A5FBFC5": { + "AssetParameters5e3cce416e15bd5ddb77e3ffc9fa3d8f5eac73b0db8c1db7ae3d7f6197c0ecb3S3Bucket666BD1AB": { "Type": "String", - "Description": "S3 bucket for asset \"75ddddb47479a218a15821c4afd818c51d26c8340cbbe49f6eca0b7b802e3923\"" + "Description": "S3 bucket for asset \"5e3cce416e15bd5ddb77e3ffc9fa3d8f5eac73b0db8c1db7ae3d7f6197c0ecb3\"" }, - "AssetParameters75ddddb47479a218a15821c4afd818c51d26c8340cbbe49f6eca0b7b802e3923S3VersionKey67EF2E81": { + "AssetParameters5e3cce416e15bd5ddb77e3ffc9fa3d8f5eac73b0db8c1db7ae3d7f6197c0ecb3S3VersionKeyA31B2B4A": { "Type": "String", - "Description": "S3 key for asset version \"75ddddb47479a218a15821c4afd818c51d26c8340cbbe49f6eca0b7b802e3923\"" + "Description": "S3 key for asset version \"5e3cce416e15bd5ddb77e3ffc9fa3d8f5eac73b0db8c1db7ae3d7f6197c0ecb3\"" }, - "AssetParameters75ddddb47479a218a15821c4afd818c51d26c8340cbbe49f6eca0b7b802e3923ArtifactHash122807F1": { + "AssetParameters5e3cce416e15bd5ddb77e3ffc9fa3d8f5eac73b0db8c1db7ae3d7f6197c0ecb3ArtifactHash8BEEBB0C": { "Type": "String", - "Description": "Artifact hash for asset \"75ddddb47479a218a15821c4afd818c51d26c8340cbbe49f6eca0b7b802e3923\"" + "Description": "Artifact hash for asset \"5e3cce416e15bd5ddb77e3ffc9fa3d8f5eac73b0db8c1db7ae3d7f6197c0ecb3\"" }, - "AssetParameters929810b8fc5030a232173abd44cd2d54f4735eece74ac8cf1c34e7b9dd9161a3S3Bucket0ECFE319": { + "AssetParameters12a01ed3f74f4bee61a4c67c4ca842472390f70c4b8bdd2cceb033abe16d7764S3BucketFA6FBCEA": { "Type": "String", - "Description": "S3 bucket for asset \"929810b8fc5030a232173abd44cd2d54f4735eece74ac8cf1c34e7b9dd9161a3\"" + "Description": "S3 bucket for asset \"12a01ed3f74f4bee61a4c67c4ca842472390f70c4b8bdd2cceb033abe16d7764\"" }, - "AssetParameters929810b8fc5030a232173abd44cd2d54f4735eece74ac8cf1c34e7b9dd9161a3S3VersionKeyA373952D": { + "AssetParameters12a01ed3f74f4bee61a4c67c4ca842472390f70c4b8bdd2cceb033abe16d7764S3VersionKey55B7E38F": { "Type": "String", - "Description": "S3 key for asset version \"929810b8fc5030a232173abd44cd2d54f4735eece74ac8cf1c34e7b9dd9161a3\"" + "Description": "S3 key for asset version \"12a01ed3f74f4bee61a4c67c4ca842472390f70c4b8bdd2cceb033abe16d7764\"" }, - "AssetParameters929810b8fc5030a232173abd44cd2d54f4735eece74ac8cf1c34e7b9dd9161a3ArtifactHash196D379D": { + "AssetParameters12a01ed3f74f4bee61a4c67c4ca842472390f70c4b8bdd2cceb033abe16d7764ArtifactHashF654A092": { "Type": "String", - "Description": "Artifact hash for asset \"929810b8fc5030a232173abd44cd2d54f4735eece74ac8cf1c34e7b9dd9161a3\"" + "Description": "Artifact hash for asset \"12a01ed3f74f4bee61a4c67c4ca842472390f70c4b8bdd2cceb033abe16d7764\"" }, - "AssetParameters5a08158e59eb223498febeed20bc4005c3e81534f6c47bd7d8a2079f256f25d0S3BucketDB10730C": { + "AssetParameters22d2bf270f7c8f776322a3bad39e8c690cbaa95a442ae2fec419b259df5632f0S3BucketC2F9D441": { "Type": "String", - "Description": "S3 bucket for asset \"5a08158e59eb223498febeed20bc4005c3e81534f6c47bd7d8a2079f256f25d0\"" + "Description": "S3 bucket for asset \"22d2bf270f7c8f776322a3bad39e8c690cbaa95a442ae2fec419b259df5632f0\"" }, - "AssetParameters5a08158e59eb223498febeed20bc4005c3e81534f6c47bd7d8a2079f256f25d0S3VersionKeyE7AE1114": { + "AssetParameters22d2bf270f7c8f776322a3bad39e8c690cbaa95a442ae2fec419b259df5632f0S3VersionKeyB53188B8": { "Type": "String", - "Description": "S3 key for asset version \"5a08158e59eb223498febeed20bc4005c3e81534f6c47bd7d8a2079f256f25d0\"" + "Description": "S3 key for asset version \"22d2bf270f7c8f776322a3bad39e8c690cbaa95a442ae2fec419b259df5632f0\"" }, - "AssetParameters5a08158e59eb223498febeed20bc4005c3e81534f6c47bd7d8a2079f256f25d0ArtifactHashF5CC0D13": { + "AssetParameters22d2bf270f7c8f776322a3bad39e8c690cbaa95a442ae2fec419b259df5632f0ArtifactHash63D0E537": { "Type": "String", - "Description": "Artifact hash for asset \"5a08158e59eb223498febeed20bc4005c3e81534f6c47bd7d8a2079f256f25d0\"" + "Description": "Artifact hash for asset \"22d2bf270f7c8f776322a3bad39e8c690cbaa95a442ae2fec419b259df5632f0\"" } }, "Outputs": { diff --git a/packages/@aws-cdk/aws-lambda-python/test/integ.function.project.expected.json b/packages/@aws-cdk/aws-lambda-python/test/integ.function.project.expected.json index 68838ca5bb2f0..0e27a16d95cac 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/integ.function.project.expected.json +++ b/packages/@aws-cdk/aws-lambda-python/test/integ.function.project.expected.json @@ -5,7 +5,7 @@ "Properties": { "Content": { "S3Bucket": { - "Ref": "AssetParameters66cf329731482a1903f36e710c64efa07dd7fc320739e9a1c0d915fe3cfa3aa0S3BucketF123FCA1" + "Ref": "AssetParameterse6dde8a412edfc6d967ce7244c803ebd6523ebc30b4c5562919028c6edf8f137S3Bucket3B546DC9" }, "S3Key": { "Fn::Join": [ @@ -18,7 +18,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters66cf329731482a1903f36e710c64efa07dd7fc320739e9a1c0d915fe3cfa3aa0S3VersionKey6FABFCA9" + "Ref": "AssetParameterse6dde8a412edfc6d967ce7244c803ebd6523ebc30b4c5562919028c6edf8f137S3VersionKeyAB88DB86" } ] } @@ -31,7 +31,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters66cf329731482a1903f36e710c64efa07dd7fc320739e9a1c0d915fe3cfa3aa0S3VersionKey6FABFCA9" + "Ref": "AssetParameterse6dde8a412edfc6d967ce7244c803ebd6523ebc30b4c5562919028c6edf8f137S3VersionKeyAB88DB86" } ] } @@ -82,7 +82,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters2355f1daf8ff0670c3287d5f2b9bf5061dce576b20f03dddb2f016ba7a203c86S3Bucket03A296A2" + "Ref": "AssetParameters4364b96840104e125d2c47166f8bada01e9a636f3c23d4cddf681c685d494856S3Bucket091DB419" }, "S3Key": { "Fn::Join": [ @@ -95,7 +95,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters2355f1daf8ff0670c3287d5f2b9bf5061dce576b20f03dddb2f016ba7a203c86S3VersionKey90B885D0" + "Ref": "AssetParameters4364b96840104e125d2c47166f8bada01e9a636f3c23d4cddf681c685d494856S3VersionKey635CFDCB" } ] } @@ -108,7 +108,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters2355f1daf8ff0670c3287d5f2b9bf5061dce576b20f03dddb2f016ba7a203c86S3VersionKey90B885D0" + "Ref": "AssetParameters4364b96840104e125d2c47166f8bada01e9a636f3c23d4cddf681c685d494856S3VersionKey635CFDCB" } ] } @@ -138,29 +138,29 @@ } }, "Parameters": { - "AssetParameters66cf329731482a1903f36e710c64efa07dd7fc320739e9a1c0d915fe3cfa3aa0S3BucketF123FCA1": { + "AssetParameterse6dde8a412edfc6d967ce7244c803ebd6523ebc30b4c5562919028c6edf8f137S3Bucket3B546DC9": { "Type": "String", - "Description": "S3 bucket for asset \"66cf329731482a1903f36e710c64efa07dd7fc320739e9a1c0d915fe3cfa3aa0\"" + "Description": "S3 bucket for asset \"e6dde8a412edfc6d967ce7244c803ebd6523ebc30b4c5562919028c6edf8f137\"" }, - "AssetParameters66cf329731482a1903f36e710c64efa07dd7fc320739e9a1c0d915fe3cfa3aa0S3VersionKey6FABFCA9": { + "AssetParameterse6dde8a412edfc6d967ce7244c803ebd6523ebc30b4c5562919028c6edf8f137S3VersionKeyAB88DB86": { "Type": "String", - "Description": "S3 key for asset version \"66cf329731482a1903f36e710c64efa07dd7fc320739e9a1c0d915fe3cfa3aa0\"" + "Description": "S3 key for asset version \"e6dde8a412edfc6d967ce7244c803ebd6523ebc30b4c5562919028c6edf8f137\"" }, - "AssetParameters66cf329731482a1903f36e710c64efa07dd7fc320739e9a1c0d915fe3cfa3aa0ArtifactHash0384F1C8": { + "AssetParameterse6dde8a412edfc6d967ce7244c803ebd6523ebc30b4c5562919028c6edf8f137ArtifactHashE6CFFE39": { "Type": "String", - "Description": "Artifact hash for asset \"66cf329731482a1903f36e710c64efa07dd7fc320739e9a1c0d915fe3cfa3aa0\"" + "Description": "Artifact hash for asset \"e6dde8a412edfc6d967ce7244c803ebd6523ebc30b4c5562919028c6edf8f137\"" }, - "AssetParameters2355f1daf8ff0670c3287d5f2b9bf5061dce576b20f03dddb2f016ba7a203c86S3Bucket03A296A2": { + "AssetParameters4364b96840104e125d2c47166f8bada01e9a636f3c23d4cddf681c685d494856S3Bucket091DB419": { "Type": "String", - "Description": "S3 bucket for asset \"2355f1daf8ff0670c3287d5f2b9bf5061dce576b20f03dddb2f016ba7a203c86\"" + "Description": "S3 bucket for asset \"4364b96840104e125d2c47166f8bada01e9a636f3c23d4cddf681c685d494856\"" }, - "AssetParameters2355f1daf8ff0670c3287d5f2b9bf5061dce576b20f03dddb2f016ba7a203c86S3VersionKey90B885D0": { + "AssetParameters4364b96840104e125d2c47166f8bada01e9a636f3c23d4cddf681c685d494856S3VersionKey635CFDCB": { "Type": "String", - "Description": "S3 key for asset version \"2355f1daf8ff0670c3287d5f2b9bf5061dce576b20f03dddb2f016ba7a203c86\"" + "Description": "S3 key for asset version \"4364b96840104e125d2c47166f8bada01e9a636f3c23d4cddf681c685d494856\"" }, - "AssetParameters2355f1daf8ff0670c3287d5f2b9bf5061dce576b20f03dddb2f016ba7a203c86ArtifactHash937218E0": { + "AssetParameters4364b96840104e125d2c47166f8bada01e9a636f3c23d4cddf681c685d494856ArtifactHashB70D4FAA": { "Type": "String", - "Description": "Artifact hash for asset \"2355f1daf8ff0670c3287d5f2b9bf5061dce576b20f03dddb2f016ba7a203c86\"" + "Description": "Artifact hash for asset \"4364b96840104e125d2c47166f8bada01e9a636f3c23d4cddf681c685d494856\"" } }, "Outputs": { diff --git a/packages/@aws-cdk/aws-lambda-python/test/integ.function.py38.expected.json b/packages/@aws-cdk/aws-lambda-python/test/integ.function.py38.expected.json index df601bc92acd9..41d154158be8a 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/integ.function.py38.expected.json +++ b/packages/@aws-cdk/aws-lambda-python/test/integ.function.py38.expected.json @@ -36,7 +36,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters29cd01a5fe529da311cdec40c163fb1c04b1b3dc8caadff0ea696fb6e63610feS3Bucket38F00249" + "Ref": "AssetParametersc3efdfac6089b6c2ee8dce3aac310085091823af614fce0fb5e42799930f526dS3Bucket2D6AE647" }, "S3Key": { "Fn::Join": [ @@ -49,7 +49,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters29cd01a5fe529da311cdec40c163fb1c04b1b3dc8caadff0ea696fb6e63610feS3VersionKey60957E7E" + "Ref": "AssetParametersc3efdfac6089b6c2ee8dce3aac310085091823af614fce0fb5e42799930f526dS3VersionKeyF8CA384F" } ] } @@ -62,7 +62,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters29cd01a5fe529da311cdec40c163fb1c04b1b3dc8caadff0ea696fb6e63610feS3VersionKey60957E7E" + "Ref": "AssetParametersc3efdfac6089b6c2ee8dce3aac310085091823af614fce0fb5e42799930f526dS3VersionKeyF8CA384F" } ] } @@ -87,17 +87,17 @@ } }, "Parameters": { - "AssetParameters29cd01a5fe529da311cdec40c163fb1c04b1b3dc8caadff0ea696fb6e63610feS3Bucket38F00249": { + "AssetParametersc3efdfac6089b6c2ee8dce3aac310085091823af614fce0fb5e42799930f526dS3Bucket2D6AE647": { "Type": "String", - "Description": "S3 bucket for asset \"29cd01a5fe529da311cdec40c163fb1c04b1b3dc8caadff0ea696fb6e63610fe\"" + "Description": "S3 bucket for asset \"c3efdfac6089b6c2ee8dce3aac310085091823af614fce0fb5e42799930f526d\"" }, - "AssetParameters29cd01a5fe529da311cdec40c163fb1c04b1b3dc8caadff0ea696fb6e63610feS3VersionKey60957E7E": { + "AssetParametersc3efdfac6089b6c2ee8dce3aac310085091823af614fce0fb5e42799930f526dS3VersionKeyF8CA384F": { "Type": "String", - "Description": "S3 key for asset version \"29cd01a5fe529da311cdec40c163fb1c04b1b3dc8caadff0ea696fb6e63610fe\"" + "Description": "S3 key for asset version \"c3efdfac6089b6c2ee8dce3aac310085091823af614fce0fb5e42799930f526d\"" }, - "AssetParameters29cd01a5fe529da311cdec40c163fb1c04b1b3dc8caadff0ea696fb6e63610feArtifactHash29C18922": { + "AssetParametersc3efdfac6089b6c2ee8dce3aac310085091823af614fce0fb5e42799930f526dArtifactHash8B92B092": { "Type": "String", - "Description": "Artifact hash for asset \"29cd01a5fe529da311cdec40c163fb1c04b1b3dc8caadff0ea696fb6e63610fe\"" + "Description": "Artifact hash for asset \"c3efdfac6089b6c2ee8dce3aac310085091823af614fce0fb5e42799930f526d\"" } }, "Outputs": { diff --git a/packages/@aws-cdk/aws-lambda-python/test/integ.function.requirements.removed.expected.json b/packages/@aws-cdk/aws-lambda-python/test/integ.function.requirements.removed.expected.json index 131ca14b5e5c9..eb7fc23f146c9 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/integ.function.requirements.removed.expected.json +++ b/packages/@aws-cdk/aws-lambda-python/test/integ.function.requirements.removed.expected.json @@ -36,7 +36,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameterse6fadc5eeabdb3ea6ecd571e8ec74e7943cd34cd4ec9d46d0506a200e6163a93S3BucketBA49B914" + "Ref": "AssetParametersdf0fb94d329926d232a09d16076d3eee0200e6a945f32ff69a97ba787087d563S3Bucket1ACC1E9E" }, "S3Key": { "Fn::Join": [ @@ -49,7 +49,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterse6fadc5eeabdb3ea6ecd571e8ec74e7943cd34cd4ec9d46d0506a200e6163a93S3VersionKey74688352" + "Ref": "AssetParametersdf0fb94d329926d232a09d16076d3eee0200e6a945f32ff69a97ba787087d563S3VersionKeyEA6BC868" } ] } @@ -62,7 +62,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterse6fadc5eeabdb3ea6ecd571e8ec74e7943cd34cd4ec9d46d0506a200e6163a93S3VersionKey74688352" + "Ref": "AssetParametersdf0fb94d329926d232a09d16076d3eee0200e6a945f32ff69a97ba787087d563S3VersionKeyEA6BC868" } ] } @@ -87,17 +87,17 @@ } }, "Parameters": { - "AssetParameterse6fadc5eeabdb3ea6ecd571e8ec74e7943cd34cd4ec9d46d0506a200e6163a93S3BucketBA49B914": { + "AssetParametersdf0fb94d329926d232a09d16076d3eee0200e6a945f32ff69a97ba787087d563S3Bucket1ACC1E9E": { "Type": "String", - "Description": "S3 bucket for asset \"e6fadc5eeabdb3ea6ecd571e8ec74e7943cd34cd4ec9d46d0506a200e6163a93\"" + "Description": "S3 bucket for asset \"df0fb94d329926d232a09d16076d3eee0200e6a945f32ff69a97ba787087d563\"" }, - "AssetParameterse6fadc5eeabdb3ea6ecd571e8ec74e7943cd34cd4ec9d46d0506a200e6163a93S3VersionKey74688352": { + "AssetParametersdf0fb94d329926d232a09d16076d3eee0200e6a945f32ff69a97ba787087d563S3VersionKeyEA6BC868": { "Type": "String", - "Description": "S3 key for asset version \"e6fadc5eeabdb3ea6ecd571e8ec74e7943cd34cd4ec9d46d0506a200e6163a93\"" + "Description": "S3 key for asset version \"df0fb94d329926d232a09d16076d3eee0200e6a945f32ff69a97ba787087d563\"" }, - "AssetParameterse6fadc5eeabdb3ea6ecd571e8ec74e7943cd34cd4ec9d46d0506a200e6163a93ArtifactHash6870D06D": { + "AssetParametersdf0fb94d329926d232a09d16076d3eee0200e6a945f32ff69a97ba787087d563ArtifactHash887C8025": { "Type": "String", - "Description": "Artifact hash for asset \"e6fadc5eeabdb3ea6ecd571e8ec74e7943cd34cd4ec9d46d0506a200e6163a93\"" + "Description": "Artifact hash for asset \"df0fb94d329926d232a09d16076d3eee0200e6a945f32ff69a97ba787087d563\"" } }, "Outputs": { diff --git a/packages/@aws-cdk/aws-lambda-python/test/integ.function.vpc.expected.json b/packages/@aws-cdk/aws-lambda-python/test/integ.function.vpc.expected.json index 1efa22d57d44f..99705bba839a3 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/integ.function.vpc.expected.json +++ b/packages/@aws-cdk/aws-lambda-python/test/integ.function.vpc.expected.json @@ -296,7 +296,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters5b8ff93384af5b488025e13b274c2dd894e474a810f1a406af1aeb4edbba6a3cS3Bucket5B146B0B" + "Ref": "AssetParametersfc7bfbf72c74b955f7bc25d2bb123c0eeec9557cda17481146d51672768907b2S3Bucket383ED51E" }, "S3Key": { "Fn::Join": [ @@ -309,7 +309,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters5b8ff93384af5b488025e13b274c2dd894e474a810f1a406af1aeb4edbba6a3cS3VersionKeyC0C8A627" + "Ref": "AssetParametersfc7bfbf72c74b955f7bc25d2bb123c0eeec9557cda17481146d51672768907b2S3VersionKeyA520554C" } ] } @@ -322,7 +322,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters5b8ff93384af5b488025e13b274c2dd894e474a810f1a406af1aeb4edbba6a3cS3VersionKeyC0C8A627" + "Ref": "AssetParametersfc7bfbf72c74b955f7bc25d2bb123c0eeec9557cda17481146d51672768907b2S3VersionKeyA520554C" } ] } @@ -368,17 +368,17 @@ } }, "Parameters": { - "AssetParameters5b8ff93384af5b488025e13b274c2dd894e474a810f1a406af1aeb4edbba6a3cS3Bucket5B146B0B": { + "AssetParametersfc7bfbf72c74b955f7bc25d2bb123c0eeec9557cda17481146d51672768907b2S3Bucket383ED51E": { "Type": "String", - "Description": "S3 bucket for asset \"5b8ff93384af5b488025e13b274c2dd894e474a810f1a406af1aeb4edbba6a3c\"" + "Description": "S3 bucket for asset \"fc7bfbf72c74b955f7bc25d2bb123c0eeec9557cda17481146d51672768907b2\"" }, - "AssetParameters5b8ff93384af5b488025e13b274c2dd894e474a810f1a406af1aeb4edbba6a3cS3VersionKeyC0C8A627": { + "AssetParametersfc7bfbf72c74b955f7bc25d2bb123c0eeec9557cda17481146d51672768907b2S3VersionKeyA520554C": { "Type": "String", - "Description": "S3 key for asset version \"5b8ff93384af5b488025e13b274c2dd894e474a810f1a406af1aeb4edbba6a3c\"" + "Description": "S3 key for asset version \"fc7bfbf72c74b955f7bc25d2bb123c0eeec9557cda17481146d51672768907b2\"" }, - "AssetParameters5b8ff93384af5b488025e13b274c2dd894e474a810f1a406af1aeb4edbba6a3cArtifactHashB6A7723E": { + "AssetParametersfc7bfbf72c74b955f7bc25d2bb123c0eeec9557cda17481146d51672768907b2ArtifactHashB863A6ED": { "Type": "String", - "Description": "Artifact hash for asset \"5b8ff93384af5b488025e13b274c2dd894e474a810f1a406af1aeb4edbba6a3c\"" + "Description": "Artifact hash for asset \"fc7bfbf72c74b955f7bc25d2bb123c0eeec9557cda17481146d51672768907b2\"" } }, "Outputs": { diff --git a/packages/@aws-cdk/aws-lambda/test/code.test.ts b/packages/@aws-cdk/aws-lambda/test/code.test.ts index 958dfe9fc7a56..ec9732baa9ea7 100644 --- a/packages/@aws-cdk/aws-lambda/test/code.test.ts +++ b/packages/@aws-cdk/aws-lambda/test/code.test.ts @@ -298,7 +298,7 @@ describe('code', () => { { Ref: 'AWS::Region' }, '.', { Ref: 'AWS::URLSuffix' }, - '/aws-cdk/assets:e8a944aeb0a08ba4811503d9c138e514b112dadca84daa5b4608e4a0fb80a0c9', + '/aws-cdk/assets:f0fe8a410cb4b860a25f6f3e09237abf69cd38ab59f9ef2441597c75f598c634', ]], }, }, diff --git a/packages/@aws-cdk/aws-lambda/test/integ.lambda.docker.expected.json b/packages/@aws-cdk/aws-lambda/test/integ.lambda.docker.expected.json index bfa1d27910000..6affea556c9d9 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.lambda.docker.expected.json +++ b/packages/@aws-cdk/aws-lambda/test/integ.lambda.docker.expected.json @@ -50,7 +50,7 @@ { "Ref": "AWS::URLSuffix" }, - "/aws-cdk/assets:e8a944aeb0a08ba4811503d9c138e514b112dadca84daa5b4608e4a0fb80a0c9" + "/aws-cdk/assets:f0fe8a410cb4b860a25f6f3e09237abf69cd38ab59f9ef2441597c75f598c634" ] ] } diff --git a/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts b/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts index 3a73b950792bf..990a1ec359d0b 100644 --- a/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts +++ b/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts @@ -196,8 +196,15 @@ export class BucketDeployment extends CoreConstruct { constructor(scope: Construct, id: string, props: BucketDeploymentProps) { super(scope, id); - if (props.distributionPaths && !props.distribution) { - throw new Error('Distribution must be specified if distribution paths are specified'); + if (props.distributionPaths) { + if (!props.distribution) { + throw new Error('Distribution must be specified if distribution paths are specified'); + } + if (!cdk.Token.isUnresolved(props.distributionPaths)) { + if (!props.distributionPaths.every(distributionPath => cdk.Token.isUnresolved(distributionPath) || distributionPath.startsWith('/'))) { + throw new Error('Distribution paths must start with "/"'); + } + } } const handler = new lambda.SingletonFunction(this, 'CustomResourceHandler', { diff --git a/packages/@aws-cdk/aws-s3-deployment/test/bucket-deployment.test.ts b/packages/@aws-cdk/aws-s3-deployment/test/bucket-deployment.test.ts index 2bbe5e34a41a6..19af404bbfa7b 100644 --- a/packages/@aws-cdk/aws-s3-deployment/test/bucket-deployment.test.ts +++ b/packages/@aws-cdk/aws-s3-deployment/test/bucket-deployment.test.ts @@ -225,7 +225,7 @@ test('honors passed asset options', () => { ], }, SourceBucketNames: [{ - Ref: 'AssetParameters86f8bca4f28a0bcafef0a98fe4cea25c0071aca27401e35cfaecd06313373bcaS3BucketB41AE64D', + Ref: 'AssetParametersa4d0f1d9c73aa029fd432ca3e640d46745f490023a241d0127f3351773a8938eS3Bucket02009982', }], SourceObjectKeys: [{ 'Fn::Join': [ @@ -238,7 +238,7 @@ test('honors passed asset options', () => { 'Fn::Split': [ '||', { - Ref: 'AssetParameters86f8bca4f28a0bcafef0a98fe4cea25c0071aca27401e35cfaecd06313373bcaS3VersionKeyF3CBA38F', + Ref: 'AssetParametersa4d0f1d9c73aa029fd432ca3e640d46745f490023a241d0127f3351773a8938eS3VersionKey07726F25', }, ], }, @@ -251,7 +251,7 @@ test('honors passed asset options', () => { 'Fn::Split': [ '||', { - Ref: 'AssetParameters86f8bca4f28a0bcafef0a98fe4cea25c0071aca27401e35cfaecd06313373bcaS3VersionKeyF3CBA38F', + Ref: 'AssetParametersa4d0f1d9c73aa029fd432ca3e640d46745f490023a241d0127f3351773a8938eS3VersionKey07726F25', }, ], }, @@ -491,6 +491,30 @@ test('fails if distribution paths provided but not distribution ID', () => { }); +test('fails if distribution paths don\'t start with "/"', () => { + // GIVEN + const stack = new cdk.Stack(); + const bucket = new s3.Bucket(stack, 'Dest'); + const distribution = new cloudfront.CloudFrontWebDistribution(stack, 'Distribution', { + originConfigs: [ + { + s3OriginSource: { + s3BucketSource: bucket, + }, + behaviors: [{ isDefaultBehavior: true }], + }, + ], + }); + + // THEN + expect(() => new s3deploy.BucketDeployment(stack, 'Deploy', { + sources: [s3deploy.Source.asset(path.join(__dirname, 'my-website.zip'))], + destinationBucket: bucket, + distribution, + distributionPaths: ['images/*'], + })).toThrow(/Distribution paths must start with "\/"/); +}); + testFutureBehavior('lambda execution role gets permissions to read from the source bucket and read/write in destination', s3GrantWriteCtx, cdk.App, (app) => { // GIVEN const stack = new cdk.Stack(app); diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/batch/integ.run-batch-job.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/batch/integ.run-batch-job.expected.json index 252eeef05dea4..97eea60b24dcc 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/batch/integ.run-batch-job.expected.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/batch/integ.run-batch-job.expected.json @@ -869,7 +869,7 @@ { "Ref": "AWS::URLSuffix" }, - "/aws-cdk/assets:10112d0d2c68a9d76297acc4623b1af0d5e9fd11b2226eee455e09c6fcf1b776" + "/aws-cdk/assets:3691f011ed75cf0fd05152b8fae3d684323da3da9eaf3be68cba18cb9def2562" ] ] }, diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/batch/integ.submit-job.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/batch/integ.submit-job.expected.json index 6b45fb3889e9b..2026e45ae3c4e 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/batch/integ.submit-job.expected.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/batch/integ.submit-job.expected.json @@ -869,7 +869,7 @@ { "Ref": "AWS::URLSuffix" }, - "/aws-cdk/assets:10112d0d2c68a9d76297acc4623b1af0d5e9fd11b2226eee455e09c6fcf1b776" + "/aws-cdk/assets:3691f011ed75cf0fd05152b8fae3d684323da3da9eaf3be68cba18cb9def2562" ] ] }, diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/ecs/integ.ec2-run-task.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/ecs/integ.ec2-run-task.expected.json index c67a1adcd7dd7..737459f21b68a 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/ecs/integ.ec2-run-task.expected.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/ecs/integ.ec2-run-task.expected.json @@ -501,7 +501,7 @@ { "Ref": "AWS::URLSuffix" }, - "/aws-cdk/assets:709fd91ba301f9b460ce1066dbc339f6a29bd4a07609ff98fb0e0faa475b650d" + "/aws-cdk/assets:3fc39b45c4fd074ceef5d0f8528b74fa7fe6e8fa0aa4a6ffe7fb5e016cf8dc04" ] ] }, diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/ecs/integ.ec2-task.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/ecs/integ.ec2-task.expected.json index 891777171b73c..bfd53444e81b3 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/ecs/integ.ec2-task.expected.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/ecs/integ.ec2-task.expected.json @@ -501,7 +501,7 @@ { "Ref": "AWS::URLSuffix" }, - "/aws-cdk/assets:709fd91ba301f9b460ce1066dbc339f6a29bd4a07609ff98fb0e0faa475b650d" + "/aws-cdk/assets:3fc39b45c4fd074ceef5d0f8528b74fa7fe6e8fa0aa4a6ffe7fb5e016cf8dc04" ] ] }, diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/ecs/integ.fargate-run-task.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/ecs/integ.fargate-run-task.expected.json index a2d79417c61df..ab2cb02fd8d0c 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/ecs/integ.fargate-run-task.expected.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/ecs/integ.fargate-run-task.expected.json @@ -34,7 +34,7 @@ { "Ref": "AWS::URLSuffix" }, - "/aws-cdk/assets:709fd91ba301f9b460ce1066dbc339f6a29bd4a07609ff98fb0e0faa475b650d" + "/aws-cdk/assets:3fc39b45c4fd074ceef5d0f8528b74fa7fe6e8fa0aa4a6ffe7fb5e016cf8dc04" ] ] }, diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/ecs/integ.fargate-task.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/ecs/integ.fargate-task.expected.json index 00682997b2a31..84c678d4c2806 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/ecs/integ.fargate-task.expected.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/ecs/integ.fargate-task.expected.json @@ -34,7 +34,7 @@ { "Ref": "AWS::URLSuffix" }, - "/aws-cdk/assets:709fd91ba301f9b460ce1066dbc339f6a29bd4a07609ff98fb0e0faa475b650d" + "/aws-cdk/assets:3fc39b45c4fd074ceef5d0f8528b74fa7fe6e8fa0aa4a6ffe7fb5e016cf8dc04" ] ] }, diff --git a/packages/@aws-cdk/cfnspec/CHANGELOG.md b/packages/@aws-cdk/cfnspec/CHANGELOG.md index cbadd0a7eda90..716c917940793 100644 --- a/packages/@aws-cdk/cfnspec/CHANGELOG.md +++ b/packages/@aws-cdk/cfnspec/CHANGELOG.md @@ -1,3 +1,41 @@ +# CloudFormation Resource Specification v39.9.0 + +## New Resource Types + +* AWS::Athena::PreparedStatement + +## Attribute Changes + +* AWS::ApiGateway::Resource ResourceId (__added__) +* AWS::Athena::WorkGroup WorkGroupConfigurationUpdates.EngineVersion.EffectiveEngineVersion (__added__) +* AWS::MWAA::Environment LoggingConfiguration.SchedulerLogs.CloudWatchLogGroupArn (__added__) +* AWS::MWAA::Environment LoggingConfiguration.TaskLogs.CloudWatchLogGroupArn (__added__) +* AWS::MWAA::Environment LoggingConfiguration.WebserverLogs.CloudWatchLogGroupArn (__added__) +* AWS::MWAA::Environment LoggingConfiguration.WorkerLogs.CloudWatchLogGroupArn (__added__) + +## Property Changes + +* AWS::AppSync::GraphQLApi LambdaAuthorizerConfig (__added__) +* AWS::LookoutEquipment::InferenceScheduler ModelName.UpdateType (__changed__) + * Old: Mutable + * New: Immutable +* AWS::LookoutEquipment::InferenceScheduler ServerSideKmsKeyId.UpdateType (__changed__) + * Old: Mutable + * New: Immutable +* AWS::S3::AccessPoint Name (__added__) +* AWS::Synthetics::Canary VisualReference (__added__) + +## Property Type Changes + +* AWS::AppSync::GraphQLApi.LambdaAuthorizerConfig (__added__) +* AWS::SageMaker::Model.RepositoryAuthConfig (__added__) +* AWS::Synthetics::Canary.BaseScreenshot (__added__) +* AWS::Synthetics::Canary.VisualReference (__added__) +* AWS::AppSync::GraphQLApi.AdditionalAuthenticationProvider LambdaAuthorizerConfig (__added__) +* AWS::IoTSiteWise::AssetModel.TumblingWindow Offset (__added__) +* AWS::SageMaker::Model.ImageConfig RepositoryAuthConfig (__added__) + + # CloudFormation Resource Specification v39.8.0 ## New Resource Types diff --git a/packages/@aws-cdk/cfnspec/cfn.version b/packages/@aws-cdk/cfnspec/cfn.version index 212ba4596ef15..ff93cfe146d81 100644 --- a/packages/@aws-cdk/cfnspec/cfn.version +++ b/packages/@aws-cdk/cfnspec/cfn.version @@ -1 +1 @@ -39.8.0 +39.9.0 diff --git a/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json b/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json index 5e6a900098e55..2d71e22ab1083 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json +++ b/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json @@ -7114,6 +7114,12 @@ "Required": true, "UpdateType": "Mutable" }, + "LambdaAuthorizerConfig": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appsync-graphqlapi-additionalauthenticationprovider.html#cfn-appsync-graphqlapi-additionalauthenticationprovider-lambdaauthorizerconfig", + "Required": false, + "Type": "LambdaAuthorizerConfig", + "UpdateType": "Mutable" + }, "OpenIDConnectConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appsync-graphqlapi-additionalauthenticationprovider.html#cfn-appsync-graphqlapi-additionalauthenticationprovider-openidconnectconfig", "Required": false, @@ -7158,6 +7164,29 @@ } } }, + "AWS::AppSync::GraphQLApi.LambdaAuthorizerConfig": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appsync-graphqlapi-lambdaauthorizerconfig.html", + "Properties": { + "AuthorizerResultTtlInSeconds": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appsync-graphqlapi-lambdaauthorizerconfig.html#cfn-appsync-graphqlapi-lambdaauthorizerconfig-authorizerresultttlinseconds", + "PrimitiveType": "Double", + "Required": false, + "UpdateType": "Mutable" + }, + "AuthorizerUri": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appsync-graphqlapi-lambdaauthorizerconfig.html#cfn-appsync-graphqlapi-lambdaauthorizerconfig-authorizeruri", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "IdentityValidationExpression": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appsync-graphqlapi-lambdaauthorizerconfig.html#cfn-appsync-graphqlapi-lambdaauthorizerconfig-identityvalidationexpression", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, "AWS::AppSync::GraphQLApi.LogConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appsync-graphqlapi-logconfig.html", "Properties": { @@ -35861,6 +35890,12 @@ "PrimitiveType": "String", "Required": true, "UpdateType": "Mutable" + }, + "Offset": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iotsitewise-assetmodel-tumblingwindow.html#cfn-iotsitewise-assetmodel-tumblingwindow-offset", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" } } }, @@ -57003,6 +57038,12 @@ "PrimitiveType": "String", "Required": true, "UpdateType": "Immutable" + }, + "RepositoryAuthConfig": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sagemaker-model-containerdefinition-imageconfig.html#cfn-sagemaker-model-containerdefinition-imageconfig-repositoryauthconfig", + "Required": false, + "Type": "RepositoryAuthConfig", + "UpdateType": "Immutable" } } }, @@ -57028,6 +57069,17 @@ } } }, + "AWS::SageMaker::Model.RepositoryAuthConfig": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sagemaker-model-containerdefinition-imageconfig-repositoryauthconfig.html", + "Properties": { + "RepositoryCredentialsProviderArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sagemaker-model-containerdefinition-imageconfig-repositoryauthconfig.html#cfn-sagemaker-model-containerdefinition-imageconfig-repositoryauthconfig-repositorycredentialsproviderarn", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + } + } + }, "AWS::SageMaker::Model.VpcConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sagemaker-model-vpcconfig.html", "Properties": { @@ -59046,6 +59098,24 @@ } } }, + "AWS::Synthetics::Canary.BaseScreenshot": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-synthetics-canary-basescreenshot.html", + "Properties": { + "IgnoreCoordinates": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-synthetics-canary-basescreenshot.html#cfn-synthetics-canary-basescreenshot-ignorecoordinates", + "PrimitiveItemType": "String", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "ScreenshotName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-synthetics-canary-basescreenshot.html#cfn-synthetics-canary-basescreenshot-screenshotname", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, "AWS::Synthetics::Canary.Code": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-synthetics-canary-code.html", "Properties": { @@ -59153,6 +59223,24 @@ } } }, + "AWS::Synthetics::Canary.VisualReference": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-synthetics-canary-visualreference.html", + "Properties": { + "BaseCanaryRunId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-synthetics-canary-visualreference.html#cfn-synthetics-canary-visualreference-basecanaryrunid", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "BaseScreenshots": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-synthetics-canary-visualreference.html#cfn-synthetics-canary-visualreference-basescreenshots", + "ItemType": "BaseScreenshot", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, "AWS::Transfer::Server.EndpointDetails": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-transfer-server-endpointdetails.html", "Properties": { @@ -61525,7 +61613,7 @@ } } }, - "ResourceSpecificationVersion": "39.8.0", + "ResourceSpecificationVersion": "39.9.0", "ResourceTypes": { "AWS::ACMPCA::Certificate": { "Attributes": { @@ -62750,6 +62838,11 @@ } }, "AWS::ApiGateway::Resource": { + "Attributes": { + "ResourceId": { + "PrimitiveType": "String" + } + }, "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-resource.html", "Properties": { "ParentId": { @@ -65302,6 +65395,12 @@ "Required": true, "UpdateType": "Mutable" }, + "LambdaAuthorizerConfig": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-appsync-graphqlapi.html#cfn-appsync-graphqlapi-lambdaauthorizerconfig", + "Required": false, + "Type": "LambdaAuthorizerConfig", + "UpdateType": "Mutable" + }, "LogConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-appsync-graphqlapi.html#cfn-appsync-graphqlapi-logconfig", "Required": false, @@ -65704,6 +65803,35 @@ } } }, + "AWS::Athena::PreparedStatement": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-athena-preparedstatement.html", + "Properties": { + "Description": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-athena-preparedstatement.html#cfn-athena-preparedstatement-description", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "QueryStatement": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-athena-preparedstatement.html#cfn-athena-preparedstatement-querystatement", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "StatementName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-athena-preparedstatement.html#cfn-athena-preparedstatement-statementname", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "WorkGroup": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-athena-preparedstatement.html#cfn-athena-preparedstatement-workgroup", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + } + } + }, "AWS::Athena::WorkGroup": { "Attributes": { "CreationTime": { @@ -65711,6 +65839,9 @@ }, "WorkGroupConfiguration.EngineVersion.EffectiveEngineVersion": { "PrimitiveType": "String" + }, + "WorkGroupConfigurationUpdates.EngineVersion.EffectiveEngineVersion": { + "PrimitiveType": "String" } }, "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-athena-workgroup.html", @@ -86872,7 +87003,7 @@ "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lookoutequipment-inferencescheduler.html#cfn-lookoutequipment-inferencescheduler-modelname", "PrimitiveType": "String", "Required": true, - "UpdateType": "Mutable" + "UpdateType": "Immutable" }, "RoleArn": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lookoutequipment-inferencescheduler.html#cfn-lookoutequipment-inferencescheduler-rolearn", @@ -86884,7 +87015,7 @@ "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lookoutequipment-inferencescheduler.html#cfn-lookoutequipment-inferencescheduler-serversidekmskeyid", "PrimitiveType": "String", "Required": false, - "UpdateType": "Mutable" + "UpdateType": "Immutable" }, "Tags": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lookoutequipment-inferencescheduler.html#cfn-lookoutequipment-inferencescheduler-tags", @@ -87072,6 +87203,18 @@ "LoggingConfiguration.DagProcessingLogs.CloudWatchLogGroupArn": { "PrimitiveType": "String" }, + "LoggingConfiguration.SchedulerLogs.CloudWatchLogGroupArn": { + "PrimitiveType": "String" + }, + "LoggingConfiguration.TaskLogs.CloudWatchLogGroupArn": { + "PrimitiveType": "String" + }, + "LoggingConfiguration.WebserverLogs.CloudWatchLogGroupArn": { + "PrimitiveType": "String" + }, + "LoggingConfiguration.WorkerLogs.CloudWatchLogGroupArn": { + "PrimitiveType": "String" + }, "WebserverUrl": { "PrimitiveType": "String" } @@ -94306,6 +94449,12 @@ "Required": true, "UpdateType": "Immutable" }, + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3-accesspoint.html#cfn-s3-accesspoint-name", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, "Policy": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3-accesspoint.html#cfn-s3-accesspoint-policy", "PrimitiveType": "Json", @@ -98539,6 +98688,12 @@ "Required": false, "Type": "VPCConfig", "UpdateType": "Mutable" + }, + "VisualReference": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-synthetics-canary.html#cfn-synthetics-canary-visualreference", + "Required": false, + "Type": "VisualReference", + "UpdateType": "Mutable" } } }, diff --git a/packages/@aws-cdk/core/lib/asset-staging.ts b/packages/@aws-cdk/core/lib/asset-staging.ts index 89f7257ae522a..c948bc377c9a6 100644 --- a/packages/@aws-cdk/core/lib/asset-staging.ts +++ b/packages/@aws-cdk/core/lib/asset-staging.ts @@ -428,16 +428,6 @@ export class AssetStaging extends CoreConstruct { // Chmod the bundleDir to full access. fs.chmodSync(bundleDir, 0o777); - let user: string; - if (options.user) { - user = options.user; - } else { // Default to current user - const userInfo = os.userInfo(); - user = userInfo.uid !== -1 // uid is -1 on Windows - ? `${userInfo.uid}:${userInfo.gid}` - : '1000:1000'; - } - // Always mount input and output dir const volumes = [ { @@ -457,6 +447,16 @@ export class AssetStaging extends CoreConstruct { localBundling = options.local?.tryBundle(bundleDir, options); if (!localBundling) { + let user: string; + if (options.user) { + user = options.user; + } else { // Default to current user + const userInfo = os.userInfo(); + user = userInfo.uid !== -1 // uid is -1 on Windows + ? `${userInfo.uid}:${userInfo.gid}` + : '1000:1000'; + } + options.image.run({ command: options.command, user, diff --git a/packages/@aws-cdk/core/lib/context-provider.ts b/packages/@aws-cdk/core/lib/context-provider.ts index 1eb210623558f..526414a9cf790 100644 --- a/packages/@aws-cdk/core/lib/context-provider.ts +++ b/packages/@aws-cdk/core/lib/context-provider.ts @@ -84,9 +84,9 @@ export class ContextProvider { if (Token.isUnresolved(stack.account) || Token.isUnresolved(stack.region)) { throw new Error(`Cannot retrieve value from context provider ${options.provider} since account/region ` + - 'are not specified at the stack level. Either configure "env" with explicit account and region when ' + - 'you define your stack, or use the environment variables "CDK_DEFAULT_ACCOUNT" and "CDK_DEFAULT_REGION" ' + - 'to inherit environment information from the CLI (not recommended for production stacks)'); + 'are not specified at the stack level. Configure "env" with an account and region when ' + + 'you define your stack.' + + 'See https://docs.aws.amazon.com/cdk/latest/guide/environments.html for more details.'); } const { key, props } = this.getKey(scope, options); diff --git a/packages/@aws-cdk/core/lib/fs/fingerprint.ts b/packages/@aws-cdk/core/lib/fs/fingerprint.ts index f7513d89743be..50a81fd53982d 100644 --- a/packages/@aws-cdk/core/lib/fs/fingerprint.ts +++ b/packages/@aws-cdk/core/lib/fs/fingerprint.ts @@ -30,10 +30,6 @@ export function fingerprint(fileOrDirectory: string, options: FingerprintOptions const rootDirectory = fs.statSync(fileOrDirectory).isDirectory() ? fileOrDirectory : path.dirname(fileOrDirectory); - const exclude = options.exclude || []; - if (exclude.length) { - _hashField(hash, 'options.exclude', JSON.stringify(exclude)); - } const ignoreMode = options.ignoreMode || IgnoreMode.GLOB; if (ignoreMode != IgnoreMode.GLOB) { diff --git a/packages/@aws-cdk/core/lib/stack.ts b/packages/@aws-cdk/core/lib/stack.ts index a74a82e55ccd6..ee80e56334101 100644 --- a/packages/@aws-cdk/core/lib/stack.ts +++ b/packages/@aws-cdk/core/lib/stack.ts @@ -785,7 +785,7 @@ export class Stack extends CoreConstruct implements ITaggable { const numberOfResources = Object.keys(resources).length; if (numberOfResources > this.maxResources) { - throw new Error(`Number of resources: ${numberOfResources} is greater than allowed maximum of ${this.maxResources}`); + throw new Error(`Number of resources in stack '${this.node.path}': ${numberOfResources} is greater than allowed maximum of ${this.maxResources}`); } else if (numberOfResources >= (this.maxResources * 0.8)) { Annotations.of(this).addInfo(`Number of resources: ${numberOfResources} is approaching allowed maximum of ${this.maxResources}`); } diff --git a/packages/@aws-cdk/core/test/fs/fs-fingerprint.test.ts b/packages/@aws-cdk/core/test/fs/fs-fingerprint.test.ts index ad5bf9cf14160..6a589b3ba159e 100644 --- a/packages/@aws-cdk/core/test/fs/fs-fingerprint.test.ts +++ b/packages/@aws-cdk/core/test/fs/fs-fingerprint.test.ts @@ -155,34 +155,4 @@ nodeunitShim({ test.done(); }, }, - exclude: { - 'encodes exclude patterns'(test: Test) { - // GIVEN - const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'fingerprint-tests')); - const options1 = { path: dir, exclude: ['**', '!file.py'], sourcePath: dir }; - const options2 = { path: dir, exclude: ['**', '!otherfile.py'], sourcePath: dir }; - - // WHEN - const f1 = FileSystem.fingerprint(dir, options1); - const f2 = FileSystem.fingerprint(dir, options2); - - // THEN - test.notDeepEqual(f1, f2); - test.done(); - }, - 'considers negated exclude patterns for fingerprint'(test: Test) { - // GIVEN - const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'fingerprint-tests')); - const options = { path: dir, exclude: ['**', '!file.txt'], sourcePath: dir }; - - // WHEN - const f1 = FileSystem.fingerprint(dir, options); - fs.writeFileSync(path.join(dir, 'file.txt'), 'data'); - const f2 = FileSystem.fingerprint(dir, options); - - // THEN - test.notDeepEqual(f1, f2); - test.done(); - }, - }, }); diff --git a/packages/@aws-cdk/core/test/stack.test.ts b/packages/@aws-cdk/core/test/stack.test.ts index 11a4a01796031..4473b99e0c2dc 100644 --- a/packages/@aws-cdk/core/test/stack.test.ts +++ b/packages/@aws-cdk/core/test/stack.test.ts @@ -59,7 +59,7 @@ describe('stack', () => { expect(() => { app.synth(); - }).toThrow('Number of resources: 1000 is greater than allowed maximum of 500'); + }).toThrow('Number of resources in stack \'MyStack\': 1000 is greater than allowed maximum of 500'); }); @@ -81,7 +81,7 @@ describe('stack', () => { expect(() => { app.synth(); - }).toThrow('Number of resources: 200 is greater than allowed maximum of 100'); + }).toThrow('Number of resources in stack \'MyStack\': 200 is greater than allowed maximum of 100'); }); diff --git a/packages/@aws-cdk/pipelines/lib/codepipeline/_codebuild-factory.ts b/packages/@aws-cdk/pipelines/lib/codepipeline/_codebuild-factory.ts index c4f2eb41ab3fd..ac125aee29f0d 100644 --- a/packages/@aws-cdk/pipelines/lib/codepipeline/_codebuild-factory.ts +++ b/packages/@aws-cdk/pipelines/lib/codepipeline/_codebuild-factory.ts @@ -326,12 +326,12 @@ function generateInputArtifactLinkCommands(artifacts: ArtifactMap, inputs: FileS const fragments = []; if (!['.', '..'].includes(path.dirname(input.directory))) { - fragments.push(`mkdir -p "${input.directory}"`); + fragments.push(`mkdir -p -- "${input.directory}"`); } const artifact = artifacts.toCodePipeline(input.fileSet); - fragments.push(`ln -s "$CODEBUILD_SRC_DIR_${artifact.artifactName}" "${input.directory}"`); + fragments.push(`ln -s -- "$CODEBUILD_SRC_DIR_${artifact.artifactName}" "${input.directory}"`); return fragments.join(' && '); }); diff --git a/packages/@aws-cdk/pipelines/lib/codepipeline/artifact-map.ts b/packages/@aws-cdk/pipelines/lib/codepipeline/artifact-map.ts index 4ec4b086815c7..2d3383bb469bf 100644 --- a/packages/@aws-cdk/pipelines/lib/codepipeline/artifact-map.ts +++ b/packages/@aws-cdk/pipelines/lib/codepipeline/artifact-map.ts @@ -1,3 +1,4 @@ +import * as crypto from 'crypto'; import * as cp from '@aws-cdk/aws-codepipeline'; import { FileSet } from '../blueprint'; import { PipelineGraph } from '../helpers-internal'; @@ -38,9 +39,27 @@ export class ArtifactMap { } } +/** + * Sanitize a string to be a valid artifact name + * + * This must comport to both the rules of artifacts in CodePipeline, as well + * as the names of Source Identifiers in CodeBuild. + * + * Artifact Name limits aren't documented. + * + * Source Identifier limits are documented here: + * https://docs.aws.amazon.com/codebuild/latest/APIReference/API_ProjectSource.html#CodeBuild-Type-ProjectSource-sourceIdentifier + */ function sanitizeArtifactName(x: string): string { - // FIXME: Does this REALLY not allow '.'? The docs don't mention it, but action names etc. do! - return x.replace(/[^A-Za-z0-9@\-_]/g, '_'); + let sani = x.replace(/[^A-Za-z0-9_]/g, '_'); // Charset requirement is imposed by CodeBuild + const maxLength = 100; // Max length of 100 is imposed by CodePipeline library + + if (sani.length > maxLength) { + const fingerprint = crypto.createHash('sha256').update(sani).digest('hex').substr(0, 8); + sani = sani.substr(0, maxLength - fingerprint.length) + fingerprint; + } + + return sani; } /** diff --git a/packages/@aws-cdk/pipelines/lib/codepipeline/codebuild-step.ts b/packages/@aws-cdk/pipelines/lib/codepipeline/codebuild-step.ts index 1454d34a167c3..3047b43c5c755 100644 --- a/packages/@aws-cdk/pipelines/lib/codepipeline/codebuild-step.ts +++ b/packages/@aws-cdk/pipelines/lib/codepipeline/codebuild-step.ts @@ -166,7 +166,7 @@ export class CodeBuildStep extends ShellStep { */ public get project(): codebuild.IProject { if (!this._project) { - throw new Error('Project becomes available after the pipeline has been built'); + throw new Error('Call pipeline.buildPipeline() before reading this property'); } return this._project; } diff --git a/packages/@aws-cdk/pipelines/lib/codepipeline/codepipeline.ts b/packages/@aws-cdk/pipelines/lib/codepipeline/codepipeline.ts index 27a2b15509532..7b1d0d87d9c22 100644 --- a/packages/@aws-cdk/pipelines/lib/codepipeline/codepipeline.ts +++ b/packages/@aws-cdk/pipelines/lib/codepipeline/codepipeline.ts @@ -830,7 +830,7 @@ enum CodeBuildProjectType { function actionName(node: GraphNode, parent: GraphNode) { const names = node.ancestorPath(parent).map(n => n.id); - return names.map(sanitizeName).join('.'); + return names.map(sanitizeName).join('.').substr(0, 100); // Cannot exceed 100 chars } function sanitizeName(x: string): string { diff --git a/packages/@aws-cdk/pipelines/test/codepipeline/codepipeline-sources.test.ts b/packages/@aws-cdk/pipelines/test/codepipeline/codepipeline-sources.test.ts index 72553c38262d3..84aef10bb171c 100644 --- a/packages/@aws-cdk/pipelines/test/codepipeline/codepipeline-sources.test.ts +++ b/packages/@aws-cdk/pipelines/test/codepipeline/codepipeline-sources.test.ts @@ -1,4 +1,4 @@ -import { anything, arrayWith, objectLike } from '@aws-cdk/assert-internal'; +import { anything, arrayWith, Capture, objectLike } from '@aws-cdk/assert-internal'; import '@aws-cdk/assert-internal/jest'; import * as ccommit from '@aws-cdk/aws-codecommit'; import { CodeCommitTrigger, GitHubTrigger } from '@aws-cdk/aws-codepipeline-actions'; @@ -140,5 +140,43 @@ test('GitHub source does not accept unresolved identifiers', () => { }).toThrow(/Step id cannot be unresolved/); }); +test('Dashes in repo names are removed from artifact names', () => { + new ModernTestGitHubNpmPipeline(pipelineStack, 'Pipeline', { + input: cdkp.CodePipelineSource.gitHub('owner/my-repo', 'main'), + }); + + expect(pipelineStack).toHaveResourceLike('AWS::CodePipeline::Pipeline', { + Stages: arrayWith({ + Name: 'Source', + Actions: [ + objectLike({ + OutputArtifacts: [ + { Name: 'owner_my_repo_Source' }, + ], + }), + ], + }), + }); +}); + +test('artifact names are never longer than 128 characters', () => { + new ModernTestGitHubNpmPipeline(pipelineStack, 'Pipeline', { + input: cdkp.CodePipelineSource.gitHub('owner/' + 'my-repo'.repeat(100), 'main'), + }); + + const artifactId = Capture.aString(); + expect(pipelineStack).toHaveResourceLike('AWS::CodePipeline::Pipeline', { + Stages: arrayWith({ + Name: 'Source', + Actions: [ + objectLike({ + OutputArtifacts: [ + { Name: artifactId.capture() }, + ], + }), + ], + }), + }); -// a-z0-9.@-_ \ No newline at end of file + expect(artifactId.capturedValue.length).toBeLessThanOrEqual(128); +}); \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/compliance/synths.test.ts b/packages/@aws-cdk/pipelines/test/compliance/synths.test.ts index 247cbd50974e0..58bae441ee156 100644 --- a/packages/@aws-cdk/pipelines/test/compliance/synths.test.ts +++ b/packages/@aws-cdk/pipelines/test/compliance/synths.test.ts @@ -948,8 +948,8 @@ behavior('Multiple input sources in side-by-side directories', (suite) => { phases: { install: { commands: [ - 'ln -s "$CODEBUILD_SRC_DIR_foo_bar_Source" "../sibling"', - 'ln -s "$CODEBUILD_SRC_DIR_Prebuild_Output" "sub"', + 'ln -s -- "$CODEBUILD_SRC_DIR_foo_bar_Source" "../sibling"', + 'ln -s -- "$CODEBUILD_SRC_DIR_Prebuild_Output" "sub"', ], }, build: { diff --git a/packages/@aws-cdk/pipelines/test/integ.newpipeline-with-vpc.expected.json b/packages/@aws-cdk/pipelines/test/integ.newpipeline-with-vpc.expected.json index 7a937f57d6a8e..e0075942e6069 100644 --- a/packages/@aws-cdk/pipelines/test/integ.newpipeline-with-vpc.expected.json +++ b/packages/@aws-cdk/pipelines/test/integ.newpipeline-with-vpc.expected.json @@ -766,7 +766,7 @@ "Name": "rix0rrr_cdk-pipelines-demo", "OutputArtifacts": [ { - "Name": "rix0rrr_cdk-pipelines-demo_Source" + "Name": "rix0rrr_cdk_pipelines_demo_Source" } ], "RunOrder": 1 @@ -791,7 +791,7 @@ }, "InputArtifacts": [ { - "Name": "rix0rrr_cdk-pipelines-demo_Source" + "Name": "rix0rrr_cdk_pipelines_demo_Source" } ], "Name": "Synth", diff --git a/packages/@aws-cdk/pipelines/test/integ.newpipeline.expected.json b/packages/@aws-cdk/pipelines/test/integ.newpipeline.expected.json index 96ffa58af1c62..07e65b9bd643f 100644 --- a/packages/@aws-cdk/pipelines/test/integ.newpipeline.expected.json +++ b/packages/@aws-cdk/pipelines/test/integ.newpipeline.expected.json @@ -232,7 +232,7 @@ "Name": "rix0rrr_cdk-pipelines-demo", "OutputArtifacts": [ { - "Name": "rix0rrr_cdk-pipelines-demo_Source" + "Name": "rix0rrr_cdk_pipelines_demo_Source" } ], "RunOrder": 1 @@ -257,7 +257,7 @@ }, "InputArtifacts": [ { - "Name": "rix0rrr_cdk-pipelines-demo_Source" + "Name": "rix0rrr_cdk_pipelines_demo_Source" } ], "Name": "Synth", diff --git a/scripts/bump.js b/scripts/bump.js index a0a05f398669f..b7e364bc0ab16 100755 --- a/scripts/bump.js +++ b/scripts/bump.js @@ -67,6 +67,9 @@ async function main() { // this is incredible, but passing this option to standard-version actually makes it crash! // good thing we're getting rid of it... opts.verbose = !!process.env.VERBOSE; + // Rename some options to match cdk-release inputs (replaces bumpFiles, packageFiles, and infile) + opts.versionFile = ver.versionFile; + opts.changelogFile = ver.changelogFile; console.error("🎉 Calling our 'cdk-release' package to make the bump"); console.error("ℹī¸ Set the LEGACY_BUMP env variable to use the old 'standard-version' bump instead"); const cdkRelease = require('cdk-release'); diff --git a/scripts/check-yarn-lock.js b/scripts/check-yarn-lock.js index d13e4e08cf64f..8fd59b5a60bc8 100755 --- a/scripts/check-yarn-lock.js +++ b/scripts/check-yarn-lock.js @@ -40,13 +40,12 @@ async function main() { const dependencyId = `${dependencyName}@${dependencyVersion}`; const isLocalDependency = dependencyVersion === '0.0.0' || dependencyVersion === '^0.0.0'; if (!isLocalDependency && !yarnPackages.has(dependencyId)) { - throw new Error(`ERROR! Dependency ${dependencyId} from ${package.name} not present in yarn.lock. Please run yarn update and try again!`); + throw new Error(`ERROR! Dependency ${dependencyId} from ${package.name} not present in yarn.lock. Please run 'yarn install' and try again!`); } } projects.forEach((p) => { Object.entries(p.devDependencies || {}).forEach(([depName, depVersion]) => errorIfNotInYarnLock(p, depName, depVersion)); - Object.entries(p.peerDependencies || {}).forEach(([depName, depVersion]) => errorIfNotInYarnLock(p, depName, depVersion)); Object.entries(p.dependencies || {}).forEach(([depName, depVersion]) => errorIfNotInYarnLock(p, depName, depVersion)); }); } diff --git a/tools/cdk-release/lib/conventional-commits.ts b/tools/cdk-release/lib/conventional-commits.ts index 4ad2b88e33005..ed42884bc8b08 100644 --- a/tools/cdk-release/lib/conventional-commits.ts +++ b/tools/cdk-release/lib/conventional-commits.ts @@ -72,10 +72,10 @@ export async function getConventionalCommitsFromGitHistory(args: ReleaseOptions, const ret = new Array(); return new Promise((resolve, reject) => { const conventionalCommitsStream = gitRawCommits({ + // Raw body (subject + body) + '\n-hash-\n' + commit hash format: '%B%n-hash-%n%H', // our tags have the 'v' prefix from: gitTag, - // path: options.path, }).pipe(conventionalCommitsParser()); conventionalCommitsStream.on('data', function (data: any) { diff --git a/tools/cdk-release/lib/defaults.ts b/tools/cdk-release/lib/defaults.ts index 22f53ebe806aa..84d823c094b5b 100644 --- a/tools/cdk-release/lib/defaults.ts +++ b/tools/cdk-release/lib/defaults.ts @@ -1,31 +1,14 @@ import { ReleaseOptions } from './types'; -const defaultPackageFiles = [ - 'package.json', - 'bower.json', - 'manifest.json', -]; - -export const defaultBumpFiles = defaultPackageFiles.concat([ - 'package-lock.json', - 'npm-shrinkwrap.json', -]); - export const defaults: Partial = { - infile: 'CHANGELOG.md', - // firstRelease: false, + changelogFile: 'CHANGELOG.md', sign: false, - // noVerify: false, - // commitAll: false, silent: false, scripts: {}, skip: { tag: true, }, - packageFiles: defaultPackageFiles, - bumpFiles: defaultBumpFiles, dryRun: false, - // gitTagFallback: true, releaseCommitMessageFormat: 'chore(release): {{currentTag}}', changeLogHeader: '# Changelog\n\nAll notable changes to this project will be documented in this file. ' + 'See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.\n', diff --git a/tools/cdk-release/lib/index.ts b/tools/cdk-release/lib/index.ts index 0f72cdda7fe2e..04159170ffd81 100644 --- a/tools/cdk-release/lib/index.ts +++ b/tools/cdk-release/lib/index.ts @@ -6,8 +6,7 @@ import { bump } from './lifecycles/bump'; import { changelog } from './lifecycles/changelog'; import { commit } from './lifecycles/commit'; import { debug, debugObject } from './private/print'; -import { ReleaseOptions } from './types'; -import { resolveUpdaterObjectFromArgument } from './updaters'; +import { ReleaseOptions, Versions } from './types'; module.exports = async function main(opts: ReleaseOptions): Promise { // handle the default options @@ -17,41 +16,28 @@ module.exports = async function main(opts: ReleaseOptions): Promise { }; debugObject(args, 'options are (including defaults)', args); - const packageInfo = determinePackageInfo(args); - debugObject(args, 'packageInfo is', packageInfo); + const currentVersion = readVersion(args.versionFile); + debugObject(args, 'Current version info', currentVersion); - const currentVersion = packageInfo.version; - debug(args, 'Current version is: ' + currentVersion); - - const commits = await getConventionalCommitsFromGitHistory(args, `v${currentVersion}`); + const commits = await getConventionalCommitsFromGitHistory(args, `v${currentVersion.stableVersion}`); const filteredCommits = filterCommits(args, commits); debugObject(args, 'Found and filtered commits', filteredCommits); - const bumpResult = await bump(args, currentVersion); - const newVersion = bumpResult.newVersion; - debug(args, 'New version is: ' + newVersion); + const newVersion = await bump(args, currentVersion); + debugObject(args, 'New version is', newVersion); - const changelogResult = await changelog(args, currentVersion, newVersion, filteredCommits); + debug(args, 'Writing Changelog'); + await changelog(args, currentVersion.stableVersion, newVersion.stableVersion, filteredCommits); - await commit(args, newVersion, [...bumpResult.changedFiles, ...changelogResult.changedFiles]); + debug(args, 'Committing result'); + await commit(args, newVersion.stableVersion, [args.versionFile, args.changelogFile]); }; -interface PackageInfo { - version: string; - private: string | boolean | null | undefined; -} - -function determinePackageInfo(args: ReleaseOptions): PackageInfo { - for (const packageFile of args.packageFiles ?? []) { - const updater = resolveUpdaterObjectFromArgument(packageFile); - const pkgPath = path.resolve(process.cwd(), updater.filename); - const contents = fs.readFileSync(pkgPath, 'utf8'); - // we stop on the first (successful) option - return { - version: updater.updater.readVersion(contents), - private: typeof updater.updater.isPrivate === 'function' ? updater.updater.isPrivate(contents) : false, - }; - } - - throw new Error('Could not establish the version to bump!'); +export function readVersion(versionFile: string): Versions { + const versionPath = path.resolve(process.cwd(), versionFile); + const contents = JSON.parse(fs.readFileSync(versionPath, { encoding: 'utf-8' })); + return { + stableVersion: contents.version, + alphaVersion: contents.alphaVersion, + }; } diff --git a/tools/cdk-release/lib/lifecycles/bump.ts b/tools/cdk-release/lib/lifecycles/bump.ts index bb4465461f2d5..0a6417cb080d6 100644 --- a/tools/cdk-release/lib/lifecycles/bump.ts +++ b/tools/cdk-release/lib/lifecycles/bump.ts @@ -1,31 +1,48 @@ -import * as fs from 'fs'; import * as path from 'path'; import * as semver from 'semver'; import { writeFile } from '../private/files'; import { notify } from '../private/print'; -import { ReleaseOptions, ReleaseType } from '../types'; -import { resolveUpdaterObjectFromArgument } from '../updaters/index'; +import { LifecyclesSkip, ReleaseType, Versions } from '../types'; -export interface BumpResult { - readonly newVersion: string; - readonly changedFiles: string[]; +export interface BumpOptions { + releaseAs: ReleaseType; + skip?: LifecyclesSkip; + versionFile: string; + prerelease?: string; + + dryRun?: boolean; + verbose?: boolean; + silent?: boolean; } -export async function bump(args: ReleaseOptions, currentVersion: string): Promise { +export async function bump(args: BumpOptions, currentVersion: Versions): Promise { if (args.skip?.bump) { - return { - newVersion: currentVersion, - changedFiles: [], - }; + return currentVersion; } - const releaseType = getReleaseType(args.prerelease, args.releaseAs, currentVersion); - const newVersion = semver.inc(currentVersion, releaseType, args.prerelease); - if (!newVersion) { - throw new Error('Could not increment version: ' + currentVersion); + const releaseType = getReleaseType(args.prerelease, args.releaseAs, currentVersion.stableVersion); + const newStableVersion = semver.inc(currentVersion.stableVersion, releaseType, args.prerelease); + if (!newStableVersion) { + throw new Error('Could not increment version: ' + currentVersion.stableVersion); } - const changedFiles = updateBumpFiles(args, newVersion); - return { newVersion, changedFiles }; + + const newVersion: Versions = { + stableVersion: newStableVersion, + alphaVersion: bumpAlphaReleaseVersion(currentVersion, releaseType), + }; + + notify(args, + 'bumping version in ' + args.versionFile + ' from %s to %s', + [currentVersion, newVersion], + ); + const versionPath = path.resolve(process.cwd(), args.versionFile); + const versionFileContents = JSON.stringify({ + version: newVersion.stableVersion, + alphaVersion: newVersion.alphaVersion, + }, undefined, 2); + writeFile(args, versionPath, versionFileContents); + + return newVersion; } function getReleaseType(prerelease: string | undefined, expectedReleaseType: ReleaseType, currentVersion: string): semver.ReleaseType { @@ -89,34 +106,21 @@ function getTypePriority(type: string): number { } /** - * attempt to update the version number in provided `bumpFiles` - * @param args config object - * @param newVersion version number to update to. - * @return the collection of file paths that were actually changed + * https://github.com/aws/aws-cdk/issues/15581 + * We version any alpha modules in one of two ways, depending on the main/stable release. + * If the main release is itself a prerelease (e.g., 2.0.0-rc.17), + * we will increment the current alpha release. + * If the main release is not a prerelease, we use the main release version, but with an alpha tag. */ -function updateBumpFiles(args: ReleaseOptions, newVersion: string): string[] { - const ret = new Array(); +function bumpAlphaReleaseVersion(currentVersion: Versions, releaseType: semver.ReleaseType): string | undefined { + if (!currentVersion.alphaVersion) { return undefined; } - for (const bumpFile of (args.bumpFiles ?? [])) { - const updater = resolveUpdaterObjectFromArgument(bumpFile); - if (!updater) { - continue; - } - const configPath = path.resolve(process.cwd(), updater.filename); - const stat = fs.lstatSync(configPath); - if (!stat.isFile()) { - continue; - } - const contents = fs.readFileSync(configPath, 'utf8'); - notify(args, - 'bumping version in ' + updater.filename + ' from %s to %s', - [updater.updater.readVersion(contents), newVersion], - ); - writeFile(args, configPath, - updater.updater.writeVersion(contents, newVersion), - ); - ret.push(updater.filename); - } + const newAlphaVersion = releaseType.startsWith('pre') + ? semver.inc(currentVersion.alphaVersion, releaseType, 'alpha') + : semver.inc(currentVersion.stableVersion, 'pre' + releaseType as semver.ReleaseType, 'alpha'); - return ret; + if (!newAlphaVersion) { + throw new Error('Could not increment alpha version: ' + currentVersion.alphaVersion); + } + return newAlphaVersion; } diff --git a/tools/cdk-release/lib/lifecycles/changelog.ts b/tools/cdk-release/lib/lifecycles/changelog.ts index f961fe53b175c..7e1052068ffd9 100644 --- a/tools/cdk-release/lib/lifecycles/changelog.ts +++ b/tools/cdk-release/lib/lifecycles/changelog.ts @@ -3,7 +3,7 @@ import * as fs from 'fs-extra'; import { ConventionalCommit } from '../conventional-commits'; import { writeFile } from '../private/files'; import { notify, debug, debugObject } from '../private/print'; -import { ReleaseOptions } from '../types'; +import { LifecyclesSkip } from '../types'; // eslint-disable-next-line @typescript-eslint/no-require-imports const conventionalChangelogPresetLoader = require('conventional-changelog-preset-loader'); // eslint-disable-next-line @typescript-eslint/no-require-imports @@ -11,13 +11,25 @@ const conventionalChangelogWriter = require('conventional-changelog-writer'); const START_OF_LAST_RELEASE_PATTERN = /(^#+ \[?[0-9]+\.[0-9]+\.[0-9]+| { if (args.skip?.changelog) { return { @@ -28,7 +40,7 @@ export async function changelog( createChangelogIfMissing(args); // find the position of the last release and remove header - let oldContent = args.dryRun ? '' : fs.readFileSync(args.infile!, 'utf-8'); + let oldContent = args.dryRun ? '' : fs.readFileSync(args.changelogFile, 'utf-8'); const oldContentStart = oldContent.search(START_OF_LAST_RELEASE_PATTERN); if (oldContentStart !== -1) { oldContent = oldContent.substring(oldContentStart); @@ -100,24 +112,23 @@ export async function changelog( content += buffer.toString(); }); changelogStream.on('end', function () { - notify(args, 'outputting changes to %s', [args.infile]); + notify(args, 'outputting changes to %s', [args.changelogFile]); if (args.dryRun) { debug(args, `\n---\n${content.trim()}\n---\n`); } else { - writeFile(args, args.infile!, args.changeLogHeader + '\n' + (content + oldContent).replace(/\n+$/, '\n')); + writeFile(args, args.changelogFile, args.changeLogHeader + '\n' + (content + oldContent).replace(/\n+$/, '\n')); } return resolve({ contents: content, - changedFiles: [args.infile!], + changedFiles: [args.changelogFile], }); }); }); } -function createChangelogIfMissing(args: ReleaseOptions) { - if (!fs.existsSync(args.infile!)) { - notify(args, 'created %s', [args.infile]); - // args.outputUnreleased = true - writeFile(args, args.infile!, '\n'); +function createChangelogIfMissing(args: ChangelogOptions) { + if (!fs.existsSync(args.changelogFile)) { + notify(args, 'created %s', [args.changelogFile]); + writeFile(args, args.changelogFile, '\n'); } } diff --git a/tools/cdk-release/lib/private/files.ts b/tools/cdk-release/lib/private/files.ts index 1850e8a79ad4a..d9a68dd894c87 100644 --- a/tools/cdk-release/lib/private/files.ts +++ b/tools/cdk-release/lib/private/files.ts @@ -1,7 +1,10 @@ import * as fs from 'fs'; -import { ReleaseOptions } from '../types'; -export function writeFile(args: ReleaseOptions, filePath: string, content: string): void { +interface WriteFileOpts { + readonly dryRun?: boolean; +} + +export function writeFile(args: WriteFileOpts, filePath: string, content: string): void { if (args.dryRun) { return; } diff --git a/tools/cdk-release/lib/private/print.ts b/tools/cdk-release/lib/private/print.ts index 84b7758a5c4b3..282f47e203040 100644 --- a/tools/cdk-release/lib/private/print.ts +++ b/tools/cdk-release/lib/private/print.ts @@ -1,21 +1,25 @@ import * as util from 'util'; -import { ReleaseOptions } from '../types'; -export function debug(opts: ReleaseOptions, message: string): void { +export interface LoggingOptions { + verbose?: boolean; + silent?: boolean; +} + +export function debug(opts: LoggingOptions, message: string): void { if (opts.verbose) { // eslint-disable-next-line no-console console.log(`[cdk-release] ${message}`); } } -export function debugObject(opts: ReleaseOptions, message: string, object: any): void { +export function debugObject(opts: LoggingOptions, message: string, object: any): void { if (opts.verbose) { // eslint-disable-next-line no-console console.log(`[cdk-release] ${message}:\n`, object); } } -export function notify(opts: ReleaseOptions, msg: string, args: any[]) { +export function notify(opts: LoggingOptions, msg: string, args: any[]) { if (!opts.silent) { // eslint-disable-next-line no-console console.info('✔ ' + util.format(msg, ...args)); diff --git a/tools/cdk-release/lib/types.ts b/tools/cdk-release/lib/types.ts index f6314261cd5f1..822a5beba84d2 100644 --- a/tools/cdk-release/lib/types.ts +++ b/tools/cdk-release/lib/types.ts @@ -9,47 +9,24 @@ export interface Lifecycles { tag?: string; } -type LifecyclesSkip = { +export type LifecyclesSkip = { [key in keyof Lifecycles]: boolean; } -/* ****** Updaters ******** */ - -export interface UpdaterModule { - isPrivate?: (contents: string) => string | boolean | null | undefined; - readVersion(contents: string): string; - writeVersion(contents: string, version: string): string; -} - -export interface ArgUpdater { - filename: string; - type?: string; - updater?: UpdaterModule | string; -} - -export type ArgFile = string | ArgUpdater; - -export interface Updater { - filename: string; - updater: UpdaterModule; +export interface Versions { + stableVersion: string; + alphaVersion?: string; } export type ReleaseType = 'major' | 'minor' | 'patch'; -export interface ConventionalCommitType { - type: string; - section?: string; - hidden?: boolean; -} - /* ****** main options ******** */ export interface ReleaseOptions { releaseAs: ReleaseType; skip?: LifecyclesSkip; - packageFiles?: ArgFile[]; - bumpFiles?: ArgFile[]; - infile?: string; + versionFile: string; + changelogFile: string; prerelease?: string; scripts?: Lifecycles; dryRun?: boolean; diff --git a/tools/cdk-release/lib/updaters/index.ts b/tools/cdk-release/lib/updaters/index.ts deleted file mode 100644 index fd61828925316..0000000000000 --- a/tools/cdk-release/lib/updaters/index.ts +++ /dev/null @@ -1,59 +0,0 @@ -import * as path from 'path'; -import { defaultBumpFiles } from '../defaults'; -import { UpdaterModule, ArgFile, Updater } from '../types'; -import jsonUpdaterModule from './types/json'; -import plainTextUpdaterModule from './types/plain-text'; - -export function resolveUpdaterObjectFromArgument(arg: ArgFile): Updater { - arg = typeof arg === 'string' ? { filename: arg } : arg; - let updaterModule: UpdaterModule; - - if (arg.updater) { - updaterModule = getCustomUpdater(arg.updater); - } else if (arg.type) { - updaterModule = getUpdaterByType(arg.type); - } else { - updaterModule = getUpdaterByFilename(arg.filename); - } - - return { - updater: updaterModule, - filename: arg.filename, - }; -} - -function getCustomUpdater(updater: string | UpdaterModule): UpdaterModule { - if (typeof updater === 'string') { - // eslint-disable-next-line @typescript-eslint/no-require-imports - return require(path.resolve(process.cwd(), updater)); - } - if ( - typeof updater.readVersion === 'function' && - typeof updater.writeVersion === 'function' - ) { - return updater; - } - throw new Error('Updater must be a string path or an object with readVersion and writeVersion methods'); -} - -const JSON_BUMP_FILES = defaultBumpFiles; -function getUpdaterByFilename(filename: any): UpdaterModule { - if (JSON_BUMP_FILES.includes(path.basename(filename))) { - return getUpdaterByType('json'); - } - throw Error( - `Unsupported file (${filename}) provided for bumping.\n Please specify the updater \`type\` or use a custom \`updater\`.`, - ); -} - -const updatersByType: { [key: string]: UpdaterModule } = { - 'json': jsonUpdaterModule, - 'plain-text': plainTextUpdaterModule, -}; -function getUpdaterByType(type: string): UpdaterModule { - const updater = updatersByType[type]; - if (!updater) { - throw Error(`Unable to locate updater for provided type (${type}).`); - } - return updater; -} diff --git a/tools/cdk-release/lib/updaters/types/json.ts b/tools/cdk-release/lib/updaters/types/json.ts deleted file mode 100644 index 23e8490f56f5c..0000000000000 --- a/tools/cdk-release/lib/updaters/types/json.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { UpdaterModule } from '../../types'; - -// eslint-disable-next-line @typescript-eslint/no-require-imports -const detectIndent = require('detect-indent'); -// eslint-disable-next-line @typescript-eslint/no-require-imports -const detectNewline = require('detect-newline'); -// eslint-disable-next-line @typescript-eslint/no-require-imports -const stringifyPackage = require('stringify-package'); - -class JsonUpdaterModule implements UpdaterModule { - public readVersion(contents: string): string { - return JSON.parse(contents).version; - }; - - public writeVersion(contents: string, version: string): string { - const json = JSON.parse(contents); - const indent = detectIndent(contents).indent; - const newline = detectNewline(contents); - json.version = version; - return stringifyPackage(json, indent, newline); - }; - - public isPrivate(contents: string): string | boolean | null | undefined { - return JSON.parse(contents).private; - }; -} -const jsonUpdaterModule = new JsonUpdaterModule(); -export default jsonUpdaterModule; diff --git a/tools/cdk-release/lib/updaters/types/plain-text.ts b/tools/cdk-release/lib/updaters/types/plain-text.ts deleted file mode 100644 index 84ed4de20d563..0000000000000 --- a/tools/cdk-release/lib/updaters/types/plain-text.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { UpdaterModule } from '../../types'; - -const plainTextUpdaterModule: UpdaterModule = { - readVersion(contents: string): string { - return contents; - }, - - writeVersion(_contents: string, version: string): string { - return version; - }, -}; -export default plainTextUpdaterModule; diff --git a/tools/cdk-release/test/bump.test.ts b/tools/cdk-release/test/bump.test.ts new file mode 100644 index 0000000000000..df0d08eaae828 --- /dev/null +++ b/tools/cdk-release/test/bump.test.ts @@ -0,0 +1,77 @@ +import * as path from 'path'; +import { bump } from '../lib/lifecycles/bump'; + +// Mock file and console output +import * as files from '../lib/private/files'; +import * as print from '../lib/private/print'; +const mockWriteFile = jest.spyOn(files, 'writeFile').mockImplementation(() => jest.fn()); +jest.spyOn(print, 'notify').mockImplementation(() => jest.fn()); + +beforeEach(() => { jest.resetAllMocks(); }); + +test('skips bump if skip.bump is set', async () => { + const currentVersion = { stableVersion: '1.1.1', alphaVersion: '1.1.1-alpha.0' }; + const bumpedVersion = await bump({ releaseAs: 'major', versionFile: 'version.json', skip: { bump: true } }, currentVersion); + + expect(bumpedVersion).toEqual(currentVersion); +}); + +describe('stable versions', () => { + + test('does a prerelease bump with provided tag if given', async () => { + const currentVersion = { stableVersion: '1.2.3' }; + const bumpedVersion = await bump({ releaseAs: 'minor', versionFile: 'version.json', prerelease: 'rc' }, currentVersion); + + expect(bumpedVersion.stableVersion).toEqual('1.3.0-rc.0'); + }); + + test('does a normal bump if no prerelease tag is given', async () => { + const currentVersion = { stableVersion: '1.2.3' }; + const bumpedVersion = await bump({ releaseAs: 'minor', versionFile: 'version.json' }, currentVersion); + + expect(bumpedVersion.stableVersion).toEqual('1.3.0'); + }); + + test('writes output to version file', async () => { + const currentVersion = { stableVersion: '1.2.3' }; + await bump({ releaseAs: 'minor', versionFile: 'version.json' }, currentVersion); + + const versionPath = path.join(process.cwd(), 'version.json'); + const version = '{\n "version": "1.3.0"\n}'; + expect(mockWriteFile).toBeCalledWith(expect.any(Object), versionPath, version); + }); + +}); + +describe('alpha versions', () => { + + test('for prerelease, bumps existing alpha counter as a prerelease', async () => { + const currentVersion = { stableVersion: '1.2.0-rc.4', alphaVersion: '1.2.0-alpha.0' }; + const bumpedVersion = await bump({ releaseAs: 'minor', versionFile: 'version.json', prerelease: 'rc' }, currentVersion); + + expect(bumpedVersion).toEqual({ + stableVersion: '1.2.0-rc.5', + alphaVersion: '1.2.0-alpha.1', + }); + }); + + test('for normal releases, bumps alpha as a prerelease of stable release', async () => { + const currentVersion = { stableVersion: '1.2.0', alphaVersion: '1.1.0-alpha.0' }; + const bumpedVersion = await bump({ releaseAs: 'minor', versionFile: 'version.json' }, currentVersion); + + expect(bumpedVersion).toEqual({ + stableVersion: '1.3.0', + alphaVersion: '1.3.0-alpha.0', + }); + }); + + test('writes output to version file', async () => { + const currentVersion = { stableVersion: '1.2.0', alphaVersion: '1.1.0-alpha.0' }; + await bump({ releaseAs: 'minor', versionFile: 'version.json' }, currentVersion); + + const versionPath = path.join(process.cwd(), 'version.json'); + const version = '{\n "version": "1.3.0",\n "alphaVersion": "1.3.0-alpha.0"\n}'; + expect(mockWriteFile).toBeCalledWith(expect.any(Object), versionPath, version); + }); + +}); diff --git a/tools/cdk-release/test/changelog.test.ts b/tools/cdk-release/test/changelog.test.ts index 7079aee1d2ac8..67e01477e4ced 100644 --- a/tools/cdk-release/test/changelog.test.ts +++ b/tools/cdk-release/test/changelog.test.ts @@ -1,10 +1,9 @@ -import { ConventionalCommit, filterCommits, getConventionalCommitsFromGitHistory } from '../lib/conventional-commits'; -import { changelog } from '../lib/lifecycles/changelog'; -import { ReleaseOptions } from '../lib/types'; +import { ConventionalCommit } from '../lib/conventional-commits'; +import { changelog, ChangelogOptions } from '../lib/lifecycles/changelog'; describe('Changelog generation', () => { - const args: ReleaseOptions = { - releaseAs: 'minor', + const args: ChangelogOptions = { + changelogFile: 'CHANGELOG.md', dryRun: true, silent: true, includeDateInChangelog: false, @@ -53,52 +52,6 @@ describe('Changelog generation', () => { `); }); - - test("correctly skips experimental modules, even with 'BREAKING CHANGES'", async () => { - const commits: ConventionalCommit[] = [ - buildCommit({ - type: 'feat', - scope: 'scope', - subject: 'super important feature', - }), - buildCommit({ - type: 'fix', - scope: 'example-construct-library', // really hope we don't stabilize this one - subject: 'hairy bugfix', - notes: [ - { - title: 'BREAKING CHANGE', - text: 'this is a breaking change', - }, - ], - }), - ]; - - const changelogContents = await invokeChangelogFrom1_23_0to1_24_0({ - ...args, - stripExperimentalChanges: true, - }, commits); - - expect(changelogContents).toBe( - `## [1.24.0](https://github.com/aws/aws-cdk/compare/v1.23.0...v1.24.0) - -### Features - -* **scope:** super important feature - -`); - }); - - test('makes it so that no Git commits are queried if Changelog generation is skipped', async () => { - const commits = await getConventionalCommitsFromGitHistory({ - ...args, - skip: { - changelog: true, - }, - }, '3.9.2'); - - expect(commits).toHaveLength(0); - }); }); interface PartialCommit extends Partial { @@ -115,7 +68,6 @@ function buildCommit(commit: PartialCommit): ConventionalCommit { }; } -async function invokeChangelogFrom1_23_0to1_24_0(args: ReleaseOptions, commits: ConventionalCommit[]): Promise { - const changelogResult = await changelog(args, '1.23.0', '1.24.0', filterCommits(args, commits)); - return changelogResult.contents; +async function invokeChangelogFrom1_23_0to1_24_0(args: ChangelogOptions, commits: ConventionalCommit[]): Promise { + return (await changelog(args, '1.23.0', '1.24.0', commits)).contents; } diff --git a/tools/cdk-release/test/conventional-commits.test.ts b/tools/cdk-release/test/conventional-commits.test.ts new file mode 100644 index 0000000000000..773f229849e5d --- /dev/null +++ b/tools/cdk-release/test/conventional-commits.test.ts @@ -0,0 +1,100 @@ +import * as crypto from 'crypto'; +import * as stream from 'stream'; +import { ConventionalCommit, filterCommits, getConventionalCommitsFromGitHistory } from '../lib/conventional-commits'; +import { ReleaseOptions } from '../lib/types'; + +// mock out Git interactions +jest.mock('git-raw-commits', () => { return jest.fn(); }); +// eslint-disable-next-line @typescript-eslint/no-require-imports +const gitRawCommits = require('git-raw-commits'); + +const args: ReleaseOptions = { + changelogFile: 'CHANGELOG.md', + dryRun: true, + silent: true, + includeDateInChangelog: false, + releaseAs: 'minor', + versionFile: 'version.json', +}; + +describe('getConventionalCommitsFromGitHistory', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + + test('makes it so that no Git commits are queried if Changelog generation is skipped', async () => { + const commits = await getConventionalCommitsFromGitHistory({ ...args, skip: { changelog: true } }, '3.9.2'); + + expect(commits).toHaveLength(0); + expect(gitRawCommits).not.toHaveBeenCalled(); + }); + + test('skips commits without types', async () => { + const commitMessages = ['some commit without a type', 'chore(cdk-release): do trivial stuff']; + gitRawCommits.mockImplementation(() => mockGitCommits(commitMessages)); + + const commits = await getConventionalCommitsFromGitHistory(args, '3.9.2'); + + expect(commits).toHaveLength(1); + }); +}); + +// NOTE - These test currently use real package.json data to determine package's stability. +describe('filterCommits', () => { + const commits: ConventionalCommit[] = [ + buildCommit({ + type: 'feat', + scope: 'scope', + subject: 'super important feature', + }), + buildCommit({ + type: 'fix', + scope: 'example-construct-library', // really hope we don't stabilize this one + subject: 'hairy bugfix', + notes: [ + { + title: 'BREAKING CHANGE', + text: 'this is a breaking change', + }, + ], + }), + ]; + + test('if stripExperimental is not set, returns original commits', async () => { + const filteredCommits = filterCommits(args, commits); + + expect(filteredCommits.length).toEqual(2); + }); + + test("skips experimental modules if requested, even with 'BREAKING CHANGES'", async () => { + const filteredCommits = filterCommits({ ...args, stripExperimentalChanges: true }, commits); + + expect(filteredCommits.length).toEqual(1); + expect(filteredCommits[0].subject).toEqual('super important feature'); + }); +}); + +function mockGitCommits(messages: string[]) { + const rStream = new stream.Readable(); + messages.forEach(msg => { + rStream.push([ + msg, '-hash-', crypto.createHash('sha256').update(msg).digest('hex'), + ].join('\n')); + }); + rStream.push(null); + return rStream; +} + +interface PartialCommit extends Partial { + readonly type: string; + readonly subject: string; +} + +function buildCommit(commit: PartialCommit): ConventionalCommit { + return { + notes: [], + references: [], + header: `${commit.type}${commit.scope ? '(' + commit.scope + ')' : ''}: ${commit.subject}`, + ...commit, + }; +} diff --git a/version.v1.json b/version.v1.json index 5c8ead95001f8..e02425c0f7b3d 100644 --- a/version.v1.json +++ b/version.v1.json @@ -1,3 +1,3 @@ { - "version": "1.118.0" -} + "version": "1.119.0" +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 7e67161d74f5e..66bb214514901 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1628,7 +1628,7 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@^26.0.24": +"@types/jest@^26.0.22", "@types/jest@^26.0.24": version "26.0.24" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.24.tgz#943d11976b16739185913a1936e0de0c4a7d595a" integrity sha512-E/X5Vib8BWqZNRlDxj9vYXhsDwPYbPINqKF9BsnSoon4RQ0D9moEuLD8txgyypFLH7J4+Lho9Nr/c8H0Fi+17w==