From 717bb047ef9513a7792f8cea6919c74125fc5fdb Mon Sep 17 00:00:00 2001 From: Otavio Macedo Date: Tue, 18 Jan 2022 15:30:39 +0000 Subject: [PATCH 01/50] chore(s3-assets): migrated tests to `assertions` (#18483) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-s3-assets/package.json | 2 +- .../@aws-cdk/aws-s3-assets/test/asset.test.ts | 37 ++++++++++--------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/packages/@aws-cdk/aws-s3-assets/package.json b/packages/@aws-cdk/aws-s3-assets/package.json index 489749cfee25e..80996d4ebc255 100644 --- a/packages/@aws-cdk/aws-s3-assets/package.json +++ b/packages/@aws-cdk/aws-s3-assets/package.json @@ -77,7 +77,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cloud-assembly-schema": "0.0.0", diff --git a/packages/@aws-cdk/aws-s3-assets/test/asset.test.ts b/packages/@aws-cdk/aws-s3-assets/test/asset.test.ts index 7a335ab3b7bb2..f04218f95929d 100644 --- a/packages/@aws-cdk/aws-s3-assets/test/asset.test.ts +++ b/packages/@aws-cdk/aws-s3-assets/test/asset.test.ts @@ -1,12 +1,11 @@ -import { ResourcePart, SynthUtils } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import * as fs from 'fs'; +import * as os from 'os'; +import * as path from 'path'; +import { Match, Template } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import * as cdk from '@aws-cdk/core'; import * as cxapi from '@aws-cdk/cx-api'; -import * as fs from 'fs'; -import * as os from 'os'; -import * as path from 'path'; import { Asset } from '../lib/asset'; const SAMPLE_ASSET_DIR = path.join(__dirname, 'sample-asset-directory'); @@ -79,7 +78,7 @@ test('"file" assets', () => { expect(entry).toBeTruthy(); // synthesize first so "prepare" is called - const template = SynthUtils.synthesize(stack).template; + const template = Template.fromStack(stack); expect(stack.resolve(entry!.data)).toEqual({ path: 'asset.78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197.txt', @@ -92,8 +91,12 @@ test('"file" assets', () => { }); // verify that now the template contains parameters for this asset - expect(template.Parameters.AssetParameters78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197S3Bucket2C60F94A.Type).toBe('String'); - expect(template.Parameters.AssetParameters78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197S3VersionKey9482DC35.Type).toBe('String'); + expect(template.findParameters('AssetParameters78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197S3Bucket2C60F94A') + .AssetParameters78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197S3Bucket2C60F94A.Type) + .toBe('String'); + expect(template.findParameters('AssetParameters78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197S3VersionKey9482DC35') + .AssetParameters78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197S3VersionKey9482DC35.Type) + .toBe('String'); }); test('"readers" or "grantRead" can be used to grant read permissions on the asset to a principal', () => { @@ -108,7 +111,7 @@ test('"readers" or "grantRead" can be used to grant read permissions on the asse asset.grantRead(group); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Version: '2012-10-17', Statement: [ @@ -200,13 +203,13 @@ test('addResourceMetadata can be used to add CFN metadata to resources', () => { asset.addResourceMetadata(resource, 'PropName'); // THEN - expect(stack).toHaveResource('My::Resource::Type', { + Template.fromStack(stack).hasResource('My::Resource::Type', { Metadata: { 'aws:asset:path': 'asset.6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2', 'aws:asset:is-bundled': false, 'aws:asset:property': 'PropName', }, - }, ResourcePart.CompleteDefinition); + }); }); test('asset metadata is only emitted if ASSET_RESOURCE_METADATA_ENABLED_CONTEXT is defined', () => { @@ -220,13 +223,13 @@ test('asset metadata is only emitted if ASSET_RESOURCE_METADATA_ENABLED_CONTEXT asset.addResourceMetadata(resource, 'PropName'); // THEN - expect(stack).not.toHaveResource('My::Resource::Type', { + Template.fromStack(stack).hasResource('My::Resource::Type', Match.not({ Metadata: { 'aws:asset:path': SAMPLE_ASSET_DIR, 'aws:asset:is-bundled': false, 'aws:asset:property': 'PropName', }, - }, ResourcePart.CompleteDefinition); + })); }); test('nested assemblies share assets: legacy synth edition', () => { @@ -350,8 +353,8 @@ describe('staging', () => { // WHEN asset.addResourceMetadata(resource, 'PropName'); - const template = SynthUtils.synthesize(stack).template; - expect(template.Resources.MyResource.Metadata).toEqual({ + const template = Template.fromStack(stack); + expect(template.findResources('My::Resource::Type').MyResource.Metadata).toEqual({ 'aws:asset:path': 'asset.6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2', 'aws:asset:is-bundled': false, 'aws:asset:property': 'PropName', @@ -377,8 +380,8 @@ describe('staging', () => { // WHEN asset.addResourceMetadata(resource, 'PropName'); - const template = SynthUtils.synthesize(stack).template; - expect(template.Resources.MyResource.Metadata).toEqual({ + const template = Template.fromStack(stack); + expect(template.findResources('My::Resource::Type').MyResource.Metadata).toEqual({ 'aws:asset:path': SAMPLE_ASSET_DIR, 'aws:asset:is-bundled': false, 'aws:asset:property': 'PropName', From a295dc6f85bd42d64dfa96ba1cc2dcaab3d5c8fb Mon Sep 17 00:00:00 2001 From: Otavio Macedo Date: Tue, 18 Jan 2022 16:18:54 +0000 Subject: [PATCH 02/50] chore(kinesis): migrated tests to `assertions` (#18482) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-kinesis/package.json | 2 +- .../@aws-cdk/aws-kinesis/test/stream.test.ts | 75 +++++++++---------- 2 files changed, 38 insertions(+), 39 deletions(-) diff --git a/packages/@aws-cdk/aws-kinesis/package.json b/packages/@aws-cdk/aws-kinesis/package.json index 37495af6cf1cd..0b1aea08d72df 100644 --- a/packages/@aws-cdk/aws-kinesis/package.json +++ b/packages/@aws-cdk/aws-kinesis/package.json @@ -79,7 +79,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-kinesis/test/stream.test.ts b/packages/@aws-cdk/aws-kinesis/test/stream.test.ts index 8e4fa385133dd..e1fce397cce4c 100644 --- a/packages/@aws-cdk/aws-kinesis/test/stream.test.ts +++ b/packages/@aws-cdk/aws-kinesis/test/stream.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { arrayWith } from '@aws-cdk/assert-internal'; +import { Match, Template } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; import { testFutureBehavior, testLegacyBehavior } from '@aws-cdk/cdk-build-tools'; @@ -16,7 +15,7 @@ describe('Kinesis data streams', () => { new Stream(stack, 'MyStream'); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyStream5C050E93: { Type: 'AWS::Kinesis::Stream', @@ -72,7 +71,7 @@ describe('Kinesis data streams', () => { new Stream(stack, 'MyStream'); new Stream(stack, 'MyOtherStream'); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyStream5C050E93: { Type: 'AWS::Kinesis::Stream', @@ -161,7 +160,7 @@ describe('Kinesis data streams', () => { shardCount: 2, }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyStream5C050E93: { Type: 'AWS::Kinesis::Stream', @@ -218,7 +217,7 @@ describe('Kinesis data streams', () => { retentionPeriod: Duration.hours(168), }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyStream5C050E93: { Type: 'AWS::Kinesis::Stream', @@ -292,7 +291,7 @@ describe('Kinesis data streams', () => { }); // THEN - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyStream5C050E93: { Type: 'AWS::Kinesis::Stream', @@ -335,7 +334,7 @@ describe('Kinesis data streams', () => { }); // THEN - expect(stack).toHaveResource('AWS::Kinesis::Stream', { + Template.fromStack(stack).hasResourceProperties('AWS::Kinesis::Stream', { ShardCount: 1, StreamModeDetails: { StreamMode: StreamMode.PROVISIONED, @@ -357,11 +356,11 @@ describe('Kinesis data streams', () => { encryption: StreamEncryption.KMS, }); - expect(stack).toHaveResource('AWS::KMS::Key', { + Template.fromStack(stack).hasResourceProperties('AWS::KMS::Key', { Description: 'Created by Default/MyStream', }); - expect(stack).toHaveResource('AWS::Kinesis::Stream', { + Template.fromStack(stack).hasResourceProperties('AWS::Kinesis::Stream', { StreamEncryption: { EncryptionType: 'KMS', KeyId: stack.resolve(stream.encryptionKey?.keyArn), @@ -381,11 +380,11 @@ describe('Kinesis data streams', () => { encryptionKey: explicitKey, }); - expect(stack).toHaveResource('AWS::KMS::Key', { + Template.fromStack(stack).hasResourceProperties('AWS::KMS::Key', { Description: 'Explicit Key', }); - expect(stack).toHaveResource('AWS::Kinesis::Stream', { + Template.fromStack(stack).hasResourceProperties('AWS::Kinesis::Stream', { ShardCount: 1, StreamModeDetails: { StreamMode: StreamMode.PROVISIONED, @@ -405,7 +404,7 @@ describe('Kinesis data streams', () => { streamMode: StreamMode.PROVISIONED, }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyStream5C050E93: { Type: 'AWS::Kinesis::Stream', @@ -462,7 +461,7 @@ describe('Kinesis data streams', () => { streamMode: StreamMode.ON_DEMAND, }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyStream5C050E93: { Type: 'AWS::Kinesis::Stream', @@ -531,17 +530,17 @@ describe('Kinesis data streams', () => { const user = new iam.User(stack, 'MyUser'); stream.grantRead(user); - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { - Statement: arrayWith({ + Statement: Match.arrayWith([{ Action: 'kms:Decrypt', Effect: 'Allow', Resource: stack.resolve(stream.encryptionKey?.keyArn), - }), + }]), }, }); - expect(stack).toHaveResourceLike('AWS::Kinesis::Stream', { + Template.fromStack(stack).hasResourceProperties('AWS::Kinesis::Stream', { StreamEncryption: { KeyId: stack.resolve(stream.encryptionKey?.keyArn), }, @@ -559,7 +558,7 @@ describe('Kinesis data streams', () => { const user = new iam.User(stack, 'MyUser'); stream.grantRead(user); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyStreamKey76F3300E: { Type: 'AWS::KMS::Key', @@ -693,17 +692,17 @@ describe('Kinesis data streams', () => { const user = new iam.User(stack, 'MyUser'); stream.grantWrite(user); - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { - Statement: arrayWith({ + Statement: Match.arrayWith([{ Action: ['kms:Encrypt', 'kms:ReEncrypt*', 'kms:GenerateDataKey*'], Effect: 'Allow', Resource: stack.resolve(stream.encryptionKey?.keyArn), - }), + }]), }, }); - expect(stack).toHaveResourceLike('AWS::Kinesis::Stream', { + Template.fromStack(stack).hasResourceProperties('AWS::Kinesis::Stream', { StreamEncryption: { KeyId: stack.resolve(stream.encryptionKey?.keyArn), }, @@ -721,7 +720,7 @@ describe('Kinesis data streams', () => { const user = new iam.User(stack, 'MyUser'); stream.grantWrite(user); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyStreamKey76F3300E: { Type: 'AWS::KMS::Key', @@ -847,17 +846,17 @@ describe('Kinesis data streams', () => { const user = new iam.User(stack, 'MyUser'); stream.grantReadWrite(user); - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { - Statement: arrayWith({ + Statement: Match.arrayWith([{ Action: ['kms:Decrypt', 'kms:Encrypt', 'kms:ReEncrypt*', 'kms:GenerateDataKey*'], Effect: 'Allow', Resource: stack.resolve(stream.encryptionKey?.keyArn), - }), + }]), }, }); - expect(stack).toHaveResourceLike('AWS::Kinesis::Stream', { + Template.fromStack(stack).hasResourceProperties('AWS::Kinesis::Stream', { StreamEncryption: { KeyId: stack.resolve(stream.encryptionKey?.keyArn), }, @@ -875,7 +874,7 @@ describe('Kinesis data streams', () => { const user = new iam.User(stack, 'MyUser'); stream.grantReadWrite(user); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyStreamKey76F3300E: { Type: 'AWS::KMS::Key', @@ -1009,7 +1008,7 @@ describe('Kinesis data streams', () => { const user = new iam.User(stack, 'MyUser'); stream.grantRead(user); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyStream5C050E93: { Type: 'AWS::Kinesis::Stream', @@ -1100,7 +1099,7 @@ describe('Kinesis data streams', () => { const user = new iam.User(stack, 'MyUser'); stream.grantWrite(user); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyStream5C050E93: { Type: 'AWS::Kinesis::Stream', @@ -1183,7 +1182,7 @@ describe('Kinesis data streams', () => { const user = new iam.User(stack, 'MyUser'); stream.grantReadWrite(user); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyStream5C050E93: { Type: 'AWS::Kinesis::Stream', @@ -1276,7 +1275,7 @@ describe('Kinesis data streams', () => { const user = new iam.User(stack, 'MyUser'); stream.grant(user, 'kinesis:DescribeStream'); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyStream5C050E93: { Type: 'AWS::Kinesis::Stream', @@ -1361,7 +1360,7 @@ describe('Kinesis data streams', () => { const user = new iam.User(stackB, 'UserWhoNeedsAccess'); streamFromStackA.grantRead(user); - expect(stackA).toMatchTemplate({ + Template.fromStack(stackA).templateMatches({ Resources: { MyStream5C050E93: { Type: 'AWS::Kinesis::Stream', @@ -1446,15 +1445,15 @@ describe('Kinesis data streams', () => { const user = new iam.User(stackB, 'UserWhoNeedsAccess'); streamFromStackA.grantRead(user); - expect(stackB).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stackB).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { - Statement: arrayWith({ + Statement: Match.arrayWith([{ Action: 'kms:Decrypt', Effect: 'Allow', Resource: { 'Fn::ImportValue': 'stackA:ExportsOutputFnGetAttMyStreamKey76F3300EArn190947B4', }, - }), + }]), }, }); }); @@ -1473,7 +1472,7 @@ describe('Kinesis data streams', () => { retentionPeriod: Duration.hours(parameter.valueAsNumber), }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Parameters: { myretentionperiod: { Type: 'Number', From d8c11fe1f5cd554accf8d0c1fec9d4f0c3a0ce06 Mon Sep 17 00:00:00 2001 From: Otavio Macedo Date: Tue, 18 Jan 2022 17:05:22 +0000 Subject: [PATCH 03/50] chore(ses): migrated tests to `assertions` (#18481) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-ses/package.json | 2 +- .../@aws-cdk/aws-ses/test/receipt-filter.test.ts | 6 +++--- .../@aws-cdk/aws-ses/test/receipt-rule-set.test.ts | 14 +++++++------- .../@aws-cdk/aws-ses/test/receipt-rule.test.ts | 10 +++++----- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/@aws-cdk/aws-ses/package.json b/packages/@aws-cdk/aws-ses/package.json index 134bc98d95365..64ad0fc5f67ce 100644 --- a/packages/@aws-cdk/aws-ses/package.json +++ b/packages/@aws-cdk/aws-ses/package.json @@ -79,7 +79,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-ses/test/receipt-filter.test.ts b/packages/@aws-cdk/aws-ses/test/receipt-filter.test.ts index 69e966555d0a9..74e6dc8742340 100644 --- a/packages/@aws-cdk/aws-ses/test/receipt-filter.test.ts +++ b/packages/@aws-cdk/aws-ses/test/receipt-filter.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { Stack } from '@aws-cdk/core'; import { AllowListReceiptFilter, ReceiptFilter, ReceiptFilterPolicy } from '../lib'; @@ -17,7 +17,7 @@ describe('receipt filter', () => { }); // THEN - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ 'Resources': { 'FilterC907D6DA': { 'Type': 'AWS::SES::ReceiptFilter', @@ -50,7 +50,7 @@ describe('receipt filter', () => { }); // THEN - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ 'Resources': { 'AllowListBlockAll094C9B97': { 'Type': 'AWS::SES::ReceiptFilter', diff --git a/packages/@aws-cdk/aws-ses/test/receipt-rule-set.test.ts b/packages/@aws-cdk/aws-ses/test/receipt-rule-set.test.ts index a80886c442144..0386b71f39425 100644 --- a/packages/@aws-cdk/aws-ses/test/receipt-rule-set.test.ts +++ b/packages/@aws-cdk/aws-ses/test/receipt-rule-set.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { Stack } from '@aws-cdk/core'; import { ReceiptRuleSet } from '../lib'; @@ -15,7 +15,7 @@ describe('receipt rule set', () => { }); // THEN - expect(stack).toHaveResource('AWS::SES::ReceiptRuleSet', { + Template.fromStack(stack).hasResourceProperties('AWS::SES::ReceiptRuleSet', { RuleSetName: 'MyRuleSet', }); @@ -32,7 +32,7 @@ describe('receipt rule set', () => { }); // THEN - expect(stack).toHaveResource('AWS::SES::ReceiptRule', { + Template.fromStack(stack).hasResourceProperties('AWS::SES::ReceiptRule', { Rule: { Actions: [ { @@ -52,7 +52,7 @@ describe('receipt rule set', () => { }, }); - expect(stack).toHaveResource('AWS::Lambda::Function'); + Template.fromStack(stack).resourceCountIs('AWS::Lambda::Function', 1); }); @@ -73,7 +73,7 @@ describe('receipt rule set', () => { }); // THEN - expect(stack).toHaveResource('AWS::SES::ReceiptRule', { + Template.fromStack(stack).hasResourceProperties('AWS::SES::ReceiptRule', { Rule: { Enabled: true, Recipients: [ @@ -87,7 +87,7 @@ describe('receipt rule set', () => { }, }); - expect(stack).toHaveResource('AWS::Lambda::Function'); + Template.fromStack(stack).resourceCountIs('AWS::Lambda::Function', 1); }); @@ -102,7 +102,7 @@ describe('receipt rule set', () => { receiptRuleSet.addRule('MyRule'); // THEN - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ 'Resources': { 'ImportedRuleSetMyRule53EE2F7F': { 'Type': 'AWS::SES::ReceiptRule', diff --git a/packages/@aws-cdk/aws-ses/test/receipt-rule.test.ts b/packages/@aws-cdk/aws-ses/test/receipt-rule.test.ts index 4cb86efe88b72..d8a3805cee858 100644 --- a/packages/@aws-cdk/aws-ses/test/receipt-rule.test.ts +++ b/packages/@aws-cdk/aws-ses/test/receipt-rule.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { Stack } from '@aws-cdk/core'; import { ReceiptRule, ReceiptRuleSet, TlsPolicy } from '../lib'; @@ -26,7 +26,7 @@ describe('receipt rule', () => { }); // THEN - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ 'Resources': { 'RuleSetE30C6C48': { 'Type': 'AWS::SES::ReceiptRuleSet', @@ -82,7 +82,7 @@ describe('receipt rule', () => { }); // THEN - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ 'Resources': { 'RuleSetE30C6C48': { 'Type': 'AWS::SES::ReceiptRuleSet', @@ -124,7 +124,7 @@ describe('receipt rule', () => { }); // THEN - expect(stack).toHaveResource('AWS::SES::ReceiptRule', { + Template.fromStack(stack).hasResourceProperties('AWS::SES::ReceiptRule', { 'Rule': { 'Actions': [ { @@ -159,7 +159,7 @@ describe('receipt rule', () => { }); // THEN - expect(stack).toHaveResource('AWS::SES::ReceiptRule', { + Template.fromStack(stack).hasResourceProperties('AWS::SES::ReceiptRule', { 'Rule': { 'Actions': [ { From 438511dfb6dd6df107621e56009311fe331e2d24 Mon Sep 17 00:00:00 2001 From: Otavio Macedo Date: Tue, 18 Jan 2022 17:53:07 +0000 Subject: [PATCH 04/50] chore(ses-actions): migrated tests to `assertions` (#18472) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-ses-actions/package.json | 2 +- .../aws-ses-actions/test/actions.test.ts | 29 +++++++++---------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/packages/@aws-cdk/aws-ses-actions/package.json b/packages/@aws-cdk/aws-ses-actions/package.json index 1c41d8f7333b0..2d42ecb26c71b 100644 --- a/packages/@aws-cdk/aws-ses-actions/package.json +++ b/packages/@aws-cdk/aws-ses-actions/package.json @@ -65,7 +65,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-ses-actions/test/actions.test.ts b/packages/@aws-cdk/aws-ses-actions/test/actions.test.ts index 4a425fd51f76b..845f99c963aa7 100644 --- a/packages/@aws-cdk/aws-ses-actions/test/actions.test.ts +++ b/packages/@aws-cdk/aws-ses-actions/test/actions.test.ts @@ -1,5 +1,4 @@ -import { arrayWith, ResourcePart } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as kms from '@aws-cdk/aws-kms'; import * as lambda from '@aws-cdk/aws-lambda'; import * as s3 from '@aws-cdk/aws-s3'; @@ -25,7 +24,7 @@ test('add header action', () => { value: 'value', })); - expect(stack).toHaveResource('AWS::SES::ReceiptRule', { + Template.fromStack(stack).hasResourceProperties('AWS::SES::ReceiptRule', { Rule: { Actions: [ { @@ -62,7 +61,7 @@ test('add bounce action', () => { topic, })); - expect(stack).toHaveResource('AWS::SES::ReceiptRule', { + Template.fromStack(stack).hasResourceProperties('AWS::SES::ReceiptRule', { Rule: { Actions: [ { @@ -95,7 +94,7 @@ test('add lambda action', () => { topic, })); - expect(stack).toHaveResource('AWS::SES::ReceiptRule', { + Template.fromStack(stack).hasResource('AWS::SES::ReceiptRule', { Properties: { Rule: { Actions: [ @@ -123,9 +122,9 @@ test('add lambda action', () => { DependsOn: [ 'FunctionAllowSes1829904A', ], - }, ResourcePart.CompleteDefinition); + }); - expect(stack).toHaveResource('AWS::Lambda::Permission', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Permission', { Action: 'lambda:InvokeFunction', FunctionName: { 'Fn::GetAtt': [ @@ -151,7 +150,7 @@ test('add s3 action', () => { topic, })); - expect(stack).toHaveResource('AWS::SES::ReceiptRule', { + Template.fromStack(stack).hasResource('AWS::SES::ReceiptRule', { Properties: { Rule: { Actions: [ @@ -182,9 +181,9 @@ test('add s3 action', () => { DependsOn: [ 'BucketPolicyE9A3008A', ], - }, ResourcePart.CompleteDefinition); + }); - expect(stack).toHaveResource('AWS::S3::BucketPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::S3::BucketPolicy', { Bucket: { Ref: 'Bucket83908E77', }, @@ -223,9 +222,9 @@ test('add s3 action', () => { }, }); - expect(stack).toHaveResourceLike('AWS::KMS::Key', { + Template.fromStack(stack).hasResourceProperties('AWS::KMS::Key', { KeyPolicy: { - Statement: arrayWith({ + Statement: Match.arrayWith([{ Action: [ 'kms:Encrypt', 'kms:GenerateDataKey', @@ -246,7 +245,7 @@ test('add s3 action', () => { Service: 'ses.amazonaws.com', }, Resource: '*', - }), + }]), }, }); }); @@ -257,7 +256,7 @@ test('add sns action', () => { topic, })); - expect(stack).toHaveResource('AWS::SES::ReceiptRule', { + Template.fromStack(stack).hasResourceProperties('AWS::SES::ReceiptRule', { Rule: { Actions: [ { @@ -279,7 +278,7 @@ test('add stop action', () => { topic, })); - expect(stack).toHaveResource('AWS::SES::ReceiptRule', { + Template.fromStack(stack).hasResourceProperties('AWS::SES::ReceiptRule', { Rule: { Actions: [ { From d735ce4bc9049b68b830641d68c97f4be565968d Mon Sep 17 00:00:00 2001 From: Otavio Macedo Date: Tue, 18 Jan 2022 18:38:57 +0000 Subject: [PATCH 05/50] chore(assets): migrated tests to `assertions` (#18471) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/assets/package.json | 2 +- packages/@aws-cdk/assets/test/compat.test.ts | 1 - packages/@aws-cdk/assets/test/staging.test.ts | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/@aws-cdk/assets/package.json b/packages/@aws-cdk/assets/package.json index d0ce86e435218..7bac0d2b66d81 100644 --- a/packages/@aws-cdk/assets/package.json +++ b/packages/@aws-cdk/assets/package.json @@ -69,7 +69,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/pkglint": "0.0.0", diff --git a/packages/@aws-cdk/assets/test/compat.test.ts b/packages/@aws-cdk/assets/test/compat.test.ts index 25afe6dd9411b..dfb3c3afd2daa 100644 --- a/packages/@aws-cdk/assets/test/compat.test.ts +++ b/packages/@aws-cdk/assets/test/compat.test.ts @@ -1,5 +1,4 @@ import { SymlinkFollowMode } from '@aws-cdk/core'; -import '@aws-cdk/assert-internal/jest'; import { FollowMode } from '../lib'; import { toSymlinkFollow } from '../lib/compat'; diff --git a/packages/@aws-cdk/assets/test/staging.test.ts b/packages/@aws-cdk/assets/test/staging.test.ts index bd924e434207b..8893c6451f2c1 100644 --- a/packages/@aws-cdk/assets/test/staging.test.ts +++ b/packages/@aws-cdk/assets/test/staging.test.ts @@ -3,7 +3,6 @@ import * as path from 'path'; import { describeDeprecated } from '@aws-cdk/cdk-build-tools'; import { App, Stack } from '@aws-cdk/core'; import * as cxapi from '@aws-cdk/cx-api'; -import '@aws-cdk/assert-internal/jest'; import { Staging } from '../lib'; describeDeprecated('staging', () => { From 15a19364c4aea7e6d12d1401b458ee09816d8a33 Mon Sep 17 00:00:00 2001 From: Otavio Macedo Date: Tue, 18 Jan 2022 19:25:06 +0000 Subject: [PATCH 06/50] chore(s3-deployment): migrated tests to `assertions` (#18467) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-s3-deployment/package.json | 2 +- .../test/bucket-deployment.test.ts | 65 +++++++++---------- 2 files changed, 33 insertions(+), 34 deletions(-) diff --git a/packages/@aws-cdk/aws-s3-deployment/package.json b/packages/@aws-cdk/aws-s3-deployment/package.json index aa967f81e56c4..70adaaac2ff88 100644 --- a/packages/@aws-cdk/aws-s3-deployment/package.json +++ b/packages/@aws-cdk/aws-s3-deployment/package.json @@ -85,7 +85,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cx-api": "0.0.0", 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 38023cc0154e6..9507cdb5dace7 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 @@ -1,6 +1,5 @@ import * as path from 'path'; -import { MatchStyle, objectLike } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as cloudfront from '@aws-cdk/aws-cloudfront'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; @@ -27,7 +26,7 @@ test('deploy from local directory asset', () => { }); // THEN - expect(stack).toHaveResource('Custom::CDKBucketDeployment', { + Template.fromStack(stack).hasResourceProperties('Custom::CDKBucketDeployment', { ServiceToken: { 'Fn::GetAtt': [ 'CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C81C01536', @@ -89,7 +88,7 @@ test('deploy with configured log retention', () => { }); // THEN - expect(stack).toHaveResourceLike('Custom::LogRetention', { RetentionInDays: 7 }); + Template.fromStack(stack).hasResourceProperties('Custom::LogRetention', { RetentionInDays: 7 }); }); test('deploy from local directory assets', () => { @@ -107,7 +106,7 @@ test('deploy from local directory assets', () => { }); // THEN - expect(stack).toHaveResource('Custom::CDKBucketDeployment', { + Template.fromStack(stack).hasResourceProperties('Custom::CDKBucketDeployment', { ServiceToken: { 'Fn::GetAtt': [ 'CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C81C01536', @@ -235,7 +234,7 @@ test('deploy from a local .zip file when efs is enabled', () => { }); //THEN - expect(stack).toHaveResource('AWS::Lambda::Function', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { Environment: { Variables: { MOUNT_PATH: '/mnt/lambda', @@ -315,7 +314,7 @@ testDeprecated('honors passed asset options', () => { }); // THEN - expect(stack).toHaveResource('Custom::CDKBucketDeployment', { + Template.fromStack(stack).hasResourceProperties('Custom::CDKBucketDeployment', { ServiceToken: { 'Fn::GetAtt': [ 'CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C81C01536', @@ -377,7 +376,7 @@ test('retainOnDelete can be used to retain files when resource is deleted', () = }); // THEN - expect(stack).toHaveResource('Custom::CDKBucketDeployment', { + Template.fromStack(stack).hasResourceProperties('Custom::CDKBucketDeployment', { RetainOnDelete: true, }); }); @@ -398,7 +397,7 @@ test('user metadata is correctly transformed', () => { }); // THEN - expect(stack).toHaveResource('Custom::CDKBucketDeployment', { + Template.fromStack(stack).hasResourceProperties('Custom::CDKBucketDeployment', { UserMetadata: { a: '1', b: '2' }, }); }); @@ -427,7 +426,7 @@ test('system metadata is correctly transformed', () => { }); // THEN - expect(stack).toHaveResource('Custom::CDKBucketDeployment', { + Template.fromStack(stack).hasResourceProperties('Custom::CDKBucketDeployment', { SystemMetadata: { 'content-type': 'text/html', 'content-language': 'en', @@ -472,7 +471,7 @@ test.each(Object.entries(accessControlMap) as [s3.BucketAccessControl, string][] }); // THEN - expect(stack).toHaveResource('Custom::CDKBucketDeployment', { + Template.fromStack(stack).hasResourceProperties('Custom::CDKBucketDeployment', { SystemMetadata: { acl: systemMetadataKeyword, }, @@ -537,7 +536,7 @@ test('distribution can be used to provide a CloudFront distribution for invalida distributionPaths: ['/images/*'], }); - expect(stack).toHaveResource('Custom::CDKBucketDeployment', { + Template.fromStack(stack).hasResourceProperties('Custom::CDKBucketDeployment', { DistributionId: { Ref: 'DistributionCFDistribution882A7313', }, @@ -567,7 +566,7 @@ test('invalidation can happen without distributionPaths provided', () => { distribution, }); - expect(stack).toHaveResource('Custom::CDKBucketDeployment', { + Template.fromStack(stack).hasResourceProperties('Custom::CDKBucketDeployment', { DistributionId: { Ref: 'DistributionCFDistribution882A7313', }, @@ -626,7 +625,7 @@ testFutureBehavior('lambda execution role gets permissions to read from the sour }); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -735,9 +734,9 @@ test('memoryLimit can be used to specify the memory limit for the deployment res // we expect to find only two handlers, one for each configuration - expect(stack).toCountResources('AWS::Lambda::Function', 2); - expect(stack).toHaveResource('AWS::Lambda::Function', { MemorySize: 256 }); - expect(stack).toHaveResource('AWS::Lambda::Function', { MemorySize: 1024 }); + Template.fromStack(stack).resourceCountIs('AWS::Lambda::Function', 2); + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { MemorySize: 256 }); + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { MemorySize: 1024 }); }); test('deployment allows custom role to be supplied', () => { @@ -757,9 +756,9 @@ test('deployment allows custom role to be supplied', () => { }); // THEN - expect(stack).toCountResources('AWS::IAM::Role', 1); - expect(stack).toCountResources('AWS::Lambda::Function', 1); - expect(stack).toHaveResource('AWS::Lambda::Function', { + Template.fromStack(stack).resourceCountIs('AWS::IAM::Role', 1); + Template.fromStack(stack).resourceCountIs('AWS::Lambda::Function', 1); + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { Role: { 'Fn::GetAtt': [ 'Role1ABCC5F0', @@ -782,7 +781,7 @@ test('deploy without deleting missing files from destination', () => { prune: false, }); - expect(stack).toHaveResourceLike('Custom::CDKBucketDeployment', { + Template.fromStack(stack).hasResourceProperties('Custom::CDKBucketDeployment', { Prune: false, }); }); @@ -799,7 +798,7 @@ test('deploy with excluded files from destination', () => { exclude: ['sample.js'], }); - expect(stack).toHaveResourceLike('Custom::CDKBucketDeployment', { + Template.fromStack(stack).hasResourceProperties('Custom::CDKBucketDeployment', { Exclude: ['sample.js'], }); }); @@ -817,7 +816,7 @@ test('deploy with included files from destination', () => { include: ['sample.js'], }); - expect(stack).toHaveResourceLike('Custom::CDKBucketDeployment', { + Template.fromStack(stack).hasResourceProperties('Custom::CDKBucketDeployment', { Include: ['sample.js'], }); }); @@ -836,7 +835,7 @@ test('deploy with excluded and included files from destination', () => { include: ['sample/include.json'], }); - expect(stack).toHaveResourceLike('Custom::CDKBucketDeployment', { + Template.fromStack(stack).hasResourceProperties('Custom::CDKBucketDeployment', { Exclude: ['sample/*'], Include: ['sample/include.json'], }); @@ -856,7 +855,7 @@ test('deploy with multiple exclude and include filters', () => { include: ['sample/include.json', 'another/include.json'], }); - expect(stack).toHaveResourceLike('Custom::CDKBucketDeployment', { + Template.fromStack(stack).hasResourceProperties('Custom::CDKBucketDeployment', { Exclude: ['sample/*', 'another/*'], Include: ['sample/include.json', 'another/include.json'], }); @@ -877,7 +876,7 @@ test('deployment allows vpc to be implicitly supplied to lambda', () => { }); // THEN - expect(stack).toHaveResource('AWS::Lambda::Function', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { VpcConfig: { SecurityGroupIds: [ { @@ -922,7 +921,7 @@ test('deployment allows vpc and subnets to be implicitly supplied to lambda', () }); // THEN - expect(stack).toHaveResource('AWS::Lambda::Function', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { VpcConfig: { SecurityGroupIds: [ { @@ -957,13 +956,13 @@ test('resource id includes memory and vpc', () => { }); // THEN - expect(stack).toMatchTemplate({ - Resources: objectLike({ - DeployWithVpc2CustomResource256MiBc8a39596cb8641929fcf6a288bc9db5ab7b0f656ad3C5F6E78: objectLike({ + Template.fromStack(stack).templateMatches({ + Resources: Match.objectLike({ + DeployWithVpc2CustomResource256MiBc8a39596cb8641929fcf6a288bc9db5ab7b0f656ad3C5F6E78: Match.objectLike({ Type: 'Custom::CDKBucketDeployment', }), }), - }, MatchStyle.SUPERSET); + }); }); test('bucket includes custom resource owner tag', () => { @@ -983,7 +982,7 @@ test('bucket includes custom resource owner tag', () => { }); // THEN - expect(stack).toHaveResource('AWS::S3::Bucket', { + Template.fromStack(stack).hasResourceProperties('AWS::S3::Bucket', { Tags: [{ Key: 'aws-cdk:cr-owned:/a/b/c:971e1fa8', Value: 'true', @@ -1040,7 +1039,7 @@ test('bucket has multiple deployments', () => { }); // THEN - expect(stack).toHaveResource('AWS::S3::Bucket', { + Template.fromStack(stack).hasResourceProperties('AWS::S3::Bucket', { Tags: [ { Key: 'aws-cdk:cr-owned:/a/b/c:6da0a4ab', From 93bc333cb8fbfc5703b7a05eb04e35b30c20f049 Mon Sep 17 00:00:00 2001 From: Otavio Macedo Date: Tue, 18 Jan 2022 20:14:43 +0000 Subject: [PATCH 07/50] chore(s3-notifications): migrated tests to `assertions` (#18465) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-s3-notifications/package.json | 2 +- .../test/lambda/lambda.test.ts | 44 +++++++++---------- .../test/notifications.test.ts | 40 ++++++++--------- .../aws-s3-notifications/test/queue.test.ts | 19 ++++---- .../aws-s3-notifications/test/sns.test.ts | 4 +- 5 files changed, 55 insertions(+), 54 deletions(-) diff --git a/packages/@aws-cdk/aws-s3-notifications/package.json b/packages/@aws-cdk/aws-s3-notifications/package.json index 3723da0f32e79..f40467d02c99d 100644 --- a/packages/@aws-cdk/aws-s3-notifications/package.json +++ b/packages/@aws-cdk/aws-s3-notifications/package.json @@ -71,7 +71,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/pkglint": "0.0.0", diff --git a/packages/@aws-cdk/aws-s3-notifications/test/lambda/lambda.test.ts b/packages/@aws-cdk/aws-s3-notifications/test/lambda/lambda.test.ts index 4d80618ad8ff3..d6e563a4438ca 100644 --- a/packages/@aws-cdk/aws-s3-notifications/test/lambda/lambda.test.ts +++ b/packages/@aws-cdk/aws-s3-notifications/test/lambda/lambda.test.ts @@ -1,6 +1,4 @@ -// import { SynthUtils } from '@aws-cdk/assert-internal'; -import { ResourcePart } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as lambda from '@aws-cdk/aws-lambda'; import * as s3 from '@aws-cdk/aws-s3'; import { Stack, App } from '@aws-cdk/core'; @@ -29,20 +27,20 @@ test('add notifications to multiple functions', () => { bucket.addEventNotification(s3.EventType.OBJECT_CREATED, lambdaDestination2, { prefix: 'v2/' }); // expecting notification configuration to have both events - expect(stack).toHaveResourceLike('Custom::S3BucketNotifications', { - NotificationConfiguration: { + Template.fromStack(stack).hasResourceProperties('Custom::S3BucketNotifications', { + NotificationConfiguration: Match.objectLike({ LambdaFunctionConfigurations: [ - { Filter: { Key: { FilterRules: [{ Name: 'prefix', Value: 'v1/' }] } } }, - { Filter: { Key: { FilterRules: [{ Name: 'prefix', Value: 'v2/' }] } } }, + Match.objectLike({ Filter: { Key: { FilterRules: [{ Name: 'prefix', Value: 'v1/' }] } } }), + Match.objectLike({ Filter: { Key: { FilterRules: [{ Name: 'prefix', Value: 'v2/' }] } } }), ], - }, + }), }); // expecting one permission for each function - expect(stack).toCountResources('AWS::Lambda::Permission', 2); + Template.fromStack(stack).resourceCountIs('AWS::Lambda::Permission', 2); // make sure each permission points to the correct function - expect(stack).toHaveResourceLike('AWS::Lambda::Permission', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Permission', { FunctionName: { 'Fn::GetAtt': [ 'MyFunction12A744C2E', @@ -56,7 +54,7 @@ test('add notifications to multiple functions', () => { ], }, }); - expect(stack).toHaveResourceLike('AWS::Lambda::Permission', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Permission', { FunctionName: { 'Fn::GetAtt': [ 'MyFunction2F2A964CA', @@ -89,7 +87,7 @@ test('lambda in a different stack as notification target', () => { bucket.addObjectCreatedNotification(new s3n.LambdaDestination(lambdaFunction)); // permission should be in the bucket stack - expect(bucketStack).toHaveResourceLike('AWS::Lambda::Permission', { + Template.fromStack(bucketStack).hasResourceProperties('AWS::Lambda::Permission', { FunctionName: { 'Fn::ImportValue': 'stack1:ExportsOutputFnGetAttlambdaFunction940E68ADArn6B2878AF', }, @@ -115,7 +113,7 @@ test('imported lambda in a different account as notification target', () => { bucket.addObjectCreatedNotification(new s3n.LambdaDestination(lambdaFunction)); // no permissions created - expect(stack).not.toHaveResourceLike('AWS::Lambda::Permission'); + Template.fromStack(stack).resourceCountIs('AWS::Lambda::Permission', 0); }); test('lambda as notification target', () => { @@ -132,7 +130,7 @@ test('lambda as notification target', () => { bucketA.addObjectCreatedNotification(new s3n.LambdaDestination(fn), { suffix: '.png' }); // THEN - expect(stack).toHaveResource('AWS::Lambda::Permission', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Permission', { Action: 'lambda:InvokeFunction', FunctionName: { 'Fn::GetAtt': ['MyFunction3BAA72D1', 'Arn'] }, Principal: 's3.amazonaws.com', @@ -140,7 +138,7 @@ test('lambda as notification target', () => { SourceArn: { 'Fn::GetAtt': ['MyBucketF68F3FF0', 'Arn'] }, }); - expect(stack).toHaveResource('Custom::S3BucketNotifications', { + Template.fromStack(stack).hasResourceProperties('Custom::S3BucketNotifications', { NotificationConfiguration: { LambdaFunctionConfigurations: [ { @@ -167,7 +165,7 @@ test('lambda as notification target specified by function arn', () => { bucketA.addObjectCreatedNotification(new s3n.LambdaDestination(fn), { suffix: '.png' }); // THEN - expect(stack).toHaveResource('Custom::S3BucketNotifications', { + Template.fromStack(stack).hasResourceProperties('Custom::S3BucketNotifications', { NotificationConfiguration: { LambdaFunctionConfigurations: [ { @@ -199,9 +197,9 @@ test('permissions are added as a dependency to the notifications resource when u bucket.addEventNotification(s3.EventType.OBJECT_CREATED, lambdaDestination, { prefix: 'v1/' }); - expect(stack).toHaveResource('Custom::S3BucketNotifications', { + Template.fromStack(stack).hasResource('Custom::S3BucketNotifications', { DependsOn: ['MyBucketAllowBucketNotificationsToSingletonLambdauuid28C96883'], - }, ResourcePart.CompleteDefinition); + }); }); test('add multiple event notifications using a singleton function', () => { @@ -220,13 +218,13 @@ test('add multiple event notifications using a singleton function', () => { bucket.addEventNotification(s3.EventType.OBJECT_CREATED, lambdaDestination, { prefix: 'v1/' }); bucket.addEventNotification(s3.EventType.OBJECT_CREATED, lambdaDestination, { prefix: 'v2/' }); - expect(stack).toHaveResourceLike('Custom::S3BucketNotifications', { - NotificationConfiguration: { + Template.fromStack(stack).hasResourceProperties('Custom::S3BucketNotifications', { + NotificationConfiguration: Match.objectLike({ LambdaFunctionConfigurations: [ - { Filter: { Key: { FilterRules: [{ Name: 'prefix', Value: 'v1/' }] } } }, - { Filter: { Key: { FilterRules: [{ Name: 'prefix', Value: 'v2/' }] } } }, + Match.objectLike({ Filter: { Key: { FilterRules: [{ Name: 'prefix', Value: 'v1/' }] } } }), + Match.objectLike({ Filter: { Key: { FilterRules: [{ Name: 'prefix', Value: 'v2/' }] } } }), ], - }, + }), }); }); diff --git a/packages/@aws-cdk/aws-s3-notifications/test/notifications.test.ts b/packages/@aws-cdk/aws-s3-notifications/test/notifications.test.ts index 2bfe968f99279..ee4cbc9c5c5d0 100644 --- a/packages/@aws-cdk/aws-s3-notifications/test/notifications.test.ts +++ b/packages/@aws-cdk/aws-s3-notifications/test/notifications.test.ts @@ -1,5 +1,4 @@ -import { SynthUtils } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as s3 from '@aws-cdk/aws-s3'; import * as sns from '@aws-cdk/aws-sns'; import * as cdk from '@aws-cdk/core'; @@ -13,7 +12,7 @@ test('bucket without notifications', () => { new s3.Bucket(stack, 'MyBucket'); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ 'Resources': { 'MyBucketF68F3FF0': { 'Type': 'AWS::S3::Bucket', @@ -33,7 +32,7 @@ test('notifications can be added to imported buckets', () => { bucket.addEventNotification(s3.EventType.OBJECT_CREATED, new s3n.SnsDestination(topic)); - expect(stack).toHaveResource('Custom::S3BucketNotifications', { + Template.fromStack(stack).hasResourceProperties('Custom::S3BucketNotifications', { ServiceToken: { 'Fn::GetAtt': ['BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691', 'Arn'] }, BucketName: 'mybucket', Managed: false, @@ -61,9 +60,9 @@ test('when notification are added, a custom resource is provisioned + a lambda h bucket.addEventNotification(s3.EventType.OBJECT_CREATED, new s3n.SnsDestination(topic)); - expect(stack).toHaveResource('AWS::S3::Bucket'); - expect(stack).toHaveResource('AWS::Lambda::Function', { Description: 'AWS CloudFormation handler for "Custom::S3BucketNotifications" resources (@aws-cdk/aws-s3)' }); - expect(stack).toHaveResource('Custom::S3BucketNotifications'); + Template.fromStack(stack).resourceCountIs('AWS::S3::Bucket', 1); + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { Description: 'AWS CloudFormation handler for "Custom::S3BucketNotifications" resources (@aws-cdk/aws-s3)' }); + Template.fromStack(stack).resourceCountIs('Custom::S3BucketNotifications', 1); }); test('when notification are added, you can tag the lambda', () => { @@ -76,12 +75,12 @@ test('when notification are added, you can tag the lambda', () => { bucket.addEventNotification(s3.EventType.OBJECT_CREATED, new s3n.SnsDestination(topic)); - expect(stack).toHaveResource('AWS::S3::Bucket'); - expect(stack).toHaveResource('AWS::Lambda::Function', { + Template.fromStack(stack).resourceCountIs('AWS::S3::Bucket', 1); + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { Tags: [{ Key: 'Lambda', Value: 'AreTagged' }], Description: 'AWS CloudFormation handler for "Custom::S3BucketNotifications" resources (@aws-cdk/aws-s3)', }); - expect(stack).toHaveResource('Custom::S3BucketNotifications'); + Template.fromStack(stack).resourceCountIs('Custom::S3BucketNotifications', 1); }); test('bucketNotificationTarget is not called during synthesis', () => { @@ -95,7 +94,7 @@ test('bucketNotificationTarget is not called during synthesis', () => { bucket.addObjectCreatedNotification(new s3n.SnsDestination(topic)); - expect(stack).toHaveResourceLike('AWS::SNS::TopicPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::SNS::TopicPolicy', { 'Topics': [ { 'Ref': 'TopicBFC7AF6E', @@ -122,6 +121,7 @@ test('bucketNotificationTarget is not called during synthesis', () => { 'Resource': { 'Ref': 'TopicBFC7AF6E', }, + 'Sid': '0', }, ], 'Version': '2012-10-17', @@ -159,7 +159,7 @@ test('subscription types', () => { bucket.addEventNotification(s3.EventType.OBJECT_CREATED, lambdaTarget); bucket.addObjectRemovedNotification(topicTarget, { prefix: 'prefix' }); - expect(stack).toHaveResource('Custom::S3BucketNotifications', { + Template.fromStack(stack).hasResourceProperties('Custom::S3BucketNotifications', { 'ServiceToken': { 'Fn::GetAtt': [ 'BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691', @@ -227,7 +227,7 @@ test('multiple subscriptions of the same type', () => { }), }); - expect(stack).toHaveResource('Custom::S3BucketNotifications', { + Template.fromStack(stack).hasResourceProperties('Custom::S3BucketNotifications', { 'ServiceToken': { 'Fn::GetAtt': [ 'BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691', @@ -268,7 +268,7 @@ test('prefix/suffix filters', () => { bucket.addEventNotification(s3.EventType.OBJECT_REMOVED_DELETE, { bind: _ => bucketNotificationTarget }, { prefix: 'images/', suffix: '.jpg' }); - expect(stack).toHaveResource('Custom::S3BucketNotifications', { + Template.fromStack(stack).hasResourceProperties('Custom::S3BucketNotifications', { 'ServiceToken': { 'Fn::GetAtt': [ 'BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691', @@ -320,7 +320,7 @@ test('a notification destination can specify a set of dependencies that must be bucket.addObjectCreatedNotification(dest); - expect(SynthUtils.synthesize(stack).template.Resources.BucketNotifications8F2E257D).toEqual({ + expect(Template.fromStack(stack).findResources('Custom::S3BucketNotifications').BucketNotifications8F2E257D).toEqual({ Type: 'Custom::S3BucketNotifications', Properties: { ServiceToken: { 'Fn::GetAtt': ['BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691', 'Arn'] }, @@ -344,7 +344,7 @@ describe('CloudWatch Events', () => { }, }); - expect(stack).toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { 'EventPattern': { 'source': [ 'aws.s3', @@ -387,7 +387,7 @@ describe('CloudWatch Events', () => { paths: ['my/path.zip'], }); - expect(stack).toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { 'EventPattern': { 'source': [ 'aws.s3', @@ -429,7 +429,7 @@ describe('CloudWatch Events', () => { }, }); - expect(stack).toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { 'EventPattern': { 'source': [ 'aws.s3', @@ -457,7 +457,7 @@ describe('CloudWatch Events', () => { }, }); - expect(stack).toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { 'EventPattern': { 'source': [ 'aws.s3', @@ -485,7 +485,7 @@ describe('CloudWatch Events', () => { paths: ['my/path.zip'], }); - expect(stack).toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { 'EventPattern': { 'source': [ 'aws.s3', diff --git a/packages/@aws-cdk/aws-s3-notifications/test/queue.test.ts b/packages/@aws-cdk/aws-s3-notifications/test/queue.test.ts index 22c2edac592c8..b1ae15e895a7c 100644 --- a/packages/@aws-cdk/aws-s3-notifications/test/queue.test.ts +++ b/packages/@aws-cdk/aws-s3-notifications/test/queue.test.ts @@ -1,5 +1,4 @@ -import { arrayWith, SynthUtils } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as s3 from '@aws-cdk/aws-s3'; import * as sqs from '@aws-cdk/aws-sqs'; import { Stack } from '@aws-cdk/core'; @@ -13,7 +12,7 @@ test('queues can be used as destinations', () => { bucket.addObjectRemovedNotification(new notif.SqsDestination(queue)); - expect(stack).toHaveResource('AWS::SQS::QueuePolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::SQS::QueuePolicy', { PolicyDocument: { Statement: [ { @@ -44,7 +43,7 @@ test('queues can be used as destinations', () => { ], }); - expect(stack).toHaveResource('Custom::S3BucketNotifications', { + Template.fromStack(stack).hasResourceProperties('Custom::S3BucketNotifications', { BucketName: { Ref: 'Bucket83908E77', }, @@ -67,7 +66,11 @@ test('queues can be used as destinations', () => { // make sure the queue policy is added as a dependency to the bucket // notifications resource so it will be created first. - expect(SynthUtils.synthesize(stack).template.Resources.BucketNotifications8F2E257D.DependsOn).toEqual(['QueuePolicy25439813', 'Queue4A7E3555']); + + const resources = Template.fromStack(stack).findResources('Custom::S3BucketNotifications'); + + expect(resources.BucketNotifications8F2E257D.DependsOn) + .toEqual(['QueuePolicy25439813', 'Queue4A7E3555']); }); test('if the queue is encrypted with a custom kms key, the key resource policy is updated to allow s3 to read messages', () => { @@ -77,9 +80,9 @@ test('if the queue is encrypted with a custom kms key, the key resource policy i bucket.addObjectCreatedNotification(new notif.SqsDestination(queue)); - expect(stack).toHaveResourceLike('AWS::KMS::Key', { + Template.fromStack(stack).hasResourceProperties('AWS::KMS::Key', { KeyPolicy: { - Statement: arrayWith({ + Statement: Match.arrayWith([{ Action: [ 'kms:GenerateDataKey*', 'kms:Decrypt', @@ -89,7 +92,7 @@ test('if the queue is encrypted with a custom kms key, the key resource policy i Service: 's3.amazonaws.com', }, Resource: '*', - }), + }]), }, }); }); diff --git a/packages/@aws-cdk/aws-s3-notifications/test/sns.test.ts b/packages/@aws-cdk/aws-s3-notifications/test/sns.test.ts index 2c91becaae188..8a1d607113059 100644 --- a/packages/@aws-cdk/aws-s3-notifications/test/sns.test.ts +++ b/packages/@aws-cdk/aws-s3-notifications/test/sns.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as s3 from '@aws-cdk/aws-s3'; import * as sns from '@aws-cdk/aws-sns'; import * as cdk from '@aws-cdk/core'; @@ -16,7 +16,7 @@ test('asBucketNotificationDestination adds bucket permissions only once for each // another bucket will be added to the topic policy new notif.SnsDestination(topic).bind(bucket2, bucket2); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { Bucket83908E77: { Type: 'AWS::S3::Bucket', From dcf6870df177f20ee238eed03d4ebe4fa52c8277 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy <36202692+kaizen3031593@users.noreply.github.com> Date: Tue, 18 Jan 2022 16:02:15 -0500 Subject: [PATCH 08/50] chore(stepfunctions): fix readme example (#18442) Makes this example work for v2, and `cdk synth` successfully. Reference: #18316. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-stepfunctions/README.md | 10 +++++++--- .../aws-stepfunctions/rosetta/default.ts-fixture | 8 ++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/@aws-cdk/aws-stepfunctions/README.md b/packages/@aws-cdk/aws-stepfunctions/README.md index e74367df84676..eae413c7c3574 100644 --- a/packages/@aws-cdk/aws-stepfunctions/README.md +++ b/packages/@aws-cdk/aws-stepfunctions/README.md @@ -485,9 +485,9 @@ The class `StateMachineFragment` contains some helper functions (like machine as a subclass of this, it will be convenient to use: ```ts nofixture -import { Construct, Stack } from '@aws-cdk/core'; +import { Stack } from '@aws-cdk/core'; +import { Construct } from 'constructs'; import * as sfn from '@aws-cdk/aws-stepfunctions'; -import * as tasks from '@aws-cdk/aws-stepfunctions-tasks'; interface MyJobProps { jobFlavor: string; @@ -515,10 +515,14 @@ class MyStack extends Stack { constructor(scope: Construct, id: string) { super(scope, id); // Do 3 different variants of MyJob in parallel - new sfn.Parallel(this, 'All jobs') + const parallel = new sfn.Parallel(this, 'All jobs') .branch(new MyJob(this, 'Quick', { jobFlavor: 'quick' }).prefixStates()) .branch(new MyJob(this, 'Medium', { jobFlavor: 'medium' }).prefixStates()) .branch(new MyJob(this, 'Slow', { jobFlavor: 'slow' }).prefixStates()); + + new sfn.StateMachine(this, 'MyStateMachine', { + definition: parallel, + }); } } ``` diff --git a/packages/@aws-cdk/aws-stepfunctions/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-stepfunctions/rosetta/default.ts-fixture index 91085b6c8933c..01f59d49ec36d 100644 --- a/packages/@aws-cdk/aws-stepfunctions/rosetta/default.ts-fixture +++ b/packages/@aws-cdk/aws-stepfunctions/rosetta/default.ts-fixture @@ -1,10 +1,10 @@ // Fixture with packages imported, but nothing else import { Construct } from 'constructs'; import { App, CfnOutput, Duration, Stack } from '@aws-cdk/core'; -import sfn = require('@aws-cdk/aws-stepfunctions'); -import tasks = require('@aws-cdk/aws-stepfunctions-tasks'); -import cloudwatch = require('@aws-cdk/aws-cloudwatch'); -import iam = require('@aws-cdk/aws-iam'); +import * as sfn from '@aws-cdk/aws-stepfunctions'; +import * as tasks from '@aws-cdk/aws-stepfunctions-tasks'; +import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; +import * as iam from '@aws-cdk/aws-iam'; class Fixture extends Stack { constructor(scope: Construct, id: string) { From 2415a802a4fb59c4c00cf014f736a46b2acd718b Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Tue, 18 Jan 2022 22:50:22 +0100 Subject: [PATCH 09/50] chore: prevent empty zip uploads (#18487) Due to something we haven't completely figured out yet, our asset packaging sometimes produces empty zip files, leading to an error like this uploading code Lambda: ``` Uploaded file must be a non-empty zip ``` Do the following to address this: * If any empty zip files already ended up in S3, do not regard this as a cache hit. Rebuild the asset and upload it again. We do this by checking the item's size: this may be overly pessimistic, but if it is we're probably not wasting a lot of time rebuilding and uploading a tiny file. * If a fresh asset build is producing an empty zip file, loudly complain and ask the user to come to our bug tracker, so we can figure out where these spurious issues are coming from. Relates to #18459. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../cdk-assets/lib/private/handlers/files.ts | 51 +++++++++- packages/cdk-assets/test/fake-listener.ts | 1 + packages/cdk-assets/test/files.test.ts | 98 +++++++++++-------- 3 files changed, 104 insertions(+), 46 deletions(-) diff --git a/packages/cdk-assets/lib/private/handlers/files.ts b/packages/cdk-assets/lib/private/handlers/files.ts index 7433d7d9be517..cf77eda43dddc 100644 --- a/packages/cdk-assets/lib/private/handlers/files.ts +++ b/packages/cdk-assets/lib/private/handlers/files.ts @@ -10,6 +10,13 @@ import { pathExists } from '../fs-extra'; import { replaceAwsPlaceholders } from '../placeholders'; import { shell } from '../shell'; +/** + * The size of an empty zip file is 22 bytes + * + * Ref: https://en.wikipedia.org/wiki/ZIP_(file_format) + */ +const EMPTY_ZIP_FILE_SIZE = 22; + export class FileAssetHandler implements IAssetHandler { private readonly fileCacheRoot: string; @@ -74,6 +81,34 @@ export class FileAssetHandler implements IAssetHandler { const publishFile = this.asset.source.executable ? await this.externalPackageFile(this.asset.source.executable) : await this.packageFile(this.asset.source); + // Add a validation to catch the cases where we're accidentally producing an empty ZIP file (or worse, + // an empty file) + if (publishFile.contentType === 'application/zip') { + const fileSize = (await fs.stat(publishFile.packagedPath)).size; + if (fileSize <= EMPTY_ZIP_FILE_SIZE) { + const message = [ + '🚨 WARNING: EMPTY ZIP FILE 🚨', + '', + 'Zipping this asset produced an empty zip file. We do not know the root cause for this yet, and we need your help tracking it down.', + '', + 'Please visit https://github.com/aws/aws-cdk/issues/18459 and tell us:', + 'Your OS version, Nodejs version, CLI version, package manager, what the asset is supposed to contain, whether', + 'or not this error is reproducible, what files are in your cdk.out directory, if you recently changed anything,', + 'and anything else you think might be relevant.', + '', + 'The deployment will continue, but it may fail. You can try removing the cdk.out directory and running the command', + 'again; let us know if that resolves it.', + '', + 'If you meant to produce an empty asset file on purpose, you can add an empty dotfile to the asset for now', + 'to disable this notice.', + ]; + + for (const line of message) { + this.host.emitMessage(EventType.FAIL, line); + } + } + } + this.host.emitMessage(EventType.UPLOAD, `Upload ${s3Url}`); const params = Object.assign({}, { @@ -101,11 +136,11 @@ export class FileAssetHandler implements IAssetHandler { const packagedPath = path.join(this.fileCacheRoot, `${this.asset.id.assetId}.zip`); if (await pathExists(packagedPath)) { - this.host.emitMessage(EventType.CACHED, `From cache ${path}`); + this.host.emitMessage(EventType.CACHED, `From cache ${packagedPath}`); return { packagedPath, contentType }; } - this.host.emitMessage(EventType.BUILD, `Zip ${fullPath} -> ${path}`); + this.host.emitMessage(EventType.BUILD, `Zip ${fullPath} -> ${packagedPath}`); await zipDirectory(fullPath, packagedPath); return { packagedPath, contentType }; } else { @@ -149,9 +184,19 @@ async function objectExists(s3: AWS.S3, bucket: string, key: string) { * prefix, and limiting results to 1. Since the list operation returns keys ordered by binary * UTF-8 representation, the key we are looking for is guaranteed to always be the first match * returned if it exists. + * + * If the file is too small, we discount it as a cache hit. There is an issue + * somewhere that sometimes produces empty zip files, and we would otherwise + * never retry building those assets without users having to manually clear + * their bucket, which is a bad experience. */ const response = await s3.listObjectsV2({ Bucket: bucket, Prefix: key, MaxKeys: 1 }).promise(); - return response.Contents != null && response.Contents.some(object => object.Key === key); + return ( + response.Contents != null && + response.Contents.some( + (object) => object.Key === key && (object.Size == null || object.Size > EMPTY_ZIP_FILE_SIZE), + ) + ); } diff --git a/packages/cdk-assets/test/fake-listener.ts b/packages/cdk-assets/test/fake-listener.ts index 46b7f31a3ecc7..7aef7fbe9d9f6 100644 --- a/packages/cdk-assets/test/fake-listener.ts +++ b/packages/cdk-assets/test/fake-listener.ts @@ -1,6 +1,7 @@ import { IPublishProgressListener, EventType, IPublishProgress } from '../lib/progress'; export class FakeListener implements IPublishProgressListener { + public readonly types = new Array(); public readonly messages = new Array(); constructor(private readonly doAbort = false) { diff --git a/packages/cdk-assets/test/files.test.ts b/packages/cdk-assets/test/files.test.ts index 7616482d43944..9ef52ef8a21a8 100644 --- a/packages/cdk-assets/test/files.test.ts +++ b/packages/cdk-assets/test/files.test.ts @@ -2,13 +2,20 @@ jest.mock('child_process'); import { Manifest } from '@aws-cdk/cloud-assembly-schema'; import * as mockfs from 'mock-fs'; -import { AssetManifest, AssetPublishing } from '../lib'; +import { AssetPublishing, AssetManifest } from '../lib'; import { FakeListener } from './fake-listener'; import { mockAws, mockedApiFailure, mockedApiResult, mockUpload } from './mock-aws'; import { mockSpawn } from './mock-child_process'; const ABS_PATH = '/simple/cdk.out/some_external_file'; +const DEFAULT_DESTINATION = { + region: 'us-north-50', + assumeRoleArn: 'arn:aws:role', + bucketName: 'some_bucket', + objectKey: 'some_key', +}; + let aws: ReturnType; beforeEach(() => { jest.resetAllMocks(); @@ -21,19 +28,12 @@ beforeEach(() => { source: { path: 'some_file', }, - destinations: { - theDestination: { - region: 'us-north-50', - assumeRoleArn: 'arn:aws:role', - bucketName: 'some_bucket', - objectKey: 'some_key', - }, - }, + destinations: { theDestination: DEFAULT_DESTINATION }, }, }, }), '/simple/cdk.out/some_file': 'FILE_CONTENTS', - [ABS_PATH]: 'FILE_CONTENTS', + [ABS_PATH]: 'ZIP_FILE_THAT_IS_DEFINITELY_NOT_EMPTY', '/abs/cdk.out/assets.json': JSON.stringify({ version: Manifest.version(), files: { @@ -41,14 +41,7 @@ beforeEach(() => { source: { path: '/simple/cdk.out/some_file', }, - destinations: { - theDestination: { - region: 'us-north-50', - assumeRoleArn: 'arn:aws:role', - bucketName: 'some_other_bucket', - objectKey: 'some_key', - }, - }, + destinations: { theDestination: { ...DEFAULT_DESTINATION, bucketName: 'some_other_bucket' } }, }, }, }), @@ -59,14 +52,7 @@ beforeEach(() => { source: { executable: ['sometool'], }, - destinations: { - theDestination: { - region: 'us-north-50', - assumeRoleArn: 'arn:aws:role', - bucketName: 'some_external_bucket', - objectKey: 'some_key', - }, - }, + destinations: { theDestination: { ...DEFAULT_DESTINATION, bucketName: 'some_external_bucket' } }, }, }, }), @@ -77,32 +63,31 @@ beforeEach(() => { source: { path: 'plain_text.txt', }, - destinations: { - theDestination: { - region: 'us-north-50', - assumeRoleArn: 'arn:aws:role', - bucketName: 'some_bucket', - objectKey: 'some_key.txt', - }, - }, + destinations: { theDestination: { ...DEFAULT_DESTINATION, objectKey: 'some_key.txt' } }, }, theImageAsset: { source: { path: 'image.png', }, - destinations: { - theDestination: { - region: 'us-north-50', - assumeRoleArn: 'arn:aws:role', - bucketName: 'some_bucket', - objectKey: 'some_key.png', - }, - }, + destinations: { theDestination: { ...DEFAULT_DESTINATION, objectKey: 'some_key.png' } }, }, }, }), '/types/cdk.out/plain_text.txt': 'FILE_CONTENTS', '/types/cdk.out/image.png': 'FILE_CONTENTS', + '/emptyzip/cdk.out/assets.json': JSON.stringify({ + version: Manifest.version(), + files: { + theTextAsset: { + source: { + path: 'empty_dir', + packaging: 'zip', + }, + destinations: { theDestination: DEFAULT_DESTINATION }, + }, + }, + }), + '/emptyzip/cdk.out/empty_dir': { }, // Empty directory }); aws = mockAws(); @@ -114,6 +99,7 @@ afterEach(() => { test('pass destination properties to AWS client', async () => { const pub = new AssetPublishing(AssetManifest.fromPath('/simple/cdk.out'), { aws, throwOnError: false }); + aws.mockS3.listObjectsV2 = mockedApiResult({}); await pub.publish(); @@ -127,6 +113,7 @@ test('Do nothing if file already exists', async () => { const pub = new AssetPublishing(AssetManifest.fromPath('/simple/cdk.out'), { aws }); aws.mockS3.listObjectsV2 = mockedApiResult({ Contents: [{ Key: 'some_key' }] }); + aws.mockS3.upload = mockUpload(); await pub.publish(); expect(aws.mockS3.listObjectsV2).toHaveBeenCalledWith(expect.objectContaining({ @@ -134,8 +121,21 @@ test('Do nothing if file already exists', async () => { Prefix: 'some_key', MaxKeys: 1, })); + expect(aws.mockS3.upload).not.toHaveBeenCalled(); +}); + +test('tiny file does not count as cache hit', async () => { + const pub = new AssetPublishing(AssetManifest.fromPath('/simple/cdk.out'), { aws }); + + aws.mockS3.listObjectsV2 = mockedApiResult({ Contents: [{ Key: 'some_key', Size: 5 }] }); + aws.mockS3.upload = mockUpload(); + + await pub.publish(); + + expect(aws.mockS3.upload).toHaveBeenCalled(); }); + test('upload file if new (list returns other key)', async () => { const pub = new AssetPublishing(AssetManifest.fromPath('/simple/cdk.out'), { aws }); @@ -310,6 +310,18 @@ test('correctly identify asset path if path is absolute', async () => { expect(true).toBeTruthy(); // No exception, satisfy linter }); +test('empty directory prints failures', async () => { + const progressListener = new FakeListener(); + const pub = new AssetPublishing(AssetManifest.fromPath('/emptyzip/cdk.out'), { aws, progressListener }); + + aws.mockS3.listObjectsV2 = mockedApiResult({ Contents: undefined }); + aws.mockS3.upload = mockUpload(); // Should not be hit + + await pub.publish(); + + expect(progressListener.messages).toContainEqual(expect.stringContaining('EMPTY ZIP FILE')); +}); + describe('external assets', () => { let pub: AssetPublishing; beforeEach(() => { @@ -330,7 +342,7 @@ describe('external assets', () => { test('upload external asset correctly', async () => { aws.mockS3.listObjectsV2 = mockedApiResult({ Contents: undefined }); - aws.mockS3.upload = mockUpload('FILE_CONTENTS'); + aws.mockS3.upload = mockUpload('ZIP_FILE_THAT_IS_DEFINITELY_NOT_EMPTY'); const expectAllSpawns = mockSpawn({ commandLine: ['sometool'], stdout: ABS_PATH }); await pub.publish(); From da5a305875f0b82b896861be3fcb12fddaa0cc7b Mon Sep 17 00:00:00 2001 From: Cory Hall <43035978+corymhall@users.noreply.github.com> Date: Tue, 18 Jan 2022 17:38:14 -0500 Subject: [PATCH 10/50] fix(cli): warning to upgrade to bootstrap version >= undefined (#18489) When we try to assume the lookup role and fail, we print out a warning telling the user to upgrade to the required bootstrap stack version. In cases where the user is not using the default stack synthesizer (legacy, custom, etc.) the cli might not have information on the lookup role. In those cases where we don't have the necessary information on the ARN of the lookup role or the required bootstrap version, we will not print any warnings. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../lib/api/cloudformation-deployments.ts | 12 +++-- packages/aws-cdk/test/cdk-toolkit.test.ts | 44 +++++++++++++++++++ 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/packages/aws-cdk/lib/api/cloudformation-deployments.ts b/packages/aws-cdk/lib/api/cloudformation-deployments.ts index 72c48bb109ee7..a4438ff0ca8f7 100644 --- a/packages/aws-cdk/lib/api/cloudformation-deployments.ts +++ b/packages/aws-cdk/lib/api/cloudformation-deployments.ts @@ -96,14 +96,20 @@ export async function prepareSdkWithLookupRoleFor( if (version < stack.lookupRole.requiresBootstrapStackVersion) { throw new Error(`Bootstrap stack version '${stack.lookupRole.requiresBootstrapStackVersion}' is required, found version '${version}'.`); } - } else if (!stackSdk.didAssumeRole) { + // we may not have assumed the lookup role because one was not provided + // if that is the case then don't print the upgrade warning + } else if (!stackSdk.didAssumeRole && stack.lookupRole?.requiresBootstrapStackVersion) { warning(upgradeMessage); } return { ...stackSdk, resolvedEnvironment }; } catch (e) { debug(e); - warning(warningMessage); - warning(upgradeMessage); + // only print out the warnings if the lookupRole exists AND there is a required + // bootstrap version, otherwise the warnings will print `undefined` + if (stack.lookupRole && stack.lookupRole.requiresBootstrapStackVersion) { + warning(warningMessage); + warning(upgradeMessage); + } throw (e); } } diff --git a/packages/aws-cdk/test/cdk-toolkit.test.ts b/packages/aws-cdk/test/cdk-toolkit.test.ts index 7b8f96d5d948c..c7ee3b87130fc 100644 --- a/packages/aws-cdk/test/cdk-toolkit.test.ts +++ b/packages/aws-cdk/test/cdk-toolkit.test.ts @@ -127,6 +127,13 @@ describe('readCurrentTemplate', () => { }, }, }, + { + stackName: 'Test-Stack-A', + template, + properties: { + assumeRoleArn: 'bloop:${AWS::Region}:${AWS::AccountId}', + }, + }, ], }); mockForEnvironment = jest.fn().mockImplementation(() => { return { sdk: mockCloudExecutable.sdkProvider.sdk, didAssumeRole: true }; }); @@ -145,6 +152,11 @@ describe('readCurrentTemplate', () => { StackStatus: 'CREATE_COMPLETE', CreationTime: new Date(), }, + { + StackName: 'Test-Stack-A', + StackStatus: 'CREATE_COMPLETE', + CreationTime: new Date(), + }, ], }; }, @@ -329,6 +341,38 @@ describe('readCurrentTemplate', () => { assumeRoleArn: 'bloop:here:123456789012', }); }); + + test('do not print warnings if lookup role not provided in stack artifact', async () => { + // GIVEN + mockCloudExecutable.sdkProvider.stubSSM({ + getParameter() { + return {}; + }, + }); + const cdkToolkit = new CdkToolkit({ + cloudExecutable: mockCloudExecutable, + configuration: mockCloudExecutable.configuration, + sdkProvider: mockCloudExecutable.sdkProvider, + cloudFormation: new CloudFormationDeployments({ sdkProvider: mockCloudExecutable.sdkProvider }), + }); + + // WHEN + await cdkToolkit.deploy({ + selector: { patterns: ['Test-Stack-A'] }, + }); + + // THEN + expect(flatten(stderrMock.mock.calls)).not.toEqual(expect.arrayContaining([ + expect.stringMatching(/Could not assume/), + expect.stringMatching(/please upgrade to bootstrap version/), + ])); + expect(mockCloudExecutable.sdkProvider.sdk.ssm).not.toHaveBeenCalled(); + expect(mockForEnvironment.mock.calls.length).toEqual(2); + expect(mockForEnvironment.mock.calls[0][2]).toEqual({ + assumeRoleArn: undefined, + assumeRoleExternalId: undefined, + }); + }); }); describe('deploy', () => { From 03d388d4f052e2d47a719378753b8ccbda3ba6ef Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Wed, 19 Jan 2022 00:27:13 +0100 Subject: [PATCH 11/50] chore(fsx): migrate tests to `assertions` (#18490) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-fsx/package.json | 2 +- .../aws-fsx/test/lustre-file-system.test.ts | 60 +++++++++---------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/packages/@aws-cdk/aws-fsx/package.json b/packages/@aws-cdk/aws-fsx/package.json index fb7cd9f8ef29d..0b0c1f0426004 100644 --- a/packages/@aws-cdk/aws-fsx/package.json +++ b/packages/@aws-cdk/aws-fsx/package.json @@ -81,7 +81,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-fsx/test/lustre-file-system.test.ts b/packages/@aws-cdk/aws-fsx/test/lustre-file-system.test.ts index ad4c4462d8c58..6f581475dbe3d 100644 --- a/packages/@aws-cdk/aws-fsx/test/lustre-file-system.test.ts +++ b/packages/@aws-cdk/aws-fsx/test/lustre-file-system.test.ts @@ -1,5 +1,5 @@ import { strictEqual } from 'assert'; -import { expect as expectCDK, haveResource, ResourcePart } from '@aws-cdk/assert-internal'; +import { Template } from '@aws-cdk/assertions'; import { ISubnet, Port, SecurityGroup, Subnet, Vpc } from '@aws-cdk/aws-ec2'; import { Key } from '@aws-cdk/aws-kms'; import { Aws, Stack, Token } from '@aws-cdk/core'; @@ -35,15 +35,15 @@ describe('FSx for Lustre File System', () => { vpcSubnet, }); - expectCDK(stack).to(haveResource('AWS::FSx::FileSystem')); - expectCDK(stack).to(haveResource('AWS::EC2::SecurityGroup')); + Template.fromStack(stack).hasResource('AWS::FSx::FileSystem', {}); + Template.fromStack(stack).hasResource('AWS::EC2::SecurityGroup', {}); strictEqual( fileSystem.dnsName, `${fileSystem.fileSystemId}.fsx.${stack.region}.${Aws.URL_SUFFIX}`); - expectCDK(stack).to(haveResource('AWS::FSx::FileSystem', { + Template.fromStack(stack).hasResource('AWS::FSx::FileSystem', { DeletionPolicy: 'Retain', - }, ResourcePart.CompleteDefinition)); + }); }); test('file system is created correctly when security group is provided', () => { @@ -63,8 +63,8 @@ describe('FSx for Lustre File System', () => { vpcSubnet, }); - expectCDK(stack).to(haveResource('AWS::FSx::FileSystem')); - expectCDK(stack).to(haveResource('AWS::EC2::SecurityGroup')); + Template.fromStack(stack).hasResource('AWS::FSx::FileSystem', {}); + Template.fromStack(stack).hasResource('AWS::EC2::SecurityGroup', {}); }); test('encrypted file system is created correctly with custom KMS', () => { @@ -88,11 +88,11 @@ describe('FSx for Lustre File System', () => { * in generated CDK, hence hardcoding the MD5 hash here for assertion. Assumption is that the path of the Key wont * change in this UT. Checked the unique id by generating the cloud formation stack. */ - expectCDK(stack).to(haveResource('AWS::FSx::FileSystem', { + Template.fromStack(stack).hasResourceProperties('AWS::FSx::FileSystem', { KmsKeyId: { Ref: 'customKeyFSDDB87C6D', }, - })); + }); }); test('file system is created correctly when weekly maintenance time is provided', () => { @@ -118,13 +118,13 @@ describe('FSx for Lustre File System', () => { vpcSubnet, }); - expectCDK(stack).to(haveResource('AWS::FSx::FileSystem', { + Template.fromStack(stack).hasResourceProperties('AWS::FSx::FileSystem', { LustreConfiguration: { DeploymentType: 'SCRATCH_2', WeeklyMaintenanceStartTime: '7:12:34', }, - })); - expectCDK(stack).to(haveResource('AWS::EC2::SecurityGroup')); + }); + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroup', {}); }); describe('when validating props', () => { @@ -145,13 +145,13 @@ describe('FSx for Lustre File System', () => { vpcSubnet, }); - expectCDK(stack).to(haveResource('AWS::FSx::FileSystem', { + Template.fromStack(stack).hasResourceProperties('AWS::FSx::FileSystem', { LustreConfiguration: { DeploymentType: LustreDeploymentType.SCRATCH_2, ExportPath: exportPath, ImportPath: importPath, }, - })); + }); }); test('export and import paths are Tokens', () => { @@ -172,13 +172,13 @@ describe('FSx for Lustre File System', () => { vpcSubnet, }); - expectCDK(stack).to(haveResource('AWS::FSx::FileSystem', { + Template.fromStack(stack).hasResourceProperties('AWS::FSx::FileSystem', { LustreConfiguration: { DeploymentType: LustreDeploymentType.SCRATCH_2, ExportPath: exportPathResolved, ImportPath: importPathResolved, }, - })); + }); }); test('only export path is Token', () => { @@ -299,12 +299,12 @@ describe('FSx for Lustre File System', () => { vpcSubnet, }); - expectCDK(stack).to(haveResource('AWS::FSx::FileSystem', { + Template.fromStack(stack).hasResourceProperties('AWS::FSx::FileSystem', { LustreConfiguration: { DeploymentType: LustreDeploymentType.SCRATCH_2, ImportedFileChunkSize: size, }, - })); + }); }); test.each([ @@ -342,12 +342,12 @@ describe('FSx for Lustre File System', () => { vpcSubnet, }); - expectCDK(stack).to(haveResource('AWS::FSx::FileSystem', { + Template.fromStack(stack).hasResourceProperties('AWS::FSx::FileSystem', { LustreConfiguration: { DeploymentType: LustreDeploymentType.SCRATCH_2, ImportPath: importPath, }, - })); + }); }); test('import path is Token', () => { @@ -364,12 +364,12 @@ describe('FSx for Lustre File System', () => { vpcSubnet, }); - expectCDK(stack).to(haveResource('AWS::FSx::FileSystem', { + Template.fromStack(stack).hasResourceProperties('AWS::FSx::FileSystem', { LustreConfiguration: { DeploymentType: LustreDeploymentType.SCRATCH_2, ImportPath: importPathResolved, }, - })); + }); }); test('invalid import path format', () => { @@ -428,12 +428,12 @@ describe('FSx for Lustre File System', () => { vpcSubnet, }); - expectCDK(stack).to(haveResource('AWS::FSx::FileSystem', { + Template.fromStack(stack).hasResourceProperties('AWS::FSx::FileSystem', { LustreConfiguration: { DeploymentType: LustreDeploymentType.PERSISTENT_1, PerUnitStorageThroughput: throughput, }, - })); + }); }); test('invalid perUnitStorageThroughput', () => { @@ -489,12 +489,12 @@ describe('FSx for Lustre File System', () => { vpcSubnet, }); - expectCDK(stack).to(haveResource('AWS::FSx::FileSystem', { + Template.fromStack(stack).hasResourceProperties('AWS::FSx::FileSystem', { LustreConfiguration: { DeploymentType: deploymentType, }, StorageCapacity: value, - })); + }); }); test.each([ @@ -529,12 +529,12 @@ describe('FSx for Lustre File System', () => { vpcSubnet, }); - expectCDK(stack).to(haveResource('AWS::FSx::FileSystem', { + Template.fromStack(stack).hasResourceProperties('AWS::FSx::FileSystem', { LustreConfiguration: { DeploymentType: LustreDeploymentType.SCRATCH_1, }, StorageCapacity: validValue, - })); + }); }); test.each([1, 3601])('invalid value of %d for storage capacity with SCRATCH_1', (invalidValue: number) => { @@ -566,8 +566,8 @@ describe('FSx for Lustre File System', () => { fs.connections.allowToAnyIpv4(Port.tcp(443)); - expectCDK(stack).to(haveResource('AWS::EC2::SecurityGroupEgress', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroupEgress', { GroupId: 'sg-123456789', - })); + }); }); }); \ No newline at end of file From 3f87b4d9bcaa54e76abed7deed4d462f15412ed4 Mon Sep 17 00:00:00 2001 From: Cory Hall <43035978+corymhall@users.noreply.github.com> Date: Tue, 18 Jan 2022 19:15:01 -0500 Subject: [PATCH 12/50] chore(lambda-nodejs): migrate tests to `assertions` (#18497) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-lambda-nodejs/package.json | 2 +- .../aws-lambda-nodejs/test/function.test.ts | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda-nodejs/package.json b/packages/@aws-cdk/aws-lambda-nodejs/package.json index 60915214f9a76..a9a64704b6be9 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/package.json +++ b/packages/@aws-cdk/aws-lambda-nodejs/package.json @@ -71,7 +71,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/aws-ec2": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", diff --git a/packages/@aws-cdk/aws-lambda-nodejs/test/function.test.ts b/packages/@aws-cdk/aws-lambda-nodejs/test/function.test.ts index 01b90d89dd6dd..f197bb34b60da 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/test/function.test.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/test/function.test.ts @@ -1,7 +1,6 @@ -import '@aws-cdk/assert-internal/jest'; import * as fs from 'fs'; import * as path from 'path'; -import { ABSENT } from '@aws-cdk/assert-internal'; +import { Template, Match } from '@aws-cdk/assertions'; import { Vpc } from '@aws-cdk/aws-ec2'; import { CodeConfig, Runtime } from '@aws-cdk/aws-lambda'; import { Stack } from '@aws-cdk/core'; @@ -40,7 +39,7 @@ test('NodejsFunction with .ts handler', () => { entry: expect.stringContaining('function.test.handler1.ts'), // Automatically finds .ts handler file })); - expect(stack).toHaveResource('AWS::Lambda::Function', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { Handler: 'index.handler', Runtime: 'nodejs14.x', }); @@ -155,7 +154,7 @@ test('configures connection reuse for aws sdk', () => { // WHEN new NodejsFunction(stack, 'handler1'); - expect(stack).toHaveResource('AWS::Lambda::Function', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { Environment: { Variables: { AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1', @@ -170,8 +169,8 @@ test('can opt-out of connection reuse for aws sdk', () => { awsSdkConnectionReuse: false, }); - expect(stack).toHaveResource('AWS::Lambda::Function', { - Environment: ABSENT, + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { + Environment: Match.absent(), }); }); @@ -183,7 +182,7 @@ test('NodejsFunction in a VPC', () => { new NodejsFunction(stack, 'handler1', { vpc }); // THEN - expect(stack).toHaveResource('AWS::Lambda::Function', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { VpcConfig: { SecurityGroupIds: [ { From 8c5d125ab438e228c7e96e5462a8b99dfe01c606 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Wed, 19 Jan 2022 02:03:44 +0100 Subject: [PATCH 13/50] chore(cloudformation): migrate tests to `assertions` (#18498) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-cloudformation/package.json | 2 +- .../aws-cloudformation/test/deps.test.ts | 39 ++++++----- .../test/nested-stack.test.ts | 66 +++++++++---------- .../aws-cloudformation/test/resource.test.ts | 15 ++--- 4 files changed, 60 insertions(+), 62 deletions(-) diff --git a/packages/@aws-cdk/aws-cloudformation/package.json b/packages/@aws-cdk/aws-cloudformation/package.json index c00a412462faa..5a7014994d6c5 100644 --- a/packages/@aws-cdk/aws-cloudformation/package.json +++ b/packages/@aws-cdk/aws-cloudformation/package.json @@ -69,7 +69,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/aws-events": "0.0.0", "@aws-cdk/aws-s3-assets": "0.0.0", "@aws-cdk/aws-sns-subscriptions": "0.0.0", diff --git a/packages/@aws-cdk/aws-cloudformation/test/deps.test.ts b/packages/@aws-cdk/aws-cloudformation/test/deps.test.ts index 0354c7e9e4fa8..1e814b754eece 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/deps.test.ts +++ b/packages/@aws-cdk/aws-cloudformation/test/deps.test.ts @@ -1,7 +1,6 @@ import * as fs from 'fs'; import * as path from 'path'; -import { ResourcePart } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { testDeprecated, describeDeprecated } from '@aws-cdk/cdk-build-tools'; import { App, CfnResource, Stack } from '@aws-cdk/core'; import * as cxapi from '@aws-cdk/cx-api'; @@ -39,8 +38,8 @@ describe('resource dependencies', () => { // THEN: the dependency needs to transfer from the resource within the // nested stack to the nested stack resource itself so the nested stack // will only be deployed the dependent resource - expect(parent).toHaveResource('AWS::CloudFormation::Stack', { DependsOn: ['ResourceInParent'] }, ResourcePart.CompleteDefinition); - expect(nested).toMatchTemplate({ Resources: { ResourceInNested: { Type: 'NESTED' } } }); // no DependsOn for the actual resource + Template.fromStack(parent).hasResource('AWS::CloudFormation::Stack', { DependsOn: ['ResourceInParent'] }); + Template.fromStack(nested).templateMatches({ Resources: { ResourceInNested: { Type: 'NESTED' } } }); // no DependsOn for the actual resource })); // eslint-disable-next-line jest/valid-describe @@ -57,8 +56,8 @@ describe('resource dependencies', () => { // THEN: the dependency needs to transfer from the resource within the // nested stack to the *parent* nested stack - expect(grantparent).toHaveResource('AWS::CloudFormation::Stack', { DependsOn: ['ResourceInGrandparent'] }, ResourcePart.CompleteDefinition); - expect(nested).toMatchTemplate({ Resources: { ResourceInNested: { Type: 'NESTED' } } }); // no DependsOn for the actual resource + Template.fromStack(grantparent).hasResource('AWS::CloudFormation::Stack', { DependsOn: ['ResourceInGrandparent'] }); + Template.fromStack(nested).templateMatches({ Resources: { ResourceInNested: { Type: 'NESTED' } } }); // no DependsOn for the actual resource })); // eslint-disable-next-line jest/valid-describe @@ -73,9 +72,9 @@ describe('resource dependencies', () => { addDep(resourceInParent, resourceInNested); // THEN: resource in parent needs to depend on the nested stack - expect(parent).toHaveResource('PARENT', { + Template.fromStack(parent).hasResource('PARENT', { DependsOn: [parent.resolve(nested.nestedStackResource!.logicalId)], - }, ResourcePart.CompleteDefinition); + }); })); // eslint-disable-next-line jest/valid-describe @@ -91,9 +90,9 @@ describe('resource dependencies', () => { addDep(resourceInGrandparent, resourceInNested); // THEN: resource in grantparent needs to depend on the top-level nested stack - expect(grandparent).toHaveResource('GRANDPARENT', { + Template.fromStack(grandparent).hasResource('GRANDPARENT', { DependsOn: [grandparent.resolve(parent.nestedStackResource!.logicalId)], - }, ResourcePart.CompleteDefinition); + }); })); // eslint-disable-next-line jest/valid-describe @@ -154,13 +153,13 @@ describe('resource dependencies', () => { addDep(resourceInNested1, resourceInNested2); // THEN: dependency transfered to nested stack resources - expect(stack).toHaveResource('AWS::CloudFormation::Stack', { + Template.fromStack(stack).hasResource('AWS::CloudFormation::Stack', { DependsOn: [stack.resolve(nested2.nestedStackResource!.logicalId)], - }, ResourcePart.CompleteDefinition); + }); - expect(stack).not.toHaveResource('AWS::CloudFormation::Stack', { + expect(Template.fromStack(stack).findResources('AWS::CloudFormation::Stack', { DependsOn: [stack.resolve(nested1.nestedStackResource!.logicalId)], - }, ResourcePart.CompleteDefinition); + })).toEqual({}); })); }); @@ -226,9 +225,9 @@ describe('stack dependencies', () => { nested1.addDependency(nested2); // THEN - expect(stack).toHaveResource('AWS::CloudFormation::Stack', { + Template.fromStack(stack).hasResource('AWS::CloudFormation::Stack', { DependsOn: [stack.resolve(nested2.nestedStackResource!.logicalId)], - }, ResourcePart.CompleteDefinition); + }); }); testDeprecated('nested stack depends on a deeply nested stack', () => { @@ -242,9 +241,9 @@ describe('stack dependencies', () => { nested1.addDependency(nested21); // THEN: transfered to a resource dep between the resources in the common stack - expect(stack).toHaveResource('AWS::CloudFormation::Stack', { + Template.fromStack(stack).hasResource('AWS::CloudFormation::Stack', { DependsOn: [stack.resolve(nested2.nestedStackResource!.logicalId)], - }, ResourcePart.CompleteDefinition); + }); }); testDeprecated('deeply nested stack depends on a parent nested stack', () => { @@ -258,9 +257,9 @@ describe('stack dependencies', () => { nested21.addDependency(nested1); // THEN: transfered to a resource dep between the resources in the common stack - expect(stack).toHaveResource('AWS::CloudFormation::Stack', { + Template.fromStack(stack).hasResource('AWS::CloudFormation::Stack', { DependsOn: [stack.resolve(nested1.nestedStackResource!.logicalId)], - }, ResourcePart.CompleteDefinition); + }); }); testDeprecated('top-level stack depends on a nested stack within a sibling', () => { diff --git a/packages/@aws-cdk/aws-cloudformation/test/nested-stack.test.ts b/packages/@aws-cdk/aws-cloudformation/test/nested-stack.test.ts index 07a43586971bd..a625012eba58a 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/nested-stack.test.ts +++ b/packages/@aws-cdk/aws-cloudformation/test/nested-stack.test.ts @@ -1,7 +1,6 @@ import * as fs from 'fs'; import * as path from 'path'; -import { SynthUtils } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as s3_assets from '@aws-cdk/aws-s3-assets'; import * as sns from '@aws-cdk/aws-sns'; import { describeDeprecated } from '@aws-cdk/cdk-build-tools'; @@ -122,7 +121,7 @@ describeDeprecated('NestedStack', () => { }); // the parent template includes the parameters and the nested stack resource which points to the s3 url - expect(parent).toMatchTemplate({ + Template.fromStack(parent).templateMatches({ Resources: { nestedstackNestedStacknestedstackNestedStackResource71CDD241: { Type: 'AWS::CloudFormation::Stack', @@ -276,7 +275,7 @@ describeDeprecated('NestedStack', () => { app.synth(); // nested template should use a parameter to reference the resource from the parent stack - expect(nested).toMatchTemplate({ + Template.fromStack(nested).templateMatches({ Resources: { resource: @@ -303,7 +302,7 @@ describeDeprecated('NestedStack', () => { }); // parent template should pass in the value through the parameter - expect(parentStack).toHaveResource('AWS::CloudFormation::Stack', { + Template.fromStack(parentStack).hasResourceProperties('AWS::CloudFormation::Stack', { Parameters: { referencetoparentparentresourceD56EA8F7Ref: { Ref: 'parentresource', @@ -347,7 +346,7 @@ describeDeprecated('NestedStack', () => { app.synth(); // nested template should use a parameter to reference the resource from the parent stack - expect(nested).toMatchTemplate({ + Template.fromStack(nested).templateMatches({ Resources: { resource: { Type: 'AWS::Child::Resource' }, }, @@ -357,7 +356,7 @@ describeDeprecated('NestedStack', () => { }); // parent template should pass in the value through the parameter - expect(parentStack).toHaveResource('AWS::Parent::Resource', { + Template.fromStack(parentStack).hasResourceProperties('AWS::Parent::Resource', { RefToResourceInNestedStack: { 'Fn::GetAtt': [ 'nestedNestedStacknestedNestedStackResource3DD143BF', @@ -387,7 +386,7 @@ describeDeprecated('NestedStack', () => { const assembly = app.synth(); // producing stack should have an export - expect(stack2).toMatchTemplate({ + Template.fromStack(stack2).templateMatches({ Resources: { ResourceInStack2: { Type: 'MyResource' }, }, @@ -400,7 +399,7 @@ describeDeprecated('NestedStack', () => { }); // nested stack uses Fn::ImportValue like normal - expect(nestedUnderStack1).toMatchTemplate({ + Template.fromStack(nestedUnderStack1).templateMatches({ Resources: { ResourceInNestedStack1: { Type: 'Nested::Resource', @@ -464,7 +463,7 @@ describeDeprecated('NestedStack', () => { const assembly = app.synth(); // nested stack should output this value as if it was referenced by the parent (without the export) - expect(nestedUnderStack1).toMatchTemplate({ + Template.fromStack(nestedUnderStack1).templateMatches({ Resources: { ResourceInNestedStack: { Type: 'MyResource', @@ -491,7 +490,7 @@ describeDeprecated('NestedStack', () => { }); // consuming stack should use ImportValue to import the value from the parent stack - expect(stack2).toMatchTemplate({ + Template.fromStack(stack2).templateMatches({ Resources: { ResourceInStack2: { Type: 'JustResource', @@ -532,7 +531,7 @@ describeDeprecated('NestedStack', () => { app.synth(); // producing nested stack - expect(nested1).toMatchTemplate({ + Template.fromStack(nested1).templateMatches({ Resources: { Resource1: { Type: 'Resource1', @@ -548,7 +547,7 @@ describeDeprecated('NestedStack', () => { }); // consuming nested stack - expect(nested2).toMatchTemplate({ + Template.fromStack(nested2).templateMatches({ Resources: { Resource2: { Type: 'Resource2', @@ -567,7 +566,7 @@ describeDeprecated('NestedStack', () => { }); // parent - expect(parent).toHaveResource('AWS::CloudFormation::Stack', { + Template.fromStack(parent).hasResourceProperties('AWS::CloudFormation::Stack', { Parameters: { referencetoParentNested1NestedStackNested1NestedStackResource9C05342COutputsParentNested1Resource15F3F0657Ref: { 'Fn::GetAtt': [ @@ -591,7 +590,7 @@ describeDeprecated('NestedStack', () => { }); // THEN - expect(nested).toHaveResource('Nested::Resource', { + Template.fromStack(nested).hasResourceProperties('Nested::Resource', { MyStackId: { Ref: 'AWS::StackId' }, }); }); @@ -608,7 +607,7 @@ describeDeprecated('NestedStack', () => { }); // THEN - expect(parent).toHaveResource('Parent::Resource', { + Template.fromStack(parent).hasResourceProperties('Parent::Resource', { NestedStackId: { Ref: 'NestedStackNestedStackNestedStackNestedStackResourceB70834FD' }, }); }); @@ -625,7 +624,7 @@ describeDeprecated('NestedStack', () => { }); // THEN - expect(nested).toHaveResource('Nested::Resource', { + Template.fromStack(nested).hasResourceProperties('Nested::Resource', { MyStackName: { Ref: 'AWS::StackName' }, }); }); @@ -642,7 +641,7 @@ describeDeprecated('NestedStack', () => { }); // THEN - expect(parent).toHaveResource('Parent::Resource', { + Template.fromStack(parent).hasResourceProperties('Parent::Resource', { NestedStackName: { 'Fn::Select': [ 1, @@ -689,7 +688,7 @@ describeDeprecated('NestedStack', () => { const assembly = app.synth(); // nested2 is a "leaf", so it's just the resource - expect(nested2).toMatchTemplate({ + Template.fromStack(nested2).templateMatches({ Resources: { Resource2: { Type: 'Resource::2' }, }, @@ -701,8 +700,9 @@ describeDeprecated('NestedStack', () => { const hashSuffix = 'E28F0693'; // nested1 wires the nested2 template through parameters, so we expect those - expect(nested1).toHaveResource('Resource::1'); - const nested2Template = SynthUtils.toCloudFormation(nested1); + const nested1Template = Template.fromStack(nested1); + nested1Template.resourceCountIs('Resource::1', 1); + const nested2Template = nested1Template.toJSON(); expect(nested2Template.Parameters).toEqual({ referencetostackAssetParameters8169c6f8aaeaf5e2e8620f5f895ffe2099202ccb4b6889df48fe0967a894235cS3BucketE8768F5CRef: { Type: 'String' }, referencetostackAssetParameters8169c6f8aaeaf5e2e8620f5f895ffe2099202ccb4b6889df48fe0967a894235cS3VersionKey49DD83A2Ref: { Type: 'String' }, @@ -710,7 +710,7 @@ describeDeprecated('NestedStack', () => { // parent stack should have two sets of parameters. one for the first nested stack and the second // for the second nested stack, passed in as parameters to the first - const template = SynthUtils.toCloudFormation(parent); + const template = Template.fromStack(parent).toJSON(); expect(template.Parameters).toEqual({ AssetParameters8169c6f8aaeaf5e2e8620f5f895ffe2099202ccb4b6889df48fe0967a894235cS3BucketDE3B88D6: { Type: 'String', Description: 'S3 bucket for asset "8169c6f8aaeaf5e2e8620f5f895ffe2099202ccb4b6889df48fe0967a894235c"' }, AssetParameters8169c6f8aaeaf5e2e8620f5f895ffe2099202ccb4b6889df48fe0967a894235cS3VersionKey3A62EFEA: { Type: 'String', Description: 'S3 key for asset version "8169c6f8aaeaf5e2e8620f5f895ffe2099202ccb4b6889df48fe0967a894235c"' }, @@ -721,7 +721,7 @@ describeDeprecated('NestedStack', () => { }); // proxy asset params to nested stack - expect(parent).toHaveResource('AWS::CloudFormation::Stack', { + Template.fromStack(parent).hasResourceProperties('AWS::CloudFormation::Stack', { Parameters: { referencetostackAssetParameters8169c6f8aaeaf5e2e8620f5f895ffe2099202ccb4b6889df48fe0967a894235cS3BucketE8768F5CRef: { Ref: 'AssetParameters8169c6f8aaeaf5e2e8620f5f895ffe2099202ccb4b6889df48fe0967a894235cS3BucketDE3B88D6' }, referencetostackAssetParameters8169c6f8aaeaf5e2e8620f5f895ffe2099202ccb4b6889df48fe0967a894235cS3VersionKey49DD83A2Ref: { Ref: 'AssetParameters8169c6f8aaeaf5e2e8620f5f895ffe2099202ccb4b6889df48fe0967a894235cS3VersionKey3A62EFEA' }, @@ -780,7 +780,7 @@ describeDeprecated('NestedStack', () => { // THEN const assembly = app.synth(); - const template = SynthUtils.toCloudFormation(parent); + const template = Template.fromStack(parent).toJSON(); // two sets of asset parameters: one for the nested stack itself and one as a proxy for the asset within the stack expect(template.Parameters).toEqual({ @@ -793,7 +793,7 @@ describeDeprecated('NestedStack', () => { }); // asset proxy parameters are passed to the nested stack - expect(parent).toHaveResource('AWS::CloudFormation::Stack', { + Template.fromStack(parent).hasResourceProperties('AWS::CloudFormation::Stack', { Parameters: { referencetoParentStackAssetParametersdb01ee2eb7adc7915e364dc410d861e569543f9be3761d535a68d5c2cc181281S3Bucket82C55B96Ref: { Ref: 'AssetParametersdb01ee2eb7adc7915e364dc410d861e569543f9be3761d535a68d5c2cc181281S3BucketC188F637' }, referencetoParentStackAssetParametersdb01ee2eb7adc7915e364dc410d861e569543f9be3761d535a68d5c2cc181281S3VersionKeyA43C3CC6Ref: { Ref: 'AssetParametersdb01ee2eb7adc7915e364dc410d861e569543f9be3761d535a68d5c2cc181281S3VersionKeyC7F4DBF2' }, @@ -887,7 +887,7 @@ describeDeprecated('NestedStack', () => { }); // THEN - expect(nested).toMatchTemplate({ + Template.fromStack(nested).templateMatches({ Resources: { resourceinnested: { Type: 'CONSUMED', @@ -905,7 +905,7 @@ describeDeprecated('NestedStack', () => { }, }); - expect(parent).toHaveResource('CONSUMER', { + Template.fromStack(parent).hasResourceProperties('CONSUMER', { ConsumedAttribute: { 'Fn::GetAtt': [ 'nestedNestedStacknestedNestedStackResource3DD143BF', @@ -984,7 +984,7 @@ describeDeprecated('NestedStack', () => { }); // THEN - expect(top).toHaveResource('AWS::CloudFormation::Stack', { + Template.fromStack(top).hasResourceProperties('AWS::CloudFormation::Stack', { Parameters: { referencetostackAssetParameters842982bd421cce9742ba27151ef12ed699d44d22801f41e8029f63f2358a3f2fS3Bucket5DA5D2E7Ref: { Ref: 'AssetParameters842982bd421cce9742ba27151ef12ed699d44d22801f41e8029f63f2358a3f2fS3BucketDD4D96B5', @@ -998,7 +998,7 @@ describeDeprecated('NestedStack', () => { }, }); - expect(nested1).toHaveResource('AWS::CloudFormation::Stack', { + Template.fromStack(nested1).hasResourceProperties('AWS::CloudFormation::Stack', { Parameters: { referencetostacktoplevelBB16BF13Ref: { Ref: 'referencetostacktoplevelBB16BF13Ref', @@ -1006,7 +1006,7 @@ describeDeprecated('NestedStack', () => { }, }); - expect(nested2).toMatchTemplate({ + Template.fromStack(nested2).templateMatches({ Resources: { refToTopLevel: { Type: 'BottomLevel', @@ -1048,7 +1048,7 @@ describeDeprecated('NestedStack', () => { const paramName = 'referencetoGrandparentResourceInGrandparent010E997ARef'; // child (bottom) references through a parameter. - expect(bottom).toMatchTemplate({ + Template.fromStack(bottom).templateMatches({ Resources: { ResourceInChild: { Type: 'ResourceInChild', @@ -1063,14 +1063,14 @@ describeDeprecated('NestedStack', () => { }); // the parent (middle) sets the value of this parameter to be a reference to another parameter - expect(middle).toHaveResource('AWS::CloudFormation::Stack', { + Template.fromStack(middle).hasResourceProperties('AWS::CloudFormation::Stack', { Parameters: { [paramName]: { Ref: paramName }, }, }); // grandparent (top) assigns the actual value to the parameter - expect(top).toHaveResource('AWS::CloudFormation::Stack', { + Template.fromStack(top).hasResourceProperties('AWS::CloudFormation::Stack', { Parameters: { [paramName]: { Ref: 'ResourceInGrandparent' }, diff --git a/packages/@aws-cdk/aws-cloudformation/test/resource.test.ts b/packages/@aws-cdk/aws-cloudformation/test/resource.test.ts index c6cefdc714675..e2f6eae16f3b3 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/resource.test.ts +++ b/packages/@aws-cdk/aws-cloudformation/test/resource.test.ts @@ -1,5 +1,4 @@ -import { ResourcePart } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as lambda from '@aws-cdk/aws-lambda'; import * as sns from '@aws-cdk/aws-sns'; import { describeDeprecated, testDeprecated } from '@aws-cdk/cdk-build-tools'; @@ -23,7 +22,7 @@ describeDeprecated('custom resources honor removalPolicy', () => { new TestCustomResource(stack, 'Custom'); // THEN - expect(stack).toHaveResource('AWS::CloudFormation::CustomResource', {}, ResourcePart.CompleteDefinition); + Template.fromStack(stack).hasResource('AWS::CloudFormation::CustomResource', {}); expect(app.synth().tryGetArtifact(stack.stackName)!.findMetadataByType('aws:cdk:protected').length).toEqual(0); }); @@ -36,7 +35,7 @@ describeDeprecated('custom resources honor removalPolicy', () => { new TestCustomResource(stack, 'Custom', { removalPolicy: cdk.RemovalPolicy.DESTROY }); // THEN - expect(stack).toHaveResource('AWS::CloudFormation::CustomResource', {}, ResourcePart.CompleteDefinition); + Template.fromStack(stack).hasResource('AWS::CloudFormation::CustomResource', {}); expect(app.synth().tryGetArtifact(stack.stackName)!.findMetadataByType('aws:cdk:protected').length).toEqual(0); }); @@ -49,10 +48,10 @@ describeDeprecated('custom resources honor removalPolicy', () => { new TestCustomResource(stack, 'Custom', { removalPolicy: cdk.RemovalPolicy.RETAIN }); // THEN - expect(stack).toHaveResource('AWS::CloudFormation::CustomResource', { + Template.fromStack(stack).hasResource('AWS::CloudFormation::CustomResource', { DeletionPolicy: 'Retain', UpdateReplacePolicy: 'Retain', - }, ResourcePart.CompleteDefinition); + }); }); }); @@ -66,7 +65,7 @@ testDeprecated('custom resource is added twice, lambda is added once', () => { new TestCustomResource(stack, 'Custom2'); // THEN - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ 'Resources': { 'SingletonLambdaTestCustomResourceProviderServiceRole81FEAB5C': { 'Type': 'AWS::IAM::Role', @@ -149,7 +148,7 @@ testDeprecated('custom resources can specify a resource type that starts with Cu resourceType: 'Custom::MyCustomResourceType', provider: CustomResourceProvider.fromTopic(new sns.Topic(stack, 'Provider')), }); - expect(stack).toHaveResource('Custom::MyCustomResourceType'); + Template.fromStack(stack).hasResourceProperties('Custom::MyCustomResourceType', {}); }); describeDeprecated('fails if custom resource type is invalid', () => { From 71ee5f60316e3afdbfce827a601462dadf20284d Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Wed, 19 Jan 2022 02:52:36 +0100 Subject: [PATCH 14/50] chore(logs-destinations): migrate tests to `assertions` (#18500) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-logs-destinations/package.json | 2 +- .../aws-logs-destinations/test/kinesis.test.ts | 14 +++++++------- .../aws-logs-destinations/test/lambda.test.ts | 16 ++++++++-------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/@aws-cdk/aws-logs-destinations/package.json b/packages/@aws-cdk/aws-logs-destinations/package.json index e8c1455b1920e..ceb9c5c120499 100644 --- a/packages/@aws-cdk/aws-logs-destinations/package.json +++ b/packages/@aws-cdk/aws-logs-destinations/package.json @@ -64,7 +64,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-logs-destinations/test/kinesis.test.ts b/packages/@aws-cdk/aws-logs-destinations/test/kinesis.test.ts index 42c87261d60b2..4325993406ccd 100644 --- a/packages/@aws-cdk/aws-logs-destinations/test/kinesis.test.ts +++ b/packages/@aws-cdk/aws-logs-destinations/test/kinesis.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as kinesis from '@aws-cdk/aws-kinesis'; import * as logs from '@aws-cdk/aws-logs'; import * as cdk from '@aws-cdk/core'; @@ -18,13 +18,13 @@ test('stream can be subscription destination', () => { }); // THEN: subscription target is Stream - expect(stack).toHaveResource('AWS::Logs::SubscriptionFilter', { + Template.fromStack(stack).hasResourceProperties('AWS::Logs::SubscriptionFilter', { DestinationArn: { 'Fn::GetAtt': ['MyStream5C050E93', 'Arn'] }, RoleArn: { 'Fn::GetAtt': ['SubscriptionCloudWatchLogsCanPutRecords9C1223EC', 'Arn'] }, }); // THEN: we have a role to write to the Stream - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Version: '2012-10-17', Statement: [{ @@ -44,7 +44,7 @@ test('stream can be subscription destination', () => { }, }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Version: '2012-10-17', Statement: [ @@ -88,13 +88,13 @@ test('stream can be subscription destination twice, without duplicating permissi }); // THEN: subscription target is Stream - expect(stack).toHaveResource('AWS::Logs::SubscriptionFilter', { + Template.fromStack(stack).hasResourceProperties('AWS::Logs::SubscriptionFilter', { DestinationArn: { 'Fn::GetAtt': ['MyStream5C050E93', 'Arn'] }, RoleArn: { 'Fn::GetAtt': ['SubscriptionCloudWatchLogsCanPutRecords9C1223EC', 'Arn'] }, }); // THEN: we have a role to write to the Stream - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Version: '2012-10-17', Statement: [{ @@ -114,7 +114,7 @@ test('stream can be subscription destination twice, without duplicating permissi }, }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Version: '2012-10-17', Statement: [ diff --git a/packages/@aws-cdk/aws-logs-destinations/test/lambda.test.ts b/packages/@aws-cdk/aws-logs-destinations/test/lambda.test.ts index b1f0048716a74..f5a80285c8146 100644 --- a/packages/@aws-cdk/aws-logs-destinations/test/lambda.test.ts +++ b/packages/@aws-cdk/aws-logs-destinations/test/lambda.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as lambda from '@aws-cdk/aws-lambda'; import * as logs from '@aws-cdk/aws-logs'; import * as cdk from '@aws-cdk/core'; @@ -28,12 +28,12 @@ test('lambda can be used as metric subscription destination', () => { }); // THEN: subscription target is Lambda - expect(stack).toHaveResource('AWS::Logs::SubscriptionFilter', { + Template.fromStack(stack).hasResourceProperties('AWS::Logs::SubscriptionFilter', { DestinationArn: { 'Fn::GetAtt': ['MyLambdaCCE802FB', 'Arn'] }, }); // THEN: Lambda has permissions to be invoked by CWL - expect(stack).toHaveResource('AWS::Lambda::Permission', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Permission', { Action: 'lambda:InvokeFunction', FunctionName: { 'Fn::GetAtt': ['MyLambdaCCE802FB', 'Arn'] }, Principal: 'logs.amazonaws.com', @@ -55,14 +55,14 @@ test('can have multiple subscriptions use the same Lambda', () => { }); // THEN: Lambda has permissions to be invoked by CWL from both Source Arns - expect(stack).toHaveResource('AWS::Lambda::Permission', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Permission', { Action: 'lambda:InvokeFunction', FunctionName: { 'Fn::GetAtt': ['MyLambdaCCE802FB', 'Arn'] }, SourceArn: { 'Fn::GetAtt': ['LogGroupF5B46931', 'Arn'] }, Principal: 'logs.amazonaws.com', }); - expect(stack).toHaveResource('AWS::Lambda::Permission', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Permission', { Action: 'lambda:InvokeFunction', FunctionName: { 'Fn::GetAtt': ['MyLambdaCCE802FB', 'Arn'] }, SourceArn: { 'Fn::GetAtt': ['LG224A94C8F', 'Arn'] }, @@ -79,14 +79,14 @@ test('lambda permissions are not added when addPermissions is false', () => { }); // THEN: subscription target is Lambda - expect(stack).toHaveResource('AWS::Logs::SubscriptionFilter', { + Template.fromStack(stack).hasResourceProperties('AWS::Logs::SubscriptionFilter', { DestinationArn: { 'Fn::GetAtt': ['MyLambdaCCE802FB', 'Arn'] }, }); // THEN: Lambda does not have permissions to be invoked by CWL - expect(stack).not.toHaveResource('AWS::Lambda::Permission', { + expect(Template.fromStack(stack).findResources('AWS::Lambda::Permission', { Action: 'lambda:InvokeFunction', FunctionName: { 'Fn::GetAtt': ['MyLambdaCCE802FB', 'Arn'] }, Principal: 'logs.amazonaws.com', - }); + })).toEqual({}); }); \ No newline at end of file From 802a031245619715fb405d04a02056cf699e0fc2 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Wed, 19 Jan 2022 03:41:14 +0100 Subject: [PATCH 15/50] chore(lambda-layer-awscli): migrate tests to `assertions` (#18499) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/lambda-layer-awscli/package.json | 2 +- .../@aws-cdk/lambda-layer-awscli/test/awscli-layer.test.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/lambda-layer-awscli/package.json b/packages/@aws-cdk/lambda-layer-awscli/package.json index 759a1c35ec1d9..cf5a3d857f5b3 100644 --- a/packages/@aws-cdk/lambda-layer-awscli/package.json +++ b/packages/@aws-cdk/lambda-layer-awscli/package.json @@ -73,7 +73,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/pkglint": "0.0.0", diff --git a/packages/@aws-cdk/lambda-layer-awscli/test/awscli-layer.test.ts b/packages/@aws-cdk/lambda-layer-awscli/test/awscli-layer.test.ts index 2a5c4b7d80043..561bb87a14ac7 100644 --- a/packages/@aws-cdk/lambda-layer-awscli/test/awscli-layer.test.ts +++ b/packages/@aws-cdk/lambda-layer-awscli/test/awscli-layer.test.ts @@ -1,6 +1,6 @@ import { Stack } from '@aws-cdk/core'; import { AwsCliLayer } from '../lib'; -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; test('synthesized to a layer version', () => { //GIVEN @@ -10,7 +10,7 @@ test('synthesized to a layer version', () => { new AwsCliLayer(stack, 'MyLayer'); // THEN - expect(stack).toHaveResource('AWS::Lambda::LayerVersion', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::LayerVersion', { Description: '/opt/awscli/aws', }); }); From c6609cf89c9fcd0443e9032a04a73ae568419ab8 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Wed, 19 Jan 2022 04:30:18 +0100 Subject: [PATCH 16/50] chore(custom-resources): migrate tests to `assertions` (#18501) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/custom-resources/package.json | 2 +- .../aws-custom-resource.test.ts | 40 ++++++++--------- .../test/provider-framework/provider.test.ts | 43 +++++++++---------- .../waiter-state-machine.test.ts | 8 ++-- 4 files changed, 46 insertions(+), 47 deletions(-) diff --git a/packages/@aws-cdk/custom-resources/package.json b/packages/@aws-cdk/custom-resources/package.json index a16653f20c6bf..cb6707b8b1611 100644 --- a/packages/@aws-cdk/custom-resources/package.json +++ b/packages/@aws-cdk/custom-resources/package.json @@ -79,7 +79,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/aws-events": "0.0.0", "@aws-cdk/aws-s3": "0.0.0", "@aws-cdk/aws-ssm": "0.0.0", diff --git a/packages/@aws-cdk/custom-resources/test/aws-custom-resource/aws-custom-resource.test.ts b/packages/@aws-cdk/custom-resources/test/aws-custom-resource/aws-custom-resource.test.ts index f072c0f926875..c3df398f6f5dd 100644 --- a/packages/@aws-cdk/custom-resources/test/aws-custom-resource/aws-custom-resource.test.ts +++ b/packages/@aws-cdk/custom-resources/test/aws-custom-resource/aws-custom-resource.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import * as logs from '@aws-cdk/aws-logs'; import * as cdk from '@aws-cdk/core'; @@ -33,7 +33,7 @@ test('aws sdk js custom resource with onCreate and onDelete', () => { }); // THEN - expect(stack).toHaveResource('Custom::LogRetentionPolicy', { + Template.fromStack(stack).hasResourceProperties('Custom::LogRetentionPolicy', { 'Create': JSON.stringify({ 'service': 'CloudWatchLogs', 'action': 'putRetentionPolicy', @@ -55,7 +55,7 @@ test('aws sdk js custom resource with onCreate and onDelete', () => { 'InstallLatestAwsSdk': true, }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { 'PolicyDocument': { 'Statement': [ { @@ -95,7 +95,7 @@ test('onCreate defaults to onUpdate', () => { }); // THEN - expect(stack).toHaveResource('Custom::S3PutObject', { + Template.fromStack(stack).hasResourceProperties('Custom::S3PutObject', { 'Create': JSON.stringify({ 'service': 's3', 'action': 'putObject', @@ -148,7 +148,7 @@ test('with custom policyStatements', () => { }); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { 'PolicyDocument': { 'Statement': [ { @@ -207,7 +207,7 @@ test('booleans are encoded in the stringified parameters object', () => { }); // THEN - expect(stack).toHaveResource('Custom::ServiceAction', { + Template.fromStack(stack).hasResourceProperties('Custom::ServiceAction', { 'Create': JSON.stringify({ 'service': 'service', 'action': 'action', @@ -263,7 +263,7 @@ test('encodes physical resource id reference', () => { }); // THEN - expect(stack).toHaveResource('Custom::ServiceAction', { + Template.fromStack(stack).hasResourceProperties('Custom::ServiceAction', { 'Create': JSON.stringify({ 'service': 'service', 'action': 'action', @@ -296,7 +296,7 @@ test('timeout defaults to 2 minutes', () => { }); // THEN - expect(stack).toHaveResource('AWS::Lambda::Function', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { Timeout: 120, }); }); @@ -317,7 +317,7 @@ test('can specify timeout', () => { }); // THEN - expect(stack).toHaveResource('AWS::Lambda::Function', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { Timeout: 900, }); }); @@ -340,7 +340,7 @@ test('implements IGrantable', () => { // WHEN role.grantPassRole(customResource.grantPrincipal); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -376,11 +376,11 @@ test('can use existing role', () => { }); // THEN - expect(stack).toHaveResource('AWS::Lambda::Function', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { Role: 'arn:aws:iam::123456789012:role/CoolRole', }); - expect(stack).not.toHaveResource('AWS::IAM::Role'); + Template.fromStack(stack).resourceCountIs('AWS::IAM::Role', 0); }); test('getData', () => { @@ -524,7 +524,7 @@ test('getDataString', () => { }); // THEN - expect(stack).toHaveResource('Custom::AWS', { + Template.fromStack(stack).hasResourceProperties('Custom::AWS', { Create: { 'Fn::Join': [ '', @@ -559,7 +559,7 @@ test('can specify log retention', () => { }); // THEN - expect(stack).toHaveResource('Custom::LogRetention', { + Template.fromStack(stack).hasResourceProperties('Custom::LogRetention', { LogGroupName: { 'Fn::Join': [ '', @@ -591,7 +591,7 @@ test('disable AWS SDK installation', () => { }); // THEN - expect(stack).toHaveResource('Custom::AWS', { + Template.fromStack(stack).hasResourceProperties('Custom::AWS', { 'InstallLatestAwsSdk': false, }); }); @@ -612,7 +612,7 @@ test('can specify function name', () => { }); // THEN - expect(stack).toHaveResource('AWS::Lambda::Function', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { FunctionName: 'my-cool-function', }); }); @@ -640,7 +640,7 @@ test('separate policies per custom resource', () => { }); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -652,7 +652,7 @@ test('separate policies per custom resource', () => { Version: '2012-10-17', }, }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -690,7 +690,7 @@ test('tokens can be used as dictionary keys', () => { }); // THEN - expect(stack).toHaveResource('Custom::AWS', { + Template.fromStack(stack).hasResourceProperties('Custom::AWS', { Create: { 'Fn::Join': [ '', @@ -730,7 +730,7 @@ test('assumedRoleArn adds statement for sts:assumeRole', () => { // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { diff --git a/packages/@aws-cdk/custom-resources/test/provider-framework/provider.test.ts b/packages/@aws-cdk/custom-resources/test/provider-framework/provider.test.ts index 724c76425a1ee..ac09be73e0f89 100644 --- a/packages/@aws-cdk/custom-resources/test/provider-framework/provider.test.ts +++ b/packages/@aws-cdk/custom-resources/test/provider-framework/provider.test.ts @@ -1,4 +1,5 @@ import * as path from 'path'; +import { Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; @@ -7,8 +8,6 @@ import { Duration, Stack } from '@aws-cdk/core'; import * as cr from '../../lib'; import * as util from '../../lib/provider-framework/util'; -import '@aws-cdk/assert-internal/jest'; - test('security groups are applied to all framework functions', () => { // GIVEN @@ -34,7 +33,7 @@ test('security groups are applied to all framework functions', () => { securityGroups: [securityGroup], }); - expect(stack).toHaveResourceLike('AWS::Lambda::Function', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { Handler: 'framework.onEvent', VpcConfig: { SecurityGroupIds: [ @@ -48,7 +47,7 @@ test('security groups are applied to all framework functions', () => { }, }); - expect(stack).toHaveResourceLike('AWS::Lambda::Function', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { Handler: 'framework.isComplete', VpcConfig: { SecurityGroupIds: [ @@ -62,7 +61,7 @@ test('security groups are applied to all framework functions', () => { }, }); - expect(stack).toHaveResourceLike('AWS::Lambda::Function', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { Handler: 'framework.onTimeout', VpcConfig: { SecurityGroupIds: [ @@ -101,7 +100,7 @@ test('vpc is applied to all framework functions', () => { vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE }, }); - expect(stack).toHaveResourceLike('AWS::Lambda::Function', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { Handler: 'framework.onEvent', VpcConfig: { SubnetIds: [ @@ -111,7 +110,7 @@ test('vpc is applied to all framework functions', () => { }, }); - expect(stack).toHaveResourceLike('AWS::Lambda::Function', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { Handler: 'framework.isComplete', VpcConfig: { SubnetIds: [ @@ -121,7 +120,7 @@ test('vpc is applied to all framework functions', () => { }, }); - expect(stack).toHaveResourceLike('AWS::Lambda::Function', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { Handler: 'framework.onTimeout', VpcConfig: { SubnetIds: [ @@ -149,23 +148,23 @@ test('minimal setup', () => { // THEN // framework "onEvent" handler - expect(stack).toHaveResource('AWS::Lambda::Function', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { Handler: 'framework.onEvent', Environment: { Variables: { USER_ON_EVENT_FUNCTION_ARN: { 'Fn::GetAtt': ['MyHandler6B74D312', 'Arn'] } } }, Timeout: 900, }); // user "onEvent" handler - expect(stack).toHaveResource('AWS::Lambda::Function', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { Handler: 'index.onEvent', }); // no framework "is complete" handler or state machine - expect(stack).not.toHaveResource('AWS::StepFunctions::StateMachine'); - expect(stack).not.toHaveResource('AWS::Lambda::Function', { + Template.fromStack(stack).resourceCountIs('AWS::StepFunctions::StateMachine', 0); + expect(Template.fromStack(stack).findResources('AWS::Lambda::Function', { Handler: 'framework.isComplete', Timeout: 900, - }); + })).toEqual({}); }); test('if isComplete is specified, the isComplete framework handler is also included', () => { @@ -193,7 +192,7 @@ test('if isComplete is specified, the isComplete framework handler is also inclu }, }; - expect(stack).toHaveResource('AWS::Lambda::Function', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { Handler: 'framework.onEvent', Timeout: 900, Environment: { @@ -204,19 +203,19 @@ test('if isComplete is specified, the isComplete framework handler is also inclu }, }); - expect(stack).toHaveResource('AWS::Lambda::Function', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { Handler: 'framework.isComplete', Timeout: 900, Environment: expectedEnv, }); - expect(stack).toHaveResource('AWS::Lambda::Function', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { Handler: 'framework.onTimeout', Timeout: 900, Environment: expectedEnv, }); - expect(stack).toHaveResource('AWS::StepFunctions::StateMachine', { + Template.fromStack(stack).hasResourceProperties('AWS::StepFunctions::StateMachine', { DefinitionString: { 'Fn::Join': [ '', @@ -310,7 +309,7 @@ describe('log retention', () => { }); // THEN - expect(stack).toHaveResource('Custom::LogRetention', { + Template.fromStack(stack).hasResourceProperties('Custom::LogRetention', { LogGroupName: { 'Fn::Join': [ '', @@ -340,7 +339,7 @@ describe('log retention', () => { }); // THEN - expect(stack).not.toHaveResource('Custom::LogRetention'); + Template.fromStack(stack).resourceCountIs('Custom::LogRetention', 0); }); }); @@ -363,7 +362,7 @@ describe('role', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::Lambda::Function', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { Role: { 'Fn::GetAtt': [ 'MyRoleF48FFE04', @@ -387,7 +386,7 @@ describe('role', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::Lambda::Function', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { Role: { 'Fn::GetAtt': [ 'MyProviderframeworkonEventServiceRole8761E48D', @@ -415,7 +414,7 @@ describe('name', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::Lambda::Function', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { FunctionName: providerFunctionName, }); }); diff --git a/packages/@aws-cdk/custom-resources/test/provider-framework/waiter-state-machine.test.ts b/packages/@aws-cdk/custom-resources/test/provider-framework/waiter-state-machine.test.ts index f9b683417dc94..7548f4e151041 100644 --- a/packages/@aws-cdk/custom-resources/test/provider-framework/waiter-state-machine.test.ts +++ b/packages/@aws-cdk/custom-resources/test/provider-framework/waiter-state-machine.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { Code, Function as lambdaFn, Runtime } from '@aws-cdk/aws-lambda'; import { Duration, Stack } from '@aws-cdk/core'; import { Node } from 'constructs'; @@ -35,7 +35,7 @@ describe('state machine', () => { // THEN const roleId = 'statemachineRole52044F93'; - expect(stack).toHaveResourceLike('AWS::StepFunctions::StateMachine', { + Template.fromStack(stack).hasResourceProperties('AWS::StepFunctions::StateMachine', { DefinitionString: { 'Fn::Join': [ '', @@ -54,7 +54,7 @@ describe('state machine', () => { 'Fn::GetAtt': [roleId, 'Arn'], }, }); - expect(stack).toHaveResourceLike('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Statement: [ { @@ -77,7 +77,7 @@ describe('state machine', () => { Version: '2012-10-17', }, }); - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { From 0e757a59d5449cf30bae98369bd47a9fec27e391 Mon Sep 17 00:00:00 2001 From: Cory Hall <43035978+corymhall@users.noreply.github.com> Date: Tue, 18 Jan 2022 23:18:10 -0500 Subject: [PATCH 17/50] chore(apprunner): migrate tests to `assertions` (#18502) Looks like tests were already migrated, just needed to remove the unused assert-internal import ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-apprunner/test/service.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/@aws-cdk/aws-apprunner/test/service.test.ts b/packages/@aws-cdk/aws-apprunner/test/service.test.ts index 412ff09cd14e9..2d5285d06f7fa 100644 --- a/packages/@aws-cdk/aws-apprunner/test/service.test.ts +++ b/packages/@aws-cdk/aws-apprunner/test/service.test.ts @@ -1,5 +1,4 @@ import * as path from 'path'; -import '@aws-cdk/assert-internal/jest'; import { Template } from '@aws-cdk/assertions'; import * as ecr from '@aws-cdk/aws-ecr'; import * as ecr_assets from '@aws-cdk/aws-ecr-assets'; From 4e4f7d2c3bd236e033df247071b57e6f5ad3fec3 Mon Sep 17 00:00:00 2001 From: Cory Hall <43035978+corymhall@users.noreply.github.com> Date: Wed, 19 Jan 2022 00:08:33 -0500 Subject: [PATCH 18/50] chore(signer): migrate tests to `assertions` (#18504) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-signer/package.json | 2 +- .../@aws-cdk/aws-signer/test/signing-profile.test.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/@aws-cdk/aws-signer/package.json b/packages/@aws-cdk/aws-signer/package.json index 93cb08670772c..064c381b6a88a 100644 --- a/packages/@aws-cdk/aws-signer/package.json +++ b/packages/@aws-cdk/aws-signer/package.json @@ -74,7 +74,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", diff --git a/packages/@aws-cdk/aws-signer/test/signing-profile.test.ts b/packages/@aws-cdk/aws-signer/test/signing-profile.test.ts index 49ada5da2f596..44db183b565a5 100644 --- a/packages/@aws-cdk/aws-signer/test/signing-profile.test.ts +++ b/packages/@aws-cdk/aws-signer/test/signing-profile.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as cdk from '@aws-cdk/core'; import * as signer from '../lib'; @@ -14,7 +14,7 @@ describe('signing profile', () => { const platform = signer.Platform.AWS_LAMBDA_SHA384_ECDSA; new signer.SigningProfile( stack, 'SigningProfile', { platform } ); - expect(stack).toHaveResource('AWS::Signer::SigningProfile', { + Template.fromStack(stack).hasResourceProperties('AWS::Signer::SigningProfile', { PlatformId: platform.platformId, SignatureValidityPeriod: { Type: 'MONTHS', @@ -30,7 +30,7 @@ describe('signing profile', () => { signatureValidity: cdk.Duration.days( 7 ), } ); - expect(stack).toHaveResource('AWS::Signer::SigningProfile', { + Template.fromStack(stack).hasResourceProperties('AWS::Signer::SigningProfile', { PlatformId: platform.platformId, SignatureValidityPeriod: { Type: 'DAYS', @@ -47,7 +47,7 @@ describe('signing profile', () => { cdk.Tags.of(signing).add('tag2', 'value2'); cdk.Tags.of(signing).add('tag3', ''); - expect(stack).toHaveResource('AWS::Signer::SigningProfile', { + Template.fromStack(stack).hasResourceProperties('AWS::Signer::SigningProfile', { PlatformId: platform.platformId, SignatureValidityPeriod: { Type: 'MONTHS', @@ -109,7 +109,7 @@ describe('signing profile', () => { ], ], }); - expect(stack).toMatchTemplate({}); + Template.fromStack(stack).templateMatches({}); }); } ); }); From 9b266f49643d058709771892f908f1c2ae248f95 Mon Sep 17 00:00:00 2001 From: Cory Hall <43035978+corymhall@users.noreply.github.com> Date: Wed, 19 Jan 2022 00:57:17 -0500 Subject: [PATCH 19/50] fix(cli): `cdk watch` constantly prints 'messages suppressed' (#18486) Currently if `filterLogEvents` returns a nextToken then we print `(...messages suppressed...)`. According to the docs "This operation can return empty results while there are more log events available through the token.". This fix adds a condition to only print out the suppression message if `filterLogEvents` returns non-empty results. Also updates the message to be more descriptive. fixes #18451 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/aws-cdk/lib/api/logs/logs-monitor.ts | 7 ++++--- packages/aws-cdk/test/api/logs/logs-monitor.test.ts | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/aws-cdk/lib/api/logs/logs-monitor.ts b/packages/aws-cdk/lib/api/logs/logs-monitor.ts index 51cd54137091c..f6f2086bc20bf 100644 --- a/packages/aws-cdk/lib/api/logs/logs-monitor.ts +++ b/packages/aws-cdk/lib/api/logs/logs-monitor.ts @@ -182,8 +182,9 @@ export class CloudWatchLogEventMonitor { limit: 100, startTime: startTime, }).promise(); + const filteredEvents = response.events ?? []; - for (const event of response.events ?? []) { + for (const event of filteredEvents) { if (event.message) { events.push({ message: event.message, @@ -200,9 +201,9 @@ export class CloudWatchLogEventMonitor { // if we have > 100 events let the user know some // messages have been supressed. We are essentially // showing them a sampling (10000 events printed out is not very useful) - if (response.nextToken) { + if (filteredEvents.length > 0 && response.nextToken) { events.push({ - message: '(...messages supressed...)', + message: '>>> `watch` shows only the first 100 log messages - the rest have been truncated...', logGroupName, timestamp: new Date(endTime), }); diff --git a/packages/aws-cdk/test/api/logs/logs-monitor.test.ts b/packages/aws-cdk/test/api/logs/logs-monitor.test.ts index 84a96da3525ad..becb3d97dfbf3 100644 --- a/packages/aws-cdk/test/api/logs/logs-monitor.test.ts +++ b/packages/aws-cdk/test/api/logs/logs-monitor.test.ts @@ -55,7 +55,7 @@ test('continue to the next page if it exists', async () => { `[${blue('loggroup')}] ${yellow(HUMAN_TIME)} message`, ); expect(stderrMock.mock.calls[1][0]).toContain( - `[${blue('loggroup')}] ${yellow(new Date(T100).toLocaleTimeString())} (...messages supressed...)`, + `[${blue('loggroup')}] ${yellow(new Date(T100).toLocaleTimeString())} >>> \`watch\` shows only the first 100 log messages - the rest have been truncated...`, ); }); From ab4a7adcfb4dfa0211c4008b23223493ee2cda35 Mon Sep 17 00:00:00 2001 From: alina144 <87857519+alina144@users.noreply.github.com> Date: Wed, 19 Jan 2022 07:43:36 +0100 Subject: [PATCH 20/50] chore(rds): deprecate SqlServerEngineVersion VER_14_00_3192_2_V1 (#18476) SqlServerEngineVersion VER_14_00_3192_2_V1 is not supported anymore. This pull request deprecates the field. Closes #17394 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-rds/lib/instance-engine.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-rds/lib/instance-engine.ts b/packages/@aws-cdk/aws-rds/lib/instance-engine.ts index 5b2388eb35b45..310d071ec226b 100644 --- a/packages/@aws-cdk/aws-rds/lib/instance-engine.ts +++ b/packages/@aws-cdk/aws-rds/lib/instance-engine.ts @@ -1392,7 +1392,10 @@ export class SqlServerEngineVersion { public static readonly VER_14_00_3035_2_V1 = SqlServerEngineVersion.of('14.00.3035.2.v1', '14.00'); /** Version "14.00.3049.1.v1". */ public static readonly VER_14_00_3049_1_V1 = SqlServerEngineVersion.of('14.00.3049.1.v1', '14.00'); - /** Version "14.00.3192.2.v1". */ + /** + * Version "14.00.3192.2.v1". + * @deprecated SQL Server version 14.00.3192.2.v1 reached end of life + */ public static readonly VER_14_00_3192_2_V1 = SqlServerEngineVersion.of('14.00.3192.2.v1', '14.00'); /** Version "14.00.3223.3.v1". */ public static readonly VER_14_00_3223_3_V1 = SqlServerEngineVersion.of('14.00.3223.3.v1', '14.00'); From 2b0b5ea5db7ce8103a641c1267b1c213453ac145 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy <36202692+kaizen3031593@users.noreply.github.com> Date: Wed, 19 Jan 2022 02:31:47 -0500 Subject: [PATCH 21/50] fix(pipelines): graphnode dependencies can have duplicates (#18450) `GraphNode.allDeps` allows duplicate dependencies to be returned. This does not have any affect on the performance of the pipelines module, but looks ugly. This was noticed in https://github.com/cdklabs/cdk-pipelines-github/pull/67, where the dependencies are written out to the `deploy.yaml` file. I did not change the underlying `GraphNode.dependencies` structure to be a set (although I think it should) because I feel like that is a breaking change. So instead I've preserved the structure of the API and deduplicated the list of GraphNodes. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../pipelines/lib/helpers-internal/graph.ts | 4 ++-- .../helpers-internal/dependencies.test.ts | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/pipelines/lib/helpers-internal/graph.ts b/packages/@aws-cdk/pipelines/lib/helpers-internal/graph.ts index 6b1c2d85ee701..798cc207f0aeb 100644 --- a/packages/@aws-cdk/pipelines/lib/helpers-internal/graph.ts +++ b/packages/@aws-cdk/pipelines/lib/helpers-internal/graph.ts @@ -35,7 +35,7 @@ export class GraphNode { */ public get allDeps(): GraphNode[] { const fromParent = this.parentGraph?.allDeps ?? []; - return [...this.dependencies, ...fromParent]; + return Array.from(new Set([...this.dependencies, ...fromParent])); } public dependOn(...dependencies: Array | undefined>) { @@ -382,4 +382,4 @@ function projectDependencies(dependencies: Map, Set export function isGraph(x: GraphNode): x is Graph { return x instanceof Graph; -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/pipelines/test/blueprint/helpers-internal/dependencies.test.ts b/packages/@aws-cdk/pipelines/test/blueprint/helpers-internal/dependencies.test.ts index f577ffae4f80c..9610472554301 100644 --- a/packages/@aws-cdk/pipelines/test/blueprint/helpers-internal/dependencies.test.ts +++ b/packages/@aws-cdk/pipelines/test/blueprint/helpers-internal/dependencies.test.ts @@ -50,3 +50,22 @@ describe('with nested graphs', () => { ]); }); }); + +test('duplicate dependencies are ignored', () => { + mkGraph('G', G => { + const A = G.graph('A', [], GA => { + GA.node('aa'); + }); + + // parent graph depnds on A also + const B = G.graph('B', [A], GB => { + // duplicate dependency on A + GB.graph('BB', [A], GBB => { + GBB.node('bbb'); + }); + GB.node('bb'); + }); + + expect(nodeNames(B.tryGetChild('BB')!.allDeps)).toStrictEqual(['A']); + }); +}); From d0b8512449759bf74bb53aabbb6d5224b5f8c5ae Mon Sep 17 00:00:00 2001 From: Masashi Tomooka Date: Wed, 19 Jan 2022 17:20:42 +0900 Subject: [PATCH 22/50] feat(cli): support hotswapping Lambda functions with inline code (#18408) Similarly to #18319, this PR supports hotswap of Lambda functions that use `InlineCode`. `InlineCode` uses [CloudFormation `ZipFile` feature](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html#:~:text=requires%3A%20No%20interruption-,ZipFile,-\(Node.js%20and). To emulate its behavior, we create a zip file of the provided inline code with its filename `index.js` or `index.py` according to the runtime (CFn only supports python or nodejs for ZipFile), and pass the file's binary buffer to Lambda API. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/aws-cdk/README.md | 2 +- .../lib/api/hotswap/lambda-functions.ts | 73 ++++++++- ...nctions-inline-hotswap-deployments.test.ts | 148 ++++++++++++++++++ 3 files changed, 220 insertions(+), 3 deletions(-) create mode 100644 packages/aws-cdk/test/api/hotswap/lambda-functions-inline-hotswap-deployments.test.ts diff --git a/packages/aws-cdk/README.md b/packages/aws-cdk/README.md index 3d1e1b237921a..f1c63975641ac 100644 --- a/packages/aws-cdk/README.md +++ b/packages/aws-cdk/README.md @@ -362,7 +362,7 @@ and that you have the necessary IAM permissions to update the resources that are Hotswapping is currently supported for the following changes (additional changes will be supported in the future): -- Code asset (including Docker image) and tag changes of AWS Lambda functions. +- Code asset (including Docker image and inline code) and tag changes of AWS Lambda functions. - AWS Lambda Versions and Aliases changes. - Definition changes of AWS Step Functions State Machines. - Container asset changes of AWS ECS Services. diff --git a/packages/aws-cdk/lib/api/hotswap/lambda-functions.ts b/packages/aws-cdk/lib/api/hotswap/lambda-functions.ts index 14d96e85ae6d9..d2966e756b69d 100644 --- a/packages/aws-cdk/lib/api/hotswap/lambda-functions.ts +++ b/packages/aws-cdk/lib/api/hotswap/lambda-functions.ts @@ -1,6 +1,8 @@ +import { Writable } from 'stream'; +import * as archiver from 'archiver'; import { flatMap } from '../../util'; import { ISDK } from '../aws-auth'; -import { EvaluateCloudFormationTemplate } from '../evaluate-cloudformation-template'; +import { CfnEvaluationException, EvaluateCloudFormationTemplate } from '../evaluate-cloudformation-template'; import { ChangeHotswapImpact, ChangeHotswapResult, HotswapOperation, HotswappableChangeCandidate } from './common'; /** @@ -108,7 +110,7 @@ async function isLambdaFunctionCodeOnlyChange( switch (updatedPropName) { case 'Code': let foundCodeDifference = false; - let s3Bucket, s3Key, imageUri; + let s3Bucket, s3Key, imageUri, functionCodeZip; for (const newPropName in updatedProp.newValue) { switch (newPropName) { @@ -124,6 +126,18 @@ async function isLambdaFunctionCodeOnlyChange( foundCodeDifference = true; imageUri = await evaluateCfnTemplate.evaluateCfnExpression(updatedProp.newValue[newPropName]); break; + case 'ZipFile': + foundCodeDifference = true; + // We must create a zip package containing a file with the inline code + const functionCode = await evaluateCfnTemplate.evaluateCfnExpression(updatedProp.newValue[newPropName]); + const functionRuntime = await evaluateCfnTemplate.evaluateCfnExpression(change.newValue.Properties?.Runtime); + if (!functionRuntime) { + return ChangeHotswapImpact.REQUIRES_FULL_DEPLOYMENT; + } + // file extension must be chosen depending on the runtime + const codeFileExt = determineCodeFileExtFromRuntime(functionRuntime); + functionCodeZip = await zipString(`index.${codeFileExt}`, functionCode); + break; default: return ChangeHotswapImpact.REQUIRES_FULL_DEPLOYMENT; } @@ -133,6 +147,7 @@ async function isLambdaFunctionCodeOnlyChange( s3Bucket, s3Key, imageUri, + functionCodeZip, }; } break; @@ -173,6 +188,7 @@ interface LambdaFunctionCode { readonly s3Bucket?: string; readonly s3Key?: string; readonly imageUri?: string; + readonly functionCodeZip?: Buffer; } enum TagDeletion { @@ -221,6 +237,7 @@ class LambdaFunctionHotswapOperation implements HotswapOperation { S3Bucket: resource.code.s3Bucket, S3Key: resource.code.s3Key, ImageUri: resource.code.imageUri, + ZipFile: resource.code.functionCodeZip, }).promise(); // only if the code changed is there any point in publishing a new Version @@ -288,3 +305,55 @@ class LambdaFunctionHotswapOperation implements HotswapOperation { return Promise.all(operations); } } + +/** + * Compress a string as a file, returning a promise for the zip buffer + * https://github.com/archiverjs/node-archiver/issues/342 + */ +function zipString(fileName: string, rawString: string): Promise { + return new Promise((resolve, reject) => { + const buffers: Buffer[] = []; + + const converter = new Writable(); + + converter._write = (chunk: Buffer, _: string, callback: () => void) => { + buffers.push(chunk); + process.nextTick(callback); + }; + + converter.on('finish', () => { + resolve(Buffer.concat(buffers)); + }); + + const archive = archiver('zip'); + + archive.on('error', (err) => { + reject(err); + }); + + archive.pipe(converter); + + archive.append(rawString, { + name: fileName, + date: new Date('1980-01-01T00:00:00.000Z'), // Add date to make resulting zip file deterministic + }); + + void archive.finalize(); + }); +} + +/** + * Get file extension from Lambda runtime string. + * We use this extension to create a deployment package from Lambda inline code. + */ +function determineCodeFileExtFromRuntime(runtime: string): string { + if (runtime.startsWith('node')) { + return 'js'; + } + if (runtime.startsWith('python')) { + return 'py'; + } + // Currently inline code only supports Node.js and Python, ignoring other runtimes. + // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html#aws-properties-lambda-function-code-properties + throw new CfnEvaluationException(`runtime ${runtime} is unsupported, only node.js and python runtimes are currently supported.`); +} diff --git a/packages/aws-cdk/test/api/hotswap/lambda-functions-inline-hotswap-deployments.test.ts b/packages/aws-cdk/test/api/hotswap/lambda-functions-inline-hotswap-deployments.test.ts new file mode 100644 index 0000000000000..13554cc655dcb --- /dev/null +++ b/packages/aws-cdk/test/api/hotswap/lambda-functions-inline-hotswap-deployments.test.ts @@ -0,0 +1,148 @@ +import { Lambda } from 'aws-sdk'; +import * as setup from './hotswap-test-setup'; + +let mockUpdateLambdaCode: (params: Lambda.Types.UpdateFunctionCodeRequest) => Lambda.Types.FunctionConfiguration; +let mockTagResource: (params: Lambda.Types.TagResourceRequest) => {}; +let mockUntagResource: (params: Lambda.Types.UntagResourceRequest) => {}; +let hotswapMockSdkProvider: setup.HotswapMockSdkProvider; + +beforeEach(() => { + hotswapMockSdkProvider = setup.setupHotswapTests(); + mockUpdateLambdaCode = jest.fn(); + mockTagResource = jest.fn(); + mockUntagResource = jest.fn(); + hotswapMockSdkProvider.stubLambda({ + updateFunctionCode: mockUpdateLambdaCode, + tagResource: mockTagResource, + untagResource: mockUntagResource, + }); +}); + +test('calls the updateLambdaCode() API when it receives only a code difference in a Lambda function (Inline Node.js code)', async () => { + // GIVEN + setup.setCurrentCfnStackTemplate({ + Resources: { + Func: { + Type: 'AWS::Lambda::Function', + Properties: { + Code: { + ZipFile: 'exports.handler = () => {return true}', + }, + Runtime: 'nodejs14.x', + FunctionName: 'my-function', + }, + }, + }, + }); + const newCode = 'exports.handler = () => {return false}'; + const cdkStackArtifact = setup.cdkStackArtifactOf({ + template: { + Resources: { + Func: { + Type: 'AWS::Lambda::Function', + Properties: { + Code: { + ZipFile: newCode, + }, + Runtime: 'nodejs14.x', + FunctionName: 'my-function', + }, + }, + }, + }, + }); + + // WHEN + const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(cdkStackArtifact); + + // THEN + expect(deployStackResult).not.toBeUndefined(); + expect(mockUpdateLambdaCode).toHaveBeenCalledWith({ + FunctionName: 'my-function', + ZipFile: expect.any(Buffer), + }); +}); + +test('calls the updateLambdaCode() API when it receives only a code difference in a Lambda function (Inline Python code)', async () => { + // GIVEN + setup.setCurrentCfnStackTemplate({ + Resources: { + Func: { + Type: 'AWS::Lambda::Function', + Properties: { + Code: { + ZipFile: 'def handler(event, context):\n return True', + }, + Runtime: 'python3.9', + FunctionName: 'my-function', + }, + }, + }, + }); + const cdkStackArtifact = setup.cdkStackArtifactOf({ + template: { + Resources: { + Func: { + Type: 'AWS::Lambda::Function', + Properties: { + Code: { + ZipFile: 'def handler(event, context):\n return False', + }, + Runtime: 'python3.9', + FunctionName: 'my-function', + }, + }, + }, + }, + }); + + // WHEN + const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(cdkStackArtifact); + + // THEN + expect(deployStackResult).not.toBeUndefined(); + expect(mockUpdateLambdaCode).toHaveBeenCalledWith({ + FunctionName: 'my-function', + ZipFile: expect.any(Buffer), + }); +}); + +test('throw a CfnEvaluationException when it receives an unsupported function runtime', async () => { + // GIVEN + setup.setCurrentCfnStackTemplate({ + Resources: { + Func: { + Type: 'AWS::Lambda::Function', + Properties: { + Code: { + ZipFile: 'def handler(event:, context:) true end', + }, + Runtime: 'ruby2.7', + FunctionName: 'my-function', + }, + }, + }, + }); + const cdkStackArtifact = setup.cdkStackArtifactOf({ + template: { + Resources: { + Func: { + Type: 'AWS::Lambda::Function', + Properties: { + Code: { + ZipFile: 'def handler(event:, context:) false end', + }, + Runtime: 'ruby2.7', + FunctionName: 'my-function', + }, + }, + }, + }, + }); + + // WHEN + const tryHotswap = hotswapMockSdkProvider.tryHotswapDeployment(cdkStackArtifact); + + // THEN + await expect(tryHotswap).rejects.toThrow('runtime ruby2.7 is unsupported, only node.js and python runtimes are currently supported.'); +}); From 7c40777da8ce585f95a3f1c964f3d3d34b3dd747 Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Wed, 19 Jan 2022 01:09:35 -0800 Subject: [PATCH 23/50] chore(codeguruprofiler): migrate to Assertions (#18509) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-codeguruprofiler/package.json | 2 +- .../test/codeguruprofiler.test.ts | 6 ----- .../test/profiling-group.test.ts | 22 +++++++++---------- 3 files changed, 11 insertions(+), 19 deletions(-) delete mode 100644 packages/@aws-cdk/aws-codeguruprofiler/test/codeguruprofiler.test.ts diff --git a/packages/@aws-cdk/aws-codeguruprofiler/package.json b/packages/@aws-cdk/aws-codeguruprofiler/package.json index fd38f19937410..92aaf5b19dbba 100644 --- a/packages/@aws-cdk/aws-codeguruprofiler/package.json +++ b/packages/@aws-cdk/aws-codeguruprofiler/package.json @@ -74,7 +74,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-codeguruprofiler/test/codeguruprofiler.test.ts b/packages/@aws-cdk/aws-codeguruprofiler/test/codeguruprofiler.test.ts deleted file mode 100644 index c4505ad966984..0000000000000 --- a/packages/@aws-cdk/aws-codeguruprofiler/test/codeguruprofiler.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -import '@aws-cdk/assert-internal/jest'; -import {} from '../lib'; - -test('No tests are specified for this package', () => { - expect(true).toBe(true); -}); diff --git a/packages/@aws-cdk/aws-codeguruprofiler/test/profiling-group.test.ts b/packages/@aws-cdk/aws-codeguruprofiler/test/profiling-group.test.ts index 00212902bbec6..94f54c65945c4 100644 --- a/packages/@aws-cdk/aws-codeguruprofiler/test/profiling-group.test.ts +++ b/packages/@aws-cdk/aws-codeguruprofiler/test/profiling-group.test.ts @@ -1,4 +1,4 @@ -import { expect, haveResourceLike } from '@aws-cdk/assert-internal'; +import { Template } from '@aws-cdk/assertions'; import { AccountRootPrincipal, Role } from '@aws-cdk/aws-iam'; import { Stack } from '@aws-cdk/core'; import { ProfilingGroup, ComputePlatform } from '../lib'; @@ -6,7 +6,6 @@ import { ProfilingGroup, ComputePlatform } from '../lib'; /* eslint-disable quote-props */ describe('profiling group', () => { - test('attach read permission to Profiling group via fromProfilingGroupArn', () => { const stack = new Stack(); // dummy role to test out read permissions on ProfilingGroup @@ -17,7 +16,7 @@ describe('profiling group', () => { const profilingGroup = ProfilingGroup.fromProfilingGroupArn(stack, 'MyProfilingGroup', 'arn:aws:codeguru-profiler:us-east-1:1234567890:profilingGroup/MyAwesomeProfilingGroup'); profilingGroup.grantRead(readAppRole); - expect(stack).toMatch({ + Template.fromStack(stack).templateMatches({ 'Resources': { 'ReadAppRole52FE6317': { 'Type': 'AWS::IAM::Role', @@ -89,7 +88,7 @@ describe('profiling group', () => { const profilingGroup = ProfilingGroup.fromProfilingGroupName(stack, 'MyProfilingGroup', 'MyAwesomeProfilingGroup'); profilingGroup.grantPublish(publishAppRole); - expect(stack).toMatch({ + Template.fromStack(stack).templateMatches({ 'Resources': { 'PublishAppRole9FEBD682': { 'Type': 'AWS::IAM::Role', @@ -176,7 +175,7 @@ describe('profiling group', () => { profilingGroupName: 'MyAwesomeProfilingGroup', }); - expect(stack).toMatch({ + Template.fromStack(stack).templateMatches({ 'Resources': { 'MyProfilingGroup829F0507': { 'Type': 'AWS::CodeGuruProfiler::ProfilingGroup', @@ -195,9 +194,9 @@ describe('profiling group', () => { computePlatform: ComputePlatform.AWS_LAMBDA, }); - expect(stack).to(haveResourceLike('AWS::CodeGuruProfiler::ProfilingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeGuruProfiler::ProfilingGroup', { 'ComputePlatform': 'AWSLambda', - })); + }); }); test('default profiling group without name', () => { @@ -205,7 +204,7 @@ describe('profiling group', () => { new ProfilingGroup(stack, 'MyProfilingGroup', { }); - expect(stack).toMatch({ + Template.fromStack(stack).templateMatches({ 'Resources': { 'MyProfilingGroup829F0507': { 'Type': 'AWS::CodeGuruProfiler::ProfilingGroup', @@ -222,7 +221,7 @@ describe('profiling group', () => { new ProfilingGroup(stack, 'MyProfilingGroupWithAReallyLongProfilingGroupNameThatExceedsTheLimitOfProfilingGroupNameSize_InOrderToDoSoTheNameMustBeGreaterThanTwoHundredAndFiftyFiveCharacters_InSuchCasesWePickUpTheFirstOneTwentyCharactersFromTheBeginningAndTheEndAndConcatenateThemToGetTheIdentifier', { }); - expect(stack).toMatch({ + Template.fromStack(stack).templateMatches({ 'Resources': { 'MyProfilingGroupWithAReallyLongProfilingGroupNameThatExceedsTheLimitOfProfilingGroupNameSizeInOrderToDoSoTheNameMustBeGreaterThanTwoHundredAndFiftyFiveCharactersInSuchCasesWePickUpTheFirstOneTwentyCharactersFromTheBeginningAndTheEndAndConca4B39908C': { 'Type': 'AWS::CodeGuruProfiler::ProfilingGroup', @@ -245,7 +244,7 @@ describe('profiling group', () => { profilingGroup.grantPublish(publishAppRole); - expect(stack).toMatch({ + Template.fromStack(stack).templateMatches({ 'Resources': { 'MyProfilingGroup829F0507': { 'Type': 'AWS::CodeGuruProfiler::ProfilingGroup', @@ -329,7 +328,7 @@ describe('profiling group', () => { profilingGroup.grantRead(readAppRole); - expect(stack).toMatch({ + Template.fromStack(stack).templateMatches({ 'Resources': { 'MyProfilingGroup829F0507': { 'Type': 'AWS::CodeGuruProfiler::ProfilingGroup', @@ -401,5 +400,4 @@ describe('profiling group', () => { }, }); }); - }); From 02d9e016812243c8eee79459f3c628f746cc6aa7 Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Wed, 19 Jan 2022 01:55:17 -0800 Subject: [PATCH 24/50] chore(codestarnotifications): migrate to Assertions (#18510) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-codestarnotifications/package.json | 2 +- .../test/notification-rule.test.ts | 22 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/@aws-cdk/aws-codestarnotifications/package.json b/packages/@aws-cdk/aws-codestarnotifications/package.json index e5b0e08f7ac76..251b0b0fa69f7 100644 --- a/packages/@aws-cdk/aws-codestarnotifications/package.json +++ b/packages/@aws-cdk/aws-codestarnotifications/package.json @@ -74,7 +74,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-codestarnotifications/test/notification-rule.test.ts b/packages/@aws-cdk/aws-codestarnotifications/test/notification-rule.test.ts index 332e808ca3176..6670135d5a416 100644 --- a/packages/@aws-cdk/aws-codestarnotifications/test/notification-rule.test.ts +++ b/packages/@aws-cdk/aws-codestarnotifications/test/notification-rule.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as cdk from '@aws-cdk/core'; import * as notifications from '../lib'; import { @@ -24,7 +24,7 @@ describe('NotificationRule', () => { events: ['codebuild-project-build-state-succeeded'], }); - expect(stack).toHaveResourceLike('AWS::CodeStarNotifications::NotificationRule', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeStarNotifications::NotificationRule', { Resource: project.projectArn, EventTypeIds: ['codebuild-project-build-state-succeeded'], }); @@ -41,7 +41,7 @@ describe('NotificationRule', () => { ], }); - expect(stack).toHaveResourceLike('AWS::CodeStarNotifications::NotificationRule', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeStarNotifications::NotificationRule', { Resource: repository.repositoryArn, EventTypeIds: [ 'codecommit-repository-pull-request-created', @@ -65,7 +65,7 @@ describe('NotificationRule', () => { targets: [slack], }); - expect(stack).toHaveResourceLike('AWS::CodeStarNotifications::NotificationRule', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeStarNotifications::NotificationRule', { Name: 'MyNotificationRule', DetailType: 'FULL', EventTypeIds: [ @@ -90,7 +90,7 @@ describe('NotificationRule', () => { events: ['codebuild-project-build-state-succeeded'], }); - expect(stack).toHaveResourceLike('AWS::CodeStarNotifications::NotificationRule', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeStarNotifications::NotificationRule', { Name: 'MyNotificationRuleGeneratedFromId', Resource: project.projectArn, EventTypeIds: ['codebuild-project-build-state-succeeded'], @@ -105,7 +105,7 @@ describe('NotificationRule', () => { events: ['codebuild-project-build-state-succeeded'], }); - expect(stack).toHaveResourceLike('AWS::CodeStarNotifications::NotificationRule', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeStarNotifications::NotificationRule', { Name: 'ificationRuleGeneratedFromIdIsToooooooooooooooooooooooooooooLong', Resource: project.projectArn, EventTypeIds: ['codebuild-project-build-state-succeeded'], @@ -121,7 +121,7 @@ describe('NotificationRule', () => { events: ['codebuild-project-build-state-succeeded'], }); - expect(stack).toHaveResourceLike('AWS::CodeStarNotifications::NotificationRule', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeStarNotifications::NotificationRule', { Name: 'MyNotificationRule', Resource: project.projectArn, EventTypeIds: ['codebuild-project-build-state-succeeded'], @@ -138,7 +138,7 @@ describe('NotificationRule', () => { enabled: false, }); - expect(stack).toHaveResourceLike('AWS::CodeStarNotifications::NotificationRule', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeStarNotifications::NotificationRule', { Name: 'MyNotificationRule', Resource: project.projectArn, EventTypeIds: ['codebuild-project-build-state-succeeded'], @@ -155,7 +155,7 @@ describe('NotificationRule', () => { enabled: true, }); - expect(stack).toHaveResourceLike('AWS::CodeStarNotifications::NotificationRule', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeStarNotifications::NotificationRule', { Name: 'MyNotificationRule', Resource: project.projectArn, EventTypeIds: ['codebuild-project-build-state-succeeded'], @@ -177,7 +177,7 @@ describe('NotificationRule', () => { expect(rule.addTarget(topic)).toEqual(true); - expect(stack).toHaveResourceLike('AWS::CodeStarNotifications::NotificationRule', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeStarNotifications::NotificationRule', { Resource: project.projectArn, EventTypeIds: ['codebuild-project-build-state-succeeded'], Targets: [ @@ -206,7 +206,7 @@ describe('NotificationRule', () => { ], }); - expect(stack).toHaveResourceLike('AWS::CodeStarNotifications::NotificationRule', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeStarNotifications::NotificationRule', { Resource: pipeline.pipelineArn, EventTypeIds: [ 'codepipeline-pipeline-pipeline-execution-succeeded', From c6d200b92777eee532dfe916b54e4f90e60f7822 Mon Sep 17 00:00:00 2001 From: Calvin Combs <66279577+comcalvi@users.noreply.github.com> Date: Wed, 19 Jan 2022 02:43:29 -0800 Subject: [PATCH 25/50] chore(autoscaling): finish migrating autoscaling modules to Assertions (#18511) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-applicationautoscaling/package.json | 2 +- .../test/scalable-target.test.ts | 16 ++++++++-------- .../test/step-scaling-policy.test.ts | 19 +++++++++---------- .../test/target-tracking.test.ts | 10 +++++----- .../aws-autoscaling-common/package.json | 1 - .../test/intervals.test.ts | 16 ---------------- 6 files changed, 23 insertions(+), 41 deletions(-) diff --git a/packages/@aws-cdk/aws-applicationautoscaling/package.json b/packages/@aws-cdk/aws-applicationautoscaling/package.json index 36da42a721e0a..e9f6d2d248dc7 100644 --- a/packages/@aws-cdk/aws-applicationautoscaling/package.json +++ b/packages/@aws-cdk/aws-applicationautoscaling/package.json @@ -79,7 +79,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", diff --git a/packages/@aws-cdk/aws-applicationautoscaling/test/scalable-target.test.ts b/packages/@aws-cdk/aws-applicationautoscaling/test/scalable-target.test.ts index 036fd608a5e6d..bb66486113531 100644 --- a/packages/@aws-cdk/aws-applicationautoscaling/test/scalable-target.test.ts +++ b/packages/@aws-cdk/aws-applicationautoscaling/test/scalable-target.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; @@ -20,7 +20,7 @@ describe('scalable target', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApplicationAutoScaling::ScalableTarget', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalableTarget', { ServiceNamespace: 'dynamodb', ScalableDimension: 'test:TestCount', ResourceId: 'test:this/test', @@ -43,7 +43,7 @@ describe('scalable target', () => { }); // THEN: no exception - expect(stack).toHaveResource('AWS::ApplicationAutoScaling::ScalableTarget', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalableTarget', { ServiceNamespace: 'dynamodb', ScalableDimension: 'test:TestCount', ResourceId: 'test:this/test', @@ -65,7 +65,7 @@ describe('scalable target', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApplicationAutoScaling::ScalableTarget', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalableTarget', { ScheduledActions: [ { ScalableTargetAction: { @@ -104,11 +104,11 @@ describe('scalable target', () => { }); // THEN - expect(stack).not.toHaveResource('AWS::CloudWatch::Alarm', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', Match.not({ Period: 60, - }); + })); - expect(stack).toHaveResource('AWS::CloudWatch::Alarm', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { ComparisonOperator: 'LessThanOrEqualToThreshold', EvaluationPeriods: 1, Metrics: [ @@ -203,7 +203,7 @@ describe('scalable target', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApplicationAutoScaling::ScalableTarget', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalableTarget', { ServiceNamespace: 'dynamodb', ScalableDimension: 'test:TestCount', ResourceId: 'test:this/test', diff --git a/packages/@aws-cdk/aws-applicationautoscaling/test/step-scaling-policy.test.ts b/packages/@aws-cdk/aws-applicationautoscaling/test/step-scaling-policy.test.ts index ca1011881ec14..4e10b53ae9ce9 100644 --- a/packages/@aws-cdk/aws-applicationautoscaling/test/step-scaling-policy.test.ts +++ b/packages/@aws-cdk/aws-applicationautoscaling/test/step-scaling-policy.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { SynthUtils } from '@aws-cdk/assert-internal'; +import { Template } from '@aws-cdk/assertions'; import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import * as cdk from '@aws-cdk/core'; import * as fc from 'fast-check'; @@ -132,7 +131,7 @@ describe('step scaling policy', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApplicationAutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalingPolicy', { PolicyType: 'StepScaling', ScalingTargetId: { Ref: 'Target3191CF44', @@ -169,14 +168,14 @@ describe('step scaling policy', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ApplicationAutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalingPolicy', { PolicyType: 'StepScaling', StepScalingPolicyConfiguration: { AdjustmentType: 'ChangeInCapacity', MetricAggregationType: 'Average', }, }); - expect(stack).toHaveResource('AWS::CloudWatch::Alarm', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { ComparisonOperator: 'GreaterThanOrEqualToThreshold', EvaluationPeriods: 1, AlarmActions: [ @@ -209,14 +208,14 @@ describe('step scaling policy', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ApplicationAutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalingPolicy', { PolicyType: 'StepScaling', StepScalingPolicyConfiguration: { AdjustmentType: 'ChangeInCapacity', MetricAggregationType: 'Maximum', }, }); - expect(stack).toHaveResource('AWS::CloudWatch::Alarm', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { ComparisonOperator: 'GreaterThanOrEqualToThreshold', EvaluationPeriods: 10, ExtendedStatistic: 'p99', @@ -247,14 +246,14 @@ describe('step scaling policy', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ApplicationAutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalingPolicy', { PolicyType: 'StepScaling', StepScalingPolicyConfiguration: { AdjustmentType: 'ChangeInCapacity', MetricAggregationType: 'Maximum', }, }); - expect(stack).toHaveResource('AWS::CloudWatch::Alarm', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { ComparisonOperator: 'GreaterThanOrEqualToThreshold', EvaluationPeriods: 10, DatapointsToAlarm: 6, @@ -297,7 +296,7 @@ function setupStepScaling(intervals: appscaling.ScalingInterval[]) { scalingSteps: intervals, }); - return new ScalingStackTemplate(SynthUtils.synthesize(stack).template); + return new ScalingStackTemplate(Template.fromStack(stack).toJSON()); } class ScalingStackTemplate { diff --git a/packages/@aws-cdk/aws-applicationautoscaling/test/target-tracking.test.ts b/packages/@aws-cdk/aws-applicationautoscaling/test/target-tracking.test.ts index fccc2e2e55f17..fb9f50e5722c7 100644 --- a/packages/@aws-cdk/aws-applicationautoscaling/test/target-tracking.test.ts +++ b/packages/@aws-cdk/aws-applicationautoscaling/test/target-tracking.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import * as cdk from '@aws-cdk/core'; import * as appscaling from '../lib'; @@ -17,7 +17,7 @@ describe('target tracking', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApplicationAutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalingPolicy', { PolicyType: 'TargetTrackingScaling', TargetTrackingScalingPolicyConfiguration: { PredefinedMetricSpecification: { PredefinedMetricType: 'EC2SpotFleetRequestAverageCPUUtilization' }, @@ -41,7 +41,7 @@ describe('target tracking', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApplicationAutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalingPolicy', { PolicyType: 'TargetTrackingScaling', TargetTrackingScalingPolicyConfiguration: { PredefinedMetricSpecification: { PredefinedMetricType: 'LambdaProvisionedConcurrencyUtilization' }, @@ -65,7 +65,7 @@ describe('target tracking', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ApplicationAutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalingPolicy', { TargetTrackingScalingPolicyConfiguration: { PredefinedMetricSpecification: { PredefinedMetricType: 'DynamoDBWriteCapacityUtilization' }, TargetValue: 0.9, @@ -85,7 +85,7 @@ describe('target tracking', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApplicationAutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalingPolicy', { PolicyType: 'TargetTrackingScaling', TargetTrackingScalingPolicyConfiguration: { CustomizedMetricSpecification: { diff --git a/packages/@aws-cdk/aws-autoscaling-common/package.json b/packages/@aws-cdk/aws-autoscaling-common/package.json index cdaaae9694498..6a0a7fa6efafd 100644 --- a/packages/@aws-cdk/aws-autoscaling-common/package.json +++ b/packages/@aws-cdk/aws-autoscaling-common/package.json @@ -64,7 +64,6 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/pkglint": "0.0.0", diff --git a/packages/@aws-cdk/aws-autoscaling-common/test/intervals.test.ts b/packages/@aws-cdk/aws-autoscaling-common/test/intervals.test.ts index 0b0b9b996a1bb..e36923299003f 100644 --- a/packages/@aws-cdk/aws-autoscaling-common/test/intervals.test.ts +++ b/packages/@aws-cdk/aws-autoscaling-common/test/intervals.test.ts @@ -14,8 +14,6 @@ describe('intervals', () => { { lower: 80, upper: 90, change: +1 }, { lower: 90, upper: Infinity, change: +2 }, ]); - - }); test('test interval completion', () => { @@ -37,8 +35,6 @@ describe('intervals', () => { { lower: 65, upper: 85, change: +3 }, { lower: 85, upper: Infinity, change: undefined }, ]); - - }); test('bounds propagation fails if middle boundary missing', () => { @@ -48,8 +44,6 @@ describe('intervals', () => { { upper: 20, change: -1 }, ], false); }).toThrow(); - - }); test('lower alarm index is lower than higher alarm index', () => { @@ -63,8 +57,6 @@ describe('intervals', () => { || alarms.lowerAlarmIntervalIndex < alarms.upperAlarmIntervalIndex); }, )); - - }); test('never pick undefined intervals for relative alarms', () => { @@ -77,8 +69,6 @@ describe('intervals', () => { && (alarms.upperAlarmIntervalIndex === undefined || intervals[alarms.upperAlarmIntervalIndex].change !== undefined); }, )); - - }); test('pick intervals on either side of the undefined interval, if present', () => { @@ -93,8 +83,6 @@ describe('intervals', () => { return (alarms.lowerAlarmIntervalIndex === i - 1 && alarms.upperAlarmIntervalIndex === i + 1); }, )); - - }); test('no picking upper bound infinity for lower alarm', () => { @@ -107,8 +95,6 @@ describe('intervals', () => { return intervals[alarms.lowerAlarmIntervalIndex!].upper !== Infinity; }, )); - - }); test('no picking lower bound 0 for upper alarm', () => { @@ -121,8 +107,6 @@ describe('intervals', () => { return intervals[alarms.upperAlarmIntervalIndex!].lower !== 0; }, )); - - }); }); From fb41a1670e1860c4710d6c8fb3f5c16fb4127555 Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Wed, 19 Jan 2022 03:32:25 -0800 Subject: [PATCH 26/50] chore(acmpca): migrate to Assertions (#18512) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-acmpca/package.json | 2 +- packages/@aws-cdk/aws-acmpca/test/acmpca.test.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-acmpca/package.json b/packages/@aws-cdk/aws-acmpca/package.json index b4dd6cff2355b..360332a20d54e 100644 --- a/packages/@aws-cdk/aws-acmpca/package.json +++ b/packages/@aws-cdk/aws-acmpca/package.json @@ -74,7 +74,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", diff --git a/packages/@aws-cdk/aws-acmpca/test/acmpca.test.ts b/packages/@aws-cdk/aws-acmpca/test/acmpca.test.ts index c4505ad966984..b7175f106094e 100644 --- a/packages/@aws-cdk/aws-acmpca/test/acmpca.test.ts +++ b/packages/@aws-cdk/aws-acmpca/test/acmpca.test.ts @@ -1,4 +1,3 @@ -import '@aws-cdk/assert-internal/jest'; import {} from '../lib'; test('No tests are specified for this package', () => { From 6a869f5cac9f421600011ed9eb458207bbb3cf68 Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Wed, 19 Jan 2022 04:20:44 -0800 Subject: [PATCH 27/50] chore(yaml-cfn): migrate to Assertions (#18513) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/yaml-cfn/package.json | 2 +- packages/@aws-cdk/yaml-cfn/test/deserialization.test.ts | 1 - packages/@aws-cdk/yaml-cfn/test/serialization.test.ts | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/@aws-cdk/yaml-cfn/package.json b/packages/@aws-cdk/yaml-cfn/package.json index cbe41433d2ac7..b0035c4e80552 100644 --- a/packages/@aws-cdk/yaml-cfn/package.json +++ b/packages/@aws-cdk/yaml-cfn/package.json @@ -69,7 +69,7 @@ "yaml": "1.10.2" }, "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/pkglint": "0.0.0", "@types/jest": "^27.4.0", diff --git a/packages/@aws-cdk/yaml-cfn/test/deserialization.test.ts b/packages/@aws-cdk/yaml-cfn/test/deserialization.test.ts index 3d790ea9d60e6..3c6bbc09f2397 100644 --- a/packages/@aws-cdk/yaml-cfn/test/deserialization.test.ts +++ b/packages/@aws-cdk/yaml-cfn/test/deserialization.test.ts @@ -1,4 +1,3 @@ -import '@aws-cdk/assert-internal/jest'; import * as yaml_cfn from '../lib'; test('Unquoted year-month-day is treated as a string, not a Date', () => { diff --git a/packages/@aws-cdk/yaml-cfn/test/serialization.test.ts b/packages/@aws-cdk/yaml-cfn/test/serialization.test.ts index 3237f16f986b4..7b9d07ca2a50d 100644 --- a/packages/@aws-cdk/yaml-cfn/test/serialization.test.ts +++ b/packages/@aws-cdk/yaml-cfn/test/serialization.test.ts @@ -1,4 +1,3 @@ -import '@aws-cdk/assert-internal/jest'; import * as yaml_cfn from '../lib'; test('An object with a single string value is serialized as a simple string', () => { From 132ab879ab26ca763e635521f19907db4f5c3aed Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Wed, 19 Jan 2022 05:09:08 -0800 Subject: [PATCH 28/50] chore(dynamodb-global): migrate to Assertions (#18515) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-dynamodb-global/package.json | 2 +- .../aws-dynamodb-global/test/dynamodb-global.test.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/@aws-cdk/aws-dynamodb-global/package.json b/packages/@aws-cdk/aws-dynamodb-global/package.json index 88856d300eda9..61e2d44daab47 100644 --- a/packages/@aws-cdk/aws-dynamodb-global/package.json +++ b/packages/@aws-cdk/aws-dynamodb-global/package.json @@ -56,7 +56,7 @@ "constructs": "^3.3.69" }, "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/pkglint": "0.0.0", diff --git a/packages/@aws-cdk/aws-dynamodb-global/test/dynamodb-global.test.ts b/packages/@aws-cdk/aws-dynamodb-global/test/dynamodb-global.test.ts index 5a6a45d5be3a3..1d6f90d4a16ad 100644 --- a/packages/@aws-cdk/aws-dynamodb-global/test/dynamodb-global.test.ts +++ b/packages/@aws-cdk/aws-dynamodb-global/test/dynamodb-global.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { Attribute, AttributeType, StreamViewType, Table } from '@aws-cdk/aws-dynamodb'; import { describeDeprecated, testDeprecated } from '@aws-cdk/cdk-build-tools'; import { App, CfnOutput, Stack } from '@aws-cdk/core'; @@ -26,7 +26,7 @@ describeDeprecated('Default Global DynamoDB stack', () => { const topStack = stack.node.findChild(CONSTRUCT_NAME) as Stack; for ( const reg of STACK_PROPS.regions ) { const tableStack = topStack.node.findChild(CONSTRUCT_NAME + '-' + reg) as Stack; - expect(tableStack).toHaveResource('AWS::DynamoDB::Table', { + Template.fromStack(tableStack).hasResourceProperties('AWS::DynamoDB::Table', { 'KeySchema': [ { 'AttributeName': 'hashKey', @@ -46,12 +46,12 @@ describeDeprecated('Default Global DynamoDB stack', () => { }); } const customResourceStack = stack.node.findChild(CONSTRUCT_NAME + '-CustomResource') as Stack; - expect(customResourceStack).toHaveResource('AWS::Lambda::Function', { + Template.fromStack(customResourceStack).hasResourceProperties('AWS::Lambda::Function', { Description: 'Lambda to make DynamoDB a global table', Handler: 'index.handler', Timeout: 300, }); - expect(customResourceStack).toHaveResource('AWS::CloudFormation::CustomResource', { + Template.fromStack(customResourceStack).hasResourceProperties('AWS::CloudFormation::CustomResource', { Regions: STACK_PROPS.regions, ResourceType: 'Custom::DynamoGlobalTableCoordinator', TableName: TABLE_NAME, @@ -75,7 +75,7 @@ testDeprecated('GlobalTable generated stacks inherit their account from the pare value: globalTable.regionalTables[0].tableStreamArn!, }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ 'Outputs': { 'DynamoDbOutput': { 'Value': { From 69375646428674647322f1ad8f534f686ef193ef Mon Sep 17 00:00:00 2001 From: Madeline Kusters <80541297+madeline-k@users.noreply.github.com> Date: Wed, 19 Jan 2022 05:56:34 -0800 Subject: [PATCH 29/50] chore(ecs): migrate to Assertions (#18516) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-ecs/package.json | 2 +- .../test/app-mesh-proxy-configuration.test.ts | 8 +- .../aws-ecs/test/aws-log-driver.test.ts | 45 ++-- .../@aws-cdk/aws-ecs/test/cluster.test.ts | 162 ++++++------ .../aws-ecs/test/container-definition.test.ts | 246 +++++++----------- .../aws-ecs/test/ec2/cross-stack.test.ts | 10 +- .../aws-ecs/test/ec2/ec2-service.test.ts | 147 +++++------ .../test/ec2/ec2-task-definition.test.ts | 109 ++++---- .../aws-ecs/test/environment-file.test.ts | 1 - .../test/external/external-service.test.ts | 12 +- .../external/external-task-definition.test.ts | 24 +- .../test/fargate/fargate-service.test.ts | 131 +++++----- .../fargate/fargate-task-definition.test.ts | 12 +- .../aws-ecs/test/firelens-log-driver.test.ts | 75 +++--- .../aws-ecs/test/fluentd-log-driver.test.ts | 36 +-- .../aws-ecs/test/gelf-log-driver.test.ts | 18 +- .../tag-parameter-container-image.test.ts | 23 +- .../aws-ecs/test/journald-log-driver.test.ts | 28 +- .../aws-ecs/test/json-file-log-driver.test.ts | 26 +- .../aws-ecs/test/splunk-log-driver.test.ts | 44 ++-- .../aws-ecs/test/syslog-log-driver.test.ts | 28 +- .../aws-ecs/test/task-definition.test.ts | 4 +- 22 files changed, 517 insertions(+), 674 deletions(-) diff --git a/packages/@aws-cdk/aws-ecs/package.json b/packages/@aws-cdk/aws-ecs/package.json index 4dd48f968fcf3..c3d3def173a2b 100644 --- a/packages/@aws-cdk/aws-ecs/package.json +++ b/packages/@aws-cdk/aws-ecs/package.json @@ -79,7 +79,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/aws-s3-deployment": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", diff --git a/packages/@aws-cdk/aws-ecs/test/app-mesh-proxy-configuration.test.ts b/packages/@aws-cdk/aws-ecs/test/app-mesh-proxy-configuration.test.ts index 7c8bd5f880f77..683a7301b54dd 100644 --- a/packages/@aws-cdk/aws-ecs/test/app-mesh-proxy-configuration.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/app-mesh-proxy-configuration.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as cdk from '@aws-cdk/core'; import * as ecs from '../lib'; @@ -33,7 +33,7 @@ describe('app mesh proxy configuration', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ProxyConfiguration: { ContainerName: 'envoy', ProxyConfigurationProperties: [ @@ -99,7 +99,7 @@ describe('app mesh proxy configuration', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ProxyConfiguration: { ContainerName: 'envoy', ProxyConfigurationProperties: [ @@ -155,7 +155,7 @@ describe('app mesh proxy configuration', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ProxyConfiguration: { ContainerName: 'envoy', ProxyConfigurationProperties: [ diff --git a/packages/@aws-cdk/aws-ecs/test/aws-log-driver.test.ts b/packages/@aws-cdk/aws-ecs/test/aws-log-driver.test.ts index fb4d5c9f691cc..de7e4faa24f19 100644 --- a/packages/@aws-cdk/aws-ecs/test/aws-log-driver.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/aws-log-driver.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as logs from '@aws-cdk/aws-logs'; import * as cdk from '@aws-cdk/core'; import * as ecs from '../lib'; @@ -29,13 +29,13 @@ describe('aws log driver', () => { }); // THEN - expect(stack).toHaveResource('AWS::Logs::LogGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::Logs::LogGroup', { RetentionInDays: logs.RetentionDays.ONE_MONTH, }); - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ LogConfiguration: { LogDriver: 'awslogs', Options: { @@ -47,11 +47,9 @@ describe('aws log driver', () => { 'mode': 'non-blocking', }, }, - }, + }), ], }); - - }); test('create an aws log driver using awsLogs', () => { @@ -67,13 +65,13 @@ describe('aws log driver', () => { }); // THEN - expect(stack).toHaveResource('AWS::Logs::LogGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::Logs::LogGroup', { RetentionInDays: logs.RetentionDays.ONE_MONTH, }); - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ LogConfiguration: { LogDriver: 'awslogs', Options: { @@ -84,11 +82,9 @@ describe('aws log driver', () => { 'awslogs-multiline-pattern': 'pattern', }, }, - }, + }), ], }); - - }); test('with a defined log group', () => { @@ -105,13 +101,13 @@ describe('aws log driver', () => { }); // THEN - expect(stack).toHaveResource('AWS::Logs::LogGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::Logs::LogGroup', { RetentionInDays: logs.RetentionDays.TWO_YEARS, }); - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ LogConfiguration: { LogDriver: 'awslogs', Options: { @@ -120,11 +116,9 @@ describe('aws log driver', () => { 'awslogs-region': { Ref: 'AWS::Region' }, }, }, - }, + }), ], }); - - }); test('without a defined log group: creates one anyway', () => { @@ -137,9 +131,7 @@ describe('aws log driver', () => { }); // THEN - expect(stack).toHaveResource('AWS::Logs::LogGroup', {}); - - + Template.fromStack(stack).hasResourceProperties('AWS::Logs::LogGroup', {}); }); test('throws when specifying log retention and log group', () => { @@ -172,10 +164,10 @@ describe('aws log driver', () => { }); // THEN - expect(stack).toCountResources('AWS::Logs::LogGroup', 0); - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).resourceCountIs('AWS::Logs::LogGroup', 0); + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ LogConfiguration: { LogDriver: 'awslogs', Options: { @@ -184,9 +176,8 @@ describe('aws log driver', () => { 'awslogs-region': logGroupRegion, }, }, - }, + }), ], }); }); - }); diff --git a/packages/@aws-cdk/aws-ecs/test/cluster.test.ts b/packages/@aws-cdk/aws-ecs/test/cluster.test.ts index 15a25f6987559..f6b34f76439c0 100644 --- a/packages/@aws-cdk/aws-ecs/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/cluster.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { ABSENT } from '@aws-cdk/assert-internal'; +import { Match, Template } from '@aws-cdk/assertions'; import * as autoscaling from '@aws-cdk/aws-autoscaling'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as kms from '@aws-cdk/aws-kms'; @@ -21,9 +20,9 @@ describe('cluster', () => { instanceType: new ec2.InstanceType('t2.micro'), }); - expect(stack).toHaveResource('AWS::ECS::Cluster'); + Template.fromStack(stack).resourceCountIs('AWS::ECS::Cluster', 1); - expect(stack).toHaveResource('AWS::EC2::VPC', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::VPC', { CidrBlock: '10.0.0.0/16', EnableDnsHostnames: true, EnableDnsSupport: true, @@ -36,7 +35,7 @@ describe('cluster', () => { ], }); - expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { ImageId: { Ref: 'SsmParameterValueawsserviceecsoptimizedamiamazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter', }, @@ -69,7 +68,7 @@ describe('cluster', () => { }, }); - expect(stack).toHaveResource('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { MaxSize: '1', MinSize: '1', LaunchConfigurationName: { @@ -92,7 +91,7 @@ describe('cluster', () => { ], }); - expect(stack).toHaveResource('AWS::EC2::SecurityGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroup', { GroupDescription: 'Default/EcsCluster/DefaultAutoScalingGroup/InstanceSecurityGroup', SecurityGroupEgress: [ { @@ -112,7 +111,7 @@ describe('cluster', () => { }, }); - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Statement: [ { @@ -127,7 +126,7 @@ describe('cluster', () => { }, }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -192,9 +191,9 @@ describe('cluster', () => { instanceType: new ec2.InstanceType('t2.micro'), }); - expect(stack).toHaveResource('AWS::ECS::Cluster'); + Template.fromStack(stack).resourceCountIs('AWS::ECS::Cluster', 1); - expect(stack).toHaveResource('AWS::EC2::VPC', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::VPC', { CidrBlock: '10.0.0.0/16', EnableDnsHostnames: true, EnableDnsSupport: true, @@ -207,7 +206,7 @@ describe('cluster', () => { ], }); - expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { ImageId: { Ref: 'SsmParameterValueawsserviceecsoptimizedamiamazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter', }, @@ -240,7 +239,7 @@ describe('cluster', () => { }, }); - expect(stack).toHaveResource('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { MaxSize: '1', MinSize: '1', LaunchConfigurationName: { @@ -263,7 +262,7 @@ describe('cluster', () => { ], }); - expect(stack).toHaveResource('AWS::EC2::SecurityGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroup', { GroupDescription: 'Default/EcsCluster/DefaultAutoScalingGroup/InstanceSecurityGroup', SecurityGroupEgress: [ { @@ -283,7 +282,7 @@ describe('cluster', () => { }, }); - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Statement: [ { @@ -298,7 +297,7 @@ describe('cluster', () => { }, }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -381,7 +380,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::LifecycleHook', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LifecycleHook', { AutoScalingGroupName: { Ref: 'EcsClusterDefaultAutoScalingGroupASGC1A785DB' }, LifecycleTransition: 'autoscaling:EC2_INSTANCE_TERMINATING', DefaultResult: 'CONTINUE', @@ -390,7 +389,7 @@ describe('cluster', () => { RoleARN: { 'Fn::GetAtt': ['EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleA38EC83B', 'Arn'] }, }); - expect(stack).toHaveResource('AWS::Lambda::Function', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { Timeout: 310, Environment: { Variables: { @@ -402,7 +401,7 @@ describe('cluster', () => { Handler: 'index.lambda_handler', }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -522,7 +521,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::SNS::Topic', { + Template.fromStack(stack).hasResourceProperties('AWS::SNS::Topic', { KmsMasterKeyId: { 'Fn::GetAtt': [ 'Key961B73FD', @@ -549,16 +548,16 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::ServiceDiscovery::PrivateDnsNamespace', { + Template.fromStack(stack).hasResourceProperties('AWS::ServiceDiscovery::PrivateDnsNamespace', { Name: 'foo.com', Vpc: { Ref: 'MyVpcF9F0CA6F', }, }); - expect(stack).toHaveResource('AWS::ECS::Cluster'); + Template.fromStack(stack).resourceCountIs('AWS::ECS::Cluster', 1); - expect(stack).toHaveResource('AWS::EC2::VPC', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::VPC', { CidrBlock: '10.0.0.0/16', EnableDnsHostnames: true, EnableDnsSupport: true, @@ -571,7 +570,7 @@ describe('cluster', () => { ], }); - expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { ImageId: { Ref: 'SsmParameterValueawsserviceecsoptimizedamiamazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter', }, @@ -604,7 +603,7 @@ describe('cluster', () => { }, }); - expect(stack).toHaveResource('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { MaxSize: '1', MinSize: '1', LaunchConfigurationName: { @@ -627,7 +626,7 @@ describe('cluster', () => { ], }); - expect(stack).toHaveResource('AWS::EC2::SecurityGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroup', { GroupDescription: 'Default/EcsCluster/DefaultAutoScalingGroup/InstanceSecurityGroup', SecurityGroupEgress: [ { @@ -647,7 +646,7 @@ describe('cluster', () => { }, }); - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Statement: [ { @@ -662,7 +661,7 @@ describe('cluster', () => { }, }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -727,7 +726,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { InstanceType: 'm3.large', }); @@ -746,7 +745,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { MaxSize: '3', }); @@ -767,7 +766,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { ImageId: { Ref: 'SsmParameterValueawsserviceecsoptimizedamiwindowsserver2019englishfullrecommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter', }, @@ -826,7 +825,7 @@ describe('cluster', () => { // THEN const assembly = app.synth(); const template = assembly.getStackByName(stack.stackName).template; - expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { ImageId: { Ref: 'SsmParameterValueawsserviceecsoptimizedamiamazonlinux2gpurecommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter', }, @@ -1028,7 +1027,7 @@ describe('cluster', () => { // THEN const assembly = app.synth(); const template = assembly.getStackByName(stack.stackName).template; - expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { ImageId: { Ref: 'SsmParameterValueawsserviceecsoptimizedamiamazonlinux2gpurecommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter', }, @@ -1059,7 +1058,7 @@ describe('cluster', () => { // THEN const assembly = app.synth(); const template = assembly.getStackByName(stack.stackName).template; - expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { ImageId: { Ref: 'SsmParameterValueawsserviceecsoptimizedamiamazonlinuxrecommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter', }, @@ -1112,7 +1111,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { SpotPrice: '0.31', }); @@ -1131,7 +1130,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::LifecycleHook', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LifecycleHook', { HeartbeatTimeout: 60, }); @@ -1151,7 +1150,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { UserData: { 'Fn::Base64': { 'Fn::Join': [ @@ -1183,7 +1182,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { UserData: { 'Fn::Base64': { 'Fn::Join': [ @@ -1219,7 +1218,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::ServiceDiscovery::PrivateDnsNamespace', { + Template.fromStack(stack).hasResourceProperties('AWS::ServiceDiscovery::PrivateDnsNamespace', { Name: 'foo.com', Vpc: { Ref: 'MyVpcF9F0CA6F', @@ -1246,7 +1245,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::ServiceDiscovery::PublicDnsNamespace', { + Template.fromStack(stack).hasResourceProperties('AWS::ServiceDiscovery::PublicDnsNamespace', { Name: 'foo.com', }); @@ -1331,11 +1330,11 @@ describe('cluster', () => { cluster.connections.allowToAnyIpv4(ec2.Port.tcp(443)); // THEN - expect(stack).toHaveResource('AWS::EC2::SecurityGroupEgress', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroupEgress', { GroupId: 'sg-1', }); - expect(stack).toCountResources('AWS::EC2::SecurityGroupEgress', 1); + Template.fromStack(stack).resourceCountIs('AWS::EC2::SecurityGroupEgress', 1); }); @@ -1403,9 +1402,9 @@ describe('cluster', () => { }, }); - expect(stack).toHaveResource('AWS::ECS::Cluster'); + Template.fromStack(stack).resourceCountIs('AWS::ECS::Cluster', 1); - expect(stack).toHaveResource('AWS::EC2::VPC', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::VPC', { CidrBlock: '10.0.0.0/16', EnableDnsHostnames: true, EnableDnsSupport: true, @@ -1418,7 +1417,7 @@ describe('cluster', () => { ], }); - expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { ImageId: { Ref: 'SsmParameterValueawsserviceecsoptimizedamiamazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter', }, @@ -1452,7 +1451,7 @@ describe('cluster', () => { }, }); - expect(stack).toHaveResource('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { MaxSize: '1', MinSize: '1', LaunchConfigurationName: { @@ -1475,7 +1474,7 @@ describe('cluster', () => { ], }); - expect(stack).toHaveResource('AWS::EC2::SecurityGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroup', { GroupDescription: 'Default/EcsCluster/DefaultAutoScalingGroup/InstanceSecurityGroup', SecurityGroupEgress: [ { @@ -1507,7 +1506,7 @@ describe('cluster', () => { new ecs.Cluster(stack, 'EcsCluster', { containerInsights: true }); // THEN - expect(stack).toHaveResource('AWS::ECS::Cluster', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Cluster', { ClusterSettings: [ { Name: 'containerInsights', @@ -1527,7 +1526,7 @@ describe('cluster', () => { new ecs.Cluster(stack, 'EcsCluster', { containerInsights: false }); // THEN - expect(stack).toHaveResource('AWS::ECS::Cluster', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Cluster', { ClusterSettings: [ { Name: 'containerInsights', @@ -1593,9 +1592,9 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Cluster'); - expect(stack).toHaveResource('AWS::AutoScaling::AutoScalingGroup'); - expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).resourceCountIs('AWS::ECS::Cluster', 1); + Template.fromStack(stack).resourceCountIs('AWS::AutoScaling::AutoScalingGroup', 1); + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { ImageId: { Ref: 'SsmParameterValueawsservicebottlerocketawsecs1x8664latestimageidC96584B6F00A464EAD1953AFF4B05118Parameter', }, @@ -1614,7 +1613,7 @@ describe('cluster', () => { }, }, }); - expect(stack).toHaveResourceLike('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Statement: [ { @@ -1685,7 +1684,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { ImageId: { Ref: 'SsmParameterValueawsservicebottlerocketawsecs1arm64latestimageidC96584B6F00A464EAD1953AFF4B05118Parameter', }, @@ -1714,7 +1713,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { UserData: { 'Fn::Base64': { 'Fn::Join': [ @@ -1742,11 +1741,11 @@ describe('cluster', () => { new ecs.Cluster(stack, 'EcsCluster', { capacityProviders: ['FARGATE_SPOT'] }); // THEN - expect(stack).toHaveResource('AWS::ECS::Cluster', { - CapacityProviders: ABSENT, + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Cluster', { + CapacityProviders: Match.absent(), }); - expect(stack).toHaveResource('AWS::ECS::ClusterCapacityProviderAssociations', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::ClusterCapacityProviderAssociations', { CapacityProviders: ['FARGATE_SPOT'], }); @@ -1764,11 +1763,11 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Cluster', { - CapacityProviders: ABSENT, + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Cluster', { + CapacityProviders: Match.absent(), }); - expect(stack).toHaveResource('AWS::ECS::ClusterCapacityProviderAssociations', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::ClusterCapacityProviderAssociations', { CapacityProviders: ['FARGATE', 'FARGATE_SPOT'], }); @@ -1785,11 +1784,11 @@ describe('cluster', () => { cluster.enableFargateCapacityProviders(); // THEN - expect(stack).toHaveResource('AWS::ECS::Cluster', { - CapacityProviders: ABSENT, + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Cluster', { + CapacityProviders: Match.absent(), }); - expect(stack).toHaveResource('AWS::ECS::ClusterCapacityProviderAssociations', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::ClusterCapacityProviderAssociations', { CapacityProviders: ['FARGATE', 'FARGATE_SPOT'], }); @@ -1807,11 +1806,11 @@ describe('cluster', () => { cluster.addCapacityProvider('FARGATE'); // does not add twice // THEN - expect(stack).toHaveResource('AWS::ECS::Cluster', { - CapacityProviders: ABSENT, + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Cluster', { + CapacityProviders: Match.absent(), }); - expect(stack).toHaveResource('AWS::ECS::ClusterCapacityProviderAssociations', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::ClusterCapacityProviderAssociations', { CapacityProviders: ['FARGATE'], }); @@ -1829,11 +1828,11 @@ describe('cluster', () => { cluster.addCapacityProvider('FARGATE'); // does not add twice // THEN - expect(stack).toHaveResource('AWS::ECS::Cluster', { - CapacityProviders: ABSENT, + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Cluster', { + CapacityProviders: Match.absent(), }); - expect(stack).toHaveResource('AWS::ECS::ClusterCapacityProviderAssociations', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::ClusterCapacityProviderAssociations', { CapacityProviders: ['FARGATE'], }); @@ -1871,7 +1870,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::CapacityProvider', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::CapacityProvider', { AutoScalingGroupProvider: { AutoScalingGroupArn: { Ref: 'asgASG4D014670', @@ -1904,12 +1903,12 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::CapacityProvider', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::CapacityProvider', { AutoScalingGroupProvider: { AutoScalingGroupArn: { Ref: 'asgASG4D014670', }, - ManagedScaling: ABSENT, + ManagedScaling: Match.absent(), ManagedTerminationProtection: 'ENABLED', }, }); @@ -1933,7 +1932,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::AutoScaling::AutoScalingGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { NewInstancesProtectedFromScaleIn: true, }); @@ -1957,10 +1956,9 @@ describe('cluster', () => { }); // THEN - expect(stack).not.toHaveResource('AWS::AutoScaling::AutoScalingGroup', { - NewInstancesProtectedFromScaleIn: true, + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::AutoScalingGroup', { + NewInstancesProtectedFromScaleIn: Match.absent(), }); - }); test('can add ASG capacity via Capacity Provider', () => { @@ -1989,7 +1987,7 @@ describe('cluster', () => { cluster.addAsgCapacityProvider(capacityProvider); // THEN - expect(stack).toHaveResource('AWS::ECS::ClusterCapacityProviderAssociations', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::ClusterCapacityProviderAssociations', { Cluster: { Ref: 'EcsCluster97242B84', }, @@ -2036,7 +2034,7 @@ describe('cluster', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Cluster', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Cluster', { Configuration: { ExecuteCommandConfiguration: { KmsKeyId: { @@ -2186,7 +2184,7 @@ test('can add ASG capacity via Capacity Provider by not specifying machineImageT // THEN Bottlerocket LaunchConfiguration - expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { ImageId: { Ref: 'SsmParameterValueawsservicebottlerocketawsecs1x8664latestimageidC96584B6F00A464EAD1953AFF4B05118Parameter', @@ -2208,7 +2206,7 @@ test('can add ASG capacity via Capacity Provider by not specifying machineImageT }); // THEN AmazonLinux2 LaunchConfiguration - expect(stack).toHaveResource('AWS::AutoScaling::LaunchConfiguration', { + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::LaunchConfiguration', { ImageId: { Ref: 'SsmParameterValueawsserviceecsoptimizedamiamazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter', }, @@ -2229,7 +2227,7 @@ test('can add ASG capacity via Capacity Provider by not specifying machineImageT }, }); - expect(stack).toHaveResource('AWS::ECS::ClusterCapacityProviderAssociations', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::ClusterCapacityProviderAssociations', { CapacityProviders: [ 'FARGATE', 'FARGATE_SPOT', 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 ac7a7a7824450..47ee57b4625d6 100644 --- a/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/container-definition.test.ts @@ -1,6 +1,5 @@ -import '@aws-cdk/assert-internal/jest'; import * as path from 'path'; -import { InspectionFailure } from '@aws-cdk/assert-internal'; +import { Match, Template } from '@aws-cdk/assertions'; import * as ecr_assets from '@aws-cdk/aws-ecr-assets'; import * as s3 from '@aws-cdk/aws-s3'; import * as secretsmanager from '@aws-cdk/aws-secretsmanager'; @@ -24,7 +23,7 @@ describe('container definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Essential: true, @@ -91,7 +90,7 @@ describe('container definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Command: [ @@ -491,16 +490,9 @@ describe('container definition', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', (props: any, inspection: InspectionFailure) => { - if (props.NetworkMode === undefined) { - return true; - } - - inspection.failureReason = 'CF template should not have NetworkMode defined for a task definition that relies on NAT network mode.'; - return false; + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { + NetworkMode: Match.absent(), }); - - }); }); }); @@ -700,9 +692,9 @@ describe('container definition', () => { container.addEnvironment('SECOND_ENVIRONEMENT_VARIABLE', 'second test value'); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Environment: [{ Name: 'TEST_ENVIRONMENT_VARIABLE', Value: 'test environment variable value', @@ -711,11 +703,9 @@ describe('container definition', () => { Name: 'SECOND_ENVIRONEMENT_VARIABLE', Value: 'second test value', }], - }, + }), ], }); - - }); test('can add environment variables to container definition with no environment', () => { @@ -731,18 +721,16 @@ describe('container definition', () => { container.addEnvironment('SECOND_ENVIRONEMENT_VARIABLE', 'second test value'); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Environment: [{ Name: 'SECOND_ENVIRONEMENT_VARIABLE', Value: 'second test value', }], - }, + }), ], }); - - }); test('can add port mappings to the container definition by props', () => { @@ -758,11 +746,11 @@ describe('container definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { - PortMappings: [{ ContainerPort: 80 }], - }, + Match.objectLike({ + PortMappings: [Match.objectLike({ ContainerPort: 80 })], + }), ], }); }); @@ -782,14 +770,14 @@ describe('container definition', () => { containerDefinition.addPortMappings({ containerPort: 443 }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ PortMappings: [ - { ContainerPort: 80 }, - { ContainerPort: 443 }, + Match.objectLike({ ContainerPort: 80 }), + Match.objectLike({ ContainerPort: 443 }), ], - }, + }), ], }); }); @@ -810,9 +798,9 @@ describe('container definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ SystemControls: [ { Namespace: 'SomeNamespace1', @@ -823,7 +811,7 @@ describe('container definition', () => { Value: 'SomeValue2', }, ], - }, + }), ], }); }); @@ -843,9 +831,9 @@ describe('container definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ EnvironmentFiles: [{ Type: 's3', Value: { @@ -887,11 +875,10 @@ describe('container definition', () => { ], }, }], - }, + }), ], }); - }); test('can add s3 bucket environment file to the container definition', () => { // GIVEN @@ -909,9 +896,9 @@ describe('container definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ EnvironmentFiles: [{ Type: 's3', Value: { @@ -927,13 +914,12 @@ describe('container definition', () => { ], }, }], - }, + }), ], }); - - }); }); + describe('with Fargate task definitions', () => { test('can add asset environment file to the container definition', () => { // GIVEN @@ -948,9 +934,9 @@ describe('container definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ EnvironmentFiles: [{ Type: 's3', Value: { @@ -992,12 +978,11 @@ describe('container definition', () => { ], }, }], - }, + }), ], }); - - }); + test('can add s3 bucket environment file to the container definition', () => { // GIVEN const stack = new cdk.Stack(); @@ -1014,9 +999,9 @@ describe('container definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ EnvironmentFiles: [{ Type: 's3', Value: { @@ -1032,11 +1017,9 @@ describe('container definition', () => { ], }, }], - }, + }), ], }); - - }); }); }); @@ -1055,9 +1038,9 @@ describe('container definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Image: 'test', ResourceRequirements: [ { @@ -1065,11 +1048,9 @@ describe('container definition', () => { Value: '4', }, ], - }, + }), ], }); - - }); }); @@ -1097,14 +1078,14 @@ describe('container definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { Family: 'Ec2TaskDef', InferenceAccelerators: [{ DeviceName: 'device1', DeviceType: 'eia2.medium', }], ContainerDefinitions: [ - { + Match.objectLike({ Image: 'test', ResourceRequirements: [ { @@ -1112,12 +1093,11 @@ describe('container definition', () => { Value: 'device1', }, ], - }, + }), ], }); - - }); + test('correctly adds resource requirements to container definition using both props and addInferenceAcceleratorResource method', () => { // GIVEN const stack = new cdk.Stack(); @@ -1146,7 +1126,7 @@ describe('container definition', () => { container.addInferenceAcceleratorResource('device2'); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { Family: 'Ec2TaskDef', InferenceAccelerators: [{ DeviceName: 'device1', @@ -1156,7 +1136,7 @@ describe('container definition', () => { DeviceType: 'eia2.large', }], ContainerDefinitions: [ - { + Match.objectLike({ Image: 'test', ResourceRequirements: [ { @@ -1168,11 +1148,11 @@ describe('container definition', () => { Value: 'device2', }, ], - }, + }), ], }); - }); + test('throws when the value of inference accelerator resource does not match any inference accelerators defined in the Task Definition', () => { // GIVEN const stack = new cdk.Stack(); @@ -1222,14 +1202,14 @@ describe('container definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { Family: 'Ec2TaskDef', InferenceAccelerators: [{ DeviceName: 'device1', DeviceType: 'eia2.medium', }], ContainerDefinitions: [ - { + Match.objectLike({ Image: 'test', ResourceRequirements: [{ Type: 'InferenceAccelerator', @@ -1238,7 +1218,7 @@ describe('container definition', () => { Type: 'GPU', Value: '2', }], - }, + }), ], }); }); @@ -1265,9 +1245,9 @@ describe('container definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Secrets: [ { Name: 'SECRET', @@ -1299,11 +1279,11 @@ describe('container definition', () => { }, }, ], - }, + }), ], }); - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -1349,8 +1329,6 @@ describe('container definition', () => { Version: '2012-10-17', }, }); - - }); test('use a specific secret JSON key as environment variable', () => { @@ -1370,9 +1348,9 @@ describe('container definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Secrets: [ { Name: 'SECRET_KEY', @@ -1389,11 +1367,9 @@ describe('container definition', () => { }, }, ], - }, + }), ], }); - - }); test('use a specific secret JSON field as environment variable for a Fargate task', () => { @@ -1413,9 +1389,9 @@ describe('container definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Secrets: [ { Name: 'SECRET_KEY', @@ -1432,11 +1408,9 @@ describe('container definition', () => { }, }, ], - }, + }), ], }); - - }); test('can add AWS logging to container definition', () => { @@ -1452,9 +1426,9 @@ describe('container definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ LogConfiguration: { LogDriver: 'awslogs', Options: { @@ -1463,11 +1437,11 @@ describe('container definition', () => { 'awslogs-region': { Ref: 'AWS::Region' }, }, }, - }, + }), ], }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -1479,8 +1453,6 @@ describe('container definition', () => { Version: '2012-10-17', }, }); - - }); test('can set Health Check with defaults', () => { @@ -1499,20 +1471,18 @@ describe('container definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ HealthCheck: { Command: ['CMD-SHELL', hcCommand], Interval: 30, Retries: 3, Timeout: 5, }, - }, + }), ], }); - - }); test('throws when setting Health Check with no commands', () => { @@ -1531,7 +1501,7 @@ describe('container definition', () => { // THEN expect(() => { - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { HealthCheck: { @@ -1567,9 +1537,9 @@ describe('container definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ HealthCheck: { Command: ['CMD-SHELL', hcCommand], Interval: 20, @@ -1577,11 +1547,9 @@ describe('container definition', () => { Timeout: 5, StartPeriod: 10, }, - }, + }), ], }); - - }); test('can specify Health Check values in array form starting with CMD-SHELL', () => { @@ -1603,9 +1571,9 @@ describe('container definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ HealthCheck: { Command: ['CMD-SHELL', hcCommand], Interval: 20, @@ -1613,11 +1581,9 @@ describe('container definition', () => { Timeout: 5, StartPeriod: 10, }, - }, + }), ], }); - - }); test('can specify Health Check values in array form starting with CMD', () => { @@ -1639,9 +1605,9 @@ describe('container definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ HealthCheck: { Command: ['CMD', hcCommand], Interval: 20, @@ -1649,11 +1615,9 @@ describe('container definition', () => { Timeout: 5, StartPeriod: 10, }, - }, + }), ], }); - - }); test('can specify private registry credentials', () => { @@ -1673,18 +1637,18 @@ describe('container definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Image: 'user-x/my-app', RepositoryCredentials: { CredentialsParameter: mySecretArn, }, - }, + }), ], }); - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -1698,8 +1662,6 @@ describe('container definition', () => { ], }, }); - - }); describe('_linkContainer works properly', () => { @@ -1756,18 +1718,16 @@ describe('container definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Image: 'test', LinuxParameters: { Capabilities: {}, }, - }, + }), ], }); - - }); test('before calling addContainer', () => { @@ -1791,9 +1751,9 @@ describe('container definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Image: 'test', LinuxParameters: { Capabilities: { @@ -1803,11 +1763,9 @@ describe('container definition', () => { InitProcessEnabled: true, SharedMemorySize: 1024, }, - }, + }), ], }); - - }); test('after calling addContainer', () => { @@ -1833,9 +1791,9 @@ describe('container definition', () => { linuxParameters.dropCapabilities(ecs.Capability.SETUID); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Image: 'test', LinuxParameters: { Capabilities: { @@ -1845,11 +1803,9 @@ describe('container definition', () => { InitProcessEnabled: true, SharedMemorySize: 1024, }, - }, + }), ], }); - - }); test('with one or more host devices', () => { @@ -1874,9 +1830,9 @@ describe('container definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Image: 'test', LinuxParameters: { Devices: [ @@ -1887,11 +1843,9 @@ describe('container definition', () => { InitProcessEnabled: true, SharedMemorySize: 1024, }, - }, + }), ], }); - - }); test('with the tmpfs mount for a container', () => { @@ -1917,9 +1871,9 @@ describe('container definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Image: 'test', LinuxParameters: { Tmpfs: [ @@ -1931,11 +1885,9 @@ describe('container definition', () => { InitProcessEnabled: true, SharedMemorySize: 1024, }, - }, + }), ], }); - - }); }); @@ -1954,7 +1906,7 @@ describe('container definition', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Essential: true, @@ -1976,7 +1928,7 @@ describe('container definition', () => { }, ], }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/cross-stack.test.ts b/packages/@aws-cdk/aws-ecs/test/ec2/cross-stack.test.ts index aebf98820d885..4ad3830fa7a9a 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/cross-stack.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/ec2/cross-stack.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as elbv2 from '@aws-cdk/aws-elasticloadbalancingv2'; import { App, Stack } from '@aws-cdk/core'; @@ -50,7 +50,7 @@ describe('cross stack', () => { }); // THEN: it shouldn't throw due to cyclic dependencies - expect(stack2).toHaveResource('AWS::ECS::Service'); + Template.fromStack(stack2).resourceCountIs('AWS::ECS::Service', 1); expectIngress(stack2); @@ -67,7 +67,7 @@ describe('cross stack', () => { }); // THEN: it shouldn't throw due to cyclic dependencies - expect(stack2).toHaveResource('AWS::ECS::Service'); + Template.fromStack(stack2).resourceCountIs('AWS::ECS::Service', 1); expectIngress(stack2); @@ -84,7 +84,7 @@ describe('cross stack', () => { }); // THEN: it shouldn't throw due to cyclic dependencies - expect(stack2).toHaveResource('AWS::ECS::Service'); + Template.fromStack(stack2).resourceCountIs('AWS::ECS::Service', 1); expectIngress(stack2); @@ -92,7 +92,7 @@ describe('cross stack', () => { }); function expectIngress(stack: Stack) { - expect(stack).toHaveResource('AWS::EC2::SecurityGroupIngress', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroupIngress', { FromPort: 32768, ToPort: 65535, GroupId: { 'Fn::ImportValue': 'Stack1:ExportsOutputFnGetAttDefaultAutoScalingGroupInstanceSecurityGroupFBA881D0GroupId2F7C804A' }, diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/ec2-service.test.ts b/packages/@aws-cdk/aws-ecs/test/ec2/ec2-service.test.ts index 7e1f5532fc207..85c5d68568b22 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/ec2-service.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/ec2/ec2-service.test.ts @@ -1,5 +1,4 @@ -import { SynthUtils } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as autoscaling from '@aws-cdk/aws-autoscaling'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as elb from '@aws-cdk/aws-elasticloadbalancing'; @@ -36,7 +35,7 @@ describe('ec2 service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { TaskDefinition: { Ref: 'Ec2TaskDef0226F28C', }, @@ -77,7 +76,7 @@ describe('ec2 service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { TaskDefinition: { Ref: 'Ec2TaskDef0226F28C', }, @@ -94,7 +93,7 @@ describe('ec2 service', () => { EnableExecuteCommand: true, }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -168,7 +167,7 @@ describe('ec2 service', () => { }); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -231,7 +230,7 @@ describe('ec2 service', () => { }); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -351,7 +350,7 @@ describe('ec2 service', () => { }); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -442,7 +441,7 @@ describe('ec2 service', () => { ], }); - expect(stack).toHaveResource('AWS::KMS::Key', { + Template.fromStack(stack).hasResourceProperties('AWS::KMS::Key', { KeyPolicy: { Statement: [ { @@ -557,7 +556,7 @@ describe('ec2 service', () => { }); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -663,7 +662,7 @@ describe('ec2 service', () => { ], }); - expect(stack).toHaveResource('AWS::KMS::Key', { + Template.fromStack(stack).hasResourceProperties('AWS::KMS::Key', { KeyPolicy: { Statement: [ { @@ -807,7 +806,7 @@ describe('ec2 service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ServiceDiscovery::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ServiceDiscovery::Service', { DnsConfig: { DnsRecords: [ { @@ -835,7 +834,7 @@ describe('ec2 service', () => { }, }); - expect(stack).toHaveResource('AWS::ServiceDiscovery::PrivateDnsNamespace', { + Template.fromStack(stack).hasResourceProperties('AWS::ServiceDiscovery::PrivateDnsNamespace', { Name: 'scorekeep.com', Vpc: { Ref: 'MyVpcF9F0CA6F', @@ -898,7 +897,7 @@ describe('ec2 service', () => { service.addPlacementStrategies(PlacementStrategy.spreadAcross(ecs.BuiltInAttributes.AVAILABILITY_ZONE)); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { TaskDefinition: { Ref: 'Ec2TaskDef0226F28C', }, @@ -1004,7 +1003,7 @@ describe('ec2 service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { CapacityProviderStrategy: [ { CapacityProvider: { @@ -1055,7 +1054,7 @@ describe('ec2 service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { TaskDefinition: { Ref: 'Ec2TaskDef0226F28C', }, @@ -1095,7 +1094,7 @@ describe('ec2 service', () => { ServiceName: 'bonjour', }); - expect(stack).toHaveResource('AWS::EC2::SecurityGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroup', { GroupDescription: 'Example', GroupName: 'Bingo', SecurityGroupEgress: [ @@ -1110,7 +1109,7 @@ describe('ec2 service', () => { }, }); - expect(stack).toHaveResource('AWS::EC2::SecurityGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroup', { GroupDescription: 'Example', GroupName: 'Rolly', SecurityGroupEgress: [ @@ -1223,7 +1222,7 @@ describe('ec2 service', () => { // THEN expect(service.node.metadataEntry[0].data).toEqual('taskDefinition and launchType are blanked out when using external deployment controller.'); - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { Cluster: { Ref: 'EcsCluster97242B84', }, @@ -1329,7 +1328,7 @@ describe('ec2 service', () => { // THEN expect(() => { - SynthUtils.synthesize(stack); + Template.fromStack(stack); }).toThrow(/one essential container/); @@ -1355,15 +1354,13 @@ describe('ec2 service', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Name: 'main', - }, + }), ], }); - - }); test('sets daemon scheduling strategy', () => { @@ -1386,7 +1383,7 @@ describe('ec2 service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { SchedulingStrategy: 'DAEMON', DeploymentConfiguration: { MaximumPercent: 100, @@ -1572,7 +1569,7 @@ describe('ec2 service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { NetworkConfiguration: { AwsvpcConfiguration: { AssignPublicIp: 'DISABLED', @@ -1647,7 +1644,7 @@ describe('ec2 service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { PlacementConstraints: [{ Type: 'distinctInstance', }], @@ -1677,7 +1674,7 @@ describe('ec2 service', () => { service.addPlacementConstraints(PlacementConstraint.memberOf('attribute:ecs.instance-type =~ t2.*')); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { PlacementConstraints: [{ Expression: 'attribute:ecs.instance-type =~ t2.*', Type: 'memberOf', @@ -1709,7 +1706,7 @@ describe('ec2 service', () => { service.addPlacementStrategies(PlacementStrategy.spreadAcrossInstances()); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { PlacementStrategies: [{ Field: 'instanceId', Type: 'spread', @@ -1740,7 +1737,7 @@ describe('ec2 service', () => { service.addPlacementStrategies(PlacementStrategy.spreadAcross(ecs.BuiltInAttributes.AVAILABILITY_ZONE)); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { PlacementStrategies: [{ Field: 'attribute:ecs.availability-zone', Type: 'spread', @@ -1841,11 +1838,9 @@ describe('ec2 service', () => { }); // THEN - expect(stack).not.toHaveResource('AWS::ECS::Service', { - PlacementConstraints: undefined, + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { + PlacementConstraints: Match.absent(), }); - - }); testDeprecated('with both propagateTags and propagateTaskTagsFrom defined', () => { @@ -1892,11 +1887,9 @@ describe('ec2 service', () => { }); // THEN - expect(stack).not.toHaveResource('AWS::ECS::Service', { - PlacementStrategies: undefined, + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { + PlacementStrategies: Match.absent(), }); - - }); test('with random placement strategy', () => { @@ -1920,7 +1913,7 @@ describe('ec2 service', () => { service.addPlacementStrategies(PlacementStrategy.randomly()); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { PlacementStrategies: [{ Type: 'random', }], @@ -1977,7 +1970,7 @@ describe('ec2 service', () => { service.addPlacementStrategies(PlacementStrategy.packedByCpu()); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { PlacementStrategies: [{ Field: 'cpu', Type: 'binpack', @@ -2008,7 +2001,7 @@ describe('ec2 service', () => { service.addPlacementStrategies(PlacementStrategy.packedByMemory()); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { PlacementStrategies: [{ Field: 'memory', Type: 'binpack', @@ -2039,7 +2032,7 @@ describe('ec2 service', () => { service.addPlacementStrategies(PlacementStrategy.packedBy(ecs.BinPackResource.MEMORY)); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { PlacementStrategies: [{ Field: 'memory', Type: 'binpack', @@ -2272,13 +2265,13 @@ describe('ec2 service', () => { }); // THEN - expect(stack).toHaveResource('AWS::EC2::SecurityGroupIngress', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroupIngress', { Description: 'Load balancer to target', FromPort: 32768, ToPort: 65535, }); - expect(stack).toHaveResource('AWS::EC2::SecurityGroupEgress', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroupEgress', { Description: 'Load balancer to target', FromPort: 32768, ToPort: 65535, @@ -2320,13 +2313,13 @@ describe('ec2 service', () => { }); // THEN - expect(stack).toHaveResource('AWS::EC2::SecurityGroupIngress', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroupIngress', { Description: 'Load balancer to target', FromPort: 80, ToPort: 80, }); - expect(stack).toHaveResource('AWS::EC2::SecurityGroupEgress', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroupEgress', { Description: 'Load balancer to target', FromPort: 80, ToPort: 80, @@ -2367,13 +2360,13 @@ describe('ec2 service', () => { }); // THEN - expect(stack).toHaveResource('AWS::EC2::SecurityGroupIngress', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroupIngress', { Description: 'Load balancer to target', FromPort: 8001, ToPort: 8001, }); - expect(stack).toHaveResource('AWS::EC2::SecurityGroupEgress', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroupEgress', { Description: 'Load balancer to target', FromPort: 8001, ToPort: 8001, @@ -2413,13 +2406,13 @@ describe('ec2 service', () => { }); // THEN - expect(stack).toHaveResource('AWS::EC2::SecurityGroupIngress', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroupIngress', { Description: 'Load balancer to target', FromPort: 8001, ToPort: 8001, }); - expect(stack).toHaveResource('AWS::EC2::SecurityGroupEgress', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroupEgress', { Description: 'Load balancer to target', FromPort: 8001, ToPort: 8001, @@ -2513,7 +2506,7 @@ describe('ec2 service', () => { lb.addTarget(service); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { LoadBalancers: [ { ContainerName: 'web', @@ -2523,7 +2516,7 @@ describe('ec2 service', () => { ], }); - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { // if any load balancer is configured and healthCheckGracePeriodSeconds is not // set, then it should default to 60 seconds. HealthCheckGracePeriodSeconds: 60, @@ -2558,7 +2551,7 @@ describe('ec2 service', () => { })); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { LoadBalancers: [ { ContainerName: 'web', @@ -2663,7 +2656,7 @@ describe('ec2 service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { ServiceRegistries: [ { ContainerName: 'MainContainer', @@ -2678,7 +2671,7 @@ describe('ec2 service', () => { ], }); - expect(stack).toHaveResource('AWS::ServiceDiscovery::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ServiceDiscovery::Service', { DnsConfig: { DnsRecords: [ { @@ -2740,7 +2733,7 @@ describe('ec2 service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { ServiceRegistries: [ { ContainerName: 'MainContainer', @@ -2755,7 +2748,7 @@ describe('ec2 service', () => { ], }); - expect(stack).toHaveResource('AWS::ServiceDiscovery::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ServiceDiscovery::Service', { DnsConfig: { DnsRecords: [ { @@ -2851,7 +2844,7 @@ describe('ec2 service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { ServiceRegistries: [ { RegistryArn: { @@ -2864,7 +2857,7 @@ describe('ec2 service', () => { ], }); - expect(stack).toHaveResource('AWS::ServiceDiscovery::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ServiceDiscovery::Service', { DnsConfig: { DnsRecords: [ { @@ -2927,7 +2920,7 @@ describe('ec2 service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { ServiceRegistries: [ { ContainerName: 'MainContainer', @@ -2942,7 +2935,7 @@ describe('ec2 service', () => { ], }); - expect(stack).toHaveResource('AWS::ServiceDiscovery::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ServiceDiscovery::Service', { DnsConfig: { DnsRecords: [ { @@ -3011,7 +3004,7 @@ describe('ec2 service', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { ServiceRegistries: [ { RegistryArn: { 'Fn::GetAtt': ['ServiceCloudmapService046058A4', 'Arn'] }, @@ -3020,8 +3013,6 @@ describe('ec2 service', () => { }, ], }); - - }); test('By default, the container name is the default', () => { @@ -3056,14 +3047,12 @@ describe('ec2 service', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::Service', { - ServiceRegistries: [{ + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { + ServiceRegistries: [Match.objectLike({ ContainerName: 'main', - ContainerPort: undefined, - }], + ContainerPort: Match.anyValue(), + })], }); - - }); test('For SRV, by default, container name is default container and port is the default container port', () => { @@ -3100,14 +3089,12 @@ describe('ec2 service', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::Service', { - ServiceRegistries: [{ + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { + ServiceRegistries: [Match.objectLike({ ContainerName: 'main', ContainerPort: 1234, - }], + })], }); - - }); test('allows SRV service discovery to select the container and port', () => { @@ -3147,14 +3134,12 @@ describe('ec2 service', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::Service', { - ServiceRegistries: [{ + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { + ServiceRegistries: [Match.objectLike({ ContainerName: 'second', ContainerPort: 4321, - }], + })], }); - - }); test('throws if SRV and container is not part of task definition', () => { 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 9217e68a412c5..27e10173bbfec 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 @@ -1,5 +1,5 @@ -import '@aws-cdk/assert-internal/jest'; import * as path from 'path'; +import { Match, Template } from '@aws-cdk/assertions'; import { Protocol } from '@aws-cdk/aws-ec2'; import { Repository } from '@aws-cdk/aws-ecr'; import * as iam from '@aws-cdk/aws-iam'; @@ -18,14 +18,13 @@ describe('ec2 task definition', () => { new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef'); // THEN - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { Family: 'Ec2TaskDef', NetworkMode: ecs.NetworkMode.BRIDGE, RequiresCompatibilities: ['EC2'], }); // test error if no container defs? - }); test('with all properties set', () => { @@ -56,7 +55,7 @@ describe('ec2 task definition', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ExecutionRoleArn: { 'Fn::GetAtt': [ 'ExecutionRole605A040B', @@ -104,7 +103,7 @@ describe('ec2 task definition', () => { taskDefinition.addPlacementConstraint(ecs.PlacementConstraint.memberOf('attribute:ecs.instance-type =~ t2.*')); // THEN - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { PlacementConstraints: [ { Expression: 'attribute:ecs.instance-type =~ t2.*', @@ -125,7 +124,7 @@ describe('ec2 task definition', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { NetworkMode: ecs.NetworkMode.AWS_VPC, }); @@ -140,7 +139,7 @@ describe('ec2 task definition', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { IpcMode: ecs.IpcMode.TASK, }); @@ -155,7 +154,7 @@ describe('ec2 task definition', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { PidMode: ecs.PidMode.HOST, }); @@ -194,7 +193,7 @@ describe('ec2 task definition', () => { })); // THEN - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { Family: 'Ec2TaskDef', ContainerDefinitions: [{ Essential: true, @@ -222,7 +221,7 @@ describe('ec2 task definition', () => { }], }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Version: '2012-10-17', Statement: [ @@ -288,7 +287,7 @@ describe('ec2 task definition', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { Family: 'Ec2TaskDef', ContainerDefinitions: [ { @@ -461,7 +460,7 @@ describe('ec2 task definition', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECR::Repository', { + Template.fromStack(stack).hasResourceProperties('AWS::ECR::Repository', { LifecyclePolicy: { // eslint-disable-next-line max-len LifecyclePolicyText: '{"rules":[{"rulePriority":10,"selection":{"tagStatus":"tagged","tagPrefixList":["abc"],"countType":"imageCountMoreThan","countNumber":1},"action":{"type":"expire"}}]}', @@ -470,7 +469,7 @@ describe('ec2 task definition', () => { RepositoryName: 'project-a/amazon-ecs-sample', }); - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { Family: 'Ec2TaskDef', ContainerDefinitions: [{ Essential: true, @@ -543,7 +542,7 @@ describe('ec2 task definition', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [{ Essential: true, Memory: 512, @@ -613,7 +612,7 @@ describe('ec2 task definition', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [{ Essential: true, Memory: 512, @@ -684,7 +683,7 @@ describe('ec2 task definition', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECR::Repository', {}); + Template.fromStack(stack).hasResourceProperties('AWS::ECR::Repository', {}); }); @@ -739,7 +738,7 @@ describe('ec2 task definition', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { Family: 'StackEc2TaskDefF03698CF', ContainerDefinitions: [ { @@ -806,9 +805,9 @@ describe('ec2 task definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { Family: 'Ec2TaskDef', - ContainerDefinitions: [{ + ContainerDefinitions: [Match.objectLike({ MountPoints: [ { ContainerPath: './cache', @@ -816,7 +815,7 @@ describe('ec2 task definition', () => { SourceVolume: 'scratch', }, ], - }], + })], Volumes: [{ Host: { SourcePath: '/tmp/cache', @@ -824,9 +823,8 @@ describe('ec2 task definition', () => { Name: 'scratch', }], }); - - }); + test('correctly sets container dependenices', () => { // GIVEN const stack = new cdk.Stack(); @@ -857,15 +855,15 @@ describe('ec2 task definition', () => { ); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { Family: 'Ec2TaskDef', - ContainerDefinitions: [{ + ContainerDefinitions: [Match.objectLike({ Name: 'dependency1', - }, - { + }), + Match.objectLike({ Name: 'dependency2', - }, - { + }), + Match.objectLike({ Name: 'web', DependsOn: [{ Condition: 'HEALTHY', @@ -875,7 +873,7 @@ describe('ec2 task definition', () => { Condition: 'SUCCESS', ContainerName: 'dependency2', }], - }], + })], }); @@ -906,25 +904,23 @@ describe('ec2 task definition', () => { container.addLink(linkedContainer2); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Links: [ 'linked1:linked', 'linked2', ], Name: 'web', - }, - { + }), + Match.objectLike({ Name: 'linked1', - }, - { + }), + Match.objectLike({ Name: 'linked2', - }, + }), ], }); - - }); test('correctly set policy statement to the task IAM role', () => { @@ -939,7 +935,7 @@ describe('ec2 task definition', () => { })); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Version: '2012-10-17', Statement: [ @@ -951,9 +947,8 @@ describe('ec2 task definition', () => { ], }, }); - - }); + test('correctly sets volumes from', () => { const stack = new cdk.Stack(); @@ -970,18 +965,16 @@ describe('ec2 task definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { - ContainerDefinitions: [{ + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { + ContainerDefinitions: [Match.objectLike({ VolumesFrom: [ { SourceContainer: 'SourceContainer', ReadOnly: true, }, ], - }], + })], }); - - }); test('correctly set policy statement to the task execution IAM role', () => { @@ -996,7 +989,7 @@ describe('ec2 task definition', () => { })); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Version: '2012-10-17', Statement: [ @@ -1040,9 +1033,9 @@ describe('ec2 task definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { Family: 'Ec2TaskDef', - ContainerDefinitions: [{ + ContainerDefinitions: [Match.objectLike({ MountPoints: [ { ContainerPath: './cache', @@ -1050,7 +1043,7 @@ describe('ec2 task definition', () => { SourceVolume: 'scratch', }, ], - }], + })], Volumes: [{ Host: { SourcePath: '/tmp/cache', @@ -1058,8 +1051,6 @@ describe('ec2 task definition', () => { Name: 'scratch', }], }); - - }); test('correctly sets placement constraints', () => { @@ -1077,7 +1068,7 @@ describe('ec2 task definition', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { PlacementConstraints: [ { Expression: 'attribute:ecs.instance-type =~ t2.*', @@ -1104,7 +1095,7 @@ describe('ec2 task definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { TaskRoleArn: stack.resolve(taskDefinition.taskRole.roleArn), }); @@ -1117,7 +1108,7 @@ describe('ec2 task definition', () => { const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef'); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { TaskRoleArn: stack.resolve(taskDefinition.taskRole.roleArn), }); @@ -1148,7 +1139,7 @@ describe('ec2 task definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { Family: 'Ec2TaskDef', Volumes: [{ Name: 'scratch', @@ -1185,7 +1176,7 @@ describe('ec2 task definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { Family: 'Ec2TaskDef', Volumes: [{ Name: 'scratch', @@ -1219,7 +1210,7 @@ describe('ec2 task definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { Family: 'Ec2TaskDef', InferenceAccelerators: [{ DeviceName: 'device1', @@ -1252,7 +1243,7 @@ describe('ec2 task definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { Family: 'Ec2TaskDef', InferenceAccelerators: [{ DeviceName: 'device1', diff --git a/packages/@aws-cdk/aws-ecs/test/environment-file.test.ts b/packages/@aws-cdk/aws-ecs/test/environment-file.test.ts index 815973a80e602..abc64332a326b 100644 --- a/packages/@aws-cdk/aws-ecs/test/environment-file.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/environment-file.test.ts @@ -1,4 +1,3 @@ -import '@aws-cdk/assert-internal/jest'; import * as path from 'path'; import * as cdk from '@aws-cdk/core'; import * as ecs from '../lib'; diff --git a/packages/@aws-cdk/aws-ecs/test/external/external-service.test.ts b/packages/@aws-cdk/aws-ecs/test/external/external-service.test.ts index b4a3edf73ea0d..69626f7473fb0 100644 --- a/packages/@aws-cdk/aws-ecs/test/external/external-service.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/external/external-service.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as autoscaling from '@aws-cdk/aws-autoscaling'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as elbv2 from '@aws-cdk/aws-elasticloadbalancingv2'; @@ -29,7 +29,7 @@ describe('external service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { TaskDefinition: { Ref: 'ExternalTaskDef6CCBDB87', }, @@ -79,7 +79,7 @@ describe('external service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { TaskDefinition: { Ref: 'ExternalTaskDef6CCBDB87', }, @@ -165,7 +165,7 @@ describe('external service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { TaskDefinition: { Ref: 'ExternalTaskDef6CCBDB87', }, @@ -177,7 +177,7 @@ describe('external service', () => { ServiceName: 'bonjour', }); - expect(stack).toHaveResource('AWS::EC2::SecurityGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroup', { GroupDescription: 'Example', GroupName: 'Bingo', SecurityGroupEgress: [ @@ -189,7 +189,7 @@ describe('external service', () => { ], }); - expect(stack).toHaveResource('AWS::EC2::SecurityGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroup', { GroupDescription: 'Example', GroupName: 'Rolly', SecurityGroupEgress: [ diff --git a/packages/@aws-cdk/aws-ecs/test/external/external-task-definition.test.ts b/packages/@aws-cdk/aws-ecs/test/external/external-task-definition.test.ts index 3b692f73fe8cf..7963ea0cfa6a1 100644 --- a/packages/@aws-cdk/aws-ecs/test/external/external-task-definition.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/external/external-task-definition.test.ts @@ -1,5 +1,5 @@ -import '@aws-cdk/assert-internal/jest'; import * as path from 'path'; +import { Template } from '@aws-cdk/assertions'; import { Protocol } from '@aws-cdk/aws-ec2'; import { Repository } from '@aws-cdk/aws-ecr'; import * as iam from '@aws-cdk/aws-iam'; @@ -16,13 +16,11 @@ describe('external task definition', () => { new ecs.ExternalTaskDefinition(stack, 'ExternalTaskDef'); // THEN - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { Family: 'ExternalTaskDef', NetworkMode: ecs.NetworkMode.BRIDGE, RequiresCompatibilities: ['EXTERNAL'], }); - - }); test('with all properties set', () => { @@ -43,7 +41,7 @@ describe('external task definition', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ExecutionRoleArn: { 'Fn::GetAtt': [ 'ExecutionRole605A040B', @@ -93,7 +91,7 @@ describe('external task definition', () => { })); // THEN - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { Family: 'ExternalTaskDef', NetworkMode: ecs.NetworkMode.BRIDGE, RequiresCompatibilities: ['EXTERNAL'], @@ -117,7 +115,7 @@ describe('external task definition', () => { }], }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Version: '2012-10-17', Statement: [ @@ -180,7 +178,7 @@ describe('external task definition', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { Family: 'ExternalTaskDef', NetworkMode: ecs.NetworkMode.BRIDGE, RequiresCompatibilities: ['EXTERNAL'], @@ -353,7 +351,7 @@ describe('external task definition', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECR::Repository', { + Template.fromStack(stack).hasResourceProperties('AWS::ECR::Repository', { LifecyclePolicy: { // eslint-disable-next-line max-len LifecyclePolicyText: '{"rules":[{"rulePriority":10,"selection":{"tagStatus":"tagged","tagPrefixList":["abc"],"countType":"imageCountMoreThan","countNumber":1},"action":{"type":"expire"}}]}', @@ -362,7 +360,7 @@ describe('external task definition', () => { RepositoryName: 'project-a/amazon-ecs-sample', }); - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { Family: 'ExternalTaskDef', NetworkMode: ecs.NetworkMode.BRIDGE, RequiresCompatibilities: ['EXTERNAL'], @@ -437,7 +435,7 @@ describe('external task definition', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { Family: 'ExternalTaskDef', NetworkMode: ecs.NetworkMode.BRIDGE, RequiresCompatibilities: ['EXTERNAL'], @@ -510,7 +508,7 @@ describe('external task definition', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { Family: 'ExternalTaskDef', NetworkMode: ecs.NetworkMode.BRIDGE, RequiresCompatibilities: ['EXTERNAL'], @@ -585,7 +583,7 @@ describe('external task definition', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECR::Repository', {}); + Template.fromStack(stack).hasResourceProperties('AWS::ECR::Repository', {}); }); diff --git a/packages/@aws-cdk/aws-ecs/test/fargate/fargate-service.test.ts b/packages/@aws-cdk/aws-ecs/test/fargate/fargate-service.test.ts index a8dbb29c98b10..3780c43903284 100644 --- a/packages/@aws-cdk/aws-ecs/test/fargate/fargate-service.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/fargate/fargate-service.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { ABSENT, SynthUtils } from '@aws-cdk/assert-internal'; +import { Match, Template } from '@aws-cdk/assertions'; import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import * as ec2 from '@aws-cdk/aws-ec2'; @@ -34,7 +33,7 @@ describe('fargate service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { TaskDefinition: { Ref: 'FargateTaskDefC6FB60B4', }, @@ -70,7 +69,7 @@ describe('fargate service', () => { }, }); - expect(stack).toHaveResource('AWS::EC2::SecurityGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroup', { GroupDescription: 'Default/FargateService/SecurityGroup', SecurityGroupEgress: [ { @@ -149,15 +148,15 @@ describe('fargate service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Cluster', { - CapacityProviders: ABSENT, + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Cluster', { + CapacityProviders: Match.absent(), }); - expect(stack).toHaveResource('AWS::ECS::ClusterCapacityProviderAssociations', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::ClusterCapacityProviderAssociations', { CapacityProviders: ['FARGATE', 'FARGATE_SPOT'], }); - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { TaskDefinition: { Ref: 'FargateTaskDefC6FB60B4', }, @@ -239,15 +238,15 @@ describe('fargate service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Cluster', { - CapacityProviders: ABSENT, + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Cluster', { + CapacityProviders: Match.absent(), }); - expect(stack).toHaveResource('AWS::ECS::ClusterCapacityProviderAssociations', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::ClusterCapacityProviderAssociations', { CapacityProviders: ['FARGATE', 'FARGATE_SPOT'], }); - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { TaskDefinition: { Ref: 'FargateTaskDefC6FB60B4', }, @@ -259,7 +258,7 @@ describe('fargate service', () => { MinimumHealthyPercent: 50, }, // no launch type - LaunchType: ABSENT, + LaunchType: Match.absent(), CapacityProviderStrategy: [ { CapacityProvider: 'FARGATE_SPOT', @@ -326,7 +325,7 @@ describe('fargate service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ServiceDiscovery::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ServiceDiscovery::Service', { DnsConfig: { DnsRecords: [ { @@ -354,7 +353,7 @@ describe('fargate service', () => { }, }); - expect(stack).toHaveResource('AWS::ServiceDiscovery::PrivateDnsNamespace', { + Template.fromStack(stack).hasResourceProperties('AWS::ServiceDiscovery::PrivateDnsNamespace', { Name: 'scorekeep.com', Vpc: { Ref: 'MyVpcF9F0CA6F', @@ -401,7 +400,7 @@ describe('fargate service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { ServiceRegistries: [ { ContainerName: 'web', @@ -507,7 +506,7 @@ describe('fargate service', () => { // THEN expect(svc.cloudMapService).toBeDefined(); - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { TaskDefinition: { Ref: 'FargateTaskDefC6FB60B4', }, @@ -634,7 +633,7 @@ describe('fargate service', () => { // THEN expect(service.node.metadataEntry[0].data).toEqual('taskDefinition and launchType are blanked out when using external deployment controller.'); - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { Cluster: { Ref: 'EcsCluster97242B84', }, @@ -687,7 +686,7 @@ describe('fargate service', () => { // THEN expect(() => { - SynthUtils.synthesize(stack); + Template.fromStack(stack); }).toThrow(/one essential container/); @@ -711,15 +710,13 @@ describe('fargate service', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Name: 'main', - }, + }), ], }); - - }); test('allows specifying assignPublicIP as enabled', () => { @@ -740,7 +737,7 @@ describe('fargate service', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { NetworkConfiguration: { AwsvpcConfiguration: { AssignPublicIp: 'ENABLED', @@ -769,7 +766,7 @@ describe('fargate service', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DeploymentConfiguration: { MinimumHealthyPercent: 0, }, @@ -844,7 +841,7 @@ describe('fargate service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { TaskDefinition: { Ref: 'FargateTaskDefC6FB60B4', }, @@ -886,7 +883,7 @@ describe('fargate service', () => { }, }); - expect(stack).toHaveResource('AWS::EC2::SecurityGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroup', { GroupDescription: 'Example', GroupName: 'Bingo', SecurityGroupEgress: [ @@ -901,7 +898,7 @@ describe('fargate service', () => { }, }); - expect(stack).toHaveResource('AWS::EC2::SecurityGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroup', { GroupDescription: 'Example', GroupName: 'Rolly', SecurityGroupEgress: [ @@ -942,7 +939,7 @@ describe('fargate service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { HealthCheckGracePeriodSeconds: 10, }); @@ -978,7 +975,7 @@ describe('fargate service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApplicationAutoScaling::ScalableTarget', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalableTarget', { MaxCapacity: 10, MinCapacity: 1, ResourceId: { @@ -1001,7 +998,7 @@ describe('fargate service', () => { }, }); - expect(stack).toHaveResource('AWS::ApplicationAutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalingPolicy', { TargetTrackingScalingPolicyConfiguration: { PredefinedMetricSpecification: { PredefinedMetricType: 'ALBRequestCountPerTarget', @@ -1018,7 +1015,7 @@ describe('fargate service', () => { }, }); - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { // if any load balancer is configured and healthCheckGracePeriodSeconds is not // set, then it should default to 60 seconds. HealthCheckGracePeriodSeconds: 60, @@ -1058,7 +1055,7 @@ describe('fargate service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApplicationAutoScaling::ScalableTarget', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalableTarget', { MaxCapacity: 10, MinCapacity: 1, ResourceId: { @@ -1113,7 +1110,7 @@ describe('fargate service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { LoadBalancers: [ { ContainerName: 'MainContainer', @@ -1125,13 +1122,13 @@ describe('fargate service', () => { ], }); - expect(stack).toHaveResource('AWS::EC2::SecurityGroupIngress', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroupIngress', { Description: 'Load balancer to target', FromPort: 8000, ToPort: 8000, }); - expect(stack).toHaveResource('AWS::EC2::SecurityGroupEgress', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroupEgress', { Description: 'Load balancer to target', FromPort: 8000, ToPort: 8000, @@ -1347,7 +1344,7 @@ describe('fargate service', () => { ); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { LoadBalancers: [ { ContainerName: 'MainContainer', @@ -1359,7 +1356,7 @@ describe('fargate service', () => { ], }); - expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::TargetGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::TargetGroup', { Port: 80, Protocol: 'HTTP', }); @@ -1399,7 +1396,7 @@ describe('fargate service', () => { ); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { LoadBalancers: [ { ContainerName: 'MainContainer', @@ -1411,7 +1408,7 @@ describe('fargate service', () => { ], }); - expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::TargetGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::TargetGroup', { Port: 80, Protocol: 'HTTP', }); @@ -1451,7 +1448,7 @@ describe('fargate service', () => { ); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { LoadBalancers: [ { ContainerName: 'MainContainer', @@ -1463,7 +1460,7 @@ describe('fargate service', () => { ], }); - expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::TargetGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::TargetGroup', { Port: 443, Protocol: 'HTTPS', }); @@ -1504,7 +1501,7 @@ describe('fargate service', () => { ); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { LoadBalancers: [ { ContainerName: 'MainContainer', @@ -1516,7 +1513,7 @@ describe('fargate service', () => { ], }); - expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::TargetGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::TargetGroup', { Port: 83, Protocol: 'HTTP', }); @@ -1556,7 +1553,7 @@ describe('fargate service', () => { ); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { LoadBalancers: [ { ContainerName: 'MainContainer', @@ -1568,7 +1565,7 @@ describe('fargate service', () => { ], }); - expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::TargetGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::TargetGroup', { Port: 80, Protocol: 'TCP', }); @@ -1608,7 +1605,7 @@ describe('fargate service', () => { ); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { LoadBalancers: [ { ContainerName: 'MainContainer', @@ -1620,7 +1617,7 @@ describe('fargate service', () => { ], }); - expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::TargetGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::TargetGroup', { Port: 81, Protocol: 'TCP', }); @@ -1655,7 +1652,7 @@ describe('fargate service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApplicationAutoScaling::ScalableTarget', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalableTarget', { ScheduledActions: [ { ScalableTargetAction: { @@ -1698,7 +1695,7 @@ describe('fargate service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApplicationAutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalingPolicy', { PolicyType: 'StepScaling', ScalingTargetId: { Ref: 'ServiceTaskCountTarget23E25614', @@ -1741,7 +1738,7 @@ describe('fargate service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApplicationAutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalingPolicy', { PolicyType: 'TargetTrackingScaling', TargetTrackingScalingPolicyConfiguration: { PredefinedMetricSpecification: { PredefinedMetricType: 'ECSServiceAverageCPUUtilization' }, @@ -1775,7 +1772,7 @@ describe('fargate service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApplicationAutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalingPolicy', { PolicyType: 'TargetTrackingScaling', TargetTrackingScalingPolicyConfiguration: { PredefinedMetricSpecification: { PredefinedMetricType: 'ECSServiceAverageMemoryUtilization' }, @@ -1810,7 +1807,7 @@ describe('fargate service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ApplicationAutoScaling::ScalingPolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalingPolicy', { PolicyType: 'TargetTrackingScaling', TargetTrackingScalingPolicyConfiguration: { CustomizedMetricSpecification: { @@ -1878,7 +1875,7 @@ describe('fargate service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ServiceDiscovery::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ServiceDiscovery::Service', { DnsConfig: { DnsRecords: [ { @@ -1939,7 +1936,7 @@ describe('fargate service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ServiceDiscovery::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ServiceDiscovery::Service', { DnsConfig: { DnsRecords: [ { @@ -2001,7 +1998,7 @@ describe('fargate service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ServiceDiscovery::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ServiceDiscovery::Service', { DnsConfig: { DnsRecords: [ { @@ -2065,7 +2062,7 @@ describe('fargate service', () => { }, }); - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { ServiceRegistries: [ { RegistryArn: { 'Fn::GetAtt': ['ServiceCloudmapService046058A4', 'Arn'] }, @@ -2168,7 +2165,7 @@ describe('fargate service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DeploymentConfiguration: { MaximumPercent: 200, MinimumHealthyPercent: 50, @@ -2233,7 +2230,7 @@ describe('fargate service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { TaskDefinition: { Ref: 'FargateTaskDefC6FB60B4', }, @@ -2270,7 +2267,7 @@ describe('fargate service', () => { }, }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -2344,7 +2341,7 @@ describe('fargate service', () => { }); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -2407,7 +2404,7 @@ describe('fargate service', () => { }); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -2527,7 +2524,7 @@ describe('fargate service', () => { }); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -2618,7 +2615,7 @@ describe('fargate service', () => { ], }); - expect(stack).toHaveResource('AWS::KMS::Key', { + Template.fromStack(stack).hasResourceProperties('AWS::KMS::Key', { KeyPolicy: { Statement: [ { @@ -2733,7 +2730,7 @@ describe('fargate service', () => { }); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -2839,7 +2836,7 @@ describe('fargate service', () => { ], }); - expect(stack).toHaveResource('AWS::KMS::Key', { + Template.fromStack(stack).hasResourceProperties('AWS::KMS::Key', { KeyPolicy: { Statement: [ { diff --git a/packages/@aws-cdk/aws-ecs/test/fargate/fargate-task-definition.test.ts b/packages/@aws-cdk/aws-ecs/test/fargate/fargate-task-definition.test.ts index 1f42e09cf83ac..e0b7afca3389f 100644 --- a/packages/@aws-cdk/aws-ecs/test/fargate/fargate-task-definition.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/fargate/fargate-task-definition.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; import * as ecs from '../../lib'; @@ -11,7 +11,7 @@ describe('fargate task definition', () => { new ecs.FargateTaskDefinition(stack, 'FargateTaskDef'); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { Family: 'FargateTaskDef', NetworkMode: ecs.NetworkMode.AWS_VPC, RequiresCompatibilities: ['FARGATE'], @@ -32,7 +32,7 @@ describe('fargate task definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { Cpu: '128', Memory: '1024', }); @@ -72,7 +72,7 @@ describe('fargate task definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { Cpu: '128', ExecutionRoleArn: { 'Fn::GetAtt': [ @@ -265,7 +265,7 @@ describe('fargate task definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { Cpu: '1024', Family: 'FargateTaskDef', Memory: '2048', @@ -299,7 +299,7 @@ describe('fargate task definition', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { Cpu: '1024', Family: 'FargateTaskDef', Memory: '2048', diff --git a/packages/@aws-cdk/aws-ecs/test/firelens-log-driver.test.ts b/packages/@aws-cdk/aws-ecs/test/firelens-log-driver.test.ts index 6ab96b05df364..547382ab8d3de 100644 --- a/packages/@aws-cdk/aws-ecs/test/firelens-log-driver.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/firelens-log-driver.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as secretsmanager from '@aws-cdk/aws-secretsmanager'; import * as ssm from '@aws-cdk/aws-ssm'; import * as cdk from '@aws-cdk/core'; @@ -24,23 +24,21 @@ describe('firelens log driver', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ LogConfiguration: { LogDriver: 'awsfirelens', }, - }, - { + }), + Match.objectLike({ Essential: true, FirelensConfiguration: { Type: 'fluentbit', }, - }, + }), ], }); - - }); test('create a firelens log driver with secret options', () => { @@ -71,9 +69,9 @@ describe('firelens log driver', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ LogConfiguration: { LogDriver: 'awsfirelens', Options: { @@ -116,19 +114,19 @@ describe('firelens log driver', () => { }, ], }, - }, - { + }), + Match.objectLike({ Essential: true, FirelensConfiguration: { Type: 'fluentbit', }, - }, + }), ], }); - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { - Statement: [ + Statement: Match.arrayWith([ { Action: [ 'secretsmanager:GetSecretValue', @@ -168,12 +166,10 @@ describe('firelens log driver', () => { ], }, }, - ], + ]), Version: '2012-10-17', }, }); - - }); test('create a firelens log driver to route logs to CloudWatch Logs with Fluent Bit', () => { @@ -193,9 +189,9 @@ describe('firelens log driver', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ LogConfiguration: { LogDriver: 'awsfirelens', Options: { @@ -206,17 +202,15 @@ describe('firelens log driver', () => { log_stream_prefix: 'from-fluent-bit', }, }, - }, - { + }), + Match.objectLike({ Essential: true, FirelensConfiguration: { Type: 'fluentbit', }, - }, + }), ], }); - - }); test('create a firelens log driver to route logs to kinesis firehose Logs with Fluent Bit', () => { @@ -234,9 +228,9 @@ describe('firelens log driver', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ LogConfiguration: { LogDriver: 'awsfirelens', Options: { @@ -245,17 +239,15 @@ describe('firelens log driver', () => { delivery_stream: 'my-stream', }, }, - }, - { + }), + Match.objectLike({ Essential: true, FirelensConfiguration: { Type: 'fluentbit', }, - }, + }), ], }); - - }); describe('Firelens Configuration', () => { @@ -270,7 +262,7 @@ describe('firelens log driver', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Essential: true, @@ -283,7 +275,6 @@ describe('firelens log driver', () => { }, ], }); - }); test('fluent-bit log router container with options', () => { @@ -304,9 +295,9 @@ describe('firelens log driver', () => { }); // THEN - expect(stack2).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack2).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Essential: true, MemoryReservation: 50, Name: 'log_router', @@ -318,11 +309,9 @@ describe('firelens log driver', () => { 'config-file-value': 'arn:aws:s3:::mybucket/fluent.conf', }, }, - }, + }), ], }); - - }); test('fluent-bit log router with file config type', () => { @@ -342,9 +331,9 @@ describe('firelens log driver', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Essential: true, MemoryReservation: 50, Name: 'log_router', @@ -356,11 +345,9 @@ describe('firelens log driver', () => { 'config-file-value': '/my/working/dir/firelens/config', }, }, - }, + }), ], }); - - }); }); }); diff --git a/packages/@aws-cdk/aws-ecs/test/fluentd-log-driver.test.ts b/packages/@aws-cdk/aws-ecs/test/fluentd-log-driver.test.ts index 81c17b8c0b76f..7bd81fed41221 100644 --- a/packages/@aws-cdk/aws-ecs/test/fluentd-log-driver.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/fluentd-log-driver.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as cdk from '@aws-cdk/core'; import * as ecs from '../lib'; @@ -10,8 +10,6 @@ describe('fluentd log driver', () => { beforeEach(() => { stack = new cdk.Stack(); td = new ecs.Ec2TaskDefinition(stack, 'TaskDefinition'); - - }); test('create a fluentd log driver with options', () => { @@ -25,20 +23,18 @@ describe('fluentd log driver', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ LogConfiguration: { LogDriver: 'fluentd', Options: { tag: 'hello', }, }, - }, + }), ], }); - - }); test('create a fluentd log driver without options', () => { @@ -50,17 +46,15 @@ describe('fluentd log driver', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ LogConfiguration: { LogDriver: 'fluentd', }, - }, + }), ], }); - - }); test('create a fluentd log driver with all possible options', () => { @@ -91,9 +85,9 @@ describe('fluentd log driver', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ LogConfiguration: { LogDriver: 'fluentd', Options: { @@ -109,11 +103,9 @@ describe('fluentd log driver', () => { 'env-regex': '[0-9]{1}', }, }, - }, + }), ], }); - - }); test('create a fluentd log driver using fluentd', () => { @@ -125,16 +117,14 @@ describe('fluentd log driver', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ LogConfiguration: { LogDriver: 'fluentd', }, - }, + }), ], }); - - }); }); diff --git a/packages/@aws-cdk/aws-ecs/test/gelf-log-driver.test.ts b/packages/@aws-cdk/aws-ecs/test/gelf-log-driver.test.ts index e8cdce10736e1..f6800bb48ac01 100644 --- a/packages/@aws-cdk/aws-ecs/test/gelf-log-driver.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/gelf-log-driver.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as cdk from '@aws-cdk/core'; import * as ecs from '../lib'; @@ -25,20 +25,18 @@ describe('gelf log driver', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ LogConfiguration: { LogDriver: 'gelf', Options: { 'gelf-address': 'my-gelf-address', }, }, - }, + }), ], }); - - }); test('create a gelf log driver using gelf with minimum options', () => { @@ -52,19 +50,17 @@ describe('gelf log driver', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ LogConfiguration: { LogDriver: 'gelf', Options: { 'gelf-address': 'my-gelf-address', }, }, - }, + }), ], }); - - }); }); diff --git a/packages/@aws-cdk/aws-ecs/test/images/tag-parameter-container-image.test.ts b/packages/@aws-cdk/aws-ecs/test/images/tag-parameter-container-image.test.ts index 3f409739d76ed..9ff3023b1e965 100644 --- a/packages/@aws-cdk/aws-ecs/test/images/tag-parameter-container-image.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/images/tag-parameter-container-image.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { SynthUtils } from '@aws-cdk/assert-internal'; +import { Match, Template } from '@aws-cdk/assertions'; import * as ecr from '@aws-cdk/aws-ecr'; import * as cdk from '@aws-cdk/core'; import * as ecs from '../../lib'; @@ -16,10 +15,8 @@ describe('tag parameter container image', () => { }); expect(() => { - SynthUtils.synthesize(stack); + Template.fromStack(stack); }).toThrow(/TagParameterContainerImage must be used in a container definition when using tagParameterName/); - - }); test('throws an error when tagParameterValue() is used without binding the image', () => { @@ -32,10 +29,8 @@ describe('tag parameter container image', () => { }); expect(() => { - SynthUtils.synthesize(stack); + Template.fromStack(stack); }).toThrow(/TagParameterContainerImage must be used in a container definition when using tagParameterValue/); - - }); test('can be used in a cross-account manner', () => { @@ -67,7 +62,7 @@ describe('tag parameter container image', () => { }); // THEN - expect(pipelineStack).toHaveResourceLike('AWS::ECR::Repository', { + Template.fromStack(pipelineStack).hasResourceProperties('AWS::ECR::Repository', { RepositoryName: repositoryName, RepositoryPolicyText: { Statement: [{ @@ -89,12 +84,12 @@ describe('tag parameter container image', () => { }], }, }); - expect(serviceStack).toHaveResourceLike('AWS::IAM::Role', { + Template.fromStack(serviceStack).hasResourceProperties('AWS::IAM::Role', { RoleName: 'servicestackionexecutionrolee7e2d9a783a54eb795f4', }); - expect(serviceStack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(serviceStack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Image: { 'Fn::Join': ['', [ { @@ -126,11 +121,9 @@ describe('tag parameter container image', () => { { Ref: 'ServiceTaskDefinitionContainerImageTagParamCEC9D0BA' }, ]], }, - }, + }), ], }); - - }); }); }); diff --git a/packages/@aws-cdk/aws-ecs/test/journald-log-driver.test.ts b/packages/@aws-cdk/aws-ecs/test/journald-log-driver.test.ts index fdf67efc3bb4f..b3f25650b9e58 100644 --- a/packages/@aws-cdk/aws-ecs/test/journald-log-driver.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/journald-log-driver.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as cdk from '@aws-cdk/core'; import * as ecs from '../lib'; @@ -10,8 +10,6 @@ describe('journald log driver', () => { beforeEach(() => { stack = new cdk.Stack(); td = new ecs.Ec2TaskDefinition(stack, 'TaskDefinition'); - - }); test('create a journald log driver with options', () => { @@ -25,20 +23,18 @@ describe('journald log driver', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ LogConfiguration: { LogDriver: 'journald', Options: { tag: 'hello', }, }, - }, + }), ], }); - - }); test('create a journald log driver without options', () => { @@ -50,17 +46,15 @@ describe('journald log driver', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ LogConfiguration: { LogDriver: 'journald', }, - }, + }), ], }); - - }); test('create a journald log driver using journald', () => { @@ -72,17 +66,15 @@ describe('journald log driver', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ LogConfiguration: { LogDriver: 'journald', Options: {}, }, - }, + }), ], }); - - }); }); diff --git a/packages/@aws-cdk/aws-ecs/test/json-file-log-driver.test.ts b/packages/@aws-cdk/aws-ecs/test/json-file-log-driver.test.ts index 0c21ff2d0b5f1..ed2622dc79eaa 100644 --- a/packages/@aws-cdk/aws-ecs/test/json-file-log-driver.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/json-file-log-driver.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as cdk from '@aws-cdk/core'; import * as ecs from '../lib'; @@ -25,20 +25,18 @@ describe('json file log driver', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ LogConfiguration: { LogDriver: 'json-file', Options: { env: 'hello', }, }, - }, + }), ], }); - - }); test('create a json-file log driver without options', () => { @@ -50,17 +48,15 @@ describe('json file log driver', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ LogConfiguration: { LogDriver: 'json-file', }, - }, + }), ], }); - - }); test('create a json-file log driver using json-file', () => { @@ -72,17 +68,15 @@ describe('json file log driver', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ LogConfiguration: { LogDriver: 'json-file', Options: {}, }, - }, + }), ], }); - - }); }); diff --git a/packages/@aws-cdk/aws-ecs/test/splunk-log-driver.test.ts b/packages/@aws-cdk/aws-ecs/test/splunk-log-driver.test.ts index cb7678d9bc6e4..b33b4155615a4 100644 --- a/packages/@aws-cdk/aws-ecs/test/splunk-log-driver.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/splunk-log-driver.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as secretsmanager from '@aws-cdk/aws-secretsmanager'; import * as ssm from '@aws-cdk/aws-ssm'; import * as cdk from '@aws-cdk/core'; @@ -28,9 +28,9 @@ describe('splunk log driver', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ LogConfiguration: { LogDriver: 'splunk', Options: { @@ -45,11 +45,9 @@ describe('splunk log driver', () => { }, }], }, - }, + }), ], }); - - }); test('create a splunk log driver using splunk with minimum options', () => { @@ -64,9 +62,9 @@ describe('splunk log driver', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ LogConfiguration: { LogDriver: 'splunk', Options: { @@ -81,11 +79,9 @@ describe('splunk log driver', () => { }, }], }, - }, + }), ], }); - - }); test('create a splunk log driver using splunk with sourcetype defined', () => { @@ -101,9 +97,9 @@ describe('splunk log driver', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ LogConfiguration: { LogDriver: 'splunk', Options: { @@ -119,11 +115,9 @@ describe('splunk log driver', () => { }, }], }, - }, + }), ], }); - - }); test('create a splunk log driver using secret splunk token from a new secret', () => { @@ -139,9 +133,9 @@ describe('splunk log driver', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ LogConfiguration: { LogDriver: 'splunk', Options: { @@ -156,11 +150,9 @@ describe('splunk log driver', () => { }, ], }, - }, + }), ], }); - - }); test('create a splunk log driver using secret splunk token from systems manager parameter store', () => { @@ -179,9 +171,9 @@ describe('splunk log driver', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ LogConfiguration: { LogDriver: 'splunk', Options: { @@ -213,11 +205,9 @@ describe('splunk log driver', () => { }, ], }, - }, + }), ], }); - - }); test('throws when neither token nor secret token are provided', () => { @@ -230,7 +220,5 @@ describe('splunk log driver', () => { memoryLimitMiB: 128, }); }).toThrow('Please provide either token or secretToken.'); - - }); }); diff --git a/packages/@aws-cdk/aws-ecs/test/syslog-log-driver.test.ts b/packages/@aws-cdk/aws-ecs/test/syslog-log-driver.test.ts index ae32f55ecd863..fa77acb52e09b 100644 --- a/packages/@aws-cdk/aws-ecs/test/syslog-log-driver.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/syslog-log-driver.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as cdk from '@aws-cdk/core'; import * as ecs from '../lib'; @@ -10,8 +10,6 @@ describe('syslog log driver', () => { beforeEach(() => { stack = new cdk.Stack(); td = new ecs.Ec2TaskDefinition(stack, 'TaskDefinition'); - - }); test('create a syslog log driver with options', () => { @@ -25,20 +23,18 @@ describe('syslog log driver', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ LogConfiguration: { LogDriver: 'syslog', Options: { tag: 'hello', }, }, - }, + }), ], }); - - }); test('create a syslog log driver without options', () => { @@ -50,17 +46,15 @@ describe('syslog log driver', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ LogConfiguration: { LogDriver: 'syslog', }, - }, + }), ], }); - - }); test('create a syslog log driver using syslog', () => { @@ -72,17 +66,15 @@ describe('syslog log driver', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ LogConfiguration: { LogDriver: 'syslog', Options: {}, }, - }, + }), ], }); - - }); }); diff --git a/packages/@aws-cdk/aws-ecs/test/task-definition.test.ts b/packages/@aws-cdk/aws-ecs/test/task-definition.test.ts index 07b3a8211da06..11e383565af81 100644 --- a/packages/@aws-cdk/aws-ecs/test/task-definition.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/task-definition.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; import * as ecs from '../lib'; @@ -17,7 +17,7 @@ describe('task definition', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { NetworkMode: 'awsvpc', }); From 1480213a032549ab7319e0c3a66e02e9b6a9c4ab Mon Sep 17 00:00:00 2001 From: Tatsuya Yamamoto Date: Wed, 19 Jan 2022 23:44:53 +0900 Subject: [PATCH 30/50] feat(iot): add Action to put record to Kinesis Data stream (#18321) Fixes #17703 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-iot-actions/README.md | 21 +++ .../@aws-cdk/aws-iot-actions/lib/index.ts | 1 + .../lib/kinesis-put-record-action.ts | 58 +++++++++ .../@aws-cdk/aws-iot-actions/package.json | 2 + ...eg.kinesis-put-record-action.expected.json | 116 +++++++++++++++++ .../integ.kinesis-put-record-action.ts | 27 ++++ .../kinesis-put-record-action.test.ts | 122 ++++++++++++++++++ 7 files changed, 347 insertions(+) create mode 100644 packages/@aws-cdk/aws-iot-actions/lib/kinesis-put-record-action.ts create mode 100644 packages/@aws-cdk/aws-iot-actions/test/kinesis-stream/integ.kinesis-put-record-action.expected.json create mode 100644 packages/@aws-cdk/aws-iot-actions/test/kinesis-stream/integ.kinesis-put-record-action.ts create mode 100644 packages/@aws-cdk/aws-iot-actions/test/kinesis-stream/kinesis-put-record-action.test.ts diff --git a/packages/@aws-cdk/aws-iot-actions/README.md b/packages/@aws-cdk/aws-iot-actions/README.md index b232878cec486..be475fe028210 100644 --- a/packages/@aws-cdk/aws-iot-actions/README.md +++ b/packages/@aws-cdk/aws-iot-actions/README.md @@ -26,6 +26,7 @@ Currently supported are: - Put logs to CloudWatch Logs - Capture CloudWatch metrics - Change state for a CloudWatch alarm +- Put records to Kinesis Data stream - Put records to Kinesis Data Firehose stream - Send messages to SQS queues @@ -172,6 +173,26 @@ const topicRule = new iot.TopicRule(this, 'TopicRule', { }); ``` +## Put records to Kinesis Data stream + +The code snippet below creates an AWS IoT Rule that put records to Kinesis Data +stream when it is triggered. + +```ts +import * as kinesis from '@aws-cdk/aws-kinesis'; + +const stream = new kinesis.Stream(this, 'MyStream'); + +const topicRule = new iot.TopicRule(this, 'TopicRule', { + sql: iot.IotSql.fromStringAsVer20160323("SELECT * FROM 'device/+/data'"), + actions: [ + new actions.KinesisPutRecordAction(stream, { + partitionKey: '${newuuid()}', + }), + ], +}); +``` + ## Put records to Kinesis Data Firehose stream The code snippet below creates an AWS IoT Rule that put records to Put records diff --git a/packages/@aws-cdk/aws-iot-actions/lib/index.ts b/packages/@aws-cdk/aws-iot-actions/lib/index.ts index 77fb9fc4e0efe..a0ee864a5bce2 100644 --- a/packages/@aws-cdk/aws-iot-actions/lib/index.ts +++ b/packages/@aws-cdk/aws-iot-actions/lib/index.ts @@ -3,6 +3,7 @@ export * from './cloudwatch-put-metric-action'; export * from './cloudwatch-set-alarm-state-action'; export * from './common-action-props'; export * from './firehose-put-record-action'; +export * from './kinesis-put-record-action'; export * from './lambda-function-action'; export * from './s3-put-object-action'; export * from './sqs-queue-action'; diff --git a/packages/@aws-cdk/aws-iot-actions/lib/kinesis-put-record-action.ts b/packages/@aws-cdk/aws-iot-actions/lib/kinesis-put-record-action.ts new file mode 100644 index 0000000000000..6baa5976bccf4 --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions/lib/kinesis-put-record-action.ts @@ -0,0 +1,58 @@ +import * as iam from '@aws-cdk/aws-iam'; +import * as iot from '@aws-cdk/aws-iot'; +import * as kinesis from '@aws-cdk/aws-kinesis'; +import { CommonActionProps } from './common-action-props'; +import { singletonActionRole } from './private/role'; + +/** + * Configuration properties of an action for the Kinesis Data stream. + */ +export interface KinesisPutRecordActionProps extends CommonActionProps { + /** + * The partition key used to determine to which shard the data is written. + * The partition key is usually composed of an expression (for example, ${topic()} or ${timestamp()}). + * + * @see https://docs.aws.amazon.com/iot/latest/developerguide/iot-substitution-templates.html + * + * You can use the expression '${newuuid()}' if your payload does not have a high cardinarity property. + * If you use empty string, this action use no partition key and all records will put same one shard. + * + * @see https://docs.aws.amazon.com/kinesis/latest/APIReference/API_PutRecord.html#API_PutRecord_RequestParameters + */ + readonly partitionKey: string; +} + +/** + * The action to put the record from an MQTT message to the Kinesis Data stream. + */ +export class KinesisPutRecordAction implements iot.IAction { + private readonly partitionKey?: string; + private readonly role?: iam.IRole; + + /** + * @param stream The Kinesis Data stream to which to put records. + * @param props Optional properties to not use default + */ + constructor(private readonly stream: kinesis.IStream, props: KinesisPutRecordActionProps) { + this.partitionKey = props.partitionKey; + this.role = props.role; + } + + bind(rule: iot.ITopicRule): iot.ActionConfig { + const role = this.role ?? singletonActionRole(rule); + role.addToPrincipalPolicy(new iam.PolicyStatement({ + actions: ['kinesis:PutRecord'], + resources: [this.stream.streamArn], + })); + + return { + configuration: { + kinesis: { + streamName: this.stream.streamName, + partitionKey: this.partitionKey || undefined, + roleArn: role.roleArn, + }, + }, + }; + } +} diff --git a/packages/@aws-cdk/aws-iot-actions/package.json b/packages/@aws-cdk/aws-iot-actions/package.json index bc112214866be..f03c1b14f1bb2 100644 --- a/packages/@aws-cdk/aws-iot-actions/package.json +++ b/packages/@aws-cdk/aws-iot-actions/package.json @@ -90,6 +90,7 @@ "@aws-cdk/aws-cloudwatch": "0.0.0", "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-iot": "0.0.0", + "@aws-cdk/aws-kinesis": "0.0.0", "@aws-cdk/aws-kinesisfirehose": "0.0.0", "@aws-cdk/aws-lambda": "0.0.0", "@aws-cdk/aws-logs": "0.0.0", @@ -104,6 +105,7 @@ "@aws-cdk/aws-cloudwatch": "0.0.0", "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-iot": "0.0.0", + "@aws-cdk/aws-kinesis": "0.0.0", "@aws-cdk/aws-kinesisfirehose": "0.0.0", "@aws-cdk/aws-lambda": "0.0.0", "@aws-cdk/aws-logs": "0.0.0", diff --git a/packages/@aws-cdk/aws-iot-actions/test/kinesis-stream/integ.kinesis-put-record-action.expected.json b/packages/@aws-cdk/aws-iot-actions/test/kinesis-stream/integ.kinesis-put-record-action.expected.json new file mode 100644 index 0000000000000..89229bf4731e3 --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions/test/kinesis-stream/integ.kinesis-put-record-action.expected.json @@ -0,0 +1,116 @@ +{ + "Resources": { + "TopicRule40A4EA44": { + "Type": "AWS::IoT::TopicRule", + "Properties": { + "TopicRulePayload": { + "Actions": [ + { + "Kinesis": { + "PartitionKey": "${timestamp()}", + "RoleArn": { + "Fn::GetAtt": [ + "TopicRuleTopicRuleActionRole246C4F77", + "Arn" + ] + }, + "StreamName": { + "Ref": "MyStream5C050E93" + } + } + } + ], + "AwsIotSqlVersion": "2016-03-23", + "Sql": "SELECT * FROM 'device/+/data'" + } + } + }, + "TopicRuleTopicRuleActionRole246C4F77": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "iot.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "TopicRuleTopicRuleActionRoleDefaultPolicy99ADD687": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "kinesis:PutRecord", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "MyStream5C050E93", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "TopicRuleTopicRuleActionRoleDefaultPolicy99ADD687", + "Roles": [ + { + "Ref": "TopicRuleTopicRuleActionRole246C4F77" + } + ] + } + }, + "MyStream5C050E93": { + "Type": "AWS::Kinesis::Stream", + "Properties": { + "RetentionPeriodHours": 24, + "ShardCount": 3, + "StreamEncryption": { + "Fn::If": [ + "AwsCdkKinesisEncryptedStreamsUnsupportedRegions", + { + "Ref": "AWS::NoValue" + }, + { + "EncryptionType": "KMS", + "KeyId": "alias/aws/kinesis" + } + ] + }, + "StreamModeDetails": { + "StreamMode": "PROVISIONED" + } + } + } + }, + "Conditions": { + "AwsCdkKinesisEncryptedStreamsUnsupportedRegions": { + "Fn::Or": [ + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "cn-north-1" + ] + }, + { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "cn-northwest-1" + ] + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-iot-actions/test/kinesis-stream/integ.kinesis-put-record-action.ts b/packages/@aws-cdk/aws-iot-actions/test/kinesis-stream/integ.kinesis-put-record-action.ts new file mode 100644 index 0000000000000..953f370957ec3 --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions/test/kinesis-stream/integ.kinesis-put-record-action.ts @@ -0,0 +1,27 @@ +import * as iot from '@aws-cdk/aws-iot'; +import * as kinesis from '@aws-cdk/aws-kinesis'; +import * as cdk from '@aws-cdk/core'; +import * as actions from '../../lib'; + +class TestStack extends cdk.Stack { + constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + const topicRule = new iot.TopicRule(this, 'TopicRule', { + sql: iot.IotSql.fromStringAsVer20160323( + "SELECT * FROM 'device/+/data'", + ), + }); + + const stream = new kinesis.Stream(this, 'MyStream', { + shardCount: 3, + }); + topicRule.addAction(new actions.KinesisPutRecordAction(stream, { + partitionKey: '${timestamp()}', + })); + } +} + +const app = new cdk.App(); +new TestStack(app, 'test-kinesis-stream-action-stack'); +app.synth(); diff --git a/packages/@aws-cdk/aws-iot-actions/test/kinesis-stream/kinesis-put-record-action.test.ts b/packages/@aws-cdk/aws-iot-actions/test/kinesis-stream/kinesis-put-record-action.test.ts new file mode 100644 index 0000000000000..05a0d603b4046 --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions/test/kinesis-stream/kinesis-put-record-action.test.ts @@ -0,0 +1,122 @@ +import { Template, Match } from '@aws-cdk/assertions'; +import * as iam from '@aws-cdk/aws-iam'; +import * as iot from '@aws-cdk/aws-iot'; +import * as kinesis from '@aws-cdk/aws-kinesis'; +import * as cdk from '@aws-cdk/core'; +import * as actions from '../../lib'; + +test('Default kinesis stream action', () => { + // GIVEN + const stack = new cdk.Stack(); + const topicRule = new iot.TopicRule(stack, 'MyTopicRule', { + sql: iot.IotSql.fromStringAsVer20160323("SELECT topic(2) as device_id FROM 'device/+/data'"), + }); + const stream = kinesis.Stream.fromStreamArn(stack, 'MyStream', 'arn:aws:kinesis:xx-west-1:111122223333:stream/my-stream'); + + // WHEN + topicRule.addAction(new actions.KinesisPutRecordAction(stream, { + partitionKey: '${newuuid()}', + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IoT::TopicRule', { + TopicRulePayload: { + Actions: [ + { + Kinesis: { + StreamName: 'my-stream', + PartitionKey: '${newuuid()}', + RoleArn: { + 'Fn::GetAtt': ['MyTopicRuleTopicRuleActionRoleCE2D05DA', 'Arn'], + }, + }, + }, + ], + }, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: 'sts:AssumeRole', + Effect: 'Allow', + Principal: { + Service: 'iot.amazonaws.com', + }, + }, + ], + Version: '2012-10-17', + }, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'kinesis:PutRecord', + Effect: 'Allow', + Resource: 'arn:aws:kinesis:xx-west-1:111122223333:stream/my-stream', + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'MyTopicRuleTopicRuleActionRoleDefaultPolicy54A701F7', + Roles: [ + { Ref: 'MyTopicRuleTopicRuleActionRoleCE2D05DA' }, + ], + }); +}); + +test('passes undefined to partitionKey if empty string is given', () => { + // GIVEN + const stack = new cdk.Stack(); + const topicRule = new iot.TopicRule(stack, 'MyTopicRule', { + sql: iot.IotSql.fromStringAsVer20160323("SELECT topic(2) as device_id FROM 'device/+/data'"), + }); + const stream = kinesis.Stream.fromStreamArn(stack, 'MyStream', 'arn:aws:kinesis:xx-west-1:111122223333:stream/my-stream'); + + // WHEN + topicRule.addAction(new actions.KinesisPutRecordAction(stream, { + partitionKey: '', + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IoT::TopicRule', { + TopicRulePayload: { + Actions: [ + Match.objectLike({ Kinesis: { PartitionKey: Match.absent() } }), + ], + }, + }); +}); + +test('can set role', () => { + // GIVEN + const stack = new cdk.Stack(); + const topicRule = new iot.TopicRule(stack, 'MyTopicRule', { + sql: iot.IotSql.fromStringAsVer20160323("SELECT topic(2) as device_id FROM 'device/+/data'"), + }); + const stream = kinesis.Stream.fromStreamArn(stack, 'MyStream', 'arn:aws:kinesis:xx-west-1:111122223333:stream/my-stream'); + const role = iam.Role.fromRoleArn(stack, 'MyRole', 'arn:aws:iam::123456789012:role/ForTest'); + + // WHEN + topicRule.addAction(new actions.KinesisPutRecordAction(stream, { + partitionKey: '${newuuid()}', + role, + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IoT::TopicRule', { + TopicRulePayload: { + Actions: [ + Match.objectLike({ Kinesis: { RoleArn: 'arn:aws:iam::123456789012:role/ForTest' } }), + ], + }, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyName: 'MyRolePolicy64AB00A5', + Roles: ['ForTest'], + }); +}); From d9d0688cd58c5d96c5fa0bbb69674dc34e34416c Mon Sep 17 00:00:00 2001 From: Calvin Combs <66279577+comcalvi@users.noreply.github.com> Date: Wed, 19 Jan 2022 07:33:09 -0800 Subject: [PATCH 31/50] chore(logs): migrate all tests to assertions (#18517) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-logs/package.json | 2 +- .../aws-logs/test/destination.test.ts | 30 +++++--- .../aws-logs/test/log-retention.test.ts | 37 +++------ .../@aws-cdk/aws-logs/test/loggroup.test.ts | 76 ++++++------------- .../@aws-cdk/aws-logs/test/logstream.test.ts | 7 +- .../aws-logs/test/metricfilter.test.ts | 10 +-- .../@aws-cdk/aws-logs/test/pattern.test.ts | 32 -------- .../@aws-cdk/aws-logs/test/policy.test.ts | 6 +- .../aws-logs/test/subscriptionfilter.test.ts | 6 +- 9 files changed, 66 insertions(+), 140 deletions(-) diff --git a/packages/@aws-cdk/aws-logs/package.json b/packages/@aws-cdk/aws-logs/package.json index 1bee0d473345d..a2c39272e68d1 100644 --- a/packages/@aws-cdk/aws-logs/package.json +++ b/packages/@aws-cdk/aws-logs/package.json @@ -79,7 +79,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-logs/test/destination.test.ts b/packages/@aws-cdk/aws-logs/test/destination.test.ts index 05f482441d265..096addb02a885 100644 --- a/packages/@aws-cdk/aws-logs/test/destination.test.ts +++ b/packages/@aws-cdk/aws-logs/test/destination.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template, Match } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; import { CrossAccountDestination } from '../lib'; @@ -19,13 +19,11 @@ describe('destination', () => { }); // THEN - expect(stack).toHaveResource('AWS::Logs::Destination', { + Template.fromStack(stack).hasResourceProperties('AWS::Logs::Destination', { DestinationName: 'MyDestination', RoleArn: { 'Fn::GetAtt': ['Role1ABCC5F0', 'Arn'] }, TargetArn: 'arn:bogus', }); - - }); test('add policy to destination', () => { @@ -47,12 +45,24 @@ describe('destination', () => { })); // THEN - expect(stack).toHaveResource('AWS::Logs::Destination', (props: any) => { - const pol = JSON.parse(props.DestinationPolicy); - - return pol.Statement[0].Action === 'logs:TalkToMe'; + Template.fromStack(stack).hasResourceProperties('AWS::Logs::Destination', { + DestinationName: 'MyDestination', + DestinationPolicy: Match.serializedJson({ + Statement: [ + { + Action: 'logs:TalkToMe', + Effect: 'Allow', + }, + ], + Version: '2012-10-17', + }), + RoleArn: { + 'Fn::GetAtt': [ + 'Role1ABCC5F0', + 'Arn', + ], + }, + TargetArn: 'arn:bogus', }); - - }); }); diff --git a/packages/@aws-cdk/aws-logs/test/log-retention.test.ts b/packages/@aws-cdk/aws-logs/test/log-retention.test.ts index 7fd7d6b8532bf..5902f7de8dd86 100644 --- a/packages/@aws-cdk/aws-logs/test/log-retention.test.ts +++ b/packages/@aws-cdk/aws-logs/test/log-retention.test.ts @@ -1,6 +1,5 @@ import * as path from 'path'; -import '@aws-cdk/assert-internal/jest'; -import { ABSENT, ResourcePart } from '@aws-cdk/assert-internal'; +import { Match, Template } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; import * as cxapi from '@aws-cdk/cx-api'; @@ -20,7 +19,7 @@ describe('log retention', () => { }); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { 'PolicyDocument': { 'Statement': [ { @@ -42,12 +41,12 @@ describe('log retention', () => { ], }); - expect(stack).toHaveResource('AWS::Lambda::Function', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { Handler: 'index.handler', Runtime: 'nodejs14.x', }); - expect(stack).toHaveResource('Custom::LogRetention', { + Template.fromStack(stack).hasResourceProperties('Custom::LogRetention', { 'ServiceToken': { 'Fn::GetAtt': [ 'LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aFD4BFC8A', @@ -57,8 +56,6 @@ describe('log retention', () => { 'LogGroupName': 'group', 'RetentionInDays': 30, }); - - }); test('with imported role', () => { @@ -74,7 +71,7 @@ describe('log retention', () => { }); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { 'PolicyDocument': { 'Statement': [ { @@ -94,9 +91,7 @@ describe('log retention', () => { ], }); - expect(stack).toCountResources('AWS::IAM::Role', 0); - - + Template.fromStack(stack).resourceCountIs('AWS::IAM::Role', 0); }); test('with RetentionPeriod set to Infinity', () => { @@ -107,11 +102,9 @@ describe('log retention', () => { retention: RetentionDays.INFINITE, }); - expect(stack).toHaveResource('Custom::LogRetention', { - RetentionInDays: ABSENT, + Template.fromStack(stack).hasResourceProperties('Custom::LogRetention', { + RetentionInDays: Match.absent(), }); - - }); test('with LogGroupRegion specified', () => { @@ -122,11 +115,9 @@ describe('log retention', () => { retention: RetentionDays.INFINITE, }); - expect(stack).toHaveResource('Custom::LogRetention', { + Template.fromStack(stack).hasResourceProperties('Custom::LogRetention', { LogGroupRegion: 'us-east-1', }); - - }); test('log group ARN is well formed and conforms', () => { @@ -140,7 +131,6 @@ describe('log retention', () => { expect(logGroupArn.indexOf('logs')).toBeGreaterThan(-1); expect(logGroupArn.indexOf('log-group')).toBeGreaterThan(-1); expect(logGroupArn.endsWith(':*')).toEqual(true); - }); test('log group ARN is well formed and conforms when region is specified', () => { @@ -156,7 +146,6 @@ describe('log retention', () => { expect(logGroupArn.indexOf('logs')).toBeGreaterThan(-1); expect(logGroupArn.indexOf('log-group')).toBeGreaterThan(-1); expect(logGroupArn.endsWith(':*')).toEqual(true); - }); test('retention Lambda CfnResource receives propagated tags', () => { @@ -167,7 +156,7 @@ describe('log retention', () => { retention: RetentionDays.ONE_MONTH, }); - expect(stack).toHaveResourceLike('AWS::Lambda::Function', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { Tags: [ { Key: 'test-key', @@ -175,7 +164,6 @@ describe('log retention', () => { }, ], }); - }); test('asset metadata added to log retention construct lambda function', () => { @@ -193,13 +181,12 @@ describe('log retention', () => { }); // Then - expect(stack).toHaveResource('AWS::Lambda::Function', { + Template.fromStack(stack).hasResource('AWS::Lambda::Function', { Metadata: { 'aws:asset:path': assetLocation, 'aws:asset:is-bundled': false, 'aws:asset:property': 'Code', }, - }, ResourcePart.CompleteDefinition); - + }); }); }); diff --git a/packages/@aws-cdk/aws-logs/test/loggroup.test.ts b/packages/@aws-cdk/aws-logs/test/loggroup.test.ts index 7928de182d7ff..4fefc67272d5f 100644 --- a/packages/@aws-cdk/aws-logs/test/loggroup.test.ts +++ b/packages/@aws-cdk/aws-logs/test/loggroup.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; import { CfnParameter, RemovalPolicy, Stack } from '@aws-cdk/core'; @@ -16,12 +16,9 @@ describe('log group', () => { }); // THEN - expect(stack).toHaveResource('AWS::Logs::LogGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::Logs::LogGroup', { KmsKeyId: { 'Fn::GetAtt': ['Key961B73FD', 'Arn'] }, - }); - - }); test('fixed retention', () => { @@ -34,11 +31,9 @@ describe('log group', () => { }); // THEN - expect(stack).toHaveResource('AWS::Logs::LogGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::Logs::LogGroup', { RetentionInDays: 7, }); - - }); test('default retention', () => { @@ -49,11 +44,9 @@ describe('log group', () => { new LogGroup(stack, 'LogGroup'); // THEN - expect(stack).toHaveResource('AWS::Logs::LogGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::Logs::LogGroup', { RetentionInDays: 731, }); - - }); test('infinite retention/dont delete log group by default', () => { @@ -66,7 +59,7 @@ describe('log group', () => { }); // THEN - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { LogGroupF5B46931: { Type: 'AWS::Logs::LogGroup', @@ -75,8 +68,6 @@ describe('log group', () => { }, }, }); - - }); test('infinite retention via legacy method', () => { @@ -92,7 +83,7 @@ describe('log group', () => { }); // THEN - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { LogGroupF5B46931: { Type: 'AWS::Logs::LogGroup', @@ -101,8 +92,6 @@ describe('log group', () => { }, }, }); - - }); test('unresolved retention', () => { @@ -116,13 +105,11 @@ describe('log group', () => { }); // THEN - expect(stack).toHaveResource('AWS::Logs::LogGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::Logs::LogGroup', { RetentionInDays: { Ref: 'RetentionInDays', }, }); - - }); test('will delete log group if asked to', () => { @@ -136,7 +123,7 @@ describe('log group', () => { }); // THEN - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { LogGroupF5B46931: { Type: 'AWS::Logs::LogGroup', @@ -145,8 +132,6 @@ describe('log group', () => { }, }, }); - - }); test('import from ARN, same region', () => { @@ -160,10 +145,9 @@ describe('log group', () => { // THEN expect(imported.logGroupName).toEqual('my-log-group'); expect(imported.logGroupArn).toEqual('arn:aws:logs:us-east-1:123456789012:log-group:my-log-group:*'); - expect(stack2).toHaveResource('AWS::Logs::LogStream', { + Template.fromStack(stack2).hasResourceProperties('AWS::Logs::LogStream', { LogGroupName: 'my-log-group', }); - }); test('import from ARN, different region', () => { @@ -182,10 +166,10 @@ describe('log group', () => { expect(imported.env.region).not.toEqual(stack.region); expect(imported.env.region).toEqual(importRegion); - expect(stack).toHaveResource('AWS::Logs::LogStream', { + Template.fromStack(stack).hasResourceProperties('AWS::Logs::LogStream', { LogGroupName: 'my-log-group', }); - expect(stack).toCountResources('AWS::Logs::LogGroup', 0); + Template.fromStack(stack).resourceCountIs('AWS::Logs::LogGroup', 0); }); test('import from name', () => { @@ -200,10 +184,9 @@ describe('log group', () => { expect(imported.logGroupName).toEqual('my-log-group'); expect(imported.logGroupArn).toMatch(/^arn:.+:logs:.+:.+:log-group:my-log-group:\*$/); - expect(stack).toHaveResource('AWS::Logs::LogStream', { + Template.fromStack(stack).hasResourceProperties('AWS::Logs::LogStream', { LogGroupName: 'my-log-group', }); - }); describe('loggroups imported by name have stream wildcard appended to grant ARN', () => void dataDrivenTests([ @@ -221,7 +204,7 @@ describe('log group', () => { imported.grantWrite(user); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Version: '2012-10-17', Statement: [ @@ -243,9 +226,8 @@ describe('log group', () => { ], }, }); - expect(imported.logGroupName).toEqual('my-log-group'); - + expect(imported.logGroupName).toEqual('my-log-group'); })); describe('loggroups imported by ARN have stream wildcard appended to grant ARN', () => void dataDrivenTests([ @@ -263,7 +245,7 @@ describe('log group', () => { imported.grantWrite(user); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Version: '2012-10-17', Statement: [ @@ -275,9 +257,8 @@ describe('log group', () => { ], }, }); - expect(imported.logGroupName).toEqual('my-log-group'); - + expect(imported.logGroupName).toEqual('my-log-group'); })); test('extractMetric', () => { @@ -289,7 +270,7 @@ describe('log group', () => { const metric = lg.extractMetric('$.myField', 'MyService', 'Field'); // THEN - expect(stack).toHaveResource('AWS::Logs::MetricFilter', { + Template.fromStack(stack).hasResourceProperties('AWS::Logs::MetricFilter', { FilterPattern: '{ $.myField = "*" }', LogGroupName: { Ref: 'LogGroupF5B46931' }, MetricTransformations: [ @@ -300,10 +281,9 @@ describe('log group', () => { }, ], }); + expect(metric.namespace).toEqual('MyService'); expect(metric.metricName).toEqual('Field'); - - }); test('extractMetric allows passing in namespaces with "/"', () => { @@ -315,7 +295,7 @@ describe('log group', () => { const metric = lg.extractMetric('$.myField', 'MyNamespace/MyService', 'Field'); // THEN - expect(stack).toHaveResource('AWS::Logs::MetricFilter', { + Template.fromStack(stack).hasResourceProperties('AWS::Logs::MetricFilter', { FilterPattern: '{ $.myField = "*" }', MetricTransformations: [ { @@ -325,10 +305,9 @@ describe('log group', () => { }, ], }); + expect(metric.namespace).toEqual('MyNamespace/MyService'); expect(metric.metricName).toEqual('Field'); - - }); test('grant', () => { @@ -341,7 +320,7 @@ describe('log group', () => { lg.grantWrite(user); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -353,8 +332,6 @@ describe('log group', () => { Version: '2012-10-17', }, }); - - }); test('grant to service principal', () => { @@ -367,7 +344,7 @@ describe('log group', () => { lg.grantWrite(sp); // THEN - expect(stack).toHaveResource('AWS::Logs::ResourcePolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::Logs::ResourcePolicy', { PolicyDocument: { 'Fn::Join': [ '', @@ -385,10 +362,8 @@ describe('log group', () => { }, PolicyName: 'LogGroupPolicy643B329C', }); - }); - test('can add a policy to the log group', () => { // GIVEN const stack = new Stack(); @@ -402,7 +377,7 @@ describe('log group', () => { })); // THEN - expect(stack).toHaveResource('AWS::Logs::ResourcePolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::Logs::ResourcePolicy', { PolicyDocument: '{"Statement":[{"Action":"logs:PutLogEvents","Effect":"Allow","Principal":{"AWS":"arn:aws:iam::123456789012:user/user-name"},"Resource":"*"}],"Version":"2012-10-17"}', PolicyName: 'LogGroupPolicy643B329C', }); @@ -419,11 +394,9 @@ describe('log group', () => { // THEN expect(logGroup.logGroupPhysicalName()).toEqual('my-log-group'); - expect(stack).toHaveResource('AWS::Logs::LogGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::Logs::LogGroup', { LogGroupName: 'my-log-group', }); - - }); }); @@ -434,5 +407,4 @@ function dataDrivenTests(cases: string[], body: (suffix: string) => void): void body(args); }); } - } diff --git a/packages/@aws-cdk/aws-logs/test/logstream.test.ts b/packages/@aws-cdk/aws-logs/test/logstream.test.ts index c55501adc6b1b..d599ca8bdbe0a 100644 --- a/packages/@aws-cdk/aws-logs/test/logstream.test.ts +++ b/packages/@aws-cdk/aws-logs/test/logstream.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { Stack } from '@aws-cdk/core'; import { LogGroup, LogStream } from '../lib'; @@ -15,9 +15,6 @@ describe('log stream', () => { }); // THEN - expect(stack).toHaveResource('AWS::Logs::LogStream', { - }); - - + Template.fromStack(stack).hasResourceProperties('AWS::Logs::LogStream', { }); }); }); diff --git a/packages/@aws-cdk/aws-logs/test/metricfilter.test.ts b/packages/@aws-cdk/aws-logs/test/metricfilter.test.ts index 15814140a9488..6ab67899db2ea 100644 --- a/packages/@aws-cdk/aws-logs/test/metricfilter.test.ts +++ b/packages/@aws-cdk/aws-logs/test/metricfilter.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { Metric } from '@aws-cdk/aws-cloudwatch'; import { Stack } from '@aws-cdk/core'; import { FilterPattern, LogGroup, MetricFilter } from '../lib'; @@ -19,7 +19,7 @@ describe('metric filter', () => { }); // THEN - expect(stack).toHaveResource('AWS::Logs::MetricFilter', { + Template.fromStack(stack).hasResourceProperties('AWS::Logs::MetricFilter', { MetricTransformations: [{ MetricNamespace: 'AWS/Test', MetricName: 'Latency', @@ -28,8 +28,6 @@ describe('metric filter', () => { FilterPattern: '{ $.latency = "*" }', LogGroupName: { Ref: 'LogGroupF5B46931' }, }); - - }); test('metric filter exposes metric', () => { @@ -54,8 +52,6 @@ describe('metric filter', () => { namespace: 'AWS/Test', statistic: 'avg', })); - - }); test('metric filter exposes metric with custom statistic', () => { @@ -80,7 +76,5 @@ describe('metric filter', () => { namespace: 'AWS/Test', statistic: 'maximum', })); - - }); }); diff --git a/packages/@aws-cdk/aws-logs/test/pattern.test.ts b/packages/@aws-cdk/aws-logs/test/pattern.test.ts index 848c3ef824aa7..b49c9cd220588 100644 --- a/packages/@aws-cdk/aws-logs/test/pattern.test.ts +++ b/packages/@aws-cdk/aws-logs/test/pattern.test.ts @@ -6,16 +6,12 @@ describe('pattern', () => { const pattern = FilterPattern.allTerms('foo', 'bar', 'baz'); expect('"foo" "bar" "baz"').toEqual(pattern.logPatternString); - - }); test('quoted terms', () => { const pattern = FilterPattern.allTerms('"foo" he said'); expect('"\\"foo\\" he said"').toEqual(pattern.logPatternString); - - }); test('disjunction of conjunctions', () => { @@ -25,8 +21,6 @@ describe('pattern', () => { ); expect('?"foo" "bar" ?"baz"').toEqual(pattern.logPatternString); - - }); test('dont prefix with ? if only one disjunction', () => { @@ -35,16 +29,12 @@ describe('pattern', () => { ); expect('"foo" "bar"').toEqual(pattern.logPatternString); - - }); test('empty log pattern is empty string', () => { const pattern = FilterPattern.anyTermGroup(); expect('').toEqual(pattern.logPatternString); - - }); }); @@ -53,24 +43,18 @@ describe('pattern', () => { const pattern = FilterPattern.stringValue('$.field', '=', 'value'); expect('{ $.field = "value" }').toEqual(pattern.logPatternString); - - }); test('also recognize ==', () => { const pattern = FilterPattern.stringValue('$.field', '==', 'value'); expect('{ $.field = "value" }').toEqual(pattern.logPatternString); - - }); test('number patterns', () => { const pattern = FilterPattern.numberValue('$.field', '<=', 300); expect('{ $.field <= 300 }').toEqual(pattern.logPatternString); - - }); test('combining with AND or OR', () => { @@ -82,8 +66,6 @@ describe('pattern', () => { const orPattern = FilterPattern.any(p1, p2); expect('{ ($.field <= 300) || ($.field = "value") }').toEqual(orPattern.logPatternString); - - }); test('single AND is not wrapped with parens', () => { @@ -92,32 +74,24 @@ describe('pattern', () => { const pattern = FilterPattern.all(p1); expect('{ $.field = "value" }').toEqual(pattern.logPatternString); - - }); test('empty AND is rejected', () => { expect(() => { FilterPattern.all(); }).toThrow(); - - }); test('invalid string operators are rejected', () => { expect(() => { FilterPattern.stringValue('$.field', '<=', 'hello'); }).toThrow(); - - }); test('can test boolean value', () => { const pattern = FilterPattern.booleanValue('$.field', false); expect('{ $.field IS FALSE }').toEqual(pattern.logPatternString); - - }); }); @@ -126,8 +100,6 @@ describe('pattern', () => { const pattern = FilterPattern.spaceDelimited('...', 'status_code', 'bytes'); expect(pattern.logPatternString).toEqual('[..., status_code, bytes]'); - - }); test('add restrictions', () => { @@ -136,16 +108,12 @@ describe('pattern', () => { .whereNumber('status_code', '!=', 403); expect(pattern.logPatternString).toEqual('[..., status_code = "4*" && status_code != 403, bytes]'); - - }); test('cant use more than one ellipsis', () => { expect(() => { FilterPattern.spaceDelimited('...', 'status_code', '...'); }).toThrow(); - - }); }); }); diff --git a/packages/@aws-cdk/aws-logs/test/policy.test.ts b/packages/@aws-cdk/aws-logs/test/policy.test.ts index 4b2684a9957b1..8ffb66ab8eb29 100644 --- a/packages/@aws-cdk/aws-logs/test/policy.test.ts +++ b/packages/@aws-cdk/aws-logs/test/policy.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { PolicyStatement, ServicePrincipal } from '@aws-cdk/aws-iam'; import { Stack } from '@aws-cdk/core'; import { LogGroup, ResourcePolicy } from '../lib'; @@ -16,7 +16,7 @@ describe('resource policy', () => { })); // THEN - expect(stack).toHaveResource('AWS::Logs::ResourcePolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::Logs::ResourcePolicy', { PolicyName: 'LogGroupPolicy643B329C', PolicyDocument: JSON.stringify({ Statement: [ @@ -45,7 +45,7 @@ describe('resource policy', () => { })); // THEN - expect(stack).toHaveResource('AWS::Logs::ResourcePolicy', { + Template.fromStack(stack).hasResourceProperties('AWS::Logs::ResourcePolicy', { PolicyName: 'ResourcePolicy', }); }); diff --git a/packages/@aws-cdk/aws-logs/test/subscriptionfilter.test.ts b/packages/@aws-cdk/aws-logs/test/subscriptionfilter.test.ts index 595e4933745ca..bc691e26aaa96 100644 --- a/packages/@aws-cdk/aws-logs/test/subscriptionfilter.test.ts +++ b/packages/@aws-cdk/aws-logs/test/subscriptionfilter.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { Stack } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { FilterPattern, ILogGroup, ILogSubscriptionDestination, LogGroup, SubscriptionFilter } from '../lib'; @@ -17,13 +17,11 @@ describe('subscription filter', () => { }); // THEN - expect(stack).toHaveResource('AWS::Logs::SubscriptionFilter', { + Template.fromStack(stack).hasResourceProperties('AWS::Logs::SubscriptionFilter', { DestinationArn: 'arn:bogus', FilterPattern: 'some pattern', LogGroupName: { Ref: 'LogGroupF5B46931' }, }); - - }); }); From 517d517a0bb3f7f6e98538dca736086b86b206c8 Mon Sep 17 00:00:00 2001 From: AWS CDK Automation <43080478+aws-cdk-automation@users.noreply.github.com> Date: Wed, 19 Jan 2022 08:19:54 -0800 Subject: [PATCH 32/50] feat(cfnspec): cloudformation spec v53.0.0 (#18524) Co-authored-by: AWS CDK Team Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- packages/@aws-cdk/cfnspec/CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/@aws-cdk/cfnspec/CHANGELOG.md b/packages/@aws-cdk/cfnspec/CHANGELOG.md index 9d2b6815ee159..a7c70ef7540e1 100644 --- a/packages/@aws-cdk/cfnspec/CHANGELOG.md +++ b/packages/@aws-cdk/cfnspec/CHANGELOG.md @@ -5,6 +5,12 @@ * AWS::SageMaker is at 51.0.0 +## Unapplied changes + +* AWS::ECS is at 51.0.0 +* AWS::SageMaker is at 51.0.0 + + ## Unapplied changes * AWS::ECS is at 51.0.0 From 204127862d5fb1d2e6dd573a1621254e52eca4aa Mon Sep 17 00:00:00 2001 From: Yerzhan Mazhkenov <20302932+yerzhan7@users.noreply.github.com> Date: Wed, 19 Jan 2022 17:08:12 +0000 Subject: [PATCH 33/50] revert(s3): add EventBridge bucket notifications (#18150) (#18507) This reverts commit 912aeda295820920ed880b9c85a98c56421647b8. ## Why? [PR#18150](https://github.com/aws/aws-cdk/pull/18150) was merged prematurely (See comments). To avoid customer confusion this PR reverts it. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- allowed-breaking-changes.txt | 5 + .../test/integ.s3.expected.json | 4 +- .../test/integ.notifications.expected.json | 2 +- .../integ.bucket-notifications.expected.json | 4 +- .../test/lambda/integ.bucket-notifications.ts | 2 +- ...teg.sns-bucket-notifications.expected.json | 2 +- .../integ.bucket-notifications.expected.json | 2 +- packages/@aws-cdk/aws-s3/README.md | 15 -- packages/@aws-cdk/aws-s3/lib/bucket.ts | 17 -- .../notifications-resource/lambda/index.py | 14 -- .../notifications-resource.ts | 10 - .../integ.bucket.notifications.expected.json | 101 --------- .../aws-s3/test/integ.bucket.notifications.ts | 14 -- .../@aws-cdk/aws-s3/test/notification.test.ts | 18 -- .../test_index.py | 203 ------------------ 15 files changed, 13 insertions(+), 400 deletions(-) delete mode 100644 packages/@aws-cdk/aws-s3/test/integ.bucket.notifications.expected.json delete mode 100644 packages/@aws-cdk/aws-s3/test/integ.bucket.notifications.ts diff --git a/allowed-breaking-changes.txt b/allowed-breaking-changes.txt index 599cc80596857..864ea5178512b 100644 --- a/allowed-breaking-changes.txt +++ b/allowed-breaking-changes.txt @@ -121,3 +121,8 @@ base-types:@aws-cdk/aws-lambda-event-sources.SelfManagedKafkaEventSourceProps changed-type:@aws-cdk/aws-elasticloadbalancingv2.ApplicationLoadBalancer.vpc changed-type:@aws-cdk/aws-elasticloadbalancingv2.BaseLoadBalancer.vpc changed-type:@aws-cdk/aws-elasticloadbalancingv2.NetworkLoadBalancer.vpc + +# removed methods and properties related to event bridge notifications for S3 buckets as they are not yet supported (19 Jan 2022) +removed:@aws-cdk/aws-s3.Bucket.enableEventBridgeNotification +removed:@aws-cdk/aws-s3.BucketBase.enableEventBridgeNotification +removed:@aws-cdk/aws-s3.BucketProps.eventBridgeEnabled \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda-event-sources/test/integ.s3.expected.json b/packages/@aws-cdk/aws-lambda-event-sources/test/integ.s3.expected.json index bea3d9952fbc6..ff71167d19f9e 100644 --- a/packages/@aws-cdk/aws-lambda-event-sources/test/integ.s3.expected.json +++ b/packages/@aws-cdk/aws-lambda-event-sources/test/integ.s3.expected.json @@ -177,7 +177,7 @@ "Properties": { "Description": "AWS CloudFormation handler for \"Custom::S3BucketNotifications\" resources (@aws-cdk/aws-s3)", "Code": { - "ZipFile": "import boto3 # type: ignore\nimport json\nimport logging\nimport urllib.request\n\ns3 = boto3.client(\"s3\")\n\nEVENTBRIDGE_CONFIGURATION = 'EventBridgeConfiguration'\n\nCONFIGURATION_TYPES = [\"TopicConfigurations\", \"QueueConfigurations\", \"LambdaFunctionConfigurations\"]\n\ndef handler(event: dict, context):\n response_status = \"SUCCESS\"\n error_message = \"\"\n try:\n props = event[\"ResourceProperties\"]\n bucket = props[\"BucketName\"]\n notification_configuration = props[\"NotificationConfiguration\"]\n request_type = event[\"RequestType\"]\n managed = props.get('Managed', 'true').lower() == 'true'\n stack_id = event['StackId']\n\n if managed:\n config = handle_managed(request_type, notification_configuration)\n else:\n config = handle_unmanaged(bucket, stack_id, request_type, notification_configuration)\n\n put_bucket_notification_configuration(bucket, config)\n except Exception as e:\n logging.exception(\"Failed to put bucket notification configuration\")\n response_status = \"FAILED\"\n error_message = f\"Error: {str(e)}. \"\n finally:\n submit_response(event, context, response_status, error_message)\n\n\ndef handle_managed(request_type, notification_configuration):\n if request_type == 'Delete':\n return {}\n return notification_configuration\n\n\ndef handle_unmanaged(bucket, stack_id, request_type, notification_configuration):\n\n # find external notifications\n external_notifications = find_external_notifications(bucket, stack_id)\n\n # if delete, that's all we need\n if request_type == 'Delete':\n return external_notifications\n\n def with_id(notification):\n notification['Id'] = f\"{stack_id}-{hash(json.dumps(notification, sort_keys=True))}\"\n return notification\n\n # otherwise, merge external with incoming config and augment with id\n notifications = {}\n for t in CONFIGURATION_TYPES:\n external = external_notifications.get(t, [])\n incoming = [with_id(n) for n in notification_configuration.get(t, [])]\n notifications[t] = external + incoming\n\n # EventBridge configuration is a special case because it's just an empty object if it exists\n if EVENTBRIDGE_CONFIGURATION in notification_configuration:\n notifications[EVENTBRIDGE_CONFIGURATION] = notification_configuration[EVENTBRIDGE_CONFIGURATION]\n elif EVENTBRIDGE_CONFIGURATION in external_notifications:\n notifications[EVENTBRIDGE_CONFIGURATION] = external_notifications[EVENTBRIDGE_CONFIGURATION]\n\n return notifications\n\n\ndef find_external_notifications(bucket, stack_id):\n existing_notifications = get_bucket_notification_configuration(bucket)\n external_notifications = {}\n for t in CONFIGURATION_TYPES:\n # if the notification was created by us, we know what id to expect\n # so we can filter by it.\n external_notifications[t] = [n for n in existing_notifications.get(t, []) if not n['Id'].startswith(f\"{stack_id}-\")]\n\n # always treat EventBridge configuration as an external config if it already exists\n # as there is no way to determine whether it's managed by us or not\n if EVENTBRIDGE_CONFIGURATION in existing_notifications:\n external_notifications[EVENTBRIDGE_CONFIGURATION] = existing_notifications[EVENTBRIDGE_CONFIGURATION]\n\n return external_notifications\n\n\ndef get_bucket_notification_configuration(bucket):\n return s3.get_bucket_notification_configuration(Bucket=bucket)\n\n\ndef put_bucket_notification_configuration(bucket, notification_configuration):\n s3.put_bucket_notification_configuration(Bucket=bucket, NotificationConfiguration=notification_configuration)\n\n\ndef submit_response(event: dict, context, response_status: str, error_message: str):\n response_body = json.dumps(\n {\n \"Status\": response_status,\n \"Reason\": f\"{error_message}See the details in CloudWatch Log Stream: {context.log_stream_name}\",\n \"PhysicalResourceId\": event.get(\"PhysicalResourceId\") or event[\"LogicalResourceId\"],\n \"StackId\": event[\"StackId\"],\n \"RequestId\": event[\"RequestId\"],\n \"LogicalResourceId\": event[\"LogicalResourceId\"],\n \"NoEcho\": False,\n }\n ).encode(\"utf-8\")\n headers = {\"content-type\": \"\", \"content-length\": str(len(response_body))}\n try:\n req = urllib.request.Request(url=event[\"ResponseURL\"], headers=headers, data=response_body, method=\"PUT\")\n with urllib.request.urlopen(req) as response:\n print(response.read().decode(\"utf-8\"))\n print(\"Status code: \" + response.reason)\n except Exception as e:\n print(\"send(..) failed executing request.urlopen(..): \" + str(e))\n" + "ZipFile": "import boto3 # type: ignore\nimport json\nimport logging\nimport urllib.request\n\ns3 = boto3.client(\"s3\")\n\nCONFIGURATION_TYPES = [\"TopicConfigurations\", \"QueueConfigurations\", \"LambdaFunctionConfigurations\"]\n\ndef handler(event: dict, context):\n response_status = \"SUCCESS\"\n error_message = \"\"\n try:\n props = event[\"ResourceProperties\"]\n bucket = props[\"BucketName\"]\n notification_configuration = props[\"NotificationConfiguration\"]\n request_type = event[\"RequestType\"]\n managed = props.get('Managed', 'true').lower() == 'true'\n stack_id = event['StackId']\n\n if managed:\n config = handle_managed(request_type, notification_configuration)\n else:\n config = handle_unmanaged(bucket, stack_id, request_type, notification_configuration)\n\n put_bucket_notification_configuration(bucket, config)\n except Exception as e:\n logging.exception(\"Failed to put bucket notification configuration\")\n response_status = \"FAILED\"\n error_message = f\"Error: {str(e)}. \"\n finally:\n submit_response(event, context, response_status, error_message)\n\n\ndef handle_managed(request_type, notification_configuration):\n if request_type == 'Delete':\n return {}\n return notification_configuration\n\n\ndef handle_unmanaged(bucket, stack_id, request_type, notification_configuration):\n\n # find external notifications\n external_notifications = find_external_notifications(bucket, stack_id)\n\n # if delete, that's all we need\n if request_type == 'Delete':\n return external_notifications\n\n def with_id(notification):\n notification['Id'] = f\"{stack_id}-{hash(json.dumps(notification, sort_keys=True))}\"\n return notification\n\n # otherwise, merge external with incoming config and augment with id\n notifications = {}\n for t in CONFIGURATION_TYPES:\n external = external_notifications.get(t, [])\n incoming = [with_id(n) for n in notification_configuration.get(t, [])]\n notifications[t] = external + incoming\n return notifications\n\n\ndef find_external_notifications(bucket, stack_id):\n existing_notifications = get_bucket_notification_configuration(bucket)\n external_notifications = {}\n for t in CONFIGURATION_TYPES:\n # if the notification was created by us, we know what id to expect\n # so we can filter by it.\n external_notifications[t] = [n for n in existing_notifications.get(t, []) if not n['Id'].startswith(f\"{stack_id}-\")]\n\n return external_notifications\n\n\ndef get_bucket_notification_configuration(bucket):\n return s3.get_bucket_notification_configuration(Bucket=bucket)\n\n\ndef put_bucket_notification_configuration(bucket, notification_configuration):\n s3.put_bucket_notification_configuration(Bucket=bucket, NotificationConfiguration=notification_configuration)\n\n\ndef submit_response(event: dict, context, response_status: str, error_message: str):\n response_body = json.dumps(\n {\n \"Status\": response_status,\n \"Reason\": f\"{error_message}See the details in CloudWatch Log Stream: {context.log_stream_name}\",\n \"PhysicalResourceId\": event.get(\"PhysicalResourceId\") or event[\"LogicalResourceId\"],\n \"StackId\": event[\"StackId\"],\n \"RequestId\": event[\"RequestId\"],\n \"LogicalResourceId\": event[\"LogicalResourceId\"],\n \"NoEcho\": False,\n }\n ).encode(\"utf-8\")\n headers = {\"content-type\": \"\", \"content-length\": str(len(response_body))}\n try:\n req = urllib.request.Request(url=event[\"ResponseURL\"], headers=headers, data=response_body, method=\"PUT\")\n with urllib.request.urlopen(req) as response:\n print(response.read().decode(\"utf-8\"))\n print(\"Status code: \" + response.reason)\n except Exception as e:\n print(\"send(..) failed executing request.urlopen(..): \" + str(e))\n" }, "Handler": "index.handler", "Role": { @@ -195,4 +195,4 @@ ] } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3-notifications/test/integ.notifications.expected.json b/packages/@aws-cdk/aws-s3-notifications/test/integ.notifications.expected.json index a467d8e2d5a04..9026931306ab4 100644 --- a/packages/@aws-cdk/aws-s3-notifications/test/integ.notifications.expected.json +++ b/packages/@aws-cdk/aws-s3-notifications/test/integ.notifications.expected.json @@ -247,7 +247,7 @@ "Properties": { "Description": "AWS CloudFormation handler for \"Custom::S3BucketNotifications\" resources (@aws-cdk/aws-s3)", "Code": { - "ZipFile": "import boto3 # type: ignore\nimport json\nimport logging\nimport urllib.request\n\ns3 = boto3.client(\"s3\")\n\nEVENTBRIDGE_CONFIGURATION = 'EventBridgeConfiguration'\n\nCONFIGURATION_TYPES = [\"TopicConfigurations\", \"QueueConfigurations\", \"LambdaFunctionConfigurations\"]\n\ndef handler(event: dict, context):\n response_status = \"SUCCESS\"\n error_message = \"\"\n try:\n props = event[\"ResourceProperties\"]\n bucket = props[\"BucketName\"]\n notification_configuration = props[\"NotificationConfiguration\"]\n request_type = event[\"RequestType\"]\n managed = props.get('Managed', 'true').lower() == 'true'\n stack_id = event['StackId']\n\n if managed:\n config = handle_managed(request_type, notification_configuration)\n else:\n config = handle_unmanaged(bucket, stack_id, request_type, notification_configuration)\n\n put_bucket_notification_configuration(bucket, config)\n except Exception as e:\n logging.exception(\"Failed to put bucket notification configuration\")\n response_status = \"FAILED\"\n error_message = f\"Error: {str(e)}. \"\n finally:\n submit_response(event, context, response_status, error_message)\n\n\ndef handle_managed(request_type, notification_configuration):\n if request_type == 'Delete':\n return {}\n return notification_configuration\n\n\ndef handle_unmanaged(bucket, stack_id, request_type, notification_configuration):\n\n # find external notifications\n external_notifications = find_external_notifications(bucket, stack_id)\n\n # if delete, that's all we need\n if request_type == 'Delete':\n return external_notifications\n\n def with_id(notification):\n notification['Id'] = f\"{stack_id}-{hash(json.dumps(notification, sort_keys=True))}\"\n return notification\n\n # otherwise, merge external with incoming config and augment with id\n notifications = {}\n for t in CONFIGURATION_TYPES:\n external = external_notifications.get(t, [])\n incoming = [with_id(n) for n in notification_configuration.get(t, [])]\n notifications[t] = external + incoming\n\n # EventBridge configuration is a special case because it's just an empty object if it exists\n if EVENTBRIDGE_CONFIGURATION in notification_configuration:\n notifications[EVENTBRIDGE_CONFIGURATION] = notification_configuration[EVENTBRIDGE_CONFIGURATION]\n elif EVENTBRIDGE_CONFIGURATION in external_notifications:\n notifications[EVENTBRIDGE_CONFIGURATION] = external_notifications[EVENTBRIDGE_CONFIGURATION]\n\n return notifications\n\n\ndef find_external_notifications(bucket, stack_id):\n existing_notifications = get_bucket_notification_configuration(bucket)\n external_notifications = {}\n for t in CONFIGURATION_TYPES:\n # if the notification was created by us, we know what id to expect\n # so we can filter by it.\n external_notifications[t] = [n for n in existing_notifications.get(t, []) if not n['Id'].startswith(f\"{stack_id}-\")]\n\n # always treat EventBridge configuration as an external config if it already exists\n # as there is no way to determine whether it's managed by us or not\n if EVENTBRIDGE_CONFIGURATION in existing_notifications:\n external_notifications[EVENTBRIDGE_CONFIGURATION] = existing_notifications[EVENTBRIDGE_CONFIGURATION]\n\n return external_notifications\n\n\ndef get_bucket_notification_configuration(bucket):\n return s3.get_bucket_notification_configuration(Bucket=bucket)\n\n\ndef put_bucket_notification_configuration(bucket, notification_configuration):\n s3.put_bucket_notification_configuration(Bucket=bucket, NotificationConfiguration=notification_configuration)\n\n\ndef submit_response(event: dict, context, response_status: str, error_message: str):\n response_body = json.dumps(\n {\n \"Status\": response_status,\n \"Reason\": f\"{error_message}See the details in CloudWatch Log Stream: {context.log_stream_name}\",\n \"PhysicalResourceId\": event.get(\"PhysicalResourceId\") or event[\"LogicalResourceId\"],\n \"StackId\": event[\"StackId\"],\n \"RequestId\": event[\"RequestId\"],\n \"LogicalResourceId\": event[\"LogicalResourceId\"],\n \"NoEcho\": False,\n }\n ).encode(\"utf-8\")\n headers = {\"content-type\": \"\", \"content-length\": str(len(response_body))}\n try:\n req = urllib.request.Request(url=event[\"ResponseURL\"], headers=headers, data=response_body, method=\"PUT\")\n with urllib.request.urlopen(req) as response:\n print(response.read().decode(\"utf-8\"))\n print(\"Status code: \" + response.reason)\n except Exception as e:\n print(\"send(..) failed executing request.urlopen(..): \" + str(e))\n" + "ZipFile": "import boto3 # type: ignore\nimport json\nimport logging\nimport urllib.request\n\ns3 = boto3.client(\"s3\")\n\nCONFIGURATION_TYPES = [\"TopicConfigurations\", \"QueueConfigurations\", \"LambdaFunctionConfigurations\"]\n\ndef handler(event: dict, context):\n response_status = \"SUCCESS\"\n error_message = \"\"\n try:\n props = event[\"ResourceProperties\"]\n bucket = props[\"BucketName\"]\n notification_configuration = props[\"NotificationConfiguration\"]\n request_type = event[\"RequestType\"]\n managed = props.get('Managed', 'true').lower() == 'true'\n stack_id = event['StackId']\n\n if managed:\n config = handle_managed(request_type, notification_configuration)\n else:\n config = handle_unmanaged(bucket, stack_id, request_type, notification_configuration)\n\n put_bucket_notification_configuration(bucket, config)\n except Exception as e:\n logging.exception(\"Failed to put bucket notification configuration\")\n response_status = \"FAILED\"\n error_message = f\"Error: {str(e)}. \"\n finally:\n submit_response(event, context, response_status, error_message)\n\n\ndef handle_managed(request_type, notification_configuration):\n if request_type == 'Delete':\n return {}\n return notification_configuration\n\n\ndef handle_unmanaged(bucket, stack_id, request_type, notification_configuration):\n\n # find external notifications\n external_notifications = find_external_notifications(bucket, stack_id)\n\n # if delete, that's all we need\n if request_type == 'Delete':\n return external_notifications\n\n def with_id(notification):\n notification['Id'] = f\"{stack_id}-{hash(json.dumps(notification, sort_keys=True))}\"\n return notification\n\n # otherwise, merge external with incoming config and augment with id\n notifications = {}\n for t in CONFIGURATION_TYPES:\n external = external_notifications.get(t, [])\n incoming = [with_id(n) for n in notification_configuration.get(t, [])]\n notifications[t] = external + incoming\n return notifications\n\n\ndef find_external_notifications(bucket, stack_id):\n existing_notifications = get_bucket_notification_configuration(bucket)\n external_notifications = {}\n for t in CONFIGURATION_TYPES:\n # if the notification was created by us, we know what id to expect\n # so we can filter by it.\n external_notifications[t] = [n for n in existing_notifications.get(t, []) if not n['Id'].startswith(f\"{stack_id}-\")]\n\n return external_notifications\n\n\ndef get_bucket_notification_configuration(bucket):\n return s3.get_bucket_notification_configuration(Bucket=bucket)\n\n\ndef put_bucket_notification_configuration(bucket, notification_configuration):\n s3.put_bucket_notification_configuration(Bucket=bucket, NotificationConfiguration=notification_configuration)\n\n\ndef submit_response(event: dict, context, response_status: str, error_message: str):\n response_body = json.dumps(\n {\n \"Status\": response_status,\n \"Reason\": f\"{error_message}See the details in CloudWatch Log Stream: {context.log_stream_name}\",\n \"PhysicalResourceId\": event.get(\"PhysicalResourceId\") or event[\"LogicalResourceId\"],\n \"StackId\": event[\"StackId\"],\n \"RequestId\": event[\"RequestId\"],\n \"LogicalResourceId\": event[\"LogicalResourceId\"],\n \"NoEcho\": False,\n }\n ).encode(\"utf-8\")\n headers = {\"content-type\": \"\", \"content-length\": str(len(response_body))}\n try:\n req = urllib.request.Request(url=event[\"ResponseURL\"], headers=headers, data=response_body, method=\"PUT\")\n with urllib.request.urlopen(req) as response:\n print(response.read().decode(\"utf-8\"))\n print(\"Status code: \" + response.reason)\n except Exception as e:\n print(\"send(..) failed executing request.urlopen(..): \" + str(e))\n" }, "Handler": "index.handler", "Role": { diff --git a/packages/@aws-cdk/aws-s3-notifications/test/lambda/integ.bucket-notifications.expected.json b/packages/@aws-cdk/aws-s3-notifications/test/lambda/integ.bucket-notifications.expected.json index 71d8c5be9f84f..731effea95a53 100644 --- a/packages/@aws-cdk/aws-s3-notifications/test/lambda/integ.bucket-notifications.expected.json +++ b/packages/@aws-cdk/aws-s3-notifications/test/lambda/integ.bucket-notifications.expected.json @@ -114,7 +114,7 @@ ] }, "Handler": "index.handler", - "Runtime": "nodejs14.x" + "Runtime": "nodejs10.x" }, "DependsOn": [ "MyFunctionServiceRole3C357FF2" @@ -237,7 +237,7 @@ "Properties": { "Description": "AWS CloudFormation handler for \"Custom::S3BucketNotifications\" resources (@aws-cdk/aws-s3)", "Code": { - "ZipFile": "import boto3 # type: ignore\nimport json\nimport logging\nimport urllib.request\n\ns3 = boto3.client(\"s3\")\n\nEVENTBRIDGE_CONFIGURATION = 'EventBridgeConfiguration'\n\nCONFIGURATION_TYPES = [\"TopicConfigurations\", \"QueueConfigurations\", \"LambdaFunctionConfigurations\"]\n\ndef handler(event: dict, context):\n response_status = \"SUCCESS\"\n error_message = \"\"\n try:\n props = event[\"ResourceProperties\"]\n bucket = props[\"BucketName\"]\n notification_configuration = props[\"NotificationConfiguration\"]\n request_type = event[\"RequestType\"]\n managed = props.get('Managed', 'true').lower() == 'true'\n stack_id = event['StackId']\n\n if managed:\n config = handle_managed(request_type, notification_configuration)\n else:\n config = handle_unmanaged(bucket, stack_id, request_type, notification_configuration)\n\n put_bucket_notification_configuration(bucket, config)\n except Exception as e:\n logging.exception(\"Failed to put bucket notification configuration\")\n response_status = \"FAILED\"\n error_message = f\"Error: {str(e)}. \"\n finally:\n submit_response(event, context, response_status, error_message)\n\n\ndef handle_managed(request_type, notification_configuration):\n if request_type == 'Delete':\n return {}\n return notification_configuration\n\n\ndef handle_unmanaged(bucket, stack_id, request_type, notification_configuration):\n\n # find external notifications\n external_notifications = find_external_notifications(bucket, stack_id)\n\n # if delete, that's all we need\n if request_type == 'Delete':\n return external_notifications\n\n def with_id(notification):\n notification['Id'] = f\"{stack_id}-{hash(json.dumps(notification, sort_keys=True))}\"\n return notification\n\n # otherwise, merge external with incoming config and augment with id\n notifications = {}\n for t in CONFIGURATION_TYPES:\n external = external_notifications.get(t, [])\n incoming = [with_id(n) for n in notification_configuration.get(t, [])]\n notifications[t] = external + incoming\n\n # EventBridge configuration is a special case because it's just an empty object if it exists\n if EVENTBRIDGE_CONFIGURATION in notification_configuration:\n notifications[EVENTBRIDGE_CONFIGURATION] = notification_configuration[EVENTBRIDGE_CONFIGURATION]\n elif EVENTBRIDGE_CONFIGURATION in external_notifications:\n notifications[EVENTBRIDGE_CONFIGURATION] = external_notifications[EVENTBRIDGE_CONFIGURATION]\n\n return notifications\n\n\ndef find_external_notifications(bucket, stack_id):\n existing_notifications = get_bucket_notification_configuration(bucket)\n external_notifications = {}\n for t in CONFIGURATION_TYPES:\n # if the notification was created by us, we know what id to expect\n # so we can filter by it.\n external_notifications[t] = [n for n in existing_notifications.get(t, []) if not n['Id'].startswith(f\"{stack_id}-\")]\n\n # always treat EventBridge configuration as an external config if it already exists\n # as there is no way to determine whether it's managed by us or not\n if EVENTBRIDGE_CONFIGURATION in existing_notifications:\n external_notifications[EVENTBRIDGE_CONFIGURATION] = existing_notifications[EVENTBRIDGE_CONFIGURATION]\n\n return external_notifications\n\n\ndef get_bucket_notification_configuration(bucket):\n return s3.get_bucket_notification_configuration(Bucket=bucket)\n\n\ndef put_bucket_notification_configuration(bucket, notification_configuration):\n s3.put_bucket_notification_configuration(Bucket=bucket, NotificationConfiguration=notification_configuration)\n\n\ndef submit_response(event: dict, context, response_status: str, error_message: str):\n response_body = json.dumps(\n {\n \"Status\": response_status,\n \"Reason\": f\"{error_message}See the details in CloudWatch Log Stream: {context.log_stream_name}\",\n \"PhysicalResourceId\": event.get(\"PhysicalResourceId\") or event[\"LogicalResourceId\"],\n \"StackId\": event[\"StackId\"],\n \"RequestId\": event[\"RequestId\"],\n \"LogicalResourceId\": event[\"LogicalResourceId\"],\n \"NoEcho\": False,\n }\n ).encode(\"utf-8\")\n headers = {\"content-type\": \"\", \"content-length\": str(len(response_body))}\n try:\n req = urllib.request.Request(url=event[\"ResponseURL\"], headers=headers, data=response_body, method=\"PUT\")\n with urllib.request.urlopen(req) as response:\n print(response.read().decode(\"utf-8\"))\n print(\"Status code: \" + response.reason)\n except Exception as e:\n print(\"send(..) failed executing request.urlopen(..): \" + str(e))\n" + "ZipFile": "import boto3 # type: ignore\nimport json\nimport logging\nimport urllib.request\n\ns3 = boto3.client(\"s3\")\n\nCONFIGURATION_TYPES = [\"TopicConfigurations\", \"QueueConfigurations\", \"LambdaFunctionConfigurations\"]\n\ndef handler(event: dict, context):\n response_status = \"SUCCESS\"\n error_message = \"\"\n try:\n props = event[\"ResourceProperties\"]\n bucket = props[\"BucketName\"]\n notification_configuration = props[\"NotificationConfiguration\"]\n request_type = event[\"RequestType\"]\n managed = props.get('Managed', 'true').lower() == 'true'\n stack_id = event['StackId']\n\n if managed:\n config = handle_managed(request_type, notification_configuration)\n else:\n config = handle_unmanaged(bucket, stack_id, request_type, notification_configuration)\n\n put_bucket_notification_configuration(bucket, config)\n except Exception as e:\n logging.exception(\"Failed to put bucket notification configuration\")\n response_status = \"FAILED\"\n error_message = f\"Error: {str(e)}. \"\n finally:\n submit_response(event, context, response_status, error_message)\n\n\ndef handle_managed(request_type, notification_configuration):\n if request_type == 'Delete':\n return {}\n return notification_configuration\n\n\ndef handle_unmanaged(bucket, stack_id, request_type, notification_configuration):\n\n # find external notifications\n external_notifications = find_external_notifications(bucket, stack_id)\n\n # if delete, that's all we need\n if request_type == 'Delete':\n return external_notifications\n\n def with_id(notification):\n notification['Id'] = f\"{stack_id}-{hash(json.dumps(notification, sort_keys=True))}\"\n return notification\n\n # otherwise, merge external with incoming config and augment with id\n notifications = {}\n for t in CONFIGURATION_TYPES:\n external = external_notifications.get(t, [])\n incoming = [with_id(n) for n in notification_configuration.get(t, [])]\n notifications[t] = external + incoming\n return notifications\n\n\ndef find_external_notifications(bucket, stack_id):\n existing_notifications = get_bucket_notification_configuration(bucket)\n external_notifications = {}\n for t in CONFIGURATION_TYPES:\n # if the notification was created by us, we know what id to expect\n # so we can filter by it.\n external_notifications[t] = [n for n in existing_notifications.get(t, []) if not n['Id'].startswith(f\"{stack_id}-\")]\n\n return external_notifications\n\n\ndef get_bucket_notification_configuration(bucket):\n return s3.get_bucket_notification_configuration(Bucket=bucket)\n\n\ndef put_bucket_notification_configuration(bucket, notification_configuration):\n s3.put_bucket_notification_configuration(Bucket=bucket, NotificationConfiguration=notification_configuration)\n\n\ndef submit_response(event: dict, context, response_status: str, error_message: str):\n response_body = json.dumps(\n {\n \"Status\": response_status,\n \"Reason\": f\"{error_message}See the details in CloudWatch Log Stream: {context.log_stream_name}\",\n \"PhysicalResourceId\": event.get(\"PhysicalResourceId\") or event[\"LogicalResourceId\"],\n \"StackId\": event[\"StackId\"],\n \"RequestId\": event[\"RequestId\"],\n \"LogicalResourceId\": event[\"LogicalResourceId\"],\n \"NoEcho\": False,\n }\n ).encode(\"utf-8\")\n headers = {\"content-type\": \"\", \"content-length\": str(len(response_body))}\n try:\n req = urllib.request.Request(url=event[\"ResponseURL\"], headers=headers, data=response_body, method=\"PUT\")\n with urllib.request.urlopen(req) as response:\n print(response.read().decode(\"utf-8\"))\n print(\"Status code: \" + response.reason)\n except Exception as e:\n print(\"send(..) failed executing request.urlopen(..): \" + str(e))\n" }, "Handler": "index.handler", "Role": { diff --git a/packages/@aws-cdk/aws-s3-notifications/test/lambda/integ.bucket-notifications.ts b/packages/@aws-cdk/aws-s3-notifications/test/lambda/integ.bucket-notifications.ts index 1493e29176362..c237b24e896e3 100644 --- a/packages/@aws-cdk/aws-s3-notifications/test/lambda/integ.bucket-notifications.ts +++ b/packages/@aws-cdk/aws-s3-notifications/test/lambda/integ.bucket-notifications.ts @@ -12,7 +12,7 @@ const bucketA = new s3.Bucket(stack, 'MyBucket', { }); const fn = new lambda.Function(stack, 'MyFunction', { - runtime: lambda.Runtime.NODEJS_14_X, + runtime: lambda.Runtime.NODEJS_10_X, handler: 'index.handler', code: lambda.Code.fromInline(`exports.handler = ${handler.toString()}`), }); diff --git a/packages/@aws-cdk/aws-s3-notifications/test/sns/integ.sns-bucket-notifications.expected.json b/packages/@aws-cdk/aws-s3-notifications/test/sns/integ.sns-bucket-notifications.expected.json index c55da2fa8dcb2..47f2a8ea6e0ce 100644 --- a/packages/@aws-cdk/aws-s3-notifications/test/sns/integ.sns-bucket-notifications.expected.json +++ b/packages/@aws-cdk/aws-s3-notifications/test/sns/integ.sns-bucket-notifications.expected.json @@ -195,7 +195,7 @@ "Properties": { "Description": "AWS CloudFormation handler for \"Custom::S3BucketNotifications\" resources (@aws-cdk/aws-s3)", "Code": { - "ZipFile": "import boto3 # type: ignore\nimport json\nimport logging\nimport urllib.request\n\ns3 = boto3.client(\"s3\")\n\nEVENTBRIDGE_CONFIGURATION = 'EventBridgeConfiguration'\n\nCONFIGURATION_TYPES = [\"TopicConfigurations\", \"QueueConfigurations\", \"LambdaFunctionConfigurations\"]\n\ndef handler(event: dict, context):\n response_status = \"SUCCESS\"\n error_message = \"\"\n try:\n props = event[\"ResourceProperties\"]\n bucket = props[\"BucketName\"]\n notification_configuration = props[\"NotificationConfiguration\"]\n request_type = event[\"RequestType\"]\n managed = props.get('Managed', 'true').lower() == 'true'\n stack_id = event['StackId']\n\n if managed:\n config = handle_managed(request_type, notification_configuration)\n else:\n config = handle_unmanaged(bucket, stack_id, request_type, notification_configuration)\n\n put_bucket_notification_configuration(bucket, config)\n except Exception as e:\n logging.exception(\"Failed to put bucket notification configuration\")\n response_status = \"FAILED\"\n error_message = f\"Error: {str(e)}. \"\n finally:\n submit_response(event, context, response_status, error_message)\n\n\ndef handle_managed(request_type, notification_configuration):\n if request_type == 'Delete':\n return {}\n return notification_configuration\n\n\ndef handle_unmanaged(bucket, stack_id, request_type, notification_configuration):\n\n # find external notifications\n external_notifications = find_external_notifications(bucket, stack_id)\n\n # if delete, that's all we need\n if request_type == 'Delete':\n return external_notifications\n\n def with_id(notification):\n notification['Id'] = f\"{stack_id}-{hash(json.dumps(notification, sort_keys=True))}\"\n return notification\n\n # otherwise, merge external with incoming config and augment with id\n notifications = {}\n for t in CONFIGURATION_TYPES:\n external = external_notifications.get(t, [])\n incoming = [with_id(n) for n in notification_configuration.get(t, [])]\n notifications[t] = external + incoming\n\n # EventBridge configuration is a special case because it's just an empty object if it exists\n if EVENTBRIDGE_CONFIGURATION in notification_configuration:\n notifications[EVENTBRIDGE_CONFIGURATION] = notification_configuration[EVENTBRIDGE_CONFIGURATION]\n elif EVENTBRIDGE_CONFIGURATION in external_notifications:\n notifications[EVENTBRIDGE_CONFIGURATION] = external_notifications[EVENTBRIDGE_CONFIGURATION]\n\n return notifications\n\n\ndef find_external_notifications(bucket, stack_id):\n existing_notifications = get_bucket_notification_configuration(bucket)\n external_notifications = {}\n for t in CONFIGURATION_TYPES:\n # if the notification was created by us, we know what id to expect\n # so we can filter by it.\n external_notifications[t] = [n for n in existing_notifications.get(t, []) if not n['Id'].startswith(f\"{stack_id}-\")]\n\n # always treat EventBridge configuration as an external config if it already exists\n # as there is no way to determine whether it's managed by us or not\n if EVENTBRIDGE_CONFIGURATION in existing_notifications:\n external_notifications[EVENTBRIDGE_CONFIGURATION] = existing_notifications[EVENTBRIDGE_CONFIGURATION]\n\n return external_notifications\n\n\ndef get_bucket_notification_configuration(bucket):\n return s3.get_bucket_notification_configuration(Bucket=bucket)\n\n\ndef put_bucket_notification_configuration(bucket, notification_configuration):\n s3.put_bucket_notification_configuration(Bucket=bucket, NotificationConfiguration=notification_configuration)\n\n\ndef submit_response(event: dict, context, response_status: str, error_message: str):\n response_body = json.dumps(\n {\n \"Status\": response_status,\n \"Reason\": f\"{error_message}See the details in CloudWatch Log Stream: {context.log_stream_name}\",\n \"PhysicalResourceId\": event.get(\"PhysicalResourceId\") or event[\"LogicalResourceId\"],\n \"StackId\": event[\"StackId\"],\n \"RequestId\": event[\"RequestId\"],\n \"LogicalResourceId\": event[\"LogicalResourceId\"],\n \"NoEcho\": False,\n }\n ).encode(\"utf-8\")\n headers = {\"content-type\": \"\", \"content-length\": str(len(response_body))}\n try:\n req = urllib.request.Request(url=event[\"ResponseURL\"], headers=headers, data=response_body, method=\"PUT\")\n with urllib.request.urlopen(req) as response:\n print(response.read().decode(\"utf-8\"))\n print(\"Status code: \" + response.reason)\n except Exception as e:\n print(\"send(..) failed executing request.urlopen(..): \" + str(e))\n" + "ZipFile": "import boto3 # type: ignore\nimport json\nimport logging\nimport urllib.request\n\ns3 = boto3.client(\"s3\")\n\nCONFIGURATION_TYPES = [\"TopicConfigurations\", \"QueueConfigurations\", \"LambdaFunctionConfigurations\"]\n\ndef handler(event: dict, context):\n response_status = \"SUCCESS\"\n error_message = \"\"\n try:\n props = event[\"ResourceProperties\"]\n bucket = props[\"BucketName\"]\n notification_configuration = props[\"NotificationConfiguration\"]\n request_type = event[\"RequestType\"]\n managed = props.get('Managed', 'true').lower() == 'true'\n stack_id = event['StackId']\n\n if managed:\n config = handle_managed(request_type, notification_configuration)\n else:\n config = handle_unmanaged(bucket, stack_id, request_type, notification_configuration)\n\n put_bucket_notification_configuration(bucket, config)\n except Exception as e:\n logging.exception(\"Failed to put bucket notification configuration\")\n response_status = \"FAILED\"\n error_message = f\"Error: {str(e)}. \"\n finally:\n submit_response(event, context, response_status, error_message)\n\n\ndef handle_managed(request_type, notification_configuration):\n if request_type == 'Delete':\n return {}\n return notification_configuration\n\n\ndef handle_unmanaged(bucket, stack_id, request_type, notification_configuration):\n\n # find external notifications\n external_notifications = find_external_notifications(bucket, stack_id)\n\n # if delete, that's all we need\n if request_type == 'Delete':\n return external_notifications\n\n def with_id(notification):\n notification['Id'] = f\"{stack_id}-{hash(json.dumps(notification, sort_keys=True))}\"\n return notification\n\n # otherwise, merge external with incoming config and augment with id\n notifications = {}\n for t in CONFIGURATION_TYPES:\n external = external_notifications.get(t, [])\n incoming = [with_id(n) for n in notification_configuration.get(t, [])]\n notifications[t] = external + incoming\n return notifications\n\n\ndef find_external_notifications(bucket, stack_id):\n existing_notifications = get_bucket_notification_configuration(bucket)\n external_notifications = {}\n for t in CONFIGURATION_TYPES:\n # if the notification was created by us, we know what id to expect\n # so we can filter by it.\n external_notifications[t] = [n for n in existing_notifications.get(t, []) if not n['Id'].startswith(f\"{stack_id}-\")]\n\n return external_notifications\n\n\ndef get_bucket_notification_configuration(bucket):\n return s3.get_bucket_notification_configuration(Bucket=bucket)\n\n\ndef put_bucket_notification_configuration(bucket, notification_configuration):\n s3.put_bucket_notification_configuration(Bucket=bucket, NotificationConfiguration=notification_configuration)\n\n\ndef submit_response(event: dict, context, response_status: str, error_message: str):\n response_body = json.dumps(\n {\n \"Status\": response_status,\n \"Reason\": f\"{error_message}See the details in CloudWatch Log Stream: {context.log_stream_name}\",\n \"PhysicalResourceId\": event.get(\"PhysicalResourceId\") or event[\"LogicalResourceId\"],\n \"StackId\": event[\"StackId\"],\n \"RequestId\": event[\"RequestId\"],\n \"LogicalResourceId\": event[\"LogicalResourceId\"],\n \"NoEcho\": False,\n }\n ).encode(\"utf-8\")\n headers = {\"content-type\": \"\", \"content-length\": str(len(response_body))}\n try:\n req = urllib.request.Request(url=event[\"ResponseURL\"], headers=headers, data=response_body, method=\"PUT\")\n with urllib.request.urlopen(req) as response:\n print(response.read().decode(\"utf-8\"))\n print(\"Status code: \" + response.reason)\n except Exception as e:\n print(\"send(..) failed executing request.urlopen(..): \" + str(e))\n" }, "Handler": "index.handler", "Role": { diff --git a/packages/@aws-cdk/aws-s3-notifications/test/sqs/integ.bucket-notifications.expected.json b/packages/@aws-cdk/aws-s3-notifications/test/sqs/integ.bucket-notifications.expected.json index 006856d6de207..229b916beac4b 100644 --- a/packages/@aws-cdk/aws-s3-notifications/test/sqs/integ.bucket-notifications.expected.json +++ b/packages/@aws-cdk/aws-s3-notifications/test/sqs/integ.bucket-notifications.expected.json @@ -184,7 +184,7 @@ "Properties": { "Description": "AWS CloudFormation handler for \"Custom::S3BucketNotifications\" resources (@aws-cdk/aws-s3)", "Code": { - "ZipFile": "import boto3 # type: ignore\nimport json\nimport logging\nimport urllib.request\n\ns3 = boto3.client(\"s3\")\n\nEVENTBRIDGE_CONFIGURATION = 'EventBridgeConfiguration'\n\nCONFIGURATION_TYPES = [\"TopicConfigurations\", \"QueueConfigurations\", \"LambdaFunctionConfigurations\"]\n\ndef handler(event: dict, context):\n response_status = \"SUCCESS\"\n error_message = \"\"\n try:\n props = event[\"ResourceProperties\"]\n bucket = props[\"BucketName\"]\n notification_configuration = props[\"NotificationConfiguration\"]\n request_type = event[\"RequestType\"]\n managed = props.get('Managed', 'true').lower() == 'true'\n stack_id = event['StackId']\n\n if managed:\n config = handle_managed(request_type, notification_configuration)\n else:\n config = handle_unmanaged(bucket, stack_id, request_type, notification_configuration)\n\n put_bucket_notification_configuration(bucket, config)\n except Exception as e:\n logging.exception(\"Failed to put bucket notification configuration\")\n response_status = \"FAILED\"\n error_message = f\"Error: {str(e)}. \"\n finally:\n submit_response(event, context, response_status, error_message)\n\n\ndef handle_managed(request_type, notification_configuration):\n if request_type == 'Delete':\n return {}\n return notification_configuration\n\n\ndef handle_unmanaged(bucket, stack_id, request_type, notification_configuration):\n\n # find external notifications\n external_notifications = find_external_notifications(bucket, stack_id)\n\n # if delete, that's all we need\n if request_type == 'Delete':\n return external_notifications\n\n def with_id(notification):\n notification['Id'] = f\"{stack_id}-{hash(json.dumps(notification, sort_keys=True))}\"\n return notification\n\n # otherwise, merge external with incoming config and augment with id\n notifications = {}\n for t in CONFIGURATION_TYPES:\n external = external_notifications.get(t, [])\n incoming = [with_id(n) for n in notification_configuration.get(t, [])]\n notifications[t] = external + incoming\n\n # EventBridge configuration is a special case because it's just an empty object if it exists\n if EVENTBRIDGE_CONFIGURATION in notification_configuration:\n notifications[EVENTBRIDGE_CONFIGURATION] = notification_configuration[EVENTBRIDGE_CONFIGURATION]\n elif EVENTBRIDGE_CONFIGURATION in external_notifications:\n notifications[EVENTBRIDGE_CONFIGURATION] = external_notifications[EVENTBRIDGE_CONFIGURATION]\n\n return notifications\n\n\ndef find_external_notifications(bucket, stack_id):\n existing_notifications = get_bucket_notification_configuration(bucket)\n external_notifications = {}\n for t in CONFIGURATION_TYPES:\n # if the notification was created by us, we know what id to expect\n # so we can filter by it.\n external_notifications[t] = [n for n in existing_notifications.get(t, []) if not n['Id'].startswith(f\"{stack_id}-\")]\n\n # always treat EventBridge configuration as an external config if it already exists\n # as there is no way to determine whether it's managed by us or not\n if EVENTBRIDGE_CONFIGURATION in existing_notifications:\n external_notifications[EVENTBRIDGE_CONFIGURATION] = existing_notifications[EVENTBRIDGE_CONFIGURATION]\n\n return external_notifications\n\n\ndef get_bucket_notification_configuration(bucket):\n return s3.get_bucket_notification_configuration(Bucket=bucket)\n\n\ndef put_bucket_notification_configuration(bucket, notification_configuration):\n s3.put_bucket_notification_configuration(Bucket=bucket, NotificationConfiguration=notification_configuration)\n\n\ndef submit_response(event: dict, context, response_status: str, error_message: str):\n response_body = json.dumps(\n {\n \"Status\": response_status,\n \"Reason\": f\"{error_message}See the details in CloudWatch Log Stream: {context.log_stream_name}\",\n \"PhysicalResourceId\": event.get(\"PhysicalResourceId\") or event[\"LogicalResourceId\"],\n \"StackId\": event[\"StackId\"],\n \"RequestId\": event[\"RequestId\"],\n \"LogicalResourceId\": event[\"LogicalResourceId\"],\n \"NoEcho\": False,\n }\n ).encode(\"utf-8\")\n headers = {\"content-type\": \"\", \"content-length\": str(len(response_body))}\n try:\n req = urllib.request.Request(url=event[\"ResponseURL\"], headers=headers, data=response_body, method=\"PUT\")\n with urllib.request.urlopen(req) as response:\n print(response.read().decode(\"utf-8\"))\n print(\"Status code: \" + response.reason)\n except Exception as e:\n print(\"send(..) failed executing request.urlopen(..): \" + str(e))\n" + "ZipFile": "import boto3 # type: ignore\nimport json\nimport logging\nimport urllib.request\n\ns3 = boto3.client(\"s3\")\n\nCONFIGURATION_TYPES = [\"TopicConfigurations\", \"QueueConfigurations\", \"LambdaFunctionConfigurations\"]\n\ndef handler(event: dict, context):\n response_status = \"SUCCESS\"\n error_message = \"\"\n try:\n props = event[\"ResourceProperties\"]\n bucket = props[\"BucketName\"]\n notification_configuration = props[\"NotificationConfiguration\"]\n request_type = event[\"RequestType\"]\n managed = props.get('Managed', 'true').lower() == 'true'\n stack_id = event['StackId']\n\n if managed:\n config = handle_managed(request_type, notification_configuration)\n else:\n config = handle_unmanaged(bucket, stack_id, request_type, notification_configuration)\n\n put_bucket_notification_configuration(bucket, config)\n except Exception as e:\n logging.exception(\"Failed to put bucket notification configuration\")\n response_status = \"FAILED\"\n error_message = f\"Error: {str(e)}. \"\n finally:\n submit_response(event, context, response_status, error_message)\n\n\ndef handle_managed(request_type, notification_configuration):\n if request_type == 'Delete':\n return {}\n return notification_configuration\n\n\ndef handle_unmanaged(bucket, stack_id, request_type, notification_configuration):\n\n # find external notifications\n external_notifications = find_external_notifications(bucket, stack_id)\n\n # if delete, that's all we need\n if request_type == 'Delete':\n return external_notifications\n\n def with_id(notification):\n notification['Id'] = f\"{stack_id}-{hash(json.dumps(notification, sort_keys=True))}\"\n return notification\n\n # otherwise, merge external with incoming config and augment with id\n notifications = {}\n for t in CONFIGURATION_TYPES:\n external = external_notifications.get(t, [])\n incoming = [with_id(n) for n in notification_configuration.get(t, [])]\n notifications[t] = external + incoming\n return notifications\n\n\ndef find_external_notifications(bucket, stack_id):\n existing_notifications = get_bucket_notification_configuration(bucket)\n external_notifications = {}\n for t in CONFIGURATION_TYPES:\n # if the notification was created by us, we know what id to expect\n # so we can filter by it.\n external_notifications[t] = [n for n in existing_notifications.get(t, []) if not n['Id'].startswith(f\"{stack_id}-\")]\n\n return external_notifications\n\n\ndef get_bucket_notification_configuration(bucket):\n return s3.get_bucket_notification_configuration(Bucket=bucket)\n\n\ndef put_bucket_notification_configuration(bucket, notification_configuration):\n s3.put_bucket_notification_configuration(Bucket=bucket, NotificationConfiguration=notification_configuration)\n\n\ndef submit_response(event: dict, context, response_status: str, error_message: str):\n response_body = json.dumps(\n {\n \"Status\": response_status,\n \"Reason\": f\"{error_message}See the details in CloudWatch Log Stream: {context.log_stream_name}\",\n \"PhysicalResourceId\": event.get(\"PhysicalResourceId\") or event[\"LogicalResourceId\"],\n \"StackId\": event[\"StackId\"],\n \"RequestId\": event[\"RequestId\"],\n \"LogicalResourceId\": event[\"LogicalResourceId\"],\n \"NoEcho\": False,\n }\n ).encode(\"utf-8\")\n headers = {\"content-type\": \"\", \"content-length\": str(len(response_body))}\n try:\n req = urllib.request.Request(url=event[\"ResponseURL\"], headers=headers, data=response_body, method=\"PUT\")\n with urllib.request.urlopen(req) as response:\n print(response.read().decode(\"utf-8\"))\n print(\"Status code: \" + response.reason)\n except Exception as e:\n print(\"send(..) failed executing request.urlopen(..): \" + str(e))\n" }, "Handler": "index.handler", "Role": { diff --git a/packages/@aws-cdk/aws-s3/README.md b/packages/@aws-cdk/aws-s3/README.md index 02c1b86be2855..4f2592b9c633a 100644 --- a/packages/@aws-cdk/aws-s3/README.md +++ b/packages/@aws-cdk/aws-s3/README.md @@ -252,21 +252,6 @@ bucket.addEventNotification(s3.EventType.OBJECT_CREATED, new s3n.SnsDestination( [S3 Bucket Notifications]: https://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html -### EventBridge notifications - -Amazon S3 can send events to Amazon EventBridge whenever certain events happen in your bucket. -Unlike other destinations, you don't need to select which event types you want to deliver. - -The following example will enable EventBridge notifications: - -```ts -const bucket = new s3.Bucket(this, 'MyEventBridgeBucket', { - eventBridgeEnabled: true, -}); -``` - -[S3 EventBridge notifications]: https://docs.aws.amazon.com/AmazonS3/latest/userguide/EventBridge.html - ## Block Public Access Use `blockPublicAccess` to specify [block public access settings] on the bucket. diff --git a/packages/@aws-cdk/aws-s3/lib/bucket.ts b/packages/@aws-cdk/aws-s3/lib/bucket.ts index 387139d718ffb..ea25e2c01467b 100644 --- a/packages/@aws-cdk/aws-s3/lib/bucket.ts +++ b/packages/@aws-cdk/aws-s3/lib/bucket.ts @@ -865,10 +865,6 @@ export abstract class BucketBase extends Resource implements IBucket { return this.addEventNotification(EventType.OBJECT_REMOVED, dest, ...filters); } - protected enableEventBridgeNotification() { - this.notifications.enableEventBridgeNotification(); - } - private get writeActions(): string[] { return [ ...perms.BUCKET_DELETE_ACTIONS, @@ -1345,13 +1341,6 @@ export interface BucketProps { */ readonly versioned?: boolean; - /** - * Whether this bucket should send notifications to Amazon EventBridge or not. - * - * @default false - */ - readonly eventBridgeEnabled?: boolean; - /** * Rules that define how Amazon S3 manages objects during their lifetime. * @@ -1632,7 +1621,6 @@ export class Bucket extends BucketBase { private accessControl?: BucketAccessControl; private readonly lifecycleRules: LifecycleRule[] = []; private readonly versioned?: boolean; - private readonly eventBridgeEnabled?: boolean; private readonly metrics: BucketMetrics[] = []; private readonly cors: CorsRule[] = []; private readonly inventories: Inventory[] = []; @@ -1672,7 +1660,6 @@ export class Bucket extends BucketBase { this.versioned = props.versioned; this.encryptionKey = encryptionKey; - this.eventBridgeEnabled = props.eventBridgeEnabled; this.bucketName = this.getResourceNameAttribute(resource.ref); this.bucketArn = this.getResourceArnAttribute(resource.attrArn, { @@ -1723,10 +1710,6 @@ export class Bucket extends BucketBase { this.enableAutoDeleteObjects(); } - - if (this.eventBridgeEnabled) { - this.enableEventBridgeNotification(); - } } /** diff --git a/packages/@aws-cdk/aws-s3/lib/notifications-resource/lambda/index.py b/packages/@aws-cdk/aws-s3/lib/notifications-resource/lambda/index.py index d46989246e827..2551398d74958 100644 --- a/packages/@aws-cdk/aws-s3/lib/notifications-resource/lambda/index.py +++ b/packages/@aws-cdk/aws-s3/lib/notifications-resource/lambda/index.py @@ -5,8 +5,6 @@ s3 = boto3.client("s3") -EVENTBRIDGE_CONFIGURATION = 'EventBridgeConfiguration' - CONFIGURATION_TYPES = ["TopicConfigurations", "QueueConfigurations", "LambdaFunctionConfigurations"] def handler(event: dict, context): @@ -59,13 +57,6 @@ def with_id(notification): external = external_notifications.get(t, []) incoming = [with_id(n) for n in notification_configuration.get(t, [])] notifications[t] = external + incoming - - # EventBridge configuration is a special case because it's just an empty object if it exists - if EVENTBRIDGE_CONFIGURATION in notification_configuration: - notifications[EVENTBRIDGE_CONFIGURATION] = notification_configuration[EVENTBRIDGE_CONFIGURATION] - elif EVENTBRIDGE_CONFIGURATION in external_notifications: - notifications[EVENTBRIDGE_CONFIGURATION] = external_notifications[EVENTBRIDGE_CONFIGURATION] - return notifications @@ -77,11 +68,6 @@ def find_external_notifications(bucket, stack_id): # so we can filter by it. external_notifications[t] = [n for n in existing_notifications.get(t, []) if not n['Id'].startswith(f"{stack_id}-")] - # always treat EventBridge configuration as an external config if it already exists - # as there is no way to determine whether it's managed by us or not - if EVENTBRIDGE_CONFIGURATION in existing_notifications: - external_notifications[EVENTBRIDGE_CONFIGURATION] = existing_notifications[EVENTBRIDGE_CONFIGURATION] - return external_notifications diff --git a/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource.ts b/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource.ts index 7ac760d006da9..d5190f1a6a913 100644 --- a/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource.ts +++ b/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource.ts @@ -31,7 +31,6 @@ interface NotificationsProps { * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-bucket-notificationconfig.html */ export class BucketNotifications extends Construct { - private eventBridgeEnabled = false; private readonly lambdaNotifications = new Array(); private readonly queueNotifications = new Array(); private readonly topicNotifications = new Array(); @@ -88,14 +87,8 @@ export class BucketNotifications extends Construct { } } - public enableEventBridgeNotification() { - this.createResourceOnce(); - this.eventBridgeEnabled = true; - } - private renderNotificationConfiguration(): NotificationConfiguration { return { - EventBridgeConfiguration: this.eventBridgeEnabled ? {} : undefined, LambdaFunctionConfigurations: this.lambdaNotifications.length > 0 ? this.lambdaNotifications : undefined, QueueConfigurations: this.queueNotifications.length > 0 ? this.queueNotifications : undefined, TopicConfigurations: this.topicNotifications.length > 0 ? this.topicNotifications : undefined, @@ -174,7 +167,6 @@ function renderFilters(filters?: NotificationKeyFilter[]): Filter | undefined { } interface NotificationConfiguration { - EventBridgeConfiguration?: EventBridgeConfiguration; LambdaFunctionConfigurations?: LambdaFunctionConfiguration[]; QueueConfigurations?: QueueConfiguration[]; TopicConfigurations?: TopicConfiguration[]; @@ -186,8 +178,6 @@ interface CommonConfiguration { Filter?: Filter } -interface EventBridgeConfiguration { } - interface LambdaFunctionConfiguration extends CommonConfiguration { LambdaFunctionArn: string; } diff --git a/packages/@aws-cdk/aws-s3/test/integ.bucket.notifications.expected.json b/packages/@aws-cdk/aws-s3/test/integ.bucket.notifications.expected.json deleted file mode 100644 index 4c3711e8018ef..0000000000000 --- a/packages/@aws-cdk/aws-s3/test/integ.bucket.notifications.expected.json +++ /dev/null @@ -1,101 +0,0 @@ -{ - "Resources": { - "MyEventBridgeBucketNotifications19C0453F": { - "Type": "Custom::S3BucketNotifications", - "Properties": { - "ServiceToken": { - "Fn::GetAtt": [ - "BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691", - "Arn" - ] - }, - "BucketName": { - "Ref": "MyEventBridgeBucket1ABD5C2A" - }, - "NotificationConfiguration": { - "EventBridgeConfiguration": {} - }, - "Managed": true - } - }, - "MyEventBridgeBucket1ABD5C2A": { - "Type": "AWS::S3::Bucket", - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, - "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleB6FB88EC": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "lambda.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - }, - "ManagedPolicyArns": [ - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ] - ] - } - ] - } - }, - "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleDefaultPolicy2CF63D36": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": "s3:PutBucketNotification", - "Effect": "Allow", - "Resource": "*" - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleDefaultPolicy2CF63D36", - "Roles": [ - { - "Ref": "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleB6FB88EC" - } - ] - } - }, - "BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Description": "AWS CloudFormation handler for \"Custom::S3BucketNotifications\" resources (@aws-cdk/aws-s3)", - "Code": { - "ZipFile": "import boto3 # type: ignore\nimport json\nimport logging\nimport urllib.request\n\ns3 = boto3.client(\"s3\")\n\nEVENTBRIDGE_CONFIGURATION = 'EventBridgeConfiguration'\n\nCONFIGURATION_TYPES = [\"TopicConfigurations\", \"QueueConfigurations\", \"LambdaFunctionConfigurations\"]\n\ndef handler(event: dict, context):\n response_status = \"SUCCESS\"\n error_message = \"\"\n try:\n props = event[\"ResourceProperties\"]\n bucket = props[\"BucketName\"]\n notification_configuration = props[\"NotificationConfiguration\"]\n request_type = event[\"RequestType\"]\n managed = props.get('Managed', 'true').lower() == 'true'\n stack_id = event['StackId']\n\n if managed:\n config = handle_managed(request_type, notification_configuration)\n else:\n config = handle_unmanaged(bucket, stack_id, request_type, notification_configuration)\n\n put_bucket_notification_configuration(bucket, config)\n except Exception as e:\n logging.exception(\"Failed to put bucket notification configuration\")\n response_status = \"FAILED\"\n error_message = f\"Error: {str(e)}. \"\n finally:\n submit_response(event, context, response_status, error_message)\n\n\ndef handle_managed(request_type, notification_configuration):\n if request_type == 'Delete':\n return {}\n return notification_configuration\n\n\ndef handle_unmanaged(bucket, stack_id, request_type, notification_configuration):\n\n # find external notifications\n external_notifications = find_external_notifications(bucket, stack_id)\n\n # if delete, that's all we need\n if request_type == 'Delete':\n return external_notifications\n\n def with_id(notification):\n notification['Id'] = f\"{stack_id}-{hash(json.dumps(notification, sort_keys=True))}\"\n return notification\n\n # otherwise, merge external with incoming config and augment with id\n notifications = {}\n for t in CONFIGURATION_TYPES:\n external = external_notifications.get(t, [])\n incoming = [with_id(n) for n in notification_configuration.get(t, [])]\n notifications[t] = external + incoming\n\n # EventBridge configuration is a special case because it's just an empty object if it exists\n if EVENTBRIDGE_CONFIGURATION in notification_configuration:\n notifications[EVENTBRIDGE_CONFIGURATION] = notification_configuration[EVENTBRIDGE_CONFIGURATION]\n elif EVENTBRIDGE_CONFIGURATION in external_notifications:\n notifications[EVENTBRIDGE_CONFIGURATION] = external_notifications[EVENTBRIDGE_CONFIGURATION]\n\n return notifications\n\n\ndef find_external_notifications(bucket, stack_id):\n existing_notifications = get_bucket_notification_configuration(bucket)\n external_notifications = {}\n for t in CONFIGURATION_TYPES:\n # if the notification was created by us, we know what id to expect\n # so we can filter by it.\n external_notifications[t] = [n for n in existing_notifications.get(t, []) if not n['Id'].startswith(f\"{stack_id}-\")]\n\n # always treat EventBridge configuration as an external config if it already exists\n # as there is no way to determine whether it's managed by us or not\n if EVENTBRIDGE_CONFIGURATION in existing_notifications:\n external_notifications[EVENTBRIDGE_CONFIGURATION] = existing_notifications[EVENTBRIDGE_CONFIGURATION]\n\n return external_notifications\n\n\ndef get_bucket_notification_configuration(bucket):\n return s3.get_bucket_notification_configuration(Bucket=bucket)\n\n\ndef put_bucket_notification_configuration(bucket, notification_configuration):\n s3.put_bucket_notification_configuration(Bucket=bucket, NotificationConfiguration=notification_configuration)\n\n\ndef submit_response(event: dict, context, response_status: str, error_message: str):\n response_body = json.dumps(\n {\n \"Status\": response_status,\n \"Reason\": f\"{error_message}See the details in CloudWatch Log Stream: {context.log_stream_name}\",\n \"PhysicalResourceId\": event.get(\"PhysicalResourceId\") or event[\"LogicalResourceId\"],\n \"StackId\": event[\"StackId\"],\n \"RequestId\": event[\"RequestId\"],\n \"LogicalResourceId\": event[\"LogicalResourceId\"],\n \"NoEcho\": False,\n }\n ).encode(\"utf-8\")\n headers = {\"content-type\": \"\", \"content-length\": str(len(response_body))}\n try:\n req = urllib.request.Request(url=event[\"ResponseURL\"], headers=headers, data=response_body, method=\"PUT\")\n with urllib.request.urlopen(req) as response:\n print(response.read().decode(\"utf-8\"))\n print(\"Status code: \" + response.reason)\n except Exception as e:\n print(\"send(..) failed executing request.urlopen(..): \" + str(e))\n" - }, - "Handler": "index.handler", - "Role": { - "Fn::GetAtt": [ - "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleB6FB88EC", - "Arn" - ] - }, - "Runtime": "python3.7", - "Timeout": 300 - }, - "DependsOn": [ - "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleDefaultPolicy2CF63D36", - "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleB6FB88EC" - ] - } - } -} diff --git a/packages/@aws-cdk/aws-s3/test/integ.bucket.notifications.ts b/packages/@aws-cdk/aws-s3/test/integ.bucket.notifications.ts deleted file mode 100644 index 3180b491f6250..0000000000000 --- a/packages/@aws-cdk/aws-s3/test/integ.bucket.notifications.ts +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env node -import * as cdk from '@aws-cdk/core'; -import * as s3 from '../lib'; - -const app = new cdk.App(); - -const stack = new cdk.Stack(app, 'aws-cdk-s3-notifications'); - -new s3.Bucket(stack, 'MyEventBridgeBucket', { - eventBridgeEnabled: true, - removalPolicy: cdk.RemovalPolicy.DESTROY, -}); - -app.synth(); diff --git a/packages/@aws-cdk/aws-s3/test/notification.test.ts b/packages/@aws-cdk/aws-s3/test/notification.test.ts index e3e1d81687100..fbc8e1aa45a49 100644 --- a/packages/@aws-cdk/aws-s3/test/notification.test.ts +++ b/packages/@aws-cdk/aws-s3/test/notification.test.ts @@ -122,22 +122,4 @@ describe('notification', () => { }), }, { suffix: '.png' }, { suffix: '.zip' })).toThrow(/suffix rule/); }); - - test('EventBridge notification custom resource', () => { - // GIVEN - const stack = new cdk.Stack(); - - // WHEN - new s3.Bucket(stack, 'MyBucket', { - eventBridgeEnabled: true, - }); - - // THEN - Template.fromStack(stack).resourceCountIs('AWS::S3::Bucket', 1); - Template.fromStack(stack).hasResourceProperties('Custom::S3BucketNotifications', { - NotificationConfiguration: { - EventBridgeConfiguration: {}, - }, - }); - }); }); diff --git a/packages/@aws-cdk/aws-s3/test/notifications-resource-handler/test_index.py b/packages/@aws-cdk/aws-s3/test/notifications-resource-handler/test_index.py index ff79da80ef669..9127677b02675 100644 --- a/packages/@aws-cdk/aws-s3/test/notifications-resource-handler/test_index.py +++ b/packages/@aws-cdk/aws-s3/test/notifications-resource-handler/test_index.py @@ -19,8 +19,6 @@ ) sys.exit(1) -EVENTBRIDGE_CONFIGURATION = 'EventBridgeConfiguration' - CONFIGURATION_TYPES = ["TopicConfigurations", "QueueConfigurations", "LambdaFunctionConfigurations"] @@ -35,16 +33,6 @@ def make_event(request_type: str, managed: bool): }, } -def make_event_with_eventbridge(request_type: str, managed: bool): - return { - "StackId": "StackId", - "RequestType": request_type, - "ResourceProperties": { - "Managed": str(managed), - "BucketName": "BucketName", - "NotificationConfiguration": make_notification_configuration_with_eventbridge(), - }, - } def make_notification_configuration(id_prefix: str = None): def make_id(): @@ -55,11 +43,6 @@ def make_id(): config[t] = [{"Id": make_id()}] return config -def make_notification_configuration_with_eventbridge(id_prefix: str = None): - return {**make_notification_configuration(id_prefix), **make_eventbridge_configuration()} - -def make_eventbridge_configuration(): - return { EVENTBRIDGE_CONFIGURATION: {} } def make_empty_notification_configuration(): config = {} @@ -67,21 +50,11 @@ def make_empty_notification_configuration(): config[t] = [] return config -def make_empty_notification_configuration_with_eventbridge(): - return {**make_empty_notification_configuration(), **make_eventbridge_configuration()} - def merge_notification_configurations(conf1: Dict, conf2: Dict): notifications = {} for t in CONFIGURATION_TYPES: notifications[t] = conf1.get(t, []) + conf2.get(t, []) - - if EVENTBRIDGE_CONFIGURATION in conf1: - notifications[EVENTBRIDGE_CONFIGURATION] = conf1[EVENTBRIDGE_CONFIGURATION] - - if EVENTBRIDGE_CONFIGURATION in conf2: - notifications[EVENTBRIDGE_CONFIGURATION] = conf2[EVENTBRIDGE_CONFIGURATION] - return notifications @@ -140,22 +113,6 @@ def test_create(self, _, get: MagicMock, put: MagicMock): event["ResourceProperties"]["NotificationConfiguration"], ) - @patch("index.put_bucket_notification_configuration") - @patch("index.get_bucket_notification_configuration") - @patch("index.submit_response") - def test_create_with_eventbridge(self, _, get: MagicMock, put: MagicMock): - - get.return_value = {} - - event = make_event_with_eventbridge("Create", False) - - index.handler(event, {}) - - put.assert_called_once_with( - event["ResourceProperties"]["BucketName"], - event["ResourceProperties"]["NotificationConfiguration"], - ) - @patch("index.put_bucket_notification_configuration") @patch("index.get_bucket_notification_configuration") @patch("index.submit_response") @@ -174,46 +131,6 @@ def test_update(self, _, get: MagicMock, put: MagicMock): event["ResourceProperties"]["NotificationConfiguration"], ) - @patch("index.put_bucket_notification_configuration") - @patch("index.get_bucket_notification_configuration") - @patch("index.submit_response") - def test_update_with_eventbridge(self, _, get: MagicMock, put: MagicMock): - - event = make_event_with_eventbridge("Update", False) - - # simulate a previous create operation - current_notifications = make_notification_configuration(f"{event['StackId']}-") - get.return_value = current_notifications - - index.handler(event, {}) - - put.assert_called_once_with( - event["ResourceProperties"]["BucketName"], - event["ResourceProperties"]["NotificationConfiguration"], - ) - - - @patch("index.put_bucket_notification_configuration") - @patch("index.get_bucket_notification_configuration") - @patch("index.submit_response") - def test_update_with_existing_eventbridge(self, _, get: MagicMock, put: MagicMock): - - event = make_event("Update", False) - - # simulate a previous create operation - current_notifications = make_notification_configuration_with_eventbridge(f"{event['StackId']}-") - get.return_value = current_notifications - - index.handler(event, {}) - - put.assert_called_once_with( - event["ResourceProperties"]["BucketName"], - merge_notification_configurations( - make_eventbridge_configuration(), - event["ResourceProperties"]["NotificationConfiguration"], - ), - ) - @patch("index.put_bucket_notification_configuration") @patch("index.get_bucket_notification_configuration") @patch("index.submit_response") @@ -232,24 +149,6 @@ def test_delete(self, _, get: MagicMock, put: MagicMock): make_empty_notification_configuration(), ) - @patch("index.put_bucket_notification_configuration") - @patch("index.get_bucket_notification_configuration") - @patch("index.submit_response") - def test_delete_with_eventbridge_should_not_remove_eventbridge(self, _, get: MagicMock, put: MagicMock): - - event = make_event_with_eventbridge("Delete", False) - - # simulate a previous create operation - current_notifications = make_notification_configuration_with_eventbridge(f"{event['StackId']}-") - get.return_value = current_notifications - - index.handler(event, {}) - - put.assert_called_once_with( - event["ResourceProperties"]["BucketName"], - make_empty_notification_configuration_with_eventbridge(), - ) - class UnmanagedDirtyBucketTest(unittest.TestCase): @patch("index.put_bucket_notification_configuration") @@ -273,48 +172,6 @@ def test_create(self, _, get: MagicMock, put: MagicMock): ), ) - @patch("index.put_bucket_notification_configuration") - @patch("index.get_bucket_notification_configuration") - @patch("index.submit_response") - def test_create_with_eventbridge(self, _, get: MagicMock, put: MagicMock): - - event = make_event_with_eventbridge("Create", False) - - # simulate external notifications - current_notifications = make_notification_configuration() - get.return_value = current_notifications - - index.handler(event, {}) - - put.assert_called_once_with( - event["ResourceProperties"]["BucketName"], - merge_notification_configurations( - current_notifications, - event["ResourceProperties"]["NotificationConfiguration"], - ), - ) - - @patch("index.put_bucket_notification_configuration") - @patch("index.get_bucket_notification_configuration") - @patch("index.submit_response") - def test_create_with_existing_eventbridge(self, _, get: MagicMock, put: MagicMock): - - event = make_event("Create", False) - - # simulate external notifications - current_notifications = make_notification_configuration_with_eventbridge() - get.return_value = current_notifications - - index.handler(event, {}) - - put.assert_called_once_with( - event["ResourceProperties"]["BucketName"], - merge_notification_configurations( - current_notifications, - event["ResourceProperties"]["NotificationConfiguration"], - ), - ) - @patch("index.put_bucket_notification_configuration") @patch("index.get_bucket_notification_configuration") @patch("index.submit_response") @@ -336,48 +193,6 @@ def test_update(self, _, get: MagicMock, put: MagicMock): ), ) - @patch("index.put_bucket_notification_configuration") - @patch("index.get_bucket_notification_configuration") - @patch("index.submit_response") - def test_update_with_eventbridge(self, _, get: MagicMock, put: MagicMock): - - event = make_event_with_eventbridge("Update", False) - - # simulate external notifications - current_notifications = make_notification_configuration() - get.return_value = current_notifications - - index.handler(event, {}) - - put.assert_called_once_with( - event["ResourceProperties"]["BucketName"], - merge_notification_configurations( - current_notifications, - event["ResourceProperties"]["NotificationConfiguration"], - ), - ) - - @patch("index.put_bucket_notification_configuration") - @patch("index.get_bucket_notification_configuration") - @patch("index.submit_response") - def test_update_without_eventbridge_should_not_remove_existing_eventbridge(self, _, get: MagicMock, put: MagicMock): - - event = make_event("Update", False) - - # simulate external notifications - current_notifications = make_notification_configuration_with_eventbridge() - get.return_value = current_notifications - - index.handler(event, {}) - - put.assert_called_once_with( - event["ResourceProperties"]["BucketName"], - merge_notification_configurations( - current_notifications, - event["ResourceProperties"]["NotificationConfiguration"], - ), - ) - @patch("index.put_bucket_notification_configuration") @patch("index.get_bucket_notification_configuration") @patch("index.submit_response") @@ -396,24 +211,6 @@ def test_delete(self, _, get: MagicMock, put: MagicMock): current_notifications, ) - @patch("index.put_bucket_notification_configuration") - @patch("index.get_bucket_notification_configuration") - @patch("index.submit_response") - def test_delete_with_eventbridge_should_not_remove_eventbridge(self, _, get: MagicMock, put: MagicMock): - - event = make_event_with_eventbridge("Delete", False) - - # simulate external notifications - current_notifications = make_notification_configuration_with_eventbridge() - get.return_value = current_notifications - - index.handler(event, {}) - - put.assert_called_once_with( - event["ResourceProperties"]["BucketName"], - current_notifications, - ) - class CfnResponsesTest(unittest.TestCase): @patch("index.put_bucket_notification_configuration") From eb29e6ff0308eb320ec772cc35cdbf781168198e Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Wed, 19 Jan 2022 18:55:51 +0100 Subject: [PATCH 34/50] fix(assertions): object partiality is dropped passing through arrays (#18525) `objectLike()` imposes partial object matching. That means that we don't need to fully specify all properties of an object to match it, but just the properties we care about (all other properties can have any value). Partial object matching is inherited. That means that in nested objects, the partiality is maintained: ```ts objectLike({ x: 'x', inner: { // Matches any object that has AT LEAST an 'y' property y: 'y', } }) ``` However, the partiality is dropped when passing through arrays: ```ts objectLike({ x: 'x', inner: [ { // Matches any object that has ONLY an 'y' property y: 'y', } ], }) ``` This is both unintuitive and different from past behavior, which makes migrating tests unnecessarily hard. Fix the discrepancy. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/assertions/lib/match.ts | 16 ++++++++++++-- .../@aws-cdk/assertions/test/match.test.ts | 21 +++++++++++++++++-- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/packages/@aws-cdk/assertions/lib/match.ts b/packages/@aws-cdk/assertions/lib/match.ts index e3afa681e6541..453174f969195 100644 --- a/packages/@aws-cdk/assertions/lib/match.ts +++ b/packages/@aws-cdk/assertions/lib/match.ts @@ -115,7 +115,7 @@ class LiteralMatch extends Matcher { public test(actual: any): MatchResult { if (Array.isArray(this.pattern)) { - return new ArrayMatch(this.name, this.pattern, { subsequence: false }).test(actual); + return new ArrayMatch(this.name, this.pattern, { subsequence: false, partialObjects: this.partialObjects }).test(actual); } if (typeof this.pattern === 'object') { @@ -155,6 +155,13 @@ interface ArrayMatchOptions { * @default true */ readonly subsequence?: boolean; + + /** + * Whether to continue matching objects inside the array partially + * + * @default false + */ + readonly partialObjects?: boolean; } /** @@ -162,6 +169,7 @@ interface ArrayMatchOptions { */ class ArrayMatch extends Matcher { private readonly subsequence: boolean; + private readonly partialObjects: boolean; constructor( public readonly name: string, @@ -170,6 +178,7 @@ class ArrayMatch extends Matcher { super(); this.subsequence = options.subsequence ?? true; + this.partialObjects = options.partialObjects ?? false; } public test(actual: any): MatchResult { @@ -195,7 +204,10 @@ class ArrayMatch extends Matcher { while (patternIdx < this.pattern.length && actualIdx < actual.length) { const patternElement = this.pattern[patternIdx]; - const matcher = Matcher.isMatcher(patternElement) ? patternElement : new LiteralMatch(this.name, patternElement); + const matcher = Matcher.isMatcher(patternElement) + ? patternElement + : new LiteralMatch(this.name, patternElement, { partialObjects: this.partialObjects }); + const matcherName = matcher.name; if (this.subsequence && (matcherName == 'absent' || matcherName == 'anyValue')) { // array subsequence matcher is not compatible with anyValue() or absent() matcher. They don't make sense to be used together. diff --git a/packages/@aws-cdk/assertions/test/match.test.ts b/packages/@aws-cdk/assertions/test/match.test.ts index 0b1ce784f9023..7dda38e269c10 100644 --- a/packages/@aws-cdk/assertions/test/match.test.ts +++ b/packages/@aws-cdk/assertions/test/match.test.ts @@ -176,7 +176,7 @@ describe('Matchers', () => { expectPass(matcher, { foo: 'bar', baz: { fred: 'waldo', wobble: 'flob' } }); }); - test('nested with ArrayMatch', () => { + test('ArrayMatch nested inside ObjectMatch', () => { matcher = Match.objectLike({ foo: Match.arrayWith(['bar']), }); @@ -184,6 +184,23 @@ describe('Matchers', () => { expectFailure(matcher, { foo: ['baz'], fred: 'waldo' }, [/Missing element \[bar\] at pattern index 0 at \/foo/]); }); + test('Partiality is maintained throughout arrays', () => { + // Before this fix: + // + // - objectLike({ x: { LITERAL }) ==> LITERAL would be matched partially as well + // - objectLike({ xs: [ { LITERAL } ] }) ==> but here LITERAL would be matched fully + // + // That passing through an array resets the partial matching to full is a + // surprising inconsistency. + // + matcher = Match.objectLike({ + foo: [{ bar: 'bar' }], + }); + expectPass(matcher, { foo: [{ bar: 'bar' }] }); // Trivially true + expectPass(matcher, { boo: 'boo', foo: [{ bar: 'bar' }] }); // Additional members at top level okay + expectPass(matcher, { foo: [{ bar: 'bar', boo: 'boo' }] }); // Additional members at inner level okay + }); + test('absent', () => { matcher = Match.objectLike({ foo: Match.absent() }); expectPass(matcher, { bar: 'baz' }); @@ -389,7 +406,7 @@ describe('Matchers', () => { function expectPass(matcher: Matcher, target: any): void { const result = matcher.test(target); if (result.hasFailed()) { - fail(result.toHumanStrings()); // eslint-disable-line jest/no-jasmine-globals + throw new Error(result.toHumanStrings().join('\n')); // eslint-disable-line jest/no-jasmine-globals } } From 9d1b2c7b1f0147089f912c32a61d7ba86edb543c Mon Sep 17 00:00:00 2001 From: Jacob Klitzke Date: Wed, 19 Jan 2022 10:43:36 -0800 Subject: [PATCH 35/50] feat(ec2): create Peers via security group ids (#18248) Allows users to add ingress/egress security group rules containing a security group id using the Peer interface. Implements #7111 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-ec2/lib/peer.ts | 56 ++++++++ .../aws-ec2/test/security-group.test.ts | 120 ++++++++++++++++++ 2 files changed, 176 insertions(+) diff --git a/packages/@aws-cdk/aws-ec2/lib/peer.ts b/packages/@aws-cdk/aws-ec2/lib/peer.ts index 333bd66bc91a9..2c0cc4be394c4 100644 --- a/packages/@aws-cdk/aws-ec2/lib/peer.ts +++ b/packages/@aws-cdk/aws-ec2/lib/peer.ts @@ -75,6 +75,13 @@ export class Peer { return new PrefixList(prefixListId); } + /** + * A security group ID + */ + public static securityGroupId(securityGroupId: string, sourceSecurityGroupOwnerId?: string): IPeer { + return new SecurityGroupId(securityGroupId, sourceSecurityGroupOwnerId); + } + protected constructor() { } } @@ -199,3 +206,52 @@ class PrefixList implements IPeer { return { destinationPrefixListId: this.prefixListId }; } } + +/** + * A connection to or from a given security group ID + * + * For ingress rules, a sourceSecurityGroupOwnerId parameter can be specified if + * the security group exists in another account. + * This parameter will be ignored for egress rules. + */ +class SecurityGroupId implements IPeer { + public readonly canInlineRule = true; + public readonly connections: Connections = new Connections({ peer: this }); + public readonly uniqueId: string; + + constructor(private readonly securityGroupId: string, private readonly sourceSecurityGroupOwnerId?: string) { + if (!Token.isUnresolved(securityGroupId)) { + const securityGroupMatch = securityGroupId.match(/^sg-[a-z0-9]{8,17}$/); + + if (!securityGroupMatch) { + throw new Error(`Invalid security group ID: "${securityGroupId}"`); + } + } + + if (sourceSecurityGroupOwnerId && !Token.isUnresolved(sourceSecurityGroupOwnerId)) { + const accountNumberMatch = sourceSecurityGroupOwnerId.match(/^[0-9]{12}$/); + + if (!accountNumberMatch) { + throw new Error(`Invalid security group owner ID: "${sourceSecurityGroupOwnerId}"`); + } + } + this.uniqueId = securityGroupId; + } + + /** + * Produce the ingress rule JSON for the given connection + */ + public toIngressRuleConfig(): any { + return { + sourceSecurityGroupId: this.securityGroupId, + ...(this.sourceSecurityGroupOwnerId && { sourceSecurityGroupOwnerId: this.sourceSecurityGroupOwnerId }), + }; + } + + /** + * Produce the egress rule JSON for the given connection + */ + public toEgressRuleConfig(): any { + return { destinationSecurityGroupId: this.securityGroupId }; + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2/test/security-group.test.ts b/packages/@aws-cdk/aws-ec2/test/security-group.test.ts index c7c3662f8592f..f4bdd13556457 100644 --- a/packages/@aws-cdk/aws-ec2/test/security-group.test.ts +++ b/packages/@aws-cdk/aws-ec2/test/security-group.test.ts @@ -178,6 +178,7 @@ describe('security group', () => { Peer.anyIpv4(), Peer.anyIpv6(), Peer.prefixList('pl-012345'), + Peer.securityGroupId('sg-012345678'), ]; const ports = [ @@ -337,6 +338,125 @@ describe('security group', () => { }); }); + describe('Peer security group ID validation', () => { + test('passes with valid security group ID', () => { + //GIVEN + const securityGroupIds = ['sg-12345678', 'sg-0123456789abcdefg']; + + // THEN + for (const securityGroupId of securityGroupIds) { + expect(Peer.securityGroupId(securityGroupId).uniqueId).toEqual(securityGroupId); + } + }); + + test('passes with valid security group ID and source owner id', () => { + //GIVEN + const securityGroupIds = ['sg-12345678', 'sg-0123456789abcdefg']; + const ownerIds = ['000000000000', '000000000001']; + + // THEN + for (const securityGroupId of securityGroupIds) { + for (const ownerId of ownerIds) { + expect(Peer.securityGroupId(securityGroupId, ownerId).uniqueId).toEqual(securityGroupId); + } + } + }); + + test('passes with unresolved security group id token or owner id token', () => { + // GIVEN + Token.asString('securityGroupId'); + + const securityGroupId = Lazy.string({ produce: () => 'sg-01234567' }); + const ownerId = Lazy.string({ produce: () => '000000000000' }); + Peer.securityGroupId(securityGroupId); + Peer.securityGroupId(securityGroupId, ownerId); + + // THEN: don't throw + }); + + test('throws if invalid security group ID', () => { + // THEN + expect(() => { + Peer.securityGroupId('invalid'); + }).toThrow(/Invalid security group ID/); + + + }); + + test('throws if invalid source security group id', () => { + // THEN + expect(() => { + Peer.securityGroupId('sg-12345678', 'invalid'); + }).toThrow(/Invalid security group owner ID/); + }); + }); + + describe('SourceSecurityGroupOwnerId property validation', () => { + test('SourceSecurityGroupOwnerId property is not present when value is not provided to ingress rule', () => { + // GIVEN + const stack = new Stack(undefined, 'TestStack'); + const vpc = new Vpc(stack, 'VPC'); + const sg = new SecurityGroup(stack, 'SG', { vpc }); + + //WHEN + sg.addIngressRule(Peer.securityGroupId('sg-123456789'), Port.allTcp(), 'no owner id property'); + + //THEN + expect(stack).toHaveResource('AWS::EC2::SecurityGroup', { + SecurityGroupIngress: [{ + SourceSecurityGroupId: 'sg-123456789', + Description: 'no owner id property', + FromPort: 0, + ToPort: 65535, + IpProtocol: 'tcp', + }], + }); + }); + + test('SourceSecurityGroupOwnerId property is present when value is provided to ingress rule', () => { + // GIVEN + const stack = new Stack(undefined, 'TestStack'); + const vpc = new Vpc(stack, 'VPC'); + const sg = new SecurityGroup(stack, 'SG', { vpc }); + + //WHEN + sg.addIngressRule(Peer.securityGroupId('sg-123456789', '000000000000'), Port.allTcp(), 'contains owner id property'); + + //THEN + expect(stack).toHaveResource('AWS::EC2::SecurityGroup', { + SecurityGroupIngress: [{ + SourceSecurityGroupId: 'sg-123456789', + SourceSecurityGroupOwnerId: '000000000000', + Description: 'contains owner id property', + FromPort: 0, + ToPort: 65535, + IpProtocol: 'tcp', + }], + }); + }); + + test('SourceSecurityGroupOwnerId property is not present when value is provided to egress rule', () => { + // GIVEN + const stack = new Stack(undefined, 'TestStack'); + const vpc = new Vpc(stack, 'VPC'); + const sg = new SecurityGroup(stack, 'SG', { vpc, allowAllOutbound: false }); + + //WHEN + sg.addEgressRule(Peer.securityGroupId('sg-123456789', '000000000000'), Port.allTcp(), 'no owner id property'); + + //THEN + expect(stack).toHaveResource('AWS::EC2::SecurityGroup', { + SecurityGroupEgress: [{ + DestinationSecurityGroupId: 'sg-123456789', + Description: 'no owner id property', + FromPort: 0, + ToPort: 65535, + IpProtocol: 'tcp', + }], + }); + }); + }); + testDeprecated('can look up a security group', () => { const app = new App(); const stack = new Stack(app, 'stack', { From a5ff6314c07cc7ae295f447e2679b838b81a01cf Mon Sep 17 00:00:00 2001 From: Nick Lynch Date: Wed, 19 Jan 2022 19:31:46 +0000 Subject: [PATCH 36/50] chore(docs): improve and clean up CONTRIBUTING for feature flags (#18485) Make both sets of helper methods (`FeatureFlags.of(..).isEnabled` and `testFutureBehavior`/`testLegacyBehavior`) more prominent, and clean up the language around v2 and flipping behavior there, as that's no longer relevant. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- CONTRIBUTING.md | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a16033a986b29..36045a4661c51 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -803,35 +803,17 @@ The pattern is simple: with the name of the context key that **enables** this new feature (for example, `ENABLE_STACK_NAME_DUPLICATES`). The context key should be in the form `module.Type:feature` (e.g. `@aws-cdk/core:enableStackNameDuplicates`). -2. Use `node.tryGetContext(cxapi.ENABLE_XXX)` to check if this feature is enabled +2. Use `FeatureFlags.of(construct).isEnabled(cxapi.ENABLE_XXX)` to check if this feature is enabled in your code. If it is not defined, revert to the legacy behavior. 3. Add your feature flag to the `FUTURE_FLAGS` map in [cx-api/lib/features.ts](https://github.com/aws/aws-cdk/blob/master/packages/%40aws-cdk/cx-api/lib/features.ts). This map is inserted to generated `cdk.json` files for new projects created through `cdk init`. -4. In your PR title (which goes into CHANGELOG), add a `(under feature flag)` suffix. e.g: +4. In your tests, use the `testFutureBehavior` and `testLegacyBehavior` [jest helper methods] to test the enabled and disabled behavior. +5. In your PR title (which goes into CHANGELOG), add a `(under feature flag)` suffix. e.g: `fix(core): impossible to use the same physical stack name for two stacks (under feature flag)` -In the [next major version of the -CDK](https://github.com/aws/aws-cdk/issues/3398) we will either remove the -legacy behavior or flip the logic for all these features and then -reset the `FEATURE_FLAGS` map for the next cycle. - -### Feature Flags - CDKv2 - -We have started working on the next version of the CDK, specifically CDKv2. This is currently being maintained -on a separate branch `v2-main` whereas `master` continues to track versions `1.x`. - -Feature flags introduced in the CDK 1.x and removed in 2.x, must be added to the `FUTURE_FLAGS_EXPIRED` list in -[cx-api/lib/features.ts](https://github.com/aws/aws-cdk/blob/master/packages/%40aws-cdk/cx-api/lib/features.ts) -on the `v2-main` branch. -This will make the default behaviour in CDKv2 as if the flag is enabled and also prevents users from disabling -the feature flag. - -A couple of [jest helper methods] are available for use with unit tests. These help run unit tests that test -behaviour when flags are enabled or disabled in the two major versions. - [jest helper methods]: https://github.com/aws/aws-cdk/blob/master/tools/@aws-cdk/cdk-build-tools/lib/feature-flag.ts ## Versioning and Release From c7f39ca97874c1d8d5286ab347a97fc458547830 Mon Sep 17 00:00:00 2001 From: Julian Michel Date: Wed, 19 Jan 2022 21:19:10 +0100 Subject: [PATCH 37/50] feat(ec2): add Hpc6a instances (#18445) Add support for Hpc6a instances (high performance computing). [Announcement](https://aws.amazon.com/about-aws/whats-new/2022/01/amazon-ec2-hpc6a-instances/) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-ec2/lib/instance-types.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/@aws-cdk/aws-ec2/lib/instance-types.ts b/packages/@aws-cdk/aws-ec2/lib/instance-types.ts index 029219ce9bb79..cf82f9b8e40a5 100644 --- a/packages/@aws-cdk/aws-ec2/lib/instance-types.ts +++ b/packages/@aws-cdk/aws-ec2/lib/instance-types.ts @@ -725,6 +725,16 @@ export enum InstanceClass { * Multi-stream video transcoding instances for resolutions up to 4K UHD, 1st generation */ VT1 = 'vt1', + + /** + * High performance computing based on AMD EPYC, 6th generation + */ + HIGH_PERFORMANCE_COMPUTING6_AMD = 'hpc6a', + + /** + * High performance computing based on AMD EPYC, 6th generation + */ + HPC6A = 'hpc6a', } /** From a7bd925db87326f71f1558598cd8b5f6ca922c8f Mon Sep 17 00:00:00 2001 From: Madeline Kusters <80541297+madeline-k@users.noreply.github.com> Date: Wed, 19 Jan 2022 13:08:08 -0800 Subject: [PATCH 38/50] chore(ecs-patterns): migrate to Assertions (#18520) I removed 3 tests that seem pointless, but I opened an issue to investigate further: https://github.com/aws/aws-cdk/issues/18519 It really seems like the scenario they were testing (omiting the cluster and only providing vpc) is not a working scenario anyway. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-ecs-patterns/package.json | 2 +- .../aws-ecs-patterns/test/ec2/l3s-v2.test.ts | 85 +++----- .../aws-ecs-patterns/test/ec2/l3s.test.ts | 187 ++++++++---------- .../ec2/queue-processing-ecs-service.test.ts | 53 +++-- .../test/ec2/scheduled-ecs-task.test.ts | 16 +- .../load-balanced-fargate-service-v2.test.ts | 40 ++-- .../load-balanced-fargate-service.test.ts | 158 ++++++++------- .../queue-processing-fargate-service.test.ts | 71 ++++--- .../fargate/scheduled-fargate-task.test.ts | 32 +-- 9 files changed, 290 insertions(+), 354 deletions(-) diff --git a/packages/@aws-cdk/aws-ecs-patterns/package.json b/packages/@aws-cdk/aws-ecs-patterns/package.json index a61d4f4f355e8..8bdbe1e23f377 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/package.json +++ b/packages/@aws-cdk/aws-ecs-patterns/package.json @@ -72,7 +72,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/l3s-v2.test.ts b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/l3s-v2.test.ts index 767c7a13f0df6..322e503a44aaf 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/l3s-v2.test.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/l3s-v2.test.ts @@ -1,5 +1,4 @@ -import { SynthUtils } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import { AutoScalingGroup } from '@aws-cdk/aws-autoscaling'; import { Certificate } from '@aws-cdk/aws-certificatemanager'; import { MachineImage, Vpc } from '@aws-cdk/aws-ec2'; @@ -44,16 +43,16 @@ describe('When Application Load Balancer', () => { }); // THEN - stack contains a load balancer, a service, and a target group. - expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::LoadBalancer'); + Template.fromStack(stack).resourceCountIs('AWS::ElasticLoadBalancingV2::LoadBalancer', 1); - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DesiredCount: 1, LaunchType: 'EC2', }); - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Image: 'test', LogConfiguration: { LogDriver: 'awslogs', @@ -76,7 +75,7 @@ describe('When Application Load Balancer', () => { Protocol: 'tcp', }, ], - }, + }), ], NetworkMode: 'bridge', RequiresCompatibilities: [ @@ -167,7 +166,7 @@ describe('When Application Load Balancer', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DesiredCount: 3, LaunchType: 'EC2', EnableECSManagedTags: true, @@ -192,7 +191,7 @@ describe('When Application Load Balancer', () => { ServiceName: 'myService', }); - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Cpu: 256, @@ -260,7 +259,7 @@ describe('When Application Load Balancer', () => { }, }); - expect(stack).toHaveResourceLike('AWS::ElasticLoadBalancingV2::Listener', { + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::Listener', { Port: 443, Protocol: 'HTTPS', Certificates: [{ @@ -270,26 +269,6 @@ describe('When Application Load Balancer', () => { }); }); - test('set vpc instead of cluster', () => { - // GIVEN - const stack = new Stack(); - const vpc = new Vpc(stack, 'VPC'); - - // WHEN - new ApplicationMultipleTargetGroupsEc2Service(stack, 'Service', { - vpc, - memoryLimitMiB: 1024, - taskImageOptions: { - image: ContainerImage.fromRegistry('test'), - }, - }); - - // THEN - stack does not contain a LaunchConfiguration - const template = SynthUtils.synthesize(stack, { skipValidation: true }); - expect(template).not.toHaveResource('AWS::AutoScaling::LaunchConfiguration'); - expect(() => SynthUtils.synthesize(stack)).toThrow(); - }); - test('able to pass pre-defined task definition', () => { // GIVEN const stack = new Stack(); @@ -319,7 +298,7 @@ describe('When Application Load Balancer', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Essential: true, @@ -408,8 +387,8 @@ describe('When Application Load Balancer', () => { }); // THEN - const template = SynthUtils.synthesize(stack).template.Outputs; - expect(template).toEqual({ + const outputs = Template.fromStack(stack).findOutputs('*'); + expect(outputs).toEqual({ ServiceLoadBalancerDNSlb175E78BFE: { Value: { 'Fn::GetAtt': [ @@ -596,7 +575,7 @@ describe('When Application Load Balancer', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { ServiceRegistries: [ { ContainerName: 'web', @@ -611,7 +590,7 @@ describe('When Application Load Balancer', () => { ], }); - expect(stack).toHaveResource('AWS::ServiceDiscovery::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ServiceDiscovery::Service', { DnsConfig: { DnsRecords: [ { @@ -926,14 +905,14 @@ describe('When Network Load Balancer', () => { }); // THEN - stack contains a load balancer and a service - expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::LoadBalancer'); + Template.fromStack(stack).resourceCountIs('AWS::ElasticLoadBalancingV2::LoadBalancer', 1); - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DesiredCount: 1, LaunchType: 'EC2', }); - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Essential: true, @@ -1066,7 +1045,7 @@ describe('When Network Load Balancer', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DesiredCount: 3, EnableECSManagedTags: true, HealthCheckGracePeriodSeconds: 2, @@ -1092,7 +1071,7 @@ describe('When Network Load Balancer', () => { ServiceName: 'myService', }); - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Cpu: 256, @@ -1161,26 +1140,6 @@ describe('When Network Load Balancer', () => { }); }); - test('set vpc instead of cluster', () => { - // GIVEN - const stack = new Stack(); - const vpc = new Vpc(stack, 'VPC'); - - // WHEN - new NetworkMultipleTargetGroupsEc2Service(stack, 'Service', { - vpc, - memoryLimitMiB: 256, - taskImageOptions: { - image: ContainerImage.fromRegistry('test'), - }, - }); - - // THEN - stack does not contain a LaunchConfiguration - const template = SynthUtils.synthesize(stack, { skipValidation: true }); - expect(template).not.toHaveResource('AWS::AutoScaling::LaunchConfiguration'); - expect(() => SynthUtils.synthesize(stack)).toThrow(); - }); - test('able to pass pre-defined task definition', () => { // GIVEN const stack = new Stack(); @@ -1210,7 +1169,7 @@ describe('When Network Load Balancer', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Essential: true, @@ -1357,7 +1316,7 @@ describe('When Network Load Balancer', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { ServiceRegistries: [ { ContainerName: 'web', @@ -1372,7 +1331,7 @@ describe('When Network Load Balancer', () => { ], }); - expect(stack).toHaveResource('AWS::ServiceDiscovery::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ServiceDiscovery::Service', { DnsConfig: { DnsRecords: [ { diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/l3s.test.ts b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/l3s.test.ts index c8f29cfbc695c..db4764c1e4d6d 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/l3s.test.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/l3s.test.ts @@ -1,5 +1,4 @@ -import { ABSENT, arrayWith, objectLike, SynthUtils } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import { AutoScalingGroup } from '@aws-cdk/aws-autoscaling'; import { Certificate } from '@aws-cdk/aws-certificatemanager'; import * as ec2 from '@aws-cdk/aws-ec2'; @@ -42,16 +41,16 @@ test('test ECS loadbalanced construct', () => { }); // THEN - stack contains a load balancer and a service - expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::LoadBalancer'); + Template.fromStack(stack).resourceCountIs('AWS::ElasticLoadBalancingV2::LoadBalancer', 1); - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DesiredCount: 2, LaunchType: 'EC2', }); - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Environment: [ { Name: 'TEST_ENVIRONMENT_VARIABLE1', @@ -67,7 +66,7 @@ test('test ECS loadbalanced construct', () => { label1: 'labelValue1', label2: 'labelValue2', }, - }, + }), ], }); }); @@ -96,8 +95,8 @@ test('ApplicationLoadBalancedEc2Service desiredCount can be undefined when featu }, }); - expect(stack).toHaveResource('AWS::ECS::Service', { - DesiredCount: ABSENT, + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { + DesiredCount: Match.absent(), }); }); @@ -117,8 +116,8 @@ test('ApplicationLoadBalancedFargateService desiredCount can be undefined when f }, }); - expect(stack).toHaveResource('AWS::ECS::Service', { - DesiredCount: ABSENT, + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { + DesiredCount: Match.absent(), }); }); @@ -146,8 +145,8 @@ test('NetworkLoadBalancedEc2Service desiredCount can be undefined when feature f }, }); - expect(stack).toHaveResource('AWS::ECS::Service', { - DesiredCount: ABSENT, + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { + DesiredCount: Match.absent(), }); }); @@ -167,36 +166,11 @@ test('NetworkLoadBalancedFargateService desiredCount can be undefined when featu }, }); - expect(stack).toHaveResource('AWS::ECS::Service', { - DesiredCount: ABSENT, + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { + DesiredCount: Match.absent(), }); }); -test('set vpc instead of cluster', () => { - // GIVEN - const stack = new cdk.Stack(); - const vpc = new ec2.Vpc(stack, 'VPC'); - - // WHEN - new ecsPatterns.ApplicationLoadBalancedEc2Service(stack, 'Service', { - vpc, - memoryLimitMiB: 1024, - taskImageOptions: { - image: ecs.ContainerImage.fromRegistry('test'), - environment: { - TEST_ENVIRONMENT_VARIABLE1: 'test environment variable 1 value', - TEST_ENVIRONMENT_VARIABLE2: 'test environment variable 2 value', - }, - }, - desiredCount: 2, - }); - - // THEN - stack does not contain a LaunchConfiguration\ - const template = SynthUtils.synthesize(stack, { skipValidation: true }); - expect(template).not.toHaveResource('AWS::AutoScaling::LaunchConfiguration'); - expect(() => SynthUtils.synthesize(stack)).toThrow(); -}); - test('setting vpc and cluster throws error', () => { // GIVEN const stack = new cdk.Stack(); @@ -236,13 +210,13 @@ test('test ECS loadbalanced construct with memoryReservationMiB', () => { }); // THEN - stack contains a load balancer and a service - expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::LoadBalancer'); + Template.fromStack(stack).resourceCountIs('AWS::ElasticLoadBalancingV2::LoadBalancer', 1); - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ MemoryReservation: 1024, - }, + }), ], }); }); @@ -279,7 +253,7 @@ test('creates AWS Cloud Map service for Private DNS namespace with application l }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { ServiceRegistries: [ { ContainerName: 'web', @@ -294,7 +268,7 @@ test('creates AWS Cloud Map service for Private DNS namespace with application l ], }); - expect(stack).toHaveResource('AWS::ServiceDiscovery::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ServiceDiscovery::Service', { DnsConfig: { DnsRecords: [ { @@ -355,7 +329,7 @@ test('creates AWS Cloud Map service for Private DNS namespace with network load }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { ServiceRegistries: [ { RegistryArn: { @@ -368,7 +342,7 @@ test('creates AWS Cloud Map service for Private DNS namespace with network load ], }); - expect(stack).toHaveResource('AWS::ServiceDiscovery::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ServiceDiscovery::Service', { DnsConfig: { DnsRecords: [ { @@ -418,10 +392,10 @@ test('test Fargate loadbalanced construct', () => { }); // THEN - stack contains a load balancer and a service - expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::LoadBalancer'); - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).resourceCountIs('AWS::ElasticLoadBalancingV2::LoadBalancer', 1); + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Environment: [ { Name: 'TEST_ENVIRONMENT_VARIABLE1', @@ -444,16 +418,16 @@ test('test Fargate loadbalanced construct', () => { label1: 'labelValue1', label2: 'labelValue2', }, - }, + }), ], }); - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DesiredCount: 2, LaunchType: 'FARGATE', }); - expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::Listener', { + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::Listener', { Port: 80, Protocol: 'HTTP', }); @@ -480,9 +454,9 @@ test('test Fargate loadbalanced construct opting out of log driver creation', () }); // THEN - stack contains a load balancer and a service - expect(stack).not.toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Environment: [ { Name: 'TEST_ENVIRONMENT_VARIABLE1', @@ -493,15 +467,8 @@ test('test Fargate loadbalanced construct opting out of log driver creation', () Value: 'test environment variable 2 value', }, ], - LogConfiguration: { - LogDriver: 'awslogs', - Options: { - 'awslogs-group': { Ref: 'ServiceTaskDefwebLogGroup2A898F61' }, - 'awslogs-stream-prefix': 'Service', - 'awslogs-region': { Ref: 'AWS::Region' }, - }, - }, - }, + LogConfiguration: Match.absent(), + }), ], }); }); @@ -526,9 +493,9 @@ test('test Fargate loadbalanced construct with TLS', () => { }); // THEN - stack contains a load balancer and a service - expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::LoadBalancer'); + Template.fromStack(stack).resourceCountIs('AWS::ElasticLoadBalancingV2::LoadBalancer', 1); - expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::Listener', { + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::Listener', { Port: 443, Protocol: 'HTTPS', Certificates: [{ @@ -537,7 +504,7 @@ test('test Fargate loadbalanced construct with TLS', () => { SslPolicy: SslPolicy.TLS12_EXT, }); - expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::TargetGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::TargetGroup', { Port: 80, Protocol: 'HTTP', TargetType: 'ip', @@ -546,12 +513,12 @@ test('test Fargate loadbalanced construct with TLS', () => { }, }); - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DesiredCount: 1, LaunchType: 'FARGATE', }); - expect(stack).toHaveResource('AWS::Route53::RecordSet', { + Template.fromStack(stack).hasResourceProperties('AWS::Route53::RecordSet', { Name: 'api.example.com.', HostedZoneId: { Ref: 'HostedZoneDB99F866', @@ -583,7 +550,7 @@ test('test Fargateloadbalanced construct with TLS and default certificate', () = }); // THEN - stack contains a load balancer, a service, and a certificate - expect(stack).toHaveResource('AWS::CertificateManager::Certificate', { + Template.fromStack(stack).hasResourceProperties('AWS::CertificateManager::Certificate', { DomainName: 'api.example.com', DomainValidationOptions: [ { @@ -596,9 +563,9 @@ test('test Fargateloadbalanced construct with TLS and default certificate', () = ValidationMethod: 'DNS', }); - expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::LoadBalancer'); + Template.fromStack(stack).resourceCountIs('AWS::ElasticLoadBalancingV2::LoadBalancer', 1); - expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::Listener', { + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::Listener', { Port: 443, Protocol: 'HTTPS', Certificates: [{ @@ -608,12 +575,12 @@ test('test Fargateloadbalanced construct with TLS and default certificate', () = }], }); - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DesiredCount: 1, LaunchType: 'FARGATE', }); - expect(stack).toHaveResource('AWS::Route53::RecordSet', { + Template.fromStack(stack).hasResourceProperties('AWS::Route53::RecordSet', { Name: 'api.example.com.', HostedZoneId: { Ref: 'HostedZoneDB99F866', @@ -744,9 +711,9 @@ test('test Fargate loadbalanced construct with optional log driver input', () => }); // THEN - stack contains a load balancer and a service - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Environment: [ { Name: 'TEST_ENVIRONMENT_VARIABLE1', @@ -765,7 +732,7 @@ test('test Fargate loadbalanced construct with optional log driver input', () => 'awslogs-region': { Ref: 'AWS::Region' }, }, }, - }, + }), ], }); }); @@ -791,9 +758,9 @@ test('test Fargate loadbalanced construct with logging enabled', () => { }); // THEN - stack contains a load balancer and a service - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Environment: [ { Name: 'TEST_ENVIRONMENT_VARIABLE1', @@ -812,7 +779,7 @@ test('test Fargate loadbalanced construct with logging enabled', () => { 'awslogs-region': { Ref: 'AWS::Region' }, }, }, - }, + }), ], }); }); @@ -868,9 +835,9 @@ test('test Fargate application loadbalanced construct with taskDefinition provid memoryLimitMiB: 1024, }); - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Image: 'amazon/amazon-ecs-sample', Memory: 512, Name: 'passedTaskDef', @@ -880,7 +847,7 @@ test('test Fargate application loadbalanced construct with taskDefinition provid Protocol: 'tcp', }, ], - }, + }), ], }); }); @@ -954,7 +921,7 @@ test('ALBFargate - having *HealthyPercent properties', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DeploymentConfiguration: { MinimumHealthyPercent: 100, MaximumPercent: 200, @@ -981,7 +948,7 @@ test('NLBFargate - having *HealthyPercent properties', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DeploymentConfiguration: { MinimumHealthyPercent: 100, MaximumPercent: 200, @@ -1015,7 +982,7 @@ test('ALB - having *HealthyPercent properties', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DeploymentConfiguration: { MinimumHealthyPercent: 100, MaximumPercent: 200, @@ -1052,7 +1019,7 @@ test('ALB - includes provided protocol version properties', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ElasticLoadBalancingV2::TargetGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::TargetGroup', { ProtocolVersion: 'GRPC', }); }); @@ -1083,7 +1050,7 @@ test('NLB - having *HealthyPercent properties', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DeploymentConfiguration: { MinimumHealthyPercent: 100, MaximumPercent: 200, @@ -1117,7 +1084,7 @@ test('ALB - having deployment controller', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DeploymentController: { Type: 'CODE_DEPLOY', }, @@ -1150,7 +1117,7 @@ test('NLB - having deployment controller', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DeploymentController: { Type: 'CODE_DEPLOY', }, @@ -1181,7 +1148,7 @@ test('ALB with circuit breaker', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DeploymentConfiguration: { DeploymentCircuitBreaker: { Enable: true, @@ -1218,7 +1185,7 @@ test('NLB with circuit breaker', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DeploymentConfiguration: { DeploymentCircuitBreaker: { Enable: true, @@ -1259,10 +1226,10 @@ test('NetworkLoadbalancedEC2Service accepts previously created load balancer', ( }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { LaunchType: 'EC2', }); - expect(stack).toHaveResourceLike('AWS::ElasticLoadBalancingV2::LoadBalancer', { + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::LoadBalancer', { Type: 'network', }); }); @@ -1302,12 +1269,12 @@ test('NetworkLoadBalancedEC2Service accepts imported load balancer', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { LaunchType: 'EC2', - LoadBalancers: [{ ContainerName: 'Container', ContainerPort: 80 }], + LoadBalancers: [Match.objectLike({ ContainerName: 'Container', ContainerPort: 80 })], }); - expect(stack).toHaveResourceLike('AWS::ElasticLoadBalancingV2::TargetGroup'); - expect(stack).toHaveResourceLike('AWS::ElasticLoadBalancingV2::Listener', { + Template.fromStack(stack).resourceCountIs('AWS::ElasticLoadBalancingV2::TargetGroup', 1); + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::Listener', { LoadBalancerArn: nlb.loadBalancerArn, Port: 80, }); @@ -1345,10 +1312,10 @@ test('ApplicationLoadBalancedEC2Service accepts previously created load balancer }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { LaunchType: 'EC2', }); - expect(stack).toHaveResourceLike('AWS::ElasticLoadBalancingV2::LoadBalancer', { + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::LoadBalancer', { Type: 'application', }); }); @@ -1388,12 +1355,12 @@ test('ApplicationLoadBalancedEC2Service accepts imported load balancer', () => { taskDefinition: taskDef, }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { LaunchType: 'EC2', - LoadBalancers: [{ ContainerName: 'Container', ContainerPort: 80 }], + LoadBalancers: [Match.objectLike({ ContainerName: 'Container', ContainerPort: 80 })], }); - expect(stack).toHaveResourceLike('AWS::ElasticLoadBalancingV2::TargetGroup'); - expect(stack).toHaveResourceLike('AWS::ElasticLoadBalancingV2::Listener', { + Template.fromStack(stack).resourceCountIs('AWS::ElasticLoadBalancingV2::TargetGroup', 1); + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::Listener', { LoadBalancerArn: alb.loadBalancerArn, Port: 80, }); @@ -1422,13 +1389,13 @@ test('test ECS loadbalanced construct default/open security group', () => { }); // THEN - Stack contains no ingress security group rules - expect(stack).toHaveResourceLike('AWS::EC2::SecurityGroup', { - SecurityGroupIngress: [{ + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroup', { + SecurityGroupIngress: [Match.objectLike({ CidrIp: '0.0.0.0/0', FromPort: 80, IpProtocol: 'tcp', ToPort: 80, - }], + })], }); }); @@ -1461,7 +1428,7 @@ test('test ECS loadbalanced construct closed security group', () => { }); // THEN - Stack contains no ingress security group rules - expect(stack).not.toHaveResourceLike('AWS::EC2::SecurityGroup', { - SecurityGroupIngress: arrayWith(objectLike({})), + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroup', { + SecurityGroupIngress: Match.absent(), }); }); diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/queue-processing-ecs-service.test.ts b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/queue-processing-ecs-service.test.ts index 9a64cfd40428e..e5b68caa55761 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/queue-processing-ecs-service.test.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/queue-processing-ecs-service.test.ts @@ -1,5 +1,4 @@ -import { ABSENT } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import { AutoScalingGroup } from '@aws-cdk/aws-autoscaling'; import * as autoscaling from '@aws-cdk/aws-autoscaling'; import { MachineImage } from '@aws-cdk/aws-ec2'; @@ -33,12 +32,12 @@ test('test ECS queue worker service construct - with only required props', () => }); // THEN - QueueWorker is of EC2 launch type, an SQS queue is created and all default properties are set. - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DesiredCount: 1, LaunchType: 'EC2', }); - expect(stack).toHaveResource('AWS::SQS::Queue', { + Template.fromStack(stack).hasResourceProperties('AWS::SQS::Queue', { RedrivePolicy: { deadLetterTargetArn: { 'Fn::GetAtt': [ @@ -50,13 +49,13 @@ test('test ECS queue worker service construct - with only required props', () => }, }); - expect(stack).toHaveResource('AWS::SQS::Queue', { + Template.fromStack(stack).hasResourceProperties('AWS::SQS::Queue', { MessageRetentionPeriod: 1209600, }); - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Environment: [ { Name: 'QUEUE_NAME', @@ -83,7 +82,7 @@ test('test ECS queue worker service construct - with only required props', () => Essential: true, Image: 'test', Memory: 512, - }, + }), ], Family: 'ServiceQueueProcessingTaskDef83DB34F1', }); @@ -112,8 +111,8 @@ test('test ECS queue worker service construct - with remove default desiredCount }); // THEN - QueueWorker is of EC2 launch type, and desiredCount is not defined on the Ec2Service. - expect(stack).toHaveResource('AWS::ECS::Service', { - DesiredCount: ABSENT, + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { + DesiredCount: Match.absent(), LaunchType: 'EC2', }); }); @@ -142,12 +141,12 @@ test('test ECS queue worker service construct - with optional props for queues', }); // THEN - QueueWorker is of EC2 launch type, an SQS queue is created and all default properties are set. - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DesiredCount: 1, LaunchType: 'EC2', }); - expect(stack).toHaveResource('AWS::SQS::Queue', { + Template.fromStack(stack).hasResourceProperties('AWS::SQS::Queue', { RedrivePolicy: { deadLetterTargetArn: { 'Fn::GetAtt': [ @@ -160,13 +159,13 @@ test('test ECS queue worker service construct - with optional props for queues', VisibilityTimeout: 300, }); - expect(stack).toHaveResource('AWS::SQS::Queue', { + Template.fromStack(stack).hasResourceProperties('AWS::SQS::Queue', { MessageRetentionPeriod: 604800, }); - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Environment: [ { Name: 'QUEUE_NAME', @@ -193,7 +192,7 @@ test('test ECS queue worker service construct - with optional props for queues', Essential: true, Image: 'test', Memory: 512, - }, + }), ], Family: 'ServiceQueueProcessingTaskDef83DB34F1', }); @@ -238,7 +237,7 @@ testDeprecated('test ECS queue worker service construct - with optional props', }); // THEN - QueueWorker is of EC2 launch type, an SQS queue is created and all optional properties are set. - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DesiredCount: 2, DeploymentConfiguration: { MinimumHealthyPercent: 60, @@ -255,13 +254,13 @@ testDeprecated('test ECS queue worker service construct - with optional props', }, }); - expect(stack).toHaveResource('AWS::SQS::Queue', { + Template.fromStack(stack).hasResourceProperties('AWS::SQS::Queue', { QueueName: 'ecs-test-sqs-queue', }); - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Command: [ '-c', '4', @@ -294,7 +293,7 @@ testDeprecated('test ECS queue worker service construct - with optional props', Value: '256', }, ], - }, + }), ], Family: 'ecs-task-family', }); @@ -323,7 +322,7 @@ testDeprecated('can set desiredTaskCount to 0', () => { }); // THEN - QueueWorker is of EC2 launch type, an SQS queue is created and all default properties are set. - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DesiredCount: 0, LaunchType: 'EC2', }); @@ -375,11 +374,11 @@ test('can set custom containerName', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Name: 'my-container', - }, + }), ], }); }); @@ -412,8 +411,8 @@ test('can set capacity provider strategies', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { - LaunchType: ABSENT, + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { + LaunchType: Match.absent(), CapacityProviderStrategy: [ { CapacityProvider: { diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/scheduled-ecs-task.test.ts b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/scheduled-ecs-task.test.ts index 9524d7a4d145c..71d48850d3b0d 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/scheduled-ecs-task.test.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/scheduled-ecs-task.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { AutoScalingGroup } from '@aws-cdk/aws-autoscaling'; import * as ec2 from '@aws-cdk/aws-ec2'; import { MachineImage } from '@aws-cdk/aws-ec2'; @@ -32,7 +32,7 @@ test('Can create a scheduled Ec2 Task - with only required props', () => { }); // THEN - expect(stack).toHaveResource('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { State: 'ENABLED', Targets: [ { @@ -48,7 +48,7 @@ test('Can create a scheduled Ec2 Task - with only required props', () => { ], }); - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Essential: true, @@ -101,7 +101,7 @@ test('Can create a scheduled Ec2 Task - with optional props', () => { }); // THEN - expect(stack).toHaveResource('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { Name: 'sample-scheduled-task-rule', State: 'DISABLED', Targets: [ @@ -118,7 +118,7 @@ test('Can create a scheduled Ec2 Task - with optional props', () => { ], }); - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Cpu: 2, @@ -169,7 +169,7 @@ test('Scheduled ECS Task - with securityGroups defined', () => { }); // THEN - expect(stack).toHaveResource('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { Targets: [ { Arn: { 'Fn::GetAtt': ['EcsCluster97242B84', 'Arn'] }, @@ -225,7 +225,7 @@ test('Scheduled Ec2 Task - with MemoryReservation defined', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Essential: true, @@ -273,7 +273,7 @@ test('Scheduled Ec2 Task - with Command defined', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Command: [ diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/load-balanced-fargate-service-v2.test.ts b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/load-balanced-fargate-service-v2.test.ts index 902d5412ee8bf..191dbd4430236 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/load-balanced-fargate-service-v2.test.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/load-balanced-fargate-service-v2.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import { Vpc } from '@aws-cdk/aws-ec2'; import * as ecs from '@aws-cdk/aws-ecs'; import { CompositePrincipal, Role, ServicePrincipal } from '@aws-cdk/aws-iam'; @@ -21,9 +21,9 @@ describe('When Application Load Balancer', () => { }); // THEN - stack contains a load balancer and a service - expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::LoadBalancer'); + Template.fromStack(stack).resourceCountIs('AWS::ElasticLoadBalancingV2::LoadBalancer', 1); - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DesiredCount: 1, LaunchType: 'FARGATE', LoadBalancers: [ @@ -37,9 +37,9 @@ describe('When Application Load Balancer', () => { ], }); - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Image: 'test', LogConfiguration: { LogDriver: 'awslogs', @@ -60,7 +60,7 @@ describe('When Application Load Balancer', () => { Protocol: 'tcp', }, ], - }, + }), ], Cpu: '256', ExecutionRoleArn: { @@ -135,7 +135,7 @@ describe('When Application Load Balancer', () => { }); // THEN - stack contains a load balancer and a service - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DesiredCount: 3, EnableECSManagedTags: true, HealthCheckGracePeriodSeconds: 2, @@ -182,7 +182,7 @@ describe('When Application Load Balancer', () => { ServiceName: 'myService', }); - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Environment: [ @@ -314,11 +314,11 @@ describe('When Application Load Balancer', () => { }); // THEN - stack contains a load balancer and a service - expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::LoadBalancer', { + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::LoadBalancer', { Name: 'alb-test-load-balancer', }); - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DesiredCount: 1, LaunchType: 'FARGATE', LoadBalancers: [ @@ -332,9 +332,9 @@ describe('When Application Load Balancer', () => { ], }); - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Image: 'test', LogConfiguration: { LogDriver: 'awslogs', @@ -355,7 +355,7 @@ describe('When Application Load Balancer', () => { Protocol: 'tcp', }, ], - }, + }), ], Cpu: '256', ExecutionRoleArn: { @@ -390,9 +390,9 @@ describe('When Network Load Balancer', () => { }); // THEN - stack contains a load balancer and a service - expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::LoadBalancer'); + Template.fromStack(stack).resourceCountIs('AWS::ElasticLoadBalancingV2::LoadBalancer', 1); - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DesiredCount: 1, LaunchType: 'FARGATE', LoadBalancers: [ @@ -406,9 +406,9 @@ describe('When Network Load Balancer', () => { ], }); - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Image: 'test', LogConfiguration: { LogDriver: 'awslogs', @@ -429,7 +429,7 @@ describe('When Network Load Balancer', () => { Protocol: 'tcp', }, ], - }, + }), ], Cpu: '256', ExecutionRoleArn: { @@ -500,7 +500,7 @@ describe('When Network Load Balancer', () => { }); // THEN - stack contains a load balancer and a service - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DesiredCount: 3, EnableECSManagedTags: true, HealthCheckGracePeriodSeconds: 2, @@ -546,7 +546,7 @@ describe('When Network Load Balancer', () => { ServiceName: 'myService', }); - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Environment: [ diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/load-balanced-fargate-service.test.ts b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/load-balanced-fargate-service.test.ts index 0f4c0ab29ba86..70763a1bc2277 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/load-balanced-fargate-service.test.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/load-balanced-fargate-service.test.ts @@ -1,5 +1,4 @@ -import { SynthUtils } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import { AutoScalingGroup } from '@aws-cdk/aws-autoscaling'; import { DnsValidatedCertificate } from '@aws-cdk/aws-certificatemanager'; import * as ec2 from '@aws-cdk/aws-ec2'; @@ -27,7 +26,7 @@ test('setting loadBalancerType to Network creates an NLB Public', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ElasticLoadBalancingV2::LoadBalancer', { + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::LoadBalancer', { Type: 'network', Scheme: 'internet-facing', }); @@ -49,7 +48,7 @@ test('setting loadBalancerType to Network and publicLoadBalancer to false create }); // THEN - expect(stack).toHaveResourceLike('AWS::ElasticLoadBalancingV2::LoadBalancer', { + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::LoadBalancer', { Type: 'network', Scheme: 'internal', }); @@ -95,8 +94,9 @@ test('setting executionRole updated taskDefinition with given execution role', ( }); // THEN - const serviceTaskDefinition = SynthUtils.synthesize(stack).template.Resources.ServiceTaskDef1922A00F; - expect(serviceTaskDefinition.Properties.ExecutionRoleArn).toEqual({ 'Fn::GetAtt': ['ExecutionRole605A040B', 'Arn'] }); + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { + ExecutionRoleArn: { 'Fn::GetAtt': ['ExecutionRole605A040B', 'Arn'] }, + }); }); test('setting taskRole updated taskDefinition with given task role', () => { @@ -122,8 +122,9 @@ test('setting taskRole updated taskDefinition with given task role', () => { }); // THEN - const serviceTaskDefinition = SynthUtils.synthesize(stack).template.Resources.ServiceTaskDef1922A00F; - expect(serviceTaskDefinition.Properties.TaskRoleArn).toEqual({ 'Fn::GetAtt': ['taskRoleTest9DA66B6E', 'Arn'] }); + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { + TaskRoleArn: { 'Fn::GetAtt': ['taskRoleTest9DA66B6E', 'Arn'] }, + }); }); test('setting containerName updates container name with given name', () => { @@ -142,8 +143,13 @@ test('setting containerName updates container name with given name', () => { }); // THEN - const serviceTaskDefinition = SynthUtils.synthesize(stack).template.Resources.ServiceTaskDef1922A00F; - expect(serviceTaskDefinition.Properties.ContainerDefinitions[0].Name).toEqual('bob'); + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { + ContainerDefinitions: [ + Match.objectLike({ + Name: 'bob', + }), + ], + }); }); test('not setting containerName updates container name with default', () => { @@ -161,8 +167,13 @@ test('not setting containerName updates container name with default', () => { }); // THEN - const serviceTaskDefinition = SynthUtils.synthesize(stack).template.Resources.ServiceTaskDef1922A00F; - expect(serviceTaskDefinition.Properties.ContainerDefinitions[0].Name).toEqual('web'); + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { + ContainerDefinitions: [ + Match.objectLike({ + Name: 'web', + }), + ], + }); }); test('setting servicename updates service name with given name', () => { @@ -180,8 +191,9 @@ test('setting servicename updates service name with given name', () => { serviceName: 'bob', }); // THEN - const serviceTaskDefinition = SynthUtils.synthesize(stack).template.Resources.Service9571FDD8; - expect(serviceTaskDefinition.Properties.ServiceName).toEqual('bob'); + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { + ServiceName: 'bob', + }); }); test('not setting servicename updates service name with default', () => { @@ -199,8 +211,9 @@ test('not setting servicename updates service name with default', () => { }); // THEN - const serviceTaskDefinition = SynthUtils.synthesize(stack).template.Resources.Service9571FDD8; - expect(serviceTaskDefinition.Properties.ServiceName).toBeUndefined(); + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { + ServiceName: Match.absent(), + }); }); test('setting healthCheckGracePeriod works', () => { @@ -215,8 +228,9 @@ test('setting healthCheckGracePeriod works', () => { healthCheckGracePeriod: cdk.Duration.seconds(600), }); // THEN - const serviceTaskDefinition = SynthUtils.synthesize(stack).template.Resources.Service9571FDD8; - expect(serviceTaskDefinition.Properties.HealthCheckGracePeriodSeconds).toEqual(600); + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { + HealthCheckGracePeriodSeconds: 600, + }); }); test('selecting correct vpcSubnets', () => { @@ -248,7 +262,7 @@ test('selecting correct vpcSubnets', () => { }, }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { NetworkConfiguration: { AwsvpcConfiguration: { Subnets: [ @@ -275,7 +289,7 @@ test('target group uses HTTP/80 as default', () => { }, }); // THEN - expect(stack).toHaveResourceLike('AWS::ElasticLoadBalancingV2::TargetGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::TargetGroup', { Port: 80, Protocol: 'HTTP', }); @@ -293,7 +307,7 @@ test('target group uses HTTPS/443 when configured', () => { targetProtocol: ApplicationProtocol.HTTPS, }); // THEN - expect(stack).toHaveResourceLike('AWS::ElasticLoadBalancingV2::TargetGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::TargetGroup', { Port: 443, Protocol: 'HTTPS', }); @@ -311,7 +325,7 @@ test('setting platform version', () => { platformVersion: ecs.FargatePlatformVersion.VERSION1_4, }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { PlatformVersion: ecs.FargatePlatformVersion.VERSION1_4, }); }); @@ -347,15 +361,15 @@ test('test load balanced service with family defined', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DesiredCount: 2, LaunchType: 'FARGATE', ServiceName: 'fargate-test-service', }); - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Environment: [ { Name: 'TEST_ENVIRONMENT_VARIABLE1', @@ -367,7 +381,7 @@ test('test load balanced service with family defined', () => { }, ], Image: '/aws/aws-example-app', - }, + }), ], Family: 'fargate-task-family', }); @@ -388,7 +402,7 @@ test('setting ALB deployment controller', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DeploymentController: { Type: 'CODE_DEPLOY', }, @@ -410,7 +424,7 @@ test('setting NLB deployment controller', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DeploymentController: { Type: 'CODE_DEPLOY', }, @@ -430,7 +444,7 @@ test('setting ALB circuitBreaker works', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DeploymentConfiguration: { DeploymentCircuitBreaker: { Enable: true, @@ -456,7 +470,7 @@ test('setting NLB circuitBreaker works', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DeploymentConfiguration: { DeploymentCircuitBreaker: { Enable: true, @@ -486,11 +500,11 @@ test('setting NLB special listener port to create the listener', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ElasticLoadBalancingV2::Listener', { + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::Listener', { DefaultActions: [ - { + Match.objectLike({ Type: 'forward', - }, + }), ], Port: 2015, Protocol: 'TCP', @@ -514,11 +528,11 @@ test('setting ALB special listener port to create the listener', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ElasticLoadBalancingV2::Listener', { + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::Listener', { DefaultActions: [ - { + Match.objectLike({ Type: 'forward', - }, + }), ], Port: 2015, Protocol: 'HTTP', @@ -547,11 +561,11 @@ test('setting ALB HTTPS protocol to create the listener on 443', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ElasticLoadBalancingV2::Listener', { + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::Listener', { DefaultActions: [ - { + Match.objectLike({ Type: 'forward', - }, + }), ], Port: 443, Protocol: 'HTTPS', @@ -580,7 +594,7 @@ test('setting ALB HTTPS correctly sets the recordset name', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::Route53::RecordSet', { + Template.fromStack(stack).hasResourceProperties('AWS::Route53::RecordSet', { Name: 'test.domain.com.', }); }); @@ -608,7 +622,7 @@ test('setting ALB cname option correctly sets the recordset type', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::Route53::RecordSet', { + Template.fromStack(stack).hasResourceProperties('AWS::Route53::RecordSet', { Name: 'test.domain.com.', Type: 'CNAME', }); @@ -637,7 +651,7 @@ test('setting ALB record type to NONE correctly omits the recordset', () => { }); // THEN - expect(stack).not.toHaveResource('AWS::Route53::RecordSet'); + Template.fromStack(stack).resourceCountIs('AWS::Route53::RecordSet', 0); }); @@ -663,7 +677,7 @@ test('setting NLB cname option correctly sets the recordset type', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::Route53::RecordSet', { + Template.fromStack(stack).hasResourceProperties('AWS::Route53::RecordSet', { Name: 'test.domain.com.', Type: 'CNAME', }); @@ -691,7 +705,7 @@ test('setting NLB record type to NONE correctly omits the recordset', () => { }); // THEN - expect(stack).not.toHaveResource('AWS::Route53::RecordSet'); + Template.fromStack(stack).resourceCountIs('AWS::Route53::RecordSet', 0); }); test('setting ALB HTTP protocol to create the listener on 80', () => { @@ -711,11 +725,11 @@ test('setting ALB HTTP protocol to create the listener on 80', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ElasticLoadBalancingV2::Listener', { + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::Listener', { DefaultActions: [ - { + Match.objectLike({ Type: 'forward', - }, + }), ], Port: 80, Protocol: 'HTTP', @@ -738,11 +752,11 @@ test('setting ALB without any protocol or listenerPort to create the listener on }); // THEN - expect(stack).toHaveResourceLike('AWS::ElasticLoadBalancingV2::Listener', { + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::Listener', { DefaultActions: [ - { + Match.objectLike({ Type: 'forward', - }, + }), ], Port: 80, Protocol: 'HTTP', @@ -765,11 +779,11 @@ test('passing in existing network load balancer to NLB Fargate Service', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { LaunchType: 'FARGATE', }); - expect(stack).toHaveResourceLike('AWS::ElasticLoadBalancingV2::LoadBalancer', { + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::LoadBalancer', { Type: 'network', }); }); @@ -813,14 +827,14 @@ test('passing in imported network load balancer and resources to NLB Fargate ser }); // THEN - expect(stack2).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack2).hasResourceProperties('AWS::ECS::Service', { LaunchType: 'FARGATE', - LoadBalancers: [{ ContainerName: 'myContainer', ContainerPort: 80 }], + LoadBalancers: [Match.objectLike({ ContainerName: 'myContainer', ContainerPort: 80 })], }); - expect(stack2).toHaveResourceLike('AWS::ElasticLoadBalancingV2::TargetGroup'); + Template.fromStack(stack2).resourceCountIs('AWS::ElasticLoadBalancingV2::TargetGroup', 1); - expect(stack2).toHaveResourceLike('AWS::ElasticLoadBalancingV2::Listener', { + Template.fromStack(stack2).hasResourceProperties('AWS::ElasticLoadBalancingV2::Listener', { LoadBalancerArn: nlb2.loadBalancerArn, Port: 80, }); @@ -845,11 +859,11 @@ test('passing in previously created application load balancer to ALB Fargate Ser }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { LaunchType: 'FARGATE', }); - expect(stack).toHaveResourceLike('AWS::ElasticLoadBalancingV2::LoadBalancer', { + Template.fromStack(stack).hasResourceProperties('AWS::ElasticLoadBalancingV2::LoadBalancer', { Type: 'application', }); }); @@ -890,14 +904,14 @@ test('passing in imported application load balancer and resources to ALB Fargate }); // THEN - expect(stack1).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack1).hasResourceProperties('AWS::ECS::Service', { LaunchType: 'FARGATE', - LoadBalancers: [{ ContainerName: 'Container', ContainerPort: 80 }], + LoadBalancers: [Match.objectLike({ ContainerName: 'Container', ContainerPort: 80 })], }); - expect(stack1).toHaveResourceLike('AWS::ElasticLoadBalancingV2::TargetGroup'); + Template.fromStack(stack1).resourceCountIs('AWS::ElasticLoadBalancingV2::TargetGroup', 1); - expect(stack1).toHaveResourceLike('AWS::ElasticLoadBalancingV2::Listener', { + Template.fromStack(stack1).hasResourceProperties('AWS::ElasticLoadBalancingV2::Listener', { LoadBalancerArn: alb.loadBalancerArn, Port: 80, }); @@ -925,11 +939,11 @@ test('passing in previously created security groups to ALB Fargate Service', () }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { LaunchType: 'FARGATE', }); - expect(stack).toHaveResource('AWS::EC2::SecurityGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroup', { GroupDescription: 'Example', GroupName: 'Rolly', SecurityGroupEgress: [ @@ -973,9 +987,7 @@ test('domainName and domainZone not required for HTTPS listener with provided ce }); // THEN - expect(stack).not.toHaveResourceLike('AWS::Route53::RecordSet', { - Name: 'test.domain.com.', - }); + Template.fromStack(stack).resourceCountIs('AWS::Route53::RecordSet', 0); }); test('test ALB load balanced service with docker labels defined', () => { @@ -994,15 +1006,15 @@ test('test ALB load balanced service with docker labels defined', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Image: '/aws/aws-example-app', DockerLabels: { label1: 'labelValue1', label2: 'labelValue2', }, - }, + }), ], }); }); @@ -1023,15 +1035,15 @@ test('test Network load balanced service with docker labels defined', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Image: '/aws/aws-example-app', DockerLabels: { label1: 'labelValue1', label2: 'labelValue2', }, - }, + }), ], }); }); diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/queue-processing-fargate-service.test.ts b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/queue-processing-fargate-service.test.ts index 6f91b633d1249..b6ca462f52a2b 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/queue-processing-fargate-service.test.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/queue-processing-fargate-service.test.ts @@ -1,5 +1,4 @@ -import { ABSENT } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import { AutoScalingGroup } from '@aws-cdk/aws-autoscaling'; import { MachineImage } from '@aws-cdk/aws-ec2'; import * as ec2 from '@aws-cdk/aws-ec2'; @@ -32,12 +31,12 @@ test('test fargate queue worker service construct - with only required props', ( }); // THEN - QueueWorker is of FARGATE launch type, an SQS queue is created and all default properties are set. - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DesiredCount: 1, LaunchType: 'FARGATE', }); - expect(stack).toHaveResource('AWS::SQS::Queue', { + Template.fromStack(stack).hasResourceProperties('AWS::SQS::Queue', { RedrivePolicy: { deadLetterTargetArn: { 'Fn::GetAtt': [ @@ -49,11 +48,11 @@ test('test fargate queue worker service construct - with only required props', ( }, }); - expect(stack).toHaveResource('AWS::SQS::Queue', { + Template.fromStack(stack).hasResourceProperties('AWS::SQS::Queue', { MessageRetentionPeriod: 1209600, }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -77,9 +76,9 @@ test('test fargate queue worker service construct - with only required props', ( }, }); - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Environment: [ { Name: 'QUEUE_NAME', @@ -104,7 +103,7 @@ test('test fargate queue worker service construct - with only required props', ( }, }, Image: 'test', - }, + }), ], Family: 'ServiceQueueProcessingTaskDef83DB34F1', }); @@ -126,8 +125,8 @@ test('test fargate queue worker service construct - with remove default desiredC }); // THEN - QueueWorker is of FARGATE launch type, and desiredCount is not defined on the FargateService. - expect(stack).toHaveResource('AWS::ECS::Service', { - DesiredCount: ABSENT, + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { + DesiredCount: Match.absent(), LaunchType: 'FARGATE', }); }); @@ -156,12 +155,12 @@ test('test fargate queue worker service construct - with optional props for queu }); // THEN - QueueWorker is of FARGATE launch type, an SQS queue is created and all default properties are set. - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DesiredCount: 1, LaunchType: 'FARGATE', }); - expect(stack).toHaveResource('AWS::SQS::Queue', { + Template.fromStack(stack).hasResourceProperties('AWS::SQS::Queue', { RedrivePolicy: { deadLetterTargetArn: { 'Fn::GetAtt': [ @@ -174,11 +173,11 @@ test('test fargate queue worker service construct - with optional props for queu VisibilityTimeout: 300, }); - expect(stack).toHaveResource('AWS::SQS::Queue', { + Template.fromStack(stack).hasResourceProperties('AWS::SQS::Queue', { MessageRetentionPeriod: 604800, }); - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -202,9 +201,9 @@ test('test fargate queue worker service construct - with optional props for queu }, }); - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Environment: [ { Name: 'QUEUE_NAME', @@ -229,7 +228,7 @@ test('test fargate queue worker service construct - with optional props for queu }, }, Image: 'test', - }, + }), ], Family: 'ServiceQueueProcessingTaskDef83DB34F1', }); @@ -276,7 +275,7 @@ test('test Fargate queue worker service construct - without desiredCount specifi }); // THEN - QueueWorker is of FARGATE launch type, an SQS queue is created and all optional properties are set. - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DeploymentConfiguration: { MinimumHealthyPercent: 60, MaximumPercent: 150, @@ -289,16 +288,16 @@ test('test Fargate queue worker service construct - without desiredCount specifi }, }); - expect(stack).toHaveResource('AWS::ApplicationAutoScaling::ScalableTarget', { + Template.fromStack(stack).hasResourceProperties('AWS::ApplicationAutoScaling::ScalableTarget', { MaxCapacity: 5, MinCapacity: 2, }); - expect(stack).toHaveResource('AWS::SQS::Queue', { QueueName: 'fargate-test-sqs-queue' }); + Template.fromStack(stack).hasResourceProperties('AWS::SQS::Queue', { QueueName: 'fargate-test-sqs-queue' }); - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Command: [ '-c', '4', @@ -324,7 +323,7 @@ test('test Fargate queue worker service construct - without desiredCount specifi }, ], Image: 'test', - }, + }), ], Family: 'fargate-task-family', }); @@ -369,7 +368,7 @@ testDeprecated('test Fargate queue worker service construct - with optional prop }); // THEN - QueueWorker is of FARGATE launch type, an SQS queue is created and all optional properties are set. - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { DesiredCount: 2, DeploymentConfiguration: { MinimumHealthyPercent: 60, @@ -387,11 +386,11 @@ testDeprecated('test Fargate queue worker service construct - with optional prop }, }); - expect(stack).toHaveResource('AWS::SQS::Queue', { QueueName: 'fargate-test-sqs-queue' }); + Template.fromStack(stack).hasResourceProperties('AWS::SQS::Queue', { QueueName: 'fargate-test-sqs-queue' }); - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Command: [ '-c', '4', @@ -417,7 +416,7 @@ testDeprecated('test Fargate queue worker service construct - with optional prop }, ], Image: 'test', - }, + }), ], Family: 'fargate-task-family', }); @@ -443,11 +442,11 @@ test('can set custom containerName', () => { image: ecs.ContainerImage.fromRegistry('test'), }); - expect(stack).toHaveResourceLike('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ - { + Match.objectLike({ Name: 'my-container', - }, + }), ], }); }); @@ -482,7 +481,7 @@ test('can set custom networking options', () => { }); // THEN - NetworkConfiguration is created with the specific security groups and selected subnets - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { LaunchType: 'FARGATE', NetworkConfiguration: { AwsvpcConfiguration: { @@ -521,7 +520,7 @@ test('can set use public IP', () => { }); // THEN - The Subnets defaults to Public and AssignPublicIp settings change to ENABLED - expect(stack).toHaveResource('AWS::ECS::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { LaunchType: 'FARGATE', NetworkConfiguration: { AwsvpcConfiguration: { @@ -573,8 +572,8 @@ test('can set capacity provider strategies', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::Service', { - LaunchType: ABSENT, + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { + LaunchType: Match.absent(), CapacityProviderStrategy: [ { CapacityProvider: 'FARGATE_SPOT', diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/scheduled-fargate-task.test.ts b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/scheduled-fargate-task.test.ts index 5d37bba01af2f..696a413ad2def 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/scheduled-fargate-task.test.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/scheduled-fargate-task.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as ecs from '@aws-cdk/aws-ecs'; import * as events from '@aws-cdk/aws-events'; @@ -21,7 +21,7 @@ test('Can create a scheduled Fargate Task - with only required props', () => { }); // THEN - expect(stack).toHaveResource('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { State: 'ENABLED', Targets: [ { @@ -56,7 +56,7 @@ test('Can create a scheduled Fargate Task - with only required props', () => { ], }); - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Essential: true, @@ -100,7 +100,7 @@ test('Can create a scheduled Fargate Task - with optional props', () => { }); // THEN - expect(stack).toHaveResource('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { Name: 'sample-scheduled-task-rule', State: 'DISABLED', Targets: [ @@ -136,7 +136,7 @@ test('Can create a scheduled Fargate Task - with optional props', () => { ], }); - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Environment: [ @@ -180,7 +180,7 @@ test('Scheduled Fargate Task - with MemoryReservation defined', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Essential: true, @@ -219,7 +219,7 @@ test('Scheduled Fargate Task - with Command defined', () => { }); // THEN - expect(stack).toHaveResource('AWS::ECS::TaskDefinition', { + Template.fromStack(stack).hasResourceProperties('AWS::ECS::TaskDefinition', { ContainerDefinitions: [ { Command: [ @@ -268,22 +268,22 @@ test('Scheduled Fargate Task - with subnetSelection defined', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { Targets: [ - { - EcsParameters: { + Match.objectLike({ + EcsParameters: Match.objectLike({ NetworkConfiguration: { - AwsVpcConfiguration: { + AwsVpcConfiguration: Match.objectLike({ AssignPublicIp: 'ENABLED', Subnets: [ { Ref: 'VpcPublicSubnet1Subnet5C2D37C4', }, ], - }, + }), }, - }, - }, + }), + }), ], }); }); @@ -305,7 +305,7 @@ test('Scheduled Fargate Task - with platformVersion defined', () => { }); // THEN - expect(stack).toHaveResource('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { Targets: [ { Arn: { 'Fn::GetAtt': ['EcsCluster97242B84', 'Arn'] }, @@ -359,7 +359,7 @@ test('Scheduled Fargate Task - with securityGroups defined', () => { }); // THEN - expect(stack).toHaveResource('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { Targets: [ { Arn: { 'Fn::GetAtt': ['EcsCluster97242B84', 'Arn'] }, From d220dd66db245292470002f4fc24137340080a6f Mon Sep 17 00:00:00 2001 From: AWS CDK Automation <43080478+aws-cdk-automation@users.noreply.github.com> Date: Wed, 19 Jan 2022 14:08:08 -0800 Subject: [PATCH 39/50] chore: npm-check-updates && yarn upgrade (#18528) Ran npm-check-updates and yarn upgrade to keep the `yarn.lock` file up-to-date. --- package.json | 2 +- .../@aws-cdk/assert-internal/package.json | 2 +- packages/@aws-cdk/assert/package.json | 2 +- packages/@aws-cdk/assertions/package.json | 2 +- .../aws-apigatewayv2-authorizers/package.json | 2 +- .../package.json | 4 +- .../@aws-cdk/aws-cloudformation/package.json | 2 +- .../aws-cognito-identitypool/package.json | 4 +- packages/@aws-cdk/aws-dynamodb/package.json | 4 +- packages/@aws-cdk/aws-ec2/package.json | 2 +- packages/@aws-cdk/aws-eks/package.json | 6 +- packages/@aws-cdk/aws-iam/package.json | 2 +- packages/@aws-cdk/aws-lambda/package.json | 2 +- packages/@aws-cdk/aws-logs/package.json | 2 +- packages/@aws-cdk/aws-route53/package.json | 2 +- packages/@aws-cdk/aws-s3/package.json | 2 +- packages/@aws-cdk/aws-sam/package.json | 2 +- packages/@aws-cdk/aws-ses/package.json | 2 +- .../@aws-cdk/cloudformation-diff/package.json | 2 +- .../cloudformation-include/package.json | 2 +- packages/@aws-cdk/core/package.json | 2 +- .../@aws-cdk/custom-resources/package.json | 2 +- .../@monocdk-experiment/assert/package.json | 2 +- packages/aws-cdk/package.json | 4 +- tools/@aws-cdk/cdk-build-tools/package.json | 2 +- yarn.lock | 118 +++++++++--------- 26 files changed, 90 insertions(+), 90 deletions(-) diff --git a/package.json b/package.json index be3d9e61e48c4..b99f8690e2171 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ }, "devDependencies": { "@yarnpkg/lockfile": "^1.1.0", - "cdk-generate-synthetic-examples": "^0.1.2", + "cdk-generate-synthetic-examples": "^0.1.3", "conventional-changelog-cli": "^2.2.2", "fs-extra": "^9.1.0", "graceful-fs": "^4.2.9", diff --git a/packages/@aws-cdk/assert-internal/package.json b/packages/@aws-cdk/assert-internal/package.json index fc4bf503aeae3..99b32de7a66c9 100644 --- a/packages/@aws-cdk/assert-internal/package.json +++ b/packages/@aws-cdk/assert-internal/package.json @@ -28,7 +28,7 @@ "@aws-cdk/pkglint": "0.0.0", "@types/jest": "^27.4.0", "jest": "^27.4.7", - "ts-jest": "^27.1.2" + "ts-jest": "^27.1.3" }, "dependencies": { "@aws-cdk/cloud-assembly-schema": "0.0.0", diff --git a/packages/@aws-cdk/assert/package.json b/packages/@aws-cdk/assert/package.json index ef707331605ac..1a66fcf3a6d99 100644 --- a/packages/@aws-cdk/assert/package.json +++ b/packages/@aws-cdk/assert/package.json @@ -41,7 +41,7 @@ "aws-cdk-migration": "0.0.0", "constructs": "^3.3.69", "jest": "^27.3.1", - "ts-jest": "^27.1.2" + "ts-jest": "^27.1.3" }, "dependencies": { "@aws-cdk/cloudformation-diff": "0.0.0", diff --git a/packages/@aws-cdk/assertions/package.json b/packages/@aws-cdk/assertions/package.json index 1073d76e836e2..31bcf0b7d0067 100644 --- a/packages/@aws-cdk/assertions/package.json +++ b/packages/@aws-cdk/assertions/package.json @@ -68,7 +68,7 @@ "@types/jest": "^27.4.0", "constructs": "^3.3.69", "jest": "^27.4.7", - "ts-jest": "^27.1.2" + "ts-jest": "^27.1.3" }, "dependencies": { "@aws-cdk/cloud-assembly-schema": "0.0.0", diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json b/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json index ffe4d53a30896..e1f2183ab9e43 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json @@ -85,7 +85,7 @@ "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/pkglint": "0.0.0", - "@types/aws-lambda": "^8.10.89", + "@types/aws-lambda": "^8.10.90", "@types/jest": "^27.4.0" }, "dependencies": { diff --git a/packages/@aws-cdk/aws-certificatemanager/lambda-packages/dns_validated_certificate_handler/package.json b/packages/@aws-cdk/aws-certificatemanager/lambda-packages/dns_validated_certificate_handler/package.json index db0b2433d6583..5766802aa1e3e 100644 --- a/packages/@aws-cdk/aws-certificatemanager/lambda-packages/dns_validated_certificate_handler/package.json +++ b/packages/@aws-cdk/aws-certificatemanager/lambda-packages/dns_validated_certificate_handler/package.json @@ -29,7 +29,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/aws-lambda": "^8.10.89", + "@types/aws-lambda": "^8.10.90", "@types/sinon": "^9.0.11", "@aws-cdk/cdk-build-tools": "0.0.0", "aws-sdk": "^2.596.0", @@ -44,6 +44,6 @@ "lambda-tester": "^3.6.0", "sinon": "^9.2.4", "nock": "^13.2.2", - "ts-jest": "^27.1.2" + "ts-jest": "^27.1.3" } } diff --git a/packages/@aws-cdk/aws-cloudformation/package.json b/packages/@aws-cdk/aws-cloudformation/package.json index 5a7014994d6c5..f9d68e24b15a3 100644 --- a/packages/@aws-cdk/aws-cloudformation/package.json +++ b/packages/@aws-cdk/aws-cloudformation/package.json @@ -79,7 +79,7 @@ "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", - "@types/aws-lambda": "^8.10.89", + "@types/aws-lambda": "^8.10.90", "@types/jest": "^27.4.0", "jest": "^27.4.7" }, diff --git a/packages/@aws-cdk/aws-cognito-identitypool/package.json b/packages/@aws-cdk/aws-cognito-identitypool/package.json index e7b9183ce8ba7..db55fece60596 100644 --- a/packages/@aws-cdk/aws-cognito-identitypool/package.json +++ b/packages/@aws-cdk/aws-cognito-identitypool/package.json @@ -74,8 +74,8 @@ "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/pkglint": "0.0.0", - "@types/jest": "^27.0.3", - "jest": "^27.4.5" + "@types/jest": "^27.4.0", + "jest": "^27.4.7" }, "dependencies": { "@aws-cdk/aws-cognito": "0.0.0", diff --git a/packages/@aws-cdk/aws-dynamodb/package.json b/packages/@aws-cdk/aws-dynamodb/package.json index 3c23858110111..a4953cd3fb355 100644 --- a/packages/@aws-cdk/aws-dynamodb/package.json +++ b/packages/@aws-cdk/aws-dynamodb/package.json @@ -84,14 +84,14 @@ "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", - "@types/aws-lambda": "^8.10.89", + "@types/aws-lambda": "^8.10.90", "@types/jest": "^27.4.0", "@types/sinon": "^9.0.11", "aws-sdk": "^2.848.0", "aws-sdk-mock": "^5.5.1", "jest": "^27.4.7", "sinon": "^9.2.4", - "ts-jest": "^27.1.2" + "ts-jest": "^27.1.3" }, "dependencies": { "@aws-cdk/aws-applicationautoscaling": "0.0.0", diff --git a/packages/@aws-cdk/aws-ec2/package.json b/packages/@aws-cdk/aws-ec2/package.json index eb8c9cac4c78c..513bd7b379b3b 100644 --- a/packages/@aws-cdk/aws-ec2/package.json +++ b/packages/@aws-cdk/aws-ec2/package.json @@ -86,7 +86,7 @@ "@aws-cdk/cloud-assembly-schema": "0.0.0", "@aws-cdk/cx-api": "0.0.0", "@aws-cdk/pkglint": "0.0.0", - "@types/aws-lambda": "^8.10.89", + "@types/aws-lambda": "^8.10.90", "@types/jest": "^27.4.0", "jest": "^27.4.7" }, diff --git a/packages/@aws-cdk/aws-eks/package.json b/packages/@aws-cdk/aws-eks/package.json index e5554ffcf8ba2..79ea6601f9fff 100644 --- a/packages/@aws-cdk/aws-eks/package.json +++ b/packages/@aws-cdk/aws-eks/package.json @@ -84,13 +84,13 @@ "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", - "@types/aws-lambda": "^8.10.89", + "@types/aws-lambda": "^8.10.90", "@types/jest": "^27.4.0", "@types/sinon": "^9.0.11", "@types/yaml": "1.9.6", "aws-sdk": "^2.848.0", - "cdk8s": "^1.3.32", - "cdk8s-plus-21": "^1.0.0-beta.73", + "cdk8s": "^1.4.6", + "cdk8s-plus-21": "^1.0.0-beta.77", "jest": "^27.4.7", "sinon": "^9.2.4" }, diff --git a/packages/@aws-cdk/aws-iam/package.json b/packages/@aws-cdk/aws-iam/package.json index fec5003622341..abe60b8a954a0 100644 --- a/packages/@aws-cdk/aws-iam/package.json +++ b/packages/@aws-cdk/aws-iam/package.json @@ -85,7 +85,7 @@ "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", - "@types/aws-lambda": "^8.10.89", + "@types/aws-lambda": "^8.10.90", "@types/jest": "^27.4.0", "@types/sinon": "^9.0.11", "jest": "^27.4.7", diff --git a/packages/@aws-cdk/aws-lambda/package.json b/packages/@aws-cdk/aws-lambda/package.json index a45dfbc33c284..271c7b3c2ff35 100644 --- a/packages/@aws-cdk/aws-lambda/package.json +++ b/packages/@aws-cdk/aws-lambda/package.json @@ -89,7 +89,7 @@ "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/cfnspec": "0.0.0", "@aws-cdk/pkglint": "0.0.0", - "@types/aws-lambda": "^8.10.89", + "@types/aws-lambda": "^8.10.90", "@types/jest": "^27.4.0", "@types/lodash": "^4.14.178", "jest": "^27.4.7", diff --git a/packages/@aws-cdk/aws-logs/package.json b/packages/@aws-cdk/aws-logs/package.json index a2c39272e68d1..9837e00b2eb05 100644 --- a/packages/@aws-cdk/aws-logs/package.json +++ b/packages/@aws-cdk/aws-logs/package.json @@ -84,7 +84,7 @@ "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", - "@types/aws-lambda": "^8.10.89", + "@types/aws-lambda": "^8.10.90", "@types/jest": "^27.4.0", "@types/sinon": "^9.0.11", "aws-sdk": "^2.848.0", diff --git a/packages/@aws-cdk/aws-route53/package.json b/packages/@aws-cdk/aws-route53/package.json index 454e121b690cd..5dff656604769 100644 --- a/packages/@aws-cdk/aws-route53/package.json +++ b/packages/@aws-cdk/aws-route53/package.json @@ -84,7 +84,7 @@ "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", - "@types/aws-lambda": "^8.10.89", + "@types/aws-lambda": "^8.10.90", "@types/jest": "^27.4.0", "aws-sdk": "^2.848.0", "jest": "^27.4.7" diff --git a/packages/@aws-cdk/aws-s3/package.json b/packages/@aws-cdk/aws-s3/package.json index 4fd85b0b4152f..2c7ea2bc6b9f2 100644 --- a/packages/@aws-cdk/aws-s3/package.json +++ b/packages/@aws-cdk/aws-s3/package.json @@ -84,7 +84,7 @@ "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", - "@types/aws-lambda": "^8.10.89", + "@types/aws-lambda": "^8.10.90", "@types/jest": "^27.4.0", "jest": "^27.4.7" }, diff --git a/packages/@aws-cdk/aws-sam/package.json b/packages/@aws-cdk/aws-sam/package.json index 4f0c15f75940a..37ddba18cf79b 100644 --- a/packages/@aws-cdk/aws-sam/package.json +++ b/packages/@aws-cdk/aws-sam/package.json @@ -79,7 +79,7 @@ "@aws-cdk/pkglint": "0.0.0", "@types/jest": "^27.4.0", "jest": "^27.4.7", - "ts-jest": "^27.1.2" + "ts-jest": "^27.1.3" }, "dependencies": { "@aws-cdk/core": "0.0.0", diff --git a/packages/@aws-cdk/aws-ses/package.json b/packages/@aws-cdk/aws-ses/package.json index 64ad0fc5f67ce..9b49c6d9b0933 100644 --- a/packages/@aws-cdk/aws-ses/package.json +++ b/packages/@aws-cdk/aws-ses/package.json @@ -84,7 +84,7 @@ "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", - "@types/aws-lambda": "^8.10.89", + "@types/aws-lambda": "^8.10.90", "@types/jest": "^27.4.0", "jest": "^27.4.7" }, diff --git a/packages/@aws-cdk/cloudformation-diff/package.json b/packages/@aws-cdk/cloudformation-diff/package.json index 6cbf48e79210d..0db6702ef67f0 100644 --- a/packages/@aws-cdk/cloudformation-diff/package.json +++ b/packages/@aws-cdk/cloudformation-diff/package.json @@ -38,7 +38,7 @@ "@types/string-width": "^4.0.1", "fast-check": "^2.21.0", "jest": "^27.4.7", - "ts-jest": "^27.1.2" + "ts-jest": "^27.1.3" }, "repository": { "url": "https://github.com/aws/aws-cdk.git", diff --git a/packages/@aws-cdk/cloudformation-include/package.json b/packages/@aws-cdk/cloudformation-include/package.json index 425756e78a3c7..f0455eb86a21f 100644 --- a/packages/@aws-cdk/cloudformation-include/package.json +++ b/packages/@aws-cdk/cloudformation-include/package.json @@ -454,7 +454,7 @@ "@aws-cdk/pkglint": "0.0.0", "@types/jest": "^27.4.0", "jest": "^27.4.7", - "ts-jest": "^27.1.2" + "ts-jest": "^27.1.3" }, "bundledDependencies": [ "yaml" diff --git a/packages/@aws-cdk/core/package.json b/packages/@aws-cdk/core/package.json index 5889ed8517559..686c71aaba3cf 100644 --- a/packages/@aws-cdk/core/package.json +++ b/packages/@aws-cdk/core/package.json @@ -178,7 +178,7 @@ "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", - "@types/aws-lambda": "^8.10.89", + "@types/aws-lambda": "^8.10.90", "@types/fs-extra": "^8.1.2", "@types/jest": "^27.4.0", "@types/lodash": "^4.14.178", diff --git a/packages/@aws-cdk/custom-resources/package.json b/packages/@aws-cdk/custom-resources/package.json index cb6707b8b1611..11f7254664669 100644 --- a/packages/@aws-cdk/custom-resources/package.json +++ b/packages/@aws-cdk/custom-resources/package.json @@ -87,7 +87,7 @@ "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", - "@types/aws-lambda": "^8.10.89", + "@types/aws-lambda": "^8.10.90", "@types/fs-extra": "^8.1.2", "@types/jest": "^27.4.0", "@types/sinon": "^9.0.11", diff --git a/packages/@monocdk-experiment/assert/package.json b/packages/@monocdk-experiment/assert/package.json index ac8eb9375ba08..f5880d8195f90 100644 --- a/packages/@monocdk-experiment/assert/package.json +++ b/packages/@monocdk-experiment/assert/package.json @@ -41,7 +41,7 @@ "jest": "^27.4.7", "monocdk": "0.0.0", "@aws-cdk/pkglint": "0.0.0", - "ts-jest": "^27.1.2" + "ts-jest": "^27.1.3" }, "dependencies": { "@aws-cdk/cloudformation-diff": "0.0.0" diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index 3480b90dbd3a1..e2404ac8515a8 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -62,7 +62,7 @@ "nock": "^13.2.2", "@aws-cdk/pkglint": "0.0.0", "sinon": "^9.2.4", - "ts-jest": "^27.1.2", + "ts-jest": "^27.1.3", "ts-mock-imports": "^1.3.8", "xml-js": "^1.6.11" }, @@ -76,7 +76,7 @@ "aws-sdk": "^2.979.0", "camelcase": "^6.3.0", "cdk-assets": "0.0.0", - "chokidar": "^3.5.2", + "chokidar": "^3.5.3", "chalk": "^4", "decamelize": "^5.0.1", "fs-extra": "^9.1.0", diff --git a/tools/@aws-cdk/cdk-build-tools/package.json b/tools/@aws-cdk/cdk-build-tools/package.json index ae33d288d14df..203d7142e47f3 100644 --- a/tools/@aws-cdk/cdk-build-tools/package.json +++ b/tools/@aws-cdk/cdk-build-tools/package.json @@ -62,7 +62,7 @@ "markdownlint-cli": "^0.30.0", "nyc": "^15.1.0", "semver": "^7.3.5", - "ts-jest": "^27.1.2", + "ts-jest": "^27.1.3", "typescript": "~3.9.10", "yargs": "^16.2.0" }, diff --git a/yarn.lock b/yarn.lock index bf3c0213efaee..da1eded3a0af7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1582,10 +1582,10 @@ dependencies: "@types/glob" "*" -"@types/aws-lambda@^8.10.89": - version "8.10.89" - resolved "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.89.tgz#22617ecc1eef9571abebb50c947553362da6051b" - integrity sha512-jwtSuEZj4rY4R2pAEOXi+RutS8RWbwMzoGlRVukdyOpnfqA/XPkAf8QoGWmg4o/UaNpQ8Mj0Xhkp5SZ1t/Zq4Q== +"@types/aws-lambda@^8.10.90": + version "8.10.90" + resolved "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.90.tgz#d9995deb8925afb78b8c6179fc58fa9316925102" + integrity sha512-B08uyjXQuOh/Kyj9NZ0BEJjkfqqj4tsAYpbBgUMRydtYAs1Fs5WL5KiYFtu+OoG5mI64pnsvW8ohFgpJYWdWIg== "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": version "7.1.18" @@ -1694,7 +1694,7 @@ jest-diff "^26.0.0" pretty-format "^26.0.0" -"@types/jest@^27.0.3", "@types/jest@^27.4.0": +"@types/jest@^27.4.0": version "27.4.0" resolved "https://registry.npmjs.org/@types/jest/-/jest-27.4.0.tgz#037ab8b872067cae842a320841693080f9cb84ed" integrity sha512-gHl8XuC1RZ8H2j5sHv/JqsaxXkDDM9iDOgu0Wp8sjs4u/snb2PVehyWXJPr+ORA0RPpgw231mnutWI1+0hgjIQ== @@ -1752,9 +1752,9 @@ integrity sha512-uv53RrNdhbkV/3VmVCtfImfYCWC3GTTRn3R11Whni3EJ+gb178tkZBVNj2edLY5CMrB749dQi+SJkg87jsN8UQ== "@types/node@*", "@types/node@>= 8": - version "17.0.8" - resolved "https://registry.npmjs.org/@types/node/-/node-17.0.8.tgz#50d680c8a8a78fe30abe6906453b21ad8ab0ad7b" - integrity sha512-YofkM6fGv4gDJq78g4j0mMuGMkZVxZDgtU0JRdx6FgiJDG+0fY0GKVolOV8WqVmEhLCXkQRjwDdKyPxJp/uucg== + version "17.0.10" + resolved "https://registry.npmjs.org/@types/node/-/node-17.0.10.tgz#616f16e9d3a2a3d618136b1be244315d95bd7cab" + integrity sha512-S/3xB4KzyFxYGCppyDt68yzBU9ysL88lSdIah4D6cptdcltc4NCPCAMc0+PCpg/lLIyC7IPvj2Z52OJWeIUkog== "@types/node@^10.17.60": version "10.17.60" @@ -1762,9 +1762,9 @@ integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== "@types/node@^16.9.2": - version "16.11.19" - resolved "https://registry.npmjs.org/@types/node/-/node-16.11.19.tgz#1afa165146997b8286b6eabcb1c2d50729055169" - integrity sha512-BPAcfDPoHlRQNKktbsbnpACGdypPFBuX4xQlsWDE7B8XXcfII+SpOLay3/qZmCLb39kV5S1RTYwXdkx2lwLYng== + version "16.11.21" + resolved "https://registry.npmjs.org/@types/node/-/node-16.11.21.tgz#474d7589a30afcf5291f59bd49cca9ad171ffde4" + integrity sha512-Pf8M1XD9i1ksZEcCP8vuSNwooJ/bZapNmIzpmsMaL+jMI+8mEYU3PKvs+xDNuQcJWF/x24WzY4qxLtB0zNow9A== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -2053,9 +2053,9 @@ ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: uri-js "^4.2.2" ajv@^8.0.1: - version "8.8.2" - resolved "https://registry.npmjs.org/ajv/-/ajv-8.8.2.tgz#01b4fef2007a28bf75f0b7fc009f62679de4abbb" - integrity sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw== + version "8.9.0" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz#738019146638824dea25edcf299dcba1b0e7eb18" + integrity sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" @@ -2301,9 +2301,9 @@ aws-sdk-mock@^5.5.1: traverse "^0.6.6" aws-sdk@^2.596.0, aws-sdk@^2.848.0, aws-sdk@^2.928.0, aws-sdk@^2.979.0: - version "2.1055.0" - resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1055.0.tgz#07beb86650d5a313f7c899807c51b12b5e2f4117" - integrity sha512-99drH3mvXakw9we8Rs2cDQmi2pS7PVAC9pvTlB7lHPUwLYftMlko5cFMceZxvTHeyLkdvg98iNIHI3hbnzitoQ== + version "2.1059.0" + resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1059.0.tgz#8ce3fa0652ef2836665d0c3566bc56a3d5cfc929" + integrity sha512-Q+6T9kpO6aobUNboTOk9MVAmWbs/KK0pxgCNFK0M8YO+7EWUFkNOLHM9tdYOP5vsJK5pLz6D2t2w3lHQjKzGlg== dependencies: buffer "4.9.2" events "1.1.1" @@ -2598,9 +2598,9 @@ camelcase@^6.2.0, camelcase@^6.2.1, camelcase@^6.3.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001286: - version "1.0.30001299" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001299.tgz#d753bf6444ed401eb503cbbe17aa3e1451b5a68c" - integrity sha512-iujN4+x7QzqA2NCSrS5VUy+4gLmRd4xv6vbBBsmfVqTx8bLAD8097euLqQgKxSVLvxjSDcvF1T/i9ocgnUFexw== + version "1.0.30001300" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001300.tgz#11ab6c57d3eb6f964cba950401fd00a146786468" + integrity sha512-cVjiJHWGcNlJi8TZVKNMnvMid3Z3TTdDHmLDzlOdIiZq138Exvo0G+G0wTdVYolxKb4AYwC+38pxodiInVtJSA== case@1.6.3, case@^1.6.3: version "1.6.3" @@ -2612,10 +2612,10 @@ caseless@~0.12.0: resolved "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -cdk-generate-synthetic-examples@^0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/cdk-generate-synthetic-examples/-/cdk-generate-synthetic-examples-0.1.2.tgz#356e9a96c8f5f681245847d15407b6fc48038cd7" - integrity sha512-vHM+U/JIBU/xiC0/Joo8UOkcqPdN3vBMsToib/ekD3nraDgeFeKZ2I0DAxetjk+9Ned34NBA+u8malVxwVuiTw== +cdk-generate-synthetic-examples@^0.1.3: + version "0.1.3" + resolved "https://registry.npmjs.org/cdk-generate-synthetic-examples/-/cdk-generate-synthetic-examples-0.1.3.tgz#ec31aa901e364e279c2ca1d081edc94c9c801f54" + integrity sha512-745IaTttxe1TZtiLBPiFvplQX1+4hIGs3DS0etzod6fNL3Kf5Ht/+2b/Aqpvldv29vyUIHg5Zozo1gHdeNudgA== dependencies: "@jsii/spec" "^1.52.1" fs-extra "^10.0.0" @@ -2624,17 +2624,17 @@ cdk-generate-synthetic-examples@^0.1.2: jsii-rosetta "^1.52.1" yargs "^17.3.1" -cdk8s-plus-21@^1.0.0-beta.73: - version "1.0.0-beta.73" - resolved "https://registry.npmjs.org/cdk8s-plus-21/-/cdk8s-plus-21-1.0.0-beta.73.tgz#72e611ad1d8a168e7b62b07e6cc979b4e73dddc8" - integrity sha512-a7RtYlNZwiSzknvhC2+9LHpLUxCMIrfgsRaIjLgxDPgNImCdpPgvlVCdMXuntvXWHgTeTSh+PwSau0mO2qphnQ== +cdk8s-plus-21@^1.0.0-beta.77: + version "1.0.0-beta.77" + resolved "https://registry.npmjs.org/cdk8s-plus-21/-/cdk8s-plus-21-1.0.0-beta.77.tgz#3ac09d3612cfd7a64e3ff94e9a1016b29eefcfbf" + integrity sha512-W/wqEqFeh7x6hVwaiqFnxilMrjNDbyfpM3Yfh7SytDVFjPdQmzISAVe/kpnP8Om07A8mUHE/synGSvDwRjoVdA== dependencies: minimatch "^3.0.4" -cdk8s@^1.3.32: - version "1.3.32" - resolved "https://registry.npmjs.org/cdk8s/-/cdk8s-1.3.32.tgz#bbb21a249827ba48b0521f25121d47fd8ec1a548" - integrity sha512-E1BM3DFEdkrwHaiBXBoil7fVN882PXwE12RLr/Czou18wUdcjb770VS3+MhJuRrqS4RDCY6bmsGr4GYLyjM7CA== +cdk8s@^1.4.6: + version "1.4.6" + resolved "https://registry.npmjs.org/cdk8s/-/cdk8s-1.4.6.tgz#7d38d5a2ac4de11a48e9e1ddf1375b66b826a355" + integrity sha512-LAUIvbIVq8qNVHn5CEnUpFlbpvgsUrXW1fj7BDgBu0FGODdUjab5iUYZjMT/3Cao8I+mQj4xLfvkhzl5uYXIAA== dependencies: fast-json-patch "^2.2.1" follow-redirects "^1.14.7" @@ -2680,10 +2680,10 @@ charenc@0.0.2: resolved "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= -chokidar@^3.5.2: - version "3.5.2" - resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" - integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== +chokidar@^3.5.3: + version "3.5.3" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== dependencies: anymatch "~3.1.2" braces "~3.0.2" @@ -2922,9 +2922,9 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= constructs@^3.3.69: - version "3.3.186" - resolved "https://registry.npmjs.org/constructs/-/constructs-3.3.186.tgz#8a2deca58191d4a47ce564d82800c78221a0d064" - integrity sha512-QgX306tEq7aUo2CnJlhlYyHgBbuwHdmSxFFg42qR2AvOr8f9tHZA4326OlfsiPrTu0I5JmcER7fGsT2K9vIhVw== + version "3.3.193" + resolved "https://registry.npmjs.org/constructs/-/constructs-3.3.193.tgz#6fe4ff6495127337cff10f70a81d546e61cab530" + integrity sha512-85lCvrPlrlTnKS/rpl/jiB9y5t4GGWHhubRP3hlQbKiGAeQ2VIqcjODsU5oorrDi5JzDxcdZ83vfPtP1RtELWA== conventional-changelog-angular@^5.0.12: version "5.0.13" @@ -3569,9 +3569,9 @@ ecc-jsbn@~0.1.1: safer-buffer "^2.1.0" electron-to-chromium@^1.4.17: - version "1.4.43" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.43.tgz#665c0cd8d5e7cce0ba78d90a514c8c813ca3bdbe" - integrity sha512-PO3kEfcxPrti/4STbXvCkNIF4fgWvCKl2508e6UI7KomCDffpIfeBZLXsh5DK/XGsjUw3kwq6WEsi0MJTlGAdg== + version "1.4.48" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.48.tgz#1948b5227aa0ca1ed690945eae1adbe9e7904575" + integrity sha512-RT3SEmpv7XUA+tKXrZGudAWLDpa7f8qmhjcLaM6OD/ERxjQ/zAojT8/Vvo0BSzbArkElFZ1WyZ9FuwAYbkdBNA== emittery@^0.8.1: version "0.8.1" @@ -4213,9 +4213,9 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-glob@^3.2.9: - version "3.2.10" - resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.10.tgz#2734f83baa7f43b7fd41e13bc34438f4ffe284ee" - integrity sha512-s9nFhFnvR63wls6/kM88kQqDhMu0AfdjqouE2l5GVQPbqLgyFjjU5ry/r2yKsJxpb9Py1EYNqieFrmMaX4v++A== + version "3.2.11" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" + integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" @@ -5802,7 +5802,7 @@ jest-worker@^27.4.6: merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^27.3.1, jest@^27.4.5, jest@^27.4.7: +jest@^27.3.1, jest@^27.4.7: version "27.4.7" resolved "https://registry.npmjs.org/jest/-/jest-27.4.7.tgz#87f74b9026a1592f2da05b4d258e57505f28eca4" integrity sha512-8heYvsx7nV/m8m24Vk26Y87g73Ba6ueUd0MWed/NXMhSZIm62U/llVbS0PJe1SHunbyXjJ/BqG1z9bFjGUIvTg== @@ -6855,9 +6855,9 @@ nock@^13.2.2: propagate "^2.0.0" node-fetch@^2.6.1: - version "2.6.6" - resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz#1751a7c01834e8e1697758732e9efb6eeadfaf89" - integrity sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA== + version "2.6.7" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== dependencies: whatwg-url "^5.0.0" @@ -7967,9 +7967,9 @@ redent@^3.0.0: strip-indent "^3.0.0" regexp.prototype.flags@^1.3.0: - version "1.3.1" - resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz#7ef352ae8d159e758c0eadca6f8fcb4eef07be26" - integrity sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA== + version "1.4.1" + resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz#b3f4c0059af9e47eca9f3f660e51d81307e72307" + integrity sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" @@ -8876,10 +8876,10 @@ trim-newlines@^3.0.0: resolved "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== -ts-jest@^27.1.2: - version "27.1.2" - resolved "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.2.tgz#5991d6eb3fd8e1a8d4b8f6de3ec0a3cc567f3151" - integrity sha512-eSOiJOWq6Hhs6Khzk5wKC5sgWIXgXqOCiIl1+3lfnearu58Hj4QpE5tUhQcA3xtZrELbcvAGCsd6HB8OsaVaTA== +ts-jest@^27.1.3: + version "27.1.3" + resolved "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.3.tgz#1f723e7e74027c4da92c0ffbd73287e8af2b2957" + integrity sha512-6Nlura7s6uM9BVUAoqLH7JHyMXjz8gluryjpPXxr3IxZdAXnU6FhjvVLHFtfd1vsE1p8zD1OJfskkc0jhTSnkA== dependencies: bs-logger "0.x" fast-json-stable-stringify "2.x" @@ -9388,9 +9388,9 @@ wordwrap@>=0.0.2, wordwrap@^1.0.0: integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= workerpool@^6.1.5: - version "6.1.5" - resolved "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz#0f7cf076b6215fd7e1da903ff6f22ddd1886b581" - integrity sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw== + version "6.2.0" + resolved "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz#827d93c9ba23ee2019c3ffaff5c27fccea289e8b" + integrity sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A== wrap-ansi@^6.2.0: version "6.2.0" From 2628cb5e6430b87f1e0d5ab6144fbd3dc16c65ee Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Wed, 19 Jan 2022 14:56:09 -0800 Subject: [PATCH 40/50] chore(docdb): migrate tests to use the Assertions module (#18518) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-docdb/package.json | 2 +- .../@aws-cdk/aws-docdb/test/cluster.test.ts | 132 +++++++++--------- .../@aws-cdk/aws-docdb/test/instance.test.ts | 53 ++++--- .../aws-docdb/test/parameter-group.test.ts | 13 +- 4 files changed, 101 insertions(+), 99 deletions(-) diff --git a/packages/@aws-cdk/aws-docdb/package.json b/packages/@aws-cdk/aws-docdb/package.json index d70ae96f75854..4242c8e2a7622 100644 --- a/packages/@aws-cdk/aws-docdb/package.json +++ b/packages/@aws-cdk/aws-docdb/package.json @@ -81,7 +81,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-docdb/test/cluster.test.ts b/packages/@aws-cdk/aws-docdb/test/cluster.test.ts index b812d2b3e4742..4a6c44156f70f 100644 --- a/packages/@aws-cdk/aws-docdb/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-docdb/test/cluster.test.ts @@ -1,13 +1,11 @@ -import { expect as expectCDK, haveResource, ResourcePart, arrayWith, haveResourceLike, objectLike } from '@aws-cdk/assert-internal'; +import { Match, Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as kms from '@aws-cdk/aws-kms'; import * as logs from '@aws-cdk/aws-logs'; import * as cdk from '@aws-cdk/core'; - import { ClusterParameterGroup, DatabaseCluster, DatabaseSecret } from '../lib'; describe('DatabaseCluster', () => { - test('check that instantiation works', () => { // GIVEN const stack = testStack(); @@ -24,7 +22,7 @@ describe('DatabaseCluster', () => { }); // THEN - expectCDK(stack).to(haveResource('AWS::DocDB::DBCluster', { + Template.fromStack(stack).hasResource('AWS::DocDB::DBCluster', { Properties: { DBSubnetGroupName: { Ref: 'DatabaseSubnets56F17B9A' }, MasterUsername: 'admin', @@ -34,20 +32,20 @@ describe('DatabaseCluster', () => { }, DeletionPolicy: 'Retain', UpdateReplacePolicy: 'Retain', - }, ResourcePart.CompleteDefinition)); + }); - expectCDK(stack).to(haveResource('AWS::DocDB::DBInstance', { + Template.fromStack(stack).hasResource('AWS::DocDB::DBInstance', { DeletionPolicy: 'Retain', UpdateReplacePolicy: 'Retain', - }, ResourcePart.CompleteDefinition)); + }); - expectCDK(stack).to(haveResource('AWS::DocDB::DBSubnetGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::DocDB::DBSubnetGroup', { SubnetIds: [ { Ref: 'VPCPrivateSubnet1Subnet8BCA10E0' }, { Ref: 'VPCPrivateSubnet2SubnetCFCDAA7A' }, { Ref: 'VPCPrivateSubnet3Subnet3EDCD457' }, ], - })); + }); }); test('can create a cluster with a single instance', () => { @@ -67,12 +65,12 @@ describe('DatabaseCluster', () => { }); // THEN - expectCDK(stack).to(haveResource('AWS::DocDB::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::DocDB::DBCluster', { DBSubnetGroupName: { Ref: 'DatabaseSubnets56F17B9A' }, MasterUsername: 'admin', MasterUserPassword: 'tooshort', VpcSecurityGroupIds: [{ 'Fn::GetAtt': ['DatabaseSecurityGroup5C91FDCB', 'GroupId'] }], - })); + }); }); test('errors when less than one instance is specified', () => { @@ -132,11 +130,11 @@ describe('DatabaseCluster', () => { }); // THEN - expectCDK(stack).to(haveResource('AWS::SecretsManager::SecretTargetAttachment', { + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::SecretTargetAttachment', { SecretId: { Ref: 'DatabaseSecret3B817195' }, TargetId: { Ref: 'DatabaseB269D8BB' }, TargetType: 'AWS::DocDB::DBCluster', - })); + }); }); test('can create a cluster with imported vpc and security group', () => { @@ -160,12 +158,12 @@ describe('DatabaseCluster', () => { }); // THEN - expectCDK(stack).to(haveResource('AWS::DocDB::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::DocDB::DBCluster', { DBSubnetGroupName: { Ref: 'DatabaseSubnets56F17B9A' }, MasterUsername: 'admin', MasterUserPassword: 'tooshort', VpcSecurityGroupIds: ['SecurityGroupId12345'], - })); + }); }); test('can configure cluster deletion protection', () => { @@ -184,9 +182,9 @@ describe('DatabaseCluster', () => { }); // THEN - expectCDK(stack).to(haveResource('AWS::DocDB::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::DocDB::DBCluster', { DeletionProtection: true, - })); + }); }); test('cluster with parameter group', () => { @@ -213,9 +211,9 @@ describe('DatabaseCluster', () => { }); // THEN - expectCDK(stack).to(haveResource('AWS::DocDB::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::DocDB::DBCluster', { DBClusterParameterGroupName: { Ref: 'ParamsA8366201' }, - })); + }); }); test('cluster with imported parameter group', () => { @@ -237,9 +235,9 @@ describe('DatabaseCluster', () => { }); // THEN - expectCDK(stack).to(haveResource('AWS::DocDB::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::DocDB::DBCluster', { DBClusterParameterGroupName: 'ParamGroupName', - })); + }); }); test('creates a secret when master credentials are not specified', () => { @@ -257,7 +255,7 @@ describe('DatabaseCluster', () => { }); // THEN - expectCDK(stack).to(haveResource('AWS::DocDB::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::DocDB::DBCluster', { MasterUsername: { 'Fn::Join': [ '', @@ -282,16 +280,16 @@ describe('DatabaseCluster', () => { ], ], }, - })); + }); - expectCDK(stack).to(haveResource('AWS::SecretsManager::Secret', { + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::Secret', { GenerateSecretString: { ExcludeCharacters: '\"@/', GenerateStringKey: 'password', PasswordLength: 41, SecretStringTemplate: '{"username":"admin"}', }, - })); + }); }); test('creates a secret with excludeCharacters', () => { @@ -310,11 +308,11 @@ describe('DatabaseCluster', () => { }); // THEN - expectCDK(stack).to(haveResourceLike('AWS::SecretsManager::Secret', { - GenerateSecretString: objectLike({ + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::Secret', { + GenerateSecretString: Match.objectLike({ ExcludeCharacters: '\"@/()[]', }), - })); + }); }); test('creates a secret with secretName set', () => { @@ -333,9 +331,9 @@ describe('DatabaseCluster', () => { }); // THEN - expectCDK(stack).to(haveResourceLike('AWS::SecretsManager::Secret', { + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::Secret', { Name: '/myapp/mydocdb/masteruser', - })); + }); }); test('create an encrypted cluster with custom KMS key', () => { @@ -354,7 +352,7 @@ describe('DatabaseCluster', () => { }); // THEN - expectCDK(stack).to(haveResource('AWS::DocDB::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::DocDB::DBCluster', { KmsKeyId: { 'Fn::GetAtt': [ 'Key961B73FD', @@ -362,7 +360,7 @@ describe('DatabaseCluster', () => { ], }, StorageEncrypted: true, - })); + }); }); test('creating a cluster defaults to using encryption', () => { @@ -380,9 +378,9 @@ describe('DatabaseCluster', () => { }); // THEN - expectCDK(stack).to(haveResource('AWS::DocDB::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::DocDB::DBCluster', { StorageEncrypted: true, - })); + }); }); test('supplying a KMS key with storageEncryption false throws an error', () => { @@ -442,9 +440,9 @@ describe('DatabaseCluster', () => { }); // THEN - expectCDK(stack).to(haveResource('AWS::DocDB::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::DocDB::DBInstance', { DBInstanceIdentifier: `${instanceIdentifierBase}1`, - })); + }); }); test('cluster identifier used', () => { @@ -464,9 +462,9 @@ describe('DatabaseCluster', () => { }); // THEN - expectCDK(stack).to(haveResource('AWS::DocDB::DBInstance', { + Template.fromStack(stack).hasResourceProperties('AWS::DocDB::DBInstance', { DBInstanceIdentifier: `${clusterIdentifier}instance1`, - })); + }); }); test('imported cluster has supplied attributes', () => { @@ -515,9 +513,9 @@ describe('DatabaseCluster', () => { cluster.connections.allowToAnyIpv4(ec2.Port.tcp(443)); // THEN - expectCDK(stack).to(haveResource('AWS::EC2::SecurityGroupEgress', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroupEgress', { GroupId: 'sg-123456789', - })); + }); }); test('backup retention period respected', () => { @@ -538,9 +536,9 @@ describe('DatabaseCluster', () => { }); // THEN - expectCDK(stack).to(haveResource('AWS::DocDB::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::DocDB::DBCluster', { BackupRetentionPeriod: 20, - })); + }); }); test('backup maintenance window respected', () => { @@ -562,10 +560,10 @@ describe('DatabaseCluster', () => { }); // THEN - expectCDK(stack).to(haveResource('AWS::DocDB::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::DocDB::DBCluster', { BackupRetentionPeriod: 20, PreferredBackupWindow: '07:34-08:04', - })); + }); }); test('regular maintenance window respected', () => { @@ -584,9 +582,9 @@ describe('DatabaseCluster', () => { }); // THEN - expectCDK(stack).to(haveResource('AWS::DocDB::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::DocDB::DBCluster', { PreferredMaintenanceWindow: '07:34-08:04', - })); + }); }); test('can configure CloudWatchLogs for audit', () => { @@ -605,9 +603,9 @@ describe('DatabaseCluster', () => { }); // THEN - expectCDK(stack).to(haveResource('AWS::DocDB::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::DocDB::DBCluster', { EnableCloudwatchLogsExports: ['audit'], - })); + }); }); test('can configure CloudWatchLogs for profiler', () => { @@ -626,9 +624,9 @@ describe('DatabaseCluster', () => { }); // THEN - expectCDK(stack).to(haveResource('AWS::DocDB::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::DocDB::DBCluster', { EnableCloudwatchLogsExports: ['profiler'], - })); + }); }); test('can configure CloudWatchLogs for all logs', () => { @@ -648,9 +646,9 @@ describe('DatabaseCluster', () => { }); // THEN - expectCDK(stack).to(haveResource('AWS::DocDB::DBCluster', { + Template.fromStack(stack).hasResourceProperties('AWS::DocDB::DBCluster', { EnableCloudwatchLogsExports: ['audit', 'profiler'], - })); + }); }); test('can set CloudWatch log retention', () => { @@ -671,7 +669,7 @@ describe('DatabaseCluster', () => { }); // THEN - expectCDK(stack).to(haveResource('Custom::LogRetention', { + Template.fromStack(stack).hasResourceProperties('Custom::LogRetention', { ServiceToken: { 'Fn::GetAtt': [ 'LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aFD4BFC8A', @@ -680,8 +678,8 @@ describe('DatabaseCluster', () => { }, LogGroupName: { 'Fn::Join': ['', ['/aws/docdb/', { Ref: 'DatabaseB269D8BB' }, '/audit']] }, RetentionInDays: 90, - })); - expectCDK(stack).to(haveResource('Custom::LogRetention', { + }); + Template.fromStack(stack).hasResourceProperties('Custom::LogRetention', { ServiceToken: { 'Fn::GetAtt': [ 'LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aFD4BFC8A', @@ -690,7 +688,7 @@ describe('DatabaseCluster', () => { }, LogGroupName: { 'Fn::Join': ['', ['/aws/docdb/', { Ref: 'DatabaseB269D8BB' }, '/profiler']] }, RetentionInDays: 90, - })); + }); }); test('single user rotation', () => { @@ -709,7 +707,7 @@ describe('DatabaseCluster', () => { cluster.addRotationSingleUser(cdk.Duration.days(5)); // THEN - expectCDK(stack).to(haveResource('AWS::Serverless::Application', { + Template.fromStack(stack).hasResourceProperties('AWS::Serverless::Application', { Location: { ApplicationId: { 'Fn::FindInMap': ['DatabaseRotationSingleUserSARMapping9AEB3E55', { Ref: 'AWS::Partition' }, 'applicationId'] }, SemanticVersion: { 'Fn::FindInMap': ['DatabaseRotationSingleUserSARMapping9AEB3E55', { Ref: 'AWS::Partition' }, 'semanticVersion'] }, @@ -742,8 +740,8 @@ describe('DatabaseCluster', () => { 'Fn::GetAtt': ['DatabaseRotationSingleUserSecurityGroupAC6E0E73', 'GroupId'], }, }, - })); - expectCDK(stack).to(haveResource('AWS::SecretsManager::RotationSchedule', { + }); + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::RotationSchedule', { SecretId: { Ref: 'DatabaseSecretAttachmentE5D1B020' }, RotationLambdaARN: { 'Fn::GetAtt': ['DatabaseRotationSingleUser65F55654', 'Outputs.RotationLambdaARN'], @@ -751,7 +749,7 @@ describe('DatabaseCluster', () => { RotationRules: { AutomaticallyAfterDays: 5, }, - })); + }); }); test('single user rotation requires secret', () => { @@ -822,7 +820,7 @@ describe('DatabaseCluster', () => { }); // THEN - expectCDK(stack).to(haveResource('AWS::Serverless::Application', { + Template.fromStack(stack).hasResourceProperties('AWS::Serverless::Application', { Location: { ApplicationId: { 'Fn::FindInMap': ['DatabaseRotationSARMappingE46CFA92', { Ref: 'AWS::Partition' }, 'applicationId'] }, SemanticVersion: { 'Fn::FindInMap': ['DatabaseRotationSARMappingE46CFA92', { Ref: 'AWS::Partition' }, 'semanticVersion'] }, @@ -856,8 +854,8 @@ describe('DatabaseCluster', () => { }, masterSecretArn: { Ref: 'DatabaseSecretAttachmentE5D1B020' }, }, - })); - expectCDK(stack).to(haveResource('AWS::SecretsManager::RotationSchedule', { + }); + Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::RotationSchedule', { SecretId: { Ref: 'UserSecret0463E4F5' }, RotationLambdaARN: { 'Fn::GetAtt': ['DatabaseRotation6B6E1D86', 'Outputs.RotationLambdaARN'], @@ -865,7 +863,7 @@ describe('DatabaseCluster', () => { RotationRules: { AutomaticallyAfterDays: 5, }, - })); + }); }); test('multi user rotation requires secret', () => { @@ -916,9 +914,9 @@ describe('DatabaseCluster', () => { cluster.addSecurityGroups(securityGroup); // THEN - expectCDK(stack).to(haveResource('AWS::DocDB::DBCluster', { - VpcSecurityGroupIds: arrayWith(stack.resolve(securityGroup.securityGroupId)), - })); + Template.fromStack(stack).hasResourceProperties('AWS::DocDB::DBCluster', { + VpcSecurityGroupIds: Match.arrayWith([stack.resolve(securityGroup.securityGroupId)]), + }); }); }); diff --git a/packages/@aws-cdk/aws-docdb/test/instance.test.ts b/packages/@aws-cdk/aws-docdb/test/instance.test.ts index f1382746db07d..d7e90af3e3e90 100644 --- a/packages/@aws-cdk/aws-docdb/test/instance.test.ts +++ b/packages/@aws-cdk/aws-docdb/test/instance.test.ts @@ -1,8 +1,7 @@ -import { expect as expectCDK, haveOutput, haveResource, ResourcePart } from '@aws-cdk/assert-internal'; +import { Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as cdk from '@aws-cdk/core'; import * as constructs from 'constructs'; - import { DatabaseCluster, DatabaseInstance } from '../lib'; const CLUSTER_INSTANCE_TYPE = ec2.InstanceType.of(ec2.InstanceClass.R5, ec2.InstanceSize.LARGE); @@ -21,7 +20,7 @@ describe('DatabaseInstance', () => { }); // THEN - expectCDK(stack).to(haveResource('AWS::DocDB::DBInstance', { + Template.fromStack(stack).hasResource('AWS::DocDB::DBInstance', { Properties: { DBClusterIdentifier: { Ref: 'DatabaseB269D8BB' }, DBInstanceClass: EXPECTED_SYNTH_INSTANCE_TYPE, @@ -29,7 +28,7 @@ describe('DatabaseInstance', () => { }, DeletionPolicy: 'Retain', UpdateReplacePolicy: 'Retain', - }, ResourcePart.CompleteDefinition)); + }); }); test.each([ @@ -48,7 +47,7 @@ describe('DatabaseInstance', () => { }); // THEN - expectCDK(stack).to(haveResource('AWS::DocDB::DBInstance', { + Template.fromStack(stack).hasResource('AWS::DocDB::DBInstance', { Properties: { DBClusterIdentifier: { Ref: 'DatabaseB269D8BB' }, DBInstanceClass: EXPECTED_SYNTH_INSTANCE_TYPE, @@ -56,7 +55,7 @@ describe('DatabaseInstance', () => { }, DeletionPolicy: 'Retain', UpdateReplacePolicy: 'Retain', - }, ResourcePart.CompleteDefinition)); + }); }); test('check that the endpoint works', () => { @@ -75,9 +74,11 @@ describe('DatabaseInstance', () => { }); // THEN - expectCDK(stack).to(haveOutput({ - exportName, - outputValue: { + Template.fromStack(stack).hasOutput(exportName, { + Export: { + Name: exportName, + }, + Value: { 'Fn::Join': [ '', [ @@ -87,7 +88,7 @@ describe('DatabaseInstance', () => { ], ], }, - })); + }); }); test('check that instanceArn property works', () => { @@ -106,9 +107,11 @@ describe('DatabaseInstance', () => { }); // THEN - expectCDK(stack).to(haveOutput({ - exportName, - outputValue: { + Template.fromStack(stack).hasOutput(exportName, { + Export: { + Name: exportName, + }, + Value: { 'Fn::Join': [ '', [ @@ -119,7 +122,7 @@ describe('DatabaseInstance', () => { ], ], }, - })); + }); }); test('check importing works as expected', () => { @@ -147,9 +150,11 @@ describe('DatabaseInstance', () => { }); // THEN - expectCDK(stack).to(haveOutput({ - exportName: arnExportName, - outputValue: { + Template.fromStack(stack).hasOutput('ArnOutput', { + Export: { + Name: arnExportName, + }, + Value: { 'Fn::Join': [ '', [ @@ -159,11 +164,13 @@ describe('DatabaseInstance', () => { ], ], }, - })); - expectCDK(stack).to(haveOutput({ - exportName: endpointExportName, - outputValue: `${instanceEndpointAddress}:${port}`, - })); + }); + Template.fromStack(stack).hasOutput('EndpointOutput', { + Export: { + Name: endpointExportName, + }, + Value: `${instanceEndpointAddress}:${port}`, + }); }); }); @@ -191,4 +198,4 @@ class TestStack extends cdk.Stack { function testStack() { const stack = new TestStack(undefined, undefined, { env: { account: '12345', region: 'us-test-1' } }); return stack; -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-docdb/test/parameter-group.test.ts b/packages/@aws-cdk/aws-docdb/test/parameter-group.test.ts index 66cf65e5ece60..03aaff0fb2e77 100644 --- a/packages/@aws-cdk/aws-docdb/test/parameter-group.test.ts +++ b/packages/@aws-cdk/aws-docdb/test/parameter-group.test.ts @@ -1,9 +1,8 @@ -import { expect, haveResource } from '@aws-cdk/assert-internal'; +import { Template } from '@aws-cdk/assertions'; import { Stack } from '@aws-cdk/core'; import { ClusterParameterGroup } from '../lib'; describe('ClusterParameterGroup', () => { - test('check that instantiation works', () => { // GIVEN const stack = new Stack(); @@ -18,14 +17,13 @@ describe('ClusterParameterGroup', () => { }); // THEN - expect(stack).to(haveResource('AWS::DocDB::DBClusterParameterGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::DocDB::DBClusterParameterGroup', { Description: 'desc', Family: 'hello', Parameters: { key: 'value', }, - })); - + }); }); test('check automatically generated descriptions', () => { @@ -41,13 +39,12 @@ describe('ClusterParameterGroup', () => { }); // THEN - expect(stack).to(haveResource('AWS::DocDB::DBClusterParameterGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::DocDB::DBClusterParameterGroup', { Description: 'Cluster parameter group for hello', Family: 'hello', Parameters: { key: 'value', }, - })); - + }); }); }); From cc138bfd9933534f36015cfa19f15f1d74f4062c Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Thu, 20 Jan 2022 00:42:14 +0100 Subject: [PATCH 41/50] chore(config): migrate tests to `assertions` (#18533) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-config/package.json | 2 +- .../aws-config/test/managed-rules.test.ts | 14 ++++++------- .../@aws-cdk/aws-config/test/rule.test.ts | 21 +++++++++---------- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/packages/@aws-cdk/aws-config/package.json b/packages/@aws-cdk/aws-config/package.json index 3695f158d2eb1..e0cd4997a725a 100644 --- a/packages/@aws-cdk/aws-config/package.json +++ b/packages/@aws-cdk/aws-config/package.json @@ -79,7 +79,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/aws-events-targets": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", diff --git a/packages/@aws-cdk/aws-config/test/managed-rules.test.ts b/packages/@aws-cdk/aws-config/test/managed-rules.test.ts index 98dd3fbd34262..ef7e98ff25709 100644 --- a/packages/@aws-cdk/aws-config/test/managed-rules.test.ts +++ b/packages/@aws-cdk/aws-config/test/managed-rules.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as sns from '@aws-cdk/aws-sns'; import * as cdk from '@aws-cdk/core'; import * as config from '../lib'; @@ -12,7 +12,7 @@ describe('access keys', () => { new config.AccessKeysRotated(stack, 'AccessKeys'); // THEN - expect(stack).toHaveResource('AWS::Config::ConfigRule', { + Template.fromStack(stack).hasResourceProperties('AWS::Config::ConfigRule', { Source: { Owner: 'AWS', SourceIdentifier: 'ACCESS_KEYS_ROTATED', @@ -30,7 +30,7 @@ describe('access keys', () => { }); // THEN - expect(stack).toHaveResource('AWS::Config::ConfigRule', { + Template.fromStack(stack).hasResourceProperties('AWS::Config::ConfigRule', { Source: { Owner: 'AWS', SourceIdentifier: 'ACCESS_KEYS_ROTATED', @@ -51,7 +51,7 @@ describe('cloudformation stack', () => { new config.CloudFormationStackDriftDetectionCheck(stack, 'Drift'); // THEN - expect(stack).toHaveResource('AWS::Config::ConfigRule', { + Template.fromStack(stack).hasResourceProperties('AWS::Config::ConfigRule', { Source: { Owner: 'AWS', SourceIdentifier: 'CLOUDFORMATION_STACK_DRIFT_DETECTION_CHECK', @@ -71,7 +71,7 @@ describe('cloudformation stack', () => { }, }); - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { AssumeRolePolicyDocument: { Statement: [ { @@ -113,7 +113,7 @@ describe('cloudformation stack', () => { }); // THEN - expect(stack).toHaveResource('AWS::Config::ConfigRule', { + Template.fromStack(stack).hasResourceProperties('AWS::Config::ConfigRule', { Source: { Owner: 'AWS', SourceIdentifier: 'CLOUDFORMATION_STACK_NOTIFICATION_CHECK', @@ -157,7 +157,7 @@ describe('ec2 instance', () => { }); // THEN - expect(stack).toHaveResource('AWS::Config::ConfigRule', { + Template.fromStack(stack).hasResourceProperties('AWS::Config::ConfigRule', { Source: { Owner: 'AWS', SourceIdentifier: config.ManagedRuleIdentifiers.EC2_INSTANCE_PROFILE_ATTACHED, diff --git a/packages/@aws-cdk/aws-config/test/rule.test.ts b/packages/@aws-cdk/aws-config/test/rule.test.ts index 259727982a330..a6e125b4d89ec 100644 --- a/packages/@aws-cdk/aws-config/test/rule.test.ts +++ b/packages/@aws-cdk/aws-config/test/rule.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { ResourcePart } from '@aws-cdk/assert-internal'; +import { Template } from '@aws-cdk/assertions'; import * as targets from '@aws-cdk/aws-events-targets'; import * as lambda from '@aws-cdk/aws-lambda'; import * as cdk from '@aws-cdk/core'; @@ -22,7 +21,7 @@ describe('rule', () => { }); // THEN - expect(stack).toHaveResource('AWS::Config::ConfigRule', { + Template.fromStack(stack).hasResourceProperties('AWS::Config::ConfigRule', { Source: { Owner: 'AWS', SourceIdentifier: 'AWS_SUPER_COOL', @@ -59,7 +58,7 @@ describe('rule', () => { }); // THEN - expect(stack).toHaveResource('AWS::Config::ConfigRule', { + Template.fromStack(stack).hasResource('AWS::Config::ConfigRule', { Properties: { Source: { Owner: 'CUSTOM_LAMBDA', @@ -97,16 +96,16 @@ describe('rule', () => { 'Function76856677', 'FunctionServiceRole675BB04A', ], - }, ResourcePart.CompleteDefinition); + }); - expect(stack).toHaveResource('AWS::Lambda::Permission', { + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Permission', { Principal: 'config.amazonaws.com', SourceAccount: { Ref: 'AWS::AccountId', }, }); - expect(stack).toHaveResource('AWS::IAM::Role', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { ManagedPolicyArns: [ { 'Fn::Join': [ @@ -147,7 +146,7 @@ describe('rule', () => { }); // THEN - expect(stack).toHaveResource('AWS::Config::ConfigRule', { + Template.fromStack(stack).hasResourceProperties('AWS::Config::ConfigRule', { Scope: { ComplianceResourceId: 'i-1234', ComplianceResourceTypes: [ @@ -168,7 +167,7 @@ describe('rule', () => { }); // THEN - expect(stack).toHaveResource('AWS::Config::ConfigRule', { + Template.fromStack(stack).hasResourceProperties('AWS::Config::ConfigRule', { Scope: { ComplianceResourceTypes: [ 'AWS::S3::Bucket', @@ -189,7 +188,7 @@ describe('rule', () => { }); // THEN - expect(stack).toHaveResource('AWS::Config::ConfigRule', { + Template.fromStack(stack).hasResourceProperties('AWS::Config::ConfigRule', { Scope: { TagKey: 'key', TagValue: 'value', @@ -247,7 +246,7 @@ describe('rule', () => { target: new targets.LambdaFunction(fn), }); - expect(stack).toHaveResource('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { EventPattern: { 'source': [ 'aws.config', From 52c02d2f9b233f0e1f26d764fd080ef862b26810 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Thu, 20 Jan 2022 01:29:02 +0100 Subject: [PATCH 42/50] chore(globalaccelerator): migrate tests to `assertions` (#18532) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../package.json | 2 +- .../test/endpoints.test.ts | 14 ++++----- .../aws-globalaccelerator/package.json | 2 +- .../globalaccelerator-security-group.test.ts | 10 +++---- .../test/globalaccelerator.test.ts | 30 +++++++++---------- 5 files changed, 29 insertions(+), 29 deletions(-) diff --git a/packages/@aws-cdk/aws-globalaccelerator-endpoints/package.json b/packages/@aws-cdk/aws-globalaccelerator-endpoints/package.json index 703a1b6679e9d..4ead0ec949749 100644 --- a/packages/@aws-cdk/aws-globalaccelerator-endpoints/package.json +++ b/packages/@aws-cdk/aws-globalaccelerator-endpoints/package.json @@ -69,7 +69,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/pkglint": "0.0.0", diff --git a/packages/@aws-cdk/aws-globalaccelerator-endpoints/test/endpoints.test.ts b/packages/@aws-cdk/aws-globalaccelerator-endpoints/test/endpoints.test.ts index 01b55508f11a0..c241c8ccd6646 100644 --- a/packages/@aws-cdk/aws-globalaccelerator-endpoints/test/endpoints.test.ts +++ b/packages/@aws-cdk/aws-globalaccelerator-endpoints/test/endpoints.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as elbv2 from '@aws-cdk/aws-elasticloadbalancingv2'; import * as ga from '@aws-cdk/aws-globalaccelerator'; @@ -32,7 +32,7 @@ test('Application Load Balancer with all properties', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::GlobalAccelerator::EndpointGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::GlobalAccelerator::EndpointGroup', { EndpointConfigurations: [ { EndpointId: { Ref: 'ALBAEE750D2' }, @@ -57,7 +57,7 @@ test('Get region from imported ALB', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::GlobalAccelerator::EndpointGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::GlobalAccelerator::EndpointGroup', { EndpointGroupRegion: 'us-west-2', EndpointConfigurations: [ { @@ -79,7 +79,7 @@ test('Network Load Balancer with all properties', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::GlobalAccelerator::EndpointGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::GlobalAccelerator::EndpointGroup', { EndpointConfigurations: [ { EndpointId: { Ref: 'NLB55158F82' }, @@ -102,7 +102,7 @@ test('Get region from imported NLB', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::GlobalAccelerator::EndpointGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::GlobalAccelerator::EndpointGroup', { EndpointGroupRegion: 'us-west-2', EndpointConfigurations: [ { @@ -124,7 +124,7 @@ test('CFN EIP with all properties', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::GlobalAccelerator::EndpointGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::GlobalAccelerator::EndpointGroup', { EndpointConfigurations: [ { EndpointId: { 'Fn::GetAtt': ['ElasticIpAddress', 'AllocationId'] }, @@ -151,7 +151,7 @@ test('EC2 Instance with all properties', () => { }); // THEN - expect(stack).toHaveResourceLike('AWS::GlobalAccelerator::EndpointGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::GlobalAccelerator::EndpointGroup', { EndpointConfigurations: [ { EndpointId: { Ref: 'InstanceC1063A87' }, diff --git a/packages/@aws-cdk/aws-globalaccelerator/package.json b/packages/@aws-cdk/aws-globalaccelerator/package.json index c89513ed8fb57..14f3c7036f24b 100644 --- a/packages/@aws-cdk/aws-globalaccelerator/package.json +++ b/packages/@aws-cdk/aws-globalaccelerator/package.json @@ -81,7 +81,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/aws-elasticloadbalancingv2": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", diff --git a/packages/@aws-cdk/aws-globalaccelerator/test/globalaccelerator-security-group.test.ts b/packages/@aws-cdk/aws-globalaccelerator/test/globalaccelerator-security-group.test.ts index 7191988ed2ed6..dfa4ea9a918bc 100644 --- a/packages/@aws-cdk/aws-globalaccelerator/test/globalaccelerator-security-group.test.ts +++ b/packages/@aws-cdk/aws-globalaccelerator/test/globalaccelerator-security-group.test.ts @@ -1,4 +1,4 @@ -import { expect, haveResource, ResourcePart } from '@aws-cdk/assert-internal'; +import { Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as ga from '../lib'; import { testFixture } from './util'; @@ -22,7 +22,7 @@ test('custom resource exists', () => { endpointGroup.connectionsPeer('GlobalAcceleratorSG', vpc); // THEN - expect(stack).to(haveResource('Custom::AWS', { + Template.fromStack(stack).hasResource('Custom::AWS', { Properties: { ServiceToken: { 'Fn::GetAtt': [ @@ -48,7 +48,7 @@ test('custom resource exists', () => { 'GroupGlobalAcceleratorSGCustomResourceCustomResourcePolicy9C957AD2', 'GroupC77FDACD', ], - }, ResourcePart.CompleteDefinition)); + }); }); test('can create security group rule', () => { @@ -73,7 +73,7 @@ test('can create security group rule', () => { instanceConnections.allowFrom(gaSg, ec2.Port.tcp(443)); // THEN - expect(stack).to(haveResource('AWS::EC2::SecurityGroupIngress', { + Template.fromStack(stack).hasResourceProperties('AWS::EC2::SecurityGroupIngress', { IpProtocol: 'tcp', FromPort: 443, GroupId: { @@ -89,5 +89,5 @@ test('can create security group rule', () => { ], }, ToPort: 443, - })); + }); }); diff --git a/packages/@aws-cdk/aws-globalaccelerator/test/globalaccelerator.test.ts b/packages/@aws-cdk/aws-globalaccelerator/test/globalaccelerator.test.ts index bd46c697ac1c3..94296978b50b6 100644 --- a/packages/@aws-cdk/aws-globalaccelerator/test/globalaccelerator.test.ts +++ b/packages/@aws-cdk/aws-globalaccelerator/test/globalaccelerator.test.ts @@ -1,4 +1,4 @@ -import { expect, haveResourceLike } from '@aws-cdk/assert-internal'; +import { Template } from '@aws-cdk/assertions'; import { Duration } from '@aws-cdk/core'; import * as ga from '../lib'; import { testFixture } from './util'; @@ -11,9 +11,9 @@ test('create accelerator', () => { new ga.Accelerator(stack, 'Accelerator'); // THEN - expect(stack).to(haveResourceLike('AWS::GlobalAccelerator::Accelerator', { + Template.fromStack(stack).hasResourceProperties('AWS::GlobalAccelerator::Accelerator', { Enabled: true, - })); + }); }); test('create listener', () => { @@ -33,7 +33,7 @@ test('create listener', () => { }); // THEN - expect(stack).to(haveResourceLike('AWS::GlobalAccelerator::Listener', { + Template.fromStack(stack).hasResourceProperties('AWS::GlobalAccelerator::Listener', { AcceleratorArn: { 'Fn::GetAtt': [ 'Accelerator8EB0B6B1', @@ -48,7 +48,7 @@ test('create listener', () => { ], Protocol: 'TCP', ClientAffinity: 'NONE', - })); + }); }); test('toPort defaults to fromPort if left out', () => { @@ -64,14 +64,14 @@ test('toPort defaults to fromPort if left out', () => { }); // THEN - expect(stack).to(haveResourceLike('AWS::GlobalAccelerator::Listener', { + Template.fromStack(stack).hasResourceProperties('AWS::GlobalAccelerator::Listener', { PortRanges: [ { FromPort: 123, ToPort: 123, }, ], - })); + }); }); test('create endpointgroup', () => { @@ -92,7 +92,7 @@ test('create endpointgroup', () => { new ga.EndpointGroup(stack, 'Group', { listener }); // THEN - expect(stack).to(haveResourceLike('AWS::GlobalAccelerator::EndpointGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::GlobalAccelerator::EndpointGroup', { EndpointGroupRegion: { Ref: 'AWS::Region', }, @@ -102,7 +102,7 @@ test('create endpointgroup', () => { 'ListenerArn', ], }, - })); + }); }); test('endpointgroup region is the first endpoint\'s region', () => { @@ -125,9 +125,9 @@ test('endpointgroup region is the first endpoint\'s region', () => { }); // THEN - expect(stack).to(haveResourceLike('AWS::GlobalAccelerator::EndpointGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::GlobalAccelerator::EndpointGroup', { EndpointGroupRegion: 'us-bla-5', - })); + }); }); test('endpointgroup with all parameters', () => { @@ -156,7 +156,7 @@ test('endpointgroup with all parameters', () => { }); // THEN - expect(stack).to(haveResourceLike('AWS::GlobalAccelerator::EndpointGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::GlobalAccelerator::EndpointGroup', { EndpointGroupRegion: 'us-bla-5', HealthCheckIntervalSeconds: 10, HealthCheckPath: '/ping', @@ -170,7 +170,7 @@ test('endpointgroup with all parameters', () => { ], ThresholdCount: 23, TrafficDialPercentage: 86, - })); + }); }); test('addEndpoint', () => { @@ -201,7 +201,7 @@ test('addEndpoint', () => { }); // THEN - expect(stack).to(haveResourceLike('AWS::GlobalAccelerator::EndpointGroup', { + Template.fromStack(stack).hasResourceProperties('AWS::GlobalAccelerator::EndpointGroup', { EndpointConfigurations: [ { EndpointId: 'i-123', @@ -209,5 +209,5 @@ test('addEndpoint', () => { Weight: 30, }, ], - })); + }); }); \ No newline at end of file From 97105c9a4e4913a7277cf2313b69d5b2a8417b2c Mon Sep 17 00:00:00 2001 From: Madeline Kusters <80541297+madeline-k@users.noreply.github.com> Date: Wed, 19 Jan 2022 17:34:40 -0800 Subject: [PATCH 43/50] chore(cloudwatch-actions): migrate to Assertions (#18539) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-cloudwatch-actions/package.json | 2 +- .../@aws-cdk/aws-cloudwatch-actions/test/appscaling.test.ts | 4 ++-- packages/@aws-cdk/aws-cloudwatch-actions/test/ec2.test.ts | 4 ++-- packages/@aws-cdk/aws-cloudwatch-actions/test/scaling.test.ts | 4 ++-- packages/@aws-cdk/aws-cloudwatch-actions/test/sns.test.ts | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/@aws-cdk/aws-cloudwatch-actions/package.json b/packages/@aws-cdk/aws-cloudwatch-actions/package.json index 97a1498ed89cf..08a59bffab0d5 100644 --- a/packages/@aws-cdk/aws-cloudwatch-actions/package.json +++ b/packages/@aws-cdk/aws-cloudwatch-actions/package.json @@ -64,7 +64,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/aws-ec2": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", diff --git a/packages/@aws-cdk/aws-cloudwatch-actions/test/appscaling.test.ts b/packages/@aws-cdk/aws-cloudwatch-actions/test/appscaling.test.ts index f355290aaa553..066df92d34b45 100644 --- a/packages/@aws-cdk/aws-cloudwatch-actions/test/appscaling.test.ts +++ b/packages/@aws-cdk/aws-cloudwatch-actions/test/appscaling.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import { Stack } from '@aws-cdk/core'; @@ -27,7 +27,7 @@ test('can use topic as alarm action', () => { alarm.addAlarmAction(new actions.ApplicationScalingAction(action)); // THEN - expect(stack).toHaveResource('AWS::CloudWatch::Alarm', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { AlarmActions: [ { Ref: 'Action62AD07C0' }, ], diff --git a/packages/@aws-cdk/aws-cloudwatch-actions/test/ec2.test.ts b/packages/@aws-cdk/aws-cloudwatch-actions/test/ec2.test.ts index 1d6dd47793796..ed4c4ba66bc85 100644 --- a/packages/@aws-cdk/aws-cloudwatch-actions/test/ec2.test.ts +++ b/packages/@aws-cdk/aws-cloudwatch-actions/test/ec2.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import { Stack } from '@aws-cdk/core'; import * as actions from '../lib'; @@ -22,7 +22,7 @@ test('can use instance reboot as alarm action', () => { alarm.addAlarmAction(new actions.Ec2Action(actions.Ec2InstanceAction.REBOOT)); // THEN - expect(stack).toHaveResource('AWS::CloudWatch::Alarm', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { AlarmActions: [ { 'Fn::Join': [ diff --git a/packages/@aws-cdk/aws-cloudwatch-actions/test/scaling.test.ts b/packages/@aws-cdk/aws-cloudwatch-actions/test/scaling.test.ts index 61561cd412e15..d3b16268e03bf 100644 --- a/packages/@aws-cdk/aws-cloudwatch-actions/test/scaling.test.ts +++ b/packages/@aws-cdk/aws-cloudwatch-actions/test/scaling.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as autoscaling from '@aws-cdk/aws-autoscaling'; import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import * as ec2 from '@aws-cdk/aws-ec2'; @@ -29,7 +29,7 @@ test('can use topic as alarm action', () => { alarm.addAlarmAction(new actions.AutoScalingAction(action)); // THEN - expect(stack).toHaveResource('AWS::CloudWatch::Alarm', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { AlarmActions: [ { Ref: 'Action62AD07C0' }, ], diff --git a/packages/@aws-cdk/aws-cloudwatch-actions/test/sns.test.ts b/packages/@aws-cdk/aws-cloudwatch-actions/test/sns.test.ts index 3cdc6e71ae9c2..ba379902b653f 100644 --- a/packages/@aws-cdk/aws-cloudwatch-actions/test/sns.test.ts +++ b/packages/@aws-cdk/aws-cloudwatch-actions/test/sns.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import * as sns from '@aws-cdk/aws-sns'; import { Stack } from '@aws-cdk/core'; @@ -18,7 +18,7 @@ test('can use topic as alarm action', () => { alarm.addAlarmAction(new actions.SnsAction(topic)); // THEN - expect(stack).toHaveResource('AWS::CloudWatch::Alarm', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { AlarmActions: [ { Ref: 'TopicBFC7AF6E' }, ], From 7babda8da1c6b7dd101608d3d9aa125aef5353c3 Mon Sep 17 00:00:00 2001 From: Elad Ben-Israel Date: Thu, 20 Jan 2022 04:23:27 +0200 Subject: [PATCH 44/50] chore: move decdk to cdklabs (#18529) In order to be able to release decdk independently and iterate quickly we have migrated it to https://github.com/cdklabs/decdk (history preserved). ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/decdk/.gitignore | 5 - packages/decdk/.npmignore | 24 - packages/decdk/README.md | 277 +- packages/decdk/bin/decdk | 2 - packages/decdk/bin/decdk-schema | 2 - packages/decdk/bin/decdk-schema.ts | 15 - packages/decdk/bin/decdk.ts | 26 - packages/decdk/cloudformation.schema.json | 143 - packages/decdk/deps.js | 64 - packages/decdk/examples/apigw.json | 28 - packages/decdk/examples/ecs.json | 47 - packages/decdk/examples/lambda-events.json | 37 - packages/decdk/examples/lambda-topic.json | 19 - packages/decdk/examples/pipeline.json | 84 - packages/decdk/examples/pure-cfn.json | 17 - packages/decdk/examples/queue-kms.json | 11 - packages/decdk/examples/vpc.json | 8 - packages/decdk/jest.config.js | 183 - packages/decdk/lib/cdk-schema.ts | 125 - packages/decdk/lib/declarative-stack.ts | 457 --- packages/decdk/lib/index.ts | 3 - packages/decdk/lib/jsii2schema.ts | 593 --- packages/decdk/lib/util.ts | 23 - packages/decdk/package.json | 282 -- .../test/__snapshots__/synth.test.js.snap | 3254 ----------------- packages/decdk/test/fixture/index.ts | 35 - packages/decdk/test/fixture/package.json | 19 - packages/decdk/test/fixture/tsconfig.json | 38 - packages/decdk/test/sanity.test.ts | 22 - packages/decdk/test/schema.test.ts | 91 - packages/decdk/test/synth.test.ts | 51 - packages/decdk/tsconfig.json | 26 - 32 files changed, 1 insertion(+), 6010 deletions(-) delete mode 100644 packages/decdk/.gitignore delete mode 100644 packages/decdk/.npmignore delete mode 100755 packages/decdk/bin/decdk delete mode 100755 packages/decdk/bin/decdk-schema delete mode 100644 packages/decdk/bin/decdk-schema.ts delete mode 100644 packages/decdk/bin/decdk.ts delete mode 100644 packages/decdk/cloudformation.schema.json delete mode 100644 packages/decdk/deps.js delete mode 100644 packages/decdk/examples/apigw.json delete mode 100644 packages/decdk/examples/ecs.json delete mode 100644 packages/decdk/examples/lambda-events.json delete mode 100644 packages/decdk/examples/lambda-topic.json delete mode 100644 packages/decdk/examples/pipeline.json delete mode 100644 packages/decdk/examples/pure-cfn.json delete mode 100644 packages/decdk/examples/queue-kms.json delete mode 100644 packages/decdk/examples/vpc.json delete mode 100644 packages/decdk/jest.config.js delete mode 100644 packages/decdk/lib/cdk-schema.ts delete mode 100644 packages/decdk/lib/declarative-stack.ts delete mode 100644 packages/decdk/lib/index.ts delete mode 100644 packages/decdk/lib/jsii2schema.ts delete mode 100644 packages/decdk/lib/util.ts delete mode 100644 packages/decdk/package.json delete mode 100644 packages/decdk/test/__snapshots__/synth.test.js.snap delete mode 100644 packages/decdk/test/fixture/index.ts delete mode 100644 packages/decdk/test/fixture/package.json delete mode 100644 packages/decdk/test/fixture/tsconfig.json delete mode 100644 packages/decdk/test/sanity.test.ts delete mode 100644 packages/decdk/test/schema.test.ts delete mode 100644 packages/decdk/test/synth.test.ts delete mode 100644 packages/decdk/tsconfig.json diff --git a/packages/decdk/.gitignore b/packages/decdk/.gitignore deleted file mode 100644 index 88e6bb7a9196f..0000000000000 --- a/packages/decdk/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -*.js -*.d.ts -!deps.js -test/fixture/.jsii -cdk.schema.json diff --git a/packages/decdk/.npmignore b/packages/decdk/.npmignore deleted file mode 100644 index 9f3f17837f80a..0000000000000 --- a/packages/decdk/.npmignore +++ /dev/null @@ -1,24 +0,0 @@ -# The basics -*.ts -*.tgz -*.snk -!*.d.ts -!*.js - -# Coverage -coverage -.nyc_output -.nycrc - -# Build gear -dist -.LAST_BUILD -.LAST_PACKAGE -.jsii - - -# Include .jsii -!.jsii - -*.tsbuildinfo - diff --git a/packages/decdk/README.md b/packages/decdk/README.md index 8d21c445a0c79..3a8d3178800f3 100644 --- a/packages/decdk/README.md +++ b/packages/decdk/README.md @@ -1,277 +1,2 @@ -# deCDK - Declarative CDK +Moved to [cdklabs/decdk](https://github.com/cdklabs/decdk) -[![experimental](http://badges.github.io/stability-badges/dist/experimental.svg)](http://github.com/badges/stability-badges) - -Define AWS CDK applications declaratively. - -This tool reads CloudFormation-like JSON/YAML templates which can contain both normal CloudFormation resources (`AWS::S3::Bucket`) and also reference AWS CDK resources (`@aws-cdk/aws-s3.Bucket`). - -## Getting Started - -Install the AWS CDK CLI and the `decdk` tool: - -```console -$ npm i -g aws-cdk decdk -``` - -This is optional (but highly recommended): You can use `decdk-schema` to generate a JSON schema and use it for IDE completion and validation: - -```console -$ decdk-schema > cdk.schema.json -``` - -Okay, we are ready to begin with a simple example. Create a file called `hello.json`: - -```json -{ - "$schema": "./cdk.schema.json", - "Resources": { - "MyQueue": { - "Type": "@aws-cdk/aws-sqs.Queue", - "Properties": { - "fifo": true - } - } - } -} -``` - -Now, you can use it as a CDK app (you'll need to `npm install -g aws-cdk`): - -```console -$ cdk -a "decdk hello.json" synth -Resources: - MyQueueE6CA6235: - Type: AWS::SQS::Queue - Properties: - FifoQueue: true - Metadata: - aws:cdk:path: hello2/MyQueue/Resource -``` - -As you can see, the deCDK has the same semantics as a CloudFormation template. It contains a section for “Resources”, where each resource is defined by a *type* and a set of *properties*. deCDK allows using constructs from AWS Construct Library in templates by identifying the class name (in this case `@aws-cdk/aws-sqs.Queue`). - -When deCDK processes a template, it identifies these special resources and under-the-hood, it instantiates an object of that type, passing in the properties to the object's constructor. All CDK constructs have a uniform signature, so this is actually straightforward. - -## Development - -### Examples/Tests - -When you build this module, it will produce a `cdk.schema.json` file at the root, which is referenced by the examples in the [`examples`](./examples) directory. This directory includes working examples of deCDK templates for various areas. We also snapshot-test those to ensure there are no unwanted regressions. - -## Design - -"Deconstruction" is the process of reflecting on the AWS Construct Library's type system and determining what would be the declarative interface for each API. This section describes how various elements in the library's type system are represented through the template format. - -### Constructs - -Constructs can be defined in the `Resources` section of the template. The `Type` of the resource is the fully-qualified class name (e.g. `@aws-cdk/aws-s3.Bucket`) and `Properties` are mapped to the deconstructed type of the construct's "Props" interface (e.g. `BucketProps`). - -### Data Interfaces ("Props") - -jsii has a concept of "data interfaces", which are basically interfaces that do not have methods. For example, all construct "props" are data interfaces. - -> In some languages (Python, Ruby), if a method accepts a data interface as the last argument, interface properties can be used as keyword arguments in the method call. Other languages have a different idiomatic representation of data such as Java PoJos and Builders. - -deCDK maps data interfaces to closed JSON objects (no additional properties), and will recursively deconstruct all property types. - -### Primitives - -Strings, numbers, booleans, dates, lists and maps are all deconstructed 1:1 to their JSON representation. - -### Enums - -Enums are mapped to JSON schema enums. - -### References - -If deCDK encounters a reference to another __construct__ (a type that extends `cdk.Construct` or an interface that extends `cdk.IConstruct`), it will allow referencing it via a “Ref” intrinsic. For example, here's a definition of an ECS cluster that references a VPC: - -```yaml -Resources: - VPC: - Type: "@aws-cdk/aws-ec2.Vpc" - Properties: - maxAZs: 1 - Cluster: - Type: "@aws-cdk/aws-ecs.Cluster" - Properties: - vpc: - Ref: VPC -``` - -### Enum-like Classes - -Based on the AWS Construct Library's consistent guidelines and conventions, which are also enforced by a tool we use called “awslint”, deCDK is also capable of expressive more complex idioms. For example, enum-like classes, which are classes that expose a set of static properties or methods can be mapped to JSON enums or method invocations. For example, this is how you define an AWS Lambda function in the CDK (TypeScript): - -```ts -new lambda.Function(this, 'MyHandler', { - handler: 'index.handler', - runtime: lambda.Runtime.NodeJS810, - code: lambda.Code.asset('./src') -}); -``` - -And here's the deCDK version: - -```json -{ - "MyHandler": { - "Type": "@aws-cdk/aws-lambda.Function", - "Properties": { - "handler": "index.handler", - "runtime": "NodeJS810", - "code": { "asset": { "path": "./src" } } - } - } -} -``` - -### Polymorphism - -Due to the decoupled nature of AWS, The AWS Construct Library highly utilizes polymorphism to expose rich APIs to users. In many cases, APIs would accept an interface of some kind, and various AWS services provide an implementation for that interface. deCDK is able to find all concrete implementation of an interface or an abstract class and offer the user an enum-like experience. The following example shows how this approach can be used to define AWS Lambda events: - -```json -{ - "Resources": { - "MyTopic": { - "Type": "@aws-cdk/aws-sns.Topic" - }, - "Table": { - "Type": "@aws-cdk/aws-dynamodb.Table", - "Properties": { - "partitionKey": { - "name": "ID", - "type": "String" - }, - "streamSpecification": "NewAndOldImages" - } - }, - "HelloWorldFunction": { - "Type": "@aws-cdk/aws-lambda.Function", - "Properties": { - "handler": "app.hello_handler", - "runtime": "Python36", - "code": { - "asset": { "path": "." } - }, - "environment": { - "Param": "f" - }, - "events": [ - { "@aws-cdk/aws-lambda-event-sources.DynamoEventSource": { "table": { "Ref": "Table" }, "startingPosition": "TrimHorizon" } }, - { "@aws-cdk/aws-lambda-event-sources.ApiEventSource": { "method": "GET", "path": "/hello" } }, - { "@aws-cdk/aws-lambda-event-sources.ApiEventSource": { "method": "POST", "path": "/hello" } }, - { "@aws-cdk/aws-lambda-event-sources.SnsEventSource": { "topic": { "Ref": "MyTopic" } } } - ] - } - } - } -} -``` - -The keys in the “events” array are all fully qualified names of classes in the AWS Construct Library. The declaration is “Array”. When deCDK deconstructs the objects in this array, it will create objects of these types and pass them in as IEventSource objects. - -### `Fn::GetAtt` - -deCDK also supports referencing specific attributes of CDK resources by the intrinsic `Fn::GetAtt`. When processing the template, if an `Fn::GetAtt` is found, and references a CDK construct, the attribute name is treated as a property name of the construct and its value is used. - -The following example shows how to output the “url” property of a `@aws-cdk/aws-lambda.Function` from above: - -```yaml -Outputs: - HelloWorldApi: - Description: API Gateway endpoint URL for Prod stage for Hello World function - Value: - Fn::GetAtt: - - MyHandler - - url -``` - -### Raw CloudFormation - -If deCDK doesn't identify a resource type as a CDK resource, it will just pass it through to the resulting output. This means that any existing CloudFormation/SAM resources (such as `AWS::SQS::Queue`) can be used as-is. - -The decdk JSON schema will simply pass through any resources that have a type that includes `::`, so don't expect any validation of raw CloudFormation resource properties. - -## Roadmap - -There is much more we can do here. This section lists API surfaces with ideas on how to deconstruct them. - -### Imports - -When decdk encounters a reference to an AWS construct, it currently requires a `Ref` to another resource in the template. We should also support importing external resources by reflecting on the various static `fromXxx`, `importXxx` and deconstructing those methods. - -For example if we have a property `Bucket` that's modeled as an `s3.IBucket`, at the moment it will only accept: - -```json -"Bucket": { "Ref": "MyBucket" } -``` - -But this requires that `MyBucket` is defined within the same template. If we want to reference a bucket by ARN, we should be able to do this: - -```json -"Bucket": { "arn": "arn-of-bucket" } -``` - -Which should be translated to a call: - -```ts -bucket: Bucket.fromBucketArn(this, 'arn-of-bucket') -``` - -### Grants - -AWS constructs expose a set of "grant" methods that can be used to grant IAM principals permissions to perform certain actions on a resource (e.g. `table.grantRead` or `lambda.grantInvoke`). - -deCDK should be able to provide a declarative-style for expressing those grants: - -```json -"MyFunction": { - "Type": "@aws-cdk/aws-lambda.Function", - "Properties": { - "grants": { - "invoke": [ { "Ref": "MyRole" }, { "Ref": "AnotherRole" } ] - } - } -} -``` - -### Events - -The CDK employs a loose pattern for event-driven programming by exposing a set of `onXxx` methods from AWS constructs. This pattern is used for various types of event systems such as CloudWatch events, bucket notifications, etc. - -It might be possible to add a bit more rigor to these patterns and expose them also via a declarative API: - -```json -"MyBucket": { - "Type": "@aws-cdk/aws-s3.Bucket", - "Properties": { - "on": { - "objectCreated": [ - { - "target": { "Ref": "MyFunction" }, - "prefix": "foo/" - } - ] - } - } -} -``` - -### addXxxx - -We should enforce in our APIs that anything that can be "added" to a construct can also be defined in props as an array. `awslint` can enforce this and ensure that `addXxx` methods always return `void` and have a corresponding prop. - -### Supporting user-defined constructs - -deCDK can deconstruct APIs that adhere to the standards defined by __awslint__ and exposed through jsii (it reflects on the jsii type system). Technically, nothing prevents us from allowing users to "bring their own constructs" to decdk, but those requirements must be met. - -### Fully qualified type names - -As you might have observed, whenever users need to reference a type in deCDK templates they are required to reference the fully qualified name (e.g. `@aws-cdk/aws-s3.Bucket`). We can obvsiouly come up with a more concise way to reference these types, as long as it will be possible to deterministically translate back and forth. - -### Special Types - -`iam.PolicyDocument` is tricky since it utilizes a fluent API. We need to think whether we want to revise the PolicyDocument API to be more compatible or add a utility class that can help. -- We should enable shorthand tags for intrinsics in YAML diff --git a/packages/decdk/bin/decdk b/packages/decdk/bin/decdk deleted file mode 100755 index a606284619892..0000000000000 --- a/packages/decdk/bin/decdk +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env node -require('./decdk.js'); diff --git a/packages/decdk/bin/decdk-schema b/packages/decdk/bin/decdk-schema deleted file mode 100755 index 2576c1d9ae5a4..0000000000000 --- a/packages/decdk/bin/decdk-schema +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env node -require('./decdk-schema.js'); diff --git a/packages/decdk/bin/decdk-schema.ts b/packages/decdk/bin/decdk-schema.ts deleted file mode 100644 index 4a1e66dcd3bf2..0000000000000 --- a/packages/decdk/bin/decdk-schema.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { loadTypeSystem } from '../lib'; -import { renderFullSchema } from '../lib/cdk-schema'; - -/* eslint-disable no-console */ - -async function main() { - const typeSystem = await loadTypeSystem(); - const schema = await renderFullSchema(typeSystem, { colors: true, warnings: true }); - console.log(JSON.stringify(schema, undefined, 2)); -} - -main().catch(e => { - console.error(e); - process.exit(1); -}); \ No newline at end of file diff --git a/packages/decdk/bin/decdk.ts b/packages/decdk/bin/decdk.ts deleted file mode 100644 index 0249084234e83..0000000000000 --- a/packages/decdk/bin/decdk.ts +++ /dev/null @@ -1,26 +0,0 @@ -import * as cdk from '@aws-cdk/core'; -import * as chalk from 'chalk'; -import { DeclarativeStack, loadTypeSystem, readTemplate, stackNameFromFileName } from '../lib'; - -async function main() { - const args = require('yargs') - .usage('$0 ', 'Hydrate a deconstruct file', (yargs: any) => { - yargs.positional('filename', { type: 'string', required: true }); - }) - .parse(); - - const templateFile = args.filename; - const template = await readTemplate(templateFile); - const stackName = stackNameFromFileName(templateFile); - const typeSystem = await loadTypeSystem(); - - const app = new cdk.App(); - new DeclarativeStack(app, stackName, { template, typeSystem }); - app.synth(); -} - -main().catch(e => { - // eslint-disable-next-line no-console - console.error(chalk.red(e)); - process.exit(1); -}); diff --git a/packages/decdk/cloudformation.schema.json b/packages/decdk/cloudformation.schema.json deleted file mode 100644 index 03259e64ab270..0000000000000 --- a/packages/decdk/cloudformation.schema.json +++ /dev/null @@ -1,143 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "additionalProperties": false, - "definitions": { - "Parameter": { - "additionalProperties": false, - "properties": { - "AllowedPattern": { - "type": "string" - }, - "AllowedValues": { - "type": "array" - }, - "ConstraintDescription": { - "type": "string" - }, - "Default": { - "type": "string" - }, - "Description": { - "type": "string" - }, - "MaxLength": { - "type": "string" - }, - "MaxValue": { - "type": "string" - }, - "MinLength": { - "type": "string" - }, - "MinValue": { - "type": "string" - }, - "NoEcho": { - "type": [ - "string", - "boolean" - ] - }, - "Type": { - "type": "string" - } - }, - "required": [ - "Type" - ], - "type": "object" - }, - "Tag": { - "additionalProperties": false, - "properties": { - "Key": { - "type": "string" - }, - "Value": { - "type": "string" - } - }, - "required": [ - "Key", - "Value" - ], - "type": "object" - } - }, - "properties": { - "AWSTemplateFormatVersion": { - "enum": [ - "2010-09-09" - ], - "type": "string" - }, - "Conditions": { - "additionalProperties": false, - "patternProperties": { - "^[a-zA-Z0-9]+$": { - "type": "object" - } - }, - "type": "object" - }, - "Description": { - "description": "Template description", - "maxLength": 1024, - "type": "string" - }, - "Mappings": { - "additionalProperties": false, - "patternProperties": { - "^[a-zA-Z0-9]+$": { - "type": "object" - } - }, - "type": "object" - }, - "Metadata": { - "type": "object" - }, - "Outputs": { - "additionalProperties": false, - "maxProperties": 60, - "minProperties": 1, - "patternProperties": { - "^[a-zA-Z0-9]+$": { - "type": "object" - } - }, - "type": "object" - }, - "Parameters": { - "additionalProperties": false, - "maxProperties": 50, - "patternProperties": { - "^[a-zA-Z0-9]+$": { - "$ref": "#/definitions/Parameter" - } - }, - "type": "object" - }, - "Resources": { - "additionalProperties": false, - "patternProperties": { - "^[a-zA-Z0-9]+$": { - "anyOf": [ - { } - ] - } - }, - "type": "object" - }, - "Transform": { - "type": [ - "object", - "string" - ] - } - }, - "required": [ - "Resources" - ], - "type": "object" -} \ No newline at end of file diff --git a/packages/decdk/deps.js b/packages/decdk/deps.js deleted file mode 100644 index c7b66f27d0827..0000000000000 --- a/packages/decdk/deps.js +++ /dev/null @@ -1,64 +0,0 @@ -// +------------------------------------------------------------------------------------------------ -// | this script runs during build to verify that this package depends on the entire aws construct -// | library. the script will fail (and update package.json) if this is not true. -// | -const fs = require('fs'); -const path = require('path'); - -const pkg = require('./package.json'); -const deps = pkg.dependencies || (pkg.dependencies = {}); - -const root = path.resolve('..', '..', 'packages', '@aws-cdk'); -const modules = fs.readdirSync(root); -let errors = false; - -for (const dir of modules) { - const module = path.resolve(root, dir); - const meta = require(path.join(module, 'package.json')); - - // skip non-jsii modules - if (!meta.jsii) { - continue; - } - - // skip the `@aws-cdk/cloudformation-include` module - if (dir === 'cloudformation-include') { - continue; - } - - const exists = deps[meta.name]; - - if (meta.deprecated) { - if (exists) { - console.error(`spurious dependency on deprecated: ${meta.name}`); - errors = true; - } - delete deps[meta.name]; - continue; - } - // skip private packages - if (meta.private) { - continue; - } - - if (!exists) { - console.error(`missing dependency: ${meta.name}`); - errors = true; - } - - const requirement = `${meta.version}`; - - if (exists && exists !== requirement) { - console.error(`invalid version requirement: expecting '${requirement}', got ${exists}`); - errors = true; - } - - deps[meta.name] = requirement; -} - -fs.writeFileSync(path.join(__dirname, 'package.json'), JSON.stringify(pkg, undefined, 2) + '\n'); - -if (errors) { - console.error('errors found. updated package.json'); - process.exit(1); -} diff --git a/packages/decdk/examples/apigw.json b/packages/decdk/examples/apigw.json deleted file mode 100644 index 458d3abf969e5..0000000000000 --- a/packages/decdk/examples/apigw.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "$schema": "../cdk.schema.json", - "Resources": { - "HelloLambda": { - "Type": "@aws-cdk/aws-lambda.Function", - "Properties": { - "code": { - "asset": { "path": "." } - }, - "runtime": "PYTHON_3_6", - "handler": "index.handler" - } - }, - "MyApi": { - "Type": "@aws-cdk/aws-apigateway.LambdaRestApi", - "Properties": { - "handler": { "Ref": "HelloLambda" } - } - }, - "GetRoot": { - "Type": "@aws-cdk/aws-apigateway.Method", - "Properties": { - "resource": { "Fn::GetAtt": [ "MyApi", "root" ] }, - "httpMethod": "GET" - } - } - } -} \ No newline at end of file diff --git a/packages/decdk/examples/ecs.json b/packages/decdk/examples/ecs.json deleted file mode 100644 index 86d63cbcf8489..0000000000000 --- a/packages/decdk/examples/ecs.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "$schema": "../cdk.schema.json", - "Resources": { - "VPC": { - "Type": "@aws-cdk/aws-ec2.Vpc", - "Properties": { - "maxAzs": 1 - } - }, - "Cluster": { - "Type": "@aws-cdk/aws-ecs.Cluster", - "Properties": { - "vpc": { "Ref": "VPC" } - } - }, - "MyTaskDef": { - "Type": "@aws-cdk/aws-ecs.TaskDefinition", - "Properties": { - "compatibility": "FARGATE", - "family": "redis", - "cpu": "1024", - "memoryMiB": "1GB", - "networkMode": "AWS_VPC" - } - }, - "ContainerDef": { - "Type": "@aws-cdk/aws-ecs.ContainerDefinition", - "Properties": { - "taskDefinition": { "Ref": "MyTaskDef" }, - "essential": true, - "memoryLimitMiB": 1024, - "image": { - "fromRegistry": { - "name": "redis" - } - } - } - }, - "Service": { - "Type": "@aws-cdk/aws-ecs.FargateService", - "Properties": { - "cluster": { "Ref": "Cluster" }, - "taskDefinition": { "Ref": "MyTaskDef" } - } - } - } -} diff --git a/packages/decdk/examples/lambda-events.json b/packages/decdk/examples/lambda-events.json deleted file mode 100644 index 0c364b1d14dd6..0000000000000 --- a/packages/decdk/examples/lambda-events.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "$schema": "../cdk.schema.json", - "Resources": { - "MyTopic": { - "Type": "@aws-cdk/aws-sns.Topic" - }, - "Table": { - "Type": "@aws-cdk/aws-dynamodb.Table", - "Properties": { - "partitionKey": { - "name": "ID", - "type": "STRING" - }, - "stream": "NEW_AND_OLD_IMAGES" - } - }, - "HelloWorldFunction": { - "Type": "@aws-cdk/aws-lambda.Function", - "Properties": { - "handler": "app.hello_handler", - "runtime": "PYTHON_3_6", - "code": { - "asset": { "path": "." } - }, - "environment": { - "Param": "f" - }, - "events": [ - { "@aws-cdk/aws-lambda-event-sources.DynamoEventSource": { "table": { "Ref": "Table" }, "startingPosition": "TRIM_HORIZON" } }, - { "@aws-cdk/aws-lambda-event-sources.ApiEventSource": { "method": "GET", "path": "/hello" } }, - { "@aws-cdk/aws-lambda-event-sources.ApiEventSource": { "method": "POST", "path": "/hello" } }, - { "@aws-cdk/aws-lambda-event-sources.SnsEventSource": { "topic": { "Ref": "MyTopic" } } } - ] - } - } - } -} \ No newline at end of file diff --git a/packages/decdk/examples/lambda-topic.json b/packages/decdk/examples/lambda-topic.json deleted file mode 100644 index 073202c663cce..0000000000000 --- a/packages/decdk/examples/lambda-topic.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "$schema": "../cdk.schema.json", - "Resources": { - "Topic": { - "Type": "@aws-cdk/aws-sns.Topic" - }, - "Lambda": { - "Type": "@aws-cdk/aws-lambda.Function", - "Properties": { - "code": { "asset": { "path": "." } }, - "runtime": "NODEJS", - "handler": "index.handler", - "events": [ - { "@aws-cdk/aws-lambda-event-sources.SnsEventSource": { "topic": { "Ref": "Topic" } } } - ] - } - } - } -} diff --git a/packages/decdk/examples/pipeline.json b/packages/decdk/examples/pipeline.json deleted file mode 100644 index 8c0c2ee5d315f..0000000000000 --- a/packages/decdk/examples/pipeline.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "$schema": "../cdk.schema.json", - "Resources": { - "Repo": { - "Type": "@aws-cdk/aws-codecommit.Repository", - "Properties": { - "repositoryName": "my-first-decdk-repo" - } - }, - "Key": { - "Type": "@aws-cdk/aws-kms.Key" - }, - "BuildProject": { - "Type": "@aws-cdk/aws-codebuild.PipelineProject", - "Properties": { - "encryptionKey": { "Ref": "Key" } - } - }, - "Pipeline": { - "Type": "@aws-cdk/aws-codepipeline.Pipeline", - "Properties": { - "stages": [ - { - "stageName": "Source", - "actions": [ - { - "@aws-cdk/aws-codepipeline-actions.CodeCommitSourceAction": { - "repository": { "Ref": "Repo" }, - "output": { - "artifact": { - "name": "Source" - } - }, - "actionName": "Source" - } - } - ] - }, - { - "stageName": "Build", - "actions": [ - { - "@aws-cdk/aws-codepipeline-actions.CodeBuildAction": { - "actionName": "Build", - "project": { "Ref": "BuildProject" }, - "input": { - "artifact": { - "name": "Source" - } - }, - "outputs": [ - { - "artifact": { - "name": "Build" - } - } - ] - } - } - ] - }, - { - "stageName": "Deploy", - "actions": [ - { - "@aws-cdk/aws-codepipeline-actions.CloudFormationCreateUpdateStackAction": { - "actionName": "Deploy", - "stackName": "MyStack", - "adminPermissions": true, - "templatePath": { - "artifactPath": { - "artifactName": "Build", - "fileName": "template.yaml" - } - } - } - } - ] - } - ] - } - } - } -} diff --git a/packages/decdk/examples/pure-cfn.json b/packages/decdk/examples/pure-cfn.json deleted file mode 100644 index 3d81a99da4978..0000000000000 --- a/packages/decdk/examples/pure-cfn.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "$schema": "../cdk.schema.json", - "Resources": { - "Hello": { - "Type": "@aws-cdk/aws-sqs.Queue", - "Properties": { - "encryption": "Unencrypted" - } - }, - "MyLogGroup": { - "Type": "AWS::Logs::LogGroup", - "Properties": { - "LogGroupName": { "Ref": "AWS::AccountId" } - } - } - } -} \ No newline at end of file diff --git a/packages/decdk/examples/queue-kms.json b/packages/decdk/examples/queue-kms.json deleted file mode 100644 index 50f7da4af5379..0000000000000 --- a/packages/decdk/examples/queue-kms.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "$schema": "../cdk.schema.json", - "Resources": { - "MyQueue": { - "Type": "@aws-cdk/aws-sqs.Queue", - "Properties": { - "encryption": "KMS" - } - } - } -} \ No newline at end of file diff --git a/packages/decdk/examples/vpc.json b/packages/decdk/examples/vpc.json deleted file mode 100644 index 091920e330a39..0000000000000 --- a/packages/decdk/examples/vpc.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "$schema": "../cdk.schema.json", - "Resources": { - "VPC": { - "Type": "@aws-cdk/aws-ec2.Vpc" - } - } -} \ No newline at end of file diff --git a/packages/decdk/jest.config.js b/packages/decdk/jest.config.js deleted file mode 100644 index f4cc833bdc324..0000000000000 --- a/packages/decdk/jest.config.js +++ /dev/null @@ -1,183 +0,0 @@ -// For a detailed explanation regarding each configuration property, visit: -// https://jestjs.io/docs/en/configuration.html - -module.exports = { - // All imported modules in your tests should be mocked automatically - // automock: false, - - // Stop running tests after `n` failures - // bail: 0, - - // Respect "browser" field in package.json when resolving modules - // browser: false, - - // The directory where Jest should store its cached dependency information - // cacheDirectory: "/private/var/folders/n2/6v4_tbz97ws0h4bn5gbyvzb0m8vcjb/T/jest_b92skr", - - // Automatically clear mock calls and instances between every test - clearMocks: true, - - // Indicates whether the coverage information should be collected while executing the test - // collectCoverage: false, - - // An array of glob patterns indicating a set of files for which coverage information should be collected - // collectCoverageFrom: null, - - // The directory where Jest should output its coverage files - coverageDirectory: "coverage", - - // An array of regexp pattern strings used to skip coverage collection - // coveragePathIgnorePatterns: [ - // "/node_modules/" - // ], - - // A list of reporter names that Jest uses when writing coverage reports - // coverageReporters: [ - // "json", - // "text", - // "lcov", - // "clover" - // ], - - // An object that configures minimum threshold enforcement for coverage results - // coverageThreshold: null, - - // A path to a custom dependency extractor - // dependencyExtractor: null, - - // Make calling deprecated APIs throw helpful error messages - // errorOnDeprecated: false, - - // Force coverage collection from ignored files usin a array of glob patterns - // forceCoverageMatch: [], - - // A path to a module which exports an async function that is triggered once before all test suites - // globalSetup: null, - - // A path to a module which exports an async function that is triggered once after all test suites - // globalTeardown: null, - - // A set of global variables that need to be available in all test environments - // globals: {}, - - // An array of directory names to be searched recursively up from the requiring module's location - // moduleDirectories: [ - // "node_modules" - // ], - - // An array of file extensions your modules use - moduleFileExtensions: [ - "js", - "json", - "jsx", - "node" - ], - - // A map from regular expressions to module names that allow to stub out resources with a single module - // moduleNameMapper: {}, - - // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader - // modulePathIgnorePatterns: [], - - // Activates notifications for test results - // notify: false, - - // An enum that specifies notification mode. Requires { notify: true } - // notifyMode: "failure-change", - - // A preset that is used as a base for Jest's configuration - // preset: null, - - // Run tests from one or more projects - // projects: null, - - // Use this configuration option to add custom reporters to Jest - // reporters: undefined, - - // Automatically reset mock state between every test - // resetMocks: false, - - // Reset the module registry before running each individual test - // resetModules: false, - - // A path to a custom resolver - // resolver: null, - - // Automatically restore mock state between every test - // restoreMocks: false, - - // The root directory that Jest should scan for tests and modules within - // rootDir: null, - - // A list of paths to directories that Jest should use to search for files in - // roots: [ - // "" - // ], - - // Allows you to use a custom runner instead of Jest's default test runner - // runner: "jest-runner", - - // The paths to modules that run some code to configure or set up the testing environment before each test - // setupFiles: [], - - // A list of paths to modules that run some code to configure or set up the testing framework before each test - // setupFilesAfterEnv: [], - - // A list of paths to snapshot serializer modules Jest should use for snapshot testing - // snapshotSerializers: [], - - // The test environment that will be used for testing - testEnvironment: "node", - - // Options that will be passed to the testEnvironment - // testEnvironmentOptions: {}, - - // Adds a location field to test results - // testLocationInResults: false, - - // The glob patterns Jest uses to detect test files - // testMatch: [ - // "**/__tests__/**/*.[jt]s?(x)", - // "**/?(*.)+(spec|test).[tj]s?(x)" - // ], - - // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped - // testPathIgnorePatterns: [ - // "/node_modules/" - // ], - - // The regexp pattern or array of patterns that Jest uses to detect test files - // testRegex: [], - - // This option allows the use of a custom results processor - // testResultsProcessor: null, - - // This option allows use of a custom test runner - // testRunner: "jasmine2", - - // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href - // testURL: "http://localhost", - - // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" - // timers: "real", - - // A map from regular expressions to paths to transformers - // transform: null, - - // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation - // transformIgnorePatterns: [ - // "/node_modules/" - // ], - - // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them - // unmockedModulePathPatterns: undefined, - - // Indicates whether each individual test should be reported during the run - // verbose: null, - - // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode - // watchPathIgnorePatterns: [], - - // Whether to use watchman for file crawling - // watchman: true, -}; diff --git a/packages/decdk/lib/cdk-schema.ts b/packages/decdk/lib/cdk-schema.ts deleted file mode 100644 index f12de0d5afa62..0000000000000 --- a/packages/decdk/lib/cdk-schema.ts +++ /dev/null @@ -1,125 +0,0 @@ -import * as chalk from 'chalk'; -import * as jsiiReflect from 'jsii-reflect'; -import { SchemaContext, schemaForTypeReference } from '../lib/jsii2schema'; - -/* eslint-disable no-console */ - -export interface RenderSchemaOptions { - warnings?: boolean; - - /** - * Use colors when printing ouput. - * @default true if tty is enabled - */ - colors?: boolean; -} - -export function renderFullSchema(typeSystem: jsiiReflect.TypeSystem, options: RenderSchemaOptions = { }) { - if (!process.stdin.isTTY || options.colors === false) { - // Disable chalk color highlighting - process.env.FORCE_COLOR = '0'; - } - - // Find all constructs for which the props interface - // (transitively) only consists of JSON primitives or interfaces - // that consist of JSON primitives - const constructType = typeSystem.findClass('constructs.Construct'); - const constructs = typeSystem.classes.filter(c => c.extends(constructType)); - - const deconstructs = constructs - .map(unpackConstruct) - .filter(c => c && !isCfnResource(c.constructClass)) as ConstructAndProps[]; - - const output = require('../cloudformation.schema.json'); - - output.definitions = output.definitions || { }; - - const ctx = SchemaContext.root(output.definitions); - - for (const deco of deconstructs) { - const resource = schemaForResource(deco, ctx); - if (resource) { - output.properties.Resources.patternProperties["^[a-zA-Z0-9]+$"].anyOf.push(resource); - } - } - - output.properties.$schema = { - type: 'string' - }; - - if (options.warnings) { - printWarnings(ctx); - } - - return output; -} - -function printWarnings(node: SchemaContext, indent = '') { - if (!node.hasWarningsOrErrors) { - return; - } - - console.error(indent + node.name); - - for (const warning of node.warnings) { - console.error(chalk.yellow(indent + ' ' + warning)); - } - - for (const error of node.errors) { - console.error(chalk.red(indent + ' ' + error)); - } - - if (!node.root) { - indent += ' '; - } - - for (const child of node.children) { - printWarnings(child, indent); - } -} - -export function schemaForResource(construct: ConstructAndProps, ctx: SchemaContext) { - ctx = ctx.child('resource', construct.constructClass.fqn); - - const propsSchema = schemaForTypeReference(construct.propsTypeRef, ctx); - if (!propsSchema) { - return undefined; - } - - return ctx.define(construct.constructClass.fqn, () => { - return { - additionalProperties: false, - properties: { - Properties: propsSchema, - Type: { - enum: [ construct.constructClass.fqn ], - type: "string" - } - } - }; - }); -} - -function isCfnResource(klass: jsiiReflect.ClassType) { - const resource = klass.system.findClass('@aws-cdk/core.CfnResource'); - return klass.extends(resource); -} - -function unpackConstruct(klass: jsiiReflect.ClassType): ConstructAndProps | undefined { - - if (!klass.initializer || klass.abstract) { return undefined; } - if (klass.initializer.parameters.length < 3) { return undefined; } - - const propsParam = klass.initializer.parameters[2]; - if (propsParam.type.fqn === undefined) { return undefined; } - - return { - constructClass: klass, - propsTypeRef: klass.initializer.parameters[2].type - }; -} - -export interface ConstructAndProps { - constructClass: jsiiReflect.ClassType; - propsTypeRef: jsiiReflect.TypeReference; -} diff --git a/packages/decdk/lib/declarative-stack.ts b/packages/decdk/lib/declarative-stack.ts deleted file mode 100644 index a093d16a5922f..0000000000000 --- a/packages/decdk/lib/declarative-stack.ts +++ /dev/null @@ -1,457 +0,0 @@ -import * as cdk from '@aws-cdk/core'; -import * as reflect from 'jsii-reflect'; -import * as jsonschema from 'jsonschema'; -import { renderFullSchema } from './cdk-schema'; -import { isConstruct, isDataType, isEnumLikeClass, isSerializableInterface, SchemaContext, schemaForPolymorphic } from './jsii2schema'; - -export interface DeclarativeStackProps extends cdk.StackProps { - typeSystem: reflect.TypeSystem; - template: any; - workingDirectory?: string; -} - -export class DeclarativeStack extends cdk.Stack { - constructor(scope: cdk.App, id: string, props: DeclarativeStackProps) { - super(scope, id); - - const typeSystem = props.typeSystem; - const template = props.template; - - const schema = renderFullSchema(typeSystem); - - const result = jsonschema.validate(template, schema); - if (!result.valid) { - throw new ValidationError('Schema validation errors:\n ' + result.errors.map(e => `"${e.property}" ${e.message}`).join('\n ')); - } - - // Replace every resource that starts with CDK:: - for (const [logicalId, resourceProps] of Object.entries(template.Resources || {})) { - const rprops: any = resourceProps; - if (!rprops.Type) { - throw new Error('Resource is missing type: ' + JSON.stringify(resourceProps)); - } - - if (isCfnResourceType(rprops.Type)) { - continue; - } - - const typeInfo = typeSystem.findFqn(rprops.Type + 'Props'); - const typeRef = new reflect.TypeReference(typeSystem, typeInfo); - const Ctor = resolveType(rprops.Type); - - // Changing working directory if needed, such that relative paths in the template are resolved relative to the - // template's location, and not to the current process' CWD. - _cwd(props.workingDirectory, () => - new Ctor(this, logicalId, deserializeValue(this, typeRef, true, 'Properties', rprops.Properties))); - - delete template.Resources[logicalId]; - } - - delete template.$schema; - - // Add an Include construct with what's left of the template - new cdk.CfnInclude(this, 'Include', { template }); - - // replace all "Fn::GetAtt" with tokens that resolve correctly both for - // constructs and raw resources. - processReferences(this); - } -} - -function resolveType(fqn: string) { - const [ mod, ...className ] = fqn.split('.'); - const module = require(mod); - return module[className.join('.')]; -} - -function tryResolveIntrinsic(value: any) { - if (Object.keys(value).length !== 1) { - return undefined; - } - - const name = Object.keys(value)[0]; - const val = value[name]; - return { name, val }; -} - -function tryResolveRef(value: any) { - const fn = tryResolveIntrinsic(value); - if (!fn) { - return undefined; - } - - if (fn.name !== 'Ref') { - return undefined; - } - - return fn.val; -} - -function tryResolveGetAtt(value: any) { - const fn = tryResolveIntrinsic(value); - if (!fn || fn.name !== 'Fn::GetAtt') { - return undefined; - } - - return fn.val; -} - -function deserializeValue(stack: cdk.Stack, typeRef: reflect.TypeReference, optional: boolean, key: string, value: any): any { - // console.error('====== deserializer ==================='); - // console.error(`type: ${typeRef}`); - // console.error(`value: ${JSON.stringify(value, undefined, 2)}`); - // console.error('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`'); - - if (value === undefined) { - if (optional) { - return undefined; - } - - throw new Error(`Missing required value for ${key} in ${typeRef}`); - } - - // deserialize arrays - if (typeRef.arrayOfType) { - if (!Array.isArray(value)) { - throw new Error(`Expecting array for ${key} in ${typeRef}`); - } - - return value.map((x, i) => deserializeValue(stack, typeRef.arrayOfType!, false, `${key}[${i}]`, x)); - } - - const asRef = tryResolveRef(value); - if (asRef) { - if (isConstruct(typeRef)) { - return findConstruct(stack, value.Ref); - } - - throw new Error( - `{ Ref } can only be used when a construct type is expected and this is ${typeRef}. ` + - `Use { Fn::GetAtt } to represent specific resource attributes`); - } - - const getAtt = tryResolveGetAtt(value); - if (getAtt) { - const [ logical, attr ] = getAtt; - - if (isConstruct(typeRef)) { - const obj: any = findConstruct(stack, logical); - return obj[attr]; - } - - if (typeRef.primitive === 'string') { - // return a lazy value, so we only try to find after all constructs - // have been added to the stack. - return deconstructGetAtt(stack, logical, attr); - } - - throw new Error(`Fn::GetAtt can only be used for string primitives and ${key} is ${typeRef}`); - } - - // deserialize maps - if (typeRef.mapOfType) { - if (typeof(value) !== 'object') { - throw new ValidationError(`Expecting object for ${key} in ${typeRef}`); - } - - const out: any = { }; - for (const [ k, v ] of Object.entries(value)) { - out[k] = deserializeValue(stack, typeRef.mapOfType, false, `${key}.${k}`, v); - } - - return out; - } - - if (typeRef.unionOfTypes) { - const errors = new Array(); - for (const x of typeRef.unionOfTypes) { - try { - return deserializeValue(stack, x, optional, key, value); - } catch (e) { - if (!(e instanceof ValidationError)) { - throw e; - } - errors.push(e); - continue; - } - } - - throw new ValidationError(`Failed to deserialize union. Errors: \n ${errors.map(e => e.message).join('\n ')}`); - } - - const enm = deconstructEnum(stack, typeRef, key, value); - if (enm) { - return enm; - } - - // if this is an interface, deserialize each property - const ifc = deconstructInterface(stack, typeRef, key, value); - if (ifc) { - return ifc; - } - - // if this is an enum type, use the name to dereference - if (typeRef.type instanceof reflect.EnumType) { - const enumType = resolveType(typeRef.type.fqn); - return enumType[value]; - } - - if (typeRef.primitive) { - return value; - } - - const enumLike = deconstructEnumLike(stack, typeRef, value); - if (enumLike) { - return enumLike; - } - - const asType = deconstructType(stack, typeRef, value); - if (asType) { - return asType; - } - - throw new Error(`Unable to deconstruct "${JSON.stringify(value)}" for type ref ${typeRef}`); -} - -function deconstructEnum(_stack: cdk.Stack, typeRef: reflect.TypeReference, _key: string, value: any) { - if (!(typeRef.type instanceof reflect.EnumType)) { - return undefined; - } - - const enumType = resolveType(typeRef.type.fqn); - return enumType[value]; -} - -function deconstructInterface(stack: cdk.Stack, typeRef: reflect.TypeReference, key: string, value: any) { - if (!isSerializableInterface(typeRef.type)) { - return undefined; - } - - const out: any = { }; - for (const prop of typeRef.type.allProperties) { - const propValue = value[prop.name]; - if (!propValue) { - if (!prop.optional) { - throw new ValidationError(`Missing required property ${key}.${prop.name} in ${typeRef}`); - } - continue; - } - - out[prop.name] = deserializeValue(stack, prop.type, prop.optional, `${key}.${prop.name}`, propValue); - } - - return out; -} - -function deconstructEnumLike(stack: cdk.Stack, typeRef: reflect.TypeReference, value: any) { - if (!isEnumLikeClass(typeRef.type)) { - return undefined; - } - - // if the value is a string, we deconstruct it as a static property - if (typeof(value) === 'string') { - return deconstructStaticProperty(typeRef.type, value); - } - - // if the value is an object, we deconstruct it as a static method - if (typeof(value) === 'object' && !Array.isArray(value)) { - return deconstructStaticMethod(stack, typeRef.type, value); - } - - throw new Error(`Invalid value for enum-like class ${typeRef.fqn}: ${JSON.stringify(value)}`); -} - -function deconstructType(stack: cdk.Stack, typeRef: reflect.TypeReference, value: any) { - const schemaDefs: any = {}; - const ctx = SchemaContext.root(schemaDefs); - const schemaRef = schemaForPolymorphic(typeRef.type, ctx); - if (!schemaRef) { - return undefined; - } - - const def = findDefinition(schemaDefs, schemaRef.$ref); - - const keys = Object.keys(value); - if (keys.length !== 1) { - throw new ValidationError(`Cannot parse class type ${typeRef} with value ${value}`); - } - - const className = keys[0]; - - // now we need to check if it's an enum or a normal class - const schema = def.anyOf.find((x: any) => x.properties && x.properties[className]); - if (!schema) { - throw new ValidationError(`Cannot find schema for ${className}`); - } - - const def2 = findDefinition(schemaDefs, schema.properties[className].$ref); - const methodFqn = def2.comment; - - const parts = methodFqn.split('.'); - const last = parts[parts.length - 1]; - if (last !== '') { - throw new Error(`Expectring an initializer`); - } - - const classFqn = parts.slice(0, parts.length - 1).join('.'); - const method = typeRef.system.findClass(classFqn).initializer; - if (!method) { - throw new Error(`Cannot find the initializer for ${classFqn}`); - } - - return invokeMethod(stack, method, value[className]); -} - -function findDefinition(defs: any, $ref: string) { - const k = $ref.split('/').slice(2).join('/'); - return defs[k]; -} - -function deconstructStaticProperty(typeRef: reflect.ClassType, value: string) { - const typeClass = resolveType(typeRef.fqn); - return typeClass[value]; -} - -function deconstructStaticMethod(stack: cdk.Stack, typeRef: reflect.ClassType, value: any) { - const methods = typeRef.allMethods.filter(m => m.static); - const members = methods.map(x => x.name); - - if (typeof(value) === 'object') { - const entries: Array<[ string, any ]> = Object.entries(value); - if (entries.length !== 1) { - throw new Error(`Value for enum-like class ${typeRef.fqn} must be an object with a single key (one of: ${members.join(',')})`); - } - - const [ methodName, args ] = entries[0]; - const method = methods.find(m => m.name === methodName); - if (!method) { - throw new Error(`Invalid member "${methodName}" for enum-like class ${typeRef.fqn}. Options: ${members.join(',')}`); - } - - if (typeof(args) !== 'object') { - throw new Error(`Expecting enum-like member ${methodName} to be an object for enum-like class ${typeRef.fqn}`); - } - - return invokeMethod(stack, method, args); - } -} - -function invokeMethod(stack: cdk.Stack, method: reflect.Callable, parameters: any) { - const typeClass = resolveType(method.parentType.fqn); - const args = new Array(); - - for (let i = 0; i < method.parameters.length; ++i) { - const p = method.parameters[i]; - - // kwargs: if this is the last argument and a data type, flatten (treat as keyword args) - if (i === method.parameters.length - 1 && isDataType(p.type.type)) { - // we pass in all parameters are the value, and the positional arguments will be ignored since - // we are promised there are no conflicts - const kwargs = deserializeValue(stack, p.type, p.optional, p.name, parameters); - args.push(kwargs); - } else { - const val = parameters[p.name]; - if (val === undefined && !p.optional) { - throw new Error(`Missing required parameter '${p.name}' for ${method.parentType.fqn}.${method.name}`); - } - - if (val !== undefined) { - args.push(deserializeValue(stack, p.type, p.optional, p.name, val)); - } - } - } - - if (reflect.Initializer.isInitializer(method)) { - return new typeClass(...args); - } - - const methodFn: (...args: any[]) => any = typeClass[method.name]; - if (!methodFn) { - throw new Error(`Cannot find method named ${method.name} in ${typeClass.fqn}`); - } - - return methodFn.apply(typeClass, args); -} - -/** - * Returns a lazy string that includes a deconstructed Fn::GetAt to a certain - * resource or construct. - * - * If `id` points to a CDK construct, the resolved value will be the value returned by - * the property `attribute`. If `id` points to a "raw" resource, the resolved value will be - * an `Fn::GetAtt`. - */ -function deconstructGetAtt(stack: cdk.Stack, id: string, attribute: string) { - return cdk.Lazy.string({ produce: () => { - const res = stack.node.tryFindChild(id); - if (!res) { - const include = stack.node.tryFindChild('Include') as cdk.CfnInclude; - if (!include) { - throw new Error(`Unexpected - "Include" should be in the stack at this point`); - } - - const raw = (include.template as any).Resources[id]; - if (!raw) { - throw new Error(`Unable to find a resource ${id}`); - } - - // just leak - return { "Fn::GetAtt": [ id, attribute ] }; - } - return (res as any)[attribute]; - }}); -} - -function findConstruct(stack: cdk.Stack, id: string) { - const child = stack.node.tryFindChild(id); - if (!child) { - throw new Error(`Construct with ID ${id} not found (it must be defined before it is referenced)`); - } - return child; -} - -function processReferences(stack: cdk.Stack) { - const include = stack.node.findChild('Include') as cdk.CfnInclude; - if (!include) { - throw new Error('Unexpected'); - } - - process(include.template as any); - - function process(value: any): any { - if (typeof(value) === 'object' && Object.keys(value).length === 1 && Object.keys(value)[0] === 'Fn::GetAtt') { - const [ id, attribute ] = value['Fn::GetAtt']; - return deconstructGetAtt(stack, id, attribute); - } - - if (Array.isArray(value)) { - return value.map(x => process(x)); - } - - if (typeof(value) === 'object') { - for (const [ k, v ] of Object.entries(value)) { - value[k] = process(v); - } - return value; - } - - return value; - } -} - -function isCfnResourceType(resourceType: string) { - return resourceType.includes('::'); -} - -class ValidationError extends Error { } - -function _cwd(workDir: string | undefined, cb: () => T): T { - if (!workDir) { return cb(); } - const prevWd = process.cwd(); - try { - process.chdir(workDir); - return cb(); - } finally { - process.chdir(prevWd); - } -} diff --git a/packages/decdk/lib/index.ts b/packages/decdk/lib/index.ts deleted file mode 100644 index f095ba5e406a8..0000000000000 --- a/packages/decdk/lib/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './declarative-stack'; -export * from './jsii2schema'; -export * from './util'; \ No newline at end of file diff --git a/packages/decdk/lib/jsii2schema.ts b/packages/decdk/lib/jsii2schema.ts deleted file mode 100644 index 2b490d7df2125..0000000000000 --- a/packages/decdk/lib/jsii2schema.ts +++ /dev/null @@ -1,593 +0,0 @@ -import * as jsiiReflect from 'jsii-reflect'; -import * as util from 'util'; - -/* eslint-disable no-console */ - -export class SchemaContext { - public static root(definitions?: { [fqn: string]: any }): SchemaContext { - return new SchemaContext(undefined, undefined, definitions); - } - - public readonly definitions: { [fqn: string]: any }; - public readonly path: string; - public readonly children = new Array(); - public readonly name: string; - public readonly root: boolean; - public readonly warnings = new Array(); - public readonly errors = new Array(); - - private readonly definitionStack: string[]; - - private constructor(name?: string, parent?: SchemaContext, definitions?: { [fqn: string]: any }) { - this.name = name || ''; - if (parent) { - this.root = false; - parent.children.push(this); - this.definitions = parent.definitions; - this.path = parent.path + '/' + this.name; - this.definitionStack = parent.definitionStack; - } else { - this.root = true; - this.definitions = definitions || { }; - this.path = this.name || ''; - this.definitionStack = new Array(); - } - } - - public child(type: string, name: string): SchemaContext { - return new SchemaContext(`[${type} "${name}"]`, this); - } - - public get hasWarningsOrErrors(): boolean { - return this.warnings.length > 0 || this.errors.length > 0 || this.children.some(child => child.hasWarningsOrErrors); - } - - public warning(format: any, ...args: any[]) { - this.warnings.push(util.format(format, ...args)); - } - - public error(format: any, ...args: any[]) { - this.errors.push(util.format(format, ...args)); - } - - public findDefinition(ref: string) { - const [ , , id ] = ref.split('/'); - return this.definitions[id]; - } - - public define(fqn: string, schema: () => any) { - const originalFqn = fqn; - fqn = fqn.replace('/', '.'); - - if (!(fqn in this.definitions)) { - if (this.definitionStack.includes(fqn)) { - this.error(`cyclic definition of ${fqn}`); - return undefined; - } - - this.definitionStack.push(fqn); - - try { - const s = schema(); - if (!s) { - this.error('cannot schematize'); - return undefined; - } - - s.comment = originalFqn; - - this.definitions[fqn] = s; - } finally { - this.definitionStack.pop(); - } - } - - return { $ref: `#/definitions/${fqn}` }; - } -} - -export function schemaForTypeReference(type: jsiiReflect.TypeReference, ctx: SchemaContext): any { - - const prim = schemaForPrimitive(type); - if (prim) { - return prim; - } - - const arr = schemaForArray(type, ctx); - if (arr) { - return arr; - } - - const map = schemaForMap(type, ctx); - if (map) { - return map; - } - - const union = schemaForUnion(type, ctx); - if (union) { - return union; - } - - const constructRef = schemaForConstructRef(type); - if (constructRef) { - return constructRef; - } - - const iface = schemaForInterface(type.type, ctx); - if (iface) { - return iface; - } - - const enm = schemaForEnum(type.type); - if (enm) { - return enm; - } - - const enumLike = schemaForEnumLikeClass(type.type, ctx); - if (enumLike) { - return enumLike; - } - - const cls = schemaForPolymorphic(type.type, ctx); - if (cls) { - return cls; - } - - if (!ctx.hasWarningsOrErrors) { - ctx.error(`didn't match any schematizable shape`); - } - - return undefined; -} - -export function schemaForPolymorphic(type: jsiiReflect.Type | undefined, ctx: SchemaContext) { - if (!type) { - return undefined; - } - - ctx = ctx.child('polymorphic', type.fqn); - - const anyOf = new Array(); - - const parentctx = ctx; - - for (const x of allImplementationsOfType(type)) { - - ctx = parentctx.child('impl', x.fqn); - - const enumLike = schemaForEnumLikeClass(x, ctx); - if (enumLike) { - anyOf.push(enumLike); - } - - if (x.initializer) { - const methd = methodSchema(x.initializer, ctx); - if (methd) { - anyOf.push({ - type: 'object', - additionalProperties: false, - properties: { - [x.fqn]: methd - } - }); - } - } - } - - if (anyOf.length === 0) { - return undefined; - } - - return ctx.define(type.fqn, () => { - return { anyOf }; - }); -} - -function schemaForEnum(type: jsiiReflect.Type | undefined) { - if (!type || !(type instanceof jsiiReflect.EnumType)) { - return undefined; - } - - return { - enum: type.members.map(m => m.name) - }; -} - -function schemaForMap(type: jsiiReflect.TypeReference, ctx: SchemaContext) { - ctx = ctx.child('map', type.toString()); - - if (!type.mapOfType) { - return undefined; - } - - const s = schemaForTypeReference(type.mapOfType, ctx); - if (!s) { - return undefined; - } - - return { - type: 'object', - additionalProperties: s - }; -} - -function schemaForArray(type: jsiiReflect.TypeReference, ctx: SchemaContext) { - ctx = ctx.child('array', type.toString()); - - if (!type.arrayOfType) { - return undefined; - } - - const s = schemaForTypeReference(type.arrayOfType, ctx); - if (!s) { - return undefined; - } - - return { - type: 'array', - items: schemaForTypeReference(type.arrayOfType, ctx) - }; -} - -function schemaForPrimitive(type: jsiiReflect.TypeReference): any { - if (!type.primitive) { - return undefined; - } - - switch (type.primitive) { - case 'date': return { type: 'string', format: 'date-time' }; - case 'json': return { type: 'object' }; - case 'any': return { }; // this means "any" - default: return { type: type.primitive }; - } -} - -function schemaForUnion(type: jsiiReflect.TypeReference, ctx: SchemaContext): any { - ctx = ctx.child('union', type.toString()); - - if (!type.unionOfTypes) { - return undefined; - } - - const anyOf = type.unionOfTypes - .map(x => schemaForTypeReference(x, ctx)) - .filter(x => x); // filter failed schemas - - if (anyOf.length === 0) { - return undefined; - } - - return { anyOf }; -} - -function schemaForConstructRef(type: jsiiReflect.TypeReference) { - if (!isConstruct(type)) { - return undefined; - } - - return { - type: 'object', - properties: { - Ref: { type: 'string' } - } - }; -} - -export function schemaForInterface(type: jsiiReflect.Type | undefined, ctx: SchemaContext) { - if (!type || !(type instanceof jsiiReflect.InterfaceType)) { - return undefined; // skip - } - - if (type.allMethods.length > 0) { - return undefined; - } - - ctx = ctx.child('interface', type.fqn); - - const ifctx = ctx; - - return ctx.define(type.fqn, () => { - const properties: any = {}; - const required = new Array(); - - for (const prop of type.allProperties) { - - ctx = ifctx.child(prop.optional ? 'optional' : 'required' + ' property', prop.name); - - const schema = schemaForTypeReference(prop.type, ctx); - if (!schema) { - // if prop is not serializable but optional, we can still serialize - // but without this property. - if (prop.optional) { - ctx.warning(`optional proprety omitted because it cannot be schematized`); - continue; - } - - // error - ctx.error('property cannot be schematized'); - return undefined; - } - - properties[prop.name] = schema; - - const docstring = prop.docs.toString(); - if (docstring) { - properties[prop.name].description = docstring; - } - - if (!prop.optional) { - required.push(prop.name); - } - } - - return { - type: 'object', - title: type.name, - additionalProperties: false, - properties, - required: required.length > 0 ? required : undefined, - }; - }); -} - -function schemaForEnumLikeClass(type: jsiiReflect.Type | undefined, ctx: SchemaContext) { - if (type) { - ctx = ctx.child('enum-like', type.toString()); - } - - if (!type || !(type instanceof jsiiReflect.ClassType)) { - return undefined; - } - - const enumLikeProps = enumLikeClassProperties(type); - const enumLikeMethods = enumLikeClassMethods(type); - - if (enumLikeProps.length === 0 && enumLikeMethods.length === 0) { - return undefined; - } - - const anyOf = new Array(); - - if (enumLikeProps.length > 0) { - anyOf.push({ enum: enumLikeProps.map(m => m.name) }); - } - - for (const method of enumLikeMethods) { - const s = methodSchema(method, ctx); - if (!s) { - continue; - } - - anyOf.push({ - type: 'object', - additionalProperties: false, - properties: { - [method.name]: methodSchema(method, ctx) - } - }); - } - - if (anyOf.length === 0) { - return undefined; - } - - return ctx.define(type.fqn, () => { - return { anyOf }; - }); -} - -function methodSchema(method: jsiiReflect.Callable, ctx: SchemaContext) { - ctx = ctx.child('method', method.name); - - const fqn = `${method.parentType.fqn}.${method.name}`; - - const methodctx = ctx; - - return ctx.define(fqn, () => { - const properties: any = { }; - const required = new Array(); - - const addProperty = (prop: jsiiReflect.Property | jsiiReflect.Parameter): void => { - const param = schemaForTypeReference(prop.type, ctx); - - // bail out - can't serialize a required parameter, so we can't serialize the method - if (!param && !prop.optional) { - ctx.error(`cannot schematize method because parameter cannot be schematized`); - return undefined; - } - - properties[prop.name] = param; - - if (!prop.optional) { - required.push(prop.name); - } - }; - - for (let i = 0; i < method.parameters.length; ++i) { - const p = method.parameters[i]; - methodctx.child('param', p.name); - - // if this is the last parameter and it's a data type, treat as keyword arguments - if (i === method.parameters.length - 1 && isDataType(p.type.type)) { - const kwargs = schemaForInterface(p.type.type, ctx); - if (kwargs) { - for (const prop of p.type.type.allProperties) { - addProperty(prop); - } - } - } else { - addProperty(p); - } - } - - return { - type: 'object', - properties, - additionalProperties: false, - required: required.length > 0 ? required : undefined - }; - }); -} - -export function isDataType(t: jsiiReflect.Type | undefined): t is jsiiReflect.InterfaceType { - if (!t) { - return false; - } - return t instanceof jsiiReflect.InterfaceType && (t as any).spec.datatype; -} - -// Must only have properties, all of which are scalars, -// lists or isSerializableInterface types. -export function isSerializableTypeReference(type: jsiiReflect.TypeReference, errorPrefix?: string): boolean { - - if (type.primitive) { - return true; - } - - if (type.arrayOfType) { - return isSerializableTypeReference(type.arrayOfType, errorPrefix); - } - - if (type.mapOfType) { - return isSerializableTypeReference(type.mapOfType, errorPrefix); - } - - if (type.type) { - return isSerializableType(type.type, errorPrefix); - } - - if (type.unionOfTypes) { - return type.unionOfTypes.some(x => isSerializableTypeReference(x, errorPrefix)); - } - - return false; -} - -function isSerializableType(type: jsiiReflect.Type, errorPrefix?: string): boolean { - // if this is a cosntruct class, we can represent it as a "Ref" - if (isConstruct(type)) { - return true; - } - - if (isEnum(type)) { - return true; - } - - if (isSerializableInterface(type)) { - return true; - } - - // if this is a class that looks like an enum, we can represent it - if (isEnumLikeClass(type)) { - return true; - } - - if (allImplementationsOfType(type).length > 0) { - return true; - } - - if (errorPrefix) { - console.error(errorPrefix, `${type} is not serializable`); - } - - return false; -} - -export function isSerializableInterface(type: jsiiReflect.Type | undefined, errorPrefix?: string): type is jsiiReflect.InterfaceType { - if (!type || !(type instanceof jsiiReflect.InterfaceType)) { - return false; - } - - if (type.allMethods.length > 0) { - return false; - } - - return type.allProperties.every(p => - isSerializableTypeReference(p.type, errorPrefix) - || isConstruct(p.type) - || p.optional); -} - -function isEnum(type: jsiiReflect.Type): type is jsiiReflect.EnumType { - return type instanceof jsiiReflect.EnumType; -} - -export function isEnumLikeClass(cls: jsiiReflect.Type | undefined): cls is jsiiReflect.ClassType { - if (!cls) { - return false; - } - - if (!(cls instanceof jsiiReflect.ClassType)) { - return false; - } - return enumLikeClassMethods(cls).length > 0 - || enumLikeClassProperties(cls).length > 0; -} - -export function enumLikeClassMethods(cls: jsiiReflect.ClassType) { - return cls.allMethods.filter(m => m.static && m.returns && m.returns.type.type && m.returns.type.type.extends(cls)); -} - -export function enumLikeClassProperties(cls: jsiiReflect.ClassType) { - return cls.allProperties.filter(p => p.static && p.type.type && p.type.type.extends(cls)); -} - -export function isConstruct(typeOrTypeRef: jsiiReflect.TypeReference | jsiiReflect.Type): boolean { - let type: jsiiReflect.Type; - - if (typeOrTypeRef instanceof jsiiReflect.Type) { - type = typeOrTypeRef; - } else { - if (typeOrTypeRef.arrayOfType) { - return isConstruct(typeOrTypeRef.arrayOfType); - } - - if (typeOrTypeRef.mapOfType) { - return isConstruct(typeOrTypeRef.mapOfType); - } - - if (typeOrTypeRef.unionOfTypes) { - return typeOrTypeRef.unionOfTypes.some(x => isConstruct(x)); - } - - if (typeOrTypeRef.type) { - type = typeOrTypeRef.type; - } else { - return false; - } - } - - // if it is an interface, it should extend constructs.IConstruct - if (type instanceof jsiiReflect.InterfaceType) { - const constructIface = type.system.findFqn('constructs.IConstruct'); - return type.extends(constructIface); - } - - // if it is a class, it should extend constructs.Construct - if (type instanceof jsiiReflect.ClassType) { - const constructClass = type.system.findFqn('constructs.Construct'); - return type.extends(constructClass); - } - - return false; -} - -function allImplementationsOfType(type: jsiiReflect.Type) { - if (type instanceof jsiiReflect.ClassType) { - return allSubclasses(type).filter(x => !x.abstract); - } - - if (type instanceof jsiiReflect.InterfaceType) { - return allImplementations(type).filter(x => !x.abstract); - } - - throw new Error(`Must either be a class or an interface`); -} - -function allSubclasses(base: jsiiReflect.ClassType) { - return base.system.classes.filter(x => x.extends(base)); -} - -function allImplementations(base: jsiiReflect.InterfaceType) { - return base.system.classes.filter(x => x.getInterfaces(true).some(i => i.extends(base))); -} diff --git a/packages/decdk/lib/util.ts b/packages/decdk/lib/util.ts deleted file mode 100644 index 4378ba01c8223..0000000000000 --- a/packages/decdk/lib/util.ts +++ /dev/null @@ -1,23 +0,0 @@ -import * as fs from 'fs-extra'; -import * as jsiiReflect from 'jsii-reflect'; -import * as path from 'path'; -import * as YAML from 'yaml'; - -/** - * Reads a YAML/JSON template file. - */ -export async function readTemplate(templateFile: string) { - const str = await fs.readFile(templateFile, { encoding: 'utf-8' }); - const template = YAML.parse(str, { schema: 'yaml-1.1' }); - return template; -} - -export async function loadTypeSystem(validate = true) { - const typeSystem = new jsiiReflect.TypeSystem(); - await typeSystem.loadNpmDependencies(path.resolve(__dirname, '..'), { validate }); - return typeSystem; -} - -export function stackNameFromFileName(fileName: string) { - return path.parse(fileName).name.replace('.', '-'); -} diff --git a/packages/decdk/package.json b/packages/decdk/package.json deleted file mode 100644 index d53367fd2dfec..0000000000000 --- a/packages/decdk/package.json +++ /dev/null @@ -1,282 +0,0 @@ -{ - "name": "decdk", - "version": "0.0.0", - "description": "Declarative CDK: a CloudFormation-like syntax for defining CDK stacks", - "main": "lib/index.js", - "types": "lib/index.d.ts", - "repository": { - "type": "git", - "url": "https://github.com/aws/aws-cdk.git" - }, - "bin": { - "decdk": "bin/decdk", - "decdk-schema": "bin/decdk-schema" - }, - "scripts": { - "build": "node ./deps.js && tsc -b && chmod +x bin/decdk && chmod +x bin/decdk-schema && bin/decdk-schema > cdk.schema.json", - "watch": "tsc -b -w", - "test": "jest", - "package": "mkdir -p dist/js && cd dist/js && npm pack ../../", - "build+test": "npm run build && npm test", - "build+test+package": "npm run build+test && npm run package", - "build+test+extract": "npm run build+test", - "build+extract": "npm run build" - }, - "author": { - "name": "Amazon Web Services", - "url": "https://aws.amazon.com", - "organization": true - }, - "license": "Apache-2.0", - "dependencies": { - "@aws-cdk/alexa-ask": "0.0.0", - "@aws-cdk/assertions": "0.0.0", - "@aws-cdk/assets": "0.0.0", - "@aws-cdk/aws-accessanalyzer": "0.0.0", - "@aws-cdk/aws-acmpca": "0.0.0", - "@aws-cdk/aws-amazonmq": "0.0.0", - "@aws-cdk/aws-amplify": "0.0.0", - "@aws-cdk/aws-amplifyuibuilder": "0.0.0", - "@aws-cdk/aws-apigateway": "0.0.0", - "@aws-cdk/aws-apigatewayv2": "0.0.0", - "@aws-cdk/aws-apigatewayv2-authorizers": "0.0.0", - "@aws-cdk/aws-apigatewayv2-integrations": "0.0.0", - "@aws-cdk/aws-appconfig": "0.0.0", - "@aws-cdk/aws-appflow": "0.0.0", - "@aws-cdk/aws-appintegrations": "0.0.0", - "@aws-cdk/aws-applicationautoscaling": "0.0.0", - "@aws-cdk/aws-applicationinsights": "0.0.0", - "@aws-cdk/aws-appmesh": "0.0.0", - "@aws-cdk/aws-apprunner": "0.0.0", - "@aws-cdk/aws-appstream": "0.0.0", - "@aws-cdk/aws-appsync": "0.0.0", - "@aws-cdk/aws-aps": "0.0.0", - "@aws-cdk/aws-athena": "0.0.0", - "@aws-cdk/aws-auditmanager": "0.0.0", - "@aws-cdk/aws-autoscaling": "0.0.0", - "@aws-cdk/aws-autoscaling-common": "0.0.0", - "@aws-cdk/aws-autoscaling-hooktargets": "0.0.0", - "@aws-cdk/aws-autoscalingplans": "0.0.0", - "@aws-cdk/aws-backup": "0.0.0", - "@aws-cdk/aws-batch": "0.0.0", - "@aws-cdk/aws-budgets": "0.0.0", - "@aws-cdk/aws-cassandra": "0.0.0", - "@aws-cdk/aws-ce": "0.0.0", - "@aws-cdk/aws-certificatemanager": "0.0.0", - "@aws-cdk/aws-chatbot": "0.0.0", - "@aws-cdk/aws-cloud9": "0.0.0", - "@aws-cdk/aws-cloudformation": "0.0.0", - "@aws-cdk/aws-cloudfront": "0.0.0", - "@aws-cdk/aws-cloudfront-origins": "0.0.0", - "@aws-cdk/aws-cloudtrail": "0.0.0", - "@aws-cdk/aws-cloudwatch": "0.0.0", - "@aws-cdk/aws-cloudwatch-actions": "0.0.0", - "@aws-cdk/aws-codeartifact": "0.0.0", - "@aws-cdk/aws-codebuild": "0.0.0", - "@aws-cdk/aws-codecommit": "0.0.0", - "@aws-cdk/aws-codedeploy": "0.0.0", - "@aws-cdk/aws-codeguruprofiler": "0.0.0", - "@aws-cdk/aws-codegurureviewer": "0.0.0", - "@aws-cdk/aws-codepipeline": "0.0.0", - "@aws-cdk/aws-codepipeline-actions": "0.0.0", - "@aws-cdk/aws-codestar": "0.0.0", - "@aws-cdk/aws-codestarconnections": "0.0.0", - "@aws-cdk/aws-codestarnotifications": "0.0.0", - "@aws-cdk/aws-cognito": "0.0.0", - "@aws-cdk/aws-cognito-identitypool": "0.0.0", - "@aws-cdk/aws-config": "0.0.0", - "@aws-cdk/aws-connect": "0.0.0", - "@aws-cdk/aws-cur": "0.0.0", - "@aws-cdk/aws-customerprofiles": "0.0.0", - "@aws-cdk/aws-databrew": "0.0.0", - "@aws-cdk/aws-datapipeline": "0.0.0", - "@aws-cdk/aws-datasync": "0.0.0", - "@aws-cdk/aws-dax": "0.0.0", - "@aws-cdk/aws-detective": "0.0.0", - "@aws-cdk/aws-devopsguru": "0.0.0", - "@aws-cdk/aws-directoryservice": "0.0.0", - "@aws-cdk/aws-dlm": "0.0.0", - "@aws-cdk/aws-dms": "0.0.0", - "@aws-cdk/aws-docdb": "0.0.0", - "@aws-cdk/aws-dynamodb": "0.0.0", - "@aws-cdk/aws-ec2": "0.0.0", - "@aws-cdk/aws-ecr": "0.0.0", - "@aws-cdk/aws-ecr-assets": "0.0.0", - "@aws-cdk/aws-ecs": "0.0.0", - "@aws-cdk/aws-ecs-patterns": "0.0.0", - "@aws-cdk/aws-efs": "0.0.0", - "@aws-cdk/aws-eks": "0.0.0", - "@aws-cdk/aws-elasticache": "0.0.0", - "@aws-cdk/aws-elasticbeanstalk": "0.0.0", - "@aws-cdk/aws-elasticloadbalancing": "0.0.0", - "@aws-cdk/aws-elasticloadbalancingv2": "0.0.0", - "@aws-cdk/aws-elasticloadbalancingv2-actions": "0.0.0", - "@aws-cdk/aws-elasticloadbalancingv2-targets": "0.0.0", - "@aws-cdk/aws-elasticsearch": "0.0.0", - "@aws-cdk/aws-emr": "0.0.0", - "@aws-cdk/aws-emrcontainers": "0.0.0", - "@aws-cdk/aws-events": "0.0.0", - "@aws-cdk/aws-events-targets": "0.0.0", - "@aws-cdk/aws-eventschemas": "0.0.0", - "@aws-cdk/aws-evidently": "0.0.0", - "@aws-cdk/aws-finspace": "0.0.0", - "@aws-cdk/aws-fis": "0.0.0", - "@aws-cdk/aws-fms": "0.0.0", - "@aws-cdk/aws-forecast": "0.0.0", - "@aws-cdk/aws-frauddetector": "0.0.0", - "@aws-cdk/aws-fsx": "0.0.0", - "@aws-cdk/aws-gamelift": "0.0.0", - "@aws-cdk/aws-globalaccelerator": "0.0.0", - "@aws-cdk/aws-globalaccelerator-endpoints": "0.0.0", - "@aws-cdk/aws-glue": "0.0.0", - "@aws-cdk/aws-greengrass": "0.0.0", - "@aws-cdk/aws-greengrassv2": "0.0.0", - "@aws-cdk/aws-groundstation": "0.0.0", - "@aws-cdk/aws-guardduty": "0.0.0", - "@aws-cdk/aws-healthlake": "0.0.0", - "@aws-cdk/aws-iam": "0.0.0", - "@aws-cdk/aws-imagebuilder": "0.0.0", - "@aws-cdk/aws-inspector": "0.0.0", - "@aws-cdk/aws-inspectorv2": "0.0.0", - "@aws-cdk/aws-iot": "0.0.0", - "@aws-cdk/aws-iot-actions": "0.0.0", - "@aws-cdk/aws-iot1click": "0.0.0", - "@aws-cdk/aws-iotanalytics": "0.0.0", - "@aws-cdk/aws-iotcoredeviceadvisor": "0.0.0", - "@aws-cdk/aws-iotevents": "0.0.0", - "@aws-cdk/aws-iotfleethub": "0.0.0", - "@aws-cdk/aws-iotsitewise": "0.0.0", - "@aws-cdk/aws-iotthingsgraph": "0.0.0", - "@aws-cdk/aws-iotwireless": "0.0.0", - "@aws-cdk/aws-ivs": "0.0.0", - "@aws-cdk/aws-kendra": "0.0.0", - "@aws-cdk/aws-kinesis": "0.0.0", - "@aws-cdk/aws-kinesisanalytics": "0.0.0", - "@aws-cdk/aws-kinesisanalytics-flink": "0.0.0", - "@aws-cdk/aws-kinesisanalyticsv2": "0.0.0", - "@aws-cdk/aws-kinesisfirehose": "0.0.0", - "@aws-cdk/aws-kinesisfirehose-destinations": "0.0.0", - "@aws-cdk/aws-kinesisvideo": "0.0.0", - "@aws-cdk/aws-kms": "0.0.0", - "@aws-cdk/aws-lakeformation": "0.0.0", - "@aws-cdk/aws-lambda": "0.0.0", - "@aws-cdk/aws-lambda-destinations": "0.0.0", - "@aws-cdk/aws-lambda-event-sources": "0.0.0", - "@aws-cdk/aws-lambda-go": "0.0.0", - "@aws-cdk/aws-lambda-nodejs": "0.0.0", - "@aws-cdk/aws-lambda-python": "0.0.0", - "@aws-cdk/aws-lex": "0.0.0", - "@aws-cdk/aws-licensemanager": "0.0.0", - "@aws-cdk/aws-lightsail": "0.0.0", - "@aws-cdk/aws-location": "0.0.0", - "@aws-cdk/aws-logs": "0.0.0", - "@aws-cdk/aws-logs-destinations": "0.0.0", - "@aws-cdk/aws-lookoutequipment": "0.0.0", - "@aws-cdk/aws-lookoutmetrics": "0.0.0", - "@aws-cdk/aws-lookoutvision": "0.0.0", - "@aws-cdk/aws-macie": "0.0.0", - "@aws-cdk/aws-managedblockchain": "0.0.0", - "@aws-cdk/aws-mediaconnect": "0.0.0", - "@aws-cdk/aws-mediaconvert": "0.0.0", - "@aws-cdk/aws-medialive": "0.0.0", - "@aws-cdk/aws-mediapackage": "0.0.0", - "@aws-cdk/aws-mediastore": "0.0.0", - "@aws-cdk/aws-memorydb": "0.0.0", - "@aws-cdk/aws-msk": "0.0.0", - "@aws-cdk/aws-mwaa": "0.0.0", - "@aws-cdk/aws-neptune": "0.0.0", - "@aws-cdk/aws-networkfirewall": "0.0.0", - "@aws-cdk/aws-networkmanager": "0.0.0", - "@aws-cdk/aws-nimblestudio": "0.0.0", - "@aws-cdk/aws-opensearchservice": "0.0.0", - "@aws-cdk/aws-opsworks": "0.0.0", - "@aws-cdk/aws-opsworkscm": "0.0.0", - "@aws-cdk/aws-panorama": "0.0.0", - "@aws-cdk/aws-pinpoint": "0.0.0", - "@aws-cdk/aws-pinpointemail": "0.0.0", - "@aws-cdk/aws-qldb": "0.0.0", - "@aws-cdk/aws-quicksight": "0.0.0", - "@aws-cdk/aws-ram": "0.0.0", - "@aws-cdk/aws-rds": "0.0.0", - "@aws-cdk/aws-redshift": "0.0.0", - "@aws-cdk/aws-refactorspaces": "0.0.0", - "@aws-cdk/aws-rekognition": "0.0.0", - "@aws-cdk/aws-resiliencehub": "0.0.0", - "@aws-cdk/aws-resourcegroups": "0.0.0", - "@aws-cdk/aws-robomaker": "0.0.0", - "@aws-cdk/aws-route53": "0.0.0", - "@aws-cdk/aws-route53-patterns": "0.0.0", - "@aws-cdk/aws-route53-targets": "0.0.0", - "@aws-cdk/aws-route53recoverycontrol": "0.0.0", - "@aws-cdk/aws-route53recoveryreadiness": "0.0.0", - "@aws-cdk/aws-route53resolver": "0.0.0", - "@aws-cdk/aws-rum": "0.0.0", - "@aws-cdk/aws-s3": "0.0.0", - "@aws-cdk/aws-s3-assets": "0.0.0", - "@aws-cdk/aws-s3-deployment": "0.0.0", - "@aws-cdk/aws-s3-notifications": "0.0.0", - "@aws-cdk/aws-s3objectlambda": "0.0.0", - "@aws-cdk/aws-s3outposts": "0.0.0", - "@aws-cdk/aws-sagemaker": "0.0.0", - "@aws-cdk/aws-sam": "0.0.0", - "@aws-cdk/aws-sdb": "0.0.0", - "@aws-cdk/aws-secretsmanager": "0.0.0", - "@aws-cdk/aws-securityhub": "0.0.0", - "@aws-cdk/aws-servicecatalog": "0.0.0", - "@aws-cdk/aws-servicecatalogappregistry": "0.0.0", - "@aws-cdk/aws-servicediscovery": "0.0.0", - "@aws-cdk/aws-ses": "0.0.0", - "@aws-cdk/aws-ses-actions": "0.0.0", - "@aws-cdk/aws-signer": "0.0.0", - "@aws-cdk/aws-sns": "0.0.0", - "@aws-cdk/aws-sns-subscriptions": "0.0.0", - "@aws-cdk/aws-sqs": "0.0.0", - "@aws-cdk/aws-ssm": "0.0.0", - "@aws-cdk/aws-ssmcontacts": "0.0.0", - "@aws-cdk/aws-ssmincidents": "0.0.0", - "@aws-cdk/aws-sso": "0.0.0", - "@aws-cdk/aws-stepfunctions": "0.0.0", - "@aws-cdk/aws-stepfunctions-tasks": "0.0.0", - "@aws-cdk/aws-synthetics": "0.0.0", - "@aws-cdk/aws-timestream": "0.0.0", - "@aws-cdk/aws-transfer": "0.0.0", - "@aws-cdk/aws-waf": "0.0.0", - "@aws-cdk/aws-wafregional": "0.0.0", - "@aws-cdk/aws-wafv2": "0.0.0", - "@aws-cdk/aws-wisdom": "0.0.0", - "@aws-cdk/aws-workspaces": "0.0.0", - "@aws-cdk/aws-xray": "0.0.0", - "@aws-cdk/cfnspec": "0.0.0", - "@aws-cdk/cloud-assembly-schema": "0.0.0", - "@aws-cdk/core": "0.0.0", - "@aws-cdk/custom-resources": "0.0.0", - "@aws-cdk/cx-api": "0.0.0", - "@aws-cdk/lambda-layer-awscli": "0.0.0", - "@aws-cdk/lambda-layer-kubectl": "0.0.0", - "@aws-cdk/lambda-layer-node-proxy-agent": "0.0.0", - "@aws-cdk/pipelines": "0.0.0", - "@aws-cdk/region-info": "0.0.0", - "constructs": "^3.3.69", - "fs-extra": "^9.1.0", - "jsii-reflect": "^1.52.1", - "jsonschema": "^1.4.0", - "yaml": "1.10.2", - "yargs": "^16.2.0" - }, - "devDependencies": { - "@types/fs-extra": "^8.1.2", - "@types/jest": "^27.4.0", - "@types/yaml": "1.9.7", - "@types/yargs": "^15.0.14", - "jest": "^27.4.7", - "jsii": "^1.52.1" - }, - "keywords": [ - "aws", - "cdk" - ], - "homepage": "https://github.com/aws/aws-cdk", - "engines": { - "node": ">= 8.10.0" - } -} diff --git a/packages/decdk/test/__snapshots__/synth.test.js.snap b/packages/decdk/test/__snapshots__/synth.test.js.snap deleted file mode 100644 index 93c4dfdf336c4..0000000000000 --- a/packages/decdk/test/__snapshots__/synth.test.js.snap +++ /dev/null @@ -1,3254 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`apigw.json: apigw 1`] = ` -Object { - "Outputs": Object { - "MyApiEndpoint869ABE96": Object { - "Value": Object { - "Fn::Join": Array [ - "", - Array [ - "https://", - Object { - "Ref": "MyApi49610EDF", - }, - ".execute-api.", - Object { - "Ref": "AWS::Region", - }, - ".", - Object { - "Ref": "AWS::URLSuffix", - }, - "/", - Object { - "Ref": "MyApiDeploymentStageprodE1054AF0", - }, - "/", - ], - ], - }, - }, - }, - "Parameters": Object { - "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cArtifactHash47CFB54E": Object { - "Description": "Artifact hash for asset \\"a6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30c\\"", - "Type": "String", - }, - "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3Bucket0D05C9AA": Object { - "Description": "S3 bucket for asset \\"a6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30c\\"", - "Type": "String", - }, - "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3VersionKeyDC0BC7B3": Object { - "Description": "S3 key for asset version \\"a6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30c\\"", - "Type": "String", - }, - }, - "Resources": Object { - "GetRootA9424890": Object { - "Properties": Object { - "AuthorizationType": "NONE", - "HttpMethod": "GET", - "Integration": Object { - "IntegrationHttpMethod": "POST", - "Type": "AWS_PROXY", - "Uri": Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":apigateway:", - Object { - "Ref": "AWS::Region", - }, - ":lambda:path/2015-03-31/functions/", - Object { - "Fn::GetAtt": Array [ - "HelloLambda3D9C82D6", - "Arn", - ], - }, - "/invocations", - ], - ], - }, - }, - "ResourceId": Object { - "Fn::GetAtt": Array [ - "MyApi49610EDF", - "RootResourceId", - ], - }, - "RestApiId": Object { - "Ref": "MyApi49610EDF", - }, - }, - "Type": "AWS::ApiGateway::Method", - }, - "GetRootApiPermissionTestapigwMyApiCBFBC5B0GET5B244F0C": Object { - "Properties": Object { - "Action": "lambda:InvokeFunction", - "FunctionName": Object { - "Fn::GetAtt": Array [ - "HelloLambda3D9C82D6", - "Arn", - ], - }, - "Principal": "apigateway.amazonaws.com", - "SourceArn": Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":execute-api:", - Object { - "Ref": "AWS::Region", - }, - ":", - Object { - "Ref": "AWS::AccountId", - }, - ":", - Object { - "Ref": "MyApi49610EDF", - }, - "/test-invoke-stage/GET/", - ], - ], - }, - }, - "Type": "AWS::Lambda::Permission", - }, - "GetRootApiPermissionapigwMyApiCBFBC5B0GETC314E626": Object { - "Properties": Object { - "Action": "lambda:InvokeFunction", - "FunctionName": Object { - "Fn::GetAtt": Array [ - "HelloLambda3D9C82D6", - "Arn", - ], - }, - "Principal": "apigateway.amazonaws.com", - "SourceArn": Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":execute-api:", - Object { - "Ref": "AWS::Region", - }, - ":", - Object { - "Ref": "AWS::AccountId", - }, - ":", - Object { - "Ref": "MyApi49610EDF", - }, - "/", - Object { - "Ref": "MyApiDeploymentStageprodE1054AF0", - }, - "/GET/", - ], - ], - }, - }, - "Type": "AWS::Lambda::Permission", - }, - "HelloLambda3D9C82D6": Object { - "DependsOn": Array [ - "HelloLambdaServiceRoleE071F162", - ], - "Properties": Object { - "Code": Object { - "S3Bucket": Object { - "Ref": "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3Bucket0D05C9AA", - }, - "S3Key": Object { - "Fn::Join": Array [ - "", - Array [ - Object { - "Fn::Select": Array [ - 0, - Object { - "Fn::Split": Array [ - "||", - Object { - "Ref": "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3VersionKeyDC0BC7B3", - }, - ], - }, - ], - }, - Object { - "Fn::Select": Array [ - 1, - Object { - "Fn::Split": Array [ - "||", - Object { - "Ref": "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3VersionKeyDC0BC7B3", - }, - ], - }, - ], - }, - ], - ], - }, - }, - "Handler": "index.handler", - "Role": Object { - "Fn::GetAtt": Array [ - "HelloLambdaServiceRoleE071F162", - "Arn", - ], - }, - "Runtime": "python3.6", - }, - "Type": "AWS::Lambda::Function", - }, - "HelloLambdaServiceRoleE071F162": Object { - "Properties": Object { - "AssumeRolePolicyDocument": Object { - "Statement": Array [ - Object { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": Object { - "Service": "lambda.amazonaws.com", - }, - }, - ], - "Version": "2012-10-17", - }, - "ManagedPolicyArns": Array [ - Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", - ], - ], - }, - ], - }, - "Type": "AWS::IAM::Role", - }, - "MyApi49610EDF": Object { - "Properties": Object { - "Name": "MyApi", - }, - "Type": "AWS::ApiGateway::RestApi", - }, - "MyApiANYApiPermissionTestapigwMyApiCBFBC5B0ANYAD86D377": Object { - "Properties": Object { - "Action": "lambda:InvokeFunction", - "FunctionName": Object { - "Fn::GetAtt": Array [ - "HelloLambda3D9C82D6", - "Arn", - ], - }, - "Principal": "apigateway.amazonaws.com", - "SourceArn": Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":execute-api:", - Object { - "Ref": "AWS::Region", - }, - ":", - Object { - "Ref": "AWS::AccountId", - }, - ":", - Object { - "Ref": "MyApi49610EDF", - }, - "/test-invoke-stage/*/", - ], - ], - }, - }, - "Type": "AWS::Lambda::Permission", - }, - "MyApiANYApiPermissionapigwMyApiCBFBC5B0ANY5BBED348": Object { - "Properties": Object { - "Action": "lambda:InvokeFunction", - "FunctionName": Object { - "Fn::GetAtt": Array [ - "HelloLambda3D9C82D6", - "Arn", - ], - }, - "Principal": "apigateway.amazonaws.com", - "SourceArn": Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":execute-api:", - Object { - "Ref": "AWS::Region", - }, - ":", - Object { - "Ref": "AWS::AccountId", - }, - ":", - Object { - "Ref": "MyApi49610EDF", - }, - "/", - Object { - "Ref": "MyApiDeploymentStageprodE1054AF0", - }, - "/*/", - ], - ], - }, - }, - "Type": "AWS::Lambda::Permission", - }, - "MyApiANYEC3618D9": Object { - "Properties": Object { - "AuthorizationType": "NONE", - "HttpMethod": "ANY", - "Integration": Object { - "IntegrationHttpMethod": "POST", - "Type": "AWS_PROXY", - "Uri": Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":apigateway:", - Object { - "Ref": "AWS::Region", - }, - ":lambda:path/2015-03-31/functions/", - Object { - "Fn::GetAtt": Array [ - "HelloLambda3D9C82D6", - "Arn", - ], - }, - "/invocations", - ], - ], - }, - }, - "ResourceId": Object { - "Fn::GetAtt": Array [ - "MyApi49610EDF", - "RootResourceId", - ], - }, - "RestApiId": Object { - "Ref": "MyApi49610EDF", - }, - }, - "Type": "AWS::ApiGateway::Method", - }, - "MyApiAccount13882D84": Object { - "DependsOn": Array [ - "MyApi49610EDF", - ], - "Properties": Object { - "CloudWatchRoleArn": Object { - "Fn::GetAtt": Array [ - "MyApiCloudWatchRole2BEC1A9C", - "Arn", - ], - }, - }, - "Type": "AWS::ApiGateway::Account", - }, - "MyApiCloudWatchRole2BEC1A9C": Object { - "Properties": Object { - "AssumeRolePolicyDocument": Object { - "Statement": Array [ - Object { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": Object { - "Service": "apigateway.amazonaws.com", - }, - }, - ], - "Version": "2012-10-17", - }, - "ManagedPolicyArns": Array [ - Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs", - ], - ], - }, - ], - }, - "Type": "AWS::IAM::Role", - }, - "MyApiDeploymentECB0D05E0597cc4592870e54c401cccc6090bd86": Object { - "DependsOn": Array [ - "GetRootA9424890", - "MyApiproxyANYFCF46C66", - "MyApiproxyE14DBEA4", - "MyApiANYEC3618D9", - ], - "Properties": Object { - "Description": "Automatically created by the RestApi construct", - "RestApiId": Object { - "Ref": "MyApi49610EDF", - }, - }, - "Type": "AWS::ApiGateway::Deployment", - }, - "MyApiDeploymentStageprodE1054AF0": Object { - "DependsOn": Array [ - "MyApiAccount13882D84", - ], - "Properties": Object { - "DeploymentId": Object { - "Ref": "MyApiDeploymentECB0D05E0597cc4592870e54c401cccc6090bd86", - }, - "RestApiId": Object { - "Ref": "MyApi49610EDF", - }, - "StageName": "prod", - }, - "Type": "AWS::ApiGateway::Stage", - }, - "MyApiproxyANYApiPermissionTestapigwMyApiCBFBC5B0ANYproxy6DC68FBB": Object { - "Properties": Object { - "Action": "lambda:InvokeFunction", - "FunctionName": Object { - "Fn::GetAtt": Array [ - "HelloLambda3D9C82D6", - "Arn", - ], - }, - "Principal": "apigateway.amazonaws.com", - "SourceArn": Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":execute-api:", - Object { - "Ref": "AWS::Region", - }, - ":", - Object { - "Ref": "AWS::AccountId", - }, - ":", - Object { - "Ref": "MyApi49610EDF", - }, - "/test-invoke-stage/*/*", - ], - ], - }, - }, - "Type": "AWS::Lambda::Permission", - }, - "MyApiproxyANYApiPermissionapigwMyApiCBFBC5B0ANYproxyB3375D73": Object { - "Properties": Object { - "Action": "lambda:InvokeFunction", - "FunctionName": Object { - "Fn::GetAtt": Array [ - "HelloLambda3D9C82D6", - "Arn", - ], - }, - "Principal": "apigateway.amazonaws.com", - "SourceArn": Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":execute-api:", - Object { - "Ref": "AWS::Region", - }, - ":", - Object { - "Ref": "AWS::AccountId", - }, - ":", - Object { - "Ref": "MyApi49610EDF", - }, - "/", - Object { - "Ref": "MyApiDeploymentStageprodE1054AF0", - }, - "/*/*", - ], - ], - }, - }, - "Type": "AWS::Lambda::Permission", - }, - "MyApiproxyANYFCF46C66": Object { - "Properties": Object { - "AuthorizationType": "NONE", - "HttpMethod": "ANY", - "Integration": Object { - "IntegrationHttpMethod": "POST", - "Type": "AWS_PROXY", - "Uri": Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":apigateway:", - Object { - "Ref": "AWS::Region", - }, - ":lambda:path/2015-03-31/functions/", - Object { - "Fn::GetAtt": Array [ - "HelloLambda3D9C82D6", - "Arn", - ], - }, - "/invocations", - ], - ], - }, - }, - "ResourceId": Object { - "Ref": "MyApiproxyE14DBEA4", - }, - "RestApiId": Object { - "Ref": "MyApi49610EDF", - }, - }, - "Type": "AWS::ApiGateway::Method", - }, - "MyApiproxyE14DBEA4": Object { - "Properties": Object { - "ParentId": Object { - "Fn::GetAtt": Array [ - "MyApi49610EDF", - "RootResourceId", - ], - }, - "PathPart": "{proxy+}", - "RestApiId": Object { - "Ref": "MyApi49610EDF", - }, - }, - "Type": "AWS::ApiGateway::Resource", - }, - }, -} -`; - -exports[`ecs.json: ecs 1`] = ` -Object { - "Resources": Object { - "ClusterEB0386A7": Object { - "Type": "AWS::ECS::Cluster", - }, - "MyTaskDef01F0D39B": Object { - "Properties": Object { - "ContainerDefinitions": Array [ - Object { - "Essential": true, - "Image": "redis", - "Memory": 1024, - "Name": "ContainerDef", - }, - ], - "Cpu": "1024", - "Family": "redis", - "Memory": "1GB", - "NetworkMode": "awsvpc", - "RequiresCompatibilities": Array [ - "FARGATE", - ], - "TaskRoleArn": Object { - "Fn::GetAtt": Array [ - "MyTaskDefTaskRole727F9D3B", - "Arn", - ], - }, - }, - "Type": "AWS::ECS::TaskDefinition", - }, - "MyTaskDefTaskRole727F9D3B": Object { - "Properties": Object { - "AssumeRolePolicyDocument": Object { - "Statement": Array [ - Object { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": Object { - "Service": "ecs-tasks.amazonaws.com", - }, - }, - ], - "Version": "2012-10-17", - }, - }, - "Type": "AWS::IAM::Role", - }, - "ServiceD69D759B": Object { - "Properties": Object { - "Cluster": Object { - "Ref": "ClusterEB0386A7", - }, - "DeploymentConfiguration": Object { - "MaximumPercent": 200, - "MinimumHealthyPercent": 50, - }, - "EnableECSManagedTags": false, - "LaunchType": "FARGATE", - "NetworkConfiguration": Object { - "AwsvpcConfiguration": Object { - "AssignPublicIp": "DISABLED", - "SecurityGroups": Array [ - Object { - "Fn::GetAtt": Array [ - "ServiceSecurityGroupC96ED6A7", - "GroupId", - ], - }, - ], - "Subnets": Array [ - Object { - "Ref": "VPCPrivateSubnet1Subnet8BCA10E0", - }, - ], - }, - }, - "TaskDefinition": Object { - "Ref": "MyTaskDef01F0D39B", - }, - }, - "Type": "AWS::ECS::Service", - }, - "ServiceSecurityGroupC96ED6A7": Object { - "Properties": Object { - "GroupDescription": "ecs/Service/SecurityGroup", - "SecurityGroupEgress": Array [ - Object { - "CidrIp": "0.0.0.0/0", - "Description": "Allow all outbound traffic by default", - "IpProtocol": "-1", - }, - ], - "VpcId": Object { - "Ref": "VPCB9E5F0B4", - }, - }, - "Type": "AWS::EC2::SecurityGroup", - }, - "VPCB9E5F0B4": Object { - "Properties": Object { - "CidrBlock": "10.0.0.0/16", - "EnableDnsHostnames": true, - "EnableDnsSupport": true, - "InstanceTenancy": "default", - "Tags": Array [ - Object { - "Key": "Name", - "Value": "ecs/VPC", - }, - ], - }, - "Type": "AWS::EC2::VPC", - }, - "VPCIGWB7E252D3": Object { - "Properties": Object { - "Tags": Array [ - Object { - "Key": "Name", - "Value": "ecs/VPC", - }, - ], - }, - "Type": "AWS::EC2::InternetGateway", - }, - "VPCPrivateSubnet1DefaultRouteAE1D6490": Object { - "Properties": Object { - "DestinationCidrBlock": "0.0.0.0/0", - "NatGatewayId": Object { - "Ref": "VPCPublicSubnet1NATGatewayE0556630", - }, - "RouteTableId": Object { - "Ref": "VPCPrivateSubnet1RouteTableBE8A6027", - }, - }, - "Type": "AWS::EC2::Route", - }, - "VPCPrivateSubnet1RouteTableAssociation347902D1": Object { - "Properties": Object { - "RouteTableId": Object { - "Ref": "VPCPrivateSubnet1RouteTableBE8A6027", - }, - "SubnetId": Object { - "Ref": "VPCPrivateSubnet1Subnet8BCA10E0", - }, - }, - "Type": "AWS::EC2::SubnetRouteTableAssociation", - }, - "VPCPrivateSubnet1RouteTableBE8A6027": Object { - "Properties": Object { - "Tags": Array [ - Object { - "Key": "Name", - "Value": "ecs/VPC/PrivateSubnet1", - }, - ], - "VpcId": Object { - "Ref": "VPCB9E5F0B4", - }, - }, - "Type": "AWS::EC2::RouteTable", - }, - "VPCPrivateSubnet1Subnet8BCA10E0": Object { - "Properties": Object { - "AvailabilityZone": Object { - "Fn::Select": Array [ - 0, - Object { - "Fn::GetAZs": "", - }, - ], - }, - "CidrBlock": "10.0.128.0/17", - "MapPublicIpOnLaunch": false, - "Tags": Array [ - Object { - "Key": "aws-cdk:subnet-name", - "Value": "Private", - }, - Object { - "Key": "aws-cdk:subnet-type", - "Value": "Private", - }, - Object { - "Key": "Name", - "Value": "ecs/VPC/PrivateSubnet1", - }, - ], - "VpcId": Object { - "Ref": "VPCB9E5F0B4", - }, - }, - "Type": "AWS::EC2::Subnet", - }, - "VPCPublicSubnet1DefaultRoute91CEF279": Object { - "DependsOn": Array [ - "VPCVPCGW99B986DC", - ], - "Properties": Object { - "DestinationCidrBlock": "0.0.0.0/0", - "GatewayId": Object { - "Ref": "VPCIGWB7E252D3", - }, - "RouteTableId": Object { - "Ref": "VPCPublicSubnet1RouteTableFEE4B781", - }, - }, - "Type": "AWS::EC2::Route", - }, - "VPCPublicSubnet1EIP6AD938E8": Object { - "Properties": Object { - "Domain": "vpc", - "Tags": Array [ - Object { - "Key": "Name", - "Value": "ecs/VPC/PublicSubnet1", - }, - ], - }, - "Type": "AWS::EC2::EIP", - }, - "VPCPublicSubnet1NATGatewayE0556630": Object { - "Properties": Object { - "AllocationId": Object { - "Fn::GetAtt": Array [ - "VPCPublicSubnet1EIP6AD938E8", - "AllocationId", - ], - }, - "SubnetId": Object { - "Ref": "VPCPublicSubnet1SubnetB4246D30", - }, - "Tags": Array [ - Object { - "Key": "Name", - "Value": "ecs/VPC/PublicSubnet1", - }, - ], - }, - "Type": "AWS::EC2::NatGateway", - }, - "VPCPublicSubnet1RouteTableAssociation0B0896DC": Object { - "Properties": Object { - "RouteTableId": Object { - "Ref": "VPCPublicSubnet1RouteTableFEE4B781", - }, - "SubnetId": Object { - "Ref": "VPCPublicSubnet1SubnetB4246D30", - }, - }, - "Type": "AWS::EC2::SubnetRouteTableAssociation", - }, - "VPCPublicSubnet1RouteTableFEE4B781": Object { - "Properties": Object { - "Tags": Array [ - Object { - "Key": "Name", - "Value": "ecs/VPC/PublicSubnet1", - }, - ], - "VpcId": Object { - "Ref": "VPCB9E5F0B4", - }, - }, - "Type": "AWS::EC2::RouteTable", - }, - "VPCPublicSubnet1SubnetB4246D30": Object { - "Properties": Object { - "AvailabilityZone": Object { - "Fn::Select": Array [ - 0, - Object { - "Fn::GetAZs": "", - }, - ], - }, - "CidrBlock": "10.0.0.0/17", - "MapPublicIpOnLaunch": true, - "Tags": Array [ - Object { - "Key": "aws-cdk:subnet-name", - "Value": "Public", - }, - Object { - "Key": "aws-cdk:subnet-type", - "Value": "Public", - }, - Object { - "Key": "Name", - "Value": "ecs/VPC/PublicSubnet1", - }, - ], - "VpcId": Object { - "Ref": "VPCB9E5F0B4", - }, - }, - "Type": "AWS::EC2::Subnet", - }, - "VPCVPCGW99B986DC": Object { - "Properties": Object { - "InternetGatewayId": Object { - "Ref": "VPCIGWB7E252D3", - }, - "VpcId": Object { - "Ref": "VPCB9E5F0B4", - }, - }, - "Type": "AWS::EC2::VPCGatewayAttachment", - }, - }, -} -`; - -exports[`lambda-events.json: lambda-events 1`] = ` -Object { - "Outputs": Object { - "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4FEndpointC6202FF3": Object { - "Value": Object { - "Fn::Join": Array [ - "", - Array [ - "https://", - Object { - "Ref": "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4F12449C5F", - }, - ".execute-api.", - Object { - "Ref": "AWS::Region", - }, - ".", - Object { - "Ref": "AWS::URLSuffix", - }, - "/", - Object { - "Ref": "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4FDeploymentStageprod6A86C016", - }, - "/", - ], - ], - }, - }, - }, - "Parameters": Object { - "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cArtifactHash47CFB54E": Object { - "Description": "Artifact hash for asset \\"a6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30c\\"", - "Type": "String", - }, - "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3Bucket0D05C9AA": Object { - "Description": "S3 bucket for asset \\"a6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30c\\"", - "Type": "String", - }, - "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3VersionKeyDC0BC7B3": Object { - "Description": "S3 key for asset version \\"a6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30c\\"", - "Type": "String", - }, - }, - "Resources": Object { - "HelloWorldFunctionAllowInvokelambdaeventsMyTopic988FAB3E43035740": Object { - "Properties": Object { - "Action": "lambda:InvokeFunction", - "FunctionName": Object { - "Fn::GetAtt": Array [ - "HelloWorldFunctionB2AB6E79", - "Arn", - ], - }, - "Principal": "sns.amazonaws.com", - "SourceArn": Object { - "Ref": "MyTopic86869434", - }, - }, - "Type": "AWS::Lambda::Permission", - }, - "HelloWorldFunctionB2AB6E79": Object { - "DependsOn": Array [ - "HelloWorldFunctionServiceRoleDefaultPolicy6CCD7798", - "HelloWorldFunctionServiceRole8E0BD458", - ], - "Properties": Object { - "Code": Object { - "S3Bucket": Object { - "Ref": "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3Bucket0D05C9AA", - }, - "S3Key": Object { - "Fn::Join": Array [ - "", - Array [ - Object { - "Fn::Select": Array [ - 0, - Object { - "Fn::Split": Array [ - "||", - Object { - "Ref": "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3VersionKeyDC0BC7B3", - }, - ], - }, - ], - }, - Object { - "Fn::Select": Array [ - 1, - Object { - "Fn::Split": Array [ - "||", - Object { - "Ref": "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3VersionKeyDC0BC7B3", - }, - ], - }, - ], - }, - ], - ], - }, - }, - "Environment": Object { - "Variables": Object { - "Param": "f", - }, - }, - "Handler": "app.hello_handler", - "Role": Object { - "Fn::GetAtt": Array [ - "HelloWorldFunctionServiceRole8E0BD458", - "Arn", - ], - }, - "Runtime": "python3.6", - }, - "Type": "AWS::Lambda::Function", - }, - "HelloWorldFunctionDynamoDBEventSourcelambdaeventsTableF97C65E3DE5C745C": Object { - "Properties": Object { - "BatchSize": 100, - "EventSourceArn": Object { - "Fn::GetAtt": Array [ - "TableCD117FA1", - "StreamArn", - ], - }, - "FunctionName": Object { - "Ref": "HelloWorldFunctionB2AB6E79", - }, - "StartingPosition": "TRIM_HORIZON", - }, - "Type": "AWS::Lambda::EventSourceMapping", - }, - "HelloWorldFunctionMyTopic90F6E0EF": Object { - "Properties": Object { - "Endpoint": Object { - "Fn::GetAtt": Array [ - "HelloWorldFunctionB2AB6E79", - "Arn", - ], - }, - "Protocol": "lambda", - "TopicArn": Object { - "Ref": "MyTopic86869434", - }, - }, - "Type": "AWS::SNS::Subscription", - }, - "HelloWorldFunctionServiceRole8E0BD458": Object { - "Properties": Object { - "AssumeRolePolicyDocument": Object { - "Statement": Array [ - Object { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": Object { - "Service": "lambda.amazonaws.com", - }, - }, - ], - "Version": "2012-10-17", - }, - "ManagedPolicyArns": Array [ - Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", - ], - ], - }, - ], - }, - "Type": "AWS::IAM::Role", - }, - "HelloWorldFunctionServiceRoleDefaultPolicy6CCD7798": Object { - "Properties": Object { - "PolicyDocument": Object { - "Statement": Array [ - Object { - "Action": "dynamodb:ListStreams", - "Effect": "Allow", - "Resource": "*", - }, - Object { - "Action": Array [ - "dynamodb:DescribeStream", - "dynamodb:GetRecords", - "dynamodb:GetShardIterator", - ], - "Effect": "Allow", - "Resource": Object { - "Fn::GetAtt": Array [ - "TableCD117FA1", - "StreamArn", - ], - }, - }, - ], - "Version": "2012-10-17", - }, - "PolicyName": "HelloWorldFunctionServiceRoleDefaultPolicy6CCD7798", - "Roles": Array [ - Object { - "Ref": "HelloWorldFunctionServiceRole8E0BD458", - }, - ], - }, - "Type": "AWS::IAM::Policy", - }, - "MyTopic86869434": Object { - "Type": "AWS::SNS::Topic", - }, - "TableCD117FA1": Object { - "DeletionPolicy": "Retain", - "Properties": Object { - "AttributeDefinitions": Array [ - Object { - "AttributeName": "ID", - "AttributeType": "S", - }, - ], - "KeySchema": Array [ - Object { - "AttributeName": "ID", - "KeyType": "HASH", - }, - ], - "ProvisionedThroughput": Object { - "ReadCapacityUnits": 5, - "WriteCapacityUnits": 5, - }, - "StreamSpecification": Object { - "StreamViewType": "NEW_AND_OLD_IMAGES", - }, - }, - "Type": "AWS::DynamoDB::Table", - "UpdateReplacePolicy": "Retain", - }, - "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4F12449C5F": Object { - "Properties": Object { - "Name": "lambdaeventsHelloWorldFunctionAB27BB65:ApiEventSourceA7A86A4F", - }, - "Type": "AWS::ApiGateway::RestApi", - }, - "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4FAccountF7734F1E": Object { - "DependsOn": Array [ - "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4F12449C5F", - ], - "Properties": Object { - "CloudWatchRoleArn": Object { - "Fn::GetAtt": Array [ - "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4FCloudWatchRole495FDB9D", - "Arn", - ], - }, - }, - "Type": "AWS::ApiGateway::Account", - }, - "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4FCloudWatchRole495FDB9D": Object { - "Properties": Object { - "AssumeRolePolicyDocument": Object { - "Statement": Array [ - Object { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": Object { - "Service": "apigateway.amazonaws.com", - }, - }, - ], - "Version": "2012-10-17", - }, - "ManagedPolicyArns": Array [ - Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs", - ], - ], - }, - ], - }, - "Type": "AWS::IAM::Role", - }, - "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4FDeploymentFF3F4A1Ac00dde791d2719be3e8ea69f9a61a5cd": Object { - "DependsOn": Array [ - "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4FhelloGET04FBC7F6", - "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4FhelloPOST53F177B1", - "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4FhelloC30B3CD9", - ], - "Properties": Object { - "Description": "Automatically created by the RestApi construct", - "RestApiId": Object { - "Ref": "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4F12449C5F", - }, - }, - "Type": "AWS::ApiGateway::Deployment", - }, - "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4FDeploymentStageprod6A86C016": Object { - "DependsOn": Array [ - "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4FAccountF7734F1E", - ], - "Properties": Object { - "DeploymentId": Object { - "Ref": "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4FDeploymentFF3F4A1Ac00dde791d2719be3e8ea69f9a61a5cd", - }, - "RestApiId": Object { - "Ref": "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4F12449C5F", - }, - "StageName": "prod", - }, - "Type": "AWS::ApiGateway::Stage", - }, - "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4FhelloC30B3CD9": Object { - "Properties": Object { - "ParentId": Object { - "Fn::GetAtt": Array [ - "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4F12449C5F", - "RootResourceId", - ], - }, - "PathPart": "hello", - "RestApiId": Object { - "Ref": "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4F12449C5F", - }, - }, - "Type": "AWS::ApiGateway::Resource", - }, - "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4FhelloGET04FBC7F6": Object { - "Properties": Object { - "AuthorizationType": "NONE", - "HttpMethod": "GET", - "Integration": Object { - "IntegrationHttpMethod": "POST", - "Type": "AWS_PROXY", - "Uri": Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":apigateway:", - Object { - "Ref": "AWS::Region", - }, - ":lambda:path/2015-03-31/functions/", - Object { - "Fn::GetAtt": Array [ - "HelloWorldFunctionB2AB6E79", - "Arn", - ], - }, - "/invocations", - ], - ], - }, - }, - "ResourceId": Object { - "Ref": "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4FhelloC30B3CD9", - }, - "RestApiId": Object { - "Ref": "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4F12449C5F", - }, - }, - "Type": "AWS::ApiGateway::Method", - }, - "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4FhelloGETApiPermissionTestlambdaeventslambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4F0AAEF724GEThello5EA12CF5": Object { - "Properties": Object { - "Action": "lambda:InvokeFunction", - "FunctionName": Object { - "Fn::GetAtt": Array [ - "HelloWorldFunctionB2AB6E79", - "Arn", - ], - }, - "Principal": "apigateway.amazonaws.com", - "SourceArn": Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":execute-api:", - Object { - "Ref": "AWS::Region", - }, - ":", - Object { - "Ref": "AWS::AccountId", - }, - ":", - Object { - "Ref": "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4F12449C5F", - }, - "/test-invoke-stage/GET/hello", - ], - ], - }, - }, - "Type": "AWS::Lambda::Permission", - }, - "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4FhelloGETApiPermissionlambdaeventslambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4F0AAEF724GEThelloAC4AB5E4": Object { - "Properties": Object { - "Action": "lambda:InvokeFunction", - "FunctionName": Object { - "Fn::GetAtt": Array [ - "HelloWorldFunctionB2AB6E79", - "Arn", - ], - }, - "Principal": "apigateway.amazonaws.com", - "SourceArn": Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":execute-api:", - Object { - "Ref": "AWS::Region", - }, - ":", - Object { - "Ref": "AWS::AccountId", - }, - ":", - Object { - "Ref": "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4F12449C5F", - }, - "/", - Object { - "Ref": "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4FDeploymentStageprod6A86C016", - }, - "/GET/hello", - ], - ], - }, - }, - "Type": "AWS::Lambda::Permission", - }, - "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4FhelloPOST53F177B1": Object { - "Properties": Object { - "AuthorizationType": "NONE", - "HttpMethod": "POST", - "Integration": Object { - "IntegrationHttpMethod": "POST", - "Type": "AWS_PROXY", - "Uri": Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":apigateway:", - Object { - "Ref": "AWS::Region", - }, - ":lambda:path/2015-03-31/functions/", - Object { - "Fn::GetAtt": Array [ - "HelloWorldFunctionB2AB6E79", - "Arn", - ], - }, - "/invocations", - ], - ], - }, - }, - "ResourceId": Object { - "Ref": "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4FhelloC30B3CD9", - }, - "RestApiId": Object { - "Ref": "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4F12449C5F", - }, - }, - "Type": "AWS::ApiGateway::Method", - }, - "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4FhelloPOSTApiPermissionTestlambdaeventslambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4F0AAEF724POSThello991892DD": Object { - "Properties": Object { - "Action": "lambda:InvokeFunction", - "FunctionName": Object { - "Fn::GetAtt": Array [ - "HelloWorldFunctionB2AB6E79", - "Arn", - ], - }, - "Principal": "apigateway.amazonaws.com", - "SourceArn": Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":execute-api:", - Object { - "Ref": "AWS::Region", - }, - ":", - Object { - "Ref": "AWS::AccountId", - }, - ":", - Object { - "Ref": "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4F12449C5F", - }, - "/test-invoke-stage/POST/hello", - ], - ], - }, - }, - "Type": "AWS::Lambda::Permission", - }, - "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4FhelloPOSTApiPermissionlambdaeventslambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4F0AAEF724POSThello3501FACF": Object { - "Properties": Object { - "Action": "lambda:InvokeFunction", - "FunctionName": Object { - "Fn::GetAtt": Array [ - "HelloWorldFunctionB2AB6E79", - "Arn", - ], - }, - "Principal": "apigateway.amazonaws.com", - "SourceArn": Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":execute-api:", - Object { - "Ref": "AWS::Region", - }, - ":", - Object { - "Ref": "AWS::AccountId", - }, - ":", - Object { - "Ref": "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4F12449C5F", - }, - "/", - Object { - "Ref": "lambdaeventsHelloWorldFunctionAB27BB65ApiEventSourceA7A86A4FDeploymentStageprod6A86C016", - }, - "/POST/hello", - ], - ], - }, - }, - "Type": "AWS::Lambda::Permission", - }, - }, -} -`; - -exports[`lambda-topic.json: lambda-topic 1`] = ` -Object { - "Parameters": Object { - "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cArtifactHash47CFB54E": Object { - "Description": "Artifact hash for asset \\"a6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30c\\"", - "Type": "String", - }, - "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3Bucket0D05C9AA": Object { - "Description": "S3 bucket for asset \\"a6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30c\\"", - "Type": "String", - }, - "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3VersionKeyDC0BC7B3": Object { - "Description": "S3 key for asset version \\"a6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30c\\"", - "Type": "String", - }, - }, - "Resources": Object { - "LambdaAllowInvokelambdatopicTopic702108A3CE1309A3": Object { - "Properties": Object { - "Action": "lambda:InvokeFunction", - "FunctionName": Object { - "Fn::GetAtt": Array [ - "LambdaD247545B", - "Arn", - ], - }, - "Principal": "sns.amazonaws.com", - "SourceArn": Object { - "Ref": "TopicBFC7AF6E", - }, - }, - "Type": "AWS::Lambda::Permission", - }, - "LambdaD247545B": Object { - "DependsOn": Array [ - "LambdaServiceRoleA8ED4D3B", - ], - "Properties": Object { - "Code": Object { - "S3Bucket": Object { - "Ref": "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3Bucket0D05C9AA", - }, - "S3Key": Object { - "Fn::Join": Array [ - "", - Array [ - Object { - "Fn::Select": Array [ - 0, - Object { - "Fn::Split": Array [ - "||", - Object { - "Ref": "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3VersionKeyDC0BC7B3", - }, - ], - }, - ], - }, - Object { - "Fn::Select": Array [ - 1, - Object { - "Fn::Split": Array [ - "||", - Object { - "Ref": "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3VersionKeyDC0BC7B3", - }, - ], - }, - ], - }, - ], - ], - }, - }, - "Handler": "index.handler", - "Role": Object { - "Fn::GetAtt": Array [ - "LambdaServiceRoleA8ED4D3B", - "Arn", - ], - }, - "Runtime": "nodejs", - }, - "Type": "AWS::Lambda::Function", - }, - "LambdaServiceRoleA8ED4D3B": Object { - "Properties": Object { - "AssumeRolePolicyDocument": Object { - "Statement": Array [ - Object { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": Object { - "Service": "lambda.amazonaws.com", - }, - }, - ], - "Version": "2012-10-17", - }, - "ManagedPolicyArns": Array [ - Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", - ], - ], - }, - ], - }, - "Type": "AWS::IAM::Role", - }, - "LambdaTopicCCB0C2A3": Object { - "Properties": Object { - "Endpoint": Object { - "Fn::GetAtt": Array [ - "LambdaD247545B", - "Arn", - ], - }, - "Protocol": "lambda", - "TopicArn": Object { - "Ref": "TopicBFC7AF6E", - }, - }, - "Type": "AWS::SNS::Subscription", - }, - "TopicBFC7AF6E": Object { - "Type": "AWS::SNS::Topic", - }, - }, -} -`; - -exports[`pipeline.json: pipeline 1`] = ` -Object { - "Resources": Object { - "BuildProject097C5DB7": Object { - "Properties": Object { - "Artifacts": Object { - "Type": "CODEPIPELINE", - }, - "Cache": Object { - "Type": "NO_CACHE", - }, - "EncryptionKey": Object { - "Fn::GetAtt": Array [ - "Key961B73FD", - "Arn", - ], - }, - "Environment": Object { - "ComputeType": "BUILD_GENERAL1_SMALL", - "Image": "aws/codebuild/standard:1.0", - "ImagePullCredentialsType": "CODEBUILD", - "PrivilegedMode": false, - "Type": "LINUX_CONTAINER", - }, - "ServiceRole": Object { - "Fn::GetAtt": Array [ - "BuildProjectRoleAA92C755", - "Arn", - ], - }, - "Source": Object { - "Type": "CODEPIPELINE", - }, - }, - "Type": "AWS::CodeBuild::Project", - }, - "BuildProjectRoleAA92C755": Object { - "Properties": Object { - "AssumeRolePolicyDocument": Object { - "Statement": Array [ - Object { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": Object { - "Service": "codebuild.amazonaws.com", - }, - }, - ], - "Version": "2012-10-17", - }, - }, - "Type": "AWS::IAM::Role", - }, - "BuildProjectRoleDefaultPolicy3E9F248C": Object { - "Properties": Object { - "PolicyDocument": Object { - "Statement": Array [ - Object { - "Action": Array [ - "logs:CreateLogGroup", - "logs:CreateLogStream", - "logs:PutLogEvents", - ], - "Effect": "Allow", - "Resource": Array [ - Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":logs:", - Object { - "Ref": "AWS::Region", - }, - ":", - Object { - "Ref": "AWS::AccountId", - }, - ":log-group:/aws/codebuild/", - Object { - "Ref": "BuildProject097C5DB7", - }, - ], - ], - }, - Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":logs:", - Object { - "Ref": "AWS::Region", - }, - ":", - Object { - "Ref": "AWS::AccountId", - }, - ":log-group:/aws/codebuild/", - Object { - "Ref": "BuildProject097C5DB7", - }, - ":*", - ], - ], - }, - ], - }, - Object { - "Action": Array [ - "codebuild:CreateReportGroup", - "codebuild:CreateReport", - "codebuild:UpdateReport", - "codebuild:BatchPutTestCases", - "codebuild:BatchPutCodeCoverages", - ], - "Effect": "Allow", - "Resource": Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":codebuild:", - Object { - "Ref": "AWS::Region", - }, - ":", - Object { - "Ref": "AWS::AccountId", - }, - ":report-group/", - Object { - "Ref": "BuildProject097C5DB7", - }, - "-*", - ], - ], - }, - }, - Object { - "Action": Array [ - "kms:Decrypt", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*", - ], - "Effect": "Allow", - "Resource": Object { - "Fn::GetAtt": Array [ - "Key961B73FD", - "Arn", - ], - }, - }, - Object { - "Action": Array [ - "s3:GetObject*", - "s3:GetBucket*", - "s3:List*", - "s3:DeleteObject*", - "s3:PutObject", - "s3:Abort*", - ], - "Effect": "Allow", - "Resource": Array [ - Object { - "Fn::GetAtt": Array [ - "PipelineArtifactsBucket22248F97", - "Arn", - ], - }, - Object { - "Fn::Join": Array [ - "", - Array [ - Object { - "Fn::GetAtt": Array [ - "PipelineArtifactsBucket22248F97", - "Arn", - ], - }, - "/*", - ], - ], - }, - ], - }, - Object { - "Action": Array [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*", - ], - "Effect": "Allow", - "Resource": Object { - "Fn::GetAtt": Array [ - "PipelineArtifactsBucketEncryptionKey01D58D69", - "Arn", - ], - }, - }, - ], - "Version": "2012-10-17", - }, - "PolicyName": "BuildProjectRoleDefaultPolicy3E9F248C", - "Roles": Array [ - Object { - "Ref": "BuildProjectRoleAA92C755", - }, - ], - }, - "Type": "AWS::IAM::Policy", - }, - "Key961B73FD": Object { - "DeletionPolicy": "Retain", - "Properties": Object { - "KeyPolicy": Object { - "Statement": Array [ - Object { - "Action": "kms:*", - "Effect": "Allow", - "Principal": Object { - "AWS": Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":iam::", - Object { - "Ref": "AWS::AccountId", - }, - ":root", - ], - ], - }, - }, - "Resource": "*", - }, - ], - "Version": "2012-10-17", - }, - }, - "Type": "AWS::KMS::Key", - "UpdateReplacePolicy": "Retain", - }, - "PipelineArtifactsBucket22248F97": Object { - "DeletionPolicy": "Retain", - "Properties": Object { - "BucketEncryption": Object { - "ServerSideEncryptionConfiguration": Array [ - Object { - "ServerSideEncryptionByDefault": Object { - "KMSMasterKeyID": Object { - "Fn::GetAtt": Array [ - "PipelineArtifactsBucketEncryptionKey01D58D69", - "Arn", - ], - }, - "SSEAlgorithm": "aws:kms", - }, - }, - ], - }, - "PublicAccessBlockConfiguration": Object { - "BlockPublicAcls": true, - "BlockPublicPolicy": true, - "IgnorePublicAcls": true, - "RestrictPublicBuckets": true, - }, - }, - "Type": "AWS::S3::Bucket", - "UpdateReplacePolicy": "Retain", - }, - "PipelineArtifactsBucketEncryptionKey01D58D69": Object { - "DeletionPolicy": "Delete", - "Properties": Object { - "KeyPolicy": Object { - "Statement": Array [ - Object { - "Action": "kms:*", - "Effect": "Allow", - "Principal": Object { - "AWS": Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":iam::", - Object { - "Ref": "AWS::AccountId", - }, - ":root", - ], - ], - }, - }, - "Resource": "*", - }, - ], - "Version": "2012-10-17", - }, - }, - "Type": "AWS::KMS::Key", - "UpdateReplacePolicy": "Delete", - }, - "PipelineArtifactsBucketEncryptionKeyAlias5C510EEE": Object { - "DeletionPolicy": "Delete", - "Properties": Object { - "AliasName": "alias/codepipeline-pipelinepipeline22f2a91d", - "TargetKeyId": Object { - "Fn::GetAtt": Array [ - "PipelineArtifactsBucketEncryptionKey01D58D69", - "Arn", - ], - }, - }, - "Type": "AWS::KMS::Alias", - "UpdateReplacePolicy": "Delete", - }, - "PipelineArtifactsBucketPolicyD4F9712A": Object { - "Properties": Object { - "Bucket": Object { - "Ref": "PipelineArtifactsBucket22248F97", - }, - "PolicyDocument": Object { - "Statement": Array [ - Object { - "Action": "s3:*", - "Condition": Object { - "Bool": Object { - "aws:SecureTransport": "false", - }, - }, - "Effect": "Deny", - "Principal": Object { - "AWS": "*", - }, - "Resource": Array [ - Object { - "Fn::GetAtt": Array [ - "PipelineArtifactsBucket22248F97", - "Arn", - ], - }, - Object { - "Fn::Join": Array [ - "", - Array [ - Object { - "Fn::GetAtt": Array [ - "PipelineArtifactsBucket22248F97", - "Arn", - ], - }, - "/*", - ], - ], - }, - ], - }, - ], - "Version": "2012-10-17", - }, - }, - "Type": "AWS::S3::BucketPolicy", - }, - "PipelineBuildCodePipelineActionRoleD77A08E6": Object { - "Properties": Object { - "AssumeRolePolicyDocument": Object { - "Statement": Array [ - Object { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": Object { - "AWS": Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":iam::", - Object { - "Ref": "AWS::AccountId", - }, - ":root", - ], - ], - }, - }, - }, - ], - "Version": "2012-10-17", - }, - }, - "Type": "AWS::IAM::Role", - }, - "PipelineBuildCodePipelineActionRoleDefaultPolicyC9CB73F8": Object { - "Properties": Object { - "PolicyDocument": Object { - "Statement": Array [ - Object { - "Action": Array [ - "codebuild:BatchGetBuilds", - "codebuild:StartBuild", - "codebuild:StopBuild", - ], - "Effect": "Allow", - "Resource": Object { - "Fn::GetAtt": Array [ - "BuildProject097C5DB7", - "Arn", - ], - }, - }, - ], - "Version": "2012-10-17", - }, - "PolicyName": "PipelineBuildCodePipelineActionRoleDefaultPolicyC9CB73F8", - "Roles": Array [ - Object { - "Ref": "PipelineBuildCodePipelineActionRoleD77A08E6", - }, - ], - }, - "Type": "AWS::IAM::Policy", - }, - "PipelineC660917D": Object { - "DependsOn": Array [ - "PipelineRoleDefaultPolicyC7A05455", - "PipelineRoleD68726F7", - ], - "Properties": Object { - "ArtifactStore": Object { - "EncryptionKey": Object { - "Id": Object { - "Fn::GetAtt": Array [ - "PipelineArtifactsBucketEncryptionKey01D58D69", - "Arn", - ], - }, - "Type": "KMS", - }, - "Location": Object { - "Ref": "PipelineArtifactsBucket22248F97", - }, - "Type": "S3", - }, - "RoleArn": Object { - "Fn::GetAtt": Array [ - "PipelineRoleD68726F7", - "Arn", - ], - }, - "Stages": Array [ - Object { - "Actions": Array [ - Object { - "ActionTypeId": Object { - "Category": "Source", - "Owner": "AWS", - "Provider": "CodeCommit", - "Version": "1", - }, - "Configuration": Object { - "BranchName": "master", - "PollForSourceChanges": false, - "RepositoryName": Object { - "Fn::GetAtt": Array [ - "Repo02AC86CF", - "Name", - ], - }, - }, - "Name": "Source", - "OutputArtifacts": Array [ - Object { - "Name": "Source", - }, - ], - "RoleArn": Object { - "Fn::GetAtt": Array [ - "PipelineSourceCodePipelineActionRoleC6F9E7F5", - "Arn", - ], - }, - "RunOrder": 1, - }, - ], - "Name": "Source", - }, - Object { - "Actions": Array [ - Object { - "ActionTypeId": Object { - "Category": "Build", - "Owner": "AWS", - "Provider": "CodeBuild", - "Version": "1", - }, - "Configuration": Object { - "ProjectName": Object { - "Ref": "BuildProject097C5DB7", - }, - }, - "InputArtifacts": Array [ - Object { - "Name": "Source", - }, - ], - "Name": "Build", - "OutputArtifacts": Array [ - Object { - "Name": "Build", - }, - ], - "RoleArn": Object { - "Fn::GetAtt": Array [ - "PipelineBuildCodePipelineActionRoleD77A08E6", - "Arn", - ], - }, - "RunOrder": 1, - }, - ], - "Name": "Build", - }, - Object { - "Actions": Array [ - Object { - "ActionTypeId": Object { - "Category": "Deploy", - "Owner": "AWS", - "Provider": "CloudFormation", - "Version": "1", - }, - "Configuration": Object { - "ActionMode": "CREATE_UPDATE", - "Capabilities": "CAPABILITY_NAMED_IAM", - "RoleArn": Object { - "Fn::GetAtt": Array [ - "PipelineDeployRole97597E3E", - "Arn", - ], - }, - "StackName": "MyStack", - "TemplatePath": "Build::template.yaml", - }, - "InputArtifacts": Array [ - Object { - "Name": "Build", - }, - ], - "Name": "Deploy", - "RoleArn": Object { - "Fn::GetAtt": Array [ - "PipelineDeployCodePipelineActionRole8B83082E", - "Arn", - ], - }, - "RunOrder": 1, - }, - ], - "Name": "Deploy", - }, - ], - }, - "Type": "AWS::CodePipeline::Pipeline", - }, - "PipelineDeployCodePipelineActionRole8B83082E": Object { - "Properties": Object { - "AssumeRolePolicyDocument": Object { - "Statement": Array [ - Object { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": Object { - "AWS": Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":iam::", - Object { - "Ref": "AWS::AccountId", - }, - ":root", - ], - ], - }, - }, - }, - ], - "Version": "2012-10-17", - }, - }, - "Type": "AWS::IAM::Role", - }, - "PipelineDeployCodePipelineActionRoleDefaultPolicyEE6D615B": Object { - "Properties": Object { - "PolicyDocument": Object { - "Statement": Array [ - Object { - "Action": "iam:PassRole", - "Effect": "Allow", - "Resource": Object { - "Fn::GetAtt": Array [ - "PipelineDeployRole97597E3E", - "Arn", - ], - }, - }, - Object { - "Action": Array [ - "s3:GetObject*", - "s3:GetBucket*", - "s3:List*", - ], - "Effect": "Allow", - "Resource": Array [ - Object { - "Fn::GetAtt": Array [ - "PipelineArtifactsBucket22248F97", - "Arn", - ], - }, - Object { - "Fn::Join": Array [ - "", - Array [ - Object { - "Fn::GetAtt": Array [ - "PipelineArtifactsBucket22248F97", - "Arn", - ], - }, - "/*", - ], - ], - }, - ], - }, - Object { - "Action": Array [ - "kms:Decrypt", - "kms:DescribeKey", - ], - "Effect": "Allow", - "Resource": Object { - "Fn::GetAtt": Array [ - "PipelineArtifactsBucketEncryptionKey01D58D69", - "Arn", - ], - }, - }, - Object { - "Action": Array [ - "cloudformation:CreateStack", - "cloudformation:DescribeStack*", - "cloudformation:GetStackPolicy", - "cloudformation:GetTemplate*", - "cloudformation:SetStackPolicy", - "cloudformation:UpdateStack", - "cloudformation:ValidateTemplate", - ], - "Effect": "Allow", - "Resource": Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":cloudformation:", - Object { - "Ref": "AWS::Region", - }, - ":", - Object { - "Ref": "AWS::AccountId", - }, - ":stack/MyStack/*", - ], - ], - }, - }, - ], - "Version": "2012-10-17", - }, - "PolicyName": "PipelineDeployCodePipelineActionRoleDefaultPolicyEE6D615B", - "Roles": Array [ - Object { - "Ref": "PipelineDeployCodePipelineActionRole8B83082E", - }, - ], - }, - "Type": "AWS::IAM::Policy", - }, - "PipelineDeployRole97597E3E": Object { - "Properties": Object { - "AssumeRolePolicyDocument": Object { - "Statement": Array [ - Object { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": Object { - "Service": "cloudformation.amazonaws.com", - }, - }, - ], - "Version": "2012-10-17", - }, - }, - "Type": "AWS::IAM::Role", - }, - "PipelineDeployRoleDefaultPolicy90429F22": Object { - "Properties": Object { - "PolicyDocument": Object { - "Statement": Array [ - Object { - "Action": Array [ - "s3:GetObject*", - "s3:GetBucket*", - "s3:List*", - ], - "Effect": "Allow", - "Resource": Array [ - Object { - "Fn::GetAtt": Array [ - "PipelineArtifactsBucket22248F97", - "Arn", - ], - }, - Object { - "Fn::Join": Array [ - "", - Array [ - Object { - "Fn::GetAtt": Array [ - "PipelineArtifactsBucket22248F97", - "Arn", - ], - }, - "/*", - ], - ], - }, - ], - }, - Object { - "Action": Array [ - "kms:Decrypt", - "kms:DescribeKey", - ], - "Effect": "Allow", - "Resource": Object { - "Fn::GetAtt": Array [ - "PipelineArtifactsBucketEncryptionKey01D58D69", - "Arn", - ], - }, - }, - Object { - "Action": "*", - "Effect": "Allow", - "Resource": "*", - }, - ], - "Version": "2012-10-17", - }, - "PolicyName": "PipelineDeployRoleDefaultPolicy90429F22", - "Roles": Array [ - Object { - "Ref": "PipelineDeployRole97597E3E", - }, - ], - }, - "Type": "AWS::IAM::Policy", - }, - "PipelineEventsRole46BEEA7C": Object { - "Properties": Object { - "AssumeRolePolicyDocument": Object { - "Statement": Array [ - Object { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": Object { - "Service": "events.amazonaws.com", - }, - }, - ], - "Version": "2012-10-17", - }, - }, - "Type": "AWS::IAM::Role", - }, - "PipelineEventsRoleDefaultPolicyFF4FCCE0": Object { - "Properties": Object { - "PolicyDocument": Object { - "Statement": Array [ - Object { - "Action": "codepipeline:StartPipelineExecution", - "Effect": "Allow", - "Resource": Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":codepipeline:", - Object { - "Ref": "AWS::Region", - }, - ":", - Object { - "Ref": "AWS::AccountId", - }, - ":", - Object { - "Ref": "PipelineC660917D", - }, - ], - ], - }, - }, - ], - "Version": "2012-10-17", - }, - "PolicyName": "PipelineEventsRoleDefaultPolicyFF4FCCE0", - "Roles": Array [ - Object { - "Ref": "PipelineEventsRole46BEEA7C", - }, - ], - }, - "Type": "AWS::IAM::Policy", - }, - "PipelineRoleD68726F7": Object { - "Properties": Object { - "AssumeRolePolicyDocument": Object { - "Statement": Array [ - Object { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": Object { - "Service": "codepipeline.amazonaws.com", - }, - }, - ], - "Version": "2012-10-17", - }, - }, - "Type": "AWS::IAM::Role", - }, - "PipelineRoleDefaultPolicyC7A05455": Object { - "Properties": Object { - "PolicyDocument": Object { - "Statement": Array [ - Object { - "Action": Array [ - "s3:GetObject*", - "s3:GetBucket*", - "s3:List*", - "s3:DeleteObject*", - "s3:PutObject", - "s3:Abort*", - ], - "Effect": "Allow", - "Resource": Array [ - Object { - "Fn::GetAtt": Array [ - "PipelineArtifactsBucket22248F97", - "Arn", - ], - }, - Object { - "Fn::Join": Array [ - "", - Array [ - Object { - "Fn::GetAtt": Array [ - "PipelineArtifactsBucket22248F97", - "Arn", - ], - }, - "/*", - ], - ], - }, - ], - }, - Object { - "Action": Array [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*", - ], - "Effect": "Allow", - "Resource": Object { - "Fn::GetAtt": Array [ - "PipelineArtifactsBucketEncryptionKey01D58D69", - "Arn", - ], - }, - }, - Object { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": Object { - "Fn::GetAtt": Array [ - "PipelineSourceCodePipelineActionRoleC6F9E7F5", - "Arn", - ], - }, - }, - Object { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": Object { - "Fn::GetAtt": Array [ - "PipelineBuildCodePipelineActionRoleD77A08E6", - "Arn", - ], - }, - }, - Object { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": Object { - "Fn::GetAtt": Array [ - "PipelineDeployCodePipelineActionRole8B83082E", - "Arn", - ], - }, - }, - ], - "Version": "2012-10-17", - }, - "PolicyName": "PipelineRoleDefaultPolicyC7A05455", - "Roles": Array [ - Object { - "Ref": "PipelineRoleD68726F7", - }, - ], - }, - "Type": "AWS::IAM::Policy", - }, - "PipelineSourceCodePipelineActionRoleC6F9E7F5": Object { - "Properties": Object { - "AssumeRolePolicyDocument": Object { - "Statement": Array [ - Object { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": Object { - "AWS": Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":iam::", - Object { - "Ref": "AWS::AccountId", - }, - ":root", - ], - ], - }, - }, - }, - ], - "Version": "2012-10-17", - }, - }, - "Type": "AWS::IAM::Role", - }, - "PipelineSourceCodePipelineActionRoleDefaultPolicy2D565925": Object { - "Properties": Object { - "PolicyDocument": Object { - "Statement": Array [ - Object { - "Action": Array [ - "s3:GetObject*", - "s3:GetBucket*", - "s3:List*", - "s3:DeleteObject*", - "s3:PutObject", - "s3:Abort*", - ], - "Effect": "Allow", - "Resource": Array [ - Object { - "Fn::GetAtt": Array [ - "PipelineArtifactsBucket22248F97", - "Arn", - ], - }, - Object { - "Fn::Join": Array [ - "", - Array [ - Object { - "Fn::GetAtt": Array [ - "PipelineArtifactsBucket22248F97", - "Arn", - ], - }, - "/*", - ], - ], - }, - ], - }, - Object { - "Action": Array [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*", - ], - "Effect": "Allow", - "Resource": Object { - "Fn::GetAtt": Array [ - "PipelineArtifactsBucketEncryptionKey01D58D69", - "Arn", - ], - }, - }, - Object { - "Action": Array [ - "codecommit:GetBranch", - "codecommit:GetCommit", - "codecommit:UploadArchive", - "codecommit:GetUploadArchiveStatus", - "codecommit:CancelUploadArchive", - ], - "Effect": "Allow", - "Resource": Object { - "Fn::GetAtt": Array [ - "Repo02AC86CF", - "Arn", - ], - }, - }, - ], - "Version": "2012-10-17", - }, - "PolicyName": "PipelineSourceCodePipelineActionRoleDefaultPolicy2D565925", - "Roles": Array [ - Object { - "Ref": "PipelineSourceCodePipelineActionRoleC6F9E7F5", - }, - ], - }, - "Type": "AWS::IAM::Policy", - }, - "Repo02AC86CF": Object { - "Properties": Object { - "RepositoryName": "my-first-decdk-repo", - }, - "Type": "AWS::CodeCommit::Repository", - }, - "RepopipelinePipeline22F2A91DEventRuleF314EE14": Object { - "Properties": Object { - "EventPattern": Object { - "detail": Object { - "event": Array [ - "referenceCreated", - "referenceUpdated", - ], - "referenceName": Array [ - "master", - ], - }, - "detail-type": Array [ - "CodeCommit Repository State Change", - ], - "resources": Array [ - Object { - "Fn::GetAtt": Array [ - "Repo02AC86CF", - "Arn", - ], - }, - ], - "source": Array [ - "aws.codecommit", - ], - }, - "State": "ENABLED", - "Targets": Array [ - Object { - "Arn": Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":codepipeline:", - Object { - "Ref": "AWS::Region", - }, - ":", - Object { - "Ref": "AWS::AccountId", - }, - ":", - Object { - "Ref": "PipelineC660917D", - }, - ], - ], - }, - "Id": "Target0", - "RoleArn": Object { - "Fn::GetAtt": Array [ - "PipelineEventsRole46BEEA7C", - "Arn", - ], - }, - }, - ], - }, - "Type": "AWS::Events::Rule", - }, - }, -} -`; - -exports[`pure-cfn.json: pure-cfn 1`] = ` -Object { - "Resources": Object { - "Hello4A628BD4": Object { - "DeletionPolicy": "Delete", - "Type": "AWS::SQS::Queue", - "UpdateReplacePolicy": "Delete", - }, - "MyLogGroup": Object { - "Properties": Object { - "LogGroupName": Object { - "Ref": "AWS::AccountId", - }, - }, - "Type": "AWS::Logs::LogGroup", - }, - }, -} -`; - -exports[`queue-kms.json: queue-kms 1`] = ` -Object { - "Resources": Object { - "MyQueueE6CA6235": Object { - "DeletionPolicy": "Delete", - "Properties": Object { - "KmsMasterKeyId": Object { - "Fn::GetAtt": Array [ - "MyQueueKey6C31ABF3", - "Arn", - ], - }, - }, - "Type": "AWS::SQS::Queue", - "UpdateReplacePolicy": "Delete", - }, - "MyQueueKey6C31ABF3": Object { - "DeletionPolicy": "Retain", - "Properties": Object { - "Description": "Created by queue-kms/MyQueue", - "KeyPolicy": Object { - "Statement": Array [ - Object { - "Action": "kms:*", - "Effect": "Allow", - "Principal": Object { - "AWS": Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":iam::", - Object { - "Ref": "AWS::AccountId", - }, - ":root", - ], - ], - }, - }, - "Resource": "*", - }, - ], - "Version": "2012-10-17", - }, - }, - "Type": "AWS::KMS::Key", - "UpdateReplacePolicy": "Retain", - }, - }, -} -`; - -exports[`vpc.json: vpc 1`] = ` -Object { - "Resources": Object { - "VPCB9E5F0B4": Object { - "Properties": Object { - "CidrBlock": "10.0.0.0/16", - "EnableDnsHostnames": true, - "EnableDnsSupport": true, - "InstanceTenancy": "default", - "Tags": Array [ - Object { - "Key": "Name", - "Value": "vpc/VPC", - }, - ], - }, - "Type": "AWS::EC2::VPC", - }, - "VPCIGWB7E252D3": Object { - "Properties": Object { - "Tags": Array [ - Object { - "Key": "Name", - "Value": "vpc/VPC", - }, - ], - }, - "Type": "AWS::EC2::InternetGateway", - }, - "VPCPrivateSubnet1DefaultRouteAE1D6490": Object { - "Properties": Object { - "DestinationCidrBlock": "0.0.0.0/0", - "NatGatewayId": Object { - "Ref": "VPCPublicSubnet1NATGatewayE0556630", - }, - "RouteTableId": Object { - "Ref": "VPCPrivateSubnet1RouteTableBE8A6027", - }, - }, - "Type": "AWS::EC2::Route", - }, - "VPCPrivateSubnet1RouteTableAssociation347902D1": Object { - "Properties": Object { - "RouteTableId": Object { - "Ref": "VPCPrivateSubnet1RouteTableBE8A6027", - }, - "SubnetId": Object { - "Ref": "VPCPrivateSubnet1Subnet8BCA10E0", - }, - }, - "Type": "AWS::EC2::SubnetRouteTableAssociation", - }, - "VPCPrivateSubnet1RouteTableBE8A6027": Object { - "Properties": Object { - "Tags": Array [ - Object { - "Key": "Name", - "Value": "vpc/VPC/PrivateSubnet1", - }, - ], - "VpcId": Object { - "Ref": "VPCB9E5F0B4", - }, - }, - "Type": "AWS::EC2::RouteTable", - }, - "VPCPrivateSubnet1Subnet8BCA10E0": Object { - "Properties": Object { - "AvailabilityZone": Object { - "Fn::Select": Array [ - 0, - Object { - "Fn::GetAZs": "", - }, - ], - }, - "CidrBlock": "10.0.128.0/18", - "MapPublicIpOnLaunch": false, - "Tags": Array [ - Object { - "Key": "aws-cdk:subnet-name", - "Value": "Private", - }, - Object { - "Key": "aws-cdk:subnet-type", - "Value": "Private", - }, - Object { - "Key": "Name", - "Value": "vpc/VPC/PrivateSubnet1", - }, - ], - "VpcId": Object { - "Ref": "VPCB9E5F0B4", - }, - }, - "Type": "AWS::EC2::Subnet", - }, - "VPCPrivateSubnet2DefaultRouteF4F5CFD2": Object { - "Properties": Object { - "DestinationCidrBlock": "0.0.0.0/0", - "NatGatewayId": Object { - "Ref": "VPCPublicSubnet2NATGateway3C070193", - }, - "RouteTableId": Object { - "Ref": "VPCPrivateSubnet2RouteTable0A19E10E", - }, - }, - "Type": "AWS::EC2::Route", - }, - "VPCPrivateSubnet2RouteTable0A19E10E": Object { - "Properties": Object { - "Tags": Array [ - Object { - "Key": "Name", - "Value": "vpc/VPC/PrivateSubnet2", - }, - ], - "VpcId": Object { - "Ref": "VPCB9E5F0B4", - }, - }, - "Type": "AWS::EC2::RouteTable", - }, - "VPCPrivateSubnet2RouteTableAssociation0C73D413": Object { - "Properties": Object { - "RouteTableId": Object { - "Ref": "VPCPrivateSubnet2RouteTable0A19E10E", - }, - "SubnetId": Object { - "Ref": "VPCPrivateSubnet2SubnetCFCDAA7A", - }, - }, - "Type": "AWS::EC2::SubnetRouteTableAssociation", - }, - "VPCPrivateSubnet2SubnetCFCDAA7A": Object { - "Properties": Object { - "AvailabilityZone": Object { - "Fn::Select": Array [ - 1, - Object { - "Fn::GetAZs": "", - }, - ], - }, - "CidrBlock": "10.0.192.0/18", - "MapPublicIpOnLaunch": false, - "Tags": Array [ - Object { - "Key": "aws-cdk:subnet-name", - "Value": "Private", - }, - Object { - "Key": "aws-cdk:subnet-type", - "Value": "Private", - }, - Object { - "Key": "Name", - "Value": "vpc/VPC/PrivateSubnet2", - }, - ], - "VpcId": Object { - "Ref": "VPCB9E5F0B4", - }, - }, - "Type": "AWS::EC2::Subnet", - }, - "VPCPublicSubnet1DefaultRoute91CEF279": Object { - "DependsOn": Array [ - "VPCVPCGW99B986DC", - ], - "Properties": Object { - "DestinationCidrBlock": "0.0.0.0/0", - "GatewayId": Object { - "Ref": "VPCIGWB7E252D3", - }, - "RouteTableId": Object { - "Ref": "VPCPublicSubnet1RouteTableFEE4B781", - }, - }, - "Type": "AWS::EC2::Route", - }, - "VPCPublicSubnet1EIP6AD938E8": Object { - "Properties": Object { - "Domain": "vpc", - "Tags": Array [ - Object { - "Key": "Name", - "Value": "vpc/VPC/PublicSubnet1", - }, - ], - }, - "Type": "AWS::EC2::EIP", - }, - "VPCPublicSubnet1NATGatewayE0556630": Object { - "Properties": Object { - "AllocationId": Object { - "Fn::GetAtt": Array [ - "VPCPublicSubnet1EIP6AD938E8", - "AllocationId", - ], - }, - "SubnetId": Object { - "Ref": "VPCPublicSubnet1SubnetB4246D30", - }, - "Tags": Array [ - Object { - "Key": "Name", - "Value": "vpc/VPC/PublicSubnet1", - }, - ], - }, - "Type": "AWS::EC2::NatGateway", - }, - "VPCPublicSubnet1RouteTableAssociation0B0896DC": Object { - "Properties": Object { - "RouteTableId": Object { - "Ref": "VPCPublicSubnet1RouteTableFEE4B781", - }, - "SubnetId": Object { - "Ref": "VPCPublicSubnet1SubnetB4246D30", - }, - }, - "Type": "AWS::EC2::SubnetRouteTableAssociation", - }, - "VPCPublicSubnet1RouteTableFEE4B781": Object { - "Properties": Object { - "Tags": Array [ - Object { - "Key": "Name", - "Value": "vpc/VPC/PublicSubnet1", - }, - ], - "VpcId": Object { - "Ref": "VPCB9E5F0B4", - }, - }, - "Type": "AWS::EC2::RouteTable", - }, - "VPCPublicSubnet1SubnetB4246D30": Object { - "Properties": Object { - "AvailabilityZone": Object { - "Fn::Select": Array [ - 0, - Object { - "Fn::GetAZs": "", - }, - ], - }, - "CidrBlock": "10.0.0.0/18", - "MapPublicIpOnLaunch": true, - "Tags": Array [ - Object { - "Key": "aws-cdk:subnet-name", - "Value": "Public", - }, - Object { - "Key": "aws-cdk:subnet-type", - "Value": "Public", - }, - Object { - "Key": "Name", - "Value": "vpc/VPC/PublicSubnet1", - }, - ], - "VpcId": Object { - "Ref": "VPCB9E5F0B4", - }, - }, - "Type": "AWS::EC2::Subnet", - }, - "VPCPublicSubnet2DefaultRouteB7481BBA": Object { - "DependsOn": Array [ - "VPCVPCGW99B986DC", - ], - "Properties": Object { - "DestinationCidrBlock": "0.0.0.0/0", - "GatewayId": Object { - "Ref": "VPCIGWB7E252D3", - }, - "RouteTableId": Object { - "Ref": "VPCPublicSubnet2RouteTable6F1A15F1", - }, - }, - "Type": "AWS::EC2::Route", - }, - "VPCPublicSubnet2EIP4947BC00": Object { - "Properties": Object { - "Domain": "vpc", - "Tags": Array [ - Object { - "Key": "Name", - "Value": "vpc/VPC/PublicSubnet2", - }, - ], - }, - "Type": "AWS::EC2::EIP", - }, - "VPCPublicSubnet2NATGateway3C070193": Object { - "Properties": Object { - "AllocationId": Object { - "Fn::GetAtt": Array [ - "VPCPublicSubnet2EIP4947BC00", - "AllocationId", - ], - }, - "SubnetId": Object { - "Ref": "VPCPublicSubnet2Subnet74179F39", - }, - "Tags": Array [ - Object { - "Key": "Name", - "Value": "vpc/VPC/PublicSubnet2", - }, - ], - }, - "Type": "AWS::EC2::NatGateway", - }, - "VPCPublicSubnet2RouteTable6F1A15F1": Object { - "Properties": Object { - "Tags": Array [ - Object { - "Key": "Name", - "Value": "vpc/VPC/PublicSubnet2", - }, - ], - "VpcId": Object { - "Ref": "VPCB9E5F0B4", - }, - }, - "Type": "AWS::EC2::RouteTable", - }, - "VPCPublicSubnet2RouteTableAssociation5A808732": Object { - "Properties": Object { - "RouteTableId": Object { - "Ref": "VPCPublicSubnet2RouteTable6F1A15F1", - }, - "SubnetId": Object { - "Ref": "VPCPublicSubnet2Subnet74179F39", - }, - }, - "Type": "AWS::EC2::SubnetRouteTableAssociation", - }, - "VPCPublicSubnet2Subnet74179F39": Object { - "Properties": Object { - "AvailabilityZone": Object { - "Fn::Select": Array [ - 1, - Object { - "Fn::GetAZs": "", - }, - ], - }, - "CidrBlock": "10.0.64.0/18", - "MapPublicIpOnLaunch": true, - "Tags": Array [ - Object { - "Key": "aws-cdk:subnet-name", - "Value": "Public", - }, - Object { - "Key": "aws-cdk:subnet-type", - "Value": "Public", - }, - Object { - "Key": "Name", - "Value": "vpc/VPC/PublicSubnet2", - }, - ], - "VpcId": Object { - "Ref": "VPCB9E5F0B4", - }, - }, - "Type": "AWS::EC2::Subnet", - }, - "VPCVPCGW99B986DC": Object { - "Properties": Object { - "InternetGatewayId": Object { - "Ref": "VPCIGWB7E252D3", - }, - "VpcId": Object { - "Ref": "VPCB9E5F0B4", - }, - }, - "Type": "AWS::EC2::VPCGatewayAttachment", - }, - }, -} -`; diff --git a/packages/decdk/test/fixture/index.ts b/packages/decdk/test/fixture/index.ts deleted file mode 100644 index 04750a5ff654b..0000000000000 --- a/packages/decdk/test/fixture/index.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Tests how an array with a bunch of primitives is represented in JSON schema. - */ -export interface InterfaceWithPrimitives { - /** - * A property of type number. - */ - readonly numberProperty: number; - - /** - * A property of type string. - */ - readonly stringProperty: string; - - /** - * Array of strings. - */ - readonly arrayOfStrings: string[]; - - /** - * Optional boolean - */ - readonly optionalBoolean?: boolean; - - // - // intentionally left blank (to check that description is omitted) - // - readonly mapOfNumbers: { [key: string]: number } -} - -export enum MyNormalEnum { - ENUM_MEMBER_1, - ENUM_MEMBER_2, - ENUM_MEMBER_3 -} diff --git a/packages/decdk/test/fixture/package.json b/packages/decdk/test/fixture/package.json deleted file mode 100644 index 37ba6d2665de7..0000000000000 --- a/packages/decdk/test/fixture/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "fixture", - "version": "0.0.0", - "description": "test fixtures for deCDK", - "main": "index.js", - "types": "index.d.ts", - "jsii": { - "outdir": "dist" - }, - "repository": { - "url": "https://github.com/aws/aws-cdk", - "type": "git" - }, - "author": { - "name": "Amazon Web Services", - "url": "https://aws.amazon.com" - }, - "license": "Apache-2.0" -} \ No newline at end of file diff --git a/packages/decdk/test/fixture/tsconfig.json b/packages/decdk/test/fixture/tsconfig.json deleted file mode 100644 index 7ca946aa3a374..0000000000000 --- a/packages/decdk/test/fixture/tsconfig.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "compilerOptions": { - "alwaysStrict": true, - "charset": "utf8", - "declaration": true, - "experimentalDecorators": true, - "incremental": true, - "inlineSourceMap": true, - "inlineSources": true, - "lib": [ - "es2019" - ], - "module": "CommonJS", - "newLine": "lf", - "noEmitOnError": true, - "noFallthroughCasesInSwitch": true, - "noImplicitAny": true, - "noImplicitReturns": true, - "noImplicitThis": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "resolveJsonModule": true, - "strict": true, - "strictNullChecks": true, - "strictPropertyInitialization": true, - "stripInternal": false, - "target": "ES2019", - "composite": false, - "tsBuildInfoFile": "tsconfig.tsbuildinfo" - }, - "include": [ - "**/*.ts" - ], - "exclude": [ - "node_modules" - ], - "_generated_by_jsii_": "Generated by jsii - safe to delete, and ideally should be in .gitignore" -} diff --git a/packages/decdk/test/sanity.test.ts b/packages/decdk/test/sanity.test.ts deleted file mode 100644 index fe765d04f6b9e..0000000000000 --- a/packages/decdk/test/sanity.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -import * as path from 'path'; - -test('path.resolve is sane', async () => { - // Reasons why this might not be true: - // graceful-fs, which is used by Jest, hooks into process.cwd() and - // process.chdir() and caches the values. Because... profit? - - const targetDir = path.join(__dirname, 'fixture'); - - const cwd = process.cwd(); - - try { - process.chdir(targetDir); - expect(process.cwd()).toEqual(targetDir); - - const resolved = path.resolve('.'); - expect(resolved).toEqual(targetDir); - - } finally { - process.chdir(cwd); - } -}); \ No newline at end of file diff --git a/packages/decdk/test/schema.test.ts b/packages/decdk/test/schema.test.ts deleted file mode 100644 index 7b097dcc9fcbe..0000000000000 --- a/packages/decdk/test/schema.test.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { spawn as spawnAsync, SpawnOptions } from 'child_process'; -import * as reflect from 'jsii-reflect'; -import * as path from 'path'; -import { SchemaContext, schemaForInterface } from '../lib/jsii2schema'; - -const fixturedir = path.join(__dirname, 'fixture'); - -/* eslint-disable no-console */ - -// building the decdk schema often does not complete in the default 5 second Jest timeout -jest.setTimeout(60_000); - -let typesys: reflect.TypeSystem; - -beforeAll(async () => { - typesys = new reflect.TypeSystem(); - - // jsii-compile the fixtures module - await spawn(require.resolve('jsii/bin/jsii'), { cwd: fixturedir, }); - - // load the resulting file system - await typesys.loadFile(path.join(fixturedir, '.jsii')); - await typesys.load(path.dirname(require.resolve('@aws-cdk/core/.jsii'))); -}); - -test('schemaForInterface: interface with primitives', async () => { - // GIVEN - const defs = { }; - const ctx = SchemaContext.root(defs); - - // WHEN - const ref = schemaForInterface(typesys.findFqn('fixture.InterfaceWithPrimitives'), ctx); - - // THEN - expect(ref).toStrictEqual({ $ref: '#/definitions/fixture.InterfaceWithPrimitives' }); - expect(ctx.definitions).toStrictEqual({ - 'fixture.InterfaceWithPrimitives': { - type: 'object', - title: 'InterfaceWithPrimitives', - additionalProperties: false, - properties: { - arrayOfStrings: { - type: 'array', - items: { type: 'string' }, - description: 'Array of strings.' - }, - mapOfNumbers: { - type: 'object', - additionalProperties: { type: 'number' } - }, - numberProperty: { - type: 'number', - description: 'A property of type number.' - }, - stringProperty: { - type: 'string', - description: 'A property of type string.' - }, - optionalBoolean: { - type: 'boolean', - description: 'Optional boolean.' - } - }, - required: [ - 'arrayOfStrings', - 'mapOfNumbers', - 'numberProperty', - 'stringProperty' - ], - comment: 'fixture.InterfaceWithPrimitives' - } - }); -}); - -/** - * Version of spawn() that returns a promise - * - * Need spawn() so that we can set stdio to inherit so that any jsii errors - * are propagated outwards. - */ -function spawn(command: string, options: SpawnOptions | undefined) { - return new Promise((resolve, reject) => { - const cp = spawnAsync(command, [], { stdio: 'inherit', ...options }); - - cp.on('error', reject); - cp.on('exit', (code, signal) => { - if (code === 0) { resolve(); } - reject(new Error(`Subprocess exited with ${code || signal}`)); - }); - }); -} diff --git a/packages/decdk/test/synth.test.ts b/packages/decdk/test/synth.test.ts deleted file mode 100644 index 8952a9aa11fd8..0000000000000 --- a/packages/decdk/test/synth.test.ts +++ /dev/null @@ -1,51 +0,0 @@ -import * as cdk from '@aws-cdk/core'; -import * as fs from 'fs'; -import * as reflect from 'jsii-reflect'; -import * as path from 'path'; -import { FUTURE_FLAGS } from '@aws-cdk/cx-api'; -import { DeclarativeStack, loadTypeSystem, readTemplate, stackNameFromFileName } from '../lib'; - -const VALIDATE_ASSEMBLIES = true; - -const dir = path.join(__dirname, '..', 'examples'); - -if (VALIDATE_ASSEMBLIES) { - // With validation loading all assemblies takes 10s on my machine. - // Without validation it's 600ms. - // - // Add a big margin for slower machines in case validation is enabled. - jest.setTimeout(60 * 1000); -} - -let _cachedTS: reflect.TypeSystem; -async function obtainTypeSystem() { - // Load the typesystem only once, it's quite expensive - if (!_cachedTS) { - _cachedTS = await loadTypeSystem(VALIDATE_ASSEMBLIES); - } - return _cachedTS; -} - -for (const templateFile of fs.readdirSync(dir)) { - test(templateFile, async () => { - const workingDirectory = dir; - const template = await readTemplate(path.resolve(dir, templateFile)); - const typeSystem = await obtainTypeSystem(); - - const app = new cdk.App({ - context: { - ...FUTURE_FLAGS, - } - }); - const stackName = stackNameFromFileName(templateFile); - - new DeclarativeStack(app, stackName, { - workingDirectory, - template, - typeSystem - }); - - const output = app.synth().getStackByName(stackName); - expect(output.template).toMatchSnapshot(stackName); - }); -} diff --git a/packages/decdk/tsconfig.json b/packages/decdk/tsconfig.json deleted file mode 100644 index 60a2dda5d1b9b..0000000000000 --- a/packages/decdk/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2018", - "module": "commonjs", - "lib": ["es2018"], - "strict": true, - "alwaysStrict": true, - "declaration": true, - "inlineSourceMap": true, - "inlineSources": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "resolveJsonModule": true, - "composite": true, - "incremental": true - }, - "exclude": [ - "test/enrichments/**" - ], - "references": [ - { "path": "../@aws-cdk/core" }, - { "path": "../@aws-cdk/cx-api" } - ] -} From e91318761867a5db7a75f5fb090f7bc8e3fa222d Mon Sep 17 00:00:00 2001 From: Calvin Combs <66279577+comcalvi@users.noreply.github.com> Date: Wed, 19 Jan 2022 19:15:37 -0800 Subject: [PATCH 45/50] chore(cfn-include): migrate all tests to Assertions (#18537) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../cloudformation-include/package.json | 2 +- .../test/invalid-templates.test.ts | 14 +- .../test/nested-stacks.test.ts | 47 +++--- .../test/serverless-transform.test.ts | 18 +-- .../fn-sub-map-dotted-attributes.json | 11 +- .../resource-attribute-update-policy.json | 2 +- .../test/valid-templates.test.ts | 146 ++++++++++-------- .../test/yaml-templates.test.ts | 34 ++-- 8 files changed, 146 insertions(+), 128 deletions(-) diff --git a/packages/@aws-cdk/cloudformation-include/package.json b/packages/@aws-cdk/cloudformation-include/package.json index f0455eb86a21f..aadda87535b0b 100644 --- a/packages/@aws-cdk/cloudformation-include/package.json +++ b/packages/@aws-cdk/cloudformation-include/package.json @@ -448,7 +448,7 @@ "constructs": "^3.3.69" }, "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/pkglint": "0.0.0", diff --git a/packages/@aws-cdk/cloudformation-include/test/invalid-templates.test.ts b/packages/@aws-cdk/cloudformation-include/test/invalid-templates.test.ts index 326b80a3585f8..fb2f4697f610e 100644 --- a/packages/@aws-cdk/cloudformation-include/test/invalid-templates.test.ts +++ b/packages/@aws-cdk/cloudformation-include/test/invalid-templates.test.ts @@ -1,15 +1,15 @@ import * as path from 'path'; -import { SynthUtils } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; import * as core from '@aws-cdk/core'; import * as constructs from 'constructs'; import * as inc from '../lib'; describe('CDK Include', () => { + let app: core.App; let stack: core.Stack; beforeEach(() => { - stack = new core.Stack(); + app = new core.App(); + stack = new core.Stack(app); }); test('throws a validation exception for a template with a missing required top-level resource property', () => { @@ -22,7 +22,7 @@ describe('CDK Include', () => { includeTestTemplate(stack, 'bucket-with-cors-rules-not-an-array.json'); expect(() => { - SynthUtils.synthesize(stack); + app.synth(); }).toThrow(/corsRules: "CorsRules!" should be a list/); }); @@ -30,7 +30,7 @@ describe('CDK Include', () => { includeTestTemplate(stack, 'bucket-with-cors-rules-null-element.json'); expect(() => { - SynthUtils.synthesize(stack); + app.synth(); }).toThrow(/allowedMethods: required but missing/); }); @@ -38,7 +38,7 @@ describe('CDK Include', () => { includeTestTemplate(stack, 'bucket-with-invalid-cors-rule.json'); expect(() => { - SynthUtils.synthesize(stack); + app.synth(); }).toThrow(/allowedOrigins: required but missing/); }); @@ -130,7 +130,7 @@ describe('CDK Include', () => { includeTestTemplate(stack, 'alphabetical-string-passed-to-number.json'); expect(() => { - SynthUtils.synthesize(stack); + app.synth(); }).toThrow(/"abc" should be a number/); }); diff --git a/packages/@aws-cdk/cloudformation-include/test/nested-stacks.test.ts b/packages/@aws-cdk/cloudformation-include/test/nested-stacks.test.ts index 2a323657f089e..4e73005db1e54 100644 --- a/packages/@aws-cdk/cloudformation-include/test/nested-stacks.test.ts +++ b/packages/@aws-cdk/cloudformation-include/test/nested-stacks.test.ts @@ -1,6 +1,5 @@ import * as path from 'path'; -import { ABSENT, ResourcePart } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as s3 from '@aws-cdk/aws-s3'; import * as core from '@aws-cdk/core'; import * as inc from '../lib'; @@ -27,7 +26,7 @@ describe('CDK Include for nested stacks', () => { }); const childStack = parentTemplate.getNestedStack('ChildStack'); - expect(childStack.stack).toMatchTemplate( + Template.fromStack(childStack.stack).templateMatches( loadTestFileToJsObject('grandchild-import-stack.json'), ); }); @@ -47,11 +46,11 @@ describe('CDK Include for nested stacks', () => { const childStack = parentTemplate.getNestedStack('ChildStack'); const anotherChildStack = parentTemplate.getNestedStack('AnotherChildStack'); - expect(childStack.stack).toMatchTemplate( + Template.fromStack(childStack.stack).templateMatches( loadTestFileToJsObject('grandchild-import-stack.json'), ); - expect(anotherChildStack.stack).toMatchTemplate( + Template.fromStack(anotherChildStack.stack).templateMatches( loadTestFileToJsObject('grandchild-import-stack.json'), ); }); @@ -73,11 +72,11 @@ describe('CDK Include for nested stacks', () => { const childStack = parentTemplate.getNestedStack('ChildStack'); const grandChildStack = childStack.includedTemplate.getNestedStack('GrandChildStack'); - expect(childStack.stack).toMatchTemplate( + Template.fromStack(childStack.stack).templateMatches( loadTestFileToJsObject('child-import-stack.expected.json'), ); - expect(grandChildStack.stack).toMatchTemplate( + Template.fromStack(grandChildStack.stack).templateMatches( loadTestFileToJsObject('grandchild-import-stack.json'), ); }); @@ -188,7 +187,7 @@ describe('CDK Include for nested stacks', () => { bucket.bucketName = 'modified-bucket-name'; - expect(childTemplate.stack).toHaveResource('AWS::S3::Bucket', { BucketName: 'modified-bucket-name' }); + Template.fromStack(childTemplate.stack).hasResourceProperties('AWS::S3::Bucket', { BucketName: 'modified-bucket-name' }); }); test('can use a condition', () => { @@ -218,7 +217,7 @@ describe('CDK Include for nested stacks', () => { const assetParam = 'AssetParameters5dc7d4a99cfe2979687dc74f2db9fd75f253b5505a1912b5ceecf70c9aefba50S3BucketEAA24F0C'; const assetParamKey = 'AssetParameters5dc7d4a99cfe2979687dc74f2db9fd75f253b5505a1912b5ceecf70c9aefba50S3VersionKey1194CAB2'; - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ "Parameters": { [assetParam]: { "Type": "String", @@ -284,7 +283,7 @@ describe('CDK Include for nested stacks', () => { templateFile: testTemplateFilePath('parent-two-children.json'), }); - expect(stack).toMatchTemplate(loadTestFileToJsObject('parent-two-children.json')); + Template.fromStack(stack).templateMatches(loadTestFileToJsObject('parent-two-children.json')); }); test('getNestedStack() throws an exception when getting a resource that does not exist in the template', () => { @@ -344,7 +343,7 @@ describe('CDK Include for nested stacks', () => { }, }); - expect(stack).toHaveResourceLike('AWS::CloudFormation::Stack', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudFormation::Stack', { "Parameters": { "Param1": { "Ref": "Param", @@ -370,7 +369,7 @@ describe('CDK Include for nested stacks', () => { const parameter = parentTemplate.getParameter('Param'); parameter.overrideLogicalId('DifferentParameter'); - expect(stack).toHaveResourceLike('AWS::CloudFormation::Stack', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudFormation::Stack', { "Parameters": { "Param1": { "Ref": "DifferentParameter", @@ -417,7 +416,7 @@ describe('CDK Include for nested stacks', () => { }, }); - expect(stack).toHaveResourceLike('AWS::CloudFormation::Stack', { + Template.fromStack(stack).hasResource('AWS::CloudFormation::Stack', { "Metadata": { "Property1": "Value1", }, @@ -426,7 +425,7 @@ describe('CDK Include for nested stacks', () => { "AnotherChildStack", ], "UpdateReplacePolicy": "Retain", - }, ResourcePart.CompleteDefinition); + }); }); test('correctly parses NotificationsARNs, Timeout', () => { @@ -442,11 +441,11 @@ describe('CDK Include for nested stacks', () => { }, }); - expect(stack).toHaveResourceLike('AWS::CloudFormation::Stack', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudFormation::Stack', { "NotificationARNs": ["arn1"], "TimeoutInMinutes": 5, }); - expect(stack).toHaveResourceLike('AWS::CloudFormation::Stack', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudFormation::Stack', { "NotificationARNs": { "Ref": "ArrayParam" }, "TimeoutInMinutes": { "Fn::Select": [0, { @@ -466,7 +465,7 @@ describe('CDK Include for nested stacks', () => { }, }); - expect(stack).toHaveResourceLike('AWS::CloudFormation::Stack', { + Template.fromStack(stack).hasResourceProperties('AWS::CloudFormation::Stack', { "Parameters": { "Number": "60", }, @@ -481,7 +480,7 @@ describe('CDK Include for nested stacks', () => { templateFile: testTemplateFilePath('child-no-bucket.json'), }); - expect(includedChild.stack).toMatchTemplate( + Template.fromStack(includedChild.stack).templateMatches( loadTestFileToJsObject('child-no-bucket.json'), ); expect(includedChild.includedTemplate.getResource('GrandChildStack')).toBeDefined(); @@ -536,7 +535,7 @@ describe('CDK Include for nested stacks', () => { }); test('correctly creates parameters in the parent stack, and passes them to the child stack', () => { - expect(assetStack).toMatchTemplate({ + Template.fromStack(assetStack).templateMatches({ "Parameters": { [parentBucketParam]: { "Type": "String", @@ -616,7 +615,7 @@ describe('CDK Include for nested stacks', () => { }); test('correctly creates parameters in the child stack, and passes them to the grandchild stack', () => { - expect(child.stack).toMatchTemplate({ + Template.fromStack(child.stack).templateMatches({ "Parameters": { "MyBucketParameter": { "Type": "String", @@ -676,7 +675,7 @@ describe('CDK Include for nested stacks', () => { }); test('leaves grandchild stack unmodified', () => { - expect(grandChild.stack).toMatchTemplate( + Template.fromStack(grandChild.stack).templateMatches( loadTestFileToJsObject('grandchild-import-stack.json'), ); }); @@ -703,7 +702,7 @@ describe('CDK Include for nested stacks', () => { }); test('correctly removes the parameter from the child stack', () => { - expect(childStack).toMatchTemplate({ + Template.fromStack(childStack).templateMatches({ "Parameters": { "SecondParameter": { "Type": "String", @@ -733,9 +732,9 @@ describe('CDK Include for nested stacks', () => { }); test('correctly removes the parameter from the parent stack', () => { - expect(parentStack).toHaveResourceLike('AWS::CloudFormation::Stack', { + Template.fromStack(parentStack).hasResourceProperties('AWS::CloudFormation::Stack', { "Parameters": { - "FirstParameter": ABSENT, + "FirstParameter": Match.absent(), "SecondParameter": "second-value", }, }); diff --git a/packages/@aws-cdk/cloudformation-include/test/serverless-transform.test.ts b/packages/@aws-cdk/cloudformation-include/test/serverless-transform.test.ts index ce0d044bb4ed3..706afb615ec72 100644 --- a/packages/@aws-cdk/cloudformation-include/test/serverless-transform.test.ts +++ b/packages/@aws-cdk/cloudformation-include/test/serverless-transform.test.ts @@ -1,5 +1,5 @@ import * as path from 'path'; -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as core from '@aws-cdk/core'; import * as constructs from 'constructs'; import * as inc from '../lib'; @@ -18,7 +18,7 @@ describe('CDK Include for templates with SAM transform', () => { test('can ingest a template with only a minimal SAM function using S3Location for CodeUri, and output it unchanged', () => { includeTestTemplate(stack, 'only-minimal-sam-function-codeuri-as-s3location.yaml'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('only-minimal-sam-function-codeuri-as-s3location.yaml'), ); }); @@ -26,7 +26,7 @@ describe('CDK Include for templates with SAM transform', () => { test('can ingest a template with only a SAM function using an array with DDB CRUD for Policies, and output it unchanged', () => { includeTestTemplate(stack, 'only-sam-function-policies-array-ddb-crud.yaml'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('only-sam-function-policies-array-ddb-crud.yaml'), ); }); @@ -34,7 +34,7 @@ describe('CDK Include for templates with SAM transform', () => { test('can ingest a template with only a minimal SAM function using a parameter for CodeUri, and output it unchanged', () => { includeTestTemplate(stack, 'only-minimal-sam-function-codeuri-as-param.yaml'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('only-minimal-sam-function-codeuri-as-param.yaml'), ); }); @@ -42,7 +42,7 @@ describe('CDK Include for templates with SAM transform', () => { test('can ingest a template with only a minimal SAM function using a parameter for CodeUri Bucket property, and output it unchanged', () => { includeTestTemplate(stack, 'only-minimal-sam-function-codeuri-bucket-as-param.yaml'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('only-minimal-sam-function-codeuri-bucket-as-param.yaml'), ); }); @@ -50,7 +50,7 @@ describe('CDK Include for templates with SAM transform', () => { test('can ingest a template with only a SAM function using an array with DDB CRUD for Policies with an Fn::If expression, and output it unchanged', () => { includeTestTemplate(stack, 'only-sam-function-policies-array-ddb-crud-if.yaml'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('only-sam-function-policies-array-ddb-crud-if.yaml'), ); }); @@ -58,7 +58,7 @@ describe('CDK Include for templates with SAM transform', () => { test('can ingest a template with a a union-type property provided as an object, and output it unchanged', () => { includeTestTemplate(stack, 'api-endpoint-config-object.yaml'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('api-endpoint-config-object.yaml'), ); }); @@ -66,7 +66,7 @@ describe('CDK Include for templates with SAM transform', () => { test('can ingest a template with a a union-type property provided as a string, and output it unchanged', () => { includeTestTemplate(stack, 'api-endpoint-config-string.yaml'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('api-endpoint-config-string.yaml'), ); }); @@ -74,7 +74,7 @@ describe('CDK Include for templates with SAM transform', () => { test('can ingest a template with a a union-type property provided as an empty string, and output it unchanged', () => { includeTestTemplate(stack, 'api-endpoint-config-string-empty.yaml'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('api-endpoint-config-string-empty.yaml'), ); }); diff --git a/packages/@aws-cdk/cloudformation-include/test/test-templates/fn-sub-map-dotted-attributes.json b/packages/@aws-cdk/cloudformation-include/test/test-templates/fn-sub-map-dotted-attributes.json index c53229d2844d8..7daef9f79f9a4 100644 --- a/packages/@aws-cdk/cloudformation-include/test/test-templates/fn-sub-map-dotted-attributes.json +++ b/packages/@aws-cdk/cloudformation-include/test/test-templates/fn-sub-map-dotted-attributes.json @@ -4,7 +4,14 @@ "Type": "AWS::S3::Bucket", "Properties": { "BucketName": { - "Fn::Sub": "${ELB.SourceSecurityGroup.GroupName}" + "Fn::Sub": [ + "${ELB.SourceSecurityGroup.GroupName}-${LoadBalancerName}", + { + "LoadBalancerName": { + "Ref": "ELB" + } + } + ] } } }, @@ -14,7 +21,7 @@ "AvailabilityZones": [ "us-east-1a" ], - "CrossZone": "true", + "CrossZone": true, "Listeners": [{ "LoadBalancerPort": "80", "InstancePort": "80", diff --git a/packages/@aws-cdk/cloudformation-include/test/test-templates/resource-attribute-update-policy.json b/packages/@aws-cdk/cloudformation-include/test/test-templates/resource-attribute-update-policy.json index fb1f6f2aab1b2..e1440a46193be 100644 --- a/packages/@aws-cdk/cloudformation-include/test/test-templates/resource-attribute-update-policy.json +++ b/packages/@aws-cdk/cloudformation-include/test/test-templates/resource-attribute-update-policy.json @@ -53,7 +53,7 @@ "BeforeAllowTrafficHook" : "Lambda2", "DeploymentGroupName" : { "Ref": "CodeDeployDg" } }, - "EnableVersionUpgrade": "true", + "EnableVersionUpgrade": true, "UseOnlineResharding": false } } diff --git a/packages/@aws-cdk/cloudformation-include/test/valid-templates.test.ts b/packages/@aws-cdk/cloudformation-include/test/valid-templates.test.ts index 0741b7f9d23df..65cd7e981cc81 100644 --- a/packages/@aws-cdk/cloudformation-include/test/valid-templates.test.ts +++ b/packages/@aws-cdk/cloudformation-include/test/valid-templates.test.ts @@ -1,6 +1,5 @@ import * as path from 'path'; -import { ResourcePart } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import * as s3 from '@aws-cdk/aws-s3'; import * as ssm from '@aws-cdk/aws-ssm'; @@ -22,7 +21,7 @@ describe('CDK Include', () => { test('can ingest a template with only an empty S3 Bucket, and output it unchanged', () => { includeTestTemplate(stack, 'only-empty-bucket.json'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('only-empty-bucket.json'), ); }); @@ -41,7 +40,7 @@ describe('CDK Include', () => { const cfnBucket = cfnTemplate.getResource('Bucket') as s3.CfnBucket; cfnBucket.bucketName = 'my-bucket-name'; - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ "Resources": { "Bucket": { "Type": "AWS::S3::Bucket", @@ -58,7 +57,7 @@ describe('CDK Include', () => { const cfnBucket = cfnTemplate.getResource('Bucket') as s3.CfnBucket; expect((cfnBucket.corsConfiguration as any).corsRules).toHaveLength(1); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('only-bucket-complex-props.json'), ); }); @@ -75,7 +74,7 @@ describe('CDK Include', () => { resources: [cfnBucket.attrArn], })); - expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { "PolicyDocument": { "Statement": [ { @@ -95,7 +94,7 @@ describe('CDK Include', () => { test('can ingest a template with a Bucket Ref-erencing a KMS Key, and output it unchanged', () => { includeTestTemplate(stack, 'bucket-with-encryption-key.json'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('bucket-with-encryption-key.json'), ); }); @@ -103,25 +102,39 @@ describe('CDK Include', () => { test('accepts strings for properties with type number', () => { includeTestTemplate(stack, 'string-for-number.json'); - expect(stack).toMatchTemplate( - loadTestFileToJsObject('string-for-number.json'), - ); + Template.fromStack(stack).hasResourceProperties('AWS::S3::Bucket', { + "CorsConfiguration": { + "CorsRules": [ + { + "MaxAge": 10, + }, + ], + }, + }); }); test('accepts numbers for properties with type string', () => { includeTestTemplate(stack, 'number-for-string.json'); - expect(stack).toMatchTemplate( - loadTestFileToJsObject('number-for-string.json'), - ); + Template.fromStack(stack).hasResourceProperties('AWS::S3::Bucket', { + "WebsiteConfiguration": { + "RoutingRules": [ + { + "RedirectRule": { + "HttpRedirectCode": "403", + }, + }, + ], + }, + }); }); test('accepts booleans for properties with type string', () => { includeTestTemplate(stack, 'boolean-for-string.json'); - expect(stack).toMatchTemplate( - loadTestFileToJsObject('boolean-for-string.json'), - ); + Template.fromStack(stack).hasResourceProperties('AWS::S3::Bucket', { + "AccessControl": "true", + }); }); test('correctly changes the logical IDs, including references, if imported with preserveLogicalIds=false', () => { @@ -135,7 +148,7 @@ describe('CDK Include', () => { const cfnBucket = cfnTemplate.getResource('Bucket') as s3.CfnBucket; cfnBucket.bucketName = 'my-bucket-name'; - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ "Resources": { "MyScopeKey7673692F": { "Type": "AWS::KMS::Key", @@ -202,7 +215,7 @@ describe('CDK Include', () => { test('can ingest a template with an Fn::If expression for simple values, and output it unchanged', () => { includeTestTemplate(stack, 'if-simple-property.json'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('if-simple-property.json'), ); }); @@ -210,7 +223,7 @@ describe('CDK Include', () => { test('can ingest a template with an Fn::If expression for complex values, and output it unchanged', () => { includeTestTemplate(stack, 'if-complex-property.json'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('if-complex-property.json'), ); }); @@ -218,7 +231,7 @@ describe('CDK Include', () => { test('can ingest a UserData script, and output it unchanged', () => { includeTestTemplate(stack, 'user-data.json'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('user-data.json'), ); }); @@ -226,7 +239,7 @@ describe('CDK Include', () => { test('can correctly ingest a resource with a property of type: Map of Lists of primitive types', () => { const cfnTemplate = includeTestTemplate(stack, 'ssm-association.json'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('ssm-association.json'), ); const association = cfnTemplate.getResource('Association') as ssm.CfnAssociation; @@ -236,7 +249,7 @@ describe('CDK Include', () => { test('can ingest a template with intrinsic functions and conditions, and output it unchanged', () => { includeTestTemplate(stack, 'functions-and-conditions.json'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('functions-and-conditions.json'), ); }); @@ -244,7 +257,7 @@ describe('CDK Include', () => { test('can ingest a JSON template with string-form Fn::GetAtt, and output it unchanged', () => { includeTestTemplate(stack, 'get-att-string-form.json'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('get-att-string-form.json'), ); }); @@ -252,7 +265,7 @@ describe('CDK Include', () => { test('can ingest a template with Fn::Sub in string form with escaped and unescaped references and output it unchanged', () => { includeTestTemplate(stack, 'fn-sub-string.json'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('fn-sub-string.json'), ); }); @@ -260,15 +273,15 @@ describe('CDK Include', () => { test('can parse the string argument Fn::Sub with escaped references that contain whitespace', () => { includeTestTemplate(stack, 'fn-sub-escaping.json'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('fn-sub-escaping.json'), ); }); - test('can ingest a template with Fn::Sub in map form and output it unchanged', () => { + test('can ingest a template with Fn::Sub using dotted attributes in map form and output it unchanged', () => { includeTestTemplate(stack, 'fn-sub-map-dotted-attributes.json'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('fn-sub-map-dotted-attributes.json'), ); }); @@ -276,7 +289,7 @@ describe('CDK Include', () => { test('preserves an empty map passed to Fn::Sub', () => { includeTestTemplate(stack, 'fn-sub-map-empty.json'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('fn-sub-map-empty.json'), ); }); @@ -284,7 +297,7 @@ describe('CDK Include', () => { test('can ingest a template with Fn::Sub shadowing a logical ID from the template and output it unchanged', () => { includeTestTemplate(stack, 'fn-sub-shadow.json'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('fn-sub-shadow.json'), ); }); @@ -292,7 +305,7 @@ describe('CDK Include', () => { test('can ingest a template with Fn::Sub attribute expression shadowing a logical ID from the template, and output it unchanged', () => { includeTestTemplate(stack, 'fn-sub-shadow-attribute.json'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('fn-sub-shadow-attribute.json'), ); }); @@ -302,7 +315,7 @@ describe('CDK Include', () => { cfnTemplate.getResource('AnotherBucket').overrideLogicalId('NewBucket'); - expect(stack).toHaveResourceLike('AWS::S3::Bucket', { + Template.fromStack(stack).hasResourceProperties('AWS::S3::Bucket', { "BucketName": { "Fn::Sub": [ "${AnotherBucket}", @@ -319,7 +332,7 @@ describe('CDK Include', () => { cfnTemplate.getResource('Bucket').overrideLogicalId('NewBucket'); - expect(stack).toHaveResourceLike('AWS::S3::Bucket', { + Template.fromStack(stack).hasResourceProperties('AWS::S3::Bucket', { "BucketName": { "Fn::Sub": "${NewBucket}-${!Bucket}-${NewBucket.DomainName}", }, @@ -329,7 +342,7 @@ describe('CDK Include', () => { test('can ingest a template with Fn::Sub with brace edge cases and output it unchanged', () => { includeTestTemplate(stack, 'fn-sub-brace-edges.json'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('fn-sub-brace-edges.json'), ); }); @@ -342,7 +355,7 @@ describe('CDK Include', () => { }, }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ "Parameters": { "AnotherParam": { "Type": "String", @@ -375,7 +388,7 @@ describe('CDK Include', () => { test('can ingest a template with a Ref expression for an array value, and output it unchanged', () => { includeTestTemplate(stack, 'ref-array-property.json'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('ref-array-property.json'), ); }); @@ -383,7 +396,7 @@ describe('CDK Include', () => { test('renders non-Resources sections unchanged', () => { includeTestTemplate(stack, 'only-empty-bucket-with-parameters.json'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('only-empty-bucket-with-parameters.json'), ); }); @@ -394,14 +407,14 @@ describe('CDK Include', () => { expect(cfnBucket2.node.dependencies).toHaveLength(1); // we always render dependsOn as an array, even if it's a single string - expect(stack).toHaveResourceLike('AWS::S3::Bucket', { + Template.fromStack(stack).hasResource('AWS::S3::Bucket', { "Properties": { "BucketName": "bucket2", }, "DependsOn": [ "Bucket1", ], - }, ResourcePart.CompleteDefinition); + }); }); test('resolves DependsOn with an array of String values to the actual L1 class instances', () => { @@ -409,7 +422,7 @@ describe('CDK Include', () => { const cfnBucket2 = cfnTemplate.getResource('Bucket2'); expect(cfnBucket2.node.dependencies).toHaveLength(2); - expect(stack).toHaveResourceLike('AWS::S3::Bucket', { + Template.fromStack(stack).hasResource('AWS::S3::Bucket', { "Properties": { "BucketName": "bucket2", }, @@ -417,7 +430,7 @@ describe('CDK Include', () => { "Bucket0", "Bucket1", ], - }, ResourcePart.CompleteDefinition); + }); }); test('correctly parses Conditions and the Condition resource attribute', () => { @@ -426,7 +439,7 @@ describe('CDK Include', () => { const cfnBucket = cfnTemplate.getResource('Bucket'); expect(cfnBucket.cfnOptions.condition).toBe(alwaysFalseCondition); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('resource-attribute-condition.json'), ); }); @@ -434,7 +447,7 @@ describe('CDK Include', () => { test('allows Conditions to reference Mappings', () => { includeTestTemplate(stack, 'condition-using-mapping.json'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('condition-using-mapping.json'), ); }); @@ -444,7 +457,7 @@ describe('CDK Include', () => { const alwaysFalse = cfnTemplate.getCondition('AlwaysFalse'); alwaysFalse.overrideLogicalId('TotallyFalse'); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ "Parameters": { "Param": { "Type": "String", @@ -481,7 +494,7 @@ describe('CDK Include', () => { }); const originalTemplate = loadTestFileToJsObject('bucket-with-parameters.json'); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ "Resources": { ...originalTemplate.Resources, "NewBucket": { @@ -526,7 +539,7 @@ describe('CDK Include', () => { numberParam.type = "NewType"; const originalTemplate = loadTestFileToJsObject('bucket-with-parameters.json'); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ "Resources": { ...originalTemplate.Resources, }, @@ -559,7 +572,7 @@ describe('CDK Include', () => { alwaysFalseCondition.expression = core.Fn.conditionEquals(1, 2); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ "Conditions": { "AlwaysFalseCond": { "Fn::Equals": [1, 2], @@ -580,7 +593,7 @@ describe('CDK Include', () => { expect(cfnBucket.cfnOptions.creationPolicy).toBeDefined(); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('resource-attribute-creation-policy.json'), ); }); @@ -590,8 +603,7 @@ describe('CDK Include', () => { const cfnBucket = cfnTemplate.getResource('Bucket'); expect(cfnBucket.cfnOptions.updatePolicy).toBeDefined(); - - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('resource-attribute-update-policy.json'), ); }); @@ -612,7 +624,7 @@ describe('CDK Include', () => { resources: [cfnBucket.attrArn], })); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ ...loadTestFileToJsObject('only-empty-bucket.json'), "Outputs": { "ExportsOutputFnGetAttBucketArn436138FE": { @@ -626,7 +638,7 @@ describe('CDK Include', () => { }, }); - expect(otherStack).toHaveResourceLike('AWS::IAM::Policy', { + Template.fromStack(otherStack).hasResourceProperties('AWS::IAM::Policy', { "PolicyDocument": { "Statement": [ { @@ -646,7 +658,7 @@ describe('CDK Include', () => { cfnKey.overrideLogicalId('TotallyDifferentKey'); const originalTemplate = loadTestFileToJsObject('bucket-with-encryption-key.json'); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ "Resources": { "Bucket": { "Type": "AWS::S3::Bucket", @@ -679,7 +691,7 @@ describe('CDK Include', () => { test('can include a template with a custom resource that uses attributes', () => { const cfnTemplate = includeTestTemplate(stack, 'custom-resource-with-attributes.json'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('custom-resource-with-attributes.json'), ); @@ -706,7 +718,7 @@ describe('CDK Include', () => { const originalTemplate = loadTestFileToJsObject('outputs-with-references.json'); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ "Conditions": { ...originalTemplate.Conditions, "MyCondition": { @@ -747,7 +759,7 @@ describe('CDK Include', () => { expect(output.value).toBeDefined(); expect(output.exportName).toBeDefined(); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('outputs-with-references.json'), ); }); @@ -766,7 +778,7 @@ describe('CDK Include', () => { someMapping.setValue('region', 'key2', 'value2'); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ "Mappings": { "SomeMapping": { "region": { @@ -803,7 +815,7 @@ describe('CDK Include', () => { test('can ingest a template that uses Fn::FindInMap with the first argument being a dynamic reference', () => { includeTestTemplate(stack, 'find-in-map-with-dynamic-mapping.json'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('find-in-map-with-dynamic-mapping.json'), ); }); @@ -814,7 +826,7 @@ describe('CDK Include', () => { someMapping.overrideLogicalId('DifferentMapping'); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ "Mappings": { "DifferentMapping": { "region": { @@ -842,7 +854,7 @@ describe('CDK Include', () => { test('can ingest a template that uses Fn::FindInMap for the value of a boolean property', () => { includeTestTemplate(stack, 'find-in-map-for-boolean-property.json'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('find-in-map-for-boolean-property.json'), ); }); @@ -853,7 +865,7 @@ describe('CDK Include', () => { expect(rule).toBeDefined(); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('only-parameters-and-rule.json'), ); }); @@ -881,7 +893,7 @@ describe('CDK Include', () => { const hook = cfnTemplate.getHook('EcsBlueGreenCodeDeployHook'); expect(hook).toBeDefined(); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('hook-code-deploy-blue-green-ecs.json'), ); }); @@ -901,7 +913,7 @@ describe('CDK Include', () => { }, }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ "Transform": { "Name": "AWS::Include", "Parameters": { @@ -948,7 +960,7 @@ describe('CDK Include', () => { }, }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ "Resources": { "Bucket": { "Type": "AWS::S3::Bucket", @@ -973,7 +985,7 @@ describe('CDK Include', () => { }, }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ "Resources": { "Bucket": { "Type": "AWS::S3::Bucket", @@ -994,7 +1006,7 @@ describe('CDK Include', () => { }, }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ "Resources": { "Bucket": { "Type": "AWS::S3::Bucket", @@ -1020,7 +1032,7 @@ describe('CDK Include', () => { }, }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ "Resources": { "Bucket": { "Type": "AWS::S3::Bucket", @@ -1065,7 +1077,7 @@ describe('CDK Include', () => { test('can ingest a template that contains properties not in the current CFN spec, and output it unchanged', () => { includeTestTemplate(stack, 'properties-not-in-cfn-spec.json'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('properties-not-in-cfn-spec.json'), ); }); diff --git a/packages/@aws-cdk/cloudformation-include/test/yaml-templates.test.ts b/packages/@aws-cdk/cloudformation-include/test/yaml-templates.test.ts index 06beafcdeb62c..4c693bd1026dc 100644 --- a/packages/@aws-cdk/cloudformation-include/test/yaml-templates.test.ts +++ b/packages/@aws-cdk/cloudformation-include/test/yaml-templates.test.ts @@ -1,5 +1,5 @@ import * as path from 'path'; -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import * as core from '@aws-cdk/core'; import * as constructs from 'constructs'; @@ -19,7 +19,7 @@ describe('CDK Include', () => { test('can ingest a template with all long-form CloudFormation functions and output it unchanged', () => { includeTestTemplate(stack, 'long-form-vpc.yaml'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('long-form-vpc.yaml'), ); }); @@ -27,7 +27,7 @@ describe('CDK Include', () => { test('can ingest a template with year-month-date parsed as string instead of Date', () => { includeTestTemplate(stack, 'year-month-date-as-strings.yaml'); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ "AWSTemplateFormatVersion": "2010-09-09", "Resources": { "Role": { @@ -54,7 +54,7 @@ describe('CDK Include', () => { test('can ingest a template with the short form Base64 function', () => { includeTestTemplate(stack, 'short-form-base64.yaml'); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ "Resources": { "Base64Bucket": { "Type": "AWS::S3::Bucket", @@ -71,7 +71,7 @@ describe('CDK Include', () => { test('can ingest a template with the short form !Cidr function', () => { includeTestTemplate(stack, 'short-form-cidr.yaml'); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ "Resources": { "CidrVpc1": { "Type": "AWS::EC2::VPC", @@ -104,7 +104,7 @@ describe('CDK Include', () => { test('can ingest a template with the short form !FindInMap function, in both hyphen and bracket notation', () => { includeTestTemplate(stack, 'short-form-find-in-map.yaml'); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ "Mappings": { "RegionMap": { "region-1": { @@ -145,7 +145,7 @@ describe('CDK Include', () => { test('can ingest a template with the short form !GetAtt function', () => { includeTestTemplate(stack, 'short-form-get-att.yaml'); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ "Resources": { "ELB": { "Type": "AWS::ElasticLoadBalancing::LoadBalancer", @@ -187,7 +187,7 @@ describe('CDK Include', () => { test('can ingest a template with short form Select, GetAZs, and Ref functions', () => { includeTestTemplate(stack, 'short-form-select.yaml'); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ "Resources": { "Subnet1": { "Type": "AWS::EC2::Subnet", @@ -220,7 +220,7 @@ describe('CDK Include', () => { test('can ingest a template with the short form !ImportValue function', () => { includeTestTemplate(stack, 'short-form-import-value.yaml'); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ "Resources": { "Bucket1": { "Type": "AWS::S3::Bucket", @@ -237,7 +237,7 @@ describe('CDK Include', () => { test('can ingest a template with the short form !Join function', () => { includeTestTemplate(stack, 'short-form-join.yaml'); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ "Resources": { "Bucket": { "Type": "AWS::S3::Bucket", @@ -257,7 +257,7 @@ describe('CDK Include', () => { test('can ingest a template with the short form !Split function that uses both brackets and hyphens', () => { includeTestTemplate(stack, 'short-form-split.yaml'); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ "Resources": { "Bucket1": { "Type": "AWS::S3::Bucket", @@ -287,7 +287,7 @@ describe('CDK Include', () => { // Note that this yaml template fails validation. It is unclear how to invoke !Transform. includeTestTemplate(stack, 'invalid/short-form-transform.yaml'); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ "Resources": { "Bucket": { "Type": "AWS::S3::Bucket", @@ -310,7 +310,7 @@ describe('CDK Include', () => { test('can ingest a template with the short form conditionals', () => { includeTestTemplate(stack, 'short-form-conditionals.yaml'); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ "Conditions": { "AlwaysTrueCond": { "Fn::And": [ @@ -348,7 +348,7 @@ describe('CDK Include', () => { test('can ingest a template with the short form Conditions', () => { includeTestTemplate(stack, 'short-form-conditions.yaml'); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ "Conditions": { "AlwaysTrueCond": { "Fn::Not": [ @@ -393,7 +393,7 @@ describe('CDK Include', () => { test('can ingest a yaml with long-form functions and output it unchanged', () => { includeTestTemplate(stack, 'long-form-subnet.yaml'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('long-form-subnet.yaml'), ); }); @@ -401,7 +401,7 @@ describe('CDK Include', () => { test('can ingest a YAML template with Fn::Sub in string form and output it unchanged', () => { includeTestTemplate(stack, 'short-form-fnsub-string.yaml'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('short-form-fnsub-string.yaml'), ); }); @@ -409,7 +409,7 @@ describe('CDK Include', () => { test('can ingest a YAML template with Fn::Sub in map form and output it unchanged', () => { includeTestTemplate(stack, 'short-form-sub-map.yaml'); - expect(stack).toMatchTemplate( + Template.fromStack(stack).templateMatches( loadTestFileToJsObject('short-form-sub-map.yaml'), ); }); From a2e89143a5cb8320e1d659180e6bf4d757116f0e Mon Sep 17 00:00:00 2001 From: Kaizen Conroy <36202692+kaizen3031593@users.noreply.github.com> Date: Wed, 19 Jan 2022 23:04:02 -0500 Subject: [PATCH 46/50] chore(codepipeline): migrate tests to assertions (#18545) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-codepipeline/package.json | 2 +- .../aws-codepipeline/test/action.test.ts | 37 +-------- .../aws-codepipeline/test/artifacts.test.ts | 20 +---- .../aws-codepipeline/test/cross-env.test.ts | 18 ++--- .../test/general-validation.test.ts | 9 --- .../test/notification-rule.test.ts | 10 +-- .../aws-codepipeline/test/pipeline.test.ts | 76 +++++++++---------- .../aws-codepipeline/test/stages.test.ts | 18 +---- .../aws-codepipeline/test/variables.test.ts | 31 +++----- 9 files changed, 74 insertions(+), 147 deletions(-) diff --git a/packages/@aws-cdk/aws-codepipeline/package.json b/packages/@aws-cdk/aws-codepipeline/package.json index b0baf652b6866..29d74c51f6628 100644 --- a/packages/@aws-cdk/aws-codepipeline/package.json +++ b/packages/@aws-cdk/aws-codepipeline/package.json @@ -84,7 +84,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-codepipeline/test/action.test.ts b/packages/@aws-cdk/aws-codepipeline/test/action.test.ts index 69a5839385fcd..a8b6529b3e80a 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/action.test.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/action.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; import * as codepipeline from '../lib'; @@ -10,54 +10,45 @@ import { FakeSourceAction } from './fake-source-action'; describe('action', () => { describe('artifact bounds validation', () => { - test('artifacts count exceed maximum', () => { const result = boundsValidationResult(1, 0, 0); expect(result.length).toEqual(1); expect(result[0]).toMatch(/cannot have more than 0/); - }); test('artifacts count below minimum', () => { const result = boundsValidationResult(1, 2, 2); expect(result.length).toEqual(1); expect(result[0]).toMatch(/must have at least 2/); - }); test('artifacts count within bounds', () => { const result = boundsValidationResult(1, 0, 2); expect(result.length).toEqual(0); - }); }); describe('action type validation', () => { - test('must be source and is source', () => { const result = validations.validateSourceAction(true, codepipeline.ActionCategory.SOURCE, 'test action', 'test stage'); expect(result.length).toEqual(0); - }); test('must be source and is not source', () => { const result = validations.validateSourceAction(true, codepipeline.ActionCategory.DEPLOY, 'test action', 'test stage'); expect(result.length).toEqual(1); expect(result[0]).toMatch(/may only contain Source actions/); - }); test('cannot be source and is source', () => { const result = validations.validateSourceAction(false, codepipeline.ActionCategory.SOURCE, 'test action', 'test stage'); expect(result.length).toEqual(1); expect(result[0]).toMatch(/may only occur in first stage/); - }); test('cannot be source and is not source', () => { const result = validations.validateSourceAction(false, codepipeline.ActionCategory.DEPLOY, 'test action', 'test stage'); expect(result.length).toEqual(0); - }); }); @@ -74,8 +65,6 @@ describe('action', () => { expect(() => { stage.addAction(action); }).toThrow(/Action name must match regular expression:/); - - }); }); @@ -121,11 +110,9 @@ describe('action', () => { }); expect(() => { - expect(stack).toHaveResourceLike('AWS::CodePipeline::Pipeline', { + Template.fromStack(stack).hasResourceProperties('AWS::CodePipeline::Pipeline', { }); }).toThrow(/Build\/Fake cannot have more than 3 input artifacts/); - - }); test('validates that output Artifacts are within bounds', () => { @@ -166,11 +153,9 @@ describe('action', () => { }); expect(() => { - expect(stack).toHaveResourceLike('AWS::CodePipeline::Pipeline', { + Template.fromStack(stack).hasResourceProperties('AWS::CodePipeline::Pipeline', { }); }).toThrow(/Source\/Fake cannot have more than 4 output artifacts/); - - }); }); @@ -199,7 +184,7 @@ describe('action', () => { ], }); - expect(stack).toHaveResourceLike('AWS::CodePipeline::Pipeline', { + Template.fromStack(stack).hasResourceProperties('AWS::CodePipeline::Pipeline', { 'Stages': [ { 'Name': 'Source', @@ -234,8 +219,6 @@ describe('action', () => { }, ], }); - - }); test('the same Action can be safely added to 2 different Stages', () => { @@ -270,8 +253,6 @@ describe('action', () => { expect(() => { pipeline.addStage(stage3); }).not.toThrow(/FakeAction/); - - }); describe('input Artifacts', () => { @@ -285,8 +266,6 @@ describe('action', () => { extraInputs: [artifact], }); }).not.toThrow(); - - }); test('can have duplicate names', () => { @@ -300,8 +279,6 @@ describe('action', () => { extraInputs: [artifact2], }); }).not.toThrow(); - - }); }); @@ -317,8 +294,6 @@ describe('action', () => { ], }); }).not.toThrow(); - - }); }); @@ -355,8 +330,6 @@ describe('action', () => { expect(() => { buildStage.addAction(buildAction); }).toThrow(/Role is not supported for actions with an owner different than 'AWS' - got 'ThirdParty' \(Action: 'build' in Stage: 'Build'\)/); - - }); test('actions can be retrieved from stages they have been added to', () => { @@ -399,8 +372,6 @@ describe('action', () => { expect(buildStage.actions.length).toEqual(2); expect(buildStage.actions[0].actionProperties.actionName).toEqual('build1'); expect(buildStage.actions[1].actionProperties.actionName).toEqual('build2'); - - }); }); diff --git a/packages/@aws-cdk/aws-codepipeline/test/artifacts.test.ts b/packages/@aws-cdk/aws-codepipeline/test/artifacts.test.ts index 4570ea850df3a..9e2e62a6aedda 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/artifacts.test.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/artifacts.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as cdk from '@aws-cdk/core'; import * as codepipeline from '../lib'; import { FakeBuildAction } from './fake-build-action'; @@ -10,8 +10,6 @@ describe('artifacts', () => { describe('Artifacts in CodePipeline', () => { test('cannot be created with an empty name', () => { expect(() => new codepipeline.Artifact('')).toThrow(/Artifact name must match regular expression/); - - }); test('without a name, when used as an input without being used as an output first - should fail validation', () => { @@ -45,8 +43,6 @@ describe('artifacts', () => { expect(errors.length).toEqual(1); const error = errors[0]; expect(error).toMatch(/Action 'Build' is using an unnamed input Artifact, which is not being produced in this pipeline/); - - }); test('with a name, when used as an input without being used as an output first - should fail validation', () => { @@ -80,8 +76,6 @@ describe('artifacts', () => { expect(errors.length).toEqual(1); const error = errors[0]; expect(error).toMatch(/Action 'Build' is using input Artifact 'named', which is not being produced in this pipeline/); - - }); test('without a name, when used as an output multiple times - should fail validation', () => { @@ -116,8 +110,6 @@ describe('artifacts', () => { expect(errors.length).toEqual(1); const error = errors[0]; expect(error).toMatch(/Both Actions 'Source' and 'Build' are producting Artifact 'Artifact_Source_Source'. Every artifact can only be produced once./); - - }); test("an Action's output can be used as input for an Action in the same Stage with a higher runOrder", () => { @@ -162,9 +154,7 @@ describe('artifacts', () => { ], }); - expect(stack).toHaveResourceLike('AWS::CodePipeline::Pipeline'); - - + Template.fromStack(stack).resourceCountIs('AWS::CodePipeline::Pipeline', 1); }); test('violation of runOrder constraints is detected and reported', () => { @@ -215,8 +205,6 @@ describe('artifacts', () => { expect(errors.length).toEqual(1); const error = errors[0]; expect(error).toMatch(/Stage 2 Action 2 \('Build'\/'build2'\) is consuming input Artifact 'buildOutput1' before it is being produced at Stage 2 Action 3 \('Build'\/'build1'\)/); - - }); test('without a name, sanitize the auto stage-action derived name', () => { @@ -246,7 +234,7 @@ describe('artifacts', () => { ], }); - expect(stack).toHaveResourceLike('AWS::CodePipeline::Pipeline', { + Template.fromStack(stack).hasResourceProperties('AWS::CodePipeline::Pipeline', { 'Stages': [ { 'Name': 'Source.@', @@ -272,8 +260,6 @@ describe('artifacts', () => { }, ], }); - - }); }); }); diff --git a/packages/@aws-cdk/aws-codepipeline/test/cross-env.test.ts b/packages/@aws-cdk/aws-codepipeline/test/cross-env.test.ts index f26de209ecaf9..9e2608dd36f36 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/cross-env.test.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/cross-env.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import * as s3 from '@aws-cdk/aws-s3'; import { Stack, App, Stage as CdkStage } from '@aws-cdk/core'; @@ -58,8 +58,8 @@ describe.each([ test('creates a bucket but no keys', () => { // THEN - expect(stack).not.toHaveResource('AWS::KMS::Key'); - expect(stack).toHaveResource('AWS::S3::Bucket'); + Template.fromStack(stack).resourceCountIs('AWS::KMS::Key', 0); + Template.fromStack(stack).resourceCountIs('AWS::S3::Bucket', 1); }); describe('prevents adding a cross-account action', () => { @@ -128,8 +128,8 @@ describe.each([ const supportStack = asm.getStackByName(`${stack.stackName}-support-eu-west-1`); // THEN - expect(supportStack).not.toHaveResource('AWS::KMS::Key'); - expect(supportStack).toHaveResourceLike('AWS::S3::Bucket', { + Template.fromJSON(supportStack.template).resourceCountIs('AWS::KMS::Key', 0); + Template.fromJSON(supportStack.template).hasResourceProperties('AWS::S3::Bucket', { PublicAccessBlockConfiguration: { BlockPublicAcls: true, BlockPublicPolicy: true, @@ -150,8 +150,8 @@ describe.each([ })); // THEN - expect(stack2).not.toHaveResource('AWS::KMS::Key'); - expect(stack2).toHaveResource('AWS::S3::Bucket'); + Template.fromStack(stack2).resourceCountIs('AWS::KMS::Key', 0); + Template.fromStack(stack2).resourceCountIs('AWS::S3::Bucket', 1); }); }); }); @@ -200,11 +200,11 @@ describe('cross-environment CodePipeline', function () { const asm = app.synth(); const supportStack = asm.getStackByName(`${pipelineStack.stackName}-support-456`); - expect(supportStack).toHaveResourceLike('AWS::IAM::Role', { + Template.fromJSON(supportStack.template).hasResourceProperties('AWS::IAM::Role', { RoleName: 'pipelinestack-support-456dbuildactionrole91c6f1a469fd11d52dfe', }); - expect(pipelineStack).toHaveResourceLike('AWS::CodePipeline::Pipeline', { + Template.fromStack(pipelineStack).hasResourceProperties('AWS::CodePipeline::Pipeline', { Stages: [ { Name: 'Source' }, { diff --git a/packages/@aws-cdk/aws-codepipeline/test/general-validation.test.ts b/packages/@aws-cdk/aws-codepipeline/test/general-validation.test.ts index d8186532d7cea..f3024ec163e9b 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/general-validation.test.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/general-validation.test.ts @@ -1,4 +1,3 @@ -import '@aws-cdk/assert-internal/jest'; import * as cdk from '@aws-cdk/core'; import { IStage } from '../lib/action'; import { Artifact } from '../lib/artifact'; @@ -30,8 +29,6 @@ describe('general validation', () => { expect(validationBlock).toThrow(); } }); - - }); describe('Stage validation', () => { @@ -39,8 +36,6 @@ describe('general validation', () => { const stage = stageForTesting(); expect((stage as any).validate().length).toEqual(1); - - }); }); @@ -50,8 +45,6 @@ describe('general validation', () => { const pipeline = new Pipeline(stack, 'Pipeline'); expect(cdk.ConstructNode.validate(pipeline.node).length).toEqual(1); - - }); test('should fail if Pipeline has a Source Action in a non-first Stage', () => { @@ -69,8 +62,6 @@ describe('general validation', () => { }); expect(cdk.ConstructNode.validate(pipeline.node).length).toEqual(1); - - }); }); }); diff --git a/packages/@aws-cdk/aws-codepipeline/test/notification-rule.test.ts b/packages/@aws-cdk/aws-codepipeline/test/notification-rule.test.ts index 46d32794dc3e1..ac178204dd263 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/notification-rule.test.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/notification-rule.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as cdk from '@aws-cdk/core'; import * as codepipeline from '../lib'; import { FakeBuildAction } from './fake-build-action'; @@ -35,7 +35,7 @@ describe('pipeline with codestar notification integration', () => { pipeline.notifyOnExecutionStateChange('NotifyOnExecutionStateChange', target); - expect(stack).toHaveResourceLike('AWS::CodeStarNotifications::NotificationRule', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeStarNotifications::NotificationRule', { Name: 'PipelineNotifyOnExecutionStateChange9FE60973', DetailType: 'FULL', EventTypeIds: [ @@ -77,7 +77,7 @@ describe('pipeline with codestar notification integration', () => { pipeline.notifyOnAnyStageStateChange('NotifyOnAnyStageStateChange', target); - expect(stack).toHaveResourceLike('AWS::CodeStarNotifications::NotificationRule', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeStarNotifications::NotificationRule', { Name: 'PipelineNotifyOnAnyStageStateChange05355CCD', DetailType: 'FULL', EventTypeIds: [ @@ -118,7 +118,7 @@ describe('pipeline with codestar notification integration', () => { pipeline.notifyOnAnyActionStateChange('NotifyOnAnyActionStateChange', target); - expect(stack).toHaveResourceLike('AWS::CodeStarNotifications::NotificationRule', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeStarNotifications::NotificationRule', { Name: 'PipelineNotifyOnAnyActionStateChange64D5B2AA', DetailType: 'FULL', EventTypeIds: [ @@ -158,7 +158,7 @@ describe('pipeline with codestar notification integration', () => { pipeline.notifyOnAnyManualApprovalStateChange('NotifyOnAnyManualApprovalStateChange', target); - expect(stack).toHaveResourceLike('AWS::CodeStarNotifications::NotificationRule', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeStarNotifications::NotificationRule', { Name: 'PipelineNotifyOnAnyManualApprovalStateChangeE60778F7', DetailType: 'FULL', EventTypeIds: [ diff --git a/packages/@aws-cdk/aws-codepipeline/test/pipeline.test.ts b/packages/@aws-cdk/aws-codepipeline/test/pipeline.test.ts index 5af1eea557f3f..dd4f79d040201 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/pipeline.test.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/pipeline.test.ts @@ -1,5 +1,4 @@ -import { expect as ourExpect, ResourcePart, arrayWith, objectLike, haveResourceLike } from '@aws-cdk/assert-internal'; -import '@aws-cdk/assert-internal/jest'; +import { Match, Template } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; import * as s3 from '@aws-cdk/aws-s3'; @@ -22,20 +21,36 @@ describe('', () => { const role = new iam.Role(stack, 'Role', { assumedBy: new iam.ServicePrincipal('codepipeline.amazonaws.com'), }); - new codepipeline.Pipeline(stack, 'Pipeline', { + const pipeline = new codepipeline.Pipeline(stack, 'Pipeline', { role, }); - ourExpect(stack, true).to(haveResourceLike('AWS::CodePipeline::Pipeline', { + // Adding 2 stages with actions so pipeline validation will pass + const sourceArtifact = new codepipeline.Artifact(); + pipeline.addStage({ + stageName: 'Source', + actions: [new FakeSourceAction({ + actionName: 'FakeSource', + output: sourceArtifact, + })], + }); + + pipeline.addStage({ + stageName: 'Build', + actions: [new FakeBuildAction({ + actionName: 'FakeBuild', + input: sourceArtifact, + })], + }); + + Template.fromStack(stack).hasResourceProperties('AWS::CodePipeline::Pipeline', { 'RoleArn': { 'Fn::GetAtt': [ 'Role1ABCC5F0', 'Arn', ], }, - })); - - + }); }); test('can be imported by ARN', () => { @@ -46,8 +61,6 @@ describe('', () => { expect(pipeline.pipelineArn).toEqual('arn:aws:codepipeline:us-east-1:123456789012:MyPipeline'); expect(pipeline.pipelineName).toEqual('MyPipeline'); - - }); describe('that is cross-region', () => { @@ -67,8 +80,6 @@ describe('', () => { expect(() => { sourceStage.addAction(sourceAction); }).toThrow(/Source action 'FakeSource' must be in the same region as the pipeline/); - - }); test('allows passing an Alias in place of the KMS Key in the replication Bucket', () => { @@ -113,7 +124,7 @@ describe('', () => { ], }); - expect(pipelineStack).toHaveResourceLike('AWS::CodePipeline::Pipeline', { + Template.fromStack(pipelineStack).hasResourceProperties('AWS::CodePipeline::Pipeline', { 'ArtifactStores': [ { 'Region': replicationRegion, @@ -142,7 +153,7 @@ describe('', () => { ], }); - expect(replicationStack).toHaveResourceLike('AWS::KMS::Key', { + Template.fromStack(replicationStack).hasResourceProperties('AWS::KMS::Key', { 'KeyPolicy': { 'Statement': [ { @@ -175,8 +186,6 @@ describe('', () => { ], }, }); - - }); test('generates ArtifactStores with the alias ARN as the KeyID', () => { @@ -208,7 +217,7 @@ describe('', () => { ], }); - expect(pipelineStack).toHaveResourceLike('AWS::CodePipeline::Pipeline', { + Template.fromStack(pipelineStack).hasResourceProperties('AWS::CodePipeline::Pipeline', { 'ArtifactStores': [ { 'Region': replicationRegion, @@ -237,12 +246,10 @@ describe('', () => { ], }); - expect(pipeline.crossRegionSupport[replicationRegion].stack).toHaveResourceLike('AWS::KMS::Alias', { + Template.fromStack(pipeline.crossRegionSupport[replicationRegion].stack).hasResource('AWS::KMS::Alias', { 'DeletionPolicy': 'Delete', 'UpdateReplacePolicy': 'Delete', - }, ResourcePart.CompleteDefinition); - - + }); }); test('allows passing an imported Bucket and Key for the replication Bucket', () => { @@ -280,7 +287,7 @@ describe('', () => { ], }); - expect(pipelineStack).toHaveResourceLike('AWS::CodePipeline::Pipeline', { + Template.fromStack(pipelineStack).hasResourceProperties('AWS::CodePipeline::Pipeline', { 'ArtifactStores': [ { 'Region': replicationRegion, @@ -298,8 +305,6 @@ describe('', () => { }, ], }); - - }); test('generates the support stack containing the replication Bucket without the need to bootstrap in that environment', () => { @@ -380,18 +385,18 @@ describe('', () => { const supportStackBArtifact = assembly.getStackByName('PipelineStackB-support-eu-south-1'); const supportStackATemplate = supportStackAArtifact.template; - expect(supportStackATemplate).toHaveResourceLike('AWS::S3::Bucket', { + Template.fromJSON(supportStackATemplate).hasResourceProperties('AWS::S3::Bucket', { BucketName: 'pipelinestacka-support-eueplicationbucket8934e91f26961aa6cbfa', }); - expect(supportStackATemplate).toHaveResourceLike('AWS::KMS::Alias', { + Template.fromJSON(supportStackATemplate).hasResourceProperties('AWS::KMS::Alias', { AliasName: 'alias/pport-eutencryptionalias02f1cda3732942f6c529', }); const supportStackBTemplate = supportStackBArtifact.template; - expect(supportStackBTemplate).toHaveResourceLike('AWS::S3::Bucket', { + Template.fromJSON(supportStackBTemplate).hasResourceProperties('AWS::S3::Bucket', { BucketName: 'pipelinestackb-support-eueplicationbucketdf7c0e10245faa377228', }); - expect(supportStackBTemplate).toHaveResourceLike('AWS::KMS::Alias', { + Template.fromJSON(supportStackBTemplate).hasResourceProperties('AWS::KMS::Alias', { AliasName: 'alias/pport-eutencryptionaliasdef3fd3fec63bc54980e', }); }); @@ -419,8 +424,6 @@ describe('', () => { account: cdk.Aws.ACCOUNT_ID, })); }).toThrow(/The 'account' property must be a concrete value \(action: 'FakeBuild'\)/); - - }); test('does not allow an env-agnostic Pipeline Stack if an Action account has been provided', () => { @@ -444,8 +447,6 @@ describe('', () => { account: '123456789012', })); }).toThrow(/Pipeline stack which uses cross-environment actions must have an explicitly set account/); - - }); test('does not allow enabling key rotation if cross account keys have been disabled', () => { @@ -478,7 +479,7 @@ describe('', () => { ], }); - expect(stack).toHaveResourceLike('AWS::KMS::Key', { + Template.fromStack(stack).hasResourceProperties('AWS::KMS::Key', { 'EnableKeyRotation': true, }); }); @@ -513,19 +514,18 @@ describe('test with shared setup', () => { pipeline.stage('Build').addAction(new FakeBuildAction({ actionName: 'debug.com', input: sourceArtifact })); // THEN - expect(stack).toHaveResourceLike('AWS::CodePipeline::Pipeline', { - Stages: arrayWith({ + Template.fromStack(stack).hasResourceProperties('AWS::CodePipeline::Pipeline', { + Stages: Match.arrayWith([{ Name: 'Build', Actions: [ - objectLike({ Name: 'Gcc' }), - objectLike({ Name: 'debug.com' }), + Match.objectLike({ Name: 'Gcc' }), + Match.objectLike({ Name: 'debug.com' }), ], - }), + }]), }); }); }); - interface ReusePipelineStackProps extends cdk.StackProps { reuseCrossRegionSupportStacks?: boolean; } diff --git a/packages/@aws-cdk/aws-codepipeline/test/stages.test.ts b/packages/@aws-cdk/aws-codepipeline/test/stages.test.ts index d7931a23a531e..ca5882a4c42a6 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/stages.test.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/stages.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as cdk from '@aws-cdk/core'; import * as codepipeline from '../lib'; import { Stage } from '../lib/private/stage'; @@ -33,14 +33,12 @@ describe('stages', () => { })); // -- - expect(stack).toHaveResourceLike('AWS::CodePipeline::Pipeline', { + Template.fromStack(stack).hasResourceProperties('AWS::CodePipeline::Pipeline', { 'Stages': [ { 'Name': 'FirstStage' }, { 'Name': 'SecondStage' }, ], }); - - }); test('can be inserted after another Stage', () => { @@ -72,15 +70,13 @@ describe('stages', () => { })); // -- - expect(stack).toHaveResourceLike('AWS::CodePipeline::Pipeline', { + Template.fromStack(stack).hasResourceProperties('AWS::CodePipeline::Pipeline', { 'Stages': [ { 'Name': 'FirstStage' }, { 'Name': 'SecondStage' }, { 'Name': 'ThirdStage' }, ], }); - - }); test("attempting to insert a Stage before a Stage that doesn't exist results in an error", () => { @@ -97,8 +93,6 @@ describe('stages', () => { }, }); }).toThrow(/before/i); - - }); test("attempting to insert a Stage after a Stage that doesn't exist results in an error", () => { @@ -115,8 +109,6 @@ describe('stages', () => { }, }); }).toThrow(/after/i); - - }); test('providing more than one placement value results in an error', () => { @@ -135,8 +127,6 @@ describe('stages', () => { // incredibly, an arrow function below causes nodeunit to crap out with: // "TypeError: Function has non-object prototype 'undefined' in instanceof check" }).toThrow(/(rightBefore.*justAfter)|(justAfter.*rightBefore)/); - - }); test('can be retrieved from a pipeline after it has been created', () => { @@ -160,8 +150,6 @@ describe('stages', () => { stageName: 'ThirdStage', }, pipeline)); expect(pipeline.stageCount).toEqual(2); - - }); }); }); diff --git a/packages/@aws-cdk/aws-codepipeline/test/variables.test.ts b/packages/@aws-cdk/aws-codepipeline/test/variables.test.ts index 537b108df805a..6d70847bec853 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/variables.test.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/variables.test.ts @@ -1,5 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; -import { arrayWith, objectLike } from '@aws-cdk/assert-internal'; +import { Match, Template } from '@aws-cdk/assertions'; import * as cdk from '@aws-cdk/core'; import * as codepipeline from '../lib'; import { FakeBuildAction } from './fake-build-action'; @@ -38,21 +37,19 @@ describe('variables', () => { })); // -- - expect(stack).toHaveResourceLike('AWS::CodePipeline::Pipeline', { - 'Stages': [ + Template.fromStack(stack).hasResourceProperties('AWS::CodePipeline::Pipeline', { + 'Stages': Match.arrayWith([ { 'Name': 'Source', 'Actions': [ - { + Match.objectLike({ 'Name': 'Source', 'Namespace': 'MyNamespace', - }, + }), ], }, - ], + ]), }); - - }); test('allows using the variable in the configuration of a different action', () => { @@ -80,7 +77,7 @@ describe('variables', () => { ], }); - expect(stack).toHaveResourceLike('AWS::CodePipeline::Pipeline', { + Template.fromStack(stack).hasResourceProperties('AWS::CodePipeline::Pipeline', { 'Stages': [ { 'Name': 'Source', @@ -98,8 +95,6 @@ describe('variables', () => { }, ], }); - - }); test('fails when trying add an action using variables with an empty string for the namespace to a pipeline', () => { @@ -116,8 +111,6 @@ describe('variables', () => { expect(() => { sourceStage.addAction(sourceAction); }).toThrow(/Namespace name must match regular expression:/); - - }); test('can use global variables', () => { @@ -144,12 +137,12 @@ describe('variables', () => { ], }); - expect(stack).toHaveResourceLike('AWS::CodePipeline::Pipeline', { - 'Stages': arrayWith( + Template.fromStack(stack).hasResourceProperties('AWS::CodePipeline::Pipeline', { + 'Stages': Match.arrayWith([ { 'Name': 'Build', 'Actions': [ - objectLike({ + Match.objectLike({ 'Name': 'Build', 'Configuration': { 'CustomConfigKey': '#{codepipeline.PipelineExecutionId}', @@ -157,10 +150,8 @@ describe('variables', () => { }), ], }, - ), + ]), }); - - }); }); }); From 4764f5b0c8450a8e4fe2a572b16a4851b96495d1 Mon Sep 17 00:00:00 2001 From: Madeline Kusters <80541297+madeline-k@users.noreply.github.com> Date: Thu, 20 Jan 2022 01:52:42 -0800 Subject: [PATCH 47/50] chore(ecr): migrate to Assertions (#18538) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-ecr/package.json | 2 +- .../@aws-cdk/aws-ecr/test/auth-token.test.ts | 10 +- .../@aws-cdk/aws-ecr/test/repository.test.ts | 179 +++++++++++------- 3 files changed, 112 insertions(+), 79 deletions(-) diff --git a/packages/@aws-cdk/aws-ecr/package.json b/packages/@aws-cdk/aws-ecr/package.json index e8e9c3402eed3..e2b8bccc7c29f 100644 --- a/packages/@aws-cdk/aws-ecr/package.json +++ b/packages/@aws-cdk/aws-ecr/package.json @@ -83,7 +83,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-ecr/test/auth-token.test.ts b/packages/@aws-cdk/aws-ecr/test/auth-token.test.ts index f9be93b1e15d0..421ba7c2f6645 100644 --- a/packages/@aws-cdk/aws-ecr/test/auth-token.test.ts +++ b/packages/@aws-cdk/aws-ecr/test/auth-token.test.ts @@ -1,4 +1,4 @@ -import { expect as expectCDK, haveResourceLike } from '@aws-cdk/assert-internal'; +import { Template } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import { Stack } from '@aws-cdk/core'; import { AuthorizationToken, PublicGalleryAuthorizationToken } from '../lib'; @@ -13,7 +13,7 @@ describe('auth-token', () => { AuthorizationToken.grantRead(user); // THEN - expectCDK(stack).to(haveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -23,7 +23,7 @@ describe('auth-token', () => { }, ], }, - })); + }); }); test('PublicGalleryAuthorizationToken.grantRead()', () => { @@ -35,7 +35,7 @@ describe('auth-token', () => { PublicGalleryAuthorizationToken.grantRead(user); // THEN - expectCDK(stack).to(haveResourceLike('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { @@ -48,6 +48,6 @@ describe('auth-token', () => { }, ], }, - })); + }); }); }); diff --git a/packages/@aws-cdk/aws-ecr/test/repository.test.ts b/packages/@aws-cdk/aws-ecr/test/repository.test.ts index 37cc58a8485eb..470638d89355e 100644 --- a/packages/@aws-cdk/aws-ecr/test/repository.test.ts +++ b/packages/@aws-cdk/aws-ecr/test/repository.test.ts @@ -1,5 +1,5 @@ import { EOL } from 'os'; -import { expect as expectCDK, haveResource, haveResourceLike, ResourcePart } from '@aws-cdk/assert-internal'; +import { Template } from '@aws-cdk/assertions'; import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; import * as ecr from '../lib'; @@ -15,7 +15,7 @@ describe('repository', () => { new ecr.Repository(stack, 'Repo'); // THEN - expectCDK(stack).toMatch({ + Template.fromStack(stack).templateMatches({ Resources: { Repo02AC86CF: { Type: 'AWS::ECR::Repository', @@ -34,11 +34,11 @@ describe('repository', () => { new ecr.Repository(stack, 'Repo', { imageScanOnPush: true }); // THEN - expectCDK(stack).to(haveResource('AWS::ECR::Repository', { + Template.fromStack(stack).hasResourceProperties('AWS::ECR::Repository', { ImageScanningConfiguration: { ScanOnPush: true, }, - })); + }); }); test('tag-based lifecycle policy', () => { @@ -50,12 +50,12 @@ describe('repository', () => { repo.addLifecycleRule({ tagPrefixList: ['abc'], maxImageCount: 1 }); // THEN - expectCDK(stack).to(haveResource('AWS::ECR::Repository', { + Template.fromStack(stack).hasResourceProperties('AWS::ECR::Repository', { LifecyclePolicy: { // eslint-disable-next-line max-len LifecyclePolicyText: '{"rules":[{"rulePriority":1,"selection":{"tagStatus":"tagged","tagPrefixList":["abc"],"countType":"imageCountMoreThan","countNumber":1},"action":{"type":"expire"}}]}', }, - })); + }); }); test('image tag mutability can be set', () => { @@ -64,9 +64,9 @@ describe('repository', () => { new ecr.Repository(stack, 'Repo', { imageTagMutability: ecr.TagMutability.IMMUTABLE }); // THEN - expectCDK(stack).to(haveResource('AWS::ECR::Repository', { + Template.fromStack(stack).hasResourceProperties('AWS::ECR::Repository', { ImageTagMutability: 'IMMUTABLE', - })); + }); }); test('add day-based lifecycle policy', () => { @@ -80,12 +80,12 @@ describe('repository', () => { }); // THEN - expectCDK(stack).to(haveResource('AWS::ECR::Repository', { + Template.fromStack(stack).hasResourceProperties('AWS::ECR::Repository', { LifecyclePolicy: { // eslint-disable-next-line max-len LifecyclePolicyText: '{"rules":[{"rulePriority":1,"selection":{"tagStatus":"any","countType":"sinceImagePushed","countNumber":5,"countUnit":"days"},"action":{"type":"expire"}}]}', }, - })); + }); }); test('add count-based lifecycle policy', () => { @@ -99,12 +99,12 @@ describe('repository', () => { }); // THEN - expectCDK(stack).to(haveResource('AWS::ECR::Repository', { + Template.fromStack(stack).hasResourceProperties('AWS::ECR::Repository', { LifecyclePolicy: { // eslint-disable-next-line max-len LifecyclePolicyText: '{"rules":[{"rulePriority":1,"selection":{"tagStatus":"any","countType":"imageCountMoreThan","countNumber":5},"action":{"type":"expire"}}]}', }, - })); + }); }); test('mixing numbered and unnumbered rules', () => { @@ -117,12 +117,12 @@ describe('repository', () => { repo.addLifecycleRule({ rulePriority: 10, tagStatus: ecr.TagStatus.TAGGED, tagPrefixList: ['b'], maxImageCount: 5 }); // THEN - expectCDK(stack).to(haveResource('AWS::ECR::Repository', { + Template.fromStack(stack).hasResourceProperties('AWS::ECR::Repository', { LifecyclePolicy: { // eslint-disable-next-line max-len LifecyclePolicyText: '{"rules":[{"rulePriority":10,"selection":{"tagStatus":"tagged","tagPrefixList":["b"],"countType":"imageCountMoreThan","countNumber":5},"action":{"type":"expire"}},{"rulePriority":11,"selection":{"tagStatus":"tagged","tagPrefixList":["a"],"countType":"imageCountMoreThan","countNumber":5},"action":{"type":"expire"}}]}', }, - })); + }); }); test('tagstatus Any is automatically sorted to the back', () => { @@ -135,12 +135,12 @@ describe('repository', () => { repo.addLifecycleRule({ tagStatus: ecr.TagStatus.TAGGED, tagPrefixList: ['important'], maxImageCount: 999 }); // THEN - expectCDK(stack).to(haveResource('AWS::ECR::Repository', { + Template.fromStack(stack).hasResourceProperties('AWS::ECR::Repository', { LifecyclePolicy: { // eslint-disable-next-line max-len LifecyclePolicyText: '{"rules":[{"rulePriority":1,"selection":{"tagStatus":"tagged","tagPrefixList":["important"],"countType":"imageCountMoreThan","countNumber":999},"action":{"type":"expire"}},{"rulePriority":2,"selection":{"tagStatus":"any","countType":"imageCountMoreThan","countNumber":5},"action":{"type":"expire"}}]}', }, - })); + }); }); test('lifecycle rules can be added upon initialization', () => { @@ -155,12 +155,12 @@ describe('repository', () => { }); // THEN - expectCDK(stack).to(haveResource('AWS::ECR::Repository', { + Template.fromStack(stack).hasResourceProperties('AWS::ECR::Repository', { 'LifecyclePolicy': { // eslint-disable-next-line max-len 'LifecyclePolicyText': '{"rules":[{"rulePriority":1,"selection":{"tagStatus":"any","countType":"imageCountMoreThan","countNumber":3},"action":{"type":"expire"}}]}', }, - })); + }); }); test('calculate repository URI', () => { @@ -168,21 +168,24 @@ describe('repository', () => { const stack = new cdk.Stack(); const repo = new ecr.Repository(stack, 'Repo'); - // WHEN - const uri = repo.repositoryUri; + new cdk.CfnOutput(stack, 'RepoUri', { + value: repo.repositoryUri, + }); // THEN const arnSplit = { 'Fn::Split': [':', { 'Fn::GetAtt': ['Repo02AC86CF', 'Arn'] }] }; - expectCDK(stack.resolve(uri)).toMatch({ - 'Fn::Join': ['', [ - { 'Fn::Select': [4, arnSplit] }, - '.dkr.ecr.', - { 'Fn::Select': [3, arnSplit] }, - '.', - { Ref: 'AWS::URLSuffix' }, - '/', - { Ref: 'Repo02AC86CF' }, - ]], + Template.fromStack(stack).hasOutput('*', { + 'Value': { + 'Fn::Join': ['', [ + { 'Fn::Select': [4, arnSplit] }, + '.dkr.ecr.', + { 'Fn::Select': [3, arnSplit] }, + '.', + { Ref: 'AWS::URLSuffix' }, + '/', + { Ref: 'Repo02AC86CF' }, + ]], + }, }); }); @@ -217,10 +220,20 @@ describe('repository', () => { repositoryArn: cdk.Fn.getAtt('Boom', 'Arn').toString(), repositoryName: cdk.Fn.getAtt('Boom', 'Name').toString(), }); + new cdk.CfnOutput(stack, 'RepoArn', { + value: repo.repositoryArn, + }); + new cdk.CfnOutput(stack, 'RepoName', { + value: repo.repositoryName, + }); // THEN - expectCDK(stack.resolve(repo.repositoryArn)).toMatch({ 'Fn::GetAtt': ['Boom', 'Arn'] }); - expectCDK(stack.resolve(repo.repositoryName)).toMatch({ 'Fn::GetAtt': ['Boom', 'Name'] }); + Template.fromStack(stack).hasOutput('*', { + Value: { 'Fn::GetAtt': ['Boom', 'Arn'] }, + }); + Template.fromStack(stack).hasOutput('*', { + Value: { 'Fn::GetAtt': ['Boom', 'Name'] }, + }); }); test('import only with a repository name (arn is deduced)', () => { @@ -229,20 +242,30 @@ describe('repository', () => { // WHEN const repo = ecr.Repository.fromRepositoryName(stack, 'just-name', 'my-repo'); + new cdk.CfnOutput(stack, 'RepoArn', { + value: repo.repositoryArn, + }); + new cdk.CfnOutput(stack, 'RepoName', { + value: repo.repositoryName, + }); // THEN - expectCDK(stack.resolve(repo.repositoryArn)).toMatch({ - 'Fn::Join': ['', [ - 'arn:', - { Ref: 'AWS::Partition' }, - ':ecr:', - { Ref: 'AWS::Region' }, - ':', - { Ref: 'AWS::AccountId' }, - ':repository/my-repo', - ]], - }); - expect(stack.resolve(repo.repositoryName)).toBe('my-repo'); + Template.fromStack(stack).hasOutput('*', { + Value: { + 'Fn::Join': ['', [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':ecr:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':repository/my-repo', + ]], + }, + }); + Template.fromStack(stack).hasOutput('*', { + Value: 'my-repo', + }); }); test('arnForLocalRepository can be used to render an ARN for a local repository', () => { @@ -255,20 +278,30 @@ describe('repository', () => { repositoryArn: ecr.Repository.arnForLocalRepository(repoName, stack), repositoryName: repoName, }); + new cdk.CfnOutput(stack, 'RepoArn', { + value: repo.repositoryArn, + }); + new cdk.CfnOutput(stack, 'RepoName', { + value: repo.repositoryName, + }); // THEN - expectCDK(stack.resolve(repo.repositoryName)).toMatch({ 'Fn::GetAtt': ['Boom', 'Name'] }); - expectCDK(stack.resolve(repo.repositoryArn)).toMatch({ - 'Fn::Join': ['', [ - 'arn:', - { Ref: 'AWS::Partition' }, - ':ecr:', - { Ref: 'AWS::Region' }, - ':', - { Ref: 'AWS::AccountId' }, - ':repository/', - { 'Fn::GetAtt': ['Boom', 'Name'] }, - ]], + Template.fromStack(stack).hasOutput('*', { + Value: { 'Fn::GetAtt': ['Boom', 'Name'] }, + }); + Template.fromStack(stack).hasOutput('*', { + Value: { + 'Fn::Join': ['', [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':ecr:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':repository/', + { 'Fn::GetAtt': ['Boom', 'Name'] }, + ]], + }, }); }); @@ -284,7 +317,7 @@ describe('repository', () => { })); // THEN - expectCDK(stack).to(haveResource('AWS::ECR::Repository', { + Template.fromStack(stack).hasResourceProperties('AWS::ECR::Repository', { RepositoryPolicyText: { Statement: [ { @@ -295,7 +328,7 @@ describe('repository', () => { ], Version: '2012-10-17', }, - })); + }); }); test('fails if repository policy has no actions', () => { @@ -341,7 +374,7 @@ describe('repository', () => { }, }); - expectCDK(stack).to(haveResourceLike('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { 'EventPattern': { 'source': [ 'aws.ecr', @@ -360,7 +393,7 @@ describe('repository', () => { }, }, 'State': 'ENABLED', - })); + }); }); test('onImageScanCompleted without imageTags creates the correct event', () => { @@ -373,7 +406,7 @@ describe('repository', () => { }, }); - expectCDK(stack).to(haveResourceLike('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { 'EventPattern': { 'source': [ 'aws.ecr', @@ -390,7 +423,7 @@ describe('repository', () => { }, }, 'State': 'ENABLED', - })); + }); }); test('onImageScanCompleted with one imageTag creates the correct event', () => { @@ -404,7 +437,7 @@ describe('repository', () => { }, }); - expectCDK(stack).to(haveResourceLike('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { 'EventPattern': { 'source': [ 'aws.ecr', @@ -424,7 +457,7 @@ describe('repository', () => { }, }, 'State': 'ENABLED', - })); + }); }); test('onImageScanCompleted with multiple imageTags creates the correct event', () => { @@ -438,7 +471,7 @@ describe('repository', () => { }, }); - expectCDK(stack).to(haveResourceLike('AWS::Events::Rule', { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { 'EventPattern': { 'source': [ 'aws.ecr', @@ -460,7 +493,7 @@ describe('repository', () => { }, }, 'State': 'ENABLED', - })); + }); }); test('removal policy is "Retain" by default', () => { @@ -471,10 +504,10 @@ describe('repository', () => { new ecr.Repository(stack, 'Repo'); // THEN - expectCDK(stack).to(haveResource('AWS::ECR::Repository', { + Template.fromStack(stack).hasResource('AWS::ECR::Repository', { 'Type': 'AWS::ECR::Repository', 'DeletionPolicy': 'Retain', - }, ResourcePart.CompleteDefinition)); + }); }); test('"Delete" removal policy can be set explicitly', () => { @@ -487,10 +520,10 @@ describe('repository', () => { }); // THEN - expectCDK(stack).to(haveResource('AWS::ECR::Repository', { + Template.fromStack(stack).hasResource('AWS::ECR::Repository', { 'Type': 'AWS::ECR::Repository', 'DeletionPolicy': 'Delete', - }, ResourcePart.CompleteDefinition)); + }); }); test('grant adds appropriate resource-*', () => { @@ -502,7 +535,7 @@ describe('repository', () => { repo.grantPull(new iam.AnyPrincipal()); // THEN - expectCDK(stack).to(haveResource('AWS::ECR::Repository', { + Template.fromStack(stack).hasResourceProperties('AWS::ECR::Repository', { 'RepositoryPolicyText': { 'Statement': [ { @@ -512,12 +545,12 @@ describe('repository', () => { 'ecr:BatchGetImage', ], 'Effect': 'Allow', - 'Principal': { AWS: '*' }, + 'Principal': { 'AWS': '*' }, }, ], 'Version': '2012-10-17', }, - })); + }); }); }); From cf0945489c880c9f2a9beff648c73039328daf2a Mon Sep 17 00:00:00 2001 From: Madeline Kusters <80541297+madeline-k@users.noreply.github.com> Date: Thu, 20 Jan 2022 02:41:02 -0800 Subject: [PATCH 48/50] chore(servicediscovery): migrate to Assertions (#18541) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-servicediscovery/package.json | 2 +- .../aws-servicediscovery/test/instance.test.ts | 16 ++++++++-------- .../aws-servicediscovery/test/namespace.test.ts | 8 ++++---- .../aws-servicediscovery/test/service.test.ts | 16 ++++++++-------- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/packages/@aws-cdk/aws-servicediscovery/package.json b/packages/@aws-cdk/aws-servicediscovery/package.json index 9a9d267d35904..3fcee22ba4a66 100644 --- a/packages/@aws-cdk/aws-servicediscovery/package.json +++ b/packages/@aws-cdk/aws-servicediscovery/package.json @@ -75,7 +75,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-servicediscovery/test/instance.test.ts b/packages/@aws-cdk/aws-servicediscovery/test/instance.test.ts index 89ee3a81f4bfa..0fb472c1b1130 100644 --- a/packages/@aws-cdk/aws-servicediscovery/test/instance.test.ts +++ b/packages/@aws-cdk/aws-servicediscovery/test/instance.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as elbv2 from '@aws-cdk/aws-elasticloadbalancingv2'; import * as cdk from '@aws-cdk/core'; @@ -24,7 +24,7 @@ describe('instance', () => { }); // THEN - expect(stack).toHaveResource('AWS::ServiceDiscovery::Instance', { + Template.fromStack(stack).hasResourceProperties('AWS::ServiceDiscovery::Instance', { InstanceAttributes: { AWS_INSTANCE_IPV4: '10.0.0.0', AWS_INSTANCE_IPV6: '0:0:0:0:0:ffff:a00:0', @@ -62,7 +62,7 @@ describe('instance', () => { }); // THEN - expect(stack).toHaveResource('AWS::ServiceDiscovery::Instance', { + Template.fromStack(stack).hasResourceProperties('AWS::ServiceDiscovery::Instance', { InstanceAttributes: { AWS_INSTANCE_IPV4: '54.239.25.192', AWS_INSTANCE_IPV6: '0:0:0:0:0:ffff:a00:0', @@ -102,7 +102,7 @@ describe('instance', () => { }); // THEN - expect(stack).toHaveResource('AWS::ServiceDiscovery::Instance', { + Template.fromStack(stack).hasResourceProperties('AWS::ServiceDiscovery::Instance', { InstanceAttributes: { AWS_INSTANCE_IPV4: '10.0.0.0', AWS_INSTANCE_IPV6: '0:0:0:0:0:ffff:a00:0', @@ -256,7 +256,7 @@ describe('instance', () => { service.registerLoadBalancer('Loadbalancer', alb, customAttributes); // THEN - expect(stack).toHaveResource('AWS::ServiceDiscovery::Instance', { + Template.fromStack(stack).hasResourceProperties('AWS::ServiceDiscovery::Instance', { InstanceAttributes: { AWS_ALIAS_DNS_NAME: { 'Fn::GetAtt': [ @@ -343,7 +343,7 @@ describe('instance', () => { }); // THEN - expect(stack).toHaveResource('AWS::ServiceDiscovery::Instance', { + Template.fromStack(stack).hasResourceProperties('AWS::ServiceDiscovery::Instance', { InstanceAttributes: { AWS_INSTANCE_CNAME: 'foo.com', dogs: 'good', @@ -397,7 +397,7 @@ describe('instance', () => { }); // THEN - expect(stack).toHaveResource('AWS::ServiceDiscovery::Instance', { + Template.fromStack(stack).hasResourceProperties('AWS::ServiceDiscovery::Instance', { InstanceAttributes: { dogs: 'good', }, @@ -494,7 +494,7 @@ describe('instance', () => { }); // THEN - expect(stack).toCountResources('AWS::ServiceDiscovery::Instance', 2); + Template.fromStack(stack).resourceCountIs('AWS::ServiceDiscovery::Instance', 2); }); diff --git a/packages/@aws-cdk/aws-servicediscovery/test/namespace.test.ts b/packages/@aws-cdk/aws-servicediscovery/test/namespace.test.ts index d0c70b057534b..30ccf20fc85f1 100644 --- a/packages/@aws-cdk/aws-servicediscovery/test/namespace.test.ts +++ b/packages/@aws-cdk/aws-servicediscovery/test/namespace.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as cdk from '@aws-cdk/core'; import * as servicediscovery from '../lib'; @@ -11,7 +11,7 @@ describe('namespace', () => { name: 'foobar.com', }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyNamespaceD0BB8558: { Type: 'AWS::ServiceDiscovery::HttpNamespace', @@ -32,7 +32,7 @@ describe('namespace', () => { name: 'foobar.com', }); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyNamespaceD0BB8558: { Type: 'AWS::ServiceDiscovery::PublicDnsNamespace', @@ -55,7 +55,7 @@ describe('namespace', () => { vpc, }); - expect(stack).toHaveResource('AWS::ServiceDiscovery::PrivateDnsNamespace', { + Template.fromStack(stack).hasResourceProperties('AWS::ServiceDiscovery::PrivateDnsNamespace', { Name: 'foobar.com', Vpc: { Ref: 'MyVpcF9F0CA6F', diff --git a/packages/@aws-cdk/aws-servicediscovery/test/service.test.ts b/packages/@aws-cdk/aws-servicediscovery/test/service.test.ts index 44c5ba3e97c6b..556bc6c2c7260 100644 --- a/packages/@aws-cdk/aws-servicediscovery/test/service.test.ts +++ b/packages/@aws-cdk/aws-servicediscovery/test/service.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as cdk from '@aws-cdk/core'; import * as servicediscovery from '../lib'; @@ -21,7 +21,7 @@ describe('service', () => { }); // THEN - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyNamespaceD0BB8558: { Type: 'AWS::ServiceDiscovery::HttpNamespace', @@ -69,7 +69,7 @@ describe('service', () => { }); // THEN - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyNamespaceD0BB8558: { Type: 'AWS::ServiceDiscovery::HttpNamespace', @@ -118,7 +118,7 @@ describe('service', () => { }); // THEN - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyNamespaceD0BB8558: { Type: 'AWS::ServiceDiscovery::PublicDnsNamespace', @@ -176,7 +176,7 @@ describe('service', () => { }); // THEN - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyNamespaceD0BB8558: { Type: 'AWS::ServiceDiscovery::PublicDnsNamespace', @@ -233,7 +233,7 @@ describe('service', () => { }); // THEN - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyNamespaceD0BB8558: { Type: 'AWS::ServiceDiscovery::PublicDnsNamespace', @@ -417,11 +417,11 @@ describe('service', () => { }); // THEN - expect(stack).toHaveResource('AWS::ServiceDiscovery::PrivateDnsNamespace', { + Template.fromStack(stack).hasResourceProperties('AWS::ServiceDiscovery::PrivateDnsNamespace', { Name: 'private', }); - expect(stack).toHaveResource('AWS::ServiceDiscovery::Service', { + Template.fromStack(stack).hasResourceProperties('AWS::ServiceDiscovery::Service', { Description: 'service description', DnsConfig: { DnsRecords: [ From 3097c0d70767cc5f3e64fd4e6ad469a00ce51d39 Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Thu, 20 Jan 2022 03:30:23 -0800 Subject: [PATCH 49/50] chore(codecommit): migrate tests to use the Assertions module (#18548) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-codecommit/package.json | 2 +- packages/@aws-cdk/aws-codecommit/test/codecommit.test.ts | 6 +++--- .../@aws-cdk/aws-codecommit/test/notification-rule.test.ts | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/@aws-cdk/aws-codecommit/package.json b/packages/@aws-cdk/aws-codecommit/package.json index f5b3925eac5a2..2374b358d9194 100644 --- a/packages/@aws-cdk/aws-codecommit/package.json +++ b/packages/@aws-cdk/aws-codecommit/package.json @@ -84,7 +84,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/aws-sns": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", diff --git a/packages/@aws-cdk/aws-codecommit/test/codecommit.test.ts b/packages/@aws-cdk/aws-codecommit/test/codecommit.test.ts index 3dca30532e1ad..623f66eb4675a 100644 --- a/packages/@aws-cdk/aws-codecommit/test/codecommit.test.ts +++ b/packages/@aws-cdk/aws-codecommit/test/codecommit.test.ts @@ -1,5 +1,5 @@ -import '@aws-cdk/assert-internal/jest'; import { join, resolve } from 'path'; +import { Template } from '@aws-cdk/assertions'; import { Role, ServicePrincipal } from '@aws-cdk/aws-iam'; import { Asset } from '@aws-cdk/aws-s3-assets'; import * as cxschema from '@aws-cdk/cloud-assembly-schema'; @@ -19,7 +19,7 @@ describe('codecommit', () => { new Repository(stack, 'MyRepository', props).notify(snsArn); - expect(stack).toMatchTemplate({ + Template.fromStack(stack).templateMatches({ Resources: { MyRepository4C4BD5FC: { Type: 'AWS::CodeCommit::Repository', @@ -258,7 +258,7 @@ describe('codecommit', () => { repository.grantPullPush(role); // THEN - expect(stack).toHaveResource('AWS::IAM::Policy', { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ { diff --git a/packages/@aws-cdk/aws-codecommit/test/notification-rule.test.ts b/packages/@aws-cdk/aws-codecommit/test/notification-rule.test.ts index 721ce01d4c490..ae65a5f4f2ef8 100644 --- a/packages/@aws-cdk/aws-codecommit/test/notification-rule.test.ts +++ b/packages/@aws-cdk/aws-codecommit/test/notification-rule.test.ts @@ -1,4 +1,4 @@ -import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import * as sns from '@aws-cdk/aws-sns'; import * as cdk from '@aws-cdk/core'; import * as codecommit from '../lib'; @@ -16,7 +16,7 @@ describe('notification rule', () => { repository.notifyOnPullRequestMerged('NotifyOnPullRequestMerged', target); - expect(stack).toHaveResource('AWS::CodeStarNotifications::NotificationRule', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeStarNotifications::NotificationRule', { Name: 'MyCodecommitRepositoryNotifyOnPullRequestCreatedBB14EA32', DetailType: 'FULL', EventTypeIds: [ @@ -38,7 +38,7 @@ describe('notification rule', () => { ], }); - expect(stack).toHaveResource('AWS::CodeStarNotifications::NotificationRule', { + Template.fromStack(stack).hasResourceProperties('AWS::CodeStarNotifications::NotificationRule', { Name: 'MyCodecommitRepositoryNotifyOnPullRequestMerged34A7EDF1', DetailType: 'FULL', EventTypeIds: [ From 7947e07f1f56d098a29fb3a09ae5151b3cfafb1e Mon Sep 17 00:00:00 2001 From: AWS CDK Automation <43080478+aws-cdk-automation@users.noreply.github.com> Date: Thu, 20 Jan 2022 04:18:04 -0800 Subject: [PATCH 50/50] docs(cfnspec): update CloudFormation documentation (#18550) Co-authored-by: AWS CDK Team Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .../spec-source/cfn-docs/cfn-docs.json | 108 +++++++++--------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/packages/@aws-cdk/cfnspec/spec-source/cfn-docs/cfn-docs.json b/packages/@aws-cdk/cfnspec/spec-source/cfn-docs/cfn-docs.json index 5b6f0ce4d8f0c..9b9671adcf8ae 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/cfn-docs/cfn-docs.json +++ b/packages/@aws-cdk/cfnspec/spec-source/cfn-docs/cfn-docs.json @@ -8317,18 +8317,18 @@ }, "AWS::Cognito::UserPool.CustomEmailSender": { "attributes": {}, - "description": "", + "description": "A custom email sender AWS Lambda trigger.", "properties": { - "LambdaArn": "", - "LambdaVersion": "" + "LambdaArn": "The Amazon Resource Name (ARN) of the AWS Lambda function that Amazon Cognito triggers to send email notifications to users.", + "LambdaVersion": "The Lambda version represents the signature of the \"request\" attribute in the \"event\" information that Amazon Cognito passes to your custom email sender AWS Lambda function. The only supported value is `V1_0` ." } }, "AWS::Cognito::UserPool.CustomSMSSender": { "attributes": {}, - "description": "", + "description": "A custom SMS sender AWS Lambda trigger.", "properties": { - "LambdaArn": "", - "LambdaVersion": "" + "LambdaArn": "The Amazon Resource Name (ARN) of the AWS Lambda function that Amazon Cognito triggers to send SMS notifications to users.", + "LambdaVersion": "The Lambda version represents the signature of the \"request\" attribute in the \"event\" information Amazon Cognito passes to your custom SMS sender Lambda function. The only supported value is `V1_0` ." } }, "AWS::Cognito::UserPool.DeviceConfiguration": { @@ -8364,11 +8364,11 @@ "description": "Specifies the configuration for AWS Lambda triggers.", "properties": { "CreateAuthChallenge": "Creates an authentication challenge.", - "CustomEmailSender": "", + "CustomEmailSender": "A custom email sender AWS Lambda trigger.", "CustomMessage": "A custom Message AWS Lambda trigger.", - "CustomSMSSender": "", + "CustomSMSSender": "A custom SMS sender AWS Lambda trigger.", "DefineAuthChallenge": "Defines the authentication challenge.", - "KMSKeyID": "", + "KMSKeyID": "The Amazon Resource Name of a AWS Key Management Service ( AWS KMS ) key. Amazon Cognito uses the key to encrypt codes and temporary passwords sent to `CustomEmailSender` and `CustomSMSSender` .", "PostAuthentication": "A post-authentication AWS Lambda trigger.", "PostConfirmation": "A post-confirmation AWS Lambda trigger.", "PreAuthentication": "A pre-authentication AWS Lambda trigger.", @@ -8500,7 +8500,7 @@ "attributes": {}, "description": "The Amazon Pinpoint analytics configuration for collecting metrics for a user pool.\n\n> In Regions where Pinpoint isn't available, User Pools only supports sending events to Amazon Pinpoint projects in us-east-1. In Regions where Pinpoint is available, User Pools will support sending events to Amazon Pinpoint projects within that same Region.", "properties": { - "ApplicationArn": "", + "ApplicationArn": "The Amazon Resource Name (ARN) of an Amazon Pinpoint project. You can use the Amazon Pinpoint project for integration with the chosen user pool client. Amazon Cognito publishes events to the Amazon Pinpoint project that the app ARN declares.", "ApplicationId": "The application ID for an Amazon Pinpoint application.", "ExternalId": "The external ID.", "RoleArn": "The ARN of an AWS Identity and Access Management role that authorizes Amazon Cognito to publish events to Amazon Pinpoint analytics.", @@ -8768,7 +8768,7 @@ }, "AWS::Config::ConfigurationAggregator": { "attributes": { - "ConfigurationAggregatorArn": "", + "ConfigurationAggregatorArn": "The Amazon Resource Name (ARN) of the aggregator.", "Ref": "`Ref` returns the ConfigurationAggregatorName, such as `myConfigurationAggregator` ." }, "description": "The details about the configuration aggregator, including information about source accounts, regions, and metadata of the aggregator.", @@ -8999,8 +8999,8 @@ }, "AWS::Config::StoredQuery": { "attributes": { - "QueryArn": "", - "QueryId": "", + "QueryArn": "Amazon Resource Name (ARN) of the query. For example, arn:partition:service:region:account-id:resource-type/resource-name/resource-id.", + "QueryId": "The ID of the query.", "Ref": "" }, "description": "Provides the details of a stored query.", @@ -13883,7 +13883,7 @@ }, "AWS::ECS::TaskDefinition.EphemeralStorage": { "attributes": {}, - "description": "The amount of ephemeral storage to allocate for the task. This parameter is used to expand the total amount of ephemeral storage available, beyond the default amount, for tasks hosted on AWS Fargate . For more information, see [Fargate task storage](https://docs.aws.amazon.com/AmazonECS/latest/userguide/using_data_volumes.html) in the *Amazon ECS User Guide for AWS Fargate* .\n\n> This parameter is only supported for tasks hosted on Fargate using the following platform versions:\n> \n> - Linux platform version `1.4.0` or later.\n> - Windows platform version `1.0.0` or later.", + "description": "The amount of ephemeral storage to allocate for the task. This parameter is used to expand the total amount of ephemeral storage available, beyond the default amount, for tasks hosted on AWS Fargate . For more information, see [Fargate task storage](https://docs.aws.amazon.com/AmazonECS/latest/userguide/using_data_volumes.html) in the *Amazon ECS User Guide for AWS Fargate* .\n\n> This parameter is only supported for tasks hosted on Fargate using Linux platform version `1.4.0` or later. This parameter is not supported for Windows containers on Fargate.", "properties": { "SizeInGiB": "The total amount, in GiB, of ephemeral storage to set for the task. The minimum supported value is `21` GiB and the maximum supported value is `200` GiB." } @@ -17982,10 +17982,10 @@ }, "AWS::Glue::Database.PrincipalPrivileges": { "attributes": {}, - "description": "", + "description": "the permissions granted to a principal", "properties": { - "Permissions": "", - "Principal": "" + "Permissions": "The permissions that are granted to the principal.", + "Principal": "The principal who is granted permissions." } }, "AWS::Glue::DevEndpoint": { @@ -22798,7 +22798,7 @@ "ServiceNowConfiguration": "Provides configuration for data sources that connect to ServiceNow instances.", "SharePointConfiguration": "Provides information necessary to create a data source connector for a Microsoft SharePoint site.", "WebCrawlerConfiguration": "Provides the configuration information required for Amazon Kendra Web Crawler.", - "WorkDocsConfiguration": "" + "WorkDocsConfiguration": "Provides the configuration information to connect to WorkDocs as your data source." } }, "AWS::Kendra::DataSource.DataSourceToIndexFieldMapping": { @@ -22873,11 +22873,11 @@ }, "AWS::Kendra::DataSource.ProxyConfiguration": { "attributes": {}, - "description": "", + "description": "Provides the configuration information for a web proxy to connect to website hosts.", "properties": { - "Credentials": "", - "Host": "", - "Port": "" + "Credentials": "Your secret ARN, which you can create in [AWS Secrets Manager](https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html)\n\nThe credentials are optional. You use a secret if web proxy credentials are required to connect to a website host. Amazon Kendra currently support basic authentication to connect to a web proxy server. The secret stores your credentials.", + "Host": "The name of the website host you want to connect to via a web proxy server.\n\nFor example, the host name of https://a.example.com/page1.html is \"a.example.com\".", + "Port": "The port number of the website host you want to connect to via a web proxy server.\n\nFor example, the port for https://a.example.com/page1.html is 443, the standard port for HTTPS." } }, "AWS::Kendra::DataSource.S3DataSourceConfiguration": { @@ -23054,14 +23054,14 @@ "description": "", "properties": { "AuthenticationConfiguration": "", - "CrawlDepth": "", - "MaxContentSizePerPageInMegaBytes": "", - "MaxLinksPerPage": "", - "MaxUrlsPerMinuteCrawlRate": "", - "ProxyConfiguration": "", - "UrlExclusionPatterns": "", - "UrlInclusionPatterns": "", - "Urls": "Specifies the seed or starting point URLs of the websites or the sitemap URLs of the websites you want to crawl.\n\nYou can include website subdomains. You can list up to 100 seed URLs and up to three sitemap URLs.\n\nYou can only crawl websites that use the secure communication protocol, Hypertext Transfer Protocol Secure (HTTPS). If you receive an error when crawling a website, it could be that the website is blocked from crawling.\n\n*When selecting websites to index, you must adhere to the [Amazon Acceptable Use Policy](https://docs.aws.amazon.com/aup/) and all other Amazon terms. Remember that you must only use the Amazon Kendra web crawler to index your own webpages, or webpages that you have authorization to index.*" + "CrawlDepth": "Specifies the number of levels in a website that you want to crawl.\n\nThe first level begins from the website seed or starting point URL. For example, if a website has 3 levels \u2013 index level (i.e. seed in this example), sections level, and subsections level \u2013 and you are only interested in crawling information up to the sections level (i.e. levels 0-1), you can set your depth to 1.\n\nThe default crawl depth is set to 2.", + "MaxContentSizePerPageInMegaBytes": "The maximum size (in MB) of a webpage or attachment to crawl.\n\nFiles larger than this size (in MB) are skipped/not crawled.\n\nThe default maximum size of a webpage or attachment is set to 50 MB.", + "MaxLinksPerPage": "The maximum number of URLs on a webpage to include when crawling a website. This number is per webpage.\n\nAs a website\u2019s webpages are crawled, any URLs the webpages link to are also crawled. URLs on a webpage are crawled in order of appearance.\n\nThe default maximum links per page is 100.", + "MaxUrlsPerMinuteCrawlRate": "The maximum number of URLs crawled per website host per minute.\n\nA minimum of one URL is required.\n\nThe default maximum number of URLs crawled per website host per minute is 300.", + "ProxyConfiguration": "Provides configuration information required to connect to your internal websites via a web proxy.\n\nYou must provide the website host name and port number. For example, the host name of https://a.example.com/page1.html is \"a.example.com\" and the port is 443, the standard port for HTTPS.\n\nWeb proxy credentials are optional and you can use them to connect to a web proxy server that requires basic authentication. To store web proxy credentials, you use a secret in [AWS Secrets Manager](https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html) .", + "UrlExclusionPatterns": "The regular expression pattern to exclude certain URLs to crawl.\n\nIf there is a regular expression pattern to include certain URLs that conflicts with the exclude pattern, the exclude pattern takes precedence.", + "UrlInclusionPatterns": "The regular expression pattern to include certain URLs to crawl.\n\nIf there is a regular expression pattern to exclude certain URLs that conflicts with the include pattern, the exclude pattern takes precedence.", + "Urls": "Specifies the seed or starting point URLs of the websites or the sitemap URLs of the websites you want to crawl.\n\nYou can include website subdomains. You can list up to 100 seed URLs and up to three sitemap URLs.\n\nYou can only crawl websites that use the secure communication protocol, Hypertext Transfer Protocol Secure (HTTPS). If you receive an error when crawling a website, it could be that the website is blocked from crawling.\n\n*When selecting websites to index, you must adhere to the [Amazon Acceptable Use Policy](https://docs.aws.amazon.com/aup/) and all other Amazon terms. Remember that you must only use Amazon Kendra Web Crawler to index your own webpages, or webpages that you have authorization to index.*" } }, "AWS::Kendra::DataSource.WebCrawlerSeedUrlConfiguration": { @@ -23076,7 +23076,7 @@ "attributes": {}, "description": "Provides the configuration information of the sitemap URLs to crawl.\n\n*When selecting websites to index, you must adhere to the [Amazon Acceptable Use Policy](https://docs.aws.amazon.com/aup/) and all other Amazon terms. Remember that you must only use the Amazon Kendra web crawler to index your own webpages, or webpages that you have authorization to index.*", "properties": { - "SiteMaps": "" + "SiteMaps": "The list of sitemap URLs of the websites you want to crawl.\n\nThe list can include a maximum of three sitemap URLs." } }, "AWS::Kendra::DataSource.WebCrawlerUrls": { @@ -23089,14 +23089,14 @@ }, "AWS::Kendra::DataSource.WorkDocsConfiguration": { "attributes": {}, - "description": "", + "description": "Provides the configuration information to connect to Amazon WorkDocs as your data source.\n\nAmazon WorkDocs connector is available in Oregon, North Virginia, Sydney, Singapore and Ireland regions.", "properties": { - "CrawlComments": "", - "ExclusionPatterns": "", - "FieldMappings": "", - "InclusionPatterns": "", - "OrganizationId": "", - "UseChangeLog": "" + "CrawlComments": "`TRUE` to include comments on documents in your index. Including comments in your index means each comment is a document that can be searched on.\n\nThe default is set to `FALSE` .", + "ExclusionPatterns": "A list of regular expression patterns to exclude certain files in your Amazon WorkDocs site repository. Files that match the patterns are excluded from the index. Files that don\u2019t match the patterns are included in the index. If a file matches both an inclusion pattern and an exclusion pattern, the exclusion pattern takes precedence and the file isn\u2019t included in the index.", + "FieldMappings": "A list of `DataSourceToIndexFieldMapping` objects that map Amazon WorkDocs field names to custom index field names in Amazon Kendra. You must first create the custom index fields using the `UpdateIndex` operation before you map to Amazon WorkDocs fields. For more information, see [Mapping Data Source Fields](https://docs.aws.amazon.com/kendra/latest/dg/field-mapping.html) . The Amazon WorkDocs data source field names need to exist in your Amazon WorkDocs custom metadata.", + "InclusionPatterns": "A list of regular expression patterns to include certain files in your Amazon WorkDocs site repository. Files that match the patterns are included in the index. Files that don't match the patterns are excluded from the index. If a file matches both an inclusion pattern and an exclusion pattern, the exclusion pattern takes precedence and the file isn\u2019t included in the index.", + "OrganizationId": "The identifier of the directory corresponding to your Amazon WorkDocs site repository.\n\nYou can find the organization ID in the [AWS Directory Service](https://docs.aws.amazon.com/directoryservicev2/) by going to *Active Directory* , then *Directories* . Your Amazon WorkDocs site directory has an ID, which is the organization ID. You can also set up a new Amazon WorkDocs directory in the AWS Directory Service console and enable a Amazon WorkDocs site for the directory in the Amazon WorkDocs console.", + "UseChangeLog": "`TRUE` to use the change logs to update documents in your index instead of scanning all documents.\n\nIf you are syncing your Amazon WorkDocs data source with your index for the first time, all documents are scanned. After your first sync, you can use the change logs to update your documents in your index for future syncs.\n\nThe default is set to `FALSE` ." } }, "AWS::Kendra::Faq": { @@ -32158,7 +32158,7 @@ "AllowMajorVersionUpgrade": "A value that indicates whether major version upgrades are allowed. Changing this parameter doesn't result in an outage and the change is asynchronously applied as soon as possible.\n\nConstraints: Major version upgrades must be allowed when specifying a value for the `EngineVersion` parameter that is a different major version than the DB instance's current version.", "AssociatedRoles": "The AWS Identity and Access Management (IAM) roles associated with the DB instance.", "AutoMinorVersionUpgrade": "A value that indicates whether minor engine upgrades are applied automatically to the DB instance during the maintenance window. By default, minor engine upgrades are applied automatically.", - "AvailabilityZone": "The Availability Zone (AZ) where the database will be created. For information on AWS Regions and Availability Zones, see [Regions and Availability Zones](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RegionsAndAvailabilityZones.html) .\n\n*Amazon Aurora*\n\nNot applicable. Availability Zones are managed by the DB cluster.\n\nDefault: A random, system-chosen Availability Zone in the endpoint's AWS Region.\n\nExample: `us-east-1d`\n\nConstraint: The `AvailabilityZone` parameter can't be specified if the DB instance is a Multi-AZ deployment. The specified Availability Zone must be in the same AWS Region as the current endpoint.\n\n> If you're creating a DB instance in an RDS on VMware environment, specify the identifier of the custom Availability Zone to create the DB instance in.\n> \n> For more information about RDS on VMware, see the [RDS on VMware User Guide.](https://docs.aws.amazon.com/AmazonRDS/latest/RDSonVMwareUserGuide/rds-on-vmware.html)", + "AvailabilityZone": "The Availability Zone that the database instance will be created in.\n\nDefault: A random, system-chosen Availability Zone in the endpoint's region.\n\nExample: `us-east-1d`\n\nConstraint: The AvailabilityZone parameter cannot be specified if the MultiAZ parameter is set to `true` . The specified Availability Zone must be in the same region as the current endpoint.", "BackupRetentionPeriod": "The number of days for which automated backups are retained. Setting this parameter to a positive number enables backups. Setting this parameter to 0 disables automated backups.\n\n*Amazon Aurora*\n\nNot applicable. The retention period for automated backups is managed by the DB cluster.\n\nDefault: 1\n\nConstraints:\n\n- Must be a value from 0 to 35\n- Can't be set to 0 if the DB instance is a source to read replicas", "CACertificateIdentifier": "The identifier of the CA certificate for this DB instance.\n\n> Specifying or updating this property triggers a reboot. \n\nFor more information about CA certificate identifiers for RDS DB engines, see [Rotating Your SSL/TLS Certificate](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.SSL-certificate-rotation.html) in the *Amazon RDS User Guide* .\n\nFor more information about CA certificate identifiers for Aurora DB engines, see [Rotating Your SSL/TLS Certificate](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/UsingWithRDS.SSL-certificate-rotation.html) in the *Amazon Aurora User Guide* .", "CharacterSetName": "For supported engines, indicates that the DB instance should be associated with the specified character set.\n\n*Amazon Aurora*\n\nNot applicable. The character set is managed by the DB cluster. For more information, see [AWS::RDS::DBCluster](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-rds-dbcluster.html) .", @@ -32230,7 +32230,7 @@ }, "description": "The `AWS::RDS::DBParameterGroup` resource creates a custom parameter group for an RDS database family.\n\nThis type can be declared in a template and referenced in the `DBParameterGroupName` property of an `[AWS::RDS::DBInstance](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-rds-database-instance.html)` resource.\n\nFor information about configuring parameters for Amazon RDS DB instances, see [Working with DB parameter groups](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_WorkingWithParamGroups.html) in the *Amazon RDS User Guide* .\n\nFor information about configuring parameters for Amazon Aurora DB instances, see [Working with DB parameter groups and DB cluster parameter groups](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/USER_WorkingWithParamGroups.html) in the *Amazon Aurora User Guide* .\n\n> Applying a parameter group to a DB instance may require the DB instance to reboot, resulting in a database outage for the duration of the reboot.", "properties": { - "Description": "Provides the customer-specified description for this DB parameter group.", + "Description": "Provides the customer-specified description for this DB Parameter Group.", "Family": "The DB parameter group family name. A DB parameter group can be associated with one and only one DB parameter group family, and can be applied only to a DB instance running a DB engine and engine version compatible with that DB parameter group family.\n\n> The DB parameter group family can't be changed when updating a DB parameter group. \n\nTo list all of the available parameter group families, use the following command:\n\n`aws rds describe-db-engine-versions --query \"DBEngineVersions[].DBParameterGroupFamily\"`\n\nThe output contains duplicates.\n\nFor more information, see `[CreateDBParameterGroup](https://docs.aws.amazon.com//AmazonRDS/latest/APIReference/API_CreateDBParameterGroup.html)` .", "Parameters": "An array of parameter names and values for the parameter update. At least one parameter name and value must be supplied. Subsequent arguments are optional.\n\nFor more information about DB parameters and DB parameter groups for Amazon RDS DB engines, see [Working with DB Parameter Groups](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_WorkingWithParamGroups.html) in the *Amazon RDS User Guide* .\n\nFor more information about DB cluster and DB instance parameters and parameter groups for Amazon Aurora DB engines, see [Working with DB Parameter Groups and DB Cluster Parameter Groups](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/USER_WorkingWithParamGroups.html) in the *Amazon Aurora User Guide* .\n\n> AWS CloudFormation doesn't support specifying an apply method for each individual parameter. The default apply method for each parameter is used.", "Tags": "Tags to assign to the DB parameter group." @@ -32334,7 +32334,7 @@ "properties": { "DBSecurityGroupIngress": "Ingress rules to be applied to the DB security group.", "EC2VpcId": "The identifier of an Amazon VPC. This property indicates the VPC that this DB security group belongs to.\n\n> The `EC2VpcId` property is for backward compatibility with older regions, and is no longer recommended for providing security information to an RDS DB instance.", - "GroupDescription": "Provides the description of the DB security group.", + "GroupDescription": "Provides the description of the DB Security Group.", "Tags": "Tags to assign to the DB security group." } }, @@ -32343,9 +32343,9 @@ "description": "The `Ingress` property type specifies an individual ingress rule within an `AWS::RDS::DBSecurityGroup` resource.", "properties": { "CIDRIP": "The IP range to authorize.", - "EC2SecurityGroupId": "Id of the EC2 security group to authorize. For VPC DB security groups, `EC2SecurityGroupId` must be provided. Otherwise, `EC2SecurityGroupOwnerId` and either `EC2SecurityGroupName` or `EC2SecurityGroupId` must be provided.", - "EC2SecurityGroupName": "Name of the EC2 security group to authorize. For VPC DB security groups, `EC2SecurityGroupId` must be provided. Otherwise, `EC2SecurityGroupOwnerId` and either `EC2SecurityGroupName` or `EC2SecurityGroupId` must be provided.", - "EC2SecurityGroupOwnerId": "AWS account number of the owner of the EC2 security group specified in the `EC2SecurityGroupName` parameter. The AWS access key ID isn't an acceptable value. For VPC DB security groups, `EC2SecurityGroupId` must be provided. Otherwise, `EC2SecurityGroupOwnerId` and either `EC2SecurityGroupName` or `EC2SecurityGroupId` must be provided." + "EC2SecurityGroupId": "Id of the EC2 Security Group to authorize. For VPC DB Security Groups, `EC2SecurityGroupId` must be provided. Otherwise, EC2SecurityGroupOwnerId and either `EC2SecurityGroupName` or `EC2SecurityGroupId` must be provided.", + "EC2SecurityGroupName": "Name of the EC2 Security Group to authorize. For VPC DB Security Groups, `EC2SecurityGroupId` must be provided. Otherwise, EC2SecurityGroupOwnerId and either `EC2SecurityGroupName` or `EC2SecurityGroupId` must be provided.", + "EC2SecurityGroupOwnerId": "AWS Account Number of the owner of the EC2 Security Group specified in the EC2SecurityGroupName parameter. The AWS Access Key ID is not an acceptable value. For VPC DB Security Groups, `EC2SecurityGroupId` must be provided. Otherwise, EC2SecurityGroupOwnerId and either `EC2SecurityGroupName` or `EC2SecurityGroupId` must be provided." } }, "AWS::RDS::DBSecurityGroupIngress": { @@ -32355,10 +32355,10 @@ "description": "The `AWS::RDS::DBSecurityGroupIngress` resource enables ingress to a DB security group using one of two forms of authorization. First, you can add EC2 or VPC security groups to the DB security group if the application using the database is running on EC2 or VPC instances. Second, IP ranges are available if the application accessing your database is running on the Internet.\n\nThis type supports updates. For more information about updating stacks, see [AWS CloudFormation Stacks Updates](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks.html) .\n\nFor details about the settings for DB security group ingress, see [AuthorizeDBSecurityGroupIngress](https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_AuthorizeDBSecurityGroupIngress.html) .", "properties": { "CIDRIP": "The IP range to authorize.", - "DBSecurityGroupName": "The name of the DB security group to add authorization to.", - "EC2SecurityGroupId": "Id of the EC2 security group to authorize. For VPC DB security groups, `EC2SecurityGroupId` must be provided. Otherwise, `EC2SecurityGroupOwnerId` and either `EC2SecurityGroupName` or `EC2SecurityGroupId` must be provided.", - "EC2SecurityGroupName": "Name of the EC2 security group to authorize. For VPC DB security groups, `EC2SecurityGroupId` must be provided. Otherwise, `EC2SecurityGroupOwnerId` and either `EC2SecurityGroupName` or `EC2SecurityGroupId` must be provided.", - "EC2SecurityGroupOwnerId": "AWS account number of the owner of the EC2 security group specified in the `EC2SecurityGroupName` parameter. The AWS access key ID isn't an acceptable value. For VPC DB security groups, `EC2SecurityGroupId` must be provided. Otherwise, `EC2SecurityGroupOwnerId` and either `EC2SecurityGroupName` or `EC2SecurityGroupId` must be provided." + "DBSecurityGroupName": "The name of the DB Security Group to add authorization to.", + "EC2SecurityGroupId": "Id of the EC2 Security Group to authorize. For VPC DB Security Groups, `EC2SecurityGroupId` must be provided. Otherwise, EC2SecurityGroupOwnerId and either `EC2SecurityGroupName` or `EC2SecurityGroupId` must be provided.", + "EC2SecurityGroupName": "Name of the EC2 Security Group to authorize. For VPC DB Security Groups, `EC2SecurityGroupId` must be provided. Otherwise, EC2SecurityGroupOwnerId and either `EC2SecurityGroupName` or `EC2SecurityGroupId` must be provided.", + "EC2SecurityGroupOwnerId": "AWS Account Number of the owner of the EC2 Security Group specified in the EC2SecurityGroupName parameter. The AWS Access Key ID is not an acceptable value. For VPC DB Security Groups, `EC2SecurityGroupId` must be provided. Otherwise, EC2SecurityGroupOwnerId and either `EC2SecurityGroupName` or `EC2SecurityGroupId` must be provided." } }, "AWS::RDS::DBSubnetGroup": { @@ -32367,9 +32367,9 @@ }, "description": "The `AWS::RDS::DBSubnetGroup` resource creates a database subnet group. Subnet groups must contain at least two subnets in two different Availability Zones in the same region.\n\nFor more information, see [Working with DB subnet groups](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_VPC.WorkingWithRDSInstanceinaVPC.html#USER_VPC.Subnets) in the *Amazon RDS User Guide* .", "properties": { - "DBSubnetGroupDescription": "The description for the DB subnet group.", + "DBSubnetGroupDescription": "The description for the DB Subnet Group.", "DBSubnetGroupName": "The name for the DB subnet group. This value is stored as a lowercase string.\n\nConstraints: Must contain no more than 255 lowercase alphanumeric characters or hyphens. Must not be \"Default\".\n\nExample: `mysubnetgroup`", - "SubnetIds": "The EC2 Subnet IDs for the DB subnet group.", + "SubnetIds": "The EC2 Subnet IDs for the DB Subnet Group.", "Tags": "Tags to assign to the DB subnet group." } }, @@ -32379,8 +32379,8 @@ }, "description": "The `AWS::RDS::EventSubscription` resource allows you to receive notifications for Amazon Relational Database Service events through the Amazon Simple Notification Service (Amazon SNS). For more information, see [Using Amazon RDS Event Notification](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_Events.html) in the *Amazon RDS User Guide* .", "properties": { - "Enabled": "A value that indicates whether to activate the subscription. If the event notification subscription isn't activated, the subscription is created but not active.", - "EventCategories": "A list of event categories for a particular source type ( `SourceType` ) that you want to subscribe to. You can see a list of the categories for a given source type in the \"Amazon RDS event categories and event messages\" section of the [*Amazon RDS User Guide*](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_Events.Messages.html) or the [*Amazon Aurora User Guide*](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/USER_Events.Messages.html) . You can also see this list by using the `DescribeEventCategories` operation.", + "Enabled": "A Boolean value; set to *true* to activate the subscription, set to *false* to create the subscription but not active it.", + "EventCategories": "A list of event categories for a SourceType that you want to subscribe to. You can see a list of the categories for a given SourceType in the [Events](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_Events.html) topic in the Amazon RDS User Guide or by using the *DescribeEventCategories* action.", "SnsTopicArn": "The Amazon Resource Name (ARN) of the SNS topic created for event notification. The ARN is created by Amazon SNS when you create a topic and subscribe to it.", "SourceIds": "The list of identifiers of the event sources for which events are returned. If not specified, then all sources are included in the response. An identifier must begin with a letter and must contain only ASCII letters, digits, and hyphens. It can't end with a hyphen or contain two consecutive hyphens.\n\nConstraints:\n\n- If a `SourceIds` value is supplied, `SourceType` must also be provided.\n- If the source type is a DB instance, a `DBInstanceIdentifier` value must be supplied.\n- If the source type is a DB cluster, a `DBClusterIdentifier` value must be supplied.\n- If the source type is a DB parameter group, a `DBParameterGroupName` value must be supplied.\n- If the source type is a DB security group, a `DBSecurityGroupName` value must be supplied.\n- If the source type is a DB snapshot, a `DBSnapshotIdentifier` value must be supplied.\n- If the source type is a DB cluster snapshot, a `DBClusterSnapshotIdentifier` value must be supplied.", "SourceType": "The type of source that is generating the events. For example, if you want to be notified of events generated by a DB instance, set this parameter to `db-instance` . If this value isn't specified, all events are returned.\n\nValid values: `db-instance` | `db-cluster` | `db-parameter-group` | `db-security-group` | `db-snapshot` | `db-cluster-snapshot`" @@ -36800,8 +36800,8 @@ "MasterSecretKmsKeyArn": "The ARN of the KMS key that Secrets Manager uses to encrypt the elevated secret if you use the [alternating users strategy](https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets_strategies.html#rotating-secrets-two-users) . If you don't specify this value and you use the alternating users strategy, then Secrets Manager uses the key `aws/secretsmanager` . If `aws/secretsmanager` doesn't yet exist, then Secrets Manager creates it for you automatically the first time it encrypts the secret value.", "RotationLambdaName": "The name of the Lambda rotation function.", "RotationType": "The type of rotation template to use. For more information, see [Secrets Manager rotation function templates](https://docs.aws.amazon.com/secretsmanager/latest/userguide/reference_available-rotation-templates.html) .\n\nYou can specify one of the following `RotationTypes` :\n\n- MySQLSingleUser\n- MySQLMultiUser\n- PostgreSQLSingleUser\n- PostgreSQLMultiUser\n- OracleSingleUser\n- OracleMultiUser\n- MariaDBSingleUser\n- MariaDBMultiUser\n- SQLServerSingleUser\n- SQLServerMultiUser\n- RedshiftSingleUser\n- RedshiftMultiUser\n- MongoDBSingleUser\n- MongoDBMultiUser", - "SuperuserSecretArn": "", - "SuperuserSecretKmsKeyArn": "", + "SuperuserSecretArn": "The ARN of the secret that contains elevated credentials. The Lambda rotation function uses this secret for the [Alternating users rotation strategy](https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets_strategies.html#rotating-secrets-two-users) .", + "SuperuserSecretKmsKeyArn": "The ARN of the KMS key that Secrets Manager uses to encrypt the elevated secret if you use the [alternating users strategy](https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets_strategies.html#rotating-secrets-two-users) . If you don't specify this value and you use the alternating users strategy, then Secrets Manager uses the key `aws/secretsmanager` . If `aws/secretsmanager` doesn't yet exist, then Secrets Manager creates it for you automatically the first time it encrypts the secret value.", "VpcSecurityGroupIds": "A comma-separated list of security group IDs applied to the target database.\n\nThe templates applies the same security groups as on the Lambda rotation function that is created as part of this stack.", "VpcSubnetIds": "A comma separated list of VPC subnet IDs of the target database network. The Lambda rotation function is in the same subnet group." }