From 6b4d709264ce1fa68a37ee4aec76ebafb3b83001 Mon Sep 17 00:00:00 2001 From: Brent Ryan Date: Sun, 28 Mar 2021 21:21:28 -0400 Subject: [PATCH 001/134] Allow taskRole to be passed in on creation of an ECS service --- .../ecs-service-extensions/lib/service.ts | 12 ++++++++++++ .../ecs-service-extensions/test/test.service.ts | 7 ++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk-containers/ecs-service-extensions/lib/service.ts b/packages/@aws-cdk-containers/ecs-service-extensions/lib/service.ts index c988c0592b6bf..45e87d4141d34 100644 --- a/packages/@aws-cdk-containers/ecs-service-extensions/lib/service.ts +++ b/packages/@aws-cdk-containers/ecs-service-extensions/lib/service.ts @@ -1,5 +1,6 @@ import * as ec2 from '@aws-cdk/aws-ec2'; import * as ecs from '@aws-cdk/aws-ecs'; +import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; import { IEnvironment } from './environment'; import { EnvironmentCapacityType, ServiceBuild } from './extensions/extension-interfaces'; @@ -22,6 +23,13 @@ export interface ServiceProps { * The environment to launch the service in */ readonly environment: IEnvironment + + /** + * The name of the IAM role that grants containers in the task permission to call AWS APIs on your behalf. + * + * @default - A task role is automatically created for you. + */ + readonly taskRole?: iam.IRole; } /** @@ -118,6 +126,10 @@ export class Service extends Construct { cpu: '256', memory: '512', + // Allow user to pre-define the taskRole so that it can be used in resource policies that may + // be defined before the ECS service exists in a CDK application + taskRole: props.taskRole, + // Ensure that the task definition supports both EC2 and Fargate compatibility: ecs.Compatibility.EC2_AND_FARGATE, } as ecs.TaskDefinitionProps; diff --git a/packages/@aws-cdk-containers/ecs-service-extensions/test/test.service.ts b/packages/@aws-cdk-containers/ecs-service-extensions/test/test.service.ts index 23b30f59afe3d..ff07cfccdc373 100644 --- a/packages/@aws-cdk-containers/ecs-service-extensions/test/test.service.ts +++ b/packages/@aws-cdk-containers/ecs-service-extensions/test/test.service.ts @@ -1,6 +1,7 @@ import { countResources, expect, haveResource } from '@aws-cdk/assert'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as ecs from '@aws-cdk/aws-ecs'; +import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; import { Test } from 'nodeunit'; import { Container, EnvironmentCapacityType, Environment, Service, ServiceDescription } from '../lib'; @@ -40,6 +41,9 @@ export = { capacityType: EnvironmentCapacityType.EC2, }); const serviceDescription = new ServiceDescription(); + const taskRole = new iam.Role(stack, 'CustomTaskRole', { + assumedBy: new iam.ServicePrincipal('ecs-tasks.amazonaws.com'), + }); serviceDescription.add(new Container({ cpu: 256, @@ -51,6 +55,7 @@ export = { new Service(stack, 'my-service', { environment, serviceDescription, + taskRole, }); // THEN @@ -89,7 +94,7 @@ export = { ], TaskRoleArn: { 'Fn::GetAtt': [ - 'myservicetaskdefinitionTaskRole92ACD903', + 'CustomTaskRole3C6B13FD', 'Arn', ], }, From 3e257a0e554851b7393f52bbbea2f5187673e8a7 Mon Sep 17 00:00:00 2001 From: Brent Ryan Date: Sun, 28 Mar 2021 21:40:01 -0400 Subject: [PATCH 002/134] feat: allow taskRole to be passed in on creation of an ECS service --- .../ecs-service-extensions/README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/@aws-cdk-containers/ecs-service-extensions/README.md b/packages/@aws-cdk-containers/ecs-service-extensions/README.md index da884a7aa85bd..bc1d5de23a4ff 100644 --- a/packages/@aws-cdk-containers/ecs-service-extensions/README.md +++ b/packages/@aws-cdk-containers/ecs-service-extensions/README.md @@ -133,6 +133,25 @@ At this point, all the service resources will be created. This includes the ECS Definition, Service, as well as any other attached resources, such as App Mesh Virtual Node or an Application Load Balancer. +## Creating your own taskRole + +Sometimes the taskRole should be defined outside of the service so that you can create strict resource policies (ie. S3 bucket policies) that are restricted to a given taskRole: + +```ts +const taskRole = new iam.Role(stack, 'CustomTaskRole', { + assumedBy: new iam.ServicePrincipal('ecs-tasks.amazonaws.com'), +}); + +// Use taskRole in any CDK resource policies +// new s3.BucketPolicy(this, 'BucketPolicy, {}); + +const nameService = new Service(stack, 'name', { + environment: environment, + serviceDescription: nameDescription, + taskRole, +}); +``` + ## Creating your own custom `ServiceExtension` In addition to using the default service extensions that come with this module, you From 71c61e81ca58c95979f66d7d7b8100777d3c7b99 Mon Sep 17 00:00:00 2001 From: Otavio Macedo Date: Mon, 10 May 2021 15:41:40 +0100 Subject: [PATCH 003/134] fix(cli): synth fails if there was an error when synthesizing the stack (#14613) All stacks created inside a pipeline stage will be flagged for validation. After synth is done, the CLI will validate all flagged stacks plus the stacks that were explicitly specified. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../lib/cloud-assembly/artifact-schema.ts | 7 +++ .../schema/cloud-assembly.schema.json | 4 ++ .../schema/cloud-assembly.version.json | 2 +- .../@aws-cdk/core/lib/construct-compat.ts | 14 ++++++ .../@aws-cdk/core/lib/private/synthesis.ts | 5 +- .../core/lib/stack-synthesizers/_shared.ts | 1 + packages/@aws-cdk/core/lib/stage.ts | 8 +++ packages/@aws-cdk/core/test/synthesis.test.ts | 5 +- .../lib/artifacts/cloudformation-artifact.ts | 8 +++ packages/@aws-cdk/pipelines/lib/stage.ts | 2 +- .../aws-cdk/lib/api/cxapp/cloud-assembly.ts | 8 +++ packages/aws-cdk/lib/cdk-toolkit.ts | 13 +++-- packages/aws-cdk/test/cdk-toolkit.test.ts | 50 ++++++++++++++++--- 13 files changed, 111 insertions(+), 16 deletions(-) diff --git a/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/artifact-schema.ts b/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/artifact-schema.ts index 51c19bf226a96..c4facf9fd4f19 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/artifact-schema.ts +++ b/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/artifact-schema.ts @@ -77,6 +77,13 @@ export interface AwsCloudFormationStackProperties { * @default - Bootstrap stack version number looked up */ readonly bootstrapStackVersionSsmParameter?: string; + + /** + * Whether this stack should be validated by the CLI after synthesis + * + * @default - false + */ + readonly validateOnSynth?: boolean; } /** diff --git a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json index 38c7538f38384..77d3117d0aae2 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json +++ b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json @@ -314,6 +314,10 @@ "bootstrapStackVersionSsmParameter": { "description": "SSM parameter where the bootstrap stack version number can be found\n\nOnly used if `requiresBootstrapStackVersion` is set.\n\n- If this value is not set, the bootstrap stack name must be known at\n deployment time so the stack version can be looked up from the stack\n outputs.\n- If this value is set, the bootstrap stack can have any name because\n we won't need to look it up. (Default - Bootstrap stack version number looked up)", "type": "string" + }, + "validateOnSynth": { + "description": "Whether this stack should be validated by the CLI after synthesis (Default - false)", + "type": "boolean" } }, "required": [ diff --git a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.version.json b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.version.json index 1829f904a3c7a..b056ff69e87b5 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.version.json +++ b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.version.json @@ -1 +1 @@ -{"version":"10.0.0"} \ No newline at end of file +{"version":"11.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/core/lib/construct-compat.ts b/packages/@aws-cdk/core/lib/construct-compat.ts index d468ce234c4f3..cc5921fb73465 100644 --- a/packages/@aws-cdk/core/lib/construct-compat.ts +++ b/packages/@aws-cdk/core/lib/construct-compat.ts @@ -43,6 +43,13 @@ export interface ISynthesisSession { * Cloud assembly builder. */ assembly: cxapi.CloudAssemblyBuilder; + + /** + * Whether the stack should be validated after synthesis to check for error metadata + * + * @default - false + */ + validateOnSynth?: boolean; } /** @@ -203,6 +210,13 @@ export interface SynthesisOptions extends cxapi.AssemblyBuildOptions { * @default false */ readonly skipValidation?: boolean; + + /** + * Whether the stack should be validated after synthesis to check for error metadata + * + * @default - false + */ + readonly validateOnSynthesis?: boolean; } /** diff --git a/packages/@aws-cdk/core/lib/private/synthesis.ts b/packages/@aws-cdk/core/lib/private/synthesis.ts index c8a54267823a8..e865481a59074 100644 --- a/packages/@aws-cdk/core/lib/private/synthesis.ts +++ b/packages/@aws-cdk/core/lib/private/synthesis.ts @@ -36,7 +36,7 @@ export function synthesize(root: IConstruct, options: SynthesisOptions = { }): c // next, we invoke "onSynthesize" on all of our children. this will allow // stacks to add themselves to the synthesized cloud assembly. - synthesizeTree(root, builder); + synthesizeTree(root, builder, options.validateOnSynthesis); return builder.buildAssembly(); } @@ -146,11 +146,12 @@ function injectMetadataResources(root: IConstruct) { * * Stop at Assembly boundaries. */ -function synthesizeTree(root: IConstruct, builder: cxapi.CloudAssemblyBuilder) { +function synthesizeTree(root: IConstruct, builder: cxapi.CloudAssemblyBuilder, validateOnSynth: boolean = false) { visit(root, 'post', construct => { const session = { outdir: builder.outdir, assembly: builder, + validateOnSynth, }; if (Stack.isStack(construct)) { diff --git a/packages/@aws-cdk/core/lib/stack-synthesizers/_shared.ts b/packages/@aws-cdk/core/lib/stack-synthesizers/_shared.ts index c5d88c9e76686..211413df2b5ed 100644 --- a/packages/@aws-cdk/core/lib/stack-synthesizers/_shared.ts +++ b/packages/@aws-cdk/core/lib/stack-synthesizers/_shared.ts @@ -46,6 +46,7 @@ export function addStackArtifactToAssembly( templateFile: stack.templateFile, terminationProtection: stack.terminationProtection, tags: nonEmptyDict(stack.tags.tagValues()), + validateOnSynth: session.validateOnSynth, ...stackProps, ...stackNameProperty, }; diff --git a/packages/@aws-cdk/core/lib/stage.ts b/packages/@aws-cdk/core/lib/stage.ts index ba0fc21d4248d..efe65f115ab55 100644 --- a/packages/@aws-cdk/core/lib/stage.ts +++ b/packages/@aws-cdk/core/lib/stage.ts @@ -179,6 +179,7 @@ export class Stage extends CoreConstruct { if (!this.assembly || options.force) { this.assembly = synthesize(this, { skipValidation: options.skipValidation, + validateOnSynthesis: options.validateOnSynthesis, }); } @@ -210,6 +211,13 @@ export interface StageSynthesisOptions { */ readonly skipValidation?: boolean; + /** + * Whether the stack should be validated after synthesis to check for error metadata + * + * @default - false + */ + readonly validateOnSynthesis?: boolean; + /** * Force a re-synth, even if the stage has already been synthesized. * This is used by tests to allow for incremental verification of the output. diff --git a/packages/@aws-cdk/core/test/synthesis.test.ts b/packages/@aws-cdk/core/test/synthesis.test.ts index 77c8c306ef81f..710620eceaa3d 100644 --- a/packages/@aws-cdk/core/test/synthesis.test.ts +++ b/packages/@aws-cdk/core/test/synthesis.test.ts @@ -104,7 +104,10 @@ nodeunitShim({ 'one-stack': { type: 'aws:cloudformation:stack', environment: 'aws://unknown-account/unknown-region', - properties: { templateFile: 'one-stack.template.json' }, + properties: { + templateFile: 'one-stack.template.json', + validateOnSynth: false, + }, displayName: 'one-stack', }, }, diff --git a/packages/@aws-cdk/cx-api/lib/artifacts/cloudformation-artifact.ts b/packages/@aws-cdk/cx-api/lib/artifacts/cloudformation-artifact.ts index 56093d702d2e0..dba5bacb6beb0 100644 --- a/packages/@aws-cdk/cx-api/lib/artifacts/cloudformation-artifact.ts +++ b/packages/@aws-cdk/cx-api/lib/artifacts/cloudformation-artifact.ts @@ -94,6 +94,13 @@ export class CloudFormationStackArtifact extends CloudArtifact { */ public readonly terminationProtection?: boolean; + /** + * Whether this stack should be validated by the CLI after synthesis + * + * @default - false + */ + public readonly validateOnSynth?: boolean; + private _template: any | undefined; constructor(assembly: CloudAssembly, artifactId: string, artifact: cxschema.ArtifactManifest) { @@ -119,6 +126,7 @@ export class CloudFormationStackArtifact extends CloudArtifact { this.requiresBootstrapStackVersion = properties.requiresBootstrapStackVersion; this.bootstrapStackVersionSsmParameter = properties.bootstrapStackVersionSsmParameter; this.terminationProtection = properties.terminationProtection; + this.validateOnSynth = properties.validateOnSynth; this.stackName = properties.stackName || artifactId; this.assets = this.findMetadataByType(cxschema.ArtifactMetadataEntryType.ASSET).map(e => e.data as cxschema.AssetMetadataEntry); diff --git a/packages/@aws-cdk/pipelines/lib/stage.ts b/packages/@aws-cdk/pipelines/lib/stage.ts index 863aa8869d8bd..0fca3f895b584 100644 --- a/packages/@aws-cdk/pipelines/lib/stage.ts +++ b/packages/@aws-cdk/pipelines/lib/stage.ts @@ -75,7 +75,7 @@ export class CdkStage extends CoreConstruct { * publishing stage. */ public addApplication(appStage: Stage, options: AddStageOptions = {}) { - const asm = appStage.synth(); + const asm = appStage.synth({ validateOnSynthesis: true }); const extraRunOrderSpace = options.extraRunOrderSpace ?? 0; if (asm.stacks.length === 0) { diff --git a/packages/aws-cdk/lib/api/cxapp/cloud-assembly.ts b/packages/aws-cdk/lib/api/cxapp/cloud-assembly.ts index 59fa022ac145c..26da10c13e6e2 100644 --- a/packages/aws-cdk/lib/api/cxapp/cloud-assembly.ts +++ b/packages/aws-cdk/lib/api/cxapp/cloud-assembly.ts @@ -190,6 +190,14 @@ export class StackCollection { return new StackCollection(this.assembly, arts); } + public filter(predicate: (art: cxapi.CloudFormationStackArtifact) => boolean): StackCollection { + return new StackCollection(this.assembly, this.stackArtifacts.filter(predicate)); + } + + public concat(other: StackCollection): StackCollection { + return new StackCollection(this.assembly, this.stackArtifacts.concat(other.stackArtifacts)); + } + /** * Extracts 'aws:cdk:warning|info|error' metadata entries from the stack synthesis */ diff --git a/packages/aws-cdk/lib/cdk-toolkit.ts b/packages/aws-cdk/lib/cdk-toolkit.ts index d7011d743ffaf..9a9a9a1c3a784 100644 --- a/packages/aws-cdk/lib/cdk-toolkit.ts +++ b/packages/aws-cdk/lib/cdk-toolkit.ts @@ -395,14 +395,17 @@ export class CdkToolkit { private async selectStacksForDiff(stackNames: string[], exclusively?: boolean) { const assembly = await this.assembly(); - const idsToValidate = process.env.STACKS_TO_VALIDATE ? process.env.STACKS_TO_VALIDATE.split(';') : stackNames; - const stacksToValidate = await this.selectStacksForList(idsToValidate); - await this.validateStacks(stacksToValidate); - - return assembly.selectStacks(stackNames, { + const selectedForDiff = await assembly.selectStacks(stackNames, { extend: exclusively ? ExtendedStackSelection.None : ExtendedStackSelection.Upstream, defaultBehavior: DefaultSelection.MainAssembly, }); + + const allStacks = await this.selectStacksForList([]); + const flaggedStacks = allStacks.filter(art => art.validateOnSynth ?? false); + + await this.validateStacks(selectedForDiff.concat(flaggedStacks)); + + return selectedForDiff; } private async selectStacksForDestroy(stackNames: string[], exclusively?: boolean) { diff --git a/packages/aws-cdk/test/cdk-toolkit.test.ts b/packages/aws-cdk/test/cdk-toolkit.test.ts index 5b918889e55a8..61ac68c52baad 100644 --- a/packages/aws-cdk/test/cdk-toolkit.test.ts +++ b/packages/aws-cdk/test/cdk-toolkit.test.ts @@ -178,20 +178,58 @@ describe('synth', () => { process.env.STACKS_TO_VALIDATE = undefined; }); - test('with STACKS_TO_VALIDATE containing a failed stack', async() => { - process.env.STACKS_TO_VALIDATE = 'Test-Stack-A;Test-Stack-A/witherrors'; + test('stack has error and is flagged for validation', async() => { + cloudExecutable = new MockCloudExecutable({ + stacks: [ + MockStack.MOCK_STACK_A, + MockStack.MOCK_STACK_B, + ], + nestedAssemblies: [{ + stacks: [ + { properties: { validateOnSynth: true }, ...MockStack.MOCK_STACK_WITH_ERROR }, + ], + }], + }); const toolkit = defaultToolkitSetup(); - await expect(toolkit.synth(['Test-Stack-A'], false, true)).rejects.toBeDefined(); + await expect(toolkit.synth([], false, true)).rejects.toBeDefined(); }); - test('with STACKS_TO_VALIDATE not containing a failed stack', async() => { - process.env.STACKS_TO_VALIDATE = 'Test-Stack-A'; + test('stack has error and was explicitly selected', async() => { + cloudExecutable = new MockCloudExecutable({ + stacks: [ + MockStack.MOCK_STACK_A, + MockStack.MOCK_STACK_B, + ], + nestedAssemblies: [{ + stacks: [ + { properties: { validateOnSynth: false }, ...MockStack.MOCK_STACK_WITH_ERROR }, + ], + }], + }); + + const toolkit = defaultToolkitSetup(); + + await expect(toolkit.synth(['witherrors'], false, true)).rejects.toBeDefined(); + }); + + test('stack has error, is not flagged for validation and was not explicitly selected', async () => { + cloudExecutable = new MockCloudExecutable({ + stacks: [ + MockStack.MOCK_STACK_A, + MockStack.MOCK_STACK_B, + ], + nestedAssemblies: [{ + stacks: [ + { properties: { validateOnSynth: false }, ...MockStack.MOCK_STACK_WITH_ERROR }, + ], + }], + }); const toolkit = defaultToolkitSetup(); - await toolkit.synth(['Test-Stack-A'], false, true); + await toolkit.synth([], false, true); }); }); From 3dfee3be7a28cb28ece54a1d88091ddb0f79e811 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Mon, 10 May 2021 17:15:26 +0200 Subject: [PATCH 004/134] chore: annotate `aws-lambda-go` with `docker` requirement (#14618) The `nozem` build tool needs to know that `docker` is required to build/test this particular package. ---- *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-go/package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/@aws-cdk/aws-lambda-go/package.json b/packages/@aws-cdk/aws-lambda-go/package.json index 4ed322e8aa710..c9d8899a3a0a8 100644 --- a/packages/@aws-cdk/aws-lambda-go/package.json +++ b/packages/@aws-cdk/aws-lambda-go/package.json @@ -90,6 +90,9 @@ "awscdkio": { "announce": false }, + "nozem": { + "ostools": ["docker"] + }, "cdk-build": { "jest": true }, From d676ffccb28d530a18d0e1630df0940632122a27 Mon Sep 17 00:00:00 2001 From: Griffin Byatt Date: Mon, 10 May 2021 11:23:09 -0500 Subject: [PATCH 005/134] feat(elbv2): preserveClientIp for NetworkTargetGroup (#14589) Allows users to configure client IP preservation for network target groups. See: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticloadbalancingv2-targetgroup-targetgroupattribute.html ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../lib/nlb/network-listener.ts | 9 ++++ .../lib/nlb/network-target-group.ts | 12 +++++ .../test/nlb/target-group.test.ts | 46 +++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-listener.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-listener.ts index 8d0312977e092..f738fbcf65bc4 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-listener.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-listener.ts @@ -239,6 +239,7 @@ export class NetworkListener extends BaseListener implements INetworkListener { port: props.port, protocol: props.protocol ?? this.protocol, proxyProtocolV2: props.proxyProtocolV2, + preserveClientIp: props.preserveClientIp, targetGroupName: props.targetGroupName, targets: props.targets, vpc: this.loadBalancer.vpc, @@ -333,6 +334,14 @@ export interface AddNetworkTargetsProps { */ readonly proxyProtocolV2?: boolean; + /** + * Indicates whether client IP preservation is enabled. + * + * @default false if the target group type is IP address and the + * target group protocol is TCP or TLS. Otherwise, true. + */ + readonly preserveClientIp?: boolean; + /** * Health check configuration * diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-target-group.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-target-group.ts index 3c2e3cb574609..f4ac146d8209d 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-target-group.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-target-group.ts @@ -33,6 +33,14 @@ export interface NetworkTargetGroupProps extends BaseTargetGroupProps { */ readonly proxyProtocolV2?: boolean; + /** + * Indicates whether client IP preservation is enabled. + * + * @default false if the target group type is IP address and the + * target group protocol is TCP or TLS. Otherwise, true. + */ + readonly preserveClientIp?: boolean; + /** * The targets to add to this target group. * @@ -82,6 +90,10 @@ export class NetworkTargetGroup extends TargetGroupBase implements INetworkTarge this.setAttribute('proxy_protocol_v2.enabled', props.proxyProtocolV2 ? 'true' : 'false'); } + if (props.preserveClientIp !== undefined) { + this.setAttribute('preserve_client_ip.enabled', props.preserveClientIp ? 'true' : 'false'); + } + this.addTarget(...(props.targets || [])); } diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/target-group.test.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/target-group.test.ts index bdddc74b2fd53..90aa39b37141b 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/target-group.test.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/target-group.test.ts @@ -28,6 +28,29 @@ describe('tests', () => { }); }); + test('Enable preserve_client_ip attribute for target group', () => { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'Vpc'); + + // WHEN + new elbv2.NetworkTargetGroup(stack, 'Group', { + vpc, + port: 80, + preserveClientIp: true, + }); + + // THEN + expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::TargetGroup', { + TargetGroupAttributes: [ + { + Key: 'preserve_client_ip.enabled', + Value: 'true', + }, + ], + }); + }); + test('Disable proxy protocol v2 for attribute target group', () => { // GIVEN const stack = new cdk.Stack(); @@ -51,6 +74,29 @@ describe('tests', () => { }); }); + test('Disable preserve_client_ip attribute for target group', () => { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'Vpc'); + + // WHEN + new elbv2.NetworkTargetGroup(stack, 'Group', { + vpc, + port: 80, + preserveClientIp: false, + }); + + // THEN + expect(stack).toHaveResource('AWS::ElasticLoadBalancingV2::TargetGroup', { + TargetGroupAttributes: [ + { + Key: 'preserve_client_ip.enabled', + Value: 'false', + }, + ], + }); + }); + test('Configure protocols for target group', () => { const stack = new cdk.Stack(); const vpc = new ec2.Vpc(stack, 'Vpc'); From 7d2684edc67b6fc4ea957e1a2a44d726c08e4047 Mon Sep 17 00:00:00 2001 From: AWS CDK Automation <43080478+aws-cdk-automation@users.noreply.github.com> Date: Mon, 10 May 2021 20:00:24 +0100 Subject: [PATCH 006/134] chore: npm-check-updates && yarn upgrade (#14620) Ran npm-check-updates and yarn upgrade to keep the `yarn.lock` file up-to-date. --- package.json | 3 +- .../@aws-cdk/assert-internal/package.json | 2 +- packages/@aws-cdk/assert/package.json | 2 +- .../package.json | 4 +- .../aws-global-table-coordinator/package.json | 2 +- packages/@aws-cdk/aws-dynamodb/package.json | 2 +- .../@aws-cdk/aws-lambda-nodejs/package.json | 2 +- packages/@aws-cdk/aws-msk/package.json | 2 +- packages/@aws-cdk/aws-sam/package.json | 2 +- .../@aws-cdk/cloudformation-diff/package.json | 4 +- .../cloudformation-include/package.json | 2 +- .../@monocdk-experiment/assert/package.json | 2 +- .../rewrite-imports/package.json | 2 +- packages/aws-cdk-migration/package.json | 2 +- packages/aws-cdk/package.json | 6 +- packages/awslint/package.json | 2 +- packages/cdk-assets/package.json | 2 +- tools/cdk-build-tools/package.json | 4 +- tools/eslint-plugin-cdk/package.json | 2 +- tools/pkglint/package.json | 4 +- yarn.lock | 1153 +++++++---------- 21 files changed, 525 insertions(+), 681 deletions(-) diff --git a/package.json b/package.json index 3084c5a4bc032..0b56f8c255e56 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,8 @@ }, "tap-mocha-reporter-resolutions-comment": "should be removed or reviewed when nodeunit dependency is dropped or adjusted", "resolutions": { - "tap-mocha-reporter": "^5.0.1" + "tap-mocha-reporter": "^5.0.1", + "string-width": "^4.2.2" }, "repository": { "type": "git", diff --git a/packages/@aws-cdk/assert-internal/package.json b/packages/@aws-cdk/assert-internal/package.json index b280034b25dab..457b39b567656 100644 --- a/packages/@aws-cdk/assert-internal/package.json +++ b/packages/@aws-cdk/assert-internal/package.json @@ -26,7 +26,7 @@ "cdk-build-tools": "0.0.0", "jest": "^26.6.3", "pkglint": "0.0.0", - "ts-jest": "^26.5.5" + "ts-jest": "^26.5.6" }, "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 1b4c8f07bd6c7..110f058431b5f 100644 --- a/packages/@aws-cdk/assert/package.json +++ b/packages/@aws-cdk/assert/package.json @@ -40,7 +40,7 @@ "jest": "^26.6.3", "pkglint": "0.0.0", "@aws-cdk/assert-internal": "0.0.0", - "ts-jest": "^26.5.5" + "ts-jest": "^26.5.6" }, "dependencies": { "@aws-cdk/cloudformation-diff": "0.0.0", 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 e3dcc7e1b26c4..02dd246c76e47 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 @@ -32,7 +32,7 @@ "cdk-build-tools": "0.0.0", "aws-sdk": "^2.596.0", "aws-sdk-mock": "^5.1.0", - "eslint": "^7.25.0", + "eslint": "^7.26.0", "eslint-config-standard": "^14.1.1", "eslint-plugin-import": "^2.22.1", "eslint-plugin-node": "^11.1.0", @@ -42,6 +42,6 @@ "lambda-tester": "^3.6.0", "sinon": "^9.2.4", "nock": "^13.0.11", - "ts-jest": "^26.5.5" + "ts-jest": "^26.5.6" } } diff --git a/packages/@aws-cdk/aws-dynamodb-global/lambda-packages/aws-global-table-coordinator/package.json b/packages/@aws-cdk/aws-dynamodb-global/lambda-packages/aws-global-table-coordinator/package.json index 3225b16fa7879..c9d9180d2d772 100644 --- a/packages/@aws-cdk/aws-dynamodb-global/lambda-packages/aws-global-table-coordinator/package.json +++ b/packages/@aws-cdk/aws-dynamodb-global/lambda-packages/aws-global-table-coordinator/package.json @@ -29,7 +29,7 @@ "devDependencies": { "aws-sdk": "^2.596.0", "aws-sdk-mock": "^5.1.0", - "eslint": "^7.25.0", + "eslint": "^7.26.0", "eslint-config-standard": "^14.1.1", "eslint-plugin-import": "^2.22.1", "eslint-plugin-node": "^11.1.0", diff --git a/packages/@aws-cdk/aws-dynamodb/package.json b/packages/@aws-cdk/aws-dynamodb/package.json index f99331bc3df35..db9b6b51ea3f5 100644 --- a/packages/@aws-cdk/aws-dynamodb/package.json +++ b/packages/@aws-cdk/aws-dynamodb/package.json @@ -82,7 +82,7 @@ "jest": "^26.6.3", "pkglint": "0.0.0", "sinon": "^9.2.4", - "ts-jest": "^26.5.5", + "ts-jest": "^26.5.6", "@aws-cdk/assert-internal": "0.0.0" }, "dependencies": { diff --git a/packages/@aws-cdk/aws-lambda-nodejs/package.json b/packages/@aws-cdk/aws-lambda-nodejs/package.json index 8578724b9a9bd..ae5a2e85cfc8b 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/package.json +++ b/packages/@aws-cdk/aws-lambda-nodejs/package.json @@ -67,7 +67,7 @@ "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "delay": "5.0.0", - "esbuild": "^0.11.18", + "esbuild": "^0.11.20", "pkglint": "0.0.0", "@aws-cdk/assert-internal": "0.0.0" }, diff --git a/packages/@aws-cdk/aws-msk/package.json b/packages/@aws-cdk/aws-msk/package.json index eb2a3937e1793..ae1b9cbdbf3f9 100644 --- a/packages/@aws-cdk/aws-msk/package.json +++ b/packages/@aws-cdk/aws-msk/package.json @@ -78,7 +78,7 @@ "cfn2ts": "0.0.0", "cdk-integ-tools": "0.0.0", "pkglint": "0.0.0", - "jest": "^26.6.0", + "jest": "^26.6.3", "@aws-cdk/assert-internal": "0.0.0" }, "dependencies": { diff --git a/packages/@aws-cdk/aws-sam/package.json b/packages/@aws-cdk/aws-sam/package.json index ed7cad3507755..0cb76210d9b1c 100644 --- a/packages/@aws-cdk/aws-sam/package.json +++ b/packages/@aws-cdk/aws-sam/package.json @@ -77,7 +77,7 @@ "cfn2ts": "0.0.0", "jest": "^26.6.3", "pkglint": "0.0.0", - "ts-jest": "^26.5.5", + "ts-jest": "^26.5.6", "@aws-cdk/assert-internal": "0.0.0" }, "dependencies": { diff --git a/packages/@aws-cdk/cloudformation-diff/package.json b/packages/@aws-cdk/cloudformation-diff/package.json index 1142093d6f7a4..4f0e79827e7a0 100644 --- a/packages/@aws-cdk/cloudformation-diff/package.json +++ b/packages/@aws-cdk/cloudformation-diff/package.json @@ -26,7 +26,7 @@ "diff": "^5.0.0", "fast-deep-equal": "^3.1.3", "string-width": "^4.2.2", - "table": "^6.6.0" + "table": "^6.7.0" }, "devDependencies": { "@types/jest": "^26.0.23", @@ -35,7 +35,7 @@ "fast-check": "^2.14.0", "jest": "^26.6.3", "pkglint": "0.0.0", - "ts-jest": "^26.5.5" + "ts-jest": "^26.5.6" }, "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 2a68f7c70654f..218742424f03e 100644 --- a/packages/@aws-cdk/cloudformation-include/package.json +++ b/packages/@aws-cdk/cloudformation-include/package.json @@ -383,7 +383,7 @@ "cdk-integ-tools": "0.0.0", "jest": "^26.6.3", "pkglint": "0.0.0", - "ts-jest": "^26.5.5", + "ts-jest": "^26.5.6", "@aws-cdk/assert-internal": "0.0.0" }, "bundledDependencies": [ diff --git a/packages/@monocdk-experiment/assert/package.json b/packages/@monocdk-experiment/assert/package.json index a8676b333fc16..6dab583bf473a 100644 --- a/packages/@monocdk-experiment/assert/package.json +++ b/packages/@monocdk-experiment/assert/package.json @@ -41,7 +41,7 @@ "jest": "^26.6.3", "monocdk": "0.0.0", "pkglint": "0.0.0", - "ts-jest": "^26.5.5" + "ts-jest": "^26.5.6" }, "dependencies": { "@aws-cdk/cloudformation-diff": "0.0.0" diff --git a/packages/@monocdk-experiment/rewrite-imports/package.json b/packages/@monocdk-experiment/rewrite-imports/package.json index 966a486b7c730..fcfbe320c9d96 100644 --- a/packages/@monocdk-experiment/rewrite-imports/package.json +++ b/packages/@monocdk-experiment/rewrite-imports/package.json @@ -32,7 +32,7 @@ }, "license": "Apache-2.0", "dependencies": { - "glob": "^7.1.6", + "glob": "^7.1.7", "typescript": "~3.9.9" }, "devDependencies": { diff --git a/packages/aws-cdk-migration/package.json b/packages/aws-cdk-migration/package.json index 88869e06c699e..98ecfa881e256 100644 --- a/packages/aws-cdk-migration/package.json +++ b/packages/aws-cdk-migration/package.json @@ -32,7 +32,7 @@ }, "license": "Apache-2.0", "dependencies": { - "glob": "^7.1.6", + "glob": "^7.1.7", "typescript": "~3.9.9" }, "devDependencies": { diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index b061bffc1d7d9..b93eb05b444ec 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -62,7 +62,7 @@ "nock": "^13.0.11", "pkglint": "0.0.0", "sinon": "^9.2.4", - "ts-jest": "^26.5.5", + "ts-jest": "^26.5.6", "ts-mock-imports": "^1.3.4", "xml-js": "^1.6.11" }, @@ -78,14 +78,14 @@ "colors": "^1.4.0", "decamelize": "^5.0.0", "fs-extra": "^9.1.0", - "glob": "^7.1.6", + "glob": "^7.1.7", "json-diff": "^0.5.4", "minimatch": ">=3.0", "promptly": "^3.2.0", "proxy-agent": "^4.0.1", "semver": "^7.3.5", "source-map-support": "^0.5.19", - "table": "^6.6.0", + "table": "^6.7.0", "uuid": "^8.3.2", "wrap-ansi": "^7.0.0", "yaml": "1.10.2", diff --git a/packages/awslint/package.json b/packages/awslint/package.json index c0e95b9f90d12..dab312dc46ea0 100644 --- a/packages/awslint/package.json +++ b/packages/awslint/package.json @@ -31,7 +31,7 @@ "typescript": "~3.9.9", "@typescript-eslint/eslint-plugin": "^4.22.1", "@typescript-eslint/parser": "^4.22.1", - "eslint": "^7.25.0", + "eslint": "^7.26.0", "eslint-import-resolver-node": "^0.3.4", "eslint-import-resolver-typescript": "^2.4.0", "eslint-plugin-cdk": "0.0.0", diff --git a/packages/cdk-assets/package.json b/packages/cdk-assets/package.json index b5e1744e4be0e..aa751f80283d7 100644 --- a/packages/cdk-assets/package.json +++ b/packages/cdk-assets/package.json @@ -48,7 +48,7 @@ "@aws-cdk/cx-api": "0.0.0", "archiver": "^5.3.0", "aws-sdk": "^2.848.0", - "glob": "^7.1.6", + "glob": "^7.1.7", "yargs": "^16.2.0" }, "repository": { diff --git a/tools/cdk-build-tools/package.json b/tools/cdk-build-tools/package.json index ba0af73cb118f..994b0248e9882 100644 --- a/tools/cdk-build-tools/package.json +++ b/tools/cdk-build-tools/package.json @@ -44,7 +44,7 @@ "@typescript-eslint/parser": "^4.22.1", "awslint": "0.0.0", "colors": "^1.4.0", - "eslint": "^7.25.0", + "eslint": "^7.26.0", "eslint-import-resolver-node": "^0.3.4", "eslint-import-resolver-typescript": "^2.4.0", "eslint-plugin-cdk": "0.0.0", @@ -60,7 +60,7 @@ "nodeunit": "^0.11.3", "nyc": "^15.1.0", "semver": "^7.3.5", - "ts-jest": "^26.5.5", + "ts-jest": "^26.5.6", "typescript": "~3.9.9", "yargs": "^16.2.0", "yarn-cling": "0.0.0" diff --git a/tools/eslint-plugin-cdk/package.json b/tools/eslint-plugin-cdk/package.json index bf221962cbf2f..0d07037e93385 100644 --- a/tools/eslint-plugin-cdk/package.json +++ b/tools/eslint-plugin-cdk/package.json @@ -22,7 +22,7 @@ }, "dependencies": { "@typescript-eslint/parser": "^4.22.1", - "eslint": "^7.25.0", + "eslint": "^7.26.0", "fs-extra": "^9.1.0" }, "jest": { diff --git a/tools/pkglint/package.json b/tools/pkglint/package.json index bf21ffaeabb62..21c682d12b91d 100644 --- a/tools/pkglint/package.json +++ b/tools/pkglint/package.json @@ -42,7 +42,7 @@ "@types/yargs": "^15.0.13", "@typescript-eslint/eslint-plugin": "^4.22.1", "@typescript-eslint/parser": "^4.22.1", - "eslint": "^7.25.0", + "eslint": "^7.26.0", "eslint-import-resolver-node": "^0.3.4", "eslint-import-resolver-typescript": "^2.4.0", "eslint-plugin-cdk": "0.0.0", @@ -58,7 +58,7 @@ "case": "^1.6.3", "colors": "^1.4.0", "fs-extra": "^9.1.0", - "glob": "^7.1.6", + "glob": "^7.1.7", "npm-bundled": "^1.1.2", "semver": "^7.3.5", "yargs": "^16.2.0" diff --git a/yarn.lock b/yarn.lock index 12fbd37abc954..cd9959d6a69fe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16,48 +16,47 @@ dependencies: "@babel/highlight" "^7.12.13" -"@babel/compat-data@^7.13.8": - version "7.13.11" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.11.tgz#9c8fe523c206979c9a81b1e12fe50c1254f1aa35" - integrity sha512-BwKEkO+2a67DcFeS3RLl0Z3Gs2OvdXewuWjc1Hfokhb5eQWP9YRYH1/+VrVZvql2CfjOiNGqSAFOYt4lsqTHzg== +"@babel/compat-data@^7.13.15": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.0.tgz#a901128bce2ad02565df95e6ecbf195cf9465919" + integrity sha512-vu9V3uMM/1o5Hl5OekMUowo3FqXLJSw+s+66nt0fSWVWTtmosdzn45JHOB3cPtZoe6CTBDzvSw0RdOY85Q37+Q== "@babel/core@^7.1.0", "@babel/core@^7.7.5": - version "7.13.10" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.13.10.tgz#07de050bbd8193fcd8a3c27918c0890613a94559" - integrity sha512-bfIYcT0BdKeAZrovpMqX2Mx5NrgAckGbwT982AkdS5GNfn3KMGiprlBAtmBcFZRUmpaufS6WZFP8trvx8ptFDw== + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.14.0.tgz#47299ff3ec8d111b493f1a9d04bf88c04e728d88" + integrity sha512-8YqpRig5NmIHlMLw09zMlPTvUVMILjqCOtVgu+TVNWEBvy9b5I3RRyhqnrV4hjgEK7n8P9OqvkWJAFmEL6Wwfw== dependencies: "@babel/code-frame" "^7.12.13" - "@babel/generator" "^7.13.9" - "@babel/helper-compilation-targets" "^7.13.10" - "@babel/helper-module-transforms" "^7.13.0" - "@babel/helpers" "^7.13.10" - "@babel/parser" "^7.13.10" + "@babel/generator" "^7.14.0" + "@babel/helper-compilation-targets" "^7.13.16" + "@babel/helper-module-transforms" "^7.14.0" + "@babel/helpers" "^7.14.0" + "@babel/parser" "^7.14.0" "@babel/template" "^7.12.13" - "@babel/traverse" "^7.13.0" - "@babel/types" "^7.13.0" + "@babel/traverse" "^7.14.0" + "@babel/types" "^7.14.0" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.1.2" - lodash "^4.17.19" semver "^6.3.0" source-map "^0.5.0" -"@babel/generator@^7.13.0", "@babel/generator@^7.13.9", "@babel/generator@^7.4.0": - version "7.13.9" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.13.9.tgz#3a7aa96f9efb8e2be42d38d80e2ceb4c64d8de39" - integrity sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw== +"@babel/generator@^7.14.0", "@babel/generator@^7.4.0": + version "7.14.1" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.1.tgz#1f99331babd65700183628da186f36f63d615c93" + integrity sha512-TMGhsXMXCP/O1WtQmZjpEYDhCYC9vFhayWZPJSZCGkPJgUqX0rF0wwtrYvnzVxIjcF80tkUertXVk5cwqi5cAQ== dependencies: - "@babel/types" "^7.13.0" + "@babel/types" "^7.14.1" jsesc "^2.5.1" source-map "^0.5.0" -"@babel/helper-compilation-targets@^7.13.10": - version "7.13.10" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.10.tgz#1310a1678cb8427c07a753750da4f8ce442bdd0c" - integrity sha512-/Xju7Qg1GQO4mHZ/Kcs6Au7gfafgZnwm+a7sy/ow/tV1sHeraRUHbjdat8/UvDor4Tez+siGKDk6zIKtCPKVJA== +"@babel/helper-compilation-targets@^7.13.16": + version "7.13.16" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz#6e91dccf15e3f43e5556dffe32d860109887563c" + integrity sha512-3gmkYIrpqsLlieFwjkGgLaSHmhnvlAYzZLlYVjlW+QwI+1zE17kGxuJGmIqDQdYp56XdmGeD+Bswx0UTyG18xA== dependencies: - "@babel/compat-data" "^7.13.8" + "@babel/compat-data" "^7.13.15" "@babel/helper-validator-option" "^7.12.17" browserslist "^4.14.5" semver "^6.3.0" @@ -78,34 +77,33 @@ dependencies: "@babel/types" "^7.12.13" -"@babel/helper-member-expression-to-functions@^7.13.0": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.0.tgz#6aa4bb678e0f8c22f58cdb79451d30494461b091" - integrity sha512-yvRf8Ivk62JwisqV1rFRMxiSMDGnN6KH1/mDMmIrij4jztpQNRoHqqMG3U6apYbGRPJpgPalhva9Yd06HlUxJQ== +"@babel/helper-member-expression-to-functions@^7.13.12": + version "7.13.12" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz#dfe368f26d426a07299d8d6513821768216e6d72" + integrity sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw== dependencies: - "@babel/types" "^7.13.0" + "@babel/types" "^7.13.12" -"@babel/helper-module-imports@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz#ec67e4404f41750463e455cc3203f6a32e93fcb0" - integrity sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g== +"@babel/helper-module-imports@^7.13.12": + version "7.13.12" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz#c6a369a6f3621cb25da014078684da9196b61977" + integrity sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA== dependencies: - "@babel/types" "^7.12.13" + "@babel/types" "^7.13.12" -"@babel/helper-module-transforms@^7.13.0": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.13.0.tgz#42eb4bd8eea68bab46751212c357bfed8b40f6f1" - integrity sha512-Ls8/VBwH577+pw7Ku1QkUWIyRRNHpYlts7+qSqBBFCW3I8QteB9DxfcZ5YJpOwH6Ihe/wn8ch7fMGOP1OhEIvw== +"@babel/helper-module-transforms@^7.14.0": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.14.0.tgz#8fcf78be220156f22633ee204ea81f73f826a8ad" + integrity sha512-L40t9bxIuGOfpIGA3HNkJhU9qYrf4y5A5LUSw7rGMSn+pcG8dfJ0g6Zval6YJGd2nEjI7oP00fRdnhLKndx6bw== dependencies: - "@babel/helper-module-imports" "^7.12.13" - "@babel/helper-replace-supers" "^7.13.0" - "@babel/helper-simple-access" "^7.12.13" + "@babel/helper-module-imports" "^7.13.12" + "@babel/helper-replace-supers" "^7.13.12" + "@babel/helper-simple-access" "^7.13.12" "@babel/helper-split-export-declaration" "^7.12.13" - "@babel/helper-validator-identifier" "^7.12.11" + "@babel/helper-validator-identifier" "^7.14.0" "@babel/template" "^7.12.13" - "@babel/traverse" "^7.13.0" - "@babel/types" "^7.13.0" - lodash "^4.17.19" + "@babel/traverse" "^7.14.0" + "@babel/types" "^7.14.0" "@babel/helper-optimise-call-expression@^7.12.13": version "7.12.13" @@ -119,22 +117,22 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz#806526ce125aed03373bc416a828321e3a6a33af" integrity sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ== -"@babel/helper-replace-supers@^7.13.0": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.13.0.tgz#6034b7b51943094cb41627848cb219cb02be1d24" - integrity sha512-Segd5me1+Pz+rmN/NFBOplMbZG3SqRJOBlY+mA0SxAv6rjj7zJqr1AVr3SfzUVTLCv7ZLU5FycOM/SBGuLPbZw== +"@babel/helper-replace-supers@^7.13.12": + version "7.13.12" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.13.12.tgz#6442f4c1ad912502481a564a7386de0c77ff3804" + integrity sha512-Gz1eiX+4yDO8mT+heB94aLVNCL+rbuT2xy4YfyNqu8F+OI6vMvJK891qGBTqL9Uc8wxEvRW92Id6G7sDen3fFw== dependencies: - "@babel/helper-member-expression-to-functions" "^7.13.0" + "@babel/helper-member-expression-to-functions" "^7.13.12" "@babel/helper-optimise-call-expression" "^7.12.13" "@babel/traverse" "^7.13.0" - "@babel/types" "^7.13.0" + "@babel/types" "^7.13.12" -"@babel/helper-simple-access@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.12.13.tgz#8478bcc5cacf6aa1672b251c1d2dde5ccd61a6c4" - integrity sha512-0ski5dyYIHEfwpWGx5GPWhH35j342JaflmCeQmsPWcrOQDtCN6C1zKAVRFVbK53lPW2c9TsuLLSUDf0tIGJ5hA== +"@babel/helper-simple-access@^7.13.12": + version "7.13.12" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz#dd6c538afb61819d205a012c31792a39c7a5eaf6" + integrity sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA== dependencies: - "@babel/types" "^7.12.13" + "@babel/types" "^7.13.12" "@babel/helper-split-export-declaration@^7.12.13": version "7.12.13" @@ -143,38 +141,38 @@ dependencies: "@babel/types" "^7.12.13" -"@babel/helper-validator-identifier@^7.12.11": - version "7.12.11" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed" - integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw== +"@babel/helper-validator-identifier@^7.14.0": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz#d26cad8a47c65286b15df1547319a5d0bcf27288" + integrity sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A== "@babel/helper-validator-option@^7.12.17": version "7.12.17" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz#d1fbf012e1a79b7eebbfdc6d270baaf8d9eb9831" integrity sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw== -"@babel/helpers@^7.13.10": - version "7.13.10" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.13.10.tgz#fd8e2ba7488533cdeac45cc158e9ebca5e3c7df8" - integrity sha512-4VO883+MWPDUVRF3PhiLBUFHoX/bsLTGFpFK/HqvvfBZz2D57u9XzPVNFVBTc0PW/CWR9BXTOKt8NF4DInUHcQ== +"@babel/helpers@^7.14.0": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.14.0.tgz#ea9b6be9478a13d6f961dbb5f36bf75e2f3b8f62" + integrity sha512-+ufuXprtQ1D1iZTO/K9+EBRn+qPWMJjZSw/S0KlFrxCw4tkrzv9grgpDHkY9MeQTjTY8i2sp7Jep8DfU6tN9Mg== dependencies: "@babel/template" "^7.12.13" - "@babel/traverse" "^7.13.0" - "@babel/types" "^7.13.0" + "@babel/traverse" "^7.14.0" + "@babel/types" "^7.14.0" "@babel/highlight@^7.10.4", "@babel/highlight@^7.12.13": - version "7.13.10" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.13.10.tgz#a8b2a66148f5b27d666b15d81774347a731d52d1" - integrity sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg== + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.0.tgz#3197e375711ef6bf834e67d0daec88e4f46113cf" + integrity sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg== dependencies: - "@babel/helper-validator-identifier" "^7.12.11" + "@babel/helper-validator-identifier" "^7.14.0" chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.12.13", "@babel/parser@^7.13.0", "@babel/parser@^7.13.10", "@babel/parser@^7.4.3": - version "7.13.11" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.11.tgz#f93ebfc99d21c1772afbbaa153f47e7ce2f50b88" - integrity sha512-PhuoqeHoO9fc4ffMEVk4qb/w/s2iOSWohvbHxLtxui0eBg3Lg5gN1U8wp1V1u61hOWkPQJJyJzGH6Y+grwkq8Q== +"@babel/parser@^7.1.0", "@babel/parser@^7.12.13", "@babel/parser@^7.14.0", "@babel/parser@^7.4.3": + version "7.14.1" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.1.tgz#1bd644b5db3f5797c4479d89ec1817fe02b84c47" + integrity sha512-muUGEKu8E/ftMTPlNp+mc6zL3E9zKWmF5sDHZ5MSsoTP9Wyz64AhEf9kD08xYJ7w6Hdcu8H550ircnPyWSIF0Q== "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" @@ -269,28 +267,26 @@ "@babel/parser" "^7.12.13" "@babel/types" "^7.12.13" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.4.3": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.0.tgz#6d95752475f86ee7ded06536de309a65fc8966cc" - integrity sha512-xys5xi5JEhzC3RzEmSGrs/b3pJW/o87SypZ+G/PhaE7uqVQNv/jlmVIBXuoh5atqQ434LfXV+sf23Oxj0bchJQ== +"@babel/traverse@^7.1.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.14.0", "@babel/traverse@^7.4.3": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.0.tgz#cea0dc8ae7e2b1dec65f512f39f3483e8cc95aef" + integrity sha512-dZ/a371EE5XNhTHomvtuLTUyx6UEoJmYX+DT5zBCQN3McHemsuIaKKYqsc/fs26BEkHs/lBZy0J571LP5z9kQA== dependencies: "@babel/code-frame" "^7.12.13" - "@babel/generator" "^7.13.0" + "@babel/generator" "^7.14.0" "@babel/helper-function-name" "^7.12.13" "@babel/helper-split-export-declaration" "^7.12.13" - "@babel/parser" "^7.13.0" - "@babel/types" "^7.13.0" + "@babel/parser" "^7.14.0" + "@babel/types" "^7.14.0" debug "^4.1.0" globals "^11.1.0" - lodash "^4.17.19" -"@babel/types@^7.0.0", "@babel/types@^7.12.13", "@babel/types@^7.13.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.0": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.0.tgz#74424d2816f0171b4100f0ab34e9a374efdf7f80" - integrity sha512-hE+HE8rnG1Z6Wzo+MhaKE5lM5eMx71T4EHJgku2E3xIfaULhDcxiiRxUYgwX8qwP1BBSlag+TdGOt6JAidIZTA== +"@babel/types@^7.0.0", "@babel/types@^7.12.13", "@babel/types@^7.13.12", "@babel/types@^7.14.0", "@babel/types@^7.14.1", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.0": + version "7.14.1" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.1.tgz#095bd12f1c08ab63eff6e8f7745fa7c9cc15a9db" + integrity sha512-S13Qe85fzLs3gYRUnrpyeIrBJIMYv33qSTg1qoBwiG6nPKwUWAD9odSzWhEedpwOIzSEI6gbdQIWEMiCI42iBA== dependencies: - "@babel/helper-validator-identifier" "^7.12.11" - lodash "^4.17.19" + "@babel/helper-validator-identifier" "^7.14.0" to-fast-properties "^2.0.0" "@balena/dockerignore@^1.0.2": @@ -311,10 +307,10 @@ exec-sh "^0.3.2" minimist "^1.2.0" -"@eslint/eslintrc@^0.4.0": - version "0.4.0" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.0.tgz#99cc0a0584d72f1df38b900fb062ba995f395547" - integrity sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog== +"@eslint/eslintrc@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.1.tgz#442763b88cecbe3ee0ec7ca6d6dd6168550cbf14" + integrity sha512-5v7TDE9plVhvxQeWLXDTvFvJBdH6pEsdnl2g/dAptmuFEPedQ4Erq5rsDsX+mvAM610IhNaO2W5V1dOOnDKxkQ== dependencies: ajv "^6.12.4" debug "^4.1.1" @@ -1218,18 +1214,17 @@ integrity sha512-oN3y7FAROHhrAt7Rr7PnTSwrHrZVRTS2ZbyxeQwSSYD0ifwM3YNgQqbaRmjcWoPyq77MjchusjJDspbzMmip1Q== "@npmcli/git@^2.0.1": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-2.0.6.tgz#47b97e96b2eede3f38379262fa3bdfa6eae57bf2" - integrity sha512-a1MnTfeRPBaKbFY07fd+6HugY1WAkKJzdiJvlRub/9o5xz2F/JtPacZZapx5zRJUQFIzSL677vmTSxEcDMrDbg== + version "2.0.9" + resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-2.0.9.tgz#915bbfe66300e67b4da5ef765a4475ffb2ca5b6b" + integrity sha512-hTMbMryvOqGLwnmMBKs5usbPsJtyEsMsgXwJbmNrsEuQQh1LAIMDU77IoOrwkCg+NgQWl+ySlarJASwM3SutCA== dependencies: - "@npmcli/promise-spawn" "^1.1.0" + "@npmcli/promise-spawn" "^1.3.2" lru-cache "^6.0.0" - mkdirp "^1.0.3" - npm-pick-manifest "^6.0.0" + mkdirp "^1.0.4" + npm-pick-manifest "^6.1.1" promise-inflight "^1.0.1" promise-retry "^2.0.1" - semver "^7.3.2" - unique-filename "^1.1.1" + semver "^7.3.5" which "^2.0.2" "@npmcli/installed-package-contents@^1.0.6": @@ -1253,7 +1248,7 @@ resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-1.0.2.tgz#3cdc1f30e9736dbc417373ed803b42b1a0a29ede" integrity sha512-yrJUe6reVMpktcvagumoqD9r08fH1iRo01gn1u0zoCApa9lnZGEigVKUd2hzsCId4gdtkZZIVscLhNxMECKgRg== -"@npmcli/promise-spawn@^1.1.0", "@npmcli/promise-spawn@^1.2.0", "@npmcli/promise-spawn@^1.3.2": +"@npmcli/promise-spawn@^1.2.0", "@npmcli/promise-spawn@^1.3.2": version "1.3.2" resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz#42d4e56a8e9274fba180dabc0aea6e38f29274f5" integrity sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg== @@ -1261,9 +1256,9 @@ infer-owner "^1.0.4" "@npmcli/run-script@^1.8.2": - version "1.8.4" - resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-1.8.4.tgz#03ced92503a6fe948cbc0975ce39210bc5e824d6" - integrity sha512-Yd9HXTtF1JGDXZw0+SOn+mWLYS0e7bHBHVC/2C8yqs4wUrs/k8rwBSinD7rfk+3WG/MFGRZKxjyoD34Pch2E/A== + version "1.8.5" + resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-1.8.5.tgz#f250a0c5e1a08a792d775a315d0ff42fc3a51e1d" + integrity sha512-NQspusBCpTjNwNRFMtz2C5MxoxyzlbuJ4YEhxAKrIonTiirKDtatsZictx9RgamQIx6+QuHMNmPl0wQdoESs9A== dependencies: "@npmcli/node-gyp" "^1.0.2" "@npmcli/promise-spawn" "^1.3.2" @@ -1279,9 +1274,9 @@ "@octokit/types" "^6.0.3" "@octokit/core@^3.2.3": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.3.1.tgz#c6bb6ba171ad84a5f430853a98892cfe8f93d8cd" - integrity sha512-Dc5NNQOYjgZU5S1goN6A/E500yXOfDUFRGQB8/2Tl16AcfvS3H9PudyOe3ZNE/MaVyHPIfC0htReHMJb1tMrvw== + version "3.4.0" + resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.4.0.tgz#b48aa27d755b339fe7550548b340dcc2b513b742" + integrity sha512-6/vlKPP8NF17cgYXqucdshWqmMZGXkuvtcrWCgU5NOI0Pl2GjlmZyWgBMrU8zJ3v2MJlM6++CiB45VKYmhiWWg== dependencies: "@octokit/auth-token" "^2.4.4" "@octokit/graphql" "^4.5.8" @@ -1309,15 +1304,10 @@ "@octokit/types" "^6.0.3" universal-user-agent "^6.0.0" -"@octokit/openapi-types@^5.3.2": - version "5.3.2" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-5.3.2.tgz#b8ac43c5c3d00aef61a34cf744e315110c78deb4" - integrity sha512-NxF1yfYOUO92rCx3dwvA2onF30Vdlg7YUkMVXkeptqpzA3tRLplThhFleV/UKWFgh7rpKu1yYRbvNDUtzSopKA== - -"@octokit/openapi-types@^6.2.0": - version "6.2.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-6.2.0.tgz#6ea796b20c7111b9e422a4d607f796c1179622cd" - integrity sha512-V2vFYuawjpP5KUb8CPYsq20bXT4qnE8sH1QKpYqUlcNOntBiRr/VzGVvY0s+YXGgrVbFUVO4EI0VnHYSVBWfBg== +"@octokit/openapi-types@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-7.0.0.tgz#0f6992db9854af15eca77d71ab0ec7fad2f20411" + integrity sha512-gV/8DJhAL/04zjTI95a7FhQwS6jlEE0W/7xeYAzuArD0KVAVWDLP2f3vi98hs3HLTczxXdRK/mF0tRoQPpolEw== "@octokit/plugin-enterprise-rest@^6.0.1": version "6.0.1" @@ -1325,9 +1315,9 @@ integrity sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw== "@octokit/plugin-paginate-rest@^2.6.2": - version "2.13.2" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.13.2.tgz#7b8244a0dd7a31135ba2adc58a533213837bfe87" - integrity sha512-mjfBcla00UNS4EI/NN7toEbUM45ow3kk4go+LxsXAFLQodsrXcIZbftUhXTqi6ZKd+r6bcqMI+Lv4dshLtFjww== + version "2.13.3" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.13.3.tgz#f0f1792230805108762d87906fb02d573b9e070a" + integrity sha512-46lptzM9lTeSmIBt/sVP/FLSTPGx6DCzAdSX3PfeJ3mTf4h9sGC26WpaQzMEq/Z44cOcmx8VsOhO+uEgE3cjYg== dependencies: "@octokit/types" "^6.11.0" @@ -1336,14 +1326,6 @@ resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.3.tgz#70a62be213e1edc04bb8897ee48c311482f9700d" integrity sha512-4RFU4li238jMJAzLgAwkBAw+4Loile5haQMQr+uhFq27BmyJXcXSKvoQKqh0agsZEiUlW6iSv3FAgvmGkur7OQ== -"@octokit/plugin-rest-endpoint-methods@4.13.5": - version "4.13.5" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.13.5.tgz#ad76285b82fe05fbb4adf2774a9c887f3534a880" - integrity sha512-kYKcWkFm4Ldk8bZai2RVEP1z97k1C/Ay2FN9FNTBg7JIyKoiiJjks4OtT6cuKeZX39tqa+C3J9xeYc6G+6g8uQ== - dependencies: - "@octokit/types" "^6.12.2" - deprecation "^2.3.1" - "@octokit/plugin-rest-endpoint-methods@5.0.1": version "5.0.1" resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.0.1.tgz#631b8d4edc6798b03489911252a25f2a4e58c594" @@ -1362,30 +1344,18 @@ once "^1.4.0" "@octokit/request@^5.3.0", "@octokit/request@^5.4.12": - version "5.4.14" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.14.tgz#ec5f96f78333bb2af390afa5ff66f114b063bc96" - integrity sha512-VkmtacOIQp9daSnBmDI92xNIeLuSRDOIuplp/CJomkvzt7M18NXgG044Cx/LFKLgjKt9T2tZR6AtJayba9GTSA== + version "5.4.15" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.15.tgz#829da413dc7dd3aa5e2cdbb1c7d0ebe1f146a128" + integrity sha512-6UnZfZzLwNhdLRreOtTkT9n57ZwulCve8q3IT/Z477vThu6snfdkBuhxnChpOKNGxcQ71ow561Qoa6uqLdPtag== dependencies: "@octokit/endpoint" "^6.0.1" "@octokit/request-error" "^2.0.0" "@octokit/types" "^6.7.1" - deprecation "^2.0.0" is-plain-object "^5.0.0" node-fetch "^2.6.1" - once "^1.4.0" universal-user-agent "^6.0.0" -"@octokit/rest@^18.1.0": - version "18.3.5" - resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.3.5.tgz#a89903d46e0b4273bd3234674ec2777a651d68ab" - integrity sha512-ZPeRms3WhWxQBEvoIh0zzf8xdU2FX0Capa7+lTca8YHmRsO3QNJzf1H3PcuKKsfgp91/xVDRtX91sTe1kexlbw== - dependencies: - "@octokit/core" "^3.2.3" - "@octokit/plugin-paginate-rest" "^2.6.2" - "@octokit/plugin-request-log" "^1.0.2" - "@octokit/plugin-rest-endpoint-methods" "4.13.5" - -"@octokit/rest@^18.5.3": +"@octokit/rest@^18.1.0", "@octokit/rest@^18.5.3": version "18.5.3" resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.5.3.tgz#6a2e6006a87ebbc34079c419258dd29ec9ff659d" integrity sha512-KPAsUCr1DOdLVbZJgGNuE/QVLWEaVBpFQwDAz/2Cnya6uW2wJ/P5RVGk0itx7yyN1aGa8uXm2pri4umEqG1JBA== @@ -1395,24 +1365,17 @@ "@octokit/plugin-request-log" "^1.0.2" "@octokit/plugin-rest-endpoint-methods" "5.0.1" -"@octokit/types@^6.0.3", "@octokit/types@^6.11.0", "@octokit/types@^6.12.2", "@octokit/types@^6.7.1": - version "6.12.2" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.12.2.tgz#5b44add079a478b8eb27d78cf384cc47e4411362" - integrity sha512-kCkiN8scbCmSq+gwdJV0iLgHc0O/GTPY1/cffo9kECu1MvatLPh9E+qFhfRIktKfHEA6ZYvv6S1B4Wnv3bi3pA== +"@octokit/types@^6.0.3", "@octokit/types@^6.11.0", "@octokit/types@^6.13.1", "@octokit/types@^6.7.1": + version "6.14.2" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.14.2.tgz#64c9457f38fb8522bdbba3c8cc814590a2d61bf5" + integrity sha512-wiQtW9ZSy4OvgQ09iQOdyXYNN60GqjCL/UdMsepDr1Gr0QzpW6irIKbH3REuAHXAhxkEk9/F2a3Gcs1P6kW5jA== dependencies: - "@octokit/openapi-types" "^5.3.2" - -"@octokit/types@^6.13.1": - version "6.14.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.14.0.tgz#587529b4a461d8b7621b99845718dc5c79281f52" - integrity sha512-43qHvDsPsKgNt4W4al3dyU6s2XZ7ZMsiiIw8rQcM9CyEo7g9W8/6m1W4xHuRqmEjTfG1U4qsE/E4Jftw1/Ak1g== - dependencies: - "@octokit/openapi-types" "^6.2.0" + "@octokit/openapi-types" "^7.0.0" "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.1": - version "1.8.2" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.2.tgz#858f5c4b48d80778fde4b9d541f27edc0d56488b" - integrity sha512-sruwd86RJHdsVf/AtBoijDmUqJp3B6hF/DGC23C+JaegnDHaZyewCjoVGTdg3J0uz3Zs7NnIT05OBOmML72lQw== + version "1.8.3" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" + integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== dependencies: type-detect "4.0.8" @@ -1455,9 +1418,9 @@ integrity sha512-lCTyeRm3NWqSwDnoji0z82Pl0tsOpr1p+33AiNeidgarloWXh3wdiVRUuxEa+sY9S5YLOYGz5X3N3Zvpibvm5w== "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": - version "7.1.13" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.13.tgz#bc6eea53975fdf163aff66c086522c6f293ae4cf" - integrity sha512-CC6amBNND16pTk4K3ZqKIaba6VGKAQs3gMjEY17FVd56oI/ZWt9OhS6riYiWv9s8ENbYUi7p8lgqb0QHQvUKQQ== + version "7.1.14" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.14.tgz#faaeefc4185ec71c389f4501ee5ec84b170cc402" + integrity sha512-zGZJzzBUVDo/eV6KgbE0f0ZI7dInEYvo12Rb70uNQDshC3SkRMb67ja0GgRHZgAX3Za6rhaWlvbDO8rrGyAb1g== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" @@ -1496,9 +1459,9 @@ "@types/json-schema" "*" "@types/estree@*": - version "0.0.46" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.46.tgz#0fb6bfbbeabd7a30880504993369c4bf1deab1fe" - integrity sha512-laIjwTQaD+5DukBZaygQ79K1Z0jb1bPEMRrkXSLjtCcZm+abyp5YbrqpSLzD42FwWW6gK/aS4NYpJ804nG2brg== + version "0.0.47" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.47.tgz#d7a51db20f0650efec24cd04994f523d93172ed4" + integrity sha512-c5ciR06jK8u9BstrmJyO97m+klJrrhCf9u3rLu3DEAJBirxRqSCvDQoYKmxuYwQI5SZChAWu+tq9oVlGRuzPAg== "@types/fs-extra@^8.1.1": version "8.1.1" @@ -1578,12 +1541,7 @@ dependencies: "@types/node" "*" -"@types/minimatch@*", "@types/minimatch@^3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" - integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== - -"@types/minimatch@^3.0.4": +"@types/minimatch@*", "@types/minimatch@^3.0.3", "@types/minimatch@^3.0.4": version "3.0.4" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.4.tgz#f0ec25dbf2f0e4b18647313ac031134ca5b24b21" integrity sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA== @@ -1605,16 +1563,21 @@ resolved "https://registry.yarnpkg.com/@types/mockery/-/mockery-1.4.29.tgz#9ba22df37f07e3780fff8531d1a38e633f9457a5" integrity sha1-m6It838H43gP/4Ux0aOOYz+UV6U= -"@types/node@*", "@types/node@^14.14.33": - version "14.14.35" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.35.tgz#42c953a4e2b18ab931f72477e7012172f4ffa313" - integrity sha512-Lt+wj8NVPx0zUmUwumiVXapmaLUcAk3yPuHCFVXras9k5VT9TdhJqKqGVUQCD60OTMCl0qxJ57OiTL0Mic3Iag== +"@types/node@*": + version "15.0.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.0.2.tgz#51e9c0920d1b45936ea04341aa3e2e58d339fb67" + integrity sha512-p68+a+KoxpoB47015IeYZYRrdqMUcpbK8re/zpFB8Ld46LHC1lPEbp3EXgkEhAYEcPvjJF6ZO+869SQ0aH1dcA== "@types/node@^10.17.59": version "10.17.59" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.59.tgz#03f440ccf746a27f7da6e141e6cbae64681dbd2f" integrity sha512-7Uc8IRrL8yZz5ti45RaFxpbU8TxlzdC3HvxV+hOWo1EyLsuKv/w7y0n+TwZzwL3vdx3oZ2k3ubxPq131hNtXyg== +"@types/node@^14.14.33": + version "14.14.44" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.44.tgz#df7503e6002847b834371c004b372529f3f85215" + integrity sha512-+gaugz6Oce6ZInfI/tK4Pq5wIIkJMEJUu92RB3Eu93mtj4wjjjz9EB5mLp5s1pSsLXdC/CPut/xF20ZzAQJbTA== + "@types/nodeunit@^0.0.31": version "0.0.31" resolved "https://registry.yarnpkg.com/@types/nodeunit/-/nodeunit-0.0.31.tgz#67eb52ad22326c7d1d9febe99d553f33b166126d" @@ -1682,9 +1645,11 @@ string-width "*" "@types/table@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@types/table/-/table-6.0.0.tgz#c3e8f1e0d80525036a7655fd650409e0230f1ead" - integrity sha512-RSmRiYetRzpcZcgNo4x6C1VSsPGBHCGGDO7Rbnz5esVLbGONxBP1CUcn8JhAkVzUVLO+AY8yOSkb27jvfauLyg== + version "6.3.2" + resolved "https://registry.yarnpkg.com/@types/table/-/table-6.3.2.tgz#e18ad2594400d81c3da28c31b342eb5a0d87a8e7" + integrity sha512-GJ82z3vQbx2BhiUo12w2A3lyBpXPJrGHjQ7iS5aH925098w8ojqiWBhgOUy97JS2PKLmRCTLT0sI+gJI4futig== + dependencies: + table "*" "@types/uuid@^8.3.0": version "8.3.0" @@ -1741,7 +1706,7 @@ semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@4.22.1": +"@typescript-eslint/experimental-utils@4.22.1", "@typescript-eslint/experimental-utils@^4.0.1": version "4.22.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.22.1.tgz#3938a5c89b27dc9a39b5de63a62ab1623ab27497" integrity sha512-svYlHecSMCQGDO2qN1v477ax/IDQwWhc7PRBiwAdAMJE7GXk5stF4Z9R/8wbRkuX/5e9dHqbIWxjeOjckK3wLQ== @@ -1753,18 +1718,6 @@ eslint-scope "^5.0.0" eslint-utils "^2.0.0" -"@typescript-eslint/experimental-utils@^4.0.1": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.18.0.tgz#ed6c955b940334132b17100d2917449b99a91314" - integrity sha512-92h723Kblt9JcT2RRY3QS2xefFKar4ZQFVs3GityOKWQYgtajxt/tuXIzL7sVCUlM1hgreiV5gkGYyBpdOwO6A== - dependencies: - "@types/json-schema" "^7.0.3" - "@typescript-eslint/scope-manager" "4.18.0" - "@typescript-eslint/types" "4.18.0" - "@typescript-eslint/typescript-estree" "4.18.0" - eslint-scope "^5.0.0" - eslint-utils "^2.0.0" - "@typescript-eslint/parser@^4.22.1": version "4.22.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.22.1.tgz#a95bda0fd01d994a15fc3e99dc984294f25c19cc" @@ -1775,14 +1728,6 @@ "@typescript-eslint/typescript-estree" "4.22.1" debug "^4.1.1" -"@typescript-eslint/scope-manager@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.18.0.tgz#d75b55234c35d2ff6ac945758d6d9e53be84a427" - integrity sha512-olX4yN6rvHR2eyFOcb6E4vmhDPsfdMyfQ3qR+oQNkAv8emKKlfxTWUXU5Mqxs2Fwe3Pf1BoPvrwZtwngxDzYzQ== - dependencies: - "@typescript-eslint/types" "4.18.0" - "@typescript-eslint/visitor-keys" "4.18.0" - "@typescript-eslint/scope-manager@4.22.1": version "4.22.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.22.1.tgz#5bb357f94f9cd8b94e6be43dd637eb73b8f355b4" @@ -1791,29 +1736,11 @@ "@typescript-eslint/types" "4.22.1" "@typescript-eslint/visitor-keys" "4.22.1" -"@typescript-eslint/types@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.18.0.tgz#bebe323f81f2a7e2e320fac9415e60856267584a" - integrity sha512-/BRociARpj5E+9yQ7cwCF/SNOWwXJ3qhjurMuK2hIFUbr9vTuDeu476Zpu+ptxY2kSxUHDGLLKy+qGq2sOg37A== - "@typescript-eslint/types@4.22.1": version "4.22.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.22.1.tgz#bf99c6cec0b4a23d53a61894816927f2adad856a" integrity sha512-2HTkbkdAeI3OOcWbqA8hWf/7z9c6gkmnWNGz0dKSLYLWywUlkOAQ2XcjhlKLj5xBFDf8FgAOF5aQbnLRvgNbCw== -"@typescript-eslint/typescript-estree@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.18.0.tgz#756d3e61da8c16ab99185532c44872f4cd5538cb" - integrity sha512-wt4xvF6vvJI7epz+rEqxmoNQ4ZADArGQO9gDU+cM0U5fdVv7N+IAuVoVAoZSOZxzGHBfvE3XQMLdy+scsqFfeg== - dependencies: - "@typescript-eslint/types" "4.18.0" - "@typescript-eslint/visitor-keys" "4.18.0" - debug "^4.1.1" - globby "^11.0.1" - is-glob "^4.0.1" - semver "^7.3.2" - tsutils "^3.17.1" - "@typescript-eslint/typescript-estree@4.22.1": version "4.22.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.22.1.tgz#dca379eead8cdfd4edc04805e83af6d148c164f9" @@ -1827,14 +1754,6 @@ semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/visitor-keys@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.18.0.tgz#4e6fe2a175ee33418318a029610845a81e2ff7b6" - integrity sha512-Q9t90JCvfYaN0OfFUgaLqByOfz8yPeTAdotn/XYNm5q9eHax90gzdb+RJ6E9T5s97Kv/UHWKERTmqA0jTKAEHw== - dependencies: - "@typescript-eslint/types" "4.18.0" - eslint-visitor-keys "^2.0.0" - "@typescript-eslint/visitor-keys@4.22.1": version "4.22.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.22.1.tgz#6045ae25a11662c671f90b3a403d682dfca0b7a6" @@ -1856,7 +1775,7 @@ JSONStream@^1.0.4: jsonparse "^1.2.0" through ">=2.2.7 <3" -abab@^2.0.3: +abab@^2.0.3, abab@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== @@ -1889,6 +1808,11 @@ acorn@^7.1.1, acorn@^7.4.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== +acorn@^8.1.0: + version "8.2.4" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.2.4.tgz#caba24b08185c3b56e3168e97d15ed17f4d31fd0" + integrity sha512-Ibt84YwBDDA890eDiDCEqcbwvHlBvzzDkU2cGBBDDI1QWT12jTiXIOn2CIw5KK4i6N5Z2HUxwYjzriDyqaqqZg== + add-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" @@ -1928,20 +1852,10 @@ ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^7.0.2: - version "7.2.1" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-7.2.1.tgz#a5ac226171912447683524fa2f1248fcf8bac83d" - integrity sha512-+nu0HDv7kNSOua9apAVc979qd932rrZeb3WOvoiD31A/p1mIE5/9bN2027pE2rOPYEdS3UHzsvof4hY+lM9/WQ== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - ajv@^8.0.1: - version "8.0.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.0.2.tgz#1396e27f208ed56dd5638ab5a251edeb1c91d402" - integrity sha512-V0HGxJd0PiDF0ecHYIesTOqfd1gJguwQUOYfMfAWnRsWQEXfc5ifbUFhD3Wjc+O+y7VAqL+g07prq9gHQ/JOZQ== + version "8.3.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.3.0.tgz#25ee7348e32cdc4a1dbb38256bf6bdc451dd577c" + integrity sha512-RYE7B5An83d7eWnDR8kbdaIFqmKCNsP16ay1hDbJEU+sa0e3H9SebskCt0Uufem6cfAVu7Col6ubcn/W+Sm8/Q== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" @@ -1954,22 +1868,17 @@ ansi-colors@^4.1.1: integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== ansi-escapes@^4.2.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" - integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== dependencies: - type-fest "^0.11.0" + type-fest "^0.21.3" ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= - ansi-regex@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" @@ -2003,9 +1912,9 @@ anymatch@^2.0.0: normalize-path "^2.1.1" anymatch@^3.0.3: - version "3.1.1" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" - integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== + version "3.1.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== dependencies: normalize-path "^3.0.0" picomatch "^2.0.4" @@ -2244,9 +2153,9 @@ aws-sdk-mock@^5.1.0: traverse "^0.6.6" aws-sdk@^2.596.0, aws-sdk@^2.637.0, aws-sdk@^2.848.0: - version "2.866.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.866.0.tgz#8150fb2e0cfecd281968edee7cad84598d8d7a09" - integrity sha512-6Z581Ek2Yfm78NpeEFMNuSoyiYG7tipEaqfWNFR1AGyYheZwql4ajhzzlpWn91LBpdm7qcFldSNY9U0tKpKWNw== + version "2.903.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.903.0.tgz#4c8252723370ebbdaffe69f4dfddc5973b1dab4a" + integrity sha512-BP/giYLP8QJ63Jta59kph1F76oPITxRt/wNr3BdoEs9BtshWlGKk149UaseDB4wJtI+0TER5jtzBIUBcP6E+wA== dependencies: buffer "4.9.2" events "1.1.1" @@ -2337,9 +2246,9 @@ babel-preset-jest@^26.6.2: babel-preset-current-node-syntax "^1.0.0" balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== base64-js@^1.0.2, base64-js@^1.3.1: version "1.5.1" @@ -2367,14 +2276,14 @@ bcrypt-pbkdf@^1.0.0: tweetnacl "^0.14.3" before-after-hook@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.0.tgz#09c40d92e936c64777aa385c4e9b904f8147eaf0" - integrity sha512-jH6rKQIfroBbhEXVmI7XmXe3ix5S/PgJqpzdDPnR8JGLHWNYLsYZ6tK5iWOF/Ra3oqEX0NobXGlzbiylIzVphQ== + version "2.2.1" + resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.1.tgz#73540563558687586b52ed217dad6a802ab1549c" + integrity sha512-/6FKxSTWoJdbsLDF8tdIjaRiFXiE6UHsEHE3OPI/cwPURCVi1ukP0gmLn7XWEiFk5TcwQjjY5PWsU+j+tgXgmw== bind-obj-methods@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/bind-obj-methods/-/bind-obj-methods-2.0.1.tgz#1c1295d6741c07b78d15f42080fe4a60a27f91f5" - integrity sha512-kKzUyCuc+jsWH4C2nW5KB2nh+rQRbQcdphfo9UN3j1uwIFGZ3JB8njtRZOiUAQCkxazH0nDQPN6x/zhvFcbZIw== + version "2.0.2" + resolved "https://registry.yarnpkg.com/bind-obj-methods/-/bind-obj-methods-2.0.2.tgz#ea603b0f2455dce76d177c69747751b40c815897" + integrity sha512-bUkRdEOppT1Xg/jG0+bp0JSjUD9U0r7skxb/42WeBUjfBpW6COQTIgQmKX5J2Z3aMXcORKgN2N+d7IQwTK3pag== bl@^4.0.3: version "4.1.0" @@ -2427,15 +2336,15 @@ browser-process-hrtime@^1.0.0: integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== browserslist@^4.14.5: - version "4.16.3" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.3.tgz#340aa46940d7db878748567c5dea24a48ddf3717" - integrity sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw== + version "4.16.6" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2" + integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ== dependencies: - caniuse-lite "^1.0.30001181" - colorette "^1.2.1" - electron-to-chromium "^1.3.649" + caniuse-lite "^1.0.30001219" + colorette "^1.2.2" + electron-to-chromium "^1.3.723" escalade "^3.1.1" - node-releases "^1.1.70" + node-releases "^1.1.71" bs-logger@0.x: version "0.2.6" @@ -2499,9 +2408,9 @@ bytes@3.1.0: integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== cacache@^15.0.5: - version "15.0.5" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.5.tgz#69162833da29170d6732334643c60e005f5f17d0" - integrity sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A== + version "15.0.6" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.6.tgz#65a8c580fda15b59150fb76bf3f3a8e45d583099" + integrity sha512-g1WYDMct/jzW+JdWEyjaX2zoBkZ6ZT9VpOyp2I/VMtDsNLffNat3kqPFfi1eDRSK9/SuKGyORDHcQMcPF8sQ/w== dependencies: "@npmcli/move-file" "^1.0.1" chownr "^2.0.0" @@ -2517,7 +2426,7 @@ cacache@^15.0.5: p-map "^4.0.0" promise-inflight "^1.0.1" rimraf "^3.0.2" - ssri "^8.0.0" + ssri "^8.0.1" tar "^6.0.2" unique-filename "^1.1.1" @@ -2601,10 +2510,10 @@ camelcase@^6.0.0, camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== -caniuse-lite@^1.0.30001181: - version "1.0.30001202" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001202.tgz#4cb3bd5e8a808e8cd89e4e66c549989bc8137201" - integrity sha512-ZcijQNqrcF8JNLjzvEiXqX4JUYxoZa7Pvcsd9UD8Kz4TvhTonOSNRsK+qtvpVL4l6+T1Rh4LFtLfnNWg6BGWCQ== +caniuse-lite@^1.0.30001219: + version "1.0.30001228" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001228.tgz#bfdc5942cd3326fa51ee0b42fbef4da9d492a7fa" + integrity sha512-QQmLOGJ3DEgokHbMSA8cj2a+geXqmnpyOFT0lhQV6P3/YOJvGDEwoedcwxEQ30gJIwIIunHIicunJ2rzK5gB2A== capture-exit@^2.0.0: version "2.0.0" @@ -2654,9 +2563,9 @@ chalk@^2.0.0, chalk@^2.4.2: supports-color "^5.3.0" chalk@^4.0.0, chalk@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" - integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== + version "4.1.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" + integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== dependencies: ansi-styles "^4.1.0" supports-color "^7.1.0" @@ -2793,11 +2702,6 @@ co@^4.6.0: resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= - codemaker@^1.29.0: version "1.29.0" resolved "https://registry.yarnpkg.com/codemaker/-/codemaker-1.29.0.tgz#c262d1149681348103c7a79913a9a39ba13098f5" @@ -2849,7 +2753,7 @@ color-support@^1.1.0: resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== -colorette@^1.2.1: +colorette@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== @@ -2946,9 +2850,9 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= constructs@^3.3.69: - version "3.3.71" - resolved "https://registry.yarnpkg.com/constructs/-/constructs-3.3.71.tgz#5a3e968de484ad327bc2650aa4a7f37a39834ac5" - integrity sha512-3KFtTsA7OV27m/+pJhN4iJkKzHbPIPvyvEX5BQ/JCAWjfCHOQEVpIgxHLpT4i8L1OFta+pJrzcEVAHo6UitwqA== + version "3.3.75" + resolved "https://registry.yarnpkg.com/constructs/-/constructs-3.3.75.tgz#222516951fd6b8380cb6fea3c171eeca0bf980a4" + integrity sha512-q10foASSSfDWmS99OQLfnWDXCzqLvoORISAVWPFg0AmIGlBv2ZdDOtXxLqrJARPxVlOldmW2JzWzdRI+4+0/ZA== contains-path@^0.1.0: version "0.1.0" @@ -2993,7 +2897,7 @@ conventional-changelog-config-spec@2.1.0: resolved "https://registry.yarnpkg.com/conventional-changelog-config-spec/-/conventional-changelog-config-spec-2.1.0.tgz#874a635287ef8b581fd8558532bf655d4fb59f2d" integrity sha512-IpVePh16EbbB02V+UA+HQnnPIohgXvJRxHcS5+Uwk4AT5LjzCZJm5sp/yqs5C6KZJ1jMsV4paEV13BN1pvDuxQ== -conventional-changelog-conventionalcommits@4.5.0, conventional-changelog-conventionalcommits@^4.5.0: +conventional-changelog-conventionalcommits@4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.5.0.tgz#a02e0b06d11d342fdc0f00c91d78265ed0bc0a62" integrity sha512-buge9xDvjjOxJlyxUnar/+6i/aVEVGA7EEh4OafBCXPlLUQPGbRUBhBUveWRxzvR8TEjhKEP4BdepnpG2FSZXw== @@ -3002,6 +2906,15 @@ conventional-changelog-conventionalcommits@4.5.0, conventional-changelog-convent lodash "^4.17.15" q "^1.5.1" +conventional-changelog-conventionalcommits@^4.5.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.6.0.tgz#7fc17211dbca160acf24687bd2fdd5fd767750eb" + integrity sha512-sj9tj3z5cnHaSJCYObA9nISf7eq/YjscLPoq6nmew4SiOjxqL2KRpK20fjnjVbpNDjJ2HR3MoVcWKXwbVvzS0A== + dependencies: + compare-func "^2.0.0" + lodash "^4.17.15" + q "^1.5.1" + conventional-changelog-core@^4.2.1, conventional-changelog-core@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-4.2.2.tgz#f0897df6d53b5d63dec36b9442bd45354f8b3ce5" @@ -3246,7 +3159,7 @@ cssom@~0.3.6: resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== -cssstyle@^2.2.0: +cssstyle@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== @@ -3338,7 +3251,7 @@ decamelize@^5.0.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-5.0.0.tgz#88358157b010ef133febfd27c18994bd80c6215b" integrity sha512-U75DcT5hrio3KNtvdULAWnLiAPbFUC4191ldxMmj4FA/mRuBnmDwU0boNfPyFRhnan+Jm+haLeSn3P0afcBn4w== -decimal.js@^10.2.0: +decimal.js@^10.2.1: version "10.2.1" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.1.tgz#238ae7b0f0c793d3e3cea410108b35a2c01426a3" integrity sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw== @@ -3577,9 +3490,9 @@ dotenv-json@^1.0.0: integrity sha512-jAssr+6r4nKhKRudQ0HOzMskOFFi9+ubXWwmrSGJFgTvpjyPXCXsCsYbjif6mXp7uxA7xY3/LGaiTQukZzSbOQ== dotenv@^8.0.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" - integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== + version "8.6.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" + integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== dotgitignore@^2.1.0: version "2.1.0" @@ -3614,21 +3527,16 @@ ejs@^2.5.2: resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba" integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== -electron-to-chromium@^1.3.649: - version "1.3.691" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.691.tgz#a671eaf135a3ccec0915eb8d844a0952aba79f3b" - integrity sha512-ZqiO69KImmOGCyoH0icQPU3SndJiW93juEvf63gQngyhODO6SpQIPMTOHldtCs5DS5GMKvAkquk230E2zt2vpw== +electron-to-chromium@^1.3.723: + version "1.3.727" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.727.tgz#857e310ca00f0b75da4e1db6ff0e073cc4a91ddf" + integrity sha512-Mfz4FIB4FSvEwBpDfdipRIrwd6uo8gUDoRDF4QEYb4h4tSuI3ov594OrjU6on042UlFHouIJpClDODGkPcBSbg== emittery@^0.7.1: version "0.7.2" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.2.tgz#25595908e13af0f5674ab419396e2fb394cdfa82" integrity sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ== -emoji-regex@^7.0.1: - version "7.0.3" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" - integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== - emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -3671,9 +3579,9 @@ env-paths@^2.2.0: integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== envinfo@^7.7.4: - version "7.7.4" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.7.4.tgz#c6311cdd38a0e86808c1c9343f667e4267c4a320" - integrity sha512-TQXTYFVVwwluWSFis6K2XKxgrD22jEv0FTuLCQI+OjH7rn93+iY0fSSFM5lrSxFY+H1+B0/cvvlamr3UsBivdQ== + version "7.8.1" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" + integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== err-code@^2.0.2: version "2.0.3" @@ -3742,10 +3650,10 @@ es6-error@^4.0.1: resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== -esbuild@^0.11.18: - version "0.11.18" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.11.18.tgz#b587ec9e84d2e291b545e3c6342b5b703fd009cb" - integrity sha512-KD7v4N9b5B8bxPUNn/3GA9r0HWo4nJk3iwjZ+2zG1ffg+r8ig+wqj7sW6zgI6Sn4/B2FnbzqWxcAokAGGM5zwQ== +esbuild@^0.11.20: + version "0.11.20" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.11.20.tgz#7cefa1aee8b372c184e42457885f7ce5d3e62a1e" + integrity sha512-QOZrVpN/Yz74xfat0H6euSgn3RnwLevY1mJTEXneukz1ln9qB+ieaerRMzSeETpz/UJWsBMzRVR/andBht5WKw== escalade@^3.1.1: version "3.1.1" @@ -3767,7 +3675,7 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -escodegen@^1.14.1, escodegen@^1.8.1: +escodegen@^1.8.1: version "1.14.3" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== @@ -3779,6 +3687,18 @@ escodegen@^1.14.1, escodegen@^1.8.1: optionalDependencies: source-map "~0.6.1" +escodegen@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" + integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + eslint-config-standard@^14.1.1: version "14.1.1" resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-14.1.1.tgz#830a8e44e7aef7de67464979ad06b406026c56ea" @@ -3893,17 +3813,17 @@ eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== eslint-visitor-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" - integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" + integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== -eslint@^7.25.0: - version "7.25.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.25.0.tgz#1309e4404d94e676e3e831b3a3ad2b050031eb67" - integrity sha512-TVpSovpvCNpLURIScDRB6g5CYu/ZFq9GfX2hLNIV4dSBKxIWojeDODvYl3t0k0VtMxYeR8OXPCFE5+oHMlGfhw== +eslint@^7.26.0: + version "7.26.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.26.0.tgz#d416fdcdcb3236cd8f282065312813f8c13982f6" + integrity sha512-4R1ieRf52/izcZE7AlLy56uIHHDLT74Yzz2Iv2l6kDaYvEu9x+wMB5dZArVL8SYGXSYV2YAg70FcW5Y5nGGNIg== dependencies: "@babel/code-frame" "7.12.11" - "@eslint/eslintrc" "^0.4.0" + "@eslint/eslintrc" "^0.4.1" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" @@ -4004,9 +3924,9 @@ events@1.1.1: integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= exec-sh@^0.3.2: - version "0.3.4" - resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.4.tgz#3a018ceb526cc6f6df2bb504b2bfe8e3a4934ec5" - integrity sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A== + version "0.3.6" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" + integrity sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w== execa@^1.0.0: version "1.0.0" @@ -4335,9 +4255,9 @@ flatted@^3.1.0: integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA== follow-redirects@^1.10.0, follow-redirects@^1.11.0: - version "1.13.3" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267" - integrity sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA== + version "1.14.1" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.1.tgz#d9114ded0a1cfdd334e164e6662ad02bfd91ff43" + integrity sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg== for-in@^1.0.2: version "1.0.2" @@ -4562,9 +4482,9 @@ get-stream@^5.0.0: pump "^3.0.0" get-stream@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.0.tgz#3e0012cb6827319da2706e601a1583e8629a6718" - integrity sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg== + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== get-uri@3: version "3.0.2" @@ -4656,10 +4576,10 @@ glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@^5.1.1: dependencies: is-glob "^4.0.1" -glob@^7.0.0, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.6: - version "7.1.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== +glob@^7.0.0, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.1.7, glob@~7.1.6: + version "7.1.7" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" + integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -4681,16 +4601,16 @@ globals@^12.1.0: type-fest "^0.8.1" globals@^13.6.0: - version "13.6.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.6.0.tgz#d77138e53738567bb96a3916ff6f6b487af20ef7" - integrity sha512-YFKCX0SiPg7l5oKYCJ2zZGxcXprVXHcSnVuvzrT3oSENQonVLqM5pf9fN5dLGZGyCjhw8TN8Btwe/jKnZ0pjvQ== + version "13.8.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.8.0.tgz#3e20f504810ce87a8d72e55aecf8435b50f4c1b3" + integrity sha512-rHtdA6+PDBIjeEvA91rpqzEvk/k3/i7EeNQiryiWuJH0Hw9cpyJMAt2jtbAwUaRdhD+573X4vWw6IcjKPasi9Q== dependencies: type-fest "^0.20.2" globby@^11.0.1, globby@^11.0.2: - version "11.0.2" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.2.tgz#1af538b766a3b540ebfb58a32b2e2d5897321d83" - integrity sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og== + version "11.0.3" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.3.tgz#9b1f0cb523e171dd1ad8c7b2a9fb4b644b9593cb" + integrity sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg== dependencies: array-union "^2.1.0" dir-glob "^3.0.1" @@ -4739,7 +4659,7 @@ hard-rejection@^2.1.0: resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== -has-bigints@^1.0.0: +has-bigints@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== @@ -4754,7 +4674,7 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-symbols@^1.0.0, has-symbols@^1.0.1, has-symbols@^1.0.2: +has-symbols@^1.0.1, has-symbols@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== @@ -4823,21 +4743,14 @@ hasha@^5.0.0: integrity sha1-CH4fELBGky/IWU3Z5tN4r8nR5aw= hosted-git-info@^2.1.4: - version "2.8.8" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" - integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== - -hosted-git-info@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.0.0.tgz#9f06639a90beff66cacae6e77f8387b431d61ddc" - integrity sha512-fqhGdjk4av7mT9fU/B01dUtZ+WZSc/XEXMoLXDVZukiQRXxeHSSz3AqbeWRJHtF8EQYHlAgB1NSAHU0Cm7aqZA== - dependencies: - lru-cache "^6.0.0" + version "2.8.9" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== hosted-git-info@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.0.1.tgz#710ef5452ea429a844abc33c981056e7371edab7" - integrity sha512-eT7NrxAsppPRQEBSwKSosReE+v8OzABwEScQYk5d4uxaEPlzxTIku7LINXtBGalthkLhJnq5lBI89PfK43zAKg== + version "4.0.2" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.0.2.tgz#5e425507eede4fea846b7262f0838456c4209961" + integrity sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg== dependencies: lru-cache "^6.0.0" @@ -4937,9 +4850,9 @@ ieee754@^1.1.13, ieee754@^1.1.4: integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== ignore-walk@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37" - integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw== + version "3.0.4" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.4.tgz#c9a09f69b7c7b479a5d74ac1a3c0d4236d2a6335" + integrity sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ== dependencies: minimatch "^3.0.4" @@ -5015,16 +4928,16 @@ ini@^1.3.2, ini@^1.3.4, ini@~1.3.0: integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== init-package-json@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/init-package-json/-/init-package-json-2.0.2.tgz#d81a7e6775af9b618f20bba288e440b8d1ce05f3" - integrity sha512-PO64kVeArePvhX7Ff0jVWkpnE1DfGRvaWcStYrPugcJz9twQGYibagKJuIMHCX7ENcp0M6LJlcjLBuLD5KeJMg== + version "2.0.3" + resolved "https://registry.yarnpkg.com/init-package-json/-/init-package-json-2.0.3.tgz#c8ae4f2a4ad353bcbc089e5ffe98a8f1a314e8fd" + integrity sha512-tk/gAgbMMxR6fn1MgMaM1HpU1ryAmBWWitnxG5OhuNXeX0cbpbgV5jA4AIpQJVNoyOfOevTtO6WX+rPs+EFqaQ== dependencies: glob "^7.1.1" - npm-package-arg "^8.1.0" + npm-package-arg "^8.1.2" promzard "^0.3.0" read "~1.0.1" - read-package-json "^3.0.0" - semver "^7.3.2" + read-package-json "^3.0.1" + semver "^7.3.5" validate-npm-package-license "^3.0.4" validate-npm-package-name "^3.0.0" @@ -5052,11 +4965,6 @@ interpret@^1.0.0: resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== -ip-regex@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" - integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= - ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" @@ -5089,16 +4997,16 @@ is-arrayish@^0.2.1: integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= is-bigint@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.1.tgz#6923051dfcbc764278540b9ce0e6b3213aa5ebc2" - integrity sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg== + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.2.tgz#ffb381442503235ad245ea89e45b3dbff040ee5a" + integrity sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA== is-boolean-object@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.0.tgz#e2aaad3a3a8fca34c28f6eee135b156ed2587ff0" - integrity sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA== + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.1.tgz#3c0878f035cb821228d350d2e1e36719716a3de8" + integrity sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng== dependencies: - call-bind "^1.0.0" + call-bind "^1.0.2" is-buffer@^1.1.5, is-buffer@~1.1.6: version "1.1.6" @@ -5118,9 +5026,9 @@ is-ci@^2.0.0: ci-info "^2.0.0" is-core-module@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a" - integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ== + version "2.4.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1" + integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A== dependencies: has "^1.0.3" @@ -5139,9 +5047,9 @@ is-data-descriptor@^1.0.0: kind-of "^6.0.0" is-date-object@^1.0.1, is-date-object@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" - integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.4.tgz#550cfcc03afada05eea3dd30981c7b09551f73e5" + integrity sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A== is-descriptor@^0.1.0: version "0.1.6" @@ -5162,9 +5070,9 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: kind-of "^6.0.2" is-docker@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.1.1.tgz#4125a88e44e450d384e09047ede71adc2d144156" - integrity sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw== + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" @@ -5188,18 +5096,6 @@ is-finite@^1.0.0: resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= - is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" @@ -5233,9 +5129,9 @@ is-negative-zero@^2.0.1: integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== is-number-object@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197" - integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw== + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.5.tgz#6edfaeed7950cff19afedce9fbfca9ee6dd289eb" + integrity sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw== is-number@^3.0.0: version "3.0.0" @@ -5282,17 +5178,17 @@ is-plain-object@^5.0.0: integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== is-potential-custom-element-name@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz#0c52e54bcca391bb2c494b21e8626d7336c6e397" - integrity sha1-DFLlS8yjkbssSUsh6GJtczbG45c= + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== is-regex@^1.1.1, is-regex@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.2.tgz#81c8ebde4db142f2cf1c53fc86d6a45788266251" - integrity sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg== + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.3.tgz#d029f9aff6448b93ebbe3f33dac71511fdcbef9f" + integrity sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ== dependencies: call-bind "^1.0.2" - has-symbols "^1.0.1" + has-symbols "^1.0.2" is-set@^2.0.1, is-set@^2.0.2: version "2.0.2" @@ -5317,16 +5213,16 @@ is-stream@^2.0.0: integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== is-string@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" - integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== + version "1.0.6" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.6.tgz#3fe5d5992fb0d93404f32584d4b0179a71b54a5f" + integrity sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w== is-symbol@^1.0.2, is-symbol@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" - integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== dependencies: - has-symbols "^1.0.1" + has-symbols "^1.0.2" is-text-path@^1.0.1: version "1.0.1" @@ -5912,7 +5808,7 @@ jest-worker@^26.6.2: merge-stream "^2.0.0" supports-color "^7.0.0" -jest@^26.6.0, jest@^26.6.3: +jest@^26.6.3: version "26.6.3" resolved "https://registry.yarnpkg.com/jest/-/jest-26.6.3.tgz#40e8fdbe48f00dfa1f0ce8121ca74b88ac9148ef" integrity sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q== @@ -5945,9 +5841,9 @@ js-yaml@^3.13.1, js-yaml@^3.2.7: esprima "^4.0.0" js-yaml@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.0.0.tgz#f426bc0ff4b4051926cd588c71113183409a121f" - integrity sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q== + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== dependencies: argparse "^2.0.1" @@ -5957,35 +5853,35 @@ jsbn@~0.1.0: integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= jsdom@^16.4.0: - version "16.4.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.4.0.tgz#36005bde2d136f73eee1a830c6d45e55408edddb" - integrity sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w== + version "16.5.3" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.5.3.tgz#13a755b3950eb938b4482c407238ddf16f0d2136" + integrity sha512-Qj1H+PEvUsOtdPJ056ewXM4UJPCi4hhLA8wpiz9F2YvsRBhuFsXxtrIFAgGBDynQA9isAMGE91PfUYbdMPXuTA== dependencies: - abab "^2.0.3" - acorn "^7.1.1" + abab "^2.0.5" + acorn "^8.1.0" acorn-globals "^6.0.0" cssom "^0.4.4" - cssstyle "^2.2.0" + cssstyle "^2.3.0" data-urls "^2.0.0" - decimal.js "^10.2.0" + decimal.js "^10.2.1" domexception "^2.0.1" - escodegen "^1.14.1" + escodegen "^2.0.0" html-encoding-sniffer "^2.0.1" is-potential-custom-element-name "^1.0.0" nwsapi "^2.2.0" - parse5 "5.1.1" + parse5 "6.0.1" request "^2.88.2" - request-promise-native "^1.0.8" - saxes "^5.0.0" + request-promise-native "^1.0.9" + saxes "^5.0.1" symbol-tree "^3.2.4" - tough-cookie "^3.0.1" + tough-cookie "^4.0.0" w3c-hr-time "^1.0.2" w3c-xmlserializer "^2.0.0" webidl-conversions "^6.1.0" whatwg-encoding "^1.0.5" whatwg-mimetype "^2.3.0" - whatwg-url "^8.0.0" - ws "^7.2.3" + whatwg-url "^8.5.0" + ws "^7.4.4" xml-name-validator "^3.0.0" jsesc@^2.5.1: @@ -6186,9 +6082,9 @@ jszip@*, jszip@^3.6.0: set-immediate-shim "~1.0.1" just-extend@^4.0.2: - version "4.1.1" - resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.1.1.tgz#158f1fdb01f128c411dc8b286a7b4837b3545282" - integrity sha512-aWgeGFW67BP3e5181Ep1Fv2v8z//iBJfrvyTnq8wG86vEESwmonn1zPBJ0VfmT9CJq2FIT0VsETtrNFm2a+SHA== + version "4.2.1" + resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.2.1.tgz#ef5e589afb61e5d66b24eca749409a8939a8c744" + integrity sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg== kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" @@ -6302,25 +6198,25 @@ levn@~0.3.0: type-check "~0.3.2" libnpmaccess@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-4.0.1.tgz#17e842e03bef759854adf6eb6c2ede32e782639f" - integrity sha512-ZiAgvfUbvmkHoMTzdwmNWCrQRsDkOC+aM5BDfO0C9aOSwF3R1LdFDBD+Rer1KWtsoQYO35nXgmMR7OUHpDRxyA== + version "4.0.2" + resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-4.0.2.tgz#781832fb7ccb867b26343a75a85ad9c43e50406e" + integrity sha512-avXtJibZuGap0/qADDYqb9zdpgzVu/yG5+tl2sTRa7MCkDNv2ZlGwCYI0r6/+tmqXPj0iB9fKexHz426vB326w== dependencies: aproba "^2.0.0" minipass "^3.1.1" - npm-package-arg "^8.0.0" - npm-registry-fetch "^9.0.0" + npm-package-arg "^8.1.2" + npm-registry-fetch "^10.0.0" libnpmpublish@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/libnpmpublish/-/libnpmpublish-4.0.0.tgz#ad6413914e0dfd78df868ce14ba3d3a4cc8b385b" - integrity sha512-2RwYXRfZAB1x/9udKpZmqEzSqNd7ouBRU52jyG14/xG8EF+O9A62d7/XVR3iABEQHf1iYhkm0Oq9iXjrL3tsXA== + version "4.0.1" + resolved "https://registry.yarnpkg.com/libnpmpublish/-/libnpmpublish-4.0.1.tgz#08ca2cbb5d7f6be1ce4f3f9c49b3822682bcf166" + integrity sha512-hZCrZ8v4G9YH3DxpIyBdob25ijD5v5LNzRbwsej4pPDopjdcLLj1Widl+BUeFa7D0ble1JYL4F3owjLJqiA8yA== dependencies: - normalize-package-data "^3.0.0" - npm-package-arg "^8.1.0" - npm-registry-fetch "^9.0.0" + normalize-package-data "^3.0.2" + npm-package-arg "^8.1.2" + npm-registry-fetch "^10.0.0" semver "^7.1.3" - ssri "^8.0.0" + ssri "^8.0.1" lie@~3.3.0: version "3.3.0" @@ -6467,11 +6363,6 @@ lodash.set@^4.3.2: resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" integrity sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM= -lodash.sortby@^4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" - integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= - lodash.template@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" @@ -6497,7 +6388,7 @@ lodash.union@^4.6.0: resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg= -lodash@4.x, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21: +lodash@4.x, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -6615,9 +6506,9 @@ map-obj@^1.0.0, map-obj@^1.0.1: integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= map-obj@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.2.0.tgz#0e8bc823e2aaca8a0942567d12ed14f389eec153" - integrity sha512-NAq0fCmZYGz9UFEQyndp7sisrow4GroyGeKluyKC/chuITZsPyOyC1UJZPJlVFImhXdROIP5xqouRLThT3BbpQ== + version "4.2.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.2.1.tgz#e4ea399dbc979ae735c83c863dd31bdf364277b7" + integrity sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ== map-visit@^1.0.0: version "1.0.0" @@ -6758,24 +6649,24 @@ micromatch@^3.1.4: to-regex "^3.0.2" micromatch@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" - integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== + version "4.0.4" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" + integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== dependencies: braces "^3.0.1" - picomatch "^2.0.5" + picomatch "^2.2.3" -mime-db@1.46.0: - version "1.46.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.46.0.tgz#6267748a7f799594de3cbc8cde91def349661cee" - integrity sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ== +mime-db@1.47.0: + version "1.47.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.47.0.tgz#8cb313e59965d3c05cfbf898915a267af46a335c" + integrity sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw== mime-types@^2.1.12, mime-types@~2.1.19: - version "2.1.29" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.29.tgz#1d4ab77da64b91f5f72489df29236563754bb1b2" - integrity sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ== + version "2.1.30" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.30.tgz#6e7be8b4c479825f85ed6326695db73f9305d62d" + integrity sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg== dependencies: - mime-db "1.46.0" + mime-db "1.47.0" mimic-fn@^2.1.0: version "2.1.0" @@ -6998,9 +6889,9 @@ nested-error-stacks@^2.0.0: integrity sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug== netmask@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.1.tgz#5a5cbdcbb7b6de650870e15e83d3e9553a414cf4" - integrity sha512-gB8eG6ubxz67c7O2gaGiyWdRUIbH61q7anjgueDqCC9kvIs/b4CTtCMaQKeJbv1/Y7FT19I4zKwYmjnjInRQsg== + version "2.0.2" + resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7" + integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg== nice-try@^1.0.4: version "1.0.5" @@ -7095,7 +6986,7 @@ node-preload@^0.2.1: dependencies: process-on-spawn "^1.0.0" -node-releases@^1.1.70: +node-releases@^1.1.71: version "1.1.71" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb" integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg== @@ -7133,14 +7024,14 @@ normalize-package-data@^2.0.0, normalize-package-data@^2.3.0, normalize-package- semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" -normalize-package-data@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.1.tgz#98dc56dfe6755d99b1c53f046e1e3d2dde55a1c7" - integrity sha512-D/ttLdxo71msR4FF3VgSwK4blHfE3/vGByz1NCeE7/Dh8reQOKNJJjk5L10mLq9jxa+ZHzT1/HLgxljzbXE7Fw== +normalize-package-data@^3.0.0, normalize-package-data@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.2.tgz#cae5c410ae2434f9a6c1baa65d5bc3b9366c8699" + integrity sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg== dependencies: - hosted-git-info "^4.0.0" - resolve "^1.17.0" - semver "^7.3.2" + hosted-git-info "^4.0.1" + resolve "^1.20.0" + semver "^7.3.4" validate-npm-package-license "^3.0.1" normalize-path@^2.1.1: @@ -7160,14 +7051,7 @@ normalize-url@^3.3.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== -npm-bundled@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b" - integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA== - dependencies: - npm-normalize-package-bin "^1.0.1" - -npm-bundled@^1.1.2: +npm-bundled@^1.1.1, npm-bundled@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1" integrity sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ== @@ -7210,16 +7094,16 @@ npm-package-arg@^8.0.0, npm-package-arg@^8.0.1, npm-package-arg@^8.1.0, npm-pack validate-npm-package-name "^3.0.0" npm-packlist@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-2.1.4.tgz#40e96b2b43787d0546a574542d01e066640d09da" - integrity sha512-Qzg2pvXC9U4I4fLnUrBmcIT4x0woLtUgxUi9eC+Zrcv1Xx5eamytGAfbDWQ67j7xOcQ2VW1I3su9smVTIdu7Hw== + version "2.2.2" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-2.2.2.tgz#076b97293fa620f632833186a7a8f65aaa6148c8" + integrity sha512-Jt01acDvJRhJGthnUJVF/w6gumWOZxO7IkpY/lsX9//zqQgnF7OJaxgQXcerd4uQOLu7W5bkb4mChL9mdfm+Zg== dependencies: glob "^7.1.6" ignore-walk "^3.0.3" npm-bundled "^1.1.1" npm-normalize-package-bin "^1.0.1" -npm-pick-manifest@^6.0.0: +npm-pick-manifest@^6.0.0, npm-pick-manifest@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz#7b5484ca2c908565f43b7f27644f36bb816f5148" integrity sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA== @@ -7229,6 +7113,19 @@ npm-pick-manifest@^6.0.0: npm-package-arg "^8.1.2" semver "^7.3.4" +npm-registry-fetch@^10.0.0: + version "10.1.1" + resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-10.1.1.tgz#97bc7a0fca5e8f76cc5162185b8de8caa8bea639" + integrity sha512-F6a3l+ffCQ7hvvN16YG5bpm1rPZntCg66PLHDQ1apWJPOCUVHoKnL2w5fqEaTVhp42dmossTyXeR7hTGirfXrg== + dependencies: + lru-cache "^6.0.0" + make-fetch-happen "^8.0.9" + minipass "^3.1.3" + minipass-fetch "^1.3.0" + minipass-json-stream "^1.0.1" + minizlib "^2.0.0" + npm-package-arg "^8.0.0" + npm-registry-fetch@^9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-9.0.0.tgz#86f3feb4ce00313bc0b8f1f8f69daae6face1661" @@ -7272,11 +7169,6 @@ null-check@^1.0.0: resolved "https://registry.yarnpkg.com/null-check/-/null-check-1.0.0.tgz#977dffd7176012b9ec30d2a39db5cf72a0439edd" integrity sha1-l33/1xdgErnsMNKjnbXPcqBDnt0= -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= - nwsapi@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" @@ -7366,9 +7258,9 @@ object-copy@^0.1.0: kind-of "^3.0.3" object-inspect@^1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" - integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw== + version "1.10.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.3.tgz#c2aa7d2d09f50c99375704f7a0adf24c5782d369" + integrity sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw== object-is@^1.1.4: version "1.1.5" @@ -7677,9 +7569,9 @@ package-hash@^4.0.0: release-zalgo "^1.0.0" pacote@^11.2.6: - version "11.3.0" - resolved "https://registry.yarnpkg.com/pacote/-/pacote-11.3.0.tgz#b2e16791a39cd4d9fb9fc1ec240cefe7ea518e6f" - integrity sha512-cygprcGpEVqvDzpuPMkGVXW/ooc2ibpoosuJ4YHcUXozDs9VJP7Vha+41pYppG2MVNis4t1BB8IygIBh7vVr2Q== + version "11.3.3" + resolved "https://registry.yarnpkg.com/pacote/-/pacote-11.3.3.tgz#d7d6091464f77c09691699df2ded13ab906b3e68" + integrity sha512-GQxBX+UcVZrrJRYMK2HoG+gPeSUX/rQhnbPkkGrCYa4n2F/bgClFPaMm0nsdnYrxnmUy85uMHoFXZ0jTD0drew== dependencies: "@npmcli/git" "^2.0.1" "@npmcli/installed-package-contents" "^1.0.6" @@ -7694,7 +7586,7 @@ pacote@^11.2.6: npm-package-arg "^8.0.1" npm-packlist "^2.1.4" npm-pick-manifest "^6.0.0" - npm-registry-fetch "^9.0.0" + npm-registry-fetch "^10.0.0" promise-retry "^2.0.1" read-package-json-fast "^2.0.1" rimraf "^3.0.2" @@ -7763,10 +7655,10 @@ parse-url@^5.0.0: parse-path "^4.0.0" protocols "^1.4.0" -parse5@5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" - integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== +parse5@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== pascalcase@^0.1.1: version "0.1.1" @@ -7869,10 +7761,10 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= -picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1: - version "2.2.2" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" - integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.3.tgz#465547f359ccc206d3c48e46a1bcb89bf7ee619d" + integrity sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg== pify@^2.0.0, pify@^2.3.0: version "2.3.0" @@ -8002,9 +7894,9 @@ promptly@^3.2.0: read "^1.0.4" prompts@^2.0.1: - version "2.4.0" - resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.0.tgz#4aa5de0723a231d1ee9121c40fdf663df73f61d7" - integrity sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ== + version "2.4.1" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.1.tgz#befd3b1195ba052f9fd2fde8a486c4e82ee77f61" + integrity sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ== dependencies: kleur "^3.0.3" sisteransi "^1.0.5" @@ -8064,7 +7956,7 @@ pseudomap@^1.0.2: resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= -psl@^1.1.28: +psl@^1.1.28, psl@^1.1.33: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== @@ -8098,9 +7990,11 @@ q@^1.5.1: integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= qs@^6.9.4: - version "6.9.6" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.6.tgz#26ed3c8243a431b2924aca84cc90471f35d5a0ee" - integrity sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ== + version "6.10.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.1.tgz#4931482fa8d647a5aab799c5271d2133b981fb6a" + integrity sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg== + dependencies: + side-channel "^1.0.4" qs@~6.5.2: version "6.5.2" @@ -8123,9 +8017,9 @@ querystring@0.2.0: integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= queue-microtask@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.2.tgz#abf64491e6ecf0f38a6502403d4cda04f372dfd3" - integrity sha512-dB15eXv3p2jDlbOiNLyMabYg1/sXvppd8DP2J3EOCQ0AkuSXCW2tP7mnVouVLJKgUMY6yP0kcQDVpLCN13h4Xg== + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== quick-lru@^4.0.1: version "4.0.1" @@ -8153,9 +8047,9 @@ rc@~1.2.8: strip-json-comments "~2.0.1" react-is@^17.0.1: - version "17.0.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339" - integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA== + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== read-cmd-shim@^2.0.0: version "2.0.0" @@ -8180,7 +8074,7 @@ read-package-json@^2.0.0: normalize-package-data "^2.0.0" npm-normalize-package-bin "^1.0.0" -read-package-json@^3.0.0: +read-package-json@^3.0.0, read-package-json@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-3.0.1.tgz#c7108f0b9390257b08c21e3004d2404c806744b9" integrity sha512-aLcPqxovhJTVJcsnROuuzQvv6oziQx4zd3JvG0vGCL5MjTONUc4uJ90zCBC6R7W7oUKBNoR/F8pkyfVwlbxqng== @@ -8390,9 +8284,9 @@ remove-trailing-separator@^1.0.1: integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= repeat-element@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" - integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== + version "1.1.4" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" + integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== repeat-string@^1.6.1: version "1.6.1" @@ -8413,7 +8307,7 @@ request-promise-core@1.1.4: dependencies: lodash "^4.17.19" -request-promise-native@^1.0.8: +request-promise-native@^1.0.9: version "1.0.9" resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28" integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g== @@ -8485,7 +8379,7 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.11.1, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.18.1: +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.11.1, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.18.1, resolve@^1.20.0: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -8553,9 +8447,9 @@ run-parallel@^1.1.9: queue-microtask "^1.2.2" rxjs@^6.6.0: - version "6.6.6" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.6.tgz#14d8417aa5a07c5e633995b525e1e3c0dec03b70" - integrity sha512-/oTwee4N4iWzAMAL9xdGKjkEHmIwupR3oXbQjCKywF1BeFohswF3vZdogbmEF6pZkOsXTzWkrZszrWpQTByYVg== + version "6.6.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== dependencies: tslib "^1.9.0" @@ -8606,7 +8500,7 @@ sax@>=0.6.0, sax@^1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -saxes@^5.0.0: +saxes@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== @@ -8625,10 +8519,10 @@ semver-intersect@^1.4.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@7.x, semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4: - version "7.3.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" - integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== +semver@7.x, semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== dependencies: lru-cache "^6.0.0" @@ -8637,13 +8531,6 @@ semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.3.5: - version "7.3.5" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" - integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== - dependencies: - lru-cache "^6.0.0" - set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -8714,7 +8601,7 @@ shellwords@^0.1.1: resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== -side-channel@^1.0.3: +side-channel@^1.0.3, side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== @@ -8814,9 +8701,9 @@ socks-proxy-agent@5, socks-proxy-agent@^5.0.0: socks "^2.3.3" socks@^2.3.3: - version "2.6.0" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.6.0.tgz#6b984928461d39871b3666754b9000ecf39dfac2" - integrity sha512-mNmr9owlinMplev0Wd7UHFlqI4ofnBnNzFuzrm63PPaHgbkqCFe4T5LzwKmtQ/f2tX0NTpcdVLyD/FHxFBstYw== + version "2.6.1" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.6.1.tgz#989e6534a07cf337deb1b1c94aaa44296520d30e" + integrity sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA== dependencies: ip "^1.1.5" smart-buffer "^4.1.0" @@ -8992,9 +8879,9 @@ ssri@^8.0.0, ssri@^8.0.1: minipass "^3.1.1" stack-utils@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.4.tgz#4b600971dcfc6aed0cbdf2a8268177cc916c87c8" - integrity sha512-IPDJfugEGbfizBwBZRZ3xpccMdRyP5lqsBWXGQWimVjua/ccLCeMOAVjlc1R7LxFjo5sEDhyNIXd8mo/AiDS9w== + version "1.0.5" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.5.tgz#a19b0b01947e0029c8e451d5d61a498f5bb1471b" + integrity sha512-KZiTzuV3CnSnSvgMRrARVCj+Ht7rMbauGDK0LdVFRGyenwdylpajAp4Q0i6SX8rEmbTpMMf6ryq2gb8pPq2WgQ== dependencies: escape-string-regexp "^2.0.0" @@ -9066,7 +8953,7 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -string-width@*, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2: +string-width@*, string-width@^1.0.1, "string-width@^1.0.2 || 2", string-width@^3.0.0, string-width@^3.1.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== @@ -9075,32 +8962,6 @@ string-width@*, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -"string-width@^1.0.2 || 2": - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - -string-width@^3.0.0, string-width@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" - integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== - dependencies: - emoji-regex "^7.0.1" - is-fullwidth-code-point "^2.0.0" - strip-ansi "^5.1.0" - string.prototype.repeat@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/string.prototype.repeat/-/string.prototype.repeat-0.2.0.tgz#aba36de08dcee6a5a337d49b2ea1da1b28fc0ecf" @@ -9153,14 +9014,7 @@ strip-ansi@^3.0.0, strip-ansi@^3.0.1: dependencies: ansi-regex "^2.0.0" -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= - dependencies: - ansi-regex "^3.0.0" - -strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: +strip-ansi@^5.0.0, strip-ansi@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== @@ -9256,9 +9110,9 @@ supports-color@^7.0.0, supports-color@^7.1.0: has-flag "^4.0.0" supports-hyperlinks@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz#f663df252af5f37c5d49bbd7eeefa9e0b9e59e47" - integrity sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA== + version "2.2.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" + integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== dependencies: has-flag "^4.0.0" supports-color "^7.0.0" @@ -9268,24 +9122,13 @@ symbol-tree@^3.2.4: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== -table@^6.0.4: - version "6.0.7" - resolved "https://registry.yarnpkg.com/table/-/table-6.0.7.tgz#e45897ffbcc1bcf9e8a87bf420f2c9e5a7a52a34" - integrity sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g== - dependencies: - ajv "^7.0.2" - lodash "^4.17.20" - slice-ansi "^4.0.0" - string-width "^4.2.0" - -table@^6.6.0: - version "6.6.0" - resolved "https://registry.yarnpkg.com/table/-/table-6.6.0.tgz#905654b79df98d9e9a973de1dd58682532c40e8e" - integrity sha512-iZMtp5tUvcnAdtHpZTWLPF0M7AgiQsURR2DwmxnJwSy8I3+cY+ozzVvYha3BOLG2TB+L0CqjIz+91htuj6yCXg== +table@*, table@^6.0.4, table@^6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/table/-/table-6.7.0.tgz#26274751f0ee099c547f6cb91d3eff0d61d155b2" + integrity sha512-SAM+5p6V99gYiiy2gT5ArdzgM1dLDed0nkrWmG6Fry/bUS/m9x83BwpJUOf1Qj/x2qJd+thL6IkIx7qPGRxqBw== dependencies: ajv "^8.0.1" lodash.clonedeep "^4.5.0" - lodash.flatten "^4.4.0" lodash.truncate "^4.4.2" slice-ansi "^4.0.0" string-width "^4.2.0" @@ -9564,14 +9407,14 @@ tough-cookie@^2.3.3, tough-cookie@~2.5.0: psl "^1.1.28" punycode "^2.1.1" -tough-cookie@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.1.tgz#9df4f57e739c26930a018184887f4adb7dca73b2" - integrity sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg== +tough-cookie@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" + integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== dependencies: - ip-regex "^2.1.0" - psl "^1.1.28" + psl "^1.1.33" punycode "^2.1.1" + universalify "^0.1.2" tr46@^2.0.2: version "2.0.2" @@ -9605,10 +9448,10 @@ trivial-deferred@^1.0.1: resolved "https://registry.yarnpkg.com/trivial-deferred/-/trivial-deferred-1.0.1.tgz#376d4d29d951d6368a6f7a0ae85c2f4d5e0658f3" integrity sha1-N21NKdlR1jaKb3oK6FwvTV4GWPM= -ts-jest@^26.5.5: - version "26.5.5" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.5.5.tgz#e40481b6ee4dd162626ba481a2be05fa57160ea5" - integrity sha512-7tP4m+silwt1NHqzNRAPjW1BswnAhopTdc2K3HEkRZjF0ZG2F/e/ypVH0xiZIMfItFtD3CX0XFbwPzp9fIEUVg== +ts-jest@^26.5.6: + version "26.5.6" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.5.6.tgz#c32e0746425274e1dfe333f43cd3c800e014ec35" + integrity sha512-rua+rCP8DxpA8b4DQD/6X2HQS8Zy/xzViVYfEs2OQu68tkCuKLV0Md8pmX55+W24uRIyAsf/BajRfxOs+R2MKA== dependencies: bs-logger "0.x" buffer-from "1.x" @@ -9670,9 +9513,9 @@ tslib@^1.8.1, tslib@^1.9.0: integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslib@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" - integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== + version "2.2.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c" + integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w== tsutils@^3.17.1: version "3.21.0" @@ -9712,11 +9555,6 @@ type-detect@4.0.8, type-detect@^4.0.8: resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== -type-fest@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" - integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== - type-fest@^0.18.0: version "0.18.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" @@ -9727,6 +9565,11 @@ type-fest@^0.20.2: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + type-fest@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.4.1.tgz#8bdf77743385d8a4f13ba95f610f5ccd68c728f8" @@ -9773,9 +9616,9 @@ typescript@^3.3.3, typescript@~3.9.9: integrity sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w== typescript@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.3.tgz#39062d8019912d43726298f09493d598048c1ce3" - integrity sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw== + version "4.2.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961" + integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg== typescript@~3.8.3: version "3.8.3" @@ -9788,9 +9631,9 @@ uc.micro@^1.0.1, uc.micro@^1.0.5: integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== uglify-js@^3.1.4: - version "3.13.1" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.13.1.tgz#2749d4b8b5b7d67460b4a418023ff73c3fefa60a" - integrity sha512-EWhx3fHy3M9JbaeTnO+rEqzCe1wtyQClv6q3YWq0voOj4E+bMZBErVS1GAHPDiRGONYq34M1/d8KuQMgvi6Gjw== + version "3.13.5" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.13.5.tgz#5d71d6dbba64cf441f32929b1efce7365bb4f113" + integrity sha512-xtB8yEqIkn7zmOyS2zUNBsYCBRhDkvlNxMMY2smuJ/qA8NCHeQvKCF3i9Z4k8FJH4+PJvZRtMrPynfZ75+CSZw== uid-number@0.0.6: version "0.0.6" @@ -9803,14 +9646,14 @@ umask@^1.1.0: integrity sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0= unbox-primitive@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.0.tgz#eeacbc4affa28e9b3d36b5eaeccc50b3251b1d3f" - integrity sha512-P/51NX+JXyxK/aigg1/ZgyccdAxm5K1+n8+tvqSntjOivPt19gvm1VC49RWYetsiub8WViUchdxl/KWHHB0kzA== + version "1.0.1" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" + integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== dependencies: function-bind "^1.1.1" - has-bigints "^1.0.0" - has-symbols "^1.0.0" - which-boxed-primitive "^1.0.1" + has-bigints "^1.0.1" + has-symbols "^1.0.2" + which-boxed-primitive "^1.0.2" unicode-length@^2.0.2: version "2.0.2" @@ -9849,7 +9692,7 @@ universal-user-agent@^6.0.0: resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== -universalify@^0.1.0: +universalify@^0.1.0, universalify@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== @@ -9940,9 +9783,9 @@ v8-compile-cache@^2.0.3: integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== v8-to-istanbul@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-7.1.0.tgz#5b95cef45c0f83217ec79f8fc7ee1c8b486aee07" - integrity sha512-uXUVqNUCLa0AH1vuVxzi+MI4RfxEOKt9pBgKwHbgH7st8Kv2P1m+jvWNnektzBh5QShF3ODgKmUFCf38LnVz1g== + version "7.1.2" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz#30898d1a7fa0c84d225a2c1434fb958f290883c1" + integrity sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow== dependencies: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^1.6.0" @@ -10027,16 +9870,16 @@ whatwg-mimetype@^2.3.0: resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== -whatwg-url@^8.0.0, whatwg-url@^8.4.0: - version "8.4.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.4.0.tgz#50fb9615b05469591d2b2bd6dfaed2942ed72837" - integrity sha512-vwTUFf6V4zhcPkWp/4CQPr1TW9Ml6SF4lVyaIMBdJw5i6qUUJ1QWM4Z6YYVkfka0OUIzVo/0aNtGVGk256IKWw== +whatwg-url@^8.0.0, whatwg-url@^8.4.0, whatwg-url@^8.5.0: + version "8.5.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.5.0.tgz#7752b8464fc0903fec89aa9846fc9efe07351fd3" + integrity sha512-fy+R77xWv0AiqfLl4nuGUlQ3/6b5uNfQ4WAbGQVMYshCTCCPK9psC1nWh3XHuxGVCtlcDDQPQW1csmmIQo+fwg== dependencies: - lodash.sortby "^4.7.0" + lodash "^4.7.0" tr46 "^2.0.2" webidl-conversions "^6.1.0" -which-boxed-primitive@^1.0.1: +which-boxed-primitive@^1.0.1, which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== @@ -10190,10 +10033,10 @@ write-pkg@^4.0.0: type-fest "^0.4.1" write-json-file "^3.2.0" -ws@^7.2.3: - version "7.4.4" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.4.tgz#383bc9742cb202292c9077ceab6f6047b17f2d59" - integrity sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw== +ws@^7.4.4: + version "7.4.5" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.5.tgz#a484dd851e9beb6fdb420027e3885e8ce48986c1" + integrity sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g== xml-js@^1.6.11: version "1.6.11" @@ -10251,14 +10094,14 @@ xtend@~4.0.1: integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== y18n@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4" - integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ== + version "4.0.3" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== y18n@^5.0.5: - version "5.0.5" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18" - integrity sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg== + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== yallist@^2.1.2: version "2.1.2" From e4cc012661fcfbda8e941f2348a1f9b2690facf7 Mon Sep 17 00:00:00 2001 From: Ben Chaimberg Date: Mon, 10 May 2021 13:03:05 -0700 Subject: [PATCH 007/134] chore(mergify): add @BenChaimberg to team roster --- .mergify.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mergify.yml b/.mergify.yml index 358575303ca74..2a4c1bb2163fc 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -6,7 +6,7 @@ pull_request_rules: label: add: [ contribution/core ] conditions: - - author~=^(eladb|RomainMuller|garnaat|nija-at|skinny85|rix0rrr|NGL321|Jerry-AWS|MrArnoldPalmer|NetaNir|iliapolo|njlynch|ericzbeard|ccfife|fulghum|pkandasamy91|SoManyHs|uttarasridhar)$ + - author~=^(eladb|RomainMuller|garnaat|nija-at|skinny85|rix0rrr|NGL321|Jerry-AWS|MrArnoldPalmer|NetaNir|iliapolo|njlynch|ericzbeard|ccfife|fulghum|pkandasamy91|SoManyHs|uttarasridhar|BenChaimberg)$ - -label~="contribution/core" - name: automatic merge actions: From 7571860d1c0e69188937c3f1527d5c18959a98b9 Mon Sep 17 00:00:00 2001 From: AWS CDK Team Date: Mon, 10 May 2021 20:18:59 +0000 Subject: [PATCH 008/134] chore(release): 1.103.0 --- CHANGELOG.md | 38 ++++++++++++++++++++++++++++++++++++++ version.v1.json | 2 +- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8487aaf34742f..63aa4c3bf3be9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,44 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [1.103.0](https://github.com/aws/aws-cdk/compare/v1.102.0...v1.103.0) (2021-05-10) + + +### ⚠ BREAKING CHANGES TO EXPERIMENTAL FEATURES + +* **appmesh:** HealthChecks require use of static factory methods +* **apigatewayv2:** The `metricXXX` methods are no longer available in the +`IApi` interface. The existing ones are moved into `IHttpApi` and new +ones will be added to `IWebsocketApi`. +* **apigatewayv2:** The `metricXXX` methods are no longer available in +the `IStage` interface. The existing ones are moved into `IHttpStage` +and new ones will be added to the `IWebsocketStage`. +* **lambda-nodejs:** the default runtime version for `NodejsFunction` is now always `NODEJS_14_X` (previously the version was derived from the local NodeJS runtime and could be either 12.x or 14.x). + +### Features + +* **appmesh:** change HealthChecks to use protocol-specific union-like classes ([#14432](https://github.com/aws/aws-cdk/issues/14432)) ([063ddc7](https://github.com/aws/aws-cdk/commit/063ddc7315954a2104ac7aa4cb98f96239b8dd1e)) +* **aws-ecs:** Expose logdriver "mode" property ([#13965](https://github.com/aws/aws-cdk/issues/13965)) ([28fce22](https://github.com/aws/aws-cdk/commit/28fce2264448820495d921ed08ae0d3084442876)), closes [#13845](https://github.com/aws/aws-cdk/issues/13845) +* **cloudwatch:** validate parameters for a metric dimensions (closes [#3116](https://github.com/aws/aws-cdk/issues/3116)) ([#14365](https://github.com/aws/aws-cdk/issues/14365)) ([4a24d61](https://github.com/aws/aws-cdk/commit/4a24d61654ef77557350e35443ddab7597d61736)) +* **docdb:** Support multiple security groups to DatabaseCluster ([#13290](https://github.com/aws/aws-cdk/issues/13290)) ([1a97b66](https://github.com/aws/aws-cdk/commit/1a97b6664f9124ec21a6db39be600cee0411ab8c)) +* **elbv2:** preserveClientIp for NetworkTargetGroup ([#14589](https://github.com/aws/aws-cdk/issues/14589)) ([d676ffc](https://github.com/aws/aws-cdk/commit/d676ffccb28d530a18d0e1630df0940632122a27)) +* **kinesis:** Basic stream level metrics ([#12556](https://github.com/aws/aws-cdk/issues/12556)) ([5f1b576](https://github.com/aws/aws-cdk/commit/5f1b57603330e707bc68f56c267a9e45faa29e55)), closes [#12555](https://github.com/aws/aws-cdk/issues/12555) +* **kms:** allow specifying key spec and key usage ([#14478](https://github.com/aws/aws-cdk/issues/14478)) ([10ae1a9](https://github.com/aws/aws-cdk/commit/10ae1a902383e69d15a17585268dd836ffb4087b)), closes [#5639](https://github.com/aws/aws-cdk/issues/5639) +* **lambda-go:** higher level construct for golang lambdas ([#11842](https://github.com/aws/aws-cdk/issues/11842)) ([0948cc7](https://github.com/aws/aws-cdk/commit/0948cc7d4e38ac4e9ae765fcc571ea4f49ca9095)) +* **msk:** Cluster L2 Construct ([#9908](https://github.com/aws/aws-cdk/issues/9908)) ([ce119ba](https://github.com/aws/aws-cdk/commit/ce119ba20d42191fa7ae2e83d459406be16e1748)) + + +### Bug Fixes + +* **apigatewayv2:** incorrect metric names for client and server-side errors ([#14541](https://github.com/aws/aws-cdk/issues/14541)) ([551182e](https://github.com/aws/aws-cdk/commit/551182efb1313425c97088b66c17d6227cb69da6)), closes [#14503](https://github.com/aws/aws-cdk/issues/14503) +* `assert` matches more than the template on multiple CDK copies ([#14544](https://github.com/aws/aws-cdk/issues/14544)) ([f8abdbf](https://github.com/aws/aws-cdk/commit/f8abdbfb37ba9efd9e24414f5b64d90f4cf3f7cb)), closes [#14468](https://github.com/aws/aws-cdk/issues/14468) +* **apigatewayv2-integrations:** fix broken lambda websocket integration uri ([#13820](https://github.com/aws/aws-cdk/issues/13820)) ([f0d5c25](https://github.com/aws/aws-cdk/commit/f0d5c25e1ae026eef03dc396e48368521dcb8331)), closes [#13679](https://github.com/aws/aws-cdk/issues/13679) +* **cfn-include:** correctly parse Fn::Sub expressions containing serialized JSON ([#14512](https://github.com/aws/aws-cdk/issues/14512)) ([fd6d6d0](https://github.com/aws/aws-cdk/commit/fd6d6d0a563816ace616dfe48b3a03f4559636f7)), closes [#14095](https://github.com/aws/aws-cdk/issues/14095) +* **cli:** 'cdk deploy *' should not deploy stacks in nested assemblies ([#14542](https://github.com/aws/aws-cdk/issues/14542)) ([93a3549](https://github.com/aws/aws-cdk/commit/93a3549e7a9791b5074dc95909f3289970800c10)) +* **cli:** synth fails if there was an error when synthesizing the stack ([#14613](https://github.com/aws/aws-cdk/issues/14613)) ([71c61e8](https://github.com/aws/aws-cdk/commit/71c61e81ca58c95979f66d7d7b8100777d3c7b99)) +* **lambda-nodejs:** non-deterministic runtime version ([#14538](https://github.com/aws/aws-cdk/issues/14538)) ([527f662](https://github.com/aws/aws-cdk/commit/527f6622146f007035ca669c33ad73861afe608a)), closes [#13893](https://github.com/aws/aws-cdk/issues/13893) +* **ssm:** dynamic SSM parameter reference breaks with lists ([#14527](https://github.com/aws/aws-cdk/issues/14527)) ([3d1baac](https://github.com/aws/aws-cdk/commit/3d1baaca015443d7ee0eecdec9e81dd61e8920ad)), closes [#14205](https://github.com/aws/aws-cdk/issues/14205) [#14476](https://github.com/aws/aws-cdk/issues/14476) + ## [1.102.0](https://github.com/aws/aws-cdk/compare/v1.101.0...v1.102.0) (2021-05-04) diff --git a/version.v1.json b/version.v1.json index 3a232490b680f..60bdf592f8796 100644 --- a/version.v1.json +++ b/version.v1.json @@ -1,3 +1,3 @@ { - "version": "1.102.0" + "version": "1.103.0" } From 461f458ce2d939f14189fb7350f66a55c0e5789e Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Tue, 11 May 2021 10:14:02 +0200 Subject: [PATCH 009/134] chore: mark "otaviomacedo" as core contributor (#14619) Co-authored-by: Otavio Macedo --- .mergify.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mergify.yml b/.mergify.yml index 2a4c1bb2163fc..55bba382d2784 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -6,7 +6,7 @@ pull_request_rules: label: add: [ contribution/core ] conditions: - - author~=^(eladb|RomainMuller|garnaat|nija-at|skinny85|rix0rrr|NGL321|Jerry-AWS|MrArnoldPalmer|NetaNir|iliapolo|njlynch|ericzbeard|ccfife|fulghum|pkandasamy91|SoManyHs|uttarasridhar|BenChaimberg)$ + - author~=^(eladb|RomainMuller|garnaat|nija-at|skinny85|rix0rrr|NGL321|Jerry-AWS|MrArnoldPalmer|NetaNir|iliapolo|njlynch|ericzbeard|ccfife|fulghum|pkandasamy91|SoManyHs|uttarasridhar|otaviomacedo|BenChaimberg)$ - -label~="contribution/core" - name: automatic merge actions: From 5858a3669a3edddf80332dc0d195c580b449a64c Mon Sep 17 00:00:00 2001 From: Jerry Kindall <52084730+Jerry-AWS@users.noreply.github.com> Date: Tue, 11 May 2021 01:42:22 -0700 Subject: [PATCH 010/134] chore(cli): add npm command to upgrade notice (#14621) A colleague had to go look up the command to update the CDK CLI. It occurred to me that is probably common with developers who don't work with NPM on a daily basis, such as anyone who isn't developing in TypeScript or JavaScript. Put the necessary command right in the upgrade notice. ---- *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/version.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aws-cdk/lib/version.ts b/packages/aws-cdk/lib/version.ts index 19bec86549c2e..e407935ce2bfa 100644 --- a/packages/aws-cdk/lib/version.ts +++ b/packages/aws-cdk/lib/version.ts @@ -108,7 +108,7 @@ export async function displayVersionMessage(): Promise { if (laterVersion) { const bannerMsg = formatAsBanner([ `Newer version of CDK is available [${colors.green(laterVersion as string)}]`, - 'Upgrade recommended', + 'Upgrade recommended (npm install -g aws-cdk)', ]); bannerMsg.forEach((e) => print(e)); } From 10a633c8cda9f21b85c82f911d88641f3a362c4d Mon Sep 17 00:00:00 2001 From: Daniel Neilson <53624638+ddneilson@users.noreply.github.com> Date: Tue, 11 May 2021 04:09:19 -0500 Subject: [PATCH 011/134] fix(lambda): custom resource fails to connect to efs filesystem (#14431) Fixes: https://github.com/aws/aws-cdk/issues/14430 ---- *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/lib/function.ts | 20 ++++++ .../@aws-cdk/aws-lambda/test/function.test.ts | 64 +++++++++++++++++++ .../integ.lambda.filesystem.expected.json | 4 +- 3 files changed, 87 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/function.ts b/packages/@aws-cdk/aws-lambda/lib/function.ts index 4a0fc2d916ec0..0565ba2a98b1e 100644 --- a/packages/@aws-cdk/aws-lambda/lib/function.ts +++ b/packages/@aws-cdk/aws-lambda/lib/function.ts @@ -701,10 +701,30 @@ export class Function extends FunctionBase { this.currentVersionOptions = props.currentVersionOptions; if (props.filesystem) { + if (!props.vpc) { + throw new Error('Cannot configure \'filesystem\' without configuring a VPC.'); + } const config = props.filesystem.config; if (config.dependency) { this.node.addDependency(...config.dependency); } + // There could be a race if the Lambda is used in a CustomResource. It is possible for the Lambda to + // fail to attach to a given FileSystem if we do not have a dependency on the SecurityGroup ingress/egress + // rules that were created between this Lambda's SG & the Filesystem SG. + this.connections.securityGroups.forEach(sg => { + sg.node.findAll().forEach(child => { + if (child instanceof CfnResource && child.cfnResourceType === 'AWS::EC2::SecurityGroupEgress') { + resource.node.addDependency(child); + } + }); + }); + config.connections?.securityGroups.forEach(sg => { + sg.node.findAll().forEach(child => { + if (child instanceof CfnResource && child.cfnResourceType === 'AWS::EC2::SecurityGroupIngress') { + resource.node.addDependency(child); + } + }); + }); } } diff --git a/packages/@aws-cdk/aws-lambda/test/function.test.ts b/packages/@aws-cdk/aws-lambda/test/function.test.ts index e4ac71c25c8d1..26a27ca76aad2 100644 --- a/packages/@aws-cdk/aws-lambda/test/function.test.ts +++ b/packages/@aws-cdk/aws-lambda/test/function.test.ts @@ -1841,6 +1841,7 @@ describe('function', () => { const accessPoint = fs.addAccessPoint('AccessPoint'); // WHEN new lambda.Function(stack, 'MyFunction', { + vpc, handler: 'foo', runtime: lambda.Runtime.NODEJS_12_X, code: lambda.Code.fromAsset(path.join(__dirname, 'handler.zip')), @@ -1879,6 +1880,69 @@ describe('function', () => { ], }); }); + + test('throw error mounting efs with no vpc', () => { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'Vpc', { + maxAzs: 3, + natGateways: 1, + }); + + const fs = new efs.FileSystem(stack, 'Efs', { + vpc, + }); + const accessPoint = fs.addAccessPoint('AccessPoint'); + + // THEN + expect(() => { + new lambda.Function(stack, 'MyFunction', { + handler: 'foo', + runtime: lambda.Runtime.NODEJS_12_X, + code: lambda.Code.fromAsset(path.join(__dirname, 'handler.zip')), + filesystem: lambda.FileSystem.fromEfsAccessPoint(accessPoint, '/mnt/msg'), + }); + }).toThrow(); + }); + + test('verify deps when mounting efs', () => { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'Vpc', { + maxAzs: 3, + natGateways: 1, + }); + const securityGroup = new ec2.SecurityGroup(stack, 'LambdaSG', { + vpc, + allowAllOutbound: false, + }); + + const fs = new efs.FileSystem(stack, 'Efs', { + vpc, + }); + const accessPoint = fs.addAccessPoint('AccessPoint'); + // WHEN + new lambda.Function(stack, 'MyFunction', { + vpc, + handler: 'foo', + securityGroups: [securityGroup], + runtime: lambda.Runtime.NODEJS_12_X, + code: lambda.Code.fromAsset(path.join(__dirname, 'handler.zip')), + filesystem: lambda.FileSystem.fromEfsAccessPoint(accessPoint, '/mnt/msg'), + }); + + // THEN + expect(stack).toHaveResource('AWS::Lambda::Function', { + DependsOn: [ + 'EfsEfsMountTarget195B2DD2E', + 'EfsEfsMountTarget2315C927F', + 'EfsEfsSecurityGroupfromLambdaSG20491B2F751D', + 'LambdaSGtoEfsEfsSecurityGroupFCE2954020499719694A', + 'MyFunctionServiceRoleDefaultPolicyB705ABD4', + 'MyFunctionServiceRole3C357FF2', + ], + }, ResourcePart.CompleteDefinition); + }); }); describe('code config', () => { diff --git a/packages/@aws-cdk/aws-lambda/test/integ.lambda.filesystem.expected.json b/packages/@aws-cdk/aws-lambda/test/integ.lambda.filesystem.expected.json index 6d9334ab8999f..abb79b5123377 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.lambda.filesystem.expected.json +++ b/packages/@aws-cdk/aws-lambda/test/integ.lambda.filesystem.expected.json @@ -801,6 +801,7 @@ "EfsEfsMountTarget195B2DD2E", "EfsEfsMountTarget2315C927F", "EfsEfsMountTarget36646B9A0", + "EfsEfsSecurityGroupfromawscdklambda1MyLambdaSecurityGroup86B085EE20490D9864A8", "MyLambdaServiceRoleDefaultPolicy5BBC6F68", "MyLambdaServiceRole4539ECB6" ] @@ -1048,7 +1049,8 @@ "MyLambdaServiceRoleDefaultPolicy5BBC6F68", "MyLambdaServiceRole4539ECB6", "MyLambda2ServiceRoleDefaultPolicy2BECE79D", - "MyLambda2ServiceRoleD09B370C" + "MyLambda2ServiceRoleD09B370C", + "securityGroupfromawscdklambda1MyLambda2SecurityGroup7492F70D20498301D9D2" ] } } From 799ce1a7d5fb261cae92d514b4f7e315d8f0e589 Mon Sep 17 00:00:00 2001 From: AWS CDK Automation <43080478+aws-cdk-automation@users.noreply.github.com> Date: Tue, 11 May 2021 15:18:41 +0100 Subject: [PATCH 012/134] feat(cfnspec): cloudformation spec v35.2.0 (#14610) * feat: cloudformation spec v35.2.0 * add spec patches Co-authored-by: AWS CDK Team Co-authored-by: Elad Ben-Israel Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- packages/@aws-cdk/aws-finspace/.eslintrc.js | 3 + packages/@aws-cdk/aws-finspace/.gitignore | 19 + packages/@aws-cdk/aws-finspace/.npmignore | 28 + packages/@aws-cdk/aws-finspace/LICENSE | 201 ++ packages/@aws-cdk/aws-finspace/NOTICE | 2 + packages/@aws-cdk/aws-finspace/README.md | 20 + packages/@aws-cdk/aws-finspace/jest.config.js | 2 + packages/@aws-cdk/aws-finspace/lib/index.ts | 2 + packages/@aws-cdk/aws-finspace/package.json | 101 + .../aws-finspace/test/finspace.test.ts | 6 + .../@aws-cdk/aws-frauddetector/.eslintrc.js | 3 + .../@aws-cdk/aws-frauddetector/.gitignore | 19 + .../@aws-cdk/aws-frauddetector/.npmignore | 28 + packages/@aws-cdk/aws-frauddetector/LICENSE | 201 ++ packages/@aws-cdk/aws-frauddetector/NOTICE | 2 + packages/@aws-cdk/aws-frauddetector/README.md | 20 + .../@aws-cdk/aws-frauddetector/jest.config.js | 2 + .../@aws-cdk/aws-frauddetector/lib/index.ts | 2 + .../@aws-cdk/aws-frauddetector/package.json | 101 + .../test/frauddetector.test.ts | 6 + packages/@aws-cdk/aws-xray/.eslintrc.js | 3 + packages/@aws-cdk/aws-xray/.gitignore | 19 + packages/@aws-cdk/aws-xray/.npmignore | 28 + packages/@aws-cdk/aws-xray/LICENSE | 201 ++ packages/@aws-cdk/aws-xray/NOTICE | 2 + packages/@aws-cdk/aws-xray/README.md | 20 + packages/@aws-cdk/aws-xray/jest.config.js | 2 + packages/@aws-cdk/aws-xray/lib/index.ts | 2 + packages/@aws-cdk/aws-xray/package.json | 101 + packages/@aws-cdk/aws-xray/test/xray.test.ts | 6 + packages/@aws-cdk/cfnspec/CHANGELOG.md | 142 ++ packages/@aws-cdk/cfnspec/cfn.version | 2 +- ...0_CloudFormationResourceSpecification.json | 1726 ++++++++++++++++- .../900_CloudFront_OriginShield_patch.json | 16 + .../900_XRay_Tags_ItemType_patch.json | 27 + .../cloudformation-include/package.json | 6 + packages/aws-cdk-lib/package.json | 5 +- packages/decdk/package.json | 5 +- packages/monocdk/package.json | 5 +- 39 files changed, 3047 insertions(+), 39 deletions(-) create mode 100644 packages/@aws-cdk/aws-finspace/.eslintrc.js create mode 100644 packages/@aws-cdk/aws-finspace/.gitignore create mode 100644 packages/@aws-cdk/aws-finspace/.npmignore create mode 100644 packages/@aws-cdk/aws-finspace/LICENSE create mode 100644 packages/@aws-cdk/aws-finspace/NOTICE create mode 100644 packages/@aws-cdk/aws-finspace/README.md create mode 100644 packages/@aws-cdk/aws-finspace/jest.config.js create mode 100644 packages/@aws-cdk/aws-finspace/lib/index.ts create mode 100644 packages/@aws-cdk/aws-finspace/package.json create mode 100644 packages/@aws-cdk/aws-finspace/test/finspace.test.ts create mode 100644 packages/@aws-cdk/aws-frauddetector/.eslintrc.js create mode 100644 packages/@aws-cdk/aws-frauddetector/.gitignore create mode 100644 packages/@aws-cdk/aws-frauddetector/.npmignore create mode 100644 packages/@aws-cdk/aws-frauddetector/LICENSE create mode 100644 packages/@aws-cdk/aws-frauddetector/NOTICE create mode 100644 packages/@aws-cdk/aws-frauddetector/README.md create mode 100644 packages/@aws-cdk/aws-frauddetector/jest.config.js create mode 100644 packages/@aws-cdk/aws-frauddetector/lib/index.ts create mode 100644 packages/@aws-cdk/aws-frauddetector/package.json create mode 100644 packages/@aws-cdk/aws-frauddetector/test/frauddetector.test.ts create mode 100644 packages/@aws-cdk/aws-xray/.eslintrc.js create mode 100644 packages/@aws-cdk/aws-xray/.gitignore create mode 100644 packages/@aws-cdk/aws-xray/.npmignore create mode 100644 packages/@aws-cdk/aws-xray/LICENSE create mode 100644 packages/@aws-cdk/aws-xray/NOTICE create mode 100644 packages/@aws-cdk/aws-xray/README.md create mode 100644 packages/@aws-cdk/aws-xray/jest.config.js create mode 100644 packages/@aws-cdk/aws-xray/lib/index.ts create mode 100644 packages/@aws-cdk/aws-xray/package.json create mode 100644 packages/@aws-cdk/aws-xray/test/xray.test.ts create mode 100644 packages/@aws-cdk/cfnspec/spec-source/900_CloudFront_OriginShield_patch.json create mode 100644 packages/@aws-cdk/cfnspec/spec-source/900_XRay_Tags_ItemType_patch.json diff --git a/packages/@aws-cdk/aws-finspace/.eslintrc.js b/packages/@aws-cdk/aws-finspace/.eslintrc.js new file mode 100644 index 0000000000000..61dd8dd001f63 --- /dev/null +++ b/packages/@aws-cdk/aws-finspace/.eslintrc.js @@ -0,0 +1,3 @@ +const baseConfig = require('cdk-build-tools/config/eslintrc'); +baseConfig.parserOptions.project = __dirname + '/tsconfig.json'; +module.exports = baseConfig; diff --git a/packages/@aws-cdk/aws-finspace/.gitignore b/packages/@aws-cdk/aws-finspace/.gitignore new file mode 100644 index 0000000000000..62ebc95d75ce6 --- /dev/null +++ b/packages/@aws-cdk/aws-finspace/.gitignore @@ -0,0 +1,19 @@ +*.js +*.js.map +*.d.ts +tsconfig.json +node_modules +*.generated.ts +dist +.jsii + +.LAST_BUILD +.nyc_output +coverage +.nycrc +.LAST_PACKAGE +*.snk +nyc.config.js +!.eslintrc.js +!jest.config.js +junit.xml diff --git a/packages/@aws-cdk/aws-finspace/.npmignore b/packages/@aws-cdk/aws-finspace/.npmignore new file mode 100644 index 0000000000000..e4486030fcb17 --- /dev/null +++ b/packages/@aws-cdk/aws-finspace/.npmignore @@ -0,0 +1,28 @@ +# Don't include original .ts files when doing `npm pack` +*.ts +!*.d.ts +coverage +.nyc_output +*.tgz + +dist +.LAST_PACKAGE +.LAST_BUILD +!*.js + +# Include .jsii +!.jsii + +*.snk + +*.tsbuildinfo + +tsconfig.json + +.eslintrc.js +jest.config.js + +# exclude cdk artifacts +**/cdk.out +junit.xml +test/ diff --git a/packages/@aws-cdk/aws-finspace/LICENSE b/packages/@aws-cdk/aws-finspace/LICENSE new file mode 100644 index 0000000000000..28e4bdcec77ec --- /dev/null +++ b/packages/@aws-cdk/aws-finspace/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/@aws-cdk/aws-finspace/NOTICE b/packages/@aws-cdk/aws-finspace/NOTICE new file mode 100644 index 0000000000000..5fc3826926b5b --- /dev/null +++ b/packages/@aws-cdk/aws-finspace/NOTICE @@ -0,0 +1,2 @@ +AWS Cloud Development Kit (AWS CDK) +Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/@aws-cdk/aws-finspace/README.md b/packages/@aws-cdk/aws-finspace/README.md new file mode 100644 index 0000000000000..686a32758ad3e --- /dev/null +++ b/packages/@aws-cdk/aws-finspace/README.md @@ -0,0 +1,20 @@ +# AWS::FinSpace Construct Library + + +--- + +![cfn-resources: Stable](https://img.shields.io/badge/cfn--resources-stable-success.svg?style=for-the-badge) + +> All classes with the `Cfn` prefix in this module ([CFN Resources]) are always stable and safe to use. +> +> [CFN Resources]: https://docs.aws.amazon.com/cdk/latest/guide/constructs.html#constructs_lib + +--- + + + +This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project. + +```ts +import finspace = require('@aws-cdk/aws-finspace'); +``` diff --git a/packages/@aws-cdk/aws-finspace/jest.config.js b/packages/@aws-cdk/aws-finspace/jest.config.js new file mode 100644 index 0000000000000..54e28beb9798b --- /dev/null +++ b/packages/@aws-cdk/aws-finspace/jest.config.js @@ -0,0 +1,2 @@ +const baseConfig = require('cdk-build-tools/config/jest.config'); +module.exports = baseConfig; diff --git a/packages/@aws-cdk/aws-finspace/lib/index.ts b/packages/@aws-cdk/aws-finspace/lib/index.ts new file mode 100644 index 0000000000000..c3d3bf207c329 --- /dev/null +++ b/packages/@aws-cdk/aws-finspace/lib/index.ts @@ -0,0 +1,2 @@ +// AWS::FinSpace CloudFormation Resources: +export * from './finspace.generated'; diff --git a/packages/@aws-cdk/aws-finspace/package.json b/packages/@aws-cdk/aws-finspace/package.json new file mode 100644 index 0000000000000..5cca013076363 --- /dev/null +++ b/packages/@aws-cdk/aws-finspace/package.json @@ -0,0 +1,101 @@ +{ + "name": "@aws-cdk/aws-finspace", + "version": "0.0.0", + "description": "The CDK Construct Library for AWS::FinSpace", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "jsii": { + "outdir": "dist", + "projectReferences": true, + "targets": { + "dotnet": { + "namespace": "Amazon.CDK.AWS.FinSpace", + "packageId": "Amazon.CDK.AWS.FinSpace", + "signAssembly": true, + "assemblyOriginatorKeyFile": "../../key.snk", + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/master/logo/default-256-dark.png" + }, + "java": { + "package": "software.amazon.awscdk.services.finspace", + "maven": { + "groupId": "software.amazon.awscdk", + "artifactId": "finspace" + } + }, + "python": { + "classifiers": [ + "Framework :: AWS CDK", + "Framework :: AWS CDK :: 1" + ], + "distName": "aws-cdk.aws-finspace", + "module": "aws_cdk.aws_finspace" + } + } + }, + "repository": { + "type": "git", + "url": "https://github.com/aws/aws-cdk.git", + "directory": "packages/@aws-cdk/aws-finspace" + }, + "homepage": "https://github.com/aws/aws-cdk", + "scripts": { + "build": "cdk-build", + "watch": "cdk-watch", + "lint": "cdk-lint", + "test": "cdk-test", + "integ": "cdk-integ", + "pkglint": "pkglint -f", + "package": "cdk-package", + "awslint": "cdk-awslint", + "cfn2ts": "cfn2ts", + "build+test": "yarn build && yarn test", + "build+test+package": "yarn build+test && yarn package", + "compat": "cdk-compat", + "gen": "cfn2ts", + "rosetta:extract": "yarn --silent jsii-rosetta extract" + }, + "cdk-build": { + "cloudformation": "AWS::FinSpace", + "jest": true, + "env": { + "AWSLINT_BASE_CONSTRUCT": "true" + } + }, + "keywords": [ + "aws", + "cdk", + "constructs", + "AWS::FinSpace", + "aws-finspace" + ], + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com", + "organization": true + }, + "license": "Apache-2.0", + "devDependencies": { + "@types/jest": "^26.0.22", + "@aws-cdk/assert-internal": "0.0.0", + "cdk-build-tools": "0.0.0", + "cfn2ts": "0.0.0", + "pkglint": "0.0.0" + }, + "dependencies": { + "@aws-cdk/core": "0.0.0" + }, + "peerDependencies": { + "@aws-cdk/core": "0.0.0" + }, + "engines": { + "node": ">= 10.13.0 <13 || >=13.7.0" + }, + "stability": "experimental", + "maturity": "cfn-only", + "awscdkio": { + "announce": false + }, + "publishConfig": { + "tag": "latest" + } +} diff --git a/packages/@aws-cdk/aws-finspace/test/finspace.test.ts b/packages/@aws-cdk/aws-finspace/test/finspace.test.ts new file mode 100644 index 0000000000000..c4505ad966984 --- /dev/null +++ b/packages/@aws-cdk/aws-finspace/test/finspace.test.ts @@ -0,0 +1,6 @@ +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-frauddetector/.eslintrc.js b/packages/@aws-cdk/aws-frauddetector/.eslintrc.js new file mode 100644 index 0000000000000..61dd8dd001f63 --- /dev/null +++ b/packages/@aws-cdk/aws-frauddetector/.eslintrc.js @@ -0,0 +1,3 @@ +const baseConfig = require('cdk-build-tools/config/eslintrc'); +baseConfig.parserOptions.project = __dirname + '/tsconfig.json'; +module.exports = baseConfig; diff --git a/packages/@aws-cdk/aws-frauddetector/.gitignore b/packages/@aws-cdk/aws-frauddetector/.gitignore new file mode 100644 index 0000000000000..62ebc95d75ce6 --- /dev/null +++ b/packages/@aws-cdk/aws-frauddetector/.gitignore @@ -0,0 +1,19 @@ +*.js +*.js.map +*.d.ts +tsconfig.json +node_modules +*.generated.ts +dist +.jsii + +.LAST_BUILD +.nyc_output +coverage +.nycrc +.LAST_PACKAGE +*.snk +nyc.config.js +!.eslintrc.js +!jest.config.js +junit.xml diff --git a/packages/@aws-cdk/aws-frauddetector/.npmignore b/packages/@aws-cdk/aws-frauddetector/.npmignore new file mode 100644 index 0000000000000..e4486030fcb17 --- /dev/null +++ b/packages/@aws-cdk/aws-frauddetector/.npmignore @@ -0,0 +1,28 @@ +# Don't include original .ts files when doing `npm pack` +*.ts +!*.d.ts +coverage +.nyc_output +*.tgz + +dist +.LAST_PACKAGE +.LAST_BUILD +!*.js + +# Include .jsii +!.jsii + +*.snk + +*.tsbuildinfo + +tsconfig.json + +.eslintrc.js +jest.config.js + +# exclude cdk artifacts +**/cdk.out +junit.xml +test/ diff --git a/packages/@aws-cdk/aws-frauddetector/LICENSE b/packages/@aws-cdk/aws-frauddetector/LICENSE new file mode 100644 index 0000000000000..28e4bdcec77ec --- /dev/null +++ b/packages/@aws-cdk/aws-frauddetector/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/@aws-cdk/aws-frauddetector/NOTICE b/packages/@aws-cdk/aws-frauddetector/NOTICE new file mode 100644 index 0000000000000..5fc3826926b5b --- /dev/null +++ b/packages/@aws-cdk/aws-frauddetector/NOTICE @@ -0,0 +1,2 @@ +AWS Cloud Development Kit (AWS CDK) +Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/@aws-cdk/aws-frauddetector/README.md b/packages/@aws-cdk/aws-frauddetector/README.md new file mode 100644 index 0000000000000..2805d63c38fb7 --- /dev/null +++ b/packages/@aws-cdk/aws-frauddetector/README.md @@ -0,0 +1,20 @@ +# AWS::FraudDetector Construct Library + + +--- + +![cfn-resources: Stable](https://img.shields.io/badge/cfn--resources-stable-success.svg?style=for-the-badge) + +> All classes with the `Cfn` prefix in this module ([CFN Resources]) are always stable and safe to use. +> +> [CFN Resources]: https://docs.aws.amazon.com/cdk/latest/guide/constructs.html#constructs_lib + +--- + + + +This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project. + +```ts +import frauddetector = require('@aws-cdk/aws-frauddetector'); +``` diff --git a/packages/@aws-cdk/aws-frauddetector/jest.config.js b/packages/@aws-cdk/aws-frauddetector/jest.config.js new file mode 100644 index 0000000000000..54e28beb9798b --- /dev/null +++ b/packages/@aws-cdk/aws-frauddetector/jest.config.js @@ -0,0 +1,2 @@ +const baseConfig = require('cdk-build-tools/config/jest.config'); +module.exports = baseConfig; diff --git a/packages/@aws-cdk/aws-frauddetector/lib/index.ts b/packages/@aws-cdk/aws-frauddetector/lib/index.ts new file mode 100644 index 0000000000000..ed9953ee506e8 --- /dev/null +++ b/packages/@aws-cdk/aws-frauddetector/lib/index.ts @@ -0,0 +1,2 @@ +// AWS::FraudDetector CloudFormation Resources: +export * from './frauddetector.generated'; diff --git a/packages/@aws-cdk/aws-frauddetector/package.json b/packages/@aws-cdk/aws-frauddetector/package.json new file mode 100644 index 0000000000000..56c7e1128fd2c --- /dev/null +++ b/packages/@aws-cdk/aws-frauddetector/package.json @@ -0,0 +1,101 @@ +{ + "name": "@aws-cdk/aws-frauddetector", + "version": "0.0.0", + "description": "The CDK Construct Library for AWS::FraudDetector", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "jsii": { + "outdir": "dist", + "projectReferences": true, + "targets": { + "dotnet": { + "namespace": "Amazon.CDK.AWS.FraudDetector", + "packageId": "Amazon.CDK.AWS.FraudDetector", + "signAssembly": true, + "assemblyOriginatorKeyFile": "../../key.snk", + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/master/logo/default-256-dark.png" + }, + "java": { + "package": "software.amazon.awscdk.services.frauddetector", + "maven": { + "groupId": "software.amazon.awscdk", + "artifactId": "frauddetector" + } + }, + "python": { + "classifiers": [ + "Framework :: AWS CDK", + "Framework :: AWS CDK :: 1" + ], + "distName": "aws-cdk.aws-frauddetector", + "module": "aws_cdk.aws_frauddetector" + } + } + }, + "repository": { + "type": "git", + "url": "https://github.com/aws/aws-cdk.git", + "directory": "packages/@aws-cdk/aws-frauddetector" + }, + "homepage": "https://github.com/aws/aws-cdk", + "scripts": { + "build": "cdk-build", + "watch": "cdk-watch", + "lint": "cdk-lint", + "test": "cdk-test", + "integ": "cdk-integ", + "pkglint": "pkglint -f", + "package": "cdk-package", + "awslint": "cdk-awslint", + "cfn2ts": "cfn2ts", + "build+test": "yarn build && yarn test", + "build+test+package": "yarn build+test && yarn package", + "compat": "cdk-compat", + "gen": "cfn2ts", + "rosetta:extract": "yarn --silent jsii-rosetta extract" + }, + "cdk-build": { + "cloudformation": "AWS::FraudDetector", + "jest": true, + "env": { + "AWSLINT_BASE_CONSTRUCT": "true" + } + }, + "keywords": [ + "aws", + "cdk", + "constructs", + "AWS::FraudDetector", + "aws-frauddetector" + ], + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com", + "organization": true + }, + "license": "Apache-2.0", + "devDependencies": { + "@types/jest": "^26.0.22", + "@aws-cdk/assert-internal": "0.0.0", + "cdk-build-tools": "0.0.0", + "cfn2ts": "0.0.0", + "pkglint": "0.0.0" + }, + "dependencies": { + "@aws-cdk/core": "0.0.0" + }, + "peerDependencies": { + "@aws-cdk/core": "0.0.0" + }, + "engines": { + "node": ">= 10.13.0 <13 || >=13.7.0" + }, + "stability": "experimental", + "maturity": "cfn-only", + "awscdkio": { + "announce": false + }, + "publishConfig": { + "tag": "latest" + } +} diff --git a/packages/@aws-cdk/aws-frauddetector/test/frauddetector.test.ts b/packages/@aws-cdk/aws-frauddetector/test/frauddetector.test.ts new file mode 100644 index 0000000000000..c4505ad966984 --- /dev/null +++ b/packages/@aws-cdk/aws-frauddetector/test/frauddetector.test.ts @@ -0,0 +1,6 @@ +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-xray/.eslintrc.js b/packages/@aws-cdk/aws-xray/.eslintrc.js new file mode 100644 index 0000000000000..61dd8dd001f63 --- /dev/null +++ b/packages/@aws-cdk/aws-xray/.eslintrc.js @@ -0,0 +1,3 @@ +const baseConfig = require('cdk-build-tools/config/eslintrc'); +baseConfig.parserOptions.project = __dirname + '/tsconfig.json'; +module.exports = baseConfig; diff --git a/packages/@aws-cdk/aws-xray/.gitignore b/packages/@aws-cdk/aws-xray/.gitignore new file mode 100644 index 0000000000000..62ebc95d75ce6 --- /dev/null +++ b/packages/@aws-cdk/aws-xray/.gitignore @@ -0,0 +1,19 @@ +*.js +*.js.map +*.d.ts +tsconfig.json +node_modules +*.generated.ts +dist +.jsii + +.LAST_BUILD +.nyc_output +coverage +.nycrc +.LAST_PACKAGE +*.snk +nyc.config.js +!.eslintrc.js +!jest.config.js +junit.xml diff --git a/packages/@aws-cdk/aws-xray/.npmignore b/packages/@aws-cdk/aws-xray/.npmignore new file mode 100644 index 0000000000000..e4486030fcb17 --- /dev/null +++ b/packages/@aws-cdk/aws-xray/.npmignore @@ -0,0 +1,28 @@ +# Don't include original .ts files when doing `npm pack` +*.ts +!*.d.ts +coverage +.nyc_output +*.tgz + +dist +.LAST_PACKAGE +.LAST_BUILD +!*.js + +# Include .jsii +!.jsii + +*.snk + +*.tsbuildinfo + +tsconfig.json + +.eslintrc.js +jest.config.js + +# exclude cdk artifacts +**/cdk.out +junit.xml +test/ diff --git a/packages/@aws-cdk/aws-xray/LICENSE b/packages/@aws-cdk/aws-xray/LICENSE new file mode 100644 index 0000000000000..28e4bdcec77ec --- /dev/null +++ b/packages/@aws-cdk/aws-xray/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/@aws-cdk/aws-xray/NOTICE b/packages/@aws-cdk/aws-xray/NOTICE new file mode 100644 index 0000000000000..5fc3826926b5b --- /dev/null +++ b/packages/@aws-cdk/aws-xray/NOTICE @@ -0,0 +1,2 @@ +AWS Cloud Development Kit (AWS CDK) +Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/@aws-cdk/aws-xray/README.md b/packages/@aws-cdk/aws-xray/README.md new file mode 100644 index 0000000000000..77a0090e7f659 --- /dev/null +++ b/packages/@aws-cdk/aws-xray/README.md @@ -0,0 +1,20 @@ +# AWS::XRay Construct Library + + +--- + +![cfn-resources: Stable](https://img.shields.io/badge/cfn--resources-stable-success.svg?style=for-the-badge) + +> All classes with the `Cfn` prefix in this module ([CFN Resources]) are always stable and safe to use. +> +> [CFN Resources]: https://docs.aws.amazon.com/cdk/latest/guide/constructs.html#constructs_lib + +--- + + + +This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project. + +```ts +import xray = require('@aws-cdk/aws-xray'); +``` diff --git a/packages/@aws-cdk/aws-xray/jest.config.js b/packages/@aws-cdk/aws-xray/jest.config.js new file mode 100644 index 0000000000000..54e28beb9798b --- /dev/null +++ b/packages/@aws-cdk/aws-xray/jest.config.js @@ -0,0 +1,2 @@ +const baseConfig = require('cdk-build-tools/config/jest.config'); +module.exports = baseConfig; diff --git a/packages/@aws-cdk/aws-xray/lib/index.ts b/packages/@aws-cdk/aws-xray/lib/index.ts new file mode 100644 index 0000000000000..9912dab6f0e95 --- /dev/null +++ b/packages/@aws-cdk/aws-xray/lib/index.ts @@ -0,0 +1,2 @@ +// AWS::XRay CloudFormation Resources: +export * from './xray.generated'; diff --git a/packages/@aws-cdk/aws-xray/package.json b/packages/@aws-cdk/aws-xray/package.json new file mode 100644 index 0000000000000..7efedfd8af300 --- /dev/null +++ b/packages/@aws-cdk/aws-xray/package.json @@ -0,0 +1,101 @@ +{ + "name": "@aws-cdk/aws-xray", + "version": "0.0.0", + "description": "The CDK Construct Library for AWS::XRay", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "jsii": { + "outdir": "dist", + "projectReferences": true, + "targets": { + "dotnet": { + "namespace": "Amazon.CDK.AWS.XRay", + "packageId": "Amazon.CDK.AWS.XRay", + "signAssembly": true, + "assemblyOriginatorKeyFile": "../../key.snk", + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/master/logo/default-256-dark.png" + }, + "java": { + "package": "software.amazon.awscdk.services.xray", + "maven": { + "groupId": "software.amazon.awscdk", + "artifactId": "xray" + } + }, + "python": { + "classifiers": [ + "Framework :: AWS CDK", + "Framework :: AWS CDK :: 1" + ], + "distName": "aws-cdk.aws-xray", + "module": "aws_cdk.aws_xray" + } + } + }, + "repository": { + "type": "git", + "url": "https://github.com/aws/aws-cdk.git", + "directory": "packages/@aws-cdk/aws-xray" + }, + "homepage": "https://github.com/aws/aws-cdk", + "scripts": { + "build": "cdk-build", + "watch": "cdk-watch", + "lint": "cdk-lint", + "test": "cdk-test", + "integ": "cdk-integ", + "pkglint": "pkglint -f", + "package": "cdk-package", + "awslint": "cdk-awslint", + "cfn2ts": "cfn2ts", + "build+test": "yarn build && yarn test", + "build+test+package": "yarn build+test && yarn package", + "compat": "cdk-compat", + "gen": "cfn2ts", + "rosetta:extract": "yarn --silent jsii-rosetta extract" + }, + "cdk-build": { + "cloudformation": "AWS::XRay", + "jest": true, + "env": { + "AWSLINT_BASE_CONSTRUCT": "true" + } + }, + "keywords": [ + "aws", + "cdk", + "constructs", + "AWS::XRay", + "aws-xray" + ], + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com", + "organization": true + }, + "license": "Apache-2.0", + "devDependencies": { + "@types/jest": "^26.0.22", + "@aws-cdk/assert-internal": "0.0.0", + "cdk-build-tools": "0.0.0", + "cfn2ts": "0.0.0", + "pkglint": "0.0.0" + }, + "dependencies": { + "@aws-cdk/core": "0.0.0" + }, + "peerDependencies": { + "@aws-cdk/core": "0.0.0" + }, + "engines": { + "node": ">= 10.13.0 <13 || >=13.7.0" + }, + "stability": "experimental", + "maturity": "cfn-only", + "awscdkio": { + "announce": false + }, + "publishConfig": { + "tag": "latest" + } +} diff --git a/packages/@aws-cdk/aws-xray/test/xray.test.ts b/packages/@aws-cdk/aws-xray/test/xray.test.ts new file mode 100644 index 0000000000000..c4505ad966984 --- /dev/null +++ b/packages/@aws-cdk/aws-xray/test/xray.test.ts @@ -0,0 +1,6 @@ +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/cfnspec/CHANGELOG.md b/packages/@aws-cdk/cfnspec/CHANGELOG.md index 4f49d0b63908e..df0747a8e2705 100644 --- a/packages/@aws-cdk/cfnspec/CHANGELOG.md +++ b/packages/@aws-cdk/cfnspec/CHANGELOG.md @@ -1,3 +1,145 @@ +# CloudFormation Resource Specification v35.2.0 + +## New Resource Types + +* AWS::CloudFront::Function +* AWS::FinSpace::Environment +* AWS::FraudDetector::Detector +* AWS::FraudDetector::EntityType +* AWS::FraudDetector::EventType +* AWS::FraudDetector::Label +* AWS::FraudDetector::Outcome +* AWS::FraudDetector::Variable +* AWS::XRay::Group +* AWS::XRay::SamplingRule + +## Attribute Changes + +* AWS::CloudFront::Distribution Id (__added__) +* AWS::Config::ConfigurationAggregator ConfigurationAggregatorArn (__added__) +* AWS::ECR::Repository RepositoryUri (__added__) +* AWS::ECS::Service ServiceArn (__added__) + +## Property Changes + +* AWS::ACMPCA::CertificateAuthority KeyStorageSecurityStandard (__added__) +* AWS::CloudFront::Distribution Tags.DuplicatesAllowed (__added__) +* AWS::CloudWatch::MetricStream OutputFormat.Required (__changed__) + * Old: false + * New: true +* AWS::Config::ConfigurationAggregator AccountAggregationSources.DuplicatesAllowed (__added__) +* AWS::Config::ConfigurationAggregator ConfigurationAggregatorName.Required (__changed__) + * Old: true + * New: false +* AWS::Config::ConfigurationAggregator Tags.DuplicatesAllowed (__added__) +* AWS::DataBrew::Job JobSample.PrimitiveType (__deleted__) +* AWS::DataBrew::Job JobSample.Type (__added__) +* AWS::DataBrew::Job OutputLocation.PrimitiveType (__deleted__) +* AWS::DataBrew::Job OutputLocation.Type (__added__) +* AWS::DataBrew::Job Recipe.Type (__added__) +* AWS::DataBrew::Project Sample.PrimitiveType (__deleted__) +* AWS::DataBrew::Project Sample.Type (__added__) +* AWS::ECR::Repository EncryptionConfiguration (__added__) +* AWS::ECS::Service ServiceArn (__deleted__) +* AWS::ECS::TaskDefinition EphemeralStorage (__added__) +* AWS::EKS::Nodegroup Taints (__added__) +* AWS::GameLift::Fleet Locations (__added__) +* AWS::GameLift::GameSessionQueue FilterConfiguration (__added__) +* AWS::GameLift::GameSessionQueue PriorityConfiguration (__added__) +* AWS::Lambda::Function Id (__added__) +* AWS::Lambda::Function FileSystemConfigs.DuplicatesAllowed (__deleted__) +* AWS::Lambda::Function Layers.DuplicatesAllowed (__changed__) + * Old: false + * New: true +* AWS::Lambda::Function Tags.DuplicatesAllowed (__changed__) + * Old: true + * New: false + +## Property Type Changes + +* AWS::FIS::ExperimentTemplate.ExperimentTemplateActionItemParameterMap (__removed__) +* AWS::FIS::ExperimentTemplate.ExperimentTemplateActionItemTargetMap (__removed__) +* AWS::FIS::ExperimentTemplate.TagMap (__removed__) +* AWS::CloudFront::Distribution.FunctionAssociation (__added__) +* AWS::CloudFront::Distribution.LegacyCustomOrigin (__added__) +* AWS::CloudFront::Distribution.LegacyS3Origin (__added__) +* AWS::DataBrew::Job.JobSample (__added__) +* AWS::DataBrew::Job.OutputLocation (__added__) +* AWS::DataBrew::Job.Recipe (__added__) +* AWS::DataBrew::Project.Sample (__added__) +* AWS::ECS::TaskDefinition.EphemeralStorage (__added__) +* AWS::EKS::Nodegroup.Taint (__added__) +* AWS::GameLift::Fleet.LocationCapacity (__added__) +* AWS::GameLift::Fleet.LocationConfiguration (__added__) +* AWS::GameLift::GameSessionQueue.FilterConfiguration (__added__) +* AWS::GameLift::GameSessionQueue.PriorityConfiguration (__added__) +* AWS::MSK::Cluster.Iam (__added__) +* AWS::CloudFront::Distribution.CacheBehavior FunctionAssociations (__added__) +* AWS::CloudFront::Distribution.CacheBehavior AllowedMethods.DuplicatesAllowed (__added__) +* AWS::CloudFront::Distribution.CacheBehavior CachedMethods.DuplicatesAllowed (__added__) +* AWS::CloudFront::Distribution.CacheBehavior LambdaFunctionAssociations.DuplicatesAllowed (__added__) +* AWS::CloudFront::Distribution.CacheBehavior TrustedKeyGroups.DuplicatesAllowed (__added__) +* AWS::CloudFront::Distribution.CacheBehavior TrustedSigners.DuplicatesAllowed (__added__) +* AWS::CloudFront::Distribution.Cookies WhitelistedNames.DuplicatesAllowed (__added__) +* AWS::CloudFront::Distribution.CustomOriginConfig OriginSSLProtocols.DuplicatesAllowed (__added__) +* AWS::CloudFront::Distribution.DefaultCacheBehavior FunctionAssociations (__added__) +* AWS::CloudFront::Distribution.DefaultCacheBehavior AllowedMethods.DuplicatesAllowed (__added__) +* AWS::CloudFront::Distribution.DefaultCacheBehavior CachedMethods.DuplicatesAllowed (__added__) +* AWS::CloudFront::Distribution.DefaultCacheBehavior LambdaFunctionAssociations.DuplicatesAllowed (__added__) +* AWS::CloudFront::Distribution.DefaultCacheBehavior TrustedKeyGroups.DuplicatesAllowed (__added__) +* AWS::CloudFront::Distribution.DefaultCacheBehavior TrustedSigners.DuplicatesAllowed (__added__) +* AWS::CloudFront::Distribution.DistributionConfig CNAMEs (__added__) +* AWS::CloudFront::Distribution.DistributionConfig CustomOrigin (__added__) +* AWS::CloudFront::Distribution.DistributionConfig S3Origin (__added__) +* AWS::CloudFront::Distribution.DistributionConfig Aliases.DuplicatesAllowed (__added__) +* AWS::CloudFront::Distribution.DistributionConfig CacheBehaviors.DuplicatesAllowed (__added__) +* AWS::CloudFront::Distribution.DistributionConfig CustomErrorResponses.DuplicatesAllowed (__added__) +* AWS::CloudFront::Distribution.DistributionConfig Origins.DuplicatesAllowed (__added__) +* AWS::CloudFront::Distribution.ForwardedValues Headers.DuplicatesAllowed (__added__) +* AWS::CloudFront::Distribution.ForwardedValues QueryStringCacheKeys.DuplicatesAllowed (__added__) +* AWS::CloudFront::Distribution.GeoRestriction Locations.DuplicatesAllowed (__added__) +* AWS::CloudFront::Distribution.Origin OriginCustomHeaders.DuplicatesAllowed (__added__) +* AWS::CloudFront::Distribution.Origin OriginShield.Type (__deleted__) +* AWS::CloudFront::Distribution.OriginGroupMembers Items.DuplicatesAllowed (__added__) +* AWS::CloudFront::Distribution.OriginGroups Items.DuplicatesAllowed (__added__) +* AWS::CloudFront::Distribution.OriginShield Enabled.Required (__changed__) + * Old: true + * New: false +* AWS::CloudFront::Distribution.StatusCodes Items.DuplicatesAllowed (__added__) +* AWS::Config::ConfigurationAggregator.AccountAggregationSource AccountIds.DuplicatesAllowed (__added__) +* AWS::Config::ConfigurationAggregator.AccountAggregationSource AwsRegions.DuplicatesAllowed (__added__) +* AWS::Config::ConfigurationAggregator.OrganizationAggregationSource AwsRegions.DuplicatesAllowed (__added__) +* AWS::FIS::ExperimentTemplate.ExperimentTemplateAction Parameters.PrimitiveItemType (__added__) +* AWS::FIS::ExperimentTemplate.ExperimentTemplateAction Parameters.Type (__changed__) + * Old: ExperimentTemplateActionItemParameterMap + * New: Map +* AWS::FIS::ExperimentTemplate.ExperimentTemplateAction Targets.PrimitiveItemType (__added__) +* AWS::FIS::ExperimentTemplate.ExperimentTemplateAction Targets.Type (__changed__) + * Old: ExperimentTemplateActionItemTargetMap + * New: Map +* AWS::FIS::ExperimentTemplate.ExperimentTemplateTarget ResourceTags.PrimitiveItemType (__added__) +* AWS::FIS::ExperimentTemplate.ExperimentTemplateTarget ResourceTags.Type (__changed__) + * Old: TagMap + * New: Map +* AWS::Lambda::Function.Environment Variables.DuplicatesAllowed (__deleted__) +* AWS::Lambda::Function.VpcConfig SecurityGroupIds.DuplicatesAllowed (__changed__) + * Old: false + * New: true +* AWS::Lambda::Function.VpcConfig SecurityGroupIds.Required (__changed__) + * Old: true + * New: false +* AWS::Lambda::Function.VpcConfig SubnetIds.DuplicatesAllowed (__changed__) + * Old: false + * New: true +* AWS::Lambda::Function.VpcConfig SubnetIds.Required (__changed__) + * Old: true + * New: false +* AWS::MSK::Cluster.Sasl Iam (__added__) +* AWS::MSK::Cluster.Sasl Scram.Required (__changed__) + * Old: true + * New: false + + # CloudFormation Resource Specification v35.1.0 ## New Resource Types diff --git a/packages/@aws-cdk/cfnspec/cfn.version b/packages/@aws-cdk/cfnspec/cfn.version index 4710c8eb98fcd..6ee5ed10b7ff5 100644 --- a/packages/@aws-cdk/cfnspec/cfn.version +++ b/packages/@aws-cdk/cfnspec/cfn.version @@ -1 +1 @@ -35.1.0 +35.2.0 diff --git a/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json b/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json index 43816f779cc2c..61926760268cd 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json +++ b/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json @@ -9950,6 +9950,7 @@ "Properties": { "AllowedMethods": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-cachebehavior.html#cfn-cloudfront-distribution-cachebehavior-allowedmethods", + "DuplicatesAllowed": true, "PrimitiveItemType": "String", "Required": false, "Type": "List", @@ -9963,6 +9964,7 @@ }, "CachedMethods": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-cachebehavior.html#cfn-cloudfront-distribution-cachebehavior-cachedmethods", + "DuplicatesAllowed": true, "PrimitiveItemType": "String", "Required": false, "Type": "List", @@ -9992,8 +9994,17 @@ "Type": "ForwardedValues", "UpdateType": "Mutable" }, + "FunctionAssociations": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-cachebehavior.html#cfn-cloudfront-distribution-cachebehavior-functionassociations", + "DuplicatesAllowed": true, + "ItemType": "FunctionAssociation", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, "LambdaFunctionAssociations": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-cachebehavior.html#cfn-cloudfront-distribution-cachebehavior-lambdafunctionassociations", + "DuplicatesAllowed": true, "ItemType": "LambdaFunctionAssociation", "Required": false, "Type": "List", @@ -10043,6 +10054,7 @@ }, "TrustedKeyGroups": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-cachebehavior.html#cfn-cloudfront-distribution-cachebehavior-trustedkeygroups", + "DuplicatesAllowed": true, "PrimitiveItemType": "String", "Required": false, "Type": "List", @@ -10050,6 +10062,7 @@ }, "TrustedSigners": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-cachebehavior.html#cfn-cloudfront-distribution-cachebehavior-trustedsigners", + "DuplicatesAllowed": true, "PrimitiveItemType": "String", "Required": false, "Type": "List", @@ -10074,6 +10087,7 @@ }, "WhitelistedNames": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-cookies.html#cfn-cloudfront-distribution-cookies-whitelistednames", + "DuplicatesAllowed": true, "PrimitiveItemType": "String", "Required": false, "Type": "List", @@ -10145,6 +10159,7 @@ }, "OriginSSLProtocols": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-customoriginconfig.html#cfn-cloudfront-distribution-customoriginconfig-originsslprotocols", + "DuplicatesAllowed": true, "PrimitiveItemType": "String", "Required": false, "Type": "List", @@ -10157,6 +10172,7 @@ "Properties": { "AllowedMethods": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-defaultcachebehavior.html#cfn-cloudfront-distribution-defaultcachebehavior-allowedmethods", + "DuplicatesAllowed": true, "PrimitiveItemType": "String", "Required": false, "Type": "List", @@ -10170,6 +10186,7 @@ }, "CachedMethods": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-defaultcachebehavior.html#cfn-cloudfront-distribution-defaultcachebehavior-cachedmethods", + "DuplicatesAllowed": true, "PrimitiveItemType": "String", "Required": false, "Type": "List", @@ -10199,8 +10216,17 @@ "Type": "ForwardedValues", "UpdateType": "Mutable" }, + "FunctionAssociations": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-defaultcachebehavior.html#cfn-cloudfront-distribution-defaultcachebehavior-functionassociations", + "DuplicatesAllowed": true, + "ItemType": "FunctionAssociation", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, "LambdaFunctionAssociations": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-defaultcachebehavior.html#cfn-cloudfront-distribution-defaultcachebehavior-lambdafunctionassociations", + "DuplicatesAllowed": true, "ItemType": "LambdaFunctionAssociation", "Required": false, "Type": "List", @@ -10244,6 +10270,7 @@ }, "TrustedKeyGroups": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-defaultcachebehavior.html#cfn-cloudfront-distribution-defaultcachebehavior-trustedkeygroups", + "DuplicatesAllowed": true, "PrimitiveItemType": "String", "Required": false, "Type": "List", @@ -10251,6 +10278,7 @@ }, "TrustedSigners": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-defaultcachebehavior.html#cfn-cloudfront-distribution-defaultcachebehavior-trustedsigners", + "DuplicatesAllowed": true, "PrimitiveItemType": "String", "Required": false, "Type": "List", @@ -10269,6 +10297,15 @@ "Properties": { "Aliases": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-distributionconfig.html#cfn-cloudfront-distribution-distributionconfig-aliases", + "DuplicatesAllowed": true, + "PrimitiveItemType": "String", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "CNAMEs": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-distributionconfig.html#cfn-cloudfront-distribution-distributionconfig-cnames", + "DuplicatesAllowed": true, "PrimitiveItemType": "String", "Required": false, "Type": "List", @@ -10276,6 +10313,7 @@ }, "CacheBehaviors": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-distributionconfig.html#cfn-cloudfront-distribution-distributionconfig-cachebehaviors", + "DuplicatesAllowed": true, "ItemType": "CacheBehavior", "Required": false, "Type": "List", @@ -10289,11 +10327,18 @@ }, "CustomErrorResponses": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-distributionconfig.html#cfn-cloudfront-distribution-distributionconfig-customerrorresponses", + "DuplicatesAllowed": true, "ItemType": "CustomErrorResponse", "Required": false, "Type": "List", "UpdateType": "Mutable" }, + "CustomOrigin": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-distributionconfig.html#cfn-cloudfront-distribution-distributionconfig-customorigin", + "Required": false, + "Type": "LegacyCustomOrigin", + "UpdateType": "Mutable" + }, "DefaultCacheBehavior": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-distributionconfig.html#cfn-cloudfront-distribution-distributionconfig-defaultcachebehavior", "Required": false, @@ -10338,6 +10383,7 @@ }, "Origins": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-distributionconfig.html#cfn-cloudfront-distribution-distributionconfig-origins", + "DuplicatesAllowed": true, "ItemType": "Origin", "Required": false, "Type": "List", @@ -10355,6 +10401,12 @@ "Type": "Restrictions", "UpdateType": "Mutable" }, + "S3Origin": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-distributionconfig.html#cfn-cloudfront-distribution-distributionconfig-s3origin", + "Required": false, + "Type": "LegacyS3Origin", + "UpdateType": "Mutable" + }, "ViewerCertificate": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-distributionconfig.html#cfn-cloudfront-distribution-distributionconfig-viewercertificate", "Required": false, @@ -10380,6 +10432,7 @@ }, "Headers": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-forwardedvalues.html#cfn-cloudfront-distribution-forwardedvalues-headers", + "DuplicatesAllowed": true, "PrimitiveItemType": "String", "Required": false, "Type": "List", @@ -10393,6 +10446,7 @@ }, "QueryStringCacheKeys": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-forwardedvalues.html#cfn-cloudfront-distribution-forwardedvalues-querystringcachekeys", + "DuplicatesAllowed": true, "PrimitiveItemType": "String", "Required": false, "Type": "List", @@ -10400,11 +10454,29 @@ } } }, + "AWS::CloudFront::Distribution.FunctionAssociation": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-functionassociation.html", + "Properties": { + "EventType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-functionassociation.html#cfn-cloudfront-distribution-functionassociation-eventtype", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "FunctionARN": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-functionassociation.html#cfn-cloudfront-distribution-functionassociation-functionarn", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, "AWS::CloudFront::Distribution.GeoRestriction": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-georestriction.html", "Properties": { "Locations": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-georestriction.html#cfn-cloudfront-distribution-georestriction-locations", + "DuplicatesAllowed": true, "PrimitiveItemType": "String", "Required": false, "Type": "List", @@ -10441,6 +10513,60 @@ } } }, + "AWS::CloudFront::Distribution.LegacyCustomOrigin": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-legacycustomorigin.html", + "Properties": { + "DNSName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-legacycustomorigin.html#cfn-cloudfront-distribution-legacycustomorigin-dnsname", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "HTTPPort": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-legacycustomorigin.html#cfn-cloudfront-distribution-legacycustomorigin-httpport", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "HTTPSPort": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-legacycustomorigin.html#cfn-cloudfront-distribution-legacycustomorigin-httpsport", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "OriginProtocolPolicy": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-legacycustomorigin.html#cfn-cloudfront-distribution-legacycustomorigin-originprotocolpolicy", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "OriginSSLProtocols": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-legacycustomorigin.html#cfn-cloudfront-distribution-legacycustomorigin-originsslprotocols", + "DuplicatesAllowed": true, + "PrimitiveItemType": "String", + "Required": true, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, + "AWS::CloudFront::Distribution.LegacyS3Origin": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-legacys3origin.html", + "Properties": { + "DNSName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-legacys3origin.html#cfn-cloudfront-distribution-legacys3origin-dnsname", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "OriginAccessIdentity": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-legacys3origin.html#cfn-cloudfront-distribution-legacys3origin-originaccessidentity", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, "AWS::CloudFront::Distribution.Logging": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-logging.html", "Properties": { @@ -10499,6 +10625,7 @@ }, "OriginCustomHeaders": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-origin.html#cfn-cloudfront-distribution-origin-origincustomheaders", + "DuplicatesAllowed": true, "ItemType": "OriginCustomHeader", "Required": false, "Type": "List", @@ -10513,7 +10640,6 @@ "OriginShield": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-origin.html#cfn-cloudfront-distribution-origin-originshield", "Required": false, - "Type": "OriginShield", "UpdateType": "Mutable" }, "S3OriginConfig": { @@ -10591,6 +10717,7 @@ "Properties": { "Items": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-origingroupmembers.html#cfn-cloudfront-distribution-origingroupmembers-items", + "DuplicatesAllowed": true, "ItemType": "OriginGroupMember", "Required": true, "Type": "List", @@ -10609,6 +10736,7 @@ "Properties": { "Items": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-origingroups.html#cfn-cloudfront-distribution-origingroups-items", + "DuplicatesAllowed": true, "ItemType": "OriginGroup", "Required": false, "Type": "List", @@ -10628,7 +10756,7 @@ "Enabled": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-originshield.html#cfn-cloudfront-distribution-originshield-enabled", "PrimitiveType": "Boolean", - "Required": true, + "Required": false, "UpdateType": "Mutable" }, "OriginShieldRegion": { @@ -10666,6 +10794,7 @@ "Properties": { "Items": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-statuscodes.html#cfn-cloudfront-distribution-statuscodes-items", + "DuplicatesAllowed": true, "PrimitiveItemType": "Integer", "Required": true, "Type": "List", @@ -10714,6 +10843,34 @@ } } }, + "AWS::CloudFront::Function.FunctionConfig": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-function-functionconfig.html", + "Properties": { + "Comment": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-function-functionconfig.html#cfn-cloudfront-function-functionconfig-comment", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "Runtime": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-function-functionconfig.html#cfn-cloudfront-function-functionconfig-runtime", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::CloudFront::Function.FunctionMetadata": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-function-functionmetadata.html", + "Properties": { + "FunctionARN": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-function-functionmetadata.html#cfn-cloudfront-function-functionmetadata-functionarn", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, "AWS::CloudFront::KeyGroup.KeyGroupConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-keygroup-keygroupconfig.html", "Properties": { @@ -13616,6 +13773,7 @@ "Properties": { "AccountIds": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-config-configurationaggregator-accountaggregationsource.html#cfn-config-configurationaggregator-accountaggregationsource-accountids", + "DuplicatesAllowed": true, "PrimitiveItemType": "String", "Required": true, "Type": "List", @@ -13629,6 +13787,7 @@ }, "AwsRegions": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-config-configurationaggregator-accountaggregationsource.html#cfn-config-configurationaggregator-accountaggregationsource-awsregions", + "DuplicatesAllowed": true, "PrimitiveItemType": "String", "Required": false, "Type": "List", @@ -13647,6 +13806,7 @@ }, "AwsRegions": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-config-configurationaggregator-organizationaggregationsource.html#cfn-config-configurationaggregator-organizationaggregationsource-awsregions", + "DuplicatesAllowed": true, "PrimitiveItemType": "String", "Required": false, "Type": "List", @@ -15150,6 +15310,23 @@ } } }, + "AWS::DataBrew::Job.JobSample": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-job-jobsample.html", + "Properties": { + "Mode": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-job-jobsample.html#cfn-databrew-job-jobsample-mode", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Size": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-job-jobsample.html#cfn-databrew-job-jobsample-size", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + } + } + }, "AWS::DataBrew::Job.Output": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-job-output.html", "Properties": { @@ -15204,6 +15381,40 @@ } } }, + "AWS::DataBrew::Job.OutputLocation": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-job-outputlocation.html", + "Properties": { + "Bucket": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-job-outputlocation.html#cfn-databrew-job-outputlocation-bucket", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "Key": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-job-outputlocation.html#cfn-databrew-job-outputlocation-key", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::DataBrew::Job.Recipe": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-job-recipe.html", + "Properties": { + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-job-recipe.html#cfn-databrew-job-recipe-name", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "Version": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-job-recipe.html#cfn-databrew-job-recipe-version", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, "AWS::DataBrew::Job.S3Location": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-job-s3location.html", "Properties": { @@ -15221,6 +15432,23 @@ } } }, + "AWS::DataBrew::Project.Sample": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-project-sample.html", + "Properties": { + "Size": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-project-sample.html#cfn-databrew-project-sample-size", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "Type": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-project-sample.html#cfn-databrew-project-sample-type", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, "AWS::DataBrew::Recipe.Action": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-recipe-action.html", "Properties": { @@ -20431,6 +20659,17 @@ } } }, + "AWS::ECS::TaskDefinition.EphemeralStorage": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-taskdefinition-ephemeralstorage.html", + "Properties": { + "SizeInGiB": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-taskdefinition-ephemeralstorage.html#cfn-ecs-taskdefinition-ephemeralstorage-sizeingib", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + } + } + }, "AWS::ECS::TaskDefinition.FirelensConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-taskdefinition-firelensconfiguration.html", "Properties": { @@ -21272,6 +21511,29 @@ } } }, + "AWS::EKS::Nodegroup.Taint": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-eks-nodegroup-taint.html", + "Properties": { + "Effect": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-eks-nodegroup-taint.html#cfn-eks-nodegroup-taint-effect", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Key": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-eks-nodegroup-taint.html#cfn-eks-nodegroup-taint-key", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Value": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-eks-nodegroup-taint.html#cfn-eks-nodegroup-taint-value", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, "AWS::EMR::Cluster.Application": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticmapreduce-cluster-application.html", "Properties": { @@ -24949,8 +25211,9 @@ }, "Parameters": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-fis-experimenttemplate-experimenttemplateaction.html#cfn-fis-experimenttemplate-experimenttemplateaction-parameters", + "PrimitiveItemType": "String", "Required": false, - "Type": "ExperimentTemplateActionItemParameterMap", + "Type": "Map", "UpdateType": "Mutable" }, "StartAfter": { @@ -24962,18 +25225,13 @@ }, "Targets": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-fis-experimenttemplate-experimenttemplateaction.html#cfn-fis-experimenttemplate-experimenttemplateaction-targets", + "PrimitiveItemType": "String", "Required": false, - "Type": "ExperimentTemplateActionItemTargetMap", + "Type": "Map", "UpdateType": "Mutable" } } }, - "AWS::FIS::ExperimentTemplate.ExperimentTemplateActionItemParameterMap": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-fis-experimenttemplate-experimenttemplateactionitemparametermap.html" - }, - "AWS::FIS::ExperimentTemplate.ExperimentTemplateActionItemTargetMap": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-fis-experimenttemplate-experimenttemplateactionitemtargetmap.html" - }, "AWS::FIS::ExperimentTemplate.ExperimentTemplateStopCondition": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-fis-experimenttemplate-experimenttemplatestopcondition.html", "Properties": { @@ -25010,8 +25268,9 @@ }, "ResourceTags": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-fis-experimenttemplate-experimenttemplatetarget.html#cfn-fis-experimenttemplate-experimenttemplatetarget-resourcetags", + "PrimitiveItemType": "String", "Required": false, - "Type": "TagMap", + "Type": "Map", "UpdateType": "Mutable" }, "ResourceType": { @@ -25046,9 +25305,6 @@ } } }, - "AWS::FIS::ExperimentTemplate.TagMap": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-fis-experimenttemplate-tagmap.html" - }, "AWS::FMS::Policy.IEMap": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-fms-policy-iemap.html", "Properties": { @@ -25281,6 +25537,586 @@ } } }, + "AWS::FinSpace::Environment.FederationParameters": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-finspace-environment-federationparameters.html", + "Properties": { + "ApplicationCallBackURL": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-finspace-environment-federationparameters.html#cfn-finspace-environment-federationparameters-applicationcallbackurl", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "AttributeMap": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-finspace-environment-federationparameters.html#cfn-finspace-environment-federationparameters-attributemap", + "PrimitiveType": "Json", + "Required": false, + "UpdateType": "Mutable" + }, + "FederationProviderName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-finspace-environment-federationparameters.html#cfn-finspace-environment-federationparameters-federationprovidername", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "FederationURN": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-finspace-environment-federationparameters.html#cfn-finspace-environment-federationparameters-federationurn", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "SamlMetadataDocument": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-finspace-environment-federationparameters.html#cfn-finspace-environment-federationparameters-samlmetadatadocument", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "SamlMetadataURL": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-finspace-environment-federationparameters.html#cfn-finspace-environment-federationparameters-samlmetadataurl", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::FraudDetector::Detector.EntityType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-entitytype.html", + "Properties": { + "Arn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-entitytype.html#cfn-frauddetector-detector-entitytype-arn", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "CreatedTime": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-entitytype.html#cfn-frauddetector-detector-entitytype-createdtime", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Description": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-entitytype.html#cfn-frauddetector-detector-entitytype-description", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Inline": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-entitytype.html#cfn-frauddetector-detector-entitytype-inline", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "LastUpdatedTime": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-entitytype.html#cfn-frauddetector-detector-entitytype-lastupdatedtime", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-entitytype.html#cfn-frauddetector-detector-entitytype-name", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-entitytype.html#cfn-frauddetector-detector-entitytype-tags", + "DuplicatesAllowed": true, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, + "AWS::FraudDetector::Detector.EventType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-eventtype.html", + "Properties": { + "Arn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-eventtype.html#cfn-frauddetector-detector-eventtype-arn", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "CreatedTime": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-eventtype.html#cfn-frauddetector-detector-eventtype-createdtime", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Description": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-eventtype.html#cfn-frauddetector-detector-eventtype-description", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "EntityTypes": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-eventtype.html#cfn-frauddetector-detector-eventtype-entitytypes", + "DuplicatesAllowed": true, + "ItemType": "EntityType", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "EventVariables": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-eventtype.html#cfn-frauddetector-detector-eventtype-eventvariables", + "DuplicatesAllowed": true, + "ItemType": "EventVariable", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "Inline": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-eventtype.html#cfn-frauddetector-detector-eventtype-inline", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "Labels": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-eventtype.html#cfn-frauddetector-detector-eventtype-labels", + "DuplicatesAllowed": true, + "ItemType": "Label", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "LastUpdatedTime": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-eventtype.html#cfn-frauddetector-detector-eventtype-lastupdatedtime", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-eventtype.html#cfn-frauddetector-detector-eventtype-name", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-eventtype.html#cfn-frauddetector-detector-eventtype-tags", + "DuplicatesAllowed": true, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, + "AWS::FraudDetector::Detector.EventVariable": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-eventvariable.html", + "Properties": { + "Arn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-eventvariable.html#cfn-frauddetector-detector-eventvariable-arn", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "CreatedTime": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-eventvariable.html#cfn-frauddetector-detector-eventvariable-createdtime", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "DataSource": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-eventvariable.html#cfn-frauddetector-detector-eventvariable-datasource", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "DataType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-eventvariable.html#cfn-frauddetector-detector-eventvariable-datatype", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "DefaultValue": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-eventvariable.html#cfn-frauddetector-detector-eventvariable-defaultvalue", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Description": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-eventvariable.html#cfn-frauddetector-detector-eventvariable-description", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Inline": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-eventvariable.html#cfn-frauddetector-detector-eventvariable-inline", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "LastUpdatedTime": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-eventvariable.html#cfn-frauddetector-detector-eventvariable-lastupdatedtime", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-eventvariable.html#cfn-frauddetector-detector-eventvariable-name", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-eventvariable.html#cfn-frauddetector-detector-eventvariable-tags", + "DuplicatesAllowed": true, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "VariableType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-eventvariable.html#cfn-frauddetector-detector-eventvariable-variabletype", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::FraudDetector::Detector.Label": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-label.html", + "Properties": { + "Arn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-label.html#cfn-frauddetector-detector-label-arn", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "CreatedTime": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-label.html#cfn-frauddetector-detector-label-createdtime", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Description": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-label.html#cfn-frauddetector-detector-label-description", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Inline": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-label.html#cfn-frauddetector-detector-label-inline", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "LastUpdatedTime": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-label.html#cfn-frauddetector-detector-label-lastupdatedtime", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-label.html#cfn-frauddetector-detector-label-name", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-label.html#cfn-frauddetector-detector-label-tags", + "DuplicatesAllowed": true, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, + "AWS::FraudDetector::Detector.Outcome": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-outcome.html", + "Properties": { + "Arn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-outcome.html#cfn-frauddetector-detector-outcome-arn", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "CreatedTime": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-outcome.html#cfn-frauddetector-detector-outcome-createdtime", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Description": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-outcome.html#cfn-frauddetector-detector-outcome-description", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Inline": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-outcome.html#cfn-frauddetector-detector-outcome-inline", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "LastUpdatedTime": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-outcome.html#cfn-frauddetector-detector-outcome-lastupdatedtime", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-outcome.html#cfn-frauddetector-detector-outcome-name", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-outcome.html#cfn-frauddetector-detector-outcome-tags", + "DuplicatesAllowed": true, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, + "AWS::FraudDetector::Detector.Rule": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-rule.html", + "Properties": { + "Arn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-rule.html#cfn-frauddetector-detector-rule-arn", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "CreatedTime": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-rule.html#cfn-frauddetector-detector-rule-createdtime", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Description": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-rule.html#cfn-frauddetector-detector-rule-description", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "DetectorId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-rule.html#cfn-frauddetector-detector-rule-detectorid", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Expression": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-rule.html#cfn-frauddetector-detector-rule-expression", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Language": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-rule.html#cfn-frauddetector-detector-rule-language", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "LastUpdatedTime": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-rule.html#cfn-frauddetector-detector-rule-lastupdatedtime", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Outcomes": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-rule.html#cfn-frauddetector-detector-rule-outcomes", + "DuplicatesAllowed": true, + "ItemType": "Outcome", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "RuleId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-rule.html#cfn-frauddetector-detector-rule-ruleid", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "RuleVersion": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-rule.html#cfn-frauddetector-detector-rule-ruleversion", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-detector-rule.html#cfn-frauddetector-detector-rule-tags", + "DuplicatesAllowed": true, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, + "AWS::FraudDetector::EventType.EntityType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-eventtype-entitytype.html", + "Properties": { + "Arn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-eventtype-entitytype.html#cfn-frauddetector-eventtype-entitytype-arn", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "CreatedTime": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-eventtype-entitytype.html#cfn-frauddetector-eventtype-entitytype-createdtime", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Description": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-eventtype-entitytype.html#cfn-frauddetector-eventtype-entitytype-description", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Inline": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-eventtype-entitytype.html#cfn-frauddetector-eventtype-entitytype-inline", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "LastUpdatedTime": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-eventtype-entitytype.html#cfn-frauddetector-eventtype-entitytype-lastupdatedtime", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-eventtype-entitytype.html#cfn-frauddetector-eventtype-entitytype-name", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-eventtype-entitytype.html#cfn-frauddetector-eventtype-entitytype-tags", + "DuplicatesAllowed": true, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, + "AWS::FraudDetector::EventType.EventVariable": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-eventtype-eventvariable.html", + "Properties": { + "Arn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-eventtype-eventvariable.html#cfn-frauddetector-eventtype-eventvariable-arn", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "CreatedTime": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-eventtype-eventvariable.html#cfn-frauddetector-eventtype-eventvariable-createdtime", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "DataSource": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-eventtype-eventvariable.html#cfn-frauddetector-eventtype-eventvariable-datasource", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "DataType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-eventtype-eventvariable.html#cfn-frauddetector-eventtype-eventvariable-datatype", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "DefaultValue": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-eventtype-eventvariable.html#cfn-frauddetector-eventtype-eventvariable-defaultvalue", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Description": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-eventtype-eventvariable.html#cfn-frauddetector-eventtype-eventvariable-description", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Inline": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-eventtype-eventvariable.html#cfn-frauddetector-eventtype-eventvariable-inline", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "LastUpdatedTime": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-eventtype-eventvariable.html#cfn-frauddetector-eventtype-eventvariable-lastupdatedtime", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-eventtype-eventvariable.html#cfn-frauddetector-eventtype-eventvariable-name", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-eventtype-eventvariable.html#cfn-frauddetector-eventtype-eventvariable-tags", + "DuplicatesAllowed": true, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "VariableType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-eventtype-eventvariable.html#cfn-frauddetector-eventtype-eventvariable-variabletype", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::FraudDetector::EventType.Label": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-eventtype-label.html", + "Properties": { + "Arn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-eventtype-label.html#cfn-frauddetector-eventtype-label-arn", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "CreatedTime": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-eventtype-label.html#cfn-frauddetector-eventtype-label-createdtime", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Description": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-eventtype-label.html#cfn-frauddetector-eventtype-label-description", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Inline": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-eventtype-label.html#cfn-frauddetector-eventtype-label-inline", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "LastUpdatedTime": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-eventtype-label.html#cfn-frauddetector-eventtype-label-lastupdatedtime", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-eventtype-label.html#cfn-frauddetector-eventtype-label-name", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-frauddetector-eventtype-label.html#cfn-frauddetector-eventtype-label-tags", + "DuplicatesAllowed": true, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, "AWS::GameLift::Alias.RoutingStrategy": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-gamelift-alias-routingstrategy.html", "Properties": { @@ -25373,6 +26209,46 @@ } } }, + "AWS::GameLift::Fleet.LocationCapacity": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-gamelift-fleet-locationcapacity.html", + "Properties": { + "DesiredEC2Instances": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-gamelift-fleet-locationcapacity.html#cfn-gamelift-fleet-locationcapacity-desiredec2instances", + "PrimitiveType": "Integer", + "Required": true, + "UpdateType": "Mutable" + }, + "MaxSize": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-gamelift-fleet-locationcapacity.html#cfn-gamelift-fleet-locationcapacity-maxsize", + "PrimitiveType": "Integer", + "Required": true, + "UpdateType": "Mutable" + }, + "MinSize": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-gamelift-fleet-locationcapacity.html#cfn-gamelift-fleet-locationcapacity-minsize", + "PrimitiveType": "Integer", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::GameLift::Fleet.LocationConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-gamelift-fleet-locationconfiguration.html", + "Properties": { + "Location": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-gamelift-fleet-locationconfiguration.html#cfn-gamelift-fleet-locationconfiguration-location", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "LocationCapacity": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-gamelift-fleet-locationconfiguration.html#cfn-gamelift-fleet-locationconfiguration-locationcapacity", + "Required": false, + "Type": "LocationCapacity", + "UpdateType": "Mutable" + } + } + }, "AWS::GameLift::Fleet.ResourceCreationLimitPolicy": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-gamelift-fleet-resourcecreationlimitpolicy.html", "Properties": { @@ -25516,6 +26392,18 @@ } } }, + "AWS::GameLift::GameSessionQueue.FilterConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-gamelift-gamesessionqueue-filterconfiguration.html", + "Properties": { + "AllowedLocations": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-gamelift-gamesessionqueue-filterconfiguration.html#cfn-gamelift-gamesessionqueue-filterconfiguration-allowedlocations", + "PrimitiveItemType": "String", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, "AWS::GameLift::GameSessionQueue.PlayerLatencyPolicy": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-gamelift-gamesessionqueue-playerlatencypolicy.html", "Properties": { @@ -25533,6 +26421,25 @@ } } }, + "AWS::GameLift::GameSessionQueue.PriorityConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-gamelift-gamesessionqueue-priorityconfiguration.html", + "Properties": { + "LocationOrder": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-gamelift-gamesessionqueue-priorityconfiguration.html#cfn-gamelift-gamesessionqueue-priorityconfiguration-locationorder", + "PrimitiveItemType": "String", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "PriorityOrder": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-gamelift-gamesessionqueue-priorityconfiguration.html#cfn-gamelift-gamesessionqueue-priorityconfiguration-priorityorder", + "PrimitiveItemType": "String", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, "AWS::GameLift::MatchmakingConfiguration.GameProperty": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-gamelift-matchmakingconfiguration-gameproperty.html", "Properties": { @@ -37248,7 +38155,6 @@ "Properties": { "Variables": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-environment.html#cfn-lambda-function-environment-variables", - "DuplicatesAllowed": false, "PrimitiveItemType": "String", "Required": false, "Type": "Map", @@ -37316,17 +38222,17 @@ "Properties": { "SecurityGroupIds": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-vpcconfig.html#cfn-lambda-function-vpcconfig-securitygroupids", - "DuplicatesAllowed": false, + "DuplicatesAllowed": true, "PrimitiveItemType": "String", - "Required": true, + "Required": false, "Type": "List", "UpdateType": "Mutable" }, "SubnetIds": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-vpcconfig.html#cfn-lambda-function-vpcconfig-subnetids", - "DuplicatesAllowed": false, + "DuplicatesAllowed": true, "PrimitiveItemType": "String", - "Required": true, + "Required": false, "Type": "List", "UpdateType": "Mutable" } @@ -38118,6 +39024,17 @@ } } }, + "AWS::MSK::Cluster.Iam": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-msk-cluster-iam.html", + "Properties": { + "Enabled": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-msk-cluster-iam.html#cfn-msk-cluster-iam-enabled", + "PrimitiveType": "Boolean", + "Required": true, + "UpdateType": "Immutable" + } + } + }, "AWS::MSK::Cluster.JmxExporter": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-msk-cluster-jmxexporter.html", "Properties": { @@ -38205,9 +39122,15 @@ "AWS::MSK::Cluster.Sasl": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-msk-cluster-sasl.html", "Properties": { + "Iam": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-msk-cluster-sasl.html#cfn-msk-cluster-sasl-iam", + "Required": false, + "Type": "Iam", + "UpdateType": "Immutable" + }, "Scram": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-msk-cluster-sasl.html#cfn-msk-cluster-sasl-scram", - "Required": true, + "Required": false, "Type": "Scram", "UpdateType": "Immutable" } @@ -57304,6 +58227,208 @@ } } }, + "AWS::XRay::Group.InsightsConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-group-insightsconfiguration.html", + "Properties": { + "InsightsEnabled": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-group-insightsconfiguration.html#cfn-xray-group-insightsconfiguration-insightsenabled", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "NotificationsEnabled": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-group-insightsconfiguration.html#cfn-xray-group-insightsconfiguration-notificationsenabled", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::XRay::SamplingRule.SamplingRule": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingrule.html", + "Properties": { + "Attributes": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingrule.html#cfn-xray-samplingrule-samplingrule-attributes", + "PrimitiveItemType": "String", + "Required": false, + "Type": "Map", + "UpdateType": "Mutable" + }, + "FixedRate": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingrule.html#cfn-xray-samplingrule-samplingrule-fixedrate", + "PrimitiveType": "Double", + "Required": false, + "UpdateType": "Mutable" + }, + "HTTPMethod": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingrule.html#cfn-xray-samplingrule-samplingrule-httpmethod", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Host": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingrule.html#cfn-xray-samplingrule-samplingrule-host", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Priority": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingrule.html#cfn-xray-samplingrule-samplingrule-priority", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "ReservoirSize": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingrule.html#cfn-xray-samplingrule-samplingrule-reservoirsize", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "ResourceARN": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingrule.html#cfn-xray-samplingrule-samplingrule-resourcearn", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "RuleARN": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingrule.html#cfn-xray-samplingrule-samplingrule-rulearn", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "RuleName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingrule.html#cfn-xray-samplingrule-samplingrule-rulename", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "ServiceName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingrule.html#cfn-xray-samplingrule-samplingrule-servicename", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "ServiceType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingrule.html#cfn-xray-samplingrule-samplingrule-servicetype", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "URLPath": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingrule.html#cfn-xray-samplingrule-samplingrule-urlpath", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Version": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingrule.html#cfn-xray-samplingrule-samplingrule-version", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::XRay::SamplingRule.SamplingRuleRecord": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingrulerecord.html", + "Properties": { + "CreatedAt": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingrulerecord.html#cfn-xray-samplingrule-samplingrulerecord-createdat", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "ModifiedAt": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingrulerecord.html#cfn-xray-samplingrule-samplingrulerecord-modifiedat", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "SamplingRule": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingrulerecord.html#cfn-xray-samplingrule-samplingrulerecord-samplingrule", + "Required": false, + "Type": "SamplingRule", + "UpdateType": "Mutable" + } + } + }, + "AWS::XRay::SamplingRule.SamplingRuleUpdate": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingruleupdate.html", + "Properties": { + "Attributes": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingruleupdate.html#cfn-xray-samplingrule-samplingruleupdate-attributes", + "PrimitiveItemType": "String", + "Required": false, + "Type": "Map", + "UpdateType": "Mutable" + }, + "FixedRate": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingruleupdate.html#cfn-xray-samplingrule-samplingruleupdate-fixedrate", + "PrimitiveType": "Double", + "Required": false, + "UpdateType": "Mutable" + }, + "HTTPMethod": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingruleupdate.html#cfn-xray-samplingrule-samplingruleupdate-httpmethod", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Host": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingruleupdate.html#cfn-xray-samplingrule-samplingruleupdate-host", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Priority": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingruleupdate.html#cfn-xray-samplingrule-samplingruleupdate-priority", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "ReservoirSize": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingruleupdate.html#cfn-xray-samplingrule-samplingruleupdate-reservoirsize", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "ResourceARN": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingruleupdate.html#cfn-xray-samplingrule-samplingruleupdate-resourcearn", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "RuleARN": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingruleupdate.html#cfn-xray-samplingrule-samplingruleupdate-rulearn", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "RuleName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingruleupdate.html#cfn-xray-samplingrule-samplingruleupdate-rulename", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "ServiceName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingruleupdate.html#cfn-xray-samplingrule-samplingruleupdate-servicename", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "ServiceType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingruleupdate.html#cfn-xray-samplingrule-samplingruleupdate-servicetype", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "URLPath": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-xray-samplingrule-samplingruleupdate.html#cfn-xray-samplingrule-samplingruleupdate-urlpath", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, "Alexa::ASK::Skill.AuthenticationConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ask-skill-authenticationconfiguration.html", "Properties": { @@ -57391,7 +58516,7 @@ } } }, - "ResourceSpecificationVersion": "35.1.0", + "ResourceSpecificationVersion": "35.2.0", "ResourceTypes": { "AWS::ACMPCA::Certificate": { "Attributes": { @@ -57471,6 +58596,12 @@ "Required": true, "UpdateType": "Immutable" }, + "KeyStorageSecurityStandard": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-acmpca-certificateauthority.html#cfn-acmpca-certificateauthority-keystoragesecuritystandard", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, "RevocationConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-acmpca-certificateauthority.html#cfn-acmpca-certificateauthority-revocationconfiguration", "Required": false, @@ -63271,6 +64402,9 @@ "Attributes": { "DomainName": { "PrimitiveType": "String" + }, + "Id": { + "PrimitiveType": "String" } }, "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudfront-distribution.html", @@ -63283,6 +64417,7 @@ }, "Tags": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudfront-distribution.html#cfn-cloudfront-distribution-tags", + "DuplicatesAllowed": true, "ItemType": "Tag", "Required": false, "Type": "List", @@ -63290,6 +64425,49 @@ } } }, + "AWS::CloudFront::Function": { + "Attributes": { + "FunctionARN": { + "PrimitiveType": "String" + }, + "Stage": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudfront-function.html", + "Properties": { + "AutoPublish": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudfront-function.html#cfn-cloudfront-function-autopublish", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "FunctionCode": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudfront-function.html#cfn-cloudfront-function-functioncode", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "FunctionConfig": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudfront-function.html#cfn-cloudfront-function-functionconfig", + "Required": false, + "Type": "FunctionConfig", + "UpdateType": "Mutable" + }, + "FunctionMetadata": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudfront-function.html#cfn-cloudfront-function-functionmetadata", + "Required": false, + "Type": "FunctionMetadata", + "UpdateType": "Mutable" + }, + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudfront-function.html#cfn-cloudfront-function-name", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, "AWS::CloudFront::KeyGroup": { "Attributes": { "Id": { @@ -63842,7 +65020,7 @@ "OutputFormat": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudwatch-metricstream.html#cfn-cloudwatch-metricstream-outputformat", "PrimitiveType": "String", - "Required": false, + "Required": true, "UpdateType": "Mutable" }, "RoleArn": { @@ -65524,10 +66702,16 @@ } }, "AWS::Config::ConfigurationAggregator": { + "Attributes": { + "ConfigurationAggregatorArn": { + "PrimitiveType": "String" + } + }, "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-config-configurationaggregator.html", "Properties": { "AccountAggregationSources": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-config-configurationaggregator.html#cfn-config-configurationaggregator-accountaggregationsources", + "DuplicatesAllowed": true, "ItemType": "AccountAggregationSource", "Required": false, "Type": "List", @@ -65536,7 +66720,7 @@ "ConfigurationAggregatorName": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-config-configurationaggregator.html#cfn-config-configurationaggregator-configurationaggregatorname", "PrimitiveType": "String", - "Required": true, + "Required": false, "UpdateType": "Immutable" }, "OrganizationAggregationSource": { @@ -65547,6 +66731,7 @@ }, "Tags": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-config-configurationaggregator.html#cfn-config-configurationaggregator-tags", + "DuplicatesAllowed": false, "ItemType": "Tag", "Required": false, "Type": "List", @@ -66728,8 +67913,8 @@ }, "JobSample": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-databrew-job.html#cfn-databrew-job-jobsample", - "PrimitiveType": "Json", "Required": false, + "Type": "JobSample", "UpdateType": "Mutable" }, "LogSubscription": { @@ -66758,8 +67943,8 @@ }, "OutputLocation": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-databrew-job.html#cfn-databrew-job-outputlocation", - "PrimitiveType": "Json", "Required": false, + "Type": "OutputLocation", "UpdateType": "Mutable" }, "Outputs": { @@ -66779,6 +67964,7 @@ "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-databrew-job.html#cfn-databrew-job-recipe", "PrimitiveType": "Json", "Required": false, + "Type": "Recipe", "UpdateType": "Mutable" }, "RoleArn": { @@ -66838,8 +68024,8 @@ }, "Sample": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-databrew-project.html#cfn-databrew-project-sample", - "PrimitiveType": "Json", "Required": false, + "Type": "Sample", "UpdateType": "Mutable" }, "Tags": { @@ -71055,10 +72241,19 @@ "Attributes": { "Arn": { "PrimitiveType": "String" + }, + "RepositoryUri": { + "PrimitiveType": "String" } }, "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecr-repository.html", "Properties": { + "EncryptionConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecr-repository.html#cfn-ecr-repository-encryptionconfiguration", + "PrimitiveType": "Json", + "Required": false, + "UpdateType": "Immutable" + }, "ImageScanningConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecr-repository.html#cfn-ecr-repository-imagescanningconfiguration", "PrimitiveType": "Json", @@ -71226,6 +72421,9 @@ "Attributes": { "Name": { "PrimitiveType": "String" + }, + "ServiceArn": { + "PrimitiveType": "String" } }, "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-service.html", @@ -71336,12 +72534,6 @@ "Required": false, "UpdateType": "Immutable" }, - "ServiceArn": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-service.html#cfn-ecs-service-servicearn", - "PrimitiveType": "String", - "Required": false, - "UpdateType": "Mutable" - }, "ServiceName": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-service.html#cfn-ecs-service-servicename", "PrimitiveType": "String", @@ -71392,6 +72584,12 @@ "Required": false, "UpdateType": "Immutable" }, + "EphemeralStorage": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-taskdefinition.html#cfn-ecs-taskdefinition-ephemeralstorage", + "Required": false, + "Type": "EphemeralStorage", + "UpdateType": "Mutable" + }, "ExecutionRoleArn": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-taskdefinition.html#cfn-ecs-taskdefinition-executionrolearn", "PrimitiveType": "String", @@ -71983,6 +73181,13 @@ "Required": false, "UpdateType": "Mutable" }, + "Taints": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-eks-nodegroup.html#cfn-eks-nodegroup-taints", + "ItemType": "Taint", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, "Version": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-eks-nodegroup.html#cfn-eks-nodegroup-version", "PrimitiveType": "String", @@ -74523,6 +75728,357 @@ } } }, + "AWS::FinSpace::Environment": { + "Attributes": { + "AwsAccountId": { + "PrimitiveType": "String" + }, + "DedicatedServiceAccountId": { + "PrimitiveType": "String" + }, + "EnvironmentArn": { + "PrimitiveType": "String" + }, + "EnvironmentId": { + "PrimitiveType": "String" + }, + "EnvironmentUrl": { + "PrimitiveType": "String" + }, + "SageMakerStudioDomainUrl": { + "PrimitiveType": "String" + }, + "Status": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-finspace-environment.html", + "Properties": { + "Description": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-finspace-environment.html#cfn-finspace-environment-description", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "FederationMode": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-finspace-environment.html#cfn-finspace-environment-federationmode", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "FederationParameters": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-finspace-environment.html#cfn-finspace-environment-federationparameters", + "Required": false, + "Type": "FederationParameters", + "UpdateType": "Mutable" + }, + "KmsKeyId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-finspace-environment.html#cfn-finspace-environment-kmskeyid", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-finspace-environment.html#cfn-finspace-environment-name", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::FraudDetector::Detector": { + "Attributes": { + "Arn": { + "PrimitiveType": "String" + }, + "CreatedTime": { + "PrimitiveType": "String" + }, + "DetectorVersionId": { + "PrimitiveType": "String" + }, + "LastUpdatedTime": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-detector.html", + "Properties": { + "Description": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-detector.html#cfn-frauddetector-detector-description", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "DetectorId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-detector.html#cfn-frauddetector-detector-detectorid", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "DetectorVersionStatus": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-detector.html#cfn-frauddetector-detector-detectorversionstatus", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "EventType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-detector.html#cfn-frauddetector-detector-eventtype", + "Required": true, + "Type": "EventType", + "UpdateType": "Mutable" + }, + "RuleExecutionMode": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-detector.html#cfn-frauddetector-detector-ruleexecutionmode", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Rules": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-detector.html#cfn-frauddetector-detector-rules", + "DuplicatesAllowed": true, + "ItemType": "Rule", + "Required": true, + "Type": "List", + "UpdateType": "Mutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-detector.html#cfn-frauddetector-detector-tags", + "DuplicatesAllowed": true, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, + "AWS::FraudDetector::EntityType": { + "Attributes": { + "Arn": { + "PrimitiveType": "String" + }, + "CreatedTime": { + "PrimitiveType": "String" + }, + "LastUpdatedTime": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-entitytype.html", + "Properties": { + "Description": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-entitytype.html#cfn-frauddetector-entitytype-description", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-entitytype.html#cfn-frauddetector-entitytype-name", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-entitytype.html#cfn-frauddetector-entitytype-tags", + "DuplicatesAllowed": true, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, + "AWS::FraudDetector::EventType": { + "Attributes": { + "Arn": { + "PrimitiveType": "String" + }, + "CreatedTime": { + "PrimitiveType": "String" + }, + "LastUpdatedTime": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-eventtype.html", + "Properties": { + "Description": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-eventtype.html#cfn-frauddetector-eventtype-description", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "EntityTypes": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-eventtype.html#cfn-frauddetector-eventtype-entitytypes", + "DuplicatesAllowed": true, + "ItemType": "EntityType", + "Required": true, + "Type": "List", + "UpdateType": "Mutable" + }, + "EventVariables": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-eventtype.html#cfn-frauddetector-eventtype-eventvariables", + "DuplicatesAllowed": true, + "ItemType": "EventVariable", + "Required": true, + "Type": "List", + "UpdateType": "Mutable" + }, + "Labels": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-eventtype.html#cfn-frauddetector-eventtype-labels", + "DuplicatesAllowed": true, + "ItemType": "Label", + "Required": true, + "Type": "List", + "UpdateType": "Mutable" + }, + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-eventtype.html#cfn-frauddetector-eventtype-name", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-eventtype.html#cfn-frauddetector-eventtype-tags", + "DuplicatesAllowed": true, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, + "AWS::FraudDetector::Label": { + "Attributes": { + "Arn": { + "PrimitiveType": "String" + }, + "CreatedTime": { + "PrimitiveType": "String" + }, + "LastUpdatedTime": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-label.html", + "Properties": { + "Description": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-label.html#cfn-frauddetector-label-description", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-label.html#cfn-frauddetector-label-name", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-label.html#cfn-frauddetector-label-tags", + "DuplicatesAllowed": true, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, + "AWS::FraudDetector::Outcome": { + "Attributes": { + "Arn": { + "PrimitiveType": "String" + }, + "CreatedTime": { + "PrimitiveType": "String" + }, + "LastUpdatedTime": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-outcome.html", + "Properties": { + "Description": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-outcome.html#cfn-frauddetector-outcome-description", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-outcome.html#cfn-frauddetector-outcome-name", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-outcome.html#cfn-frauddetector-outcome-tags", + "DuplicatesAllowed": true, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, + "AWS::FraudDetector::Variable": { + "Attributes": { + "Arn": { + "PrimitiveType": "String" + }, + "CreatedTime": { + "PrimitiveType": "String" + }, + "LastUpdatedTime": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-variable.html", + "Properties": { + "DataSource": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-variable.html#cfn-frauddetector-variable-datasource", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "DataType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-variable.html#cfn-frauddetector-variable-datatype", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "DefaultValue": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-variable.html#cfn-frauddetector-variable-defaultvalue", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "Description": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-variable.html#cfn-frauddetector-variable-description", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-variable.html#cfn-frauddetector-variable-name", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-variable.html#cfn-frauddetector-variable-tags", + "DuplicatesAllowed": true, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "VariableType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-frauddetector-variable.html#cfn-frauddetector-variable-variabletype", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, "AWS::GameLift::Alias": { "Attributes": { "AliasId": { @@ -74637,6 +76193,13 @@ "Required": false, "UpdateType": "Immutable" }, + "Locations": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-gamelift-fleet.html#cfn-gamelift-fleet-locations", + "ItemType": "LocationConfiguration", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, "MaxSize": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-gamelift-fleet.html#cfn-gamelift-fleet-maxsize", "PrimitiveType": "Integer", @@ -74812,6 +76375,12 @@ "Type": "List", "UpdateType": "Mutable" }, + "FilterConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-gamelift-gamesessionqueue.html#cfn-gamelift-gamesessionqueue-filterconfiguration", + "Required": false, + "Type": "FilterConfiguration", + "UpdateType": "Mutable" + }, "Name": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-gamelift-gamesessionqueue.html#cfn-gamelift-gamesessionqueue-name", "PrimitiveType": "String", @@ -74831,6 +76400,12 @@ "Type": "List", "UpdateType": "Mutable" }, + "PriorityConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-gamelift-gamesessionqueue.html#cfn-gamelift-gamesessionqueue-priorityconfiguration", + "Required": false, + "Type": "PriorityConfiguration", + "UpdateType": "Mutable" + }, "TimeoutInSeconds": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-gamelift-gamesessionqueue.html#cfn-gamelift-gamesessionqueue-timeoutinseconds", "PrimitiveType": "Integer", @@ -80645,7 +82220,6 @@ }, "FileSystemConfigs": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-filesystemconfigs", - "DuplicatesAllowed": false, "ItemType": "FileSystemConfig", "Required": false, "Type": "List", @@ -80663,6 +82237,12 @@ "Required": false, "UpdateType": "Mutable" }, + "Id": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-id", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, "ImageConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-imageconfig", "Required": false, @@ -80677,7 +82257,7 @@ }, "Layers": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-layers", - "DuplicatesAllowed": false, + "DuplicatesAllowed": true, "PrimitiveItemType": "String", "Required": false, "Type": "List", @@ -80715,7 +82295,7 @@ }, "Tags": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-tags", - "DuplicatesAllowed": true, + "DuplicatesAllowed": false, "ItemType": "Tag", "Required": false, "Type": "List", @@ -93249,6 +94829,82 @@ } } }, + "AWS::XRay::Group": { + "Attributes": { + "GroupARN": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-xray-group.html", + "Properties": { + "FilterExpression": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-xray-group.html#cfn-xray-group-filterexpression", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "GroupName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-xray-group.html#cfn-xray-group-groupname", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "InsightsConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-xray-group.html#cfn-xray-group-insightsconfiguration", + "Required": false, + "Type": "InsightsConfiguration", + "UpdateType": "Mutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-xray-group.html#cfn-xray-group-tags", + "ItemType": "Json", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, + "AWS::XRay::SamplingRule": { + "Attributes": { + "RuleARN": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-xray-samplingrule.html", + "Properties": { + "RuleName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-xray-samplingrule.html#cfn-xray-samplingrule-rulename", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "SamplingRule": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-xray-samplingrule.html#cfn-xray-samplingrule-samplingrule", + "Required": false, + "Type": "SamplingRule", + "UpdateType": "Mutable" + }, + "SamplingRuleRecord": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-xray-samplingrule.html#cfn-xray-samplingrule-samplingrulerecord", + "Required": false, + "Type": "SamplingRuleRecord", + "UpdateType": "Mutable" + }, + "SamplingRuleUpdate": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-xray-samplingrule.html#cfn-xray-samplingrule-samplingruleupdate", + "Required": false, + "Type": "SamplingRuleUpdate", + "UpdateType": "Mutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-xray-samplingrule.html#cfn-xray-samplingrule-tags", + "ItemType": "Json", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, "Alexa::ASK::Skill": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ask-skill.html", "Properties": { diff --git a/packages/@aws-cdk/cfnspec/spec-source/900_CloudFront_OriginShield_patch.json b/packages/@aws-cdk/cfnspec/spec-source/900_CloudFront_OriginShield_patch.json new file mode 100644 index 0000000000000..44c5c8eb33400 --- /dev/null +++ b/packages/@aws-cdk/cfnspec/spec-source/900_CloudFront_OriginShield_patch.json @@ -0,0 +1,16 @@ +{ + "PropertyTypes": { + "AWS::CloudFront::Distribution.Origin": { + "patch": { + "description": "Add Type to OriginShield property of AWS::CloudFront::Distribution.Origin", + "operations": [ + { + "op": "add", + "path": "/Properties/OriginShield/Type", + "value": "OriginShield" + } + ] + } + } + } +} diff --git a/packages/@aws-cdk/cfnspec/spec-source/900_XRay_Tags_ItemType_patch.json b/packages/@aws-cdk/cfnspec/spec-source/900_XRay_Tags_ItemType_patch.json new file mode 100644 index 0000000000000..c504e20b59c24 --- /dev/null +++ b/packages/@aws-cdk/cfnspec/spec-source/900_XRay_Tags_ItemType_patch.json @@ -0,0 +1,27 @@ +{ + "ResourceTypes": { + "patch": { + "description": "Rename ItemType to PrimitiveItemType for Tags property of AWS::XRay Group and SamplingRule resources", + "operations": [ + { + "op": "remove", + "path": "/AWS::XRay::Group/Properties/Tags/ItemType" + }, + { + "op": "add", + "path": "/AWS::XRay::Group/Properties/Tags/PrimitiveItemType", + "value": "Json" + }, + { + "op": "remove", + "path": "/AWS::XRay::SamplingRule/Properties/Tags/ItemType" + }, + { + "op": "add", + "path": "/AWS::XRay::SamplingRule/Properties/Tags/PrimitiveItemType", + "value": "Json" + } + ] + } + } +} diff --git a/packages/@aws-cdk/cloudformation-include/package.json b/packages/@aws-cdk/cloudformation-include/package.json index 218742424f03e..36fd0bfd649a5 100644 --- a/packages/@aws-cdk/cloudformation-include/package.json +++ b/packages/@aws-cdk/cloudformation-include/package.json @@ -133,8 +133,10 @@ "@aws-cdk/aws-emrcontainers": "0.0.0", "@aws-cdk/aws-events": "0.0.0", "@aws-cdk/aws-eventschemas": "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-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", @@ -217,6 +219,7 @@ "@aws-cdk/aws-wafregional": "0.0.0", "@aws-cdk/aws-wafv2": "0.0.0", "@aws-cdk/aws-workspaces": "0.0.0", + "@aws-cdk/aws-xray": "0.0.0", "@aws-cdk/core": "0.0.0", "constructs": "^3.3.69", "yaml": "1.10.2" @@ -290,8 +293,10 @@ "@aws-cdk/aws-emrcontainers": "0.0.0", "@aws-cdk/aws-events": "0.0.0", "@aws-cdk/aws-eventschemas": "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-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", @@ -374,6 +379,7 @@ "@aws-cdk/aws-wafregional": "0.0.0", "@aws-cdk/aws-wafv2": "0.0.0", "@aws-cdk/aws-workspaces": "0.0.0", + "@aws-cdk/aws-xray": "0.0.0", "@aws-cdk/core": "0.0.0", "constructs": "^3.3.69" }, diff --git a/packages/aws-cdk-lib/package.json b/packages/aws-cdk-lib/package.json index ecc5f73614f4b..22ba72ab4eafd 100644 --- a/packages/aws-cdk-lib/package.json +++ b/packages/aws-cdk-lib/package.json @@ -192,8 +192,10 @@ "@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-finspace": "0.0.0", "@aws-cdk/aws-fis": "0.0.0", "@aws-cdk/aws-fms": "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", @@ -225,8 +227,8 @@ "@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-nodejs": "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-licensemanager": "0.0.0", "@aws-cdk/aws-logs": "0.0.0", @@ -292,6 +294,7 @@ "@aws-cdk/aws-wafregional": "0.0.0", "@aws-cdk/aws-wafv2": "0.0.0", "@aws-cdk/aws-workspaces": "0.0.0", + "@aws-cdk/aws-xray": "0.0.0", "@aws-cdk/cloud-assembly-schema": "0.0.0", "@aws-cdk/cloudformation-include": "0.0.0", "@aws-cdk/core": "0.0.0", diff --git a/packages/decdk/package.json b/packages/decdk/package.json index 210bc6ee109db..f72bb5df7e89b 100644 --- a/packages/decdk/package.json +++ b/packages/decdk/package.json @@ -109,8 +109,10 @@ "@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-finspace": "0.0.0", "@aws-cdk/aws-fis": "0.0.0", "@aws-cdk/aws-fms": "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", @@ -142,10 +144,10 @@ "@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-licensemanager": "0.0.0", - "@aws-cdk/aws-lambda-go": "0.0.0", "@aws-cdk/aws-logs": "0.0.0", "@aws-cdk/aws-logs-destinations": "0.0.0", "@aws-cdk/aws-lookoutmetrics": "0.0.0", @@ -209,6 +211,7 @@ "@aws-cdk/aws-wafregional": "0.0.0", "@aws-cdk/aws-wafv2": "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", diff --git a/packages/monocdk/package.json b/packages/monocdk/package.json index 0a5750d59db78..06b2c423cfc45 100644 --- a/packages/monocdk/package.json +++ b/packages/monocdk/package.json @@ -193,8 +193,10 @@ "@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-finspace": "0.0.0", "@aws-cdk/aws-fis": "0.0.0", "@aws-cdk/aws-fms": "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", @@ -226,8 +228,8 @@ "@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-nodejs": "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-licensemanager": "0.0.0", "@aws-cdk/aws-logs": "0.0.0", @@ -293,6 +295,7 @@ "@aws-cdk/aws-wafregional": "0.0.0", "@aws-cdk/aws-wafv2": "0.0.0", "@aws-cdk/aws-workspaces": "0.0.0", + "@aws-cdk/aws-xray": "0.0.0", "@aws-cdk/cloud-assembly-schema": "0.0.0", "@aws-cdk/cloudformation-include": "0.0.0", "@aws-cdk/core": "0.0.0", From 256fd4c6fcdbe6519bc70f62415557dbeae950a1 Mon Sep 17 00:00:00 2001 From: Otavio Macedo Date: Tue, 11 May 2021 16:15:02 +0100 Subject: [PATCH 013/134] fix(lambda-nodejs): handler filename missing from error message (#14564) ---- *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/lib/function.ts | 2 +- packages/@aws-cdk/aws-lambda-nodejs/test/function.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts b/packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts index 49e9b40b5cd5d..00e5315654796 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts @@ -155,7 +155,7 @@ function findEntry(id: string, entry?: string): string { return jsHandlerFile; } - throw new Error('Cannot find entry file.'); + throw new Error(`Cannot find handler file ${tsHandlerFile} or ${jsHandlerFile}`); } /** 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 cc4544ec6e732..83ea2131c043a 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/test/function.test.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/test/function.test.ts @@ -98,7 +98,7 @@ test('throws when entry does not exist', () => { }); test('throws when entry cannot be automatically found', () => { - expect(() => new NodejsFunction(stack, 'Fn')).toThrow(/Cannot find entry file./); + expect(() => new NodejsFunction(stack, 'Fn')).toThrow(/Cannot find handler file .*function.test.Fn.ts or .*function.test.Fn.js/); }); test('throws with the wrong runtime family', () => { From bf16642482a9264eda0a4866b5372580744fad94 Mon Sep 17 00:00:00 2001 From: Ben Chaimberg Date: Tue, 11 May 2021 14:36:56 -0700 Subject: [PATCH 014/134] chore(build): clarify prerequisites in CONTRIBUTING and verify before build (#14642) The build script at the top level of the repository performs a prerequisites check before beginning the build. This verification is not complete as it does not include the required checks for .NET and Python executables. Further, the prerequisites documentation in the contributing guide does not note the Docker requirement. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- CONTRIBUTING.md | 5 ++++- scripts/check-build-prerequisites.sh | 33 ++++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 5 deletions(-) mode change 100644 => 100755 scripts/check-build-prerequisites.sh diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0762461d541d5..6a7065377b28e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -56,6 +56,8 @@ The following tools need to be installed on your system prior to installing the - [Yarn >= 1.19.1, < 2](https://yarnpkg.com/lang/en/docs/install) - [.NET Core SDK 3.1.x](https://www.microsoft.com/net/download) - [Python >= 3.6.5, < 4.0](https://www.python.org/downloads/release/python-365/) +- [Docker >= 19.03](https://docs.docker.com/get-docker/) + - the Docker daemon must also be running First fork the repository, and then run the following commands to clone the repository locally. @@ -113,8 +115,9 @@ However, if you wish to build the the entire repository, the following command w ```console cd -yarn build +scripts/foreach.sh yarn build ``` +Note: The `foreach` command is resumable by default; you must supply `-r` or `--reset` to start a new session. You are now ready to start contributing to the CDK. See the [Pull Requests](#pull-requests) section on how to make your changes and submit it as a pull request. diff --git a/scripts/check-build-prerequisites.sh b/scripts/check-build-prerequisites.sh old mode 100644 new mode 100755 index c703d9f3e94e7..4414ff378a48d --- a/scripts/check-build-prerequisites.sh +++ b/scripts/check-build-prerequisites.sh @@ -42,7 +42,7 @@ app_v=$(node --version) # Check for version 10.*.* - 29.*.* echo -e "Checking node version... \c" if [ $(echo $app_v | grep -c -E "v[12][0-9]\.[0-9]+\.[0-9]+") -eq 1 ] -then +then # Check for version 13.0 to 13.6 if [ $(echo $app_v | grep -c -E "v13\.[0-6]\.[0-9]+") -eq 1 ] then @@ -50,7 +50,7 @@ then else # Check for version < 10.13 if [ $(echo $app_v | grep -c -E "v10\.([0-9]|1[0-2])\.[0-9]+") -eq 1 ] - then + then wrong_version else echo "Ok" @@ -68,7 +68,7 @@ check_which $app $app_min app_v=$(${app} --version) echo -e "Checking yarn version... \c" if [ $(echo $app_v | grep -c -E "1\.(19|2[0-9])\.[0-9]+") -eq 1 ] -then +then echo "Ok" else wrong_version @@ -83,9 +83,34 @@ check_which $app $app_min echo -e "Checking if docker is running... \c" docker_running=$(docker ps) if [ $? -eq 0 ] -then +then echo "Ok" else die "Docker is not running" fi +# [.NET == 3.1.x] +app="dotnet" +app_min="3.1.0" +check_which $app $app_min +app_v=$(${app} --version) +echo -e "Checking dotnet version... \c" +if [ $(echo $app_v | grep -c -E "3\.1\.[0-9]+") -eq 1 ] +then + echo "Ok" +else + wrong_version +fi + +# [Python >= 3.6.5, < 4.0] +app="python3" +app_min="3.6.5" +check_which $app $app_min +app_v=$(${app} --version) +echo -e "Checking python3 version... \c" +if [ $(echo $app_v | grep -c -E "3\.[6-9]+\.[0-9]+") -eq 1 ] +then + echo "Ok" +else + wrong_version +fi From 8a9f6bbcf2084a35c27b616fce9df58bac32596f Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Wed, 12 May 2021 02:16:26 -0700 Subject: [PATCH 015/134] chore: change `??` to `||` in check-yarn-lock.js (#14644) `??` is only supported since Node 14, but CDK should build with Node 10. Fixes #14634 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- scripts/check-yarn-lock.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/check-yarn-lock.js b/scripts/check-yarn-lock.js index 285128b3b18a6..d13e4e08cf64f 100755 --- a/scripts/check-yarn-lock.js +++ b/scripts/check-yarn-lock.js @@ -45,9 +45,9 @@ async function main() { } projects.forEach((p) => { - Object.entries(p.devDependencies ?? {}).forEach(([depName, depVersion]) => errorIfNotInYarnLock(p, depName, depVersion)); - Object.entries(p.peerDependencies ?? {}).forEach(([depName, depVersion]) => errorIfNotInYarnLock(p, depName, depVersion)); - Object.entries(p.dependencies ?? {}).forEach(([depName, depVersion]) => errorIfNotInYarnLock(p, depName, depVersion)); + Object.entries(p.devDependencies || {}).forEach(([depName, depVersion]) => errorIfNotInYarnLock(p, depName, depVersion)); + Object.entries(p.peerDependencies || {}).forEach(([depName, depVersion]) => errorIfNotInYarnLock(p, depName, depVersion)); + Object.entries(p.dependencies || {}).forEach(([depName, depVersion]) => errorIfNotInYarnLock(p, depName, depVersion)); }); } From 0ea24e95939412765c0e09133a7793557f779c76 Mon Sep 17 00:00:00 2001 From: Niranjan Jayakar Date: Wed, 12 May 2021 10:56:25 +0100 Subject: [PATCH 016/134] fix(lambda-event-sources): incorrect documented defaults for stream types (#14562) The defaults documented for `maxRecordAge` and `retryAttempts` properties for event sources that are 'stream' type were documented incorrectly. The implementation falls back to the defaults provided by CloudFormation. fixes #13908 ---- *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-event-sources/lib/stream.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda-event-sources/lib/stream.ts b/packages/@aws-cdk/aws-lambda-event-sources/lib/stream.ts index c9de62653a93e..85b9728fafbc2 100644 --- a/packages/@aws-cdk/aws-lambda-event-sources/lib/stream.ts +++ b/packages/@aws-cdk/aws-lambda-event-sources/lib/stream.ts @@ -41,7 +41,7 @@ export interface StreamEventSourceProps { * * Minimum value of 60 seconds * * Maximum value of 7 days * - * @default Duration.days(7) + * @default - the retention period configured on the stream */ readonly maxRecordAge?: Duration; @@ -51,7 +51,7 @@ export interface StreamEventSourceProps { * * Minimum value of 0 * * Maximum value of 10000 * - * @default 10000 + * @default - retry until the record expires */ readonly retryAttempts?: number; From 3698a91ac81a31f763c55487f200458d5b5eaf0f Mon Sep 17 00:00:00 2001 From: Nathanael Law Date: Wed, 12 May 2021 04:32:44 -0600 Subject: [PATCH 017/134] fix(apigatewayv2): authorizer is not removed when HttpNoneAuthorizer is used (#14424) CloudFormation will not remove an existing Authorizer if AuthorizationType and AuthorizerId are simply removed. The AuthorizationType must be explicitly set to NONE for CloudFormation to remove the existing Authorizer. As such, I updated the HttpRoute constructor to include the AuthorizationType even if it is NONE; otherwise it is impossible to remove an authorizer in CDK. Some thought had obviously gone into this previously because of the following line: https://github.com/aws/aws-cdk/blob/2f5eeb08f8790c73db7305cc7f85116e2730267d/packages/%40aws-cdk/aws-apigatewayv2/lib/http/route.ts#L159 I did not manage to track down the reasoning for this in commit comments, so I would be interested to hear why this was done, since I may have overlooked a desired use case. I'm wondering if it was assumed that the default CloudFormation value for AuthorizationType is NONE, so to have a more compact template it was omitted. However, the behavior when AuthorizationType is not present, is to not change the existing Authorizer. Basically in the CloudFormation template, ```yaml APIGETintegrationgoogleapiregister1D8736BD: Type: AWS::ApiGatewayV2::Route Properties: ApiId: Ref: API62EA1CEE RouteKey: GET /integration/google-api/register Target: ... ``` does not have the same effect as ```yaml APIGETintegrationgoogleapiregister1D8736BD: Type: AWS::ApiGatewayV2::Route Properties: ApiId: Ref: API62EA1CEE RouteKey: GET /integration/google-api/register AuthorizationType: NONE Target: ... ``` Only the later will remove an existing Authorizer. If you think this is a bug in CloudFormation and not its intended behavior, please let me know. I am assuming that they would not change the behavior anyway since that could have unintended consequence for anyone who redeploys a template without the AuthorizationType set. BREAKING CHANGE: setting the authorizer of an API route to HttpNoneAuthorizer will now remove any existing authorizer on the route ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../test/http/integ.alb.expected.json | 1 + .../test/http/integ.http-proxy.expected.json | 2 ++ .../test/http/integ.lambda-proxy.expected.json | 1 + .../test/http/integ.nlb.expected.json | 1 + .../test/http/integ.service-discovery.expected.json | 1 + packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts | 4 +--- packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts | 1 + packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts | 1 + .../test/apigateway/integ.call-http-api.expected.json | 1 + 9 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.alb.expected.json b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.alb.expected.json index ae65d49847d54..b9f5d96ff4656 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.alb.expected.json +++ b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.alb.expected.json @@ -633,6 +633,7 @@ "Ref": "HttpProxyPrivateApiA55E154D" }, "RouteKey": "$default", + "AuthorizationType": "NONE", "Target": { "Fn::Join": [ "", diff --git a/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.http-proxy.expected.json b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.http-proxy.expected.json index 378e7b2395f03..0e53d0a223e42 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.http-proxy.expected.json +++ b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.http-proxy.expected.json @@ -117,6 +117,7 @@ "Ref": "LambdaProxyApi67594471" }, "RouteKey": "$default", + "AuthorizationType": "NONE", "Target": { "Fn::Join": [ "", @@ -185,6 +186,7 @@ "Ref": "HttpProxyApiD0217C67" }, "RouteKey": "$default", + "AuthorizationType": "NONE", "Target": { "Fn::Join": [ "", diff --git a/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.lambda-proxy.expected.json b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.lambda-proxy.expected.json index 58e37b0f64e0a..7963d3534e099 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.lambda-proxy.expected.json +++ b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.lambda-proxy.expected.json @@ -117,6 +117,7 @@ "Ref": "LambdaProxyApi67594471" }, "RouteKey": "$default", + "AuthorizationType": "NONE", "Target": { "Fn::Join": [ "", diff --git a/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.nlb.expected.json b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.nlb.expected.json index aed54a5a8395c..0a3241cdc8139 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.nlb.expected.json +++ b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.nlb.expected.json @@ -598,6 +598,7 @@ "Ref": "HttpProxyPrivateApiA55E154D" }, "RouteKey": "$default", + "AuthorizationType": "NONE", "Target": { "Fn::Join": [ "", diff --git a/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.service-discovery.expected.json b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.service-discovery.expected.json index 1aaf644336b8c..00e587f8ac85f 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.service-discovery.expected.json +++ b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.service-discovery.expected.json @@ -602,6 +602,7 @@ "Ref": "HttpProxyPrivateApiA55E154D" }, "RouteKey": "$default", + "AuthorizationType": "NONE", "Target": { "Fn::Join": [ "", diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts index 2252630930c27..5178281d08953 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts @@ -156,8 +156,6 @@ export class HttpRoute extends Resource implements IHttpRoute { ])); } - const authorizationType = authBindResult?.authorizationType === HttpAuthorizerType.NONE ? undefined : authBindResult?.authorizationType; - if (authorizationScopes?.length === 0) { authorizationScopes = undefined; } @@ -167,7 +165,7 @@ export class HttpRoute extends Resource implements IHttpRoute { routeKey: props.routeKey.key, target: `integrations/${integration.integrationId}`, authorizerId: authBindResult?.authorizerId, - authorizationType, + authorizationType: authBindResult?.authorizationType ?? HttpAuthorizerType.NONE, // must be explicitly NONE (not undefined) for stack updates to work correctly authorizationScopes, }; diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts index 25b0a5bca3189..12d2c68aa0ecb 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts @@ -429,6 +429,7 @@ describe('HttpApi', () => { expect(stack).toHaveResource('AWS::ApiGatewayV2::Route', { RouteKey: 'GET /chickens', + AuthorizationType: 'NONE', AuthorizerId: ABSENT, }); }); diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts index 044d278e086e4..8de7d2ae7f1d6 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts @@ -30,6 +30,7 @@ describe('HttpRoute', () => { ], ], }, + AuthorizationType: 'NONE', }); expect(stack).toHaveResource('AWS::ApiGatewayV2::Integration', { diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/apigateway/integ.call-http-api.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/apigateway/integ.call-http-api.expected.json index 56d4889af3d55..c6a6abaa89273 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/apigateway/integ.call-http-api.expected.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/apigateway/integ.call-http-api.expected.json @@ -77,6 +77,7 @@ "Ref": "MyHttpApi8AEAAC21" }, "RouteKey": "ANY /", + "AuthorizationType": "NONE", "Target": { "Fn::Join": [ "", From c8d3128a38edb01db3f8bfcf9fc5520562509e17 Mon Sep 17 00:00:00 2001 From: AWS CDK Automation <43080478+aws-cdk-automation@users.noreply.github.com> Date: Wed, 12 May 2021 16:19:40 +0200 Subject: [PATCH 018/134] chore: npm-check-updates && yarn upgrade (#14661) Ran npm-check-updates and yarn upgrade to keep the `yarn.lock` file up-to-date. --- .../aws-codepipeline-actions/package.json | 2 +- packages/@aws-cdk/aws-finspace/package.json | 2 +- .../@aws-cdk/aws-frauddetector/package.json | 2 +- packages/@aws-cdk/aws-lambda/package.json | 2 +- packages/@aws-cdk/aws-xray/package.json | 2 +- packages/@aws-cdk/core/package.json | 2 +- packages/awslint/package.json | 4 +- tools/cdk-build-tools/package.json | 4 +- tools/eslint-plugin-cdk/package.json | 2 +- tools/pkglint/package.json | 4 +- yarn.lock | 82 +++++++++++++++---- 11 files changed, 77 insertions(+), 31 deletions(-) diff --git a/packages/@aws-cdk/aws-codepipeline-actions/package.json b/packages/@aws-cdk/aws-codepipeline-actions/package.json index 48b7c111f56f6..281edb052d5a6 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/package.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/package.json @@ -69,7 +69,7 @@ "@types/jest": "^26.0.23", "@aws-cdk/aws-cloudtrail": "0.0.0", "@aws-cdk/cx-api": "0.0.0", - "@types/lodash": "^4.14.168", + "@types/lodash": "^4.14.169", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "lodash": "^4.17.21", diff --git a/packages/@aws-cdk/aws-finspace/package.json b/packages/@aws-cdk/aws-finspace/package.json index 5cca013076363..4c3c6f007e64b 100644 --- a/packages/@aws-cdk/aws-finspace/package.json +++ b/packages/@aws-cdk/aws-finspace/package.json @@ -75,7 +75,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/jest": "^26.0.22", + "@types/jest": "^26.0.23", "@aws-cdk/assert-internal": "0.0.0", "cdk-build-tools": "0.0.0", "cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-frauddetector/package.json b/packages/@aws-cdk/aws-frauddetector/package.json index 56c7e1128fd2c..58b9f0393e614 100644 --- a/packages/@aws-cdk/aws-frauddetector/package.json +++ b/packages/@aws-cdk/aws-frauddetector/package.json @@ -75,7 +75,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/jest": "^26.0.22", + "@types/jest": "^26.0.23", "@aws-cdk/assert-internal": "0.0.0", "cdk-build-tools": "0.0.0", "cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-lambda/package.json b/packages/@aws-cdk/aws-lambda/package.json index 3fdc26ada0d5e..6279fa92e0518 100644 --- a/packages/@aws-cdk/aws-lambda/package.json +++ b/packages/@aws-cdk/aws-lambda/package.json @@ -77,7 +77,7 @@ "devDependencies": { "@types/jest": "^26.0.23", "@types/aws-lambda": "^8.10.76", - "@types/lodash": "^4.14.168", + "@types/lodash": "^4.14.169", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-xray/package.json b/packages/@aws-cdk/aws-xray/package.json index 7efedfd8af300..baaafffc7e70e 100644 --- a/packages/@aws-cdk/aws-xray/package.json +++ b/packages/@aws-cdk/aws-xray/package.json @@ -75,7 +75,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/jest": "^26.0.22", + "@types/jest": "^26.0.23", "@aws-cdk/assert-internal": "0.0.0", "cdk-build-tools": "0.0.0", "cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/core/package.json b/packages/@aws-cdk/core/package.json index af2e803a1f7f8..c926741368a48 100644 --- a/packages/@aws-cdk/core/package.json +++ b/packages/@aws-cdk/core/package.json @@ -177,7 +177,7 @@ "@types/aws-lambda": "^8.10.76", "@types/fs-extra": "^8.1.1", "@types/jest": "^26.0.23", - "@types/lodash": "^4.14.168", + "@types/lodash": "^4.14.169", "@types/minimatch": "^3.0.4", "@types/node": "^10.17.59", "@types/sinon": "^9.0.11", diff --git a/packages/awslint/package.json b/packages/awslint/package.json index dab312dc46ea0..6b91a94c8349d 100644 --- a/packages/awslint/package.json +++ b/packages/awslint/package.json @@ -29,8 +29,8 @@ "@types/yargs": "^15.0.13", "pkglint": "0.0.0", "typescript": "~3.9.9", - "@typescript-eslint/eslint-plugin": "^4.22.1", - "@typescript-eslint/parser": "^4.22.1", + "@typescript-eslint/eslint-plugin": "^4.23.0", + "@typescript-eslint/parser": "^4.23.0", "eslint": "^7.26.0", "eslint-import-resolver-node": "^0.3.4", "eslint-import-resolver-typescript": "^2.4.0", diff --git a/tools/cdk-build-tools/package.json b/tools/cdk-build-tools/package.json index 994b0248e9882..8e31468544319 100644 --- a/tools/cdk-build-tools/package.json +++ b/tools/cdk-build-tools/package.json @@ -40,8 +40,8 @@ "pkglint": "0.0.0" }, "dependencies": { - "@typescript-eslint/eslint-plugin": "^4.22.1", - "@typescript-eslint/parser": "^4.22.1", + "@typescript-eslint/eslint-plugin": "^4.23.0", + "@typescript-eslint/parser": "^4.23.0", "awslint": "0.0.0", "colors": "^1.4.0", "eslint": "^7.26.0", diff --git a/tools/eslint-plugin-cdk/package.json b/tools/eslint-plugin-cdk/package.json index 0d07037e93385..562f31110ad8d 100644 --- a/tools/eslint-plugin-cdk/package.json +++ b/tools/eslint-plugin-cdk/package.json @@ -21,7 +21,7 @@ "typescript": "~3.9.9" }, "dependencies": { - "@typescript-eslint/parser": "^4.22.1", + "@typescript-eslint/parser": "^4.23.0", "eslint": "^7.26.0", "fs-extra": "^9.1.0" }, diff --git a/tools/pkglint/package.json b/tools/pkglint/package.json index 21c682d12b91d..0fd91fcb0d2a0 100644 --- a/tools/pkglint/package.json +++ b/tools/pkglint/package.json @@ -40,8 +40,8 @@ "@types/jest": "^26.0.23", "@types/semver": "^7.3.5", "@types/yargs": "^15.0.13", - "@typescript-eslint/eslint-plugin": "^4.22.1", - "@typescript-eslint/parser": "^4.22.1", + "@typescript-eslint/eslint-plugin": "^4.23.0", + "@typescript-eslint/parser": "^4.23.0", "eslint": "^7.26.0", "eslint-import-resolver-node": "^0.3.4", "eslint-import-resolver-typescript": "^2.4.0", diff --git a/yarn.lock b/yarn.lock index cd9959d6a69fe..0be97e7c4e3ba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1529,10 +1529,10 @@ dependencies: jszip "*" -"@types/lodash@^4.14.168": - version "4.14.168" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.168.tgz#fe24632e79b7ade3f132891afff86caa5e5ce008" - integrity sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q== +"@types/lodash@^4.14.169": + version "4.14.169" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.169.tgz#83c217688f07a4d9ef8f28a3ebd1d318f6ff4cbb" + integrity sha512-DvmZHoHTFJ8zhVYwCLWbQ7uAbYQEk52Ev2/ZiQ7Y7gQGeV9pjBqjnQpECMHfKS1rCYAhMI7LHVxwyZLZinJgdw== "@types/md5@^2.3.0": version "2.3.0" @@ -1692,13 +1692,13 @@ resolved "https://registry.yarnpkg.com/@types/yarnpkg__lockfile/-/yarnpkg__lockfile-1.1.4.tgz#445251eb00bd9c1e751f82c7c6bf4f714edfd464" integrity sha512-/emrKCfQMQmFCqRqqBJ0JueHBT06jBRM3e8OgnvDUcvuExONujIk2hFA5dNsN9Nt41ljGVDdChvCydATZ+KOZw== -"@typescript-eslint/eslint-plugin@^4.22.1": - version "4.22.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.22.1.tgz#6bcdbaa4548553ab861b4e5f34936ead1349a543" - integrity sha512-kVTAghWDDhsvQ602tHBc6WmQkdaYbkcTwZu+7l24jtJiYvm9l+/y/b2BZANEezxPDiX5MK2ZecE+9BFi/YJryw== +"@typescript-eslint/eslint-plugin@^4.23.0": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.23.0.tgz#29d3c9c81f6200b1fd6d8454cfb007ba176cde80" + integrity sha512-tGK1y3KIvdsQEEgq6xNn1DjiFJtl+wn8JJQiETtCbdQxw1vzjXyAaIkEmO2l6Nq24iy3uZBMFQjZ6ECf1QdgGw== dependencies: - "@typescript-eslint/experimental-utils" "4.22.1" - "@typescript-eslint/scope-manager" "4.22.1" + "@typescript-eslint/experimental-utils" "4.23.0" + "@typescript-eslint/scope-manager" "4.23.0" debug "^4.1.1" functional-red-black-tree "^1.0.1" lodash "^4.17.15" @@ -1706,7 +1706,19 @@ semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@4.22.1", "@typescript-eslint/experimental-utils@^4.0.1": +"@typescript-eslint/experimental-utils@4.23.0": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.23.0.tgz#f2059434cd6e5672bfeab2fb03b7c0a20622266f" + integrity sha512-WAFNiTDnQfrF3Z2fQ05nmCgPsO5o790vOhmWKXbbYQTO9erE1/YsFot5/LnOUizLzU2eeuz6+U/81KV5/hFTGA== + dependencies: + "@types/json-schema" "^7.0.3" + "@typescript-eslint/scope-manager" "4.23.0" + "@typescript-eslint/types" "4.23.0" + "@typescript-eslint/typescript-estree" "4.23.0" + eslint-scope "^5.0.0" + eslint-utils "^2.0.0" + +"@typescript-eslint/experimental-utils@^4.0.1": version "4.22.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.22.1.tgz#3938a5c89b27dc9a39b5de63a62ab1623ab27497" integrity sha512-svYlHecSMCQGDO2qN1v477ax/IDQwWhc7PRBiwAdAMJE7GXk5stF4Z9R/8wbRkuX/5e9dHqbIWxjeOjckK3wLQ== @@ -1718,14 +1730,14 @@ eslint-scope "^5.0.0" eslint-utils "^2.0.0" -"@typescript-eslint/parser@^4.22.1": - version "4.22.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.22.1.tgz#a95bda0fd01d994a15fc3e99dc984294f25c19cc" - integrity sha512-l+sUJFInWhuMxA6rtirzjooh8cM/AATAe3amvIkqKFeMzkn85V+eLzb1RyuXkHak4dLfYzOmF6DXPyflJvjQnw== +"@typescript-eslint/parser@^4.23.0": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.23.0.tgz#239315d38e42e852bef43a4b0b01bef78f78911c" + integrity sha512-wsvjksHBMOqySy/Pi2Q6UuIuHYbgAMwLczRl4YanEPKW5KVxI9ZzDYh3B5DtcZPQTGRWFJrfcbJ6L01Leybwug== dependencies: - "@typescript-eslint/scope-manager" "4.22.1" - "@typescript-eslint/types" "4.22.1" - "@typescript-eslint/typescript-estree" "4.22.1" + "@typescript-eslint/scope-manager" "4.23.0" + "@typescript-eslint/types" "4.23.0" + "@typescript-eslint/typescript-estree" "4.23.0" debug "^4.1.1" "@typescript-eslint/scope-manager@4.22.1": @@ -1736,11 +1748,24 @@ "@typescript-eslint/types" "4.22.1" "@typescript-eslint/visitor-keys" "4.22.1" +"@typescript-eslint/scope-manager@4.23.0": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.23.0.tgz#8792ef7eacac122e2ec8fa2d30a59b8d9a1f1ce4" + integrity sha512-ZZ21PCFxPhI3n0wuqEJK9omkw51wi2bmeKJvlRZPH5YFkcawKOuRMQMnI8mH6Vo0/DoHSeZJnHiIx84LmVQY+w== + dependencies: + "@typescript-eslint/types" "4.23.0" + "@typescript-eslint/visitor-keys" "4.23.0" + "@typescript-eslint/types@4.22.1": version "4.22.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.22.1.tgz#bf99c6cec0b4a23d53a61894816927f2adad856a" integrity sha512-2HTkbkdAeI3OOcWbqA8hWf/7z9c6gkmnWNGz0dKSLYLWywUlkOAQ2XcjhlKLj5xBFDf8FgAOF5aQbnLRvgNbCw== +"@typescript-eslint/types@4.23.0": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.23.0.tgz#da1654c8a5332f4d1645b2d9a1c64193cae3aa3b" + integrity sha512-oqkNWyG2SLS7uTWLZf6Sr7Dm02gA5yxiz1RP87tvsmDsguVATdpVguHr4HoGOcFOpCvx9vtCSCyQUGfzq28YCw== + "@typescript-eslint/typescript-estree@4.22.1": version "4.22.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.22.1.tgz#dca379eead8cdfd4edc04805e83af6d148c164f9" @@ -1754,6 +1779,19 @@ semver "^7.3.2" tsutils "^3.17.1" +"@typescript-eslint/typescript-estree@4.23.0": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.23.0.tgz#0753b292097523852428a6f5a1aa8ccc1aae6cd9" + integrity sha512-5Sty6zPEVZF5fbvrZczfmLCOcby3sfrSPu30qKoY1U3mca5/jvU5cwsPb/CO6Q3ByRjixTMIVsDkqwIxCf/dMw== + dependencies: + "@typescript-eslint/types" "4.23.0" + "@typescript-eslint/visitor-keys" "4.23.0" + debug "^4.1.1" + globby "^11.0.1" + is-glob "^4.0.1" + semver "^7.3.2" + tsutils "^3.17.1" + "@typescript-eslint/visitor-keys@4.22.1": version "4.22.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.22.1.tgz#6045ae25a11662c671f90b3a403d682dfca0b7a6" @@ -1762,6 +1800,14 @@ "@typescript-eslint/types" "4.22.1" eslint-visitor-keys "^2.0.0" +"@typescript-eslint/visitor-keys@4.23.0": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.23.0.tgz#7215cc977bd3b4ef22467b9023594e32f9e4e455" + integrity sha512-5PNe5cmX9pSifit0H+nPoQBXdbNzi5tOEec+3riK+ku4e3er37pKxMKDH5Ct5Y4fhWxcD4spnlYjxi9vXbSpwg== + dependencies: + "@typescript-eslint/types" "4.23.0" + eslint-visitor-keys "^2.0.0" + "@yarnpkg/lockfile@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" From 010a6b1a14f14be5001779644df3d3a2e27d4e71 Mon Sep 17 00:00:00 2001 From: Nick Lynch Date: Wed, 12 May 2021 15:58:49 +0100 Subject: [PATCH 019/134] feat(cloudwatch): time range support for GraphWidget (#14659) The `setPeriodToTimeRange` property affects number (SingleValue), bar, and pie charts. If set, it displays all data points in the time range in the bar/pie chart, instead of only the most recent value. Support for this property for `SingleValueWidget` was introduced way back in #4649, but was never added to `GraphWidget`. ---- *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/lib/graph.ts | 13 +++++++- ...teg.math-alarm-and-dashboard.expected.json | 20 ++++++++++++- .../test/integ.math-alarm-and-dashboard.ts | 7 +++++ .../aws-cloudwatch/test/test.graphs.ts | 30 ++++++++++++++++++- 4 files changed, 67 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/graph.ts b/packages/@aws-cdk/aws-cloudwatch/lib/graph.ts index 98f8980db4f4b..bc6407a52f8e6 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/graph.ts +++ b/packages/@aws-cdk/aws-cloudwatch/lib/graph.ts @@ -213,6 +213,16 @@ export interface GraphWidgetProps extends MetricWidgetProps { * @default TimeSeries */ readonly view?: GraphWidgetView; + + /** + * Whether to show the value from the entire time range. Only applicable for Bar and Pie charts. + * + * If false, values will be from the most recent period of your chosen time range; + * if true, shows the value from the entire time range. + * + * @default false + */ + readonly setPeriodToTimeRange?: boolean; } /** @@ -276,6 +286,7 @@ export class GraphWidget extends ConcreteWidget { }, legend: this.props.legendPosition !== undefined ? { position: this.props.legendPosition } : undefined, liveData: this.props.liveData, + setPeriodToTimeRange: this.props.setPeriodToTimeRange, }, }]; } @@ -447,4 +458,4 @@ function mapAnnotation(yAxis: string): ((x: HorizontalAnnotation) => any) { return (a: HorizontalAnnotation) => { return { ...a, yAxis }; }; -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-cloudwatch/test/integ.math-alarm-and-dashboard.expected.json b/packages/@aws-cdk/aws-cloudwatch/test/integ.math-alarm-and-dashboard.expected.json index 8e9b235bb2b65..9d6d0b5b1c4bc 100644 --- a/packages/@aws-cdk/aws-cloudwatch/test/integ.math-alarm-and-dashboard.expected.json +++ b/packages/@aws-cdk/aws-cloudwatch/test/integ.math-alarm-and-dashboard.expected.json @@ -116,7 +116,25 @@ "QueueName" ] }, - "\",{\"label\":\"NotVisible Messages\",\"period\":30,\"yAxis\":\"right\"}]],\"annotations\":{\"horizontal\":[{\"label\":\"Total Messages >= 100 for 3 datapoints within 3 minutes\",\"value\":100,\"yAxis\":\"left\"}]},\"yAxis\":{}}},{\"type\":\"metric\",\"width\":6,\"height\":3,\"x\":0,\"y\":12,\"properties\":{\"view\":\"singleValue\",\"title\":\"Current total messages in queue\",\"region\":\"", + "\",{\"label\":\"NotVisible Messages\",\"period\":30,\"yAxis\":\"right\"}]],\"annotations\":{\"horizontal\":[{\"label\":\"Total Messages >= 100 for 3 datapoints within 3 minutes\",\"value\":100,\"yAxis\":\"left\"}]},\"yAxis\":{}}},{\"type\":\"metric\",\"width\":6,\"height\":6,\"x\":0,\"y\":12,\"properties\":{\"view\":\"pie\",\"title\":\"Percentage of messages in each queue as pie chart\",\"region\":\"", + { + "Ref": "AWS::Region" + }, + "\",\"metrics\":[[\"AWS/SQS\",\"ApproximateNumberOfMessagesVisible\",\"QueueName\",\"", + { + "Fn::GetAtt": [ + "queue", + "QueueName" + ] + }, + "\",{\"label\":\"Visible Messages\",\"period\":10}],[\"AWS/SQS\",\"ApproximateNumberOfMessagesNotVisible\",\"QueueName\",\"", + { + "Fn::GetAtt": [ + "queue", + "QueueName" + ] + }, + "\",{\"label\":\"NotVisible Messages\",\"period\":30}]],\"yAxis\":{},\"setPeriodToTimeRange\":true}},{\"type\":\"metric\",\"width\":6,\"height\":3,\"x\":0,\"y\":18,\"properties\":{\"view\":\"singleValue\",\"title\":\"Current total messages in queue\",\"region\":\"", { "Ref": "AWS::Region" }, diff --git a/packages/@aws-cdk/aws-cloudwatch/test/integ.math-alarm-and-dashboard.ts b/packages/@aws-cdk/aws-cloudwatch/test/integ.math-alarm-and-dashboard.ts index 9de88e6bc729a..5a3285d873fe8 100644 --- a/packages/@aws-cdk/aws-cloudwatch/test/integ.math-alarm-and-dashboard.ts +++ b/packages/@aws-cdk/aws-cloudwatch/test/integ.math-alarm-and-dashboard.ts @@ -59,6 +59,13 @@ dashboard.addWidgets(new cloudwatch.GraphWidget({ leftAnnotations: [alarm.toAnnotation()], })); +dashboard.addWidgets(new cloudwatch.GraphWidget({ + title: 'Percentage of messages in each queue as pie chart', + left: [metricA, metricB], + view: cloudwatch.GraphWidgetView.PIE, + setPeriodToTimeRange: true, +})); + dashboard.addWidgets(new cloudwatch.SingleValueWidget({ title: 'Current total messages in queue', metrics: [sumExpression], diff --git a/packages/@aws-cdk/aws-cloudwatch/test/test.graphs.ts b/packages/@aws-cdk/aws-cloudwatch/test/test.graphs.ts index 7e306e2bf0691..e6420bbec1955 100644 --- a/packages/@aws-cdk/aws-cloudwatch/test/test.graphs.ts +++ b/packages/@aws-cdk/aws-cloudwatch/test/test.graphs.ts @@ -660,4 +660,32 @@ export = { test.done(); }, -}; \ No newline at end of file + + 'add setPeriodToTimeRange to GraphWidget'(test: Test) { + // GIVEN + const stack = new Stack(); + const widget = new GraphWidget({ + left: [new Metric({ namespace: 'CDK', metricName: 'Test' })], + view: GraphWidgetView.PIE, + setPeriodToTimeRange: true, + }); + + // THEN + test.deepEqual(stack.resolve(widget.toJson()), [{ + type: 'metric', + width: 6, + height: 6, + properties: { + view: 'pie', + region: { Ref: 'AWS::Region' }, + metrics: [ + ['CDK', 'Test'], + ], + yAxis: {}, + setPeriodToTimeRange: true, + }, + }]); + + test.done(); + }, +}; From 85e00faf1e3bcc32c2f7aa881d42c6d1f6c17f63 Mon Sep 17 00:00:00 2001 From: Florian Eitel Date: Wed, 12 May 2021 17:25:29 +0200 Subject: [PATCH 020/134] feat(secretsmanager): Automatically grant permissions to rotation Lambda (#14471) When you use the AWS Secrets Manager console to configure rotation for a secret for one of the fully supported databases, the console configures almost all parameters for you. But if you create a function or opt to do anything manually for other reasons, you also might have to manually configure the permissions for that part of the rotation. https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-required-permissions.html ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-secretsmanager/README.md | 2 + .../lib/rotation-schedule.ts | 30 ++++++++ .../test/rotation-schedule.test.ts | 69 +++++++++++++++++++ 3 files changed, 101 insertions(+) diff --git a/packages/@aws-cdk/aws-secretsmanager/README.md b/packages/@aws-cdk/aws-secretsmanager/README.md index 0c0f45828e9ef..81c8e3c5e8e24 100644 --- a/packages/@aws-cdk/aws-secretsmanager/README.md +++ b/packages/@aws-cdk/aws-secretsmanager/README.md @@ -87,6 +87,8 @@ secret.addRotationSchedule('RotationSchedule', { }); ``` +Note: The required permissions for Lambda to call SecretsManager and the other way round are automatically granted based on [AWS Documentation](https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-required-permissions.html) as long as the Lambda is not imported. + See [Overview of the Lambda Rotation Function](https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html) on how to implement a Lambda Rotation Function. ### Using a Hosted Lambda Function diff --git a/packages/@aws-cdk/aws-secretsmanager/lib/rotation-schedule.ts b/packages/@aws-cdk/aws-secretsmanager/lib/rotation-schedule.ts index 1243976963386..7e00492f2cb2f 100644 --- a/packages/@aws-cdk/aws-secretsmanager/lib/rotation-schedule.ts +++ b/packages/@aws-cdk/aws-secretsmanager/lib/rotation-schedule.ts @@ -1,4 +1,5 @@ import * as ec2 from '@aws-cdk/aws-ec2'; +import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; import { Duration, Resource, Stack } from '@aws-cdk/core'; import { Construct } from 'constructs'; @@ -70,6 +71,35 @@ export class RotationSchedule extends Resource { throw new Error('One of `rotationLambda` or `hostedRotation` must be specified.'); } + if (props.rotationLambda?.permissionsNode.defaultChild) { + props.rotationLambda.grantInvoke(new iam.ServicePrincipal('secretsmanager.amazonaws.com')); + + props.rotationLambda.addToRolePolicy( + new iam.PolicyStatement({ + actions: [ + 'secretsmanager:DescribeSecret', + 'secretsmanager:GetSecretValue', + 'secretsmanager:PutSecretValue', + 'secretsmanager:UpdateSecretVersionStage', + ], + resources: [props.secret.secretArn], + conditions: { + StringEquals: { + 'secretsmanager:resource/AllowRotationLambdaArn': props.rotationLambda.functionArn, + }, + }, + }), + ); + props.rotationLambda.addToRolePolicy( + new iam.PolicyStatement({ + actions: [ + 'secretsmanager:GetRandomPassword', + ], + resources: ['*'], + }), + ); + } + new CfnRotationSchedule(this, 'Resource', { secretId: props.secret.secretArn, rotationLambdaArn: props.rotationLambda?.functionArn, diff --git a/packages/@aws-cdk/aws-secretsmanager/test/rotation-schedule.test.ts b/packages/@aws-cdk/aws-secretsmanager/test/rotation-schedule.test.ts index e77336732d51b..3ab3422cd265b 100644 --- a/packages/@aws-cdk/aws-secretsmanager/test/rotation-schedule.test.ts +++ b/packages/@aws-cdk/aws-secretsmanager/test/rotation-schedule.test.ts @@ -41,6 +41,75 @@ test('create a rotation schedule with a rotation Lambda', () => { }); }); +test('assign permissions for rotation schedule with a rotation Lambda', () => { + // GIVEN + const secret = new secretsmanager.Secret(stack, 'Secret'); + const rotationLambda = new lambda.Function(stack, 'Lambda', { + runtime: lambda.Runtime.NODEJS_10_X, + code: lambda.Code.fromInline('export.handler = event => event;'), + handler: 'index.handler', + }); + + // WHEN + new secretsmanager.RotationSchedule(stack, 'RotationSchedule', { + secret, + rotationLambda, + }); + + // THEN + expect(stack).toHaveResource('AWS::Lambda::Permission', { + Action: 'lambda:InvokeFunction', + FunctionName: { + 'Fn::GetAtt': [ + 'LambdaD247545B', + 'Arn', + ], + }, + Principal: 'secretsmanager.amazonaws.com', + }); + + expect(stack).toHaveResource('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: [ + 'secretsmanager:DescribeSecret', + 'secretsmanager:GetSecretValue', + 'secretsmanager:PutSecretValue', + 'secretsmanager:UpdateSecretVersionStage', + ], + Effect: 'Allow', + Resource: { + Ref: 'SecretA720EF05', + }, + Condition: { + StringEquals: { + 'secretsmanager:resource/AllowRotationLambdaArn': { + 'Fn::GetAtt': [ + 'LambdaD247545B', + 'Arn', + ], + }, + }, + }, + }, + { + Action: 'secretsmanager:GetRandomPassword', + Effect: 'Allow', + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'LambdaServiceRoleDefaultPolicyDAE46E21', + Roles: [ + { + Ref: 'LambdaServiceRoleA8ED4D3B', + }, + ], + }); +}); + describe('hosted rotation', () => { test('single user not in a vpc', () => { // GIVEN From c1136a3ff2d911f3e42e23069e9d25577dfebbb7 Mon Sep 17 00:00:00 2001 From: Hsing-Hui Hsu Date: Wed, 12 May 2021 11:02:40 -0700 Subject: [PATCH 021/134] docs(ecs-service-extensions): fix README (#14646) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../ecs-service-extensions/README.md | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/@aws-cdk-containers/ecs-service-extensions/README.md b/packages/@aws-cdk-containers/ecs-service-extensions/README.md index da884a7aa85bd..1bf5a63a39fc5 100644 --- a/packages/@aws-cdk-containers/ecs-service-extensions/README.md +++ b/packages/@aws-cdk-containers/ecs-service-extensions/README.md @@ -61,19 +61,19 @@ const nameService = new Service(stack, 'name', { ## Creating an `Environment` An `Environment` is a place to deploy your services. You can have multiple environments -on a single AWS account. For example you could create a `test` environment as well -as a `production` environment so you have a place to verify that you application +on a single AWS account. For example, you could create a `test` environment as well +as a `production` environment so you have a place to verify that your application works as intended before you deploy it to a live environment. -Each environment is isolated from other environments. In specific -by default when you create an environment the construct supplies its own VPC, +Each environment is isolated from other environments. In other words, +when you create an environment, by default the construct supplies its own VPC, ECS Cluster, and any other required resources for the environment: ```ts const environment = new Environment(stack, 'production'); ``` -However, you can also choose to build an environment out of a pre-existing VPC, +However, you can also choose to build an environment out of a pre-existing VPC or ECS Cluster: ```ts @@ -89,7 +89,7 @@ const environment = new Environment(stack, 'production', { ## Defining your `ServiceDescription` The `ServiceDescription` defines what application you want the service to run and -what optional extensions you want to add to the service. The most basic form of a `ServiceExtension` looks like this: +what optional extensions you want to add to the service. The most basic form of a `ServiceDescription` looks like this: ```ts const nameDescription = new ServiceDescription(); @@ -105,9 +105,9 @@ nameDescription.add(new Container({ ``` Every `ServiceDescription` requires at minimum that you add a `Container` extension -which defines the main application container to run for the service. +which defines the main application (essential) container to run for the service. -After that you can optionally enable additional features for the service using the `ServiceDescription.add()` method: +After that, you can optionally enable additional features for the service using the `ServiceDescription.add()` method: ```ts nameDescription.add(new AppMeshExtension({ mesh })); @@ -238,7 +238,7 @@ frontend.connectTo(backend); The address that a service will use to talk to another service depends on the type of ingress that has been created by the extension that did the connecting. -For example if an App Mesh extension has been used then the service is accessible +For example, if an App Mesh extension has been used, then the service is accessible at a DNS address of `.`. For example: ```ts @@ -280,7 +280,7 @@ const backend = new Service(stack, 'backend', { frontend.connectTo(backend); ``` -The above code uses the well known service discovery name for each +The above code uses the well-known service discovery name for each service, and passes it as an environment variable to the container so that the container knows what address to use when communicating to the other service. From c1060ef3eec9d1bccad9b37839c78ad0b6905b36 Mon Sep 17 00:00:00 2001 From: Madeline Kusters <80541297+madeline-k@users.noreply.github.com> Date: Wed, 12 May 2021 12:07:40 -0700 Subject: [PATCH 022/134] chore: add madeline-k to .mergify.yml (#14666) --- .mergify.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mergify.yml b/.mergify.yml index 55bba382d2784..81cce822cc2b6 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -6,7 +6,7 @@ pull_request_rules: label: add: [ contribution/core ] conditions: - - author~=^(eladb|RomainMuller|garnaat|nija-at|skinny85|rix0rrr|NGL321|Jerry-AWS|MrArnoldPalmer|NetaNir|iliapolo|njlynch|ericzbeard|ccfife|fulghum|pkandasamy91|SoManyHs|uttarasridhar|otaviomacedo|BenChaimberg)$ + - author~=^(eladb|RomainMuller|garnaat|nija-at|skinny85|rix0rrr|NGL321|Jerry-AWS|MrArnoldPalmer|NetaNir|iliapolo|njlynch|ericzbeard|ccfife|fulghum|pkandasamy91|SoManyHs|uttarasridhar|otaviomacedo|BenChaimberg|madeline-k)$ - -label~="contribution/core" - name: automatic merge actions: From 114f7ccdaf736988834fe2be487363a992a31369 Mon Sep 17 00:00:00 2001 From: "Michael S. Fischer" Date: Wed, 12 May 2021 13:49:46 -0700 Subject: [PATCH 023/134] feat(ecs): add support for EC2 Capacity Providers (#14386) Add support for EC2 Capacity Providers. I tried to make the UX reasonably decent without disrupting existing stacks. There are some new types being created here, including `ICapacityProvider` (interface type) and its concrete type `EC2CapacityProvider` (which is a class users can instantiate). There are also a couple of singleton classes for `FargateCapacityProvider` and `FargateSpotCapacityProvider`. Closes https://github.com/aws/aws-cdk/issues/5471 ---- *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/README.md | 121 ++-- packages/@aws-cdk/aws-ecs/lib/cluster.ts | 222 +++++- .../@aws-cdk/aws-ecs/test/cluster.test.ts | 207 +++++- .../aws-ecs/test/ec2/ec2-service.test.ts | 53 ++ .../ec2/integ.capacity-provider.expected.json | 655 ++++++++++++++++++ .../test/ec2/integ.capacity-provider.ts | 46 ++ .../test/fargate/fargate-service.test.ts | 91 ++- 7 files changed, 1325 insertions(+), 70 deletions(-) create mode 100644 packages/@aws-cdk/aws-ecs/test/ec2/integ.capacity-provider.expected.json create mode 100644 packages/@aws-cdk/aws-ecs/test/ec2/integ.capacity-provider.ts diff --git a/packages/@aws-cdk/aws-ecs/README.md b/packages/@aws-cdk/aws-ecs/README.md index 7f0338ed3ff18..5e63747de137f 100644 --- a/packages/@aws-cdk/aws-ecs/README.md +++ b/packages/@aws-cdk/aws-ecs/README.md @@ -14,16 +14,13 @@ This package contains constructs for working with **Amazon Elastic Container Service** (Amazon ECS). -Amazon ECS is a highly scalable, fast, container management service -that makes it easy to run, stop, -and manage Docker containers on a cluster of Amazon EC2 instances. +Amazon Elastic Container Service (Amazon ECS) is a fully managed container orchestration service. For further information on Amazon ECS, see the [Amazon ECS documentation](https://docs.aws.amazon.com/ecs) -The following example creates an Amazon ECS cluster, -adds capacity to it, -and instantiates the Amazon ECS Service with an automatic load balancer. +The following example creates an Amazon ECS cluster, adds capacity to it, and +runs a service on it: ```ts import * as ecs from '@aws-cdk/aws-ecs'; @@ -496,38 +493,6 @@ scaling.scaleOnRequestCount('RequestScaling', { Task auto-scaling is powered by *Application Auto-Scaling*. See that section for details. -## Instance Auto-Scaling - -If you're running on AWS Fargate, AWS manages the physical machines that your -containers are running on for you. If you're running an Amazon ECS cluster however, -your Amazon EC2 instances might fill up as your number of Tasks goes up. - -To avoid placement errors, configure auto-scaling for your -Amazon EC2 instance group so that your instance count scales with demand. To keep -your Amazon EC2 instances halfway loaded, scaling up to a maximum of 30 instances -if required: - -```ts -const autoScalingGroup = cluster.addCapacity('DefaultAutoScalingGroup', { - instanceType: new ec2.InstanceType("t2.xlarge"), - minCapacity: 3, - maxCapacity: 30, - desiredCapacity: 3, - - // Give instances 5 minutes to drain running tasks when an instance is - // terminated. This is the default, turn this off by specifying 0 or - // change the timeout up to 900 seconds. - taskDrainTime: Duration.seconds(300) -}); - -autoScalingGroup.scaleOnCpuUtilization('KeepCpuHalfwayLoaded', { - targetUtilizationPercent: 50 -}); -``` - -See the `@aws-cdk/aws-autoscaling` library for more autoscaling options -you can configure on your instances. - ## Integration with CloudWatch Events To start an Amazon ECS task on an Amazon EC2-backed Cluster, instantiate an @@ -760,25 +725,24 @@ ecsService.associateCloudMapService({ ## Capacity Providers -Currently, only `FARGATE` and `FARGATE_SPOT` capacity providers are supported. - -To enable capacity providers on your cluster, set the `capacityProviders` field -to [`FARGATE`, `FARGATE_SPOT`]. Then, specify capacity provider strategies on -the `capacityProviderStrategies` field for your Fargate Service. - -```ts -import * as cdk from '@aws-cdk/core'; -import * as ec2 from '@aws-cdk/aws-ec2'; -import * as ecs from '../../lib'; +There are two major families of Capacity Providers: [AWS +Fargate](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/fargate-capacity-providers.html) +(including Fargate Spot) and EC2 [Auto Scaling +Group](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/asg-capacity-providers.html) +Capacity Providers. Both are supported. -const app = new cdk.App(); -const stack = new cdk.Stack(app, 'aws-ecs-integ-capacity-provider'); +### Fargate Capacity Providers -const vpc = new ec2.Vpc(stack, 'Vpc', { maxAzs: 2 }); +To enable Fargate capacity providers, you can either set +`enableFargateCapacityProviders` to `true` when creating your cluster, or by +invoking the `enableFargateCapacityProviders()` method after creating your +cluster. This will add both `FARGATE` and `FARGATE_SPOT` as available capacity +providers on your cluster. +```ts const cluster = new ecs.Cluster(stack, 'FargateCPCluster', { vpc, - capacityProviders: ['FARGATE', 'FARGATE_SPOT'], + enableFargateCapacityProviders: true, }); const taskDefinition = new ecs.FargateTaskDefinition(stack, 'TaskDef'); @@ -801,8 +765,57 @@ new ecs.FargateService(stack, 'FargateService', { } ], }); +``` + +### Auto Scaling Group Capacity Providers + +To add an Auto Scaling Group Capacity Provider, first create an EC2 Auto Scaling +Group. Then, create an `AsgCapacityProvider` and pass the Auto Scaling Group to +it in the constructor. Then add the Capacity Provider to the cluster. Finally, +you can refer to the Provider by its name in your service's or task's Capacity +Provider strategy. + +By default, an Auto Scaling Group Capacity Provider will manage the Auto Scaling +Group's size for you. It will also enable managed termination protection, in +order to prevent EC2 Auto Scaling from terminating EC2 instances that have tasks +running on them. If you want to disable this behavior, set both +`enableManagedScaling` to and `enableManagedTerminationProtection` to `false`. -app.synth(); +```ts +const cluster = new ecs.Cluster(stack, 'Cluster', { + vpc, +}); + +const autoScalingGroup = new autoscaling.AutoScalingGroup(stack, 'ASG', { + vpc, + instanceType: new ec2.InstanceType('t2.micro'), + machineImage: ecs.EcsOptimizedImage.amazonLinux2(), + minCapacity: 0, + maxCapacity: 100, +}); + +const capacityProvider = new ecs.AsgCapacityProvider(stack, 'AsgCapacityProvider', { + autoScalingGroup, +}); +cluster.addAsgCapacityProvider(capacityProvider); + +const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef'); + +taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample', + memoryReservationMiB: 256, +}); + +new ecs.Ec2Service(stack, 'EC2Service', { + cluster, + taskDefinition, + capacityProviderStrategies: [ + { + capacityProvider: capacityProvider.capacityProviderName, + weight: 1, + } + ], +}); ``` ## Elastic Inference Accelerators @@ -810,7 +823,7 @@ app.synth(); Currently, this feature is only supported for services with EC2 launch types. To add elastic inference accelerators to your EC2 instance, first add -`inferenceAccelerators` field to the EC2TaskDefinition and set the `deviceName` +`inferenceAccelerators` field to the Ec2TaskDefinition and set the `deviceName` and `deviceType` properties. ```ts diff --git a/packages/@aws-cdk/aws-ecs/lib/cluster.ts b/packages/@aws-cdk/aws-ecs/lib/cluster.ts index a65efaa83e17e..294a88fcb1858 100644 --- a/packages/@aws-cdk/aws-ecs/lib/cluster.ts +++ b/packages/@aws-cdk/aws-ecs/lib/cluster.ts @@ -5,11 +5,11 @@ import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; import * as cloudmap from '@aws-cdk/aws-servicediscovery'; import * as ssm from '@aws-cdk/aws-ssm'; -import { Duration, Lazy, IResource, Resource, Stack } from '@aws-cdk/core'; +import { Duration, Lazy, IResource, Resource, Stack, Aspects, IAspect, IConstruct } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { InstanceDrainHook } from './drain-hook/instance-drain-hook'; import { ECSMetrics } from './ecs-canned-metrics.generated'; -import { CfnCluster } from './ecs.generated'; +import { CfnCluster, CfnCapacityProvider, CfnClusterCapacityProviderAssociations } from './ecs.generated'; // v2 - keep this import as a separate section to reduce merge conflict when forward merging with the v2 branch. // eslint-disable-next-line @@ -52,9 +52,17 @@ export interface ClusterProps { * The capacity providers to add to the cluster * * @default - None. Currently only FARGATE and FARGATE_SPOT are supported. + * @deprecated Use {@link ClusterProps.enableFargateCapacityProviders} instead. */ readonly capacityProviders?: string[]; + /** + * Whether to enable Fargate Capacity Providers + * + * @default false + */ + readonly enableFargateCapacityProviders?: boolean; + /** * If true CloudWatch Container Insights will be enabled for the cluster * @@ -109,11 +117,14 @@ export class Cluster extends Resource implements ICluster { public readonly clusterName: string; /** - * The capacity providers associated with the cluster. - * - * Currently only FARGATE and FARGATE_SPOT are supported. + * The cluster-level (FARGATE, FARGATE_SPOT) capacity providers. */ - private _capacityProviders: string[] = []; + private _fargateCapacityProviders: string[] = []; + + /** + * The EC2 Auto Scaling Group capacity providers associated with the cluster. + */ + private _asgCapacityProviders: AsgCapacityProvider[] = []; /** * The AWS Cloud Map namespace to associate with the cluster. @@ -148,12 +159,15 @@ export class Cluster extends Resource implements ICluster { clusterSettings = [{ name: 'containerInsights', value: props.containerInsights ? ContainerInsights.ENABLED : ContainerInsights.DISABLED }]; } - this._capacityProviders = props.capacityProviders ?? []; + this._fargateCapacityProviders = props.capacityProviders ?? []; + if (props.enableFargateCapacityProviders) { + this.enableFargateCapacityProviders(); + } const cluster = new CfnCluster(this, 'Resource', { clusterName: this.physicalName, clusterSettings, - capacityProviders: Lazy.list({ produce: () => this._capacityProviders }, { omitEmpty: true }), + capacityProviders: Lazy.list({ produce: () => this._fargateCapacityProviders }, { omitEmpty: true }), }); this.clusterArn = this.getResourceArnAttribute(cluster.attrArn, { @@ -173,6 +187,24 @@ export class Cluster extends Resource implements ICluster { this._autoscalingGroup = props.capacity !== undefined ? this.addCapacity('DefaultAutoScalingGroup', props.capacity) : undefined; + + // Only create cluster capacity provider associations if there are any EC2 + // capacity providers. Ordinarily we'd just add the construct to the tree + // since it's harmless, but we'd prefer not to add unexpected new + // resources to the stack which could surprise users working with + // brown-field CDK apps and stacks. + Aspects.of(this).add(new MaybeCreateCapacityProviderAssociations(this, id, this._asgCapacityProviders)); + } + + /** + * Enable the Fargate capacity providers for this cluster. + */ + public enableFargateCapacityProviders() { + for (const provider of ['FARGATE', 'FARGATE_SPOT']) { + if (!this._fargateCapacityProviders.includes(provider)) { + this._fargateCapacityProviders.push(provider); + } + } } /** @@ -214,6 +246,8 @@ export class Cluster extends Resource implements ICluster { * This method adds compute capacity to a cluster by creating an AutoScalingGroup with the specified options. * * Returns the AutoScalingGroup so you can add autoscaling settings to it. + * + * @deprecated Use {@link Cluster.addAsgCapacityProvider} instead. */ public addCapacity(id: string, options: AddCapacityOptions): autoscaling.AutoScalingGroup { if (options.machineImage && options.machineImageType) { @@ -238,9 +272,31 @@ export class Cluster extends Resource implements ICluster { return autoScalingGroup; } + /** + * This method adds an Auto Scaling Group Capacity Provider to a cluster. + * + * @param provider the capacity provider to add to this cluster. + */ + public addAsgCapacityProvider(provider: AsgCapacityProvider, options: AddAutoScalingGroupCapacityOptions = {}) { + // Don't add the same capacity provider more than once. + if (this._asgCapacityProviders.includes(provider)) { + return; + } + + this._hasEc2Capacity = true; + this.configureAutoScalingGroup(provider.autoScalingGroup, { + ...options, + // Don't enable the instance-draining lifecycle hook if managed termination protection is enabled + taskDrainTime: provider.enableManagedTerminationProtection ? Duration.seconds(0) : options.taskDrainTime, + }); + + this._asgCapacityProviders.push(provider); + } + /** * This method adds compute capacity to a cluster using the specified AutoScalingGroup. * + * @deprecated Use {@link Cluster.addAsgCapacityProvider} instead. * @param autoScalingGroup the ASG to add to this cluster. * [disable-awslint:ref-via-interface] is needed in order to install the ECS * agent by updating the ASGs user data. @@ -248,8 +304,11 @@ export class Cluster extends Resource implements ICluster { public addAutoScalingGroup(autoScalingGroup: autoscaling.AutoScalingGroup, options: AddAutoScalingGroupCapacityOptions = {}) { this._hasEc2Capacity = true; this.connections.connections.addSecurityGroup(...autoScalingGroup.connections.securityGroups); + this.configureAutoScalingGroup(autoScalingGroup, options); + } - if ( autoScalingGroup.osType === ec2.OperatingSystemType.WINDOWS ) { + private configureAutoScalingGroup(autoScalingGroup: autoscaling.AutoScalingGroup, options: AddAutoScalingGroupCapacityOptions = {}) { + if (autoScalingGroup.osType === ec2.OperatingSystemType.WINDOWS) { this.configureWindowsAutoScalingGroup(autoScalingGroup, options); } else { // Tie instances to cluster @@ -342,17 +401,19 @@ export class Cluster extends Resource implements ICluster { } /** - * addCapacityProvider adds the name of a capacityProvider to the list of supproted capacityProviders for a cluster. + * This method enables the Fargate or Fargate Spot capacity providers on the cluster. * * @param provider the capacity provider to add to this cluster. + * @deprecated Use {@link enableFargateCapacityProviders} instead. + * @see {@link addAsgCapacityProvider} to add an Auto Scaling Group capacity provider to the cluster. */ public addCapacityProvider(provider: string) { if (!(provider === 'FARGATE' || provider === 'FARGATE_SPOT')) { throw new Error('CapacityProvider not supported'); } - if (!this._capacityProviders.includes(provider)) { - this._capacityProviders.push(provider); + if (!this._fargateCapacityProviders.includes(provider)) { + this._fargateCapacityProviders.push(provider); } } @@ -859,6 +920,7 @@ export interface AddAutoScalingGroupCapacityOptions { * * Set to 0 to disable task draining. * + * @deprecated The lifecycle draining hook is not configured if using the EC2 Capacity Provider. Enable managed termination protection instead. * @default Duration.minutes(5) */ readonly taskDrainTime?: Duration; @@ -975,7 +1037,7 @@ enum ContainerInsights { */ export interface CapacityProviderStrategy { /** - * The name of the Capacity Provider. Currently only FARGATE and FARGATE_SPOT are supported. + * The name of the capacity provider. */ readonly capacityProvider: string; @@ -997,3 +1059,137 @@ capacity provider. The weight value is taken into consideration after the base v */ readonly weight?: number; } + +/** + * The options for creating an Auto Scaling Group Capacity Provider. + */ +export interface AsgCapacityProviderProps extends AddAutoScalingGroupCapacityOptions { + /** + * The name for the capacity provider. + * + * @default CloudFormation-generated name + */ + readonly capacityProviderName?: string; + + /** + * The autoscaling group to add as a Capacity Provider. + */ + readonly autoScalingGroup: autoscaling.IAutoScalingGroup; + + /** + * Whether to enable managed scaling + * + * @default true + */ + readonly enableManagedScaling?: boolean; + + /** + * Whether to enable managed termination protection + * + * @default true + */ + readonly enableManagedTerminationProtection?: boolean; + + /** + * Maximum scaling step size. In most cases this should be left alone. + * + * @default 1000 + */ + readonly maximumScalingStepSize?: number; + + /** + * Minimum scaling step size. In most cases this should be left alone. + * + * @default 1 + */ + readonly minimumScalingStepSize?: number; + + /** + * Target capacity percent. In most cases this should be left alone. + * + * @default 100 + */ + readonly targetCapacityPercent?: number; +} + +/** + * An Auto Scaling Group Capacity Provider. This allows an ECS cluster to target + * a specific EC2 Auto Scaling Group for the placement of tasks. Optionally (and + * recommended), ECS can manage the number of instances in the ASG to fit the + * tasks, and can ensure that instances are not prematurely terminated while + * there are still tasks running on them. + */ +export class AsgCapacityProvider extends CoreConstruct { + /** + * Capacity provider name + * @default Chosen by CloudFormation + */ + readonly capacityProviderName: string; + + /** + * Auto Scaling Group + */ + readonly autoScalingGroup: autoscaling.AutoScalingGroup; + + /** + * Whether managed termination protection is enabled + */ + readonly enableManagedTerminationProtection?: boolean; + + constructor(scope: Construct, id: string, props: AsgCapacityProviderProps) { + super(scope, id); + + this.autoScalingGroup = props.autoScalingGroup as autoscaling.AutoScalingGroup; + + this.enableManagedTerminationProtection = + props.enableManagedTerminationProtection === undefined ? true : props.enableManagedTerminationProtection; + + if (this.enableManagedTerminationProtection) { + this.autoScalingGroup.protectNewInstancesFromScaleIn(); + } + + const capacityProvider = new CfnCapacityProvider(this, id, { + name: props.capacityProviderName, + autoScalingGroupProvider: { + autoScalingGroupArn: this.autoScalingGroup.autoScalingGroupName, + managedScaling: props.enableManagedScaling === false ? undefined : { + status: 'ENABLED', + targetCapacity: props.targetCapacityPercent || 100, + maximumScalingStepSize: props.maximumScalingStepSize, + minimumScalingStepSize: props.minimumScalingStepSize, + }, + managedTerminationProtection: this.enableManagedTerminationProtection ? 'ENABLED' : 'DISABLED', + }, + }); + + this.capacityProviderName = capacityProvider.ref; + } +} + +/** + * A visitor that adds a capacity provider association to a Cluster only if + * the caller created any EC2 Capacity Providers. + */ +class MaybeCreateCapacityProviderAssociations implements IAspect { + private scope: CoreConstruct; + private id: string; + private capacityProviders: AsgCapacityProvider[] + + constructor(scope: CoreConstruct, id: string, capacityProviders: AsgCapacityProvider[]) { + this.scope = scope; + this.id = id; + this.capacityProviders = capacityProviders; + } + public visit(node: IConstruct): void { + if (node instanceof Cluster) { + const providers = this.capacityProviders.map(p => p.capacityProviderName).filter(p => p !== 'FARGATE' && p !== 'FARGATE_SPOT'); + if (providers.length > 0) { + new CfnClusterCapacityProviderAssociations(this.scope, this.id, { + cluster: node.clusterName, + defaultCapacityProviderStrategy: [], + capacityProviders: Lazy.list({ produce: () => providers }), + }); + } + } + } +} diff --git a/packages/@aws-cdk/aws-ecs/test/cluster.test.ts b/packages/@aws-cdk/aws-ecs/test/cluster.test.ts index 713338284e57b..a76c98377c1db 100644 --- a/packages/@aws-cdk/aws-ecs/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/cluster.test.ts @@ -4,7 +4,9 @@ import { haveResource, haveResourceLike, ResourcePart, + ABSENT, } from '@aws-cdk/assert-internal'; +import * as autoscaling from '@aws-cdk/aws-autoscaling'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as kms from '@aws-cdk/aws-kms'; import * as cloudmap from '@aws-cdk/aws-servicediscovery'; @@ -1695,7 +1697,7 @@ nodeunitShim({ test.done(); }, - 'allows specifying capacityProviders'(test: Test) { + 'allows specifying capacityProviders (deprecated)'(test: Test) { // GIVEN const app = new cdk.App(); const stack = new cdk.Stack(app, 'test'); @@ -1711,6 +1713,59 @@ nodeunitShim({ test.done(); }, + 'allows specifying Fargate capacityProviders'(test: Test) { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'test'); + + // WHEN + new ecs.Cluster(stack, 'EcsCluster', { + enableFargateCapacityProviders: true, + }); + + // THEN + expect(stack).to(haveResource('AWS::ECS::Cluster', { + CapacityProviders: ['FARGATE', 'FARGATE_SPOT'], + })); + + test.done(); + }, + + 'allows specifying capacityProviders (alternate method)'(test: Test) { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'test'); + + // WHEN + const cluster = new ecs.Cluster(stack, 'EcsCluster'); + cluster.enableFargateCapacityProviders(); + + // THEN + expect(stack).to(haveResource('AWS::ECS::Cluster', { + CapacityProviders: ['FARGATE', 'FARGATE_SPOT'], + })); + + test.done(); + }, + + 'allows adding capacityProviders post-construction (deprecated)'(test: Test) { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'test'); + const cluster = new ecs.Cluster(stack, 'EcsCluster'); + + // WHEN + cluster.addCapacityProvider('FARGATE'); + cluster.addCapacityProvider('FARGATE'); // does not add twice + + // THEN + expect(stack).to(haveResource('AWS::ECS::Cluster', { + CapacityProviders: ['FARGATE'], + })); + + test.done(); + }, + 'allows adding capacityProviders post-construction'(test: Test) { // GIVEN const app = new cdk.App(); @@ -1742,4 +1797,154 @@ nodeunitShim({ test.done(); }, + + 'creates ASG capacity providers with expected defaults'(test: Test) { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'test'); + const vpc = new ec2.Vpc(stack, 'Vpc'); + const autoScalingGroup = new autoscaling.AutoScalingGroup(stack, 'asg', { + vpc, + instanceType: new ec2.InstanceType('bogus'), + machineImage: ecs.EcsOptimizedImage.amazonLinux2(), + }); + + // WHEN + new ecs.AsgCapacityProvider(stack, 'provider', { + autoScalingGroup, + }); + + // THEN + expect(stack).to(haveResource('AWS::ECS::CapacityProvider', { + AutoScalingGroupProvider: { + AutoScalingGroupArn: { + Ref: 'asgASG4D014670', + }, + ManagedScaling: { + Status: 'ENABLED', + TargetCapacity: 100, + }, + ManagedTerminationProtection: 'ENABLED', + }, + })); + test.done(); + }, + + 'can disable managed scaling for ASG capacity provider'(test: Test) { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'test'); + const vpc = new ec2.Vpc(stack, 'Vpc'); + const autoScalingGroup = new autoscaling.AutoScalingGroup(stack, 'asg', { + vpc, + instanceType: new ec2.InstanceType('bogus'), + machineImage: ecs.EcsOptimizedImage.amazonLinux2(), + }); + + // WHEN + new ecs.AsgCapacityProvider(stack, 'provider', { + autoScalingGroup, + enableManagedScaling: false, + }); + + // THEN + expect(stack).to(haveResource('AWS::ECS::CapacityProvider', { + AutoScalingGroupProvider: { + AutoScalingGroupArn: { + Ref: 'asgASG4D014670', + }, + ManagedScaling: ABSENT, + ManagedTerminationProtection: 'ENABLED', + }, + })); + test.done(); + }, + + 'capacity provider enables ASG new instance scale-in protection by default'(test: Test) { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'test'); + const vpc = new ec2.Vpc(stack, 'Vpc'); + const autoScalingGroup = new autoscaling.AutoScalingGroup(stack, 'asg', { + vpc, + instanceType: new ec2.InstanceType('bogus'), + machineImage: ecs.EcsOptimizedImage.amazonLinux2(), + }); + + // WHEN + new ecs.AsgCapacityProvider(stack, 'provider', { + autoScalingGroup, + }); + + // THEN + expect(stack).to(haveResource('AWS::AutoScaling::AutoScalingGroup', { + NewInstancesProtectedFromScaleIn: true, + })); + test.done(); + }, + + 'capacity provider disables ASG new instance scale-in protection'(test: Test) { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'test'); + const vpc = new ec2.Vpc(stack, 'Vpc'); + const autoScalingGroup = new autoscaling.AutoScalingGroup(stack, 'asg', { + vpc, + instanceType: new ec2.InstanceType('bogus'), + machineImage: ecs.EcsOptimizedImage.amazonLinux2(), + }); + + // WHEN + new ecs.AsgCapacityProvider(stack, 'provider', { + autoScalingGroup, + enableManagedTerminationProtection: false, + }); + + // THEN + expect(stack).notTo(haveResource('AWS::AutoScaling::AutoScalingGroup', { + NewInstancesProtectedFromScaleIn: true, + })); + test.done(); + }, + + 'can add ASG capacity via Capacity Provider'(test: Test) { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'test'); + const vpc = new ec2.Vpc(stack, 'Vpc'); + const cluster = new ecs.Cluster(stack, 'EcsCluster'); + + const autoScalingGroup = new autoscaling.AutoScalingGroup(stack, 'asg', { + vpc, + instanceType: new ec2.InstanceType('bogus'), + machineImage: ecs.EcsOptimizedImage.amazonLinux2(), + }); + + // WHEN + const capacityProvider = new ecs.AsgCapacityProvider(stack, 'provider', { + autoScalingGroup, + enableManagedTerminationProtection: false, + }); + + // These should not be added at the association level + cluster.enableFargateCapacityProviders(); + + // Ensure not added twice + cluster.addAsgCapacityProvider(capacityProvider); + cluster.addAsgCapacityProvider(capacityProvider); + + // THEN + expect(stack).to(haveResource('AWS::ECS::ClusterCapacityProviderAssociations', { + Cluster: { + Ref: 'EcsCluster97242B84', + }, + CapacityProviders: [ + { + Ref: 'providerD3FF4D3A', + }, + ], + DefaultCapacityProviderStrategy: [], + })); + test.done(); + }, }); 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 f6d6a47efc6c3..c63c86cce9f65 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,4 +1,5 @@ import { expect, haveResource, haveResourceLike } from '@aws-cdk/assert-internal'; +import * as autoscaling from '@aws-cdk/aws-autoscaling'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as elb from '@aws-cdk/aws-elasticloadbalancing'; import * as elbv2 from '@aws-cdk/aws-elasticloadbalancingv2'; @@ -236,6 +237,58 @@ nodeunitShim({ test.done(); }, + 'with autoscaling group capacity provider'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'Vpc'); + const cluster = new ecs.Cluster(stack, 'EcsCluster'); + + const autoScalingGroup = new autoscaling.AutoScalingGroup(stack, 'asg', { + vpc, + instanceType: new ec2.InstanceType('bogus'), + machineImage: ecs.EcsOptimizedImage.amazonLinux2(), + }); + + // WHEN + const capacityProvider = new ecs.AsgCapacityProvider(stack, 'provider', { + autoScalingGroup, + enableManagedTerminationProtection: false, + }); + cluster.addAsgCapacityProvider(capacityProvider); + + const taskDefinition = new ecs.TaskDefinition(stack, 'ServerTask', { + compatibility: ecs.Compatibility.EC2, + }); + taskDefinition.addContainer('app', { + image: new ecs.RepositoryImage('bogus'), + cpu: 1024, + memoryReservationMiB: 900, + portMappings: [{ + containerPort: 80, + }], + }); + new ecs.Ec2Service(stack, 'Service', { + cluster, + taskDefinition, + desiredCount: 0, + capacityProviderStrategies: [{ + capacityProvider: capacityProvider.capacityProviderName, + }], + }); + + // THEN + expect(stack).to(haveResource('AWS::ECS::Service', { + CapacityProviderStrategy: [ + { + CapacityProvider: { + Ref: 'providerD3FF4D3A', + }, + }, + ], + })); + test.done(); + }, + 'with multiple security groups, it correctly updates the cfn template'(test: Test) { // GIVEN const stack = new cdk.Stack(); diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.capacity-provider.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.capacity-provider.expected.json new file mode 100644 index 0000000000000..d4e008750fe93 --- /dev/null +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.capacity-provider.expected.json @@ -0,0 +1,655 @@ +{ + "Resources": { + "Vpc8378EB38": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/Vpc" + } + ] + } + }, + "VpcPublicSubnet1Subnet5C2D37C4": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.0.0/18", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1RouteTable6C95E38E": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1RouteTableAssociation97140677": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + } + } + }, + "VpcPublicSubnet1DefaultRoute3DA9E72A": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VpcIGWD7BA715C" + } + }, + "DependsOn": [ + "VpcVPCGWBF912B6E" + ] + }, + "VpcPublicSubnet1EIPD7E02669": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1NATGateway4D7517AA": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet1EIPD7E02669", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet2Subnet691E08A3": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.64.0/18", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1b", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2RouteTable94F7E489": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2RouteTableAssociationDD5762D8": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + }, + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + } + } + }, + "VpcPublicSubnet2DefaultRoute97F91067": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VpcIGWD7BA715C" + } + }, + "DependsOn": [ + "VpcVPCGWBF912B6E" + ] + }, + "VpcPublicSubnet2EIP3C605A87": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2NATGateway9182C01D": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet2EIP3C605A87", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + }, + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPrivateSubnet1Subnet536B997A": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.128.0/18", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/Vpc/PrivateSubnet1" + } + ] + } + }, + "VpcPrivateSubnet1RouteTableB2C5B500": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/Vpc/PrivateSubnet1" + } + ] + } + }, + "VpcPrivateSubnet1RouteTableAssociation70C59FA6": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "SubnetId": { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + } + }, + "VpcPrivateSubnet1DefaultRouteBE02A9ED": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VpcPublicSubnet1NATGateway4D7517AA" + } + } + }, + "VpcPrivateSubnet2Subnet3788AAA1": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.192.0/18", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1b", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/Vpc/PrivateSubnet2" + } + ] + } + }, + "VpcPrivateSubnet2RouteTableA678073B": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/Vpc/PrivateSubnet2" + } + ] + } + }, + "VpcPrivateSubnet2RouteTableAssociationA89CAD56": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + }, + "SubnetId": { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + } + }, + "VpcPrivateSubnet2DefaultRoute060D2087": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VpcPublicSubnet2NATGateway9182C01D" + } + } + }, + "VpcIGWD7BA715C": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/Vpc" + } + ] + } + }, + "VpcVPCGWBF912B6E": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "InternetGatewayId": { + "Ref": "VpcIGWD7BA715C" + } + } + }, + "EC2CPClusterD5F0FD32": { + "Type": "AWS::ECS::Cluster" + }, + "EC2CPCluster4CFED4DD": { + "Type": "AWS::ECS::ClusterCapacityProviderAssociations", + "Properties": { + "CapacityProviders": [ + { + "Ref": "EC2CapacityProvider5A2E35CD" + } + ], + "Cluster": { + "Ref": "EC2CPClusterD5F0FD32" + }, + "DefaultCapacityProviderStrategy": [] + } + }, + "TaskDefTaskRole1EDB4A67": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "TaskDef54694570": { + "Type": "AWS::ECS::TaskDefinition", + "Properties": { + "ContainerDefinitions": [ + { + "Essential": true, + "Image": "amazon/amazon-ecs-sample", + "MemoryReservation": 256, + "Name": "web" + } + ], + "Family": "integec2capacityproviderTaskDefA6140A6B", + "NetworkMode": "bridge", + "RequiresCompatibilities": [ + "EC2" + ], + "TaskRoleArn": { + "Fn::GetAtt": [ + "TaskDefTaskRole1EDB4A67", + "Arn" + ] + } + } + }, + "ASGInstanceSecurityGroup0525485D": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "integ-ec2-capacity-provider/ASG/InstanceSecurityGroup", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/ASG" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "ASGInstanceRoleE263A41B": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": { + "Fn::Join": [ + "", + [ + "ec2.", + { + "Ref": "AWS::URLSuffix" + } + ] + ] + } + } + } + ], + "Version": "2012-10-17" + }, + "Tags": [ + { + "Key": "Name", + "Value": "integ-ec2-capacity-provider/ASG" + } + ] + } + }, + "ASGInstanceRoleDefaultPolicy7636D8BF": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ecs:DeregisterContainerInstance", + "ecs:RegisterContainerInstance", + "ecs:Submit*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "EC2CPClusterD5F0FD32", + "Arn" + ] + } + }, + { + "Action": [ + "ecs:Poll", + "ecs:StartTelemetrySession" + ], + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EC2CPClusterD5F0FD32", + "Arn" + ] + } + } + }, + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "ecs:DiscoverPollEndpoint", + "ecr:GetAuthorizationToken", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "ASGInstanceRoleDefaultPolicy7636D8BF", + "Roles": [ + { + "Ref": "ASGInstanceRoleE263A41B" + } + ] + } + }, + "ASGInstanceProfile0A2834D7": { + "Type": "AWS::IAM::InstanceProfile", + "Properties": { + "Roles": [ + { + "Ref": "ASGInstanceRoleE263A41B" + } + ] + } + }, + "ASGLaunchConfigC00AF12B": { + "Type": "AWS::AutoScaling::LaunchConfiguration", + "Properties": { + "ImageId": { + "Ref": "SsmParameterValueawsserviceecsoptimizedamiamazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter" + }, + "InstanceType": "t2.micro", + "IamInstanceProfile": { + "Ref": "ASGInstanceProfile0A2834D7" + }, + "SecurityGroups": [ + { + "Fn::GetAtt": [ + "ASGInstanceSecurityGroup0525485D", + "GroupId" + ] + } + ], + "UserData": { + "Fn::Base64": { + "Fn::Join": [ + "", + [ + "#!/bin/bash\necho ECS_CLUSTER=", + { + "Ref": "EC2CPClusterD5F0FD32" + }, + " >> /etc/ecs/ecs.config\nsudo iptables --insert FORWARD 1 --in-interface docker+ --destination 169.254.169.254/32 --jump DROP\nsudo service iptables save\necho ECS_AWSVPC_BLOCK_IMDS=true >> /etc/ecs/ecs.config" + ] + ] + } + } + }, + "DependsOn": [ + "ASGInstanceRoleDefaultPolicy7636D8BF", + "ASGInstanceRoleE263A41B" + ] + }, + "ASG46ED3070": { + "Type": "AWS::AutoScaling::AutoScalingGroup", + "Properties": { + "MaxSize": "1", + "MinSize": "1", + "LaunchConfigurationName": { + "Ref": "ASGLaunchConfigC00AF12B" + }, + "NewInstancesProtectedFromScaleIn": true, + "Tags": [ + { + "Key": "Name", + "PropagateAtLaunch": true, + "Value": "integ-ec2-capacity-provider/ASG" + } + ], + "VPCZoneIdentifier": [ + { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + }, + { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + ] + }, + "UpdatePolicy": { + "AutoScalingScheduledAction": { + "IgnoreUnmodifiedGroupSizeProperties": true + } + } + }, + "EC2CapacityProvider5A2E35CD": { + "Type": "AWS::ECS::CapacityProvider", + "Properties": { + "AutoScalingGroupProvider": { + "AutoScalingGroupArn": { + "Ref": "ASG46ED3070" + }, + "ManagedScaling": { + "Status": "ENABLED", + "TargetCapacity": 100 + }, + "ManagedTerminationProtection": "ENABLED" + } + } + }, + "EC2Service5392EF94": { + "Type": "AWS::ECS::Service", + "Properties": { + "CapacityProviderStrategy": [ + { + "CapacityProvider": { + "Ref": "EC2CapacityProvider5A2E35CD" + }, + "Weight": 1 + } + ], + "Cluster": { + "Ref": "EC2CPClusterD5F0FD32" + }, + "DeploymentConfiguration": { + "MaximumPercent": 200, + "MinimumHealthyPercent": 50 + }, + "EnableECSManagedTags": false, + "SchedulingStrategy": "REPLICA", + "TaskDefinition": { + "Ref": "TaskDef54694570" + } + } + } + }, + "Parameters": { + "SsmParameterValueawsserviceecsoptimizedamiamazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.capacity-provider.ts b/packages/@aws-cdk/aws-ecs/test/ec2/integ.capacity-provider.ts new file mode 100644 index 0000000000000..f82ce6a9f9f56 --- /dev/null +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.capacity-provider.ts @@ -0,0 +1,46 @@ +import * as autoscaling from '@aws-cdk/aws-autoscaling'; +import * as ec2 from '@aws-cdk/aws-ec2'; +import * as cdk from '@aws-cdk/core'; +import * as ecs from '../../lib'; + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'integ-ec2-capacity-provider'); + +const vpc = new ec2.Vpc(stack, 'Vpc', { maxAzs: 2 }); + +const cluster = new ecs.Cluster(stack, 'EC2CPCluster', { + vpc, +}); + +const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef'); + +taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + memoryReservationMiB: 256, +}); + +const autoScalingGroup = new autoscaling.AutoScalingGroup(stack, 'ASG', { + vpc, + instanceType: new ec2.InstanceType('t2.micro'), + machineImage: ecs.EcsOptimizedImage.amazonLinux2(), +}); + +const cp = new ecs.AsgCapacityProvider(stack, 'EC2CapacityProvider', { + autoScalingGroup, +}); + +cluster.addAsgCapacityProvider(cp); + +new ecs.Ec2Service(stack, 'EC2Service', { + cluster, + taskDefinition, + capacityProviderStrategies: [ + { + capacityProvider: cp.capacityProviderName, + weight: 1, + }, + ], +}); + +app.synth(); + 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 0bdbeac0c04d5..a75ff256cc457 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,4 +1,4 @@ -import { expect, haveResource, haveResourceLike } from '@aws-cdk/assert-internal'; +import { expect, haveResource, haveResourceLike, ABSENT } from '@aws-cdk/assert-internal'; import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import * as ec2 from '@aws-cdk/aws-ec2'; @@ -110,7 +110,7 @@ nodeunitShim({ test.done(); }, - 'does not set launchType when capacity provider strategies specified'(test: Test) { + 'does not set launchType when capacity provider strategies specified (deprecated)'(test: Test) { // GIVEN const stack = new cdk.Stack(); const vpc = new ec2.Vpc(stack, 'MyVpc', {}); @@ -195,6 +195,93 @@ nodeunitShim({ test.done(); }, + 'does not set launchType when capacity provider strategies specified'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { + vpc, + }); + cluster.enableFargateCapacityProviders(); + + const taskDefinition = new ecs.FargateTaskDefinition(stack, 'FargateTaskDef'); + + const container = taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + memoryLimitMiB: 512, + }); + container.addPortMappings({ containerPort: 8000 }); + + new ecs.FargateService(stack, 'FargateService', { + cluster, + taskDefinition, + capacityProviderStrategies: [ + { + capacityProvider: 'FARGATE_SPOT', + weight: 2, + }, + { + capacityProvider: 'FARGATE', + weight: 1, + }, + ], + }); + + // THEN + expect(stack).to(haveResource('AWS::ECS::Cluster', { + CapacityProviders: ['FARGATE', 'FARGATE_SPOT'], + })); + + expect(stack).to(haveResource('AWS::ECS::Service', { + TaskDefinition: { + Ref: 'FargateTaskDefC6FB60B4', + }, + Cluster: { + Ref: 'EcsCluster97242B84', + }, + DeploymentConfiguration: { + MaximumPercent: 200, + MinimumHealthyPercent: 50, + }, + // no launch type + LaunchType: ABSENT, + CapacityProviderStrategy: [ + { + CapacityProvider: 'FARGATE_SPOT', + Weight: 2, + }, + { + CapacityProvider: 'FARGATE', + Weight: 1, + }, + ], + EnableECSManagedTags: false, + NetworkConfiguration: { + AwsvpcConfiguration: { + AssignPublicIp: 'DISABLED', + SecurityGroups: [ + { + 'Fn::GetAtt': [ + 'FargateServiceSecurityGroup0A0E79CB', + 'GroupId', + ], + }, + ], + Subnets: [ + { + Ref: 'MyVpcPrivateSubnet1Subnet5057CF7E', + }, + { + Ref: 'MyVpcPrivateSubnet2Subnet0040C983', + }, + ], + }, + }, + })); + + test.done(); + }, + 'with custom cloudmap namespace'(test: Test) { // GIVEN const stack = new cdk.Stack(); From 0df9f9badb18629a9f60870b5a18b8793a00f5e3 Mon Sep 17 00:00:00 2001 From: Peter Woodworth <44349620+peterwoodworth@users.noreply.github.com> Date: Wed, 12 May 2021 15:27:42 -0700 Subject: [PATCH 024/134] chore: remove fixed region in python sample app (#14667) Sourced from guidance issue #14577 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../lib/init-templates/v1/sample-app/python/app.template.py | 2 +- .../lib/init-templates/v2/sample-app/python/app.template.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/aws-cdk/lib/init-templates/v1/sample-app/python/app.template.py b/packages/aws-cdk/lib/init-templates/v1/sample-app/python/app.template.py index 580bbee9e069c..808bc22af32e4 100644 --- a/packages/aws-cdk/lib/init-templates/v1/sample-app/python/app.template.py +++ b/packages/aws-cdk/lib/init-templates/v1/sample-app/python/app.template.py @@ -6,6 +6,6 @@ app = core.App() -%name.PascalCased%Stack(app, "%name.StackName%", env={'region': 'us-west-2'}) +%name.PascalCased%Stack(app, "%name.StackName%") app.synth() diff --git a/packages/aws-cdk/lib/init-templates/v2/sample-app/python/app.template.py b/packages/aws-cdk/lib/init-templates/v2/sample-app/python/app.template.py index 1f64e6a67bc56..49094e6711cbf 100644 --- a/packages/aws-cdk/lib/init-templates/v2/sample-app/python/app.template.py +++ b/packages/aws-cdk/lib/init-templates/v2/sample-app/python/app.template.py @@ -6,6 +6,6 @@ app = cdk.App() -%name.PascalCased%Stack(app, "%name.StackName%", env={'region': 'us-west-2'}) +%name.PascalCased%Stack(app, "%name.StackName%") app.synth() From 2337b5d965028ba06d6ff72f991c0b8e46433a8f Mon Sep 17 00:00:00 2001 From: Bryan Pan Date: Wed, 12 May 2021 17:56:23 -0700 Subject: [PATCH 025/134] feat(appsync): elasticsearch data source for graphql api (#14651) Implement support for elasticsearch data source. Endpoint is fixed to `https` URL as through testing, CloudFormations requires the domain to be in the form of `https://[...]`. Fixes #6063 ---- *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-appsync/README.md | 41 ++++ .../@aws-cdk/aws-appsync/lib/data-source.ts | 28 +++ .../aws-appsync/lib/graphqlapi-base.ts | 28 ++- packages/@aws-cdk/aws-appsync/package.json | 2 + .../test/appsync-elasticsearch.test.ts | 150 +++++++++++++ .../integ.graphql-elasticsearch.expected.json | 210 ++++++++++++++++++ .../test/integ.graphql-elasticsearch.ts | 66 ++++++ 7 files changed, 524 insertions(+), 1 deletion(-) create mode 100644 packages/@aws-cdk/aws-appsync/test/appsync-elasticsearch.test.ts create mode 100644 packages/@aws-cdk/aws-appsync/test/integ.graphql-elasticsearch.expected.json create mode 100644 packages/@aws-cdk/aws-appsync/test/integ.graphql-elasticsearch.ts diff --git a/packages/@aws-cdk/aws-appsync/README.md b/packages/@aws-cdk/aws-appsync/README.md index e0a636217f38b..36c3f4c337b6c 100644 --- a/packages/@aws-cdk/aws-appsync/README.md +++ b/packages/@aws-cdk/aws-appsync/README.md @@ -240,6 +240,47 @@ httpDs.createResolver({ }); ``` +### Elasticsearch + +AppSync has builtin support for Elasticsearch from domains that are provisioned +through your AWS account. You can use AppSync resolvers to perform GraphQL operations +such as queries, mutations, and subscriptions. + +```ts +const user = new User(stack, 'User'); +const domain = new es.Domain(stack, 'Domain', { + version: es.ElasticsearchVersion.V7_1, + removalPolicy: cdk.RemovalPolicy.DESTROY, + fineGrainedAccessControl: { masterUserArn: user.userArn }, + encryptionAtRest: { enabled: true }, + nodeToNodeEncryption: true, + enforceHttps: true, +}); + +const ds = api.addElasticsearchDataSource('ds', domain); + +ds.createResolver({ + typeName: 'Query', + fieldName: 'getTests', + requestMappingTemplate: appsync.MappingTemplate.fromString(JSON.stringify({ + version: '2017-02-28', + operation: 'GET', + path: '/id/post/_search', + params: { + headers: {}, + queryString: {}, + body: { from: 0, size: 50 }, + }, + })), + responseMappingTemplate: appsync.MappingTemplate.fromString(`[ + #foreach($entry in $context.result.hits.hits) + #if( $velocityCount > 1 ) , #end + $utils.toJson($entry.get("_source")) + #end + ]`), +}); +``` + ## Schema Every GraphQL Api needs a schema to define the Api. CDK offers `appsync.Schema` diff --git a/packages/@aws-cdk/aws-appsync/lib/data-source.ts b/packages/@aws-cdk/aws-appsync/lib/data-source.ts index ac22771916400..4c1280c2196d9 100644 --- a/packages/@aws-cdk/aws-appsync/lib/data-source.ts +++ b/packages/@aws-cdk/aws-appsync/lib/data-source.ts @@ -1,4 +1,5 @@ import { ITable } from '@aws-cdk/aws-dynamodb'; +import { IDomain } from '@aws-cdk/aws-elasticsearch'; import { Grant, IGrantable, IPrincipal, IRole, Role, ServicePrincipal } from '@aws-cdk/aws-iam'; import { IFunction } from '@aws-cdk/aws-lambda'; import { IServerlessCluster } from '@aws-cdk/aws-rds'; @@ -363,4 +364,31 @@ export class RdsDataSource extends BackedDataSource { scope: this, }); } +} + +/** + * Properities for the Elasticsearch Data Source + */ +export interface ElasticsearchDataSourceProps extends BackedDataSourceProps { + /** + * The elasticsearch domain containing the endpoint for the data source + */ + readonly domain: IDomain; +} + +/** + * An Appsync datasource backed by Elasticsearch + */ +export class ElasticsearchDataSource extends BackedDataSource { + constructor(scope: Construct, id: string, props: ElasticsearchDataSourceProps) { + super(scope, id, props, { + type: 'AMAZON_ELASTICSEARCH', + elasticsearchConfig: { + awsRegion: props.domain.stack.region, + endpoint: `https://${props.domain.domainEndpoint}`, + }, + }); + + props.domain.grantReadWrite(this); + } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appsync/lib/graphqlapi-base.ts b/packages/@aws-cdk/aws-appsync/lib/graphqlapi-base.ts index 060d57a34c276..dfd596b929903 100644 --- a/packages/@aws-cdk/aws-appsync/lib/graphqlapi-base.ts +++ b/packages/@aws-cdk/aws-appsync/lib/graphqlapi-base.ts @@ -1,9 +1,10 @@ import { ITable } from '@aws-cdk/aws-dynamodb'; +import { IDomain } from '@aws-cdk/aws-elasticsearch'; import { IFunction } from '@aws-cdk/aws-lambda'; import { IServerlessCluster } from '@aws-cdk/aws-rds'; import { ISecret } from '@aws-cdk/aws-secretsmanager'; import { CfnResource, IResource, Resource } from '@aws-cdk/core'; -import { DynamoDbDataSource, HttpDataSource, LambdaDataSource, NoneDataSource, RdsDataSource, AwsIamConfig } from './data-source'; +import { DynamoDbDataSource, HttpDataSource, LambdaDataSource, NoneDataSource, RdsDataSource, AwsIamConfig, ElasticsearchDataSource } from './data-source'; import { Resolver, ExtendedResolverProps } from './resolver'; /** @@ -110,6 +111,15 @@ export interface IGraphqlApi extends IResource { options?: DataSourceOptions ): RdsDataSource; + /** + * add a new elasticsearch data source to this API + * + * @param id The data source's id + * @param domain The elasticsearch domain for this data source + * @param options The optional configuration for this data source + */ + addElasticsearchDataSource(id: string, domain: IDomain, options?: DataSourceOptions): ElasticsearchDataSource; + /** * creates a new resolver for this datasource and API using the given properties */ @@ -228,6 +238,22 @@ export abstract class GraphqlApiBase extends Resource implements IGraphqlApi { }); } + /** + * add a new elasticsearch data source to this API + * + * @param id The data source's id + * @param domain The elasticsearch domain for this data source + * @param options The optional configuration for this data source + */ + public addElasticsearchDataSource(id: string, domain: IDomain, options?: DataSourceOptions): ElasticsearchDataSource { + return new ElasticsearchDataSource(this, id, { + api: this, + name: options?.name, + description: options?.description, + domain, + }); + } + /** * creates a new resolver for this datasource and API using the given properties */ diff --git a/packages/@aws-cdk/aws-appsync/package.json b/packages/@aws-cdk/aws-appsync/package.json index 61ac81e9d385c..bb3d07854903f 100644 --- a/packages/@aws-cdk/aws-appsync/package.json +++ b/packages/@aws-cdk/aws-appsync/package.json @@ -84,6 +84,7 @@ "@aws-cdk/aws-cognito": "0.0.0", "@aws-cdk/aws-dynamodb": "0.0.0", "@aws-cdk/aws-ec2": "0.0.0", + "@aws-cdk/aws-elasticsearch": "0.0.0", "@aws-cdk/aws-rds": "0.0.0", "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-secretsmanager": "0.0.0", @@ -97,6 +98,7 @@ "@aws-cdk/aws-cognito": "0.0.0", "@aws-cdk/aws-dynamodb": "0.0.0", "@aws-cdk/aws-ec2": "0.0.0", + "@aws-cdk/aws-elasticsearch": "0.0.0", "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-lambda": "0.0.0", "@aws-cdk/aws-s3-assets": "0.0.0", diff --git a/packages/@aws-cdk/aws-appsync/test/appsync-elasticsearch.test.ts b/packages/@aws-cdk/aws-appsync/test/appsync-elasticsearch.test.ts new file mode 100644 index 0000000000000..1a974f982b61f --- /dev/null +++ b/packages/@aws-cdk/aws-appsync/test/appsync-elasticsearch.test.ts @@ -0,0 +1,150 @@ +import '@aws-cdk/assert-internal/jest'; +import * as path from 'path'; +import * as es from '@aws-cdk/aws-elasticsearch'; +import * as cdk from '@aws-cdk/core'; +import * as appsync from '../lib'; + +// GLOBAL GIVEN +let stack: cdk.Stack; +let api: appsync.GraphqlApi; +let domain: es.Domain; +beforeEach(() => { + stack = new cdk.Stack(); + api = new appsync.GraphqlApi(stack, 'baseApi', { + name: 'api', + schema: appsync.Schema.fromAsset(path.join(__dirname, 'appsync.test.graphql')), + }); + domain = new es.Domain(stack, 'EsDomain', { + version: es.ElasticsearchVersion.V7_10, + }); +}); + +describe('Elasticsearch Data Source Configuration', () => { + test('Elasticsearch configure properly', () => { + // WHEN + api.addElasticsearchDataSource('ds', domain); + + // THEN + expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + PolicyDocument: { + Version: '2012-10-17', + Statement: [{ + Action: [ + 'es:ESHttpGet', + 'es:ESHttpHead', + 'es:ESHttpDelete', + 'es:ESHttpPost', + 'es:ESHttpPut', + 'es:ESHttpPatch', + ], + Effect: 'Allow', + Resource: [{ + 'Fn::GetAtt': ['EsDomain1213C634', 'Arn'], + }, + { + 'Fn::Join': ['', [{ + 'Fn::GetAtt': ['EsDomain1213C634', 'Arn'], + }, '/*']], + }], + }], + }, + }); + }); + + test('Elastic search configuration contains fully qualified url', () => { + // WHEN + api.addElasticsearchDataSource('ds', domain); + + // THEN + expect(stack).toHaveResourceLike('AWS::AppSync::DataSource', { + ElasticsearchConfig: { + Endpoint: { + 'Fn::Join': ['', ['https://', { + 'Fn::GetAtt': ['EsDomain1213C634', 'DomainEndpoint'], + }]], + }, + }, + }); + }); + + test('default configuration produces name identical to the id', () => { + // WHEN + api.addElasticsearchDataSource('ds', domain); + + // THEN + expect(stack).toHaveResourceLike('AWS::AppSync::DataSource', { + Type: 'AMAZON_ELASTICSEARCH', + Name: 'ds', + }); + }); + + test('appsync configures name correctly', () => { + // WHEN + api.addElasticsearchDataSource('ds', domain, { + name: 'custom', + }); + + // THEN + expect(stack).toHaveResourceLike('AWS::AppSync::DataSource', { + Type: 'AMAZON_ELASTICSEARCH', + Name: 'custom', + }); + }); + + test('appsync configures name and description correctly', () => { + // WHEN + api.addElasticsearchDataSource('ds', domain, { + name: 'custom', + description: 'custom description', + }); + + // THEN + expect(stack).toHaveResourceLike('AWS::AppSync::DataSource', { + Type: 'AMAZON_ELASTICSEARCH', + Name: 'custom', + Description: 'custom description', + }); + }); + + test('appsync errors when creating multiple elasticsearch data sources with no configuration', () => { + // WHEN + const when = () => { + api.addElasticsearchDataSource('ds', domain); + api.addElasticsearchDataSource('ds', domain); + }; + + // THEN + expect(when).toThrow('There is already a Construct with name \'ds\' in GraphqlApi [baseApi]'); + }); +}); + +describe('adding elasticsearch data source from imported api', () => { + test('imported api can add ElasticsearchDataSource from id', () => { + // WHEN + const importedApi = appsync.GraphqlApi.fromGraphqlApiAttributes(stack, 'importedApi', { + graphqlApiId: api.apiId, + }); + importedApi.addElasticsearchDataSource('ds', domain); + + // THEN + expect(stack).toHaveResourceLike('AWS::AppSync::DataSource', { + Type: 'AMAZON_ELASTICSEARCH', + ApiId: { 'Fn::GetAtt': ['baseApiCDA4D43A', 'ApiId'] }, + }); + }); + + test('imported api can add ElasticsearchDataSource from attributes', () => { + // WHEN + const importedApi = appsync.GraphqlApi.fromGraphqlApiAttributes(stack, 'importedApi', { + graphqlApiId: api.apiId, + graphqlApiArn: api.arn, + }); + importedApi.addElasticsearchDataSource('ds', domain); + + // THEN + expect(stack).toHaveResourceLike('AWS::AppSync::DataSource', { + Type: 'AMAZON_ELASTICSEARCH', + ApiId: { 'Fn::GetAtt': ['baseApiCDA4D43A', 'ApiId'] }, + }); + }); +}); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appsync/test/integ.graphql-elasticsearch.expected.json b/packages/@aws-cdk/aws-appsync/test/integ.graphql-elasticsearch.expected.json new file mode 100644 index 0000000000000..22e64957700e9 --- /dev/null +++ b/packages/@aws-cdk/aws-appsync/test/integ.graphql-elasticsearch.expected.json @@ -0,0 +1,210 @@ +{ + "Resources": { + "User00B015A1": { + "Type": "AWS::IAM::User" + }, + "Domain66AC69E0": { + "Type": "AWS::Elasticsearch::Domain", + "Properties": { + "AdvancedSecurityOptions": { + "Enabled": true, + "InternalUserDatabaseEnabled": false, + "MasterUserOptions": { + "MasterUserARN": { + "Fn::GetAtt": [ + "User00B015A1", + "Arn" + ] + } + } + }, + "CognitoOptions": { + "Enabled": false + }, + "DomainEndpointOptions": { + "EnforceHTTPS": true, + "TLSSecurityPolicy": "Policy-Min-TLS-1-0-2019-07" + }, + "EBSOptions": { + "EBSEnabled": true, + "VolumeSize": 10, + "VolumeType": "gp2" + }, + "ElasticsearchClusterConfig": { + "DedicatedMasterEnabled": false, + "InstanceCount": 1, + "InstanceType": "r5.large.elasticsearch", + "ZoneAwarenessEnabled": false + }, + "ElasticsearchVersion": "7.1", + "EncryptionAtRestOptions": { + "Enabled": true + }, + "LogPublishingOptions": {}, + "NodeToNodeEncryptionOptions": { + "Enabled": true + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "apiC8550315": { + "Type": "AWS::AppSync::GraphQLApi", + "Properties": { + "AuthenticationType": "API_KEY", + "Name": "api" + } + }, + "apiSchema0EA92056": { + "Type": "AWS::AppSync::GraphQLSchema", + "Properties": { + "ApiId": { + "Fn::GetAtt": [ + "apiC8550315", + "ApiId" + ] + }, + "Definition": "type test {\n version: String!\n}\ntype Query {\n getTests: [test]!\n}\ntype Mutation {\n addTest(version: String!): test\n}\n" + } + }, + "apiDefaultApiKey6AB8D7C4": { + "Type": "AWS::AppSync::ApiKey", + "Properties": { + "ApiId": { + "Fn::GetAtt": [ + "apiC8550315", + "ApiId" + ] + } + }, + "DependsOn": [ + "apiSchema0EA92056" + ] + }, + "apidsServiceRoleBDB08107": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "appsync.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "apidsServiceRoleDefaultPolicy5634EFD0": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "es:ESHttpGet", + "es:ESHttpHead", + "es:ESHttpDelete", + "es:ESHttpPost", + "es:ESHttpPut", + "es:ESHttpPatch" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "Domain66AC69E0", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "Domain66AC69E0", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "apidsServiceRoleDefaultPolicy5634EFD0", + "Roles": [ + { + "Ref": "apidsServiceRoleBDB08107" + } + ] + } + }, + "apids4328272F": { + "Type": "AWS::AppSync::DataSource", + "Properties": { + "ApiId": { + "Fn::GetAtt": [ + "apiC8550315", + "ApiId" + ] + }, + "Name": "ds", + "Type": "AMAZON_ELASTICSEARCH", + "ElasticsearchConfig": { + "AwsRegion": { + "Ref": "AWS::Region" + }, + "Endpoint": { + "Fn::Join": [ + "", + [ + "https://", + { + "Fn::GetAtt": [ + "Domain66AC69E0", + "DomainEndpoint" + ] + } + ] + ] + } + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "apidsServiceRoleBDB08107", + "Arn" + ] + } + } + }, + "apidsQuerygetTestsResolver5C6FBB59": { + "Type": "AWS::AppSync::Resolver", + "Properties": { + "ApiId": { + "Fn::GetAtt": [ + "apiC8550315", + "ApiId" + ] + }, + "FieldName": "getTests", + "TypeName": "Query", + "DataSourceName": "ds", + "Kind": "UNIT", + "RequestMappingTemplate": "{\"version\":\"2017-02-28\",\"operation\":\"GET\",\"path\":\"/id/post/_search\",\"params\":{\"headers\":{},\"queryString\":{},\"body\":{\"from\":0,\"size\":50}}}", + "ResponseMappingTemplate": "{\"version\":\"2017-02-28\",\"operation\":\"GET\",\"path\":\"/id/post/_search\",\"params\":{\"headers\":{},\"queryString\":{},\"body\":{\"from\":0,\"size\":50,\"query\":{\"term\":{\"author\":\"$util.toJson($context.arguments.author)\"}}}}}" + }, + "DependsOn": [ + "apids4328272F", + "apiSchema0EA92056" + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appsync/test/integ.graphql-elasticsearch.ts b/packages/@aws-cdk/aws-appsync/test/integ.graphql-elasticsearch.ts new file mode 100644 index 0000000000000..933572da29eca --- /dev/null +++ b/packages/@aws-cdk/aws-appsync/test/integ.graphql-elasticsearch.ts @@ -0,0 +1,66 @@ +import * as path from 'path'; +import * as es from '@aws-cdk/aws-elasticsearch'; +import { User } from '@aws-cdk/aws-iam'; +import * as cdk from '@aws-cdk/core'; +import * as appsync from '../lib'; + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'appsync-elasticsearch'); +const user = new User(stack, 'User'); +const domain = new es.Domain(stack, 'Domain', { + version: es.ElasticsearchVersion.V7_1, + removalPolicy: cdk.RemovalPolicy.DESTROY, + fineGrainedAccessControl: { + masterUserArn: user.userArn, + }, + encryptionAtRest: { + enabled: true, + }, + nodeToNodeEncryption: true, + enforceHttps: true, +}); + +const api = new appsync.GraphqlApi(stack, 'api', { + name: 'api', + schema: appsync.Schema.fromAsset(path.join(__dirname, 'appsync.test.graphql')), +}); + +const ds = api.addElasticsearchDataSource('ds', domain); + +ds.createResolver({ + typeName: 'Query', + fieldName: 'getTests', + requestMappingTemplate: appsync.MappingTemplate.fromString(JSON.stringify({ + version: '2017-02-28', + operation: 'GET', + path: '/id/post/_search', + params: { + headers: {}, + queryString: {}, + body: { + from: 0, + size: 50, + }, + }, + })), + responseMappingTemplate: appsync.MappingTemplate.fromString(JSON.stringify({ + version: '2017-02-28', + operation: 'GET', + path: '/id/post/_search', + params: { + headers: {}, + queryString: {}, + body: { + from: 0, + size: 50, + query: { + term: { + author: '$util.toJson($context.arguments.author)', + }, + }, + }, + }, + })), +}); + +app.synth(); \ No newline at end of file From 367f1513ba88755fa55c1ed5846b2d73fdbe5017 Mon Sep 17 00:00:00 2001 From: Jeffrey Swan <61218880+plan-do-break-fix@users.noreply.github.com> Date: Thu, 13 May 2021 00:06:46 -0500 Subject: [PATCH 026/134] chore(docs): corrects common typos in various README files (#14363) ---- Scope of changes is restricted to docs only. Changes have been made in agreement with Standard American English. *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-appmesh/README.md | 2 +- packages/@aws-cdk/aws-appsync/README.md | 6 +++--- packages/@aws-cdk/aws-eks/README.md | 2 +- packages/@aws-cdk/aws-s3-deployment/README.md | 2 +- packages/@aws-cdk/pipelines/README.md | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/README.md b/packages/@aws-cdk/aws-appmesh/README.md index 067575fb73ec0..46c568fc3eb8c 100644 --- a/packages/@aws-cdk/aws-appmesh/README.md +++ b/packages/@aws-cdk/aws-appmesh/README.md @@ -27,7 +27,7 @@ App Mesh gives you consistent visibility and network traffic controls for every App Mesh supports microservice applications that use service discovery naming for their components. To use App Mesh, you must have an existing application running on AWS Fargate, Amazon ECS, Amazon EKS, Kubernetes on AWS, or Amazon EC2. -For futher information on **AWS AppMesh** visit the [AWS Docs for AppMesh](https://docs.aws.amazon.com/app-mesh/index.html). +For further information on **AWS AppMesh** visit the [AWS Docs for AppMesh](https://docs.aws.amazon.com/app-mesh/index.html). ## Create the App and Stack diff --git a/packages/@aws-cdk/aws-appsync/README.md b/packages/@aws-cdk/aws-appsync/README.md index 36c3f4c337b6c..32e0558ccd4f8 100644 --- a/packages/@aws-cdk/aws-appsync/README.md +++ b/packages/@aws-cdk/aws-appsync/README.md @@ -759,7 +759,7 @@ You can create Object Types in three ways: name: 'demo', }); const demo = new appsync.ObjectType('Demo', { - defintion: { + definition: { id: appsync.GraphqlType.string({ isRequired: true }), version: appsync.GraphqlType.string({ isRequired: true }), }, @@ -782,7 +782,7 @@ You can create Object Types in three ways: ```ts import { required_string } from './scalar-types'; export const demo = new appsync.ObjectType('Demo', { - defintion: { + definition: { id: required_string, version: required_string, }, @@ -806,7 +806,7 @@ You can create Object Types in three ways: }); const demo = new appsync.ObjectType('Demo', { interfaceTypes: [ node ], - defintion: { + definition: { version: appsync.GraphqlType.string({ isRequired: true }), }, }); diff --git a/packages/@aws-cdk/aws-eks/README.md b/packages/@aws-cdk/aws-eks/README.md index bb64c62070a0e..2eae725b53873 100644 --- a/packages/@aws-cdk/aws-eks/README.md +++ b/packages/@aws-cdk/aws-eks/README.md @@ -654,7 +654,7 @@ const cluster = new eks.Cluster(this, 'MyCluster', { }); ``` -You can also use a similiar configuration for running a cluster built using the FargateCluster construct. +You can also use a similar configuration for running a cluster built using the FargateCluster construct. ```ts const secretsKey = new kms.Key(this, 'SecretsKey'); diff --git a/packages/@aws-cdk/aws-s3-deployment/README.md b/packages/@aws-cdk/aws-s3-deployment/README.md index 64c3872c59f74..84af913dcaccd 100644 --- a/packages/@aws-cdk/aws-s3-deployment/README.md +++ b/packages/@aws-cdk/aws-s3-deployment/README.md @@ -77,7 +77,7 @@ Configuring this has a few implications you should be aware of: - **Destination Changes** When the destination bucket or prefix is changed, all files in the previous destination will **first** be - deleted and then uploaded to the new destination location. This could have availablity implications + deleted and then uploaded to the new destination location. This could have availability implications on your users. ### General Recommendations diff --git a/packages/@aws-cdk/pipelines/README.md b/packages/@aws-cdk/pipelines/README.md index 6097c87c16426..074c1b331ee41 100644 --- a/packages/@aws-cdk/pipelines/README.md +++ b/packages/@aws-cdk/pipelines/README.md @@ -596,7 +596,7 @@ to create it in. If you are deploying your application to different environments also have to bootstrap those and be sure to add a *trust* relationship. > This library requires a newer version of the bootstrapping stack which has -> been updated specifically to support cross-account continous delivery. In the future, +> been updated specifically to support cross-account continuous delivery. In the future, > this new bootstrapping stack will become the default, but for now it is still > opt-in. > From 5fa9c106e1afe14eaac6407b3c11b57a03953b33 Mon Sep 17 00:00:00 2001 From: Shane Handley <68725881+shanehandley-lt@users.noreply.github.com> Date: Thu, 13 May 2021 15:51:42 +1000 Subject: [PATCH 027/134] docs: typo in documentation (dependning -> depending) (#13409) Fix a typo in `/core/lib/assets.ts` ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/core/lib/assets.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/core/lib/assets.ts b/packages/@aws-cdk/core/lib/assets.ts index d9bd02d4d91a9..289ef925189fe 100644 --- a/packages/@aws-cdk/core/lib/assets.ts +++ b/packages/@aws-cdk/core/lib/assets.ts @@ -117,7 +117,7 @@ export interface FileAssetSource { /** * The path, relative to the root of the cloud assembly, in which this asset - * source resides. This can be a path to a file or a directory, dependning on the + * source resides. This can be a path to a file or a directory, depending on the * packaging type. * * @default - Exactly one of `directory` and `executable` is required From 9a4d6243ae009dc02949dd2cf415523dbf582678 Mon Sep 17 00:00:00 2001 From: Oliver Bowman Date: Thu, 13 May 2021 07:40:55 +0100 Subject: [PATCH 028/134] docs(lambda-nodejs): Example for esbuild missing comma in property (#13520) ---- Example under esbuild appears to be missing comma. *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/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-lambda-nodejs/README.md b/packages/@aws-cdk/aws-lambda-nodejs/README.md index 7a1662dc26fb2..c52b0fe2570c9 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/README.md +++ b/packages/@aws-cdk/aws-lambda-nodejs/README.md @@ -142,7 +142,7 @@ new lambda.NodejsFunction(this, 'my-handler', { }, logLevel: LogLevel.SILENT, // defaults to LogLevel.WARNING keepNames: true, // defaults to false - tsconfig: 'custom-tsconfig.json' // use custom-tsconfig.json instead of default, + tsconfig: 'custom-tsconfig.json', // use custom-tsconfig.json instead of default, metafile: true, // include meta file, defaults to false banner : '/* comments */', // by default no comments are passed footer : '/* comments */', // by default no comments are passed From 282d242c513a9bc853e9b96ff782a7823abd5a5a Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Thu, 13 May 2021 00:37:33 -0700 Subject: [PATCH 029/134] chore(custom-resources): import the AWSLambda package explicitly (#14643) When linking the aws-cdk repository to a CDK app using the `link-all.sh` script, if the app uses `ts-node`, the Lambda code in the @aws-cdk/custom-resources package gets picked up by the TypeScript compiler. That code relied on the `aws-lambda` package being implicitly available, but that would cause `ts-node` to fail. Add an explicit import of it in the code - I checked the only difference in the generated JS code is the sourceMappingUrl, so it shouldn't make a difference at runtime, but allows `ts-node` to load that file successfully. Fixes #11627 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-msk/test/integ.cluster.expected.json | 18 +++++++++--------- .../lib/aws-custom-resource/runtime/index.ts | 8 +++++++- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/packages/@aws-cdk/aws-msk/test/integ.cluster.expected.json b/packages/@aws-cdk/aws-msk/test/integ.cluster.expected.json index 43769c1b25ac8..769c0533269cd 100644 --- a/packages/@aws-cdk/aws-msk/test/integ.cluster.expected.json +++ b/packages/@aws-cdk/aws-msk/test/integ.cluster.expected.json @@ -524,7 +524,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3Bucket4DD075F7" + "Ref": "AssetParametersf56a9c742f3e99b26237d5d0912c69f9db8289c13656fdcb490fd017d801c483S3Bucket9DEDD0AB" }, "S3Key": { "Fn::Join": [ @@ -537,7 +537,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3VersionKeyBD0E03B7" + "Ref": "AssetParametersf56a9c742f3e99b26237d5d0912c69f9db8289c13656fdcb490fd017d801c483S3VersionKeyA54743D3" } ] } @@ -550,7 +550,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3VersionKeyBD0E03B7" + "Ref": "AssetParametersf56a9c742f3e99b26237d5d0912c69f9db8289c13656fdcb490fd017d801c483S3VersionKeyA54743D3" } ] } @@ -576,17 +576,17 @@ } }, "Parameters": { - "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3Bucket4DD075F7": { + "AssetParametersf56a9c742f3e99b26237d5d0912c69f9db8289c13656fdcb490fd017d801c483S3Bucket9DEDD0AB": { "Type": "String", - "Description": "S3 bucket for asset \"b965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98\"" + "Description": "S3 bucket for asset \"f56a9c742f3e99b26237d5d0912c69f9db8289c13656fdcb490fd017d801c483\"" }, - "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3VersionKeyBD0E03B7": { + "AssetParametersf56a9c742f3e99b26237d5d0912c69f9db8289c13656fdcb490fd017d801c483S3VersionKeyA54743D3": { "Type": "String", - "Description": "S3 key for asset version \"b965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98\"" + "Description": "S3 key for asset version \"f56a9c742f3e99b26237d5d0912c69f9db8289c13656fdcb490fd017d801c483\"" }, - "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98ArtifactHash35A756EB": { + "AssetParametersf56a9c742f3e99b26237d5d0912c69f9db8289c13656fdcb490fd017d801c483ArtifactHash228F5AF4": { "Type": "String", - "Description": "Artifact hash for asset \"b965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98\"" + "Description": "Artifact hash for asset \"f56a9c742f3e99b26237d5d0912c69f9db8289c13656fdcb490fd017d801c483\"" } }, "Outputs": { diff --git a/packages/@aws-cdk/custom-resources/lib/aws-custom-resource/runtime/index.ts b/packages/@aws-cdk/custom-resources/lib/aws-custom-resource/runtime/index.ts index 56d485e83d4fa..807dc7e78fec5 100644 --- a/packages/@aws-cdk/custom-resources/lib/aws-custom-resource/runtime/index.ts +++ b/packages/@aws-cdk/custom-resources/lib/aws-custom-resource/runtime/index.ts @@ -1,5 +1,11 @@ /* eslint-disable no-console */ import { execSync } from 'child_process'; +// import the AWSLambda package explicitly, +// which is globally available in the Lambda runtime, +// as otherwise linking this repository with link-all.sh +// fails in the CDK app executed with ts-node +/* eslint-disable-next-line import/no-extraneous-dependencies,import/no-unresolved */ +import * as AWSLambda from 'aws-lambda'; import { AwsSdkCall } from '../aws-custom-resource'; /** @@ -204,4 +210,4 @@ export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent function decodeCall(call: string | undefined) { if (!call) { return undefined; } return JSON.parse(call); -} \ No newline at end of file +} From 9d97b7db1fa4634c9d62778badb0f90ed9f6aef6 Mon Sep 17 00:00:00 2001 From: Mitchell Valine Date: Thu, 13 May 2021 02:01:08 -0700 Subject: [PATCH 030/134] chore: init templates use node jest environment (#14632) Remove usage of the `jsdom` test environment in init templates to speed up unit testing by default. Testing: ran cdk init --language=(typescript|javascript) against local build of CLI then ran yarn test to verify that the testing config was valid and jest correctly used the node environment. fix: #14630 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../lib/init-templates/v1/app/javascript/jest.config.js | 3 +++ .../lib/init-templates/v1/app/typescript/jest.config.js | 1 + .../lib/init-templates/v1/lib/typescript/jest.config.js | 1 + .../lib/init-templates/v1/sample-app/javascript/jest.config.js | 3 +++ .../lib/init-templates/v1/sample-app/typescript/jest.config.js | 1 + .../lib/init-templates/v2/app/javascript/jest.config.js | 3 +++ .../lib/init-templates/v2/app/typescript/jest.config.js | 1 + .../lib/init-templates/v2/lib/typescript/jest.config.js | 1 + .../lib/init-templates/v2/sample-app/javascript/jest.config.js | 3 +++ .../lib/init-templates/v2/sample-app/typescript/jest.config.js | 1 + 10 files changed, 18 insertions(+) create mode 100644 packages/aws-cdk/lib/init-templates/v1/app/javascript/jest.config.js create mode 100644 packages/aws-cdk/lib/init-templates/v1/sample-app/javascript/jest.config.js create mode 100644 packages/aws-cdk/lib/init-templates/v2/app/javascript/jest.config.js create mode 100644 packages/aws-cdk/lib/init-templates/v2/sample-app/javascript/jest.config.js diff --git a/packages/aws-cdk/lib/init-templates/v1/app/javascript/jest.config.js b/packages/aws-cdk/lib/init-templates/v1/app/javascript/jest.config.js new file mode 100644 index 0000000000000..668e089fb02b3 --- /dev/null +++ b/packages/aws-cdk/lib/init-templates/v1/app/javascript/jest.config.js @@ -0,0 +1,3 @@ +module.exports = { + testEnvironment: "node" +} diff --git a/packages/aws-cdk/lib/init-templates/v1/app/typescript/jest.config.js b/packages/aws-cdk/lib/init-templates/v1/app/typescript/jest.config.js index 772f974903b79..08263b8954a42 100644 --- a/packages/aws-cdk/lib/init-templates/v1/app/typescript/jest.config.js +++ b/packages/aws-cdk/lib/init-templates/v1/app/typescript/jest.config.js @@ -1,4 +1,5 @@ module.exports = { + testEnvironment: 'node', roots: ['/test'], testMatch: ['**/*.test.ts'], transform: { diff --git a/packages/aws-cdk/lib/init-templates/v1/lib/typescript/jest.config.js b/packages/aws-cdk/lib/init-templates/v1/lib/typescript/jest.config.js index 772f974903b79..08263b8954a42 100644 --- a/packages/aws-cdk/lib/init-templates/v1/lib/typescript/jest.config.js +++ b/packages/aws-cdk/lib/init-templates/v1/lib/typescript/jest.config.js @@ -1,4 +1,5 @@ module.exports = { + testEnvironment: 'node', roots: ['/test'], testMatch: ['**/*.test.ts'], transform: { diff --git a/packages/aws-cdk/lib/init-templates/v1/sample-app/javascript/jest.config.js b/packages/aws-cdk/lib/init-templates/v1/sample-app/javascript/jest.config.js new file mode 100644 index 0000000000000..95495de92eb8c --- /dev/null +++ b/packages/aws-cdk/lib/init-templates/v1/sample-app/javascript/jest.config.js @@ -0,0 +1,3 @@ +module.exports = { + testEnvironment: 'node' +} diff --git a/packages/aws-cdk/lib/init-templates/v1/sample-app/typescript/jest.config.js b/packages/aws-cdk/lib/init-templates/v1/sample-app/typescript/jest.config.js index 772f974903b79..08263b8954a42 100644 --- a/packages/aws-cdk/lib/init-templates/v1/sample-app/typescript/jest.config.js +++ b/packages/aws-cdk/lib/init-templates/v1/sample-app/typescript/jest.config.js @@ -1,4 +1,5 @@ module.exports = { + testEnvironment: 'node', roots: ['/test'], testMatch: ['**/*.test.ts'], transform: { diff --git a/packages/aws-cdk/lib/init-templates/v2/app/javascript/jest.config.js b/packages/aws-cdk/lib/init-templates/v2/app/javascript/jest.config.js new file mode 100644 index 0000000000000..95495de92eb8c --- /dev/null +++ b/packages/aws-cdk/lib/init-templates/v2/app/javascript/jest.config.js @@ -0,0 +1,3 @@ +module.exports = { + testEnvironment: 'node' +} diff --git a/packages/aws-cdk/lib/init-templates/v2/app/typescript/jest.config.js b/packages/aws-cdk/lib/init-templates/v2/app/typescript/jest.config.js index 772f974903b79..08263b8954a42 100644 --- a/packages/aws-cdk/lib/init-templates/v2/app/typescript/jest.config.js +++ b/packages/aws-cdk/lib/init-templates/v2/app/typescript/jest.config.js @@ -1,4 +1,5 @@ module.exports = { + testEnvironment: 'node', roots: ['/test'], testMatch: ['**/*.test.ts'], transform: { diff --git a/packages/aws-cdk/lib/init-templates/v2/lib/typescript/jest.config.js b/packages/aws-cdk/lib/init-templates/v2/lib/typescript/jest.config.js index 772f974903b79..08263b8954a42 100644 --- a/packages/aws-cdk/lib/init-templates/v2/lib/typescript/jest.config.js +++ b/packages/aws-cdk/lib/init-templates/v2/lib/typescript/jest.config.js @@ -1,4 +1,5 @@ module.exports = { + testEnvironment: 'node', roots: ['/test'], testMatch: ['**/*.test.ts'], transform: { diff --git a/packages/aws-cdk/lib/init-templates/v2/sample-app/javascript/jest.config.js b/packages/aws-cdk/lib/init-templates/v2/sample-app/javascript/jest.config.js new file mode 100644 index 0000000000000..95495de92eb8c --- /dev/null +++ b/packages/aws-cdk/lib/init-templates/v2/sample-app/javascript/jest.config.js @@ -0,0 +1,3 @@ +module.exports = { + testEnvironment: 'node' +} diff --git a/packages/aws-cdk/lib/init-templates/v2/sample-app/typescript/jest.config.js b/packages/aws-cdk/lib/init-templates/v2/sample-app/typescript/jest.config.js index 772f974903b79..08263b8954a42 100644 --- a/packages/aws-cdk/lib/init-templates/v2/sample-app/typescript/jest.config.js +++ b/packages/aws-cdk/lib/init-templates/v2/sample-app/typescript/jest.config.js @@ -1,4 +1,5 @@ module.exports = { + testEnvironment: 'node', roots: ['/test'], testMatch: ['**/*.test.ts'], transform: { From 3063818aa7c3c3ff56cf55254b0f6561db190a3e Mon Sep 17 00:00:00 2001 From: Madeline Kusters <80541297+madeline-k@users.noreply.github.com> Date: Thu, 13 May 2021 02:29:45 -0700 Subject: [PATCH 031/134] fix(events-targets): circular dependency when adding a KMS-encrypted SQS queue (#14638) fixes #11158 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-events-targets/lib/sqs.ts | 15 +++-- .../@aws-cdk/aws-events-targets/package.json | 2 + .../integ.sqs-event-rule-target.expected.json | 66 ++++++++++++++++--- .../test/sqs/integ.sqs-event-rule-target.ts | 9 ++- 4 files changed, 74 insertions(+), 18 deletions(-) diff --git a/packages/@aws-cdk/aws-events-targets/lib/sqs.ts b/packages/@aws-cdk/aws-events-targets/lib/sqs.ts index 8d711b4b9f5be..501414ecee348 100644 --- a/packages/@aws-cdk/aws-events-targets/lib/sqs.ts +++ b/packages/@aws-cdk/aws-events-targets/lib/sqs.ts @@ -52,14 +52,15 @@ export class SqsQueue implements events.IRuleTarget { * @see https://docs.aws.amazon.com/eventbridge/latest/userguide/resource-based-policies-eventbridge.html#sqs-permissions */ public bind(rule: events.IRule, _id?: string): events.RuleTargetConfig { + // Only add the rule as a condition if the queue is not encrypted, to avoid circular dependency. See issue #11158. + const principalOpts = this.queue.encryptionMasterKey ? {} : { + conditions: { + ArnEquals: { 'aws:SourceArn': rule.ruleArn }, + }, + }; + // deduplicated automatically - this.queue.grantSendMessages(new iam.ServicePrincipal('events.amazonaws.com', - { - conditions: { - ArnEquals: { 'aws:SourceArn': rule.ruleArn }, - }, - }), - ); + this.queue.grantSendMessages(new iam.ServicePrincipal('events.amazonaws.com', principalOpts)); return { arn: this.queue.queueArn, diff --git a/packages/@aws-cdk/aws-events-targets/package.json b/packages/@aws-cdk/aws-events-targets/package.json index e36df0baf5c71..f262d5897aa5a 100644 --- a/packages/@aws-cdk/aws-events-targets/package.json +++ b/packages/@aws-cdk/aws-events-targets/package.json @@ -93,6 +93,7 @@ "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-kinesis": "0.0.0", "@aws-cdk/aws-kinesisfirehose": "0.0.0", + "@aws-cdk/aws-kms": "0.0.0", "@aws-cdk/aws-lambda": "0.0.0", "@aws-cdk/aws-logs": "0.0.0", "@aws-cdk/aws-sns": "0.0.0", @@ -114,6 +115,7 @@ "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-kinesis": "0.0.0", "@aws-cdk/aws-kinesisfirehose": "0.0.0", + "@aws-cdk/aws-kms": "0.0.0", "@aws-cdk/aws-lambda": "0.0.0", "@aws-cdk/aws-logs": "0.0.0", "@aws-cdk/aws-sns": "0.0.0", diff --git a/packages/@aws-cdk/aws-events-targets/test/sqs/integ.sqs-event-rule-target.expected.json b/packages/@aws-cdk/aws-events-targets/test/sqs/integ.sqs-event-rule-target.expected.json index eebbc3a996344..eb2a7dd26ef5f 100644 --- a/packages/@aws-cdk/aws-events-targets/test/sqs/integ.sqs-event-rule-target.expected.json +++ b/packages/@aws-cdk/aws-events-targets/test/sqs/integ.sqs-event-rule-target.expected.json @@ -1,5 +1,53 @@ { "Resources": { + "MyKey6AB29FA6": { + "Type": "AWS::KMS::Key", + "Properties": { + "KeyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": [ + "kms:Decrypt", + "kms:Encrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*" + ], + "Effect": "Allow", + "Principal": { + "Service": "events.amazonaws.com" + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, "MyRuleA44AB831": { "Type": "AWS::Events::Rule", "Properties": { @@ -20,6 +68,14 @@ }, "MyQueueE6CA6235": { "Type": "AWS::SQS::Queue", + "Properties": { + "KmsMasterKeyId": { + "Fn::GetAtt": [ + "MyKey6AB29FA6", + "Arn" + ] + } + }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, @@ -34,16 +90,6 @@ "sqs:GetQueueAttributes", "sqs:GetQueueUrl" ], - "Condition": { - "ArnEquals": { - "aws:SourceArn": { - "Fn::GetAtt": [ - "MyRuleA44AB831", - "Arn" - ] - } - } - }, "Effect": "Allow", "Principal": { "Service": "events.amazonaws.com" diff --git a/packages/@aws-cdk/aws-events-targets/test/sqs/integ.sqs-event-rule-target.ts b/packages/@aws-cdk/aws-events-targets/test/sqs/integ.sqs-event-rule-target.ts index b58641f727d03..b2b8fb334bff6 100644 --- a/packages/@aws-cdk/aws-events-targets/test/sqs/integ.sqs-event-rule-target.ts +++ b/packages/@aws-cdk/aws-events-targets/test/sqs/integ.sqs-event-rule-target.ts @@ -1,4 +1,5 @@ import * as events from '@aws-cdk/aws-events'; +import * as kms from '@aws-cdk/aws-kms'; import * as sqs from '@aws-cdk/aws-sqs'; import * as cdk from '@aws-cdk/core'; import * as targets from '../../lib'; @@ -12,11 +13,17 @@ const app = new cdk.App(); const stack = new cdk.Stack(app, 'aws-cdk-sqs-event-target'); +const key = new kms.Key(stack, 'MyKey'); + const event = new events.Rule(stack, 'MyRule', { schedule: events.Schedule.rate(cdk.Duration.minutes(1)), }); -const queue = new sqs.Queue(stack, 'MyQueue'); +const queue = new sqs.Queue(stack, 'MyQueue', { + encryption: sqs.QueueEncryption.KMS, + encryptionMasterKey: key, +}); + event.addTarget(new targets.SqsQueue(queue)); app.synth(); From b240f6ece74d129e5f43b210e8ad12f95c4a2971 Mon Sep 17 00:00:00 2001 From: Nick Lynch Date: Thu, 13 May 2021 14:21:05 +0100 Subject: [PATCH 032/134] feat(cloudwatch): GraphWidget supports period and statistic (#14679) Dashboard metric widgets support overridding/setting both period and stat on the widget as a whole. This is often useful in combination with `MathExpression` metrics. Reference: https://docs.aws.amazon.com/AmazonCloudWatch/latest/APIReference/CloudWatch-Dashboard-Body-Structure.html#CloudWatch-Dashboard-Properties-Metric-Widget-Object ---- *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/lib/graph.ts | 20 +++++++++++- .../aws-cloudwatch/test/test.graphs.ts | 31 ++++++++++++++++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/graph.ts b/packages/@aws-cdk/aws-cloudwatch/lib/graph.ts index bc6407a52f8e6..709baba719109 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/graph.ts +++ b/packages/@aws-cdk/aws-cloudwatch/lib/graph.ts @@ -206,7 +206,6 @@ export interface GraphWidgetProps extends MetricWidgetProps { */ readonly liveData?: boolean; - /** * Display this metric * @@ -223,6 +222,23 @@ export interface GraphWidgetProps extends MetricWidgetProps { * @default false */ readonly setPeriodToTimeRange?: boolean; + + /** + * The default period for all metrics in this widget. + * The period is the length of time represented by one data point on the graph. + * This default can be overridden within each metric definition. + * + * @default cdk.Duration.seconds(300) + */ + readonly period?: cdk.Duration; + + /** + * The default statistic to be displayed for each metric. + * This default can be overridden within the definition of each individual metric + * + * @default - The statistic for each metric is used + */ + readonly statistic?: string; } /** @@ -287,6 +303,8 @@ export class GraphWidget extends ConcreteWidget { legend: this.props.legendPosition !== undefined ? { position: this.props.legendPosition } : undefined, liveData: this.props.liveData, setPeriodToTimeRange: this.props.setPeriodToTimeRange, + period: this.props.period?.toSeconds(), + stat: this.props.statistic, }, }]; } diff --git a/packages/@aws-cdk/aws-cloudwatch/test/test.graphs.ts b/packages/@aws-cdk/aws-cloudwatch/test/test.graphs.ts index e6420bbec1955..e5cc11781393d 100644 --- a/packages/@aws-cdk/aws-cloudwatch/test/test.graphs.ts +++ b/packages/@aws-cdk/aws-cloudwatch/test/test.graphs.ts @@ -1,4 +1,4 @@ -import { Stack } from '@aws-cdk/core'; +import { Duration, Stack } from '@aws-cdk/core'; import { Test } from 'nodeunit'; import { Alarm, AlarmWidget, Color, GraphWidget, GraphWidgetView, LegendPosition, LogQueryWidget, Metric, Shading, SingleValueWidget, LogQueryVisualizationType } from '../lib'; @@ -688,4 +688,33 @@ export = { test.done(); }, + + 'GraphWidget supports stat and period'(test: Test) { + // GIVEN + const stack = new Stack(); + const widget = new GraphWidget({ + left: [new Metric({ namespace: 'CDK', metricName: 'Test' })], + statistic: 'Average', + period: Duration.days(2), + }); + + // THEN + test.deepEqual(stack.resolve(widget.toJson()), [{ + type: 'metric', + width: 6, + height: 6, + properties: { + view: 'timeSeries', + region: { Ref: 'AWS::Region' }, + metrics: [ + ['CDK', 'Test'], + ], + yAxis: {}, + stat: 'Average', + period: 172800, + }, + }]); + + test.done(); + }, }; From 82966237556bee9d2bc608a19e7e7558aa2a58b3 Mon Sep 17 00:00:00 2001 From: Hsing-Hui Hsu Date: Thu, 13 May 2021 08:22:23 -0700 Subject: [PATCH 033/134] test(ecs-patterns): update l3 fargate integ tests (#14668) This adds integ tests for NLB fargate services -- previously, there were duplicate ALB fargate services being spun up. Also gives integ test stacks unique names. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- ...g.alb-fargate-service-https.expected.json} | 49 +- ....ts => integ.alb-fargate-service-https.ts} | 2 +- .../fargate/integ.l3-autocreate.expected.json | 252 ++--- .../test/fargate/integ.l3-autocreate.ts | 10 +- .../fargate/integ.l3-vpconly.expected.json | 959 ++---------------- .../test/fargate/integ.l3-vpconly.ts | 22 +- .../test/fargate/integ.l3.expected.json | 353 ++++++- .../aws-ecs-patterns/test/fargate/integ.l3.ts | 17 +- 8 files changed, 531 insertions(+), 1133 deletions(-) rename packages/@aws-cdk/aws-ecs-patterns/test/fargate/{integ.load-balanced-fargate-service.expected.json => integ.alb-fargate-service-https.expected.json} (92%) rename packages/@aws-cdk/aws-ecs-patterns/test/fargate/{integ.load-balanced-fargate-service.ts => integ.alb-fargate-service-https.ts} (94%) diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.load-balanced-fargate-service.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.alb-fargate-service-https.expected.json similarity index 92% rename from packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.load-balanced-fargate-service.expected.json rename to packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.alb-fargate-service-https.expected.json index 1d670f79f58a6..5a67a969707c8 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.load-balanced-fargate-service.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.alb-fargate-service-https.expected.json @@ -10,7 +10,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc" + "Value": "aws-ecs-integ-alb-fg-https/Vpc" } ] } @@ -35,7 +35,7 @@ }, { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PublicSubnet1" + "Value": "aws-ecs-integ-alb-fg-https/Vpc/PublicSubnet1" } ] } @@ -49,7 +49,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PublicSubnet1" + "Value": "aws-ecs-integ-alb-fg-https/Vpc/PublicSubnet1" } ] } @@ -87,7 +87,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PublicSubnet1" + "Value": "aws-ecs-integ-alb-fg-https/Vpc/PublicSubnet1" } ] } @@ -107,7 +107,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PublicSubnet1" + "Value": "aws-ecs-integ-alb-fg-https/Vpc/PublicSubnet1" } ] } @@ -132,7 +132,7 @@ }, { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PublicSubnet2" + "Value": "aws-ecs-integ-alb-fg-https/Vpc/PublicSubnet2" } ] } @@ -146,7 +146,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PublicSubnet2" + "Value": "aws-ecs-integ-alb-fg-https/Vpc/PublicSubnet2" } ] } @@ -184,7 +184,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PublicSubnet2" + "Value": "aws-ecs-integ-alb-fg-https/Vpc/PublicSubnet2" } ] } @@ -204,7 +204,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PublicSubnet2" + "Value": "aws-ecs-integ-alb-fg-https/Vpc/PublicSubnet2" } ] } @@ -229,7 +229,7 @@ }, { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PrivateSubnet1" + "Value": "aws-ecs-integ-alb-fg-https/Vpc/PrivateSubnet1" } ] } @@ -243,7 +243,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PrivateSubnet1" + "Value": "aws-ecs-integ-alb-fg-https/Vpc/PrivateSubnet1" } ] } @@ -291,7 +291,7 @@ }, { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PrivateSubnet2" + "Value": "aws-ecs-integ-alb-fg-https/Vpc/PrivateSubnet2" } ] } @@ -305,7 +305,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PrivateSubnet2" + "Value": "aws-ecs-integ-alb-fg-https/Vpc/PrivateSubnet2" } ] } @@ -339,7 +339,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc" + "Value": "aws-ecs-integ-alb-fg-https/Vpc" } ] } @@ -394,7 +394,7 @@ "myServiceLBSecurityGroupFE0ED608": { "Type": "AWS::EC2::SecurityGroup", "Properties": { - "GroupDescription": "Automatically created Security Group for ELB awsecsintegmyServiceLB1F7A535D", + "GroupDescription": "Automatically created Security Group for ELB awsecsintegalbfghttpsmyServiceLB8BEE3C49", "SecurityGroupIngress": [ { "CidrIp": "0.0.0.0/0", @@ -416,7 +416,7 @@ } } }, - "myServiceLBSecurityGrouptoawsecsintegmyServiceSecurityGroup8DAB521180B6703B07": { + "myServiceLBSecurityGrouptoawsecsintegalbfghttpsmyServiceSecurityGroup49C558AD803FB613FF": { "Type": "AWS::EC2::SecurityGroupEgress", "Properties": { "GroupId": { @@ -451,15 +451,15 @@ "LoadBalancerArn": { "Ref": "myServiceLB168895E1" }, - "Port": 443, - "Protocol": "HTTPS", "Certificates": [ { "CertificateArn": { "Ref": "myServiceCertificate152F9DDA" } } - ] + ], + "Port": 443, + "Protocol": "HTTPS" } }, "myServiceLBPublicListenerECSGroup17E9BBC1": { @@ -513,8 +513,7 @@ "Type": "A", "AliasTarget": { "DNSName": { - "Fn::Join": - [ + "Fn::Join": [ "", [ "dualstack.", @@ -589,7 +588,7 @@ "Arn" ] }, - "Family": "awsecsintegmyServiceTaskDefA3A33D18", + "Family": "awsecsintegalbfghttpsmyServiceTaskDefD8ABFBF2", "Memory": "512", "NetworkMode": "awsvpc", "RequiresCompatibilities": [ @@ -709,7 +708,7 @@ "myServiceSecurityGroupC3B9D4E0": { "Type": "AWS::EC2::SecurityGroup", "Properties": { - "GroupDescription": "aws-ecs-integ/myService/Service/SecurityGroup", + "GroupDescription": "aws-ecs-integ-alb-fg-https/myService/Service/SecurityGroup", "SecurityGroupEgress": [ { "CidrIp": "0.0.0.0/0", @@ -722,7 +721,7 @@ } } }, - "myServiceSecurityGroupfromawsecsintegmyServiceLBSecurityGroupFA544FE5800A81885C": { + "myServiceSecurityGroupfromawsecsintegalbfghttpsmyServiceLBSecurityGroupA934AF89808E9FB7A3": { "Type": "AWS::EC2::SecurityGroupIngress", "Properties": { "IpProtocol": "tcp", @@ -767,4 +766,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.load-balanced-fargate-service.ts b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.alb-fargate-service-https.ts similarity index 94% rename from packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.load-balanced-fargate-service.ts rename to packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.alb-fargate-service-https.ts index b78ff8da2304f..fe6940d272cc9 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.load-balanced-fargate-service.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.alb-fargate-service-https.ts @@ -7,7 +7,7 @@ import { App, Stack } from '@aws-cdk/core'; import { ApplicationLoadBalancedFargateService } from '../../lib'; const app = new App(); -const stack = new Stack(app, 'aws-ecs-integ'); +const stack = new Stack(app, 'aws-ecs-integ-alb-fg-https'); const vpc = new Vpc(stack, 'Vpc', { maxAzs: 2 }); const cluster = new Cluster(stack, 'Cluster', { vpc }); diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-autocreate.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-autocreate.expected.json index 5813cd78e41f3..778523d6bc6df 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-autocreate.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-autocreate.expected.json @@ -1,6 +1,6 @@ { "Resources": { - "L3LB212FC0E0": { + "ALBFargateServiceLB64A0074E": { "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer", "Properties": { "LoadBalancerAttributes": [ @@ -13,7 +13,7 @@ "SecurityGroups": [ { "Fn::GetAtt": [ - "L3LBSecurityGroupEDE61198", + "ALBFargateServiceLBSecurityGroup5DC3060E", "GroupId" ] } @@ -33,10 +33,10 @@ "EcsDefaultClusterMnL3mNNYNVpcPublicSubnet2DefaultRouteB1375520" ] }, - "L3LBSecurityGroupEDE61198": { + "ALBFargateServiceLBSecurityGroup5DC3060E": { "Type": "AWS::EC2::SecurityGroup", "Properties": { - "GroupDescription": "Automatically created Security Group for ELB awsecsintegL3LB6453BA0A", + "GroupDescription": "Automatically created Security Group for ELB awsecsintegl3autocreateALBFargateServiceLB31EA4AB6", "SecurityGroupIngress": [ { "CidrIp": "0.0.0.0/0", @@ -51,12 +51,12 @@ } } }, - "L3LBSecurityGrouptoawsecsintegL3ServiceSecurityGroup7B96C87F8094933E0A": { + "ALBFargateServiceLBSecurityGrouptoawsecsintegl3autocreateALBFargateServiceSecurityGroup6F9400B580770A6C60": { "Type": "AWS::EC2::SecurityGroupEgress", "Properties": { "GroupId": { "Fn::GetAtt": [ - "L3LBSecurityGroupEDE61198", + "ALBFargateServiceLBSecurityGroup5DC3060E", "GroupId" ] }, @@ -64,7 +64,7 @@ "Description": "Load balancer to target", "DestinationSecurityGroupId": { "Fn::GetAtt": [ - "L3ServiceSecurityGroup677B0897", + "ALBFargateServiceSecurityGroup82F7A67E", "GroupId" ] }, @@ -72,25 +72,25 @@ "ToPort": 80 } }, - "L3LBPublicListener156FFC0F": { + "ALBFargateServiceLBPublicListener3489002A": { "Type": "AWS::ElasticLoadBalancingV2::Listener", "Properties": { "DefaultActions": [ { "TargetGroupArn": { - "Ref": "L3LBPublicListenerECSGroup648EEA11" + "Ref": "ALBFargateServiceLBPublicListenerECSGroup6871FB8C" }, "Type": "forward" } ], "LoadBalancerArn": { - "Ref": "L3LB212FC0E0" + "Ref": "ALBFargateServiceLB64A0074E" }, "Port": 80, "Protocol": "HTTP" } }, - "L3LBPublicListenerECSGroup648EEA11": { + "ALBFargateServiceLBPublicListenerECSGroup6871FB8C": { "Type": "AWS::ElasticLoadBalancingV2::TargetGroup", "Properties": { "Port": 80, @@ -101,7 +101,7 @@ } } }, - "L3TaskDefTaskRole21C75D10": { + "ALBFargateServiceTaskDefTaskRole11408723": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { @@ -118,7 +118,7 @@ } } }, - "L3TaskDef48D8ACB8": { + "ALBFargateServiceTaskDefF69F17D6": { "Type": "AWS::ECS::TaskDefinition", "Properties": { "ContainerDefinitions": [ @@ -129,9 +129,9 @@ "LogDriver": "awslogs", "Options": { "awslogs-group": { - "Ref": "L3TaskDefwebLogGroupC6E4A38A" + "Ref": "ALBFargateServiceTaskDefwebLogGroup7073A41D" }, - "awslogs-stream-prefix": "L3", + "awslogs-stream-prefix": "ALBFargateService", "awslogs-region": { "Ref": "AWS::Region" } @@ -149,11 +149,11 @@ "Cpu": "512", "ExecutionRoleArn": { "Fn::GetAtt": [ - "L3TaskDefExecutionRole49AF0996", + "ALBFargateServiceTaskDefExecutionRole9E885E7B", "Arn" ] }, - "Family": "awsecsintegL3TaskDefAA25240E", + "Family": "awsecsintegl3autocreateALBFargateServiceTaskDefDA905826", "Memory": "1024", "NetworkMode": "awsvpc", "RequiresCompatibilities": [ @@ -161,18 +161,18 @@ ], "TaskRoleArn": { "Fn::GetAtt": [ - "L3TaskDefTaskRole21C75D10", + "ALBFargateServiceTaskDefTaskRole11408723", "Arn" ] } } }, - "L3TaskDefwebLogGroupC6E4A38A": { + "ALBFargateServiceTaskDefwebLogGroup7073A41D": { "Type": "AWS::Logs::LogGroup", "UpdateReplacePolicy": "Retain", "DeletionPolicy": "Retain" }, - "L3TaskDefExecutionRole49AF0996": { + "ALBFargateServiceTaskDefExecutionRole9E885E7B": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { @@ -189,7 +189,7 @@ } } }, - "L3TaskDefExecutionRoleDefaultPolicy4656E642": { + "ALBFargateServiceTaskDefExecutionRoleDefaultPolicy574B9EAD": { "Type": "AWS::IAM::Policy", "Properties": { "PolicyDocument": { @@ -202,7 +202,7 @@ "Effect": "Allow", "Resource": { "Fn::GetAtt": [ - "L3TaskDefwebLogGroupC6E4A38A", + "ALBFargateServiceTaskDefwebLogGroup7073A41D", "Arn" ] } @@ -210,15 +210,15 @@ ], "Version": "2012-10-17" }, - "PolicyName": "L3TaskDefExecutionRoleDefaultPolicy4656E642", + "PolicyName": "ALBFargateServiceTaskDefExecutionRoleDefaultPolicy574B9EAD", "Roles": [ { - "Ref": "L3TaskDefExecutionRole49AF0996" + "Ref": "ALBFargateServiceTaskDefExecutionRole9E885E7B" } ] } }, - "L3Service616D5A93": { + "ALBFargateService90FDCE10": { "Type": "AWS::ECS::Service", "Properties": { "Cluster": { @@ -236,7 +236,7 @@ "ContainerName": "web", "ContainerPort": 80, "TargetGroupArn": { - "Ref": "L3LBPublicListenerECSGroup648EEA11" + "Ref": "ALBFargateServiceLBPublicListenerECSGroup6871FB8C" } } ], @@ -246,7 +246,7 @@ "SecurityGroups": [ { "Fn::GetAtt": [ - "L3ServiceSecurityGroup677B0897", + "ALBFargateServiceSecurityGroup82F7A67E", "GroupId" ] } @@ -262,18 +262,18 @@ } }, "TaskDefinition": { - "Ref": "L3TaskDef48D8ACB8" + "Ref": "ALBFargateServiceTaskDefF69F17D6" } }, "DependsOn": [ - "L3LBPublicListenerECSGroup648EEA11", - "L3LBPublicListener156FFC0F" + "ALBFargateServiceLBPublicListenerECSGroup6871FB8C", + "ALBFargateServiceLBPublicListener3489002A" ] }, - "L3ServiceSecurityGroup677B0897": { + "ALBFargateServiceSecurityGroup82F7A67E": { "Type": "AWS::EC2::SecurityGroup", "Properties": { - "GroupDescription": "aws-ecs-integ/L3/Service/SecurityGroup", + "GroupDescription": "aws-ecs-integ-l3-autocreate/ALBFargateService/Service/SecurityGroup", "SecurityGroupEgress": [ { "CidrIp": "0.0.0.0/0", @@ -286,7 +286,7 @@ } } }, - "L3ServiceSecurityGroupfromawsecsintegL3LBSecurityGroupA70DA46C80DBDFBCD6": { + "ALBFargateServiceSecurityGroupfromawsecsintegl3autocreateALBFargateServiceLBSecurityGroupD565E0BF802E7B8344": { "Type": "AWS::EC2::SecurityGroupIngress", "Properties": { "IpProtocol": "tcp", @@ -294,13 +294,13 @@ "FromPort": 80, "GroupId": { "Fn::GetAtt": [ - "L3ServiceSecurityGroup677B0897", + "ALBFargateServiceSecurityGroup82F7A67E", "GroupId" ] }, "SourceSecurityGroupId": { "Fn::GetAtt": [ - "L3LBSecurityGroupEDE61198", + "ALBFargateServiceLBSecurityGroup5DC3060E", "GroupId" ] }, @@ -320,7 +320,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/EcsDefaultClusterMnL3mNNYN/Vpc" + "Value": "aws-ecs-integ-l3-autocreate/EcsDefaultClusterMnL3mNNYN/Vpc" } ] } @@ -345,7 +345,7 @@ }, { "Key": "Name", - "Value": "aws-ecs-integ/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet1" + "Value": "aws-ecs-integ-l3-autocreate/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet1" } ] } @@ -359,7 +359,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet1" + "Value": "aws-ecs-integ-l3-autocreate/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet1" } ] } @@ -397,7 +397,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet1" + "Value": "aws-ecs-integ-l3-autocreate/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet1" } ] } @@ -417,7 +417,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet1" + "Value": "aws-ecs-integ-l3-autocreate/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet1" } ] } @@ -442,7 +442,7 @@ }, { "Key": "Name", - "Value": "aws-ecs-integ/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet2" + "Value": "aws-ecs-integ-l3-autocreate/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet2" } ] } @@ -456,7 +456,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet2" + "Value": "aws-ecs-integ-l3-autocreate/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet2" } ] } @@ -494,7 +494,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet2" + "Value": "aws-ecs-integ-l3-autocreate/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet2" } ] } @@ -514,7 +514,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet2" + "Value": "aws-ecs-integ-l3-autocreate/EcsDefaultClusterMnL3mNNYN/Vpc/PublicSubnet2" } ] } @@ -539,7 +539,7 @@ }, { "Key": "Name", - "Value": "aws-ecs-integ/EcsDefaultClusterMnL3mNNYN/Vpc/PrivateSubnet1" + "Value": "aws-ecs-integ-l3-autocreate/EcsDefaultClusterMnL3mNNYN/Vpc/PrivateSubnet1" } ] } @@ -553,7 +553,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/EcsDefaultClusterMnL3mNNYN/Vpc/PrivateSubnet1" + "Value": "aws-ecs-integ-l3-autocreate/EcsDefaultClusterMnL3mNNYN/Vpc/PrivateSubnet1" } ] } @@ -601,7 +601,7 @@ }, { "Key": "Name", - "Value": "aws-ecs-integ/EcsDefaultClusterMnL3mNNYN/Vpc/PrivateSubnet2" + "Value": "aws-ecs-integ-l3-autocreate/EcsDefaultClusterMnL3mNNYN/Vpc/PrivateSubnet2" } ] } @@ -615,7 +615,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/EcsDefaultClusterMnL3mNNYN/Vpc/PrivateSubnet2" + "Value": "aws-ecs-integ-l3-autocreate/EcsDefaultClusterMnL3mNNYN/Vpc/PrivateSubnet2" } ] } @@ -649,7 +649,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/EcsDefaultClusterMnL3mNNYN/Vpc" + "Value": "aws-ecs-integ-l3-autocreate/EcsDefaultClusterMnL3mNNYN/Vpc" } ] } @@ -665,7 +665,7 @@ } } }, - "L3bLBB8FADA4E": { + "NLBFargateServiceLB659EC17C": { "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer", "Properties": { "LoadBalancerAttributes": [ @@ -675,14 +675,6 @@ } ], "Scheme": "internet-facing", - "SecurityGroups": [ - { - "Fn::GetAtt": [ - "L3bLBSecurityGroup7A2B0AA0", - "GroupId" - ] - } - ], "Subnets": [ { "Ref": "EcsDefaultClusterMnL3mNNYNVpcPublicSubnet1Subnet3C273B99" @@ -691,82 +683,43 @@ "Ref": "EcsDefaultClusterMnL3mNNYNVpcPublicSubnet2Subnet95FF715A" } ], - "Type": "application" + "Type": "network" }, "DependsOn": [ "EcsDefaultClusterMnL3mNNYNVpcPublicSubnet1DefaultRouteFF4E2178", "EcsDefaultClusterMnL3mNNYNVpcPublicSubnet2DefaultRouteB1375520" ] }, - "L3bLBSecurityGroup7A2B0AA0": { - "Type": "AWS::EC2::SecurityGroup", - "Properties": { - "GroupDescription": "Automatically created Security Group for ELB awsecsintegL3bLB9C1497A7", - "SecurityGroupIngress": [ - { - "CidrIp": "0.0.0.0/0", - "Description": "Allow from anyone on port 80", - "FromPort": 80, - "IpProtocol": "tcp", - "ToPort": 80 - } - ], - "VpcId": { - "Ref": "EcsDefaultClusterMnL3mNNYNVpc7788A521" - } - } - }, - "L3bLBSecurityGrouptoawsecsintegL3bServiceSecurityGroupC2BD1A598019C4C37D": { - "Type": "AWS::EC2::SecurityGroupEgress", - "Properties": { - "GroupId": { - "Fn::GetAtt": [ - "L3bLBSecurityGroup7A2B0AA0", - "GroupId" - ] - }, - "IpProtocol": "tcp", - "Description": "Load balancer to target", - "DestinationSecurityGroupId": { - "Fn::GetAtt": [ - "L3bServiceSecurityGroupA8DA736E", - "GroupId" - ] - }, - "FromPort": 80, - "ToPort": 80 - } - }, - "L3bLBPublicListenerA825925B": { + "NLBFargateServiceLBPublicListenerB0DCA73C": { "Type": "AWS::ElasticLoadBalancingV2::Listener", "Properties": { "DefaultActions": [ { "TargetGroupArn": { - "Ref": "L3bLBPublicListenerECSGroup0070C5CA" + "Ref": "NLBFargateServiceLBPublicListenerECSGroupC469CAA2" }, "Type": "forward" } ], "LoadBalancerArn": { - "Ref": "L3bLBB8FADA4E" + "Ref": "NLBFargateServiceLB659EC17C" }, "Port": 80, - "Protocol": "HTTP" + "Protocol": "TCP" } }, - "L3bLBPublicListenerECSGroup0070C5CA": { + "NLBFargateServiceLBPublicListenerECSGroupC469CAA2": { "Type": "AWS::ElasticLoadBalancingV2::TargetGroup", "Properties": { "Port": 80, - "Protocol": "HTTP", + "Protocol": "TCP", "TargetType": "ip", "VpcId": { "Ref": "EcsDefaultClusterMnL3mNNYNVpc7788A521" } } }, - "L3bTaskDefTaskRoleADAB80C8": { + "NLBFargateServiceTaskDefTaskRole6C88F40B": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { @@ -783,7 +736,7 @@ } } }, - "L3bTaskDef5506864D": { + "NLBFargateServiceTaskDefB836FA89": { "Type": "AWS::ECS::TaskDefinition", "Properties": { "ContainerDefinitions": [ @@ -794,9 +747,9 @@ "LogDriver": "awslogs", "Options": { "awslogs-group": { - "Ref": "L3bTaskDefwebLogGroup8E5F1183" + "Ref": "NLBFargateServiceTaskDefwebLogGroupC4A42FE2" }, - "awslogs-stream-prefix": "L3b", + "awslogs-stream-prefix": "NLBFargateService", "awslogs-region": { "Ref": "AWS::Region" } @@ -814,11 +767,11 @@ "Cpu": "512", "ExecutionRoleArn": { "Fn::GetAtt": [ - "L3bTaskDefExecutionRole9A3E2688", + "NLBFargateServiceTaskDefExecutionRoleF6D642D5", "Arn" ] }, - "Family": "awsecsintegL3bTaskDef24D7E4F1", + "Family": "awsecsintegl3autocreateNLBFargateServiceTaskDef7AC6C114", "Memory": "1024", "NetworkMode": "awsvpc", "RequiresCompatibilities": [ @@ -826,18 +779,18 @@ ], "TaskRoleArn": { "Fn::GetAtt": [ - "L3bTaskDefTaskRoleADAB80C8", + "NLBFargateServiceTaskDefTaskRole6C88F40B", "Arn" ] } } }, - "L3bTaskDefwebLogGroup8E5F1183": { + "NLBFargateServiceTaskDefwebLogGroupC4A42FE2": { "Type": "AWS::Logs::LogGroup", "UpdateReplacePolicy": "Retain", "DeletionPolicy": "Retain" }, - "L3bTaskDefExecutionRole9A3E2688": { + "NLBFargateServiceTaskDefExecutionRoleF6D642D5": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { @@ -854,7 +807,7 @@ } } }, - "L3bTaskDefExecutionRoleDefaultPolicy0CEA0ED2": { + "NLBFargateServiceTaskDefExecutionRoleDefaultPolicy90080805": { "Type": "AWS::IAM::Policy", "Properties": { "PolicyDocument": { @@ -867,7 +820,7 @@ "Effect": "Allow", "Resource": { "Fn::GetAtt": [ - "L3bTaskDefwebLogGroup8E5F1183", + "NLBFargateServiceTaskDefwebLogGroupC4A42FE2", "Arn" ] } @@ -875,15 +828,15 @@ ], "Version": "2012-10-17" }, - "PolicyName": "L3bTaskDefExecutionRoleDefaultPolicy0CEA0ED2", + "PolicyName": "NLBFargateServiceTaskDefExecutionRoleDefaultPolicy90080805", "Roles": [ { - "Ref": "L3bTaskDefExecutionRole9A3E2688" + "Ref": "NLBFargateServiceTaskDefExecutionRoleF6D642D5" } ] } }, - "L3bServiceF9D33D5A": { + "NLBFargateServiceB92AC095": { "Type": "AWS::ECS::Service", "Properties": { "Cluster": { @@ -901,7 +854,7 @@ "ContainerName": "web", "ContainerPort": 80, "TargetGroupArn": { - "Ref": "L3bLBPublicListenerECSGroup0070C5CA" + "Ref": "NLBFargateServiceLBPublicListenerECSGroupC469CAA2" } } ], @@ -911,7 +864,7 @@ "SecurityGroups": [ { "Fn::GetAtt": [ - "L3bServiceSecurityGroupA8DA736E", + "NLBFargateServiceSecurityGroup9D81388B", "GroupId" ] } @@ -927,18 +880,18 @@ } }, "TaskDefinition": { - "Ref": "L3bTaskDef5506864D" + "Ref": "NLBFargateServiceTaskDefB836FA89" } }, "DependsOn": [ - "L3bLBPublicListenerECSGroup0070C5CA", - "L3bLBPublicListenerA825925B" + "NLBFargateServiceLBPublicListenerECSGroupC469CAA2", + "NLBFargateServiceLBPublicListenerB0DCA73C" ] }, - "L3bServiceSecurityGroupA8DA736E": { + "NLBFargateServiceSecurityGroup9D81388B": { "Type": "AWS::EC2::SecurityGroup", "Properties": { - "GroupDescription": "aws-ecs-integ/L3b/Service/SecurityGroup", + "GroupDescription": "aws-ecs-integ-l3-autocreate/NLBFargateService/Service/SecurityGroup", "SecurityGroupEgress": [ { "CidrIp": "0.0.0.0/0", @@ -950,39 +903,18 @@ "Ref": "EcsDefaultClusterMnL3mNNYNVpc7788A521" } } - }, - "L3bServiceSecurityGroupfromawsecsintegL3bLBSecurityGroupA7B79A628034042CE5": { - "Type": "AWS::EC2::SecurityGroupIngress", - "Properties": { - "IpProtocol": "tcp", - "Description": "Load balancer to target", - "FromPort": 80, - "GroupId": { - "Fn::GetAtt": [ - "L3bServiceSecurityGroupA8DA736E", - "GroupId" - ] - }, - "SourceSecurityGroupId": { - "Fn::GetAtt": [ - "L3bLBSecurityGroup7A2B0AA0", - "GroupId" - ] - }, - "ToPort": 80 - } } }, "Outputs": { - "L3LoadBalancerDNSC6CB4A70": { + "ALBFargateServiceLoadBalancerDNSAFB2EDDB": { "Value": { "Fn::GetAtt": [ - "L3LB212FC0E0", + "ALBFargateServiceLB64A0074E", "DNSName" ] } }, - "L3ServiceURL0F065F2D": { + "ALBFargateServiceServiceURL4A19CF25": { "Value": { "Fn::Join": [ "", @@ -990,7 +922,7 @@ "http://", { "Fn::GetAtt": [ - "L3LB212FC0E0", + "ALBFargateServiceLB64A0074E", "DNSName" ] } @@ -998,29 +930,13 @@ ] } }, - "L3bLoadBalancerDNSED096132": { + "NLBFargateServiceLoadBalancerDNSC2B2922F": { "Value": { "Fn::GetAtt": [ - "L3bLBB8FADA4E", + "NLBFargateServiceLB659EC17C", "DNSName" ] } - }, - "L3bServiceURL0EDED888": { - "Value": { - "Fn::Join": [ - "", - [ - "http://", - { - "Fn::GetAtt": [ - "L3bLBB8FADA4E", - "DNSName" - ] - } - ] - ] - } } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-autocreate.ts b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-autocreate.ts index aae9efc969bac..3644cbbe8ec9f 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-autocreate.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-autocreate.ts @@ -3,9 +3,12 @@ import * as cdk from '@aws-cdk/core'; import * as ecsPatterns from '../../lib'; const app = new cdk.App(); -const stack = new cdk.Stack(app, 'aws-ecs-integ'); +const stack = new cdk.Stack(app, 'aws-ecs-integ-l3-autocreate'); -new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'L3', { +// No VPC or Cluster specified + +// Create ALB service +new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'ALBFargateService', { memoryLimitMiB: 1024, cpu: 512, taskImageOptions: { @@ -13,7 +16,8 @@ new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'L3', { }, }); -new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'L3b', { +// Create NLB service +new ecsPatterns.NetworkLoadBalancedFargateService(stack, 'NLBFargateService', { memoryLimitMiB: 1024, cpu: 512, taskImageOptions: { diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-vpconly.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-vpconly.expected.json index 5556df70a59b7..f221c99ccf4fc 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-vpconly.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-vpconly.expected.json @@ -10,7 +10,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc" + "Value": "aws-ecs-integ-l3-vpconly/Vpc" } ] } @@ -35,7 +35,7 @@ }, { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PublicSubnet1" + "Value": "aws-ecs-integ-l3-vpconly/Vpc/PublicSubnet1" } ] } @@ -49,7 +49,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PublicSubnet1" + "Value": "aws-ecs-integ-l3-vpconly/Vpc/PublicSubnet1" } ] } @@ -87,7 +87,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PublicSubnet1" + "Value": "aws-ecs-integ-l3-vpconly/Vpc/PublicSubnet1" } ] } @@ -107,7 +107,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PublicSubnet1" + "Value": "aws-ecs-integ-l3-vpconly/Vpc/PublicSubnet1" } ] } @@ -132,7 +132,7 @@ }, { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PublicSubnet2" + "Value": "aws-ecs-integ-l3-vpconly/Vpc/PublicSubnet2" } ] } @@ -146,7 +146,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PublicSubnet2" + "Value": "aws-ecs-integ-l3-vpconly/Vpc/PublicSubnet2" } ] } @@ -184,7 +184,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PublicSubnet2" + "Value": "aws-ecs-integ-l3-vpconly/Vpc/PublicSubnet2" } ] } @@ -204,7 +204,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PublicSubnet2" + "Value": "aws-ecs-integ-l3-vpconly/Vpc/PublicSubnet2" } ] } @@ -229,7 +229,7 @@ }, { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PrivateSubnet1" + "Value": "aws-ecs-integ-l3-vpconly/Vpc/PrivateSubnet1" } ] } @@ -243,7 +243,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PrivateSubnet1" + "Value": "aws-ecs-integ-l3-vpconly/Vpc/PrivateSubnet1" } ] } @@ -291,7 +291,7 @@ }, { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PrivateSubnet2" + "Value": "aws-ecs-integ-l3-vpconly/Vpc/PrivateSubnet2" } ] } @@ -305,7 +305,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PrivateSubnet2" + "Value": "aws-ecs-integ-l3-vpconly/Vpc/PrivateSubnet2" } ] } @@ -339,7 +339,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc" + "Value": "aws-ecs-integ-l3-vpconly/Vpc" } ] } @@ -355,7 +355,7 @@ } } }, - "L3LB212FC0E0": { + "ALBFargateServiceLB64A0074E": { "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer", "Properties": { "LoadBalancerAttributes": [ @@ -368,7 +368,7 @@ "SecurityGroups": [ { "Fn::GetAtt": [ - "L3LBSecurityGroupEDE61198", + "ALBFargateServiceLBSecurityGroup5DC3060E", "GroupId" ] } @@ -388,10 +388,10 @@ "VpcPublicSubnet2DefaultRoute97F91067" ] }, - "L3LBSecurityGroupEDE61198": { + "ALBFargateServiceLBSecurityGroup5DC3060E": { "Type": "AWS::EC2::SecurityGroup", "Properties": { - "GroupDescription": "Automatically created Security Group for ELB awsecsintegL3LB6453BA0A", + "GroupDescription": "Automatically created Security Group for ELB awsecsintegl3vpconlyALBFargateServiceLBE08492C1", "SecurityGroupIngress": [ { "CidrIp": "0.0.0.0/0", @@ -406,12 +406,12 @@ } } }, - "L3LBSecurityGrouptoawsecsintegL3ServiceSecurityGroup7B96C87F8094933E0A": { + "ALBFargateServiceLBSecurityGrouptoawsecsintegl3vpconlyALBFargateServiceSecurityGroup3700A42180D1AB9DBC": { "Type": "AWS::EC2::SecurityGroupEgress", "Properties": { "GroupId": { "Fn::GetAtt": [ - "L3LBSecurityGroupEDE61198", + "ALBFargateServiceLBSecurityGroup5DC3060E", "GroupId" ] }, @@ -419,7 +419,7 @@ "Description": "Load balancer to target", "DestinationSecurityGroupId": { "Fn::GetAtt": [ - "L3ServiceSecurityGroup677B0897", + "ALBFargateServiceSecurityGroup82F7A67E", "GroupId" ] }, @@ -427,25 +427,25 @@ "ToPort": 80 } }, - "L3LBPublicListener156FFC0F": { + "ALBFargateServiceLBPublicListener3489002A": { "Type": "AWS::ElasticLoadBalancingV2::Listener", "Properties": { "DefaultActions": [ { "TargetGroupArn": { - "Ref": "L3LBPublicListenerECSGroup648EEA11" + "Ref": "ALBFargateServiceLBPublicListenerECSGroup6871FB8C" }, "Type": "forward" } ], "LoadBalancerArn": { - "Ref": "L3LB212FC0E0" + "Ref": "ALBFargateServiceLB64A0074E" }, "Port": 80, "Protocol": "HTTP" } }, - "L3LBPublicListenerECSGroup648EEA11": { + "ALBFargateServiceLBPublicListenerECSGroup6871FB8C": { "Type": "AWS::ElasticLoadBalancingV2::TargetGroup", "Properties": { "Port": 80, @@ -456,7 +456,7 @@ } } }, - "L3TaskDefTaskRole21C75D10": { + "ALBFargateServiceTaskDefTaskRole11408723": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { @@ -473,7 +473,7 @@ } } }, - "L3TaskDef48D8ACB8": { + "ALBFargateServiceTaskDefF69F17D6": { "Type": "AWS::ECS::TaskDefinition", "Properties": { "ContainerDefinitions": [ @@ -484,9 +484,9 @@ "LogDriver": "awslogs", "Options": { "awslogs-group": { - "Ref": "L3TaskDefwebLogGroupC6E4A38A" + "Ref": "ALBFargateServiceTaskDefwebLogGroup7073A41D" }, - "awslogs-stream-prefix": "L3", + "awslogs-stream-prefix": "ALBFargateService", "awslogs-region": { "Ref": "AWS::Region" } @@ -504,11 +504,11 @@ "Cpu": "512", "ExecutionRoleArn": { "Fn::GetAtt": [ - "L3TaskDefExecutionRole49AF0996", + "ALBFargateServiceTaskDefExecutionRole9E885E7B", "Arn" ] }, - "Family": "awsecsintegL3TaskDefAA25240E", + "Family": "awsecsintegl3vpconlyALBFargateServiceTaskDef846555AE", "Memory": "1024", "NetworkMode": "awsvpc", "RequiresCompatibilities": [ @@ -516,18 +516,18 @@ ], "TaskRoleArn": { "Fn::GetAtt": [ - "L3TaskDefTaskRole21C75D10", + "ALBFargateServiceTaskDefTaskRole11408723", "Arn" ] } } }, - "L3TaskDefwebLogGroupC6E4A38A": { + "ALBFargateServiceTaskDefwebLogGroup7073A41D": { "Type": "AWS::Logs::LogGroup", "UpdateReplacePolicy": "Retain", "DeletionPolicy": "Retain" }, - "L3TaskDefExecutionRole49AF0996": { + "ALBFargateServiceTaskDefExecutionRole9E885E7B": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { @@ -544,7 +544,7 @@ } } }, - "L3TaskDefExecutionRoleDefaultPolicy4656E642": { + "ALBFargateServiceTaskDefExecutionRoleDefaultPolicy574B9EAD": { "Type": "AWS::IAM::Policy", "Properties": { "PolicyDocument": { @@ -557,7 +557,7 @@ "Effect": "Allow", "Resource": { "Fn::GetAtt": [ - "L3TaskDefwebLogGroupC6E4A38A", + "ALBFargateServiceTaskDefwebLogGroup7073A41D", "Arn" ] } @@ -565,15 +565,15 @@ ], "Version": "2012-10-17" }, - "PolicyName": "L3TaskDefExecutionRoleDefaultPolicy4656E642", + "PolicyName": "ALBFargateServiceTaskDefExecutionRoleDefaultPolicy574B9EAD", "Roles": [ { - "Ref": "L3TaskDefExecutionRole49AF0996" + "Ref": "ALBFargateServiceTaskDefExecutionRole9E885E7B" } ] } }, - "L3Service616D5A93": { + "ALBFargateService90FDCE10": { "Type": "AWS::ECS::Service", "Properties": { "Cluster": { @@ -591,7 +591,7 @@ "ContainerName": "web", "ContainerPort": 80, "TargetGroupArn": { - "Ref": "L3LBPublicListenerECSGroup648EEA11" + "Ref": "ALBFargateServiceLBPublicListenerECSGroup6871FB8C" } } ], @@ -601,7 +601,7 @@ "SecurityGroups": [ { "Fn::GetAtt": [ - "L3ServiceSecurityGroup677B0897", + "ALBFargateServiceSecurityGroup82F7A67E", "GroupId" ] } @@ -617,18 +617,18 @@ } }, "TaskDefinition": { - "Ref": "L3TaskDef48D8ACB8" + "Ref": "ALBFargateServiceTaskDefF69F17D6" } }, "DependsOn": [ - "L3LBPublicListenerECSGroup648EEA11", - "L3LBPublicListener156FFC0F" + "ALBFargateServiceLBPublicListenerECSGroup6871FB8C", + "ALBFargateServiceLBPublicListener3489002A" ] }, - "L3ServiceSecurityGroup677B0897": { + "ALBFargateServiceSecurityGroup82F7A67E": { "Type": "AWS::EC2::SecurityGroup", "Properties": { - "GroupDescription": "aws-ecs-integ/L3/Service/SecurityGroup", + "GroupDescription": "aws-ecs-integ-l3-vpconly/ALBFargateService/Service/SecurityGroup", "SecurityGroupEgress": [ { "CidrIp": "0.0.0.0/0", @@ -641,7 +641,7 @@ } } }, - "L3ServiceSecurityGroupfromawsecsintegL3LBSecurityGroupA70DA46C80DBDFBCD6": { + "ALBFargateServiceSecurityGroupfromawsecsintegl3vpconlyALBFargateServiceLBSecurityGroup96E9BBBD8073FB670D": { "Type": "AWS::EC2::SecurityGroupIngress", "Properties": { "IpProtocol": "tcp", @@ -649,13 +649,13 @@ "FromPort": 80, "GroupId": { "Fn::GetAtt": [ - "L3ServiceSecurityGroup677B0897", + "ALBFargateServiceSecurityGroup82F7A67E", "GroupId" ] }, "SourceSecurityGroupId": { "Fn::GetAtt": [ - "L3LBSecurityGroupEDE61198", + "ALBFargateServiceLBSecurityGroup5DC3060E", "GroupId" ] }, @@ -665,672 +665,7 @@ "EcsDefaultClusterMnL3mNNYNVpc18E0451A": { "Type": "AWS::ECS::Cluster" }, - "Vpc299FDBC5F": { - "Type": "AWS::EC2::VPC", - "Properties": { - "CidrBlock": "10.0.0.0/16", - "EnableDnsHostnames": true, - "EnableDnsSupport": true, - "InstanceTenancy": "default", - "Tags": [ - { - "Key": "Name", - "Value": "aws-ecs-integ/Vpc2" - } - ] - } - }, - "Vpc2PublicSubnet1Subnet758D49A9": { - "Type": "AWS::EC2::Subnet", - "Properties": { - "CidrBlock": "10.0.0.0/18", - "VpcId": { - "Ref": "Vpc299FDBC5F" - }, - "AvailabilityZone": "test-region-1a", - "MapPublicIpOnLaunch": true, - "Tags": [ - { - "Key": "aws-cdk:subnet-name", - "Value": "Public" - }, - { - "Key": "aws-cdk:subnet-type", - "Value": "Public" - }, - { - "Key": "Name", - "Value": "aws-ecs-integ/Vpc2/PublicSubnet1" - } - ] - } - }, - "Vpc2PublicSubnet1RouteTable424A19D4": { - "Type": "AWS::EC2::RouteTable", - "Properties": { - "VpcId": { - "Ref": "Vpc299FDBC5F" - }, - "Tags": [ - { - "Key": "Name", - "Value": "aws-ecs-integ/Vpc2/PublicSubnet1" - } - ] - } - }, - "Vpc2PublicSubnet1RouteTableAssociationA1651F3A": { - "Type": "AWS::EC2::SubnetRouteTableAssociation", - "Properties": { - "RouteTableId": { - "Ref": "Vpc2PublicSubnet1RouteTable424A19D4" - }, - "SubnetId": { - "Ref": "Vpc2PublicSubnet1Subnet758D49A9" - } - } - }, - "Vpc2PublicSubnet1DefaultRoute64172CA2": { - "Type": "AWS::EC2::Route", - "Properties": { - "RouteTableId": { - "Ref": "Vpc2PublicSubnet1RouteTable424A19D4" - }, - "DestinationCidrBlock": "0.0.0.0/0", - "GatewayId": { - "Ref": "Vpc2IGWB10A76EB" - } - }, - "DependsOn": [ - "Vpc2VPCGW62C338EF" - ] - }, - "Vpc2PublicSubnet1EIP42DB8E45": { - "Type": "AWS::EC2::EIP", - "Properties": { - "Domain": "vpc", - "Tags": [ - { - "Key": "Name", - "Value": "aws-ecs-integ/Vpc2/PublicSubnet1" - } - ] - } - }, - "Vpc2PublicSubnet1NATGateway26016506": { - "Type": "AWS::EC2::NatGateway", - "Properties": { - "AllocationId": { - "Fn::GetAtt": [ - "Vpc2PublicSubnet1EIP42DB8E45", - "AllocationId" - ] - }, - "SubnetId": { - "Ref": "Vpc2PublicSubnet1Subnet758D49A9" - }, - "Tags": [ - { - "Key": "Name", - "Value": "aws-ecs-integ/Vpc2/PublicSubnet1" - } - ] - } - }, - "Vpc2PublicSubnet2Subnet0BF8C291": { - "Type": "AWS::EC2::Subnet", - "Properties": { - "CidrBlock": "10.0.64.0/18", - "VpcId": { - "Ref": "Vpc299FDBC5F" - }, - "AvailabilityZone": "test-region-1b", - "MapPublicIpOnLaunch": true, - "Tags": [ - { - "Key": "aws-cdk:subnet-name", - "Value": "Public" - }, - { - "Key": "aws-cdk:subnet-type", - "Value": "Public" - }, - { - "Key": "Name", - "Value": "aws-ecs-integ/Vpc2/PublicSubnet2" - } - ] - } - }, - "Vpc2PublicSubnet2RouteTableF9AE47B1": { - "Type": "AWS::EC2::RouteTable", - "Properties": { - "VpcId": { - "Ref": "Vpc299FDBC5F" - }, - "Tags": [ - { - "Key": "Name", - "Value": "aws-ecs-integ/Vpc2/PublicSubnet2" - } - ] - } - }, - "Vpc2PublicSubnet2RouteTableAssociation361E1341": { - "Type": "AWS::EC2::SubnetRouteTableAssociation", - "Properties": { - "RouteTableId": { - "Ref": "Vpc2PublicSubnet2RouteTableF9AE47B1" - }, - "SubnetId": { - "Ref": "Vpc2PublicSubnet2Subnet0BF8C291" - } - } - }, - "Vpc2PublicSubnet2DefaultRouteBAB514C1": { - "Type": "AWS::EC2::Route", - "Properties": { - "RouteTableId": { - "Ref": "Vpc2PublicSubnet2RouteTableF9AE47B1" - }, - "DestinationCidrBlock": "0.0.0.0/0", - "GatewayId": { - "Ref": "Vpc2IGWB10A76EB" - } - }, - "DependsOn": [ - "Vpc2VPCGW62C338EF" - ] - }, - "Vpc2PublicSubnet2EIP66DD26A4": { - "Type": "AWS::EC2::EIP", - "Properties": { - "Domain": "vpc", - "Tags": [ - { - "Key": "Name", - "Value": "aws-ecs-integ/Vpc2/PublicSubnet2" - } - ] - } - }, - "Vpc2PublicSubnet2NATGateway6CBF7FA6": { - "Type": "AWS::EC2::NatGateway", - "Properties": { - "AllocationId": { - "Fn::GetAtt": [ - "Vpc2PublicSubnet2EIP66DD26A4", - "AllocationId" - ] - }, - "SubnetId": { - "Ref": "Vpc2PublicSubnet2Subnet0BF8C291" - }, - "Tags": [ - { - "Key": "Name", - "Value": "aws-ecs-integ/Vpc2/PublicSubnet2" - } - ] - } - }, - "Vpc2PrivateSubnet1Subnet34902000": { - "Type": "AWS::EC2::Subnet", - "Properties": { - "CidrBlock": "10.0.128.0/18", - "VpcId": { - "Ref": "Vpc299FDBC5F" - }, - "AvailabilityZone": "test-region-1a", - "MapPublicIpOnLaunch": false, - "Tags": [ - { - "Key": "aws-cdk:subnet-name", - "Value": "Private" - }, - { - "Key": "aws-cdk:subnet-type", - "Value": "Private" - }, - { - "Key": "Name", - "Value": "aws-ecs-integ/Vpc2/PrivateSubnet1" - } - ] - } - }, - "Vpc2PrivateSubnet1RouteTableF8A2430B": { - "Type": "AWS::EC2::RouteTable", - "Properties": { - "VpcId": { - "Ref": "Vpc299FDBC5F" - }, - "Tags": [ - { - "Key": "Name", - "Value": "aws-ecs-integ/Vpc2/PrivateSubnet1" - } - ] - } - }, - "Vpc2PrivateSubnet1RouteTableAssociation74320528": { - "Type": "AWS::EC2::SubnetRouteTableAssociation", - "Properties": { - "RouteTableId": { - "Ref": "Vpc2PrivateSubnet1RouteTableF8A2430B" - }, - "SubnetId": { - "Ref": "Vpc2PrivateSubnet1Subnet34902000" - } - } - }, - "Vpc2PrivateSubnet1DefaultRoute24717F54": { - "Type": "AWS::EC2::Route", - "Properties": { - "RouteTableId": { - "Ref": "Vpc2PrivateSubnet1RouteTableF8A2430B" - }, - "DestinationCidrBlock": "0.0.0.0/0", - "NatGatewayId": { - "Ref": "Vpc2PublicSubnet1NATGateway26016506" - } - } - }, - "Vpc2PrivateSubnet2Subnet3BA0F39B": { - "Type": "AWS::EC2::Subnet", - "Properties": { - "CidrBlock": "10.0.192.0/18", - "VpcId": { - "Ref": "Vpc299FDBC5F" - }, - "AvailabilityZone": "test-region-1b", - "MapPublicIpOnLaunch": false, - "Tags": [ - { - "Key": "aws-cdk:subnet-name", - "Value": "Private" - }, - { - "Key": "aws-cdk:subnet-type", - "Value": "Private" - }, - { - "Key": "Name", - "Value": "aws-ecs-integ/Vpc2/PrivateSubnet2" - } - ] - } - }, - "Vpc2PrivateSubnet2RouteTableB4F37E84": { - "Type": "AWS::EC2::RouteTable", - "Properties": { - "VpcId": { - "Ref": "Vpc299FDBC5F" - }, - "Tags": [ - { - "Key": "Name", - "Value": "aws-ecs-integ/Vpc2/PrivateSubnet2" - } - ] - } - }, - "Vpc2PrivateSubnet2RouteTableAssociation19A1B68F": { - "Type": "AWS::EC2::SubnetRouteTableAssociation", - "Properties": { - "RouteTableId": { - "Ref": "Vpc2PrivateSubnet2RouteTableB4F37E84" - }, - "SubnetId": { - "Ref": "Vpc2PrivateSubnet2Subnet3BA0F39B" - } - } - }, - "Vpc2PrivateSubnet2DefaultRouteA55B1734": { - "Type": "AWS::EC2::Route", - "Properties": { - "RouteTableId": { - "Ref": "Vpc2PrivateSubnet2RouteTableB4F37E84" - }, - "DestinationCidrBlock": "0.0.0.0/0", - "NatGatewayId": { - "Ref": "Vpc2PublicSubnet2NATGateway6CBF7FA6" - } - } - }, - "Vpc2IGWB10A76EB": { - "Type": "AWS::EC2::InternetGateway", - "Properties": { - "Tags": [ - { - "Key": "Name", - "Value": "aws-ecs-integ/Vpc2" - } - ] - } - }, - "Vpc2VPCGW62C338EF": { - "Type": "AWS::EC2::VPCGatewayAttachment", - "Properties": { - "VpcId": { - "Ref": "Vpc299FDBC5F" - }, - "InternetGatewayId": { - "Ref": "Vpc2IGWB10A76EB" - } - } - }, - "L3bLBB8FADA4E": { - "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer", - "Properties": { - "LoadBalancerAttributes": [ - { - "Key": "deletion_protection.enabled", - "Value": "false" - } - ], - "Scheme": "internet-facing", - "SecurityGroups": [ - { - "Fn::GetAtt": [ - "L3bLBSecurityGroup7A2B0AA0", - "GroupId" - ] - } - ], - "Subnets": [ - { - "Ref": "Vpc2PublicSubnet1Subnet758D49A9" - }, - { - "Ref": "Vpc2PublicSubnet2Subnet0BF8C291" - } - ], - "Type": "application" - }, - "DependsOn": [ - "Vpc2PublicSubnet1DefaultRoute64172CA2", - "Vpc2PublicSubnet2DefaultRouteBAB514C1" - ] - }, - "L3bLBSecurityGroup7A2B0AA0": { - "Type": "AWS::EC2::SecurityGroup", - "Properties": { - "GroupDescription": "Automatically created Security Group for ELB awsecsintegL3bLB9C1497A7", - "SecurityGroupIngress": [ - { - "CidrIp": "0.0.0.0/0", - "Description": "Allow from anyone on port 80", - "FromPort": 80, - "IpProtocol": "tcp", - "ToPort": 80 - } - ], - "VpcId": { - "Ref": "Vpc299FDBC5F" - } - } - }, - "L3bLBSecurityGrouptoawsecsintegL3bServiceSecurityGroupC2BD1A598019C4C37D": { - "Type": "AWS::EC2::SecurityGroupEgress", - "Properties": { - "GroupId": { - "Fn::GetAtt": [ - "L3bLBSecurityGroup7A2B0AA0", - "GroupId" - ] - }, - "IpProtocol": "tcp", - "Description": "Load balancer to target", - "DestinationSecurityGroupId": { - "Fn::GetAtt": [ - "L3bServiceSecurityGroupA8DA736E", - "GroupId" - ] - }, - "FromPort": 80, - "ToPort": 80 - } - }, - "L3bLBPublicListenerA825925B": { - "Type": "AWS::ElasticLoadBalancingV2::Listener", - "Properties": { - "DefaultActions": [ - { - "TargetGroupArn": { - "Ref": "L3bLBPublicListenerECSGroup0070C5CA" - }, - "Type": "forward" - } - ], - "LoadBalancerArn": { - "Ref": "L3bLBB8FADA4E" - }, - "Port": 80, - "Protocol": "HTTP" - } - }, - "L3bLBPublicListenerECSGroup0070C5CA": { - "Type": "AWS::ElasticLoadBalancingV2::TargetGroup", - "Properties": { - "Port": 80, - "Protocol": "HTTP", - "TargetType": "ip", - "VpcId": { - "Ref": "Vpc299FDBC5F" - } - } - }, - "L3bTaskDefTaskRoleADAB80C8": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "ecs-tasks.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - } - } - }, - "L3bTaskDef5506864D": { - "Type": "AWS::ECS::TaskDefinition", - "Properties": { - "ContainerDefinitions": [ - { - "Essential": true, - "Image": "amazon/amazon-ecs-sample", - "LogConfiguration": { - "LogDriver": "awslogs", - "Options": { - "awslogs-group": { - "Ref": "L3bTaskDefwebLogGroup8E5F1183" - }, - "awslogs-stream-prefix": "L3b", - "awslogs-region": { - "Ref": "AWS::Region" - } - } - }, - "Name": "web", - "PortMappings": [ - { - "ContainerPort": 80, - "Protocol": "tcp" - } - ] - } - ], - "Cpu": "512", - "ExecutionRoleArn": { - "Fn::GetAtt": [ - "L3bTaskDefExecutionRole9A3E2688", - "Arn" - ] - }, - "Family": "awsecsintegL3bTaskDef24D7E4F1", - "Memory": "1024", - "NetworkMode": "awsvpc", - "RequiresCompatibilities": [ - "FARGATE" - ], - "TaskRoleArn": { - "Fn::GetAtt": [ - "L3bTaskDefTaskRoleADAB80C8", - "Arn" - ] - } - } - }, - "L3bTaskDefwebLogGroup8E5F1183": { - "Type": "AWS::Logs::LogGroup", - "UpdateReplacePolicy": "Retain", - "DeletionPolicy": "Retain" - }, - "L3bTaskDefExecutionRole9A3E2688": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "ecs-tasks.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - } - } - }, - "L3bTaskDefExecutionRoleDefaultPolicy0CEA0ED2": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": [ - "logs:CreateLogStream", - "logs:PutLogEvents" - ], - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "L3bTaskDefwebLogGroup8E5F1183", - "Arn" - ] - } - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "L3bTaskDefExecutionRoleDefaultPolicy0CEA0ED2", - "Roles": [ - { - "Ref": "L3bTaskDefExecutionRole9A3E2688" - } - ] - } - }, - "L3bServiceF9D33D5A": { - "Type": "AWS::ECS::Service", - "Properties": { - "Cluster": { - "Ref": "EcsDefaultClusterMnL3mNNYNVpc2B5DB011D" - }, - "DeploymentConfiguration": { - "MaximumPercent": 200, - "MinimumHealthyPercent": 50 - }, - "EnableECSManagedTags": false, - "HealthCheckGracePeriodSeconds": 60, - "LaunchType": "FARGATE", - "LoadBalancers": [ - { - "ContainerName": "web", - "ContainerPort": 80, - "TargetGroupArn": { - "Ref": "L3bLBPublicListenerECSGroup0070C5CA" - } - } - ], - "NetworkConfiguration": { - "AwsvpcConfiguration": { - "AssignPublicIp": "DISABLED", - "SecurityGroups": [ - { - "Fn::GetAtt": [ - "L3bServiceSecurityGroupA8DA736E", - "GroupId" - ] - } - ], - "Subnets": [ - { - "Ref": "Vpc2PrivateSubnet1Subnet34902000" - }, - { - "Ref": "Vpc2PrivateSubnet2Subnet3BA0F39B" - } - ] - } - }, - "TaskDefinition": { - "Ref": "L3bTaskDef5506864D" - } - }, - "DependsOn": [ - "L3bLBPublicListenerECSGroup0070C5CA", - "L3bLBPublicListenerA825925B" - ] - }, - "L3bServiceSecurityGroupA8DA736E": { - "Type": "AWS::EC2::SecurityGroup", - "Properties": { - "GroupDescription": "aws-ecs-integ/L3b/Service/SecurityGroup", - "SecurityGroupEgress": [ - { - "CidrIp": "0.0.0.0/0", - "Description": "Allow all outbound traffic by default", - "IpProtocol": "-1" - } - ], - "VpcId": { - "Ref": "Vpc299FDBC5F" - } - } - }, - "L3bServiceSecurityGroupfromawsecsintegL3bLBSecurityGroupA7B79A628034042CE5": { - "Type": "AWS::EC2::SecurityGroupIngress", - "Properties": { - "IpProtocol": "tcp", - "Description": "Load balancer to target", - "FromPort": 80, - "GroupId": { - "Fn::GetAtt": [ - "L3bServiceSecurityGroupA8DA736E", - "GroupId" - ] - }, - "SourceSecurityGroupId": { - "Fn::GetAtt": [ - "L3bLBSecurityGroup7A2B0AA0", - "GroupId" - ] - }, - "ToPort": 80 - } - }, - "EcsDefaultClusterMnL3mNNYNVpc2B5DB011D": { - "Type": "AWS::ECS::Cluster" - }, - "L3cLB041B1E8C": { + "NLBFargateServiceLB659EC17C": { "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer", "Properties": { "LoadBalancerAttributes": [ @@ -1340,98 +675,51 @@ } ], "Scheme": "internet-facing", - "SecurityGroups": [ - { - "Fn::GetAtt": [ - "L3cLBSecurityGroup818CBDE1", - "GroupId" - ] - } - ], "Subnets": [ { - "Ref": "Vpc2PublicSubnet1Subnet758D49A9" + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" }, { - "Ref": "Vpc2PublicSubnet2Subnet0BF8C291" + "Ref": "VpcPublicSubnet2Subnet691E08A3" } ], - "Type": "application" + "Type": "network" }, "DependsOn": [ - "Vpc2PublicSubnet1DefaultRoute64172CA2", - "Vpc2PublicSubnet2DefaultRouteBAB514C1" + "VpcPublicSubnet1DefaultRoute3DA9E72A", + "VpcPublicSubnet2DefaultRoute97F91067" ] }, - "L3cLBSecurityGroup818CBDE1": { - "Type": "AWS::EC2::SecurityGroup", - "Properties": { - "GroupDescription": "Automatically created Security Group for ELB awsecsintegL3cLB16505710", - "SecurityGroupIngress": [ - { - "CidrIp": "0.0.0.0/0", - "Description": "Allow from anyone on port 80", - "FromPort": 80, - "IpProtocol": "tcp", - "ToPort": 80 - } - ], - "VpcId": { - "Ref": "Vpc299FDBC5F" - } - } - }, - "L3cLBSecurityGrouptoawsecsintegL3cServiceSecurityGroupA4254E838029E3B246": { - "Type": "AWS::EC2::SecurityGroupEgress", - "Properties": { - "GroupId": { - "Fn::GetAtt": [ - "L3cLBSecurityGroup818CBDE1", - "GroupId" - ] - }, - "IpProtocol": "tcp", - "Description": "Load balancer to target", - "DestinationSecurityGroupId": { - "Fn::GetAtt": [ - "L3cServiceSecurityGroup94AFACED", - "GroupId" - ] - }, - "FromPort": 80, - "ToPort": 80 - } - }, - "L3cLBPublicListener1D4B3F11": { + "NLBFargateServiceLBPublicListenerB0DCA73C": { "Type": "AWS::ElasticLoadBalancingV2::Listener", "Properties": { "DefaultActions": [ { "TargetGroupArn": { - "Ref": "L3cLBPublicListenerECSGroup62D7B705" + "Ref": "NLBFargateServiceLBPublicListenerECSGroupC469CAA2" }, "Type": "forward" } ], "LoadBalancerArn": { - "Ref": "L3cLB041B1E8C" + "Ref": "NLBFargateServiceLB659EC17C" }, "Port": 80, - "Protocol": "HTTP" + "Protocol": "TCP" } }, - "L3cLBPublicListenerECSGroup62D7B705": { + "NLBFargateServiceLBPublicListenerECSGroupC469CAA2": { "Type": "AWS::ElasticLoadBalancingV2::TargetGroup", "Properties": { "Port": 80, - "Protocol": "HTTP", + "Protocol": "TCP", "TargetType": "ip", "VpcId": { - "Ref": "Vpc299FDBC5F" + "Ref": "Vpc8378EB38" } } }, - "L3cTaskDefTaskRole3C3C6124": { + "NLBFargateServiceTaskDefTaskRole6C88F40B": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { @@ -1448,7 +736,7 @@ } } }, - "L3cTaskDefA575AF8A": { + "NLBFargateServiceTaskDefB836FA89": { "Type": "AWS::ECS::TaskDefinition", "Properties": { "ContainerDefinitions": [ @@ -1459,9 +747,9 @@ "LogDriver": "awslogs", "Options": { "awslogs-group": { - "Ref": "L3cTaskDefwebLogGroupE4BDEC1B" + "Ref": "NLBFargateServiceTaskDefwebLogGroupC4A42FE2" }, - "awslogs-stream-prefix": "L3c", + "awslogs-stream-prefix": "NLBFargateService", "awslogs-region": { "Ref": "AWS::Region" } @@ -1479,11 +767,11 @@ "Cpu": "512", "ExecutionRoleArn": { "Fn::GetAtt": [ - "L3cTaskDefExecutionRoleF366B4B2", + "NLBFargateServiceTaskDefExecutionRoleF6D642D5", "Arn" ] }, - "Family": "awsecsintegL3cTaskDefF83D4A1D", + "Family": "awsecsintegl3vpconlyNLBFargateServiceTaskDef1E6E41A6", "Memory": "1024", "NetworkMode": "awsvpc", "RequiresCompatibilities": [ @@ -1491,18 +779,18 @@ ], "TaskRoleArn": { "Fn::GetAtt": [ - "L3cTaskDefTaskRole3C3C6124", + "NLBFargateServiceTaskDefTaskRole6C88F40B", "Arn" ] } } }, - "L3cTaskDefwebLogGroupE4BDEC1B": { + "NLBFargateServiceTaskDefwebLogGroupC4A42FE2": { "Type": "AWS::Logs::LogGroup", "UpdateReplacePolicy": "Retain", "DeletionPolicy": "Retain" }, - "L3cTaskDefExecutionRoleF366B4B2": { + "NLBFargateServiceTaskDefExecutionRoleF6D642D5": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { @@ -1519,7 +807,7 @@ } } }, - "L3cTaskDefExecutionRoleDefaultPolicy364B8E8C": { + "NLBFargateServiceTaskDefExecutionRoleDefaultPolicy90080805": { "Type": "AWS::IAM::Policy", "Properties": { "PolicyDocument": { @@ -1532,7 +820,7 @@ "Effect": "Allow", "Resource": { "Fn::GetAtt": [ - "L3cTaskDefwebLogGroupE4BDEC1B", + "NLBFargateServiceTaskDefwebLogGroupC4A42FE2", "Arn" ] } @@ -1540,19 +828,19 @@ ], "Version": "2012-10-17" }, - "PolicyName": "L3cTaskDefExecutionRoleDefaultPolicy364B8E8C", + "PolicyName": "NLBFargateServiceTaskDefExecutionRoleDefaultPolicy90080805", "Roles": [ { - "Ref": "L3cTaskDefExecutionRoleF366B4B2" + "Ref": "NLBFargateServiceTaskDefExecutionRoleF6D642D5" } ] } }, - "L3cServiceADA1E573": { + "NLBFargateServiceB92AC095": { "Type": "AWS::ECS::Service", "Properties": { "Cluster": { - "Ref": "EcsDefaultClusterMnL3mNNYNVpc2B5DB011D" + "Ref": "EcsDefaultClusterMnL3mNNYNVpc18E0451A" }, "DeploymentConfiguration": { "MaximumPercent": 200, @@ -1566,7 +854,7 @@ "ContainerName": "web", "ContainerPort": 80, "TargetGroupArn": { - "Ref": "L3cLBPublicListenerECSGroup62D7B705" + "Ref": "NLBFargateServiceLBPublicListenerECSGroupC469CAA2" } } ], @@ -1576,34 +864,34 @@ "SecurityGroups": [ { "Fn::GetAtt": [ - "L3cServiceSecurityGroup94AFACED", + "NLBFargateServiceSecurityGroup9D81388B", "GroupId" ] } ], "Subnets": [ { - "Ref": "Vpc2PrivateSubnet1Subnet34902000" + "Ref": "VpcPrivateSubnet1Subnet536B997A" }, { - "Ref": "Vpc2PrivateSubnet2Subnet3BA0F39B" + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" } ] } }, "TaskDefinition": { - "Ref": "L3cTaskDefA575AF8A" + "Ref": "NLBFargateServiceTaskDefB836FA89" } }, "DependsOn": [ - "L3cLBPublicListenerECSGroup62D7B705", - "L3cLBPublicListener1D4B3F11" + "NLBFargateServiceLBPublicListenerECSGroupC469CAA2", + "NLBFargateServiceLBPublicListenerB0DCA73C" ] }, - "L3cServiceSecurityGroup94AFACED": { + "NLBFargateServiceSecurityGroup9D81388B": { "Type": "AWS::EC2::SecurityGroup", "Properties": { - "GroupDescription": "aws-ecs-integ/L3c/Service/SecurityGroup", + "GroupDescription": "aws-ecs-integ-l3-vpconly/NLBFargateService/Service/SecurityGroup", "SecurityGroupEgress": [ { "CidrIp": "0.0.0.0/0", @@ -1612,66 +900,21 @@ } ], "VpcId": { - "Ref": "Vpc299FDBC5F" + "Ref": "Vpc8378EB38" } } - }, - "L3cServiceSecurityGroupfromawsecsintegL3cLBSecurityGroup7820B0B28070DB6447": { - "Type": "AWS::EC2::SecurityGroupIngress", - "Properties": { - "IpProtocol": "tcp", - "Description": "Load balancer to target", - "FromPort": 80, - "GroupId": { - "Fn::GetAtt": [ - "L3cServiceSecurityGroup94AFACED", - "GroupId" - ] - }, - "SourceSecurityGroupId": { - "Fn::GetAtt": [ - "L3cLBSecurityGroup818CBDE1", - "GroupId" - ] - }, - "ToPort": 80 - } } }, "Outputs": { - "L3LoadBalancerDNSC6CB4A70": { - "Value": { - "Fn::GetAtt": [ - "L3LB212FC0E0", - "DNSName" - ] - } - }, - "L3ServiceURL0F065F2D": { - "Value": { - "Fn::Join": [ - "", - [ - "http://", - { - "Fn::GetAtt": [ - "L3LB212FC0E0", - "DNSName" - ] - } - ] - ] - } - }, - "L3bLoadBalancerDNSED096132": { + "ALBFargateServiceLoadBalancerDNSAFB2EDDB": { "Value": { "Fn::GetAtt": [ - "L3bLBB8FADA4E", + "ALBFargateServiceLB64A0074E", "DNSName" ] } }, - "L3bServiceURL0EDED888": { + "ALBFargateServiceServiceURL4A19CF25": { "Value": { "Fn::Join": [ "", @@ -1679,7 +922,7 @@ "http://", { "Fn::GetAtt": [ - "L3bLBB8FADA4E", + "ALBFargateServiceLB64A0074E", "DNSName" ] } @@ -1687,29 +930,13 @@ ] } }, - "L3cLoadBalancerDNS9409202E": { + "NLBFargateServiceLoadBalancerDNSC2B2922F": { "Value": { "Fn::GetAtt": [ - "L3cLB041B1E8C", + "NLBFargateServiceLB659EC17C", "DNSName" ] } - }, - "L3cServiceURL2E1758C7": { - "Value": { - "Fn::Join": [ - "", - [ - "http://", - { - "Fn::GetAtt": [ - "L3cLB041B1E8C", - "DNSName" - ] - } - ] - ] - } } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-vpconly.ts b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-vpconly.ts index e3e7fedceda97..f0f76f754639e 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-vpconly.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3-vpconly.ts @@ -4,21 +4,14 @@ import * as cdk from '@aws-cdk/core'; import * as ecsPatterns from '../../lib'; const app = new cdk.App(); -const stack = new cdk.Stack(app, 'aws-ecs-integ'); +const stack = new cdk.Stack(app, 'aws-ecs-integ-l3-vpconly'); +// Create VPC only const vpc = new ec2.Vpc(stack, 'Vpc', { maxAzs: 2 }); -new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'L3', { - vpc, - memoryLimitMiB: 1024, - cpu: 512, - taskImageOptions: { - image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), - }, -}); -const vpc2 = new ec2.Vpc(stack, 'Vpc2', { maxAzs: 2 }); -new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'L3b', { - vpc: vpc2, +// Create ALB service +new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'ALBFargateService', { + vpc, memoryLimitMiB: 1024, cpu: 512, taskImageOptions: { @@ -26,8 +19,9 @@ new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'L3b', { }, }); -new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'L3c', { - vpc: vpc2, +// Create NLB service +new ecsPatterns.NetworkLoadBalancedFargateService(stack, 'NLBFargateService', { + vpc, memoryLimitMiB: 1024, cpu: 512, taskImageOptions: { diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3.expected.json index 40d86be5d331b..efd340e79b5cf 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3.expected.json @@ -10,7 +10,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc" + "Value": "aws-ecs-integ-lb-fargate/Vpc" } ] } @@ -35,7 +35,7 @@ }, { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PublicSubnet1" + "Value": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet1" } ] } @@ -49,7 +49,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PublicSubnet1" + "Value": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet1" } ] } @@ -87,7 +87,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PublicSubnet1" + "Value": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet1" } ] } @@ -107,7 +107,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PublicSubnet1" + "Value": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet1" } ] } @@ -132,7 +132,7 @@ }, { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PublicSubnet2" + "Value": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet2" } ] } @@ -146,7 +146,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PublicSubnet2" + "Value": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet2" } ] } @@ -184,7 +184,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PublicSubnet2" + "Value": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet2" } ] } @@ -204,7 +204,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PublicSubnet2" + "Value": "aws-ecs-integ-lb-fargate/Vpc/PublicSubnet2" } ] } @@ -229,7 +229,7 @@ }, { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PrivateSubnet1" + "Value": "aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet1" } ] } @@ -243,7 +243,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PrivateSubnet1" + "Value": "aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet1" } ] } @@ -291,7 +291,7 @@ }, { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PrivateSubnet2" + "Value": "aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet2" } ] } @@ -305,7 +305,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc/PrivateSubnet2" + "Value": "aws-ecs-integ-lb-fargate/Vpc/PrivateSubnet2" } ] } @@ -339,7 +339,7 @@ "Tags": [ { "Key": "Name", - "Value": "aws-ecs-integ/Vpc" + "Value": "aws-ecs-integ-lb-fargate/Vpc" } ] } @@ -358,7 +358,7 @@ "FargateCluster7CCD5F93": { "Type": "AWS::ECS::Cluster" }, - "L3LB212FC0E0": { + "ALBFargateServiceLB64A0074E": { "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer", "Properties": { "LoadBalancerAttributes": [ @@ -371,7 +371,7 @@ "SecurityGroups": [ { "Fn::GetAtt": [ - "L3LBSecurityGroupEDE61198", + "ALBFargateServiceLBSecurityGroup5DC3060E", "GroupId" ] } @@ -391,10 +391,10 @@ "VpcPublicSubnet2DefaultRoute97F91067" ] }, - "L3LBSecurityGroupEDE61198": { + "ALBFargateServiceLBSecurityGroup5DC3060E": { "Type": "AWS::EC2::SecurityGroup", "Properties": { - "GroupDescription": "Automatically created Security Group for ELB awsecsintegL3LB6453BA0A", + "GroupDescription": "Automatically created Security Group for ELB awsecsinteglbfargateALBFargateServiceLBF93E98F2", "SecurityGroupIngress": [ { "CidrIp": "0.0.0.0/0", @@ -409,12 +409,12 @@ } } }, - "L3LBSecurityGrouptoawsecsintegL3ServiceSecurityGroup7B96C87F8094933E0A": { + "ALBFargateServiceLBSecurityGrouptoawsecsinteglbfargateALBFargateServiceSecurityGroup0D9B5AEB80C5CFCE6C": { "Type": "AWS::EC2::SecurityGroupEgress", "Properties": { "GroupId": { "Fn::GetAtt": [ - "L3LBSecurityGroupEDE61198", + "ALBFargateServiceLBSecurityGroup5DC3060E", "GroupId" ] }, @@ -422,7 +422,7 @@ "Description": "Load balancer to target", "DestinationSecurityGroupId": { "Fn::GetAtt": [ - "L3ServiceSecurityGroup677B0897", + "ALBFargateServiceSecurityGroup82F7A67E", "GroupId" ] }, @@ -430,25 +430,25 @@ "ToPort": 80 } }, - "L3LBPublicListener156FFC0F": { + "ALBFargateServiceLBPublicListener3489002A": { "Type": "AWS::ElasticLoadBalancingV2::Listener", "Properties": { "DefaultActions": [ { "TargetGroupArn": { - "Ref": "L3LBPublicListenerECSGroup648EEA11" + "Ref": "ALBFargateServiceLBPublicListenerECSGroup6871FB8C" }, "Type": "forward" } ], "LoadBalancerArn": { - "Ref": "L3LB212FC0E0" + "Ref": "ALBFargateServiceLB64A0074E" }, "Port": 80, "Protocol": "HTTP" } }, - "L3LBPublicListenerECSGroup648EEA11": { + "ALBFargateServiceLBPublicListenerECSGroup6871FB8C": { "Type": "AWS::ElasticLoadBalancingV2::TargetGroup", "Properties": { "Port": 80, @@ -459,7 +459,7 @@ } } }, - "L3TaskDefTaskRole21C75D10": { + "ALBFargateServiceTaskDefTaskRole11408723": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { @@ -476,7 +476,7 @@ } } }, - "L3TaskDef48D8ACB8": { + "ALBFargateServiceTaskDefF69F17D6": { "Type": "AWS::ECS::TaskDefinition", "Properties": { "ContainerDefinitions": [ @@ -487,9 +487,9 @@ "LogDriver": "awslogs", "Options": { "awslogs-group": { - "Ref": "L3TaskDefwebLogGroupC6E4A38A" + "Ref": "ALBFargateServiceTaskDefwebLogGroup7073A41D" }, - "awslogs-stream-prefix": "L3", + "awslogs-stream-prefix": "ALBFargateService", "awslogs-region": { "Ref": "AWS::Region" } @@ -507,11 +507,11 @@ "Cpu": "512", "ExecutionRoleArn": { "Fn::GetAtt": [ - "L3TaskDefExecutionRole49AF0996", + "ALBFargateServiceTaskDefExecutionRole9E885E7B", "Arn" ] }, - "Family": "awsecsintegL3TaskDefAA25240E", + "Family": "awsecsinteglbfargateALBFargateServiceTaskDef26FE75C0", "Memory": "1024", "NetworkMode": "awsvpc", "RequiresCompatibilities": [ @@ -519,18 +519,18 @@ ], "TaskRoleArn": { "Fn::GetAtt": [ - "L3TaskDefTaskRole21C75D10", + "ALBFargateServiceTaskDefTaskRole11408723", "Arn" ] } } }, - "L3TaskDefwebLogGroupC6E4A38A": { + "ALBFargateServiceTaskDefwebLogGroup7073A41D": { "Type": "AWS::Logs::LogGroup", "UpdateReplacePolicy": "Retain", "DeletionPolicy": "Retain" }, - "L3TaskDefExecutionRole49AF0996": { + "ALBFargateServiceTaskDefExecutionRole9E885E7B": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { @@ -547,7 +547,7 @@ } } }, - "L3TaskDefExecutionRoleDefaultPolicy4656E642": { + "ALBFargateServiceTaskDefExecutionRoleDefaultPolicy574B9EAD": { "Type": "AWS::IAM::Policy", "Properties": { "PolicyDocument": { @@ -560,7 +560,7 @@ "Effect": "Allow", "Resource": { "Fn::GetAtt": [ - "L3TaskDefwebLogGroupC6E4A38A", + "ALBFargateServiceTaskDefwebLogGroup7073A41D", "Arn" ] } @@ -568,15 +568,15 @@ ], "Version": "2012-10-17" }, - "PolicyName": "L3TaskDefExecutionRoleDefaultPolicy4656E642", + "PolicyName": "ALBFargateServiceTaskDefExecutionRoleDefaultPolicy574B9EAD", "Roles": [ { - "Ref": "L3TaskDefExecutionRole49AF0996" + "Ref": "ALBFargateServiceTaskDefExecutionRole9E885E7B" } ] } }, - "L3Service616D5A93": { + "ALBFargateService90FDCE10": { "Type": "AWS::ECS::Service", "Properties": { "Cluster": { @@ -594,7 +594,7 @@ "ContainerName": "web", "ContainerPort": 80, "TargetGroupArn": { - "Ref": "L3LBPublicListenerECSGroup648EEA11" + "Ref": "ALBFargateServiceLBPublicListenerECSGroup6871FB8C" } } ], @@ -604,7 +604,7 @@ "SecurityGroups": [ { "Fn::GetAtt": [ - "L3ServiceSecurityGroup677B0897", + "ALBFargateServiceSecurityGroup82F7A67E", "GroupId" ] } @@ -620,18 +620,18 @@ } }, "TaskDefinition": { - "Ref": "L3TaskDef48D8ACB8" + "Ref": "ALBFargateServiceTaskDefF69F17D6" } }, "DependsOn": [ - "L3LBPublicListenerECSGroup648EEA11", - "L3LBPublicListener156FFC0F" + "ALBFargateServiceLBPublicListenerECSGroup6871FB8C", + "ALBFargateServiceLBPublicListener3489002A" ] }, - "L3ServiceSecurityGroup677B0897": { + "ALBFargateServiceSecurityGroup82F7A67E": { "Type": "AWS::EC2::SecurityGroup", "Properties": { - "GroupDescription": "aws-ecs-integ/L3/Service/SecurityGroup", + "GroupDescription": "aws-ecs-integ-lb-fargate/ALBFargateService/Service/SecurityGroup", "SecurityGroupEgress": [ { "CidrIp": "0.0.0.0/0", @@ -644,7 +644,7 @@ } } }, - "L3ServiceSecurityGroupfromawsecsintegL3LBSecurityGroupA70DA46C80DBDFBCD6": { + "ALBFargateServiceSecurityGroupfromawsecsinteglbfargateALBFargateServiceLBSecurityGroupCD911D2880462ECC11": { "Type": "AWS::EC2::SecurityGroupIngress", "Properties": { "IpProtocol": "tcp", @@ -652,30 +652,269 @@ "FromPort": 80, "GroupId": { "Fn::GetAtt": [ - "L3ServiceSecurityGroup677B0897", + "ALBFargateServiceSecurityGroup82F7A67E", "GroupId" ] }, "SourceSecurityGroupId": { "Fn::GetAtt": [ - "L3LBSecurityGroupEDE61198", + "ALBFargateServiceLBSecurityGroup5DC3060E", "GroupId" ] }, "ToPort": 80 } + }, + "NLBFargateServiceLB659EC17C": { + "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer", + "Properties": { + "LoadBalancerAttributes": [ + { + "Key": "deletion_protection.enabled", + "Value": "false" + } + ], + "Scheme": "internet-facing", + "Subnets": [ + { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, + { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + } + ], + "Type": "network" + }, + "DependsOn": [ + "VpcPublicSubnet1DefaultRoute3DA9E72A", + "VpcPublicSubnet2DefaultRoute97F91067" + ] + }, + "NLBFargateServiceLBPublicListenerB0DCA73C": { + "Type": "AWS::ElasticLoadBalancingV2::Listener", + "Properties": { + "DefaultActions": [ + { + "TargetGroupArn": { + "Ref": "NLBFargateServiceLBPublicListenerECSGroupC469CAA2" + }, + "Type": "forward" + } + ], + "LoadBalancerArn": { + "Ref": "NLBFargateServiceLB659EC17C" + }, + "Port": 80, + "Protocol": "TCP" + } + }, + "NLBFargateServiceLBPublicListenerECSGroupC469CAA2": { + "Type": "AWS::ElasticLoadBalancingV2::TargetGroup", + "Properties": { + "Port": 80, + "Protocol": "TCP", + "TargetType": "ip", + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "NLBFargateServiceTaskDefTaskRole6C88F40B": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "NLBFargateServiceTaskDefB836FA89": { + "Type": "AWS::ECS::TaskDefinition", + "Properties": { + "ContainerDefinitions": [ + { + "Essential": true, + "Image": "amazon/amazon-ecs-sample", + "LogConfiguration": { + "LogDriver": "awslogs", + "Options": { + "awslogs-group": { + "Ref": "NLBFargateServiceTaskDefwebLogGroupC4A42FE2" + }, + "awslogs-stream-prefix": "NLBFargateService", + "awslogs-region": { + "Ref": "AWS::Region" + } + } + }, + "Name": "web", + "PortMappings": [ + { + "ContainerPort": 80, + "Protocol": "tcp" + } + ] + } + ], + "Cpu": "512", + "ExecutionRoleArn": { + "Fn::GetAtt": [ + "NLBFargateServiceTaskDefExecutionRoleF6D642D5", + "Arn" + ] + }, + "Family": "awsecsinteglbfargateNLBFargateServiceTaskDef1265FF34", + "Memory": "1024", + "NetworkMode": "awsvpc", + "RequiresCompatibilities": [ + "FARGATE" + ], + "TaskRoleArn": { + "Fn::GetAtt": [ + "NLBFargateServiceTaskDefTaskRole6C88F40B", + "Arn" + ] + } + } + }, + "NLBFargateServiceTaskDefwebLogGroupC4A42FE2": { + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "NLBFargateServiceTaskDefExecutionRoleF6D642D5": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "NLBFargateServiceTaskDefExecutionRoleDefaultPolicy90080805": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "NLBFargateServiceTaskDefwebLogGroupC4A42FE2", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "NLBFargateServiceTaskDefExecutionRoleDefaultPolicy90080805", + "Roles": [ + { + "Ref": "NLBFargateServiceTaskDefExecutionRoleF6D642D5" + } + ] + } + }, + "NLBFargateServiceB92AC095": { + "Type": "AWS::ECS::Service", + "Properties": { + "Cluster": { + "Ref": "FargateCluster7CCD5F93" + }, + "DeploymentConfiguration": { + "MaximumPercent": 200, + "MinimumHealthyPercent": 50 + }, + "EnableECSManagedTags": false, + "HealthCheckGracePeriodSeconds": 60, + "LaunchType": "FARGATE", + "LoadBalancers": [ + { + "ContainerName": "web", + "ContainerPort": 80, + "TargetGroupArn": { + "Ref": "NLBFargateServiceLBPublicListenerECSGroupC469CAA2" + } + } + ], + "NetworkConfiguration": { + "AwsvpcConfiguration": { + "AssignPublicIp": "DISABLED", + "SecurityGroups": [ + { + "Fn::GetAtt": [ + "NLBFargateServiceSecurityGroup9D81388B", + "GroupId" + ] + } + ], + "Subnets": [ + { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + }, + { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + ] + } + }, + "TaskDefinition": { + "Ref": "NLBFargateServiceTaskDefB836FA89" + } + }, + "DependsOn": [ + "NLBFargateServiceLBPublicListenerECSGroupC469CAA2", + "NLBFargateServiceLBPublicListenerB0DCA73C" + ] + }, + "NLBFargateServiceSecurityGroup9D81388B": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "aws-ecs-integ-lb-fargate/NLBFargateService/Service/SecurityGroup", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } } }, "Outputs": { - "L3LoadBalancerDNSC6CB4A70": { + "ALBFargateServiceLoadBalancerDNSAFB2EDDB": { "Value": { "Fn::GetAtt": [ - "L3LB212FC0E0", + "ALBFargateServiceLB64A0074E", "DNSName" ] } }, - "L3ServiceURL0F065F2D": { + "ALBFargateServiceServiceURL4A19CF25": { "Value": { "Fn::Join": [ "", @@ -683,13 +922,21 @@ "http://", { "Fn::GetAtt": [ - "L3LB212FC0E0", + "ALBFargateServiceLB64A0074E", "DNSName" ] } ] ] } + }, + "NLBFargateServiceLoadBalancerDNSC2B2922F": { + "Value": { + "Fn::GetAtt": [ + "NLBFargateServiceLB659EC17C", + "DNSName" + ] + } } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3.ts b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3.ts index 5819bb3955190..b7cca5925e67c 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.l3.ts @@ -4,13 +4,24 @@ import * as cdk from '@aws-cdk/core'; import * as ecsPatterns from '../../lib'; const app = new cdk.App(); -const stack = new cdk.Stack(app, 'aws-ecs-integ'); +const stack = new cdk.Stack(app, 'aws-ecs-integ-lb-fargate'); +// Create VPC and cluster const vpc = new ec2.Vpc(stack, 'Vpc', { maxAzs: 2 }); - const cluster = new ecs.Cluster(stack, 'FargateCluster', { vpc }); -new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'L3', { +// Create ALB service +new ecsPatterns.ApplicationLoadBalancedFargateService(stack, 'ALBFargateService', { + cluster, + memoryLimitMiB: 1024, + cpu: 512, + taskImageOptions: { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + }, +}); + +// Create NLB service +new ecsPatterns.NetworkLoadBalancedFargateService(stack, 'NLBFargateService', { cluster, memoryLimitMiB: 1024, cpu: 512, From d82de0518abf4deb2c5b38d8eed9653fa8181cbd Mon Sep 17 00:00:00 2001 From: Bryan Pan Date: Thu, 13 May 2021 08:48:46 -0700 Subject: [PATCH 034/134] chore(appsync): rds data source service integration with grantDataApi (#14671) Utilize the `grantDataApi` from RDS to complete service integration. Fixes: #13189 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-appsync/lib/data-source.ts | 4 +++- .../aws-appsync/test/appsync-rds.test.ts | 20 ++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-appsync/lib/data-source.ts b/packages/@aws-cdk/aws-appsync/lib/data-source.ts index 4c1280c2196d9..b7570be255fac 100644 --- a/packages/@aws-cdk/aws-appsync/lib/data-source.ts +++ b/packages/@aws-cdk/aws-appsync/lib/data-source.ts @@ -350,12 +350,14 @@ export class RdsDataSource extends BackedDataSource { props.secretStore.grantRead(this); // Change to grant with RDS grant becomes implemented + + props.serverlessCluster.grantDataApiAccess(this); + Grant.addToPrincipal({ grantee: this, actions: [ 'rds-data:DeleteItems', 'rds-data:ExecuteSql', - 'rds-data:ExecuteStatement', 'rds-data:GetItems', 'rds-data:InsertItems', 'rds-data:UpdateItems', diff --git a/packages/@aws-cdk/aws-appsync/test/appsync-rds.test.ts b/packages/@aws-cdk/aws-appsync/test/appsync-rds.test.ts index 1f7c942811791..9a328b0fe65a0 100644 --- a/packages/@aws-cdk/aws-appsync/test/appsync-rds.test.ts +++ b/packages/@aws-cdk/aws-appsync/test/appsync-rds.test.ts @@ -58,11 +58,29 @@ describe('Rds Data Source configuration', () => { Effect: 'Allow', Resource: { Ref: 'AuroraSecret41E6E877' }, }, + { + Action: [ + 'rds-data:BatchExecuteStatement', + 'rds-data:BeginTransaction', + 'rds-data:CommitTransaction', + 'rds-data:ExecuteStatement', + 'rds-data:RollbackTransaction', + ], + Effect: 'Allow', + Resource: '*', + }, + { + Action: [ + 'secretsmanager:GetSecretValue', + 'secretsmanager:DescribeSecret', + ], + Effect: 'Allow', + Resource: { Ref: 'AuroraClusterSecretAttachmentDB8032DA' }, + }, { Action: [ 'rds-data:DeleteItems', 'rds-data:ExecuteSql', - 'rds-data:ExecuteStatement', 'rds-data:GetItems', 'rds-data:InsertItems', 'rds-data:UpdateItems', From 348e11e26edc0ff90b623b7cec778f4935e61e6d Mon Sep 17 00:00:00 2001 From: Madeline Kusters <80541297+madeline-k@users.noreply.github.com> Date: Fri, 14 May 2021 14:27:19 -0700 Subject: [PATCH 035/134] fix(ecs): Classes FargateService and Ec2Service have no defaultChild (#14691) * fix(ecs): Class FargateService has no defaultChild fixes #14665 * update unit tests --- packages/@aws-cdk/aws-ecs/lib/base/base-service.ts | 2 ++ packages/@aws-cdk/aws-ecs/test/ec2/ec2-service.test.ts | 4 +++- .../@aws-cdk/aws-ecs/test/fargate/fargate-service.test.ts | 4 +++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts b/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts index e51756cf7fa91..8d46952bf1baa 100644 --- a/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts +++ b/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts @@ -415,6 +415,8 @@ export abstract class BaseService extends Resource if (props.cloudMapOptions) { this.enableCloudMap(props.cloudMapOptions); } + + this.node.defaultChild = this.resource; } /** 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 c63c86cce9f65..036604d079c71 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 @@ -25,7 +25,7 @@ nodeunitShim({ memoryLimitMiB: 512, }); - new ecs.Ec2Service(stack, 'Ec2Service', { + const service = new ecs.Ec2Service(stack, 'Ec2Service', { cluster, taskDefinition, }); @@ -47,6 +47,8 @@ nodeunitShim({ EnableECSManagedTags: false, })); + test.notEqual(service.node.defaultChild, undefined); + test.done(); }, 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 a75ff256cc457..af4b92370726d 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 @@ -23,7 +23,7 @@ nodeunitShim({ image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), }); - new ecs.FargateService(stack, 'FargateService', { + const service = new ecs.FargateService(stack, 'FargateService', { cluster, taskDefinition, }); @@ -79,6 +79,8 @@ nodeunitShim({ }, })); + test.notEqual(service.node.defaultChild, undefined); + test.done(); }, From 0328b03f43cfe1112f658b48c974fa77403e24ee Mon Sep 17 00:00:00 2001 From: AWS CDK Team Date: Fri, 14 May 2021 21:30:07 +0000 Subject: [PATCH 036/134] chore(release): 1.104.0 --- CHANGELOG.md | 26 ++++++++++++++++++++++++++ version.v1.json | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63aa4c3bf3be9..4608b33a6503c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,32 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [1.104.0](https://github.com/aws/aws-cdk/compare/v1.103.0...v1.104.0) (2021-05-14) + + +### ⚠ BREAKING CHANGES TO EXPERIMENTAL FEATURES + +* **apigatewayv2:** setting the authorizer of an API route to HttpNoneAuthorizer will now remove any existing authorizer on the route + +### Features + +* **appsync:** elasticsearch data source for graphql api ([#14651](https://github.com/aws/aws-cdk/issues/14651)) ([2337b5d](https://github.com/aws/aws-cdk/commit/2337b5d965028ba06d6ff72f991c0b8e46433a8f)), closes [#6063](https://github.com/aws/aws-cdk/issues/6063) +* **cfnspec:** cloudformation spec v35.2.0 ([#14610](https://github.com/aws/aws-cdk/issues/14610)) ([799ce1a](https://github.com/aws/aws-cdk/commit/799ce1a7d5fb261cae92d514b4f7e315d8f0e589)) +* **cloudwatch:** GraphWidget supports period and statistic ([#14679](https://github.com/aws/aws-cdk/issues/14679)) ([b240f6e](https://github.com/aws/aws-cdk/commit/b240f6ece74d129e5f43b210e8ad12f95c4a2971)) +* **cloudwatch:** time range support for GraphWidget ([#14659](https://github.com/aws/aws-cdk/issues/14659)) ([010a6b1](https://github.com/aws/aws-cdk/commit/010a6b1a14f14be5001779644df3d3a2e27d4e71)), closes [#4649](https://github.com/aws/aws-cdk/issues/4649) +* **ecs:** add support for EC2 Capacity Providers ([#14386](https://github.com/aws/aws-cdk/issues/14386)) ([114f7cc](https://github.com/aws/aws-cdk/commit/114f7ccdaf736988834fe2be487363a992a31369)) +* **secretsmanager:** Automatically grant permissions to rotation Lambda ([#14471](https://github.com/aws/aws-cdk/issues/14471)) ([85e00fa](https://github.com/aws/aws-cdk/commit/85e00faf1e3bcc32c2f7aa881d42c6d1f6c17f63)) + + +### Bug Fixes + +* **apigatewayv2:** authorizer is not removed when HttpNoneAuthorizer is used ([#14424](https://github.com/aws/aws-cdk/issues/14424)) ([3698a91](https://github.com/aws/aws-cdk/commit/3698a91ac81a31f763c55487f200458d5b5eaf0f)), closes [40aws-cdk/aws-apigatewayv2/lib/http/route.ts#L159](https://github.com/40aws-cdk/aws-apigatewayv2/lib/http/route.ts/issues/L159) +* **ecs:** Classes FargateService and Ec2Service have no defaultChild ([#14691](https://github.com/aws/aws-cdk/issues/14691)) ([348e11e](https://github.com/aws/aws-cdk/commit/348e11e26edc0ff90b623b7cec778f4935e61e6d)), closes [#14665](https://github.com/aws/aws-cdk/issues/14665) +* **events-targets:** circular dependency when adding a KMS-encrypted SQS queue ([#14638](https://github.com/aws/aws-cdk/issues/14638)) ([3063818](https://github.com/aws/aws-cdk/commit/3063818aa7c3c3ff56cf55254b0f6561db190a3e)), closes [#11158](https://github.com/aws/aws-cdk/issues/11158) +* **lambda:** custom resource fails to connect to efs filesystem ([#14431](https://github.com/aws/aws-cdk/issues/14431)) ([10a633c](https://github.com/aws/aws-cdk/commit/10a633c8cda9f21b85c82f911d88641f3a362c4d)) +* **lambda-event-sources:** incorrect documented defaults for stream types ([#14562](https://github.com/aws/aws-cdk/issues/14562)) ([0ea24e9](https://github.com/aws/aws-cdk/commit/0ea24e95939412765c0e09133a7793557f779c76)), closes [#13908](https://github.com/aws/aws-cdk/issues/13908) +* **lambda-nodejs:** handler filename missing from error message ([#14564](https://github.com/aws/aws-cdk/issues/14564)) ([256fd4c](https://github.com/aws/aws-cdk/commit/256fd4c6fcdbe6519bc70f62415557dbeae950a1)) + ## [1.103.0](https://github.com/aws/aws-cdk/compare/v1.102.0...v1.103.0) (2021-05-10) diff --git a/version.v1.json b/version.v1.json index 60bdf592f8796..9076251f5032f 100644 --- a/version.v1.json +++ b/version.v1.json @@ -1,3 +1,3 @@ { - "version": "1.103.0" + "version": "1.104.0" } From aaa0d052a040d02f59cfaef8b02c71d7dd9d42a4 Mon Sep 17 00:00:00 2001 From: Neta Nir Date: Fri, 14 May 2021 14:33:52 -0700 Subject: [PATCH 037/134] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4608b33a6503c..30f83c5d5d399 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,7 @@ All notable changes to this project will be documented in this file. See [standa ### Bug Fixes -* **apigatewayv2:** authorizer is not removed when HttpNoneAuthorizer is used ([#14424](https://github.com/aws/aws-cdk/issues/14424)) ([3698a91](https://github.com/aws/aws-cdk/commit/3698a91ac81a31f763c55487f200458d5b5eaf0f)), closes [40aws-cdk/aws-apigatewayv2/lib/http/route.ts#L159](https://github.com/40aws-cdk/aws-apigatewayv2/lib/http/route.ts/issues/L159) +* **apigatewayv2:** authorizer is not removed when HttpNoneAuthorizer is used ([#14424](https://github.com/aws/aws-cdk/issues/14424)) ([3698a91](https://github.com/aws/aws-cdk/commit/3698a91ac81a31f763c55487f200458d5b5eaf0f)) * **ecs:** Classes FargateService and Ec2Service have no defaultChild ([#14691](https://github.com/aws/aws-cdk/issues/14691)) ([348e11e](https://github.com/aws/aws-cdk/commit/348e11e26edc0ff90b623b7cec778f4935e61e6d)), closes [#14665](https://github.com/aws/aws-cdk/issues/14665) * **events-targets:** circular dependency when adding a KMS-encrypted SQS queue ([#14638](https://github.com/aws/aws-cdk/issues/14638)) ([3063818](https://github.com/aws/aws-cdk/commit/3063818aa7c3c3ff56cf55254b0f6561db190a3e)), closes [#11158](https://github.com/aws/aws-cdk/issues/11158) * **lambda:** custom resource fails to connect to efs filesystem ([#14431](https://github.com/aws/aws-cdk/issues/14431)) ([10a633c](https://github.com/aws/aws-cdk/commit/10a633c8cda9f21b85c82f911d88641f3a362c4d)) From 1effc9fe0d8f30dc855c3ac49f58830726991f20 Mon Sep 17 00:00:00 2001 From: Hsing-Hui Hsu Date: Fri, 14 May 2021 15:18:14 -0700 Subject: [PATCH 038/134] docs(ecs): add contributing guide (#14672) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../ecs-service-extensions/CONTRIBUTING.md | 1 + .../@aws-cdk/aws-ecs-patterns/CONTRIBUTING.md | 1 + packages/@aws-cdk/aws-ecs/CONTRIBUTING.md | 56 +++++++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 packages/@aws-cdk-containers/ecs-service-extensions/CONTRIBUTING.md create mode 100644 packages/@aws-cdk/aws-ecs-patterns/CONTRIBUTING.md create mode 100644 packages/@aws-cdk/aws-ecs/CONTRIBUTING.md diff --git a/packages/@aws-cdk-containers/ecs-service-extensions/CONTRIBUTING.md b/packages/@aws-cdk-containers/ecs-service-extensions/CONTRIBUTING.md new file mode 100644 index 0000000000000..58d8e97ba78e7 --- /dev/null +++ b/packages/@aws-cdk-containers/ecs-service-extensions/CONTRIBUTING.md @@ -0,0 +1 @@ +See: [Contributing Guide](https://github.com/aws/aws-cdk/blob/master/packages/%40aws-cdk/aws-ecs/README.md) diff --git a/packages/@aws-cdk/aws-ecs-patterns/CONTRIBUTING.md b/packages/@aws-cdk/aws-ecs-patterns/CONTRIBUTING.md new file mode 100644 index 0000000000000..58d8e97ba78e7 --- /dev/null +++ b/packages/@aws-cdk/aws-ecs-patterns/CONTRIBUTING.md @@ -0,0 +1 @@ +See: [Contributing Guide](https://github.com/aws/aws-cdk/blob/master/packages/%40aws-cdk/aws-ecs/README.md) diff --git a/packages/@aws-cdk/aws-ecs/CONTRIBUTING.md b/packages/@aws-cdk/aws-ecs/CONTRIBUTING.md new file mode 100644 index 0000000000000..da767696a0b39 --- /dev/null +++ b/packages/@aws-cdk/aws-ecs/CONTRIBUTING.md @@ -0,0 +1,56 @@ +# Contributing to the AWS ECS and AWS ECS Patterns modules + +Hiya! Thanks for your interest in contributing to the ECS modules! The [ECS +Developer Experience](https://github.com/orgs/aws/teams/aws-ecs-devx) team +currently owns the following construct libraries: + +- [@aws-cdk/aws-ecs](https://github.com/aws/aws-cdk/tree/master/packages/%40aws-cdk/aws-ecs): + the main construct library for AWS ECS +- [@aws-cdk/aws-ecs-patterns](https://github.com/aws/aws-cdk/tree/master/packages/%40aws-cdk/aws-ecs-patterns): + a set of simplified, higher-level constructs based on common container-based +application architectures. Great for first-time container developers! +- [@aws-cdk-containers/ecs-service-extensions](https://github.com/aws/aws-cdk/tree/master/packages/%40aws-cdk-containers/ecs-service-extensions): + a set of ECS constructs that promote best practices for container +infrastructure by using composable add-ons, such as load balancers and sidecar +containers used for tracing and metric logging. More suitable for advanced +container configuration. + +## Find something to work on + +Issues related to ECS, ECS Patterns, and ECS Service extensions are tracked on +our public [project board](https://github.com/aws/aws-cdk/projects/2). + +If you want to contribute a specific feature or fix you have in mind, check our +[in-flight work](https://github.com/aws/aws-cdk/projects/2#column-8268897) or +[open pull requests](https://github.com/aws/aws-cdk/projects/2#column-11918985) +to see if someone else is already working on it. If an issue has someone +assigned to it in our "In Progress" column, that means they are actively +working on it. Otherwise, any unassigned issue is up for grabs! + +If an issue doesn't exist for your feature/fix, please create one using the +appropriate [issue +template](https://github.com/aws/aws-cdk/tree/master/.github/ISSUE_TEMPLATE). + +If you're simply looking for any issue to work on, explore our [Backlog of +issues](https://github.com/aws/aws-cdk/projects/2#column-8114389) on the public +project board and find something that piques your interest. If you are looking +for your first contribution, the ['good first issue' +label](https://github.com/aws/aws-cdk/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) +will be of help. + +### Let us know what you're working on! +Once you've chosen to work on an existing issue, please **add a comment to +indicate that you are starting work on it!** This will help us assign the +issue to you and keep track of issues being actively worked on, to avoid +duplicate effort. + +### Include a design if you can! +For larger features, your contribution is far more likely to be accepted if you: +1. let us know you are working on it! +2. include a design document. + +Examples of past designs for the ECS module can be found in under the +[design](https://github.com/aws/aws-cdk/tree/master/design/aws-ecs) directory. + +## Breaking Changes +See guidance on breaking changes in the [Contributing Guide](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md#breaking-changes). From 3bca82238bce28cac538c4cb444f01e0fa0f0f20 Mon Sep 17 00:00:00 2001 From: Nick Lynch Date: Mon, 17 May 2021 11:47:57 +0100 Subject: [PATCH 039/134] chore(msk): add ignore-assets pragma to cluster integ test (#14725) The MSK module relies on custom resources, which in turn create a Lambda function with assets. The way the current (lerna/yarn) build works includes the .ts file (as well as the .d.ts and .js) files in the asset bundle. Using the new `nozem` build (correctly) only includes the .d.ts and .js files, leading to a different asset hash. Since we don't care about the actual hash anyway, adding the ignore-assets pragma so this test can pass with either build tool. ---- *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-msk/test/integ.cluster.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/@aws-cdk/aws-msk/test/integ.cluster.ts b/packages/@aws-cdk/aws-msk/test/integ.cluster.ts index c422a26b5cc32..c05fa496d7210 100644 --- a/packages/@aws-cdk/aws-msk/test/integ.cluster.ts +++ b/packages/@aws-cdk/aws-msk/test/integ.cluster.ts @@ -1,3 +1,4 @@ +/// !cdk-integ pragma:ignore-assets import * as ec2 from '@aws-cdk/aws-ec2'; import * as cdk from '@aws-cdk/core'; import * as msk from '../lib'; From 8154e91952b994197e7bc92562cafb281cc68e52 Mon Sep 17 00:00:00 2001 From: Nick Lynch Date: Mon, 17 May 2021 15:29:02 +0100 Subject: [PATCH 040/134] chore: skip Go proxy for lambda-go integ tests (#14727) Privacy-conscious users and/or organizations may choose to skip sending all package requests to the Google proxy (as is default). Some corporate environments may block network access to proxy.golang.org altogether. ---- *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-go/lib/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/@aws-cdk/aws-lambda-go/lib/Dockerfile b/packages/@aws-cdk/aws-lambda-go/lib/Dockerfile index 149d117cffdb9..ffa102c84803c 100644 --- a/packages/@aws-cdk/aws-lambda-go/lib/Dockerfile +++ b/packages/@aws-cdk/aws-lambda-go/lib/Dockerfile @@ -5,6 +5,7 @@ FROM $IMAGE # set the GOCACHE ENV GOCACHE=$GOPATH/.cache/go-build +ENV GOPROXY=direct # Ensure all users can write to GOPATH RUN chmod -R 777 $GOPATH From 49d18ab8e8f55f8b36584f7fb95427106139a140 Mon Sep 17 00:00:00 2001 From: Austin Yattoni Date: Mon, 17 May 2021 11:18:49 -0500 Subject: [PATCH 041/134] fix(lambda): unable to access SingletonFunction vpc connections (#14533) fixes #6261 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-lambda/lib/singleton-lambda.ts | 15 ++++++++++++ .../aws-lambda/test/singleton-lambda.test.ts | 24 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/packages/@aws-cdk/aws-lambda/lib/singleton-lambda.ts b/packages/@aws-cdk/aws-lambda/lib/singleton-lambda.ts index 170f1f3e37573..431b9bf6a71d9 100644 --- a/packages/@aws-cdk/aws-lambda/lib/singleton-lambda.ts +++ b/packages/@aws-cdk/aws-lambda/lib/singleton-lambda.ts @@ -1,3 +1,4 @@ +import * as ec2 from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; import { Construct } from 'constructs'; @@ -63,6 +64,20 @@ export class SingletonFunction extends FunctionBase { this.canCreatePermissions = true; // Doesn't matter, addPermission is overriden anyway } + /** + * @inheritdoc + */ + public get isBoundToVpc(): boolean { + return this.lambdaFunction.isBoundToVpc; + } + + /** + * @inheritdoc + */ + public get connections(): ec2.Connections { + return this.lambdaFunction.connections; + } + /** * Returns a `lambda.Version` which represents the current version of this * singleton Lambda function. A new version will be created every time the diff --git a/packages/@aws-cdk/aws-lambda/test/singleton-lambda.test.ts b/packages/@aws-cdk/aws-lambda/test/singleton-lambda.test.ts index ebe9a5c5253cd..3bf78253d5ed6 100644 --- a/packages/@aws-cdk/aws-lambda/test/singleton-lambda.test.ts +++ b/packages/@aws-cdk/aws-lambda/test/singleton-lambda.test.ts @@ -1,5 +1,6 @@ import '@aws-cdk/assert-internal/jest'; import { ResourcePart } from '@aws-cdk/assert-internal'; +import * as ec2 from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; import * as lambda from '../lib'; @@ -174,4 +175,27 @@ describe('singleton lambda', () => { }, }); }); + + test('bind to vpc and access connections', () => { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'VPC', { maxAzs: 2 }); + const securityGroup = new ec2.SecurityGroup(stack, 'SecurityGroup', { + vpc: vpc, + }); + + // WHEN + const singleton = new lambda.SingletonFunction(stack, 'Singleton', { + uuid: '84c0de93-353f-4217-9b0b-45b6c993251a', + code: new lambda.InlineCode('foo'), + runtime: lambda.Runtime.NODEJS_12_X, + handler: 'index.handler', + securityGroups: [securityGroup], + vpc: vpc, + }); + + // THEN + expect(singleton.isBoundToVpc).toBeTruthy(); + expect(singleton.connections).toEqual(new ec2.Connections({ securityGroups: [securityGroup] })); + }); }); From 4da78f6ba2036f4a94d0e47c8581131b9bc23e14 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Mon, 17 May 2021 12:45:39 -0400 Subject: [PATCH 042/134] feat(apigatewayv2): http api - lambda authorizer (#13181) Second part of #10534 Had to make small changes to `authorizerType` that route expects, since Route and Authorizer enums are not the same. See https://github.com/aws/aws-cdk/issues/10534#issuecomment-780271588. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-apigatewayv2-authorizers/README.md | 30 ++ .../lib/http/index.ts | 3 +- .../lib/http/jwt.ts | 2 +- .../lib/http/lambda.ts | 130 ++++++ .../lib/http/user-pool.ts | 2 +- .../aws-apigatewayv2-authorizers/package.json | 4 + .../test/auth-handler/index.ts | 9 + .../test/http/integ.lambda.expected.json | 397 ++++++++++++++++++ .../test/http/integ.lambda.ts | 49 +++ .../test/http/lambda.test.ts | 182 ++++++++ .../test/integ.lambda.handler/index.ts | 9 + .../aws-apigatewayv2/lib/http/authorizer.ts | 83 +++- .../aws-apigatewayv2/lib/http/route.ts | 22 +- .../aws-apigatewayv2/test/http/api.test.ts | 6 +- .../test/http/authorizer.test.ts | 20 + .../aws-apigatewayv2/test/http/route.test.ts | 38 +- 16 files changed, 971 insertions(+), 15 deletions(-) create mode 100644 packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/lambda.ts create mode 100644 packages/@aws-cdk/aws-apigatewayv2-authorizers/test/auth-handler/index.ts create mode 100644 packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/integ.lambda.expected.json create mode 100644 packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/integ.lambda.ts create mode 100644 packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/lambda.test.ts create mode 100644 packages/@aws-cdk/aws-apigatewayv2-authorizers/test/integ.lambda.handler/index.ts diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md b/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md index ae44e5e5d97fc..3e888e57c9a9c 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md @@ -24,6 +24,7 @@ - [Route Authorization](#route-authorization) - [JWT Authorizers](#jwt-authorizers) - [User Pool Authorizer](#user-pool-authorizer) +- [Lambda Authorizers](#lambda-authorizers) ## Introduction @@ -162,3 +163,32 @@ api.addRoutes({ authorizer, }); ``` + +## Lambda Authorizers + +Lambda authorizers use a Lambda function to control access to your HTTP API. When a client calls your API, API Gateway invokes your Lambda function and uses the response to determine whether the client can access your API. + +Lambda authorizers depending on their response, fall into either two types - Simple or IAM. You can learn about differences [here](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-lambda-authorizer.html#http-api-lambda-authorizer.payload-format-response). + + +```ts +// This function handles your auth logic +const authHandler = new Function(this, 'auth-function', { + //... +}); + +const authorizer = new HttpLambdaAuthorizer({ + responseTypes: [HttpLambdaAuthorizerType.SIMPLE] // Define if returns simple and/or iam response + handler: authHandler, +}); + +const api = new HttpApi(stack, 'HttpApi'); + +api.addRoutes({ + integration: new HttpProxyIntegration({ + url: 'https://get-books-proxy.myproxy.internal', + }), + path: '/books', + authorizer, +}); +``` diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/index.ts b/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/index.ts index 9f9ad94c6a4b7..410cc8aa09f2e 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/index.ts +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/index.ts @@ -1,2 +1,3 @@ export * from './user-pool'; -export * from './jwt'; \ No newline at end of file +export * from './jwt'; +export * from './lambda'; \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/jwt.ts b/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/jwt.ts index afb5f10ac07f8..184d02f3382b6 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/jwt.ts +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/jwt.ts @@ -64,7 +64,7 @@ export class HttpJwtAuthorizer implements IHttpRouteAuthorizer { return { authorizerId: this.authorizer.authorizerId, - authorizationType: HttpAuthorizerType.JWT, + authorizationType: 'JWT', }; } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/lambda.ts b/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/lambda.ts new file mode 100644 index 0000000000000..fcb4f327c08a2 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/lambda.ts @@ -0,0 +1,130 @@ +import { + HttpAuthorizer, + HttpAuthorizerType, + HttpRouteAuthorizerBindOptions, + HttpRouteAuthorizerConfig, + IHttpRouteAuthorizer, + AuthorizerPayloadVersion, + IHttpApi, +} from '@aws-cdk/aws-apigatewayv2'; +import { ServicePrincipal } from '@aws-cdk/aws-iam'; +import { IFunction } from '@aws-cdk/aws-lambda'; +import { Stack, Duration, Names } from '@aws-cdk/core'; + +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct as CoreConstruct } from '@aws-cdk/core'; + +/** + * Specifies the type responses the lambda returns + */ +export enum HttpLambdaResponseType { + /** Returns simple boolean response */ + SIMPLE, + + /** Returns an IAM Policy */ + IAM, +} + +/** + * Properties to initialize HttpTokenAuthorizer. + */ +export interface HttpLambdaAuthorizerProps { + + /** + * The name of the authorizer + */ + readonly authorizerName: string; + + /** + * The identity source for which authorization is requested. + * + * @default ['$request.header.Authorization'] + */ + readonly identitySource?: string[]; + + /** + * The lambda function used for authorization + */ + readonly handler: IFunction; + + /** + * How long APIGateway should cache the results. Max 1 hour. + * Disable caching by setting this to `Duration.seconds(0)`. + * + * @default Duration.minutes(5) + */ + readonly resultsCacheTtl?: Duration; + + /** + * The types of responses the lambda can return + * + * If HttpLambdaResponseType.SIMPLE is included then + * response format 2.0 will be used. + * + * @see https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-lambda-authorizer.html#http-api-lambda-authorizer.payload-format-response + * + * @default [HttpLambdaResponseType.IAM] + */ + readonly responseTypes?: HttpLambdaResponseType[]; +} + +/** + * Authorize Http Api routes via a lambda function + */ +export class HttpLambdaAuthorizer implements IHttpRouteAuthorizer { + private authorizer?: HttpAuthorizer; + private httpApi?: IHttpApi; + + constructor(private readonly props: HttpLambdaAuthorizerProps) { + } + + public bind(options: HttpRouteAuthorizerBindOptions): HttpRouteAuthorizerConfig { + if (this.httpApi && (this.httpApi.apiId !== options.route.httpApi.apiId)) { + throw new Error('Cannot attach the same authorizer to multiple Apis'); + } + + if (!this.authorizer) { + const id = this.props.authorizerName; + + const responseTypes = this.props.responseTypes ?? [HttpLambdaResponseType.IAM]; + const enableSimpleResponses = responseTypes.includes(HttpLambdaResponseType.SIMPLE) || undefined; + + this.httpApi = options.route.httpApi; + this.authorizer = new HttpAuthorizer(options.scope, id, { + httpApi: options.route.httpApi, + identitySource: this.props.identitySource ?? [ + '$request.header.Authorization', + ], + type: HttpAuthorizerType.LAMBDA, + authorizerName: this.props.authorizerName, + enableSimpleResponses, + payloadFormatVersion: enableSimpleResponses ? AuthorizerPayloadVersion.VERSION_2_0 : AuthorizerPayloadVersion.VERSION_1_0, + authorizerUri: lambdaAuthorizerArn(this.props.handler), + resultsCacheTtl: this.props.resultsCacheTtl ?? Duration.minutes(5), + }); + + this.props.handler.addPermission(`${Names.nodeUniqueId(this.authorizer.node)}-Permission`, { + scope: options.scope as CoreConstruct, + principal: new ServicePrincipal('apigateway.amazonaws.com'), + sourceArn: Stack.of(options.route).formatArn({ + service: 'execute-api', + resource: options.route.httpApi.apiId, + resourceName: `authorizers/${this.authorizer.authorizerId}`, + }), + }); + } + + return { + authorizerId: this.authorizer.authorizerId, + authorizationType: 'CUSTOM', + }; + } +} + +/** + * constructs the authorizerURIArn. + */ +function lambdaAuthorizerArn(handler: IFunction) { + return `arn:${Stack.of(handler).partition}:apigateway:${Stack.of(handler).region}:lambda:path/2015-03-31/functions/${handler.functionArn}/invocations`; +} diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/user-pool.ts b/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/user-pool.ts index 4a251b8eb7406..702a3a05576ec 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/user-pool.ts +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/user-pool.ts @@ -63,7 +63,7 @@ export class HttpUserPoolAuthorizer implements IHttpRouteAuthorizer { return { authorizerId: this.authorizer.authorizerId, - authorizationType: HttpAuthorizerType.JWT, + authorizationType: 'JWT', }; } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json b/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json index c70e7a5dd8115..58de08da038e4 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json @@ -82,12 +82,16 @@ "dependencies": { "@aws-cdk/aws-apigatewayv2": "0.0.0", "@aws-cdk/aws-cognito": "0.0.0", + "@aws-cdk/aws-iam": "0.0.0", + "@aws-cdk/aws-lambda": "0.0.0", "@aws-cdk/core": "0.0.0", "constructs": "^3.3.69" }, "peerDependencies": { "@aws-cdk/aws-apigatewayv2": "0.0.0", "@aws-cdk/aws-cognito": "0.0.0", + "@aws-cdk/aws-iam": "0.0.0", + "@aws-cdk/aws-lambda": "0.0.0", "@aws-cdk/core": "0.0.0", "constructs": "^3.3.69" }, diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/auth-handler/index.ts b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/auth-handler/index.ts new file mode 100644 index 0000000000000..f08c1bdb1b42a --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/auth-handler/index.ts @@ -0,0 +1,9 @@ +/* eslint-disable no-console */ + +export const handler = async (event: AWSLambda.APIGatewayProxyEventV2) => { + const key = event.headers['x-api-key']; + + return { + isAuthorized: key === '123', + }; +}; \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/integ.lambda.expected.json b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/integ.lambda.expected.json new file mode 100644 index 0000000000000..69c407f274908 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/integ.lambda.expected.json @@ -0,0 +1,397 @@ +{ + "Resources": { + "MyHttpApi8AEAAC21": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Name": "MyHttpApi", + "ProtocolType": "HTTP" + } + }, + "MyHttpApiDefaultStageDCB9BC49": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "MyHttpApi8AEAAC21" + }, + "StageName": "$default", + "AutoDeploy": true + } + }, + "MyHttpApiGETAuthorizerIntegMyHttpApiGET16D02385PermissionBB02EBFE": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "lambda8B5974B5", + "Arn" + ] + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "MyHttpApi8AEAAC21" + }, + "/*/*/" + ] + ] + } + } + }, + "MyHttpApiGETHttpIntegration6f095b8469365f72e33fa33d9711b140516EBE31": { + "Type": "AWS::ApiGatewayV2::Integration", + "Properties": { + "ApiId": { + "Ref": "MyHttpApi8AEAAC21" + }, + "IntegrationType": "AWS_PROXY", + "IntegrationUri": { + "Fn::GetAtt": [ + "lambda8B5974B5", + "Arn" + ] + }, + "PayloadFormatVersion": "2.0" + } + }, + "MyHttpApiGETE0EFC6F8": { + "Type": "AWS::ApiGatewayV2::Route", + "Properties": { + "ApiId": { + "Ref": "MyHttpApi8AEAAC21" + }, + "RouteKey": "GET /", + "AuthorizationType": "CUSTOM", + "AuthorizerId": { + "Ref": "MyHttpApimysimpleauthorizer98398C16" + }, + "Target": { + "Fn::Join": [ + "", + [ + "integrations/", + { + "Ref": "MyHttpApiGETHttpIntegration6f095b8469365f72e33fa33d9711b140516EBE31" + } + ] + ] + } + } + }, + "MyHttpApimysimpleauthorizer98398C16": { + "Type": "AWS::ApiGatewayV2::Authorizer", + "Properties": { + "ApiId": { + "Ref": "MyHttpApi8AEAAC21" + }, + "AuthorizerType": "REQUEST", + "IdentitySource": [ + "$request.header.X-API-Key" + ], + "Name": "my-simple-authorizer", + "AuthorizerPayloadFormatVersion": "2.0", + "AuthorizerResultTtlInSeconds": 300, + "AuthorizerUri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":apigateway:", + { + "Ref": "AWS::Region" + }, + ":lambda:path/2015-03-31/functions/", + { + "Fn::GetAtt": [ + "authfunction96361832", + "Arn" + ] + }, + "/invocations" + ] + ] + }, + "EnableSimpleResponses": true + } + }, + "MyHttpApiAuthorizerIntegMyHttpApimysimpleauthorizer0F14A472PermissionF37EF5C8": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "authfunction96361832", + "Arn" + ] + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "MyHttpApi8AEAAC21" + }, + "/authorizers/", + { + "Ref": "MyHttpApimysimpleauthorizer98398C16" + } + ] + ] + } + } + }, + "authfunctionServiceRoleFCB72198": { + "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" + ] + ] + } + ] + } + }, + "authfunction96361832": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Ref": "AssetParameters7f2fe4e4fa40a84f0f773203f5c5fdaac31c80ce42c5185ed2659a049db03043S3BucketC7E46972" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters7f2fe4e4fa40a84f0f773203f5c5fdaac31c80ce42c5185ed2659a049db03043S3VersionKeyA8ECA032" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters7f2fe4e4fa40a84f0f773203f5c5fdaac31c80ce42c5185ed2659a049db03043S3VersionKeyA8ECA032" + } + ] + } + ] + } + ] + ] + } + }, + "Role": { + "Fn::GetAtt": [ + "authfunctionServiceRoleFCB72198", + "Arn" + ] + }, + "Handler": "index.handler", + "Runtime": "nodejs14.x" + }, + "DependsOn": [ + "authfunctionServiceRoleFCB72198" + ] + }, + "lambdaServiceRole494E4CA6": { + "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" + ] + ] + } + ] + } + }, + "lambda8B5974B5": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Ref": "AssetParameters1fd1c15cb7d5e2e36a11745fd10b4b7c3ca8eb30642b41954630413d2b913cdaS3Bucket2E6D85D3" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters1fd1c15cb7d5e2e36a11745fd10b4b7c3ca8eb30642b41954630413d2b913cdaS3VersionKey22B8E7C6" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters1fd1c15cb7d5e2e36a11745fd10b4b7c3ca8eb30642b41954630413d2b913cdaS3VersionKey22B8E7C6" + } + ] + } + ] + } + ] + ] + } + }, + "Role": { + "Fn::GetAtt": [ + "lambdaServiceRole494E4CA6", + "Arn" + ] + }, + "Handler": "index.handler", + "Runtime": "nodejs12.x" + }, + "DependsOn": [ + "lambdaServiceRole494E4CA6" + ] + } + }, + "Parameters": { + "AssetParameters7f2fe4e4fa40a84f0f773203f5c5fdaac31c80ce42c5185ed2659a049db03043S3BucketC7E46972": { + "Type": "String", + "Description": "S3 bucket for asset \"7f2fe4e4fa40a84f0f773203f5c5fdaac31c80ce42c5185ed2659a049db03043\"" + }, + "AssetParameters7f2fe4e4fa40a84f0f773203f5c5fdaac31c80ce42c5185ed2659a049db03043S3VersionKeyA8ECA032": { + "Type": "String", + "Description": "S3 key for asset version \"7f2fe4e4fa40a84f0f773203f5c5fdaac31c80ce42c5185ed2659a049db03043\"" + }, + "AssetParameters7f2fe4e4fa40a84f0f773203f5c5fdaac31c80ce42c5185ed2659a049db03043ArtifactHashE679D99A": { + "Type": "String", + "Description": "Artifact hash for asset \"7f2fe4e4fa40a84f0f773203f5c5fdaac31c80ce42c5185ed2659a049db03043\"" + }, + "AssetParameters1fd1c15cb7d5e2e36a11745fd10b4b7c3ca8eb30642b41954630413d2b913cdaS3Bucket2E6D85D3": { + "Type": "String", + "Description": "S3 bucket for asset \"1fd1c15cb7d5e2e36a11745fd10b4b7c3ca8eb30642b41954630413d2b913cda\"" + }, + "AssetParameters1fd1c15cb7d5e2e36a11745fd10b4b7c3ca8eb30642b41954630413d2b913cdaS3VersionKey22B8E7C6": { + "Type": "String", + "Description": "S3 key for asset version \"1fd1c15cb7d5e2e36a11745fd10b4b7c3ca8eb30642b41954630413d2b913cda\"" + }, + "AssetParameters1fd1c15cb7d5e2e36a11745fd10b4b7c3ca8eb30642b41954630413d2b913cdaArtifactHash82A279EA": { + "Type": "String", + "Description": "Artifact hash for asset \"1fd1c15cb7d5e2e36a11745fd10b4b7c3ca8eb30642b41954630413d2b913cda\"" + } + }, + "Outputs": { + "URL": { + "Value": { + "Fn::Join": [ + "", + [ + "https://", + { + "Ref": "MyHttpApi8AEAAC21" + }, + ".execute-api.", + { + "Ref": "AWS::Region" + }, + ".", + { + "Ref": "AWS::URLSuffix" + }, + "/" + ] + ] + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/integ.lambda.ts b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/integ.lambda.ts new file mode 100644 index 0000000000000..264da5f4bf510 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/integ.lambda.ts @@ -0,0 +1,49 @@ +import * as path from 'path'; +import { HttpApi, HttpMethod } from '@aws-cdk/aws-apigatewayv2'; +import { LambdaProxyIntegration } from '@aws-cdk/aws-apigatewayv2-integrations'; +import * as lambda from '@aws-cdk/aws-lambda'; +import { App, Stack, CfnOutput } from '@aws-cdk/core'; +import { HttpLambdaAuthorizer, HttpLambdaResponseType } from '../../lib'; + +/* + * Stack verification steps: + * * `curl -H 'X-API-Key: 123' ` should return 200 + * * `curl ` should return 401 + * * `curl -H 'X-API-Key: 1234' ` should return 403 + */ + +const app = new App(); +const stack = new Stack(app, 'AuthorizerInteg'); + +const httpApi = new HttpApi(stack, 'MyHttpApi'); + +const authHandler = new lambda.Function(stack, 'auth-function', { + runtime: lambda.Runtime.NODEJS_14_X, + handler: 'index.handler', + code: lambda.Code.fromAsset(path.join(__dirname, '../auth-handler')), +}); + + +const authorizer = new HttpLambdaAuthorizer({ + authorizerName: 'my-simple-authorizer', + identitySource: ['$request.header.X-API-Key'], + handler: authHandler, + responseTypes: [HttpLambdaResponseType.SIMPLE], +}); + +const handler = new lambda.Function(stack, 'lambda', { + runtime: lambda.Runtime.NODEJS_12_X, + handler: 'index.handler', + code: lambda.AssetCode.fromAsset(path.join(__dirname, '../integ.lambda.handler')), +}); + +httpApi.addRoutes({ + path: '/', + methods: [HttpMethod.GET], + integration: new LambdaProxyIntegration({ handler }), + authorizer, +}); + +new CfnOutput(stack, 'URL', { + value: httpApi.url!, +}); diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/lambda.test.ts b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/lambda.test.ts new file mode 100644 index 0000000000000..a9efd500e6bf1 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/lambda.test.ts @@ -0,0 +1,182 @@ +import '@aws-cdk/assert-internal/jest'; +import { ABSENT } from '@aws-cdk/assert-internal'; +import { HttpApi, HttpIntegrationType, HttpRouteIntegrationBindOptions, IHttpRouteIntegration, PayloadFormatVersion } from '@aws-cdk/aws-apigatewayv2'; +import { Code, Function, Runtime } from '@aws-cdk/aws-lambda'; +import { Duration, Stack } from '@aws-cdk/core'; +import { HttpLambdaAuthorizer, HttpLambdaResponseType } from '../../lib'; + +describe('HttpLambdaAuthorizer', () => { + + test('default', () => { + // GIVEN + const stack = new Stack(); + const api = new HttpApi(stack, 'HttpApi'); + + const handler = new Function(stack, 'auth-function', { + runtime: Runtime.NODEJS_12_X, + code: Code.fromInline('exports.handler = () => {return true}'), + handler: 'index.handler', + }); + + const authorizer = new HttpLambdaAuthorizer({ + authorizerName: 'default-authorizer', + handler, + }); + + // WHEN + api.addRoutes({ + integration: new DummyRouteIntegration(), + path: '/books', + authorizer, + }); + + // THEN + expect(stack).toHaveResource('AWS::ApiGatewayV2::Authorizer', { + Name: 'default-authorizer', + AuthorizerType: 'REQUEST', + AuthorizerResultTtlInSeconds: 300, + AuthorizerPayloadFormatVersion: '1.0', + IdentitySource: [ + '$request.header.Authorization', + ], + }); + + expect(stack).toHaveResource('AWS::ApiGatewayV2::Route', { + AuthorizationType: 'CUSTOM', + }); + }); + + test('should use format 2.0 and simple responses when simple response type is requested', () => { + // GIVEN + const stack = new Stack(); + const api = new HttpApi(stack, 'HttpApi'); + + const handler = new Function(stack, 'auth-function', { + runtime: Runtime.NODEJS_12_X, + code: Code.fromInline('exports.handler = () => {return true}'), + handler: 'index.handler', + }); + + const authorizer = new HttpLambdaAuthorizer({ + authorizerName: 'my-simple-authorizer', + responseTypes: [HttpLambdaResponseType.SIMPLE], + handler, + }); + + // WHEN + api.addRoutes({ + integration: new DummyRouteIntegration(), + path: '/books', + authorizer, + }); + + // THEN + expect(stack).toHaveResource('AWS::ApiGatewayV2::Authorizer', { + AuthorizerPayloadFormatVersion: '2.0', + EnableSimpleResponses: true, + }); + }); + + test('should use format 1.0 when only IAM response type is requested', () => { + // GIVEN + const stack = new Stack(); + const api = new HttpApi(stack, 'HttpApi'); + + const handler = new Function(stack, 'auth-function', { + runtime: Runtime.NODEJS_12_X, + code: Code.fromInline('exports.handler = () => {return true}'), + handler: 'index.handler', + }); + + const authorizer = new HttpLambdaAuthorizer({ + authorizerName: 'my-iam-authorizer', + responseTypes: [HttpLambdaResponseType.IAM], + handler, + }); + + // WHEN + api.addRoutes({ + integration: new DummyRouteIntegration(), + path: '/books', + authorizer, + }); + + // THEN + expect(stack).toHaveResource('AWS::ApiGatewayV2::Authorizer', { + AuthorizerPayloadFormatVersion: '1.0', + EnableSimpleResponses: ABSENT, + }); + }); + + test('should use format 2.0 and simple responses when both response types are requested', () => { + // GIVEN + const stack = new Stack(); + const api = new HttpApi(stack, 'HttpApi'); + + const handler = new Function(stack, 'auth-function', { + runtime: Runtime.NODEJS_12_X, + code: Code.fromInline('exports.handler = () => {return true}'), + handler: 'index.handler', + }); + + const authorizer = new HttpLambdaAuthorizer({ + authorizerName: 'my-simple-iam-authorizer', + responseTypes: [HttpLambdaResponseType.IAM, HttpLambdaResponseType.SIMPLE], + handler, + }); + + // WHEN + api.addRoutes({ + integration: new DummyRouteIntegration(), + path: '/books', + authorizer, + }); + + // THEN + expect(stack).toHaveResource('AWS::ApiGatewayV2::Authorizer', { + AuthorizerPayloadFormatVersion: '2.0', + EnableSimpleResponses: true, + }); + }); + + test('can override cache ttl', () => { + // GIVEN + const stack = new Stack(); + const api = new HttpApi(stack, 'HttpApi'); + + const handler = new Function(stack, 'auth-functon', { + runtime: Runtime.NODEJS_12_X, + code: Code.fromInline('exports.handler = () => {return true}'), + handler: 'index.handler', + }); + + const authorizer = new HttpLambdaAuthorizer({ + authorizerName: 'my-simple-authorizer', + responseTypes: [HttpLambdaResponseType.SIMPLE], + handler, + resultsCacheTtl: Duration.minutes(10), + }); + + // WHEN + api.addRoutes({ + integration: new DummyRouteIntegration(), + path: '/books', + authorizer, + }); + + // THEN + expect(stack).toHaveResource('AWS::ApiGatewayV2::Authorizer', { + AuthorizerResultTtlInSeconds: 600, + }); + }); +}); + +class DummyRouteIntegration implements IHttpRouteIntegration { + public bind(_: HttpRouteIntegrationBindOptions) { + return { + payloadFormatVersion: PayloadFormatVersion.VERSION_2_0, + type: HttpIntegrationType.HTTP_PROXY, + uri: 'some-uri', + }; + } +} diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/integ.lambda.handler/index.ts b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/integ.lambda.handler/index.ts new file mode 100644 index 0000000000000..def194e303e1e --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/integ.lambda.handler/index.ts @@ -0,0 +1,9 @@ +export const handler = async () => { + return { + statusCode: 200, + body: JSON.stringify({ message: 'Hello from authenticated lambda' }), + headers: { + 'Content-Type': 'application/json', + }, + }; +}; \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts index 297abf12e78e2..08936ecf36d8f 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts @@ -1,4 +1,4 @@ -import { Resource } from '@aws-cdk/core'; +import { Duration, Resource } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnAuthorizer } from '../apigatewayv2.generated'; @@ -15,9 +15,18 @@ export enum HttpAuthorizerType { /** Lambda Authorizer */ LAMBDA = 'REQUEST', +} + +/** + * Payload format version for lambda authorizers + * @see https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-lambda-authorizer.html + */ +export enum AuthorizerPayloadVersion { + /** Version 1.0 */ + VERSION_1_0 = '1.0', - /** No authorizer */ - NONE = 'NONE' + /** Version 2.0 */ + VERSION_2_0 = '2.0' } /** @@ -58,6 +67,38 @@ export interface HttpAuthorizerProps { * @default - required for JWT authorizer types. */ readonly jwtIssuer?: string; + + /** + * Specifies whether a Lambda authorizer returns a response in a simple format. + * + * If enabled, the Lambda authorizer can return a boolean value instead of an IAM policy. + * + * @default - The lambda authorizer must return an IAM policy as its response + */ + readonly enableSimpleResponses?: boolean; + + /** + * Specifies the format of the payload sent to an HTTP API Lambda authorizer. + * + * @default AuthorizerPayloadVersion.VERSION_2_0 if the authorizer type is HttpAuthorizerType.LAMBDA + */ + readonly payloadFormatVersion?: AuthorizerPayloadVersion; + + /** + * The authorizer's Uniform Resource Identifier (URI). + * + * For REQUEST authorizers, this must be a well-formed Lambda function URI. + * + * @default - required for Request authorizer types + */ + readonly authorizerUri?: string; + + /** + * How long APIGateway should cache the results. Max 1 hour. + * + * @default - API Gateway will not cache authorizer responses + */ + readonly resultsCacheTtl?: Duration; } /** @@ -77,8 +118,13 @@ export interface HttpAuthorizerAttributes { /** * Type of authorizer + * + * Possible values are: + * - JWT - JSON Web Token Authorizer + * - CUSTOM - Lambda Authorizer + * - NONE - No Authorization */ - readonly authorizerType: HttpAuthorizerType + readonly authorizerType: string } /** @@ -109,10 +155,24 @@ export class HttpAuthorizer extends Resource implements IHttpAuthorizer { constructor(scope: Construct, id: string, props: HttpAuthorizerProps) { super(scope, id); + let authorizerPayloadFormatVersion = props.payloadFormatVersion; + if (props.type === HttpAuthorizerType.JWT && (!props.jwtAudience || props.jwtAudience.length === 0 || !props.jwtIssuer)) { throw new Error('jwtAudience and jwtIssuer are mandatory for JWT authorizers'); } + if (props.type === HttpAuthorizerType.LAMBDA && !props.authorizerUri) { + throw new Error('authorizerUri is mandatory for Lambda authorizers'); + } + + /** + * This check is required because Cloudformation will fail stack creation is this property + * is set for the JWT authorizer. AuthorizerPayloadFormatVersion can only be set for REQUEST authorizer + */ + if (props.type === HttpAuthorizerType.LAMBDA && typeof authorizerPayloadFormatVersion === 'undefined') { + authorizerPayloadFormatVersion = AuthorizerPayloadVersion.VERSION_2_0; + } + const resource = new CfnAuthorizer(this, 'Resource', { name: props.authorizerName ?? id, apiId: props.httpApi.apiId, @@ -122,6 +182,10 @@ export class HttpAuthorizer extends Resource implements IHttpAuthorizer { audience: props.jwtAudience, issuer: props.jwtIssuer, }), + enableSimpleResponses: props.enableSimpleResponses, + authorizerPayloadFormatVersion, + authorizerUri: props.authorizerUri, + authorizerResultTtlInSeconds: props.resultsCacheTtl?.toSeconds(), }); this.authorizerId = resource.ref; @@ -152,10 +216,17 @@ export interface HttpRouteAuthorizerConfig { * @default - No authorizer id (useful for AWS_IAM route authorizer) */ readonly authorizerId?: string; + /** * The type of authorization + * + * Possible values are: + * - JWT - JSON Web Token Authorizer + * - CUSTOM - Lambda Authorizer + * - NONE - No Authorization */ - readonly authorizationType: HttpAuthorizerType; + readonly authorizationType: string; + /** * The list of OIDC scopes to include in the authorization. * @default - no authorization scopes @@ -184,7 +255,7 @@ function undefinedIfNoKeys(obj: A): A | undefined { export class HttpNoneAuthorizer implements IHttpRouteAuthorizer { public bind(_: HttpRouteAuthorizerBindOptions): HttpRouteAuthorizerConfig { return { - authorizationType: HttpAuthorizerType.NONE, + authorizationType: 'NONE', }; } } diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts index 5178281d08953..a88aaae0b3416 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts @@ -3,7 +3,7 @@ import { Construct } from 'constructs'; import { CfnRoute, CfnRouteProps } from '../apigatewayv2.generated'; import { IRoute } from '../common'; import { IHttpApi } from './api'; -import { HttpAuthorizerType, IHttpRouteAuthorizer } from './authorizer'; +import { IHttpRouteAuthorizer } from './authorizer'; import { IHttpRouteIntegration } from './integration'; /** @@ -120,6 +120,20 @@ export interface HttpRouteProps extends BatchHttpRouteOptions { readonly authorizationScopes?: string[]; } +/** + * Supported Route Authorizer types + */ +enum HttpRouteAuthorizationType { + /** JSON Web Tokens */ + JWT = 'JWT', + + /** Lambda Authorizer */ + CUSTOM = 'CUSTOM', + + /** No authorizer */ + NONE = 'NONE' +} + /** * Route class that creates the Route for API Gateway HTTP API * @resource AWS::ApiGatewayV2::Route @@ -147,6 +161,10 @@ export class HttpRoute extends Resource implements IHttpRoute { scope: this.httpApi instanceof Construct ? this.httpApi : this, // scope under the API if it's not imported }) : undefined; + if (authBindResult && !(authBindResult.authorizationType in HttpRouteAuthorizationType)) { + throw new Error('authorizationType should either be JWT, CUSTOM, or NONE'); + } + let authorizationScopes = authBindResult?.authorizationScopes; if (authBindResult && props.authorizationScopes) { @@ -165,7 +183,7 @@ export class HttpRoute extends Resource implements IHttpRoute { routeKey: props.routeKey.key, target: `integrations/${integration.integrationId}`, authorizerId: authBindResult?.authorizerId, - authorizationType: authBindResult?.authorizationType ?? HttpAuthorizerType.NONE, // must be explicitly NONE (not undefined) for stack updates to work correctly + authorizationType: authBindResult?.authorizationType ?? 'NONE', // must be explicitly NONE (not undefined) for stack updates to work correctly authorizationScopes, }; diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts index 12d2c68aa0ecb..3b07593676c11 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts @@ -5,7 +5,7 @@ import * as ec2 from '@aws-cdk/aws-ec2'; import { Duration, Stack } from '@aws-cdk/core'; import { CorsHttpMethod, - HttpApi, HttpAuthorizer, HttpAuthorizerType, HttpIntegrationType, HttpMethod, HttpRouteAuthorizerBindOptions, HttpRouteAuthorizerConfig, + HttpApi, HttpAuthorizer, HttpIntegrationType, HttpMethod, HttpRouteAuthorizerBindOptions, HttpRouteAuthorizerConfig, HttpRouteIntegrationBindOptions, HttpRouteIntegrationConfig, IHttpRouteAuthorizer, IHttpRouteIntegration, HttpNoneAuthorizer, PayloadFormatVersion, } from '../../lib'; @@ -310,7 +310,7 @@ describe('HttpApi', () => { const authorizer = HttpAuthorizer.fromHttpAuthorizerAttributes(stack, 'auth', { authorizerId: '12345', - authorizerType: HttpAuthorizerType.JWT, + authorizerType: 'JWT', }); // WHEN @@ -506,7 +506,7 @@ class DummyAuthorizer implements IHttpRouteAuthorizer { public bind(_: HttpRouteAuthorizerBindOptions): HttpRouteAuthorizerConfig { return { authorizerId: 'auth-1234', - authorizationType: HttpAuthorizerType.JWT, + authorizationType: 'JWT', }; } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/authorizer.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/authorizer.test.ts index 9f99ccb9f7691..92c0ee0422c17 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/authorizer.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/authorizer.test.ts @@ -64,4 +64,24 @@ describe('HttpAuthorizer', () => { }); }); }); + + describe('lambda', () => { + it('default', () => { + const stack = new Stack(); + const httpApi = new HttpApi(stack, 'HttpApi'); + + new HttpAuthorizer(stack, 'HttpAuthorizer', { + httpApi, + identitySource: ['identitysource.1', 'identitysource.2'], + type: HttpAuthorizerType.LAMBDA, + authorizerUri: 'arn:cool-lambda-arn', + }); + + expect(stack).toHaveResource('AWS::ApiGatewayV2::Authorizer', { + AuthorizerType: 'REQUEST', + AuthorizerPayloadFormatVersion: '2.0', + AuthorizerUri: 'arn:cool-lambda-arn', + }); + }); + }); }); diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts index 8de7d2ae7f1d6..f30bdaba9205e 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts @@ -242,6 +242,20 @@ describe('HttpRoute', () => { AuthorizationScopes: ['read:books'], }); }); + + test('should fail when unsupported authorization type is used', () => { + const stack = new Stack(); + const httpApi = new HttpApi(stack, 'HttpApi'); + + const authorizer = new InvalidTypeAuthorizer(); + + expect(() => new HttpRoute(stack, 'HttpRoute', { + httpApi, + integration: new DummyIntegration(), + routeKey: HttpRouteKey.with('/books', HttpMethod.GET), + authorizer, + })).toThrowError('authorizationType should either be JWT, CUSTOM, or NONE'); + }); }); class DummyIntegration implements IHttpRouteIntegration { @@ -272,7 +286,29 @@ class DummyAuthorizer implements IHttpRouteAuthorizer { return { authorizerId: this.authorizer.authorizerId, - authorizationType: HttpAuthorizerType.JWT, + authorizationType: 'JWT', }; } } + +class InvalidTypeAuthorizer implements IHttpRouteAuthorizer { + private authorizer?: HttpAuthorizer; + + public bind(options: HttpRouteAuthorizerBindOptions): HttpRouteAuthorizerConfig { + if (!this.authorizer) { + + this.authorizer = new HttpAuthorizer(options.scope, 'auth-1234', { + httpApi: options.route.httpApi, + identitySource: ['identitysource.1', 'identitysource.2'], + type: HttpAuthorizerType.JWT, + jwtAudience: ['audience.1', 'audience.2'], + jwtIssuer: 'issuer', + }); + } + + return { + authorizerId: this.authorizer.authorizerId, + authorizationType: 'Random', + }; + } +} \ No newline at end of file From 50d486ad4e7d175dfac048dbb4abf5e4084ce4fe Mon Sep 17 00:00:00 2001 From: Adam Wong <55506708+wong-a@users.noreply.github.com> Date: Mon, 17 May 2021 10:12:49 -0700 Subject: [PATCH 043/134] feat(stepfunctions): Add support for ResultSelector (#14648) ### Description Adds support for `ResultSelector`. [ResultSelector](https://docs.aws.amazon.com/step-functions/latest/dg/input-output-inputpath-params.html#input-output-resultselector) was added to ASL in August 2020 and is currently missing coverage in CDK. This change exposes a new `resultSelector` field to Task, Map, and Parallel state props. This is a JSON object that functions similarly to `Parameters` but where `$` refers to the state's raw result instead of the state input. This allows you to reshape the result without using extra Pass states. The implementation mimics what exists for Parameters. I'm not convinced we need extra types here. #### Example ```ts new tasks.LambdaInvoke(this, 'Invoke Handler', { lambdaFunction: fn, resultSelector: { lambdaOutput: sfn.JsonPath.stringAt('$.Payload'), invokeRequestId: sfn.JsonPath.stringAt('$.SdkResponseMetadata.RequestId'), staticValue: 'foo', }, }) ``` Which produces the following ASL: ```json { "Type": "Task", "Resource": "arn:aws:states:::lambda:invoke", "Parameters": { "FunctionName": ${functionName}, "Payload.$": "$" }, "ResultSelector": { "lambdaOutput.$": "$.Payload", "invokeRequestId.$": "$.SdkResponseMetadata.RequestId", "staticValue": "foo", }, "Next": ${nextState} } ``` ### Testing * Unit tests for Map, Task, and Parallel states to include `resultSelector` * Unit test with ResultSelector for `LambdaInvoke` state updated to include `resultSelector` * Updated LambdaInvoke integ test to use ResultSelector for one of the states. Executed state machine manually through the AWS console to ensure the example actually works too. Closes #9904 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-stepfunctions-tasks/README.md | 38 +++++++++++--- .../test/lambda/integ.invoke.expected.json | 2 +- .../test/lambda/integ.invoke.ts | 4 +- .../test/lambda/invoke.test.ts | 52 +++++++++++++++++++ .../aws-stepfunctions/lib/states/map.ts | 15 ++++++ .../aws-stepfunctions/lib/states/parallel.ts | 15 ++++++ .../aws-stepfunctions/lib/states/state.ts | 27 +++++++++- .../aws-stepfunctions/lib/states/task-base.ts | 15 ++++++ .../aws-stepfunctions/test/map.test.ts | 41 +++++++++++++++ .../aws-stepfunctions/test/parallel.test.ts | 32 ++++++++++++ .../aws-stepfunctions/test/task-base.test.ts | 27 ++++++++++ 11 files changed, 259 insertions(+), 9 deletions(-) diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/README.md b/packages/@aws-cdk/aws-stepfunctions-tasks/README.md index 0888563679d47..74134d35ba3e3 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/README.md +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/README.md @@ -133,6 +133,32 @@ const submitJob = new tasks.LambdaInvoke(this, 'Invoke Handler', { }); ``` +### ResultSelector + +You can use [`ResultSelector`](https://docs.aws.amazon.com/step-functions/latest/dg/input-output-inputpath-params.html#input-output-resultselector) +to manipulate the raw result of a Task, Map or Parallel state before it is +passed to [`ResultPath`](###ResultPath). For service integrations, the raw +result contains metadata in addition to the response payload. You can use +ResultSelector to construct a JSON payload that becomes the effective result +using static values or references to the raw result or context object. + +The following example extracts the output payload of a Lambda function Task and combines +it with some static values and the state name from the context object. + +```ts +new tasks.LambdaInvoke(this, 'Invoke Handler', { + lambdaFunction: fn, + resultSelector: { + lambdaOutput: sfn.JsonPath.stringAt('$.Payload'), + invokeRequestId: sfn.JsonPath.stringAt('$.SdkResponseMetadata.RequestId'), + staticValue: { + foo: 'bar', + }, + stateName: sfn.JsonPath.stringAt('$$.State.Name'), + }, +}) +``` + ### ResultPath The output of a state can be a copy of its input, the result it produces (for @@ -226,8 +252,8 @@ of the Node.js family are supported. Step Functions supports [API Gateway](https://docs.aws.amazon.com/step-functions/latest/dg/connect-api-gateway.html) through the service integration pattern. -HTTP APIs are designed for low-latency, cost-effective integrations with AWS services, including AWS Lambda, and HTTP endpoints. -HTTP APIs support OIDC and OAuth 2.0 authorization, and come with built-in support for CORS and automatic deployments. +HTTP APIs are designed for low-latency, cost-effective integrations with AWS services, including AWS Lambda, and HTTP endpoints. +HTTP APIs support OIDC and OAuth 2.0 authorization, and come with built-in support for CORS and automatic deployments. Previous-generation REST APIs currently offer more features. More details can be found [here](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-vs-rest.html). ### Call REST API Endpoint @@ -507,8 +533,8 @@ isolation by design. Learn more about [Fargate](https://aws.amazon.com/fargate/) The Fargate launch type allows you to run your containerized applications without the need to provision and manage the backend infrastructure. Just register your task definition and -Fargate launches the container for you. The latest ACTIVE revision of the passed -task definition is used for running the task. Learn more about +Fargate launches the container for you. The latest ACTIVE revision of the passed +task definition is used for running the task. Learn more about [Fargate Versioning](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_DescribeTaskDefinition.html) The following example runs a job from a task definition on Fargate @@ -718,7 +744,7 @@ You can call the [`StartJobRun`](https://docs.aws.amazon.com/glue/latest/dg/aws- new tasks.GlueStartJobRun(this, 'Task', { glueJobName: 'my-glue-job', arguments: sfn.TaskInput.fromObject({ - key: 'value', + key: 'value', }), timeout: cdk.Duration.minutes(30), notifyDelayAfter: cdk.Duration.minutes(5), @@ -1020,7 +1046,7 @@ a specific task in a state machine. When Step Functions reaches an activity task state, the workflow waits for an activity worker to poll for a task. An activity worker polls Step Functions by -using GetActivityTask, and sending the ARN for the related activity. +using GetActivityTask, and sending the ARN for the related activity. After the activity worker completes its work, it can provide a report of its success or failure by using `SendTaskSuccess` or `SendTaskFailure`. These two diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/lambda/integ.invoke.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/lambda/integ.invoke.expected.json index 1d0e7fcdc6f51..fe1262610ffd2 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/lambda/integ.invoke.expected.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/lambda/integ.invoke.expected.json @@ -188,7 +188,7 @@ "Arn" ] }, - "\",\"Payload.$\":\"$\"}},\"Check the job state\":{\"Next\":\"Job Complete?\",\"Retry\":[{\"ErrorEquals\":[\"Lambda.ServiceException\",\"Lambda.AWSLambdaException\",\"Lambda.SdkClientException\"],\"IntervalSeconds\":2,\"MaxAttempts\":6,\"BackoffRate\":2}],\"Type\":\"Task\",\"OutputPath\":\"$.Payload\",\"Resource\":\"arn:", + "\",\"Payload.$\":\"$\"}},\"Check the job state\":{\"Next\":\"Job Complete?\",\"Retry\":[{\"ErrorEquals\":[\"Lambda.ServiceException\",\"Lambda.AWSLambdaException\",\"Lambda.SdkClientException\"],\"IntervalSeconds\":2,\"MaxAttempts\":6,\"BackoffRate\":2}],\"Type\":\"Task\",\"ResultSelector\":{\"status.$\":\"$.Payload.status\"},\"Resource\":\"arn:", { "Ref": "AWS::Partition" }, diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/lambda/integ.invoke.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/test/lambda/integ.invoke.ts index 870589ff72b3e..b7006b2ad33c4 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/lambda/integ.invoke.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/lambda/integ.invoke.ts @@ -46,7 +46,9 @@ const checkJobStateLambda = new Function(stack, 'checkJobStateLambda', { const checkJobState = new LambdaInvoke(stack, 'Check the job state', { lambdaFunction: checkJobStateLambda, - outputPath: '$.Payload', + resultSelector: { + status: sfn.JsonPath.stringAt('$.Payload.status'), + }, }); const isComplete = new sfn.Choice(stack, 'Job Complete?'); diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/lambda/invoke.test.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/test/lambda/invoke.test.ts index e0166427b502c..05bc1245d903e 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/lambda/invoke.test.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/lambda/invoke.test.ts @@ -112,6 +112,58 @@ describe('LambdaInvoke', () => { })); }); + test('resultSelector', () => { + // WHEN + const task = new LambdaInvoke(stack, 'Task', { + lambdaFunction, + resultSelector: { + Result: sfn.JsonPath.stringAt('$.output.Payload'), + }, + }); + + // THEN + expect(stack.resolve(task.toStateJson())).toEqual(expect.objectContaining({ + Type: 'Task', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':states:::lambda:invoke', + ], + ], + }, + End: true, + Parameters: { + FunctionName: { + 'Fn::GetAtt': [ + 'Fn9270CBC0', + 'Arn', + ], + }, + 'Payload.$': '$', + }, + ResultSelector: { + 'Result.$': '$.output.Payload', + }, + Retry: [ + { + ErrorEquals: [ + 'Lambda.ServiceException', + 'Lambda.AWSLambdaException', + 'Lambda.SdkClientException', + ], + IntervalSeconds: 2, + MaxAttempts: 6, + BackoffRate: 2, + }, + ], + })); + }); + test('invoke Lambda function and wait for task token', () => { // GIVEN const task = new LambdaInvoke(stack, 'Task', { diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/states/map.ts b/packages/@aws-cdk/aws-stepfunctions/lib/states/map.ts index e3f6e4800991f..431886e59e0b2 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/states/map.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/states/map.ts @@ -61,6 +61,20 @@ export interface MapProps { */ readonly parameters?: { [key: string]: any }; + /** + * The JSON that will replace the state's raw result and become the effective + * result before ResultPath is applied. + * + * You can use ResultSelector to create a payload with values that are static + * or selected from the state's raw result. + * + * @see + * https://docs.aws.amazon.com/step-functions/latest/dg/input-output-inputpath-params.html#input-output-resultselector + * + * @default - None + */ + readonly resultSelector?: { [key: string]: any }; + /** * MaxConcurrency * @@ -158,6 +172,7 @@ export class Map extends State implements INextable { ...this.renderNextEnd(), ...this.renderInputOutput(), ...this.renderParameters(), + ...this.renderResultSelector(), ...this.renderRetryCatch(), ...this.renderIterator(), ...this.renderItemsPath(), diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/states/parallel.ts b/packages/@aws-cdk/aws-stepfunctions/lib/states/parallel.ts index 70ac5819a6888..9167c7d0d0355 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/states/parallel.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/states/parallel.ts @@ -45,6 +45,20 @@ export interface ParallelProps { * @default $ */ readonly resultPath?: string; + + /** + * The JSON that will replace the state's raw result and become the effective + * result before ResultPath is applied. + * + * You can use ResultSelector to create a payload with values that are static + * or selected from the state's raw result. + * + * @see + * https://docs.aws.amazon.com/step-functions/latest/dg/input-output-inputpath-params.html#input-output-resultselector + * + * @default - None + */ + readonly resultSelector?: { [key: string]: any }; } /** @@ -117,6 +131,7 @@ export class Parallel extends State implements INextable { ...this.renderInputOutput(), ...this.renderRetryCatch(), ...this.renderBranches(), + ...this.renderResultSelector(), }; } diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/states/state.ts b/packages/@aws-cdk/aws-stepfunctions/lib/states/state.ts index 42a4c8e9bf1b5..5dee29cf978ac 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/states/state.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/states/state.ts @@ -1,6 +1,6 @@ import { IConstruct, Construct, Node } from 'constructs'; import { Condition } from '../condition'; -import { JsonPath } from '../fields'; +import { FieldUtils, JsonPath } from '../fields'; import { StateGraph } from '../state-graph'; import { CatchProps, Errors, IChainable, INextable, RetryProps } from '../types'; @@ -58,6 +58,20 @@ export interface StateProps { * @default $ */ readonly resultPath?: string; + + /** + * The JSON that will replace the state's raw result and become the effective + * result before ResultPath is applied. + * + * You can use ResultSelector to create a payload with values that are static + * or selected from the state's raw result. + * + * @see + * https://docs.aws.amazon.com/step-functions/latest/dg/input-output-inputpath-params.html#input-output-resultselector + * + * @default - None + */ + readonly resultSelector?: { [key: string]: any }; } /** @@ -149,6 +163,7 @@ export abstract class State extends CoreConstruct implements IChainable { protected readonly parameters?: object; protected readonly outputPath?: string; protected readonly resultPath?: string; + protected readonly resultSelector?: object; protected readonly branches: StateGraph[] = []; protected iteration?: StateGraph; protected defaultChoice?: State; @@ -187,6 +202,7 @@ export abstract class State extends CoreConstruct implements IChainable { this.parameters = props.parameters; this.outputPath = props.outputPath; this.resultPath = props.resultPath; + this.resultSelector = props.resultSelector; } public get id() { @@ -398,6 +414,15 @@ export abstract class State extends CoreConstruct implements IChainable { }; } + /** + * Render ResultSelector in ASL JSON format + */ + protected renderResultSelector(): any { + return FieldUtils.renderObject({ + ResultSelector: this.resultSelector, + }); + } + /** * Called whenever this state is bound to a graph * diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/states/task-base.ts b/packages/@aws-cdk/aws-stepfunctions/lib/states/task-base.ts index f530113de2202..5743c0171d188 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/states/task-base.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/states/task-base.ts @@ -51,6 +51,20 @@ export interface TaskStateBaseProps { */ readonly resultPath?: string; + /** + * The JSON that will replace the state's raw result and become the effective + * result before ResultPath is applied. + * + * You can use ResultSelector to create a payload with values that are static + * or selected from the state's raw result. + * + * @see + * https://docs.aws.amazon.com/step-functions/latest/dg/input-output-inputpath-params.html#input-output-resultselector + * + * @default - None + */ + readonly resultSelector?: { [key: string]: any }; + /** * Timeout for the state machine * @@ -269,6 +283,7 @@ export abstract class TaskStateBase extends State implements INextable { InputPath: renderJsonPath(this.inputPath), OutputPath: renderJsonPath(this.outputPath), ResultPath: renderJsonPath(this.resultPath), + ...this.renderResultSelector(), }; } } diff --git a/packages/@aws-cdk/aws-stepfunctions/test/map.test.ts b/packages/@aws-cdk/aws-stepfunctions/test/map.test.ts index 1f9ea12651d4a..e5e379f578800 100644 --- a/packages/@aws-cdk/aws-stepfunctions/test/map.test.ts +++ b/packages/@aws-cdk/aws-stepfunctions/test/map.test.ts @@ -44,6 +44,47 @@ describe('Map State', () => { }, }); }), + test('State Machine With Map State and ResultSelector', () => { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + const map = new stepfunctions.Map(stack, 'Map State', { + maxConcurrency: 1, + itemsPath: stepfunctions.JsonPath.stringAt('$.inputForMap'), + resultSelector: { + buz: 'buz', + baz: stepfunctions.JsonPath.stringAt('$.baz'), + }, + }); + map.iterator(new stepfunctions.Pass(stack, 'Pass State')); + + // THEN + expect(render(map)).toStrictEqual({ + StartAt: 'Map State', + States: { + 'Map State': { + Type: 'Map', + End: true, + Iterator: { + StartAt: 'Pass State', + States: { + 'Pass State': { + Type: 'Pass', + End: true, + }, + }, + }, + ItemsPath: '$.inputForMap', + MaxConcurrency: 1, + ResultSelector: { + 'buz': 'buz', + 'baz.$': '$.baz', + }, + }, + }, + }); + }), test('synth is successful', () => { const app = createAppWithMap((stack) => { const map = new stepfunctions.Map(stack, 'Map State', { diff --git a/packages/@aws-cdk/aws-stepfunctions/test/parallel.test.ts b/packages/@aws-cdk/aws-stepfunctions/test/parallel.test.ts index cae6bdf543e99..b89a567b22826 100644 --- a/packages/@aws-cdk/aws-stepfunctions/test/parallel.test.ts +++ b/packages/@aws-cdk/aws-stepfunctions/test/parallel.test.ts @@ -27,6 +27,38 @@ describe('Parallel State', () => { }, }); }); + + test('State Machine With Parallel State and ResultSelector', () => { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + const parallel = new stepfunctions.Parallel(stack, 'Parallel State', { + resultSelector: { + buz: 'buz', + baz: stepfunctions.JsonPath.stringAt('$.baz'), + }, + }); + parallel.branch(new stepfunctions.Pass(stack, 'Branch 1')); + + // THEN + expect(render(parallel)).toStrictEqual({ + StartAt: 'Parallel State', + States: { + 'Parallel State': { + Type: 'Parallel', + End: true, + Branches: [ + { StartAt: 'Branch 1', States: { 'Branch 1': { Type: 'Pass', End: true } } }, + ], + ResultSelector: { + 'buz': 'buz', + 'baz.$': '$.baz', + }, + }, + }, + }); + }); }); function render(sm: stepfunctions.IChainable) { diff --git a/packages/@aws-cdk/aws-stepfunctions/test/task-base.test.ts b/packages/@aws-cdk/aws-stepfunctions/test/task-base.test.ts index 3a8132a1f5cb8..a57c489134efd 100644 --- a/packages/@aws-cdk/aws-stepfunctions/test/task-base.test.ts +++ b/packages/@aws-cdk/aws-stepfunctions/test/task-base.test.ts @@ -44,6 +44,33 @@ describe('Task base', () => { }); }); + test('instantiate a concrete implementation with resultSelector', () => { + // WHEN + task = new FakeTask(stack, 'my-exciting-task', { + resultSelector: { + buz: 'buz', + baz: sfn.JsonPath.stringAt('$.baz'), + }, + }); + + // THEN + expect(render(task)).toEqual({ + StartAt: 'my-exciting-task', + States: { + 'my-exciting-task': { + End: true, + Type: 'Task', + Resource: 'my-resource', + Parameters: { MyParameter: 'myParameter' }, + ResultSelector: { + 'buz': 'buz', + 'baz.$': '$.baz', + }, + }, + }, + }); + }); + test('add catch configuration', () => { // GIVEN const failure = new sfn.Fail(stack, 'failed', { From f65d826f2be59cc576272e1f9a257fd19f14af3e Mon Sep 17 00:00:00 2001 From: Jared Short Date: Mon, 17 May 2021 13:38:57 -0400 Subject: [PATCH 044/134] docs(stepfunctions-tasks): fix integration patterns of step-function-task docs (#14722) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../lib/stepfunctions/start-execution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/stepfunctions/start-execution.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/stepfunctions/start-execution.ts index 16e64fcc9cd54..bb8cf601f3bf0 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/stepfunctions/start-execution.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/stepfunctions/start-execution.ts @@ -35,7 +35,7 @@ export interface StepFunctionsStartExecutionProps extends sfn.TaskStateBaseProps /** * A Step Functions Task to call StartExecution on another state machine. * - * It supports three service integration patterns: FIRE_AND_FORGET, SYNC and WAIT_FOR_TASK_TOKEN. + * It supports three service integration patterns: REQUEST_RESPONSE, RUN_JOB, and WAIT_FOR_TASK_TOKEN. */ export class StepFunctionsStartExecution extends sfn.TaskStateBase { private static readonly SUPPORTED_INTEGRATION_PATTERNS = [ From b70a5fa77c5861ef955b86097583bf3e6f8405f2 Mon Sep 17 00:00:00 2001 From: Carter Van Deuren Date: Mon, 17 May 2021 11:29:47 -0700 Subject: [PATCH 045/134] docs(kinesis): correct grantRead and grantWrite comments (#14707) This commit swaps the comments for `grantRead` and `grantWrite` so that the comments match the permissions being granted. No extra verification was done for this change, as it only effects comments. ---- *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/lib/stream.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-kinesis/lib/stream.ts b/packages/@aws-cdk/aws-kinesis/lib/stream.ts index 0a73c6b9062f8..b2fed1eb10329 100644 --- a/packages/@aws-cdk/aws-kinesis/lib/stream.ts +++ b/packages/@aws-cdk/aws-kinesis/lib/stream.ts @@ -326,7 +326,7 @@ abstract class StreamBase extends Resource implements IStream { public abstract readonly encryptionKey?: kms.IKey; /** - * Grant write permissions for this stream and its contents to an IAM + * Grant read permissions for this stream and its contents to an IAM * principal (Role/Group/User). * * If an encryption key is used, permission to ues the key to decrypt the @@ -343,10 +343,10 @@ abstract class StreamBase extends Resource implements IStream { } /** - * Grant read permissions for this stream and its contents to an IAM + * Grant write permissions for this stream and its contents to an IAM * principal (Role/Group/User). * - * If an encryption key is used, permission to ues the key to decrypt the + * If an encryption key is used, permission to ues the key to encrypt the * contents of the stream will also be granted. */ public grantWrite(grantee: iam.IGrantable) { From 88564825a4da04ed38ec1f1b10ee2263698d636f Mon Sep 17 00:00:00 2001 From: Elad Ben-Israel Date: Mon, 17 May 2021 23:19:30 +0300 Subject: [PATCH 046/134] chore: set license of eslint-plugin-cdk (#14720) Fixes #14594 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- tools/eslint-plugin-cdk/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/eslint-plugin-cdk/package.json b/tools/eslint-plugin-cdk/package.json index 562f31110ad8d..eb1af069bc75b 100644 --- a/tools/eslint-plugin-cdk/package.json +++ b/tools/eslint-plugin-cdk/package.json @@ -32,5 +32,5 @@ }, "keywords": [], "author": "", - "license": "ISC" + "license": "Apache-2.0" } From 704946a9df25d73f1ebcb47f9df9c73d75d82acc Mon Sep 17 00:00:00 2001 From: kirintw Date: Tue, 18 May 2021 15:36:56 +0800 Subject: [PATCH 047/134] chore(lambda-layer-awscli): add awscli v2.2.5 (#14487) Update awscli to version 2.2.5, fixes #13993. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../ec2/integ.environment-file.expected.json | 42 ++++++------ .../@aws-cdk/aws-eks/lib/kubectl-provider.ts | 4 +- .../lib/bucket-deployment.ts | 4 +- .../aws-s3-deployment/lib/lambda/index.py | 2 +- ...bucket-deployment-cloudfront.expected.json | 42 ++++++------ .../integ.bucket-deployment.expected.json | 66 +++++++++---------- .../@aws-cdk/lambda-layer-awscli/README.md | 4 +- .../lambda-layer-awscli/layer/build.sh | 23 ++++--- .../lambda-layer-awscli/layer/v2.Dockerfile | 21 ++++++ .../lambda-layer-awscli/lib/awscli-layer.ts | 19 +++++- .../test/awscli-layer.test.ts | 17 ++++- 11 files changed, 149 insertions(+), 95 deletions(-) create mode 100644 packages/@aws-cdk/lambda-layer-awscli/layer/v2.Dockerfile diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.environment-file.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.environment-file.expected.json index c309c8a70f249..18ba2efb78daf 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.environment-file.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.environment-file.expected.json @@ -991,12 +991,12 @@ } } }, - "EnvFileDeploymentAwsCliLayerA8FC897D": { + "EnvFileDeploymentAwsCliV2Layer0F17A9A2": { "Type": "AWS::Lambda::LayerVersion", "Properties": { "Content": { "S3Bucket": { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7" + "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3Bucket4AD27E1E" }, "S3Key": { "Fn::Join": [ @@ -1009,7 +1009,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" + "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD" } ] } @@ -1022,7 +1022,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" + "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD" } ] } @@ -1219,7 +1219,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3Bucket55EFA30C" + "Ref": "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3BucketE7100B42" }, "S3Key": { "Fn::Join": [ @@ -1232,7 +1232,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3VersionKey60329B70" + "Ref": "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3VersionKeyFB53BEE5" } ] } @@ -1245,7 +1245,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3VersionKey60329B70" + "Ref": "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3VersionKeyFB53BEE5" } ] } @@ -1264,7 +1264,7 @@ "Handler": "index.handler", "Layers": [ { - "Ref": "EnvFileDeploymentAwsCliLayerA8FC897D" + "Ref": "EnvFileDeploymentAwsCliV2Layer0F17A9A2" } ], "Runtime": "python3.6", @@ -1336,29 +1336,29 @@ "Type": "AWS::SSM::Parameter::Value", "Default": "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id" }, - "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7": { + "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3Bucket4AD27E1E": { "Type": "String", - "Description": "S3 bucket for asset \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" + "Description": "S3 bucket for asset \"9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667\"" }, - "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F": { + "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD": { "Type": "String", - "Description": "S3 key for asset version \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" + "Description": "S3 key for asset version \"9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667\"" }, - "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68ArtifactHashD9A515C3": { + "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667ArtifactHashCF1F391F": { "Type": "String", - "Description": "Artifact hash for asset \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" + "Description": "Artifact hash for asset \"9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667\"" }, - "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3Bucket55EFA30C": { + "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3BucketE7100B42": { "Type": "String", - "Description": "S3 bucket for asset \"c24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cf\"" + "Description": "S3 bucket for asset \"02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319\"" }, - "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3VersionKey60329B70": { + "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3VersionKeyFB53BEE5": { "Type": "String", - "Description": "S3 key for asset version \"c24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cf\"" + "Description": "S3 key for asset version \"02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319\"" }, - "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfArtifactHash85F58E48": { + "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319ArtifactHash0F167E69": { "Type": "String", - "Description": "Artifact hash for asset \"c24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cf\"" + "Description": "Artifact hash for asset \"02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319\"" }, "AssetParameters972240f9dd6e036a93d5f081af9a24315b2053828ac049b3b19b2fa12d7ae64aS3Bucket1F1A8472": { "Type": "String", @@ -1385,4 +1385,4 @@ "Description": "Artifact hash for asset \"872561bf078edd1685d50c9ff821cdd60d2b2ddfb0013c4087e79bf2bb50724d\"" } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-eks/lib/kubectl-provider.ts b/packages/@aws-cdk/aws-eks/lib/kubectl-provider.ts index b5bd8ed51b876..0144a38a46e0e 100644 --- a/packages/@aws-cdk/aws-eks/lib/kubectl-provider.ts +++ b/packages/@aws-cdk/aws-eks/lib/kubectl-provider.ts @@ -3,7 +3,7 @@ import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; import { Duration, Stack, NestedStack, Names } from '@aws-cdk/core'; import * as cr from '@aws-cdk/custom-resources'; -import { AwsCliLayer } from '@aws-cdk/lambda-layer-awscli'; +import { AwsCliV2Layer } from '@aws-cdk/lambda-layer-awscli'; import { KubectlLayer } from '@aws-cdk/lambda-layer-kubectl'; import { Construct } from 'constructs'; import { ICluster, Cluster } from './cluster'; @@ -86,7 +86,7 @@ export class KubectlProvider extends NestedStack { // allow user to customize the layer if (!props.cluster.kubectlLayer) { - handler.addLayers(new AwsCliLayer(this, 'AwsCliLayer')); + handler.addLayers(new AwsCliV2Layer(this, 'AwsCliV2Layer')); handler.addLayers(new KubectlLayer(this, 'KubectlLayer')); } else { handler.addLayers(props.cluster.kubectlLayer); diff --git a/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts b/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts index ca91f036dc5ed..09991219a1922 100644 --- a/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts +++ b/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts @@ -5,7 +5,7 @@ import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; import * as s3 from '@aws-cdk/aws-s3'; import * as cdk from '@aws-cdk/core'; -import { AwsCliLayer } from '@aws-cdk/lambda-layer-awscli'; +import { AwsCliV2Layer } from '@aws-cdk/lambda-layer-awscli'; import { Construct } from 'constructs'; import { ISource, SourceConfig } from './source'; @@ -196,7 +196,7 @@ export class BucketDeployment extends CoreConstruct { const handler = new lambda.SingletonFunction(this, 'CustomResourceHandler', { uuid: this.renderSingletonUuid(props.memoryLimit), code: lambda.Code.fromAsset(path.join(__dirname, 'lambda')), - layers: [new AwsCliLayer(this, 'AwsCliLayer')], + layers: [new AwsCliV2Layer(this, 'AwsCliV2Layer')], runtime: lambda.Runtime.PYTHON_3_6, handler: 'index.handler', lambdaPurpose: 'Custom::CDKBucketDeployment', diff --git a/packages/@aws-cdk/aws-s3-deployment/lib/lambda/index.py b/packages/@aws-cdk/aws-s3-deployment/lib/lambda/index.py index 3935e3122529d..f9a9ea69f4593 100644 --- a/packages/@aws-cdk/aws-s3-deployment/lib/lambda/index.py +++ b/packages/@aws-cdk/aws-s3-deployment/lib/lambda/index.py @@ -183,7 +183,7 @@ def create_metadata_args(raw_user_metadata, raw_system_metadata): #--------------------------------------------------------------------------------------------------- # executes an "aws" cli command def aws_command(*args): - aws="/opt/awscli/aws" # from AwsCliLayer + aws="/opt/awscli/aws" # from AwsCliV2Layer logger.info("| aws %s" % ' '.join(args)) subprocess.check_call([aws] + list(args)) diff --git a/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment-cloudfront.expected.json b/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment-cloudfront.expected.json index 76f906941f2bd..abfcd004e8c4b 100644 --- a/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment-cloudfront.expected.json +++ b/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment-cloudfront.expected.json @@ -53,12 +53,12 @@ } } }, - "DeployWithInvalidationAwsCliLayerDEDD5787": { + "DeployWithInvalidationAwsCliV2Layer0A4BF3DF": { "Type": "AWS::Lambda::LayerVersion", "Properties": { "Content": { "S3Bucket": { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7" + "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3Bucket4AD27E1E" }, "S3Key": { "Fn::Join": [ @@ -71,7 +71,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" + "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD" } ] } @@ -84,7 +84,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" + "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD" } ] } @@ -295,7 +295,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3Bucket55EFA30C" + "Ref": "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3BucketE7100B42" }, "S3Key": { "Fn::Join": [ @@ -308,7 +308,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3VersionKey60329B70" + "Ref": "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3VersionKeyFB53BEE5" } ] } @@ -321,7 +321,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3VersionKey60329B70" + "Ref": "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3VersionKeyFB53BEE5" } ] } @@ -340,7 +340,7 @@ "Handler": "index.handler", "Layers": [ { - "Ref": "DeployWithInvalidationAwsCliLayerDEDD5787" + "Ref": "DeployWithInvalidationAwsCliV2Layer0A4BF3DF" } ], "Runtime": "python3.6", @@ -353,29 +353,29 @@ } }, "Parameters": { - "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7": { + "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3Bucket4AD27E1E": { "Type": "String", - "Description": "S3 bucket for asset \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" + "Description": "S3 bucket for asset \"9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667\"" }, - "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F": { + "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD": { "Type": "String", - "Description": "S3 key for asset version \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" + "Description": "S3 key for asset version \"9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667\"" }, - "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68ArtifactHashD9A515C3": { + "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667ArtifactHashCF1F391F": { "Type": "String", - "Description": "Artifact hash for asset \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" + "Description": "Artifact hash for asset \"9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667\"" }, - "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3Bucket55EFA30C": { + "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3BucketE7100B42": { "Type": "String", - "Description": "S3 bucket for asset \"c24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cf\"" + "Description": "S3 bucket for asset \"02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319\"" }, - "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3VersionKey60329B70": { + "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3VersionKeyFB53BEE5": { "Type": "String", - "Description": "S3 key for asset version \"c24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cf\"" + "Description": "S3 key for asset version \"02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319\"" }, - "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfArtifactHash85F58E48": { + "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319ArtifactHash0F167E69": { "Type": "String", - "Description": "Artifact hash for asset \"c24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cf\"" + "Description": "Artifact hash for asset \"02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319\"" }, "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eS3Bucket9CD8B20A": { "Type": "String", @@ -390,4 +390,4 @@ "Description": "Artifact hash for asset \"fc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222e\"" } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment.expected.json b/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment.expected.json index 877298773816b..054938cc10920 100644 --- a/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment.expected.json +++ b/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment.expected.json @@ -10,12 +10,12 @@ "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, - "DeployMeAwsCliLayer5F9219E9": { + "DeployMeAwsCliV2LayerA4132521": { "Type": "AWS::Lambda::LayerVersion", "Properties": { "Content": { "S3Bucket": { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7" + "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3Bucket4AD27E1E" }, "S3Key": { "Fn::Join": [ @@ -28,7 +28,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" + "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD" } ] } @@ -41,7 +41,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" + "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD" } ] } @@ -304,7 +304,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3Bucket55EFA30C" + "Ref": "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3BucketE7100B42" }, "S3Key": { "Fn::Join": [ @@ -317,7 +317,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3VersionKey60329B70" + "Ref": "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3VersionKeyFB53BEE5" } ] } @@ -330,7 +330,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3VersionKey60329B70" + "Ref": "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3VersionKeyFB53BEE5" } ] } @@ -349,7 +349,7 @@ "Handler": "index.handler", "Layers": [ { - "Ref": "DeployMeAwsCliLayer5F9219E9" + "Ref": "DeployMeAwsCliV2LayerA4132521" } ], "Runtime": "python3.6", @@ -365,12 +365,12 @@ "UpdateReplacePolicy": "Retain", "DeletionPolicy": "Retain" }, - "DeployWithPrefixAwsCliLayerC9DDB597": { + "DeployWithPrefixAwsCliV2Layer076E52D4": { "Type": "AWS::Lambda::LayerVersion", "Properties": { "Content": { "S3Bucket": { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7" + "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3Bucket4AD27E1E" }, "S3Key": { "Fn::Join": [ @@ -383,7 +383,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" + "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD" } ] } @@ -396,7 +396,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" + "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD" } ] } @@ -473,12 +473,12 @@ "UpdateReplacePolicy": "Retain", "DeletionPolicy": "Retain" }, - "DeployWithMetadataAwsCliLayer2C774B41": { + "DeployWithMetadataAwsCliV2Layer791A6E48": { "Type": "AWS::Lambda::LayerVersion", "Properties": { "Content": { "S3Bucket": { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7" + "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3Bucket4AD27E1E" }, "S3Key": { "Fn::Join": [ @@ -491,7 +491,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" + "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD" } ] } @@ -504,7 +504,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" + "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD" } ] } @@ -584,12 +584,12 @@ "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, - "DeployMeWithoutDeletingFilesOnDestinationAwsCliLayer4D54C41C": { + "DeployMeWithoutDeletingFilesOnDestinationAwsCliV2Layer7C5D4E6E": { "Type": "AWS::Lambda::LayerVersion", "Properties": { "Content": { "S3Bucket": { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7" + "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3Bucket4AD27E1E" }, "S3Key": { "Fn::Join": [ @@ -602,7 +602,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" + "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD" } ] } @@ -615,7 +615,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" + "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD" } ] } @@ -688,29 +688,29 @@ } }, "Parameters": { - "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7": { + "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3Bucket4AD27E1E": { "Type": "String", - "Description": "S3 bucket for asset \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" + "Description": "S3 bucket for asset \"9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667\"" }, - "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F": { + "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD": { "Type": "String", - "Description": "S3 key for asset version \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" + "Description": "S3 key for asset version \"9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667\"" }, - "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68ArtifactHashD9A515C3": { + "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667ArtifactHashCF1F391F": { "Type": "String", - "Description": "Artifact hash for asset \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" + "Description": "Artifact hash for asset \"9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667\"" }, - "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3Bucket55EFA30C": { + "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3BucketE7100B42": { "Type": "String", - "Description": "S3 bucket for asset \"c24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cf\"" + "Description": "S3 bucket for asset \"02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319\"" }, - "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3VersionKey60329B70": { + "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3VersionKeyFB53BEE5": { "Type": "String", - "Description": "S3 key for asset version \"c24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cf\"" + "Description": "S3 key for asset version \"02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319\"" }, - "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfArtifactHash85F58E48": { + "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319ArtifactHash0F167E69": { "Type": "String", - "Description": "Artifact hash for asset \"c24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cf\"" + "Description": "Artifact hash for asset \"02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319\"" }, "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eS3Bucket9CD8B20A": { "Type": "String", @@ -725,4 +725,4 @@ "Description": "Artifact hash for asset \"fc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222e\"" } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/lambda-layer-awscli/README.md b/packages/@aws-cdk/lambda-layer-awscli/README.md index baad6362f5e21..4132783bc3966 100644 --- a/packages/@aws-cdk/lambda-layer-awscli/README.md +++ b/packages/@aws-cdk/lambda-layer-awscli/README.md @@ -10,13 +10,13 @@ -This module exports a single class called `AwsCliLayer` which is a `lambda.Layer` that bundles the AWS CLI. +This module exports two classes called `AwsCliLayer` & `AwsCliV2Layer` which is a `lambda.Layer` that bundles the AWS CLI. Usage: ```ts const fn = new lambda.Function(...); -fn.addLayers(new AwsCliLayer(stack, 'AwsCliLayer')); +fn.addLayers(new AwsCliV2Layer(stack, 'AwsCliV2Layer')); ``` The CLI will be installed under `/opt/awscli/aws`. diff --git a/packages/@aws-cdk/lambda-layer-awscli/layer/build.sh b/packages/@aws-cdk/lambda-layer-awscli/layer/build.sh index a7c13263ebdce..d1f547312dde7 100755 --- a/packages/@aws-cdk/lambda-layer-awscli/layer/build.sh +++ b/packages/@aws-cdk/lambda-layer-awscli/layer/build.sh @@ -3,16 +3,21 @@ set -euo pipefail cd $(dirname $0) -echo ">> Building AWS Lambda layer inside a docker image..." +echo ">> Building AWS Lambda layer inside docker images..." -TAG='aws-lambda-layer' +V1_TAG="lambda-awscli-layer" +V2_TAG="lambda-awscli-v2-layer" -docker build -t ${TAG} . +docker build -t "${V1_TAG}" . -f Dockerfile +docker build -t "${V2_TAG}" . -f v2.Dockerfile -echo ">> Extrating layer.zip from the build container..." -CONTAINER=$(docker run -d ${TAG} false) -docker cp ${CONTAINER}:/layer.zip ../lib/layer.zip +echo ">> Extrating layer zip files from the build containers..." +V1_CONTAINER=$(docker run -d ${V1_TAG} false) +V2_CONTAINER=$(docker run -d ${V2_TAG} false) +docker cp "${V1_CONTAINER}:/layer.zip" ../lib/layer.zip +docker cp "${V2_CONTAINER}:/layer_v2.zip" ../lib/layer_v2.zip -echo ">> Stopping container..." -docker rm -f ${CONTAINER} -echo ">> lib/layer.zip is ready" +echo ">> Stopping containers..." +docker rm -f "${V1_CONTAINER}" +docker rm -f "${V2_CONTAINER}" +echo ">> lib/layer.zip & lib/layer_v2.zip is ready" diff --git a/packages/@aws-cdk/lambda-layer-awscli/layer/v2.Dockerfile b/packages/@aws-cdk/lambda-layer-awscli/layer/v2.Dockerfile new file mode 100644 index 0000000000000..801dd434e4c3e --- /dev/null +++ b/packages/@aws-cdk/lambda-layer-awscli/layer/v2.Dockerfile @@ -0,0 +1,21 @@ +FROM public.ecr.aws/lambda/provided:latest + +ARG AWSCLI_V2_VERSION=2.2.5 +USER root +RUN set -ex; \ + yum update -y; \ + yum install -y zip unzip wget tar gzip; +WORKDIR /tmp +RUN set -ex; \ + mkdir -p /opt; \ + curl -sSL "https://awscli.amazonaws.com/awscli-exe-linux-x86_64-$AWSCLI_V2_VERSION.zip" -o awscli-bundle.zip; \ + unzip awscli-bundle.zip; \ + mv ./aws/dist /opt/awscli; \ + rm -rf /opt/awscli/awscli/examples; \ + cd /opt; \ + zip --symlinks -r ../layer_v2.zip *; \ + ls -alh /layer_v2.zip; \ + /opt/awscli/aws --version; + +WORKDIR / +ENTRYPOINT [ "/bin/bash" ] diff --git a/packages/@aws-cdk/lambda-layer-awscli/lib/awscli-layer.ts b/packages/@aws-cdk/lambda-layer-awscli/lib/awscli-layer.ts index 525babcac82f3..58f7f39882bb3 100644 --- a/packages/@aws-cdk/lambda-layer-awscli/lib/awscli-layer.ts +++ b/packages/@aws-cdk/lambda-layer-awscli/lib/awscli-layer.ts @@ -5,7 +5,7 @@ import * as lambda from '@aws-cdk/aws-lambda'; import { Construct } from 'constructs'; /** - * An AWS Lambda layer that includes the AWS CLI. + * An AWS Lambda layer that includes the AWS CLI V1. */ export class AwsCliLayer extends lambda.LayerVersion { constructor(scope: Construct, id: string) { @@ -19,9 +19,24 @@ export class AwsCliLayer extends lambda.LayerVersion { } } +/** + * An AWS Lambda layer that includes the AWS CLI V2. + */ +export class AwsCliV2Layer extends lambda.LayerVersion { + constructor(scope: Construct, id: string) { + super(scope, id, { + code: lambda.Code.fromAsset(path.join(__dirname, 'layer_v2.zip'), { + // we hash the Dockerfile (it contains the tools versions) because hashing the zip is non-deterministic + assetHash: hashFile(path.join(__dirname, '..', 'layer', 'v2.Dockerfile')), + }), + description: '/opt/awscli/aws', + }); + } +} + function hashFile(fileName: string) { return crypto .createHash('sha256') .update(fs.readFileSync(fileName)) .digest('hex'); -} \ No newline at end of file +} 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..dec56b52a1aed 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,8 +1,8 @@ import { Stack } from '@aws-cdk/core'; -import { AwsCliLayer } from '../lib'; +import { AwsCliLayer, AwsCliV2Layer } from '../lib'; import '@aws-cdk/assert-internal/jest'; -test('synthesized to a layer version', () => { +test('synthesized to a v1 layer version', () => { //GIVEN const stack = new Stack(); @@ -14,3 +14,16 @@ test('synthesized to a layer version', () => { Description: '/opt/awscli/aws', }); }); + +test('synthesized to a v2 layer version', () => { + //GIVEN + const stack = new Stack(); + + // WHEN + new AwsCliV2Layer(stack, 'MyLayer'); + + // THEN + expect(stack).toHaveResource('AWS::Lambda::LayerVersion', { + Description: '/opt/awscli/aws', + }); +}); From d81e06d5a5651cf332614d73e27bf6ed95d083a3 Mon Sep 17 00:00:00 2001 From: Otavio Macedo Date: Tue, 18 May 2021 10:09:32 +0100 Subject: [PATCH 048/134] fix(pipelines): stackOutput generates names too long to be used in useOutputs (#14680) If the name is ends up longer than 100 characters, it will drop as many characters as needed from the beginning of the string, since the last ones are usually more significant. Fixes #13552 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/pipelines/lib/pipeline.ts | 5 ++- packages/@aws-cdk/pipelines/test/testutil.ts | 17 ++++++++ .../pipelines/test/validation.test.ts | 40 ++++++++++++++++++- 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/pipelines/lib/pipeline.ts b/packages/@aws-cdk/pipelines/lib/pipeline.ts index 20f751078646e..e709f59fd4b82 100644 --- a/packages/@aws-cdk/pipelines/lib/pipeline.ts +++ b/packages/@aws-cdk/pipelines/lib/pipeline.ts @@ -12,6 +12,7 @@ import { AddStageOptions, AssetPublishingCommand, CdkStage, StackOutput } from ' // eslint-disable-next-line import { Construct as CoreConstruct } from '@aws-cdk/core'; +const CODE_BUILD_LENGTH_LIMIT = 100; /** * Properties for a CdkPipeline */ @@ -286,7 +287,9 @@ export class CdkPipeline extends CoreConstruct { if (!this._outputArtifacts[stack.artifactId]) { // We should have stored the ArtifactPath in the map, but its Artifact // property isn't publicly readable... - this._outputArtifacts[stack.artifactId] = new codepipeline.Artifact(`Artifact_${stack.artifactId}_Outputs`); + const artifactName = `${stack.artifactId}_Outputs`; + const compactName = artifactName.slice(artifactName.length - Math.min(artifactName.length, CODE_BUILD_LENGTH_LIMIT)); + this._outputArtifacts[stack.artifactId] = new codepipeline.Artifact(compactName); } return new StackOutput(this._outputArtifacts[stack.artifactId].atPath('outputs.json'), cfnOutput.logicalId); diff --git a/packages/@aws-cdk/pipelines/test/testutil.ts b/packages/@aws-cdk/pipelines/test/testutil.ts index f3513eee6c5ce..26d7f686399af 100644 --- a/packages/@aws-cdk/pipelines/test/testutil.ts +++ b/packages/@aws-cdk/pipelines/test/testutil.ts @@ -1,5 +1,6 @@ import * as fs from 'fs'; import * as path from 'path'; +import { annotateMatcher, InspectionFailure, PropertyMatcher } from '@aws-cdk/assert-internal'; import * as codepipeline from '@aws-cdk/aws-codepipeline'; import * as codepipeline_actions from '@aws-cdk/aws-codepipeline-actions'; import * as s3 from '@aws-cdk/aws-s3'; @@ -110,4 +111,20 @@ export function stackTemplate(stack: Stack) { const stage = Stage.of(stack); if (!stage) { throw new Error('stack not in a Stage'); } return stage.synth().getStackArtifact(stack.artifactId); +} + +export function stringNoLongerThan(length: number): PropertyMatcher { + return annotateMatcher({ $stringIsNoLongerThan: length }, (value: any, failure: InspectionFailure) => { + if (typeof value !== 'string') { + failure.failureReason = `Expected a string, but got '${typeof value}'`; + return false; + } + + if (value.length > length) { + failure.failureReason = `String is ${value.length} characters long. Expected at most ${length} characters`; + return false; + } + + return true; + }); } \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/validation.test.ts b/packages/@aws-cdk/pipelines/test/validation.test.ts index c78903612714e..71fa95a16f107 100644 --- a/packages/@aws-cdk/pipelines/test/validation.test.ts +++ b/packages/@aws-cdk/pipelines/test/validation.test.ts @@ -9,7 +9,7 @@ import { CfnOutput, Stack, Stage, StageProps } from '@aws-cdk/core'; import { Construct } from 'constructs'; import * as cdkp from '../lib'; import { } from './testmatchers'; -import { BucketStack, PIPELINE_ENV, TestApp, TestGitHubNpmPipeline } from './testutil'; +import { BucketStack, PIPELINE_ENV, stringNoLongerThan, TestApp, TestGitHubNpmPipeline } from './testutil'; let app: TestApp; let pipelineStack: Stack; @@ -39,6 +39,44 @@ afterEach(() => { app.cleanup(); }); +test('stackOutput generates names limited to 100 characters', () => { + const stage = new AppWithStackOutput(app, 'APreposterouslyLongAndComplicatedNameMadeUpJustToMakeItExceedTheLimitDefinedByCodeBuild'); + const pipeStage = pipeline.addApplicationStage(stage); + pipeStage.addActions(new cdkp.ShellScriptAction({ + actionName: 'TestOutput', + useOutputs: { + BUCKET_NAME: pipeline.stackOutput(stage.output), + }, + commands: ['echo $BUCKET_NAME'], + })); + + // THEN + expect(pipelineStack).toHaveResourceLike('AWS::CodePipeline::Pipeline', { + Stages: arrayWith({ + Name: 'APreposterouslyLongAndComplicatedNameMadeUpJustToMakeItExceedTheLimitDefinedByCodeBuild', + Actions: arrayWith( + deepObjectLike({ + Name: 'Stack.Deploy', + OutputArtifacts: [{ Name: stringNoLongerThan(100) }], + Configuration: { + OutputFileName: 'outputs.json', + }, + }), + deepObjectLike({ + ActionTypeId: { + Provider: 'CodeBuild', + }, + Configuration: { + ProjectName: anything(), + }, + InputArtifacts: [{ Name: stringNoLongerThan(100) }], + Name: 'TestOutput', + }), + ), + }), + }); +}); + test('can use stack outputs as validation inputs', () => { // GIVEN const stage = new AppWithStackOutput(app, 'MyApp'); From 73b9b4a89078d1425f4acdf50a6e9b5275b7e555 Mon Sep 17 00:00:00 2001 From: Otavio Macedo Date: Tue, 18 May 2021 11:09:45 +0100 Subject: [PATCH 049/134] fix(pipelines): self-mutating builds cannot be run in privileged mode (#14655) Fixes #11425 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../lib/actions/update-pipeline-action.ts | 12 +++++++++++- packages/@aws-cdk/pipelines/lib/pipeline.ts | 17 +++++++++++++++++ .../@aws-cdk/pipelines/test/pipeline.test.ts | 16 ++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/pipelines/lib/actions/update-pipeline-action.ts b/packages/@aws-cdk/pipelines/lib/actions/update-pipeline-action.ts index 0d77f616e7cf9..a5922ada39926 100644 --- a/packages/@aws-cdk/pipelines/lib/actions/update-pipeline-action.ts +++ b/packages/@aws-cdk/pipelines/lib/actions/update-pipeline-action.ts @@ -37,6 +37,13 @@ export interface UpdatePipelineActionProps { * @default - Automatically generated */ readonly projectName?: string; + + /** + * Whether the build step should run in privileged mode. + * + * @default - false + */ + readonly privileged?: boolean } /** @@ -58,7 +65,10 @@ export class UpdatePipelineAction extends CoreConstruct implements codepipeline. const selfMutationProject = new codebuild.PipelineProject(this, 'SelfMutation', { projectName: props.projectName, - environment: { buildImage: codebuild.LinuxBuildImage.STANDARD_5_0 }, + environment: { + buildImage: codebuild.LinuxBuildImage.STANDARD_5_0, + privileged: props.privileged ?? false, + }, buildSpec: codebuild.BuildSpec.fromObject({ version: '0.2', phases: { diff --git a/packages/@aws-cdk/pipelines/lib/pipeline.ts b/packages/@aws-cdk/pipelines/lib/pipeline.ts index e709f59fd4b82..5684a4eb0d604 100644 --- a/packages/@aws-cdk/pipelines/lib/pipeline.ts +++ b/packages/@aws-cdk/pipelines/lib/pipeline.ts @@ -120,6 +120,22 @@ export interface CdkPipelineProps { * @default true */ readonly selfMutating?: boolean; + + /** + * Whether the pipeline needs to build Docker images in the UpdatePipeline stage. + * + * If the UpdatePipeline stage tries to build a Docker image and this flag is not + * set to `true`, the build step will run in non-privileged mode and consequently + * will fail with a message like: + * + * > Cannot connect to the Docker daemon at unix:///var/run/docker.sock. + * > Is the docker daemon running? + * + * This flag has an effect only if `selfMutating` is also `true`. + * + * @default - false + */ + readonly supportDockerAssets?: boolean; } /** @@ -201,6 +217,7 @@ export class CdkPipeline extends CoreConstruct { pipelineStackName: pipelineStack.stackName, cdkCliVersion: props.cdkCliVersion, projectName: maybeSuffix(props.pipelineName, '-selfupdate'), + privileged: props.supportDockerAssets, })], }); } diff --git a/packages/@aws-cdk/pipelines/test/pipeline.test.ts b/packages/@aws-cdk/pipelines/test/pipeline.test.ts index 5069e9c6d542e..b04f9d7ff3a6e 100644 --- a/packages/@aws-cdk/pipelines/test/pipeline.test.ts +++ b/packages/@aws-cdk/pipelines/test/pipeline.test.ts @@ -302,6 +302,7 @@ test('pipeline has self-mutation stage', () => { expect(pipelineStack).toHaveResourceLike('AWS::CodeBuild::Project', { Environment: { Image: 'aws/codebuild/standard:5.0', + PrivilegedMode: false, }, Source: { BuildSpec: encodedJson(deepObjectLike({ @@ -358,6 +359,21 @@ test('selfmutation feature can be turned off', () => { }); }); +test('generates CodeBuild project in privileged mode', () => { + // WHEN + const stack = new Stack(app, 'PrivilegedPipelineStack', { env: PIPELINE_ENV }); + new TestGitHubNpmPipeline(stack, 'PrivilegedPipeline', { + supportDockerAssets: true, + }); + + // THEN + expect(stack).toHaveResourceLike('AWS::CodeBuild::Project', { + Environment: { + PrivilegedMode: true, + }, + }); +}); + test('overridden stack names are respected', () => { // WHEN pipeline.addApplicationStage(new OneStackAppWithCustomName(app, 'App1')); From 8395d9d1843beb40b2b7386104503e19d9c9cf87 Mon Sep 17 00:00:00 2001 From: Niranjan Jayakar Date: Tue, 18 May 2021 12:10:20 +0100 Subject: [PATCH 050/134] chore(cli): raise the timeout for the bootstrap test (#14740) The test case "upgrade legacy bootstrap stack to new bootstrap stack while in use" has been observed to intermittently timeout. The standard timeout of 10 minutes seems insufficient. The cause seems to be CloudFormation, which in some cases, sits there during an update for 10ish minutes perceivably doing nothing. Example (account: 416588550161): https://eu-west-2.console.aws.amazon.com/cloudformation/home?region=eu-west-2#/stacks/events?stackId=arn%3Aaws%3Acloudformation%3Aeu-west-2%3A416588550161%3Astack%2Fcdktest-07s3iezgzhzv-bootstrap-stack%2Fb38a15d0-b750-11eb-8ba6-0a290e8f7d04 Raise the timeout to 1 hour. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-cdk/test/integ/cli/bootstrapping.integtest.ts | 3 ++- packages/aws-cdk/test/integ/helpers/test-helpers.ts | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/aws-cdk/test/integ/cli/bootstrapping.integtest.ts b/packages/aws-cdk/test/integ/cli/bootstrapping.integtest.ts index e8510d3e49aee..37b0a803fb81e 100644 --- a/packages/aws-cdk/test/integ/cli/bootstrapping.integtest.ts +++ b/packages/aws-cdk/test/integ/cli/bootstrapping.integtest.ts @@ -54,7 +54,8 @@ integTest('upgrade legacy bootstrap stack to new bootstrap stack while in use', '--force', ], }); -})); +}), 3_600_000, // Observed in eu-west-2 that CF update takes over 10 minutes for this test. +); integTest('can and deploy if omitting execution policies', withDefaultFixture(async (fixture) => { const bootstrapStackName = fixture.bootstrapStackName; diff --git a/packages/aws-cdk/test/integ/helpers/test-helpers.ts b/packages/aws-cdk/test/integ/helpers/test-helpers.ts index 549c7a3b747d7..cfe2e57b59750 100644 --- a/packages/aws-cdk/test/integ/helpers/test-helpers.ts +++ b/packages/aws-cdk/test/integ/helpers/test-helpers.ts @@ -9,8 +9,11 @@ export type TestContext = { readonly output: NodeJS.WritableStream; }; /** * A wrapper for jest's 'test' which takes regression-disabled tests into account and prints a banner */ -export function integTest(name: string, - callback: (context: TestContext) => Promise) { +export function integTest( + name: string, + callback: (context: TestContext) => Promise, + timeoutMillis?: number, +) { // Integ tests can run concurrently, and are responsible for blocking themselves if they cannot. const runner = shouldSkip(name) ? test.skip : test.concurrent; @@ -36,7 +39,7 @@ export function integTest(name: string, process.stderr.write('✅'); } } - }); + }, timeoutMillis); } function shouldSkip(testName: string) { From 7fe329cd17502cf04c451153f6d19955621952dc Mon Sep 17 00:00:00 2001 From: Erik Karlsson Date: Tue, 18 May 2021 14:23:41 +0200 Subject: [PATCH 051/134] fix(cli): Updated typo user to uses (#14357) Corrected potential typo from 'user' to 'uses' ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/aws-cdk/bin/cdk.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aws-cdk/bin/cdk.ts b/packages/aws-cdk/bin/cdk.ts index a9d5c8e0d9f0b..7c69bcb43a3f3 100644 --- a/packages/aws-cdk/bin/cdk.ts +++ b/packages/aws-cdk/bin/cdk.ts @@ -58,7 +58,7 @@ async function parseCommandLineArguments() { .option('ec2creds', { type: 'boolean', alias: 'i', default: undefined, desc: 'Force trying to fetch EC2 instance credentials. Default: guess EC2 instance status' }) .option('version-reporting', { type: 'boolean', desc: 'Include the "AWS::CDK::Metadata" resource in synthesized templates (enabled by default)', default: undefined }) .option('path-metadata', { type: 'boolean', desc: 'Include "aws:cdk:path" CloudFormation metadata for each resource (enabled by default)', default: true }) - .option('asset-metadata', { type: 'boolean', desc: 'Include "aws:asset:*" CloudFormation metadata for resources that user assets (enabled by default)', default: true }) + .option('asset-metadata', { type: 'boolean', desc: 'Include "aws:asset:*" CloudFormation metadata for resources that uses assets (enabled by default)', default: true }) .option('role-arn', { type: 'string', alias: 'r', desc: 'ARN of Role to use when invoking CloudFormation', default: undefined, requiresArg: true }) .option('toolkit-stack-name', { type: 'string', desc: 'The name of the CDK toolkit stack', requiresArg: true }) .option('staging', { type: 'boolean', desc: 'Copy assets to the output directory (use --no-staging to disable, needed for local debugging the source files with SAM CLI)', default: true }) From 163e8122db994d0bea7077f025876dbeac490ead Mon Sep 17 00:00:00 2001 From: Jonathan Goldwasser Date: Tue, 18 May 2021 15:17:49 +0200 Subject: [PATCH 052/134] fix(core): cannot determine packaging when bundling that produces an archive is skipped (#14372) Bundling is skipped when the bundling directory already exists on disk (cache). If it previously produced a single archive file that has been moved to the staging directory the current logic either failed to determine the correct packaging type or threw because it expected the bundling directory to include a single archive file. Fix it by "touching" the archive file in the bundling directory after it has been moved to the staging directory. Fixes #14369 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/core/lib/asset-staging.ts | 9 ++++ packages/@aws-cdk/core/test/staging.test.ts | 54 ++++++++++++++++++++- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/core/lib/asset-staging.ts b/packages/@aws-cdk/core/lib/asset-staging.ts index 09820293b63ec..44ab0de0bdd5d 100644 --- a/packages/@aws-cdk/core/lib/asset-staging.ts +++ b/packages/@aws-cdk/core/lib/asset-staging.ts @@ -336,6 +336,15 @@ export class AssetStaging extends CoreConstruct { const stagedPath = path.resolve(this.assetOutdir, renderAssetFilename(assetHash, bundledAsset.extension)); this.stageAsset(bundledAsset.path, stagedPath, 'move'); + + // If bundling produced a single archive file we "touch" this file in the bundling + // directory after it has been moved to the staging directory. This way if bundling + // is skipped because the bundling directory already exists we can still determine + // the correct packaging type. + if (bundledAsset.packaging === FileAssetPackaging.FILE) { + fs.closeSync(fs.openSync(bundledAsset.path, 'w')); + } + return { assetHash, stagedPath, diff --git a/packages/@aws-cdk/core/test/staging.test.ts b/packages/@aws-cdk/core/test/staging.test.ts index ee87780a0957e..e492f0a2dce88 100644 --- a/packages/@aws-cdk/core/test/staging.test.ts +++ b/packages/@aws-cdk/core/test/staging.test.ts @@ -887,20 +887,70 @@ nodeunitShim({ // THEN const assembly = app.synth(); test.deepEqual(fs.readdirSync(assembly.directory), [ - 'asset.f43148c61174f444925231b5849b468f21e93b5d1469cd07c53625ffd039ef48', // this is the bundle dir but it's empty + 'asset.f43148c61174f444925231b5849b468f21e93b5d1469cd07c53625ffd039ef48', // this is the bundle dir 'asset.f43148c61174f444925231b5849b468f21e93b5d1469cd07c53625ffd039ef48.zip', 'cdk.out', 'manifest.json', 'stack.template.json', 'tree.json', ]); - test.equal(fs.readdirSync(path.join(assembly.directory, 'asset.f43148c61174f444925231b5849b468f21e93b5d1469cd07c53625ffd039ef48')).length, 0); // empty bundle dir + test.deepEqual(fs.readdirSync(path.join(assembly.directory, 'asset.f43148c61174f444925231b5849b468f21e93b5d1469cd07c53625ffd039ef48')), [ + 'test.zip', // bundle dir with "touched" bundled output file + ]); test.deepEqual(staging.packaging, FileAssetPackaging.FILE); test.deepEqual(staging.isArchive, true); test.done(); }, + 'bundling that produces a single archive file with disk cache'(test: Test) { + // GIVEN + const TEST_OUTDIR = path.join(__dirname, 'cdk.out'); + if (fs.existsSync(TEST_OUTDIR)) { + fs.removeSync(TEST_OUTDIR); + } + + const directory = path.join(__dirname, 'fs', 'fixtures', 'test1'); + + const app1 = new App({ outdir: TEST_OUTDIR }); + const stack1 = new Stack(app1, 'Stack'); + + const app2 = new App({ outdir: TEST_OUTDIR }); // same OUTDIR + const stack2 = new Stack(app2, 'stack'); + + // WHEN + const staging1 = new AssetStaging(stack1, 'Asset', { + sourcePath: directory, + bundling: { + image: BundlingDockerImage.fromRegistry('alpine'), + command: [DockerStubCommand.SINGLE_ARCHIVE], + outputType: BundlingOutput.ARCHIVED, + }, + }); + + // Now clear asset hash cache to show that during the second staging + // even though bundling is skipped it will correctly be considered + // as a FileAssetPackaging.FILE. + AssetStaging.clearAssetHashCache(); + + const staging2 = new AssetStaging(stack2, 'Asset', { + sourcePath: directory, + bundling: { + image: BundlingDockerImage.fromRegistry('alpine'), + command: [DockerStubCommand.SINGLE_ARCHIVE], + outputType: BundlingOutput.ARCHIVED, + }, + }); + + // THEN + test.deepEqual(staging1.packaging, FileAssetPackaging.FILE); + test.deepEqual(staging1.isArchive, true); + test.deepEqual(staging2.packaging, staging1.packaging); + test.deepEqual(staging2.isArchive, staging1.isArchive); + + test.done(); + }, + 'bundling that produces a single archive file with NOT_ARCHIVED'(test: Test) { // GIVEN const app = new App(); From c7469cfb59c8023eff88a8a5fb0512c94ab14204 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Tue, 18 May 2021 15:54:41 +0200 Subject: [PATCH 053/134] chore: declare more missing dependencies (#14491) Some more missing dependencies. `@types/node` is actually a proper public dependency of `cloudformation-diff`, as the `NodeJS` types escape into the public API. That means the rule that says `@types` may only occur in `devDependencies` is no longer true. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/cloudformation-diff/package.json | 1 + tools/eslint-plugin-cdk/package.json | 1 + tools/pkglint/lib/rules.ts | 21 ------------------- 3 files changed, 2 insertions(+), 21 deletions(-) diff --git a/packages/@aws-cdk/cloudformation-diff/package.json b/packages/@aws-cdk/cloudformation-diff/package.json index 4f0e79827e7a0..9d51aa3274650 100644 --- a/packages/@aws-cdk/cloudformation-diff/package.json +++ b/packages/@aws-cdk/cloudformation-diff/package.json @@ -22,6 +22,7 @@ "license": "Apache-2.0", "dependencies": { "@aws-cdk/cfnspec": "0.0.0", + "@types/node": "^10.17.56", "colors": "^1.4.0", "diff": "^5.0.0", "fast-deep-equal": "^3.1.3", diff --git a/tools/eslint-plugin-cdk/package.json b/tools/eslint-plugin-cdk/package.json index eb1af069bc75b..9a28d67e390b2 100644 --- a/tools/eslint-plugin-cdk/package.json +++ b/tools/eslint-plugin-cdk/package.json @@ -16,6 +16,7 @@ "@types/fs-extra": "^8.1.1", "@types/jest": "^26.0.23", "@types/node": "^10.17.59", + "@types/estree": "*", "eslint-plugin-rulesdir": "^0.2.0", "jest": "^26.6.3", "typescript": "~3.9.9" diff --git a/tools/pkglint/lib/rules.ts b/tools/pkglint/lib/rules.ts index 3555b4e4d0ae0..d7494a372c1c7 100644 --- a/tools/pkglint/lib/rules.ts +++ b/tools/pkglint/lib/rules.ts @@ -818,27 +818,6 @@ export class NodeCompatibility extends ValidationRule { } } -/** - * Verifies that the ``@types/`` dependencies are correctly recorded in ``devDependencies`` and not ``dependencies``. - */ -export class NoAtTypesInDependencies extends ValidationRule { - public readonly name = 'dependencies/at-types'; - - public validate(pkg: PackageJson): void { - const predicate = (s: string) => s.startsWith('@types/'); - for (const dependency of pkg.getDependencies(predicate)) { - pkg.report({ - ruleName: this.name, - message: `dependency on ${dependency.name}@${dependency.version} must be in devDependencies`, - fix: () => { - pkg.addDevDependency(dependency.name, dependency.version); - pkg.removeDependency(predicate); - }, - }); - } - } -} - function isCdkModuleName(name: string) { return !!name.match(/^@aws-cdk\//); } From 81aa61213b4f5e3bd9cbbc155264252bd64d0f5b Mon Sep 17 00:00:00 2001 From: Jonathan Goldwasser Date: Tue, 18 May 2021 16:35:02 +0200 Subject: [PATCH 054/134] fix(lambda-nodejs): banner and footer values not escaped (#14743) Escape values and use the new CLI options. Closes #13576 BREAKING CHANGE: using `banner` and `footer` now requires `esbuild` >= 0.9.0 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-lambda-nodejs/README.md | 5 ++--- packages/@aws-cdk/aws-lambda-nodejs/lib/bundling.ts | 4 ++-- packages/@aws-cdk/aws-lambda-nodejs/test/bundling.test.ts | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda-nodejs/README.md b/packages/@aws-cdk/aws-lambda-nodejs/README.md index c52b0fe2570c9..981bf6755148e 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/README.md +++ b/packages/@aws-cdk/aws-lambda-nodejs/README.md @@ -144,8 +144,8 @@ new lambda.NodejsFunction(this, 'my-handler', { keepNames: true, // defaults to false tsconfig: 'custom-tsconfig.json', // use custom-tsconfig.json instead of default, metafile: true, // include meta file, defaults to false - banner : '/* comments */', // by default no comments are passed - footer : '/* comments */', // by default no comments are passed + banner : '/* comments */', // requires esbuild >= 0.9.0, defaults to none + footer : '/* comments */', // requires esbuild >= 0.9.0, defaults to none }, }); ``` @@ -166,7 +166,6 @@ new lambda.NodejsFunction(this, 'my-handler-with-commands', { } // ... } - }); ``` diff --git a/packages/@aws-cdk/aws-lambda-nodejs/lib/bundling.ts b/packages/@aws-cdk/aws-lambda-nodejs/lib/bundling.ts index 8624dce8c1359..d28f43ca874c1 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/lib/bundling.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/lib/bundling.ts @@ -153,8 +153,8 @@ export class Bundling implements cdk.BundlingOptions { ...this.props.keepNames ? ['--keep-names'] : [], ...this.relativeTsconfigPath ? [`--tsconfig=${pathJoin(inputDir, this.relativeTsconfigPath)}`] : [], ...this.props.metafile ? [`--metafile=${pathJoin(outputDir, 'index.meta.json')}`] : [], - ...this.props.banner ? [`--banner='${this.props.banner}'`] : [], - ...this.props.footer ? [`--footer='${this.props.footer}'`] : [], + ...this.props.banner ? [`--banner:js=${JSON.stringify(this.props.banner)}`] : [], + ...this.props.footer ? [`--footer:js=${JSON.stringify(this.props.footer)}`] : [], ].join(' '); let depsCommand = ''; diff --git a/packages/@aws-cdk/aws-lambda-nodejs/test/bundling.test.ts b/packages/@aws-cdk/aws-lambda-nodejs/test/bundling.test.ts index 064c8458dbf97..e5441cb5aeadb 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/test/bundling.test.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/test/bundling.test.ts @@ -189,7 +189,7 @@ test('esbuild bundling with esbuild options', () => { '--minify --sourcemap --external:aws-sdk --loader:.png=dataurl', defineInstructions, '--log-level=silent --keep-names --tsconfig=/asset-input/lib/custom-tsconfig.ts', - '--metafile=/asset-output/index.meta.json --banner=\'/* comments */\' --footer=\'/* comments */\'', + '--metafile=/asset-output/index.meta.json --banner:js="/* comments */" --footer:js="/* comments */"', ].join(' '), ], }), From 396dca965b56bfbe8a7aedb2bcaddb196b5560c4 Mon Sep 17 00:00:00 2001 From: kirintw Date: Tue, 18 May 2021 23:07:50 +0800 Subject: [PATCH 055/134] fix(ecr): add validations for ECR repository names (#12613) Repository names are verified to conform with rules published by CloudFormation - https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecr-repository.html close #9877 ---- *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/lib/repository.ts | 28 ++++++++ .../@aws-cdk/aws-ecr/test/repository.test.ts | 69 +++++++++++++++++++ 2 files changed, 97 insertions(+) diff --git a/packages/@aws-cdk/aws-ecr/lib/repository.ts b/packages/@aws-cdk/aws-ecr/lib/repository.ts index 3734db8176d36..16331864e1e23 100644 --- a/packages/@aws-cdk/aws-ecr/lib/repository.ts +++ b/packages/@aws-cdk/aws-ecr/lib/repository.ts @@ -1,3 +1,4 @@ +import { EOL } from 'os'; import * as events from '@aws-cdk/aws-events'; import * as iam from '@aws-cdk/aws-iam'; import { IResource, Lazy, RemovalPolicy, Resource, Stack, Token } from '@aws-cdk/core'; @@ -440,6 +441,31 @@ export class Repository extends RepositoryBase { }); } + + private static validateRepositoryName(physicalName: string) { + const repositoryName = physicalName; + if (!repositoryName || Token.isUnresolved(repositoryName)) { + // the name is a late-bound value, not a defined string, + // so skip validation + return; + } + + const errors: string[] = []; + + // Rules codified from https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecr-repository.html + if (repositoryName.length < 2 || repositoryName.length > 256) { + errors.push('Repository name must be at least 2 and no more than 256 characters'); + } + const isPatternMatch = /^(?:[a-z0-9]+(?:[._-][a-z0-9]+)*\/)*[a-z0-9]+(?:[._-][a-z0-9]+)*$/.test(repositoryName); + if (!isPatternMatch) { + errors.push('Repository name must follow the specified pattern: (?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)*[a-z0-9]+(?:[._-][a-z0-9]+)*'); + } + + if (errors.length > 0) { + throw new Error(`Invalid ECR repository name (value: ${repositoryName})${EOL}${errors.join(EOL)}`); + } + } + public readonly repositoryName: string; public readonly repositoryArn: string; private readonly lifecycleRules = new Array(); @@ -451,6 +477,8 @@ export class Repository extends RepositoryBase { physicalName: props.repositoryName, }); + Repository.validateRepositoryName(this.physicalName); + const resource = new CfnRepository(this, 'Resource', { repositoryName: this.physicalName, // It says "Text", but they actually mean "Object". diff --git a/packages/@aws-cdk/aws-ecr/test/repository.test.ts b/packages/@aws-cdk/aws-ecr/test/repository.test.ts index 5c7efecca1380..9b8c1f79796c5 100644 --- a/packages/@aws-cdk/aws-ecr/test/repository.test.ts +++ b/packages/@aws-cdk/aws-ecr/test/repository.test.ts @@ -1,3 +1,4 @@ +import { EOL } from 'os'; import { expect as expectCDK, haveResource, haveResourceLike, ResourcePart } from '@aws-cdk/assert-internal'; import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; @@ -519,4 +520,72 @@ describe('repository', () => { })); }); }); + + describe('repository name validation', () => { + test('repository name validations', () => { + const stack = new cdk.Stack(); + + expect(() => new ecr.Repository(stack, 'Repo1', { + repositoryName: 'abc-xyz-34ab', + })).not.toThrow(); + + expect(() => new ecr.Repository(stack, 'Repo2', { + repositoryName: '124/pp-33', + })).not.toThrow(); + }); + + test('repository name validation skips tokenized values', () => { + const stack = new cdk.Stack(); + + expect(() => new ecr.Repository(stack, 'Repo', { + repositoryName: cdk.Lazy.string({ produce: () => '_REPO' }), + })).not.toThrow(); + }); + + test('fails with message on invalid repository names', () => { + const stack = new cdk.Stack(); + const repositoryName = `-repositoRy.--${new Array(256).join('$')}`; + const expectedErrors = [ + `Invalid ECR repository name (value: ${repositoryName})`, + 'Repository name must be at least 2 and no more than 256 characters', + 'Repository name must follow the specified pattern: (?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)*[a-z0-9]+(?:[._-][a-z0-9]+)*', + ].join(EOL); + + expect(() => new ecr.Repository(stack, 'Repo', { + repositoryName, + })).toThrow(expectedErrors); + }); + + test('fails if repository name has less than 2 or more than 256 characters', () => { + const stack = new cdk.Stack(); + + expect(() => new ecr.Repository(stack, 'Repo1', { + repositoryName: 'a', + })).toThrow(/at least 2/); + + expect(() => new ecr.Repository(stack, 'Repo2', { + repositoryName: new Array(258).join('x'), + })).toThrow(/no more than 256/); + }); + + test('fails if repository name does not follow the specified pattern', () => { + const stack = new cdk.Stack(); + + expect(() => new ecr.Repository(stack, 'Repo1', { + repositoryName: 'aAa', + })).toThrow(/must follow the specified pattern/); + + expect(() => new ecr.Repository(stack, 'Repo2', { + repositoryName: 'a--a', + })).toThrow(/must follow the specified pattern/); + + expect(() => new ecr.Repository(stack, 'Repo3', { + repositoryName: 'a./a-a', + })).toThrow(/must follow the specified pattern/); + + expect(() => new ecr.Repository(stack, 'Repo4', { + repositoryName: 'a//a-a', + })).toThrow(/must follow the specified pattern/); + }); + }); }); From 0b8ee97b7c029c5195de694a1d2eea309c343f61 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Tue, 18 May 2021 17:35:04 +0200 Subject: [PATCH 056/134] fix(pipelines): synth fails if 'aws-cdk' is not in `package.json` (#14745) Our `aws-sdk` dependency has recently added a `postinstall` script to their package. For whatever reason, if the `unsafe_perm` NPM config flag is not set to `true` and `npx` needs to install the `aws-cdk` package (because it's not in the user's `package.json` and so didn't get installed as part of `npm ci`), then the `postinstall` script fails. I've confirmed that this fixes it but I haven't 100% put my finger on the smoking gun yet. * `npx package-without-postinstall` works fine, even without this setting. * `npm install -g package-with-postinstall` also works fine, even though it's still installed as `root`. It seems to be the combination of `npx`, `root` and `postinstall` that is causing the issue. Fixes #14658. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../lib/synths/simple-synth-action.ts | 20 ++++ .../@aws-cdk/pipelines/test/builds.test.ts | 25 +++++ .../integ.pipeline-with-assets.expected.json | 103 ++++++++++-------- .../test/integ.pipeline.expected.json | 103 ++++++++++-------- 4 files changed, 155 insertions(+), 96 deletions(-) diff --git a/packages/@aws-cdk/pipelines/lib/synths/simple-synth-action.ts b/packages/@aws-cdk/pipelines/lib/synths/simple-synth-action.ts index eec90185b9744..b43355789feb0 100644 --- a/packages/@aws-cdk/pipelines/lib/synths/simple-synth-action.ts +++ b/packages/@aws-cdk/pipelines/lib/synths/simple-synth-action.ts @@ -211,6 +211,16 @@ export class SimpleSynthAction implements codepipeline.IAction, iam.IGrantable { synthCommand: options.synthCommand ?? 'npx cdk synth', vpc: options.vpc, subnetSelection: options.subnetSelection, + environment: { + ...options.environment, + environmentVariables: { + // Need this in case the CDK CLI is not in the 'package.json' of the project, + // and 'npx' is going to download it; without this setting, 'npx' will not properly + // install the package into the root user's home directory + NPM_CONFIG_UNSAFE_PERM: { value: 'true' }, + ...options.environment?.environmentVariables, + }, + }, }); } @@ -228,6 +238,16 @@ export class SimpleSynthAction implements codepipeline.IAction, iam.IGrantable { synthCommand: options.synthCommand ?? 'npx cdk synth', vpc: options.vpc, subnetSelection: options.subnetSelection, + environment: { + ...options.environment, + environmentVariables: { + // Need this in case the CDK CLI is not in the 'package.json' of the project, + // and 'npx' is going to download it; without this setting, 'npx' will not properly + // install the package into the root user's home directory + NPM_CONFIG_UNSAFE_PERM: { value: 'true' }, + ...options.environment?.environmentVariables, + }, + }, }); } diff --git a/packages/@aws-cdk/pipelines/test/builds.test.ts b/packages/@aws-cdk/pipelines/test/builds.test.ts index 5f91472b458f1..1a8f9b5c41208 100644 --- a/packages/@aws-cdk/pipelines/test/builds.test.ts +++ b/packages/@aws-cdk/pipelines/test/builds.test.ts @@ -124,6 +124,31 @@ test.each([['npm'], ['yarn']])('%s build respects subdirectory', (npmYarn) => { }); }); +test.each([['npm'], ['yarn']])('%s build sets UNSAFE_PERM=true', (npmYarn) => { + // WHEN + new TestGitHubNpmPipeline(pipelineStack, 'Cdk', { + sourceArtifact, + cloudAssemblyArtifact, + synthAction: npmYarnBuild(npmYarn)({ + sourceArtifact, + cloudAssemblyArtifact, + }), + }); + + // THEN + expect(pipelineStack).toHaveResourceLike('AWS::CodeBuild::Project', { + Environment: { + EnvironmentVariables: [ + { + Name: 'NPM_CONFIG_UNSAFE_PERM', + Type: 'PLAINTEXT', + Value: 'true', + }, + ], + }, + }); +}); + test.each([['npm'], ['yarn']])('%s assumes no build step by default', (npmYarn) => { // WHEN new TestGitHubNpmPipeline(pipelineStack, 'Cdk', { diff --git a/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets.expected.json b/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets.expected.json index 871d741989ca3..9d869296f2148 100644 --- a/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets.expected.json +++ b/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets.expected.json @@ -1,36 +1,4 @@ { - "Parameters": { - "BootstrapVersion": { - "Type": "AWS::SSM::Parameter::Value", - "Default": "/cdk-bootstrap/hnb659fds/version", - "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store." - } - }, - "Rules": { - "CheckBootstrapVersion": { - "Assertions": [ - { - "Assert": { - "Fn::Not": [ - { - "Fn::Contains": [ - [ - "1", - "2", - "3" - ], - { - "Ref": "BootstrapVersion" - } - ] - } - ] - }, - "AssertDescription": "CDK bootstrap stack version 4 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." - } - ] - } - }, "Resources": { "PipelineArtifactsBucketEncryptionKeyF5BF0670": { "Type": "AWS::KMS::Key", @@ -63,6 +31,20 @@ "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, + "PipelineArtifactsBucketEncryptionKeyAlias94A07392": { + "Type": "AWS::KMS::Alias", + "Properties": { + "AliasName": "alias/codepipeline-pipelinestackpipelinee95eedaa", + "TargetKeyId": { + "Fn::GetAtt": [ + "PipelineArtifactsBucketEncryptionKeyF5BF0670", + "Arn" + ] + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "PipelineArtifactsBucketAEA9A052": { "Type": "AWS::S3::Bucket", "Properties": { @@ -91,20 +73,6 @@ "UpdateReplacePolicy": "Retain", "DeletionPolicy": "Retain" }, - "PipelineArtifactsBucketEncryptionKeyAlias94A07392": { - "Type": "AWS::KMS::Alias", - "Properties": { - "AliasName": "alias/codepipeline-pipelinestackpipelinee95eedaa", - "TargetKeyId": { - "Fn::GetAtt": [ - "PipelineArtifactsBucketEncryptionKeyF5BF0670", - "Arn" - ] - } - }, - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, "PipelineRoleB27FAA37": { "Type": "AWS::IAM::Role", "Properties": { @@ -293,7 +261,7 @@ "ProjectName": { "Ref": "PipelineBuildSynthCdkBuildProject6BEFA8E6" }, - "EnvironmentVariables": "[{\"name\":\"_PROJECT_CONFIG_HASH\",\"type\":\"PLAINTEXT\",\"value\":\"a0d828d64c88aa603631eb7a08bc4748769f45d4b84d1b550cc85f41e49dc87e\"}]" + "EnvironmentVariables": "[{\"name\":\"_PROJECT_CONFIG_HASH\",\"type\":\"PLAINTEXT\",\"value\":\"da8c1ee6d645a5802d25355fec94a3f22a961102b74ac3846898d6b14e3a2c30\"}]" }, "InputArtifacts": [ { @@ -765,6 +733,13 @@ }, "Environment": { "ComputeType": "BUILD_GENERAL1_SMALL", + "EnvironmentVariables": [ + { + "Name": "NPM_CONFIG_UNSAFE_PERM", + "Type": "PLAINTEXT", + "Value": "true" + } + ], "Image": "aws/codebuild/standard:5.0", "ImagePullCredentialsType": "CODEBUILD", "PrivilegedMode": false, @@ -1507,5 +1482,37 @@ } } } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store." + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 4 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/integ.pipeline.expected.json b/packages/@aws-cdk/pipelines/test/integ.pipeline.expected.json index 68e86fd208660..304126cf148e6 100644 --- a/packages/@aws-cdk/pipelines/test/integ.pipeline.expected.json +++ b/packages/@aws-cdk/pipelines/test/integ.pipeline.expected.json @@ -1,36 +1,4 @@ { - "Parameters": { - "BootstrapVersion": { - "Type": "AWS::SSM::Parameter::Value", - "Default": "/cdk-bootstrap/hnb659fds/version", - "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store." - } - }, - "Rules": { - "CheckBootstrapVersion": { - "Assertions": [ - { - "Assert": { - "Fn::Not": [ - { - "Fn::Contains": [ - [ - "1", - "2", - "3" - ], - { - "Ref": "BootstrapVersion" - } - ] - } - ] - }, - "AssertDescription": "CDK bootstrap stack version 4 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." - } - ] - } - }, "Resources": { "PipelineArtifactsBucketEncryptionKeyF5BF0670": { "Type": "AWS::KMS::Key", @@ -63,6 +31,20 @@ "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, + "PipelineArtifactsBucketEncryptionKeyAlias94A07392": { + "Type": "AWS::KMS::Alias", + "Properties": { + "AliasName": "alias/codepipeline-pipelinestackpipelinee95eedaa", + "TargetKeyId": { + "Fn::GetAtt": [ + "PipelineArtifactsBucketEncryptionKeyF5BF0670", + "Arn" + ] + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "PipelineArtifactsBucketAEA9A052": { "Type": "AWS::S3::Bucket", "Properties": { @@ -91,20 +73,6 @@ "UpdateReplacePolicy": "Retain", "DeletionPolicy": "Retain" }, - "PipelineArtifactsBucketEncryptionKeyAlias94A07392": { - "Type": "AWS::KMS::Alias", - "Properties": { - "AliasName": "alias/codepipeline-pipelinestackpipelinee95eedaa", - "TargetKeyId": { - "Fn::GetAtt": [ - "PipelineArtifactsBucketEncryptionKeyF5BF0670", - "Arn" - ] - } - }, - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, "PipelineRoleB27FAA37": { "Type": "AWS::IAM::Role", "Properties": { @@ -283,7 +251,7 @@ "ProjectName": { "Ref": "PipelineBuildSynthCdkBuildProject6BEFA8E6" }, - "EnvironmentVariables": "[{\"name\":\"_PROJECT_CONFIG_HASH\",\"type\":\"PLAINTEXT\",\"value\":\"a0d828d64c88aa603631eb7a08bc4748769f45d4b84d1b550cc85f41e49dc87e\"}]" + "EnvironmentVariables": "[{\"name\":\"_PROJECT_CONFIG_HASH\",\"type\":\"PLAINTEXT\",\"value\":\"da8c1ee6d645a5802d25355fec94a3f22a961102b74ac3846898d6b14e3a2c30\"}]" }, "InputArtifacts": [ { @@ -698,6 +666,13 @@ }, "Environment": { "ComputeType": "BUILD_GENERAL1_SMALL", + "EnvironmentVariables": [ + { + "Name": "NPM_CONFIG_UNSAFE_PERM", + "Type": "PLAINTEXT", + "Value": "true" + } + ], "Image": "aws/codebuild/standard:5.0", "ImagePullCredentialsType": "CODEBUILD", "PrivilegedMode": false, @@ -1234,5 +1209,37 @@ } } } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store." + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 4 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } } -} +} \ No newline at end of file From beb01b549abc5a5c825e951649786589d2150a72 Mon Sep 17 00:00:00 2001 From: Niranjan Jayakar Date: Tue, 18 May 2021 17:01:18 +0100 Subject: [PATCH 057/134] revert(lambda-layer-awscli): add awscli v2.2.5 (#14487) (#14744) This reverts commit 704946a9df25d73f1ebcb47f9df9c73d75d82acc. The addition of the new lambda layer increases the size of the bundled monocdk package. This causes our pipeline to fail during our integration test. Including the awscliv2 lambda layer, the artifact size for monocdk is 86MB. It's not clear if the limitation is one from npm or a limitation imposed by verdaccio; the latter is used by our release pipeline to test unreleased npm modules. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../ec2/integ.environment-file.expected.json | 42 ++++++------ .../@aws-cdk/aws-eks/lib/kubectl-provider.ts | 4 +- .../lib/bucket-deployment.ts | 4 +- .../aws-s3-deployment/lib/lambda/index.py | 2 +- ...bucket-deployment-cloudfront.expected.json | 42 ++++++------ .../integ.bucket-deployment.expected.json | 66 +++++++++---------- .../@aws-cdk/lambda-layer-awscli/README.md | 4 +- .../lambda-layer-awscli/layer/build.sh | 23 +++---- .../lambda-layer-awscli/layer/v2.Dockerfile | 21 ------ .../lambda-layer-awscli/lib/awscli-layer.ts | 19 +----- .../test/awscli-layer.test.ts | 17 +---- 11 files changed, 95 insertions(+), 149 deletions(-) delete mode 100644 packages/@aws-cdk/lambda-layer-awscli/layer/v2.Dockerfile diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.environment-file.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.environment-file.expected.json index 18ba2efb78daf..c309c8a70f249 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.environment-file.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.environment-file.expected.json @@ -991,12 +991,12 @@ } } }, - "EnvFileDeploymentAwsCliV2Layer0F17A9A2": { + "EnvFileDeploymentAwsCliLayerA8FC897D": { "Type": "AWS::Lambda::LayerVersion", "Properties": { "Content": { "S3Bucket": { - "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3Bucket4AD27E1E" + "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7" }, "S3Key": { "Fn::Join": [ @@ -1009,7 +1009,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD" + "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" } ] } @@ -1022,7 +1022,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD" + "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" } ] } @@ -1219,7 +1219,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3BucketE7100B42" + "Ref": "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3Bucket55EFA30C" }, "S3Key": { "Fn::Join": [ @@ -1232,7 +1232,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3VersionKeyFB53BEE5" + "Ref": "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3VersionKey60329B70" } ] } @@ -1245,7 +1245,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3VersionKeyFB53BEE5" + "Ref": "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3VersionKey60329B70" } ] } @@ -1264,7 +1264,7 @@ "Handler": "index.handler", "Layers": [ { - "Ref": "EnvFileDeploymentAwsCliV2Layer0F17A9A2" + "Ref": "EnvFileDeploymentAwsCliLayerA8FC897D" } ], "Runtime": "python3.6", @@ -1336,29 +1336,29 @@ "Type": "AWS::SSM::Parameter::Value", "Default": "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id" }, - "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3Bucket4AD27E1E": { + "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7": { "Type": "String", - "Description": "S3 bucket for asset \"9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667\"" + "Description": "S3 bucket for asset \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" }, - "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD": { + "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F": { "Type": "String", - "Description": "S3 key for asset version \"9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667\"" + "Description": "S3 key for asset version \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" }, - "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667ArtifactHashCF1F391F": { + "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68ArtifactHashD9A515C3": { "Type": "String", - "Description": "Artifact hash for asset \"9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667\"" + "Description": "Artifact hash for asset \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" }, - "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3BucketE7100B42": { + "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3Bucket55EFA30C": { "Type": "String", - "Description": "S3 bucket for asset \"02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319\"" + "Description": "S3 bucket for asset \"c24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cf\"" }, - "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3VersionKeyFB53BEE5": { + "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3VersionKey60329B70": { "Type": "String", - "Description": "S3 key for asset version \"02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319\"" + "Description": "S3 key for asset version \"c24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cf\"" }, - "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319ArtifactHash0F167E69": { + "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfArtifactHash85F58E48": { "Type": "String", - "Description": "Artifact hash for asset \"02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319\"" + "Description": "Artifact hash for asset \"c24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cf\"" }, "AssetParameters972240f9dd6e036a93d5f081af9a24315b2053828ac049b3b19b2fa12d7ae64aS3Bucket1F1A8472": { "Type": "String", @@ -1385,4 +1385,4 @@ "Description": "Artifact hash for asset \"872561bf078edd1685d50c9ff821cdd60d2b2ddfb0013c4087e79bf2bb50724d\"" } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-eks/lib/kubectl-provider.ts b/packages/@aws-cdk/aws-eks/lib/kubectl-provider.ts index 0144a38a46e0e..b5bd8ed51b876 100644 --- a/packages/@aws-cdk/aws-eks/lib/kubectl-provider.ts +++ b/packages/@aws-cdk/aws-eks/lib/kubectl-provider.ts @@ -3,7 +3,7 @@ import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; import { Duration, Stack, NestedStack, Names } from '@aws-cdk/core'; import * as cr from '@aws-cdk/custom-resources'; -import { AwsCliV2Layer } from '@aws-cdk/lambda-layer-awscli'; +import { AwsCliLayer } from '@aws-cdk/lambda-layer-awscli'; import { KubectlLayer } from '@aws-cdk/lambda-layer-kubectl'; import { Construct } from 'constructs'; import { ICluster, Cluster } from './cluster'; @@ -86,7 +86,7 @@ export class KubectlProvider extends NestedStack { // allow user to customize the layer if (!props.cluster.kubectlLayer) { - handler.addLayers(new AwsCliV2Layer(this, 'AwsCliV2Layer')); + handler.addLayers(new AwsCliLayer(this, 'AwsCliLayer')); handler.addLayers(new KubectlLayer(this, 'KubectlLayer')); } else { handler.addLayers(props.cluster.kubectlLayer); diff --git a/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts b/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts index 09991219a1922..ca91f036dc5ed 100644 --- a/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts +++ b/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts @@ -5,7 +5,7 @@ import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; import * as s3 from '@aws-cdk/aws-s3'; import * as cdk from '@aws-cdk/core'; -import { AwsCliV2Layer } from '@aws-cdk/lambda-layer-awscli'; +import { AwsCliLayer } from '@aws-cdk/lambda-layer-awscli'; import { Construct } from 'constructs'; import { ISource, SourceConfig } from './source'; @@ -196,7 +196,7 @@ export class BucketDeployment extends CoreConstruct { const handler = new lambda.SingletonFunction(this, 'CustomResourceHandler', { uuid: this.renderSingletonUuid(props.memoryLimit), code: lambda.Code.fromAsset(path.join(__dirname, 'lambda')), - layers: [new AwsCliV2Layer(this, 'AwsCliV2Layer')], + layers: [new AwsCliLayer(this, 'AwsCliLayer')], runtime: lambda.Runtime.PYTHON_3_6, handler: 'index.handler', lambdaPurpose: 'Custom::CDKBucketDeployment', diff --git a/packages/@aws-cdk/aws-s3-deployment/lib/lambda/index.py b/packages/@aws-cdk/aws-s3-deployment/lib/lambda/index.py index f9a9ea69f4593..3935e3122529d 100644 --- a/packages/@aws-cdk/aws-s3-deployment/lib/lambda/index.py +++ b/packages/@aws-cdk/aws-s3-deployment/lib/lambda/index.py @@ -183,7 +183,7 @@ def create_metadata_args(raw_user_metadata, raw_system_metadata): #--------------------------------------------------------------------------------------------------- # executes an "aws" cli command def aws_command(*args): - aws="/opt/awscli/aws" # from AwsCliV2Layer + aws="/opt/awscli/aws" # from AwsCliLayer logger.info("| aws %s" % ' '.join(args)) subprocess.check_call([aws] + list(args)) diff --git a/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment-cloudfront.expected.json b/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment-cloudfront.expected.json index abfcd004e8c4b..76f906941f2bd 100644 --- a/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment-cloudfront.expected.json +++ b/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment-cloudfront.expected.json @@ -53,12 +53,12 @@ } } }, - "DeployWithInvalidationAwsCliV2Layer0A4BF3DF": { + "DeployWithInvalidationAwsCliLayerDEDD5787": { "Type": "AWS::Lambda::LayerVersion", "Properties": { "Content": { "S3Bucket": { - "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3Bucket4AD27E1E" + "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7" }, "S3Key": { "Fn::Join": [ @@ -71,7 +71,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD" + "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" } ] } @@ -84,7 +84,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD" + "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" } ] } @@ -295,7 +295,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3BucketE7100B42" + "Ref": "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3Bucket55EFA30C" }, "S3Key": { "Fn::Join": [ @@ -308,7 +308,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3VersionKeyFB53BEE5" + "Ref": "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3VersionKey60329B70" } ] } @@ -321,7 +321,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3VersionKeyFB53BEE5" + "Ref": "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3VersionKey60329B70" } ] } @@ -340,7 +340,7 @@ "Handler": "index.handler", "Layers": [ { - "Ref": "DeployWithInvalidationAwsCliV2Layer0A4BF3DF" + "Ref": "DeployWithInvalidationAwsCliLayerDEDD5787" } ], "Runtime": "python3.6", @@ -353,29 +353,29 @@ } }, "Parameters": { - "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3Bucket4AD27E1E": { + "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7": { "Type": "String", - "Description": "S3 bucket for asset \"9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667\"" + "Description": "S3 bucket for asset \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" }, - "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD": { + "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F": { "Type": "String", - "Description": "S3 key for asset version \"9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667\"" + "Description": "S3 key for asset version \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" }, - "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667ArtifactHashCF1F391F": { + "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68ArtifactHashD9A515C3": { "Type": "String", - "Description": "Artifact hash for asset \"9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667\"" + "Description": "Artifact hash for asset \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" }, - "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3BucketE7100B42": { + "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3Bucket55EFA30C": { "Type": "String", - "Description": "S3 bucket for asset \"02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319\"" + "Description": "S3 bucket for asset \"c24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cf\"" }, - "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3VersionKeyFB53BEE5": { + "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3VersionKey60329B70": { "Type": "String", - "Description": "S3 key for asset version \"02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319\"" + "Description": "S3 key for asset version \"c24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cf\"" }, - "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319ArtifactHash0F167E69": { + "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfArtifactHash85F58E48": { "Type": "String", - "Description": "Artifact hash for asset \"02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319\"" + "Description": "Artifact hash for asset \"c24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cf\"" }, "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eS3Bucket9CD8B20A": { "Type": "String", @@ -390,4 +390,4 @@ "Description": "Artifact hash for asset \"fc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222e\"" } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment.expected.json b/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment.expected.json index 054938cc10920..877298773816b 100644 --- a/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment.expected.json +++ b/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment.expected.json @@ -10,12 +10,12 @@ "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, - "DeployMeAwsCliV2LayerA4132521": { + "DeployMeAwsCliLayer5F9219E9": { "Type": "AWS::Lambda::LayerVersion", "Properties": { "Content": { "S3Bucket": { - "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3Bucket4AD27E1E" + "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7" }, "S3Key": { "Fn::Join": [ @@ -28,7 +28,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD" + "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" } ] } @@ -41,7 +41,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD" + "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" } ] } @@ -304,7 +304,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3BucketE7100B42" + "Ref": "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3Bucket55EFA30C" }, "S3Key": { "Fn::Join": [ @@ -317,7 +317,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3VersionKeyFB53BEE5" + "Ref": "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3VersionKey60329B70" } ] } @@ -330,7 +330,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3VersionKeyFB53BEE5" + "Ref": "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3VersionKey60329B70" } ] } @@ -349,7 +349,7 @@ "Handler": "index.handler", "Layers": [ { - "Ref": "DeployMeAwsCliV2LayerA4132521" + "Ref": "DeployMeAwsCliLayer5F9219E9" } ], "Runtime": "python3.6", @@ -365,12 +365,12 @@ "UpdateReplacePolicy": "Retain", "DeletionPolicy": "Retain" }, - "DeployWithPrefixAwsCliV2Layer076E52D4": { + "DeployWithPrefixAwsCliLayerC9DDB597": { "Type": "AWS::Lambda::LayerVersion", "Properties": { "Content": { "S3Bucket": { - "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3Bucket4AD27E1E" + "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7" }, "S3Key": { "Fn::Join": [ @@ -383,7 +383,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD" + "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" } ] } @@ -396,7 +396,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD" + "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" } ] } @@ -473,12 +473,12 @@ "UpdateReplacePolicy": "Retain", "DeletionPolicy": "Retain" }, - "DeployWithMetadataAwsCliV2Layer791A6E48": { + "DeployWithMetadataAwsCliLayer2C774B41": { "Type": "AWS::Lambda::LayerVersion", "Properties": { "Content": { "S3Bucket": { - "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3Bucket4AD27E1E" + "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7" }, "S3Key": { "Fn::Join": [ @@ -491,7 +491,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD" + "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" } ] } @@ -504,7 +504,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD" + "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" } ] } @@ -584,12 +584,12 @@ "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, - "DeployMeWithoutDeletingFilesOnDestinationAwsCliV2Layer7C5D4E6E": { + "DeployMeWithoutDeletingFilesOnDestinationAwsCliLayer4D54C41C": { "Type": "AWS::Lambda::LayerVersion", "Properties": { "Content": { "S3Bucket": { - "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3Bucket4AD27E1E" + "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7" }, "S3Key": { "Fn::Join": [ @@ -602,7 +602,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD" + "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" } ] } @@ -615,7 +615,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD" + "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" } ] } @@ -688,29 +688,29 @@ } }, "Parameters": { - "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3Bucket4AD27E1E": { + "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7": { "Type": "String", - "Description": "S3 bucket for asset \"9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667\"" + "Description": "S3 bucket for asset \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" }, - "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667S3VersionKeyDCC716CD": { + "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F": { "Type": "String", - "Description": "S3 key for asset version \"9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667\"" + "Description": "S3 key for asset version \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" }, - "AssetParameters9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667ArtifactHashCF1F391F": { + "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68ArtifactHashD9A515C3": { "Type": "String", - "Description": "Artifact hash for asset \"9789bc66af5e585ba19c61644177b6f5c813f00545d0202416c16d332625a667\"" + "Description": "Artifact hash for asset \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" }, - "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3BucketE7100B42": { + "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3Bucket55EFA30C": { "Type": "String", - "Description": "S3 bucket for asset \"02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319\"" + "Description": "S3 bucket for asset \"c24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cf\"" }, - "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319S3VersionKeyFB53BEE5": { + "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfS3VersionKey60329B70": { "Type": "String", - "Description": "S3 key for asset version \"02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319\"" + "Description": "S3 key for asset version \"c24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cf\"" }, - "AssetParameters02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319ArtifactHash0F167E69": { + "AssetParametersc24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cfArtifactHash85F58E48": { "Type": "String", - "Description": "Artifact hash for asset \"02c8ff4f6d45dba3e787012cafd143192b2369a89115356da36548706b54f319\"" + "Description": "Artifact hash for asset \"c24b999656e4fe6c609c31bae56a1cf4717a405619c3aa6ba1bc686b8c2c86cf\"" }, "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eS3Bucket9CD8B20A": { "Type": "String", @@ -725,4 +725,4 @@ "Description": "Artifact hash for asset \"fc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222e\"" } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/lambda-layer-awscli/README.md b/packages/@aws-cdk/lambda-layer-awscli/README.md index 4132783bc3966..baad6362f5e21 100644 --- a/packages/@aws-cdk/lambda-layer-awscli/README.md +++ b/packages/@aws-cdk/lambda-layer-awscli/README.md @@ -10,13 +10,13 @@ -This module exports two classes called `AwsCliLayer` & `AwsCliV2Layer` which is a `lambda.Layer` that bundles the AWS CLI. +This module exports a single class called `AwsCliLayer` which is a `lambda.Layer` that bundles the AWS CLI. Usage: ```ts const fn = new lambda.Function(...); -fn.addLayers(new AwsCliV2Layer(stack, 'AwsCliV2Layer')); +fn.addLayers(new AwsCliLayer(stack, 'AwsCliLayer')); ``` The CLI will be installed under `/opt/awscli/aws`. diff --git a/packages/@aws-cdk/lambda-layer-awscli/layer/build.sh b/packages/@aws-cdk/lambda-layer-awscli/layer/build.sh index d1f547312dde7..a7c13263ebdce 100755 --- a/packages/@aws-cdk/lambda-layer-awscli/layer/build.sh +++ b/packages/@aws-cdk/lambda-layer-awscli/layer/build.sh @@ -3,21 +3,16 @@ set -euo pipefail cd $(dirname $0) -echo ">> Building AWS Lambda layer inside docker images..." +echo ">> Building AWS Lambda layer inside a docker image..." -V1_TAG="lambda-awscli-layer" -V2_TAG="lambda-awscli-v2-layer" +TAG='aws-lambda-layer' -docker build -t "${V1_TAG}" . -f Dockerfile -docker build -t "${V2_TAG}" . -f v2.Dockerfile +docker build -t ${TAG} . -echo ">> Extrating layer zip files from the build containers..." -V1_CONTAINER=$(docker run -d ${V1_TAG} false) -V2_CONTAINER=$(docker run -d ${V2_TAG} false) -docker cp "${V1_CONTAINER}:/layer.zip" ../lib/layer.zip -docker cp "${V2_CONTAINER}:/layer_v2.zip" ../lib/layer_v2.zip +echo ">> Extrating layer.zip from the build container..." +CONTAINER=$(docker run -d ${TAG} false) +docker cp ${CONTAINER}:/layer.zip ../lib/layer.zip -echo ">> Stopping containers..." -docker rm -f "${V1_CONTAINER}" -docker rm -f "${V2_CONTAINER}" -echo ">> lib/layer.zip & lib/layer_v2.zip is ready" +echo ">> Stopping container..." +docker rm -f ${CONTAINER} +echo ">> lib/layer.zip is ready" diff --git a/packages/@aws-cdk/lambda-layer-awscli/layer/v2.Dockerfile b/packages/@aws-cdk/lambda-layer-awscli/layer/v2.Dockerfile deleted file mode 100644 index 801dd434e4c3e..0000000000000 --- a/packages/@aws-cdk/lambda-layer-awscli/layer/v2.Dockerfile +++ /dev/null @@ -1,21 +0,0 @@ -FROM public.ecr.aws/lambda/provided:latest - -ARG AWSCLI_V2_VERSION=2.2.5 -USER root -RUN set -ex; \ - yum update -y; \ - yum install -y zip unzip wget tar gzip; -WORKDIR /tmp -RUN set -ex; \ - mkdir -p /opt; \ - curl -sSL "https://awscli.amazonaws.com/awscli-exe-linux-x86_64-$AWSCLI_V2_VERSION.zip" -o awscli-bundle.zip; \ - unzip awscli-bundle.zip; \ - mv ./aws/dist /opt/awscli; \ - rm -rf /opt/awscli/awscli/examples; \ - cd /opt; \ - zip --symlinks -r ../layer_v2.zip *; \ - ls -alh /layer_v2.zip; \ - /opt/awscli/aws --version; - -WORKDIR / -ENTRYPOINT [ "/bin/bash" ] diff --git a/packages/@aws-cdk/lambda-layer-awscli/lib/awscli-layer.ts b/packages/@aws-cdk/lambda-layer-awscli/lib/awscli-layer.ts index 58f7f39882bb3..525babcac82f3 100644 --- a/packages/@aws-cdk/lambda-layer-awscli/lib/awscli-layer.ts +++ b/packages/@aws-cdk/lambda-layer-awscli/lib/awscli-layer.ts @@ -5,7 +5,7 @@ import * as lambda from '@aws-cdk/aws-lambda'; import { Construct } from 'constructs'; /** - * An AWS Lambda layer that includes the AWS CLI V1. + * An AWS Lambda layer that includes the AWS CLI. */ export class AwsCliLayer extends lambda.LayerVersion { constructor(scope: Construct, id: string) { @@ -19,24 +19,9 @@ export class AwsCliLayer extends lambda.LayerVersion { } } -/** - * An AWS Lambda layer that includes the AWS CLI V2. - */ -export class AwsCliV2Layer extends lambda.LayerVersion { - constructor(scope: Construct, id: string) { - super(scope, id, { - code: lambda.Code.fromAsset(path.join(__dirname, 'layer_v2.zip'), { - // we hash the Dockerfile (it contains the tools versions) because hashing the zip is non-deterministic - assetHash: hashFile(path.join(__dirname, '..', 'layer', 'v2.Dockerfile')), - }), - description: '/opt/awscli/aws', - }); - } -} - function hashFile(fileName: string) { return crypto .createHash('sha256') .update(fs.readFileSync(fileName)) .digest('hex'); -} +} \ No newline at end of file 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 dec56b52a1aed..2a5c4b7d80043 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,8 +1,8 @@ import { Stack } from '@aws-cdk/core'; -import { AwsCliLayer, AwsCliV2Layer } from '../lib'; +import { AwsCliLayer } from '../lib'; import '@aws-cdk/assert-internal/jest'; -test('synthesized to a v1 layer version', () => { +test('synthesized to a layer version', () => { //GIVEN const stack = new Stack(); @@ -14,16 +14,3 @@ test('synthesized to a v1 layer version', () => { Description: '/opt/awscli/aws', }); }); - -test('synthesized to a v2 layer version', () => { - //GIVEN - const stack = new Stack(); - - // WHEN - new AwsCliV2Layer(stack, 'MyLayer'); - - // THEN - expect(stack).toHaveResource('AWS::Lambda::LayerVersion', { - Description: '/opt/awscli/aws', - }); -}); From 2678b714a08f9756b02ce5217c9f8d836081968b Mon Sep 17 00:00:00 2001 From: Niranjan Jayakar Date: Tue, 18 May 2021 17:33:11 +0100 Subject: [PATCH 058/134] chore: disable automated dependency upgrades (#14749) Temporary disable --- .github/workflows/yarn-upgrade.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/yarn-upgrade.yml b/.github/workflows/yarn-upgrade.yml index 13d551cf740cf..c0df170ff30ef 100644 --- a/.github/workflows/yarn-upgrade.yml +++ b/.github/workflows/yarn-upgrade.yml @@ -1,9 +1,10 @@ name: Yarn Upgrade on: - schedule: + # Disable this workflow + #schedule: # Every wednesday at 13:37 UTC - - cron: 37 13 * * 3 + #- cron: 37 13 * * 3 workflow_dispatch: {} jobs: From 773ca8c5d2a845f392f530d7710020075b884c72 Mon Sep 17 00:00:00 2001 From: Jonathan Goldwasser Date: Wed, 19 May 2021 06:28:16 +0200 Subject: [PATCH 059/134] feat(custom-resources): restrict output of AwsCustomResource to list of paths (#14041) It was already possible to restrict to a single path. Add option to restrict to multiple paths and deprecate the single path option. Also document this option in the README. See https://github.com/aws/aws-cdk/issues/2825#issuecomment-814999890 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../test/integ.assign-public-ip.expected.json | 18 +++---- ...nteg.user-pool-domain-cfdist.expected.json | 18 +++---- ...elasticsearch.custom-kms-key.expected.json | 18 +++---- .../test/integ.elasticsearch.expected.json | 18 +++---- ...sticsearch.unsignedbasicauth.expected.json | 18 +++---- .../test/logs/integ.log-group.expected.json | 18 +++---- .../integ.globalaccelerator.expected.json | 18 +++---- .../aws-msk/test/integ.cluster.expected.json | 18 +++---- ...endpoint-service-domain-name.expected.json | 18 +++---- packages/@aws-cdk/custom-resources/README.md | 24 ++++++++++ .../aws-custom-resource.ts | 14 ++++++ .../lib/aws-custom-resource/runtime/index.ts | 27 +++++++++-- .../aws-custom-resource-provider.test.ts | 47 +++++++++++++++++++ 13 files changed, 190 insertions(+), 84 deletions(-) diff --git a/packages/@aws-cdk-containers/ecs-service-extensions/test/integ.assign-public-ip.expected.json b/packages/@aws-cdk-containers/ecs-service-extensions/test/integ.assign-public-ip.expected.json index 2ed4b9bb1fe3e..f3a7aeeb43fb1 100644 --- a/packages/@aws-cdk-containers/ecs-service-extensions/test/integ.assign-public-ip.expected.json +++ b/packages/@aws-cdk-containers/ecs-service-extensions/test/integ.assign-public-ip.expected.json @@ -1190,7 +1190,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3Bucket4DD075F7" + "Ref": "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3BucketD609D0D9" }, "S3Key": { "Fn::Join": [ @@ -1203,7 +1203,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3VersionKeyBD0E03B7" + "Ref": "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3VersionKey77CF589B" } ] } @@ -1216,7 +1216,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3VersionKeyBD0E03B7" + "Ref": "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3VersionKey77CF589B" } ] } @@ -1266,17 +1266,17 @@ "Type": "String", "Description": "Artifact hash for asset \"daeb79e3cee39c9b902dc0d5c780223e227ed573ea60976252947adab5fb2be1\"" }, - "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3Bucket4DD075F7": { + "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3BucketD609D0D9": { "Type": "String", - "Description": "S3 bucket for asset \"b965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98\"" + "Description": "S3 bucket for asset \"4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02c\"" }, - "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3VersionKeyBD0E03B7": { + "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3VersionKey77CF589B": { "Type": "String", - "Description": "S3 key for asset version \"b965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98\"" + "Description": "S3 key for asset version \"4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02c\"" }, - "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98ArtifactHash35A756EB": { + "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cArtifactHash86CFA15D": { "Type": "String", - "Description": "Artifact hash for asset \"b965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98\"" + "Description": "Artifact hash for asset \"4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02c\"" } }, "Outputs": { diff --git a/packages/@aws-cdk/aws-cognito/test/integ.user-pool-domain-cfdist.expected.json b/packages/@aws-cdk/aws-cognito/test/integ.user-pool-domain-cfdist.expected.json index 6d825755af54d..31a16a8f26393 100644 --- a/packages/@aws-cdk/aws-cognito/test/integ.user-pool-domain-cfdist.expected.json +++ b/packages/@aws-cdk/aws-cognito/test/integ.user-pool-domain-cfdist.expected.json @@ -146,7 +146,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3Bucket4DD075F7" + "Ref": "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3BucketD609D0D9" }, "S3Key": { "Fn::Join": [ @@ -159,7 +159,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3VersionKeyBD0E03B7" + "Ref": "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3VersionKey77CF589B" } ] } @@ -172,7 +172,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3VersionKeyBD0E03B7" + "Ref": "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3VersionKey77CF589B" } ] } @@ -213,17 +213,17 @@ } }, "Parameters": { - "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3Bucket4DD075F7": { + "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3BucketD609D0D9": { "Type": "String", - "Description": "S3 bucket for asset \"b965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98\"" + "Description": "S3 bucket for asset \"4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02c\"" }, - "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3VersionKeyBD0E03B7": { + "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3VersionKey77CF589B": { "Type": "String", - "Description": "S3 key for asset version \"b965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98\"" + "Description": "S3 key for asset version \"4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02c\"" }, - "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98ArtifactHash35A756EB": { + "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cArtifactHash86CFA15D": { "Type": "String", - "Description": "Artifact hash for asset \"b965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98\"" + "Description": "Artifact hash for asset \"4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02c\"" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-elasticsearch/test/integ.elasticsearch.custom-kms-key.expected.json b/packages/@aws-cdk/aws-elasticsearch/test/integ.elasticsearch.custom-kms-key.expected.json index eb826dda1f40a..714b07a76c75b 100644 --- a/packages/@aws-cdk/aws-elasticsearch/test/integ.elasticsearch.custom-kms-key.expected.json +++ b/packages/@aws-cdk/aws-elasticsearch/test/integ.elasticsearch.custom-kms-key.expected.json @@ -343,7 +343,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3Bucket4DD075F7" + "Ref": "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3BucketD609D0D9" }, "S3Key": { "Fn::Join": [ @@ -356,7 +356,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3VersionKeyBD0E03B7" + "Ref": "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3VersionKey77CF589B" } ] } @@ -369,7 +369,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3VersionKeyBD0E03B7" + "Ref": "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3VersionKey77CF589B" } ] } @@ -396,17 +396,17 @@ } }, "Parameters": { - "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3Bucket4DD075F7": { + "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3BucketD609D0D9": { "Type": "String", - "Description": "S3 bucket for asset \"b965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98\"" + "Description": "S3 bucket for asset \"4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02c\"" }, - "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3VersionKeyBD0E03B7": { + "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3VersionKey77CF589B": { "Type": "String", - "Description": "S3 key for asset version \"b965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98\"" + "Description": "S3 key for asset version \"4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02c\"" }, - "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98ArtifactHash35A756EB": { + "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cArtifactHash86CFA15D": { "Type": "String", - "Description": "Artifact hash for asset \"b965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98\"" + "Description": "Artifact hash for asset \"4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02c\"" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-elasticsearch/test/integ.elasticsearch.expected.json b/packages/@aws-cdk/aws-elasticsearch/test/integ.elasticsearch.expected.json index 8788b810235ec..4644a8905a74b 100644 --- a/packages/@aws-cdk/aws-elasticsearch/test/integ.elasticsearch.expected.json +++ b/packages/@aws-cdk/aws-elasticsearch/test/integ.elasticsearch.expected.json @@ -275,7 +275,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3Bucket4DD075F7" + "Ref": "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3BucketD609D0D9" }, "S3Key": { "Fn::Join": [ @@ -288,7 +288,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3VersionKeyBD0E03B7" + "Ref": "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3VersionKey77CF589B" } ] } @@ -301,7 +301,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3VersionKeyBD0E03B7" + "Ref": "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3VersionKey77CF589B" } ] } @@ -566,17 +566,17 @@ } }, "Parameters": { - "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3Bucket4DD075F7": { + "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3BucketD609D0D9": { "Type": "String", - "Description": "S3 bucket for asset \"b965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98\"" + "Description": "S3 bucket for asset \"4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02c\"" }, - "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3VersionKeyBD0E03B7": { + "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3VersionKey77CF589B": { "Type": "String", - "Description": "S3 key for asset version \"b965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98\"" + "Description": "S3 key for asset version \"4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02c\"" }, - "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98ArtifactHash35A756EB": { + "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cArtifactHash86CFA15D": { "Type": "String", - "Description": "Artifact hash for asset \"b965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98\"" + "Description": "Artifact hash for asset \"4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02c\"" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-elasticsearch/test/integ.elasticsearch.unsignedbasicauth.expected.json b/packages/@aws-cdk/aws-elasticsearch/test/integ.elasticsearch.unsignedbasicauth.expected.json index 3a59982581776..35a39a6eea608 100644 --- a/packages/@aws-cdk/aws-elasticsearch/test/integ.elasticsearch.unsignedbasicauth.expected.json +++ b/packages/@aws-cdk/aws-elasticsearch/test/integ.elasticsearch.unsignedbasicauth.expected.json @@ -191,7 +191,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3Bucket4DD075F7" + "Ref": "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3BucketD609D0D9" }, "S3Key": { "Fn::Join": [ @@ -204,7 +204,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3VersionKeyBD0E03B7" + "Ref": "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3VersionKey77CF589B" } ] } @@ -217,7 +217,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3VersionKeyBD0E03B7" + "Ref": "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3VersionKey77CF589B" } ] } @@ -243,17 +243,17 @@ } }, "Parameters": { - "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3Bucket4DD075F7": { + "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3BucketD609D0D9": { "Type": "String", - "Description": "S3 bucket for asset \"b965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98\"" + "Description": "S3 bucket for asset \"4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02c\"" }, - "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3VersionKeyBD0E03B7": { + "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3VersionKey77CF589B": { "Type": "String", - "Description": "S3 key for asset version \"b965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98\"" + "Description": "S3 key for asset version \"4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02c\"" }, - "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98ArtifactHash35A756EB": { + "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cArtifactHash86CFA15D": { "Type": "String", - "Description": "Artifact hash for asset \"b965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98\"" + "Description": "Artifact hash for asset \"4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02c\"" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-events-targets/test/logs/integ.log-group.expected.json b/packages/@aws-cdk/aws-events-targets/test/logs/integ.log-group.expected.json index 9764d7387b08a..8ef384b7f43a7 100644 --- a/packages/@aws-cdk/aws-events-targets/test/logs/integ.log-group.expected.json +++ b/packages/@aws-cdk/aws-events-targets/test/logs/integ.log-group.expected.json @@ -172,7 +172,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3Bucket4DD075F7" + "Ref": "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3BucketD609D0D9" }, "S3Key": { "Fn::Join": [ @@ -185,7 +185,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3VersionKeyBD0E03B7" + "Ref": "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3VersionKey77CF589B" } ] } @@ -198,7 +198,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3VersionKeyBD0E03B7" + "Ref": "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3VersionKey77CF589B" } ] } @@ -472,17 +472,17 @@ } }, "Parameters": { - "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3Bucket4DD075F7": { + "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3BucketD609D0D9": { "Type": "String", - "Description": "S3 bucket for asset \"b965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98\"" + "Description": "S3 bucket for asset \"4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02c\"" }, - "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3VersionKeyBD0E03B7": { + "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3VersionKey77CF589B": { "Type": "String", - "Description": "S3 key for asset version \"b965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98\"" + "Description": "S3 key for asset version \"4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02c\"" }, - "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98ArtifactHash35A756EB": { + "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cArtifactHash86CFA15D": { "Type": "String", - "Description": "Artifact hash for asset \"b965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98\"" + "Description": "Artifact hash for asset \"4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02c\"" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-globalaccelerator-endpoints/test/integ.globalaccelerator.expected.json b/packages/@aws-cdk/aws-globalaccelerator-endpoints/test/integ.globalaccelerator.expected.json index 72b6e81da9827..9d516680000e1 100644 --- a/packages/@aws-cdk/aws-globalaccelerator-endpoints/test/integ.globalaccelerator.expected.json +++ b/packages/@aws-cdk/aws-globalaccelerator-endpoints/test/integ.globalaccelerator.expected.json @@ -920,7 +920,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3Bucket4DD075F7" + "Ref": "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3BucketD609D0D9" }, "S3Key": { "Fn::Join": [ @@ -933,7 +933,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3VersionKeyBD0E03B7" + "Ref": "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3VersionKey77CF589B" } ] } @@ -946,7 +946,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3VersionKeyBD0E03B7" + "Ref": "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3VersionKey77CF589B" } ] } @@ -976,17 +976,17 @@ "Type": "AWS::SSM::Parameter::Value", "Default": "/aws/service/ami-amazon-linux-latest/amzn-ami-hvm-x86_64-gp2" }, - "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3Bucket4DD075F7": { + "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3BucketD609D0D9": { "Type": "String", - "Description": "S3 bucket for asset \"b965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98\"" + "Description": "S3 bucket for asset \"4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02c\"" }, - "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3VersionKeyBD0E03B7": { + "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3VersionKey77CF589B": { "Type": "String", - "Description": "S3 key for asset version \"b965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98\"" + "Description": "S3 key for asset version \"4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02c\"" }, - "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98ArtifactHash35A756EB": { + "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cArtifactHash86CFA15D": { "Type": "String", - "Description": "Artifact hash for asset \"b965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98\"" + "Description": "Artifact hash for asset \"4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02c\"" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-msk/test/integ.cluster.expected.json b/packages/@aws-cdk/aws-msk/test/integ.cluster.expected.json index 769c0533269cd..9a0d0db6e2325 100644 --- a/packages/@aws-cdk/aws-msk/test/integ.cluster.expected.json +++ b/packages/@aws-cdk/aws-msk/test/integ.cluster.expected.json @@ -524,7 +524,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParametersf56a9c742f3e99b26237d5d0912c69f9db8289c13656fdcb490fd017d801c483S3Bucket9DEDD0AB" + "Ref": "AssetParameters5c61041c12314e1ad8e67a0107fa3733382a206a78cdc1576fffa7e93caca5b4S3BucketB17E5ABD" }, "S3Key": { "Fn::Join": [ @@ -537,7 +537,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersf56a9c742f3e99b26237d5d0912c69f9db8289c13656fdcb490fd017d801c483S3VersionKeyA54743D3" + "Ref": "AssetParameters5c61041c12314e1ad8e67a0107fa3733382a206a78cdc1576fffa7e93caca5b4S3VersionKey77778F6A" } ] } @@ -550,7 +550,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersf56a9c742f3e99b26237d5d0912c69f9db8289c13656fdcb490fd017d801c483S3VersionKeyA54743D3" + "Ref": "AssetParameters5c61041c12314e1ad8e67a0107fa3733382a206a78cdc1576fffa7e93caca5b4S3VersionKey77778F6A" } ] } @@ -576,17 +576,17 @@ } }, "Parameters": { - "AssetParametersf56a9c742f3e99b26237d5d0912c69f9db8289c13656fdcb490fd017d801c483S3Bucket9DEDD0AB": { + "AssetParameters5c61041c12314e1ad8e67a0107fa3733382a206a78cdc1576fffa7e93caca5b4S3BucketB17E5ABD": { "Type": "String", - "Description": "S3 bucket for asset \"f56a9c742f3e99b26237d5d0912c69f9db8289c13656fdcb490fd017d801c483\"" + "Description": "S3 bucket for asset \"5c61041c12314e1ad8e67a0107fa3733382a206a78cdc1576fffa7e93caca5b4\"" }, - "AssetParametersf56a9c742f3e99b26237d5d0912c69f9db8289c13656fdcb490fd017d801c483S3VersionKeyA54743D3": { + "AssetParameters5c61041c12314e1ad8e67a0107fa3733382a206a78cdc1576fffa7e93caca5b4S3VersionKey77778F6A": { "Type": "String", - "Description": "S3 key for asset version \"f56a9c742f3e99b26237d5d0912c69f9db8289c13656fdcb490fd017d801c483\"" + "Description": "S3 key for asset version \"5c61041c12314e1ad8e67a0107fa3733382a206a78cdc1576fffa7e93caca5b4\"" }, - "AssetParametersf56a9c742f3e99b26237d5d0912c69f9db8289c13656fdcb490fd017d801c483ArtifactHash228F5AF4": { + "AssetParameters5c61041c12314e1ad8e67a0107fa3733382a206a78cdc1576fffa7e93caca5b4ArtifactHash580E429C": { "Type": "String", - "Description": "Artifact hash for asset \"f56a9c742f3e99b26237d5d0912c69f9db8289c13656fdcb490fd017d801c483\"" + "Description": "Artifact hash for asset \"5c61041c12314e1ad8e67a0107fa3733382a206a78cdc1576fffa7e93caca5b4\"" } }, "Outputs": { diff --git a/packages/@aws-cdk/aws-route53/test/integ.vpc-endpoint-service-domain-name.expected.json b/packages/@aws-cdk/aws-route53/test/integ.vpc-endpoint-service-domain-name.expected.json index 02dfb6c770668..030333512f860 100644 --- a/packages/@aws-cdk/aws-route53/test/integ.vpc-endpoint-service-domain-name.expected.json +++ b/packages/@aws-cdk/aws-route53/test/integ.vpc-endpoint-service-domain-name.expected.json @@ -936,7 +936,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3Bucket4DD075F7" + "Ref": "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3BucketD609D0D9" }, "S3Key": { "Fn::Join": [ @@ -949,7 +949,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3VersionKeyBD0E03B7" + "Ref": "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3VersionKey77CF589B" } ] } @@ -962,7 +962,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3VersionKeyBD0E03B7" + "Ref": "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3VersionKey77CF589B" } ] } @@ -988,17 +988,17 @@ } }, "Parameters": { - "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3Bucket4DD075F7": { + "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3BucketD609D0D9": { "Type": "String", - "Description": "S3 bucket for asset \"b965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98\"" + "Description": "S3 bucket for asset \"4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02c\"" }, - "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98S3VersionKeyBD0E03B7": { + "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cS3VersionKey77CF589B": { "Type": "String", - "Description": "S3 key for asset version \"b965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98\"" + "Description": "S3 key for asset version \"4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02c\"" }, - "AssetParametersb965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98ArtifactHash35A756EB": { + "AssetParameters4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02cArtifactHash86CFA15D": { "Type": "String", - "Description": "Artifact hash for asset \"b965ea3084ec95e24846d4975623e62a02c21883c3ddea9366b2ae42d21cef98\"" + "Description": "Artifact hash for asset \"4600faecd25ab407ff0a9d16f935c93062aaea5d415e97046bb8befe6c8ec02c\"" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/custom-resources/README.md b/packages/@aws-cdk/custom-resources/README.md index 327ed2a62c0dd..693b60209d7a9 100644 --- a/packages/@aws-cdk/custom-resources/README.md +++ b/packages/@aws-cdk/custom-resources/README.md @@ -428,6 +428,30 @@ new AwsCustomResource(this, 'Customized', { }) ``` +### Restricting the output of the Custom Resource + +CloudFormation imposes a hard limit of 4096 bytes for custom resources response +objects. If your API call returns an object that exceeds this limit, you can restrict +the data returned by the custom resource to specific paths in the API response: + +```ts +new AwsCustomResource(stack, 'ListObjects', { + onCreate: { + service: 's3', + action: 'listObjectsV2', + parameters: { + Bucket: 'my-bucket', + }, + physicalResourceId: PhysicalResourceId.of('id'), + outputPaths: ['Contents.0.Key', 'Contents.1.Key'], // Output only the two first keys + }, + policy: AwsCustomResourcePolicy.fromSdkCalls({ resources: AwsCustomResourcePolicy.ANY_RESOURCE }), +}); +``` + +Note that even if you restrict the output of your custom resource you can still use any +path in `PhysicalResourceId.fromResponse()`. + ### Custom Resource Examples #### Verify a domain with SES diff --git a/packages/@aws-cdk/custom-resources/lib/aws-custom-resource/aws-custom-resource.ts b/packages/@aws-cdk/custom-resources/lib/aws-custom-resource/aws-custom-resource.ts index dd16c15a4646d..9d9c938f3f894 100644 --- a/packages/@aws-cdk/custom-resources/lib/aws-custom-resource/aws-custom-resource.ts +++ b/packages/@aws-cdk/custom-resources/lib/aws-custom-resource/aws-custom-resource.ts @@ -128,9 +128,23 @@ export interface AwsSdkCall { * Example for ECS / updateService: 'service.deploymentConfiguration.maximumPercent' * * @default - return all data + * + * @deprecated use outputPaths instead */ readonly outputPath?: string; + /** + * Restrict the data returned by the custom resource to specific paths in + * the API response. Use this to limit the data returned by the custom + * resource if working with API calls that could potentially result in custom + * response objects exceeding the hard limit of 4096 bytes. + * + * Example for ECS / updateService: ['service.deploymentConfiguration.maximumPercent'] + * + * @default - return all data + */ + readonly outputPaths?: string[]; + /** * Used for running the SDK calls in underlying lambda with a different role * Can be used primarily for cross-account requests to for example connect diff --git a/packages/@aws-cdk/custom-resources/lib/aws-custom-resource/runtime/index.ts b/packages/@aws-cdk/custom-resources/lib/aws-custom-resource/runtime/index.ts index 807dc7e78fec5..56215192fb2f2 100644 --- a/packages/@aws-cdk/custom-resources/lib/aws-custom-resource/runtime/index.ts +++ b/packages/@aws-cdk/custom-resources/lib/aws-custom-resource/runtime/index.ts @@ -150,9 +150,19 @@ export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent region: awsService.config.region, // For test purposes: check if region was correctly passed. ...flatten(response), }; - data = call.outputPath - ? filterKeys(flatData, k => k.startsWith(call.outputPath!)) - : flatData; + + let outputPaths: string[] | undefined; + if (call.outputPath) { + outputPaths = [call.outputPath]; + } else if (call.outputPaths) { + outputPaths = call.outputPaths; + } + + if (outputPaths) { + data = filterKeys(flatData, startsWithOneOf(outputPaths)); + } else { + data = flatData; + } } catch (e) { if (!call.ignoreErrorCodesMatching || !new RegExp(call.ignoreErrorCodesMatching).test(e.code)) { throw e; @@ -211,3 +221,14 @@ function decodeCall(call: string | undefined) { if (!call) { return undefined; } return JSON.parse(call); } + +function startsWithOneOf(searchStrings: string[]): (string: string) => boolean { + return function(string: string): boolean { + for (const searchString of searchStrings) { + if (string.startsWith(searchString)) { + return true; + } + } + return false; + }; +} diff --git a/packages/@aws-cdk/custom-resources/test/aws-custom-resource/aws-custom-resource-provider.test.ts b/packages/@aws-cdk/custom-resources/test/aws-custom-resource/aws-custom-resource-provider.test.ts index 66f4fbb5dfc08..8a4786a138468 100644 --- a/packages/@aws-cdk/custom-resources/test/aws-custom-resource/aws-custom-resource-provider.test.ts +++ b/packages/@aws-cdk/custom-resources/test/aws-custom-resource/aws-custom-resource-provider.test.ts @@ -302,6 +302,53 @@ test('restrict output path', async () => { expect(request.isDone()).toBeTruthy(); }); +test('restrict output paths', async () => { + const listObjectsFake = sinon.fake.resolves({ + Contents: [ + { + Key: 'first-key', + ETag: 'first-key-etag', + }, + { + Key: 'second-key', + ETag: 'second-key-etag', + }, + ], + } as SDK.S3.ListObjectsOutput); + + AWS.mock('S3', 'listObjects', listObjectsFake); + + const event: AWSLambda.CloudFormationCustomResourceCreateEvent = { + ...eventCommon, + RequestType: 'Create', + ResourceProperties: { + ServiceToken: 'token', + Create: JSON.stringify({ + service: 'S3', + action: 'listObjects', + parameters: { + Bucket: 'my-bucket', + }, + physicalResourceId: PhysicalResourceId.of('id'), + outputPaths: ['Contents.0.Key', 'Contents.1.Key'], + } as AwsSdkCall), + }, + }; + + const request = createRequest(body => + body.Status === 'SUCCESS' && + body.PhysicalResourceId === 'id' && + JSON.stringify(body.Data) === JSON.stringify({ + 'Contents.0.Key': 'first-key', + 'Contents.1.Key': 'second-key', + }), + ); + + await handler(event, {} as AWSLambda.Context); + + expect(request.isDone()).toBeTruthy(); +}); + test('can specify apiVersion and region', async () => { const publishFake = sinon.fake.resolves({}); From 79fb4f36dc679ec701532ac0c7d377fc6ff061fa Mon Sep 17 00:00:00 2001 From: AWS CDK Team Date: Wed, 19 May 2021 09:01:11 +0000 Subject: [PATCH 060/134] chore(release): 1.105.0 --- CHANGELOG.md | 25 +++++++++++++++++++++++++ version.v1.json | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30f83c5d5d399..6354de8d8c529 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,31 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [1.105.0](https://github.com/aws/aws-cdk/compare/v1.104.0...v1.105.0) (2021-05-19) + + +### ⚠ BREAKING CHANGES TO EXPERIMENTAL FEATURES + +* **lambda-nodejs:** using `banner` and `footer` now requires `esbuild` >= 0.9.0 + +### Features + +* **apigatewayv2:** http api - lambda authorizer ([#13181](https://github.com/aws/aws-cdk/issues/13181)) ([4da78f6](https://github.com/aws/aws-cdk/commit/4da78f6ba2036f4a94d0e47c8581131b9bc23e14)), closes [#10534](https://github.com/aws/aws-cdk/issues/10534) [/github.com/aws/aws-cdk/issues/10534#issuecomment-780271588](https://github.com/aws//github.com/aws/aws-cdk/issues/10534/issues/issuecomment-780271588) +* **custom-resources:** restrict output of AwsCustomResource to list of paths ([#14041](https://github.com/aws/aws-cdk/issues/14041)) ([773ca8c](https://github.com/aws/aws-cdk/commit/773ca8c5d2a845f392f530d7710020075b884c72)), closes [/github.com/aws/aws-cdk/issues/2825#issuecomment-814999890](https://github.com/aws//github.com/aws/aws-cdk/issues/2825/issues/issuecomment-814999890) +* **stepfunctions:** Add support for ResultSelector ([#14648](https://github.com/aws/aws-cdk/issues/14648)) ([50d486a](https://github.com/aws/aws-cdk/commit/50d486ad4e7d175dfac048dbb4abf5e4084ce4fe)), closes [#9904](https://github.com/aws/aws-cdk/issues/9904) + + +### Bug Fixes + +* **cli:** Updated typo user to uses ([#14357](https://github.com/aws/aws-cdk/issues/14357)) ([7fe329c](https://github.com/aws/aws-cdk/commit/7fe329cd17502cf04c451153f6d19955621952dc)) +* **core:** cannot determine packaging when bundling that produces an archive is skipped ([#14372](https://github.com/aws/aws-cdk/issues/14372)) ([163e812](https://github.com/aws/aws-cdk/commit/163e8122db994d0bea7077f025876dbeac490ead)), closes [#14369](https://github.com/aws/aws-cdk/issues/14369) +* **ecr:** add validations for ECR repository names ([#12613](https://github.com/aws/aws-cdk/issues/12613)) ([396dca9](https://github.com/aws/aws-cdk/commit/396dca965b56bfbe8a7aedb2bcaddb196b5560c4)), closes [#9877](https://github.com/aws/aws-cdk/issues/9877) +* **lambda:** unable to access SingletonFunction vpc connections ([#14533](https://github.com/aws/aws-cdk/issues/14533)) ([49d18ab](https://github.com/aws/aws-cdk/commit/49d18ab8e8f55f8b36584f7fb95427106139a140)), closes [#6261](https://github.com/aws/aws-cdk/issues/6261) +* **lambda-nodejs:** banner and footer values not escaped ([#14743](https://github.com/aws/aws-cdk/issues/14743)) ([81aa612](https://github.com/aws/aws-cdk/commit/81aa61213b4f5e3bd9cbbc155264252bd64d0f5b)), closes [#13576](https://github.com/aws/aws-cdk/issues/13576) +* **pipelines:** self-mutating builds cannot be run in privileged mode ([#14655](https://github.com/aws/aws-cdk/issues/14655)) ([73b9b4a](https://github.com/aws/aws-cdk/commit/73b9b4a89078d1425f4acdf50a6e9b5275b7e555)), closes [#11425](https://github.com/aws/aws-cdk/issues/11425) +* **pipelines:** stackOutput generates names too long to be used in useOutputs ([#14680](https://github.com/aws/aws-cdk/issues/14680)) ([d81e06d](https://github.com/aws/aws-cdk/commit/d81e06d5a5651cf332614d73e27bf6ed95d083a3)), closes [#13552](https://github.com/aws/aws-cdk/issues/13552) +* **pipelines:** synth fails if 'aws-cdk' is not in `package.json` ([#14745](https://github.com/aws/aws-cdk/issues/14745)) ([0b8ee97](https://github.com/aws/aws-cdk/commit/0b8ee97b7c029c5195de694a1d2eea309c343f61)), closes [#14658](https://github.com/aws/aws-cdk/issues/14658) + ## [1.104.0](https://github.com/aws/aws-cdk/compare/v1.103.0...v1.104.0) (2021-05-14) diff --git a/version.v1.json b/version.v1.json index 9076251f5032f..b99e8c208bdc0 100644 --- a/version.v1.json +++ b/version.v1.json @@ -1,3 +1,3 @@ { - "version": "1.104.0" + "version": "1.105.0" } From 0855649cf0c830f19653e6a127f2aca8cb38dcb3 Mon Sep 17 00:00:00 2001 From: Niranjan Jayakar Date: Wed, 19 May 2021 10:05:53 +0100 Subject: [PATCH 061/134] Update CHANGELOG.md --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6354de8d8c529..e601ac973e2c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,8 +11,8 @@ All notable changes to this project will be documented in this file. See [standa ### Features -* **apigatewayv2:** http api - lambda authorizer ([#13181](https://github.com/aws/aws-cdk/issues/13181)) ([4da78f6](https://github.com/aws/aws-cdk/commit/4da78f6ba2036f4a94d0e47c8581131b9bc23e14)), closes [#10534](https://github.com/aws/aws-cdk/issues/10534) [/github.com/aws/aws-cdk/issues/10534#issuecomment-780271588](https://github.com/aws//github.com/aws/aws-cdk/issues/10534/issues/issuecomment-780271588) -* **custom-resources:** restrict output of AwsCustomResource to list of paths ([#14041](https://github.com/aws/aws-cdk/issues/14041)) ([773ca8c](https://github.com/aws/aws-cdk/commit/773ca8c5d2a845f392f530d7710020075b884c72)), closes [/github.com/aws/aws-cdk/issues/2825#issuecomment-814999890](https://github.com/aws//github.com/aws/aws-cdk/issues/2825/issues/issuecomment-814999890) +* **apigatewayv2:** http api - lambda authorizer ([#13181](https://github.com/aws/aws-cdk/issues/13181)) ([4da78f6](https://github.com/aws/aws-cdk/commit/4da78f6ba2036f4a94d0e47c8581131b9bc23e14)), closes [#10534](https://github.com/aws/aws-cdk/issues/10534) +* **custom-resources:** restrict output of AwsCustomResource to list of paths ([#14041](https://github.com/aws/aws-cdk/issues/14041)) ([773ca8c](https://github.com/aws/aws-cdk/commit/773ca8c5d2a845f392f530d7710020075b884c72)), closes [#2825](https://github.com/aws/aws-cdk/issues/2825) * **stepfunctions:** Add support for ResultSelector ([#14648](https://github.com/aws/aws-cdk/issues/14648)) ([50d486a](https://github.com/aws/aws-cdk/commit/50d486ad4e7d175dfac048dbb4abf5e4084ce4fe)), closes [#9904](https://github.com/aws/aws-cdk/issues/9904) From 5c84696a88f9319af1b2782b747e10f408c4c8fb Mon Sep 17 00:00:00 2001 From: Jonathan Goldwasser Date: Wed, 19 May 2021 12:39:19 +0200 Subject: [PATCH 062/134] fix(lambda-nodejs): esbuild detection with Yarn 2 in PnP mode (#14739) --- .../aws-lambda-nodejs/lib/bundling.ts | 170 ++++++++++-------- .../lib/esbuild-installation.ts | 35 ++++ .../aws-lambda-nodejs/lib/function.ts | 5 +- .../aws-lambda-nodejs/lib/package-manager.ts | 57 ++++++ .../@aws-cdk/aws-lambda-nodejs/lib/util.ts | 46 ++--- .../aws-lambda-nodejs/test/bundling.test.ts | 46 +++-- .../test/esbuild-installation.test.ts | 52 ++++++ .../test/package-manager.test.ts | 30 ++++ .../aws-lambda-nodejs/test/util.test.ts | 70 +------- 9 files changed, 317 insertions(+), 194 deletions(-) create mode 100644 packages/@aws-cdk/aws-lambda-nodejs/lib/esbuild-installation.ts create mode 100644 packages/@aws-cdk/aws-lambda-nodejs/lib/package-manager.ts create mode 100644 packages/@aws-cdk/aws-lambda-nodejs/test/esbuild-installation.test.ts create mode 100644 packages/@aws-cdk/aws-lambda-nodejs/test/package-manager.test.ts diff --git a/packages/@aws-cdk/aws-lambda-nodejs/lib/bundling.ts b/packages/@aws-cdk/aws-lambda-nodejs/lib/bundling.ts index d28f43ca874c1..240114fbfa43d 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/lib/bundling.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/lib/bundling.ts @@ -2,10 +2,12 @@ import * as os from 'os'; import * as path from 'path'; import { AssetCode, Code, Runtime } from '@aws-cdk/aws-lambda'; import * as cdk from '@aws-cdk/core'; +import { EsbuildInstallation } from './esbuild-installation'; +import { PackageManager } from './package-manager'; import { BundlingOptions } from './types'; -import { exec, extractDependencies, findUp, getEsBuildVersion, LockFile } from './util'; +import { exec, extractDependencies, findUp } from './util'; -const ESBUILD_VERSION = '0'; +const ESBUILD_MAJOR_VERSION = '0'; /** * Bundling properties @@ -41,11 +43,11 @@ export class Bundling implements cdk.BundlingOptions { }); } - public static clearRunsLocallyCache(): void { - this.runsLocally = undefined; + public static clearEsbuildInstallationCache(): void { + this.esbuildInstallation = undefined; } - private static runsLocally?: boolean; + private static esbuildInstallation?: EsbuildInstallation; // Core bundling options public readonly image: cdk.DockerImage; @@ -54,20 +56,22 @@ export class Bundling implements cdk.BundlingOptions { public readonly workingDirectory: string; public readonly local?: cdk.ILocalBundling; + private readonly projectRoot: string; private readonly relativeEntryPath: string; private readonly relativeTsconfigPath?: string; private readonly externals: string[]; + private readonly packageManager: PackageManager; constructor(private readonly props: BundlingProps) { - Bundling.runsLocally = Bundling.runsLocally - ?? getEsBuildVersion()?.startsWith(ESBUILD_VERSION) - ?? false; + this.packageManager = PackageManager.fromLockFile(props.depsLockFilePath); - const projectRoot = path.dirname(props.depsLockFilePath); - this.relativeEntryPath = path.relative(projectRoot, path.resolve(props.entry)); + Bundling.esbuildInstallation = Bundling.esbuildInstallation ?? EsbuildInstallation.detect(); + + this.projectRoot = path.dirname(props.depsLockFilePath); + this.relativeEntryPath = path.relative(this.projectRoot, path.resolve(props.entry)); if (props.tsconfig) { - this.relativeTsconfigPath = path.relative(projectRoot, path.resolve(props.tsconfig)); + this.relativeTsconfigPath = path.relative(this.projectRoot, path.resolve(props.tsconfig)); } this.externals = [ @@ -76,18 +80,23 @@ export class Bundling implements cdk.BundlingOptions { ]; // Docker bundling - const shouldBuildImage = props.forceDockerBundling || !Bundling.runsLocally; + const shouldBuildImage = props.forceDockerBundling || !Bundling.esbuildInstallation; this.image = shouldBuildImage ? props.dockerImage ?? cdk.DockerImage.fromBuild(path.join(__dirname, '../lib'), { buildArgs: { ...props.buildArgs ?? {}, - IMAGE: props.runtime.bundlingDockerImage.image, - ESBUILD_VERSION: props.esbuildVersion ?? ESBUILD_VERSION, + IMAGE: props.runtime.bundlingImage.image, + ESBUILD_VERSION: props.esbuildVersion ?? ESBUILD_MAJOR_VERSION, }, }) : cdk.DockerImage.fromRegistry('dummy'); // Do not build if we don't need to - const bundlingCommand = this.createBundlingCommand(cdk.AssetStaging.BUNDLING_INPUT_DIR, cdk.AssetStaging.BUNDLING_OUTPUT_DIR); + const bundlingCommand = this.createBundlingCommand({ + inputDir: cdk.AssetStaging.BUNDLING_INPUT_DIR, + outputDir: cdk.AssetStaging.BUNDLING_OUTPUT_DIR, + esbuildRunner: 'esbuild', // esbuild is installed globally in the docker image + osPlatform: 'linux', // linux docker image + }); this.command = ['bash', '-c', bundlingCommand]; this.environment = props.environment; // Bundling sets the working directory to cdk.AssetStaging.BUNDLING_INPUT_DIR @@ -96,54 +105,22 @@ export class Bundling implements cdk.BundlingOptions { // Local bundling if (!props.forceDockerBundling) { // only if Docker is not forced - const osPlatform = os.platform(); - const createLocalCommand = (outputDir: string) => this.createBundlingCommand(projectRoot, outputDir, osPlatform); - - this.local = { - tryBundle(outputDir: string) { - if (Bundling.runsLocally === false) { - process.stderr.write('esbuild cannot run locally. Switching to Docker bundling.\n'); - return false; - } - - const localCommand = createLocalCommand(outputDir); - - exec( - osPlatform === 'win32' ? 'cmd' : 'bash', - [ - osPlatform === 'win32' ? '/c' : '-c', - localCommand, - ], - { - env: { ...process.env, ...props.environment ?? {} }, - stdio: [ // show output - 'ignore', // ignore stdio - process.stderr, // redirect stdout to stderr - 'inherit', // inherit stderr - ], - cwd: path.dirname(props.entry), - windowsVerbatimArguments: osPlatform === 'win32', - }); - - return true; - }, - }; + this.local = this.getLocalBundlingProvider(); } } - public createBundlingCommand(inputDir: string, outputDir: string, osPlatform: NodeJS.Platform = 'linux'): string { - const pathJoin = osPathJoin(osPlatform); + private createBundlingCommand(options: BundlingCommandOptions): string { + const pathJoin = osPathJoin(options.osPlatform); - const npx = osPlatform === 'win32' ? 'npx.cmd' : 'npx'; const loaders = Object.entries(this.props.loader ?? {}); const defines = Object.entries(this.props.define ?? {}); - const esbuildCommand: string = [ - npx, 'esbuild', - '--bundle', `"${pathJoin(inputDir, this.relativeEntryPath)}"`, + const esbuildCommand: string[] = [ + options.esbuildRunner, + '--bundle', `"${pathJoin(options.inputDir, this.relativeEntryPath)}"`, `--target=${this.props.target ?? toTarget(this.props.runtime)}`, '--platform=node', - `--outfile="${pathJoin(outputDir, 'index.js')}"`, + `--outfile="${pathJoin(options.outputDir, 'index.js')}"`, ...this.props.minify ? ['--minify'] : [], ...this.props.sourceMap ? ['--sourcemap'] : [], ...this.externals.map(external => `--external:${external}`), @@ -151,11 +128,11 @@ export class Bundling implements cdk.BundlingOptions { ...defines.map(([key, value]) => `--define:${key}=${JSON.stringify(value)}`), ...this.props.logLevel ? [`--log-level=${this.props.logLevel}`] : [], ...this.props.keepNames ? ['--keep-names'] : [], - ...this.relativeTsconfigPath ? [`--tsconfig=${pathJoin(inputDir, this.relativeTsconfigPath)}`] : [], - ...this.props.metafile ? [`--metafile=${pathJoin(outputDir, 'index.meta.json')}`] : [], + ...this.relativeTsconfigPath ? [`--tsconfig=${pathJoin(options.inputDir, this.relativeTsconfigPath)}`] : [], + ...this.props.metafile ? [`--metafile=${pathJoin(options.outputDir, 'index.meta.json')}`] : [], ...this.props.banner ? [`--banner:js=${JSON.stringify(this.props.banner)}`] : [], ...this.props.footer ? [`--footer:js=${JSON.stringify(this.props.footer)}`] : [], - ].join(' '); + ]; let depsCommand = ''; if (this.props.nodeModules) { @@ -168,37 +145,78 @@ export class Bundling implements cdk.BundlingOptions { // Determine dependencies versions, lock file and installer const dependencies = extractDependencies(pkgPath, this.props.nodeModules); - let installer = Installer.NPM; - let lockFile = LockFile.NPM; - if (this.props.depsLockFilePath.endsWith(LockFile.YARN)) { - lockFile = LockFile.YARN; - installer = Installer.YARN; - } - - const osCommand = new OsCommand(osPlatform); + const osCommand = new OsCommand(options.osPlatform); // Create dummy package.json, copy lock file if any and then install depsCommand = chain([ - osCommand.writeJson(pathJoin(outputDir, 'package.json'), { dependencies }), - osCommand.copy(pathJoin(inputDir, lockFile), pathJoin(outputDir, lockFile)), - osCommand.changeDirectory(outputDir), - `${installer} install`, + osCommand.writeJson(pathJoin(options.outputDir, 'package.json'), { dependencies }), + osCommand.copy(pathJoin(options.inputDir, this.packageManager.lockFile), pathJoin(options.outputDir, this.packageManager.lockFile)), + osCommand.changeDirectory(options.outputDir), + this.packageManager.installCommand.join(' '), ]); } return chain([ - ...this.props.commandHooks?.beforeBundling(inputDir, outputDir) ?? [], - esbuildCommand, - ...(this.props.nodeModules && this.props.commandHooks?.beforeInstall(inputDir, outputDir)) ?? [], + ...this.props.commandHooks?.beforeBundling(options.inputDir, options.outputDir) ?? [], + esbuildCommand.join(' '), + ...(this.props.nodeModules && this.props.commandHooks?.beforeInstall(options.inputDir, options.outputDir)) ?? [], depsCommand, - ...this.props.commandHooks?.afterBundling(inputDir, outputDir) ?? [], + ...this.props.commandHooks?.afterBundling(options.inputDir, options.outputDir) ?? [], ]); } + + private getLocalBundlingProvider(): cdk.ILocalBundling { + const osPlatform = os.platform(); + const createLocalCommand = (outputDir: string, esbuild: EsbuildInstallation) => this.createBundlingCommand({ + inputDir: this.projectRoot, + outputDir, + esbuildRunner: esbuild.isLocal ? this.packageManager.runBinCommand('esbuild') : 'esbuild', + osPlatform, + }); + const environment = this.props.environment ?? {}; + const cwd = path.dirname(this.props.entry); + + return { + tryBundle(outputDir: string) { + if (!Bundling.esbuildInstallation) { + process.stderr.write('esbuild cannot run locally. Switching to Docker bundling.\n'); + return false; + } + + if (!Bundling.esbuildInstallation.version.startsWith(`${ESBUILD_MAJOR_VERSION}.`)) { + throw new Error(`Expected esbuild version ${ESBUILD_MAJOR_VERSION}.x but got ${Bundling.esbuildInstallation.version}`); + } + + const localCommand = createLocalCommand(outputDir, Bundling.esbuildInstallation); + + exec( + osPlatform === 'win32' ? 'cmd' : 'bash', + [ + osPlatform === 'win32' ? '/c' : '-c', + localCommand, + ], + { + env: { ...process.env, ...environment }, + stdio: [ // show output + 'ignore', // ignore stdio + process.stderr, // redirect stdout to stderr + 'inherit', // inherit stderr + ], + cwd, + windowsVerbatimArguments: osPlatform === 'win32', + }); + + return true; + }, + }; + } } -enum Installer { - NPM = 'npm', - YARN = 'yarn', +interface BundlingCommandOptions { + readonly inputDir: string; + readonly outputDir: string; + readonly esbuildRunner: string; + readonly osPlatform: NodeJS.Platform; } /** diff --git a/packages/@aws-cdk/aws-lambda-nodejs/lib/esbuild-installation.ts b/packages/@aws-cdk/aws-lambda-nodejs/lib/esbuild-installation.ts new file mode 100644 index 0000000000000..8ef2e8dbb23d9 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-nodejs/lib/esbuild-installation.ts @@ -0,0 +1,35 @@ +import { spawnSync } from 'child_process'; +import { tryGetModuleVersion } from './util'; + +/** + * An esbuild installation + */ +export abstract class EsbuildInstallation { + public static detect(): EsbuildInstallation | undefined { + try { + // Check local version first + const version = tryGetModuleVersion('esbuild'); + if (version) { + return { + isLocal: true, + version, + }; + } + + // Fallback to a global version + const esbuild = spawnSync('esbuild', ['--version']); + if (esbuild.status === 0 && !esbuild.error) { + return { + isLocal: false, + version: esbuild.stdout.toString().trim(), + }; + } + return undefined; + } catch (err) { + return undefined; + } + } + + public abstract readonly isLocal: boolean; + public abstract readonly version: string; +} diff --git a/packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts b/packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts index 00e5315654796..476c1e45b595c 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts @@ -2,8 +2,9 @@ import * as fs from 'fs'; import * as path from 'path'; import * as lambda from '@aws-cdk/aws-lambda'; import { Bundling } from './bundling'; +import { PackageManager } from './package-manager'; import { BundlingOptions } from './types'; -import { callsites, findUp, LockFile } from './util'; +import { callsites, findUp } from './util'; // keep this import separate from other imports to reduce chance for merge conflicts with v2-main // eslint-disable-next-line no-duplicate-imports, import/order @@ -94,7 +95,7 @@ export class NodejsFunction extends lambda.Function { } depsLockFilePath = path.resolve(props.depsLockFilePath); } else { - const lockFile = findUp(LockFile.YARN) ?? findUp(LockFile.NPM); + const lockFile = findUp(PackageManager.YARN.lockFile) ?? findUp(PackageManager.NPM.lockFile); if (!lockFile) { throw new Error('Cannot find a package lock file (`yarn.lock` or `package-lock.json`). Please specify it with `depsFileLockPath`.'); } diff --git a/packages/@aws-cdk/aws-lambda-nodejs/lib/package-manager.ts b/packages/@aws-cdk/aws-lambda-nodejs/lib/package-manager.ts new file mode 100644 index 0000000000000..dd5260f87838c --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-nodejs/lib/package-manager.ts @@ -0,0 +1,57 @@ +import * as os from 'os'; +import * as path from 'path'; + +interface PackageManagerProps { + readonly lockFile: string; + readonly installCommand: string[]; + readonly runCommand: string[]; +} + +/** + * A node package manager + */ +export class PackageManager { + public static NPM = new PackageManager({ + lockFile: 'package-lock.json', + installCommand: ['npm', 'install'], + runCommand: ['npx', '--no-install'], + }); + + public static YARN = new PackageManager({ + lockFile: 'yarn.lock', + installCommand: ['yarn', 'install'], + runCommand: ['yarn', 'run'], + }); + + public static fromLockFile(lockFilePath: string): PackageManager { + const lockFile = path.basename(lockFilePath); + + switch (lockFile) { + case PackageManager.NPM.lockFile: + return PackageManager.NPM; + case PackageManager.YARN.lockFile: + return PackageManager.YARN; + default: + return PackageManager.NPM; + } + } + + public readonly lockFile: string; + public readonly installCommand: string[]; + public readonly runCommand: string[]; + + constructor(props: PackageManagerProps) { + this.lockFile = props.lockFile; + this.installCommand = props.installCommand; + this.runCommand = props.runCommand; + } + + public runBinCommand(bin: string): string { + const [runCommand, ...runArgs] = this.runCommand; + return [ + os.platform() === 'win32' ? `${runCommand}.cmd` : runCommand, + ...runArgs, + bin, + ].join(' '); + } +} diff --git a/packages/@aws-cdk/aws-lambda-nodejs/lib/util.ts b/packages/@aws-cdk/aws-lambda-nodejs/lib/util.ts index 206941f71f66d..5ead91793e93b 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/lib/util.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/lib/util.ts @@ -1,6 +1,5 @@ import { spawnSync, SpawnSyncOptions } from 'child_process'; import * as fs from 'fs'; -import * as os from 'os'; import * as path from 'path'; export interface CallSite { @@ -71,6 +70,18 @@ export function exec(cmd: string, args: string[], options?: SpawnSyncOptions) { return proc; } +/** + * Returns a module version by requiring its package.json file + */ +export function tryGetModuleVersion(mod: string): string | undefined { + try { + // eslint-disable-next-line @typescript-eslint/no-require-imports + return require(`${mod}/package.json`).version; + } catch (err) { + return undefined; + } +} + /** * Extract versions for a list of modules. * @@ -90,39 +101,12 @@ export function extractDependencies(pkgPath: string, modules: string[]): { [key: }; for (const mod of modules) { - try { - // eslint-disable-next-line @typescript-eslint/no-require-imports - const version = pkgDependencies[mod] ?? require(`${mod}/package.json`).version; - dependencies[mod] = version; - } catch (err) { + const version = pkgDependencies[mod] ?? tryGetModuleVersion(mod); + if (!version) { throw new Error(`Cannot extract version for module '${mod}'. Check that it's referenced in your package.json or installed.`); } + dependencies[mod] = version; } return dependencies; } - -/** - * Returns the installed esbuild version - */ -export function getEsBuildVersion(): string | undefined { - try { - // --no-install ensures that we are checking for an installed version - // (either locally or globally) - const npx = os.platform() === 'win32' ? 'npx.cmd' : 'npx'; - const esbuild = spawnSync(npx, ['--no-install', 'esbuild', '--version']); - - if (esbuild.status !== 0 || esbuild.error) { - return undefined; - } - - return esbuild.stdout.toString().trim(); - } catch (err) { - return undefined; - } -} - -export enum LockFile { - NPM = 'package-lock.json', - YARN = 'yarn.lock' -} diff --git a/packages/@aws-cdk/aws-lambda-nodejs/test/bundling.test.ts b/packages/@aws-cdk/aws-lambda-nodejs/test/bundling.test.ts index e5441cb5aeadb..7d79365da5077 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/test/bundling.test.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/test/bundling.test.ts @@ -5,20 +5,27 @@ import { Code, Runtime } from '@aws-cdk/aws-lambda'; import { AssetHashType, DockerImage } from '@aws-cdk/core'; import { version as delayVersion } from 'delay/package.json'; import { Bundling } from '../lib/bundling'; +import { EsbuildInstallation } from '../lib/esbuild-installation'; import { LogLevel } from '../lib/types'; import * as util from '../lib/util'; jest.mock('@aws-cdk/aws-lambda'); -// Mock BundlingDockerImage.fromAsset() to avoid building the image -let fromAssetMock = jest.spyOn(DockerImage, 'fromBuild'); -let getEsBuildVersionMock = jest.spyOn(util, 'getEsBuildVersion'); +// Mock DockerImage.fromAsset() to avoid building the image +let fromBuildMock: jest.SpyInstance; +let detectEsbuildMock: jest.SpyInstance; beforeEach(() => { jest.clearAllMocks(); jest.resetAllMocks(); - Bundling.clearRunsLocallyCache(); - getEsBuildVersionMock.mockReturnValue('0.8.8'); - fromAssetMock.mockReturnValue({ + jest.restoreAllMocks(); + Bundling.clearEsbuildInstallationCache(); + + detectEsbuildMock = jest.spyOn(EsbuildInstallation, 'detect').mockReturnValue({ + isLocal: true, + version: '0.8.8', + }); + + fromBuildMock = jest.spyOn(DockerImage, 'fromBuild').mockReturnValue({ image: 'built-image', cp: () => 'dest-path', run: () => {}, @@ -53,7 +60,7 @@ test('esbuild bundling in Docker', () => { }, command: [ 'bash', '-c', - 'npx esbuild --bundle "/asset-input/lib/handler.ts" --target=node12 --platform=node --outfile="/asset-output/index.js" --external:aws-sdk --loader:.png=dataurl', + 'esbuild --bundle "/asset-input/lib/handler.ts" --target=node12 --platform=node --outfile="/asset-output/index.js" --external:aws-sdk --loader:.png=dataurl', ], workingDirectory: '/', }), @@ -74,7 +81,7 @@ test('esbuild bundling with handler named index.ts', () => { bundling: expect.objectContaining({ command: [ 'bash', '-c', - 'npx esbuild --bundle "/asset-input/lib/index.ts" --target=node12 --platform=node --outfile="/asset-output/index.js" --external:aws-sdk', + 'esbuild --bundle "/asset-input/lib/index.ts" --target=node12 --platform=node --outfile="/asset-output/index.js" --external:aws-sdk', ], }), }); @@ -94,7 +101,7 @@ test('esbuild bundling with tsx handler', () => { bundling: expect.objectContaining({ command: [ 'bash', '-c', - 'npx esbuild --bundle "/asset-input/lib/handler.tsx" --target=node12 --platform=node --outfile="/asset-output/index.js" --external:aws-sdk', + 'esbuild --bundle "/asset-input/lib/handler.tsx" --target=node12 --platform=node --outfile="/asset-output/index.js" --external:aws-sdk', ], }), }); @@ -102,6 +109,9 @@ test('esbuild bundling with tsx handler', () => { test('esbuild with Windows paths', () => { const osPlatformMock = jest.spyOn(os, 'platform').mockReturnValue('win32'); + // Mock path.basename() because it cannot extract the basename of a Windows + // path when running on Linux + jest.spyOn(path, 'basename').mockReturnValueOnce('package-lock.json'); Bundling.bundle({ entry: 'C:\\my-project\\lib\\entry.ts', @@ -139,7 +149,7 @@ test('esbuild bundling with externals and dependencies', () => { command: [ 'bash', '-c', [ - 'npx esbuild --bundle "/asset-input/test/bundling.test.js" --target=node12 --platform=node --outfile="/asset-output/index.js" --external:abc --external:delay', + 'esbuild --bundle "/asset-input/test/bundling.test.js" --target=node12 --platform=node --outfile="/asset-output/index.js" --external:abc --external:delay', `echo \'{\"dependencies\":{\"delay\":\"${delayVersion}\"}}\' > /asset-output/package.json`, 'cp /asset-input/package-lock.json /asset-output/package-lock.json', 'cd /asset-output', @@ -184,7 +194,7 @@ test('esbuild bundling with esbuild options', () => { command: [ 'bash', '-c', [ - 'npx esbuild --bundle "/asset-input/lib/handler.ts"', + 'esbuild --bundle "/asset-input/lib/handler.ts"', '--target=es2020 --platform=node --outfile="/asset-output/index.js"', '--minify --sourcemap --external:aws-sdk --loader:.png=dataurl', defineInstructions, @@ -232,7 +242,7 @@ test('with Docker build args', () => { forceDockerBundling: true, }); - expect(fromAssetMock).toHaveBeenCalledWith(expect.stringMatching(/lib$/), expect.objectContaining({ + expect(fromBuildMock).toHaveBeenCalledWith(expect.stringMatching(/lib$/), expect.objectContaining({ buildArgs: expect.objectContaining({ HELLO: 'WORLD', }), @@ -273,14 +283,17 @@ test('Local bundling', () => { ); // Docker image is not built - expect(fromAssetMock).not.toHaveBeenCalled(); + expect(fromBuildMock).not.toHaveBeenCalled(); spawnSyncMock.mockRestore(); }); test('Incorrect esbuild version', () => { - getEsBuildVersionMock.mockReturnValueOnce('3.4.5'); + detectEsbuildMock.mockReturnValueOnce({ + isLocal: true, + version: '3.4.5', + }); const bundler = new Bundling({ entry, @@ -288,8 +301,9 @@ test('Incorrect esbuild version', () => { runtime: Runtime.NODEJS_12_X, }); - const tryBundle = bundler.local?.tryBundle('/outdir', { image: Runtime.NODEJS_12_X.bundlingDockerImage }); - expect(tryBundle).toBe(false); + expect(() => bundler.local?.tryBundle('/outdir', { + image: Runtime.NODEJS_12_X.bundlingImage, + })).toThrow(/Expected esbuild version 0.x but got 3.4.5/); }); test('Custom bundling docker image', () => { diff --git a/packages/@aws-cdk/aws-lambda-nodejs/test/esbuild-installation.test.ts b/packages/@aws-cdk/aws-lambda-nodejs/test/esbuild-installation.test.ts new file mode 100644 index 0000000000000..24b9b512c98bc --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-nodejs/test/esbuild-installation.test.ts @@ -0,0 +1,52 @@ +import * as child_process from 'child_process'; +import { EsbuildInstallation } from '../lib/esbuild-installation'; +import * as util from '../lib/util'; + +// eslint-disable-next-line @typescript-eslint/no-require-imports, import/no-extraneous-dependencies +const version = require('esbuild/package.json').version; + +test('detects local version', () => { + expect(EsbuildInstallation.detect()).toEqual({ + isLocal: true, + version, + }); +}); + +test('checks global version if local detection fails', () => { + const getModuleVersionMock = jest.spyOn(util, 'tryGetModuleVersion').mockReturnValue(undefined); + const spawnSyncMock = jest.spyOn(child_process, 'spawnSync').mockReturnValue({ + status: 0, + stderr: Buffer.from('stderr'), + stdout: Buffer.from('global-version'), + pid: 123, + output: ['stdout', 'stderr'], + signal: null, + }); + + expect(EsbuildInstallation.detect()).toEqual({ + isLocal: false, + version: 'global-version', + }); + + spawnSyncMock.mockRestore(); + getModuleVersionMock.mockRestore(); +}); + +test('returns undefined on error', () => { + const getModuleVersionMock = jest.spyOn(util, 'tryGetModuleVersion').mockReturnValue(undefined); + const spawnSyncMock = jest.spyOn(child_process, 'spawnSync').mockReturnValue({ + error: new Error('bad error'), + status: 0, + stderr: Buffer.from('stderr'), + stdout: Buffer.from('stdout'), + pid: 123, + output: ['stdout', 'stderr'], + signal: null, + }); + + expect(EsbuildInstallation.detect()).toBeUndefined(); + + spawnSyncMock.mockRestore(); + getModuleVersionMock.mockRestore(); +}); + diff --git a/packages/@aws-cdk/aws-lambda-nodejs/test/package-manager.test.ts b/packages/@aws-cdk/aws-lambda-nodejs/test/package-manager.test.ts new file mode 100644 index 0000000000000..f561bce592f12 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-nodejs/test/package-manager.test.ts @@ -0,0 +1,30 @@ +import * as os from 'os'; +import { PackageManager } from '../lib/package-manager'; + +test('from a package-lock.json', () => { + const packageManager = PackageManager.fromLockFile('/path/to/package-lock.json'); + expect(packageManager).toEqual(PackageManager.NPM); + + expect(packageManager.runBinCommand('my-bin')).toBe('npx --no-install my-bin'); +}); + +test('from a yarn.lock', () => { + const packageManager = PackageManager.fromLockFile('/path/to/yarn.lock'); + expect(packageManager).toEqual(PackageManager.YARN); + + expect(packageManager.runBinCommand('my-bin')).toBe('yarn run my-bin'); +}); + +test('defaults to NPM', () => { + const packageManager = PackageManager.fromLockFile('/path/to/pnpm-lock.yaml'); + expect(packageManager).toEqual(PackageManager.NPM); +}); + +test('Windows', () => { + const osPlatformMock = jest.spyOn(os, 'platform').mockReturnValue('win32'); + + const packageManager = PackageManager.NPM; + expect(packageManager.runBinCommand('my-bin')).toEqual('npx.cmd --no-install my-bin'); + + osPlatformMock.mockRestore(); +}); diff --git a/packages/@aws-cdk/aws-lambda-nodejs/test/util.test.ts b/packages/@aws-cdk/aws-lambda-nodejs/test/util.test.ts index df91c4433f153..4962ed203b31f 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/test/util.test.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/test/util.test.ts @@ -1,7 +1,6 @@ import * as child_process from 'child_process'; -import * as os from 'os'; import * as path from 'path'; -import { callsites, exec, extractDependencies, findUp, getEsBuildVersion } from '../lib/util'; +import { callsites, exec, extractDependencies, findUp } from '../lib/util'; beforeEach(() => { jest.clearAllMocks(); @@ -121,70 +120,3 @@ describe('extractDependencies', () => { )).toThrow(/Cannot extract version for module 'unknown'/); }); }); - -describe('getEsBuildVersion', () => { - test('returns the version', () => { - const spawnSyncMock = jest.spyOn(child_process, 'spawnSync').mockReturnValue({ - status: 0, - stderr: Buffer.from('stderr'), - stdout: Buffer.from('version'), - pid: 123, - output: ['stdout', 'stderr'], - signal: null, - }); - - expect(getEsBuildVersion()).toBe('version'); - expect(spawnSyncMock).toHaveBeenCalledWith('npx', ['--no-install', 'esbuild', '--version']); - - spawnSyncMock.mockRestore(); - }); - - test('returns undefined on non zero status', () => { - const spawnSyncMock = jest.spyOn(child_process, 'spawnSync').mockReturnValue({ - status: 127, // status error - stderr: Buffer.from('stderr'), - stdout: Buffer.from('stdout'), - pid: 123, - output: ['stdout', 'stderr'], - signal: null, - }); - - expect(getEsBuildVersion()).toBeUndefined(); - - spawnSyncMock.mockRestore(); - }); - - test('returns undefined on error', () => { - const spawnSyncMock = jest.spyOn(child_process, 'spawnSync').mockReturnValue({ - error: new Error('bad error'), - status: 0, - stderr: Buffer.from('stderr'), - stdout: Buffer.from('stdout'), - pid: 123, - output: ['stdout', 'stderr'], - signal: null, - }); - - expect(getEsBuildVersion()).toBeUndefined(); - - spawnSyncMock.mockRestore(); - }); - - test('Windows', () => { - const spawnSyncMock = jest.spyOn(child_process, 'spawnSync').mockReturnValue({ - status: 0, - stderr: Buffer.from('stderr'), - stdout: Buffer.from('version'), - pid: 123, - output: ['stdout', 'stderr'], - signal: null, - }); - const osPlatformMock = jest.spyOn(os, 'platform').mockReturnValue('win32'); - - expect(getEsBuildVersion()).toBe('version'); - expect(spawnSyncMock).toHaveBeenCalledWith('npx.cmd', expect.any(Array)); - - spawnSyncMock.mockRestore(); - osPlatformMock.mockRestore(); - }); -}); From 7013f5884f298ab217044dd104e27236e53c57c0 Mon Sep 17 00:00:00 2001 From: Niranjan Jayakar Date: Wed, 19 May 2021 14:59:05 +0100 Subject: [PATCH 063/134] chore: permissions for github actions (#14767) By default, all Github actions have read permissions via the standard `GITHUB_TOKEN`. For jobs that require write permission, explicitly add the necessary permissions. In the case of the 'Yarn Upgrade' Github action, separated the 'upgrade' step and the 'pull request' step into separate jobs to build a better security boundary between the two. Inspired from: https://github.com/projen/projen/blob/a4f875d07b57f8f8247b9352e34c3c83759afe82/.github/workflows/upgrade-dependencies.yml --- .github/workflows/auto-approve.yml | 2 ++ .github/workflows/close-stale-issues.yml | 2 ++ .github/workflows/closed-issue-message.yml | 30 ++++++++-------- .github/workflows/issue-label-assign.yml | 2 ++ .github/workflows/pr-linter.yml | 2 ++ .github/workflows/v2-pull-request.yml | 3 ++ .github/workflows/yarn-upgrade.yml | 41 +++++++++++++++++++--- 7 files changed, 63 insertions(+), 19 deletions(-) diff --git a/.github/workflows/auto-approve.yml b/.github/workflows/auto-approve.yml index 9b185ec0fcd47..cf881f5bf20f2 100644 --- a/.github/workflows/auto-approve.yml +++ b/.github/workflows/auto-approve.yml @@ -13,6 +13,8 @@ jobs: || github.event.pull_request.user.login == 'dependabot[bot]' || github.event.pull_request.user.login == 'dependabot-preview[bot]') runs-on: ubuntu-latest + permissions: + pull-requests: write steps: - uses: hmarr/auto-approve-action@v2.1.0 with: diff --git a/.github/workflows/close-stale-issues.yml b/.github/workflows/close-stale-issues.yml index df8e9f3d5ffb5..db75a56aa7f8b 100644 --- a/.github/workflows/close-stale-issues.yml +++ b/.github/workflows/close-stale-issues.yml @@ -7,6 +7,8 @@ on: jobs: cleanup: + permissions: + issues: write runs-on: ubuntu-latest name: Stale issue job steps: diff --git a/.github/workflows/closed-issue-message.yml b/.github/workflows/closed-issue-message.yml index 3340afb1f3b11..f1a0a3b0fc7b1 100644 --- a/.github/workflows/closed-issue-message.yml +++ b/.github/workflows/closed-issue-message.yml @@ -1,17 +1,19 @@ name: Closed Issue Message on: - issues: - types: [closed] + issues: + types: [closed] jobs: - auto_comment: - runs-on: ubuntu-latest - steps: - - uses: aws-actions/closed-issue-message@v1 - with: - # These inputs are both required - repo-token: "${{ secrets.GITHUB_TOKEN }}" - message: | - ### ⚠️COMMENT VISIBILITY WARNING⚠️ - Comments on closed issues are hard for our team to see. - If you need more assistance, please either tag a team member or open a new issue that references this one. - If you wish to keep having a conversation with other community members under this issue feel free to do so. + auto_comment: + permissions: + issues: write + runs-on: ubuntu-latest + steps: + - uses: aws-actions/closed-issue-message@v1 + with: + # These inputs are both required + repo-token: "${{ secrets.GITHUB_TOKEN }}" + message: | + ### ⚠️COMMENT VISIBILITY WARNING⚠️ + Comments on closed issues are hard for our team to see. + If you need more assistance, please either tag a team member or open a new issue that references this one. + If you wish to keep having a conversation with other community members under this issue feel free to do so. diff --git a/.github/workflows/issue-label-assign.yml b/.github/workflows/issue-label-assign.yml index 237e44deefc36..70b40968a4289 100644 --- a/.github/workflows/issue-label-assign.yml +++ b/.github/workflows/issue-label-assign.yml @@ -9,6 +9,8 @@ on: jobs: test: + permissions: + issues: write runs-on: ubuntu-latest steps: - uses: Naturalclar/issue-action@v2.0.2 diff --git a/.github/workflows/pr-linter.yml b/.github/workflows/pr-linter.yml index 5702b254d4a0b..80137c4068020 100644 --- a/.github/workflows/pr-linter.yml +++ b/.github/workflows/pr-linter.yml @@ -14,6 +14,8 @@ on: jobs: validate-pr: + permissions: + pull-requests: read runs-on: ubuntu-latest steps: diff --git a/.github/workflows/v2-pull-request.yml b/.github/workflows/v2-pull-request.yml index e820647947705..351ee2c8c427f 100644 --- a/.github/workflows/v2-pull-request.yml +++ b/.github/workflows/v2-pull-request.yml @@ -16,6 +16,9 @@ on: jobs: # Run yarn pkglint on merge forward PRs and commit the results pkglint: + permissions: + pull-requests: write + contents: write if: contains(github.event.pull_request.labels.*.name, 'pr/forward-merge') runs-on: ubuntu-latest steps: diff --git a/.github/workflows/yarn-upgrade.yml b/.github/workflows/yarn-upgrade.yml index c0df170ff30ef..dd3bec734d944 100644 --- a/.github/workflows/yarn-upgrade.yml +++ b/.github/workflows/yarn-upgrade.yml @@ -1,10 +1,9 @@ name: Yarn Upgrade on: - # Disable this workflow - #schedule: + schedule: # Every wednesday at 13:37 UTC - #- cron: 37 13 * * 3 + - cron: 37 13 * * 3 workflow_dispatch: {} jobs: @@ -69,6 +68,39 @@ jobs: # also - jest-enviroment-jsdom doesnt actually require 16.5.1 (https://github.com/facebook/jest/blob/master/packages/jest-environment-jsdom/package.json#L23) run: yarn upgrade --pattern '!(jsdom)' + # Next, create and upload the changes as a patch file. This will later be downloaded to create a pull request + # Creating a pull request requires write permissions and it's best to keep write privileges isolated. + - name: Create Patch + run: |- + git add . + git diff --patch --staged > ${{ runner.temp }}/upgrade.patch + - name: Upload Patch + uses: actions/upload-artifact@v2 + with: + name: upgrade.patch + path: ${{ runner.temp }}/upgrade.patch + + pr: + name: Create Pull Request + needs: upgrade + permissions: + contents: write + pull-requests: write + runs-on: ubuntu-latest + steps: + - name: Check Out + uses: actions/checkout@v2 + + - name: Download patch + uses: actions/download-artifact@v2 + with: + name: upgrade.patch + path: ${{ runner.temp }} + + - name: Apply patch + run: '[ -s ${{ runner.temp }}/upgrade.patch ] && git apply ${{ runner.temp + }}/upgrade.patch || echo "Empty patch. Skipping."' + - name: Make Pull Request uses: peter-evans/create-pull-request@v3 with: @@ -83,5 +115,4 @@ jobs: Ran npm-check-updates and yarn upgrade to keep the `yarn.lock` file up-to-date. labels: contribution/core,dependencies,pr/auto-approve team-reviewers: aws-cdk-team - # Privileged token so automated PR validation happens - token: ${{ secrets.AUTOMATION_GITHUB_TOKEN }} + token: ${{ secrets.GITHUB_TOKEN }} From b816ef902b8e4285f8c2476d839f6fc98db6f557 Mon Sep 17 00:00:00 2001 From: Niranjan Jayakar Date: Wed, 19 May 2021 15:14:25 +0100 Subject: [PATCH 064/134] chore: switch back to privileged token in upgrade github action (#14775) The previous commit incorrectly removed this token and used the default Github token. Github prevents subsequent Github actions to be triggered if the default token is used. Switch it back. --- .github/workflows/yarn-upgrade.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/yarn-upgrade.yml b/.github/workflows/yarn-upgrade.yml index dd3bec734d944..473e9423051cb 100644 --- a/.github/workflows/yarn-upgrade.yml +++ b/.github/workflows/yarn-upgrade.yml @@ -9,6 +9,8 @@ on: jobs: upgrade: name: Yarn Upgrade + permissions: + contents: read runs-on: ubuntu-latest steps: @@ -115,4 +117,6 @@ jobs: Ran npm-check-updates and yarn upgrade to keep the `yarn.lock` file up-to-date. labels: contribution/core,dependencies,pr/auto-approve team-reviewers: aws-cdk-team - token: ${{ secrets.GITHUB_TOKEN }} + # Github prevents further Github actions to be run if the default Github token is used. + # Instead use a privileged token here, so further GH actions can be triggered on this PR. + token: ${{ secrets.AUTOMATION_GITHUB_TOKEN }} From 3c7a89de6edaf7a1910bf716419dbe5568d79374 Mon Sep 17 00:00:00 2001 From: Ryan Allan <4126720+sudo-ryan@users.noreply.github.com> Date: Wed, 19 May 2021 23:42:02 +0100 Subject: [PATCH 065/134] feat(dynamodb): add ability to enable contributor insights on Table (#14742) Resolve https://github.com/aws/aws-cdk/issues/11626 *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/lib/table.ts | 8 ++++++++ packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts | 2 ++ 2 files changed, 10 insertions(+) diff --git a/packages/@aws-cdk/aws-dynamodb/lib/table.ts b/packages/@aws-cdk/aws-dynamodb/lib/table.ts index 776159b110e27..7894994c98b36 100644 --- a/packages/@aws-cdk/aws-dynamodb/lib/table.ts +++ b/packages/@aws-cdk/aws-dynamodb/lib/table.ts @@ -224,6 +224,13 @@ export interface TableOptions { * @default Duration.minutes(30) */ readonly replicationTimeout?: Duration; + + /** + * Whether CloudWatch contributor insights is enabled. + * + * @default false + */ + readonly contributorInsightsEnabled?: boolean; } /** @@ -1114,6 +1121,7 @@ export class Table extends TableBase { sseSpecification, streamSpecification, timeToLiveSpecification: props.timeToLiveAttribute ? { attributeName: props.timeToLiveAttribute, enabled: true } : undefined, + contributorInsightsSpecification: props.contributorInsightsEnabled !== undefined ? { enabled: props.contributorInsightsEnabled } : undefined, }); this.table.applyRemovalPolicy(props.removalPolicy); diff --git a/packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts index 31fb1d682d674..74d18e2dd28e3 100644 --- a/packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts +++ b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts @@ -330,6 +330,7 @@ test('when specifying every property', () => { timeToLiveAttribute: 'timeToLive', partitionKey: TABLE_PARTITION_KEY, sortKey: TABLE_SORT_KEY, + contributorInsightsEnabled: true, }); Tags.of(table).add('Environment', 'Production'); @@ -353,6 +354,7 @@ test('when specifying every property', () => { TableName: 'MyTable', Tags: [{ Key: 'Environment', Value: 'Production' }], TimeToLiveSpecification: { AttributeName: 'timeToLive', Enabled: true }, + ContributorInsightsSpecification: { Enabled: true }, }, ); }); From 6179ac16f2fcab5bf5e03a3629db281018d7585b Mon Sep 17 00:00:00 2001 From: AWS CDK Automation <43080478+aws-cdk-automation@users.noreply.github.com> Date: Thu, 20 May 2021 01:07:49 +0200 Subject: [PATCH 066/134] chore: npm-check-updates && yarn upgrade (#14776) Ran npm-check-updates and yarn upgrade to keep the `yarn.lock` file up-to-date. --- .../package.json | 2 +- .../aws-global-table-coordinator/package.json | 2 +- .../@aws-cdk/aws-lambda-nodejs/package.json | 2 +- .../cloud-assembly-schema/package.json | 2 +- .../@aws-cdk/cloudformation-diff/package.json | 4 +- packages/@aws-cdk/core/package.json | 2 +- packages/@aws-cdk/cx-api/package.json | 2 +- .../@monocdk-experiment/assert/package.json | 2 +- .../rewrite-imports/package.json | 2 +- packages/aws-cdk-lib/package.json | 2 +- packages/aws-cdk-migration/package.json | 2 +- packages/aws-cdk/package.json | 8 +- packages/awslint/package.json | 6 +- packages/cdk-assets/package.json | 2 +- packages/monocdk/package.json | 2 +- tools/cdk-build-tools/package.json | 8 +- tools/eslint-plugin-cdk/package.json | 4 +- tools/nodeunit-shim/package.json | 2 +- tools/pkglint/package.json | 8 +- tools/prlint/package.json | 2 +- tools/yarn-cling/package.json | 4 +- yarn.lock | 254 +++++++++--------- 22 files changed, 164 insertions(+), 160 deletions(-) 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 02dd246c76e47..c8c69cdbd69b9 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 @@ -34,7 +34,7 @@ "aws-sdk-mock": "^5.1.0", "eslint": "^7.26.0", "eslint-config-standard": "^14.1.1", - "eslint-plugin-import": "^2.22.1", + "eslint-plugin-import": "^2.23.2", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^4.3.1", "eslint-plugin-standard": "^4.1.0", diff --git a/packages/@aws-cdk/aws-dynamodb-global/lambda-packages/aws-global-table-coordinator/package.json b/packages/@aws-cdk/aws-dynamodb-global/lambda-packages/aws-global-table-coordinator/package.json index c9d9180d2d772..0a9ee083461e6 100644 --- a/packages/@aws-cdk/aws-dynamodb-global/lambda-packages/aws-global-table-coordinator/package.json +++ b/packages/@aws-cdk/aws-dynamodb-global/lambda-packages/aws-global-table-coordinator/package.json @@ -31,7 +31,7 @@ "aws-sdk-mock": "^5.1.0", "eslint": "^7.26.0", "eslint-config-standard": "^14.1.1", - "eslint-plugin-import": "^2.22.1", + "eslint-plugin-import": "^2.23.2", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^4.3.1", "eslint-plugin-standard": "^4.1.0", diff --git a/packages/@aws-cdk/aws-lambda-nodejs/package.json b/packages/@aws-cdk/aws-lambda-nodejs/package.json index ae5a2e85cfc8b..6f2d2e2049b5e 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/package.json +++ b/packages/@aws-cdk/aws-lambda-nodejs/package.json @@ -67,7 +67,7 @@ "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "delay": "5.0.0", - "esbuild": "^0.11.20", + "esbuild": "^0.12.1", "pkglint": "0.0.0", "@aws-cdk/assert-internal": "0.0.0" }, diff --git a/packages/@aws-cdk/cloud-assembly-schema/package.json b/packages/@aws-cdk/cloud-assembly-schema/package.json index e7c5db4003ee8..5b573ad4c418e 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/package.json +++ b/packages/@aws-cdk/cloud-assembly-schema/package.json @@ -60,7 +60,7 @@ "devDependencies": { "@types/jest": "^26.0.23", "@types/mock-fs": "^4.13.0", - "@types/semver": "^7.3.5", + "@types/semver": "^7.3.6", "cdk-build-tools": "0.0.0", "jest": "^26.6.3", "mock-fs": "^4.14.0", diff --git a/packages/@aws-cdk/cloudformation-diff/package.json b/packages/@aws-cdk/cloudformation-diff/package.json index 9d51aa3274650..c4e3b0a8a1b6a 100644 --- a/packages/@aws-cdk/cloudformation-diff/package.json +++ b/packages/@aws-cdk/cloudformation-diff/package.json @@ -22,12 +22,12 @@ "license": "Apache-2.0", "dependencies": { "@aws-cdk/cfnspec": "0.0.0", - "@types/node": "^10.17.56", + "@types/node": "^10.17.60", "colors": "^1.4.0", "diff": "^5.0.0", "fast-deep-equal": "^3.1.3", "string-width": "^4.2.2", - "table": "^6.7.0" + "table": "^6.7.1" }, "devDependencies": { "@types/jest": "^26.0.23", diff --git a/packages/@aws-cdk/core/package.json b/packages/@aws-cdk/core/package.json index c926741368a48..eb730c5e5dc34 100644 --- a/packages/@aws-cdk/core/package.json +++ b/packages/@aws-cdk/core/package.json @@ -179,7 +179,7 @@ "@types/jest": "^26.0.23", "@types/lodash": "^4.14.169", "@types/minimatch": "^3.0.4", - "@types/node": "^10.17.59", + "@types/node": "^10.17.60", "@types/sinon": "^9.0.11", "cdk-build-tools": "0.0.0", "cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/cx-api/package.json b/packages/@aws-cdk/cx-api/package.json index e362a20ae0650..493537f0b0762 100644 --- a/packages/@aws-cdk/cx-api/package.json +++ b/packages/@aws-cdk/cx-api/package.json @@ -66,7 +66,7 @@ "devDependencies": { "@types/jest": "^26.0.23", "@types/mock-fs": "^4.13.0", - "@types/semver": "^7.3.5", + "@types/semver": "^7.3.6", "cdk-build-tools": "0.0.0", "jest": "^26.6.3", "mock-fs": "^4.14.0", diff --git a/packages/@monocdk-experiment/assert/package.json b/packages/@monocdk-experiment/assert/package.json index 6dab583bf473a..3b0b3312742f3 100644 --- a/packages/@monocdk-experiment/assert/package.json +++ b/packages/@monocdk-experiment/assert/package.json @@ -35,7 +35,7 @@ "devDependencies": { "@monocdk-experiment/rewrite-imports": "0.0.0", "@types/jest": "^26.0.23", - "@types/node": "^10.17.59", + "@types/node": "^10.17.60", "cdk-build-tools": "0.0.0", "constructs": "^3.3.69", "jest": "^26.6.3", diff --git a/packages/@monocdk-experiment/rewrite-imports/package.json b/packages/@monocdk-experiment/rewrite-imports/package.json index fcfbe320c9d96..a2341279af695 100644 --- a/packages/@monocdk-experiment/rewrite-imports/package.json +++ b/packages/@monocdk-experiment/rewrite-imports/package.json @@ -38,7 +38,7 @@ "devDependencies": { "@types/glob": "^7.1.3", "@types/jest": "^26.0.23", - "@types/node": "^10.17.59", + "@types/node": "^10.17.60", "cdk-build-tools": "0.0.0", "pkglint": "0.0.0" }, diff --git a/packages/aws-cdk-lib/package.json b/packages/aws-cdk-lib/package.json index 22ba72ab4eafd..a57f3ec6c14af 100644 --- a/packages/aws-cdk-lib/package.json +++ b/packages/aws-cdk-lib/package.json @@ -305,7 +305,7 @@ "@aws-cdk/pipelines": "0.0.0", "@aws-cdk/region-info": "0.0.0", "@types/fs-extra": "^8.1.1", - "@types/node": "^10.17.59", + "@types/node": "^10.17.60", "cdk-build-tools": "0.0.0", "constructs": "^3.3.69", "fs-extra": "^9.1.0", diff --git a/packages/aws-cdk-migration/package.json b/packages/aws-cdk-migration/package.json index 98ecfa881e256..845c95b9389ef 100644 --- a/packages/aws-cdk-migration/package.json +++ b/packages/aws-cdk-migration/package.json @@ -38,7 +38,7 @@ "devDependencies": { "@types/glob": "^7.1.3", "@types/jest": "^26.0.23", - "@types/node": "^10.17.59", + "@types/node": "^10.17.60", "cdk-build-tools": "0.0.0", "pkglint": "0.0.0" }, diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index b93eb05b444ec..0ed2f35138803 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -46,9 +46,9 @@ "@types/jest": "^26.0.23", "@types/minimatch": "^3.0.4", "@types/mockery": "^1.4.29", - "@types/node": "^10.17.59", + "@types/node": "^10.17.60", "@types/promptly": "^3.0.1", - "@types/semver": "^7.3.5", + "@types/semver": "^7.3.6", "@types/sinon": "^9.0.11", "@types/table": "^6.0.0", "@types/uuid": "^8.3.0", @@ -57,7 +57,7 @@ "aws-sdk-mock": "^5.1.0", "cdk-build-tools": "0.0.0", "jest": "^26.6.3", - "make-runnable": "^1.3.8", + "make-runnable": "^1.3.9", "mockery": "^2.1.0", "nock": "^13.0.11", "pkglint": "0.0.0", @@ -85,7 +85,7 @@ "proxy-agent": "^4.0.1", "semver": "^7.3.5", "source-map-support": "^0.5.19", - "table": "^6.7.0", + "table": "^6.7.1", "uuid": "^8.3.2", "wrap-ansi": "^7.0.0", "yaml": "1.10.2", diff --git a/packages/awslint/package.json b/packages/awslint/package.json index 6b91a94c8349d..82bdd1818168c 100644 --- a/packages/awslint/package.json +++ b/packages/awslint/package.json @@ -29,13 +29,13 @@ "@types/yargs": "^15.0.13", "pkglint": "0.0.0", "typescript": "~3.9.9", - "@typescript-eslint/eslint-plugin": "^4.23.0", - "@typescript-eslint/parser": "^4.23.0", + "@typescript-eslint/eslint-plugin": "^4.24.0", + "@typescript-eslint/parser": "^4.24.0", "eslint": "^7.26.0", "eslint-import-resolver-node": "^0.3.4", "eslint-import-resolver-typescript": "^2.4.0", "eslint-plugin-cdk": "0.0.0", - "eslint-plugin-import": "^2.22.1", + "eslint-plugin-import": "^2.23.2", "eslint-plugin-jest": "^24.3.6", "jest": "^26.6.3" }, diff --git a/packages/cdk-assets/package.json b/packages/cdk-assets/package.json index aa751f80283d7..31d3e16b8f067 100644 --- a/packages/cdk-assets/package.json +++ b/packages/cdk-assets/package.json @@ -35,7 +35,7 @@ "@types/jest": "^26.0.23", "@types/jszip": "^3.4.1", "@types/mock-fs": "^4.13.0", - "@types/node": "^10.17.59", + "@types/node": "^10.17.60", "@types/yargs": "^15.0.13", "cdk-build-tools": "0.0.0", "jest": "^26.6.3", diff --git a/packages/monocdk/package.json b/packages/monocdk/package.json index 06b2c423cfc45..dab457a5e031c 100644 --- a/packages/monocdk/package.json +++ b/packages/monocdk/package.json @@ -307,7 +307,7 @@ "@aws-cdk/region-info": "0.0.0", "@aws-cdk/yaml-cfn": "0.0.0", "@types/fs-extra": "^8.1.1", - "@types/node": "^10.17.59", + "@types/node": "^10.17.60", "cdk-build-tools": "0.0.0", "constructs": "^3.3.69", "fs-extra": "^9.1.0", diff --git a/tools/cdk-build-tools/package.json b/tools/cdk-build-tools/package.json index 8e31468544319..2b1e2c4d8c653 100644 --- a/tools/cdk-build-tools/package.json +++ b/tools/cdk-build-tools/package.json @@ -36,19 +36,19 @@ "@types/fs-extra": "^8.1.1", "@types/jest": "^26.0.23", "@types/yargs": "^15.0.13", - "@types/semver": "^7.3.5", + "@types/semver": "^7.3.6", "pkglint": "0.0.0" }, "dependencies": { - "@typescript-eslint/eslint-plugin": "^4.23.0", - "@typescript-eslint/parser": "^4.23.0", + "@typescript-eslint/eslint-plugin": "^4.24.0", + "@typescript-eslint/parser": "^4.24.0", "awslint": "0.0.0", "colors": "^1.4.0", "eslint": "^7.26.0", "eslint-import-resolver-node": "^0.3.4", "eslint-import-resolver-typescript": "^2.4.0", "eslint-plugin-cdk": "0.0.0", - "eslint-plugin-import": "^2.22.1", + "eslint-plugin-import": "^2.23.2", "eslint-plugin-jest": "^24.3.6", "fs-extra": "^9.1.0", "jest": "^26.6.3", diff --git a/tools/eslint-plugin-cdk/package.json b/tools/eslint-plugin-cdk/package.json index 9a28d67e390b2..7e3806cd7ee89 100644 --- a/tools/eslint-plugin-cdk/package.json +++ b/tools/eslint-plugin-cdk/package.json @@ -15,14 +15,14 @@ "@types/eslint": "^7.2.10", "@types/fs-extra": "^8.1.1", "@types/jest": "^26.0.23", - "@types/node": "^10.17.59", + "@types/node": "^10.17.60", "@types/estree": "*", "eslint-plugin-rulesdir": "^0.2.0", "jest": "^26.6.3", "typescript": "~3.9.9" }, "dependencies": { - "@typescript-eslint/parser": "^4.23.0", + "@typescript-eslint/parser": "^4.24.0", "eslint": "^7.26.0", "fs-extra": "^9.1.0" }, diff --git a/tools/nodeunit-shim/package.json b/tools/nodeunit-shim/package.json index 63c784d19321c..7de70380c4d2d 100644 --- a/tools/nodeunit-shim/package.json +++ b/tools/nodeunit-shim/package.json @@ -13,7 +13,7 @@ }, "devDependencies": { "@types/jest": "^26.0.23", - "@types/node": "^10.17.59", + "@types/node": "^10.17.60", "typescript": "~3.9.9" }, "dependencies": { diff --git a/tools/pkglint/package.json b/tools/pkglint/package.json index 0fd91fcb0d2a0..47796fe237303 100644 --- a/tools/pkglint/package.json +++ b/tools/pkglint/package.json @@ -38,15 +38,15 @@ "@types/fs-extra": "^8.1.1", "@types/glob": "^7.1.3", "@types/jest": "^26.0.23", - "@types/semver": "^7.3.5", + "@types/semver": "^7.3.6", "@types/yargs": "^15.0.13", - "@typescript-eslint/eslint-plugin": "^4.23.0", - "@typescript-eslint/parser": "^4.23.0", + "@typescript-eslint/eslint-plugin": "^4.24.0", + "@typescript-eslint/parser": "^4.24.0", "eslint": "^7.26.0", "eslint-import-resolver-node": "^0.3.4", "eslint-import-resolver-typescript": "^2.4.0", "eslint-plugin-cdk": "0.0.0", - "eslint-plugin-import": "^2.22.1", + "eslint-plugin-import": "^2.23.2", "eslint-plugin-jest": "^24.3.6", "jest": "^26.6.3", "typescript": "~3.9.9" diff --git a/tools/prlint/package.json b/tools/prlint/package.json index d30230c2d9cd2..d8f17fbba3592 100644 --- a/tools/prlint/package.json +++ b/tools/prlint/package.json @@ -15,7 +15,7 @@ }, "devDependencies": { "jest": "^26.6.3", - "make-runnable": "^1.3.8" + "make-runnable": "^1.3.9" }, "scripts": { "build": "echo success", diff --git a/tools/yarn-cling/package.json b/tools/yarn-cling/package.json index 14bb7e2ffef4c..1fb5302f27293 100644 --- a/tools/yarn-cling/package.json +++ b/tools/yarn-cling/package.json @@ -39,9 +39,9 @@ }, "devDependencies": { "@types/jest": "^26.0.23", - "@types/node": "^10.17.59", + "@types/node": "^10.17.60", "@types/yarnpkg__lockfile": "^1.1.4", - "@types/semver": "^7.3.5", + "@types/semver": "^7.3.6", "jest": "^26.6.3", "pkglint": "0.0.0", "typescript": "~3.9.9" diff --git a/yarn.lock b/yarn.lock index 0be97e7c4e3ba..c45f8e5dc4173 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1568,10 +1568,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-15.0.2.tgz#51e9c0920d1b45936ea04341aa3e2e58d339fb67" integrity sha512-p68+a+KoxpoB47015IeYZYRrdqMUcpbK8re/zpFB8Ld46LHC1lPEbp3EXgkEhAYEcPvjJF6ZO+869SQ0aH1dcA== -"@types/node@^10.17.59": - version "10.17.59" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.59.tgz#03f440ccf746a27f7da6e141e6cbae64681dbd2f" - integrity sha512-7Uc8IRrL8yZz5ti45RaFxpbU8TxlzdC3HvxV+hOWo1EyLsuKv/w7y0n+TwZzwL3vdx3oZ2k3ubxPq131hNtXyg== +"@types/node@^10.17.60": + version "10.17.60" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" + integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== "@types/node@^14.14.33": version "14.14.44" @@ -1615,10 +1615,10 @@ resolved "https://registry.yarnpkg.com/@types/punycode/-/punycode-2.1.0.tgz#89e4f3d09b3f92e87a80505af19be7e0c31d4e83" integrity sha512-PG5aLpW6PJOeV2fHRslP4IOMWn+G+Uq8CfnyJ+PDS8ndCbU+soO+fB3NKCKo0p/Jh2Y4aPaiQZsrOXFdzpcA6g== -"@types/semver@^7.3.5": - version "7.3.5" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.5.tgz#74deebbbcb1e86634dbf10a5b5e8798626f5a597" - integrity sha512-iotVxtCCsPLRAvxMFFgxL8HD2l4mAZ2Oin7/VJ2ooWO0VOK4EGOGmZWZn1uCq7RofR3I/1IOSjCHlFT71eVK0Q== +"@types/semver@^7.3.6": + version "7.3.6" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.6.tgz#e9831776f4512a7ba6da53e71c26e5fb67882d63" + integrity sha512-0caWDWmpCp0uifxFh+FaqK3CuZ2SkRR/ZRxAV5+zNdC3QVUi6wyOJnefhPvtNt8NQWXB5OA93BUvZsXpWat2Xw== "@types/sinon@^9.0.11": version "9.0.11" @@ -1692,13 +1692,13 @@ resolved "https://registry.yarnpkg.com/@types/yarnpkg__lockfile/-/yarnpkg__lockfile-1.1.4.tgz#445251eb00bd9c1e751f82c7c6bf4f714edfd464" integrity sha512-/emrKCfQMQmFCqRqqBJ0JueHBT06jBRM3e8OgnvDUcvuExONujIk2hFA5dNsN9Nt41ljGVDdChvCydATZ+KOZw== -"@typescript-eslint/eslint-plugin@^4.23.0": - version "4.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.23.0.tgz#29d3c9c81f6200b1fd6d8454cfb007ba176cde80" - integrity sha512-tGK1y3KIvdsQEEgq6xNn1DjiFJtl+wn8JJQiETtCbdQxw1vzjXyAaIkEmO2l6Nq24iy3uZBMFQjZ6ECf1QdgGw== +"@typescript-eslint/eslint-plugin@^4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.24.0.tgz#03801ffc25b2af9d08f3dc9bccfc0b7ce3780d0f" + integrity sha512-qbCgkPM7DWTsYQGjx9RTuQGswi+bEt0isqDBeo+CKV0953zqI0Tp7CZ7Fi9ipgFA6mcQqF4NOVNwS/f2r6xShw== dependencies: - "@typescript-eslint/experimental-utils" "4.23.0" - "@typescript-eslint/scope-manager" "4.23.0" + "@typescript-eslint/experimental-utils" "4.24.0" + "@typescript-eslint/scope-manager" "4.24.0" debug "^4.1.1" functional-red-black-tree "^1.0.1" lodash "^4.17.15" @@ -1706,15 +1706,15 @@ semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@4.23.0": - version "4.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.23.0.tgz#f2059434cd6e5672bfeab2fb03b7c0a20622266f" - integrity sha512-WAFNiTDnQfrF3Z2fQ05nmCgPsO5o790vOhmWKXbbYQTO9erE1/YsFot5/LnOUizLzU2eeuz6+U/81KV5/hFTGA== +"@typescript-eslint/experimental-utils@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.24.0.tgz#c23ead9de44b99c3a5fd925c33a106b00165e172" + integrity sha512-IwTT2VNDKH1h8RZseMH4CcYBz6lTvRoOLDuuqNZZoThvfHEhOiZPQCow+5El3PtyxJ1iDr6UXZwYtE3yZQjhcw== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/scope-manager" "4.23.0" - "@typescript-eslint/types" "4.23.0" - "@typescript-eslint/typescript-estree" "4.23.0" + "@typescript-eslint/scope-manager" "4.24.0" + "@typescript-eslint/types" "4.24.0" + "@typescript-eslint/typescript-estree" "4.24.0" eslint-scope "^5.0.0" eslint-utils "^2.0.0" @@ -1730,14 +1730,14 @@ eslint-scope "^5.0.0" eslint-utils "^2.0.0" -"@typescript-eslint/parser@^4.23.0": - version "4.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.23.0.tgz#239315d38e42e852bef43a4b0b01bef78f78911c" - integrity sha512-wsvjksHBMOqySy/Pi2Q6UuIuHYbgAMwLczRl4YanEPKW5KVxI9ZzDYh3B5DtcZPQTGRWFJrfcbJ6L01Leybwug== +"@typescript-eslint/parser@^4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.24.0.tgz#2e5f1cc78ffefe43bfac7e5659309a92b09a51bd" + integrity sha512-dj1ZIh/4QKeECLb2f/QjRwMmDArcwc2WorWPRlB8UNTZlY1KpTVsbX7e3ZZdphfRw29aTFUSNuGB8w9X5sS97w== dependencies: - "@typescript-eslint/scope-manager" "4.23.0" - "@typescript-eslint/types" "4.23.0" - "@typescript-eslint/typescript-estree" "4.23.0" + "@typescript-eslint/scope-manager" "4.24.0" + "@typescript-eslint/types" "4.24.0" + "@typescript-eslint/typescript-estree" "4.24.0" debug "^4.1.1" "@typescript-eslint/scope-manager@4.22.1": @@ -1748,23 +1748,23 @@ "@typescript-eslint/types" "4.22.1" "@typescript-eslint/visitor-keys" "4.22.1" -"@typescript-eslint/scope-manager@4.23.0": - version "4.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.23.0.tgz#8792ef7eacac122e2ec8fa2d30a59b8d9a1f1ce4" - integrity sha512-ZZ21PCFxPhI3n0wuqEJK9omkw51wi2bmeKJvlRZPH5YFkcawKOuRMQMnI8mH6Vo0/DoHSeZJnHiIx84LmVQY+w== +"@typescript-eslint/scope-manager@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.24.0.tgz#38088216f0eaf235fa30ed8cabf6948ec734f359" + integrity sha512-9+WYJGDnuC9VtYLqBhcSuM7du75fyCS/ypC8c5g7Sdw7pGL4NDTbeH38eJPfzIydCHZDoOgjloxSAA3+4l/zsA== dependencies: - "@typescript-eslint/types" "4.23.0" - "@typescript-eslint/visitor-keys" "4.23.0" + "@typescript-eslint/types" "4.24.0" + "@typescript-eslint/visitor-keys" "4.24.0" "@typescript-eslint/types@4.22.1": version "4.22.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.22.1.tgz#bf99c6cec0b4a23d53a61894816927f2adad856a" integrity sha512-2HTkbkdAeI3OOcWbqA8hWf/7z9c6gkmnWNGz0dKSLYLWywUlkOAQ2XcjhlKLj5xBFDf8FgAOF5aQbnLRvgNbCw== -"@typescript-eslint/types@4.23.0": - version "4.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.23.0.tgz#da1654c8a5332f4d1645b2d9a1c64193cae3aa3b" - integrity sha512-oqkNWyG2SLS7uTWLZf6Sr7Dm02gA5yxiz1RP87tvsmDsguVATdpVguHr4HoGOcFOpCvx9vtCSCyQUGfzq28YCw== +"@typescript-eslint/types@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.24.0.tgz#6d0cca2048cbda4e265e0c4db9c2a62aaad8228c" + integrity sha512-tkZUBgDQKdvfs8L47LaqxojKDE+mIUmOzdz7r+u+U54l3GDkTpEbQ1Jp3cNqqAU9vMUCBA1fitsIhm7yN0vx9Q== "@typescript-eslint/typescript-estree@4.22.1": version "4.22.1" @@ -1779,13 +1779,13 @@ semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/typescript-estree@4.23.0": - version "4.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.23.0.tgz#0753b292097523852428a6f5a1aa8ccc1aae6cd9" - integrity sha512-5Sty6zPEVZF5fbvrZczfmLCOcby3sfrSPu30qKoY1U3mca5/jvU5cwsPb/CO6Q3ByRjixTMIVsDkqwIxCf/dMw== +"@typescript-eslint/typescript-estree@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.24.0.tgz#b49249679a98014d8b03e8d4b70864b950e3c90f" + integrity sha512-kBDitL/by/HK7g8CYLT7aKpAwlR8doshfWz8d71j97n5kUa5caHWvY0RvEUEanL/EqBJoANev8Xc/mQ6LLwXGA== dependencies: - "@typescript-eslint/types" "4.23.0" - "@typescript-eslint/visitor-keys" "4.23.0" + "@typescript-eslint/types" "4.24.0" + "@typescript-eslint/visitor-keys" "4.24.0" debug "^4.1.1" globby "^11.0.1" is-glob "^4.0.1" @@ -1800,12 +1800,12 @@ "@typescript-eslint/types" "4.22.1" eslint-visitor-keys "^2.0.0" -"@typescript-eslint/visitor-keys@4.23.0": - version "4.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.23.0.tgz#7215cc977bd3b4ef22467b9023594e32f9e4e455" - integrity sha512-5PNe5cmX9pSifit0H+nPoQBXdbNzi5tOEec+3riK+ku4e3er37pKxMKDH5Ct5Y4fhWxcD4spnlYjxi9vXbSpwg== +"@typescript-eslint/visitor-keys@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.24.0.tgz#a8fafdc76cad4e04a681a945fbbac4e35e98e297" + integrity sha512-4ox1sjmGHIxjEDBnMCtWFFhErXtKA1Ec0sBpuz0fqf3P+g3JFGyTxxbF06byw0FRsPnnbq44cKivH7Ks1/0s6g== dependencies: - "@typescript-eslint/types" "4.23.0" + "@typescript-eslint/types" "4.24.0" eslint-visitor-keys "^2.0.0" "@yarnpkg/lockfile@^1.1.0": @@ -2088,7 +2088,7 @@ array-ify@^1.0.0: resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" integrity sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4= -array-includes@^3.1.1: +array-includes@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.3.tgz#c7f619b382ad2afaf5326cddfdc0afc61af7690a" integrity sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A== @@ -2109,7 +2109,7 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= -array.prototype.flat@^1.2.3: +array.prototype.flat@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz#6ef638b43312bd401b4c6199fdec7e2dc9e9a123" integrity sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg== @@ -2900,10 +2900,13 @@ constructs@^3.3.69: resolved "https://registry.yarnpkg.com/constructs/-/constructs-3.3.75.tgz#222516951fd6b8380cb6fea3c171eeca0bf980a4" integrity sha512-q10foASSSfDWmS99OQLfnWDXCzqLvoORISAVWPFg0AmIGlBv2ZdDOtXxLqrJARPxVlOldmW2JzWzdRI+4+0/ZA== -contains-path@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" - integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= +contains-path@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-1.0.0.tgz#3458b332185603e8eed18f518d4a10888a3abc91" + integrity sha1-NFizMhhWA+ju0Y9RjUoQiIo6vJE= + dependencies: + normalize-path "^2.1.1" + path-starts-with "^1.0.0" conventional-changelog-angular@^5.0.12: version "5.0.12" @@ -3274,6 +3277,13 @@ debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: dependencies: ms "2.0.0" +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" @@ -3489,13 +3499,12 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" -doctrine@1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" - integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo= +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== dependencies: esutils "^2.0.2" - isarray "^1.0.0" doctrine@^3.0.0: version "3.0.0" @@ -3696,10 +3705,10 @@ es6-error@^4.0.1: resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== -esbuild@^0.11.20: - version "0.11.20" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.11.20.tgz#7cefa1aee8b372c184e42457885f7ce5d3e62a1e" - integrity sha512-QOZrVpN/Yz74xfat0H6euSgn3RnwLevY1mJTEXneukz1ln9qB+ieaerRMzSeETpz/UJWsBMzRVR/andBht5WKw== +esbuild@^0.12.1: + version "0.12.1" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.12.1.tgz#f652d5b3b9432dbb42fc2c034ddd62360296e03d" + integrity sha512-WfQ00MKm/Y4ysz1u9PCUAsV66k5lbrcEvS6aG9jhBIavpB94FBdaWeBkaZXxCZB4w+oqh+j4ozJFWnnFprOXbg== escalade@^3.1.1: version "3.1.1" @@ -3769,12 +3778,12 @@ eslint-import-resolver-typescript@^2.4.0: resolve "^1.17.0" tsconfig-paths "^3.9.0" -eslint-module-utils@^2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz#579ebd094f56af7797d19c9866c9c9486629bfa6" - integrity sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA== +eslint-module-utils@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz#b51be1e473dd0de1c5ea638e22429c2490ea8233" + integrity sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A== dependencies: - debug "^2.6.9" + debug "^3.2.7" pkg-dir "^2.0.0" eslint-plugin-es@^3.0.0: @@ -3785,23 +3794,26 @@ eslint-plugin-es@^3.0.0: eslint-utils "^2.0.0" regexpp "^3.0.0" -eslint-plugin-import@^2.22.1: - version "2.22.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz#0896c7e6a0cf44109a2d97b95903c2bb689d7702" - integrity sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw== +eslint-plugin-import@^2.23.2: + version "2.23.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.23.2.tgz#ee15dd68fc7a1a1ba4c653c734e0d01c100d3484" + integrity sha512-LmNoRptHBxOP+nb0PIKz1y6OSzCJlB+0g0IGS3XV4KaKk2q4szqQ6s6F1utVf5ZRkxk/QOTjdxe7v4VjS99Bsg== dependencies: - array-includes "^3.1.1" - array.prototype.flat "^1.2.3" - contains-path "^0.1.0" + array-includes "^3.1.3" + array.prototype.flat "^1.2.4" + contains-path "^1.0.0" debug "^2.6.9" - doctrine "1.5.0" + doctrine "^2.1.0" eslint-import-resolver-node "^0.3.4" - eslint-module-utils "^2.6.0" + eslint-module-utils "^2.6.1" + find-up "^2.0.0" has "^1.0.3" + is-core-module "^2.4.0" minimatch "^3.0.4" - object.values "^1.1.1" - read-pkg-up "^2.0.0" - resolve "^1.17.0" + object.values "^1.1.3" + pkg-up "^2.0.0" + read-pkg-up "^3.0.0" + resolve "^1.20.0" tsconfig-paths "^3.9.0" eslint-plugin-jest@^24.3.6: @@ -5071,7 +5083,7 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" -is-core-module@^2.2.0: +is-core-module@^2.2.0, is-core-module@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1" integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A== @@ -6294,16 +6306,6 @@ load-json-file@^1.0.0: pinkie-promise "^2.0.0" strip-bom "^2.0.0" -load-json-file@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" - integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg= - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - strip-bom "^3.0.0" - load-json-file@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" @@ -6526,13 +6528,13 @@ make-fetch-happen@^8.0.9: socks-proxy-agent "^5.0.0" ssri "^8.0.0" -make-runnable@^1.3.8: - version "1.3.8" - resolved "https://registry.yarnpkg.com/make-runnable/-/make-runnable-1.3.8.tgz#ce547757d776c1f369028dbd937731db9514fe6a" - integrity sha512-8lXEwzB1eLl1pGFx5JZxFbWac6v6bHfRrCn/xDO4Qk9H6DN0QqIsJOquGxz7NkZxedhh7uki1txsbgTci1W/Vw== +make-runnable@^1.3.9: + version "1.3.9" + resolved "https://registry.yarnpkg.com/make-runnable/-/make-runnable-1.3.9.tgz#924bf6b28c4f1524391ec34ec1f14fc24aac3af8" + integrity sha512-yiAvZX8hAEXOcYP6ibvnjWcmxZVQ2aBZMQ2148IEPFga0ODr3htJfc+bdeUQfQCUuVe2lrY3VmajPAVSDZG8xw== dependencies: bluebird "^3.5.0" - yargs "^16.0.3" + yargs "^16.2.0" makeerror@1.0.x: version "1.0.11" @@ -6881,7 +6883,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.0.0: +ms@^2.0.0, ms@^2.1.1: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -7354,7 +7356,7 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" -object.values@^1.1.1: +object.values@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.3.tgz#eaa8b1e17589f02f698db093f7c62ee1699742ee" integrity sha512-nkF6PfDB9alkOUxpf1HNm/QlkeW3SReqL5WXeBLpEJJnlPSvRaDQpW3gQTksTN3fgJX4hL42RzKyOin6ff3tyw== @@ -7767,6 +7769,13 @@ path-parse@^1.0.6: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== +path-starts-with@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/path-starts-with/-/path-starts-with-1.0.0.tgz#b28243015e8b138de572682ac52da42e646ad84e" + integrity sha1-soJDAV6LE43lcmgqxS2kLmRq2E4= + dependencies: + normalize-path "^2.1.1" + path-to-regexp@^1.7.0: version "1.8.0" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" @@ -7783,13 +7792,6 @@ path-type@^1.0.0: pify "^2.0.0" pinkie-promise "^2.0.0" -path-type@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" - integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM= - dependencies: - pify "^2.0.0" - path-type@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" @@ -7872,6 +7874,13 @@ pkg-dir@^4.1.0, pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f" + integrity sha1-yBmscoBZpGHKscOImivjxJoATX8= + dependencies: + find-up "^2.1.0" + posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" @@ -8147,14 +8156,6 @@ read-pkg-up@^1.0.1: find-up "^1.0.0" read-pkg "^1.0.0" -read-pkg-up@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" - integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4= - dependencies: - find-up "^2.0.0" - read-pkg "^2.0.0" - read-pkg-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" @@ -8189,15 +8190,6 @@ read-pkg@^1.0.0: normalize-package-data "^2.3.2" path-type "^1.0.0" -read-pkg@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" - integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg= - dependencies: - load-json-file "^2.0.0" - normalize-package-data "^2.3.2" - path-type "^2.0.0" - read-pkg@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" @@ -9168,7 +9160,7 @@ symbol-tree@^3.2.4: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== -table@*, table@^6.0.4, table@^6.7.0: +table@*, table@^6.0.4: version "6.7.0" resolved "https://registry.yarnpkg.com/table/-/table-6.7.0.tgz#26274751f0ee099c547f6cb91d3eff0d61d155b2" integrity sha512-SAM+5p6V99gYiiy2gT5ArdzgM1dLDed0nkrWmG6Fry/bUS/m9x83BwpJUOf1Qj/x2qJd+thL6IkIx7qPGRxqBw== @@ -9180,6 +9172,18 @@ table@*, table@^6.0.4, table@^6.7.0: string-width "^4.2.0" strip-ansi "^6.0.0" +table@^6.7.1: + version "6.7.1" + resolved "https://registry.yarnpkg.com/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2" + integrity sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg== + dependencies: + ajv "^8.0.1" + lodash.clonedeep "^4.5.0" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.0" + strip-ansi "^6.0.0" + tap-mocha-reporter@^3.0.9, tap-mocha-reporter@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/tap-mocha-reporter/-/tap-mocha-reporter-5.0.1.tgz#74f00be2ddd2a380adad45e085795137bc39497a" @@ -10238,7 +10242,7 @@ yargs@^15.0.2, yargs@^15.4.1: y18n "^4.0.0" yargs-parser "^18.1.2" -yargs@^16.0.0, yargs@^16.0.3, yargs@^16.2.0: +yargs@^16.0.0, yargs@^16.2.0: version "16.2.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== From e478bf5a3e458efbe1cd7755516dcf4877121b12 Mon Sep 17 00:00:00 2001 From: Mitchell Valine Date: Wed, 19 May 2021 17:45:10 -0700 Subject: [PATCH 067/134] chore: add go v2 init template (#14785) Adds go `app` init template for cdk v2. References aws-cdk-go v2 and constructs v10. Testing: Checked out the v2-main branch and added this init template to the CLI. Changed the version of `packages/aws-cdk/package.json` from `0.0.0` to `2.0.0-rc.4` and ran `buildup`. Then ran `cdk init --language=go` against local build of CLI and verified that `cdk ls` and `cdk synth` both output correctly using both local cli and `npx cdk@next`. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../v2/app/go/%name%.template.go | 68 +++++++++++++++++++ .../v2/app/go/%name%_test.template.go | 28 ++++++++ .../v2/app/go/.template.gitignore | 19 ++++++ .../lib/init-templates/v2/app/go/README.md | 14 ++++ .../v2/app/go/cdk.template.json | 3 + .../init-templates/v2/app/go/go.template.mod | 13 ++++ 6 files changed, 145 insertions(+) create mode 100644 packages/aws-cdk/lib/init-templates/v2/app/go/%name%.template.go create mode 100644 packages/aws-cdk/lib/init-templates/v2/app/go/%name%_test.template.go create mode 100644 packages/aws-cdk/lib/init-templates/v2/app/go/.template.gitignore create mode 100644 packages/aws-cdk/lib/init-templates/v2/app/go/README.md create mode 100644 packages/aws-cdk/lib/init-templates/v2/app/go/cdk.template.json create mode 100644 packages/aws-cdk/lib/init-templates/v2/app/go/go.template.mod diff --git a/packages/aws-cdk/lib/init-templates/v2/app/go/%name%.template.go b/packages/aws-cdk/lib/init-templates/v2/app/go/%name%.template.go new file mode 100644 index 0000000000000..46577faef90f6 --- /dev/null +++ b/packages/aws-cdk/lib/init-templates/v2/app/go/%name%.template.go @@ -0,0 +1,68 @@ +package main + +import ( + "github.com/aws/aws-cdk-go/awscdk/v2" + "github.com/aws/aws-cdk-go/awscdk/v2/awssns" + "github.com/aws/constructs-go/constructs/v10" + "github.com/aws/jsii-runtime-go" +) + +type %name.PascalCased%StackProps struct { + awscdk.StackProps +} + +func New%name.PascalCased%Stack(scope constructs.Construct, id string, props *%name.PascalCased%StackProps) awscdk.Stack { + var sprops awscdk.StackProps + if props != nil { + sprops = props.StackProps + } + stack := awscdk.NewStack(scope, &id, &sprops) + + // The code that defines your stack goes here + + // as an example, here's how you would define an AWS SNS topic: + awssns.NewTopic(stack, jsii.String("MyTopic"), &awssns.TopicProps{ + DisplayName: jsii.String("MyCoolTopic"), + }) + + return stack +} + +func main() { + app := awscdk.NewApp(nil) + + New%name.PascalCased%Stack(app, "%name.PascalCased%Stack", &%name.PascalCased%StackProps{ + awscdk.StackProps{ + Env: env(), + }, + }) + + app.Synth(nil) +} + +// env determines the AWS environment (account+region) in which our stack is to +// be deployed. For more information see: https://docs.aws.amazon.com/cdk/latest/guide/environments.html +func env() *awscdk.Environment { + // If unspecified, this stack will be "environment-agnostic". + // Account/Region-dependent features and context lookups will not work, but a + // single synthesized template can be deployed anywhere. + //--------------------------------------------------------------------------- + return nil + + // Uncomment if you know exactly what account and region you want to deploy + // the stack to. This is the recommendation for production stacks. + //--------------------------------------------------------------------------- + // return &awscdk.Environment{ + // Account: jsii.String("123456789012"), + // Region: jsii.String("us-east-1"), + // } + + // Uncomment to specialize this stack for the AWS Account and Region that are + // implied by the current CLI configuration. This is recommended for dev + // stacks. + //--------------------------------------------------------------------------- + // return &awscdk.Environment{ + // Account: jsii.String(os.Getenv("CDK_DEFAULT_ACCOUNT")), + // Region: jsii.String(os.Getenv("CDK_DEFAULT_REGION")), + // } +} diff --git a/packages/aws-cdk/lib/init-templates/v2/app/go/%name%_test.template.go b/packages/aws-cdk/lib/init-templates/v2/app/go/%name%_test.template.go new file mode 100644 index 0000000000000..6c166d681b0ce --- /dev/null +++ b/packages/aws-cdk/lib/init-templates/v2/app/go/%name%_test.template.go @@ -0,0 +1,28 @@ +package main + +import ( + "encoding/json" + "testing" + + "github.com/aws/aws-cdk-go/awscdk/v2" + "github.com/stretchr/testify/assert" + "github.com/tidwall/gjson" +) + +func Test%name.PascalCased%Stack(t *testing.T) { + // GIVEN + app := awscdk.NewApp(nil) + + // WHEN + stack := New%name.PascalCased%Stack(app, "MyStack", nil) + + // THEN + bytes, err := json.Marshal(app.Synth(nil).GetStackArtifact(stack.ArtifactId()).Template()) + if err != nil { + t.Error(err) + } + + template := gjson.ParseBytes(bytes) + displayName := template.Get("Resources.MyTopic86869434.Properties.DisplayName").String() + assert.Equal(t, "MyCoolTopic", displayName) +} diff --git a/packages/aws-cdk/lib/init-templates/v2/app/go/.template.gitignore b/packages/aws-cdk/lib/init-templates/v2/app/go/.template.gitignore new file mode 100644 index 0000000000000..92fe1ec34b4b6 --- /dev/null +++ b/packages/aws-cdk/lib/init-templates/v2/app/go/.template.gitignore @@ -0,0 +1,19 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# go.sum should be committed +!go.sum + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/packages/aws-cdk/lib/init-templates/v2/app/go/README.md b/packages/aws-cdk/lib/init-templates/v2/app/go/README.md new file mode 100644 index 0000000000000..9b8d4b29f26e3 --- /dev/null +++ b/packages/aws-cdk/lib/init-templates/v2/app/go/README.md @@ -0,0 +1,14 @@ +# Welcome to your CDK Go project! + +This is a blank project for Go development with CDK. + +**NOTICE**: Go support is still in Developer Preview. This implies that APIs may +change while we address early feedback from the community. We would love to hear +about your experience through GitHub issues. + +## Useful commands + + * `cdk deploy` deploy this stack to your default AWS account/region + * `cdk diff` compare deployed stack with current state + * `cdk synth` emits the synthesized CloudFormation template + * `go test` run unit tests diff --git a/packages/aws-cdk/lib/init-templates/v2/app/go/cdk.template.json b/packages/aws-cdk/lib/init-templates/v2/app/go/cdk.template.json new file mode 100644 index 0000000000000..ad88cd7ef75f3 --- /dev/null +++ b/packages/aws-cdk/lib/init-templates/v2/app/go/cdk.template.json @@ -0,0 +1,3 @@ +{ + "app": "go mod download && go run %name%.go" +} \ No newline at end of file diff --git a/packages/aws-cdk/lib/init-templates/v2/app/go/go.template.mod b/packages/aws-cdk/lib/init-templates/v2/app/go/go.template.mod new file mode 100644 index 0000000000000..50f21389478f4 --- /dev/null +++ b/packages/aws-cdk/lib/init-templates/v2/app/go/go.template.mod @@ -0,0 +1,13 @@ +module %name% + +go 1.16 + +require ( + github.com/aws/aws-cdk-go/awscdk/v2 v%cdk-version% + github.com/aws/constructs-go/constructs/v10 v10.0.5 + github.com/aws/jsii-runtime-go v1.29.0 + + // for testing + github.com/tidwall/gjson v1.7.4 + github.com/stretchr/testify v1.7.0 +) From b02311cd55b5bdbe408085488dd17816f181fd2c Mon Sep 17 00:00:00 2001 From: Jonathan Goldwasser Date: Thu, 20 May 2021 09:54:01 +0200 Subject: [PATCH 068/134] feat(lambda-nodejs): pnpm support (#14772) Closes #14757 --- packages/@aws-cdk/aws-lambda-nodejs/README.md | 14 +++--- .../@aws-cdk/aws-lambda-nodejs/lib/Dockerfile | 5 +- .../aws-lambda-nodejs/lib/function.ts | 46 +++++++++++-------- .../aws-lambda-nodejs/lib/package-manager.ts | 8 ++++ .../aws-lambda-nodejs/test/bundling.test.ts | 21 +++++++++ .../aws-lambda-nodejs/test/docker.test.ts | 13 ++++++ .../test/package-manager.test.ts | 9 +++- 7 files changed, 89 insertions(+), 27 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda-nodejs/README.md b/packages/@aws-cdk/aws-lambda-nodejs/README.md index 981bf6755148e..359f914da040a 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/README.md +++ b/packages/@aws-cdk/aws-lambda-nodejs/README.md @@ -46,10 +46,9 @@ when working with the AWS SDK for JavaScript. Set the `awsSdkConnectionReuse` pr ## Lock file -The `NodejsFunction` requires a dependencies lock file (`yarn.lock` or -`package-lock.json`). When bundling in a Docker container, the path containing this -lock file is used as the source (`/asset-input`) for the volume mounted in the -container. +The `NodejsFunction` requires a dependencies lock file (`yarn.lock`, `pnpm-lock.yaml` or +`package-lock.json`). When bundling in a Docker container, the path containing this lock file is +used as the source (`/asset-input`) for the volume mounted in the container. By default, the construct will try to automatically determine your project lock file. Alternatively, you can specify the `depsLockFilePath` prop manually. In this @@ -114,8 +113,9 @@ new lambda.NodejsFunction(this, 'my-handler', { ``` The modules listed in `nodeModules` must be present in the `package.json`'s dependencies or -installed. The same version will be used for installation. The lock file (`yarn.lock` or -`package-lock.json`) will be used along with the right installer (`yarn` or `npm`). +installed. The same version will be used for installation. The lock file (`yarn.lock`, +`pnpm-lock.yaml` or `package-lock.json`) will be used along with the right installer (`yarn`, +`pnpm` or `npm`). When working with `nodeModules` using native dependencies, you might want to force bundling in a Docker container even if `esbuild` is available in your environment. This can be done by setting @@ -219,7 +219,7 @@ new lambda.NodejsFunction(this, 'my-handler', { ``` This image should have `esbuild` installed **globally**. If you plan to use `nodeModules` it -should also have `npm` or `yarn` depending on the lock file you're using. +should also have `npm`, `yarn` or `pnpm` depending on the lock file you're using. Use the [default image provided by `@aws-cdk/aws-lambda-nodejs`](https://github.com/aws/aws-cdk/blob/master/packages/%40aws-cdk/aws-lambda-nodejs/lib/Dockerfile) as a source of inspiration. diff --git a/packages/@aws-cdk/aws-lambda-nodejs/lib/Dockerfile b/packages/@aws-cdk/aws-lambda-nodejs/lib/Dockerfile index 27fe83af43c35..578b1b8135d5c 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/lib/Dockerfile +++ b/packages/@aws-cdk/aws-lambda-nodejs/lib/Dockerfile @@ -1,11 +1,14 @@ # The correct AWS SAM build image based on the runtime of the function will be # passed as build arg. The default allows to do `docker build .` when testing. -ARG IMAGE=public.ecr.aws/sam/build-nodejs12.x +ARG IMAGE=public.ecr.aws/sam/build-nodejs14.x FROM $IMAGE # Install yarn RUN npm install --global yarn@1.22.5 +# Install pnpm +RUN npm install --global pnpm + # Install esbuild # (unsafe-perm because esbuild has a postinstall script) ARG ESBUILD_VERSION=0 diff --git a/packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts b/packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts index 476c1e45b595c..d5519dbc5da34 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts @@ -84,28 +84,11 @@ export class NodejsFunction extends lambda.Function { throw new Error('Only `NODEJS` runtimes are supported.'); } - // Find lock file - let depsLockFilePath: string; - if (props.depsLockFilePath) { - if (!fs.existsSync(props.depsLockFilePath)) { - throw new Error(`Lock file at ${props.depsLockFilePath} doesn't exist`); - } - if (!fs.statSync(props.depsLockFilePath).isFile()) { - throw new Error('`depsLockFilePath` should point to a file'); - } - depsLockFilePath = path.resolve(props.depsLockFilePath); - } else { - const lockFile = findUp(PackageManager.YARN.lockFile) ?? findUp(PackageManager.NPM.lockFile); - if (!lockFile) { - throw new Error('Cannot find a package lock file (`yarn.lock` or `package-lock.json`). Please specify it with `depsFileLockPath`.'); - } - depsLockFilePath = lockFile; - } - // Entry and defaults const entry = path.resolve(findEntry(id, props.entry)); const handler = props.handler ?? 'handler'; const runtime = props.runtime ?? lambda.Runtime.NODEJS_14_X; + const depsLockFilePath = findLockFile(props.depsLockFilePath); super(scope, id, { ...props, @@ -126,6 +109,33 @@ export class NodejsFunction extends lambda.Function { } } +/** + * Checks given lock file or searches for a lock file + */ +function findLockFile(depsLockFilePath?: string): string { + if (depsLockFilePath) { + if (!fs.existsSync(depsLockFilePath)) { + throw new Error(`Lock file at ${depsLockFilePath} doesn't exist`); + } + + if (!fs.statSync(depsLockFilePath).isFile()) { + throw new Error('`depsLockFilePath` should point to a file'); + } + + return path.resolve(depsLockFilePath); + } + + const lockFile = findUp(PackageManager.PNPM.lockFile) + ?? findUp(PackageManager.YARN.lockFile) + ?? findUp(PackageManager.NPM.lockFile); + + if (!lockFile) { + throw new Error('Cannot find a package lock file (`pnpm-lock.yaml`, `yarn.lock` or `package-lock.json`). Please specify it with `depsFileLockPath`.'); + } + + return lockFile; +} + /** * Searches for an entry file. Preference order is the following: * 1. Given entry file diff --git a/packages/@aws-cdk/aws-lambda-nodejs/lib/package-manager.ts b/packages/@aws-cdk/aws-lambda-nodejs/lib/package-manager.ts index dd5260f87838c..a95373bd6d45f 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/lib/package-manager.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/lib/package-manager.ts @@ -23,6 +23,12 @@ export class PackageManager { runCommand: ['yarn', 'run'], }); + public static PNPM = new PackageManager({ + lockFile: 'pnpm-lock.yaml', + installCommand: ['pnpm', 'install'], + runCommand: ['pnpm', 'run'], + }); + public static fromLockFile(lockFilePath: string): PackageManager { const lockFile = path.basename(lockFilePath); @@ -31,6 +37,8 @@ export class PackageManager { return PackageManager.NPM; case PackageManager.YARN.lockFile: return PackageManager.YARN; + case PackageManager.PNPM.lockFile: + return PackageManager.PNPM; default: return PackageManager.NPM; } diff --git a/packages/@aws-cdk/aws-lambda-nodejs/test/bundling.test.ts b/packages/@aws-cdk/aws-lambda-nodejs/test/bundling.test.ts index 7d79365da5077..5a2549e8fbfc6 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/test/bundling.test.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/test/bundling.test.ts @@ -231,6 +231,27 @@ test('Detects yarn.lock', () => { }); }); +test('Detects pnpm-lock.yaml', () => { + const pnpmLock = '/project/pnpm-lock.yaml'; + Bundling.bundle({ + entry: __filename, + depsLockFilePath: pnpmLock, + runtime: Runtime.NODEJS_12_X, + nodeModules: ['delay'], + forceDockerBundling: true, + }); + + // Correctly bundles with esbuild + expect(Code.fromAsset).toHaveBeenCalledWith(path.dirname(pnpmLock), { + assetHashType: AssetHashType.OUTPUT, + bundling: expect.objectContaining({ + command: expect.arrayContaining([ + expect.stringMatching(/pnpm-lock\.yaml.+pnpm install/), + ]), + }), + }); +}); + test('with Docker build args', () => { Bundling.bundle({ entry, diff --git a/packages/@aws-cdk/aws-lambda-nodejs/test/docker.test.ts b/packages/@aws-cdk/aws-lambda-nodejs/test/docker.test.ts index efdbd8b8aa4cc..d1b85dfcca2b8 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/test/docker.test.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/test/docker.test.ts @@ -39,6 +39,19 @@ test('can yarn install with non root user', () => { expect(proc.status).toEqual(0); }); +test('can pnpm install with non root user', () => { + const proc = spawnSync('docker', [ + 'run', '-u', '500:500', + 'esbuild', + 'bash', '-c', [ + 'mkdir /tmp/test', + 'cd /tmp/test', + 'pnpm add constructs', + ].join(' && '), + ]); + expect(proc.status).toEqual(0); +}); + test('cache folders have the right permissions', () => { const proc = spawnSync('docker', [ 'run', 'esbuild', diff --git a/packages/@aws-cdk/aws-lambda-nodejs/test/package-manager.test.ts b/packages/@aws-cdk/aws-lambda-nodejs/test/package-manager.test.ts index f561bce592f12..e0721bbec3e38 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/test/package-manager.test.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/test/package-manager.test.ts @@ -15,8 +15,15 @@ test('from a yarn.lock', () => { expect(packageManager.runBinCommand('my-bin')).toBe('yarn run my-bin'); }); -test('defaults to NPM', () => { +test('from a pnpm-lock.yaml', () => { const packageManager = PackageManager.fromLockFile('/path/to/pnpm-lock.yaml'); + expect(packageManager).toEqual(PackageManager.PNPM); + + expect(packageManager.runBinCommand('my-bin')).toBe('pnpm run my-bin'); +}); + +test('defaults to NPM', () => { + const packageManager = PackageManager.fromLockFile('/path/to/other.lock'); expect(packageManager).toEqual(PackageManager.NPM); }); From f7a397c783e1a9232f4f00778cc81e089e62396c Mon Sep 17 00:00:00 2001 From: Niranjan Jayakar Date: Thu, 20 May 2021 11:16:15 +0100 Subject: [PATCH 069/134] chore(cfnspec): update cfn-lint repo name (#14789) The repo for cfn-lint was previously cfn-python-lint. This is now changed to cfn-lint, causing the bump job to fail. Fix up the repo name. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/cfnspec/build-tools/update-cfnlint.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/cfnspec/build-tools/update-cfnlint.sh b/packages/@aws-cdk/cfnspec/build-tools/update-cfnlint.sh index 594e7dd165b87..129b3d4391923 100755 --- a/packages/@aws-cdk/cfnspec/build-tools/update-cfnlint.sh +++ b/packages/@aws-cdk/cfnspec/build-tools/update-cfnlint.sh @@ -5,14 +5,14 @@ scriptdir=$(cd $(dirname $0) && pwd) # Download (parts of) the cfn-lint repo that we use to enhance our model intermediate="$(mktemp -d)/tmp.zip" -url="https://github.com/aws-cloudformation/cfn-python-lint/archive/master.zip" -echo >&2 "Downloading from ${url}..." +url="https://github.com/aws-cloudformation/cfn-lint/archive/master.zip" +echo >&2 "Downloading from ${url} ..." curl -sSfL "${url}" -o ${intermediate} for file in StatefulResources; do echo >&2 "${file}.json" mkdir -p "spec-source/cfn-lint/${file}" - unzip -p "${intermediate}" cfn-python-lint-master/src/cfnlint/data/AdditionalSpecs/${file}.json > spec-source/cfn-lint/${file}/000.json + unzip -p "${intermediate}" cfn-lint-master/src/cfnlint/data/AdditionalSpecs/${file}.json > spec-source/cfn-lint/${file}/000.json done echo >&2 "Done." From 3a9f56d5167aab6a1bd0bf8b29b53dd8658a2313 Mon Sep 17 00:00:00 2001 From: AWS CDK Automation <43080478+aws-cdk-automation@users.noreply.github.com> Date: Thu, 20 May 2021 12:50:26 +0200 Subject: [PATCH 070/134] feat(cfnspec): cloudformation spec v36.0.0 (#14791) Co-authored-by: AWS CDK Team --- .../@aws-cdk/aws-ssmcontacts/.eslintrc.js | 3 + packages/@aws-cdk/aws-ssmcontacts/.gitignore | 19 + packages/@aws-cdk/aws-ssmcontacts/.npmignore | 28 + packages/@aws-cdk/aws-ssmcontacts/LICENSE | 201 +++ packages/@aws-cdk/aws-ssmcontacts/NOTICE | 2 + packages/@aws-cdk/aws-ssmcontacts/README.md | 20 + .../@aws-cdk/aws-ssmcontacts/jest.config.js | 2 + .../@aws-cdk/aws-ssmcontacts/lib/index.ts | 2 + .../@aws-cdk/aws-ssmcontacts/package.json | 101 ++ .../aws-ssmcontacts/test/ssmcontacts.test.ts | 6 + .../@aws-cdk/aws-ssmincidents/.eslintrc.js | 3 + packages/@aws-cdk/aws-ssmincidents/.gitignore | 19 + packages/@aws-cdk/aws-ssmincidents/.npmignore | 28 + packages/@aws-cdk/aws-ssmincidents/LICENSE | 201 +++ packages/@aws-cdk/aws-ssmincidents/NOTICE | 2 + packages/@aws-cdk/aws-ssmincidents/README.md | 20 + .../@aws-cdk/aws-ssmincidents/jest.config.js | 2 + .../@aws-cdk/aws-ssmincidents/lib/index.ts | 2 + .../@aws-cdk/aws-ssmincidents/package.json | 101 ++ .../test/ssmincidents.test.ts | 6 + packages/@aws-cdk/cfnspec/CHANGELOG.md | 67 + packages/@aws-cdk/cfnspec/cfn.version | 2 +- ...0_CloudFormationResourceSpecification.json | 1240 ++++++++++++++++- .../cloudformation-include/package.json | 4 + packages/aws-cdk-lib/package.json | 2 + packages/decdk/package.json | 2 + packages/monocdk/package.json | 2 + 27 files changed, 2072 insertions(+), 15 deletions(-) create mode 100644 packages/@aws-cdk/aws-ssmcontacts/.eslintrc.js create mode 100644 packages/@aws-cdk/aws-ssmcontacts/.gitignore create mode 100644 packages/@aws-cdk/aws-ssmcontacts/.npmignore create mode 100644 packages/@aws-cdk/aws-ssmcontacts/LICENSE create mode 100644 packages/@aws-cdk/aws-ssmcontacts/NOTICE create mode 100644 packages/@aws-cdk/aws-ssmcontacts/README.md create mode 100644 packages/@aws-cdk/aws-ssmcontacts/jest.config.js create mode 100644 packages/@aws-cdk/aws-ssmcontacts/lib/index.ts create mode 100644 packages/@aws-cdk/aws-ssmcontacts/package.json create mode 100644 packages/@aws-cdk/aws-ssmcontacts/test/ssmcontacts.test.ts create mode 100644 packages/@aws-cdk/aws-ssmincidents/.eslintrc.js create mode 100644 packages/@aws-cdk/aws-ssmincidents/.gitignore create mode 100644 packages/@aws-cdk/aws-ssmincidents/.npmignore create mode 100644 packages/@aws-cdk/aws-ssmincidents/LICENSE create mode 100644 packages/@aws-cdk/aws-ssmincidents/NOTICE create mode 100644 packages/@aws-cdk/aws-ssmincidents/README.md create mode 100644 packages/@aws-cdk/aws-ssmincidents/jest.config.js create mode 100644 packages/@aws-cdk/aws-ssmincidents/lib/index.ts create mode 100644 packages/@aws-cdk/aws-ssmincidents/package.json create mode 100644 packages/@aws-cdk/aws-ssmincidents/test/ssmincidents.test.ts diff --git a/packages/@aws-cdk/aws-ssmcontacts/.eslintrc.js b/packages/@aws-cdk/aws-ssmcontacts/.eslintrc.js new file mode 100644 index 0000000000000..61dd8dd001f63 --- /dev/null +++ b/packages/@aws-cdk/aws-ssmcontacts/.eslintrc.js @@ -0,0 +1,3 @@ +const baseConfig = require('cdk-build-tools/config/eslintrc'); +baseConfig.parserOptions.project = __dirname + '/tsconfig.json'; +module.exports = baseConfig; diff --git a/packages/@aws-cdk/aws-ssmcontacts/.gitignore b/packages/@aws-cdk/aws-ssmcontacts/.gitignore new file mode 100644 index 0000000000000..62ebc95d75ce6 --- /dev/null +++ b/packages/@aws-cdk/aws-ssmcontacts/.gitignore @@ -0,0 +1,19 @@ +*.js +*.js.map +*.d.ts +tsconfig.json +node_modules +*.generated.ts +dist +.jsii + +.LAST_BUILD +.nyc_output +coverage +.nycrc +.LAST_PACKAGE +*.snk +nyc.config.js +!.eslintrc.js +!jest.config.js +junit.xml diff --git a/packages/@aws-cdk/aws-ssmcontacts/.npmignore b/packages/@aws-cdk/aws-ssmcontacts/.npmignore new file mode 100644 index 0000000000000..e4486030fcb17 --- /dev/null +++ b/packages/@aws-cdk/aws-ssmcontacts/.npmignore @@ -0,0 +1,28 @@ +# Don't include original .ts files when doing `npm pack` +*.ts +!*.d.ts +coverage +.nyc_output +*.tgz + +dist +.LAST_PACKAGE +.LAST_BUILD +!*.js + +# Include .jsii +!.jsii + +*.snk + +*.tsbuildinfo + +tsconfig.json + +.eslintrc.js +jest.config.js + +# exclude cdk artifacts +**/cdk.out +junit.xml +test/ diff --git a/packages/@aws-cdk/aws-ssmcontacts/LICENSE b/packages/@aws-cdk/aws-ssmcontacts/LICENSE new file mode 100644 index 0000000000000..28e4bdcec77ec --- /dev/null +++ b/packages/@aws-cdk/aws-ssmcontacts/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/@aws-cdk/aws-ssmcontacts/NOTICE b/packages/@aws-cdk/aws-ssmcontacts/NOTICE new file mode 100644 index 0000000000000..5fc3826926b5b --- /dev/null +++ b/packages/@aws-cdk/aws-ssmcontacts/NOTICE @@ -0,0 +1,2 @@ +AWS Cloud Development Kit (AWS CDK) +Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/@aws-cdk/aws-ssmcontacts/README.md b/packages/@aws-cdk/aws-ssmcontacts/README.md new file mode 100644 index 0000000000000..6418d35f765af --- /dev/null +++ b/packages/@aws-cdk/aws-ssmcontacts/README.md @@ -0,0 +1,20 @@ +# AWS::SSMContacts Construct Library + + +--- + +![cfn-resources: Stable](https://img.shields.io/badge/cfn--resources-stable-success.svg?style=for-the-badge) + +> All classes with the `Cfn` prefix in this module ([CFN Resources]) are always stable and safe to use. +> +> [CFN Resources]: https://docs.aws.amazon.com/cdk/latest/guide/constructs.html#constructs_lib + +--- + + + +This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project. + +```ts +import ssmcontacts = require('@aws-cdk/aws-ssmcontacts'); +``` diff --git a/packages/@aws-cdk/aws-ssmcontacts/jest.config.js b/packages/@aws-cdk/aws-ssmcontacts/jest.config.js new file mode 100644 index 0000000000000..54e28beb9798b --- /dev/null +++ b/packages/@aws-cdk/aws-ssmcontacts/jest.config.js @@ -0,0 +1,2 @@ +const baseConfig = require('cdk-build-tools/config/jest.config'); +module.exports = baseConfig; diff --git a/packages/@aws-cdk/aws-ssmcontacts/lib/index.ts b/packages/@aws-cdk/aws-ssmcontacts/lib/index.ts new file mode 100644 index 0000000000000..9c366102b2dab --- /dev/null +++ b/packages/@aws-cdk/aws-ssmcontacts/lib/index.ts @@ -0,0 +1,2 @@ +// AWS::SSMContacts CloudFormation Resources: +export * from './ssmcontacts.generated'; diff --git a/packages/@aws-cdk/aws-ssmcontacts/package.json b/packages/@aws-cdk/aws-ssmcontacts/package.json new file mode 100644 index 0000000000000..b5e53987c4a89 --- /dev/null +++ b/packages/@aws-cdk/aws-ssmcontacts/package.json @@ -0,0 +1,101 @@ +{ + "name": "@aws-cdk/aws-ssmcontacts", + "version": "0.0.0", + "description": "The CDK Construct Library for AWS::SSMContacts", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "jsii": { + "outdir": "dist", + "projectReferences": true, + "targets": { + "dotnet": { + "namespace": "Amazon.CDK.AWS.SSMContacts", + "packageId": "Amazon.CDK.AWS.SSMContacts", + "signAssembly": true, + "assemblyOriginatorKeyFile": "../../key.snk", + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/master/logo/default-256-dark.png" + }, + "java": { + "package": "software.amazon.awscdk.services.ssmcontacts", + "maven": { + "groupId": "software.amazon.awscdk", + "artifactId": "ssmcontacts" + } + }, + "python": { + "classifiers": [ + "Framework :: AWS CDK", + "Framework :: AWS CDK :: 1" + ], + "distName": "aws-cdk.aws-ssmcontacts", + "module": "aws_cdk.aws_ssmcontacts" + } + } + }, + "repository": { + "type": "git", + "url": "https://github.com/aws/aws-cdk.git", + "directory": "packages/@aws-cdk/aws-ssmcontacts" + }, + "homepage": "https://github.com/aws/aws-cdk", + "scripts": { + "build": "cdk-build", + "watch": "cdk-watch", + "lint": "cdk-lint", + "test": "cdk-test", + "integ": "cdk-integ", + "pkglint": "pkglint -f", + "package": "cdk-package", + "awslint": "cdk-awslint", + "cfn2ts": "cfn2ts", + "build+test": "yarn build && yarn test", + "build+test+package": "yarn build+test && yarn package", + "compat": "cdk-compat", + "gen": "cfn2ts", + "rosetta:extract": "yarn --silent jsii-rosetta extract" + }, + "cdk-build": { + "cloudformation": "AWS::SSMContacts", + "jest": true, + "env": { + "AWSLINT_BASE_CONSTRUCT": "true" + } + }, + "keywords": [ + "aws", + "cdk", + "constructs", + "AWS::SSMContacts", + "aws-ssmcontacts" + ], + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com", + "organization": true + }, + "license": "Apache-2.0", + "devDependencies": { + "@types/jest": "^26.0.22", + "@aws-cdk/assert-internal": "0.0.0", + "cdk-build-tools": "0.0.0", + "cfn2ts": "0.0.0", + "pkglint": "0.0.0" + }, + "dependencies": { + "@aws-cdk/core": "0.0.0" + }, + "peerDependencies": { + "@aws-cdk/core": "0.0.0" + }, + "engines": { + "node": ">= 10.13.0 <13 || >=13.7.0" + }, + "stability": "experimental", + "maturity": "cfn-only", + "awscdkio": { + "announce": false + }, + "publishConfig": { + "tag": "latest" + } +} diff --git a/packages/@aws-cdk/aws-ssmcontacts/test/ssmcontacts.test.ts b/packages/@aws-cdk/aws-ssmcontacts/test/ssmcontacts.test.ts new file mode 100644 index 0000000000000..c4505ad966984 --- /dev/null +++ b/packages/@aws-cdk/aws-ssmcontacts/test/ssmcontacts.test.ts @@ -0,0 +1,6 @@ +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-ssmincidents/.eslintrc.js b/packages/@aws-cdk/aws-ssmincidents/.eslintrc.js new file mode 100644 index 0000000000000..61dd8dd001f63 --- /dev/null +++ b/packages/@aws-cdk/aws-ssmincidents/.eslintrc.js @@ -0,0 +1,3 @@ +const baseConfig = require('cdk-build-tools/config/eslintrc'); +baseConfig.parserOptions.project = __dirname + '/tsconfig.json'; +module.exports = baseConfig; diff --git a/packages/@aws-cdk/aws-ssmincidents/.gitignore b/packages/@aws-cdk/aws-ssmincidents/.gitignore new file mode 100644 index 0000000000000..62ebc95d75ce6 --- /dev/null +++ b/packages/@aws-cdk/aws-ssmincidents/.gitignore @@ -0,0 +1,19 @@ +*.js +*.js.map +*.d.ts +tsconfig.json +node_modules +*.generated.ts +dist +.jsii + +.LAST_BUILD +.nyc_output +coverage +.nycrc +.LAST_PACKAGE +*.snk +nyc.config.js +!.eslintrc.js +!jest.config.js +junit.xml diff --git a/packages/@aws-cdk/aws-ssmincidents/.npmignore b/packages/@aws-cdk/aws-ssmincidents/.npmignore new file mode 100644 index 0000000000000..e4486030fcb17 --- /dev/null +++ b/packages/@aws-cdk/aws-ssmincidents/.npmignore @@ -0,0 +1,28 @@ +# Don't include original .ts files when doing `npm pack` +*.ts +!*.d.ts +coverage +.nyc_output +*.tgz + +dist +.LAST_PACKAGE +.LAST_BUILD +!*.js + +# Include .jsii +!.jsii + +*.snk + +*.tsbuildinfo + +tsconfig.json + +.eslintrc.js +jest.config.js + +# exclude cdk artifacts +**/cdk.out +junit.xml +test/ diff --git a/packages/@aws-cdk/aws-ssmincidents/LICENSE b/packages/@aws-cdk/aws-ssmincidents/LICENSE new file mode 100644 index 0000000000000..28e4bdcec77ec --- /dev/null +++ b/packages/@aws-cdk/aws-ssmincidents/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/@aws-cdk/aws-ssmincidents/NOTICE b/packages/@aws-cdk/aws-ssmincidents/NOTICE new file mode 100644 index 0000000000000..5fc3826926b5b --- /dev/null +++ b/packages/@aws-cdk/aws-ssmincidents/NOTICE @@ -0,0 +1,2 @@ +AWS Cloud Development Kit (AWS CDK) +Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/@aws-cdk/aws-ssmincidents/README.md b/packages/@aws-cdk/aws-ssmincidents/README.md new file mode 100644 index 0000000000000..169151903df0d --- /dev/null +++ b/packages/@aws-cdk/aws-ssmincidents/README.md @@ -0,0 +1,20 @@ +# AWS::SSMIncidents Construct Library + + +--- + +![cfn-resources: Stable](https://img.shields.io/badge/cfn--resources-stable-success.svg?style=for-the-badge) + +> All classes with the `Cfn` prefix in this module ([CFN Resources]) are always stable and safe to use. +> +> [CFN Resources]: https://docs.aws.amazon.com/cdk/latest/guide/constructs.html#constructs_lib + +--- + + + +This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project. + +```ts +import ssmincidents = require('@aws-cdk/aws-ssmincidents'); +``` diff --git a/packages/@aws-cdk/aws-ssmincidents/jest.config.js b/packages/@aws-cdk/aws-ssmincidents/jest.config.js new file mode 100644 index 0000000000000..54e28beb9798b --- /dev/null +++ b/packages/@aws-cdk/aws-ssmincidents/jest.config.js @@ -0,0 +1,2 @@ +const baseConfig = require('cdk-build-tools/config/jest.config'); +module.exports = baseConfig; diff --git a/packages/@aws-cdk/aws-ssmincidents/lib/index.ts b/packages/@aws-cdk/aws-ssmincidents/lib/index.ts new file mode 100644 index 0000000000000..880f8e6be6b58 --- /dev/null +++ b/packages/@aws-cdk/aws-ssmincidents/lib/index.ts @@ -0,0 +1,2 @@ +// AWS::SSMIncidents CloudFormation Resources: +export * from './ssmincidents.generated'; diff --git a/packages/@aws-cdk/aws-ssmincidents/package.json b/packages/@aws-cdk/aws-ssmincidents/package.json new file mode 100644 index 0000000000000..9079c02e0b4d8 --- /dev/null +++ b/packages/@aws-cdk/aws-ssmincidents/package.json @@ -0,0 +1,101 @@ +{ + "name": "@aws-cdk/aws-ssmincidents", + "version": "0.0.0", + "description": "The CDK Construct Library for AWS::SSMIncidents", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "jsii": { + "outdir": "dist", + "projectReferences": true, + "targets": { + "dotnet": { + "namespace": "Amazon.CDK.AWS.SSMIncidents", + "packageId": "Amazon.CDK.AWS.SSMIncidents", + "signAssembly": true, + "assemblyOriginatorKeyFile": "../../key.snk", + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/master/logo/default-256-dark.png" + }, + "java": { + "package": "software.amazon.awscdk.services.ssmincidents", + "maven": { + "groupId": "software.amazon.awscdk", + "artifactId": "ssmincidents" + } + }, + "python": { + "classifiers": [ + "Framework :: AWS CDK", + "Framework :: AWS CDK :: 1" + ], + "distName": "aws-cdk.aws-ssmincidents", + "module": "aws_cdk.aws_ssmincidents" + } + } + }, + "repository": { + "type": "git", + "url": "https://github.com/aws/aws-cdk.git", + "directory": "packages/@aws-cdk/aws-ssmincidents" + }, + "homepage": "https://github.com/aws/aws-cdk", + "scripts": { + "build": "cdk-build", + "watch": "cdk-watch", + "lint": "cdk-lint", + "test": "cdk-test", + "integ": "cdk-integ", + "pkglint": "pkglint -f", + "package": "cdk-package", + "awslint": "cdk-awslint", + "cfn2ts": "cfn2ts", + "build+test": "yarn build && yarn test", + "build+test+package": "yarn build+test && yarn package", + "compat": "cdk-compat", + "gen": "cfn2ts", + "rosetta:extract": "yarn --silent jsii-rosetta extract" + }, + "cdk-build": { + "cloudformation": "AWS::SSMIncidents", + "jest": true, + "env": { + "AWSLINT_BASE_CONSTRUCT": "true" + } + }, + "keywords": [ + "aws", + "cdk", + "constructs", + "AWS::SSMIncidents", + "aws-ssmincidents" + ], + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com", + "organization": true + }, + "license": "Apache-2.0", + "devDependencies": { + "@types/jest": "^26.0.22", + "@aws-cdk/assert-internal": "0.0.0", + "cdk-build-tools": "0.0.0", + "cfn2ts": "0.0.0", + "pkglint": "0.0.0" + }, + "dependencies": { + "@aws-cdk/core": "0.0.0" + }, + "peerDependencies": { + "@aws-cdk/core": "0.0.0" + }, + "engines": { + "node": ">= 10.13.0 <13 || >=13.7.0" + }, + "stability": "experimental", + "maturity": "cfn-only", + "awscdkio": { + "announce": false + }, + "publishConfig": { + "tag": "latest" + } +} diff --git a/packages/@aws-cdk/aws-ssmincidents/test/ssmincidents.test.ts b/packages/@aws-cdk/aws-ssmincidents/test/ssmincidents.test.ts new file mode 100644 index 0000000000000..c4505ad966984 --- /dev/null +++ b/packages/@aws-cdk/aws-ssmincidents/test/ssmincidents.test.ts @@ -0,0 +1,6 @@ +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/cfnspec/CHANGELOG.md b/packages/@aws-cdk/cfnspec/CHANGELOG.md index df0747a8e2705..2a8a6cbc0f9a1 100644 --- a/packages/@aws-cdk/cfnspec/CHANGELOG.md +++ b/packages/@aws-cdk/cfnspec/CHANGELOG.md @@ -1,3 +1,70 @@ +# CloudFormation Resource Specification v36.0.0 + +## New Resource Types + +* AWS::DynamoDB::GlobalTable +* AWS::SSMContacts::Contact +* AWS::SSMContacts::ContactChannel +* AWS::SSMIncidents::ReplicationSet +* AWS::SSMIncidents::ResponsePlan + +## Attribute Changes + +* AWS::ApiGateway::RequestValidator RequestValidatorId (__added__) + +## Property Changes + +* AWS::CloudFormation::StackSet CallAs (__added__) +* AWS::CustomerProfiles::Integration FlowDefinition (__added__) +* AWS::CustomerProfiles::Integration ObjectTypeName.Required (__changed__) + * Old: false + * New: true +* AWS::ECS::TaskDefinition EphemeralStorage.UpdateType (__changed__) + * Old: Mutable + * New: Immutable +* AWS::Elasticsearch::Domain EncryptionAtRestOptions.UpdateType (__changed__) + * Old: Immutable + * New: Conditional +* AWS::Elasticsearch::Domain NodeToNodeEncryptionOptions.UpdateType (__changed__) + * Old: Immutable + * New: Conditional +* AWS::Lambda::EventSourceMapping PartialBatchResponse (__deleted__) +* AWS::Lambda::LayerVersion CompatibleArchitectures (__added__) +* AWS::SSM::Association CalendarNames (__added__) + +## Property Type Changes + +* AWS::AppFlow::Flow.ZendeskDestinationProperties (__added__) +* AWS::CustomerProfiles::Integration.ConnectorOperator (__added__) +* AWS::CustomerProfiles::Integration.FlowDefinition (__added__) +* AWS::CustomerProfiles::Integration.IncrementalPullConfig (__added__) +* AWS::CustomerProfiles::Integration.MarketoSourceProperties (__added__) +* AWS::CustomerProfiles::Integration.S3SourceProperties (__added__) +* AWS::CustomerProfiles::Integration.SalesforceSourceProperties (__added__) +* AWS::CustomerProfiles::Integration.ScheduledTriggerProperties (__added__) +* AWS::CustomerProfiles::Integration.ServiceNowSourceProperties (__added__) +* AWS::CustomerProfiles::Integration.SourceConnectorProperties (__added__) +* AWS::CustomerProfiles::Integration.SourceFlowConfig (__added__) +* AWS::CustomerProfiles::Integration.Task (__added__) +* AWS::CustomerProfiles::Integration.TaskPropertiesMap (__added__) +* AWS::CustomerProfiles::Integration.TriggerConfig (__added__) +* AWS::CustomerProfiles::Integration.TriggerProperties (__added__) +* AWS::CustomerProfiles::Integration.ZendeskSourceProperties (__added__) +* AWS::AppFlow::Flow.DestinationConnectorProperties Zendesk (__added__) +* AWS::CloudFront::Distribution.Origin OriginShield.Type (__added__) +* AWS::ECS::CapacityProvider.ManagedScaling InstanceWarmupPeriod (__added__) +* AWS::ECS::TaskDefinition.EphemeralStorage SizeInGiB.UpdateType (__changed__) + * Old: Mutable + * New: Immutable +* AWS::Elasticsearch::Domain.EncryptionAtRestOptions Enabled.UpdateType (__changed__) + * Old: Immutable + * New: Conditional +* AWS::Elasticsearch::Domain.NodeToNodeEncryptionOptions Enabled.UpdateType (__changed__) + * Old: Immutable + * New: Conditional +* AWS::S3::Bucket.Rule ExpiredObjectDeleteMarker (__added__) + + # CloudFormation Resource Specification v35.2.0 ## New Resource Types diff --git a/packages/@aws-cdk/cfnspec/cfn.version b/packages/@aws-cdk/cfnspec/cfn.version index 6ee5ed10b7ff5..2b8c161364217 100644 --- a/packages/@aws-cdk/cfnspec/cfn.version +++ b/packages/@aws-cdk/cfnspec/cfn.version @@ -1 +1 @@ -35.2.0 +36.0.0 diff --git a/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json b/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json index 61926760268cd..5a9f29a6bc518 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json +++ b/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json @@ -3287,6 +3287,12 @@ "Required": false, "Type": "UpsolverDestinationProperties", "UpdateType": "Mutable" + }, + "Zendesk": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appflow-flow-destinationconnectorproperties.html#cfn-appflow-flow-destinationconnectorproperties-zendesk", + "Required": false, + "Type": "ZendeskDestinationProperties", + "UpdateType": "Mutable" } } }, @@ -3935,6 +3941,36 @@ } } }, + "AWS::AppFlow::Flow.ZendeskDestinationProperties": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appflow-flow-zendeskdestinationproperties.html", + "Properties": { + "ErrorHandlingConfig": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appflow-flow-zendeskdestinationproperties.html#cfn-appflow-flow-zendeskdestinationproperties-errorhandlingconfig", + "Required": false, + "Type": "ErrorHandlingConfig", + "UpdateType": "Mutable" + }, + "IdFieldNames": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appflow-flow-zendeskdestinationproperties.html#cfn-appflow-flow-zendeskdestinationproperties-idfieldnames", + "PrimitiveItemType": "String", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "Object": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appflow-flow-zendeskdestinationproperties.html#cfn-appflow-flow-zendeskdestinationproperties-object", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "WriteOperationType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appflow-flow-zendeskdestinationproperties.html#cfn-appflow-flow-zendeskdestinationproperties-writeoperationtype", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, "AWS::AppFlow::Flow.ZendeskSourceProperties": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appflow-flow-zendesksourceproperties.html", "Properties": { @@ -10640,6 +10676,7 @@ "OriginShield": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-origin.html#cfn-cloudfront-distribution-origin-originshield", "Required": false, + "Type": "OriginShield", "UpdateType": "Mutable" }, "S3OriginConfig": { @@ -14073,6 +14110,360 @@ } } }, + "AWS::CustomerProfiles::Integration.ConnectorOperator": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-connectoroperator.html", + "Properties": { + "Marketo": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-connectoroperator.html#cfn-customerprofiles-integration-connectoroperator-marketo", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "S3": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-connectoroperator.html#cfn-customerprofiles-integration-connectoroperator-s3", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Salesforce": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-connectoroperator.html#cfn-customerprofiles-integration-connectoroperator-salesforce", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "ServiceNow": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-connectoroperator.html#cfn-customerprofiles-integration-connectoroperator-servicenow", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Zendesk": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-connectoroperator.html#cfn-customerprofiles-integration-connectoroperator-zendesk", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::CustomerProfiles::Integration.FlowDefinition": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-flowdefinition.html", + "Properties": { + "Description": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-flowdefinition.html#cfn-customerprofiles-integration-flowdefinition-description", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "FlowName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-flowdefinition.html#cfn-customerprofiles-integration-flowdefinition-flowname", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "KmsArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-flowdefinition.html#cfn-customerprofiles-integration-flowdefinition-kmsarn", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "SourceFlowConfig": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-flowdefinition.html#cfn-customerprofiles-integration-flowdefinition-sourceflowconfig", + "Required": true, + "Type": "SourceFlowConfig", + "UpdateType": "Mutable" + }, + "Tasks": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-flowdefinition.html#cfn-customerprofiles-integration-flowdefinition-tasks", + "ItemType": "Task", + "Required": true, + "Type": "List", + "UpdateType": "Mutable" + }, + "TriggerConfig": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-flowdefinition.html#cfn-customerprofiles-integration-flowdefinition-triggerconfig", + "Required": true, + "Type": "TriggerConfig", + "UpdateType": "Mutable" + } + } + }, + "AWS::CustomerProfiles::Integration.IncrementalPullConfig": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-incrementalpullconfig.html", + "Properties": { + "DatetimeTypeFieldName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-incrementalpullconfig.html#cfn-customerprofiles-integration-incrementalpullconfig-datetimetypefieldname", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::CustomerProfiles::Integration.MarketoSourceProperties": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-marketosourceproperties.html", + "Properties": { + "Object": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-marketosourceproperties.html#cfn-customerprofiles-integration-marketosourceproperties-object", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::CustomerProfiles::Integration.S3SourceProperties": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-s3sourceproperties.html", + "Properties": { + "BucketName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-s3sourceproperties.html#cfn-customerprofiles-integration-s3sourceproperties-bucketname", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "BucketPrefix": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-s3sourceproperties.html#cfn-customerprofiles-integration-s3sourceproperties-bucketprefix", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::CustomerProfiles::Integration.SalesforceSourceProperties": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-salesforcesourceproperties.html", + "Properties": { + "EnableDynamicFieldUpdate": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-salesforcesourceproperties.html#cfn-customerprofiles-integration-salesforcesourceproperties-enabledynamicfieldupdate", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "IncludeDeletedRecords": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-salesforcesourceproperties.html#cfn-customerprofiles-integration-salesforcesourceproperties-includedeletedrecords", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "Object": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-salesforcesourceproperties.html#cfn-customerprofiles-integration-salesforcesourceproperties-object", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::CustomerProfiles::Integration.ScheduledTriggerProperties": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-scheduledtriggerproperties.html", + "Properties": { + "DataPullMode": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-scheduledtriggerproperties.html#cfn-customerprofiles-integration-scheduledtriggerproperties-datapullmode", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "FirstExecutionFrom": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-scheduledtriggerproperties.html#cfn-customerprofiles-integration-scheduledtriggerproperties-firstexecutionfrom", + "PrimitiveType": "Double", + "Required": false, + "UpdateType": "Mutable" + }, + "ScheduleEndTime": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-scheduledtriggerproperties.html#cfn-customerprofiles-integration-scheduledtriggerproperties-scheduleendtime", + "PrimitiveType": "Double", + "Required": false, + "UpdateType": "Mutable" + }, + "ScheduleExpression": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-scheduledtriggerproperties.html#cfn-customerprofiles-integration-scheduledtriggerproperties-scheduleexpression", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "ScheduleOffset": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-scheduledtriggerproperties.html#cfn-customerprofiles-integration-scheduledtriggerproperties-scheduleoffset", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "ScheduleStartTime": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-scheduledtriggerproperties.html#cfn-customerprofiles-integration-scheduledtriggerproperties-schedulestarttime", + "PrimitiveType": "Double", + "Required": false, + "UpdateType": "Mutable" + }, + "Timezone": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-scheduledtriggerproperties.html#cfn-customerprofiles-integration-scheduledtriggerproperties-timezone", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::CustomerProfiles::Integration.ServiceNowSourceProperties": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-servicenowsourceproperties.html", + "Properties": { + "Object": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-servicenowsourceproperties.html#cfn-customerprofiles-integration-servicenowsourceproperties-object", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::CustomerProfiles::Integration.SourceConnectorProperties": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-sourceconnectorproperties.html", + "Properties": { + "Marketo": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-sourceconnectorproperties.html#cfn-customerprofiles-integration-sourceconnectorproperties-marketo", + "Required": false, + "Type": "MarketoSourceProperties", + "UpdateType": "Mutable" + }, + "S3": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-sourceconnectorproperties.html#cfn-customerprofiles-integration-sourceconnectorproperties-s3", + "Required": false, + "Type": "S3SourceProperties", + "UpdateType": "Mutable" + }, + "Salesforce": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-sourceconnectorproperties.html#cfn-customerprofiles-integration-sourceconnectorproperties-salesforce", + "Required": false, + "Type": "SalesforceSourceProperties", + "UpdateType": "Mutable" + }, + "ServiceNow": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-sourceconnectorproperties.html#cfn-customerprofiles-integration-sourceconnectorproperties-servicenow", + "Required": false, + "Type": "ServiceNowSourceProperties", + "UpdateType": "Mutable" + }, + "Zendesk": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-sourceconnectorproperties.html#cfn-customerprofiles-integration-sourceconnectorproperties-zendesk", + "Required": false, + "Type": "ZendeskSourceProperties", + "UpdateType": "Mutable" + } + } + }, + "AWS::CustomerProfiles::Integration.SourceFlowConfig": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-sourceflowconfig.html", + "Properties": { + "ConnectorProfileName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-sourceflowconfig.html#cfn-customerprofiles-integration-sourceflowconfig-connectorprofilename", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "ConnectorType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-sourceflowconfig.html#cfn-customerprofiles-integration-sourceflowconfig-connectortype", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "IncrementalPullConfig": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-sourceflowconfig.html#cfn-customerprofiles-integration-sourceflowconfig-incrementalpullconfig", + "Required": false, + "Type": "IncrementalPullConfig", + "UpdateType": "Mutable" + }, + "SourceConnectorProperties": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-sourceflowconfig.html#cfn-customerprofiles-integration-sourceflowconfig-sourceconnectorproperties", + "Required": true, + "Type": "SourceConnectorProperties", + "UpdateType": "Mutable" + } + } + }, + "AWS::CustomerProfiles::Integration.Task": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-task.html", + "Properties": { + "ConnectorOperator": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-task.html#cfn-customerprofiles-integration-task-connectoroperator", + "Required": false, + "Type": "ConnectorOperator", + "UpdateType": "Mutable" + }, + "DestinationField": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-task.html#cfn-customerprofiles-integration-task-destinationfield", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "SourceFields": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-task.html#cfn-customerprofiles-integration-task-sourcefields", + "PrimitiveItemType": "String", + "Required": true, + "Type": "List", + "UpdateType": "Mutable" + }, + "TaskProperties": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-task.html#cfn-customerprofiles-integration-task-taskproperties", + "ItemType": "TaskPropertiesMap", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "TaskType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-task.html#cfn-customerprofiles-integration-task-tasktype", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::CustomerProfiles::Integration.TaskPropertiesMap": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-taskpropertiesmap.html", + "Properties": { + "OperatorPropertyKey": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-taskpropertiesmap.html#cfn-customerprofiles-integration-taskpropertiesmap-operatorpropertykey", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "Property": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-taskpropertiesmap.html#cfn-customerprofiles-integration-taskpropertiesmap-property", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::CustomerProfiles::Integration.TriggerConfig": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-triggerconfig.html", + "Properties": { + "TriggerProperties": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-triggerconfig.html#cfn-customerprofiles-integration-triggerconfig-triggerproperties", + "Required": false, + "Type": "TriggerProperties", + "UpdateType": "Mutable" + }, + "TriggerType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-triggerconfig.html#cfn-customerprofiles-integration-triggerconfig-triggertype", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::CustomerProfiles::Integration.TriggerProperties": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-triggerproperties.html", + "Properties": { + "Scheduled": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-triggerproperties.html#cfn-customerprofiles-integration-triggerproperties-scheduled", + "Required": false, + "Type": "ScheduledTriggerProperties", + "UpdateType": "Mutable" + } + } + }, + "AWS::CustomerProfiles::Integration.ZendeskSourceProperties": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-zendesksourceproperties.html", + "Properties": { + "Object": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-integration-zendesksourceproperties.html#cfn-customerprofiles-integration-zendesksourceproperties-object", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, "AWS::CustomerProfiles::ObjectType.FieldMap": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-customerprofiles-objecttype-fieldmap.html", "Properties": { @@ -16559,6 +16950,353 @@ } } }, + "AWS::DynamoDB::GlobalTable.AttributeDefinition": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-attributedefinition.html", + "Properties": { + "AttributeName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-attributedefinition.html#cfn-dynamodb-globaltable-attributedefinition-attributename", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "AttributeType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-attributedefinition.html#cfn-dynamodb-globaltable-attributedefinition-attributetype", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::DynamoDB::GlobalTable.CapacityAutoScalingSettings": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-capacityautoscalingsettings.html", + "Properties": { + "MaxCapacity": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-capacityautoscalingsettings.html#cfn-dynamodb-globaltable-capacityautoscalingsettings-maxcapacity", + "PrimitiveType": "Integer", + "Required": true, + "UpdateType": "Mutable" + }, + "MinCapacity": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-capacityautoscalingsettings.html#cfn-dynamodb-globaltable-capacityautoscalingsettings-mincapacity", + "PrimitiveType": "Integer", + "Required": true, + "UpdateType": "Mutable" + }, + "SeedCapacity": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-capacityautoscalingsettings.html#cfn-dynamodb-globaltable-capacityautoscalingsettings-seedcapacity", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "TargetTrackingScalingPolicyConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-capacityautoscalingsettings.html#cfn-dynamodb-globaltable-capacityautoscalingsettings-targettrackingscalingpolicyconfiguration", + "Required": true, + "Type": "TargetTrackingScalingPolicyConfiguration", + "UpdateType": "Mutable" + } + } + }, + "AWS::DynamoDB::GlobalTable.ContributorInsightsSpecification": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-contributorinsightsspecification.html", + "Properties": { + "Enabled": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-contributorinsightsspecification.html#cfn-dynamodb-globaltable-contributorinsightsspecification-enabled", + "PrimitiveType": "Boolean", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::DynamoDB::GlobalTable.GlobalSecondaryIndex": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-globalsecondaryindex.html", + "Properties": { + "IndexName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-globalsecondaryindex.html#cfn-dynamodb-globaltable-globalsecondaryindex-indexname", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "KeySchema": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-globalsecondaryindex.html#cfn-dynamodb-globaltable-globalsecondaryindex-keyschema", + "DuplicatesAllowed": false, + "ItemType": "KeySchema", + "Required": true, + "Type": "List", + "UpdateType": "Mutable" + }, + "Projection": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-globalsecondaryindex.html#cfn-dynamodb-globaltable-globalsecondaryindex-projection", + "Required": true, + "Type": "Projection", + "UpdateType": "Mutable" + }, + "WriteProvisionedThroughputSettings": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-globalsecondaryindex.html#cfn-dynamodb-globaltable-globalsecondaryindex-writeprovisionedthroughputsettings", + "Required": false, + "Type": "WriteProvisionedThroughputSettings", + "UpdateType": "Mutable" + } + } + }, + "AWS::DynamoDB::GlobalTable.KeySchema": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-keyschema.html", + "Properties": { + "AttributeName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-keyschema.html#cfn-dynamodb-globaltable-keyschema-attributename", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "KeyType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-keyschema.html#cfn-dynamodb-globaltable-keyschema-keytype", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::DynamoDB::GlobalTable.LocalSecondaryIndex": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-localsecondaryindex.html", + "Properties": { + "IndexName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-localsecondaryindex.html#cfn-dynamodb-globaltable-localsecondaryindex-indexname", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "KeySchema": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-localsecondaryindex.html#cfn-dynamodb-globaltable-localsecondaryindex-keyschema", + "DuplicatesAllowed": false, + "ItemType": "KeySchema", + "Required": true, + "Type": "List", + "UpdateType": "Immutable" + }, + "Projection": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-localsecondaryindex.html#cfn-dynamodb-globaltable-localsecondaryindex-projection", + "Required": true, + "Type": "Projection", + "UpdateType": "Immutable" + } + } + }, + "AWS::DynamoDB::GlobalTable.PointInTimeRecoverySpecification": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-pointintimerecoveryspecification.html", + "Properties": { + "PointInTimeRecoveryEnabled": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-pointintimerecoveryspecification.html#cfn-dynamodb-globaltable-pointintimerecoveryspecification-pointintimerecoveryenabled", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::DynamoDB::GlobalTable.Projection": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-projection.html", + "Properties": { + "NonKeyAttributes": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-projection.html#cfn-dynamodb-globaltable-projection-nonkeyattributes", + "DuplicatesAllowed": false, + "PrimitiveItemType": "String", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "ProjectionType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-projection.html#cfn-dynamodb-globaltable-projection-projectiontype", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::DynamoDB::GlobalTable.ReadProvisionedThroughputSettings": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-readprovisionedthroughputsettings.html", + "Properties": { + "ReadCapacityAutoScalingSettings": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-readprovisionedthroughputsettings.html#cfn-dynamodb-globaltable-readprovisionedthroughputsettings-readcapacityautoscalingsettings", + "Required": false, + "Type": "CapacityAutoScalingSettings", + "UpdateType": "Mutable" + }, + "ReadCapacityUnits": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-readprovisionedthroughputsettings.html#cfn-dynamodb-globaltable-readprovisionedthroughputsettings-readcapacityunits", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::DynamoDB::GlobalTable.ReplicaGlobalSecondaryIndexSpecification": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-replicaglobalsecondaryindexspecification.html", + "Properties": { + "ContributorInsightsSpecification": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-replicaglobalsecondaryindexspecification.html#cfn-dynamodb-globaltable-replicaglobalsecondaryindexspecification-contributorinsightsspecification", + "Required": false, + "Type": "ContributorInsightsSpecification", + "UpdateType": "Mutable" + }, + "IndexName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-replicaglobalsecondaryindexspecification.html#cfn-dynamodb-globaltable-replicaglobalsecondaryindexspecification-indexname", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "ReadProvisionedThroughputSettings": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-replicaglobalsecondaryindexspecification.html#cfn-dynamodb-globaltable-replicaglobalsecondaryindexspecification-readprovisionedthroughputsettings", + "Required": false, + "Type": "ReadProvisionedThroughputSettings", + "UpdateType": "Mutable" + } + } + }, + "AWS::DynamoDB::GlobalTable.ReplicaSSESpecification": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-replicassespecification.html", + "Properties": { + "KMSMasterKeyId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-replicassespecification.html#cfn-dynamodb-globaltable-replicassespecification-kmsmasterkeyid", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::DynamoDB::GlobalTable.ReplicaSpecification": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-replicaspecification.html", + "Properties": { + "ContributorInsightsSpecification": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-replicaspecification.html#cfn-dynamodb-globaltable-replicaspecification-contributorinsightsspecification", + "Required": false, + "Type": "ContributorInsightsSpecification", + "UpdateType": "Mutable" + }, + "GlobalSecondaryIndexes": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-replicaspecification.html#cfn-dynamodb-globaltable-replicaspecification-globalsecondaryindexes", + "DuplicatesAllowed": false, + "ItemType": "ReplicaGlobalSecondaryIndexSpecification", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "PointInTimeRecoverySpecification": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-replicaspecification.html#cfn-dynamodb-globaltable-replicaspecification-pointintimerecoveryspecification", + "Required": false, + "Type": "PointInTimeRecoverySpecification", + "UpdateType": "Mutable" + }, + "ReadProvisionedThroughputSettings": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-replicaspecification.html#cfn-dynamodb-globaltable-replicaspecification-readprovisionedthroughputsettings", + "Required": false, + "Type": "ReadProvisionedThroughputSettings", + "UpdateType": "Mutable" + }, + "Region": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-replicaspecification.html#cfn-dynamodb-globaltable-replicaspecification-region", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "SSESpecification": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-replicaspecification.html#cfn-dynamodb-globaltable-replicaspecification-ssespecification", + "Required": false, + "Type": "ReplicaSSESpecification", + "UpdateType": "Mutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-replicaspecification.html#cfn-dynamodb-globaltable-replicaspecification-tags", + "DuplicatesAllowed": false, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, + "AWS::DynamoDB::GlobalTable.SSESpecification": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-ssespecification.html", + "Properties": { + "SSEEnabled": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-ssespecification.html#cfn-dynamodb-globaltable-ssespecification-sseenabled", + "PrimitiveType": "Boolean", + "Required": true, + "UpdateType": "Mutable" + }, + "SSEType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-ssespecification.html#cfn-dynamodb-globaltable-ssespecification-ssetype", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::DynamoDB::GlobalTable.StreamSpecification": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-streamspecification.html", + "Properties": { + "StreamViewType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-streamspecification.html#cfn-dynamodb-globaltable-streamspecification-streamviewtype", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::DynamoDB::GlobalTable.TargetTrackingScalingPolicyConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-targettrackingscalingpolicyconfiguration.html", + "Properties": { + "DisableScaleIn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-targettrackingscalingpolicyconfiguration.html#cfn-dynamodb-globaltable-targettrackingscalingpolicyconfiguration-disablescalein", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "ScaleInCooldown": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-targettrackingscalingpolicyconfiguration.html#cfn-dynamodb-globaltable-targettrackingscalingpolicyconfiguration-scaleincooldown", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "ScaleOutCooldown": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-targettrackingscalingpolicyconfiguration.html#cfn-dynamodb-globaltable-targettrackingscalingpolicyconfiguration-scaleoutcooldown", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "TargetValue": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-targettrackingscalingpolicyconfiguration.html#cfn-dynamodb-globaltable-targettrackingscalingpolicyconfiguration-targetvalue", + "PrimitiveType": "Double", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::DynamoDB::GlobalTable.TimeToLiveSpecification": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-timetolivespecification.html", + "Properties": { + "AttributeName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-timetolivespecification.html#cfn-dynamodb-globaltable-timetolivespecification-attributename", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Enabled": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-timetolivespecification.html#cfn-dynamodb-globaltable-timetolivespecification-enabled", + "PrimitiveType": "Boolean", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::DynamoDB::GlobalTable.WriteProvisionedThroughputSettings": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-writeprovisionedthroughputsettings.html", + "Properties": { + "WriteCapacityAutoScalingSettings": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-writeprovisionedthroughputsettings.html#cfn-dynamodb-globaltable-writeprovisionedthroughputsettings-writecapacityautoscalingsettings", + "Required": false, + "Type": "CapacityAutoScalingSettings", + "UpdateType": "Mutable" + } + } + }, "AWS::DynamoDB::Table.AttributeDefinition": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-attributedef.html", "Properties": { @@ -19888,6 +20626,12 @@ "AWS::ECS::CapacityProvider.ManagedScaling": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-capacityprovider-managedscaling.html", "Properties": { + "InstanceWarmupPeriod": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-capacityprovider-managedscaling.html#cfn-ecs-capacityprovider-managedscaling-instancewarmupperiod", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, "MaximumScalingStepSize": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-capacityprovider-managedscaling.html#cfn-ecs-capacityprovider-managedscaling-maximumscalingstepsize", "PrimitiveType": "Integer", @@ -20666,7 +21410,7 @@ "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-taskdefinition-ephemeralstorage.html#cfn-ecs-taskdefinition-ephemeralstorage-sizeingib", "PrimitiveType": "Integer", "Required": false, - "UpdateType": "Mutable" + "UpdateType": "Immutable" } } }, @@ -24620,7 +25364,7 @@ "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticsearch-domain-encryptionatrestoptions.html#cfn-elasticsearch-domain-encryptionatrestoptions-enabled", "PrimitiveType": "Boolean", "Required": false, - "UpdateType": "Immutable" + "UpdateType": "Conditional" }, "KmsKeyId": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticsearch-domain-encryptionatrestoptions.html#cfn-elasticsearch-domain-encryptionatrestoptions-kmskeyid", @@ -24677,7 +25421,7 @@ "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticsearch-domain-nodetonodeencryptionoptions.html#cfn-elasticsearch-domain-nodetonodeencryptionoptions-enabled", "PrimitiveType": "Boolean", "Required": false, - "UpdateType": "Immutable" + "UpdateType": "Conditional" } } }, @@ -51590,6 +52334,12 @@ "Required": false, "UpdateType": "Mutable" }, + "ExpiredObjectDeleteMarker": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-bucket-lifecycleconfig-rule.html#cfn-s3-bucket-rule-expiredobjectdeletemarker", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, "Id": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-bucket-lifecycleconfig-rule.html#cfn-s3-bucket-lifecycleconfig-rule-id", "PrimitiveType": "String", @@ -53187,6 +53937,195 @@ } } }, + "AWS::SSMContacts::Contact.Stage": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmcontacts-contact-stage.html", + "Properties": { + "DurationInMinutes": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmcontacts-contact-stage.html#cfn-ssmcontacts-contact-stage-durationinminutes", + "PrimitiveType": "Integer", + "Required": true, + "UpdateType": "Mutable" + }, + "Targets": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmcontacts-contact-stage.html#cfn-ssmcontacts-contact-stage-targets", + "ItemType": "Targets", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, + "AWS::SSMContacts::Contact.Targets": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmcontacts-contact-targets.html", + "Properties": { + "ChannelTargetInfo": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmcontacts-contact-targets.html#cfn-ssmcontacts-contact-targets-channeltargetinfo", + "PrimitiveType": "Json", + "Required": false, + "UpdateType": "Mutable" + }, + "ContactTargetInfo": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmcontacts-contact-targets.html#cfn-ssmcontacts-contact-targets-contacttargetinfo", + "PrimitiveType": "Json", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::SSMIncidents::ReplicationSet.RegionConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-replicationset-regionconfiguration.html", + "Properties": { + "SseKmsKeyId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-replicationset-regionconfiguration.html#cfn-ssmincidents-replicationset-regionconfiguration-ssekmskeyid", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::SSMIncidents::ReplicationSet.ReplicationRegion": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-replicationset-replicationregion.html", + "Properties": { + "RegionConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-replicationset-replicationregion.html#cfn-ssmincidents-replicationset-replicationregion-regionconfiguration", + "Required": false, + "Type": "RegionConfiguration", + "UpdateType": "Mutable" + }, + "RegionName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-replicationset-replicationregion.html#cfn-ssmincidents-replicationset-replicationregion-regionname", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::SSMIncidents::ResponsePlan.Action": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-responseplan-action.html", + "Properties": { + "SsmAutomation": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-responseplan-action.html#cfn-ssmincidents-responseplan-action-ssmautomation", + "Required": false, + "Type": "SsmAutomation", + "UpdateType": "Mutable" + } + } + }, + "AWS::SSMIncidents::ResponsePlan.ChatChannel": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-responseplan-chatchannel.html", + "Properties": { + "ChatbotSns": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-responseplan-chatchannel.html#cfn-ssmincidents-responseplan-chatchannel-chatbotsns", + "PrimitiveItemType": "String", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, + "AWS::SSMIncidents::ResponsePlan.IncidentTemplate": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-responseplan-incidenttemplate.html", + "Properties": { + "DedupeString": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-responseplan-incidenttemplate.html#cfn-ssmincidents-responseplan-incidenttemplate-dedupestring", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Impact": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-responseplan-incidenttemplate.html#cfn-ssmincidents-responseplan-incidenttemplate-impact", + "PrimitiveType": "Integer", + "Required": true, + "UpdateType": "Mutable" + }, + "NotificationTargets": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-responseplan-incidenttemplate.html#cfn-ssmincidents-responseplan-incidenttemplate-notificationtargets", + "ItemType": "NotificationTargetItem", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "Summary": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-responseplan-incidenttemplate.html#cfn-ssmincidents-responseplan-incidenttemplate-summary", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Title": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-responseplan-incidenttemplate.html#cfn-ssmincidents-responseplan-incidenttemplate-title", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::SSMIncidents::ResponsePlan.NotificationTargetItem": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-responseplan-notificationtargetitem.html", + "Properties": { + "SnsTopicArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-responseplan-notificationtargetitem.html#cfn-ssmincidents-responseplan-notificationtargetitem-snstopicarn", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::SSMIncidents::ResponsePlan.SsmAutomation": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-responseplan-ssmautomation.html", + "Properties": { + "DocumentName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-responseplan-ssmautomation.html#cfn-ssmincidents-responseplan-ssmautomation-documentname", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "DocumentVersion": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-responseplan-ssmautomation.html#cfn-ssmincidents-responseplan-ssmautomation-documentversion", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Parameters": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-responseplan-ssmautomation.html#cfn-ssmincidents-responseplan-ssmautomation-parameters", + "DuplicatesAllowed": false, + "ItemType": "SsmParameter", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "RoleArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-responseplan-ssmautomation.html#cfn-ssmincidents-responseplan-ssmautomation-rolearn", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "TargetAccount": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-responseplan-ssmautomation.html#cfn-ssmincidents-responseplan-ssmautomation-targetaccount", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::SSMIncidents::ResponsePlan.SsmParameter": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-responseplan-ssmparameter.html", + "Properties": { + "Key": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-responseplan-ssmparameter.html#cfn-ssmincidents-responseplan-ssmparameter-key", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "Values": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ssmincidents-responseplan-ssmparameter.html#cfn-ssmincidents-responseplan-ssmparameter-values", + "DuplicatesAllowed": false, + "PrimitiveItemType": "String", + "Required": true, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, "AWS::SSO::InstanceAccessControlAttributeConfiguration.AccessControlAttribute": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sso-instanceaccesscontrolattributeconfiguration-accesscontrolattribute.html", "Properties": { @@ -58516,7 +59455,7 @@ } } }, - "ResourceSpecificationVersion": "35.2.0", + "ResourceSpecificationVersion": "36.0.0", "ResourceTypes": { "AWS::ACMPCA::Certificate": { "Attributes": { @@ -59699,6 +60638,11 @@ } }, "AWS::ApiGateway::RequestValidator": { + "Attributes": { + "RequestValidatorId": { + "PrimitiveType": "String" + } + }, "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-requestvalidator.html", "Properties": { "Name": { @@ -64252,6 +65196,12 @@ "Type": "AutoDeployment", "UpdateType": "Mutable" }, + "CallAs": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudformation-stackset.html#cfn-cloudformation-stackset-callas", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, "Capabilities": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudformation-stackset.html#cfn-cloudformation-stackset-capabilities", "DuplicatesAllowed": false, @@ -67089,10 +68039,16 @@ "Required": true, "UpdateType": "Immutable" }, + "FlowDefinition": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-customerprofiles-integration.html#cfn-customerprofiles-integration-flowdefinition", + "Required": false, + "Type": "FlowDefinition", + "UpdateType": "Mutable" + }, "ObjectTypeName": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-customerprofiles-integration.html#cfn-customerprofiles-integration-objecttypename", "PrimitiveType": "String", - "Required": false, + "Required": true, "UpdateType": "Mutable" }, "Tags": { @@ -69066,6 +70022,98 @@ } } }, + "AWS::DynamoDB::GlobalTable": { + "Attributes": { + "Arn": { + "PrimitiveType": "String" + }, + "StreamArn": { + "PrimitiveType": "String" + }, + "TableId": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-globaltable.html", + "Properties": { + "AttributeDefinitions": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-globaltable.html#cfn-dynamodb-globaltable-attributedefinitions", + "DuplicatesAllowed": false, + "ItemType": "AttributeDefinition", + "Required": true, + "Type": "List", + "UpdateType": "Mutable" + }, + "BillingMode": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-globaltable.html#cfn-dynamodb-globaltable-billingmode", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "GlobalSecondaryIndexes": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-globaltable.html#cfn-dynamodb-globaltable-globalsecondaryindexes", + "DuplicatesAllowed": false, + "ItemType": "GlobalSecondaryIndex", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "KeySchema": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-globaltable.html#cfn-dynamodb-globaltable-keyschema", + "DuplicatesAllowed": false, + "ItemType": "KeySchema", + "Required": true, + "Type": "List", + "UpdateType": "Immutable" + }, + "LocalSecondaryIndexes": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-globaltable.html#cfn-dynamodb-globaltable-localsecondaryindexes", + "DuplicatesAllowed": false, + "ItemType": "LocalSecondaryIndex", + "Required": false, + "Type": "List", + "UpdateType": "Immutable" + }, + "Replicas": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-globaltable.html#cfn-dynamodb-globaltable-replicas", + "DuplicatesAllowed": false, + "ItemType": "ReplicaSpecification", + "Required": true, + "Type": "List", + "UpdateType": "Mutable" + }, + "SSESpecification": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-globaltable.html#cfn-dynamodb-globaltable-ssespecification", + "Required": false, + "Type": "SSESpecification", + "UpdateType": "Mutable" + }, + "StreamSpecification": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-globaltable.html#cfn-dynamodb-globaltable-streamspecification", + "Required": false, + "Type": "StreamSpecification", + "UpdateType": "Mutable" + }, + "TableName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-globaltable.html#cfn-dynamodb-globaltable-tablename", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "TimeToLiveSpecification": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-globaltable.html#cfn-dynamodb-globaltable-timetolivespecification", + "Required": false, + "Type": "TimeToLiveSpecification", + "UpdateType": "Mutable" + }, + "WriteProvisionedThroughputSettings": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-globaltable.html#cfn-dynamodb-globaltable-writeprovisionedthroughputsettings", + "Required": false, + "Type": "WriteProvisionedThroughputSettings", + "UpdateType": "Mutable" + } + } + }, "AWS::DynamoDB::Table": { "Attributes": { "Arn": { @@ -72588,7 +73636,7 @@ "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-taskdefinition.html#cfn-ecs-taskdefinition-ephemeralstorage", "Required": false, "Type": "EphemeralStorage", - "UpdateType": "Mutable" + "UpdateType": "Immutable" }, "ExecutionRoleArn": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-taskdefinition.html#cfn-ecs-taskdefinition-executionrolearn", @@ -75068,7 +76116,7 @@ "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-elasticsearch-domain.html#cfn-elasticsearch-domain-encryptionatrestoptions", "Required": false, "Type": "EncryptionAtRestOptions", - "UpdateType": "Immutable" + "UpdateType": "Conditional" }, "LogPublishingOptions": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-elasticsearch-domain.html#cfn-elasticsearch-domain-logpublishingoptions", @@ -75082,7 +76130,7 @@ "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-elasticsearch-domain.html#cfn-elasticsearch-domain-nodetonodeencryptionoptions", "Required": false, "Type": "NodeToNodeEncryptionOptions", - "UpdateType": "Immutable" + "UpdateType": "Conditional" }, "SnapshotOptions": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-elasticsearch-domain.html#cfn-elasticsearch-domain-snapshotoptions", @@ -82130,12 +83178,6 @@ "Required": false, "UpdateType": "Mutable" }, - "PartialBatchResponse": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-eventsourcemapping.html#cfn-lambda-eventsourcemapping-partialbatchresponse", - "PrimitiveType": "Boolean", - "Required": false, - "UpdateType": "Mutable" - }, "Queues": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-eventsourcemapping.html#cfn-lambda-eventsourcemapping-queues", "DuplicatesAllowed": false, @@ -82324,6 +83366,13 @@ "AWS::Lambda::LayerVersion": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-layerversion.html", "Properties": { + "CompatibleArchitectures": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-layerversion.html#cfn-lambda-layerversion-compatiblearchitectures", + "PrimitiveItemType": "String", + "Required": false, + "Type": "List", + "UpdateType": "Immutable" + }, "CompatibleRuntimes": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-layerversion.html#cfn-lambda-layerversion-compatibleruntimes", "PrimitiveItemType": "String", @@ -90639,6 +91688,13 @@ "Required": false, "UpdateType": "Mutable" }, + "CalendarNames": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssm-association.html#cfn-ssm-association-calendarnames", + "PrimitiveItemType": "String", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, "ComplianceSeverity": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssm-association.html#cfn-ssm-association-complianceseverity", "PrimitiveType": "String", @@ -91194,6 +92250,162 @@ } } }, + "AWS::SSMContacts::Contact": { + "Attributes": { + "Arn": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssmcontacts-contact.html", + "Properties": { + "Alias": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssmcontacts-contact.html#cfn-ssmcontacts-contact-alias", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "DisplayName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssmcontacts-contact.html#cfn-ssmcontacts-contact-displayname", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "Plan": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssmcontacts-contact.html#cfn-ssmcontacts-contact-plan", + "ItemType": "Stage", + "Required": true, + "Type": "List", + "UpdateType": "Mutable" + }, + "Type": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssmcontacts-contact.html#cfn-ssmcontacts-contact-type", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + } + } + }, + "AWS::SSMContacts::ContactChannel": { + "Attributes": { + "Arn": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssmcontacts-contactchannel.html", + "Properties": { + "ChannelAddress": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssmcontacts-contactchannel.html#cfn-ssmcontacts-contactchannel-channeladdress", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "ChannelName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssmcontacts-contactchannel.html#cfn-ssmcontacts-contactchannel-channelname", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "ChannelType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssmcontacts-contactchannel.html#cfn-ssmcontacts-contactchannel-channeltype", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "ContactId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssmcontacts-contactchannel.html#cfn-ssmcontacts-contactchannel-contactid", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "DeferActivation": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssmcontacts-contactchannel.html#cfn-ssmcontacts-contactchannel-deferactivation", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::SSMIncidents::ReplicationSet": { + "Attributes": { + "Arn": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssmincidents-replicationset.html", + "Properties": { + "DeletionProtected": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssmincidents-replicationset.html#cfn-ssmincidents-replicationset-deletionprotected", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "Regions": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssmincidents-replicationset.html#cfn-ssmincidents-replicationset-regions", + "ItemType": "ReplicationRegion", + "Required": true, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, + "AWS::SSMIncidents::ResponsePlan": { + "Attributes": { + "Arn": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssmincidents-responseplan.html", + "Properties": { + "Actions": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssmincidents-responseplan.html#cfn-ssmincidents-responseplan-actions", + "DuplicatesAllowed": false, + "ItemType": "Action", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "ChatChannel": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssmincidents-responseplan.html#cfn-ssmincidents-responseplan-chatchannel", + "Required": false, + "Type": "ChatChannel", + "UpdateType": "Mutable" + }, + "DisplayName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssmincidents-responseplan.html#cfn-ssmincidents-responseplan-displayname", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Engagements": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssmincidents-responseplan.html#cfn-ssmincidents-responseplan-engagements", + "DuplicatesAllowed": false, + "PrimitiveItemType": "String", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "IncidentTemplate": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssmincidents-responseplan.html#cfn-ssmincidents-responseplan-incidenttemplate", + "Required": true, + "Type": "IncidentTemplate", + "UpdateType": "Mutable" + }, + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssmincidents-responseplan.html#cfn-ssmincidents-responseplan-name", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssmincidents-responseplan.html#cfn-ssmincidents-responseplan-tags", + "DuplicatesAllowed": false, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, "AWS::SSO::Assignment": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sso-assignment.html", "Properties": { diff --git a/packages/@aws-cdk/cloudformation-include/package.json b/packages/@aws-cdk/cloudformation-include/package.json index 36fd0bfd649a5..c0dec59603b9d 100644 --- a/packages/@aws-cdk/cloudformation-include/package.json +++ b/packages/@aws-cdk/cloudformation-include/package.json @@ -210,6 +210,8 @@ "@aws-cdk/aws-sns": "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-synthetics": "0.0.0", @@ -370,6 +372,8 @@ "@aws-cdk/aws-sns": "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-synthetics": "0.0.0", diff --git a/packages/aws-cdk-lib/package.json b/packages/aws-cdk-lib/package.json index a57f3ec6c14af..4556f3c89a433 100644 --- a/packages/aws-cdk-lib/package.json +++ b/packages/aws-cdk-lib/package.json @@ -284,6 +284,8 @@ "@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", diff --git a/packages/decdk/package.json b/packages/decdk/package.json index f72bb5df7e89b..fd0de8150e914 100644 --- a/packages/decdk/package.json +++ b/packages/decdk/package.json @@ -201,6 +201,8 @@ "@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", diff --git a/packages/monocdk/package.json b/packages/monocdk/package.json index dab457a5e031c..c23f29c7cc1f4 100644 --- a/packages/monocdk/package.json +++ b/packages/monocdk/package.json @@ -285,6 +285,8 @@ "@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", From f36feb52a750a326842903ac4dc23be83e4aee1a Mon Sep 17 00:00:00 2001 From: Ben Chaimberg Date: Thu, 20 May 2021 05:08:34 -0700 Subject: [PATCH 071/134] fix(iam): permissions boundaries not added to custom resource roles (#14754) The role created by `CustomResourceProvider` is a `CfnResource` with a manual type, not a `CfnRole` to avoid a cyclical dependency. But since `PermissionBoundary` assumes all role/user resources in scope are instances of `CfnRole` or `CfnUser`, a permission boundary is not correctly applied to the custom resource's role (or any other role or user created directly through `CfnResource`). This PR solves the above problem by adding extra conditionals for the `CfnResource` case and adds permission boundaries through the `addPropertyOverride` escape hatch. fixes #13310 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-iam/lib/permissions-boundary.ts | 16 ++++- .../aws-iam/test/custom-resource/index.ts | 0 .../aws-iam/test/permissions-boundary.test.ts | 72 ++++++++++++++++++- 3 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 packages/@aws-cdk/aws-iam/test/custom-resource/index.ts diff --git a/packages/@aws-cdk/aws-iam/lib/permissions-boundary.ts b/packages/@aws-cdk/aws-iam/lib/permissions-boundary.ts index 7b4320462a1ac..94964918b8658 100644 --- a/packages/@aws-cdk/aws-iam/lib/permissions-boundary.ts +++ b/packages/@aws-cdk/aws-iam/lib/permissions-boundary.ts @@ -1,3 +1,4 @@ +import { CfnResource } from '@aws-cdk/core'; import { Node, IConstruct } from 'constructs'; import { CfnRole, CfnUser } from './iam.generated'; import { IManagedPolicy } from './managed-policy'; @@ -22,7 +23,8 @@ export class PermissionsBoundary { } /** - * Apply the given policy as Permissions Boundary to all Roles in the scope + * Apply the given policy as Permissions Boundary to all Roles and Users in + * the scope. * * Will override any Permissions Boundaries configured previously; in case * a Permission Boundary is applied in multiple scopes, the Boundary applied @@ -33,6 +35,11 @@ export class PermissionsBoundary { visit(node: IConstruct) { if (node instanceof CfnRole || node instanceof CfnUser) { node.permissionsBoundary = boundaryPolicy.managedPolicyArn; + } else if ( + node instanceof CfnResource && + (node.cfnResourceType == CfnRole.CFN_RESOURCE_TYPE_NAME || node.cfnResourceType == CfnUser.CFN_RESOURCE_TYPE_NAME) + ) { + node.addPropertyOverride('PermissionsBoundary', boundaryPolicy.managedPolicyArn); } }, }); @@ -46,8 +53,13 @@ export class PermissionsBoundary { visit(node: IConstruct) { if (node instanceof CfnRole || node instanceof CfnUser) { node.permissionsBoundary = undefined; + } else if ( + node instanceof CfnResource && + (node.cfnResourceType == CfnRole.CFN_RESOURCE_TYPE_NAME || node.cfnResourceType == CfnUser.CFN_RESOURCE_TYPE_NAME) + ) { + node.addPropertyDeletionOverride('PermissionsBoundary'); } }, }); } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-iam/test/custom-resource/index.ts b/packages/@aws-cdk/aws-iam/test/custom-resource/index.ts new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/packages/@aws-cdk/aws-iam/test/permissions-boundary.test.ts b/packages/@aws-cdk/aws-iam/test/permissions-boundary.test.ts index 46d840b17159b..ab304f3481a15 100644 --- a/packages/@aws-cdk/aws-iam/test/permissions-boundary.test.ts +++ b/packages/@aws-cdk/aws-iam/test/permissions-boundary.test.ts @@ -1,6 +1,7 @@ +import * as path from 'path'; import { ABSENT } from '@aws-cdk/assert-internal'; import '@aws-cdk/assert-internal/jest'; -import { App, Stack } from '@aws-cdk/core'; +import { App, CfnResource, CustomResourceProvider, CustomResourceProviderRuntime, Stack } from '@aws-cdk/core'; import * as iam from '../lib'; let app: App; @@ -72,6 +73,73 @@ test('apply newly created boundary to a role', () => { }); }); +test('apply boundary to role created by a custom resource', () => { + // GIVEN + const provider = CustomResourceProvider.getOrCreateProvider(stack, 'Empty', { + codeDirectory: path.join(__dirname, 'custom-resource'), + runtime: CustomResourceProviderRuntime.NODEJS_12_X, + }); + + // WHEN + iam.PermissionsBoundary.of(provider).apply(new iam.ManagedPolicy(stack, 'Policy', { + statements: [ + new iam.PolicyStatement({ + actions: ['*'], + resources: ['*'], + }), + ], + })); + + // THEN + expect(stack).toHaveResource('AWS::IAM::Role', { + PermissionsBoundary: { Ref: 'Policy23B91518' }, + }); +}); + +test('apply boundary to users created via CfnResource', () => { + // GIVEN + const user = new CfnResource(stack, 'User', { + type: 'AWS::IAM::User', + }); + + // WHEN + iam.PermissionsBoundary.of(user).apply(new iam.ManagedPolicy(stack, 'Policy', { + statements: [ + new iam.PolicyStatement({ + actions: ['*'], + resources: ['*'], + }), + ], + })); + + // THEN + expect(stack).toHaveResource('AWS::IAM::User', { + PermissionsBoundary: { Ref: 'Policy23B91518' }, + }); +}); + +test('apply boundary to roles created via CfnResource', () => { + // GIVEN + const role = new CfnResource(stack, 'Role', { + type: 'AWS::IAM::Role', + }); + + // WHEN + iam.PermissionsBoundary.of(role).apply(new iam.ManagedPolicy(stack, 'Policy', { + statements: [ + new iam.PolicyStatement({ + actions: ['*'], + resources: ['*'], + }), + ], + })); + + // THEN + expect(stack).toHaveResource('AWS::IAM::Role', { + PermissionsBoundary: { Ref: 'Policy23B91518' }, + }); +}); + test('unapply inherited boundary from a user: order 1', () => { // GIVEN const user = new iam.User(stack, 'User'); @@ -98,4 +166,4 @@ test('unapply inherited boundary from a user: order 2', () => { expect(stack).toHaveResource('AWS::IAM::User', { PermissionsBoundary: ABSENT, }); -}); \ No newline at end of file +}); From cd2589f560600294cc50988a98e69b091c42e3f8 Mon Sep 17 00:00:00 2001 From: Niranjan Jayakar Date: Thu, 20 May 2021 13:36:51 +0100 Subject: [PATCH 072/134] fix(cognito): user pool - phoneNumberVerified attribute fails deployment (#14699) The error from the Cognito service: > Value 'phone_number_verified' at 'schema.1.member.name' failed to > satisfy constraint: Member must have length less than or equal to 20 The attributes 'phone_number_verified' and 'email_verified' were incorrectly modeled as standard attributes in the CDK. When specified, Cognito interprets these as custom attributes, and applies the corresponding validation. Hence, the above error message. These two attributes are actually Cognito built-in attributes (not documented as such as of the time of this commit). Users cannot specify or configure these as part of user pool creation. They can only be modified after creation using the Cognito IdP control plane APIs, ex: AdminUpdateUserAttributes API. see https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-attributes.html internal ref: t.corp/D23141918 closes #14175 ---- *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-cognito/README.md | 6 ++++++ .../@aws-cdk/aws-cognito/lib/private/attr-names.ts | 2 ++ packages/@aws-cdk/aws-cognito/lib/user-pool-attr.ts | 12 ++++++++---- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/@aws-cdk/aws-cognito/README.md b/packages/@aws-cdk/aws-cognito/README.md index 0582fbdec0f2a..a018457eff368 100644 --- a/packages/@aws-cdk/aws-cognito/README.md +++ b/packages/@aws-cdk/aws-cognito/README.md @@ -214,6 +214,12 @@ Custom attributes cannot be marked as required. All custom attributes share the property `mutable` that specifies whether the value of the attribute can be changed. The default value is `false`. +User pools come with two 'built-in' attributes - `email_verified` and `phone_number_verified`. These cannot be +configured (required-ness or mutability) as part of user pool creation. However, user pool administrators can modify +them for specific users using the [AdminUpdateUserAttributes API]. + +[AdminUpdateUserAttributes API]: https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminUpdateUserAttributes.html + ### Security Cognito sends various messages to its users via SMS, for different actions, ranging from account verification to diff --git a/packages/@aws-cdk/aws-cognito/lib/private/attr-names.ts b/packages/@aws-cdk/aws-cognito/lib/private/attr-names.ts index c3b07ddbb7742..449df8ffada7a 100644 --- a/packages/@aws-cdk/aws-cognito/lib/private/attr-names.ts +++ b/packages/@aws-cdk/aws-cognito/lib/private/attr-names.ts @@ -16,6 +16,8 @@ export const StandardAttributeNames = { timezone: 'zoneinfo', lastUpdateTime: 'updated_at', website: 'website', + /** @deprecated */ emailVerified: 'email_verified', + /** @deprecated */ phoneNumberVerified: 'phone_number_verified', }; \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cognito/lib/user-pool-attr.ts b/packages/@aws-cdk/aws-cognito/lib/user-pool-attr.ts index 0687a0cb123ba..50572382325ea 100644 --- a/packages/@aws-cdk/aws-cognito/lib/user-pool-attr.ts +++ b/packages/@aws-cdk/aws-cognito/lib/user-pool-attr.ts @@ -110,13 +110,17 @@ export interface StandardAttributes { readonly website?: StandardAttribute; /** - * Whether the email address has been verified. + * DEPRECATED + * @deprecated this is not a standard attribute and was incorrectly added to the CDK. + * It is a Cognito built-in attribute and cannot be controlled as part of user pool creation. * @default - see the defaults under `StandardAttribute` */ readonly emailVerified?: StandardAttribute; /** - * Whether the phone number has been verified. + * DEPRECATED + * @deprecated this is not a standard attribute and was incorrectly added to the CDK. + * It is a Cognito built-in attribute and cannot be controlled as part of user pool creation. * @default - see the defaults under `StandardAttribute` */ readonly phoneNumberVerified?: StandardAttribute; @@ -356,9 +360,9 @@ export class DateTimeAttribute implements ICustomAttribute { } /** - * This interface contains all standard attributes recognized by Cognito + * This interface contains standard attributes recognized by Cognito * from https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-attributes.html - * including `email_verified` and `phone_number_verified` + * including built-in attributes `email_verified` and `phone_number_verified` */ export interface StandardAttributesMask { /** From b78a1bbf445743d96c8e4f54e7d2e7cac204342a Mon Sep 17 00:00:00 2001 From: Ben Chaimberg Date: Thu, 20 May 2021 09:17:01 -0700 Subject: [PATCH 073/134] feat(lambda): support Principal conditions in Permission (#14674) If the principal passed to Function.addPermission has conditions associated with it (ie., PrincipalWithConditions) and the conditions are supported by CfnPermission (ie., aws:SourceAccount and aws:SourceArn), then the values will be passed to their corresponding CfnPermission parameter. fixes #8116 ---- *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/README.md | 56 ++++++++++++++++++ .../@aws-cdk/aws-lambda/lib/function-base.ts | 59 +++++++++++++++---- packages/@aws-cdk/aws-lambda/lib/util.ts | 14 +++++ packages/@aws-cdk/aws-lambda/package.json | 4 +- .../@aws-cdk/aws-lambda/test/function.test.ts | 58 ++++++++++++++++++ 5 files changed, 178 insertions(+), 13 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/README.md b/packages/@aws-cdk/aws-lambda/README.md index ca67608a5e19f..a12e1bc923a4d 100644 --- a/packages/@aws-cdk/aws-lambda/README.md +++ b/packages/@aws-cdk/aws-lambda/README.md @@ -114,6 +114,62 @@ myRole.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName("service-role/AWS myRole.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName("service-role/AWSLambdaVPCAccessExecutionRole")); // only required if your function lives in a VPC ``` +## Resource-based Policies + +AWS Lambda supports resource-based policies for controlling access to Lambda +functions and layers on a per-resource basis. In particular, this allows you to +give permission to AWS services and other AWS accounts to modify and invoke your +functions. You can also restrict permissions given to AWS services by providing +a source account or ARN (representing the account and identifier of the resource +that accesses the function or layer). + +```ts +import * as iam from '@aws-cdk/aws-iam'; +const principal = new iam.ServicePrincipal('my-service'); + +fn.grantInvoke(principal); + +// Equivalent to: +fn.addPermission('my-service Invocation', { + principal: principal, +}); +``` + +For more information, see [Resource-based +policies](https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html) +in the AWS Lambda Developer Guide. + +Providing an unowned principal (such as account principals, generic ARN +principals, service principals, and principals in other accounts) to a call to +`fn.grantInvoke` will result in a resource-based policy being created. If the +principal in question has conditions limiting the source account or ARN of the +operation (see above), these conditions will be automatically added to the +resource policy. + +```ts +import * as iam from '@aws-cdk/aws-iam'; +const servicePrincipal = new iam.ServicePrincipal('my-service'); +const sourceArn = 'arn:aws:s3:::my-bucket'; +const sourceAccount = '111122223333'; +const servicePrincipalWithConditions = servicePrincipal.withConditions({ + ArnLike: { + 'aws:SourceArn': sourceArn, + }, + StringEquals: { + 'aws:SourceAccount': sourceAccount, + }, +}); + +fn.grantInvoke(servicePrincipalWithConditions); + +// Equivalent to: +fn.addPermission('my-service Invocation', { + principal: servicePrincipal, + sourceArn: sourceArn, + sourceAccount: sourceAccount, +}); +``` + ## Versions and Aliases You can use diff --git a/packages/@aws-cdk/aws-lambda/lib/function-base.ts b/packages/@aws-cdk/aws-lambda/lib/function-base.ts index f5e7b383f8196..e8935fc2cdb6b 100644 --- a/packages/@aws-cdk/aws-lambda/lib/function-base.ts +++ b/packages/@aws-cdk/aws-lambda/lib/function-base.ts @@ -9,7 +9,7 @@ import { EventSourceMapping, EventSourceMappingOptions } from './event-source-ma import { IVersion } from './lambda-version'; import { CfnPermission } from './lambda.generated'; import { Permission } from './permission'; -import { addAlias } from './util'; +import { addAlias, flatMap } from './util'; export interface IFunction extends IResource, ec2.IConnectable, iam.IGrantable { @@ -65,7 +65,7 @@ export interface IFunction extends IResource, ec2.IConnectable, iam.IGrantable { /** * Adds a permission to the Lambda resource policy. - * @param id The id ƒor the permission construct + * @param id The id for the permission construct * @param permission The permission to grant to this Lambda function. @see Permission for details. */ addPermission(id: string, permission: Permission): void; @@ -229,7 +229,7 @@ export abstract class FunctionBase extends Resource implements IFunction, ec2.IC /** * Adds a permission to the Lambda resource policy. - * @param id The id ƒor the permission construct + * @param id The id for the permission construct * @param permission The permission to grant to this Lambda function. @see Permission for details. */ public addPermission(id: string, permission: Permission) { @@ -239,16 +239,17 @@ export abstract class FunctionBase extends Resource implements IFunction, ec2.IC } const principal = this.parsePermissionPrincipal(permission.principal); - const action = permission.action || 'lambda:InvokeFunction'; - const scope = permission.scope || this; + const { sourceAccount, sourceArn } = this.parseConditions(permission.principal) ?? {}; + const action = permission.action ?? 'lambda:InvokeFunction'; + const scope = permission.scope ?? this; new CfnPermission(scope, id, { action, principal, functionName: this.functionArn, eventSourceToken: permission.eventSourceToken, - sourceAccount: permission.sourceAccount, - sourceArn: permission.sourceArn, + sourceAccount: permission.sourceAccount ?? sourceAccount, + sourceArn: permission.sourceArn ?? sourceArn, }); } @@ -395,14 +396,15 @@ export abstract class FunctionBase extends Resource implements IFunction, ec2.IC * Try to recognize some specific Principal classes first, then try a generic * fallback. */ - private parsePermissionPrincipal(principal?: iam.IPrincipal) { - if (!principal) { - return undefined; - } - + private parsePermissionPrincipal(principal: iam.IPrincipal) { // Try some specific common classes first. // use duck-typing, not instance of // @deprecated: after v2, we can change these to 'instanceof' + if ('conditions' in principal) { + // eslint-disable-next-line dot-notation + principal = principal['principal']; + } + if ('accountId' in principal) { return (principal as iam.AccountPrincipal).accountId; } @@ -431,6 +433,39 @@ export abstract class FunctionBase extends Resource implements IFunction, ec2.IC throw new Error(`Invalid principal type for Lambda permission statement: ${principal.constructor.name}. ` + 'Supported: AccountPrincipal, ArnPrincipal, ServicePrincipal'); } + + private parseConditions(principal: iam.IPrincipal): { sourceAccount: string, sourceArn: string } | null { + if (this.isPrincipalWithConditions(principal)) { + const conditions: iam.Conditions = principal.policyFragment.conditions; + const conditionPairs = flatMap( + Object.entries(conditions), + ([operator, conditionObjs]) => Object.keys(conditionObjs as object).map(key => { return { operator, key }; }), + ); + const supportedPrincipalConditions = [{ operator: 'ArnLike', key: 'aws:SourceArn' }, { operator: 'StringEquals', key: 'aws:SourceAccount' }]; + + const unsupportedConditions = conditionPairs.filter( + (condition) => !supportedPrincipalConditions.some( + (supportedCondition) => supportedCondition.operator === condition.operator && supportedCondition.key === condition.key, + ), + ); + + if (unsupportedConditions.length == 0) { + return { + sourceAccount: conditions.StringEquals['aws:SourceAccount'], + sourceArn: conditions.ArnLike['aws:SourceArn'], + }; + } else { + throw new Error(`PrincipalWithConditions had unsupported conditions for Lambda permission statement: ${JSON.stringify(unsupportedConditions)}. ` + + `Supported operator/condition pairs: ${JSON.stringify(supportedPrincipalConditions)}`); + } + } else { + return null; + } + } + + private isPrincipalWithConditions(principal: iam.IPrincipal): principal is iam.PrincipalWithConditions { + return 'conditions' in principal; + } } export abstract class QualifiedFunctionBase extends FunctionBase { diff --git a/packages/@aws-cdk/aws-lambda/lib/util.ts b/packages/@aws-cdk/aws-lambda/lib/util.ts index fd5aa246e63cc..d09e52a636d30 100644 --- a/packages/@aws-cdk/aws-lambda/lib/util.ts +++ b/packages/@aws-cdk/aws-lambda/lib/util.ts @@ -9,3 +9,17 @@ export function addAlias(scope: Construct, version: IVersion, aliasName: string, ...options, }); } + +/** + * Map a function over an array and concatenate the results + */ +export function flatMap(xs: T[], fn: ((x: T, i: number) => U[])): U[] { + return flatten(xs.map(fn)); +} + +/** + * Flatten a list of lists into a list of elements + */ +export function flatten(xs: T[][]): T[] { + return Array.prototype.concat.apply([], xs); +} diff --git a/packages/@aws-cdk/aws-lambda/package.json b/packages/@aws-cdk/aws-lambda/package.json index 6279fa92e0518..dc5d073fceaf7 100644 --- a/packages/@aws-cdk/aws-lambda/package.json +++ b/packages/@aws-cdk/aws-lambda/package.json @@ -181,7 +181,9 @@ "announce": false }, "nozem": { - "ostools": ["docker"] + "ostools": [ + "docker" + ] }, "maturity": "stable", "publishConfig": { diff --git a/packages/@aws-cdk/aws-lambda/test/function.test.ts b/packages/@aws-cdk/aws-lambda/test/function.test.ts index 26a27ca76aad2..c8bd8ac3b9889 100644 --- a/packages/@aws-cdk/aws-lambda/test/function.test.ts +++ b/packages/@aws-cdk/aws-lambda/test/function.test.ts @@ -191,6 +191,64 @@ describe('function', () => { fn.addPermission('S3', { principal: new iam.ArnPrincipal('my:arn') }); }); + test('applies source account/ARN conditions if the principal has conditions', () => { + const stack = new cdk.Stack(); + const fn = newTestLambda(stack); + const sourceAccount = 'some-account'; + const sourceArn = 'some-arn'; + const service = 'my-service'; + const principal = new iam.PrincipalWithConditions(new iam.ServicePrincipal(service), { + ArnLike: { + 'aws:SourceArn': sourceArn, + }, + StringEquals: { + 'aws:SourceAccount': sourceAccount, + }, + }); + + fn.addPermission('S1', { principal: principal }); + + expect(stack).toHaveResource('AWS::Lambda::Permission', { + Action: 'lambda:InvokeFunction', + FunctionName: { + 'Fn::GetAtt': [ + 'MyLambdaCCE802FB', + 'Arn', + ], + }, + Principal: service, + SourceAccount: sourceAccount, + SourceArn: sourceArn, + }); + }); + + test('fails if the principal has conditions that are not supported', () => { + const stack = new cdk.Stack(); + const fn = newTestLambda(stack); + + expect(() => fn.addPermission('F1', { + principal: new iam.PrincipalWithConditions(new iam.ServicePrincipal('my-service'), { + ArnEquals: { + 'aws:SourceArn': 'source-arn', + }, + }), + })).toThrow(/PrincipalWithConditions had unsupported conditions for Lambda permission statement/); + expect(() => fn.addPermission('F2', { + principal: new iam.PrincipalWithConditions(new iam.ServicePrincipal('my-service'), { + StringLike: { + 'aws:SourceAccount': 'source-account', + }, + }), + })).toThrow(/PrincipalWithConditions had unsupported conditions for Lambda permission statement/); + expect(() => fn.addPermission('F3', { + principal: new iam.PrincipalWithConditions(new iam.ServicePrincipal('my-service'), { + ArnLike: { + 's3:DataAccessPointArn': 'data-access-point-arn', + }, + }), + })).toThrow(/PrincipalWithConditions had unsupported conditions for Lambda permission statement/); + }); + test('BYORole', () => { // GIVEN const stack = new cdk.Stack(); From 2a3133595172a14e98636d26fd20dd818de374b1 Mon Sep 17 00:00:00 2001 From: "Eric Z. Beard" Date: Thu, 20 May 2021 18:13:35 -0400 Subject: [PATCH 074/134] chore: move ownership from Romain to Adam (#14797) --- .github/workflows/issue-label-assign.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/issue-label-assign.yml b/.github/workflows/issue-label-assign.yml index 70b40968a4289..fce1ea1e9bbae 100644 --- a/.github/workflows/issue-label-assign.yml +++ b/.github/workflows/issue-label-assign.yml @@ -78,8 +78,8 @@ jobs: {"keywords":["(@aws-cdk/aws-dlm)","(aws-dlm)","(dlm)"],"labels":["@aws-cdk/aws-dlm"],"assignees":["njlynch"]}, {"keywords":["(@aws-cdk/aws-dms)","(aws-dms)","(dms)"],"labels":["@aws-cdk/aws-dms"],"assignees":["njlynch"]}, {"keywords":["(@aws-cdk/aws-docdb)","(aws-docdb)","(docdb)","(doc db)","(doc-db)"],"labels":["@aws-cdk/aws-docdb"],"assignees":["iliapolo"]}, - {"keywords":["(@aws-cdk/aws-dynamodb)","(aws-dynamodb)","(dynamodb)","(dynamo db)","(dynamo-db)"],"labels":["@aws-cdk/aws-dynamodb"],"assignees":["RomainMuller"]}, - {"keywords":["(@aws-cdk/aws-dynamodb-global)","(aws-dynamodb-global)","(dynamodb-global)","(dynamodb global)"],"labels":["@aws-cdk/aws-dynamodb-global"],"assignees":["RomainMuller"]}, + {"keywords":["(@aws-cdk/aws-dynamodb)","(aws-dynamodb)","(dynamodb)","(dynamo db)","(dynamo-db)"],"labels":["@aws-cdk/aws-dynamodb"],"assignees":["skinny85"]}, + {"keywords":["(@aws-cdk/aws-dynamodb-global)","(aws-dynamodb-global)","(dynamodb-global)","(dynamodb global)"],"labels":["@aws-cdk/aws-dynamodb-global"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-ec2)","(aws-ec2)","(ec2)","(vpc)"],"labels":["@aws-cdk/aws-ec2"],"assignees":["rix0rrr"]}, {"keywords":["(@aws-cdk/aws-ecr)","(aws-ecr)","(ecr)"],"labels":["@aws-cdk/aws-ecr"],"assignees":["MrArnoldPalmer"]}, {"keywords":["(@aws-cdk/aws-ecr-assets)","(aws-ecr-assets)","(ecr-assets)","(ecr assets)","(ecrassets)"],"labels":["@aws-cdk/aws-ecr-assets"],"assignees":["eladb"]}, @@ -184,7 +184,7 @@ jobs: {"keywords":["(@aws-cdk/custom-resources)","(custom-resources)","(custom resources)"],"labels":["@aws-cdk/custom-resources"],"assignees":["rix0rrr"]}, {"keywords":["(@aws-cdk/cx-api)","(cx-api)","(cx api)"],"labels":["@aws-cdk/cx-api"],"assignees":["rix0rrr"]}, {"keywords":["(@aws-cdk/pipelines)","(pipelines)","(cdk pipelines)","(cdk-pipelines)"],"labels":["@aws-cdk/pipelines"],"assignees":["rix0rrr"]}, - {"keywords":["(@aws-cdk/region-info)","(region-info)","(region info)"],"labels":["@aws-cdk/region-info"],"assignees":["RomainMuller"]}, + {"keywords":["(@aws-cdk/region-info)","(region-info)","(region info)"],"labels":["@aws-cdk/region-info"],"assignees":["skinny85"]}, {"keywords":["(aws-cdk-lib)","(cdk-v2)", "(v2)", "(ubergen)"],"labels":["aws-cdk-lib"],"assignees":["nija-at"]}, {"keywords":["(monocdk)","(monocdk-experiment)"],"labels":["monocdk"],"assignees":["nija-at"]} ] From f4a2938cf6773bf80e3316abda82d03aed051108 Mon Sep 17 00:00:00 2001 From: Alex Johnson Date: Thu, 20 May 2021 16:04:35 -0700 Subject: [PATCH 075/134] feat(appmesh): add IAM grants for StreamAggregatedResources (#13596) This adds the IAM grant `grantStreamAggregatedResources` to VirtualNodes and VirtualGateways. Example below ```ts const gateway = new appmesh.VirtualGateway(stack, 'testGateway', { mesh: mesh }); const envoyUser = new iam.User(stack, 'envoyUser'); /** * This will grant `grantStreamAggregatedResources` ONLY for this gateway. */ gateway.grantStreamAggregatedResources(envoyUser) ``` resolves #11639 ---- *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-appmesh/README.md | 15 +++++++ .../aws-appmesh/lib/virtual-gateway.ts | 14 +++++++ .../@aws-cdk/aws-appmesh/lib/virtual-node.ts | 13 ++++++ .../aws-appmesh/test/test.virtual-gateway.ts | 34 +++++++++++++++ .../aws-appmesh/test/test.virtual-node.ts | 42 +++++++++++++++++++ 5 files changed, 118 insertions(+) diff --git a/packages/@aws-cdk/aws-appmesh/README.md b/packages/@aws-cdk/aws-appmesh/README.md index 46c568fc3eb8c..839a982c93f31 100644 --- a/packages/@aws-cdk/aws-appmesh/README.md +++ b/packages/@aws-cdk/aws-appmesh/README.md @@ -584,3 +584,18 @@ appmesh.Mesh.fromMeshArn(stack, 'imported-mesh', arn); ```ts appmesh.Mesh.fromMeshName(stack, 'imported-mesh', 'abc'); ``` + +## IAM Grants + +Virtual Node and Virtual Gateway implement `grantStreamAggregatedResources` that will grant identities that are running +Envoy access to stream generated config from App Mesh. + +```ts +const gateway = new appmesh.VirtualGateway(stack, 'testGateway', { mesh: mesh }); +const envoyUser = new iam.User(stack, 'envoyUser'); + +/** + * This will grant `grantStreamAggregatedResources` ONLY for this gateway. + */ +gateway.grantStreamAggregatedResources(envoyUser) +``` diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts index d2f0a873a0849..4a86f137a6337 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts @@ -1,3 +1,4 @@ +import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnVirtualGateway } from './appmesh.generated'; @@ -33,6 +34,11 @@ export interface IVirtualGateway extends cdk.IResource { * Utility method to add a new GatewayRoute to the VirtualGateway */ addGatewayRoute(id: string, route: GatewayRouteBaseProps): GatewayRoute; + + /** + * Grants the given entity `appmesh:StreamAggregatedResources`. + */ + grantStreamAggregatedResources(identity: iam.IGrantable): iam.Grant; } /** @@ -103,6 +109,14 @@ abstract class VirtualGatewayBase extends cdk.Resource implements IVirtualGatewa virtualGateway: this, }); } + + public grantStreamAggregatedResources(identity: iam.IGrantable): iam.Grant { + return iam.Grant.addToPrincipal({ + grantee: identity, + actions: ['appmesh:StreamAggregatedResources'], + resourceArns: [this.virtualGatewayArn], + }); + } } /** diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts index cf14fcbdb3547..82c6aff3992e0 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts @@ -1,3 +1,4 @@ +import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnVirtualNode } from './appmesh.generated'; @@ -33,6 +34,10 @@ export interface IVirtualNode extends cdk.IResource { */ readonly mesh: IMesh; + /** + * Grants the given entity `appmesh:StreamAggregatedResources`. + */ + grantStreamAggregatedResources(identity: iam.IGrantable): iam.Grant; } /** @@ -108,6 +113,14 @@ abstract class VirtualNodeBase extends cdk.Resource implements IVirtualNode { * The Mesh which the VirtualNode belongs to */ public abstract readonly mesh: IMesh; + + public grantStreamAggregatedResources(identity: iam.IGrantable): iam.Grant { + return iam.Grant.addToPrincipal({ + grantee: identity, + actions: ['appmesh:StreamAggregatedResources'], + resourceArns: [this.virtualNodeArn], + }); + } } /** diff --git a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts index bafc0eccd6822..6f9d92b467911 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts @@ -1,5 +1,6 @@ import { expect, haveResourceLike } from '@aws-cdk/assert-internal'; import * as acm from '@aws-cdk/aws-certificatemanager'; +import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; import { Test } from 'nodeunit'; import * as appmesh from '../lib'; @@ -563,6 +564,7 @@ export = { test.equal(virtualGateway.virtualGatewayName, virtualGatewayName); test.done(); }, + 'Can import VirtualGateways using attributes'(test: Test) { const app = new cdk.App(); // GIVEN @@ -583,4 +585,36 @@ export = { test.done(); }, + + 'Can grant an identity StreamAggregatedResources for a given VirtualGateway'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const mesh = new appmesh.Mesh(stack, 'mesh', { + meshName: 'test-mesh', + }); + const gateway = new appmesh.VirtualGateway(stack, 'testGateway', { + mesh: mesh, + }); + + // WHEN + const user = new iam.User(stack, 'test'); + gateway.grantStreamAggregatedResources(user); + + // THEN + expect(stack).to(haveResourceLike('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'appmesh:StreamAggregatedResources', + Effect: 'Allow', + Resource: { + Ref: 'testGatewayF09EC349', + }, + }, + ], + }, + })); + + test.done(); + }, }; \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appmesh/test/test.virtual-node.ts b/packages/@aws-cdk/aws-appmesh/test/test.virtual-node.ts index b993309547ab8..e4956078679c3 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.virtual-node.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.virtual-node.ts @@ -1,6 +1,7 @@ import { expect, haveResourceLike } from '@aws-cdk/assert-internal'; import * as acmpca from '@aws-cdk/aws-acmpca'; import * as acm from '@aws-cdk/aws-certificatemanager'; +import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; import { Test } from 'nodeunit'; import * as appmesh from '../lib'; @@ -736,6 +737,7 @@ export = { test.done(); }, + 'Can import Virtual Nodes using attributes'(test: Test) { // GIVEN const stack = new cdk.Stack(); @@ -752,4 +754,44 @@ export = { test.done(); }, + + 'Can grant an identity StreamAggregatedResources for a given VirtualNode'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const mesh = new appmesh.Mesh(stack, 'mesh', { + meshName: 'test-mesh', + }); + const node = new appmesh.VirtualNode(stack, 'test-node', { + mesh, + listeners: [appmesh.VirtualNodeListener.http({ + port: 80, + tlsCertificate: appmesh.TlsCertificate.file({ + certificateChainPath: 'path/to/certChain', + privateKeyPath: 'path/to/privateKey', + tlsMode: appmesh.TlsMode.PERMISSIVE, + }), + })], + }); + + // WHEN + const user = new iam.User(stack, 'test'); + node.grantStreamAggregatedResources(user); + + // THEN + expect(stack).to(haveResourceLike('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'appmesh:StreamAggregatedResources', + Effect: 'Allow', + Resource: { + Ref: 'testnode3EE2776E', + }, + }, + ], + }, + })); + + test.done(); + }, }; From 37e90316a43f7ac040b9a7d8302b49334b02cf37 Mon Sep 17 00:00:00 2001 From: Otavio Macedo Date: Fri, 21 May 2021 10:01:21 +0100 Subject: [PATCH 076/134] chore(docs): added troubleshooting entry for Docker error in the synthesis step (#14793) Documents the action the user should take to prevent #9217. The problem here is that, for any app that does asset bundling or uses constructs like `NodeJsFunction` (or its counterparts in other languages), the CodeBuild project has to be configured to run in privileged mode. But the only way to check whether the app uses these things is to run it and check the generated cloud assembly, which will fail if the privileged flag is not set. The only way to break out of this circular dependency would be to perform some sort of static analysis in the app before running it, or perhaps creating some concept of dry-run, in which the app just reports what it would do without actually doing it. But these solutions would be very complex to implement and probably not worth it. So we're resorting to ask the user to tell the CDK what to do. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/pipelines/README.md | 35 +++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/packages/@aws-cdk/pipelines/README.md b/packages/@aws-cdk/pipelines/README.md index 074c1b331ee41..8614825c53ad9 100644 --- a/packages/@aws-cdk/pipelines/README.md +++ b/packages/@aws-cdk/pipelines/README.md @@ -761,6 +761,41 @@ leading NPM 6 reading that same file to not install all required packages anymor Make sure you are using the same NPM version everywhere, either downgrade your workstation's version or upgrade the CodeBuild version. +### Cannot connect to the Docker daemon at unix:///var/run/docker.sock + +If, in the 'Synth' action (inside the 'Build' stage) of your pipeline, you get an error like this: + +```console +stderr: docker: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?. +See 'docker run --help'. +``` + +It means that the AWS CodeBuild project for 'Synth' is not configured to run in privileged mode, +which prevents Docker builds from happening. This typically happens if you use a CDK construct +that bundles asset using tools run via Docker, like `aws-lambda-nodejs`, `aws-lambda-python`, +`aws-lambda-go` and others. + +Make sure you set the `privileged` environment variable to `true` in the synth definition: + +```typescript + const pipeline = new CdkPipeline(this, 'MyPipeline', { + ... + + synthAction: SimpleSynthAction.standardNpmSynth({ + sourceArtifact: ..., + cloudAssemblyArtifact: ..., + + environment: { + privileged: true, + }, + }), + }); +``` + +After turning on `privilegedMode: true`, you will need to do a one-time manual cdk deploy of your +pipeline to get it going again (as with a broken 'synth' the pipeline will not be able to self +update to the right state). + ## Current Limitations Limitations that we are aware of and will address: From eff9c7504710929da58eab96c45d7b925132f73e Mon Sep 17 00:00:00 2001 From: Otavio Macedo Date: Fri, 21 May 2021 10:28:17 +0100 Subject: [PATCH 077/134] fix(pipelines): self-update build fails with named pipeline stack (#14729) Fixes #10782 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../pipelines/lib/actions/update-pipeline-action.ts | 13 +++++++++++-- packages/@aws-cdk/pipelines/lib/pipeline.ts | 2 +- packages/@aws-cdk/pipelines/test/pipeline.test.ts | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/@aws-cdk/pipelines/lib/actions/update-pipeline-action.ts b/packages/@aws-cdk/pipelines/lib/actions/update-pipeline-action.ts index a5922ada39926..c2bf636f02ca1 100644 --- a/packages/@aws-cdk/pipelines/lib/actions/update-pipeline-action.ts +++ b/packages/@aws-cdk/pipelines/lib/actions/update-pipeline-action.ts @@ -21,8 +21,16 @@ export interface UpdatePipelineActionProps { /** * Name of the pipeline stack + * + * @deprecated - Use `pipelineStackHierarchicalId` instead. + * @default - none + */ + readonly pipelineStackName?: string; + + /** + * Hierarchical id of the pipeline stack */ - readonly pipelineStackName: string; + readonly pipelineStackHierarchicalId: string; /** * Version of CDK CLI to 'npm install'. @@ -63,6 +71,7 @@ export class UpdatePipelineAction extends CoreConstruct implements codepipeline. const installSuffix = props.cdkCliVersion ? `@${props.cdkCliVersion}` : ''; + const stackIdentifier = props.pipelineStackHierarchicalId ?? props.pipelineStackName; const selfMutationProject = new codebuild.PipelineProject(this, 'SelfMutation', { projectName: props.projectName, environment: { @@ -78,7 +87,7 @@ export class UpdatePipelineAction extends CoreConstruct implements codepipeline. build: { commands: [ // Cloud Assembly is in *current* directory. - `cdk -a ${embeddedAsmPath(scope)} deploy ${props.pipelineStackName} --require-approval=never --verbose`, + `cdk -a ${embeddedAsmPath(scope)} deploy ${stackIdentifier} --require-approval=never --verbose`, ], }, }, diff --git a/packages/@aws-cdk/pipelines/lib/pipeline.ts b/packages/@aws-cdk/pipelines/lib/pipeline.ts index 5684a4eb0d604..3d9fe0c2063d1 100644 --- a/packages/@aws-cdk/pipelines/lib/pipeline.ts +++ b/packages/@aws-cdk/pipelines/lib/pipeline.ts @@ -214,7 +214,7 @@ export class CdkPipeline extends CoreConstruct { stageName: 'UpdatePipeline', actions: [new UpdatePipelineAction(this, 'UpdatePipeline', { cloudAssemblyInput: this._cloudAssemblyArtifact, - pipelineStackName: pipelineStack.stackName, + pipelineStackHierarchicalId: pipelineStack.node.path, cdkCliVersion: props.cdkCliVersion, projectName: maybeSuffix(props.pipelineName, '-selfupdate'), privileged: props.supportDockerAssets, diff --git a/packages/@aws-cdk/pipelines/test/pipeline.test.ts b/packages/@aws-cdk/pipelines/test/pipeline.test.ts index b04f9d7ff3a6e..e5c76b5488cd4 100644 --- a/packages/@aws-cdk/pipelines/test/pipeline.test.ts +++ b/packages/@aws-cdk/pipelines/test/pipeline.test.ts @@ -334,7 +334,7 @@ test('selfmutation stage correctly identifies nested assembly of pipeline stack' BuildSpec: encodedJson(deepObjectLike({ phases: { build: { - commands: arrayWith('cdk -a assembly-PipelineStage deploy PipelineStage-PipelineStack --require-approval=never --verbose'), + commands: arrayWith('cdk -a assembly-PipelineStage deploy PipelineStage/PipelineStack --require-approval=never --verbose'), }, }, })), From fc91fa84b6baf1d0a4d6e94e754b356e2274f4eb Mon Sep 17 00:00:00 2001 From: Niranjan Jayakar Date: Fri, 21 May 2021 12:34:36 +0100 Subject: [PATCH 078/134] chore(cdk): increase timeout for all bootstrapping integ tests (#14819) In a previous commit - 8395d9d1 - the timeout for a single test was increased to 1 hour since it was noticed to fail consistently. Since then, other tests have also failed intermittently due to CloudFormation actions taking longer than expected in some regions. Increase the timeout for all tests to 1 hour when executing in CodeBuild. To keep the dev-test cycle reasonable, maintain a 10 minute timeout when developing outside of CodeBuild. --- .../aws-cdk/test/integ/cli/bootstrapping.integtest.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/aws-cdk/test/integ/cli/bootstrapping.integtest.ts b/packages/aws-cdk/test/integ/cli/bootstrapping.integtest.ts index 37b0a803fb81e..6fce054dbbf10 100644 --- a/packages/aws-cdk/test/integ/cli/bootstrapping.integtest.ts +++ b/packages/aws-cdk/test/integ/cli/bootstrapping.integtest.ts @@ -3,7 +3,11 @@ import * as path from 'path'; import { randomString, withDefaultFixture } from '../helpers/cdk'; import { integTest } from '../helpers/test-helpers'; -jest.setTimeout(600_000); +const timeout = process.env.CODEBUILD_BUILD_ID ? // if the process is running in CodeBuild + 3_600_000 : // 1 hour + 600_000; // 10 minutes +jest.setTimeout(timeout); +process.stdout.write(`bootstrapping.integtest.ts: Setting jest time out to ${timeout} ms`); integTest('can bootstrap without execution', withDefaultFixture(async (fixture) => { const bootstrapStackName = fixture.bootstrapStackName; @@ -54,8 +58,7 @@ integTest('upgrade legacy bootstrap stack to new bootstrap stack while in use', '--force', ], }); -}), 3_600_000, // Observed in eu-west-2 that CF update takes over 10 minutes for this test. -); +})); integTest('can and deploy if omitting execution policies', withDefaultFixture(async (fixture) => { const bootstrapStackName = fixture.bootstrapStackName; From 457f7377b841068b5aceea998af06eaa7812272f Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Fri, 21 May 2021 16:04:32 +0200 Subject: [PATCH 079/134] chore: add missing `@types/aws-lambda` dependency (#14796) Follow-up to #13181 where a Lambda was added to this package. ---- *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-apigatewayv2-authorizers/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json b/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json index 58de08da038e4..7aebeb33359e4 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json @@ -72,6 +72,7 @@ "license": "Apache-2.0", "devDependencies": { "@types/jest": "^26.0.23", + "@types/aws-lambda": "^8.10.76", "@aws-cdk/aws-apigatewayv2-integrations": "0.0.0", "@aws-cdk/aws-lambda": "0.0.0", "cdk-build-tools": "0.0.0", From a652fa8506c07e4714067f50d5a3de007df60400 Mon Sep 17 00:00:00 2001 From: Matias Salgado <82465985+matias-salgado@users.noreply.github.com> Date: Fri, 21 May 2021 11:32:03 -0300 Subject: [PATCH 080/134] docs(cognito): correct App Clients section (#14717) ---- *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-cognito/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-cognito/README.md b/packages/@aws-cdk/aws-cognito/README.md index a018457eff368..0c9c77ce42fdf 100644 --- a/packages/@aws-cdk/aws-cognito/README.md +++ b/packages/@aws-cdk/aws-cognito/README.md @@ -491,7 +491,7 @@ new cognito.UserPoolClient(this, 'customer-app-client', { ``` Clients can be configured with authentication flows. Authentication flows allow users on a client to be authenticated -with a user pool. Cognito user pools provide several several different types of authentication, such as, SRP (Secure +with a user pool. Cognito user pools provide several different types of authentication, such as, SRP (Secure Remote Password) authentication, username-and-password authentication, etc. Learn more about this at [UserPool Authentication Flow](https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html). From f47d5cb48e641515b503bae092cd32071dae2ed9 Mon Sep 17 00:00:00 2001 From: Niranjan Jayakar Date: Fri, 21 May 2021 15:58:11 +0100 Subject: [PATCH 081/134] fix(lambda): changing reserved concurrency fails lambda version deployment (#14586) Function hash calculations include properties such as `ReservedConcurrentExecutions`, `Tags` and `DependsOn` which do not affect lambda versions. The change is to only use specific properties in the `Properties` section of `AWS::Lambda::Function` for hash calculation. We record and classify the current set of properties for the resource type `AWS::Lambda::Function`. Any unclassified property will error out. To support escape hatches, a `Function.classifyVersionProperty()` API is introduced. However, this change causes logical for the `Version` resource to change. Since the lambda service does not allow duplicate versions, users will see deployment failures without additional changes to the `Function` resouce. Hence, keep all this logic behind a feature flag that users can opt into. fixes #11537 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- ...ribution-lambda-cross-region.expected.json | 16 +-- .../integ.distribution-lambda.expected.json | 6 +- .../integ.deployment-group.expected.json | 4 +- packages/@aws-cdk/aws-lambda/README.md | 113 +++++++++++++----- .../@aws-cdk/aws-lambda/lib/function-hash.ts | 86 +++++++++++-- packages/@aws-cdk/aws-lambda/lib/function.ts | 17 +++ packages/@aws-cdk/aws-lambda/package.json | 1 - .../aws-lambda/test/function-hash.test.ts | 97 ++++++++++++++- .../test/integ.current-version.expected.json | 6 +- packages/@aws-cdk/cx-api/lib/features.ts | 14 +++ 10 files changed, 304 insertions(+), 56 deletions(-) diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-lambda-cross-region.expected.json b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-lambda-cross-region.expected.json index d95e038c2c890..f02507fb5fa05 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-lambda-cross-region.expected.json +++ b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-lambda-cross-region.expected.json @@ -12,7 +12,7 @@ }, "Region": "us-east-1", "ParameterName": "/cdk/EdgeFunctionArn/eu-west-1/integ-distribution-lambda-cross-region/Lambda", - "RefreshToken": "LambdaCurrentVersionDF706F6A97fb843e9bd06fcd2bb15eeace80e13e" + "RefreshToken": "LambdaCurrentVersionDF706F6A25bf7d67df4eb614ea2e1ea69c8759b6" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -138,7 +138,7 @@ }, "Region": "us-east-1", "ParameterName": "/cdk/EdgeFunctionArn/eu-west-1/integ-distribution-lambda-cross-region/Lambda2", - "RefreshToken": "Lambda2CurrentVersion72012B74b9eef8becb98501bc795baca3c6169c4" + "RefreshToken": "Lambda2CurrentVersion72012B74ae3cccfdad93f3cb5c0d547683bcfea9" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -263,7 +263,7 @@ "LambdaServiceRoleA8ED4D3B" ] }, - "LambdaCurrentVersionDF706F6A97fb843e9bd06fcd2bb15eeace80e13e": { + "LambdaCurrentVersionDF706F6A25bf7d67df4eb614ea2e1ea69c8759b6": { "Type": "AWS::Lambda::Version", "Properties": { "FunctionName": { @@ -276,7 +276,7 @@ "Properties": { "Type": "String", "Value": { - "Ref": "LambdaCurrentVersionDF706F6A97fb843e9bd06fcd2bb15eeace80e13e" + "Ref": "LambdaCurrentVersionDF706F6A25bf7d67df4eb614ea2e1ea69c8759b6" }, "Name": "/cdk/EdgeFunctionArn/eu-west-1/integ-distribution-lambda-cross-region/Lambda" } @@ -289,7 +289,7 @@ }, "FunctionVersion": { "Fn::GetAtt": [ - "LambdaCurrentVersionDF706F6A97fb843e9bd06fcd2bb15eeace80e13e", + "LambdaCurrentVersionDF706F6A25bf7d67df4eb614ea2e1ea69c8759b6", "Version" ] }, @@ -357,7 +357,7 @@ "Lambda2ServiceRole31A072E1" ] }, - "Lambda2CurrentVersion72012B74b9eef8becb98501bc795baca3c6169c4": { + "Lambda2CurrentVersion72012B74ae3cccfdad93f3cb5c0d547683bcfea9": { "Type": "AWS::Lambda::Version", "Properties": { "FunctionName": { @@ -370,7 +370,7 @@ "Properties": { "Type": "String", "Value": { - "Ref": "Lambda2CurrentVersion72012B74b9eef8becb98501bc795baca3c6169c4" + "Ref": "Lambda2CurrentVersion72012B74ae3cccfdad93f3cb5c0d547683bcfea9" }, "Name": "/cdk/EdgeFunctionArn/eu-west-1/integ-distribution-lambda-cross-region/Lambda2" } @@ -383,7 +383,7 @@ }, "FunctionVersion": { "Fn::GetAtt": [ - "Lambda2CurrentVersion72012B74b9eef8becb98501bc795baca3c6169c4", + "Lambda2CurrentVersion72012B74ae3cccfdad93f3cb5c0d547683bcfea9", "Version" ] }, diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-lambda.expected.json b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-lambda.expected.json index fde47299f760e..44ad319c62a4b 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-lambda.expected.json +++ b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-lambda.expected.json @@ -57,7 +57,7 @@ "LambdaServiceRoleA8ED4D3B" ] }, - "LambdaCurrentVersionDF706F6A97fb843e9bd06fcd2bb15eeace80e13e": { + "LambdaCurrentVersionDF706F6A25bf7d67df4eb614ea2e1ea69c8759b6": { "Type": "AWS::Lambda::Version", "Properties": { "FunctionName": { @@ -76,7 +76,7 @@ { "EventType": "origin-request", "LambdaFunctionARN": { - "Ref": "LambdaCurrentVersionDF706F6A97fb843e9bd06fcd2bb15eeace80e13e" + "Ref": "LambdaCurrentVersionDF706F6A25bf7d67df4eb614ea2e1ea69c8759b6" } } ], @@ -99,4 +99,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-group.expected.json b/packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-group.expected.json index 2d8f66303edee..2d9262dcf3212 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-group.expected.json +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-group.expected.json @@ -85,7 +85,7 @@ "HandlerServiceRoleFCDC14AE" ] }, - "HandlerCurrentVersion93FB80BFa1c6e812d19e0a94f85605af18cacb0c": { + "HandlerCurrentVersion93FB80BFb2de9794fd0f0df5e5c01c16ba4b05cf": { "Type": "AWS::Lambda::Version", "Properties": { "FunctionName": { @@ -101,7 +101,7 @@ }, "FunctionVersion": { "Fn::GetAtt": [ - "HandlerCurrentVersion93FB80BFa1c6e812d19e0a94f85605af18cacb0c", + "HandlerCurrentVersion93FB80BFb2de9794fd0f0df5e5c01c16ba4b05cf", "Version" ] }, diff --git a/packages/@aws-cdk/aws-lambda/README.md b/packages/@aws-cdk/aws-lambda/README.md index a12e1bc923a4d..7e086632286ce 100644 --- a/packages/@aws-cdk/aws-lambda/README.md +++ b/packages/@aws-cdk/aws-lambda/README.md @@ -170,7 +170,7 @@ fn.addPermission('my-service Invocation', { }); ``` -## Versions and Aliases +## Versions You can use [versions](https://docs.aws.amazon.com/lambda/latest/dg/configuration-versions.html) @@ -185,11 +185,39 @@ The function version includes the following information: * All of the function settings, including the environment variables. * A unique Amazon Resource Name (ARN) to identify this version of the function. -You can define one or more -[aliases](https://docs.aws.amazon.com/lambda/latest/dg/configuration-aliases.html) -for your AWS Lambda function. A Lambda alias is like a pointer to a specific -Lambda function version. Users can access the function version using the alias -ARN. +You could create a version to your lambda function using the `Version` construct. + +```ts +const fn = new Function(this, 'MyFunction', ...); +const version = new Version(this, 'MyVersion', { + lambda: fn, +}); +``` + +The major caveat to know here is that a function version must always point to a +specific 'version' of the function. When the function is modified, the version +will continue to point to the 'then version' of the function. + +One way to ensure that the `lambda.Version` always points to the latest version +of your `lambda.Function` is to set an environment variable which changes at +least as often as your code does. This makes sure the function always has the +latest code. For instance - + +```ts +const codeVersion = "stringOrMethodToGetCodeVersion"; +const fn = new lambda.Function(this, 'MyFunction', { + environment: { + 'CodeVersionString': codeVersion + } +}); +``` + +The `fn.latestVersion` property returns a `lambda.IVersion` which represents +the `$LATEST` pseudo-version. + +However, most AWS services require a specific AWS Lambda version, +and won't allow you to use `$LATEST`. Therefore, you would normally want +to use `lambda.currentVersion`. The `fn.currentVersion` property can be used to obtain a `lambda.Version` resource that represents the AWS Lambda function defined in your application. @@ -197,24 +225,56 @@ Any change to your function's code or configuration will result in the creation of a new version resource. You can specify options for this version through the `currentVersionOptions` property. -> The `currentVersion` property is only supported when your AWS Lambda function -> uses either `lambda.Code.fromAsset` or `lambda.Code.fromInline`. Other types -> of code providers (such as `lambda.Code.fromBucket`) require that you define a -> `lambda.Version` resource directly since the CDK is unable to determine if -> their contents had changed. -> -> An alternative to defining a `lambda.Version` is to set an environment variable -> which changes at least as often as your code does. This makes sure the function -> always has the latest code. -> -> ```ts -> const codeVersion = "stringOrMethodToGetCodeVersion"; -> const fn = new lambda.Function(this, 'MyFunction', { -> environment: { -> 'CodeVersionString': codeVersion -> } -> }); -> ``` +NOTE: The `currentVersion` property is only supported when your AWS Lambda function +uses either `lambda.Code.fromAsset` or `lambda.Code.fromInline`. Other types +of code providers (such as `lambda.Code.fromBucket`) require that you define a +`lambda.Version` resource directly since the CDK is unable to determine if +their contents had changed. + +### `currentVersion`: Updated hashing logic + +To produce a new lambda version each time the lambda function is modified, the +`currentVersion` property under the hood, computes a new logical id based on the +properties of the function. This informs CloudFormation that a new +`AWS::Lambda::Version` resource should be created pointing to the updated Lambda +function. + +However, a bug was introduced in this calculation that caused the logical id to +change when it was not required (ex: when the Function's `Tags` property, or +when the `DependsOn` clause was modified). This caused the deployment to fail +since the Lambda service does not allow creating duplicate versions. + +This has been fixed in the AWS CDK but *existing* users need to opt-in via a +[feature flag]. Users who have run `cdk init` since this fix will be opted in, +by default. + +Existing users will need to enable the [feature flag] +`@aws-cdk/aws-lambda:recognizeVersionProps`. Since CloudFormation does not +allow duplicate versions, they will also need to make some modification to +their function so that a new version can be created. Any trivial change such as +a whitespace change in the code or a no-op environment variable will suffice. + +When the new logic is in effect, you may rarely come across the following error: +`The following properties are not recognized as version properties`. This will +occur, typically when [property overrides] are used, when a new property +introduced in `AWS::Lambda::Function` is used that CDK is still unaware of. + +To overcome this error, use the API `Function.classifyVersionProperty()` to +record whether a new version should be generated when this property is changed. +This can be typically determined by checking whether the property can be +modified using the *[UpdateFunctionConfiguration]* API or not. + +[feature flag]: https://docs.aws.amazon.com/cdk/latest/guide/featureflags.html +[property overrides]: https://docs.aws.amazon.com/cdk/latest/guide/cfn_layer.html#cfn_layer_raw +[UpdateFunctionConfiguration]: https://docs.aws.amazon.com/lambda/latest/dg/API_UpdateFunctionConfiguration.html + +## Aliases + +You can define one or more +[aliases](https://docs.aws.amazon.com/lambda/latest/dg/configuration-aliases.html) +for your AWS Lambda function. A Lambda alias is like a pointer to a specific +Lambda function version. Users can access the function version using the alias +ARN. The `version.addAlias()` method can be used to define an AWS Lambda alias that points to a specific version. @@ -236,11 +296,6 @@ const fn = new lambda.Function(this, 'MyFunction', { fn.currentVersion.addAlias('live'); ``` -> NOTE: The `fn.latestVersion` property returns a `lambda.IVersion` which -> represents the `$LATEST` pseudo-version. Most AWS services require a specific -> AWS Lambda version, and won't allow you to use `$LATEST`. Therefore, you would -> normally want to use `lambda.currentVersion`. - ## Layers The `lambda.LayerVersion` class can be used to define Lambda layers and manage diff --git a/packages/@aws-cdk/aws-lambda/lib/function-hash.ts b/packages/@aws-cdk/aws-lambda/lib/function-hash.ts index 555b7b4b852ba..c651aee68cd4d 100644 --- a/packages/@aws-cdk/aws-lambda/lib/function-hash.ts +++ b/packages/@aws-cdk/aws-lambda/lib/function-hash.ts @@ -1,5 +1,6 @@ import * as crypto from 'crypto'; -import { CfnResource, Stack } from '@aws-cdk/core'; +import { CfnResource, FeatureFlags, Stack } from '@aws-cdk/core'; +import { LAMBDA_RECOGNIZE_VERSION_PROPS } from '@aws-cdk/cx-api'; import { Function as LambdaFunction } from './function'; export function calculateFunctionHash(fn: LambdaFunction) { @@ -9,8 +10,24 @@ export function calculateFunctionHash(fn: LambdaFunction) { // render the cloudformation resource from this function const config = stack.resolve((functionResource as any)._toCloudFormation()); - sortProperties(config); - const stringifiedConfig = JSON.stringify(config); + // config is of the shape: { Resources: { LogicalId: { Type: 'Function', Properties: { ... } }}} + const resources = config.Resources; + const resourceKeys = Object.keys(resources); + if (resourceKeys.length !== 1) { + throw new Error(`Expected one rendered CloudFormation resource but found ${resourceKeys.length}`); + } + const logicalId = resourceKeys[0]; + const properties = resources[logicalId].Properties; + + let stringifiedConfig; + if (FeatureFlags.of(fn).isEnabled(LAMBDA_RECOGNIZE_VERSION_PROPS)) { + const updatedProps = sortProperties(filterUsefulKeys(properties)); + stringifiedConfig = JSON.stringify(updatedProps); + } else { + const sorted = sortProperties(properties); + config.Resources[logicalId].Properties = sorted; + stringifiedConfig = JSON.stringify(config); + } const hash = crypto.createHash('md5'); hash.update(stringifiedConfig); @@ -23,11 +40,62 @@ export function trimFromStart(s: string, maxLength: number) { return s.substring(newStart); } -function sortProperties(templateObject: any): void { - // templateObject is of the shape: { Resources: { LogicalId: { Type: 'Function', Properties: { ... } }}} - const resources = templateObject.Resources; - const logicalId = Object.keys(resources)[0]; - const properties = resources[logicalId].Properties; +/* + * The list of properties found in CfnFunction (or AWS::Lambda::Function). + * They are classified as "locked" to a Function Version or not. + * When a property is locked, any change to that property will not take effect on previously created Versions. + * Instead, a new Version must be generated for the change to take effect. + * Similarly, if a property that's not locked to a Version is modified, a new Version + * must not be generated. + * + * Adding a new property to this list - If the property is part of the UpdateFunctionConfiguration + * API, then it must be classified as true, otherwise false. + * See https://docs.aws.amazon.com/lambda/latest/dg/API_UpdateFunctionConfiguration.html + */ +const VERSION_LOCKED: { [key: string]: boolean } = { + // locked to the version + Code: true, + DeadLetterConfig: true, + Description: true, + Environment: true, + FileSystemConfigs: true, + FunctionName: true, + Handler: true, + ImageConfig: true, + KmsKeyArn: true, + Layers: true, + MemorySize: true, + PackageType: true, + Role: true, + Runtime: true, + Timeout: true, + TracingConfig: true, + VpcConfig: true, + + // not locked to the version + CodeSigningConfigArn: false, + ReservedConcurrentExecutions: false, + Tags: false, +}; + +function filterUsefulKeys(properties: any) { + const versionProps = { ...VERSION_LOCKED, ...LambdaFunction._VER_PROPS }; + const unclassified = Object.entries(properties) + .filter(([k, v]) => v != null && !Object.keys(versionProps).includes(k)) + .map(([k, _]) => k); + if (unclassified.length > 0) { + throw new Error(`The following properties are not recognized as version properties: [${unclassified}].` + + ' See the README of the aws-lambda module to learn more about this and to fix it.'); + } + const notLocked = Object.entries(versionProps).filter(([_, v]) => !v).map(([k, _]) => k); + notLocked.forEach(p => delete properties[p]); + + const ret: { [key: string]: any } = {}; + Object.entries(properties).filter(([k, _]) => versionProps[k]).forEach(([k, v]) => ret[k] = v); + return ret; +} + +function sortProperties(properties: any) { const ret: any = {}; // We take all required properties in the order that they were historically, // to make sure the hash we calculate is stable. @@ -44,5 +112,5 @@ function sortProperties(templateObject: any): void { ret[property] = properties[property]; } } - templateObject.Resources[logicalId].Properties = ret; + return ret; } diff --git a/packages/@aws-cdk/aws-lambda/lib/function.ts b/packages/@aws-cdk/aws-lambda/lib/function.ts index 0565ba2a98b1e..8bc6704c1321d 100644 --- a/packages/@aws-cdk/aws-lambda/lib/function.ts +++ b/packages/@aws-cdk/aws-lambda/lib/function.ts @@ -383,6 +383,23 @@ export class Function extends FunctionBase { return this._currentVersion; } + /** @internal */ + public static _VER_PROPS: { [key: string]: boolean } = {}; + + /** + * Record whether specific properties in the `AWS::Lambda::Function` resource should + * also be associated to the Version resource. + * See 'currentVersion' section in the module README for more details. + * @param propertyName The property to classify + * @param locked whether the property should be associated to the version or not. + */ + public static classifyVersionProperty(propertyName: string, locked: boolean) { + this._VER_PROPS[propertyName] = locked; + } + + /** + * Import a lambda function into the CDK using its ARN + */ public static fromFunctionArn(scope: Construct, id: string, functionArn: string): IFunction { return Function.fromFunctionAttributes(scope, id, { functionArn }); } diff --git a/packages/@aws-cdk/aws-lambda/package.json b/packages/@aws-cdk/aws-lambda/package.json index dc5d073fceaf7..57cd9274d617d 100644 --- a/packages/@aws-cdk/aws-lambda/package.json +++ b/packages/@aws-cdk/aws-lambda/package.json @@ -145,7 +145,6 @@ "docs-public-apis:@aws-cdk/aws-lambda.RuntimeFamily.NODEJS", "docs-public-apis:@aws-cdk/aws-lambda.Alias.lambda", "docs-public-apis:@aws-cdk/aws-lambda.Alias.fromAliasAttributes", - "docs-public-apis:@aws-cdk/aws-lambda.Function.fromFunctionArn", "docs-public-apis:@aws-cdk/aws-lambda.FunctionBase", "docs-public-apis:@aws-cdk/aws-lambda.QualifiedFunctionBase", "docs-public-apis:@aws-cdk/aws-lambda.QualifiedFunctionBase.lambda", diff --git a/packages/@aws-cdk/aws-lambda/test/function-hash.test.ts b/packages/@aws-cdk/aws-lambda/test/function-hash.test.ts index 97ce20f0a77b8..b1b4ab5200ea4 100644 --- a/packages/@aws-cdk/aws-lambda/test/function-hash.test.ts +++ b/packages/@aws-cdk/aws-lambda/test/function-hash.test.ts @@ -1,6 +1,7 @@ import '@aws-cdk/assert-internal/jest'; import * as path from 'path'; -import { CfnOutput, Stack } from '@aws-cdk/core'; +import { App, CfnOutput, CfnResource, Stack } from '@aws-cdk/core'; +import { LAMBDA_RECOGNIZE_VERSION_PROPS } from '@aws-cdk/cx-api'; import * as lambda from '../lib'; import { calculateFunctionHash, trimFromStart } from '../lib/function-hash'; @@ -180,4 +181,98 @@ describe('function hash', () => { expect(calculateFunctionHash(fn1)).toEqual(calculateFunctionHash(fn2)); }); }); + + describe('corrected function hash', () => { + let app: App; + beforeEach(() => { + app = new App({ context: { [LAMBDA_RECOGNIZE_VERSION_PROPS]: true } }); + }); + + test('DependsOn does not impact function hash', () => { + const stack1 = new Stack(app, 'Stack1'); + const fn1 = new lambda.Function(stack1, 'MyFunction1', { + runtime: lambda.Runtime.NODEJS_12_X, + code: lambda.Code.fromAsset(path.join(__dirname, 'handler.zip')), + handler: 'index.handler', + }); + + const stack2 = new Stack(app, 'Stack2'); + const fn2 = new lambda.Function(stack2, 'MyFunction1', { + runtime: lambda.Runtime.NODEJS_12_X, + code: lambda.Code.fromAsset(path.join(__dirname, 'handler.zip')), + handler: 'index.handler', + }); + const res = new CfnResource(stack2, 'MyResource', { + type: 'AWS::Foo::Bar', + properties: { + Name: 'Value', + }, + }); + fn2.node.addDependency(res); + + expect(calculateFunctionHash(fn1)).toEqual('e5235e3cb7a9b70c42c1a665a3ebd77c'); + expect(calculateFunctionHash(fn1)).toEqual(calculateFunctionHash(fn2)); + }); + + test('properties not locked to the version do not impact function hash', () => { + const stack1 = new Stack(app, 'Stack1'); + const fn1 = new lambda.Function(stack1, 'MyFunction', { + runtime: lambda.Runtime.NODEJS_12_X, + code: lambda.Code.fromAsset(path.join(__dirname, 'handler.zip')), + handler: 'index.handler', + }); + + const stack2 = new Stack(app, 'Stack2'); + const fn2 = new lambda.Function(stack2, 'MyFunction', { + runtime: lambda.Runtime.NODEJS_12_X, + code: lambda.Code.fromAsset(path.join(__dirname, 'handler.zip')), + handler: 'index.handler', + + reservedConcurrentExecutions: 5, // property not locked to the version + }); + + // expect(calculateFunctionHash(fn1)).toEqual('b0d8729d597bdde2d79312fbf619c974'); + expect(calculateFunctionHash(fn1)).toEqual(calculateFunctionHash(fn2)); + }); + + test('unclassified property throws an error', () => { + const stack = new Stack(app); + const fn1 = new lambda.Function(stack, 'MyFunction1', { + runtime: lambda.Runtime.NODEJS_12_X, + code: lambda.Code.fromAsset(path.join(__dirname, 'handler.zip')), + handler: 'index.handler', + }); + (fn1.node.defaultChild as CfnResource).addPropertyOverride('UnclassifiedProp', 'Value'); + + expect(() => calculateFunctionHash(fn1)).toThrow(/properties are not recognized/); + }); + + test('manual classification as version locked', () => { + const stack = new Stack(app); + const fn1 = new lambda.Function(stack, 'MyFunction1', { + runtime: lambda.Runtime.NODEJS_12_X, + code: lambda.Code.fromAsset(path.join(__dirname, 'handler.zip')), + handler: 'index.handler', + }); + + const original = calculateFunctionHash(fn1); + lambda.Function.classifyVersionProperty('UnclassifiedProp', true); + (fn1.node.defaultChild as CfnResource).addPropertyOverride('UnclassifiedProp', 'Value'); + expect(calculateFunctionHash(fn1)).not.toEqual(original); + }); + + test('manual classification as not version locked', () => { + const stack = new Stack(app); + const fn1 = new lambda.Function(stack, 'MyFunction1', { + runtime: lambda.Runtime.NODEJS_12_X, + code: lambda.Code.fromAsset(path.join(__dirname, 'handler.zip')), + handler: 'index.handler', + }); + + const original = calculateFunctionHash(fn1); + lambda.Function.classifyVersionProperty('UnclassifiedProp', false); + (fn1.node.defaultChild as CfnResource).addPropertyOverride('UnclassifiedProp', 'Value'); + expect(calculateFunctionHash(fn1)).toEqual(original); + }); + }); }); diff --git a/packages/@aws-cdk/aws-lambda/test/integ.current-version.expected.json b/packages/@aws-cdk/aws-lambda/test/integ.current-version.expected.json index bf269c8f6d555..86a20d58e17c1 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.current-version.expected.json +++ b/packages/@aws-cdk/aws-lambda/test/integ.current-version.expected.json @@ -85,7 +85,7 @@ "MyLambdaServiceRole4539ECB6" ] }, - "MyLambdaCurrentVersionE7A382CC721de083c6b4b6360a9c534b79eb610e": { + "MyLambdaCurrentVersionE7A382CC132baf6493c3210af4c8c0e36b416660": { "Type": "AWS::Lambda::Version", "Properties": { "FunctionName": { @@ -103,7 +103,7 @@ }, "Qualifier": { "Fn::GetAtt": [ - "MyLambdaCurrentVersionE7A382CC721de083c6b4b6360a9c534b79eb610e", + "MyLambdaCurrentVersionE7A382CC132baf6493c3210af4c8c0e36b416660", "Version" ] }, @@ -118,7 +118,7 @@ }, "FunctionVersion": { "Fn::GetAtt": [ - "MyLambdaCurrentVersionE7A382CC721de083c6b4b6360a9c534b79eb610e", + "MyLambdaCurrentVersionE7A382CC132baf6493c3210af4c8c0e36b416660", "Version" ] }, diff --git a/packages/@aws-cdk/cx-api/lib/features.ts b/packages/@aws-cdk/cx-api/lib/features.ts index a2525fafa71c0..92cce54f2325c 100644 --- a/packages/@aws-cdk/cx-api/lib/features.ts +++ b/packages/@aws-cdk/cx-api/lib/features.ts @@ -141,6 +141,18 @@ export const APIGATEWAY_USAGEPLANKEY_ORDERINSENSITIVE_ID = '@aws-cdk/aws-apigate * Encryption can also be configured explicitly using the `encrypted` property. */ export const EFS_DEFAULT_ENCRYPTION_AT_REST = '@aws-cdk/aws-efs:defaultEncryptionAtRest'; + +/** + * Enable this feature flag to opt in to the updated logical id calculation for Lambda Version created using the + * `fn.currentVersion`. + * + * The previous calculation incorrectly considered properties of the `AWS::Lambda::Function` resource that did + * not constitute creating a new Version. + * + * See 'currentVersion' section in the aws-lambda module's README for more details. + */ +export const LAMBDA_RECOGNIZE_VERSION_PROPS = '@aws-cdk/aws-lambda:recognizeVersionProps'; + /** * This map includes context keys and values for feature flags that enable * capabilities "from the future", which we could not introduce as the default @@ -166,6 +178,7 @@ export const FUTURE_FLAGS: { [key: string]: any } = { [ECS_REMOVE_DEFAULT_DESIRED_COUNT]: true, [RDS_LOWERCASE_DB_IDENTIFIER]: true, [EFS_DEFAULT_ENCRYPTION_AT_REST]: true, + [LAMBDA_RECOGNIZE_VERSION_PROPS]: true, // We will advertise this flag when the feature is complete // [NEW_STYLE_STACK_SYNTHESIS_CONTEXT]: 'true', @@ -195,6 +208,7 @@ const FUTURE_FLAGS_DEFAULTS: { [key: string]: boolean } = { [ECS_REMOVE_DEFAULT_DESIRED_COUNT]: false, [RDS_LOWERCASE_DB_IDENTIFIER]: false, [EFS_DEFAULT_ENCRYPTION_AT_REST]: false, + [LAMBDA_RECOGNIZE_VERSION_PROPS]: false, }; export function futureFlagDefault(flag: string): boolean { From 32ebe7942495f9be794941219dd48fc25680d3ee Mon Sep 17 00:00:00 2001 From: Jonathan Goldwasser Date: Fri, 21 May 2021 19:52:59 +0200 Subject: [PATCH 082/134] chore(dynamodb): validate that a Stack does not contain more than 10 global Tables (#14054) The custom resource implementation uses IAM managed policies. There's a limit of 10 managed policies per role in IAM. Throw if we reach this limit. The long term solution is to implement a L2 for [`AWS::DynamoDB::GlobalTable`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-globaltable.html). ---- *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/README.md | 3 ++ .../aws-dynamodb/lib/replica-provider.ts | 15 ++++++++++ .../aws-dynamodb/test/dynamodb.test.ts | 28 +++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/packages/@aws-cdk/aws-dynamodb/README.md b/packages/@aws-cdk/aws-dynamodb/README.md index 2f43d83f88c4f..c94bce5101c35 100644 --- a/packages/@aws-cdk/aws-dynamodb/README.md +++ b/packages/@aws-cdk/aws-dynamodb/README.md @@ -120,6 +120,9 @@ const globalTable = new dynamodb.Table(this, 'Table', { }); ``` +A maximum of 10 tables with replication can be added to a stack. +Consider splitting your tables across multiple stacks if your reach this limit. + ## Encryption All user data stored in Amazon DynamoDB is fully encrypted at rest. When creating a new table, you can choose to encrypt using the following customer master keys (CMK) to encrypt your table: diff --git a/packages/@aws-cdk/aws-dynamodb/lib/replica-provider.ts b/packages/@aws-cdk/aws-dynamodb/lib/replica-provider.ts index d984c4bb7b3ff..ced7f4abaa409 100644 --- a/packages/@aws-cdk/aws-dynamodb/lib/replica-provider.ts +++ b/packages/@aws-cdk/aws-dynamodb/lib/replica-provider.ts @@ -27,10 +27,25 @@ export class ReplicaProvider extends NestedStack { */ public static getOrCreate(scope: Construct, props: ReplicaProviderProps = {}) { const stack = Stack.of(scope); + this.checkManagedPoliciesLimit(stack); const uid = '@aws-cdk/aws-dynamodb.ReplicaProvider'; return stack.node.tryFindChild(uid) as ReplicaProvider ?? new ReplicaProvider(stack, uid, props); } + // Map of getOrCreate() calls per stack + private static getOrCreateCalls = new Map(); + + private static checkManagedPoliciesLimit(stack: Stack): void { + // The custom resource implementation uses IAM managed policies. There's + // a limit of 10 managed policies per role in IAM. Throw if we reach this + // limit. + const calls = this.getOrCreateCalls.get(stack.stackName) ?? 0; + if (calls >= 10) { + throw new Error('You currently cannot have more than 10 global tables in a single stack. Consider splitting your tables across multiple stacks.'); + } + this.getOrCreateCalls.set(stack.stackName, calls + 1); + } + /** * The custom resource provider. */ diff --git a/packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts index 74d18e2dd28e3..3c7de6e7c55b6 100644 --- a/packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts +++ b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts @@ -20,6 +20,7 @@ import { Operation, CfnTable, } from '../lib'; +import { ReplicaProvider } from '../lib/replica-provider'; jest.mock('@aws-cdk/custom-resources'); @@ -2281,6 +2282,10 @@ describe('import', () => { }); describe('global', () => { + beforeEach(() => { + (ReplicaProvider as any).getOrCreateCalls.clear(); + }); + test('create replicas', () => { // GIVEN const stack = new Stack(); @@ -2827,6 +2832,29 @@ describe('global', () => { totalTimeout: Duration.hours(1), })); }); + + test('throws when reaching the maximum table with replication limit', () => { + // GIVEN + const stack = new Stack(); + for (let i = 1; i <= 10; i++) { + new Table(stack, `Table${i}`, { + partitionKey: { + name: 'id', + type: AttributeType.STRING, + }, + replicationRegions: ['eu-central-1'], + }); + } + + // THEN + expect(() => new Table(stack, 'OverLimit', { + partitionKey: { + name: 'id', + type: AttributeType.STRING, + }, + replicationRegions: ['eu-central-1'], + })).toThrow(/cannot have more than 10 global tables/); + }); }); test('L1 inside L2 expects removalpolicy to have been set', () => { From 27092c184537aafc9989533c840fb7a972d70933 Mon Sep 17 00:00:00 2001 From: Niranjan Jayakar Date: Sun, 23 May 2021 16:06:54 +0100 Subject: [PATCH 083/134] chore: remove prlinter module and merge it with prlint (#14825) Previously, the actual linter is modeled as a regular npm package named 'prlint' and a shim module named 'prlinter' existed that exposed this as a Github action package. This extra layer of indirection is not very useful until we have another use case where the linter will be used outside of Github Actions. For the forseeable future, this will be only used via Github Actions. Remove this extra layer to simplify. --- .github/actions/prlinter/LICENSE | 201 - .github/actions/prlinter/NOTICE | 2 - .github/actions/prlinter/index.js | 19 - .github/actions/prlinter/package-lock.json | 773 --- .github/actions/prlinter/package.json | 19 - .github/workflows/pr-linter.yml | 5 +- tools/prlint/README.md | 2 +- .../prlinter => tools/prlint}/action.yml | 0 tools/prlint/index.js | 149 +- tools/prlint/lint.js | 148 + tools/prlint/package-lock.json | 5267 +++++++++++++++++ tools/prlint/package.json | 4 +- .../test/{index.test.js => lint.test.js} | 2 +- 13 files changed, 5433 insertions(+), 1158 deletions(-) delete mode 100644 .github/actions/prlinter/LICENSE delete mode 100644 .github/actions/prlinter/NOTICE delete mode 100644 .github/actions/prlinter/index.js delete mode 100644 .github/actions/prlinter/package-lock.json delete mode 100644 .github/actions/prlinter/package.json rename {.github/actions/prlinter => tools/prlint}/action.yml (100%) mode change 100755 => 100644 tools/prlint/index.js create mode 100755 tools/prlint/lint.js create mode 100644 tools/prlint/package-lock.json rename tools/prlint/test/{index.test.js => lint.test.js} (98%) diff --git a/.github/actions/prlinter/LICENSE b/.github/actions/prlinter/LICENSE deleted file mode 100644 index 28e4bdcec77ec..0000000000000 --- a/.github/actions/prlinter/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/.github/actions/prlinter/NOTICE b/.github/actions/prlinter/NOTICE deleted file mode 100644 index 5fc3826926b5b..0000000000000 --- a/.github/actions/prlinter/NOTICE +++ /dev/null @@ -1,2 +0,0 @@ -AWS Cloud Development Kit (AWS CDK) -Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/.github/actions/prlinter/index.js b/.github/actions/prlinter/index.js deleted file mode 100644 index 12e213bb0ebd4..0000000000000 --- a/.github/actions/prlinter/index.js +++ /dev/null @@ -1,19 +0,0 @@ -const core = require('@actions/core'); -const github = require('@actions/github'); -const linter = require('prlint') - -async function run() { - const number = github.context.issue.number; - - try { - - await linter.validatePr(number); - - } catch (error) { - - core.setFailed(error.message); - } - -} - -run() diff --git a/.github/actions/prlinter/package-lock.json b/.github/actions/prlinter/package-lock.json deleted file mode 100644 index 551e0261530ee..0000000000000 --- a/.github/actions/prlinter/package-lock.json +++ /dev/null @@ -1,773 +0,0 @@ -{ - "name": "prlinter", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@actions/core": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.2.tgz", - "integrity": "sha512-IbCx7oefq+Gi6FWbSs2Fnw8VkEI6Y4gvjrYprY3RV//ksq/KPMlClOerJ4jRosyal6zkUIc8R9fS/cpRMlGClg==" - }, - "@actions/github": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@actions/github/-/github-2.1.0.tgz", - "integrity": "sha512-G4ncMlh4pLLAvNgHUYUtpWQ1zPf/VYqmRH9oshxLabdaOOnp7i1hgSgzr2xne2YUaSND3uqemd3YYTIsm2f/KQ==", - "requires": { - "@actions/http-client": "^1.0.3", - "@octokit/graphql": "^4.3.1", - "@octokit/rest": "^16.15.0" - } - }, - "@actions/http-client": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.3.tgz", - "integrity": "sha512-wFwh1U4adB/Zsk4cc9kVqaBOHoknhp/pJQk+aWTocbAZWpIl4Zx/At83WFRLXvxB+5HVTWOACM6qjULMZfQSfw==", - "requires": { - "tunnel": "0.0.6" - } - }, - "@octokit/auth-token": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.0.tgz", - "integrity": "sha512-eoOVMjILna7FVQf96iWc3+ZtE/ZT6y8ob8ZzcqKY1ibSQCnu4O/B7pJvzMx5cyZ/RjAff6DAdEb0O0Cjcxidkg==", - "requires": { - "@octokit/types": "^2.0.0" - } - }, - "@octokit/endpoint": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-5.5.1.tgz", - "integrity": "sha512-nBFhRUb5YzVTCX/iAK1MgQ4uWo89Gu0TH00qQHoYRCsE12dWcG1OiLd7v2EIo2+tpUKPMOQ62QFy9hy9Vg2ULg==", - "requires": { - "@octokit/types": "^2.0.0", - "is-plain-object": "^3.0.0", - "universal-user-agent": "^4.0.0" - } - }, - "@octokit/graphql": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.3.1.tgz", - "integrity": "sha512-hCdTjfvrK+ilU2keAdqNBWOk+gm1kai1ZcdjRfB30oA3/T6n53UVJb7w0L5cR3/rhU91xT3HSqCd+qbvH06yxA==", - "requires": { - "@octokit/request": "^5.3.0", - "@octokit/types": "^2.0.0", - "universal-user-agent": "^4.0.0" - } - }, - "@octokit/request": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.3.1.tgz", - "integrity": "sha512-5/X0AL1ZgoU32fAepTfEoggFinO3rxsMLtzhlUX+RctLrusn/CApJuGFCd0v7GMFhF+8UiCsTTfsu7Fh1HnEJg==", - "requires": { - "@octokit/endpoint": "^5.5.0", - "@octokit/request-error": "^1.0.1", - "@octokit/types": "^2.0.0", - "deprecation": "^2.0.0", - "is-plain-object": "^3.0.0", - "node-fetch": "^2.3.0", - "once": "^1.4.0", - "universal-user-agent": "^4.0.0" - } - }, - "@octokit/request-error": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.2.0.tgz", - "integrity": "sha512-DNBhROBYjjV/I9n7A8kVkmQNkqFAMem90dSxqvPq57e2hBr7mNTX98y3R2zDpqMQHVRpBDjsvsfIGgBzy+4PAg==", - "requires": { - "@octokit/types": "^2.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "@octokit/rest": { - "version": "16.38.3", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.38.3.tgz", - "integrity": "sha512-Ui5W4Gzv0YHe9P3KDZAuU/BkRrT88PCuuATfWBMBf4fux4nB8th8LlyVAVnHKba1s/q4umci+sNHzoFYFujPEg==", - "requires": { - "@octokit/auth-token": "^2.4.0", - "@octokit/request": "^5.2.0", - "@octokit/request-error": "^1.0.2", - "atob-lite": "^2.0.0", - "before-after-hook": "^2.0.0", - "btoa-lite": "^1.0.0", - "deprecation": "^2.0.0", - "lodash.get": "^4.4.2", - "lodash.set": "^4.3.2", - "lodash.uniq": "^4.5.0", - "octokit-pagination-methods": "^1.1.0", - "once": "^1.4.0", - "universal-user-agent": "^4.0.0" - } - }, - "@octokit/types": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.1.1.tgz", - "integrity": "sha512-89LOYH+d/vsbDX785NOfLxTW88GjNd0lWRz1DVPVsZgg9Yett5O+3MOvwo7iHgvUwbFz0mf/yPIjBkUbs4kxoQ==", - "requires": { - "@types/node": ">= 8" - } - }, - "@types/node": { - "version": "13.5.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.5.0.tgz", - "integrity": "sha512-Onhn+z72D2O2Pb2ql2xukJ55rglumsVo1H6Fmyi8mlU9SvKdBk/pUSUAiBY/d9bAOF7VVWajX3sths/+g6ZiAQ==" - }, - "atob-lite": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-2.0.0.tgz", - "integrity": "sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY=" - }, - "before-after-hook": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.1.0.tgz", - "integrity": "sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A==" - }, - "btoa-lite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz", - "integrity": "sha1-M3dm2hWAEhD92VbCLpxokaudAzc=" - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "requires": { - "once": "^1.4.0" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "requires": { - "pump": "^3.0.0" - } - }, - "is-plain-object": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz", - "integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==", - "requires": { - "isobject": "^4.0.0" - } - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "isobject": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", - "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==" - }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" - }, - "lodash.set": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", - "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=" - }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" - }, - "macos-release": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.3.0.tgz", - "integrity": "sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA==" - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" - }, - "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "requires": { - "path-key": "^2.0.0" - } - }, - "octokit-pagination-methods": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz", - "integrity": "sha512-fZ4qZdQ2nxJvtcasX7Ghl+WlWS/d9IgnBIwFZXVNNZUmzpno91SX5bc5vuxiuKoCtK78XxGGNuSCrDC7xYB3OQ==" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "os-name": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-name/-/os-name-3.1.0.tgz", - "integrity": "sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg==", - "requires": { - "macos-release": "^2.2.0", - "windows-release": "^3.1.0" - } - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" - }, - "prlint": { - "version": "file:../../../tools/prlint", - "requires": { - "github-api": "^3.3.0", - "make-runnable": "^1.3.6" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "axios": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", - "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", - "requires": { - "follow-redirects": "1.5.10" - } - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - }, - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" - }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" - } - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "follow-redirects": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", - "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", - "requires": { - "debug": "=3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - } - } - }, - "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" - }, - "github-api": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/github-api/-/github-api-3.3.0.tgz", - "integrity": "sha512-30pABj/1ciHmlqmjnWXn+A4JL8j9qB2IcQgibrJ7euGbaNRkAj+T6QhJwjLcPx4Hxlj+BP1TcdvaQ/7resw+VA==", - "requires": { - "axios": "^0.19.0", - "debug": "^2.2.0", - "js-base64": "^2.1.9", - "utf8": "^2.1.1" - } - }, - "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" - }, - "hosted-git-info": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz", - "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==" - }, - "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" - }, - "js-base64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz", - "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==" - }, - "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "requires": { - "invert-kv": "^1.0.0" - } - }, - "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" - } - }, - "lodash.assign": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", - "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=" - }, - "make-runnable": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-runnable/-/make-runnable-1.3.6.tgz", - "integrity": "sha1-ypsdMbBvBR43Vw+3rZi8U2n5gr4=", - "requires": { - "bluebird": "^3.5.0", - "yargs": "^4.7.1" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "requires": { - "lcid": "^1.0.0" - } - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "requires": { - "error-ex": "^1.2.0" - } - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "requires": { - "pinkie-promise": "^2.0.0" - } - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" - }, - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "requires": { - "pinkie": "^2.0.0" - } - }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" - } - }, - "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" - }, - "resolve": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.0.tgz", - "integrity": "sha512-+hTmAldEGE80U2wJJDC1lebb5jWqvTYAfm3YZ1ckk1gBr0MnCqUKlwK1e+anaFljIl+F5tR5IoZcm4ZDA1zMQw==", - "requires": { - "path-parse": "^1.0.6" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" - }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==" - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "requires": { - "is-utf8": "^0.2.0" - } - }, - "utf8": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/utf8/-/utf8-2.1.2.tgz", - "integrity": "sha1-H6DZJw6b6FDZsFAn9jUZv0ZFfZY=" - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "which-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", - "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" - }, - "window-size": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz", - "integrity": "sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU=" - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - } - }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" - }, - "yargs": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz", - "integrity": "sha1-wMQpJMpKqmsObaFznfshZDn53cA=", - "requires": { - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "lodash.assign": "^4.0.3", - "os-locale": "^1.4.0", - "read-pkg-up": "^1.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^1.0.1", - "which-module": "^1.0.0", - "window-size": "^0.2.0", - "y18n": "^3.2.1", - "yargs-parser": "^2.4.1" - } - }, - "yargs-parser": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz", - "integrity": "sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ=", - "requires": { - "camelcase": "^3.0.0", - "lodash.assign": "^4.0.6" - } - } - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" - }, - "tunnel": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" - }, - "universal-user-agent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.0.tgz", - "integrity": "sha512-eM8knLpev67iBDizr/YtqkJsF3GK8gzDc6st/WKzrTuPtcsOKW/0IdL4cnMBsU69pOx0otavLWBDGTwg+dB0aA==", - "requires": { - "os-name": "^3.1.0" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "requires": { - "isexe": "^2.0.0" - } - }, - "windows-release": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.2.0.tgz", - "integrity": "sha512-QTlz2hKLrdqukrsapKsINzqMgOUpQW268eJ0OaOpJN32h272waxR9fkB9VoWRtK7uKHG5EHJcTXQBD8XZVJkFA==", - "requires": { - "execa": "^1.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - } - } -} diff --git a/.github/actions/prlinter/package.json b/.github/actions/prlinter/package.json deleted file mode 100644 index 2927c04d2fdcd..0000000000000 --- a/.github/actions/prlinter/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "prlinter", - "private": true, - "version": "0.0.0", - "description": "", - "main": "index.js", - "keywords": [], - "license": "Apache-2.0", - "author": { - "name": "Amazon Web Services", - "url": "https://aws.amazon.com", - "organization": true - }, - "dependencies": { - "@actions/core": "^1.2.2", - "@actions/github": "^2.1.0", - "prlint": "file:../../../tools/prlint" - } -} \ No newline at end of file diff --git a/.github/workflows/pr-linter.yml b/.github/workflows/pr-linter.yml index 80137c4068020..64ea53bc1ad4b 100644 --- a/.github/workflows/pr-linter.yml +++ b/.github/workflows/pr-linter.yml @@ -15,6 +15,7 @@ on: jobs: validate-pr: permissions: + contents: read pull-requests: read runs-on: ubuntu-latest steps: @@ -23,9 +24,9 @@ jobs: uses: actions/checkout@v2 - name: Install packages - run: cd .github/actions/prlinter && npm ci + run: cd tools/prlint && npm ci - name: Validate - uses: ./.github/actions/prlinter + uses: ./tools/prlint env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} diff --git a/tools/prlint/README.md b/tools/prlint/README.md index cae15583ddbaa..4466acedcbf68 100644 --- a/tools/prlint/README.md +++ b/tools/prlint/README.md @@ -1,4 +1,4 @@ -# prlint (beta) +# prlint A linter that can run various checks to validate a PR adheres to our standards. diff --git a/.github/actions/prlinter/action.yml b/tools/prlint/action.yml similarity index 100% rename from .github/actions/prlinter/action.yml rename to tools/prlint/action.yml diff --git a/tools/prlint/index.js b/tools/prlint/index.js old mode 100755 new mode 100644 index 6f8de5cb38dbb..68a26ca247b42 --- a/tools/prlint/index.js +++ b/tools/prlint/index.js @@ -1,148 +1,19 @@ -const path = require("path") -const GitHub = require("github-api") +const core = require('@actions/core'); +const github = require('@actions/github'); +const linter = require('./lint'); -const OWNER = "aws" -const REPO = "aws-cdk" -const EXEMPT_README = 'pr-linter/exempt-readme' -const EXEMPT_TEST = 'pr-linter/exempt-test' +async function run() { + const number = github.context.issue.number; -class LinterError extends Error { - constructor(message) { - super(message); - } -} - -function createGitHubClient() { - const token = process.env.GITHUB_TOKEN; - - if (token) { - console.log("Creating authenticated GitHub Client") - } else { - console.log("Creating un-authenticated GitHub Client") - } - - return new GitHub({'token': token}); -} - -function isPkgCfnspec(issue) { - return issue.title.indexOf("(cfnspec)") > -1; -} - -function isFeature(issue) { - return issue.title.startsWith("feat") -} - -function isFix(issue) { - return issue.title.startsWith("fix") -} - -function testChanged(files) { - return files.filter(f => f.filename.toLowerCase().includes("test")).length != 0; -} - -function readmeChanged(files) { - return files.filter(f => path.basename(f.filename) == "README.md").length != 0; -} - -function featureContainsReadme(issue, files) { - if (isFeature(issue) && !readmeChanged(files) && !isPkgCfnspec(issue)) { - throw new LinterError("Features must contain a change to a README file"); - }; -}; - -function featureContainsTest(issue, files) { - if (isFeature(issue) && !testChanged(files)) { - throw new LinterError("Features must contain a change to a test file"); - }; -}; - -function fixContainsTest(issue, files) { - if (isFix(issue) && !testChanged(files)) { - throw new LinterError("Fixes must contain a change to a test file"); - }; -}; + try { -function shouldExemptReadme(issue) { - return hasLabel(issue, EXEMPT_README); -} - -function shouldExemptTest(issue) { - return hasLabel(issue, EXEMPT_TEST); -} - -function hasLabel(issue, labelName) { - return issue.labels.some(function (l) { - return l.name === labelName; - }) -} - -/** - * Check that the 'BREAKING CHANGE:' note in the body is correct. - * - * Check this by looking for something that most likely was intended - * to be said note, but got misspelled as "BREAKING CHANGES:" or - * "BREAKING CHANGES(module):" - */ -function validateBreakingChangeFormat(title, body) { - const re = /^BREAKING.*$/m; - const m = re.exec(body); - if (m) { - if (!m[0].startsWith('BREAKING CHANGE: ')) { - throw new LinterError(`Breaking changes should be indicated by starting a line with 'BREAKING CHANGE: ', variations are not allowed. (found: '${m[0]}')`); - } - if (m[0].substr('BREAKING CHANGE:'.length).trim().length === 0) { - throw new LinterError("The description of the first breaking change should immediately follow the 'BREAKING CHANGE: ' clause") - } - const titleRe = /^[a-z]+\([0-9a-z-_]+\)/; - if (!titleRe.exec(title)) { - throw new LinterError("The title of this PR must specify the module name that the first breaking change should be associated to"); - } - } -} - -async function validatePr(number) { - - if (!number) { - throw new Error('Must provide a PR number') - } - - const gh = createGitHubClient(); - - const issues = gh.getIssues(OWNER, REPO); - const repo = gh.getRepo(OWNER, REPO); - - console.log(`⌛ Fetching PR number ${number}`) - const issue = (await issues.getIssue(number)).data; - - console.log(`⌛ Fetching files for PR number ${number}`) - const files = (await repo.listPullRequestFiles(number)).data; + await linter.validatePr(number); - console.log("⌛ Validating..."); + } catch (error) { - if (shouldExemptReadme(issue)) { - console.log(`Not validating README changes since the PR is labeled with '${EXEMPT_README}'`) - } else { - featureContainsReadme(issue, files); + core.setFailed(error.message); } - if (shouldExemptTest(issue)) { - console.log(`Not validating test changes since the PR is labeled with '${EXEMPT_TEST}'`) - } else { - featureContainsTest(issue, files); - fixContainsTest(issue, files); - } - - validateBreakingChangeFormat(issue.title, issue.body); - - console.log("✅ Success") - } -// we don't use the 'export' prefix because github actions -// node runtime doesn't seem to support ES6. -// TODO need to verify this. -module.exports.validatePr = validatePr - -require('make-runnable/custom')({ - printOutputFrame: false -}) +run() diff --git a/tools/prlint/lint.js b/tools/prlint/lint.js new file mode 100755 index 0000000000000..6f8de5cb38dbb --- /dev/null +++ b/tools/prlint/lint.js @@ -0,0 +1,148 @@ +const path = require("path") +const GitHub = require("github-api") + +const OWNER = "aws" +const REPO = "aws-cdk" +const EXEMPT_README = 'pr-linter/exempt-readme' +const EXEMPT_TEST = 'pr-linter/exempt-test' + +class LinterError extends Error { + constructor(message) { + super(message); + } +} + +function createGitHubClient() { + const token = process.env.GITHUB_TOKEN; + + if (token) { + console.log("Creating authenticated GitHub Client") + } else { + console.log("Creating un-authenticated GitHub Client") + } + + return new GitHub({'token': token}); +} + +function isPkgCfnspec(issue) { + return issue.title.indexOf("(cfnspec)") > -1; +} + +function isFeature(issue) { + return issue.title.startsWith("feat") +} + +function isFix(issue) { + return issue.title.startsWith("fix") +} + +function testChanged(files) { + return files.filter(f => f.filename.toLowerCase().includes("test")).length != 0; +} + +function readmeChanged(files) { + return files.filter(f => path.basename(f.filename) == "README.md").length != 0; +} + +function featureContainsReadme(issue, files) { + if (isFeature(issue) && !readmeChanged(files) && !isPkgCfnspec(issue)) { + throw new LinterError("Features must contain a change to a README file"); + }; +}; + +function featureContainsTest(issue, files) { + if (isFeature(issue) && !testChanged(files)) { + throw new LinterError("Features must contain a change to a test file"); + }; +}; + +function fixContainsTest(issue, files) { + if (isFix(issue) && !testChanged(files)) { + throw new LinterError("Fixes must contain a change to a test file"); + }; +}; + +function shouldExemptReadme(issue) { + return hasLabel(issue, EXEMPT_README); +} + +function shouldExemptTest(issue) { + return hasLabel(issue, EXEMPT_TEST); +} + +function hasLabel(issue, labelName) { + return issue.labels.some(function (l) { + return l.name === labelName; + }) +} + +/** + * Check that the 'BREAKING CHANGE:' note in the body is correct. + * + * Check this by looking for something that most likely was intended + * to be said note, but got misspelled as "BREAKING CHANGES:" or + * "BREAKING CHANGES(module):" + */ +function validateBreakingChangeFormat(title, body) { + const re = /^BREAKING.*$/m; + const m = re.exec(body); + if (m) { + if (!m[0].startsWith('BREAKING CHANGE: ')) { + throw new LinterError(`Breaking changes should be indicated by starting a line with 'BREAKING CHANGE: ', variations are not allowed. (found: '${m[0]}')`); + } + if (m[0].substr('BREAKING CHANGE:'.length).trim().length === 0) { + throw new LinterError("The description of the first breaking change should immediately follow the 'BREAKING CHANGE: ' clause") + } + const titleRe = /^[a-z]+\([0-9a-z-_]+\)/; + if (!titleRe.exec(title)) { + throw new LinterError("The title of this PR must specify the module name that the first breaking change should be associated to"); + } + } +} + +async function validatePr(number) { + + if (!number) { + throw new Error('Must provide a PR number') + } + + const gh = createGitHubClient(); + + const issues = gh.getIssues(OWNER, REPO); + const repo = gh.getRepo(OWNER, REPO); + + console.log(`⌛ Fetching PR number ${number}`) + const issue = (await issues.getIssue(number)).data; + + console.log(`⌛ Fetching files for PR number ${number}`) + const files = (await repo.listPullRequestFiles(number)).data; + + console.log("⌛ Validating..."); + + if (shouldExemptReadme(issue)) { + console.log(`Not validating README changes since the PR is labeled with '${EXEMPT_README}'`) + } else { + featureContainsReadme(issue, files); + } + + if (shouldExemptTest(issue)) { + console.log(`Not validating test changes since the PR is labeled with '${EXEMPT_TEST}'`) + } else { + featureContainsTest(issue, files); + fixContainsTest(issue, files); + } + + validateBreakingChangeFormat(issue.title, issue.body); + + console.log("✅ Success") + +} + +// we don't use the 'export' prefix because github actions +// node runtime doesn't seem to support ES6. +// TODO need to verify this. +module.exports.validatePr = validatePr + +require('make-runnable/custom')({ + printOutputFrame: false +}) diff --git a/tools/prlint/package-lock.json b/tools/prlint/package-lock.json new file mode 100644 index 0000000000000..9ce4da7edc1e3 --- /dev/null +++ b/tools/prlint/package-lock.json @@ -0,0 +1,5267 @@ +{ + "name": "prlint", + "version": "0.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@actions/core": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.3.0.tgz", + "integrity": "sha512-xxtX0Cwdhb8LcgatfJkokqT8KzPvcIbwL9xpLU09nOwBzaStbfm0dNncsP0M4us+EpoPdWy7vbzU5vSOH7K6pg==" + }, + "@actions/github": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@actions/github/-/github-2.2.0.tgz", + "integrity": "sha512-9UAZqn8ywdR70n3GwVle4N8ALosQs4z50N7XMXrSTUVOmVpaBC5kE3TRTT7qQdi3OaQV24mjGuJZsHUmhD+ZXw==", + "requires": { + "@actions/http-client": "^1.0.3", + "@octokit/graphql": "^4.3.1", + "@octokit/rest": "^16.43.1" + } + }, + "@actions/http-client": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz", + "integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==", + "requires": { + "tunnel": "0.0.6" + } + }, + "@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.12.13" + } + }, + "@babel/compat-data": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.0.tgz", + "integrity": "sha512-vu9V3uMM/1o5Hl5OekMUowo3FqXLJSw+s+66nt0fSWVWTtmosdzn45JHOB3cPtZoe6CTBDzvSw0RdOY85Q37+Q==", + "dev": true + }, + "@babel/core": { + "version": "7.14.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.3.tgz", + "integrity": "sha512-jB5AmTKOCSJIZ72sd78ECEhuPiDMKlQdDI/4QRI6lzYATx5SSogS1oQA2AoPecRCknm30gHi2l+QVvNUu3wZAg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.14.3", + "@babel/helper-compilation-targets": "^7.13.16", + "@babel/helper-module-transforms": "^7.14.2", + "@babel/helpers": "^7.14.0", + "@babel/parser": "^7.14.3", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.14.2", + "@babel/types": "^7.14.2", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", + "source-map": "^0.5.0" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.14.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.3.tgz", + "integrity": "sha512-bn0S6flG/j0xtQdz3hsjJ624h3W0r3llttBMfyHX3YrZ/KtLYr15bjA0FXkgW7FpvrDuTuElXeVjiKlYRpnOFA==", + "dev": true, + "requires": { + "@babel/types": "^7.14.2", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-compilation-targets": { + "version": "7.13.16", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz", + "integrity": "sha512-3gmkYIrpqsLlieFwjkGgLaSHmhnvlAYzZLlYVjlW+QwI+1zE17kGxuJGmIqDQdYp56XdmGeD+Bswx0UTyG18xA==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.13.15", + "@babel/helper-validator-option": "^7.12.17", + "browserslist": "^4.14.5", + "semver": "^6.3.0" + } + }, + "@babel/helper-function-name": { + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.2.tgz", + "integrity": "sha512-NYZlkZRydxw+YT56IlhIcS8PAhb+FEUiOzuhFTfqDyPmzAhRge6ua0dQYT/Uh0t/EDHq05/i+e5M2d4XvjgarQ==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.14.2" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz", + "integrity": "sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw==", + "dev": true, + "requires": { + "@babel/types": "^7.13.12" + } + }, + "@babel/helper-module-imports": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz", + "integrity": "sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA==", + "dev": true, + "requires": { + "@babel/types": "^7.13.12" + } + }, + "@babel/helper-module-transforms": { + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.2.tgz", + "integrity": "sha512-OznJUda/soKXv0XhpvzGWDnml4Qnwp16GN+D/kZIdLsWoHj05kyu8Rm5kXmMef+rVJZ0+4pSGLkeixdqNUATDA==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.13.12", + "@babel/helper-replace-supers": "^7.13.12", + "@babel/helper-simple-access": "^7.13.12", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/helper-validator-identifier": "^7.14.0", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.14.2", + "@babel/types": "^7.14.2" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", + "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz", + "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==", + "dev": true + }, + "@babel/helper-replace-supers": { + "version": "7.14.3", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.3.tgz", + "integrity": "sha512-Rlh8qEWZSTfdz+tgNV/N4gz1a0TMNwCUcENhMjHTHKp3LseYH5Jha0NSlyTQWMnjbYcwFt+bqAMqSLHVXkQ6UA==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.13.12", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/traverse": "^7.14.2", + "@babel/types": "^7.14.2" + } + }, + "@babel/helper-simple-access": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz", + "integrity": "sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA==", + "dev": true, + "requires": { + "@babel/types": "^7.13.12" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", + "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.12.17", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz", + "integrity": "sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw==", + "dev": true + }, + "@babel/helpers": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.0.tgz", + "integrity": "sha512-+ufuXprtQ1D1iZTO/K9+EBRn+qPWMJjZSw/S0KlFrxCw4tkrzv9grgpDHkY9MeQTjTY8i2sp7Jep8DfU6tN9Mg==", + "dev": true, + "requires": { + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.14.0", + "@babel/types": "^7.14.0" + } + }, + "@babel/highlight": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz", + "integrity": "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.0", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.14.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.3.tgz", + "integrity": "sha512-7MpZDIfI7sUC5zWo2+foJ50CSI5lcqDehZ0lVgIhSi4bFEk94fLAKlF3Q0nzSQQ+ca0lm+O6G9ztKVBeu8PMRQ==", + "dev": true + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz", + "integrity": "sha512-A81F9pDwyS7yM//KwbCSDqy3Uj4NMIurtplxphWxoYtNPov7cJsDkAFNNyVlIZ3jwGycVsurZ+LtOA8gZ376iQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/template": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/traverse": { + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.2.tgz", + "integrity": "sha512-TsdRgvBFHMyHOOzcP9S6QU0QQtjxlRpEYOy3mcCO5RgmC305ki42aSAmfZEMSSYBla2oZ9BMqYlncBaKmD/7iA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.14.2", + "@babel/helper-function-name": "^7.14.2", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/parser": "^7.14.2", + "@babel/types": "^7.14.2", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.2.tgz", + "integrity": "sha512-SdjAG/3DikRHpUOjxZgnkbR11xUlyDMUFJdvnIgZEE16mqmY0BINMmc4//JMJglEmn6i7sq6p+mGrFWyZ98EEw==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.0", + "to-fast-properties": "^2.0.0" + } + }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "@cnakazawa/watch": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", + "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", + "dev": true, + "requires": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + } + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, + "@jest/console": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", + "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^26.6.2", + "jest-util": "^26.6.2", + "slash": "^3.0.0" + } + }, + "@jest/core": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.6.3.tgz", + "integrity": "sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/reporters": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-changed-files": "^26.6.2", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-resolve-dependencies": "^26.6.3", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "jest-watcher": "^26.6.2", + "micromatch": "^4.0.2", + "p-each-series": "^2.1.0", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "@jest/environment": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", + "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", + "dev": true, + "requires": { + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2" + } + }, + "@jest/fake-timers": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", + "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@sinonjs/fake-timers": "^6.0.1", + "@types/node": "*", + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" + } + }, + "@jest/globals": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.6.2.tgz", + "integrity": "sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==", + "dev": true, + "requires": { + "@jest/environment": "^26.6.2", + "@jest/types": "^26.6.2", + "expect": "^26.6.2" + } + }, + "@jest/reporters": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.6.2.tgz", + "integrity": "sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.4", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^4.0.3", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "jest-haste-map": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "node-notifier": "^8.0.0", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^7.0.0" + } + }, + "@jest/source-map": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz", + "integrity": "sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.4", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", + "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz", + "integrity": "sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==", + "dev": true, + "requires": { + "@jest/test-result": "^26.6.2", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.6.2", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3" + } + }, + "@jest/transform": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz", + "integrity": "sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^26.6.2", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-util": "^26.6.2", + "micromatch": "^4.0.2", + "pirates": "^4.0.1", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + } + }, + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@octokit/auth-token": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.5.tgz", + "integrity": "sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA==", + "requires": { + "@octokit/types": "^6.0.3" + } + }, + "@octokit/endpoint": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.11.tgz", + "integrity": "sha512-fUIPpx+pZyoLW4GCs3yMnlj2LfoXTWDUVPTC4V3MUEKZm48W+XYpeWSZCv+vYF1ZABUm2CqnDVf1sFtIYrj7KQ==", + "requires": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/graphql": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.6.2.tgz", + "integrity": "sha512-WmsIR1OzOr/3IqfG9JIczI8gMJUMzzyx5j0XXQ4YihHtKlQc+u35VpVoOXhlKAlaBntvry1WpAzPl/a+s3n89Q==", + "requires": { + "@octokit/request": "^5.3.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/openapi-types": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-7.0.0.tgz", + "integrity": "sha512-gV/8DJhAL/04zjTI95a7FhQwS6jlEE0W/7xeYAzuArD0KVAVWDLP2f3vi98hs3HLTczxXdRK/mF0tRoQPpolEw==" + }, + "@octokit/plugin-paginate-rest": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-1.1.2.tgz", + "integrity": "sha512-jbsSoi5Q1pj63sC16XIUboklNw+8tL9VOnJsWycWYR78TKss5PVpIPb1TUUcMQ+bBh7cY579cVAWmf5qG+dw+Q==", + "requires": { + "@octokit/types": "^2.0.1" + }, + "dependencies": { + "@octokit/types": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.16.2.tgz", + "integrity": "sha512-O75k56TYvJ8WpAakWwYRN8Bgu60KrmX0z1KqFp1kNiFNkgW+JW+9EBKZ+S33PU6SLvbihqd+3drvPxKK68Ee8Q==", + "requires": { + "@types/node": ">= 8" + } + } + } + }, + "@octokit/plugin-request-log": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.3.tgz", + "integrity": "sha512-4RFU4li238jMJAzLgAwkBAw+4Loile5haQMQr+uhFq27BmyJXcXSKvoQKqh0agsZEiUlW6iSv3FAgvmGkur7OQ==" + }, + "@octokit/plugin-rest-endpoint-methods": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-2.4.0.tgz", + "integrity": "sha512-EZi/AWhtkdfAYi01obpX0DF7U6b1VRr30QNQ5xSFPITMdLSfhcBqjamE3F+sKcxPbD7eZuMHu3Qkk2V+JGxBDQ==", + "requires": { + "@octokit/types": "^2.0.1", + "deprecation": "^2.3.1" + }, + "dependencies": { + "@octokit/types": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.16.2.tgz", + "integrity": "sha512-O75k56TYvJ8WpAakWwYRN8Bgu60KrmX0z1KqFp1kNiFNkgW+JW+9EBKZ+S33PU6SLvbihqd+3drvPxKK68Ee8Q==", + "requires": { + "@types/node": ">= 8" + } + } + } + }, + "@octokit/request": { + "version": "5.4.15", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.15.tgz", + "integrity": "sha512-6UnZfZzLwNhdLRreOtTkT9n57ZwulCve8q3IT/Z477vThu6snfdkBuhxnChpOKNGxcQ71ow561Qoa6uqLdPtag==", + "requires": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.0.0", + "@octokit/types": "^6.7.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.1", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/request-error": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.5.tgz", + "integrity": "sha512-T/2wcCFyM7SkXzNoyVNWjyVlUwBvW3igM3Btr/eKYiPmucXTtkxt2RBsf6gn3LTzaLSLTQtNmvg+dGsOxQrjZg==", + "requires": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "@octokit/rest": { + "version": "16.43.2", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.43.2.tgz", + "integrity": "sha512-ngDBevLbBTFfrHZeiS7SAMAZ6ssuVmXuya+F/7RaVvlysgGa1JKJkKWY+jV6TCJYcW0OALfJ7nTIGXcBXzycfQ==", + "requires": { + "@octokit/auth-token": "^2.4.0", + "@octokit/plugin-paginate-rest": "^1.1.1", + "@octokit/plugin-request-log": "^1.0.0", + "@octokit/plugin-rest-endpoint-methods": "2.4.0", + "@octokit/request": "^5.2.0", + "@octokit/request-error": "^1.0.2", + "atob-lite": "^2.0.0", + "before-after-hook": "^2.0.0", + "btoa-lite": "^1.0.0", + "deprecation": "^2.0.0", + "lodash.get": "^4.4.2", + "lodash.set": "^4.3.2", + "lodash.uniq": "^4.5.0", + "octokit-pagination-methods": "^1.1.0", + "once": "^1.4.0", + "universal-user-agent": "^4.0.0" + }, + "dependencies": { + "@octokit/request-error": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.2.1.tgz", + "integrity": "sha512-+6yDyk1EES6WK+l3viRDElw96MvwfJxCt45GvmjDUKWjYIb3PJZQkq3i46TwGwoPD4h8NmTrENmtyA1FwbmhRA==", + "requires": { + "@octokit/types": "^2.0.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "@octokit/types": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.16.2.tgz", + "integrity": "sha512-O75k56TYvJ8WpAakWwYRN8Bgu60KrmX0z1KqFp1kNiFNkgW+JW+9EBKZ+S33PU6SLvbihqd+3drvPxKK68Ee8Q==", + "requires": { + "@types/node": ">= 8" + } + }, + "universal-user-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.1.tgz", + "integrity": "sha512-LnST3ebHwVL2aNe4mejI9IQh2HfZ1RLo8Io2HugSif8ekzD1TlWpHpColOB/eh8JHMLkGH3Akqf040I+4ylNxg==", + "requires": { + "os-name": "^3.1.0" + } + } + } + }, + "@octokit/types": { + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.14.2.tgz", + "integrity": "sha512-wiQtW9ZSy4OvgQ09iQOdyXYNN60GqjCL/UdMsepDr1Gr0QzpW6irIKbH3REuAHXAhxkEk9/F2a3Gcs1P6kW5jA==", + "requires": { + "@octokit/openapi-types": "^7.0.0" + } + }, + "@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", + "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@types/babel__core": { + "version": "7.1.14", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.14.tgz", + "integrity": "sha512-zGZJzzBUVDo/eV6KgbE0f0ZI7dInEYvo12Rb70uNQDshC3SkRMb67ja0GgRHZgAX3Za6rhaWlvbDO8rrGyAb1g==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.2.tgz", + "integrity": "sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.0.tgz", + "integrity": "sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.11.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.11.1.tgz", + "integrity": "sha512-Vs0hm0vPahPMYi9tDjtP66llufgO3ST16WXaSTtDGEl9cewAl3AibmxWw6TINOqHPT9z0uABKAYjT9jNSg4npw==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/node": { + "version": "15.6.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-15.6.0.tgz", + "integrity": "sha512-gCYSfQpy+LYhOFTKAeE8BkyGqaxmlFxe+n4DKM6DR0wzw/HISUE/hAmkC/KT8Sw5PCJblqg062b3z9gucv3k0A==" + }, + "@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "dev": true + }, + "@types/prettier": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.2.3.tgz", + "integrity": "sha512-PijRCG/K3s3w1We6ynUKdxEc5AcuuH3NBmMDP8uvKVp6X43UY7NQlTzczakXP3DJR0F4dfNQIGjU2cUeRYs2AA==", + "dev": true + }, + "@types/stack-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", + "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", + "dev": true + }, + "@types/yargs": { + "version": "15.0.13", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz", + "integrity": "sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "20.2.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz", + "integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==", + "dev": true + }, + "abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true + }, + "acorn": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.2.4.tgz", + "integrity": "sha512-Ibt84YwBDDA890eDiDCEqcbwvHlBvzzDkU2cGBBDDI1QWT12jTiXIOn2CIw5KK4i6N5Z2HUxwYjzriDyqaqqZg==", + "dev": true + }, + "acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + } + } + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + }, + "dependencies": { + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true + } + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "atob-lite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-2.0.0.tgz", + "integrity": "sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "dev": true + }, + "axios": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", + "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "requires": { + "follow-redirects": "^1.10.0" + } + }, + "babel-jest": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", + "integrity": "sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==", + "dev": true, + "requires": { + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/babel__core": "^7.1.7", + "babel-plugin-istanbul": "^6.0.0", + "babel-preset-jest": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "slash": "^3.0.0" + } + }, + "babel-plugin-istanbul": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" + } + }, + "babel-plugin-jest-hoist": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz", + "integrity": "sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==", + "dev": true, + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "requires": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + } + }, + "babel-preset-jest": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz", + "integrity": "sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==", + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "^26.6.2", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "before-after-hook": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.1.tgz", + "integrity": "sha512-/6FKxSTWoJdbsLDF8tdIjaRiFXiE6UHsEHE3OPI/cwPURCVi1ukP0gmLn7XWEiFk5TcwQjjY5PWsU+j+tgXgmw==" + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true + }, + "browserslist": { + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", + "escalade": "^3.1.1", + "node-releases": "^1.1.71" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "requires": { + "node-int64": "^0.4.0" + } + }, + "btoa-lite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz", + "integrity": "sha1-M3dm2hWAEhD92VbCLpxokaudAzc=" + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30001228", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001228.tgz", + "integrity": "sha512-QQmLOGJ3DEgokHbMSA8cj2a+geXqmnpyOFT0lhQV6P3/YOJvGDEwoedcwxEQ30gJIwIIunHIicunJ2rzK5gB2A==", + "dev": true + }, + "capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "dev": true, + "requires": { + "rsvp": "^4.8.4" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "cjs-module-lexer": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz", + "integrity": "sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw==", + "dev": true + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "colorette": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", + "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + } + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dev": true, + "requires": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decimal.js": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.1.tgz", + "integrity": "sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw==", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, + "detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true + }, + "diff-sequences": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", + "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", + "dev": true + }, + "domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dev": true, + "requires": { + "webidl-conversions": "^5.0.0" + }, + "dependencies": { + "webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true + } + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "electron-to-chromium": { + "version": "1.3.735", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.735.tgz", + "integrity": "sha512-cp7MWzC3NseUJV2FJFgaiesdrS+A8ZUjX5fLAxdRlcaPDkaPGFplX930S5vf84yqDp4LjuLdKouWuVOTwUfqHQ==", + "dev": true + }, + "emittery": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.2.tgz", + "integrity": "sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "exec-sh": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz", + "integrity": "sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w==", + "dev": true + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expect": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", + "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "ansi-styles": "^4.0.0", + "jest-get-type": "^26.3.0", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-regex-util": "^26.0.0" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dev": true, + "requires": { + "bser": "2.1.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "follow-redirects": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", + "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==" + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "github-api": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/github-api/-/github-api-3.4.0.tgz", + "integrity": "sha512-2yYqYS6Uy4br1nw0D3VrlYWxtGTkUhIZrumBrcBwKdBOzMT8roAe8IvI6kjIOkxqxapKR5GkEsHtz3Du/voOpA==", + "requires": { + "axios": "^0.21.1", + "debug": "^2.2.0", + "js-base64": "^2.1.9", + "utf8": "^2.1.1" + } + }, + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", + "dev": true + }, + "growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", + "dev": true, + "optional": true + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "dev": true, + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dev": true, + "requires": { + "whatwg-encoding": "^1.0.5" + } + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "import-local": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", + "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "is-core-module": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", + "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "optional": true + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" + }, + "is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "optional": true, + "requires": { + "is-docker": "^2.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jest": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-26.6.3.tgz", + "integrity": "sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q==", + "dev": true, + "requires": { + "@jest/core": "^26.6.3", + "import-local": "^3.0.2", + "jest-cli": "^26.6.3" + }, + "dependencies": { + "jest-cli": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz", + "integrity": "sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==", + "dev": true, + "requires": { + "@jest/core": "^26.6.3", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "import-local": "^3.0.2", + "is-ci": "^2.0.0", + "jest-config": "^26.6.3", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "prompts": "^2.0.1", + "yargs": "^15.4.1" + } + } + } + }, + "jest-changed-files": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz", + "integrity": "sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "execa": "^4.0.0", + "throat": "^5.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "jest-config": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz", + "integrity": "sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^26.6.3", + "@jest/types": "^26.6.2", + "babel-jest": "^26.6.3", + "chalk": "^4.0.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.4", + "jest-environment-jsdom": "^26.6.2", + "jest-environment-node": "^26.6.2", + "jest-get-type": "^26.3.0", + "jest-jasmine2": "^26.6.3", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "micromatch": "^4.0.2", + "pretty-format": "^26.6.2" + } + }, + "jest-diff": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", + "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + } + }, + "jest-docblock": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz", + "integrity": "sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==", + "dev": true, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz", + "integrity": "sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "jest-get-type": "^26.3.0", + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2" + } + }, + "jest-environment-jsdom": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz", + "integrity": "sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==", + "dev": true, + "requires": { + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2", + "jsdom": "^16.4.0" + } + }, + "jest-environment-node": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.6.2.tgz", + "integrity": "sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==", + "dev": true, + "requires": { + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" + } + }, + "jest-get-type": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "dev": true + }, + "jest-haste-map": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz", + "integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.1.2", + "graceful-fs": "^4.2.4", + "jest-regex-util": "^26.0.0", + "jest-serializer": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "micromatch": "^4.0.2", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, + "jest-jasmine2": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz", + "integrity": "sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==", + "dev": true, + "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^26.6.2", + "is-generator-fn": "^2.0.0", + "jest-each": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2", + "throat": "^5.0.0" + } + }, + "jest-leak-detector": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz", + "integrity": "sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg==", + "dev": true, + "requires": { + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + } + }, + "jest-matcher-utils": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", + "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + } + }, + "jest-message-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", + "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/types": "^26.6.2", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "micromatch": "^4.0.2", + "pretty-format": "^26.6.2", + "slash": "^3.0.0", + "stack-utils": "^2.0.2" + } + }, + "jest-mock": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", + "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*" + } + }, + "jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "dev": true + }, + "jest-regex-util": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", + "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", + "dev": true + }, + "jest-resolve": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", + "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^26.6.2", + "read-pkg-up": "^7.0.1", + "resolve": "^1.18.1", + "slash": "^3.0.0" + } + }, + "jest-resolve-dependencies": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz", + "integrity": "sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-snapshot": "^26.6.2" + } + }, + "jest-runner": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.6.3.tgz", + "integrity": "sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.7.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-config": "^26.6.3", + "jest-docblock": "^26.0.0", + "jest-haste-map": "^26.6.2", + "jest-leak-detector": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "source-map-support": "^0.5.6", + "throat": "^5.0.0" + } + }, + "jest-runtime": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.6.3.tgz", + "integrity": "sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/globals": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0", + "cjs-module-lexer": "^0.6.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.4", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "slash": "^3.0.0", + "strip-bom": "^4.0.0", + "yargs": "^15.4.1" + } + }, + "jest-serializer": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz", + "integrity": "sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==", + "dev": true, + "requires": { + "@types/node": "*", + "graceful-fs": "^4.2.4" + } + }, + "jest-snapshot": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz", + "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0", + "@jest/types": "^26.6.2", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.0.0", + "chalk": "^4.0.0", + "expect": "^26.6.2", + "graceful-fs": "^4.2.4", + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "jest-haste-map": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", + "natural-compare": "^1.4.0", + "pretty-format": "^26.6.2", + "semver": "^7.3.2" + }, + "dependencies": { + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + }, + "jest-validate": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz", + "integrity": "sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "camelcase": "^6.0.0", + "chalk": "^4.0.0", + "jest-get-type": "^26.3.0", + "leven": "^3.1.0", + "pretty-format": "^26.6.2" + }, + "dependencies": { + "camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true + } + } + }, + "jest-watcher": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.6.2.tgz", + "integrity": "sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ==", + "dev": true, + "requires": { + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^26.6.2", + "string-length": "^4.0.1" + } + }, + "jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + } + }, + "js-base64": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", + "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "jsdom": { + "version": "16.5.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.5.3.tgz", + "integrity": "sha512-Qj1H+PEvUsOtdPJ056ewXM4UJPCi4hhLA8wpiz9F2YvsRBhuFsXxtrIFAgGBDynQA9isAMGE91PfUYbdMPXuTA==", + "dev": true, + "requires": { + "abab": "^2.0.5", + "acorn": "^8.1.0", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "html-encoding-sniffer": "^2.0.1", + "is-potential-custom-element-name": "^1.0.0", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "request": "^2.88.2", + "request-promise-native": "^1.0.9", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.4", + "xml-name-validator": "^3.0.0" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + }, + "lodash.set": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", + "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=" + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "macos-release": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.4.1.tgz", + "integrity": "sha512-H/QHeBIN1fIGJX517pvK8IEK53yQOW7YcEI55oYtgjDdoCQQz7eJS94qt5kNrscReEyuD/JcdFCm2XBEcGOITg==" + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "make-runnable": { + "version": "1.3.9", + "resolved": "https://registry.npmjs.org/make-runnable/-/make-runnable-1.3.9.tgz", + "integrity": "sha512-yiAvZX8hAEXOcYP6ibvnjWcmxZVQ2aBZMQ2148IEPFga0ODr3htJfc+bdeUQfQCUuVe2lrY3VmajPAVSDZG8xw==", + "dev": true, + "requires": { + "bluebird": "^3.5.0", + "yargs": "^16.2.0" + }, + "dependencies": { + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.7", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", + "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==", + "dev": true + } + } + }, + "makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "dev": true, + "requires": { + "tmpl": "1.0.x" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "mime-db": { + "version": "1.47.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz", + "integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==", + "dev": true + }, + "mime-types": { + "version": "2.1.30", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz", + "integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==", + "dev": true, + "requires": { + "mime-db": "1.47.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "dev": true + }, + "node-modules-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", + "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", + "dev": true + }, + "node-notifier": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.2.tgz", + "integrity": "sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg==", + "dev": true, + "optional": true, + "requires": { + "growly": "^1.3.0", + "is-wsl": "^2.2.0", + "semver": "^7.3.2", + "shellwords": "^0.1.1", + "uuid": "^8.3.0", + "which": "^2.0.2" + }, + "dependencies": { + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "optional": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "optional": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "node-releases": { + "version": "1.1.72", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.72.tgz", + "integrity": "sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "octokit-pagination-methods": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz", + "integrity": "sha512-fZ4qZdQ2nxJvtcasX7Ghl+WlWS/d9IgnBIwFZXVNNZUmzpno91SX5bc5vuxiuKoCtK78XxGGNuSCrDC7xYB3OQ==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "os-name": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-name/-/os-name-3.1.0.tgz", + "integrity": "sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg==", + "requires": { + "macos-release": "^2.2.0", + "windows-release": "^3.1.0" + } + }, + "p-each-series": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", + "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "picomatch": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", + "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", + "dev": true + }, + "pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "dev": true, + "requires": { + "node-modules-regexp": "^1.0.0" + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + } + }, + "prompts": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz", + "integrity": "sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } + } + }, + "request-promise-core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "dev": true, + "requires": { + "lodash": "^4.17.19" + } + }, + "request-promise-native": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", + "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", + "dev": true, + "requires": { + "request-promise-core": "1.1.4", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + }, + "dependencies": { + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "dev": true + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "dev": true, + "requires": { + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "requires": { + "xmlchars": "^2.2.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + } + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "dev": true + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.8.tgz", + "integrity": "sha512-NDgA96EnaLSvtbM7trJj+t1LUR3pirkDCcz9nOUlPb5DMBGsH7oES6C3hs3j7R9oHEa1EMvReS/BUAIT5Tcr0g==", + "dev": true + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", + "dev": true, + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + } + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", + "dev": true + }, + "string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-hyperlinks": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", + "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", + "dev": true, + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + } + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "throat": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", + "dev": true + }, + "tmpl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", + "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "dev": true, + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + } + }, + "tr46": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz", + "integrity": "sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "utf8": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-2.1.2.tgz", + "integrity": "sha1-H6DZJw6b6FDZsFAn9jUZv0ZFfZY=" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "optional": true + }, + "v8-to-istanbul": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz", + "integrity": "sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "dependencies": { + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + } + } + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dev": true, + "requires": { + "xml-name-validator": "^3.0.0" + } + }, + "walker": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "dev": true, + "requires": { + "makeerror": "1.0.x" + } + }, + "webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true + }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "requires": { + "iconv-lite": "0.4.24" + } + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "whatwg-url": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.5.0.tgz", + "integrity": "sha512-fy+R77xWv0AiqfLl4nuGUlQ3/6b5uNfQ4WAbGQVMYshCTCCPK9psC1nWh3XHuxGVCtlcDDQPQW1csmmIQo+fwg==", + "dev": true, + "requires": { + "lodash": "^4.7.0", + "tr46": "^2.0.2", + "webidl-conversions": "^6.1.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "windows-release": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.3.3.tgz", + "integrity": "sha512-OSOGH1QYiW5yVor9TtmXKQvt2vjQqbYS+DqmsZw+r7xDwLXEeT3JGW0ZppFmHx4diyXmxt238KFR3N9jzevBRg==", + "requires": { + "execa": "^1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "requires": { + "path-key": "^2.0.0" + } + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "ws": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz", + "integrity": "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g==", + "dev": true + }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } +} diff --git a/tools/prlint/package.json b/tools/prlint/package.json index d8f17fbba3592..b257e95734c34 100644 --- a/tools/prlint/package.json +++ b/tools/prlint/package.json @@ -11,6 +11,8 @@ }, "license": "Apache-2.0", "dependencies": { + "@actions/core": "^1.2.2", + "@actions/github": "^2.1.0", "github-api": "^3.4.0" }, "devDependencies": { @@ -20,6 +22,6 @@ "scripts": { "build": "echo success", "test": "jest", - "build+test": "npm run build test && npm run test" + "build+test": "npm run build && npm run test" } } diff --git a/tools/prlint/test/index.test.js b/tools/prlint/test/lint.test.js similarity index 98% rename from tools/prlint/test/index.test.js rename to tools/prlint/test/lint.test.js index eda53bb5ce137..04ca8740e5575 100644 --- a/tools/prlint/test/index.test.js +++ b/tools/prlint/test/lint.test.js @@ -1,6 +1,6 @@ const GitHub = require('github-api'); jest.mock('github-api'); -const linter = require('../index'); +const linter = require('../lint'); beforeEach(() => { GitHub.mockClear(); From 53ee025ae038018e56aea84b3de20f5770c874fa Mon Sep 17 00:00:00 2001 From: Jason Del Ponte <961963+jasdel@users.noreply.github.com> Date: Sun, 23 May 2021 22:55:40 -0700 Subject: [PATCH 084/134] docs(aws-stepfunctions-tasks): Update TaskEnvironmentVariable doc to refer to JsonPath (#14126) Updates the TaskEnvironmentVariable interface member documentation to refer to JsonPath class's static methods as helpers to specify a value from the Task's JSON path. Related to #3445 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../lib/ecs/run-ecs-task-base-types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/ecs/run-ecs-task-base-types.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/ecs/run-ecs-task-base-types.ts index 107f86362543b..57221503a686e 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/ecs/run-ecs-task-base-types.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/ecs/run-ecs-task-base-types.ts @@ -56,14 +56,14 @@ export interface TaskEnvironmentVariable { /** * Name for the environment variable * - * Exactly one of `name` and `namePath` must be specified. + * Use `JsonPath` class's static methods to specify name from a JSON path. */ readonly name: string; /** * Value of the environment variable * - * Exactly one of `value` and `valuePath` must be specified. + * Use `JsonPath` class's static methods to specify value from a JSON path. */ readonly value: string; } From c7f9f9796d9dffc5a7674bbb10c2b382de23527c Mon Sep 17 00:00:00 2001 From: Taehyun Kim Date: Mon, 24 May 2021 18:23:14 +0900 Subject: [PATCH 085/134] bump lambda-layer-kubectl (#14678) --- packages/@aws-cdk/lambda-layer-kubectl/layer/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/lambda-layer-kubectl/layer/Dockerfile b/packages/@aws-cdk/lambda-layer-kubectl/layer/Dockerfile index c04c0cc9bfbea..6887b3a19948b 100644 --- a/packages/@aws-cdk/lambda-layer-kubectl/layer/Dockerfile +++ b/packages/@aws-cdk/lambda-layer-kubectl/layer/Dockerfile @@ -5,8 +5,8 @@ FROM public.ecr.aws/lambda/provided:latest # versions # -ARG KUBECTL_VERSION=1.20.0 -ARG HELM_VERSION=3.4.2 +ARG KUBECTL_VERSION=1.21.0 +ARG HELM_VERSION=3.5.4 USER root RUN mkdir -p /opt From 4439763a0574d2007b1ef64779ecfdcf1e01c9fd Mon Sep 17 00:00:00 2001 From: AlbertGao Date: Mon, 24 May 2021 21:50:55 +1200 Subject: [PATCH 086/134] docs(apigatewayv2): fix incorrect response type enum (#14840) @iRoachie I think this is the proper type here? *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-apigatewayv2-authorizers/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md b/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md index 3e888e57c9a9c..7dd9c2f5e61bd 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md @@ -178,7 +178,7 @@ const authHandler = new Function(this, 'auth-function', { }); const authorizer = new HttpLambdaAuthorizer({ - responseTypes: [HttpLambdaAuthorizerType.SIMPLE] // Define if returns simple and/or iam response + responseTypes: [HttpLambdaResponseType.SIMPLE] // Define if returns simple and/or iam response handler: authHandler, }); From b9189dfa526de07c198d65e0394d2149df7d0e61 Mon Sep 17 00:00:00 2001 From: Mitchell Valine Date: Mon, 24 May 2021 10:05:49 -0700 Subject: [PATCH 087/134] chore: add go to list of supported languages (#14849) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 44313c65df9d4..b5442a3e68862 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ [![PyPI version](https://badge.fury.io/py/aws-cdk.core.svg)](https://badge.fury.io/py/aws-cdk.core) [![NuGet version](https://badge.fury.io/nu/Amazon.CDK.svg)](https://badge.fury.io/nu/Amazon.CDK) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/software.amazon.awscdk/core/badge.svg)](https://maven-badges.herokuapp.com/maven-central/software.amazon.awscdk/core) +[![Go Reference](https://pkg.go.dev/badge/github.com/aws/aws-cdk-go/awscdk.svg)](https://pkg.go.dev/github.com/aws/aws-cdk-go/awscdk) [![Mergify](https://img.shields.io/endpoint.svg?url=https://gh.mergify.io/badges/aws/aws-cdk&style=flat)](https://mergify.io) The **AWS Cloud Development Kit (AWS CDK)** is an open-source software development @@ -24,6 +25,8 @@ The CDK is available in the following languages: * Python ([Python ≥ 3.6](https://www.python.org/downloads/)) * Java ([Java ≥ 8](https://www.oracle.com/technetwork/java/javase/downloads/index.html) and [Maven ≥ 3.5.4](https://maven.apache.org/download.cgi)) * .NET ([.NET Core ≥ 3.1](https://dotnet.microsoft.com/download)) +* Go ([Go ≥ 1.16.4](https://golang.org/)) + - Go is currently in developer preview and is not recommended for production use. \ Jump To: From 8b293b058594317bf4b77234ba00d2deb7afd5d9 Mon Sep 17 00:00:00 2001 From: Niranjan Jayakar Date: Mon, 24 May 2021 18:50:56 +0100 Subject: [PATCH 088/134] chore(prlint): switch to typescript (#14848) Switch package to typescript so we get type safety and all the typescript goodness. --- .github/workflows/pr-linter.yml | 4 +- tools/prlint/.gitignore | 4 + tools/prlint/decs.d.ts | 1 + tools/prlint/{index.js => index.ts} | 6 +- tools/prlint/lint.js | 148 ------------------ tools/prlint/lint.ts | 143 +++++++++++++++++ tools/prlint/package-lock.json | 16 ++ tools/prlint/package.json | 11 +- .../test/{lint.test.js => lint.test.ts} | 7 +- tools/prlint/tsconfig.json | 19 +++ 10 files changed, 201 insertions(+), 158 deletions(-) create mode 100644 tools/prlint/.gitignore create mode 100644 tools/prlint/decs.d.ts rename tools/prlint/{index.js => index.ts} (63%) delete mode 100755 tools/prlint/lint.js create mode 100755 tools/prlint/lint.ts rename tools/prlint/test/{lint.test.js => lint.test.ts} (93%) create mode 100644 tools/prlint/tsconfig.json diff --git a/.github/workflows/pr-linter.yml b/.github/workflows/pr-linter.yml index 64ea53bc1ad4b..bfcb5376d74d4 100644 --- a/.github/workflows/pr-linter.yml +++ b/.github/workflows/pr-linter.yml @@ -23,8 +23,8 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Install packages - run: cd tools/prlint && npm ci + - name: Install & Build prlint + run: cd tools/prlint && npm ci && npm run build+test - name: Validate uses: ./tools/prlint diff --git a/tools/prlint/.gitignore b/tools/prlint/.gitignore new file mode 100644 index 0000000000000..5801cd7fb289f --- /dev/null +++ b/tools/prlint/.gitignore @@ -0,0 +1,4 @@ +*.d.ts +*.js + +!decs.d.ts diff --git a/tools/prlint/decs.d.ts b/tools/prlint/decs.d.ts new file mode 100644 index 0000000000000..aca419554766b --- /dev/null +++ b/tools/prlint/decs.d.ts @@ -0,0 +1 @@ +declare module 'github-api' \ No newline at end of file diff --git a/tools/prlint/index.js b/tools/prlint/index.ts similarity index 63% rename from tools/prlint/index.js rename to tools/prlint/index.ts index 68a26ca247b42..13231e1c37edd 100644 --- a/tools/prlint/index.js +++ b/tools/prlint/index.ts @@ -1,6 +1,6 @@ -const core = require('@actions/core'); -const github = require('@actions/github'); -const linter = require('./lint'); +import * as core from '@actions/core'; +import * as github from '@actions/github'; +import * as linter from './lint'; async function run() { const number = github.context.issue.number; diff --git a/tools/prlint/lint.js b/tools/prlint/lint.js deleted file mode 100755 index 6f8de5cb38dbb..0000000000000 --- a/tools/prlint/lint.js +++ /dev/null @@ -1,148 +0,0 @@ -const path = require("path") -const GitHub = require("github-api") - -const OWNER = "aws" -const REPO = "aws-cdk" -const EXEMPT_README = 'pr-linter/exempt-readme' -const EXEMPT_TEST = 'pr-linter/exempt-test' - -class LinterError extends Error { - constructor(message) { - super(message); - } -} - -function createGitHubClient() { - const token = process.env.GITHUB_TOKEN; - - if (token) { - console.log("Creating authenticated GitHub Client") - } else { - console.log("Creating un-authenticated GitHub Client") - } - - return new GitHub({'token': token}); -} - -function isPkgCfnspec(issue) { - return issue.title.indexOf("(cfnspec)") > -1; -} - -function isFeature(issue) { - return issue.title.startsWith("feat") -} - -function isFix(issue) { - return issue.title.startsWith("fix") -} - -function testChanged(files) { - return files.filter(f => f.filename.toLowerCase().includes("test")).length != 0; -} - -function readmeChanged(files) { - return files.filter(f => path.basename(f.filename) == "README.md").length != 0; -} - -function featureContainsReadme(issue, files) { - if (isFeature(issue) && !readmeChanged(files) && !isPkgCfnspec(issue)) { - throw new LinterError("Features must contain a change to a README file"); - }; -}; - -function featureContainsTest(issue, files) { - if (isFeature(issue) && !testChanged(files)) { - throw new LinterError("Features must contain a change to a test file"); - }; -}; - -function fixContainsTest(issue, files) { - if (isFix(issue) && !testChanged(files)) { - throw new LinterError("Fixes must contain a change to a test file"); - }; -}; - -function shouldExemptReadme(issue) { - return hasLabel(issue, EXEMPT_README); -} - -function shouldExemptTest(issue) { - return hasLabel(issue, EXEMPT_TEST); -} - -function hasLabel(issue, labelName) { - return issue.labels.some(function (l) { - return l.name === labelName; - }) -} - -/** - * Check that the 'BREAKING CHANGE:' note in the body is correct. - * - * Check this by looking for something that most likely was intended - * to be said note, but got misspelled as "BREAKING CHANGES:" or - * "BREAKING CHANGES(module):" - */ -function validateBreakingChangeFormat(title, body) { - const re = /^BREAKING.*$/m; - const m = re.exec(body); - if (m) { - if (!m[0].startsWith('BREAKING CHANGE: ')) { - throw new LinterError(`Breaking changes should be indicated by starting a line with 'BREAKING CHANGE: ', variations are not allowed. (found: '${m[0]}')`); - } - if (m[0].substr('BREAKING CHANGE:'.length).trim().length === 0) { - throw new LinterError("The description of the first breaking change should immediately follow the 'BREAKING CHANGE: ' clause") - } - const titleRe = /^[a-z]+\([0-9a-z-_]+\)/; - if (!titleRe.exec(title)) { - throw new LinterError("The title of this PR must specify the module name that the first breaking change should be associated to"); - } - } -} - -async function validatePr(number) { - - if (!number) { - throw new Error('Must provide a PR number') - } - - const gh = createGitHubClient(); - - const issues = gh.getIssues(OWNER, REPO); - const repo = gh.getRepo(OWNER, REPO); - - console.log(`⌛ Fetching PR number ${number}`) - const issue = (await issues.getIssue(number)).data; - - console.log(`⌛ Fetching files for PR number ${number}`) - const files = (await repo.listPullRequestFiles(number)).data; - - console.log("⌛ Validating..."); - - if (shouldExemptReadme(issue)) { - console.log(`Not validating README changes since the PR is labeled with '${EXEMPT_README}'`) - } else { - featureContainsReadme(issue, files); - } - - if (shouldExemptTest(issue)) { - console.log(`Not validating test changes since the PR is labeled with '${EXEMPT_TEST}'`) - } else { - featureContainsTest(issue, files); - fixContainsTest(issue, files); - } - - validateBreakingChangeFormat(issue.title, issue.body); - - console.log("✅ Success") - -} - -// we don't use the 'export' prefix because github actions -// node runtime doesn't seem to support ES6. -// TODO need to verify this. -module.exports.validatePr = validatePr - -require('make-runnable/custom')({ - printOutputFrame: false -}) diff --git a/tools/prlint/lint.ts b/tools/prlint/lint.ts new file mode 100755 index 0000000000000..524675d4c81e0 --- /dev/null +++ b/tools/prlint/lint.ts @@ -0,0 +1,143 @@ +import * as path from 'path'; +import * as GitHub from 'github-api'; + +const OWNER = "aws" +const REPO = "aws-cdk" +const EXEMPT_README = 'pr-linter/exempt-readme' +const EXEMPT_TEST = 'pr-linter/exempt-test' + +class LinterError extends Error { + constructor(message: string) { + super(message); + } +} + +function createGitHubClient() { + const token = process.env.GITHUB_TOKEN; + + if (token) { + console.log("Creating authenticated GitHub Client") + } else { + console.log("Creating un-authenticated GitHub Client") + } + + return new GitHub({'token': token}); +} + +function isPkgCfnspec(issue: any) { + return issue.title.indexOf("(cfnspec)") > -1; +} + +function isFeature(issue: any) { + return issue.title.startsWith("feat") +} + +function isFix(issue: any) { + return issue.title.startsWith("fix") +} + +function testChanged(files: any[]) { + return files.filter(f => f.filename.toLowerCase().includes("test")).length != 0; +} + +function readmeChanged(files: any[]) { + return files.filter(f => path.basename(f.filename) == "README.md").length != 0; +} + +function featureContainsReadme(issue: any, files: any[]) { + if (isFeature(issue) && !readmeChanged(files) && !isPkgCfnspec(issue)) { + throw new LinterError("Features must contain a change to a README file"); + }; +}; + +function featureContainsTest(issue: any, files: any[]) { + if (isFeature(issue) && !testChanged(files)) { + throw new LinterError("Features must contain a change to a test file"); + }; +}; + +function fixContainsTest(issue: any, files: any[]) { + if (isFix(issue) && !testChanged(files)) { + throw new LinterError("Fixes must contain a change to a test file"); + }; +}; + +function shouldExemptReadme(issue: any) { + return hasLabel(issue, EXEMPT_README); +} + +function shouldExemptTest(issue: any) { + return hasLabel(issue, EXEMPT_TEST); +} + +function hasLabel(issue: any, labelName: string) { + return issue.labels.some(function (l: any) { + return l.name === labelName; + }) +} + +/** + * Check that the 'BREAKING CHANGE:' note in the body is correct. + * + * Check this by looking for something that most likely was intended + * to be said note, but got misspelled as "BREAKING CHANGES:" or + * "BREAKING CHANGES(module):" + */ +function validateBreakingChangeFormat(title: string, body: string) { + const re = /^BREAKING.*$/m; + const m = re.exec(body); + if (m) { + if (!m[0].startsWith('BREAKING CHANGE: ')) { + throw new LinterError(`Breaking changes should be indicated by starting a line with 'BREAKING CHANGE: ', variations are not allowed. (found: '${m[0]}')`); + } + if (m[0].substr('BREAKING CHANGE:'.length).trim().length === 0) { + throw new LinterError("The description of the first breaking change should immediately follow the 'BREAKING CHANGE: ' clause") + } + const titleRe = /^[a-z]+\([0-9a-z-_]+\)/; + if (!titleRe.exec(title)) { + throw new LinterError("The title of this PR must specify the module name that the first breaking change should be associated to"); + } + } +} + +export async function validatePr(number: number) { + + if (!number) { + throw new Error('Must provide a PR number') + } + + const gh = createGitHubClient(); + + const issues = gh.getIssues(OWNER, REPO); + const repo = gh.getRepo(OWNER, REPO); + + console.log(`⌛ Fetching PR number ${number}`) + const issue = (await issues.getIssue(number)).data; + + console.log(`⌛ Fetching files for PR number ${number}`) + const files = (await repo.listPullRequestFiles(number)).data; + + console.log("⌛ Validating..."); + + if (shouldExemptReadme(issue)) { + console.log(`Not validating README changes since the PR is labeled with '${EXEMPT_README}'`) + } else { + featureContainsReadme(issue, files); + } + + if (shouldExemptTest(issue)) { + console.log(`Not validating test changes since the PR is labeled with '${EXEMPT_TEST}'`) + } else { + featureContainsTest(issue, files); + fixContainsTest(issue, files); + } + + validateBreakingChangeFormat(issue.title, issue.body); + + console.log("✅ Success") + +} + +require('make-runnable/custom')({ + printOutputFrame: false +}) diff --git a/tools/prlint/package-lock.json b/tools/prlint/package-lock.json index 9ce4da7edc1e3..a92e7dedb436d 100644 --- a/tools/prlint/package-lock.json +++ b/tools/prlint/package-lock.json @@ -948,6 +948,16 @@ "@types/istanbul-lib-report": "*" } }, + "@types/jest": { + "version": "26.0.23", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.23.tgz", + "integrity": "sha512-ZHLmWMJ9jJ9PTiT58juykZpL7KjwJywFN3Rr2pTSkyQfydf/rk22yS7W8p5DaVUMQ2BQC7oYiU3FjbTM/mYrOA==", + "dev": true, + "requires": { + "jest-diff": "^26.0.0", + "pretty-format": "^26.0.0" + } + }, "@types/node": { "version": "15.6.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-15.6.0.tgz", @@ -4872,6 +4882,12 @@ "is-typedarray": "^1.0.0" } }, + "typescript": { + "version": "3.9.9", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.9.tgz", + "integrity": "sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w==", + "dev": true + }, "union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", diff --git a/tools/prlint/package.json b/tools/prlint/package.json index b257e95734c34..059f214bd2f57 100644 --- a/tools/prlint/package.json +++ b/tools/prlint/package.json @@ -16,11 +16,18 @@ "github-api": "^3.4.0" }, "devDependencies": { + "@types/jest": "^26.0.23", "jest": "^26.6.3", - "make-runnable": "^1.3.9" + "make-runnable": "^1.3.9", + "typescript": "~3.9.9" + }, + "jest": { + "testMatch": [ + "/test/**/?(*.)+(test).js" + ] }, "scripts": { - "build": "echo success", + "build": "tsc --build", "test": "jest", "build+test": "npm run build && npm run test" } diff --git a/tools/prlint/test/lint.test.js b/tools/prlint/test/lint.test.ts similarity index 93% rename from tools/prlint/test/lint.test.js rename to tools/prlint/test/lint.test.ts index 04ca8740e5575..96f0c3f6cdadf 100644 --- a/tools/prlint/test/lint.test.js +++ b/tools/prlint/test/lint.test.ts @@ -1,6 +1,7 @@ -const GitHub = require('github-api'); +import * as GitHub from 'github-api'; +import * as linter from '../lint'; + jest.mock('github-api'); -const linter = require('../lint'); beforeEach(() => { GitHub.mockClear(); @@ -47,7 +48,7 @@ describe('breaking changes format', () => { }); }); -function configureMock(issue, prFiles) { +function configureMock(issue: any, prFiles: any[] | undefined) { GitHub.mockImplementation(() => { return { getIssues: () => { diff --git a/tools/prlint/tsconfig.json b/tools/prlint/tsconfig.json new file mode 100644 index 0000000000000..6b870d2cb95a2 --- /dev/null +++ b/tools/prlint/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ES2018", + "module": "commonjs", + "lib": ["es2018"], + "strict": true, + "alwaysStrict": true, + "declaration": true, + "inlineSourceMap": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "resolveJsonModule": true, + "composite": true, + "incremental": true + }, + "include": ["**/*.ts"] +} From bcc352e3436e58a04033adc4bf19678617cbe653 Mon Sep 17 00:00:00 2001 From: AWS CDK Team Date: Tue, 25 May 2021 17:10:25 +0000 Subject: [PATCH 089/134] chore(release): 1.106.0 --- CHANGELOG.md | 21 +++++++++++++++++++++ version.v1.json | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e601ac973e2c0..cdfe32bba6d39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,27 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [1.106.0](https://github.com/aws/aws-cdk/compare/v1.105.0...v1.106.0) (2021-05-25) + + +### Features + +* allow taskRole to be passed in on creation of an ECS service ([3e257a0](https://github.com/aws/aws-cdk/commit/3e257a0e554851b7393f52bbbea2f5187673e8a7)) +* **appmesh:** add IAM grants for StreamAggregatedResources ([#13596](https://github.com/aws/aws-cdk/issues/13596)) ([f4a2938](https://github.com/aws/aws-cdk/commit/f4a2938cf6773bf80e3316abda82d03aed051108)), closes [#11639](https://github.com/aws/aws-cdk/issues/11639) +* **cfnspec:** cloudformation spec v36.0.0 ([#14791](https://github.com/aws/aws-cdk/issues/14791)) ([3a9f56d](https://github.com/aws/aws-cdk/commit/3a9f56d5167aab6a1bd0bf8b29b53dd8658a2313)) +* **dynamodb:** add ability to enable contributor insights on Table ([#14742](https://github.com/aws/aws-cdk/issues/14742)) ([3c7a89d](https://github.com/aws/aws-cdk/commit/3c7a89de6edaf7a1910bf716419dbe5568d79374)) +* **lambda:** support Principal conditions in Permission ([#14674](https://github.com/aws/aws-cdk/issues/14674)) ([b78a1bb](https://github.com/aws/aws-cdk/commit/b78a1bbf445743d96c8e4f54e7d2e7cac204342a)), closes [#8116](https://github.com/aws/aws-cdk/issues/8116) +* **lambda-nodejs:** pnpm support ([#14772](https://github.com/aws/aws-cdk/issues/14772)) ([b02311c](https://github.com/aws/aws-cdk/commit/b02311cd55b5bdbe408085488dd17816f181fd2c)), closes [#14757](https://github.com/aws/aws-cdk/issues/14757) + + +### Bug Fixes + +* **cognito:** user pool - phoneNumberVerified attribute fails deployment ([#14699](https://github.com/aws/aws-cdk/issues/14699)) ([cd2589f](https://github.com/aws/aws-cdk/commit/cd2589f560600294cc50988a98e69b091c42e3f8)), closes [#14175](https://github.com/aws/aws-cdk/issues/14175) +* **iam:** permissions boundaries not added to custom resource roles ([#14754](https://github.com/aws/aws-cdk/issues/14754)) ([f36feb5](https://github.com/aws/aws-cdk/commit/f36feb52a750a326842903ac4dc23be83e4aee1a)), closes [#13310](https://github.com/aws/aws-cdk/issues/13310) +* **lambda:** changing reserved concurrency fails lambda version deployment ([#14586](https://github.com/aws/aws-cdk/issues/14586)) ([f47d5cb](https://github.com/aws/aws-cdk/commit/f47d5cb48e641515b503bae092cd32071dae2ed9)), closes [#11537](https://github.com/aws/aws-cdk/issues/11537) +* **lambda-nodejs:** esbuild detection with Yarn 2 in PnP mode ([#14739](https://github.com/aws/aws-cdk/issues/14739)) ([5c84696](https://github.com/aws/aws-cdk/commit/5c84696a88f9319af1b2782b747e10f408c4c8fb)) +* **pipelines:** self-update build fails with named pipeline stack ([#14729](https://github.com/aws/aws-cdk/issues/14729)) ([eff9c75](https://github.com/aws/aws-cdk/commit/eff9c7504710929da58eab96c45d7b925132f73e)), closes [#10782](https://github.com/aws/aws-cdk/issues/10782) + ## [1.105.0](https://github.com/aws/aws-cdk/compare/v1.104.0...v1.105.0) (2021-05-19) diff --git a/version.v1.json b/version.v1.json index b99e8c208bdc0..554280be5c164 100644 --- a/version.v1.json +++ b/version.v1.json @@ -1,3 +1,3 @@ { - "version": "1.105.0" + "version": "1.106.0" } From b517d64a64b1ba3dd9340a0b04449fb006feffb8 Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Tue, 25 May 2021 10:17:04 -0700 Subject: [PATCH 090/134] Add `ecs-service-extensions` module name to a feature entry --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cdfe32bba6d39..a7d68b10fd6ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ All notable changes to this project will be documented in this file. See [standa ### Features -* allow taskRole to be passed in on creation of an ECS service ([3e257a0](https://github.com/aws/aws-cdk/commit/3e257a0e554851b7393f52bbbea2f5187673e8a7)) +* **ecs-service-extensions**: allow taskRole to be passed in on creation of an ECS service ([3e257a0](https://github.com/aws/aws-cdk/commit/3e257a0e554851b7393f52bbbea2f5187673e8a7)) * **appmesh:** add IAM grants for StreamAggregatedResources ([#13596](https://github.com/aws/aws-cdk/issues/13596)) ([f4a2938](https://github.com/aws/aws-cdk/commit/f4a2938cf6773bf80e3316abda82d03aed051108)), closes [#11639](https://github.com/aws/aws-cdk/issues/11639) * **cfnspec:** cloudformation spec v36.0.0 ([#14791](https://github.com/aws/aws-cdk/issues/14791)) ([3a9f56d](https://github.com/aws/aws-cdk/commit/3a9f56d5167aab6a1bd0bf8b29b53dd8658a2313)) * **dynamodb:** add ability to enable contributor insights on Table ([#14742](https://github.com/aws/aws-cdk/issues/14742)) ([3c7a89d](https://github.com/aws/aws-cdk/commit/3c7a89de6edaf7a1910bf716419dbe5568d79374)) From fd8445ff1bf94b3dde26211c497bda7211b54dc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Manuel=20Ruiz=20Fern=C3=A1ndez?= Date: Tue, 25 May 2021 22:57:40 +0200 Subject: [PATCH 091/134] fix(rds): Add exception throw when az is defined for multi-az db instance (#14837) This PR is intended to cover a small bug that is produced when you create a RDS instance with both Multi-AZ and Availability Zone properties defined, which are incompatible. That can cause RDS replace if Multi-AZ is setup after to an existing stack with AZ defined previously, because CDK removes AZ setup without any notice to the developer. This contribution will give the developer visibility of this incompatibility so is not possible to apply both parameters at the same time. More details in issue [#10949](https://github.com/aws/aws-cdk/issues/10949) Closes [#10949](https://github.com/aws/aws-cdk/issues/10949) ---- *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.ts | 4 ++++ packages/@aws-cdk/aws-rds/test/instance.test.ts | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/packages/@aws-cdk/aws-rds/lib/instance.ts b/packages/@aws-cdk/aws-rds/lib/instance.ts index 632e3cfec431b..20073ad021b40 100644 --- a/packages/@aws-cdk/aws-rds/lib/instance.ts +++ b/packages/@aws-cdk/aws-rds/lib/instance.ts @@ -654,6 +654,10 @@ abstract class DatabaseInstanceNew extends DatabaseInstanceBase implements IData } this.vpcPlacement = props.vpcSubnets ?? props.vpcPlacement; + if (props.multiAz === true && props.availabilityZone) { + throw new Error('Requesting a specific availability zone is not valid for Multi-AZ instances'); + } + const subnetGroup = props.subnetGroup ?? new SubnetGroup(this, 'SubnetGroup', { description: `Subnet group for ${this.node.id} database`, vpc: this.vpc, diff --git a/packages/@aws-cdk/aws-rds/test/instance.test.ts b/packages/@aws-cdk/aws-rds/test/instance.test.ts index 883f10b93d2db..4621a5bf6512d 100644 --- a/packages/@aws-cdk/aws-rds/test/instance.test.ts +++ b/packages/@aws-cdk/aws-rds/test/instance.test.ts @@ -199,6 +199,17 @@ describe('instance', () => { }); + test('throws when create database with specific AZ and multiAZ enabled', () => { + expect(() => { + new rds.DatabaseInstance(stack, 'Instance', { + engine: rds.DatabaseInstanceEngine.mysql({ version: rds.MysqlEngineVersion.VER_8_0_19 }), + vpc, + multiAz: true, + availabilityZone: 'ew-west-1a', + }); + }).toThrow(/Requesting a specific availability zone is not valid for Multi-AZ instances/); + }); + test('instance with option and parameter group', () => { const optionGroup = new rds.OptionGroup(stack, 'OptionGroup', { engine: rds.DatabaseInstanceEngine.oracleSe2({ version: rds.OracleEngineVersion.VER_19_0_0_0_2020_04_R1 }), From b45d126b8289454be90c1868d4a270677941f05f Mon Sep 17 00:00:00 2001 From: Hari Ohm Prasath Date: Tue, 25 May 2021 21:23:48 +0000 Subject: [PATCH 092/134] chore(ecs): Add support for ECS anywhere (#14811) ### Notes * Customers can now extend the base`ecs.TaskDefinition` and `ecs.BaseService` constructs to provision resources for ECS Anywhere, a sample below. * I will follow up with another PR to introduce two new constructs for ECS anywhere tasks and services (like `ExternalTaskDefinition` & `ExternalService`) so we can directly use these new constructs for provisioning ECS anywhere resources ---- **Sample Task:** ```typescript export class ExternalTaskDefinition extends ecs.TaskDefinition implements ecs.ITaskDefinition { constructor(scope: Construct, id: string, props: ecs.CommonTaskDefinitionProps = {}) { super(scope, id, { ...props, compatibility: ecs.Compatibility.EXTERNAL }); } } ``` **Sample Service:** ```typescript export interface ExternalServiceProps extends ecs.BaseServiceOptions { readonly taskDefinition: ecs.TaskDefinition; } export class ExternalService extends ecs.BaseService implements ecs.IService { constructor(scope: Construct, id: string, props: ExternalServiceProps) { super(scope, id, { ...props, desiredCount: 1, launchType: ecs.LaunchType.EXTERNAL, }, { cluster: props.cluster.clusterName, taskDefinition: props.taskDefinition.taskDefinitionArn }, props.taskDefinition); } } ``` ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../lib/base/_imported-task-definition.ts | 9 ++++- .../@aws-cdk/aws-ecs/lib/base/base-service.ts | 9 +++-- .../aws-ecs/lib/base/task-definition.ts | 34 +++++++++++++++++-- 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/packages/@aws-cdk/aws-ecs/lib/base/_imported-task-definition.ts b/packages/@aws-cdk/aws-ecs/lib/base/_imported-task-definition.ts index accffb9dac20b..ccf39378620e7 100644 --- a/packages/@aws-cdk/aws-ecs/lib/base/_imported-task-definition.ts +++ b/packages/@aws-cdk/aws-ecs/lib/base/_imported-task-definition.ts @@ -3,7 +3,7 @@ import { Resource } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { IEc2TaskDefinition } from '../ec2/ec2-task-definition'; import { IFargateTaskDefinition } from '../fargate/fargate-task-definition'; -import { Compatibility, NetworkMode, isEc2Compatible, isFargateCompatible } from './task-definition'; +import { Compatibility, NetworkMode, isEc2Compatible, isFargateCompatible, isExternalCompatible } from './task-definition'; /** * The properties of ImportedTaskDefinition @@ -105,4 +105,11 @@ export class ImportedTaskDefinition extends Resource implements IEc2TaskDefiniti public get isFargateCompatible(): boolean { return isFargateCompatible(this.compatibility); } + + /** + * Return true if the task definition can be run on a ECS Anywhere cluster + */ + public get isExternalCompatible(): boolean { + return isExternalCompatible(this.compatibility); + } } diff --git a/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts b/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts index 8d46952bf1baa..fa33efd2fe0ab 100644 --- a/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts +++ b/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts @@ -203,7 +203,7 @@ export interface BaseServiceProps extends BaseServiceOptions { * * @see - https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-service.html#cfn-ecs-service-capacityproviderstrategy * - * Valid values are: LaunchType.ECS or LaunchType.FARGATE + * Valid values are: LaunchType.ECS or LaunchType.FARGATE or LaunchType.EXTERNAL */ readonly launchType: LaunchType; } @@ -907,7 +907,12 @@ export enum LaunchType { /** * The service will be launched using the FARGATE launch type */ - FARGATE = 'FARGATE' + FARGATE = 'FARGATE', + + /** + * The service will be launched using the EXTERNAL launch type + */ + EXTERNAL = 'EXTERNAL' } /** diff --git a/packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts b/packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts index 3e7f82160d3fa..0cc89d633160f 100644 --- a/packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts +++ b/packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts @@ -40,6 +40,12 @@ export interface ITaskDefinition extends IResource { */ readonly isFargateCompatible: boolean; + /** + * Return true if the task definition can be run on a ECS Anywhere cluster + */ + readonly isExternalCompatible: boolean; + + /** * The networking mode to use for the containers in the task. */ @@ -104,7 +110,7 @@ export interface TaskDefinitionProps extends CommonTaskDefinitionProps { * * On Fargate, the only supported networking mode is AwsVpc. * - * @default - NetworkMode.Bridge for EC2 tasks, AwsVpc for Fargate tasks. + * @default - NetworkMode.Bridge for EC2 & External tasks, AwsVpc for Fargate tasks. */ readonly networkMode?: NetworkMode; @@ -252,6 +258,13 @@ abstract class TaskDefinitionBase extends Resource implements ITaskDefinition { public get isFargateCompatible(): boolean { return isFargateCompatible(this.compatibility); } + + /** + * Return true if the task definition can be run on a ECS anywhere cluster + */ + public get isExternalCompatible(): boolean { + return isExternalCompatible(this.compatibility); + } } /** @@ -372,6 +385,10 @@ export class TaskDefinition extends TaskDefinitionBase { throw new Error('Cannot use inference accelerators on tasks that run on Fargate'); } + if (this.isExternalCompatible && this.networkMode !== NetworkMode.BRIDGE) { + throw new Error(`External tasks can only have Bridge network mode, got: ${this.networkMode}`); + } + this._executionRole = props.executionRole; this.taskRole = props.taskRole || new iam.Role(this, 'TaskRole', { @@ -391,6 +408,7 @@ export class TaskDefinition extends TaskDefinitionBase { requiresCompatibilities: [ ...(isEc2Compatible(props.compatibility) ? ['EC2'] : []), ...(isFargateCompatible(props.compatibility) ? ['FARGATE'] : []), + ...(isExternalCompatible(props.compatibility) ? ['EXTERNAL'] : []), ], networkMode: this.renderNetworkMode(this.networkMode), placementConstraints: Lazy.any({ @@ -993,7 +1011,12 @@ export enum Compatibility { /** * The task can specify either the EC2 or Fargate launch types. */ - EC2_AND_FARGATE + EC2_AND_FARGATE, + + /** + * The task should specify the External launch type. + */ + EXTERNAL } /** @@ -1027,3 +1050,10 @@ export function isEc2Compatible(compatibility: Compatibility): boolean { export function isFargateCompatible(compatibility: Compatibility): boolean { return [Compatibility.FARGATE, Compatibility.EC2_AND_FARGATE].includes(compatibility); } + +/** + * Return true if the given task definition can be run on a ECS Anywhere cluster + */ +export function isExternalCompatible(compatibility: Compatibility): boolean { + return [Compatibility.EXTERNAL].includes(compatibility); +} From d290b1ddebdefbc634397f4ca681b2a7eaa49526 Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Tue, 25 May 2021 16:11:21 -0700 Subject: [PATCH 093/134] =?UTF-8?q?revert(secretsmanager):=20revert=20"Aut?= =?UTF-8?q?omatically=20grant=20permissions=20to=20ro=E2=80=A6=20(#14869)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …tation Lambda (#14471)" This reverts commit 85e00faf ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-secretsmanager/README.md | 2 - .../lib/rotation-schedule.ts | 30 -------- .../test/rotation-schedule.test.ts | 69 ------------------- 3 files changed, 101 deletions(-) diff --git a/packages/@aws-cdk/aws-secretsmanager/README.md b/packages/@aws-cdk/aws-secretsmanager/README.md index 81c8e3c5e8e24..0c0f45828e9ef 100644 --- a/packages/@aws-cdk/aws-secretsmanager/README.md +++ b/packages/@aws-cdk/aws-secretsmanager/README.md @@ -87,8 +87,6 @@ secret.addRotationSchedule('RotationSchedule', { }); ``` -Note: The required permissions for Lambda to call SecretsManager and the other way round are automatically granted based on [AWS Documentation](https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-required-permissions.html) as long as the Lambda is not imported. - See [Overview of the Lambda Rotation Function](https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html) on how to implement a Lambda Rotation Function. ### Using a Hosted Lambda Function diff --git a/packages/@aws-cdk/aws-secretsmanager/lib/rotation-schedule.ts b/packages/@aws-cdk/aws-secretsmanager/lib/rotation-schedule.ts index 7e00492f2cb2f..1243976963386 100644 --- a/packages/@aws-cdk/aws-secretsmanager/lib/rotation-schedule.ts +++ b/packages/@aws-cdk/aws-secretsmanager/lib/rotation-schedule.ts @@ -1,5 +1,4 @@ import * as ec2 from '@aws-cdk/aws-ec2'; -import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; import { Duration, Resource, Stack } from '@aws-cdk/core'; import { Construct } from 'constructs'; @@ -71,35 +70,6 @@ export class RotationSchedule extends Resource { throw new Error('One of `rotationLambda` or `hostedRotation` must be specified.'); } - if (props.rotationLambda?.permissionsNode.defaultChild) { - props.rotationLambda.grantInvoke(new iam.ServicePrincipal('secretsmanager.amazonaws.com')); - - props.rotationLambda.addToRolePolicy( - new iam.PolicyStatement({ - actions: [ - 'secretsmanager:DescribeSecret', - 'secretsmanager:GetSecretValue', - 'secretsmanager:PutSecretValue', - 'secretsmanager:UpdateSecretVersionStage', - ], - resources: [props.secret.secretArn], - conditions: { - StringEquals: { - 'secretsmanager:resource/AllowRotationLambdaArn': props.rotationLambda.functionArn, - }, - }, - }), - ); - props.rotationLambda.addToRolePolicy( - new iam.PolicyStatement({ - actions: [ - 'secretsmanager:GetRandomPassword', - ], - resources: ['*'], - }), - ); - } - new CfnRotationSchedule(this, 'Resource', { secretId: props.secret.secretArn, rotationLambdaArn: props.rotationLambda?.functionArn, diff --git a/packages/@aws-cdk/aws-secretsmanager/test/rotation-schedule.test.ts b/packages/@aws-cdk/aws-secretsmanager/test/rotation-schedule.test.ts index 3ab3422cd265b..e77336732d51b 100644 --- a/packages/@aws-cdk/aws-secretsmanager/test/rotation-schedule.test.ts +++ b/packages/@aws-cdk/aws-secretsmanager/test/rotation-schedule.test.ts @@ -41,75 +41,6 @@ test('create a rotation schedule with a rotation Lambda', () => { }); }); -test('assign permissions for rotation schedule with a rotation Lambda', () => { - // GIVEN - const secret = new secretsmanager.Secret(stack, 'Secret'); - const rotationLambda = new lambda.Function(stack, 'Lambda', { - runtime: lambda.Runtime.NODEJS_10_X, - code: lambda.Code.fromInline('export.handler = event => event;'), - handler: 'index.handler', - }); - - // WHEN - new secretsmanager.RotationSchedule(stack, 'RotationSchedule', { - secret, - rotationLambda, - }); - - // THEN - expect(stack).toHaveResource('AWS::Lambda::Permission', { - Action: 'lambda:InvokeFunction', - FunctionName: { - 'Fn::GetAtt': [ - 'LambdaD247545B', - 'Arn', - ], - }, - Principal: 'secretsmanager.amazonaws.com', - }); - - expect(stack).toHaveResource('AWS::IAM::Policy', { - PolicyDocument: { - Statement: [ - { - Action: [ - 'secretsmanager:DescribeSecret', - 'secretsmanager:GetSecretValue', - 'secretsmanager:PutSecretValue', - 'secretsmanager:UpdateSecretVersionStage', - ], - Effect: 'Allow', - Resource: { - Ref: 'SecretA720EF05', - }, - Condition: { - StringEquals: { - 'secretsmanager:resource/AllowRotationLambdaArn': { - 'Fn::GetAtt': [ - 'LambdaD247545B', - 'Arn', - ], - }, - }, - }, - }, - { - Action: 'secretsmanager:GetRandomPassword', - Effect: 'Allow', - Resource: '*', - }, - ], - Version: '2012-10-17', - }, - PolicyName: 'LambdaServiceRoleDefaultPolicyDAE46E21', - Roles: [ - { - Ref: 'LambdaServiceRoleA8ED4D3B', - }, - ], - }); -}); - describe('hosted rotation', () => { test('single user not in a vpc', () => { // GIVEN From 8bb4357036f549af1235de81f2f5c528f5fa80f8 Mon Sep 17 00:00:00 2001 From: AWS CDK Automation <43080478+aws-cdk-automation@users.noreply.github.com> Date: Wed, 26 May 2021 11:30:28 +0200 Subject: [PATCH 094/134] feat(cfnspec): cloudformation spec v37.0.0 (#14873) Co-authored-by: AWS CDK Team --- packages/@aws-cdk/aws-apprunner/.eslintrc.js | 3 + packages/@aws-cdk/aws-apprunner/.gitignore | 19 + packages/@aws-cdk/aws-apprunner/.npmignore | 28 + packages/@aws-cdk/aws-apprunner/LICENSE | 201 +++++++ packages/@aws-cdk/aws-apprunner/NOTICE | 2 + packages/@aws-cdk/aws-apprunner/README.md | 20 + .../@aws-cdk/aws-apprunner/jest.config.js | 2 + packages/@aws-cdk/aws-apprunner/lib/index.ts | 2 + packages/@aws-cdk/aws-apprunner/package.json | 101 ++++ .../aws-apprunner/test/apprunner.test.ts | 6 + .../aws-iotcoredeviceadvisor/.eslintrc.js | 3 + .../aws-iotcoredeviceadvisor/.gitignore | 19 + .../aws-iotcoredeviceadvisor/.npmignore | 28 + .../@aws-cdk/aws-iotcoredeviceadvisor/LICENSE | 201 +++++++ .../@aws-cdk/aws-iotcoredeviceadvisor/NOTICE | 2 + .../aws-iotcoredeviceadvisor/README.md | 20 + .../aws-iotcoredeviceadvisor/jest.config.js | 2 + .../aws-iotcoredeviceadvisor/lib/index.ts | 2 + .../aws-iotcoredeviceadvisor/package.json | 101 ++++ .../test/iotcoredeviceadvisor.test.ts | 6 + packages/@aws-cdk/cfnspec/CHANGELOG.md | 34 ++ packages/@aws-cdk/cfnspec/cfn.version | 2 +- ...0_CloudFormationResourceSpecification.json | 493 +++++++++++++++++- .../cloudformation-include/package.json | 4 + packages/aws-cdk-lib/package.json | 2 + packages/decdk/package.json | 2 + packages/monocdk/package.json | 2 + 27 files changed, 1303 insertions(+), 4 deletions(-) create mode 100644 packages/@aws-cdk/aws-apprunner/.eslintrc.js create mode 100644 packages/@aws-cdk/aws-apprunner/.gitignore create mode 100644 packages/@aws-cdk/aws-apprunner/.npmignore create mode 100644 packages/@aws-cdk/aws-apprunner/LICENSE create mode 100644 packages/@aws-cdk/aws-apprunner/NOTICE create mode 100644 packages/@aws-cdk/aws-apprunner/README.md create mode 100644 packages/@aws-cdk/aws-apprunner/jest.config.js create mode 100644 packages/@aws-cdk/aws-apprunner/lib/index.ts create mode 100644 packages/@aws-cdk/aws-apprunner/package.json create mode 100644 packages/@aws-cdk/aws-apprunner/test/apprunner.test.ts create mode 100644 packages/@aws-cdk/aws-iotcoredeviceadvisor/.eslintrc.js create mode 100644 packages/@aws-cdk/aws-iotcoredeviceadvisor/.gitignore create mode 100644 packages/@aws-cdk/aws-iotcoredeviceadvisor/.npmignore create mode 100644 packages/@aws-cdk/aws-iotcoredeviceadvisor/LICENSE create mode 100644 packages/@aws-cdk/aws-iotcoredeviceadvisor/NOTICE create mode 100644 packages/@aws-cdk/aws-iotcoredeviceadvisor/README.md create mode 100644 packages/@aws-cdk/aws-iotcoredeviceadvisor/jest.config.js create mode 100644 packages/@aws-cdk/aws-iotcoredeviceadvisor/lib/index.ts create mode 100644 packages/@aws-cdk/aws-iotcoredeviceadvisor/package.json create mode 100644 packages/@aws-cdk/aws-iotcoredeviceadvisor/test/iotcoredeviceadvisor.test.ts diff --git a/packages/@aws-cdk/aws-apprunner/.eslintrc.js b/packages/@aws-cdk/aws-apprunner/.eslintrc.js new file mode 100644 index 0000000000000..61dd8dd001f63 --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner/.eslintrc.js @@ -0,0 +1,3 @@ +const baseConfig = require('cdk-build-tools/config/eslintrc'); +baseConfig.parserOptions.project = __dirname + '/tsconfig.json'; +module.exports = baseConfig; diff --git a/packages/@aws-cdk/aws-apprunner/.gitignore b/packages/@aws-cdk/aws-apprunner/.gitignore new file mode 100644 index 0000000000000..62ebc95d75ce6 --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner/.gitignore @@ -0,0 +1,19 @@ +*.js +*.js.map +*.d.ts +tsconfig.json +node_modules +*.generated.ts +dist +.jsii + +.LAST_BUILD +.nyc_output +coverage +.nycrc +.LAST_PACKAGE +*.snk +nyc.config.js +!.eslintrc.js +!jest.config.js +junit.xml diff --git a/packages/@aws-cdk/aws-apprunner/.npmignore b/packages/@aws-cdk/aws-apprunner/.npmignore new file mode 100644 index 0000000000000..e4486030fcb17 --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner/.npmignore @@ -0,0 +1,28 @@ +# Don't include original .ts files when doing `npm pack` +*.ts +!*.d.ts +coverage +.nyc_output +*.tgz + +dist +.LAST_PACKAGE +.LAST_BUILD +!*.js + +# Include .jsii +!.jsii + +*.snk + +*.tsbuildinfo + +tsconfig.json + +.eslintrc.js +jest.config.js + +# exclude cdk artifacts +**/cdk.out +junit.xml +test/ diff --git a/packages/@aws-cdk/aws-apprunner/LICENSE b/packages/@aws-cdk/aws-apprunner/LICENSE new file mode 100644 index 0000000000000..28e4bdcec77ec --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/@aws-cdk/aws-apprunner/NOTICE b/packages/@aws-cdk/aws-apprunner/NOTICE new file mode 100644 index 0000000000000..5fc3826926b5b --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner/NOTICE @@ -0,0 +1,2 @@ +AWS Cloud Development Kit (AWS CDK) +Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/@aws-cdk/aws-apprunner/README.md b/packages/@aws-cdk/aws-apprunner/README.md new file mode 100644 index 0000000000000..f6619f99bd149 --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner/README.md @@ -0,0 +1,20 @@ +# AWS::AppRunner Construct Library + + +--- + +![cfn-resources: Stable](https://img.shields.io/badge/cfn--resources-stable-success.svg?style=for-the-badge) + +> All classes with the `Cfn` prefix in this module ([CFN Resources]) are always stable and safe to use. +> +> [CFN Resources]: https://docs.aws.amazon.com/cdk/latest/guide/constructs.html#constructs_lib + +--- + + + +This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project. + +```ts +import apprunner = require('@aws-cdk/aws-apprunner'); +``` diff --git a/packages/@aws-cdk/aws-apprunner/jest.config.js b/packages/@aws-cdk/aws-apprunner/jest.config.js new file mode 100644 index 0000000000000..54e28beb9798b --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner/jest.config.js @@ -0,0 +1,2 @@ +const baseConfig = require('cdk-build-tools/config/jest.config'); +module.exports = baseConfig; diff --git a/packages/@aws-cdk/aws-apprunner/lib/index.ts b/packages/@aws-cdk/aws-apprunner/lib/index.ts new file mode 100644 index 0000000000000..bebaa074b0dd0 --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner/lib/index.ts @@ -0,0 +1,2 @@ +// AWS::AppRunner CloudFormation Resources: +export * from './apprunner.generated'; diff --git a/packages/@aws-cdk/aws-apprunner/package.json b/packages/@aws-cdk/aws-apprunner/package.json new file mode 100644 index 0000000000000..e45da28fd3637 --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner/package.json @@ -0,0 +1,101 @@ +{ + "name": "@aws-cdk/aws-apprunner", + "version": "0.0.0", + "description": "The CDK Construct Library for AWS::AppRunner", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "jsii": { + "outdir": "dist", + "projectReferences": true, + "targets": { + "dotnet": { + "namespace": "Amazon.CDK.AWS.AppRunner", + "packageId": "Amazon.CDK.AWS.AppRunner", + "signAssembly": true, + "assemblyOriginatorKeyFile": "../../key.snk", + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/master/logo/default-256-dark.png" + }, + "java": { + "package": "software.amazon.awscdk.services.apprunner", + "maven": { + "groupId": "software.amazon.awscdk", + "artifactId": "apprunner" + } + }, + "python": { + "classifiers": [ + "Framework :: AWS CDK", + "Framework :: AWS CDK :: 1" + ], + "distName": "aws-cdk.aws-apprunner", + "module": "aws_cdk.aws_apprunner" + } + } + }, + "repository": { + "type": "git", + "url": "https://github.com/aws/aws-cdk.git", + "directory": "packages/@aws-cdk/aws-apprunner" + }, + "homepage": "https://github.com/aws/aws-cdk", + "scripts": { + "build": "cdk-build", + "watch": "cdk-watch", + "lint": "cdk-lint", + "test": "cdk-test", + "integ": "cdk-integ", + "pkglint": "pkglint -f", + "package": "cdk-package", + "awslint": "cdk-awslint", + "cfn2ts": "cfn2ts", + "build+test": "yarn build && yarn test", + "build+test+package": "yarn build+test && yarn package", + "compat": "cdk-compat", + "gen": "cfn2ts", + "rosetta:extract": "yarn --silent jsii-rosetta extract" + }, + "cdk-build": { + "cloudformation": "AWS::AppRunner", + "jest": true, + "env": { + "AWSLINT_BASE_CONSTRUCT": "true" + } + }, + "keywords": [ + "aws", + "cdk", + "constructs", + "AWS::AppRunner", + "aws-apprunner" + ], + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com", + "organization": true + }, + "license": "Apache-2.0", + "devDependencies": { + "@types/jest": "^26.0.22", + "@aws-cdk/assert-internal": "0.0.0", + "cdk-build-tools": "0.0.0", + "cfn2ts": "0.0.0", + "pkglint": "0.0.0" + }, + "dependencies": { + "@aws-cdk/core": "0.0.0" + }, + "peerDependencies": { + "@aws-cdk/core": "0.0.0" + }, + "engines": { + "node": ">= 10.13.0 <13 || >=13.7.0" + }, + "stability": "experimental", + "maturity": "cfn-only", + "awscdkio": { + "announce": false + }, + "publishConfig": { + "tag": "latest" + } +} diff --git a/packages/@aws-cdk/aws-apprunner/test/apprunner.test.ts b/packages/@aws-cdk/aws-apprunner/test/apprunner.test.ts new file mode 100644 index 0000000000000..c4505ad966984 --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner/test/apprunner.test.ts @@ -0,0 +1,6 @@ +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-iotcoredeviceadvisor/.eslintrc.js b/packages/@aws-cdk/aws-iotcoredeviceadvisor/.eslintrc.js new file mode 100644 index 0000000000000..61dd8dd001f63 --- /dev/null +++ b/packages/@aws-cdk/aws-iotcoredeviceadvisor/.eslintrc.js @@ -0,0 +1,3 @@ +const baseConfig = require('cdk-build-tools/config/eslintrc'); +baseConfig.parserOptions.project = __dirname + '/tsconfig.json'; +module.exports = baseConfig; diff --git a/packages/@aws-cdk/aws-iotcoredeviceadvisor/.gitignore b/packages/@aws-cdk/aws-iotcoredeviceadvisor/.gitignore new file mode 100644 index 0000000000000..62ebc95d75ce6 --- /dev/null +++ b/packages/@aws-cdk/aws-iotcoredeviceadvisor/.gitignore @@ -0,0 +1,19 @@ +*.js +*.js.map +*.d.ts +tsconfig.json +node_modules +*.generated.ts +dist +.jsii + +.LAST_BUILD +.nyc_output +coverage +.nycrc +.LAST_PACKAGE +*.snk +nyc.config.js +!.eslintrc.js +!jest.config.js +junit.xml diff --git a/packages/@aws-cdk/aws-iotcoredeviceadvisor/.npmignore b/packages/@aws-cdk/aws-iotcoredeviceadvisor/.npmignore new file mode 100644 index 0000000000000..e4486030fcb17 --- /dev/null +++ b/packages/@aws-cdk/aws-iotcoredeviceadvisor/.npmignore @@ -0,0 +1,28 @@ +# Don't include original .ts files when doing `npm pack` +*.ts +!*.d.ts +coverage +.nyc_output +*.tgz + +dist +.LAST_PACKAGE +.LAST_BUILD +!*.js + +# Include .jsii +!.jsii + +*.snk + +*.tsbuildinfo + +tsconfig.json + +.eslintrc.js +jest.config.js + +# exclude cdk artifacts +**/cdk.out +junit.xml +test/ diff --git a/packages/@aws-cdk/aws-iotcoredeviceadvisor/LICENSE b/packages/@aws-cdk/aws-iotcoredeviceadvisor/LICENSE new file mode 100644 index 0000000000000..28e4bdcec77ec --- /dev/null +++ b/packages/@aws-cdk/aws-iotcoredeviceadvisor/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/@aws-cdk/aws-iotcoredeviceadvisor/NOTICE b/packages/@aws-cdk/aws-iotcoredeviceadvisor/NOTICE new file mode 100644 index 0000000000000..5fc3826926b5b --- /dev/null +++ b/packages/@aws-cdk/aws-iotcoredeviceadvisor/NOTICE @@ -0,0 +1,2 @@ +AWS Cloud Development Kit (AWS CDK) +Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/@aws-cdk/aws-iotcoredeviceadvisor/README.md b/packages/@aws-cdk/aws-iotcoredeviceadvisor/README.md new file mode 100644 index 0000000000000..abd988512eeb5 --- /dev/null +++ b/packages/@aws-cdk/aws-iotcoredeviceadvisor/README.md @@ -0,0 +1,20 @@ +# AWS::IoTCoreDeviceAdvisor Construct Library + + +--- + +![cfn-resources: Stable](https://img.shields.io/badge/cfn--resources-stable-success.svg?style=for-the-badge) + +> All classes with the `Cfn` prefix in this module ([CFN Resources]) are always stable and safe to use. +> +> [CFN Resources]: https://docs.aws.amazon.com/cdk/latest/guide/constructs.html#constructs_lib + +--- + + + +This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project. + +```ts +import iotcoredeviceadvisor = require('@aws-cdk/aws-iotcoredeviceadvisor'); +``` diff --git a/packages/@aws-cdk/aws-iotcoredeviceadvisor/jest.config.js b/packages/@aws-cdk/aws-iotcoredeviceadvisor/jest.config.js new file mode 100644 index 0000000000000..54e28beb9798b --- /dev/null +++ b/packages/@aws-cdk/aws-iotcoredeviceadvisor/jest.config.js @@ -0,0 +1,2 @@ +const baseConfig = require('cdk-build-tools/config/jest.config'); +module.exports = baseConfig; diff --git a/packages/@aws-cdk/aws-iotcoredeviceadvisor/lib/index.ts b/packages/@aws-cdk/aws-iotcoredeviceadvisor/lib/index.ts new file mode 100644 index 0000000000000..0f565b114706d --- /dev/null +++ b/packages/@aws-cdk/aws-iotcoredeviceadvisor/lib/index.ts @@ -0,0 +1,2 @@ +// AWS::IoTCoreDeviceAdvisor CloudFormation Resources: +export * from './iotcoredeviceadvisor.generated'; diff --git a/packages/@aws-cdk/aws-iotcoredeviceadvisor/package.json b/packages/@aws-cdk/aws-iotcoredeviceadvisor/package.json new file mode 100644 index 0000000000000..e6e889086eac0 --- /dev/null +++ b/packages/@aws-cdk/aws-iotcoredeviceadvisor/package.json @@ -0,0 +1,101 @@ +{ + "name": "@aws-cdk/aws-iotcoredeviceadvisor", + "version": "0.0.0", + "description": "The CDK Construct Library for AWS::IoTCoreDeviceAdvisor", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "jsii": { + "outdir": "dist", + "projectReferences": true, + "targets": { + "dotnet": { + "namespace": "Amazon.CDK.AWS.IoTCoreDeviceAdvisor", + "packageId": "Amazon.CDK.AWS.IoTCoreDeviceAdvisor", + "signAssembly": true, + "assemblyOriginatorKeyFile": "../../key.snk", + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/master/logo/default-256-dark.png" + }, + "java": { + "package": "software.amazon.awscdk.services.iotcoredeviceadvisor", + "maven": { + "groupId": "software.amazon.awscdk", + "artifactId": "iotcoredeviceadvisor" + } + }, + "python": { + "classifiers": [ + "Framework :: AWS CDK", + "Framework :: AWS CDK :: 1" + ], + "distName": "aws-cdk.aws-iotcoredeviceadvisor", + "module": "aws_cdk.aws_iotcoredeviceadvisor" + } + } + }, + "repository": { + "type": "git", + "url": "https://github.com/aws/aws-cdk.git", + "directory": "packages/@aws-cdk/aws-iotcoredeviceadvisor" + }, + "homepage": "https://github.com/aws/aws-cdk", + "scripts": { + "build": "cdk-build", + "watch": "cdk-watch", + "lint": "cdk-lint", + "test": "cdk-test", + "integ": "cdk-integ", + "pkglint": "pkglint -f", + "package": "cdk-package", + "awslint": "cdk-awslint", + "cfn2ts": "cfn2ts", + "build+test": "yarn build && yarn test", + "build+test+package": "yarn build+test && yarn package", + "compat": "cdk-compat", + "gen": "cfn2ts", + "rosetta:extract": "yarn --silent jsii-rosetta extract" + }, + "cdk-build": { + "cloudformation": "AWS::IoTCoreDeviceAdvisor", + "jest": true, + "env": { + "AWSLINT_BASE_CONSTRUCT": "true" + } + }, + "keywords": [ + "aws", + "cdk", + "constructs", + "AWS::IoTCoreDeviceAdvisor", + "aws-iotcoredeviceadvisor" + ], + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com", + "organization": true + }, + "license": "Apache-2.0", + "devDependencies": { + "@types/jest": "^26.0.22", + "@aws-cdk/assert-internal": "0.0.0", + "cdk-build-tools": "0.0.0", + "cfn2ts": "0.0.0", + "pkglint": "0.0.0" + }, + "dependencies": { + "@aws-cdk/core": "0.0.0" + }, + "peerDependencies": { + "@aws-cdk/core": "0.0.0" + }, + "engines": { + "node": ">= 10.13.0 <13 || >=13.7.0" + }, + "stability": "experimental", + "maturity": "cfn-only", + "awscdkio": { + "announce": false + }, + "publishConfig": { + "tag": "latest" + } +} diff --git a/packages/@aws-cdk/aws-iotcoredeviceadvisor/test/iotcoredeviceadvisor.test.ts b/packages/@aws-cdk/aws-iotcoredeviceadvisor/test/iotcoredeviceadvisor.test.ts new file mode 100644 index 0000000000000..c4505ad966984 --- /dev/null +++ b/packages/@aws-cdk/aws-iotcoredeviceadvisor/test/iotcoredeviceadvisor.test.ts @@ -0,0 +1,6 @@ +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/cfnspec/CHANGELOG.md b/packages/@aws-cdk/cfnspec/CHANGELOG.md index 2a8a6cbc0f9a1..0c6d276cbc8d3 100644 --- a/packages/@aws-cdk/cfnspec/CHANGELOG.md +++ b/packages/@aws-cdk/cfnspec/CHANGELOG.md @@ -1,3 +1,37 @@ +# CloudFormation Resource Specification v37.0.0 + +## New Resource Types + +* AWS::AppRunner::Service +* AWS::EC2::TransitGatewayPeeringAttachment +* AWS::IoTCoreDeviceAdvisor::SuiteDefinition + +## Attribute Changes + + +## Property Changes + +* AWS::MediaPackage::Channel EgressAccessLogs (__added__) +* AWS::MediaPackage::Channel IngressAccessLogs (__added__) +* AWS::MediaPackage::OriginEndpoint Id.UpdateType (__changed__) + * Old: Mutable + * New: Immutable +* AWS::MediaPackage::PackagingConfiguration Id.UpdateType (__changed__) + * Old: Mutable + * New: Immutable +* AWS::MediaPackage::PackagingGroup EgressAccessLogs (__added__) + +## Property Type Changes + +* AWS::MediaPackage::Channel.LogConfiguration (__added__) +* AWS::MediaPackage::PackagingGroup.LogConfiguration (__added__) +* AWS::MediaPackage::OriginEndpoint.CmafEncryption ConstantInitializationVector (__added__) +* AWS::MediaPackage::OriginEndpoint.DashPackage UtcTiming (__added__) +* AWS::MediaPackage::OriginEndpoint.DashPackage UtcTimingUri (__added__) +* AWS::MediaPackage::PackagingConfiguration.CmafPackage IncludeEncoderConfigurationInSegments (__added__) +* AWS::MediaPackage::PackagingConfiguration.DashPackage IncludeEncoderConfigurationInSegments (__added__) + + # CloudFormation Resource Specification v36.0.0 ## New Resource Types diff --git a/packages/@aws-cdk/cfnspec/cfn.version b/packages/@aws-cdk/cfnspec/cfn.version index 2b8c161364217..09374235967a4 100644 --- a/packages/@aws-cdk/cfnspec/cfn.version +++ b/packages/@aws-cdk/cfnspec/cfn.version @@ -1 +1 @@ -36.0.0 +37.0.0 diff --git a/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json b/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json index 5a9f29a6bc518..1487b4a21e720 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json +++ b/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json @@ -6015,6 +6015,284 @@ } } }, + "AWS::AppRunner::Service.AuthenticationConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-authenticationconfiguration.html", + "Properties": { + "AccessRoleArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-authenticationconfiguration.html#cfn-apprunner-service-authenticationconfiguration-accessrolearn", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "ConnectionArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-authenticationconfiguration.html#cfn-apprunner-service-authenticationconfiguration-connectionarn", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::AppRunner::Service.CodeConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-codeconfiguration.html", + "Properties": { + "CodeConfigurationValues": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-codeconfiguration.html#cfn-apprunner-service-codeconfiguration-codeconfigurationvalues", + "Required": false, + "Type": "CodeConfigurationValues", + "UpdateType": "Mutable" + }, + "ConfigurationSource": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-codeconfiguration.html#cfn-apprunner-service-codeconfiguration-configurationsource", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::AppRunner::Service.CodeConfigurationValues": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-codeconfigurationvalues.html", + "Properties": { + "BuildCommand": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-codeconfigurationvalues.html#cfn-apprunner-service-codeconfigurationvalues-buildcommand", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Port": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-codeconfigurationvalues.html#cfn-apprunner-service-codeconfigurationvalues-port", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Runtime": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-codeconfigurationvalues.html#cfn-apprunner-service-codeconfigurationvalues-runtime", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "RuntimeEnvironmentVariables": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-codeconfigurationvalues.html#cfn-apprunner-service-codeconfigurationvalues-runtimeenvironmentvariables", + "ItemType": "KeyValuePair", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "StartCommand": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-codeconfigurationvalues.html#cfn-apprunner-service-codeconfigurationvalues-startcommand", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::AppRunner::Service.CodeRepository": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-coderepository.html", + "Properties": { + "CodeConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-coderepository.html#cfn-apprunner-service-coderepository-codeconfiguration", + "Required": false, + "Type": "CodeConfiguration", + "UpdateType": "Mutable" + }, + "RepositoryUrl": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-coderepository.html#cfn-apprunner-service-coderepository-repositoryurl", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "SourceCodeVersion": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-coderepository.html#cfn-apprunner-service-coderepository-sourcecodeversion", + "Required": true, + "Type": "SourceCodeVersion", + "UpdateType": "Mutable" + } + } + }, + "AWS::AppRunner::Service.EncryptionConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-encryptionconfiguration.html", + "Properties": { + "KmsKey": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-encryptionconfiguration.html#cfn-apprunner-service-encryptionconfiguration-kmskey", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + } + } + }, + "AWS::AppRunner::Service.HealthCheckConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-healthcheckconfiguration.html", + "Properties": { + "HealthyThreshold": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-healthcheckconfiguration.html#cfn-apprunner-service-healthcheckconfiguration-healthythreshold", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "Interval": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-healthcheckconfiguration.html#cfn-apprunner-service-healthcheckconfiguration-interval", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "Path": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-healthcheckconfiguration.html#cfn-apprunner-service-healthcheckconfiguration-path", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Protocol": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-healthcheckconfiguration.html#cfn-apprunner-service-healthcheckconfiguration-protocol", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Timeout": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-healthcheckconfiguration.html#cfn-apprunner-service-healthcheckconfiguration-timeout", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "UnhealthyThreshold": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-healthcheckconfiguration.html#cfn-apprunner-service-healthcheckconfiguration-unhealthythreshold", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::AppRunner::Service.ImageConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-imageconfiguration.html", + "Properties": { + "Port": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-imageconfiguration.html#cfn-apprunner-service-imageconfiguration-port", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "RuntimeEnvironmentVariables": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-imageconfiguration.html#cfn-apprunner-service-imageconfiguration-runtimeenvironmentvariables", + "ItemType": "KeyValuePair", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "StartCommand": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-imageconfiguration.html#cfn-apprunner-service-imageconfiguration-startcommand", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::AppRunner::Service.ImageRepository": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-imagerepository.html", + "Properties": { + "ImageConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-imagerepository.html#cfn-apprunner-service-imagerepository-imageconfiguration", + "Required": false, + "Type": "ImageConfiguration", + "UpdateType": "Mutable" + }, + "ImageIdentifier": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-imagerepository.html#cfn-apprunner-service-imagerepository-imageidentifier", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "ImageRepositoryType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-imagerepository.html#cfn-apprunner-service-imagerepository-imagerepositorytype", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::AppRunner::Service.InstanceConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-instanceconfiguration.html", + "Properties": { + "Cpu": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-instanceconfiguration.html#cfn-apprunner-service-instanceconfiguration-cpu", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "InstanceRoleArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-instanceconfiguration.html#cfn-apprunner-service-instanceconfiguration-instancerolearn", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Memory": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-instanceconfiguration.html#cfn-apprunner-service-instanceconfiguration-memory", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::AppRunner::Service.KeyValuePair": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-keyvaluepair.html", + "Properties": { + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-keyvaluepair.html#cfn-apprunner-service-keyvaluepair-name", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Value": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-keyvaluepair.html#cfn-apprunner-service-keyvaluepair-value", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::AppRunner::Service.SourceCodeVersion": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-sourcecodeversion.html", + "Properties": { + "Type": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-sourcecodeversion.html#cfn-apprunner-service-sourcecodeversion-type", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "Value": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-sourcecodeversion.html#cfn-apprunner-service-sourcecodeversion-value", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::AppRunner::Service.SourceConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-sourceconfiguration.html", + "Properties": { + "AuthenticationConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-sourceconfiguration.html#cfn-apprunner-service-sourceconfiguration-authenticationconfiguration", + "Required": false, + "Type": "AuthenticationConfiguration", + "UpdateType": "Mutable" + }, + "AutoDeploymentsEnabled": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-sourceconfiguration.html#cfn-apprunner-service-sourceconfiguration-autodeploymentsenabled", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "CodeRepository": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-sourceconfiguration.html#cfn-apprunner-service-sourceconfiguration-coderepository", + "Required": false, + "Type": "CodeRepository", + "UpdateType": "Mutable" + }, + "ImageRepository": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apprunner-service-sourceconfiguration.html#cfn-apprunner-service-sourceconfiguration-imagerepository", + "Required": false, + "Type": "ImageRepository", + "UpdateType": "Mutable" + } + } + }, "AWS::AppStream::DirectoryConfig.ServiceAccountCredentials": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appstream-directoryconfig-serviceaccountcredentials.html", "Properties": { @@ -45174,6 +45452,17 @@ } } }, + "AWS::MediaPackage::Channel.LogConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mediapackage-channel-logconfiguration.html", + "Properties": { + "LogGroupName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mediapackage-channel-logconfiguration.html#cfn-mediapackage-channel-logconfiguration-loggroupname", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, "AWS::MediaPackage::OriginEndpoint.Authorization": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mediapackage-originendpoint-authorization.html", "Properties": { @@ -45194,6 +45483,12 @@ "AWS::MediaPackage::OriginEndpoint.CmafEncryption": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mediapackage-originendpoint-cmafencryption.html", "Properties": { + "ConstantInitializationVector": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mediapackage-originendpoint-cmafencryption.html#cfn-mediapackage-originendpoint-cmafencryption-constantinitializationvector", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, "KeyRotationIntervalSeconds": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mediapackage-originendpoint-cmafencryption.html#cfn-mediapackage-originendpoint-cmafencryption-keyrotationintervalseconds", "PrimitiveType": "Integer", @@ -45343,6 +45638,18 @@ "PrimitiveType": "Integer", "Required": false, "UpdateType": "Mutable" + }, + "UtcTiming": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mediapackage-originendpoint-dashpackage.html#cfn-mediapackage-originendpoint-dashpackage-utctiming", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "UtcTimingUri": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mediapackage-originendpoint-dashpackage.html#cfn-mediapackage-originendpoint-dashpackage-utctiminguri", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" } } }, @@ -45645,6 +45952,12 @@ "Type": "List", "UpdateType": "Mutable" }, + "IncludeEncoderConfigurationInSegments": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mediapackage-packagingconfiguration-cmafpackage.html#cfn-mediapackage-packagingconfiguration-cmafpackage-includeencoderconfigurationinsegments", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, "SegmentDurationSeconds": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mediapackage-packagingconfiguration-cmafpackage.html#cfn-mediapackage-packagingconfiguration-cmafpackage-segmentdurationseconds", "PrimitiveType": "Integer", @@ -45715,6 +46028,12 @@ "Type": "DashEncryption", "UpdateType": "Mutable" }, + "IncludeEncoderConfigurationInSegments": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mediapackage-packagingconfiguration-dashpackage.html#cfn-mediapackage-packagingconfiguration-dashpackage-includeencoderconfigurationinsegments", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, "PeriodTriggers": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mediapackage-packagingconfiguration-dashpackage.html#cfn-mediapackage-packagingconfiguration-dashpackage-periodtriggers", "PrimitiveItemType": "String", @@ -45946,6 +46265,17 @@ } } }, + "AWS::MediaPackage::PackagingGroup.LogConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mediapackage-packaginggroup-logconfiguration.html", + "Properties": { + "LogGroupName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mediapackage-packaginggroup-logconfiguration.html#cfn-mediapackage-packaginggroup-logconfiguration-loggroupname", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, "AWS::MediaStore::Container.CorsRule": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mediastore-container-corsrule.html", "Properties": { @@ -59455,7 +59785,7 @@ } } }, - "ResourceSpecificationVersion": "36.0.0", + "ResourceSpecificationVersion": "37.0.0", "ResourceTypes": { "AWS::ACMPCA::Certificate": { "Attributes": { @@ -62503,6 +62833,68 @@ } } }, + "AWS::AppRunner::Service": { + "Attributes": { + "ServiceArn": { + "PrimitiveType": "String" + }, + "ServiceId": { + "PrimitiveType": "String" + }, + "ServiceUrl": { + "PrimitiveType": "String" + }, + "Status": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apprunner-service.html", + "Properties": { + "AutoScalingConfigurationArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apprunner-service.html#cfn-apprunner-service-autoscalingconfigurationarn", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "EncryptionConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apprunner-service.html#cfn-apprunner-service-encryptionconfiguration", + "Required": false, + "Type": "EncryptionConfiguration", + "UpdateType": "Immutable" + }, + "HealthCheckConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apprunner-service.html#cfn-apprunner-service-healthcheckconfiguration", + "Required": false, + "Type": "HealthCheckConfiguration", + "UpdateType": "Mutable" + }, + "InstanceConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apprunner-service.html#cfn-apprunner-service-instanceconfiguration", + "Required": false, + "Type": "InstanceConfiguration", + "UpdateType": "Mutable" + }, + "ServiceName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apprunner-service.html#cfn-apprunner-service-servicename", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "SourceConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apprunner-service.html#cfn-apprunner-service-sourceconfiguration", + "Required": true, + "Type": "SourceConfiguration", + "UpdateType": "Mutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apprunner-service.html#cfn-apprunner-service-tags", + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Immutable" + } + } + }, "AWS::AppStream::DirectoryConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-appstream-directoryconfig.html", "Properties": { @@ -72615,6 +73007,53 @@ } } }, + "AWS::EC2::TransitGatewayPeeringAttachment": { + "Attributes": { + "CreationTime": { + "PrimitiveType": "String" + }, + "State": { + "PrimitiveType": "String" + }, + "TransitGatewayAttachmentId": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-transitgatewaypeeringattachment.html", + "Properties": { + "PeerAccountId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-transitgatewaypeeringattachment.html#cfn-ec2-transitgatewaypeeringattachment-peeraccountid", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "PeerRegion": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-transitgatewaypeeringattachment.html#cfn-ec2-transitgatewaypeeringattachment-peerregion", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "PeerTransitGatewayId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-transitgatewaypeeringattachment.html#cfn-ec2-transitgatewaypeeringattachment-peertransitgatewayid", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-transitgatewaypeeringattachment.html#cfn-ec2-transitgatewaypeeringattachment-tags", + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, + "TransitGatewayId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-transitgatewaypeeringattachment.html#cfn-ec2-transitgatewaypeeringattachment-transitgatewayid", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + } + } + }, "AWS::EC2::TransitGatewayRoute": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-transitgatewayroute.html", "Properties": { @@ -81482,6 +81921,36 @@ } } }, + "AWS::IoTCoreDeviceAdvisor::SuiteDefinition": { + "Attributes": { + "SuiteDefinitionArn": { + "PrimitiveType": "String" + }, + "SuiteDefinitionId": { + "PrimitiveType": "String" + }, + "SuiteDefinitionVersion": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iotcoredeviceadvisor-suitedefinition.html", + "Properties": { + "SuiteDefinitionConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iotcoredeviceadvisor-suitedefinition.html#cfn-iotcoredeviceadvisor-suitedefinition-suitedefinitionconfiguration", + "PrimitiveType": "Json", + "Required": true, + "UpdateType": "Mutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iotcoredeviceadvisor-suitedefinition.html#cfn-iotcoredeviceadvisor-suitedefinition-tags", + "DuplicatesAllowed": false, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, "AWS::IoTEvents::DetectorModel": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iotevents-detectormodel.html", "Properties": { @@ -85038,12 +85507,24 @@ "Required": false, "UpdateType": "Mutable" }, + "EgressAccessLogs": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-mediapackage-channel.html#cfn-mediapackage-channel-egressaccesslogs", + "Required": false, + "Type": "LogConfiguration", + "UpdateType": "Mutable" + }, "Id": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-mediapackage-channel.html#cfn-mediapackage-channel-id", "PrimitiveType": "String", "Required": true, "UpdateType": "Immutable" }, + "IngressAccessLogs": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-mediapackage-channel.html#cfn-mediapackage-channel-ingressaccesslogs", + "Required": false, + "Type": "LogConfiguration", + "UpdateType": "Mutable" + }, "Tags": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-mediapackage-channel.html#cfn-mediapackage-channel-tags", "DuplicatesAllowed": false, @@ -85105,7 +85586,7 @@ "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-mediapackage-originendpoint.html#cfn-mediapackage-originendpoint-id", "PrimitiveType": "String", "Required": true, - "UpdateType": "Mutable" + "UpdateType": "Immutable" }, "ManifestName": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-mediapackage-originendpoint.html#cfn-mediapackage-originendpoint-manifestname", @@ -85184,7 +85665,7 @@ "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-mediapackage-packagingconfiguration.html#cfn-mediapackage-packagingconfiguration-id", "PrimitiveType": "String", "Required": true, - "UpdateType": "Mutable" + "UpdateType": "Immutable" }, "MssPackage": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-mediapackage-packagingconfiguration.html#cfn-mediapackage-packagingconfiguration-msspackage", @@ -85225,6 +85706,12 @@ "Type": "Authorization", "UpdateType": "Mutable" }, + "EgressAccessLogs": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-mediapackage-packaginggroup.html#cfn-mediapackage-packaginggroup-egressaccesslogs", + "Required": false, + "Type": "LogConfiguration", + "UpdateType": "Mutable" + }, "Id": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-mediapackage-packaginggroup.html#cfn-mediapackage-packaginggroup-id", "PrimitiveType": "String", diff --git a/packages/@aws-cdk/cloudformation-include/package.json b/packages/@aws-cdk/cloudformation-include/package.json index c0dec59603b9d..db6efaa5d4416 100644 --- a/packages/@aws-cdk/cloudformation-include/package.json +++ b/packages/@aws-cdk/cloudformation-include/package.json @@ -78,6 +78,7 @@ "@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-athena": "0.0.0", @@ -151,6 +152,7 @@ "@aws-cdk/aws-iot": "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", @@ -240,6 +242,7 @@ "@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-athena": "0.0.0", @@ -313,6 +316,7 @@ "@aws-cdk/aws-iot": "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", diff --git a/packages/aws-cdk-lib/package.json b/packages/aws-cdk-lib/package.json index 4556f3c89a433..126f6579c9e80 100644 --- a/packages/aws-cdk-lib/package.json +++ b/packages/aws-cdk-lib/package.json @@ -125,6 +125,7 @@ "@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-athena": "0.0.0", @@ -211,6 +212,7 @@ "@aws-cdk/aws-iot": "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", diff --git a/packages/decdk/package.json b/packages/decdk/package.json index fd0de8150e914..52ed71e1f050e 100644 --- a/packages/decdk/package.json +++ b/packages/decdk/package.json @@ -43,6 +43,7 @@ "@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-athena": "0.0.0", @@ -128,6 +129,7 @@ "@aws-cdk/aws-iot": "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", diff --git a/packages/monocdk/package.json b/packages/monocdk/package.json index c23f29c7cc1f4..a2f21672d76cf 100644 --- a/packages/monocdk/package.json +++ b/packages/monocdk/package.json @@ -126,6 +126,7 @@ "@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-athena": "0.0.0", @@ -212,6 +213,7 @@ "@aws-cdk/aws-iot": "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", From 92bbc63ecc357d235ab9ec692a6ade13ed301c80 Mon Sep 17 00:00:00 2001 From: AWS CDK Automation <43080478+aws-cdk-automation@users.noreply.github.com> Date: Wed, 26 May 2021 16:30:26 +0200 Subject: [PATCH 095/134] chore: npm-check-updates && yarn upgrade (#14875) Ran npm-check-updates and yarn upgrade to keep the `yarn.lock` file up-to-date. --- package.json | 2 +- packages/@aws-cdk/assets/package.json | 2 +- packages/@aws-cdk/aws-apprunner/package.json | 2 +- .../package.json | 4 +- .../aws-codepipeline-actions/package.json | 2 +- .../aws-global-table-coordinator/package.json | 4 +- .../aws-iotcoredeviceadvisor/package.json | 2 +- .../@aws-cdk/aws-lambda-nodejs/package.json | 2 +- packages/@aws-cdk/aws-lambda/package.json | 2 +- .../@aws-cdk/aws-ssmcontacts/package.json | 2 +- .../@aws-cdk/aws-ssmincidents/package.json | 2 +- packages/@aws-cdk/core/package.json | 4 +- packages/aws-cdk/package.json | 2 +- packages/awslint/package.json | 8 +- tools/cdk-build-tools/package.json | 8 +- tools/eslint-plugin-cdk/package.json | 6 +- tools/pkglint/package.json | 8 +- tools/prlint/package.json | 4 +- yarn.lock | 317 +++++++++++++----- 19 files changed, 257 insertions(+), 126 deletions(-) diff --git a/package.json b/package.json index 0b56f8c255e56..088c4705b123d 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "conventional-changelog-cli": "^2.1.1", "fs-extra": "^9.1.0", "graceful-fs": "^4.2.6", - "jest-junit": "^12.0.0", + "jest-junit": "^12.1.0", "jsii-diff": "^1.29.0", "jsii-pacmak": "^1.29.0", "jsii-reflect": "^1.29.0", diff --git a/packages/@aws-cdk/assets/package.json b/packages/@aws-cdk/assets/package.json index e962ce96ebf94..f41454de0bb98 100644 --- a/packages/@aws-cdk/assets/package.json +++ b/packages/@aws-cdk/assets/package.json @@ -75,7 +75,7 @@ "nodeunit": "^0.11.3", "pkglint": "0.0.0", "sinon": "^9.2.4", - "ts-mock-imports": "^1.3.4", + "ts-mock-imports": "^1.3.7", "@aws-cdk/assert-internal": "0.0.0" }, "dependencies": { diff --git a/packages/@aws-cdk/aws-apprunner/package.json b/packages/@aws-cdk/aws-apprunner/package.json index e45da28fd3637..94ed90273f3b8 100644 --- a/packages/@aws-cdk/aws-apprunner/package.json +++ b/packages/@aws-cdk/aws-apprunner/package.json @@ -75,7 +75,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/jest": "^26.0.22", + "@types/jest": "^26.0.23", "@aws-cdk/assert-internal": "0.0.0", "cdk-build-tools": "0.0.0", "cfn2ts": "0.0.0", 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 c8c69cdbd69b9..13c4f54cacc2e 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 @@ -32,9 +32,9 @@ "cdk-build-tools": "0.0.0", "aws-sdk": "^2.596.0", "aws-sdk-mock": "^5.1.0", - "eslint": "^7.26.0", + "eslint": "^7.27.0", "eslint-config-standard": "^14.1.1", - "eslint-plugin-import": "^2.23.2", + "eslint-plugin-import": "^2.23.3", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^4.3.1", "eslint-plugin-standard": "^4.1.0", diff --git a/packages/@aws-cdk/aws-codepipeline-actions/package.json b/packages/@aws-cdk/aws-codepipeline-actions/package.json index 281edb052d5a6..ee3cfaf444279 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/package.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/package.json @@ -69,7 +69,7 @@ "@types/jest": "^26.0.23", "@aws-cdk/aws-cloudtrail": "0.0.0", "@aws-cdk/cx-api": "0.0.0", - "@types/lodash": "^4.14.169", + "@types/lodash": "^4.14.170", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "lodash": "^4.17.21", diff --git a/packages/@aws-cdk/aws-dynamodb-global/lambda-packages/aws-global-table-coordinator/package.json b/packages/@aws-cdk/aws-dynamodb-global/lambda-packages/aws-global-table-coordinator/package.json index 0a9ee083461e6..cb343eb6c866b 100644 --- a/packages/@aws-cdk/aws-dynamodb-global/lambda-packages/aws-global-table-coordinator/package.json +++ b/packages/@aws-cdk/aws-dynamodb-global/lambda-packages/aws-global-table-coordinator/package.json @@ -29,9 +29,9 @@ "devDependencies": { "aws-sdk": "^2.596.0", "aws-sdk-mock": "^5.1.0", - "eslint": "^7.26.0", + "eslint": "^7.27.0", "eslint-config-standard": "^14.1.1", - "eslint-plugin-import": "^2.23.2", + "eslint-plugin-import": "^2.23.3", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^4.3.1", "eslint-plugin-standard": "^4.1.0", diff --git a/packages/@aws-cdk/aws-iotcoredeviceadvisor/package.json b/packages/@aws-cdk/aws-iotcoredeviceadvisor/package.json index e6e889086eac0..68850b3c5804b 100644 --- a/packages/@aws-cdk/aws-iotcoredeviceadvisor/package.json +++ b/packages/@aws-cdk/aws-iotcoredeviceadvisor/package.json @@ -75,7 +75,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/jest": "^26.0.22", + "@types/jest": "^26.0.23", "@aws-cdk/assert-internal": "0.0.0", "cdk-build-tools": "0.0.0", "cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-lambda-nodejs/package.json b/packages/@aws-cdk/aws-lambda-nodejs/package.json index 6f2d2e2049b5e..d3a1ee74f9380 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/package.json +++ b/packages/@aws-cdk/aws-lambda-nodejs/package.json @@ -67,7 +67,7 @@ "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "delay": "5.0.0", - "esbuild": "^0.12.1", + "esbuild": "^0.12.3", "pkglint": "0.0.0", "@aws-cdk/assert-internal": "0.0.0" }, diff --git a/packages/@aws-cdk/aws-lambda/package.json b/packages/@aws-cdk/aws-lambda/package.json index 57cd9274d617d..e6197ec716495 100644 --- a/packages/@aws-cdk/aws-lambda/package.json +++ b/packages/@aws-cdk/aws-lambda/package.json @@ -77,7 +77,7 @@ "devDependencies": { "@types/jest": "^26.0.23", "@types/aws-lambda": "^8.10.76", - "@types/lodash": "^4.14.169", + "@types/lodash": "^4.14.170", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-ssmcontacts/package.json b/packages/@aws-cdk/aws-ssmcontacts/package.json index b5e53987c4a89..c00a2e875368f 100644 --- a/packages/@aws-cdk/aws-ssmcontacts/package.json +++ b/packages/@aws-cdk/aws-ssmcontacts/package.json @@ -75,7 +75,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/jest": "^26.0.22", + "@types/jest": "^26.0.23", "@aws-cdk/assert-internal": "0.0.0", "cdk-build-tools": "0.0.0", "cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-ssmincidents/package.json b/packages/@aws-cdk/aws-ssmincidents/package.json index 9079c02e0b4d8..b0277e8e04d17 100644 --- a/packages/@aws-cdk/aws-ssmincidents/package.json +++ b/packages/@aws-cdk/aws-ssmincidents/package.json @@ -75,7 +75,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/jest": "^26.0.22", + "@types/jest": "^26.0.23", "@aws-cdk/assert-internal": "0.0.0", "cdk-build-tools": "0.0.0", "cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/core/package.json b/packages/@aws-cdk/core/package.json index eb730c5e5dc34..cf43b0a09c5aa 100644 --- a/packages/@aws-cdk/core/package.json +++ b/packages/@aws-cdk/core/package.json @@ -177,7 +177,7 @@ "@types/aws-lambda": "^8.10.76", "@types/fs-extra": "^8.1.1", "@types/jest": "^26.0.23", - "@types/lodash": "^4.14.169", + "@types/lodash": "^4.14.170", "@types/minimatch": "^3.0.4", "@types/node": "^10.17.60", "@types/sinon": "^9.0.11", @@ -188,7 +188,7 @@ "nodeunit-shim": "0.0.0", "pkglint": "0.0.0", "sinon": "^9.2.4", - "ts-mock-imports": "^1.3.4" + "ts-mock-imports": "^1.3.7" }, "dependencies": { "@aws-cdk/cloud-assembly-schema": "0.0.0", diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index 0ed2f35138803..aa6ec027a38ca 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -63,7 +63,7 @@ "pkglint": "0.0.0", "sinon": "^9.2.4", "ts-jest": "^26.5.6", - "ts-mock-imports": "^1.3.4", + "ts-mock-imports": "^1.3.7", "xml-js": "^1.6.11" }, "dependencies": { diff --git a/packages/awslint/package.json b/packages/awslint/package.json index 82bdd1818168c..3ddbccad9cdb5 100644 --- a/packages/awslint/package.json +++ b/packages/awslint/package.json @@ -29,13 +29,13 @@ "@types/yargs": "^15.0.13", "pkglint": "0.0.0", "typescript": "~3.9.9", - "@typescript-eslint/eslint-plugin": "^4.24.0", - "@typescript-eslint/parser": "^4.24.0", - "eslint": "^7.26.0", + "@typescript-eslint/eslint-plugin": "^4.25.0", + "@typescript-eslint/parser": "^4.25.0", + "eslint": "^7.27.0", "eslint-import-resolver-node": "^0.3.4", "eslint-import-resolver-typescript": "^2.4.0", "eslint-plugin-cdk": "0.0.0", - "eslint-plugin-import": "^2.23.2", + "eslint-plugin-import": "^2.23.3", "eslint-plugin-jest": "^24.3.6", "jest": "^26.6.3" }, diff --git a/tools/cdk-build-tools/package.json b/tools/cdk-build-tools/package.json index 2b1e2c4d8c653..a53f7158034d2 100644 --- a/tools/cdk-build-tools/package.json +++ b/tools/cdk-build-tools/package.json @@ -40,15 +40,15 @@ "pkglint": "0.0.0" }, "dependencies": { - "@typescript-eslint/eslint-plugin": "^4.24.0", - "@typescript-eslint/parser": "^4.24.0", + "@typescript-eslint/eslint-plugin": "^4.25.0", + "@typescript-eslint/parser": "^4.25.0", "awslint": "0.0.0", "colors": "^1.4.0", - "eslint": "^7.26.0", + "eslint": "^7.27.0", "eslint-import-resolver-node": "^0.3.4", "eslint-import-resolver-typescript": "^2.4.0", "eslint-plugin-cdk": "0.0.0", - "eslint-plugin-import": "^2.23.2", + "eslint-plugin-import": "^2.23.3", "eslint-plugin-jest": "^24.3.6", "fs-extra": "^9.1.0", "jest": "^26.6.3", diff --git a/tools/eslint-plugin-cdk/package.json b/tools/eslint-plugin-cdk/package.json index 7e3806cd7ee89..cc1037445d739 100644 --- a/tools/eslint-plugin-cdk/package.json +++ b/tools/eslint-plugin-cdk/package.json @@ -12,7 +12,7 @@ "build+test": "npm run build && npm test" }, "devDependencies": { - "@types/eslint": "^7.2.10", + "@types/eslint": "^7.2.11", "@types/fs-extra": "^8.1.1", "@types/jest": "^26.0.23", "@types/node": "^10.17.60", @@ -22,8 +22,8 @@ "typescript": "~3.9.9" }, "dependencies": { - "@typescript-eslint/parser": "^4.24.0", - "eslint": "^7.26.0", + "@typescript-eslint/parser": "^4.25.0", + "eslint": "^7.27.0", "fs-extra": "^9.1.0" }, "jest": { diff --git a/tools/pkglint/package.json b/tools/pkglint/package.json index 47796fe237303..e3f1e6c55b676 100644 --- a/tools/pkglint/package.json +++ b/tools/pkglint/package.json @@ -40,13 +40,13 @@ "@types/jest": "^26.0.23", "@types/semver": "^7.3.6", "@types/yargs": "^15.0.13", - "@typescript-eslint/eslint-plugin": "^4.24.0", - "@typescript-eslint/parser": "^4.24.0", - "eslint": "^7.26.0", + "@typescript-eslint/eslint-plugin": "^4.25.0", + "@typescript-eslint/parser": "^4.25.0", + "eslint": "^7.27.0", "eslint-import-resolver-node": "^0.3.4", "eslint-import-resolver-typescript": "^2.4.0", "eslint-plugin-cdk": "0.0.0", - "eslint-plugin-import": "^2.23.2", + "eslint-plugin-import": "^2.23.3", "eslint-plugin-jest": "^24.3.6", "jest": "^26.6.3", "typescript": "~3.9.9" diff --git a/tools/prlint/package.json b/tools/prlint/package.json index 059f214bd2f57..0ee54b249cf7a 100644 --- a/tools/prlint/package.json +++ b/tools/prlint/package.json @@ -11,8 +11,8 @@ }, "license": "Apache-2.0", "dependencies": { - "@actions/core": "^1.2.2", - "@actions/github": "^2.1.0", + "@actions/core": "^1.3.0", + "@actions/github": "^2.2.0", "github-api": "^3.4.0" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index c45f8e5dc4173..45a920fd274cf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,27 @@ # yarn lockfile v1 +"@actions/core@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.3.0.tgz#f5e4b24c889e7f2e58b466cc8c7481292284eba0" + integrity sha512-xxtX0Cwdhb8LcgatfJkokqT8KzPvcIbwL9xpLU09nOwBzaStbfm0dNncsP0M4us+EpoPdWy7vbzU5vSOH7K6pg== + +"@actions/github@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@actions/github/-/github-2.2.0.tgz#8952fe96b12b881fa39340f0e7202b04dc5c3e71" + integrity sha512-9UAZqn8ywdR70n3GwVle4N8ALosQs4z50N7XMXrSTUVOmVpaBC5kE3TRTT7qQdi3OaQV24mjGuJZsHUmhD+ZXw== + dependencies: + "@actions/http-client" "^1.0.3" + "@octokit/graphql" "^4.3.1" + "@octokit/rest" "^16.43.1" + +"@actions/http-client@^1.0.3": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@actions/http-client/-/http-client-1.0.11.tgz#c58b12e9aa8b159ee39e7dd6cbd0e91d905633c0" + integrity sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg== + dependencies: + tunnel "0.0.6" + "@babel/code-frame@7.12.11": version "7.12.11" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" @@ -1266,7 +1287,7 @@ node-gyp "^7.1.0" read-package-json-fast "^2.0.1" -"@octokit/auth-token@^2.4.4": +"@octokit/auth-token@^2.4.0", "@octokit/auth-token@^2.4.4": version "2.4.5" resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.5.tgz#568ccfb8cb46f36441fac094ce34f7a875b197f3" integrity sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA== @@ -1295,6 +1316,15 @@ is-plain-object "^5.0.0" universal-user-agent "^6.0.0" +"@octokit/graphql@^4.3.1": + version "4.6.2" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.6.2.tgz#ec44abdfa87f2b9233282136ae33e4ba446a04e7" + integrity sha512-WmsIR1OzOr/3IqfG9JIczI8gMJUMzzyx5j0XXQ4YihHtKlQc+u35VpVoOXhlKAlaBntvry1WpAzPl/a+s3n89Q== + dependencies: + "@octokit/request" "^5.3.0" + "@octokit/types" "^6.0.3" + universal-user-agent "^6.0.0" + "@octokit/graphql@^4.5.8": version "4.6.1" resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.6.1.tgz#f975486a46c94b7dbe58a0ca751935edc7e32cc9" @@ -1314,6 +1344,13 @@ resolved "https://registry.yarnpkg.com/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz#e07896739618dab8da7d4077c658003775f95437" integrity sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw== +"@octokit/plugin-paginate-rest@^1.1.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-1.1.2.tgz#004170acf8c2be535aba26727867d692f7b488fc" + integrity sha512-jbsSoi5Q1pj63sC16XIUboklNw+8tL9VOnJsWycWYR78TKss5PVpIPb1TUUcMQ+bBh7cY579cVAWmf5qG+dw+Q== + dependencies: + "@octokit/types" "^2.0.1" + "@octokit/plugin-paginate-rest@^2.6.2": version "2.13.3" resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.13.3.tgz#f0f1792230805108762d87906fb02d573b9e070a" @@ -1321,11 +1358,19 @@ dependencies: "@octokit/types" "^6.11.0" -"@octokit/plugin-request-log@^1.0.2": +"@octokit/plugin-request-log@^1.0.0", "@octokit/plugin-request-log@^1.0.2": version "1.0.3" resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.3.tgz#70a62be213e1edc04bb8897ee48c311482f9700d" integrity sha512-4RFU4li238jMJAzLgAwkBAw+4Loile5haQMQr+uhFq27BmyJXcXSKvoQKqh0agsZEiUlW6iSv3FAgvmGkur7OQ== +"@octokit/plugin-rest-endpoint-methods@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-2.4.0.tgz#3288ecf5481f68c494dd0602fc15407a59faf61e" + integrity sha512-EZi/AWhtkdfAYi01obpX0DF7U6b1VRr30QNQ5xSFPITMdLSfhcBqjamE3F+sKcxPbD7eZuMHu3Qkk2V+JGxBDQ== + dependencies: + "@octokit/types" "^2.0.1" + deprecation "^2.3.1" + "@octokit/plugin-rest-endpoint-methods@5.0.1": version "5.0.1" resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.0.1.tgz#631b8d4edc6798b03489911252a25f2a4e58c594" @@ -1334,6 +1379,15 @@ "@octokit/types" "^6.13.1" deprecation "^2.3.1" +"@octokit/request-error@^1.0.2": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-1.2.1.tgz#ede0714c773f32347576c25649dc013ae6b31801" + integrity sha512-+6yDyk1EES6WK+l3viRDElw96MvwfJxCt45GvmjDUKWjYIb3PJZQkq3i46TwGwoPD4h8NmTrENmtyA1FwbmhRA== + dependencies: + "@octokit/types" "^2.0.0" + deprecation "^2.0.0" + once "^1.4.0" + "@octokit/request-error@^2.0.0", "@octokit/request-error@^2.0.5": version "2.0.5" resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.0.5.tgz#72cc91edc870281ad583a42619256b380c600143" @@ -1343,7 +1397,7 @@ deprecation "^2.0.0" once "^1.4.0" -"@octokit/request@^5.3.0", "@octokit/request@^5.4.12": +"@octokit/request@^5.2.0", "@octokit/request@^5.3.0", "@octokit/request@^5.4.12": version "5.4.15" resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.15.tgz#829da413dc7dd3aa5e2cdbb1c7d0ebe1f146a128" integrity sha512-6UnZfZzLwNhdLRreOtTkT9n57ZwulCve8q3IT/Z477vThu6snfdkBuhxnChpOKNGxcQ71ow561Qoa6uqLdPtag== @@ -1355,6 +1409,28 @@ node-fetch "^2.6.1" universal-user-agent "^6.0.0" +"@octokit/rest@^16.43.1": + version "16.43.2" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-16.43.2.tgz#c53426f1e1d1044dee967023e3279c50993dd91b" + integrity sha512-ngDBevLbBTFfrHZeiS7SAMAZ6ssuVmXuya+F/7RaVvlysgGa1JKJkKWY+jV6TCJYcW0OALfJ7nTIGXcBXzycfQ== + dependencies: + "@octokit/auth-token" "^2.4.0" + "@octokit/plugin-paginate-rest" "^1.1.1" + "@octokit/plugin-request-log" "^1.0.0" + "@octokit/plugin-rest-endpoint-methods" "2.4.0" + "@octokit/request" "^5.2.0" + "@octokit/request-error" "^1.0.2" + atob-lite "^2.0.0" + before-after-hook "^2.0.0" + btoa-lite "^1.0.0" + deprecation "^2.0.0" + lodash.get "^4.4.2" + lodash.set "^4.3.2" + lodash.uniq "^4.5.0" + octokit-pagination-methods "^1.1.0" + once "^1.4.0" + universal-user-agent "^4.0.0" + "@octokit/rest@^18.1.0", "@octokit/rest@^18.5.3": version "18.5.3" resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.5.3.tgz#6a2e6006a87ebbc34079c419258dd29ec9ff659d" @@ -1365,6 +1441,13 @@ "@octokit/plugin-request-log" "^1.0.2" "@octokit/plugin-rest-endpoint-methods" "5.0.1" +"@octokit/types@^2.0.0", "@octokit/types@^2.0.1": + version "2.16.2" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-2.16.2.tgz#4c5f8da3c6fecf3da1811aef678fda03edac35d2" + integrity sha512-O75k56TYvJ8WpAakWwYRN8Bgu60KrmX0z1KqFp1kNiFNkgW+JW+9EBKZ+S33PU6SLvbihqd+3drvPxKK68Ee8Q== + dependencies: + "@types/node" ">= 8" + "@octokit/types@^6.0.3", "@octokit/types@^6.11.0", "@octokit/types@^6.13.1", "@octokit/types@^6.7.1": version "6.14.2" resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.14.2.tgz#64c9457f38fb8522bdbba3c8cc814590a2d61bf5" @@ -1450,10 +1533,10 @@ dependencies: "@babel/types" "^7.3.0" -"@types/eslint@^7.2.10": - version "7.2.10" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.10.tgz#4b7a9368d46c0f8cd5408c23288a59aa2394d917" - integrity sha512-kUEPnMKrqbtpCq/KTaGFFKAcz6Ethm2EjCoKIDaCmfRBWLbFuTcOJfTlorwbnboXBzahqWLgUp1BQeKHiJzPUQ== +"@types/eslint@^7.2.11": + version "7.2.11" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.11.tgz#180b58f5bb7d7376e39d22496e2b08901aa52fd2" + integrity sha512-WYhv//5K8kQtsSc9F1Kn2vHzhYor6KpwPbARH7hwYe3C3ETD0EVx/3P5qQybUoaBEuUa9f/02JjBiXFWalYUmw== dependencies: "@types/estree" "*" "@types/json-schema" "*" @@ -1529,10 +1612,10 @@ dependencies: jszip "*" -"@types/lodash@^4.14.169": - version "4.14.169" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.169.tgz#83c217688f07a4d9ef8f28a3ebd1d318f6ff4cbb" - integrity sha512-DvmZHoHTFJ8zhVYwCLWbQ7uAbYQEk52Ev2/ZiQ7Y7gQGeV9pjBqjnQpECMHfKS1rCYAhMI7LHVxwyZLZinJgdw== +"@types/lodash@^4.14.170": + version "4.14.170" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.170.tgz#0d67711d4bf7f4ca5147e9091b847479b87925d6" + integrity sha512-bpcvu/MKHHeYX+qeEN8GE7DIravODWdACVA1ctevD8CN24RhPZIKMn9ntfAsrvLfSX3cR5RrBKAbYm9bGs0A+Q== "@types/md5@^2.3.0": version "2.3.0" @@ -1568,6 +1651,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-15.0.2.tgz#51e9c0920d1b45936ea04341aa3e2e58d339fb67" integrity sha512-p68+a+KoxpoB47015IeYZYRrdqMUcpbK8re/zpFB8Ld46LHC1lPEbp3EXgkEhAYEcPvjJF6ZO+869SQ0aH1dcA== +"@types/node@>= 8": + version "15.6.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.6.1.tgz#32d43390d5c62c5b6ec486a9bc9c59544de39a08" + integrity sha512-7EIraBEyRHEe7CH+Fm1XvgqU6uwZN8Q7jppJGcqjROMT29qhAuuOxYB1uEY5UMYQKEmA5D+5tBnhdaPXSsLONA== + "@types/node@^10.17.60": version "10.17.60" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" @@ -1692,13 +1780,13 @@ resolved "https://registry.yarnpkg.com/@types/yarnpkg__lockfile/-/yarnpkg__lockfile-1.1.4.tgz#445251eb00bd9c1e751f82c7c6bf4f714edfd464" integrity sha512-/emrKCfQMQmFCqRqqBJ0JueHBT06jBRM3e8OgnvDUcvuExONujIk2hFA5dNsN9Nt41ljGVDdChvCydATZ+KOZw== -"@typescript-eslint/eslint-plugin@^4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.24.0.tgz#03801ffc25b2af9d08f3dc9bccfc0b7ce3780d0f" - integrity sha512-qbCgkPM7DWTsYQGjx9RTuQGswi+bEt0isqDBeo+CKV0953zqI0Tp7CZ7Fi9ipgFA6mcQqF4NOVNwS/f2r6xShw== +"@typescript-eslint/eslint-plugin@^4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.25.0.tgz#d82657b6ab4caa4c3f888ff923175fadc2f31f2a" + integrity sha512-Qfs3dWkTMKkKwt78xp2O/KZQB8MPS1UQ5D3YW2s6LQWBE1074BE+Rym+b1pXZIX3M3fSvPUDaCvZLKV2ylVYYQ== dependencies: - "@typescript-eslint/experimental-utils" "4.24.0" - "@typescript-eslint/scope-manager" "4.24.0" + "@typescript-eslint/experimental-utils" "4.25.0" + "@typescript-eslint/scope-manager" "4.25.0" debug "^4.1.1" functional-red-black-tree "^1.0.1" lodash "^4.17.15" @@ -1706,15 +1794,15 @@ semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.24.0.tgz#c23ead9de44b99c3a5fd925c33a106b00165e172" - integrity sha512-IwTT2VNDKH1h8RZseMH4CcYBz6lTvRoOLDuuqNZZoThvfHEhOiZPQCow+5El3PtyxJ1iDr6UXZwYtE3yZQjhcw== +"@typescript-eslint/experimental-utils@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.25.0.tgz#b2febcfa715d2c1806fd5f0335193a6cd270df54" + integrity sha512-f0doRE76vq7NEEU0tw+ajv6CrmPelw5wLoaghEHkA2dNLFb3T/zJQqGPQ0OYt5XlZaS13MtnN+GTPCuUVg338w== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/scope-manager" "4.24.0" - "@typescript-eslint/types" "4.24.0" - "@typescript-eslint/typescript-estree" "4.24.0" + "@typescript-eslint/scope-manager" "4.25.0" + "@typescript-eslint/types" "4.25.0" + "@typescript-eslint/typescript-estree" "4.25.0" eslint-scope "^5.0.0" eslint-utils "^2.0.0" @@ -1730,14 +1818,14 @@ eslint-scope "^5.0.0" eslint-utils "^2.0.0" -"@typescript-eslint/parser@^4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.24.0.tgz#2e5f1cc78ffefe43bfac7e5659309a92b09a51bd" - integrity sha512-dj1ZIh/4QKeECLb2f/QjRwMmDArcwc2WorWPRlB8UNTZlY1KpTVsbX7e3ZZdphfRw29aTFUSNuGB8w9X5sS97w== +"@typescript-eslint/parser@^4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.25.0.tgz#6b2cb6285aa3d55bfb263c650739091b0f19aceb" + integrity sha512-OZFa1SKyEJpAhDx8FcbWyX+vLwh7OEtzoo2iQaeWwxucyfbi0mT4DijbOSsTgPKzGHr6GrF2V5p/CEpUH/VBxg== dependencies: - "@typescript-eslint/scope-manager" "4.24.0" - "@typescript-eslint/types" "4.24.0" - "@typescript-eslint/typescript-estree" "4.24.0" + "@typescript-eslint/scope-manager" "4.25.0" + "@typescript-eslint/types" "4.25.0" + "@typescript-eslint/typescript-estree" "4.25.0" debug "^4.1.1" "@typescript-eslint/scope-manager@4.22.1": @@ -1748,23 +1836,23 @@ "@typescript-eslint/types" "4.22.1" "@typescript-eslint/visitor-keys" "4.22.1" -"@typescript-eslint/scope-manager@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.24.0.tgz#38088216f0eaf235fa30ed8cabf6948ec734f359" - integrity sha512-9+WYJGDnuC9VtYLqBhcSuM7du75fyCS/ypC8c5g7Sdw7pGL4NDTbeH38eJPfzIydCHZDoOgjloxSAA3+4l/zsA== +"@typescript-eslint/scope-manager@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.25.0.tgz#9d86a5bcc46ef40acd03d85ad4e908e5aab8d4ca" + integrity sha512-2NElKxMb/0rya+NJG1U71BuNnp1TBd1JgzYsldsdA83h/20Tvnf/HrwhiSlNmuq6Vqa0EzidsvkTArwoq+tH6w== dependencies: - "@typescript-eslint/types" "4.24.0" - "@typescript-eslint/visitor-keys" "4.24.0" + "@typescript-eslint/types" "4.25.0" + "@typescript-eslint/visitor-keys" "4.25.0" "@typescript-eslint/types@4.22.1": version "4.22.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.22.1.tgz#bf99c6cec0b4a23d53a61894816927f2adad856a" integrity sha512-2HTkbkdAeI3OOcWbqA8hWf/7z9c6gkmnWNGz0dKSLYLWywUlkOAQ2XcjhlKLj5xBFDf8FgAOF5aQbnLRvgNbCw== -"@typescript-eslint/types@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.24.0.tgz#6d0cca2048cbda4e265e0c4db9c2a62aaad8228c" - integrity sha512-tkZUBgDQKdvfs8L47LaqxojKDE+mIUmOzdz7r+u+U54l3GDkTpEbQ1Jp3cNqqAU9vMUCBA1fitsIhm7yN0vx9Q== +"@typescript-eslint/types@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.25.0.tgz#0e444a5c5e3c22d7ffa5e16e0e60510b3de5af87" + integrity sha512-+CNINNvl00OkW6wEsi32wU5MhHti2J25TJsJJqgQmJu3B3dYDBcmOxcE5w9cgoM13TrdE/5ND2HoEnBohasxRQ== "@typescript-eslint/typescript-estree@4.22.1": version "4.22.1" @@ -1779,13 +1867,13 @@ semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/typescript-estree@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.24.0.tgz#b49249679a98014d8b03e8d4b70864b950e3c90f" - integrity sha512-kBDitL/by/HK7g8CYLT7aKpAwlR8doshfWz8d71j97n5kUa5caHWvY0RvEUEanL/EqBJoANev8Xc/mQ6LLwXGA== +"@typescript-eslint/typescript-estree@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.25.0.tgz#942e4e25888736bff5b360d9b0b61e013d0cfa25" + integrity sha512-1B8U07TGNAFMxZbSpF6jqiDs1cVGO0izVkf18Q/SPcUAc9LhHxzvSowXDTvkHMWUVuPpagupaW63gB6ahTXVlg== dependencies: - "@typescript-eslint/types" "4.24.0" - "@typescript-eslint/visitor-keys" "4.24.0" + "@typescript-eslint/types" "4.25.0" + "@typescript-eslint/visitor-keys" "4.25.0" debug "^4.1.1" globby "^11.0.1" is-glob "^4.0.1" @@ -1800,12 +1888,12 @@ "@typescript-eslint/types" "4.22.1" eslint-visitor-keys "^2.0.0" -"@typescript-eslint/visitor-keys@4.24.0": - version "4.24.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.24.0.tgz#a8fafdc76cad4e04a681a945fbbac4e35e98e297" - integrity sha512-4ox1sjmGHIxjEDBnMCtWFFhErXtKA1Ec0sBpuz0fqf3P+g3JFGyTxxbF06byw0FRsPnnbq44cKivH7Ks1/0s6g== +"@typescript-eslint/visitor-keys@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.25.0.tgz#863e7ed23da4287c5b469b13223255d0fde6aaa7" + integrity sha512-AmkqV9dDJVKP/TcZrbf6s6i1zYXt5Hl8qOLrRDTFfRNae4+LB8A4N3i+FLZPW85zIxRy39BgeWOfMS3HoH5ngg== dependencies: - "@typescript-eslint/types" "4.24.0" + "@typescript-eslint/types" "4.25.0" eslint-visitor-keys "^2.0.0" "@yarnpkg/lockfile@^1.1.0": @@ -2177,6 +2265,11 @@ at-least-node@^1.0.0: resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== +atob-lite@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/atob-lite/-/atob-lite-2.0.0.tgz#0fef5ad46f1bd7a8502c65727f0367d5ee43d696" + integrity sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY= + atob@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" @@ -2321,7 +2414,7 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" -before-after-hook@^2.2.0: +before-after-hook@^2.0.0, before-after-hook@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.1.tgz#73540563558687586b52ed217dad6a802ab1549c" integrity sha512-/6FKxSTWoJdbsLDF8tdIjaRiFXiE6UHsEHE3OPI/cwPURCVi1ukP0gmLn7XWEiFk5TcwQjjY5PWsU+j+tgXgmw== @@ -2406,6 +2499,11 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" +btoa-lite@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/btoa-lite/-/btoa-lite-1.0.0.tgz#337766da15801210fdd956c22e9c6891ab9d0337" + integrity sha1-M3dm2hWAEhD92VbCLpxokaudAzc= + buffer-crc32@^0.2.1, buffer-crc32@^0.2.13: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" @@ -2900,14 +2998,6 @@ constructs@^3.3.69: resolved "https://registry.yarnpkg.com/constructs/-/constructs-3.3.75.tgz#222516951fd6b8380cb6fea3c171eeca0bf980a4" integrity sha512-q10foASSSfDWmS99OQLfnWDXCzqLvoORISAVWPFg0AmIGlBv2ZdDOtXxLqrJARPxVlOldmW2JzWzdRI+4+0/ZA== -contains-path@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-1.0.0.tgz#3458b332185603e8eed18f518d4a10888a3abc91" - integrity sha1-NFizMhhWA+ju0Y9RjUoQiIo6vJE= - dependencies: - normalize-path "^2.1.1" - path-starts-with "^1.0.0" - conventional-changelog-angular@^5.0.12: version "5.0.12" resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.12.tgz#c979b8b921cbfe26402eb3da5bbfda02d865a2b9" @@ -3705,10 +3795,10 @@ es6-error@^4.0.1: resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== -esbuild@^0.12.1: - version "0.12.1" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.12.1.tgz#f652d5b3b9432dbb42fc2c034ddd62360296e03d" - integrity sha512-WfQ00MKm/Y4ysz1u9PCUAsV66k5lbrcEvS6aG9jhBIavpB94FBdaWeBkaZXxCZB4w+oqh+j4ozJFWnnFprOXbg== +esbuild@^0.12.3: + version "0.12.3" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.12.3.tgz#9d978e8aa700034c3e3316e47df1c142ef290a72" + integrity sha512-0kuxNbGLGli8DYMHebJ4pk+RQ9ORYL1PmCSrSQpna1ioHvm+fzCAs99jwRYXGSfCNed1mj0vFSM9LbBm6YVbIQ== escalade@^3.1.1: version "3.1.1" @@ -3794,14 +3884,13 @@ eslint-plugin-es@^3.0.0: eslint-utils "^2.0.0" regexpp "^3.0.0" -eslint-plugin-import@^2.23.2: - version "2.23.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.23.2.tgz#ee15dd68fc7a1a1ba4c653c734e0d01c100d3484" - integrity sha512-LmNoRptHBxOP+nb0PIKz1y6OSzCJlB+0g0IGS3XV4KaKk2q4szqQ6s6F1utVf5ZRkxk/QOTjdxe7v4VjS99Bsg== +eslint-plugin-import@^2.23.3: + version "2.23.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.23.3.tgz#8a1b073289fff03c4af0f04b6df956b7d463e191" + integrity sha512-wDxdYbSB55F7T5CC7ucDjY641VvKmlRwT0Vxh7PkY1mI4rclVRFWYfsrjDgZvwYYDZ5ee0ZtfFKXowWjqvEoRQ== dependencies: array-includes "^3.1.3" array.prototype.flat "^1.2.4" - contains-path "^1.0.0" debug "^2.6.9" doctrine "^2.1.0" eslint-import-resolver-node "^0.3.4" @@ -3875,10 +3964,10 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== -eslint@^7.26.0: - version "7.26.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.26.0.tgz#d416fdcdcb3236cd8f282065312813f8c13982f6" - integrity sha512-4R1ieRf52/izcZE7AlLy56uIHHDLT74Yzz2Iv2l6kDaYvEu9x+wMB5dZArVL8SYGXSYV2YAg70FcW5Y5nGGNIg== +eslint@^7.27.0: + version "7.27.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.27.0.tgz#665a1506d8f95655c9274d84bd78f7166b07e9c7" + integrity sha512-JZuR6La2ZF0UD384lcbnd0Cgg6QJjiCwhMD6eU4h/VGPcVGwawNNzKU41tgokGXnfjOOyI6QIffthhJTPzzuRA== dependencies: "@babel/code-frame" "7.12.11" "@eslint/eslintrc" "^0.4.1" @@ -3888,12 +3977,14 @@ eslint@^7.26.0: debug "^4.0.1" doctrine "^3.0.0" enquirer "^2.3.5" + escape-string-regexp "^4.0.0" eslint-scope "^5.1.1" eslint-utils "^2.1.0" eslint-visitor-keys "^2.0.0" espree "^7.3.1" esquery "^1.4.0" esutils "^2.0.2" + fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" functional-red-black-tree "^1.0.1" glob-parent "^5.0.0" @@ -3905,7 +3996,7 @@ eslint@^7.26.0: js-yaml "^3.13.1" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" - lodash "^4.17.21" + lodash.merge "^4.6.2" minimatch "^3.0.4" natural-compare "^1.4.0" optionator "^0.9.1" @@ -3914,7 +4005,7 @@ eslint@^7.26.0: semver "^7.2.1" strip-ansi "^6.0.0" strip-json-comments "^3.1.0" - table "^6.0.4" + table "^6.0.9" text-table "^0.2.0" v8-compile-cache "^2.0.3" @@ -5647,10 +5738,10 @@ jest-junit@^11.1.0: uuid "^3.3.3" xml "^1.0.1" -jest-junit@^12.0.0: - version "12.0.0" - resolved "https://registry.yarnpkg.com/jest-junit/-/jest-junit-12.0.0.tgz#3ebd4a6a84b50c4ab18323a8f7d9cceb9d845df6" - integrity sha512-+8K35LlboWiPuCnXSyiid7rFdxNlpCWWM20WEYe6IZH6psfUWKZmSpSRQ5tk0C0cBeDsvsnIzcef5mYhyJsbug== +jest-junit@^12.1.0: + version "12.1.0" + resolved "https://registry.yarnpkg.com/jest-junit/-/jest-junit-12.1.0.tgz#f27173529e7f8f10eac37beb30f8b9bc97e8f3c3" + integrity sha512-Z45INyzEAqTkCHX/hGCPgVFfZP3hQVgI68CgoEwkCiBuxETsPsniq5yPd8oxbMMHtDCpUlxXzoq7jY35dcuLKw== dependencies: mkdirp "^1.0.4" strip-ansi "^5.2.0" @@ -6406,6 +6497,11 @@ lodash.isplainobject@^4.0.6: resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + lodash.set@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" @@ -6436,6 +6532,11 @@ lodash.union@^4.6.0: resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg= +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= + lodash@4.x, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" @@ -6487,6 +6588,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +macos-release@^2.2.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.4.1.tgz#64033d0ec6a5e6375155a74b1a1eba8e509820ac" + integrity sha512-H/QHeBIN1fIGJX517pvK8IEK53yQOW7YcEI55oYtgjDdoCQQz7eJS94qt5kNrscReEyuD/JcdFCm2XBEcGOITg== + make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" @@ -7366,6 +7472,11 @@ object.values@^1.1.3: es-abstract "^1.18.0-next.2" has "^1.0.3" +octokit-pagination-methods@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz#cf472edc9d551055f9ef73f6e42b4dbb4c80bea4" + integrity sha512-fZ4qZdQ2nxJvtcasX7Ghl+WlWS/d9IgnBIwFZXVNNZUmzpno91SX5bc5vuxiuKoCtK78XxGGNuSCrDC7xYB3OQ== + once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -7427,6 +7538,14 @@ os-homedir@^1.0.0, os-homedir@^1.0.1, os-homedir@^1.0.2: resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= +os-name@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/os-name/-/os-name-3.1.0.tgz#dec19d966296e1cd62d701a5a66ee1ddeae70801" + integrity sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg== + dependencies: + macos-release "^2.2.0" + windows-release "^3.1.0" + os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -7769,13 +7888,6 @@ path-parse@^1.0.6: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== -path-starts-with@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/path-starts-with/-/path-starts-with-1.0.0.tgz#b28243015e8b138de572682ac52da42e646ad84e" - integrity sha1-soJDAV6LE43lcmgqxS2kLmRq2E4= - dependencies: - normalize-path "^2.1.1" - path-to-regexp@^1.7.0: version "1.8.0" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" @@ -9160,7 +9272,7 @@ symbol-tree@^3.2.4: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== -table@*, table@^6.0.4: +table@*: version "6.7.0" resolved "https://registry.yarnpkg.com/table/-/table-6.7.0.tgz#26274751f0ee099c547f6cb91d3eff0d61d155b2" integrity sha512-SAM+5p6V99gYiiy2gT5ArdzgM1dLDed0nkrWmG6Fry/bUS/m9x83BwpJUOf1Qj/x2qJd+thL6IkIx7qPGRxqBw== @@ -9172,7 +9284,7 @@ table@*, table@^6.0.4: string-width "^4.2.0" strip-ansi "^6.0.0" -table@^6.7.1: +table@^6.0.9, table@^6.7.1: version "6.7.1" resolved "https://registry.yarnpkg.com/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2" integrity sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg== @@ -9514,10 +9626,10 @@ ts-jest@^26.5.6: semver "7.x" yargs-parser "20.x" -ts-mock-imports@^1.3.4: - version "1.3.4" - resolved "https://registry.yarnpkg.com/ts-mock-imports/-/ts-mock-imports-1.3.4.tgz#09f23f2ad24258fd2e6e2723b4ad6172429fc093" - integrity sha512-sfLou3sXExgkq/ia0XKfRLtnXwMBIqfpSoPRT5vQRt8fqSlDPy8PaPz5N6lfkebnb0qW2qM4cKOURYP1L/+yuA== +ts-mock-imports@^1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/ts-mock-imports/-/ts-mock-imports-1.3.7.tgz#8c3210a641f40fd5cadbd1c9c88574b51df59bde" + integrity sha512-zy4B3QSGaOhjaX9j0h9YKwM1oHG4Kd1KIUJBeXlXIQrFnATNLgh4+NyRcaAHsPeqwe3TWeRtHXkNXPxySEKk3w== ts-node@^8.0.2: version "8.10.2" @@ -9581,6 +9693,11 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" +tunnel@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" + integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== + tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" @@ -9737,6 +9854,13 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" +universal-user-agent@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-4.0.1.tgz#fd8d6cb773a679a709e967ef8288a31fcc03e557" + integrity sha512-LnST3ebHwVL2aNe4mejI9IQh2HfZ1RLo8Io2HugSif8ekzD1TlWpHpColOB/eh8JHMLkGH3Akqf040I+4ylNxg== + dependencies: + os-name "^3.1.0" + universal-user-agent@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" @@ -9989,6 +10113,13 @@ wide-align@^1.1.0: dependencies: string-width "^1.0.2 || 2" +windows-release@^3.1.0: + version "3.3.3" + resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-3.3.3.tgz#1c10027c7225743eec6b89df160d64c2e0293999" + integrity sha512-OSOGH1QYiW5yVor9TtmXKQvt2vjQqbYS+DqmsZw+r7xDwLXEeT3JGW0ZppFmHx4diyXmxt238KFR3N9jzevBRg== + dependencies: + execa "^1.0.0" + word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" From 32b0b784d0b8c643c47a4768da57ad7af15cc55d Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Tue, 25 May 2021 16:11:21 -0700 Subject: [PATCH 096/134] revert(secretsmanager): revert "Automatically grant permissions to rotation Lambda (#14471)" This reverts commit 85e00faf --- .../@aws-cdk/aws-secretsmanager/README.md | 2 - .../lib/rotation-schedule.ts | 30 -------- .../test/rotation-schedule.test.ts | 69 ------------------- 3 files changed, 101 deletions(-) diff --git a/packages/@aws-cdk/aws-secretsmanager/README.md b/packages/@aws-cdk/aws-secretsmanager/README.md index 81c8e3c5e8e24..0c0f45828e9ef 100644 --- a/packages/@aws-cdk/aws-secretsmanager/README.md +++ b/packages/@aws-cdk/aws-secretsmanager/README.md @@ -87,8 +87,6 @@ secret.addRotationSchedule('RotationSchedule', { }); ``` -Note: The required permissions for Lambda to call SecretsManager and the other way round are automatically granted based on [AWS Documentation](https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-required-permissions.html) as long as the Lambda is not imported. - See [Overview of the Lambda Rotation Function](https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html) on how to implement a Lambda Rotation Function. ### Using a Hosted Lambda Function diff --git a/packages/@aws-cdk/aws-secretsmanager/lib/rotation-schedule.ts b/packages/@aws-cdk/aws-secretsmanager/lib/rotation-schedule.ts index 7e00492f2cb2f..1243976963386 100644 --- a/packages/@aws-cdk/aws-secretsmanager/lib/rotation-schedule.ts +++ b/packages/@aws-cdk/aws-secretsmanager/lib/rotation-schedule.ts @@ -1,5 +1,4 @@ import * as ec2 from '@aws-cdk/aws-ec2'; -import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; import { Duration, Resource, Stack } from '@aws-cdk/core'; import { Construct } from 'constructs'; @@ -71,35 +70,6 @@ export class RotationSchedule extends Resource { throw new Error('One of `rotationLambda` or `hostedRotation` must be specified.'); } - if (props.rotationLambda?.permissionsNode.defaultChild) { - props.rotationLambda.grantInvoke(new iam.ServicePrincipal('secretsmanager.amazonaws.com')); - - props.rotationLambda.addToRolePolicy( - new iam.PolicyStatement({ - actions: [ - 'secretsmanager:DescribeSecret', - 'secretsmanager:GetSecretValue', - 'secretsmanager:PutSecretValue', - 'secretsmanager:UpdateSecretVersionStage', - ], - resources: [props.secret.secretArn], - conditions: { - StringEquals: { - 'secretsmanager:resource/AllowRotationLambdaArn': props.rotationLambda.functionArn, - }, - }, - }), - ); - props.rotationLambda.addToRolePolicy( - new iam.PolicyStatement({ - actions: [ - 'secretsmanager:GetRandomPassword', - ], - resources: ['*'], - }), - ); - } - new CfnRotationSchedule(this, 'Resource', { secretId: props.secret.secretArn, rotationLambdaArn: props.rotationLambda?.functionArn, diff --git a/packages/@aws-cdk/aws-secretsmanager/test/rotation-schedule.test.ts b/packages/@aws-cdk/aws-secretsmanager/test/rotation-schedule.test.ts index 3ab3422cd265b..e77336732d51b 100644 --- a/packages/@aws-cdk/aws-secretsmanager/test/rotation-schedule.test.ts +++ b/packages/@aws-cdk/aws-secretsmanager/test/rotation-schedule.test.ts @@ -41,75 +41,6 @@ test('create a rotation schedule with a rotation Lambda', () => { }); }); -test('assign permissions for rotation schedule with a rotation Lambda', () => { - // GIVEN - const secret = new secretsmanager.Secret(stack, 'Secret'); - const rotationLambda = new lambda.Function(stack, 'Lambda', { - runtime: lambda.Runtime.NODEJS_10_X, - code: lambda.Code.fromInline('export.handler = event => event;'), - handler: 'index.handler', - }); - - // WHEN - new secretsmanager.RotationSchedule(stack, 'RotationSchedule', { - secret, - rotationLambda, - }); - - // THEN - expect(stack).toHaveResource('AWS::Lambda::Permission', { - Action: 'lambda:InvokeFunction', - FunctionName: { - 'Fn::GetAtt': [ - 'LambdaD247545B', - 'Arn', - ], - }, - Principal: 'secretsmanager.amazonaws.com', - }); - - expect(stack).toHaveResource('AWS::IAM::Policy', { - PolicyDocument: { - Statement: [ - { - Action: [ - 'secretsmanager:DescribeSecret', - 'secretsmanager:GetSecretValue', - 'secretsmanager:PutSecretValue', - 'secretsmanager:UpdateSecretVersionStage', - ], - Effect: 'Allow', - Resource: { - Ref: 'SecretA720EF05', - }, - Condition: { - StringEquals: { - 'secretsmanager:resource/AllowRotationLambdaArn': { - 'Fn::GetAtt': [ - 'LambdaD247545B', - 'Arn', - ], - }, - }, - }, - }, - { - Action: 'secretsmanager:GetRandomPassword', - Effect: 'Allow', - Resource: '*', - }, - ], - Version: '2012-10-17', - }, - PolicyName: 'LambdaServiceRoleDefaultPolicyDAE46E21', - Roles: [ - { - Ref: 'LambdaServiceRoleA8ED4D3B', - }, - ], - }); -}); - describe('hosted rotation', () => { test('single user not in a vpc', () => { // GIVEN From a94af94ec6865593cb02a09b128ca0f2bc8726ab Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Wed, 26 May 2021 11:15:26 -0700 Subject: [PATCH 097/134] chore(release): 1.106.1 --- CHANGELOG.md | 8 ++++++++ version.v1.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7d68b10fd6ca..ffd56de3683ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [1.106.1](https://github.com/aws/aws-cdk/compare/v1.106.0...v1.106.1) (2021-05-26) + + +### Bug Fixes + +* **secretsmanager**: revert "Automatically grant permissions to rotation Lambda ([#14471](https://github.com/aws/aws-cdk/issues/14471))", + fixes [#14868](https://github.com/aws/aws-cdk/issues/14868) + ## [1.106.0](https://github.com/aws/aws-cdk/compare/v1.105.0...v1.106.0) (2021-05-25) diff --git a/version.v1.json b/version.v1.json index 554280be5c164..0a4e31e665886 100644 --- a/version.v1.json +++ b/version.v1.json @@ -1,3 +1,3 @@ { - "version": "1.106.0" + "version": "1.106.1" } From f8813bf3ecfcebea7a5fed4317e9cf88e210db99 Mon Sep 17 00:00:00 2001 From: Andre Noberto Date: Wed, 26 May 2021 15:39:16 -0700 Subject: [PATCH 098/134] docs(iam): fixed typo in documentation (#14884) Fixed a small typo in aws-iam docs. ---- *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-iam/lib/policy-statement.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-iam/lib/policy-statement.ts b/packages/@aws-cdk/aws-iam/lib/policy-statement.ts index 78a588760c9d6..8777e40d5d7f7 100644 --- a/packages/@aws-cdk/aws-iam/lib/policy-statement.ts +++ b/packages/@aws-cdk/aws-iam/lib/policy-statement.ts @@ -276,7 +276,7 @@ export class PolicyStatement { } /** - * Indicates if this permission as at least one resource associated with it. + * Indicates if this permission has at least one resource associated with it. */ public get hasResource() { return this.resource && this.resource.length > 0; From 061fd558a3327b805bb5fe0abc72de7c21bbbdb9 Mon Sep 17 00:00:00 2001 From: Seiya6329 Date: Thu, 27 May 2021 13:43:03 -0700 Subject: [PATCH 099/134] fix(appmesh): TLS mode is set on the Certificate class (#14856) This is the second part of the series for [issue#12733](https://github.com/aws/aws-cdk/issues/12733). For the breakdown on series, please refer to [this comment](https://github.com/aws/aws-cdk/pull/14782#discussion_r636334863). #### Collaborators @alexbrjo and @dfezzie. Thank you! #### Design notes - Adding `TlsListener` interface above `TlsCertificate` in order to use `TlsCertificate` for `TlsClientPolicy` ([example](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appmesh-virtualnode-clientpolicytls.html)) - Updating TLS certificate abstraction by removing `tlsMode` property since `tlsMode` is part `TlsListener` property. In addition, this update is also required because `TlsClientPolicy` does NOT have `tlsMode` property. BREAKING CHANGE: the creation property `tlsCertificate` in `VirtualNode` has been renamed to `tls`, and its type changed to `TlsListener` - **appmesh**: the creation property `tlsCertificate` in `VirtualGatewayListener` has been renamed to `tls`, and its type changed to `TlsListener` - **appmesh**: the `tlsMode` property has been removed from the options when creating a `TlsCertificate`, moved to the new `TlsListener` interface, and renamed `mode` ---- *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-appmesh/README.md | 26 +++++---- packages/@aws-cdk/aws-appmesh/lib/index.ts | 1 + .../aws-appmesh/lib/tls-certificate.ts | 55 +------------------ .../@aws-cdk/aws-appmesh/lib/tls-listener.ts | 36 ++++++++++++ .../lib/virtual-gateway-listener.ts | 28 +++++----- .../aws-appmesh/lib/virtual-node-listener.ts | 29 +++++----- .../@aws-cdk/aws-appmesh/test/integ.mesh.ts | 12 ++-- .../aws-appmesh/test/test.virtual-gateway.ts | 34 +++++++----- .../aws-appmesh/test/test.virtual-node.ts | 47 +++++++++------- 9 files changed, 136 insertions(+), 132 deletions(-) create mode 100644 packages/@aws-cdk/aws-appmesh/lib/tls-listener.ts diff --git a/packages/@aws-cdk/aws-appmesh/README.md b/packages/@aws-cdk/aws-appmesh/README.md index 839a982c93f31..aa7d9f22318aa 100644 --- a/packages/@aws-cdk/aws-appmesh/README.md +++ b/packages/@aws-cdk/aws-appmesh/README.md @@ -248,13 +248,15 @@ const cert = new certificatemanager.Certificate(this, 'cert', {...}); const node = new appmesh.VirtualNode(stack, 'node', { mesh, - dnsHostName: 'node', + serviceDiscovery: appmesh.ServiceDiscovery.dns('node'), listeners: [appmesh.VirtualNodeListener.grpc({ port: 80, - tlsCertificate: appmesh.TlsCertificate.acm({ - certificate: cert, - tlsMode: TlsMode.STRICT, - }), + tls: { + mode: appmesh.TlsMode.STRICT, + certificate: appmesh.TlsCertificate.acm({ + certificate: cert, + }), + }, })], }); @@ -263,11 +265,13 @@ const gateway = new appmesh.VirtualGateway(this, 'gateway', { mesh: mesh, listeners: [appmesh.VirtualGatewayListener.grpc({ port: 8080, - tlsCertificate: appmesh.TlsCertificate.file({ - certificateChain: 'path/to/certChain', - privateKey: 'path/to/privateKey', - tlsMode: TlsMode.STRICT, - }), + tls: { + mode: appmesh.TlsMode.STRICT, + certificate: appmesh.TlsCertificate.file({ + certificateChainPath: 'path/to/certChain', + privateKeyPath: 'path/to/privateKey', + }), + }, })], virtualGatewayName: 'gateway', }); @@ -309,7 +313,7 @@ connection pool properties per listener protocol types. // A Virtual Node with a gRPC listener with a connection pool set const node = new appmesh.VirtualNode(stack, 'node', { mesh, - dnsHostName: 'node', + serviceDiscovery: appmesh.ServiceDiscovery.dns('node'), listeners: [appmesh.VirtualNodeListener.http({ port: 80, connectionPool: { diff --git a/packages/@aws-cdk/aws-appmesh/lib/index.ts b/packages/@aws-cdk/aws-appmesh/lib/index.ts index 4365a00da1279..2fe71eed2cd62 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/index.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/index.ts @@ -17,3 +17,4 @@ export * from './gateway-route'; export * from './gateway-route-spec'; export * from './client-policy'; export * from './health-checks'; +export * from './tls-listener'; diff --git a/packages/@aws-cdk/aws-appmesh/lib/tls-certificate.ts b/packages/@aws-cdk/aws-appmesh/lib/tls-certificate.ts index ec227fb13df99..fc3dbb660c8e3 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/tls-certificate.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/tls-certificate.ts @@ -5,50 +5,20 @@ import { CfnVirtualNode } from './appmesh.generated'; // eslint-disable-next-line no-duplicate-imports, import/order import { Construct } from '@aws-cdk/core'; -/** - * Enum of supported TLS modes - */ -export enum TlsMode { - /** - * Only accept encrypted traffic - */ - STRICT = 'STRICT', - - /** - * Accept encrypted and plaintext traffic. - */ - PERMISSIVE = 'PERMISSIVE', - - /** - * TLS is disabled, only accept plaintext traffic. - */ - DISABLED = 'DISABLED', -} - /** * A wrapper for the tls config returned by {@link TlsCertificate.bind} */ export interface TlsCertificateConfig { /** - * The CFN shape for a listener TLS certificate + * The CFN shape for a TLS certificate */ readonly tlsCertificate: CfnVirtualNode.ListenerTlsCertificateProperty, - - /** - * The TLS mode. - */ - readonly tlsMode: TlsMode; } /** * ACM Certificate Properties */ export interface AcmCertificateOptions { - /** - * The TLS mode. - */ - readonly tlsMode: TlsMode; - /** * The ACM certificate */ @@ -59,11 +29,6 @@ export interface AcmCertificateOptions { * File Certificate Properties */ export interface FileCertificateOptions { - /** - * The TLS mode. - */ - readonly tlsMode: TlsMode; - /** * The file path of the certificate chain file. */ @@ -104,13 +69,6 @@ export abstract class TlsCertificate { * Represents a ACM provided TLS certificate */ class AcmTlsCertificate extends TlsCertificate { - /** - * The TLS mode. - * - * @default - TlsMode.DISABLED - */ - readonly tlsMode: TlsMode; - /** * The ARN of the ACM certificate */ @@ -118,7 +76,6 @@ class AcmTlsCertificate extends TlsCertificate { constructor(props: AcmCertificateOptions) { super(); - this.tlsMode = props.tlsMode; this.acmCertificate = props.certificate; } @@ -129,7 +86,6 @@ class AcmTlsCertificate extends TlsCertificate { certificateArn: this.acmCertificate.certificateArn, }, }, - tlsMode: this.tlsMode, }; } } @@ -138,13 +94,6 @@ class AcmTlsCertificate extends TlsCertificate { * Represents a file provided TLS certificate */ class FileTlsCertificate extends TlsCertificate { - /** - * The TLS mode. - * - * @default - TlsMode.DISABLED - */ - readonly tlsMode: TlsMode; - /** * The file path of the certificate chain file. */ @@ -157,7 +106,6 @@ class FileTlsCertificate extends TlsCertificate { constructor(props: FileCertificateOptions) { super(); - this.tlsMode = props.tlsMode; this.certificateChain = props.certificateChainPath; this.privateKey = props.privateKeyPath; } @@ -170,7 +118,6 @@ class FileTlsCertificate extends TlsCertificate { privateKey: this.privateKey, }, }, - tlsMode: this.tlsMode, }; } } diff --git a/packages/@aws-cdk/aws-appmesh/lib/tls-listener.ts b/packages/@aws-cdk/aws-appmesh/lib/tls-listener.ts new file mode 100644 index 0000000000000..f5574e1fdf142 --- /dev/null +++ b/packages/@aws-cdk/aws-appmesh/lib/tls-listener.ts @@ -0,0 +1,36 @@ +import { TlsCertificate } from './tls-certificate'; + +/** + * Enum of supported TLS modes + */ +export enum TlsMode { + /** + * Only accept encrypted traffic + */ + STRICT = 'STRICT', + + /** + * Accept encrypted and plaintext traffic. + */ + PERMISSIVE = 'PERMISSIVE', + + /** + * TLS is disabled, only accept plaintext traffic. + */ + DISABLED = 'DISABLED', +} + +/** + * Represents TLS properties for listener + */ +export interface TlsListener { + /** + * Represents TLS certificate + */ + readonly certificate: TlsCertificate; + + /** + * The TLS mode. + */ + readonly mode: TlsMode; +} diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway-listener.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway-listener.ts index d4b235a8dc4ed..152200229af88 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway-listener.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway-listener.ts @@ -7,7 +7,7 @@ import { HttpConnectionPool, Protocol, } from './shared-interfaces'; -import { TlsCertificate, TlsCertificateConfig } from './tls-certificate'; +import { TlsListener } from './tls-listener'; // keep this import separate from other imports to reduce chance for merge conflicts with v2-main // eslint-disable-next-line no-duplicate-imports, import/order @@ -36,7 +36,7 @@ interface VirtualGatewayListenerCommonOptions { * * @default - none */ - readonly tlsCertificate?: TlsCertificate; + readonly tls?: TlsListener; } /** @@ -93,21 +93,21 @@ export abstract class VirtualGatewayListener { * Returns an HTTP Listener for a VirtualGateway */ public static http(options: HttpGatewayListenerOptions = {}): VirtualGatewayListener { - return new VirtualGatewayListenerImpl(Protocol.HTTP, options.healthCheck, options.port, options.tlsCertificate, options.connectionPool); + return new VirtualGatewayListenerImpl(Protocol.HTTP, options.healthCheck, options.port, options.tls, options.connectionPool); } /** * Returns an HTTP2 Listener for a VirtualGateway */ public static http2(options: Http2GatewayListenerOptions = {}): VirtualGatewayListener { - return new VirtualGatewayListenerImpl(Protocol.HTTP2, options.healthCheck, options.port, options.tlsCertificate, options.connectionPool); + return new VirtualGatewayListenerImpl(Protocol.HTTP2, options.healthCheck, options.port, options.tls, options.connectionPool); } /** * Returns a GRPC Listener for a VirtualGateway */ public static grpc(options: GrpcGatewayListenerOptions = {}): VirtualGatewayListener { - return new VirtualGatewayListenerImpl(Protocol.GRPC, options.healthCheck, options.port, options.tlsCertificate, options.connectionPool); + return new VirtualGatewayListenerImpl(Protocol.GRPC, options.healthCheck, options.port, options.tls, options.connectionPool); } /** @@ -125,7 +125,7 @@ class VirtualGatewayListenerImpl extends VirtualGatewayListener { constructor(private readonly protocol: Protocol, private readonly healthCheck: HealthCheck | undefined, private readonly port: number = 8080, - private readonly tlsCertificate: TlsCertificate | undefined, + private readonly tls: TlsListener | undefined, private readonly connectionPool: ConnectionPoolConfig | undefined) { super(); } @@ -135,7 +135,6 @@ class VirtualGatewayListenerImpl extends VirtualGatewayListener { * mutual exclusivity */ public bind(scope: Construct): VirtualGatewayListenerConfig { - const tlsConfig = this.tlsCertificate?.bind(scope); return { listener: { portMapping: { @@ -143,22 +142,23 @@ class VirtualGatewayListenerImpl extends VirtualGatewayListener { protocol: this.protocol, }, healthCheck: this.healthCheck?.bind(scope, { defaultPort: this.port }).virtualGatewayHealthCheck, - tls: tlsConfig ? renderTls(tlsConfig) : undefined, + tls: renderTls(scope, this.tls), connectionPool: this.connectionPool ? renderConnectionPool(this.connectionPool, this.protocol) : undefined, }, }; } - } /** * Renders the TLS config for a listener */ -function renderTls(tlsCertificateConfig: TlsCertificateConfig): CfnVirtualGateway.VirtualGatewayListenerTlsProperty { - return { - certificate: tlsCertificateConfig.tlsCertificate, - mode: tlsCertificateConfig.tlsMode.toString(), - }; +function renderTls(scope: Construct, tls: TlsListener | undefined): CfnVirtualGateway.VirtualGatewayListenerTlsProperty | undefined { + return tls + ? { + certificate: tls.certificate.bind(scope).tlsCertificate, + mode: tls.mode, + } + : undefined; } function renderConnectionPool(connectionPool: ConnectionPoolConfig, listenerProtocol: Protocol): diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-node-listener.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-node-listener.ts index 6613be6ed2949..e1abecc1a3b50 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-node-listener.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-node-listener.ts @@ -5,7 +5,7 @@ import { GrpcConnectionPool, GrpcTimeout, Http2ConnectionPool, HttpConnectionPool, HttpTimeout, OutlierDetection, Protocol, TcpConnectionPool, TcpTimeout, } from './shared-interfaces'; -import { TlsCertificate, TlsCertificateConfig } from './tls-certificate'; +import { TlsListener } from './tls-listener'; // keep this import separate from other imports to reduce chance for merge conflicts with v2-main // eslint-disable-next-line no-duplicate-imports, import/order @@ -44,7 +44,7 @@ interface VirtualNodeListenerCommonOptions { * * @default - none */ - readonly tlsCertificate?: TlsCertificate; + readonly tls?: TlsListener; /** * Represents the configuration for enabling outlier detection @@ -134,7 +134,7 @@ export abstract class VirtualNodeListener { * Returns an HTTP Listener for a VirtualNode */ public static http(props: HttpVirtualNodeListenerOptions = {}): VirtualNodeListener { - return new VirtualNodeListenerImpl(Protocol.HTTP, props.healthCheck, props.timeout, props.port, props.tlsCertificate, props.outlierDetection, + return new VirtualNodeListenerImpl(Protocol.HTTP, props.healthCheck, props.timeout, props.port, props.tls, props.outlierDetection, props.connectionPool); } @@ -142,7 +142,7 @@ export abstract class VirtualNodeListener { * Returns an HTTP2 Listener for a VirtualNode */ public static http2(props: Http2VirtualNodeListenerOptions = {}): VirtualNodeListener { - return new VirtualNodeListenerImpl(Protocol.HTTP2, props.healthCheck, props.timeout, props.port, props.tlsCertificate, props.outlierDetection, + return new VirtualNodeListenerImpl(Protocol.HTTP2, props.healthCheck, props.timeout, props.port, props.tls, props.outlierDetection, props.connectionPool); } @@ -150,7 +150,7 @@ export abstract class VirtualNodeListener { * Returns an GRPC Listener for a VirtualNode */ public static grpc(props: GrpcVirtualNodeListenerOptions = {}): VirtualNodeListener { - return new VirtualNodeListenerImpl(Protocol.GRPC, props.healthCheck, props.timeout, props.port, props.tlsCertificate, props.outlierDetection, + return new VirtualNodeListenerImpl(Protocol.GRPC, props.healthCheck, props.timeout, props.port, props.tls, props.outlierDetection, props.connectionPool); } @@ -158,7 +158,7 @@ export abstract class VirtualNodeListener { * Returns an TCP Listener for a VirtualNode */ public static tcp(props: TcpVirtualNodeListenerOptions = {}): VirtualNodeListener { - return new VirtualNodeListenerImpl(Protocol.TCP, props.healthCheck, props.timeout, props.port, props.tlsCertificate, props.outlierDetection, + return new VirtualNodeListenerImpl(Protocol.TCP, props.healthCheck, props.timeout, props.port, props.tls, props.outlierDetection, props.connectionPool); } @@ -173,12 +173,11 @@ class VirtualNodeListenerImpl extends VirtualNodeListener { private readonly healthCheck: HealthCheck | undefined, private readonly timeout: HttpTimeout | undefined, private readonly port: number = 8080, - private readonly tlsCertificate: TlsCertificate | undefined, + private readonly tls: TlsListener | undefined, private readonly outlierDetection: OutlierDetection | undefined, private readonly connectionPool: ConnectionPoolConfig | undefined) { super(); } public bind(scope: Construct): VirtualNodeListenerConfig { - const tlsConfig = this.tlsCertificate?.bind(scope); return { listener: { portMapping: { @@ -187,7 +186,7 @@ class VirtualNodeListenerImpl extends VirtualNodeListener { }, healthCheck: this.healthCheck?.bind(scope, { defaultPort: this.port }).virtualNodeHealthCheck, timeout: this.timeout ? this.renderTimeout(this.timeout) : undefined, - tls: tlsConfig ? this.renderTls(tlsConfig) : undefined, + tls: this.renderTls(scope, this.tls), outlierDetection: this.outlierDetection ? this.renderOutlierDetection(this.outlierDetection) : undefined, connectionPool: this.connectionPool ? this.renderConnectionPool(this.connectionPool) : undefined, }, @@ -197,11 +196,13 @@ class VirtualNodeListenerImpl extends VirtualNodeListener { /** * Renders the TLS config for a listener */ - private renderTls(tlsCertificateConfig: TlsCertificateConfig): CfnVirtualNode.ListenerTlsProperty { - return { - certificate: tlsCertificateConfig.tlsCertificate, - mode: tlsCertificateConfig.tlsMode.toString(), - }; + private renderTls(scope: Construct, tls: TlsListener | undefined): CfnVirtualNode.ListenerTlsProperty | undefined { + return tls + ? { + certificate: tls.certificate.bind(scope).tlsCertificate, + mode: tls.mode, + } + : undefined; } private renderTimeout(timeout: HttpTimeout): CfnVirtualNode.ListenerTimeoutProperty { diff --git a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts index 3e1b18a0073b1..f53174b15b449 100644 --- a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts +++ b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts @@ -207,11 +207,13 @@ new appmesh.VirtualGateway(stack, 'gateway2', { healthCheck: appmesh.HealthCheck.http({ interval: cdk.Duration.seconds(10), }), - tlsCertificate: appmesh.TlsCertificate.file({ - certificateChainPath: 'path/to/certChain', - privateKeyPath: 'path/to/privateKey', - tlsMode: appmesh.TlsMode.STRICT, - }), + tls: { + mode: appmesh.TlsMode.STRICT, + certificate: appmesh.TlsCertificate.file({ + certificateChainPath: 'path/to/certChain', + privateKeyPath: 'path/to/privateKey', + }), + }, })], }); diff --git a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts index 6f9d92b467911..35e441e54d622 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts @@ -170,10 +170,12 @@ export = { mesh: mesh, listeners: [appmesh.VirtualGatewayListener.http({ port: 8080, - tlsCertificate: appmesh.TlsCertificate.acm({ - tlsMode: appmesh.TlsMode.STRICT, - certificate: cert, - }), + tls: { + mode: appmesh.TlsMode.STRICT, + certificate: appmesh.TlsCertificate.acm({ + certificate: cert, + }), + }, })], }); @@ -214,11 +216,13 @@ export = { mesh: mesh, listeners: [appmesh.VirtualGatewayListener.grpc({ port: 8080, - tlsCertificate: appmesh.TlsCertificate.file({ - certificateChainPath: 'path/to/certChain', - privateKeyPath: 'path/to/privateKey', - tlsMode: appmesh.TlsMode.STRICT, - }), + tls: { + mode: appmesh.TlsMode.STRICT, + certificate: appmesh.TlsCertificate.file({ + certificateChainPath: 'path/to/certChain', + privateKeyPath: 'path/to/privateKey', + }), + }, })], }); @@ -258,11 +262,13 @@ export = { mesh: mesh, listeners: [appmesh.VirtualGatewayListener.grpc({ port: 8080, - tlsCertificate: appmesh.TlsCertificate.file({ - certificateChainPath: 'path/to/certChain', - privateKeyPath: 'path/to/privateKey', - tlsMode: appmesh.TlsMode.PERMISSIVE, - }), + tls: { + mode: appmesh.TlsMode.PERMISSIVE, + certificate: appmesh.TlsCertificate.file({ + certificateChainPath: 'path/to/certChain', + privateKeyPath: 'path/to/privateKey', + }), + }, })], }); diff --git a/packages/@aws-cdk/aws-appmesh/test/test.virtual-node.ts b/packages/@aws-cdk/aws-appmesh/test/test.virtual-node.ts index e4956078679c3..870af04737384 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.virtual-node.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.virtual-node.ts @@ -438,16 +438,17 @@ export = { mesh, listeners: [appmesh.VirtualNodeListener.grpc({ port: 80, - tlsCertificate: appmesh.TlsCertificate.acm({ - certificate: cert, - tlsMode: appmesh.TlsMode.STRICT, - }), + tls: { + mode: appmesh.TlsMode.STRICT, + certificate: appmesh.TlsCertificate.acm({ + certificate: cert, + }), + }, }, )], }); // THEN - expect(stack).to(haveResourceLike('AWS::AppMesh::VirtualNode', { Spec: { Listeners: [ @@ -485,11 +486,13 @@ export = { mesh, listeners: [appmesh.VirtualNodeListener.http({ port: 80, - tlsCertificate: appmesh.TlsCertificate.file({ - certificateChainPath: 'path/to/certChain', - privateKeyPath: 'path/to/privateKey', - tlsMode: appmesh.TlsMode.STRICT, - }), + tls: { + mode: appmesh.TlsMode.STRICT, + certificate: appmesh.TlsCertificate.file({ + certificateChainPath: 'path/to/certChain', + privateKeyPath: 'path/to/privateKey', + }), + }, })], }); @@ -530,11 +533,13 @@ export = { mesh, listeners: [appmesh.VirtualNodeListener.http({ port: 80, - tlsCertificate: appmesh.TlsCertificate.file({ - certificateChainPath: 'path/to/certChain', - privateKeyPath: 'path/to/privateKey', - tlsMode: appmesh.TlsMode.PERMISSIVE, - }), + tls: { + mode: appmesh.TlsMode.PERMISSIVE, + certificate: appmesh.TlsCertificate.file({ + certificateChainPath: 'path/to/certChain', + privateKeyPath: 'path/to/privateKey', + }), + }, })], }); @@ -765,11 +770,13 @@ export = { mesh, listeners: [appmesh.VirtualNodeListener.http({ port: 80, - tlsCertificate: appmesh.TlsCertificate.file({ - certificateChainPath: 'path/to/certChain', - privateKeyPath: 'path/to/privateKey', - tlsMode: appmesh.TlsMode.PERMISSIVE, - }), + tls: { + mode: appmesh.TlsMode.PERMISSIVE, + certificate: appmesh.TlsCertificate.file({ + certificateChainPath: 'path/to/certChain', + privateKeyPath: 'path/to/privateKey', + }), + }, })], }); From b5596c713a6c8cce58baa2b1e4ae2a48353018b3 Mon Sep 17 00:00:00 2001 From: Niranjan Jayakar Date: Fri, 28 May 2021 13:52:38 +0100 Subject: [PATCH 100/134] chore(prlint): ban 'breaking changes' clause on stable modules (#14861) The `scripts/check-api-compatibility.sh` script prevents breaking API changes in stable CDK modules. However, this does not prevent functional breaking changes. It is infeasible to actually detect and prevent this. The CDK must be more strict on preventing such changes and the impact due to their perception. A linter error when a 'BREAKING CHANGE' clause for a stable module is encountered in the PR is a good way to communicate this information. --- .github/workflows/pr-linter.yml | 3 +- tools/prlint/README.md | 32 ++- tools/prlint/decs.d.ts | 16 +- tools/prlint/lint.ts | 13 + tools/prlint/module.ts | 49 ++++ tools/prlint/package-lock.json | 432 +++++++++++++++++++++++++------ tools/prlint/package.json | 7 +- tools/prlint/parser.ts | 24 ++ tools/prlint/test/lint.test.ts | 55 +++- tools/prlint/test/module.test.ts | 57 ++++ tools/prlint/test/parser.test.ts | 54 ++++ 11 files changed, 645 insertions(+), 97 deletions(-) create mode 100644 tools/prlint/module.ts create mode 100644 tools/prlint/parser.ts create mode 100644 tools/prlint/test/module.test.ts create mode 100644 tools/prlint/test/parser.test.ts diff --git a/.github/workflows/pr-linter.yml b/.github/workflows/pr-linter.yml index bfcb5376d74d4..9c1980d9bea37 100644 --- a/.github/workflows/pr-linter.yml +++ b/.github/workflows/pr-linter.yml @@ -29,4 +29,5 @@ jobs: - name: Validate uses: ./tools/prlint env: - GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO_ROOT: ${{ github.workspace }} diff --git a/tools/prlint/README.md b/tools/prlint/README.md index 4466acedcbf68..4e37c087c9acb 100644 --- a/tools/prlint/README.md +++ b/tools/prlint/README.md @@ -1,10 +1,6 @@ # prlint -A linter that can run various checks to validate a PR adheres to our standards. - -### Disclaimer - -This is a very naive implementation that we currently consider as a PoC/Prototype to see how valuable it is. There are no API or functionality guarantees whatsoever. +A Github action that checks pull requests around PR titles, description and other metadata. # Checks @@ -15,9 +11,13 @@ This check validates that the modified files in the PR follow these rules: 1. `feat` requires a change to a `README.md`. 2. `feat` requires a change to a test file. 3. `fix` requires a change to a test file. +4. `BREAKING CHANGE` section is formatted correctly, per the [conventional commits] spec. +5. No breaking changes announced for stable modules. > These rules are currently hard coded, in the future, we should consider using [danger.js](https://danger.systems/js/). +[conventional commits]: https://www.conventionalcommits.org + # Installation ```console @@ -27,8 +27,28 @@ npm install # Usage +The steps for your Github action would look something like this - + +```yaml +steps: + - name: Checkout # checkout the package that contains prlint + uses: actions/checkout@v2 + + - name: Install & Build # install & build prlint + run: cd path/to/prlint && npm ci && npm build + + - name: Lint + uses: ./path/to/prlint + env: + REPO_ROOT: ${{ github.workspace }} +``` + +# Testing locally + +To test the linter against an actual PR locally, run + ```console -node tools/prlint/index.js mandatoryChanges +env REPO_ROOT=/path/to/cdk/repo node lint.js validatePr 5857 Creating un-authenticated GitHub Client ⌛ Fetching PR number 5857 diff --git a/tools/prlint/decs.d.ts b/tools/prlint/decs.d.ts index aca419554766b..c1b941dcc4cdd 100644 --- a/tools/prlint/decs.d.ts +++ b/tools/prlint/decs.d.ts @@ -1 +1,15 @@ -declare module 'github-api' \ No newline at end of file +declare module 'github-api'; + +declare module 'conventional-commits-parser' { + function sync(commitMsg: string): Parsed; + + interface Parsed { + readonly type: string | null; + readonly scope: string | null; + readonly subject: string | null; + readonly header: string | null; + readonly body: string | null; + readonly footer: string | null; + readonly notes: { title: string; text: string; }[]; + } +} \ No newline at end of file diff --git a/tools/prlint/lint.ts b/tools/prlint/lint.ts index 524675d4c81e0..12777f66da291 100755 --- a/tools/prlint/lint.ts +++ b/tools/prlint/lint.ts @@ -1,5 +1,7 @@ import * as path from 'path'; import * as GitHub from 'github-api'; +import { breakingModules } from './parser'; +import { findModulePath, moduleStability } from './module'; const OWNER = "aws" const REPO = "aws-cdk" @@ -100,6 +102,15 @@ function validateBreakingChangeFormat(title: string, body: string) { } } +function assertStability(title: string, body: string) { + const breakingStable = breakingModules(title, body) + .filter(mod => 'stable' === moduleStability(findModulePath(mod))) + + if (breakingStable.length > 0) { + throw new Error(`Breaking changes in stable modules [${breakingStable.join(', ')}] is disallowed.`); + } +} + export async function validatePr(number: number) { if (!number) { @@ -133,6 +144,8 @@ export async function validatePr(number: number) { } validateBreakingChangeFormat(issue.title, issue.body); + + assertStability(issue.title, issue.body) console.log("✅ Success") diff --git a/tools/prlint/module.ts b/tools/prlint/module.ts new file mode 100644 index 0000000000000..0712676c4c70b --- /dev/null +++ b/tools/prlint/module.ts @@ -0,0 +1,49 @@ +import * as fs from 'fs-extra'; +import * as glob from 'glob'; +import * as path from 'path'; + +const modules: string[] = []; + +export function findModulePath(fuzz: string): string { + discoverModules(); + + const regex = new RegExp(`[-_/]${fuzz}$`) + const matched = modules.filter(m => regex.test(m)); + if (matched.length === 0) { + throw new Error(`No module with name '${fuzz}' in the repo`); + } else if (matched.length > 1) { + // if multiple fuzzy matches, use an exact match + // if there are multiple exact matches, give up and return the first + return matched.find(m => path.basename(m) === fuzz) || matched[0]; + } + return matched[0]; +} + +function discoverModules() { + if (modules.length === 0) { + if (!process.env.REPO_ROOT) { + throw new Error('env REPO_ROOT must be set'); + } + const repoRoot = process.env.REPO_ROOT; + const lernaConfig = require(path.join(repoRoot, 'lerna.json')); + const searchPaths: string[] = lernaConfig.packages; + searchPaths.forEach(p => { + const globMatches = glob.sync(path.join(repoRoot, p, 'package.json')); + const trimmed = globMatches.map(m => path.dirname(m)); + modules.push(...trimmed); + }); + + if (modules.length === 0) { + throw new Error('unexpected: discovered no modules. ' + + 'Check that you have set REPO_ROOT correctly.'); + } + } +} + +export function moduleStability(loc: string): 'stable' | 'experimental' | undefined { + if (!fs.existsSync(path.join(loc, 'package.json'))) { + throw new Error(`unexpected: no package.json found at location "${loc}"`); + } + const pkgjson = require(path.join(loc, 'package.json')); + return pkgjson.stability; +} \ No newline at end of file diff --git a/tools/prlint/package-lock.json b/tools/prlint/package-lock.json index a92e7dedb436d..67f15b0519118 100644 --- a/tools/prlint/package-lock.json +++ b/tools/prlint/package-lock.json @@ -31,7 +31,6 @@ "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", - "dev": true, "requires": { "@babel/highlight": "^7.12.13" } @@ -221,8 +220,7 @@ "@babel/helper-validator-identifier": { "version": "7.14.0", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", - "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==", - "dev": true + "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==" }, "@babel/helper-validator-option": { "version": "7.12.17", @@ -245,7 +243,6 @@ "version": "7.14.0", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz", "integrity": "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.14.0", "chalk": "^2.0.0", @@ -256,7 +253,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -265,7 +261,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -276,7 +271,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "requires": { "color-name": "1.1.3" } @@ -284,20 +278,17 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -915,6 +906,25 @@ "@babel/types": "^7.3.0" } }, + "@types/fs-extra": { + "version": "9.0.11", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.11.tgz", + "integrity": "sha512-mZsifGG4QeQ7hlkhO56u7zt/ycBgGxSVsFI/6lGTU34VtwkiqrrSDgw0+ygs8kFGWcXnFQWMrzF2h7TtDFNixA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", + "dev": true, + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, "@types/graceful-fs": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", @@ -958,6 +968,17 @@ "pretty-format": "^26.0.0" } }, + "@types/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==", + "dev": true + }, + "@types/minimist": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz", + "integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==" + }, "@types/node": { "version": "15.6.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-15.6.0.tgz", @@ -966,8 +987,7 @@ "@types/normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", - "dev": true + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==" }, "@types/prettier": { "version": "2.2.3", @@ -996,6 +1016,15 @@ "integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==", "dev": true }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, "abab": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", @@ -1119,6 +1148,11 @@ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" + }, "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", @@ -1146,6 +1180,11 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" + }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -1251,8 +1290,7 @@ "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "base": { "version": "0.11.2", @@ -1333,7 +1371,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1413,8 +1450,17 @@ "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + } }, "caniuse-lite": { "version": "1.0.30001228", @@ -1560,8 +1606,21 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "conventional-commits-parser": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.1.tgz", + "integrity": "sha512-OG9kQtmMZBJD/32NEw5IhN5+HnBqVjy03eC+I71I0oQRFA5rOgA4OtPOYG7mz1GkCfCNxn3gKIX8EiHJYuf1cA==", + "requires": { + "JSONStream": "^1.0.4", + "is-text-path": "^1.0.1", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^3.0.0", + "through2": "^4.0.0", + "trim-off-newlines": "^1.0.0" + } }, "convert-source-map": { "version": "1.7.0", @@ -1659,8 +1718,23 @@ "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=" + } + } }, "decimal.js": { "version": "10.2.1", @@ -1807,7 +1881,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, "requires": { "is-arrayish": "^0.2.1" } @@ -1821,8 +1894,7 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "escodegen": { "version": "2.0.0", @@ -2078,7 +2150,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, "requires": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -2121,11 +2192,28 @@ "map-cache": "^0.2.2" } }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "dependencies": { + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + } + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { "version": "2.3.2", @@ -2137,8 +2225,7 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "gensync": { "version": "1.0.0-beta.2", @@ -2197,7 +2284,6 @@ "version": "7.1.7", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2216,8 +2302,7 @@ "graceful-fs": { "version": "4.2.6", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", - "dev": true + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" }, "growly": { "version": "1.3.0", @@ -2242,11 +2327,15 @@ "har-schema": "^2.0.0" } }, + "hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==" + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -2312,8 +2401,7 @@ "hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" }, "html-encoding-sniffer": { "version": "2.0.1", @@ -2372,11 +2460,15 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -2385,8 +2477,7 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "is-accessor-descriptor": { "version": "0.1.6", @@ -2411,8 +2502,7 @@ "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" }, "is-buffer": { "version": "1.1.6", @@ -2433,7 +2523,6 @@ "version": "2.4.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", - "dev": true, "requires": { "has": "^1.0.3" } @@ -2508,6 +2597,11 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" + }, "is-plain-object": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", @@ -2525,6 +2619,14 @@ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "dev": true }, + "is-text-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", + "integrity": "sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4=", + "requires": { + "text-extensions": "^1.0.0" + } + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -3175,8 +3277,7 @@ "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "js-yaml": { "version": "3.14.1", @@ -3237,8 +3338,7 @@ "json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, "json-schema": { "version": "0.2.3", @@ -3267,6 +3367,27 @@ "minimist": "^1.2.5" } }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + }, + "dependencies": { + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + } + } + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -3282,8 +3403,7 @@ "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" }, "kleur": { "version": "3.0.3", @@ -3310,14 +3430,12 @@ "lines-and-columns": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", - "dev": true + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=" }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, "requires": { "p-locate": "^4.1.0" } @@ -3325,8 +3443,7 @@ "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "lodash.get": { "version": "4.4.2", @@ -3347,7 +3464,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "requires": { "yallist": "^4.0.0" } @@ -3442,6 +3558,11 @@ "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", "dev": true }, + "map-obj": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", + "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==" + }, "map-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", @@ -3451,6 +3572,63 @@ "object-visit": "^1.0.0" } }, + "meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "dependencies": { + "hosted-git-info": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", + "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "normalize-package-data": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", + "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", + "requires": { + "hosted-git-info": "^4.0.1", + "resolve": "^1.20.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==" + }, + "yargs-parser": { + "version": "20.2.7", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", + "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==" + } + } + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -3488,11 +3666,15 @@ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==" + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -3503,6 +3685,16 @@ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, + "minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + } + }, "mixin-deep": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", @@ -3632,7 +3824,6 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, "requires": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", @@ -3643,8 +3834,7 @@ "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" } } }, @@ -3784,7 +3974,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, "requires": { "p-try": "^2.0.0" } @@ -3793,7 +3982,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, "requires": { "p-limit": "^2.2.0" } @@ -3801,14 +3989,12 @@ "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, "parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, "requires": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -3831,14 +4017,12 @@ "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-key": { "version": "2.0.1", @@ -3849,8 +4033,7 @@ "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" }, "performance-now": { "version": "2.1.0", @@ -3943,6 +4126,11 @@ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", "dev": true }, + "quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==" + }, "react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -3953,7 +4141,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, "requires": { "@types/normalize-package-data": "^2.4.0", "normalize-package-data": "^2.5.0", @@ -3964,8 +4151,7 @@ "type-fest": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==" } } }, @@ -3973,13 +4159,31 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, "requires": { "find-up": "^4.1.0", "read-pkg": "^5.2.0", "type-fest": "^0.8.1" } }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", @@ -4102,7 +4306,6 @@ "version": "1.20.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dev": true, "requires": { "is-core-module": "^2.2.0", "path-parse": "^1.0.6" @@ -4556,7 +4759,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -4565,14 +4767,12 @@ "spdx-exceptions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" }, "spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, "requires": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -4581,8 +4781,7 @@ "spdx-license-ids": { "version": "3.0.8", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.8.tgz", - "integrity": "sha512-NDgA96EnaLSvtbM7trJj+t1LUR3pirkDCcz9nOUlPb5DMBGsH7oES6C3hs3j7R9oHEa1EMvReS/BUAIT5Tcr0g==", - "dev": true + "integrity": "sha512-NDgA96EnaLSvtbM7trJj+t1LUR3pirkDCcz9nOUlPb5DMBGsH7oES6C3hs3j7R9oHEa1EMvReS/BUAIT5Tcr0g==" }, "split-string": { "version": "3.1.0", @@ -4593,6 +4792,14 @@ "extend-shallow": "^3.0.0" } }, + "split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "requires": { + "readable-stream": "^3.0.0" + } + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -4681,6 +4888,21 @@ "strip-ansi": "^6.0.0" } }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, "strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", @@ -4707,6 +4929,14 @@ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "requires": { + "min-indent": "^1.0.0" + } + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -4753,12 +4983,30 @@ "minimatch": "^3.0.4" } }, + "text-extensions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", + "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==" + }, "throat": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", "dev": true }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "requires": { + "readable-stream": "3" + } + }, "tmpl": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", @@ -4832,6 +5080,16 @@ "punycode": "^2.1.1" } }, + "trim-newlines": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz", + "integrity": "sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==" + }, + "trim-off-newlines": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz", + "integrity": "sha1-n5up2e+odkw4dpi8v+sshI8RrbM=" + }, "tunnel": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", @@ -4870,8 +5128,7 @@ "type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" }, "typedarray-to-buffer": { "version": "3.1.5", @@ -4977,6 +5234,11 @@ "resolved": "https://registry.npmjs.org/utf8/-/utf8-2.1.2.tgz", "integrity": "sha1-H6DZJw6b6FDZsFAn9jUZv0ZFfZY=" }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, "uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -5007,7 +5269,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, "requires": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" @@ -5247,8 +5508,7 @@ "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "yargs": { "version": "15.4.1", diff --git a/tools/prlint/package.json b/tools/prlint/package.json index 0ee54b249cf7a..fd7c131abaa76 100644 --- a/tools/prlint/package.json +++ b/tools/prlint/package.json @@ -13,9 +13,14 @@ "dependencies": { "@actions/core": "^1.3.0", "@actions/github": "^2.2.0", - "github-api": "^3.4.0" + "conventional-commits-parser": "^3.2.1", + "fs-extra": "^9.1.0", + "github-api": "^3.4.0", + "glob": "^7.1.7" }, "devDependencies": { + "@types/fs-extra": "^9.0.11", + "@types/glob": "^7.1.3", "@types/jest": "^26.0.23", "jest": "^26.6.3", "make-runnable": "^1.3.9", diff --git a/tools/prlint/parser.ts b/tools/prlint/parser.ts new file mode 100644 index 0000000000000..1f24b590d6f88 --- /dev/null +++ b/tools/prlint/parser.ts @@ -0,0 +1,24 @@ +import * as parser from 'conventional-commits-parser'; + +export function breakingModules(title: string, body: string): string[] { + const parsed = parser.sync(`${title}\n\n${body}`); + const breakingNotes = parsed.notes && parsed.notes.filter(n => n.title === 'BREAKING CHANGE'); + if (!breakingNotes || breakingNotes.length === 0) { + return []; + } + if (breakingNotes.length > 1) { + throw new Error('Multiple "BREAKING CHANGE" clause is disallowed.'); + } + const lines = breakingNotes[0].text.split(/[\n\r]+/).map(l => l.trim()); + const breakingModules: string[] = [] + if (!parsed.scope) { + throw new Error('Commits with breaking change must specify a "scope" in the title. See https://www.conventionalcommits.org'); + } + breakingModules.push(parsed.scope); + const re = /^\* \*\*([\w]+)\*\*/; + for (const line of lines) { + const match = re.exec(line); + match && breakingModules.push(match[1]); + } + return breakingModules; +} \ No newline at end of file diff --git a/tools/prlint/test/lint.test.ts b/tools/prlint/test/lint.test.ts index 96f0c3f6cdadf..2f86691c942ef 100644 --- a/tools/prlint/test/lint.test.ts +++ b/tools/prlint/test/lint.test.ts @@ -1,5 +1,6 @@ import * as GitHub from 'github-api'; import * as linter from '../lint'; +import * as path from 'path'; jest.mock('github-api'); @@ -7,6 +8,14 @@ beforeEach(() => { GitHub.mockClear(); }); +beforeAll(() => { + process.env.REPO_ROOT = path.join(__dirname, '..', '..', '..'); +}); + +afterAll(() => { + process.env.REPO_ROOT = undefined; +}); + describe('breaking changes format', () => { test('disallow variations to "BREAKING CHANGE:"', async () => { const issue = { @@ -39,12 +48,54 @@ describe('breaking changes format', () => { test('valid title', async () => { const issue = { - title: 'chore(foo): some title', + title: 'chore(cdk-build-tools): some title', body: 'BREAKING CHANGE: this breaking change', labels: [{ name: 'pr-linter/exempt-test' }, { name: 'pr-linter/exempt-readme' }] }; configureMock(issue, undefined); - await linter.validatePr(1000); // not throw + await expect(linter.validatePr(1000)).resolves; // not throw + }); +}); + +describe('ban breaking changes in stable modules', () => { + test('breaking change in stable module', async () => { + const issue = { + title: 'chore(s3): some title', + body: 'BREAKING CHANGE: this breaking change', + labels: [{ name: 'pr-linter/exempt-test' }, { name: 'pr-linter/exempt-readme' }] + }; + configureMock(issue, undefined); + await expect(linter.validatePr(1000)).rejects.toThrow('Breaking changes in stable modules [s3] is disallowed.'); + }); + + test('breaking changes multiple in stable modules', async () => { + const issue = { + title: 'chore(lambda): some title', + body: ` + BREAKING CHANGE: this breaking change + continued message + * **ecs**: further breaking in ecs + `, + labels: [{ name: 'pr-linter/exempt-test' }, { name: 'pr-linter/exempt-readme' }] + }; + configureMock(issue, undefined); + await expect(linter.validatePr(1000)).rejects.toThrow('Breaking changes in stable modules [lambda, ecs] is disallowed.'); + }); + + test('with additional "closes" footer', async () => { + const issue = { + title: 'chore(s3): some title', + body: ` + description of the commit + + closes #123456789 + + BREAKING CHANGE: this breaking change + `, + labels: [{ name: 'pr-linter/exempt-test' }, { name: 'pr-linter/exempt-readme' }] + }; + configureMock(issue, undefined); + await expect(linter.validatePr(1000)).rejects.toThrow('Breaking changes in stable modules [s3] is disallowed.'); }); }); diff --git a/tools/prlint/test/module.test.ts b/tools/prlint/test/module.test.ts new file mode 100644 index 0000000000000..9ec5172ee28b6 --- /dev/null +++ b/tools/prlint/test/module.test.ts @@ -0,0 +1,57 @@ +import { findModulePath, moduleStability } from '../module'; +import * as path from 'path'; + +const repoRoot = path.join(__dirname, '..', '..', '..'); + +describe('findModulePath', () => { + beforeAll(() => { + process.env.REPO_ROOT = repoRoot; + }); + + afterAll(() => { + process.env.REPO_ROOT = undefined; + }); + + test('single fuzzy match', () => { + expect(relative(findModulePath('lambda'))).toEqual('packages/@aws-cdk/aws-lambda'); + expect(relative(findModulePath('s3'))).toEqual('packages/@aws-cdk/aws-s3'); + expect(relative(findModulePath('cdk-build-tools'))).toEqual('tools/cdk-build-tools'); + }); + + test('multiple fuzzy matches', () => { + // also matches 'packages/@monocdk-experiment/assert' + expect(relative(findModulePath('assert'))).toEqual('packages/@aws-cdk/assert'); + + // also matches 'packages/aws-cdk' and 'tools/eslint-plugin-cdk' + expect(relative(findModulePath('cdk'))).toEqual('packages/cdk'); + }); + + test('no matches', () => { + expect(() => findModulePath('doesnotexist')).toThrow(/No module/); + }); + + function relative(loc: string) { + return path.relative(repoRoot, loc); + } +}); + +describe('moduleStability', () => { + test('happy', () => { + expect(moduleStability(absolute('packages/@aws-cdk/aws-lambda'))).toEqual('stable'); + expect(moduleStability(absolute('packages/@aws-cdk/aws-s3'))).toEqual('stable'); + + // The list of experimental modules is constantly changing. Comment out the assertion. + // expect(moduleStability(absolute('packages/@aws-cdk/aws-apigatewayv2'))).toEqual('experimental'); + // expect(moduleStability(absolute('packages/@aws-cdk/assert'))).toEqual('experimental'); + + expect(moduleStability(absolute('tools/cdk-build-tools'))).toBeUndefined(); + }); + + test('error', () => { + expect(() => moduleStability(absolute('tools'))).toThrow(/no package.json found/); + }); + + function absolute(loc: string) { + return path.join(repoRoot, loc); + } +}) \ No newline at end of file diff --git a/tools/prlint/test/parser.test.ts b/tools/prlint/test/parser.test.ts new file mode 100644 index 0000000000000..638dfcf4e126c --- /dev/null +++ b/tools/prlint/test/parser.test.ts @@ -0,0 +1,54 @@ +import { breakingModules } from '../parser'; + +describe('breakingModules', () => { + test('no breakages', () => { + const title = 'feat(m1): this is not a breaking change'; + const body = 'a regular description'; + + expect(breakingModules(title, body)).toEqual([]); + }); + + test('main module breaks', () => { + const title = 'feat(m1): this is a breaking change'; + const body = ` + a breaking change description + BREAKING CHANGE: unintended breaking change + `; + + expect(breakingModules(title, body)).toEqual(['m1']); + }); + + test('errors on missing scope', () => { + const title = 'feat: this is a breaking change'; + const body = ` + a breaking change description + BREAKING CHANGE: unintended breaking change + `; + + expect(() => breakingModules(title, body)).toThrow(/must specify a "scope"/); + }); + + test('multiple breaking changes', () => { + const title = 'feat(m1): this is a breaking change'; + const body = ` + a breaking change description + BREAKING CHANGE: unintended breaking change + continued message + * **m2**: Another breaking change here + continuing again + `; + + expect(breakingModules(title, body)).toEqual(['m1', 'm2']); + }); + + test('additional footer', () => { + const title = 'feat(m1): this is a breaking change'; + const body = ` + a breaking change description + closes #123456789 + BREAKING CHANGE: unintended breaking change + `; + + expect(breakingModules(title, body)).toEqual(['m1']); + }); +}); \ No newline at end of file From 8263c788a8e71006a4b2dce0f37444199de9c435 Mon Sep 17 00:00:00 2001 From: Seiya6329 Date: Fri, 28 May 2021 09:53:18 -0700 Subject: [PATCH 101/134] fix(appmesh): introduce the TlsClientPolicy and TlsValidation concepts (#14782) This is the first part of the series for [issue#12733](#12733). For the breakdown on series, please refer to [this comment](#14782 (comment)). #### Collaborators @alexbrjo and @dfezzie. Thank you! #### REV: - Adding `TlsValidation` abstract class with factory methods for available certificate sources. - Converting client policy into an interface. In addition, it is renamed to `TlsClientPolicy` to better reflect its representation. #### Design Note: - Adding `TlsValidation` abstract class since it became a common property for TLS client policy and TLS listener after mTLS feature support. BREAKING CHANGE: the creation property `clientPolicy` in `VirtualNode` has been renamed to `tlsClientPolicy`, and its type changed to `TlsClientPolicy` - **appmesh**: the creation property `clientPolicy` in `VirtualGateway` has been renamed to `tlsClientPolicy`, and its type changed to `TlsClientPolicy` - **appmesh**: to create `TlsClientPolicy`, `validation` property must be defined. --- *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-appmesh/README.md | 28 ++-- .../@aws-cdk/aws-appmesh/lib/client-policy.ts | 113 --------------- packages/@aws-cdk/aws-appmesh/lib/index.ts | 3 +- .../@aws-cdk/aws-appmesh/lib/private/utils.ts | 27 +++- .../aws-appmesh/lib/shared-interfaces.ts | 23 +-- .../aws-appmesh/lib/tls-client-policy.ts | 26 ++++ .../aws-appmesh/lib/tls-validation.ts | 135 ++++++++++++++++++ .../aws-appmesh/lib/virtual-gateway.ts | 5 +- .../@aws-cdk/aws-appmesh/lib/virtual-node.ts | 5 +- .../@aws-cdk/aws-appmesh/test/integ.mesh.ts | 20 ++- .../aws-appmesh/test/test.virtual-gateway.ts | 10 +- .../aws-appmesh/test/test.virtual-node.ts | 20 ++- 12 files changed, 262 insertions(+), 153 deletions(-) delete mode 100644 packages/@aws-cdk/aws-appmesh/lib/client-policy.ts create mode 100644 packages/@aws-cdk/aws-appmesh/lib/tls-client-policy.ts create mode 100644 packages/@aws-cdk/aws-appmesh/lib/tls-validation.ts diff --git a/packages/@aws-cdk/aws-appmesh/README.md b/packages/@aws-cdk/aws-appmesh/README.md index aa7d9f22318aa..b71bd5d9ab53c 100644 --- a/packages/@aws-cdk/aws-appmesh/README.md +++ b/packages/@aws-cdk/aws-appmesh/README.md @@ -183,9 +183,13 @@ const node = new VirtualNode(this, 'node', { }, })], backendDefaults: { - clientPolicy: appmesh.ClientPolicy.fileTrust({ - certificateChain: '/keys/local_cert_chain.pem', - }), + tlsClientPolicy: { + validation: { + trust: appmesh.TlsValidationTrust.file({ + certificateChain: '/keys/local_cert_chain.pem', + }), + }, + }, }, accessLog: appmesh.AccessLog.fromFilePath('/dev/stdout'), }); @@ -218,12 +222,8 @@ const node = new VirtualNode(this, 'node', { }); const virtualService = new appmesh.VirtualService(stack, 'service-1', { - serviceDiscovery: appmesh.ServiceDiscovery.dns('service1.domain.local'), - mesh, - clientPolicy: appmesh.ClientPolicy.fileTrust({ - certificateChain: '/keys/local_cert_chain.pem', - ports: [8080, 8081], - }), + virtualServiceProvider: appmesh.VirtualServiceProvider.virtualRouter(router), + virtualServiceName: 'service1.domain.local', }); node.addBackend(appmesh.Backend.virtualService(virtualService)); @@ -497,10 +497,14 @@ const gateway = new appmesh.VirtualGateway(stack, 'gateway', { }), })], backendDefaults: { - clientPolicy: appmesh.ClientPolicy.acmTrust({ - certificateAuthorities: [acmpca.CertificateAuthority.fromCertificateAuthorityArn(stack, 'certificate', certificateAuthorityArn)], + tlsClientPolicy: { ports: [8080, 8081], - }), + validation: { + trust: appmesh.TlsValidationTrust.acm({ + certificateAuthorities: [acmpca.CertificateAuthority.fromCertificateAuthorityArn(stack, 'certificate', certificateAuthorityArn)], + }), + }, + }, }, accessLog: appmesh.AccessLog.fromFilePath('/dev/stdout'), virtualGatewayName: 'virtualGateway', diff --git a/packages/@aws-cdk/aws-appmesh/lib/client-policy.ts b/packages/@aws-cdk/aws-appmesh/lib/client-policy.ts deleted file mode 100644 index 8f39b88399111..0000000000000 --- a/packages/@aws-cdk/aws-appmesh/lib/client-policy.ts +++ /dev/null @@ -1,113 +0,0 @@ -import * as acmpca from '@aws-cdk/aws-acmpca'; -import { CfnVirtualNode } from './appmesh.generated'; - -// keep this import separate from other imports to reduce chance for merge conflicts with v2-main -// eslint-disable-next-line no-duplicate-imports, import/order -import { Construct } from '@aws-cdk/core'; - -enum CertificateType { - ACMPCA = 'acm', - FILE = 'file', -} - -/** - * Properties of TLS Client Policy - */ -export interface ClientPolicyConfig { - /** - * Represents single Client Policy property - */ - readonly clientPolicy: CfnVirtualNode.ClientPolicyProperty; -} - -/** - * Represents the property needed to define a Client Policy - */ -export interface ClientPolicyOptions { - /** - * TLS is enforced on the ports specified here. - * If no ports are specified, TLS will be enforced on all the ports. - * - * @default - none - */ - readonly ports?: number[]; -} - -/** - * ACM Trust Properties - */ -export interface AcmTrustOptions extends ClientPolicyOptions { - /** - * Contains information for your private certificate authority - */ - readonly certificateAuthorities: acmpca.ICertificateAuthority[]; -} - -/** - * File Trust Properties - */ -export interface FileTrustOptions extends ClientPolicyOptions { - /** - * Path to the Certificate Chain file on the file system where the Envoy is deployed. - */ - readonly certificateChain: string; -} - -/** - * Defines the TLS validation context trust. - */ -export abstract class ClientPolicy { - /** - * Tells envoy where to fetch the validation context from - */ - public static fileTrust(props: FileTrustOptions): ClientPolicy { - return new ClientPolicyImpl(props.ports, CertificateType.FILE, props.certificateChain, undefined); - } - - /** - * TLS validation context trust for ACM Private Certificate Authority (CA). - */ - public static acmTrust(props: AcmTrustOptions): ClientPolicy { - return new ClientPolicyImpl(props.ports, CertificateType.ACMPCA, undefined, props.certificateAuthorities); - } - - /** - * Returns Trust context based on trust type. - */ - public abstract bind(scope: Construct): ClientPolicyConfig; - -} - -class ClientPolicyImpl extends ClientPolicy { - constructor (private readonly ports: number[] | undefined, - private readonly certificateType: CertificateType, - private readonly certificateChain: string | undefined, - private readonly certificateAuthorityArns: acmpca.ICertificateAuthority[] | undefined) { super(); } - - public bind(_scope: Construct): ClientPolicyConfig { - if (this.certificateType === CertificateType.ACMPCA && this.certificateAuthorityArns?.map(certificateArn => - certificateArn.certificateAuthorityArn).length === 0) { - throw new Error('You must provide at least one Certificate Authority when creating an ACM Trust ClientPolicy'); - } else { - return { - clientPolicy: { - tls: { - ports: this.ports, - validation: { - trust: { - [this.certificateType]: this.certificateType === CertificateType.FILE - ? { - certificateChain: this.certificateChain, - } - : { - certificateAuthorityArns: this.certificateAuthorityArns?.map(certificateArn => - certificateArn.certificateAuthorityArn), - }, - }, - }, - }, - }, - }; - } - } -} diff --git a/packages/@aws-cdk/aws-appmesh/lib/index.ts b/packages/@aws-cdk/aws-appmesh/lib/index.ts index 2fe71eed2cd62..052cbe622ce7d 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/index.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/index.ts @@ -15,6 +15,7 @@ export * from './virtual-gateway'; export * from './virtual-gateway-listener'; export * from './gateway-route'; export * from './gateway-route-spec'; -export * from './client-policy'; export * from './health-checks'; export * from './tls-listener'; +export * from './tls-validation'; +export * from './tls-client-policy'; diff --git a/packages/@aws-cdk/aws-appmesh/lib/private/utils.ts b/packages/@aws-cdk/aws-appmesh/lib/private/utils.ts index 8b6bd42f5b27e..6efca633d7a2d 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/private/utils.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/private/utils.ts @@ -1,3 +1,11 @@ +import { CfnVirtualNode } from '../appmesh.generated'; +import { TlsClientPolicy } from '../tls-client-policy'; +import { TlsValidationTrustConfig } from '../tls-validation'; + +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * Generated Connection pool config */ @@ -22,4 +30,21 @@ export interface ConnectionPoolConfig { * @default - none */ readonly maxRequests?: number; -} \ No newline at end of file +} + +/** + * This is the helper method to render TLS property of client policy. + * + */ +export function renderTlsClientPolicy(scope: Construct, tlsClientPolicy: TlsClientPolicy | undefined, + extractor: (c: TlsValidationTrustConfig) => CfnVirtualNode.TlsValidationContextTrustProperty): CfnVirtualNode.ClientPolicyTlsProperty | undefined { + return tlsClientPolicy + ? { + ports: tlsClientPolicy.ports, + enforce: tlsClientPolicy.enforce, + validation: { + trust: extractor(tlsClientPolicy.validation.trust.bind(scope)), + }, + } + : undefined; +} diff --git a/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts b/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts index f1753ef7bb3b3..deb319d1c3d93 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/shared-interfaces.ts @@ -1,6 +1,7 @@ import * as cdk from '@aws-cdk/core'; import { CfnVirtualGateway, CfnVirtualNode } from './appmesh.generated'; -import { ClientPolicy } from './client-policy'; +import { renderTlsClientPolicy } from './private/utils'; +import { TlsClientPolicy } from './tls-client-policy'; import { IVirtualService } from './virtual-service'; // keep this import separate from other imports to reduce chance for merge conflicts with v2-main @@ -174,9 +175,9 @@ export interface BackendDefaults { /** * Client policy for backend defaults * - * @default none + * @default - none */ - readonly clientPolicy?: ClientPolicy; + readonly tlsClientPolicy?: TlsClientPolicy; } /** @@ -187,9 +188,9 @@ export interface VirtualServiceBackendOptions { /** * Client policy for the backend * - * @default none + * @default - none */ - readonly clientPolicy?: ClientPolicy; + readonly tlsClientPolicy?: TlsClientPolicy; } /** @@ -211,7 +212,7 @@ export abstract class Backend { * Construct a Virtual Service backend */ public static virtualService(virtualService: IVirtualService, props: VirtualServiceBackendOptions = {}): Backend { - return new VirtualServiceBackend(virtualService, props.clientPolicy); + return new VirtualServiceBackend(virtualService, props.tlsClientPolicy); } /** @@ -226,19 +227,23 @@ export abstract class Backend { class VirtualServiceBackend extends Backend { constructor (private readonly virtualService: IVirtualService, - private readonly clientPolicy: ClientPolicy | undefined) { + private readonly tlsClientPolicy: TlsClientPolicy | undefined) { super(); } /** * Return config for a Virtual Service backend */ - public bind(_scope: Construct): BackendConfig { + public bind(scope: Construct): BackendConfig { return { virtualServiceBackend: { virtualService: { virtualServiceName: this.virtualService.virtualServiceName, - clientPolicy: this.clientPolicy?.bind(_scope).clientPolicy, + clientPolicy: this.tlsClientPolicy + ? { + tls: renderTlsClientPolicy(scope, this.tlsClientPolicy, (config) => config.virtualNodeClientTlsValidationTrust), + } + : undefined, }, }, }; diff --git a/packages/@aws-cdk/aws-appmesh/lib/tls-client-policy.ts b/packages/@aws-cdk/aws-appmesh/lib/tls-client-policy.ts new file mode 100644 index 0000000000000..3efe617ec1b9e --- /dev/null +++ b/packages/@aws-cdk/aws-appmesh/lib/tls-client-policy.ts @@ -0,0 +1,26 @@ +import { TlsValidation } from './tls-validation'; + +/** + * Represents the properties needed to define client policy + */ +export interface TlsClientPolicy { + /** + * Whether the policy is enforced. + * + * @default true + */ + readonly enforce?: boolean; + + /** + * TLS is enforced on the ports specified here. + * If no ports are specified, TLS will be enforced on all the ports. + * + * @default - all ports + */ + readonly ports?: number[]; + + /** + * Represents the object for TLS validation context + */ + readonly validation: TlsValidation; +} diff --git a/packages/@aws-cdk/aws-appmesh/lib/tls-validation.ts b/packages/@aws-cdk/aws-appmesh/lib/tls-validation.ts new file mode 100644 index 0000000000000..aa7be21f8dacf --- /dev/null +++ b/packages/@aws-cdk/aws-appmesh/lib/tls-validation.ts @@ -0,0 +1,135 @@ +import * as acmpca from '@aws-cdk/aws-acmpca'; +import { CfnVirtualGateway, CfnVirtualNode } from './appmesh.generated'; + +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + +/** + * Represents the properties needed to define TLS validation context + */ +export interface TlsValidation { + /** + * Reference to where to retrieve the trust chain. + */ + readonly trust: TlsValidationTrust; +} + +/** + * All Properties for TLS Validations for both Client Policy and Listener. + */ +export interface TlsValidationTrustConfig { + /** + * VirtualNode CFN configuration for client policy's TLS Validation + */ + readonly virtualNodeClientTlsValidationTrust: CfnVirtualNode.TlsValidationContextTrustProperty; + + /** + * VirtualGateway CFN configuration for client policy's TLS Validation + */ + readonly virtualGatewayClientTlsValidationTrust: CfnVirtualGateway.VirtualGatewayTlsValidationContextTrustProperty; +} + +/** + * ACM Trust Properties + */ +export interface TlsValidationAcmTrustOptions { + /** + * Contains information for your private certificate authority + */ + readonly certificateAuthorities: acmpca.ICertificateAuthority[]; +} + +/** + * File Trust Properties + */ +export interface TlsValidationFileTrustOptions { + /** + * Path to the Certificate Chain file on the file system where the Envoy is deployed. + */ + readonly certificateChain: string; +} + +/** + * Defines the TLS validation context trust. + */ +export abstract class TlsValidationTrust { + /** + * Tells envoy where to fetch the validation context from + */ + public static file(props: TlsValidationFileTrustOptions): TlsValidationTrust { + return new TlsValidationFileTrust(props); + } + + /** + * TLS validation context trust for ACM Private Certificate Authority (CA). + */ + public static acm(props: TlsValidationAcmTrustOptions): TlsValidationTrust { + return new TlsValidationAcmTrust(props); + } + + /** + * Returns Trust context based on trust type. + */ + public abstract bind(scope: Construct): TlsValidationTrustConfig; +} + +class TlsValidationAcmTrust extends TlsValidationTrust { + /** + * Contains information for your private certificate authority + */ + readonly certificateAuthorities: acmpca.ICertificateAuthority[]; + + constructor (props: TlsValidationAcmTrustOptions) { + super(); + this.certificateAuthorities = props.certificateAuthorities; + } + + public bind(_scope: Construct): TlsValidationTrustConfig { + if (this.certificateAuthorities.length === 0) { + throw new Error('you must provide at least one Certificate Authority when creating an ACM Trust ClientPolicy'); + } else { + return { + virtualNodeClientTlsValidationTrust: { + acm: { + certificateAuthorityArns: this.certificateAuthorities.map(certificateArn => + certificateArn.certificateAuthorityArn), + }, + }, + virtualGatewayClientTlsValidationTrust: { + acm: { + certificateAuthorityArns: this.certificateAuthorities.map(certificateArn => + certificateArn.certificateAuthorityArn), + }, + }, + }; + } + } +} + +class TlsValidationFileTrust extends TlsValidationTrust { + /** + * Path to the Certificate Chain file on the file system where the Envoy is deployed. + */ + readonly certificateChain: string; + + constructor (props: TlsValidationFileTrustOptions) { + super(); + this.certificateChain = props.certificateChain; + } + + public bind(_scope: Construct): TlsValidationTrustConfig { + return { + virtualNodeClientTlsValidationTrust: { + file: { + certificateChain: this.certificateChain, + }, + }, + virtualGatewayClientTlsValidationTrust: { + file: { + certificateChain: this.certificateChain, + }, + }, + }; + } +} diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts index 4a86f137a6337..e2ef227c373a8 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-gateway.ts @@ -4,6 +4,7 @@ import { Construct } from 'constructs'; import { CfnVirtualGateway } from './appmesh.generated'; import { GatewayRoute, GatewayRouteBaseProps } from './gateway-route'; import { IMesh, Mesh } from './mesh'; +import { renderTlsClientPolicy } from './private/utils'; import { AccessLog, BackendDefaults } from './shared-interfaces'; import { VirtualGatewayListener, VirtualGatewayListenerConfig } from './virtual-gateway-listener'; @@ -195,7 +196,9 @@ export class VirtualGateway extends VirtualGatewayBase { listeners: this.listeners.map(listener => listener.listener), backendDefaults: props.backendDefaults !== undefined ? { - clientPolicy: props.backendDefaults?.clientPolicy?.bind(this).clientPolicy, + clientPolicy: { + tls: renderTlsClientPolicy(this, props.backendDefaults?.tlsClientPolicy, (config) => config.virtualGatewayClientTlsValidationTrust), + }, } : undefined, logging: accessLogging !== undefined ? { diff --git a/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts b/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts index 82c6aff3992e0..6c4a41c85b270 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/virtual-node.ts @@ -3,6 +3,7 @@ import * as cdk from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnVirtualNode } from './appmesh.generated'; import { IMesh, Mesh } from './mesh'; +import { renderTlsClientPolicy } from './private/utils'; import { ServiceDiscovery } from './service-discovery'; import { AccessLog, BackendDefaults, Backend } from './shared-interfaces'; import { VirtualNodeListener, VirtualNodeListenerConfig } from './virtual-node-listener'; @@ -198,7 +199,9 @@ export class VirtualNode extends VirtualNodeBase { listeners: cdk.Lazy.any({ produce: () => this.listeners.map(listener => listener.listener) }, { omitEmptyArray: true }), backendDefaults: props.backendDefaults !== undefined ? { - clientPolicy: props.backendDefaults?.clientPolicy?.bind(this).clientPolicy, + clientPolicy: { + tls: renderTlsClientPolicy(this, props.backendDefaults?.tlsClientPolicy, (config) => config.virtualNodeClientTlsValidationTrust), + }, } : undefined, serviceDiscovery: { diff --git a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts index f53174b15b449..d691ed086d121 100644 --- a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts +++ b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts @@ -76,9 +76,13 @@ const node2 = mesh.addVirtualNode('node2', { }), })], backendDefaults: { - clientPolicy: appmesh.ClientPolicy.fileTrust({ - certificateChain: 'path/to/cert', - }), + tlsClientPolicy: { + validation: { + trust: appmesh.TlsValidationTrust.file({ + certificateChain: 'path/to/cert', + }), + }, + }, }, backends: [appmesh.Backend.virtualService( new appmesh.VirtualService(stack, 'service-3', { @@ -100,9 +104,13 @@ const node3 = mesh.addVirtualNode('node3', { }), })], backendDefaults: { - clientPolicy: appmesh.ClientPolicy.fileTrust({ - certificateChain: 'path-to-certificate', - }), + tlsClientPolicy: { + validation: { + trust: appmesh.TlsValidationTrust.file({ + certificateChain: 'path-to-certificate', + }), + }, + }, }, accessLog: appmesh.AccessLog.fromFilePath('/dev/stdout'), }); diff --git a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts index 35e441e54d622..096e068f19afb 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.virtual-gateway.ts @@ -397,9 +397,13 @@ export = { virtualGatewayName: 'virtual-gateway', mesh: mesh, backendDefaults: { - clientPolicy: appmesh.ClientPolicy.fileTrust({ - certificateChain: 'path-to-certificate', - }), + tlsClientPolicy: { + validation: { + trust: appmesh.TlsValidationTrust.file({ + certificateChain: 'path-to-certificate', + }), + }, + }, }, }); diff --git a/packages/@aws-cdk/aws-appmesh/test/test.virtual-node.ts b/packages/@aws-cdk/aws-appmesh/test/test.virtual-node.ts index 870af04737384..3353c972141cd 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.virtual-node.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.virtual-node.ts @@ -331,10 +331,14 @@ export = { mesh, serviceDiscovery: appmesh.ServiceDiscovery.dns('test'), backendDefaults: { - clientPolicy: appmesh.ClientPolicy.acmTrust({ - certificateAuthorities: [acmpca.CertificateAuthority.fromCertificateAuthorityArn(stack, 'certificate', certificateAuthorityArn)], + tlsClientPolicy: { ports: [8080, 8081], - }), + validation: { + trust: appmesh.TlsValidationTrust.acm({ + certificateAuthorities: [acmpca.CertificateAuthority.fromCertificateAuthorityArn(stack, 'certificate', certificateAuthorityArn)], + }), + }, + }, }, }); @@ -383,10 +387,14 @@ export = { }); node.addBackend(appmesh.Backend.virtualService(service1, { - clientPolicy: appmesh.ClientPolicy.fileTrust({ - certificateChain: 'path-to-certificate', + tlsClientPolicy: { ports: [8080, 8081], - }), + validation: { + trust: appmesh.TlsValidationTrust.file({ + certificateChain: 'path-to-certificate', + }), + }, + }, })); // THEN From f62facbd763e2553530ec747186e6b459b6d0abd Mon Sep 17 00:00:00 2001 From: "Eric Z. Beard" Date: Fri, 28 May 2021 14:10:32 -0700 Subject: [PATCH 102/134] chore(docs): add owners for missing modules (#14829) * Change servicecatalog ownership * Add owners for missing modules * Update .github/workflows/issue-label-assign.yml Co-authored-by: Adam Ruka * Remove empty line * Adding new team members to auto assignment Co-authored-by: Adam Ruka --- .github/workflows/issue-label-assign.yml | 162 ++++++++++++++--------- 1 file changed, 100 insertions(+), 62 deletions(-) diff --git a/.github/workflows/issue-label-assign.yml b/.github/workflows/issue-label-assign.yml index fce1ea1e9bbae..08d852b209784 100644 --- a/.github/workflows/issue-label-assign.yml +++ b/.github/workflows/issue-label-assign.yml @@ -20,44 +20,47 @@ jobs: parameters: > [ {"keywords":["(cli)","(command line)"],"labels":["package/tools"],"assignees":["rix0rrr"]}, - {"keywords":["(@aws-cdk/alexa-ask)","(alexa-ask)","(alexa ask)"],"labels":["@aws-cdk/alexa-ask"],"assignees":["MrArnoldPalmer"]}, + {"keywords":["(@aws-cdk/alexa-ask)","(alexa-ask)","(alexa ask)"],"labels":["@aws-cdk/alexa-ask"],"assignees":["madeline-k"]}, {"keywords":["(@aws-cdk/app-delivery)","(app-delivery)","(app delivery)"],"labels":["@aws-cdk/app-delivery"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/assert)","(assert)"],"labels":["@aws-cdk/assert"],"assignees":["nija-at"]}, {"keywords":["(@aws-cdk/assets)","(assets)"],"labels":["@aws-cdk/assets"],"assignees":["eladb"]}, {"keywords":["(@aws-cdk/aws-accessanalyzer)","(aws-accessanalyzer)","(accessanalyzer)","(access analyzer)"],"labels":["@aws-cdk/aws-accessanalyzer"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-acmpca)","(aws-acmpca)","(acmpca)"],"labels":["@aws-cdk/aws-acmpca"],"assignees":["skinny85"]}, - {"keywords":["(@aws-cdk/aws-amazonmq)","(aws-amazonmq)","(amazonmq)","(amazon mq)","(amazon-mq)"],"labels":["@aws-cdk/aws-amazonmq"],"assignees":["MrArnoldPalmer"]}, - {"keywords":["(@aws-cdk/aws-amplify)","(aws-amplify)","(amplify)"],"labels":["@aws-cdk/aws-amplify"],"assignees":["MrArnoldPalmer"]}, + {"keywords":["(@aws-cdk/aws-amazonmq)","(aws-amazonmq)","(amazonmq)","(amazon mq)","(amazon-mq)"],"labels":["@aws-cdk/aws-amazonmq"],"assignees":["otaviomacedo"]}, + {"keywords":["(@aws-cdk/aws-amplify)","(aws-amplify)","(amplify)"],"labels":["@aws-cdk/aws-amplify"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-apigateway)","(aws-apigateway)","(apigateway)","(api gateway)","(api-gateway)"],"labels":["@aws-cdk/aws-apigateway"],"assignees":["nija-at"]}, {"keywords":["(@aws-cdk/aws-apigatewayv2)","(aws-apigatewayv2)","(apigatewayv2)","(apigateway v2)","(api-gateway-v2)"],"labels":["@aws-cdk/aws-apigatewayv2"],"assignees":["nija-at"]}, + {"keywords":["(@aws-cdk/aws-apigatewayv2-authorizers)","(aws-apigatewayv2-authorizers)","(apigatewayv2-authorizers)"],"labels":["@aws-cdk/aws-apigatewayv2-authorizers"],"assignees":["nija-at"]}, {"keywords":["(@aws-cdk/aws-apigatewayv2-integrations)","(aws-apigatewayv2-integrations)","(apigatewayv2-integrations)","(apigateway v2 integrations)","(api-gateway-v2-integrations)"],"labels":["@aws-cdk/aws-apigatewayv2-integrations"],"assignees":["nija-at"]}, - {"keywords":["(@aws-cdk/aws-appconfig)","(aws-appconfig)","(appconfig)","(app config)","(app-config)"],"labels":["@aws-cdk/aws-appconfig"],"assignees":["MrArnoldPalmer"]}, + {"keywords":["(@aws-cdk/aws-appconfig)","(aws-appconfig)","(appconfig)","(app config)","(app-config)"],"labels":["@aws-cdk/aws-appconfig"],"assignees":["rix0rrr"]}, {"keywords":["(@aws-cdk/aws-appflow)","(aws-appflow)","(appflow)","(app flow)","(app-flow)"],"labels":["@aws-cdk/aws-appflow"],"assignees":["skinny85"]}, - {"keywords":["(@aws-cdk/aws-applicationautoscaling)","(aws-applicationautoscaling)","(applicationautoscaling)","(application autoscaling)","(application-autoscaling)"],"labels":["@aws-cdk/aws-applicationautoscaling"],"assignees":["NetaNir"]}, + {"keywords":["(@aws-cdk/aws-appintegrations)","(aws-appintegrations)","(appintegrations)"],"labels":["@aws-cdk/aws-appintegrations"],"assignees":["skinny85"]}, + {"keywords":["(@aws-cdk/aws-applicationautoscaling)","(aws-applicationautoscaling)","(applicationautoscaling)","(application autoscaling)","(application-autoscaling)"],"labels":["@aws-cdk/aws-applicationautoscaling"],"assignees":["rix0rrr"]}, {"keywords":["(@aws-cdk/aws-applicationinsights)","(aws-applicationinsights)","(applicationinsights)","(application insights)","(application-insights)"],"labels":["@aws-cdk/aws-applicationinsights"],"assignees":["njlynch"]}, {"keywords":["(@aws-cdk/aws-appmesh)","(aws-appmesh)","(appmesh)","(app mesh)","(app-mesh)"],"labels":["@aws-cdk/aws-appmesh"],"assignees":["skinny85"]}, - {"keywords":["(@aws-cdk/aws-appstream)","(aws-appstream)","(appstream)","(app stream)","(app-stream)"],"labels":["@aws-cdk/aws-appstream"],"assignees":["NetaNir"]}, - {"keywords":["(@aws-cdk/aws-appsync)","(aws-appsync)","(appsync)","(app sync)","(app-sync)"],"labels":["@aws-cdk/aws-appsync"],"assignees":["MrArnoldPalmer"]}, - {"keywords":["(@aws-cdk/aws-athena)","(aws-athena)","(athena)"],"labels":["@aws-cdk/aws-athena"],"assignees":["iliapolo"]}, - {"keywords":["(@aws-cdk/aws-autoscaling)","(aws-autoscaling)","(autoscaling)","(auto scaling)","(auto-scaling)"],"labels":["@aws-cdk/aws-autoscaling"],"assignees":["NetaNir"]}, - {"keywords":["(@aws-cdk/aws-autoscaling-api)","(aws-autoscaling-api)","(autoscaling-api)","(autoscaling api)","(autoscaling-api)"],"labels":["@aws-cdk/aws-autoscaling-api"],"assignees":["NetaNir"]}, - {"keywords":["(@aws-cdk/aws-autoscaling-common)","(aws-autoscaling-common)","(autoscaling-common)","(autoscaling common)","(autoscaling-common)"],"labels":["@aws-cdk/aws-autoscaling-common"],"assignees":["NetaNir"]}, - {"keywords":["(@aws-cdk/aws-autoscaling-hooktargets)","(aws-autoscaling-hooktargets)","(autoscaling-hooktargets)","(autoscaling hooktargets)"],"labels":["@aws-cdk/aws-autoscaling-hooktargets"],"assignees":["NetaNir"]}, - {"keywords":["(@aws-cdk/aws-autoscalingplans)","(aws-autoscalingplans)","(autoscalingplans)","(autoscaling plans)", "(autoscaling-plans)"],"labels":["@aws-cdk/aws-autoscalingplans"],"assignees":["NetaNir"]}, - {"keywords":["(@aws-cdk/aws-backup)","(aws-backup)","(backup)"],"labels":["@aws-cdk/aws-backup"],"assignees":["MrArnoldPalmer"]}, - {"keywords":["(@aws-cdk/aws-batch)","(aws-batch)","(batch)"],"labels":["@aws-cdk/aws-batch"],"assignees":["iliapolo"]}, - {"keywords":["(@aws-cdk/aws-budgets)","(aws-budgets)","(budgets)"],"labels":["@aws-cdk/aws-budgets"],"assignees":["MrArnoldPalmer"]}, - {"keywords":["(@aws-cdk/aws-cassandra)","(aws-cassandra)","(cassandra)"],"labels":["@aws-cdk/aws-cassandra"],"assignees":["iliapolo"]}, - {"keywords":["(@aws-cdk/aws-ce)","(aws-ce)","(ce)"],"labels":["@aws-cdk/aws-ce"],"assignees":["MrArnoldPalmer"]}, + {"keywords":["(@aws-cdk/aws-appstream)","(aws-appstream)","(appstream)","(app stream)","(app-stream)"],"labels":["@aws-cdk/aws-appstream"],"assignees":["madeline-k"]}, + {"keywords":["(@aws-cdk/aws-appsync)","(aws-appsync)","(appsync)","(app sync)","(app-sync)"],"labels":["@aws-cdk/aws-appsync"],"assignees":["otaviomacedo"]}, + {"keywords":["(@aws-cdk/aws-athena)","(aws-athena)","(athena)"],"labels":["@aws-cdk/aws-athena"],"assignees":["BenChaimberg"]}, + {"keywords":["(@aws-cdk/aws-auditmanager)","(aws-auditmanager)","(auditmanager)"],"labels":["@aws-cdk/aws-auditmanager"],"assignees":["njlynch"]}, + {"keywords":["(@aws-cdk/aws-autoscaling)","(aws-autoscaling)","(autoscaling)","(auto scaling)","(auto-scaling)"],"labels":["@aws-cdk/aws-autoscaling"],"assignees":["rix0rrr"]}, + {"keywords":["(@aws-cdk/aws-autoscaling-api)","(aws-autoscaling-api)","(autoscaling-api)","(autoscaling api)","(autoscaling-api)"],"labels":["@aws-cdk/aws-autoscaling-api"],"assignees":["rix0rrr"]}, + {"keywords":["(@aws-cdk/aws-autoscaling-common)","(aws-autoscaling-common)","(autoscaling-common)","(autoscaling common)","(autoscaling-common)"],"labels":["@aws-cdk/aws-autoscaling-common"],"assignees":["rix0rrr"]}, + {"keywords":["(@aws-cdk/aws-autoscaling-hooktargets)","(aws-autoscaling-hooktargets)","(autoscaling-hooktargets)","(autoscaling hooktargets)"],"labels":["@aws-cdk/aws-autoscaling-hooktargets"],"assignees":["rix0rrr"]}, + {"keywords":["(@aws-cdk/aws-autoscalingplans)","(aws-autoscalingplans)","(autoscalingplans)","(autoscaling plans)", "(autoscaling-plans)"],"labels":["@aws-cdk/aws-autoscalingplans"],"assignees":["rix0rrr"]}, + {"keywords":["(@aws-cdk/aws-backup)","(aws-backup)","(backup)"],"labels":["@aws-cdk/aws-backup"],"assignees":["BenChaimberg"]}, + {"keywords":["(@aws-cdk/aws-batch)","(aws-batch)","(batch)"],"labels":["@aws-cdk/aws-batch"],"assignees":["madeline-k"]}, + {"keywords":["(@aws-cdk/aws-budgets)","(aws-budgets)","(budgets)"],"labels":["@aws-cdk/aws-budgets"],"assignees":["njlynch"]}, + {"keywords":["(@aws-cdk/aws-cassandra)","(aws-cassandra)","(cassandra)"],"labels":["@aws-cdk/aws-cassandra"],"assignees":["otaviomacedo"]}, + {"keywords":["(@aws-cdk/aws-ce)","(aws-ce)","(ce)"],"labels":["@aws-cdk/aws-ce"],"assignees":["njlynch"]}, {"keywords":["(@aws-cdk/aws-certificatemanager)","(aws-certificatemanager)","(certificatemanager)","(certificate manager)","(certificate-manager)","(acm)"],"labels":["@aws-cdk/aws-certificatemanager"],"assignees":["njlynch"]}, - {"keywords":["(@aws-cdk/aws-chatbot)","(aws-chatbot)","(chatbot)"],"labels":["@aws-cdk/aws-chatbot"],"assignees":["MrArnoldPalmer"]}, + {"keywords":["(@aws-cdk/aws-chatbot)","(aws-chatbot)","(chatbot)"],"labels":["@aws-cdk/aws-chatbot"],"assignees":["BenChaimberg"]}, {"keywords":["(@aws-cdk/aws-cloud9)","(aws-cloud9)","(cloud9)","(cloud 9)"],"labels":["@aws-cdk/aws-cloud9"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-cloudformation)","(aws-cloudformation)","(cloudformation)","(cloud formation)"],"labels":["@aws-cdk/aws-cloudformation"],"assignees":["rix0rrr"]}, {"keywords":["(@aws-cdk/aws-cloudfront)","(aws-cloudfront)","(cloudfront)","(cloud front)"],"labels":["@aws-cdk/aws-cloudfront"],"assignees":["njlynch"]}, {"keywords":["(@aws-cdk/aws-cloudfront-origins)","(aws-cloudfront-origins)","(cloudfront-origins)","(cloudfront origins)"],"labels":["@aws-cdk/aws-cloudfront-origins"],"assignees":["njlynch"]}, - {"keywords":["(@aws-cdk/aws-cloudtrail)","(aws-cloudtrail)","(cloudtrail)","(cloud trail)"],"labels":["@aws-cdk/aws-cloudtrail"],"assignees":["NetaNir"]}, - {"keywords":["(@aws-cdk/aws-cloudwatch)","(aws-cloudwatch)","(cloudwatch)","(cloud watch)"],"labels":["@aws-cdk/aws-cloudwatch"],"assignees":["NetaNir"]}, - {"keywords":["(@aws-cdk/aws-cloudwatch-actions)","(aws-cloudwatch-actions)","(cloudwatch-actions)","(cloudwatch actions)"],"labels":["@aws-cdk/aws-cloudwatch-actions"],"assignees":["NetaNir"]}, + {"keywords":["(@aws-cdk/aws-cloudtrail)","(aws-cloudtrail)","(cloudtrail)","(cloud trail)"],"labels":["@aws-cdk/aws-cloudtrail"],"assignees":["rix0rrr"]}, + {"keywords":["(@aws-cdk/aws-cloudwatch)","(aws-cloudwatch)","(cloudwatch)","(cloud watch)"],"labels":["@aws-cdk/aws-cloudwatch"],"assignees":["madeline-k"]}, + {"keywords":["(@aws-cdk/aws-cloudwatch-actions)","(aws-cloudwatch-actions)","(cloudwatch-actions)","(cloudwatch actions)"],"labels":["@aws-cdk/aws-cloudwatch-actions"],"assignees":["madeline-k"]}, {"keywords":["(@aws-cdk/aws-codeartifact)","(aws-codeartifact)","(codeartifact)","(code artifact)","(code-artifact)"],"labels":["@aws-cdk/aws-codeartifact"],"assignees":["njlynch"]}, {"keywords":["(@aws-cdk/aws-codebuild)","(aws-codebuild)","(codebuild)","(code build)","(code-build)"],"labels":["@aws-cdk/aws-codebuild"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-codecommit)","(aws-codecommit)","(codecommit)","(code commit)", "(code-commit)"],"labels":["@aws-cdk/aws-codecommit"],"assignees":["skinny85"]}, @@ -70,37 +73,52 @@ jobs: {"keywords":["(@aws-cdk/aws-codestarconnections)","(aws-codestarconnections)","(codestarconnections)","(codestar connections)","(codestar-connections)"],"labels":["@aws-cdk/aws-codestarconnections"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-codestarnotifications)","(aws-codestarnotifications)","(codestarnotifications)","(codestar notifications)","(codestar-notifications)"],"labels":["@aws-cdk/aws-codestarnotifications"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-cognito)","(aws-cognito)","(cognito)"],"labels":["@aws-cdk/aws-cognito"],"assignees":["nija-at"]}, - {"keywords":["(@aws-cdk/aws-config)","(aws-config)","(config)"],"labels":["@aws-cdk/aws-config"],"assignees":["MrArnoldPalmer"]}, - {"keywords":["(@aws-cdk/aws-datapipeline)","(aws-datapipeline)","(datapipeline)","(data pipeline)","(data-pipeline)"],"labels":["@aws-cdk/aws-datapipeline"],"assignees":["iliapolo"]}, + {"keywords":["(@aws-cdk/aws-config)","(aws-config)","(config)"],"labels":["@aws-cdk/aws-config"],"assignees":["rix0rrr"]}, + {"keywords":["(@aws-cdk/aws-customerprofiles)","(aws-customerprofiles)","(customerprofiles)"],"labels":["@aws-cdk/aws-customerprofiles"],"assignees":["otaviomacedo"]}, + {"keywords":["(@aws-cdk/aws-databrew)","(aws-databrew)","(databrew)"],"labels":["@aws-cdk/aws-databrew"],"assignees":["BenChaimberg"]}, + {"keywords":["(@aws-cdk/aws-datapipeline)","(aws-datapipeline)","(datapipeline)","(data pipeline)","(data-pipeline)"],"labels":["@aws-cdk/aws-datapipeline"],"assignees":["BenChaimberg"]}, + {"keywords":["(@aws-cdk/aws-datasync)","(aws-datasync)","(datasync)"],"labels":["@aws-cdk/aws-datasync"],"assignees":["BenChaimberg"]}, {"keywords":["(@aws-cdk/aws-dax)","(aws-dax)","(dax)"],"labels":["@aws-cdk/aws-dax"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-detective)","(aws-detective)","(detective)"],"labels":["@aws-cdk/aws-detective"],"assignees":["skinny85"]}, - {"keywords":["(@aws-cdk/aws-directoryservice)","(aws-directoryservice)","(directoryservice)","(directory service)","(directory-service)"],"labels":["@aws-cdk/aws-directoryservice"],"assignees":["NetaNir"]}, + {"keywords":["(@aws-cdk/aws-devopsguru)","(aws-devopsguru)","(devopsguru)"],"labels":["@aws-cdk/aws-devopsguru"],"assignees":["nija-at"]}, + {"keywords":["(@aws-cdk/aws-directoryservice)","(aws-directoryservice)","(directoryservice)","(directory service)","(directory-service)"],"labels":["@aws-cdk/aws-directoryservice"],"assignees":["rix0rrr"]}, {"keywords":["(@aws-cdk/aws-dlm)","(aws-dlm)","(dlm)"],"labels":["@aws-cdk/aws-dlm"],"assignees":["njlynch"]}, {"keywords":["(@aws-cdk/aws-dms)","(aws-dms)","(dms)"],"labels":["@aws-cdk/aws-dms"],"assignees":["njlynch"]}, - {"keywords":["(@aws-cdk/aws-docdb)","(aws-docdb)","(docdb)","(doc db)","(doc-db)"],"labels":["@aws-cdk/aws-docdb"],"assignees":["iliapolo"]}, + {"keywords":["(@aws-cdk/aws-docdb)","(aws-docdb)","(docdb)","(doc db)","(doc-db)"],"labels":["@aws-cdk/aws-docdb"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-dynamodb)","(aws-dynamodb)","(dynamodb)","(dynamo db)","(dynamo-db)"],"labels":["@aws-cdk/aws-dynamodb"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-dynamodb-global)","(aws-dynamodb-global)","(dynamodb-global)","(dynamodb global)"],"labels":["@aws-cdk/aws-dynamodb-global"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-ec2)","(aws-ec2)","(ec2)","(vpc)"],"labels":["@aws-cdk/aws-ec2"],"assignees":["rix0rrr"]}, - {"keywords":["(@aws-cdk/aws-ecr)","(aws-ecr)","(ecr)"],"labels":["@aws-cdk/aws-ecr"],"assignees":["MrArnoldPalmer"]}, + {"keywords":["(@aws-cdk/aws-ecr)","(aws-ecr)","(ecr)"],"labels":["@aws-cdk/aws-ecr"],"assignees":["madeline-k"]}, {"keywords":["(@aws-cdk/aws-ecr-assets)","(aws-ecr-assets)","(ecr-assets)","(ecr assets)","(ecrassets)"],"labels":["@aws-cdk/aws-ecr-assets"],"assignees":["eladb"]}, - {"keywords":["(@aws-cdk/aws-efs)","(aws-efs)","(efs)"],"labels":["@aws-cdk/aws-efs"],"assignees":["iliapolo"]}, + {"keywords":["(@aws-cdk/aws-ecs)","(aws-ecs)","(ecs)"],"labels":["@aws-cdk/aws-ecs"],"assignees":["madeline-k"]}, + {"keywords":["(@aws-cdk/aws-ecs-patterns)","(aws-ecs-patterns)","(ecs-patterns)"],"labels":["@aws-cdk/aws-ecs-patterns"],"assignees":["madeline-k"]}, + {"keywords":["(@aws-cdk/aws-efs)","(aws-efs)","(efs)"],"labels":["@aws-cdk/aws-efs"],"assignees":["nija-at"]}, {"keywords":["(@aws-cdk/aws-eks)","(aws-eks)","(eks)"],"labels":["@aws-cdk/aws-eks"],"assignees":["iliapolo"]}, - {"keywords":["(@aws-cdk/aws-elasticache)","(aws-elasticache)","(elasticache)","(elastic cache)","(elastic-cache)"],"labels":["@aws-cdk/aws-elasticache"],"assignees":["iliapolo"]}, + {"keywords":["(@aws-cdk/aws-eks-legacy)","(aws-eks-legacy)","(eks-legacy)"],"labels":["@aws-cdk/aws-eks-legacy"],"assignees":["iliapolo"]}, + {"keywords":["(@aws-cdk/aws-elasticache)","(aws-elasticache)","(elasticache)","(elastic cache)","(elastic-cache)"],"labels":["@aws-cdk/aws-elasticache"],"assignees":["otaviomacedo"]}, {"keywords":["(@aws-cdk/aws-elasticbeanstalk)","(aws-elasticbeanstalk)","(elasticbeanstalk)","(elastic beanstalk)","(elastic-beanstalk)"],"labels":["@aws-cdk/aws-elasticbeanstalk"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-elasticloadbalancing)","(aws-elasticloadbalancing)","(elasticloadbalancing)","(elastic loadbalancing)","(elastic-loadbalancing)","(elb)"],"labels":["@aws-cdk/aws-elasticloadbalancing"],"assignees":["njlynch"]}, {"keywords":["(@aws-cdk/aws-elasticloadbalancingv2)","(aws-elasticloadbalancingv2)","(elasticloadbalancingv2)","(elastic-loadbalancing-v2)","(elbv2)","(elb v2)"],"labels":["@aws-cdk/aws-elasticloadbalancingv2"],"assignees":["njlynch"]}, + {"keywords":["(@aws-cdk/aws-elasticloadbalancingv2-actions)","(aws-elasticloadbalancingv2-actions)","(elasticloadbalancingv2-actions)"],"labels":["@aws-cdk/aws-elasticloadbalancingv2-actions"],"assignees":["njlynch"]}, {"keywords":["(@aws-cdk/aws-elasticloadbalancingv2-targets)","(aws-elasticloadbalancingv2-targets)","(elasticloadbalancingv2-targets)","(elasticloadbalancingv2 targets)","(elbv2 targets)"],"labels":["@aws-cdk/aws-elasticloadbalancingv2-targets"],"assignees":["njlynch"]}, - {"keywords":["(@aws-cdk/aws-elasticsearch)","(aws-elasticsearch)","(elasticsearch)","(elastic search)","(elastic-search)"],"labels":["@aws-cdk/aws-elasticsearch"],"assignees":["iliapolo"]}, - {"keywords":["(@aws-cdk/aws-emr)","(aws-emr)","(emr)"],"labels":["@aws-cdk/aws-emr"],"assignees":["iliapolo"]}, + {"keywords":["(@aws-cdk/aws-elasticsearch)","(aws-elasticsearch)","(elasticsearch)","(elastic search)","(elastic-search)"],"labels":["@aws-cdk/aws-elasticsearch"],"assignees":["BenChaimberg"]}, + {"keywords":["(@aws-cdk/aws-emr)","(aws-emr)","(emr)"],"labels":["@aws-cdk/aws-emr"],"assignees":["BenChaimberg"]}, + {"keywords":["(@aws-cdk/aws-emrcontainers)","(aws-emrcontainers)","(emrcontainers)"],"labels":["@aws-cdk/aws-emrcontainers"],"assignees":["BenChaimberg"]}, {"keywords":["(@aws-cdk/aws-events)","(aws-events)","(events)","(eventbridge)","(event-bridge)"],"labels":["@aws-cdk/aws-events"],"assignees":["rix0rrr"]}, {"keywords":["(@aws-cdk/aws-events-targets)","(aws-events-targets)","(events-targets)","(events targets)"],"labels":["@aws-cdk/aws-events-targets"],"assignees":["rix0rrr"]}, {"keywords":["(@aws-cdk/aws-eventschemas)","(aws-eventschemas)","(eventschemas)","(event schemas)"],"labels":["@aws-cdk/aws-eventschemas"],"assignees":["skinny85"]}, + {"keywords":["(@aws-cdk/aws-finspace)","(aws-finspace)","(finspace)"],"labels":["@aws-cdk/aws-finspace"],"assignees":["njlynch"]}, + {"keywords":["(@aws-cdk/aws-fis)","(aws-fis)","(fis)"],"labels":["@aws-cdk/aws-fis"],"assignees":["njlynch"]}, {"keywords":["(@aws-cdk/aws-fms)","(aws-fms)","(fms)"],"labels":["@aws-cdk/aws-fms"],"assignees":["rix0rrr"]}, + {"keywords":["(@aws-cdk/aws-frauddetector)","(aws-frauddetector)","(frauddetector)"],"labels":["@aws-cdk/aws-frauddetector"],"assignees":["njlynch"]}, {"keywords":["(@aws-cdk/aws-fsx)","(aws-fsx)","(fsx)"],"labels":["@aws-cdk/aws-fsx"],"assignees":["rix0rrr"]}, - {"keywords":["(@aws-cdk/aws-gamelift)","(aws-gamelift)","(gamelift)","(game lift)"],"labels":["@aws-cdk/aws-gamelift"],"assignees":["MrArnoldPalmer"]}, + {"keywords":["(@aws-cdk/aws-gamelift)","(aws-gamelift)","(gamelift)","(game lift)"],"labels":["@aws-cdk/aws-gamelift"],"assignees":["madeline-k"]}, {"keywords":["(@aws-cdk/aws-globalaccelerator)","(aws-globalaccelerator)","(globalaccelerator)","(global accelerator)","(global-accelerator)"],"labels":["@aws-cdk/aws-globalaccelerator"],"assignees":["rix0rrr"]}, - {"keywords":["(@aws-cdk/aws-glue)","(aws-glue)","(glue)"],"labels":["@aws-cdk/aws-glue"],"assignees":["iliapolo"]}, + {"keywords":["(@aws-cdk/aws-globalaccelerator-endpoints)","(aws-globalaccelerator-endpoints)","(globalaccelerator-endpoints)"],"labels":["@aws-cdk/aws-globalaccelerator-endpoints"],"assignees":["rix0rrr"]}, + {"keywords":["(@aws-cdk/aws-glue)","(aws-glue)","(glue)"],"labels":["@aws-cdk/aws-glue"],"assignees":["BenChaimberg"]}, {"keywords":["(@aws-cdk/aws-greengrass)","(aws-greengrass)","(greengrass)","(green grass)","(green-grass)"],"labels":["@aws-cdk/aws-greengrass"],"assignees":["skinny85"]}, + {"keywords":["(@aws-cdk/aws-greengrassv2)","(aws-greengrassv2)","(greengrassv2)"],"labels":["@aws-cdk/aws-greengrassv2"],"assignees":["skinny85"]}, + {"keywords":["(@aws-cdk/aws-groundstation)","(aws-groundstation)","(groundstation)"],"labels":["@aws-cdk/aws-groundstation"],"assignees":["rix0rrr"]}, {"keywords":["(@aws-cdk/aws-guardduty)","(aws-guardduty)","(guardduty)","(guard duty)","(guard-duty)"],"labels":["@aws-cdk/aws-guardduty"],"assignees":["rix0rrr"]}, {"keywords":["(@aws-cdk/aws-iam)","(aws-iam)","(iam)"],"labels":["@aws-cdk/aws-iam"],"assignees":["rix0rrr"]}, {"keywords":["(@aws-cdk/aws-imagebuilder)","(aws-imagebuilder)","(imagebuilder)","(image builder)","(image-builder)"],"labels":["@aws-cdk/aws-imagebuilder"],"assignees":["skinny85"]}, @@ -109,82 +127,102 @@ jobs: {"keywords":["(@aws-cdk/aws-iot1click)","(aws-iot1click)","(iot1click)","(iot 1click)"],"labels":["@aws-cdk/aws-iot1click"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-iotanalytics)","(aws-iotanalytics)","(iotanalytics)","(iot analytics)","(iot-analytics)"],"labels":["@aws-cdk/aws-iotanalytics"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-iotevents)","(aws-iotevents)","(iotevents)","(iot events)","(iot-events)"],"labels":["@aws-cdk/aws-iotevents"],"assignees":["skinny85"]}, + {"keywords":["(@aws-cdk/aws-iotfleethub)","(aws-iotfleethub)","(iotfleethub)"],"labels":["@aws-cdk/aws-iotfleethub"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-iotsitewise)","(aws-iotsitewise)","(iotsitewise)","(iot sitewise)","(iot-sitewise)","(iot-site-wise)","(iot site wise)"],"labels":["@aws-cdk/aws-iotsitewise"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-iotthingsgraph)","(aws-iotthingsgraph)","(iotthingsgraph)","(iot things graph)","(iot-things-graph)"],"labels":["@aws-cdk/aws-iotthingsgraph"],"assignees":["skinny85"]}, + {"keywords":["(@aws-cdk/aws-iotwireless)","(aws-iotwireless)","(iotwireless)"],"labels":["@aws-cdk/aws-iotwireless"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-ivs)","(aws-ivs)","(Interactive Video Service)","(ivs)"],"labels":["@aws-cdk/aws-ivs"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-kendra)","(aws-kendra)","(kendra)"],"labels":["@aws-cdk/aws-kendra"],"assignees":["skinny85"]}, - {"keywords":["(@aws-cdk/aws-kinesis)","(aws-kinesis)","(kinesis)"],"labels":["@aws-cdk/aws-kinesis"],"assignees":["iliapolo"]}, - {"keywords":["(@aws-cdk/aws-kinesisanalytics)","(aws-kinesisanalytics)","(kinesisanalytics)","(kinesis analytics)","(kinesis-analytics)"],"labels":["@aws-cdk/aws-kinesisanalytics"],"assignees":["iliapolo"]}, - {"keywords":["(@aws-cdk/aws-kinesisfirehose)","(aws-kinesisfirehose)","(kinesisfirehose)","(kinesis firehose)","(kinesis-firehose)"],"labels":["@aws-cdk/aws-kinesisfirehose"],"assignees":["iliapolo"]}, + {"keywords":["(@aws-cdk/aws-kinesis)","(aws-kinesis)","(kinesis)"],"labels":["@aws-cdk/aws-kinesis"],"assignees":["otaviomacedo"]}, + {"keywords":["(@aws-cdk/aws-kinesisanalytics)","(aws-kinesisanalytics)","(kinesisanalytics)","(kinesis analytics)","(kinesis-analytics)"],"labels":["@aws-cdk/aws-kinesisanalytics"],"assignees":["otaviomacedo"]}, + {"keywords":["(@aws-cdk/aws-kinesisanalytics-flink)","(aws-kinesisanalytics-flink)","(kinesisanalytics-flink)"],"labels":["@aws-cdk/aws-kinesisanalytics-flink"],"assignees":["otaviomacedo"]}, + {"keywords":["(@aws-cdk/aws-kinesisfirehose)","(aws-kinesisfirehose)","(kinesisfirehose)","(kinesis firehose)","(kinesis-firehose)"],"labels":["@aws-cdk/aws-kinesisfirehose"],"assignees":["otaviomacedo"]}, {"keywords":["(@aws-cdk/aws-kms)","(aws-kms)","(kms)"],"labels":["@aws-cdk/aws-kms"],"assignees":["njlynch"]}, - {"keywords":["(@aws-cdk/aws-lakeformation)","(aws-lakeformation)","(lakeformation)","(lake formation)","(lake-formation)"],"labels":["@aws-cdk/aws-lakeformation"],"assignees":["iliapolo"]}, + {"keywords":["(@aws-cdk/aws-lakeformation)","(aws-lakeformation)","(lakeformation)","(lake formation)","(lake-formation)"],"labels":["@aws-cdk/aws-lakeformation"],"assignees":["BenChaimberg"]}, {"keywords":["(@aws-cdk/aws-lambda)","(aws-lambda)","(lambda)"],"labels":["@aws-cdk/aws-lambda"],"assignees":["nija-at"]}, + {"keywords":["(@aws-cdk/aws-lambda-destinations)","(aws-lambda-destinations)","(lambda-destinations)"],"labels":["@aws-cdk/aws-lambda-destinations"],"assignees":["nija-at"]}, {"keywords":["(@aws-cdk/aws-lambda-event-sources)","(aws-lambda-event-sources)","(lambda-event-sources)","(lambda event sources)"],"labels":["@aws-cdk/aws-lambda-event-sources"],"assignees":["nija-at"]}, - {"keywords":["(@aws-cdk/aws-lambda-nodejs)","(aws-lambda-nodejs)","(lambda-nodejs)","(lambda nodejs)"],"labels":["@aws-cdk/aws-lambda-nodejs"],"assignees":["eladb"]}, - {"keywords":["(@aws-cdk/aws-lambda-python)","(aws-lambda-python)","(lambda-python)","(lambda python)"],"labels":["@aws-cdk/aws-lambda-python"],"assignees":["eladb"]}, + {"keywords":["(@aws-cdk/aws-lambda-go)","(aws-lambda-go)","(lambda-go)"],"labels":["@aws-cdk/aws-lambda-go"],"assignees":["nija-at"]}, + {"keywords":["(@aws-cdk/aws-lambda-nodejs)","(aws-lambda-nodejs)","(lambda-nodejs)","(lambda nodejs)"],"labels":["@aws-cdk/aws-lambda-nodejs"],"assignees":["nija-at"]}, + {"keywords":["(@aws-cdk/aws-lambda-python)","(aws-lambda-python)","(lambda-python)","(lambda python)"],"labels":["@aws-cdk/aws-lambda-python"],"assignees":["nija-at"]}, + {"keywords":["(@aws-cdk/aws-licensemanager)","(aws-licensemanager)","(licensemanager)"],"labels":["@aws-cdk/aws-licensemanager"],"assignees":["njlynch"]}, {"keywords":["(@aws-cdk/aws-logs)","(aws-logs)","(logs)"],"labels":["@aws-cdk/aws-logs"],"assignees":["rix0rrr"]}, {"keywords":["(@aws-cdk/aws-logs-destinations)","(aws-logs-destinations)","(logs-destinations)","(logs destinations)"],"labels":["@aws-cdk/aws-logs-destinations"],"assignees":["rix0rrr"]}, + {"keywords":["(@aws-cdk/aws-lookoutmetrics)","(aws-lookoutmetrics)","(lookoutmetrics)"],"labels":["@aws-cdk/aws-lookoutmetrics"],"assignees":["BenChaimberg"]}, + {"keywords":["(@aws-cdk/aws-lookoutvision)","(aws-lookoutvision)","(lookoutvision)"],"labels":["@aws-cdk/aws-lookoutvision"],"assignees":["BenChaimberg"]}, {"keywords":["(@aws-cdk/aws-macie)","(aws-macie)","(macie)"],"labels":["@aws-cdk/aws-macie"],"assignees":["njlynch"]}, {"keywords":["(@aws-cdk/aws-managedblockchain)","(aws-managedblockchain)","(managedblockchain)","(managed blockchain)","(managed-blockchain)"],"labels":["@aws-cdk/aws-managedblockchain"],"assignees":["skinny85"]}, + {"keywords":["(@aws-cdk/aws-mediaconnect)","(aws-mediaconnect)","(mediaconnect)"],"labels":["@aws-cdk/aws-mediaconnect"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-mediaconvert)","(aws-mediaconvert)","(mediaconvert)","(media convert)","(media-convert)"],"labels":["@aws-cdk/aws-mediaconvert"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-medialive)","(aws-medialive)","(medialive)","(media live)","(media-live)"],"labels":["@aws-cdk/aws-medialive"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-mediastore)","(aws-mediastore)","(mediastore)","(media store)","(media-store)"],"labels":["@aws-cdk/aws-mediastore"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-mediapackage)","(aws-mediapackage)","(mediapackage)","(media package)","(media-package)"],"labels":["@aws-cdk/aws-mediapackage"],"assignees":["skinny85"]}, - {"keywords":["(@aws-cdk/aws-msk)","(aws-msk)","(msk)"],"labels":["@aws-cdk/aws-msk"],"assignees":["iliapolo"]}, + {"keywords":["(@aws-cdk/aws-msk)","(aws-msk)","(msk)"],"labels":["@aws-cdk/aws-msk"],"assignees":["otaviomacedo"]}, + {"keywords":["(@aws-cdk/aws-mwaa)","(aws-mwaa)","(mwaa)"],"labels":["@aws-cdk/aws-mwaa"],"assignees":["rix0rrr"]}, {"keywords":["(@aws-cdk/aws-neptune)","(aws-neptune)","(neptune)"],"labels":["@aws-cdk/aws-neptune"],"assignees":["njlynch"]}, + {"keywords":["(@aws-cdk/aws-networkfirewall)","(aws-networkfirewall)","(networkfirewall)"],"labels":["@aws-cdk/aws-networkfirewall"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-networkmanager)","(aws-networkmanager)","(networkmanager)","(network manager)","(network-manager)"],"labels":["@aws-cdk/aws-networkmanager"],"assignees":["skinny85"]}, - {"keywords":["(@aws-cdk/aws-opsworks)","(aws-opsworks)","(opsworks)","(ops works)","(ops-works)"],"labels":["@aws-cdk/aws-opsworks"],"assignees":["MrArnoldPalmer"]}, - {"keywords":["(@aws-cdk/aws-opsworkscm)","(aws-opsworkscm)","(opsworkscm)","(opsworks cm)","(opsworks-cm)"],"labels":["@aws-cdk/aws-opsworkscm"],"assignees":["MrArnoldPalmer"]}, + {"keywords":["(@aws-cdk/aws-nimblestudio)","(aws-nimblestudio)","(nimblestudio)"],"labels":["@aws-cdk/aws-nimblestudio"],"assignees":["madeline-k"]}, + {"keywords":["(@aws-cdk/aws-opsworks)","(aws-opsworks)","(opsworks)","(ops works)","(ops-works)"],"labels":["@aws-cdk/aws-opsworks"],"assignees":["madeline-k"]}, + {"keywords":["(@aws-cdk/aws-opsworkscm)","(aws-opsworkscm)","(opsworkscm)","(opsworks cm)","(opsworks-cm)"],"labels":["@aws-cdk/aws-opsworkscm"],"assignees":["madeline-k"]}, {"keywords":["(@aws-cdk/aws-personalize)","(aws-personalize)","(personalize)"],"labels":["@aws-cdk/aws-personalize"],"assignees":["njlynch"]}, - {"keywords":["(@aws-cdk/aws-pinpoint)","(aws-pinpoint)","(pinpoint)"],"labels":["@aws-cdk/aws-pinpoint"],"assignees":["iliapolo"]}, - {"keywords":["(@aws-cdk/aws-pinpointemail)","(aws-pinpointemail)","(pinpointemail)","(pinpoint email)","(pinpoint-email)"],"labels":["@aws-cdk/aws-pinpointemail"],"assignees":["iliapolo"]}, + {"keywords":["(@aws-cdk/aws-pinpoint)","(aws-pinpoint)","(pinpoint)"],"labels":["@aws-cdk/aws-pinpoint"],"assignees":["otaviomacedo"]}, + {"keywords":["(@aws-cdk/aws-pinpointemail)","(aws-pinpointemail)","(pinpointemail)","(pinpoint email)","(pinpoint-email)"],"labels":["@aws-cdk/aws-pinpointemail"],"assignees":["otaviomacedo"]}, {"keywords":["(@aws-cdk/aws-qldb)","(aws-qldb)","(qldb)"],"labels":["@aws-cdk/aws-qldb"],"assignees":["skinny85"]}, - {"keywords":["(@aws-cdk/aws-ram)","(aws-ram)","(ram)"],"labels":["@aws-cdk/aws-ram"],"assignees":["MrArnoldPalmer"]}, + {"keywords":["(@aws-cdk/aws-quicksight)","(aws-quicksight)","(quicksight)"],"labels":["@aws-cdk/aws-quicksight"],"assignees":["BenChaimberg"]}, + {"keywords":["(@aws-cdk/aws-ram)","(aws-ram)","(ram)"],"labels":["@aws-cdk/aws-ram"],"assignees":["madeline-k"]}, {"keywords":["(@aws-cdk/aws-rds)","(aws-rds)","(rds)"],"labels":["@aws-cdk/aws-rds"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-redshift)","(aws-redshift)","(redshift)","(red shift)","(red-shift)"],"labels":["@aws-cdk/aws-redshift"],"assignees":["njlynch"]}, - {"keywords":["(@aws-cdk/aws-resourcegroups)","(aws-resourcegroups)","(resourcegroups)","(resource groups)","(resource-groups)"],"labels":["@aws-cdk/aws-resourcegroups"],"assignees":["MrArnoldPalmer"]}, + {"keywords":["(@aws-cdk/aws-resourcegroups)","(aws-resourcegroups)","(resourcegroups)","(resource groups)","(resource-groups)"],"labels":["@aws-cdk/aws-resourcegroups"],"assignees":["madeline-k"]}, {"keywords":["(@aws-cdk/aws-robomaker)","(aws-robomaker)","(robomaker)","(robo maker)","(robo-maker)"],"labels":["@aws-cdk/aws-robomaker"],"assignees":["njlynch"]}, {"keywords":["(@aws-cdk/aws-route53)","(aws-route53)","(route53)","(route 53)","(route-53)"],"labels":["@aws-cdk/aws-route53"],"assignees":["njlynch"]}, {"keywords":["(@aws-cdk/aws-route53-patterns)","(aws-route53-patterns)","(route53-patterns)","(route53 patterns)","(route-53-patterns)"],"labels":["@aws-cdk/aws-route53-patterns"],"assignees":["njlynch"]}, {"keywords":["(@aws-cdk/aws-route53-targets)","(aws-route53-targets)","(route53-targets)","(route53 targets)","(route-53-targets)"],"labels":["@aws-cdk/aws-route53-targets"],"assignees":["njlynch"]}, {"keywords":["(@aws-cdk/aws-route53resolver)","(aws-route53resolver)","(route53resolver)","(route53 resolver)","(route-53-resolver)"],"labels":["@aws-cdk/aws-route53resolver"],"assignees":["njlynch"]}, - {"keywords":["(@aws-cdk/aws-s3)","(aws-s3)","(s3)"],"labels":["@aws-cdk/aws-s3"],"assignees":["iliapolo"]}, - {"keywords":["(@aws-cdk/aws-s3-assets)","(aws-s3-assets)","(s3-assets)","(s3 assets)"],"labels":["@aws-cdk/aws-s3-assets"],"assignees":["iliapolo"]}, - {"keywords":["(@aws-cdk/aws-s3-deployment)","(aws-s3-deployment)","(s3-deployment)","(s3 deployment)"],"labels":["@aws-cdk/aws-s3-deployment"],"assignees":["iliapolo"]}, - {"keywords":["(@aws-cdk/aws-s3-notifications)","(aws-s3-notifications)","(s3-notifications)","(s3 notifications)"],"labels":["@aws-cdk/aws-s3-notifications"],"assignees":["iliapolo"]}, + {"keywords":["(@aws-cdk/aws-s3)","(aws-s3)","(s3)"],"labels":["@aws-cdk/aws-s3"],"assignees":["otaviomacedo"]}, + {"keywords":["(@aws-cdk/aws-s3-assets)","(aws-s3-assets)","(s3-assets)","(s3 assets)"],"labels":["@aws-cdk/aws-s3-assets"],"assignees":["otaviomacedo"]}, + {"keywords":["(@aws-cdk/aws-s3-deployment)","(aws-s3-deployment)","(s3-deployment)","(s3 deployment)"],"labels":["@aws-cdk/aws-s3-deployment"],"assignees":["otaviomacedo"]}, + {"keywords":["(@aws-cdk/aws-s3-notifications)","(aws-s3-notifications)","(s3-notifications)","(s3 notifications)"],"labels":["@aws-cdk/aws-s3-notifications"],"assignees":["otaviomacedo"]}, + {"keywords":["(@aws-cdk/aws-s3objectlambda)","(aws-s3objectlambda)","(s3objectlambda)"],"labels":["@aws-cdk/aws-s3objectlambda"],"assignees":["nija-at"]}, + {"keywords":["(@aws-cdk/aws-s3outposts)","(aws-s3outposts)","(s3outposts)"],"labels":["@aws-cdk/aws-s3outposts"],"assignees":["otaviomacedo"]}, {"keywords":["(@aws-cdk/aws-sagemaker)","(aws-sagemaker)","(sagemaker)","(sage maker)","(sage-maker)"],"labels":["@aws-cdk/aws-sagemaker"],"assignees":["njlynch"]}, {"keywords":["(@aws-cdk/aws-sam)","(aws-sam)","(sam)"],"labels":["@aws-cdk/aws-sam"],"assignees":["njlynch"]}, {"keywords":["(@aws-cdk/aws-sdb)","(aws-sdb)","(sdb)"],"labels":["@aws-cdk/aws-sdb"],"assignees":["njlynch"]}, {"keywords":["(@aws-cdk/aws-secretsmanager)","(aws-secretsmanager)","(secretsmanager)","(secrets manager)","(secrets-manager)"],"labels":["@aws-cdk/aws-secretsmanager"],"assignees":["njlynch"]}, {"keywords":["(@aws-cdk/aws-securityhub)","(aws-securityhub)","(securityhub)","(security hub)","(security-hub)"],"labels":["@aws-cdk/aws-securityhub"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-servicecatalog)","(aws-servicecatalog)","(servicecatalog)","(service catalog)","(service-catalog)"],"labels":["@aws-cdk/aws-servicecatalog"],"assignees":["skinny85"]}, - {"keywords":["(@aws-cdk/aws-servicediscovery)","(aws-servicediscovery)","(servicediscovery)","(service discovery)","(service-discovery)"],"labels":["@aws-cdk/aws-servicediscovery"],"assignees":["MrArnoldPalmer"]}, - {"keywords":["(@aws-cdk/aws-ses)","(aws-ses)","(ses)"],"labels":["@aws-cdk/aws-ses"],"assignees":["iliapolo"]}, - {"keywords":["(@aws-cdk/aws-ses-actions)","(aws-ses-actions)","(ses-actions)","(ses actions)"],"labels":["@aws-cdk/aws-ses-actions"],"assignees":["iliapolo"]}, + {"keywords":["(@aws-cdk/aws-servicecatalogappregistry)","(aws-servicecatalogappregistry)","(servicecatalogappregistry)"],"labels":["@aws-cdk/aws-servicecatalogappregistry"],"assignees":["skinny85"]}, + {"keywords":["(@aws-cdk/aws-servicediscovery)","(aws-servicediscovery)","(servicediscovery)","(service discovery)","(service-discovery)"],"labels":["@aws-cdk/aws-servicediscovery"],"assignees":["madeline-k"]}, + {"keywords":["(@aws-cdk/aws-ses)","(aws-ses)","(ses)"],"labels":["@aws-cdk/aws-ses"],"assignees":["otaviomacedo"]}, + {"keywords":["(@aws-cdk/aws-ses-actions)","(aws-ses-actions)","(ses-actions)","(ses actions)"],"labels":["@aws-cdk/aws-ses-actions"],"assignees":["otaviomacedo"]}, {"keywords":["(@aws-cdk/aws-signer)","(aws-signer)","(signer)"],"labels":["@aws-cdk/aws-signer"],"assignees":["nija-at"]}, - {"keywords":["(@aws-cdk/aws-sns)","(aws-sns)","(sns)"],"labels":["@aws-cdk/aws-sns"],"assignees":["MrArnoldPalmer"]}, - {"keywords":["(@aws-cdk/aws-sns-subscriptions)","(aws-sns-subscriptions)","(sns-subscriptions)","(sns subscriptions)"],"labels":["@aws-cdk/aws-sns-subscriptions"],"assignees":["MrArnoldPalmer"]}, - {"keywords":["(@aws-cdk/aws-sqs)","(aws-sqs)","(sqs)"],"labels":["@aws-cdk/aws-sqs"],"assignees":["MrArnoldPalmer"]}, - {"keywords":["(@aws-cdk/aws-ssm)","(aws-ssm)","(ssm)"],"labels":["@aws-cdk/aws-ssm"],"assignees":["MrArnoldPalmer"]}, + {"keywords":["(@aws-cdk/aws-sns)","(aws-sns)","(sns)"],"labels":["@aws-cdk/aws-sns"],"assignees":["njlynch"]}, + {"keywords":["(@aws-cdk/aws-sns-subscriptions)","(aws-sns-subscriptions)","(sns-subscriptions)","(sns subscriptions)"],"labels":["@aws-cdk/aws-sns-subscriptions"],"assignees":["njlynch"]}, + {"keywords":["(@aws-cdk/aws-sqs)","(aws-sqs)","(sqs)"],"labels":["@aws-cdk/aws-sqs"],"assignees":["njlynch"]}, + {"keywords":["(@aws-cdk/aws-ssm)","(aws-ssm)","(ssm)"],"labels":["@aws-cdk/aws-ssm"],"assignees":["njlynch"]}, {"keywords":["(@aws-cdk/aws-sso)","(aws-sso)","(sso)"],"labels":["@aws-cdk/aws-sso"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-stepfunctions)","(aws-stepfunctions)","(stepfunctions)","(step functions)","(step-functions)"],"labels":["@aws-cdk/aws-stepfunctions"],"assignees":["shivlaks"]}, {"keywords":["(@aws-cdk/aws-stepfunctions-tasks)","(aws-stepfunctions-tasks)","(stepfunctions-tasks)","(stepfunctions tasks)"],"labels":["@aws-cdk/aws-stepfunctions-tasks"],"assignees":["shivlaks"]}, - {"keywords":["(@aws-cdk/aws-synthetics)","(aws-synthetics)","(synthetics)"],"labels":["@aws-cdk/aws-synthetics"],"assignees":["NetaNir"]}, + {"keywords":["(@aws-cdk/aws-synthetics)","(aws-synthetics)","(synthetics)"],"labels":["@aws-cdk/aws-synthetics"],"assignees":["BenChaimberg"]}, {"keywords":["(@aws-cdk/aws-timestream)","(aws-timestream)","(timestream)"],"labels":["@aws-cdk/aws-timestream"],"assignees":["skinny85"]}, - {"keywords":["(@aws-cdk/aws-transfer)","(aws-transfer)","(transfer)"],"labels":["@aws-cdk/aws-transfer"],"assignees":["iliapolo"]}, + {"keywords":["(@aws-cdk/aws-transfer)","(aws-transfer)","(transfer)"],"labels":["@aws-cdk/aws-transfer"],"assignees":["otaviomacedo"]}, {"keywords":["(@aws-cdk/aws-waf)","(aws-waf)","(waf)"],"labels":["@aws-cdk/aws-waf"],"assignees":["njlynch"]}, {"keywords":["(@aws-cdk/aws-wafregional)","(aws-wafregional)","(wafregional)","(waf regional)","(waf-regional)"],"labels":["@aws-cdk/aws-wafregional"],"assignees":["njlynch"]}, {"keywords":["(@aws-cdk/aws-wafv2)","(aws-wafv2)","(wafv2)","(waf v2)","(waf-v2)"],"labels":["@aws-cdk/aws-wafv2"],"assignees":["njlynch"]}, - {"keywords":["(@aws-cdk/aws-workspaces)","(aws-workspaces)","(workspaces)"],"labels":["@aws-cdk/aws-workspaces"],"assignees":["NetaNir"]}, + {"keywords":["(@aws-cdk/aws-workspaces)","(aws-workspaces)","(workspaces)"],"labels":["@aws-cdk/aws-workspaces"],"assignees":["madeline-k"]}, + {"keywords":["(@aws-cdk/aws-xray)","(aws-xray)","(xray)"],"labels":["@aws-cdk/aws-xray"],"assignees":["nija-at"]}, {"keywords":["(@aws-cdk/cfnspec)","(cfnspec)","(cfn spec)","(cfn-spec)"],"labels":["@aws-cdk/cfnspec"],"assignees":["rix0rrr"]}, {"keywords":["(@aws-cdk/cloud-assembly-schema)","(cloud-assembly-schema)","(cloud assembly schema)"],"labels":["@aws-cdk/cloud-assembly-schema"],"assignees":["rix0rrr"]}, - {"keywords":["(@aws-cdk/cloudformation-diff)","(cloudformation-diff)","(cloudformation diff)","(cfn diff)"],"labels":["@aws-cdk/cloudformation-diff"],"assignees":["NetaNir"]}, + {"keywords":["(@aws-cdk/cloudformation-diff)","(cloudformation-diff)","(cloudformation diff)","(cfn diff)"],"labels":["@aws-cdk/cloudformation-diff"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/cloudformation-include)","(cloudformation-include)","(cloudformation include)","(cfn include)","(cfn-include)"],"labels":["@aws-cdk/cloudformation-include"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/core)","(core)"],"labels":["@aws-cdk/core"],"assignees":["rix0rrr"]}, {"keywords":["(@aws-cdk/custom-resources)","(custom-resources)","(custom resources)"],"labels":["@aws-cdk/custom-resources"],"assignees":["rix0rrr"]}, {"keywords":["(@aws-cdk/cx-api)","(cx-api)","(cx api)"],"labels":["@aws-cdk/cx-api"],"assignees":["rix0rrr"]}, + {"keywords":["(@aws-cdk/aws-lambda-layer-awscli)","(aws-lambda-layer-awscli)","(lambda-layer-awscli)"],"labels":["@aws-cdk/aws-lambda-layer-awscli"],"assignees":["rix0rrr"]}, + {"keywords":["(@aws-cdk/aws-lambda-layer-kubectl)","(aws-lambda-layer-kubectl)","(lambda-layer-kubectl)"],"labels":["@aws-cdk/aws-lambda-layer-kubectl"],"assignees":["eladb"]}, {"keywords":["(@aws-cdk/pipelines)","(pipelines)","(cdk pipelines)","(cdk-pipelines)"],"labels":["@aws-cdk/pipelines"],"assignees":["rix0rrr"]}, {"keywords":["(@aws-cdk/region-info)","(region-info)","(region info)"],"labels":["@aws-cdk/region-info"],"assignees":["skinny85"]}, {"keywords":["(aws-cdk-lib)","(cdk-v2)", "(v2)", "(ubergen)"],"labels":["aws-cdk-lib"],"assignees":["nija-at"]}, - {"keywords":["(monocdk)","(monocdk-experiment)"],"labels":["monocdk"],"assignees":["nija-at"]} + {"keywords":["(monocdk)","(monocdk-experiment)"],"labels":["monocdk"],"assignees":["nija-at"]}, + {"keywords":["(@aws-cdk/yaml-cfn)","(aws-yaml-cfn)","(yaml-cfn)"],"labels":["@aws-cdk/aws-yaml-cfn"],"assignees":["skinny85"]} ] From 52da59c34c4be74d696af0637521eeb0d6e69fa9 Mon Sep 17 00:00:00 2001 From: Jonathan Goldwasser Date: Sun, 30 May 2021 13:23:57 +0200 Subject: [PATCH 103/134] fix(lambda-nodejs): cannot bundle locally when consuming a node module with a NodejsFunction (#14914) #14739 changed how `esbuild` is run. It now uses the package manager. When consuming a node module with a `NodejsFunction` we need to run `esbuild` from the directory containing the lock file. This is where we have `node_modules\.bin`. If we run it from the directory containing the entry file the package manager doesn't "see" `node_modules\.bin`. ---- *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/lib/bundling.ts | 2 +- packages/@aws-cdk/aws-lambda-nodejs/test/bundling.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda-nodejs/lib/bundling.ts b/packages/@aws-cdk/aws-lambda-nodejs/lib/bundling.ts index 240114fbfa43d..14020859b296d 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/lib/bundling.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/lib/bundling.ts @@ -174,7 +174,7 @@ export class Bundling implements cdk.BundlingOptions { osPlatform, }); const environment = this.props.environment ?? {}; - const cwd = path.dirname(this.props.entry); + const cwd = path.dirname(this.props.depsLockFilePath); return { tryBundle(outputDir: string) { diff --git a/packages/@aws-cdk/aws-lambda-nodejs/test/bundling.test.ts b/packages/@aws-cdk/aws-lambda-nodejs/test/bundling.test.ts index 5a2549e8fbfc6..45556403d8398 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/test/bundling.test.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/test/bundling.test.ts @@ -299,7 +299,7 @@ test('Local bundling', () => { expect.arrayContaining(['-c', expect.stringContaining(entry)]), expect.objectContaining({ env: expect.objectContaining({ KEY: 'value' }), - cwd: '/project/lib', + cwd: '/project', }), ); From a7a9c9848410a00d03781cca392fe3c2cc8283b3 Mon Sep 17 00:00:00 2001 From: Jonathan Goldwasser Date: Mon, 31 May 2021 13:32:15 +0200 Subject: [PATCH 104/134] docs(lambda-nodejs): reference architecture (#14924) Closes #14823 ---- *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/README.md | 63 +++++++++++++++---- 1 file changed, 52 insertions(+), 11 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda-nodejs/README.md b/packages/@aws-cdk/aws-lambda-nodejs/README.md index 359f914da040a..b792f125c2c10 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/README.md +++ b/packages/@aws-cdk/aws-lambda-nodejs/README.md @@ -13,33 +13,74 @@ This library provides constructs for Node.js Lambda functions. ## Node.js Function -Define a `NodejsFunction`: +The `NodejsFunction` construct creates a Lambda function with automatic transpiling and bundling +of TypeScript or Javascript code. This results in smaller Lambda packages that contain only the +code and dependencies needed to run the function. -```ts -new lambda.NodejsFunction(this, 'my-handler'); -``` +It uses [esbuild](https://esbuild.github.io/) under the hood. -By default, the construct will use the name of the defining file and the construct's id to look -up the entry file: +## Reference project architecture + +The `NodejsFunction` allows you to define your CDK and runtime dependencies in a single +package.json and to collocate your runtime code with your infrastructure code: ```plaintext . -├── stack.ts # defines a 'NodejsFunction' with 'my-handler' as id -├── stack.my-handler.ts # exports a function named 'handler' +├── lib +│   ├── my-construct.api.ts # Lambda handler for API +│   ├── my-construct.auth.ts # Lambda handler for Auth +│   └── my-construct.ts # CDK construct with two Lambda functions +├── package-lock.json # single lock file +├── package.json # CDK and runtime dependencies defined in a single package.json +└── tsconfig.json ``` -This file is used as "entry" for [esbuild](https://esbuild.github.io/). This means that your code is automatically transpiled and bundled whether it's written in JavaScript or TypeScript. +By default, the construct will use the name of the defining file and the construct's +id to look up the entry file. In `my-construct.ts` above we have: + +```ts +// automatic entry look up +const apiHandler = new lambda.NodejsFunction(this, 'api'); +const authHandler = new lambda.NodejsFunction(this, 'auth'); +``` Alternatively, an entry file and handler can be specified: ```ts new lambda.NodejsFunction(this, 'MyFunction', { entry: '/path/to/my/file.ts', // accepts .js, .jsx, .ts and .tsx files - handler: 'myExportedFunc' + handler: 'myExportedFunc', // defaults to 'handler' }); ``` -All other properties of `lambda.Function` are supported, see also the [AWS Lambda construct library](https://github.com/aws/aws-cdk/tree/master/packages/%40aws-cdk/aws-lambda). +For monorepos, the reference architecture becomes: + +```plaintext +. +├── packages +│   ├── cool-package +│   │   ├── lib +│   │   │   ├── cool-construct.api.ts +│   │   │   ├── cool-construct.auth.ts +│   │   │   └── cool-construct.ts +│   │   ├── package.json # CDK and runtime dependencies for cool-package +│   │   └── tsconfig.json +│   └── super-package +│   ├── lib +│   │   ├── super-construct.handler.ts +│   │   └── super-construct.ts +│   ├── package.json # CDK and runtime dependencies for super-package +│   └── tsconfig.json +├── package-lock.json # single lock file +├── package.json # root dependencies +└── tsconfig.json +``` + +## Customizing the underlying Lambda function + +All properties of `lambda.Function` can be used to customize the underlying `lambda.Function`. + +See also the [AWS Lambda construct library](https://github.com/aws/aws-cdk/tree/master/packages/%40aws-cdk/aws-lambda). The `NodejsFunction` construct automatically [reuses existing connections](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/node-reusing-connections.html) when working with the AWS SDK for JavaScript. Set the `awsSdkConnectionReuse` prop to `false` to disable it. From ed72ad322a2739709cad91759ea18e159f28f795 Mon Sep 17 00:00:00 2001 From: Thorsten Hoeger Date: Mon, 31 May 2021 16:14:12 +0200 Subject: [PATCH 105/134] feat(pipelines): allow switching to one CodeBuild action for same-typed assets (#13803) This PR refactors the Asset Stage of the CDK pipeline so that only one FileAsset and one DockerAsset Action are created that are uploading all assets of the respective type. The main benefit of this is improved performance as uploading in parallel does not save enough time to compensate slower startups of multiple CodeBuilds. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/pipelines/README.md | 4 + packages/@aws-cdk/pipelines/lib/pipeline.ts | 34 +- ...ne-with-assets-single-upload.expected.json | 1461 +++++++++++++++++ ...nteg.pipeline-with-assets-single-upload.ts | 94 ++ .../pipelines/test/pipeline-assets.test.ts | 48 + 5 files changed, 1634 insertions(+), 7 deletions(-) create mode 100644 packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets-single-upload.expected.json create mode 100644 packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets-single-upload.ts diff --git a/packages/@aws-cdk/pipelines/README.md b/packages/@aws-cdk/pipelines/README.md index 8614825c53ad9..82aded1d69b4b 100644 --- a/packages/@aws-cdk/pipelines/README.md +++ b/packages/@aws-cdk/pipelines/README.md @@ -205,6 +205,10 @@ const cdkPipeline = new CdkPipeline(app, 'CdkPipeline', { }); ``` +If you use assets for files or Docker images, every asset will get its own upload action during the asset stage. +By setting the value `singlePublisherPerType` to `true`, only one action for files and one action for +Docker images is created that handles all assets of the respective type. + ## Initial pipeline deployment You provision this pipeline by making sure the target environment has been diff --git a/packages/@aws-cdk/pipelines/lib/pipeline.ts b/packages/@aws-cdk/pipelines/lib/pipeline.ts index 3d9fe0c2063d1..9f6d4fbee3ebd 100644 --- a/packages/@aws-cdk/pipelines/lib/pipeline.ts +++ b/packages/@aws-cdk/pipelines/lib/pipeline.ts @@ -121,6 +121,13 @@ export interface CdkPipelineProps { */ readonly selfMutating?: boolean; + /** + * Whether this pipeline creates one asset upload action per asset type or one asset upload per asset + * + * @default false + */ + readonly singlePublisherPerType?: boolean; + /** * Whether the pipeline needs to build Docker images in the UpdatePipeline stage. * @@ -229,6 +236,7 @@ export class CdkPipeline extends CoreConstruct { projectName: maybeSuffix(props.pipelineName, '-publish'), vpc: props.vpc, subnetSelection: props.subnetSelection, + singlePublisherPerType: props.singlePublisherPerType, }); } @@ -334,7 +342,7 @@ export class CdkPipeline extends CoreConstruct { return flatMap(this._pipeline.stages, s => s.actions.filter(isDeployAction)); } - private* validateDeployOrder(): IterableIterator { + private * validateDeployOrder(): IterableIterator { const stackActions = this.stackActions; for (const stackAction of stackActions) { // For every dependency, it must be executed in an action before this one is prepared. @@ -352,7 +360,7 @@ export class CdkPipeline extends CoreConstruct { } } - private* validateRequestedOutputs(): IterableIterator { + private * validateRequestedOutputs(): IterableIterator { const artifactIds = this.stackActions.map(s => s.stackArtifactId); for (const artifactId of Object.keys(this._outputArtifacts)) { @@ -378,6 +386,7 @@ interface AssetPublishingProps { readonly projectName?: string; readonly vpc?: ec2.IVpc; readonly subnetSelection?: ec2.SubnetSelection; + readonly singlePublisherPerType?: boolean; } /** @@ -432,16 +441,25 @@ class AssetPublishing extends CoreConstruct { this.generateAssetRole(command.assetType); } - let action = this.publishers[command.assetId]; + const publisherKey = this.props.singlePublisherPerType ? command.assetType.toString() : command.assetId; + + let action = this.publishers[publisherKey]; if (!action) { // Dynamically create new stages as needed, with `MAX_PUBLISHERS_PER_STAGE` assets per stage. - const stageIndex = Math.floor((this._fileAssetCtr + this._dockerAssetCtr) / this.MAX_PUBLISHERS_PER_STAGE); - if (stageIndex >= this.stages.length) { + const stageIndex = this.props.singlePublisherPerType ? 0 : + Math.floor((this._fileAssetCtr + this._dockerAssetCtr) / this.MAX_PUBLISHERS_PER_STAGE); + + if (!this.props.singlePublisherPerType && stageIndex >= this.stages.length) { const previousStage = this.stages.slice(-1)[0] ?? this.lastStageBeforePublishing; this.stages.push(this.pipeline.addStage({ stageName: `Assets${stageIndex > 0 ? stageIndex + 1 : ''}`, placement: { justAfter: previousStage }, })); + } else if (this.props.singlePublisherPerType && this.stages.length == 0) { + this.stages.push(this.pipeline.addStage({ + stageName: 'Assets', + placement: { justAfter: this.lastStageBeforePublishing }, + })); } // The asset ID would be a logical candidate for the construct path and project names, but if the asset @@ -450,12 +468,14 @@ class AssetPublishing extends CoreConstruct { // // FIXME: The ultimate best solution is probably to generate a single Project per asset type // and reuse that for all assets. - const id = command.assetType === AssetType.FILE ? `FileAsset${++this._fileAssetCtr}` : `DockerAsset${++this._dockerAssetCtr}`; + const id = this.props.singlePublisherPerType ? + command.assetType === AssetType.FILE ? 'FileAsset' : 'DockerAsset' : + command.assetType === AssetType.FILE ? `FileAsset${++this._fileAssetCtr}` : `DockerAsset${++this._dockerAssetCtr}`; // NOTE: It's important that asset changes don't force a pipeline self-mutation. // This can cause an infinite loop of updates (see https://github.com/aws/aws-cdk/issues/9080). // For that reason, we use the id as the actionName below, rather than the asset hash. - action = this.publishers[command.assetId] = new PublishAssetsAction(this, id, { + action = this.publishers[publisherKey] = new PublishAssetsAction(this, id, { actionName: id, cloudAssemblyInput: this.props.cloudAssemblyInput, cdkCliVersion: this.props.cdkCliVersion, diff --git a/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets-single-upload.expected.json b/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets-single-upload.expected.json new file mode 100644 index 0000000000000..2bfe179472c52 --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets-single-upload.expected.json @@ -0,0 +1,1461 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store." + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 4 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + }, + "Resources": { + "PipelineArtifactsBucketEncryptionKeyF5BF0670": { + "Type": "AWS::KMS::Key", + "Properties": { + "KeyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::12345678:root" + ] + ] + } + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "PipelineArtifactsBucketAEA9A052": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "KMSMasterKeyID": { + "Fn::GetAtt": [ + "PipelineArtifactsBucketEncryptionKeyF5BF0670", + "Arn" + ] + }, + "SSEAlgorithm": "aws:kms" + } + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "PipelineArtifactsBucketEncryptionKeyAlias94A07392": { + "Type": "AWS::KMS::Alias", + "Properties": { + "AliasName": "alias/codepipeline-pipelinestackpipelinee95eedaa", + "TargetKeyId": { + "Fn::GetAtt": [ + "PipelineArtifactsBucketEncryptionKeyF5BF0670", + "Arn" + ] + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "PipelineRoleB27FAA37": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codepipeline.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelineRoleDefaultPolicy7BDC1ABB": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*", + "s3:PutObject", + "s3:Abort*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "kms:Decrypt", + "kms:DescribeKey", + "kms:Encrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelineArtifactsBucketEncryptionKeyF5BF0670", + "Arn" + ] + } + }, + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelineBuildSynthCodePipelineActionRole4E7A6C97", + "Arn" + ] + } + }, + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelineUpdatePipelineSelfMutateCodePipelineActionRoleD6D4E5CF", + "Arn" + ] + } + }, + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelineAssetsFileRole59943A77", + "Arn" + ] + } + }, + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelinePreProdUseSourceCodePipelineActionRoleA2043BDA", + "Arn" + ] + } + }, + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-test-region" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelineRoleDefaultPolicy7BDC1ABB", + "Roles": [ + { + "Ref": "PipelineRoleB27FAA37" + } + ] + } + }, + "Pipeline9850B417": { + "Type": "AWS::CodePipeline::Pipeline", + "Properties": { + "RoleArn": { + "Fn::GetAtt": [ + "PipelineRoleB27FAA37", + "Arn" + ] + }, + "Stages": [ + { + "Actions": [ + { + "ActionTypeId": { + "Category": "Source", + "Owner": "ThirdParty", + "Provider": "GitHub", + "Version": "1" + }, + "Configuration": { + "Owner": "OWNER", + "Repo": "REPO", + "Branch": "master", + "OAuthToken": "not-a-secret", + "PollForSourceChanges": true + }, + "Name": "GitHub", + "OutputArtifacts": [ + { + "Name": "Artifact_Source_GitHub" + } + ], + "RunOrder": 1 + } + ], + "Name": "Source" + }, + { + "Actions": [ + { + "ActionTypeId": { + "Category": "Build", + "Owner": "AWS", + "Provider": "CodeBuild", + "Version": "1" + }, + "Configuration": { + "ProjectName": { + "Ref": "PipelineBuildSynthCdkBuildProject6BEFA8E6" + }, + "EnvironmentVariables": "[{\"name\":\"_PROJECT_CONFIG_HASH\",\"type\":\"PLAINTEXT\",\"value\":\"da8c1ee6d645a5802d25355fec94a3f22a961102b74ac3846898d6b14e3a2c30\"}]" + }, + "InputArtifacts": [ + { + "Name": "Artifact_Source_GitHub" + } + ], + "Name": "Synth", + "OutputArtifacts": [ + { + "Name": "CloudAsm" + }, + { + "Name": "IntegTests" + } + ], + "RoleArn": { + "Fn::GetAtt": [ + "PipelineBuildSynthCodePipelineActionRole4E7A6C97", + "Arn" + ] + }, + "RunOrder": 1 + } + ], + "Name": "Build" + }, + { + "Actions": [ + { + "ActionTypeId": { + "Category": "Build", + "Owner": "AWS", + "Provider": "CodeBuild", + "Version": "1" + }, + "Configuration": { + "ProjectName": { + "Ref": "PipelineUpdatePipelineSelfMutationDAA41400" + } + }, + "InputArtifacts": [ + { + "Name": "CloudAsm" + } + ], + "Name": "SelfMutate", + "RoleArn": { + "Fn::GetAtt": [ + "PipelineUpdatePipelineSelfMutateCodePipelineActionRoleD6D4E5CF", + "Arn" + ] + }, + "RunOrder": 1 + } + ], + "Name": "UpdatePipeline" + }, + { + "Actions": [ + { + "ActionTypeId": { + "Category": "Build", + "Owner": "AWS", + "Provider": "CodeBuild", + "Version": "1" + }, + "Configuration": { + "ProjectName": { + "Ref": "PipelineAssetsFileAsset5D8C5DA6" + } + }, + "InputArtifacts": [ + { + "Name": "CloudAsm" + } + ], + "Name": "FileAsset", + "RoleArn": { + "Fn::GetAtt": [ + "PipelineAssetsFileRole59943A77", + "Arn" + ] + }, + "RunOrder": 1 + } + ], + "Name": "Assets" + }, + { + "Actions": [ + { + "ActionTypeId": { + "Category": "Build", + "Owner": "AWS", + "Provider": "CodeBuild", + "Version": "1" + }, + "Configuration": { + "ProjectName": { + "Ref": "PipelinePreProdUseSourceProject2E711EB4" + } + }, + "InputArtifacts": [ + { + "Name": "Artifact_Source_GitHub" + } + ], + "Name": "UseSource", + "RoleArn": { + "Fn::GetAtt": [ + "PipelinePreProdUseSourceCodePipelineActionRoleA2043BDA", + "Arn" + ] + }, + "RunOrder": 100 + }, + { + "ActionTypeId": { + "Category": "Deploy", + "Owner": "AWS", + "Provider": "CloudFormation", + "Version": "1" + }, + "Configuration": { + "StackName": "PreProd-Stack", + "Capabilities": "CAPABILITY_NAMED_IAM,CAPABILITY_AUTO_EXPAND", + "RoleArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::12345678:role/cdk-hnb659fds-cfn-exec-role-12345678-test-region" + ] + ] + }, + "ActionMode": "CHANGE_SET_REPLACE", + "ChangeSetName": "PipelineChange", + "TemplatePath": "CloudAsm::assembly-PipelineStack-PreProd/PipelineStackPreProdStack65A0AD1F.template.json" + }, + "InputArtifacts": [ + { + "Name": "CloudAsm" + } + ], + "Name": "Stack.Prepare", + "RoleArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-test-region" + ] + ] + }, + "RunOrder": 1 + }, + { + "ActionTypeId": { + "Category": "Deploy", + "Owner": "AWS", + "Provider": "CloudFormation", + "Version": "1" + }, + "Configuration": { + "StackName": "PreProd-Stack", + "ActionMode": "CHANGE_SET_EXECUTE", + "ChangeSetName": "PipelineChange" + }, + "Name": "Stack.Deploy", + "RoleArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-test-region" + ] + ] + }, + "RunOrder": 2 + } + ], + "Name": "PreProd" + } + ], + "ArtifactStore": { + "EncryptionKey": { + "Id": { + "Fn::GetAtt": [ + "PipelineArtifactsBucketEncryptionKeyF5BF0670", + "Arn" + ] + }, + "Type": "KMS" + }, + "Location": { + "Ref": "PipelineArtifactsBucketAEA9A052" + }, + "Type": "S3" + }, + "RestartExecutionOnUpdate": true + }, + "DependsOn": [ + "PipelineRoleDefaultPolicy7BDC1ABB", + "PipelineRoleB27FAA37" + ] + }, + "PipelineBuildSynthCodePipelineActionRole4E7A6C97": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::12345678:root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelineBuildSynthCodePipelineActionRoleDefaultPolicy92C90290": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "codebuild:BatchGetBuilds", + "codebuild:StartBuild", + "codebuild:StopBuild" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelineBuildSynthCdkBuildProject6BEFA8E6", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelineBuildSynthCodePipelineActionRoleDefaultPolicy92C90290", + "Roles": [ + { + "Ref": "PipelineBuildSynthCodePipelineActionRole4E7A6C97" + } + ] + } + }, + "PipelineBuildSynthCdkBuildProjectRole231EEA2A": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codebuild.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelineBuildSynthCdkBuildProjectRoleDefaultPolicyFB6C941C": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:test-region:12345678:log-group:/aws/codebuild/", + { + "Ref": "PipelineBuildSynthCdkBuildProject6BEFA8E6" + } + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:test-region:12345678:log-group:/aws/codebuild/", + { + "Ref": "PipelineBuildSynthCdkBuildProject6BEFA8E6" + }, + ":*" + ] + ] + } + ] + }, + { + "Action": [ + "codebuild:CreateReportGroup", + "codebuild:CreateReport", + "codebuild:UpdateReport", + "codebuild:BatchPutTestCases", + "codebuild:BatchPutCodeCoverages" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":codebuild:test-region:12345678:report-group/", + { + "Ref": "PipelineBuildSynthCdkBuildProject6BEFA8E6" + }, + "-*" + ] + ] + } + }, + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*", + "s3:PutObject", + "s3:Abort*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "kms:Decrypt", + "kms:DescribeKey", + "kms:Encrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelineArtifactsBucketEncryptionKeyF5BF0670", + "Arn" + ] + } + }, + { + "Action": [ + "kms:Decrypt", + "kms:Encrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelineArtifactsBucketEncryptionKeyF5BF0670", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelineBuildSynthCdkBuildProjectRoleDefaultPolicyFB6C941C", + "Roles": [ + { + "Ref": "PipelineBuildSynthCdkBuildProjectRole231EEA2A" + } + ] + } + }, + "PipelineBuildSynthCdkBuildProject6BEFA8E6": { + "Type": "AWS::CodeBuild::Project", + "Properties": { + "Artifacts": { + "Type": "CODEPIPELINE" + }, + "Environment": { + "ComputeType": "BUILD_GENERAL1_SMALL", + "EnvironmentVariables": [ + { + "Name": "NPM_CONFIG_UNSAFE_PERM", + "Type": "PLAINTEXT", + "Value": "true" + } + ], + "Image": "aws/codebuild/standard:5.0", + "ImagePullCredentialsType": "CODEBUILD", + "PrivilegedMode": false, + "Type": "LINUX_CONTAINER" + }, + "ServiceRole": { + "Fn::GetAtt": [ + "PipelineBuildSynthCdkBuildProjectRole231EEA2A", + "Arn" + ] + }, + "Source": { + "BuildSpec": "{\n \"version\": \"0.2\",\n \"phases\": {\n \"pre_build\": {\n \"commands\": [\n \"npm ci\"\n ]\n },\n \"build\": {\n \"commands\": [\n \"npx cdk synth\"\n ]\n }\n },\n \"artifacts\": {\n \"secondary-artifacts\": {\n \"CloudAsm\": {\n \"base-directory\": \"cdk.out\",\n \"files\": \"**/*\"\n },\n \"IntegTests\": {\n \"base-directory\": \"test\",\n \"files\": \"**/*\"\n }\n }\n }\n}", + "Type": "CODEPIPELINE" + }, + "EncryptionKey": { + "Fn::GetAtt": [ + "PipelineArtifactsBucketEncryptionKeyF5BF0670", + "Arn" + ] + }, + "Name": "MyServicePipeline-synth" + } + }, + "PipelineUpdatePipelineSelfMutateCodePipelineActionRoleD6D4E5CF": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::12345678:root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelineUpdatePipelineSelfMutateCodePipelineActionRoleDefaultPolicyE626265B": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "codebuild:BatchGetBuilds", + "codebuild:StartBuild", + "codebuild:StopBuild" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelineUpdatePipelineSelfMutationDAA41400", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelineUpdatePipelineSelfMutateCodePipelineActionRoleDefaultPolicyE626265B", + "Roles": [ + { + "Ref": "PipelineUpdatePipelineSelfMutateCodePipelineActionRoleD6D4E5CF" + } + ] + } + }, + "PipelinePreProdUseSourceCodePipelineActionRoleA2043BDA": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::12345678:root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelinePreProdUseSourceCodePipelineActionRoleDefaultPolicy9BE325AD": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "codebuild:BatchGetBuilds", + "codebuild:StartBuild", + "codebuild:StopBuild" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelinePreProdUseSourceProject2E711EB4", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelinePreProdUseSourceCodePipelineActionRoleDefaultPolicy9BE325AD", + "Roles": [ + { + "Ref": "PipelinePreProdUseSourceCodePipelineActionRoleA2043BDA" + } + ] + } + }, + "PipelinePreProdUseSourceProjectRole69B20A71": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codebuild.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelinePreProdUseSourceProjectRoleDefaultPolicy50F68DF3": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:test-region:12345678:log-group:/aws/codebuild/", + { + "Ref": "PipelinePreProdUseSourceProject2E711EB4" + } + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:test-region:12345678:log-group:/aws/codebuild/", + { + "Ref": "PipelinePreProdUseSourceProject2E711EB4" + }, + ":*" + ] + ] + } + ] + }, + { + "Action": [ + "codebuild:CreateReportGroup", + "codebuild:CreateReport", + "codebuild:UpdateReport", + "codebuild:BatchPutTestCases", + "codebuild:BatchPutCodeCoverages" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":codebuild:test-region:12345678:report-group/", + { + "Ref": "PipelinePreProdUseSourceProject2E711EB4" + }, + "-*" + ] + ] + } + }, + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "kms:Decrypt", + "kms:DescribeKey" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelineArtifactsBucketEncryptionKeyF5BF0670", + "Arn" + ] + } + }, + { + "Action": [ + "kms:Decrypt", + "kms:Encrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelineArtifactsBucketEncryptionKeyF5BF0670", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelinePreProdUseSourceProjectRoleDefaultPolicy50F68DF3", + "Roles": [ + { + "Ref": "PipelinePreProdUseSourceProjectRole69B20A71" + } + ] + } + }, + "PipelinePreProdUseSourceProject2E711EB4": { + "Type": "AWS::CodeBuild::Project", + "Properties": { + "Artifacts": { + "Type": "CODEPIPELINE" + }, + "Environment": { + "ComputeType": "BUILD_GENERAL1_SMALL", + "Image": "aws/codebuild/standard:5.0", + "ImagePullCredentialsType": "CODEBUILD", + "PrivilegedMode": false, + "Type": "LINUX_CONTAINER" + }, + "ServiceRole": { + "Fn::GetAtt": [ + "PipelinePreProdUseSourceProjectRole69B20A71", + "Arn" + ] + }, + "Source": { + "BuildSpec": "{\n \"version\": \"0.2\",\n \"phases\": {\n \"build\": {\n \"commands\": [\n \"set -eu\",\n \"cat README.md\"\n ]\n }\n }\n}", + "Type": "CODEPIPELINE" + }, + "EncryptionKey": { + "Fn::GetAtt": [ + "PipelineArtifactsBucketEncryptionKeyF5BF0670", + "Arn" + ] + } + } + }, + "PipelineUpdatePipelineSelfMutationRole57E559E8": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codebuild.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelineUpdatePipelineSelfMutationRoleDefaultPolicyA225DA4E": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:test-region:12345678:log-group:/aws/codebuild/", + { + "Ref": "PipelineUpdatePipelineSelfMutationDAA41400" + } + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:test-region:12345678:log-group:/aws/codebuild/", + { + "Ref": "PipelineUpdatePipelineSelfMutationDAA41400" + }, + ":*" + ] + ] + } + ] + }, + { + "Action": [ + "codebuild:CreateReportGroup", + "codebuild:CreateReport", + "codebuild:UpdateReport", + "codebuild:BatchPutTestCases", + "codebuild:BatchPutCodeCoverages" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":codebuild:test-region:12345678:report-group/", + { + "Ref": "PipelineUpdatePipelineSelfMutationDAA41400" + }, + "-*" + ] + ] + } + }, + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Resource": [ + "arn:*:iam::*:role/*-deploy-role-*", + "arn:*:iam::*:role/*-publishing-role-*" + ] + }, + { + "Action": "cloudformation:DescribeStacks", + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": "s3:ListBucket", + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "kms:Decrypt", + "kms:DescribeKey" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelineArtifactsBucketEncryptionKeyF5BF0670", + "Arn" + ] + } + }, + { + "Action": [ + "kms:Decrypt", + "kms:Encrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelineArtifactsBucketEncryptionKeyF5BF0670", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelineUpdatePipelineSelfMutationRoleDefaultPolicyA225DA4E", + "Roles": [ + { + "Ref": "PipelineUpdatePipelineSelfMutationRole57E559E8" + } + ] + } + }, + "PipelineUpdatePipelineSelfMutationDAA41400": { + "Type": "AWS::CodeBuild::Project", + "Properties": { + "Artifacts": { + "Type": "CODEPIPELINE" + }, + "Environment": { + "ComputeType": "BUILD_GENERAL1_SMALL", + "Image": "aws/codebuild/standard:5.0", + "ImagePullCredentialsType": "CODEBUILD", + "PrivilegedMode": false, + "Type": "LINUX_CONTAINER" + }, + "ServiceRole": { + "Fn::GetAtt": [ + "PipelineUpdatePipelineSelfMutationRole57E559E8", + "Arn" + ] + }, + "Source": { + "BuildSpec": "{\n \"version\": \"0.2\",\n \"phases\": {\n \"install\": {\n \"commands\": \"npm install -g aws-cdk\"\n },\n \"build\": {\n \"commands\": [\n \"cdk -a . deploy PipelineStack --require-approval=never --verbose\"\n ]\n }\n }\n}", + "Type": "CODEPIPELINE" + }, + "EncryptionKey": { + "Fn::GetAtt": [ + "PipelineArtifactsBucketEncryptionKeyF5BF0670", + "Arn" + ] + } + } + }, + "PipelineAssetsFileRole59943A77": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codebuild.amazonaws.com", + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::12345678:root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelineAssetsFileRoleDefaultPolicy14DB8755": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:test-region:12345678:log-group:/aws/codebuild/*" + ] + ] + } + }, + { + "Action": [ + "codebuild:CreateReportGroup", + "codebuild:CreateReport", + "codebuild:UpdateReport", + "codebuild:BatchPutTestCases", + "codebuild:BatchPutCodeCoverages" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":codebuild:test-region:12345678:report-group/*" + ] + ] + } + }, + { + "Action": [ + "codebuild:BatchGetBuilds", + "codebuild:StartBuild", + "codebuild:StopBuild" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Resource": "arn:*:iam::*:role/*-file-publishing-role-*" + }, + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "kms:Decrypt", + "kms:DescribeKey" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelineArtifactsBucketEncryptionKeyF5BF0670", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelineAssetsFileRoleDefaultPolicy14DB8755", + "Roles": [ + { + "Ref": "PipelineAssetsFileRole59943A77" + } + ] + } + }, + "PipelineAssetsFileAsset5D8C5DA6": { + "Type": "AWS::CodeBuild::Project", + "Properties": { + "Artifacts": { + "Type": "CODEPIPELINE" + }, + "Environment": { + "ComputeType": "BUILD_GENERAL1_SMALL", + "Image": "aws/codebuild/standard:5.0", + "ImagePullCredentialsType": "CODEBUILD", + "PrivilegedMode": false, + "Type": "LINUX_CONTAINER" + }, + "ServiceRole": { + "Fn::GetAtt": [ + "PipelineAssetsFileRole59943A77", + "Arn" + ] + }, + "Source": { + "BuildSpec": "{\n \"version\": \"0.2\",\n \"phases\": {\n \"install\": {\n \"commands\": \"npm install -g cdk-assets\"\n },\n \"build\": {\n \"commands\": [\n \"cdk-assets --path \\\"assembly-PipelineStack-PreProd/PipelineStackPreProdStack65A0AD1F.assets.json\\\" --verbose publish \\\"8289faf53c7da377bb2b90615999171adef5e1d8f6b88810e5fef75e6ca09ba5:12345678-test-region\\\"\",\n \"cdk-assets --path \\\"assembly-PipelineStack-PreProd/PipelineStackPreProdStack65A0AD1F.assets.json\\\" --verbose publish \\\"ac76997971c3f6ddf37120660003f1ced72b4fc58c498dfd99c78fa77e721e0e:12345678-test-region\\\"\"\n ]\n }\n }\n}", + "Type": "CODEPIPELINE" + }, + "EncryptionKey": { + "Fn::GetAtt": [ + "PipelineArtifactsBucketEncryptionKeyF5BF0670", + "Arn" + ] + } + } + } + } +} diff --git a/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets-single-upload.ts b/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets-single-upload.ts new file mode 100644 index 0000000000000..a4f35010a10b7 --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets-single-upload.ts @@ -0,0 +1,94 @@ +/// !cdk-integ PipelineStack +import * as path from 'path'; +import * as codepipeline from '@aws-cdk/aws-codepipeline'; +import * as codepipeline_actions from '@aws-cdk/aws-codepipeline-actions'; +import * as s3_assets from '@aws-cdk/aws-s3-assets'; +import { App, CfnResource, SecretValue, Stack, StackProps, Stage, StageProps } from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import * as cdkp from '../lib'; + +class MyStage extends Stage { + constructor(scope: Construct, id: string, props?: StageProps) { + super(scope, id, props); + + const stack = new Stack(this, 'Stack', props); + + new s3_assets.Asset(stack, 'Asset', { + path: path.join(__dirname, 'test-file-asset.txt'), + }); + new s3_assets.Asset(stack, 'Asset2', { + path: path.join(__dirname, 'test-file-asset-two.txt'), + }); + + new CfnResource(stack, 'Resource', { + type: 'AWS::Test::SomeResource', + }); + } +} + +/** + * The stack that defines the application pipeline + */ +class CdkpipelinesDemoPipelineStack extends Stack { + constructor(scope: Construct, id: string, props?: StackProps) { + super(scope, id, props); + + const sourceArtifact = new codepipeline.Artifact(); + const cloudAssemblyArtifact = new codepipeline.Artifact('CloudAsm'); + const integTestArtifact = new codepipeline.Artifact('IntegTests'); + + const pipeline = new cdkp.CdkPipeline(this, 'Pipeline', { + cloudAssemblyArtifact, + singlePublisherPerType: true, + + // Where the source can be found + sourceAction: new codepipeline_actions.GitHubSourceAction({ + actionName: 'GitHub', + output: sourceArtifact, + oauthToken: SecretValue.plainText('not-a-secret'), + owner: 'OWNER', + repo: 'REPO', + trigger: codepipeline_actions.GitHubTrigger.POLL, + }), + + // How it will be built + synthAction: cdkp.SimpleSynthAction.standardNpmSynth({ + sourceArtifact, + cloudAssemblyArtifact, + projectName: 'MyServicePipeline-synth', + additionalArtifacts: [ + { + directory: 'test', + artifact: integTestArtifact, + }, + ], + }), + }); + + // This is where we add the application stages + // ... + const stage = pipeline.addApplicationStage(new MyStage(this, 'PreProd', { + env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, + })); + stage.addActions( + new cdkp.ShellScriptAction({ + actionName: 'UseSource', + commands: [ + // Comes from source + 'cat README.md', + ], + additionalArtifacts: [sourceArtifact], + }), + ); + } +} + +const app = new App({ + context: { + '@aws-cdk/core:newStyleStackSynthesis': 'true', + }, +}); +new CdkpipelinesDemoPipelineStack(app, 'PipelineStack', { + env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, +}); +app.synth(); diff --git a/packages/@aws-cdk/pipelines/test/pipeline-assets.test.ts b/packages/@aws-cdk/pipelines/test/pipeline-assets.test.ts index ec89b621d6443..b3da0fb5a5a59 100644 --- a/packages/@aws-cdk/pipelines/test/pipeline-assets.test.ts +++ b/packages/@aws-cdk/pipelines/test/pipeline-assets.test.ts @@ -11,6 +11,7 @@ import * as cdkp from '../lib'; import { BucketStack, PIPELINE_ENV, TestApp, TestGitHubAction, TestGitHubNpmPipeline } from './testutil'; const FILE_ASSET_SOURCE_HASH = '8289faf53c7da377bb2b90615999171adef5e1d8f6b88810e5fef75e6ca09ba5'; +const FILE_ASSET_SOURCE_HASH2 = 'ac76997971c3f6ddf37120660003f1ced72b4fc58c498dfd99c78fa77e721e0e'; let app: TestApp; let pipelineStack: Stack; @@ -406,6 +407,53 @@ describe('pipeline with VPC', () => { }); }); +describe('pipeline with single asset publisher', () => { + beforeEach(() => { + app = new TestApp(); + pipelineStack = new Stack(app, 'PipelineStack', { env: PIPELINE_ENV }); + pipeline = new TestGitHubNpmPipeline(pipelineStack, 'Cdk', { + singlePublisherPerType: true, + }); + }); + + afterEach(() => { + app.cleanup(); + }); + + test('multiple assets are using the same job in singlePublisherMode', () => { + // WHEN + pipeline.addApplicationStage(new TwoFileAssetsApp(app, 'FileAssetApp')); + + // THEN + expect(pipelineStack).toHaveResourceLike('AWS::CodePipeline::Pipeline', { + Stages: arrayWith({ + Name: 'Assets', + Actions: [ + // Only one file asset action + objectLike({ RunOrder: 1, Name: 'FileAsset' }), + ], + }), + }); + expect(pipelineStack).toHaveResourceLike('AWS::CodeBuild::Project', { + Environment: { + Image: 'aws/codebuild/standard:5.0', + }, + Source: { + BuildSpec: encodedJson(deepObjectLike({ + phases: { + build: { + // Both assets are uploaded in the same action + commands: arrayWith( + `cdk-assets --path "assembly-FileAssetApp/FileAssetAppStackEADD68C5.assets.json" --verbose publish "${FILE_ASSET_SOURCE_HASH}:current_account-current_region"`, + `cdk-assets --path "assembly-FileAssetApp/FileAssetAppStackEADD68C5.assets.json" --verbose publish "${FILE_ASSET_SOURCE_HASH2}:current_account-current_region"`, + ), + }, + }, + })), + }, + }); + }); +}); class PlainStackApp extends Stage { constructor(scope: Construct, id: string, props?: StageProps) { super(scope, id, props); From 882508caa1b3f91be9d976b810e324deead21f85 Mon Sep 17 00:00:00 2001 From: Shane Handley <68725881+shanehandley-lt@users.noreply.github.com> Date: Tue, 1 Jun 2021 20:13:03 +1000 Subject: [PATCH 106/134] chore(apigatewayv2): improve error message for invalid route path (#13981) Specify the source of the error when an invalid http route path is detected The error currently indicates an issue with 'path' which is unfortunately unhelpful when working with large stacks various paths (files/assets/etc...). I spent a while triple checking lambda asset paths before searching the codebase to find the source of this error. I think the additional context might help someone find the source of the error more quickly. ---- *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-apigatewayv2/lib/http/route.ts | 2 +- packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts index a88aaae0b3416..ecc54b7f6acd9 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts @@ -59,7 +59,7 @@ export class HttpRouteKey { */ public static with(path: string, method?: HttpMethod) { if (path !== '/' && (!path.startsWith('/') || path.endsWith('/'))) { - throw new Error('path must always start with a "/" and not end with a "/"'); + throw new Error('A route path must always start with a "/" and not end with a "/"'); } return new HttpRouteKey(`${method ?? HttpMethod.ANY} ${path}`, path); } diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts index f30bdaba9205e..018b23ba1e10d 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts @@ -145,7 +145,7 @@ describe('HttpRoute', () => { httpApi, integration: new DummyIntegration(), routeKey: HttpRouteKey.with('books', HttpMethod.GET), - })).toThrowError(/path must always start with a "\/" and not end with a "\/"/); + })).toThrowError(/A route path must always start with a "\/" and not end with a "\/"/); }); test('throws when path ends with /', () => { @@ -156,7 +156,7 @@ describe('HttpRoute', () => { httpApi, integration: new DummyIntegration(), routeKey: HttpRouteKey.with('/books/', HttpMethod.GET), - })).toThrowError(/path must always start with a "\/" and not end with a "\/"/); + })).toThrowError(/A route path must always start with a "\/" and not end with a "\/"/); }); test('configures private integration correctly when all props are passed', () => { From 40d2ff964c97954c70d79a09d60fcb795ef16791 Mon Sep 17 00:00:00 2001 From: Thorsten Hoeger Date: Tue, 1 Jun 2021 13:49:10 +0200 Subject: [PATCH 107/134] feat(cloudfront): add L2 support for CloudFront functions (#14511) Add support for CloudFront functions /cc @njlynch ---- *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-cloudfront/README.md | 22 +++ .../aws-cloudfront/lib/distribution.ts | 10 +- .../@aws-cdk/aws-cloudfront/lib/function.ts | 180 ++++++++++++++++++ packages/@aws-cdk/aws-cloudfront/lib/index.ts | 1 + .../lib/private/cache-behavior.ts | 4 + .../aws-cloudfront/lib/web-distribution.ts | 40 ++-- .../aws-cloudfront/test/distribution.test.ts | 40 +++- .../aws-cloudfront/test/function.test.ts | 109 +++++++++++ .../integ.distribution-function.expected.json | 70 +++++++ .../test/integ.distribution-function.ts | 26 +++ .../test/web-distribution.test.ts | 49 +++++ 11 files changed, 537 insertions(+), 14 deletions(-) create mode 100644 packages/@aws-cdk/aws-cloudfront/lib/function.ts create mode 100644 packages/@aws-cdk/aws-cloudfront/test/function.test.ts create mode 100644 packages/@aws-cdk/aws-cloudfront/test/integ.distribution-function.expected.json create mode 100644 packages/@aws-cdk/aws-cloudfront/test/integ.distribution-function.ts diff --git a/packages/@aws-cdk/aws-cloudfront/README.md b/packages/@aws-cdk/aws-cloudfront/README.md index debc3b09d734f..3b35de52533da 100644 --- a/packages/@aws-cdk/aws-cloudfront/README.md +++ b/packages/@aws-cdk/aws-cloudfront/README.md @@ -386,6 +386,28 @@ new cloudfront.Distribution(this, 'distro', { }); ``` +### CloudFront Function + +You can also deploy CloudFront functions and add them to a CloudFront distribution. + +```ts +const cfFunction = new cloudfront.Function(stack, 'Function', { + code: cloudfront.FunctionCode.fromInline('function handler(event) { return event.request }'), +}); + +new cloudfront.Distribution(stack, 'distro', { + defaultBehavior: { + origin: new origins.S3Origin(s3Bucket), + functionAssociations: [{ + function: cfFunction, + eventType: cloudfront.FunctionEventType.VIEWER_REQUEST, + }], + }, +}); +``` + +It will auto-generate the name of the function and deploy it to the `live` stage. + ### Logging You can configure CloudFront to create log files that contain detailed information about every user request that CloudFront receives. diff --git a/packages/@aws-cdk/aws-cloudfront/lib/distribution.ts b/packages/@aws-cdk/aws-cloudfront/lib/distribution.ts index 3482d53e9624c..fb1bca2c0b278 100644 --- a/packages/@aws-cdk/aws-cloudfront/lib/distribution.ts +++ b/packages/@aws-cdk/aws-cloudfront/lib/distribution.ts @@ -5,6 +5,7 @@ import { IResource, Lazy, Resource, Stack, Token, Duration, Names } from '@aws-c import { Construct } from 'constructs'; import { ICachePolicy } from './cache-policy'; import { CfnDistribution } from './cloudfront.generated'; +import { FunctionAssociation } from './function'; import { GeoRestriction } from './geo-restriction'; import { IKeyGroup } from './key-group'; import { IOrigin, OriginBindConfig, OriginBindOptions } from './origin'; @@ -445,7 +446,7 @@ export class Distribution extends Resource implements IDistribution { } private renderViewerCertificate(certificate: acm.ICertificate, - minimumProtocolVersion: SecurityPolicyProtocol = SecurityPolicyProtocol.TLS_V1_2_2019) : CfnDistribution.ViewerCertificateProperty { + minimumProtocolVersion: SecurityPolicyProtocol = SecurityPolicyProtocol.TLS_V1_2_2019): CfnDistribution.ViewerCertificateProperty { return { acmCertificateArn: certificate.certificateArn, sslSupportMethod: SSLMethod.SNI, @@ -706,6 +707,13 @@ export interface AddBehaviorOptions { */ readonly viewerProtocolPolicy?: ViewerProtocolPolicy; + /** + * The CloudFront functions to invoke before serving the contents. + * + * @default - no functions will be invoked + */ + readonly functionAssociations?: FunctionAssociation[]; + /** * The Lambda@Edge functions to invoke before serving the contents. * diff --git a/packages/@aws-cdk/aws-cloudfront/lib/function.ts b/packages/@aws-cdk/aws-cloudfront/lib/function.ts new file mode 100644 index 0000000000000..8f8f396ca6afa --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/lib/function.ts @@ -0,0 +1,180 @@ +import { IResource, Names, Resource, Stack } from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import { CfnFunction } from './cloudfront.generated'; + +/** + * Represents the function's source code + */ +export abstract class FunctionCode { + + /** + * Inline code for function + * @returns `InlineCode` with inline code. + * @param code The actual function code + */ + public static fromInline(code: string): FunctionCode { + return new InlineCode(code); + } + + /** + * renders the function code + */ + public abstract render(): string; +} + +/** + * Represents the function's source code as inline code + */ +class InlineCode extends FunctionCode { + + constructor(private code: string) { + super(); + } + + public render(): string { + return this.code; + } +} + +/** + * Represents a CloudFront Function + */ +export interface IFunction extends IResource { + /** + * The name of the function. + * @attribute + */ + readonly functionName: string; + + /** + * The ARN of the function. + * @attribute + */ + readonly functionArn: string; +} + +/** + * Attributes of an existing CloudFront Function to import it + */ +export interface FunctionAttributes { + /** + * The name of the function. + */ + readonly functionName: string; + + /** + * The ARN of the function. + */ + readonly functionArn: string; +} + +/** + * Properties for creating a CloudFront Function + */ +export interface FunctionProps { + /** + * A name to identify the function. + * @default - generated from the `id` + */ + readonly functionName?: string; + + /** + * A comment to describe the function. + * @default - same as `functionName` + */ + readonly comment?: string; + + /** + * The source code of the function. + */ + readonly code: FunctionCode; +} + +/** + * A CloudFront Function + * + * @resource AWS::CloudFront::Function + */ +export class Function extends Resource implements IFunction { + + /** Imports a function by its name and ARN */ + public static fromFunctionAttributes(scope: Construct, id: string, attrs: FunctionAttributes): IFunction { + return new class extends Resource implements IFunction { + public readonly functionName = attrs.functionName; + public readonly functionArn = attrs.functionArn; + }(scope, id); + } + + /** + * the name of the CloudFront function + * @attribute + */ + public readonly functionName: string; + /** + * the ARN of the CloudFront function + * @attribute + */ + public readonly functionArn: string; + /** + * the deployment stage of the CloudFront function + * @attribute + */ + public readonly functionStage: string; + + constructor(scope: Construct, id: string, props: FunctionProps) { + super(scope, id); + + this.functionName = props.functionName ?? this.generateName(); + + const resource = new CfnFunction(this, 'Resource', { + autoPublish: true, + functionCode: props.code.render(), + functionConfig: { + comment: props.comment ?? this.functionName, + runtime: 'cloudfront-js-1.0', + }, + name: this.functionName, + }); + + this.functionArn = resource.attrFunctionArn; + this.functionStage = resource.attrStage; + } + + private generateName(): string { + const name = Stack.of(this).region + Names.uniqueId(this); + if (name.length > 64) { + return name.substring(0, 32) + name.substring(name.length - 32); + } + return name; + } +} + +/** + * The type of events that a CloudFront function can be invoked in response to. + */ +export enum FunctionEventType { + + /** + * The viewer-request specifies the incoming request + */ + VIEWER_REQUEST = 'viewer-request', + + /** + * The viewer-response specifies the outgoing response + */ + VIEWER_RESPONSE = 'viewer-response', +} + +/** + * Represents a CloudFront function and event type when using CF Functions. + * The type of the {@link AddBehaviorOptions.functionAssociations} property. + */ +export interface FunctionAssociation { + /** + * The CloudFront function that will be invoked. + */ + readonly function: IFunction; + + /** The type of event which should invoke the function. */ + readonly eventType: FunctionEventType; +} diff --git a/packages/@aws-cdk/aws-cloudfront/lib/index.ts b/packages/@aws-cdk/aws-cloudfront/lib/index.ts index 7de2aa62b4412..74b5b4644919c 100644 --- a/packages/@aws-cdk/aws-cloudfront/lib/index.ts +++ b/packages/@aws-cdk/aws-cloudfront/lib/index.ts @@ -1,5 +1,6 @@ export * from './cache-policy'; export * from './distribution'; +export * from './function'; export * from './geo-restriction'; export * from './key-group'; export * from './origin'; diff --git a/packages/@aws-cdk/aws-cloudfront/lib/private/cache-behavior.ts b/packages/@aws-cdk/aws-cloudfront/lib/private/cache-behavior.ts index d804dd8465750..4e6f71589bb7e 100644 --- a/packages/@aws-cdk/aws-cloudfront/lib/private/cache-behavior.ts +++ b/packages/@aws-cdk/aws-cloudfront/lib/private/cache-behavior.ts @@ -50,6 +50,10 @@ export class CacheBehavior { originRequestPolicyId: this.props.originRequestPolicy?.originRequestPolicyId, smoothStreaming: this.props.smoothStreaming, viewerProtocolPolicy: this.props.viewerProtocolPolicy ?? ViewerProtocolPolicy.ALLOW_ALL, + functionAssociations: this.props.functionAssociations?.map(association => ({ + functionArn: association.function.functionArn, + eventType: association.eventType.toString(), + })), lambdaFunctionAssociations: this.props.edgeLambdas?.map(edgeLambda => ({ lambdaFunctionArn: edgeLambda.functionVersion.edgeArn, eventType: edgeLambda.eventType.toString(), diff --git a/packages/@aws-cdk/aws-cloudfront/lib/web-distribution.ts b/packages/@aws-cdk/aws-cloudfront/lib/web-distribution.ts index 17aafe5e4f6fd..db8db2b2bdeb4 100644 --- a/packages/@aws-cdk/aws-cloudfront/lib/web-distribution.ts +++ b/packages/@aws-cdk/aws-cloudfront/lib/web-distribution.ts @@ -6,6 +6,7 @@ import * as cdk from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnDistribution } from './cloudfront.generated'; import { HttpVersion, IDistribution, LambdaEdgeEventType, OriginProtocolPolicy, PriceClass, ViewerProtocolPolicy, SSLMethod, SecurityPolicyProtocol } from './distribution'; +import { FunctionAssociation } from './function'; import { GeoRestriction } from './geo-restriction'; import { IKeyGroup } from './key-group'; import { IOriginAccessIdentity } from './origin-access-identity'; @@ -422,6 +423,13 @@ export interface Behavior { */ readonly lambdaFunctionAssociations?: LambdaFunctionAssociation[]; + /** + * The CloudFront functions to invoke before serving the contents. + * + * @default - no functions will be invoked + */ + readonly functionAssociations?: FunctionAssociation[]; + } export interface LambdaFunctionAssociation { @@ -771,9 +779,9 @@ export class CloudFrontWebDistribution extends cdk.Resource implements IDistribu // Comments have an undocumented limit of 128 characters const trimmedComment = - props.comment && props.comment.length > 128 - ? `${props.comment.substr(0, 128 - 3)}...` - : props.comment; + props.comment && props.comment.length > 128 + ? `${props.comment.substr(0, 128 - 3)}...` + : props.comment; let distributionConfig: CfnDistribution.DistributionConfigProperty = { comment: trimmedComment, @@ -957,6 +965,14 @@ export class CloudFrontWebDistribution extends cdk.Resource implements IDistribu if (!input.isDefaultBehavior) { toReturn = Object.assign(toReturn, { pathPattern: input.pathPattern }); } + if (input.functionAssociations) { + toReturn = Object.assign(toReturn, { + functionAssociations: input.functionAssociations.map(association => ({ + functionArn: association.function.functionArn, + eventType: association.eventType.toString(), + })), + }); + } if (input.lambdaFunctionAssociations) { const includeBodyEventTypes = [LambdaEdgeEventType.ORIGIN_REQUEST, LambdaEdgeEventType.VIEWER_REQUEST]; if (input.lambdaFunctionAssociations.some(fna => fna.includeBody && !includeBodyEventTypes.includes(fna.eventType))) { @@ -1069,23 +1085,23 @@ export class CloudFrontWebDistribution extends cdk.Resource implements IDistribu : originConfig.customOriginSource!.domainName, originPath: originConfig.originPath ?? originConfig.customOriginSource?.originPath ?? originConfig.s3OriginSource?.originPath, originCustomHeaders: - originHeaders.length > 0 ? originHeaders : undefined, + originHeaders.length > 0 ? originHeaders : undefined, s3OriginConfig, customOriginConfig: originConfig.customOriginSource ? { httpPort: originConfig.customOriginSource.httpPort || 80, httpsPort: originConfig.customOriginSource.httpsPort || 443, originKeepaliveTimeout: - (originConfig.customOriginSource.originKeepaliveTimeout && - originConfig.customOriginSource.originKeepaliveTimeout.toSeconds()) || - 5, + (originConfig.customOriginSource.originKeepaliveTimeout && + originConfig.customOriginSource.originKeepaliveTimeout.toSeconds()) || + 5, originReadTimeout: - (originConfig.customOriginSource.originReadTimeout && - originConfig.customOriginSource.originReadTimeout.toSeconds()) || - 30, + (originConfig.customOriginSource.originReadTimeout && + originConfig.customOriginSource.originReadTimeout.toSeconds()) || + 30, originProtocolPolicy: - originConfig.customOriginSource.originProtocolPolicy || - OriginProtocolPolicy.HTTPS_ONLY, + originConfig.customOriginSource.originProtocolPolicy || + OriginProtocolPolicy.HTTPS_ONLY, originSslProtocols: originConfig.customOriginSource .allowedOriginSSLVersions || [OriginSslPolicy.TLS_V1_2], } diff --git a/packages/@aws-cdk/aws-cloudfront/test/distribution.test.ts b/packages/@aws-cdk/aws-cloudfront/test/distribution.test.ts index f6294ad1b6d08..52bd0a81c2c8a 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/distribution.test.ts +++ b/packages/@aws-cdk/aws-cloudfront/test/distribution.test.ts @@ -4,7 +4,7 @@ import * as acm from '@aws-cdk/aws-certificatemanager'; import * as lambda from '@aws-cdk/aws-lambda'; import * as s3 from '@aws-cdk/aws-s3'; import { App, Duration, Stack } from '@aws-cdk/core'; -import { CfnDistribution, Distribution, GeoRestriction, HttpVersion, IOrigin, LambdaEdgeEventType, PriceClass, SecurityPolicyProtocol } from '../lib'; +import { CfnDistribution, Distribution, Function, FunctionCode, FunctionEventType, GeoRestriction, HttpVersion, IOrigin, LambdaEdgeEventType, PriceClass, SecurityPolicyProtocol } from '../lib'; import { defaultOrigin, defaultOriginGroup } from './test-origin'; let app: App; @@ -730,6 +730,44 @@ describe('with Lambda@Edge functions', () => { }); }); +describe('with CloudFront functions', () => { + + test('can add a CloudFront function to the default behavior', () => { + new Distribution(stack, 'MyDist', { + defaultBehavior: { + origin: defaultOrigin(), + functionAssociations: [ + { + eventType: FunctionEventType.VIEWER_REQUEST, + function: new Function(stack, 'TestFunction', { + code: FunctionCode.fromInline('foo'), + }), + }, + ], + }, + }); + + expect(stack).toHaveResourceLike('AWS::CloudFront::Distribution', { + DistributionConfig: { + DefaultCacheBehavior: { + FunctionAssociations: [ + { + EventType: 'viewer-request', + FunctionARN: { + 'Fn::GetAtt': [ + 'TestFunction22AD90FC', + 'FunctionARN', + ], + }, + }, + ], + }, + }, + }); + }); + +}); + test('price class is included if provided', () => { const origin = defaultOrigin(); new Distribution(stack, 'Dist', { diff --git a/packages/@aws-cdk/aws-cloudfront/test/function.test.ts b/packages/@aws-cdk/aws-cloudfront/test/function.test.ts new file mode 100644 index 0000000000000..22b29ba48c857 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/function.test.ts @@ -0,0 +1,109 @@ +import '@aws-cdk/assert-internal/jest'; +import { expect as expectStack } from '@aws-cdk/assert-internal'; +import { App, Stack } from '@aws-cdk/core'; +import { Function, FunctionCode } from '../lib'; + +describe('CloudFront Function', () => { + + test('minimal example', () => { + const app = new App(); + const stack = new Stack(app, 'Stack', { + env: { account: '123456789012', region: 'testregion' }, + }); + new Function(stack, 'CF2', { + code: FunctionCode.fromInline('code'), + }); + + expectStack(stack).toMatch({ + Resources: { + CF2D7241DD7: { + Type: 'AWS::CloudFront::Function', + Properties: { + Name: 'testregionStackCF2CE3F783F', + AutoPublish: true, + FunctionCode: 'code', + FunctionConfig: { + Comment: 'testregionStackCF2CE3F783F', + Runtime: 'cloudfront-js-1.0', + }, + }, + }, + }, + }); + }); + + test('minimal example in environment agnostic stack', () => { + const app = new App(); + const stack = new Stack(app, 'Stack'); + new Function(stack, 'CF2', { + code: FunctionCode.fromInline('code'), + }); + + expectStack(stack).toMatch({ + Resources: { + CF2D7241DD7: { + Type: 'AWS::CloudFront::Function', + Properties: { + Name: { + 'Fn::Join': [ + '', + [ + { + Ref: 'AWS::Region', + }, + 'StackCF2CE3F783F', + ], + ], + }, + AutoPublish: true, + FunctionCode: 'code', + FunctionConfig: { + Comment: { + 'Fn::Join': [ + '', + [ + { + Ref: 'AWS::Region', + }, + 'StackCF2CE3F783F', + ], + ], + }, + Runtime: 'cloudfront-js-1.0', + }, + }, + }, + }, + }); + }); + + test('maximum example', () => { + const app = new App(); + const stack = new Stack(app, 'Stack', { + env: { account: '123456789012', region: 'testregion' }, + }); + new Function(stack, 'CF2', { + code: FunctionCode.fromInline('code'), + comment: 'My super comment', + functionName: 'FunctionName', + }); + + expectStack(stack).toMatch({ + Resources: { + CF2D7241DD7: { + Type: 'AWS::CloudFront::Function', + Properties: { + Name: 'FunctionName', + AutoPublish: true, + FunctionCode: 'code', + FunctionConfig: { + Comment: 'My super comment', + Runtime: 'cloudfront-js-1.0', + }, + }, + }, + }, + }); + }); + +}); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-function.expected.json b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-function.expected.json new file mode 100644 index 0000000000000..cc3517ea5897b --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-function.expected.json @@ -0,0 +1,70 @@ +{ + "Resources": { + "Function76856677": { + "Type": "AWS::CloudFront::Function", + "Properties": { + "Name": "eu-west-1integdistributionfunctionFunctionDCD62A02", + "AutoPublish": true, + "FunctionCode": "function handler(event) { return event.request }", + "FunctionConfig": { + "Comment": "eu-west-1integdistributionfunctionFunctionDCD62A02", + "Runtime": "cloudfront-js-1.0" + } + } + }, + "DistB3B78991": { + "Type": "AWS::CloudFront::Distribution", + "Properties": { + "DistributionConfig": { + "DefaultCacheBehavior": { + "CachePolicyId": "4135ea2d-6df8-44a3-9df3-4b5a84be39ad", + "Compress": true, + "FunctionAssociations": [ + { + "EventType": "viewer-request", + "FunctionARN": { + "Fn::GetAtt": [ + "Function76856677", + "FunctionARN" + ] + } + } + ], + "TargetOriginId": "integdistributionfunctionDistOrigin1D1E9DF17", + "ViewerProtocolPolicy": "allow-all" + }, + "Enabled": true, + "HttpVersion": "http2", + "IPV6Enabled": true, + "Origins": [ + { + "CustomOriginConfig": { + "OriginProtocolPolicy": "https-only" + }, + "DomainName": "www.example.com", + "Id": "integdistributionfunctionDistOrigin1D1E9DF17" + } + ] + } + } + } + }, + "Outputs": { + "FunctionArn": { + "Value": { + "Fn::GetAtt": [ + "Function76856677", + "FunctionARN" + ] + } + }, + "FunctionStage": { + "Value": { + "Fn::GetAtt": [ + "Function76856677", + "Stage" + ] + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-function.ts b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-function.ts new file mode 100644 index 0000000000000..df48a6799cdf3 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-function.ts @@ -0,0 +1,26 @@ +import * as cdk from '@aws-cdk/core'; +import * as cloudfront from '../lib'; +import { TestOrigin } from './test-origin'; + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'integ-distribution-function', { env: { region: 'eu-west-1' } }); + +const cfFunction = new cloudfront.Function(stack, 'Function', { + code: cloudfront.FunctionCode.fromInline('function handler(event) { return event.request }'), +}); + +new cloudfront.Distribution(stack, 'Dist', { + defaultBehavior: { + origin: new TestOrigin('www.example.com'), + cachePolicy: cloudfront.CachePolicy.CACHING_DISABLED, + functionAssociations: [{ + function: cfFunction, + eventType: cloudfront.FunctionEventType.VIEWER_REQUEST, + }], + }, +}); + +new cdk.CfnOutput(stack, 'FunctionArn', { value: cfFunction.functionArn }); +new cdk.CfnOutput(stack, 'FunctionStage', { value: cfFunction.functionStage }); + +app.synth(); diff --git a/packages/@aws-cdk/aws-cloudfront/test/web-distribution.test.ts b/packages/@aws-cdk/aws-cloudfront/test/web-distribution.test.ts index 6e10b54defc77..517aa18b3364c 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/web-distribution.test.ts +++ b/packages/@aws-cdk/aws-cloudfront/test/web-distribution.test.ts @@ -7,6 +7,9 @@ import { nodeunitShim, Test } from 'nodeunit-shim'; import { CfnDistribution, CloudFrontWebDistribution, + Function, + FunctionCode, + FunctionEventType, GeoRestriction, KeyGroup, LambdaEdgeEventType, @@ -597,6 +600,52 @@ added the ellipsis so a user would know there was more to ...`, test.done(); }, + 'distribution with CloudFront function-association'(test: Test) { + const stack = new cdk.Stack(); + const sourceBucket = new s3.Bucket(stack, 'Bucket'); + + new CloudFrontWebDistribution(stack, 'AnAmazingWebsiteProbably', { + originConfigs: [ + { + s3OriginSource: { + s3BucketSource: sourceBucket, + }, + behaviors: [ + { + isDefaultBehavior: true, + functionAssociations: [{ + eventType: FunctionEventType.VIEWER_REQUEST, + function: new Function(stack, 'TestFunction', { + code: FunctionCode.fromInline('foo'), + }), + }], + }, + ], + }, + ], + }); + + expect(stack).to(haveResourceLike('AWS::CloudFront::Distribution', { + 'DistributionConfig': { + 'DefaultCacheBehavior': { + 'FunctionAssociations': [ + { + 'EventType': 'viewer-request', + 'FunctionARN': { + 'Fn::GetAtt': [ + 'TestFunction22AD90FC', + 'FunctionARN', + ], + }, + }, + ], + }, + }, + })); + + test.done(); + }, + 'distribution with resolvable lambda-association'(test: Test) { const stack = new cdk.Stack(); const sourceBucket = new s3.Bucket(stack, 'Bucket'); From f8b191e13e3828c628a00b7409e63415c903c3fc Mon Sep 17 00:00:00 2001 From: Nick Lynch Date: Tue, 1 Jun 2021 14:56:25 +0100 Subject: [PATCH 108/134] chore: move check-yarn-lock to build.sh (#14938) Having this check in the prebuild is causing issues with the way we set up GitPod integration. Our build step actually does install as well, so our build is really a build+install, so it makes sense to do the check here anyway. fixes #14896 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- build.sh | 3 +++ package.json | 1 - yarn.lock | 9 ++++++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/build.sh b/build.sh index 14d8c27935405..3f1e425cf1095 100755 --- a/build.sh +++ b/build.sh @@ -59,6 +59,9 @@ if [ "$check_prereqs" == "true" ]; then /bin/bash ./scripts/check-build-prerequisites.sh fi +# Check that the yarn.lock is consistent +node ./scripts/check-yarn-lock.js + # Prepare for build with references /bin/bash scripts/generate-aggregate-tsconfig.sh > tsconfig.json diff --git a/package.json b/package.json index 088c4705b123d..a465b3f433d86 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,6 @@ }, "scripts": { "pkglint": "lerna --scope pkglint run build && lerna run pkglint", - "prebuild": "node ./scripts/check-yarn-lock.js", "build": "./build.sh", "pack": "./pack.sh", "compat": "./scripts/check-api-compatibility.sh", diff --git a/yarn.lock b/yarn.lock index 45a920fd274cf..60b0cc2629c51 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1553,6 +1553,13 @@ dependencies: "@types/node" "*" +"@types/fs-extra@^9.0.11": + version "9.0.11" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.11.tgz#8cc99e103499eab9f347dbc6ca4e99fb8d2c2b87" + integrity sha512-mZsifGG4QeQ7hlkhO56u7zt/ycBgGxSVsFI/6lGTU34VtwkiqrrSDgw0+ygs8kFGWcXnFQWMrzF2h7TtDFNixA== + dependencies: + "@types/node" "*" + "@types/glob@*", "@types/glob@^7.1.3": version "7.1.3" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183" @@ -3157,7 +3164,7 @@ conventional-commits-filter@^2.0.7: lodash.ismatch "^4.4.0" modify-values "^1.0.0" -conventional-commits-parser@^3.2.0: +conventional-commits-parser@^3.2.0, conventional-commits-parser@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-3.2.1.tgz#ba44f0b3b6588da2ee9fd8da508ebff50d116ce2" integrity sha512-OG9kQtmMZBJD/32NEw5IhN5+HnBqVjy03eC+I71I0oQRFA5rOgA4OtPOYG7mz1GkCfCNxn3gKIX8EiHJYuf1cA== From d07a49ff00ae07ea013ce6cc83d768e7729225a8 Mon Sep 17 00:00:00 2001 From: Ofir Naor Date: Tue, 1 Jun 2021 18:23:05 +0300 Subject: [PATCH 109/134] fix(elasticsearch): 'r6gd' not marked as supported type for instance storage (#14894) According to https://aws.amazon.com/elasticsearch-service/pricing/ it's now supported . Fixes #14773 ---- *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-elasticsearch/lib/domain.ts | 10 +++++----- .../@aws-cdk/aws-elasticsearch/test/domain.test.ts | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/@aws-cdk/aws-elasticsearch/lib/domain.ts b/packages/@aws-cdk/aws-elasticsearch/lib/domain.ts index 3f83da9ad3328..d8a8db02d8a8a 100644 --- a/packages/@aws-cdk/aws-elasticsearch/lib/domain.ts +++ b/packages/@aws-cdk/aws-elasticsearch/lib/domain.ts @@ -1454,8 +1454,8 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable { // Validate against instance type restrictions, per // https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/aes-supported-instance-types.html - if (isInstanceType('i3') && ebsEnabled) { - throw new Error('I3 instance types do not support EBS storage volumes.'); + if (isSomeInstanceType('i3', 'r6gd') && ebsEnabled) { + throw new Error('I3 and R6GD instance types do not support EBS storage volumes.'); } if (isSomeInstanceType('m3', 'r3', 't2') && encryptionAtRestEnabled) { @@ -1470,10 +1470,10 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable { throw new Error('T2 and T3 instance types do not support UltraWarm storage.'); } - // Only R3 and I3 support instance storage, per + // Only R3, I3 and r6gd support instance storage, per // https://aws.amazon.com/elasticsearch-service/pricing/ - if (!ebsEnabled && !isEveryInstanceType('r3', 'i3')) { - throw new Error('EBS volumes are required when using instance types other than r3 or i3.'); + if (!ebsEnabled && !isEveryInstanceType('r3', 'i3', 'r6gd')) { + throw new Error('EBS volumes are required when using instance types other than r3, i3 or r6gd.'); } // Fine-grained access control requires node-to-node encryption, encryption at rest, diff --git a/packages/@aws-cdk/aws-elasticsearch/test/domain.test.ts b/packages/@aws-cdk/aws-elasticsearch/test/domain.test.ts index 63cc2a7cdd853..0135d979860b1 100644 --- a/packages/@aws-cdk/aws-elasticsearch/test/domain.test.ts +++ b/packages/@aws-cdk/aws-elasticsearch/test/domain.test.ts @@ -1368,7 +1368,7 @@ describe('custom error responses', () => { volumeSize: 100, volumeType: EbsDeviceVolumeType.GENERAL_PURPOSE_SSD, }, - })).toThrow(/I3 instance types do not support EBS storage volumes/); + })).toThrow(/I3 and R6GD instance types do not support EBS storage volumes/); }); test('error when m3, r3, or t2 instance types are specified with encryption at rest enabled', () => { @@ -1411,7 +1411,7 @@ describe('custom error responses', () => { })).toThrow(/t2.micro.elasticsearch instance type supports only Elasticsearch 1.5 and 2.3/); }); - test('error when any instance type other than R3 and I3 are specified without EBS enabled', () => { + test('error when any instance type other than R3, I3 and R6GD are specified without EBS enabled', () => { expect(() => new Domain(stack, 'Domain1', { version: ElasticsearchVersion.V7_4, ebs: { @@ -1420,7 +1420,7 @@ describe('custom error responses', () => { capacity: { masterNodeInstanceType: 'm5.large.elasticsearch', }, - })).toThrow(/EBS volumes are required when using instance types other than r3 or i3/); + })).toThrow(/EBS volumes are required when using instance types other than r3, i3 or r6gd/); }); test('error when availabilityZoneCount is not 2 or 3', () => { From 5a0e7b9ef4ff948e6299e8605bbbeabc557f7a6e Mon Sep 17 00:00:00 2001 From: hanukoon Date: Wed, 2 Jun 2021 07:06:18 +0900 Subject: [PATCH 110/134] docs(Readme): remove unused image resource (screencast.gif) (#14852) base issue : #14851 I think "screencast.gif" at project root directory is not used (I thinks it already deprecated) So I think it's possible to delete this file. --- screencast.gif | Bin 835879 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 screencast.gif diff --git a/screencast.gif b/screencast.gif deleted file mode 100644 index d5cd629d526aacdf10e42c72987f5cca43a7f0a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 835879 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%0D{>e|!TUB%Tt)^{oo{5!s{^mEnyzi+$tM5c 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%0DWo_P`7C=C`n+0 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 ADgXcg From 1956ef6708d59329da61fbdd6056de4727e1e2e1 Mon Sep 17 00:00:00 2001 From: kirintw Date: Wed, 2 Jun 2021 07:09:08 +0800 Subject: [PATCH 111/134] feat(eks): support Kubernetes 1.20 (#14758) Support KubernetesVersion 1.20 Fixes: #14756 ---- *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-eks/README.md | 24 +++++++-------- packages/@aws-cdk/aws-eks/lib/cluster.ts | 5 ++++ .../test/example.ssh-into-nodes.lit.ts | 2 +- ...teg.eks-cluster-handlers-vpc.expected.json | 2 +- .../test/integ.eks-cluster-handlers-vpc.ts | 2 +- ...eks-cluster-private-endpoint.expected.json | 2 +- .../integ.eks-cluster-private-endpoint.ts | 2 +- .../test/integ.eks-cluster.expected.json | 30 +++++++++---------- .../aws-eks/test/integ.eks-cluster.ts | 4 +-- .../test/integ.fargate-cluster.expected.json | 2 +- .../aws-eks/test/integ.fargate-cluster.ts | 4 +-- .../@aws-cdk/aws-eks/test/test.cluster.ts | 18 +++++------ .../@aws-cdk/aws-eks/test/test.fargate.ts | 2 +- .../@aws-cdk/aws-eks/test/test.nodegroup.ts | 2 +- .../@aws-cdk/aws-eks/test/test.user-data.ts | 2 +- 15 files changed, 54 insertions(+), 49 deletions(-) diff --git a/packages/@aws-cdk/aws-eks/README.md b/packages/@aws-cdk/aws-eks/README.md index 2eae725b53873..13c123a4fb940 100644 --- a/packages/@aws-cdk/aws-eks/README.md +++ b/packages/@aws-cdk/aws-eks/README.md @@ -49,7 +49,7 @@ This example defines an Amazon EKS cluster with the following configuration: ```ts // provisiong a cluster const cluster = new eks.Cluster(this, 'hello-eks', { - version: eks.KubernetesVersion.V1_19, + version: eks.KubernetesVersion.V1_20, }); // apply a kubernetes manifest to the cluster @@ -142,7 +142,7 @@ Creating a new cluster is done using the `Cluster` or `FargateCluster` construct ```ts new eks.Cluster(this, 'HelloEKS', { - version: eks.KubernetesVersion.V1_19, + version: eks.KubernetesVersion.V1_20, }); ``` @@ -150,7 +150,7 @@ You can also use `FargateCluster` to provision a cluster that uses only fargate ```ts new eks.FargateCluster(this, 'HelloEKS', { - version: eks.KubernetesVersion.V1_19, + version: eks.KubernetesVersion.V1_20, }); ``` @@ -174,7 +174,7 @@ At cluster instantiation time, you can customize the number of instances and the ```ts new eks.Cluster(this, 'HelloEKS', { - version: eks.KubernetesVersion.V1_19, + version: eks.KubernetesVersion.V1_20, defaultCapacity: 5, defaultCapacityInstance: ec2.InstanceType.of(ec2.InstanceClass.M5, ec2.InstanceSize.SMALL), }); @@ -186,7 +186,7 @@ Additional customizations are available post instantiation. To apply them, set t ```ts const cluster = new eks.Cluster(this, 'HelloEKS', { - version: eks.KubernetesVersion.V1_19, + version: eks.KubernetesVersion.V1_20, defaultCapacity: 0, }); @@ -322,7 +322,7 @@ The following code defines an Amazon EKS cluster with a default Fargate Profile ```ts const cluster = new eks.FargateCluster(this, 'MyCluster', { - version: eks.KubernetesVersion.V1_19, + version: eks.KubernetesVersion.V1_20, }); ``` @@ -381,7 +381,7 @@ You can also configure the cluster to use an auto-scaling group as the default c ```ts cluster = new eks.Cluster(this, 'HelloEKS', { - version: eks.KubernetesVersion.V1_19, + version: eks.KubernetesVersion.V1_20, defaultCapacityType: eks.DefaultCapacityType.EC2, }); ``` @@ -461,7 +461,7 @@ You can configure the [cluster endpoint access](https://docs.aws.amazon.com/eks/ ```ts const cluster = new eks.Cluster(this, 'hello-eks', { - version: eks.KubernetesVersion.V1_19, + version: eks.KubernetesVersion.V1_20, endpointAccess: eks.EndpointAccess.PRIVATE // No access outside of your VPC. }); ``` @@ -476,7 +476,7 @@ You can specify the VPC of the cluster using the `vpc` and `vpcSubnets` properti const vpc = new ec2.Vpc(this, 'Vpc'); new eks.Cluster(this, 'HelloEKS', { - version: eks.KubernetesVersion.V1_19, + version: eks.KubernetesVersion.V1_20, vpc, vpcSubnets: [{ subnetType: ec2.SubnetType.PRIVATE }] }); @@ -515,7 +515,7 @@ You can configure the environment of this function by specifying it at cluster i ```ts const cluster = new eks.Cluster(this, 'hello-eks', { - version: eks.KubernetesVersion.V1_19, + version: eks.KubernetesVersion.V1_20, clusterHandlerEnvironment: { 'http_proxy': 'http://proxy.myproxy.com' } @@ -532,7 +532,7 @@ You can configure the environment of this function by specifying it at cluster i ```ts const cluster = new eks.Cluster(this, 'hello-eks', { - version: eks.KubernetesVersion.V1_19, + version: eks.KubernetesVersion.V1_20, kubectlEnvironment: { 'http_proxy': 'http://proxy.myproxy.com' } @@ -622,7 +622,7 @@ When you create a cluster, you can specify a `mastersRole`. The `Cluster` constr ```ts const role = new iam.Role(...); new eks.Cluster(this, 'HelloEKS', { - version: eks.KubernetesVersion.V1_19, + version: eks.KubernetesVersion.V1_20, mastersRole: role, }); ``` diff --git a/packages/@aws-cdk/aws-eks/lib/cluster.ts b/packages/@aws-cdk/aws-eks/lib/cluster.ts index 9e9a22d916e56..2f2232861a240 100644 --- a/packages/@aws-cdk/aws-eks/lib/cluster.ts +++ b/packages/@aws-cdk/aws-eks/lib/cluster.ts @@ -639,6 +639,11 @@ export class KubernetesVersion { */ public static readonly V1_19 = KubernetesVersion.of('1.19'); + /** + * Kubernetes version 1.20 + */ + public static readonly V1_20 = KubernetesVersion.of('1.20'); + /** * Custom cluster version * @param version custom version number diff --git a/packages/@aws-cdk/aws-eks/test/example.ssh-into-nodes.lit.ts b/packages/@aws-cdk/aws-eks/test/example.ssh-into-nodes.lit.ts index 14780774ca44f..9b1ea16a0fed3 100644 --- a/packages/@aws-cdk/aws-eks/test/example.ssh-into-nodes.lit.ts +++ b/packages/@aws-cdk/aws-eks/test/example.ssh-into-nodes.lit.ts @@ -10,7 +10,7 @@ class EksClusterStack extends cdk.Stack { const cluster = new eks.Cluster(this, 'EKSCluster', { vpc, - version: eks.KubernetesVersion.V1_19, + version: eks.KubernetesVersion.V1_20, }); /// !show diff --git a/packages/@aws-cdk/aws-eks/test/integ.eks-cluster-handlers-vpc.expected.json b/packages/@aws-cdk/aws-eks/test/integ.eks-cluster-handlers-vpc.expected.json index 7f612f0788b59..8b901e2f20436 100644 --- a/packages/@aws-cdk/aws-eks/test/integ.eks-cluster-handlers-vpc.expected.json +++ b/packages/@aws-cdk/aws-eks/test/integ.eks-cluster-handlers-vpc.expected.json @@ -818,7 +818,7 @@ ] }, "Config": { - "version": "1.19", + "version": "1.20", "roleArn": { "Fn::GetAtt": [ "EksAllHandlersInVpcStackRoleC36F09F0", diff --git a/packages/@aws-cdk/aws-eks/test/integ.eks-cluster-handlers-vpc.ts b/packages/@aws-cdk/aws-eks/test/integ.eks-cluster-handlers-vpc.ts index cd1682a30d318..c7434dd1f93ac 100644 --- a/packages/@aws-cdk/aws-eks/test/integ.eks-cluster-handlers-vpc.ts +++ b/packages/@aws-cdk/aws-eks/test/integ.eks-cluster-handlers-vpc.ts @@ -3,7 +3,7 @@ import { App } from '@aws-cdk/core'; import * as eks from '../lib'; import { TestStack } from './util'; -const CLUSTER_VERSION = eks.KubernetesVersion.V1_19; +const CLUSTER_VERSION = eks.KubernetesVersion.V1_20; class EksAllHandlersInVpcStack extends TestStack { diff --git a/packages/@aws-cdk/aws-eks/test/integ.eks-cluster-private-endpoint.expected.json b/packages/@aws-cdk/aws-eks/test/integ.eks-cluster-private-endpoint.expected.json index 8f59f96ca7a6b..82a9699ed1d0b 100644 --- a/packages/@aws-cdk/aws-eks/test/integ.eks-cluster-private-endpoint.expected.json +++ b/packages/@aws-cdk/aws-eks/test/integ.eks-cluster-private-endpoint.expected.json @@ -758,7 +758,7 @@ ] }, "Config": { - "version": "1.19", + "version": "1.20", "roleArn": { "Fn::GetAtt": [ "ClusterRoleFA261979", diff --git a/packages/@aws-cdk/aws-eks/test/integ.eks-cluster-private-endpoint.ts b/packages/@aws-cdk/aws-eks/test/integ.eks-cluster-private-endpoint.ts index a9cb68866e5d8..ab25232b25882 100644 --- a/packages/@aws-cdk/aws-eks/test/integ.eks-cluster-private-endpoint.ts +++ b/packages/@aws-cdk/aws-eks/test/integ.eks-cluster-private-endpoint.ts @@ -5,7 +5,7 @@ import { App } from '@aws-cdk/core'; import * as eks from '../lib'; import { TestStack } from './util'; -const CLUSTER_VERSION = eks.KubernetesVersion.V1_19; +const CLUSTER_VERSION = eks.KubernetesVersion.V1_20; class EksClusterStack extends TestStack { diff --git a/packages/@aws-cdk/aws-eks/test/integ.eks-cluster.expected.json b/packages/@aws-cdk/aws-eks/test/integ.eks-cluster.expected.json index de8e6fe0bf8ae..f8185453880b9 100644 --- a/packages/@aws-cdk/aws-eks/test/integ.eks-cluster.expected.json +++ b/packages/@aws-cdk/aws-eks/test/integ.eks-cluster.expected.json @@ -919,7 +919,7 @@ ] }, "Config": { - "version": "1.19", + "version": "1.20", "roleArn": { "Fn::GetAtt": [ "ClusterRoleFA261979", @@ -1653,7 +1653,7 @@ "Type": "AWS::AutoScaling::LaunchConfiguration", "Properties": { "ImageId": { - "Ref": "SsmParameterValueawsserviceeksoptimizedami119amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter" + "Ref": "SsmParameterValueawsserviceeksoptimizedami120amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter" }, "InstanceType": "t2.medium", "IamInstanceProfile": { @@ -1978,7 +1978,7 @@ "Type": "AWS::AutoScaling::LaunchConfiguration", "Properties": { "ImageId": { - "Ref": "SsmParameterValueawsserviceeksoptimizedami119amazonlinux2arm64recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter" + "Ref": "SsmParameterValueawsserviceeksoptimizedami120amazonlinux2arm64recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter" }, "InstanceType": "m6g.medium", "IamInstanceProfile": { @@ -2303,7 +2303,7 @@ "Type": "AWS::AutoScaling::LaunchConfiguration", "Properties": { "ImageId": { - "Ref": "SsmParameterValueawsservicebottlerocketawsk8s119x8664latestimageidC96584B6F00A464EAD1953AFF4B05118Parameter" + "Ref": "SsmParameterValueawsservicebottlerocketawsk8s120x8664latestimageidC96584B6F00A464EAD1953AFF4B05118Parameter" }, "InstanceType": "t3.small", "IamInstanceProfile": { @@ -2628,7 +2628,7 @@ "Type": "AWS::AutoScaling::LaunchConfiguration", "Properties": { "ImageId": { - "Ref": "SsmParameterValueawsserviceeksoptimizedami119amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter" + "Ref": "SsmParameterValueawsserviceeksoptimizedami120amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter" }, "InstanceType": "t3.large", "IamInstanceProfile": { @@ -2986,7 +2986,7 @@ "Type": "AWS::AutoScaling::LaunchConfiguration", "Properties": { "ImageId": { - "Ref": "SsmParameterValueawsserviceeksoptimizedami119amazonlinux2gpurecommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter" + "Ref": "SsmParameterValueawsserviceeksoptimizedami120amazonlinux2gpurecommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter" }, "InstanceType": "inf1.2xlarge", "IamInstanceProfile": { @@ -4029,7 +4029,7 @@ "Properties": { "LaunchTemplateData": { "ImageId": { - "Ref": "SsmParameterValueawsserviceeksoptimizedami119amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter" + "Ref": "SsmParameterValueawsserviceeksoptimizedami120amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter" }, "InstanceType": "t3.small", "UserData": { @@ -4778,21 +4778,21 @@ "Type": "String", "Description": "Artifact hash for asset \"e334ff007b5126b62d66d4baac94004f4281dc2b0b0c4685ec93e330cb59e921\"" }, - "SsmParameterValueawsserviceeksoptimizedami119amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { + "SsmParameterValueawsserviceeksoptimizedami120amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { "Type": "AWS::SSM::Parameter::Value", - "Default": "/aws/service/eks/optimized-ami/1.19/amazon-linux-2/recommended/image_id" + "Default": "/aws/service/eks/optimized-ami/1.20/amazon-linux-2/recommended/image_id" }, - "SsmParameterValueawsserviceeksoptimizedami119amazonlinux2arm64recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { + "SsmParameterValueawsserviceeksoptimizedami120amazonlinux2arm64recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { "Type": "AWS::SSM::Parameter::Value", - "Default": "/aws/service/eks/optimized-ami/1.19/amazon-linux-2-arm64/recommended/image_id" + "Default": "/aws/service/eks/optimized-ami/1.20/amazon-linux-2-arm64/recommended/image_id" }, - "SsmParameterValueawsservicebottlerocketawsk8s119x8664latestimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { + "SsmParameterValueawsservicebottlerocketawsk8s120x8664latestimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { "Type": "AWS::SSM::Parameter::Value", - "Default": "/aws/service/bottlerocket/aws-k8s-1.19/x86_64/latest/image_id" + "Default": "/aws/service/bottlerocket/aws-k8s-1.20/x86_64/latest/image_id" }, - "SsmParameterValueawsserviceeksoptimizedami119amazonlinux2gpurecommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { + "SsmParameterValueawsserviceeksoptimizedami120amazonlinux2gpurecommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { "Type": "AWS::SSM::Parameter::Value", - "Default": "/aws/service/eks/optimized-ami/1.19/amazon-linux-2-gpu/recommended/image_id" + "Default": "/aws/service/eks/optimized-ami/1.20/amazon-linux-2-gpu/recommended/image_id" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-eks/test/integ.eks-cluster.ts b/packages/@aws-cdk/aws-eks/test/integ.eks-cluster.ts index 080cd744a57f0..04579ed71e922 100644 --- a/packages/@aws-cdk/aws-eks/test/integ.eks-cluster.ts +++ b/packages/@aws-cdk/aws-eks/test/integ.eks-cluster.ts @@ -35,7 +35,7 @@ class EksClusterStack extends TestStack { vpc: this.vpc, mastersRole, defaultCapacity: 2, - version: eks.KubernetesVersion.V1_19, + version: eks.KubernetesVersion.V1_20, secretsEncryptionKey, }); @@ -188,7 +188,7 @@ class EksClusterStack extends TestStack { const lt = new ec2.CfnLaunchTemplate(this, 'LaunchTemplate', { launchTemplateData: { imageId: new eks.EksOptimizedImage({ - kubernetesVersion: eks.KubernetesVersion.V1_19.version, + kubernetesVersion: eks.KubernetesVersion.V1_20.version, }).getImage(this).imageId, instanceType: new ec2.InstanceType('t3.small').toString(), userData: Fn.base64(userData.render()), diff --git a/packages/@aws-cdk/aws-eks/test/integ.fargate-cluster.expected.json b/packages/@aws-cdk/aws-eks/test/integ.fargate-cluster.expected.json index c8d1c3a68bdf9..624ed521e7a6e 100644 --- a/packages/@aws-cdk/aws-eks/test/integ.fargate-cluster.expected.json +++ b/packages/@aws-cdk/aws-eks/test/integ.fargate-cluster.expected.json @@ -828,7 +828,7 @@ ] }, "Config": { - "version": "1.19", + "version": "1.20", "roleArn": { "Fn::GetAtt": [ "FargateClusterRole8E36B33A", diff --git a/packages/@aws-cdk/aws-eks/test/integ.fargate-cluster.ts b/packages/@aws-cdk/aws-eks/test/integ.fargate-cluster.ts index 957b60ee23e3b..8fe5666c76dc8 100644 --- a/packages/@aws-cdk/aws-eks/test/integ.fargate-cluster.ts +++ b/packages/@aws-cdk/aws-eks/test/integ.fargate-cluster.ts @@ -3,7 +3,7 @@ import { App } from '@aws-cdk/core'; import * as eks from '../lib'; import { TestStack } from './util'; -const CLUSTER_VERSION = eks.KubernetesVersion.V1_19; +const CLUSTER_VERSION = eks.KubernetesVersion.V1_20; class EksFargateClusterStack extends TestStack { @@ -22,4 +22,4 @@ const app = new App(); new EksFargateClusterStack(app, 'aws-cdk-eks-fargate-cluster-test'); -app.synth(); \ No newline at end of file +app.synth(); diff --git a/packages/@aws-cdk/aws-eks/test/test.cluster.ts b/packages/@aws-cdk/aws-eks/test/test.cluster.ts index 3a1866d6f460b..61eef3a2bed13 100644 --- a/packages/@aws-cdk/aws-eks/test/test.cluster.ts +++ b/packages/@aws-cdk/aws-eks/test/test.cluster.ts @@ -17,7 +17,7 @@ import { testFixture, testFixtureNoVpc } from './util'; /* eslint-disable max-len */ -const CLUSTER_VERSION = eks.KubernetesVersion.V1_19; +const CLUSTER_VERSION = eks.KubernetesVersion.V1_20; export = { @@ -85,7 +85,7 @@ export = { vpc: vpc, vpcSubnets: [{ subnetType: ec2.SubnetType.PUBLIC }, { subnetType: ec2.SubnetType.PRIVATE }], defaultCapacity: 0, - version: eks.KubernetesVersion.V1_19, + version: eks.KubernetesVersion.V1_20, }), /cannot select multiple subnet groups/); test.done(); @@ -97,7 +97,7 @@ export = { vpc: vpc, vpcSubnets: [{ subnetType: ec2.SubnetType.PUBLIC }], defaultCapacity: 0, - version: eks.KubernetesVersion.V1_19, + version: eks.KubernetesVersion.V1_20, }); // THEN @@ -629,7 +629,7 @@ export = { expect(stack).to(haveResourceLike('Custom::AWSCDK-EKS-Cluster', { Config: { roleArn: { 'Fn::GetAtt': ['ClusterRoleFA261979', 'Arn'] }, - version: '1.19', + version: '1.20', resourcesVpcConfig: { securityGroupIds: [{ 'Fn::GetAtt': ['ClusterControlPlaneSecurityGroupD274242C', 'GroupId'] }], subnetIds: [ @@ -1462,7 +1462,7 @@ export = { const { app, stack } = testFixtureNoVpc(); // WHEN - new eks.EksOptimizedImage({ kubernetesVersion: '1.19' }).getImage(stack); + new eks.EksOptimizedImage({ kubernetesVersion: '1.20' }).getImage(stack); // THEN const assembly = app.synth(); @@ -1473,7 +1473,7 @@ export = { ), 'EKS STANDARD AMI should be in ssm parameters'); test.ok(Object.entries(parameters).some( ([k, v]) => k.startsWith('SsmParameterValueawsserviceeksoptimizedami') && - (v as any).Default.includes('/1.19/'), + (v as any).Default.includes('/1.20/'), ), 'kubernetesVersion should be in ssm parameters'); test.done(); }, @@ -1611,7 +1611,7 @@ export = { const { app, stack } = testFixtureNoVpc(); // WHEN - new BottleRocketImage({ kubernetesVersion: '1.19' }).getImage(stack); + new BottleRocketImage({ kubernetesVersion: '1.20' }).getImage(stack); // THEN const assembly = app.synth(); @@ -1622,7 +1622,7 @@ export = { ), 'BottleRocket AMI should be in ssm parameters'); test.ok(Object.entries(parameters).some( ([k, v]) => k.startsWith('SsmParameterValueawsservicebottlerocketaws') && - (v as any).Default.includes('/aws-k8s-1.19/'), + (v as any).Default.includes('/aws-k8s-1.20/'), ), 'kubernetesVersion should be in ssm parameters'); test.done(); }, @@ -1643,7 +1643,7 @@ export = { Config: { name: 'my-cluster-name', roleArn: { 'Fn::GetAtt': ['MyClusterRoleBA20FE72', 'Arn'] }, - version: '1.19', + version: '1.20', resourcesVpcConfig: { securityGroupIds: [ { 'Fn::GetAtt': ['MyClusterControlPlaneSecurityGroup6B658F79', 'GroupId'] }, diff --git a/packages/@aws-cdk/aws-eks/test/test.fargate.ts b/packages/@aws-cdk/aws-eks/test/test.fargate.ts index 5dabba46d0157..e21160386c556 100644 --- a/packages/@aws-cdk/aws-eks/test/test.fargate.ts +++ b/packages/@aws-cdk/aws-eks/test/test.fargate.ts @@ -7,7 +7,7 @@ import { Test } from 'nodeunit'; import * as eks from '../lib'; -const CLUSTER_VERSION = eks.KubernetesVersion.V1_19; +const CLUSTER_VERSION = eks.KubernetesVersion.V1_20; export = { 'can be added to a cluster'(test: Test) { diff --git a/packages/@aws-cdk/aws-eks/test/test.nodegroup.ts b/packages/@aws-cdk/aws-eks/test/test.nodegroup.ts index e82586d1cc79a..a7f76a86bfe6a 100644 --- a/packages/@aws-cdk/aws-eks/test/test.nodegroup.ts +++ b/packages/@aws-cdk/aws-eks/test/test.nodegroup.ts @@ -7,7 +7,7 @@ import { testFixture } from './util'; /* eslint-disable max-len */ -const CLUSTER_VERSION = eks.KubernetesVersion.V1_19; +const CLUSTER_VERSION = eks.KubernetesVersion.V1_20; export = { diff --git a/packages/@aws-cdk/aws-eks/test/test.user-data.ts b/packages/@aws-cdk/aws-eks/test/test.user-data.ts index ce582a374a3fc..ce5c61539c1a8 100644 --- a/packages/@aws-cdk/aws-eks/test/test.user-data.ts +++ b/packages/@aws-cdk/aws-eks/test/test.user-data.ts @@ -367,7 +367,7 @@ function newFixtures(spot = false) { const stack = new Stack(app, 'my-stack', { env: { region: 'us-west-33' } }); const vpc = new ec2.Vpc(stack, 'vpc'); const cluster = new Cluster(stack, 'cluster', { - version: KubernetesVersion.V1_19, + version: KubernetesVersion.V1_20, clusterName: 'my-cluster-name', vpc, }); From a66fe88086742a3ea42316e5e5e0f00739488be6 Mon Sep 17 00:00:00 2001 From: Eli Polonsky Date: Wed, 2 Jun 2021 13:26:30 +0300 Subject: [PATCH 112/134] chore: auto-approve to run on pull_request_target (#14939) --- .github/workflows/auto-approve.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/auto-approve.yml b/.github/workflows/auto-approve.yml index cf881f5bf20f2..dc3e7febfcf07 100644 --- a/.github/workflows/auto-approve.yml +++ b/.github/workflows/auto-approve.yml @@ -2,7 +2,7 @@ name: auto-approve on: - pull_request: + pull_request_target: types: [ labeled, unlabeled, opened, synchronize, reopened, ready_for_review, review_requested ] jobs: From 3716eedb7cff9023fd6a2aa9fac4d38877692ded Mon Sep 17 00:00:00 2001 From: Unnati Parekh <80710604+upparekh@users.noreply.github.com> Date: Wed, 2 Jun 2021 03:52:42 -0700 Subject: [PATCH 113/134] chore(logs): Adding a public method to access the physical name of the log group (#14947) This PR adds a public method `logGroupPhysicalName()` to access the physical name of a log group which is a private property of the `Resource` class. This change is needed to enable using KMS keys with log groups for use with ECS exec. Related: https://github.com/aws/aws-cdk/issues/13618 ---- *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/lib/log-group.ts | 13 +++++++++++++ .../@aws-cdk/aws-logs/test/test.loggroup.ts | 18 ++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/packages/@aws-cdk/aws-logs/lib/log-group.ts b/packages/@aws-cdk/aws-logs/lib/log-group.ts index 8b24506f11ba0..4c74dbf02f3ee 100644 --- a/packages/@aws-cdk/aws-logs/lib/log-group.ts +++ b/packages/@aws-cdk/aws-logs/lib/log-group.ts @@ -72,6 +72,11 @@ export interface ILogGroup extends IResource { * Give the indicated permissions on this log group and all streams */ grant(grantee: iam.IGrantable, ...actions: string[]): iam.Grant; + + /** + * Public method to get the physical name of this log group + */ + logGroupPhysicalName(): string; } /** @@ -173,6 +178,14 @@ abstract class LogGroupBase extends Resource implements ILogGroup { scope: this, }); } + + /** + * Public method to get the physical name of this log group + * @returns Physical name of log group + */ + public logGroupPhysicalName(): string { + return this.physicalName; + } } /** diff --git a/packages/@aws-cdk/aws-logs/test/test.loggroup.ts b/packages/@aws-cdk/aws-logs/test/test.loggroup.ts index 7c1e66c339d10..78c812e8a3bbd 100644 --- a/packages/@aws-cdk/aws-logs/test/test.loggroup.ts +++ b/packages/@aws-cdk/aws-logs/test/test.loggroup.ts @@ -335,6 +335,24 @@ export = { test.done(); }, + + 'correctly returns physical name of the log group'(test: Test) { + // GIVEN + const stack = new Stack(); + + // WHEN + const logGroup = new LogGroup(stack, 'LogGroup', { + logGroupName: 'my-log-group', + }); + + // THEN + test.equal(logGroup.logGroupPhysicalName(), 'my-log-group'); + expect(stack).to(haveResource('AWS::Logs::LogGroup', { + LogGroupName: 'my-log-group', + })); + + test.done(); + }, }; function dataDrivenTests(cases: any[][], body: (test: Test, ...args: any[]) => void) { From 49d2bd18ad632414d0c91dbc0ba6e17d6278274f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Jun 2021 18:50:07 +0300 Subject: [PATCH 114/134] chore(deps): bump actions/cache from 2.1.5 to 2.1.6 (#14922) Bumps [actions/cache](https://github.com/actions/cache) from 2.1.5 to 2.1.6. - [Release notes](https://github.com/actions/cache/releases) - [Commits](https://github.com/actions/cache/compare/v2.1.5...v2.1.6) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/yarn-upgrade.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/yarn-upgrade.yml b/.github/workflows/yarn-upgrade.yml index 473e9423051cb..20d9073b02b3d 100644 --- a/.github/workflows/yarn-upgrade.yml +++ b/.github/workflows/yarn-upgrade.yml @@ -27,7 +27,7 @@ jobs: run: echo "::set-output name=dir::$(yarn cache dir)" - name: Restore Yarn cache - uses: actions/cache@v2.1.5 + uses: actions/cache@v2.1.6 with: path: ${{ steps.yarn-cache.outputs.dir }} key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} From 8c896fac2dec16609d62a0caf204f7441f93dec5 Mon Sep 17 00:00:00 2001 From: AWS CDK Team Date: Wed, 2 Jun 2021 16:34:32 +0000 Subject: [PATCH 115/134] chore(release): 1.107.0 --- CHANGELOG.md | 28 ++++++++++++++++++++++++++++ version.v1.json | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ffd56de3683ed..9ded9ccd3b591 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,34 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [1.107.0](https://github.com/aws/aws-cdk/compare/v1.106.1...v1.107.0) (2021-06-02) + + +### ⚠ BREAKING CHANGES TO EXPERIMENTAL FEATURES + +* **appmesh:** the creation property `clientPolicy` in `VirtualNode` has been renamed to `tlsClientPolicy`, and its type changed to `TlsClientPolicy` +- **appmesh**: the creation property `clientPolicy` in `VirtualGateway` has been renamed to `tlsClientPolicy`, and its type changed to `TlsClientPolicy` +- **appmesh**: to create `TlsClientPolicy`, `validation` property must be defined. +* **appmesh:** the creation property `tlsCertificate` in `VirtualNode` has been renamed to `tls`, and its type changed to `TlsListener` +- **appmesh**: the creation property `tlsCertificate` in `VirtualGatewayListener` has been renamed to `tls`, and its type changed to `TlsListener` +- **appmesh**: the `tlsMode` property has been removed from the options when creating a `TlsCertificate`, moved to the new `TlsListener` interface, and renamed `mode` + +### Features + +* **cfnspec:** cloudformation spec v37.0.0 ([#14873](https://github.com/aws/aws-cdk/issues/14873)) ([8bb4357](https://github.com/aws/aws-cdk/commit/8bb4357036f549af1235de81f2f5c528f5fa80f8)) +* **cloudfront:** add L2 support for CloudFront functions ([#14511](https://github.com/aws/aws-cdk/issues/14511)) ([40d2ff9](https://github.com/aws/aws-cdk/commit/40d2ff964c97954c70d79a09d60fcb795ef16791)) +* **eks:** support Kubernetes 1.20 ([#14758](https://github.com/aws/aws-cdk/issues/14758)) ([1956ef6](https://github.com/aws/aws-cdk/commit/1956ef6708d59329da61fbdd6056de4727e1e2e1)), closes [#14756](https://github.com/aws/aws-cdk/issues/14756) +* **pipelines:** allow switching to one CodeBuild action for same-typed assets ([#13803](https://github.com/aws/aws-cdk/issues/13803)) ([ed72ad3](https://github.com/aws/aws-cdk/commit/ed72ad322a2739709cad91759ea18e159f28f795)) + + +### Bug Fixes + +* **appmesh:** introduce the TlsClientPolicy and TlsValidation concepts ([#14782](https://github.com/aws/aws-cdk/issues/14782)) ([8263c78](https://github.com/aws/aws-cdk/commit/8263c788a8e71006a4b2dce0f37444199de9c435)), closes [issue#12733](https://github.com/aws/issue/issues/12733) [#12733](https://github.com/aws/aws-cdk/issues/12733) +* **appmesh:** TLS mode is set on the Certificate class ([#14856](https://github.com/aws/aws-cdk/issues/14856)) ([061fd55](https://github.com/aws/aws-cdk/commit/061fd558a3327b805bb5fe0abc72de7c21bbbdb9)), closes [issue#12733](https://github.com/aws/issue/issues/12733) [/github.com/aws/aws-cdk/pull/14782#discussion_r636334863](https://github.com/aws//github.com/aws/aws-cdk/pull/14782/issues/discussion_r636334863) +* **elasticsearch:** 'r6gd' not marked as supported type for instance storage ([#14894](https://github.com/aws/aws-cdk/issues/14894)) ([d07a49f](https://github.com/aws/aws-cdk/commit/d07a49ff00ae07ea013ce6cc83d768e7729225a8)), closes [#14773](https://github.com/aws/aws-cdk/issues/14773) +* **lambda-nodejs:** cannot bundle locally when consuming a node module with a NodejsFunction ([#14914](https://github.com/aws/aws-cdk/issues/14914)) ([52da59c](https://github.com/aws/aws-cdk/commit/52da59c34c4be74d696af0637521eeb0d6e69fa9)), closes [#14739](https://github.com/aws/aws-cdk/issues/14739) +* **rds:** Add exception throw when az is defined for multi-az db instance ([#14837](https://github.com/aws/aws-cdk/issues/14837)) ([fd8445f](https://github.com/aws/aws-cdk/commit/fd8445ff1bf94b3dde26211c497bda7211b54dc0)), closes [#10949](https://github.com/aws/aws-cdk/issues/10949) [#10949](https://github.com/aws/aws-cdk/issues/10949) + ## [1.106.1](https://github.com/aws/aws-cdk/compare/v1.106.0...v1.106.1) (2021-05-26) diff --git a/version.v1.json b/version.v1.json index 0a4e31e665886..58a27f352558f 100644 --- a/version.v1.json +++ b/version.v1.json @@ -1,3 +1,3 @@ { - "version": "1.106.1" + "version": "1.107.0" } From ff880feed0a79d2406206010e01047f2d249047a Mon Sep 17 00:00:00 2001 From: Eli Polonsky Date: Wed, 2 Jun 2021 19:40:51 +0300 Subject: [PATCH 116/134] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ded9ccd3b591..541b8aeced77d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,7 +25,7 @@ All notable changes to this project will be documented in this file. See [standa ### Bug Fixes * **appmesh:** introduce the TlsClientPolicy and TlsValidation concepts ([#14782](https://github.com/aws/aws-cdk/issues/14782)) ([8263c78](https://github.com/aws/aws-cdk/commit/8263c788a8e71006a4b2dce0f37444199de9c435)), closes [issue#12733](https://github.com/aws/issue/issues/12733) [#12733](https://github.com/aws/aws-cdk/issues/12733) -* **appmesh:** TLS mode is set on the Certificate class ([#14856](https://github.com/aws/aws-cdk/issues/14856)) ([061fd55](https://github.com/aws/aws-cdk/commit/061fd558a3327b805bb5fe0abc72de7c21bbbdb9)), closes [issue#12733](https://github.com/aws/issue/issues/12733) [/github.com/aws/aws-cdk/pull/14782#discussion_r636334863](https://github.com/aws//github.com/aws/aws-cdk/pull/14782/issues/discussion_r636334863) +* **appmesh:** TLS mode is set on the Certificate class ([#14856](https://github.com/aws/aws-cdk/issues/14856)) ([061fd55](https://github.com/aws/aws-cdk/commit/061fd558a3327b805bb5fe0abc72de7c21bbbdb9)) * **elasticsearch:** 'r6gd' not marked as supported type for instance storage ([#14894](https://github.com/aws/aws-cdk/issues/14894)) ([d07a49f](https://github.com/aws/aws-cdk/commit/d07a49ff00ae07ea013ce6cc83d768e7729225a8)), closes [#14773](https://github.com/aws/aws-cdk/issues/14773) * **lambda-nodejs:** cannot bundle locally when consuming a node module with a NodejsFunction ([#14914](https://github.com/aws/aws-cdk/issues/14914)) ([52da59c](https://github.com/aws/aws-cdk/commit/52da59c34c4be74d696af0637521eeb0d6e69fa9)), closes [#14739](https://github.com/aws/aws-cdk/issues/14739) * **rds:** Add exception throw when az is defined for multi-az db instance ([#14837](https://github.com/aws/aws-cdk/issues/14837)) ([fd8445f](https://github.com/aws/aws-cdk/commit/fd8445ff1bf94b3dde26211c497bda7211b54dc0)), closes [#10949](https://github.com/aws/aws-cdk/issues/10949) [#10949](https://github.com/aws/aws-cdk/issues/10949) From 7d0601d8ddca579902cea3077de6dcb8c340fb9f Mon Sep 17 00:00:00 2001 From: Eli Polonsky Date: Wed, 2 Jun 2021 19:44:27 +0300 Subject: [PATCH 117/134] Update CHANGELOG.md --- CHANGELOG.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 541b8aeced77d..2fb542c6dd026 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,11 +8,9 @@ All notable changes to this project will be documented in this file. See [standa ### ⚠ BREAKING CHANGES TO EXPERIMENTAL FEATURES * **appmesh:** the creation property `clientPolicy` in `VirtualNode` has been renamed to `tlsClientPolicy`, and its type changed to `TlsClientPolicy` -- **appmesh**: the creation property `clientPolicy` in `VirtualGateway` has been renamed to `tlsClientPolicy`, and its type changed to `TlsClientPolicy` -- **appmesh**: to create `TlsClientPolicy`, `validation` property must be defined. +* **appmesh:** to create `TlsClientPolicy`, `validation` property must be defined. * **appmesh:** the creation property `tlsCertificate` in `VirtualNode` has been renamed to `tls`, and its type changed to `TlsListener` -- **appmesh**: the creation property `tlsCertificate` in `VirtualGatewayListener` has been renamed to `tls`, and its type changed to `TlsListener` -- **appmesh**: the `tlsMode` property has been removed from the options when creating a `TlsCertificate`, moved to the new `TlsListener` interface, and renamed `mode` +* **appmesh:** the `tlsMode` property has been removed from the options when creating a `TlsCertificate`, moved to the new `TlsListener` interface, and renamed `mode` ### Features @@ -24,7 +22,7 @@ All notable changes to this project will be documented in this file. See [standa ### Bug Fixes -* **appmesh:** introduce the TlsClientPolicy and TlsValidation concepts ([#14782](https://github.com/aws/aws-cdk/issues/14782)) ([8263c78](https://github.com/aws/aws-cdk/commit/8263c788a8e71006a4b2dce0f37444199de9c435)), closes [issue#12733](https://github.com/aws/issue/issues/12733) [#12733](https://github.com/aws/aws-cdk/issues/12733) +* **appmesh:** introduce the TlsClientPolicy and TlsValidation concepts ([#14782](https://github.com/aws/aws-cdk/issues/14782)) ([8263c78](https://github.com/aws/aws-cdk/commit/8263c788a8e71006a4b2dce0f37444199de9c435)), closes [#12733](https://github.com/aws/aws-cdk/issues/12733) * **appmesh:** TLS mode is set on the Certificate class ([#14856](https://github.com/aws/aws-cdk/issues/14856)) ([061fd55](https://github.com/aws/aws-cdk/commit/061fd558a3327b805bb5fe0abc72de7c21bbbdb9)) * **elasticsearch:** 'r6gd' not marked as supported type for instance storage ([#14894](https://github.com/aws/aws-cdk/issues/14894)) ([d07a49f](https://github.com/aws/aws-cdk/commit/d07a49ff00ae07ea013ce6cc83d768e7729225a8)), closes [#14773](https://github.com/aws/aws-cdk/issues/14773) * **lambda-nodejs:** cannot bundle locally when consuming a node module with a NodejsFunction ([#14914](https://github.com/aws/aws-cdk/issues/14914)) ([52da59c](https://github.com/aws/aws-cdk/commit/52da59c34c4be74d696af0637521eeb0d6e69fa9)), closes [#14739](https://github.com/aws/aws-cdk/issues/14739) From 957a205efcb732760fd904b939ae6f3b4a7597be Mon Sep 17 00:00:00 2001 From: Eli Polonsky Date: Wed, 2 Jun 2021 19:45:21 +0300 Subject: [PATCH 118/134] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fb542c6dd026..48c085f2b3080 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,7 @@ All notable changes to this project will be documented in this file. See [standa * **appmesh:** TLS mode is set on the Certificate class ([#14856](https://github.com/aws/aws-cdk/issues/14856)) ([061fd55](https://github.com/aws/aws-cdk/commit/061fd558a3327b805bb5fe0abc72de7c21bbbdb9)) * **elasticsearch:** 'r6gd' not marked as supported type for instance storage ([#14894](https://github.com/aws/aws-cdk/issues/14894)) ([d07a49f](https://github.com/aws/aws-cdk/commit/d07a49ff00ae07ea013ce6cc83d768e7729225a8)), closes [#14773](https://github.com/aws/aws-cdk/issues/14773) * **lambda-nodejs:** cannot bundle locally when consuming a node module with a NodejsFunction ([#14914](https://github.com/aws/aws-cdk/issues/14914)) ([52da59c](https://github.com/aws/aws-cdk/commit/52da59c34c4be74d696af0637521eeb0d6e69fa9)), closes [#14739](https://github.com/aws/aws-cdk/issues/14739) -* **rds:** Add exception throw when az is defined for multi-az db instance ([#14837](https://github.com/aws/aws-cdk/issues/14837)) ([fd8445f](https://github.com/aws/aws-cdk/commit/fd8445ff1bf94b3dde26211c497bda7211b54dc0)), closes [#10949](https://github.com/aws/aws-cdk/issues/10949) [#10949](https://github.com/aws/aws-cdk/issues/10949) +* **rds:** Add exception throw when az is defined for multi-az db instance ([#14837](https://github.com/aws/aws-cdk/issues/14837)) ([fd8445f](https://github.com/aws/aws-cdk/commit/fd8445ff1bf94b3dde26211c497bda7211b54dc0)), closes [#10949](https://github.com/aws/aws-cdk/issues/10949) ## [1.106.1](https://github.com/aws/aws-cdk/compare/v1.106.0...v1.106.1) (2021-05-26) From 093ebcb942cd789b2516b4b3f577db3c9c1766c4 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Wed, 2 Jun 2021 22:25:20 +0200 Subject: [PATCH 119/134] chore: eks -> Otavio, ec2 -> Nick (#14955) Assign new module ownerships --- .github/workflows/issue-label-assign.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/issue-label-assign.yml b/.github/workflows/issue-label-assign.yml index 08d852b209784..d8053118ca4f0 100644 --- a/.github/workflows/issue-label-assign.yml +++ b/.github/workflows/issue-label-assign.yml @@ -87,14 +87,14 @@ jobs: {"keywords":["(@aws-cdk/aws-docdb)","(aws-docdb)","(docdb)","(doc db)","(doc-db)"],"labels":["@aws-cdk/aws-docdb"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-dynamodb)","(aws-dynamodb)","(dynamodb)","(dynamo db)","(dynamo-db)"],"labels":["@aws-cdk/aws-dynamodb"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-dynamodb-global)","(aws-dynamodb-global)","(dynamodb-global)","(dynamodb global)"],"labels":["@aws-cdk/aws-dynamodb-global"],"assignees":["skinny85"]}, - {"keywords":["(@aws-cdk/aws-ec2)","(aws-ec2)","(ec2)","(vpc)"],"labels":["@aws-cdk/aws-ec2"],"assignees":["rix0rrr"]}, + {"keywords":["(@aws-cdk/aws-ec2)","(aws-ec2)","(ec2)","(vpc)"],"labels":["@aws-cdk/aws-ec2"],"assignees":["njlynch"]}, {"keywords":["(@aws-cdk/aws-ecr)","(aws-ecr)","(ecr)"],"labels":["@aws-cdk/aws-ecr"],"assignees":["madeline-k"]}, {"keywords":["(@aws-cdk/aws-ecr-assets)","(aws-ecr-assets)","(ecr-assets)","(ecr assets)","(ecrassets)"],"labels":["@aws-cdk/aws-ecr-assets"],"assignees":["eladb"]}, {"keywords":["(@aws-cdk/aws-ecs)","(aws-ecs)","(ecs)"],"labels":["@aws-cdk/aws-ecs"],"assignees":["madeline-k"]}, {"keywords":["(@aws-cdk/aws-ecs-patterns)","(aws-ecs-patterns)","(ecs-patterns)"],"labels":["@aws-cdk/aws-ecs-patterns"],"assignees":["madeline-k"]}, {"keywords":["(@aws-cdk/aws-efs)","(aws-efs)","(efs)"],"labels":["@aws-cdk/aws-efs"],"assignees":["nija-at"]}, - {"keywords":["(@aws-cdk/aws-eks)","(aws-eks)","(eks)"],"labels":["@aws-cdk/aws-eks"],"assignees":["iliapolo"]}, - {"keywords":["(@aws-cdk/aws-eks-legacy)","(aws-eks-legacy)","(eks-legacy)"],"labels":["@aws-cdk/aws-eks-legacy"],"assignees":["iliapolo"]}, + {"keywords":["(@aws-cdk/aws-eks)","(aws-eks)","(eks)"],"labels":["@aws-cdk/aws-eks"],"assignees":["otaviomacedo"]}, + {"keywords":["(@aws-cdk/aws-eks-legacy)","(aws-eks-legacy)","(eks-legacy)"],"labels":["@aws-cdk/aws-eks-legacy"],"assignees":["otaviomacedo"]}, {"keywords":["(@aws-cdk/aws-elasticache)","(aws-elasticache)","(elasticache)","(elastic cache)","(elastic-cache)"],"labels":["@aws-cdk/aws-elasticache"],"assignees":["otaviomacedo"]}, {"keywords":["(@aws-cdk/aws-elasticbeanstalk)","(aws-elasticbeanstalk)","(elasticbeanstalk)","(elastic beanstalk)","(elastic-beanstalk)"],"labels":["@aws-cdk/aws-elasticbeanstalk"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-elasticloadbalancing)","(aws-elasticloadbalancing)","(elasticloadbalancing)","(elastic loadbalancing)","(elastic-loadbalancing)","(elb)"],"labels":["@aws-cdk/aws-elasticloadbalancing"],"assignees":["njlynch"]}, From df16d40352e56c2d4b33b2066f3fe030792d32d6 Mon Sep 17 00:00:00 2001 From: Reagan Elm <1347066+relm923@users.noreply.github.com> Date: Thu, 3 Jun 2021 01:28:04 -0400 Subject: [PATCH 120/134] fix(lambda-nodejs): pnpm exec command (#14954) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix #14757 Expands on #14772 Currently get the following error when bundling with `pnpm-lock.yaml` ``` ERROR   ERROR  Unknown options: 'bundle', 'target', 'platform', 'outfile', 'external:aws-sdk' ``` ---- Switch from `pnpm run esbuild` to `pnpm exec esbuild --` to properly enable esbuild with `pnpm` `pnpm run` only supports running commands defined in the package's manifest file - [docs](https://pnpm.io/cli/run) `pnpm exec` supports executing a shell command in scope of a project - [docs](https://pnpm.io/cli/exec) ---- *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/lib/package-manager.ts | 7 ++++++- .../aws-lambda-nodejs/test/package-manager.test.ts | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda-nodejs/lib/package-manager.ts b/packages/@aws-cdk/aws-lambda-nodejs/lib/package-manager.ts index a95373bd6d45f..f10f423a4c38b 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/lib/package-manager.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/lib/package-manager.ts @@ -5,6 +5,7 @@ interface PackageManagerProps { readonly lockFile: string; readonly installCommand: string[]; readonly runCommand: string[]; + readonly argsSeparator?: string } /** @@ -26,7 +27,8 @@ export class PackageManager { public static PNPM = new PackageManager({ lockFile: 'pnpm-lock.yaml', installCommand: ['pnpm', 'install'], - runCommand: ['pnpm', 'run'], + runCommand: ['pnpm', 'exec'], + argsSeparator: '--', }); public static fromLockFile(lockFilePath: string): PackageManager { @@ -47,11 +49,13 @@ export class PackageManager { public readonly lockFile: string; public readonly installCommand: string[]; public readonly runCommand: string[]; + public readonly argsSeparator?: string; constructor(props: PackageManagerProps) { this.lockFile = props.lockFile; this.installCommand = props.installCommand; this.runCommand = props.runCommand; + this.argsSeparator = props.argsSeparator; } public runBinCommand(bin: string): string { @@ -60,6 +64,7 @@ export class PackageManager { os.platform() === 'win32' ? `${runCommand}.cmd` : runCommand, ...runArgs, bin, + ...(this.argsSeparator ? [this.argsSeparator] : []), ].join(' '); } } diff --git a/packages/@aws-cdk/aws-lambda-nodejs/test/package-manager.test.ts b/packages/@aws-cdk/aws-lambda-nodejs/test/package-manager.test.ts index e0721bbec3e38..7f64a18d2123f 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/test/package-manager.test.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/test/package-manager.test.ts @@ -19,7 +19,7 @@ test('from a pnpm-lock.yaml', () => { const packageManager = PackageManager.fromLockFile('/path/to/pnpm-lock.yaml'); expect(packageManager).toEqual(PackageManager.PNPM); - expect(packageManager.runBinCommand('my-bin')).toBe('pnpm run my-bin'); + expect(packageManager.runBinCommand('my-bin')).toBe('pnpm exec my-bin --'); }); test('defaults to NPM', () => { From f66f4b80da22b4d24d4419acc3984b56d5690b2e Mon Sep 17 00:00:00 2001 From: Otavio Macedo Date: Thu, 3 Jun 2021 12:46:52 +0100 Subject: [PATCH 121/134] feat(cli): new bootstrap supports cross-account lookups (#14874) Fixes #8905 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../lib/cloud-assembly/context-queries.ts | 64 ++++++++++++++++++ .../schema/cloud-assembly.schema.json | 36 ++++++++++ .../schema/cloud-assembly.version.json | 2 +- .../stack-synthesizers/default-synthesizer.ts | 20 +++++- packages/@aws-cdk/core/lib/stack.ts | 8 ++- packages/@aws-cdk/core/test/app.test.ts | 6 +- .../new-style-synthesis.test.ts | 25 ++++++- packages/@aws-cdk/pipelines/README.md | 15 +++++ packages/aws-cdk/bin/cdk.ts | 2 + packages/aws-cdk/lib/api/aws-auth/sdk.ts | 3 +- .../api/bootstrap/bootstrap-environment.ts | 6 +- .../lib/api/bootstrap/bootstrap-props.ts | 7 ++ .../lib/api/bootstrap/bootstrap-template.yaml | 65 ++++++++++++++++++- packages/aws-cdk/lib/context-providers/ami.ts | 3 +- .../context-providers/availability-zones.ts | 3 +- .../endpoint-service-availability-zones.ts | 5 +- .../lib/context-providers/hosted-zones.ts | 3 +- .../aws-cdk/lib/context-providers/index.ts | 2 +- .../lib/context-providers/load-balancers.ts | 6 +- .../lib/context-providers/security-groups.ts | 3 +- .../lib/context-providers/ssm-parameters.ts | 9 ++- .../aws-cdk/lib/context-providers/vpcs.ts | 3 +- packages/aws-cdk/test/api/bootstrap2.test.ts | 15 +++++ .../aws-cdk/test/api/sdk-provider.test.ts | 2 +- 24 files changed, 289 insertions(+), 24 deletions(-) diff --git a/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts b/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts index 56c609a08dd39..a9e5df9bd1d0b 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts +++ b/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts @@ -64,6 +64,13 @@ export interface AmiContextQuery { */ readonly region: string; + /** + * The ARN of the role that should be used to look up the missing values + * + * @default - None + */ + readonly lookupRoleArn?: string; + /** * Owners to DescribeImages call * @@ -90,6 +97,14 @@ export interface AvailabilityZonesContextQuery { * Query region */ readonly region: string; + + /** + * The ARN of the role that should be used to look up the missing values + * + * @default - None + */ + readonly lookupRoleArn?: string; + } /** @@ -106,6 +121,13 @@ export interface HostedZoneContextQuery { */ readonly region: string; + /** + * The ARN of the role that should be used to look up the missing values + * + * @default - None + */ + readonly lookupRoleArn?: string; + /** * The domain name e.g. example.com to lookup */ @@ -143,6 +165,13 @@ export interface SSMParameterContextQuery { */ readonly region: string; + /** + * The ARN of the role that should be used to look up the missing values + * + * @default - None + */ + readonly lookupRoleArn?: string; + /** * Parameter name to query */ @@ -163,6 +192,13 @@ export interface VpcContextQuery { */ readonly region: string; + /** + * The ARN of the role that should be used to look up the missing values + * + * @default - None + */ + readonly lookupRoleArn?: string; + /** * Filters to apply to the VPC * @@ -205,6 +241,13 @@ export interface EndpointServiceAvailabilityZonesContextQuery { */ readonly region: string; + /** + * The ARN of the role that should be used to look up the missing values + * + * @default - None + */ + readonly lookupRoleArn?: string; + /** * Query service name */ @@ -261,6 +304,13 @@ export interface LoadBalancerContextQuery extends LoadBalancerFilter { * Query region */ readonly region: string; + + /** + * The ARN of the role that should be used to look up the missing values + * + * @default - None + */ + readonly lookupRoleArn?: string; } /** @@ -312,6 +362,13 @@ export interface LoadBalancerListenerContextQuery extends LoadBalancerFilter { */ readonly region: string; + /** + * The ARN of the role that should be used to look up the missing values + * + * @default - None + */ + readonly lookupRoleArn?: string; + /** * Find by listener's arn * @default - does not find by listener arn @@ -345,6 +402,13 @@ export interface SecurityGroupContextQuery { */ readonly region: string; + /** + * The ARN of the role that should be used to look up the missing values + * + * @default - None + */ + readonly lookupRoleArn?: string; + /** * Security group id */ diff --git a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json index 77d3117d0aae2..3c0f38f598570 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json +++ b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json @@ -453,6 +453,10 @@ "description": "Region to query", "type": "string" }, + "lookupRoleArn": { + "description": "The ARN of the role that should be used to look up the missing values (Default - None)", + "type": "string" + }, "owners": { "description": "Owners to DescribeImages call (Default - All owners)", "type": "array", @@ -488,6 +492,10 @@ "region": { "description": "Query region", "type": "string" + }, + "lookupRoleArn": { + "description": "The ARN of the role that should be used to look up the missing values (Default - None)", + "type": "string" } }, "required": [ @@ -507,6 +515,10 @@ "description": "Query region", "type": "string" }, + "lookupRoleArn": { + "description": "The ARN of the role that should be used to look up the missing values (Default - None)", + "type": "string" + }, "domainName": { "description": "The domain name e.g. example.com to lookup", "type": "string" @@ -539,6 +551,10 @@ "description": "Query region", "type": "string" }, + "lookupRoleArn": { + "description": "The ARN of the role that should be used to look up the missing values (Default - None)", + "type": "string" + }, "parameterName": { "description": "Parameter name to query", "type": "string" @@ -562,6 +578,10 @@ "description": "Query region", "type": "string" }, + "lookupRoleArn": { + "description": "The ARN of the role that should be used to look up the missing values (Default - None)", + "type": "string" + }, "filter": { "description": "Filters to apply to the VPC\n\nFilter parameters are the same as passed to DescribeVpcs.", "type": "object", @@ -597,6 +617,10 @@ "description": "Query region", "type": "string" }, + "lookupRoleArn": { + "description": "The ARN of the role that should be used to look up the missing values (Default - None)", + "type": "string" + }, "serviceName": { "description": "Query service name", "type": "string" @@ -620,6 +644,10 @@ "description": "Query region", "type": "string" }, + "lookupRoleArn": { + "description": "The ARN of the role that should be used to look up the missing values (Default - None)", + "type": "string" + }, "loadBalancerType": { "$ref": "#/definitions/LoadBalancerType", "description": "Filter load balancers by their type" @@ -662,6 +690,10 @@ "description": "Query region", "type": "string" }, + "lookupRoleArn": { + "description": "The ARN of the role that should be used to look up the missing values (Default - None)", + "type": "string" + }, "listenerArn": { "description": "Find by listener's arn (Default - does not find by listener arn)", "type": "string" @@ -716,6 +748,10 @@ "description": "Query region", "type": "string" }, + "lookupRoleArn": { + "description": "The ARN of the role that should be used to look up the missing values (Default - None)", + "type": "string" + }, "securityGroupId": { "description": "Security group id", "type": "string" diff --git a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.version.json b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.version.json index b056ff69e87b5..42c883f995fd4 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.version.json +++ b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.version.json @@ -1 +1 @@ -{"version":"11.0.0"} \ No newline at end of file +{"version":"12.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/core/lib/stack-synthesizers/default-synthesizer.ts b/packages/@aws-cdk/core/lib/stack-synthesizers/default-synthesizer.ts index c902e3326d44b..dba94194d6338 100644 --- a/packages/@aws-cdk/core/lib/stack-synthesizers/default-synthesizer.ts +++ b/packages/@aws-cdk/core/lib/stack-synthesizers/default-synthesizer.ts @@ -84,6 +84,13 @@ export interface DefaultStackSynthesizerProps { */ readonly imageAssetPublishingRoleArn?: string; + /** + * The role to use to look up values from the target AWS account during synthesis + * + * @default - None + */ + readonly lookupRoleArn?: string; + /** * External ID to use when assuming role for image asset publishing * @@ -195,6 +202,11 @@ export class DefaultStackSynthesizer extends StackSynthesizer { */ public static readonly DEFAULT_IMAGE_ASSET_PUBLISHING_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-image-publishing-role-${AWS::AccountId}-${AWS::Region}'; + /** + * Default lookup role ARN for missing values. + */ + public static readonly DEFAULT_LOOKUP_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-lookup-role-${AWS::AccountId}-${AWS::Region}'; + /** * Default image assets repository name */ @@ -222,8 +234,9 @@ export class DefaultStackSynthesizer extends StackSynthesizer { private _cloudFormationExecutionRoleArn?: string; private fileAssetPublishingRoleArn?: string; private imageAssetPublishingRoleArn?: string; + private lookupRoleArn?: string; private qualifier?: string; - private bucketPrefix?: string + private bucketPrefix?: string; private readonly files: NonNullable = {}; private readonly dockerImages: NonNullable = {}; @@ -282,6 +295,7 @@ export class DefaultStackSynthesizer extends StackSynthesizer { this._cloudFormationExecutionRoleArn = specialize(this.props.cloudFormationExecutionRole ?? DefaultStackSynthesizer.DEFAULT_CLOUDFORMATION_ROLE_ARN); this.fileAssetPublishingRoleArn = specialize(this.props.fileAssetPublishingRoleArn ?? DefaultStackSynthesizer.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_ARN); this.imageAssetPublishingRoleArn = specialize(this.props.imageAssetPublishingRoleArn ?? DefaultStackSynthesizer.DEFAULT_IMAGE_ASSET_PUBLISHING_ROLE_ARN); + this.lookupRoleArn = specialize(this.props.lookupRoleArn ?? DefaultStackSynthesizer.DEFAULT_LOOKUP_ROLE_ARN); this.bucketPrefix = specialize(this.props.bucketPrefix ?? DefaultStackSynthesizer.DEFAULT_FILE_ASSET_PREFIX); /* eslint-enable max-len */ } @@ -362,6 +376,10 @@ export class DefaultStackSynthesizer extends StackSynthesizer { }; } + protected synthesizeStackTemplate(stack: Stack, session: ISynthesisSession): void { + stack._synthesizeTemplate(session, this.lookupRoleArn); + } + /** * Synthesize the associated stack to the session */ diff --git a/packages/@aws-cdk/core/lib/stack.ts b/packages/@aws-cdk/core/lib/stack.ts index e256f73231714..b32095233b80d 100644 --- a/packages/@aws-cdk/core/lib/stack.ts +++ b/packages/@aws-cdk/core/lib/stack.ts @@ -750,7 +750,7 @@ export class Stack extends CoreConstruct implements ITaggable { * Synthesizes the cloudformation template into a cloud assembly. * @internal */ - public _synthesizeTemplate(session: ISynthesisSession): void { + public _synthesizeTemplate(session: ISynthesisSession, lookupRoleArn?: string): void { // In principle, stack synthesis is delegated to the // StackSynthesis object. // @@ -777,7 +777,11 @@ export class Stack extends CoreConstruct implements ITaggable { fs.writeFileSync(outPath, JSON.stringify(template, undefined, 2)); for (const ctx of this._missingContext) { - builder.addMissing(ctx); + if (lookupRoleArn != null) { + builder.addMissing({ ...ctx, props: { ...ctx.props, lookupRoleArn } }); + } else { + builder.addMissing(ctx); + } } } diff --git a/packages/@aws-cdk/core/test/app.test.ts b/packages/@aws-cdk/core/test/app.test.ts index 199b36dc87465..3bb178edef56a 100644 --- a/packages/@aws-cdk/core/test/app.test.ts +++ b/packages/@aws-cdk/core/test/app.test.ts @@ -1,7 +1,7 @@ import { ContextProvider } from '@aws-cdk/cloud-assembly-schema'; import * as cxapi from '@aws-cdk/cx-api'; import { nodeunitShim, Test } from 'nodeunit-shim'; -import { CfnResource, Construct, Stack, StackProps } from '../lib'; +import { CfnResource, Construct, DefaultStackSynthesizer, Stack, StackProps } from '../lib'; import { Annotations } from '../lib/annotations'; import { App, AppProps } from '../lib/app'; @@ -219,7 +219,7 @@ nodeunitShim({ } const assembly = withApp({}, app => { - new MyStack(app, 'MyStack'); + new MyStack(app, 'MyStack', { synthesizer: new DefaultStackSynthesizer() }); }); test.deepEqual(assembly.manifest.missing, [ @@ -227,6 +227,7 @@ nodeunitShim({ key: 'missing-context-key', provider: ContextProvider.AVAILABILITY_ZONE_PROVIDER, props: { + lookupRoleArn: 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}', account: '12345689012', region: 'ab-north-1', }, @@ -235,6 +236,7 @@ nodeunitShim({ key: 'missing-context-key-2', provider: ContextProvider.AVAILABILITY_ZONE_PROVIDER, props: { + lookupRoleArn: 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}', account: '12345689012', region: 'ab-south-1', }, diff --git a/packages/@aws-cdk/core/test/stack-synthesis/new-style-synthesis.test.ts b/packages/@aws-cdk/core/test/stack-synthesis/new-style-synthesis.test.ts index 73f8f185f06ba..5505f6ca9e8fd 100644 --- a/packages/@aws-cdk/core/test/stack-synthesis/new-style-synthesis.test.ts +++ b/packages/@aws-cdk/core/test/stack-synthesis/new-style-synthesis.test.ts @@ -2,7 +2,7 @@ import * as fs from 'fs'; import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import * as cxapi from '@aws-cdk/cx-api'; import { nodeunitShim, Test } from 'nodeunit-shim'; -import { App, Aws, CfnResource, DefaultStackSynthesizer, FileAssetPackaging, Stack } from '../../lib'; +import { App, Aws, CfnResource, ContextProvider, DefaultStackSynthesizer, FileAssetPackaging, Stack } from '../../lib'; import { evaluateCFN } from '../evaluate-cfn'; const CFN_CONTEXT = { @@ -101,6 +101,29 @@ nodeunitShim({ test.done(); }, + 'generates missing context with the lookup role ARN as one of the missing context properties'(test: Test) { + // GIVEN + stack = new Stack(app, 'Stack2', { + synthesizer: new DefaultStackSynthesizer({ + generateBootstrapVersionRule: false, + }), + env: { + account: '111111111111', region: 'us-east-1', + }, + }); + ContextProvider.getValue(stack, { + provider: cxschema.ContextProvider.VPC_PROVIDER, + props: {}, + dummyValue: undefined, + }).value; + + // THEN + const assembly = app.synth(); + test.equal(assembly.manifest.missing![0].props.lookupRoleArn, 'arn:${AWS::Partition}:iam::111111111111:role/cdk-hnb659fds-lookup-role-111111111111-us-east-1'); + + test.done(); + }, + 'add file asset'(test: Test) { // WHEN const location = stack.synthesizer.addFileAsset({ diff --git a/packages/@aws-cdk/pipelines/README.md b/packages/@aws-cdk/pipelines/README.md index 82aded1d69b4b..a99dba1630f3c 100644 --- a/packages/@aws-cdk/pipelines/README.md +++ b/packages/@aws-cdk/pipelines/README.md @@ -632,6 +632,17 @@ $ env CDK_NEW_BOOTSTRAP=1 npx cdk bootstrap \ aws://222222222222/us-east-2 ``` +If you only want to trust an account to do lookups (e.g, when your CDK application has a +`Vpc.fromLookup()` call), use the option `--trust-for-lookup`: + +```console +$ env CDK_NEW_BOOTSTRAP=1 npx cdk bootstrap \ + [--profile admin-profile-2] \ + --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess \ + --trust-for-lookup 11111111111 \ + aws://222222222222/us-east-2 +``` + These command lines explained: * `npx`: means to use the CDK CLI from the current NPM install. If you are using @@ -647,6 +658,10 @@ These command lines explained: CDK applications into this account. In this case we indicate the Pipeline's account, but you could also use this for developer accounts (don't do that for production application accounts though!). +* `--trust-for-lookup`: similar to `--trust`, but gives a more limited set of permissions to the + trusted account, allowing it to only look up values, such as availability zones, EC2 images and + VPCs. Note that if you provide an account using `--trust`, that account can also do lookups. + So you only need to pass `--trust-for-lookup` if you need to use a different account. * `aws://222222222222/us-east-2`: the account and region we're bootstrapping. > **Security tip**: we recommend that you use administrative credentials to an diff --git a/packages/aws-cdk/bin/cdk.ts b/packages/aws-cdk/bin/cdk.ts index 7c69bcb43a3f3..b36c01e10969b 100644 --- a/packages/aws-cdk/bin/cdk.ts +++ b/packages/aws-cdk/bin/cdk.ts @@ -79,6 +79,7 @@ async function parseCommandLineArguments() { .option('tags', { type: 'array', alias: 't', desc: 'Tags to add for the stack (KEY=VALUE)', nargs: 1, requiresArg: true, default: [] }) .option('execute', { type: 'boolean', desc: 'Whether to execute ChangeSet (--no-execute will NOT execute the ChangeSet)', default: true }) .option('trust', { type: 'array', desc: 'The AWS account IDs that should be trusted to perform deployments into this environment (may be repeated, modern bootstrapping only)', default: [], nargs: 1, requiresArg: true }) + .option('trust-for-lookup', { type: 'array', desc: 'The AWS account IDs that should be trusted to look up values in this environment (may be repeated, modern bootstrapping only)', default: [], nargs: 1, requiresArg: true }) .option('cloudformation-execution-policies', { type: 'array', desc: 'The Managed Policy ARNs that should be attached to the role performing deployments into this environment (may be repeated, modern bootstrapping only)', default: [], nargs: 1, requiresArg: true }) .option('force', { alias: 'f', type: 'boolean', desc: 'Always bootstrap even if it would downgrade template version', default: false }) .option('termination-protection', { type: 'boolean', default: undefined, desc: 'Toggle CloudFormation termination protection on the bootstrap stacks' }) @@ -279,6 +280,7 @@ async function initCommandLine() { qualifier: args.qualifier, publicAccessBlockConfiguration: args.publicAccessBlockConfiguration, trustedAccounts: arrayFromYargs(args.trust), + trustedAccountsForLookup: arrayFromYargs(args.trustForLookup), cloudFormationExecutionPolicies: arrayFromYargs(args.cloudformationExecutionPolicies), }, }); diff --git a/packages/aws-cdk/lib/api/aws-auth/sdk.ts b/packages/aws-cdk/lib/api/aws-auth/sdk.ts index d5787a258275d..888901a8c33bf 100644 --- a/packages/aws-cdk/lib/api/aws-auth/sdk.ts +++ b/packages/aws-cdk/lib/api/aws-auth/sdk.ts @@ -158,8 +158,9 @@ export class SDK implements ISDK { ...this.sdkOptions.assumeRoleCredentialsSourceDescription ? [`using ${this.sdkOptions.assumeRoleCredentialsSourceDescription}`] : [], - '(did you bootstrap the environment with the right \'--trust\'s?):', e.message, + '. Please make sure that this role exists in the account. If it doesn\'t exist, (re)-bootstrap the environment ' + + 'with the right \'--trust\', using the latest version of the CDK CLI.', ].join(' ')); } } diff --git a/packages/aws-cdk/lib/api/bootstrap/bootstrap-environment.ts b/packages/aws-cdk/lib/api/bootstrap/bootstrap-environment.ts index 5350524b0dae3..7877368710c5f 100644 --- a/packages/aws-cdk/lib/api/bootstrap/bootstrap-environment.ts +++ b/packages/aws-cdk/lib/api/bootstrap/bootstrap-environment.ts @@ -92,7 +92,10 @@ export class Bootstrapper { // templates doesn't seem to be able to express the conditions that we need // (can't use Fn::Join or reference Conditions) so we do it here instead. const trustedAccounts = params.trustedAccounts ?? splitCfnArray(current.parameters.TrustedAccounts); - info(`Trusted accounts: ${trustedAccounts.length > 0 ? trustedAccounts.join(', ') : '(none)'}`); + info(`Trusted accounts for deployment: ${trustedAccounts.length > 0 ? trustedAccounts.join(', ') : '(none)'}`); + + const trustedAccountsForLookup = params.trustedAccountsForLookup ?? splitCfnArray(current.parameters.TrustedAccountsForLookup); + info(`Trusted accounts for lookup: ${trustedAccountsForLookup.length > 0 ? trustedAccountsForLookup.join(', ') : '(none)'}`); const cloudFormationExecutionPolicies = params.cloudFormationExecutionPolicies ?? splitCfnArray(current.parameters.CloudFormationExecutionPolicies); if (trustedAccounts.length === 0 && cloudFormationExecutionPolicies.length === 0) { @@ -137,6 +140,7 @@ export class Bootstrapper { FileAssetsBucketKmsKeyId: kmsKeyId, // Empty array becomes empty string TrustedAccounts: trustedAccounts.join(','), + TrustedAccountsForLookup: trustedAccountsForLookup.join(','), CloudFormationExecutionPolicies: cloudFormationExecutionPolicies.join(','), Qualifier: params.qualifier, PublicAccessBlockConfiguration: params.publicAccessBlockConfiguration || params.publicAccessBlockConfiguration === undefined ? 'true' : 'false', diff --git a/packages/aws-cdk/lib/api/bootstrap/bootstrap-props.ts b/packages/aws-cdk/lib/api/bootstrap/bootstrap-props.ts index dd36739d4381d..10fc4fc8ff598 100644 --- a/packages/aws-cdk/lib/api/bootstrap/bootstrap-props.ts +++ b/packages/aws-cdk/lib/api/bootstrap/bootstrap-props.ts @@ -71,6 +71,13 @@ export interface BootstrappingParameters { */ readonly trustedAccounts?: string[]; + /** + * The list of AWS account IDs that are trusted to look up values in the environment being bootstrapped. + * + * @default - only the bootstrapped account can look up values in this environment + */ + readonly trustedAccountsForLookup?: string[]; + /** * The ARNs of the IAM managed policies that should be attached to the role performing CloudFormation deployments. * In most cases, this will be the AdministratorAccess policy. diff --git a/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml b/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml index b541401f930e7..6fb29fbbb6d5b 100644 --- a/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml +++ b/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml @@ -6,6 +6,11 @@ Parameters: stacks to this environment Default: '' Type: CommaDelimitedList + TrustedAccountsForLookup: + Description: List of AWS accounts that are trusted to look up values in this + environment + Default: '' + Type: CommaDelimitedList CloudFormationExecutionPolicies: Description: List of the ManagedPolicy ARN(s) to attach to the CloudFormation deployment role @@ -45,6 +50,13 @@ Conditions: - Fn::Join: - '' - Ref: TrustedAccounts + HasTrustedAccountsForLookup: + Fn::Not: + - Fn::Equals: + - '' + - Fn::Join: + - '' + - Ref: TrustedAccountsForLookup HasCloudFormationExecutionPolicies: Fn::Not: - Fn::Equals: @@ -233,6 +245,57 @@ Resources: - Ref: AWS::NoValue RoleName: Fn::Sub: cdk-${Qualifier}-image-publishing-role-${AWS::AccountId}-${AWS::Region} + LookupRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Statement: + - Action: sts:AssumeRole + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId + - Fn::If: + - HasTrustedAccountsForLookup + - Action: sts:AssumeRole + Effect: Allow + Principal: + AWS: + Ref: TrustedAccountsForLookup + - Ref: AWS::NoValue + - Fn::If: + - HasTrustedAccounts + - Action: sts:AssumeRole + Effect: Allow + Principal: + AWS: + Ref: TrustedAccounts + - Ref: AWS::NoValue + RoleName: + Fn::Sub: cdk-${Qualifier}-lookup-role-${AWS::AccountId}-${AWS::Region} + Policies: + - PolicyDocument: + Statement: + - Action: + - ec2:DescribeVpcs + - ec2:DescribeAvailabilityZones + - ec2:DescribeSubnets + - ec2:DescribeRouteTables + - ec2:DescribeVpnGateways + - ec2:DescribeImages + - ec2:DescribeVpcEndpointServices + - ec2:DescribeSecurityGroups + - elasticloadbalancing:DescribeLoadBalancers + - elasticloadbalancing:DescribeTags + - elasticloadbalancing:DescribeListeners + - route53:ListHostedZonesByName + - route53:GetHostedZone + - ssm:GetParameter + Resource: "*" + Effect: Allow + Version: '2012-10-17' + PolicyName: + Fn::Sub: cdk-${Qualifier}-lookup-role-default-policy-${AWS::AccountId}-${AWS::Region} FilePublishingRoleDefaultPolicy: Type: AWS::IAM::Policy Properties: @@ -393,7 +456,7 @@ Resources: Type: String Name: Fn::Sub: '/cdk-bootstrap/${Qualifier}/version' - Value: '5' + Value: '6' Outputs: BucketName: Description: The name of the S3 bucket owned by the CDK toolkit stack diff --git a/packages/aws-cdk/lib/context-providers/ami.ts b/packages/aws-cdk/lib/context-providers/ami.ts index 050afdb0a1b5f..f49bbef114175 100644 --- a/packages/aws-cdk/lib/context-providers/ami.ts +++ b/packages/aws-cdk/lib/context-providers/ami.ts @@ -20,7 +20,8 @@ export class AmiContextProviderPlugin implements ContextProviderPlugin { print(`Searching for AMI in ${account}:${region}`); debug(`AMI search parameters: ${JSON.stringify(args)}`); - const ec2 = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(account, region), Mode.ForReading)).ec2(); + const options = { assumeRoleArn: args.lookupRoleArn }; + const ec2 = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(account, region), Mode.ForReading, options)).ec2(); const response = await ec2.describeImages({ Owners: args.owners, Filters: Object.entries(args.filters).map(([key, values]) => ({ diff --git a/packages/aws-cdk/lib/context-providers/availability-zones.ts b/packages/aws-cdk/lib/context-providers/availability-zones.ts index beaa651cf22ba..1f3d656b17c11 100644 --- a/packages/aws-cdk/lib/context-providers/availability-zones.ts +++ b/packages/aws-cdk/lib/context-providers/availability-zones.ts @@ -15,7 +15,8 @@ export class AZContextProviderPlugin implements ContextProviderPlugin { const region = args.region; const account = args.account; debug(`Reading AZs for ${account}:${region}`); - const ec2 = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(account, region), Mode.ForReading)).ec2(); + const options = { assumeRoleArn: args.lookupRoleArn }; + const ec2 = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(account, region), Mode.ForReading, options)).ec2(); const response = await ec2.describeAvailabilityZones().promise(); if (!response.AvailabilityZones) { return []; } const azs = response.AvailabilityZones.filter(zone => zone.State === 'available').map(zone => zone.ZoneName); diff --git a/packages/aws-cdk/lib/context-providers/endpoint-service-availability-zones.ts b/packages/aws-cdk/lib/context-providers/endpoint-service-availability-zones.ts index f9099154e2407..68147799ff517 100644 --- a/packages/aws-cdk/lib/context-providers/endpoint-service-availability-zones.ts +++ b/packages/aws-cdk/lib/context-providers/endpoint-service-availability-zones.ts @@ -10,12 +10,13 @@ export class EndpointServiceAZContextProviderPlugin implements ContextProviderPl constructor(private readonly aws: SdkProvider) { } - public async getValue(args: {[key: string]: any}) { + public async getValue(args: { [key: string]: any }) { const region = args.region; const account = args.account; const serviceName = args.serviceName; debug(`Reading AZs for ${account}:${region}:${serviceName}`); - const ec2 = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(account, region), Mode.ForReading)).ec2(); + const options = { assumeRoleArn: args.lookupRoleArn }; + const ec2 = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(account, region), Mode.ForReading, options)).ec2(); const response = await ec2.describeVpcEndpointServices({ ServiceNames: [serviceName] }).promise(); // expect a service in the response diff --git a/packages/aws-cdk/lib/context-providers/hosted-zones.ts b/packages/aws-cdk/lib/context-providers/hosted-zones.ts index 8d959c5c1e142..909475eaccc06 100644 --- a/packages/aws-cdk/lib/context-providers/hosted-zones.ts +++ b/packages/aws-cdk/lib/context-providers/hosted-zones.ts @@ -17,7 +17,8 @@ export class HostedZoneContextProviderPlugin implements ContextProviderPlugin { } const domainName = args.domainName; debug(`Reading hosted zone ${account}:${region}:${domainName}`); - const r53 = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(account, region), Mode.ForReading)).route53(); + const options = { assumeRoleArn: args.lookupRoleArn }; + const r53 = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(account, region), Mode.ForReading, options)).route53(); const response = await r53.listHostedZonesByName({ DNSName: domainName }).promise(); if (!response.HostedZones) { throw new Error(`Hosted Zone not found in account ${account}, region ${region}: ${domainName}`); diff --git a/packages/aws-cdk/lib/context-providers/index.ts b/packages/aws-cdk/lib/context-providers/index.ts index e60ad6066d280..a87183d3b4e46 100644 --- a/packages/aws-cdk/lib/context-providers/index.ts +++ b/packages/aws-cdk/lib/context-providers/index.ts @@ -13,7 +13,7 @@ import { SecurityGroupContextProviderPlugin } from './security-groups'; import { SSMContextProviderPlugin } from './ssm-parameters'; import { VpcNetworkContextProviderPlugin } from './vpcs'; -type ProviderConstructor = (new (sdk: SdkProvider) => ContextProviderPlugin); +type ProviderConstructor = (new (sdk: SdkProvider, lookupRoleArn?: string) => ContextProviderPlugin); export type ProviderMap = {[name: string]: ProviderConstructor}; /** diff --git a/packages/aws-cdk/lib/context-providers/load-balancers.ts b/packages/aws-cdk/lib/context-providers/load-balancers.ts index 26f00c7746fe3..a36e29c0bec58 100644 --- a/packages/aws-cdk/lib/context-providers/load-balancers.ts +++ b/packages/aws-cdk/lib/context-providers/load-balancers.ts @@ -12,7 +12,8 @@ export class LoadBalancerContextProviderPlugin implements ContextProviderPlugin } async getValue(query: cxschema.LoadBalancerContextQuery): Promise { - const elbv2 = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(query.account, query.region), Mode.ForReading)).elbv2(); + const options = { assumeRoleArn: query.lookupRoleArn }; + const elbv2 = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(query.account, query.region), Mode.ForReading, options)).elbv2(); if (!query.loadBalancerArn && !query.loadBalancerTags) { throw new Error('The load balancer lookup query must specify either `loadBalancerArn` or `loadBalancerTags`'); @@ -57,7 +58,8 @@ export class LoadBalancerListenerContextProviderPlugin implements ContextProvide } async getValue(query: LoadBalancerListenerQuery): Promise { - const elbv2 = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(query.account, query.region), Mode.ForReading)).elbv2(); + const options = { assumeRoleArn: query.lookupRoleArn }; + const elbv2 = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(query.account, query.region), Mode.ForReading, options)).elbv2(); if (!query.listenerArn && !query.loadBalancerArn && !query.loadBalancerTags) { throw new Error('The load balancer listener query must specify at least one of: `listenerArn`, `loadBalancerArn` or `loadBalancerTags`'); diff --git a/packages/aws-cdk/lib/context-providers/security-groups.ts b/packages/aws-cdk/lib/context-providers/security-groups.ts index e8f464128b68d..7edde696fba45 100644 --- a/packages/aws-cdk/lib/context-providers/security-groups.ts +++ b/packages/aws-cdk/lib/context-providers/security-groups.ts @@ -12,7 +12,8 @@ export class SecurityGroupContextProviderPlugin implements ContextProviderPlugin const account: string = args.account!; const region: string = args.region!; - const ec2 = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(account, region), Mode.ForReading)).ec2(); + const options = { assumeRoleArn: args.lookupRoleArn }; + const ec2 = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(account, region), Mode.ForReading, options)).ec2(); const response = await ec2.describeSecurityGroups({ GroupIds: [args.securityGroupId], diff --git a/packages/aws-cdk/lib/context-providers/ssm-parameters.ts b/packages/aws-cdk/lib/context-providers/ssm-parameters.ts index 79558a89ff4bf..150f4f14dad40 100644 --- a/packages/aws-cdk/lib/context-providers/ssm-parameters.ts +++ b/packages/aws-cdk/lib/context-providers/ssm-parameters.ts @@ -21,7 +21,7 @@ export class SSMContextProviderPlugin implements ContextProviderPlugin { const parameterName = args.parameterName; debug(`Reading SSM parameter ${account}:${region}:${parameterName}`); - const response = await this.getSsmParameterValue(account, region, parameterName); + const response = await this.getSsmParameterValue(account, region, parameterName, args.lookupRoleArn); if (!response.Parameter || response.Parameter.Value === undefined) { throw new Error(`SSM parameter not available in account ${account}, region ${region}: ${parameterName}`); } @@ -33,13 +33,16 @@ export class SSMContextProviderPlugin implements ContextProviderPlugin { * @param account the account in which the SSM Parameter is expected to be. * @param region the region in which the SSM Parameter is expected to be. * @param parameterName the name of the SSM Parameter + * @param lookupRoleArn the ARN of the lookup role. * * @returns the result of the ``GetParameter`` operation. * * @throws Error if a service error (other than ``ParameterNotFound``) occurs. */ - private async getSsmParameterValue(account: string, region: string, parameterName: string): Promise { - const ssm = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(account, region), Mode.ForReading)).ssm(); + private async getSsmParameterValue(account: string, region: string, parameterName: string, lookupRoleArn?: string) + : Promise { + const options = { assumeRoleArn: lookupRoleArn }; + const ssm = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(account, region), Mode.ForReading, options)).ssm(); try { return await ssm.getParameter({ Name: parameterName }).promise(); } catch (e) { diff --git a/packages/aws-cdk/lib/context-providers/vpcs.ts b/packages/aws-cdk/lib/context-providers/vpcs.ts index 33e2e09ff9499..9b37eadee7aef 100644 --- a/packages/aws-cdk/lib/context-providers/vpcs.ts +++ b/packages/aws-cdk/lib/context-providers/vpcs.ts @@ -14,7 +14,8 @@ export class VpcNetworkContextProviderPlugin implements ContextProviderPlugin { const account: string = args.account!; const region: string = args.region!; - const ec2 = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(account, region), Mode.ForReading)).ec2(); + const options = { assumeRoleArn: args.lookupRoleArn }; + const ec2 = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(account, region), Mode.ForReading, options)).ec2(); const vpcId = await this.findVpc(ec2, args); diff --git a/packages/aws-cdk/test/api/bootstrap2.test.ts b/packages/aws-cdk/test/api/bootstrap2.test.ts index 639f0a6d759bc..bf6f6e836a79a 100644 --- a/packages/aws-cdk/test/api/bootstrap2.test.ts +++ b/packages/aws-cdk/test/api/bootstrap2.test.ts @@ -123,6 +123,21 @@ describe('Bootstrapping v2', () => { })); }); + test('passing trusted accounts for lookup generates the correct stack parameter', async () => { + await bootstrapper.bootstrapEnvironment(env, sdk, { + parameters: { + trustedAccountsForLookup: ['123456789012'], + cloudFormationExecutionPolicies: ['aws://foo'], + }, + }); + + expect(mockDeployStack).toHaveBeenCalledWith(expect.objectContaining({ + parameters: expect.objectContaining({ + TrustedAccountsForLookup: '123456789012', + }), + })); + }); + test('allow adding trusted account if there was already a policy on the stack', async () => { // GIVEN mockTheToolkitInfo({ diff --git a/packages/aws-cdk/test/api/sdk-provider.test.ts b/packages/aws-cdk/test/api/sdk-provider.test.ts index c7dfbbcfeb45c..0e9f0bd01e366 100644 --- a/packages/aws-cdk/test/api/sdk-provider.test.ts +++ b/packages/aws-cdk/test/api/sdk-provider.test.ts @@ -312,7 +312,7 @@ describe('with intercepted network calls', () => { }); // THEN - error message contains both a helpful hint and the underlying AssumeRole message - await expect(promise).rejects.toThrow('did you bootstrap'); + await expect(promise).rejects.toThrow('(re)-bootstrap the environment'); await expect(promise).rejects.toThrow('doesnotexist.role.arn'); }); From 25412a60971d3e332fa22fad4c44122eef9dfd2c Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Thu, 3 Jun 2021 12:20:12 +0000 Subject: [PATCH 122/134] fix(apigatewayv2): http api - default route does not use the default authorizer (#14904) The default authorizer worked by passing the authorizer config to routes in the api by the addRoutes method. We completely forgot about the use case of the default integration, so currently using default integration + default authorizer does not create an authorizer. This PR fixes the bug and allows using default authorizer + default integration as expected. Reported by https://github.com/aws/aws-cdk/issues/10534#issuecomment-837895317 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-apigatewayv2/lib/http/api.ts | 2 ++ .../aws-apigatewayv2/test/http/api.test.ts | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts index 73abc83c16111..f650d62bd289b 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts @@ -422,6 +422,8 @@ export class HttpApi extends HttpApiBase { httpApi: this, routeKey: HttpRouteKey.DEFAULT, integration: props.defaultIntegration, + authorizer: props.defaultAuthorizer, + authorizationScopes: props.defaultAuthorizationScopes, }); } diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts index 3b07593676c11..c2324412d3396 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts @@ -400,6 +400,24 @@ describe('HttpApi', () => { }); }); + test('can add default authorizer when using default integration', () => { + const stack = new Stack(); + + const authorizer = new DummyAuthorizer(); + + new HttpApi(stack, 'api', { + defaultIntegration: new DummyRouteIntegration(), + defaultAuthorizer: authorizer, + defaultAuthorizationScopes: ['read:pets'], + }); + + expect(stack).toHaveResource('AWS::ApiGatewayV2::Route', { + AuthorizerId: 'auth-1234', + AuthorizationType: 'JWT', + AuthorizationScopes: ['read:pets'], + }); + }); + test('can add default authorizer, but remove it for a route', () => { const stack = new Stack(); const authorizer = new DummyAuthorizer(); From 1ff5b9e5b728116171cb1922a861c1ecd4105292 Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Thu, 3 Jun 2021 08:14:07 -0700 Subject: [PATCH 123/134] feat(kms): introduce `fromCfnKey()` method (#14859) This is part 1 of adding support from converting L1 resources to L2 without making them immutable in the process. Next phase after this will be adding support for `Bucket.fromCfnBucket()` (which will use the method from KMS defined here). Related issues: #9719 #14795 #14809 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-iam/lib/policy-statement.ts | 10 +- packages/@aws-cdk/aws-kms/lib/key.ts | 51 ++- packages/@aws-cdk/aws-kms/test/key.test.ts | 319 +++++++++++++++++- .../@aws-cdk/cloudformation-include/README.md | 106 +++++- 4 files changed, 479 insertions(+), 7 deletions(-) diff --git a/packages/@aws-cdk/aws-iam/lib/policy-statement.ts b/packages/@aws-cdk/aws-iam/lib/policy-statement.ts index 8777e40d5d7f7..62257140e9c30 100644 --- a/packages/@aws-cdk/aws-iam/lib/policy-statement.ts +++ b/packages/@aws-cdk/aws-iam/lib/policy-statement.ts @@ -30,7 +30,7 @@ export class PolicyStatement { * @param obj the PolicyStatement in object form. */ public static fromJson(obj: any) { - return new PolicyStatement({ + const ret = new PolicyStatement({ sid: obj.Sid, actions: ensureArrayOrUndefined(obj.Action), resources: ensureArrayOrUndefined(obj.Resource), @@ -41,6 +41,14 @@ export class PolicyStatement { principals: obj.Principal ? [new JsonPrincipal(obj.Principal)] : undefined, notPrincipals: obj.NotPrincipal ? [new JsonPrincipal(obj.NotPrincipal)] : undefined, }); + + // validate that the PolicyStatement has the correct shape + const errors = ret.validateForAnyPolicy(); + if (errors.length > 0) { + throw new Error('Incorrect Policy Statement: ' + errors.join('\n')); + } + + return ret; } /** diff --git a/packages/@aws-cdk/aws-kms/lib/key.ts b/packages/@aws-cdk/aws-kms/lib/key.ts index bea78532bbf90..03097d9d49073 100644 --- a/packages/@aws-cdk/aws-kms/lib/key.ts +++ b/packages/@aws-cdk/aws-kms/lib/key.ts @@ -1,5 +1,5 @@ import * as iam from '@aws-cdk/aws-iam'; -import { FeatureFlags, IResource, RemovalPolicy, Resource, Stack, Duration } from '@aws-cdk/core'; +import { FeatureFlags, IResource, Lazy, RemovalPolicy, Resource, Stack, Duration } from '@aws-cdk/core'; import * as cxapi from '@aws-cdk/cx-api'; import { IConstruct, Construct } from 'constructs'; import { Alias } from './alias'; @@ -485,6 +485,55 @@ export class Key extends KeyBase { return new Import(keyResourceName); } + /** + * Create a mutable {@link IKey} based on a low-level {@link CfnKey}. + * This is most useful when combined with the cloudformation-include module. + * This method is different than {@link fromKeyArn()} because the {@link IKey} + * returned from this method is mutable; + * meaning, calling any mutating methods on it, + * like {@link IKey.addToResourcePolicy()}, + * will actually be reflected in the resulting template, + * as opposed to the object returned from {@link fromKeyArn()}, + * on which calling those methods would have no effect. + */ + public static fromCfnKey(cfnKey: CfnKey): IKey { + // use a "weird" id that has a higher chance of being unique + const id = '@FromCfnKey'; + + // if fromCfnKey() was already called on this cfnKey, + // return the same L2 + // (as different L2s would conflict, because of the mutation of the keyPolicy property of the L1 below) + const existing = cfnKey.node.tryFindChild(id); + if (existing) { + return existing; + } + + let keyPolicy: iam.PolicyDocument; + try { + keyPolicy = iam.PolicyDocument.fromJson(cfnKey.keyPolicy); + } catch (e) { + // If the KeyPolicy contains any CloudFormation functions, + // PolicyDocument.fromJson() throws an exception. + // In that case, because we would have to effectively make the returned IKey immutable, + // throw an exception suggesting to use the other importing methods instead. + // We might make this parsing logic smarter later, + // but let's start by erroring out. + throw new Error('Could not parse the PolicyDocument of the passed AWS::KMS::Key resource because it contains CloudFormation functions. ' + + 'This makes it impossible to create a mutable IKey from that Policy. ' + + 'You have to use fromKeyArn instead, passing it the ARN attribute property of the low-level CfnKey'); + } + + // change the key policy of the L1, so that all changes done in the L2 are reflected in the resulting template + cfnKey.keyPolicy = Lazy.any({ produce: () => keyPolicy.toJSON() }); + + return new class extends KeyBase { + public readonly keyArn = cfnKey.attrArn; + public readonly keyId = cfnKey.ref; + protected readonly policy = keyPolicy; + protected readonly trustAccountIdentities = false; + }(cfnKey, id); + } + public readonly keyArn: string; public readonly keyId: string; protected readonly policy?: iam.PolicyDocument; diff --git a/packages/@aws-cdk/aws-kms/test/key.test.ts b/packages/@aws-cdk/aws-kms/test/key.test.ts index 0bdb6c755d7bb..f80dce397b4b1 100644 --- a/packages/@aws-cdk/aws-kms/test/key.test.ts +++ b/packages/@aws-cdk/aws-kms/test/key.test.ts @@ -1,4 +1,4 @@ -import { arrayWith, ResourcePart } from '@aws-cdk/assert-internal'; +import { arrayWith, countResources, expect as expectCdk, haveResource, haveResourceLike, ResourcePart } from '@aws-cdk/assert-internal'; import '@aws-cdk/assert-internal/jest'; import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; @@ -582,6 +582,323 @@ describe('imported keys', () => { }); }); +describe('fromCfnKey()', () => { + let stack: cdk.Stack; + let cfnKey: kms.CfnKey; + let key: kms.IKey; + + beforeEach(() => { + stack = new cdk.Stack(); + cfnKey = new kms.CfnKey(stack, 'CfnKey', { + keyPolicy: { + Statement: [ + { + Action: 'kms:*', + Effect: 'Allow', + Principal: { + AWS: cdk.Fn.join('', [ + 'arn:', + cdk.Aws.PARTITION, + ':iam::', + cdk.Aws.ACCOUNT_ID, + ':root', + ]), + }, + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + }); + key = kms.Key.fromCfnKey(cfnKey); + }); + + test("correctly resolves the 'keyId' property", () => { + expect(stack.resolve(key.keyId)).toStrictEqual({ + Ref: 'CfnKey', + }); + }); + + test("correctly resolves the 'keyArn' property", () => { + expect(stack.resolve(key.keyArn)).toStrictEqual({ + 'Fn::GetAtt': ['CfnKey', 'Arn'], + }); + }); + + test('preserves the KMS Key resource', () => { + expectCdk(stack).to(haveResource('AWS::KMS::Key', { + KeyPolicy: { + Statement: [ + { + Action: 'kms:*', + Effect: 'Allow', + Principal: { + AWS: { + 'Fn::Join': ['', [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':iam::', + { Ref: 'AWS::AccountId' }, + ':root', + ]], + }, + }, + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + })); + + expectCdk(stack).to(countResources('AWS::KMS::Key', 1)); + }); + + describe("calling 'addToResourcePolicy()' on the returned Key", () => { + let addToResourcePolicyResult: iam.AddToResourcePolicyResult; + + beforeEach(() => { + addToResourcePolicyResult = key.addToResourcePolicy(new iam.PolicyStatement({ + actions: ['kms:action'], + resources: ['*'], + principals: [new iam.AnyPrincipal()], + })); + }); + + test("the AddToResourcePolicyResult returned has 'statementAdded' set to 'true'", () => { + expect(addToResourcePolicyResult.statementAdded).toBeTruthy(); + }); + + test('preserves the mutating call in the resulting template', () => { + expectCdk(stack).to(haveResource('AWS::KMS::Key', { + KeyPolicy: { + Statement: [ + { + Action: 'kms:*', + Effect: 'Allow', + Principal: { + AWS: { + 'Fn::Join': ['', [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':iam::', + { Ref: 'AWS::AccountId' }, + ':root', + ]], + }, + }, + Resource: '*', + }, + { + Action: 'kms:action', + Effect: 'Allow', + Principal: '*', + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + })); + }); + }); + + describe('calling fromCfnKey() again', () => { + beforeEach(() => { + key = kms.Key.fromCfnKey(cfnKey); + }); + + describe('and using it for grantDecrypt() on a Role', function () { + beforeEach(() => { + const role = new iam.Role(stack, 'Role', { + assumedBy: new iam.AnyPrincipal(), + }); + key.grantDecrypt(role); + }); + + test('creates the correct IAM Policy', () => { + expectCdk(stack).to(haveResourceLike('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'kms:Decrypt', + Effect: 'Allow', + Resource: { + 'Fn::GetAtt': ['CfnKey', 'Arn'], + }, + }, + ], + }, + })); + }); + + test('correctly mutates the Policy of the underlying CfnKey', () => { + expectCdk(stack).to(haveResourceLike('AWS::KMS::Key', { + KeyPolicy: { + Statement: [ + { + Action: 'kms:*', + Effect: 'Allow', + Principal: { + AWS: { + 'Fn::Join': ['', [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':iam::', + { Ref: 'AWS::AccountId' }, + ':root', + ]], + }, + }, + Resource: '*', + }, + { + Action: 'kms:Decrypt', + Effect: 'Allow', + Principal: { + AWS: { + 'Fn::GetAtt': ['Role1ABCC5F0', 'Arn'], + }, + }, + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + })); + }); + }); + }); + + describe("called with a CfnKey that has an 'Fn::If' passed as the KeyPolicy", () => { + beforeEach(() => { + cfnKey = new kms.CfnKey(stack, 'CfnKey2', { + keyPolicy: cdk.Fn.conditionIf( + 'AlwaysTrue', + { + Statement: [ + { + Action: 'kms:action1', + Effect: 'Allow', + Principal: '*', + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + { + Statement: [ + { + Action: 'kms:action2', + Effect: 'Allow', + Principal: '*', + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + ), + }); + }); + + test('throws a descriptive exception', () => { + expect(() => { + kms.Key.fromCfnKey(cfnKey); + }).toThrow(/Could not parse the PolicyDocument of the passed AWS::KMS::Key/); + }); + }); + + describe("called with a CfnKey that has an 'Fn::If' passed as the Statement of a KeyPolicy", () => { + beforeEach(() => { + cfnKey = new kms.CfnKey(stack, 'CfnKey2', { + keyPolicy: { + Statement: cdk.Fn.conditionIf( + 'AlwaysTrue', + [ + { + Action: 'kms:action1', + Effect: 'Allow', + Principal: '*', + Resource: '*', + }, + ], + [ + { + Action: 'kms:action2', + Effect: 'Allow', + Principal: '*', + Resource: '*', + }, + ], + ), + Version: '2012-10-17', + }, + }); + }); + + test('throws a descriptive exception', () => { + expect(() => { + kms.Key.fromCfnKey(cfnKey); + }).toThrow(/Could not parse the PolicyDocument of the passed AWS::KMS::Key/); + }); + }); + + describe("called with a CfnKey that has an 'Fn::If' passed as one of the statements of a KeyPolicy", () => { + beforeEach(() => { + cfnKey = new kms.CfnKey(stack, 'CfnKey2', { + keyPolicy: { + Statement: [ + cdk.Fn.conditionIf( + 'AlwaysTrue', + { + Action: 'kms:action1', + Effect: 'Allow', + Principal: '*', + Resource: '*', + }, + { + Action: 'kms:action2', + Effect: 'Allow', + Principal: '*', + Resource: '*', + }, + ), + ], + Version: '2012-10-17', + }, + }); + }); + + test('throws a descriptive exception', () => { + expect(() => { + kms.Key.fromCfnKey(cfnKey); + }).toThrow(/Could not parse the PolicyDocument of the passed AWS::KMS::Key/); + }); + }); + + describe("called with a CfnKey that has an 'Fn::If' passed for the Action in one of the statements of a KeyPolicy", () => { + beforeEach(() => { + cfnKey = new kms.CfnKey(stack, 'CfnKey2', { + keyPolicy: { + Statement: [ + { + Action: cdk.Fn.conditionIf('AlwaysTrue', 'kms:action1', 'kms:action2'), + Effect: 'Allow', + Principal: '*', + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + }); + }); + + test('throws a descriptive exception', () => { + expect(() => { + key = kms.Key.fromCfnKey(cfnKey); + }).toThrow(/Could not parse the PolicyDocument of the passed AWS::KMS::Key/); + }); + }); +}); + describe('addToResourcePolicy allowNoOp and there is no policy', () => { // eslint-disable-next-line jest/expect-expect testFutureBehavior('succeed if set to true (default)', flags, cdk.App, (app) => { diff --git a/packages/@aws-cdk/cloudformation-include/README.md b/packages/@aws-cdk/cloudformation-include/README.md index 2dcd71edc2b38..2c67aeb4d554b 100644 --- a/packages/@aws-cdk/cloudformation-include/README.md +++ b/packages/@aws-cdk/cloudformation-include/README.md @@ -118,14 +118,112 @@ role.addToPolicy(new iam.PolicyStatement({ })); ``` -If you need, you can also convert the CloudFormation resource to a higher-level -resource by importing it: +### Converting L1 resources to L2 + +The resources the `getResource` method returns are what the CDK calls +[Layer 1 resources](https://docs.aws.amazon.com/cdk/latest/guide/cfn_layer.html#cfn_layer_cfn) +(like `CfnBucket`). +However, in many places in the Construct Library, +the CDK requires so-called Layer 2 resources, like `IBucket`. +There are two ways of going from an L1 to an L2 resource. + +#### Using`fromCfn*()` methods + +This is the preferred method of converting an L1 resource to an L2. +It works by invoking a static method of the class of the L2 resource +whose name starts with `fromCfn` - +for example, for KMS Keys, that would be the `Kms.fromCfnKey()` method - +and passing the L1 instance as an argument: + +```ts +import * as kms from '@aws-cdk/aws-kms'; + +const cfnKey = cfnTemplate.getResource('Key') as kms.CfnKey; +const key = kms.Key.fromCfnKey(cfnKey); +``` + +This returns an instance of the `kms.IKey` type that can be passed anywhere in the CDK an `IKey` is expected. +What is more, that `IKey` instance will be mutable - +which means calling any mutating methods on it, +like `addToResourcePolicy()`, +will be reflected in the resulting template. + +Note that, in some cases, the `fromCfn*()` method might not be able to create an L2 from the underlying L1. +This can happen when the underlying L1 heavily uses CloudFormation functions. +For example, if you tried to create an L2 `IKey` +from an L1 represented as this CloudFormation template: + +```json +{ + "Resources": { + "Key": { + "Type": "AWS::KMS::Key", + "Properties": { + "KeyPolicy": { + "Statement": [ + { + "Fn::If": [ + "Condition", + { + "Action": "kms:if-action", + "Resource": "*", + "Principal": "*", + "Effect": "Allow" + }, + { + "Action": "kms:else-action", + "Resource": "*", + "Principal": "*", + "Effect": "Allow" + } + ] + } + ], + "Version": "2012-10-17" + } + } + } + } +} +``` + +The `Key.fromCfnKey()` method does not know how to translate that into CDK L2 concepts, +and would throw an exception. + +In those cases, you need the use the second method of converting an L1 to an L2. + +#### Using `from*Name/Arn/Attributes()` methods + +If the resource you need does not have a `fromCfn*()` method, +or if it does, but it throws an exception for your particular L1, +you need to use the second method of converting an L1 resource to L2. + +Each L2 class has static factory methods with names like `from*Name()`, +`from*Arn()`, and/or `from*Attributes()`. +You can obtain an L2 resource from an L1 by passing the correct properties of the L1 as the arguments to those methods: ```ts +// using from*Name() const bucket = s3.Bucket.fromBucketName(this, 'L2Bucket', cfnBucket.ref); -// bucket is of type s3.IBucket + +// using from*Arn() +const key = kms.Key.fromKeyArn(this, 'L2Key', cfnKey.attrArn); + +// using from*Attributes() +const vpc = ec2.Vpc.fromVpcAttributes(this, 'L2Vpc', { + vpcId: cfnVpc.ref, + availabilityZones: cdk.Fn.getAzs(), + privateSubnetIds: [privateCfnSubnet1.ref, privateCfnSubnet2.ref], +}); ``` +As long as they just need to be referenced, +and not changed in any way, everything should work; +however, note that resources returned from those methods, +unlike those returned by `fromCfn*()` methods, +are immutable, which means calling any mutating methods on them will have no effect. +You will have to mutate the underlying L1 in order to change them. + ## Non-resource template elements In addition to resources, @@ -215,7 +313,7 @@ new inc.CfnInclude(this, 'includeTemplate', { ``` This will replace all references to `MyParam` with the string `'my-value'`, -and `MyParam` will be removed from the 'Parameters' section of the template. +and `MyParam` will be removed from the 'Parameters' section of the resulting template. ## Nested Stacks From da6f2c6752a5ad0bd602d267480ff05cb919e13b Mon Sep 17 00:00:00 2001 From: Ben Chaimberg Date: Thu, 3 Jun 2021 11:04:50 -0700 Subject: [PATCH 124/134] chore: transfer shivlaks ownership to BenChaimberg (#14976) --- .github/workflows/issue-label-assign.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/issue-label-assign.yml b/.github/workflows/issue-label-assign.yml index d8053118ca4f0..fe09e74b25e2e 100644 --- a/.github/workflows/issue-label-assign.yml +++ b/.github/workflows/issue-label-assign.yml @@ -201,8 +201,8 @@ jobs: {"keywords":["(@aws-cdk/aws-sqs)","(aws-sqs)","(sqs)"],"labels":["@aws-cdk/aws-sqs"],"assignees":["njlynch"]}, {"keywords":["(@aws-cdk/aws-ssm)","(aws-ssm)","(ssm)"],"labels":["@aws-cdk/aws-ssm"],"assignees":["njlynch"]}, {"keywords":["(@aws-cdk/aws-sso)","(aws-sso)","(sso)"],"labels":["@aws-cdk/aws-sso"],"assignees":["skinny85"]}, - {"keywords":["(@aws-cdk/aws-stepfunctions)","(aws-stepfunctions)","(stepfunctions)","(step functions)","(step-functions)"],"labels":["@aws-cdk/aws-stepfunctions"],"assignees":["shivlaks"]}, - {"keywords":["(@aws-cdk/aws-stepfunctions-tasks)","(aws-stepfunctions-tasks)","(stepfunctions-tasks)","(stepfunctions tasks)"],"labels":["@aws-cdk/aws-stepfunctions-tasks"],"assignees":["shivlaks"]}, + {"keywords":["(@aws-cdk/aws-stepfunctions)","(aws-stepfunctions)","(stepfunctions)","(step functions)","(step-functions)"],"labels":["@aws-cdk/aws-stepfunctions"],"assignees":["BenChaimberg"]}, + {"keywords":["(@aws-cdk/aws-stepfunctions-tasks)","(aws-stepfunctions-tasks)","(stepfunctions-tasks)","(stepfunctions tasks)"],"labels":["@aws-cdk/aws-stepfunctions-tasks"],"assignees":["BenChaimberg"]}, {"keywords":["(@aws-cdk/aws-synthetics)","(aws-synthetics)","(synthetics)"],"labels":["@aws-cdk/aws-synthetics"],"assignees":["BenChaimberg"]}, {"keywords":["(@aws-cdk/aws-timestream)","(aws-timestream)","(timestream)"],"labels":["@aws-cdk/aws-timestream"],"assignees":["skinny85"]}, {"keywords":["(@aws-cdk/aws-transfer)","(aws-transfer)","(transfer)"],"labels":["@aws-cdk/aws-transfer"],"assignees":["otaviomacedo"]}, From af6d49f2e245b60ae3bbea3bb2c5d283beedba3f Mon Sep 17 00:00:00 2001 From: Christian Moore Date: Thu, 3 Jun 2021 14:39:17 -0400 Subject: [PATCH 125/134] fix(ec2): add missing entry for XLARGE3 (#14750) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-ec2/lib/instance-types.ts | 5 +++ .../@aws-cdk/aws-ec2/test/instance.test.ts | 33 +++++++++++++------ 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/packages/@aws-cdk/aws-ec2/lib/instance-types.ts b/packages/@aws-cdk/aws-ec2/lib/instance-types.ts index fc3b3b411d7b3..a12dfb92061c6 100644 --- a/packages/@aws-cdk/aws-ec2/lib/instance-types.ts +++ b/packages/@aws-cdk/aws-ec2/lib/instance-types.ts @@ -527,6 +527,11 @@ export enum InstanceSize { */ XLARGE2 = '2xlarge', + /** + * Instance size XLARGE3 (3xlarge) + */ + XLARGE3 = '3xlarge', + /** * Instance size XLARGE4 (4xlarge) */ diff --git a/packages/@aws-cdk/aws-ec2/test/instance.test.ts b/packages/@aws-cdk/aws-ec2/test/instance.test.ts index d5b77e01ad0ea..392aeb8ec22d9 100644 --- a/packages/@aws-cdk/aws-ec2/test/instance.test.ts +++ b/packages/@aws-cdk/aws-ec2/test/instance.test.ts @@ -21,17 +21,30 @@ beforeEach(() => { nodeunitShim({ 'instance is created correctly'(test: Test) { - // WHEN - new Instance(stack, 'Instance', { - vpc, - machineImage: new AmazonLinuxImage(), - instanceType: InstanceType.of(InstanceClass.BURSTABLE4_GRAVITON, InstanceSize.LARGE), - }); + // GIVEN + const sampleInstances = [{ + instanceClass: InstanceClass.BURSTABLE4_GRAVITON, + instanceSize: InstanceSize.LARGE, + instanceType: 't4g.large', + }, { + instanceClass: InstanceClass.HIGH_COMPUTE_MEMORY1, + instanceSize: InstanceSize.XLARGE3, + instanceType: 'z1d.3xlarge', + }]; - // THEN - cdkExpect(stack).to(haveResource('AWS::EC2::Instance', { - InstanceType: 't4g.large', - })); + for (const [i, sampleInstance] of sampleInstances.entries()) { + // WHEN + new Instance(stack, `Instance${i}`, { + vpc, + machineImage: new AmazonLinuxImage(), + instanceType: InstanceType.of(sampleInstance.instanceClass, sampleInstance.instanceSize), + }); + + // THEN + cdkExpect(stack).to(haveResource('AWS::EC2::Instance', { + InstanceType: sampleInstance.instanceType, + })); + } test.done(); }, From b35328c1197dfed572532e114d1ded89ddb523ac Mon Sep 17 00:00:00 2001 From: Unnati Parekh <80710604+upparekh@users.noreply.github.com> Date: Thu, 3 Jun 2021 14:35:10 -0700 Subject: [PATCH 126/134] feat(ecs): Adding support for ECS Exec (#14670) Fixes https://github.com/aws/aws-cdk/issues/13618 This PR adds support for ECS exec command. It adds necessary IAM permissions for the AWS Systems Manager (SSM) to enable exec and also adds IAM permissions for allowing the exec command result logs to be routed to either CloudWatch logs/ S3 Bucket or both. ---- *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/README.md | 57 + .../@aws-cdk/aws-ecs/lib/base/base-service.ts | 112 +- packages/@aws-cdk/aws-ecs/lib/cluster.ts | 167 +++ .../@aws-cdk/aws-ecs/test/cluster.test.ts | 140 ++ .../aws-ecs/test/ec2/ec2-service.test.ts | 723 ++++++++++ .../test/ec2/integ.exec-command.expected.json | 1173 +++++++++++++++++ .../aws-ecs/test/ec2/integ.exec-command.ts | 54 + .../test/fargate/fargate-service.test.ts | 741 +++++++++++ .../fargate/integ.exec-command.expected.json | 724 ++++++++++ .../test/fargate/integ.exec-command.ts | 50 + 10 files changed, 3940 insertions(+), 1 deletion(-) create mode 100644 packages/@aws-cdk/aws-ecs/test/ec2/integ.exec-command.expected.json create mode 100644 packages/@aws-cdk/aws-ecs/test/ec2/integ.exec-command.ts create mode 100644 packages/@aws-cdk/aws-ecs/test/fargate/integ.exec-command.expected.json create mode 100644 packages/@aws-cdk/aws-ecs/test/fargate/integ.exec-command.ts diff --git a/packages/@aws-cdk/aws-ecs/README.md b/packages/@aws-cdk/aws-ecs/README.md index 5e63747de137f..6aa894f458b94 100644 --- a/packages/@aws-cdk/aws-ecs/README.md +++ b/packages/@aws-cdk/aws-ecs/README.md @@ -850,3 +850,60 @@ taskDefinition.addContainer('cont', { inferenceAcceleratorResources, }); ``` + +## ECS Exec command + +Please note, ECS Exec leverages AWS Systems Manager (SSM). So as a prerequisite for the exec command +to work, you need to have the SSM plugin for the AWS CLI installed locally. For more information, see +[Install Session Manager plugin for AWS CLI] (https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html). + +To enable the ECS Exec feature for your containers, set the boolean flag `enableExecuteCommand` to `true` in +your `Ec2Service` or `FargateService`. + +```ts +const service = new ecs.Ec2Service(stack, 'Service', { + cluster, + taskDefinition, + enableExecuteCommand: true, +}); +``` + +### Enabling logging + +You can enable sending logs of your execute session commands to a CloudWatch log group or S3 bucket by configuring +the `executeCommandConfiguration` property for your cluster. The default configuration will send the +logs to the CloudWatch Logs using the `awslogs` log driver that is configured in your task definition. Please note, +when using your own `logConfiguration` the log group or S3 Bucket specified must already be created. + +To encrypt data using your own KMS Customer Key (CMK), you must create a CMK and provide the key in the `kmsKey` field +of the `executeCommandConfiguration`. To use this key for encrypting CloudWatch log data or S3 bucket, make sure to associate the key +to these resources on creation. + +```ts +const kmsKey = new kms.Key(stack, 'KmsKey'); + +// Pass the KMS key in the `encryptionKey` field to associate the key to the log group +const logGroup = new logs.LogGroup(stack, 'LogGroup', { + encryptionKey: kmsKey, +}); + +// Pass the KMS key in the `encryptionKey` field to associate the key to the S3 bucket +const execBucket = new s3.Bucket(stack, 'EcsExecBucket', { + encryptionKey: kmsKey, +}); + +const cluster = new ecs.Cluster(stack, 'Cluster', { + vpc, + executeCommandConfiguration: { + kmsKey, + logConfiguration: { + cloudWatchLogGroup: logGroup, + cloudWatchEncryptionEnabled: true, + s3Bucket: execBucket, + s3EncryptionEnabled: true, + s3KeyPrefix: 'exec-command-output', + }, + logging: ecs.ExecuteCommandLogging.OVERRIDE, + }, +}); +``` diff --git a/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts b/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts index fa33efd2fe0ab..d4023a4df6f9e 100644 --- a/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts +++ b/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts @@ -8,7 +8,7 @@ import * as cloudmap from '@aws-cdk/aws-servicediscovery'; import { Annotations, Duration, IResolvable, IResource, Lazy, Resource, Stack } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { LoadBalancerTargetOptions, NetworkMode, TaskDefinition } from '../base/task-definition'; -import { ICluster, CapacityProviderStrategy } from '../cluster'; +import { ICluster, CapacityProviderStrategy, ExecuteCommandLogging } from '../cluster'; import { ContainerDefinition, Protocol } from '../container-definition'; import { CfnService } from '../ecs.generated'; import { ScalableTaskCount } from './scalable-task-count'; @@ -189,6 +189,13 @@ export interface BaseServiceOptions { * */ readonly capacityProviderStrategies?: CapacityProviderStrategy[]; + + /** + * Whether to enable the ability to execute into a container + * + * @default - undefined + */ + readonly enableExecuteCommand?: boolean; } /** @@ -391,6 +398,7 @@ export abstract class BaseService extends Resource type: DeploymentControllerType.ECS, } : props.deploymentController, launchType: launchType, + enableExecuteCommand: props.enableExecuteCommand, capacityProviderStrategy: props.capacityProviderStrategies, healthCheckGracePeriodSeconds: this.evaluateHealthGracePeriod(props.healthCheckGracePeriod), /* role: never specified, supplanted by Service Linked Role */ @@ -416,6 +424,18 @@ export abstract class BaseService extends Resource this.enableCloudMap(props.cloudMapOptions); } + if (props.enableExecuteCommand) { + this.enableExecuteCommand(); + + const logging = this.cluster.executeCommandConfiguration?.logging ?? ExecuteCommandLogging.DEFAULT; + + if (this.cluster.executeCommandConfiguration?.kmsKey) { + this.enableExecuteCommandEncryption(logging); + } + if (logging !== ExecuteCommandLogging.NONE) { + this.executeCommandLogConfiguration(); + } + } this.node.defaultChild = this.resource; } @@ -426,6 +446,84 @@ export abstract class BaseService extends Resource return this.cloudmapService; } + private executeCommandLogConfiguration() { + const logConfiguration = this.cluster.executeCommandConfiguration?.logConfiguration; + this.taskDefinition.addToTaskRolePolicy(new iam.PolicyStatement({ + actions: [ + 'logs:DescribeLogGroups', + ], + resources: ['*'], + })); + + const logGroupArn = logConfiguration?.cloudWatchLogGroup ? `arn:aws:logs:${this.stack.region}:${this.stack.account}:log-group:${logConfiguration.cloudWatchLogGroup.logGroupName}:*` : '*'; + this.taskDefinition.addToTaskRolePolicy(new iam.PolicyStatement({ + actions: [ + 'logs:CreateLogStream', + 'logs:DescribeLogStreams', + 'logs:PutLogEvents', + ], + resources: [logGroupArn], + })); + + if (logConfiguration?.s3Bucket?.bucketName) { + this.taskDefinition.addToTaskRolePolicy(new iam.PolicyStatement({ + actions: [ + 's3:GetBucketLocation', + ], + resources: ['*'], + })); + this.taskDefinition.addToTaskRolePolicy(new iam.PolicyStatement({ + actions: [ + 's3:PutObject', + ], + resources: [`arn:aws:s3:::${logConfiguration.s3Bucket.bucketName}/*`], + })); + if (logConfiguration.s3EncryptionEnabled) { + this.taskDefinition.addToTaskRolePolicy(new iam.PolicyStatement({ + actions: [ + 's3:GetEncryptionConfiguration', + ], + resources: [`arn:aws:s3:::${logConfiguration.s3Bucket.bucketName}`], + })); + } + } + } + + private enableExecuteCommandEncryption(logging: ExecuteCommandLogging) { + this.taskDefinition.addToTaskRolePolicy(new iam.PolicyStatement({ + actions: [ + 'kms:Decrypt', + 'kms:GenerateDataKey', + ], + resources: [`${this.cluster.executeCommandConfiguration?.kmsKey?.keyArn}`], + })); + + this.cluster.executeCommandConfiguration?.kmsKey?.addToResourcePolicy(new iam.PolicyStatement({ + actions: [ + 'kms:*', + ], + resources: ['*'], + principals: [new iam.ArnPrincipal(`arn:aws:iam::${this.stack.account}:root`)], + })); + + if (logging === ExecuteCommandLogging.DEFAULT || this.cluster.executeCommandConfiguration?.logConfiguration?.cloudWatchEncryptionEnabled) { + this.cluster.executeCommandConfiguration?.kmsKey?.addToResourcePolicy(new iam.PolicyStatement({ + actions: [ + 'kms:Encrypt*', + 'kms:Decrypt*', + 'kms:ReEncrypt*', + 'kms:GenerateDataKey*', + 'kms:Describe*', + ], + resources: ['*'], + principals: [new iam.ServicePrincipal(`logs.${this.stack.region}.amazonaws.com`)], + conditions: { + ArnLike: { 'kms:EncryptionContext:aws:logs:arn': `arn:aws:logs:${this.stack.region}:${this.stack.account}:*` }, + }, + })); + } + } + /** * This method is called to attach this service to an Application Load Balancer. * @@ -790,6 +888,18 @@ export abstract class BaseService extends Resource produce: () => providedHealthCheckGracePeriod?.toSeconds() ?? (this.loadBalancers.length > 0 ? 60 : undefined), }); } + + private enableExecuteCommand() { + this.taskDefinition.addToTaskRolePolicy(new iam.PolicyStatement({ + actions: [ + 'ssmmessages:CreateControlChannel', + 'ssmmessages:CreateDataChannel', + 'ssmmessages:OpenControlChannel', + 'ssmmessages:OpenDataChannel', + ], + resources: ['*'], + })); + } } /** diff --git a/packages/@aws-cdk/aws-ecs/lib/cluster.ts b/packages/@aws-cdk/aws-ecs/lib/cluster.ts index 294a88fcb1858..5c6e910e4507e 100644 --- a/packages/@aws-cdk/aws-ecs/lib/cluster.ts +++ b/packages/@aws-cdk/aws-ecs/lib/cluster.ts @@ -3,6 +3,8 @@ import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; +import * as logs from '@aws-cdk/aws-logs'; +import * as s3 from '@aws-cdk/aws-s3'; import * as cloudmap from '@aws-cdk/aws-servicediscovery'; import * as ssm from '@aws-cdk/aws-ssm'; import { Duration, Lazy, IResource, Resource, Stack, Aspects, IAspect, IConstruct } from '@aws-cdk/core'; @@ -69,6 +71,13 @@ export interface ClusterProps { * @default - Container Insights will be disabled for this cluser. */ readonly containerInsights?: boolean; + + /** + * The execute command configuration for the cluster + * + * @default - no configuration will be provided. + */ + readonly executeCommandConfiguration?: ExecuteCommandConfiguration; } /** @@ -141,6 +150,11 @@ export class Cluster extends Resource implements ICluster { */ private _autoscalingGroup?: autoscaling.IAutoScalingGroup; + /** + * The execute command configuration for the cluster + */ + private _executeCommandConfiguration?: ExecuteCommandConfiguration; + /** * Constructs a new instance of the Cluster class. */ @@ -164,10 +178,19 @@ export class Cluster extends Resource implements ICluster { this.enableFargateCapacityProviders(); } + if (props.executeCommandConfiguration) { + if ((props.executeCommandConfiguration.logging === ExecuteCommandLogging.OVERRIDE) !== + (props.executeCommandConfiguration.logConfiguration !== undefined)) { + throw new Error('Execute command log configuration must only be specified when logging is OVERRIDE.'); + } + this._executeCommandConfiguration = props.executeCommandConfiguration; + } + const cluster = new CfnCluster(this, 'Resource', { clusterName: this.physicalName, clusterSettings, capacityProviders: Lazy.list({ produce: () => this._fargateCapacityProviders }, { omitEmpty: true }), + configuration: this._executeCommandConfiguration && this.renderExecuteCommandConfiguration(), }); this.clusterArn = this.getResourceArnAttribute(cluster.attrArn, { @@ -207,6 +230,33 @@ export class Cluster extends Resource implements ICluster { } } + private renderExecuteCommandConfiguration() : CfnCluster.ClusterConfigurationProperty { + return { + executeCommandConfiguration: { + kmsKeyId: this._executeCommandConfiguration?.kmsKey?.keyArn, + logConfiguration: this._executeCommandConfiguration?.logConfiguration && this.renderExecuteCommandLogConfiguration(), + logging: this._executeCommandConfiguration?.logging, + }, + }; + } + + private renderExecuteCommandLogConfiguration(): CfnCluster.ExecuteCommandLogConfigurationProperty { + const logConfiguration = this._executeCommandConfiguration?.logConfiguration; + if (logConfiguration?.s3EncryptionEnabled && !logConfiguration?.s3Bucket) { + throw new Error('You must specify an S3 bucket name in the execute command log configuration to enable S3 encryption.'); + } + if (logConfiguration?.cloudWatchEncryptionEnabled && !logConfiguration?.cloudWatchLogGroup) { + throw new Error('You must specify a CloudWatch log group in the execute command log configuration to enable CloudWatch encryption.'); + } + return { + cloudWatchEncryptionEnabled: logConfiguration?.cloudWatchEncryptionEnabled, + cloudWatchLogGroupName: logConfiguration?.cloudWatchLogGroup?.logGroupName, + s3BucketName: logConfiguration?.s3Bucket?.bucketName, + s3EncryptionEnabled: logConfiguration?.s3EncryptionEnabled, + s3KeyPrefix: logConfiguration?.s3KeyPrefix, + }; + } + /** * Add an AWS Cloud Map DNS namespace for this cluster. * NOTE: HttpNamespaces are not supported, as ECS always requires a DNSConfig when registering an instance to a Cloud @@ -458,6 +508,13 @@ export class Cluster extends Resource implements ICluster { return this._hasEc2Capacity; } + /** + * Getter for execute command configuration associated with the cluster. + */ + public get executeCommandConfiguration(): ExecuteCommandConfiguration | undefined { + return this._executeCommandConfiguration; + } + /** * This method returns the CloudWatch metric for this clusters CPU reservation. * @@ -790,6 +847,11 @@ export interface ICluster extends IResource { * The autoscaling group added to the cluster if capacity is associated to the cluster */ readonly autoscalingGroup?: autoscaling.IAutoScalingGroup; + + /** + * The execute command configuration for the cluster + */ + readonly executeCommandConfiguration?: ExecuteCommandConfiguration; } /** @@ -838,6 +900,13 @@ export interface ClusterAttributes { * @default - No default autoscaling group */ readonly autoscalingGroup?: autoscaling.IAutoScalingGroup; + + /** + * The execute command configuration for the cluster + * + * @default - none. + */ + readonly executeCommandConfiguration?: ExecuteCommandConfiguration; } /** @@ -874,6 +943,11 @@ class ImportedCluster extends Resource implements ICluster { */ private _defaultCloudMapNamespace?: cloudmap.INamespace; + /** + * The execute command configuration for the cluster + */ + private _executeCommandConfiguration?: ExecuteCommandConfiguration; + /** * Constructs a new instance of the ImportedCluster class. */ @@ -883,6 +957,7 @@ class ImportedCluster extends Resource implements ICluster { this.vpc = props.vpc; this.hasEc2Capacity = props.hasEc2Capacity !== false; this._defaultCloudMapNamespace = props.defaultCloudMapNamespace; + this._executeCommandConfiguration = props.executeCommandConfiguration; this.clusterArn = props.clusterArn ?? Stack.of(this).formatArn({ service: 'ecs', @@ -898,6 +973,10 @@ class ImportedCluster extends Resource implements ICluster { public get defaultCloudMapNamespace(): cloudmap.INamespace | undefined { return this._defaultCloudMapNamespace; } + + public get executeCommandConfiguration(): ExecuteCommandConfiguration | undefined { + return this._executeCommandConfiguration; + } } /** @@ -1060,6 +1139,94 @@ capacity provider. The weight value is taken into consideration after the base v readonly weight?: number; } +/** + * The details of the execute command configuration. For more information, see + * [ExecuteCommandConfiguration] https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-cluster-executecommandconfiguration.html + */ +export interface ExecuteCommandConfiguration { + /** + * The AWS Key Management Service key ID to encrypt the data between the local client and the container. + * + * @default - none + */ + readonly kmsKey?: kms.IKey, + + /** + * The log configuration for the results of the execute command actions. The logs can be sent to CloudWatch Logs or an Amazon S3 bucket. + * + * @default - none + */ + readonly logConfiguration?: ExecuteCommandLogConfiguration, + + /** + * The log settings to use for logging the execute command session. + * + * @default - none + */ + readonly logging?: ExecuteCommandLogging, +} + +/** + * The log settings to use to for logging the execute command session. For more information, see + * [Logging] https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-cluster-executecommandconfiguration.html#cfn-ecs-cluster-executecommandconfiguration-logging + */ +export enum ExecuteCommandLogging { + /** + * The execute command session is not logged. + */ + NONE = 'NONE', + + /** + * The awslogs configuration in the task definition is used. If no logging parameter is specified, it defaults to this value. If no awslogs log driver is configured in the task definition, the output won't be logged. + */ + DEFAULT = 'DEFAULT', + + /** + * Specify the logging details as a part of logConfiguration. + */ + OVERRIDE = 'OVERRIDE', +} + +/** + * The log configuration for the results of the execute command actions. The logs can be sent to CloudWatch Logs and/ or an Amazon S3 bucket. + * For more information, see [ExecuteCommandLogConfiguration] https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-cluster-executecommandlogconfiguration.html + */ +export interface ExecuteCommandLogConfiguration { + /** + * Whether or not to enable encryption on the CloudWatch logs. + * + * @default - encryption will be disabled. + */ + readonly cloudWatchEncryptionEnabled?: boolean, + + /** + * The name of the CloudWatch log group to send logs to. The CloudWatch log group must already be created. + * @default - none + */ + readonly cloudWatchLogGroup?: logs.ILogGroup, + + /** + * The name of the S3 bucket to send logs to. The S3 bucket must already be created. + * + * @default - none + */ + readonly s3Bucket?: s3.IBucket, + + /** + * Whether or not to enable encryption on the CloudWatch logs. + * + * @default - encryption will be disabled. + */ + readonly s3EncryptionEnabled?: boolean, + + /** + * An optional folder in the S3 bucket to place logs in. + * + * @default - none + */ + readonly s3KeyPrefix?: string +} + /** * The options for creating an Auto Scaling Group Capacity Provider. */ diff --git a/packages/@aws-cdk/aws-ecs/test/cluster.test.ts b/packages/@aws-cdk/aws-ecs/test/cluster.test.ts index a76c98377c1db..a580cb5dca197 100644 --- a/packages/@aws-cdk/aws-ecs/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-ecs/test/cluster.test.ts @@ -9,6 +9,8 @@ import { import * as autoscaling from '@aws-cdk/aws-autoscaling'; 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 s3 from '@aws-cdk/aws-s3'; import * as cloudmap from '@aws-cdk/aws-servicediscovery'; import * as cdk from '@aws-cdk/core'; import { nodeunitShim, Test } from 'nodeunit-shim'; @@ -1947,4 +1949,142 @@ nodeunitShim({ })); test.done(); }, + + 'correctly sets log configuration for execute command'(test: Test) { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'test'); + + const kmsKey = new kms.Key(stack, 'KmsKey'); + + const logGroup = new logs.LogGroup(stack, 'LogGroup', { + encryptionKey: kmsKey, + }); + + const execBucket = new s3.Bucket(stack, 'EcsExecBucket', { + encryptionKey: kmsKey, + }); + + // WHEN + new ecs.Cluster(stack, 'EcsCluster', { + executeCommandConfiguration: { + kmsKey: kmsKey, + logConfiguration: { + cloudWatchLogGroup: logGroup, + cloudWatchEncryptionEnabled: true, + s3Bucket: execBucket, + s3EncryptionEnabled: true, + s3KeyPrefix: 'exec-output', + }, + logging: ecs.ExecuteCommandLogging.OVERRIDE, + }, + }); + + // THEN + expect(stack).to(haveResource('AWS::ECS::Cluster', { + Configuration: { + ExecuteCommandConfiguration: { + KmsKeyId: { + 'Fn::GetAtt': [ + 'KmsKey46693ADD', + 'Arn', + ], + }, + LogConfiguration: { + CloudWatchEncryptionEnabled: true, + CloudWatchLogGroupName: { + Ref: 'LogGroupF5B46931', + }, + S3BucketName: { + Ref: 'EcsExecBucket4F468651', + }, + S3EncryptionEnabled: true, + S3KeyPrefix: 'exec-output', + }, + Logging: 'OVERRIDE', + }, + }, + })); + + test.done(); + }, + + 'throws when no log configuration is provided when logging is set to OVERRIDE'(test: Test) { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'test'); + + // THEN + test.throws(() => { + new ecs.Cluster(stack, 'EcsCluster', { + executeCommandConfiguration: { + logging: ecs.ExecuteCommandLogging.OVERRIDE, + }, + }); + }, /Execute command log configuration must only be specified when logging is OVERRIDE./); + + test.done(); + }, + + 'throws when log configuration provided but logging is set to DEFAULT'(test: Test) { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'test'); + + const logGroup = new logs.LogGroup(stack, 'LogGroup'); + + // THEN + test.throws(() => { + new ecs.Cluster(stack, 'EcsCluster', { + executeCommandConfiguration: { + logConfiguration: { + cloudWatchLogGroup: logGroup, + }, + logging: ecs.ExecuteCommandLogging.DEFAULT, + }, + }); + }, /Execute command log configuration must only be specified when logging is OVERRIDE./); + + test.done(); + }, + + 'throws when CloudWatchEncryptionEnabled without providing CloudWatch Logs log group name'(test: Test) { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'test'); + + // THEN + test.throws(() => { + new ecs.Cluster(stack, 'EcsCluster', { + executeCommandConfiguration: { + logConfiguration: { + cloudWatchEncryptionEnabled: true, + }, + logging: ecs.ExecuteCommandLogging.OVERRIDE, + }, + }); + }, /You must specify a CloudWatch log group in the execute command log configuration to enable CloudWatch encryption./); + + test.done(); + }, + + 'throws when S3EncryptionEnabled without providing S3 Bucket name'(test: Test) { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'test'); + + // THEN + test.throws(() => { + new ecs.Cluster(stack, 'EcsCluster', { + executeCommandConfiguration: { + logConfiguration: { + s3EncryptionEnabled: true, + }, + logging: ecs.ExecuteCommandLogging.OVERRIDE, + }, + }); + }, /You must specify an S3 bucket name in the execute command log configuration to enable S3 encryption./); + + test.done(); + }, }); 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 036604d079c71..f53f66d8755ad 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 @@ -3,6 +3,9 @@ import * as autoscaling from '@aws-cdk/aws-autoscaling'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as elb from '@aws-cdk/aws-elasticloadbalancing'; import * as elbv2 from '@aws-cdk/aws-elasticloadbalancingv2'; +import * as kms from '@aws-cdk/aws-kms'; +import * as logs from '@aws-cdk/aws-logs'; +import * as s3 from '@aws-cdk/aws-s3'; import * as cloudmap from '@aws-cdk/aws-servicediscovery'; import * as cdk from '@aws-cdk/core'; import { nodeunitShim, Test } from 'nodeunit-shim'; @@ -52,6 +55,726 @@ nodeunitShim({ test.done(); }, + 'allows setting enable execute command'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + cluster.addCapacity('DefaultAutoScalingGroup', { instanceType: new ec2.InstanceType('t2.micro') }); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef'); + + taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + memoryLimitMiB: 512, + }); + + new ecs.Ec2Service(stack, 'Ec2Service', { + cluster, + taskDefinition, + enableExecuteCommand: true, + }); + + // THEN + expect(stack).to(haveResource('AWS::ECS::Service', { + TaskDefinition: { + Ref: 'Ec2TaskDef0226F28C', + }, + Cluster: { + Ref: 'EcsCluster97242B84', + }, + DeploymentConfiguration: { + MaximumPercent: 200, + MinimumHealthyPercent: 50, + }, + LaunchType: LaunchType.EC2, + SchedulingStrategy: 'REPLICA', + EnableECSManagedTags: false, + EnableExecuteCommand: true, + })); + + expect(stack).to(haveResource('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: [ + 'ssmmessages:CreateControlChannel', + 'ssmmessages:CreateDataChannel', + 'ssmmessages:OpenControlChannel', + 'ssmmessages:OpenDataChannel', + ], + Effect: 'Allow', + Resource: '*', + }, + { + Action: 'logs:DescribeLogGroups', + Effect: 'Allow', + Resource: '*', + }, + { + Action: [ + 'logs:CreateLogStream', + 'logs:DescribeLogStreams', + 'logs:PutLogEvents', + ], + Effect: 'Allow', + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'Ec2TaskDefTaskRoleDefaultPolicyA24FB970', + Roles: [ + { + Ref: 'Ec2TaskDefTaskRole400FA349', + }, + ], + })); + + test.done(); + }, + + 'no logging enabled when logging field is set to NONE'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + + // WHEN + const cluster = new ecs.Cluster(stack, 'EcsCluster', { + vpc, + executeCommandConfiguration: { + logging: ecs.ExecuteCommandLogging.NONE, + }, + }); + cluster.addCapacity('DefaultAutoScalingGroup', { instanceType: new ec2.InstanceType('t2.micro') }); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef'); + + const logGroup = new logs.LogGroup(stack, 'LogGroup'); + + taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + logging: ecs.LogDrivers.awsLogs({ + logGroup, + streamPrefix: 'log-group', + }), + memoryLimitMiB: 512, + }); + + new ecs.Ec2Service(stack, 'Ec2Service', { + cluster, + taskDefinition, + enableExecuteCommand: true, + }); + + // THEN + expect(stack).to(haveResource('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: [ + 'ssmmessages:CreateControlChannel', + 'ssmmessages:CreateDataChannel', + 'ssmmessages:OpenControlChannel', + 'ssmmessages:OpenDataChannel', + ], + Effect: 'Allow', + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'Ec2TaskDefTaskRoleDefaultPolicyA24FB970', + Roles: [ + { + Ref: 'Ec2TaskDefTaskRole400FA349', + }, + ], + })); + + test.done(); + }, + + 'enables execute command logging when logging field is set to OVERRIDE'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + + const logGroup = new logs.LogGroup(stack, 'LogGroup'); + + const execBucket = new s3.Bucket(stack, 'ExecBucket'); + + // WHEN + const cluster = new ecs.Cluster(stack, 'EcsCluster', { + vpc, + executeCommandConfiguration: { + logConfiguration: { + cloudWatchLogGroup: logGroup, + s3Bucket: execBucket, + s3KeyPrefix: 'exec-output', + }, + logging: ecs.ExecuteCommandLogging.OVERRIDE, + }, + }); + cluster.addCapacity('DefaultAutoScalingGroup', { instanceType: new ec2.InstanceType('t2.micro') }); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef'); + + taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + memoryLimitMiB: 512, + }); + + new ecs.Ec2Service(stack, 'Ec2Service', { + cluster, + taskDefinition, + enableExecuteCommand: true, + }); + + // THEN + expect(stack).to(haveResource('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: [ + 'ssmmessages:CreateControlChannel', + 'ssmmessages:CreateDataChannel', + 'ssmmessages:OpenControlChannel', + 'ssmmessages:OpenDataChannel', + ], + Effect: 'Allow', + Resource: '*', + }, + { + Action: 'logs:DescribeLogGroups', + Effect: 'Allow', + Resource: '*', + }, + { + Action: [ + 'logs:CreateLogStream', + 'logs:DescribeLogStreams', + 'logs:PutLogEvents', + ], + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:aws:logs:', + { + Ref: 'AWS::Region', + }, + ':', + { + Ref: 'AWS::AccountId', + }, + ':log-group:', + { + Ref: 'LogGroupF5B46931', + }, + ':*', + ], + ], + }, + }, + { + Action: 's3:GetBucketLocation', + Effect: 'Allow', + Resource: '*', + }, + { + Action: 's3:PutObject', + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:aws:s3:::', + { + Ref: 'ExecBucket29559356', + }, + '/*', + ], + ], + }, + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'Ec2TaskDefTaskRoleDefaultPolicyA24FB970', + Roles: [ + { + Ref: 'Ec2TaskDefTaskRole400FA349', + }, + ], + })); + + test.done(); + }, + + 'enables only execute command session encryption'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + + const kmsKey = new kms.Key(stack, 'KmsKey'); + + const logGroup = new logs.LogGroup(stack, 'LogGroup'); + + const execBucket = new s3.Bucket(stack, 'EcsExecBucket'); + + // WHEN + const cluster = new ecs.Cluster(stack, 'EcsCluster', { + vpc, + executeCommandConfiguration: { + kmsKey, + logConfiguration: { + cloudWatchLogGroup: logGroup, + s3Bucket: execBucket, + }, + logging: ecs.ExecuteCommandLogging.OVERRIDE, + }, + }); + + cluster.addCapacity('DefaultAutoScalingGroup', { instanceType: new ec2.InstanceType('t2.micro') }); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef'); + + taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + memoryLimitMiB: 512, + }); + + new ecs.Ec2Service(stack, 'Ec2Service', { + cluster, + taskDefinition, + enableExecuteCommand: true, + }); + + // THEN + expect(stack).to(haveResource('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: [ + 'ssmmessages:CreateControlChannel', + 'ssmmessages:CreateDataChannel', + 'ssmmessages:OpenControlChannel', + 'ssmmessages:OpenDataChannel', + ], + Effect: 'Allow', + Resource: '*', + }, + { + Action: [ + 'kms:Decrypt', + 'kms:GenerateDataKey', + ], + Effect: 'Allow', + Resource: { + 'Fn::GetAtt': [ + 'KmsKey46693ADD', + 'Arn', + ], + }, + }, + { + Action: 'logs:DescribeLogGroups', + Effect: 'Allow', + Resource: '*', + }, + { + Action: [ + 'logs:CreateLogStream', + 'logs:DescribeLogStreams', + 'logs:PutLogEvents', + ], + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:aws:logs:', + { + Ref: 'AWS::Region', + }, + ':', + { + Ref: 'AWS::AccountId', + }, + ':log-group:', + { + Ref: 'LogGroupF5B46931', + }, + ':*', + ], + ], + }, + }, + { + Action: 's3:GetBucketLocation', + Effect: 'Allow', + Resource: '*', + }, + { + Action: 's3:PutObject', + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:aws:s3:::', + { + Ref: 'EcsExecBucket4F468651', + }, + '/*', + ], + ], + }, + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'Ec2TaskDefTaskRoleDefaultPolicyA24FB970', + Roles: [ + { + Ref: 'Ec2TaskDefTaskRole400FA349', + }, + ], + })); + + expect(stack).to(haveResource('AWS::KMS::Key', { + KeyPolicy: { + Statement: [ + { + Action: [ + 'kms:Create*', + 'kms:Describe*', + 'kms:Enable*', + 'kms:List*', + 'kms:Put*', + 'kms:Update*', + 'kms:Revoke*', + 'kms:Disable*', + 'kms:Get*', + 'kms:Delete*', + 'kms:ScheduleKeyDeletion', + 'kms:CancelKeyDeletion', + 'kms:GenerateDataKey', + 'kms:TagResource', + 'kms:UntagResource', + ], + Effect: 'Allow', + Principal: { + AWS: { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':iam::', + { + Ref: 'AWS::AccountId', + }, + ':root', + ], + ], + }, + }, + Resource: '*', + }, + { + Action: 'kms:*', + Effect: 'Allow', + Principal: { + AWS: { + 'Fn::Join': [ + '', + [ + 'arn:aws:iam::', + { + Ref: 'AWS::AccountId', + }, + ':root', + ], + ], + }, + }, + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + })); + + test.done(); + }, + + 'enables encryption for execute command logging'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + + const kmsKey = new kms.Key(stack, 'KmsKey'); + + const logGroup = new logs.LogGroup(stack, 'LogGroup', { + encryptionKey: kmsKey, + }); + + const execBucket = new s3.Bucket(stack, 'EcsExecBucket', { + encryptionKey: kmsKey, + }); + + // WHEN + const cluster = new ecs.Cluster(stack, 'EcsCluster', { + vpc, + executeCommandConfiguration: { + kmsKey, + logConfiguration: { + cloudWatchLogGroup: logGroup, + cloudWatchEncryptionEnabled: true, + s3Bucket: execBucket, + s3EncryptionEnabled: true, + s3KeyPrefix: 'exec-output', + }, + logging: ecs.ExecuteCommandLogging.OVERRIDE, + }, + }); + + cluster.addCapacity('DefaultAutoScalingGroup', { instanceType: new ec2.InstanceType('t2.micro') }); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef'); + + taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + memoryLimitMiB: 512, + }); + + new ecs.Ec2Service(stack, 'Ec2Service', { + cluster, + taskDefinition, + enableExecuteCommand: true, + }); + + // THEN + expect(stack).to(haveResource('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: [ + 'ssmmessages:CreateControlChannel', + 'ssmmessages:CreateDataChannel', + 'ssmmessages:OpenControlChannel', + 'ssmmessages:OpenDataChannel', + ], + Effect: 'Allow', + Resource: '*', + }, + { + Action: [ + 'kms:Decrypt', + 'kms:GenerateDataKey', + ], + Effect: 'Allow', + Resource: { + 'Fn::GetAtt': [ + 'KmsKey46693ADD', + 'Arn', + ], + }, + }, + { + Action: 'logs:DescribeLogGroups', + Effect: 'Allow', + Resource: '*', + }, + { + Action: [ + 'logs:CreateLogStream', + 'logs:DescribeLogStreams', + 'logs:PutLogEvents', + ], + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:aws:logs:', + { + Ref: 'AWS::Region', + }, + ':', + { + Ref: 'AWS::AccountId', + }, + ':log-group:', + { + Ref: 'LogGroupF5B46931', + }, + ':*', + ], + ], + }, + }, + { + Action: 's3:GetBucketLocation', + Effect: 'Allow', + Resource: '*', + }, + { + Action: 's3:PutObject', + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:aws:s3:::', + { + Ref: 'EcsExecBucket4F468651', + }, + '/*', + ], + ], + }, + }, + { + Action: 's3:GetEncryptionConfiguration', + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:aws:s3:::', + { + Ref: 'EcsExecBucket4F468651', + }, + ], + ], + }, + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'Ec2TaskDefTaskRoleDefaultPolicyA24FB970', + Roles: [ + { + Ref: 'Ec2TaskDefTaskRole400FA349', + }, + ], + })); + + expect(stack).to(haveResource('AWS::KMS::Key', { + KeyPolicy: { + Statement: [ + { + Action: [ + 'kms:Create*', + 'kms:Describe*', + 'kms:Enable*', + 'kms:List*', + 'kms:Put*', + 'kms:Update*', + 'kms:Revoke*', + 'kms:Disable*', + 'kms:Get*', + 'kms:Delete*', + 'kms:ScheduleKeyDeletion', + 'kms:CancelKeyDeletion', + 'kms:GenerateDataKey', + 'kms:TagResource', + 'kms:UntagResource', + ], + Effect: 'Allow', + Principal: { + AWS: { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':iam::', + { + Ref: 'AWS::AccountId', + }, + ':root', + ], + ], + }, + }, + Resource: '*', + }, + { + Action: 'kms:*', + Effect: 'Allow', + Principal: { + AWS: { + 'Fn::Join': [ + '', + [ + 'arn:aws:iam::', + { + Ref: 'AWS::AccountId', + }, + ':root', + ], + ], + }, + }, + Resource: '*', + }, + { + Action: [ + 'kms:Encrypt*', + 'kms:Decrypt*', + 'kms:ReEncrypt*', + 'kms:GenerateDataKey*', + 'kms:Describe*', + ], + Condition: { + ArnLike: { + 'kms:EncryptionContext:aws:logs:arn': { + 'Fn::Join': [ + '', + [ + 'arn:aws:logs:', + { + Ref: 'AWS::Region', + }, + ':', + { + Ref: 'AWS::AccountId', + }, + ':*', + ], + ], + }, + }, + }, + Effect: 'Allow', + Principal: { + Service: { + 'Fn::Join': [ + '', + [ + 'logs.', + { + Ref: 'AWS::Region', + }, + '.amazonaws.com', + ], + ], + }, + }, + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + })); + + test.done(); + }, + 'with custom cloudmap namespace'(test: Test) { // GIVEN const stack = new cdk.Stack(); diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.exec-command.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.exec-command.expected.json new file mode 100644 index 0000000000000..3f032ba6cc07e --- /dev/null +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.exec-command.expected.json @@ -0,0 +1,1173 @@ +{ + "Resources": { + "Vpc8378EB38": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Vpc" + } + ] + } + }, + "VpcPublicSubnet1Subnet5C2D37C4": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.0.0/18", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1RouteTable6C95E38E": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1RouteTableAssociation97140677": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + } + } + }, + "VpcPublicSubnet1DefaultRoute3DA9E72A": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VpcIGWD7BA715C" + } + }, + "DependsOn": [ + "VpcVPCGWBF912B6E" + ] + }, + "VpcPublicSubnet1EIPD7E02669": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1NATGateway4D7517AA": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet1EIPD7E02669", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet2Subnet691E08A3": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.64.0/18", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1b", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2RouteTable94F7E489": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2RouteTableAssociationDD5762D8": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + }, + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + } + } + }, + "VpcPublicSubnet2DefaultRoute97F91067": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VpcIGWD7BA715C" + } + }, + "DependsOn": [ + "VpcVPCGWBF912B6E" + ] + }, + "VpcPublicSubnet2EIP3C605A87": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2NATGateway9182C01D": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet2EIP3C605A87", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPrivateSubnet1Subnet536B997A": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.128.0/18", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Vpc/PrivateSubnet1" + } + ] + } + }, + "VpcPrivateSubnet1RouteTableB2C5B500": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Vpc/PrivateSubnet1" + } + ] + } + }, + "VpcPrivateSubnet1RouteTableAssociation70C59FA6": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "SubnetId": { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + } + }, + "VpcPrivateSubnet1DefaultRouteBE02A9ED": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VpcPublicSubnet1NATGateway4D7517AA" + } + } + }, + "VpcPrivateSubnet2Subnet3788AAA1": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.192.0/18", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1b", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Vpc/PrivateSubnet2" + } + ] + } + }, + "VpcPrivateSubnet2RouteTableA678073B": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Vpc/PrivateSubnet2" + } + ] + } + }, + "VpcPrivateSubnet2RouteTableAssociationA89CAD56": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + }, + "SubnetId": { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + } + }, + "VpcPrivateSubnet2DefaultRoute060D2087": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VpcPublicSubnet2NATGateway9182C01D" + } + } + }, + "VpcIGWD7BA715C": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Vpc" + } + ] + } + }, + "VpcVPCGWBF912B6E": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "InternetGatewayId": { + "Ref": "VpcIGWD7BA715C" + } + } + }, + "LogGroupF5B46931": { + "Type": "AWS::Logs::LogGroup", + "Properties": { + "RetentionInDays": "731", + "KmsKeyId": { + "Fn::GetAtt": [ + "KmsKey46693ADD", + "Arn" + ] + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "KmsKey46693ADD": { + "Type": "AWS::KMS::Key", + "Properties": { + "KeyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:aws:iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": [ + "kms:Encrypt*", + "kms:Decrypt*", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + "kms:Describe*" + ], + "Condition": { + "ArnLike": { + "kms:EncryptionContext:aws:logs:arn": { + "Fn::Join": [ + "", + [ + "arn:aws:logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":*" + ] + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": { + "Fn::Join": [ + "", + [ + "logs.", + { + "Ref": "AWS::Region" + }, + ".amazonaws.com" + ] + ] + } + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "EcsExecBucket4F468651": { + "Type": "AWS::S3::Bucket", + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "KMSMasterKeyID": { + "Fn::GetAtt": [ + "KmsKey46693ADD", + "Arn" + ] + }, + "SSEAlgorithm": "aws:kms" + } + } + ] + } + } + }, + "Ec2ClusterEE43E89D": { + "Type": "AWS::ECS::Cluster", + "Properties": { + "Configuration": { + "ExecuteCommandConfiguration": { + "KmsKeyId": { + "Fn::GetAtt": [ + "KmsKey46693ADD", + "Arn" + ] + }, + "LogConfiguration": { + "CloudWatchEncryptionEnabled": true, + "CloudWatchLogGroupName": { + "Ref": "LogGroupF5B46931" + }, + "S3BucketName": { + "Ref": "EcsExecBucket4F468651" + }, + "S3EncryptionEnabled": true, + "S3KeyPrefix": "exec-output" + }, + "Logging": "OVERRIDE" + } + } + } + }, + "Ec2ClusterDefaultAutoScalingGroupInstanceSecurityGroup149B0A9E": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "aws-ecs-integ-exec-command/Ec2Cluster/DefaultAutoScalingGroup/InstanceSecurityGroup", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Ec2Cluster/DefaultAutoScalingGroup" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "Ec2ClusterDefaultAutoScalingGroupInstanceRole73D80898": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": { + "Fn::Join": [ + "", + [ + "ec2.", + { + "Ref": "AWS::URLSuffix" + } + ] + ] + } + } + } + ], + "Version": "2012-10-17" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Ec2Cluster/DefaultAutoScalingGroup" + } + ] + } + }, + "Ec2ClusterDefaultAutoScalingGroupInstanceRoleDefaultPolicy6D2DC2FD": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ecs:DeregisterContainerInstance", + "ecs:RegisterContainerInstance", + "ecs:Submit*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "Ec2ClusterEE43E89D", + "Arn" + ] + } + }, + { + "Action": [ + "ecs:Poll", + "ecs:StartTelemetrySession" + ], + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "Ec2ClusterEE43E89D", + "Arn" + ] + } + } + }, + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "ecs:DiscoverPollEndpoint", + "ecr:GetAuthorizationToken", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "Ec2ClusterDefaultAutoScalingGroupInstanceRoleDefaultPolicy6D2DC2FD", + "Roles": [ + { + "Ref": "Ec2ClusterDefaultAutoScalingGroupInstanceRole73D80898" + } + ] + } + }, + "Ec2ClusterDefaultAutoScalingGroupInstanceProfileDB232471": { + "Type": "AWS::IAM::InstanceProfile", + "Properties": { + "Roles": [ + { + "Ref": "Ec2ClusterDefaultAutoScalingGroupInstanceRole73D80898" + } + ] + } + }, + "Ec2ClusterDefaultAutoScalingGroupLaunchConfig7B2FED3A": { + "Type": "AWS::AutoScaling::LaunchConfiguration", + "Properties": { + "ImageId": { + "Ref": "SsmParameterValueawsserviceecsoptimizedamiamazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter" + }, + "InstanceType": "t2.micro", + "IamInstanceProfile": { + "Ref": "Ec2ClusterDefaultAutoScalingGroupInstanceProfileDB232471" + }, + "SecurityGroups": [ + { + "Fn::GetAtt": [ + "Ec2ClusterDefaultAutoScalingGroupInstanceSecurityGroup149B0A9E", + "GroupId" + ] + } + ], + "UserData": { + "Fn::Base64": { + "Fn::Join": [ + "", + [ + "#!/bin/bash\necho ECS_CLUSTER=", + { + "Ref": "Ec2ClusterEE43E89D" + }, + " >> /etc/ecs/ecs.config\nsudo iptables --insert FORWARD 1 --in-interface docker+ --destination 169.254.169.254/32 --jump DROP\nsudo service iptables save\necho ECS_AWSVPC_BLOCK_IMDS=true >> /etc/ecs/ecs.config" + ] + ] + } + } + }, + "DependsOn": [ + "Ec2ClusterDefaultAutoScalingGroupInstanceRoleDefaultPolicy6D2DC2FD", + "Ec2ClusterDefaultAutoScalingGroupInstanceRole73D80898" + ] + }, + "Ec2ClusterDefaultAutoScalingGroupASGC5A6D4C0": { + "Type": "AWS::AutoScaling::AutoScalingGroup", + "Properties": { + "MaxSize": "1", + "MinSize": "1", + "LaunchConfigurationName": { + "Ref": "Ec2ClusterDefaultAutoScalingGroupLaunchConfig7B2FED3A" + }, + "Tags": [ + { + "Key": "Name", + "PropagateAtLaunch": true, + "Value": "aws-ecs-integ-exec-command/Ec2Cluster/DefaultAutoScalingGroup" + } + ], + "VPCZoneIdentifier": [ + { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + }, + { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + ] + }, + "UpdatePolicy": { + "AutoScalingReplacingUpdate": { + "WillReplace": true + }, + "AutoScalingScheduledAction": { + "IgnoreUnmodifiedGroupSizeProperties": true + } + } + }, + "Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole23116FA3": { + "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" + ] + ] + } + ], + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Ec2Cluster/DefaultAutoScalingGroup" + } + ] + } + }, + "Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRoleDefaultPolicy638C9E33": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ec2:DescribeInstances", + "ec2:DescribeInstanceAttribute", + "ec2:DescribeInstanceStatus", + "ec2:DescribeHosts" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": "autoscaling:CompleteLifecycleAction", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":autoscaling:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":autoScalingGroup:*:autoScalingGroupName/", + { + "Ref": "Ec2ClusterDefaultAutoScalingGroupASGC5A6D4C0" + } + ] + ] + } + }, + { + "Action": [ + "ecs:DescribeContainerInstances", + "ecs:DescribeTasks" + ], + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "Ec2ClusterEE43E89D", + "Arn" + ] + } + } + }, + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "ecs:ListContainerInstances", + "ecs:SubmitContainerStateChange", + "ecs:SubmitTaskStateChange" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "Ec2ClusterEE43E89D", + "Arn" + ] + } + }, + { + "Action": [ + "ecs:UpdateContainerInstancesState", + "ecs:ListTasks" + ], + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "Ec2ClusterEE43E89D", + "Arn" + ] + } + } + }, + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRoleDefaultPolicy638C9E33", + "Roles": [ + { + "Ref": "Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole23116FA3" + } + ] + } + }, + "Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionE0DEFB31": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "import boto3, json, os, time\n\necs = boto3.client('ecs')\nautoscaling = boto3.client('autoscaling')\n\n\ndef lambda_handler(event, context):\n print(json.dumps(event))\n cluster = os.environ['CLUSTER']\n snsTopicArn = event['Records'][0]['Sns']['TopicArn']\n lifecycle_event = json.loads(event['Records'][0]['Sns']['Message'])\n instance_id = lifecycle_event.get('EC2InstanceId')\n if not instance_id:\n print('Got event without EC2InstanceId: %s', json.dumps(event))\n return\n\n instance_arn = container_instance_arn(cluster, instance_id)\n print('Instance %s has container instance ARN %s' % (lifecycle_event['EC2InstanceId'], instance_arn))\n\n if not instance_arn:\n return\n\n task_arns = container_instance_task_arns(cluster, instance_arn)\n \n if task_arns:\n print('Instance ARN %s has task ARNs %s' % (instance_arn, ', '.join(task_arns)))\n\n while has_tasks(cluster, instance_arn, task_arns):\n time.sleep(10)\n\n try:\n print('Terminating instance %s' % instance_id)\n autoscaling.complete_lifecycle_action(\n LifecycleActionResult='CONTINUE',\n **pick(lifecycle_event, 'LifecycleHookName', 'LifecycleActionToken', 'AutoScalingGroupName'))\n except Exception as e:\n # Lifecycle action may have already completed.\n print(str(e))\n\n\ndef container_instance_arn(cluster, instance_id):\n \"\"\"Turn an instance ID into a container instance ARN.\"\"\"\n arns = ecs.list_container_instances(cluster=cluster, filter='ec2InstanceId==' + instance_id)['containerInstanceArns']\n if not arns:\n return None\n return arns[0]\n\ndef container_instance_task_arns(cluster, instance_arn):\n \"\"\"Fetch tasks for a container instance ARN.\"\"\"\n arns = ecs.list_tasks(cluster=cluster, containerInstance=instance_arn)['taskArns']\n return arns\n\ndef has_tasks(cluster, instance_arn, task_arns):\n \"\"\"Return True if the instance is running tasks for the given cluster.\"\"\"\n instances = ecs.describe_container_instances(cluster=cluster, containerInstances=[instance_arn])['containerInstances']\n if not instances:\n return False\n instance = instances[0]\n\n if instance['status'] == 'ACTIVE':\n # Start draining, then try again later\n set_container_instance_to_draining(cluster, instance_arn)\n return True\n\n task_count = None\n\n if task_arns:\n # Fetch details for tasks running on the container instance\n tasks = ecs.describe_tasks(cluster=cluster, tasks=task_arns)['tasks']\n if tasks:\n # Consider any non-stopped tasks as running\n task_count = sum(task['lastStatus'] != 'STOPPED' for task in tasks) + instance['pendingTasksCount']\n \n if not task_count:\n # Fallback to instance task counts if detailed task information is unavailable\n task_count = instance['runningTasksCount'] + instance['pendingTasksCount']\n \n print('Instance %s has %s tasks' % (instance_arn, task_count))\n\n return task_count > 0\n\ndef set_container_instance_to_draining(cluster, instance_arn):\n ecs.update_container_instances_state(\n cluster=cluster,\n containerInstances=[instance_arn], status='DRAINING')\n\n\ndef pick(dct, *keys):\n \"\"\"Pick a subset of a dict.\"\"\"\n return {k: v for k, v in dct.items() if k in keys}\n" + }, + "Role": { + "Fn::GetAtt": [ + "Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole23116FA3", + "Arn" + ] + }, + "Environment": { + "Variables": { + "CLUSTER": { + "Ref": "Ec2ClusterEE43E89D" + } + } + }, + "Handler": "index.lambda_handler", + "Runtime": "python3.6", + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Ec2Cluster/DefaultAutoScalingGroup" + } + ], + "Timeout": 310 + }, + "DependsOn": [ + "Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRoleDefaultPolicy638C9E33", + "Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole23116FA3" + ] + }, + "Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionAllowInvokeawsecsintegexeccommandEc2ClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopic05F8C92983E1AD32": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionE0DEFB31", + "Arn" + ] + }, + "Principal": "sns.amazonaws.com", + "SourceArn": { + "Ref": "Ec2ClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicF7263B30" + } + } + }, + "Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionTopic4795E0F6": { + "Type": "AWS::SNS::Subscription", + "Properties": { + "Protocol": "lambda", + "TopicArn": { + "Ref": "Ec2ClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicF7263B30" + }, + "Endpoint": { + "Fn::GetAtt": [ + "Ec2ClusterDefaultAutoScalingGroupDrainECSHookFunctionE0DEFB31", + "Arn" + ] + } + } + }, + "Ec2ClusterDefaultAutoScalingGroupLifecycleHookDrainHookRole71045ED7": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "autoscaling.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Ec2Cluster/DefaultAutoScalingGroup" + } + ] + } + }, + "Ec2ClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleDefaultPolicyE499974B": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sns:Publish", + "Effect": "Allow", + "Resource": { + "Ref": "Ec2ClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicF7263B30" + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "Ec2ClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleDefaultPolicyE499974B", + "Roles": [ + { + "Ref": "Ec2ClusterDefaultAutoScalingGroupLifecycleHookDrainHookRole71045ED7" + } + ] + } + }, + "Ec2ClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicF7263B30": { + "Type": "AWS::SNS::Topic", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Ec2Cluster/DefaultAutoScalingGroup" + } + ] + } + }, + "Ec2ClusterDefaultAutoScalingGroupLifecycleHookDrainHook5CB1467E": { + "Type": "AWS::AutoScaling::LifecycleHook", + "Properties": { + "AutoScalingGroupName": { + "Ref": "Ec2ClusterDefaultAutoScalingGroupASGC5A6D4C0" + }, + "LifecycleTransition": "autoscaling:EC2_INSTANCE_TERMINATING", + "DefaultResult": "CONTINUE", + "HeartbeatTimeout": 300, + "NotificationTargetARN": { + "Ref": "Ec2ClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicF7263B30" + }, + "RoleARN": { + "Fn::GetAtt": [ + "Ec2ClusterDefaultAutoScalingGroupLifecycleHookDrainHookRole71045ED7", + "Arn" + ] + } + }, + "DependsOn": [ + "Ec2ClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleDefaultPolicyE499974B", + "Ec2ClusterDefaultAutoScalingGroupLifecycleHookDrainHookRole71045ED7" + ] + }, + "TaskDefTaskRole1EDB4A67": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "TaskDefTaskRoleDefaultPolicyA592CB18": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ssmmessages:CreateControlChannel", + "ssmmessages:CreateDataChannel", + "ssmmessages:OpenControlChannel", + "ssmmessages:OpenDataChannel" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "kms:Decrypt", + "kms:GenerateDataKey" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "KmsKey46693ADD", + "Arn" + ] + } + }, + { + "Action": "logs:DescribeLogGroups", + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "logs:CreateLogStream", + "logs:DescribeLogStreams", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:aws:logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:", + { + "Ref": "LogGroupF5B46931" + }, + ":*" + ] + ] + } + }, + { + "Action": "s3:GetBucketLocation", + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": "s3:PutObject", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:aws:s3:::", + { + "Ref": "EcsExecBucket4F468651" + }, + "/*" + ] + ] + } + }, + { + "Action": "s3:GetEncryptionConfiguration", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:aws:s3:::", + { + "Ref": "EcsExecBucket4F468651" + } + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "TaskDefTaskRoleDefaultPolicyA592CB18", + "Roles": [{ + "Ref": "TaskDefTaskRole1EDB4A67" + }] + } + }, + "TaskDef54694570": { + "Type": "AWS::ECS::TaskDefinition", + "Properties": { + "ContainerDefinitions": [ + { + "Essential": true, + "Image": "amazon/amazon-ecs-sample", + "Memory": "256", + "Name": "web" + } + ], + "Family": "awsecsintegexeccommandTaskDef44709274", + "NetworkMode": "bridge", + "RequiresCompatibilities": [ + "EC2" + ], + "TaskRoleArn": { + "Fn::GetAtt": [ + "TaskDefTaskRole1EDB4A67", + "Arn" + ] + } + } + }, + "Ec2Service04A33183": { + "Type": "AWS::ECS::Service", + "Properties": { + "Cluster": { + "Ref": "Ec2ClusterEE43E89D" + }, + "DeploymentConfiguration": { + "MaximumPercent": 200, + "MinimumHealthyPercent": 50 + }, + "LaunchType": "EC2", + "EnableECSManagedTags": false, + "EnableExecuteCommand": true, + "SchedulingStrategy": "REPLICA", + "TaskDefinition": { + "Ref": "TaskDef54694570" + } + } + } + }, + "Parameters": { + "SsmParameterValueawsserviceecsoptimizedamiamazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.exec-command.ts b/packages/@aws-cdk/aws-ecs/test/ec2/integ.exec-command.ts new file mode 100644 index 0000000000000..ce48860fc4e6b --- /dev/null +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.exec-command.ts @@ -0,0 +1,54 @@ +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 s3 from '@aws-cdk/aws-s3'; +import * as cdk from '@aws-cdk/core'; +import * as ecs from '../../lib'; + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'aws-ecs-integ-exec-command'); + +const vpc = new ec2.Vpc(stack, 'Vpc', { maxAzs: 2 }); + +const kmsKey = new kms.Key(stack, 'KmsKey'); + +const logGroup = new logs.LogGroup(stack, 'LogGroup', { + encryptionKey: kmsKey, +}); + +const execBucket = new s3.Bucket(stack, 'EcsExecBucket', { + encryptionKey: kmsKey, +}); + +const cluster = new ecs.Cluster(stack, 'Ec2Cluster', { + vpc, + executeCommandConfiguration: { + kmsKey, + logConfiguration: { + cloudWatchLogGroup: logGroup, + cloudWatchEncryptionEnabled: true, + s3Bucket: execBucket, + s3EncryptionEnabled: true, + s3KeyPrefix: 'exec-output', + }, + logging: ecs.ExecuteCommandLogging.OVERRIDE, + }, +}); +cluster.addCapacity('DefaultAutoScalingGroup', { + instanceType: new ec2.InstanceType('t2.micro'), +}); + +const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef'); + +taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + memoryLimitMiB: 256, +}); + +new ecs.Ec2Service(stack, 'Ec2Service', { + cluster, + taskDefinition, + enableExecuteCommand: true, +}); + +app.synth(); \ No newline at end of file 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 af4b92370726d..33a5f2baf28d4 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 @@ -3,6 +3,9 @@ import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as elbv2 from '@aws-cdk/aws-elasticloadbalancingv2'; +import * as kms from '@aws-cdk/aws-kms'; +import * as logs from '@aws-cdk/aws-logs'; +import * as s3 from '@aws-cdk/aws-s3'; import * as secretsmanager from '@aws-cdk/aws-secretsmanager'; import * as cloudmap from '@aws-cdk/aws-servicediscovery'; import * as cdk from '@aws-cdk/core'; @@ -2199,5 +2202,743 @@ nodeunitShim({ test.done(); }, + + 'allows setting enable execute command'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + const taskDefinition = new ecs.FargateTaskDefinition(stack, 'FargateTaskDef'); + + taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + }); + + new ecs.FargateService(stack, 'FargateService', { + cluster, + taskDefinition, + enableExecuteCommand: true, + }); + + // THEN + expect(stack).to(haveResource('AWS::ECS::Service', { + TaskDefinition: { + Ref: 'FargateTaskDefC6FB60B4', + }, + Cluster: { + Ref: 'EcsCluster97242B84', + }, + DeploymentConfiguration: { + MaximumPercent: 200, + MinimumHealthyPercent: 50, + }, + LaunchType: LaunchType.FARGATE, + EnableECSManagedTags: false, + EnableExecuteCommand: true, + NetworkConfiguration: { + AwsvpcConfiguration: { + AssignPublicIp: 'DISABLED', + SecurityGroups: [ + { + 'Fn::GetAtt': [ + 'FargateServiceSecurityGroup0A0E79CB', + 'GroupId', + ], + }, + ], + Subnets: [ + { + Ref: 'MyVpcPrivateSubnet1Subnet5057CF7E', + }, + { + Ref: 'MyVpcPrivateSubnet2Subnet0040C983', + }, + ], + }, + }, + })); + + expect(stack).to(haveResource('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: [ + 'ssmmessages:CreateControlChannel', + 'ssmmessages:CreateDataChannel', + 'ssmmessages:OpenControlChannel', + 'ssmmessages:OpenDataChannel', + ], + Effect: 'Allow', + Resource: '*', + }, + { + Action: 'logs:DescribeLogGroups', + Effect: 'Allow', + Resource: '*', + }, + { + Action: [ + 'logs:CreateLogStream', + 'logs:DescribeLogStreams', + 'logs:PutLogEvents', + ], + Effect: 'Allow', + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'FargateTaskDefTaskRoleDefaultPolicy8EB25BBD', + Roles: [ + { + Ref: 'FargateTaskDefTaskRole0B257552', + }, + ], + })); + + test.done(); + }, + + 'no logging enabled when logging field is set to NONE'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + + // WHEN + const cluster = new ecs.Cluster(stack, 'EcsCluster', { + vpc, + executeCommandConfiguration: { + logging: ecs.ExecuteCommandLogging.NONE, + }, + }); + cluster.addCapacity('DefaultAutoScalingGroup', { instanceType: new ec2.InstanceType('t2.micro') }); + const taskDefinition = new ecs.FargateTaskDefinition(stack, 'FargateTaskDef'); + + const logGroup = new logs.LogGroup(stack, 'LogGroup'); + + taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + logging: ecs.LogDrivers.awsLogs({ + logGroup, + streamPrefix: 'log-group', + }), + memoryLimitMiB: 512, + }); + + new ecs.FargateService(stack, 'FargateService', { + cluster, + taskDefinition, + enableExecuteCommand: true, + }); + + // THEN + expect(stack).to(haveResource('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: [ + 'ssmmessages:CreateControlChannel', + 'ssmmessages:CreateDataChannel', + 'ssmmessages:OpenControlChannel', + 'ssmmessages:OpenDataChannel', + ], + Effect: 'Allow', + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'FargateTaskDefTaskRoleDefaultPolicy8EB25BBD', + Roles: [ + { + Ref: 'FargateTaskDefTaskRole0B257552', + }, + ], + })); + + test.done(); + }, + + 'enables execute command logging with logging field set to OVERRIDE'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + + const logGroup = new logs.LogGroup(stack, 'LogGroup'); + + const execBucket = new s3.Bucket(stack, 'ExecBucket'); + + // WHEN + const cluster = new ecs.Cluster(stack, 'EcsCluster', { + vpc, + executeCommandConfiguration: { + logConfiguration: { + cloudWatchLogGroup: logGroup, + s3Bucket: execBucket, + s3KeyPrefix: 'exec-output', + }, + logging: ecs.ExecuteCommandLogging.OVERRIDE, + }, + }); + cluster.addCapacity('DefaultAutoScalingGroup', { instanceType: new ec2.InstanceType('t2.micro') }); + const taskDefinition = new ecs.FargateTaskDefinition(stack, 'FargateTaskDef'); + + taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + memoryLimitMiB: 512, + }); + + new ecs.FargateService(stack, 'FargateService', { + cluster, + taskDefinition, + enableExecuteCommand: true, + }); + + // THEN + expect(stack).to(haveResource('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: [ + 'ssmmessages:CreateControlChannel', + 'ssmmessages:CreateDataChannel', + 'ssmmessages:OpenControlChannel', + 'ssmmessages:OpenDataChannel', + ], + Effect: 'Allow', + Resource: '*', + }, + { + Action: 'logs:DescribeLogGroups', + Effect: 'Allow', + Resource: '*', + }, + { + Action: [ + 'logs:CreateLogStream', + 'logs:DescribeLogStreams', + 'logs:PutLogEvents', + ], + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:aws:logs:', + { + Ref: 'AWS::Region', + }, + ':', + { + Ref: 'AWS::AccountId', + }, + ':log-group:', + { + Ref: 'LogGroupF5B46931', + }, + ':*', + ], + ], + }, + }, + { + Action: 's3:GetBucketLocation', + Effect: 'Allow', + Resource: '*', + }, + { + Action: 's3:PutObject', + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:aws:s3:::', + { + Ref: 'ExecBucket29559356', + }, + '/*', + ], + ], + }, + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'FargateTaskDefTaskRoleDefaultPolicy8EB25BBD', + Roles: [ + { + Ref: 'FargateTaskDefTaskRole0B257552', + }, + ], + })); + + test.done(); + }, + + 'enables only execute command session encryption'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + + const kmsKey = new kms.Key(stack, 'KmsKey'); + + const logGroup = new logs.LogGroup(stack, 'LogGroup'); + + const execBucket = new s3.Bucket(stack, 'EcsExecBucket'); + + // WHEN + const cluster = new ecs.Cluster(stack, 'EcsCluster', { + vpc, + executeCommandConfiguration: { + kmsKey, + logConfiguration: { + cloudWatchLogGroup: logGroup, + s3Bucket: execBucket, + }, + logging: ecs.ExecuteCommandLogging.OVERRIDE, + }, + }); + + cluster.addCapacity('DefaultAutoScalingGroup', { instanceType: new ec2.InstanceType('t2.micro') }); + const taskDefinition = new ecs.FargateTaskDefinition(stack, 'FargateTaskDef'); + + taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + memoryLimitMiB: 512, + }); + + new ecs.FargateService(stack, 'Ec2Service', { + cluster, + taskDefinition, + enableExecuteCommand: true, + }); + + // THEN + expect(stack).to(haveResource('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: [ + 'ssmmessages:CreateControlChannel', + 'ssmmessages:CreateDataChannel', + 'ssmmessages:OpenControlChannel', + 'ssmmessages:OpenDataChannel', + ], + Effect: 'Allow', + Resource: '*', + }, + { + Action: [ + 'kms:Decrypt', + 'kms:GenerateDataKey', + ], + Effect: 'Allow', + Resource: { + 'Fn::GetAtt': [ + 'KmsKey46693ADD', + 'Arn', + ], + }, + }, + { + Action: 'logs:DescribeLogGroups', + Effect: 'Allow', + Resource: '*', + }, + { + Action: [ + 'logs:CreateLogStream', + 'logs:DescribeLogStreams', + 'logs:PutLogEvents', + ], + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:aws:logs:', + { + Ref: 'AWS::Region', + }, + ':', + { + Ref: 'AWS::AccountId', + }, + ':log-group:', + { + Ref: 'LogGroupF5B46931', + }, + ':*', + ], + ], + }, + }, + { + Action: 's3:GetBucketLocation', + Effect: 'Allow', + Resource: '*', + }, + { + Action: 's3:PutObject', + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:aws:s3:::', + { + Ref: 'EcsExecBucket4F468651', + }, + '/*', + ], + ], + }, + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'FargateTaskDefTaskRoleDefaultPolicy8EB25BBD', + Roles: [ + { + Ref: 'FargateTaskDefTaskRole0B257552', + }, + ], + })); + + expect(stack).to(haveResource('AWS::KMS::Key', { + KeyPolicy: { + Statement: [ + { + Action: [ + 'kms:Create*', + 'kms:Describe*', + 'kms:Enable*', + 'kms:List*', + 'kms:Put*', + 'kms:Update*', + 'kms:Revoke*', + 'kms:Disable*', + 'kms:Get*', + 'kms:Delete*', + 'kms:ScheduleKeyDeletion', + 'kms:CancelKeyDeletion', + 'kms:GenerateDataKey', + 'kms:TagResource', + 'kms:UntagResource', + ], + Effect: 'Allow', + Principal: { + AWS: { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':iam::', + { + Ref: 'AWS::AccountId', + }, + ':root', + ], + ], + }, + }, + Resource: '*', + }, + { + Action: 'kms:*', + Effect: 'Allow', + Principal: { + AWS: { + 'Fn::Join': [ + '', + [ + 'arn:aws:iam::', + { + Ref: 'AWS::AccountId', + }, + ':root', + ], + ], + }, + }, + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + })); + + test.done(); + }, + + 'enables encryption for execute command logging'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + + const kmsKey = new kms.Key(stack, 'KmsKey'); + + const logGroup = new logs.LogGroup(stack, 'LogGroup', { + encryptionKey: kmsKey, + }); + + const execBucket = new s3.Bucket(stack, 'EcsExecBucket', { + encryptionKey: kmsKey, + }); + + // WHEN + const cluster = new ecs.Cluster(stack, 'EcsCluster', { + vpc, + executeCommandConfiguration: { + kmsKey, + logConfiguration: { + cloudWatchLogGroup: logGroup, + cloudWatchEncryptionEnabled: true, + s3Bucket: execBucket, + s3EncryptionEnabled: true, + s3KeyPrefix: 'exec-output', + }, + logging: ecs.ExecuteCommandLogging.OVERRIDE, + }, + }); + + cluster.addCapacity('DefaultAutoScalingGroup', { instanceType: new ec2.InstanceType('t2.micro') }); + const taskDefinition = new ecs.FargateTaskDefinition(stack, 'FargateTaskDef'); + + taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + memoryLimitMiB: 512, + }); + + new ecs.FargateService(stack, 'FargateService', { + cluster, + taskDefinition, + enableExecuteCommand: true, + }); + + // THEN + expect(stack).to(haveResource('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: [ + 'ssmmessages:CreateControlChannel', + 'ssmmessages:CreateDataChannel', + 'ssmmessages:OpenControlChannel', + 'ssmmessages:OpenDataChannel', + ], + Effect: 'Allow', + Resource: '*', + }, + { + Action: [ + 'kms:Decrypt', + 'kms:GenerateDataKey', + ], + Effect: 'Allow', + Resource: { + 'Fn::GetAtt': [ + 'KmsKey46693ADD', + 'Arn', + ], + }, + }, + { + Action: 'logs:DescribeLogGroups', + Effect: 'Allow', + Resource: '*', + }, + { + Action: [ + 'logs:CreateLogStream', + 'logs:DescribeLogStreams', + 'logs:PutLogEvents', + ], + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:aws:logs:', + { + Ref: 'AWS::Region', + }, + ':', + { + Ref: 'AWS::AccountId', + }, + ':log-group:', + { + Ref: 'LogGroupF5B46931', + }, + ':*', + ], + ], + }, + }, + { + Action: 's3:GetBucketLocation', + Effect: 'Allow', + Resource: '*', + }, + { + Action: 's3:PutObject', + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:aws:s3:::', + { + Ref: 'EcsExecBucket4F468651', + }, + '/*', + ], + ], + }, + }, + { + Action: 's3:GetEncryptionConfiguration', + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:aws:s3:::', + { + Ref: 'EcsExecBucket4F468651', + }, + ], + ], + }, + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'FargateTaskDefTaskRoleDefaultPolicy8EB25BBD', + Roles: [ + { + Ref: 'FargateTaskDefTaskRole0B257552', + }, + ], + })); + + expect(stack).to(haveResource('AWS::KMS::Key', { + KeyPolicy: { + Statement: [ + { + Action: [ + 'kms:Create*', + 'kms:Describe*', + 'kms:Enable*', + 'kms:List*', + 'kms:Put*', + 'kms:Update*', + 'kms:Revoke*', + 'kms:Disable*', + 'kms:Get*', + 'kms:Delete*', + 'kms:ScheduleKeyDeletion', + 'kms:CancelKeyDeletion', + 'kms:GenerateDataKey', + 'kms:TagResource', + 'kms:UntagResource', + ], + Effect: 'Allow', + Principal: { + AWS: { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':iam::', + { + Ref: 'AWS::AccountId', + }, + ':root', + ], + ], + }, + }, + Resource: '*', + }, + { + Action: 'kms:*', + Effect: 'Allow', + Principal: { + AWS: { + 'Fn::Join': [ + '', + [ + 'arn:aws:iam::', + { + Ref: 'AWS::AccountId', + }, + ':root', + ], + ], + }, + }, + Resource: '*', + }, + { + Action: [ + 'kms:Encrypt*', + 'kms:Decrypt*', + 'kms:ReEncrypt*', + 'kms:GenerateDataKey*', + 'kms:Describe*', + ], + Condition: { + ArnLike: { + 'kms:EncryptionContext:aws:logs:arn': { + 'Fn::Join': [ + '', + [ + 'arn:aws:logs:', + { + Ref: 'AWS::Region', + }, + ':', + { + Ref: 'AWS::AccountId', + }, + ':*', + ], + ], + }, + }, + }, + Effect: 'Allow', + Principal: { + Service: { + 'Fn::Join': [ + '', + [ + 'logs.', + { + Ref: 'AWS::Region', + }, + '.amazonaws.com', + ], + ], + }, + }, + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + })); + + test.done(); + }, }, }); diff --git a/packages/@aws-cdk/aws-ecs/test/fargate/integ.exec-command.expected.json b/packages/@aws-cdk/aws-ecs/test/fargate/integ.exec-command.expected.json new file mode 100644 index 0000000000000..083a16ee75820 --- /dev/null +++ b/packages/@aws-cdk/aws-ecs/test/fargate/integ.exec-command.expected.json @@ -0,0 +1,724 @@ +{ + "Resources": { + "Vpc8378EB38": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Vpc" + } + ] + } + }, + "VpcPublicSubnet1Subnet5C2D37C4": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.0.0/18", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1RouteTable6C95E38E": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1RouteTableAssociation97140677": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + } + } + }, + "VpcPublicSubnet1DefaultRoute3DA9E72A": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VpcIGWD7BA715C" + } + }, + "DependsOn": [ + "VpcVPCGWBF912B6E" + ] + }, + "VpcPublicSubnet1EIPD7E02669": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1NATGateway4D7517AA": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet1EIPD7E02669", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet2Subnet691E08A3": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.64.0/18", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1b", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2RouteTable94F7E489": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2RouteTableAssociationDD5762D8": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + }, + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + } + } + }, + "VpcPublicSubnet2DefaultRoute97F91067": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet2RouteTable94F7E489" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VpcIGWD7BA715C" + } + }, + "DependsOn": [ + "VpcVPCGWBF912B6E" + ] + }, + "VpcPublicSubnet2EIP3C605A87": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2NATGateway9182C01D": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet2EIP3C605A87", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPrivateSubnet1Subnet536B997A": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.128.0/18", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Vpc/PrivateSubnet1" + } + ] + } + }, + "VpcPrivateSubnet1RouteTableB2C5B500": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Vpc/PrivateSubnet1" + } + ] + } + }, + "VpcPrivateSubnet1RouteTableAssociation70C59FA6": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "SubnetId": { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + } + }, + "VpcPrivateSubnet1DefaultRouteBE02A9ED": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VpcPublicSubnet1NATGateway4D7517AA" + } + } + }, + "VpcPrivateSubnet2Subnet3788AAA1": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.192.0/18", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1b", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Vpc/PrivateSubnet2" + } + ] + } + }, + "VpcPrivateSubnet2RouteTableA678073B": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Vpc/PrivateSubnet2" + } + ] + } + }, + "VpcPrivateSubnet2RouteTableAssociationA89CAD56": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + }, + "SubnetId": { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + } + }, + "VpcPrivateSubnet2DefaultRoute060D2087": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet2RouteTableA678073B" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VpcPublicSubnet2NATGateway9182C01D" + } + } + }, + "VpcIGWD7BA715C": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-exec-command/Vpc" + } + ] + } + }, + "VpcVPCGWBF912B6E": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "InternetGatewayId": { + "Ref": "VpcIGWD7BA715C" + } + } + }, + "LogGroupF5B46931": { + "Type": "AWS::Logs::LogGroup", + "Properties": { + "RetentionInDays": "731", + "KmsKeyId": { + "Fn::GetAtt": [ + "KmsKey46693ADD", + "Arn" + ] + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "KmsKey46693ADD": { + "Type": "AWS::KMS::Key", + "Properties": { + "KeyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:aws:iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": [ + "kms:Encrypt*", + "kms:Decrypt*", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + "kms:Describe*" + ], + "Condition": { + "ArnLike": { + "kms:EncryptionContext:aws:logs:arn": { + "Fn::Join": [ + "", + [ + "arn:aws:logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":*" + ] + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": { + "Fn::Join": [ + "", + [ + "logs.", + { + "Ref": "AWS::Region" + }, + ".amazonaws.com" + ] + ] + } + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "EcsExecBucket4F468651": { + "Type": "AWS::S3::Bucket", + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "KMSMasterKeyID": { + "Fn::GetAtt": [ + "KmsKey46693ADD", + "Arn" + ] + }, + "SSEAlgorithm": "aws:kms" + } + } + ] + } + } + }, + "FargateCluster7CCD5F93": { + "Type": "AWS::ECS::Cluster", + "Properties": { + "Configuration": { + "ExecuteCommandConfiguration": { + "KmsKeyId": { + "Fn::GetAtt": [ + "KmsKey46693ADD", + "Arn" + ] + }, + "LogConfiguration": { + "CloudWatchEncryptionEnabled": true, + "CloudWatchLogGroupName": { + "Ref": "LogGroupF5B46931" + }, + "S3BucketName": { + "Ref": "EcsExecBucket4F468651" + }, + "S3EncryptionEnabled": true, + "S3KeyPrefix": "exec-output" + }, + "Logging": "OVERRIDE" + } + } + } + }, + "TaskDefTaskRole1EDB4A67": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "TaskDefTaskRoleDefaultPolicyA592CB18": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ssmmessages:CreateControlChannel", + "ssmmessages:CreateDataChannel", + "ssmmessages:OpenControlChannel", + "ssmmessages:OpenDataChannel" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "kms:Decrypt", + "kms:GenerateDataKey" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "KmsKey46693ADD", + "Arn" + ] + } + }, + { + "Action": "logs:DescribeLogGroups", + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "logs:CreateLogStream", + "logs:DescribeLogStreams", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:aws:logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:", + { + "Ref": "LogGroupF5B46931" + }, + ":*" + ] + ] + } + }, + { + "Action": "s3:GetBucketLocation", + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": "s3:PutObject", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:aws:s3:::", + { + "Ref": "EcsExecBucket4F468651" + }, + "/*" + ] + ] + } + }, + { + "Action": "s3:GetEncryptionConfiguration", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:aws:s3:::", + { + "Ref": "EcsExecBucket4F468651" + } + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "TaskDefTaskRoleDefaultPolicyA592CB18", + "Roles": [{ + "Ref": "TaskDefTaskRole1EDB4A67" + }] + } + }, + "TaskDef54694570": { + "Type": "AWS::ECS::TaskDefinition", + "Properties": { + "ContainerDefinitions": [ + { + "Essential": true, + "Image": "amazon/amazon-ecs-sample", + "Name": "web" + } + ], + "Cpu": "256", + "Family": "awsecsintegexeccommandTaskDef44709274", + "Memory": "512", + "NetworkMode": "awsvpc", + "RequiresCompatibilities": [ + "FARGATE" + ], + "TaskRoleArn": { + "Fn::GetAtt": [ + "TaskDefTaskRole1EDB4A67", + "Arn" + ] + } + } + }, + "FargateServiceAC2B3B85": { + "Type": "AWS::ECS::Service", + "Properties": { + "Cluster": { + "Ref": "FargateCluster7CCD5F93" + }, + "DeploymentConfiguration": { + "MaximumPercent": 200, + "MinimumHealthyPercent": 50 + }, + "LaunchType": "FARGATE", + "EnableECSManagedTags": false, + "EnableExecuteCommand": true, + "NetworkConfiguration": { + "AwsvpcConfiguration": { + "AssignPublicIp": "DISABLED", + "SecurityGroups": [ + { + "Fn::GetAtt": [ + "FargateServiceSecurityGroup0A0E79CB", + "GroupId" + ] + } + ], + "Subnets": [ + { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + }, + { + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" + } + ] + } + }, + "TaskDefinition": { + "Ref": "TaskDef54694570" + } + } + }, + "FargateServiceSecurityGroup0A0E79CB": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "aws-ecs-integ-exec-command/FargateService/SecurityGroup", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecs/test/fargate/integ.exec-command.ts b/packages/@aws-cdk/aws-ecs/test/fargate/integ.exec-command.ts new file mode 100644 index 0000000000000..5bc89fb2432b7 --- /dev/null +++ b/packages/@aws-cdk/aws-ecs/test/fargate/integ.exec-command.ts @@ -0,0 +1,50 @@ +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 s3 from '@aws-cdk/aws-s3'; +import * as cdk from '@aws-cdk/core'; +import * as ecs from '../../lib'; + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'aws-ecs-integ-exec-command'); + +const vpc = new ec2.Vpc(stack, 'Vpc', { maxAzs: 2 }); + +const kmsKey = new kms.Key(stack, 'KmsKey'); + +const logGroup = new logs.LogGroup(stack, 'LogGroup', { + encryptionKey: kmsKey, +}); + +const execBucket = new s3.Bucket(stack, 'EcsExecBucket', { + encryptionKey: kmsKey, +}); + +const cluster = new ecs.Cluster(stack, 'FargateCluster', { + vpc, + executeCommandConfiguration: { + kmsKey, + logConfiguration: { + cloudWatchLogGroup: logGroup, + cloudWatchEncryptionEnabled: true, + s3Bucket: execBucket, + s3EncryptionEnabled: true, + s3KeyPrefix: 'exec-output', + }, + logging: ecs.ExecuteCommandLogging.OVERRIDE, + }, +}); + +const taskDefinition = new ecs.FargateTaskDefinition(stack, 'TaskDef'); + +taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), +}); + +new ecs.FargateService(stack, 'FargateService', { + cluster, + taskDefinition, + enableExecuteCommand: true, +}); + +app.synth(); \ No newline at end of file From a466ba556c311e32695ee1f8da8f0b2791a7c1fe Mon Sep 17 00:00:00 2001 From: Jerry Kindall <52084730+Jerry-AWS@users.noreply.github.com> Date: Thu, 3 Jun 2021 15:29:05 -0700 Subject: [PATCH 127/134] docs(aws-glue): catalogID should be described as the AWS account ID (#14942) In response to a support request from a customer, updates the description for the catalogID property to mention it is usually the AWS account ID, using the same wording used to describe the property on the corresponding properties interface. --- packages/@aws-cdk/aws-glue/lib/database.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-glue/lib/database.ts b/packages/@aws-cdk/aws-glue/lib/database.ts index 8b359251f54d2..673b5748537c5 100644 --- a/packages/@aws-cdk/aws-glue/lib/database.ts +++ b/packages/@aws-cdk/aws-glue/lib/database.ts @@ -67,7 +67,7 @@ export class Database extends Resource implements IDatabase { public readonly catalogArn: string; /** - * ID of the Glue catalog in which this database is stored. + * The catalog id of the database (usually, the AWS account id). */ public readonly catalogId: string; From ced9b38e0e30613befd48a9e198086412d19c175 Mon Sep 17 00:00:00 2001 From: Eric Andresen Date: Fri, 4 Jun 2021 00:55:54 +0200 Subject: [PATCH 128/134] fix(docs): fixed typos in documentation (#14760) Fixed some documenations typos. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- design/construct-tree.md | 2 +- packages/@aws-cdk/aws-lambda/lib/function.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/design/construct-tree.md b/design/construct-tree.md index 9c9c44c327f74..9771ee6f9cd21 100644 --- a/design/construct-tree.md +++ b/design/construct-tree.md @@ -16,7 +16,7 @@ ALL CDK applications are composed of constructs, which are the basic building bl A construct can represent a single resource, such as an Amazon Simple Storage Service (Amazon S3) bucket, or it can represent a higher-level component consisting of multiple AWS CDK resources. Examples of such components include a worker queue with its associated compute capacity, a cron job with monitoring resources and a dashboard, or even an entire app spanning multiple AWS accounts and regions. -The CDK CLI is the primary mechanism through which developers currently interact with their AWS CDK applications. It supports various operations throughout the lifecycle of application development from from the initialization of a CDK app from a template to the deployment and destruction of the AWS CloudFormation stacks. +The CDK CLI is the primary mechanism through which developers currently interact with their AWS CDK applications. It supports various operations throughout the lifecycle of application development from the initialization of a CDK app from a template to the deployment and destruction of the AWS CloudFormation stacks. ## Motivation diff --git a/packages/@aws-cdk/aws-lambda/lib/function.ts b/packages/@aws-cdk/aws-lambda/lib/function.ts index 8bc6704c1321d..8e52a80bf0e32 100644 --- a/packages/@aws-cdk/aws-lambda/lib/function.ts +++ b/packages/@aws-cdk/aws-lambda/lib/function.ts @@ -336,7 +336,7 @@ export interface FunctionProps extends FunctionOptions { } /** - * Deploys a file from from inside the construct library as a function. + * Deploys a file from inside the construct library as a function. * * The supplied file is subject to the 4096 bytes limit of being embedded in a * CloudFormation template. From cd31457b1b75162cc1986e6e4410d72e663372c3 Mon Sep 17 00:00:00 2001 From: Andrew Tennikoff Date: Fri, 4 Jun 2021 10:07:24 +1000 Subject: [PATCH 129/134] docs(core): simple typo fix (#14982) Simple typo fix: assemly -> assembly ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/core/lib/stage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/core/lib/stage.ts b/packages/@aws-cdk/core/lib/stage.ts index efe65f115ab55..737f376848c69 100644 --- a/packages/@aws-cdk/core/lib/stage.ts +++ b/packages/@aws-cdk/core/lib/stage.ts @@ -202,7 +202,7 @@ export class Stage extends CoreConstruct { } /** - * Options for assemly synthesis. + * Options for assembly synthesis. */ export interface StageSynthesisOptions { /** From adf60bee27edb8fbedb8777b322db0443d8c90bb Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Fri, 4 Jun 2021 03:22:43 -0700 Subject: [PATCH 130/134] chore(pkglint): v2 CLI package.json cannot have "main" and "types" keys (#14962) To prevent errors like #14909. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- tools/pkglint/lib/rules.ts | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/tools/pkglint/lib/rules.ts b/tools/pkglint/lib/rules.ts index d7494a372c1c7..07d6ad69c552e 100644 --- a/tools/pkglint/lib/rules.ts +++ b/tools/pkglint/lib/rules.ts @@ -1707,6 +1707,31 @@ export class AwsCdkLibReadmeMatchesCore extends ValidationRule { } } +/** + * Enforces that the aws-cdk's package.json on the V2 branch does not have the "main" + * and "types" keys filled. + */ +export class CdkCliV2MissesMainAndTypes extends ValidationRule { + public readonly name = 'aws-cdk/cli/v2/package.json/main'; + + public validate(pkg: PackageJson): void { + // this rule only applies to the CLI + if (pkg.json.name !== 'aws-cdk') { return; } + // this only applies to V2 + if (cdkMajorVersion() === 1) { return; } + + if (pkg.json.main || pkg.json.types) { + pkg.report({ + ruleName: this.name, + message: 'The package.json file for the aws-cdk CLI package in V2 cannot have "main" and "types" keys', + fix: () => { + delete pkg.json.main; + delete pkg.json.types; + }, + }); + } + } +} /** * Determine whether this is a JSII package @@ -1794,4 +1819,4 @@ function isIncludedInMonolith(pkg: PackageJson): boolean { return false; } return true; -} \ No newline at end of file +} From a12db624ce394f5b9e786a5eea35be6716265673 Mon Sep 17 00:00:00 2001 From: Colin Ihrig Date: Fri, 4 Jun 2021 06:49:13 -0400 Subject: [PATCH 131/134] feat(cognito): user pool - customize mfa message (#14241) This commit adds a new, optional `mfaMessage` to Cognito UserPools. Fixes: https://github.com/aws/aws-cdk/issues/14084 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-cognito/lib/user-pool.ts | 26 +++++++++++ .../aws-cognito/test/user-pool.test.ts | 46 ++++++++++++++++++- 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-cognito/lib/user-pool.ts b/packages/@aws-cdk/aws-cognito/lib/user-pool.ts index 28e37670b05a2..9f598761ee843 100644 --- a/packages/@aws-cdk/aws-cognito/lib/user-pool.ts +++ b/packages/@aws-cdk/aws-cognito/lib/user-pool.ts @@ -527,6 +527,13 @@ export interface UserPoolProps { */ readonly mfa?: Mfa; + /** + * The SMS message template sent during MFA verification. + * Use '{####}' in the template where Cognito should insert the verification code. + * @default 'Your authentication code is {####}.' + */ + readonly mfaMessage?: string; + /** * Configure the MFA types that users can use in this user pool. Ignored if `mfa` is set to `OFF`. * @@ -761,6 +768,7 @@ export class UserPool extends UserPoolBase { aliasAttributes: signIn.aliasAttrs, autoVerifiedAttributes: signIn.autoVerifyAttrs, lambdaConfig: Lazy.any({ produce: () => undefinedIfNoKeys(this.triggers) }), + smsAuthenticationMessage: this.mfaMessage(props), smsConfiguration: this.smsConfiguration(props), adminCreateUserConfig, emailVerificationMessage, @@ -810,6 +818,24 @@ export class UserPool extends UserPoolBase { }); } + private mfaMessage(props: UserPoolProps): string | undefined { + const CODE_TEMPLATE = '{####}'; + const MAX_LENGTH = 140; + const message = props.mfaMessage; + + if (message && !Token.isUnresolved(message)) { + if (!message.includes(CODE_TEMPLATE)) { + throw new Error(`MFA message must contain the template string '${CODE_TEMPLATE}'`); + } + + if (message.length > MAX_LENGTH) { + throw new Error(`MFA message must be between ${CODE_TEMPLATE.length} and ${MAX_LENGTH} characters`); + } + } + + return message; + } + private verificationMessageConfiguration(props: UserPoolProps): CfnUserPool.VerificationMessageTemplateProperty { const CODE_TEMPLATE = '{####}'; const VERIFY_EMAIL_TEMPLATE = '{##Verify Email##}'; diff --git a/packages/@aws-cdk/aws-cognito/test/user-pool.test.ts b/packages/@aws-cdk/aws-cognito/test/user-pool.test.ts index 7f1c02c786a4e..ccd5959750a58 100644 --- a/packages/@aws-cdk/aws-cognito/test/user-pool.test.ts +++ b/packages/@aws-cdk/aws-cognito/test/user-pool.test.ts @@ -29,6 +29,7 @@ describe('User Pool', () => { EmailSubject: 'Verify your new account', SmsMessage: 'The verification code to your new account is {####}', }, + SmsAuthenticationMessage: ABSENT, SmsConfiguration: ABSENT, lambdaTriggers: ABSENT, }); @@ -80,6 +81,49 @@ describe('User Pool', () => { }); }), + test('mfa authentication message is configured correctly', () => { + // GIVEN + const stack = new Stack(); + const message = 'The authentication code to your account is {####}'; + + // WHEN + new UserPool(stack, 'Pool', { + mfaMessage: message, + }); + + // THEN + expect(stack).toHaveResourceLike('AWS::Cognito::UserPool', { + SmsAuthenticationMessage: message, + }); + }), + + test('mfa authentication message is validated', () => { + const stack = new Stack(); + + expect(() => new UserPool(stack, 'Pool1', { + mfaMessage: '{####', + })).toThrow(/MFA message must contain the template string/); + + expect(() => new UserPool(stack, 'Pool2', { + mfaMessage: '{####}', + })).not.toThrow(); + + expect(() => new UserPool(stack, 'Pool3', { + mfaMessage: `{####}${'x'.repeat(135)}`, + })).toThrow(/MFA message must be between 6 and 140 characters/); + + expect(() => new UserPool(stack, 'Pool4', { + mfaMessage: `{####}${'x'.repeat(134)}`, + })).not.toThrow(); + + // Validation is skipped for tokens. + const parameter = new CfnParameter(stack, 'Parameter'); + + expect(() => new UserPool(stack, 'Pool5', { + mfaMessage: parameter.valueAsString, + })).not.toThrow(); + }); + test('email and sms verification messages are validated', () => { const stack = new Stack(); @@ -1355,4 +1399,4 @@ function fooFunction(scope: Construct, name: string): lambda.IFunction { runtime: lambda.Runtime.NODEJS_12_X, handler: 'index.handler', }); -} \ No newline at end of file +} From 14fbb11af407a5834dedb6aeb095285dd44695ba Mon Sep 17 00:00:00 2001 From: John Mortlock Date: Fri, 4 Jun 2021 23:52:21 +0930 Subject: [PATCH 132/134] fix(cli): cross account docker image assets upload no longer works (#14816) Fixes #14815 Cross account docker images fail to upload when using modern stack and assumed roles. Similar to the fix in https://github.com/aws/aws-cdk/commit/afd70453de70e8e54bfd941404efda74d594e0e6 only perform the account lookup when required ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../lib/private/handlers/container-images.ts | 4 ++-- packages/cdk-assets/test/docker-images.test.ts | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/cdk-assets/lib/private/handlers/container-images.ts b/packages/cdk-assets/lib/private/handlers/container-images.ts index a3b6756ecb18d..1d2b5bedee38e 100644 --- a/packages/cdk-assets/lib/private/handlers/container-images.ts +++ b/packages/cdk-assets/lib/private/handlers/container-images.ts @@ -19,11 +19,11 @@ export class ContainerImageAssetHandler implements IAssetHandler { public async publish(): Promise { const destination = await replaceAwsPlaceholders(this.asset.destination, this.host.aws); const ecr = await this.host.aws.ecrClient(destination); - const account = (await this.host.aws.discoverCurrentAccount()).accountId; + const account = async () => (await this.host.aws.discoverCurrentAccount())?.accountId; const repoUri = await repositoryUri(ecr, destination.repositoryName); if (!repoUri) { - throw new Error(`No ECR repository named '${destination.repositoryName}' in account ${account}. Is this account bootstrapped?`); + throw new Error(`No ECR repository named '${destination.repositoryName}' in account ${await account()}. Is this account bootstrapped?`); } const imageUri = `${repoUri}:${destination.imageTag}`; diff --git a/packages/cdk-assets/test/docker-images.test.ts b/packages/cdk-assets/test/docker-images.test.ts index 3b608a1e63ffe..8dc4f70b7726a 100644 --- a/packages/cdk-assets/test/docker-images.test.ts +++ b/packages/cdk-assets/test/docker-images.test.ts @@ -104,6 +104,18 @@ describe('with a complete manifest', () => { })); }); + test('Displays an error if the ECR repository cannot be found', async () => { + aws.mockEcr.describeImages = mockedApiFailure('RepositoryNotFoundException', 'Repository not Found'); + + await expect(pub.publish()).rejects.toThrow('Error publishing: Repository not Found'); + }); + + test('successful run does not need to query account ID', async () => { + aws.mockEcr.describeImages = mockedApiResult({ /* No error == image exists */ }); + await pub.publish(); + expect(aws.discoverCurrentAccount).not.toHaveBeenCalled(); + }); + test('upload docker image if not uploaded yet but exists locally', async () => { aws.mockEcr.describeImages = mockedApiFailure('ImageNotFoundException', 'File does not exist'); aws.mockEcr.getAuthorizationToken = mockedApiResult({ From f932e0fbcf95f755d11bd322e6ac9c350b38c149 Mon Sep 17 00:00:00 2001 From: Otavio Macedo Date: Fri, 4 Jun 2021 15:49:03 +0100 Subject: [PATCH 133/134] feat(s3): support ExpiredObjectDeleteMarker (#14970) Fixes #14752 ---- *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/lib/bucket.ts | 1 + packages/@aws-cdk/aws-s3/lib/rule.ts | 8 +++++++ packages/@aws-cdk/aws-s3/test/rules.test.ts | 24 +++++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/packages/@aws-cdk/aws-s3/lib/bucket.ts b/packages/@aws-cdk/aws-s3/lib/bucket.ts index 002435a234c0c..1bbd7f6cb8fbb 100644 --- a/packages/@aws-cdk/aws-s3/lib/bucket.ts +++ b/packages/@aws-cdk/aws-s3/lib/bucket.ts @@ -1656,6 +1656,7 @@ export class Bucket extends BucketBase { transitionDate: t.transitionDate, transitionInDays: t.transitionAfter && t.transitionAfter.toDays(), })), + expiredObjectDeleteMarker: rule.expiredObjectDeleteMarker, tagFilters: self.parseTagFilters(rule.tagFilters), }; diff --git a/packages/@aws-cdk/aws-s3/lib/rule.ts b/packages/@aws-cdk/aws-s3/lib/rule.ts index 294f3852a1d0a..92ad24e2cf6a8 100644 --- a/packages/@aws-cdk/aws-s3/lib/rule.ts +++ b/packages/@aws-cdk/aws-s3/lib/rule.ts @@ -100,6 +100,14 @@ export interface LifecycleRule { * @default Rule applies to all objects */ readonly tagFilters?: {[tag: string]: any}; + + /** + * Indicates whether Amazon S3 will remove a delete marker with no noncurrent versions. + * If set to true, the delete marker will be expired. + * + * @default false + */ + readonly expiredObjectDeleteMarker?: boolean; } /** diff --git a/packages/@aws-cdk/aws-s3/test/rules.test.ts b/packages/@aws-cdk/aws-s3/test/rules.test.ts index 6b64f919895e1..fb613bcf109e8 100644 --- a/packages/@aws-cdk/aws-s3/test/rules.test.ts +++ b/packages/@aws-cdk/aws-s3/test/rules.test.ts @@ -128,4 +128,28 @@ nodeunitShim({ test.done(); }, + + 'Bucket with expiredObjectDeleteMarker'(test: Test) { + // GIVEN + const stack = new Stack(); + + // WHEN + new Bucket(stack, 'Bucket', { + lifecycleRules: [{ + expiredObjectDeleteMarker: true, + }], + }); + + // THEN + expect(stack).to(haveResource('AWS::S3::Bucket', { + LifecycleConfiguration: { + Rules: [{ + ExpiredObjectDeleteMarker: true, + Status: 'Enabled', + }], + }, + })); + + test.done(); + }, }); From beaffa9aec25875649ad4ef02d0885d8de0f5eac Mon Sep 17 00:00:00 2001 From: Jonathan Goldwasser Date: Fri, 4 Jun 2021 17:17:59 +0200 Subject: [PATCH 134/134] fix(cli): image publishing role doesn't have docker pull permissions (#14662) Using a common docker asset as base image for other docker assets requires the image publishing role to have the `ecr:BatchGetImage`, `ecr:GetDownloadUrlForLayer` and `ecr:InitiateLayerUpload` permissions. Closes #14656 ---- *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/bootstrap/bootstrap-template.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml b/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml index 6fb29fbbb6d5b..eec900592e641 100644 --- a/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml +++ b/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml @@ -342,6 +342,8 @@ Resources: - ecr:BatchCheckLayerAvailability - ecr:DescribeRepositories - ecr:DescribeImages + - ecr:BatchGetImage + - ecr:GetDownloadUrlForLayer Resource: Fn::Sub: "${ContainerAssetsRepository.Arn}" Effect: Allow

I1URwe$;}*BTf_sKg@d^Dw|K| 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⪼KI_e4yEd~%x(V}po5|aQTO|}$MG-BaM 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 htiRg7c<}Mmw{nc0Q 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