diff --git a/.github/workflows/issue-label-assign.yml b/.github/workflows/issue-label-assign.yml index 9e2f2ce7e3e34..22f20538795c7 100644 --- a/.github/workflows/issue-label-assign.yml +++ b/.github/workflows/issue-label-assign.yml @@ -11,7 +11,7 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: Naturalclar/issue-action@f229cda + - uses: Naturalclar/issue-action@v2.0.2 with: github-token: "${{ secrets.GITHUB_TOKEN }}" title-or-body: 'title' @@ -90,7 +90,7 @@ jobs: {"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-events)","(aws-events)","(events)","(eventbridge)","event-bridge)"],"labels":["@aws-cdk/aws-events"],"assignees":["rix0rrr"]}, + {"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-fms)","(aws-fms)","(fms)"],"labels":["@aws-cdk/aws-fms"],"assignees":["rix0rrr"]}, diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bf7a31e44214..7a0a72835f9ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,43 @@ 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.90.0](https://github.com/aws/aws-cdk/compare/v1.89.0...v1.90.0) (2021-02-17) + + +### Features + +* **apigatewayv2:** http api - jwt and cognito user pool authorizers ([#10972](https://github.com/aws/aws-cdk/issues/10972)) ([dd90e54](https://github.com/aws/aws-cdk/commit/dd90e5464b24e097a3e41a81556924018a422181)), closes [#10534](https://github.com/aws/aws-cdk/issues/10534) +* **aws-ecs-patterns:** allow ScheduledTaskBase be created in a DISABLED state ([#12837](https://github.com/aws/aws-cdk/issues/12837)) ([c625699](https://github.com/aws/aws-cdk/commit/c6256992902fc4237ceb9f965e970e2c2ef00777)), closes [#12836](https://github.com/aws/aws-cdk/issues/12836) +* **aws-kinesisanalyticsv2:** L2 construct for Flink applications ([#12464](https://github.com/aws/aws-cdk/issues/12464)) ([94279f3](https://github.com/aws/aws-cdk/commit/94279f35e4f5ef961e0ba8528e34a8fccb9ef3fe)), closes [#12407](https://github.com/aws/aws-cdk/issues/12407) +* **cfnspec:** cloudformation spec v27.0.0 ([#12960](https://github.com/aws/aws-cdk/issues/12960)) ([7730ac8](https://github.com/aws/aws-cdk/commit/7730ac8c6c7aedb233a24c665666b9651b2401a5)) +* **cli:** change set name is now a constant, and --no-execute will always produce one (even if empty) ([#12683](https://github.com/aws/aws-cdk/issues/12683)) ([00cdd2a](https://github.com/aws/aws-cdk/commit/00cdd2a2188d146af8b8df998e97da91c77dc270)), closes [#11075](https://github.com/aws/aws-cdk/issues/11075) +* **core:** customize bundling output packaging ([#13076](https://github.com/aws/aws-cdk/issues/13076)) ([367a055](https://github.com/aws/aws-cdk/commit/367a055688c97ca3b01aff19d6d91ed5b1b86e1e)), closes [#10776](https://github.com/aws/aws-cdk/issues/10776) +* **ecs:** support Fargate and Fargate spot capacity providers ([#12893](https://github.com/aws/aws-cdk/issues/12893)) ([843b480](https://github.com/aws/aws-cdk/commit/843b480e7a1bc51594d3580d2774d3b9a4eec2fb)), closes [#5850](https://github.com/aws/aws-cdk/issues/5850) +* **ecs-patterns:** Add support for taskSubnets and securityGroups on QueueProcessingFagateService ([#12604](https://github.com/aws/aws-cdk/issues/12604)) ([996e69d](https://github.com/aws/aws-cdk/commit/996e69dd6d33a3478f88a6e32afeebc4fd0e7ec5)), closes [#12603](https://github.com/aws/aws-cdk/issues/12603) +* **eks:** support Kubernetes 1.19 ([#13094](https://github.com/aws/aws-cdk/issues/13094)) ([72c22dc](https://github.com/aws/aws-cdk/commit/72c22dc39c1fa69905cfd0d3259b429e1c5b8447)), closes [#13093](https://github.com/aws/aws-cdk/issues/13093) +* **elasticsearch:** add custom endpoint options ([#12904](https://github.com/aws/aws-cdk/issues/12904)) ([f67ab86](https://github.com/aws/aws-cdk/commit/f67ab8689dc38803253067c4f9632b9bc5ea653f)), closes [#12261](https://github.com/aws/aws-cdk/issues/12261) +* **glue:** Connection construct ([#12444](https://github.com/aws/aws-cdk/issues/12444)) ([c64ec6b](https://github.com/aws/aws-cdk/commit/c64ec6bea6c4cee90530f292ea29f774c68c7667)), closes [#12442](https://github.com/aws/aws-cdk/issues/12442) +* **glue:** SecurityConfiguration construct ([#12450](https://github.com/aws/aws-cdk/issues/12450)) ([0a8e681](https://github.com/aws/aws-cdk/commit/0a8e68185d75327d37a00b967520ba98026d6fad)), closes [#12449](https://github.com/aws/aws-cdk/issues/12449) +* **redshift:** add missing current generation RA3 NodeTypes ([#12784](https://github.com/aws/aws-cdk/issues/12784)) ([f91a3f1](https://github.com/aws/aws-cdk/commit/f91a3f1302c395e8c7ffe9d6164e8f3b252f9a27)), closes [#12783](https://github.com/aws/aws-cdk/issues/12783) +* **stepfunctions:** Implement IGrantable ([#12830](https://github.com/aws/aws-cdk/issues/12830)) ([3b5ff05](https://github.com/aws/aws-cdk/commit/3b5ff0562090059f3a94140161acce53e484776c)), closes [#12829](https://github.com/aws/aws-cdk/issues/12829) + + +### Bug Fixes + +* **apigatewayv2:** HttpApi and Route in different stacks creates cycles ([#13010](https://github.com/aws/aws-cdk/issues/13010)) ([b5efb88](https://github.com/aws/aws-cdk/commit/b5efb88aebebb14673ea2a3736c710b09626f8e1)), closes [#13021](https://github.com/aws/aws-cdk/issues/13021) +* **aws-rds:** correct Policy resource for Proxy::grantConnect() ([#12416](https://github.com/aws/aws-cdk/issues/12416)) ([b3197db](https://github.com/aws/aws-cdk/commit/b3197db1c87067231b0642b7f9e1e37a48b12b6d)), closes [#12415](https://github.com/aws/aws-cdk/issues/12415) +* **cfn-diff:** correctly handle version strings like '0.0.0' ([#13022](https://github.com/aws/aws-cdk/issues/13022)) ([34a921b](https://github.com/aws/aws-cdk/commit/34a921b9667402b6d90731f1fd9e3de1ef27f8bf)), closes [#13016](https://github.com/aws/aws-cdk/issues/13016) +* **cfn2ts:** correctly choose between string and object without required properties in a union ([#12954](https://github.com/aws/aws-cdk/issues/12954)) ([b7137c5](https://github.com/aws/aws-cdk/commit/b7137c59d04f14a6ad890bff1faf0f36cae131b0)), closes [#12854](https://github.com/aws/aws-cdk/issues/12854) +* **cloudfront:** bucket policy for Origin Access Identities is overly permissive ([#13087](https://github.com/aws/aws-cdk/issues/13087)) ([cc28312](https://github.com/aws/aws-cdk/commit/cc2831238d965950dad74607ac0199b75b4bc459)), closes [#3486](https://github.com/aws/aws-cdk/issues/3486) [#13086](https://github.com/aws/aws-cdk/issues/13086) +* **cloudfront:** EdgeFunction us-east-1 stack created in different account ([#13055](https://github.com/aws/aws-cdk/issues/13055)) ([2f1fc95](https://github.com/aws/aws-cdk/commit/2f1fc959b1cbe406351cc0d3e057841497af1c19)), closes [#12789](https://github.com/aws/aws-cdk/issues/12789) +* **codecommit:** take the region and account of an imported Repository from its ARN ([#13066](https://github.com/aws/aws-cdk/issues/13066)) ([5f0ee88](https://github.com/aws/aws-cdk/commit/5f0ee88ff1e618a3f3c50dec7308c4da279e42ac)), closes [#13025](https://github.com/aws/aws-cdk/issues/13025) +* **codedeploy:** allow the install agent script's commands to exit with errors ([#12782](https://github.com/aws/aws-cdk/issues/12782)) ([23d52a5](https://github.com/aws/aws-cdk/commit/23d52a570b591f080eebfbd9dc679a9ef2daeebf)), closes [#12764](https://github.com/aws/aws-cdk/issues/12764) +* **codepipeline-actions:** use BatchGetBuildBatches permission for batch builds ([#13018](https://github.com/aws/aws-cdk/issues/13018)) ([09ba573](https://github.com/aws/aws-cdk/commit/09ba573a816cc4fa9898c1700136bb332801721c)) +* **core:** `exportValue()` does not work with resource names ([#13052](https://github.com/aws/aws-cdk/issues/13052)) ([46043e0](https://github.com/aws/aws-cdk/commit/46043e04a1603796c766dd1e280384f46c27e2de)), closes [#13002](https://github.com/aws/aws-cdk/issues/13002) [#12918](https://github.com/aws/aws-cdk/issues/12918) +* **ec2:** volume props validations are incorrect ([#12821](https://github.com/aws/aws-cdk/issues/12821)) ([12cddff](https://github.com/aws/aws-cdk/commit/12cddffcfa38cc0522e4c36327f193e6a605f441)), closes [#12816](https://github.com/aws/aws-cdk/issues/12816) [#12816](https://github.com/aws/aws-cdk/issues/12816) [#12074](https://github.com/aws/aws-cdk/issues/12074) +* **rds:** proxy cannot connect to cluster/instance ([#12953](https://github.com/aws/aws-cdk/issues/12953)) ([4b0abbc](https://github.com/aws/aws-cdk/commit/4b0abbcdc6efe2d37e2a9eee382848d2de82de5c)) +* **tools:** doc block links not clickable in VS Code ([#12336](https://github.com/aws/aws-cdk/issues/12336)) ([4f17f92](https://github.com/aws/aws-cdk/commit/4f17f923edc5e55b0977dcb250c9908027297d1b)) + ## [1.89.0](https://github.com/aws/aws-cdk/compare/v1.88.0...v1.89.0) (2021-02-09) @@ -121,6 +158,12 @@ used by CDK Pipelines) must upgrade their bootstrap stacks. Run `cdk bootstrap`. ## [1.85.0](https://github.com/aws/aws-cdk/compare/v1.84.0...v1.85.0) (2021-01-14) * **s3-deployment**: This version includes an important update, please upgrade to prevent deployment failure. This is in prepartion of Lambda deprecation of the request module in boto, more details are available in [AWS blog](https://aws.amazon.com/blogs/compute/upcoming-changes-to-the-python-sdk-in-aws-lambda/). Note, users of versions < `1.81.0` will not be impacted by this deprecation, but are still encouraged to upgrade to the latest version. +* **s3**: The `grantWrite()` and `grantReadWrite()` methods no longer add the `s3:PutObject*` permissions that included `s3:PutObjectAcl`, + which could be used to grant read/write object access to IAM principals in other accounts. + This change is gated behind the `@aws-cdk/aws-s3:grantWriteWithoutAcl` feature flag, + so make sure to set it to `true` in the `context` key of your `cdk.json` file when upgrading. + If you still need the principal to have `s3:PutObjectAcl` permissions after upgrading, + use the new `grantPutAcl()` method. ### Features diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1d113183c9a67..8f26e8198abc2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -849,6 +849,22 @@ CDK](https://github.com/aws/aws-cdk/issues/3398) we will either remove the legacy behavior or flip the logic for all these features and then reset the `FEATURE_FLAGS` map for the next cycle. +#### CDKv2 + +We have started working on the next version of the CDK, specifically CDKv2. This is currently being maintained +on a separate branch `v2-main` whereas `master` continues to track versions `1.x`. + +Feature flags introduced in the CDK 1.x and removed in 2.x, must be added to the `FUTURE_FLAGS_EXPIRED` list in +[cx-api/lib/features.ts](https://github.com/aws/aws-cdk/blob/master/packages/%40aws-cdk/cx-api/lib/features.ts) +on the `v2-main` branch. +This will make the default behaviour in CDKv2 as if the flag is enabled and also prevents users from disabling +the feature flag. + +A couple of [jest helper methods] are available for use with unit tests. These help run unit tests that test +behaviour when flags are enabled or disabled in the two major versions. + +[jest helper methods]: https://github.com/aws/aws-cdk/blob/master/tools/cdk-build-tools/lib/feature-flag.ts + ### Versioning and Release The `release.json` file at the root of the repo determines which release line diff --git a/DESIGN_GUIDELINES.md b/DESIGN_GUIDELINES.md index 6a3da024723c4..e6cebe9f434c6 100644 --- a/DESIGN_GUIDELINES.md +++ b/DESIGN_GUIDELINES.md @@ -199,7 +199,7 @@ the fact that the Bucket class needs the ARN or that it needs to request encryption permissions are not the user's concern, and the API of the Bucket class should not “leak” these implementation details. In the future, the Bucket class can decide to interact differently with the **key** and this won't require -expanding it's surface area. It also allows the **Key** class to change it's +expanding its surface area. It also allows the **Key** class to change its behavior (i.e. add an IAM action to enable encryption of certain types of keys) without affecting the API of the consumer. @@ -207,7 +207,7 @@ without affecting the API of the consumer. Using object references instead of attribute references provides a richer API, but also introduces an inherent challenge: how do we reference constructs that -are not defined inside the same app (“**owned**” by the app). These could be +are not defined inside the same app (“**owned**” by the app)? These could be resources that were created by some other AWS CDK app, via the AWS console, etc. We call these **“unowned” constructs.** @@ -272,7 +272,7 @@ as “props” (to distinguish them from JavaScript object properties). Props are the most important aspect of designing a construct. Props are the entry point of the construct. They should reflect the entire surface area of the service through semantics that are intuitive to how developers perceive the -service and it's capabilities. +service and its capabilities. When designing the props of an AWS resource, consult the AWS Console experience for creating this resource. Service teams spend a lot of energy thinking about @@ -300,7 +300,7 @@ API. In almost all cases, a richer object-oriented API can be exposed to encapsulate the low-level surface [_awslint:props-no-cfn-types_]. Do not use the **Token** type. It provides zero type safety, and is a functional -interface that may not translate cleanly in other JSII runtimes: ergo it should +interface that may not translate cleanly in other JSII runtimes. Therefore, it should be avoided wherever possible [_awslint:props-no-tokens_]. **deCDK** allows users to synthesize CDK stacks through a CloudFormation-like @@ -308,7 +308,7 @@ be avoided wherever possible [_awslint:props-no-tokens_]. like CloudFormation resources. Technically, this means that when a construct is defined, users supply an ID, type and a set of properties. In order to allow users to instantiate all AWS Construct Library constructs through the - deCDK syntax, we pose restrictions on prop types _[awslint:props-decdk]_: + deCDK syntax, we impose restrictions on prop types _[awslint:props-decdk]_: * Primitives (string, number, boolean, date) * Collections (list, map) @@ -390,7 +390,7 @@ item). It just means that you can remove redundant context from the property names. For example, there is no need to repeat the resource type, the property type or indicate that this is a "configuration". -For example prefer “readCapacity” versus “readCapacityUnits”. +For example, prefer “readCapacity” versus “readCapacityUnits”. #### Naming @@ -546,7 +546,7 @@ be treated as an opaque token, the JSDoc “@returns” annotation should begin When an app defines a construct or resource, it specifies its provisioning configuration upon initialization. For example, when an SQS queue is defined, -it's visibility timeout can be configured. +its visibility timeout can be configured. Naturally, when constructs are imported (unowned), the importing app does not have control over its configuration (e.g. you cannot change the visibility @@ -609,17 +609,17 @@ consistency and interoperability, we allow mutating methods to be exposed on the interface. For example, **grant** methods are exposed on the construct interface and not on the concrete class. In most cases, when you grant a permission on an AWS resource, the *principal's* policy needs to be updated, which mutates the -consumer . However, there are certain cases where a *resource policy* must be +consumer. However, there are certain cases where a *resource policy* must be updated. However, if the resource is unowned, it doesn't make sense (or even impossible) to update its policy (there is usually a 1:1 relationship between a -resource and a resource policy). In such a case, we decided that grant methods -will simply skip any changes to resource policies, but will issue attach a +resource and a resource policy). In such cases, we decided that grant methods +will simply skip any changes to resource policies, but will attach a **permission notice** to the app, which will be printed when the stack is synthesized by the toolkit. ### Factories -In most AWS services, there's a one or more resource which can be referred to as +In most AWS services, there are one or more resources which can be referred to as “primary resources” (normally one), while other resources exposed by the service can be considered “secondary resources”. @@ -687,7 +687,7 @@ their app. The signature of all “from” methods should adhere to the following rules _[awslint:from-signature]_: -* First argument must be **scope** of type **Construct** +* First argument must be **scope** of type **Construct**. * Second argument is a **string**. This string will be used to determine the ID of the new construct. If the import method uses some value that is promised to be unique within the stack scope (such as ARN, export name), @@ -697,8 +697,8 @@ _[awslint:from-signature]_: #### “from” Methods Resource constructs should export static “from” methods for importing unowned -resources given one more of it's physical attributes such as ARN, name, etc. All -constructs should have at least one fromXxx method _[awslint:from-method]_: +resources given one more of its physical attributes such as ARN, name, etc. All +constructs should have at least one "fromXxx" method _[awslint:from-method]_: ```ts static fromFooArn(scope: Construct, id: string, bucketArn: string): IFoo; @@ -713,7 +713,7 @@ static fromFooName(scope: Construct, id: string, bucketName: string): IFoo; doesn't have unresolved tokens (using **Token.unresolved**). Preferably, they can use **Stack.parseArn** to achieve this purpose. -If a resource has an ARN attribute it should implement at least a **fromFooArn** +If a resource has an ARN attribute, it should implement at least a **fromFooArn** import method [_awslint:from-arn_]. To implement **fromAttribute** methods, use the abstract base class construct as @@ -769,7 +769,7 @@ interface FooProps { } ``` -The construct interface should expose a **role**property, and extends +The construct interface should expose a **role** property, and extends **iam.IGrantable** _[awslint:role-property]_: ```ts @@ -793,7 +793,7 @@ interface IFoo { } ``` -If the construct is unowned this method should no-op and issue a **permissions +If the construct is unowned, this method should no-op and issue a **permissions notice** (TODO) to the user indicating that they should ensure that the role of this resource should have the specified permission. @@ -947,7 +947,7 @@ suffix and adhere to the following rules _[awslint:metrics-method-signature]:_ * Name should be “metricXxx” where “Xxx” is the official metric name * Accepts a single “options” argument of type **MetricOptions** -* Returns a **Metric** object. +* Returns a **Metric** object ```ts interface IFunction { @@ -1001,7 +1001,7 @@ extend **ec2.IConnectable** _[awslint:connectable-interface]_. ### Integrations -Many AWS services offer “integrations” to other services. For example, AWS +Many AWS services offer “integrations” with other services. For example, AWS CodePipeline has actions that can trigger AWS Lambda functions, ECS tasks, CodeBuild projects and more. AWS Lambda can be triggered by a variety of event sources, AWS CloudWatch event rules can trigger many types of targets, SNS can @@ -1017,7 +1017,7 @@ the central service and can be triggered by multiple event sources. Integrations are an abstract concept, not necessarily a specific mechanism. For example, each AWS Lambda event source is implemented in a different way (SNS, -Bucket notifications, CloudWatch events, etc), but conceptually, *some*users +Bucket notifications, CloudWatch events, etc), but conceptually, *some* users like to think about AWS Lambda as the “center”. It is also completely legitimate to have multiple ways to connect two services on AWS. To trigger an AWS Lambda function from an SNS topic, you could either use the integration or the SNS APIs @@ -1102,7 +1102,7 @@ export class Table { } ``` Persistent resources must have a **removalPolicy** prop, defaults to -**Orphan**_[awslint:state-removal-policy-prop]_: +**Orphan** _[awslint:state-removal-policy-prop]_: ```ts import { RemovalPolicy } from '@aws-cdk/cdk'; @@ -1179,14 +1179,14 @@ implementation of AWS constructs. not one that you made up and you force them to learn. * Multiple ways of achieving the same thing is legitimate. * Constantly maintain the invariants. -* Fewer “if statements” the better. +* The fewer “if statements” the better. ### Construct IDs Construct IDs (the second argument passed to all constructs when they are defined) are used to formulate resource logical IDs which must be **stable** across updates. The logical ID of a resource is calculated based on the **full -path** of it's construct in the construct scope hierarchy. This means that any +path** of its construct in the construct scope hierarchy. This means that any change to a logical ID in this path will invalidate all the logical IDs within this scope. This will result in **replacements of all underlying resources** within the next update, which is extremely undesirable. @@ -1196,7 +1196,7 @@ construct. Therefore, when implementing constructs, you should treat the construct hierarchy and all construct IDs as part of the **external contract** of the -construct. Any chance to either should be considered and called out as a +construct. Any change to either should be considered and called out as a breaking change. There is no need to concatenate logical IDs. If you find yourself needing to @@ -1226,10 +1226,10 @@ Error since all errors in the CDK are unrecoverable): * Include a descriptive message * Include the value provided * Include the expected/allowed values -* No need to include information that can be obtained from the stack trace. -* No need to add a period at the end of error messages. +* No need to include information that can be obtained from the stack trace +* No need to add a period at the end of error messages -#### Avoid Errors if Possible +#### Avoid Errors If Possible Always prefer to do the right thing for the user instead of raising an error. Only fail if the user has explicitly specified bad configuration. For diff --git a/design/aws-ecs/aws-ecs-fargate-capacity-providers.md b/design/aws-ecs/aws-ecs-fargate-capacity-providers.md new file mode 100644 index 0000000000000..66451ece2843a --- /dev/null +++ b/design/aws-ecs/aws-ecs-fargate-capacity-providers.md @@ -0,0 +1,121 @@ +# Fargate Spot Capacity Provider support in the CDK + +## Objective + +Since Capacity Providers are now supported in CloudFormation, incorporating support for Fargate Spot capacity has been one of the [top asks](https://github.com/aws/aws-cdk/issues?q=is%3Aissue+is%3Aopen+label%3A%40aws-cdk%2Faws-ecs+sort%3Areactions-%2B1-desc) for the ECS CDK module, with over 60 customer reactions. While there are still some outstanding issues regarding capacity provider support in general, specifically regarding cyclic workflows with named clusters (See: [CFN issue](http://%20https//github.com/aws/containers-roadmap/issues/631#issuecomment-702580141)), we should be able to move ahead with supporting `FARGATE` and `FARGATE_SPOT` capacity providers with our existing FargateService construct. + +See: https://github.com/aws/aws-cdk/issues/5850 + +## CloudFormation Requirements + +### Cluster + +A list of capacity providers (specifically, `FARGATE` and `FARGATE_SPOT`) need to be specified on the cluster itself as part of the [CapacityProviders](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-cluster.html#cfn-ecs-cluster-capacityproviders) field. + +Additionally, there is a [DefaultCapacityProviderStrategy](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-cluster.html#cfn-ecs-cluster-defaultcapacityproviderstrategy) on the cluster. While it is considered best practice to specify one if using capacity providers, this may not be necessary when only using Fargate capacity providers. + +### Service + +The [CapacityProviderStrategy](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-service.html#cfn-ecs-service-capacityproviderstrategy) field will need to be added to the Service construct. This would be a list of capacity provider strategies (aka [CapacityProviderStrategyItem](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-service-capacityproviderstrategyitem.html) in CFN) used for the service. + +_Note_: It may be more readable to name the `CapacityProviderStrategy` field on the service to *CapacityProviderStrategies*, which would be a list of *CapacityProviderStrategy* objects that correspond to the CFN `CapacityProviderStrategyItem`. + + +## Proposed solution + +### User Experience + +The most straightforward solution would be to add the *capacityProviders* field on cluster, which the customer would have to set to the Fargate capacity providers (`FARGATE` and `FARGATE_SPOT`), and then specify the *capacityProviderStrategies* field on the FargateService with one or more strategies that use the Fargate capacity providers. + +Example: + +```ts +const stack = new cdk.Stack(); +const vpc = new ec2.Vpc(stack, 'MyVpc', {}); +const cluster = new ecs.Cluster(stack, 'EcsCluster', { + vpc, + *capacityProviders: ['FARGATE', 'FARGATE_SPOT'],* +}); + +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, + } + ],* +}); +``` + +The type for the *capacityProviders* field on a *Cluster* would be a list of string literals. An alternative that ensures type safety is to have `FARGATE` and `FARGATE_SPOT` as enum values; however, this would make it potentially more difficult to support Autoscaling Group capacity providers in the future, since [capacity providers](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/cluster-capacity-providers.html) of that type would have be specified by their capacity provider name (as a string literal). + +The type for the *capacityProviderStrategies* field on a *Service* would be a list of [*CapacityProviderStrategy*](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-service-capacityproviderstrategyitem.html) objects, taking the form: + +{"[Base](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-service-capacityproviderstrategyitem.html#cfn-ecs-service-capacityproviderstrategyitem-base)" : Integer, "[CapacityProvider](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-service-capacityproviderstrategyitem.html#cfn-ecs-service-capacityproviderstrategyitem-capacityprovider)" : String, "[Weight](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-service-capacityproviderstrategyitem.html#cfn-ecs-service-capacityproviderstrategyitem-weight)" : Integer } + +*Base* and *Weight* fields will be *optional*; *CapacityProvider* is *required*. I.e.: + +```ts +/** + * A Capacity Provider strategy to use for the service. + */ +export interface CapacityProviderStrategy { + /** + * The name of the Capacity Provider. Currently only FARGATE and FARGATE_SPOT are supported. + */ + readonly capacityProvider: string; + + /** + * The base value designates how many tasks, at a minimum, to run on the specified capacity provider. Only one + * capacity provider in a capacity provider strategy can have a base defined. If no value is specified, the default + * value of 0 is used. + * + * @default - none + */ + readonly base?: number; + + /** + * The weight value designates the relative percentage of the total number of tasks launched that should use the + * specified +capacity provider. The weight value is taken into consideration after the base value, if defined, is satisfied. + * + * @default - 0 + */ + readonly weight?: number; +} + +``` +This new field would be added to the BaseService, not only for better extensibility when we add support for ASG capacity providers, but also to facilitate construction, since the FargateService extends the BaseService and would necessarily call super into the BaseService constructor. + +Implications Setting Launch Type + +Since it can be reasonably assumed that any CapacityProvideStrategies defined on the Service are what the customer intends to use on the Service, the LaunchType will *not* be set on the Service if CapacityProvideStrategies are specified. This is similar to how the LaunchType field is unset if the service uses an external DeploymentController (https://github.com/aws/aws-cdk/blob/master/packages/%40aws-cdk/aws-ecs/lib/base/base-service.ts#L374). + +On the other hand, this intent would not be as obvious with Default Capacity Provider Strategies defined a cluster. A *defaultCapacityProviderStrategy* specified on a cluster is used for any service that does not specify either a launchType or its own CapacityProviderStrategies. From the point of view of the ECS APIs, similar to how custom CapacityProvideStrategies defined on the Service are expected to supersede the defaultCapacityProviderStrategy on a cluster, the expected behavior for an ECS Service that specifies a launchType is for it to also ignore the Cluster’s defaultCapacityProviderStrategy. + +However, since the two Service constructs in the CDK (Ec2Service and FargateService) do not support having the launchType field passed in explicitly, it would not possible to infer whether the intent of the customer using one of these Service constructs is to use the implied launchType (currently set under the hood in the service’s constructor (https://github.com/aws/aws-cdk/blob/master/packages/%40aws-cdk/aws-ecs/lib/fargate/fargate-service.ts#L155)) or the defaultCapacityProviderStrategy. For this reason, we will not be adding the defaultCapacityProviderStrategy field on the Cluster construct for this iteration. + +_*Note*_: Future for support will be dependent on a re-design of the existing Service strategies. This will be treated in v2 of the ECS modules, likely with a single Service L2 construct and deprecation of the Ec2Service and FargateService constructs. + + +### Alternatives: +One alternative considered was to provide a more magical experience by populating the capacityProviders field under the hood (for example, by modifying the cluster if capacityProviderStrategies is set on a FargateService). However, there is the slight disadvantage of this being a less consistent behavior with how ASG capacity providers will be set in the future, and would break from the general pattern of setting resource fields at construction time. Furthermore, given that the cluster field on a service is of type ICluster, this may make it prohibitively difficult/impossible to provide the magical experience of modifying fields on the Cluster from the service. In the case where an ICluster is defined in a different stack, its properties cannot be modified from the stack where the Service is defined at all. For this reason, we will have to enforce the capacityProviders field being set explicitly on the Cluster construct. + +For future extensibility, we can however add an `addCapacityProvider` method on the Cluster resource, to allow modifying the cluster CapacityProviders field post-construction. + +Another option would be to create a new FargateCluster resource, that would have the two Fargate capacity providers set by default. The main advantage with this alternative would be that it would be consistent with the current Console experience, which sets the Fargate capacity providers for you if you choose the “Networking Only” cluster template via the cluster wizard. The downside is that it would be a more restrictive resource model that would go back on the decision to have a single generic ECS Cluster resource that could potentially contain both Fargate and EC2 services or tasks. Given that we are moving towards more generic versions of ECS resources, this is not a preferable solution. That being said, in the current iteration we can set the Fargate Capacity Providers on the cluster by default, but put them behind a feature flag, which we would be able to remove in the v2 version of the ECS module. Using the feature flag would ensure that there would not be a diff in the generated CFN template for existing customers defining ECS clusters in their stack who redeploy using an updated version of the CDK. + diff --git a/package.json b/package.json index 3cc060d034c26..e2ff66c0e9476 100644 --- a/package.json +++ b/package.json @@ -16,14 +16,14 @@ "devDependencies": { "conventional-changelog-cli": "^2.1.1", "fs-extra": "^9.1.0", - "graceful-fs": "^4.2.4", + "graceful-fs": "^4.2.6", "jest-junit": "^12.0.0", - "jsii-diff": "^1.15.0", - "jsii-pacmak": "^1.15.0", - "jsii-rosetta": "^1.15.0", + "jsii-diff": "^1.21.0", + "jsii-pacmak": "^1.21.0", + "jsii-rosetta": "^1.21.0", "lerna": "^3.22.1", - "standard-version": "^9.0.0", - "typescript": "~3.9.7" + "standard-version": "^9.1.0", + "typescript": "~3.9.9" }, "resolutions-comment": "should be removed or reviewed when nodeunit dependency is dropped or adjusted", "resolutions": { diff --git a/packages/@aws-cdk-containers/ecs-service-extensions/package.json b/packages/@aws-cdk-containers/ecs-service-extensions/package.json index f388877969ce0..05781b7cc3750 100644 --- a/packages/@aws-cdk-containers/ecs-service-extensions/package.json +++ b/packages/@aws-cdk-containers/ecs-service-extensions/package.json @@ -100,5 +100,8 @@ "announce": false }, "maturity": "stable", - "stability": "stable" + "stability": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/alexa-ask/package.json b/packages/@aws-cdk/alexa-ask/package.json index 04cddb184db13..48b5fa2b1e003 100644 --- a/packages/@aws-cdk/alexa-ask/package.json +++ b/packages/@aws-cdk/alexa-ask/package.json @@ -92,5 +92,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/app-delivery/package.json b/packages/@aws-cdk/app-delivery/package.json index b122d9803fc8b..8ac9e9cae6a12 100644 --- a/packages/@aws-cdk/app-delivery/package.json +++ b/packages/@aws-cdk/app-delivery/package.json @@ -63,7 +63,7 @@ "@types/nodeunit": "^0.0.31", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", - "fast-check": "^2.11.0", + "fast-check": "^2.13.0", "nodeunit": "^0.11.3", "pkglint": "0.0.0" }, @@ -114,5 +114,8 @@ }, "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/assert/package.json b/packages/@aws-cdk/assert/package.json index 9842fc4dcdfe3..1416fd7f4673c 100644 --- a/packages/@aws-cdk/assert/package.json +++ b/packages/@aws-cdk/assert/package.json @@ -21,11 +21,11 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/jest": "^26.0.15", + "@types/jest": "^26.0.20", "cdk-build-tools": "0.0.0", "jest": "^26.6.3", "pkglint": "0.0.0", - "ts-jest": "^26.4.4" + "ts-jest": "^26.5.1" }, "dependencies": { "@aws-cdk/cloud-assembly-schema": "0.0.0", @@ -56,5 +56,8 @@ "maturity": "experimental", "cdk-build": { "jest": true + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/assets/package.json b/packages/@aws-cdk/assets/package.json index b4cbb05123930..45735a4da140f 100644 --- a/packages/@aws-cdk/assets/package.json +++ b/packages/@aws-cdk/assets/package.json @@ -69,14 +69,14 @@ "devDependencies": { "@aws-cdk/assert": "0.0.0", "@types/nodeunit": "^0.0.31", - "@types/sinon": "^9.0.9", + "@types/sinon": "^9.0.10", "aws-cdk": "0.0.0", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "nodeunit": "^0.11.3", "pkglint": "0.0.0", - "sinon": "^9.2.1", - "ts-mock-imports": "^1.3.1" + "sinon": "^9.2.4", + "ts-mock-imports": "^1.3.3" }, "dependencies": { "@aws-cdk/core": "0.0.0", @@ -96,5 +96,8 @@ "maturity": "deprecated", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-accessanalyzer/package.json b/packages/@aws-cdk/aws-accessanalyzer/package.json index 6981503a54ad7..85c2a0d682662 100644 --- a/packages/@aws-cdk/aws-accessanalyzer/package.json +++ b/packages/@aws-cdk/aws-accessanalyzer/package.json @@ -93,5 +93,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-acmpca/package.json b/packages/@aws-cdk/aws-acmpca/package.json index c46c2f99c1757..e0956bffbf2c6 100644 --- a/packages/@aws-cdk/aws-acmpca/package.json +++ b/packages/@aws-cdk/aws-acmpca/package.json @@ -93,5 +93,8 @@ "maturity": "experimental", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-amazonmq/package.json b/packages/@aws-cdk/aws-amazonmq/package.json index 9c6ab30f28c1e..c9d6412f5effa 100644 --- a/packages/@aws-cdk/aws-amazonmq/package.json +++ b/packages/@aws-cdk/aws-amazonmq/package.json @@ -92,5 +92,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-amplify/package.json b/packages/@aws-cdk/aws-amplify/package.json index 865109cf27561..ea1abbde2ba37 100644 --- a/packages/@aws-cdk/aws-amplify/package.json +++ b/packages/@aws-cdk/aws-amplify/package.json @@ -109,5 +109,8 @@ }, "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-apigateway/lib/access-log.ts b/packages/@aws-cdk/aws-apigateway/lib/access-log.ts index 72550daa9e528..726a6928315ee 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/access-log.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/access-log.ts @@ -411,7 +411,7 @@ export class AccessLogField { * not from the backend Lambda function. */ public static contextIntegrationStatus() { - return '$context.integrationStatus.'; + return '$context.integrationStatus'; } /** diff --git a/packages/@aws-cdk/aws-apigateway/package.json b/packages/@aws-cdk/aws-apigateway/package.json index 3a0641e8b1162..a7e21f2842eca 100644 --- a/packages/@aws-cdk/aws-apigateway/package.json +++ b/packages/@aws-cdk/aws-apigateway/package.json @@ -325,5 +325,8 @@ "awscdkio": { "announce": false }, - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/.eslintrc.js b/packages/@aws-cdk/aws-apigatewayv2-authorizers/.eslintrc.js new file mode 100644 index 0000000000000..61dd8dd001f63 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/.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-apigatewayv2-authorizers/.gitignore b/packages/@aws-cdk/aws-apigatewayv2-authorizers/.gitignore new file mode 100644 index 0000000000000..becda34c45624 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/.gitignore @@ -0,0 +1,17 @@ +*.d.ts +*.generated.ts +*.js +*.js.map +*.snk +.jsii +.LAST_BUILD +.LAST_PACKAGE +nyc.config.js +.nyc_output +coverage +dist +tsconfig.json +!.eslintrc.js +!jest.config.js + +junit.xml \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/.npmignore b/packages/@aws-cdk/aws-apigatewayv2-authorizers/.npmignore new file mode 100644 index 0000000000000..093c734b1bd2f --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/.npmignore @@ -0,0 +1,28 @@ +# The basics +*.ts +*.tgz +*.snk +!*.d.ts +!*.js +**/cdk.out + +# Coverage +coverage +.nyc_output +.nycrc + +# Build gear +dist +.LAST_BUILD +.LAST_PACKAGE + +*.tsbuildinfo +tsconfig.json +!.jsii +.eslintrc.js +jest.config.js + +# exclude cdk artifacts +**/cdk.out +junit.xml +test/ \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/LICENSE b/packages/@aws-cdk/aws-apigatewayv2-authorizers/LICENSE new file mode 100644 index 0000000000000..28e4bdcec77ec --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/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-apigatewayv2-authorizers/NOTICE b/packages/@aws-cdk/aws-apigatewayv2-authorizers/NOTICE new file mode 100644 index 0000000000000..5fc3826926b5b --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/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-apigatewayv2-authorizers/README.md b/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md new file mode 100644 index 0000000000000..9591ecfe4ffde --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md @@ -0,0 +1,82 @@ +# AWS APIGatewayv2 Authorizers + + +--- + +![cdk-constructs: Experimental](https://img.shields.io/badge/cdk--constructs-experimental-important.svg?style=for-the-badge) + +> The APIs of higher level constructs in this module are experimental and under active development. +> They are subject to non-backward compatible changes or removal in any future version. These are +> not subject to the [Semantic Versioning](https://semver.org/) model and breaking changes will be +> announced in the release notes. This means that while you may use them, you may need to update +> your source code when upgrading to a newer version of this package. + +--- + + + +## Table of Contents + +- [HTTP APIs](#http-apis) +- [JWT Authorizers](#jwt-authorizers) + - [User Pool Authorizer](#user-pool-authorizer) + +## HTTP APIs + +API Gateway supports multiple mechanisms for controlling and managing access to your HTTP API. They are mainly +classified into Lambda Authorizers, JWT authorizers and standard AWS IAM roles and policies. More information is +available at [Controlling and managing access to an HTTP +API](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-access-control.html). + +## JWT Authorizers + +JWT authorizers allow the use of JSON Web Tokens (JWTs) as part of [OpenID Connect](https://openid.net/specs/openid-connect-core-1_0.html) and [OAuth 2.0](https://oauth.net/2/) frameworks to allow and restrict clients from accessing HTTP APIs. + +When configured on a route, the API Gateway service validates the JWTs submitted by the client, and allows or denies access based on its content. + +API gateway uses the `identitySource` to determine where to look for the token. By default it checks the http `Authorization` header. However it also [supports a number of other options](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-lambda-authorizer.html#http-api-lambda-authorizer.identity-sources). It then decodes the JWT and validates the signature and claims, against the options defined in the authorizer and route (scopes). For more information check the [JWT Authorizer documentation](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-jwt-authorizer.html). + +```ts +const authorizer = new HttpJwtAuthorizer({ + jwtAudience: ['3131231'], + jwtIssuer: 'https://test.us.auth0.com', +}); + +const api = new HttpApi(stack, 'HttpApi'); + +api.addRoutes({ + integration: new HttpProxyIntegration({ + url: 'https://get-books-proxy.myproxy.internal', + }), + path: '/books', + authorizer, +}); +``` + +### User Pool Authorizer + +User Pool Authorizer is a type of JWT Authorizer that uses a Cognito user pool and app client to control who can access your Api. After a successful authorization from the app client, the generated access token will be used as the JWT. + +Clients accessing an API that uses a user pool authorizer must first sign in to a user pool and obtain an identity or access token. +They must then use this token in the `Authorization` header of the API call. More information is available at [using Amazon Cognito user +pools as authorizer](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-integrate-with-cognito.html). + +```ts +const userPool = new UserPool(stack, 'UserPool'); +const userPoolClient = userPool.addClient('UserPoolClient'); + +const authorizer = new HttpUserPoolAuthorizer({ + userPool, + userPoolClient, +}); + +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/jest.config.js b/packages/@aws-cdk/aws-apigatewayv2-authorizers/jest.config.js new file mode 100644 index 0000000000000..b5ccdecc15ee0 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/jest.config.js @@ -0,0 +1,10 @@ +const baseConfig = require('cdk-build-tools/config/jest.config'); +module.exports = { + ...baseConfig, + coverageThreshold: { + global: { + ...baseConfig.coverageThreshold.global, + branches: 70, + }, + }, +}; \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/index.ts b/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/index.ts new file mode 100644 index 0000000000000..9f9ad94c6a4b7 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/index.ts @@ -0,0 +1,2 @@ +export * from './user-pool'; +export * from './jwt'; \ 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 new file mode 100644 index 0000000000000..afb5f10ac07f8 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/jwt.ts @@ -0,0 +1,70 @@ +import { + HttpAuthorizer, + HttpAuthorizerType, + HttpRouteAuthorizerBindOptions, + HttpRouteAuthorizerConfig, + IHttpRouteAuthorizer, +} from '@aws-cdk/aws-apigatewayv2'; +import { Token } from '@aws-cdk/core'; + +/** + * Properties to initialize HttpJwtAuthorizer. + */ +export interface HttpJwtAuthorizerProps { + + /** + * The name of the authorizer + * @default 'JwtAuthorizer' + */ + readonly authorizerName?: string; + + /** + * The identity source for which authorization is requested. + * + * @default ['$request.header.Authorization'] + */ + readonly identitySource?: string[], + + /** + * A list of the intended recipients of the JWT. + * A valid JWT must provide an aud that matches at least one entry in this list. + */ + readonly jwtAudience: string[] + + /** + * The base domain of the identity provider that issues JWT. + */ + readonly jwtIssuer: string; +} + +/** + * Authorize Http Api routes on whether the requester is registered as part of + * an AWS Cognito user pool. + */ +export class HttpJwtAuthorizer implements IHttpRouteAuthorizer { + private authorizer?: HttpAuthorizer; + + constructor(private readonly props: HttpJwtAuthorizerProps) { + } + + public bind(options: HttpRouteAuthorizerBindOptions): HttpRouteAuthorizerConfig { + if (!this.authorizer) { + const id = this.props.authorizerName && !Token.isUnresolved(this.props.authorizerName) ? + this.props.authorizerName : 'JwtAuthorizer'; + + this.authorizer = new HttpAuthorizer(options.scope, id, { + httpApi: options.route.httpApi, + identitySource: this.props.identitySource ?? ['$request.header.Authorization'], + type: HttpAuthorizerType.JWT, + authorizerName: this.props.authorizerName, + jwtAudience: this.props.jwtAudience, + jwtIssuer: this.props.jwtIssuer, + }); + } + + return { + authorizerId: this.authorizer.authorizerId, + authorizationType: HttpAuthorizerType.JWT, + }; + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000000..4a251b8eb7406 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/user-pool.ts @@ -0,0 +1,69 @@ +import { HttpAuthorizer, HttpAuthorizerType, HttpRouteAuthorizerBindOptions, HttpRouteAuthorizerConfig, IHttpRouteAuthorizer } from '@aws-cdk/aws-apigatewayv2'; +import { IUserPool, IUserPoolClient } from '@aws-cdk/aws-cognito'; +import { Stack, Token } from '@aws-cdk/core'; + +/** + * Properties to initialize UserPoolAuthorizer. + */ +export interface UserPoolAuthorizerProps { + /** + * The user pool client that should be used to authorize requests with the user pool. + */ + readonly userPoolClient: IUserPoolClient; + + /** + * The associated user pool + */ + readonly userPool: IUserPool; + + /** + * The AWS region in which the user pool is present + * @default - same region as the Route the authorizer is attached to. + */ + readonly userPoolRegion?: string; + + /** + * The name of the authorizer + * @default 'UserPoolAuthorizer' + */ + readonly authorizerName?: string; + + /** + * The identity source for which authorization is requested. + * + * @default ['$request.header.Authorization'] + */ + readonly identitySource?: string[], +} + +/** + * Authorize Http Api routes on whether the requester is registered as part of + * an AWS Cognito user pool. + */ +export class HttpUserPoolAuthorizer implements IHttpRouteAuthorizer { + private authorizer?: HttpAuthorizer; + + constructor(private readonly props: UserPoolAuthorizerProps) { + } + + public bind(options: HttpRouteAuthorizerBindOptions): HttpRouteAuthorizerConfig { + if (!this.authorizer) { + const id = this.props.authorizerName && !Token.isUnresolved(this.props.authorizerName) ? + this.props.authorizerName : 'UserPoolAuthorizer'; + const region = this.props.userPoolRegion ?? Stack.of(options.scope).region; + this.authorizer = new HttpAuthorizer(options.scope, id, { + httpApi: options.route.httpApi, + identitySource: this.props.identitySource ?? ['$request.header.Authorization'], + type: HttpAuthorizerType.JWT, + authorizerName: this.props.authorizerName, + jwtAudience: [this.props.userPoolClient.userPoolClientId], + jwtIssuer: `https://cognito-idp.${region}.amazonaws.com/${this.props.userPool.userPoolId}`, + }); + } + + return { + authorizerId: this.authorizer.authorizerId, + authorizationType: HttpAuthorizerType.JWT, + }; + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/index.ts b/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/index.ts new file mode 100644 index 0000000000000..c202386ae710e --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/index.ts @@ -0,0 +1 @@ +export * from './http'; diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json b/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json new file mode 100644 index 0000000000000..f21b19c75f329 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json @@ -0,0 +1,104 @@ +{ + "name": "@aws-cdk/aws-apigatewayv2-authorizers", + "version": "0.0.0", + "description": "Authorizers for AWS APIGateway V2", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "jsii": { + "outdir": "dist", + "targets": { + "dotnet": { + "namespace": "Amazon.CDK.AWS.APIGatewayv2.Authorizers", + "packageId": "Amazon.CDK.AWS.APIGatewayv2.Authorizers", + "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.apigatewayv2.authorizers", + "maven": { + "groupId": "software.amazon.awscdk", + "artifactId": "apigatewayv2-authorizers" + } + }, + "python": { + "distName": "aws-cdk.aws-apigatewayv2-authorizers", + "module": "aws_cdk.aws_apigatewayv2_authorizers", + "classifiers": [ + "Framework :: AWS CDK", + "Framework :: AWS CDK :: 1" + ] + } + }, + "projectReferences": true + }, + "repository": { + "type": "git", + "url": "https://github.com/aws/aws-cdk.git", + "directory": "packages/@aws-cdk/aws-apigatewayv2-authorizers" + }, + "homepage": "https://github.com/aws/aws-cdk", + "scripts": { + "build": "cdk-build", + "integ": "cdk-integ", + "lint": "cdk-lint", + "package": "cdk-package", + "awslint": "cdk-awslint", + "pkglint": "pkglint -f", + "test": "cdk-test", + "watch": "cdk-watch", + "compat": "cdk-compat", + "build+test": "yarn build && yarn test", + "build+test+package": "yarn build+test && yarn package", + "rosetta:extract": "yarn --silent jsii-rosetta extract" + }, + "cdk-build": { + "jest": true, + "env": { + "AWSLINT_BASE_CONSTRUCT": true + } + }, + "keywords": [ + "aws", + "cdk", + "constructs", + "apigateway" + ], + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com", + "organization": true + }, + "license": "Apache-2.0", + "devDependencies": { + "@aws-cdk/assert": "0.0.0", + "@aws-cdk/aws-apigatewayv2-integrations": "0.0.0", + "@aws-cdk/aws-lambda": "0.0.0", + "cdk-build-tools": "0.0.0", + "cdk-integ-tools": "0.0.0", + "pkglint": "0.0.0" + }, + "dependencies": { + "@aws-cdk/aws-apigatewayv2": "0.0.0", + "@aws-cdk/aws-cognito": "0.0.0", + "@aws-cdk/core": "0.0.0", + "constructs": "^3.2.0" + }, + "peerDependencies": { + "@aws-cdk/aws-apigatewayv2": "0.0.0", + "@aws-cdk/aws-cognito": "0.0.0", + "@aws-cdk/core": "0.0.0", + "constructs": "^3.2.0" + }, + "engines": { + "node": ">= 10.13.0 <13 || >=13.7.0" + }, + "stability": "experimental", + "maturity": "experimental", + "awscdkio": { + "announce": false + }, + "publishConfig": { + "tag": "latest" + } +} diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/integ.user-pool.expected.json b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/integ.user-pool.expected.json new file mode 100644 index 0000000000000..b53be9c1b9c27 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/integ.user-pool.expected.json @@ -0,0 +1,288 @@ +{ + "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" + }, + "/*/*/" + ] + ] + } + } + }, + "MyHttpApiGETE0EFC6F8": { + "Type": "AWS::ApiGatewayV2::Route", + "Properties": { + "ApiId": { + "Ref": "MyHttpApi8AEAAC21" + }, + "RouteKey": "GET /", + "AuthorizationScopes": [], + "AuthorizationType": "JWT", + "AuthorizerId": { + "Ref": "MyHttpApiUserPoolAuthorizer8754262B" + }, + "Target": { + "Fn::Join": [ + "", + [ + "integrations/", + { + "Ref": "MyHttpApiGETHttpIntegration6f095b8469365f72e33fa33d9711b140516EBE31" + } + ] + ] + } + } + }, + "MyHttpApiGETHttpIntegration6f095b8469365f72e33fa33d9711b140516EBE31": { + "Type": "AWS::ApiGatewayV2::Integration", + "Properties": { + "ApiId": { + "Ref": "MyHttpApi8AEAAC21" + }, + "IntegrationType": "AWS_PROXY", + "IntegrationUri": { + "Fn::GetAtt": [ + "lambda8B5974B5", + "Arn" + ] + }, + "PayloadFormatVersion": "2.0" + } + }, + "MyHttpApiUserPoolAuthorizer8754262B": { + "Type": "AWS::ApiGatewayV2::Authorizer", + "Properties": { + "ApiId": { + "Ref": "MyHttpApi8AEAAC21" + }, + "AuthorizerType": "JWT", + "IdentitySource": [ + "$request.header.Authorization" + ], + "Name": "UserPoolAuthorizer", + "JwtConfiguration": { + "Audience": [ + { + "Ref": "userpoolmyclientFAD947AB" + } + ], + "Issuer": { + "Fn::Join": [ + "", + [ + "https://cognito-idp.", + { + "Ref": "AWS::Region" + }, + ".amazonaws.com/", + { + "Ref": "userpool0AC4AA96" + } + ] + ] + } + } + } + }, + "userpool0AC4AA96": { + "Type": "AWS::Cognito::UserPool", + "Properties": { + "AccountRecoverySetting": { + "RecoveryMechanisms": [ + { + "Name": "verified_phone_number", + "Priority": 1 + }, + { + "Name": "verified_email", + "Priority": 2 + } + ] + }, + "AdminCreateUserConfig": { + "AllowAdminCreateUserOnly": true + }, + "EmailVerificationMessage": "The verification code to your new account is {####}", + "EmailVerificationSubject": "Verify your new account", + "SmsVerificationMessage": "The verification code to your new account is {####}", + "VerificationMessageTemplate": { + "DefaultEmailOption": "CONFIRM_WITH_CODE", + "EmailMessage": "The verification code to your new account is {####}", + "EmailSubject": "Verify your new account", + "SmsMessage": "The verification code to your new account is {####}" + } + } + }, + "userpoolmyclientFAD947AB": { + "Type": "AWS::Cognito::UserPoolClient", + "Properties": { + "UserPoolId": { + "Ref": "userpool0AC4AA96" + }, + "AllowedOAuthFlows": [ + "implicit", + "code" + ], + "AllowedOAuthFlowsUserPoolClient": true, + "AllowedOAuthScopes": [ + "profile", + "phone", + "email", + "openid", + "aws.cognito.signin.user.admin" + ], + "CallbackURLs": [ + "https://example.com" + ], + "SupportedIdentityProviders": [ + "COGNITO" + ] + } + }, + "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": "AssetParameters7410bbb25893071ddf955447cf906ac518465ea509469e6b012c28dde8f8b5cdS3Bucket0AFE1748" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters7410bbb25893071ddf955447cf906ac518465ea509469e6b012c28dde8f8b5cdS3VersionKey8E654BCC" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters7410bbb25893071ddf955447cf906ac518465ea509469e6b012c28dde8f8b5cdS3VersionKey8E654BCC" + } + ] + } + ] + } + ] + ] + } + }, + "Role": { + "Fn::GetAtt": [ + "lambdaServiceRole494E4CA6", + "Arn" + ] + }, + "Handler": "index.handler", + "Runtime": "nodejs12.x" + }, + "DependsOn": [ + "lambdaServiceRole494E4CA6" + ] + } + }, + "Parameters": { + "AssetParameters7410bbb25893071ddf955447cf906ac518465ea509469e6b012c28dde8f8b5cdS3Bucket0AFE1748": { + "Type": "String", + "Description": "S3 bucket for asset \"7410bbb25893071ddf955447cf906ac518465ea509469e6b012c28dde8f8b5cd\"" + }, + "AssetParameters7410bbb25893071ddf955447cf906ac518465ea509469e6b012c28dde8f8b5cdS3VersionKey8E654BCC": { + "Type": "String", + "Description": "S3 key for asset version \"7410bbb25893071ddf955447cf906ac518465ea509469e6b012c28dde8f8b5cd\"" + }, + "AssetParameters7410bbb25893071ddf955447cf906ac518465ea509469e6b012c28dde8f8b5cdArtifactHashC4761AE9": { + "Type": "String", + "Description": "Artifact hash for asset \"7410bbb25893071ddf955447cf906ac518465ea509469e6b012c28dde8f8b5cd\"" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/integ.user-pool.ts b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/integ.user-pool.ts new file mode 100644 index 0000000000000..edf455f4a787c --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/integ.user-pool.ts @@ -0,0 +1,42 @@ +/// !cdk-integ pragma:ignore-assets +import * as path from 'path'; +import { HttpApi, HttpMethod } from '@aws-cdk/aws-apigatewayv2'; +import { LambdaProxyIntegration } from '@aws-cdk/aws-apigatewayv2-integrations'; +import * as cognito from '@aws-cdk/aws-cognito'; +import * as lambda from '@aws-cdk/aws-lambda'; +import { App, Stack } from '@aws-cdk/core'; +import { HttpUserPoolAuthorizer } from '../../lib'; + +/* + * Stack verification steps: + * * `curl -s -o /dev/null -w "%{http_code}" ` should return 401 + * * `curl -s -o /dev/null -w "%{http_code}" -H 'Authorization: deny' ` should return 403 + * * `curl -s -o /dev/null -w "%{http_code}" -H 'Authorization: allow' ` should return 200 + */ + +const app = new App(); +const stack = new Stack(app, 'AuthorizerInteg'); + +const httpApi = new HttpApi(stack, 'MyHttpApi'); + +const userPool = new cognito.UserPool(stack, 'userpool'); + +const userPoolClient = userPool.addClient('my-client'); + +const authorizer = new HttpUserPoolAuthorizer({ + userPool, + userPoolClient, +}); + +const handler = new lambda.Function(stack, 'lambda', { + runtime: lambda.Runtime.NODEJS_12_X, + handler: 'index.handler', + code: lambda.AssetCode.fromAsset(path.join(__dirname, '../integ.user-pool.handler')), +}); + +httpApi.addRoutes({ + path: '/', + methods: [HttpMethod.GET], + integration: new LambdaProxyIntegration({ handler }), + authorizer, +}); diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/jwt.test.ts b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/jwt.test.ts new file mode 100644 index 0000000000000..8b27dc312a1a3 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/jwt.test.ts @@ -0,0 +1,70 @@ +import '@aws-cdk/assert/jest'; +import { HttpApi, HttpIntegrationType, HttpRouteIntegrationBindOptions, IHttpRouteIntegration, PayloadFormatVersion } from '@aws-cdk/aws-apigatewayv2'; +import { Stack } from '@aws-cdk/core'; +import { HttpJwtAuthorizer } from '../../lib'; + +describe('HttpJwtAuthorizer', () => { + test('default', () => { + // GIVEN + const stack = new Stack(); + const api = new HttpApi(stack, 'HttpApi'); + + const authorizer = new HttpJwtAuthorizer({ + jwtAudience: ['3131231'], + jwtIssuer: 'https://test.us.auth0.com', + }); + + // WHEN + api.addRoutes({ + integration: new DummyRouteIntegration(), + path: '/books', + authorizer, + }); + + // THEN + expect(stack).toHaveResource('AWS::ApiGatewayV2::Authorizer', { + AuthorizerType: 'JWT', + IdentitySource: ['$request.header.Authorization'], + JwtConfiguration: { + Audience: ['3131231'], + Issuer: 'https://test.us.auth0.com', + }, + }); + }); + + test('same authorizer is used when bound to multiple routes', () => { + // GIVEN + const stack = new Stack(); + const api = new HttpApi(stack, 'HttpApi'); + + const authorizer = new HttpJwtAuthorizer({ + jwtAudience: ['3131231'], + jwtIssuer: 'https://test.us.auth0.com', + }); + + // WHEN + api.addRoutes({ + integration: new DummyRouteIntegration(), + path: '/books', + authorizer, + }); + api.addRoutes({ + integration: new DummyRouteIntegration(), + path: '/pets', + authorizer, + }); + + // THEN + expect(stack).toCountResources('AWS::ApiGatewayV2::Authorizer', 1); + }); +}); + +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/http/user-pool.test.ts b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/user-pool.test.ts new file mode 100644 index 0000000000000..c12e00a342acd --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/user-pool.test.ts @@ -0,0 +1,83 @@ +import '@aws-cdk/assert/jest'; +import { HttpApi, HttpIntegrationType, HttpRouteIntegrationBindOptions, IHttpRouteIntegration, PayloadFormatVersion } from '@aws-cdk/aws-apigatewayv2'; +import { UserPool } from '@aws-cdk/aws-cognito'; +import { Stack } from '@aws-cdk/core'; +import { HttpUserPoolAuthorizer } from '../../lib'; + +describe('HttpUserPoolAuthorizer', () => { + test('default', () => { + // GIVEN + const stack = new Stack(); + const api = new HttpApi(stack, 'HttpApi'); + const userPool = new UserPool(stack, 'UserPool'); + const userPoolClient = userPool.addClient('UserPoolClient'); + const authorizer = new HttpUserPoolAuthorizer({ + userPool, + userPoolClient, + }); + + // WHEN + api.addRoutes({ + integration: new DummyRouteIntegration(), + path: '/books', + authorizer, + }); + + // THEN + expect(stack).toHaveResource('AWS::ApiGatewayV2::Authorizer', { + AuthorizerType: 'JWT', + IdentitySource: ['$request.header.Authorization'], + JwtConfiguration: { + Audience: [stack.resolve(userPoolClient.userPoolClientId)], + Issuer: { + 'Fn::Join': [ + '', + [ + 'https://cognito-idp.', + { Ref: 'AWS::Region' }, + '.amazonaws.com/', + stack.resolve(userPool.userPoolId), + ], + ], + }, + }, + }); + }); + + test('same authorizer is used when bound to multiple routes', () => { + // GIVEN + const stack = new Stack(); + const api = new HttpApi(stack, 'HttpApi'); + const userPool = new UserPool(stack, 'UserPool'); + const userPoolClient = userPool.addClient('UserPoolClient'); + const authorizer = new HttpUserPoolAuthorizer({ + userPool, + userPoolClient, + }); + + // WHEN + api.addRoutes({ + integration: new DummyRouteIntegration(), + path: '/books', + authorizer, + }); + api.addRoutes({ + integration: new DummyRouteIntegration(), + path: '/pets', + authorizer, + }); + + // THEN + expect(stack).toCountResources('AWS::ApiGatewayV2::Authorizer', 1); + }); +}); + +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.user-pool.handler/index.ts b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/integ.user-pool.handler/index.ts new file mode 100644 index 0000000000000..afedb7efe3311 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/integ.user-pool.handler/index.ts @@ -0,0 +1,23 @@ +/* eslint-disable no-console */ + +export const handler = async (event: any, _context: any = {}): Promise => { + const authToken: string = event.authorizationToken; + console.log(`event.authorizationToken = ${authToken}`); + if (authToken === 'allow' || authToken === 'deny') { + return { + principalId: 'user', + policyDocument: { + Version: '2012-10-17', + Statement: [ + { + Action: 'execute-api:Invoke', + Effect: authToken, + Resource: event.methodArn, + }, + ], + }, + }; + } else { + throw new Error('Unauthorized'); + } +}; \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2-integrations/package.json b/packages/@aws-cdk/aws-apigatewayv2-integrations/package.json index a6bbd74293860..c4a3e3039b9a3 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-integrations/package.json +++ b/packages/@aws-cdk/aws-apigatewayv2-integrations/package.json @@ -103,5 +103,8 @@ "maturity": "experimental", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } 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 d96b67d1582f9..e0ffbc32a3349 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 @@ -615,13 +615,14 @@ "Ref": "HttpProxyPrivateApiA55E154D" }, "RouteKey": "$default", + "AuthorizationScopes": [], "Target": { "Fn::Join": [ "", [ "integrations/", { - "Ref": "HttpProxyPrivateApiHttpIntegrationa4f5237945b4bba2a6a4cd8c5a7be8839B686B4E" + "Ref": "HttpProxyPrivateApiDefaultRouteHttpIntegration1a580b19954e4317026ffbce1f7d5ade7A32685B" } ] ] @@ -646,7 +647,7 @@ "SecurityGroupIds": [] } }, - "HttpProxyPrivateApiHttpIntegrationa4f5237945b4bba2a6a4cd8c5a7be8839B686B4E": { + "HttpProxyPrivateApiDefaultRouteHttpIntegration1a580b19954e4317026ffbce1f7d5ade7A32685B": { "Type": "AWS::ApiGatewayV2::Integration", "Properties": { "ApiId": { 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 1c2bc834f7f48..e5220fe2959b7 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 @@ -101,20 +101,21 @@ "Ref": "LambdaProxyApi67594471" }, "RouteKey": "$default", + "AuthorizationScopes": [], "Target": { "Fn::Join": [ "", [ "integrations/", { - "Ref": "LambdaProxyApiHttpIntegration5fc3e065dcc18f77be1ff86cabb5294a98B12A6C" + "Ref": "LambdaProxyApiDefaultRouteHttpIntegration70df0ec52c3e3b6bbc96e64ce3a05f24EE575CBA" } ] ] } } }, - "LambdaProxyApiHttpIntegration5fc3e065dcc18f77be1ff86cabb5294a98B12A6C": { + "LambdaProxyApiDefaultRouteHttpIntegration70df0ec52c3e3b6bbc96e64ce3a05f24EE575CBA": { "Type": "AWS::ApiGatewayV2::Integration", "Properties": { "ApiId": { @@ -154,20 +155,21 @@ "Ref": "HttpProxyApiD0217C67" }, "RouteKey": "$default", + "AuthorizationScopes": [], "Target": { "Fn::Join": [ "", [ "integrations/", { - "Ref": "HttpProxyApiHttpIntegration54d3201aa1691a2307067d718d9dea776A187065" + "Ref": "HttpProxyApiDefaultRouteHttpIntegration8eeecf9ecdb91f31bebf6bd54fb711a41921AB82" } ] ] } } }, - "HttpProxyApiHttpIntegration54d3201aa1691a2307067d718d9dea776A187065": { + "HttpProxyApiDefaultRouteHttpIntegration8eeecf9ecdb91f31bebf6bd54fb711a41921AB82": { "Type": "AWS::ApiGatewayV2::Integration", "Properties": { "ApiId": { 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 c2661f62833a8..f5bc444d929c3 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 @@ -101,20 +101,21 @@ "Ref": "LambdaProxyApi67594471" }, "RouteKey": "$default", + "AuthorizationScopes": [], "Target": { "Fn::Join": [ "", [ "integrations/", { - "Ref": "LambdaProxyApiHttpIntegration5fc3e065dcc18f77be1ff86cabb5294a98B12A6C" + "Ref": "LambdaProxyApiDefaultRouteHttpIntegration70df0ec52c3e3b6bbc96e64ce3a05f24EE575CBA" } ] ] } } }, - "LambdaProxyApiHttpIntegration5fc3e065dcc18f77be1ff86cabb5294a98B12A6C": { + "LambdaProxyApiDefaultRouteHttpIntegration70df0ec52c3e3b6bbc96e64ce3a05f24EE575CBA": { "Type": "AWS::ApiGatewayV2::Integration", "Properties": { "ApiId": { 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 a29f632928938..0446031c61a19 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 @@ -580,13 +580,14 @@ "Ref": "HttpProxyPrivateApiA55E154D" }, "RouteKey": "$default", + "AuthorizationScopes": [], "Target": { "Fn::Join": [ "", [ "integrations/", { - "Ref": "HttpProxyPrivateApiHttpIntegration392420cf56fd6784275525566e09a727DDE15AB1" + "Ref": "HttpProxyPrivateApiDefaultRouteHttpIntegration1a580b19954e4317026ffbce1f7d5ade7A32685B" } ] ] @@ -611,7 +612,7 @@ "SecurityGroupIds": [] } }, - "HttpProxyPrivateApiHttpIntegration392420cf56fd6784275525566e09a727DDE15AB1": { + "HttpProxyPrivateApiDefaultRouteHttpIntegration1a580b19954e4317026ffbce1f7d5ade7A32685B": { "Type": "AWS::ApiGatewayV2::Integration", "Properties": { "ApiId": { 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 d815552e3107d..28b636ef54808 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 @@ -581,20 +581,21 @@ "Ref": "HttpProxyPrivateApiA55E154D" }, "RouteKey": "$default", + "AuthorizationScopes": [], "Target": { "Fn::Join": [ "", [ "integrations/", { - "Ref": "HttpProxyPrivateApiHttpIntegration763b273f031b9eb3c58b2fa36954470072FBC87C" + "Ref": "HttpProxyPrivateApiDefaultRouteHttpIntegrationa5ec5390ca688d567e9449daf58afc6f6DEAA8A8" } ] ] } } }, - "HttpProxyPrivateApiHttpIntegration763b273f031b9eb3c58b2fa36954470072FBC87C": { + "HttpProxyPrivateApiDefaultRouteHttpIntegrationa5ec5390ca688d567e9449daf58afc6f6DEAA8A8": { "Type": "AWS::ApiGatewayV2::Integration", "Properties": { "ApiId": { diff --git a/packages/@aws-cdk/aws-apigatewayv2/README.md b/packages/@aws-cdk/aws-apigatewayv2/README.md index 1fda5a731ff40..4da900f271e8f 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/README.md +++ b/packages/@aws-cdk/aws-apigatewayv2/README.md @@ -34,6 +34,7 @@ Higher level constructs for Websocket APIs | ![Not Implemented](https://img.shie - [Cross Origin Resource Sharing (CORS)](#cross-origin-resource-sharing-cors) - [Publishing HTTP APIs](#publishing-http-apis) - [Custom Domain](#custom-domain) + - [Managing access](#managing-access) - [Metrics](#metrics) - [VPC Link](#vpc-link) - [Private Integration](#private-integration) @@ -222,6 +223,13 @@ with 3 API mapping resources across different APIs and Stages. | api | beta | `https://${domainName}/bar` | | apiDemo | $default | `https://${domainName}/demo` | +### Managing access + +API Gateway supports multiple mechanisms for [controlling and managing access to your HTTP +API](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-access-control.html) through authorizers. + +These authorizers can be found in the [APIGatewayV2-Authorizers](https://docs.aws.amazon.com/cdk/api/latest/docs/aws-apigatewayv2-authorizers-readme.html) constructs library. + ## Metrics The API Gateway v2 service sends metrics around the performance of HTTP APIs to Amazon CloudWatch. diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/common/authorizer.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/common/authorizer.ts new file mode 100644 index 0000000000000..609d469a572fa --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/common/authorizer.ts @@ -0,0 +1,12 @@ +import { IResource } from '@aws-cdk/core'; + +/** + * Represents an Authorizer. + */ +export interface IAuthorizer extends IResource { + /** + * Id of the Authorizer + * @attribute + */ + readonly authorizerId: string +} diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/common/index.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/common/index.ts index d727436b86c99..eeb237a4e7f84 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/common/index.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/common/index.ts @@ -3,3 +3,4 @@ export * from './route'; export * from './stage'; export * from './domain-name'; export * from './api-mapping'; +export * from './authorizer'; diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts index 94ea7991360ba..52e5f1fccbe07 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts @@ -1,10 +1,11 @@ import * as crypto from 'crypto'; import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; -import { Duration, IResource, Resource } from '@aws-cdk/core'; +import { Duration, IResource, Resource, Stack } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnApi, CfnApiProps } from '../apigatewayv2.generated'; import { DefaultDomainMappingOptions } from '../http/stage'; -import { HttpIntegration, HttpRouteIntegrationConfig, IHttpRouteIntegration } from './integration'; +import { IHttpRouteAuthorizer } from './authorizer'; +import { IHttpRouteIntegration, HttpIntegration, HttpRouteIntegrationConfig } from './integration'; import { BatchHttpRouteOptions, HttpMethod, HttpRoute, HttpRouteKey } from './route'; import { HttpStage, HttpStageOptions } from './stage'; import { VpcLink, VpcLinkProps } from './vpc-link'; @@ -91,7 +92,7 @@ export interface IHttpApi extends IResource { * Add a http integration * @internal */ - _addIntegration(config: HttpRouteIntegrationConfig): HttpIntegration; + _addIntegration(scope: Construct, config: HttpRouteIntegrationConfig): HttpIntegration; } /** @@ -201,6 +202,20 @@ export interface AddRoutesOptions extends BatchHttpRouteOptions { * @default HttpMethod.ANY */ readonly methods?: HttpMethod[]; + + /** + * Authorizer to be associated to these routes. + * @default - No authorizer + */ + readonly authorizer?: IHttpRouteAuthorizer; + + /** + * The list of OIDC scopes to include in the authorization. + * + * These scopes will be merged with the scopes from the attached authorizer + * @default - no additional authorization scopes + */ + readonly authorizationScopes?: string[]; } abstract class HttpApiBase extends Resource implements IHttpApi { // note that this is not exported @@ -259,15 +274,15 @@ abstract class HttpApiBase extends Resource implements IHttpApi { // note that t /** * @internal */ - public _addIntegration(config: HttpRouteIntegrationConfig): HttpIntegration { - const stringifiedConfig = JSON.stringify(config); + public _addIntegration(scope: Construct, config: HttpRouteIntegrationConfig): HttpIntegration { + const stringifiedConfig = JSON.stringify(Stack.of(scope).resolve(config)); const configHash = crypto.createHash('md5').update(stringifiedConfig).digest('hex'); if (configHash in this.httpIntegrations) { return this.httpIntegrations[configHash]; } - const integration = new HttpIntegration(this, `HttpIntegration-${configHash}`, { + const integration = new HttpIntegration(scope, `HttpIntegration-${configHash}`, { httpApi: this, integrationType: config.type, integrationUri: config.uri, @@ -446,6 +461,8 @@ export class HttpApi extends HttpApiBase { httpApi: this, routeKey: HttpRouteKey.with(options.path, method), integration: options.integration, + authorizer: options.authorizer, + authorizationScopes: options.authorizationScopes, })); } } diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts new file mode 100644 index 0000000000000..aadfb630ba276 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts @@ -0,0 +1,174 @@ +import { Resource } from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import { CfnAuthorizer } from '../apigatewayv2.generated'; + +import { IAuthorizer } from '../common'; +import { IHttpApi } from './api'; +import { IHttpRoute } from './route'; + +/** + * Supported Authorizer types + */ +export enum HttpAuthorizerType { + /** JSON Web Tokens */ + JWT = 'JWT', + + /** Lambda Authorizer */ + LAMBDA = 'REQUEST', +} + +/** + * Properties to initialize an instance of `HttpAuthorizer`. + */ +export interface HttpAuthorizerProps { + /** + * Name of the authorizer + * @default - id of the HttpAuthorizer construct. + */ + readonly authorizerName?: string + + /** + * HTTP Api to attach the authorizer to + */ + readonly httpApi: IHttpApi + + /** + * The type of authorizer + */ + readonly type: HttpAuthorizerType; + + /** + * The identity source for which authorization is requested. + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigatewayv2-authorizer.html#cfn-apigatewayv2-authorizer-identitysource + */ + readonly identitySource: string[]; + + /** + * A list of the intended recipients of the JWT. + * A valid JWT must provide an aud that matches at least one entry in this list. + * @default - required for JWT authorizer typess. + */ + readonly jwtAudience?: string[] + + /** + * The base domain of the identity provider that issues JWT. + * @default - required for JWT authorizer types. + */ + readonly jwtIssuer?: string; +} + +/** + * An authorizer for HTTP APIs + */ +export interface IHttpAuthorizer extends IAuthorizer { +} + +/** + * Reference to an http authorizer + */ +export interface HttpAuthorizerAttributes { + /** + * Id of the Authorizer + */ + readonly authorizerId: string + + /** + * Type of authorizer + */ + readonly authorizerType: HttpAuthorizerType +} + +/** + * An authorizer for Http Apis + * @resource AWS::ApiGatewayV2::Authorizer + */ +export class HttpAuthorizer extends Resource implements IHttpAuthorizer { + /** + * Import an existing HTTP Authorizer into this CDK app. + */ + public static fromHttpAuthorizerAttributes(scope: Construct, id: string, attrs: HttpAuthorizerAttributes): IHttpRouteAuthorizer { + class Import extends Resource implements IHttpRouteAuthorizer { + public readonly authorizerId = attrs.authorizerId; + public readonly authorizerType = attrs.authorizerType; + + public bind(): HttpRouteAuthorizerConfig { + return { + authorizerId: attrs.authorizerId, + authorizationType: attrs.authorizerType, + }; + } + } + return new Import(scope, id); + } + + public readonly authorizerId: string; + + constructor(scope: Construct, id: string, props: HttpAuthorizerProps) { + super(scope, id); + + if (props.type === HttpAuthorizerType.JWT && (!props.jwtAudience || props.jwtAudience.length === 0 || !props.jwtIssuer)) { + throw new Error('jwtAudience and jwtIssuer are mandatory for JWT authorizers'); + } + + const resource = new CfnAuthorizer(this, 'Resource', { + name: props.authorizerName ?? id, + apiId: props.httpApi.httpApiId, + authorizerType: props.type, + identitySource: props.identitySource, + jwtConfiguration: undefinedIfNoKeys({ + audience: props.jwtAudience, + issuer: props.jwtIssuer, + }), + }); + + this.authorizerId = resource.ref; + } +} + +/** + * Input to the bind() operation, that binds an authorizer to a route. + */ +export interface HttpRouteAuthorizerBindOptions { + /** + * The route to which the authorizer is being bound. + */ + readonly route: IHttpRoute; + /** + * The scope for any constructs created as part of the bind. + */ + readonly scope: Construct; +} + +/** + * Results of binding an authorizer to an http route. + */ +export interface HttpRouteAuthorizerConfig { + /** + * The authorizer id + */ + readonly authorizerId: string; + /** + * The type of authorization + */ + readonly authorizationType: HttpAuthorizerType; + /** + * The list of OIDC scopes to include in the authorization. + * @default - no authorization scopes + */ + readonly authorizationScopes?: string[]; +} + +/** + * An authorizer that can attach to an Http Route. + */ +export interface IHttpRouteAuthorizer { + /** + * Bind this authorizer to a specified Http route. + */ + bind(options: HttpRouteAuthorizerBindOptions): HttpRouteAuthorizerConfig; +} + +function undefinedIfNoKeys(obj: A): A | undefined { + const allUndefined = Object.values(obj).every(val => val === undefined); + return allUndefined ? undefined : obj; +} diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/index.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/index.ts index c594da33bac91..efd60f9f24d7c 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/index.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/index.ts @@ -4,3 +4,4 @@ export * from './integration'; export * from './stage'; export * from './api-mapping'; export * from './vpc-link'; +export * from './authorizer'; diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts index 27a0d1d7584a0..4510d13ed6f2b 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts @@ -3,6 +3,7 @@ import { Construct } from 'constructs'; import { CfnRoute, CfnRouteProps } from '../apigatewayv2.generated'; import { IRoute } from '../common'; import { IHttpApi } from './api'; +import { IHttpRouteAuthorizer } from './authorizer'; import { IHttpRouteIntegration } from './integration'; /** @@ -103,6 +104,20 @@ export interface HttpRouteProps extends BatchHttpRouteOptions { * The key to this route. This is a combination of an HTTP method and an HTTP path. */ readonly routeKey: HttpRouteKey; + + /** + * Authorizer for a WebSocket API or an HTTP API. + * @default - No authorizer + */ + readonly authorizer?: IHttpRouteAuthorizer; + + /** + * The list of OIDC scopes to include in the authorization. + * + * These scopes will be merged with the scopes from the attached authorizer + * @default - no additional authorization scopes + */ + readonly authorizationScopes?: string[]; } /** @@ -125,12 +140,29 @@ export class HttpRoute extends Resource implements IHttpRoute { scope: this, }); - const integration = props.httpApi._addIntegration(config); + const integration = props.httpApi._addIntegration(this, config); + + const authBindResult = props.authorizer ? props.authorizer.bind({ + route: this, + scope: this.httpApi instanceof Construct ? this.httpApi : this, // scope under the API if it's not imported + }) : undefined; + + let authorizationScopes = authBindResult?.authorizationScopes ?? []; + + if (authBindResult && props.authorizationScopes) { + authorizationScopes = Array.from(new Set([ + ...authorizationScopes, + ...props.authorizationScopes, + ])); + } const routeProps: CfnRouteProps = { apiId: props.httpApi.httpApiId, routeKey: props.routeKey.key, target: `integrations/${integration.integrationId}`, + authorizerId: authBindResult?.authorizerId, + authorizationType: authBindResult?.authorizationType, + authorizationScopes, }; const route = new CfnRoute(this, 'Resource', routeProps); diff --git a/packages/@aws-cdk/aws-apigatewayv2/package.json b/packages/@aws-cdk/aws-apigatewayv2/package.json index 68a56a7acb216..19abb0ca10b3f 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/package.json +++ b/packages/@aws-cdk/aws-apigatewayv2/package.json @@ -126,5 +126,8 @@ ], "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } 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 70bc45000ddec..01252be7d84f1 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts @@ -3,7 +3,10 @@ import { ABSENT } from '@aws-cdk/assert'; import { Metric } from '@aws-cdk/aws-cloudwatch'; import * as ec2 from '@aws-cdk/aws-ec2'; import { Duration, Stack } from '@aws-cdk/core'; -import { HttpApi, HttpIntegrationType, HttpMethod, HttpRouteIntegrationBindOptions, HttpRouteIntegrationConfig, IHttpRouteIntegration, PayloadFormatVersion } from '../../lib'; +import { + HttpApi, HttpAuthorizer, HttpAuthorizerType, HttpIntegrationType, HttpMethod, HttpRouteAuthorizerBindOptions, HttpRouteAuthorizerConfig, + HttpRouteIntegrationBindOptions, HttpRouteIntegrationConfig, IHttpRouteAuthorizer, IHttpRouteIntegration, PayloadFormatVersion, +} from '../../lib'; describe('HttpApi', () => { test('default', () => { @@ -274,6 +277,83 @@ describe('HttpApi', () => { expect(api.apiEndpoint).toBeDefined(); }); + test('can attach authorizer to route', () => { + const stack = new Stack(); + const httpApi = new HttpApi(stack, 'api'); + + const authorizer = new DummyAuthorizer(); + + httpApi.addRoutes({ + path: '/pets', + integration: new DummyRouteIntegration(), + authorizer, + }); + + expect(stack).toHaveResource('AWS::ApiGatewayV2::Api', { + Name: 'api', + ProtocolType: 'HTTP', + }); + + expect(stack).toHaveResource('AWS::ApiGatewayV2::Route', { + AuthorizerId: 'auth-1234', + AuthorizationType: 'JWT', + }); + }); + + test('can import existing authorizer and attach to route', () => { + // GIVEN + const stack = new Stack(); + const api = new HttpApi(stack, 'HttpApi'); + + const authorizer = HttpAuthorizer.fromHttpAuthorizerAttributes(stack, 'auth', { + authorizerId: '12345', + authorizerType: HttpAuthorizerType.JWT, + }); + + // WHEN + api.addRoutes({ + integration: new DummyRouteIntegration(), + path: '/books', + authorizer, + }); + + api.addRoutes({ + integration: new DummyRouteIntegration(), + path: '/pets', + authorizer, + }); + + // THEN + expect(stack).toHaveResource('AWS::ApiGatewayV2::Route', { + AuthorizerId: '12345', + }); + }); + + test('can attach custom scopes to authorizer route', () => { + const stack = new Stack(); + const httpApi = new HttpApi(stack, 'api'); + + const authorizer = new DummyAuthorizer(); + + httpApi.addRoutes({ + path: '/pets', + integration: new DummyRouteIntegration(), + authorizer, + authorizationScopes: ['read:scopes'], + }); + + expect(stack).toHaveResource('AWS::ApiGatewayV2::Api', { + Name: 'api', + ProtocolType: 'HTTP', + }); + + expect(stack).toHaveResource('AWS::ApiGatewayV2::Route', { + AuthorizerId: 'auth-1234', + AuthorizationType: 'JWT', + AuthorizationScopes: ['read:scopes'], + }); + }); + test('throws when accessing apiEndpoint and disableExecuteApiEndpoint is true', () => { const stack = new Stack(); const api = new HttpApi(stack, 'api', { @@ -301,4 +381,13 @@ class DummyRouteIntegration implements IHttpRouteIntegration { uri: 'some-uri', }; } +} + +class DummyAuthorizer implements IHttpRouteAuthorizer { + public bind(_: HttpRouteAuthorizerBindOptions): HttpRouteAuthorizerConfig { + return { + authorizerId: 'auth-1234', + authorizationType: HttpAuthorizerType.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 new file mode 100644 index 0000000000000..bee2f7d05be1b --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/authorizer.test.ts @@ -0,0 +1,67 @@ +import '@aws-cdk/assert/jest'; +import { Stack } from '@aws-cdk/core'; +import { + HttpApi, HttpAuthorizer, HttpAuthorizerType, +} from '../../lib'; + +describe('HttpAuthorizer', () => { + test('default', () => { + const stack = new Stack(); + const httpApi = new HttpApi(stack, 'HttpApi'); + + new HttpAuthorizer(stack, 'HttpAuthorizer', { + httpApi, + identitySource: ['identitysource.1', 'identitysource.2'], + type: HttpAuthorizerType.JWT, + jwtAudience: ['audience.1', 'audience.2'], + jwtIssuer: 'issuer', + }); + + expect(stack).toHaveResource('AWS::ApiGatewayV2::Authorizer', { + ApiId: stack.resolve(httpApi.httpApiId), + Name: 'HttpAuthorizer', + AuthorizerType: 'JWT', + IdentitySource: ['identitysource.1', 'identitysource.2'], + }); + }); + + test('authorizer name', () => { + const stack = new Stack(); + const httpApi = new HttpApi(stack, 'HttpApi'); + + new HttpAuthorizer(stack, 'HttpAuthorizer', { + httpApi, + authorizerName: 'my-authorizer', + identitySource: ['identitysource.1', 'identitysource.2'], + type: HttpAuthorizerType.JWT, + jwtAudience: ['audience.1', 'audience.2'], + jwtIssuer: 'issuer', + }); + + expect(stack).toHaveResource('AWS::ApiGatewayV2::Authorizer', { + Name: 'my-authorizer', + }); + }); + + describe('jwt configuration', () => { + test('audience and issuer', () => { + const stack = new Stack(); + const httpApi = new HttpApi(stack, 'HttpApi'); + + new HttpAuthorizer(stack, 'HttpAuthorizer', { + httpApi, + identitySource: ['identitysource.1', 'identitysource.2'], + type: HttpAuthorizerType.JWT, + jwtAudience: ['audience.1', 'audience.2'], + jwtIssuer: 'issuer', + }); + + expect(stack).toHaveResource('AWS::ApiGatewayV2::Authorizer', { + JwtConfiguration: { + Audience: ['audience.1', 'audience.2'], + Issuer: 'issuer', + }, + }); + }); + }); +}); 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 9ca235814cd37..a5ce1b7b64b64 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts @@ -1,8 +1,8 @@ import '@aws-cdk/assert/jest'; -import { Stack } from '@aws-cdk/core'; +import { Stack, App } from '@aws-cdk/core'; import { - HttpApi, HttpConnectionType, HttpIntegrationType, HttpMethod, HttpRoute, HttpRouteIntegrationConfig, HttpRouteKey, IHttpRouteIntegration, - PayloadFormatVersion, + HttpApi, HttpAuthorizer, HttpAuthorizerType, HttpConnectionType, HttpIntegrationType, HttpMethod, HttpRoute, HttpRouteAuthorizerBindOptions, + HttpRouteAuthorizerConfig, HttpRouteIntegrationConfig, HttpRouteKey, IHttpRouteAuthorizer, IHttpRouteIntegration, PayloadFormatVersion, } from '../../lib'; describe('HttpRoute', () => { @@ -25,7 +25,7 @@ describe('HttpRoute', () => { [ 'integrations/', { - Ref: 'HttpApiHttpIntegrationcff2618c192d3bd8581dd2a4093464f6CDB667B8', + Ref: 'HttpRouteHttpIntegrationcff2618c192d3bd8581dd2a4093464f6FB1097D0', }, ], ], @@ -115,6 +115,27 @@ describe('HttpRoute', () => { expect(stack2).toCountResources('AWS::ApiGatewayV2::Integration', 1); }); + test('route defined in a separate stack does not create cycles', () => { + // GIVEN + const integration = new DummyIntegration(); + + // WHEN + const app = new App(); + const stack1 = new Stack(app, 'ApiStack'); + const httpApi = new HttpApi(stack1, 'HttpApi'); + + const stack2 = new Stack(app, 'RouteStack'); + new HttpRoute(stack2, 'HttpRoute1', { + httpApi, + integration, + routeKey: HttpRouteKey.with('/books', HttpMethod.GET), + }); + + // THEN + expect(stack1).toCountResources('AWS::ApiGatewayV2::Integration', 0); + expect(stack2).toCountResources('AWS::ApiGatewayV2::Integration', 1); + }); + test('throws when path not start with /', () => { const stack = new Stack(); const httpApi = new HttpApi(stack, 'HttpApi'); @@ -173,6 +194,53 @@ describe('HttpRoute', () => { }); expect(stack).not.toHaveResource('AWS::ApiGatewayV2::VpcLink'); }); + + test('can create route with an authorizer attached', () => { + const stack = new Stack(); + const httpApi = new HttpApi(stack, 'HttpApi'); + + const authorizer = new DummyAuthorizer(); + + const route = new HttpRoute(stack, 'HttpRoute', { + httpApi, + integration: new DummyIntegration(), + routeKey: HttpRouteKey.with('/books', HttpMethod.GET), + authorizer, + }); + + expect(stack).toHaveResource('AWS::ApiGatewayV2::Integration', { + ApiId: stack.resolve(httpApi.httpApiId), + IntegrationType: 'HTTP_PROXY', + PayloadFormatVersion: '2.0', + IntegrationUri: 'some-uri', + }); + + expect(stack).toHaveResource('AWS::ApiGatewayV2::Authorizer'); + + expect(stack).toHaveResource('AWS::ApiGatewayV2::Route', { + AuthorizerId: stack.resolve(authorizer.bind({ scope: stack, route: route }).authorizerId), + AuthorizationType: 'JWT', + }); + }); + + test('can attach additional scopes to a route with an authorizer attached', () => { + const stack = new Stack(); + const httpApi = new HttpApi(stack, 'HttpApi'); + + const authorizer = new DummyAuthorizer(); + + new HttpRoute(stack, 'HttpRoute', { + httpApi, + integration: new DummyIntegration(), + routeKey: HttpRouteKey.with('/books', HttpMethod.GET), + authorizer, + authorizationScopes: ['read:books'], + }); + + expect(stack).toHaveResource('AWS::ApiGatewayV2::Route', { + AuthorizationScopes: ['read:books'], + }); + }); }); class DummyIntegration implements IHttpRouteIntegration { @@ -184,4 +252,26 @@ class DummyIntegration implements IHttpRouteIntegration { method: HttpMethod.DELETE, }; } -} \ No newline at end of file +} + +class DummyAuthorizer 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: HttpAuthorizerType.JWT, + }; + } +} diff --git a/packages/@aws-cdk/aws-appconfig/package.json b/packages/@aws-cdk/aws-appconfig/package.json index c009b9ea40639..0836a936f672c 100644 --- a/packages/@aws-cdk/aws-appconfig/package.json +++ b/packages/@aws-cdk/aws-appconfig/package.json @@ -93,5 +93,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-appflow/package.json b/packages/@aws-cdk/aws-appflow/package.json index f1cbc900a0d13..6d047b697b394 100644 --- a/packages/@aws-cdk/aws-appflow/package.json +++ b/packages/@aws-cdk/aws-appflow/package.json @@ -88,5 +88,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-applicationautoscaling/package.json b/packages/@aws-cdk/aws-applicationautoscaling/package.json index 86d5f3f272bf2..71036cd220e5f 100644 --- a/packages/@aws-cdk/aws-applicationautoscaling/package.json +++ b/packages/@aws-cdk/aws-applicationautoscaling/package.json @@ -74,7 +74,7 @@ "@types/nodeunit": "^0.0.31", "cdk-build-tools": "0.0.0", "cfn2ts": "0.0.0", - "fast-check": "^2.11.0", + "fast-check": "^2.13.0", "nodeunit": "^0.11.3", "pkglint": "0.0.0" }, @@ -131,5 +131,8 @@ "awscdkio": { "announce": false }, - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-applicationinsights/package.json b/packages/@aws-cdk/aws-applicationinsights/package.json index 67037d3b3524c..edef6de6de7f0 100644 --- a/packages/@aws-cdk/aws-applicationinsights/package.json +++ b/packages/@aws-cdk/aws-applicationinsights/package.json @@ -91,5 +91,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-appmesh/package.json b/packages/@aws-cdk/aws-appmesh/package.json index cf21301f13e5c..32176758bd410 100644 --- a/packages/@aws-cdk/aws-appmesh/package.json +++ b/packages/@aws-cdk/aws-appmesh/package.json @@ -190,5 +190,8 @@ "maturity": "developer-preview", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-appstream/package.json b/packages/@aws-cdk/aws-appstream/package.json index 6d2011bf3bfe1..e9655519ab834 100644 --- a/packages/@aws-cdk/aws-appstream/package.json +++ b/packages/@aws-cdk/aws-appstream/package.json @@ -92,5 +92,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-appsync/package.json b/packages/@aws-cdk/aws-appsync/package.json index 5e14842c6f758..f6bfb64813364 100644 --- a/packages/@aws-cdk/aws-appsync/package.json +++ b/packages/@aws-cdk/aws-appsync/package.json @@ -120,5 +120,8 @@ "maturity": "experimental", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-athena/package.json b/packages/@aws-cdk/aws-athena/package.json index 28960103e97cc..2a3cd7207b97f 100644 --- a/packages/@aws-cdk/aws-athena/package.json +++ b/packages/@aws-cdk/aws-athena/package.json @@ -94,5 +94,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-auditmanager/package.json b/packages/@aws-cdk/aws-auditmanager/package.json index b9446f72238c8..b263be5fd8c92 100644 --- a/packages/@aws-cdk/aws-auditmanager/package.json +++ b/packages/@aws-cdk/aws-auditmanager/package.json @@ -91,5 +91,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-autoscaling-common/package.json b/packages/@aws-cdk/aws-autoscaling-common/package.json index 49bb106bd02cb..644801e0dbc71 100644 --- a/packages/@aws-cdk/aws-autoscaling-common/package.json +++ b/packages/@aws-cdk/aws-autoscaling-common/package.json @@ -66,7 +66,7 @@ "@types/nodeunit": "^0.0.31", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", - "fast-check": "^2.11.0", + "fast-check": "^2.13.0", "nodeunit": "^0.11.3", "pkglint": "0.0.0" }, @@ -114,5 +114,8 @@ "env": { "AWSLINT_BASE_CONSTRUCT": true } + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-autoscaling-hooktargets/package.json b/packages/@aws-cdk/aws-autoscaling-hooktargets/package.json index 0b40faf0fabec..b15364996c1f3 100644 --- a/packages/@aws-cdk/aws-autoscaling-hooktargets/package.json +++ b/packages/@aws-cdk/aws-autoscaling-hooktargets/package.json @@ -106,5 +106,8 @@ "env": { "AWSLINT_BASE_CONSTRUCT": true } + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-autoscaling/package.json b/packages/@aws-cdk/aws-autoscaling/package.json index cbb4534533ade..79a8b3e02b967 100644 --- a/packages/@aws-cdk/aws-autoscaling/package.json +++ b/packages/@aws-cdk/aws-autoscaling/package.json @@ -152,5 +152,8 @@ "awscdkio": { "announce": false }, - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-autoscalingplans/package.json b/packages/@aws-cdk/aws-autoscalingplans/package.json index 186564ad5e31c..5674f715e2b80 100644 --- a/packages/@aws-cdk/aws-autoscalingplans/package.json +++ b/packages/@aws-cdk/aws-autoscalingplans/package.json @@ -92,5 +92,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-backup/package.json b/packages/@aws-cdk/aws-backup/package.json index 8f70998f41a1d..841f29f929f36 100644 --- a/packages/@aws-cdk/aws-backup/package.json +++ b/packages/@aws-cdk/aws-backup/package.json @@ -110,5 +110,8 @@ "maturity": "experimental", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-batch/package.json b/packages/@aws-cdk/aws-batch/package.json index f20ee3b682f21..0b8f4666ab077 100644 --- a/packages/@aws-cdk/aws-batch/package.json +++ b/packages/@aws-cdk/aws-batch/package.json @@ -106,5 +106,8 @@ "maturity": "experimental", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-batch/test/job-definition.test.ts b/packages/@aws-cdk/aws-batch/test/job-definition.test.ts index d7d1b6fbf11b7..1658be4c8e22e 100644 --- a/packages/@aws-cdk/aws-batch/test/job-definition.test.ts +++ b/packages/@aws-cdk/aws-batch/test/job-definition.test.ts @@ -222,7 +222,7 @@ describe('Batch Job Definition', () => { // THEN expect(importedJob.jobDefinitionName).toEqual('job-def-name'); expect(importedJob.jobDefinitionArn) - .toEqual('arn:${Token[AWS.Partition.3]}:batch:${Token[AWS.Region.4]}:${Token[AWS.AccountId.0]}:job-definition/job-def-name'); + .toEqual(`arn:${cdk.Aws.PARTITION}:batch:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:job-definition/job-def-name`); }); test('can configure log configuration secrets properly', () => { diff --git a/packages/@aws-cdk/aws-budgets/package.json b/packages/@aws-cdk/aws-budgets/package.json index 924d2ec1ab5b8..3d8297269395f 100644 --- a/packages/@aws-cdk/aws-budgets/package.json +++ b/packages/@aws-cdk/aws-budgets/package.json @@ -92,5 +92,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-cassandra/package.json b/packages/@aws-cdk/aws-cassandra/package.json index 67abfdf545526..5b0a317a627d8 100644 --- a/packages/@aws-cdk/aws-cassandra/package.json +++ b/packages/@aws-cdk/aws-cassandra/package.json @@ -91,5 +91,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-ce/package.json b/packages/@aws-cdk/aws-ce/package.json index cb5b60b4242a7..07c5e186da907 100644 --- a/packages/@aws-cdk/aws-ce/package.json +++ b/packages/@aws-cdk/aws-ce/package.json @@ -91,5 +91,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } 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 85da591914c1d..1e220d75e96a9 100644 --- a/packages/@aws-cdk/aws-certificatemanager/lambda-packages/dns_validated_certificate_handler/package.json +++ b/packages/@aws-cdk/aws-certificatemanager/lambda-packages/dns_validated_certificate_handler/package.json @@ -29,15 +29,15 @@ "devDependencies": { "aws-sdk": "^2.596.0", "aws-sdk-mock": "^5.1.0", - "eslint": "^7.13.0", + "eslint": "^7.20.0", "eslint-config-standard": "^14.1.1", "eslint-plugin-import": "^2.22.1", "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^4.2.1", + "eslint-plugin-promise": "^4.3.1", "eslint-plugin-standard": "^4.1.0", "jest": "^26.6.3", "lambda-tester": "^3.6.0", - "nock": "^13.0.5", - "ts-jest": "^26.4.4" + "nock": "^13.0.7", + "ts-jest": "^26.5.1" } } diff --git a/packages/@aws-cdk/aws-certificatemanager/package.json b/packages/@aws-cdk/aws-certificatemanager/package.json index 52afcdf0d0e83..96e0787fb06ec 100644 --- a/packages/@aws-cdk/aws-certificatemanager/package.json +++ b/packages/@aws-cdk/aws-certificatemanager/package.json @@ -104,5 +104,8 @@ "awscdkio": { "announce": false }, - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-certificatemanager/test/util.test.ts b/packages/@aws-cdk/aws-certificatemanager/test/util.test.ts index 3fc833eb1d79e..2b14771c3a517 100644 --- a/packages/@aws-cdk/aws-certificatemanager/test/util.test.ts +++ b/packages/@aws-cdk/aws-certificatemanager/test/util.test.ts @@ -1,6 +1,6 @@ import '@aws-cdk/assert/jest'; import { PublicHostedZone } from '@aws-cdk/aws-route53'; -import { App, Stack } from '@aws-cdk/core'; +import { App, Aws, Stack } from '@aws-cdk/core'; import { Certificate, DnsValidatedCertificate } from '../lib'; import { apexDomain, getCertificateRegion, isDnsValidatedCertificate } from '../lib/util'; @@ -99,7 +99,7 @@ describe('getCertificateRegion', () => { domainName: 'www.example.com', }); - expect(getCertificateRegion(certificate)).toEqual('${Token[AWS.Region.4]}'); + expect(getCertificateRegion(certificate)).toEqual(Aws.REGION); }); }); diff --git a/packages/@aws-cdk/aws-chatbot/package.json b/packages/@aws-cdk/aws-chatbot/package.json index 8368c18a90199..ab267bf8f1c0c 100644 --- a/packages/@aws-cdk/aws-chatbot/package.json +++ b/packages/@aws-cdk/aws-chatbot/package.json @@ -102,5 +102,8 @@ "maturity": "experimental", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-cloud9/package.json b/packages/@aws-cdk/aws-cloud9/package.json index 9a126f4afc5f6..e71b8c60f3bc0 100644 --- a/packages/@aws-cdk/aws-cloud9/package.json +++ b/packages/@aws-cdk/aws-cloud9/package.json @@ -107,5 +107,8 @@ "maturity": "experimental", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-cloudformation/package.json b/packages/@aws-cdk/aws-cloudformation/package.json index a2afd8f325bd6..550bc780b750d 100644 --- a/packages/@aws-cdk/aws-cloudformation/package.json +++ b/packages/@aws-cdk/aws-cloudformation/package.json @@ -73,7 +73,7 @@ "@aws-cdk/aws-sns-subscriptions": "0.0.0", "@aws-cdk/aws-sqs": "0.0.0", "@aws-cdk/aws-ssm": "0.0.0", - "@types/aws-lambda": "^8.10.64", + "@types/aws-lambda": "^8.10.72", "@types/nodeunit": "^0.0.31", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", @@ -115,5 +115,8 @@ "awscdkio": { "announce": false }, - "maturity": "deprecated" + "maturity": "deprecated", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-cloudfront-origins/lib/s3-origin.ts b/packages/@aws-cdk/aws-cloudfront-origins/lib/s3-origin.ts index 518e3e728ec1a..a0ac4037d9741 100644 --- a/packages/@aws-cdk/aws-cloudfront-origins/lib/s3-origin.ts +++ b/packages/@aws-cdk/aws-cloudfront-origins/lib/s3-origin.ts @@ -1,4 +1,5 @@ import * as cloudfront from '@aws-cdk/aws-cloudfront'; +import * as iam from '@aws-cdk/aws-iam'; import * as s3 from '@aws-cdk/aws-s3'; import * as cdk from '@aws-cdk/core'; import { HttpOrigin } from './http-origin'; @@ -68,7 +69,7 @@ class S3BucketOrigin extends cloudfront.OriginBase { public bind(scope: Construct, options: cloudfront.OriginBindOptions): cloudfront.OriginBindConfig { if (!this.originAccessIdentity) { // Using a bucket from another stack creates a cyclic reference with - // the bucket taking a dependency on the generated S3CanonicalUserId when `grantRead` is called, + // the bucket taking a dependency on the generated S3CanonicalUserId for the grant principal, // and the distribution having a dependency on the bucket's domain name. // Fix this by parenting the OAI in the bucket's stack when cross-stack usage is detected. const bucketStack = cdk.Stack.of(this.bucket); @@ -79,7 +80,16 @@ class S3BucketOrigin extends cloudfront.OriginBase { this.originAccessIdentity = new cloudfront.OriginAccessIdentity(oaiScope, oaiId, { comment: `Identity for ${options.originId}`, }); - this.bucket.grantRead(this.originAccessIdentity); + + // Used rather than `grantRead` because `grantRead` will grant overly-permissive policies. + // Only GetObject is needed to retrieve objects for the distribution. + // This also excludes KMS permissions; currently, OAI only supports SSE-S3 for buckets. + // Source: https://aws.amazon.com/blogs/networking-and-content-delivery/serving-sse-kms-encrypted-content-from-s3-using-cloudfront/ + this.bucket.addToResourcePolicy(new iam.PolicyStatement({ + resources: [this.bucket.arnForObjects('*')], + actions: ['s3:GetObject'], + principals: [this.originAccessIdentity.grantPrincipal], + })); } return super.bind(scope, options); } diff --git a/packages/@aws-cdk/aws-cloudfront-origins/package.json b/packages/@aws-cdk/aws-cloudfront-origins/package.json index c6c5e867e0471..7ea4b267dbf32 100644 --- a/packages/@aws-cdk/aws-cloudfront-origins/package.json +++ b/packages/@aws-cdk/aws-cloudfront-origins/package.json @@ -71,7 +71,7 @@ "devDependencies": { "@aws-cdk/assert": "0.0.0", "@aws-cdk/aws-ec2": "0.0.0", - "aws-sdk": "^2.830.0", + "aws-sdk": "^2.845.0", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "pkglint": "0.0.0" @@ -79,6 +79,7 @@ "dependencies": { "@aws-cdk/aws-cloudfront": "0.0.0", "@aws-cdk/aws-elasticloadbalancingv2": "0.0.0", + "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-s3": "0.0.0", "@aws-cdk/core": "0.0.0", "constructs": "^3.2.0" @@ -88,8 +89,9 @@ "@aws-cdk/core": "0.0.0", "constructs": "^3.2.0", "@aws-cdk/aws-cloudfront": "0.0.0", - "@aws-cdk/aws-s3": "0.0.0", - "@aws-cdk/aws-elasticloadbalancingv2": "0.0.0" + "@aws-cdk/aws-elasticloadbalancingv2": "0.0.0", + "@aws-cdk/aws-iam": "0.0.0", + "@aws-cdk/aws-s3": "0.0.0" }, "engines": { "node": ">= 10.13.0 <13 || >=13.7.0" @@ -98,5 +100,8 @@ "maturity": "stable", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-cloudfront-origins/test/integ.origin-group.expected.json b/packages/@aws-cdk/aws-cloudfront-origins/test/integ.origin-group.expected.json index 8e123f87338d4..ecd2096329404 100644 --- a/packages/@aws-cdk/aws-cloudfront-origins/test/integ.origin-group.expected.json +++ b/packages/@aws-cdk/aws-cloudfront-origins/test/integ.origin-group.expected.json @@ -14,11 +14,7 @@ "PolicyDocument": { "Statement": [ { - "Action": [ - "s3:GetObject*", - "s3:GetBucket*", - "s3:List*" - ], + "Action": "s3:GetObject", "Effect": "Allow", "Principal": { "CanonicalUser": { @@ -28,28 +24,20 @@ ] } }, - "Resource": [ - { - "Fn::GetAtt": [ - "Bucket83908E77", - "Arn" - ] - }, - { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "Bucket83908E77", - "Arn" - ] - }, - "/*" - ] + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "Bucket83908E77", + "Arn" + ] + }, + "/*" ] - } - ] + ] + } } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/aws-cloudfront-origins/test/integ.s3-origin.expected.json b/packages/@aws-cdk/aws-cloudfront-origins/test/integ.s3-origin.expected.json index 5eb310b5b92d7..25461aa9cf7de 100644 --- a/packages/@aws-cdk/aws-cloudfront-origins/test/integ.s3-origin.expected.json +++ b/packages/@aws-cdk/aws-cloudfront-origins/test/integ.s3-origin.expected.json @@ -14,11 +14,7 @@ "PolicyDocument": { "Statement": [ { - "Action": [ - "s3:GetObject*", - "s3:GetBucket*", - "s3:List*" - ], + "Action": "s3:GetObject", "Effect": "Allow", "Principal": { "CanonicalUser": { @@ -28,28 +24,20 @@ ] } }, - "Resource": [ - { - "Fn::GetAtt": [ - "Bucket83908E77", - "Arn" - ] - }, - { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "Bucket83908E77", - "Arn" - ] - }, - "/*" - ] + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "Bucket83908E77", + "Arn" + ] + }, + "/*" ] - } - ] + ] + } } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/aws-cloudfront-origins/test/s3-origin.test.ts b/packages/@aws-cdk/aws-cloudfront-origins/test/s3-origin.test.ts index 54f9861685945..a502dd5cf4f46 100644 --- a/packages/@aws-cdk/aws-cloudfront-origins/test/s3-origin.test.ts +++ b/packages/@aws-cdk/aws-cloudfront-origins/test/s3-origin.test.ts @@ -87,9 +87,13 @@ describe('With bucket', () => { expect(stack).toHaveResourceLike('AWS::S3::BucketPolicy', { PolicyDocument: { Statement: [{ + Action: 's3:GetObject', Principal: { CanonicalUser: { 'Fn::GetAtt': ['DistOrigin1S3Origin87D64058', 'S3CanonicalUserId'] }, }, + Resource: { + 'Fn::Join': ['', [{ 'Fn::GetAtt': ['Bucket83908E77', 'Arn'] }, '/*']], + }, }], }, }); diff --git a/packages/@aws-cdk/aws-cloudfront/README.md b/packages/@aws-cdk/aws-cloudfront/README.md index 3a5834d98d51f..34b0922913c4c 100644 --- a/packages/@aws-cdk/aws-cloudfront/README.md +++ b/packages/@aws-cdk/aws-cloudfront/README.md @@ -401,10 +401,10 @@ new cloudfront.Distribution(this, 'myDist', { // You can optionally log to a specific bucket, configure whether cookies are logged, and give the log files a prefix. new cloudfront.Distribution(this, 'myDist', { defaultBehavior: { origin: new origins.HttpOrigin('www.example.com') }, - enableLogging: true, // Optional, this is implied if loggingBucket is specified - loggingBucket: new s3.Bucket(this, 'LoggingBucket'), - loggingFilePrefix: 'distribution-access-logs/', - loggingIncludesCookies: true, + enableLogging: true, // Optional, this is implied if logBucket is specified + logBucket: new s3.Bucket(this, 'LogBucket'), + logFilePrefix: 'distribution-access-logs/', + logIncludesCookies: true, }); ``` diff --git a/packages/@aws-cdk/aws-cloudfront/lib/experimental/edge-function.ts b/packages/@aws-cdk/aws-cloudfront/lib/experimental/edge-function.ts index f97b7e176d874..33b0e070d144e 100644 --- a/packages/@aws-cdk/aws-cloudfront/lib/experimental/edge-function.ts +++ b/packages/@aws-cdk/aws-cloudfront/lib/experimental/edge-function.ts @@ -218,7 +218,10 @@ export class EdgeFunction extends Resource implements lambda.IVersion { let edgeStack = stage.node.tryFindChild(edgeStackId) as Stack; if (!edgeStack) { edgeStack = new Stack(stage, edgeStackId, { - env: { region: EdgeFunction.EDGE_REGION }, + env: { + region: EdgeFunction.EDGE_REGION, + account: Stack.of(this).account, + }, }); } this.stack.addDependency(edgeStack); diff --git a/packages/@aws-cdk/aws-cloudfront/lib/web-distribution.ts b/packages/@aws-cdk/aws-cloudfront/lib/web-distribution.ts index f191813dada2b..a678c82ba6087 100644 --- a/packages/@aws-cdk/aws-cloudfront/lib/web-distribution.ts +++ b/packages/@aws-cdk/aws-cloudfront/lib/web-distribution.ts @@ -1027,9 +1027,15 @@ export class CloudFrontWebDistribution extends cdk.Resource implements IDistribu // first case for backwards compatibility if (originConfig.s3OriginSource.originAccessIdentity) { // grant CloudFront OriginAccessIdentity read access to S3 bucket - originConfig.s3OriginSource.s3BucketSource.grantRead( - originConfig.s3OriginSource.originAccessIdentity, - ); + // Used rather than `grantRead` because `grantRead` will grant overly-permissive policies. + // Only GetObject is needed to retrieve objects for the distribution. + // This also excludes KMS permissions; currently, OAI only supports SSE-S3 for buckets. + // Source: https://aws.amazon.com/blogs/networking-and-content-delivery/serving-sse-kms-encrypted-content-from-s3-using-cloudfront/ + originConfig.s3OriginSource.s3BucketSource.addToResourcePolicy(new iam.PolicyStatement({ + resources: [originConfig.s3OriginSource.s3BucketSource.arnForObjects('*')], + actions: ['s3:GetObject'], + principals: [originConfig.s3OriginSource.originAccessIdentity.grantPrincipal], + })); s3OriginConfig = { originAccessIdentity: `origin-access-identity/cloudfront/${originConfig.s3OriginSource.originAccessIdentity.originAccessIdentityName}`, diff --git a/packages/@aws-cdk/aws-cloudfront/package.json b/packages/@aws-cdk/aws-cloudfront/package.json index c781a065bbc0a..e2faa8f8e12cf 100644 --- a/packages/@aws-cdk/aws-cloudfront/package.json +++ b/packages/@aws-cdk/aws-cloudfront/package.json @@ -72,7 +72,7 @@ "license": "Apache-2.0", "devDependencies": { "@aws-cdk/assert": "0.0.0", - "aws-sdk": "^2.830.0", + "aws-sdk": "^2.845.0", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "cfn2ts": "0.0.0", @@ -160,5 +160,8 @@ }, "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-cloudfront/test/experimental/edge-function.test.ts b/packages/@aws-cdk/aws-cloudfront/test/experimental/edge-function.test.ts index 4154f79cabff2..7a743056dfcb7 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/experimental/edge-function.test.ts +++ b/packages/@aws-cdk/aws-cloudfront/test/experimental/edge-function.test.ts @@ -102,6 +102,25 @@ describe('stacks', () => { }); }); + test('us-east-1 stack inherits account of parent stack', () => { + new cloudfront.experimental.EdgeFunction(stack, 'MyFn', defaultEdgeFunctionProps()); + + const fnStack = getFnStack(); + + expect(fnStack.account).toEqual('111111111111'); + }); + + test('us-east-1 stack inherits account of parent stack, when parent stack account is undefined', () => { + stack = new cdk.Stack(app, 'StackWithDefaultAccount', { + env: { region: 'testregion' }, + }); + new cloudfront.experimental.EdgeFunction(stack, 'MyFn', defaultEdgeFunctionProps()); + + const fnStack = getFnStack(); + + expect(fnStack.account).toEqual(cdk.Aws.ACCOUNT_ID); + }); + test('creates minimal constructs if scope region is us-east-1', () => { app = new cdk.App(); stack = new cdk.Stack(app, 'Stack', { diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-s3.expected.json b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-s3.expected.json index 8f6357e72272e..72ed767994cc0 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-s3.expected.json +++ b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-s3.expected.json @@ -14,11 +14,7 @@ "PolicyDocument": { "Statement": [ { - "Action": [ - "s3:GetObject*", - "s3:GetBucket*", - "s3:List*" - ], + "Action": "s3:GetObject", "Effect": "Allow", "Principal": { "AWS": { @@ -37,28 +33,20 @@ ] } }, - "Resource": [ - { - "Fn::GetAtt": [ - "Bucket83908E77", - "Arn" + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "Bucket83908E77", + "Arn" + ] + }, + "/*" ] - }, - { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "Bucket83908E77", - "Arn" - ] - }, - "/*" - ] - ] - } - ] + ] + } } ], "Version": "2012-10-17" 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 8eea2cad5bc92..d24c7aa1220dc 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/web-distribution.test.ts +++ b/packages/@aws-cdk/aws-cloudfront/test/web-distribution.test.ts @@ -10,6 +10,7 @@ import { GeoRestriction, KeyGroup, LambdaEdgeEventType, + OriginAccessIdentity, PublicKey, SecurityPolicyProtocol, SSLMethod, @@ -197,6 +198,59 @@ nodeunitShim({ test.done(); }, + 'distribution with bucket and OAI'(test: Test) { + const stack = new cdk.Stack(); + const s3BucketSource = new s3.Bucket(stack, 'Bucket'); + const originAccessIdentity = new OriginAccessIdentity(stack, 'OAI'); + + new CloudFrontWebDistribution(stack, 'AnAmazingWebsiteProbably', { + originConfigs: [{ + s3OriginSource: { s3BucketSource, originAccessIdentity }, + behaviors: [{ isDefaultBehavior: true }], + }], + }); + + expect(stack).to(haveResourceLike('AWS::CloudFront::Distribution', { + DistributionConfig: { + Origins: [ + { + ConnectionAttempts: 3, + ConnectionTimeout: 10, + DomainName: { + 'Fn::GetAtt': [ + 'Bucket83908E77', + 'RegionalDomainName', + ], + }, + Id: 'origin1', + S3OriginConfig: { + OriginAccessIdentity: { + 'Fn::Join': ['', ['origin-access-identity/cloudfront/', { Ref: 'OAIE1EFC67F' }]], + }, + }, + }, + ], + }, + })); + + expect(stack).to(haveResourceLike('AWS::S3::BucketPolicy', { + PolicyDocument: { + Statement: [{ + Action: 's3:GetObject', + Principal: { + CanonicalUser: { 'Fn::GetAtt': ['OAIE1EFC67F', 'S3CanonicalUserId'] }, + }, + Resource: { + 'Fn::Join': ['', [{ 'Fn::GetAtt': ['Bucket83908E77', 'Arn'] }, '/*']], + }, + }], + }, + })); + + test.done(); + }, + + 'distribution with trusted signers on default distribution'(test: Test) { const stack = new cdk.Stack(); const sourceBucket = new s3.Bucket(stack, 'Bucket'); diff --git a/packages/@aws-cdk/aws-cloudtrail/package.json b/packages/@aws-cdk/aws-cloudtrail/package.json index 2dd9a2fbc7971..a3e7573dfe731 100644 --- a/packages/@aws-cdk/aws-cloudtrail/package.json +++ b/packages/@aws-cdk/aws-cloudtrail/package.json @@ -72,7 +72,7 @@ "license": "Apache-2.0", "devDependencies": { "@aws-cdk/assert": "0.0.0", - "aws-sdk": "^2.830.0", + "aws-sdk": "^2.845.0", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "cfn2ts": "0.0.0", @@ -115,5 +115,8 @@ "maturity": "stable", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-cloudwatch-actions/package.json b/packages/@aws-cdk/aws-cloudwatch-actions/package.json index 72d8c10917954..04ed6918384c1 100644 --- a/packages/@aws-cdk/aws-cloudwatch-actions/package.json +++ b/packages/@aws-cdk/aws-cloudwatch-actions/package.json @@ -102,5 +102,8 @@ "env": { "AWSLINT_BASE_CONSTRUCT": true } + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-cloudwatch/package.json b/packages/@aws-cdk/aws-cloudwatch/package.json index 147793d66e74b..c974f8fe0199a 100644 --- a/packages/@aws-cdk/aws-cloudwatch/package.json +++ b/packages/@aws-cdk/aws-cloudwatch/package.json @@ -118,5 +118,8 @@ "awscdkio": { "announce": false }, - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-codeartifact/package.json b/packages/@aws-cdk/aws-codeartifact/package.json index 8181c14bca256..e4b8ef3ffaad6 100644 --- a/packages/@aws-cdk/aws-codeartifact/package.json +++ b/packages/@aws-cdk/aws-codeartifact/package.json @@ -91,5 +91,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-codebuild/package.json b/packages/@aws-cdk/aws-codebuild/package.json index 31985a89b2f3c..cddc43d715ba0 100644 --- a/packages/@aws-cdk/aws-codebuild/package.json +++ b/packages/@aws-cdk/aws-codebuild/package.json @@ -78,7 +78,7 @@ "@aws-cdk/aws-sns": "0.0.0", "@aws-cdk/aws-sqs": "0.0.0", "@types/nodeunit": "^0.0.31", - "aws-sdk": "^2.830.0", + "aws-sdk": "^2.845.0", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "cfn2ts": "0.0.0", @@ -224,5 +224,8 @@ "awscdkio": { "announce": false }, - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-codecommit/lib/repository.ts b/packages/@aws-cdk/aws-codecommit/lib/repository.ts index c1bd88ff6d4f6..495be2f981d86 100644 --- a/packages/@aws-cdk/aws-codecommit/lib/repository.ts +++ b/packages/@aws-cdk/aws-codecommit/lib/repository.ts @@ -310,7 +310,10 @@ export class Repository extends RepositoryBase { public readonly repositoryCloneUrlGrc = makeCloneUrl(stack, repositoryName, 'grc', region); } - return new Import(scope, id); + return new Import(scope, id, { + account: arn.account, + region, + }); } public static fromRepositoryName(scope: Construct, id: string, repositoryName: string): IRepository { diff --git a/packages/@aws-cdk/aws-codecommit/package.json b/packages/@aws-cdk/aws-codecommit/package.json index 6f617c91a0183..36d6a84f8ad2e 100644 --- a/packages/@aws-cdk/aws-codecommit/package.json +++ b/packages/@aws-cdk/aws-codecommit/package.json @@ -78,7 +78,7 @@ "@aws-cdk/assert": "0.0.0", "@aws-cdk/aws-sns": "0.0.0", "@types/nodeunit": "^0.0.31", - "aws-sdk": "^2.830.0", + "aws-sdk": "^2.845.0", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "cfn2ts": "0.0.0", @@ -121,5 +121,8 @@ "awscdkio": { "announce": false }, - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-codecommit/test/test.codecommit.ts b/packages/@aws-cdk/aws-codecommit/test/test.codecommit.ts index 954c48561758a..1f49f852b1e57 100644 --- a/packages/@aws-cdk/aws-codecommit/test/test.codecommit.ts +++ b/packages/@aws-cdk/aws-codecommit/test/test.codecommit.ts @@ -104,6 +104,9 @@ export = { test.deepEqual(stack.resolve(repo.repositoryCloneUrlGrc), 'codecommit::us-west-2://my-repo'); + test.deepEqual(repo.env.account, '585695036304'); + test.deepEqual(repo.env.region, 'us-west-2'); + test.done(); }, diff --git a/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-group.ts b/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-group.ts index e1108acb5205d..32a3ccb3e40d1 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-group.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-group.ts @@ -348,15 +348,20 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupBase { switch (asg.osType) { case ec2.OperatingSystemType.LINUX: asg.addUserData( + 'set +e', // make sure we don't exit on the `which` failing 'PKG_CMD=`which yum 2>/dev/null`', + 'set -e', // continue with failing on error 'if [ -z "$PKG_CMD" ]; then', 'PKG_CMD=apt-get', 'else', 'PKG=CMD=yum', 'fi', '$PKG_CMD update -y', + 'set +e', // make sure we don't exit on the next command failing (we check its exit code below) '$PKG_CMD install -y ruby2.0', - 'if [ $? -ne 0 ]; then', + 'RUBY2_INSTALL=$?', + 'set -e', // continue with failing on error + 'if [ $RUBY2_INSTALL -ne 0 ]; then', '$PKG_CMD install -y ruby', 'fi', '$PKG_CMD install -y awscli', diff --git a/packages/@aws-cdk/aws-codedeploy/package.json b/packages/@aws-cdk/aws-codedeploy/package.json index 6ccc7bcf31bfa..257e001f0fdbe 100644 --- a/packages/@aws-cdk/aws-codedeploy/package.json +++ b/packages/@aws-cdk/aws-codedeploy/package.json @@ -173,5 +173,8 @@ "awscdkio": { "announce": false }, - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-codedeploy/test/server/integ.deployment-group.expected.json b/packages/@aws-cdk/aws-codedeploy/test/server/integ.deployment-group.expected.json index c463bc84e8df3..e50bc6ef02f2b 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/server/integ.deployment-group.expected.json +++ b/packages/@aws-cdk/aws-codedeploy/test/server/integ.deployment-group.expected.json @@ -659,7 +659,7 @@ "Fn::Join": [ "", [ - "#!/bin/bash\nPKG_CMD=`which yum 2>/dev/null`\nif [ -z \"$PKG_CMD\" ]; then\nPKG_CMD=apt-get\nelse\nPKG=CMD=yum\nfi\n$PKG_CMD update -y\n$PKG_CMD install -y ruby2.0\nif [ $? -ne 0 ]; then\n$PKG_CMD install -y ruby\nfi\n$PKG_CMD install -y awscli\nTMP_DIR=`mktemp -d`\ncd $TMP_DIR\naws s3 cp s3://aws-codedeploy-", + "#!/bin/bash\nset +e\nPKG_CMD=`which yum 2>/dev/null`\nset -e\nif [ -z \"$PKG_CMD\" ]; then\nPKG_CMD=apt-get\nelse\nPKG=CMD=yum\nfi\n$PKG_CMD update -y\nset +e\n$PKG_CMD install -y ruby2.0\nRUBY2_INSTALL=$?\nset -e\nif [ $RUBY2_INSTALL -ne 0 ]; then\n$PKG_CMD install -y ruby\nfi\n$PKG_CMD install -y awscli\nTMP_DIR=`mktemp -d`\ncd $TMP_DIR\naws s3 cp s3://aws-codedeploy-", { "Ref": "AWS::Region" }, @@ -884,4 +884,4 @@ "Default": "/aws/service/ami-amazon-linux-latest/amzn-ami-hvm-x86_64-gp2" } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-codedeploy/test/server/test.deployment-group.ts b/packages/@aws-cdk/aws-codedeploy/test/server/test.deployment-group.ts index 761d0e38eb6a9..aebb3aa8a074f 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/server/test.deployment-group.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/server/test.deployment-group.ts @@ -62,7 +62,7 @@ export = { 'Fn::Join': [ '', [ - '#!/bin/bash\nPKG_CMD=`which yum 2>/dev/null`\nif [ -z "$PKG_CMD" ]; then\nPKG_CMD=apt-get\nelse\nPKG=CMD=yum\nfi\n$PKG_CMD update -y\n$PKG_CMD install -y ruby2.0\nif [ $? -ne 0 ]; then\n$PKG_CMD install -y ruby\nfi\n$PKG_CMD install -y awscli\nTMP_DIR=`mktemp -d`\ncd $TMP_DIR\naws s3 cp s3://aws-codedeploy-', + '#!/bin/bash\nset +e\nPKG_CMD=`which yum 2>/dev/null`\nset -e\nif [ -z "$PKG_CMD" ]; then\nPKG_CMD=apt-get\nelse\nPKG=CMD=yum\nfi\n$PKG_CMD update -y\nset +e\n$PKG_CMD install -y ruby2.0\nRUBY2_INSTALL=$?\nset -e\nif [ $RUBY2_INSTALL -ne 0 ]; then\n$PKG_CMD install -y ruby\nfi\n$PKG_CMD install -y awscli\nTMP_DIR=`mktemp -d`\ncd $TMP_DIR\naws s3 cp s3://aws-codedeploy-', { 'Ref': 'AWS::Region', }, diff --git a/packages/@aws-cdk/aws-codeguruprofiler/package.json b/packages/@aws-cdk/aws-codeguruprofiler/package.json index 8a23838477f9b..dd7c44d1a3527 100644 --- a/packages/@aws-cdk/aws-codeguruprofiler/package.json +++ b/packages/@aws-cdk/aws-codeguruprofiler/package.json @@ -96,5 +96,8 @@ "maturity": "stable", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-codegurureviewer/package.json b/packages/@aws-cdk/aws-codegurureviewer/package.json index 254ea4f99130c..1b75ae00b4682 100644 --- a/packages/@aws-cdk/aws-codegurureviewer/package.json +++ b/packages/@aws-cdk/aws-codegurureviewer/package.json @@ -91,5 +91,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-codepipeline-actions/lib/codebuild/build-action.ts b/packages/@aws-cdk/aws-codepipeline-actions/lib/codebuild/build-action.ts index 1fc1611bd9ff2..54735d4ec1d9d 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/lib/codebuild/build-action.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/lib/codebuild/build-action.ts @@ -156,7 +156,7 @@ export class CodeBuildAction extends Action { options.role.addToPolicy(new iam.PolicyStatement({ resources: [this.props.project.projectArn], actions: [ - 'codebuild:BatchGetBuilds', + `codebuild:${this.props.executeBatchBuild ? 'BatchGetBuildBatches' : 'BatchGetBuilds'}`, `codebuild:${this.props.executeBatchBuild ? 'StartBuildBatch' : 'StartBuild'}`, `codebuild:${this.props.executeBatchBuild ? 'StopBuildBatch' : 'StopBuild'}`, ], diff --git a/packages/@aws-cdk/aws-codepipeline-actions/package.json b/packages/@aws-cdk/aws-codepipeline-actions/package.json index 1e63ddf9854cd..c5b2115f2d944 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/package.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/package.json @@ -180,5 +180,8 @@ "env": { "AWSLINT_BASE_CONSTRUCT": true } + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.cfn-template-from-repo.lit.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.cfn-template-from-repo.lit.expected.json index 9236ecca96b41..8f708cfa71a21 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.cfn-template-from-repo.lit.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.cfn-template-from-repo.lit.expected.json @@ -12,23 +12,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { @@ -49,76 +33,6 @@ } }, "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineRoleD68726F7", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineSourceCodePipelineActionRoleC6F9E7F5", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineDeployPrepareChangesRoleD28C853C", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineDeployPrepareChangesCodePipelineActionRole41931444", - "Arn" - ] - } - }, - "Resource": "*" } ], "Version": "2012-10-17" @@ -878,4 +792,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-deployed-through-codepipeline.lit.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-deployed-through-codepipeline.lit.expected.json index 023573c87412f..9547f050e1e8b 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-deployed-through-codepipeline.lit.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-deployed-through-codepipeline.lit.expected.json @@ -6,23 +6,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { @@ -43,169 +27,6 @@ } }, "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineRoleD68726F7", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineSourceCdkCodeSourceCodePipelineActionRole237947B8", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineSourceLambdaCodeSourceCodePipelineActionRole4E89EF60", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "CdkBuildProjectRoleE0B6FEB0", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "CdkBuildProjectRoleE0B6FEB0", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "LambdaBuildProjectRoleD0C4F982", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "LambdaBuildProjectRoleD0C4F982", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineDeployLambdaCFNDeployRole89CA1043", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineDeployLambdaCFNDeployCodePipelineActionRoleF8A74488", - "Arn" - ] - } - }, - "Resource": "*" } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-pipeline.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-pipeline.expected.json index 906a2ebb7ccbd..3ff81600a78eb 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-pipeline.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-pipeline.expected.json @@ -6,23 +6,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { @@ -43,43 +27,6 @@ } }, "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineRoleD68726F7", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*", - "kms:Decrypt" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineSourceCodePipelineActionRoleC6F9E7F5", - "Arn" - ] - } - }, - "Resource": "*" } ], "Version": "2012-10-17" @@ -841,4 +788,4 @@ ] } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-alexa-deploy.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-alexa-deploy.expected.json index 262def042dc8a..561b739cb4b1c 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-alexa-deploy.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-alexa-deploy.expected.json @@ -16,23 +16,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { @@ -53,43 +37,6 @@ } }, "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineRoleD68726F7", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*", - "kms:Decrypt" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineSourceCodePipelineActionRoleC6F9E7F5", - "Arn" - ] - } - }, - "Resource": "*" } ], "Version": "2012-10-17" @@ -446,4 +393,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-cfn.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-cfn.expected.json index 3571c79c9a81f..67aaceac34f75 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-cfn.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-cfn.expected.json @@ -6,23 +6,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { @@ -43,77 +27,6 @@ } }, "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineRoleD68726F7", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*", - "kms:Decrypt" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineSourceCodePipelineActionRoleC6F9E7F5", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*", - "kms:Decrypt" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineSourceAdditionalSourceCodePipelineActionRole0897461A", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineCFNDeployCFNCodePipelineActionRole444CF5DD", - "Arn" - ] - } - }, - "Resource": "*" } ], "Version": "2012-10-17" @@ -818,4 +731,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-build-batch.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-build-batch.expected.json index 13357ba22c85b..3e9b5d76ec027 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-build-batch.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-build-batch.expected.json @@ -164,7 +164,7 @@ }, { "Action": [ - "codebuild:BatchGetBuilds", + "codebuild:BatchGetBuildBatches", "codebuild:StartBuildBatch", "codebuild:StopBuildBatch" ], diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit-build.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit-build.expected.json index dbf8c85f91394..e2292c1bbbdbd 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit-build.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit-build.expected.json @@ -263,23 +263,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { @@ -300,97 +284,6 @@ } }, "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineRoleD68726F7", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelinesourceCodePipelineActionRoleB7E0306A", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "MyBuildProjectRole6B7E2258", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "MyBuildProjectRole6B7E2258", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "MyBuildProjectRole6B7E2258", - "Arn" - ] - } - }, - "Resource": "*" } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit.expected.json index 0561884b64bd3..bde54408d887d 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit.expected.json @@ -77,23 +77,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { @@ -114,44 +98,6 @@ } }, "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineRoleD68726F7", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelinesourceCodePipelineActionRoleB7E0306A", - "Arn" - ] - } - }, - "Resource": "*" } ], "Version": "2012-10-17" @@ -599,4 +545,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-events.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-events.expected.json index d2031a00a8252..50e081f6300f9 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-events.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-events.expected.json @@ -6,23 +6,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { @@ -43,81 +27,6 @@ } }, "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "MyPipelineRoleC0D47CA4", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "MyPipelineSourceCodeCommitSourceCodePipelineActionRole0B6D0F4F", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "BuildProjectRoleAA92C755", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "BuildProjectRoleAA92C755", - "Arn" - ] - } - }, - "Resource": "*" } ], "Version": "2012-10-17" @@ -933,4 +842,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-stepfunctions.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-stepfunctions.expected.json index 2d7df75522359..da70221749108 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-stepfunctions.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-stepfunctions.expected.json @@ -49,23 +49,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { @@ -86,43 +70,6 @@ } }, "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "MyPipelineRoleC0D47CA4", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*", - "kms:Decrypt" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "MyPipelineSourceCodePipelineActionRoleAA05D76F", - "Arn" - ] - } - }, - "Resource": "*" } ], "Version": "2012-10-17" @@ -593,4 +540,4 @@ "DeletionPolicy": "Retain" } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-codepipeline/package.json b/packages/@aws-cdk/aws-codepipeline/package.json index d8f3dc77e7064..1cc830be83ec6 100644 --- a/packages/@aws-cdk/aws-codepipeline/package.json +++ b/packages/@aws-cdk/aws-codepipeline/package.json @@ -175,5 +175,8 @@ "awscdkio": { "announce": false }, - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-codepipeline/test/pipeline.test.ts b/packages/@aws-cdk/aws-codepipeline/test/pipeline.test.ts index 880c45b1f8a05..3b091ac8ef339 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/pipeline.test.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/pipeline.test.ts @@ -5,16 +5,15 @@ import * as kms from '@aws-cdk/aws-kms'; import * as s3 from '@aws-cdk/aws-s3'; import * as cdk from '@aws-cdk/core'; import * as cxapi from '@aws-cdk/cx-api'; -import { nodeunitShim, Test } from 'nodeunit-shim'; import * as codepipeline from '../lib'; import { FakeBuildAction } from './fake-build-action'; import { FakeSourceAction } from './fake-source-action'; /* eslint-disable quote-props */ -nodeunitShim({ - 'Pipeline': { - 'can be passed an IAM role during pipeline creation'(test: Test) { +describe('', () => { + describe('Pipeline', () => { + test('can be passed an IAM role during pipeline creation', () => { const stack = new cdk.Stack(); const role = new iam.Role(stack, 'Role', { assumedBy: new iam.ServicePrincipal('codepipeline.amazonaws.com'), @@ -32,23 +31,23 @@ nodeunitShim({ }, })); - test.done(); - }, - 'can be imported by ARN'(test: Test) { + }); + + test('can be imported by ARN', () => { const stack = new cdk.Stack(); const pipeline = codepipeline.Pipeline.fromPipelineArn(stack, 'Pipeline', 'arn:aws:codepipeline:us-east-1:123456789012:MyPipeline'); - test.equal(pipeline.pipelineArn, 'arn:aws:codepipeline:us-east-1:123456789012:MyPipeline'); - test.equal(pipeline.pipelineName, 'MyPipeline'); + expect(pipeline.pipelineArn).toEqual('arn:aws:codepipeline:us-east-1:123456789012:MyPipeline'); + expect(pipeline.pipelineName).toEqual('MyPipeline'); + - test.done(); - }, + }); - 'that is cross-region': { - 'validates that source actions are in the same region as the pipeline'(test: Test) { + describe('that is cross-region', () => { + test('validates that source actions are in the same region as the pipeline', () => { const app = new cdk.App(); const stack = new cdk.Stack(app, 'PipelineStack', { env: { region: 'us-west-1', account: '123456789012' } }); const pipeline = new codepipeline.Pipeline(stack, 'Pipeline'); @@ -61,14 +60,14 @@ nodeunitShim({ region: 'ap-southeast-1', }); - test.throws(() => { + expect(() => { sourceStage.addAction(sourceAction); - }, /Source action 'FakeSource' must be in the same region as the pipeline/); + }).toThrow(/Source action 'FakeSource' must be in the same region as the pipeline/); + - test.done(); - }, + }); - 'allows passing an Alias in place of the KMS Key in the replication Bucket'(test: Test) { + test('allows passing an Alias in place of the KMS Key in the replication Bucket', () => { const app = new cdk.App(); const replicationRegion = 'us-west-1'; @@ -173,10 +172,10 @@ nodeunitShim({ }, }); - test.done(); - }, - "generates ArtifactStores with the alias' ARN as the KeyID"(test: Test) { + }); + + test('generates ArtifactStores with the alias ARN as the KeyID', () => { const app = new cdk.App(); const replicationRegion = 'us-west-1'; @@ -239,10 +238,10 @@ nodeunitShim({ 'UpdateReplacePolicy': 'Delete', }, ResourcePart.CompleteDefinition); - test.done(); - }, - 'allows passing an imported Bucket and Key for the replication Bucket'(test: Test) { + }); + + test('allows passing an imported Bucket and Key for the replication Bucket', () => { const replicationRegion = 'us-west-1'; const pipelineRegion = 'us-west-2'; @@ -296,10 +295,10 @@ nodeunitShim({ ], }); - test.done(); - }, - 'generates the support stack containing the replication Bucket without the need to bootstrap in that environment'(test: Test) { + }); + + test('generates the support stack containing the replication Bucket without the need to bootstrap in that environment', () => { const app = new cdk.App({ treeMetadata: false, // we can't set the context otherwise, because App will have a child }); @@ -331,17 +330,17 @@ nodeunitShim({ const assembly = app.synth(); const supportStackArtifact = assembly.getStackByName('PipelineStack-support-eu-south-1'); - test.equal(supportStackArtifact.assumeRoleArn, + expect(supportStackArtifact.assumeRoleArn).toEqual( 'arn:${AWS::Partition}:iam::123456789012:role/cdk-hnb659fds-deploy-role-123456789012-us-west-2'); - test.equal(supportStackArtifact.cloudFormationExecutionRoleArn, + expect(supportStackArtifact.cloudFormationExecutionRoleArn).toEqual( 'arn:${AWS::Partition}:iam::123456789012:role/cdk-hnb659fds-cfn-exec-role-123456789012-us-west-2'); - test.done(); - }, - }, - 'that is cross-account': { - 'does not allow passing a dynamic value in the Action account property'(test: Test) { + }); + }); + + describe('that is cross-account', () => { + test('does not allow passing a dynamic value in the Action account property', () => { const app = new cdk.App(); const stack = new cdk.Stack(app, 'PipelineStack', { env: { account: '123456789012' } }); const sourceOutput = new codepipeline.Artifact(); @@ -355,18 +354,18 @@ nodeunitShim({ }); const buildStage = pipeline.addStage({ stageName: 'Build' }); - test.throws(() => { + expect(() => { buildStage.addAction(new FakeBuildAction({ actionName: 'FakeBuild', input: sourceOutput, account: cdk.Aws.ACCOUNT_ID, })); - }, /The 'account' property must be a concrete value \(action: 'FakeBuild'\)/); + }).toThrow(/The 'account' property must be a concrete value \(action: 'FakeBuild'\)/); + - test.done(); - }, + }); - 'does not allow an env-agnostic Pipeline Stack if an Action account has been provided'(test: Test) { + test('does not allow an env-agnostic Pipeline Stack if an Action account has been provided', () => { const app = new cdk.App(); const stack = new cdk.Stack(app, 'PipelineStack'); const sourceOutput = new codepipeline.Artifact(); @@ -380,18 +379,18 @@ nodeunitShim({ }); const buildStage = pipeline.addStage({ stageName: 'Build' }); - test.throws(() => { + expect(() => { buildStage.addAction(new FakeBuildAction({ actionName: 'FakeBuild', input: sourceOutput, account: '123456789012', })); - }, /Pipeline stack which uses cross-environment actions must have an explicitly set account/); + }).toThrow(/Pipeline stack which uses cross-environment actions must have an explicitly set account/); + - test.done(); - }, - }, - }, + }); + }); + }); }); describe('test with shared setup', () => { diff --git a/packages/@aws-cdk/aws-codestar/package.json b/packages/@aws-cdk/aws-codestar/package.json index fb70800f217c9..01024a24b1248 100644 --- a/packages/@aws-cdk/aws-codestar/package.json +++ b/packages/@aws-cdk/aws-codestar/package.json @@ -101,5 +101,8 @@ "maturity": "experimental", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-codestarconnections/package.json b/packages/@aws-cdk/aws-codestarconnections/package.json index cc087ac2da2cc..66ab2aab76ea6 100644 --- a/packages/@aws-cdk/aws-codestarconnections/package.json +++ b/packages/@aws-cdk/aws-codestarconnections/package.json @@ -91,5 +91,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-codestarnotifications/package.json b/packages/@aws-cdk/aws-codestarnotifications/package.json index ca8438caaae4a..18667b45db8b1 100644 --- a/packages/@aws-cdk/aws-codestarnotifications/package.json +++ b/packages/@aws-cdk/aws-codestarnotifications/package.json @@ -93,5 +93,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-cognito/lib/user-pool-idps/base.ts b/packages/@aws-cdk/aws-cognito/lib/user-pool-idps/base.ts index 27ee88777d885..be155fca69a6d 100644 --- a/packages/@aws-cdk/aws-cognito/lib/user-pool-idps/base.ts +++ b/packages/@aws-cdk/aws-cognito/lib/user-pool-idps/base.ts @@ -38,17 +38,17 @@ export class ProviderAttribute { public static readonly GOOGLE_GENDER = new ProviderAttribute('gender'); /** The birthday attribute provided by Google */ public static readonly GOOGLE_BIRTHDAYS = new ProviderAttribute('birthdays'); - /** The birthday attribute provided by Google */ + /** The phone number attribute provided by Google */ public static readonly GOOGLE_PHONE_NUMBERS = new ProviderAttribute('phoneNumbers'); /** The email attribute provided by Google */ public static readonly GOOGLE_EMAIL = new ProviderAttribute('email'); /** The name attribute provided by Google */ public static readonly GOOGLE_NAME = new ProviderAttribute('name'); - /** The email attribute provided by Google */ + /** The picture attribute provided by Google */ public static readonly GOOGLE_PICTURE = new ProviderAttribute('picture'); - /** The email attribute provided by Google */ + /** The given name attribute provided by Google */ public static readonly GOOGLE_GIVEN_NAME = new ProviderAttribute('given_name'); - /** The email attribute provided by Google */ + /** The family name attribute provided by Google */ public static readonly GOOGLE_FAMILY_NAME = new ProviderAttribute('family_name'); /** @@ -195,4 +195,4 @@ export interface UserPoolIdentityProviderProps { * @default - no attribute mapping */ readonly attributeMapping?: AttributeMapping; -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-cognito/package.json b/packages/@aws-cdk/aws-cognito/package.json index 8f1c747c1dbc7..c9bae0f953907 100644 --- a/packages/@aws-cdk/aws-cognito/package.json +++ b/packages/@aws-cdk/aws-cognito/package.json @@ -127,5 +127,8 @@ ], "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-config/package.json b/packages/@aws-cdk/aws-config/package.json index 641207bcd1a6d..59055c7030216 100644 --- a/packages/@aws-cdk/aws-config/package.json +++ b/packages/@aws-cdk/aws-config/package.json @@ -113,5 +113,8 @@ ], "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-databrew/package.json b/packages/@aws-cdk/aws-databrew/package.json index 51d40a0f6e767..be8bdfacf71db 100644 --- a/packages/@aws-cdk/aws-databrew/package.json +++ b/packages/@aws-cdk/aws-databrew/package.json @@ -91,5 +91,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-datapipeline/package.json b/packages/@aws-cdk/aws-datapipeline/package.json index 4079f7aaf6f65..2e1f552ef864a 100644 --- a/packages/@aws-cdk/aws-datapipeline/package.json +++ b/packages/@aws-cdk/aws-datapipeline/package.json @@ -92,5 +92,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-datasync/package.json b/packages/@aws-cdk/aws-datasync/package.json index 9ec4b17a3cf8c..124d3871ae0c3 100644 --- a/packages/@aws-cdk/aws-datasync/package.json +++ b/packages/@aws-cdk/aws-datasync/package.json @@ -91,5 +91,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-dax/package.json b/packages/@aws-cdk/aws-dax/package.json index b6c4a91029939..83fecea8535ee 100644 --- a/packages/@aws-cdk/aws-dax/package.json +++ b/packages/@aws-cdk/aws-dax/package.json @@ -92,5 +92,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-detective/package.json b/packages/@aws-cdk/aws-detective/package.json index 41010df5668d7..741eade5dc9e2 100644 --- a/packages/@aws-cdk/aws-detective/package.json +++ b/packages/@aws-cdk/aws-detective/package.json @@ -91,5 +91,8 @@ "awscdkio": { "announce": false }, - "maturity": "cfn-only" + "maturity": "cfn-only", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-devopsguru/package.json b/packages/@aws-cdk/aws-devopsguru/package.json index c0c9653ca7c2b..7632ba1d35cea 100644 --- a/packages/@aws-cdk/aws-devopsguru/package.json +++ b/packages/@aws-cdk/aws-devopsguru/package.json @@ -91,5 +91,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-directoryservice/package.json b/packages/@aws-cdk/aws-directoryservice/package.json index e3d0354c6d391..9be65b5324fff 100644 --- a/packages/@aws-cdk/aws-directoryservice/package.json +++ b/packages/@aws-cdk/aws-directoryservice/package.json @@ -92,5 +92,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-dlm/package.json b/packages/@aws-cdk/aws-dlm/package.json index 3bce8f24787b6..5829ed6a34e11 100644 --- a/packages/@aws-cdk/aws-dlm/package.json +++ b/packages/@aws-cdk/aws-dlm/package.json @@ -92,5 +92,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-dms/package.json b/packages/@aws-cdk/aws-dms/package.json index b83918ab2640d..1c6f0a473b7b3 100644 --- a/packages/@aws-cdk/aws-dms/package.json +++ b/packages/@aws-cdk/aws-dms/package.json @@ -92,5 +92,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-docdb/package.json b/packages/@aws-cdk/aws-docdb/package.json index de00eea1db9b8..658583ff0d644 100644 --- a/packages/@aws-cdk/aws-docdb/package.json +++ b/packages/@aws-cdk/aws-docdb/package.json @@ -108,5 +108,8 @@ "maturity": "experimental", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-docdb/test/integ.cluster.expected.json b/packages/@aws-cdk/aws-docdb/test/integ.cluster.expected.json index 69ced2a747f81..e8737956c6440 100644 --- a/packages/@aws-cdk/aws-docdb/test/integ.cluster.expected.json +++ b/packages/@aws-cdk/aws-docdb/test/integ.cluster.expected.json @@ -373,23 +373,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { @@ -522,4 +506,4 @@ "DeletionPolicy": "Delete" } } -} \ No newline at end of file +} 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 2dc031452ab61..8d1e45ac66b83 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,14 +29,14 @@ "devDependencies": { "aws-sdk": "^2.596.0", "aws-sdk-mock": "^5.1.0", - "eslint": "^7.13.0", + "eslint": "^7.20.0", "eslint-config-standard": "^14.1.1", "eslint-plugin-import": "^2.22.1", "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^4.2.1", + "eslint-plugin-promise": "^4.3.1", "eslint-plugin-standard": "^4.1.0", "jest": "^26.6.3", "lambda-tester": "^3.6.0", - "nock": "^13.0.5" + "nock": "^13.0.7" } } diff --git a/packages/@aws-cdk/aws-dynamodb-global/package.json b/packages/@aws-cdk/aws-dynamodb-global/package.json index 2b42ccaec9524..2a3a41bc29fbb 100644 --- a/packages/@aws-cdk/aws-dynamodb-global/package.json +++ b/packages/@aws-cdk/aws-dynamodb-global/package.json @@ -93,5 +93,8 @@ "awscdkio": { "announce": false }, - "maturity": "deprecated" + "maturity": "deprecated", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-dynamodb/package.json b/packages/@aws-cdk/aws-dynamodb/package.json index 6bae1adb77c14..ad19b5e91da6b 100644 --- a/packages/@aws-cdk/aws-dynamodb/package.json +++ b/packages/@aws-cdk/aws-dynamodb/package.json @@ -72,16 +72,16 @@ "license": "Apache-2.0", "devDependencies": { "@aws-cdk/assert": "0.0.0", - "@types/jest": "^26.0.15", - "aws-sdk": "^2.830.0", + "@types/jest": "^26.0.20", + "aws-sdk": "^2.845.0", "aws-sdk-mock": "^5.1.0", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "cfn2ts": "0.0.0", "jest": "^26.6.3", "pkglint": "0.0.0", - "sinon": "^9.2.1", - "ts-jest": "^26.4.4" + "sinon": "^9.2.4", + "ts-jest": "^26.5.1" }, "dependencies": { "@aws-cdk/aws-applicationautoscaling": "0.0.0", @@ -111,5 +111,8 @@ "awscdkio": { "announce": false }, - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts index ae77a3024adbd..30dae02e8a0df 100644 --- a/packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts +++ b/packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts @@ -1,9 +1,10 @@ -import { ABSENT, ResourcePart, SynthUtils } from '@aws-cdk/assert'; +import { arrayWith, ABSENT, ResourcePart, SynthUtils } from '@aws-cdk/assert'; import '@aws-cdk/assert/jest'; import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; -import { App, CfnDeletionPolicy, ConstructNode, Duration, PhysicalName, RemovalPolicy, Stack, Tags } from '@aws-cdk/core'; +import { App, Aws, CfnDeletionPolicy, ConstructNode, Duration, PhysicalName, RemovalPolicy, Stack, Tags } from '@aws-cdk/core'; +import { testLegacyBehavior } from 'cdk-build-tools/lib/feature-flag'; import { Attribute, AttributeType, @@ -486,154 +487,10 @@ test('fails if both replication regions used with customer managed CMK', () => { })).toThrow('TableEncryption.CUSTOMER_MANAGED is not supported by DynamoDB Global Tables (where replicationRegions was set)'); }); -test('if an encryption key is included, decrypt permissions are also added for grantStream', () => { - const stack = new Stack(); - const encryptionKey = new kms.Key(stack, 'Key', { - enableKeyRotation: true, - }); - const table = new Table(stack, 'Table A', { - tableName: TABLE_NAME, - partitionKey: TABLE_PARTITION_KEY, - encryptionKey, - stream: StreamViewType.NEW_IMAGE, - }); - const user = new iam.User(stack, 'MyUser'); - table.grantStreamRead(user); - expect(stack).toMatchTemplate({ - 'Resources': { - 'Key961B73FD': { - 'Type': 'AWS::KMS::Key', - 'Properties': { - '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': '*', - }, - ], - 'Version': '2012-10-17', - }, - 'EnableKeyRotation': true, - }, - 'UpdateReplacePolicy': 'Retain', - 'DeletionPolicy': 'Retain', - }, - 'TableA3D7B5AFA': { - 'Type': 'AWS::DynamoDB::Table', - 'Properties': { - 'KeySchema': [ - { - 'AttributeName': 'hashKey', - 'KeyType': 'HASH', - }, - ], - 'AttributeDefinitions': [ - { - 'AttributeName': 'hashKey', - 'AttributeType': 'S', - }, - ], - 'ProvisionedThroughput': { - 'ReadCapacityUnits': 5, - 'WriteCapacityUnits': 5, - }, - 'SSESpecification': { - 'KMSMasterKeyId': { - 'Fn::GetAtt': [ - 'Key961B73FD', - 'Arn', - ], - }, - 'SSEEnabled': true, - 'SSEType': 'KMS', - }, - 'StreamSpecification': { - 'StreamViewType': 'NEW_IMAGE', - }, - 'TableName': 'MyTable', - }, - 'UpdateReplacePolicy': 'Retain', - 'DeletionPolicy': 'Retain', - }, - 'MyUserDC45028B': { - 'Type': 'AWS::IAM::User', - }, - 'MyUserDefaultPolicy7B897426': { - 'Type': 'AWS::IAM::Policy', - 'Properties': { - 'PolicyDocument': { - 'Statement': [ - { - 'Action': 'dynamodb:ListStreams', - 'Effect': 'Allow', - 'Resource': '*', - }, - { - 'Action': [ - 'dynamodb:DescribeStream', - 'dynamodb:GetRecords', - 'dynamodb:GetShardIterator', - ], - 'Effect': 'Allow', - 'Resource': { - 'Fn::GetAtt': [ - 'TableA3D7B5AFA', - 'StreamArn', - ], - }, - }, - ], - 'Version': '2012-10-17', - }, - 'PolicyName': 'MyUserDefaultPolicy7B897426', - 'Users': [ - { - 'Ref': 'MyUserDC45028B', - }, - ], - }, - }, - }, - }); -}); - -test('if an encryption key is included, encrypt/decrypt permissions are also added both ways', () => { - const stack = new Stack(); +// this behaviour is only applicable without the future flag 'aws-kms:defaultKeyPolicies' +// see subsequent test for the updated behaviour +testLegacyBehavior('if an encryption key is included, encrypt/decrypt permissions are also added both ways', App, (app) => { + const stack = new Stack(app); const table = new Table(stack, 'Table A', { tableName: TABLE_NAME, partitionKey: TABLE_PARTITION_KEY, @@ -815,6 +672,38 @@ test('if an encryption key is included, encrypt/decrypt permissions are also add }); }); +test('if an encryption key is included, encrypt/decrypt permissions are added to the principal', () => { + const stack = new Stack(); + const table = new Table(stack, 'Table A', { + tableName: TABLE_NAME, + partitionKey: TABLE_PARTITION_KEY, + encryption: TableEncryption.CUSTOMER_MANAGED, + }); + const user = new iam.User(stack, 'MyUser'); + table.grantReadWriteData(user); + + expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + 'PolicyDocument': { + 'Statement': arrayWith({ + 'Action': [ + 'kms:Decrypt', + 'kms:DescribeKey', + 'kms:Encrypt', + 'kms:ReEncrypt*', + 'kms:GenerateDataKey*', + ], + 'Effect': 'Allow', + 'Resource': { + 'Fn::GetAtt': [ + 'TableAKey07CC09EC', + 'Arn', + ], + }, + }), + }, + }); +}); + test('when specifying PAY_PER_REQUEST billing mode', () => { const stack = new Stack(); new Table(stack, CONSTRUCT_NAME, { @@ -2244,7 +2133,7 @@ describe('import', () => { 'Roles': [{ 'Ref': 'NewRole99763075' }], }); - expect(table.tableArn).toBe('arn:${Token[AWS.Partition.3]}:dynamodb:${Token[AWS.Region.4]}:${Token[AWS.AccountId.0]}:table/MyTable'); + expect(table.tableArn).toBe(`arn:${Aws.PARTITION}:dynamodb:${Aws.REGION}:${Aws.ACCOUNT_ID}:table/MyTable`); expect(stack.resolve(table.tableName)).toBe(tableName); }); diff --git a/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.sse.expected.json b/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.sse.expected.json index ee7c8f9988f9e..2f49a9b48157d 100644 --- a/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.sse.expected.json +++ b/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.sse.expected.json @@ -6,23 +6,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { @@ -43,22 +27,6 @@ } }, "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "Role1ABCC5F0", - "Arn" - ] - } - }, - "Resource": "*" } ], "Version": "2012-10-17" @@ -358,23 +326,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { @@ -619,4 +571,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-ec2/lib/volume.ts b/packages/@aws-cdk/aws-ec2/lib/volume.ts index 36fb86faa2bac..a23614b89ed63 100644 --- a/packages/@aws-cdk/aws-ec2/lib/volume.ts +++ b/packages/@aws-cdk/aws-ec2/lib/volume.ts @@ -340,7 +340,7 @@ export interface VolumeProps { /** * The size of the volume, in GiBs. You must specify either a snapshot ID or a volume size. - * See {@link https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-volume-types.html#ebs-volume-characteristics|Volume Characteristics} + * See {@link https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-ebs-volume.html} * for details on the allowable size for each type of volume. * * @default If you're creating the volume from a snapshot and don't specify a volume size, the default is the snapshot size. @@ -421,13 +421,14 @@ export interface VolumeProps { readonly volumeType?: EbsDeviceVolumeType; /** - * The number of I/O operations per second (IOPS) to provision for the volume, with a maximum ratio of 50 IOPS/GiB. - * See {@link https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-volume-types.html#EBSVolumeTypes_piops|Provisioned IOPS SSD (io1) volumes} + * The number of I/O operations per second (IOPS) to provision for the volume. The maximum ratio is 50 IOPS/GiB for PROVISIONED_IOPS_SSD, + * and 500 IOPS/GiB for both PROVISIONED_IOPS_SSD_IO2 and GENERAL_PURPOSE_SSD_GP3. + * See {@link https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-ebs-volume.html} * for more information. * - * This parameter is valid only for PROVISIONED_IOPS_SSD volumes. + * This parameter is valid only for PROVISIONED_IOPS_SSD, PROVISIONED_IOPS_SSD_IO2 and GENERAL_PURPOSE_SSD_GP3 volumes. * - * @default None -- Required for {@link EbsDeviceVolumeType.PROVISIONED_IOPS_SSD} + * @default None -- Required for io1 and io2 volumes. The default for gp3 volumes is 3,000 IOPS if omitted. */ readonly iops?: number; } @@ -642,34 +643,79 @@ export class Volume extends VolumeBase { throw new Error('`encrypted` must be true when providing an `encryptionKey`.'); } + if ( + props.volumeType && + [ + EbsDeviceVolumeType.PROVISIONED_IOPS_SSD, + EbsDeviceVolumeType.PROVISIONED_IOPS_SSD_IO2, + ].includes(props.volumeType) && + !props.iops + ) { + throw new Error( + '`iops` must be specified if the `volumeType` is `PROVISIONED_IOPS_SSD` or `PROVISIONED_IOPS_SSD_IO2`.', + ); + } + if (props.iops) { - if (props.volumeType !== EbsDeviceVolumeType.PROVISIONED_IOPS_SSD) { - throw new Error('`iops` may only be specified if the `volumeType` is `PROVISIONED_IOPS_SSD`/`IO1`'); + const volumeType = props.volumeType ?? EbsDeviceVolumeType.GENERAL_PURPOSE_SSD; + if ( + ![ + EbsDeviceVolumeType.PROVISIONED_IOPS_SSD, + EbsDeviceVolumeType.PROVISIONED_IOPS_SSD_IO2, + EbsDeviceVolumeType.GENERAL_PURPOSE_SSD_GP3, + ].includes(volumeType) + ) { + throw new Error( + '`iops` may only be specified if the `volumeType` is `PROVISIONED_IOPS_SSD`, `PROVISIONED_IOPS_SSD_IO2` or `GENERAL_PURPOSE_SSD_GP3`.', + ); } - - if (props.iops < 100 || props.iops > 64000) { - throw new Error('`iops` must be in the range 100 to 64,000, inclusive.'); + // Enforce minimum & maximum IOPS: + // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-ebs-volume.html + const iopsRanges: { [key: string]: { Min: number, Max: number } } = {}; + iopsRanges[EbsDeviceVolumeType.GENERAL_PURPOSE_SSD_GP3] = { Min: 3000, Max: 16000 }; + iopsRanges[EbsDeviceVolumeType.PROVISIONED_IOPS_SSD] = { Min: 100, Max: 64000 }; + iopsRanges[EbsDeviceVolumeType.PROVISIONED_IOPS_SSD_IO2] = { Min: 100, Max: 64000 }; + const { Min, Max } = iopsRanges[volumeType]; + if (props.iops < Min || props.iops > Max) { + throw new Error(`\`${volumeType}\` volumes iops must be between ${Min} and ${Max}.`); } - if (props.size && (props.iops > 50 * props.size.toGibibytes({ rounding: SizeRoundingBehavior.FAIL }))) { - throw new Error('`iops` has a maximum ratio of 50 IOPS/GiB.'); + // Enforce maximum ratio of IOPS/GiB: + // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-volume-types.html + const maximumRatios: { [key: string]: number } = {}; + maximumRatios[EbsDeviceVolumeType.GENERAL_PURPOSE_SSD_GP3] = 500; + maximumRatios[EbsDeviceVolumeType.PROVISIONED_IOPS_SSD] = 50; + maximumRatios[EbsDeviceVolumeType.PROVISIONED_IOPS_SSD_IO2] = 500; + const maximumRatio = maximumRatios[volumeType]; + if (props.size && (props.iops > maximumRatio * props.size.toGibibytes({ rounding: SizeRoundingBehavior.FAIL }))) { + throw new Error(`\`${volumeType}\` volumes iops has a maximum ratio of ${maximumRatio} IOPS/GiB.`); } } - if (props.enableMultiAttach && props.volumeType !== EbsDeviceVolumeType.PROVISIONED_IOPS_SSD) { - throw new Error('multi-attach is supported exclusively on `PROVISIONED_IOPS_SSD` volumes.'); + if (props.enableMultiAttach) { + const volumeType = props.volumeType ?? EbsDeviceVolumeType.GENERAL_PURPOSE_SSD; + if ( + ![ + EbsDeviceVolumeType.PROVISIONED_IOPS_SSD, + EbsDeviceVolumeType.PROVISIONED_IOPS_SSD_IO2, + ].includes(volumeType) + ) { + throw new Error('multi-attach is supported exclusively on `PROVISIONED_IOPS_SSD` and `PROVISIONED_IOPS_SSD_IO2` volumes.'); + } } if (props.size) { const size = props.size.toGibibytes({ rounding: SizeRoundingBehavior.FAIL }); - // Enforce maximum volume size: - // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-volume-types.html#ebs-volume-characteristics + // Enforce minimum & maximum volume size: + // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-ebs-volume.html const sizeRanges: { [key: string]: { Min: number, Max: number } } = {}; - sizeRanges[EbsDeviceVolumeType.GENERAL_PURPOSE_SSD] = { Min: 1, Max: 16000 }; - sizeRanges[EbsDeviceVolumeType.PROVISIONED_IOPS_SSD] = { Min: 4, Max: 16000 }; - sizeRanges[EbsDeviceVolumeType.THROUGHPUT_OPTIMIZED_HDD] = { Min: 500, Max: 16000 }; - sizeRanges[EbsDeviceVolumeType.COLD_HDD] = { Min: 500, Max: 16000 }; - sizeRanges[EbsDeviceVolumeType.MAGNETIC] = { Min: 1, Max: 1000 }; + sizeRanges[EbsDeviceVolumeType.GENERAL_PURPOSE_SSD] = { Min: 1, Max: 16384 }; + sizeRanges[EbsDeviceVolumeType.GENERAL_PURPOSE_SSD_GP3] = { Min: 1, Max: 16384 }; + sizeRanges[EbsDeviceVolumeType.PROVISIONED_IOPS_SSD] = { Min: 4, Max: 16384 }; + sizeRanges[EbsDeviceVolumeType.PROVISIONED_IOPS_SSD_IO2] = { Min: 4, Max: 16384 }; + sizeRanges[EbsDeviceVolumeType.THROUGHPUT_OPTIMIZED_HDD] = { Min: 125, Max: 16384 }; + sizeRanges[EbsDeviceVolumeType.COLD_HDD] = { Min: 125, Max: 16384 }; + sizeRanges[EbsDeviceVolumeType.MAGNETIC] = { Min: 1, Max: 1024 }; const volumeType = props.volumeType ?? EbsDeviceVolumeType.GENERAL_PURPOSE_SSD; const { Min, Max } = sizeRanges[volumeType]; if (size < Min || size > Max) { diff --git a/packages/@aws-cdk/aws-ec2/package.json b/packages/@aws-cdk/aws-ec2/package.json index 74ceca63c984f..97f9edc2c3300 100644 --- a/packages/@aws-cdk/aws-ec2/package.json +++ b/packages/@aws-cdk/aws-ec2/package.json @@ -485,5 +485,8 @@ "awscdkio": { "announce": false }, - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-ec2/test/userdata.test.ts b/packages/@aws-cdk/aws-ec2/test/userdata.test.ts index b674e72abac68..883794bd5c585 100644 --- a/packages/@aws-cdk/aws-ec2/test/userdata.test.ts +++ b/packages/@aws-cdk/aws-ec2/test/userdata.test.ts @@ -1,6 +1,6 @@ import { Bucket } from '@aws-cdk/aws-s3'; +import { Aws, Stack } from '@aws-cdk/core'; import { nodeunitShim, Test } from 'nodeunit-shim'; -import { Stack } from '../../core/lib'; import * as ec2 from '../lib'; nodeunitShim({ @@ -52,7 +52,7 @@ nodeunitShim({ test.equals(rendered, 'trap {\n' + '$success=($PSItem.Exception.Message -eq "Success")\n' + - 'cfn-signal --stack Default --resource RESOURCE1989552F --region ${Token[AWS.Region.4]} --success ($success.ToString().ToLower())\n' + + `cfn-signal --stack Default --resource RESOURCE1989552F --region ${Aws.REGION} --success ($success.ToString().ToLower())\n` + 'break\n' + '}\n' + 'command1\n' + @@ -157,7 +157,7 @@ nodeunitShim({ test.equals(rendered, '#!/bin/bash\n' + 'function exitTrap(){\n' + 'exitCode=$?\n' + - '/opt/aws/bin/cfn-signal --stack Default --resource RESOURCE1989552F --region ${Token[AWS.Region.4]} -e $exitCode || echo \'Failed to send Cloudformation Signal\'\n' + + `/opt/aws/bin/cfn-signal --stack Default --resource RESOURCE1989552F --region ${Aws.REGION} -e $exitCode || echo \'Failed to send Cloudformation Signal\'\n` + '}\n' + 'trap exitTrap EXIT\n' + 'command1'); diff --git a/packages/@aws-cdk/aws-ec2/test/volume.test.ts b/packages/@aws-cdk/aws-ec2/test/volume.test.ts index e0d37a232c6d6..4e5da04a38976 100644 --- a/packages/@aws-cdk/aws-ec2/test/volume.test.ts +++ b/packages/@aws-cdk/aws-ec2/test/volume.test.ts @@ -1,4 +1,5 @@ import { + arrayWith, expect as cdkExpect, haveResource, haveResourceLike, @@ -10,7 +11,7 @@ import { } from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; import * as cdk from '@aws-cdk/core'; -import { nodeunitShim, Test } from 'nodeunit-shim'; +import { testFutureBehavior, testLegacyBehavior } from 'cdk-build-tools/lib/feature-flag'; import { AmazonLinuxGeneration, EbsDeviceVolumeType, @@ -21,8 +22,8 @@ import { Vpc, } from '../lib'; -nodeunitShim({ - 'basic volume'(test: Test) { +describe('volume', () => { + test('basic volume', () => { // GIVEN const stack = new cdk.Stack(); @@ -40,10 +41,10 @@ nodeunitShim({ VolumeType: 'gp2', }, ResourcePart.Properties)); - test.done(); - }, - 'fromVolumeAttributes'(test: Test) { + }); + + test('fromVolumeAttributes', () => { // GIVEN const stack = new cdk.Stack(); const encryptionKey = new kms.Key(stack, 'Key'); @@ -58,13 +59,13 @@ nodeunitShim({ }); // THEN - test.strictEqual(volume.volumeId, volumeId); - test.strictEqual(volume.availabilityZone, availabilityZone); - test.strictEqual(volume.encryptionKey, encryptionKey); - test.done(); - }, + expect(volume.volumeId).toEqual(volumeId); + expect(volume.availabilityZone).toEqual(availabilityZone); + expect(volume.encryptionKey).toEqual(encryptionKey); + + }); - 'tagged volume'(test: Test) { + test('tagged volume', () => { // GIVEN const stack = new cdk.Stack(); const volume = new Volume(stack, 'Volume', { @@ -87,10 +88,10 @@ nodeunitShim({ }], }, ResourcePart.Properties)); - test.done(); - }, - 'autoenableIO'(test: Test) { + }); + + test('autoenableIO', () => { // GIVEN const stack = new cdk.Stack(); @@ -106,10 +107,10 @@ nodeunitShim({ AutoEnableIO: true, }, ResourcePart.Properties)); - test.done(); - }, - 'encryption'(test: Test) { + }); + + test('encryption', () => { // GIVEN const stack = new cdk.Stack(); @@ -125,10 +126,10 @@ nodeunitShim({ Encrypted: true, }, ResourcePart.Properties)); - test.done(); - }, - 'encryption with kms'(test: Test) { + }); + + test('encryption with kms', () => { // GIVEN const stack = new cdk.Stack(); const encryptionKey = new kms.Key(stack, 'Key'); @@ -204,12 +205,14 @@ nodeunitShim({ }, })); - test.done(); - }, - 'encryption with kms from snapshot'(test: Test) { + }); + + // only enable for legacy behaviour + // see https://github.com/aws/aws-cdk/issues/12962 + testLegacyBehavior('encryption with kms from snapshot', cdk.App, (app) => { // GIVEN - const stack = new cdk.Stack(); + const stack = new cdk.Stack(app); const encryptionKey = new kms.Key(stack, 'Key'); // WHEN @@ -237,10 +240,10 @@ nodeunitShim({ }, })); - test.done(); - }, - 'iops'(test: Test) { + }); + + test('iops', () => { // GIVEN const stack = new cdk.Stack(); @@ -258,10 +261,10 @@ nodeunitShim({ VolumeType: 'io1', }, ResourcePart.Properties)); - test.done(); - }, - 'multi-attach'(test: Test) { + }); + + test('multi-attach', () => { // GIVEN const stack = new cdk.Stack(); @@ -279,10 +282,10 @@ nodeunitShim({ MultiAttachEnabled: true, }, ResourcePart.Properties)); - test.done(); - }, - 'snapshotId'(test: Test) { + }); + + test('snapshotId', () => { // GIVEN const stack = new cdk.Stack(); @@ -297,10 +300,10 @@ nodeunitShim({ SnapshotId: 'snap-00000000', }, ResourcePart.Properties)); - test.done(); - }, - 'volume: standard'(test: Test) { + }); + + test('volume: standard', () => { // GIVEN const stack = new cdk.Stack(); @@ -316,10 +319,10 @@ nodeunitShim({ VolumeType: 'standard', }, ResourcePart.Properties)); - test.done(); - }, - 'volume: io1'(test: Test) { + }); + + test('volume: io1', () => { // GIVEN const stack = new cdk.Stack(); @@ -328,6 +331,7 @@ nodeunitShim({ availabilityZone: 'us-east-1a', size: cdk.Size.gibibytes(500), volumeType: EbsDeviceVolumeType.PROVISIONED_IOPS_SSD, + iops: 300, }); // THEN @@ -335,10 +339,30 @@ nodeunitShim({ VolumeType: 'io1', }, ResourcePart.Properties)); - test.done(); - }, - 'volume: gp2'(test: Test) { + }); + + test('volume: io2', () => { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + new Volume(stack, 'Volume', { + availabilityZone: 'us-east-1a', + size: cdk.Size.gibibytes(500), + volumeType: EbsDeviceVolumeType.PROVISIONED_IOPS_SSD_IO2, + iops: 300, + }); + + // THEN + cdkExpect(stack).to(haveResourceLike('AWS::EC2::Volume', { + VolumeType: 'io2', + }, ResourcePart.Properties)); + + + }); + + test('volume: gp2', () => { // GIVEN const stack = new cdk.Stack(); @@ -354,10 +378,29 @@ nodeunitShim({ VolumeType: 'gp2', }, ResourcePart.Properties)); - test.done(); - }, - 'volume: st1'(test: Test) { + }); + + test('volume: gp3', () => { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + new Volume(stack, 'Volume', { + availabilityZone: 'us-east-1a', + size: cdk.Size.gibibytes(500), + volumeType: EbsDeviceVolumeType.GENERAL_PURPOSE_SSD_GP3, + }); + + // THEN + cdkExpect(stack).to(haveResourceLike('AWS::EC2::Volume', { + VolumeType: 'gp3', + }, ResourcePart.Properties)); + + + }); + + test('volume: st1', () => { // GIVEN const stack = new cdk.Stack(); @@ -373,10 +416,10 @@ nodeunitShim({ VolumeType: 'st1', }, ResourcePart.Properties)); - test.done(); - }, - 'volume: sc1'(test: Test) { + }); + + test('volume: sc1', () => { // GIVEN const stack = new cdk.Stack(); @@ -392,10 +435,10 @@ nodeunitShim({ VolumeType: 'sc1', }, ResourcePart.Properties)); - test.done(); - }, - 'grantAttachVolume to any instance'(test: Test) { + }); + + test('grantAttachVolume to any instance', () => { // GIVEN const stack = new cdk.Stack(); const role = new Role(stack, 'Role', { assumedBy: new AccountRootPrincipal() }); @@ -462,40 +505,94 @@ nodeunitShim({ }], }, })); - test.done(); - }, - 'grantAttachVolume to any instance with encryption'(test: Test) { - // GIVEN - const stack = new cdk.Stack(); - const role = new Role(stack, 'Role', { assumedBy: new AccountRootPrincipal() }); - const encryptionKey = new kms.Key(stack, 'Key'); - const volume = new Volume(stack, 'Volume', { - availabilityZone: 'us-east-1a', - size: cdk.Size.gibibytes(8), - encrypted: true, - encryptionKey, - }); + }); - // WHEN - volume.grantAttachVolume(role); + describe('grantAttachVolume to any instance with encryption', () => { - // THEN - cdkExpect(stack).to(haveResourceLike('AWS::KMS::Key', { - KeyPolicy: { - Statement: [ - {}, - {}, - { - Effect: 'Allow', - Principal: { - AWS: { - 'Fn::GetAtt': [ - 'Role1ABCC5F0', - 'Arn', - ], + // This exact assertions here are only applicable when 'aws-kms:defaultKeyPolicies' feature flag is disabled. + // See subsequent test case for the updated behaviour + testLegacyBehavior('legacy', cdk.App, (app) => { + // GIVEN + const stack = new cdk.Stack(app); + const role = new Role(stack, 'Role', { assumedBy: new AccountRootPrincipal() }); + const encryptionKey = new kms.Key(stack, 'Key'); + const volume = new Volume(stack, 'Volume', { + availabilityZone: 'us-east-1a', + size: cdk.Size.gibibytes(8), + encrypted: true, + encryptionKey, + }); + + // WHEN + volume.grantAttachVolume(role); + + // THEN + cdkExpect(stack).to(haveResourceLike('AWS::KMS::Key', { + KeyPolicy: { + Statement: [ + {}, + {}, + { + Effect: 'Allow', + Principal: { + AWS: { + 'Fn::GetAtt': [ + 'Role1ABCC5F0', + 'Arn', + ], + }, + }, + Action: 'kms:CreateGrant', + Condition: { + Bool: { + 'kms:GrantIsForAWSResource': true, + }, + StringEquals: { + 'kms:ViaService': { + 'Fn::Join': [ + '', + [ + 'ec2.', + { + Ref: 'AWS::Region', + }, + '.amazonaws.com', + ], + ], + }, + 'kms:GrantConstraintType': 'EncryptionContextSubset', + }, }, + Resource: '*', }, + ], + }, + })); + + + }); + + testFutureBehavior('with future flag aws-kms:defaultKeyPolicies', { '@aws-cdk/aws-kms:defaultKeyPolicies': true }, cdk.App, (app) => { + // GIVEN + const stack = new cdk.Stack(app); + const role = new Role(stack, 'Role', { assumedBy: new AccountRootPrincipal() }); + const encryptionKey = new kms.Key(stack, 'Key'); + const volume = new Volume(stack, 'Volume', { + availabilityZone: 'us-east-1a', + size: cdk.Size.gibibytes(8), + encrypted: true, + encryptionKey, + }); + + // WHEN + volume.grantAttachVolume(role); + + // THEN + cdkExpect(stack).to(haveResourceLike('AWS::IAM::Policy', { + PolicyDocument: { + Statement: arrayWith({ + Effect: 'Allow', Action: 'kms:CreateGrant', Condition: { Bool: { @@ -517,16 +614,22 @@ nodeunitShim({ 'kms:GrantConstraintType': 'EncryptionContextSubset', }, }, - Resource: '*', - }, - ], - }, - })); + Resource: { + 'Fn::GetAtt': [ + 'Key961B73FD', + 'Arn', + ], + }, + }), + }, + })); - test.done(); - }, - 'grantAttachVolume to any instance with KMS.fromKeyArn() encryption'(test: Test) { + }); + + }); + + test('grantAttachVolume to any instance with KMS.fromKeyArn() encryption', () => { // GIVEN const stack = new cdk.Stack(); const role = new Role(stack, 'Role', { assumedBy: new AccountRootPrincipal() }); @@ -598,10 +701,10 @@ nodeunitShim({ }, })); - test.done(); - }, - 'grantAttachVolume to specific instances'(test: Test) { + }); + + test('grantAttachVolume to specific instances', () => { // GIVEN const stack = new cdk.Stack(); const role = new Role(stack, 'Role', { assumedBy: new AccountRootPrincipal() }); @@ -686,10 +789,10 @@ nodeunitShim({ }, })); - test.done(); - }, - 'grantAttachVolume to instance self'(test: Test) { + }); + + test('grantAttachVolume to instance self', () => { // GIVEN const stack = new cdk.Stack(); const vpc = new Vpc(stack, 'Vpc'); @@ -763,10 +866,10 @@ nodeunitShim({ ], }, ResourcePart.Properties)); - test.done(); - }, - 'grantAttachVolume to instance self with suffix'(test: Test) { + }); + + test('grantAttachVolume to instance self with suffix', () => { // GIVEN const stack = new cdk.Stack(); const vpc = new Vpc(stack, 'Vpc'); @@ -839,10 +942,10 @@ nodeunitShim({ }, ], }, ResourcePart.Properties)); - test.done(); - }, - 'grantDetachVolume to any instance'(test: Test) { + }); + + test('grantDetachVolume to any instance', () => { // GIVEN const stack = new cdk.Stack(); const role = new Role(stack, 'Role', { assumedBy: new AccountRootPrincipal() }); @@ -909,10 +1012,10 @@ nodeunitShim({ }], }, })); - test.done(); - }, - 'grantDetachVolume from specific instance'(test: Test) { + }); + + test('grantDetachVolume from specific instance', () => { // GIVEN const stack = new cdk.Stack(); const role = new Role(stack, 'Role', { assumedBy: new AccountRootPrincipal() }); @@ -997,10 +1100,10 @@ nodeunitShim({ }, })); - test.done(); - }, - 'grantDetachVolume from instance self'(test: Test) { + }); + + test('grantDetachVolume from instance self', () => { // GIVEN const stack = new cdk.Stack(); const vpc = new Vpc(stack, 'Vpc'); @@ -1074,10 +1177,10 @@ nodeunitShim({ ], }, ResourcePart.Properties)); - test.done(); - }, - 'grantDetachVolume from instance self with suffix'(test: Test) { + }); + + test('grantDetachVolume from instance self with suffix', () => { // GIVEN const stack = new cdk.Stack(); const vpc = new Vpc(stack, 'Vpc'); @@ -1150,10 +1253,10 @@ nodeunitShim({ }, ], }, ResourcePart.Properties)); - test.done(); - }, - 'validation fromVolumeAttributes'(test: Test) { + }); + + test('validation fromVolumeAttributes', () => { // GIVEN let idx: number = 0; const stack = new cdk.Stack(); @@ -1163,93 +1266,93 @@ nodeunitShim({ }); // THEN - test.doesNotThrow(() => { + expect(() => { Volume.fromVolumeAttributes(stack, `Volume${idx++}`, { volumeId: volume.volumeId, availabilityZone: volume.availabilityZone, }); - }); - test.doesNotThrow(() => { + }).not.toThrow(); + expect(() => { Volume.fromVolumeAttributes(stack, `Volume${idx++}`, { volumeId: 'vol-0123456789abcdefABCDEF', availabilityZone: 'us-east-1a', }); - }); - test.throws(() => { + }).not.toThrow(); + expect(() => { Volume.fromVolumeAttributes(stack, `Volume${idx++}`, { volumeId: ' vol-0123456789abcdefABCDEF', // leading invalid character(s) availabilityZone: 'us-east-1a', }); - }, '`volumeId` does not match expected pattern. Expected `vol-` (ex: `vol-05abe246af`) or a Token'); - test.throws(() => { + }).toThrow('`volumeId` does not match expected pattern. Expected `vol-` (ex: `vol-05abe246af`) or a Token'); + expect(() => { Volume.fromVolumeAttributes(stack, `Volume${idx++}`, { volumeId: 'vol-0123456789abcdefABCDEF ', // trailing invalid character(s) availabilityZone: 'us-east-1a', }); - }, '`volumeId` does not match expected pattern. Expected `vol-` (ex: `vol-05abe246af`) or a Token'); - test.done(); - }, + }).toThrow('`volumeId` does not match expected pattern. Expected `vol-` (ex: `vol-05abe246af`) or a Token'); + + }); - 'validation required props'(test: Test) { + test('validation required props', () => { // GIVEN const stack = new cdk.Stack(); const key = new kms.Key(stack, 'Key'); let idx: number = 0; // THEN - test.throws(() => { + expect(() => { new Volume(stack, `Volume${idx++}`, { availabilityZone: 'us-east-1a', }); - }, 'Must provide at least one of `size` or `snapshotId`'); - test.doesNotThrow(() => { + }).toThrow('Must provide at least one of `size` or `snapshotId`'); + expect(() => { new Volume(stack, `Volume${idx++}`, { availabilityZone: 'us-east-1a', size: cdk.Size.gibibytes(8), }); - }); - test.doesNotThrow(() => { + }).not.toThrow(); + expect(() => { new Volume(stack, `Volume${idx++}`, { availabilityZone: 'us-east-1a', snapshotId: 'snap-000000000', }); - }); - test.doesNotThrow(() => { + }).not.toThrow(); + expect(() => { new Volume(stack, `Volume${idx++}`, { availabilityZone: 'us-east-1a', size: cdk.Size.gibibytes(8), snapshotId: 'snap-000000000', }); - }); + }).not.toThrow(); - test.throws(() => { + expect(() => { new Volume(stack, `Volume${idx++}`, { availabilityZone: 'us-east-1a', size: cdk.Size.gibibytes(8), encryptionKey: key, }); - }, '`encrypted` must be true when providing an `encryptionKey`.'); - test.throws(() => { + }).toThrow('`encrypted` must be true when providing an `encryptionKey`.'); + expect(() => { new Volume(stack, `Volume${idx++}`, { availabilityZone: 'us-east-1a', size: cdk.Size.gibibytes(8), encrypted: false, encryptionKey: key, }); - }, '`encrypted` must be true when providing an `encryptionKey`.'); - test.doesNotThrow(() => { + }).toThrow('`encrypted` must be true when providing an `encryptionKey`.'); + expect(() => { new Volume(stack, `Volume${idx++}`, { availabilityZone: 'us-east-1a', size: cdk.Size.gibibytes(8), encrypted: true, encryptionKey: key, }); - }); + }).not.toThrow(); + - test.done(); - }, + }); - 'validation snapshotId'(test: Test) { + test('validation snapshotId', () => { // GIVEN const stack = new cdk.Stack(); const volume = new Volume(stack, 'ForToken', { @@ -1259,114 +1362,160 @@ nodeunitShim({ let idx: number = 0; // THEN - test.doesNotThrow(() => { + expect(() => { // Should not throw if we provide a Token for the snapshotId new Volume(stack, `Volume${idx++}`, { availabilityZone: 'us-east-1a', snapshotId: volume.volumeId, }); - }); - test.doesNotThrow(() => { + }).not.toThrow(); + expect(() => { new Volume(stack, `Volume${idx++}`, { availabilityZone: 'us-east-1a', snapshotId: 'snap-0123456789abcdefABCDEF', }); - }); - test.throws(() => { + }).not.toThrow(); + expect(() => { new Volume(stack, `Volume${idx++}`, { availabilityZone: 'us-east-1a', snapshotId: ' snap-1234', // leading extra character(s) }); - }, '`snapshotId` does match expected pattern. Expected `snap-` (ex: `snap-05abe246af`) or Token'); - test.throws(() => { + }).toThrow('`snapshotId` does match expected pattern. Expected `snap-` (ex: `snap-05abe246af`) or Token'); + expect(() => { new Volume(stack, `Volume${idx++}`, { availabilityZone: 'us-east-1a', snapshotId: 'snap-1234 ', // trailing extra character(s) }); - }, '`snapshotId` does match expected pattern. Expected `snap-` (ex: `snap-05abe246af`) or Token'); + }).toThrow('`snapshotId` does match expected pattern. Expected `snap-` (ex: `snap-05abe246af`) or Token'); + - test.done(); - }, + }); - 'validation iops'(test: Test) { + test('validation iops', () => { // GIVEN const stack = new cdk.Stack(); let idx: number = 0; // THEN // Test: Type of volume + for (const volumeType of [ + EbsDeviceVolumeType.PROVISIONED_IOPS_SSD, + EbsDeviceVolumeType.PROVISIONED_IOPS_SSD_IO2, + EbsDeviceVolumeType.GENERAL_PURPOSE_SSD_GP3, + ]) { + expect(() => { + new Volume(stack, `Volume${idx++}`, { + availabilityZone: 'us-east-1a', + size: cdk.Size.gibibytes(500), + iops: 3000, + volumeType, + }); + }).not.toThrow(); + } + + for (const volumeType of [ + EbsDeviceVolumeType.PROVISIONED_IOPS_SSD, + EbsDeviceVolumeType.PROVISIONED_IOPS_SSD_IO2, + ]) { + expect(() => { + new Volume(stack, `Volume${idx++}`, { + availabilityZone: 'us-east-1a', + size: cdk.Size.gibibytes(500), + volumeType, + }); + }).toThrow(/`iops` must be specified if the `volumeType` is/); + } + for (const volumeType of [ EbsDeviceVolumeType.GENERAL_PURPOSE_SSD, EbsDeviceVolumeType.THROUGHPUT_OPTIMIZED_HDD, EbsDeviceVolumeType.COLD_HDD, EbsDeviceVolumeType.MAGNETIC, ]) { - test.throws(() => { + expect(() => { new Volume(stack, `Volume${idx++}`, { availabilityZone: 'us-east-1a', size: cdk.Size.gibibytes(500), iops: 100, volumeType, }); - }, '`iops` may only be specified if the `volumeType` is `PROVISIONED_IOPS_SSD`/`IO1`'); + }).toThrow(/`iops` may only be specified if the `volumeType` is/); } // Test: iops in range - test.throws(() => { - new Volume(stack, `Volume${idx++}`, { - availabilityZone: 'us-east-1a', - size: cdk.Size.gibibytes(10), - iops: 99, - volumeType: EbsDeviceVolumeType.PROVISIONED_IOPS_SSD, - }); - }, '`iops` must be in the range 100 to 64,000, inclusive.'); - test.doesNotThrow(() => { - new Volume(stack, `Volume${idx++}`, { - availabilityZone: 'us-east-1a', - size: cdk.Size.gibibytes(10), - iops: 100, - volumeType: EbsDeviceVolumeType.PROVISIONED_IOPS_SSD, - }); - }); - test.doesNotThrow(() => { - new Volume(stack, `Volume${idx++}`, { - availabilityZone: 'us-east-1a', - size: cdk.Size.gibibytes(1300), - iops: 64000, - volumeType: EbsDeviceVolumeType.PROVISIONED_IOPS_SSD, - }); - }); - test.throws(() => { - new Volume(stack, `Volume${idx++}`, { - availabilityZone: 'us-east-1a', - size: cdk.Size.gibibytes(1300), - iops: 64001, - volumeType: EbsDeviceVolumeType.PROVISIONED_IOPS_SSD, - }); - }, '`iops` must be in the range 100 to 64,000, inclusive.'); + for (const testData of [ + [EbsDeviceVolumeType.GENERAL_PURPOSE_SSD_GP3, 3000, 16000], + [EbsDeviceVolumeType.PROVISIONED_IOPS_SSD, 100, 64000], + [EbsDeviceVolumeType.PROVISIONED_IOPS_SSD_IO2, 100, 64000], + ]) { + const volumeType = testData[0] as EbsDeviceVolumeType; + const min = testData[1] as number; + const max = testData[2] as number; + expect(() => { + new Volume(stack, `Volume${idx++}`, { + availabilityZone: 'us-east-1a', + size: cdk.Size.tebibytes(10), + volumeType, + iops: min - 1, + }); + }).toThrow(/iops must be between/); + expect(() => { + new Volume(stack, `Volume${idx++}`, { + availabilityZone: 'us-east-1a', + size: cdk.Size.tebibytes(10), + volumeType, + iops: min, + }); + }).not.toThrow(); + expect(() => { + new Volume(stack, `Volume${idx++}`, { + availabilityZone: 'us-east-1a', + size: cdk.Size.tebibytes(10), + volumeType, + iops: max, + }); + }).not.toThrow(); + expect(() => { + new Volume(stack, `Volume${idx++}`, { + availabilityZone: 'us-east-1a', + size: cdk.Size.tebibytes(10), + volumeType, + iops: max + 1, + }); + }).toThrow(/iops must be between/); + } // Test: iops ratio - test.doesNotThrow(() => { - new Volume(stack, `Volume${idx++}`, { - availabilityZone: 'us-east-1a', - size: cdk.Size.gibibytes(10), - iops: 500, - volumeType: EbsDeviceVolumeType.PROVISIONED_IOPS_SSD, - }); - }); - test.throws(() => { - new Volume(stack, `Volume${idx++}`, { - availabilityZone: 'us-east-1a', - size: cdk.Size.gibibytes(10), - iops: 501, - volumeType: EbsDeviceVolumeType.PROVISIONED_IOPS_SSD, - }); - }, '`iops` has a maximum ratio of 50 IOPS/GiB.'); + for (const testData of [ + [EbsDeviceVolumeType.GENERAL_PURPOSE_SSD_GP3, 500], + [EbsDeviceVolumeType.PROVISIONED_IOPS_SSD, 50], + [EbsDeviceVolumeType.PROVISIONED_IOPS_SSD_IO2, 500], + ]) { + const volumeType = testData[0] as EbsDeviceVolumeType; + const max = testData[1] as number; + const size = 10; + expect(() => { + new Volume(stack, `Volume${idx++}`, { + availabilityZone: 'us-east-1a', + size: cdk.Size.gibibytes(size), + volumeType, + iops: max * size, + }); + }).not.toThrow(); + expect(() => { + new Volume(stack, `Volume${idx++}`, { + availabilityZone: 'us-east-1a', + size: cdk.Size.gibibytes(size), + volumeType, + iops: max * size + 1, + }); + }).toThrow(/iops has a maximum ratio of/); + } - test.done(); - }, - 'validation multi-attach'(test: Test) { + }); + + test('validation multi-attach', () => { // GIVEN const stack = new cdk.Stack(); let idx: number = 0; @@ -1374,70 +1523,109 @@ nodeunitShim({ // THEN for (const volumeType of [ EbsDeviceVolumeType.GENERAL_PURPOSE_SSD, + EbsDeviceVolumeType.GENERAL_PURPOSE_SSD_GP3, + EbsDeviceVolumeType.PROVISIONED_IOPS_SSD, + EbsDeviceVolumeType.PROVISIONED_IOPS_SSD_IO2, EbsDeviceVolumeType.THROUGHPUT_OPTIMIZED_HDD, EbsDeviceVolumeType.COLD_HDD, EbsDeviceVolumeType.MAGNETIC, ]) { - test.throws(() => { - new Volume(stack, `Volume${idx++}`, { - availabilityZone: 'us-east-1a', - size: cdk.Size.gibibytes(500), - enableMultiAttach: true, - volumeType, - }); - }, 'multi-attach is supported exclusively on `PROVISIONED_IOPS_SSD` volumes.'); + if ( + [ + EbsDeviceVolumeType.PROVISIONED_IOPS_SSD, + EbsDeviceVolumeType.PROVISIONED_IOPS_SSD_IO2, + ].includes(volumeType) + ) { + expect(() => { + new Volume(stack, `Volume${idx++}`, { + availabilityZone: 'us-east-1a', + size: cdk.Size.gibibytes(500), + enableMultiAttach: true, + volumeType, + iops: 100, + }); + }).not.toThrow(); + } else { + expect(() => { + new Volume(stack, `Volume${idx++}`, { + availabilityZone: 'us-east-1a', + size: cdk.Size.gibibytes(500), + enableMultiAttach: true, + volumeType, + }); + }).toThrow(/multi-attach is supported exclusively/); + } } - test.done(); - }, - 'validation size in range'(test: Test) { + }); + + test('validation size in range', () => { // GIVEN const stack = new cdk.Stack(); let idx: number = 0; // THEN for (const testData of [ - [EbsDeviceVolumeType.GENERAL_PURPOSE_SSD, 1, 16000], - [EbsDeviceVolumeType.PROVISIONED_IOPS_SSD, 4, 16000], - [EbsDeviceVolumeType.THROUGHPUT_OPTIMIZED_HDD, 500, 16000], - [EbsDeviceVolumeType.COLD_HDD, 500, 16000], - [EbsDeviceVolumeType.MAGNETIC, 1, 1000], + [EbsDeviceVolumeType.GENERAL_PURPOSE_SSD, 1, 16384], + [EbsDeviceVolumeType.GENERAL_PURPOSE_SSD_GP3, 1, 16384], + [EbsDeviceVolumeType.PROVISIONED_IOPS_SSD, 4, 16384], + [EbsDeviceVolumeType.PROVISIONED_IOPS_SSD_IO2, 4, 16384], + [EbsDeviceVolumeType.THROUGHPUT_OPTIMIZED_HDD, 125, 16384], + [EbsDeviceVolumeType.COLD_HDD, 125, 16384], + [EbsDeviceVolumeType.MAGNETIC, 1, 1024], ]) { const volumeType = testData[0] as EbsDeviceVolumeType; const min = testData[1] as number; const max = testData[2] as number; - test.throws(() => { + const iops = [ + EbsDeviceVolumeType.PROVISIONED_IOPS_SSD, + EbsDeviceVolumeType.PROVISIONED_IOPS_SSD_IO2, + ].includes(volumeType) ? 100 : null; + + expect(() => { new Volume(stack, `Volume${idx++}`, { availabilityZone: 'us-east-1a', size: cdk.Size.gibibytes(min - 1), volumeType, + ...iops + ? { iops } + : {}, }); - }, `\`${volumeType}\` volumes must be between ${min} GiB and ${max} GiB in size.`); - test.doesNotThrow(() => { + }).toThrow(/volumes must be between/); + expect(() => { new Volume(stack, `Volume${idx++}`, { availabilityZone: 'us-east-1a', size: cdk.Size.gibibytes(min), volumeType, + ...iops + ? { iops } + : {}, }); - }); - test.doesNotThrow(() => { + }).not.toThrow(); + expect(() => { new Volume(stack, `Volume${idx++}`, { availabilityZone: 'us-east-1a', size: cdk.Size.gibibytes(max), volumeType, + ...iops + ? { iops } + : {}, }); - }); - test.throws(() => { + }).not.toThrow(); + expect(() => { new Volume(stack, `Volume${idx++}`, { availabilityZone: 'us-east-1a', size: cdk.Size.gibibytes(max + 1), volumeType, + ...iops + ? { iops } + : {}, }); - }, `\`${volumeType}\` volumes must be between ${min} GiB and ${max} GiB in size.`); + }).toThrow(/volumes must be between/); } - test.done(); - }, + + }); }); diff --git a/packages/@aws-cdk/aws-ecr-assets/package.json b/packages/@aws-cdk/aws-ecr-assets/package.json index be4c4747d2408..3d8b54ee08122 100644 --- a/packages/@aws-cdk/aws-ecr-assets/package.json +++ b/packages/@aws-cdk/aws-ecr-assets/package.json @@ -112,5 +112,8 @@ "env": { "AWSLINT_BASE_CONSTRUCT": true } + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-ecr-assets/test/image-asset.test.ts b/packages/@aws-cdk/aws-ecr-assets/test/image-asset.test.ts index efa1498b41d90..83f27e16a3857 100644 --- a/packages/@aws-cdk/aws-ecr-assets/test/image-asset.test.ts +++ b/packages/@aws-cdk/aws-ecr-assets/test/image-asset.test.ts @@ -5,7 +5,6 @@ import * as iam from '@aws-cdk/aws-iam'; import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import { App, DefaultStackSynthesizer, IgnoreMode, Lazy, LegacyStackSynthesizer, Stack, Stage } from '@aws-cdk/core'; import * as cxapi from '@aws-cdk/cx-api'; -import { nodeunitShim, Test } from 'nodeunit-shim'; import { DockerImageAsset } from '../lib'; /* eslint-disable quote-props */ @@ -24,8 +23,8 @@ beforeEach(() => { stack = new Stack(app, 'Stack'); }); -nodeunitShim({ - 'test instantiating Asset Image'(test: Test) { +describe('image asset', () => { + test('test instantiating Asset Image', () => { // WHEN new DockerImageAsset(stack, 'Image', { directory: path.join(__dirname, 'demo-image'), @@ -34,8 +33,8 @@ nodeunitShim({ // THEN const asm = app.synth(); const artifact = asm.getStackArtifact(stack.artifactId); - test.deepEqual(artifact.template, {}, 'template is empty'); - test.deepEqual(artifact.assets, [ + expect(artifact.template).toEqual({}); + expect(artifact.assets).toEqual([ { repositoryName: 'aws-cdk/assets', imageTag: 'b2c69bfbfe983b634456574587443159b3b7258849856a118ad3d2772238f1a5', @@ -45,10 +44,10 @@ nodeunitShim({ sourceHash: 'b2c69bfbfe983b634456574587443159b3b7258849856a118ad3d2772238f1a5', }, ]); - test.done(); - }, - 'with build args'(test: Test) { + }); + + test('with build args', () => { // WHEN new DockerImageAsset(stack, 'Image', { directory: path.join(__dirname, 'demo-image'), @@ -59,11 +58,11 @@ nodeunitShim({ // THEN const assetMetadata = stack.node.metadata.find(({ type }) => type === cxschema.ArtifactMetadataEntryType.ASSET); - test.deepEqual(assetMetadata && (assetMetadata.data as cxschema.ContainerImageAssetMetadataEntry).buildArgs, { a: 'b' }); - test.done(); - }, + expect(assetMetadata && (assetMetadata.data as cxschema.ContainerImageAssetMetadataEntry).buildArgs).toEqual({ a: 'b' }); - 'with target'(test: Test) { + }); + + test('with target', () => { // WHEN new DockerImageAsset(stack, 'Image', { directory: path.join(__dirname, 'demo-image'), @@ -75,11 +74,11 @@ nodeunitShim({ // THEN const assetMetadata = stack.node.metadata.find(({ type }) => type === cxschema.ArtifactMetadataEntryType.ASSET); - test.deepEqual(assetMetadata && (assetMetadata.data as cxschema.ContainerImageAssetMetadataEntry).target, 'a-target'); - test.done(); - }, + expect(assetMetadata && (assetMetadata.data as cxschema.ContainerImageAssetMetadataEntry).target).toEqual('a-target'); + + }); - 'with file'(test: Test) { + test('with file', () => { // GIVEN const directoryPath = path.join(__dirname, 'demo-image-custom-docker-file'); // WHEN @@ -90,11 +89,11 @@ nodeunitShim({ // THEN const assetMetadata = stack.node.metadata.find(({ type }) => type === cxschema.ArtifactMetadataEntryType.ASSET); - test.deepEqual(assetMetadata && (assetMetadata.data as cxschema.ContainerImageAssetMetadataEntry).file, 'Dockerfile.Custom'); - test.done(); - }, + expect(assetMetadata && (assetMetadata.data as cxschema.ContainerImageAssetMetadataEntry).file).toEqual('Dockerfile.Custom'); + + }); - 'asset.repository.grantPull can be used to grant a principal permissions to use the image'(test: Test) { + test('asset.repository.grantPull can be used to grant a principal permissions to use the image', () => { // GIVEN const user = new iam.User(stack, 'MyUser'); const asset = new DockerImageAsset(stack, 'Image', { @@ -152,61 +151,61 @@ nodeunitShim({ ], })); - test.done(); - }, - 'fails if the directory does not exist'(test: Test) { + }); + + test('fails if the directory does not exist', () => { // THEN - test.throws(() => { + expect(() => { new DockerImageAsset(stack, 'MyAsset', { directory: `/does/not/exist/${Math.floor(Math.random() * 9999)}`, }); - }, /Cannot find image directory at/); - test.done(); - }, + }).toThrow(/Cannot find image directory at/); - 'fails if the directory does not contain a Dockerfile'(test: Test) { + }); + + test('fails if the directory does not contain a Dockerfile', () => { // THEN - test.throws(() => { + expect(() => { new DockerImageAsset(stack, 'Asset', { directory: __dirname, }); - }, /Cannot find file at/); - test.done(); - }, + }).toThrow(/Cannot find file at/); - 'fails if the file does not exist'(test: Test) { + }); + + test('fails if the file does not exist', () => { // THEN - test.throws(() => { + expect(() => { new DockerImageAsset(stack, 'Asset', { directory: __dirname, file: 'doesnt-exist', }); - }, /Cannot find file at/); - test.done(); - }, + }).toThrow(/Cannot find file at/); - 'docker directory is staged if asset staging is enabled'(test: Test) { + }); + + test('docker directory is staged if asset staging is enabled', () => { const image = new DockerImageAsset(stack, 'MyAsset', { directory: path.join(__dirname, 'demo-image'), }); const session = app.synth(); - test.ok(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'Dockerfile'))); - test.ok(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'index.py'))); - test.done(); - }, + expect(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'Dockerfile'))).toBeDefined(); + expect(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'index.py'))).toBeDefined(); - 'docker directory is staged without files specified in .dockerignore'(test: Test) { - testDockerDirectoryIsStagedWithoutFilesSpecifiedInDockerignore(test); - }, + }); - 'docker directory is staged without files specified in .dockerignore with IgnoreMode.GLOB'(test: Test) { - testDockerDirectoryIsStagedWithoutFilesSpecifiedInDockerignore(test, IgnoreMode.GLOB); - }, + test('docker directory is staged without files specified in .dockerignore', () => { + testDockerDirectoryIsStagedWithoutFilesSpecifiedInDockerignore(); + }); - 'docker directory is staged with whitelisted files specified in .dockerignore'(test: Test) { + test('docker directory is staged without files specified in .dockerignore with IgnoreMode.GLOB', () => { + testDockerDirectoryIsStagedWithoutFilesSpecifiedInDockerignore(IgnoreMode.GLOB); + }); + + test('docker directory is staged with whitelisted files specified in .dockerignore', () => { const image = new DockerImageAsset(stack, 'MyAsset', { directory: path.join(__dirname, 'whitelisted-image'), }); @@ -214,61 +213,61 @@ nodeunitShim({ const session = app.synth(); // Only the files exempted above should be included. - test.ok(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, '.dockerignore'))); - test.ok(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'Dockerfile'))); - test.ok(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'index.py'))); - test.ok(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'foobar.txt'))); - test.ok(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'subdirectory'))); - test.ok(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'subdirectory', 'baz.txt'))); - test.ok(!fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'node_modules'))); - test.ok(!fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'node_modules', 'one'))); - test.ok(!fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'node_modules', 'some_dep'))); - test.ok(!fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'node_modules', 'some_dep', 'file'))); - - test.done(); - }, - - 'docker directory is staged without files specified in exclude option'(test: Test) { - testDockerDirectoryIsStagedWithoutFilesSpecifiedInExcludeOption(test); - }, - - 'docker directory is staged without files specified in exclude option with IgnoreMode.GLOB'(test: Test) { - testDockerDirectoryIsStagedWithoutFilesSpecifiedInExcludeOption(test, IgnoreMode.GLOB); - }, - - 'fails if using tokens in build args keys or values'(test: Test) { + expect(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, '.dockerignore'))).toBeDefined(); + expect(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'Dockerfile'))).toBeDefined(); + expect(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'index.py'))).toBeDefined(); + expect(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'foobar.txt'))).toBeDefined(); + expect(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'subdirectory'))).toBeDefined(); + expect(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'subdirectory', 'baz.txt'))).toBeDefined(); + expect(!fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'node_modules'))).toBeDefined(); + expect(!fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'node_modules', 'one'))).toBeDefined(); + expect(!fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'node_modules', 'some_dep'))).toBeDefined(); + expect(!fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'node_modules', 'some_dep', 'file'))).toBeDefined(); + + + }); + + test('docker directory is staged without files specified in exclude option', () => { + testDockerDirectoryIsStagedWithoutFilesSpecifiedInExcludeOption(); + }); + + test('docker directory is staged without files specified in exclude option with IgnoreMode.GLOB', () => { + testDockerDirectoryIsStagedWithoutFilesSpecifiedInExcludeOption(IgnoreMode.GLOB); + }); + + test('fails if using tokens in build args keys or values', () => { // GIVEN const token = Lazy.string({ produce: () => 'foo' }); const expected = /Cannot use tokens in keys or values of "buildArgs" since they are needed before deployment/; // THEN - test.throws(() => new DockerImageAsset(stack, 'MyAsset1', { + expect(() => new DockerImageAsset(stack, 'MyAsset1', { directory: path.join(__dirname, 'demo-image'), buildArgs: { [token]: 'value' }, - }), expected); + })).toThrow(expected); - test.throws(() => new DockerImageAsset(stack, 'MyAsset2', { + expect(() => new DockerImageAsset(stack, 'MyAsset2', { directory: path.join(__dirname, 'demo-image'), buildArgs: { key: token }, - }), expected); + })).toThrow(expected); - test.done(); - }, - 'fails if using token as repositoryName'(test: Test) { + }); + + test('fails if using token as repositoryName', () => { // GIVEN const token = Lazy.string({ produce: () => 'foo' }); // THEN - test.throws(() => new DockerImageAsset(stack, 'MyAsset1', { + expect(() => new DockerImageAsset(stack, 'MyAsset1', { directory: path.join(__dirname, 'demo-image'), repositoryName: token, - }), /Cannot use Token as value of 'repositoryName'/); + })).toThrow(/Cannot use Token as value of 'repositoryName'/); - test.done(); - }, - 'docker build options are included in the asset id'(test: Test) { + }); + + test('docker build options are included in the asset id', () => { // GIVEN const directory = path.join(__dirname, 'demo-image-custom-docker-file'); @@ -280,18 +279,18 @@ nodeunitShim({ const asset6 = new DockerImageAsset(stack, 'Asset6', { directory, extraHash: 'random-extra' }); const asset7 = new DockerImageAsset(stack, 'Asset7', { directory, repositoryName: 'foo' }); - test.deepEqual(asset1.sourceHash, 'ab01ecd4419f59e1ec0ac9e57a60dbb653be68a29af0223fa8cb24b4b747bc73'); - test.deepEqual(asset2.sourceHash, '7fb12f6148098e3f5c56c788a865d2af689125ead403b795fe6a262ec34384b3'); - test.deepEqual(asset3.sourceHash, 'fc3b6d802ba198ba2ee55079dbef27682bcd1288d5849eb5bbd5cd69038359b3'); - test.deepEqual(asset4.sourceHash, '30439ea6dfeb4ddfd9175097286895c78393ef52a78c68f92db08abc4513cad6'); - test.deepEqual(asset5.sourceHash, '5775170880e26ba31799745241b90d4340c674bb3b1c01d758e416ee3f1c386f'); - test.deepEqual(asset6.sourceHash, 'ba82fd351a4d3e3f5c5d948b9948e7e829badc3da90f97e00bb7724afbeacfd4'); - test.deepEqual(asset7.sourceHash, '26ec194928431cab6ec5af24ea9f01af2cf7b20e361128b07b2a7405d2951f95'); - test.done(); - }, + expect(asset1.sourceHash).toEqual('ab01ecd4419f59e1ec0ac9e57a60dbb653be68a29af0223fa8cb24b4b747bc73'); + expect(asset2.sourceHash).toEqual('7fb12f6148098e3f5c56c788a865d2af689125ead403b795fe6a262ec34384b3'); + expect(asset3.sourceHash).toEqual('fc3b6d802ba198ba2ee55079dbef27682bcd1288d5849eb5bbd5cd69038359b3'); + expect(asset4.sourceHash).toEqual('30439ea6dfeb4ddfd9175097286895c78393ef52a78c68f92db08abc4513cad6'); + expect(asset5.sourceHash).toEqual('5775170880e26ba31799745241b90d4340c674bb3b1c01d758e416ee3f1c386f'); + expect(asset6.sourceHash).toEqual('ba82fd351a4d3e3f5c5d948b9948e7e829badc3da90f97e00bb7724afbeacfd4'); + expect(asset7.sourceHash).toEqual('26ec194928431cab6ec5af24ea9f01af2cf7b20e361128b07b2a7405d2951f95'); + + }); }); -function testDockerDirectoryIsStagedWithoutFilesSpecifiedInDockerignore(test: Test, ignoreMode?: IgnoreMode) { +function testDockerDirectoryIsStagedWithoutFilesSpecifiedInDockerignore(ignoreMode?: IgnoreMode) { const image = new DockerImageAsset(stack, 'MyAsset', { ignoreMode, directory: path.join(__dirname, 'dockerignore-image'), @@ -300,17 +299,17 @@ function testDockerDirectoryIsStagedWithoutFilesSpecifiedInDockerignore(test: Te const session = app.synth(); // .dockerignore itself should be included in output to be processed during docker build - test.ok(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, '.dockerignore'))); - test.ok(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'Dockerfile'))); - test.ok(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'index.py'))); - test.ok(!fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'foobar.txt'))); - test.ok(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'subdirectory'))); - test.ok(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'subdirectory', 'baz.txt'))); - - test.done(); + expect(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, '.dockerignore'))).toBeDefined(); + expect(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'Dockerfile'))).toBeDefined(); + expect(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'index.py'))).toBeDefined(); + expect(!fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'foobar.txt'))).toBeDefined(); + expect(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'subdirectory'))).toBeDefined(); + expect(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'subdirectory', 'baz.txt'))).toBeDefined(); + + } -function testDockerDirectoryIsStagedWithoutFilesSpecifiedInExcludeOption(test: Test, ignoreMode?: IgnoreMode) { +function testDockerDirectoryIsStagedWithoutFilesSpecifiedInExcludeOption(ignoreMode?: IgnoreMode) { const image = new DockerImageAsset(stack, 'MyAsset', { directory: path.join(__dirname, 'dockerignore-image'), exclude: ['subdirectory'], @@ -319,14 +318,14 @@ function testDockerDirectoryIsStagedWithoutFilesSpecifiedInExcludeOption(test: T const session = app.synth(); - test.ok(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, '.dockerignore'))); - test.ok(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'Dockerfile'))); - test.ok(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'index.py'))); - test.ok(!fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'foobar.txt'))); - test.ok(!fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'subdirectory'))); - test.ok(!fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'subdirectory', 'baz.txt'))); + expect(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, '.dockerignore'))).toBeDefined(); + expect(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'Dockerfile'))).toBeDefined(); + expect(fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'index.py'))).toBeDefined(); + expect(!fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'foobar.txt'))).toBeDefined(); + expect(!fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'subdirectory'))).toBeDefined(); + expect(!fs.existsSync(path.join(session.directory, `asset.${image.sourceHash}`, 'subdirectory', 'baz.txt'))).toBeDefined(); + - test.done(); } test('nested assemblies share assets: legacy synth edition', () => { diff --git a/packages/@aws-cdk/aws-ecr/package.json b/packages/@aws-cdk/aws-ecr/package.json index c51fefa8ce7b4..95a78ae04c522 100644 --- a/packages/@aws-cdk/aws-ecr/package.json +++ b/packages/@aws-cdk/aws-ecr/package.json @@ -117,5 +117,8 @@ "maturity": "stable", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-ecs-patterns/README.md b/packages/@aws-cdk/aws-ecs-patterns/README.md index 38915031f4346..c52f35bb65f02 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/README.md +++ b/packages/@aws-cdk/aws-ecs-patterns/README.md @@ -305,6 +305,7 @@ const ecsScheduledTask = new ScheduledEc2Task(stack, 'ScheduledTask', { environment: { name: 'TRIGGER', value: 'CloudWatch Events' }, }, schedule: events.Schedule.expression('rate(1 minute)'), + enabled: true, ruleName: 'sample-scheduled-task-rule' }); ``` @@ -393,6 +394,7 @@ const loadBalancedFargateService = new ApplicationLoadBalancedFargateService(sta }); ``` + ### Set deployment configuration on QueueProcessingService ```ts @@ -411,6 +413,18 @@ const queueProcessingFargateService = new QueueProcessingFargateService(stack, ' }); ``` +### Set taskSubnets and securityGroups on QueueProcessingFargateService + +```ts +const queueProcessingFargateService = new QueueProcessingFargateService(stack, 'Service', { + vpc, + memoryLimitMiB: 512, + image: ecs.ContainerImage.fromRegistry('test'), + securityGroups: [securityGroup], + taskSubnets: { subnetType: ec2.SubnetType.ISOLATED }, +}); +``` + ### Select specific vpc subnets for ApplicationLoadBalancedFargateService ```ts diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/base/scheduled-task-base.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/base/scheduled-task-base.ts index 38ac4e30a79af..7dc6492f42919 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/base/scheduled-task-base.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/base/scheduled-task-base.ts @@ -38,6 +38,13 @@ export interface ScheduledTaskBaseProps { */ readonly schedule: Schedule; + /** + * Indicates whether the rule is enabled. + * + * @default true + */ + readonly enabled?: boolean; + /** * A name for the rule. * @@ -148,6 +155,7 @@ export abstract class ScheduledTaskBase extends CoreConstruct { this.eventRule = new Rule(this, 'ScheduledEventRule', { schedule: props.schedule, ruleName: props.ruleName, + enabled: props.enabled, }); } diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/queue-processing-fargate-service.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/queue-processing-fargate-service.ts index 68d0278c3e203..8ba7fed3f5e76 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/queue-processing-fargate-service.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/queue-processing-fargate-service.ts @@ -1,3 +1,4 @@ +import * as ec2 from '@aws-cdk/aws-ec2'; import { FargatePlatformVersion, FargateService, FargateTaskDefinition } from '@aws-cdk/aws-ecs'; import { Construct } from 'constructs'; import { QueueProcessingServiceBase, QueueProcessingServiceBaseProps } from '../base/queue-processing-service-base'; @@ -66,6 +67,20 @@ export interface QueueProcessingFargateServiceProps extends QueueProcessingServi * @default - QueueProcessingContainer */ readonly containerName?: string; + + /** + * The subnets to associate with the service. + * + * @default - Public subnets if `assignPublicIp` is set, otherwise the first available one of Private, Isolated, Public, in that order. + */ + readonly taskSubnets?: ec2.SubnetSelection; + + /** + * The security groups to associate with the service. If you do not specify a security group, the default security group for the VPC is used. + * + * @default - A new security group is created. + */ + readonly securityGroups?: ec2.ISecurityGroup[]; } /** @@ -117,6 +132,8 @@ export class QueueProcessingFargateService extends QueueProcessingServiceBase { enableECSManagedTags: props.enableECSManagedTags, platformVersion: props.platformVersion, deploymentController: props.deploymentController, + securityGroups: props.securityGroups, + vpcSubnets: props.taskSubnets, }); this.configureAutoscalingForService(this.service); this.grantPermissionsToService(this.service); diff --git a/packages/@aws-cdk/aws-ecs-patterns/package.json b/packages/@aws-cdk/aws-ecs-patterns/package.json index c455df841fbae..bc2a816cd6b11 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/package.json +++ b/packages/@aws-cdk/aws-ecs-patterns/package.json @@ -125,5 +125,8 @@ "env": { "AWSLINT_BASE_CONSTRUCT": true } + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/test.scheduled-ecs-task.ts b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/test.scheduled-ecs-task.ts index d96c8cd2edc7d..af262cd5de5ef 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/test.scheduled-ecs-task.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/test.scheduled-ecs-task.ts @@ -27,6 +27,7 @@ export = { // THEN expect(stack).to(haveResource('AWS::Events::Rule', { + State: 'ENABLED', Targets: [ { Arn: { 'Fn::GetAtt': ['EcsCluster97242B84', 'Arn'] }, @@ -78,6 +79,7 @@ export = { new ScheduledEc2Task(stack, 'ScheduledEc2Task', { cluster, + enabled: false, scheduledEc2TaskImageOptions: { image: ecs.ContainerImage.fromRegistry('henk'), memoryLimitMiB: 512, @@ -92,6 +94,7 @@ export = { // THEN expect(stack).to(haveResource('AWS::Events::Rule', { Name: 'sample-scheduled-task-rule', + State: 'DISABLED', Targets: [ { Arn: { 'Fn::GetAtt': ['EcsCluster97242B84', 'Arn'] }, diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service-isolated.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service-isolated.expected.json new file mode 100644 index 0000000000000..d19d9c6a9fbb9 --- /dev/null +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service-isolated.expected.json @@ -0,0 +1,1201 @@ +{ + "Resources": { + "VPCB9E5F0B4": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-patterns-queue-isolated/VPC" + } + ] + } + }, + "VPCPublicSubnet1SubnetB4246D30": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.0.0/24", + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "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-patterns-queue-isolated/VPC/PublicSubnet1" + } + ] + } + }, + "VPCPublicSubnet1RouteTableFEE4B781": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-patterns-queue-isolated/VPC/PublicSubnet1" + } + ] + } + }, + "VPCPublicSubnet1RouteTableAssociation0B0896DC": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet1RouteTableFEE4B781" + }, + "SubnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + } + } + }, + "VPCPublicSubnet1DefaultRoute91CEF279": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet1RouteTableFEE4B781" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VPCIGWB7E252D3" + } + }, + "DependsOn": [ + "VPCVPCGW99B986DC" + ] + }, + "VPCPublicSubnet2Subnet74179F39": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.1.0/24", + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "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-patterns-queue-isolated/VPC/PublicSubnet2" + } + ] + } + }, + "VPCPublicSubnet2RouteTable6F1A15F1": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-patterns-queue-isolated/VPC/PublicSubnet2" + } + ] + } + }, + "VPCPublicSubnet2RouteTableAssociation5A808732": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet2RouteTable6F1A15F1" + }, + "SubnetId": { + "Ref": "VPCPublicSubnet2Subnet74179F39" + } + } + }, + "VPCPublicSubnet2DefaultRouteB7481BBA": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet2RouteTable6F1A15F1" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VPCIGWB7E252D3" + } + }, + "DependsOn": [ + "VPCVPCGW99B986DC" + ] + }, + "VPCIsolatedSubnet1SubnetEBD00FC6": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.2.0/24", + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Isolated" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Isolated" + }, + { + "Key": "Name", + "Value": "aws-ecs-patterns-queue-isolated/VPC/IsolatedSubnet1" + } + ] + } + }, + "VPCIsolatedSubnet1RouteTableEB156210": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-patterns-queue-isolated/VPC/IsolatedSubnet1" + } + ] + } + }, + "VPCIsolatedSubnet1RouteTableAssociationA2D18F7C": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCIsolatedSubnet1RouteTableEB156210" + }, + "SubnetId": { + "Ref": "VPCIsolatedSubnet1SubnetEBD00FC6" + } + } + }, + "VPCIsolatedSubnet2Subnet4B1C8CAA": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.3.0/24", + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "AvailabilityZone": "test-region-1b", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Isolated" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Isolated" + }, + { + "Key": "Name", + "Value": "aws-ecs-patterns-queue-isolated/VPC/IsolatedSubnet2" + } + ] + } + }, + "VPCIsolatedSubnet2RouteTable9B4F78DC": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-patterns-queue-isolated/VPC/IsolatedSubnet2" + } + ] + } + }, + "VPCIsolatedSubnet2RouteTableAssociation7BF8E0EB": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCIsolatedSubnet2RouteTable9B4F78DC" + }, + "SubnetId": { + "Ref": "VPCIsolatedSubnet2Subnet4B1C8CAA" + } + } + }, + "VPCIGWB7E252D3": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-patterns-queue-isolated/VPC" + } + ] + } + }, + "VPCVPCGW99B986DC": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "InternetGatewayId": { + "Ref": "VPCIGWB7E252D3" + } + } + }, + "VPCS3Endpoint18C9C7CA": { + "Type": "AWS::EC2::VPCEndpoint", + "Properties": { + "ServiceName": { + "Fn::Join": [ + "", + [ + "com.amazonaws.", + { + "Ref": "AWS::Region" + }, + ".s3" + ] + ] + }, + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "RouteTableIds": [ + { + "Ref": "VPCIsolatedSubnet1RouteTableEB156210" + }, + { + "Ref": "VPCIsolatedSubnet2RouteTable9B4F78DC" + } + ], + "VpcEndpointType": "Gateway" + } + }, + "VPCSqsEndpointSecurityGroupAE06A78D": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "aws-ecs-patterns-queue-isolated/VPC/SqsEndpoint/SecurityGroup", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "SecurityGroupIngress": [ + { + "CidrIp": { + "Fn::GetAtt": [ + "VPCB9E5F0B4", + "CidrBlock" + ] + }, + "Description": { + "Fn::Join": [ + "", + [ + "from ", + { + "Fn::GetAtt": [ + "VPCB9E5F0B4", + "CidrBlock" + ] + }, + ":443" + ] + ] + }, + "FromPort": 443, + "IpProtocol": "tcp", + "ToPort": 443 + } + ], + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-patterns-queue-isolated/VPC" + } + ], + "VpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "VPCSqsEndpoint9A40D77F": { + "Type": "AWS::EC2::VPCEndpoint", + "Properties": { + "ServiceName": { + "Fn::Join": [ + "", + [ + "com.amazonaws.", + { + "Ref": "AWS::Region" + }, + ".sqs" + ] + ] + }, + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "PrivateDnsEnabled": true, + "SecurityGroupIds": [ + { + "Fn::GetAtt": [ + "VPCSqsEndpointSecurityGroupAE06A78D", + "GroupId" + ] + } + ], + "SubnetIds": [ + { + "Ref": "VPCIsolatedSubnet1SubnetEBD00FC6" + }, + { + "Ref": "VPCIsolatedSubnet2Subnet4B1C8CAA" + } + ], + "VpcEndpointType": "Interface" + } + }, + "VPCEcrEndpointSecurityGroup50ED8BA4": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "aws-ecs-patterns-queue-isolated/VPC/EcrEndpoint/SecurityGroup", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "SecurityGroupIngress": [ + { + "CidrIp": { + "Fn::GetAtt": [ + "VPCB9E5F0B4", + "CidrBlock" + ] + }, + "Description": { + "Fn::Join": [ + "", + [ + "from ", + { + "Fn::GetAtt": [ + "VPCB9E5F0B4", + "CidrBlock" + ] + }, + ":443" + ] + ] + }, + "FromPort": 443, + "IpProtocol": "tcp", + "ToPort": 443 + } + ], + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-patterns-queue-isolated/VPC" + } + ], + "VpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "VPCEcrEndpointB4F98F37": { + "Type": "AWS::EC2::VPCEndpoint", + "Properties": { + "ServiceName": { + "Fn::Join": [ + "", + [ + "com.amazonaws.", + { + "Ref": "AWS::Region" + }, + ".ecr.api" + ] + ] + }, + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "PrivateDnsEnabled": true, + "SecurityGroupIds": [ + { + "Fn::GetAtt": [ + "VPCEcrEndpointSecurityGroup50ED8BA4", + "GroupId" + ] + } + ], + "SubnetIds": [ + { + "Ref": "VPCIsolatedSubnet1SubnetEBD00FC6" + }, + { + "Ref": "VPCIsolatedSubnet2Subnet4B1C8CAA" + } + ], + "VpcEndpointType": "Interface" + } + }, + "VPCEcrImageEndpointSecurityGroup83621638": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "aws-ecs-patterns-queue-isolated/VPC/EcrImageEndpoint/SecurityGroup", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "SecurityGroupIngress": [ + { + "CidrIp": { + "Fn::GetAtt": [ + "VPCB9E5F0B4", + "CidrBlock" + ] + }, + "Description": { + "Fn::Join": [ + "", + [ + "from ", + { + "Fn::GetAtt": [ + "VPCB9E5F0B4", + "CidrBlock" + ] + }, + ":443" + ] + ] + }, + "FromPort": 443, + "IpProtocol": "tcp", + "ToPort": 443 + } + ], + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-patterns-queue-isolated/VPC" + } + ], + "VpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "VPCEcrImageEndpointD55381DC": { + "Type": "AWS::EC2::VPCEndpoint", + "Properties": { + "ServiceName": { + "Fn::Join": [ + "", + [ + "com.amazonaws.", + { + "Ref": "AWS::Region" + }, + ".ecr.dkr" + ] + ] + }, + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "PrivateDnsEnabled": true, + "SecurityGroupIds": [ + { + "Fn::GetAtt": [ + "VPCEcrImageEndpointSecurityGroup83621638", + "GroupId" + ] + } + ], + "SubnetIds": [ + { + "Ref": "VPCIsolatedSubnet1SubnetEBD00FC6" + }, + { + "Ref": "VPCIsolatedSubnet2Subnet4B1C8CAA" + } + ], + "VpcEndpointType": "Interface" + } + }, + "VPCCloudWatchLogsEndpointSecurityGroup967DBC94": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "aws-ecs-patterns-queue-isolated/VPC/CloudWatchLogsEndpoint/SecurityGroup", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "SecurityGroupIngress": [ + { + "CidrIp": { + "Fn::GetAtt": [ + "VPCB9E5F0B4", + "CidrBlock" + ] + }, + "Description": { + "Fn::Join": [ + "", + [ + "from ", + { + "Fn::GetAtt": [ + "VPCB9E5F0B4", + "CidrBlock" + ] + }, + ":443" + ] + ] + }, + "FromPort": 443, + "IpProtocol": "tcp", + "ToPort": 443 + } + ], + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-patterns-queue-isolated/VPC" + } + ], + "VpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "VPCCloudWatchLogsEndpointE175AF65": { + "Type": "AWS::EC2::VPCEndpoint", + "Properties": { + "ServiceName": { + "Fn::Join": [ + "", + [ + "com.amazonaws.", + { + "Ref": "AWS::Region" + }, + ".logs" + ] + ] + }, + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "PrivateDnsEnabled": true, + "SecurityGroupIds": [ + { + "Fn::GetAtt": [ + "VPCCloudWatchLogsEndpointSecurityGroup967DBC94", + "GroupId" + ] + } + ], + "SubnetIds": [ + { + "Ref": "VPCIsolatedSubnet1SubnetEBD00FC6" + }, + { + "Ref": "VPCIsolatedSubnet2Subnet4B1C8CAA" + } + ], + "VpcEndpointType": "Interface" + } + }, + "MyCustomSGDE27C661": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "aws-ecs-patterns-queue-isolated/MyCustomSG", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "IsolatedQueueServiceEcsProcessingDeadLetterQueue7CC1D07D": { + "Type": "AWS::SQS::Queue", + "Properties": { + "MessageRetentionPeriod": 1209600 + } + }, + "IsolatedQueueServiceEcsProcessingQueueCCE172F1": { + "Type": "AWS::SQS::Queue", + "Properties": { + "RedrivePolicy": { + "deadLetterTargetArn": { + "Fn::GetAtt": [ + "IsolatedQueueServiceEcsProcessingDeadLetterQueue7CC1D07D", + "Arn" + ] + }, + "maxReceiveCount": 3 + } + } + }, + "IsolatedQueueServiceQueueProcessingTaskDefTaskRoleCFCB7511": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "IsolatedQueueServiceQueueProcessingTaskDefTaskRoleDefaultPolicyD52E156B": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "sqs:ReceiveMessage", + "sqs:ChangeMessageVisibility", + "sqs:GetQueueUrl", + "sqs:DeleteMessage", + "sqs:GetQueueAttributes" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "IsolatedQueueServiceEcsProcessingQueueCCE172F1", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "IsolatedQueueServiceQueueProcessingTaskDefTaskRoleDefaultPolicyD52E156B", + "Roles": [ + { + "Ref": "IsolatedQueueServiceQueueProcessingTaskDefTaskRoleCFCB7511" + } + ] + } + }, + "IsolatedQueueServiceQueueProcessingTaskDef0F0CE105": { + "Type": "AWS::ECS::TaskDefinition", + "Properties": { + "ContainerDefinitions": [ + { + "Environment": [ + { + "Name": "QUEUE_NAME", + "Value": { + "Fn::GetAtt": [ + "IsolatedQueueServiceEcsProcessingQueueCCE172F1", + "QueueName" + ] + } + } + ], + "Essential": true, + "Image": { + "Fn::Join": [ + "", + [ + { + "Ref": "AWS::AccountId" + }, + ".dkr.ecr.", + { + "Ref": "AWS::Region" + }, + ".", + { + "Ref": "AWS::URLSuffix" + }, + "/aws-cdk/assets:3a8ba3ad06ed212b075efa3157fb407649c5996812bc64eeb5209e220aab4be5" + ] + ] + }, + "LogConfiguration": { + "LogDriver": "awslogs", + "Options": { + "awslogs-group": { + "Ref": "IsolatedQueueServiceQueueProcessingTaskDefQueueProcessingContainerLogGroupAEB959E6" + }, + "awslogs-stream-prefix": "IsolatedQueueService", + "awslogs-region": { + "Ref": "AWS::Region" + } + } + }, + "Name": "QueueProcessingContainer" + } + ], + "Cpu": "256", + "ExecutionRoleArn": { + "Fn::GetAtt": [ + "IsolatedQueueServiceQueueProcessingTaskDefExecutionRole1D7ACC77", + "Arn" + ] + }, + "Family": "awsecspatternsqueueisolatedIsolatedQueueServiceQueueProcessingTaskDef27DBAF49", + "Memory": "512", + "NetworkMode": "awsvpc", + "RequiresCompatibilities": [ + "FARGATE" + ], + "TaskRoleArn": { + "Fn::GetAtt": [ + "IsolatedQueueServiceQueueProcessingTaskDefTaskRoleCFCB7511", + "Arn" + ] + } + } + }, + "IsolatedQueueServiceQueueProcessingTaskDefQueueProcessingContainerLogGroupAEB959E6": { + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "IsolatedQueueServiceQueueProcessingTaskDefExecutionRole1D7ACC77": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "IsolatedQueueServiceQueueProcessingTaskDefExecutionRoleDefaultPolicy5667D265": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ecr:BatchCheckLayerAvailability", + "ecr:GetDownloadUrlForLayer", + "ecr:BatchGetImage" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ecr:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":repository/aws-cdk/assets" + ] + ] + } + }, + { + "Action": "ecr:GetAuthorizationToken", + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "IsolatedQueueServiceQueueProcessingTaskDefQueueProcessingContainerLogGroupAEB959E6", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "IsolatedQueueServiceQueueProcessingTaskDefExecutionRoleDefaultPolicy5667D265", + "Roles": [ + { + "Ref": "IsolatedQueueServiceQueueProcessingTaskDefExecutionRole1D7ACC77" + } + ] + } + }, + "IsolatedQueueServiceQueueProcessingFargateServiceE868AEE1": { + "Type": "AWS::ECS::Service", + "Properties": { + "Cluster": { + "Ref": "EcsDefaultClusterMnL3mNNYNVPC9C1EC7A3" + }, + "DeploymentConfiguration": { + "MaximumPercent": 200, + "MinimumHealthyPercent": 50 + }, + "DesiredCount": 1, + "EnableECSManagedTags": false, + "LaunchType": "FARGATE", + "NetworkConfiguration": { + "AwsvpcConfiguration": { + "AssignPublicIp": "DISABLED", + "SecurityGroups": [ + { + "Fn::GetAtt": [ + "MyCustomSGDE27C661", + "GroupId" + ] + } + ], + "Subnets": [ + { + "Ref": "VPCIsolatedSubnet1SubnetEBD00FC6" + }, + { + "Ref": "VPCIsolatedSubnet2Subnet4B1C8CAA" + } + ] + } + }, + "TaskDefinition": { + "Ref": "IsolatedQueueServiceQueueProcessingTaskDef0F0CE105" + } + }, + "DependsOn": [ + "VPCCloudWatchLogsEndpointE175AF65", + "VPCCloudWatchLogsEndpointSecurityGroup967DBC94", + "VPCEcrEndpointB4F98F37", + "VPCEcrEndpointSecurityGroup50ED8BA4", + "VPCEcrImageEndpointD55381DC", + "VPCEcrImageEndpointSecurityGroup83621638", + "VPCSqsEndpoint9A40D77F", + "VPCSqsEndpointSecurityGroupAE06A78D" + ] + }, + "IsolatedQueueServiceQueueProcessingFargateServiceTaskCountTargetB06FD17D": { + "Type": "AWS::ApplicationAutoScaling::ScalableTarget", + "Properties": { + "MaxCapacity": 2, + "MinCapacity": 1, + "ResourceId": { + "Fn::Join": [ + "", + [ + "service/", + { + "Ref": "EcsDefaultClusterMnL3mNNYNVPC9C1EC7A3" + }, + "/", + { + "Fn::GetAtt": [ + "IsolatedQueueServiceQueueProcessingFargateServiceE868AEE1", + "Name" + ] + } + ] + ] + }, + "RoleARN": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":role/aws-service-role/ecs.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_ECSService" + ] + ] + }, + "ScalableDimension": "ecs:service:DesiredCount", + "ServiceNamespace": "ecs" + }, + "DependsOn": [ + "VPCCloudWatchLogsEndpointE175AF65", + "VPCCloudWatchLogsEndpointSecurityGroup967DBC94", + "VPCEcrEndpointB4F98F37", + "VPCEcrEndpointSecurityGroup50ED8BA4", + "VPCEcrImageEndpointD55381DC", + "VPCEcrImageEndpointSecurityGroup83621638", + "VPCSqsEndpoint9A40D77F", + "VPCSqsEndpointSecurityGroupAE06A78D" + ] + }, + "IsolatedQueueServiceQueueProcessingFargateServiceTaskCountTargetCpuScaling2B518D9D": { + "Type": "AWS::ApplicationAutoScaling::ScalingPolicy", + "Properties": { + "PolicyName": "awsecspatternsqueueisolatedIsolatedQueueServiceQueueProcessingFargateServiceTaskCountTargetCpuScaling8B2FB6C4", + "PolicyType": "TargetTrackingScaling", + "ScalingTargetId": { + "Ref": "IsolatedQueueServiceQueueProcessingFargateServiceTaskCountTargetB06FD17D" + }, + "TargetTrackingScalingPolicyConfiguration": { + "PredefinedMetricSpecification": { + "PredefinedMetricType": "ECSServiceAverageCPUUtilization" + }, + "TargetValue": 50 + } + }, + "DependsOn": [ + "VPCCloudWatchLogsEndpointE175AF65", + "VPCCloudWatchLogsEndpointSecurityGroup967DBC94", + "VPCEcrEndpointB4F98F37", + "VPCEcrEndpointSecurityGroup50ED8BA4", + "VPCEcrImageEndpointD55381DC", + "VPCEcrImageEndpointSecurityGroup83621638", + "VPCSqsEndpoint9A40D77F", + "VPCSqsEndpointSecurityGroupAE06A78D" + ] + }, + "IsolatedQueueServiceQueueProcessingFargateServiceTaskCountTargetQueueMessagesVisibleScalingLowerPolicy960D4BA1": { + "Type": "AWS::ApplicationAutoScaling::ScalingPolicy", + "Properties": { + "PolicyName": "awsecspatternsqueueisolatedIsolatedQueueServiceQueueProcessingFargateServiceTaskCountTargetQueueMessagesVisibleScalingLowerPolicy5EFC8D1B", + "PolicyType": "StepScaling", + "ScalingTargetId": { + "Ref": "IsolatedQueueServiceQueueProcessingFargateServiceTaskCountTargetB06FD17D" + }, + "StepScalingPolicyConfiguration": { + "AdjustmentType": "ChangeInCapacity", + "MetricAggregationType": "Maximum", + "StepAdjustments": [ + { + "MetricIntervalUpperBound": 0, + "ScalingAdjustment": -1 + } + ] + } + }, + "DependsOn": [ + "VPCCloudWatchLogsEndpointE175AF65", + "VPCCloudWatchLogsEndpointSecurityGroup967DBC94", + "VPCEcrEndpointB4F98F37", + "VPCEcrEndpointSecurityGroup50ED8BA4", + "VPCEcrImageEndpointD55381DC", + "VPCEcrImageEndpointSecurityGroup83621638", + "VPCSqsEndpoint9A40D77F", + "VPCSqsEndpointSecurityGroupAE06A78D" + ] + }, + "IsolatedQueueServiceQueueProcessingFargateServiceTaskCountTargetQueueMessagesVisibleScalingLowerAlarm88D1A0F9": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "ComparisonOperator": "LessThanOrEqualToThreshold", + "EvaluationPeriods": 1, + "AlarmActions": [ + { + "Ref": "IsolatedQueueServiceQueueProcessingFargateServiceTaskCountTargetQueueMessagesVisibleScalingLowerPolicy960D4BA1" + } + ], + "AlarmDescription": "Lower threshold scaling alarm", + "Dimensions": [ + { + "Name": "QueueName", + "Value": { + "Fn::GetAtt": [ + "IsolatedQueueServiceEcsProcessingQueueCCE172F1", + "QueueName" + ] + } + } + ], + "MetricName": "ApproximateNumberOfMessagesVisible", + "Namespace": "AWS/SQS", + "Period": 300, + "Statistic": "Maximum", + "Threshold": 0 + }, + "DependsOn": [ + "VPCCloudWatchLogsEndpointE175AF65", + "VPCCloudWatchLogsEndpointSecurityGroup967DBC94", + "VPCEcrEndpointB4F98F37", + "VPCEcrEndpointSecurityGroup50ED8BA4", + "VPCEcrImageEndpointD55381DC", + "VPCEcrImageEndpointSecurityGroup83621638", + "VPCSqsEndpoint9A40D77F", + "VPCSqsEndpointSecurityGroupAE06A78D" + ] + }, + "IsolatedQueueServiceQueueProcessingFargateServiceTaskCountTargetQueueMessagesVisibleScalingUpperPolicyFAB35025": { + "Type": "AWS::ApplicationAutoScaling::ScalingPolicy", + "Properties": { + "PolicyName": "awsecspatternsqueueisolatedIsolatedQueueServiceQueueProcessingFargateServiceTaskCountTargetQueueMessagesVisibleScalingUpperPolicy51E582BF", + "PolicyType": "StepScaling", + "ScalingTargetId": { + "Ref": "IsolatedQueueServiceQueueProcessingFargateServiceTaskCountTargetB06FD17D" + }, + "StepScalingPolicyConfiguration": { + "AdjustmentType": "ChangeInCapacity", + "MetricAggregationType": "Maximum", + "StepAdjustments": [ + { + "MetricIntervalLowerBound": 0, + "MetricIntervalUpperBound": 400, + "ScalingAdjustment": 1 + }, + { + "MetricIntervalLowerBound": 400, + "ScalingAdjustment": 5 + } + ] + } + }, + "DependsOn": [ + "VPCCloudWatchLogsEndpointE175AF65", + "VPCCloudWatchLogsEndpointSecurityGroup967DBC94", + "VPCEcrEndpointB4F98F37", + "VPCEcrEndpointSecurityGroup50ED8BA4", + "VPCEcrImageEndpointD55381DC", + "VPCEcrImageEndpointSecurityGroup83621638", + "VPCSqsEndpoint9A40D77F", + "VPCSqsEndpointSecurityGroupAE06A78D" + ] + }, + "IsolatedQueueServiceQueueProcessingFargateServiceTaskCountTargetQueueMessagesVisibleScalingUpperAlarm351987F5": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "ComparisonOperator": "GreaterThanOrEqualToThreshold", + "EvaluationPeriods": 1, + "AlarmActions": [ + { + "Ref": "IsolatedQueueServiceQueueProcessingFargateServiceTaskCountTargetQueueMessagesVisibleScalingUpperPolicyFAB35025" + } + ], + "AlarmDescription": "Upper threshold scaling alarm", + "Dimensions": [ + { + "Name": "QueueName", + "Value": { + "Fn::GetAtt": [ + "IsolatedQueueServiceEcsProcessingQueueCCE172F1", + "QueueName" + ] + } + } + ], + "MetricName": "ApproximateNumberOfMessagesVisible", + "Namespace": "AWS/SQS", + "Period": 300, + "Statistic": "Maximum", + "Threshold": 100 + }, + "DependsOn": [ + "VPCCloudWatchLogsEndpointE175AF65", + "VPCCloudWatchLogsEndpointSecurityGroup967DBC94", + "VPCEcrEndpointB4F98F37", + "VPCEcrEndpointSecurityGroup50ED8BA4", + "VPCEcrImageEndpointD55381DC", + "VPCEcrImageEndpointSecurityGroup83621638", + "VPCSqsEndpoint9A40D77F", + "VPCSqsEndpointSecurityGroupAE06A78D" + ] + }, + "EcsDefaultClusterMnL3mNNYNVPC9C1EC7A3": { + "Type": "AWS::ECS::Cluster" + } + }, + "Outputs": { + "IsolatedQueueServiceSQSDeadLetterQueue43D346B9": { + "Value": { + "Fn::GetAtt": [ + "IsolatedQueueServiceEcsProcessingDeadLetterQueue7CC1D07D", + "QueueName" + ] + } + }, + "IsolatedQueueServiceSQSDeadLetterQueueArnCE7C60F2": { + "Value": { + "Fn::GetAtt": [ + "IsolatedQueueServiceEcsProcessingDeadLetterQueue7CC1D07D", + "Arn" + ] + } + }, + "IsolatedQueueServiceSQSQueueA65E2641": { + "Value": { + "Fn::GetAtt": [ + "IsolatedQueueServiceEcsProcessingQueueCCE172F1", + "QueueName" + ] + } + }, + "IsolatedQueueServiceSQSQueueArn571FDB86": { + "Value": { + "Fn::GetAtt": [ + "IsolatedQueueServiceEcsProcessingQueueCCE172F1", + "Arn" + ] + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service-isolated.ts b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service-isolated.ts new file mode 100644 index 0000000000000..f985ac8c9d564 --- /dev/null +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service-isolated.ts @@ -0,0 +1,56 @@ +import * as path from 'path'; +import * as ec2 from '@aws-cdk/aws-ec2'; +import * as ecs from '@aws-cdk/aws-ecs'; +import { App, Stack } from '@aws-cdk/core'; + +import { QueueProcessingFargateService } from '../../lib'; + +const app = new App(); +const stack = new Stack(app, 'aws-ecs-patterns-queue-isolated'); +const vpc = new ec2.Vpc(stack, 'VPC', { + maxAzs: 2, + subnetConfiguration: [ + { + cidrMask: 24, + name: 'Public', + subnetType: ec2.SubnetType.PUBLIC, + }, + { + cidrMask: 24, + name: 'Isolated', + subnetType: ec2.SubnetType.ISOLATED, + }, + ], +}); + + +vpc.addS3Endpoint('S3Endpoint', [{ subnetType: ec2.SubnetType.ISOLATED }]); + +const securityGroup = new ec2.SecurityGroup(stack, 'MyCustomSG', { + vpc, +}); + +const queueProcessing = new QueueProcessingFargateService(stack, 'IsolatedQueueService', { + vpc, + memoryLimitMiB: 512, + image: new ecs.AssetImage(path.join(__dirname, '..', 'sqs-reader')), + securityGroups: [securityGroup], + taskSubnets: { subnetType: ec2.SubnetType.ISOLATED }, +}); + +queueProcessing.service.node.addDependency( + vpc.addInterfaceEndpoint('SqsEndpoint', { + service: ec2.InterfaceVpcEndpointAwsService.SQS, + }), + vpc.addInterfaceEndpoint('EcrEndpoint', { + service: ec2.InterfaceVpcEndpointAwsService.ECR, + }), + vpc.addInterfaceEndpoint('EcrImageEndpoint', { + service: ec2.InterfaceVpcEndpointAwsService.ECR_DOCKER, + }), + vpc.addInterfaceEndpoint('CloudWatchLogsEndpoint', { + service: ec2.InterfaceVpcEndpointAwsService.CLOUDWATCH_LOGS, + }), +); + +app.synth(); diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service.expected.json new file mode 100644 index 0000000000000..3fd9825b7db3b --- /dev/null +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service.expected.json @@ -0,0 +1,838 @@ +{ + "Resources": { + "VPCB9E5F0B4": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-patterns-queue/VPC" + } + ] + } + }, + "VPCPublicSubnet1SubnetB4246D30": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.0.0/18", + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "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-patterns-queue/VPC/PublicSubnet1" + } + ] + } + }, + "VPCPublicSubnet1RouteTableFEE4B781": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-patterns-queue/VPC/PublicSubnet1" + } + ] + } + }, + "VPCPublicSubnet1RouteTableAssociation0B0896DC": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet1RouteTableFEE4B781" + }, + "SubnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + } + } + }, + "VPCPublicSubnet1DefaultRoute91CEF279": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet1RouteTableFEE4B781" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VPCIGWB7E252D3" + } + }, + "DependsOn": [ + "VPCVPCGW99B986DC" + ] + }, + "VPCPublicSubnet1EIP6AD938E8": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-patterns-queue/VPC/PublicSubnet1" + } + ] + } + }, + "VPCPublicSubnet1NATGatewayE0556630": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VPCPublicSubnet1EIP6AD938E8", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-patterns-queue/VPC/PublicSubnet1" + } + ] + } + }, + "VPCPublicSubnet2Subnet74179F39": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.64.0/18", + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "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-patterns-queue/VPC/PublicSubnet2" + } + ] + } + }, + "VPCPublicSubnet2RouteTable6F1A15F1": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-patterns-queue/VPC/PublicSubnet2" + } + ] + } + }, + "VPCPublicSubnet2RouteTableAssociation5A808732": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet2RouteTable6F1A15F1" + }, + "SubnetId": { + "Ref": "VPCPublicSubnet2Subnet74179F39" + } + } + }, + "VPCPublicSubnet2DefaultRouteB7481BBA": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet2RouteTable6F1A15F1" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VPCIGWB7E252D3" + } + }, + "DependsOn": [ + "VPCVPCGW99B986DC" + ] + }, + "VPCPublicSubnet2EIP4947BC00": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-patterns-queue/VPC/PublicSubnet2" + } + ] + } + }, + "VPCPublicSubnet2NATGateway3C070193": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VPCPublicSubnet2EIP4947BC00", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VPCPublicSubnet2Subnet74179F39" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-patterns-queue/VPC/PublicSubnet2" + } + ] + } + }, + "VPCPrivateSubnet1Subnet8BCA10E0": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.128.0/18", + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "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-patterns-queue/VPC/PrivateSubnet1" + } + ] + } + }, + "VPCPrivateSubnet1RouteTableBE8A6027": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-patterns-queue/VPC/PrivateSubnet1" + } + ] + } + }, + "VPCPrivateSubnet1RouteTableAssociation347902D1": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet1RouteTableBE8A6027" + }, + "SubnetId": { + "Ref": "VPCPrivateSubnet1Subnet8BCA10E0" + } + } + }, + "VPCPrivateSubnet1DefaultRouteAE1D6490": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet1RouteTableBE8A6027" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VPCPublicSubnet1NATGatewayE0556630" + } + } + }, + "VPCPrivateSubnet2SubnetCFCDAA7A": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.192.0/18", + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "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-patterns-queue/VPC/PrivateSubnet2" + } + ] + } + }, + "VPCPrivateSubnet2RouteTable0A19E10E": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-patterns-queue/VPC/PrivateSubnet2" + } + ] + } + }, + "VPCPrivateSubnet2RouteTableAssociation0C73D413": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet2RouteTable0A19E10E" + }, + "SubnetId": { + "Ref": "VPCPrivateSubnet2SubnetCFCDAA7A" + } + } + }, + "VPCPrivateSubnet2DefaultRouteF4F5CFD2": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet2RouteTable0A19E10E" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VPCPublicSubnet2NATGateway3C070193" + } + } + }, + "VPCIGWB7E252D3": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-patterns-queue/VPC" + } + ] + } + }, + "VPCVPCGW99B986DC": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "InternetGatewayId": { + "Ref": "VPCIGWB7E252D3" + } + } + }, + "QueueProcessingServiceEcsProcessingDeadLetterQueueD47A7C6B": { + "Type": "AWS::SQS::Queue", + "Properties": { + "MessageRetentionPeriod": 1209600 + } + }, + "QueueProcessingServiceEcsProcessingQueue552F0B37": { + "Type": "AWS::SQS::Queue", + "Properties": { + "RedrivePolicy": { + "deadLetterTargetArn": { + "Fn::GetAtt": [ + "QueueProcessingServiceEcsProcessingDeadLetterQueueD47A7C6B", + "Arn" + ] + }, + "maxReceiveCount": 3 + } + } + }, + "QueueProcessingServiceQueueProcessingTaskDefTaskRole782B79A6": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "QueueProcessingServiceQueueProcessingTaskDefTaskRoleDefaultPolicyAE808B19": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "sqs:ReceiveMessage", + "sqs:ChangeMessageVisibility", + "sqs:GetQueueUrl", + "sqs:DeleteMessage", + "sqs:GetQueueAttributes" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "QueueProcessingServiceEcsProcessingQueue552F0B37", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "QueueProcessingServiceQueueProcessingTaskDefTaskRoleDefaultPolicyAE808B19", + "Roles": [ + { + "Ref": "QueueProcessingServiceQueueProcessingTaskDefTaskRole782B79A6" + } + ] + } + }, + "QueueProcessingServiceQueueProcessingTaskDef4982F68B": { + "Type": "AWS::ECS::TaskDefinition", + "Properties": { + "ContainerDefinitions": [ + { + "Environment": [ + { + "Name": "QUEUE_NAME", + "Value": { + "Fn::GetAtt": [ + "QueueProcessingServiceEcsProcessingQueue552F0B37", + "QueueName" + ] + } + } + ], + "Essential": true, + "Image": { + "Fn::Join": [ + "", + [ + { + "Ref": "AWS::AccountId" + }, + ".dkr.ecr.", + { + "Ref": "AWS::Region" + }, + ".", + { + "Ref": "AWS::URLSuffix" + }, + "/aws-cdk/assets:3a8ba3ad06ed212b075efa3157fb407649c5996812bc64eeb5209e220aab4be5" + ] + ] + }, + "LogConfiguration": { + "LogDriver": "awslogs", + "Options": { + "awslogs-group": { + "Ref": "QueueProcessingServiceQueueProcessingTaskDefQueueProcessingContainerLogGroupCC92448A" + }, + "awslogs-stream-prefix": "QueueProcessingService", + "awslogs-region": { + "Ref": "AWS::Region" + } + } + }, + "Name": "QueueProcessingContainer" + } + ], + "Cpu": "256", + "ExecutionRoleArn": { + "Fn::GetAtt": [ + "QueueProcessingServiceQueueProcessingTaskDefExecutionRole37838985", + "Arn" + ] + }, + "Family": "awsecspatternsqueueQueueProcessingServiceQueueProcessingTaskDef2D9F8C2B", + "Memory": "512", + "NetworkMode": "awsvpc", + "RequiresCompatibilities": [ + "FARGATE" + ], + "TaskRoleArn": { + "Fn::GetAtt": [ + "QueueProcessingServiceQueueProcessingTaskDefTaskRole782B79A6", + "Arn" + ] + } + } + }, + "QueueProcessingServiceQueueProcessingTaskDefQueueProcessingContainerLogGroupCC92448A": { + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "QueueProcessingServiceQueueProcessingTaskDefExecutionRole37838985": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "QueueProcessingServiceQueueProcessingTaskDefExecutionRoleDefaultPolicyA83D332D": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ecr:BatchCheckLayerAvailability", + "ecr:GetDownloadUrlForLayer", + "ecr:BatchGetImage" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ecr:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":repository/aws-cdk/assets" + ] + ] + } + }, + { + "Action": "ecr:GetAuthorizationToken", + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "QueueProcessingServiceQueueProcessingTaskDefQueueProcessingContainerLogGroupCC92448A", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "QueueProcessingServiceQueueProcessingTaskDefExecutionRoleDefaultPolicyA83D332D", + "Roles": [ + { + "Ref": "QueueProcessingServiceQueueProcessingTaskDefExecutionRole37838985" + } + ] + } + }, + "QueueProcessingServiceQueueProcessingFargateService0340DB9F": { + "Type": "AWS::ECS::Service", + "Properties": { + "Cluster": { + "Ref": "EcsDefaultClusterMnL3mNNYNVPC9C1EC7A3" + }, + "DeploymentConfiguration": { + "MaximumPercent": 200, + "MinimumHealthyPercent": 50 + }, + "DesiredCount": 1, + "EnableECSManagedTags": false, + "LaunchType": "FARGATE", + "NetworkConfiguration": { + "AwsvpcConfiguration": { + "AssignPublicIp": "DISABLED", + "SecurityGroups": [ + { + "Fn::GetAtt": [ + "QueueProcessingServiceQueueProcessingFargateServiceSecurityGroup8FDF413D", + "GroupId" + ] + } + ], + "Subnets": [ + { + "Ref": "VPCPrivateSubnet1Subnet8BCA10E0" + }, + { + "Ref": "VPCPrivateSubnet2SubnetCFCDAA7A" + } + ] + } + }, + "TaskDefinition": { + "Ref": "QueueProcessingServiceQueueProcessingTaskDef4982F68B" + } + } + }, + "QueueProcessingServiceQueueProcessingFargateServiceSecurityGroup8FDF413D": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "aws-ecs-patterns-queue/QueueProcessingService/QueueProcessingFargateService/SecurityGroup", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "QueueProcessingServiceQueueProcessingFargateServiceTaskCountTargetA9D54444": { + "Type": "AWS::ApplicationAutoScaling::ScalableTarget", + "Properties": { + "MaxCapacity": 2, + "MinCapacity": 1, + "ResourceId": { + "Fn::Join": [ + "", + [ + "service/", + { + "Ref": "EcsDefaultClusterMnL3mNNYNVPC9C1EC7A3" + }, + "/", + { + "Fn::GetAtt": [ + "QueueProcessingServiceQueueProcessingFargateService0340DB9F", + "Name" + ] + } + ] + ] + }, + "RoleARN": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":role/aws-service-role/ecs.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_ECSService" + ] + ] + }, + "ScalableDimension": "ecs:service:DesiredCount", + "ServiceNamespace": "ecs" + } + }, + "QueueProcessingServiceQueueProcessingFargateServiceTaskCountTargetCpuScaling330150E9": { + "Type": "AWS::ApplicationAutoScaling::ScalingPolicy", + "Properties": { + "PolicyName": "awsecspatternsqueueQueueProcessingServiceQueueProcessingFargateServiceTaskCountTargetCpuScaling374CE648", + "PolicyType": "TargetTrackingScaling", + "ScalingTargetId": { + "Ref": "QueueProcessingServiceQueueProcessingFargateServiceTaskCountTargetA9D54444" + }, + "TargetTrackingScalingPolicyConfiguration": { + "PredefinedMetricSpecification": { + "PredefinedMetricType": "ECSServiceAverageCPUUtilization" + }, + "TargetValue": 50 + } + } + }, + "QueueProcessingServiceQueueProcessingFargateServiceTaskCountTargetQueueMessagesVisibleScalingLowerPolicy332E2644": { + "Type": "AWS::ApplicationAutoScaling::ScalingPolicy", + "Properties": { + "PolicyName": "awsecspatternsqueueQueueProcessingServiceQueueProcessingFargateServiceTaskCountTargetQueueMessagesVisibleScalingLowerPolicy74582401", + "PolicyType": "StepScaling", + "ScalingTargetId": { + "Ref": "QueueProcessingServiceQueueProcessingFargateServiceTaskCountTargetA9D54444" + }, + "StepScalingPolicyConfiguration": { + "AdjustmentType": "ChangeInCapacity", + "MetricAggregationType": "Maximum", + "StepAdjustments": [ + { + "MetricIntervalUpperBound": 0, + "ScalingAdjustment": -1 + } + ] + } + } + }, + "QueueProcessingServiceQueueProcessingFargateServiceTaskCountTargetQueueMessagesVisibleScalingLowerAlarm20C30A06": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "ComparisonOperator": "LessThanOrEqualToThreshold", + "EvaluationPeriods": 1, + "AlarmActions": [ + { + "Ref": "QueueProcessingServiceQueueProcessingFargateServiceTaskCountTargetQueueMessagesVisibleScalingLowerPolicy332E2644" + } + ], + "AlarmDescription": "Lower threshold scaling alarm", + "Dimensions": [ + { + "Name": "QueueName", + "Value": { + "Fn::GetAtt": [ + "QueueProcessingServiceEcsProcessingQueue552F0B37", + "QueueName" + ] + } + } + ], + "MetricName": "ApproximateNumberOfMessagesVisible", + "Namespace": "AWS/SQS", + "Period": 300, + "Statistic": "Maximum", + "Threshold": 0 + } + }, + "QueueProcessingServiceQueueProcessingFargateServiceTaskCountTargetQueueMessagesVisibleScalingUpperPolicy84DD739A": { + "Type": "AWS::ApplicationAutoScaling::ScalingPolicy", + "Properties": { + "PolicyName": "awsecspatternsqueueQueueProcessingServiceQueueProcessingFargateServiceTaskCountTargetQueueMessagesVisibleScalingUpperPolicy23C5F983", + "PolicyType": "StepScaling", + "ScalingTargetId": { + "Ref": "QueueProcessingServiceQueueProcessingFargateServiceTaskCountTargetA9D54444" + }, + "StepScalingPolicyConfiguration": { + "AdjustmentType": "ChangeInCapacity", + "MetricAggregationType": "Maximum", + "StepAdjustments": [ + { + "MetricIntervalLowerBound": 0, + "MetricIntervalUpperBound": 400, + "ScalingAdjustment": 1 + }, + { + "MetricIntervalLowerBound": 400, + "ScalingAdjustment": 5 + } + ] + } + } + }, + "QueueProcessingServiceQueueProcessingFargateServiceTaskCountTargetQueueMessagesVisibleScalingUpperAlarm2660BEDF": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "ComparisonOperator": "GreaterThanOrEqualToThreshold", + "EvaluationPeriods": 1, + "AlarmActions": [ + { + "Ref": "QueueProcessingServiceQueueProcessingFargateServiceTaskCountTargetQueueMessagesVisibleScalingUpperPolicy84DD739A" + } + ], + "AlarmDescription": "Upper threshold scaling alarm", + "Dimensions": [ + { + "Name": "QueueName", + "Value": { + "Fn::GetAtt": [ + "QueueProcessingServiceEcsProcessingQueue552F0B37", + "QueueName" + ] + } + } + ], + "MetricName": "ApproximateNumberOfMessagesVisible", + "Namespace": "AWS/SQS", + "Period": 300, + "Statistic": "Maximum", + "Threshold": 100 + } + }, + "EcsDefaultClusterMnL3mNNYNVPC9C1EC7A3": { + "Type": "AWS::ECS::Cluster" + } + }, + "Outputs": { + "QueueProcessingServiceSQSDeadLetterQueueE9058015": { + "Value": { + "Fn::GetAtt": [ + "QueueProcessingServiceEcsProcessingDeadLetterQueueD47A7C6B", + "QueueName" + ] + } + }, + "QueueProcessingServiceSQSDeadLetterQueueArnF7C6D3A8": { + "Value": { + "Fn::GetAtt": [ + "QueueProcessingServiceEcsProcessingDeadLetterQueueD47A7C6B", + "Arn" + ] + } + }, + "QueueProcessingServiceSQSQueue1AD9CD9C": { + "Value": { + "Fn::GetAtt": [ + "QueueProcessingServiceEcsProcessingQueue552F0B37", + "QueueName" + ] + } + }, + "QueueProcessingServiceSQSQueueArn8C4AE4AE": { + "Value": { + "Fn::GetAtt": [ + "QueueProcessingServiceEcsProcessingQueue552F0B37", + "Arn" + ] + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service.ts b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service.ts new file mode 100644 index 0000000000000..540b8d2302e9a --- /dev/null +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.queue-processing-fargate-service.ts @@ -0,0 +1,20 @@ +import * as path from 'path'; +import * as ec2 from '@aws-cdk/aws-ec2'; +import * as ecs from '@aws-cdk/aws-ecs'; +import { App, Stack } from '@aws-cdk/core'; + +import { QueueProcessingFargateService } from '../../lib'; + +const app = new App(); +const stack = new Stack(app, 'aws-ecs-patterns-queue'); +const vpc = new ec2.Vpc(stack, 'VPC', { + maxAzs: 2, +}); + +new QueueProcessingFargateService(stack, 'QueueProcessingService', { + vpc, + memoryLimitMiB: 512, + image: new ecs.AssetImage(path.join(__dirname, '..', 'sqs-reader')), +}); + +app.synth(); diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/test.queue-processing-fargate-service.ts b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/test.queue-processing-fargate-service.ts index a0c09d572343f..0bee7da1c51f3 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/test.queue-processing-fargate-service.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/test.queue-processing-fargate-service.ts @@ -310,4 +310,62 @@ export = { test.done(); }, + + 'can set custom networking options'(test: Test) { + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'VPC', { + subnetConfiguration: [ + { + cidrMask: 24, + name: 'Public', + subnetType: ec2.SubnetType.PUBLIC, + }, + { + cidrMask: 24, + name: 'Isolated', + subnetType: ec2.SubnetType.ISOLATED, + }, + ], + }); + const securityGroup = new ec2.SecurityGroup(stack, 'MyCustomSG', { + vpc, + }); + + // WHEN + new ecsPatterns.QueueProcessingFargateService(stack, 'Service', { + vpc, + memoryLimitMiB: 512, + image: ecs.ContainerImage.fromRegistry('test'), + securityGroups: [securityGroup], + taskSubnets: { subnetType: ec2.SubnetType.ISOLATED }, + }); + + // THEN - QueueWorker is of FARGATE launch type, an SQS queue is created and all default properties are set. + expect(stack).to(haveResource('AWS::ECS::Service', { + LaunchType: 'FARGATE', + NetworkConfiguration: { + AwsvpcConfiguration: { + AssignPublicIp: 'DISABLED', + SecurityGroups: [ + { + 'Fn::GetAtt': [ + 'MyCustomSGDE27C661', + 'GroupId', + ], + }, + ], + Subnets: [ + { + Ref: 'VPCIsolatedSubnet1SubnetEBD00FC6', + }, + { + Ref: 'VPCIsolatedSubnet2Subnet4B1C8CAA', + }, + ], + }, + }, + })); + + test.done(); + }, }; diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/test.scheduled-fargate-task.ts b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/test.scheduled-fargate-task.ts index fa62f079862f1..9496288f3b235 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/test.scheduled-fargate-task.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/test.scheduled-fargate-task.ts @@ -24,6 +24,7 @@ export = { // THEN expect(stack).to(haveResource('AWS::Events::Rule', { + State: 'ENABLED', Targets: [ { Arn: { 'Fn::GetAtt': ['EcsCluster97242B84', 'Arn'] }, @@ -90,6 +91,7 @@ export = { new ScheduledFargateTask(stack, 'ScheduledFargateTask', { cluster, + enabled: false, scheduledFargateTaskImageOptions: { image: ecs.ContainerImage.fromRegistry('henk'), memoryLimitMiB: 512, @@ -104,6 +106,7 @@ export = { // THEN expect(stack).to(haveResource('AWS::Events::Rule', { Name: 'sample-scheduled-task-rule', + State: 'DISABLED', Targets: [ { Arn: { 'Fn::GetAtt': ['EcsCluster97242B84', 'Arn'] }, diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/sqs-reader/Dockerfile b/packages/@aws-cdk/aws-ecs-patterns/test/sqs-reader/Dockerfile new file mode 100644 index 0000000000000..e6618640549e2 --- /dev/null +++ b/packages/@aws-cdk/aws-ecs-patterns/test/sqs-reader/Dockerfile @@ -0,0 +1,10 @@ +FROM python:3.6 + +RUN pip3 install boto3 + +ENV QUEUE_NAME $QUEUE_NAME + +WORKDIR /src +ADD . /src + +CMD python3 index.py diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/sqs-reader/index.py b/packages/@aws-cdk/aws-ecs-patterns/test/sqs-reader/index.py new file mode 100644 index 0000000000000..8b53f5149cb24 --- /dev/null +++ b/packages/@aws-cdk/aws-ecs-patterns/test/sqs-reader/index.py @@ -0,0 +1,22 @@ +#!/usr/bin/python +import os +import boto3 + +QUEUE_NAME = os.environ.get('QUEUE_NAME') +print('QUEUE_NAME ' + QUEUE_NAME) + +if __name__ == '__main__': + client = boto3.client('sqs') + queue_url = client.get_queue_url(QueueName=QUEUE_NAME)['QueueUrl'] + print('queue_url ' + queue_url) + while True: + response = client.receive_message( + QueueUrl=queue_url, + WaitTimeSeconds=10, + ) + if response and 'Messages' in response: + for msg in response['Messages']: + print(msg['Body']) + entries = [{'Id': x['MessageId'], 'ReceiptHandle': x['ReceiptHandle']} for x in response['Messages']] + client.delete_message_batch(QueueUrl=queue_url, Entries=entries) + diff --git a/packages/@aws-cdk/aws-ecs/README.md b/packages/@aws-cdk/aws-ecs/README.md index f2056229c6bf5..438f63e14e009 100644 --- a/packages/@aws-cdk/aws-ecs/README.md +++ b/packages/@aws-cdk/aws-ecs/README.md @@ -670,3 +670,50 @@ taskDefinition.addContainer('TheContainer', { }) }); ``` + +## 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'; + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'aws-ecs-integ-capacity-provider'); + +const vpc = new ec2.Vpc(stack, 'Vpc', { maxAzs: 2 }); + +const cluster = new ecs.Cluster(stack, 'FargateCPCluster', { + vpc, + capacityProviders: ['FARGATE', 'FARGATE_SPOT'], +}); + +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, + capacityProviderStrategies: [ + { + capacityProvider: 'FARGATE_SPOT', + weight: 2, + }, + { + capacityProvider: 'FARGATE', + weight: 1, + } + ], +}); + +app.synth(); +``` 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 5c5160a849835..8e2483f98838f 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 } from '../cluster'; +import { ICluster, CapacityProviderStrategy } from '../cluster'; import { Protocol } from '../container-definition'; import { CfnService } from '../ecs.generated'; import { ScalableTaskCount } from './scalable-task-count'; @@ -181,6 +181,14 @@ export interface BaseServiceOptions { * @default - disabled */ readonly circuitBreaker?: DeploymentCircuitBreaker; + + /** + * A list of Capacity Provider strategies used to place a service. + * + * @default - undefined + * + */ + readonly capacityProviderStrategies?: CapacityProviderStrategy[]; } /** @@ -191,6 +199,10 @@ export interface BaseServiceProps extends BaseServiceOptions { /** * The launch type on which to run your service. * + * LaunchType will be omitted if capacity provider strategies are specified on the service. + * + * @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 */ readonly launchType: LaunchType; @@ -356,6 +368,11 @@ export abstract class BaseService extends Resource this.taskDefinition = taskDefinition; + // launchType will set to undefined if using external DeploymentController or capacityProviderStrategies + const launchType = props.deploymentController?.type === DeploymentControllerType.EXTERNAL || + props.capacityProviderStrategies !== undefined ? + undefined : props.launchType; + this.resource = new CfnService(this, 'Service', { desiredCount: props.desiredCount, serviceName: this.physicalName, @@ -371,7 +388,8 @@ export abstract class BaseService extends Resource propagateTags: props.propagateTags === PropagatedTagSource.NONE ? undefined : props.propagateTags, enableEcsManagedTags: props.enableECSManagedTags ?? false, deploymentController: props.deploymentController, - launchType: props.deploymentController?.type === DeploymentControllerType.EXTERNAL ? undefined : props.launchType, + launchType: launchType, + capacityProviderStrategy: props.capacityProviderStrategies, healthCheckGracePeriodSeconds: this.evaluateHealthGracePeriod(props.healthCheckGracePeriod), /* role: never specified, supplanted by Service Linked Role */ networkConfiguration: Lazy.any({ produce: () => this.networkConfiguration }, { omitEmptyArray: true }), diff --git a/packages/@aws-cdk/aws-ecs/lib/cluster.ts b/packages/@aws-cdk/aws-ecs/lib/cluster.ts index ca84679e7b970..c60fc9f4b1dee 100644 --- a/packages/@aws-cdk/aws-ecs/lib/cluster.ts +++ b/packages/@aws-cdk/aws-ecs/lib/cluster.ts @@ -5,7 +5,7 @@ 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, IResource, Resource, Stack } from '@aws-cdk/core'; +import { Duration, Lazy, IResource, Resource, Stack } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { InstanceDrainHook } from './drain-hook/instance-drain-hook'; import { ECSMetrics } from './ecs-canned-metrics.generated'; @@ -48,6 +48,13 @@ export interface ClusterProps { */ readonly capacity?: AddCapacityOptions; + /** + * The capacity providers to add to the cluster + * + * @default - None. Currently only FARGATE and FARGATE_SPOT are supported. + */ + readonly capacityProviders?: string[]; + /** * If true CloudWatch Container Insights will be enabled for the cluster * @@ -101,6 +108,13 @@ 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. + */ + private _capacityProviders: string[] = []; + /** * The AWS Cloud Map namespace to associate with the cluster. */ @@ -134,9 +148,12 @@ export class Cluster extends Resource implements ICluster { clusterSettings = [{ name: 'containerInsights', value: props.containerInsights ? ContainerInsights.ENABLED : ContainerInsights.DISABLED }]; } + this._capacityProviders = props.capacityProviders ?? []; + const cluster = new CfnCluster(this, 'Resource', { clusterName: this.physicalName, clusterSettings, + capacityProviders: Lazy.list({ produce: () => this._capacityProviders }, { omitEmpty: true }), }); this.clusterArn = this.getResourceArnAttribute(cluster.attrArn, { @@ -148,6 +165,7 @@ export class Cluster extends Resource implements ICluster { this.vpc = props.vpc || new ec2.Vpc(this, 'Vpc', { maxAzs: 2 }); + this._defaultCloudMapNamespace = props.defaultCloudMapNamespace !== undefined ? this.addDefaultCloudMapNamespace(props.defaultCloudMapNamespace) : undefined; @@ -323,6 +341,21 @@ export class Cluster extends Resource implements ICluster { } } + /** + * addCapacityProvider adds the name of a capacityProvider to the list of supproted capacityProviders for a cluster. + * + * @param provider the capacity provider to add to this 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); + } + } + private configureWindowsAutoScalingGroup(autoScalingGroup: autoscaling.AutoScalingGroup, options: AddAutoScalingGroupCapacityOptions = {}) { // clear the cache of the agent autoScalingGroup.addUserData('Remove-Item -Recurse C:\\ProgramData\\Amazon\\ECS\\Cache'); @@ -934,3 +967,33 @@ enum ContainerInsights { */ DISABLED = 'disabled', } + +/** + * A Capacity Provider strategy to use for the service. + * + * NOTE: defaultCapacityProviderStrategy on cluster not currently supported. + */ +export interface CapacityProviderStrategy { + /** + * The name of the Capacity Provider. Currently only FARGATE and FARGATE_SPOT are supported. + */ + readonly capacityProvider: string; + + /** + * The base value designates how many tasks, at a minimum, to run on the specified capacity provider. Only one + * capacity provider in a capacity provider strategy can have a base defined. If no value is specified, the default + * value of 0 is used. + * + * @default - none + */ + readonly base?: number; + + /** + * The weight value designates the relative percentage of the total number of tasks launched that should use the + * specified +capacity provider. The weight value is taken into consideration after the base value, if defined, is satisfied. + * + * @default - 0 + */ + readonly weight?: number; +} diff --git a/packages/@aws-cdk/aws-ecs/lib/fargate/fargate-service.ts b/packages/@aws-cdk/aws-ecs/lib/fargate/fargate-service.ts index 01a9f75665c57..1db94fc5286e0 100644 --- a/packages/@aws-cdk/aws-ecs/lib/fargate/fargate-service.ts +++ b/packages/@aws-cdk/aws-ecs/lib/fargate/fargate-service.ts @@ -67,6 +67,7 @@ export interface FargateServiceProps extends BaseServiceOptions { * @default PropagatedTagSource.NONE */ readonly propagateTaskTagsFrom?: PropagatedTagSource; + } /** @@ -153,6 +154,7 @@ export class FargateService extends BaseService implements IFargateService { ...props, desiredCount: props.desiredCount, launchType: LaunchType.FARGATE, + capacityProviderStrategies: props.capacityProviderStrategies, propagateTags: propagateTagsFromSource, enableECSManagedTags: props.enableECSManagedTags, }, { diff --git a/packages/@aws-cdk/aws-ecs/package.json b/packages/@aws-cdk/aws-ecs/package.json index 86da4b1cfde8e..418619f6ee09c 100644 --- a/packages/@aws-cdk/aws-ecs/package.json +++ b/packages/@aws-cdk/aws-ecs/package.json @@ -271,5 +271,8 @@ "awscdkio": { "announce": false }, - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.app-mesh-proxy-config.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.app-mesh-proxy-config.expected.json index 176bf38d788a5..7a40a11d952ca 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.app-mesh-proxy-config.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.app-mesh-proxy-config.expected.json @@ -698,14 +698,12 @@ "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 while has_tasks(cluster, instance_arn):\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\n\ndef has_tasks(cluster, instance_arn):\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 tasks = instance['runningTasksCount'] + instance['pendingTasksCount']\n print('Instance %s has %s tasks' % (instance_arn, tasks))\n\n return tasks > 0\n\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" }, - "Handler": "index.lambda_handler", "Role": { "Fn::GetAtt": [ "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA", "Arn" ] }, - "Runtime": "python3.6", "Environment": { "Variables": { "CLUSTER": { @@ -713,6 +711,8 @@ } } }, + "Handler": "index.lambda_handler", + "Runtime": "python3.6", "Tags": [ { "Key": "Name", diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.bottlerocket.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.bottlerocket.expected.json index 3644a517b73af..23ba70a23584d 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.bottlerocket.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.bottlerocket.expected.json @@ -692,14 +692,12 @@ "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 while has_tasks(cluster, instance_arn):\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\n\ndef has_tasks(cluster, instance_arn):\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 tasks = instance['runningTasksCount'] + instance['pendingTasksCount']\n print('Instance %s has %s tasks' % (instance_arn, tasks))\n\n return tasks > 0\n\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" }, - "Handler": "index.lambda_handler", "Role": { "Fn::GetAtt": [ "EcsClusterbottlerocketasgDrainECSHookFunctionServiceRole2F16AFAB", "Arn" ] }, - "Runtime": "python3.6", "Environment": { "Variables": { "CLUSTER": { @@ -707,6 +705,8 @@ } } }, + "Handler": "index.lambda_handler", + "Runtime": "python3.6", "Tags": [ { "Key": "Name", diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.clb-host-nw.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.clb-host-nw.expected.json index 67289e175fa8b..3a323b0d36eff 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.clb-host-nw.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.clb-host-nw.expected.json @@ -719,14 +719,12 @@ "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 while has_tasks(cluster, instance_arn):\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\n\ndef has_tasks(cluster, instance_arn):\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 tasks = instance['runningTasksCount'] + instance['pendingTasksCount']\n print('Instance %s has %s tasks' % (instance_arn, tasks))\n\n return tasks > 0\n\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" }, - "Handler": "index.lambda_handler", "Role": { "Fn::GetAtt": [ "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA", "Arn" ] }, - "Runtime": "python3.6", "Environment": { "Variables": { "CLUSTER": { @@ -734,6 +732,8 @@ } } }, + "Handler": "index.lambda_handler", + "Runtime": "python3.6", "Tags": [ { "Key": "Name", diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.firelens-s3-config.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.firelens-s3-config.expected.json index d76ccbf580d80..967d38891c487 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.firelens-s3-config.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.firelens-s3-config.expected.json @@ -698,14 +698,12 @@ "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 while has_tasks(cluster, instance_arn):\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\n\ndef has_tasks(cluster, instance_arn):\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 tasks = instance['runningTasksCount'] + instance['pendingTasksCount']\n print('Instance %s has %s tasks' % (instance_arn, tasks))\n\n return tasks > 0\n\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" }, - "Handler": "index.lambda_handler", "Role": { "Fn::GetAtt": [ "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA", "Arn" ] }, - "Runtime": "python3.6", "Environment": { "Variables": { "CLUSTER": { @@ -713,6 +711,8 @@ } } }, + "Handler": "index.lambda_handler", + "Runtime": "python3.6", "Tags": [ { "Key": "Name", diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-awsvpc-nw.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-awsvpc-nw.expected.json index a8f0ceeeebb15..90fd28f84cef2 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-awsvpc-nw.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-awsvpc-nw.expected.json @@ -698,14 +698,12 @@ "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 while has_tasks(cluster, instance_arn):\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\n\ndef has_tasks(cluster, instance_arn):\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 tasks = instance['runningTasksCount'] + instance['pendingTasksCount']\n print('Instance %s has %s tasks' % (instance_arn, tasks))\n\n return tasks > 0\n\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" }, - "Handler": "index.lambda_handler", "Role": { "Fn::GetAtt": [ "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA", "Arn" ] }, - "Runtime": "python3.6", "Environment": { "Variables": { "CLUSTER": { @@ -713,6 +711,8 @@ } } }, + "Handler": "index.lambda_handler", + "Runtime": "python3.6", "Tags": [ { "Key": "Name", diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-bridge-nw.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-bridge-nw.expected.json index 2b0faf9e340f1..0737785f5fea4 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-bridge-nw.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-bridge-nw.expected.json @@ -719,14 +719,12 @@ "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 while has_tasks(cluster, instance_arn):\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\n\ndef has_tasks(cluster, instance_arn):\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 tasks = instance['runningTasksCount'] + instance['pendingTasksCount']\n print('Instance %s has %s tasks' % (instance_arn, tasks))\n\n return tasks > 0\n\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" }, - "Handler": "index.lambda_handler", "Role": { "Fn::GetAtt": [ "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA", "Arn" ] }, - "Runtime": "python3.6", "Environment": { "Variables": { "CLUSTER": { @@ -734,6 +732,8 @@ } } }, + "Handler": "index.lambda_handler", + "Runtime": "python3.6", "Tags": [ { "Key": "Name", diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-awsvpc-nw.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-awsvpc-nw.expected.json index 3cc8789e3021a..b4e2c72807b0e 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-awsvpc-nw.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-awsvpc-nw.expected.json @@ -698,14 +698,12 @@ "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 while has_tasks(cluster, instance_arn):\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\n\ndef has_tasks(cluster, instance_arn):\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 tasks = instance['runningTasksCount'] + instance['pendingTasksCount']\n print('Instance %s has %s tasks' % (instance_arn, tasks))\n\n return tasks > 0\n\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" }, - "Handler": "index.lambda_handler", "Role": { "Fn::GetAtt": [ "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA", "Arn" ] }, - "Runtime": "python3.6", "Environment": { "Variables": { "CLUSTER": { @@ -713,6 +711,8 @@ } } }, + "Handler": "index.lambda_handler", + "Runtime": "python3.6", "Tags": [ { "Key": "Name", diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-bridge-nw.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-bridge-nw.expected.json index 49e99b166b764..ef42895037361 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-bridge-nw.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-bridge-nw.expected.json @@ -698,14 +698,12 @@ "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 while has_tasks(cluster, instance_arn):\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\n\ndef has_tasks(cluster, instance_arn):\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 tasks = instance['runningTasksCount'] + instance['pendingTasksCount']\n print('Instance %s has %s tasks' % (instance_arn, tasks))\n\n return tasks > 0\n\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" }, - "Handler": "index.lambda_handler", "Role": { "Fn::GetAtt": [ "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA", "Arn" ] }, - "Runtime": "python3.6", "Environment": { "Variables": { "CLUSTER": { @@ -713,6 +711,8 @@ } } }, + "Handler": "index.lambda_handler", + "Runtime": "python3.6", "Tags": [ { "Key": "Name", diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.spot-drain.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.spot-drain.expected.json index ae30eab9d2419..77acf122d0168 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.spot-drain.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.spot-drain.expected.json @@ -700,14 +700,12 @@ "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 while has_tasks(cluster, instance_arn):\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\n\ndef has_tasks(cluster, instance_arn):\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 tasks = instance['runningTasksCount'] + instance['pendingTasksCount']\n print('Instance %s has %s tasks' % (instance_arn, tasks))\n\n return tasks > 0\n\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" }, - "Handler": "index.lambda_handler", "Role": { "Fn::GetAtt": [ "EcsClusterasgSpotDrainECSHookFunctionServiceRole8EEDDFE0", "Arn" ] }, - "Runtime": "python3.6", "Environment": { "Variables": { "CLUSTER": { @@ -715,6 +713,8 @@ } } }, + "Handler": "index.lambda_handler", + "Runtime": "python3.6", "Tags": [ { "Key": "Name", @@ -1181,14 +1181,12 @@ "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 while has_tasks(cluster, instance_arn):\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\n\ndef has_tasks(cluster, instance_arn):\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 tasks = instance['runningTasksCount'] + instance['pendingTasksCount']\n print('Instance %s has %s tasks' % (instance_arn, tasks))\n\n return tasks > 0\n\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" }, - "Handler": "index.lambda_handler", "Role": { "Fn::GetAtt": [ "EcsClusterasgOdDrainECSHookFunctionServiceRoleFC088D55", "Arn" ] }, - "Runtime": "python3.6", "Environment": { "Variables": { "CLUSTER": { @@ -1196,6 +1194,8 @@ } } }, + "Handler": "index.lambda_handler", + "Runtime": "python3.6", "Tags": [ { "Key": "Name", diff --git a/packages/@aws-cdk/aws-ecs/test/fargate/integ.capacity-providers.expected.json b/packages/@aws-cdk/aws-ecs/test/fargate/integ.capacity-providers.expected.json new file mode 100644 index 0000000000000..c0281dc8e14ae --- /dev/null +++ b/packages/@aws-cdk/aws-ecs/test/fargate/integ.capacity-providers.expected.json @@ -0,0 +1,473 @@ +{ + "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-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": "aws-ecs-integ-capacity-provider/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1RouteTable6C95E38E": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-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": "aws-ecs-integ-capacity-provider/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1NATGateway4D7517AA": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet1EIPD7E02669", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-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": "aws-ecs-integ-capacity-provider/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2RouteTable94F7E489": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-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": "aws-ecs-integ-capacity-provider/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2NATGateway9182C01D": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet2EIP3C605A87", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-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": "aws-ecs-integ-capacity-provider/Vpc/PrivateSubnet1" + } + ] + } + }, + "VpcPrivateSubnet1RouteTableB2C5B500": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-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": "aws-ecs-integ-capacity-provider/Vpc/PrivateSubnet2" + } + ] + } + }, + "VpcPrivateSubnet2RouteTableA678073B": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-ecs-integ-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": "aws-ecs-integ-capacity-provider/Vpc" + } + ] + } + }, + "VpcVPCGWBF912B6E": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "InternetGatewayId": { + "Ref": "VpcIGWD7BA715C" + } + } + }, + "FargateCPCluster668E71F2": { + "Type": "AWS::ECS::Cluster", + "Properties": { + "CapacityProviders": [ + "FARGATE", + "FARGATE_SPOT" + ] + } + }, + "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", + "Name": "web" + } + ], + "Cpu": "256", + "Family": "awsecsintegcapacityproviderTaskDef80D341CB", + "Memory": "512", + "NetworkMode": "awsvpc", + "RequiresCompatibilities": [ + "FARGATE" + ], + "TaskRoleArn": { + "Fn::GetAtt": [ + "TaskDefTaskRole1EDB4A67", + "Arn" + ] + } + } + }, + "FargateServiceAC2B3B85": { + "Type": "AWS::ECS::Service", + "Properties": { + "CapacityProviderStrategy": [ + { + "CapacityProvider": "FARGATE_SPOT", + "Weight": 2 + }, + { + "CapacityProvider": "FARGATE", + "Weight": 1 + } + ], + "Cluster": { + "Ref": "FargateCPCluster668E71F2" + }, + "DeploymentConfiguration": { + "MaximumPercent": 200, + "MinimumHealthyPercent": 50 + }, + "EnableECSManagedTags": false, + "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-capacity-provider/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.capacity-providers.ts b/packages/@aws-cdk/aws-ecs/test/fargate/integ.capacity-providers.ts new file mode 100644 index 0000000000000..673b15284a577 --- /dev/null +++ b/packages/@aws-cdk/aws-ecs/test/fargate/integ.capacity-providers.ts @@ -0,0 +1,37 @@ +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, 'aws-ecs-integ-capacity-provider'); + +const vpc = new ec2.Vpc(stack, 'Vpc', { maxAzs: 2 }); + +const cluster = new ecs.Cluster(stack, 'FargateCPCluster', { + vpc, + capacityProviders: ['FARGATE', 'FARGATE_SPOT'], +}); + +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, + capacityProviderStrategies: [ + { + capacityProvider: 'FARGATE_SPOT', + weight: 2, + }, + { + capacityProvider: 'FARGATE', + weight: 1, + }, + ], +}); + +app.synth(); + diff --git a/packages/@aws-cdk/aws-ecs/test/fargate/test.fargate-service.ts b/packages/@aws-cdk/aws-ecs/test/fargate/test.fargate-service.ts index 85ddcd6a07176..ebb36580d19ba 100644 --- a/packages/@aws-cdk/aws-ecs/test/fargate/test.fargate-service.ts +++ b/packages/@aws-cdk/aws-ecs/test/fargate/test.fargate-service.ts @@ -110,6 +110,91 @@ export = { 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, + capacityProviders: ['FARGATE', 'FARGATE_SPOT'], + }); + 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 + 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(); diff --git a/packages/@aws-cdk/aws-ecs/test/test.ecs-cluster.ts b/packages/@aws-cdk/aws-ecs/test/test.ecs-cluster.ts index 71a5fc23081d4..7741c6b5bdb9f 100644 --- a/packages/@aws-cdk/aws-ecs/test/test.ecs-cluster.ts +++ b/packages/@aws-cdk/aws-ecs/test/test.ecs-cluster.ts @@ -1666,6 +1666,7 @@ export = { ); test.done(); }, + 'throws when machineImage and machineImageType both specified'(test: Test) { // GIVEN const app = new cdk.App(); @@ -1682,4 +1683,52 @@ export = { }, /You can only specify either machineImage or machineImageType, not both./); test.done(); }, + + 'allows specifying capacityProviders'(test: Test) { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'test'); + + // WHEN + new ecs.Cluster(stack, 'EcsCluster', { capacityProviders: ['FARGATE_SPOT'] }); + + // THEN + expect(stack).to(haveResource('AWS::ECS::Cluster', { + CapacityProviders: ['FARGATE_SPOT'], + })); + + test.done(); + }, + + 'allows adding capacityProviders post-construction'(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(); + }, + + 'throws for unsupported capacity providers'(test: Test) { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'test'); + const cluster = new ecs.Cluster(stack, 'EcsCluster'); + + // THEN + test.throws(() => { + cluster.addCapacityProvider('HONK'); + }, /CapacityProvider not supported/); + + test.done(); + }, }; diff --git a/packages/@aws-cdk/aws-efs/package.json b/packages/@aws-cdk/aws-efs/package.json index a909287fbec25..6a7cef054b6a2 100644 --- a/packages/@aws-cdk/aws-efs/package.json +++ b/packages/@aws-cdk/aws-efs/package.json @@ -107,5 +107,8 @@ "maturity": "experimental", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-eks-legacy/package.json b/packages/@aws-cdk/aws-eks-legacy/package.json index 32c6b684fa2c6..67078e5830dcf 100644 --- a/packages/@aws-cdk/aws-eks-legacy/package.json +++ b/packages/@aws-cdk/aws-eks-legacy/package.json @@ -127,5 +127,8 @@ "awscdkio": { "announce": false }, - "maturity": "deprecated" + "maturity": "deprecated", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-eks/README.md b/packages/@aws-cdk/aws-eks/README.md index efe50404a2ffa..ee758461b986f 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_18, + version: eks.KubernetesVersion.V1_19, }); // 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_18, + version: eks.KubernetesVersion.V1_19, }); ``` @@ -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_18, + version: eks.KubernetesVersion.V1_19, }); ``` @@ -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_18, + version: eks.KubernetesVersion.V1_19, 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_18, + version: eks.KubernetesVersion.V1_19, 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_18, + version: eks.KubernetesVersion.V1_19, }); ``` @@ -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_18, + version: eks.KubernetesVersion.V1_19, 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_18, + version: eks.KubernetesVersion.V1_19, endpointAccess: eks.EndpointAccess.PRIVATE // No access outside of your VPC. }); ``` @@ -474,7 +474,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_18, + version: eks.KubernetesVersion.V1_19, vpc, vpcSubnets: [{ subnetType: ec2.SubnetType.PRIVATE }] }); @@ -513,7 +513,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_18, + version: eks.KubernetesVersion.V1_19, clusterHandlerEnvironment: { 'http_proxy': 'http://proxy.myproxy.com' } @@ -530,7 +530,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_18, + version: eks.KubernetesVersion.V1_19, kubectlEnvironment: { 'http_proxy': 'http://proxy.myproxy.com' } @@ -620,7 +620,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_18, + version: eks.KubernetesVersion.V1_19, mastersRole: role, }); ``` diff --git a/packages/@aws-cdk/aws-eks/lib/cluster.ts b/packages/@aws-cdk/aws-eks/lib/cluster.ts index 706a23720c157..c578b90fa388b 100644 --- a/packages/@aws-cdk/aws-eks/lib/cluster.ts +++ b/packages/@aws-cdk/aws-eks/lib/cluster.ts @@ -634,6 +634,11 @@ export class KubernetesVersion { */ public static readonly V1_18 = KubernetesVersion.of('1.18'); + /** + * Kubernetes version 1.19 + */ + public static readonly V1_19 = KubernetesVersion.of('1.19'); + /** * Custom cluster version * @param version custom version number diff --git a/packages/@aws-cdk/aws-eks/package.json b/packages/@aws-cdk/aws-eks/package.json index 939d90d267ac8..6fef676ea0058 100644 --- a/packages/@aws-cdk/aws-eks/package.json +++ b/packages/@aws-cdk/aws-eks/package.json @@ -73,13 +73,13 @@ "@aws-cdk/assert": "0.0.0", "@types/nodeunit": "^0.0.31", "@types/yaml": "1.9.6", - "aws-sdk": "^2.830.0", + "aws-sdk": "^2.845.0", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "cfn2ts": "0.0.0", "nodeunit": "^0.11.3", "pkglint": "0.0.0", - "sinon": "^9.2.1", + "sinon": "^9.2.4", "cdk8s-plus": "^0.33.0", "cdk8s": "^0.33.0" }, @@ -127,5 +127,8 @@ "maturity": "stable", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } 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 ac924ac20038d..14780774ca44f 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_18, + version: eks.KubernetesVersion.V1_19, }); /// !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 82488919bedb9..50e88fe3277d0 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 @@ -832,7 +832,7 @@ ] }, "Config": { - "version": "1.18", + "version": "1.19", "roleArn": { "Fn::GetAtt": [ "EksAllHandlersInVpcStackRoleC36F09F0", @@ -1427,4 +1427,4 @@ "Description": "Artifact hash for asset \"11ba420a0c99f0c77f563fb974e76d6110b4445114137af1fe1b69b0d366d2d7\"" } } -} \ No newline at end of file +} 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 ec9fe15d081da..cd1682a30d318 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_18; +const CLUSTER_VERSION = eks.KubernetesVersion.V1_19; class EksAllHandlersInVpcStack extends TestStack { @@ -22,4 +22,4 @@ const app = new App(); new EksAllHandlersInVpcStack(app, 'aws-cdk-eks-handlers-in-vpc-test'); -app.synth(); \ No newline at end of file +app.synth(); 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 f76e62c73ee73..b8246afd2d180 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 @@ -772,7 +772,7 @@ ] }, "Config": { - "version": "1.18", + "version": "1.19", "roleArn": { "Fn::GetAtt": [ "ClusterRoleFA261979", @@ -1062,7 +1062,7 @@ }, "/", { - "Ref": "AssetParameters84ba29b05aaf6a233dbb97b37e48eb1300f9d014f270252e29a8b2c22d6a08beS3Bucket9E737267" + "Ref": "AssetParameters75667ab2bbef2c8efc57fb73bf352f345af1d471fb09cb11f5b7bc27d009b609S3BucketA8C94679" }, "/", { @@ -1072,7 +1072,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters84ba29b05aaf6a233dbb97b37e48eb1300f9d014f270252e29a8b2c22d6a08beS3VersionKeyD5E002BC" + "Ref": "AssetParameters75667ab2bbef2c8efc57fb73bf352f345af1d471fb09cb11f5b7bc27d009b609S3VersionKey3777DB64" } ] } @@ -1085,7 +1085,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters84ba29b05aaf6a233dbb97b37e48eb1300f9d014f270252e29a8b2c22d6a08beS3VersionKeyD5E002BC" + "Ref": "AssetParameters75667ab2bbef2c8efc57fb73bf352f345af1d471fb09cb11f5b7bc27d009b609S3VersionKey3777DB64" } ] } @@ -1129,7 +1129,7 @@ }, "/", { - "Ref": "AssetParameters2e2ec0fae5975d4ee5f3580e522c46615c1bd344e0302bc5d2df7501b7bb1ad0S3Bucket8FBFE327" + "Ref": "AssetParameterseb49ce353c5ff251ebe2c3225fe00fb3e9a68fcd8b10207e63a36bfc6e981519S3Bucket686DCA97" }, "/", { @@ -1139,7 +1139,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters2e2ec0fae5975d4ee5f3580e522c46615c1bd344e0302bc5d2df7501b7bb1ad0S3VersionKeyF5A05918" + "Ref": "AssetParameterseb49ce353c5ff251ebe2c3225fe00fb3e9a68fcd8b10207e63a36bfc6e981519S3VersionKey7EDC0140" } ] } @@ -1152,7 +1152,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters2e2ec0fae5975d4ee5f3580e522c46615c1bd344e0302bc5d2df7501b7bb1ad0S3VersionKeyF5A05918" + "Ref": "AssetParameterseb49ce353c5ff251ebe2c3225fe00fb3e9a68fcd8b10207e63a36bfc6e981519S3VersionKey7EDC0140" } ] } @@ -1195,17 +1195,17 @@ "ClusterSecurityGroupId" ] }, - "referencetoawscdkeksclusterprivateendpointtestAssetParametersefd72738f046105c96299fb31b3da40320e71ee9cf74bc37720042898403e2a1S3Bucket4A0D6BE2Ref": { - "Ref": "AssetParametersefd72738f046105c96299fb31b3da40320e71ee9cf74bc37720042898403e2a1S3Bucket6DACDE73" + "referencetoawscdkeksclusterprivateendpointtestAssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketFD6C4D26Ref": { + "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7" }, - "referencetoawscdkeksclusterprivateendpointtestAssetParametersefd72738f046105c96299fb31b3da40320e71ee9cf74bc37720042898403e2a1S3VersionKey6D9B8A02Ref": { - "Ref": "AssetParametersefd72738f046105c96299fb31b3da40320e71ee9cf74bc37720042898403e2a1S3VersionKey015AEA61" + "referencetoawscdkeksclusterprivateendpointtestAssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKey69E4725CRef": { + "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" }, - "referencetoawscdkeksclusterprivateendpointtestAssetParametersb61858bbf1a0be803552e3efa9647befd728156696dff1b413b7b2fd4da1449fS3BucketD44FB215Ref": { - "Ref": "AssetParametersb61858bbf1a0be803552e3efa9647befd728156696dff1b413b7b2fd4da1449fS3Bucket7EE7EA15" + "referencetoawscdkeksclusterprivateendpointtestAssetParameters844c1a4b13479b359ea0e607dccb4a04b73e22cf88cf9b64feed2c5f0de213c0S3Bucket5323F34ARef": { + "Ref": "AssetParameters844c1a4b13479b359ea0e607dccb4a04b73e22cf88cf9b64feed2c5f0de213c0S3Bucket6ABE1927" }, - "referencetoawscdkeksclusterprivateendpointtestAssetParametersb61858bbf1a0be803552e3efa9647befd728156696dff1b413b7b2fd4da1449fS3VersionKey6C30661CRef": { - "Ref": "AssetParametersb61858bbf1a0be803552e3efa9647befd728156696dff1b413b7b2fd4da1449fS3VersionKey6C948E78" + "referencetoawscdkeksclusterprivateendpointtestAssetParameters844c1a4b13479b359ea0e607dccb4a04b73e22cf88cf9b64feed2c5f0de213c0S3VersionKey548D79B4Ref": { + "Ref": "AssetParameters844c1a4b13479b359ea0e607dccb4a04b73e22cf88cf9b64feed2c5f0de213c0S3VersionKeyF55A2EA9" }, "referencetoawscdkeksclusterprivateendpointtestVpcFCD064BFRef": { "Ref": "Vpc8378EB38" @@ -1299,53 +1299,53 @@ "Type": "String", "Description": "Artifact hash for asset \"bafd50ae9f214e496ff8c72c6425f93dca3ccd590e20963706d5d610d9c75757\"" }, - "AssetParametersefd72738f046105c96299fb31b3da40320e71ee9cf74bc37720042898403e2a1S3Bucket6DACDE73": { + "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7": { "Type": "String", - "Description": "S3 bucket for asset \"efd72738f046105c96299fb31b3da40320e71ee9cf74bc37720042898403e2a1\"" + "Description": "S3 bucket for asset \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" }, - "AssetParametersefd72738f046105c96299fb31b3da40320e71ee9cf74bc37720042898403e2a1S3VersionKey015AEA61": { + "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F": { "Type": "String", - "Description": "S3 key for asset version \"efd72738f046105c96299fb31b3da40320e71ee9cf74bc37720042898403e2a1\"" + "Description": "S3 key for asset version \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" }, - "AssetParametersefd72738f046105c96299fb31b3da40320e71ee9cf74bc37720042898403e2a1ArtifactHashC9FD06BA": { + "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68ArtifactHashD9A515C3": { "Type": "String", - "Description": "Artifact hash for asset \"efd72738f046105c96299fb31b3da40320e71ee9cf74bc37720042898403e2a1\"" + "Description": "Artifact hash for asset \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" }, - "AssetParametersb61858bbf1a0be803552e3efa9647befd728156696dff1b413b7b2fd4da1449fS3Bucket7EE7EA15": { + "AssetParameters844c1a4b13479b359ea0e607dccb4a04b73e22cf88cf9b64feed2c5f0de213c0S3Bucket6ABE1927": { "Type": "String", - "Description": "S3 bucket for asset \"b61858bbf1a0be803552e3efa9647befd728156696dff1b413b7b2fd4da1449f\"" + "Description": "S3 bucket for asset \"844c1a4b13479b359ea0e607dccb4a04b73e22cf88cf9b64feed2c5f0de213c0\"" }, - "AssetParametersb61858bbf1a0be803552e3efa9647befd728156696dff1b413b7b2fd4da1449fS3VersionKey6C948E78": { + "AssetParameters844c1a4b13479b359ea0e607dccb4a04b73e22cf88cf9b64feed2c5f0de213c0S3VersionKeyF55A2EA9": { "Type": "String", - "Description": "S3 key for asset version \"b61858bbf1a0be803552e3efa9647befd728156696dff1b413b7b2fd4da1449f\"" + "Description": "S3 key for asset version \"844c1a4b13479b359ea0e607dccb4a04b73e22cf88cf9b64feed2c5f0de213c0\"" }, - "AssetParametersb61858bbf1a0be803552e3efa9647befd728156696dff1b413b7b2fd4da1449fArtifactHash7E705796": { + "AssetParameters844c1a4b13479b359ea0e607dccb4a04b73e22cf88cf9b64feed2c5f0de213c0ArtifactHash1D7A2D6E": { "Type": "String", - "Description": "Artifact hash for asset \"b61858bbf1a0be803552e3efa9647befd728156696dff1b413b7b2fd4da1449f\"" + "Description": "Artifact hash for asset \"844c1a4b13479b359ea0e607dccb4a04b73e22cf88cf9b64feed2c5f0de213c0\"" }, - "AssetParameters84ba29b05aaf6a233dbb97b37e48eb1300f9d014f270252e29a8b2c22d6a08beS3Bucket9E737267": { + "AssetParameters75667ab2bbef2c8efc57fb73bf352f345af1d471fb09cb11f5b7bc27d009b609S3BucketA8C94679": { "Type": "String", - "Description": "S3 bucket for asset \"84ba29b05aaf6a233dbb97b37e48eb1300f9d014f270252e29a8b2c22d6a08be\"" + "Description": "S3 bucket for asset \"75667ab2bbef2c8efc57fb73bf352f345af1d471fb09cb11f5b7bc27d009b609\"" }, - "AssetParameters84ba29b05aaf6a233dbb97b37e48eb1300f9d014f270252e29a8b2c22d6a08beS3VersionKeyD5E002BC": { + "AssetParameters75667ab2bbef2c8efc57fb73bf352f345af1d471fb09cb11f5b7bc27d009b609S3VersionKey3777DB64": { "Type": "String", - "Description": "S3 key for asset version \"84ba29b05aaf6a233dbb97b37e48eb1300f9d014f270252e29a8b2c22d6a08be\"" + "Description": "S3 key for asset version \"75667ab2bbef2c8efc57fb73bf352f345af1d471fb09cb11f5b7bc27d009b609\"" }, - "AssetParameters84ba29b05aaf6a233dbb97b37e48eb1300f9d014f270252e29a8b2c22d6a08beArtifactHashDF0A0444": { + "AssetParameters75667ab2bbef2c8efc57fb73bf352f345af1d471fb09cb11f5b7bc27d009b609ArtifactHash14CC8C95": { "Type": "String", - "Description": "Artifact hash for asset \"84ba29b05aaf6a233dbb97b37e48eb1300f9d014f270252e29a8b2c22d6a08be\"" + "Description": "Artifact hash for asset \"75667ab2bbef2c8efc57fb73bf352f345af1d471fb09cb11f5b7bc27d009b609\"" }, - "AssetParameters2e2ec0fae5975d4ee5f3580e522c46615c1bd344e0302bc5d2df7501b7bb1ad0S3Bucket8FBFE327": { + "AssetParameterseb49ce353c5ff251ebe2c3225fe00fb3e9a68fcd8b10207e63a36bfc6e981519S3Bucket686DCA97": { "Type": "String", - "Description": "S3 bucket for asset \"2e2ec0fae5975d4ee5f3580e522c46615c1bd344e0302bc5d2df7501b7bb1ad0\"" + "Description": "S3 bucket for asset \"eb49ce353c5ff251ebe2c3225fe00fb3e9a68fcd8b10207e63a36bfc6e981519\"" }, - "AssetParameters2e2ec0fae5975d4ee5f3580e522c46615c1bd344e0302bc5d2df7501b7bb1ad0S3VersionKeyF5A05918": { + "AssetParameterseb49ce353c5ff251ebe2c3225fe00fb3e9a68fcd8b10207e63a36bfc6e981519S3VersionKey7EDC0140": { "Type": "String", - "Description": "S3 key for asset version \"2e2ec0fae5975d4ee5f3580e522c46615c1bd344e0302bc5d2df7501b7bb1ad0\"" + "Description": "S3 key for asset version \"eb49ce353c5ff251ebe2c3225fe00fb3e9a68fcd8b10207e63a36bfc6e981519\"" }, - "AssetParameters2e2ec0fae5975d4ee5f3580e522c46615c1bd344e0302bc5d2df7501b7bb1ad0ArtifactHashDFBC9DE7": { + "AssetParameterseb49ce353c5ff251ebe2c3225fe00fb3e9a68fcd8b10207e63a36bfc6e981519ArtifactHashE5817DEB": { "Type": "String", - "Description": "Artifact hash for asset \"2e2ec0fae5975d4ee5f3580e522c46615c1bd344e0302bc5d2df7501b7bb1ad0\"" + "Description": "Artifact hash for asset \"eb49ce353c5ff251ebe2c3225fe00fb3e9a68fcd8b10207e63a36bfc6e981519\"" } } } \ No newline at end of file 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 0a0c9a61230e9..a9cb68866e5d8 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_18; +const CLUSTER_VERSION = eks.KubernetesVersion.V1_19; 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 5c0b4bf402a5b..3e172020cae6d 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 @@ -34,23 +34,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { @@ -949,7 +933,7 @@ ] }, "Config": { - "version": "1.18", + "version": "1.19", "roleArn": { "Fn::GetAtt": [ "ClusterRoleFA261979", @@ -1683,7 +1667,7 @@ "Type": "AWS::AutoScaling::LaunchConfiguration", "Properties": { "ImageId": { - "Ref": "SsmParameterValueawsserviceeksoptimizedami118amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter" + "Ref": "SsmParameterValueawsserviceeksoptimizedami119amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter" }, "InstanceType": "t2.medium", "IamInstanceProfile": { @@ -1994,7 +1978,7 @@ "Type": "AWS::AutoScaling::LaunchConfiguration", "Properties": { "ImageId": { - "Ref": "SsmParameterValueawsserviceeksoptimizedami118amazonlinux2arm64recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter" + "Ref": "SsmParameterValueawsserviceeksoptimizedami119amazonlinux2arm64recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter" }, "InstanceType": "m6g.medium", "IamInstanceProfile": { @@ -2305,7 +2289,7 @@ "Type": "AWS::AutoScaling::LaunchConfiguration", "Properties": { "ImageId": { - "Ref": "SsmParameterValueawsservicebottlerocketawsk8s118x8664latestimageidC96584B6F00A464EAD1953AFF4B05118Parameter" + "Ref": "SsmParameterValueawsservicebottlerocketawsk8s119x8664latestimageidC96584B6F00A464EAD1953AFF4B05118Parameter" }, "InstanceType": "t3.small", "IamInstanceProfile": { @@ -2630,7 +2614,7 @@ "Type": "AWS::AutoScaling::LaunchConfiguration", "Properties": { "ImageId": { - "Ref": "SsmParameterValueawsserviceeksoptimizedami118amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter" + "Ref": "SsmParameterValueawsserviceeksoptimizedami119amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter" }, "InstanceType": "t3.large", "IamInstanceProfile": { @@ -2974,7 +2958,7 @@ "Type": "AWS::AutoScaling::LaunchConfiguration", "Properties": { "ImageId": { - "Ref": "SsmParameterValueawsserviceeksoptimizedami118amazonlinux2gpurecommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter" + "Ref": "SsmParameterValueawsserviceeksoptimizedami119amazonlinux2gpurecommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter" }, "InstanceType": "inf1.2xlarge", "IamInstanceProfile": { @@ -3277,6 +3261,7 @@ } ], "AmiType": "AL2_x86_64", + "CapacityType": "SPOT", "ForceUpdateEnabled": true, "InstanceTypes": [ "c5.large", @@ -3287,8 +3272,7 @@ "DesiredSize": 3, "MaxSize": 3, "MinSize": 3 - }, - "CapacityType": "SPOT" + } } }, "ClusterNodegroupextrangarmNodeGroupRoleADF5749F": { @@ -3840,7 +3824,7 @@ }, "/", { - "Ref": "AssetParametersa69aadbed84d554dd9f2eb7987ffe5d8f76b53a86f1909059df07050e57bef0cS3Bucket1CB7A187" + "Ref": "AssetParameters264acf17cbf0c643f47bec1f4dbaed805e3bd1bad3f018c093d16fb936227daaS3Bucket862E8D6F" }, "/", { @@ -3850,7 +3834,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersa69aadbed84d554dd9f2eb7987ffe5d8f76b53a86f1909059df07050e57bef0cS3VersionKey7C13F243" + "Ref": "AssetParameters264acf17cbf0c643f47bec1f4dbaed805e3bd1bad3f018c093d16fb936227daaS3VersionKey9466BE3D" } ] } @@ -3863,7 +3847,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersa69aadbed84d554dd9f2eb7987ffe5d8f76b53a86f1909059df07050e57bef0cS3VersionKey7C13F243" + "Ref": "AssetParameters264acf17cbf0c643f47bec1f4dbaed805e3bd1bad3f018c093d16fb936227daaS3VersionKey9466BE3D" } ] } @@ -3907,7 +3891,7 @@ }, "/", { - "Ref": "AssetParameters6b9ad3782e5bfd49d7a58fc915b6151dbed2e24d824730d7720bc8237ba252c8S3Bucket0B8E3806" + "Ref": "AssetParameters9f954a0baf5cb008231906c33569617ace43f4b2c804d16d0d4bae15fe9dfabcS3BucketF9C7C3C5" }, "/", { @@ -3917,7 +3901,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters6b9ad3782e5bfd49d7a58fc915b6151dbed2e24d824730d7720bc8237ba252c8S3VersionKey862F0970" + "Ref": "AssetParameters9f954a0baf5cb008231906c33569617ace43f4b2c804d16d0d4bae15fe9dfabcS3VersionKey950894D5" } ] } @@ -3930,7 +3914,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters6b9ad3782e5bfd49d7a58fc915b6151dbed2e24d824730d7720bc8237ba252c8S3VersionKey862F0970" + "Ref": "AssetParameters9f954a0baf5cb008231906c33569617ace43f4b2c804d16d0d4bae15fe9dfabcS3VersionKey950894D5" } ] } @@ -4002,7 +3986,7 @@ "Properties": { "LaunchTemplateData": { "ImageId": { - "Ref": "SsmParameterValueawsserviceeksoptimizedami114amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter" + "Ref": "SsmParameterValueawsserviceeksoptimizedami119amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter" }, "InstanceType": "t3.small", "UserData": { @@ -4347,13 +4331,13 @@ ] } }, - "Handler": "index.handler", "Role": { "Fn::GetAtt": [ "ServicePingerFunctionServiceRole3120191B", "Arn" ] }, + "Handler": "index.handler", "Runtime": "python3.6", "Timeout": 600, "VpcConfig": { @@ -4480,14 +4464,12 @@ ] } }, - "Handler": "framework.onEvent", "Role": { "Fn::GetAtt": [ "ServicePingerProviderframeworkonEventServiceRole3DB083B7", "Arn" ] }, - "Runtime": "nodejs10.x", "Description": "AWS CDK resource provider framework - onEvent (aws-cdk-eks-cluster-test/ServicePinger/Provider)", "Environment": { "Variables": { @@ -4499,6 +4481,8 @@ } } }, + "Handler": "framework.onEvent", + "Runtime": "nodejs10.x", "Timeout": 900 }, "DependsOn": [ @@ -4727,49 +4711,45 @@ "Type": "String", "Description": "Artifact hash for asset \"5f49893093e1ad14831626016699156d48da5f0890f19eb930bc3c46cf5f636d\"" }, - "AssetParametersa69aadbed84d554dd9f2eb7987ffe5d8f76b53a86f1909059df07050e57bef0cS3Bucket1CB7A187": { + "AssetParameters264acf17cbf0c643f47bec1f4dbaed805e3bd1bad3f018c093d16fb936227daaS3Bucket862E8D6F": { "Type": "String", - "Description": "S3 bucket for asset \"a69aadbed84d554dd9f2eb7987ffe5d8f76b53a86f1909059df07050e57bef0c\"" + "Description": "S3 bucket for asset \"264acf17cbf0c643f47bec1f4dbaed805e3bd1bad3f018c093d16fb936227daa\"" }, - "AssetParametersa69aadbed84d554dd9f2eb7987ffe5d8f76b53a86f1909059df07050e57bef0cS3VersionKey7C13F243": { + "AssetParameters264acf17cbf0c643f47bec1f4dbaed805e3bd1bad3f018c093d16fb936227daaS3VersionKey9466BE3D": { "Type": "String", - "Description": "S3 key for asset version \"a69aadbed84d554dd9f2eb7987ffe5d8f76b53a86f1909059df07050e57bef0c\"" + "Description": "S3 key for asset version \"264acf17cbf0c643f47bec1f4dbaed805e3bd1bad3f018c093d16fb936227daa\"" }, - "AssetParametersa69aadbed84d554dd9f2eb7987ffe5d8f76b53a86f1909059df07050e57bef0cArtifactHashBADE945D": { + "AssetParameters264acf17cbf0c643f47bec1f4dbaed805e3bd1bad3f018c093d16fb936227daaArtifactHashC5F5158C": { "Type": "String", - "Description": "Artifact hash for asset \"a69aadbed84d554dd9f2eb7987ffe5d8f76b53a86f1909059df07050e57bef0c\"" + "Description": "Artifact hash for asset \"264acf17cbf0c643f47bec1f4dbaed805e3bd1bad3f018c093d16fb936227daa\"" }, - "AssetParameters6b9ad3782e5bfd49d7a58fc915b6151dbed2e24d824730d7720bc8237ba252c8S3Bucket0B8E3806": { + "AssetParameters9f954a0baf5cb008231906c33569617ace43f4b2c804d16d0d4bae15fe9dfabcS3BucketF9C7C3C5": { "Type": "String", - "Description": "S3 bucket for asset \"6b9ad3782e5bfd49d7a58fc915b6151dbed2e24d824730d7720bc8237ba252c8\"" + "Description": "S3 bucket for asset \"9f954a0baf5cb008231906c33569617ace43f4b2c804d16d0d4bae15fe9dfabc\"" }, - "AssetParameters6b9ad3782e5bfd49d7a58fc915b6151dbed2e24d824730d7720bc8237ba252c8S3VersionKey862F0970": { + "AssetParameters9f954a0baf5cb008231906c33569617ace43f4b2c804d16d0d4bae15fe9dfabcS3VersionKey950894D5": { "Type": "String", - "Description": "S3 key for asset version \"6b9ad3782e5bfd49d7a58fc915b6151dbed2e24d824730d7720bc8237ba252c8\"" + "Description": "S3 key for asset version \"9f954a0baf5cb008231906c33569617ace43f4b2c804d16d0d4bae15fe9dfabc\"" }, - "AssetParameters6b9ad3782e5bfd49d7a58fc915b6151dbed2e24d824730d7720bc8237ba252c8ArtifactHashAAFBAA4D": { + "AssetParameters9f954a0baf5cb008231906c33569617ace43f4b2c804d16d0d4bae15fe9dfabcArtifactHash5984E3CE": { "Type": "String", - "Description": "Artifact hash for asset \"6b9ad3782e5bfd49d7a58fc915b6151dbed2e24d824730d7720bc8237ba252c8\"" - }, - "SsmParameterValueawsserviceeksoptimizedami118amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { - "Type": "AWS::SSM::Parameter::Value", - "Default": "/aws/service/eks/optimized-ami/1.18/amazon-linux-2/recommended/image_id" + "Description": "Artifact hash for asset \"9f954a0baf5cb008231906c33569617ace43f4b2c804d16d0d4bae15fe9dfabc\"" }, - "SsmParameterValueawsserviceeksoptimizedami118amazonlinux2arm64recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { + "SsmParameterValueawsserviceeksoptimizedami119amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { "Type": "AWS::SSM::Parameter::Value", - "Default": "/aws/service/eks/optimized-ami/1.18/amazon-linux-2-arm64/recommended/image_id" + "Default": "/aws/service/eks/optimized-ami/1.19/amazon-linux-2/recommended/image_id" }, - "SsmParameterValueawsservicebottlerocketawsk8s118x8664latestimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { + "SsmParameterValueawsserviceeksoptimizedami119amazonlinux2arm64recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { "Type": "AWS::SSM::Parameter::Value", - "Default": "/aws/service/bottlerocket/aws-k8s-1.18/x86_64/latest/image_id" + "Default": "/aws/service/eks/optimized-ami/1.19/amazon-linux-2-arm64/recommended/image_id" }, - "SsmParameterValueawsserviceeksoptimizedami118amazonlinux2gpurecommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { + "SsmParameterValueawsservicebottlerocketawsk8s119x8664latestimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { "Type": "AWS::SSM::Parameter::Value", - "Default": "/aws/service/eks/optimized-ami/1.18/amazon-linux-2-gpu/recommended/image_id" + "Default": "/aws/service/bottlerocket/aws-k8s-1.19/x86_64/latest/image_id" }, - "SsmParameterValueawsserviceeksoptimizedami114amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { + "SsmParameterValueawsserviceeksoptimizedami119amazonlinux2gpurecommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { "Type": "AWS::SSM::Parameter::Value", - "Default": "/aws/service/eks/optimized-ami/1.14/amazon-linux-2/recommended/image_id" + "Default": "/aws/service/eks/optimized-ami/1.19/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 12c7334bd96a5..080cd744a57f0 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_18, + version: eks.KubernetesVersion.V1_19, secretsEncryptionKey, }); @@ -187,7 +187,9 @@ class EksClusterStack extends TestStack { ); const lt = new ec2.CfnLaunchTemplate(this, 'LaunchTemplate', { launchTemplateData: { - imageId: new eks.EksOptimizedImage().getImage(this).imageId, + imageId: new eks.EksOptimizedImage({ + kubernetesVersion: eks.KubernetesVersion.V1_19.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 5d6efed26b63f..631236f803499 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 @@ -842,7 +842,7 @@ ] }, "Config": { - "version": "1.18", + "version": "1.19", "roleArn": { "Fn::GetAtt": [ "FargateClusterRole8E36B33A", @@ -1139,7 +1139,7 @@ }, "/", { - "Ref": "AssetParametersae946640aaf0743990584e4a1cf45ddebbaddcaf60611f572e80100a02162f48S3BucketDA5FB24D" + "Ref": "AssetParameters3d252d05ccf0ae2934dd20707e8a709b466b2b8ea00c04ee8735667f90b17ac1S3Bucket01B07207" }, "/", { @@ -1149,7 +1149,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersae946640aaf0743990584e4a1cf45ddebbaddcaf60611f572e80100a02162f48S3VersionKey798A3941" + "Ref": "AssetParameters3d252d05ccf0ae2934dd20707e8a709b466b2b8ea00c04ee8735667f90b17ac1S3VersionKey3EEF52BA" } ] } @@ -1162,7 +1162,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersae946640aaf0743990584e4a1cf45ddebbaddcaf60611f572e80100a02162f48S3VersionKey798A3941" + "Ref": "AssetParameters3d252d05ccf0ae2934dd20707e8a709b466b2b8ea00c04ee8735667f90b17ac1S3VersionKey3EEF52BA" } ] } @@ -1206,7 +1206,7 @@ }, "/", { - "Ref": "AssetParameters7449e3c4cf21a811d1d6612d2f1a806025f018320ebc8c1d8037eb34f0d0e98dS3BucketF39EF776" + "Ref": "AssetParameters37d93b8a35af568f83ecce5e05c6f06adaa06c68b84dfad9c3d82f57cd54ff13S3BucketDCD73A4E" }, "/", { @@ -1216,7 +1216,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters7449e3c4cf21a811d1d6612d2f1a806025f018320ebc8c1d8037eb34f0d0e98dS3VersionKeyE6E734A4" + "Ref": "AssetParameters37d93b8a35af568f83ecce5e05c6f06adaa06c68b84dfad9c3d82f57cd54ff13S3VersionKey796F5C1B" } ] } @@ -1229,7 +1229,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters7449e3c4cf21a811d1d6612d2f1a806025f018320ebc8c1d8037eb34f0d0e98dS3VersionKeyE6E734A4" + "Ref": "AssetParameters37d93b8a35af568f83ecce5e05c6f06adaa06c68b84dfad9c3d82f57cd54ff13S3VersionKey796F5C1B" } ] } @@ -1272,17 +1272,17 @@ "ClusterSecurityGroupId" ] }, - "referencetoawscdkeksfargateclustertestAssetParametersefd72738f046105c96299fb31b3da40320e71ee9cf74bc37720042898403e2a1S3Bucket9D3BB190Ref": { - "Ref": "AssetParametersefd72738f046105c96299fb31b3da40320e71ee9cf74bc37720042898403e2a1S3Bucket6DACDE73" + "referencetoawscdkeksfargateclustertestAssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3Bucket4F20F642Ref": { + "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7" }, - "referencetoawscdkeksfargateclustertestAssetParametersefd72738f046105c96299fb31b3da40320e71ee9cf74bc37720042898403e2a1S3VersionKey3BB3C6F5Ref": { - "Ref": "AssetParametersefd72738f046105c96299fb31b3da40320e71ee9cf74bc37720042898403e2a1S3VersionKey015AEA61" + "referencetoawscdkeksfargateclustertestAssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyB82BAEF8Ref": { + "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" }, - "referencetoawscdkeksfargateclustertestAssetParametersb61858bbf1a0be803552e3efa9647befd728156696dff1b413b7b2fd4da1449fS3Bucket99BFDD36Ref": { - "Ref": "AssetParametersb61858bbf1a0be803552e3efa9647befd728156696dff1b413b7b2fd4da1449fS3Bucket7EE7EA15" + "referencetoawscdkeksfargateclustertestAssetParameters844c1a4b13479b359ea0e607dccb4a04b73e22cf88cf9b64feed2c5f0de213c0S3Bucket9ED34BB4Ref": { + "Ref": "AssetParameters844c1a4b13479b359ea0e607dccb4a04b73e22cf88cf9b64feed2c5f0de213c0S3Bucket6ABE1927" }, - "referencetoawscdkeksfargateclustertestAssetParametersb61858bbf1a0be803552e3efa9647befd728156696dff1b413b7b2fd4da1449fS3VersionKeyEEC9E8C1Ref": { - "Ref": "AssetParametersb61858bbf1a0be803552e3efa9647befd728156696dff1b413b7b2fd4da1449fS3VersionKey6C948E78" + "referencetoawscdkeksfargateclustertestAssetParameters844c1a4b13479b359ea0e607dccb4a04b73e22cf88cf9b64feed2c5f0de213c0S3VersionKeyFE6D1F78Ref": { + "Ref": "AssetParameters844c1a4b13479b359ea0e607dccb4a04b73e22cf88cf9b64feed2c5f0de213c0S3VersionKeyF55A2EA9" }, "referencetoawscdkeksfargateclustertestFargateClusterDefaultVpcBD3C976FRef": { "Ref": "FargateClusterDefaultVpcE69D3A13" @@ -1376,53 +1376,53 @@ "Type": "String", "Description": "Artifact hash for asset \"bafd50ae9f214e496ff8c72c6425f93dca3ccd590e20963706d5d610d9c75757\"" }, - "AssetParametersefd72738f046105c96299fb31b3da40320e71ee9cf74bc37720042898403e2a1S3Bucket6DACDE73": { + "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7": { "Type": "String", - "Description": "S3 bucket for asset \"efd72738f046105c96299fb31b3da40320e71ee9cf74bc37720042898403e2a1\"" + "Description": "S3 bucket for asset \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" }, - "AssetParametersefd72738f046105c96299fb31b3da40320e71ee9cf74bc37720042898403e2a1S3VersionKey015AEA61": { + "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F": { "Type": "String", - "Description": "S3 key for asset version \"efd72738f046105c96299fb31b3da40320e71ee9cf74bc37720042898403e2a1\"" + "Description": "S3 key for asset version \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" }, - "AssetParametersefd72738f046105c96299fb31b3da40320e71ee9cf74bc37720042898403e2a1ArtifactHashC9FD06BA": { + "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68ArtifactHashD9A515C3": { "Type": "String", - "Description": "Artifact hash for asset \"efd72738f046105c96299fb31b3da40320e71ee9cf74bc37720042898403e2a1\"" + "Description": "Artifact hash for asset \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" }, - "AssetParametersb61858bbf1a0be803552e3efa9647befd728156696dff1b413b7b2fd4da1449fS3Bucket7EE7EA15": { + "AssetParameters844c1a4b13479b359ea0e607dccb4a04b73e22cf88cf9b64feed2c5f0de213c0S3Bucket6ABE1927": { "Type": "String", - "Description": "S3 bucket for asset \"b61858bbf1a0be803552e3efa9647befd728156696dff1b413b7b2fd4da1449f\"" + "Description": "S3 bucket for asset \"844c1a4b13479b359ea0e607dccb4a04b73e22cf88cf9b64feed2c5f0de213c0\"" }, - "AssetParametersb61858bbf1a0be803552e3efa9647befd728156696dff1b413b7b2fd4da1449fS3VersionKey6C948E78": { + "AssetParameters844c1a4b13479b359ea0e607dccb4a04b73e22cf88cf9b64feed2c5f0de213c0S3VersionKeyF55A2EA9": { "Type": "String", - "Description": "S3 key for asset version \"b61858bbf1a0be803552e3efa9647befd728156696dff1b413b7b2fd4da1449f\"" + "Description": "S3 key for asset version \"844c1a4b13479b359ea0e607dccb4a04b73e22cf88cf9b64feed2c5f0de213c0\"" }, - "AssetParametersb61858bbf1a0be803552e3efa9647befd728156696dff1b413b7b2fd4da1449fArtifactHash7E705796": { + "AssetParameters844c1a4b13479b359ea0e607dccb4a04b73e22cf88cf9b64feed2c5f0de213c0ArtifactHash1D7A2D6E": { "Type": "String", - "Description": "Artifact hash for asset \"b61858bbf1a0be803552e3efa9647befd728156696dff1b413b7b2fd4da1449f\"" + "Description": "Artifact hash for asset \"844c1a4b13479b359ea0e607dccb4a04b73e22cf88cf9b64feed2c5f0de213c0\"" }, - "AssetParametersae946640aaf0743990584e4a1cf45ddebbaddcaf60611f572e80100a02162f48S3BucketDA5FB24D": { + "AssetParameters3d252d05ccf0ae2934dd20707e8a709b466b2b8ea00c04ee8735667f90b17ac1S3Bucket01B07207": { "Type": "String", - "Description": "S3 bucket for asset \"ae946640aaf0743990584e4a1cf45ddebbaddcaf60611f572e80100a02162f48\"" + "Description": "S3 bucket for asset \"3d252d05ccf0ae2934dd20707e8a709b466b2b8ea00c04ee8735667f90b17ac1\"" }, - "AssetParametersae946640aaf0743990584e4a1cf45ddebbaddcaf60611f572e80100a02162f48S3VersionKey798A3941": { + "AssetParameters3d252d05ccf0ae2934dd20707e8a709b466b2b8ea00c04ee8735667f90b17ac1S3VersionKey3EEF52BA": { "Type": "String", - "Description": "S3 key for asset version \"ae946640aaf0743990584e4a1cf45ddebbaddcaf60611f572e80100a02162f48\"" + "Description": "S3 key for asset version \"3d252d05ccf0ae2934dd20707e8a709b466b2b8ea00c04ee8735667f90b17ac1\"" }, - "AssetParametersae946640aaf0743990584e4a1cf45ddebbaddcaf60611f572e80100a02162f48ArtifactHash865DB842": { + "AssetParameters3d252d05ccf0ae2934dd20707e8a709b466b2b8ea00c04ee8735667f90b17ac1ArtifactHash812ED4D5": { "Type": "String", - "Description": "Artifact hash for asset \"ae946640aaf0743990584e4a1cf45ddebbaddcaf60611f572e80100a02162f48\"" + "Description": "Artifact hash for asset \"3d252d05ccf0ae2934dd20707e8a709b466b2b8ea00c04ee8735667f90b17ac1\"" }, - "AssetParameters7449e3c4cf21a811d1d6612d2f1a806025f018320ebc8c1d8037eb34f0d0e98dS3BucketF39EF776": { + "AssetParameters37d93b8a35af568f83ecce5e05c6f06adaa06c68b84dfad9c3d82f57cd54ff13S3BucketDCD73A4E": { "Type": "String", - "Description": "S3 bucket for asset \"7449e3c4cf21a811d1d6612d2f1a806025f018320ebc8c1d8037eb34f0d0e98d\"" + "Description": "S3 bucket for asset \"37d93b8a35af568f83ecce5e05c6f06adaa06c68b84dfad9c3d82f57cd54ff13\"" }, - "AssetParameters7449e3c4cf21a811d1d6612d2f1a806025f018320ebc8c1d8037eb34f0d0e98dS3VersionKeyE6E734A4": { + "AssetParameters37d93b8a35af568f83ecce5e05c6f06adaa06c68b84dfad9c3d82f57cd54ff13S3VersionKey796F5C1B": { "Type": "String", - "Description": "S3 key for asset version \"7449e3c4cf21a811d1d6612d2f1a806025f018320ebc8c1d8037eb34f0d0e98d\"" + "Description": "S3 key for asset version \"37d93b8a35af568f83ecce5e05c6f06adaa06c68b84dfad9c3d82f57cd54ff13\"" }, - "AssetParameters7449e3c4cf21a811d1d6612d2f1a806025f018320ebc8c1d8037eb34f0d0e98dArtifactHash93FFAA4A": { + "AssetParameters37d93b8a35af568f83ecce5e05c6f06adaa06c68b84dfad9c3d82f57cd54ff13ArtifactHash8F2277C1": { "Type": "String", - "Description": "Artifact hash for asset \"7449e3c4cf21a811d1d6612d2f1a806025f018320ebc8c1d8037eb34f0d0e98d\"" + "Description": "Artifact hash for asset \"37d93b8a35af568f83ecce5e05c6f06adaa06c68b84dfad9c3d82f57cd54ff13\"" } } } \ No newline at end of file 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 a97389ce4622e..957b60ee23e3b 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_18; +const CLUSTER_VERSION = eks.KubernetesVersion.V1_19; class EksFargateClusterStack extends TestStack { diff --git a/packages/@aws-cdk/aws-eks/test/test.cluster.ts b/packages/@aws-cdk/aws-eks/test/test.cluster.ts index 7e7f01d7ede84..0f8ad05549bdd 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_18; +const CLUSTER_VERSION = eks.KubernetesVersion.V1_19; export = { @@ -85,7 +85,7 @@ export = { vpc: vpc, vpcSubnets: [{ subnetType: ec2.SubnetType.PUBLIC }, { subnetType: ec2.SubnetType.PRIVATE }], defaultCapacity: 0, - version: eks.KubernetesVersion.V1_18, + version: eks.KubernetesVersion.V1_19, }), /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_18, + version: eks.KubernetesVersion.V1_19, }); // THEN @@ -629,7 +629,7 @@ export = { expect(stack).to(haveResourceLike('Custom::AWSCDK-EKS-Cluster', { Config: { roleArn: { 'Fn::GetAtt': ['ClusterRoleFA261979', 'Arn'] }, - version: '1.18', + version: '1.19', resourcesVpcConfig: { securityGroupIds: [{ 'Fn::GetAtt': ['ClusterControlPlaneSecurityGroupD274242C', 'GroupId'] }], subnetIds: [ @@ -1462,7 +1462,7 @@ export = { const { app, stack } = testFixtureNoVpc(); // WHEN - new eks.EksOptimizedImage({ kubernetesVersion: '1.18' }).getImage(stack); + new eks.EksOptimizedImage({ kubernetesVersion: '1.19' }).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.18/'), + (v as any).Default.includes('/1.19/'), ), 'kubernetesVersion should be in ssm parameters'); test.done(); }, @@ -1611,7 +1611,7 @@ export = { const { app, stack } = testFixtureNoVpc(); // WHEN - new BottleRocketImage({ kubernetesVersion: '1.18' }).getImage(stack); + new BottleRocketImage({ kubernetesVersion: '1.19' }).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.18/'), + (v as any).Default.includes('/aws-k8s-1.19/'), ), '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.18', + version: '1.19', 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 a606a5cfe7496..ba1ec4009d1d1 100644 --- a/packages/@aws-cdk/aws-eks/test/test.fargate.ts +++ b/packages/@aws-cdk/aws-eks/test/test.fargate.ts @@ -5,7 +5,7 @@ import { Stack, Tags } from '@aws-cdk/core'; import { Test } from 'nodeunit'; import * as eks from '../lib'; -const CLUSTER_VERSION = eks.KubernetesVersion.V1_16; +const CLUSTER_VERSION = eks.KubernetesVersion.V1_19; 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 241482d469c65..0599c349f6190 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_18; +const CLUSTER_VERSION = eks.KubernetesVersion.V1_19; export = { diff --git a/packages/@aws-cdk/aws-elasticache/package.json b/packages/@aws-cdk/aws-elasticache/package.json index aa85b7db2ecfd..f0b728276adda 100644 --- a/packages/@aws-cdk/aws-elasticache/package.json +++ b/packages/@aws-cdk/aws-elasticache/package.json @@ -92,5 +92,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-elasticbeanstalk/package.json b/packages/@aws-cdk/aws-elasticbeanstalk/package.json index bb500913fc817..31472f9e21d51 100644 --- a/packages/@aws-cdk/aws-elasticbeanstalk/package.json +++ b/packages/@aws-cdk/aws-elasticbeanstalk/package.json @@ -92,5 +92,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-elasticloadbalancing/package.json b/packages/@aws-cdk/aws-elasticloadbalancing/package.json index 3fc6fd2bb62ba..24f25fb2470f8 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancing/package.json +++ b/packages/@aws-cdk/aws-elasticloadbalancing/package.json @@ -120,5 +120,8 @@ "awscdkio": { "announce": false }, - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2-actions/package.json b/packages/@aws-cdk/aws-elasticloadbalancingv2-actions/package.json index 2018764c662af..dc606985d7c17 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2-actions/package.json +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2-actions/package.json @@ -100,5 +100,8 @@ "env": { "AWSLINT_BASE_CONSTRUCT": true } + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2-targets/package.json b/packages/@aws-cdk/aws-elasticloadbalancingv2-targets/package.json index 0d5a3abc474d8..51ae07a4caaa7 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2-targets/package.json +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2-targets/package.json @@ -105,5 +105,8 @@ "env": { "AWSLINT_BASE_CONSTRUCT": true } + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json b/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json index 269f18af95f12..745b87fb4453f 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json @@ -142,5 +142,8 @@ "awscdkio": { "announce": false }, - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-elasticsearch/README.md b/packages/@aws-cdk/aws-elasticsearch/README.md index 35d3f63c6bf55..37d117c393616 100644 --- a/packages/@aws-cdk/aws-elasticsearch/README.md +++ b/packages/@aws-cdk/aws-elasticsearch/README.md @@ -225,3 +225,20 @@ const domain = new es.Domain(this, 'Domain', { }, }); ``` + +## Custom endpoint + +Custom endpoints can be configured to reach the ES domain under a custom domain name. + +```ts +new Domain(stack, 'Domain', { + version: ElasticsearchVersion.V7_7, + customEndpoint: { + domainName: 'search.example.com', + }, +}); +``` + +It is also possible to specify a custom certificate instead of the auto-generated one. + +Additionally, an automatic CNAME-Record is created if a hosted zone is provided for the custom endpoint diff --git a/packages/@aws-cdk/aws-elasticsearch/lib/domain.ts b/packages/@aws-cdk/aws-elasticsearch/lib/domain.ts index 603177c87c19f..28728e6cdf58f 100644 --- a/packages/@aws-cdk/aws-elasticsearch/lib/domain.ts +++ b/packages/@aws-cdk/aws-elasticsearch/lib/domain.ts @@ -1,10 +1,12 @@ import { URL } from 'url'; +import * as acm from '@aws-cdk/aws-certificatemanager'; import { Metric, MetricOptions, Statistic } 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 route53 from '@aws-cdk/aws-route53'; import * as secretsmanager from '@aws-cdk/aws-secretsmanager'; import * as cdk from '@aws-cdk/core'; import { Construct } from 'constructs'; @@ -395,6 +397,28 @@ export interface AdvancedSecurityOptions { readonly masterUserPassword?: cdk.SecretValue; } +/** + * Configures a custom domain endpoint for the ES domain + */ +export interface CustomEndpointOptions { + /** + * The custom domain name to assign + */ + readonly domainName: string; + + /** + * The certificate to use + * @default - create a new one + */ + readonly certificate?: acm.ICertificate; + + /** + * The hosted zone in Route53 to create the CNAME record in + * @default - do not create a CNAME + */ + readonly hostedZone?: route53.IHostedZone; +} + /** * Properties for an AWS Elasticsearch Domain. */ @@ -545,6 +569,13 @@ export interface DomainProps { */ readonly enableVersionUpgrade?: boolean; + /** + * To configure a custom domain configure these options + * + * If you specify a Route53 hosted zone it will create a CNAME record and use DNS validation for the certificate + * @default - no custom domain endpoint will be configured + */ + readonly customEndpoint?: CustomEndpointOptions; } /** @@ -1547,6 +1578,18 @@ export class Domain extends DomainBase implements IDomain { }; } + let customEndpointCertificate: acm.ICertificate | undefined; + if (props.customEndpoint) { + if (props.customEndpoint.certificate) { + customEndpointCertificate = props.customEndpoint.certificate; + } else { + customEndpointCertificate = new acm.Certificate(this, 'CustomEndpointCertificate', { + domainName: props.customEndpoint.domainName, + validation: props.customEndpoint.hostedZone ? acm.CertificateValidation.fromDns(props.customEndpoint.hostedZone) : undefined, + }); + } + } + // Create the domain this.domain = new CfnDomain(this, 'Resource', { domainName: this.physicalName, @@ -1602,6 +1645,11 @@ export class Domain extends DomainBase implements IDomain { domainEndpointOptions: { enforceHttps, tlsSecurityPolicy: props.tlsSecurityPolicy ?? TLSSecurityPolicy.TLS_1_0, + ...props.customEndpoint && { + customEndpointEnabled: true, + customEndpoint: props.customEndpoint.domainName, + customEndpointCertificateArn: customEndpointCertificate!.certificateArn, + }, }, advancedSecurityOptions: advancedSecurityEnabled ? { @@ -1637,6 +1685,14 @@ export class Domain extends DomainBase implements IDomain { resourceName: this.physicalName, }); + if (props.customEndpoint?.hostedZone) { + new route53.CnameRecord(this, 'CnameRecord', { + recordName: props.customEndpoint.domainName, + zone: props.customEndpoint.hostedZone, + domainName: this.domainEndpoint, + }); + } + const accessPolicyStatements: iam.PolicyStatement[] | undefined = unsignedBasicAuthEnabled ? (props.accessPolicies ?? []).concat(unsignedAccessPolicy) : props.accessPolicies; diff --git a/packages/@aws-cdk/aws-elasticsearch/package.json b/packages/@aws-cdk/aws-elasticsearch/package.json index c93f1397c06b8..df7d8934559ea 100644 --- a/packages/@aws-cdk/aws-elasticsearch/package.json +++ b/packages/@aws-cdk/aws-elasticsearch/package.json @@ -78,11 +78,13 @@ "pkglint": "0.0.0" }, "dependencies": { + "@aws-cdk/aws-certificatemanager": "0.0.0", "@aws-cdk/aws-cloudwatch": "0.0.0", "@aws-cdk/aws-ec2": "0.0.0", "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-kms": "0.0.0", "@aws-cdk/aws-logs": "0.0.0", + "@aws-cdk/aws-route53": "0.0.0", "@aws-cdk/aws-secretsmanager": "0.0.0", "@aws-cdk/custom-resources": "0.0.0", "@aws-cdk/core": "0.0.0", @@ -90,11 +92,13 @@ }, "homepage": "https://github.com/aws/aws-cdk", "peerDependencies": { + "@aws-cdk/aws-certificatemanager": "0.0.0", "@aws-cdk/aws-cloudwatch": "0.0.0", "@aws-cdk/aws-ec2": "0.0.0", "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-kms": "0.0.0", "@aws-cdk/aws-logs": "0.0.0", + "@aws-cdk/aws-route53": "0.0.0", "@aws-cdk/aws-secretsmanager": "0.0.0", "@aws-cdk/custom-resources": "0.0.0", "@aws-cdk/core": "0.0.0", @@ -113,5 +117,8 @@ ], "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-elasticsearch/test/domain.test.ts b/packages/@aws-cdk/aws-elasticsearch/test/domain.test.ts index f01fba3c4b332..98a6f350d6f34 100644 --- a/packages/@aws-cdk/aws-elasticsearch/test/domain.test.ts +++ b/packages/@aws-cdk/aws-elasticsearch/test/domain.test.ts @@ -1,11 +1,13 @@ /* eslint-disable jest/expect-expect */ import '@aws-cdk/assert/jest'; import * as assert from '@aws-cdk/assert'; +import * as acm from '@aws-cdk/aws-certificatemanager'; import { Metric, Statistic } from '@aws-cdk/aws-cloudwatch'; import { Subnet, Vpc, EbsDeviceVolumeType } 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 route53 from '@aws-cdk/aws-route53'; import { App, Stack, Duration, SecretValue } from '@aws-cdk/core'; import { Domain, ElasticsearchVersion } from '../lib'; @@ -987,6 +989,134 @@ describe('advanced security options', () => { }); }); +describe('custom endpoints', () => { + const customDomainName = 'search.example.com'; + + test('custom domain without hosted zone and default cert', () => { + new Domain(stack, 'Domain', { + version: ElasticsearchVersion.V7_1, + nodeToNodeEncryption: true, + enforceHttps: true, + customEndpoint: { + domainName: customDomainName, + }, + }); + + expect(stack).toHaveResourceLike('AWS::Elasticsearch::Domain', { + DomainEndpointOptions: { + EnforceHTTPS: true, + CustomEndpointEnabled: true, + CustomEndpoint: customDomainName, + CustomEndpointCertificateArn: { + Ref: 'DomainCustomEndpointCertificateD080A69E', // Auto-generated certificate + }, + }, + }); + expect(stack).toHaveResourceLike('AWS::CertificateManager::Certificate', { + DomainName: customDomainName, + ValidationMethod: 'EMAIL', + }); + }); + + test('custom domain with hosted zone and default cert', () => { + const zone = new route53.HostedZone(stack, 'DummyZone', { zoneName: 'example.com' }); + new Domain(stack, 'Domain', { + version: ElasticsearchVersion.V7_1, + nodeToNodeEncryption: true, + enforceHttps: true, + customEndpoint: { + domainName: customDomainName, + hostedZone: zone, + }, + }); + + expect(stack).toHaveResourceLike('AWS::Elasticsearch::Domain', { + DomainEndpointOptions: { + EnforceHTTPS: true, + CustomEndpointEnabled: true, + CustomEndpoint: customDomainName, + CustomEndpointCertificateArn: { + Ref: 'DomainCustomEndpointCertificateD080A69E', // Auto-generated certificate + }, + }, + }); + expect(stack).toHaveResourceLike('AWS::CertificateManager::Certificate', { + DomainName: customDomainName, + DomainValidationOptions: [ + { + DomainName: customDomainName, + HostedZoneId: { + Ref: 'DummyZone03E0FE81', + }, + }, + ], + ValidationMethod: 'DNS', + }); + expect(stack).toHaveResourceLike('AWS::Route53::RecordSet', { + Name: 'search.example.com.', + Type: 'CNAME', + HostedZoneId: { + Ref: 'DummyZone03E0FE81', + }, + ResourceRecords: [ + { + 'Fn::GetAtt': [ + 'Domain66AC69E0', + 'DomainEndpoint', + ], + }, + ], + }); + }); + + test('custom domain with hosted zone and given cert', () => { + const zone = new route53.HostedZone(stack, 'DummyZone', { + zoneName: 'example.com', + }); + const certificate = new acm.Certificate(stack, 'DummyCert', { + domainName: customDomainName, + }); + + new Domain(stack, 'Domain', { + version: ElasticsearchVersion.V7_1, + nodeToNodeEncryption: true, + enforceHttps: true, + customEndpoint: { + domainName: customDomainName, + hostedZone: zone, + certificate, + }, + }); + + expect(stack).toHaveResourceLike('AWS::Elasticsearch::Domain', { + DomainEndpointOptions: { + EnforceHTTPS: true, + CustomEndpointEnabled: true, + CustomEndpoint: customDomainName, + CustomEndpointCertificateArn: { + Ref: 'DummyCertFA37670B', + }, + }, + }); + expect(stack).toHaveResourceLike('AWS::Route53::RecordSet', { + Name: 'search.example.com.', + Type: 'CNAME', + HostedZoneId: { + Ref: 'DummyZone03E0FE81', + }, + ResourceRecords: [ + { + 'Fn::GetAtt': [ + 'Domain66AC69E0', + 'DomainEndpoint', + ], + }, + ], + }); + }); + +}); + describe('custom error responses', () => { test('error when availabilityZoneCount does not match vpcOptions.subnets length', () => { 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 201e3d81bd99d..1da03e98c1984 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 @@ -6,23 +6,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { @@ -466,4 +450,4 @@ "Description": "Artifact hash for asset \"b64b129569a5ac7a9abf88a18ac0b504d1fb1208872460476ed3fd435830eb94\"" } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-emr/package.json b/packages/@aws-cdk/aws-emr/package.json index 09b7d70c7ece6..0865f9a31783d 100644 --- a/packages/@aws-cdk/aws-emr/package.json +++ b/packages/@aws-cdk/aws-emr/package.json @@ -92,5 +92,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-emrcontainers/package.json b/packages/@aws-cdk/aws-emrcontainers/package.json index ecd7363f54aad..c1e102c6a81e6 100644 --- a/packages/@aws-cdk/aws-emrcontainers/package.json +++ b/packages/@aws-cdk/aws-emrcontainers/package.json @@ -93,5 +93,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-events-targets/package.json b/packages/@aws-cdk/aws-events-targets/package.json index 2fbad9a4cd421..8e342f7cda858 100644 --- a/packages/@aws-cdk/aws-events-targets/package.json +++ b/packages/@aws-cdk/aws-events-targets/package.json @@ -74,7 +74,7 @@ "@aws-cdk/assert": "0.0.0", "@aws-cdk/aws-codecommit": "0.0.0", "@aws-cdk/aws-s3": "0.0.0", - "aws-sdk": "^2.830.0", + "aws-sdk": "^2.845.0", "aws-sdk-mock": "^5.1.0", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", @@ -140,5 +140,8 @@ "awscdkio": { "announce": false }, - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-events-targets/test/codepipeline/integ.pipeline-event-target.expected.json b/packages/@aws-cdk/aws-events-targets/test/codepipeline/integ.pipeline-event-target.expected.json index f2ed2f2752e75..333930c6c23ff 100644 --- a/packages/@aws-cdk/aws-events-targets/test/codepipeline/integ.pipeline-event-target.expected.json +++ b/packages/@aws-cdk/aws-events-targets/test/codepipeline/integ.pipeline-event-target.expected.json @@ -12,23 +12,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { @@ -49,25 +33,6 @@ } }, "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "pipelinePipeline22F2A91DRole58B7B05E", - "Arn" - ] - } - }, - "Resource": "*" } ], "Version": "2012-10-17" @@ -474,4 +439,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-events/package.json b/packages/@aws-cdk/aws-events/package.json index 624ff18646bb6..4c8d4fe304598 100644 --- a/packages/@aws-cdk/aws-events/package.json +++ b/packages/@aws-cdk/aws-events/package.json @@ -108,5 +108,8 @@ "awscdkio": { "announce": false }, - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-eventschemas/package.json b/packages/@aws-cdk/aws-eventschemas/package.json index 179f02a568a31..c141a1a389382 100644 --- a/packages/@aws-cdk/aws-eventschemas/package.json +++ b/packages/@aws-cdk/aws-eventschemas/package.json @@ -93,5 +93,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-fms/package.json b/packages/@aws-cdk/aws-fms/package.json index 2e85eba2e9ffe..352a762f1e877 100644 --- a/packages/@aws-cdk/aws-fms/package.json +++ b/packages/@aws-cdk/aws-fms/package.json @@ -93,5 +93,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-fsx/package.json b/packages/@aws-cdk/aws-fsx/package.json index 7f03e65a80b11..f004ef50dc8b4 100644 --- a/packages/@aws-cdk/aws-fsx/package.json +++ b/packages/@aws-cdk/aws-fsx/package.json @@ -106,5 +106,8 @@ "maturity": "experimental", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-gamelift/package.json b/packages/@aws-cdk/aws-gamelift/package.json index 0f866c675bacf..9ce7dd38156cb 100644 --- a/packages/@aws-cdk/aws-gamelift/package.json +++ b/packages/@aws-cdk/aws-gamelift/package.json @@ -92,5 +92,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-globalaccelerator/package.json b/packages/@aws-cdk/aws-globalaccelerator/package.json index 84f457cd0fcce..44d8895ed30a3 100644 --- a/packages/@aws-cdk/aws-globalaccelerator/package.json +++ b/packages/@aws-cdk/aws-globalaccelerator/package.json @@ -99,5 +99,8 @@ "maturity": "experimental", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-glue/README.md b/packages/@aws-cdk/aws-glue/README.md index 77ee00834ddd7..20e08d7c14e31 100644 --- a/packages/@aws-cdk/aws-glue/README.md +++ b/packages/@aws-cdk/aws-glue/README.md @@ -23,6 +23,24 @@ This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project. +## Connection + +A `Connection` allows Glue jobs, crawlers and development endpoints to access certain types of data stores. For example, to create a network connection to connect to a data source within a VPC: + +```ts +new glue.Connection(stack, 'MyConnection', { + connectionType: glue.ConnectionTypes.NETWORK, + // The security groups granting AWS Glue inbound access to the data source within the VPC + securityGroups: [securityGroup], + // The VPC subnet which contains the data source + subnet, +}); +``` + +If you need to use a connection type that doesn't exist as a static member on `ConnectionType`, you can instantiate a `ConnectionType` object, e.g: `new glue.ConnectionType('NEW_TYPE')`. + +See [Adding a Connection to Your Data Store](https://docs.aws.amazon.com/glue/latest/dg/populate-add-connection.html) and [Connection Structure](https://docs.aws.amazon.com/glue/latest/dg/aws-glue-api-catalog-connections.html#aws-glue-api-catalog-connections-Connection) documentation for more information on the supported data stores and their configurations. + ## Database A `Database` is a logical grouping of `Tables` in the Glue Catalog. @@ -33,6 +51,40 @@ new glue.Database(stack, 'MyDatabase', { }); ``` +## SecurityConfiguration + +A `SecurityConfiguration` is a set of security properties that can be used by AWS Glue to encrypt data at rest. + +```ts +new glue.SecurityConfiguration(stack, 'MySecurityConfiguration', { + securityConfigurationName: 'name', + cloudWatchEncryption: { + mode: glue.CloudWatchEncryptionMode.KMS, + }, + jobBookmarksEncryption: { + mode: glue.JobBookmarksEncryptionMode.CLIENT_SIDE_KMS, + }, + s3Encryption: { + mode: glue.S3EncryptionMode.KMS, + }, +}); +``` + +By default, a shared KMS key is created for use with the encryption configurations that require one. You can also supply your own key for each encryption config, for example, for CloudWatch encryption: + +```ts +new glue.SecurityConfiguration(stack, 'MySecurityConfiguration', { + securityConfigurationName: 'name', + cloudWatchEncryption: { + mode: glue.CloudWatchEncryptionMode.KMS, + kmsKey: key, + }, +}); +``` + +See [documentation](https://docs.aws.amazon.com/glue/latest/dg/encryption-security-configuration.html) for more info for Glue encrypting data written by Crawlers, Jobs, and Development Endpoints. + + ## Table A Glue table describes a table of data in S3: its structure (column names and types), location of data (S3 objects with a common prefix in a S3 bucket), and format for the files (Json, Avro, Parquet, etc.): diff --git a/packages/@aws-cdk/aws-glue/lib/connection.ts b/packages/@aws-cdk/aws-glue/lib/connection.ts new file mode 100644 index 0000000000000..56cd115e4c6f9 --- /dev/null +++ b/packages/@aws-cdk/aws-glue/lib/connection.ts @@ -0,0 +1,216 @@ +import * as ec2 from '@aws-cdk/aws-ec2'; +import * as cdk from '@aws-cdk/core'; +import * as constructs from 'constructs'; +import { CfnConnection } from './glue.generated'; + +/** + * The type of the glue connection + * + * If you need to use a connection type that doesn't exist as a static member, you + * can instantiate a `ConnectionType` object, e.g: `new ConnectionType('NEW_TYPE')`. + */ +export class ConnectionType { + + /** + * Designates a connection to a database through Java Database Connectivity (JDBC). + */ + public static readonly JDBC = new ConnectionType('JDBC'); + + /** + * Designates a connection to an Apache Kafka streaming platform. + */ + public static readonly KAFKA = new ConnectionType('KAFKA'); + + /** + * Designates a connection to a MongoDB document database. + */ + public static readonly MONGODB = new ConnectionType('MONGODB'); + + /** + * Designates a network connection to a data source within an Amazon Virtual Private Cloud environment (Amazon VPC). + */ + public static readonly NETWORK = new ConnectionType('NETWORK'); + + /** + * The name of this ConnectionType, as expected by Connection resource. + */ + public readonly name: string; + + constructor(name: string) { + this.name = name; + } + + /** + * The connection type name as expected by Connection resource. + */ + public toString(): string { + return this.name; + } +} + +/** + * Interface representing a created or an imported {@link Connection} + */ +export interface IConnection extends cdk.IResource { + /** + * The name of the connection + * @attribute + */ + readonly connectionName: string; + + /** + * The ARN of the connection + * @attribute + */ + readonly connectionArn: string; +} + +/** + * Base Connection Options + */ +export interface ConnectionOptions { + /** + * The name of the connection + * @default cloudformation generated name + */ + readonly connectionName?: string; + + /** + * The description of the connection. + * @default no description + */ + readonly description?: string; + + /** + * Key-Value pairs that define parameters for the connection. + * @default empty properties + * @see https://docs.aws.amazon.com/glue/latest/dg/aws-glue-programming-etl-connect.html + */ + readonly properties?: { [key: string]: string }; + + /** + * A list of criteria that can be used in selecting this connection. + * This is useful for filtering the results of https://awscli.amazonaws.com/v2/documentation/api/latest/reference/glue/get-connections.html + * @default no match criteria + */ + readonly matchCriteria?: string[]; + + /** + * The list of security groups needed to successfully make this connection e.g. to successfully connect to VPC. + * @default no security group + */ + readonly securityGroups?: ec2.ISecurityGroup[]; + + /** + * The VPC subnet to connect to resources within a VPC. See more at https://docs.aws.amazon.com/glue/latest/dg/start-connecting.html. + * @default no subnet + */ + readonly subnet?: ec2.ISubnet; +} + +/** + * Construction properties for {@link Connection} + */ +export interface ConnectionProps extends ConnectionOptions { + /** + * The type of the connection + */ + readonly type: ConnectionType; +} + +/** + * An AWS Glue connection to a data source. + */ +export class Connection extends cdk.Resource implements IConnection { + + /** + * Creates a Connection construct that represents an external connection. + * + * @param scope The scope creating construct (usually `this`). + * @param id The construct's id. + * @param connectionArn arn of external connection. + */ + public static fromConnectionArn(scope: constructs.Construct, id: string, connectionArn: string): IConnection { + class Import extends cdk.Resource implements IConnection { + public readonly connectionName = cdk.Arn.extractResourceName(connectionArn, 'connection'); + public readonly connectionArn = connectionArn; + } + + return new Import(scope, id); + } + + /** + * Creates a Connection construct that represents an external connection. + * + * @param scope The scope creating construct (usually `this`). + * @param id The construct's id. + * @param connectionName name of external connection. + */ + public static fromConnectionName(scope: constructs.Construct, id: string, connectionName: string): IConnection { + class Import extends cdk.Resource implements IConnection { + public readonly connectionName = connectionName; + public readonly connectionArn = Connection.buildConnectionArn(scope, connectionName); + } + + return new Import(scope, id); + } + + private static buildConnectionArn(scope: constructs.Construct, connectionName: string) : string { + return cdk.Stack.of(scope).formatArn({ + service: 'glue', + resource: 'connection', + resourceName: connectionName, + }); + } + + /** + * The ARN of the connection + */ + public readonly connectionArn: string; + + /** + * The name of the connection + */ + public readonly connectionName: string; + + private readonly properties: {[key: string]: string}; + + constructor(scope: constructs.Construct, id: string, props: ConnectionProps) { + super(scope, id, { + physicalName: props.connectionName, + }); + + this.properties = props.properties || {}; + + const physicalConnectionRequirements = props.subnet || props.securityGroups ? { + availabilityZone: props.subnet ? props.subnet.availabilityZone : undefined, + subnetId: props.subnet ? props.subnet.subnetId : undefined, + securityGroupIdList: props.securityGroups ? props.securityGroups.map(sg => sg.securityGroupId) : undefined, + } : undefined; + + const connectionResource = new CfnConnection(this, 'Resource', { + catalogId: cdk.Stack.of(this).account, + connectionInput: { + connectionProperties: cdk.Lazy.any({ produce: () => Object.keys(this.properties).length > 0 ? this.properties : undefined }), + connectionType: props.type.name, + description: props.description, + matchCriteria: props.matchCriteria, + name: props.connectionName, + physicalConnectionRequirements, + }, + }); + + const resourceName = this.getResourceNameAttribute(connectionResource.ref); + this.connectionArn = Connection.buildConnectionArn(this, resourceName); + this.connectionName = resourceName; + } + + /** + * Add additional connection parameters + * @param key parameter key + * @param value parameter value + */ + public addProperty(key: string, value: string): void { + this.properties[key] = value; + } +} diff --git a/packages/@aws-cdk/aws-glue/lib/index.ts b/packages/@aws-cdk/aws-glue/lib/index.ts index f0370c21041b2..a3dfa85b3be71 100644 --- a/packages/@aws-cdk/aws-glue/lib/index.ts +++ b/packages/@aws-cdk/aws-glue/lib/index.ts @@ -1,7 +1,9 @@ // AWS::Glue CloudFormation Resources: export * from './glue.generated'; +export * from './connection'; +export * from './data-format'; export * from './database'; export * from './schema'; -export * from './data-format'; +export * from './security-configuration'; export * from './table'; \ No newline at end of file diff --git a/packages/@aws-cdk/aws-glue/lib/security-configuration.ts b/packages/@aws-cdk/aws-glue/lib/security-configuration.ts new file mode 100644 index 0000000000000..f1e7297147794 --- /dev/null +++ b/packages/@aws-cdk/aws-glue/lib/security-configuration.ts @@ -0,0 +1,243 @@ +import * as kms from '@aws-cdk/aws-kms'; +import * as cdk from '@aws-cdk/core'; +import * as constructs from 'constructs'; +import { CfnSecurityConfiguration } from './glue.generated'; + +/** + * Interface representing a created or an imported {@link SecurityConfiguration}. + */ +export interface ISecurityConfiguration extends cdk.IResource { + /** + * The name of the security configuration. + * @attribute + */ + readonly securityConfigurationName: string; +} + +/** + * Encryption mode for S3. + * @see https://docs.aws.amazon.com/glue/latest/webapi/API_S3Encryption.html#Glue-Type-S3Encryption-S3EncryptionMode + */ +export enum S3EncryptionMode { + /** + * Server side encryption (SSE) with an Amazon S3-managed key. + * + * @see https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingServerSideEncryption.html + */ + S3_MANAGED = 'SSE-S3', + + /** + * Server-side encryption (SSE) with an AWS KMS key managed by the account owner. + * + * @see https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingKMSEncryption.html + */ + KMS = 'SSE-KMS', +} + +/** + * Encryption mode for CloudWatch Logs. + * @see https://docs.aws.amazon.com/glue/latest/webapi/API_CloudWatchEncryption.html#Glue-Type-CloudWatchEncryption-CloudWatchEncryptionMode + */ +export enum CloudWatchEncryptionMode { + /** + * Server-side encryption (SSE) with an AWS KMS key managed by the account owner. + * + * @see https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingKMSEncryption.html + */ + KMS = 'SSE-KMS', +} + +/** + * Encryption mode for Job Bookmarks. + * @see https://docs.aws.amazon.com/glue/latest/webapi/API_JobBookmarksEncryption.html#Glue-Type-JobBookmarksEncryption-JobBookmarksEncryptionMode + */ +export enum JobBookmarksEncryptionMode { + /** + * Client-side encryption (CSE) with an AWS KMS key managed by the account owner. + * + * @see https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingClientSideEncryption.html + */ + CLIENT_SIDE_KMS = 'CSE-KMS', +} + +/** + * S3 encryption configuration. + */ +export interface S3Encryption { + /** + * Encryption mode. + */ + readonly mode: S3EncryptionMode, + + /** + * The KMS key to be used to encrypt the data. + * @default no kms key if mode = S3_MANAGED. A key will be created if one is not provided and mode = KMS. + */ + readonly kmsKey?: kms.IKey, +} + +/** + * CloudWatch Logs encryption configuration. + */ +export interface CloudWatchEncryption { + /** + * Encryption mode + */ + readonly mode: CloudWatchEncryptionMode; + + /** + * The KMS key to be used to encrypt the data. + * @default A key will be created if one is not provided. + */ + readonly kmsKey?: kms.IKey, +} + +/** + * Job bookmarks encryption configuration. + */ +export interface JobBookmarksEncryption { + /** + * Encryption mode. + */ + readonly mode: JobBookmarksEncryptionMode; + + /** + * The KMS key to be used to encrypt the data. + * @default A key will be created if one is not provided. + */ + readonly kmsKey?: kms.IKey, +} + +/** + * Constructions properties of {@link SecurityConfiguration}. + */ +export interface SecurityConfigurationProps { + /** + * The name of the security configuration. + */ + readonly securityConfigurationName: string; + + /** + * The encryption configuration for Amazon CloudWatch Logs. + * @default no cloudwatch logs encryption. + */ + readonly cloudWatchEncryption?: CloudWatchEncryption, + + /** + * The encryption configuration for Glue Job Bookmarks. + * @default no job bookmarks encryption. + */ + readonly jobBookmarksEncryption?: JobBookmarksEncryption, + + /** + * The encryption configuration for Amazon Simple Storage Service (Amazon S3) data. + * @default no s3 encryption. + */ + readonly s3Encryption?: S3Encryption, +} + +/** + * A security configuration is a set of security properties that can be used by AWS Glue to encrypt data at rest. + * + * The following scenarios show some of the ways that you can use a security configuration. + * - Attach a security configuration to an AWS Glue crawler to write encrypted Amazon CloudWatch Logs. + * - Attach a security configuration to an extract, transform, and load (ETL) job to write encrypted Amazon Simple Storage Service (Amazon S3) targets and encrypted CloudWatch Logs. + * - Attach a security configuration to an ETL job to write its jobs bookmarks as encrypted Amazon S3 data. + * - Attach a security configuration to a development endpoint to write encrypted Amazon S3 targets. + */ +export class SecurityConfiguration extends cdk.Resource implements ISecurityConfiguration { + + /** + * Creates a Connection construct that represents an external security configuration. + * + * @param scope The scope creating construct (usually `this`). + * @param id The construct's id. + * @param securityConfigurationName name of external security configuration. + */ + public static fromSecurityConfigurationName(scope: constructs.Construct, id: string, + securityConfigurationName: string): ISecurityConfiguration { + + class Import extends cdk.Resource implements ISecurityConfiguration { + public readonly securityConfigurationName = securityConfigurationName; + } + return new Import(scope, id); + } + + /** + * The name of the security configuration. + * @attribute + */ + public readonly securityConfigurationName: string; + + /** + * The KMS key used in CloudWatch encryption if it requires a kms key. + */ + public readonly cloudWatchEncryptionKey?: kms.IKey; + + /** + * The KMS key used in job bookmarks encryption if it requires a kms key. + */ + public readonly jobBookmarksEncryptionKey?: kms.IKey; + + /** + * The KMS key used in S3 encryption if it requires a kms key. + */ + public readonly s3EncryptionKey?: kms.IKey; + + constructor(scope: constructs.Construct, id: string, props: SecurityConfigurationProps) { + super(scope, id, { + physicalName: props.securityConfigurationName, + }); + + if (!props.s3Encryption && !props.cloudWatchEncryption && !props.jobBookmarksEncryption) { + throw new Error('One of cloudWatchEncryption, jobBookmarksEncryption or s3Encryption must be defined'); + } + + const kmsKeyCreationRequired = + (props.s3Encryption && props.s3Encryption.mode === S3EncryptionMode.KMS && !props.s3Encryption.kmsKey) || + (props.cloudWatchEncryption && !props.cloudWatchEncryption.kmsKey) || + (props.jobBookmarksEncryption && !props.jobBookmarksEncryption.kmsKey); + const autoCreatedKmsKey = kmsKeyCreationRequired ? new kms.Key(this, 'Key') : undefined; + + let cloudWatchEncryption; + if (props.cloudWatchEncryption) { + this.cloudWatchEncryptionKey = props.cloudWatchEncryption.kmsKey || autoCreatedKmsKey; + cloudWatchEncryption = { + cloudWatchEncryptionMode: props.cloudWatchEncryption.mode, + kmsKeyArn: this.cloudWatchEncryptionKey?.keyArn, + }; + } + + let jobBookmarksEncryption; + if (props.jobBookmarksEncryption) { + this.jobBookmarksEncryptionKey = props.jobBookmarksEncryption.kmsKey || autoCreatedKmsKey; + jobBookmarksEncryption = { + jobBookmarksEncryptionMode: props.jobBookmarksEncryption.mode, + kmsKeyArn: this.jobBookmarksEncryptionKey?.keyArn, + }; + } + + let s3Encryptions; + if (props.s3Encryption) { + if (props.s3Encryption.mode === S3EncryptionMode.KMS) { + this.s3EncryptionKey = props.s3Encryption.kmsKey || autoCreatedKmsKey; + } + // NOTE: CloudFormations errors out if array is of length > 1. That's why the props don't expose an array + s3Encryptions = [{ + s3EncryptionMode: props.s3Encryption.mode, + kmsKeyArn: this.s3EncryptionKey?.keyArn, + }]; + } + + const resource = new CfnSecurityConfiguration(this, 'Resource', { + name: props.securityConfigurationName, + encryptionConfiguration: { + cloudWatchEncryption, + jobBookmarksEncryption, + s3Encryptions, + }, + }); + + this.securityConfigurationName = this.getResourceNameAttribute(resource.ref); + } +} diff --git a/packages/@aws-cdk/aws-glue/package.json b/packages/@aws-cdk/aws-glue/package.json index ca6ed1bb731a5..0eaa0ba607b63 100644 --- a/packages/@aws-cdk/aws-glue/package.json +++ b/packages/@aws-cdk/aws-glue/package.json @@ -80,6 +80,7 @@ "pkglint": "0.0.0" }, "dependencies": { + "@aws-cdk/aws-ec2": "0.0.0", "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-kms": "0.0.0", "@aws-cdk/aws-s3": "0.0.0", @@ -88,6 +89,7 @@ }, "homepage": "https://github.com/aws/aws-cdk", "peerDependencies": { + "@aws-cdk/aws-ec2": "0.0.0", "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-kms": "0.0.0", "@aws-cdk/aws-s3": "0.0.0", @@ -142,5 +144,8 @@ }, "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-glue/test/connection.test.ts b/packages/@aws-cdk/aws-glue/test/connection.test.ts new file mode 100644 index 0000000000000..5846bf161fa69 --- /dev/null +++ b/packages/@aws-cdk/aws-glue/test/connection.test.ts @@ -0,0 +1,159 @@ +import * as cdkassert from '@aws-cdk/assert'; +import * as ec2 from '@aws-cdk/aws-ec2'; +import * as cdk from '@aws-cdk/core'; +import '@aws-cdk/assert/jest'; +import * as glue from '../lib'; + +test('a connection with connection properties', () => { + const stack = new cdk.Stack(); + new glue.Connection(stack, 'Connection', { + type: glue.ConnectionType.JDBC, + properties: { + JDBC_CONNECTION_URL: 'jdbc:server://server:443/connection', + USERNAME: 'username', + PASSWORD: 'password', + }, + }); + + cdkassert.expect(stack).to(cdkassert.haveResource('AWS::Glue::Connection', { + CatalogId: { + Ref: 'AWS::AccountId', + }, + ConnectionInput: { + ConnectionProperties: { + JDBC_CONNECTION_URL: 'jdbc:server://server:443/connection', + USERNAME: 'username', + PASSWORD: 'password', + }, + ConnectionType: 'JDBC', + }, + })); +}); + +test('a connection with a subnet and security group', () => { + const stack = new cdk.Stack(); + const subnet = ec2.Subnet.fromSubnetAttributes(stack, 'subnet', { + subnetId: 'subnetId', + availabilityZone: 'azId', + }); + const securityGroup = ec2.SecurityGroup.fromSecurityGroupId(stack, 'securityGroup', 'sgId'); + new glue.Connection(stack, 'Connection', { + type: glue.ConnectionType.NETWORK, + securityGroups: [securityGroup], + subnet, + }); + + cdkassert.expect(stack).to(cdkassert.haveResource('AWS::Glue::Connection', { + CatalogId: { + Ref: 'AWS::AccountId', + }, + ConnectionInput: { + ConnectionType: 'NETWORK', + PhysicalConnectionRequirements: { + AvailabilityZone: 'azId', + SubnetId: 'subnetId', + SecurityGroupIdList: ['sgId'], + }, + }, + })); +}); + +test('a connection with a name and description', () => { + const stack = new cdk.Stack(); + new glue.Connection(stack, 'Connection', { + connectionName: 'name', + description: 'description', + type: glue.ConnectionType.NETWORK, + }); + + cdkassert.expect(stack).to(cdkassert.haveResource('AWS::Glue::Connection', { + CatalogId: { + Ref: 'AWS::AccountId', + }, + ConnectionInput: { + ConnectionType: 'NETWORK', + Name: 'name', + Description: 'description', + }, + })); +}); + +test('a connection with a custom type', () => { + const stack = new cdk.Stack(); + new glue.Connection(stack, 'Connection', { + connectionName: 'name', + description: 'description', + type: new glue.ConnectionType('CUSTOM_TYPE'), + }); + + cdkassert.expect(stack).to(cdkassert.haveResource('AWS::Glue::Connection', { + CatalogId: { + Ref: 'AWS::AccountId', + }, + ConnectionInput: { + ConnectionType: 'CUSTOM_TYPE', + Name: 'name', + Description: 'description', + }, + })); +}); + +test('a connection with match criteria', () => { + const stack = new cdk.Stack(); + new glue.Connection(stack, 'Connection', { + type: glue.ConnectionType.NETWORK, + matchCriteria: ['c1', 'c2'], + }); + + cdkassert.expect(stack).to(cdkassert.haveResource('AWS::Glue::Connection', { + CatalogId: { + Ref: 'AWS::AccountId', + }, + ConnectionInput: { + ConnectionType: 'NETWORK', + MatchCriteria: ['c1', 'c2'], + }, + })); +}); + +test('addProperty', () => { + const stack = new cdk.Stack(); + const connection = new glue.Connection(stack, 'Connection', { + type: glue.ConnectionType.NETWORK, + }); + connection.addProperty('SomeKey', 'SomeValue'); + + cdkassert.expect(stack).to(cdkassert.haveResource('AWS::Glue::Connection', { + CatalogId: { + Ref: 'AWS::AccountId', + }, + ConnectionInput: { + ConnectionType: 'NETWORK', + ConnectionProperties: { + SomeKey: 'SomeValue', + }, + }, + })); +}); + +test('fromConnectionName', () => { + const connectionName = 'name'; + const stack = new cdk.Stack(); + const connection = glue.Connection.fromConnectionName(stack, 'ImportedConnection', connectionName); + + expect(connection.connectionName).toEqual(connectionName); + expect(connection.connectionArn).toEqual(stack.formatArn({ + service: 'glue', + resource: 'connection', + resourceName: connectionName, + })); +}); + +test('fromConnectionArn', () => { + const connectionArn = 'arn:aws:glue:region:account-id:connection/name'; + const stack = new cdk.Stack(); + const connection = glue.Connection.fromConnectionArn(stack, 'ImportedConnection', connectionArn); + + expect(connection.connectionName).toEqual('name'); + expect(connection.connectionArn).toEqual(connectionArn); +}); diff --git a/packages/@aws-cdk/aws-glue/test/integ.connection.expected.json b/packages/@aws-cdk/aws-glue/test/integ.connection.expected.json new file mode 100644 index 0000000000000..3422ce4ff408b --- /dev/null +++ b/packages/@aws-cdk/aws-glue/test/integ.connection.expected.json @@ -0,0 +1,559 @@ +{ + "Resources": { + "Vpc8378EB38": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "aws-glue-connection/Vpc" + } + ] + } + }, + "VpcPublicSubnet1Subnet5C2D37C4": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.0.0/19", + "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-glue-connection/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1RouteTable6C95E38E": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-glue-connection/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-glue-connection/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet1NATGateway4D7517AA": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet1EIPD7E02669", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-glue-connection/Vpc/PublicSubnet1" + } + ] + } + }, + "VpcPublicSubnet2Subnet691E08A3": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.32.0/19", + "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-glue-connection/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2RouteTable94F7E489": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-glue-connection/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-glue-connection/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet2NATGateway9182C01D": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet2EIP3C605A87", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VpcPublicSubnet2Subnet691E08A3" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-glue-connection/Vpc/PublicSubnet2" + } + ] + } + }, + "VpcPublicSubnet3SubnetBE12F0B6": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.64.0/19", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1c", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "aws-glue-connection/Vpc/PublicSubnet3" + } + ] + } + }, + "VpcPublicSubnet3RouteTable93458DBB": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-glue-connection/Vpc/PublicSubnet3" + } + ] + } + }, + "VpcPublicSubnet3RouteTableAssociation1F1EDF02": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet3RouteTable93458DBB" + }, + "SubnetId": { + "Ref": "VpcPublicSubnet3SubnetBE12F0B6" + } + } + }, + "VpcPublicSubnet3DefaultRoute4697774F": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPublicSubnet3RouteTable93458DBB" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VpcIGWD7BA715C" + } + }, + "DependsOn": [ + "VpcVPCGWBF912B6E" + ] + }, + "VpcPublicSubnet3EIP3A666A23": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "aws-glue-connection/Vpc/PublicSubnet3" + } + ] + } + }, + "VpcPublicSubnet3NATGateway7640CD1D": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VpcPublicSubnet3EIP3A666A23", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VpcPublicSubnet3SubnetBE12F0B6" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-glue-connection/Vpc/PublicSubnet3" + } + ] + } + }, + "VpcPrivateSubnet1Subnet536B997A": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.96.0/19", + "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-glue-connection/Vpc/PrivateSubnet1" + } + ] + } + }, + "VpcPrivateSubnet1RouteTableB2C5B500": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-glue-connection/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.128.0/19", + "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-glue-connection/Vpc/PrivateSubnet2" + } + ] + } + }, + "VpcPrivateSubnet2RouteTableA678073B": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-glue-connection/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" + } + } + }, + "VpcPrivateSubnet3SubnetF258B56E": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.160.0/19", + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "AvailabilityZone": "test-region-1c", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "aws-glue-connection/Vpc/PrivateSubnet3" + } + ] + } + }, + "VpcPrivateSubnet3RouteTableD98824C7": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-glue-connection/Vpc/PrivateSubnet3" + } + ] + } + }, + "VpcPrivateSubnet3RouteTableAssociation16BDDC43": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet3RouteTableD98824C7" + }, + "SubnetId": { + "Ref": "VpcPrivateSubnet3SubnetF258B56E" + } + } + }, + "VpcPrivateSubnet3DefaultRoute94B74F0D": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VpcPrivateSubnet3RouteTableD98824C7" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VpcPublicSubnet3NATGateway7640CD1D" + } + } + }, + "VpcIGWD7BA715C": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-glue-connection/Vpc" + } + ] + } + }, + "VpcVPCGWBF912B6E": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "VpcId": { + "Ref": "Vpc8378EB38" + }, + "InternetGatewayId": { + "Ref": "VpcIGWD7BA715C" + } + } + }, + "SecurityGroupDD263621": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "aws-glue-connection/SecurityGroup", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + } + }, + "NetworkConnection2B07B7E5": { + "Type": "AWS::Glue::Connection", + "Properties": { + "CatalogId": { + "Ref": "AWS::AccountId" + }, + "ConnectionInput": { + "ConnectionType": "NETWORK", + "PhysicalConnectionRequirements": { + "AvailabilityZone": "test-region-1a", + "SecurityGroupIdList": [ + { + "Fn::GetAtt": [ + "SecurityGroupDD263621", + "GroupId" + ] + } + ], + "SubnetId": { + "Ref": "VpcPrivateSubnet1Subnet536B997A" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-glue/test/integ.connection.ts b/packages/@aws-cdk/aws-glue/test/integ.connection.ts new file mode 100644 index 0000000000000..3d586f97c85c0 --- /dev/null +++ b/packages/@aws-cdk/aws-glue/test/integ.connection.ts @@ -0,0 +1,20 @@ +import * as ec2 from '@aws-cdk/aws-ec2'; +import * as cdk from '@aws-cdk/core'; +import * as glue from '../lib'; + +const app = new cdk.App(); + +const stack = new cdk.Stack(app, 'aws-glue-connection'); + +const vpc = new ec2.Vpc(stack, 'Vpc'); + +const sg = new ec2.SecurityGroup(stack, 'SecurityGroup', { + vpc, +}); + +// Network connection +new glue.Connection(stack, 'NetworkConnection', { + type: glue.ConnectionType.NETWORK, + subnet: vpc.privateSubnets[0], + securityGroups: [sg], +}); diff --git a/packages/@aws-cdk/aws-glue/test/integ.security-configuration.expected.json b/packages/@aws-cdk/aws-glue/test/integ.security-configuration.expected.json new file mode 100644 index 0000000000000..8d8dd30dab49d --- /dev/null +++ b/packages/@aws-cdk/aws-glue/test/integ.security-configuration.expected.json @@ -0,0 +1,161 @@ +{ + "Resources": { + "Key961B73FD": { + "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": "*" + } + ], + "Version": "2012-10-17" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "KeyedSC862A23F3": { + "Type": "AWS::Glue::SecurityConfiguration", + "Properties": { + "EncryptionConfiguration": { + "CloudWatchEncryption": { + "CloudWatchEncryptionMode": "SSE-KMS", + "KmsKeyArn": { + "Fn::GetAtt": [ + "Key961B73FD", + "Arn" + ] + } + }, + "JobBookmarksEncryption": { + "JobBookmarksEncryptionMode": "CSE-KMS", + "KmsKeyArn": { + "Fn::GetAtt": [ + "Key961B73FD", + "Arn" + ] + } + }, + "S3Encryptions": [ + { + "KmsKeyArn": { + "Fn::GetAtt": [ + "Key961B73FD", + "Arn" + ] + }, + "S3EncryptionMode": "SSE-KMS" + } + ] + }, + "Name": "KeyedSC" + } + }, + "KeylessSCKey4D3DE803": { + "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": "*" + } + ], + "Version": "2012-10-17" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "KeylessSC42E312EC": { + "Type": "AWS::Glue::SecurityConfiguration", + "Properties": { + "EncryptionConfiguration": { + "CloudWatchEncryption": { + "CloudWatchEncryptionMode": "SSE-KMS", + "KmsKeyArn": { + "Fn::GetAtt": [ + "KeylessSCKey4D3DE803", + "Arn" + ] + } + }, + "JobBookmarksEncryption": { + "JobBookmarksEncryptionMode": "CSE-KMS", + "KmsKeyArn": { + "Fn::GetAtt": [ + "KeylessSCKey4D3DE803", + "Arn" + ] + } + }, + "S3Encryptions": [ + { + "KmsKeyArn": { + "Fn::GetAtt": [ + "KeylessSCKey4D3DE803", + "Arn" + ] + }, + "S3EncryptionMode": "SSE-KMS" + } + ] + }, + "Name": "KeylessSC" + } + }, + "S3SCE31C83BE": { + "Type": "AWS::Glue::SecurityConfiguration", + "Properties": { + "EncryptionConfiguration": { + "S3Encryptions": [ + { + "S3EncryptionMode": "SSE-S3" + } + ] + }, + "Name": "S3SC" + } + } + } +} diff --git a/packages/@aws-cdk/aws-glue/test/integ.security-configuration.ts b/packages/@aws-cdk/aws-glue/test/integ.security-configuration.ts new file mode 100644 index 0000000000000..e10ef4d140adc --- /dev/null +++ b/packages/@aws-cdk/aws-glue/test/integ.security-configuration.ts @@ -0,0 +1,50 @@ +import * as kms from '@aws-cdk/aws-kms'; +import * as cdk from '@aws-cdk/core'; +import * as glue from '../lib'; + +const app = new cdk.App(); + +const stack = new cdk.Stack(app, 'aws-glue-security-configuration'); + +const key = new kms.Key(stack, 'Key'); + +// SecurityConfiguration for all 3 (s3, cloudwatch and job bookmarks) in modes requiring kms keys +new glue.SecurityConfiguration(stack, 'KeyedSC', { + securityConfigurationName: 'KeyedSC', + jobBookmarksEncryption: { + mode: glue.JobBookmarksEncryptionMode.CLIENT_SIDE_KMS, + kmsKey: key, + }, + cloudWatchEncryption: { + mode: glue.CloudWatchEncryptionMode.KMS, + kmsKey: key, + }, + s3Encryption: { + mode: glue.S3EncryptionMode.KMS, + kmsKey: key, + }, +}); + +// SecurityConfiguration for all 3 (s3, cloudwatch and job bookmarks) in modes requiring kms keys without one provided +new glue.SecurityConfiguration(stack, 'KeylessSC', { + securityConfigurationName: 'KeylessSC', + jobBookmarksEncryption: { + mode: glue.JobBookmarksEncryptionMode.CLIENT_SIDE_KMS, + }, + cloudWatchEncryption: { + mode: glue.CloudWatchEncryptionMode.KMS, + }, + s3Encryption: { + mode: glue.S3EncryptionMode.KMS, + }, +}); + +// SecurityConfiguration for s3 not requiring kms key +new glue.SecurityConfiguration(stack, 'S3SC', { + securityConfigurationName: 'S3SC', + s3Encryption: { + mode: glue.S3EncryptionMode.S3_MANAGED, + }, +}); + +app.synth(); diff --git a/packages/@aws-cdk/aws-glue/test/integ.table.expected.json b/packages/@aws-cdk/aws-glue/test/integ.table.expected.json index d9f6d7f602195..c95d567dfa2bd 100644 --- a/packages/@aws-cdk/aws-glue/test/integ.table.expected.json +++ b/packages/@aws-cdk/aws-glue/test/integ.table.expected.json @@ -302,23 +302,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { @@ -339,25 +323,6 @@ } }, "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "MyUserDC45028B", - "Arn" - ] - } - }, - "Resource": "*" } ], "Version": "2012-10-17" @@ -837,4 +802,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-glue/test/security-configuration.test.ts b/packages/@aws-cdk/aws-glue/test/security-configuration.test.ts new file mode 100644 index 0000000000000..cd94a660dfcb3 --- /dev/null +++ b/packages/@aws-cdk/aws-glue/test/security-configuration.test.ts @@ -0,0 +1,122 @@ +import * as cdkassert from '@aws-cdk/assert'; +import * as kms from '@aws-cdk/aws-kms'; +import * as cdk from '@aws-cdk/core'; +import '@aws-cdk/assert/jest'; +import * as glue from '../lib'; + +test('throws when a security configuration has no encryption config', () => { + const stack = new cdk.Stack(); + + expect(() => new glue.SecurityConfiguration(stack, 'SecurityConfiguration', { + securityConfigurationName: 'name', + })).toThrowError(/One of cloudWatchEncryption, jobBookmarksEncryption or s3Encryption must be defined/); +}); + +test('a security configuration with encryption configuration requiring kms key and providing an explicit one', () => { + const stack = new cdk.Stack(); + const keyArn = 'arn:aws:kms:us-west-2:111122223333:key/test-key'; + const key = kms.Key.fromKeyArn(stack, 'ImportedKey', keyArn); + + const securityConfiguration = new glue.SecurityConfiguration(stack, 'SecurityConfiguration', { + securityConfigurationName: 'name', + cloudWatchEncryption: { + mode: glue.CloudWatchEncryptionMode.KMS, + kmsKey: key, + }, + }); + + expect(securityConfiguration.cloudWatchEncryptionKey?.keyArn).toEqual(keyArn); + expect(securityConfiguration.jobBookmarksEncryptionKey).toBeUndefined(); + expect(securityConfiguration.s3EncryptionKey).toBeUndefined(); + + cdkassert.expect(stack).to(cdkassert.haveResource('AWS::Glue::SecurityConfiguration', { + Name: 'name', + EncryptionConfiguration: { + CloudWatchEncryption: { + CloudWatchEncryptionMode: 'SSE-KMS', + KmsKeyArn: keyArn, + }, + }, + })); +}); + +test('a security configuration with an encryption configuration requiring kms key but not providing an explicit one', () => { + const stack = new cdk.Stack(); + + const securityConfiguration = new glue.SecurityConfiguration(stack, 'SecurityConfiguration', { + securityConfigurationName: 'name', + cloudWatchEncryption: { + mode: glue.CloudWatchEncryptionMode.KMS, + }, + }); + + expect(securityConfiguration.cloudWatchEncryptionKey).toBeDefined(); + expect(securityConfiguration.jobBookmarksEncryptionKey).toBeUndefined(); + expect(securityConfiguration.s3EncryptionKey).toBeUndefined(); + + cdkassert.expect(stack).to(cdkassert.haveResource('AWS::KMS::Key')); + + cdkassert.expect(stack).to(cdkassert.haveResource('AWS::Glue::SecurityConfiguration', { + Name: 'name', + EncryptionConfiguration: { + CloudWatchEncryption: { + CloudWatchEncryptionMode: 'SSE-KMS', + KmsKeyArn: stack.resolve(securityConfiguration.cloudWatchEncryptionKey?.keyArn), + }, + }, + })); +}); + +test('a security configuration with all encryption configs and mixed kms key inputs', () => { + const stack = new cdk.Stack(); + const keyArn = 'arn:aws:kms:us-west-2:111122223333:key/test-key'; + const key = kms.Key.fromKeyArn(stack, 'ImportedKey', keyArn); + + const securityConfiguration = new glue.SecurityConfiguration(stack, 'SecurityConfiguration', { + securityConfigurationName: 'name', + cloudWatchEncryption: { + mode: glue.CloudWatchEncryptionMode.KMS, + }, + jobBookmarksEncryption: { + mode: glue.JobBookmarksEncryptionMode.CLIENT_SIDE_KMS, + kmsKey: key, + }, + s3Encryption: { + mode: glue.S3EncryptionMode.S3_MANAGED, + }, + }); + + expect(securityConfiguration.cloudWatchEncryptionKey).toBeDefined(); + expect(securityConfiguration.jobBookmarksEncryptionKey?.keyArn).toEqual(keyArn); + expect(securityConfiguration.s3EncryptionKey).toBeUndefined(); + + cdkassert.expect(stack).to(cdkassert.haveResource('AWS::KMS::Key')); + + cdkassert.expect(stack).to(cdkassert.haveResource('AWS::Glue::SecurityConfiguration', { + Name: 'name', + EncryptionConfiguration: { + CloudWatchEncryption: { + CloudWatchEncryptionMode: 'SSE-KMS', + // auto-created kms key + KmsKeyArn: stack.resolve(securityConfiguration.cloudWatchEncryptionKey?.keyArn), + }, + JobBookmarksEncryption: { + JobBookmarksEncryptionMode: 'CSE-KMS', + // explicitly provided kms key + KmsKeyArn: keyArn, + }, + S3Encryptions: [{ + S3EncryptionMode: 'SSE-S3', + }], + }, + })); +}); + +test('fromSecurityConfigurationName', () => { + const stack = new cdk.Stack(); + const name = 'name'; + + const securityConfiguration = glue.SecurityConfiguration.fromSecurityConfigurationName(stack, 'ImportedSecurityConfiguration', name); + + expect(securityConfiguration.securityConfigurationName).toEqual(name); +}); diff --git a/packages/@aws-cdk/aws-glue/test/table.test.ts b/packages/@aws-cdk/aws-glue/test/table.test.ts index ec92e7ced4ff2..0969088346491 100644 --- a/packages/@aws-cdk/aws-glue/test/table.test.ts +++ b/packages/@aws-cdk/aws-glue/test/table.test.ts @@ -345,50 +345,6 @@ test('encrypted table: SSE-KMS (implicitly created key)', () => { equal(table.encryptionKey, table.bucket.encryptionKey); cdkExpect(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: '*', - }, - ], - Version: '2012-10-17', - }, Description: 'Created by Default/Table/Bucket', })); @@ -462,7 +418,9 @@ test('encrypted table: SSE-KMS (explicitly created key)', () => { const database = new glue.Database(stack, 'Database', { databaseName: 'database', }); - const encryptionKey = new kms.Key(stack, 'MyKey'); + const encryptionKey = new kms.Key(stack, 'MyKey', { + description: 'OurKey', + }); const table = new glue.Table(stack, 'Table', { database, @@ -480,50 +438,7 @@ test('encrypted table: SSE-KMS (explicitly created key)', () => { notEqual(table.encryptionKey, undefined); cdkExpect(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: '*', - }, - ], - Version: '2012-10-17', - }, + Description: 'OurKey', })); cdkExpect(stack).to(haveResource('AWS::S3::Bucket', { @@ -690,52 +605,7 @@ test('encrypted table: CSE-KMS (implicitly created key)', () => { notEqual(table.encryptionKey, undefined); equal(table.bucket.encryptionKey, undefined); - cdkExpect(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: '*', - }, - ], - Version: '2012-10-17', - }, - })); + cdkExpect(stack).to(haveResource('AWS::KMS::Key')); cdkExpect(stack).to(haveResource('AWS::Glue::Table', { CatalogId: { @@ -789,7 +659,9 @@ test('encrypted table: CSE-KMS (explicitly created key)', () => { const database = new glue.Database(stack, 'Database', { databaseName: 'database', }); - const encryptionKey = new kms.Key(stack, 'MyKey'); + const encryptionKey = new kms.Key(stack, 'MyKey', { + description: 'MyKey', + }); const table = new glue.Table(stack, 'Table', { database, @@ -807,50 +679,7 @@ test('encrypted table: CSE-KMS (explicitly created key)', () => { equal(table.bucket.encryptionKey, undefined); cdkExpect(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: '*', - }, - ], - Version: '2012-10-17', - }, + Description: 'MyKey', })); cdkExpect(stack).to(haveResource('AWS::Glue::Table', { @@ -906,7 +735,9 @@ test('encrypted table: CSE-KMS (explicitly passed bucket and key)', () => { databaseName: 'database', }); const bucket = new s3.Bucket(stack, 'Bucket'); - const encryptionKey = new kms.Key(stack, 'MyKey'); + const encryptionKey = new kms.Key(stack, 'MyKey', { + description: 'MyKey', + }); const table = new glue.Table(stack, 'Table', { database, @@ -925,50 +756,7 @@ test('encrypted table: CSE-KMS (explicitly passed bucket and key)', () => { equal(table.bucket.encryptionKey, undefined); cdkExpect(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: '*', - }, - ], - Version: '2012-10-17', - }, + Description: 'MyKey', })); cdkExpect(stack).to(haveResource('AWS::Glue::Table', { diff --git a/packages/@aws-cdk/aws-greengrass/package.json b/packages/@aws-cdk/aws-greengrass/package.json index 3ecb652d79f37..666c2f02b99f3 100644 --- a/packages/@aws-cdk/aws-greengrass/package.json +++ b/packages/@aws-cdk/aws-greengrass/package.json @@ -93,5 +93,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-greengrassv2/package.json b/packages/@aws-cdk/aws-greengrassv2/package.json index be7357654ce55..68d9557122c34 100644 --- a/packages/@aws-cdk/aws-greengrassv2/package.json +++ b/packages/@aws-cdk/aws-greengrassv2/package.json @@ -93,5 +93,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-guardduty/package.json b/packages/@aws-cdk/aws-guardduty/package.json index 11e01410f2509..2d772252718cb 100644 --- a/packages/@aws-cdk/aws-guardduty/package.json +++ b/packages/@aws-cdk/aws-guardduty/package.json @@ -92,5 +92,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-iam/package.json b/packages/@aws-cdk/aws-iam/package.json index b34b959da8488..6455688b5c006 100644 --- a/packages/@aws-cdk/aws-iam/package.json +++ b/packages/@aws-cdk/aws-iam/package.json @@ -84,7 +84,7 @@ "cfn2ts": "0.0.0", "jest": "^26.6.3", "pkglint": "0.0.0", - "sinon": "^9.2.1" + "sinon": "^9.2.4" }, "dependencies": { "@aws-cdk/core": "0.0.0", @@ -113,5 +113,8 @@ "awscdkio": { "announce": false }, - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-imagebuilder/package.json b/packages/@aws-cdk/aws-imagebuilder/package.json index a05909901d105..25cf2f2c92657 100644 --- a/packages/@aws-cdk/aws-imagebuilder/package.json +++ b/packages/@aws-cdk/aws-imagebuilder/package.json @@ -91,5 +91,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-inspector/package.json b/packages/@aws-cdk/aws-inspector/package.json index d61b15d3c8b21..a94beb9c3ab2e 100644 --- a/packages/@aws-cdk/aws-inspector/package.json +++ b/packages/@aws-cdk/aws-inspector/package.json @@ -92,5 +92,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-iot/package.json b/packages/@aws-cdk/aws-iot/package.json index 57e349ab43059..baec64baf8142 100644 --- a/packages/@aws-cdk/aws-iot/package.json +++ b/packages/@aws-cdk/aws-iot/package.json @@ -92,5 +92,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-iot1click/package.json b/packages/@aws-cdk/aws-iot1click/package.json index 7206bea590918..4000a6cd6721d 100644 --- a/packages/@aws-cdk/aws-iot1click/package.json +++ b/packages/@aws-cdk/aws-iot1click/package.json @@ -92,5 +92,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-iotanalytics/package.json b/packages/@aws-cdk/aws-iotanalytics/package.json index 9ccf98c8c45b3..e3b33ffa331b8 100644 --- a/packages/@aws-cdk/aws-iotanalytics/package.json +++ b/packages/@aws-cdk/aws-iotanalytics/package.json @@ -93,5 +93,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-iotevents/package.json b/packages/@aws-cdk/aws-iotevents/package.json index eed3b7f8cde17..d0ecd71421f76 100644 --- a/packages/@aws-cdk/aws-iotevents/package.json +++ b/packages/@aws-cdk/aws-iotevents/package.json @@ -93,5 +93,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-iotsitewise/package.json b/packages/@aws-cdk/aws-iotsitewise/package.json index 1f6d59ceaf8a2..e7301a75e3f49 100644 --- a/packages/@aws-cdk/aws-iotsitewise/package.json +++ b/packages/@aws-cdk/aws-iotsitewise/package.json @@ -91,5 +91,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-iotthingsgraph/package.json b/packages/@aws-cdk/aws-iotthingsgraph/package.json index 7f861b23c861b..59c5396d37c79 100644 --- a/packages/@aws-cdk/aws-iotthingsgraph/package.json +++ b/packages/@aws-cdk/aws-iotthingsgraph/package.json @@ -93,5 +93,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-iotwireless/package.json b/packages/@aws-cdk/aws-iotwireless/package.json index 68730c1aaebd6..df0a35c2bd8fe 100644 --- a/packages/@aws-cdk/aws-iotwireless/package.json +++ b/packages/@aws-cdk/aws-iotwireless/package.json @@ -91,5 +91,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-ivs/package.json b/packages/@aws-cdk/aws-ivs/package.json index e89ed75d3c82f..ce179233a9efa 100644 --- a/packages/@aws-cdk/aws-ivs/package.json +++ b/packages/@aws-cdk/aws-ivs/package.json @@ -104,5 +104,8 @@ "maturity": "experimental", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-kendra/package.json b/packages/@aws-cdk/aws-kendra/package.json index c8992330a0419..0ce7d188ea30f 100644 --- a/packages/@aws-cdk/aws-kendra/package.json +++ b/packages/@aws-cdk/aws-kendra/package.json @@ -91,5 +91,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-kinesis/package.json b/packages/@aws-cdk/aws-kinesis/package.json index a8b384d677161..e8153880812db 100644 --- a/packages/@aws-cdk/aws-kinesis/package.json +++ b/packages/@aws-cdk/aws-kinesis/package.json @@ -99,5 +99,8 @@ "maturity": "stable", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-kinesis/test/stream.test.ts b/packages/@aws-cdk/aws-kinesis/test/stream.test.ts index b9bcf8b92bca8..9c3cdc1eb7125 100644 --- a/packages/@aws-cdk/aws-kinesis/test/stream.test.ts +++ b/packages/@aws-cdk/aws-kinesis/test/stream.test.ts @@ -1,7 +1,9 @@ import '@aws-cdk/assert/jest'; +import { arrayWith } from '@aws-cdk/assert'; import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; import { App, Duration, Stack, CfnParameter } from '@aws-cdk/core'; +import { testFutureBehavior, testLegacyBehavior } from 'cdk-build-tools/lib/feature-flag'; import { Stream, StreamEncryption } from '../lib'; /* eslint-disable quote-props */ @@ -329,77 +331,18 @@ describe('Kinesis data streams', () => { test('auto-creates KMS key if encryption type is KMS but no key is provided', () => { const stack = new Stack(); - new Stream(stack, 'MyStream', { + const stream = new Stream(stack, 'MyStream', { encryption: StreamEncryption.KMS, }); - expect(stack).toMatchTemplate({ - Resources: { - MyStreamKey76F3300E: { - Type: 'AWS::KMS::Key', - Properties: { - Description: 'Created by Default/MyStream', - 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: '*', - }, - ], - Version: '2012-10-17', - }, - }, - DeletionPolicy: 'Retain', - UpdateReplacePolicy: 'Retain', - }, - MyStream5C050E93: { - Type: 'AWS::Kinesis::Stream', - Properties: { - RetentionPeriodHours: 24, - ShardCount: 1, - StreamEncryption: { - EncryptionType: 'KMS', - KeyId: { - 'Fn::GetAtt': ['MyStreamKey76F3300E', 'Arn'], - }, - }, - }, - }, + expect(stack).toHaveResource('AWS::KMS::Key', { + Description: 'Created by Default/MyStream', + }); + + expect(stack).toHaveResource('AWS::Kinesis::Stream', { + StreamEncryption: { + EncryptionType: 'KMS', + KeyId: stack.resolve(stream.encryptionKey?.keyArn), }, }); }), @@ -416,78 +359,21 @@ describe('Kinesis data streams', () => { encryptionKey: explicitKey, }); - expect(stack).toMatchTemplate({ - Resources: { - ExplicitKey7DF42F37: { - Type: 'AWS::KMS::Key', - Properties: { - Description: 'Explicit 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: '*', - }, - ], - Version: '2012-10-17', - }, - }, - DeletionPolicy: 'Retain', - UpdateReplacePolicy: 'Retain', - }, - MyStream5C050E93: { - Type: 'AWS::Kinesis::Stream', - Properties: { - RetentionPeriodHours: 24, - ShardCount: 1, - StreamEncryption: { - EncryptionType: 'KMS', - KeyId: { - 'Fn::GetAtt': ['ExplicitKey7DF42F37', 'Arn'], - }, - }, - }, - }, + expect(stack).toHaveResource('AWS::KMS::Key', { + Description: 'Explicit Key', + }); + + expect(stack).toHaveResource('AWS::Kinesis::Stream', { + RetentionPeriodHours: 24, + ShardCount: 1, + StreamEncryption: { + EncryptionType: 'KMS', + KeyId: stack.resolve(explicitKey.keyArn), }, }); }), - test('grantRead creates and attaches a policy with read only access to Stream and EncryptionKey', () => { + test('grantRead creates and attaches a policy with read only access to the principal', () => { const stack = new Stack(); const stream = new Stream(stack, 'MyStream', { encryption: StreamEncryption.KMS, @@ -496,6 +382,34 @@ describe('Kinesis data streams', () => { const user = new iam.User(stack, 'MyUser'); stream.grantRead(user); + expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + PolicyDocument: { + Statement: arrayWith({ + Action: 'kms:Decrypt', + Effect: 'Allow', + Resource: stack.resolve(stream.encryptionKey?.keyArn), + }), + }, + }); + + expect(stack).toHaveResourceLike('AWS::Kinesis::Stream', { + StreamEncryption: { + KeyId: stack.resolve(stream.encryptionKey?.keyArn), + }, + }); + }); + + // only applicable to legacy behaviour + // With the '@aws-cdk/aws-kms:defaultKeyPolicies' feature flag, KMS key policy is not updated. + testLegacyBehavior('grantRead creates and attaches a policy with read only access to EncryptionKey', App, (app) => { + const stack = new Stack(app); + const stream = new Stream(stack, 'MyStream', { + encryption: StreamEncryption.KMS, + }); + + const user = new iam.User(stack, 'MyUser'); + stream.grantRead(user); + expect(stack).toMatchTemplate({ Resources: { MyStreamKey76F3300E: { @@ -616,7 +530,7 @@ describe('Kinesis data streams', () => { }); }), - test('grantWrite creates and attaches a policy with write only access to Stream and EncryptionKey', () => { + test('grantWrite creates and attaches a policy with write only access to the principal', () => { const stack = new Stack(); const stream = new Stream(stack, 'MyStream', { encryption: StreamEncryption.KMS, @@ -625,6 +539,34 @@ describe('Kinesis data streams', () => { const user = new iam.User(stack, 'MyUser'); stream.grantWrite(user); + expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + PolicyDocument: { + Statement: arrayWith({ + Action: ['kms:Encrypt', 'kms:ReEncrypt*', 'kms:GenerateDataKey*'], + Effect: 'Allow', + Resource: stack.resolve(stream.encryptionKey?.keyArn), + }), + }, + }); + + expect(stack).toHaveResourceLike('AWS::Kinesis::Stream', { + StreamEncryption: { + KeyId: stack.resolve(stream.encryptionKey?.keyArn), + }, + }); + }); + + // only applicable to legacy behaviour + // With the '@aws-cdk/aws-kms:defaultKeyPolicies' feature flag, KMS key policy is not updated. + testLegacyBehavior('grantWrite creates and attaches a policy with write only access to EncryptionKey', App, (app) => { + const stack = new Stack(app); + const stream = new Stream(stack, 'MyStream', { + encryption: StreamEncryption.KMS, + }); + + const user = new iam.User(stack, 'MyUser'); + stream.grantWrite(user); + expect(stack).toMatchTemplate({ Resources: { MyStreamKey76F3300E: { @@ -739,7 +681,7 @@ describe('Kinesis data streams', () => { }); }), - test('grantReadWrite creates and attaches a policy with access to Stream and EncryptionKey', () => { + test('grantReadWrite creates and attaches a policy to the principal', () => { const stack = new Stack(); const stream = new Stream(stack, 'MyStream', { encryption: StreamEncryption.KMS, @@ -748,6 +690,34 @@ describe('Kinesis data streams', () => { const user = new iam.User(stack, 'MyUser'); stream.grantReadWrite(user); + expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + PolicyDocument: { + Statement: arrayWith({ + Action: ['kms:Decrypt', 'kms:Encrypt', 'kms:ReEncrypt*', 'kms:GenerateDataKey*'], + Effect: 'Allow', + Resource: stack.resolve(stream.encryptionKey?.keyArn), + }), + }, + }); + + expect(stack).toHaveResourceLike('AWS::Kinesis::Stream', { + StreamEncryption: { + KeyId: stack.resolve(stream.encryptionKey?.keyArn), + }, + }); + }); + + // only applicable to legacy behaviour + // With the '@aws-cdk/aws-kms:defaultKeyPolicies' feature flag, KMS key policy is not updated. + testLegacyBehavior('grantReadWrite creates and attaches a policy with access to EncryptionKey', App, (app) => { + const stack = new Stack(app); + const stream = new Stream(stack, 'MyStream', { + encryption: StreamEncryption.KMS, + }); + + const user = new iam.User(stack, 'MyUser'); + stream.grantReadWrite(user); + expect(stack).toMatchTemplate({ Resources: { MyStreamKey76F3300E: { @@ -1270,8 +1240,8 @@ describe('Kinesis data streams', () => { }); }), - test('fails with encryption due to cyclic dependency', () => { - const app = new App(); + // legacy behaviour as this is fixed with the feature flag. see subsequent test. + testLegacyBehavior('fails with encryption due to cyclic dependency', App, (app) => { const stackA = new Stack(app, 'stackA'); const streamFromStackA = new Stream(stackA, 'MyStream', { encryption: StreamEncryption.KMS, @@ -1285,6 +1255,29 @@ describe('Kinesis data streams', () => { }).toThrow(/'stack.' depends on 'stack.'/); }); + testFutureBehavior('cross stack permissions - with encryption', { '@aws-cdk/aws-kms:defaultKeyPolicies': true }, App, (app) => { + const stackA = new Stack(app, 'stackA'); + const streamFromStackA = new Stream(stackA, 'MyStream', { + encryption: StreamEncryption.KMS, + }); + + const stackB = new Stack(app, 'stackB'); + const user = new iam.User(stackB, 'UserWhoNeedsAccess'); + streamFromStackA.grantRead(user); + + expect(stackB).toHaveResourceLike('AWS::IAM::Policy', { + PolicyDocument: { + Statement: arrayWith({ + Action: 'kms:Decrypt', + Effect: 'Allow', + Resource: { + 'Fn::ImportValue': 'stackA:ExportsOutputFnGetAttMyStreamKey76F3300EArn190947B4', + }, + }), + }, + }); + }); + test('accepts if retentionPeriodHours is a Token', () => { const stack = new Stack(); diff --git a/packages/@aws-cdk/aws-kinesisanalytics-flink/.eslintrc.js b/packages/@aws-cdk/aws-kinesisanalytics-flink/.eslintrc.js new file mode 100644 index 0000000000000..61dd8dd001f63 --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisanalytics-flink/.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-kinesisanalytics-flink/.gitignore b/packages/@aws-cdk/aws-kinesisanalytics-flink/.gitignore new file mode 100644 index 0000000000000..d8a8561d50885 --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisanalytics-flink/.gitignore @@ -0,0 +1,19 @@ +*.js +*.js.map +*.d.ts +tsconfig.json +node_modules +*.generated.ts +dist +.jsii + +.LAST_BUILD +.nyc_output +coverage +nyc.config.js +.LAST_PACKAGE +*.snk +!.eslintrc.js +!jest.config.js + +junit.xml \ No newline at end of file diff --git a/packages/@aws-cdk/aws-kinesisanalytics-flink/.npmignore b/packages/@aws-cdk/aws-kinesisanalytics-flink/.npmignore new file mode 100644 index 0000000000000..63ab95621c764 --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisanalytics-flink/.npmignore @@ -0,0 +1,27 @@ +# 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/ \ No newline at end of file diff --git a/packages/@aws-cdk/aws-kinesisanalytics-flink/LICENSE b/packages/@aws-cdk/aws-kinesisanalytics-flink/LICENSE new file mode 100644 index 0000000000000..28e4bdcec77ec --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisanalytics-flink/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-kinesisanalytics-flink/NOTICE b/packages/@aws-cdk/aws-kinesisanalytics-flink/NOTICE new file mode 100644 index 0000000000000..5fc3826926b5b --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisanalytics-flink/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-kinesisanalytics-flink/README.md b/packages/@aws-cdk/aws-kinesisanalytics-flink/README.md new file mode 100644 index 0000000000000..7dc9b1a088b16 --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisanalytics-flink/README.md @@ -0,0 +1,74 @@ +# Kinesis Analytics Flink + + +--- + +![cdk-constructs: Experimental](https://img.shields.io/badge/cdk--constructs-experimental-important.svg?style=for-the-badge) + +> The APIs of higher level constructs in this module are experimental and under active development. +> They are subject to non-backward compatible changes or removal in any future version. These are +> not subject to the [Semantic Versioning](https://semver.org/) model and breaking changes will be +> announced in the release notes. This means that while you may use them, you may need to update +> your source code when upgrading to a newer version of this package. + +--- + + + +This package provides constructs for creating Kinesis Analytics Flink +applications. To learn more about using using managed Flink applications, see +the [AWS developer +guide](https://docs.aws.amazon.com/kinesisanalytics/latest/java/what-is.html). + +## Creating Flink Applications + +To create a new Flink application, use the `Application` construct: + +[simple flink application](test/integ.application.lit.ts) + +The `code` property can use `fromAsset` as shown above to reference a local jar +file in s3 or `fromBucket` to reference a file in s3. + +[flink application using code from bucket](test/integ.application-code-from-bucket.lit.ts) + +The `propertyGroups` property provides a way of passing arbitrary runtime +properties to your Flink application. You can use the +aws-kinesisanalytics-runtime library to [retrieve these +properties](https://docs.aws.amazon.com/kinesisanalytics/latest/java/how-properties.html#how-properties-access). + +```ts +import * as flink from '@aws-cdk/aws-kinesisanalytics-flink'; + +const flinkApp = new flink.Application(this, 'Application', { + // ... + propertyGroups: { + FlinkApplicationProperties: { + inputStreamName: 'my-input-kinesis-stream', + outputStreamName: 'my-output-kinesis-stream', + }, + }, +}); +``` + +Flink applications also have specific configuration for passing parameters +when the Flink job starts. These include parameters for checkpointing, +snapshotting, monitoring, and parallelism. + +```ts +import * as logs from '@aws-cdk/aws-logs'; + +const flinkApp = new flink.Application(this, 'Application', { + code: flink.ApplicationCode.fromBucket(bucket, 'my-app.jar'), + runtime: file.Runtime.FLINK_1_11, + checkpointingEnabled: true, // default is true + checkpointInterval: cdk.Duration.seconds(30), // default is 1 minute + minPausesBetweenCheckpoints: cdk.Duration.seconds(10), // default is 5 seconds + logLevel: flink.LogLevel.ERROR, // default is INFO + metricsLevel: flink.MetricsLevel.PARALLELISM, // default is APPLICATION + autoScalingEnabled: false, // default is true + parallelism: 32, // default is 1 + parallelismPerKpu: 2, // default is 1 + snapshotsEnabled: false, // default is true + logGroup: new logs.LogGroup(this, 'LogGroup'), // by default, a new LogGroup will be created +}); +``` diff --git a/packages/@aws-cdk/aws-kinesisanalytics-flink/jest.config.js b/packages/@aws-cdk/aws-kinesisanalytics-flink/jest.config.js new file mode 100644 index 0000000000000..54e28beb9798b --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisanalytics-flink/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-kinesisanalytics-flink/lib/application-code.ts b/packages/@aws-cdk/aws-kinesisanalytics-flink/lib/application-code.ts new file mode 100644 index 0000000000000..c7d7b49180086 --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisanalytics-flink/lib/application-code.ts @@ -0,0 +1,139 @@ +import * as ka from '@aws-cdk/aws-kinesisanalytics'; +import * as s3 from '@aws-cdk/aws-s3'; +import * as s3_assets from '@aws-cdk/aws-s3-assets'; +import { Construct } from '@aws-cdk/core'; + +/** + * The return type of {@link ApplicationCode.bind}. This represents + * CloudFormation configuration and an s3 bucket holding the Flink application + * JAR file. + */ +export interface ApplicationCodeConfig { + /** + * Low-level Cloudformation ApplicationConfigurationProperty + */ + readonly applicationCodeConfigurationProperty: ka.CfnApplicationV2.ApplicationConfigurationProperty; + + /** + * S3 Bucket that stores the Flink application code + */ + readonly bucket: s3.IBucket; +} + +/** + * Code configuration providing the location to a Flink application JAR file. + */ +export abstract class ApplicationCode { + /** + * Reference code from an S3 bucket. + * + * @param bucket - an s3 bucket + * @param fileKey - a key pointing to a Flink JAR file + * @param objectVersion - an optional version string for the provided fileKey + */ + public static fromBucket(bucket: s3.IBucket, fileKey: string, objectVersion?: string): ApplicationCode { + return new BucketApplicationCode({ + bucket, + fileKey, + objectVersion, + }); + } + + /** + * Reference code from a local directory containing a Flink JAR file. + * + * @param path - a local directory path + * @parm options - standard s3 AssetOptions + */ + public static fromAsset(path: string, options?: s3_assets.AssetOptions): ApplicationCode { + return new AssetApplicationCode(path, options); + } + + /** + * A method to lazily bind asset resources to the parent FlinkApplication. + */ + public abstract bind(scope: Construct): ApplicationCodeConfig; +} + +interface BucketApplicationCodeProps { + readonly bucket: s3.IBucket; + readonly fileKey: string; + readonly objectVersion?: string; +} + +class BucketApplicationCode extends ApplicationCode { + public readonly bucket?: s3.IBucket; + public readonly fileKey: string; + public readonly objectVersion?: string; + + constructor(props: BucketApplicationCodeProps) { + super(); + this.bucket = props.bucket; + this.fileKey = props.fileKey; + this.objectVersion = props.objectVersion; + } + + public bind(_scope: Construct): ApplicationCodeConfig { + return { + applicationCodeConfigurationProperty: { + applicationCodeConfiguration: { + codeContent: { + s3ContentLocation: { + bucketArn: this.bucket!.bucketArn, + fileKey: this.fileKey, + objectVersion: this.objectVersion, + }, + }, + codeContentType: 'ZIPFILE', + }, + }, + bucket: this.bucket!, + }; + } +} + +class AssetApplicationCode extends ApplicationCode { + private readonly path: string; + private readonly options?: s3_assets.AssetOptions; + private _asset?: s3_assets.Asset; + + constructor(path: string, options?: s3_assets.AssetOptions) { + super(); + this.path = path; + this.options = options; + } + + public bind(scope: Construct): ApplicationCodeConfig { + this._asset = new s3_assets.Asset(scope, 'Code', { + path: this.path, + ...this.options, + }); + + if (!this._asset.isZipArchive) { + throw new Error(`Asset must be a .zip file or a directory (${this.path})`); + } + + return { + applicationCodeConfigurationProperty: { + applicationCodeConfiguration: { + codeContent: { + s3ContentLocation: { + bucketArn: this._asset.bucket.bucketArn, + fileKey: this._asset.s3ObjectKey, + }, + }, + codeContentType: 'ZIPFILE', + }, + }, + bucket: this._asset.bucket, + }; + } + + get asset(): s3_assets.Asset | undefined { + return this._asset; + } + + get bucket(): s3.IBucket | undefined { + return this._asset?.bucket; + } +} diff --git a/packages/@aws-cdk/aws-kinesisanalytics-flink/lib/application.ts b/packages/@aws-cdk/aws-kinesisanalytics-flink/lib/application.ts new file mode 100644 index 0000000000000..1bcfa0fc4cbbf --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisanalytics-flink/lib/application.ts @@ -0,0 +1,341 @@ +import * as iam from '@aws-cdk/aws-iam'; +import * as ka from '@aws-cdk/aws-kinesisanalytics'; +import * as logs from '@aws-cdk/aws-logs'; +import * as core from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import { ApplicationCode } from './application-code'; +import { environmentProperties } from './private/environment-properties'; +import { flinkApplicationConfiguration } from './private/flink-application-configuration'; +import { validateFlinkApplicationProps as validateApplicationProps } from './private/validation'; +import { LogLevel, MetricsLevel, PropertyGroups, Runtime } from './types'; + +/** + * An interface expressing the public properties on both an imported and + * CDK-created Flink application. + */ +export interface IApplication extends core.IResource, iam.IGrantable { + /** + * The application ARN. + * + * @attribute + */ + readonly applicationArn: string; + + /** + * The name of the Flink application. + * + * @attribute + */ + readonly applicationName: string; + + /** + * The application IAM role. + */ + readonly role?: iam.IRole; + + /** + * Convenience method for adding a policy statement to the application role. + */ + addToRolePolicy(policyStatement: iam.PolicyStatement): boolean; +} + +/** + * Implements the functionality shared between CDK created and imported + * IApplications. + */ +abstract class ApplicationBase extends core.Resource implements IApplication { + public abstract readonly applicationArn: string; + public abstract readonly applicationName: string; + public abstract readonly role?: iam.IRole; + + // Implement iam.IGrantable interface + public abstract readonly grantPrincipal: iam.IPrincipal; + + /** Implement the convenience {@link IApplication.addToPrincipalPolicy} method. */ + public addToRolePolicy(policyStatement: iam.PolicyStatement): boolean { + if (this.role) { + this.role.addToPrincipalPolicy(policyStatement); + return true; + } + + return false; + } +} + +/** + * Props for creating an Application construct. + */ +export interface ApplicationProps { + /** + * A name for your Application that is unique to an AWS account. + * + * @default - CloudFormation-generated name + */ + readonly applicationName?: string; + + /** + * The Flink version to use for this application. + */ + readonly runtime: Runtime; + + /** + * The Flink code asset to run. + */ + readonly code: ApplicationCode; + + /** + * Whether checkpointing is enabled while your application runs. + * + * @default true + */ + readonly checkpointingEnabled?: boolean; + + /** + * The interval between checkpoints. + * + * @default 1 minute + */ + readonly checkpointInterval?: core.Duration; + + /** + * The minimum amount of time in to wait after a checkpoint finishes to start + * a new checkpoint. + * + * @default 5 seconds + */ + readonly minPauseBetweenCheckpoints?: core.Duration; + + /** + * The level of log verbosity from the Flink application. + * + * @default FlinkLogLevel.INFO + */ + readonly logLevel?: LogLevel; + + /** + * Describes the granularity of the CloudWatch metrics for an application. + * Use caution with Parallelism level metrics. Parallelism granularity logs + * metrics for each parallel thread and can quickly become expensive when + * parallelism is high (e.g. > 64). + * + * @default MetricsLevel.APPLICATION + */ + readonly metricsLevel?: MetricsLevel; + + /** + * Whether the Kinesis Data Analytics service can increase the parallelism of + * the application in response to resource usage. + * + * @default true + */ + readonly autoScalingEnabled?: boolean; + + /** + * The initial parallelism for the application. Kinesis Data Analytics can + * stop the app, increase the parallelism, and start the app again if + * autoScalingEnabled is true (the default value). + * + * @default 1 + */ + readonly parallelism?: number; + + /** + * The Flink parallelism allowed per Kinesis Processing Unit (KPU). + * + * @default 1 + */ + readonly parallelismPerKpu?: number + + /** + * Determines if Flink snapshots are enabled. + * + * @default true + */ + readonly snapshotsEnabled?: boolean; + + /** + * Configuration PropertyGroups. You can use these property groups to pass + * arbitrary runtime configuration values to your Flink app. + * + * @default No property group configuration provided to the Flink app + */ + readonly propertyGroups?: PropertyGroups; + + /** + * A role to use to grant permissions to your application. Prefer omitting + * this property and using the default role. + * + * @default - a new Role will be created + */ + readonly role?: iam.IRole; + + /** + * Provide a RemovalPolicy to override the default. + * + * @default RemovalPolicy.DESTROY + */ + readonly removalPolicy?: core.RemovalPolicy; + + /** + * The log group to send log entries to. + * + * @default CDK's default LogGroup + */ + readonly logGroup?: logs.ILogGroup; +} + +/** + * An imported Flink application. + */ +class Import extends ApplicationBase { + public readonly grantPrincipal: iam.IPrincipal; + public readonly role?: iam.IRole; + public readonly applicationName: string; + public readonly applicationArn: string; + + constructor(scope: Construct, id: string, attrs: { applicationArn: string, applicationName: string }) { + super(scope, id); + + // Imported applications have no associated role or grantPrincipal + this.grantPrincipal = new iam.UnknownPrincipal({ resource: this }); + this.role = undefined; + + this.applicationArn = attrs.applicationArn; + this.applicationName = attrs.applicationName; + } +} + +/** + * The L2 construct for Flink Kinesis Data Applications. + * + * @resource AWS::KinesisAnalyticsV2::Application + * + * @experimental + */ +export class Application extends ApplicationBase { + /** + * Import an existing Flink application defined outside of CDK code by + * applicationName. + */ + public static fromApplicationName(scope: Construct, id: string, applicationName: string): IApplication { + const applicationArn = core.Stack.of(scope).formatArn(applicationArnComponents(applicationName)); + + return new Import(scope, id, { applicationArn, applicationName }); + } + + /** + * Import an existing application defined outside of CDK code by + * applicationArn. + */ + public static fromApplicationArn(scope: Construct, id: string, applicationArn: string): IApplication { + const applicationName = core.Stack.of(scope).parseArn(applicationArn).resourceName; + if (!applicationName) { + throw new Error(`applicationArn for fromApplicationArn (${applicationArn}) must include resource name`); + } + + return new Import(scope, id, { applicationArn, applicationName }); + } + + public readonly applicationArn: string; + public readonly applicationName: string; + + // Role must be optional for JSII compatibility + public readonly role?: iam.IRole; + + public readonly grantPrincipal: iam.IPrincipal; + + constructor(scope: Construct, id: string, props: ApplicationProps) { + super(scope, id, { physicalName: props.applicationName }); + validateApplicationProps(props); + + this.role = props.role ?? new iam.Role(this, 'Role', { + assumedBy: new iam.ServicePrincipal('kinesisanalytics.amazonaws.com'), + }); + this.grantPrincipal = this.role; + + // Permit metric publishing to CloudWatch + this.role.addToPrincipalPolicy(new iam.PolicyStatement({ + actions: ['cloudwatch:PutMetricData'], + resources: ['*'], + })); + + const code = props.code.bind(this); + code.bucket.grantRead(this); + + const resource = new ka.CfnApplicationV2(this, 'Resource', { + runtimeEnvironment: props.runtime.value, + serviceExecutionRole: this.role.roleArn, + applicationConfiguration: { + ...code.applicationCodeConfigurationProperty, + environmentProperties: environmentProperties(props.propertyGroups), + flinkApplicationConfiguration: flinkApplicationConfiguration({ + checkpointingEnabled: props.checkpointingEnabled, + checkpointInterval: props.checkpointInterval, + minPauseBetweenCheckpoints: props.minPauseBetweenCheckpoints, + logLevel: props.logLevel, + metricsLevel: props.metricsLevel, + autoScalingEnabled: props.autoScalingEnabled, + parallelism: props.parallelism, + parallelismPerKpu: props.parallelismPerKpu, + }), + applicationSnapshotConfiguration: { + snapshotsEnabled: props.snapshotsEnabled ?? true, + }, + }, + }); + resource.node.addDependency(this.role); + + const logGroup = props.logGroup ?? new logs.LogGroup(this, 'LogGroup'); + const logStream = new logs.LogStream(this, 'LogStream', { logGroup }); + + /* Permit logging */ + + this.role.addToPrincipalPolicy(new iam.PolicyStatement({ + actions: ['logs:DescribeLogGroups'], + resources: [ + core.Stack.of(this).formatArn({ + service: 'logs', + resource: 'log-group', + sep: ':', + resourceName: '*', + }), + ], + })); + + this.role.addToPrincipalPolicy(new iam.PolicyStatement({ + actions: ['logs:DescribeLogStreams'], + resources: [logGroup.logGroupArn], + })); + + const logStreamArn = `arn:${core.Aws.PARTITION}:logs:${core.Aws.REGION}:${core.Aws.ACCOUNT_ID}:log-group:${logGroup.logGroupName}:log-stream:${logStream.logStreamName}`; + this.role.addToPrincipalPolicy(new iam.PolicyStatement({ + actions: ['logs:PutLogEvents'], + resources: [logStreamArn], + })); + + new ka.CfnApplicationCloudWatchLoggingOptionV2(this, 'LoggingOption', { + applicationName: resource.ref, + cloudWatchLoggingOption: { + logStreamArn, + }, + }); + + this.applicationName = this.getResourceNameAttribute(resource.ref); + this.applicationArn = this.getResourceArnAttribute( + core.Stack.of(this).formatArn(applicationArnComponents(resource.ref)), + applicationArnComponents(this.physicalName), + ); + + resource.applyRemovalPolicy(props.removalPolicy, { + default: core.RemovalPolicy.DESTROY, + }); + } +} + +function applicationArnComponents(resourceName: string): core.ArnComponents { + return { + service: 'kinesisanalytics', + resource: 'application', + resourceName, + }; +} diff --git a/packages/@aws-cdk/aws-kinesisanalytics-flink/lib/index.ts b/packages/@aws-cdk/aws-kinesisanalytics-flink/lib/index.ts new file mode 100644 index 0000000000000..6e2bea43a0ebf --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisanalytics-flink/lib/index.ts @@ -0,0 +1,4 @@ +export * from './application'; +export * from './application-code'; +export * from './types'; + diff --git a/packages/@aws-cdk/aws-kinesisanalytics-flink/lib/private/environment-properties.ts b/packages/@aws-cdk/aws-kinesisanalytics-flink/lib/private/environment-properties.ts new file mode 100644 index 0000000000000..d13a0e870e23e --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisanalytics-flink/lib/private/environment-properties.ts @@ -0,0 +1,16 @@ +import * as ka from '@aws-cdk/aws-kinesisanalytics'; +import { PropertyGroups } from '../types'; + +export function environmentProperties(propertyGroups?: PropertyGroups): ka.CfnApplicationV2.EnvironmentPropertiesProperty | undefined { + const entries = Object.entries(propertyGroups ?? {}); + if (entries.length === 0) { + return; + } + + return { + propertyGroups: entries.map(([id, map]) => ({ + propertyGroupId: id, + propertyMap: map, + })), + }; +} diff --git a/packages/@aws-cdk/aws-kinesisanalytics-flink/lib/private/flink-application-configuration.ts b/packages/@aws-cdk/aws-kinesisanalytics-flink/lib/private/flink-application-configuration.ts new file mode 100644 index 0000000000000..c0a93edac20d6 --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisanalytics-flink/lib/private/flink-application-configuration.ts @@ -0,0 +1,78 @@ +import * as core from '@aws-cdk/core'; +import { LogLevel, MetricsLevel } from '../types'; + +interface FlinkApplicationConfiguration extends + CheckpointConfiguration, + MonitoringConfiguration, + ParallelismConfiguration {} + +interface CheckpointConfiguration { + checkpointingEnabled?: boolean; + checkpointInterval?: core.Duration; + minPauseBetweenCheckpoints?: core.Duration; +} + +interface MonitoringConfiguration { + logLevel?: LogLevel; + metricsLevel?: MetricsLevel; +} + +interface ParallelismConfiguration { + autoScalingEnabled?: boolean; + parallelism?: number; + parallelismPerKpu?: number; +} + +/** + * Build the nested Cfn FlinkApplicationConfiguration object. This function + * doesn't return empty config objects, returning the minimal config needed to + * express the supplied properties. + * + * This function also handles the quirky configType: 'CUSTOM' setting required + * whenever config in one of the nested groupings. + */ +export function flinkApplicationConfiguration(config: FlinkApplicationConfiguration) { + const checkpointConfiguration = configFor({ + checkpointingEnabled: config.checkpointingEnabled, + checkpointInterval: config.checkpointInterval?.toMilliseconds(), + minPauseBetweenCheckpoints: config.minPauseBetweenCheckpoints?.toMilliseconds(), + }); + + const monitoringConfiguration = configFor({ + logLevel: config.logLevel, + metricsLevel: config.metricsLevel, + }); + + const parallelismConfiguration = configFor({ + autoScalingEnabled: config.autoScalingEnabled, + parallelism: config.parallelism, + parallelismPerKpu: config.parallelismPerKpu, + }); + + const applicationConfiguration = { + checkpointConfiguration, + monitoringConfiguration, + parallelismConfiguration, + }; + + if (isEmptyObj(applicationConfiguration)) { + return; + } + + return applicationConfiguration; +} + +function configFor(config: {[key: string]: unknown}) { + if (isEmptyObj(config)) { + return; + } + + return { + ...config, + configurationType: 'CUSTOM', + }; +} + +function isEmptyObj(obj: {[key: string]: unknown}) { + return Object.values(obj).every(v => v === undefined); +} diff --git a/packages/@aws-cdk/aws-kinesisanalytics-flink/lib/private/validation.ts b/packages/@aws-cdk/aws-kinesisanalytics-flink/lib/private/validation.ts new file mode 100644 index 0000000000000..b0f94f56daf77 --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisanalytics-flink/lib/private/validation.ts @@ -0,0 +1,54 @@ +import * as core from '@aws-cdk/core'; + +interface ValidatedProps { + applicationName?: string; + parallelism?: number; + parallelismPerKpu?: number; +} + +/** + * Early validation for the props used to create FlinkApplications. + */ +export function validateFlinkApplicationProps(props: ValidatedProps) { + validateApplicationName(props.applicationName); + validateParallelism(props.parallelism); + validateParallelismPerKpu(props.parallelismPerKpu); +} + +function validateApplicationName(applicationName?: string) { + if (applicationName === undefined || core.Token.isUnresolved(applicationName)) { + return; + } + + if (applicationName.length === 0) { + throw new Error('applicationName cannot be empty. It must contain at least 1 character.'); + } + + if (!/^[a-zA-Z0-9_.-]+$/.test(applicationName)) { + throw new Error(`applicationName may only contain letters, numbers, underscores, hyphens, and periods. Name: ${applicationName}`); + } + + if (applicationName.length > 128) { + throw new Error(`applicationName max length is 128. Name: ${applicationName} is ${applicationName.length} characters.`); + } +} + +function validateParallelism(parallelism?: number) { + if (parallelism === undefined || core.Token.isUnresolved(parallelism)) { + return; + } + + if (parallelism < 1) { + throw new Error('parallelism must be at least 1'); + } +} + +function validateParallelismPerKpu(parallelismPerKpu?: number) { + if (parallelismPerKpu === undefined || core.Token.isUnresolved(parallelismPerKpu)) { + return; + } + + if (parallelismPerKpu < 1) { + throw new Error('parallelismPerKpu must be at least 1'); + } +} diff --git a/packages/@aws-cdk/aws-kinesisanalytics-flink/lib/types.ts b/packages/@aws-cdk/aws-kinesisanalytics-flink/lib/types.ts new file mode 100644 index 0000000000000..b92d55cb48518 --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisanalytics-flink/lib/types.ts @@ -0,0 +1,67 @@ +/** + * Available log levels for Flink applications. + */ +export enum LogLevel { + /** Debug level logging */ + DEBUG = 'DEBUG', + + /** Info level logging */ + INFO = 'INFO', + + /** Warn level logging */ + WARN = 'WARN', + + /** Error level logging */ + ERROR = 'ERROR', +} + +/** + * Granularity of metrics sent to CloudWatch. + */ +export enum MetricsLevel { + /** Application sends the least metrics to CloudWatch */ + APPLICATION = 'APPLICATION', + + /** Task includes task-level metrics sent to CloudWatch */ + TASK = 'TASK', + + /** Operator includes task-level and operator-level metrics sent to CloudWatch */ + OPERATOR = 'OPERATOR', + + /** Send all metrics including metrics per task thread */ + PARALLELISM = 'PARALLELISM', +} + +/** + * Interface for building AWS::KinesisAnalyticsV2::Application PropertyGroup + * configuration. + */ +export interface PropertyGroups { + readonly [propertyId: string]: {[mapKey: string]: string}; +} + +/** + * Available Flink runtimes for Kinesis Analytics. + */ +export class Runtime { + /** Flink Version 1.6 */ + public static readonly FLINK_1_6 = Runtime.of('FLINK-1_6'); + + /** Flink Version 1.8 */ + public static readonly FLINK_1_8 = Runtime.of('FLINK-1_8'); + + /** Flink Version 1.11 */ + public static readonly FLINK_1_11 = Runtime.of('FLINK-1_11'); + + /** Create a new Runtime with with an arbitrary Flink version string */ + public static of(value: string) { + return new Runtime(value); + } + + /** The Cfn string that represents a version of Flink */ + public readonly value: string; + + private constructor(value: string) { + this.value = value; + } +} diff --git a/packages/@aws-cdk/aws-kinesisanalytics-flink/package.json b/packages/@aws-cdk/aws-kinesisanalytics-flink/package.json new file mode 100644 index 0000000000000..2d28b8fd4df7e --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisanalytics-flink/package.json @@ -0,0 +1,118 @@ +{ + "name": "@aws-cdk/aws-kinesisanalytics-flink", + "version": "0.0.0", + "description": "A CDK Construct Library for Kinesis Analytics Flink applications", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "jsii": { + "outdir": "dist", + "targets": { + "java": { + "package": "software.amazon.awscdk.services.kinesis.analytics.flink", + "maven": { + "groupId": "software.amazon.awscdk", + "artifactId": "kinesisanalytics-flink" + } + }, + "dotnet": { + "namespace": "Amazon.CDK.AWS.KinesisAnalyticsFlink", + "packageId": "Amazon.CDK.AWS.KinesisAnalyticsFlink", + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/master/logo/default-256-dark.png" + }, + "python": { + "distName": "aws-cdk.aws-kinesisanalytics-flink", + "module": "aws_cdk.aws_kinesisanalytics_flink", + "classifiers": [ + "Framework :: AWS CDK", + "Framework :: AWS CDK :: 1" + ] + } + }, + "projectReferences": true + }, + "repository": { + "type": "git", + "url": "https://github.com/aws/aws-cdk.git", + "directory": "packages/@aws-cdk/aws-kinesisanalytics-flink" + }, + "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", + "build+test": "yarn build && yarn test", + "build+test+package": "yarn build+test && yarn package", + "compat": "cdk-compat", + "rosetta:extract": "yarn --silent jsii-rosetta extract" + }, + "keywords": [ + "aws", + "cdk", + "kinesis", + "analytics", + "kinesisanalytcs", + "flink" + ], + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com", + "organization": true + }, + "license": "Apache-2.0", + "devDependencies": { + "@aws-cdk/assert": "0.0.0", + "cdk-build-tools": "0.0.0", + "cdk-integ-tools": "0.0.0", + "jest": "^26.6.3", + "pkglint": "0.0.0" + }, + "dependencies": { + "@aws-cdk/assets": "0.0.0", + "@aws-cdk/core": "0.0.0", + "@aws-cdk/aws-iam": "0.0.0", + "@aws-cdk/aws-kinesisanalytics": "0.0.0", + "@aws-cdk/aws-kms": "0.0.0", + "@aws-cdk/aws-logs": "0.0.0", + "@aws-cdk/aws-s3": "0.0.0", + "@aws-cdk/aws-s3-assets": "0.0.0", + "constructs": "^3.2.0" + }, + "homepage": "https://github.com/aws/aws-cdk", + "peerDependencies": { + "@aws-cdk/assets": "0.0.0", + "@aws-cdk/core": "0.0.0", + "@aws-cdk/aws-iam": "0.0.0", + "@aws-cdk/aws-kinesisanalytics": "0.0.0", + "@aws-cdk/aws-kms": "0.0.0", + "@aws-cdk/aws-logs": "0.0.0", + "@aws-cdk/aws-s3": "0.0.0", + "@aws-cdk/aws-s3-assets": "0.0.0", + "constructs": "^3.2.0" + }, + "engines": { + "node": ">= 10.13.0 <13 || >=13.7.0" + }, + "awslint": { + "exclude": [ + "props-physical-name:@aws-cdk/aws-kinesisanalytics-flink.ApplicationProps" + ] + }, + "stability": "experimental", + "maturity": "experimental", + "awscdkio": { + "announce": false + }, + "cdk-build": { + "jest": true, + "env": { + "AWSLINT_BASE_CONSTRUCT": true + } + }, + "publishConfig": { + "tag": "latest" + } +} diff --git a/packages/@aws-cdk/aws-kinesisanalytics-flink/test/application.test.ts b/packages/@aws-cdk/aws-kinesisanalytics-flink/test/application.test.ts new file mode 100644 index 0000000000000..6dc35fadd44b6 --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisanalytics-flink/test/application.test.ts @@ -0,0 +1,604 @@ +import { arrayWith, objectLike, ResourcePart } from '@aws-cdk/assert'; +import '@aws-cdk/assert/jest'; +import * as iam from '@aws-cdk/aws-iam'; +import * as logs from '@aws-cdk/aws-logs'; +import * as s3 from '@aws-cdk/aws-s3'; +import * as core from '@aws-cdk/core'; +import * as path from 'path'; +import * as flink from '../lib'; + +describe('Application', () => { + let stack: core.Stack; + let bucket: s3.Bucket; + let requiredProps: { + runtime: flink.Runtime; + code: flink.ApplicationCode; + }; + + beforeEach(() => { + stack = new core.Stack(); + bucket = new s3.Bucket(stack, 'CodeBucket'); + requiredProps = { + runtime: flink.Runtime.FLINK_1_11, + code: flink.ApplicationCode.fromBucket(bucket, 'my-app.jar'), + }; + }); + + test('default Flink Application', () => { + new flink.Application(stack, 'FlinkApplication', { + runtime: flink.Runtime.FLINK_1_11, + code: flink.ApplicationCode.fromBucket(bucket, 'my-app.jar'), + }); + + expect(stack).toHaveResource('AWS::KinesisAnalyticsV2::Application', { + RuntimeEnvironment: 'FLINK-1_11', + ServiceExecutionRole: { + 'Fn::GetAtt': [ + 'FlinkApplicationRole2F7BCBF6', + 'Arn', + ], + }, + ApplicationConfiguration: { + ApplicationCodeConfiguration: { + CodeContent: { + S3ContentLocation: { + BucketARN: stack.resolve(bucket.bucketArn), + FileKey: 'my-app.jar', + }, + }, + CodeContentType: 'ZIPFILE', + }, + ApplicationSnapshotConfiguration: { + SnapshotsEnabled: true, + }, + }, + }); + + expect(stack).toHaveResourceLike('AWS::KinesisAnalyticsV2::Application', { + DeletionPolicy: 'Delete', + }, ResourcePart.CompleteDefinition); + + expect(stack).toHaveResource('AWS::IAM::Role', { + AssumeRolePolicyDocument: { + Statement: [{ + Action: 'sts:AssumeRole', + Effect: 'Allow', + Principal: { + Service: 'kinesisanalytics.amazonaws.com', + }, + }], + Version: '2012-10-17', + }, + }); + + expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + PolicyDocument: { + Statement: arrayWith( + { Action: 'cloudwatch:PutMetricData', Effect: 'Allow', Resource: '*' }, + { + Action: 'logs:DescribeLogGroups', + Effect: 'Allow', + Resource: { + // looks like arn:aws:logs:us-east-1:123456789012:log-group:*, + 'Fn::Join': ['', [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':logs:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':log-group:*', + ]], + }, + }, + { + Action: 'logs:DescribeLogStreams', + Effect: 'Allow', + Resource: { + // looks like: arn:aws:logs:us-east-1:123456789012:log-group:my-log-group:*, + 'Fn::GetAtt': ['FlinkApplicationLogGroup7739479C', 'Arn'], + }, + }, + { + Action: 'logs:PutLogEvents', + Effect: 'Allow', + Resource: { + 'Fn::Join': ['', [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':logs:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':log-group:', + { Ref: 'FlinkApplicationLogGroup7739479C' }, + ':log-stream:', + { Ref: 'FlinkApplicationLogStreamB633AF32' }, + ]], + }, + }, + ), + }, + }); + }); + + test('providing a custom role', () => { + new flink.Application(stack, 'FlinkApplication', { + ...requiredProps, + role: new iam.Role(stack, 'CustomRole', { + assumedBy: new iam.ServicePrincipal('custom-principal'), + }), + }); + + expect(stack).toHaveResource('AWS::IAM::Role', { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: 'sts:AssumeRole', + Effect: 'Allow', + Principal: { + Service: 'custom-principal.amazonaws.com', + }, + }, + ], + Version: '2012-10-17', + }, + }); + }); + + test('addToPrincipalPolicy', () => { + const app = new flink.Application(stack, 'FlinkApplication', { + ...requiredProps, + }); + + app.addToRolePolicy(new iam.PolicyStatement({ + actions: ['custom:action'], + resources: ['*'], + })); + + expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + PolicyDocument: { + Statement: arrayWith( + objectLike({ Action: 'custom:action', Effect: 'Allow', Resource: '*' }), + ), + }, + }); + }); + + test('providing a custom runtime', () => { + new flink.Application(stack, 'FlinkApplication', { + ...requiredProps, + runtime: flink.Runtime.of('custom'), + }); + + expect(stack).toHaveResourceLike('AWS::KinesisAnalyticsV2::Application', { + RuntimeEnvironment: 'custom', + }); + }); + + test('providing a custom removal policy', () => { + new flink.Application(stack, 'FlinkApplication', { + ...requiredProps, + removalPolicy: core.RemovalPolicy.RETAIN, + }); + + expect(stack).toHaveResourceLike('AWS::KinesisAnalyticsV2::Application', { + DeletionPolicy: 'Retain', + }, ResourcePart.CompleteDefinition); + }); + + test('granting permissions to resources', () => { + const app = new flink.Application(stack, 'FlinkApplication', { + ...requiredProps, + }); + + const dataBucket = new s3.Bucket(stack, 'DataBucket'); + dataBucket.grantRead(app); + + expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + PolicyDocument: { + Version: '2012-10-17', + Statement: arrayWith( + objectLike({ Action: ['s3:GetObject*', 's3:GetBucket*', 's3:List*'] }), + ), + }, + }); + }); + + test('using an asset for code', () => { + const code = flink.ApplicationCode.fromAsset(path.join(__dirname, 'code-asset')); + new flink.Application(stack, 'FlinkApplication', { + ...requiredProps, + code, + }); + const assetRef = 'AssetParameters8be9e0b5f53d41e9a3b1d51c9572c65f24f8170a7188d0ed57fb7d571de4d577S3BucketEBA17A67'; + const versionKeyRef = 'AssetParameters8be9e0b5f53d41e9a3b1d51c9572c65f24f8170a7188d0ed57fb7d571de4d577S3VersionKey5922697E'; + + expect(stack).toHaveResourceLike('AWS::KinesisAnalyticsV2::Application', { + ApplicationConfiguration: { + ApplicationCodeConfiguration: { + CodeContent: { + S3ContentLocation: { + BucketARN: { + 'Fn::Join': ['', [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':s3:::', + { Ref: assetRef }, + ]], + }, + FileKey: { + 'Fn::Join': ['', [ + { 'Fn::Select': [0, { 'Fn::Split': ['||', { Ref: versionKeyRef }] }] }, + { 'Fn::Select': [1, { 'Fn::Split': ['||', { Ref: versionKeyRef }] }] }, + ]], + }, + }, + }, + CodeContentType: 'ZIPFILE', + }, + }, + }); + }); + + test('adding property groups', () => { + new flink.Application(stack, 'FlinkApplication', { + ...requiredProps, + propertyGroups: { + FlinkApplicationProperties: { + SomeProperty: 'SomeValue', + }, + }, + }); + + expect(stack).toHaveResourceLike('AWS::KinesisAnalyticsV2::Application', { + ApplicationConfiguration: { + EnvironmentProperties: { + PropertyGroups: [ + { + PropertyGroupId: 'FlinkApplicationProperties', + PropertyMap: { + SomeProperty: 'SomeValue', + }, + }, + ], + }, + }, + }); + }); + + test('checkpointEnabled setting', () => { + new flink.Application(stack, 'FlinkApplication', { + ...requiredProps, + checkpointingEnabled: false, + }); + + expect(stack).toHaveResourceLike('AWS::KinesisAnalyticsV2::Application', { + ApplicationConfiguration: { + FlinkApplicationConfiguration: { + CheckpointConfiguration: { + ConfigurationType: 'CUSTOM', + CheckpointingEnabled: false, + }, + }, + }, + }); + }); + + test('checkpointInterval setting', () => { + new flink.Application(stack, 'FlinkApplication', { + ...requiredProps, + checkpointInterval: core.Duration.minutes(5), + }); + + expect(stack).toHaveResourceLike('AWS::KinesisAnalyticsV2::Application', { + ApplicationConfiguration: { + FlinkApplicationConfiguration: { + CheckpointConfiguration: { + ConfigurationType: 'CUSTOM', + CheckpointInterval: 300_000, + }, + }, + }, + }); + }); + + test('minPauseBetweenCheckpoints setting', () => { + new flink.Application(stack, 'FlinkApplication', { + ...requiredProps, + minPauseBetweenCheckpoints: core.Duration.seconds(10), + }); + + expect(stack).toHaveResourceLike('AWS::KinesisAnalyticsV2::Application', { + ApplicationConfiguration: { + FlinkApplicationConfiguration: { + CheckpointConfiguration: { + ConfigurationType: 'CUSTOM', + MinPauseBetweenCheckpoints: 10_000, + }, + }, + }, + }); + }); + + test('logLevel setting', () => { + new flink.Application(stack, 'FlinkApplication', { + ...requiredProps, + logLevel: flink.LogLevel.DEBUG, + }); + + expect(stack).toHaveResourceLike('AWS::KinesisAnalyticsV2::Application', { + ApplicationConfiguration: { + FlinkApplicationConfiguration: { + MonitoringConfiguration: { + ConfigurationType: 'CUSTOM', + LogLevel: 'DEBUG', + }, + }, + }, + }); + }); + + test('metricsLevel setting', () => { + new flink.Application(stack, 'FlinkApplication', { + ...requiredProps, + metricsLevel: flink.MetricsLevel.PARALLELISM, + }); + + expect(stack).toHaveResourceLike('AWS::KinesisAnalyticsV2::Application', { + ApplicationConfiguration: { + FlinkApplicationConfiguration: { + MonitoringConfiguration: { + ConfigurationType: 'CUSTOM', + MetricsLevel: 'PARALLELISM', + }, + }, + }, + }); + }); + + test('autoscalingEnabled setting', () => { + new flink.Application(stack, 'FlinkApplication', { + ...requiredProps, + autoScalingEnabled: false, + }); + + expect(stack).toHaveResourceLike('AWS::KinesisAnalyticsV2::Application', { + ApplicationConfiguration: { + FlinkApplicationConfiguration: { + ParallelismConfiguration: { + ConfigurationType: 'CUSTOM', + AutoScalingEnabled: false, + }, + }, + }, + }); + }); + + test('parallelism setting', () => { + new flink.Application(stack, 'FlinkApplication', { + ...requiredProps, + parallelism: 2, + }); + + expect(stack).toHaveResourceLike('AWS::KinesisAnalyticsV2::Application', { + ApplicationConfiguration: { + FlinkApplicationConfiguration: { + ParallelismConfiguration: { + ConfigurationType: 'CUSTOM', + Parallelism: 2, + }, + }, + }, + }); + }); + + test('parallelismPerKpu setting', () => { + new flink.Application(stack, 'FlinkApplication', { + ...requiredProps, + parallelismPerKpu: 2, + }); + + expect(stack).toHaveResourceLike('AWS::KinesisAnalyticsV2::Application', { + ApplicationConfiguration: { + FlinkApplicationConfiguration: { + ParallelismConfiguration: { + ConfigurationType: 'CUSTOM', + ParallelismPerKPU: 2, + }, + }, + }, + }); + }); + + test('snapshotsEnabled setting', () => { + new flink.Application(stack, 'FlinkApplication', { + ...requiredProps, + snapshotsEnabled: false, + }); + + expect(stack).toHaveResourceLike('AWS::KinesisAnalyticsV2::Application', { + ApplicationConfiguration: { + ApplicationSnapshotConfiguration: { + SnapshotsEnabled: false, + }, + }, + }); + }); + + test('default logging option', () => { + new flink.Application(stack, 'FlinkApplication', { + ...requiredProps, + snapshotsEnabled: false, + }); + + expect(stack).toHaveResource('AWS::KinesisAnalyticsV2::ApplicationCloudWatchLoggingOption', { + ApplicationName: { + Ref: 'FlinkApplicationC5836815', + }, + CloudWatchLoggingOption: { + LogStreamARN: { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':logs:', + { + Ref: 'AWS::Region', + }, + ':', + { + Ref: 'AWS::AccountId', + }, + ':log-group:', + { + Ref: 'FlinkApplicationLogGroup7739479C', + }, + ':log-stream:', + { + Ref: 'FlinkApplicationLogStreamB633AF32', + }, + ], + ], + }, + }, + }); + + expect(stack).toHaveResource('AWS::Logs::LogGroup', { + Properties: { + RetentionInDays: 731, + }, + UpdateReplacePolicy: 'Retain', + DeletionPolicy: 'Retain', + }, ResourcePart.CompleteDefinition); + + expect(stack).toHaveResource('AWS::Logs::LogStream', { + UpdateReplacePolicy: 'Retain', + DeletionPolicy: 'Retain', + }, ResourcePart.CompleteDefinition); + }); + + test('logGroup setting', () => { + new flink.Application(stack, 'FlinkApplication', { + ...requiredProps, + logGroup: new logs.LogGroup(stack, 'LogGroup', { + logGroupName: 'custom', + }), + }); + + expect(stack).toHaveResource('AWS::Logs::LogGroup', { + LogGroupName: 'custom', + }); + }); + + test('validating applicationName', () => { + // Expect no error with valid name + new flink.Application(stack, 'ValidString', { + ...requiredProps, + applicationName: 'my-VALID.app_name', + }); + + // Expect no error with ref + new flink.Application(stack, 'ValidRef', { + ...requiredProps, + applicationName: new core.CfnParameter(stack, 'Parameter').valueAsString, + }); + + expect(() => { + new flink.Application(stack, 'Empty', { + ...requiredProps, + applicationName: '', + }); + }).toThrow(/cannot be empty/); + + expect(() => { + new flink.Application(stack, 'InvalidCharacters', { + ...requiredProps, + applicationName: '!!!', + }); + }).toThrow(/may only contain letters, numbers, underscores, hyphens, and periods/); + + expect(() => { + new flink.Application(stack, 'TooLong', { + ...requiredProps, + applicationName: 'a'.repeat(129), + }); + }).toThrow(/max length is 128/); + }); + + test('validating parallelism', () => { + // Expect no error with valid value + new flink.Application(stack, 'ValidNumber', { + ...requiredProps, + parallelism: 32, + }); + + // Expect no error with ref + new flink.Application(stack, 'ValidRef', { + ...requiredProps, + parallelism: new core.CfnParameter(stack, 'Parameter', { + type: 'Number', + }).valueAsNumber, + }); + + expect(() => { + new flink.Application(stack, 'TooSmall', { + ...requiredProps, + parallelism: 0, + }); + }).toThrow(/must be at least 1/); + }); + + test('validating parallelismPerKpu', () => { + // Expect no error with valid value + new flink.Application(stack, 'ValidNumber', { + ...requiredProps, + parallelismPerKpu: 10, + }); + + // Expect no error with ref + new flink.Application(stack, 'ValidRef', { + ...requiredProps, + parallelismPerKpu: new core.CfnParameter(stack, 'Parameter', { + type: 'Number', + }).valueAsNumber, + }); + + expect(() => { + new flink.Application(stack, 'TooSmall', { + ...requiredProps, + parallelismPerKpu: 0, + }); + }).toThrow(/must be at least 1/); + }); + + test('fromFlinkApplicationName', () => { + const flinkApp = flink.Application.fromApplicationName(stack, 'Imported', 'my-app'); + + expect(flinkApp.applicationName).toEqual('my-app'); + expect(stack.resolve(flinkApp.applicationArn)).toEqual({ + 'Fn::Join': ['', [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':kinesisanalytics:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':application/my-app', + ]], + }); + expect(flinkApp.addToRolePolicy(new iam.PolicyStatement())).toBe(false); + }); + + test('fromFlinkApplicationArn', () => { + const arn = 'arn:aws:kinesisanalytics:us-west-2:012345678901:application/my-app'; + const flinkApp = flink.Application.fromApplicationArn(stack, 'Imported', arn); + + expect(flinkApp.applicationName).toEqual('my-app'); + expect(flinkApp.applicationArn).toEqual(arn); + expect(flinkApp.addToRolePolicy(new iam.PolicyStatement())).toBe(false); + }); +}); diff --git a/packages/@aws-cdk/aws-kinesisanalytics-flink/test/code-asset/WordCount.jar b/packages/@aws-cdk/aws-kinesisanalytics-flink/test/code-asset/WordCount.jar new file mode 100644 index 0000000000000..9c533e6fea607 Binary files /dev/null and b/packages/@aws-cdk/aws-kinesisanalytics-flink/test/code-asset/WordCount.jar differ diff --git a/packages/@aws-cdk/aws-kinesisanalytics-flink/test/integ.application-code-from-bucket.lit.expected.json b/packages/@aws-cdk/aws-kinesisanalytics-flink/test/integ.application-code-from-bucket.lit.expected.json new file mode 100644 index 0000000000000..6ab6a5f40bcab --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisanalytics-flink/test/integ.application-code-from-bucket.lit.expected.json @@ -0,0 +1,295 @@ +{ + "Parameters": { + "AssetParameters8be9e0b5f53d41e9a3b1d51c9572c65f24f8170a7188d0ed57fb7d571de4d577S3BucketEBA17A67": { + "Type": "String", + "Description": "S3 bucket for asset \"8be9e0b5f53d41e9a3b1d51c9572c65f24f8170a7188d0ed57fb7d571de4d577\"" + }, + "AssetParameters8be9e0b5f53d41e9a3b1d51c9572c65f24f8170a7188d0ed57fb7d571de4d577S3VersionKey5922697E": { + "Type": "String", + "Description": "S3 key for asset version \"8be9e0b5f53d41e9a3b1d51c9572c65f24f8170a7188d0ed57fb7d571de4d577\"" + }, + "AssetParameters8be9e0b5f53d41e9a3b1d51c9572c65f24f8170a7188d0ed57fb7d571de4d577ArtifactHash211A4F2F": { + "Type": "String", + "Description": "Artifact hash for asset \"8be9e0b5f53d41e9a3b1d51c9572c65f24f8170a7188d0ed57fb7d571de4d577\"" + } + }, + "Resources": { + "AppRole1AF9B530": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "kinesisanalytics.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "AppRoleDefaultPolicy9CADBAA1": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "cloudwatch:PutMetricData", + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::", + { + "Ref": "AssetParameters8be9e0b5f53d41e9a3b1d51c9572c65f24f8170a7188d0ed57fb7d571de4d577S3BucketEBA17A67" + } + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::", + { + "Ref": "AssetParameters8be9e0b5f53d41e9a3b1d51c9572c65f24f8170a7188d0ed57fb7d571de4d577S3BucketEBA17A67" + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "logs:DescribeLogGroups", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:*" + ] + ] + } + }, + { + "Action": "logs:DescribeLogStreams", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "AppLogGroupC72EEC8C", + "Arn" + ] + } + }, + { + "Action": "logs:PutLogEvents", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:", + { + "Ref": "AppLogGroupC72EEC8C" + }, + ":log-stream:", + { + "Ref": "AppLogStream3CAF66A7" + } + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "AppRoleDefaultPolicy9CADBAA1", + "Roles": [ + { + "Ref": "AppRole1AF9B530" + } + ] + } + }, + "AppF1B96344": { + "Type": "AWS::KinesisAnalyticsV2::Application", + "Properties": { + "RuntimeEnvironment": "FLINK-1_11", + "ServiceExecutionRole": { + "Fn::GetAtt": [ + "AppRole1AF9B530", + "Arn" + ] + }, + "ApplicationConfiguration": { + "ApplicationCodeConfiguration": { + "CodeContent": { + "S3ContentLocation": { + "BucketARN": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::", + { + "Ref": "AssetParameters8be9e0b5f53d41e9a3b1d51c9572c65f24f8170a7188d0ed57fb7d571de4d577S3BucketEBA17A67" + } + ] + ] + }, + "FileKey": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters8be9e0b5f53d41e9a3b1d51c9572c65f24f8170a7188d0ed57fb7d571de4d577S3VersionKey5922697E" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters8be9e0b5f53d41e9a3b1d51c9572c65f24f8170a7188d0ed57fb7d571de4d577S3VersionKey5922697E" + } + ] + } + ] + } + ] + ] + } + } + }, + "CodeContentType": "ZIPFILE" + }, + "ApplicationSnapshotConfiguration": { + "SnapshotsEnabled": true + } + } + }, + "DependsOn": [ + "AppRoleDefaultPolicy9CADBAA1", + "AppRole1AF9B530" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "AppLogGroupC72EEC8C": { + "Type": "AWS::Logs::LogGroup", + "Properties": { + "RetentionInDays": 731 + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "AppLogStream3CAF66A7": { + "Type": "AWS::Logs::LogStream", + "Properties": { + "LogGroupName": { + "Ref": "AppLogGroupC72EEC8C" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "AppLoggingOption75BE995E": { + "Type": "AWS::KinesisAnalyticsV2::ApplicationCloudWatchLoggingOption", + "Properties": { + "ApplicationName": { + "Ref": "AppF1B96344" + }, + "CloudWatchLoggingOption": { + "LogStreamARN": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:", + { + "Ref": "AppLogGroupC72EEC8C" + }, + ":log-stream:", + { + "Ref": "AppLogStream3CAF66A7" + } + ] + ] + } + } + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-kinesisanalytics-flink/test/integ.application-code-from-bucket.lit.ts b/packages/@aws-cdk/aws-kinesisanalytics-flink/test/integ.application-code-from-bucket.lit.ts new file mode 100644 index 0000000000000..eb8c5ce3e9d03 --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisanalytics-flink/test/integ.application-code-from-bucket.lit.ts @@ -0,0 +1,22 @@ +import * as path from 'path'; +import * as assets from '@aws-cdk/aws-s3-assets'; +import * as core from '@aws-cdk/core'; +import * as flink from '../lib'; + +const app = new core.App(); +const stack = new core.Stack(app, 'FlinkAppCodeFromBucketTest'); + +const asset = new assets.Asset(stack, 'CodeAsset', { + path: path.join(__dirname, 'code-asset'), +}); +const bucket = asset.bucket; +const fileKey = asset.s3ObjectKey; + +///! show +new flink.Application(stack, 'App', { + code: flink.ApplicationCode.fromBucket(bucket, fileKey), + runtime: flink.Runtime.FLINK_1_11, +}); +///! hide + +app.synth(); diff --git a/packages/@aws-cdk/aws-kinesisanalytics-flink/test/integ.application.lit.expected.json b/packages/@aws-cdk/aws-kinesisanalytics-flink/test/integ.application.lit.expected.json new file mode 100644 index 0000000000000..3b4f7ecf64f7e --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisanalytics-flink/test/integ.application.lit.expected.json @@ -0,0 +1,295 @@ +{ + "Resources": { + "AppRole1AF9B530": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "kinesisanalytics.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "AppRoleDefaultPolicy9CADBAA1": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "cloudwatch:PutMetricData", + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::", + { + "Ref": "AssetParameters8be9e0b5f53d41e9a3b1d51c9572c65f24f8170a7188d0ed57fb7d571de4d577S3BucketEBA17A67" + } + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::", + { + "Ref": "AssetParameters8be9e0b5f53d41e9a3b1d51c9572c65f24f8170a7188d0ed57fb7d571de4d577S3BucketEBA17A67" + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "logs:DescribeLogGroups", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:*" + ] + ] + } + }, + { + "Action": "logs:DescribeLogStreams", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "AppLogGroupC72EEC8C", + "Arn" + ] + } + }, + { + "Action": "logs:PutLogEvents", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:", + { + "Ref": "AppLogGroupC72EEC8C" + }, + ":log-stream:", + { + "Ref": "AppLogStream3CAF66A7" + } + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "AppRoleDefaultPolicy9CADBAA1", + "Roles": [ + { + "Ref": "AppRole1AF9B530" + } + ] + } + }, + "AppF1B96344": { + "Type": "AWS::KinesisAnalyticsV2::Application", + "Properties": { + "RuntimeEnvironment": "FLINK-1_11", + "ServiceExecutionRole": { + "Fn::GetAtt": [ + "AppRole1AF9B530", + "Arn" + ] + }, + "ApplicationConfiguration": { + "ApplicationCodeConfiguration": { + "CodeContent": { + "S3ContentLocation": { + "BucketARN": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::", + { + "Ref": "AssetParameters8be9e0b5f53d41e9a3b1d51c9572c65f24f8170a7188d0ed57fb7d571de4d577S3BucketEBA17A67" + } + ] + ] + }, + "FileKey": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters8be9e0b5f53d41e9a3b1d51c9572c65f24f8170a7188d0ed57fb7d571de4d577S3VersionKey5922697E" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters8be9e0b5f53d41e9a3b1d51c9572c65f24f8170a7188d0ed57fb7d571de4d577S3VersionKey5922697E" + } + ] + } + ] + } + ] + ] + } + } + }, + "CodeContentType": "ZIPFILE" + }, + "ApplicationSnapshotConfiguration": { + "SnapshotsEnabled": true + } + } + }, + "DependsOn": [ + "AppRoleDefaultPolicy9CADBAA1", + "AppRole1AF9B530" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "AppLogGroupC72EEC8C": { + "Type": "AWS::Logs::LogGroup", + "Properties": { + "RetentionInDays": 731 + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "AppLogStream3CAF66A7": { + "Type": "AWS::Logs::LogStream", + "Properties": { + "LogGroupName": { + "Ref": "AppLogGroupC72EEC8C" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "AppLoggingOption75BE995E": { + "Type": "AWS::KinesisAnalyticsV2::ApplicationCloudWatchLoggingOption", + "Properties": { + "ApplicationName": { + "Ref": "AppF1B96344" + }, + "CloudWatchLoggingOption": { + "LogStreamARN": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:", + { + "Ref": "AppLogGroupC72EEC8C" + }, + ":log-stream:", + { + "Ref": "AppLogStream3CAF66A7" + } + ] + ] + } + } + } + } + }, + "Parameters": { + "AssetParameters8be9e0b5f53d41e9a3b1d51c9572c65f24f8170a7188d0ed57fb7d571de4d577S3BucketEBA17A67": { + "Type": "String", + "Description": "S3 bucket for asset \"8be9e0b5f53d41e9a3b1d51c9572c65f24f8170a7188d0ed57fb7d571de4d577\"" + }, + "AssetParameters8be9e0b5f53d41e9a3b1d51c9572c65f24f8170a7188d0ed57fb7d571de4d577S3VersionKey5922697E": { + "Type": "String", + "Description": "S3 key for asset version \"8be9e0b5f53d41e9a3b1d51c9572c65f24f8170a7188d0ed57fb7d571de4d577\"" + }, + "AssetParameters8be9e0b5f53d41e9a3b1d51c9572c65f24f8170a7188d0ed57fb7d571de4d577ArtifactHash211A4F2F": { + "Type": "String", + "Description": "Artifact hash for asset \"8be9e0b5f53d41e9a3b1d51c9572c65f24f8170a7188d0ed57fb7d571de4d577\"" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-kinesisanalytics-flink/test/integ.application.lit.ts b/packages/@aws-cdk/aws-kinesisanalytics-flink/test/integ.application.lit.ts new file mode 100644 index 0000000000000..02a6a9949dcfa --- /dev/null +++ b/packages/@aws-cdk/aws-kinesisanalytics-flink/test/integ.application.lit.ts @@ -0,0 +1,15 @@ +///! show +import * as path from 'path'; +import * as core from '@aws-cdk/core'; +import * as flink from '../lib'; + +const app = new core.App(); +const stack = new core.Stack(app, 'FlinkAppTest'); + +new flink.Application(stack, 'App', { + code: flink.ApplicationCode.fromAsset(path.join(__dirname, 'code-asset')), + runtime: flink.Runtime.FLINK_1_11, +}); +///! hide + +app.synth(); diff --git a/packages/@aws-cdk/aws-kinesisanalytics/README.md b/packages/@aws-cdk/aws-kinesisanalytics/README.md index e65b812cefc97..33dfe482719b0 100644 --- a/packages/@aws-cdk/aws-kinesisanalytics/README.md +++ b/packages/@aws-cdk/aws-kinesisanalytics/README.md @@ -14,3 +14,10 @@ This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project. + +## Kinesis Analytics Flink + +The `aws-kinesisanalytics-flink` package provides constructs for building Flink applications. + + * [Github](https://github.com/aws/aws-cdk/tree/master/packages/%40aws-cdk/aws-kinesisanalytics-flink) + * [CDK Docs](https://docs.aws.amazon.com/cdk/api/latest/docs/aws-kinesisanalytics-flink.html) diff --git a/packages/@aws-cdk/aws-kinesisanalytics/package.json b/packages/@aws-cdk/aws-kinesisanalytics/package.json index bbd35a2c47cd2..c6b67502056e0 100644 --- a/packages/@aws-cdk/aws-kinesisanalytics/package.json +++ b/packages/@aws-cdk/aws-kinesisanalytics/package.json @@ -95,5 +95,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-kinesisfirehose/package.json b/packages/@aws-cdk/aws-kinesisfirehose/package.json index b4e0dd9e8487f..ae2d309ed8e24 100644 --- a/packages/@aws-cdk/aws-kinesisfirehose/package.json +++ b/packages/@aws-cdk/aws-kinesisfirehose/package.json @@ -92,5 +92,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-kms/package.json b/packages/@aws-cdk/aws-kms/package.json index d4b3f10a58e3f..272c610530357 100644 --- a/packages/@aws-cdk/aws-kms/package.json +++ b/packages/@aws-cdk/aws-kms/package.json @@ -102,5 +102,8 @@ "awscdkio": { "announce": false }, - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-kms/test/integ.key-sharing.lit.expected.json b/packages/@aws-cdk/aws-kms/test/integ.key-sharing.lit.expected.json index 1a2290cc9bce4..24704085b587e 100644 --- a/packages/@aws-cdk/aws-kms/test/integ.key-sharing.lit.expected.json +++ b/packages/@aws-cdk/aws-kms/test/integ.key-sharing.lit.expected.json @@ -7,23 +7,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { diff --git a/packages/@aws-cdk/aws-kms/test/integ.key.expected.json b/packages/@aws-cdk/aws-kms/test/integ.key.expected.json index a11ff1abc7e94..6ed1da4638a2f 100644 --- a/packages/@aws-cdk/aws-kms/test/integ.key.expected.json +++ b/packages/@aws-cdk/aws-kms/test/integ.key.expected.json @@ -6,23 +6,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { diff --git a/packages/@aws-cdk/aws-lakeformation/package.json b/packages/@aws-cdk/aws-lakeformation/package.json index d2dfab5e2df22..8c3f72401111d 100644 --- a/packages/@aws-cdk/aws-lakeformation/package.json +++ b/packages/@aws-cdk/aws-lakeformation/package.json @@ -93,5 +93,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-lambda-destinations/package.json b/packages/@aws-cdk/aws-lambda-destinations/package.json index 6f46b2a23fd6c..096ee2f6fc12f 100644 --- a/packages/@aws-cdk/aws-lambda-destinations/package.json +++ b/packages/@aws-cdk/aws-lambda-destinations/package.json @@ -104,5 +104,8 @@ "env": { "AWSLINT_BASE_CONSTRUCT": true } + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-lambda-event-sources/package.json b/packages/@aws-cdk/aws-lambda-event-sources/package.json index 6d5a36395f693..20112e1029cbf 100644 --- a/packages/@aws-cdk/aws-lambda-event-sources/package.json +++ b/packages/@aws-cdk/aws-lambda-event-sources/package.json @@ -126,5 +126,8 @@ "env": { "AWSLINT_BASE_CONSTRUCT": true } + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-lambda-event-sources/test/integ.s3.expected.json b/packages/@aws-cdk/aws-lambda-event-sources/test/integ.s3.expected.json index afac04a03e5fb..53b599aa22c5a 100644 --- a/packages/@aws-cdk/aws-lambda-event-sources/test/integ.s3.expected.json +++ b/packages/@aws-cdk/aws-lambda-event-sources/test/integ.s3.expected.json @@ -176,7 +176,7 @@ "Properties": { "Description": "AWS CloudFormation handler for \"Custom::S3BucketNotifications\" resources (@aws-cdk/aws-s3)", "Code": { - "ZipFile": "exports.handler = (event, context) => {\n // eslint-disable-next-line @typescript-eslint/no-require-imports, import/no-extraneous-dependencies\n const s3 = new (require('aws-sdk').S3)();\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const https = require('https');\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const url = require('url');\n log(JSON.stringify(event, undefined, 2));\n const props = event.ResourceProperties;\n if (event.RequestType === 'Delete') {\n props.NotificationConfiguration = {}; // this is how you clean out notifications\n }\n const req = {\n Bucket: props.BucketName,\n NotificationConfiguration: props.NotificationConfiguration,\n };\n return s3.putBucketNotificationConfiguration(req, (err, data) => {\n log({ err, data });\n if (err) {\n return submitResponse('FAILED', err.message + `\\nMore information in CloudWatch Log Stream: ${context.logStreamName}`);\n }\n else {\n return submitResponse('SUCCESS');\n }\n });\n function log(obj) {\n console.error(event.RequestId, event.StackId, event.LogicalResourceId, obj);\n }\n // eslint-disable-next-line max-len\n // adapted from https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html#cfn-lambda-function-code-cfnresponsemodule\n // to allow sending an error messge as a reason.\n function submitResponse(responseStatus, reason) {\n const responseBody = JSON.stringify({\n Status: responseStatus,\n Reason: reason || 'See the details in CloudWatch Log Stream: ' + context.logStreamName,\n PhysicalResourceId: event.PhysicalResourceId || event.LogicalResourceId,\n StackId: event.StackId,\n RequestId: event.RequestId,\n LogicalResourceId: event.LogicalResourceId,\n NoEcho: false,\n });\n log({ responseBody });\n const parsedUrl = url.parse(event.ResponseURL);\n const options = {\n hostname: parsedUrl.hostname,\n port: 443,\n path: parsedUrl.path,\n method: 'PUT',\n headers: {\n 'content-type': '',\n 'content-length': responseBody.length,\n },\n };\n const request = https.request(options, (r) => {\n log({ statusCode: r.statusCode, statusMessage: r.statusMessage });\n context.done();\n });\n request.on('error', (error) => {\n log({ sendError: error });\n context.done();\n });\n request.write(responseBody);\n request.end();\n }\n};" + "ZipFile": "exports.handler = (event, context) => {\n // eslint-disable-next-line @typescript-eslint/no-require-imports, import/no-extraneous-dependencies\n const s3 = new (require('aws-sdk').S3)();\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const https = require('https');\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const url = require('url');\n log(JSON.stringify(event, undefined, 2));\n const props = event.ResourceProperties;\n if (event.RequestType === 'Delete') {\n props.NotificationConfiguration = {}; // this is how you clean out notifications\n }\n const req = {\n Bucket: props.BucketName,\n NotificationConfiguration: props.NotificationConfiguration,\n };\n return s3.putBucketNotificationConfiguration(req, (err, data) => {\n log({ err, data });\n if (err) {\n return submitResponse('FAILED', err.message + `\\nMore information in CloudWatch Log Stream: ${context.logStreamName}`);\n }\n else {\n return submitResponse('SUCCESS');\n }\n });\n function log(obj) {\n console.error(event.RequestId, event.StackId, event.LogicalResourceId, obj);\n }\n // eslint-disable-next-line max-len\n // adapted from https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html#cfn-lambda-function-code-cfnresponsemodule\n // to allow sending an error message as a reason.\n function submitResponse(responseStatus, reason) {\n const responseBody = JSON.stringify({\n Status: responseStatus,\n Reason: reason || 'See the details in CloudWatch Log Stream: ' + context.logStreamName,\n PhysicalResourceId: event.PhysicalResourceId || event.LogicalResourceId,\n StackId: event.StackId,\n RequestId: event.RequestId,\n LogicalResourceId: event.LogicalResourceId,\n NoEcho: false,\n });\n log({ responseBody });\n const parsedUrl = url.parse(event.ResponseURL);\n const options = {\n hostname: parsedUrl.hostname,\n port: 443,\n path: parsedUrl.path,\n method: 'PUT',\n headers: {\n 'content-type': '',\n 'content-length': responseBody.length,\n },\n };\n const request = https.request(options, (r) => {\n log({ statusCode: r.statusCode, statusMessage: r.statusMessage });\n context.done();\n });\n request.on('error', (error) => {\n log({ sendError: error });\n context.done();\n });\n request.write(responseBody);\n request.end();\n }\n};" }, "Handler": "index.handler", "Role": { diff --git a/packages/@aws-cdk/aws-lambda-nodejs/package.json b/packages/@aws-cdk/aws-lambda-nodejs/package.json index eeb868b60073e..82762f1a3e690 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/package.json +++ b/packages/@aws-cdk/aws-lambda-nodejs/package.json @@ -66,8 +66,8 @@ "@aws-cdk/aws-ec2": "0.0.0", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", - "delay": "4.4.0", - "esbuild": "^0.8.34", + "delay": "5.0.0", + "esbuild": "^0.8.46", "pkglint": "0.0.0" }, "dependencies": { @@ -91,5 +91,8 @@ }, "cdk-build": { "jest": true + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-lambda-python/package.json b/packages/@aws-cdk/aws-lambda-python/package.json index 33b6f04fa903a..f22b2076ee43f 100644 --- a/packages/@aws-cdk/aws-lambda-python/package.json +++ b/packages/@aws-cdk/aws-lambda-python/package.json @@ -90,5 +90,8 @@ }, "cdk-build": { "jest": true + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-lambda/README.md b/packages/@aws-cdk/aws-lambda/README.md index de6940cd249e8..98994962ec129 100644 --- a/packages/@aws-cdk/aws-lambda/README.md +++ b/packages/@aws-cdk/aws-lambda/README.md @@ -450,10 +450,8 @@ new lambda.Function(this, 'Function', { bundling: { image: lambda.Runtime.PYTHON_3_6.bundlingDockerImage, command: [ - 'bash', '-c', ` - pip install -r requirements.txt -t /asset-output && - cp -au . /asset-output - `, + 'bash', '-c', + 'pip install -r requirements.txt -t /asset-output && cp -au . /asset-output' ], }, }), diff --git a/packages/@aws-cdk/aws-lambda/package.json b/packages/@aws-cdk/aws-lambda/package.json index 3387ef50c2623..d9e2c556cf146 100644 --- a/packages/@aws-cdk/aws-lambda/package.json +++ b/packages/@aws-cdk/aws-lambda/package.json @@ -76,7 +76,7 @@ "license": "Apache-2.0", "devDependencies": { "@aws-cdk/assert": "0.0.0", - "@types/aws-lambda": "^8.10.64", + "@types/aws-lambda": "^8.10.72", "@types/lodash": "^4.14.168", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", @@ -176,5 +176,8 @@ "awscdkio": { "announce": false }, - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.lit.expected.json b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.lit.expected.json index 39ff5f8f0e0fb..dc1f0b431db23 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.lit.expected.json +++ b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.lit.expected.json @@ -35,7 +35,7 @@ "Type": "AWS::Lambda::Function", "Properties": { "Code": { - "ZipFile": "exports.handler = async () => {\nconsole.log('hello world');\n};" + "ZipFile": "exports.handler = async () => { console.log('hello world'); };" }, "Handler": "index.handler", "Role": { @@ -161,4 +161,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.lit.ts b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.lit.ts index 2bd3a195682e2..949670ec636b6 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.lit.ts +++ b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.lit.ts @@ -12,7 +12,7 @@ class TestStack extends cdk.Stack { super(scope, id); const fn = new lambda.Function(this, 'MyLambda', { - code: new lambda.InlineCode('exports.handler = async () => {\nconsole.log(\'hello world\');\n};'), + code: new lambda.InlineCode('exports.handler = async () => { console.log(\'hello world\'); };'), handler: 'index.handler', runtime: lambda.Runtime.NODEJS_10_X, }); @@ -50,4 +50,4 @@ const app = new cdk.App(); new TestStack(app, 'aws-lambda-autoscaling'); -app.synth(); \ No newline at end of file +app.synth(); diff --git a/packages/@aws-cdk/aws-licensemanager/package.json b/packages/@aws-cdk/aws-licensemanager/package.json index a84c776b55897..951f8982392ab 100644 --- a/packages/@aws-cdk/aws-licensemanager/package.json +++ b/packages/@aws-cdk/aws-licensemanager/package.json @@ -91,5 +91,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-logs-destinations/package.json b/packages/@aws-cdk/aws-logs-destinations/package.json index 644ca59bf6c39..a4edf4ce3473b 100644 --- a/packages/@aws-cdk/aws-logs-destinations/package.json +++ b/packages/@aws-cdk/aws-logs-destinations/package.json @@ -99,5 +99,8 @@ "env": { "AWSLINT_BASE_CONSTRUCT": true } + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-logs/package.json b/packages/@aws-cdk/aws-logs/package.json index 2b1b897cd3785..51ae5b8acf1cb 100644 --- a/packages/@aws-cdk/aws-logs/package.json +++ b/packages/@aws-cdk/aws-logs/package.json @@ -72,15 +72,15 @@ "devDependencies": { "@aws-cdk/assert": "0.0.0", "@types/nodeunit": "^0.0.31", - "aws-sdk": "^2.830.0", + "aws-sdk": "^2.845.0", "aws-sdk-mock": "^5.1.0", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "cfn2ts": "0.0.0", - "nock": "^13.0.5", + "nock": "^13.0.7", "nodeunit": "^0.11.3", "pkglint": "0.0.0", - "sinon": "^9.2.1" + "sinon": "^9.2.4" }, "dependencies": { "@aws-cdk/aws-cloudwatch": "0.0.0", @@ -123,5 +123,8 @@ "awscdkio": { "announce": false }, - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-lookoutvision/package.json b/packages/@aws-cdk/aws-lookoutvision/package.json index 9f7d59fd4c321..0a812168e8241 100644 --- a/packages/@aws-cdk/aws-lookoutvision/package.json +++ b/packages/@aws-cdk/aws-lookoutvision/package.json @@ -93,5 +93,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-macie/package.json b/packages/@aws-cdk/aws-macie/package.json index 9dc3906117ef3..7e2851d383765 100644 --- a/packages/@aws-cdk/aws-macie/package.json +++ b/packages/@aws-cdk/aws-macie/package.json @@ -91,5 +91,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-managedblockchain/package.json b/packages/@aws-cdk/aws-managedblockchain/package.json index 06f73c3d71b60..c94f2391242fb 100644 --- a/packages/@aws-cdk/aws-managedblockchain/package.json +++ b/packages/@aws-cdk/aws-managedblockchain/package.json @@ -93,5 +93,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-mediaconnect/package.json b/packages/@aws-cdk/aws-mediaconnect/package.json index e859f9c908c75..dbfd544d52dd2 100644 --- a/packages/@aws-cdk/aws-mediaconnect/package.json +++ b/packages/@aws-cdk/aws-mediaconnect/package.json @@ -91,5 +91,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-mediaconvert/package.json b/packages/@aws-cdk/aws-mediaconvert/package.json index df3d541f95ac8..938a726f92183 100644 --- a/packages/@aws-cdk/aws-mediaconvert/package.json +++ b/packages/@aws-cdk/aws-mediaconvert/package.json @@ -93,5 +93,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-medialive/package.json b/packages/@aws-cdk/aws-medialive/package.json index 43dd5b58cf04f..16ecfadc7c1bb 100644 --- a/packages/@aws-cdk/aws-medialive/package.json +++ b/packages/@aws-cdk/aws-medialive/package.json @@ -93,5 +93,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-mediapackage/package.json b/packages/@aws-cdk/aws-mediapackage/package.json index 22cf558399e53..40913de5b816e 100644 --- a/packages/@aws-cdk/aws-mediapackage/package.json +++ b/packages/@aws-cdk/aws-mediapackage/package.json @@ -91,5 +91,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-mediastore/package.json b/packages/@aws-cdk/aws-mediastore/package.json index 5e53e2568b020..46878264355f9 100644 --- a/packages/@aws-cdk/aws-mediastore/package.json +++ b/packages/@aws-cdk/aws-mediastore/package.json @@ -93,5 +93,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-msk/package.json b/packages/@aws-cdk/aws-msk/package.json index 7f1d3b8cf4076..939b66619cda7 100644 --- a/packages/@aws-cdk/aws-msk/package.json +++ b/packages/@aws-cdk/aws-msk/package.json @@ -93,5 +93,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-mwaa/package.json b/packages/@aws-cdk/aws-mwaa/package.json index 306739c25c7f9..17a5ba80b55f1 100644 --- a/packages/@aws-cdk/aws-mwaa/package.json +++ b/packages/@aws-cdk/aws-mwaa/package.json @@ -91,5 +91,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-neptune/package.json b/packages/@aws-cdk/aws-neptune/package.json index b0ff4727beb8a..f4ae26436389b 100644 --- a/packages/@aws-cdk/aws-neptune/package.json +++ b/packages/@aws-cdk/aws-neptune/package.json @@ -92,5 +92,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-networkfirewall/package.json b/packages/@aws-cdk/aws-networkfirewall/package.json index 318ecf359f489..8c218072fe92b 100644 --- a/packages/@aws-cdk/aws-networkfirewall/package.json +++ b/packages/@aws-cdk/aws-networkfirewall/package.json @@ -91,5 +91,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-networkmanager/package.json b/packages/@aws-cdk/aws-networkmanager/package.json index b90a276b6145c..9c38f74d32916 100644 --- a/packages/@aws-cdk/aws-networkmanager/package.json +++ b/packages/@aws-cdk/aws-networkmanager/package.json @@ -91,5 +91,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-opsworks/package.json b/packages/@aws-cdk/aws-opsworks/package.json index de850b75cff6f..b00cff3b7056e 100644 --- a/packages/@aws-cdk/aws-opsworks/package.json +++ b/packages/@aws-cdk/aws-opsworks/package.json @@ -92,5 +92,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-opsworkscm/package.json b/packages/@aws-cdk/aws-opsworkscm/package.json index 996ccc36dc81c..3c102533e89dc 100644 --- a/packages/@aws-cdk/aws-opsworkscm/package.json +++ b/packages/@aws-cdk/aws-opsworkscm/package.json @@ -93,5 +93,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-pinpoint/package.json b/packages/@aws-cdk/aws-pinpoint/package.json index 3afb1b9429da4..ed7fe94c40090 100644 --- a/packages/@aws-cdk/aws-pinpoint/package.json +++ b/packages/@aws-cdk/aws-pinpoint/package.json @@ -93,5 +93,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-pinpointemail/package.json b/packages/@aws-cdk/aws-pinpointemail/package.json index bf69610b3548a..5f98fa3d04e24 100644 --- a/packages/@aws-cdk/aws-pinpointemail/package.json +++ b/packages/@aws-cdk/aws-pinpointemail/package.json @@ -93,5 +93,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-qldb/package.json b/packages/@aws-cdk/aws-qldb/package.json index 433bb0a1c24d9..257716f07faea 100644 --- a/packages/@aws-cdk/aws-qldb/package.json +++ b/packages/@aws-cdk/aws-qldb/package.json @@ -93,5 +93,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-quicksight/package.json b/packages/@aws-cdk/aws-quicksight/package.json index f0bccf72e3de3..14526eb10a35a 100644 --- a/packages/@aws-cdk/aws-quicksight/package.json +++ b/packages/@aws-cdk/aws-quicksight/package.json @@ -93,5 +93,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-ram/package.json b/packages/@aws-cdk/aws-ram/package.json index 6dd1956dce5a7..2ca7d27497147 100644 --- a/packages/@aws-cdk/aws-ram/package.json +++ b/packages/@aws-cdk/aws-ram/package.json @@ -93,5 +93,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-rds/.gitignore b/packages/@aws-cdk/aws-rds/.gitignore index dcc1dc41e477f..17a41566f0002 100644 --- a/packages/@aws-cdk/aws-rds/.gitignore +++ b/packages/@aws-cdk/aws-rds/.gitignore @@ -15,4 +15,5 @@ nyc.config.js *.snk !.eslintrc.js -junit.xml \ No newline at end of file +junit.xml +!jest.config.js \ No newline at end of file diff --git a/packages/@aws-cdk/aws-rds/.npmignore b/packages/@aws-cdk/aws-rds/.npmignore index a94c531529866..9e88226921c33 100644 --- a/packages/@aws-cdk/aws-rds/.npmignore +++ b/packages/@aws-cdk/aws-rds/.npmignore @@ -23,4 +23,5 @@ tsconfig.json # exclude cdk artifacts **/cdk.out junit.xml -test/ \ No newline at end of file +test/ +jest.config.js \ No newline at end of file diff --git a/packages/@aws-cdk/aws-rds/README.md b/packages/@aws-cdk/aws-rds/README.md index 67bb8328696bc..b00e3c64187f0 100644 --- a/packages/@aws-cdk/aws-rds/README.md +++ b/packages/@aws-cdk/aws-rds/README.md @@ -283,7 +283,7 @@ instance.grantConnect(role); // Grant the role connection access to the DB. The following example shows granting connection access for RDS Proxy to an IAM role. ```ts -const cluster = new rds.DatabaseCluster(stack, 'Database'{ +const cluster = new rds.DatabaseCluster(stack, 'Database', { engine: rds.DatabaseClusterEngine.AURORA, instanceProps: { vpc }, }); @@ -295,7 +295,7 @@ const proxy = new rds.DatabaseProxy(stack, 'Proxy', { }); const role = new Role(stack, 'DBProxyRole', { assumedBy: new AccountPrincipal(stack.account) }); -proxy.grantConnect(role); // Grant the role connection access to the DB Proxy. +proxy.grantConnect(role, 'admin'); // Grant the role connection access to the DB Proxy for database user 'admin'. ``` **Note**: In addition to the setup above, a database user will need to be created to support IAM auth. diff --git a/packages/@aws-cdk/aws-rds/jest.config.js b/packages/@aws-cdk/aws-rds/jest.config.js new file mode 100644 index 0000000000000..54e28beb9798b --- /dev/null +++ b/packages/@aws-cdk/aws-rds/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-rds/lib/cluster-engine.ts b/packages/@aws-cdk/aws-rds/lib/cluster-engine.ts index 9c2645f64ab43..7e75162761a33 100644 --- a/packages/@aws-cdk/aws-rds/lib/cluster-engine.ts +++ b/packages/@aws-cdk/aws-rds/lib/cluster-engine.ts @@ -455,6 +455,8 @@ export class AuroraPostgresEngineVersion { public static readonly VER_11_8 = AuroraPostgresEngineVersion.of('11.8', '11', { s3Import: true, s3Export: true }); /** Version "11.9". */ public static readonly VER_11_9 = AuroraPostgresEngineVersion.of('11.9', '11', { s3Import: true, s3Export: true }); + /** Version "12.4". */ + public static readonly VER_12_4 = AuroraPostgresEngineVersion.of('12.4', '12', { s3Import: true, s3Export: true }); /** * Create a new AuroraPostgresEngineVersion with an arbitrary version. diff --git a/packages/@aws-cdk/aws-rds/lib/instance-engine.ts b/packages/@aws-cdk/aws-rds/lib/instance-engine.ts index e2e425eb71736..95d462c23f5fc 100644 --- a/packages/@aws-cdk/aws-rds/lib/instance-engine.ts +++ b/packages/@aws-cdk/aws-rds/lib/instance-engine.ts @@ -425,76 +425,181 @@ export interface PostgresEngineFeatures { * (those returned by {@link DatabaseInstanceEngine.postgres}). */ export class PostgresEngineVersion { - /** Version "9.5" (only a major version, without a specific minor version). */ + /** + * Version "9.5" (only a major version, without a specific minor version). + * @deprecated PostgreSQL 9.5 will reach end of life on February 16, 2021 + */ public static readonly VER_9_5 = PostgresEngineVersion.of('9.5', '9.5'); - /** Version "9.5.2". */ + /** + * Version "9.5.2". + * @deprecated PostgreSQL 9.5 will reach end of life on February 16, 2021 + */ public static readonly VER_9_5_2 = PostgresEngineVersion.of('9.5.2', '9.5'); - /** Version "9.5.4". */ + /** + * Version "9.5.4". + * @deprecated PostgreSQL 9.5 will reach end of life on February 16, 2021 + */ public static readonly VER_9_5_4 = PostgresEngineVersion.of('9.5.4', '9.5'); - /** Version "9.5.6". */ + /** + * Version "9.5.6". + * @deprecated PostgreSQL 9.5 will reach end of life on February 16, 2021 + */ public static readonly VER_9_5_6 = PostgresEngineVersion.of('9.5.6', '9.5'); - /** Version "9.5.7". */ + /** + * Version "9.5.7". + * @deprecated PostgreSQL 9.5 will reach end of life on February 16, 2021 + */ public static readonly VER_9_5_7 = PostgresEngineVersion.of('9.5.7', '9.5'); - /** Version "9.5.9". */ + /** + * Version "9.5.9". + * @deprecated PostgreSQL 9.5 will reach end of life on February 16, 2021 + */ public static readonly VER_9_5_9 = PostgresEngineVersion.of('9.5.9', '9.5'); - /** Version "9.5.10". */ + /** + * Version "9.5.10". + * @deprecated PostgreSQL 9.5 will reach end of life on February 16, 2021 + */ public static readonly VER_9_5_10 = PostgresEngineVersion.of('9.5.10', '9.5'); - /** Version "9.5.12". */ + /** + * Version "9.5.12". + * @deprecated PostgreSQL 9.5 will reach end of life on February 16, 2021 + */ public static readonly VER_9_5_12 = PostgresEngineVersion.of('9.5.12', '9.5'); - /** Version "9.5.13". */ + /** + * Version "9.5.13". + * @deprecated PostgreSQL 9.5 will reach end of life on February 16, 2021 + */ public static readonly VER_9_5_13 = PostgresEngineVersion.of('9.5.13', '9.5'); - /** Version "9.5.14". */ + /** + * Version "9.5.14". + * @deprecated PostgreSQL 9.5 will reach end of life on February 16, 2021 + */ public static readonly VER_9_5_14 = PostgresEngineVersion.of('9.5.14', '9.5'); - /** Version "9.5.15". */ + /** + * Version "9.5.15". + * @deprecated PostgreSQL 9.5 will reach end of life on February 16, 2021 + */ public static readonly VER_9_5_15 = PostgresEngineVersion.of('9.5.15', '9.5'); - /** Version "9.5.16". */ + /** + * Version "9.5.16". + * @deprecated PostgreSQL 9.5 will reach end of life on February 16, 2021 + */ public static readonly VER_9_5_16 = PostgresEngineVersion.of('9.5.16', '9.5'); - /** Version "9.5.18". */ + /** + * Version "9.5.18". + * @deprecated PostgreSQL 9.5 will reach end of life on February 16, 2021 + */ public static readonly VER_9_5_18 = PostgresEngineVersion.of('9.5.18', '9.5'); - /** Version "9.5.19". */ + /** + * Version "9.5.19". + * @deprecated PostgreSQL 9.5 will reach end of life on February 16, 2021 + */ public static readonly VER_9_5_19 = PostgresEngineVersion.of('9.5.19', '9.5'); - /** Version "9.5.20". */ + /** + * Version "9.5.20". + * @deprecated PostgreSQL 9.5 will reach end of life on February 16, 2021 + */ public static readonly VER_9_5_20 = PostgresEngineVersion.of('9.5.20', '9.5'); - /** Version "9.5.21". */ + /** + * Version "9.5.21". + * @deprecated PostgreSQL 9.5 will reach end of life on February 16, 2021 + */ public static readonly VER_9_5_21 = PostgresEngineVersion.of('9.5.21', '9.5'); - /** Version "9.5.22". */ + /** + * Version "9.5.22". + * @deprecated PostgreSQL 9.5 will reach end of life on February 16, 2021 + */ public static readonly VER_9_5_22 = PostgresEngineVersion.of('9.5.22', '9.5'); - /** Version "9.5.23". */ + /** + * Version "9.5.23". + * @deprecated PostgreSQL 9.5 will reach end of life on February 16, 2021 + */ public static readonly VER_9_5_23 = PostgresEngineVersion.of('9.5.23', '9.5'); - /** Version "9.6" (only a major version, without a specific minor version). */ + /** + * Version "9.6" (only a major version, without a specific minor version). + * @deprecated PostgreSQL 9.6 will reach end of life in November 2021 + */ public static readonly VER_9_6 = PostgresEngineVersion.of('9.6', '9.6'); - /** Version "9.6.1". */ + /** + * Version "9.6.1". + * @deprecated PostgreSQL 9.6 will reach end of life in November 2021 + */ public static readonly VER_9_6_1 = PostgresEngineVersion.of('9.6.1', '9.6'); - /** Version "9.6.2". */ + /** + * Version "9.6.2". + * @deprecated PostgreSQL 9.6 will reach end of life in November 2021 + */ public static readonly VER_9_6_2 = PostgresEngineVersion.of('9.6.2', '9.6'); - /** Version "9.6.3". */ + /** + * Version "9.6.3". + * @deprecated PostgreSQL 9.6 will reach end of life in November 2021 + */ public static readonly VER_9_6_3 = PostgresEngineVersion.of('9.6.3', '9.6'); - /** Version "9.6.5". */ + /** + * Version "9.6.5". + * @deprecated PostgreSQL 9.6 will reach end of life in November 2021 + */ public static readonly VER_9_6_5 = PostgresEngineVersion.of('9.6.5', '9.6'); - /** Version "9.6.6". */ + /** + * Version "9.6.6". + * @deprecated PostgreSQL 9.6 will reach end of life in November 2021 + */ public static readonly VER_9_6_6 = PostgresEngineVersion.of('9.6.6', '9.6'); - /** Version "9.6.8". */ + /** + * Version "9.6.8". + * @deprecated PostgreSQL 9.6 will reach end of life in November 2021 + */ public static readonly VER_9_6_8 = PostgresEngineVersion.of('9.6.8', '9.6'); - /** Version "9.6.9". */ + /** + * Version "9.6.9". + * @deprecated PostgreSQL 9.6 will reach end of life in November 2021 + */ public static readonly VER_9_6_9 = PostgresEngineVersion.of('9.6.9', '9.6'); - /** Version "9.6.10". */ + /** + * Version "9.6.10". + * @deprecated PostgreSQL 9.6 will reach end of life in November 2021 + */ public static readonly VER_9_6_10 = PostgresEngineVersion.of('9.6.10', '9.6'); - /** Version "9.6.11". */ + /** + * Version "9.6.11". + * @deprecated PostgreSQL 9.6 will reach end of life in November 2021 + */ public static readonly VER_9_6_11 = PostgresEngineVersion.of('9.6.11', '9.6'); - /** Version "9.6.12". */ + /** + * Version "9.6.12". + * @deprecated PostgreSQL 9.6 will reach end of life in November 2021 + */ public static readonly VER_9_6_12 = PostgresEngineVersion.of('9.6.12', '9.6'); - /** Version "9.6.14". */ + /** + * Version "9.6.14". + * @deprecated PostgreSQL 9.6 will reach end of life in November 2021 + */ public static readonly VER_9_6_14 = PostgresEngineVersion.of('9.6.14', '9.6'); - /** Version "9.6.15". */ + /** + * Version "9.6.15". + * @deprecated PostgreSQL 9.6 will reach end of life in November 2021 + */ public static readonly VER_9_6_15 = PostgresEngineVersion.of('9.6.15', '9.6'); - /** Version "9.6.16". */ + /** + * Version "9.6.16". + * @deprecated PostgreSQL 9.6 will reach end of life in November 2021 + */ public static readonly VER_9_6_16 = PostgresEngineVersion.of('9.6.16', '9.6'); - /** Version "9.6.17". */ + /** + * Version "9.6.17". + * @deprecated PostgreSQL 9.6 will reach end of life in November 2021 + */ public static readonly VER_9_6_17 = PostgresEngineVersion.of('9.6.17', '9.6'); - /** Version "9.6.18". */ + /** + * Version "9.6.18". + * @deprecated PostgreSQL 9.6 will reach end of life in November 2021 + */ public static readonly VER_9_6_18 = PostgresEngineVersion.of('9.6.18', '9.6'); - /** Version "9.6.19". */ + /** + * Version "9.6.19". + * @deprecated PostgreSQL 9.6 will reach end of life in November 2021 + */ public static readonly VER_9_6_19 = PostgresEngineVersion.of('9.6.19', '9.6'); /** Version "10" (only a major version, without a specific minor version). */ @@ -551,6 +656,8 @@ export class PostgresEngineVersion { public static readonly VER_12_3 = PostgresEngineVersion.of('12.3', '12', { s3Import: true }); /** Version "12.4". */ public static readonly VER_12_4 = PostgresEngineVersion.of('12.4', '12', { s3Import: true }); + /** Version "12.5". */ + public static readonly VER_12_5 = PostgresEngineVersion.of('12.5', '12', { s3Import: true }); /** * Create a new PostgresEngineVersion with an arbitrary version. diff --git a/packages/@aws-cdk/aws-rds/lib/proxy.ts b/packages/@aws-cdk/aws-rds/lib/proxy.ts index e0105702523c8..fa50b891fca80 100644 --- a/packages/@aws-cdk/aws-rds/lib/proxy.ts +++ b/packages/@aws-cdk/aws-rds/lib/proxy.ts @@ -70,7 +70,7 @@ export class ProxyTarget { /** * Bind this target to the specified database proxy. */ - public bind(_: DatabaseProxy): ProxyTargetConfig { + public bind(proxy: DatabaseProxy): ProxyTargetConfig { const engine: IEngine | undefined = this.dbInstance?.engine ?? this.dbCluster?.engine; if (!engine) { @@ -84,6 +84,10 @@ export class ProxyTarget { throw new Error(`Engine '${engineDescription(engine)}' does not support proxies`); } + // allow connecting to the Cluster/Instance from the Proxy + this.dbCluster?.connections.allowDefaultPortFrom(proxy, 'Allow connections to the database Cluster from the Proxy'); + this.dbInstance?.connections.allowDefaultPortFrom(proxy, 'Allow connections to the database Instance from the Proxy'); + return { engineFamily, dbClusters: this.dbCluster ? [this.dbCluster] : undefined, @@ -318,8 +322,14 @@ export interface IDatabaseProxy extends cdk.IResource { /** * Grant the given identity connection access to the proxy. + * + * @param grantee the Principal to grant the permissions to + * @param dbUser the name of the database user to allow connecting as to the proxy + * + * @default - if the Proxy had been provided a single Secret value, + * the user will be taken from that Secret */ - grantConnect(grantee: iam.IGrantable): iam.Grant; + grantConnect(grantee: iam.IGrantable, dbUser?: string): iam.Grant; } /** @@ -331,11 +341,22 @@ abstract class DatabaseProxyBase extends cdk.Resource implements IDatabaseProxy public abstract readonly dbProxyArn: string; public abstract readonly endpoint: string; - public grantConnect(grantee: iam.IGrantable): iam.Grant { + public grantConnect(grantee: iam.IGrantable, dbUser?: string): iam.Grant { + if (!dbUser) { + throw new Error('For imported Database Proxies, the dbUser is required in grantConnect()'); + } + const scopeStack = cdk.Stack.of(this); + const proxyGeneratedId = scopeStack.parseArn(this.dbProxyArn, ':').resourceName; + const userArn = scopeStack.formatArn({ + service: 'rds-db', + resource: 'dbuser', + resourceName: `${proxyGeneratedId}/${dbUser}`, + sep: ':', + }); return iam.Grant.addToPrincipal({ grantee, actions: ['rds-db:connect'], - resourceArns: [this.dbProxyArn], + resourceArns: [userArn], }); } } @@ -389,6 +410,7 @@ export class DatabaseProxy extends DatabaseProxyBase */ public readonly connections: ec2.Connections; + private readonly secrets: secretsmanager.ISecret[]; private readonly resource: CfnDBProxy; constructor(scope: Construct, id: string, props: DatabaseProxyProps) { @@ -402,13 +424,20 @@ export class DatabaseProxy extends DatabaseProxyBase secret.grantRead(role); } - this.connections = new ec2.Connections({ securityGroups: props.securityGroups }); + const securityGroups = props.securityGroups ?? [ + new ec2.SecurityGroup(this, 'ProxySecurityGroup', { + description: 'SecurityGroup for Database Proxy', + vpc: props.vpc, + }), + ]; + this.connections = new ec2.Connections({ securityGroups }); const bindResult = props.proxyTarget.bind(this); if (props.secrets.length < 1) { throw new Error('One or more secrets are required.'); } + this.secrets = props.secrets; this.resource = new CfnDBProxy(this, 'Resource', { auth: props.secrets.map(_ => { @@ -424,7 +453,7 @@ export class DatabaseProxy extends DatabaseProxyBase idleClientTimeout: props.idleClientTimeout?.toSeconds(), requireTls: props.requireTLS ?? true, roleArn: role.roleArn, - vpcSecurityGroupIds: props.securityGroups?.map(_ => _.securityGroupId), + vpcSecurityGroupIds: cdk.Lazy.list({ produce: () => this.connections.securityGroups.map(_ => _.securityGroupId) }), vpcSubnetIds: props.vpc.selectSubnets(props.vpcSubnets).subnetIds, }); @@ -467,6 +496,18 @@ export class DatabaseProxy extends DatabaseProxyBase targetType: secretsmanager.AttachmentTargetType.RDS_DB_PROXY, }; } + + public grantConnect(grantee: iam.IGrantable, dbUser?: string): iam.Grant { + if (!dbUser) { + if (this.secrets.length > 1) { + throw new Error('When the Proxy contains multiple Secrets, you must pass a dbUser explicitly to grantConnect()'); + } + // 'username' is the field RDS uses here, + // see https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/rds-proxy.html#rds-proxy-secrets-arns + dbUser = this.secrets[0].secretValueFromJson('username').toString(); + } + return super.grantConnect(grantee, dbUser); + } } /** diff --git a/packages/@aws-cdk/aws-rds/package.json b/packages/@aws-cdk/aws-rds/package.json index bf1ce83d40d00..01ce0a8ca4a12 100644 --- a/packages/@aws-cdk/aws-rds/package.json +++ b/packages/@aws-cdk/aws-rds/package.json @@ -53,6 +53,7 @@ }, "cdk-build": { "cloudformation": "AWS::RDS", + "jest": true, "env": { "AWSLINT_BASE_CONSTRUCT": true } @@ -73,12 +74,11 @@ "@aws-cdk/assert": "0.0.0", "@aws-cdk/aws-events-targets": "0.0.0", "@aws-cdk/aws-lambda": "0.0.0", - "@types/nodeunit": "^0.0.31", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "cfn2ts": "0.0.0", - "nodeunit": "^0.11.3", - "pkglint": "0.0.0" + "pkglint": "0.0.0", + "nodeunit-shim": "0.0.0" }, "dependencies": { "@aws-cdk/aws-cloudwatch": "0.0.0", @@ -140,5 +140,8 @@ "maturity": "stable", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-rds/test/test.cluster-engine.ts b/packages/@aws-cdk/aws-rds/test/cluster-engine.test.ts similarity index 98% rename from packages/@aws-cdk/aws-rds/test/test.cluster-engine.ts rename to packages/@aws-cdk/aws-rds/test/cluster-engine.test.ts index d5af56f1d10da..54a02441fb9c7 100644 --- a/packages/@aws-cdk/aws-rds/test/test.cluster-engine.ts +++ b/packages/@aws-cdk/aws-rds/test/cluster-engine.test.ts @@ -1,7 +1,7 @@ -import { Test } from 'nodeunit'; +import { nodeunitShim, Test } from 'nodeunit-shim'; import { AuroraEngineVersion, AuroraMysqlEngineVersion, AuroraPostgresEngineVersion, DatabaseClusterEngine } from '../lib'; -export = { +nodeunitShim({ "default parameterGroupFamily for versionless Aurora cluster engine is 'aurora5.6'"(test: Test) { // GIVEN const engine = DatabaseClusterEngine.AURORA; @@ -115,4 +115,4 @@ export = { test.deepEqual(DatabaseClusterEngine.auroraPostgres({ version: AuroraPostgresEngineVersion.VER_9_6_9 }).supportedLogTypes, ['postgresql']); test.done(); }, -} +}); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-rds/test/test.cluster.ts b/packages/@aws-cdk/aws-rds/test/cluster.test.ts similarity index 99% rename from packages/@aws-cdk/aws-rds/test/test.cluster.ts rename to packages/@aws-cdk/aws-rds/test/cluster.test.ts index fc877641dcc42..ec7c36129b30d 100644 --- a/packages/@aws-cdk/aws-rds/test/test.cluster.ts +++ b/packages/@aws-cdk/aws-rds/test/cluster.test.ts @@ -5,13 +5,13 @@ 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 { Test } from 'nodeunit'; +import { nodeunitShim, Test } from 'nodeunit-shim'; import { AuroraEngineVersion, AuroraMysqlEngineVersion, AuroraPostgresEngineVersion, CfnDBCluster, Credentials, DatabaseCluster, DatabaseClusterEngine, DatabaseClusterFromSnapshot, ParameterGroup, PerformanceInsightRetention, SubnetGroup, } from '../lib'; -export = { +nodeunitShim({ 'creating a Cluster also creates 2 DB Instances'(test: Test) { // GIVEN const stack = testStack(); @@ -1838,7 +1838,7 @@ export = { test.done(); }, -}; +}); function testStack() { const stack = new cdk.Stack(undefined, undefined, { env: { account: '12345', region: 'us-test-1' } }); diff --git a/packages/@aws-cdk/aws-rds/test/test.database-secret.ts b/packages/@aws-cdk/aws-rds/test/database-secret.test.ts similarity index 97% rename from packages/@aws-cdk/aws-rds/test/test.database-secret.ts rename to packages/@aws-cdk/aws-rds/test/database-secret.test.ts index 6e4edb551d764..97de0085806e0 100644 --- a/packages/@aws-cdk/aws-rds/test/test.database-secret.ts +++ b/packages/@aws-cdk/aws-rds/test/database-secret.test.ts @@ -1,10 +1,10 @@ import { expect, haveResource } from '@aws-cdk/assert'; import { CfnResource, Stack } from '@aws-cdk/core'; -import { Test } from 'nodeunit'; +import { nodeunitShim, Test } from 'nodeunit-shim'; import { DatabaseSecret } from '../lib'; import { DEFAULT_PASSWORD_EXCLUDE_CHARS } from '../lib/private/util'; -export = { +nodeunitShim({ 'create a database secret'(test: Test) { // GIVEN const stack = new Stack(); @@ -110,7 +110,7 @@ export = { test.done(); }, -}; +}); function getSecretLogicalId(dbSecret: DatabaseSecret, stack: Stack): string { const cfnSecret = dbSecret.node.defaultChild as CfnResource; diff --git a/packages/@aws-cdk/aws-rds/test/test.database-secretmanager.ts b/packages/@aws-cdk/aws-rds/test/database-secretmanager.test.ts similarity index 96% rename from packages/@aws-cdk/aws-rds/test/test.database-secretmanager.ts rename to packages/@aws-cdk/aws-rds/test/database-secretmanager.test.ts index 8d6310d21ce42..760a16e228cd7 100644 --- a/packages/@aws-cdk/aws-rds/test/test.database-secretmanager.ts +++ b/packages/@aws-cdk/aws-rds/test/database-secretmanager.test.ts @@ -2,10 +2,10 @@ import { ABSENT, expect, haveResource, ResourcePart } from '@aws-cdk/assert'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as secretsmanager from '@aws-cdk/aws-secretsmanager'; import * as cdk from '@aws-cdk/core'; -import { Test } from 'nodeunit'; +import { nodeunitShim, Test } from 'nodeunit-shim'; import { ServerlessCluster, DatabaseClusterEngine, ParameterGroup, Credentials } from '../lib'; -export = { +nodeunitShim({ 'can create a Serverless Cluster using an existing secret from secretmanager'(test: Test) { // GIVEN const stack = testStack(); @@ -47,7 +47,7 @@ export = { test.done(); }, -}; +}); function testStack() { const stack = new cdk.Stack(undefined, undefined, { env: { account: '12345', region: 'us-test-1' } }); diff --git a/packages/@aws-cdk/aws-rds/test/test.instance-engine.ts b/packages/@aws-cdk/aws-rds/test/instance-engine.test.ts similarity index 99% rename from packages/@aws-cdk/aws-rds/test/test.instance-engine.ts rename to packages/@aws-cdk/aws-rds/test/instance-engine.test.ts index 90d5a7a204eae..2c9f1fddf8076 100644 --- a/packages/@aws-cdk/aws-rds/test/test.instance-engine.ts +++ b/packages/@aws-cdk/aws-rds/test/instance-engine.test.ts @@ -1,10 +1,10 @@ import { expect, haveResourceLike } from '@aws-cdk/assert'; import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; -import { Test } from 'nodeunit'; +import { nodeunitShim, Test } from 'nodeunit-shim'; import * as rds from '../lib'; -export = { +nodeunitShim({ 'default parameterGroupFamily for versionless MariaDB instance engine is not defined'(test: Test) { const engine = rds.DatabaseInstanceEngine.MARIADB; @@ -284,4 +284,4 @@ export = { test.done(); }, }, -}; +}); diff --git a/packages/@aws-cdk/aws-rds/test/test.instance.ts b/packages/@aws-cdk/aws-rds/test/instance.test.ts similarity index 99% rename from packages/@aws-cdk/aws-rds/test/test.instance.ts rename to packages/@aws-cdk/aws-rds/test/instance.test.ts index 7591a576946cb..8c4e4460f65f1 100644 --- a/packages/@aws-cdk/aws-rds/test/test.instance.ts +++ b/packages/@aws-cdk/aws-rds/test/instance.test.ts @@ -7,13 +7,13 @@ import * as lambda from '@aws-cdk/aws-lambda'; import * as logs from '@aws-cdk/aws-logs'; import * as s3 from '@aws-cdk/aws-s3'; import * as cdk from '@aws-cdk/core'; -import { Test } from 'nodeunit'; +import { nodeunitShim, Test } from 'nodeunit-shim'; import * as rds from '../lib'; let stack: cdk.Stack; let vpc: ec2.Vpc; -export = { +nodeunitShim({ 'setUp'(cb: () => void) { stack = new cdk.Stack(); vpc = new ec2.Vpc(stack, 'VPC'); @@ -1023,7 +1023,7 @@ export = { new rds.DatabaseInstance(stack, 'Instance', { vpc, engine: rds.DatabaseInstanceEngine.postgres({ - version: rds.PostgresEngineVersion.VER_9_5_7, + version: rds.PostgresEngineVersion.VER_12_4, }), }); @@ -1242,4 +1242,4 @@ export = { test.done(); }, -}; +}); diff --git a/packages/@aws-cdk/aws-rds/test/integ.cluster-s3.expected.json b/packages/@aws-cdk/aws-rds/test/integ.cluster-s3.expected.json index 710884195806a..394c50be1df78 100644 --- a/packages/@aws-cdk/aws-rds/test/integ.cluster-s3.expected.json +++ b/packages/@aws-cdk/aws-rds/test/integ.cluster-s3.expected.json @@ -361,23 +361,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { diff --git a/packages/@aws-cdk/aws-rds/test/integ.cluster.expected.json b/packages/@aws-cdk/aws-rds/test/integ.cluster.expected.json index 37f63d001843e..25f2149bf8a0c 100644 --- a/packages/@aws-cdk/aws-rds/test/integ.cluster.expected.json +++ b/packages/@aws-cdk/aws-rds/test/integ.cluster.expected.json @@ -371,23 +371,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { diff --git a/packages/@aws-cdk/aws-rds/test/integ.proxy.expected.json b/packages/@aws-cdk/aws-rds/test/integ.proxy.expected.json index 3a77314058702..52aab8716ba8c 100644 --- a/packages/@aws-cdk/aws-rds/test/integ.proxy.expected.json +++ b/packages/@aws-cdk/aws-rds/test/integ.proxy.expected.json @@ -385,6 +385,37 @@ } } }, + "dbInstanceSecurityGroupfromawscdkrdsproxydbProxyProxySecurityGroupA345AFE5IndirectPortE3621D4F": { + "Type": "AWS::EC2::SecurityGroupIngress", + "Properties": { + "IpProtocol": "tcp", + "Description": "Allow connections to the database Instance from the Proxy", + "FromPort": { + "Fn::GetAtt": [ + "dbInstance4076B1EC", + "Endpoint.Port" + ] + }, + "GroupId": { + "Fn::GetAtt": [ + "dbInstanceSecurityGroupA58A00A3", + "GroupId" + ] + }, + "SourceSecurityGroupId": { + "Fn::GetAtt": [ + "dbProxyProxySecurityGroup16E727A7", + "GroupId" + ] + }, + "ToPort": { + "Fn::GetAtt": [ + "dbInstance4076B1EC", + "Endpoint.Port" + ] + } + } + }, "dbInstanceSecret032D3661": { "Type": "AWS::SecretsManager::Secret", "Properties": { @@ -429,6 +460,7 @@ "Ref": "dbInstanceSubnetGroupD062EC9E" }, "Engine": "postgres", + "EngineVersion": "11.5", "MasterUsername": { "Fn::Join": [ "", @@ -508,6 +540,22 @@ ] } }, + "dbProxyProxySecurityGroup16E727A7": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "SecurityGroup for Database Proxy", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Ref": "vpcA2121C38" + } + } + }, "dbProxy3B89EAF2": { "Type": "AWS::RDS::DBProxy", "Properties": { @@ -522,7 +570,6 @@ ], "DBProxyName": "dbProxy", "EngineFamily": "POSTGRESQL", - "RequireTLS": true, "RoleArn": { "Fn::GetAtt": [ "dbProxyIAMRole662F3AB8", @@ -536,6 +583,15 @@ { "Ref": "vpcPrivateSubnet2Subnet7031C2BA" } + ], + "RequireTLS": true, + "VpcSecurityGroupIds": [ + { + "Fn::GetAtt": [ + "dbProxyProxySecurityGroup16E727A7", + "GroupId" + ] + } ] } }, @@ -545,6 +601,7 @@ "DBProxyName": { "Ref": "dbProxy3B89EAF2" }, + "TargetGroupName": "default", "ConnectionPoolConfigurationInfo": { "ConnectionBorrowTimeout": 30, "MaxConnectionsPercent": 50 @@ -553,9 +610,8 @@ { "Ref": "dbInstance4076B1EC" } - ], - "TargetGroupName": "default" + ] } } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-rds/test/integ.proxy.ts b/packages/@aws-cdk/aws-rds/test/integ.proxy.ts index d3945cf4a5b99..e59ead63f3590 100644 --- a/packages/@aws-cdk/aws-rds/test/integ.proxy.ts +++ b/packages/@aws-cdk/aws-rds/test/integ.proxy.ts @@ -8,7 +8,9 @@ const stack = new cdk.Stack(app, 'aws-cdk-rds-proxy'); const vpc = new ec2.Vpc(stack, 'vpc', { maxAzs: 2 }); const dbInstance = new rds.DatabaseInstance(stack, 'dbInstance', { - engine: rds.DatabaseInstanceEngine.POSTGRES, + engine: rds.DatabaseInstanceEngine.postgres({ + version: rds.PostgresEngineVersion.VER_11_5, + }), instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE3, ec2.InstanceSize.MEDIUM), credentials: rds.Credentials.fromUsername('master', { excludeCharacters: '"@/\\', diff --git a/packages/@aws-cdk/aws-rds/test/test.option-group.ts b/packages/@aws-cdk/aws-rds/test/option-group.test.ts similarity index 98% rename from packages/@aws-cdk/aws-rds/test/test.option-group.ts rename to packages/@aws-cdk/aws-rds/test/option-group.test.ts index d4129b6aebd5a..b7c6cc851874c 100644 --- a/packages/@aws-cdk/aws-rds/test/test.option-group.ts +++ b/packages/@aws-cdk/aws-rds/test/option-group.test.ts @@ -1,10 +1,10 @@ import { expect, haveResource } from '@aws-cdk/assert'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as cdk from '@aws-cdk/core'; -import { Test } from 'nodeunit'; +import { nodeunitShim, Test } from 'nodeunit-shim'; import { DatabaseInstanceEngine, OptionGroup, OracleEngineVersion, OracleLegacyEngineVersion } from '../lib'; -export = { +nodeunitShim({ 'create an option group'(test: Test) { // GIVEN const stack = new cdk.Stack(); @@ -160,4 +160,4 @@ export = { test.done(); }, -}; +}); diff --git a/packages/@aws-cdk/aws-rds/test/test.parameter-group.ts b/packages/@aws-cdk/aws-rds/test/parameter-group.test.ts similarity index 97% rename from packages/@aws-cdk/aws-rds/test/test.parameter-group.ts rename to packages/@aws-cdk/aws-rds/test/parameter-group.test.ts index f4a21db1fdec0..0f9fd73f44e8c 100644 --- a/packages/@aws-cdk/aws-rds/test/test.parameter-group.ts +++ b/packages/@aws-cdk/aws-rds/test/parameter-group.test.ts @@ -1,9 +1,9 @@ import { countResources, expect, haveResource } from '@aws-cdk/assert'; import * as cdk from '@aws-cdk/core'; -import { Test } from 'nodeunit'; +import { nodeunitShim, Test } from 'nodeunit-shim'; import { DatabaseClusterEngine, ParameterGroup } from '../lib'; -export = { +nodeunitShim({ "does not create a parameter group if it wasn't bound to a cluster or instance"(test: Test) { // GIVEN const stack = new cdk.Stack(); @@ -126,4 +126,4 @@ export = { test.done(); }, -}; +}); diff --git a/packages/@aws-cdk/aws-rds/test/test.proxy.ts b/packages/@aws-cdk/aws-rds/test/proxy.test.ts similarity index 64% rename from packages/@aws-cdk/aws-rds/test/test.proxy.ts rename to packages/@aws-cdk/aws-rds/test/proxy.test.ts index 83d79146cebf7..a0835879b5d79 100644 --- a/packages/@aws-cdk/aws-rds/test/test.proxy.ts +++ b/packages/@aws-cdk/aws-rds/test/proxy.test.ts @@ -3,13 +3,15 @@ import * as ec2 from '@aws-cdk/aws-ec2'; import { AccountPrincipal, Role } from '@aws-cdk/aws-iam'; import * as secretsmanager from '@aws-cdk/aws-secretsmanager'; import * as cdk from '@aws-cdk/core'; -import { Test } from 'nodeunit'; +import { nodeunitShim, Test } from 'nodeunit-shim'; import * as rds from '../lib'; let stack: cdk.Stack; let vpc: ec2.IVpc; -export = { +let importedDbProxy: rds.IDatabaseProxy; + +nodeunitShim({ 'setUp'(cb: () => void) { stack = new cdk.Stack(); vpc = new ec2.Vpc(stack, 'VPC'); @@ -123,8 +125,6 @@ export = { }, ], })); - - // THEN expect(stack).to(haveResourceLike('AWS::RDS::DBProxyTargetGroup', { DBProxyName: { Ref: 'ProxyCB0DFB71', @@ -138,6 +138,22 @@ export = { DBInstanceIdentifiers: ABSENT, TargetGroupName: 'default', })); + expect(stack).to(haveResourceLike('AWS::EC2::SecurityGroupIngress', { + IpProtocol: 'tcp', + Description: 'Allow connections to the database Cluster from the Proxy', + FromPort: { + 'Fn::GetAtt': ['DatabaseB269D8BB', 'Endpoint.Port'], + }, + GroupId: { + 'Fn::GetAtt': ['DatabaseSecurityGroup5C91FDCB', 'GroupId'], + }, + SourceSecurityGroupId: { + 'Fn::GetAtt': ['ProxyProxySecurityGroupC42FC3CE', 'GroupId'], + }, + ToPort: { + 'Fn::GetAtt': ['DatabaseB269D8BB', 'Endpoint.Port'], + }, + })); test.done(); }, @@ -205,6 +221,7 @@ export = { engine: rds.DatabaseClusterEngine.auroraPostgres({ version: rds.AuroraPostgresEngineVersion.VER_9_6_11, }), + port: 5432, }); new rds.DatabaseProxy(stack, 'Proxy', { @@ -221,11 +238,75 @@ export = { 'my-cluster', ], })); + expect(stack).to(haveResourceLike('AWS::EC2::SecurityGroup', { + GroupDescription: 'SecurityGroup for Database Proxy', + VpcId: { Ref: 'VPCB9E5F0B4' }, + })); test.done(); }, - 'grantConnect should add IAM Policy with action rds-db:connect'(test: Test) { + 'imported Proxies': { + 'setUp'(cb: () => void) { + importedDbProxy = rds.DatabaseProxy.fromDatabaseProxyAttributes(stack, 'Proxy', { + dbProxyName: 'my-proxy', + dbProxyArn: 'arn:aws:rds:us-east-1:123456789012:db-proxy:prx-1234abcd', + endpoint: 'my-endpoint', + securityGroups: [], + }); + + cb(); + }, + + 'grant rds-db:connect in grantConnect() with a dbUser explicitly passed'(test: Test) { + // WHEN + const role = new Role(stack, 'DBProxyRole', { + assumedBy: new AccountPrincipal(stack.account), + }); + const databaseUser = 'test'; + importedDbProxy.grantConnect(role, databaseUser); + + // THEN + expect(stack).to(haveResourceLike('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [{ + Effect: 'Allow', + Action: 'rds-db:connect', + Resource: { + 'Fn::Join': ['', [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':rds-db:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':dbuser:prx-1234abcd/test', + ]], + }, + }], + Version: '2012-10-17', + }, + })); + + test.done(); + }, + + 'throws when grantConnect() is used without a dbUser'(test: Test) { + // WHEN + const role = new Role(stack, 'DBProxyRole', { + assumedBy: new AccountPrincipal(stack.account), + }); + + // THEN + test.throws(() => { + importedDbProxy.grantConnect(role); + }, /For imported Database Proxies, the dbUser is required in grantConnect/); + + test.done(); + }, + }, + + 'new Proxy with a single Secret can use grantConnect() without a dbUser passed'(test: Test) { // GIVEN const cluster = new rds.DatabaseCluster(stack, 'Database', { engine: rds.DatabaseClusterEngine.AURORA, @@ -251,10 +332,29 @@ export = { Effect: 'Allow', Action: 'rds-db:connect', Resource: { - 'Fn::GetAtt': [ - 'ProxyCB0DFB71', - 'DBProxyArn', - ], + 'Fn::Join': ['', [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':rds-db:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':dbuser:', + { + 'Fn::Select': [ + 6, + { + 'Fn::Split': [ + ':', + { 'Fn::GetAtt': ['ProxyCB0DFB71', 'DBProxyArn'] }, + ], + }, + ], + }, + '/{{resolve:secretsmanager:', + { Ref: 'DatabaseSecretAttachmentE5D1B020' }, + ':SecretString:username::}}', + ]], }, }], Version: '2012-10-17', @@ -264,6 +364,35 @@ export = { test.done(); }, + 'new Proxy with multiple Secrets cannot use grantConnect() without a dbUser passed'(test: Test) { + // GIVEN + const cluster = new rds.DatabaseCluster(stack, 'Database', { + engine: rds.DatabaseClusterEngine.AURORA, + instanceProps: { vpc }, + }); + + const proxy = new rds.DatabaseProxy(stack, 'Proxy', { + proxyTarget: rds.ProxyTarget.fromCluster(cluster), + secrets: [ + cluster.secret!, + new secretsmanager.Secret(stack, 'ProxySecret'), + ], + vpc, + }); + + // WHEN + const role = new Role(stack, 'DBProxyRole', { + assumedBy: new AccountPrincipal(stack.account), + }); + + // THEN + test.throws(() => { + proxy.grantConnect(role); + }, /When the Proxy contains multiple Secrets, you must pass a dbUser explicitly to grantConnect/); + + test.done(); + }, + 'DBProxyTargetGroup should have dependency on the proxy targets'(test: Test) { // GIVEN const cluster = new rds.DatabaseCluster(stack, 'cluster', { @@ -294,6 +423,7 @@ export = { 'cluster611F8AFF', 'clusterSecretAttachment69BFCEC4', 'clusterSecretE349B730', + 'clusterSecurityGroupfromproxyProxySecurityGroupA80F0525IndirectPortA13E5F3D', 'clusterSecurityGroupF441DCEA', 'clusterSubnets81E3593F', ], @@ -301,4 +431,4 @@ export = { test.done(); }, -}; +}); diff --git a/packages/@aws-cdk/aws-rds/test/test.serverless-cluster.ts b/packages/@aws-cdk/aws-rds/test/serverless-cluster.test.ts similarity index 99% rename from packages/@aws-cdk/aws-rds/test/test.serverless-cluster.ts rename to packages/@aws-cdk/aws-rds/test/serverless-cluster.test.ts index de3c8a0018beb..257d23d9ffec2 100644 --- a/packages/@aws-cdk/aws-rds/test/test.serverless-cluster.ts +++ b/packages/@aws-cdk/aws-rds/test/serverless-cluster.test.ts @@ -3,10 +3,10 @@ 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 cdk from '@aws-cdk/core'; -import { Test } from 'nodeunit'; +import { nodeunitShim, Test } from 'nodeunit-shim'; import { AuroraPostgresEngineVersion, ServerlessCluster, DatabaseClusterEngine, ParameterGroup, AuroraCapacityUnit, DatabaseSecret } from '../lib'; -export = { +nodeunitShim({ 'can create a Serverless Cluster with Aurora Postgres database engine'(test: Test) { // GIVEN const stack = testStack(); @@ -818,7 +818,7 @@ export = { test.done(); }, -}; +}); function testStack() { diff --git a/packages/@aws-cdk/aws-rds/test/sql-server/test.sql-server.instance-engine.ts b/packages/@aws-cdk/aws-rds/test/sql-server/sql-server.instance-engine.test.ts similarity index 94% rename from packages/@aws-cdk/aws-rds/test/sql-server/test.sql-server.instance-engine.ts rename to packages/@aws-cdk/aws-rds/test/sql-server/sql-server.instance-engine.test.ts index 7f9ecf0311faa..4255298f87d10 100644 --- a/packages/@aws-cdk/aws-rds/test/sql-server/test.sql-server.instance-engine.ts +++ b/packages/@aws-cdk/aws-rds/test/sql-server/sql-server.instance-engine.test.ts @@ -1,9 +1,9 @@ import { expect, haveResourceLike } from '@aws-cdk/assert'; import * as core from '@aws-cdk/core'; -import { Test } from 'nodeunit'; +import { nodeunitShim, Test } from 'nodeunit-shim'; import * as rds from '../../lib'; -export = { +nodeunitShim({ 'SQL Server instance engine': { "has ParameterGroup family ending in '11.0' for major version 11"(test: Test) { const stack = new core.Stack(); @@ -43,4 +43,4 @@ export = { test.done(); }, }, -}; +}); diff --git a/packages/@aws-cdk/aws-rds/test/test.subnet-group.ts b/packages/@aws-cdk/aws-rds/test/subnet-group.test.ts similarity index 97% rename from packages/@aws-cdk/aws-rds/test/test.subnet-group.ts rename to packages/@aws-cdk/aws-rds/test/subnet-group.test.ts index 6d2a71e84bad7..c6c16add591ce 100644 --- a/packages/@aws-cdk/aws-rds/test/test.subnet-group.ts +++ b/packages/@aws-cdk/aws-rds/test/subnet-group.test.ts @@ -1,13 +1,13 @@ import { expect, haveResource } from '@aws-cdk/assert'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as cdk from '@aws-cdk/core'; -import { Test } from 'nodeunit'; +import { nodeunitShim, Test } from 'nodeunit-shim'; import * as rds from '../lib'; let stack: cdk.Stack; let vpc: ec2.IVpc; -export = { +nodeunitShim({ 'setUp'(cb: () => void) { stack = new cdk.Stack(); vpc = new ec2.Vpc(stack, 'VPC'); @@ -95,4 +95,4 @@ export = { test.done(); }, -}; +}); diff --git a/packages/@aws-cdk/aws-redshift/lib/cluster.ts b/packages/@aws-cdk/aws-redshift/lib/cluster.ts index 0add37fbbdff3..a94bfa31b7fb5 100644 --- a/packages/@aws-cdk/aws-redshift/lib/cluster.ts +++ b/packages/@aws-cdk/aws-redshift/lib/cluster.ts @@ -40,6 +40,14 @@ export enum NodeType { * dc2.8xlarge */ DC2_8XLARGE = 'dc2.8xlarge', + /** + * ra3.xlplus + */ + RA3_XLPLUS = 'ra3.xlplus', + /** + * ra3.4xlarge + */ + RA3_4XLARGE = 'ra3.4xlarge', /** * ra3.16xlarge */ diff --git a/packages/@aws-cdk/aws-redshift/package.json b/packages/@aws-cdk/aws-redshift/package.json index cac9210cac25b..cede50f836f54 100644 --- a/packages/@aws-cdk/aws-redshift/package.json +++ b/packages/@aws-cdk/aws-redshift/package.json @@ -114,5 +114,8 @@ "maturity": "experimental", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-resourcegroups/package.json b/packages/@aws-cdk/aws-resourcegroups/package.json index 2616f1f58fe7e..bd1848d6681e9 100644 --- a/packages/@aws-cdk/aws-resourcegroups/package.json +++ b/packages/@aws-cdk/aws-resourcegroups/package.json @@ -91,5 +91,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-robomaker/package.json b/packages/@aws-cdk/aws-robomaker/package.json index 2727122c5e1df..be52d60f6a739 100644 --- a/packages/@aws-cdk/aws-robomaker/package.json +++ b/packages/@aws-cdk/aws-robomaker/package.json @@ -93,5 +93,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-route53-patterns/package.json b/packages/@aws-cdk/aws-route53-patterns/package.json index d71489948d599..b10d3e4ceb714 100644 --- a/packages/@aws-cdk/aws-route53-patterns/package.json +++ b/packages/@aws-cdk/aws-route53-patterns/package.json @@ -106,5 +106,8 @@ "env": { "AWSLINT_BASE_CONSTRUCT": true } + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-route53-targets/package.json b/packages/@aws-cdk/aws-route53-targets/package.json index e8e2d73947f21..08ea2cb9e91f4 100644 --- a/packages/@aws-cdk/aws-route53-targets/package.json +++ b/packages/@aws-cdk/aws-route53-targets/package.json @@ -115,5 +115,8 @@ "env": { "AWSLINT_BASE_CONSTRUCT": true } + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-route53/README.md b/packages/@aws-cdk/aws-route53/README.md index b9f75fb1b9d4e..aa5bf109b7473 100644 --- a/packages/@aws-cdk/aws-route53/README.md +++ b/packages/@aws-cdk/aws-route53/README.md @@ -117,7 +117,7 @@ import * as route53 from '@aws-cdk/aws-route53'; // In the account containing the HostedZone const parentZone = new route53.PublicHostedZone(this, 'HostedZone', { zoneName: 'someexample.com', - crossAccountZoneDelegationPrinciple: new iam.AccountPrincipal('12345678901') + crossAccountZoneDelegationPrincipal: new iam.AccountPrincipal('12345678901') }); // In this account diff --git a/packages/@aws-cdk/aws-route53/package.json b/packages/@aws-cdk/aws-route53/package.json index 70aa8fc17f4a8..0501fc2b8d654 100644 --- a/packages/@aws-cdk/aws-route53/package.json +++ b/packages/@aws-cdk/aws-route53/package.json @@ -73,11 +73,11 @@ "devDependencies": { "@aws-cdk/assert": "0.0.0", "@types/nodeunit": "^0.0.31", - "aws-sdk": "^2.830.0", + "aws-sdk": "^2.845.0", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "cfn2ts": "0.0.0", - "jest": "^26.6.0", + "jest": "^26.6.3", "nodeunit-shim": "0.0.0", "pkglint": "0.0.0" }, @@ -129,5 +129,8 @@ "awscdkio": { "announce": false }, - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-route53/test/hosted-zone.test.ts b/packages/@aws-cdk/aws-route53/test/hosted-zone.test.ts index 6e7810824c2bf..824e97cc93278 100644 --- a/packages/@aws-cdk/aws-route53/test/hosted-zone.test.ts +++ b/packages/@aws-cdk/aws-route53/test/hosted-zone.test.ts @@ -63,7 +63,7 @@ nodeunitShim({ test.done(); }, - 'with crossAccountZoneDelegationPrinciple'(test: Test) { + 'with crossAccountZoneDelegationPrincipal'(test: Test) { // GIVEN const stack = new cdk.Stack(undefined, 'TestStack', { env: { account: '123456789012', region: 'us-east-1' }, diff --git a/packages/@aws-cdk/aws-route53resolver/package.json b/packages/@aws-cdk/aws-route53resolver/package.json index 2d7c49574f4a7..79142ad18b7ef 100644 --- a/packages/@aws-cdk/aws-route53resolver/package.json +++ b/packages/@aws-cdk/aws-route53resolver/package.json @@ -92,5 +92,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-s3-assets/README.md b/packages/@aws-cdk/aws-s3-assets/README.md index aab4c46d9c44d..f2583b7c10a24 100644 --- a/packages/@aws-cdk/aws-s3-assets/README.md +++ b/packages/@aws-cdk/aws-s3-assets/README.md @@ -124,6 +124,27 @@ new assets.Asset(this, 'BundledAsset', { Although optional, it's recommended to provide a local bundling method which can greatly improve performance. +If the bundling output contains a single archive file (zip or jar) it will be +uploaded to S3 as-is and will not be zipped. Otherwise the contents of the +output directory will be zipped and the zip file will be uploaded to S3. This +is the default behavior for `bundling.outputType` (`BundlingOutput.AUTO_DISCOVER`). + +Use `BundlingOutput.NOT_ARCHIVED` if the bundling output must always be zipped: + +```ts +const asset = new assets.Asset(this, 'BundledAsset', { + path: '/path/to/asset', + bundling: { + image: BundlingDockerImage.fromRegistry('alpine'), + command: ['command-that-produces-an-archive.sh'], + outputType: BundlingOutput.NOT_ARCHIVED, // Bundling output will be zipped even though it produces a single archive file. + }, +}); +``` + +Use `BundlingOutput.ARCHIVED` if the bundling output contains a single archive file and +you don't want it to be zippped. + ## CloudFormation Resource Metadata > NOTE: This section is relevant for authors of AWS Resource Constructs. diff --git a/packages/@aws-cdk/aws-s3-assets/lib/asset.ts b/packages/@aws-cdk/aws-s3-assets/lib/asset.ts index 938778d1381f4..510834a61c634 100644 --- a/packages/@aws-cdk/aws-s3-assets/lib/asset.ts +++ b/packages/@aws-cdk/aws-s3-assets/lib/asset.ts @@ -1,4 +1,3 @@ -import * as fs from 'fs'; import * as path from 'path'; import * as assets from '@aws-cdk/assets'; import * as iam from '@aws-cdk/aws-iam'; @@ -13,8 +12,6 @@ import { toSymlinkFollow } from './compat'; // eslint-disable-next-line no-duplicate-imports, import/order import { Construct as CoreConstruct } from '@aws-cdk/core'; -const ARCHIVE_EXTENSIONS = ['.zip', '.jar']; - export interface AssetOptions extends assets.CopyOptions, cdk.AssetOptions { /** * A list of principals that should be able to read this asset from S3. @@ -139,17 +136,12 @@ export class Asset extends CoreConstruct implements cdk.IAsset { this.assetPath = staging.relativeStagedPath(stack); - const packaging = determinePackaging(staging.sourcePath); - - this.isFile = packaging === cdk.FileAssetPackaging.FILE; + this.isFile = staging.packaging === cdk.FileAssetPackaging.FILE; - // sets isZipArchive based on the type of packaging and file extension - this.isZipArchive = packaging === cdk.FileAssetPackaging.ZIP_DIRECTORY - ? true - : ARCHIVE_EXTENSIONS.some(ext => staging.sourcePath.toLowerCase().endsWith(ext)); + this.isZipArchive = staging.isArchive; const location = stack.synthesizer.addFileAsset({ - packaging, + packaging: staging.packaging, sourceHash: this.sourceHash, fileName: this.assetPath, }); @@ -210,19 +202,3 @@ export class Asset extends CoreConstruct implements cdk.IAsset { this.bucket.grantRead(grantee); } } - -function determinePackaging(assetPath: string): cdk.FileAssetPackaging { - if (!fs.existsSync(assetPath)) { - throw new Error(`Cannot find asset at ${assetPath}`); - } - - if (fs.statSync(assetPath).isDirectory()) { - return cdk.FileAssetPackaging.ZIP_DIRECTORY; - } - - if (fs.statSync(assetPath).isFile()) { - return cdk.FileAssetPackaging.FILE; - } - - throw new Error(`Asset ${assetPath} is expected to be either a directory or a regular file`); -} diff --git a/packages/@aws-cdk/aws-s3-assets/package.json b/packages/@aws-cdk/aws-s3-assets/package.json index 9f9d1e968fcbc..6d03905f64f85 100644 --- a/packages/@aws-cdk/aws-s3-assets/package.json +++ b/packages/@aws-cdk/aws-s3-assets/package.json @@ -107,5 +107,8 @@ }, "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-s3-deployment/package.json b/packages/@aws-cdk/aws-s3-deployment/package.json index a035fdb80e343..fadd40d30149a 100644 --- a/packages/@aws-cdk/aws-s3-deployment/package.json +++ b/packages/@aws-cdk/aws-s3-deployment/package.json @@ -78,7 +78,7 @@ "license": "Apache-2.0", "devDependencies": { "@aws-cdk/assert": "0.0.0", - "@types/jest": "^26.0.15", + "@types/jest": "^26.0.20", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "jest": "^26.6.3", @@ -143,5 +143,8 @@ }, "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-s3-notifications/package.json b/packages/@aws-cdk/aws-s3-notifications/package.json index afa1b27a1c80c..00d38a536ca6a 100644 --- a/packages/@aws-cdk/aws-s3-notifications/package.json +++ b/packages/@aws-cdk/aws-s3-notifications/package.json @@ -100,5 +100,8 @@ "env": { "AWSLINT_BASE_CONSTRUCT": true } + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-s3-notifications/test/integ.notifications.expected.json b/packages/@aws-cdk/aws-s3-notifications/test/integ.notifications.expected.json index e28b703234d0d..4e12e4dd9badf 100644 --- a/packages/@aws-cdk/aws-s3-notifications/test/integ.notifications.expected.json +++ b/packages/@aws-cdk/aws-s3-notifications/test/integ.notifications.expected.json @@ -211,7 +211,7 @@ "Properties": { "Description": "AWS CloudFormation handler for \"Custom::S3BucketNotifications\" resources (@aws-cdk/aws-s3)", "Code": { - "ZipFile": "exports.handler = (event, context) => {\n // eslint-disable-next-line @typescript-eslint/no-require-imports, import/no-extraneous-dependencies\n const s3 = new (require('aws-sdk').S3)();\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const https = require('https');\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const url = require('url');\n log(JSON.stringify(event, undefined, 2));\n const props = event.ResourceProperties;\n if (event.RequestType === 'Delete') {\n props.NotificationConfiguration = {}; // this is how you clean out notifications\n }\n const req = {\n Bucket: props.BucketName,\n NotificationConfiguration: props.NotificationConfiguration,\n };\n return s3.putBucketNotificationConfiguration(req, (err, data) => {\n log({ err, data });\n if (err) {\n return submitResponse('FAILED', err.message + `\\nMore information in CloudWatch Log Stream: ${context.logStreamName}`);\n }\n else {\n return submitResponse('SUCCESS');\n }\n });\n function log(obj) {\n console.error(event.RequestId, event.StackId, event.LogicalResourceId, obj);\n }\n // eslint-disable-next-line max-len\n // adapted from https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html#cfn-lambda-function-code-cfnresponsemodule\n // to allow sending an error messge as a reason.\n function submitResponse(responseStatus, reason) {\n const responseBody = JSON.stringify({\n Status: responseStatus,\n Reason: reason || 'See the details in CloudWatch Log Stream: ' + context.logStreamName,\n PhysicalResourceId: event.PhysicalResourceId || event.LogicalResourceId,\n StackId: event.StackId,\n RequestId: event.RequestId,\n LogicalResourceId: event.LogicalResourceId,\n NoEcho: false,\n });\n log({ responseBody });\n const parsedUrl = url.parse(event.ResponseURL);\n const options = {\n hostname: parsedUrl.hostname,\n port: 443,\n path: parsedUrl.path,\n method: 'PUT',\n headers: {\n 'content-type': '',\n 'content-length': responseBody.length,\n },\n };\n const request = https.request(options, (r) => {\n log({ statusCode: r.statusCode, statusMessage: r.statusMessage });\n context.done();\n });\n request.on('error', (error) => {\n log({ sendError: error });\n context.done();\n });\n request.write(responseBody);\n request.end();\n }\n};" + "ZipFile": "exports.handler = (event, context) => {\n // eslint-disable-next-line @typescript-eslint/no-require-imports, import/no-extraneous-dependencies\n const s3 = new (require('aws-sdk').S3)();\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const https = require('https');\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const url = require('url');\n log(JSON.stringify(event, undefined, 2));\n const props = event.ResourceProperties;\n if (event.RequestType === 'Delete') {\n props.NotificationConfiguration = {}; // this is how you clean out notifications\n }\n const req = {\n Bucket: props.BucketName,\n NotificationConfiguration: props.NotificationConfiguration,\n };\n return s3.putBucketNotificationConfiguration(req, (err, data) => {\n log({ err, data });\n if (err) {\n return submitResponse('FAILED', err.message + `\\nMore information in CloudWatch Log Stream: ${context.logStreamName}`);\n }\n else {\n return submitResponse('SUCCESS');\n }\n });\n function log(obj) {\n console.error(event.RequestId, event.StackId, event.LogicalResourceId, obj);\n }\n // eslint-disable-next-line max-len\n // adapted from https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html#cfn-lambda-function-code-cfnresponsemodule\n // to allow sending an error message as a reason.\n function submitResponse(responseStatus, reason) {\n const responseBody = JSON.stringify({\n Status: responseStatus,\n Reason: reason || 'See the details in CloudWatch Log Stream: ' + context.logStreamName,\n PhysicalResourceId: event.PhysicalResourceId || event.LogicalResourceId,\n StackId: event.StackId,\n RequestId: event.RequestId,\n LogicalResourceId: event.LogicalResourceId,\n NoEcho: false,\n });\n log({ responseBody });\n const parsedUrl = url.parse(event.ResponseURL);\n const options = {\n hostname: parsedUrl.hostname,\n port: 443,\n path: parsedUrl.path,\n method: 'PUT',\n headers: {\n 'content-type': '',\n 'content-length': responseBody.length,\n },\n };\n const request = https.request(options, (r) => {\n log({ statusCode: r.statusCode, statusMessage: r.statusMessage });\n context.done();\n });\n request.on('error', (error) => {\n log({ sendError: error });\n context.done();\n });\n request.write(responseBody);\n request.end();\n }\n};" }, "Handler": "index.handler", "Role": { diff --git a/packages/@aws-cdk/aws-s3-notifications/test/lambda/integ.bucket-notifications.expected.json b/packages/@aws-cdk/aws-s3-notifications/test/lambda/integ.bucket-notifications.expected.json index 98ddf87498738..8a1c134ff2651 100644 --- a/packages/@aws-cdk/aws-s3-notifications/test/lambda/integ.bucket-notifications.expected.json +++ b/packages/@aws-cdk/aws-s3-notifications/test/lambda/integ.bucket-notifications.expected.json @@ -235,7 +235,7 @@ "Properties": { "Description": "AWS CloudFormation handler for \"Custom::S3BucketNotifications\" resources (@aws-cdk/aws-s3)", "Code": { - "ZipFile": "exports.handler = (event, context) => {\n // eslint-disable-next-line @typescript-eslint/no-require-imports, import/no-extraneous-dependencies\n const s3 = new (require('aws-sdk').S3)();\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const https = require('https');\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const url = require('url');\n log(JSON.stringify(event, undefined, 2));\n const props = event.ResourceProperties;\n if (event.RequestType === 'Delete') {\n props.NotificationConfiguration = {}; // this is how you clean out notifications\n }\n const req = {\n Bucket: props.BucketName,\n NotificationConfiguration: props.NotificationConfiguration,\n };\n return s3.putBucketNotificationConfiguration(req, (err, data) => {\n log({ err, data });\n if (err) {\n return submitResponse('FAILED', err.message + `\\nMore information in CloudWatch Log Stream: ${context.logStreamName}`);\n }\n else {\n return submitResponse('SUCCESS');\n }\n });\n function log(obj) {\n console.error(event.RequestId, event.StackId, event.LogicalResourceId, obj);\n }\n // eslint-disable-next-line max-len\n // adapted from https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html#cfn-lambda-function-code-cfnresponsemodule\n // to allow sending an error messge as a reason.\n function submitResponse(responseStatus, reason) {\n const responseBody = JSON.stringify({\n Status: responseStatus,\n Reason: reason || 'See the details in CloudWatch Log Stream: ' + context.logStreamName,\n PhysicalResourceId: event.PhysicalResourceId || event.LogicalResourceId,\n StackId: event.StackId,\n RequestId: event.RequestId,\n LogicalResourceId: event.LogicalResourceId,\n NoEcho: false,\n });\n log({ responseBody });\n const parsedUrl = url.parse(event.ResponseURL);\n const options = {\n hostname: parsedUrl.hostname,\n port: 443,\n path: parsedUrl.path,\n method: 'PUT',\n headers: {\n 'content-type': '',\n 'content-length': responseBody.length,\n },\n };\n const request = https.request(options, (r) => {\n log({ statusCode: r.statusCode, statusMessage: r.statusMessage });\n context.done();\n });\n request.on('error', (error) => {\n log({ sendError: error });\n context.done();\n });\n request.write(responseBody);\n request.end();\n }\n};" + "ZipFile": "exports.handler = (event, context) => {\n // eslint-disable-next-line @typescript-eslint/no-require-imports, import/no-extraneous-dependencies\n const s3 = new (require('aws-sdk').S3)();\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const https = require('https');\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const url = require('url');\n log(JSON.stringify(event, undefined, 2));\n const props = event.ResourceProperties;\n if (event.RequestType === 'Delete') {\n props.NotificationConfiguration = {}; // this is how you clean out notifications\n }\n const req = {\n Bucket: props.BucketName,\n NotificationConfiguration: props.NotificationConfiguration,\n };\n return s3.putBucketNotificationConfiguration(req, (err, data) => {\n log({ err, data });\n if (err) {\n return submitResponse('FAILED', err.message + `\\nMore information in CloudWatch Log Stream: ${context.logStreamName}`);\n }\n else {\n return submitResponse('SUCCESS');\n }\n });\n function log(obj) {\n console.error(event.RequestId, event.StackId, event.LogicalResourceId, obj);\n }\n // eslint-disable-next-line max-len\n // adapted from https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html#cfn-lambda-function-code-cfnresponsemodule\n // to allow sending an error message as a reason.\n function submitResponse(responseStatus, reason) {\n const responseBody = JSON.stringify({\n Status: responseStatus,\n Reason: reason || 'See the details in CloudWatch Log Stream: ' + context.logStreamName,\n PhysicalResourceId: event.PhysicalResourceId || event.LogicalResourceId,\n StackId: event.StackId,\n RequestId: event.RequestId,\n LogicalResourceId: event.LogicalResourceId,\n NoEcho: false,\n });\n log({ responseBody });\n const parsedUrl = url.parse(event.ResponseURL);\n const options = {\n hostname: parsedUrl.hostname,\n port: 443,\n path: parsedUrl.path,\n method: 'PUT',\n headers: {\n 'content-type': '',\n 'content-length': responseBody.length,\n },\n };\n const request = https.request(options, (r) => {\n log({ statusCode: r.statusCode, statusMessage: r.statusMessage });\n context.done();\n });\n request.on('error', (error) => {\n log({ sendError: error });\n context.done();\n });\n request.write(responseBody);\n request.end();\n }\n};" }, "Handler": "index.handler", "Role": { diff --git a/packages/@aws-cdk/aws-s3-notifications/test/queue.test.ts b/packages/@aws-cdk/aws-s3-notifications/test/queue.test.ts index 5e384d2377318..9cc4dad9712c7 100644 --- a/packages/@aws-cdk/aws-s3-notifications/test/queue.test.ts +++ b/packages/@aws-cdk/aws-s3-notifications/test/queue.test.ts @@ -1,4 +1,4 @@ -import { SynthUtils } from '@aws-cdk/assert'; +import { arrayWith, SynthUtils } from '@aws-cdk/assert'; import '@aws-cdk/assert/jest'; import * as s3 from '@aws-cdk/aws-s3'; import * as sqs from '@aws-cdk/aws-sqs'; @@ -77,65 +77,19 @@ test('if the queue is encrypted with a custom kms key, the key resource policy i bucket.addObjectCreatedNotification(new notif.SqsDestination(queue)); - expect(stack).toHaveResource('AWS::KMS::Key', { + expect(stack).toHaveResourceLike('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:Decrypt', - 'kms:Encrypt', - 'kms:ReEncrypt*', - 'kms:GenerateDataKey*', - ], - Condition: { - ArnLike: { - 'aws:SourceArn': { 'Fn::GetAtt': ['Bucket83908E77', 'Arn'] }, - }, - }, - Effect: 'Allow', - Principal: { - Service: 's3.amazonaws.com', - }, - Resource: '*', + Statement: arrayWith({ + Action: [ + 'kms:GenerateDataKey*', + 'kms:Decrypt', + ], + Effect: 'Allow', + Principal: { + Service: 's3.amazonaws.com', }, - { - Action: [ - 'kms:GenerateDataKey*', - 'kms:Decrypt', - ], - Effect: 'Allow', - Principal: { - Service: 's3.amazonaws.com', - }, - Resource: '*', - }, - ], - Version: '2012-10-17', + Resource: '*', + }), }, - Description: 'Created by Default/Queue', }); }); diff --git a/packages/@aws-cdk/aws-s3-notifications/test/sns/integ.sns-bucket-notifications.expected.json b/packages/@aws-cdk/aws-s3-notifications/test/sns/integ.sns-bucket-notifications.expected.json index 3ee1f5979fbdf..0aabfdd9f8e19 100644 --- a/packages/@aws-cdk/aws-s3-notifications/test/sns/integ.sns-bucket-notifications.expected.json +++ b/packages/@aws-cdk/aws-s3-notifications/test/sns/integ.sns-bucket-notifications.expected.json @@ -194,7 +194,7 @@ "Properties": { "Description": "AWS CloudFormation handler for \"Custom::S3BucketNotifications\" resources (@aws-cdk/aws-s3)", "Code": { - "ZipFile": "exports.handler = (event, context) => {\n // eslint-disable-next-line @typescript-eslint/no-require-imports, import/no-extraneous-dependencies\n const s3 = new (require('aws-sdk').S3)();\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const https = require('https');\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const url = require('url');\n log(JSON.stringify(event, undefined, 2));\n const props = event.ResourceProperties;\n if (event.RequestType === 'Delete') {\n props.NotificationConfiguration = {}; // this is how you clean out notifications\n }\n const req = {\n Bucket: props.BucketName,\n NotificationConfiguration: props.NotificationConfiguration,\n };\n return s3.putBucketNotificationConfiguration(req, (err, data) => {\n log({ err, data });\n if (err) {\n return submitResponse('FAILED', err.message + `\\nMore information in CloudWatch Log Stream: ${context.logStreamName}`);\n }\n else {\n return submitResponse('SUCCESS');\n }\n });\n function log(obj) {\n console.error(event.RequestId, event.StackId, event.LogicalResourceId, obj);\n }\n // eslint-disable-next-line max-len\n // adapted from https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html#cfn-lambda-function-code-cfnresponsemodule\n // to allow sending an error messge as a reason.\n function submitResponse(responseStatus, reason) {\n const responseBody = JSON.stringify({\n Status: responseStatus,\n Reason: reason || 'See the details in CloudWatch Log Stream: ' + context.logStreamName,\n PhysicalResourceId: event.PhysicalResourceId || event.LogicalResourceId,\n StackId: event.StackId,\n RequestId: event.RequestId,\n LogicalResourceId: event.LogicalResourceId,\n NoEcho: false,\n });\n log({ responseBody });\n const parsedUrl = url.parse(event.ResponseURL);\n const options = {\n hostname: parsedUrl.hostname,\n port: 443,\n path: parsedUrl.path,\n method: 'PUT',\n headers: {\n 'content-type': '',\n 'content-length': responseBody.length,\n },\n };\n const request = https.request(options, (r) => {\n log({ statusCode: r.statusCode, statusMessage: r.statusMessage });\n context.done();\n });\n request.on('error', (error) => {\n log({ sendError: error });\n context.done();\n });\n request.write(responseBody);\n request.end();\n }\n};" + "ZipFile": "exports.handler = (event, context) => {\n // eslint-disable-next-line @typescript-eslint/no-require-imports, import/no-extraneous-dependencies\n const s3 = new (require('aws-sdk').S3)();\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const https = require('https');\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const url = require('url');\n log(JSON.stringify(event, undefined, 2));\n const props = event.ResourceProperties;\n if (event.RequestType === 'Delete') {\n props.NotificationConfiguration = {}; // this is how you clean out notifications\n }\n const req = {\n Bucket: props.BucketName,\n NotificationConfiguration: props.NotificationConfiguration,\n };\n return s3.putBucketNotificationConfiguration(req, (err, data) => {\n log({ err, data });\n if (err) {\n return submitResponse('FAILED', err.message + `\\nMore information in CloudWatch Log Stream: ${context.logStreamName}`);\n }\n else {\n return submitResponse('SUCCESS');\n }\n });\n function log(obj) {\n console.error(event.RequestId, event.StackId, event.LogicalResourceId, obj);\n }\n // eslint-disable-next-line max-len\n // adapted from https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html#cfn-lambda-function-code-cfnresponsemodule\n // to allow sending an error message as a reason.\n function submitResponse(responseStatus, reason) {\n const responseBody = JSON.stringify({\n Status: responseStatus,\n Reason: reason || 'See the details in CloudWatch Log Stream: ' + context.logStreamName,\n PhysicalResourceId: event.PhysicalResourceId || event.LogicalResourceId,\n StackId: event.StackId,\n RequestId: event.RequestId,\n LogicalResourceId: event.LogicalResourceId,\n NoEcho: false,\n });\n log({ responseBody });\n const parsedUrl = url.parse(event.ResponseURL);\n const options = {\n hostname: parsedUrl.hostname,\n port: 443,\n path: parsedUrl.path,\n method: 'PUT',\n headers: {\n 'content-type': '',\n 'content-length': responseBody.length,\n },\n };\n const request = https.request(options, (r) => {\n log({ statusCode: r.statusCode, statusMessage: r.statusMessage });\n context.done();\n });\n request.on('error', (error) => {\n log({ sendError: error });\n context.done();\n });\n request.write(responseBody);\n request.end();\n }\n};" }, "Handler": "index.handler", "Role": { diff --git a/packages/@aws-cdk/aws-s3-notifications/test/sqs/integ.bucket-notifications.expected.json b/packages/@aws-cdk/aws-s3-notifications/test/sqs/integ.bucket-notifications.expected.json index b4d969a3c6d42..dee4dafe9e74b 100644 --- a/packages/@aws-cdk/aws-s3-notifications/test/sqs/integ.bucket-notifications.expected.json +++ b/packages/@aws-cdk/aws-s3-notifications/test/sqs/integ.bucket-notifications.expected.json @@ -181,7 +181,7 @@ "Properties": { "Description": "AWS CloudFormation handler for \"Custom::S3BucketNotifications\" resources (@aws-cdk/aws-s3)", "Code": { - "ZipFile": "exports.handler = (event, context) => {\n // eslint-disable-next-line @typescript-eslint/no-require-imports, import/no-extraneous-dependencies\n const s3 = new (require('aws-sdk').S3)();\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const https = require('https');\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const url = require('url');\n log(JSON.stringify(event, undefined, 2));\n const props = event.ResourceProperties;\n if (event.RequestType === 'Delete') {\n props.NotificationConfiguration = {}; // this is how you clean out notifications\n }\n const req = {\n Bucket: props.BucketName,\n NotificationConfiguration: props.NotificationConfiguration,\n };\n return s3.putBucketNotificationConfiguration(req, (err, data) => {\n log({ err, data });\n if (err) {\n return submitResponse('FAILED', err.message + `\\nMore information in CloudWatch Log Stream: ${context.logStreamName}`);\n }\n else {\n return submitResponse('SUCCESS');\n }\n });\n function log(obj) {\n console.error(event.RequestId, event.StackId, event.LogicalResourceId, obj);\n }\n // eslint-disable-next-line max-len\n // adapted from https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html#cfn-lambda-function-code-cfnresponsemodule\n // to allow sending an error messge as a reason.\n function submitResponse(responseStatus, reason) {\n const responseBody = JSON.stringify({\n Status: responseStatus,\n Reason: reason || 'See the details in CloudWatch Log Stream: ' + context.logStreamName,\n PhysicalResourceId: event.PhysicalResourceId || event.LogicalResourceId,\n StackId: event.StackId,\n RequestId: event.RequestId,\n LogicalResourceId: event.LogicalResourceId,\n NoEcho: false,\n });\n log({ responseBody });\n const parsedUrl = url.parse(event.ResponseURL);\n const options = {\n hostname: parsedUrl.hostname,\n port: 443,\n path: parsedUrl.path,\n method: 'PUT',\n headers: {\n 'content-type': '',\n 'content-length': responseBody.length,\n },\n };\n const request = https.request(options, (r) => {\n log({ statusCode: r.statusCode, statusMessage: r.statusMessage });\n context.done();\n });\n request.on('error', (error) => {\n log({ sendError: error });\n context.done();\n });\n request.write(responseBody);\n request.end();\n }\n};" + "ZipFile": "exports.handler = (event, context) => {\n // eslint-disable-next-line @typescript-eslint/no-require-imports, import/no-extraneous-dependencies\n const s3 = new (require('aws-sdk').S3)();\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const https = require('https');\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const url = require('url');\n log(JSON.stringify(event, undefined, 2));\n const props = event.ResourceProperties;\n if (event.RequestType === 'Delete') {\n props.NotificationConfiguration = {}; // this is how you clean out notifications\n }\n const req = {\n Bucket: props.BucketName,\n NotificationConfiguration: props.NotificationConfiguration,\n };\n return s3.putBucketNotificationConfiguration(req, (err, data) => {\n log({ err, data });\n if (err) {\n return submitResponse('FAILED', err.message + `\\nMore information in CloudWatch Log Stream: ${context.logStreamName}`);\n }\n else {\n return submitResponse('SUCCESS');\n }\n });\n function log(obj) {\n console.error(event.RequestId, event.StackId, event.LogicalResourceId, obj);\n }\n // eslint-disable-next-line max-len\n // adapted from https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html#cfn-lambda-function-code-cfnresponsemodule\n // to allow sending an error message as a reason.\n function submitResponse(responseStatus, reason) {\n const responseBody = JSON.stringify({\n Status: responseStatus,\n Reason: reason || 'See the details in CloudWatch Log Stream: ' + context.logStreamName,\n PhysicalResourceId: event.PhysicalResourceId || event.LogicalResourceId,\n StackId: event.StackId,\n RequestId: event.RequestId,\n LogicalResourceId: event.LogicalResourceId,\n NoEcho: false,\n });\n log({ responseBody });\n const parsedUrl = url.parse(event.ResponseURL);\n const options = {\n hostname: parsedUrl.hostname,\n port: 443,\n path: parsedUrl.path,\n method: 'PUT',\n headers: {\n 'content-type': '',\n 'content-length': responseBody.length,\n },\n };\n const request = https.request(options, (r) => {\n log({ statusCode: r.statusCode, statusMessage: r.statusMessage });\n context.done();\n });\n request.on('error', (error) => {\n log({ sendError: error });\n context.done();\n });\n request.write(responseBody);\n request.end();\n }\n};" }, "Handler": "index.handler", "Role": { @@ -252,23 +252,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { diff --git a/packages/@aws-cdk/aws-s3/lib/bucket.ts b/packages/@aws-cdk/aws-s3/lib/bucket.ts index 525b22f6afd1d..859f774565a93 100644 --- a/packages/@aws-cdk/aws-s3/lib/bucket.ts +++ b/packages/@aws-cdk/aws-s3/lib/bucket.ts @@ -147,6 +147,14 @@ export interface IBucket extends IResource { * If encryption is used, permission to use the key to encrypt the contents * of written files will also be granted to the same principal. * + * Before CDK version 1.85.0, this method granted the `s3:PutObject*` permission that included `s3:PutObjectAcl`, + * which could be used to grant read/write object access to IAM principals in other accounts. + * If you want to get rid of that behavior, update your CDK version to 1.85.0 or later, + * and make sure the `@aws-cdk/aws-s3:grantWriteWithoutAcl` feature flag is set to `true` + * in the `context` key of your cdk.json file. + * If you've already updated, but still need the principal to have permissions to modify the ACLs, + * use the {@link grantPutAcl} method. + * * @param identity The principal * @param objectsKeyPattern Restrict the permission to a certain key pattern (default '*') */ @@ -190,6 +198,14 @@ export interface IBucket extends IResource { * If an encryption key is used, permission to use the key for * encrypt/decrypt will also be granted. * + * Before CDK version 1.85.0, this method granted the `s3:PutObject*` permission that included `s3:PutObjectAcl`, + * which could be used to grant read/write object access to IAM principals in other accounts. + * If you want to get rid of that behavior, update your CDK version to 1.85.0 or later, + * and make sure the `@aws-cdk/aws-s3:grantWriteWithoutAcl` feature flag is set to `true` + * in the `context` key of your cdk.json file. + * If you've already updated, but still need the principal to have permissions to modify the ACLs, + * use the {@link grantPutAcl} method. + * * @param identity The principal * @param objectsKeyPattern Restrict the permission to a certain key pattern (default '*') */ @@ -587,15 +603,6 @@ abstract class BucketBase extends Resource implements IBucket { this.arnForObjects(objectsKeyPattern)); } - /** - * Grant write permissions to this bucket to an IAM principal. - * - * If encryption is used, permission to use the key to encrypt the contents - * of written files will also be granted to the same principal. - * - * @param identity The principal - * @param objectsKeyPattern Restrict the permission to a certain key pattern (default '*') - */ public grantWrite(identity: iam.IGrantable, objectsKeyPattern: any = '*') { return this.grant(identity, this.writeActions, perms.KEY_WRITE_ACTIONS, this.bucketArn, @@ -632,16 +639,6 @@ abstract class BucketBase extends Resource implements IBucket { this.arnForObjects(objectsKeyPattern)); } - /** - * Grants read/write permissions for this bucket and it's contents to an IAM - * principal (Role/Group/User). - * - * If an encryption key is used, permission to use the key for - * encrypt/decrypt will also be granted. - * - * @param identity The principal - * @param objectsKeyPattern Restrict the permission to a certain key pattern (default '*') - */ public grantReadWrite(identity: iam.IGrantable, objectsKeyPattern: any = '*') { const bucketActions = perms.BUCKET_READ_ACTIONS.concat(this.writeActions); // we need unique permissions because some permissions are common between read and write key actions diff --git a/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource-handler.ts b/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource-handler.ts index cd7427a5cbc6f..3f07a0d5b7bdf 100644 --- a/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource-handler.ts +++ b/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource-handler.ts @@ -138,7 +138,7 @@ const handler = (event: any, context: any) => { // eslint-disable-next-line max-len // adapted from https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html#cfn-lambda-function-code-cfnresponsemodule - // to allow sending an error messge as a reason. + // to allow sending an error message as a reason. function submitResponse(responseStatus: string, reason?: string) { const responseBody = JSON.stringify({ Status: responseStatus, diff --git a/packages/@aws-cdk/aws-s3/package.json b/packages/@aws-cdk/aws-s3/package.json index b91cd909efb56..23d3345e1bfd5 100644 --- a/packages/@aws-cdk/aws-s3/package.json +++ b/packages/@aws-cdk/aws-s3/package.json @@ -151,5 +151,8 @@ "awscdkio": { "announce": false }, - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-s3/test/bucket.test.ts b/packages/@aws-cdk/aws-s3/test/bucket.test.ts index adf4858bae8c8..b54957f32d500 100644 --- a/packages/@aws-cdk/aws-s3/test/bucket.test.ts +++ b/packages/@aws-cdk/aws-s3/test/bucket.test.ts @@ -5,6 +5,7 @@ import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; import * as cdk from '@aws-cdk/core'; import * as cxapi from '@aws-cdk/cx-api'; +import { testLegacyBehavior } from 'cdk-build-tools/lib/feature-flag'; import * as s3 from '../lib'; // to make it easy to copy & paste from output: @@ -254,82 +255,23 @@ describe('bucket', () => { new s3.Bucket(stack, 'MyBucket', { encryptionKey, encryption: s3.BucketEncryption.KMS }); - expect(stack).toMatchTemplate({ - 'Resources': { - 'MyKey6AB29FA6': { - 'Type': 'AWS::KMS::Key', - 'Properties': { - 'Description': 'hello, world', - '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': '*', - }, - ], - 'Version': '2012-10-17', - }, - }, - 'DeletionPolicy': 'Retain', - 'UpdateReplacePolicy': 'Retain', - }, - 'MyBucketF68F3FF0': { - 'Type': 'AWS::S3::Bucket', - 'Properties': { - 'BucketEncryption': { - 'ServerSideEncryptionConfiguration': [ - { - 'ServerSideEncryptionByDefault': { - 'KMSMasterKeyID': { - 'Fn::GetAtt': [ - 'MyKey6AB29FA6', - 'Arn', - ], - }, - 'SSEAlgorithm': 'aws:kms', - }, - }, - ], + expect(stack).toHaveResource('AWS::KMS::Key'); + + expect(stack).toHaveResource('AWS::S3::Bucket', { + 'BucketEncryption': { + 'ServerSideEncryptionConfiguration': [ + { + 'ServerSideEncryptionByDefault': { + 'KMSMasterKeyID': { + 'Fn::GetAtt': [ + 'MyKey6AB29FA6', + 'Arn', + ], + }, + 'SSEAlgorithm': 'aws:kms', }, }, - 'DeletionPolicy': 'Retain', - 'UpdateReplacePolicy': 'Retain', - }, + ], }, }); @@ -918,23 +860,9 @@ describe('bucket', () => { }, }); - expect(stack).toHaveResource('AWS::KMS::Key', { + expect(stack).toHaveResourceLike('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': '*', - }, + 'Statement': arrayWith( { 'Action': ['kms:Decrypt', 'kms:DescribeKey'], 'Effect': 'Allow', @@ -942,7 +870,7 @@ describe('bucket', () => { 'Principal': '*', 'Condition': { 'StringEquals': { 'aws:PrincipalOrgID': 'o-1234' } }, }, - ], + ), 'Version': '2012-10-17', }, @@ -951,8 +879,8 @@ describe('bucket', () => { }); - test('if an encryption key is included, encrypt/decrypt permissions are also added both ways', () => { - const stack = new cdk.Stack(); + testLegacyBehavior('if an encryption key is included, encrypt/decrypt permissions are also added both ways', cdk.App, (app) => { + const stack = new cdk.Stack(app); const bucket = new s3.Bucket(stack, 'MyBucket', { encryption: s3.BucketEncryption.KMS }); const user = new iam.User(stack, 'MyUser'); bucket.grantReadWrite(user); @@ -1122,8 +1050,6 @@ describe('bucket', () => { }, }, }); - - }); test('does not grant PutObjectAcl when the S3_GRANT_WRITE_WITHOUT_ACL feature is enabled', () => { @@ -1175,165 +1101,63 @@ describe('bucket', () => { const user = new iam.User(stack, 'MyUser'); bucket.grantWrite(user); - expect(stack).toMatchTemplate({ - 'Resources': { - 'MyBucketKeyC17130CF': { - 'Type': 'AWS::KMS::Key', - 'Properties': { - '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:Encrypt', - 'kms:ReEncrypt*', - 'kms:GenerateDataKey*', - 'kms:Decrypt', - ], - 'Effect': 'Allow', - 'Principal': { - 'AWS': { - 'Fn::GetAtt': [ - 'MyUserDC45028B', - 'Arn', - ], - }, - }, - 'Resource': '*', - }, - ], - 'Version': '2012-10-17', - }, - 'Description': 'Created by Default/MyBucket', - }, - 'UpdateReplacePolicy': 'Retain', - 'DeletionPolicy': 'Retain', - }, - 'MyBucketF68F3FF0': { - 'Type': 'AWS::S3::Bucket', - 'Properties': { - 'BucketEncryption': { - 'ServerSideEncryptionConfiguration': [ - { - 'ServerSideEncryptionByDefault': { - 'KMSMasterKeyID': { - 'Fn::GetAtt': [ - 'MyBucketKeyC17130CF', - 'Arn', - ], - }, - 'SSEAlgorithm': 'aws:kms', - }, - }, - ], - }, - }, - 'UpdateReplacePolicy': 'Retain', - 'DeletionPolicy': 'Retain', - }, - 'MyUserDC45028B': { - 'Type': 'AWS::IAM::User', - }, - 'MyUserDefaultPolicy7B897426': { - 'Type': 'AWS::IAM::Policy', - 'Properties': { - 'PolicyDocument': { - 'Statement': [ - { - 'Action': [ - 's3:DeleteObject*', - 's3:PutObject*', - 's3:Abort*', - ], - 'Effect': 'Allow', - 'Resource': [ + expect(stack).toHaveResource('AWS::IAM::Policy', { + 'PolicyDocument': { + 'Statement': [ + { + 'Action': [ + 's3:DeleteObject*', + 's3:PutObject*', + 's3:Abort*', + ], + 'Effect': 'Allow', + 'Resource': [ + { + 'Fn::GetAtt': [ + 'MyBucketF68F3FF0', + 'Arn', + ], + }, + { + 'Fn::Join': [ + '', + [ { 'Fn::GetAtt': [ 'MyBucketF68F3FF0', 'Arn', ], }, - { - 'Fn::Join': [ - '', - [ - { - 'Fn::GetAtt': [ - 'MyBucketF68F3FF0', - 'Arn', - ], - }, - '/*', - ], - ], - }, - ], - }, - { - 'Action': [ - 'kms:Encrypt', - 'kms:ReEncrypt*', - 'kms:GenerateDataKey*', - 'kms:Decrypt', + '/*', ], - 'Effect': 'Allow', - 'Resource': { - 'Fn::GetAtt': [ - 'MyBucketKeyC17130CF', - 'Arn', - ], - }, - }, - ], - 'Version': '2012-10-17', - }, - 'PolicyName': 'MyUserDefaultPolicy7B897426', - 'Users': [ - { - 'Ref': 'MyUserDC45028B', + ], }, ], }, - }, + { + 'Action': [ + 'kms:Encrypt', + 'kms:ReEncrypt*', + 'kms:GenerateDataKey*', + 'kms:Decrypt', + ], + 'Effect': 'Allow', + 'Resource': { + 'Fn::GetAtt': [ + 'MyBucketKeyC17130CF', + 'Arn', + ], + }, + }, + ], + 'Version': '2012-10-17', }, + 'PolicyName': 'MyUserDefaultPolicy7B897426', + 'Users': [ + { + 'Ref': 'MyUserDC45028B', + }, + ], }); @@ -2581,7 +2405,5 @@ describe('bucket', () => { expect(() => new s3.Bucket(stack, 'MyBucket', { autoDeleteObjects: true, })).toThrow(/Cannot use \'autoDeleteObjects\' property on a bucket without setting removal policy to \'DESTROY\'/); - - }); }); diff --git a/packages/@aws-cdk/aws-s3/test/integ.bucket-grantdelete-kms.expected.json b/packages/@aws-cdk/aws-s3/test/integ.bucket-grantdelete-kms.expected.json index 08c82035e2188..bf2f24e021308 100644 --- a/packages/@aws-cdk/aws-s3/test/integ.bucket-grantdelete-kms.expected.json +++ b/packages/@aws-cdk/aws-s3/test/integ.bucket-grantdelete-kms.expected.json @@ -6,23 +6,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { diff --git a/packages/@aws-cdk/aws-s3/test/integ.bucket.expected.json b/packages/@aws-cdk/aws-s3/test/integ.bucket.expected.json index 1919b71360c2d..e816e78115fde 100644 --- a/packages/@aws-cdk/aws-s3/test/integ.bucket.expected.json +++ b/packages/@aws-cdk/aws-s3/test/integ.bucket.expected.json @@ -6,23 +6,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { @@ -43,25 +27,6 @@ } }, "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "MyUserDC45028B", - "Arn" - ] - } - }, - "Resource": "*" } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/aws-sagemaker/package.json b/packages/@aws-cdk/aws-sagemaker/package.json index 60c95684c3e51..532be18efa37d 100644 --- a/packages/@aws-cdk/aws-sagemaker/package.json +++ b/packages/@aws-cdk/aws-sagemaker/package.json @@ -92,5 +92,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-sam/package.json b/packages/@aws-cdk/aws-sam/package.json index e447bfef72916..6e76bd0098faf 100644 --- a/packages/@aws-cdk/aws-sam/package.json +++ b/packages/@aws-cdk/aws-sam/package.json @@ -73,12 +73,12 @@ "license": "Apache-2.0", "devDependencies": { "@aws-cdk/assert": "0.0.0", - "@types/jest": "^26.0.15", + "@types/jest": "^26.0.20", "cdk-build-tools": "0.0.0", "cfn2ts": "0.0.0", "jest": "^26.6.3", "pkglint": "0.0.0", - "ts-jest": "^26.4.4" + "ts-jest": "^26.5.1" }, "dependencies": { "@aws-cdk/core": "0.0.0", @@ -95,5 +95,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-sam/test/sam.test.ts b/packages/@aws-cdk/aws-sam/test/application.test.ts similarity index 100% rename from packages/@aws-cdk/aws-sam/test/sam.test.ts rename to packages/@aws-cdk/aws-sam/test/application.test.ts diff --git a/packages/@aws-cdk/aws-sam/test/function.test.ts b/packages/@aws-cdk/aws-sam/test/function.test.ts new file mode 100644 index 0000000000000..d8dade625be0e --- /dev/null +++ b/packages/@aws-cdk/aws-sam/test/function.test.ts @@ -0,0 +1,27 @@ +import '@aws-cdk/assert/jest'; +import * as cdk from '@aws-cdk/core'; +import * as sam from '../lib'; + +test("correctly chooses a string array from the type unions of the 'policies' property", () => { + const stack = new cdk.Stack(); + + new sam.CfnFunction(stack, 'MyFunction', { + codeUri: { + bucket: 'my-bucket', + key: 'my-key', + }, + runtime: 'nodejs-12.x', + handler: 'index.handler', + policies: ['AWSLambdaExecute'], + }); + + expect(stack).toHaveResourceLike('AWS::Serverless::Function', { + CodeUri: { + Bucket: 'my-bucket', + Key: 'my-key', + }, + Handler: 'index.handler', + Runtime: 'nodejs-12.x', + Policies: ['AWSLambdaExecute'], + }); +}); diff --git a/packages/@aws-cdk/aws-sdb/package.json b/packages/@aws-cdk/aws-sdb/package.json index 7d7f6d78a4bd6..9c1660f111f4b 100644 --- a/packages/@aws-cdk/aws-sdb/package.json +++ b/packages/@aws-cdk/aws-sdb/package.json @@ -92,5 +92,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-secretsmanager/package.json b/packages/@aws-cdk/aws-secretsmanager/package.json index 2906dd1c3ad99..fbd131bf94e89 100644 --- a/packages/@aws-cdk/aws-secretsmanager/package.json +++ b/packages/@aws-cdk/aws-secretsmanager/package.json @@ -127,5 +127,8 @@ "awscdkio": { "announce": false }, - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.ts b/packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.ts index 49dde40e8b9f4..2d245cc6e3e01 100644 --- a/packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.ts +++ b/packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.ts @@ -39,9 +39,6 @@ class SecretsManagerStack extends cdk.Stack { } const app = new cdk.App({ - context: { - '@aws-cdk/aws-secretsmanager:parseOwnedSecretName': 'true', - }, }); new SecretsManagerStack(app, 'Integ-SecretsManager-ParsedSecretName'); app.synth(); diff --git a/packages/@aws-cdk/aws-securityhub/package.json b/packages/@aws-cdk/aws-securityhub/package.json index a434471266f9b..32dc742b38c9c 100644 --- a/packages/@aws-cdk/aws-securityhub/package.json +++ b/packages/@aws-cdk/aws-securityhub/package.json @@ -93,5 +93,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-servicecatalog/package.json b/packages/@aws-cdk/aws-servicecatalog/package.json index 93012365276b5..546f615a66693 100644 --- a/packages/@aws-cdk/aws-servicecatalog/package.json +++ b/packages/@aws-cdk/aws-servicecatalog/package.json @@ -92,5 +92,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-servicecatalogappregistry/package.json b/packages/@aws-cdk/aws-servicecatalogappregistry/package.json index 5e889cf4373ea..8887c8c0682d6 100644 --- a/packages/@aws-cdk/aws-servicecatalogappregistry/package.json +++ b/packages/@aws-cdk/aws-servicecatalogappregistry/package.json @@ -93,5 +93,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-servicediscovery/package.json b/packages/@aws-cdk/aws-servicediscovery/package.json index b76e7009fd3b0..4af44ea9879e1 100644 --- a/packages/@aws-cdk/aws-servicediscovery/package.json +++ b/packages/@aws-cdk/aws-servicediscovery/package.json @@ -161,5 +161,8 @@ "awscdkio": { "announce": false }, - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-ses-actions/package.json b/packages/@aws-cdk/aws-ses-actions/package.json index 00a789c607b3f..ea141e0c8d37f 100644 --- a/packages/@aws-cdk/aws-ses-actions/package.json +++ b/packages/@aws-cdk/aws-ses-actions/package.json @@ -116,5 +116,8 @@ "env": { "AWSLINT_BASE_CONSTRUCT": true } + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-ses-actions/test/actions.test.ts b/packages/@aws-cdk/aws-ses-actions/test/actions.test.ts index 3a963fcf3f24b..d3268e949b7dc 100644 --- a/packages/@aws-cdk/aws-ses-actions/test/actions.test.ts +++ b/packages/@aws-cdk/aws-ses-actions/test/actions.test.ts @@ -1,4 +1,4 @@ -import { ResourcePart } from '@aws-cdk/assert'; +import { arrayWith, ResourcePart } from '@aws-cdk/assert'; import '@aws-cdk/assert/jest'; import * as kms from '@aws-cdk/aws-kms'; import * as lambda from '@aws-cdk/aws-lambda'; @@ -223,72 +223,30 @@ test('add s3 action', () => { }, }); - expect(stack).toHaveResource('AWS::KMS::Key', { + expect(stack).toHaveResourceLike('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', - ], - ], - }, + Statement: arrayWith({ + Action: [ + 'kms:Encrypt', + 'kms:GenerateDataKey', + ], + Condition: { + Null: { + 'kms:EncryptionContext:aws:ses:rule-name': 'false', + 'kms:EncryptionContext:aws:ses:message-id': 'false', }, - Resource: '*', - }, - { - Action: [ - 'kms:Encrypt', - 'kms:GenerateDataKey', - ], - Condition: { - Null: { - 'kms:EncryptionContext:aws:ses:rule-name': 'false', - 'kms:EncryptionContext:aws:ses:message-id': 'false', - }, - StringEquals: { - 'kms:EncryptionContext:aws:ses:source-account': { - Ref: 'AWS::AccountId', - }, + StringEquals: { + 'kms:EncryptionContext:aws:ses:source-account': { + Ref: 'AWS::AccountId', }, }, - Effect: 'Allow', - Principal: { - Service: 'ses.amazonaws.com', - }, - Resource: '*', }, - ], - Version: '2012-10-17', + Effect: 'Allow', + Principal: { + Service: 'ses.amazonaws.com', + }, + Resource: '*', + }), }, }); }); diff --git a/packages/@aws-cdk/aws-ses-actions/test/integ.actions.expected.json b/packages/@aws-cdk/aws-ses-actions/test/integ.actions.expected.json index 2bde038826803..b58b770bcd426 100644 --- a/packages/@aws-cdk/aws-ses-actions/test/integ.actions.expected.json +++ b/packages/@aws-cdk/aws-ses-actions/test/integ.actions.expected.json @@ -121,23 +121,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { diff --git a/packages/@aws-cdk/aws-ses/package.json b/packages/@aws-cdk/aws-ses/package.json index 5a28111cbaaf8..c4fac8762a988 100644 --- a/packages/@aws-cdk/aws-ses/package.json +++ b/packages/@aws-cdk/aws-ses/package.json @@ -112,5 +112,8 @@ }, "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-signer/package.json b/packages/@aws-cdk/aws-signer/package.json index 4d9d5cef26a04..40a8f5872b5b1 100644 --- a/packages/@aws-cdk/aws-signer/package.json +++ b/packages/@aws-cdk/aws-signer/package.json @@ -91,5 +91,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-sns-subscriptions/package.json b/packages/@aws-cdk/aws-sns-subscriptions/package.json index f89258c018931..354b1c386b44e 100644 --- a/packages/@aws-cdk/aws-sns-subscriptions/package.json +++ b/packages/@aws-cdk/aws-sns-subscriptions/package.json @@ -99,5 +99,8 @@ "env": { "AWSLINT_BASE_CONSTRUCT": true } + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-sns/package.json b/packages/@aws-cdk/aws-sns/package.json index dd9486fc85b0e..4b60a53f24c35 100644 --- a/packages/@aws-cdk/aws-sns/package.json +++ b/packages/@aws-cdk/aws-sns/package.json @@ -114,5 +114,8 @@ "awscdkio": { "announce": false }, - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-sns/test/integ.sns.expected.json b/packages/@aws-cdk/aws-sns/test/integ.sns.expected.json index 8ef28440f5185..86cfd60faf932 100644 --- a/packages/@aws-cdk/aws-sns/test/integ.sns.expected.json +++ b/packages/@aws-cdk/aws-sns/test/integ.sns.expected.json @@ -6,23 +6,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { @@ -65,4 +49,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-sqs/package.json b/packages/@aws-cdk/aws-sqs/package.json index 87c9aa9ffebff..008b36d37ed4d 100644 --- a/packages/@aws-cdk/aws-sqs/package.json +++ b/packages/@aws-cdk/aws-sqs/package.json @@ -73,7 +73,7 @@ "@aws-cdk/assert": "0.0.0", "@aws-cdk/aws-s3": "0.0.0", "@types/nodeunit": "^0.0.31", - "aws-sdk": "^2.830.0", + "aws-sdk": "^2.845.0", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "cfn2ts": "0.0.0", @@ -107,5 +107,8 @@ "awscdkio": { "announce": false }, - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-sqs/test/integ.sqs.expected.json b/packages/@aws-cdk/aws-sqs/test/integ.sqs.expected.json index 4ad0f8e0c8b34..30c3a8a66bfac 100644 --- a/packages/@aws-cdk/aws-sqs/test/integ.sqs.expected.json +++ b/packages/@aws-cdk/aws-sqs/test/integ.sqs.expected.json @@ -24,23 +24,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { @@ -61,19 +45,6 @@ } }, "Resource": "*" - }, - { - "Action": "kms:Decrypt", - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "Role1ABCC5F0", - "Arn" - ] - } - }, - "Resource": "*" } ], "Version": "2012-10-17" @@ -208,4 +179,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-ssm/package.json b/packages/@aws-cdk/aws-ssm/package.json index 3345c0368b78d..234d166c9152f 100644 --- a/packages/@aws-cdk/aws-ssm/package.json +++ b/packages/@aws-cdk/aws-ssm/package.json @@ -100,5 +100,8 @@ "awscdkio": { "announce": false }, - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/aws-sso/package.json b/packages/@aws-cdk/aws-sso/package.json index ec332f75b3477..af088e448aff9 100644 --- a/packages/@aws-cdk/aws-sso/package.json +++ b/packages/@aws-cdk/aws-sso/package.json @@ -91,5 +91,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/package.json b/packages/@aws-cdk/aws-stepfunctions-tasks/package.json index 3031ab05dc9a3..63496e7c5fca5 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/package.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/package.json @@ -127,5 +127,8 @@ "env": { "AWSLINT_BASE_CONSTRUCT": true } + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/sagemaker/integ.call-sagemaker.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/sagemaker/integ.call-sagemaker.expected.json index 06de00eb99d3f..699a2ff2d686b 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/sagemaker/integ.call-sagemaker.expected.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/sagemaker/integ.call-sagemaker.expected.json @@ -6,23 +6,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { @@ -43,40 +27,6 @@ } }, "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "TrainTaskSagemakerRoleD5A6F967", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*", - "kms:Decrypt" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "TrainTaskSagemakerRoleD5A6F967", - "Arn" - ] - } - }, - "Resource": "*" } ], "Version": "2012-10-17" @@ -636,4 +586,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/sagemaker/integ.create-training-job.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/sagemaker/integ.create-training-job.expected.json index fe1ebdecd9509..5b60bc0b1e500 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/sagemaker/integ.create-training-job.expected.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/sagemaker/integ.create-training-job.expected.json @@ -6,23 +6,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { @@ -43,40 +27,6 @@ } }, "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "TrainTaskSagemakerRole0A9B1CDD", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*", - "kms:Decrypt" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "TrainTaskSagemakerRole0A9B1CDD", - "Arn" - ] - } - }, - "Resource": "*" } ], "Version": "2012-10-17" @@ -408,4 +358,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-stepfunctions/README.md b/packages/@aws-cdk/aws-stepfunctions/README.md index ae6635358d0ec..8682de8dd13a9 100644 --- a/packages/@aws-cdk/aws-stepfunctions/README.md +++ b/packages/@aws-cdk/aws-stepfunctions/README.md @@ -397,7 +397,7 @@ const sm = new sfn.StateMachine(this, 'StateMachine', { }); // don't forget permissions. You need to assign them -table.grantWriteData(sm.role); +table.grantWriteData(sm); ``` ## Task Chaining diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts b/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts index 8c7ee585e4b09..cced1d4519660 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts @@ -140,12 +140,18 @@ abstract class StateMachineBase extends Resource implements IStateMachine { public static fromStateMachineArn(scope: Construct, id: string, stateMachineArn: string): IStateMachine { class Import extends StateMachineBase { public readonly stateMachineArn = stateMachineArn; + public readonly grantPrincipal = new iam.UnknownPrincipal({ resource: this }); } return new Import(scope, id); } public abstract readonly stateMachineArn: string; + /** + * The principal this state machine is running as + */ + public abstract readonly grantPrincipal: iam.IPrincipal; + /** * Grant the given identity permissions to start an execution of this state * machine. @@ -406,6 +412,13 @@ export class StateMachine extends StateMachineBase { }); } + /** + * The principal this state machine is running as + */ + public get grantPrincipal() { + return this.role.grantPrincipal; + } + /** * Add the given statement to the role's policy */ @@ -461,7 +474,7 @@ export class StateMachine extends StateMachineBase { /** * A State Machine */ -export interface IStateMachine extends IResource { +export interface IStateMachine extends IResource, iam.IGrantable { /** * The ARN of the state machine * @attribute diff --git a/packages/@aws-cdk/aws-stepfunctions/package.json b/packages/@aws-cdk/aws-stepfunctions/package.json index 2e9f998e1175f..80035201f8b36 100644 --- a/packages/@aws-cdk/aws-stepfunctions/package.json +++ b/packages/@aws-cdk/aws-stepfunctions/package.json @@ -82,6 +82,7 @@ "@aws-cdk/aws-events": "0.0.0", "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-logs": "0.0.0", + "@aws-cdk/aws-s3": "0.0.0", "@aws-cdk/core": "0.0.0", "constructs": "^3.2.0" }, @@ -91,6 +92,7 @@ "@aws-cdk/aws-events": "0.0.0", "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-logs": "0.0.0", + "@aws-cdk/aws-s3": "0.0.0", "@aws-cdk/core": "0.0.0", "constructs": "^3.2.0" }, @@ -107,5 +109,8 @@ "maturity": "stable", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-stepfunctions/test/state-machine.test.ts b/packages/@aws-cdk/aws-stepfunctions/test/state-machine.test.ts index 33353447abeec..e721b460a5358 100644 --- a/packages/@aws-cdk/aws-stepfunctions/test/state-machine.test.ts +++ b/packages/@aws-cdk/aws-stepfunctions/test/state-machine.test.ts @@ -1,5 +1,6 @@ import '@aws-cdk/assert/jest'; 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 stepfunctions from '../lib'; @@ -160,4 +161,61 @@ describe('State Machine', () => { ], }); }); + + test('grant access', () => { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + const sm = new stepfunctions.StateMachine(stack, 'MyStateMachine', { + definition: stepfunctions.Chain.start(new stepfunctions.Pass(stack, 'Pass')), + }); + const bucket = new s3.Bucket(stack, 'MyBucket'); + bucket.grantRead(sm); + + // THEN + expect(stack).toHaveResource('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: [ + 's3:GetObject*', + 's3:GetBucket*', + 's3:List*', + ], + Effect: 'Allow', + Resource: [ + { + 'Fn::GetAtt': [ + 'MyBucketF68F3FF0', + 'Arn', + ], + }, + { + 'Fn::Join': [ + '', + [ + { + 'Fn::GetAtt': [ + 'MyBucketF68F3FF0', + 'Arn', + ], + }, + '/*', + ], + ], + }, + ], + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'MyStateMachineRoleDefaultPolicyE468EB18', + Roles: [ + { + Ref: 'MyStateMachineRoleD59FFEBC', + }, + ], + }); + }); }); diff --git a/packages/@aws-cdk/aws-synthetics/package.json b/packages/@aws-cdk/aws-synthetics/package.json index 8cb33be39e399..3b15e2a0b8ffb 100644 --- a/packages/@aws-cdk/aws-synthetics/package.json +++ b/packages/@aws-cdk/aws-synthetics/package.json @@ -102,5 +102,8 @@ "maturity": "developer-preview", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-timestream/package.json b/packages/@aws-cdk/aws-timestream/package.json index 5baa3f1625ad6..c432583d5cbdb 100644 --- a/packages/@aws-cdk/aws-timestream/package.json +++ b/packages/@aws-cdk/aws-timestream/package.json @@ -91,5 +91,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-transfer/package.json b/packages/@aws-cdk/aws-transfer/package.json index e9a884bb6c2d5..b70fc5929c4c0 100644 --- a/packages/@aws-cdk/aws-transfer/package.json +++ b/packages/@aws-cdk/aws-transfer/package.json @@ -93,5 +93,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-waf/package.json b/packages/@aws-cdk/aws-waf/package.json index f8ef1f1343d4d..ced3ca7dc741b 100644 --- a/packages/@aws-cdk/aws-waf/package.json +++ b/packages/@aws-cdk/aws-waf/package.json @@ -92,5 +92,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-wafregional/package.json b/packages/@aws-cdk/aws-wafregional/package.json index fe819f3890521..8e165fd4169f8 100644 --- a/packages/@aws-cdk/aws-wafregional/package.json +++ b/packages/@aws-cdk/aws-wafregional/package.json @@ -92,5 +92,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-wafv2/package.json b/packages/@aws-cdk/aws-wafv2/package.json index 9fe9d0c90d02e..7bae0807f8209 100644 --- a/packages/@aws-cdk/aws-wafv2/package.json +++ b/packages/@aws-cdk/aws-wafv2/package.json @@ -93,5 +93,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/aws-workspaces/package.json b/packages/@aws-cdk/aws-workspaces/package.json index e0272248924c4..d2a3313f491dd 100644 --- a/packages/@aws-cdk/aws-workspaces/package.json +++ b/packages/@aws-cdk/aws-workspaces/package.json @@ -92,5 +92,8 @@ "maturity": "cfn-only", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/cdk-assets-schema/package.json b/packages/@aws-cdk/cdk-assets-schema/package.json index 897d8cdc829c8..272b962c836ad 100644 --- a/packages/@aws-cdk/cdk-assets-schema/package.json +++ b/packages/@aws-cdk/cdk-assets-schema/package.json @@ -50,7 +50,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/jest": "^26.0.15", + "@types/jest": "^26.0.20", "cdk-build-tools": "0.0.0", "jest": "^26.6.3", "pkglint": "0.0.0" @@ -76,5 +76,8 @@ "maturity": "deprecated", "cdk-build": { "jest": true + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/cfnspec/CHANGELOG.md b/packages/@aws-cdk/cfnspec/CHANGELOG.md index 2d117eebb4978..d8ca8b54af678 100644 --- a/packages/@aws-cdk/cfnspec/CHANGELOG.md +++ b/packages/@aws-cdk/cfnspec/CHANGELOG.md @@ -1,3 +1,117 @@ +# CloudFormation Resource Specification v27.0.0 + +## New Resource Types + +* AWS::ECR::RegistryPolicy +* AWS::ECR::ReplicationConfiguration +* AWS::ElastiCache::GlobalReplicationGroup +* AWS::ImageBuilder::ContainerRecipe + +## Attribute Changes + +* AWS::IoTWireless::ServiceProfile LoRaWANResponse (__added__) +* AWS::MWAA::Environment CreatedAt (__deleted__) +* AWS::MWAA::Environment LastUpdate (__deleted__) +* AWS::MWAA::Environment Name (__deleted__) +* AWS::MWAA::Environment ServiceRoleArn (__deleted__) +* AWS::MWAA::Environment Status (__deleted__) +* AWS::MWAA::Environment WebserverUrl (__added__) + +## Property Changes + +* AWS::AppMesh::GatewayRoute GatewayRouteName.Required (__changed__) + * Old: true + * New: false +* AWS::AppMesh::Mesh MeshName.Required (__changed__) + * Old: true + * New: false +* AWS::AppMesh::Route RouteName.Required (__changed__) + * Old: true + * New: false +* AWS::AppMesh::VirtualGateway VirtualGatewayName.Required (__changed__) + * Old: true + * New: false +* AWS::AppMesh::VirtualNode VirtualNodeName.Required (__changed__) + * Old: true + * New: false +* AWS::AppMesh::VirtualRouter VirtualRouterName.Required (__changed__) + * Old: true + * New: false +* AWS::Cassandra::Keyspace Tags (__added__) +* AWS::Cassandra::Table PointInTimeRecoveryEnabled (__added__) +* AWS::Cassandra::Table Tags (__added__) +* AWS::CloudWatch::MetricStream OutputFormat (__added__) +* AWS::ECS::Service EnableExecuteCommand (__added__) +* AWS::ECS::Service PlatformVersion.UpdateType (__changed__) + * Old: Immutable + * New: Mutable +* AWS::IoTWireless::Destination NextToken (__deleted__) +* AWS::IoTWireless::Destination Name.UpdateType (__changed__) + * Old: Mutable + * New: Immutable +* AWS::IoTWireless::DeviceProfile LoRaWANDeviceProfile (__deleted__) +* AWS::IoTWireless::DeviceProfile NextToken (__deleted__) +* AWS::IoTWireless::DeviceProfile LoRaWAN (__added__) +* AWS::IoTWireless::ServiceProfile LoRaWANGetServiceProfileInfo (__deleted__) +* AWS::IoTWireless::ServiceProfile LoRaWANServiceProfile (__deleted__) +* AWS::IoTWireless::ServiceProfile NextToken (__deleted__) +* AWS::IoTWireless::ServiceProfile LoRaWAN (__added__) +* AWS::IoTWireless::WirelessDevice LoRaWANDevice (__deleted__) +* AWS::IoTWireless::WirelessDevice NextToken (__deleted__) +* AWS::IoTWireless::WirelessDevice LastUplinkReceivedAt (__added__) +* AWS::IoTWireless::WirelessDevice LoRaWAN (__added__) +* AWS::IoTWireless::WirelessGateway LoRaWANGateway (__deleted__) +* AWS::IoTWireless::WirelessGateway NextToken (__deleted__) +* AWS::IoTWireless::WirelessGateway LastUplinkReceivedAt (__added__) +* AWS::IoTWireless::WirelessGateway LoRaWAN (__added__) +* AWS::MWAA::Environment WebserverUrl (__deleted__) +* AWS::MWAA::Environment Name (__added__) +* AWS::MWAA::Environment KmsKey.UpdateType (__changed__) + * Old: Mutable + * New: Immutable +* AWS::MWAA::Environment NetworkConfiguration.UpdateType (__changed__) + * Old: Immutable + * New: Mutable +* AWS::SageMaker::Model InferenceExecutionConfig (__deleted__) + +## Property Type Changes + +* AWS::IoTWireless::WirelessDevice.AbpV10X (__removed__) +* AWS::IoTWireless::WirelessDevice.OtaaV10X (__removed__) +* AWS::IoTWireless::WirelessDevice.SessionKeysAbpV10X (__removed__) +* AWS::MWAA::Environment.LastUpdate (__removed__) +* AWS::MWAA::Environment.SecurityGroupList (__removed__) +* AWS::MWAA::Environment.SubnetList (__removed__) +* AWS::MWAA::Environment.UpdateError (__removed__) +* AWS::SageMaker::Model.InferenceExecutionConfig (__removed__) +* AWS::DataBrew::Job.CsvOutputOptions (__added__) +* AWS::DataBrew::Job.OutputFormatOptions (__added__) +* AWS::IoTWireless::WirelessDevice.AbpV10x (__added__) +* AWS::IoTWireless::WirelessDevice.OtaaV10x (__added__) +* AWS::IoTWireless::WirelessDevice.SessionKeysAbpV10x (__added__) +* AWS::Cognito::UserPoolClient.AnalyticsConfiguration ApplicationArn (__added__) +* AWS::DataBrew::Job.Output FormatOptions (__added__) +* AWS::IoTWireless::WirelessDevice.LoRaWANDevice AbpV10X (__deleted__) +* AWS::IoTWireless::WirelessDevice.LoRaWANDevice OtaaV10X (__deleted__) +* AWS::IoTWireless::WirelessDevice.LoRaWANDevice AbpV10x (__added__) +* AWS::IoTWireless::WirelessDevice.LoRaWANDevice OtaaV10x (__added__) +* AWS::MWAA::Environment.NetworkConfiguration SecurityGroupIds.PrimitiveItemType (__added__) +* AWS::MWAA::Environment.NetworkConfiguration SecurityGroupIds.Type (__changed__) + * Old: SecurityGroupList + * New: List +* AWS::MWAA::Environment.NetworkConfiguration SecurityGroupIds.UpdateType (__changed__) + * Old: Immutable + * New: Mutable +* AWS::MWAA::Environment.NetworkConfiguration SubnetIds.PrimitiveItemType (__added__) +* AWS::MWAA::Environment.NetworkConfiguration SubnetIds.Type (__changed__) + * Old: SubnetList + * New: List +* AWS::MediaPackage::PackagingConfiguration.CmafEncryption SpekeKeyProvider.Type (__added__) +* AWS::MediaPackage::PackagingConfiguration.DashEncryption SpekeKeyProvider.Type (__added__) +* AWS::MediaPackage::PackagingConfiguration.HlsEncryption SpekeKeyProvider.Type (__added__) +* AWS::MediaPackage::PackagingConfiguration.MssEncryption SpekeKeyProvider.Type (__added__) + + # CloudFormation Resource Specification v26.0.0 ## New Resource Types diff --git a/packages/@aws-cdk/cfnspec/build-tools/create-missing-libraries.ts b/packages/@aws-cdk/cfnspec/build-tools/create-missing-libraries.ts index 6ddfb76a4f782..93d20a169a12a 100644 --- a/packages/@aws-cdk/cfnspec/build-tools/create-missing-libraries.ts +++ b/packages/@aws-cdk/cfnspec/build-tools/create-missing-libraries.ts @@ -195,6 +195,9 @@ async function main() { awscdkio: { announce: false, }, + publishConfig: { + tag: 'latest', + }, }); await write('.gitignore', [ diff --git a/packages/@aws-cdk/cfnspec/cfn.version b/packages/@aws-cdk/cfnspec/cfn.version index 1e212a919f492..008c39a438806 100644 --- a/packages/@aws-cdk/cfnspec/cfn.version +++ b/packages/@aws-cdk/cfnspec/cfn.version @@ -1 +1 @@ -26.0.0 +27.0.0 diff --git a/packages/@aws-cdk/cfnspec/package.json b/packages/@aws-cdk/cfnspec/package.json index a9cca1eb3107a..3ab38256028bd 100644 --- a/packages/@aws-cdk/cfnspec/package.json +++ b/packages/@aws-cdk/cfnspec/package.json @@ -25,7 +25,7 @@ "types": "lib/index.d.ts", "devDependencies": { "@types/fs-extra": "^8.1.1", - "@types/md5": "^2.2.1", + "@types/md5": "^2.3.0", "@types/nodeunit": "^0.0.31", "cdk-build-tools": "0.0.0", "fast-json-patch": "^2.2.1", diff --git a/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json b/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json index 9391cf7efc80d..a2e212d9f552e 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json +++ b/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json @@ -12702,6 +12702,12 @@ "AWS::Cognito::UserPoolClient.AnalyticsConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cognito-userpoolclient-analyticsconfiguration.html", "Properties": { + "ApplicationArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cognito-userpoolclient-analyticsconfiguration.html#cfn-cognito-userpoolclient-analyticsconfiguration-applicationarn", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, "ApplicationId": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cognito-userpoolclient-analyticsconfiguration.html#cfn-cognito-userpoolclient-analyticsconfiguration-applicationid", "PrimitiveType": "String", @@ -14010,6 +14016,17 @@ } } }, + "AWS::DataBrew::Job.CsvOutputOptions": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-job-csvoutputoptions.html", + "Properties": { + "Delimiter": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-job-csvoutputoptions.html#cfn-databrew-job-csvoutputoptions-delimiter", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, "AWS::DataBrew::Job.Output": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-job-output.html", "Properties": { @@ -14025,6 +14042,12 @@ "Required": false, "UpdateType": "Mutable" }, + "FormatOptions": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-job-output.html#cfn-databrew-job-output-formatoptions", + "Required": false, + "Type": "OutputFormatOptions", + "UpdateType": "Mutable" + }, "Location": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-job-output.html#cfn-databrew-job-output-location", "Required": true, @@ -14047,6 +14070,17 @@ } } }, + "AWS::DataBrew::Job.OutputFormatOptions": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-job-outputformatoptions.html", + "Properties": { + "Csv": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-job-outputformatoptions.html#cfn-databrew-job-outputformatoptions-csv", + "Required": false, + "Type": "CsvOutputOptions", + "UpdateType": "Mutable" + } + } + }, "AWS::DataBrew::Job.S3Location": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-databrew-job-s3location.html", "Properties": { @@ -18362,6 +18396,47 @@ } } }, + "AWS::ECR::ReplicationConfiguration.ReplicationConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecr-replicationconfiguration-replicationconfiguration.html", + "Properties": { + "Rules": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecr-replicationconfiguration-replicationconfiguration.html#cfn-ecr-replicationconfiguration-replicationconfiguration-rules", + "ItemType": "ReplicationRule", + "Required": true, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, + "AWS::ECR::ReplicationConfiguration.ReplicationDestination": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecr-replicationconfiguration-replicationdestination.html", + "Properties": { + "Region": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecr-replicationconfiguration-replicationdestination.html#cfn-ecr-replicationconfiguration-replicationdestination-region", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + }, + "RegistryId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecr-replicationconfiguration-replicationdestination.html#cfn-ecr-replicationconfiguration-replicationdestination-registryid", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::ECR::ReplicationConfiguration.ReplicationRule": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecr-replicationconfiguration-replicationrule.html", + "Properties": { + "Destinations": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecr-replicationconfiguration-replicationrule.html#cfn-ecr-replicationconfiguration-replicationrule-destinations", + "ItemType": "ReplicationDestination", + "Required": true, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, "AWS::ECR::Repository.LifecyclePolicy": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecr-repository-lifecyclepolicy.html", "Properties": { @@ -21368,6 +21443,73 @@ } } }, + "AWS::ElastiCache::GlobalReplicationGroup.GlobalReplicationGroupMember": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticache-globalreplicationgroup-globalreplicationgroupmember.html", + "Properties": { + "ReplicationGroupId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticache-globalreplicationgroup-globalreplicationgroupmember.html#cfn-elasticache-globalreplicationgroup-globalreplicationgroupmember-replicationgroupid", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "ReplicationGroupRegion": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticache-globalreplicationgroup-globalreplicationgroupmember.html#cfn-elasticache-globalreplicationgroup-globalreplicationgroupmember-replicationgroupregion", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Role": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticache-globalreplicationgroup-globalreplicationgroupmember.html#cfn-elasticache-globalreplicationgroup-globalreplicationgroupmember-role", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::ElastiCache::GlobalReplicationGroup.RegionalConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticache-globalreplicationgroup-regionalconfiguration.html", + "Properties": { + "ReplicationGroupId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticache-globalreplicationgroup-regionalconfiguration.html#cfn-elasticache-globalreplicationgroup-regionalconfiguration-replicationgroupid", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "ReplicationGroupRegion": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticache-globalreplicationgroup-regionalconfiguration.html#cfn-elasticache-globalreplicationgroup-regionalconfiguration-replicationgroupregion", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "ReshardingConfigurations": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticache-globalreplicationgroup-regionalconfiguration.html#cfn-elasticache-globalreplicationgroup-regionalconfiguration-reshardingconfigurations", + "DuplicatesAllowed": false, + "ItemType": "ReshardingConfiguration", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, + "AWS::ElastiCache::GlobalReplicationGroup.ReshardingConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticache-globalreplicationgroup-reshardingconfiguration.html", + "Properties": { + "NodeGroupId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticache-globalreplicationgroup-reshardingconfiguration.html#cfn-elasticache-globalreplicationgroup-reshardingconfiguration-nodegroupid", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "PreferredAvailabilityZones": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticache-globalreplicationgroup-reshardingconfiguration.html#cfn-elasticache-globalreplicationgroup-reshardingconfiguration-preferredavailabilityzones", + "DuplicatesAllowed": true, + "PrimitiveItemType": "String", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, "AWS::ElastiCache::ReplicationGroup.NodeGroupConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticache-replicationgroup-nodegroupconfiguration.html", "Properties": { @@ -26986,6 +27128,34 @@ } } }, + "AWS::ImageBuilder::ContainerRecipe.ComponentConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-imagebuilder-containerrecipe-componentconfiguration.html", + "Properties": { + "ComponentArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-imagebuilder-containerrecipe-componentconfiguration.html#cfn-imagebuilder-containerrecipe-componentconfiguration-componentarn", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + } + } + }, + "AWS::ImageBuilder::ContainerRecipe.TargetContainerRepository": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-imagebuilder-containerrecipe-targetcontainerrepository.html", + "Properties": { + "RepositoryName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-imagebuilder-containerrecipe-targetcontainerrepository.html#cfn-imagebuilder-containerrecipe-targetcontainerrepository-repositoryname", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "Service": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-imagebuilder-containerrecipe-targetcontainerrepository.html#cfn-imagebuilder-containerrecipe-targetcontainerrepository-service", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + } + } + }, "AWS::ImageBuilder::DistributionConfiguration.Distribution": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-imagebuilder-distributionconfiguration-distribution.html", "Properties": { @@ -30179,7 +30349,7 @@ } } }, - "AWS::IoTWireless::WirelessDevice.AbpV10X": { + "AWS::IoTWireless::WirelessDevice.AbpV10x": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iotwireless-wirelessdevice-abpv10x.html", "Properties": { "DevAddr": { @@ -30191,7 +30361,7 @@ "SessionKeys": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iotwireless-wirelessdevice-abpv10x.html#cfn-iotwireless-wirelessdevice-abpv10x-sessionkeys", "Required": true, - "Type": "SessionKeysAbpV10X", + "Type": "SessionKeysAbpV10x", "UpdateType": "Mutable" } } @@ -30216,10 +30386,10 @@ "AWS::IoTWireless::WirelessDevice.LoRaWANDevice": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iotwireless-wirelessdevice-lorawandevice.html", "Properties": { - "AbpV10X": { + "AbpV10x": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iotwireless-wirelessdevice-lorawandevice.html#cfn-iotwireless-wirelessdevice-lorawandevice-abpv10x", "Required": false, - "Type": "AbpV10X", + "Type": "AbpV10x", "UpdateType": "Mutable" }, "AbpV11": { @@ -30240,10 +30410,10 @@ "Required": false, "UpdateType": "Mutable" }, - "OtaaV10X": { + "OtaaV10x": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iotwireless-wirelessdevice-lorawandevice.html#cfn-iotwireless-wirelessdevice-lorawandevice-otaav10x", "Required": false, - "Type": "OtaaV10X", + "Type": "OtaaV10x", "UpdateType": "Mutable" }, "OtaaV11": { @@ -30260,7 +30430,7 @@ } } }, - "AWS::IoTWireless::WirelessDevice.OtaaV10X": { + "AWS::IoTWireless::WirelessDevice.OtaaV10x": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iotwireless-wirelessdevice-otaav10x.html", "Properties": { "AppEui": { @@ -30300,7 +30470,7 @@ } } }, - "AWS::IoTWireless::WirelessDevice.SessionKeysAbpV10X": { + "AWS::IoTWireless::WirelessDevice.SessionKeysAbpV10x": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iotwireless-wirelessdevice-sessionkeysabpv10x.html", "Properties": { "AppSKey": { @@ -35037,29 +35207,6 @@ "AWS::MWAA::Environment.AirflowConfigurationOptions": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mwaa-environment-airflowconfigurationoptions.html" }, - "AWS::MWAA::Environment.LastUpdate": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mwaa-environment-lastupdate.html", - "Properties": { - "CreatedAt": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mwaa-environment-lastupdate.html#cfn-mwaa-environment-lastupdate-createdat", - "PrimitiveType": "String", - "Required": false, - "UpdateType": "Mutable" - }, - "Error": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mwaa-environment-lastupdate.html#cfn-mwaa-environment-lastupdate-error", - "Required": false, - "Type": "UpdateError", - "UpdateType": "Mutable" - }, - "Status": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mwaa-environment-lastupdate.html#cfn-mwaa-environment-lastupdate-status", - "PrimitiveType": "String", - "Required": false, - "UpdateType": "Mutable" - } - } - }, "AWS::MWAA::Environment.LoggingConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mwaa-environment-loggingconfiguration.html", "Properties": { @@ -35123,35 +35270,13 @@ "Properties": { "SecurityGroupIds": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mwaa-environment-networkconfiguration.html#cfn-mwaa-environment-networkconfiguration-securitygroupids", + "PrimitiveItemType": "String", "Required": false, - "Type": "SecurityGroupList", - "UpdateType": "Immutable" + "Type": "List", + "UpdateType": "Mutable" }, "SubnetIds": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mwaa-environment-networkconfiguration.html#cfn-mwaa-environment-networkconfiguration-subnetids", - "Required": false, - "Type": "SubnetList", - "UpdateType": "Immutable" - } - } - }, - "AWS::MWAA::Environment.SecurityGroupList": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mwaa-environment-securitygrouplist.html", - "Properties": { - "SecurityGroupList": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mwaa-environment-securitygrouplist.html#cfn-mwaa-environment-securitygrouplist-securitygrouplist", - "PrimitiveItemType": "String", - "Required": false, - "Type": "List", - "UpdateType": "Immutable" - } - } - }, - "AWS::MWAA::Environment.SubnetList": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mwaa-environment-subnetlist.html", - "Properties": { - "SubnetList": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mwaa-environment-subnetlist.html#cfn-mwaa-environment-subnetlist-subnetlist", "PrimitiveItemType": "String", "Required": false, "Type": "List", @@ -35162,23 +35287,6 @@ "AWS::MWAA::Environment.TagMap": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mwaa-environment-tagmap.html" }, - "AWS::MWAA::Environment.UpdateError": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mwaa-environment-updateerror.html", - "Properties": { - "ErrorCode": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mwaa-environment-updateerror.html#cfn-mwaa-environment-updateerror-errorcode", - "PrimitiveType": "String", - "Required": false, - "UpdateType": "Mutable" - }, - "ErrorMessage": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mwaa-environment-updateerror.html#cfn-mwaa-environment-updateerror-errormessage", - "PrimitiveType": "String", - "Required": false, - "UpdateType": "Mutable" - } - } - }, "AWS::Macie::FindingsFilter.Criterion": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-macie-findingsfilter-criterion.html" }, @@ -40645,6 +40753,7 @@ "SpekeKeyProvider": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mediapackage-packagingconfiguration-cmafencryption.html#cfn-mediapackage-packagingconfiguration-cmafencryption-spekekeyprovider", "Required": true, + "Type": "SpekeKeyProvider", "UpdateType": "Mutable" } } @@ -40679,6 +40788,7 @@ "SpekeKeyProvider": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mediapackage-packagingconfiguration-dashencryption.html#cfn-mediapackage-packagingconfiguration-dashencryption-spekekeyprovider", "Required": true, + "Type": "SpekeKeyProvider", "UpdateType": "Mutable" } } @@ -40773,6 +40883,7 @@ "SpekeKeyProvider": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mediapackage-packagingconfiguration-hlsencryption.html#cfn-mediapackage-packagingconfiguration-hlsencryption-spekekeyprovider", "Required": true, + "Type": "SpekeKeyProvider", "UpdateType": "Mutable" } } @@ -40854,6 +40965,7 @@ "SpekeKeyProvider": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-mediapackage-packagingconfiguration-mssencryption.html#cfn-mediapackage-packagingconfiguration-mssencryption-spekekeyprovider", "Required": true, + "Type": "SpekeKeyProvider", "UpdateType": "Mutable" } } @@ -48310,17 +48422,6 @@ } } }, - "AWS::SageMaker::Model.InferenceExecutionConfig": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sagemaker-model-inferenceexecutionconfig.html", - "Properties": { - "Mode": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sagemaker-model-inferenceexecutionconfig.html#cfn-sagemaker-model-inferenceexecutionconfig-mode", - "PrimitiveType": "String", - "Required": true, - "UpdateType": "Immutable" - } - } - }, "AWS::SageMaker::Model.MultiModelConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sagemaker-model-containerdefinition-multimodelconfig.html", "Properties": { @@ -52460,7 +52561,7 @@ } } }, - "ResourceSpecificationVersion": "26.0.0", + "ResourceSpecificationVersion": "27.0.0", "ResourceTypes": { "AWS::ACMPCA::Certificate": { "Attributes": { @@ -55075,7 +55176,7 @@ "GatewayRouteName": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-appmesh-gatewayroute.html#cfn-appmesh-gatewayroute-gatewayroutename", "PrimitiveType": "String", - "Required": true, + "Required": false, "UpdateType": "Immutable" }, "MeshName": { @@ -55134,7 +55235,7 @@ "MeshName": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-appmesh-mesh.html#cfn-appmesh-mesh-meshname", "PrimitiveType": "String", - "Required": true, + "Required": false, "UpdateType": "Immutable" }, "Spec": { @@ -55193,7 +55294,7 @@ "RouteName": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-appmesh-route.html#cfn-appmesh-route-routename", "PrimitiveType": "String", - "Required": true, + "Required": false, "UpdateType": "Immutable" }, "Spec": { @@ -55268,7 +55369,7 @@ "VirtualGatewayName": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-appmesh-virtualgateway.html#cfn-appmesh-virtualgateway-virtualgatewayname", "PrimitiveType": "String", - "Required": true, + "Required": false, "UpdateType": "Immutable" } } @@ -55324,7 +55425,7 @@ "VirtualNodeName": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-appmesh-virtualnode.html#cfn-appmesh-virtualnode-virtualnodename", "PrimitiveType": "String", - "Required": true, + "Required": false, "UpdateType": "Immutable" } } @@ -55380,7 +55481,7 @@ "VirtualRouterName": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-appmesh-virtualrouter.html#cfn-appmesh-virtualrouter-virtualroutername", "PrimitiveType": "String", - "Required": true, + "Required": false, "UpdateType": "Immutable" } } @@ -57427,6 +57528,14 @@ "PrimitiveType": "String", "Required": false, "UpdateType": "Immutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cassandra-keyspace.html#cfn-cassandra-keyspace-tags", + "DuplicatesAllowed": false, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" } } }, @@ -57461,6 +57570,12 @@ "Type": "List", "UpdateType": "Immutable" }, + "PointInTimeRecoveryEnabled": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cassandra-table.html#cfn-cassandra-table-pointintimerecoveryenabled", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, "RegularColumns": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cassandra-table.html#cfn-cassandra-table-regularcolumns", "DuplicatesAllowed": false, @@ -57474,6 +57589,14 @@ "PrimitiveType": "String", "Required": false, "UpdateType": "Immutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cassandra-table.html#cfn-cassandra-table-tags", + "DuplicatesAllowed": false, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" } } }, @@ -58535,6 +58658,12 @@ "Required": false, "UpdateType": "Immutable" }, + "OutputFormat": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudwatch-metricstream.html#cfn-cloudwatch-metricstream-outputformat", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, "RoleArn": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudwatch-metricstream.html#cfn-cloudwatch-metricstream-rolearn", "PrimitiveType": "String", @@ -65178,6 +65307,38 @@ } } }, + "AWS::ECR::RegistryPolicy": { + "Attributes": { + "RegistryId": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecr-registrypolicy.html", + "Properties": { + "PolicyText": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecr-registrypolicy.html#cfn-ecr-registrypolicy-policytext", + "PrimitiveType": "Json", + "Required": true, + "UpdateType": "Mutable" + } + } + }, + "AWS::ECR::ReplicationConfiguration": { + "Attributes": { + "RegistryId": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecr-replicationconfiguration.html", + "Properties": { + "ReplicationConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecr-replicationconfiguration.html#cfn-ecr-replicationconfiguration-replicationconfiguration", + "Required": true, + "Type": "ReplicationConfiguration", + "UpdateType": "Mutable" + } + } + }, "AWS::ECR::Repository": { "Attributes": { "Arn": { @@ -65368,6 +65529,12 @@ "Required": false, "UpdateType": "Immutable" }, + "EnableExecuteCommand": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-service.html#cfn-ecs-service-enableexecutecommand", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, "HealthCheckGracePeriodSeconds": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-service.html#cfn-ecs-service-healthcheckgraceperiodseconds", "PrimitiveType": "Integer", @@ -65411,7 +65578,7 @@ "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-service.html#cfn-ecs-service-platformversion", "PrimitiveType": "String", "Required": false, - "UpdateType": "Immutable" + "UpdateType": "Mutable" }, "PropagateTags": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-service.html#cfn-ecs-service-propagatetags", @@ -66525,6 +66692,71 @@ } } }, + "AWS::ElastiCache::GlobalReplicationGroup": { + "Attributes": { + "GlobalReplicationGroupId": { + "PrimitiveType": "String" + }, + "Status": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-elasticache-globalreplicationgroup.html", + "Properties": { + "AutomaticFailoverEnabled": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-elasticache-globalreplicationgroup.html#cfn-elasticache-globalreplicationgroup-automaticfailoverenabled", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "CacheNodeType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-elasticache-globalreplicationgroup.html#cfn-elasticache-globalreplicationgroup-cachenodetype", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "EngineVersion": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-elasticache-globalreplicationgroup.html#cfn-elasticache-globalreplicationgroup-engineversion", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "GlobalNodeGroupCount": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-elasticache-globalreplicationgroup.html#cfn-elasticache-globalreplicationgroup-globalnodegroupcount", + "PrimitiveType": "Integer", + "Required": false, + "UpdateType": "Mutable" + }, + "GlobalReplicationGroupDescription": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-elasticache-globalreplicationgroup.html#cfn-elasticache-globalreplicationgroup-globalreplicationgroupdescription", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "GlobalReplicationGroupIdSuffix": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-elasticache-globalreplicationgroup.html#cfn-elasticache-globalreplicationgroup-globalreplicationgroupidsuffix", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "Members": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-elasticache-globalreplicationgroup.html#cfn-elasticache-globalreplicationgroup-members", + "DuplicatesAllowed": false, + "ItemType": "GlobalReplicationGroupMember", + "Required": true, + "Type": "List", + "UpdateType": "Mutable" + }, + "RegionalConfigurations": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-elasticache-globalreplicationgroup.html#cfn-elasticache-globalreplicationgroup-regionalconfigurations", + "DuplicatesAllowed": false, + "ItemType": "RegionalConfiguration", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, "AWS::ElastiCache::ParameterGroup": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticache-parameter-group.html", "Properties": { @@ -70841,6 +71073,102 @@ } } }, + "AWS::ImageBuilder::ContainerRecipe": { + "Attributes": { + "Arn": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-imagebuilder-containerrecipe.html", + "Properties": { + "Components": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-imagebuilder-containerrecipe.html#cfn-imagebuilder-containerrecipe-components", + "ItemType": "ComponentConfiguration", + "Required": true, + "Type": "List", + "UpdateType": "Immutable" + }, + "ContainerType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-imagebuilder-containerrecipe.html#cfn-imagebuilder-containerrecipe-containertype", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "Description": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-imagebuilder-containerrecipe.html#cfn-imagebuilder-containerrecipe-description", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "DockerfileTemplateData": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-imagebuilder-containerrecipe.html#cfn-imagebuilder-containerrecipe-dockerfiletemplatedata", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "DockerfileTemplateUri": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-imagebuilder-containerrecipe.html#cfn-imagebuilder-containerrecipe-dockerfiletemplateuri", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "ImageOsVersionOverride": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-imagebuilder-containerrecipe.html#cfn-imagebuilder-containerrecipe-imageosversionoverride", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "KmsKeyId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-imagebuilder-containerrecipe.html#cfn-imagebuilder-containerrecipe-kmskeyid", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-imagebuilder-containerrecipe.html#cfn-imagebuilder-containerrecipe-name", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "ParentImage": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-imagebuilder-containerrecipe.html#cfn-imagebuilder-containerrecipe-parentimage", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "PlatformOverride": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-imagebuilder-containerrecipe.html#cfn-imagebuilder-containerrecipe-platformoverride", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-imagebuilder-containerrecipe.html#cfn-imagebuilder-containerrecipe-tags", + "PrimitiveItemType": "String", + "Required": false, + "Type": "Map", + "UpdateType": "Immutable" + }, + "TargetRepository": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-imagebuilder-containerrecipe.html#cfn-imagebuilder-containerrecipe-targetrepository", + "Required": true, + "Type": "TargetContainerRepository", + "UpdateType": "Immutable" + }, + "Version": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-imagebuilder-containerrecipe.html#cfn-imagebuilder-containerrecipe-version", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "WorkingDirectory": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-imagebuilder-containerrecipe.html#cfn-imagebuilder-containerrecipe-workingdirectory", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + } + } + }, "AWS::ImageBuilder::DistributionConfiguration": { "Attributes": { "Arn": { @@ -72236,13 +72564,7 @@ "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iotwireless-destination.html#cfn-iotwireless-destination-name", "PrimitiveType": "String", "Required": true, - "UpdateType": "Mutable" - }, - "NextToken": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iotwireless-destination.html#cfn-iotwireless-destination-nexttoken", - "PrimitiveType": "String", - "Required": false, - "UpdateType": "Mutable" + "UpdateType": "Immutable" }, "RoleArn": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iotwireless-destination.html#cfn-iotwireless-destination-rolearn", @@ -72271,8 +72593,8 @@ }, "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iotwireless-deviceprofile.html", "Properties": { - "LoRaWANDeviceProfile": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iotwireless-deviceprofile.html#cfn-iotwireless-deviceprofile-lorawandeviceprofile", + "LoRaWAN": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iotwireless-deviceprofile.html#cfn-iotwireless-deviceprofile-lorawan", "Required": false, "Type": "LoRaWANDeviceProfile", "UpdateType": "Mutable" @@ -72283,12 +72605,6 @@ "Required": false, "UpdateType": "Mutable" }, - "NextToken": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iotwireless-deviceprofile.html#cfn-iotwireless-deviceprofile-nexttoken", - "PrimitiveType": "String", - "Required": false, - "UpdateType": "Mutable" - }, "Tags": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iotwireless-deviceprofile.html#cfn-iotwireless-deviceprofile-tags", "DuplicatesAllowed": false, @@ -72306,18 +72622,15 @@ }, "Id": { "PrimitiveType": "String" + }, + "LoRaWANResponse": { + "Type": "LoRaWANGetServiceProfileInfo" } }, "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iotwireless-serviceprofile.html", "Properties": { - "LoRaWANGetServiceProfileInfo": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iotwireless-serviceprofile.html#cfn-iotwireless-serviceprofile-lorawangetserviceprofileinfo", - "Required": false, - "Type": "LoRaWANGetServiceProfileInfo", - "UpdateType": "Mutable" - }, - "LoRaWANServiceProfile": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iotwireless-serviceprofile.html#cfn-iotwireless-serviceprofile-lorawanserviceprofile", + "LoRaWAN": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iotwireless-serviceprofile.html#cfn-iotwireless-serviceprofile-lorawan", "Required": false, "Type": "LoRaWANServiceProfile", "UpdateType": "Mutable" @@ -72328,12 +72641,6 @@ "Required": false, "UpdateType": "Mutable" }, - "NextToken": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iotwireless-serviceprofile.html#cfn-iotwireless-serviceprofile-nexttoken", - "PrimitiveType": "String", - "Required": false, - "UpdateType": "Mutable" - }, "Tags": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iotwireless-serviceprofile.html#cfn-iotwireless-serviceprofile-tags", "DuplicatesAllowed": false, @@ -72373,20 +72680,20 @@ "Required": true, "UpdateType": "Mutable" }, - "LoRaWANDevice": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iotwireless-wirelessdevice.html#cfn-iotwireless-wirelessdevice-lorawandevice", + "LastUplinkReceivedAt": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iotwireless-wirelessdevice.html#cfn-iotwireless-wirelessdevice-lastuplinkreceivedat", + "PrimitiveType": "String", "Required": false, - "Type": "LoRaWANDevice", "UpdateType": "Mutable" }, - "Name": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iotwireless-wirelessdevice.html#cfn-iotwireless-wirelessdevice-name", - "PrimitiveType": "String", + "LoRaWAN": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iotwireless-wirelessdevice.html#cfn-iotwireless-wirelessdevice-lorawan", "Required": false, + "Type": "LoRaWANDevice", "UpdateType": "Mutable" }, - "NextToken": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iotwireless-wirelessdevice.html#cfn-iotwireless-wirelessdevice-nexttoken", + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iotwireless-wirelessdevice.html#cfn-iotwireless-wirelessdevice-name", "PrimitiveType": "String", "Required": false, "UpdateType": "Mutable" @@ -72427,8 +72734,14 @@ "Required": false, "UpdateType": "Mutable" }, - "LoRaWANGateway": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iotwireless-wirelessgateway.html#cfn-iotwireless-wirelessgateway-lorawangateway", + "LastUplinkReceivedAt": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iotwireless-wirelessgateway.html#cfn-iotwireless-wirelessgateway-lastuplinkreceivedat", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "LoRaWAN": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iotwireless-wirelessgateway.html#cfn-iotwireless-wirelessgateway-lorawan", "Required": true, "Type": "LoRaWANGateway", "UpdateType": "Mutable" @@ -72439,12 +72752,6 @@ "Required": false, "UpdateType": "Mutable" }, - "NextToken": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iotwireless-wirelessgateway.html#cfn-iotwireless-wirelessgateway-nexttoken", - "PrimitiveType": "String", - "Required": false, - "UpdateType": "Mutable" - }, "Tags": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iotwireless-wirelessgateway.html#cfn-iotwireless-wirelessgateway-tags", "DuplicatesAllowed": false, @@ -73987,19 +74294,7 @@ "Arn": { "PrimitiveType": "String" }, - "CreatedAt": { - "PrimitiveType": "String" - }, - "LastUpdate": { - "Type": "LastUpdate" - }, - "Name": { - "PrimitiveType": "String" - }, - "ServiceRoleArn": { - "PrimitiveType": "String" - }, - "Status": { + "WebserverUrl": { "PrimitiveType": "String" } }, @@ -74039,7 +74334,7 @@ "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-mwaa-environment.html#cfn-mwaa-environment-kmskey", "PrimitiveType": "String", "Required": false, - "UpdateType": "Mutable" + "UpdateType": "Immutable" }, "LoggingConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-mwaa-environment.html#cfn-mwaa-environment-loggingconfiguration", @@ -74053,11 +74348,17 @@ "Required": false, "UpdateType": "Mutable" }, + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-mwaa-environment.html#cfn-mwaa-environment-name", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, "NetworkConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-mwaa-environment.html#cfn-mwaa-environment-networkconfiguration", "Required": false, "Type": "NetworkConfiguration", - "UpdateType": "Immutable" + "UpdateType": "Mutable" }, "PluginsS3ObjectVersion": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-mwaa-environment.html#cfn-mwaa-environment-pluginss3objectversion", @@ -74101,12 +74402,6 @@ "Required": false, "UpdateType": "Mutable" }, - "WebserverUrl": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-mwaa-environment.html#cfn-mwaa-environment-webserverurl", - "PrimitiveType": "String", - "Required": false, - "UpdateType": "Mutable" - }, "WeeklyMaintenanceWindowStart": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-mwaa-environment.html#cfn-mwaa-environment-weeklymaintenancewindowstart", "PrimitiveType": "String", @@ -81769,12 +82064,6 @@ "Required": true, "UpdateType": "Immutable" }, - "InferenceExecutionConfig": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sagemaker-model.html#cfn-sagemaker-model-inferenceexecutionconfig", - "Required": false, - "Type": "InferenceExecutionConfig", - "UpdateType": "Immutable" - }, "ModelName": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sagemaker-model.html#cfn-sagemaker-model-modelname", "PrimitiveType": "String", diff --git a/packages/@aws-cdk/cfnspec/spec-source/800_IoTWireless_ServicProfile_patch.json b/packages/@aws-cdk/cfnspec/spec-source/800_IoTWireless_ServicProfile_patch.json new file mode 100644 index 0000000000000..8fbd5d77b4619 --- /dev/null +++ b/packages/@aws-cdk/cfnspec/spec-source/800_IoTWireless_ServicProfile_patch.json @@ -0,0 +1,17 @@ +{ + "ResourceTypes": { + "patch": { + "description": "Invalid attribute type for LoRaWANResponse", + "operations": [ + { + "op": "add", + "path": "/AWS::IoTWireless::ServiceProfile/Attributes/LoRaWANResponse", + "value": { + "Type": "Map", + "PrimitiveItemType": "String" + } + } + ] + } + } +} diff --git a/packages/@aws-cdk/cloud-assembly-schema/NOTICE b/packages/@aws-cdk/cloud-assembly-schema/NOTICE index 17186448448a4..e2fc9a315f5ee 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/NOTICE +++ b/packages/@aws-cdk/cloud-assembly-schema/NOTICE @@ -28,6 +28,40 @@ SOFTWARE. ---------------- +** lru-cache - https://www.npmjs.com/package/lru-cache +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +---------------- + +** yallist - https://www.npmjs.com/package/yallist +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +---------------- + ** semver - https://www.npmjs.com/package/semver Copyright (c) Isaac Z. Schlueter and Contributors diff --git a/packages/@aws-cdk/cloud-assembly-schema/package.json b/packages/@aws-cdk/cloud-assembly-schema/package.json index 39c0254c1cc5d..724512a094826 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/package.json +++ b/packages/@aws-cdk/cloud-assembly-schema/package.json @@ -58,13 +58,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/jest": "^26.0.15", + "@types/jest": "^26.0.20", "@types/mock-fs": "^4.13.0", "cdk-build-tools": "0.0.0", "jest": "^26.6.3", "mock-fs": "^4.13.0", "pkglint": "0.0.0", - "typescript-json-schema": "^0.47.0" + "typescript-json-schema": "^0.49.0" }, "repository": { "url": "https://github.com/aws/aws-cdk.git", @@ -89,7 +89,7 @@ }, "dependencies": { "jsonschema": "^1.4.0", - "semver": "^7.3.2" + "semver": "^7.3.4" }, "awscdkio": { "announce": false @@ -97,5 +97,8 @@ "maturity": "stable", "cdk-build": { "jest": true + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/cloudformation-diff/lib/diff/util.ts b/packages/@aws-cdk/cloudformation-diff/lib/diff/util.ts index 763afbecaf79e..f4c452499ee09 100644 --- a/packages/@aws-cdk/cloudformation-diff/lib/diff/util.ts +++ b/packages/@aws-cdk/cloudformation-diff/lib/diff/util.ts @@ -23,7 +23,8 @@ export function deepEqual(lvalue: any, rvalue: any): boolean { } // allows a numeric 10 and a literal "10" to be equivalent; // this is consistent with CloudFormation. - if (((typeof lvalue === 'string') || (typeof rvalue === 'string')) && (parseFloat(lvalue) === parseFloat(rvalue))) { + if ((typeof lvalue === 'string' || typeof rvalue === 'string') && + safeParseFloat(lvalue) === safeParseFloat(rvalue)) { return true; } if (typeof lvalue !== typeof rvalue) { return false; } @@ -132,3 +133,20 @@ export function unionOf(lv: string[] | Set, rv: string[] | Set): } return new Array(...result); } + +/** + * A parseFloat implementation that does the right thing for + * strings like '0.0.0' + * (for which JavaScript's parseFloat() returns 0). + */ +function safeParseFloat(str: string): number { + const ret = parseFloat(str); + if (ret === 0) { + // if the str is exactly '0', that's OK; + // but parseFloat() also returns 0 for things like '0.0'; + // in this case, return NaN, so we'll fall back to string comparison + return str === '0' ? ret : NaN; + } else { + return ret; + } +} diff --git a/packages/@aws-cdk/cloudformation-diff/package.json b/packages/@aws-cdk/cloudformation-diff/package.json index 089a802613048..2a1705ab876fd 100644 --- a/packages/@aws-cdk/cloudformation-diff/package.json +++ b/packages/@aws-cdk/cloudformation-diff/package.json @@ -29,14 +29,14 @@ "table": "^6.0.7" }, "devDependencies": { - "@types/jest": "^26.0.15", + "@types/jest": "^26.0.20", "@types/string-width": "^4.0.1", - "@types/table": "^5.0.0", + "@types/table": "^6.0.0", "cdk-build-tools": "0.0.0", - "fast-check": "^2.11.0", + "fast-check": "^2.13.0", "jest": "^26.6.3", "pkglint": "0.0.0", - "ts-jest": "^26.4.4" + "ts-jest": "^26.5.1" }, "repository": { "url": "https://github.com/aws/aws-cdk.git", @@ -55,5 +55,8 @@ "maturity": "experimental", "cdk-build": { "jest": true + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/cloudformation-diff/test/diff-template.test.ts b/packages/@aws-cdk/cloudformation-diff/test/diff-template.test.ts index 8110922a41790..bde05ef09056e 100644 --- a/packages/@aws-cdk/cloudformation-diff/test/diff-template.test.ts +++ b/packages/@aws-cdk/cloudformation-diff/test/diff-template.test.ts @@ -375,6 +375,34 @@ test('adding and removing quotes from a numeric property causes no changes', () expect(differences.resources.differenceCount).toBe(0); }); +test('versions are correctly detected as not numbers', () => { + const currentTemplate = { + Resources: { + ImageBuilderComponent: { + Type: 'AWS::ImageBuilder::Component', + Properties: { + Platform: 'Linux', + Version: '0.0.1', + }, + }, + }, + }; + const newTemplate = { + Resources: { + ImageBuilderComponent: { + Type: 'AWS::ImageBuilder::Component', + Properties: { + Platform: 'Linux', + Version: '0.0.2', + }, + }, + }, + }; + + const differences = diffTemplate(currentTemplate, newTemplate); + expect(differences.resources.differenceCount).toBe(1); +}); + test('single element arrays are equivalent to the single element in DependsOn expressions', () => { // GIVEN const currentTemplate = { diff --git a/packages/@aws-cdk/cloudformation-include/package.json b/packages/@aws-cdk/cloudformation-include/package.json index 1732065bd3a96..457f11834e88a 100644 --- a/packages/@aws-cdk/cloudformation-include/package.json +++ b/packages/@aws-cdk/cloudformation-include/package.json @@ -362,12 +362,12 @@ }, "devDependencies": { "@aws-cdk/assert": "0.0.0", - "@types/jest": "^26.0.15", + "@types/jest": "^26.0.20", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "jest": "^26.6.3", "pkglint": "0.0.0", - "ts-jest": "^26.4.4" + "ts-jest": "^26.5.1" }, "keywords": [ "aws", @@ -393,5 +393,8 @@ "maturity": "stable", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/core/.vscode/tasks.json b/packages/@aws-cdk/core/.vscode/tasks.json new file mode 100644 index 0000000000000..3726d98503637 --- /dev/null +++ b/packages/@aws-cdk/core/.vscode/tasks.json @@ -0,0 +1,16 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "watch", + "problemMatcher": [ + "$tsc-watch" + ], + "group": "build", + "label": "npm: watch", + "detail": "cdk-watch", + "isBackground": true + } + ] +} \ No newline at end of file diff --git a/packages/@aws-cdk/core/README.md b/packages/@aws-cdk/core/README.md index 880aea79a55fc..0e0b38943a803 100644 --- a/packages/@aws-cdk/core/README.md +++ b/packages/@aws-cdk/core/README.md @@ -141,7 +141,7 @@ DEPLOYMENT 1: break the relationship - Make sure `stack2` no longer references `bucket.bucketName` (maybe the consumer stack now uses its own bucket, or it writes to an AWS DynamoDB table, or maybe you just remove the Lambda Function altogether). -- In the `stack1` class, call `this.exportAttribute(this.bucket.bucketName)`. This +- In the `stack1` class, call `this.exportValue(this.bucket.bucketName)`. This will make sure the CloudFormation Export continues to exist while the relationship between the two stacks is being broken. - Deploy (this will effectively only change the `stack2`, but it's safe to deploy both). @@ -149,7 +149,7 @@ DEPLOYMENT 1: break the relationship DEPLOYMENT 2: remove the resource - You are now free to remove the `bucket` resource from `stack1`. -- Don't forget to remove the `exportAttribute()` call as well. +- Don't forget to remove the `exportValue()` call as well. - Deploy again (this time only the `stack1` will be changed -- the bucket will be deleted). ## Durations diff --git a/packages/@aws-cdk/core/lib/asset-staging.ts b/packages/@aws-cdk/core/lib/asset-staging.ts index 66c65e3d14864..17fd89781004a 100644 --- a/packages/@aws-cdk/core/lib/asset-staging.ts +++ b/packages/@aws-cdk/core/lib/asset-staging.ts @@ -5,8 +5,8 @@ import * as cxapi from '@aws-cdk/cx-api'; import { Construct } from 'constructs'; import * as fs from 'fs-extra'; import * as minimatch from 'minimatch'; -import { AssetHashType, AssetOptions } from './assets'; -import { BundlingOptions } from './bundling'; +import { AssetHashType, AssetOptions, FileAssetPackaging } from './assets'; +import { BundlingOptions, BundlingOutput } from './bundling'; import { FileSystem, FingerprintOptions } from './fs'; import { Names } from './names'; import { Cache } from './private/cache'; @@ -17,6 +17,8 @@ import { Stage } from './stage'; // eslint-disable-next-line import { Construct as CoreConstruct } from './construct-compat'; +const ARCHIVE_EXTENSIONS = ['.zip', '.jar']; + /** * A previously staged asset */ @@ -138,6 +140,9 @@ export class AssetStaging extends CoreConstruct { private readonly cacheKey: string; + private _packaging = FileAssetPackaging.ZIP_DIRECTORY; + private _isArchive = true; + constructor(scope: Construct, id: string, props: AssetStagingProps) { super(scope, id); @@ -203,6 +208,20 @@ export class AssetStaging extends CoreConstruct { return this.assetHash; } + /** + * How this asset should be packaged. + */ + public get packaging(): FileAssetPackaging { + return this._packaging; + } + + /** + * Whether this asset is an archive (zip or jar). + */ + public get isArchive(): boolean { + return this._isArchive; + } + /** * Return the path to the staged asset, relative to the Cloud Assembly (manifest) directory of the given stack * @@ -281,11 +300,16 @@ export class AssetStaging extends CoreConstruct { const bundleDir = this.determineBundleDir(this.assetOutdir, assetHash); this.bundle(bundling, bundleDir); + // Check bundling output content and determine if we will need to archive + const bundlingOutputType = bundling.outputType ?? BundlingOutput.AUTO_DISCOVER; + const bundledAsset = determineBundledAsset(bundleDir, bundlingOutputType); + this._packaging = bundledAsset.packaging; + // Calculate assetHash afterwards if we still must - assetHash = assetHash ?? this.calculateHash(this.hashType, bundling, bundleDir); - const stagedPath = path.resolve(this.assetOutdir, renderAssetFilename(assetHash)); + assetHash = assetHash ?? this.calculateHash(this.hashType, bundling, bundledAsset.path); + const stagedPath = path.resolve(this.assetOutdir, renderAssetFilename(assetHash, bundledAsset.extension)); - this.stageAsset(bundleDir, stagedPath, 'move'); + this.stageAsset(bundledAsset.path, stagedPath, 'move'); return { assetHash, stagedPath }; } @@ -323,6 +347,8 @@ export class AssetStaging extends CoreConstruct { const stat = fs.statSync(sourcePath); if (stat.isFile()) { fs.copyFileSync(sourcePath, targetPath); + this._packaging = FileAssetPackaging.FILE; + this._isArchive = ARCHIVE_EXTENSIONS.includes(path.extname(sourcePath).toLowerCase()); } else if (stat.isDirectory()) { fs.mkdirSync(targetPath); FileSystem.copyDirectory(sourcePath, targetPath, this.fingerprintOptions); @@ -502,3 +528,57 @@ function sortObject(object: { [key: string]: any }): { [key: string]: any } { } return ret; } + +/** + * Returns the single archive file of a directory or undefined + */ +function singleArchiveFile(directory: string): string | undefined { + if (!fs.existsSync(directory)) { + throw new Error(`Directory ${directory} does not exist.`); + } + + if (!fs.statSync(directory).isDirectory()) { + throw new Error(`${directory} is not a directory.`); + } + + const content = fs.readdirSync(directory); + if (content.length === 1) { + const file = path.join(directory, content[0]); + const extension = path.extname(content[0]).toLowerCase(); + if (fs.statSync(file).isFile() && ARCHIVE_EXTENSIONS.includes(extension)) { + return file; + } + } + + return undefined; +} + +interface BundledAsset { + path: string, + packaging: FileAssetPackaging, + extension?: string +} + +/** + * Returns the bundled asset to use based on the content of the bundle directory + * and the type of output. + */ +function determineBundledAsset(bundleDir: string, outputType: BundlingOutput): BundledAsset { + const archiveFile = singleArchiveFile(bundleDir); + + // auto-discover means that if there is an archive file, we take it as the + // bundle, otherwise, we will archive here. + if (outputType === BundlingOutput.AUTO_DISCOVER) { + outputType = archiveFile ? BundlingOutput.ARCHIVED : BundlingOutput.NOT_ARCHIVED; + } + + switch (outputType) { + case BundlingOutput.NOT_ARCHIVED: + return { path: bundleDir, packaging: FileAssetPackaging.ZIP_DIRECTORY }; + case BundlingOutput.ARCHIVED: + if (!archiveFile) { + throw new Error('Bundling output directory is expected to include only a single .zip or .jar file when `output` is set to `ARCHIVED`'); + } + return { path: archiveFile, packaging: FileAssetPackaging.FILE, extension: path.extname(archiveFile) }; + } +} diff --git a/packages/@aws-cdk/core/lib/bundling.ts b/packages/@aws-cdk/core/lib/bundling.ts index b1247fd913ea0..57885ceeca05c 100644 --- a/packages/@aws-cdk/core/lib/bundling.ts +++ b/packages/@aws-cdk/core/lib/bundling.ts @@ -79,6 +79,41 @@ export interface BundlingOptions { * @experimental */ readonly local?: ILocalBundling; + + /** + * The type of output that this bundling operation is producing. + * + * @default BundlingOutput.AUTO_DISCOVER + * + * @experimental + */ + readonly outputType?: BundlingOutput; +} + +/** + * The type of output that a bundling operation is producing. + * + * @experimental + */ +export enum BundlingOutput { + /** + * The bundling output directory includes a single .zip or .jar file which + * will be used as the final bundle. If the output directory does not + * include exactly a single archive, bundling will fail. + */ + ARCHIVED = 'archived', + + /** + * The bundling output directory contains one or more files which will be + * archived and uploaded as a .zip file to S3. + */ + NOT_ARCHIVED = 'not-archived', + + /** + * If the bundling output directory contains a single archive file (zip or jar) + * it will not be zipped. Otherwise the bundling output will be zipped. + */ + AUTO_DISCOVER = 'auto-discover', } /** diff --git a/packages/@aws-cdk/core/lib/private/runtime-info.ts b/packages/@aws-cdk/core/lib/private/runtime-info.ts index 34a4129abe491..b0cf266e8e11d 100644 --- a/packages/@aws-cdk/core/lib/private/runtime-info.ts +++ b/packages/@aws-cdk/core/lib/private/runtime-info.ts @@ -3,9 +3,9 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import { major as nodeMajorVersion } from './node-version'; // list of NPM scopes included in version reporting e.g. @aws-cdk and @aws-solutions-konstruk -const WHITELIST_SCOPES = ['@aws-cdk', '@aws-cdk-containers', '@aws-solutions-konstruk', '@aws-solutions-constructs', '@amzn']; +const ALLOWED_SCOPES = ['@aws-cdk', '@aws-cdk-containers', '@aws-solutions-konstruk', '@aws-solutions-constructs', '@amzn']; // list of NPM packages included in version reporting -const WHITELIST_PACKAGES = ['aws-rfdk', 'aws-cdk-lib']; +const ALLOWED_PACKAGES = ['aws-rfdk', 'aws-cdk-lib', 'monocdk']; /** * Returns a list of loaded modules and their versions. @@ -20,15 +20,15 @@ export function collectRuntimeInformation(): cxschema.RuntimeInfo { } } - // include only libraries that are in the whitelistLibraries list + // include only libraries that are in the allowlistLibraries list for (const name of Object.keys(libraries)) { let foundMatch = false; - for (const scope of WHITELIST_SCOPES) { + for (const scope of ALLOWED_SCOPES) { if (name.startsWith(`${scope}/`)) { foundMatch = true; } } - foundMatch = foundMatch || WHITELIST_PACKAGES.includes(name); + foundMatch = foundMatch || ALLOWED_PACKAGES.includes(name); if (!foundMatch) { delete libraries[name]; diff --git a/packages/@aws-cdk/core/lib/private/token-map.ts b/packages/@aws-cdk/core/lib/private/token-map.ts index 1b4ea48c04440..2523037724ef0 100644 --- a/packages/@aws-cdk/core/lib/private/token-map.ts +++ b/packages/@aws-cdk/core/lib/private/token-map.ts @@ -34,7 +34,34 @@ export class TokenMap { private readonly stringTokenMap = new Map(); private readonly numberTokenMap = new Map(); - private tokenCounter = 0; + + /** + * Counter to assign unique IDs to tokens + * + * Start at a random number to prevent people from accidentally taking + * dependencies on token values between runs. + * + * This is most prominent in tests, where people will write: + * + * ```ts + * sha256(JSON.stringify({ ...some structure that can contain tokens ... })) + * ``` + * + * This should have been: + * + * ```ts + * sha256(JSON.stringify(stack.resolve({ ...some structure that can contain tokens ... }))) + * ``` + * + * The hash value is hard to inspect for correctness. It will LOOK consistent + * during testing, but will break as soon as someone stringifies another + * token before the run. + * + * By changing the starting number for tokens, we ensure that the hash is almost + * guaranteed to be different during a few test runs, so the hashing of unresolved + * tokens can be detected. + */ + private tokenCounter = Math.floor(Math.random() * 10); /** * Generate a unique string for this Token, returning a key diff --git a/packages/@aws-cdk/core/lib/private/tree-metadata.ts b/packages/@aws-cdk/core/lib/private/tree-metadata.ts index eb18252a0e8bd..caa5c37a5940d 100644 --- a/packages/@aws-cdk/core/lib/private/tree-metadata.ts +++ b/packages/@aws-cdk/core/lib/private/tree-metadata.ts @@ -9,6 +9,13 @@ import { IInspectable, TreeInspector } from '../tree'; const FILE_PATH = 'tree.json'; +/** + * Symbol for accessing jsii runtime information + * + * Introduced in jsii 1.19.0, cdk 1.90.0. + */ +const JSII_RUNTIME_SYMBOL = Symbol.for('jsii.rtti'); + /** * Construct that is automatically attached to the top-level `App`. * This generates, as part of synthesis, a file containing the construct tree and the metadata for each node in the tree. @@ -41,11 +48,14 @@ export class TreeMetadata extends Construct { .filter((child) => child !== undefined) .reduce((map, child) => Object.assign(map, { [child!.id]: child }), {}); + const jsiiRuntimeInfo = Object.getPrototypeOf(construct).constructor[JSII_RUNTIME_SYMBOL]; + const node: Node = { id: construct.node.id || 'App', path: construct.node.path, children: Object.keys(childrenMap).length === 0 ? undefined : childrenMap, attributes: this.synthAttributes(construct), + constructInfo: constructInfoFromRuntimeInfo(jsiiRuntimeInfo), }; lookup[node.path] = node; @@ -86,9 +96,32 @@ export class TreeMetadata extends Construct { } } +function constructInfoFromRuntimeInfo(jsiiRuntimeInfo: any): ConstructInfo | undefined { + if (typeof jsiiRuntimeInfo === 'object' + && jsiiRuntimeInfo !== null + && typeof jsiiRuntimeInfo.fqn === 'string' + && typeof jsiiRuntimeInfo.version === 'string') { + return { fqn: jsiiRuntimeInfo.fqn, version: jsiiRuntimeInfo.version }; + } + return undefined; +} + interface Node { - id: string; - path: string; - children?: { [key: string]: Node }; - attributes?: { [key: string]: any }; + readonly id: string; + readonly path: string; + readonly children?: { [key: string]: Node }; + readonly attributes?: { [key: string]: any }; + + /** + * Information on the construct class that led to this node, if available + */ + readonly constructInfo?: ConstructInfo; +} + +/** + * Source information on a construct (class fqn and version) + */ +interface ConstructInfo { + readonly fqn: string; + readonly version: string; } diff --git a/packages/@aws-cdk/core/lib/resource.ts b/packages/@aws-cdk/core/lib/resource.ts index 917a2c442dcf1..6445fe718c547 100644 --- a/packages/@aws-cdk/core/lib/resource.ts +++ b/packages/@aws-cdk/core/lib/resource.ts @@ -4,11 +4,12 @@ import { IConstruct, Construct as CoreConstruct } from './construct-compat'; import { Construct } from 'constructs'; import { ArnComponents } from './arn'; -import { Lazy } from './lazy'; +import { IStringProducer, Lazy } from './lazy'; import { generatePhysicalName, isGeneratedWhenNeededMarker } from './private/physical-name-generator'; import { IResolveContext } from './resolvable'; import { Stack } from './stack'; -import { Token } from './token'; +import { Token, Tokenization } from './token'; +import { Reference } from './reference'; /** * Represents the environment a given resource lives in. @@ -181,7 +182,7 @@ export abstract class Resource extends CoreConstruct implements IResource { * @experimental */ protected getResourceNameAttribute(nameAttr: string) { - return Lazy.uncachedString({ + return mimicReference(nameAttr, { produce: (context: IResolveContext) => { const consumingStack = Stack.of(context.scope); @@ -214,8 +215,8 @@ export abstract class Resource extends CoreConstruct implements IResource { * @experimental */ protected getResourceArnAttribute(arnAttr: string, arnComponents: ArnComponents) { - return Token.asString({ - resolve: (context: IResolveContext) => { + return mimicReference(arnAttr, { + produce: (context: IResolveContext) => { const consumingStack = Stack.of(context.scope); if (this.stack.environment !== consumingStack.environment) { this._enableCrossEnvironment(); @@ -227,3 +228,28 @@ export abstract class Resource extends CoreConstruct implements IResource { }); } } + +/** + * Produce a Lazy that is also a Reference (if the base value is a Reference). + * + * If the given value is a Reference (or resolves to a Reference), return a new + * Reference that mimics the same target and display name, but resolves using + * the logic of the passed lazy. + * + * If the given value is NOT a Reference, just return a simple Lazy. + */ +function mimicReference(refSource: any, producer: IStringProducer): string { + const reference = Tokenization.reverse(refSource, { + // If this is an ARN concatenation, just fail to extract a reference. + failConcat: false, + }); + if (!Reference.isReference(reference)) { + return Lazy.uncachedString(producer); + } + + return Token.asString(new class extends Reference { + resolve(context: IResolveContext) { + return producer.produce(context); + } + }(reference, reference.target, reference.displayName)); +} \ No newline at end of file diff --git a/packages/@aws-cdk/core/lib/token.ts b/packages/@aws-cdk/core/lib/token.ts index f92a2560cac7c..e17227d0f8ef5 100644 --- a/packages/@aws-cdk/core/lib/token.ts +++ b/packages/@aws-cdk/core/lib/token.ts @@ -164,9 +164,16 @@ export class Tokenization { * * In case of a string, the string must not be a concatenation. */ - public static reverse(x: any): IResolvable | undefined { + public static reverse(x: any, options: ReverseOptions = {}): IResolvable | undefined { if (Tokenization.isResolvable(x)) { return x; } - if (typeof x === 'string') { return Tokenization.reverseCompleteString(x); } + if (typeof x === 'string') { + if (options.failConcat === false) { + // Handle this specially because reverseCompleteString might fail + const fragments = Tokenization.reverseString(x); + return fragments.length === 1 ? fragments.firstToken : undefined; + } + return Tokenization.reverseCompleteString(x); + } if (Array.isArray(x)) { return Tokenization.reverseList(x); } if (typeof x === 'number') { return Tokenization.reverseNumber(x); } return undefined; @@ -220,6 +227,20 @@ export class Tokenization { } } +/** + * Options for the 'reverse()' operation + */ +export interface ReverseOptions { + /** + * Fail if the given string is a concatenation + * + * If `false`, just return `undefined`. + * + * @default true + */ + readonly failConcat?: boolean; +} + /** * Options to the resolve() operation * diff --git a/packages/@aws-cdk/core/package.json b/packages/@aws-cdk/core/package.json index d1245e62faf73..d18faeea26e0a 100644 --- a/packages/@aws-cdk/core/package.json +++ b/packages/@aws-cdk/core/package.json @@ -176,16 +176,16 @@ "devDependencies": { "@types/lodash": "^4.14.168", "@types/minimatch": "^3.0.3", - "@types/node": "^10.17.51", - "@types/sinon": "^9.0.9", + "@types/node": "^10.17.52", + "@types/sinon": "^9.0.10", "cdk-build-tools": "0.0.0", "cfn2ts": "0.0.0", - "fast-check": "^2.11.0", + "fast-check": "^2.13.0", "lodash": "^4.17.20", "nodeunit-shim": "0.0.0", "pkglint": "0.0.0", - "sinon": "^9.2.1", - "ts-mock-imports": "^1.3.1" + "sinon": "^9.2.4", + "ts-mock-imports": "^1.3.3" }, "dependencies": { "@aws-cdk/cloud-assembly-schema": "0.0.0", @@ -217,5 +217,8 @@ "awscdkio": { "announce": false }, - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/core/test/archive/archive.zip b/packages/@aws-cdk/core/test/archive/archive.zip new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/packages/@aws-cdk/core/test/cross-environment-token.test.ts b/packages/@aws-cdk/core/test/cross-environment-token.test.ts index 4ab55922369e4..a0d833996121c 100644 --- a/packages/@aws-cdk/core/test/cross-environment-token.test.ts +++ b/packages/@aws-cdk/core/test/cross-environment-token.test.ts @@ -244,6 +244,36 @@ nodeunitShim({ }, }); +test.each([undefined, 'SomeName'])('stack.exportValue() on name attributes with PhysicalName=%s', physicalName => { + // Check that automatic exports and manual exports look the same + // GIVEN - auto + const appA = new App(); + const producerA = new Stack(appA, 'Producer'); + const resourceA = new MyResource(producerA, 'Resource', physicalName); + + const consumerA = new Stack(appA, 'Consumer'); + new CfnOutput(consumerA, 'ConsumeName', { value: resourceA.name }); + new CfnOutput(consumerA, 'ConsumeArn', { value: resourceA.arn }); + + // WHEN - manual + const appM = new App(); + const producerM = new Stack(appM, 'Producer'); + const resourceM = new MyResource(producerM, 'Resource', physicalName); + producerM.exportValue(resourceM.name); + producerM.exportValue(resourceM.arn); + + // THEN - producers are the same + const templateA = appA.synth().getStackByName(producerA.stackName).template; + const templateM = appM.synth().getStackByName(producerM.stackName).template; + + expect(templateA).toEqual(templateM); +}); + +test('can instantiate resource with constructed physicalname ARN', () => { + const stack = new Stack(); + new MyResourceWithConstructedArnAttribute(stack, 'Resource'); +}); + class MyResource extends Resource { public readonly arn: string; public readonly name: string; @@ -251,20 +281,54 @@ class MyResource extends Resource { constructor(scope: Construct, id: string, physicalName?: string) { super(scope, id, { physicalName }); - this.arn = this.getResourceArnAttribute('simple-arn', { + const res = new CfnResource(this, 'Resource', { + type: 'My::Resource', + properties: { + resourceName: this.physicalName, + }, + }); + + this.name = this.getResourceNameAttribute(res.ref.toString()); + this.arn = this.getResourceArnAttribute(res.getAtt('Arn').toString(), { region: '', account: '', resource: 'my-resource', resourceName: this.physicalName, service: 'myservice', }); - this.name = this.getResourceNameAttribute('simple-name'); + } +} - new CfnResource(this, 'Resource', { +/** + * Some resources build their own Arn attribute by constructing strings + * + * This will be because the L1 doesn't expose a `{ Fn::GetAtt: ['Arn'] }`. + * + * They won't be able to `exportValue()` it, but it shouldn't crash. + */ +class MyResourceWithConstructedArnAttribute extends Resource { + public readonly arn: string; + public readonly name: string; + + constructor(scope: Construct, id: string, physicalName?: string) { + super(scope, id, { physicalName }); + + const res = new CfnResource(this, 'Resource', { type: 'My::Resource', properties: { resourceName: this.physicalName, }, }); + + this.name = this.getResourceNameAttribute(res.ref.toString()); + this.arn = this.getResourceArnAttribute(Stack.of(this).formatArn({ + resource: 'my-resource', + resourceName: res.ref.toString(), + service: 'myservice', + }), { + resource: 'my-resource', + resourceName: this.physicalName, + service: 'myservice', + }); } } diff --git a/packages/@aws-cdk/core/test/docker-stub.sh b/packages/@aws-cdk/core/test/docker-stub.sh index fe48e93d4a207..94f806f69a120 100755 --- a/packages/@aws-cdk/core/test/docker-stub.sh +++ b/packages/@aws-cdk/core/test/docker-stub.sh @@ -24,5 +24,18 @@ if echo "$@" | grep "DOCKER_STUB_SUCCESS"; then exit 0 fi -echo "Docker mock only supports one of the following commands: DOCKER_STUB_SUCCESS_NO_OUTPUT,DOCKER_STUB_FAIL,DOCKER_STUB_SUCCESS" +if echo "$@" | grep "DOCKER_STUB_MULTIPLE_FILES"; then + outdir=$(echo "$@" | xargs -n1 | grep "/asset-output" | head -n1 | cut -d":" -f1) + touch ${outdir}/test1.txt + touch ${outdir}/test2.txt + exit 0 +fi + +if echo "$@" | grep "DOCKER_STUB_SINGLE_ARCHIVE"; then + outdir=$(echo "$@" | xargs -n1 | grep "/asset-output" | head -n1 | cut -d":" -f1) + touch ${outdir}/test.zip + exit 0 +fi + +echo "Docker mock only supports one of the following commands: DOCKER_STUB_SUCCESS_NO_OUTPUT,DOCKER_STUB_FAIL,DOCKER_STUB_SUCCESS,DOCKER_STUB_MULTIPLE_FILES,DOCKER_SINGLE_ARCHIVE" exit 1 diff --git a/packages/@aws-cdk/core/test/private/tree-metadata.test.ts b/packages/@aws-cdk/core/test/private/tree-metadata.test.ts index f62df683c422a..b7173aca585ec 100644 --- a/packages/@aws-cdk/core/test/private/tree-metadata.test.ts +++ b/packages/@aws-cdk/core/test/private/tree-metadata.test.ts @@ -2,10 +2,10 @@ import * as fs from 'fs'; import * as path from 'path'; import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import { nodeunitShim, Test } from 'nodeunit-shim'; -import { App, CfnParameter, CfnResource, Construct, Lazy, Stack, TreeInspector } from '../../lib/index'; +import { App, CfnParameter, CfnResource, Construct as CfnConstruct, Lazy, Stack, TreeInspector } from '../../lib/index'; abstract class AbstractCfnResource extends CfnResource { - constructor(scope: Construct, id: string) { + constructor(scope: CfnConstruct, id: string) { super(scope, id, { type: 'CDK::UnitTest::MyCfnResource', }); @@ -24,7 +24,7 @@ nodeunitShim({ const app = new App(); const stack = new Stack(app, 'mystack'); - new Construct(stack, 'myconstruct'); + new CfnConstruct(stack, 'myconstruct'); const assembly = app.synth(); const treeArtifact = assembly.tree(); @@ -32,26 +32,26 @@ nodeunitShim({ test.deepEqual(readJson(assembly.directory, treeArtifact!.file), { version: 'tree-0.1', - tree: { + tree: expect.objectContaining({ id: 'App', path: '', children: { - Tree: { + Tree: expect.objectContaining({ id: 'Tree', path: 'Tree', - }, - mystack: { + }), + mystack: expect.objectContaining({ id: 'mystack', path: 'mystack', children: { - myconstruct: { + myconstruct: expect.objectContaining({ id: 'myconstruct', path: 'mystack/myconstruct', - }, + }), }, - }, + }), }, - }, + }), }); test.done(); }, @@ -80,19 +80,19 @@ nodeunitShim({ test.deepEqual(readJson(assembly.directory, treeArtifact!.file), { version: 'tree-0.1', - tree: { + tree: expect.objectContaining({ id: 'App', path: '', children: { - Tree: { + Tree: expect.objectContaining({ id: 'Tree', path: 'Tree', - }, - mystack: { + }), + mystack: expect.objectContaining({ id: 'mystack', path: 'mystack', children: { - mycfnresource: { + mycfnresource: expect.objectContaining({ id: 'mycfnresource', path: 'mystack/mycfnresource', attributes: { @@ -106,15 +106,72 @@ nodeunitShim({ }, }, }, - }, + }), }, - }, + }), }, - }, + }), }); test.done(); }, + 'tree metadata has construct class & version in there'(test: Test) { + // The runtime metadata this test relies on is only available if the most + // recent compile has happened using 'jsii', as the jsii compiler injects + // this metadata. + // + // If the most recent compile was using 'tsc', the metadata will not have + // been injected, and the test will fail. + // + // People may choose to run `tsc` directly (instead of `yarn build` for + // example) to escape the additional TSC compilation time that is necessary + // to run 'eslint', or the additional time that 'jsii' needs to analyze the + // type system), this test is allowed to fail if we're not running on CI. + // + // If the compile of this library has been done using `tsc`, the runtime + // information will always find `constructs.Construct` as the construct + // identifier, since `constructs` will have had a release build done using `jsii`. + // + // If this test is running on CodeBuild, we will require that the more specific + // class names are found. If this test is NOT running on CodeBuild, we will + // allow the specific class name (for a 'jsii' build) or the generic + // 'constructs.Construct' class name (for a 'tsc' build). + const app = new App(); + + const stack = new Stack(app, 'mystack'); + new CfnResource(stack, 'myconstruct', { type: 'Aws::Some::Resource' }); + + const assembly = app.synth(); + const treeArtifact = assembly.tree(); + test.ok(treeArtifact); + + const codeBuild = !!process.env.CODEBUILD_BUILD_ID; + + test.deepEqual(readJson(assembly.directory, treeArtifact!.file), { + version: 'tree-0.1', + tree: expect.objectContaining({ + children: expect.objectContaining({ + mystack: expect.objectContaining({ + constructInfo: { + fqn: expect.stringMatching(codeBuild ? /\bStack$/ : /\bStack$|^constructs.Construct$/), + version: expect.any(String), + }, + children: { + myconstruct: expect.objectContaining({ + constructInfo: { + fqn: expect.stringMatching(codeBuild ? /\bCfnResource$/ : /\bCfnResource$|^constructs.Construct$/), + version: expect.any(String), + }, + }), + }, + }), + }), + }), + }); + + test.done(); + }, + 'token resolution & cfn parameter'(test: Test) { const app = new App(); const stack = new Stack(app, 'mystack'); @@ -137,23 +194,23 @@ nodeunitShim({ test.deepEqual(readJson(assembly.directory, treeArtifact!.file), { version: 'tree-0.1', - tree: { + tree: expect.objectContaining({ id: 'App', path: '', children: { - Tree: { + Tree: expect.objectContaining({ id: 'Tree', path: 'Tree', - }, - mystack: { + }), + mystack: expect.objectContaining({ id: 'mystack', path: 'mystack', children: { - mycfnparam: { + mycfnparam: expect.objectContaining({ id: 'mycfnparam', path: 'mystack/mycfnparam', - }, - mycfnresource: { + }), + mycfnresource: expect.objectContaining({ id: 'mycfnresource', path: 'mystack/mycfnresource', attributes: { @@ -163,11 +220,11 @@ nodeunitShim({ cfnparamkey: { Ref: 'mycfnparam' }, }, }, - }, + }), }, - }, + }), }, - }, + }), }); test.done(); }, @@ -176,7 +233,7 @@ nodeunitShim({ class MyFirstResource extends AbstractCfnResource { public readonly lazykey: string; - constructor(scope: Construct, id: string) { + constructor(scope: CfnConstruct, id: string) { super(scope, id); this.lazykey = Lazy.string({ produce: () => 'LazyResolved!' }); } @@ -191,7 +248,7 @@ nodeunitShim({ class MySecondResource extends AbstractCfnResource { public readonly myprop: string; - constructor(scope: Construct, id: string, myprop: string) { + constructor(scope: CfnConstruct, id: string, myprop: string) { super(scope, id); this.myprop = myprop; } @@ -215,19 +272,19 @@ nodeunitShim({ test.deepEqual(readJson(assembly.directory, treeArtifact!.file), { version: 'tree-0.1', - tree: { + tree: expect.objectContaining({ id: 'App', path: '', children: { - Tree: { + Tree: expect.objectContaining({ id: 'Tree', path: 'Tree', - }, - myfirststack: { + }), + myfirststack: expect.objectContaining({ id: 'myfirststack', path: 'myfirststack', children: { - myfirstresource: { + myfirstresource: expect.objectContaining({ id: 'myfirstresource', path: 'myfirststack/myfirstresource', attributes: { @@ -236,14 +293,14 @@ nodeunitShim({ lazykey: 'LazyResolved!', }, }, - }, + }), }, - }, - mysecondstack: { + }), + mysecondstack: expect.objectContaining({ id: 'mysecondstack', path: 'mysecondstack', children: { - mysecondresource: { + mysecondresource: expect.objectContaining({ id: 'mysecondresource', path: 'mysecondstack/mysecondresource', attributes: { @@ -252,11 +309,11 @@ nodeunitShim({ myprop: 'LazyResolved!', }, }, - }, + }), }, - }, + }), }, - }, + }), }); test.done(); @@ -291,20 +348,20 @@ nodeunitShim({ // assert that the rest of the construct tree is rendered test.deepEqual(readJson(assembly.directory, treeArtifact!.file), { version: 'tree-0.1', - tree: { + tree: expect.objectContaining({ id: 'App', path: '', children: { - Tree: { + Tree: expect.objectContaining({ id: 'Tree', path: 'Tree', - }, - mystack: { + }), + mystack: expect.objectContaining({ id: 'mystack', path: 'mystack', - }, + }), }, - }, + }), }); test.done(); diff --git a/packages/@aws-cdk/core/test/staging.test.ts b/packages/@aws-cdk/core/test/staging.test.ts index 347c5fcea3b63..76bf74f9cffd7 100644 --- a/packages/@aws-cdk/core/test/staging.test.ts +++ b/packages/@aws-cdk/core/test/staging.test.ts @@ -1,10 +1,11 @@ import * as os from 'os'; import * as path from 'path'; +import { FileAssetPackaging } from '@aws-cdk/cloud-assembly-schema'; import * as cxapi from '@aws-cdk/cx-api'; import * as fs from 'fs-extra'; import { nodeunitShim, Test } from 'nodeunit-shim'; import * as sinon from 'sinon'; -import { App, AssetHashType, AssetStaging, BundlingDockerImage, BundlingOptions, FileSystem, Stack, Stage } from '../lib'; +import { App, AssetHashType, AssetStaging, BundlingDockerImage, BundlingOptions, BundlingOutput, FileSystem, Stack, Stage } from '../lib'; const STUB_INPUT_FILE = '/tmp/docker-stub.input'; const STUB_INPUT_CONCAT_FILE = '/tmp/docker-stub.input.concat'; @@ -12,7 +13,9 @@ const STUB_INPUT_CONCAT_FILE = '/tmp/docker-stub.input.concat'; enum DockerStubCommand { SUCCESS = 'DOCKER_STUB_SUCCESS', FAIL = 'DOCKER_STUB_FAIL', - SUCCESS_NO_OUTPUT = 'DOCKER_STUB_SUCCESS_NO_OUTPUT' + SUCCESS_NO_OUTPUT = 'DOCKER_STUB_SUCCESS_NO_OUTPUT', + MULTIPLE_FILES = 'DOCKER_STUB_MULTIPLE_FILES', + SINGLE_ARCHIVE = 'DOCKER_STUB_SINGLE_ARCHIVE', } const FIXTURE_TEST1_DIR = path.join(__dirname, 'fs', 'fixtures', 'test1'); @@ -50,6 +53,34 @@ nodeunitShim({ test.deepEqual(staging.sourcePath, sourcePath); test.deepEqual(path.basename(staging.stagedPath), 'asset.2f37f937c51e2c191af66acf9b09f548926008ec68c575bd2ee54b6e997c0e00'); test.deepEqual(path.basename(staging.relativeStagedPath(stack)), 'asset.2f37f937c51e2c191af66acf9b09f548926008ec68c575bd2ee54b6e997c0e00'); + test.deepEqual(staging.packaging, FileAssetPackaging.ZIP_DIRECTORY); + test.deepEqual(staging.isArchive, true); + test.done(); + }, + + 'staging of an archive file correctly sets packaging and isArchive'(test: Test) { + // GIVEN + const stack = new Stack(); + const sourcePath = path.join(__dirname, 'archive', 'archive.zip'); + + // WHEN + const staging = new AssetStaging(stack, 's1', { sourcePath }); + + test.deepEqual(staging.packaging, FileAssetPackaging.FILE); + test.deepEqual(staging.isArchive, true); + test.done(); + }, + + 'staging of a non-archive file correctly sets packaging and isArchive'(test: Test) { + // GIVEN + const stack = new Stack(); + const sourcePath = __filename; + + // WHEN + const staging = new AssetStaging(stack, 's1', { sourcePath }); + + test.deepEqual(staging.packaging, FileAssetPackaging.FILE); + test.deepEqual(staging.isArchive, false); test.done(); }, @@ -785,6 +816,89 @@ nodeunitShim({ ); test.equal(asset.assetHash, '33cbf2cae5432438e0f046bc45ba8c3cef7b6afcf47b59d1c183775c1918fb1f'); // hash of MyStack/Asset + test.done(); + }, + + 'bundling that produces a single archive file is autodiscovered'(test: Test) { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'stack'); + const directory = path.join(__dirname, 'fs', 'fixtures', 'test1'); + + // WHEN + const staging = new AssetStaging(stack, 'Asset', { + sourcePath: directory, + bundling: { + image: BundlingDockerImage.fromRegistry('alpine'), + command: [DockerStubCommand.SINGLE_ARCHIVE], + }, + }); + + // THEN + const assembly = app.synth(); + test.deepEqual(fs.readdirSync(assembly.directory), [ + 'asset.f43148c61174f444925231b5849b468f21e93b5d1469cd07c53625ffd039ef48', // this is the bundle dir but it's empty + '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(staging.packaging, FileAssetPackaging.FILE); + test.deepEqual(staging.isArchive, true); + + test.done(); + }, + + 'bundling that produces a single archive file with NOT_ARCHIVED'(test: Test) { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'stack'); + const directory = path.join(__dirname, 'fs', 'fixtures', 'test1'); + + // WHEN + const staging = new AssetStaging(stack, 'Asset', { + sourcePath: directory, + bundling: { + image: BundlingDockerImage.fromRegistry('alpine'), + command: [DockerStubCommand.SINGLE_ARCHIVE], + outputType: BundlingOutput.NOT_ARCHIVED, + }, + }); + + // THEN + const assembly = app.synth(); + test.deepEqual(fs.readdirSync(assembly.directory), [ + 'asset.86ec07746e1d859290cfd8b9c648e581555649c75f51f741f11e22cab6775abc', + 'cdk.out', + 'manifest.json', + 'stack.template.json', + 'tree.json', + ]); + test.deepEqual(staging.packaging, FileAssetPackaging.ZIP_DIRECTORY); + test.deepEqual(staging.isArchive, true); + + test.done(); + }, + + 'throws with ARCHIVED and bundling that does not produce a single archive file'(test: Test) { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'stack'); + const directory = path.join(__dirname, 'fs', 'fixtures', 'test1'); + + // WHEN + test.throws(() => new AssetStaging(stack, 'Asset', { + sourcePath: directory, + bundling: { + image: BundlingDockerImage.fromRegistry('alpine'), + command: [DockerStubCommand.MULTIPLE_FILES], + outputType: BundlingOutput.ARCHIVED, + }, + }), /Bundling output directory is expected to include only a single .zip or .jar file when `output` is set to `ARCHIVED`/); + + test.done(); }, }); diff --git a/packages/@aws-cdk/core/test/synthesis.test.ts b/packages/@aws-cdk/core/test/synthesis.test.ts index 464e544cc562a..8b5200c1dfb72 100644 --- a/packages/@aws-cdk/core/test/synthesis.test.ts +++ b/packages/@aws-cdk/core/test/synthesis.test.ts @@ -28,13 +28,13 @@ nodeunitShim({ }); test.deepEqual(readJson(session.directory, 'tree.json'), { version: 'tree-0.1', - tree: { + tree: expect.objectContaining({ id: 'App', path: '', children: { - Tree: { id: 'Tree', path: 'Tree' }, + Tree: expect.objectContaining({ id: 'Tree', path: 'Tree' }), }, - }, + }), }); test.done(); }, diff --git a/packages/@aws-cdk/custom-resources/package.json b/packages/@aws-cdk/custom-resources/package.json index cab6d86231e9a..8fd47caf032b0 100644 --- a/packages/@aws-cdk/custom-resources/package.json +++ b/packages/@aws-cdk/custom-resources/package.json @@ -75,18 +75,18 @@ "@aws-cdk/aws-events": "0.0.0", "@aws-cdk/aws-s3": "0.0.0", "@aws-cdk/aws-ssm": "0.0.0", - "@types/aws-lambda": "^8.10.64", + "@types/aws-lambda": "^8.10.72", "@types/fs-extra": "^8.1.1", - "@types/sinon": "^9.0.9", - "aws-sdk": "^2.830.0", + "@types/sinon": "^9.0.10", + "aws-sdk": "^2.845.0", "aws-sdk-mock": "^5.1.0", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "cfn2ts": "0.0.0", "fs-extra": "^9.1.0", - "nock": "^13.0.5", + "nock": "^13.0.7", "pkglint": "0.0.0", - "sinon": "^9.2.1" + "sinon": "^9.2.4" }, "dependencies": { "@aws-cdk/aws-cloudformation": "0.0.0", @@ -121,5 +121,8 @@ "awscdkio": { "announce": false }, - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@aws-cdk/cx-api/NOTICE b/packages/@aws-cdk/cx-api/NOTICE index 070bcdddebcc3..f2197d8374ddd 100644 --- a/packages/@aws-cdk/cx-api/NOTICE +++ b/packages/@aws-cdk/cx-api/NOTICE @@ -20,4 +20,36 @@ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ----------------- \ No newline at end of file +---------------- + +** lru-cache - https://www.npmjs.com/package/lru-cache +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +---------------- + +** yallist - https://www.npmjs.com/package/yallist +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/packages/@aws-cdk/cx-api/lib/features.ts b/packages/@aws-cdk/cx-api/lib/features.ts index a9773d86ead60..57c9a5738ee96 100644 --- a/packages/@aws-cdk/cx-api/lib/features.ts +++ b/packages/@aws-cdk/cx-api/lib/features.ts @@ -83,7 +83,7 @@ export const KMS_DEFAULT_KEY_POLICIES = '@aws-cdk/aws-kms:defaultKeyPolicies'; /** * Change the old 's3:PutObject*' permission to 's3:PutObject' on Bucket, * as the former includes 's3:PutObjectAcl', - * which allows changing the visibility of an object written to the Bucket. + * which could be used to grant read/write object access to IAM principals in other accounts. * Use a feature flag to make sure existing customers who might be relying * on the overly-broad permissions are not broken. */ diff --git a/packages/@aws-cdk/cx-api/package.json b/packages/@aws-cdk/cx-api/package.json index 62e692fd08012..9b5c09a9a999d 100644 --- a/packages/@aws-cdk/cx-api/package.json +++ b/packages/@aws-cdk/cx-api/package.json @@ -57,14 +57,14 @@ }, "dependencies": { "@aws-cdk/cloud-assembly-schema": "0.0.0", - "semver": "^7.3.2" + "semver": "^7.3.4" }, "peerDependencies": { "@aws-cdk/cloud-assembly-schema": "0.0.0" }, "license": "Apache-2.0", "devDependencies": { - "@types/jest": "^26.0.15", + "@types/jest": "^26.0.20", "@types/mock-fs": "^4.13.0", "@types/semver": "^7.3.4", "cdk-build-tools": "0.0.0", @@ -156,5 +156,8 @@ }, "cdk-build": { "jest": true + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/cx-api/test/features.test.ts b/packages/@aws-cdk/cx-api/test/features.test.ts index fbff6c236b984..81b42773fd292 100644 --- a/packages/@aws-cdk/cx-api/test/features.test.ts +++ b/packages/@aws-cdk/cx-api/test/features.test.ts @@ -1,7 +1,12 @@ +import { testLegacyBehavior } from 'cdk-build-tools/lib/feature-flag'; import * as feats from '../lib/features'; test('all future flags have defaults configured', () => { Object.keys(feats.FUTURE_FLAGS).forEach(flag => { expect(typeof(feats.futureFlagDefault(flag))).toEqual('boolean'); }); +}); + +testLegacyBehavior('FUTURE_FLAGS_EXPIRED must be empty in CDKv1', Object, () => { + expect(feats.FUTURE_FLAGS_EXPIRED.length).toEqual(0); }); \ No newline at end of file diff --git a/packages/@aws-cdk/lambda-layer-awscli/package.json b/packages/@aws-cdk/lambda-layer-awscli/package.json index 838d613b03ddf..115f686f9e414 100644 --- a/packages/@aws-cdk/lambda-layer-awscli/package.json +++ b/packages/@aws-cdk/lambda-layer-awscli/package.json @@ -100,5 +100,8 @@ }, "ubergen": { "exclude": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/lambda-layer-kubectl/package.json b/packages/@aws-cdk/lambda-layer-kubectl/package.json index 7f561b67712e5..9ba2b10e89d47 100644 --- a/packages/@aws-cdk/lambda-layer-kubectl/package.json +++ b/packages/@aws-cdk/lambda-layer-kubectl/package.json @@ -106,5 +106,8 @@ }, "ubergen": { "exclude": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/pipelines/package.json b/packages/@aws-cdk/pipelines/package.json index b13b5877cace7..8717570fc8f1d 100644 --- a/packages/@aws-cdk/pipelines/package.json +++ b/packages/@aws-cdk/pipelines/package.json @@ -124,5 +124,8 @@ "events-method-signature:@aws-cdk/pipelines.UpdatePipelineAction.onStateChange" ] }, - "homepage": "https://github.com/aws/aws-cdk" + "homepage": "https://github.com/aws/aws-cdk", + "publishConfig": { + "tag": "latest" + } } 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 365e0fa9d06ee..f0427b95f7b51 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 @@ -38,23 +38,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { @@ -71,186 +55,6 @@ } }, "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineRoleB27FAA37", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineBuildSynthCdkBuildProjectRole231EEA2A", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineBuildSynthCdkBuildProjectRole231EEA2A", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineUpdatePipelineSelfMutationRole57E559E8", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineUpdatePipelineSelfMutationRole57E559E8", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineAssetsFileRole59943A77", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineAssetsFileRole59943A77", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelinePreProdUseSourceProjectRole69B20A71", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelinePreProdUseSourceProjectRole69B20A71", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-test-region" - ] - ] - } - }, - "Resource": "*" } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/pipelines/test/integ.pipeline.expected.json b/packages/@aws-cdk/pipelines/test/integ.pipeline.expected.json index 9e0541be1e17d..787f89f4c09fb 100644 --- a/packages/@aws-cdk/pipelines/test/integ.pipeline.expected.json +++ b/packages/@aws-cdk/pipelines/test/integ.pipeline.expected.json @@ -38,23 +38,7 @@ "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" - ], + "Action": "kms:*", "Effect": "Allow", "Principal": { "AWS": { @@ -71,152 +55,6 @@ } }, "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineRoleB27FAA37", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineBuildSynthCdkBuildProjectRole231EEA2A", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineBuildSynthCdkBuildProjectRole231EEA2A", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineUpdatePipelineSelfMutationRole57E559E8", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelineUpdatePipelineSelfMutationRole57E559E8", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelinePreProdUseSourceProjectRole69B20A71", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::GetAtt": [ - "PipelinePreProdUseSourceProjectRole69B20A71", - "Arn" - ] - } - }, - "Resource": "*" - }, - { - "Action": [ - "kms:Decrypt", - "kms:DescribeKey" - ], - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-test-region" - ] - ] - } - }, - "Resource": "*" } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/region-info/package.json b/packages/@aws-cdk/region-info/package.json index 72ffe996d0060..fe5a7e1acf1ec 100644 --- a/packages/@aws-cdk/region-info/package.json +++ b/packages/@aws-cdk/region-info/package.json @@ -83,5 +83,8 @@ }, "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@aws-cdk/yaml-cfn/package.json b/packages/@aws-cdk/yaml-cfn/package.json index a6ba44bac701b..45e121a4cb7f5 100644 --- a/packages/@aws-cdk/yaml-cfn/package.json +++ b/packages/@aws-cdk/yaml-cfn/package.json @@ -67,7 +67,7 @@ }, "devDependencies": { "@aws-cdk/assert": "0.0.0", - "@types/jest": "^26.0.15", + "@types/jest": "^26.0.20", "@types/yaml": "^1.9.7", "cdk-build-tools": "0.0.0", "jest": "^26.6.3", @@ -86,5 +86,8 @@ "maturity": "experimental", "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/@monocdk-experiment/assert/package.json b/packages/@monocdk-experiment/assert/package.json index ed98d3c1ccab4..63fd5a2e86164 100644 --- a/packages/@monocdk-experiment/assert/package.json +++ b/packages/@monocdk-experiment/assert/package.json @@ -34,14 +34,14 @@ "license": "Apache-2.0", "devDependencies": { "@monocdk-experiment/rewrite-imports": "0.0.0", - "@types/jest": "^26.0.15", - "@types/node": "^10.17.51", + "@types/jest": "^26.0.20", + "@types/node": "^10.17.52", "cdk-build-tools": "0.0.0", "constructs": "^3.2.0", "jest": "^26.6.3", "monocdk": "0.0.0", "pkglint": "0.0.0", - "ts-jest": "^26.4.4" + "ts-jest": "^26.5.1" }, "dependencies": { "@aws-cdk/cloudformation-diff": "0.0.0" @@ -65,5 +65,8 @@ "node": ">= 10.13.0 <13 || >=13.7.0" }, "stability": "experimental", - "maturity": "developer-preview" + "maturity": "developer-preview", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/@monocdk-experiment/rewrite-imports/package.json b/packages/@monocdk-experiment/rewrite-imports/package.json index 18f0a6053f092..678eba3f64183 100644 --- a/packages/@monocdk-experiment/rewrite-imports/package.json +++ b/packages/@monocdk-experiment/rewrite-imports/package.json @@ -33,12 +33,12 @@ "license": "Apache-2.0", "dependencies": { "glob": "^7.1.6", - "typescript": "~3.9.7" + "typescript": "~3.9.9" }, "devDependencies": { "@types/glob": "^7.1.3", - "@types/jest": "^26.0.15", - "@types/node": "^10.17.51", + "@types/jest": "^26.0.20", + "@types/node": "^10.17.52", "cdk-build-tools": "0.0.0", "pkglint": "0.0.0" }, @@ -52,5 +52,8 @@ "maturity": "developer-preview", "engines": { "node": ">= 10.13.0 <13 || >=13.7.0" + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/aws-cdk-lib/NOTICE b/packages/aws-cdk-lib/NOTICE index a2e13dfe49345..bd46bd848ec36 100644 --- a/packages/aws-cdk-lib/NOTICE +++ b/packages/aws-cdk-lib/NOTICE @@ -335,3 +335,40 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------- + +** lru-cache - https://www.npmjs.com/package/lru-cache + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +---------------- + +** yallist - https://www.npmjs.com/package/yallist + + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +---------------- diff --git a/packages/aws-cdk-lib/package.json b/packages/aws-cdk-lib/package.json index 10849cc784a4a..cb0a8f61b8892 100644 --- a/packages/aws-cdk-lib/package.json +++ b/packages/aws-cdk-lib/package.json @@ -95,7 +95,7 @@ "jsonschema": "^1.4.0", "minimatch": "^3.0.4", "punycode": "^2.1.1", - "semver": "^7.3.2", + "semver": "^7.3.4", "yaml": "1.10.0" }, "devDependencies": { @@ -108,6 +108,7 @@ "@aws-cdk/aws-amplify": "0.0.0", "@aws-cdk/aws-apigateway": "0.0.0", "@aws-cdk/aws-apigatewayv2": "0.0.0", + "@aws-cdk/aws-apigatewayv2-authorizers": "0.0.0", "@aws-cdk/aws-apigatewayv2-integrations": "0.0.0", "@aws-cdk/aws-appconfig": "0.0.0", "@aws-cdk/aws-appflow": "0.0.0", @@ -202,6 +203,7 @@ "@aws-cdk/aws-kendra": "0.0.0", "@aws-cdk/aws-kinesis": "0.0.0", "@aws-cdk/aws-kinesisanalytics": "0.0.0", + "@aws-cdk/aws-kinesisanalytics-flink": "0.0.0", "@aws-cdk/aws-kinesisfirehose": "0.0.0", "@aws-cdk/aws-kms": "0.0.0", "@aws-cdk/aws-lakeformation": "0.0.0", @@ -281,12 +283,12 @@ "@aws-cdk/region-info": "0.0.0", "@aws-cdk/yaml-cfn": "0.0.0", "@types/fs-extra": "^8.1.1", - "@types/node": "^10.17.51", + "@types/node": "^10.17.52", "cdk-build-tools": "0.0.0", "constructs": "^3.2.0", "fs-extra": "^9.1.0", "pkglint": "0.0.0", - "ts-node": "^9.1.0", + "ts-node": "^9.1.1", "typescript": "~3.8.3", "ubergen": "0.0.0" }, diff --git a/packages/aws-cdk/README.md b/packages/aws-cdk/README.md index d17a38e62f923..78e2177b186cd 100644 --- a/packages/aws-cdk/README.md +++ b/packages/aws-cdk/README.md @@ -285,6 +285,18 @@ When `cdk deploy` is executed, deployment events will include the complete histo The `progress` key can also be specified as a user setting (`~/.cdk.json`) +#### Externally Executable CloudFormation Change Sets + +For more control over when stack changes are deployed, the CDK can generate a +CloudFormation change set but not execute it. The name of the generated +change set is *cdk-deploy-change-set*, and a previous change set with that +name will be overwritten. The change set will always be created, even if it +is empty. + +```console +$ cdk deploy --no-execute +``` + ### `cdk destroy` Deletes a stack from it's environment. This will cause the resources in the stack to be destroyed (unless they were diff --git a/packages/aws-cdk/bin/cdk.ts b/packages/aws-cdk/bin/cdk.ts index a5c6e04d39f1c..707c4ab770758 100644 --- a/packages/aws-cdk/bin/cdk.ts +++ b/packages/aws-cdk/bin/cdk.ts @@ -1,6 +1,5 @@ #!/usr/bin/env node import 'source-map-support/register'; - import * as cxapi from '@aws-cdk/cx-api'; import * as colors from 'colors/safe'; import * as yargs from 'yargs'; @@ -195,6 +194,10 @@ async function initCommandLine() { const cmd = argv._[0]; + if (typeof(cmd) !== 'string') { + throw new Error(`First argument should be a string. Got: ${cmd} (${typeof(cmd)})`); + } + // Bundle up global objects so the commands have access to them const commandOptions = { args: argv, configuration, aws: sdkProvider }; diff --git a/packages/aws-cdk/lib/api/deploy-stack.ts b/packages/aws-cdk/lib/api/deploy-stack.ts index 1b52fb736e946..2dfb2f5119c71 100644 --- a/packages/aws-cdk/lib/api/deploy-stack.ts +++ b/packages/aws-cdk/lib/api/deploy-stack.ts @@ -174,6 +174,7 @@ export interface DeployStackOptions { } const LARGE_TEMPLATE_SIZE_KB = 50; +const CDK_CHANGE_SET_NAME = 'cdk-deploy-change-set'; /** @experimental */ export async function deployStack(options: DeployStackOptions): Promise { @@ -228,14 +229,20 @@ export async function deployStack(options: DeployStackOptions): Promise=3.0", "promptly": "^3.2.0", "proxy-agent": "^4.0.1", - "semver": "^7.3.2", + "semver": "^7.3.4", "source-map-support": "^0.5.19", "table": "^6.0.7", "uuid": "^8.3.2", @@ -105,5 +105,8 @@ "node": ">= 10.13.0 <13 || >=13.7.0" }, "stability": "stable", - "maturity": "stable" + "maturity": "stable", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/aws-cdk/test/api/bootstrap.test.ts b/packages/aws-cdk/test/api/bootstrap.test.ts index d04a49ec11472..888a649cc735e 100644 --- a/packages/aws-cdk/test/api/bootstrap.test.ts +++ b/packages/aws-cdk/test/api/bootstrap.test.ts @@ -49,6 +49,7 @@ beforeEach(() => { executed = true; return {}; }), + deleteChangeSet: jest.fn(), getTemplate: jest.fn(() => { executed = true; return {}; diff --git a/packages/aws-cdk/test/api/deploy-stack.test.ts b/packages/aws-cdk/test/api/deploy-stack.test.ts index 1908b4716aac0..8fffb321ef995 100644 --- a/packages/aws-cdk/test/api/deploy-stack.test.ts +++ b/packages/aws-cdk/test/api/deploy-stack.test.ts @@ -459,6 +459,53 @@ test('not executed and no error if --no-execute is given', async () => { expect(cfnMocks.executeChangeSet).not.toHaveBeenCalled(); }); +test('empty change set is deleted if --execute is given', async () => { + cfnMocks.describeChangeSet?.mockImplementation(() => ({ + Status: 'FAILED', + StatusReason: 'No updates are to be performed.', + })); + + // GIVEN + givenStackExists(); + + // WHEN + await deployStack({ + ...standardDeployStackArguments(), + execute: true, + force: true, // Necessary to bypass "skip deploy" + }); + + // THEN + expect(cfnMocks.createChangeSet).toHaveBeenCalled(); + expect(cfnMocks.executeChangeSet).not.toHaveBeenCalled(); + + //the first deletion is for any existing cdk change sets, the second is for the deleting the new empty change set + expect(cfnMocks.deleteChangeSet).toHaveBeenCalledTimes(2); +}); + +test('empty change set is not deleted if --no-execute is given', async () => { + cfnMocks.describeChangeSet?.mockImplementation(() => ({ + Status: 'FAILED', + StatusReason: 'No updates are to be performed.', + })); + + // GIVEN + givenStackExists(); + + // WHEN + await deployStack({ + ...standardDeployStackArguments(), + execute: false, + }); + + // THEN + expect(cfnMocks.createChangeSet).toHaveBeenCalled(); + expect(cfnMocks.executeChangeSet).not.toHaveBeenCalled(); + + //the first deletion is for any existing cdk change sets + expect(cfnMocks.deleteChangeSet).toHaveBeenCalledTimes(1); +}); + test('use S3 url for stack deployment if present in Stack Artifact', async () => { // WHEN await deployStack({ diff --git a/packages/aws-cdk/test/integ/github-helpers.ts b/packages/aws-cdk/test/integ/github-helpers.ts index 3a0bc1fb84195..10cff22158a8f 100644 --- a/packages/aws-cdk/test/integ/github-helpers.ts +++ b/packages/aws-cdk/test/integ/github-helpers.ts @@ -15,8 +15,8 @@ module.exports.fetchPreviousVersion = async function(base: string) { // this returns a list in decsending order, newest releases first for (const release of releases.data) { - const version = release.name.replace('v', ''); - if (semver.lt(version, base)) { + const version = release.name?.replace('v', ''); + if (version && semver.lt(version, base)) { return version; } } diff --git a/packages/awslint/package.json b/packages/awslint/package.json index 191a3bd6e24c2..f0a74898f0148 100644 --- a/packages/awslint/package.json +++ b/packages/awslint/package.json @@ -16,18 +16,18 @@ "awslint": "bin/awslint" }, "dependencies": { - "@jsii/spec": "^1.15.0", + "@jsii/spec": "^1.21.0", "camelcase": "^6.2.0", "colors": "^1.4.0", "fs-extra": "^9.1.0", - "jsii-reflect": "^1.15.0", + "jsii-reflect": "^1.21.0", "yargs": "^16.2.0" }, "devDependencies": { "@types/fs-extra": "^8.1.1", - "@types/yargs": "^15.0.10", + "@types/yargs": "^15.0.13", "pkglint": "0.0.0", - "typescript": "~3.9.7" + "typescript": "~3.9.9" }, "repository": { "type": "git", @@ -49,5 +49,8 @@ "stability": "experimental", "engines": { "node": ">= 10.13.0 <13 || >=13.7.0" + }, + "publishConfig": { + "tag": "latest" } } diff --git a/packages/cdk-assets/package.json b/packages/cdk-assets/package.json index ba54b8625f279..b2a142c523dd6 100644 --- a/packages/cdk-assets/package.json +++ b/packages/cdk-assets/package.json @@ -32,14 +32,14 @@ "devDependencies": { "@types/archiver": "^5.1.0", "@types/glob": "^7.1.3", - "@types/jest": "^26.0.15", + "@types/jest": "^26.0.20", "@types/jszip": "^3.4.1", "@types/mock-fs": "^4.13.0", - "@types/node": "^10.17.51", - "@types/yargs": "^15.0.10", + "@types/node": "^10.17.52", + "@types/yargs": "^15.0.13", "cdk-build-tools": "0.0.0", "jest": "^26.6.3", - "jszip": "^3.5.0", + "jszip": "^3.6.0", "mock-fs": "^4.13.0", "pkglint": "0.0.0" }, @@ -47,7 +47,7 @@ "@aws-cdk/cloud-assembly-schema": "0.0.0", "@aws-cdk/cx-api": "0.0.0", "archiver": "^5.2.0", - "aws-sdk": "^2.830.0", + "aws-sdk": "^2.845.0", "glob": "^7.1.6", "yargs": "^16.2.0" }, @@ -68,5 +68,8 @@ "shrinkWrap": true }, "stability": "experimental", - "maturity": "experimental" + "maturity": "experimental", + "publishConfig": { + "tag": "latest" + } } diff --git a/packages/cdk-dasm/package.json b/packages/cdk-dasm/package.json index ca73297f8f386..917a0b25715aa 100644 --- a/packages/cdk-dasm/package.json +++ b/packages/cdk-dasm/package.json @@ -26,11 +26,11 @@ }, "license": "Apache-2.0", "dependencies": { - "codemaker": "^1.15.0", + "codemaker": "^1.21.0", "yaml": "1.10.0" }, "devDependencies": { - "@types/jest": "^26.0.15", + "@types/jest": "^26.0.20", "@types/yaml": "1.9.7", "jest": "^26.6.3" }, diff --git a/packages/decdk/package.json b/packages/decdk/package.json index a5c184e65f1d5..ec918d8d77feb 100644 --- a/packages/decdk/package.json +++ b/packages/decdk/package.json @@ -36,6 +36,7 @@ "@aws-cdk/aws-apigateway": "0.0.0", "@aws-cdk/aws-apigatewayv2": "0.0.0", "@aws-cdk/aws-apigatewayv2-integrations": "0.0.0", + "@aws-cdk/aws-apigatewayv2-authorizers": "0.0.0", "@aws-cdk/aws-appconfig": "0.0.0", "@aws-cdk/aws-appflow": "0.0.0", "@aws-cdk/aws-applicationautoscaling": "0.0.0", @@ -128,6 +129,7 @@ "@aws-cdk/aws-kendra": "0.0.0", "@aws-cdk/aws-kinesis": "0.0.0", "@aws-cdk/aws-kinesisanalytics": "0.0.0", + "@aws-cdk/aws-kinesisanalytics-flink": "0.0.0", "@aws-cdk/aws-kinesisfirehose": "0.0.0", "@aws-cdk/aws-kms": "0.0.0", "@aws-cdk/aws-lakeformation": "0.0.0", @@ -208,18 +210,18 @@ "@aws-cdk/yaml-cfn": "0.0.0", "constructs": "^3.2.0", "fs-extra": "^9.1.0", - "jsii-reflect": "^1.15.0", + "jsii-reflect": "^1.21.0", "jsonschema": "^1.4.0", "yaml": "1.10.0", "yargs": "^16.2.0" }, "devDependencies": { "@types/fs-extra": "^8.1.1", - "@types/jest": "^26.0.15", + "@types/jest": "^26.0.20", "@types/yaml": "1.9.7", - "@types/yargs": "^15.0.10", + "@types/yargs": "^15.0.13", "jest": "^26.6.3", - "jsii": "^1.15.0" + "jsii": "^1.21.0" }, "keywords": [ "aws", diff --git a/packages/decdk/test/__snapshots__/synth.test.js.snap b/packages/decdk/test/__snapshots__/synth.test.js.snap index 0befd96765754..1e8c00e6bd957 100644 --- a/packages/decdk/test/__snapshots__/synth.test.js.snap +++ b/packages/decdk/test/__snapshots__/synth.test.js.snap @@ -1847,23 +1847,7 @@ Object { "KeyPolicy": Object { "Statement": Array [ Object { - "Action": Array [ - "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", - ], + "Action": "kms:*", "Effect": "Allow", "Principal": Object { "AWS": Object { @@ -1885,24 +1869,6 @@ Object { }, "Resource": "*", }, - Object { - "Action": Array [ - "kms:Decrypt", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*", - ], - "Effect": "Allow", - "Principal": Object { - "AWS": Object { - "Fn::GetAtt": Array [ - "BuildProjectRoleAA92C755", - "Arn", - ], - }, - }, - "Resource": "*", - }, ], "Version": "2012-10-17", }, @@ -1944,23 +1910,7 @@ Object { "KeyPolicy": Object { "Statement": Array [ Object { - "Action": Array [ - "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", - ], + "Action": "kms:*", "Effect": "Allow", "Principal": Object { "AWS": Object { @@ -1982,95 +1932,6 @@ Object { }, "Resource": "*", }, - Object { - "Action": Array [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*", - ], - "Effect": "Allow", - "Principal": Object { - "AWS": Object { - "Fn::GetAtt": Array [ - "PipelineRoleD68726F7", - "Arn", - ], - }, - }, - "Resource": "*", - }, - Object { - "Action": Array [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*", - ], - "Effect": "Allow", - "Principal": Object { - "AWS": Object { - "Fn::GetAtt": Array [ - "PipelineSourceCodePipelineActionRoleC6F9E7F5", - "Arn", - ], - }, - }, - "Resource": "*", - }, - Object { - "Action": Array [ - "kms:Decrypt", - "kms:DescribeKey", - "kms:Encrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*", - ], - "Effect": "Allow", - "Principal": Object { - "AWS": Object { - "Fn::GetAtt": Array [ - "BuildProjectRoleAA92C755", - "Arn", - ], - }, - }, - "Resource": "*", - }, - Object { - "Action": Array [ - "kms:Decrypt", - "kms:DescribeKey", - ], - "Effect": "Allow", - "Principal": Object { - "AWS": Object { - "Fn::GetAtt": Array [ - "PipelineDeployRole97597E3E", - "Arn", - ], - }, - }, - "Resource": "*", - }, - Object { - "Action": Array [ - "kms:Decrypt", - "kms:DescribeKey", - ], - "Effect": "Allow", - "Principal": Object { - "AWS": Object { - "Fn::GetAtt": Array [ - "PipelineDeployCodePipelineActionRole8B83082E", - "Arn", - ], - }, - }, - "Resource": "*", - }, ], "Version": "2012-10-17", }, @@ -2909,23 +2770,7 @@ Object { "KeyPolicy": Object { "Statement": Array [ Object { - "Action": Array [ - "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", - ], + "Action": "kms:*", "Effect": "Allow", "Principal": Object { "AWS": Object { diff --git a/packages/decdk/test/synth.test.ts b/packages/decdk/test/synth.test.ts index ab025ab1ddbdd..8c3a27e056d52 100644 --- a/packages/decdk/test/synth.test.ts +++ b/packages/decdk/test/synth.test.ts @@ -31,7 +31,14 @@ for (const templateFile of fs.readdirSync(dir)) { const template = await readTemplate(path.resolve(dir, templateFile)); const typeSystem = await obtainTypeSystem(); - const app = new cdk.App(); + const app = new cdk.App({ + context: { + '@aws-cdk/aws-ecr-assets:dockerIgnoreSupport': true, + '@aws-cdk/aws-kms:defaultKeyPolicies': true, + '@aws-cdk/core:enableStackNameDuplicates': true, + '@aws-cdk/aws-secretsmanager:parseOwnedSecretName': true, + } + }); const stackName = stackNameFromFileName(templateFile); new DeclarativeStack(app, stackName, { diff --git a/packages/monocdk/NOTICE b/packages/monocdk/NOTICE index a2e13dfe49345..bd46bd848ec36 100644 --- a/packages/monocdk/NOTICE +++ b/packages/monocdk/NOTICE @@ -335,3 +335,40 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------- + +** lru-cache - https://www.npmjs.com/package/lru-cache + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +---------------- + +** yallist - https://www.npmjs.com/package/yallist + + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +---------------- diff --git a/packages/monocdk/package.json b/packages/monocdk/package.json index a02fee1b78939..d328a702272e3 100644 --- a/packages/monocdk/package.json +++ b/packages/monocdk/package.json @@ -100,7 +100,7 @@ "jsonschema": "^1.4.0", "minimatch": "^3.0.4", "punycode": "^2.1.1", - "semver": "^7.3.2", + "semver": "^7.3.4", "yaml": "1.10.0" }, "devDependencies": { @@ -113,6 +113,7 @@ "@aws-cdk/aws-amplify": "0.0.0", "@aws-cdk/aws-apigateway": "0.0.0", "@aws-cdk/aws-apigatewayv2": "0.0.0", + "@aws-cdk/aws-apigatewayv2-authorizers": "0.0.0", "@aws-cdk/aws-apigatewayv2-integrations": "0.0.0", "@aws-cdk/aws-appconfig": "0.0.0", "@aws-cdk/aws-appflow": "0.0.0", @@ -207,6 +208,7 @@ "@aws-cdk/aws-kendra": "0.0.0", "@aws-cdk/aws-kinesis": "0.0.0", "@aws-cdk/aws-kinesisanalytics": "0.0.0", + "@aws-cdk/aws-kinesisanalytics-flink": "0.0.0", "@aws-cdk/aws-kinesisfirehose": "0.0.0", "@aws-cdk/aws-kms": "0.0.0", "@aws-cdk/aws-lakeformation": "0.0.0", @@ -286,12 +288,12 @@ "@aws-cdk/region-info": "0.0.0", "@aws-cdk/yaml-cfn": "0.0.0", "@types/fs-extra": "^8.1.1", - "@types/node": "^10.17.51", + "@types/node": "^10.17.52", "cdk-build-tools": "0.0.0", "constructs": "^3.2.0", "fs-extra": "^9.1.0", "pkglint": "0.0.0", - "ts-node": "^9.1.0", + "ts-node": "^9.1.1", "typescript": "~3.8.3", "ubergen": "0.0.0" }, @@ -308,5 +310,8 @@ ], "awscdkio": { "announce": false + }, + "publishConfig": { + "tag": "latest" } } diff --git a/release.json b/release.json index bbd42d0c80ea6..318d5d9a12072 100644 --- a/release.json +++ b/release.json @@ -1,5 +1,4 @@ { "majorVersion": 1, - "releaseType": "stable", - "distTag": "latest" + "releaseType": "stable" } diff --git a/scripts/bump.js b/scripts/bump.js index a1d678c57ce18..5d8b4d63a62d0 100755 --- a/scripts/bump.js +++ b/scripts/bump.js @@ -1,8 +1,9 @@ #!/usr/bin/env node -const standardVersion = require('standard-version'); const fs = require('fs'); const path = require('path'); +const semver = require('semver'); const ver = require('./resolve-version'); +const { exec } = require('child_process'); const repoRoot = path.join(__dirname, '..'); const releaseAs = process.argv[2] || 'minor'; @@ -40,7 +41,19 @@ async function main() { console.error(`BUMP_CANDIDATE is set, so bumping version for testing (with the "${opts.prerelease}" prerelease tag)`); } - return standardVersion(opts); + // `standard-release` will -- among other things -- create the changelog. + // However, on the v2 branch, `conventional-changelog` (which `standard-release` uses) gets confused + // and creates really muddled changelogs with both v1 and v2 releases intermingled, and lots of missing data. + // A super HACK here is to locally remove all version tags that don't match this major version prior + // to doing the bump, and then later fetching to restore those tags. + const majorVersion = semver.major(ver.version); + await exec(`git tag -d $(git tag -l | grep -v '^v${majorVersion}.')`); + + // Delay loading standard-version until the git tags have been pruned. + const standardVersion = require('standard-version'); + await standardVersion(opts); + + await exec('git fetch -t'); } main().catch(err => { diff --git a/scripts/check-api-compatibility.sh b/scripts/check-api-compatibility.sh index ece902e7130f9..1b99f9e05f188 100755 --- a/scripts/check-api-compatibility.sh +++ b/scripts/check-api-compatibility.sh @@ -9,6 +9,10 @@ package_name() { node -pe "require('$1/package.json').name" } +package_name_and_dist_tag() { + node -pe "const pkg = require('$1/package.json'); pkg.name + '@' + pkg.publishConfig.tag" +} + # Determine whether an NPM package exists on NPM # # Doesn't use 'npm view' as that is slow. Direct curl'ing npmjs is better @@ -18,7 +22,6 @@ package_exists_on_npm() { curl -I 2>/dev/null https://registry.npmjs.org/$pkg/$ver | head -n 1 | grep 200 >/dev/null } - #---------------------------------------------------------------------- list_jsii_packages() { @@ -33,12 +36,11 @@ jsii_package_dirs=$(list_jsii_packages) #---------------------------------------------------------------------- -# Input a directory, output the package name IF it exists on GitHub -dirs_to_existing_names() { +# Input a directory, output the directory IF it exists on NPM +dirs_for_existing_pkgs() { local dir="$1" - local name=$(package_name "$dir") - if package_exists_on_npm $name; then - echo "$name" + if package_exists_on_npm $(package_name $dir); then + echo "$dir" echo -n "." >&2 else echo -n "x" >&2 @@ -46,28 +48,29 @@ dirs_to_existing_names() { } export -f package_name +export -f package_name_and_dist_tag export -f package_exists_on_npm -export -f dirs_to_existing_names +export -f dirs_for_existing_pkgs if ! ${SKIP_DOWNLOAD:-false}; then echo "Filtering on existing packages on NPM..." >&2 # In parallel - existing_names=$(echo "$jsii_package_dirs" | xargs -n1 -P4 -I {} bash -c 'dirs_to_existing_names "$@"' _ {}) + existing_pkg_dirs=$(echo "$jsii_package_dirs" | xargs -n1 -P4 -I {} bash -c 'dirs_for_existing_pkgs "$@"' _ {}) + existing_names=$(echo "$existing_pkg_dirs" | xargs -n1 -P4 -I {} bash -c 'package_name "$@"' _ {}) echo " Done." >&2 echo "Determining baseline version..." >&2 version=$(node -p 'require("./scripts/resolve-version.js").version') - disttag=$(node -p 'require("./scripts/resolve-version.js").npmDistTag') echo " Current version is $version." >&2 if ! package_exists_on_npm aws-cdk $version; then - echo " Version $version does not exist in npm. Falling back to resolved dist tag '$disttag'" >&2 - version=$disttag + echo " Version $version does not exist in npm. Falling back to package dist tags" >&2 + existing_names=$(echo "$existing_pkg_dirs" | xargs -n1 -P4 -I {} bash -c 'package_name_and_dist_tag "$@"' _ {}) + else + echo "Using version '$version' as the baseline..." + existing_names=$(echo "$existing_names" | sed -e "s/$/@$version/") fi - echo "Using version '$version' as the baseline..." - existing_names=$(echo "$existing_names" | sed -e "s/$/@$version/") - rm -rf $tmpdir mkdir -p $tmpdir diff --git a/scripts/resolve-version-lib.js b/scripts/resolve-version-lib.js index 63d54f33c4a70..2a7f0e4eecebc 100755 --- a/scripts/resolve-version-lib.js +++ b/scripts/resolve-version-lib.js @@ -3,13 +3,13 @@ const path = require('path'); const fs = require('fs'); //============================================================= -// UNIT TESTS: tools/script-tests/test/resolve-version.test.js +// UNIT TESTS: tools/script-tests/test/resolve-version.test.js //============================================================= function resolveVersion(rootdir) { const ALLOWED_RELEASE_TYPES = [ 'alpha', 'rc', 'stable' ]; const MIN_MAJOR = 1, MAX_MAJOR = 2; // extra safety: update to allow new major versions - + // // parse release.json // @@ -17,31 +17,30 @@ function resolveVersion(rootdir) { const releaseConfig = require(releaseFile); const majorVersion = releaseConfig.majorVersion; const releaseType = releaseConfig.releaseType; - const distTag = releaseConfig.distTag; if (!majorVersion) { throw new Error(`"majorVersion"" must be defined in ${releaseFile}`); } if (!releaseType) { throw new Error(`"releaseType" must be defined in ${releaseFile}`); } if (typeof(majorVersion) !== 'number') { throw new Error(`majorVersion=${majorVersion} must be a number`); } if (majorVersion < MIN_MAJOR || majorVersion > MAX_MAJOR) { throw new Error(`majorVersion=${majorVersion} is an unsupported major version (should be between ${MIN_MAJOR} and ${MAX_MAJOR})`); } if (!ALLOWED_RELEASE_TYPES.includes(releaseType)) { throw new Error(`releaseType=${releaseType} is not allowed. Allowed values: ${ALLOWED_RELEASE_TYPES.join(',')}`); } - + // // resolve and check that we have a version file // - + const versionFile = `version.v${majorVersion}.json`; const versionFilePath = path.join(rootdir, versionFile); if (!fs.existsSync(versionFilePath)) { throw new Error(`unable to find version file ${versionFile} for major version ${majorVersion}`); } - + // // validate that current version matches the requirements // - + const currentVersion = require(versionFilePath).version; console.error(`current version: ${currentVersion}`); - if (!currentVersion.startsWith(`${majorVersion}.`)) { - throw new Error(`current version "${currentVersion}" does not use the expected major version ${majorVersion}`); + if (!currentVersion.startsWith(`${majorVersion}.`)) { + throw new Error(`current version "${currentVersion}" does not use the expected major version ${majorVersion}`); } // if this is a pre-release, make sure current version includes the // pre-release tag (e.g. "1.0.0-alpha.0"). we allow stable branches to bump to @@ -51,27 +50,26 @@ function resolveVersion(rootdir) { throw new Error(`could not find pre-release tag "${releaseType}" in current version "${currentVersion}" defined in ${versionFile}`); } } - + // // determine changelog file name // - - const changelogFile = majorVersion === 1 - ? 'CHANGELOG.md' + + const changelogFile = majorVersion === 1 + ? 'CHANGELOG.md' : `CHANGELOG.v${majorVersion}.md`; - + // // export all of it // - + return { version: currentVersion, versionFile: versionFile, changelogFile: changelogFile, prerelease: releaseType !== 'stable' ? releaseType : undefined, marker: '0.0.0', - ...(distTag ? { npmDistTag: distTag } : {}), }; } -module.exports = resolveVersion; \ No newline at end of file +module.exports = resolveVersion; diff --git a/scripts/script-tests/resolve-version.test.js b/scripts/script-tests/resolve-version.test.js index 57e72019245d8..442e97e45b3cf 100644 --- a/scripts/script-tests/resolve-version.test.js +++ b/scripts/script-tests/resolve-version.test.js @@ -67,7 +67,7 @@ happy({ happy({ name: 'to support BUMP_CANDIDATE stable branches can be bumped towards a pre-release', - inputs: { + inputs: { 'release.json': { majorVersion: 2, releaseType: 'stable' }, 'version.v2.json': { version: '2.0.0-rc.0' } }, @@ -80,23 +80,6 @@ happy({ } }); -happy({ - name: 'dist tag is correctly shown', - inputs: { - 'release.json': { majorVersion: 2, releaseType: 'alpha', distTag: 'v2-preview' }, - 'version.v2.json': { version: '2.0.0-alpha.5' } - }, - expected: { - changelogFile: 'CHANGELOG.v2.md', - marker: '0.0.0', - prerelease: 'alpha', - version: '2.0.0-alpha.5', - versionFile: 'version.v2.json', - npmDistTag: 'v2-preview', - } -}); - - failure({ name: 'invalid release type', inputs: { 'release.json': { majorVersion: 2, releaseType: 'build' } }, @@ -129,7 +112,7 @@ failure({ failure({ name: 'actual version not the right major', - inputs: { + inputs: { 'release.json': { majorVersion: 1, releaseType: 'stable' }, 'version.v1.json': { version: '2.0.0' } }, @@ -138,7 +121,7 @@ failure({ failure({ name: 'actual version not the right pre-release', - inputs: { + inputs: { 'release.json': { majorVersion: 2, releaseType: 'alpha' }, 'version.v2.json': { version: '2.0.0-rc.0' } }, diff --git a/tools/cdk-build-tools/lib/feature-flag.ts b/tools/cdk-build-tools/lib/feature-flag.ts index eec1d28d6e9e4..36b22588288aa 100644 --- a/tools/cdk-build-tools/lib/feature-flag.ts +++ b/tools/cdk-build-tools/lib/feature-flag.ts @@ -30,9 +30,8 @@ export function testFutureBehavior( const major = cdkMajorVersion(repoRoot); if (major === 2) { - // Temporaily disable CDKv2 behaviour - // const app = new cdkApp(); - // return test(name, async () => fn(app)); + const app = new cdkApp(); + return test(name, async () => fn(app)); } const app = new cdkApp({ context: flags }); return test(name, () => fn(app)); @@ -59,8 +58,7 @@ export function testLegacyBehavior( const major = cdkMajorVersion(repoRoot); if (major === 2) { - // Temporarily disable CDKv2 behaviour - // return; + return; } const app = new cdkApp(); return test(name, () => fn(app)); @@ -78,4 +76,4 @@ function cdkMajorVersion(repoRoot: string) { throw new Error(`version ${ver} is not a semver`); } return sem.major; -} \ No newline at end of file +} diff --git a/tools/cdk-build-tools/package.json b/tools/cdk-build-tools/package.json index f94236d05bc66..8b2a6236620e9 100644 --- a/tools/cdk-build-tools/package.json +++ b/tools/cdk-build-tools/package.json @@ -34,31 +34,31 @@ "license": "Apache-2.0", "devDependencies": { "@types/fs-extra": "^8.1.1", - "@types/jest": "^26.0.15", - "@types/yargs": "^15.0.10", + "@types/jest": "^26.0.20", + "@types/yargs": "^15.0.13", "pkglint": "0.0.0" }, "dependencies": { - "@typescript-eslint/eslint-plugin": "^4.14.0", - "@typescript-eslint/parser": "^4.7.0", + "@typescript-eslint/eslint-plugin": "^4.15.1", + "@typescript-eslint/parser": "^4.15.1", "awslint": "0.0.0", "colors": "^1.4.0", - "eslint": "^7.13.0", + "eslint": "^7.20.0", "eslint-import-resolver-node": "^0.3.4", - "eslint-import-resolver-typescript": "^2.3.0", + "eslint-import-resolver-typescript": "^2.4.0", "eslint-plugin-cdk": "0.0.0", "eslint-plugin-import": "^2.22.1", - "eslint-plugin-jest": "^24.1.3", + "eslint-plugin-jest": "^24.1.5", "fs-extra": "^9.1.0", "jest": "^26.6.3", - "jsii": "^1.15.0", - "jsii-pacmak": "^1.15.0", + "jsii": "^1.21.0", + "jsii-pacmak": "^1.21.0", "markdownlint-cli": "^0.26.0", "nodeunit": "^0.11.3", "nyc": "^15.1.0", - "semver": "^7.3.2", - "ts-jest": "^26.4.4", - "typescript": "~3.9.7", + "semver": "^7.3.4", + "ts-jest": "^26.5.1", + "typescript": "~3.9.9", "yargs": "^16.2.0", "yarn-cling": "0.0.0" }, diff --git a/tools/cdk-integ-tools/bin/cdk-integ.ts b/tools/cdk-integ-tools/bin/cdk-integ.ts index 49110c8dccb17..30afa7c8102b9 100644 --- a/tools/cdk-integ-tools/bin/cdk-integ.ts +++ b/tools/cdk-integ-tools/bin/cdk-integ.ts @@ -14,7 +14,7 @@ async function main() { .option('dry-run', { type: 'boolean', default: false, desc: 'do not actually deploy the stack. just update the snapshot (not recommended!)' }) .argv; - const tests = await new IntegrationTests('test').fromCliArgs(argv._); + const tests = await new IntegrationTests('test').fromCliArgs(argv._.map(x => x.toString())); if (argv.list) { process.stdout.write(tests.map(t => t.name).join(' ') + '\n'); diff --git a/tools/cdk-integ-tools/lib/integ-helpers.ts b/tools/cdk-integ-tools/lib/integ-helpers.ts index 3a81fe8466465..7e7e4ee460e7f 100644 --- a/tools/cdk-integ-tools/lib/integ-helpers.ts +++ b/tools/cdk-integ-tools/lib/integ-helpers.ts @@ -337,7 +337,11 @@ export const DEFAULT_SYNTH_OPTIONS = { }, ], }, + // Enable feature flags for all integ tests '@aws-cdk/aws-ecr-assets:dockerIgnoreSupport': true, + '@aws-cdk/aws-kms:defaultKeyPolicies': true, + '@aws-cdk/core:enableStackNameDuplicates': true, + '@aws-cdk/aws-secretsmanager:parseOwnedSecretName': true, }, env: { CDK_INTEG_ACCOUNT: '12345678', diff --git a/tools/cdk-integ-tools/package.json b/tools/cdk-integ-tools/package.json index e1eb7d6946a65..bb9fbacb970b0 100644 --- a/tools/cdk-integ-tools/package.json +++ b/tools/cdk-integ-tools/package.json @@ -30,7 +30,7 @@ "license": "Apache-2.0", "devDependencies": { "@types/fs-extra": "^8.1.1", - "@types/yargs": "^15.0.10", + "@types/yargs": "^15.0.13", "cdk-build-tools": "0.0.0", "pkglint": "0.0.0" }, diff --git a/tools/cfn2ts/lib/codegen.ts b/tools/cfn2ts/lib/codegen.ts index f1e9f184e67af..08d0636a841f5 100644 --- a/tools/cfn2ts/lib/codegen.ts +++ b/tools/cfn2ts/lib/codegen.ts @@ -684,6 +684,15 @@ export default class CodeGenerator { this.code.line(`const errors = new ${CORE}.ValidationResults();`); + // check that the argument is an object + // normally, we would have to explicitly check for null here, + // as typeof null is 'object' in JavaScript, + // but validators are never called with null + // (as evidenced by the code below accessing properties of the argument without checking for null) + this.code.openBlock("if (typeof properties !== 'object')"); + this.code.line(`errors.collect(new ${CORE}.ValidationResult('Expected an object, but received: ' + JSON.stringify(properties)));`); + this.code.closeBlock(); + Object.keys(propSpecs).forEach(cfnPropName => { const propSpec = propSpecs[cfnPropName]; const propName = nameConversionTable[cfnPropName]; @@ -898,7 +907,7 @@ export default class CodeGenerator { this.code.line('/**'); before.forEach(line => this.code.line(` * ${line}`.trimRight())); if (link) { - this.code.line(` * @see ${link}`); + this.code.line(` * @link ${link}`); } this.code.line(' */'); return; diff --git a/tools/cfn2ts/package.json b/tools/cfn2ts/package.json index c97edb5b5d470..7a62104df3ea9 100644 --- a/tools/cfn2ts/package.json +++ b/tools/cfn2ts/package.json @@ -30,15 +30,15 @@ "license": "Apache-2.0", "dependencies": { "@aws-cdk/cfnspec": "0.0.0", - "codemaker": "^1.15.0", + "codemaker": "^1.21.0", "fast-json-patch": "^3.0.0-1", "fs-extra": "^9.1.0", "yargs": "^16.2.0" }, "devDependencies": { "@types/fs-extra": "^8.1.1", - "@types/jest": "^26.0.15", - "@types/yargs": "^15.0.10", + "@types/jest": "^26.0.20", + "@types/yargs": "^15.0.13", "cdk-build-tools": "0.0.0", "jest": "^26.6.3", "pkglint": "0.0.0" diff --git a/tools/eslint-plugin-cdk/package.json b/tools/eslint-plugin-cdk/package.json index ae52785ea619a..ac31c477d5687 100644 --- a/tools/eslint-plugin-cdk/package.json +++ b/tools/eslint-plugin-cdk/package.json @@ -14,15 +14,15 @@ "devDependencies": { "@types/eslint": "^7.2.6", "@types/fs-extra": "^8.1.1", - "@types/jest": "^26.0.15", - "@types/node": "^10.17.51", + "@types/jest": "^26.0.20", + "@types/node": "^10.17.52", "eslint-plugin-rulesdir": "^0.1.0", "jest": "^26.6.3", - "typescript": "~3.9.7" + "typescript": "~3.9.9" }, "dependencies": { - "@typescript-eslint/parser": "^4.7.0", - "eslint": "^7.13.0", + "@typescript-eslint/parser": "^4.15.1", + "eslint": "^7.20.0", "fs-extra": "^9.1.0" }, "jest": { diff --git a/tools/nodeunit-shim/package.json b/tools/nodeunit-shim/package.json index 3aa95f20e3dfc..44b49ce65ddff 100644 --- a/tools/nodeunit-shim/package.json +++ b/tools/nodeunit-shim/package.json @@ -12,9 +12,9 @@ "build+test": "npm run build && npm test" }, "devDependencies": { - "@types/jest": "^26.0.15", - "@types/node": "^10.17.51", - "typescript": "~3.9.7" + "@types/jest": "^26.0.20", + "@types/node": "^10.17.52", + "typescript": "~3.9.9" }, "dependencies": { "jest": "^26.6.3" diff --git a/tools/pkglint/bin/pkglint.ts b/tools/pkglint/bin/pkglint.ts index 625c8ab00223f..0b5cd61ef1649 100644 --- a/tools/pkglint/bin/pkglint.ts +++ b/tools/pkglint/bin/pkglint.ts @@ -12,13 +12,17 @@ const argv = yargs // Our version of yargs doesn't support positional arguments yet const directory = argv._[0] || '.'; +if (typeof(directory) !== 'string') { + throw new Error(`First argument should be a string. Got: ${directory} (${typeof(directory)})`); +} + argv.directory = path.resolve(directory, process.cwd()); async function main(): Promise { const ruleClasses = require('../lib/rules'); // eslint-disable-line @typescript-eslint/no-require-imports const rules: ValidationRule[] = Object.keys(ruleClasses).map(key => new ruleClasses[key]()).filter(obj => obj instanceof ValidationRule); - const pkgs = findPackageJsons(directory); + const pkgs = findPackageJsons(argv.directory as string); rules.forEach(rule => pkgs.filter(pkg => pkg.shouldApply(rule)).forEach(pkg => rule.prepare(pkg))); rules.forEach(rule => pkgs.filter(pkg => pkg.shouldApply(rule)).forEach(pkg => rule.validate(pkg))); @@ -27,7 +31,7 @@ async function main(): Promise { pkgs.forEach(pkg => pkg.applyFixes()); } - pkgs.forEach(pkg => pkg.displayReports(directory)); + pkgs.forEach(pkg => pkg.displayReports(argv.directory as string)); if (pkgs.some(p => p.hasReports)) { throw new Error('Some package.json files had errors'); diff --git a/tools/pkglint/lib/rules.ts b/tools/pkglint/lib/rules.ts index 0d75c3b1113cf..67e26c9634712 100644 --- a/tools/pkglint/lib/rules.ts +++ b/tools/pkglint/lib/rules.ts @@ -46,9 +46,38 @@ export class DescriptionIsRequired extends ValidationRule { } } +/** + * Verify that all packages have a publishConfig with a publish tag set. + */ +export class PublishConfigTagIsRequired extends ValidationRule { + public readonly name = 'package-info/publish-config-tag'; + + public validate(pkg: PackageJson): void { + if (pkg.json.private) { return; } + + // While v2 is still under development, we publish all v2 packages with the 'next' + // distribution tag, while still tagging all v1 packages as 'latest'. + // The one exception is 'aws-cdk-lib', since it's a new package for v2. + const newV2Packages = ['aws-cdk-lib']; + const defaultPublishTag = (cdkMajorVersion() === 2 && !newV2Packages.includes(pkg.json.name)) ? 'next' : 'latest'; + + if (pkg.json.publishConfig?.tag !== defaultPublishTag) { + pkg.report({ + ruleName: this.name, + message: `publishConfig.tag must be ${defaultPublishTag}`, + fix: (() => { + const publishConfig = pkg.json.publishConfig ?? {}; + publishConfig.tag = defaultPublishTag; + pkg.json.publishConfig = publishConfig; + }), + }); + } + } +} + /** * Verify cdk.out directory is included in npmignore since we should not be - * publihsing it. + * publishing it. */ export class CdkOutMustBeNpmIgnored extends ValidationRule { diff --git a/tools/pkglint/package.json b/tools/pkglint/package.json index 5272d5a1b1508..0bfea5465a4a0 100644 --- a/tools/pkglint/package.json +++ b/tools/pkglint/package.json @@ -37,10 +37,10 @@ "devDependencies": { "@types/fs-extra": "^8.1.1", "@types/semver": "^7.3.4", - "@types/yargs": "^15.0.10", + "@types/yargs": "^15.0.13", "eslint-plugin-cdk": "0.0.0", "jest": "^26.6.3", - "typescript": "~3.9.7" + "typescript": "~3.9.9" }, "dependencies": { "case": "^1.6.3", @@ -48,7 +48,7 @@ "fs-extra": "^9.1.0", "glob": "^7.1.6", "npm-bundled": "^1.1.1", - "semver": "^7.3.2", + "semver": "^7.3.4", "yargs": "^16.2.0" } } diff --git a/tools/pkgtools/package.json b/tools/pkgtools/package.json index 556178ccfd165..18b4ef85db565 100644 --- a/tools/pkgtools/package.json +++ b/tools/pkgtools/package.json @@ -30,7 +30,7 @@ "license": "Apache-2.0", "devDependencies": { "@types/fs-extra": "^8.1.1", - "@types/yargs": "^15.0.10", + "@types/yargs": "^15.0.13", "cdk-build-tools": "0.0.0", "pkglint": "0.0.0" }, diff --git a/tools/ubergen/package.json b/tools/ubergen/package.json index bac96b9391693..65c88331540a8 100644 --- a/tools/ubergen/package.json +++ b/tools/ubergen/package.json @@ -33,7 +33,7 @@ }, "dependencies": { "fs-extra": "^9.1.0", - "typescript": "~3.9.7" + "typescript": "~3.9.9" }, "keywords": [ "aws", diff --git a/tools/yarn-cling/package.json b/tools/yarn-cling/package.json index 421d3314e0400..b1fcceb28877a 100644 --- a/tools/yarn-cling/package.json +++ b/tools/yarn-cling/package.json @@ -38,12 +38,12 @@ ] }, "devDependencies": { - "@types/jest": "^26.0.15", - "@types/node": "^10.17.51", + "@types/jest": "^26.0.20", + "@types/node": "^10.17.52", "@types/yarnpkg__lockfile": "^1.1.4", "jest": "^26.6.3", "pkglint": "0.0.0", - "typescript": "~3.9.7" + "typescript": "~3.9.9" }, "dependencies": { "@yarnpkg/lockfile": "^1.1.0" diff --git a/version.v1.json b/version.v1.json index 345b477275b1d..b3cba233aa620 100644 --- a/version.v1.json +++ b/version.v1.json @@ -1,3 +1,3 @@ { - "version": "1.89.0" + "version": "1.90.0" } diff --git a/yarn.lock b/yarn.lock index aaee849150073..bfc36ea7eb75a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,152 +2,158 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" - integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== +"@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" + integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== dependencies: "@babel/highlight" "^7.10.4" +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658" + integrity sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g== + dependencies: + "@babel/highlight" "^7.12.13" + "@babel/core@^7.1.0", "@babel/core@^7.7.5": - version "7.12.3" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.3.tgz#1b436884e1e3bff6fb1328dc02b208759de92ad8" - integrity sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/generator" "^7.12.1" - "@babel/helper-module-transforms" "^7.12.1" - "@babel/helpers" "^7.12.1" - "@babel/parser" "^7.12.3" - "@babel/template" "^7.10.4" - "@babel/traverse" "^7.12.1" - "@babel/types" "^7.12.1" + version "7.12.16" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.16.tgz#8c6ba456b23b680a6493ddcfcd9d3c3ad51cab7c" + integrity sha512-t/hHIB504wWceOeaOoONOhu+gX+hpjfeN6YRBT209X/4sibZQfSF1I0HFRRlBe97UZZosGx5XwUg1ZgNbelmNw== + dependencies: + "@babel/code-frame" "^7.12.13" + "@babel/generator" "^7.12.15" + "@babel/helper-module-transforms" "^7.12.13" + "@babel/helpers" "^7.12.13" + "@babel/parser" "^7.12.16" + "@babel/template" "^7.12.13" + "@babel/traverse" "^7.12.13" + "@babel/types" "^7.12.13" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.1" json5 "^2.1.2" lodash "^4.17.19" - resolve "^1.3.2" semver "^5.4.1" source-map "^0.5.0" -"@babel/generator@^7.12.1", "@babel/generator@^7.12.5", "@babel/generator@^7.4.0": - version "7.12.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.5.tgz#a2c50de5c8b6d708ab95be5e6053936c1884a4de" - integrity sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A== +"@babel/generator@^7.12.13", "@babel/generator@^7.12.15", "@babel/generator@^7.4.0": + version "7.12.15" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.15.tgz#4617b5d0b25cc572474cc1aafee1edeaf9b5368f" + integrity sha512-6F2xHxBiFXWNSGb7vyCUTBF8RCLY66rS0zEPcP8t/nQyXjha5EuK4z7H5o7fWG8B4M7y6mqVWq1J+1PuwRhecQ== dependencies: - "@babel/types" "^7.12.5" + "@babel/types" "^7.12.13" jsesc "^2.5.1" source-map "^0.5.0" -"@babel/helper-function-name@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz#d2d3b20c59ad8c47112fa7d2a94bc09d5ef82f1a" - integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ== - dependencies: - "@babel/helper-get-function-arity" "^7.10.4" - "@babel/template" "^7.10.4" - "@babel/types" "^7.10.4" - -"@babel/helper-get-function-arity@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2" - integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A== - dependencies: - "@babel/types" "^7.10.4" - -"@babel/helper-member-expression-to-functions@^7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.1.tgz#fba0f2fcff3fba00e6ecb664bb5e6e26e2d6165c" - integrity sha512-k0CIe3tXUKTRSoEx1LQEPFU9vRQfqHtl+kf8eNnDqb4AUJEy5pz6aIiog+YWtVm2jpggjS1laH68bPsR+KWWPQ== - dependencies: - "@babel/types" "^7.12.1" - -"@babel/helper-module-imports@^7.12.1": - version "7.12.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz#1bfc0229f794988f76ed0a4d4e90860850b54dfb" - integrity sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA== - dependencies: - "@babel/types" "^7.12.5" - -"@babel/helper-module-transforms@^7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz#7954fec71f5b32c48e4b303b437c34453fd7247c" - integrity sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w== - dependencies: - "@babel/helper-module-imports" "^7.12.1" - "@babel/helper-replace-supers" "^7.12.1" - "@babel/helper-simple-access" "^7.12.1" - "@babel/helper-split-export-declaration" "^7.11.0" - "@babel/helper-validator-identifier" "^7.10.4" - "@babel/template" "^7.10.4" - "@babel/traverse" "^7.12.1" - "@babel/types" "^7.12.1" +"@babel/helper-function-name@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz#93ad656db3c3c2232559fd7b2c3dbdcbe0eb377a" + integrity sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA== + dependencies: + "@babel/helper-get-function-arity" "^7.12.13" + "@babel/template" "^7.12.13" + "@babel/types" "^7.12.13" + +"@babel/helper-get-function-arity@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz#bc63451d403a3b3082b97e1d8b3fe5bd4091e583" + integrity sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg== + dependencies: + "@babel/types" "^7.12.13" + +"@babel/helper-member-expression-to-functions@^7.12.13": + version "7.12.16" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.16.tgz#41e0916b99f8d5f43da4f05d85f4930fa3d62b22" + integrity sha512-zYoZC1uvebBFmj1wFAlXwt35JLEgecefATtKp20xalwEK8vHAixLBXTGxNrVGEmTT+gzOThUgr8UEdgtalc1BQ== + dependencies: + "@babel/types" "^7.12.13" + +"@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== + dependencies: + "@babel/types" "^7.12.13" + +"@babel/helper-module-transforms@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.12.13.tgz#01afb052dcad2044289b7b20beb3fa8bd0265bea" + integrity sha512-acKF7EjqOR67ASIlDTupwkKM1eUisNAjaSduo5Cz+793ikfnpe7p4Q7B7EWU2PCoSTPWsQkR7hRUWEIZPiVLGA== + dependencies: + "@babel/helper-module-imports" "^7.12.13" + "@babel/helper-replace-supers" "^7.12.13" + "@babel/helper-simple-access" "^7.12.13" + "@babel/helper-split-export-declaration" "^7.12.13" + "@babel/helper-validator-identifier" "^7.12.11" + "@babel/template" "^7.12.13" + "@babel/traverse" "^7.12.13" + "@babel/types" "^7.12.13" lodash "^4.17.19" -"@babel/helper-optimise-call-expression@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673" - integrity sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg== +"@babel/helper-optimise-call-expression@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz#5c02d171b4c8615b1e7163f888c1c81c30a2aaea" + integrity sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA== dependencies: - "@babel/types" "^7.10.4" + "@babel/types" "^7.12.13" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.8.0": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" - integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.8.0": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz#174254d0f2424d8aefb4dd48057511247b0a9eeb" + integrity sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA== -"@babel/helper-replace-supers@^7.12.1": - version "7.12.5" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.12.5.tgz#f009a17543bbbbce16b06206ae73b63d3fca68d9" - integrity sha512-5YILoed0ZyIpF4gKcpZitEnXEJ9UoDRki1Ey6xz46rxOzfNMAhVIJMoune1hmPVxh40LRv1+oafz7UsWX+vyWA== +"@babel/helper-replace-supers@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.12.13.tgz#00ec4fb6862546bd3d0aff9aac56074277173121" + integrity sha512-pctAOIAMVStI2TMLhozPKbf5yTEXc0OJa0eENheb4w09SrgOWEs+P4nTOZYJQCqs8JlErGLDPDJTiGIp3ygbLg== dependencies: - "@babel/helper-member-expression-to-functions" "^7.12.1" - "@babel/helper-optimise-call-expression" "^7.10.4" - "@babel/traverse" "^7.12.5" - "@babel/types" "^7.12.5" + "@babel/helper-member-expression-to-functions" "^7.12.13" + "@babel/helper-optimise-call-expression" "^7.12.13" + "@babel/traverse" "^7.12.13" + "@babel/types" "^7.12.13" -"@babel/helper-simple-access@^7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz#32427e5aa61547d38eb1e6eaf5fd1426fdad9136" - integrity sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA== +"@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== dependencies: - "@babel/types" "^7.12.1" + "@babel/types" "^7.12.13" -"@babel/helper-split-export-declaration@^7.11.0": - version "7.11.0" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f" - integrity sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg== +"@babel/helper-split-export-declaration@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz#e9430be00baf3e88b0e13e6f9d4eaf2136372b05" + integrity sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg== dependencies: - "@babel/types" "^7.11.0" + "@babel/types" "^7.12.13" -"@babel/helper-validator-identifier@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" - integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== +"@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/helpers@^7.12.1": - version "7.12.5" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.12.5.tgz#1a1ba4a768d9b58310eda516c449913fe647116e" - integrity sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA== +"@babel/helpers@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.12.13.tgz#3c75e993632e4dadc0274eae219c73eb7645ba47" + integrity sha512-oohVzLRZ3GQEk4Cjhfs9YkJA4TdIDTObdBEZGrd6F/T0GPSnuV6l22eMcxlvcvzVIPH3VTtxbseudM1zIE+rPQ== dependencies: - "@babel/template" "^7.10.4" - "@babel/traverse" "^7.12.5" - "@babel/types" "^7.12.5" + "@babel/template" "^7.12.13" + "@babel/traverse" "^7.12.13" + "@babel/types" "^7.12.13" -"@babel/highlight@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" - integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== +"@babel/highlight@^7.10.4", "@babel/highlight@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.12.13.tgz#8ab538393e00370b26271b01fa08f7f27f2e795c" + integrity sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww== dependencies: - "@babel/helper-validator-identifier" "^7.10.4" + "@babel/helper-validator-identifier" "^7.12.11" chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.10.4", "@babel/parser@^7.12.3", "@babel/parser@^7.12.5", "@babel/parser@^7.4.3": - version "7.12.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.5.tgz#b4af32ddd473c0bfa643bd7ff0728b8e71b81ea0" - integrity sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ== +"@babel/parser@^7.1.0", "@babel/parser@^7.12.13", "@babel/parser@^7.12.16", "@babel/parser@^7.4.3": + version "7.12.16" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.16.tgz#cc31257419d2c3189d394081635703f549fc1ed4" + integrity sha512-c/+u9cqV6F0+4Hpq01jnJO+GLp2DdT63ppz9Xa+6cHaajM9VFzK/iDXiKK65YtpeVwu+ctfS6iqlMqRgQRzeCw== "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" @@ -164,11 +170,11 @@ "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-class-properties@^7.8.3": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz#bcb297c5366e79bebadef509549cd93b04f19978" - integrity sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA== + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" @@ -227,51 +233,42 @@ "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-top-level-await@^7.8.3": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz#dd6c0b357ac1bb142d98537450a319625d13d2a0" - integrity sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/template@^7.10.4", "@babel/template@^7.3.3", "@babel/template@^7.4.0": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" - integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/parser" "^7.10.4" - "@babel/types" "^7.10.4" - -"@babel/traverse@^7.1.0", "@babel/traverse@^7.12.1", "@babel/traverse@^7.12.5", "@babel/traverse@^7.4.3": - version "7.12.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.5.tgz#78a0c68c8e8a35e4cacfd31db8bb303d5606f095" - integrity sha512-xa15FbQnias7z9a62LwYAA5SZZPkHIXpd42C6uW68o8uTuua96FHZy1y61Va5P/i83FAAcMpW8+A/QayntzuqA== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/generator" "^7.12.5" - "@babel/helper-function-name" "^7.10.4" - "@babel/helper-split-export-declaration" "^7.11.0" - "@babel/parser" "^7.12.5" - "@babel/types" "^7.12.5" + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz#c5f0fa6e249f5b739727f923540cf7a806130178" + integrity sha512-A81F9pDwyS7yM//KwbCSDqy3Uj4NMIurtplxphWxoYtNPov7cJsDkAFNNyVlIZ3jwGycVsurZ+LtOA8gZ376iQ== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/template@^7.12.13", "@babel/template@^7.3.3", "@babel/template@^7.4.0": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327" + integrity sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA== + dependencies: + "@babel/code-frame" "^7.12.13" + "@babel/parser" "^7.12.13" + "@babel/types" "^7.12.13" + +"@babel/traverse@^7.1.0", "@babel/traverse@^7.12.13", "@babel/traverse@^7.4.3": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.13.tgz#689f0e4b4c08587ad26622832632735fb8c4e0c0" + integrity sha512-3Zb4w7eE/OslI0fTp8c7b286/cQps3+vdLW3UcwC8VSJC6GbKn55aeVVu2QJNuCDoeKyptLOFrPq8WqZZBodyA== + dependencies: + "@babel/code-frame" "^7.12.13" + "@babel/generator" "^7.12.13" + "@babel/helper-function-name" "^7.12.13" + "@babel/helper-split-export-declaration" "^7.12.13" + "@babel/parser" "^7.12.13" + "@babel/types" "^7.12.13" debug "^4.1.0" globals "^11.1.0" lodash "^4.17.19" -"@babel/types@^7.0.0", "@babel/types@^7.10.4", "@babel/types@^7.11.0", "@babel/types@^7.12.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.0": - version "7.12.6" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.6.tgz#ae0e55ef1cce1fbc881cd26f8234eb3e657edc96" - integrity sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA== - dependencies: - "@babel/helper-validator-identifier" "^7.10.4" - lodash "^4.17.19" - to-fast-properties "^2.0.0" - -"@babel/types@^7.12.1": - version "7.12.7" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.7.tgz#6039ff1e242640a29452c9ae572162ec9a8f5d13" - integrity sha512-MNyI92qZq6jrQkXvtIiykvl4WtoRrVV9MPn+ZfsoEENjiWcBQ3ZSHrkxnJWgWtLX3XXqX5hrSQ+X69wkmesXuQ== +"@babel/types@^7.0.0", "@babel/types@^7.12.13", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.0": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.13.tgz#8be1aa8f2c876da11a9cf650c0ecf656913ad611" + integrity sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ== dependencies: - "@babel/helper-validator-identifier" "^7.10.4" + "@babel/helper-validator-identifier" "^7.12.11" lodash "^4.17.19" to-fast-properties "^2.0.0" @@ -293,10 +290,10 @@ exec-sh "^0.3.2" minimist "^1.2.0" -"@eslint/eslintrc@^0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.2.1.tgz#f72069c330461a06684d119384435e12a5d76e3c" - integrity sha512-XRUeBZ5zBWLYgSANMpThFddrZZkEbGHgUdt5UJjZfnlN9BGCiUBrf+nvbRupSjMvqzwnQN0qwCmOxITt1cfywA== +"@eslint/eslintrc@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.3.0.tgz#d736d6963d7003b6514e6324bec9c602ac340318" + integrity sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg== dependencies: ajv "^6.12.4" debug "^4.1.1" @@ -305,7 +302,7 @@ ignore "^4.0.6" import-fresh "^3.2.1" js-yaml "^3.13.1" - lodash "^4.17.19" + lodash "^4.17.20" minimatch "^3.0.4" strip-json-comments "^3.1.1" @@ -395,9 +392,9 @@ resolve-from "^5.0.0" "@istanbuljs/schema@^0.1.2": - version "0.1.2" - resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" - integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== "@jest/console@^26.6.2": version "26.6.2" @@ -570,10 +567,10 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" -"@jsii/spec@^1.15.0": - version "1.15.0" - resolved "https://registry.yarnpkg.com/@jsii/spec/-/spec-1.15.0.tgz#e7f3a18d231ef6d1826ce7257de7b2f6e2071f40" - integrity sha512-aybTXziVMQcCp2EGIMCJgAs4uvXtN/iBVeJMyBRzqznhtctG4flOu37FjIdib/OOJLrRLt4NAA95R5kNm/jLpA== +"@jsii/spec@^1.21.0": + version "1.21.0" + resolved "https://registry.yarnpkg.com/@jsii/spec/-/spec-1.21.0.tgz#363c740567747aa03083d2f7bf9ef14e55d8ae9d" + integrity sha512-MWQpJKciYytEmYzuwsT+4UM1JPiQyCAqr3PfkZxuosoPUaF7vBrWSs2+TXDb5dcCwpSnSim9iKZrM/Uc2ppUzA== dependencies: jsonschema "^1.4.0" @@ -1270,18 +1267,18 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" -"@nodelib/fs.scandir@2.1.3": - version "2.1.3" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b" - integrity sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw== +"@nodelib/fs.scandir@2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69" + integrity sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA== dependencies: - "@nodelib/fs.stat" "2.0.3" + "@nodelib/fs.stat" "2.0.4" run-parallel "^1.1.9" -"@nodelib/fs.stat@2.0.3", "@nodelib/fs.stat@^2.0.2": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz#34dc5f4cabbc720f4e60f75a747e7ecd6c175bd3" - integrity sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA== +"@nodelib/fs.stat@2.0.4", "@nodelib/fs.stat@^2.0.2": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz#a3f2dd61bab43b8db8fa108a121cfffe4c676655" + integrity sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q== "@nodelib/fs.stat@^1.1.2": version "1.1.3" @@ -1289,50 +1286,55 @@ integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== "@nodelib/fs.walk@^1.2.3": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz#011b9202a70a6366e436ca5c065844528ab04976" - integrity sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ== + version "1.2.6" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz#cce9396b30aa5afe9e3756608f5831adcb53d063" + integrity sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow== dependencies: - "@nodelib/fs.scandir" "2.1.3" + "@nodelib/fs.scandir" "2.1.4" fastq "^1.6.0" -"@octokit/auth-token@^2.4.0": - version "2.4.3" - resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.3.tgz#b868b5f2366533a7e62933eaa1181a8924228cc4" - integrity sha512-fdGoOQ3kQJh+hrilc0Plg50xSfaCKOeYN9t6dpJKXN9BxhhfquL0OzoQXg3spLYymL5rm29uPeI3KEXRaZQ9zg== +"@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== dependencies: - "@octokit/types" "^5.0.0" + "@octokit/types" "^6.0.3" -"@octokit/core@^3.0.0": - version "3.2.1" - resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.2.1.tgz#9e04df3f4e7f825ac0559327490ce34299140af5" - integrity sha512-XfFSDDwv6tclUenS0EmB6iA7u+4aOHBT1Lz4PtQNQQg3hBbNaR/+Uv5URU+egeIuuGAiMRiDyY92G4GBOWOqDA== +"@octokit/core@^3.2.3": + version "3.2.5" + resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.2.5.tgz#57becbd5fd789b0592b915840855f3a5f233d554" + integrity sha512-+DCtPykGnvXKWWQI0E1XD+CCeWSBhB6kwItXqfFmNBlIlhczuDPbg+P6BtLnVBaRJDAjv+1mrUJuRsFSjktopg== dependencies: - "@octokit/auth-token" "^2.4.0" - "@octokit/graphql" "^4.3.1" - "@octokit/request" "^5.4.0" - "@octokit/types" "^5.0.0" + "@octokit/auth-token" "^2.4.4" + "@octokit/graphql" "^4.5.8" + "@octokit/request" "^5.4.12" + "@octokit/types" "^6.0.3" before-after-hook "^2.1.0" universal-user-agent "^6.0.0" "@octokit/endpoint@^6.0.1": - version "6.0.9" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.9.tgz#c6a772e024202b1bd19ab69f90e0536a2598b13e" - integrity sha512-3VPLbcCuqji4IFTclNUtGdp9v7g+nspWdiCUbK3+iPMjJCZ6LEhn1ts626bWLOn0GiDb6j+uqGvPpqLnY7pBgw== + version "6.0.11" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.11.tgz#082adc2aebca6dcefa1fb383f5efb3ed081949d1" + integrity sha512-fUIPpx+pZyoLW4GCs3yMnlj2LfoXTWDUVPTC4V3MUEKZm48W+XYpeWSZCv+vYF1ZABUm2CqnDVf1sFtIYrj7KQ== dependencies: - "@octokit/types" "^5.0.0" + "@octokit/types" "^6.0.3" is-plain-object "^5.0.0" universal-user-agent "^6.0.0" -"@octokit/graphql@^4.3.1": - version "4.5.7" - resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.5.7.tgz#f4562dcd9e80ea94602068e85aefac19a88f8578" - integrity sha512-Gk0AR+DcwIK/lK/GX+OQ99UqtenQhcbrhHHfOYlrCQe17ADnX3EKAOKRsAZ9qZvpi5MuwWm/Nm+9aO2kTDSdyA== +"@octokit/graphql@^4.5.8": + version "4.6.0" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.6.0.tgz#f9abca55f82183964a33439d5264674c701c3327" + integrity sha512-CJ6n7izLFXLvPZaWzCQDjU/RP+vHiZmWdOunaCS87v+2jxMsW9FB5ktfIxybRBxZjxuJGRnxk7xJecWTVxFUYQ== dependencies: "@octokit/request" "^5.3.0" - "@octokit/types" "^5.0.0" + "@octokit/types" "^6.0.3" universal-user-agent "^6.0.0" +"@octokit/openapi-types@^4.0.3": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-4.0.4.tgz#96fcce11e929802898646205ac567e5df592f82b" + integrity sha512-31zY8JIuz3h6RAFOnyA8FbOwhILILiBu1qD81RyZZWY7oMBhIdBn6MaAmnnptLhB4jk0g50nkQkUVP4kUzppcA== + "@octokit/plugin-enterprise-rest@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz#e07896739618dab8da7d4077c658003775f95437" @@ -1345,17 +1347,17 @@ dependencies: "@octokit/types" "^2.0.1" -"@octokit/plugin-paginate-rest@^2.2.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.6.0.tgz#03416396e7a227b268c5b827365238f620a9c5c1" - integrity sha512-o+O8c1PqsC5++BHXfMZabRRsBIVb34tXPWyQLyp2IXq5MmkxdipS7TXM4Y9ldL1PzY9CTrCsn/lzFFJGM3oRRA== +"@octokit/plugin-paginate-rest@^2.6.2": + version "2.9.1" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.9.1.tgz#e9bb34a89b7ed5b801f1c976feeb9b0078ecd201" + integrity sha512-8wnuWGjwDIEobbBet2xAjZwgiMVTgIer5wBsnGXzV3lJ4yqphLU2FEMpkhSrDx7y+WkZDfZ+V+1cFMZ1mAaFag== dependencies: - "@octokit/types" "^5.5.0" + "@octokit/types" "^6.8.0" -"@octokit/plugin-request-log@^1.0.0": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.2.tgz#394d59ec734cd2f122431fbaf05099861ece3c44" - integrity sha512-oTJSNAmBqyDR41uSMunLQKMX0jmEXbwD1fpz8FG27lScV3RhtGfBa1/BBLym+PxcC16IBlF7KH9vP1BUYxA+Eg== +"@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" @@ -1365,12 +1367,12 @@ "@octokit/types" "^2.0.1" deprecation "^2.3.1" -"@octokit/plugin-rest-endpoint-methods@4.2.1": - version "4.2.1" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.2.1.tgz#8224833a45c3394836dc6e86f1e6c49269a2c350" - integrity sha512-QyFr4Bv807Pt1DXZOC5a7L5aFdrwz71UHTYoHVajYV5hsqffWm8FUl9+O7nxRu5PDMtB/IKrhFqTmdBTK5cx+A== +"@octokit/plugin-rest-endpoint-methods@4.10.3": + version "4.10.3" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.10.3.tgz#d78ddf926bca3b81a4d9b79a463d32b3750a4485" + integrity sha512-CsNQeVY34Vs9iea2Z9/TCPlebxv6KpjO9f1BUPz+14qundTSYT9kgf8j5wA1k37VstfBQ4xnuURYdnbGzJBJXw== dependencies: - "@octokit/types" "^5.5.0" + "@octokit/types" "^6.8.3" deprecation "^2.3.1" "@octokit/request-error@^1.0.2": @@ -1383,22 +1385,22 @@ once "^1.4.0" "@octokit/request-error@^2.0.0": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.0.3.tgz#b51b200052bf483f6fa56c9e7e3aa51ead36ecd8" - integrity sha512-GgD5z8Btm301i2zfvJLk/mkhvGCdjQ7wT8xF9ov5noQY8WbKZDH9cOBqXzoeKd1mLr1xH2FwbtGso135zGBgTA== + version "2.0.5" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.0.5.tgz#72cc91edc870281ad583a42619256b380c600143" + integrity sha512-T/2wcCFyM7SkXzNoyVNWjyVlUwBvW3igM3Btr/eKYiPmucXTtkxt2RBsf6gn3LTzaLSLTQtNmvg+dGsOxQrjZg== dependencies: - "@octokit/types" "^5.0.1" + "@octokit/types" "^6.0.3" deprecation "^2.0.0" once "^1.4.0" -"@octokit/request@^5.2.0", "@octokit/request@^5.3.0", "@octokit/request@^5.4.0": - version "5.4.10" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.10.tgz#402d2c53768bde12b99348329ba4129746aebb9c" - integrity sha512-egA49HkqEORVGDZGav1mh+VD+7uLgOxtn5oODj6guJk0HCy+YBSYapFkSLFgeYj3Fr18ZULKGURkjyhkAChylw== +"@octokit/request@^5.2.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== dependencies: "@octokit/endpoint" "^6.0.1" "@octokit/request-error" "^2.0.0" - "@octokit/types" "^5.0.0" + "@octokit/types" "^6.7.1" deprecation "^2.0.0" is-plain-object "^5.0.0" node-fetch "^2.6.1" @@ -1427,15 +1429,15 @@ once "^1.4.0" universal-user-agent "^4.0.0" -"@octokit/rest@^18.0.9": - version "18.0.9" - resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.0.9.tgz#964d707d914eb34b1787895fdcacff96de47844d" - integrity sha512-CC5+cIx974Ygx9lQNfUn7/oXDQ9kqGiKUC6j1A9bAVZZ7aoTF8K6yxu0pQhQrLBwSl92J6Z3iVDhGhGFgISCZg== +"@octokit/rest@^18.1.1": + version "18.1.1" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.1.1.tgz#bd7053c28db3577c936029e9da6bfbd046474a2f" + integrity sha512-ZcCHMyfGT1qtJD72usigAfUQ6jU89ZUPFb2AOubR6WZ7/RRFVZUENVm1I2yvJBUicqTujezPW9cY1+o3Mb4rNA== dependencies: - "@octokit/core" "^3.0.0" - "@octokit/plugin-paginate-rest" "^2.2.0" - "@octokit/plugin-request-log" "^1.0.0" - "@octokit/plugin-rest-endpoint-methods" "4.2.1" + "@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.10.3" "@octokit/types@^2.0.0", "@octokit/types@^2.0.1": version "2.16.2" @@ -1444,17 +1446,17 @@ dependencies: "@types/node" ">= 8" -"@octokit/types@^5.0.0", "@octokit/types@^5.0.1", "@octokit/types@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-5.5.0.tgz#e5f06e8db21246ca102aa28444cdb13ae17a139b" - integrity sha512-UZ1pErDue6bZNjYOotCNveTXArOMZQFG6hKJfOnGnulVCMcVVi7YIIuuR4WfBhjo7zgpmzn/BkPDnUXtNx+PcQ== +"@octokit/types@^6.0.3", "@octokit/types@^6.7.1", "@octokit/types@^6.8.0", "@octokit/types@^6.8.3": + version "6.8.5" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.8.5.tgz#797dfdad8c75718e97dc687d4c9fc49200ca8d17" + integrity sha512-ZsQawftZoi0kSF2pCsdgLURbOjtVcHnBOXiSxBKSNF56CRjARt5rb/g8WJgqB8vv4lgUEHrv06EdDKYQ22vA9Q== dependencies: - "@types/node" ">= 8" + "@octokit/openapi-types" "^4.0.3" -"@sinonjs/commons@^1", "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.1": - version "1.8.1" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.1.tgz#e7df00f98a203324f6dc7cc606cad9d4a8ab2217" - integrity sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw== +"@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== dependencies: type-detect "4.0.8" @@ -1465,18 +1467,10 @@ dependencies: "@sinonjs/commons" "^1.7.0" -"@sinonjs/formatio@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-5.0.1.tgz#f13e713cb3313b1ab965901b01b0828ea6b77089" - integrity sha512-KaiQ5pBf1MpS09MuA0kp6KBQt2JUOQycqVG1NZXvzeaXe5LGFqAKueIS0bw4w0P9r7KuBSVdUk5QjXsUdu2CxQ== - dependencies: - "@sinonjs/commons" "^1" - "@sinonjs/samsam" "^5.0.2" - -"@sinonjs/samsam@^5.0.2", "@sinonjs/samsam@^5.2.0": - version "5.2.0" - resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-5.2.0.tgz#fcff83ab86f83b5498f4a967869c079408d9b5eb" - integrity sha512-CaIcyX5cDsjcW/ab7HposFWzV1kC++4HNsfnEdFJa7cP1QIuILAKV+BgfeqRXhcnSAc76r/Rh/O5C+300BwUIw== +"@sinonjs/samsam@^5.3.1": + version "5.3.1" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-5.3.1.tgz#375a45fe6ed4e92fca2fb920e007c48232a6507f" + integrity sha512-1Hc0b1TtyfBu8ixF/tpfSHTVWKwCBLY4QJbkgnE7HcwyvT2xArDxb4K7dMgqRm3szI+LJbzmW/s4xxEhv6hwDg== dependencies: "@sinonjs/commons" "^1.6.0" lodash.get "^4.4.2" @@ -1499,10 +1493,10 @@ dependencies: "@types/glob" "*" -"@types/aws-lambda@^8.10.64": - version "8.10.64" - resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.64.tgz#4bdcb725aef96bef0cb1decf19c7efff1df22fe7" - integrity sha512-LRKk2UQCSi7BsO5TlfSI8cTNpOGz+MH6+RXEWtuZmxJficQgxwEYJDiKVirzgyiHce0L0F4CqCVvKTwblAeOUw== +"@types/aws-lambda@^8.10.72": + version "8.10.72" + resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.72.tgz#af2a6eeaf39be9674e3856f1870d9d15cf75e2e0" + integrity sha512-jOrTwAhSiUtBIN/QsWNKlI4+4aDtpZ0sr2BRvKW6XQZdspgHUSHPcuzxbzCRiHUiDQ+0026u5TSE38VyIhNnfA== "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": version "7.1.12" @@ -1531,9 +1525,9 @@ "@babel/types" "^7.0.0" "@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": - version "7.0.15" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.15.tgz#db9e4238931eb69ef8aab0ad6523d4d4caa39d03" - integrity sha512-Pzh9O3sTK8V6I1olsXpCfj2k/ygO2q1X0vhhnDrEQyYLHZesWz+zMZMVcwXLCYf0U36EtmyYaFGPfXlTtDHe3A== + version "7.11.0" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.11.0.tgz#b9a1efa635201ba9bc850323a8793ee2d36c04a0" + integrity sha512-kSjgDMZONiIfSH1Nxcr5JIRMwUetDki63FSQfpTCz8ogF3Ulqm8+mr5f78dUYs6vMiB6gBusQqfQmBvHZj/lwg== dependencies: "@babel/types" "^7.3.0" @@ -1546,9 +1540,9 @@ "@types/json-schema" "*" "@types/estree@*": - version "0.0.45" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.45.tgz#e9387572998e5ecdac221950dab3e8c3b16af884" - integrity sha512-jnqIUKDUqJbDIUxm0Uj7bnlMnRm1T/eZ9N+AVMqhPgzrba2GhGG5o/jCTwmdPK709nEZsGoMzXEDUjcXHa3W0g== + version "0.0.46" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.46.tgz#0fb6bfbbeabd7a30880504993369c4bf1deab1fe" + integrity sha512-laIjwTQaD+5DukBZaygQ79K1Z0jb1bPEMRrkXSLjtCcZm+abyp5YbrqpSLzD42FwWW6gK/aS4NYpJ804nG2brg== "@types/fs-extra@^8.1.1": version "8.1.1" @@ -1566,9 +1560,9 @@ "@types/node" "*" "@types/graceful-fs@^4.1.2": - version "4.1.4" - resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.4.tgz#4ff9f641a7c6d1a3508ff88bc3141b152772e753" - integrity sha512-mWA/4zFQhfvOA8zWkXobwJvBD7vzcxgrOQ0J5CH1votGqdq9m7+FwtGaqyCZqC3NyyBkc9z4m+iry4LlqcMWJg== + version "4.1.5" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" + integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== dependencies: "@types/node" "*" @@ -1591,18 +1585,18 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@26.x", "@types/jest@^26.0.15": - version "26.0.15" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.15.tgz#12e02c0372ad0548e07b9f4e19132b834cb1effe" - integrity sha512-s2VMReFXRg9XXxV+CW9e5Nz8fH2K1aEhwgjUqPPbQd7g95T0laAcvLv032EhFHIa5GHsZ8W7iJEQVaJq6k3Gog== +"@types/jest@26.x", "@types/jest@^26.0.20": + version "26.0.20" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.20.tgz#cd2f2702ecf69e86b586e1f5223a60e454056307" + integrity sha512-9zi2Y+5USJRxd0FsahERhBwlcvFh6D2GLQnY2FH2BzK8J9s9omvNHIbvABwIluXa0fD8XVKMLTO0aOEuUfACAA== dependencies: jest-diff "^26.0.0" pretty-format "^26.0.0" "@types/json-schema@*", "@types/json-schema@^7.0.3", "@types/json-schema@^7.0.6": - version "7.0.6" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0" - integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw== + version "7.0.7" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" + integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== "@types/json5@^0.0.29": version "0.0.29" @@ -1621,10 +1615,10 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.168.tgz#fe24632e79b7ade3f132891afff86caa5e5ce008" integrity sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q== -"@types/md5@^2.2.1": - version "2.2.1" - resolved "https://registry.yarnpkg.com/@types/md5/-/md5-2.2.1.tgz#0e2d65d8f6cc91aafbc9be5a4c08fee9250406ce" - integrity sha512-bZB0jqBL7JETFqvRKyuDETFceFaVcLm2MBPP5LFEEL/SZuqLnyvzF37tXmMERDncC3oeEj/fOUw88ftJeMpZaw== +"@types/md5@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@types/md5/-/md5-2.3.0.tgz#3b6a623091160f4dc75be3173e25f2110dc3fa1f" + integrity sha512-556YJ7ejzxIqSSxzyGGpctuZOarNZJt/zlEkhmmDc1f/slOEANHuwu2ZX7YaZ40rMiWoxt8GvAhoDpW1cmSy6A== dependencies: "@types/node" "*" @@ -1651,14 +1645,14 @@ integrity sha1-m6It838H43gP/4Ux0aOOYz+UV6U= "@types/node@*", "@types/node@>= 8": - version "14.14.7" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.7.tgz#8ea1e8f8eae2430cf440564b98c6dfce1ec5945d" - integrity sha512-Zw1vhUSQZYw+7u5dAwNbIA9TuTotpzY/OF7sJM9FqPOF3SPjKnxrjoTktXDZgUjybf4cWVBP7O8wvKdSaGHweg== + version "14.14.28" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.28.tgz#cade4b64f8438f588951a6b35843ce536853f25b" + integrity sha512-lg55ArB+ZiHHbBBttLpzD07akz0QPrZgUODNakeC09i62dnrywr9mFErHuaPlB6I7z+sEbK+IYmplahvplCj2g== -"@types/node@^10.17.51": - version "10.17.51" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.51.tgz#639538575befbcf3d3861f95c41de8e47124d674" - integrity sha512-KANw+MkL626tq90l++hGelbl67irOJzGhUJk6a1Bt8QHOeh9tztJx+L0AqttraWKinmZn7Qi5lJZJzx45Gq0dg== +"@types/node@^10.17.52": + version "10.17.52" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.52.tgz#dc960d4e256331b3c697b7a573ee98b882febee5" + integrity sha512-bKnO8Rcj03i6JTzweabq96k29uVNcXGB0bkwjVQTFagDgxxNged18281AZ0nTMHl+aFpPPWyPrk4Z3+NtW/z5w== "@types/nodeunit@^0.0.31": version "0.0.31" @@ -1671,9 +1665,9 @@ integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== "@types/prettier@^2.0.0": - version "2.1.5" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.1.5.tgz#b6ab3bba29e16b821d84e09ecfaded462b816b00" - integrity sha512-UEyp8LwZ4Dg30kVU2Q3amHHyTn1jEdhCIE59ANed76GaT1Vp76DD3ZWSAxgCrw6wJ0TqeoBpqmfUHiUDPs//HQ== + version "2.2.1" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.2.1.tgz#374e31645d58cb18a07b3ecd8e9dede4deb2cccd" + integrity sha512-DxZZbyMAM9GWEzXL+BMZROWz9oo6A9EilwwOMET2UVu2uZTqMWS5S69KVtuVKaRjCUpcrOXRalet86/OpG4kqw== "@types/promptly@^3.0.1": version "3.0.1" @@ -1697,10 +1691,10 @@ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.4.tgz#43d7168fec6fa0988bb1a513a697b29296721afb" integrity sha512-+nVsLKlcUCeMzD2ufHEYuJ9a2ovstb6Dp52A5VsoKxDXgvE051XgHI/33I1EymwkRGQkwnA0LkhnUzituGs4EQ== -"@types/sinon@^9.0.9": - version "9.0.9" - resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-9.0.9.tgz#115843b491583f924080f684b6d0d7438344f73c" - integrity sha512-z/y8maYOQyYLyqaOB+dYQ6i0pxKLOsfwCmHmn4T7jS/SDHicIslr37oE3Dg8SCqKrKeBy6Lemu7do2yy+unLrw== +"@types/sinon@^9.0.10": + version "9.0.10" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-9.0.10.tgz#7fb9bcb6794262482859cab66d59132fca18fcf7" + integrity sha512-/faDC0erR06wMdybwI/uR8wEKV/E83T0k4sepIpB7gXuy2gzx2xiOjmztq6a2Y6rIGJ04D+6UU0VBmWy+4HEMA== dependencies: "@types/sinonjs__fake-timers" "*" @@ -1721,10 +1715,10 @@ dependencies: string-width "*" -"@types/table@^5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@types/table/-/table-5.0.0.tgz#67c3821138eb41d538c3d9286771c6cdeeac7172" - integrity sha512-fQLtGLZXor264zUPWI95WNDsZ3QV43/c0lJpR/h1hhLJumXRmHNsrvBfEzW2YMhb0EWCsn4U6h82IgwsajAuTA== +"@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== "@types/uuid@^8.3.0": version "8.3.0" @@ -1751,21 +1745,14 @@ yaml "*" "@types/yargs-parser@*": - version "15.0.0" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" - integrity sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw== - -"@types/yargs@^15.0.0": - version "15.0.9" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.9.tgz#524cd7998fe810cdb02f26101b699cccd156ff19" - integrity sha512-HmU8SeIRhZCWcnRskCs36Q1Q00KBV6Cqh/ora8WN1+22dY07AZdn6Gel8QZ3t26XYPImtcL8WV/eqjhVmMEw4g== - dependencies: - "@types/yargs-parser" "*" + version "20.2.0" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9" + integrity sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA== -"@types/yargs@^15.0.10": - version "15.0.10" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.10.tgz#0fe3c8173a0d5c3e780b389050140c3f5ea6ea74" - integrity sha512-z8PNtlhrj7eJNLmrAivM7rjBESG6JwC5xP3RVk12i/8HVP7Xnx/sEmERnRImyEuUaJfO942X0qMOYsoupaJbZQ== +"@types/yargs@^15.0.0", "@types/yargs@^15.0.13": + version "15.0.13" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.13.tgz#34f7fec8b389d7f3c1fd08026a5763e072d3c6dc" + integrity sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ== dependencies: "@types/yargs-parser" "*" @@ -1774,13 +1761,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.14.0": - version "4.14.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.14.0.tgz#92db8e7c357ed7d69632d6843ca70b71be3a721d" - integrity sha512-IJ5e2W7uFNfg4qh9eHkHRUCbgZ8VKtGwD07kannJvM5t/GU8P8+24NX8gi3Hf5jST5oWPY8kyV1s/WtfiZ4+Ww== +"@typescript-eslint/eslint-plugin@^4.15.1": + version "4.15.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.15.1.tgz#835f64aa0a403e5e9e64c10ceaf8d05c3f015180" + integrity sha512-yW2epMYZSpNJXZy22Biu+fLdTG8Mn6b22kR3TqblVk50HGNV8Zya15WAXuQCr8tKw4Qf1BL4QtI6kv6PCkLoJw== dependencies: - "@typescript-eslint/experimental-utils" "4.14.0" - "@typescript-eslint/scope-manager" "4.14.0" + "@typescript-eslint/experimental-utils" "4.15.1" + "@typescript-eslint/scope-manager" "4.15.1" debug "^4.1.1" functional-red-black-tree "^1.0.1" lodash "^4.17.15" @@ -1788,96 +1775,60 @@ semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@4.14.0", "@typescript-eslint/experimental-utils@^4.0.1": - version "4.14.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.14.0.tgz#5aa7b006736634f588a69ee343ca959cd09988df" - integrity sha512-6i6eAoiPlXMKRbXzvoQD5Yn9L7k9ezzGRvzC/x1V3650rUk3c3AOjQyGYyF9BDxQQDK2ElmKOZRD0CbtdkMzQQ== +"@typescript-eslint/experimental-utils@4.15.1", "@typescript-eslint/experimental-utils@^4.0.1": + version "4.15.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.15.1.tgz#d744d1ac40570a84b447f7aa1b526368afd17eec" + integrity sha512-9LQRmOzBRI1iOdJorr4jEnQhadxK4c9R2aEAsm7WE/7dq8wkKD1suaV0S/JucTL8QlYUPU1y2yjqg+aGC0IQBQ== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/scope-manager" "4.14.0" - "@typescript-eslint/types" "4.14.0" - "@typescript-eslint/typescript-estree" "4.14.0" + "@typescript-eslint/scope-manager" "4.15.1" + "@typescript-eslint/types" "4.15.1" + "@typescript-eslint/typescript-estree" "4.15.1" eslint-scope "^5.0.0" eslint-utils "^2.0.0" -"@typescript-eslint/parser@^4.7.0": - version "4.7.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.7.0.tgz#44bdab0f788b478178368baa65d3365fdc63da1c" - integrity sha512-+meGV8bMP1sJHBI2AFq1GeTwofcGiur8LoIr6v+rEmD9knyCqDlrQcFHR0KDDfldHIFDU/enZ53fla6ReF4wRw== +"@typescript-eslint/parser@^4.15.1": + version "4.15.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.15.1.tgz#4c91a0602733db63507e1dbf13187d6c71a153c4" + integrity sha512-V8eXYxNJ9QmXi5ETDguB7O9diAXlIyS+e3xzLoP/oVE4WCAjssxLIa0mqCLsCGXulYJUfT+GV70Jv1vHsdKwtA== dependencies: - "@typescript-eslint/scope-manager" "4.7.0" - "@typescript-eslint/types" "4.7.0" - "@typescript-eslint/typescript-estree" "4.7.0" + "@typescript-eslint/scope-manager" "4.15.1" + "@typescript-eslint/types" "4.15.1" + "@typescript-eslint/typescript-estree" "4.15.1" debug "^4.1.1" -"@typescript-eslint/scope-manager@4.14.0": - version "4.14.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.14.0.tgz#55a4743095d684e1f7b7180c4bac2a0a3727f517" - integrity sha512-/J+LlRMdbPh4RdL4hfP1eCwHN5bAhFAGOTsvE6SxsrM/47XQiPSgF5MDgLyp/i9kbZV9Lx80DW0OpPkzL+uf8Q== +"@typescript-eslint/scope-manager@4.15.1": + version "4.15.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.15.1.tgz#f6511eb38def2a8a6be600c530c243bbb56ac135" + integrity sha512-ibQrTFcAm7yG4C1iwpIYK7vDnFg+fKaZVfvyOm3sNsGAerKfwPVFtYft5EbjzByDJ4dj1WD8/34REJfw/9wdVA== dependencies: - "@typescript-eslint/types" "4.14.0" - "@typescript-eslint/visitor-keys" "4.14.0" + "@typescript-eslint/types" "4.15.1" + "@typescript-eslint/visitor-keys" "4.15.1" -"@typescript-eslint/scope-manager@4.7.0": - version "4.7.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.7.0.tgz#2115526085fb72723ccdc1eeae75dec7126220ed" - integrity sha512-ILITvqwDJYbcDCROj6+Ob0oCKNg3SH46iWcNcTIT9B5aiVssoTYkhKjxOMNzR1F7WSJkik4zmuqve5MdnA0DyA== - dependencies: - "@typescript-eslint/types" "4.7.0" - "@typescript-eslint/visitor-keys" "4.7.0" - -"@typescript-eslint/types@4.14.0": - version "4.14.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.14.0.tgz#d8a8202d9b58831d6fd9cee2ba12f8a5a5dd44b6" - integrity sha512-VsQE4VvpldHrTFuVPY1ZnHn/Txw6cZGjL48e+iBxTi2ksa9DmebKjAeFmTVAYoSkTk7gjA7UqJ7pIsyifTsI4A== - -"@typescript-eslint/types@4.7.0": - version "4.7.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.7.0.tgz#5e95ef5c740f43d942542b35811f87b62fccca69" - integrity sha512-uLszFe0wExJc+I7q0Z/+BnP7wao/kzX0hB5vJn4LIgrfrMLgnB2UXoReV19lkJQS1a1mHWGGODSxnBx6JQC3Sg== - -"@typescript-eslint/typescript-estree@4.14.0": - version "4.14.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.14.0.tgz#4bcd67486e9acafc3d0c982b23a9ab8ac8911ed7" - integrity sha512-wRjZ5qLao+bvS2F7pX4qi2oLcOONIB+ru8RGBieDptq/SudYwshveORwCVU4/yMAd4GK7Fsf8Uq1tjV838erag== - dependencies: - "@typescript-eslint/types" "4.14.0" - "@typescript-eslint/visitor-keys" "4.14.0" - debug "^4.1.1" - globby "^11.0.1" - is-glob "^4.0.1" - lodash "^4.17.15" - semver "^7.3.2" - tsutils "^3.17.1" +"@typescript-eslint/types@4.15.1": + version "4.15.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.15.1.tgz#da702f544ef1afae4bc98da699eaecd49cf31c8c" + integrity sha512-iGsaUyWFyLz0mHfXhX4zO6P7O3sExQpBJ2dgXB0G5g/8PRVfBBsmQIc3r83ranEQTALLR3Vko/fnCIVqmH+mPw== -"@typescript-eslint/typescript-estree@4.7.0": - version "4.7.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.7.0.tgz#539531167f05ba20eb0b6785567076679e29d393" - integrity sha512-5XZRQznD1MfUmxu1t8/j2Af4OxbA7EFU2rbo0No7meb46eHgGkSieFdfV6omiC/DGIBhH9H9gXn7okBbVOm8jw== +"@typescript-eslint/typescript-estree@4.15.1": + version "4.15.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.15.1.tgz#fa9a9ff88b4a04d901ddbe5b248bc0a00cd610be" + integrity sha512-z8MN3CicTEumrWAEB2e2CcoZa3KP9+SMYLIA2aM49XW3cWIaiVSOAGq30ffR5XHxRirqE90fgLw3e6WmNx5uNw== dependencies: - "@typescript-eslint/types" "4.7.0" - "@typescript-eslint/visitor-keys" "4.7.0" + "@typescript-eslint/types" "4.15.1" + "@typescript-eslint/visitor-keys" "4.15.1" debug "^4.1.1" globby "^11.0.1" is-glob "^4.0.1" - lodash "^4.17.15" semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/visitor-keys@4.14.0": - version "4.14.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.14.0.tgz#b1090d9d2955b044b2ea2904a22496849acbdf54" - integrity sha512-MeHHzUyRI50DuiPgV9+LxcM52FCJFYjJiWHtXlbyC27b80mfOwKeiKI+MHOTEpcpfmoPFm/vvQS88bYIx6PZTA== - dependencies: - "@typescript-eslint/types" "4.14.0" - eslint-visitor-keys "^2.0.0" - -"@typescript-eslint/visitor-keys@4.7.0": - version "4.7.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.7.0.tgz#6783824f22acfc49e754970ed21b88ac03b80e6f" - integrity sha512-aDJDWuCRsf1lXOtignlfiPODkzSxxop7D0rZ91L6ZuMlcMCSh0YyK+gAfo5zN/ih6WxMwhoXgJWC3cWQdaKC+A== +"@typescript-eslint/visitor-keys@4.15.1": + version "4.15.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.15.1.tgz#c76abbf2a3be8a70ed760f0e5756bf62de5865dd" + integrity sha512-tYzaTP9plooRJY8eNlpAewTOqtWW/4ff/5wBjNVaJ0S0wC4Gpq/zDVRTJa5bq2v1pCNQ08xxMCndcvR+h7lMww== dependencies: - "@typescript-eslint/types" "4.7.0" + "@typescript-eslint/types" "4.15.1" eslint-visitor-keys "^2.0.0" "@yarnpkg/lockfile@^1.1.0": @@ -1920,7 +1871,7 @@ acorn-globals@^6.0.0: acorn "^7.1.1" acorn-walk "^7.1.1" -acorn-jsx@^5.2.0: +acorn-jsx@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== @@ -1976,7 +1927,7 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" -ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4: +ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -1987,9 +1938,9 @@ ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4: uri-js "^4.2.2" ajv@^7.0.2: - version "7.0.3" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-7.0.3.tgz#13ae747eff125cafb230ac504b2406cf371eece2" - integrity sha512-R50QRlXSxqXcQP5SvKUrw8VZeypvo12i2IX0EeR5PiZ7bEKeHWgzgo264LDadUsCU42lTJVhFikTqJwNeH34gQ== + version "7.1.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-7.1.0.tgz#f982ea7933dc7f1012eae9eec5a86687d805421b" + integrity sha512-svS9uILze/cXbH0z2myCK2Brqprx/+JJYK5pHicT/GQiBfzzhUVAIT6MwqJg8y4xV/zoGsUeuPuwtoiKSGE15g== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" @@ -2068,11 +2019,6 @@ anymatch@^3.0.3: normalize-path "^3.0.0" picomatch "^2.0.4" -app-root-path@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.2.1.tgz#d0df4a682ee408273583d43f6f79e9892624bc9a" - integrity sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA== - append-transform@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-1.0.0.tgz#046a52ae582a228bd72f58acfbe2967c678759ab" @@ -2192,12 +2138,14 @@ array-ify@^1.0.0: integrity sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4= array-includes@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.1.tgz#cdd67e6852bdf9c1215460786732255ed2459348" - integrity sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ== + version "3.1.2" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.2.tgz#a8db03e0b88c8c6aeddc49cb132f9bcab4ebf9c8" + integrity sha512-w2GspexNQpx+PutG3QpT437/BenZBj0M/MZGn5mzv/MofYqo0xmRHzn4lFsoDlWJ+THYsGJmFlW68WlDFx7VRw== dependencies: + call-bind "^1.0.0" define-properties "^1.1.3" - es-abstract "^1.17.0" + es-abstract "^1.18.0-next.1" + get-intrinsic "^1.0.1" is-string "^1.0.5" array-union@^1.0.2: @@ -2223,12 +2171,13 @@ array-unique@^0.3.2: integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= array.prototype.flat@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b" - integrity sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ== + 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== dependencies: + call-bind "^1.0.0" define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" + es-abstract "^1.18.0-next.1" arrify@^1.0.1: version "1.0.1" @@ -2264,11 +2213,6 @@ ast-types@^0.13.2: dependencies: tslib "^2.0.1" -astral-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" - integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== - astral-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" @@ -2299,7 +2243,7 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -available-typed-arrays@^1.0.0, available-typed-arrays@^1.0.2: +available-typed-arrays@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz#6b098ca9d8039079ee3f77f7b783c4480ba513f5" integrity sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ== @@ -2315,25 +2259,10 @@ aws-sdk-mock@^5.1.0: sinon "^9.0.1" traverse "^0.6.6" -aws-sdk@^2.596.0: - version "2.831.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.831.0.tgz#02607cc911a2136e5aabe624c1282e821830aef2" - integrity sha512-lrOjbGFpjk2xpESyUx2PGsTZgptCy5xycZazPeakNbFO19cOoxjHx3xyxOHsMCYb3pQwns35UvChQT60B4u6cw== - dependencies: - buffer "4.9.2" - events "1.1.1" - ieee754 "1.1.13" - jmespath "0.15.0" - querystring "0.2.0" - sax "1.2.1" - url "0.10.3" - uuid "3.3.2" - xml2js "0.4.19" - -aws-sdk@^2.637.0, aws-sdk@^2.830.0: - version "2.830.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.830.0.tgz#1d3631d573d18c48373046da7ad92855a7fd1636" - integrity sha512-vFatoWkdJmRzpymWbqsuwVsAJdhdAvU2JcM9jKRENTNKJw90ljnLyeP1eKCp4O3/4Lg43PVBwY/KUqPy4wL+OA== +aws-sdk@^2.637.0, aws-sdk@^2.845.0: + version "2.845.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.845.0.tgz#d1f932577dc1a1dcb34a35f81af97b221b741a42" + integrity sha512-lIOsMK6X6tyIXUB8rzYimklpFHMu96+cbWlDQkbRc5hymzSjPVY7L2LGP+PyCRjQHEnJUOk2EWswUIozhZy59A== dependencies: buffer "4.9.2" events "1.1.1" @@ -2398,9 +2327,9 @@ babel-plugin-jest-hoist@^26.6.2: "@types/babel__traverse" "^7.0.6" babel-preset-current-node-syntax@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.0.tgz#cf5feef29551253471cfa82fc8e0f5063df07a77" - integrity sha512-mGkvkpocWJes1CmMKtgGUwCeeq0pOhALyymozzDWYomHTbDLwueDYG6p4TK1YOeYHCzBzYPsWkgTto10JubI1Q== + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" + integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== dependencies: "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-syntax-bigint" "^7.8.3" @@ -2454,9 +2383,9 @@ bcrypt-pbkdf@^1.0.0: tweetnacl "^0.14.3" before-after-hook@^2.0.0, before-after-hook@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.1.0.tgz#b6c03487f44e24200dd30ca5e6a1979c5d2fb635" - integrity sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A== + version "2.1.1" + resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.1.1.tgz#99ae36992b5cfab4a83f6bee74ab27835f28f405" + integrity sha512-5ekuQOvO04MDj7kYZJaMab2S8SPjGJbotVNyv7QYFCOAwrGZs/YnoDNlh1U+m5hl7H2D/+n0taaAV/tfyd3KMA== bind-obj-methods@^2.0.0: version "2.0.0" @@ -2464,9 +2393,9 @@ bind-obj-methods@^2.0.0: integrity sha512-3/qRXczDi2Cdbz6jE+W3IflJOutRVica8frpBn14de1mBOkzDo+6tY33kNhvkw54Kn3PzRRD2VnGbGPcTAk4sw== bl@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.3.tgz#12d6287adc29080e22a705e5764b2a9522cdc489" - integrity sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg== + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== dependencies: buffer "^5.5.0" inherits "^2.0.4" @@ -2635,13 +2564,13 @@ caching-transform@^4.0.0: package-hash "^4.0.0" write-file-atomic "^3.0.0" -call-bind@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.0.tgz#24127054bb3f9bdcb4b1fb82418186072f77b8ce" - integrity sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w== +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== dependencies: function-bind "^1.1.1" - get-intrinsic "^1.0.0" + get-intrinsic "^1.0.2" call-me-maybe@^1.0.1: version "1.0.1" @@ -2898,14 +2827,14 @@ code-point-at@^1.0.0: resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= -codemaker@^1.15.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/codemaker/-/codemaker-1.15.0.tgz#3e2b319b6eb83be6094be166470158b186abdd04" - integrity sha512-2xXzYKUYrl79m1sertY+NL62T15Q5m1RLGuf5K8ZxX0gik0Ok3AmEhhEpSUaFBS48ocjHZ1rg5EgKK2A+7CY3g== +codemaker@^1.21.0: + version "1.21.0" + resolved "https://registry.yarnpkg.com/codemaker/-/codemaker-1.21.0.tgz#3dd1c236b6af3d8ac90f99e300e56e80c799ea76" + integrity sha512-YxTt3lWcR6PC/3fByU7FGjIoUDOcTs1KmqRJcK14xN9X7wxBOWO129WuSTm/4XfKlz/3iSo9CtRX/5HYkE1oCQ== dependencies: camelcase "^6.2.0" - decamelize "^4.0.0" - fs-extra "^9.0.1" + decamelize "^5.0.0" + fs-extra "^9.1.0" collect-v8-coverage@^1.0.0: version "1.0.1" @@ -2979,10 +2908,10 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= -commonmark@^0.29.2: - version "0.29.2" - resolved "https://registry.yarnpkg.com/commonmark/-/commonmark-0.29.2.tgz#e7bd5582400f2a45421f2f64eca19fc89cbd4e1b" - integrity sha512-spe43MvEIaPpHss1T7z4yQaFQfLGmMu+yvCwv6xqhELIwkG/ZGgDpxOPzKxnuYzYT2c+aziCCc8m2rBVLA7jUA== +commonmark@^0.29.3: + version "0.29.3" + resolved "https://registry.yarnpkg.com/commonmark/-/commonmark-0.29.3.tgz#bb1d5733bfe3ea213b412f33f16439cc12999c2c" + integrity sha512-fvt/NdOFKaL2gyhltSy6BC4LxbbxbnPxBMl923ittqO/JBM0wQHaoYZliE4tp26cRxX/ZZtRsJlZzQrVdUkXAA== dependencies: entities "~2.0" mdurl "~1.0.1" @@ -3051,16 +2980,16 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= constructs@^3.2.0: - version "3.2.30" - resolved "https://registry.yarnpkg.com/constructs/-/constructs-3.2.30.tgz#950a1e38d7193791fea55f847f87013959748475" - integrity sha512-tzWUxXc9UjPbw1+c0s6gFL0ae4gPgsKck59xfkjOnyezPNcG2myB/xh9wGD51kbn+GInW0vMgW0QWOn0WhKa4g== + version "3.3.21" + resolved "https://registry.yarnpkg.com/constructs/-/constructs-3.3.21.tgz#1290fbebe8716e729a6ac2a47118b58b8f7c4461" + integrity sha512-FsdsSAJlmlWSiWWuKTTP/a0SpEtRMM2w0P5lsJxvP4/42AOJOSmqckDyrr9WkESiaX94WnnK9khoVWGlBzkvNA== 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= -conventional-changelog-angular@^5.0.11, conventional-changelog-angular@^5.0.12, conventional-changelog-angular@^5.0.3: +conventional-changelog-angular@^5.0.12, conventional-changelog-angular@^5.0.3: version "5.0.12" resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.12.tgz#c979b8b921cbfe26402eb3da5bbfda02d865a2b9" integrity sha512-5GLsbnkR/7A89RyHLvvoExbiGbd9xKdKqDTrArnPbOqBqG/2wIosu0fHwpeIRI8Tl94MhVNBXcLJZl92ZQ5USw== @@ -3068,7 +2997,7 @@ conventional-changelog-angular@^5.0.11, conventional-changelog-angular@^5.0.12, compare-func "^2.0.0" q "^1.5.1" -conventional-changelog-atom@^2.0.7, conventional-changelog-atom@^2.0.8: +conventional-changelog-atom@^2.0.8: version "2.0.8" resolved "https://registry.yarnpkg.com/conventional-changelog-atom/-/conventional-changelog-atom-2.0.8.tgz#a759ec61c22d1c1196925fca88fe3ae89fd7d8de" integrity sha512-xo6v46icsFTK3bb7dY/8m2qvc8sZemRgdqLb/bjpBsH2UyOS8rKNTgcb5025Hri6IpANPApbXMg15QLb1LJpBw== @@ -3086,7 +3015,7 @@ conventional-changelog-cli@^2.1.1: meow "^8.0.0" tempfile "^3.0.0" -conventional-changelog-codemirror@^2.0.7, conventional-changelog-codemirror@^2.0.8: +conventional-changelog-codemirror@^2.0.8: version "2.0.8" resolved "https://registry.yarnpkg.com/conventional-changelog-codemirror/-/conventional-changelog-codemirror-2.0.8.tgz#398e9530f08ce34ec4640af98eeaf3022eb1f7dc" integrity sha512-z5DAsn3uj1Vfp7po3gpt2Boc+Bdwmw2++ZHa5Ak9k0UKsYAO5mH1UBTN0qSCuJZREIhX6WU4E1p3IW2oRCNzQw== @@ -3098,16 +3027,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.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.4.0.tgz#8d96687141c9bbd725a89b95c04966d364194cd4" - integrity sha512-ybvx76jTh08tpaYrYn/yd0uJNLt5yMrb1BphDe4WBredMlvPisvMghfpnJb6RmRNcqXeuhR6LfGZGewbkRm9yA== - dependencies: - compare-func "^2.0.0" - lodash "^4.17.15" - q "^1.5.1" - -conventional-changelog-conventionalcommits@^4.4.0, 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== @@ -3135,17 +3055,17 @@ conventional-changelog-core@^3.1.6: read-pkg-up "^3.0.0" through2 "^3.0.0" -conventional-changelog-core@^4.2.0, conventional-changelog-core@^4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-4.2.1.tgz#f811ad98ab2ff080becafc61407509420c9b447d" - integrity sha512-8cH8/DEoD3e5Q6aeogdR5oaaKs0+mG6+f+Om0ZYt3PNv7Zo0sQhu4bMDRsqAF+UTekTAtP1W/C41jH/fkm8Jtw== +conventional-changelog-core@^4.2.1: + version "4.2.2" + resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-4.2.2.tgz#f0897df6d53b5d63dec36b9442bd45354f8b3ce5" + integrity sha512-7pDpRUiobQDNkwHyJG7k9f6maPo9tfPzkSWbRq97GGiZqisElhnvUZSvyQH20ogfOjntB5aadvv6NNcKL1sReg== dependencies: add-stream "^1.0.0" conventional-changelog-writer "^4.0.18" conventional-commits-parser "^3.2.0" dateformat "^3.0.0" get-pkg-repo "^1.0.0" - git-raw-commits "2.0.0" + git-raw-commits "^2.0.8" git-remote-origin-url "^2.0.0" git-semver-tags "^4.1.1" lodash "^4.17.15" @@ -3156,35 +3076,35 @@ conventional-changelog-core@^4.2.0, conventional-changelog-core@^4.2.1: shelljs "^0.8.3" through2 "^4.0.0" -conventional-changelog-ember@^2.0.8, conventional-changelog-ember@^2.0.9: +conventional-changelog-ember@^2.0.9: version "2.0.9" resolved "https://registry.yarnpkg.com/conventional-changelog-ember/-/conventional-changelog-ember-2.0.9.tgz#619b37ec708be9e74a220f4dcf79212ae1c92962" integrity sha512-ulzIReoZEvZCBDhcNYfDIsLTHzYHc7awh+eI44ZtV5cx6LVxLlVtEmcO+2/kGIHGtw+qVabJYjdI5cJOQgXh1A== dependencies: q "^1.5.1" -conventional-changelog-eslint@^3.0.8, conventional-changelog-eslint@^3.0.9: +conventional-changelog-eslint@^3.0.9: version "3.0.9" resolved "https://registry.yarnpkg.com/conventional-changelog-eslint/-/conventional-changelog-eslint-3.0.9.tgz#689bd0a470e02f7baafe21a495880deea18b7cdb" integrity sha512-6NpUCMgU8qmWmyAMSZO5NrRd7rTgErjrm4VASam2u5jrZS0n38V7Y9CzTtLT2qwz5xEChDR4BduoWIr8TfwvXA== dependencies: q "^1.5.1" -conventional-changelog-express@^2.0.5, conventional-changelog-express@^2.0.6: +conventional-changelog-express@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/conventional-changelog-express/-/conventional-changelog-express-2.0.6.tgz#420c9d92a347b72a91544750bffa9387665a6ee8" integrity sha512-SDez2f3iVJw6V563O3pRtNwXtQaSmEfTCaTBPCqn0oG0mfkq0rX4hHBq5P7De2MncoRixrALj3u3oQsNK+Q0pQ== dependencies: q "^1.5.1" -conventional-changelog-jquery@^3.0.10, conventional-changelog-jquery@^3.0.11: +conventional-changelog-jquery@^3.0.11: version "3.0.11" resolved "https://registry.yarnpkg.com/conventional-changelog-jquery/-/conventional-changelog-jquery-3.0.11.tgz#d142207400f51c9e5bb588596598e24bba8994bf" integrity sha512-x8AWz5/Td55F7+o/9LQ6cQIPwrCjfJQ5Zmfqi8thwUEKHstEn4kTIofXub7plf1xvFA2TqhZlq7fy5OmV6BOMw== dependencies: q "^1.5.1" -conventional-changelog-jshint@^2.0.8, conventional-changelog-jshint@^2.0.9: +conventional-changelog-jshint@^2.0.9: version "2.0.9" resolved "https://registry.yarnpkg.com/conventional-changelog-jshint/-/conventional-changelog-jshint-2.0.9.tgz#f2d7f23e6acd4927a238555d92c09b50fe3852ff" integrity sha512-wMLdaIzq6TNnMHMy31hql02OEQ8nCQfExw1SE0hYL5KvU+JCTuPaDO+7JiogGT2gJAxiUGATdtYYfh+nT+6riA== @@ -3198,9 +3118,9 @@ conventional-changelog-preset-loader@^2.1.1, conventional-changelog-preset-loade integrity sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g== conventional-changelog-writer@^4.0.18, conventional-changelog-writer@^4.0.6: - version "4.0.18" - resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-4.0.18.tgz#10b73baa59c7befc69b360562f8b9cd19e63daf8" - integrity sha512-mAQDCKyB9HsE8Ko5cCM1Jn1AWxXPYV0v8dFPabZRkvsiWUul2YyAqbIaoMKF88Zf2ffnOPSvKhboLf3fnjo5/A== + version "4.1.0" + resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-4.1.0.tgz#1ca7880b75aa28695ad33312a1f2366f4b12659f" + integrity sha512-WwKcUp7WyXYGQmkLsX4QmU42AZ1lqlvRW9mqoyiQzdD+rJWbTepdWoKJuwXTS+yq79XKnQNa93/roViPQrAQgw== dependencies: compare-func "^2.0.0" conventional-commits-filter "^2.0.7" @@ -3213,24 +3133,7 @@ conventional-changelog-writer@^4.0.18, conventional-changelog-writer@^4.0.6: split "^1.0.0" through2 "^4.0.0" -conventional-changelog@3.1.23: - version "3.1.23" - resolved "https://registry.yarnpkg.com/conventional-changelog/-/conventional-changelog-3.1.23.tgz#d696408021b579a3814aba79b38729ed86478aea" - integrity sha512-sScUu2NHusjRC1dPc5p8/b3kT78OYr95/Bx7Vl8CPB8tF2mG1xei5iylDTRjONV5hTlzt+Cn/tBWrKdd299b7A== - dependencies: - conventional-changelog-angular "^5.0.11" - conventional-changelog-atom "^2.0.7" - conventional-changelog-codemirror "^2.0.7" - conventional-changelog-conventionalcommits "^4.4.0" - conventional-changelog-core "^4.2.0" - conventional-changelog-ember "^2.0.8" - conventional-changelog-eslint "^3.0.8" - conventional-changelog-express "^2.0.5" - conventional-changelog-jquery "^3.0.10" - conventional-changelog-jshint "^2.0.8" - conventional-changelog-preset-loader "^2.3.4" - -conventional-changelog@^3.1.24: +conventional-changelog@3.1.24, conventional-changelog@^3.1.24: version "3.1.24" resolved "https://registry.yarnpkg.com/conventional-changelog/-/conventional-changelog-3.1.24.tgz#ebd180b0fd1b2e1f0095c4b04fd088698348a464" integrity sha512-ed6k8PO00UVvhExYohroVPXcOJ/K1N0/drJHx/faTH37OIZthlecuLIRX/T6uOp682CAoVoFpu+sSEaeuH6Asg== @@ -3247,7 +3150,7 @@ conventional-changelog@^3.1.24: conventional-changelog-jshint "^2.0.9" conventional-changelog-preset-loader "^2.3.4" -conventional-commits-filter@^2.0.2, conventional-commits-filter@^2.0.6, conventional-commits-filter@^2.0.7: +conventional-commits-filter@^2.0.2, conventional-commits-filter@^2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz#f8d9b4f182fce00c9af7139da49365b136c8a0b3" integrity sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA== @@ -3255,7 +3158,7 @@ conventional-commits-filter@^2.0.2, conventional-commits-filter@^2.0.6, conventi lodash.ismatch "^4.4.0" modify-values "^1.0.0" -conventional-commits-parser@^3.0.3, conventional-commits-parser@^3.1.0, conventional-commits-parser@^3.2.0: +conventional-commits-parser@^3.0.3, conventional-commits-parser@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-3.2.0.tgz#9e261b139ca4b7b29bcebbc54460da36894004ca" integrity sha512-XmJiXPxsF0JhAKyfA2Nn+rZwYKJ60nanlbSWwwkGwLQFbugsc0gv1rzc7VbbUWAzJfR1qR87/pNgv9NgmxtBMQ== @@ -3268,18 +3171,18 @@ conventional-commits-parser@^3.0.3, conventional-commits-parser@^3.1.0, conventi through2 "^4.0.0" trim-off-newlines "^1.0.0" -conventional-recommended-bump@6.0.10: - version "6.0.10" - resolved "https://registry.yarnpkg.com/conventional-recommended-bump/-/conventional-recommended-bump-6.0.10.tgz#ac2fb3e31bad2aeda80086b345bf0c52edd1d1b3" - integrity sha512-2ibrqAFMN3ZA369JgVoSbajdD/BHN6zjY7DZFKTHzyzuQejDUCjQ85S5KHxCRxNwsbDJhTPD5hOKcis/jQhRgg== +conventional-recommended-bump@6.0.11: + version "6.0.11" + resolved "https://registry.yarnpkg.com/conventional-recommended-bump/-/conventional-recommended-bump-6.0.11.tgz#fcc39acb51d1946b63fc478737d1e52712f36356" + integrity sha512-FciYBMwzwwBZ1K4NS8c57rsOfSc51e1V6UVSNIosrjH+A6xXkyiA4ELwoWyRKdMhJ+m3O6ru9ZJ7F2QFjjYJdQ== dependencies: concat-stream "^2.0.0" conventional-changelog-preset-loader "^2.3.4" - conventional-commits-filter "^2.0.6" - conventional-commits-parser "^3.1.0" + conventional-commits-filter "^2.0.7" + conventional-commits-parser "^3.2.0" git-raw-commits "2.0.0" - git-semver-tags "^4.1.0" - meow "^7.0.0" + git-semver-tags "^4.1.1" + meow "^8.0.0" q "^1.5.1" conventional-recommended-bump@^5.0.0: @@ -3366,9 +3269,9 @@ crc-32@^1.2.0: printj "~1.1.0" crc32-stream@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-4.0.1.tgz#0f047d74041737f8a55e86837a1b826bd8ab0067" - integrity sha512-FN5V+weeO/8JaXsamelVYO1PHyeCsuL3HcG4cqsj0ceARcocxalaShCsohZMSAF+db7UYFwBy1rARK/0oFItUw== + version "4.0.2" + resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-4.0.2.tgz#c922ad22b38395abe9d3870f02fa8134ed709007" + integrity sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w== dependencies: crc-32 "^1.2.0" readable-stream "^3.4.0" @@ -3447,6 +3350,11 @@ dargs@^4.0.1: dependencies: number-is-nan "^1.0.0" +dargs@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" + integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== + dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -3491,9 +3399,9 @@ debug@3.1.0: ms "2.0.0" debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: - version "4.2.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" - integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== dependencies: ms "2.1.2" @@ -3505,9 +3413,9 @@ debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: ms "2.0.0" debug@^3.1.0: - version "3.2.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + 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" @@ -3529,11 +3437,6 @@ decamelize@^1.1.0, decamelize@^1.1.2, decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= -decamelize@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" - integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== - decamelize@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-5.0.0.tgz#88358157b010ef133febfd27c18994bd80c6215b" @@ -3554,20 +3457,21 @@ dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= -deep-equal@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.0.4.tgz#6b0b407a074666033169df3acaf128e1c6f3eab6" - integrity sha512-BUfaXrVoCfgkOQY/b09QdO9L3XNoF2XH0A3aY9IQwQL/ZjLOe8FQgCNVl1wiolhsFo8kFdO9zdPViCPbmaJA5w== +deep-equal@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.0.5.tgz#55cd2fe326d83f9cbf7261ef0e060b3f724c5cb9" + integrity sha512-nPiRgmbAtm1a3JsnLCf6/SLfXcjyN5v8L1TXzdCmHrXJ4hx+gW/w1YCcn7z8gJtSiDArZCgYtbao3QqLm/N1Sw== dependencies: - es-abstract "^1.18.0-next.1" - es-get-iterator "^1.1.0" + call-bind "^1.0.0" + es-get-iterator "^1.1.1" + get-intrinsic "^1.0.1" is-arguments "^1.0.4" is-date-object "^1.0.2" is-regex "^1.1.1" isarray "^2.0.5" - object-is "^1.1.3" + object-is "^1.1.4" object-keys "^1.1.1" - object.assign "^4.1.1" + object.assign "^4.1.2" regexp.prototype.flags "^1.3.0" side-channel "^1.0.3" which-boxed-primitive "^1.0.1" @@ -3648,10 +3552,10 @@ degenerator@^2.2.0: escodegen "^1.8.1" esprima "^4.0.0" -delay@4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/delay/-/delay-4.4.0.tgz#71abc745f3ce043fe7f450491236541edec4ad0c" - integrity sha512-txgOrJu3OdtOfTiEOT2e76dJVfG/1dz2NZ4F0Pyt4UGZJryssMRp5vdM5wQoLwSOBNdrJv3F9PAhp/heqd7vrA== +delay@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d" + integrity sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw== delayed-stream@~1.0.0: version "1.0.0" @@ -3778,16 +3682,6 @@ dot-prop@^5.1.0: dependencies: is-obj "^2.0.0" -dotenv-json@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/dotenv-json/-/dotenv-json-1.0.0.tgz#fc7f672aafea04bed33818733b9f94662332815c" - 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== - dotgitignore@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/dotgitignore/-/dotgitignore-2.1.0.tgz#a4b15a4e4ef3cf383598aaf1dfa4a04bcc089b7b" @@ -3878,9 +3772,9 @@ env-paths@^2.2.0: integrity sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA== envinfo@^7.3.1: - version "7.7.3" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.7.3.tgz#4b2d8622e3e7366afb8091b23ed95569ea0208cc" - integrity sha512-46+j5QxbPWza0PB1i15nZx0xQ4I/EfQxg9J8Had3b408SV63nEtor2e+oiY63amTo9KTuh2a3XLObNwduxYwwA== + version "7.7.4" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.7.4.tgz#c6311cdd38a0e86808c1c9343f667e4267c4a320" + integrity sha512-TQXTYFVVwwluWSFis6K2XKxgrD22jEv0FTuLCQI+OjH7rn93+iY0fSSFM5lrSxFY+H1+B0/cvvlamr3UsBivdQ== err-code@^1.0.0: version "1.1.2" @@ -3894,52 +3788,37 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.4, es-abstract@^1.17.5: - version "1.17.7" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.7.tgz#a4de61b2f66989fc7421676c1cb9787573ace54c" - integrity sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g== +es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2: + version "1.18.0-next.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.2.tgz#088101a55f0541f595e7e057199e27ddc8f3a5c2" + integrity sha512-Ih4ZMFHEtZupnUh6497zEL4y2+w8+1ljnCyaTa+adcoafI1GOvMwFlDjBLfWR7y9VLfrjRJe9ocuHY1PSR9jjw== dependencies: + call-bind "^1.0.2" es-to-primitive "^1.2.1" function-bind "^1.1.1" + get-intrinsic "^1.0.2" has "^1.0.3" has-symbols "^1.0.1" is-callable "^1.2.2" + is-negative-zero "^2.0.1" is-regex "^1.1.1" - object-inspect "^1.8.0" + object-inspect "^1.9.0" object-keys "^1.1.1" - object.assign "^4.1.1" - string.prototype.trimend "^1.0.1" - string.prototype.trimstart "^1.0.1" + object.assign "^4.1.2" + string.prototype.trimend "^1.0.3" + string.prototype.trimstart "^1.0.3" -es-abstract@^1.18.0-next.0, es-abstract@^1.18.0-next.1: - version "1.18.0-next.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.1.tgz#6e3a0a4bda717e5023ab3b8e90bec36108d22c68" - integrity sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA== - dependencies: - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" - is-callable "^1.2.2" - is-negative-zero "^2.0.0" - is-regex "^1.1.1" - object-inspect "^1.8.0" - object-keys "^1.1.1" - object.assign "^4.1.1" - string.prototype.trimend "^1.0.1" - string.prototype.trimstart "^1.0.1" - -es-get-iterator@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.1.tgz#b93ddd867af16d5118e00881396533c1c6647ad9" - integrity sha512-qorBw8Y7B15DVLaJWy6WdEV/ZkieBcu6QCq/xzWzGOKJqgG1j754vXRfZ3NY7HSShneqU43mPB4OkQBTkvHhFw== +es-get-iterator@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.2.tgz#9234c54aba713486d7ebde0220864af5e2b283f7" + integrity sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ== dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.1" + call-bind "^1.0.2" + get-intrinsic "^1.1.0" has-symbols "^1.0.1" - is-arguments "^1.0.4" - is-map "^2.0.1" - is-set "^2.0.1" + is-arguments "^1.1.0" + is-map "^2.0.2" + is-set "^2.0.2" is-string "^1.0.5" isarray "^2.0.5" @@ -3974,10 +3853,10 @@ es6-promisify@^5.0.0: dependencies: es6-promise "^4.0.3" -esbuild@^0.8.34: - version "0.8.34" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.8.34.tgz#16b4ac58f74c821d2c5a8c2f0585ca96a38ab4e6" - integrity sha512-tnr0V1ooakYr1aRLXQLzCn2GVG1kBTW3FWpRyC+NgrR3ntsouVpJOlTOV0BS4YLATx3/c+x3h/uBq9lWJlUAtQ== +esbuild@^0.8.46: + version "0.8.46" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.8.46.tgz#8fc7230ce3019b12e2553399f0c03875a729c26b" + integrity sha512-xck9sXNCNmjDHCCfxTCyhKTiFuEBweh+IDAhMLOJI990v1Fzii6MyIkT1LbkvjgoVgPX2SK1kpi5eZVGNrl8yg== escalade@^3.1.1: version "3.1.1" @@ -4011,11 +3890,6 @@ escodegen@^1.14.1, escodegen@^1.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" - integrity sha512-Z9B+VR+JIXRxz21udPTL9HpFMyoMUEeX1G251EQ6e05WD9aPVtVBn09XUmZ259wCMlCDmYDSZG62Hhm+ZTJcUg== - eslint-import-resolver-node@^0.3.4: version "0.3.4" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717" @@ -4024,10 +3898,10 @@ eslint-import-resolver-node@^0.3.4: debug "^2.6.9" resolve "^1.13.1" -eslint-import-resolver-typescript@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.3.0.tgz#0870988098bc6c6419c87705e6b42bee89425445" - integrity sha512-MHSXvmj5e0SGOOBhBbt7C+fWj1bJbtSYFAD85Xeg8nvUtuooTod2HQb8bfhE9f5QyyNxEfgzqOYFCvmdDIcCuw== +eslint-import-resolver-typescript@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.4.0.tgz#ec1e7063ebe807f0362a7320543aaed6fe1100e1" + integrity sha512-useJKURidCcldRLCNKWemr1fFQL1SzB3G4a0li6lFGvlc5xGe1hY343bvG07cbpCzPuM/lK19FIJB3XGFSkplA== dependencies: debug "^4.1.1" glob "^7.1.6" @@ -4043,14 +3917,6 @@ eslint-module-utils@^2.6.0: debug "^2.6.9" pkg-dir "^2.0.0" -eslint-plugin-es@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz#75a7cdfdccddc0589934aeeb384175f221c57893" - integrity sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ== - dependencies: - 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" @@ -4070,40 +3936,18 @@ eslint-plugin-import@^2.22.1: resolve "^1.17.0" tsconfig-paths "^3.9.0" -eslint-plugin-jest@^24.1.3: - version "24.1.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-24.1.3.tgz#fa3db864f06c5623ff43485ca6c0e8fc5fe8ba0c" - integrity sha512-dNGGjzuEzCE3d5EPZQ/QGtmlMotqnYWD/QpCZ1UuZlrMAdhG5rldh0N0haCvhGnUkSeuORS5VNROwF9Hrgn3Lg== +eslint-plugin-jest@^24.1.5: + version "24.1.5" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-24.1.5.tgz#1e866a9f0deac587d0a3d5d7cefe99815a580de2" + integrity sha512-FIP3lwC8EzEG+rOs1y96cOJmMVpdFNreoDJv29B5vIupVssRi8zrSY3QadogT0K3h1Y8TMxJ6ZSAzYUmFCp2hg== dependencies: "@typescript-eslint/experimental-utils" "^4.0.1" -eslint-plugin-node@^11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz#c95544416ee4ada26740a30474eefc5402dc671d" - integrity sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g== - dependencies: - eslint-plugin-es "^3.0.0" - eslint-utils "^2.0.0" - ignore "^5.1.1" - minimatch "^3.0.4" - resolve "^1.10.1" - semver "^6.1.0" - -eslint-plugin-promise@^4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz#845fd8b2260ad8f82564c1222fce44ad71d9418a" - integrity sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw== - eslint-plugin-rulesdir@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/eslint-plugin-rulesdir/-/eslint-plugin-rulesdir-0.1.0.tgz#ad144d7e98464fda82963eff3fab331aecb2bf08" integrity sha1-rRRNfphGT9qClj7/P6szGuyyvwg= -eslint-plugin-standard@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-4.1.0.tgz#0c3bf3a67e853f8bbbc580fb4945fbf16f41b7c5" - integrity sha512-ZL7+QRixjTR6/528YNGyDotyffm5OQst/sGxKDwGb9Uqs4In5Egi4+jbobhqJoyoCM6/7v/1A5fhQ7ScMtDjaQ== - eslint-scope@^5.0.0, eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" @@ -4129,13 +3973,13 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== -eslint@^7.13.0: - version "7.13.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.13.0.tgz#7f180126c0dcdef327bfb54b211d7802decc08da" - integrity sha512-uCORMuOO8tUzJmsdRtrvcGq5qposf7Rw0LwkTJkoDbOycVQtQjmnhZSuLQnozLE4TmAzlMVV45eCHmQ1OpDKUQ== +eslint@^7.20.0: + version "7.20.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.20.0.tgz#db07c4ca4eda2e2316e7aa57ac7fc91ec550bdc7" + integrity sha512-qGi0CTcOGP2OtCQBgWZlQjcTuP0XkIpYFj25XtRTQSHC+umNnp7UMshr2G8SLsRFYDdAPFeHOsiteadmMH02Yw== dependencies: - "@babel/code-frame" "^7.0.0" - "@eslint/eslintrc" "^0.2.1" + "@babel/code-frame" "7.12.11" + "@eslint/eslintrc" "^0.3.0" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" @@ -4145,10 +3989,10 @@ eslint@^7.13.0: eslint-scope "^5.1.1" eslint-utils "^2.1.0" eslint-visitor-keys "^2.0.0" - espree "^7.3.0" - esquery "^1.2.0" + espree "^7.3.1" + esquery "^1.4.0" esutils "^2.0.2" - file-entry-cache "^5.0.1" + file-entry-cache "^6.0.0" functional-red-black-tree "^1.0.1" glob-parent "^5.0.0" globals "^12.1.0" @@ -4159,7 +4003,7 @@ eslint@^7.13.0: js-yaml "^3.13.1" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" - lodash "^4.17.19" + lodash "^4.17.20" minimatch "^3.0.4" natural-compare "^1.4.0" optionator "^0.9.1" @@ -4168,7 +4012,7 @@ eslint@^7.13.0: semver "^7.2.1" strip-ansi "^6.0.0" strip-json-comments "^3.1.0" - table "^5.2.3" + table "^6.0.4" text-table "^0.2.0" v8-compile-cache "^2.0.3" @@ -4177,13 +4021,13 @@ esm@^3.2.5: resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== -espree@^7.3.0: - version "7.3.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.0.tgz#dc30437cf67947cf576121ebd780f15eeac72348" - integrity sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw== +espree@^7.3.0, espree@^7.3.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" + integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== dependencies: acorn "^7.4.0" - acorn-jsx "^5.2.0" + acorn-jsx "^5.3.1" eslint-visitor-keys "^1.3.0" esprima@^4.0.0, esprima@^4.0.1: @@ -4191,10 +4035,10 @@ esprima@^4.0.0, esprima@^4.0.1: resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" - integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== +esquery@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== dependencies: estraverse "^5.1.0" @@ -4356,10 +4200,10 @@ extsprintf@^1.2.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= -fast-check@^2.11.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/fast-check/-/fast-check-2.11.0.tgz#a21bdbdcab27812fbf93612f3c84483883115ca2" - integrity sha512-galBVrbyjdHOW+WOCp/NFP3J6t6Pc0uajz0oJaUAFRXLHXt6lcUeD1bcBFqUWV1aeK9QJgeRpIYf4e+PHeASUQ== +fast-check@^2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/fast-check/-/fast-check-2.13.0.tgz#92a50a6a39b58760d4b0b52b12f98f28a9f020f6" + integrity sha512-IOfzKm/SCA+jpUEgAfqAuxHYPmgtmpnnwljQmYPRGrqYczcTKApXKHza/SNxFxYkecWfZilYa0DJdBvqz1bcSw== dependencies: pure-rand "^4.1.1" @@ -4386,9 +4230,9 @@ fast-glob@^2.2.6: micromatch "^3.1.10" fast-glob@^3.1.1: - version "3.2.4" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3" - integrity sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ== + version "3.2.5" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661" + integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" @@ -4420,9 +4264,9 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= fastq@^1.6.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.9.0.tgz#e16a72f338eaca48e91b5c23593bcc2ef66b7947" - integrity sha512-i7FVWL8HhVY+CTkwFxkN2mk3h+787ixS5S63eb78diVRc1MCssarHq3W5cj0av7YDSwmaV928RNag+U1etRQ7w== + version "1.10.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.10.1.tgz#8b8f2ac8bf3632d67afcd65dac248d5fdc45385e" + integrity sha512-AWuv6Ery3pM+dY7LYS8YIaCiQvUaos9OB1RyNgaOWnaX+Tik7Onvcsf8x8c+YtDeT0maYLniBip2hox5KtEXXA== dependencies: reusify "^1.0.4" @@ -4452,12 +4296,12 @@ figures@^3.1.0: dependencies: escape-string-regexp "^1.0.5" -file-entry-cache@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" - integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== +file-entry-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.0.tgz#7921a89c391c6d93efec2169ac6bf300c527ea0a" + integrity sha512-fqoO76jZ3ZnYrXLDRxBR1YvOvc0k844kcOg40bgsPrE25LAb/PDqTY+ho64Xh2c8ZXgIKldchCFHczG2UVRcWA== dependencies: - flat-cache "^2.0.1" + flat-cache "^3.0.4" file-uri-to-path@2: version "2.0.0" @@ -4489,6 +4333,11 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +filter-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" + integrity sha1-mzERErxsYSehbgFsbF1/GeCAXFs= + find-cache-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" @@ -4537,20 +4386,32 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" -flat-cache@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" - integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== dependencies: - flatted "^2.0.0" - rimraf "2.6.3" - write "1.0.3" + flatted "^3.1.0" + rimraf "^3.0.2" -flatted@^2.0.0, flatted@^2.0.1: +flatted@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== +flatted@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469" + integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA== + flush-write-stream@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" @@ -4559,16 +4420,11 @@ flush-write-stream@^1.0.0: inherits "^2.0.3" readable-stream "^2.3.6" -follow-redirects@^1.10.0: +follow-redirects@^1.10.0, follow-redirects@^1.11.0: version "1.13.2" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.2.tgz#dd73c8effc12728ba5cf4259d760ea5fb83e3147" integrity sha512-6mPTgLxYm3r6Bkkg0vNM0HTjfGrOEtsfbhagQvbxDEsEkpNhw582upBaoRZylzen6krEmxXJgt9Ju6HiI4O7BA== -follow-redirects@^1.11.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db" - integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA== - for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -4655,7 +4511,7 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@^9.0.1, fs-extra@^9.1.0: +fs-extra@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== @@ -4688,9 +4544,9 @@ fs.realpath@^1.0.0: integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= fsevents@^2.1.2: - version "2.2.1" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.2.1.tgz#1fb02ded2036a8ac288d507a65962bd87b97628d" - integrity sha512-bTLYHSeC0UH/EFXS9KqWnXuOl/wHK5Z/d+ghd5AsFMYN7wIGkUCOJyzy88+wJKkZPGON8u4Z9f6U4FdgURE9qA== + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== ftp@^0.3.10: version "0.3.10" @@ -4744,10 +4600,10 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.0, get-intrinsic@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.0.1.tgz#94a9768fcbdd0595a1c9273aacf4c89d075631be" - integrity sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg== +get-intrinsic@^1.0.1, get-intrinsic@^1.0.2, get-intrinsic@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== dependencies: function-bind "^1.1.1" has "^1.0.3" @@ -4833,6 +4689,17 @@ git-raw-commits@2.0.0: split2 "^2.0.0" through2 "^2.0.0" +git-raw-commits@^2.0.8: + version "2.0.10" + resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-2.0.10.tgz#e2255ed9563b1c9c3ea6bd05806410290297bbc1" + integrity sha512-sHhX5lsbG9SOO6yXdlwgEMQ/ljIn7qMpAbJZCGfXX2fq5T8M5SrDnpYk9/4HswTildcIqatsWa91vty6VhWSaQ== + dependencies: + dargs "^7.0.0" + lodash "^4.17.15" + meow "^8.0.0" + split2 "^3.0.0" + through2 "^4.0.0" + git-remote-origin-url@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz#5282659dae2107145a11126112ad3216ec5fa65f" @@ -4849,7 +4716,7 @@ git-semver-tags@^2.0.3: meow "^4.0.0" semver "^6.0.0" -git-semver-tags@^4.0.0, git-semver-tags@^4.1.0, git-semver-tags@^4.1.1: +git-semver-tags@^4.0.0, git-semver-tags@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/git-semver-tags/-/git-semver-tags-4.1.1.tgz#63191bcd809b0ec3e151ba4751c16c444e5b5780" integrity sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA== @@ -4866,9 +4733,9 @@ git-up@^4.0.0: parse-url "^5.0.0" git-url-parse@^11.1.2: - version "11.4.0" - resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-11.4.0.tgz#f2bb1f2b00f05552540e95a62e31399a639a6aa6" - integrity sha512-KlIa5jvMYLjXMQXkqpFzobsyD/V2K5DRHl5OAf+6oDFPlPLxrGDVQlIdI63c4/Kt6kai4kALENSALlzTGST3GQ== + version "11.4.4" + resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-11.4.4.tgz#5d747debc2469c17bc385719f7d0427802d83d77" + integrity sha512-Y4o9o7vQngQDIU9IjyCmRJBin5iYjI5u9ZITnddRZpD7dcCFQj2sL2XuMNbLRE4b4B/4ENPsp2Q8P44fjAZ0Pw== dependencies: git-up "^4.0.0" @@ -4934,9 +4801,9 @@ globals@^12.1.0: type-fest "^0.8.1" globby@^11.0.1: - version "11.0.1" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357" - integrity sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ== + version "11.0.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.2.tgz#1af538b766a3b540ebfb58a32b2e2d5897321d83" + integrity sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og== dependencies: array-union "^2.1.0" dir-glob "^3.0.1" @@ -4959,10 +4826,10 @@ globby@^9.2.0: pify "^4.0.1" slash "^2.0.0" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4: - version "4.2.4" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" - integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4, graceful-fs@^4.2.6: + version "4.2.6" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" + integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== growly@^1.3.0: version "1.3.0" @@ -4970,9 +4837,9 @@ growly@^1.3.0: integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= handlebars@^4.7.6: - version "4.7.6" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.6.tgz#d4c05c1baf90e9945f77aa68a7a219aa4a7df74e" - integrity sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA== + version "4.7.7" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" + integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== dependencies: minimist "^1.2.5" neo-async "^2.6.0" @@ -5083,9 +4950,9 @@ hosted-git-info@^2.1.4, hosted-git-info@^2.7.1: integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== hosted-git-info@^3.0.6: - version "3.0.7" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-3.0.7.tgz#a30727385ea85acfcee94e0aad9e368c792e036c" - integrity sha512-fWqc0IcuXs+BmE9orLDyVykAG9GJtGLGuZAAqgcckPgv5xad4AcXGIv8galtQvlwutxSlaMcdw7BUtq2EIvqCQ== + version "3.0.8" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-3.0.8.tgz#6e35d4cc87af2c5f816e4cb9ce350ba87a3f370d" + integrity sha512-aXpmwoOhRBrw6X3j0h5RloK4x1OzsxMPyxqIHyNfSe2pypkVTZFpEiRoSipPEPlMrh0HW/XsjkJ5WgnCirpNUw== dependencies: lru-cache "^6.0.0" @@ -5212,7 +5079,7 @@ ignore@^4.0.3, ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.1.1, ignore@^5.1.4, ignore@^5.1.8, ignore@~5.1.8: +ignore@^5.1.4, ignore@^5.1.8, ignore@~5.1.8: version "5.1.8" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== @@ -5231,9 +5098,9 @@ import-fresh@^2.0.0: resolve-from "^3.0.0" import-fresh@^3.0.0, import-fresh@^3.2.1: - version "3.2.2" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.2.tgz#fc129c160c5d68235507f4331a6baad186bdbc3e" - integrity sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw== + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== dependencies: parent-module "^1.0.0" resolve-from "^4.0.0" @@ -5295,9 +5162,9 @@ inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, i integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== ini@^1.3.2, ini@^1.3.4, ini@~1.3.0: - version "1.3.7" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.7.tgz#a09363e1911972ea16d7a8851005d84cf09a9a84" - integrity sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ== + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== init-package-json@^1.10.3: version "1.10.3" @@ -5361,25 +5228,29 @@ is-accessor-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" -is-arguments@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" - integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA== +is-arguments@^1.0.4, is-arguments@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9" + integrity sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg== + dependencies: + call-bind "^1.0.0" is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= -is-bigint@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.0.tgz#73da8c33208d00f130e9b5e15d23eac9215601c4" - integrity sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g== - -is-boolean-object@^1.0.0: +is-bigint@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.1.tgz#10edc0900dd127697a92f6f9807c7617d68ac48e" - integrity sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ== + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.1.tgz#6923051dfcbc764278540b9ce0e6b3213aa5ebc2" + integrity sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg== + +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== + dependencies: + call-bind "^1.0.0" is-buffer@^1.1.5, is-buffer@~1.1.6: version "1.1.6" @@ -5387,9 +5258,9 @@ is-buffer@^1.1.5, is-buffer@~1.1.6: integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== is-callable@^1.1.4, is-callable@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9" - integrity sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA== + version "1.2.3" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" + integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== is-ci@^2.0.0: version "2.0.0" @@ -5398,7 +5269,7 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" -is-core-module@^2.0.0, is-core-module@^2.1.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== @@ -5510,17 +5381,17 @@ is-glob@^4.0.0, is-glob@^4.0.1: dependencies: is-extglob "^2.1.1" -is-map@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.1.tgz#520dafc4307bb8ebc33b813de5ce7c9400d644a1" - integrity sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw== +is-map@^2.0.1, is-map@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" + integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== -is-negative-zero@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461" - integrity sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE= +is-negative-zero@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" + integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== -is-number-object@^1.0.3: +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== @@ -5548,9 +5419,9 @@ is-obj@^2.0.0: integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== is-object@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470" - integrity sha1-iVJojF7C/9awPsyF52ngKQMINHA= + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf" + integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: version "1.1.0" @@ -5575,16 +5446,17 @@ is-potential-custom-element-name@^1.0.0: integrity sha1-DFLlS8yjkbssSUsh6GJtczbG45c= is-regex@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" - integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg== + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.2.tgz#81c8ebde4db142f2cf1c53fc86d6a45788266251" + integrity sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg== dependencies: + call-bind "^1.0.2" has-symbols "^1.0.1" -is-set@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.1.tgz#d1604afdab1724986d30091575f54945da7e5f43" - integrity sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA== +is-set@^2.0.1, is-set@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" + integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== is-ssh@^1.3.0: version "1.3.2" @@ -5603,12 +5475,12 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== -is-string@^1.0.4, is-string@^1.0.5: +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== -is-symbol@^1.0.2: +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== @@ -5623,12 +5495,13 @@ is-text-path@^1.0.1: text-extensions "^1.0.0" is-typed-array@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.3.tgz#a4ff5a5e672e1a55f99c7f54e59597af5c1df04d" - integrity sha512-BSYUBOK/HJibQ30wWkWold5txYwMUXQct9YHAQJr8fSwvZoiglcqB0pd7vEN23+Tsi9IUEjztdOSzl4qLVYGTQ== + version "1.1.5" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.5.tgz#f32e6e096455e329eb7b423862456aa213f0eb4e" + integrity sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug== dependencies: - available-typed-arrays "^1.0.0" - es-abstract "^1.17.4" + available-typed-arrays "^1.0.2" + call-bind "^1.0.2" + es-abstract "^1.18.0-next.2" foreach "^2.0.5" has-symbols "^1.0.1" @@ -6188,7 +6061,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== @@ -6262,76 +6135,76 @@ jsesc@^2.5.1: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== -jsii-diff@^1.15.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/jsii-diff/-/jsii-diff-1.15.0.tgz#77e03f5c5557ba20d9bc2cc354a7f03bb93f21f0" - integrity sha512-T47ogWBdztPrOhy53ngm9ZBF0gYz89BcNnM8WJAET6GcpO5qyoAwpQpf4WuA9oDdX8Q9yV1xOHPtBDD+7PmeFQ== +jsii-diff@^1.21.0: + version "1.21.0" + resolved "https://registry.yarnpkg.com/jsii-diff/-/jsii-diff-1.21.0.tgz#4e7f5b38fa42b4ee109592686ec5d5b168491c0b" + integrity sha512-88YUb9FO2jb6We9fTowR2k1+YhnFJ1LYDOOw7ThyyNyjsgEAheOV5vB4u43HTduZncVmncUNjcMirrUDm2JDzA== dependencies: - "@jsii/spec" "^1.15.0" - fs-extra "^9.0.1" - jsii-reflect "^1.15.0" + "@jsii/spec" "^1.21.0" + fs-extra "^9.1.0" + jsii-reflect "^1.21.0" log4js "^6.3.0" - typescript "~3.9.7" - yargs "^16.1.1" + typescript "~3.9.9" + yargs "^16.2.0" -jsii-pacmak@^1.15.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/jsii-pacmak/-/jsii-pacmak-1.15.0.tgz#48060233cfe693a24554c0e28a851aa1f690c734" - integrity sha512-/VxBDjC7Mi3zhuopm1oL0mXNBuC32YhuQJnHyHkFEnFduso7gnc6/3OCQbm3l5pOxHfg7oTBXIKxwDkh7EkK0w== +jsii-pacmak@^1.21.0: + version "1.21.0" + resolved "https://registry.yarnpkg.com/jsii-pacmak/-/jsii-pacmak-1.21.0.tgz#b9da4b5d2e980bc75ec37336175a2b99752d677f" + integrity sha512-04/fIZqM31cfTf48v7ni7MGeAwBEREP1WhvGkf4TSAZmAdMx1FUWQxuKiDK1/YeEvIUhNHIy/Ng9GcoSf+Rwfg== dependencies: - "@jsii/spec" "^1.15.0" + "@jsii/spec" "^1.21.0" clone "^2.1.2" - codemaker "^1.15.0" - commonmark "^0.29.2" + codemaker "^1.21.0" + commonmark "^0.29.3" escape-string-regexp "^4.0.0" - fs-extra "^9.0.1" - jsii-reflect "^1.15.0" - jsii-rosetta "^1.15.0" - semver "^7.3.2" - spdx-license-list "^6.3.0" + fs-extra "^9.1.0" + jsii-reflect "^1.21.0" + jsii-rosetta "^1.21.0" + semver "^7.3.4" + spdx-license-list "^6.4.0" xmlbuilder "^15.1.1" - yargs "^16.1.1" + yargs "^16.2.0" -jsii-reflect@^1.15.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/jsii-reflect/-/jsii-reflect-1.15.0.tgz#a65297a581b2b7bfbaa213d0bca7f76c693f718e" - integrity sha512-8mKV5OVt/FwRdLNmr7By5T0HyhjjLlHsE5oiOta6NdkdMB7magjzZ1bucenJ2DdvPidX2dP6IDujfOKoXVNsjA== +jsii-reflect@^1.21.0: + version "1.21.0" + resolved "https://registry.yarnpkg.com/jsii-reflect/-/jsii-reflect-1.21.0.tgz#2f6f7835d7428c5cc9d2c81f1c881e613710bc78" + integrity sha512-OwXhVhe+NRv/e6jaGBdIpm3S1KQcEXTZN+USiBd+c4kROLqxw+ubpMBsEVSKEZ7t+4WksLTWWNot31VZkJrZ5g== dependencies: - "@jsii/spec" "^1.15.0" + "@jsii/spec" "^1.21.0" colors "^1.4.0" - fs-extra "^9.0.1" - oo-ascii-tree "^1.15.0" - yargs "^16.1.1" - -jsii-rosetta@^1.15.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/jsii-rosetta/-/jsii-rosetta-1.15.0.tgz#a092591662e4c2f39144562eafe30cbc0d935e13" - integrity sha512-0yXdm6X0IAsjzKDq0QO0n7EPVQ3vTW3qwgFH+ZNirqFLP5xqnpxXS1BtrQNP9zxC7gfacgsDS48MroagKQWDHg== - dependencies: - "@jsii/spec" "^1.15.0" - commonmark "^0.29.2" - fs-extra "^9.0.1" - typescript "~3.9.7" + fs-extra "^9.1.0" + oo-ascii-tree "^1.21.0" + yargs "^16.2.0" + +jsii-rosetta@^1.21.0: + version "1.21.0" + resolved "https://registry.yarnpkg.com/jsii-rosetta/-/jsii-rosetta-1.21.0.tgz#591ebb2ce390b81f269a66d906446f8bebe9e23b" + integrity sha512-8W0vcWTr28q+1NWhVAY4lOwOlPHdGdg8b/gPHFccRi9ZM4uwRjW7YjmqD9FmX74dEg1Qmvd8nujW4Opow6PFtQ== + dependencies: + "@jsii/spec" "^1.21.0" + commonmark "^0.29.3" + fs-extra "^9.1.0" + typescript "~3.9.9" xmldom "^0.4.0" - yargs "^16.1.1" + yargs "^16.2.0" -jsii@^1.15.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/jsii/-/jsii-1.15.0.tgz#3f3160ee2c5fe62473855c1ac8d2aa9e3bf72ad1" - integrity sha512-kYiO1WkeqN7or4rz5ccYooO576+wiqiRGzv+UCI6hShKd42ff3xYZ1oTUSnBQdh9lp9i/nlNtx7KGUEqjC16Aw== +jsii@^1.21.0: + version "1.21.0" + resolved "https://registry.yarnpkg.com/jsii/-/jsii-1.21.0.tgz#6fd5dd9a18bb820a127ab51f55b2081cf14b2181" + integrity sha512-6siaRt1OyrQxC9pzLaFGj6bDkHMTsofcu8ODM0NCcukq2P4PlF1O39H0DV8Z40QF3KWbawJ/Utl7GtaSrdG2Ww== dependencies: - "@jsii/spec" "^1.15.0" + "@jsii/spec" "^1.21.0" case "^1.6.3" colors "^1.4.0" - deep-equal "^2.0.4" - fs-extra "^9.0.1" + deep-equal "^2.0.5" + fs-extra "^9.1.0" log4js "^6.3.0" - semver "^7.3.2" + semver "^7.3.4" semver-intersect "^1.4.0" sort-json "^2.0.0" - spdx-license-list "^6.3.0" - typescript "~3.9.7" - yargs "^16.1.1" + spdx-license-list "^6.4.0" + typescript "~3.9.9" + yargs "^16.2.0" json-diff@^0.5.4: version "0.5.4" @@ -6385,9 +6258,9 @@ json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= json5@2.x, json5@^2.1.2: - version "2.1.3" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" - integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA== + version "2.2.0" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" + integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== dependencies: minimist "^1.2.5" @@ -6444,10 +6317,10 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" -jszip@*, jszip@^3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.5.0.tgz#b4fd1f368245346658e781fec9675802489e15f6" - integrity sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA== +jszip@*, jszip@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.6.0.tgz#839b72812e3f97819cc13ac4134ffced95dd6af9" + integrity sha512-jgnQoG9LKnWO3mnVNBnfhkh0QknICd1FGSrXcgrl67zioyJ4wgx25o9ZqwNtrROSflGBCGYnJfjrIyRIby1OoQ== dependencies: lie "~3.3.0" pako "~1.0.2" @@ -6488,24 +6361,6 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== -lambda-leak@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lambda-leak/-/lambda-leak-2.0.0.tgz#771985d3628487f6e885afae2b54510dcfb2cd7e" - integrity sha1-dxmF02KEh/boha+uK1RRDc+yzX4= - -lambda-tester@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/lambda-tester/-/lambda-tester-3.6.0.tgz#ceb7d4f4f0da768487a05cff37dcd088508b5247" - integrity sha512-F2ZTGWCLyIR95o/jWK46V/WnOCFAEUG/m/V7/CLhPJ7PCM+pror1rZ6ujP3TkItSGxUfpJi0kqwidw+M/nEqWw== - dependencies: - app-root-path "^2.2.1" - dotenv "^8.0.0" - dotenv-json "^1.0.0" - lambda-leak "^2.0.0" - semver "^6.1.1" - uuid "^3.3.2" - vandium-utils "^1.1.1" - lazystream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" @@ -6647,6 +6502,13 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + lodash._reinterpolate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" @@ -6697,11 +6559,6 @@ lodash.isplainobject@^4.0.6: resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= -lodash.memoize@4.x: - version "4.1.2" - resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" - integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= - lodash.set@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" @@ -6737,7 +6594,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.2.1: +lodash@4.x, lodash@^4.17.12, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.2.1: version "4.17.20" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== @@ -6967,27 +6824,10 @@ meow@^4.0.0: redent "^2.0.0" trim-newlines "^2.0.0" -meow@^7.0.0: - version "7.1.1" - resolved "https://registry.yarnpkg.com/meow/-/meow-7.1.1.tgz#7c01595e3d337fcb0ec4e8eed1666ea95903d306" - integrity sha512-GWHvA5QOcS412WCo8vwKDlTelGLsCGBVevQB5Kva961rmNfun0PCbv5+xta2kUMFJyR8/oWnn7ddeKdosbAPbA== - dependencies: - "@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 "^2.5.0" - read-pkg-up "^7.0.1" - redent "^3.0.0" - trim-newlines "^3.0.0" - type-fest "^0.13.1" - yargs-parser "^18.1.3" - meow@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/meow/-/meow-8.0.0.tgz#1aa10ee61046719e334ffdc038bb5069250ec99a" - integrity sha512-nbsTRz2fwniJBFgUkcdISq8y/q9n9VbiHYbfwklFh5V4V2uAcxtKQkDc0yCLPM/kP0d+inZBewn3zJqewHE7kg== + version "8.1.2" + resolved "https://registry.yarnpkg.com/meow/-/meow-8.1.2.tgz#bcbe45bda0ee1729d350c03cffc8395a36c4e897" + integrity sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q== dependencies: "@types/minimist" "^1.2.0" camelcase-keys "^6.2.2" @@ -7050,17 +6890,17 @@ micromatch@^4.0.2: braces "^3.0.1" picomatch "^2.0.5" -mime-db@1.44.0: - version "1.44.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" - integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== +mime-db@1.45.0: + version "1.45.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.45.0.tgz#cceeda21ccd7c3a745eba2decd55d4b73e7879ea" + integrity sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w== mime-types@^2.1.12, mime-types@~2.1.19: - version "2.1.27" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" - integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== + version "2.1.28" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.28.tgz#1160c4757eab2c5363888e005273ecf79d2a0ecd" + integrity sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ== dependencies: - mime-db "1.44.0" + mime-db "1.45.0" mimic-fn@^1.0.0: version "1.2.0" @@ -7208,11 +7048,16 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@2.1.2, ms@^2.0.0, ms@^2.1.1: +ms@2.1.2: version "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.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== + multimatch@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-3.0.0.tgz#0e2534cc6bc238d9ab67e1b9cd5fcd85a6dbf70b" @@ -7295,10 +7140,10 @@ nise@^4.0.4: just-extend "^4.0.2" path-to-regexp "^1.7.0" -nock@^13.0.5: - version "13.0.5" - resolved "https://registry.yarnpkg.com/nock/-/nock-13.0.5.tgz#a618c6f86372cb79fac04ca9a2d1e4baccdb2414" - integrity sha512-1ILZl0zfFm2G4TIeJFW0iHknxr2NyA+aGCMTjDVUsBY4CkMRispF1pfIYkTRdAR/3Bg+UzdEuK0B6HczMQZcCg== +nock@^13.0.7: + version "13.0.7" + resolved "https://registry.yarnpkg.com/nock/-/nock-13.0.7.tgz#9bc718c66bd0862dfa14601a9ba678a406127910" + integrity sha512-WBz73VYIjdbO6BwmXODRQLtn7B5tldA9pNpWJe5QTtTEscQlY5KXU4srnGzBOK2fWakkXj69gfTnXGzmrsaRWw== dependencies: debug "^4.1.0" json-stringify-safe "^5.0.1" @@ -7594,18 +7439,18 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" - integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA== +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== -object-is@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.3.tgz#2e3b9e65560137455ee3bd62aec4d90a2ea1cc81" - integrity sha512-teyqLvFWzLkq5B9ki8FVWA902UER2qkxmdA4nLf+wjOLAWgxzCWZNCxpDq9MvE8MmhWNr+I8w3BN49Vx36Y6Xg== +object-is@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.4.tgz#63d6c83c00a43f4cbc9434eb9757c8a5b8565068" + integrity sha512-1ZvAZ4wlF7IyPVOcE1Omikt7UpaFlOQq0HlSti+ZvDH3UiD2brwGMwDbyV43jao2bKJ+4+WdPJHSd7kgzKYVqg== dependencies: + call-bind "^1.0.0" define-properties "^1.1.3" - es-abstract "^1.18.0-next.1" object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" @@ -7619,7 +7464,7 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" -object.assign@^4.1.1: +object.assign@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== @@ -7630,12 +7475,13 @@ object.assign@^4.1.1: object-keys "^1.1.1" object.getownpropertydescriptors@^2.0.3: - version "2.1.0" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649" - integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg== + version "2.1.1" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.1.tgz#0dfda8d108074d9c563e80490c883b6661091544" + integrity sha512-6DtXgZ/lIZ9hqx4GtZETobXLR/ZLaa0aqV0kzbn80Rf8Z2e/XFnhA0I7p07N2wH8bBBltr2xQPi6sbKWAY2Eng== dependencies: + call-bind "^1.0.0" define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" + es-abstract "^1.18.0-next.1" object.pick@^1.3.0: version "1.3.0" @@ -7645,13 +7491,13 @@ object.pick@^1.3.0: isobject "^3.0.1" object.values@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e" - integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA== + version "1.1.2" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.2.tgz#7a2015e06fcb0f546bd652486ce8583a4731c731" + integrity sha512-MYC0jvJopr8EK6dPBiO8Nb9mvjdypOachO5REGk6MXzujbBrAisKo3HmdEI6kZDL6fC31Mwee/5YbtMebixeag== dependencies: + call-bind "^1.0.0" define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" - function-bind "^1.1.1" + es-abstract "^1.18.0-next.1" has "^1.0.3" octokit-pagination-methods@^1.1.0: @@ -7680,10 +7526,10 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" -oo-ascii-tree@^1.15.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/oo-ascii-tree/-/oo-ascii-tree-1.15.0.tgz#51227b6a0a8a1c933afe312556c696058fcc32a3" - integrity sha512-FR8ygFwcH9DBkQIcp/lAe49EFcTNMGjU3jgAsRaZ8ktNVxDM9EszlLNEO1K10QTHZwT3iZxq+E+KwT811B4ayw== +oo-ascii-tree@^1.21.0: + version "1.21.0" + resolved "https://registry.yarnpkg.com/oo-ascii-tree/-/oo-ascii-tree-1.21.0.tgz#8408fceb90799c7af769b7ab709ec35ea9868b8b" + integrity sha512-N91VyM/R9K8axskaVYSg+IJiSDJVKFQ2IfQyBp5Rv7t2YETjJDMgA6Ew9MGv82fhpz95qKLlZmgrQsb7scb2Eg== opener@^1.5.1: version "1.5.2" @@ -7753,9 +7599,9 @@ own-or@^1.0.0: integrity sha1-Tod/vtqaLsgAD7wLyuOWRe6L+Nw= p-each-series@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.1.0.tgz#961c8dd3f195ea96c747e636b262b800a6b1af48" - integrity sha512-ZuRs1miPT4HrjFa+9fRfOFXxGJfORgelKV9f9nNOWw2gl6gVsRaVDOQP0+MI0G0wGKns1Yacsu0GjOFbTK0JFQ== + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.2.0.tgz#105ab0357ce72b202a8a8b94933672657b5e2a9a" + integrity sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA== p-finally@^1.0.0: version "1.0.0" @@ -7776,6 +7622,13 @@ p-limit@^2.0.0, p-limit@^2.2.0: dependencies: p-try "^2.0.0" +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" @@ -7797,6 +7650,13 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + p-map-series@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-1.0.0.tgz#bf98fe575705658a9e1351befb85ae4c1f07bdca" @@ -7936,9 +7796,9 @@ parse-json@^4.0.0: json-parse-better-errors "^1.0.1" parse-json@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.1.0.tgz#f96088cdf24a8faa9aea9a009f2d9d942c999646" - integrity sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ== + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== dependencies: "@babel/code-frame" "^7.0.0" error-ex "^1.3.1" @@ -7946,12 +7806,14 @@ parse-json@^5.0.0: lines-and-columns "^1.1.6" parse-path@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.2.tgz#ef14f0d3d77bae8dd4bc66563a4c151aac9e65aa" - integrity sha512-HSqVz6iuXSiL8C1ku5Gl1Z5cwDd9Wo0q8CoffdAghP6bz8pJa1tcMC+m4N+z6VAS8QdksnIGq1TB6EgR4vPR6w== + version "4.0.3" + resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.3.tgz#82d81ec3e071dcc4ab49aa9f2c9c0b8966bb22bf" + integrity sha512-9Cepbp2asKnWTJ9x2kpw6Fe8y9JDbqwahGCTvklzd/cEq5C5JC59x2Xb0Kx+x0QZ8bvNquGO8/BWP0cwBHzSAA== dependencies: is-ssh "^1.3.0" protocols "^1.4.0" + qs "^6.9.4" + query-string "^6.13.8" parse-url@^5.0.0: version "5.0.2" @@ -8293,25 +8155,45 @@ punycode@^2.0.0, punycode@^2.1.0, punycode@^2.1.1: integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== pure-rand@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-4.1.1.tgz#9fca2d4af5c4e870bac337ed860977426ed17bf6" - integrity sha512-cZw4AL/KI6aDTdqHEbJPe2ZoHM3kSdpJRLJetv8c3tfq9o+PvQDXrHNEpB0AWukAGFx4fmeOerAGwkA4rtUgdA== + version "4.1.2" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-4.1.2.tgz#cbad2a3e3ea6df0a8d80d8ba204779b5679a5205" + integrity sha512-uLzZpQWfroIqyFWmX/pl0OL2JHJdoU3dbh0dvZ25fChHFJJi56J5oQZhW6QgbT2Llwh1upki84LnTwlZvsungA== q@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" 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== + qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== +query-string@^6.13.8: + version "6.14.0" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.14.0.tgz#0b7b7ca326f5facf10dd2d45d26645cd287f8c92" + integrity sha512-In3o+lUxlgejoVJgwEdYtdxrmlL0cQWJXj0+kkI7RWVo7hg5AhFtybeKlC9Dpgbr8eOC4ydpEh8017WwyfzqVQ== + dependencies: + decode-uri-component "^0.2.0" + filter-obj "^1.1.0" + split-on-first "^1.0.0" + strict-uri-encode "^2.0.0" + querystring@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" 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== + quick-lru@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" @@ -8481,7 +8363,7 @@ readable-stream@1.1.x: isarray "0.0.1" string_decoder "~0.10.x" -"readable-stream@2 || 3", readable-stream@3, readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: +"readable-stream@2 || 3", readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -8547,12 +8429,12 @@ regex-not@^1.0.0, regex-not@^1.0.2: safe-regex "^1.1.0" regexp.prototype.flags@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75" - integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ== + version "1.3.1" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz#7ef352ae8d159e758c0eadca6f8fcb4eef07be26" + integrity sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA== dependencies: + call-bind "^1.0.2" define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" regexpp@^3.0.0, regexpp@^3.1.0: version "3.1.0" @@ -8679,27 +8561,12 @@ 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.11.1, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.3.2: - version "1.17.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" - integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== - dependencies: - path-parse "^1.0.6" - -resolve@^1.10.1: - version "1.19.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" - integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== - dependencies: - is-core-module "^2.1.0" - path-parse "^1.0.6" - -resolve@^1.18.1: - version "1.18.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.18.1.tgz#018fcb2c5b207d2a6424aee361c5a266da8f4130" - integrity sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA== +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.11.1, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.18.1: + version "1.20.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" + integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== dependencies: - is-core-module "^2.0.0" + is-core-module "^2.2.0" path-parse "^1.0.6" restore-cursor@^2.0.0: @@ -8726,16 +8593,9 @@ reusify@^1.0.4: integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== rfdc@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.1.4.tgz#ba72cc1367a0ccd9cf81a870b3b58bd3ad07f8c2" - integrity sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug== - -rimraf@2.6.3: - version "2.6.3" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== - dependencies: - glob "^7.1.3" + version "1.2.0" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.2.0.tgz#9e9894258f48f284b43c3143c68070a4f373b949" + integrity sha512-ijLyszTMmUrXvjSooucVQwimGUk84eRcmCuLV8Xghe3UO85mjUtRAHRyoMM6XtyqbECaXuBWx18La3523sXINA== rimraf@^2.5.4, rimraf@^2.6.2, rimraf@^2.6.3: version "2.7.1" @@ -8744,7 +8604,7 @@ rimraf@^2.5.4, rimraf@^2.6.2, rimraf@^2.6.3: dependencies: glob "^7.1.3" -rimraf@^3.0.0: +rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== @@ -8762,9 +8622,11 @@ run-async@^2.2.0: integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== run-parallel@^1.1.9: - version "1.1.10" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.10.tgz#60a51b2ae836636c81377df16cb107351bcd13ef" - integrity sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw== + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" run-queue@^1.0.0, run-queue@^1.0.3: version "1.0.3" @@ -8846,12 +8708,14 @@ 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.2.1, semver@^7.3.2: - version "7.3.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" - integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== +semver@7.x, semver@^7.1.1, 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== + dependencies: + lru-cache "^6.0.0" -semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.2.0, semver@^6.3.0: +semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== @@ -8927,27 +8791,27 @@ shellwords@^0.1.1: integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== side-channel@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.3.tgz#cdc46b057550bbab63706210838df5d4c19519c3" - integrity sha512-A6+ByhlLkksFoUepsGxfj5x1gTSrs+OydsRptUxeNCabQpCFUvcwIczgOigI8vhY/OJCnPnyE9rGiwgvr9cS1g== + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== dependencies: - es-abstract "^1.18.0-next.0" - object-inspect "^1.8.0" + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== -sinon@^9.0.1, sinon@^9.2.1: - version "9.2.1" - resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.2.1.tgz#64cc88beac718557055bd8caa526b34a2231be6d" - integrity sha512-naPfsamB5KEE1aiioaoqJ6MEhdUs/2vtI5w1hPAXX/UwvoPjXcwh1m5HiKx0HGgKR8lQSoFIgY5jM6KK8VrS9w== +sinon@^9.0.1, sinon@^9.2.4: + version "9.2.4" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.2.4.tgz#e55af4d3b174a4443a8762fa8421c2976683752b" + integrity sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg== dependencies: "@sinonjs/commons" "^1.8.1" "@sinonjs/fake-timers" "^6.0.1" - "@sinonjs/formatio" "^5.0.1" - "@sinonjs/samsam" "^5.2.0" + "@sinonjs/samsam" "^5.3.1" diff "^4.0.2" nise "^4.0.4" supports-color "^7.1.0" @@ -8967,15 +8831,6 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -slice-ansi@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" - integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== - dependencies: - ansi-styles "^3.2.0" - astral-regex "^1.0.0" - is-fullwidth-code-point "^2.0.0" - slice-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" @@ -9043,9 +8898,9 @@ socks-proxy-agent@^4.0.0: socks "~2.3.2" socks@^2.3.3: - version "2.5.0" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.5.0.tgz#3a7c286db114f67864a4bd8b4207a91d1db3d6db" - integrity sha512-00OqQHp5SCbwm9ecOMJj9aQtMSjwi1uVuGQoxnpKCS50VKZcOZ8z11CTKypmR8sEy7nZimy/qXY7rYJYbRlXmA== + version "2.5.1" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.5.1.tgz#7720640b6b5ec9a07d556419203baa3f0596df5f" + integrity sha512-oZCsJJxapULAYJaEYBSzMcz8m3jqgGrHaGhkmU/o/PQfFWYWxkAaA0UMGImb6s6tEXfKi959X6VJjMMQ3P6TTQ== dependencies: ip "^1.1.5" smart-buffer "^4.1.0" @@ -9094,9 +8949,9 @@ source-map-support@^0.5.10, source-map-support@^0.5.17, source-map-support@^0.5. source-map "^0.6.0" source-map-url@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" - integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= + version "0.4.1" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" + integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== source-map@^0.5.0, source-map@^0.5.6: version "0.5.7" @@ -9159,14 +9014,19 @@ spdx-expression-parse@^3.0.0: spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.6" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz#c80757383c28abf7296744998cbc106ae8b854ce" - integrity sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw== + version "3.0.7" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz#e9c18a410e5ed7e12442a549fbd8afa767038d65" + integrity sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ== -spdx-license-list@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/spdx-license-list/-/spdx-license-list-6.3.0.tgz#29507bdd88e5f1dcf62634d3c7f9051245e7ef07" - integrity sha512-Qz8ru5VVK5T4cFOBrshIzggzrQ15fVBcpjpZLCVz2j9KNnpslGbw8w1r06v2vi6YP6bnUSY5CXsFCfUypLZ2GA== +spdx-license-list@^6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/spdx-license-list/-/spdx-license-list-6.4.0.tgz#9850c3699c1d35745285607d064d2a5145049d12" + integrity sha512-4BxgJ1IZxTJuX1YxMGu2cRYK46Bk9zJNTK2/R0wNZR0cm+6SVl26/uG7FQmQtxoJQX1uZ0EpTi2L7zvMLboaBA== + +split-on-first@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" + integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" @@ -9182,6 +9042,13 @@ split2@^2.0.0: dependencies: through2 "^2.0.2" +split2@^3.0.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" + integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== + dependencies: + readable-stream "^3.0.0" + split@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" @@ -9217,9 +9084,9 @@ ssri@^6.0.0, ssri@^6.0.1: figgy-pudding "^3.5.1" stack-utils@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.3.tgz#db7a475733b5b8bf6521907b18891d29006f7751" - integrity sha512-WldO+YmqhEpjp23eHZRhOT1NQF51STsbxZ+/AdpFD+EhheFxAe5d0WoK4DQVJkSHacPrJJX3OqRAl9CgHf78pg== + version "1.0.4" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.4.tgz#4b600971dcfc6aed0cbdf2a8268177cc916c87c8" + integrity sha512-IPDJfugEGbfizBwBZRZ3xpccMdRyP5lqsBWXGQWimVjua/ccLCeMOAVjlc1R7LxFjo5sEDhyNIXd8mo/AiDS9w== dependencies: escape-string-regexp "^2.0.0" @@ -9230,21 +9097,21 @@ stack-utils@^2.0.2: dependencies: escape-string-regexp "^2.0.0" -standard-version@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/standard-version/-/standard-version-9.0.0.tgz#814055add91eec8679a773768927f927183fc818" - integrity sha512-eRR04IscMP3xW9MJTykwz13HFNYs8jS33AGuDiBKgfo5YrO0qX0Nxb4rjupVwT5HDYL/aR+MBEVLjlmVFmFEDQ== +standard-version@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/standard-version/-/standard-version-9.1.0.tgz#07589469324d967ffe665fa86ef612949a858a80" + integrity sha512-EJcbKUGKBuHjiDSUL5XjPhT1KGVM+UCvv/ti70fHnJwJyJqTSJWl0mWj/Wj0WwsoskyvKWURESzBsZmCCMUZzg== dependencies: chalk "^2.4.2" - conventional-changelog "3.1.23" + conventional-changelog "3.1.24" conventional-changelog-config-spec "2.1.0" - conventional-changelog-conventionalcommits "4.4.0" - conventional-recommended-bump "6.0.10" + conventional-changelog-conventionalcommits "4.5.0" + conventional-recommended-bump "6.0.11" detect-indent "^6.0.0" detect-newline "^3.1.0" dotgitignore "^2.1.0" figures "^3.1.0" - find-up "^4.1.0" + find-up "^5.0.0" fs-access "^1.0.1" git-semver-tags "^4.0.0" semver "^7.1.1" @@ -9291,6 +9158,11 @@ streamroller@^2.2.4: debug "^4.1.1" fs-extra "^8.1.0" +strict-uri-encode@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" + integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY= + string-length@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.1.tgz#4a973bf31ef77c4edbceadd6af2611996985f8a1" @@ -9339,21 +9211,21 @@ string.prototype.repeat@^0.2.0: resolved "https://registry.yarnpkg.com/string.prototype.repeat/-/string.prototype.repeat-0.2.0.tgz#aba36de08dcee6a5a337d49b2ea1da1b28fc0ecf" integrity sha1-q6Nt4I3O5qWjN9SbLqHaGyj8Ds8= -string.prototype.trimend@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.2.tgz#6ddd9a8796bc714b489a3ae22246a208f37bfa46" - integrity sha512-8oAG/hi14Z4nOVP0z6mdiVZ/wqjDtWSLygMigTzAb+7aPEDTleeFf+WrF+alzecxIRkckkJVn+dTlwzJXORATw== +string.prototype.trimend@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz#a22bd53cca5c7cf44d7c9d5c732118873d6cd18b" + integrity sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw== dependencies: + call-bind "^1.0.0" define-properties "^1.1.3" - es-abstract "^1.18.0-next.1" -string.prototype.trimstart@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.2.tgz#22d45da81015309cd0cdd79787e8919fc5c613e7" - integrity sha512-7F6CdBTl5zyu30BJFdzSTlSlLPwODC23Od+iLoVH8X6+3fvDPPuBVVj9iaB1GOsSTSIgVfsfm27R2FGrAPznWg== +string.prototype.trimstart@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz#9b4cb590e123bb36564401d59824298de50fd5aa" + integrity sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg== dependencies: + call-bind "^1.0.0" define-properties "^1.1.3" - es-abstract "^1.18.0-next.1" string_decoder@^1.1.1: version "1.3.0" @@ -9506,17 +9378,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@^5.2.3: - version "5.4.6" - resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" - integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== - dependencies: - ajv "^6.10.2" - lodash "^4.17.14" - slice-ansi "^2.1.0" - string-width "^3.0.0" - -table@^6.0.7: +table@^6.0.4, table@^6.0.7: version "6.0.7" resolved "https://registry.yarnpkg.com/table/-/table-6.0.7.tgz#e45897ffbcc1bcf9e8a87bf420f2c9e5a7a52a34" integrity sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g== @@ -9606,9 +9468,9 @@ tap@^12.0.1: yapool "^1.0.0" tar-stream@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.4.tgz#c4fb1a11eb0da29b893a5b25476397ba2d053bfa" - integrity sha512-o3pS2zlG4gxr67GmFYBLlq+dM8gyRGUOvsrHclSkvtVtQbjV0s/+ZE8OpICbaj8clrX3tjeHngYGP7rweaBnuw== + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== dependencies: bl "^4.0.3" end-of-stream "^1.4.1" @@ -9863,10 +9725,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.4.4: - version "26.4.4" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.4.4.tgz#61f13fb21ab400853c532270e52cc0ed7e502c49" - integrity sha512-3lFWKbLxJm34QxyVNNCgXX1u4o/RV0myvA2y2Bxm46iGIjKlaY0own9gIckbjZJPn+WaJEnfPPJ20HHGpoq4yg== +ts-jest@^26.5.1: + version "26.5.1" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.5.1.tgz#4d53ee4481552f57c1624f0bd3425c8b17996150" + integrity sha512-G7Rmo3OJMvlqE79amJX8VJKDiRcd7/r61wh9fnvvG8cAjhA9edklGw/dCxRSQmfZ/z8NDums5srSVgwZos1qfg== dependencies: "@types/jest" "26.x" bs-logger "0.x" @@ -9874,16 +9736,16 @@ ts-jest@^26.4.4: fast-json-stable-stringify "2.x" jest-util "^26.1.0" json5 "2.x" - lodash.memoize "4.x" + lodash "4.x" make-error "1.x" mkdirp "1.x" semver "7.x" yargs-parser "20.x" -ts-mock-imports@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/ts-mock-imports/-/ts-mock-imports-1.3.1.tgz#1bb701c05a24f3ba30621b5e31af123dbd994db8" - integrity sha512-MKEGXb40TUbpQ6b/if424zs0gfTyHfsebw+FUBkqbC0kVoPwoXhoe82lJH4dC92j4vDoId6pSjtIvwvtSMnS5w== +ts-mock-imports@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/ts-mock-imports/-/ts-mock-imports-1.3.3.tgz#7865888382806aa8f09412ab582a8e06f2bd1b6d" + integrity sha512-zCAcb89Y+f3Bhw5VaHrHMh5tiuwAQEl5D3e0r5ELCdLl9EnZpb8Oeei/S60Qf4LORIfmJEXb3TpR5kxtL6j2cg== ts-node@^8.0.2: version "8.10.2" @@ -9896,10 +9758,10 @@ ts-node@^8.0.2: source-map-support "^0.5.17" yn "3.1.1" -ts-node@^9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.1.0.tgz#95eae4c6d0f94f2545884078e1eb1b14d2155639" - integrity sha512-0yqcL4sgruCvM+w64LiAfNJo6+lHfCYc5Ajj4yiLNkJ9oZ2HWaa+Kso7htYOOxVQ7+csAjdUjffOe9PIqC4pMg== +ts-node@^9.1.1: + version "9.1.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.1.1.tgz#51a9a450a3e959401bda5f004a72d54b936d376d" + integrity sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg== dependencies: arg "^4.1.0" create-require "^1.1.0" @@ -9929,14 +9791,14 @@ tslib@^1.8.1, tslib@^1.9.0: integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslib@^2.0.1: - version "2.0.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" - integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== + version "2.1.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" + integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== tsutils@^3.17.1: - version "3.17.1" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" - integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g== + version "3.20.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.20.0.tgz#ea03ea45462e146b53d70ce0893de453ff24f698" + integrity sha512-RYbuQuvkhuqVeXweWT3tJLKOEJ/UUw9GjNEZGWdrLLlM+611o1gwLHBpxoFJKKl25fLprp2eVthtKs5JOrNeXg== dependencies: tslib "^1.8.1" @@ -9976,11 +9838,6 @@ type-fest@^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.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" - integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== - type-fest@^0.18.0: version "0.18.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" @@ -10013,26 +9870,27 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript-json-schema@^0.47.0: - version "0.47.0" - resolved "https://registry.yarnpkg.com/typescript-json-schema/-/typescript-json-schema-0.47.0.tgz#84dde5460b127c6774da81bf70b23c7e04857b13" - integrity sha512-A6NVwSOTSsNDHfaqDcDeKwwyXEeKqBHoAr20jcetnYj4e8C6zVFofAVhAuwsBXCRYiWEE/lyHrcxpsSpbIk0Mg== +typescript-json-schema@^0.49.0: + version "0.49.0" + resolved "https://registry.yarnpkg.com/typescript-json-schema/-/typescript-json-schema-0.49.0.tgz#442f6347ca85fb0d9811f217fb0d6537b68734b3" + integrity sha512-PumZkTmEE3T8TVyoJU6ZCp3K6VCmCb3Ei6fUaRIuDsIzYtmdJc6jV1D0RyBe5sd5mJ1iB6Zckm4KAKbqXs9oDw== dependencies: "@types/json-schema" "^7.0.6" glob "^7.1.6" json-stable-stringify "^1.0.1" + ts-node "^9.1.1" typescript "^4.1.3" yargs "^16.2.0" -typescript@^3.3.3, typescript@~3.9.7: - version "3.9.7" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa" - integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw== +typescript@^3.3.3, typescript@~3.9.9: + version "3.9.9" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.9.tgz#e69905c54bc0681d0518bd4d587cc6f2d0b1a674" + integrity sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w== typescript@^4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7" - integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg== + version "4.1.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.5.tgz#123a3b214aaff3be32926f0d8f1f6e704eb89a72" + integrity sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA== typescript@~3.8.3: version "3.8.3" @@ -10045,9 +9903,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.11.6" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.11.6.tgz#144b50d3e05eadd3ad4dd047c60ca541a8cd4e9c" - integrity sha512-oASI1FOJ7BBFkSCNDZ446EgkSuHkOZBuqRFrwXIKWCoXw8ZXQETooTQjkAcBS03Acab7ubCKsXnwuV2svy061g== + version "3.12.8" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.12.8.tgz#a82e6e53c9be14f7382de3d068ef1e26e7d4aaf8" + integrity sha512-fvBeuXOsvqjecUtF/l1dwsrrf5y2BCUk9AOJGzGcm6tE7vegku5u/YvqjyDaAGr422PLoLnrxg3EnRvTqsdC1w== uid-number@0.0.6: version "0.0.6" @@ -10132,9 +9990,9 @@ upath@^1.2.0: integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== uri-js@^4.2.2: - version "4.4.0" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602" - integrity sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g== + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== dependencies: punycode "^2.1.0" @@ -10194,9 +10052,9 @@ v8-compile-cache@^2.0.3: integrity sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q== v8-to-istanbul@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-7.0.0.tgz#b4fe00e35649ef7785a9b7fcebcea05f37c332fc" - integrity sha512-fLL2rFuQpMtm9r8hrAV2apXX/WqHJ6+IC4/eQVdMDGBUgH/YMV4Gv3duk3kjmyg6uiQWBAA9nJwue4iJUOkHeA== + 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== dependencies: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^1.6.0" @@ -10217,11 +10075,6 @@ validate-npm-package-name@^3.0.0: dependencies: builtins "^1.0.3" -vandium-utils@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/vandium-utils/-/vandium-utils-1.2.0.tgz#44735de4b7641a05de59ebe945f174e582db4f59" - integrity sha1-RHNd5LdkGgXeWevpRfF05YLbT1k= - verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" @@ -10305,15 +10158,15 @@ whatwg-url@^8.0.0: webidl-conversions "^6.1.0" which-boxed-primitive@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz#cbe8f838ebe91ba2471bb69e9edbda67ab5a5ec1" - integrity sha512-7BT4TwISdDGBgaemWU0N0OU7FeAEJ9Oo2P1PHRm/FCWoEi2VLWC9b6xvxAA3C/NMpxg3HXVgi0sMmGbNUbNepQ== + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== dependencies: - is-bigint "^1.0.0" - is-boolean-object "^1.0.0" - is-number-object "^1.0.3" - is-string "^1.0.4" - is-symbol "^1.0.2" + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" which-collection@^1.0.1: version "1.0.1" @@ -10331,12 +10184,13 @@ which-module@^2.0.0: integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= which-typed-array@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.2.tgz#e5f98e56bda93e3dac196b01d47c1156679c00b2" - integrity sha512-KT6okrd1tE6JdZAy3o2VhMoYPh3+J6EMZLyrxBQsZflI1QCZIxMrIYLkosd8Twf+YfknVIHmYQPgJt238p8dnQ== + version "1.1.4" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.4.tgz#8fcb7d3ee5adf2d771066fba7cf37e32fe8711ff" + integrity sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA== dependencies: available-typed-arrays "^1.0.2" - es-abstract "^1.17.5" + call-bind "^1.0.0" + es-abstract "^1.18.0-next.1" foreach "^2.0.5" function-bind "^1.1.1" has-symbols "^1.0.1" @@ -10463,17 +10317,10 @@ write-pkg@^3.1.0: sort-keys "^2.0.0" write-json-file "^2.2.0" -write@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" - integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== - dependencies: - mkdirp "^0.5.1" - ws@^7.2.3: - version "7.4.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.0.tgz#a5dd76a24197940d4a8bb9e0e152bb4503764da7" - integrity sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ== + version "7.4.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.3.tgz#1f9643de34a543b8edb124bdcbc457ae55a6e5cd" + integrity sha512-hr6vCR76GsossIRsr8OLR9acVVm1jyfEWvhbNjtgPOrfvAlKzvyeg/P6r8RuDjRyrcQoPQT7K0DGEPc7Ae6jzA== xml-js@^1.6.11: version "1.6.11" @@ -10531,9 +10378,9 @@ xtend@~4.0.1: integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== y18n@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" - integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + version "4.0.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4" + integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ== y18n@^5.0.5: version "5.0.5" @@ -10571,9 +10418,9 @@ yapool@^1.0.0: integrity sha1-9pPymjFbUNmp2iZGp6ZkXJaYW2o= yargs-parser@20.x, yargs-parser@^20.2.2, yargs-parser@^20.2.3: - version "20.2.4" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" - integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== + version "20.2.5" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.5.tgz#5d37729146d3f894f39fc94b6796f5b239513186" + integrity sha512-jYRGS3zWy20NtDtK2kBgo/TlAoy5YUuhD9/LZ7z7W4j1Fdw2cqD0xEEclf8fxc8xjD6X5Qr+qQQwCEsP8iRiYg== yargs-parser@^13.0.0, yargs-parser@^13.1.2: version "13.1.2" @@ -10591,7 +10438,7 @@ yargs-parser@^15.0.1: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^18.1.2, yargs-parser@^18.1.3: +yargs-parser@^18.1.2: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== @@ -10649,7 +10496,7 @@ yargs@^15.0.2, yargs@^15.3.1, yargs@^15.4.1: y18n "^4.0.0" yargs-parser "^18.1.2" -yargs@^16.0.3, yargs@^16.1.1, yargs@^16.2.0: +yargs@^16.0.3, 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== @@ -10667,6 +10514,11 @@ yn@3.1.1: resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + zip-stream@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-4.0.4.tgz#3a8f100b73afaa7d1ae9338d910b321dec77ff3a"