diff --git a/.github/workflows/README.md b/.github/workflows/README.md new file mode 100644 index 0000000000000..9d54ed7236ef7 --- /dev/null +++ b/.github/workflows/README.md @@ -0,0 +1,47 @@ +# AWS CDK GitHub Actions + +These workflows and actions are configured in the AWS CDK GitHub repository. + +## Pull Request Triggered + +### Auto Approve +[auto-approve.yml](auto-approve.yml): Approves merging PRs with the +`pr/auto-approve` label. +Owner: Core CDK team + +### PR Linter +[pr-linter.yml](pr-linter.yml): Runs `tools/@aws-cdk-prlint` on each PR to +check for correctness. +Owner: Core CDK team + +### v2-main PR automation +[v2-pull-request.yml](v2-pull-request.yml): Runs `pkglint` on merge forward PRs +and commits the results. +Owner: Core CDK team + +### Label Assigner +[issue-label-assign.yml](issue-label-assign.yml): Github action for automatically adding labels and/or setting assignees when an Issue or PR is opened or edited based on user-defined Area +Owner: CDK support team + +## Issue Triggered + +### Closed Issue Message +[closed-issue-message.yml](closed-issue-message.yml): Adds a reminder message +to issues that are closed. +Owner: CDK support team + +### Label Assigner +[issue-label-assign.yml](issue-label-assign.yml): Github action for automatically adding labels and/or setting assignees when an Issue or PR is opened or edited based on user-defined Area +Owner: CDK support team + +## Scheduled Actions + +### Issue Lifecycle Handling +[close-stale-issues.yml](close-stale-issues.yml): Handles labeling issues and +PRs with `closing-soon`, `response-requested`, etc. +Owner: CDK support team + +### Yarn Upgrader +[yarn-upgrade.yml](yarn-upgrade.yml): Upgrades yarn dependencies and creates a +patch file for downloading. +Owner: Core CDK team \ No newline at end of file diff --git a/.github/workflows/close-stale-issues.yml b/.github/workflows/close-stale-issues.yml index 487a095e0c372..ee427df90ef79 100644 --- a/.github/workflows/close-stale-issues.yml +++ b/.github/workflows/close-stale-issues.yml @@ -4,7 +4,7 @@ name: "Close Stale Issues" on: workflow_dispatch: schedule: - - cron: "0 6 * * *" + - cron: "0 */4 * * *" jobs: cleanup: diff --git a/.yarnrc b/.yarnrc index ac05beb3696fd..019f345f39305 100644 --- a/.yarnrc +++ b/.yarnrc @@ -7,3 +7,4 @@ # be forced to registry.yarnpkg.com. # https://github.com/npm/cli/issues/3783 registry "https://registry.npmjs.org" +ignore-engines true # the 'engines' key for 'aws-cdk-lib' has specifies node14 as min while v1 will remain at node10 diff --git a/packages/@aws-cdk/aws-appsync/README.md b/packages/@aws-cdk/aws-appsync/README.md index a191b51a86483..8e78c92bd4b7b 100644 --- a/packages/@aws-cdk/aws-appsync/README.md +++ b/packages/@aws-cdk/aws-appsync/README.md @@ -24,6 +24,10 @@ The `@aws-cdk/aws-appsync` package contains constructs for building flexible APIs that use GraphQL. +```ts nofixture +import * as appsync from '@aws-cdk/aws-appsync'; +``` + ## Example ### DynamoDB @@ -52,24 +56,21 @@ type Mutation { CDK stack file `app-stack.ts`: ```ts -import * as appsync from '@aws-cdk/aws-appsync'; -import * as db from '@aws-cdk/aws-dynamodb'; - -const api = new appsync.GraphqlApi(stack, 'Api', { +const api = new appsync.GraphqlApi(this, 'Api', { name: 'demo', - schema: appsync.Schema.fromAsset(join(__dirname, 'schema.graphql')), + schema: appsync.Schema.fromAsset(path.join(__dirname, 'schema.graphql')), authorizationConfig: { defaultAuthorization: { - authorizationType: appsync.AuthorizationType.IAM + authorizationType: appsync.AuthorizationType.IAM, }, }, xrayEnabled: true, }); -const demoTable = new db.Table(stack, 'DemoTable', { +const demoTable = new dynamodb.Table(this, 'DemoTable', { partitionKey: { name: 'id', - type: db.AttributeType.STRING, + type: dynamodb.AttributeType.STRING, }, }); @@ -89,7 +90,7 @@ demoDS.createResolver({ fieldName: 'addDemo', requestMappingTemplate: appsync.MappingTemplate.dynamoDbPutItem( appsync.PrimaryKey.partition('id').auto(), - appsync.Values.projecting('input') + appsync.Values.projecting('input'), ), responseMappingTemplate: appsync.MappingTemplate.dynamoDbResultItem(), }); @@ -103,15 +104,15 @@ against the Data API with GraphQL queries, mutations, and subscriptions. ```ts // Create username and password secret for DB Cluster -const secret = new rds.DatabaseSecret(stack, 'AuroraSecret', { +const secret = new rds.DatabaseSecret(this, 'AuroraSecret', { username: 'clusteradmin', }); // The VPC to place the cluster in -const vpc = new ec2.Vpc(stack, 'AuroraVpc'); +const vpc = new ec2.Vpc(this, 'AuroraVpc'); // Create the serverless cluster, provide all values needed to customise the database. -const cluster = new rds.ServerlessCluster(stack, 'AuroraCluster', { +const cluster = new rds.ServerlessCluster(this, 'AuroraCluster', { engine: rds.DatabaseClusterEngine.AURORA_MYSQL, vpc, credentials: { username: 'clusteradmin' }, @@ -120,13 +121,14 @@ const cluster = new rds.ServerlessCluster(stack, 'AuroraCluster', { }); // Build a data source for AppSync to access the database. +declare const api: appsync.GraphqlApi; const rdsDS = api.addRdsDataSource('rds', cluster, secret, 'demos'); // Set up a resolver for an RDS query. rdsDS.createResolver({ typeName: 'Query', fieldName: 'getDemosRds', - requestMappingTemplate: MappingTemplate.fromString(` + requestMappingTemplate: appsync.MappingTemplate.fromString(` { "version": "2018-05-29", "statements": [ @@ -134,7 +136,7 @@ rdsDS.createResolver({ ] } `), - responseMappingTemplate: MappingTemplate.fromString(` + responseMappingTemplate: appsync.MappingTemplate.fromString(` $utils.toJson($utils.rds.toJsonObject($ctx.result)[0]) `), }); @@ -143,7 +145,7 @@ rdsDS.createResolver({ rdsDS.createResolver({ typeName: 'Mutation', fieldName: 'addDemoRds', - requestMappingTemplate: MappingTemplate.fromString(` + requestMappingTemplate: appsync.MappingTemplate.fromString(` { "version": "2018-05-29", "statements": [ @@ -156,7 +158,7 @@ rdsDS.createResolver({ } } `), - responseMappingTemplate: MappingTemplate.fromString(` + responseMappingTemplate: appsync.MappingTemplate.fromString(` $utils.toJson($utils.rds.toJsonObject($ctx.result)[1][0]) `), }); @@ -212,11 +214,9 @@ GraphQL response mapping template `response.vtl`: CDK stack file `app-stack.ts`: ```ts -import * as appsync from '@aws-cdk/aws-appsync'; - -const api = new appsync.GraphqlApi(scope, 'api', { +const api = new appsync.GraphqlApi(this, 'api', { name: 'api', - schema: appsync.Schema.fromFile(join(__dirname, 'schema.graphql')), + schema: appsync.Schema.fromAsset(path.join(__dirname, 'schema.graphql')), }); const httpDs = api.addHttpDataSource( @@ -227,7 +227,7 @@ const httpDs = api.addHttpDataSource( description: 'from appsync to StepFunctions Workflow', authorizationConfig: { signingRegion: 'us-east-1', - signingServiceName: 'states' + signingServiceName: 'states', } } ); @@ -235,8 +235,8 @@ const httpDs = api.addHttpDataSource( httpDs.createResolver({ typeName: 'Mutation', fieldName: 'callStepFunction', - requestMappingTemplate: MappingTemplate.fromFile('request.vtl'), - responseMappingTemplate: MappingTemplate.fromFile('response.vtl') + requestMappingTemplate: appsync.MappingTemplate.fromFile('request.vtl'), + responseMappingTemplate: appsync.MappingTemplate.fromFile('response.vtl'), }); ``` @@ -247,16 +247,19 @@ through your AWS account. You can use AppSync resolvers to perform GraphQL opera such as queries, mutations, and subscriptions. ```ts -const user = new User(stack, 'User'); -const domain = new es.Domain(stack, 'Domain', { +import * as es from '@aws-cdk/aws-elasticsearch'; + +const user = new iam.User(this, 'User'); +const domain = new es.Domain(this, 'Domain', { version: es.ElasticsearchVersion.V7_1, - removalPolicy: cdk.RemovalPolicy.DESTROY, + removalPolicy: RemovalPolicy.DESTROY, fineGrainedAccessControl: { masterUserArn: user.userArn }, encryptionAtRest: { enabled: true }, nodeToNodeEncryption: true, enforceHttps: true, }); +declare const api: appsync.GraphqlApi; const ds = api.addElasticsearchDataSource('ds', domain); ds.createResolver({ @@ -293,23 +296,23 @@ When declaring your GraphQL Api, CDK defaults to a code-first approach if the `schema` property is not configured. ```ts -const api = new appsync.GraphqlApi(stack, 'api', { name: 'myApi' }); +const api = new appsync.GraphqlApi(this, 'api', { name: 'myApi' }); ``` CDK will declare a `Schema` class that will give your Api access functions to -define your schema code-first: `addType`, `addObjectType`, `addToSchema`, etc. +define your schema code-first: `addType`, `addToSchema`, etc. You can also declare your `Schema` class outside of your CDK stack, to define your schema externally. ```ts const schema = new appsync.Schema(); -schema.addObjectType('demo', { +schema.addType(new appsync.ObjectType('demo', { definition: { id: appsync.GraphqlType.id() }, -}); -const api = new appsync.GraphqlApi(stack, 'api', { +})); +const api = new appsync.GraphqlApi(this, 'api', { name: 'myApi', - schema + schema, }); ``` @@ -321,9 +324,9 @@ You can define your GraphQL Schema from a file on disk. For convenience, use the `appsync.Schema.fromAsset` to specify the file representing your schema. ```ts -const api = appsync.GraphqlApi(stack, 'api', { +const api = new appsync.GraphqlApi(this, 'api', { name: 'myApi', - schema: appsync.Schema.fromAsset(join(__dirname, 'schema.graphl')), + schema: appsync.Schema.fromAsset(path.join(__dirname, 'schema.graphl')), }); ``` @@ -334,9 +337,11 @@ another stack into your CDK app. Utilizing the `fromXxx` function, you have the ability to add data sources and resolvers through a `IGraphqlApi` interface. ```ts -const importedApi = appsync.GraphqlApi.fromGraphqlApiAttributes(stack, 'IApi', { +declare const api: appsync.GraphqlApi; +declare const table: dynamodb.Table; +const importedApi = appsync.GraphqlApi.fromGraphqlApiAttributes(this, 'IApi', { graphqlApiId: api.apiId, - graphqlArn: api.arn, + graphqlApiArn: api.arn, }); importedApi.addDynamoDbDataSource('TableDataSource', table); ``` @@ -362,9 +367,10 @@ authorization mode to finish defining your authorization. For example, this is a with AWS Lambda Authorization. ```ts -authFunction = new lambda.Function(stack, 'auth-function', {}); +import * as lambda from '@aws-cdk/aws-lambda'; +declare const authFunction: lambda.Function; -new appsync.GraphqlApi(stack, 'api', { +new appsync.GraphqlApi(this, 'api', { name: 'api', schema: appsync.Schema.fromAsset(path.join(__dirname, 'appsync.test.graphql')), authorizationConfig: { @@ -390,7 +396,7 @@ for `IAM` authorized access you would configure the following. In `schema.graphql`: -```ts +```gql type Mutation { updateExample(...): ... @aws_iam @@ -401,18 +407,18 @@ In `IAM`: ```json { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "appsync:GraphQL" - ], - "Resource": [ - "arn:aws:appsync:REGION:ACCOUNT_ID:apis/GRAPHQL_ID/types/Mutation/fields/updateExample" - ] - } - ] + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "appsync:GraphQL" + ], + "Resource": [ + "arn:aws:appsync:REGION:ACCOUNT_ID:apis/GRAPHQL_ID/types/Mutation/fields/updateExample" + ] + } + ] } ``` @@ -423,14 +429,12 @@ To make this easier, CDK provides `grant` API. Use the `grant` function for more granular authorization. ```ts -const role = new iam.Role(stack, 'Role', { +const role = new iam.Role(this, 'Role', { assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'), }); -const api = new appsync.GraphqlApi(stack, 'API', { - definition -}); +declare const api: appsync.GraphqlApi; -api.grant(role, appsync.IamResource.custom('types/Mutation/fields/updateExample'), 'appsync:GraphQL') +api.grant(role, appsync.IamResource.custom('types/Mutation/fields/updateExample'), 'appsync:GraphQL'); ``` ### IamResource @@ -454,6 +458,9 @@ These include: - grantSubscription (use to grant access to Subscription fields) ```ts +declare const api: appsync.GraphqlApi; +declare const role: iam.Role; + // For generic types api.grantMutation(role, 'updateExample'); @@ -468,10 +475,12 @@ backend data source. Developers can compose operations (Functions) and execute them in sequence with Pipeline Resolvers. ```ts -const appsyncFunction = new appsync.AppsyncFunction(stack, 'function', { +declare const api: appsync.GraphqlApi; + +const appsyncFunction = new appsync.AppsyncFunction(this, 'function', { name: 'appsync_function', - api: api, - dataSource: apiDataSource, + api, + dataSource: api.addNoneDataSource('none'), requestMappingTemplate: appsync.MappingTemplate.fromFile('request.vtl'), responseMappingTemplate: appsync.MappingTemplate.fromFile('response.vtl'), }); @@ -481,10 +490,14 @@ AppSync Functions are used in tandem with pipeline resolvers to compose multiple operations. ```ts -const pipelineResolver = new appsync.Resolver(stack, 'pipeline', { - name: 'pipeline_resolver', - api: api, - dataSource: apiDataSource, +declare const api: appsync.GraphqlApi; +declare const appsyncFunction: appsync.AppsyncFunction; + +const pipelineResolver = new appsync.Resolver(this, 'pipeline', { + api, + dataSource: api.addNoneDataSource('none'), + typeName: 'typeName', + fieldName: 'fieldName', requestMappingTemplate: appsync.MappingTemplate.fromFile('beforeRequest.vtl'), pipelineConfig: [appsyncFunction], responseMappingTemplate: appsync.MappingTemplate.fromFile('afterResponse.vtl'), @@ -537,48 +550,38 @@ Above we see a schema that allows for generating paginated responses. For exampl we can query `allFilms(first: 100)` since `FilmConnection` acts as an intermediary for holding `FilmEdges` we can write a resolver to return the first 100 films. -In a separate file, we can declare our scalar types: `scalar-types.ts`. - -```ts -import { GraphqlType } from '@aws-cdk/aws-appsync'; - -export const string = appsync.GraphqlType.string(); -export const int = appsync.GraphqlType.int(); -``` - -In another separate file, we can declare our object types and related functions. +In a separate file, we can declare our object types and related functions. We will call this file `object-types.ts` and we will have created it in a way that allows us to generate other `XxxConnection` and `XxxEdges` in the future. -```ts -const pluralize = require('pluralize'); -import * as scalar from './scalar-types.ts'; +```ts nofixture import * as appsync from '@aws-cdk/aws-appsync'; +const pluralize = require('pluralize'); export const args = { - after: scalar.string, - first: scalar.int, - before: scalar.string, - last: scalar.int, + after: appsync.GraphqlType.string(), + first: appsync.GraphqlType.int(), + before: appsync.GraphqlType.string(), + last: appsync.GraphqlType.int(), }; export const Node = new appsync.InterfaceType('Node', { - definition: { id: scalar.string } + definition: { id: appsync.GraphqlType.string() } }); -export const FilmNode = new appsync.ObjectType.implementInterface('FilmNode', { +export const FilmNode = new appsync.ObjectType('FilmNode', { interfaceTypes: [Node], - definition: { filmName: scalar.string } + definition: { filmName: appsync.GraphqlType.string() } }); export function generateEdgeAndConnection(base: appsync.ObjectType) { const edge = new appsync.ObjectType(`${base.name}Edge`, { - definition: { node: base.attribute(), cursor: scalar.string } + definition: { node: base.attribute(), cursor: appsync.GraphqlType.string() } }); const connection = new appsync.ObjectType(`${base.name}Connection`, { definition: { - edges: edges.attribute({ isList: true }), + edges: edge.attribute({ isList: true }), [pluralize(base.name)]: base.attribute({ isList: true }), - totalCount: scalar.int, + totalCount: appsync.GraphqlType.int(), } }); return { edge: edge, connection: connection }; @@ -588,29 +591,30 @@ export function generateEdgeAndConnection(base: appsync.ObjectType) { Finally, we will go to our `cdk-stack` and combine everything together to generate our schema. -```ts -import * as appsync from '@aws-cdk/aws-appsync'; -import * as schema from './object-types'; +```ts fixture=with-objects +declare const dummyRequest: appsync.MappingTemplate; +declare const dummyResponse: appsync.MappingTemplate; -const api = new appsync.GraphqlApi(stack, 'Api', { +const api = new appsync.GraphqlApi(this, 'Api', { name: 'demo', }); -this.objectTypes = [ schema.Node, schema.Film ]; +const objectTypes = [ Node, FilmNode ]; -const filmConnections = schema.generateEdgeAndConnection(schema.Film); +const filmConnections = generateEdgeAndConnection(FilmNode); api.addQuery('allFilms', new appsync.ResolvableField({ - returnType: filmConnections.connection.attribute(), - args: schema.args, - dataSource: dummyDataSource, - requestMappingTemplate: dummyRequest, - responseMappingTemplate: dummyResponse, - }), -}); + returnType: filmConnections.connection.attribute(), + args: args, + dataSource: api.addNoneDataSource('none'), + requestMappingTemplate: dummyRequest, + responseMappingTemplate: dummyResponse, +})); -this.objectTypes.map((t) => api.addType(t)); -Object.keys(filmConnections).forEach((key) => api.addType(filmConnections[key])); +api.addType(Node); +api.addType(FilmNode); +api.addType(filmConnections.edge); +api.addType(filmConnections.connection); ``` Notice how we can utilize the `generateEdgeAndConnection` function to generate @@ -701,6 +705,9 @@ type Info { The CDK code required would be: ```ts +declare const api: appsync.GraphqlApi; +declare const dummyRequest: appsync.MappingTemplate; +declare const dummyResponse: appsync.MappingTemplate; const info = new appsync.ObjectType('Info', { definition: { node: new appsync.ResolvableField({ @@ -729,6 +736,9 @@ type Query { The CDK code required would be: ```ts +declare const api: appsync.GraphqlApi; +declare const dummyRequest: appsync.MappingTemplate; +declare const dummyResponse: appsync.MappingTemplate; const query = new appsync.ObjectType('Query', { definition: { get: new appsync.ResolvableField({ @@ -784,12 +794,12 @@ To learn more about **Interface Types**, read the docs [here](https://graphql.or the `demo` variable is an **Object Type**. **Object Types** are defined by GraphQL Types and are only usable when linked to a GraphQL Api. -You can create Object Types in three ways: +You can create Object Types in two ways: 1. Object Types can be created ***externally***. ```ts - const api = new appsync.GraphqlApi(stack, 'Api', { + const api = new appsync.GraphqlApi(this, 'Api', { name: 'demo', }); const demo = new appsync.ObjectType('Demo', { @@ -799,34 +809,28 @@ You can create Object Types in three ways: }, }); - api.addType(object); + api.addType(demo); ``` > This method allows for reusability and modularity, ideal for larger projects. For example, imagine moving all Object Type definition outside the stack. - `scalar-types.ts` - a file for scalar type definitions - - ```ts - export const required_string = appsync.GraphqlType.string({ isRequired: true }); - ``` - `object-types.ts` - a file for object type definitions - ```ts - import { required_string } from './scalar-types'; + ```ts nofixture + import * as appsync from '@aws-cdk/aws-appsync'; export const demo = new appsync.ObjectType('Demo', { definition: { - id: required_string, - version: required_string, + id: appsync.GraphqlType.string({ isRequired: true }), + version: appsync.GraphqlType.string({ isRequired: true }), }, }); ``` `cdk-stack.ts` - a file containing our cdk stack - ```ts - import { demo } from './object-types'; + ```ts fixture=with-objects + declare const api: appsync.GraphqlApi; api.addType(demo); ``` @@ -869,6 +873,7 @@ enum Episode { The above GraphQL Enumeration Type can be expressed in CDK as the following: ```ts +declare const api: appsync.GraphqlApi; const episode = new appsync.EnumType('Episode', { definition: [ 'NEWHOPE', @@ -896,10 +901,11 @@ input Review { The above GraphQL Input Type can be expressed in CDK as the following: ```ts +declare const api: appsync.GraphqlApi; const review = new appsync.InputType('Review', { definition: { - stars: GraphqlType.int({ isRequired: true }), - commentary: GraphqlType.string(), + stars: appsync.GraphqlType.int({ isRequired: true }), + commentary: appsync.GraphqlType.string(), }, }); api.addType(review); @@ -923,6 +929,7 @@ The above GraphQL Union Type encompasses the Object Types of Human, Droid and St can be expressed in CDK as the following: ```ts +declare const api: appsync.GraphqlApi; const string = appsync.GraphqlType.string(); const human = new appsync.ObjectType('Human', { definition: { name: string } }); const droid = new appsync.ObjectType('Droid', { definition: { name: string } }); @@ -945,6 +952,11 @@ To add fields for these queries, we can simply run the `addQuery` function to ad to the schema's `Query` type. ```ts +declare const api: appsync.GraphqlApi; +declare const filmConnection: appsync.InterfaceType; +declare const dummyRequest: appsync.MappingTemplate; +declare const dummyResponse: appsync.MappingTemplate; + const string = appsync.GraphqlType.string(); const int = appsync.GraphqlType.int(); api.addQuery('allFilms', new appsync.ResolvableField({ @@ -968,10 +980,15 @@ To add fields for these mutations, we can simply run the `addMutation` function to the schema's `Mutation` type. ```ts +declare const api: appsync.GraphqlApi; +declare const filmNode: appsync.ObjectType; +declare const dummyRequest: appsync.MappingTemplate; +declare const dummyResponse: appsync.MappingTemplate; + const string = appsync.GraphqlType.string(); const int = appsync.GraphqlType.int(); api.addMutation('addFilm', new appsync.ResolvableField({ - returnType: film.attribute(), + returnType: filmNode.attribute(), args: { name: string, film_number: int }, dataSource: api.addNoneDataSource('none'), requestMappingTemplate: dummyRequest, @@ -994,10 +1011,13 @@ To add fields for these subscriptions, we can simply run the `addSubscription` f to the schema's `Subscription` type. ```ts +declare const api: appsync.GraphqlApi; +declare const film: appsync.InterfaceType; + api.addSubscription('addedFilm', new appsync.Field({ returnType: film.attribute(), args: { id: appsync.GraphqlType.id({ isRequired: true }) }, - directive: [appsync.Directive.subscribe('addFilm')], + directives: [appsync.Directive.subscribe('addFilm')], })); ``` diff --git a/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts b/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts index 93c9078e32358..71cd6e32060c1 100644 --- a/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts +++ b/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts @@ -150,7 +150,7 @@ export interface OpenIdConnectConfig { /** * The client identifier of the Relying party at the OpenID identity provider. * A regular expression can be specified so AppSync can validate against multiple client identifiers at a time. - * @example - 'ABCD|CDEF' where ABCD and CDEF are two different clientId + * @example - 'ABCD|CDEF' // where ABCD and CDEF are two different clientId * @default - * (All) */ readonly clientId?: string; diff --git a/packages/@aws-cdk/aws-appsync/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-appsync/rosetta/default.ts-fixture new file mode 100644 index 0000000000000..2b84336958cc7 --- /dev/null +++ b/packages/@aws-cdk/aws-appsync/rosetta/default.ts-fixture @@ -0,0 +1,15 @@ +// Fixture with packages imported, but nothing else +import { Construct, RemovalPolicy, Stack } from '@aws-cdk/core'; +import appsync = require('@aws-cdk/aws-appsync'); +import ec2 = require('@aws-cdk/aws-ec2'); +import dynamodb = require('@aws-cdk/aws-dynamodb'); +import iam = require('@aws-cdk/aws-iam'); +import rds = require('@aws-cdk/aws-rds'); +import path = require('path'); + +class Fixture extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + /// here + } +} diff --git a/packages/@aws-cdk/aws-appsync/rosetta/with-objects.ts-fixture b/packages/@aws-cdk/aws-appsync/rosetta/with-objects.ts-fixture new file mode 100644 index 0000000000000..1251aad728423 --- /dev/null +++ b/packages/@aws-cdk/aws-appsync/rosetta/with-objects.ts-fixture @@ -0,0 +1,49 @@ +// Fixture with packages imported, but nothing else +import { Construct, Stack } from '@aws-cdk/core'; +import appsync = require('@aws-cdk/aws-appsync'); +const pluralize = require('pluralize'); + +const args = { + after: appsync.GraphqlType.string(), + first: appsync.GraphqlType.int(), + before: appsync.GraphqlType.string(), + last: appsync.GraphqlType.int(), +}; + +const Node = new appsync.InterfaceType('Node', { + definition: { id: appsync.GraphqlType.string() } +}); + +const FilmNode = new appsync.ObjectType('FilmNode', { + interfaceTypes: [Node], + definition: { filmName: appsync.GraphqlType.string() } +}); + +function generateEdgeAndConnection(base: appsync.ObjectType) { + const edge = new appsync.ObjectType(`${base.name}Edge`, { + definition: { node: base.attribute(), cursor: appsync.GraphqlType.string() } + }); + const connection = new appsync.ObjectType(`${base.name}Connection`, { + definition: { + edges: edge.attribute({ isList: true }), + [pluralize(base.name)]: base.attribute({ isList: true }), + totalCount: appsync.GraphqlType.int(), + } + }); + return { edge: edge, connection: connection }; +} + +const demo = new appsync.ObjectType('Demo', { + definition: { + id: appsync.GraphqlType.string({ isRequired: true }), + version: appsync.GraphqlType.string({ isRequired: true }), + }, +}); + +class Fixture extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + + /// here + } +} diff --git a/packages/@aws-cdk/aws-autoscaling/README.md b/packages/@aws-cdk/aws-autoscaling/README.md index 67e55eee91a9f..75aa4f66807e2 100644 --- a/packages/@aws-cdk/aws-autoscaling/README.md +++ b/packages/@aws-cdk/aws-autoscaling/README.md @@ -378,6 +378,32 @@ new autoscaling.AutoScalingGroup(stack, 'ASG', { }); ``` +## Configuring Instance Metadata Service (IMDS) + +### Toggling IMDSv1 + +You can configure [EC2 Instance Metadata Service](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html) options to either +allow both IMDSv1 and IMDSv2 or enforce IMDSv2 when interacting with the IMDS. + +To do this for a single `AutoScalingGroup`, you can use set the `requireImdsv2` property. +The example below demonstrates IMDSv2 being required on a single `AutoScalingGroup`: + +```ts +new autoscaling.AutoScalingGroup(stack, 'ASG', { + requireImdsv2: true, + // ... +}); +``` + +You can also use `AutoScalingGroupRequireImdsv2Aspect` to apply the operation to multiple AutoScalingGroups. +The example below demonstrates the `AutoScalingGroupRequireImdsv2Aspect` being used to require IMDSv2 for all AutoScalingGroups in a stack: + +```ts +const aspect = new autoscaling.AutoScalingGroupRequireImdsv2Aspect(); + +Aspects.of(stack).add(aspect); +``` + ## Future work * [ ] CloudWatch Events (impossible to add currently as the AutoScalingGroup ARN is diff --git a/packages/@aws-cdk/aws-autoscaling/lib/aspects/index.ts b/packages/@aws-cdk/aws-autoscaling/lib/aspects/index.ts new file mode 100644 index 0000000000000..31fc534776144 --- /dev/null +++ b/packages/@aws-cdk/aws-autoscaling/lib/aspects/index.ts @@ -0,0 +1 @@ +export * from './require-imdsv2-aspect'; \ No newline at end of file diff --git a/packages/@aws-cdk/aws-autoscaling/lib/aspects/require-imdsv2-aspect.ts b/packages/@aws-cdk/aws-autoscaling/lib/aspects/require-imdsv2-aspect.ts new file mode 100644 index 0000000000000..e399dce585d79 --- /dev/null +++ b/packages/@aws-cdk/aws-autoscaling/lib/aspects/require-imdsv2-aspect.ts @@ -0,0 +1,38 @@ +import * as cdk from '@aws-cdk/core'; +import { AutoScalingGroup } from '../auto-scaling-group'; +import { CfnLaunchConfiguration } from '../autoscaling.generated'; + +/** + * Aspect that makes IMDSv2 required on instances deployed by AutoScalingGroups. + */ +export class AutoScalingGroupRequireImdsv2Aspect implements cdk.IAspect { + constructor() { + } + + public visit(node: cdk.IConstruct): void { + if (!(node instanceof AutoScalingGroup)) { + return; + } + + const launchConfig = node.node.tryFindChild('LaunchConfig') as CfnLaunchConfiguration; + if (cdk.isResolvableObject(launchConfig.metadataOptions)) { + this.warn(node, 'CfnLaunchConfiguration.MetadataOptions field is a CDK token.'); + return; + } + + launchConfig.metadataOptions = { + ...launchConfig.metadataOptions, + httpTokens: 'required', + }; + } + + /** + * Adds a warning annotation to a node. + * + * @param node The scope to add the warning to. + * @param message The warning message. + */ + protected warn(node: cdk.IConstruct, message: string) { + cdk.Annotations.of(node).addWarning(`${AutoScalingGroupRequireImdsv2Aspect.name} failed on node ${node.node.id}: ${message}`); + } +} diff --git a/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts b/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts index 027034249c4dd..45fd06c478dfc 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts @@ -7,6 +7,7 @@ import * as sns from '@aws-cdk/aws-sns'; import { Annotations, + Aspects, Aws, CfnAutoScalingRollingUpdate, CfnCreationPolicy, CfnUpdatePolicy, Duration, Fn, IResource, Lazy, PhysicalName, Resource, Stack, Tags, @@ -14,6 +15,7 @@ import { Tokenization, withResolved, } from '@aws-cdk/core'; import { Construct } from 'constructs'; +import { AutoScalingGroupRequireImdsv2Aspect } from './aspects'; import { CfnAutoScalingGroup, CfnAutoScalingGroupProps, CfnLaunchConfiguration } from './autoscaling.generated'; import { BasicLifecycleHookProps, LifecycleHook } from './lifecycle-hook'; import { BasicScheduledActionProps, ScheduledAction } from './scheduled-action'; @@ -384,6 +386,13 @@ export interface AutoScalingGroupProps extends CommonAutoScalingGroupProps { * @default - default options */ readonly initOptions?: ApplyCloudFormationInitOptions; + + /** + * Whether IMDSv2 should be required on launched instances. + * + * @default - false + */ + readonly requireImdsv2?: boolean; } /** @@ -1065,6 +1074,10 @@ export class AutoScalingGroup extends AutoScalingGroupBase implements } this.spotPrice = props.spotPrice; + + if (props.requireImdsv2) { + Aspects.of(this).add(new AutoScalingGroupRequireImdsv2Aspect()); + } } /** diff --git a/packages/@aws-cdk/aws-autoscaling/lib/index.ts b/packages/@aws-cdk/aws-autoscaling/lib/index.ts index 69fede92e300b..186d1a3058fae 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/index.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/index.ts @@ -1,3 +1,4 @@ +export * from './aspects'; export * from './auto-scaling-group'; export * from './schedule'; export * from './lifecycle-hook'; diff --git a/packages/@aws-cdk/aws-autoscaling/test/aspects/require-imdsv2-aspect.test.ts b/packages/@aws-cdk/aws-autoscaling/test/aspects/require-imdsv2-aspect.test.ts new file mode 100644 index 0000000000000..22a58f097a98b --- /dev/null +++ b/packages/@aws-cdk/aws-autoscaling/test/aspects/require-imdsv2-aspect.test.ts @@ -0,0 +1,79 @@ +import { + expect as expectCDK, + haveResourceLike, +} from '@aws-cdk/assert-internal'; +import '@aws-cdk/assert-internal/jest'; +import * as ec2 from '@aws-cdk/aws-ec2'; +import * as cdk from '@aws-cdk/core'; +import { + AutoScalingGroup, + AutoScalingGroupRequireImdsv2Aspect, + CfnLaunchConfiguration, +} from '../../lib'; + +describe('AutoScalingGroupRequireImdsv2Aspect', () => { + let app: cdk.App; + let stack: cdk.Stack; + let vpc: ec2.Vpc; + + beforeEach(() => { + app = new cdk.App(); + stack = new cdk.Stack(app, 'Stack'); + vpc = new ec2.Vpc(stack, 'Vpc'); + }); + + test('warns when metadataOptions is a token', () => { + // GIVEN + const asg = new AutoScalingGroup(stack, 'AutoScalingGroup', { + vpc, + instanceType: new ec2.InstanceType('t2.micro'), + machineImage: ec2.MachineImage.latestAmazonLinux(), + }); + const launchConfig = asg.node.tryFindChild('LaunchConfig') as CfnLaunchConfiguration; + launchConfig.metadataOptions = fakeToken(); + const aspect = new AutoScalingGroupRequireImdsv2Aspect(); + + // WHEN + cdk.Aspects.of(stack).add(aspect); + + // THEN + expectCDK(stack).notTo(haveResourceLike('AWS::AutoScaling::LaunchConfiguration', { + MetadataOptions: { + HttpTokens: 'required', + }, + })); + expect(asg.node.metadataEntry).toContainEqual({ + data: expect.stringContaining('CfnLaunchConfiguration.MetadataOptions field is a CDK token.'), + type: 'aws:cdk:warning', + trace: undefined, + }); + }); + + test('requires IMDSv2', () => { + // GIVEN + new AutoScalingGroup(stack, 'AutoScalingGroup', { + vpc, + instanceType: new ec2.InstanceType('t2.micro'), + machineImage: ec2.MachineImage.latestAmazonLinux(), + }); + const aspect = new AutoScalingGroupRequireImdsv2Aspect(); + + // WHEN + cdk.Aspects.of(stack).add(aspect); + + // THEN + expectCDK(stack).to(haveResourceLike('AWS::AutoScaling::LaunchConfiguration', { + MetadataOptions: { + HttpTokens: 'required', + }, + })); + }); +}); + +function fakeToken(): cdk.IResolvable { + return { + creationStack: [], + resolve: (_c) => {}, + toString: () => '', + }; +} diff --git a/packages/@aws-cdk/aws-autoscaling/test/auto-scaling-group.test.ts b/packages/@aws-cdk/aws-autoscaling/test/auto-scaling-group.test.ts index 64795593e8ec4..d74860638fd30 100644 --- a/packages/@aws-cdk/aws-autoscaling/test/auto-scaling-group.test.ts +++ b/packages/@aws-cdk/aws-autoscaling/test/auto-scaling-group.test.ts @@ -1364,6 +1364,27 @@ describe('auto scaling group', () => { }); + + test('requires imdsv2', () => { + // GIVEN + const stack = new cdk.Stack(); + const vpc = mockVpc(stack); + + // WHEN + new autoscaling.AutoScalingGroup(stack, 'MyASG', { + vpc, + instanceType: new ec2.InstanceType('t2.micro'), + machineImage: ec2.MachineImage.latestAmazonLinux(), + requireImdsv2: true, + }); + + // THEN + expect(stack).toHaveResourceLike('AWS::AutoScaling::LaunchConfiguration', { + MetadataOptions: { + HttpTokens: 'required', + }, + }); + }); }); function mockVpc(stack: cdk.Stack) { diff --git a/packages/@aws-cdk/aws-codepipeline-actions/lib/servicecatalog/deploy-action-beta1.ts b/packages/@aws-cdk/aws-codepipeline-actions/lib/servicecatalog/deploy-action-beta1.ts index 34b055c14735b..302b82b2a8725 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/lib/servicecatalog/deploy-action-beta1.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/lib/servicecatalog/deploy-action-beta1.ts @@ -35,12 +35,8 @@ export interface ServiceCatalogDeployActionBeta1Props extends codepipeline.Commo /** * CodePipeline action to connect to an existing ServiceCatalog product. -<<<<<<< HEAD:packages/@aws-cdk/aws-codepipeline-actions/lib/servicecatalog/deploy-action.ts * * **Note**: this class is still experimental, and may have breaking changes in the future! - * -======= ->>>>>>> master:packages/@aws-cdk/aws-codepipeline-actions/lib/servicecatalog/deploy-action-beta1.ts */ export class ServiceCatalogDeployActionBeta1 extends Action { private readonly templatePath: string; diff --git a/packages/@aws-cdk/aws-ec2/test/vpc.test.ts b/packages/@aws-cdk/aws-ec2/test/vpc.test.ts index ca375dc7fe321..90942056865c7 100644 --- a/packages/@aws-cdk/aws-ec2/test/vpc.test.ts +++ b/packages/@aws-cdk/aws-ec2/test/vpc.test.ts @@ -361,7 +361,7 @@ describe('vpc', () => { } }); - test('with custom subents and natGateways = 2 there should be only two NATGW', () => { + test('with custom subnets and natGateways = 2 there should be only two NATGW', () => { const stack = getTestStack(); new Vpc(stack, 'TheVPC', { cidr: '10.0.0.0/21', diff --git a/packages/@aws-cdk/aws-eks/lib/cluster-resource.ts b/packages/@aws-cdk/aws-eks/lib/cluster-resource.ts index 662f4e345a24c..88f3cd0138344 100644 --- a/packages/@aws-cdk/aws-eks/lib/cluster-resource.ts +++ b/packages/@aws-cdk/aws-eks/lib/cluster-resource.ts @@ -16,6 +16,7 @@ export interface ClusterResourceProps { readonly resourcesVpcConfig: CfnCluster.ResourcesVpcConfigProperty; readonly roleArn: string; readonly encryptionConfig?: Array; + readonly kubernetesNetworkConfig?: CfnCluster.KubernetesNetworkConfigProperty; readonly name: string; readonly version?: string; readonly endpointPrivateAccess: boolean; @@ -78,6 +79,7 @@ export class ClusterResource extends CoreConstruct { version: props.version, roleArn: props.roleArn, encryptionConfig: props.encryptionConfig, + kubernetesNetworkConfig: props.kubernetesNetworkConfig, resourcesVpcConfig: { subnetIds: (props.resourcesVpcConfig as CfnCluster.ResourcesVpcConfigProperty).subnetIds, securityGroupIds: (props.resourcesVpcConfig as CfnCluster.ResourcesVpcConfigProperty).securityGroupIds, diff --git a/packages/@aws-cdk/aws-eks/lib/cluster.ts b/packages/@aws-cdk/aws-eks/lib/cluster.ts index 2db0438537f97..c1d00a9dcd767 100644 --- a/packages/@aws-cdk/aws-eks/lib/cluster.ts +++ b/packages/@aws-cdk/aws-eks/lib/cluster.ts @@ -540,6 +540,15 @@ export interface ClusterOptions extends CommonClusterOptions { * using AWS-Managed encryption keys. */ readonly secretsEncryptionKey?: kms.IKey; + + /** + * The CIDR block to assign Kubernetes service IP addresses from. + * + * @default - Kubernetes assigns addresses from either the + * 10.100.0.0/16 or 172.20.0.0/16 CIDR blocks + * @see https://docs.aws.amazon.com/eks/latest/APIReference/API_KubernetesNetworkConfigRequest.html#AmazonEKS-Type-KubernetesNetworkConfigRequest-serviceIpv4Cidr + */ + readonly serviceIpv4Cidr?: string; } /** @@ -1182,7 +1191,7 @@ export class Cluster extends ClusterBase { this.onEventLayer = props.onEventLayer; this.kubectlMemory = props.kubectlMemory; - const privateSubents = this.selectPrivateSubnets().slice(0, 16); + const privateSubnets = this.selectPrivateSubnets().slice(0, 16); const publicAccessDisabled = !this.endpointAccess._config.publicAccess; const publicAccessRestricted = !publicAccessDisabled && this.endpointAccess._config.publicCidrs @@ -1190,19 +1199,19 @@ export class Cluster extends ClusterBase { // validate endpoint access configuration - if (privateSubents.length === 0 && publicAccessDisabled) { + if (privateSubnets.length === 0 && publicAccessDisabled) { // no private subnets and no public access at all, no good. throw new Error('Vpc must contain private subnets when public endpoint access is disabled'); } - if (privateSubents.length === 0 && publicAccessRestricted) { - // no private subents and public access is restricted, no good. + if (privateSubnets.length === 0 && publicAccessRestricted) { + // no private subnets and public access is restricted, no good. throw new Error('Vpc must contain private subnets when public endpoint access is restricted'); } const placeClusterHandlerInVpc = props.placeClusterHandlerInVpc ?? false; - if (placeClusterHandlerInVpc && privateSubents.length === 0) { + if (placeClusterHandlerInVpc && privateSubnets.length === 0) { throw new Error('Cannot place cluster handler in the VPC since no private subnets could be selected'); } @@ -1223,16 +1232,19 @@ export class Cluster extends ClusterBase { resources: ['secrets'], }], } : {}), + kubernetesNetworkConfig: props.serviceIpv4Cidr ? { + serviceIpv4Cidr: props.serviceIpv4Cidr, + } : undefined, endpointPrivateAccess: this.endpointAccess._config.privateAccess, endpointPublicAccess: this.endpointAccess._config.publicAccess, publicAccessCidrs: this.endpointAccess._config.publicCidrs, secretsEncryptionKey: props.secretsEncryptionKey, vpc: this.vpc, - subnets: placeClusterHandlerInVpc ? privateSubents : undefined, + subnets: placeClusterHandlerInVpc ? privateSubnets : undefined, onEventLayer: this.onEventLayer, }); - if (this.endpointAccess._config.privateAccess && privateSubents.length !== 0) { + if (this.endpointAccess._config.privateAccess && privateSubnets.length !== 0) { // when private access is enabled and the vpc has private subnets, lets connect // the provider to the vpc so that it will work even when restricting public access. @@ -1242,7 +1254,7 @@ export class Cluster extends ClusterBase { throw new Error('Private endpoint access requires the VPC to have DNS support and DNS hostnames enabled. Use `enableDnsHostnames: true` and `enableDnsSupport: true` when creating the VPC.'); } - this.kubectlPrivateSubnets = privateSubents; + this.kubectlPrivateSubnets = privateSubnets; // the vpc must exist in order to properly delete the cluster (since we run `kubectl delete`). // this ensures that. diff --git a/packages/@aws-cdk/aws-eks/test/cluster.test.ts b/packages/@aws-cdk/aws-eks/test/cluster.test.ts index 14e2bfad0746b..295092509ffd2 100644 --- a/packages/@aws-cdk/aws-eks/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-eks/test/cluster.test.ts @@ -2888,4 +2888,26 @@ describe('cluster', () => { expect(providerNestedStackTemplate?.Resources?.Handler886CB40B?.Properties?.MemorySize).toEqual(4096); }); + + test('create a cluster using custom kubernetes network config', () => { + // GIVEN + const { stack } = testFixture(); + const customCidr = '172.16.0.0/12'; + + // WHEN + new eks.Cluster(stack, 'Cluster', { + version: CLUSTER_VERSION, + serviceIpv4Cidr: customCidr, + }); + + // THEN + expect(stack).toHaveResourceLike('Custom::AWSCDK-EKS-Cluster', { + Config: { + kubernetesNetworkConfig: { + serviceIpv4Cidr: customCidr, + }, + }, + }); + + }); }); diff --git a/packages/@aws-cdk/aws-events-targets/README.md b/packages/@aws-cdk/aws-events-targets/README.md index 994c24ab8a0a5..a5c8fde1d9d98 100644 --- a/packages/@aws-cdk/aws-events-targets/README.md +++ b/packages/@aws-cdk/aws-events-targets/README.md @@ -35,7 +35,7 @@ EventBridge. ## Event retry policy and using dead-letter queues -The Codebuild, CodePipeline, Lambda, StepFunctions and LogGroup targets support attaching a [dead letter queue and setting retry policies](https://docs.aws.amazon.com/eventbridge/latest/userguide/rule-dlq.html). See the [lambda example](#invoke-a-lambda-function). +The Codebuild, CodePipeline, Lambda, StepFunctions, LogGroup and SQSQueue targets support attaching a [dead letter queue and setting retry policies](https://docs.aws.amazon.com/eventbridge/latest/userguide/rule-dlq.html). See the [lambda example](#invoke-a-lambda-function). Use [escape hatches](https://docs.aws.amazon.com/cdk/latest/guide/cfn_layer.html) for the other target types. ## Invoke a Lambda function diff --git a/packages/@aws-cdk/aws-events-targets/lib/sqs.ts b/packages/@aws-cdk/aws-events-targets/lib/sqs.ts index 43fb9b8ed15d0..465f0355516cf 100644 --- a/packages/@aws-cdk/aws-events-targets/lib/sqs.ts +++ b/packages/@aws-cdk/aws-events-targets/lib/sqs.ts @@ -1,11 +1,12 @@ import * as events from '@aws-cdk/aws-events'; import * as iam from '@aws-cdk/aws-iam'; import * as sqs from '@aws-cdk/aws-sqs'; +import { addToDeadLetterQueueResourcePolicy, TargetBaseProps, bindBaseTargetConfig } from './util'; /** * Customize the SQS Queue Event Target */ -export interface SqsQueueProps { +export interface SqsQueueProps extends TargetBaseProps { /** * Message Group ID for messages sent to this queue @@ -24,7 +25,6 @@ export interface SqsQueueProps { * @default the entire EventBridge event */ readonly message?: events.RuleTargetInput; - } /** @@ -62,7 +62,12 @@ export class SqsQueue implements events.IRuleTarget { // deduplicated automatically this.queue.grantSendMessages(new iam.ServicePrincipal('events.amazonaws.com', principalOpts)); + if (this.props.deadLetterQueue) { + addToDeadLetterQueueResourcePolicy(rule, this.props.deadLetterQueue); + } + return { + ...bindBaseTargetConfig(this.props), arn: this.queue.queueArn, input: this.props.message, targetResource: this.queue, diff --git a/packages/@aws-cdk/aws-events-targets/test/sqs/integ.sqs-event-rule-target.expected.json b/packages/@aws-cdk/aws-events-targets/test/sqs/integ.sqs-event-rule-target.expected.json index eb2a7dd26ef5f..f35a7a93b9e42 100644 --- a/packages/@aws-cdk/aws-events-targets/test/sqs/integ.sqs-event-rule-target.expected.json +++ b/packages/@aws-cdk/aws-events-targets/test/sqs/integ.sqs-event-rule-target.expected.json @@ -61,6 +61,14 @@ "Arn" ] }, + "DeadLetterConfig": { + "Arn": { + "Fn::GetAtt": [ + "MyDeadLetterQueueD997968A", + "Arn" + ] + } + }, "Id": "Target0" } ] @@ -110,6 +118,50 @@ } ] } + }, + "MyDeadLetterQueueD997968A": { + "Type": "AWS::SQS::Queue", + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "MyDeadLetterQueuePolicyCC35D52C": { + "Type": "AWS::SQS::QueuePolicy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sqs:SendMessage", + "Condition": { + "ArnEquals": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "MyRuleA44AB831", + "Arn" + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "events.amazonaws.com" + }, + "Resource": { + "Fn::GetAtt": [ + "MyDeadLetterQueueD997968A", + "Arn" + ] + }, + "Sid": "AllowEventRuleawscdksqseventtargetMyRule0027A8F4" + } + ], + "Version": "2012-10-17" + }, + "Queues": [ + { + "Ref": "MyDeadLetterQueueD997968A" + } + ] + } } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-events-targets/test/sqs/integ.sqs-event-rule-target.ts b/packages/@aws-cdk/aws-events-targets/test/sqs/integ.sqs-event-rule-target.ts index b2b8fb334bff6..f2375dd7c2a37 100644 --- a/packages/@aws-cdk/aws-events-targets/test/sqs/integ.sqs-event-rule-target.ts +++ b/packages/@aws-cdk/aws-events-targets/test/sqs/integ.sqs-event-rule-target.ts @@ -24,6 +24,10 @@ const queue = new sqs.Queue(stack, 'MyQueue', { encryptionMasterKey: key, }); -event.addTarget(new targets.SqsQueue(queue)); +const deadLetterQueue = new sqs.Queue(stack, 'MyDeadLetterQueue'); + +event.addTarget(new targets.SqsQueue(queue, { + deadLetterQueue, +})); app.synth(); diff --git a/packages/@aws-cdk/aws-events-targets/test/sqs/sqs.test.ts b/packages/@aws-cdk/aws-events-targets/test/sqs/sqs.test.ts index 8893a8820f37e..ad2a5296714e6 100644 --- a/packages/@aws-cdk/aws-events-targets/test/sqs/sqs.test.ts +++ b/packages/@aws-cdk/aws-events-targets/test/sqs/sqs.test.ts @@ -180,3 +180,75 @@ test('fifo queues are synthesized correctly', () => { ], })); }); + +test('dead letter queue is configured correctly', () => { + const stack = new Stack(); + const queue = new sqs.Queue(stack, 'MyQueue', { fifo: true }); + const deadLetterQueue = new sqs.Queue(stack, 'MyDeadLetterQueue'); + const rule = new events.Rule(stack, 'MyRule', { + schedule: events.Schedule.rate(Duration.hours(1)), + }); + + // WHEN + rule.addTarget(new targets.SqsQueue(queue, { + deadLetterQueue, + })); + + cdkExpect(stack).to(haveResource('AWS::Events::Rule', { + ScheduleExpression: 'rate(1 hour)', + State: 'ENABLED', + Targets: [ + { + Arn: { + 'Fn::GetAtt': [ + 'MyQueueE6CA6235', + 'Arn', + ], + }, + Id: 'Target0', + DeadLetterConfig: { + Arn: { + 'Fn::GetAtt': [ + 'MyDeadLetterQueueD997968A', + 'Arn', + ], + }, + }, + }, + ], + })); +}); + +test('specifying retry policy', () => { + const stack = new Stack(); + const queue = new sqs.Queue(stack, 'MyQueue', { fifo: true }); + const rule = new events.Rule(stack, 'MyRule', { + schedule: events.Schedule.rate(Duration.hours(1)), + }); + + // WHEN + rule.addTarget(new targets.SqsQueue(queue, { + retryAttempts: 2, + maxEventAge: Duration.hours(2), + })); + + cdkExpect(stack).to(haveResource('AWS::Events::Rule', { + ScheduleExpression: 'rate(1 hour)', + State: 'ENABLED', + Targets: [ + { + Arn: { + 'Fn::GetAtt': [ + 'MyQueueE6CA6235', + 'Arn', + ], + }, + Id: 'Target0', + RetryPolicy: { + MaximumEventAgeInSeconds: 7200, + MaximumRetryAttempts: 2, + }, + }, + ], + })); +}); diff --git a/packages/@aws-cdk/aws-events/lib/event-bus.ts b/packages/@aws-cdk/aws-events/lib/event-bus.ts index df0859b3d3259..bdddbc9cceb61 100644 --- a/packages/@aws-cdk/aws-events/lib/event-bus.ts +++ b/packages/@aws-cdk/aws-events/lib/event-bus.ts @@ -276,6 +276,8 @@ export class EventBus extends EventBusBase { ); } return { eventBusName: eventSourceName, eventSourceName }; + } else { + return { eventBusName: props.eventBusName }; } } return { eventBusName: defaultEventBusName }; @@ -311,7 +313,7 @@ export class EventBus extends EventBusBase { super(scope, id, { physicalName: eventBusName }); const eventBus = new CfnEventBus(this, 'Resource', { - name: eventBusName, + name: this.physicalName, eventSourceName, }); diff --git a/packages/@aws-cdk/aws-events/test/event-bus.test.ts b/packages/@aws-cdk/aws-events/test/event-bus.test.ts index b4384255ea7b4..e50f0b24db771 100644 --- a/packages/@aws-cdk/aws-events/test/event-bus.test.ts +++ b/packages/@aws-cdk/aws-events/test/event-bus.test.ts @@ -1,6 +1,6 @@ import '@aws-cdk/assert-internal/jest'; import * as iam from '@aws-cdk/aws-iam'; -import { Aws, CfnResource, Stack, Arn } from '@aws-cdk/core'; +import { Aws, CfnResource, Stack, Arn, App, PhysicalName, CfnOutput } from '@aws-cdk/core'; import { EventBus } from '../lib'; describe('event bus', () => { @@ -515,4 +515,32 @@ describe('event bus', () => { }); + test('cross account event bus uses generated physical name', () => { + // GIVEN + const app = new App(); + const stack1 = new Stack(app, 'Stack1', { + env: { + account: '11111111111', + region: 'us-east-1', + }, + }); + const stack2 = new Stack(app, 'Stack2', { + env: { + account: '22222222222', + region: 'us-east-1', + }, + }); + + // WHEN + const bus1 = new EventBus(stack1, 'Bus', { + eventBusName: PhysicalName.GENERATE_IF_NEEDED, + }); + + new CfnOutput(stack2, 'BusName', { value: bus1.eventBusName }); + + // THEN + expect(stack1).toHaveResource('AWS::Events::EventBus', { + Name: 'stack1stack1busca19bdf8ab2e51b62a5a', + }); + }); }); diff --git a/packages/@aws-cdk/aws-route53-targets/test/bucket-website-target.test.ts b/packages/@aws-cdk/aws-route53-targets/test/bucket-website-target.test.ts index eebb53b2d488a..ff1938e66447b 100644 --- a/packages/@aws-cdk/aws-route53-targets/test/bucket-website-target.test.ts +++ b/packages/@aws-cdk/aws-route53-targets/test/bucket-website-target.test.ts @@ -74,10 +74,10 @@ test('throws if region agnostic', () => { }).toThrow(/Cannot use an S3 record alias in region-agnostic stacks/); }); -test('throws if bucket website hosting is unavailable (cn-northwest-1)', () => { +test('throws if bucket website hosting is unavailable (cn-north-1)', () => { // GIVEN const app = new App(); - const stack = new Stack(app, 'test', { env: { region: 'cn-northwest-1' } }); + const stack = new Stack(app, 'test', { env: { region: 'cn-north-1' } }); const bucketWebsite = new s3.Bucket(stack, 'Bucket'); diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/README.md b/packages/@aws-cdk/aws-stepfunctions-tasks/README.md index d33d3c7ff801e..a0f47ee7519c1 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/README.md +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/README.md @@ -969,6 +969,10 @@ disable this behavior. Step Functions supports [AWS SageMaker](https://docs.aws.amazon.com/step-functions/latest/dg/connect-sagemaker.html) through the service integration pattern. +If your training job or model uses resources from AWS Marketplace, +[network isolation is required](https://docs.aws.amazon.com/sagemaker/latest/dg/mkt-algo-model-internet-free.html). +To do so, set the `enableNetworkIsolation` property to `true` for `SageMakerCreateModel` or `SageMakerCreateTrainingJob`. + ### Create Training Job You can call the [`CreateTrainingJob`](https://docs.aws.amazon.com/sagemaker/latest/dg/API_CreateTrainingJob.html) API from a `Task` state. diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/sagemaker/create-training-job.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/sagemaker/create-training-job.ts index 0aa9954e859d5..64680dc357747 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/sagemaker/create-training-job.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/sagemaker/create-training-job.ts @@ -32,6 +32,13 @@ export interface SageMakerCreateTrainingJobProps extends sfn.TaskStateBaseProps */ readonly algorithmSpecification: AlgorithmSpecification; + /** + * Isolates the training container. No inbound or outbound network calls can be made to or from the training container. + * + * @default false + */ + readonly enableNetworkIsolation?: boolean; + /** * Algorithm-specific parameters that influence the quality of the model. Set hyperparameters before you start the learning process. * For a list of hyperparameters provided by Amazon SageMaker @@ -217,6 +224,7 @@ export class SageMakerCreateTrainingJob extends sfn.TaskStateBase implements iam private renderParameters(): { [key: string]: any } { return { TrainingJobName: this.props.trainingJobName, + EnableNetworkIsolation: this.props.enableNetworkIsolation, RoleArn: this._role!.roleArn, ...this.renderAlgorithmSpecification(this.algorithmSpecification), ...this.renderInputDataConfig(this.inputDataConfig), diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/sagemaker/create-training-job.test.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/test/sagemaker/create-training-job.test.ts index 59f5a1bb36229..e16d20f94cfeb 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/sagemaker/create-training-job.test.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/sagemaker/create-training-job.test.ts @@ -142,6 +142,7 @@ test('create complex training job', () => { }, ], }, + enableNetworkIsolation: true, hyperparameters: { lr: '0.1', }, @@ -221,6 +222,7 @@ test('create complex training job', () => { { Name: 'mymetric', Regex: 'regex_pattern' }, ], }, + EnableNetworkIsolation: true, HyperParameters: { lr: '0.1', }, diff --git a/packages/@aws-cdk/cloudformation-diff/lib/diff-template.ts b/packages/@aws-cdk/cloudformation-diff/lib/diff-template.ts index b3f282802b675..6e06e56f90af1 100644 --- a/packages/@aws-cdk/cloudformation-diff/lib/diff-template.ts +++ b/packages/@aws-cdk/cloudformation-diff/lib/diff-template.ts @@ -99,13 +99,17 @@ function calculateTemplateDiff(currentTemplate: { [key: string]: any }, newTempl for (const key of unionOf(Object.keys(currentTemplate), Object.keys(newTemplate)).sort()) { const oldValue = currentTemplate[key]; const newValue = newTemplate[key]; - if (deepEqual(oldValue, newValue)) { continue; } + if (deepEqual(oldValue, newValue)) { + continue; + } const handler: DiffHandler = DIFF_HANDLERS[key] || ((_diff, oldV, newV) => unknown[key] = impl.diffUnknown(oldV, newV)); handler(differences, oldValue, newValue); } - if (Object.keys(unknown).length > 0) { differences.unknown = new types.DifferenceCollection(unknown); } + if (Object.keys(unknown).length > 0) { + differences.unknown = new types.DifferenceCollection(unknown); + } return new types.TemplateDiff(differences); } diff --git a/packages/@aws-cdk/cloudformation-diff/lib/diff/util.ts b/packages/@aws-cdk/cloudformation-diff/lib/diff/util.ts index 59c8606be0a35..1cbd4b1a111d7 100644 --- a/packages/@aws-cdk/cloudformation-diff/lib/diff/util.ts +++ b/packages/@aws-cdk/cloudformation-diff/lib/diff/util.ts @@ -138,20 +138,10 @@ export function unionOf(lv: string[] | Set, rv: string[] | Set): * A parseFloat implementation that does the right thing for * strings like '0.0.0' * (for which JavaScript's parseFloat() returns 0). + * We return NaN for all of these strings that do not represent numbers, + * and so comparing them fails, + * and doesn't short-circuit the diff logic. */ function safeParseFloat(str: string): number { - const ret = parseFloat(str); - const nonNumericRegex = /\d*\.\d+\./; - 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 if (nonNumericRegex.test(str)) { - // if the str contains non-numeric characters, - // return NaN, so we'll fall back to string comparison - return NaN; - } else { - return ret; - } + return Number(str); } 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 9241d0e8e28eb..d43ec99808d31 100644 --- a/packages/@aws-cdk/cloudformation-diff/test/diff-template.test.ts +++ b/packages/@aws-cdk/cloudformation-diff/test/diff-template.test.ts @@ -582,70 +582,57 @@ test('when a property changes including equivalent DependsOn', () => { expect(differences.resources.differenceCount).toBe(1); }); -test('when a property with a number-like format changes', () => { - const bucketName = 'ShineyBucketName'; - const tagChanges = { - '0.31.1-prod': '0.31.2-prod', - '8.0.5.5.4-identifier': '8.0.5.5.5-identifier', - '1.1.1.1': '1.1.2.2', - '1.2.3': '1.2.4', - '2.2.2.2': '2.2.3.2', - '3.3.3.3': '3.4.3.3', - }; - const oldTags = Object.keys(tagChanges); - const newTags = Object.values(tagChanges); +test.each([ + ['0.31.1-prod', '0.31.2-prod'], + ['8.0.5.5.4-identifier', '8.0.5.5.5-identifier'], + ['1.1.1.1', '1.1.1.2'], + ['1.2.3', '1.2.4'], + ['2.2.2.2', '2.2.3.2'], + ['3.3.3.3', '3.4.3.3'], + ['2021-10-23T06:07:08.000Z', '2021-10-23T09:10:11.123Z'], +])("reports a change when a string property with a number-like format changes from '%s' to '%s'", (oldValue, newValue) => { + // GIVEN const currentTemplate = { Resources: { - QueueResource: { - Type: 'AWS::SQS::Queue', - }, BucketResource: { Type: 'AWS::S3::Bucket', Properties: { - BucketName: bucketName, - Tags: oldTags, + Tags: [oldValue], }, }, }, }; const newTemplate = { Resources: { - QueueResource: { - Type: 'AWS::SQS::Queue', - }, BucketResource: { Type: 'AWS::S3::Bucket', Properties: { - BucketName: bucketName, - Tags: newTags, + Tags: [newValue], }, }, }, }; - + // WHEN const differences = diffTemplate(currentTemplate, newTemplate); + + // THEN expect(differences.differenceCount).toBe(1); expect(differences.resources.differenceCount).toBe(1); const difference = differences.resources.changes.BucketResource; expect(difference).not.toBeUndefined(); expect(difference?.oldResourceType).toEqual('AWS::S3::Bucket'); expect(difference?.propertyUpdates).toEqual({ - Tags: { oldValue: oldTags, newValue: newTags, changeImpact: ResourceImpact.WILL_UPDATE, isDifferent: true }, + Tags: { oldValue: [oldValue], newValue: [newValue], changeImpact: ResourceImpact.WILL_UPDATE, isDifferent: true }, }); }); test('when a property with a number-like format doesn\'t change', () => { - const bucketName = 'ShineyBucketName'; const tags = ['0.31.1-prod', '8.0.5.5.4-identifier', '1.1.1.1', '1.2.3']; const currentTemplate = { Resources: { - QueueResource: { - Type: 'AWS::SQS::Queue', - }, BucketResource: { Type: 'AWS::S3::Bucket', Properties: { - BucketName: bucketName, Tags: tags, }, }, @@ -653,13 +640,9 @@ test('when a property with a number-like format doesn\'t change', () => { }; const newTemplate = { Resources: { - QueueResource: { - Type: 'AWS::SQS::Queue', - }, BucketResource: { Type: 'AWS::S3::Bucket', Properties: { - BucketName: bucketName, Tags: tags, }, }, @@ -671,4 +654,4 @@ test('when a property with a number-like format doesn\'t change', () => { expect(differences.resources.differenceCount).toBe(0); const difference = differences.resources.changes.BucketResource; expect(difference).toBeUndefined(); -}); \ No newline at end of file +}); diff --git a/packages/@aws-cdk/cx-api/lib/features.ts b/packages/@aws-cdk/cx-api/lib/features.ts index 554c41929651d..986be7f4e136b 100644 --- a/packages/@aws-cdk/cx-api/lib/features.ts +++ b/packages/@aws-cdk/cx-api/lib/features.ts @@ -175,9 +175,9 @@ export const CLOUDFRONT_DEFAULT_SECURITY_POLICY_TLS_V1_2_2021 = '@aws-cdk/aws-cl */ export const FUTURE_FLAGS: { [key: string]: any } = { [APIGATEWAY_USAGEPLANKEY_ORDERINSENSITIVE_ID]: true, - [ENABLE_STACK_NAME_DUPLICATES_CONTEXT]: 'true', - [ENABLE_DIFF_NO_FAIL_CONTEXT]: 'true', - [STACK_RELATIVE_EXPORTS_CONTEXT]: 'true', + [ENABLE_STACK_NAME_DUPLICATES_CONTEXT]: true, + [ENABLE_DIFF_NO_FAIL_CONTEXT]: true, + [STACK_RELATIVE_EXPORTS_CONTEXT]: true, [DOCKER_IGNORE_SUPPORT]: true, [SECRETS_MANAGER_PARSE_OWNED_SECRET_NAME]: true, [KMS_DEFAULT_KEY_POLICIES]: true, diff --git a/packages/@aws-cdk/pipelines/lib/legacy/actions/deploy-cdk-stack-action.ts b/packages/@aws-cdk/pipelines/lib/legacy/actions/deploy-cdk-stack-action.ts index af6b7821a308d..1fc92da472ea1 100644 --- a/packages/@aws-cdk/pipelines/lib/legacy/actions/deploy-cdk-stack-action.ts +++ b/packages/@aws-cdk/pipelines/lib/legacy/actions/deploy-cdk-stack-action.ts @@ -16,6 +16,8 @@ import { Construct as CoreConstruct } from '@aws-cdk/core'; /** * Customization options for a DeployCdkStackAction + * + * @deprecated This class is part of the old API. Use the API based on the `CodePipeline` class instead */ export interface DeployCdkStackActionOptions { /** @@ -68,6 +70,8 @@ export interface DeployCdkStackActionOptions { /** * Properties for a DeployCdkStackAction + * + * @deprecated This class is part of the old API. Use the API based on the `CodePipeline` class instead */ export interface DeployCdkStackActionProps extends DeployCdkStackActionOptions { /** @@ -129,6 +133,8 @@ export interface DeployCdkStackActionProps extends DeployCdkStackActionOptions { /** * Options for the 'fromStackArtifact' operation + * + * @deprecated This class is part of the old API. Use the API based on the `CodePipeline` class instead */ export interface CdkStackActionFromArtifactOptions extends DeployCdkStackActionOptions { /** @@ -147,6 +153,8 @@ export interface CdkStackActionFromArtifactOptions extends DeployCdkStackActionO * * You do not need to instantiate this action yourself -- it will automatically * be added by the pipeline when you add stack artifacts or entire stages. + * + * @deprecated This class is part of the old API. Use the API based on the `CodePipeline` class instead */ export class DeployCdkStackAction implements codepipeline.IAction { /** @@ -317,6 +325,8 @@ function roleFromPlaceholderArn(scope: Construct, region: string | undefined, /** * Options for CdkDeployAction.fromStackArtifact + * + * @deprecated This class is part of the old API. Use the API based on the `CodePipeline` class instead */ export interface FromStackArtifactOptions { /** diff --git a/packages/@aws-cdk/pipelines/lib/legacy/actions/publish-assets-action.ts b/packages/@aws-cdk/pipelines/lib/legacy/actions/publish-assets-action.ts index 1c112aec9afbe..055744cb971c2 100644 --- a/packages/@aws-cdk/pipelines/lib/legacy/actions/publish-assets-action.ts +++ b/packages/@aws-cdk/pipelines/lib/legacy/actions/publish-assets-action.ts @@ -17,6 +17,8 @@ import { Construct as CoreConstruct } from '@aws-cdk/core'; /** * Props for a PublishAssetsAction + * + * @deprecated This class is part of the old API. Use the API based on the `CodePipeline` class instead */ export interface PublishAssetsActionProps { /** @@ -111,6 +113,8 @@ export interface PublishAssetsActionProps { * * You do not need to instantiate this action -- it will automatically * be added by the pipeline when you add stacks that use assets. + * + * @deprecated This class is part of the old API. Use the API based on the `CodePipeline` class instead */ export class PublishAssetsAction extends CoreConstruct implements codepipeline.IAction { private readonly action: codepipeline.IAction; diff --git a/packages/@aws-cdk/pipelines/lib/legacy/actions/update-pipeline-action.ts b/packages/@aws-cdk/pipelines/lib/legacy/actions/update-pipeline-action.ts index bc50e80c0aa56..da4278dd6faf2 100644 --- a/packages/@aws-cdk/pipelines/lib/legacy/actions/update-pipeline-action.ts +++ b/packages/@aws-cdk/pipelines/lib/legacy/actions/update-pipeline-action.ts @@ -14,6 +14,8 @@ import { Construct as CoreConstruct } from '@aws-cdk/core'; /** * Props for the UpdatePipelineAction + * + * @deprecated This class is part of the old API. Use the API based on the `CodePipeline` class instead */ export interface UpdatePipelineActionProps { /** @@ -79,6 +81,8 @@ export interface UpdatePipelineActionProps { * * You do not need to instantiate this action -- it will automatically * be added by the pipeline. + * + * @deprecated This class is part of the old API. Use the API based on the `CodePipeline` class instead */ export class UpdatePipelineAction extends CoreConstruct implements codepipeline.IAction { private readonly action: codepipeline.IAction; diff --git a/packages/@aws-cdk/pipelines/lib/legacy/pipeline.ts b/packages/@aws-cdk/pipelines/lib/legacy/pipeline.ts index 60f158f298fce..ef0d1d209619b 100644 --- a/packages/@aws-cdk/pipelines/lib/legacy/pipeline.ts +++ b/packages/@aws-cdk/pipelines/lib/legacy/pipeline.ts @@ -21,6 +21,8 @@ import { Construct as CoreConstruct } from '@aws-cdk/core'; const CODE_BUILD_LENGTH_LIMIT = 100; /** * Properties for a CdkPipeline + * + * @deprecated This class is part of the old API. Use the API based on the `CodePipeline` class instead */ export interface CdkPipelineProps { /** @@ -49,7 +51,7 @@ export interface CdkPipelineProps { * You can choose to not pass this value, in which case a new CodePipeline is * created with default settings. * - * If you pass an existing CodePipeline, it should should have been created + * If you pass an existing CodePipeline, it should have been created * with `restartExecutionOnUpdate: true`. * * [disable-awslint:ref-via-interface] @@ -206,6 +208,8 @@ export interface CdkPipelineProps { * - Asset publishing. * - Keeping the pipeline up-to-date as the CDK apps change. * - Using stack outputs later on in the pipeline. + * + * @deprecated This class is part of the old API. Use the API based on the `CodePipeline` class instead */ export class CdkPipeline extends CoreConstruct { private readonly _pipeline: codepipeline.Pipeline; diff --git a/packages/@aws-cdk/pipelines/lib/legacy/stage.ts b/packages/@aws-cdk/pipelines/lib/legacy/stage.ts index bfb997e908196..c054e9a0592fb 100644 --- a/packages/@aws-cdk/pipelines/lib/legacy/stage.ts +++ b/packages/@aws-cdk/pipelines/lib/legacy/stage.ts @@ -20,6 +20,8 @@ import { Construct as CoreConstruct } from '@aws-cdk/core'; /** * Construction properties for a CdkStage + * + * @deprecated This class is part of the old API. Use the API based on the `CodePipeline` class instead */ export interface CdkStageProps { /** @@ -70,6 +72,8 @@ export interface CdkStageProps { * * You don't need to instantiate this class directly. Use * `cdkPipeline.addStage()` instead. + * + * @deprecated This class is part of the old API. Use the API based on the `CodePipeline` class instead */ export class CdkStage extends CoreConstruct { private _nextSequentialRunOrder = 1; // Must start at 1 eh @@ -396,6 +400,8 @@ export class CdkStage extends CoreConstruct { /** * Additional options for adding a stack deployment + * + * @deprecated This class is part of the old API. Use the API based on the `CodePipeline` class instead */ export interface AddStackOptions { /** @@ -415,6 +421,8 @@ export interface AddStackOptions { /** * A single output of a Stack + * + * @deprecated This class is part of the old API. Use the API based on the `CodePipeline` class instead */ export class StackOutput { /** @@ -448,6 +456,8 @@ function isAssetManifest(s: cxapi.CloudArtifact): s is cxapi.AssetManifestArtifa /** * Features that the Stage needs from its environment + * + * @deprecated This class is part of the old API. Use the API based on the `CodePipeline` class instead */ export interface IStageHost { /** @@ -463,6 +473,8 @@ export interface IStageHost { /** * Instructions to publish certain assets + * + * @deprecated This class is part of the old API. Use the API based on the `CodePipeline` class instead */ export interface AssetPublishingCommand { /** @@ -493,6 +505,8 @@ export interface AssetPublishingCommand { /** * Base options for a pipelines stage + * + * @deprecated This class is part of the old API. Use the API based on the `CodePipeline` class instead */ export interface BaseStageOptions { /** @@ -522,6 +536,8 @@ export interface BaseStageOptions { /** * Options for adding an application stage to a pipeline + * + * @deprecated This class is part of the old API. Use the API based on the `CodePipeline` class instead */ export interface AddStageOptions extends BaseStageOptions { /** @@ -546,6 +562,8 @@ export interface AddStageOptions extends BaseStageOptions { /** * Options for addManualApproval + * + * @deprecated This class is part of the old API. Use the API based on the `CodePipeline` class instead */ export interface AddManualApprovalOptions { /** diff --git a/packages/@aws-cdk/pipelines/lib/legacy/synths/simple-synth-action.ts b/packages/@aws-cdk/pipelines/lib/legacy/synths/simple-synth-action.ts index 1fd0b96bb2db6..226a75d2ed23c 100644 --- a/packages/@aws-cdk/pipelines/lib/legacy/synths/simple-synth-action.ts +++ b/packages/@aws-cdk/pipelines/lib/legacy/synths/simple-synth-action.ts @@ -19,6 +19,8 @@ import { Construct } from '@aws-cdk/core'; /** * Configuration options for a SimpleSynth + * + * @deprecated This class is part of the old API. Use the API based on the `CodePipeline` class instead */ export interface SimpleSynthOptions { /** @@ -128,6 +130,8 @@ export interface SimpleSynthOptions { /** * Construction props for SimpleSynthAction + * + * @deprecated This class is part of the old API. Use the API based on the `CodePipeline` class instead */ export interface SimpleSynthActionProps extends SimpleSynthOptions { /** @@ -192,6 +196,8 @@ export interface SimpleSynthActionProps extends SimpleSynthOptions { /** * Specification of an additional artifact to generate + * + * @deprecated This class is part of the old API. Use the API based on the `CodePipeline` class instead */ export interface AdditionalArtifact { /** @@ -207,6 +213,8 @@ export interface AdditionalArtifact { /** * A standard synth with a generated buildspec + * + * @deprecated This class is part of the old API. Use the API based on the `CodePipeline` class instead */ export class SimpleSynthAction implements codepipeline.IAction, iam.IGrantable { @@ -479,6 +487,8 @@ export class SimpleSynthAction implements codepipeline.IAction, iam.IGrantable { /** * Options for a convention-based synth using NPM + * + * @deprecated This class is part of the old API. Use the API based on the `CodePipeline` class instead */ export interface StandardNpmSynthOptions extends SimpleSynthOptions { /** @@ -520,6 +530,8 @@ export interface StandardNpmSynthOptions extends SimpleSynthOptions { /** * Options for a convention-based synth using Yarn + * + * @deprecated This class is part of the old API. Use the API based on the `CodePipeline` class instead */ export interface StandardYarnSynthOptions extends SimpleSynthOptions { /** diff --git a/packages/@aws-cdk/pipelines/lib/legacy/validation/shell-script-action.ts b/packages/@aws-cdk/pipelines/lib/legacy/validation/shell-script-action.ts index 436f099d3c67d..78f223919cafd 100644 --- a/packages/@aws-cdk/pipelines/lib/legacy/validation/shell-script-action.ts +++ b/packages/@aws-cdk/pipelines/lib/legacy/validation/shell-script-action.ts @@ -13,6 +13,8 @@ import { Construct } from '@aws-cdk/core'; /** * Properties for ShellScriptAction + * + * @deprecated This class is part of the old API. Use the API based on the `CodePipeline` class instead */ export interface ShellScriptActionProps { /** @@ -122,6 +124,8 @@ export interface ShellScriptActionProps { /** * Validate a revision using shell commands + * + * @deprecated This class is part of the old API. Use the API based on the `CodePipeline` class instead */ export class ShellScriptAction implements codepipeline.IAction, iam.IGrantable { private _project?: codebuild.IProject; diff --git a/packages/@aws-cdk/region-info/build-tools/fact-tables.ts b/packages/@aws-cdk/region-info/build-tools/fact-tables.ts index 28d61f7fd9387..0e1f923dc2a02 100644 --- a/packages/@aws-cdk/region-info/build-tools/fact-tables.ts +++ b/packages/@aws-cdk/region-info/build-tools/fact-tables.ts @@ -43,7 +43,7 @@ export const AWS_CDK_METADATA = new Set([ /** * The hosted zone Id if using an alias record in Route53. * - * @see https://docs.aws.amazon.com/general/latest/gr/rande.html#s3_website_region_endpoints + * @see https://docs.aws.amazon.com/general/latest/gr/s3.html#s3_region */ export const ROUTE_53_BUCKET_WEBSITE_ZONE_IDS: { [region: string]: string } = { 'af-south-1': 'Z11KHD8FBVPUYU', @@ -55,6 +55,7 @@ export const ROUTE_53_BUCKET_WEBSITE_ZONE_IDS: { [region: string]: string } = { 'ap-southeast-1': 'Z3O0J2DXBE1FTB', 'ap-southeast-2': 'Z1WCIGYICN2BYD', 'ca-central-1': 'Z1QDHH18159H29', + 'cn-northwest-1': 'Z282HJ1KT0DH03', 'eu-central-1': 'Z21DNDUVLTQW6Q', 'eu-north-1': 'Z3BAZG2TWCNX0D', 'eu-south-1': 'Z3IXVV8C73GIO3', @@ -135,6 +136,7 @@ export const DLC_REPOSITORY_ACCOUNTS: { [region: string]: string } = { }; // https://docs.aws.amazon.com/app-mesh/latest/userguide/envoy.html +// https://docs.amazonaws.cn/app-mesh/latest/userguide/envoy.html export const APPMESH_ECR_ACCOUNTS: { [region: string]: string } = { 'af-south-1': '924023996002', 'ap-east-1': '856666278305', @@ -145,6 +147,8 @@ export const APPMESH_ECR_ACCOUNTS: { [region: string]: string } = { 'ap-southeast-1': '840364872350', 'ap-southeast-2': '840364872350', 'ca-central-1': '840364872350', + 'cn-north-1': '919366029133', + 'cn-northwest-1': '919830735681', 'eu-central-1': '840364872350', 'eu-north-1': '840364872350', 'eu-south-1': '422531588944', diff --git a/tools/@aws-cdk/pkglint/lib/rules.ts b/tools/@aws-cdk/pkglint/lib/rules.ts index f991f7b0a2441..e18fcb1086fdb 100644 --- a/tools/@aws-cdk/pkglint/lib/rules.ts +++ b/tools/@aws-cdk/pkglint/lib/rules.ts @@ -1129,7 +1129,11 @@ export class MustHaveNodeEnginesDeclaration extends ValidationRule { public readonly name = 'package-info/engines'; public validate(pkg: PackageJson): void { - expectJSON(this.name, pkg, 'engines.node', '>= 10.13.0 <13 || >=13.7.0'); + if (cdkMajorVersion() === 2) { + expectJSON(this.name, pkg, 'engines.node', '>= 14.15.0'); + } else { + expectJSON(this.name, pkg, 'engines.node', '>= 10.13.0 <13 || >=13.7.0'); + } } }