diff --git a/.gitallowed b/.gitallowed index 20a94b7dae003..6ac984df8735b 100644 --- a/.gitallowed +++ b/.gitallowed @@ -14,7 +14,11 @@ account: '012345678913' # Account patterns used in the CHANGELOG account: '123456789012' + +111111111111 +222222222222 123456789012 +333333333333 # The account ID's of public facing ECR images for App Mesh Envoy # https://docs.aws.amazon.com/app-mesh/latest/userguide/envoy.html diff --git a/.github/workflows/close-stale-prs.yml b/.github/workflows/close-stale-prs.yml index 5fef3433ff06e..d8543bc1725df 100644 --- a/.github/workflows/close-stale-prs.yml +++ b/.github/workflows/close-stale-prs.yml @@ -2,7 +2,7 @@ on: schedule: # Cron format: min hr day month dow - cron: "0 0 * * *" - workflow_dispatch: + workflow_dispatch: jobs: close-stale-prs: permissions: @@ -23,5 +23,5 @@ jobs: important-checks-regex: AutoBuildv2Project1C6BFA3F warn-message: This PR has been in the STATE state for 3 weeks, and looks abandoned. To keep this PR from being closed, please continue work on it. If not, it will automatically be closed in a week. close-message: This PR has been deemed to be abandoned, and will be automatically closed. Please create a new PR for these changes if you think this decision has been made in error. - skip-labels: contribution/core + skip-labels: contribution/core, pr-linter/do-not-close close-label: closed-for-staleness diff --git a/CHANGELOG.v2.alpha.md b/CHANGELOG.v2.alpha.md index 17ed885b301e6..bb662176a330f 100644 --- a/CHANGELOG.v2.alpha.md +++ b/CHANGELOG.v2.alpha.md @@ -2,6 +2,23 @@ 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. +## [2.64.0-alpha.0](https://github.com/aws/aws-cdk/compare/v2.63.2-alpha.0...v2.64.0-alpha.0) (2023-02-09) + + +### Features + +* **cloud9:** support setting environment owner ([#23878](https://github.com/aws/aws-cdk/issues/23878)) ([08a2f36](https://github.com/aws/aws-cdk/commit/08a2f363093f39d04026778bb8d5d7f673698b57)), closes [#22474](https://github.com/aws/aws-cdk/issues/22474) +* **redshift:** Tables can include comments ([#23847](https://github.com/aws/aws-cdk/issues/23847)) ([46cadd4](https://github.com/aws/aws-cdk/commit/46cadd4b2dd417e1484ba63389b33e1504cfd842)), closes [#22682](https://github.com/aws/aws-cdk/issues/22682) + + +### Bug Fixes + +* **servicecatalogappregistry:** default stack name is not meaningful and causes conflict when multiple stacks deployed to the same account-region ([#23823](https://github.com/aws/aws-cdk/issues/23823)) ([420b5ff](https://github.com/aws/aws-cdk/commit/420b5ff2bd08311f2c8cabbe0787c0e0bf4f8ae3)) + +## [2.63.2-alpha.0](https://github.com/aws/aws-cdk/compare/v2.63.1-alpha.0...v2.63.2-alpha.0) (2023-02-04) + +## [2.63.1-alpha.0](https://github.com/aws/aws-cdk/compare/v2.63.0-alpha.0...v2.63.1-alpha.0) (2023-02-03) + ## [2.63.0-alpha.0](https://github.com/aws/aws-cdk/compare/v2.62.2-alpha.0...v2.63.0-alpha.0) (2023-01-31) diff --git a/CHANGELOG.v2.md b/CHANGELOG.v2.md index 44c0548929370..d78c292fd363f 100644 --- a/CHANGELOG.v2.md +++ b/CHANGELOG.v2.md @@ -2,6 +2,41 @@ 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. +## [2.64.0](https://github.com/aws/aws-cdk/compare/v2.63.2...v2.64.0) (2023-02-09) + + +### Features + +* **cfnspec:** cloudformation spec v109.0.0 ([#23968](https://github.com/aws/aws-cdk/issues/23968)) ([5d59134](https://github.com/aws/aws-cdk/commit/5d5913455da2cdb834feef708fb01f9e77df656f)) +* **cfnspec:** cloudformation spec v109.0.0 ([#23984](https://github.com/aws/aws-cdk/issues/23984)) ([affe040](https://github.com/aws/aws-cdk/commit/affe040c8443be074822254d1e75a28b264cd801)) +* **cli:** --hotswap will not use CFN anymore, --hotswap-fallback to fall back if necessary ([#23653](https://github.com/aws/aws-cdk/issues/23653)) ([a5317ca](https://github.com/aws/aws-cdk/commit/a5317ca52f05ebc34d9f22196ab0ef36d5cac967)), closes [#22784](https://github.com/aws/aws-cdk/issues/22784) [#21773](https://github.com/aws/aws-cdk/issues/21773) [#21556](https://github.com/aws/aws-cdk/issues/21556) [#23640](https://github.com/aws/aws-cdk/issues/23640) +* **elbv2:** add metrics to INetworkLoadBalancer and IApplicationLoadBalancer ([#23853](https://github.com/aws/aws-cdk/issues/23853)) ([cb889bc](https://github.com/aws/aws-cdk/commit/cb889bc2c267654ca97e3d85a16a99a667d3584c)), closes [#10850](https://github.com/aws/aws-cdk/issues/10850) +* **iam:** implement IGrantable to Policy and ManagedPolicy ([#22712](https://github.com/aws/aws-cdk/issues/22712)) ([d3df40f](https://github.com/aws/aws-cdk/commit/d3df40ff89c70b9243ec175747eb398368067095)), closes [#10308](https://github.com/aws/aws-cdk/issues/10308) +* **lambda:** enable RuntimeManagementConfig ([#23891](https://github.com/aws/aws-cdk/issues/23891)) ([be4f971](https://github.com/aws/aws-cdk/commit/be4f97129f4237b39d0b99977eb597e2af49ed2a)), closes [#23890](https://github.com/aws/aws-cdk/issues/23890) +* **s3:** allow configuring S3 Object Lock ([#23744](https://github.com/aws/aws-cdk/issues/23744)) ([bdcd6c8](https://github.com/aws/aws-cdk/commit/bdcd6c890878fb71c480bf40964f1b6ea0a5f270)), closes [#5247](https://github.com/aws/aws-cdk/issues/5247) [#21738](https://github.com/aws/aws-cdk/issues/21738) + + +### Bug Fixes + +* Use the correct LB full name when creating metrics for imported LBs ([#23972](https://github.com/aws/aws-cdk/issues/23972)) ([16c23b7](https://github.com/aws/aws-cdk/commit/16c23b7554923bf6c2703ba5f229e6c34b459a2f)), closes [#23853](https://github.com/aws/aws-cdk/issues/23853) +* **cdk-assets:** asset concurrency leaves a corrupted archive ([#24026](https://github.com/aws/aws-cdk/issues/24026)) ([989454f](https://github.com/aws/aws-cdk/commit/989454f7e27f3cbf33180d8aab29d56472378126)) +* **cdk-assets:** packaging assets is broken on Node older than 14.17 ([#23994](https://github.com/aws/aws-cdk/issues/23994)) ([5bde92c](https://github.com/aws/aws-cdk/commit/5bde92c2ae29781aafd8c3817d08e93748c39885)), closes [#23859](https://github.com/aws/aws-cdk/issues/23859) +* **codedeploy:** cross-region referenced groups use wrong config ([#23986](https://github.com/aws/aws-cdk/issues/23986)) ([390ec78](https://github.com/aws/aws-cdk/commit/390ec78437a55ad68757f8ce812535e9bc149a2a)) +* **core:** cross-stack reference error doesn't include violation ([#23987](https://github.com/aws/aws-cdk/issues/23987)) ([c7ad66f](https://github.com/aws/aws-cdk/commit/c7ad66fad6ca5aff5f2ae9754d263dea9d1de368)) +* **ec2:** Cannot deploy VPC flow log with other resources that requires bucket policies ([#23889](https://github.com/aws/aws-cdk/issues/23889)) ([e646ad5](https://github.com/aws/aws-cdk/commit/e646ad5b5496b176549f8c039a5ffabbf07403ff)), closes [#18985](https://github.com/aws/aws-cdk/issues/18985) +* **pipelines:** cannot configure actionName for all sources ([#24027](https://github.com/aws/aws-cdk/issues/24027)) ([9cd639b](https://github.com/aws/aws-cdk/commit/9cd639b0f83e65fbe531d56210f68e99874f506e)) +* **s3:** infer bucketWebsiteUrl and bucketDomainName suffixes from bucket region ([#23919](https://github.com/aws/aws-cdk/issues/23919)) ([252f052](https://github.com/aws/aws-cdk/commit/252f052d4239b320ac542c7db256683425ad7eba)) +* **s3-deployment:** wrong URL in BucketDeployment.deployedBucket.bucketWebsiteUrl ([#24055](https://github.com/aws/aws-cdk/issues/24055)) ([ece46db](https://github.com/aws/aws-cdk/commit/ece46dbd939383f240023172a491767b51eaa722)), closes [#23354](https://github.com/aws/aws-cdk/issues/23354) + +## [2.63.2](https://github.com/aws/aws-cdk/compare/v2.63.1...v2.63.2) (2023-02-04) + +## [2.63.1](https://github.com/aws/aws-cdk/compare/v2.63.0...v2.63.1) (2023-02-03) + + +### Reverts + +* **cdk-assets:** packaging assets is broken on Node older than 14.17 ([#23994](https://github.com/aws/aws-cdk/issues/23994)) ([1976f1a](https://github.com/aws/aws-cdk/commit/1976f1a7f585b1adb582c5cb557b96ed38418fca)), closes [#23859](https://github.com/aws/aws-cdk/issues/23859) + ## [2.63.0](https://github.com/aws/aws-cdk/compare/v2.62.2...v2.63.0) (2023-01-31) diff --git a/package.json b/package.json index 5ba300b3ae8cb..41b1578b5f8f6 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "devDependencies": { "@types/prettier": "2.6.0", "@yarnpkg/lockfile": "^1.1.0", - "cdk-generate-synthetic-examples": "^0.1.138", + "cdk-generate-synthetic-examples": "^0.1.140", "conventional-changelog-cli": "^2.2.2", "fs-extra": "^9.1.0", "graceful-fs": "^4.2.10", diff --git a/packages/@aws-cdk/aws-cloud9/README.md b/packages/@aws-cdk/aws-cloud9/README.md index 84b00eb03218e..f87860ae71b3d 100644 --- a/packages/@aws-cdk/aws-cloud9/README.md +++ b/packages/@aws-cdk/aws-cloud9/README.md @@ -23,19 +23,19 @@ This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project. -AWS Cloud9 is a cloud-based integrated development environment (IDE) that lets you write, run, and debug your code with just a -browser. It includes a code editor, debugger, and terminal. Cloud9 comes prepackaged with essential tools for popular -programming languages, including JavaScript, Python, PHP, and more, so you don’t need to install files or configure your -development machine to start new projects. Since your Cloud9 IDE is cloud-based, you can work on your projects from your -office, home, or anywhere using an internet-connected machine. Cloud9 also provides a seamless experience for developing -serverless applications enabling you to easily define resources, debug, and switch between local and remote execution of -serverless applications. With Cloud9, you can quickly share your development environment with your team, enabling you to pair +AWS Cloud9 is a cloud-based integrated development environment (IDE) that lets you write, run, and debug your code with just a +browser. It includes a code editor, debugger, and terminal. Cloud9 comes prepackaged with essential tools for popular +programming languages, including JavaScript, Python, PHP, and more, so you don’t need to install files or configure your +development machine to start new projects. Since your Cloud9 IDE is cloud-based, you can work on your projects from your +office, home, or anywhere using an internet-connected machine. Cloud9 also provides a seamless experience for developing +serverless applications enabling you to easily define resources, debug, and switch between local and remote execution of +serverless applications. With Cloud9, you can quickly share your development environment with your team, enabling you to pair program and track each other's inputs in real time. ## Creating EC2 Environment -EC2 Environments are defined with `Ec2Environment`. To create an EC2 environment in the private subnet, specify +EC2 Environments are defined with `Ec2Environment`. To create an EC2 environment in the private subnet, specify `subnetSelection` with private `subnetType`. @@ -52,7 +52,7 @@ new cloud9.Ec2Environment(this, 'Cloud9Env2', { imageId: cloud9.ImageId.AMAZON_LINUX_2, }); -// or specify in a different subnetSelection +// or specify in a different subnetSelection const c9env = new cloud9.Ec2Environment(this, 'Cloud9Env3', { vpc, subnetSelection: { @@ -104,3 +104,39 @@ new cloud9.Ec2Environment(this, 'C9Env', { imageId: cloud9.ImageId.AMAZON_LINUX_2, }); ``` + +## Specifying Owners + +Every Cloud9 Environment has an **owner**. An owner has full control over the environment, and can invite additional members to the environment for collaboration purposes. For more information, see [Working with shared environments in AWS Cloud9](https://docs.aws.amazon.com/cloud9/latest/user-guide/share-environment.html)). + +By default, the owner will be the identity that creates the Environment, which is most likely your CloudFormation Execution Role when the Environment is created using CloudFormation. Provider a value for the `owner` property to assign a different owner, either a specific IAM User or the AWS Account Root User. + +`Owner` is a user that owns a Cloud9 environment . `Owner` has their own access permissions, resources. And we can specify an `Owner`in an Ec2 environment which could be of two types, 1. AccountRoot and 2. Iam User. It allows AWS to determine who has permissions to manage the environment, either an IAM user or the account root user (but using the account root user is not recommended, see [environment sharing best practices](https://docs.aws.amazon.com/cloud9/latest/user-guide/share-environment.html#share-environment-best-practices)). + +To specify the AWS Account Root User as the environment owner, use `Owner.accountRoot()` + +```ts +declare const vpc: ec2.Vpc; +new cloud9.Ec2Environment(this, 'C9Env', { + vpc, + imageId: cloud9.ImageId.AMAZON_LINUX_2, + + owner: cloud9.Owner.accountRoot('111111111') +}) +``` + +To specify a specific IAM User as the environment owner, use `Owner.user()`. The user should have the `AWSCloud9Administrator` managed policy + +```ts +import * as iam from '@aws-cdk/aws-iam'; + +const user = new iam.User(this, 'user'); +user.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('AWSCloud9Administrator')); +declare const vpc: ec2.Vpc; +new cloud9.Ec2Environment(this, 'C9Env', { + vpc, + imageId: cloud9.ImageId.AMAZON_LINUX_2, + + owner: cloud9.Owner.user(user) +}) +``` diff --git a/packages/@aws-cdk/aws-cloud9/lib/environment.ts b/packages/@aws-cdk/aws-cloud9/lib/environment.ts index 15d7390dd6ee5..d1e4565f786ff 100644 --- a/packages/@aws-cdk/aws-cloud9/lib/environment.ts +++ b/packages/@aws-cdk/aws-cloud9/lib/environment.ts @@ -1,5 +1,6 @@ import * as codecommit from '@aws-cdk/aws-codecommit'; import * as ec2 from '@aws-cdk/aws-ec2'; +import { IUser } from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnEnvironmentEC2 } from '../lib/cloud9.generated'; @@ -53,11 +54,19 @@ export enum ImageId { */ UBUNTU_18_04 = 'ubuntu-18.04-x86_64' } - /** * Properties for Ec2Environment */ export interface Ec2EnvironmentProps { + /** + * Owner of the environment. + * + * The owner has full control of the environment and can invite additional members. + * + * @default - The identity that CloudFormation executes under will be the owner + */ + readonly owner?: Owner; + /** * The type of instance to connect to the environment. * @@ -182,6 +191,7 @@ export class Ec2Environment extends cdk.Resource implements IEc2Environment { const c9env = new CfnEnvironmentEC2(this, 'Resource', { name: props.ec2EnvironmentName, description: props.description, + ownerArn: props.owner?.ownerArn, instanceType: props.instanceType?.toString() ?? ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.MICRO).toString(), subnetId: this.vpc.selectSubnets(vpcSubnets).subnetIds[0], repositories: props.clonedRepositories ? props.clonedRepositories.map(r => ({ @@ -217,3 +227,38 @@ export class CloneRepository { private constructor(public readonly repositoryUrl: string, public readonly pathComponent: string) {} } + +/** + * An environment owner + * + * + */ +export class Owner { + /** + * Make an IAM user the environment owner + * + * User need to have AWSCloud9Administrator permissions + * @see https://docs.aws.amazon.com/cloud9/latest/user-guide/share-environment.html#share-environment-about + * + * @param user the User object to use as the environment owner + */ + public static user(user: IUser): Owner { + return { ownerArn: user.userArn }; + } + + + /** + * Make the Account Root User the environment owner (not recommended) + * + * @param accountId the AccountId to use as the environment owner. + */ + public static accountRoot(accountId: string): Owner { + return { ownerArn: `arn:aws:iam::${accountId}:root` }; + } + + /** + * + * @param ownerArn of environment owner. + */ + private constructor(public readonly ownerArn: string) {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloud9/package.json b/packages/@aws-cdk/aws-cloud9/package.json index c52e71087c1e2..b76154609d311 100644 --- a/packages/@aws-cdk/aws-cloud9/package.json +++ b/packages/@aws-cdk/aws-cloud9/package.json @@ -92,6 +92,7 @@ "dependencies": { "@aws-cdk/aws-codecommit": "0.0.0", "@aws-cdk/aws-ec2": "0.0.0", + "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/core": "0.0.0", "constructs": "^10.0.0" }, @@ -99,6 +100,7 @@ "peerDependencies": { "@aws-cdk/aws-codecommit": "0.0.0", "@aws-cdk/aws-ec2": "0.0.0", + "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/core": "0.0.0", "constructs": "^10.0.0" }, diff --git a/packages/@aws-cdk/aws-cloud9/test/cloud9.environment.test.ts b/packages/@aws-cdk/aws-cloud9/test/cloud9.environment.test.ts index 948cfa5bee9ec..69210bf01a135 100644 --- a/packages/@aws-cdk/aws-cloud9/test/cloud9.environment.test.ts +++ b/packages/@aws-cdk/aws-cloud9/test/cloud9.environment.test.ts @@ -1,9 +1,10 @@ import { Match, Template } from '@aws-cdk/assertions'; import * as codecommit from '@aws-cdk/aws-codecommit'; import * as ec2 from '@aws-cdk/aws-ec2'; +import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; import * as cloud9 from '../lib'; -import { ConnectionType, ImageId } from '../lib'; +import { ConnectionType, ImageId, Owner } from '../lib'; let stack: cdk.Stack; let vpc: ec2.IVpc; @@ -79,7 +80,6 @@ test('throw error when subnetSelection not specified and the provided VPC has no test('can use CodeCommit repositories', () => { // WHEN const repo = codecommit.Repository.fromRepositoryName(stack, 'Repo', 'foo'); - new cloud9.Ec2Environment(stack, 'C9Env', { vpc, clonedRepositories: [ @@ -114,6 +114,37 @@ test('can use CodeCommit repositories', () => { }); }); +test('environment owner can be an IAM user', () => { + // WHEN + const user = new iam.User(stack, 'User', { + userName: 'testUser', + }); + new cloud9.Ec2Environment(stack, 'C9Env', { + vpc, + imageId: cloud9.ImageId.AMAZON_LINUX_2, + owner: Owner.user(user), + }); + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Cloud9::EnvironmentEC2', { + OwnerArn: { + 'Fn::GetAtt': ['User00B015A1', 'Arn'], + }, + }); +}); + +test('environment owner can be account root', () => { + // WHEN + new cloud9.Ec2Environment(stack, 'C9Env', { + vpc, + imageId: cloud9.ImageId.AMAZON_LINUX_2, + owner: Owner.accountRoot('12345678'), + }); + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Cloud9::EnvironmentEC2', { + OwnerArn: 'arn:aws:iam::12345678:root', + }); +}); + test.each([ [ConnectionType.CONNECT_SSH, 'CONNECT_SSH'], [ConnectionType.CONNECT_SSM, 'CONNECT_SSM'], diff --git a/packages/@aws-cdk/aws-codedeploy/lib/ecs/deployment-group.ts b/packages/@aws-cdk/aws-codedeploy/lib/ecs/deployment-group.ts index 40ddcfb31069e..7b549e79d173c 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/ecs/deployment-group.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/ecs/deployment-group.ts @@ -222,7 +222,7 @@ export class EcsDeploymentGroup extends DeploymentGroupBase implements IEcsDeplo this.alarms = props.alarms || []; this.role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('AWSCodeDeployRoleForECS')); - this.deploymentConfig = props.deploymentConfig || EcsDeploymentConfig.ALL_AT_ONCE; + this.deploymentConfig = this._bindDeploymentConfig(props.deploymentConfig || EcsDeploymentConfig.ALL_AT_ONCE); if (cdk.Resource.isOwnedResource(props.service)) { const cfnSvc = (props.service as ecs.BaseService).node.defaultChild as ecs.CfnService; @@ -358,6 +358,6 @@ class ImportedEcsDeploymentGroup extends ImportedDeploymentGroupBase implements }); this.application = props.application; - this.deploymentConfig = props.deploymentConfig || EcsDeploymentConfig.ALL_AT_ONCE; + this.deploymentConfig = this._bindDeploymentConfig(props.deploymentConfig || EcsDeploymentConfig.ALL_AT_ONCE); } } diff --git a/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-group.ts b/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-group.ts index 85110a037e6ea..ea61370f9a140 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-group.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-group.ts @@ -162,7 +162,7 @@ export class LambdaDeploymentGroup extends DeploymentGroupBase implements ILambd this.alarms = props.alarms || []; this.role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSCodeDeployRoleForLambdaLimited')); - this.deploymentConfig = props.deploymentConfig || LambdaDeploymentConfig.CANARY_10PERCENT_5MINUTES; + this.deploymentConfig = this._bindDeploymentConfig(props.deploymentConfig || LambdaDeploymentConfig.CANARY_10PERCENT_5MINUTES); const resource = new CfnDeploymentGroup(this, 'Resource', { applicationName: this.application.applicationName, @@ -290,6 +290,6 @@ class ImportedLambdaDeploymentGroup extends ImportedDeploymentGroupBase implemen }); this.application = props.application; - this.deploymentConfig = props.deploymentConfig || LambdaDeploymentConfig.CANARY_10PERCENT_5MINUTES; + this.deploymentConfig = this._bindDeploymentConfig(props.deploymentConfig || LambdaDeploymentConfig.CANARY_10PERCENT_5MINUTES); } } diff --git a/packages/@aws-cdk/aws-codedeploy/lib/private/base-deployment-group.ts b/packages/@aws-cdk/aws-codedeploy/lib/private/base-deployment-group.ts index 3ea12aa50d702..e3d09743bff58 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/private/base-deployment-group.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/private/base-deployment-group.ts @@ -1,7 +1,9 @@ import * as iam from '@aws-cdk/aws-iam'; import { Resource, IResource, ArnFormat, Arn, Aws } from '@aws-cdk/core'; import { Construct } from 'constructs'; +import { IBaseDeploymentConfig } from '../base-deployment-config'; import { CfnDeploymentGroup } from '../codedeploy.generated'; +import { isPredefinedDeploymentConfig } from './predefined-deployment-config'; import { validateName } from './utils'; /** @@ -52,6 +54,15 @@ export class ImportedDeploymentGroupBase extends Resource { this.deploymentGroupName = deploymentGroupName; this.deploymentGroupArn = deploymentGroupArn; } + + /** + * Bind DeploymentGroupConfig to the current group, if supported + * + * @internal + */ + protected _bindDeploymentConfig(config: IBaseDeploymentConfig) { + return isPredefinedDeploymentConfig(config) ? config.bindEnvironment(this) : config; + } } export interface DeploymentGroupBaseProps { @@ -114,6 +125,15 @@ export class DeploymentGroupBase extends Resource { this.node.addValidation({ validate: () => validateName('Deployment group', this.physicalName) }); } + /** + * Bind DeploymentGroupConfig to the current group, if supported + * + * @internal + */ + protected _bindDeploymentConfig(config: IBaseDeploymentConfig) { + return isPredefinedDeploymentConfig(config) ? config.bindEnvironment(this) : config; + } + /** * Set name and ARN properties. * @@ -135,4 +155,4 @@ export class DeploymentGroupBase extends Resource { arnFormat: ArnFormat.COLON_RESOURCE_NAME, }); } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-codedeploy/lib/private/predefined-deployment-config.ts b/packages/@aws-cdk/aws-codedeploy/lib/private/predefined-deployment-config.ts new file mode 100644 index 0000000000000..3341a4df0af95 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/lib/private/predefined-deployment-config.ts @@ -0,0 +1,31 @@ +import { IResource } from '@aws-cdk/core'; +import { IBaseDeploymentConfig } from '../base-deployment-config'; + +/** + * A reference to a DeploymentConfig that is managed by AWS + * + * Since these DeploymentConfigs are present in every region, and we might use + * them in conjunction with cross-region DeploymentGroups, we need to specialize + * the account and region to the DeploymentGroup before using. + * + * A DeploymentGroup must call `bindEnvironment()` first if it detects this type, + * before reading the DeploymentConfig ARN. + * + * This type is fully hidden, which means that the constant objects provided by + * CDK will have magical behavior that customers can't reimplement themselves. + * Not ideal, but our DeploymentConfig type inheritance is already overly + * complicated and to do it properly with the nominal typing we are emplying + * will require adding 4 more empty or nearly empty interfaces, which seems a + * bit silly for a need that's not necessarily clearly needed by customers. + * We can always move to exposing later. + */ +export interface IPredefinedDeploymentConfig { + /** + * Bind the predefined deployment config to the environment of the given resource + */ + bindEnvironment(deploymentGroup: IResource): IBaseDeploymentConfig; +} + +export function isPredefinedDeploymentConfig(x: unknown): x is IPredefinedDeploymentConfig { + return typeof x === 'object' && !!x && !!(x as any).bindEnvironment; +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codedeploy/lib/private/utils.ts b/packages/@aws-cdk/aws-codedeploy/lib/private/utils.ts index 9dccb367d8578..2cd2857e3079f 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/private/utils.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/private/utils.ts @@ -1,8 +1,9 @@ import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; -import { Token, Stack, ArnFormat, Arn, Fn, Aws } from '@aws-cdk/core'; +import { Token, Stack, ArnFormat, Arn, Fn, Aws, IResource } from '@aws-cdk/core'; import { IBaseDeploymentConfig } from '../base-deployment-config'; import { CfnDeploymentGroup } from '../codedeploy.generated'; import { AutoRollbackConfig } from '../rollback-config'; +import { IPredefinedDeploymentConfig } from './predefined-deployment-config'; export function arnForApplication(stack: Stack, applicationName: string): string { return stack.formatArn({ @@ -18,11 +19,11 @@ export function nameFromDeploymentGroupArn(deploymentGroupArn: string): string { return Fn.select(1, Fn.split('/', components.resourceName ?? '')); } -export function arnForDeploymentConfig(name: string): string { +export function arnForDeploymentConfig(name: string, resource?: IResource): string { return Arn.format({ partition: Aws.PARTITION, - account: Aws.ACCOUNT_ID, - region: Aws.REGION, + account: resource?.env.account ?? Aws.ACCOUNT_ID, + region: resource?.env.region ?? Aws.REGION, service: 'codedeploy', resource: 'deploymentconfig', resourceName: name, @@ -41,10 +42,14 @@ CfnDeploymentGroup.AlarmConfigurationProperty | undefined { }; } -export function deploymentConfig(name: string): IBaseDeploymentConfig { +export function deploymentConfig(name: string): IBaseDeploymentConfig & IPredefinedDeploymentConfig { return { deploymentConfigName: name, deploymentConfigArn: arnForDeploymentConfig(name), + bindEnvironment: (resource) => ({ + deploymentConfigName: name, + deploymentConfigArn: arnForDeploymentConfig(name, resource), + }), }; } 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 828529358a6cd..b78bc0179af67 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-group.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-group.ts @@ -68,7 +68,7 @@ class ImportedServerDeploymentGroup extends ImportedDeploymentGroupBase implemen }); this.application = props.application; - this.deploymentConfig = props.deploymentConfig || ServerDeploymentConfig.ONE_AT_A_TIME; + this.deploymentConfig = this._bindDeploymentConfig(props.deploymentConfig || ServerDeploymentConfig.ONE_AT_A_TIME); } } @@ -255,7 +255,7 @@ export class ServerDeploymentGroup extends DeploymentGroupBase implements IServe this.application = props.application || new ServerApplication(this, 'Application', { applicationName: props.deploymentGroupName === cdk.PhysicalName.GENERATE_IF_NEEDED ? cdk.PhysicalName.GENERATE_IF_NEEDED : undefined, }); - this.deploymentConfig = props.deploymentConfig || ServerDeploymentConfig.ONE_AT_A_TIME; + this.deploymentConfig = this._bindDeploymentConfig(props.deploymentConfig || ServerDeploymentConfig.ONE_AT_A_TIME); this.role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSCodeDeployRole')); this._autoScalingGroups = props.autoScalingGroups || []; diff --git a/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-group.test.ts b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-group.test.ts index aed7b8655516e..0b6a6ee653baa 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-group.test.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-group.test.ts @@ -5,7 +5,7 @@ import * as ecs from '@aws-cdk/aws-ecs'; import * as elbv2 from '@aws-cdk/aws-elasticloadbalancingv2'; import * as iam from '@aws-cdk/aws-iam'; import * as cdk from '@aws-cdk/core'; -import { Duration } from '@aws-cdk/core'; +import { Duration, Stack } from '@aws-cdk/core'; import * as codedeploy from '../../lib'; const mockCluster = 'my-cluster'; @@ -45,7 +45,7 @@ describe('CodeDeploy ECS DeploymentGroup', () => { deploymentGroupName: 'EcsDeploymentGroup', }); - expect(importedGroup.deploymentConfig).toEqual(codedeploy.EcsDeploymentConfig.ALL_AT_ONCE); + expect(importedGroup.deploymentConfig.deploymentConfigName).toEqual('CodeDeployDefault.ECSAllAtOnce'); }); }); @@ -849,25 +849,34 @@ describe('CodeDeploy ECS DeploymentGroup', () => { }); }); - test('deploymentGroup from Arn knows its account and region', () => { - // GIVEN - const stack = new cdk.Stack(undefined, 'Stack', { env: { account: '111111111111', region: 'blabla-1' } }); + describe('deploymentGroup from ARN in different account and region', () => { + let stack: Stack; + let application: codedeploy.IEcsApplication; + let group: codedeploy.IEcsDeploymentGroup; - // WHEN - const application = codedeploy.EcsApplication.fromEcsApplicationArn(stack, 'Application', 'arn:aws:codedeploy:theregion-1:222222222222:application:MyApplication'); - const group = codedeploy.EcsDeploymentGroup.fromEcsDeploymentGroupAttributes(stack, 'Group', { - application, - deploymentGroupName: 'DeploymentGroup', + const account = '222222222222'; + const region = 'theregion-1'; + + beforeEach(() => { + stack = new cdk.Stack(undefined, 'Stack', { env: { account: '111111111111', region: 'blabla-1' } }); + + application = codedeploy.EcsApplication.fromEcsApplicationArn(stack, 'Application', `arn:aws:codedeploy:${region}:${account}:application:MyApplication`); + group = codedeploy.EcsDeploymentGroup.fromEcsDeploymentGroupAttributes(stack, 'Group', { + application, + deploymentGroupName: 'DeploymentGroup', + }); }); - // THEN - expect(application.env).toEqual(expect.objectContaining({ - account: '222222222222', - region: 'theregion-1', - })); - expect(group.env).toEqual(expect.objectContaining({ - account: '222222222222', - region: 'theregion-1', - })); + test('knows its account and region', () => { + // THEN + expect(application.env).toEqual(expect.objectContaining({ account, region })); + expect(group.env).toEqual(expect.objectContaining({ account, region })); + }); + + test('references the predefined DeploymentGroupConfig in the right region', () => { + expect(group.deploymentConfig.deploymentConfigArn).toEqual(expect.stringContaining( + `:codedeploy:${region}:${account}:deploymentconfig:CodeDeployDefault.ECSAllAtOnce`, + )); + }); }); }); diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-group.test.ts b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-group.test.ts index 9eae61c7a4e4f..202f4127432ed 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-group.test.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-group.test.ts @@ -3,6 +3,7 @@ import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; import * as cdk from '@aws-cdk/core'; +import { Stack } from '@aws-cdk/core'; import * as codedeploy from '../../lib'; import { TrafficRouting } from '../../lib'; @@ -616,26 +617,35 @@ describe('CodeDeploy Lambda DeploymentGroup', () => { }); }); - test('deploymentGroup from Arn knows its account and region', () => { - // GIVEN - const stack = new cdk.Stack(undefined, 'Stack', { env: { account: '111111111111', region: 'blabla-1' } }); + describe('deploymentGroup from ARN in different account and region', () => { + let stack: Stack; + let application: codedeploy.ILambdaApplication; + let group: codedeploy.ILambdaDeploymentGroup; - // WHEN - const application = codedeploy.LambdaApplication.fromLambdaApplicationArn(stack, 'Application', 'arn:aws:codedeploy:theregion-1:222222222222:application:MyApplication'); - const group = codedeploy.LambdaDeploymentGroup.fromLambdaDeploymentGroupAttributes(stack, 'Group', { - application, - deploymentGroupName: 'DeploymentGroup', - }); - - // THEN - expect(application.env).toEqual(expect.objectContaining({ - account: '222222222222', - region: 'theregion-1', - })); - expect(group.env).toEqual(expect.objectContaining({ - account: '222222222222', - region: 'theregion-1', - })); + const account = '222222222222'; + const region = 'theregion-1'; + + beforeEach(() => { + stack = new cdk.Stack(undefined, 'Stack', { env: { account: '111111111111', region: 'blabla-1' } }); + + application = codedeploy.LambdaApplication.fromLambdaApplicationArn(stack, 'Application', `arn:aws:codedeploy:${region}:${account}:application:MyApplication`); + group = codedeploy.LambdaDeploymentGroup.fromLambdaDeploymentGroupAttributes(stack, 'Group', { + application, + deploymentGroupName: 'DeploymentGroup', + }); + }); + + test('knows its account and region', () => { + // THEN + expect(application.env).toEqual(expect.objectContaining({ account, region })); + expect(group.env).toEqual(expect.objectContaining({ account, region })); + }); + + test('references the predefined DeploymentGroupConfig in the right region', () => { + expect(group.deploymentConfig.deploymentConfigArn).toEqual(expect.stringContaining( + `:codedeploy:${region}:${account}:deploymentconfig:CodeDeployDefault.LambdaCanary10Percent5Minutes`, + )); + }); }); }); @@ -649,7 +659,7 @@ describe('imported with fromLambdaDeploymentGroupAttributes', () => { deploymentGroupName: 'LambdaDeploymentGroup', }); - expect(importedGroup.deploymentConfig).toEqual(codedeploy.LambdaDeploymentConfig.CANARY_10PERCENT_5MINUTES); + expect(importedGroup.deploymentConfig.deploymentConfigName).toEqual('CodeDeployDefault.LambdaCanary10Percent5Minutes'); }); }); diff --git a/packages/@aws-cdk/aws-codedeploy/test/server/deployment-group.test.ts b/packages/@aws-cdk/aws-codedeploy/test/server/deployment-group.test.ts index 4f360aa9cb9c2..02d76400d3120 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/server/deployment-group.test.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/server/deployment-group.test.ts @@ -494,25 +494,34 @@ describe('CodeDeploy Server Deployment Group', () => { expect(() => app.synth()).toThrow('Deployment group name: "my name" can only contain letters (a-z, A-Z), numbers (0-9), periods (.), underscores (_), + (plus signs), = (equals signs), , (commas), @ (at signs), - (minus signs).'); }); - test('deploymentGroup from Arn knows its account and region', () => { - // GIVEN - const stack = new cdk.Stack(undefined, 'Stack', { env: { account: '111111111111', region: 'blabla-1' } }); + describe('deploymentGroup from ARN in different account and region', () => { + let stack: cdk.Stack; + let application: codedeploy.IServerApplication; + let group: codedeploy.IServerDeploymentGroup; - // WHEN - const application = codedeploy.ServerApplication.fromServerApplicationArn(stack, 'Application', 'arn:aws:codedeploy:theregion-1:222222222222:application:MyApplication'); - const group = codedeploy.ServerDeploymentGroup.fromServerDeploymentGroupAttributes(stack, 'Group', { - application, - deploymentGroupName: 'DeploymentGroup', - }); - - // THEN - expect(application.env).toEqual(expect.objectContaining({ - account: '222222222222', - region: 'theregion-1', - })); - expect(group.env).toEqual(expect.objectContaining({ - account: '222222222222', - region: 'theregion-1', - })); + const account = '222222222222'; + const region = 'theregion-1'; + + beforeEach(() => { + stack = new cdk.Stack(undefined, 'Stack', { env: { account: '111111111111', region: 'blabla-1' } }); + + application = codedeploy.ServerApplication.fromServerApplicationArn(stack, 'Application', `arn:aws:codedeploy:${region}:${account}:application:MyApplication`); + group = codedeploy.ServerDeploymentGroup.fromServerDeploymentGroupAttributes(stack, 'Group', { + application, + deploymentGroupName: 'DeploymentGroup', + }); + }); + + test('knows its account and region', () => { + // THEN + expect(application.env).toEqual(expect.objectContaining({ account, region })); + expect(group.env).toEqual(expect.objectContaining({ account, region })); + }); + + test('references the predefined DeploymentGroupConfig in the right region', () => { + expect(group.deploymentConfig.deploymentConfigArn).toEqual(expect.stringContaining( + `:codedeploy:${region}:${account}:deploymentconfig:CodeDeployDefault.OneAtATime`, + )); + }); }); }); diff --git a/packages/@aws-cdk/aws-ecr-assets/README.md b/packages/@aws-cdk/aws-ecr-assets/README.md index e6f8fa7c5f43a..32059529f1ee1 100644 --- a/packages/@aws-cdk/aws-ecr-assets/README.md +++ b/packages/@aws-cdk/aws-ecr-assets/README.md @@ -56,6 +56,9 @@ the `buildArgs` property. It is recommended to skip hashing of `buildArgs` for values that can change between different machines to maintain a consistent asset hash. +Additionally, you can supply `buildSecrets`. Your system must have Buildkit +enabled, see https://docs.docker.com/build/buildkit/. + ```ts import { DockerImageAsset } from '@aws-cdk/aws-ecr-assets'; diff --git a/packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts b/packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts index a0269f1f495f3..da03e18f80046 100644 --- a/packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts +++ b/packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts @@ -98,6 +98,13 @@ export interface DockerImageAssetInvalidationOptions { */ readonly buildArgs?: boolean; + /** + * Use `buildSecrets` while calculating the asset hash + * + * @default true + */ + readonly buildSecrets?: boolean; + /** * Use `target` while calculating the asset hash * @@ -170,6 +177,23 @@ export interface DockerImageAssetOptions extends FingerprintOptions, FileFingerp */ readonly buildArgs?: { [key: string]: string }; + /** + * Build secrets. + * + * Docker BuildKit must be enabled to use build secrets. + * + * @see https://docs.docker.com/build/buildkit/ + * + * @default - no build secrets + * + * @example + * + * { + * 'MY_SECRET': DockerBuildSecret.fromSrc('file.txt') + * } + */ + readonly buildSecrets?: { [key: string]: string } + /** * Docker target to build to * @@ -282,6 +306,11 @@ export class DockerImageAsset extends Construct implements IAsset { */ private readonly dockerBuildArgs?: { [key: string]: string }; + /** + * Build secrets to pass to the `docker build` command. + */ + private readonly dockerBuildSecrets?: { [key: string]: string }; + /** * Outputs to pass to the `docker build` command. */ @@ -345,6 +374,7 @@ export class DockerImageAsset extends Construct implements IAsset { const extraHash: { [field: string]: any } = {}; if (props.invalidation?.extraHash !== false && props.extraHash) { extraHash.user = props.extraHash; } if (props.invalidation?.buildArgs !== false && props.buildArgs) { extraHash.buildArgs = props.buildArgs; } + if (props.invalidation?.buildSecrets !== false && props.buildSecrets) { extraHash.buildSecrets = props.buildSecrets; } if (props.invalidation?.target !== false && props.target) { extraHash.target = props.target; } if (props.invalidation?.file !== false && props.file) { extraHash.file = props.file; } if (props.invalidation?.repositoryName !== false && props.repositoryName) { extraHash.repositoryName = props.repositoryName; } @@ -374,12 +404,14 @@ export class DockerImageAsset extends Construct implements IAsset { const stack = Stack.of(this); this.assetPath = staging.relativeStagedPath(stack); this.dockerBuildArgs = props.buildArgs; + this.dockerBuildSecrets = props.buildSecrets; this.dockerBuildTarget = props.target; this.dockerOutputs = props.outputs; const location = stack.synthesizer.addDockerImageAsset({ directoryName: this.assetPath, dockerBuildArgs: this.dockerBuildArgs, + dockerBuildSecrets: this.dockerBuildSecrets, dockerBuildTarget: this.dockerBuildTarget, dockerFile: props.file, sourceHash: staging.assetHash, @@ -420,6 +452,7 @@ export class DockerImageAsset extends Construct implements IAsset { resource.cfnOptions.metadata[cxapi.ASSET_RESOURCE_METADATA_PATH_KEY] = this.assetPath; resource.cfnOptions.metadata[cxapi.ASSET_RESOURCE_METADATA_DOCKERFILE_PATH_KEY] = this.dockerfilePath; resource.cfnOptions.metadata[cxapi.ASSET_RESOURCE_METADATA_DOCKER_BUILD_ARGS_KEY] = this.dockerBuildArgs; + resource.cfnOptions.metadata[cxapi.ASSET_RESOURCE_METADATA_DOCKER_BUILD_SECRETS_KEY] = this.dockerBuildSecrets; resource.cfnOptions.metadata[cxapi.ASSET_RESOURCE_METADATA_DOCKER_BUILD_TARGET_KEY] = this.dockerBuildTarget; resource.cfnOptions.metadata[cxapi.ASSET_RESOURCE_METADATA_PROPERTY_KEY] = resourceProperty; resource.cfnOptions.metadata[cxapi.ASSET_RESOURCE_METADATA_DOCKER_OUTPUTS_KEY] = this.dockerOutputs; @@ -435,16 +468,25 @@ function validateProps(props: DockerImageAssetProps) { } validateBuildArgs(props.buildArgs); + validateBuildSecrets(props.buildSecrets); } -function validateBuildArgs(buildArgs?: { [key: string]: string }) { - for (const [key, value] of Object.entries(buildArgs || {})) { +function validateBuildProps(buildPropName: string, buildProps?: { [key: string]: string }) { + for (const [key, value] of Object.entries(buildProps || {})) { if (Token.isUnresolved(key) || Token.isUnresolved(value)) { - throw new Error('Cannot use tokens in keys or values of "buildArgs" since they are needed before deployment'); + throw new Error(`Cannot use tokens in keys or values of "${buildPropName}" since they are needed before deployment`); } } } +function validateBuildArgs(buildArgs?: { [key: string]: string }) { + validateBuildProps('buildArgs', buildArgs); +} + +function validateBuildSecrets(buildSecrets?: { [key: string]: string }) { + validateBuildProps('buildSecrets', buildSecrets); +} + function toSymlinkFollow(follow?: FollowMode): SymlinkFollowMode | undefined { switch (follow) { case undefined: return undefined; diff --git a/packages/@aws-cdk/aws-ecr-assets/test/demo-image-secret/Dockerfile b/packages/@aws-cdk/aws-ecr-assets/test/demo-image-secret/Dockerfile new file mode 100644 index 0000000000000..72a0396611404 --- /dev/null +++ b/packages/@aws-cdk/aws-ecr-assets/test/demo-image-secret/Dockerfile @@ -0,0 +1,6 @@ +FROM public.ecr.aws/lambda/python:3.6 +RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret +EXPOSE 8000 +WORKDIR /src +ADD . /src +CMD python3 index.py diff --git a/packages/@aws-cdk/aws-ecr-assets/test/demo-image-secret/index.py b/packages/@aws-cdk/aws-ecr-assets/test/demo-image-secret/index.py new file mode 100644 index 0000000000000..2ccedfce3ab76 --- /dev/null +++ b/packages/@aws-cdk/aws-ecr-assets/test/demo-image-secret/index.py @@ -0,0 +1,33 @@ +#!/usr/bin/python +import sys +import textwrap +import http.server +import socketserver + +PORT = 8000 + + +class Handler(http.server.SimpleHTTPRequestHandler): + def do_GET(self): + self.send_response(200) + self.send_header('Content-Type', 'text/html') + self.end_headers() + self.wfile.write(textwrap.dedent('''\ + +
This container got built and started as part of the integ test.
+ + + ''').encode('utf-8')) + + +def main(): + httpd = http.server.HTTPServer(("", PORT), Handler) + print("serving at port", PORT) + httpd.serve_forever() + + +if __name__ == '__main__': + main() 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 db926dedb562f..2b5c98a100f4d 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 @@ -2,7 +2,7 @@ import * as fs from 'fs'; import * as path from 'path'; import { describeDeprecated, testDeprecated } from '@aws-cdk/cdk-build-tools'; import * as cxschema from '@aws-cdk/cloud-assembly-schema'; -import { App, DefaultStackSynthesizer, IgnoreMode, Lazy, LegacyStackSynthesizer, Stack, Stage } from '@aws-cdk/core'; +import { App, DefaultStackSynthesizer, DockerBuildSecret, IgnoreMode, Lazy, LegacyStackSynthesizer, Stack, Stage } from '@aws-cdk/core'; import * as cxapi from '@aws-cdk/cx-api'; import { DockerImageAsset } from '../lib'; @@ -150,6 +150,7 @@ describe('image asset', () => { const asset5 = new DockerImageAsset(stack, 'Asset5', { directory, file: 'Dockerfile.Custom', target: 'NonDefaultTarget' }); const asset6 = new DockerImageAsset(stack, 'Asset6', { directory, extraHash: 'random-extra' }); const asset7 = new DockerImageAsset(stack, 'Asset7', { directory, outputs: ['123'] }); + const asset8 = new DockerImageAsset(stack, 'Asset8', { directory, buildSecrets: { mySecret: DockerBuildSecret.fromSrc('abc.txt') } }); expect(asset1.assetHash).toEqual('13248c55633f3b198a628bb2ea4663cb5226f8b2801051bd0c725950266fd590'); expect(asset2.assetHash).toEqual('36bf205fb9adc5e45ba1c8d534158a0aed96d190eff433af1d90f3b94f96e751'); @@ -158,6 +159,7 @@ describe('image asset', () => { expect(asset5.assetHash).toEqual('c02bfba13b2e7e1ff5c778a76e10296b9e8d17f7f8252d097f4170ae04ce0eb4'); expect(asset6.assetHash).toEqual('3528d6838647a5e9011b0f35aec514d03ad11af05a94653cdcf4dacdbb070a06'); expect(asset7.assetHash).toEqual('ced0a3076efe217f9cbdff0943e543f36ecf77f70b9a6fe28b8633deb728a462'); + expect(asset8.assetHash).toEqual('ffc2718e616141d18c8f4623d13cdfd68cb8f010ca5db31c916c8b5f10c162be'); }); diff --git a/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/asset.60dea2e16e94d1977b92fe03fa7085fea446233f1fe499702b69593438baa59f/Dockerfile b/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/asset.60dea2e16e94d1977b92fe03fa7085fea446233f1fe499702b69593438baa59f/Dockerfile new file mode 100644 index 0000000000000..72a0396611404 --- /dev/null +++ b/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/asset.60dea2e16e94d1977b92fe03fa7085fea446233f1fe499702b69593438baa59f/Dockerfile @@ -0,0 +1,6 @@ +FROM public.ecr.aws/lambda/python:3.6 +RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret +EXPOSE 8000 +WORKDIR /src +ADD . /src +CMD python3 index.py diff --git a/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/asset.60dea2e16e94d1977b92fe03fa7085fea446233f1fe499702b69593438baa59f/index.py b/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/asset.60dea2e16e94d1977b92fe03fa7085fea446233f1fe499702b69593438baa59f/index.py new file mode 100644 index 0000000000000..2ccedfce3ab76 --- /dev/null +++ b/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/asset.60dea2e16e94d1977b92fe03fa7085fea446233f1fe499702b69593438baa59f/index.py @@ -0,0 +1,33 @@ +#!/usr/bin/python +import sys +import textwrap +import http.server +import socketserver + +PORT = 8000 + + +class Handler(http.server.SimpleHTTPRequestHandler): + def do_GET(self): + self.send_response(200) + self.send_header('Content-Type', 'text/html') + self.end_headers() + self.wfile.write(textwrap.dedent('''\ + +This container got built and started as part of the integ test.
+ + + ''').encode('utf-8')) + + +def main(): + httpd = http.server.HTTPServer(("", PORT), Handler) + print("serving at port", PORT) + httpd.serve_forever() + + +if __name__ == '__main__': + main() diff --git a/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/cdk.out b/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/cdk.out index e425c25d285ad..d8b441d447f8a 100644 --- a/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/cdk.out @@ -1 +1 @@ -{"version":"24.0.0"} \ No newline at end of file +{"version":"29.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/integ-assets-docker.assets.json b/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/integ-assets-docker.assets.json index 4630499e9d3a3..9e78ad767b944 100644 --- a/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/integ-assets-docker.assets.json +++ b/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/integ-assets-docker.assets.json @@ -1,7 +1,7 @@ { - "version": "24.0.0", + "version": "29.0.0", "files": { - "3ef2c8ebbbb128e6fbd2f26a8c80b8154d5fe5157a29846585cb36feac29318e": { + "b1025f887a56783d23c02c714067f4e119f3a3393c9db47c7ce05076e52e58bd": { "source": { "path": "integ-assets-docker.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "3ef2c8ebbbb128e6fbd2f26a8c80b8154d5fe5157a29846585cb36feac29318e.json", + "objectKey": "b1025f887a56783d23c02c714067f4e119f3a3393c9db47c7ce05076e52e58bd.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } @@ -55,6 +55,21 @@ "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-image-publishing-role-${AWS::AccountId}-${AWS::Region}" } } + }, + "60dea2e16e94d1977b92fe03fa7085fea446233f1fe499702b69593438baa59f": { + "source": { + "directory": "asset.60dea2e16e94d1977b92fe03fa7085fea446233f1fe499702b69593438baa59f", + "dockerBuildSecrets": { + "mysecret": "src=index.py" + } + }, + "destinations": { + "current_account-current_region": { + "repositoryName": "cdk-hnb659fds-container-assets-${AWS::AccountId}-${AWS::Region}", + "imageTag": "60dea2e16e94d1977b92fe03fa7085fea446233f1fe499702b69593438baa59f", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-image-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/integ-assets-docker.template.json b/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/integ-assets-docker.template.json index 56e3637207a43..8c7c033450117 100644 --- a/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/integ-assets-docker.template.json +++ b/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/integ-assets-docker.template.json @@ -76,6 +76,11 @@ "Value": { "Fn::Sub": "${AWS::AccountId}.dkr.ecr.${AWS::Region}.${AWS::URLSuffix}/cdk-hnb659fds-container-assets-${AWS::AccountId}-${AWS::Region}:fa08370824fa0a7eab2c59a4f371fe7631019044d6c906b4268193120dc213b4" } + }, + "ImageUri5": { + "Value": { + "Fn::Sub": "${AWS::AccountId}.dkr.ecr.${AWS::Region}.${AWS::URLSuffix}/cdk-hnb659fds-container-assets-${AWS::AccountId}-${AWS::Region}:60dea2e16e94d1977b92fe03fa7085fea446233f1fe499702b69593438baa59f" + } } }, "Parameters": { diff --git a/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/integ.json b/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/integ.json index 4848cd7f244e2..f4aed5a9c37d0 100644 --- a/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/integ.json +++ b/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "24.0.0", + "version": "29.0.0", "testCases": { "integ.assets-docker": { "stacks": [ diff --git a/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/manifest.json b/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/manifest.json index 48be2750bd076..cd31c93490241 100644 --- a/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "24.0.0", + "version": "29.0.0", "artifacts": { "integ-assets-docker.assets": { "type": "cdk:asset-manifest", @@ -17,7 +17,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/3ef2c8ebbbb128e6fbd2f26a8c80b8154d5fe5157a29846585cb36feac29318e.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/b1025f887a56783d23c02c714067f4e119f3a3393c9db47c7ce05076e52e58bd.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -69,6 +69,12 @@ "data": "ImageUri4" } ], + "/integ-assets-docker/ImageUri5": [ + { + "type": "aws:cdk:logicalId", + "data": "ImageUri5" + } + ], "/integ-assets-docker/BootstrapVersion": [ { "type": "aws:cdk:logicalId", diff --git a/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/tree.json b/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/tree.json index c013950bfa133..32988bdc52723 100644 --- a/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/tree.json +++ b/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.js.snapshot/tree.json @@ -112,6 +112,32 @@ "version": "0.0.0" } }, + "DockerImage5": { + "id": "DockerImage5", + "path": "integ-assets-docker/DockerImage5", + "children": { + "Staging": { + "id": "Staging", + "path": "integ-assets-docker/DockerImage5/Staging", + "constructInfo": { + "fqn": "@aws-cdk/core.AssetStaging", + "version": "0.0.0" + } + }, + "Repository": { + "id": "Repository", + "path": "integ-assets-docker/DockerImage5/Repository", + "constructInfo": { + "fqn": "@aws-cdk/aws-ecr.RepositoryBase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ecr-assets.DockerImageAsset", + "version": "0.0.0" + } + }, "MyUser": { "id": "MyUser", "path": "integ-assets-docker/MyUser", @@ -236,6 +262,14 @@ "version": "0.0.0" } }, + "ImageUri5": { + "id": "ImageUri5", + "path": "integ-assets-docker/ImageUri5", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" + } + }, "BootstrapVersion": { "id": "BootstrapVersion", "path": "integ-assets-docker/BootstrapVersion", @@ -263,7 +297,7 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.182" + "version": "10.1.216" } } }, diff --git a/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.ts b/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.ts index 1aa9fa392af0a..702b83fe011a5 100644 --- a/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.ts +++ b/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.ts @@ -24,15 +24,24 @@ const asset4 = new assets.DockerImageAsset(stack, 'DockerImage4', { outputs: ['type=docker'], }); +const asset5 = new assets.DockerImageAsset(stack, 'DockerImage5', { + directory: path.join(__dirname, 'demo-image-secret'), + buildSecrets: { + mysecret: cdk.DockerBuildSecret.fromSrc('index.py'), + }, +}); + const user = new iam.User(stack, 'MyUser'); asset.repository.grantPull(user); asset2.repository.grantPull(user); asset3.repository.grantPull(user); asset4.repository.grantPull(user); +asset5.repository.grantPull(user); new cdk.CfnOutput(stack, 'ImageUri', { value: asset.imageUri }); new cdk.CfnOutput(stack, 'ImageUri2', { value: asset2.imageUri }); new cdk.CfnOutput(stack, 'ImageUri3', { value: asset3.imageUri }); new cdk.CfnOutput(stack, 'ImageUri4', { value: asset4.imageUri }); +new cdk.CfnOutput(stack, 'ImageUri5', { value: asset5.imageUri }); app.synth(); diff --git a/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.js.snapshot/cdk.out b/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.js.snapshot/cdk.out index 588d7b269d34f..d8b441d447f8a 100644 --- a/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.js.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.js.snapshot/cdk.out @@ -1 +1 @@ -{"version":"20.0.0"} \ No newline at end of file +{"version":"29.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.js.snapshot/integ.json b/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.js.snapshot/integ.json index c1dff06736e53..7c8541b18231b 100644 --- a/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.js.snapshot/integ.json +++ b/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.js.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "20.0.0", + "version": "29.0.0", "testCases": { "integ.nested-stacks-docker": { "stacks": [ diff --git a/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.js.snapshot/manifest.json b/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.js.snapshot/manifest.json index f318c19a2b7cf..a8cd150aeb96f 100644 --- a/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.js.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.js.snapshot/manifest.json @@ -1,12 +1,6 @@ { - "version": "20.0.0", + "version": "29.0.0", "artifacts": { - "Tree": { - "type": "cdk:tree", - "properties": { - "file": "tree.json" - } - }, "nested-stacks-docker.assets": { "type": "cdk:asset-manifest", "properties": { @@ -77,6 +71,12 @@ ] }, "displayName": "nested-stacks-docker" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.js.snapshot/nested-stacks-docker.assets.json b/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.js.snapshot/nested-stacks-docker.assets.json index 8f3fdeddfade8..aa40b93d5962f 100644 --- a/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.js.snapshot/nested-stacks-docker.assets.json +++ b/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.js.snapshot/nested-stacks-docker.assets.json @@ -1,5 +1,5 @@ { - "version": "20.0.0", + "version": "29.0.0", "files": { "eaf17d410c9c3958b50b406011121bab5f3147b4a61a0f93a9ab1db097033867": { "source": { diff --git a/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.js.snapshot/tree.json b/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.js.snapshot/tree.json index 9bbc4b8027746..0360789f62220 100644 --- a/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.js.snapshot/tree.json +++ b/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.js.snapshot/tree.json @@ -4,14 +4,6 @@ "id": "App", "path": "", "children": { - "Tree": { - "id": "Tree", - "path": "Tree", - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" - } - }, "nested-stacks-docker": { "id": "nested-stacks-docker", "path": "nested-stacks-docker", @@ -28,8 +20,8 @@ "id": "Staging", "path": "nested-stacks-docker/nested-stack-with-image/my-image/Staging", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.AssetStaging", + "version": "0.0.0" } }, "Repository": { @@ -142,14 +134,14 @@ "id": "output", "path": "nested-stacks-docker/nested-stack-with-image/output", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.NestedStack", + "version": "0.0.0" } }, "nested-stack-with-image.NestedStack": { @@ -185,26 +177,50 @@ } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.CfnStack", + "version": "0.0.0" } } }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.85" + "version": "10.1.216" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "nested-stacks-docker/BootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "nested-stacks-docker/CheckBootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnRule", + "version": "0.0.0" } } }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.85" + "version": "10.1.216" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-eks/package.json b/packages/@aws-cdk/aws-eks/package.json index 8c9645856d00f..92fd09a911fcd 100644 --- a/packages/@aws-cdk/aws-eks/package.json +++ b/packages/@aws-cdk/aws-eks/package.json @@ -80,7 +80,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/lambda-layer-kubectl-v24": "^2.0.83", + "@aws-cdk/lambda-layer-kubectl-v24": "^2.0.85", "aws-cdk-lib": "2.47.0", "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", @@ -93,8 +93,8 @@ "@types/sinon": "^9.0.11", "@types/yaml": "1.9.6", "aws-sdk": "^2.1211.0", - "cdk8s": "^2.6.34", - "cdk8s-plus-24": "2.4.5", + "cdk8s": "^2.6.36", + "cdk8s-plus-24": "2.4.8", "jest": "^27.5.1", "sinon": "^9.2.4" }, diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/util.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/util.ts index 3666d078f4a7f..518e0865161c9 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/util.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/util.ts @@ -1,6 +1,6 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; +import { Arn, ArnFormat, Fn, Token } from '@aws-cdk/core'; import { ApplicationProtocol, Protocol } from './enums'; -import { Arn, ArnFormat } from '@aws-cdk/core'; export type Attributes = { [key: string]: string | undefined }; @@ -92,10 +92,21 @@ export function mapTagMapToCxschema(tagMap: Record