From ccab485b87a7090ddf0773508d7b8ee84ff654b0 Mon Sep 17 00:00:00 2001 From: sakurai-ryo Date: Tue, 29 Oct 2024 20:06:06 +0900 Subject: [PATCH] fix(s3-assets): cannot publish a file without extension (#30597) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Issue # (if applicable) Closes #30471 ### ### Reason for this change Publishing a file with no extension using the `Asset` class with `BundlingOutput.SINGLE_FILE` and `AssetHashType.SOURCE`(default), as shown below, will result in an error `fail: EISDIR: illegal operation on a directory, read`, and publishing will fail. ```ts export class AssetTestStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); const asset = new s3assets.Asset(this, 'asset', { path: path.join(__dirname, '../assets/main'), bundling: { image: DockerImage.fromRegistry('golang:1.21'), entrypoint: ["bash", "-c"], command: ["echo 123 > /asset-output/main"], // a file without extension outputType: BundlingOutput.SINGLE_FILE, }, }); new CfnOutput(this, 'AssetHash', { value: asset.assetHash }); } } ``` This is because the path in `*.asset.json` is different from the actual file path. The `*.asset.json` expects the file to be in `asset.bead5b2c0d128650228f146d2326d5f3cbfb36738a9383fc6a09b1e9278803f0`, but when I check the `cdk.out` directory, I see that `asset.bead5b2c0d128650228f146d2326d5f3cbfb36738a9383fc6a09b1e9278803f0` is a directory, not a file. ```json { "version": "36.0.0", "files": { "bead5b2c0d128650228f146d2326d5f3cbfb36738a9383fc6a09b1e9278803f0": { "source": { "path": "asset.bead5b2c0d128650228f146d2326d5f3cbfb36738a9383fc6a09b1e9278803f0", "packaging": "file" }, "destinations": { "current_account-us-east-1": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1", "objectKey": "bead5b2c0d128650228f146d2326d5f3cbfb36738a9383fc6a09b1e9278803f0.bead5b2c0d128650228f146d2326d5f3cbfb36738a9383fc6a09b1e9278803f0", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-1" } } } } ``` ```bash cdk.out ├── SampleStack.assets.json ├── SampleStack.template.json ├── asset.bead5b2c0d128650228f146d2326d5f3cbfb36738a9383fc6a09b1e9278803f0 │   └── main ├── cdk.out ├── manifest.json └── tree.json ``` If I change it to a file with an extension, as shown below, I see that the file with the extension is staged under `cdk.out` dir, and the asset is published successfully. ```ts export class AssetTestStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); const asset = new s3assets.Asset(this, 'asset', { path: path.join(__dirname, '../assets/main'), bundling: { image: DockerImage.fromRegistry('golang:1.21'), entrypoint: ["bash", "-c"], command: ["echo 123 > /asset-output/main.bin"], // a file with an extension outputType: BundlingOutput.SINGLE_FILE, }, }); new CfnOutput(this, 'AssetHash', { value: asset.assetHash }); } } ``` ```bash cdk.out ├── SampleStack.assets.json ├── SampleStack.template.json ├── asset.dc5ce447844d7490834e46df016edc7f671b4fae19ab55b6c78973dcb5af98f8 │   └── main.bin ├── asset.dc5ce447844d7490834e46df016edc7f671b4fae19ab55b6c78973dcb5af98f8.bin # !! staged file here !! ├── cdk.out ├── manifest.json └── tree.json ``` ```json { "version": "36.0.0", "files": { "dc5ce447844d7490834e46df016edc7f671b4fae19ab55b6c78973dcb5af98f8": { "source": { "path": "asset.dc5ce447844d7490834e46df016edc7f671b4fae19ab55b6c78973dcb5af98f8.bin", "packaging": "file" }, "destinations": { "current_account-us-east-1": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1", "objectKey": "dc5ce447844d7490834e46df016edc7f671b4fae19ab55b6c78973dcb5af98f8.bin", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-1" } } } } ``` Files without extensions must be staged correctly in order to be published correctly. ### Description of changes The directory to write the bundling output is usually `cdk.out/asset.{asset hash}`. If the extension exists, it will be renamed from `cdk.out/asset.{asset hash}/{asset file name}` to `cdk.out/{asset hash}.{asset file extension}`. https://github.com/aws/aws-cdk/blob/c826d8faaeb310623eb9a1a1c82930b679768007/packages/aws-cdk-lib/core/lib/asset-staging.ts#L392 If the extension does not exist, the file name `cdk.out/asset.{asset hash}` (without extension) will be the same as the directory where bundling output is written. Therefore, the file is already considered staged and will not be staged correctly. https://github.com/aws/aws-cdk/blob/c826d8faaeb310623eb9a1a1c82930b679768007/packages/aws-cdk-lib/core/lib/asset-staging.ts#L383 Therefore, in such cases, I fix to change the file name by adding a suffix such as `noext` after the file name so that the file is correctly renamed. ### Description of how you validated changes unit tests and integ test. ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- ...efaultTestDeployAssertE3E7D2A4.assets.json | 2 +- .../main | 0 ...111e9aa20db0d42e929187a1b592a593ea713noext | 1 + .../cdk-integ-assets-bundling.assets.json | 19 ++- .../cdk.out | 2 +- .../integ.json | 2 +- .../manifest.json | 4 +- .../tree.json | 31 ++++- .../test/integ.assets.file-bundling.lit.ts | 11 ++ .../aws-cdk-lib/core/lib/asset-staging.ts | 21 ++- .../aws-cdk-lib/core/test/docker-stub-cp.sh | 11 ++ packages/aws-cdk-lib/core/test/docker-stub.sh | 6 + .../aws-cdk-lib/core/test/staging.test.ts | 128 ++++++++++++++++++ 13 files changed, 224 insertions(+), 14 deletions(-) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/asset.eb4b352e81f47c1880938665c15111e9aa20db0d42e929187a1b592a593ea713/main create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/asset.eb4b352e81f47c1880938665c15111e9aa20db0d42e929187a1b592a593ea713noext diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/IntegTestDefaultTestDeployAssertE3E7D2A4.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/IntegTestDefaultTestDeployAssertE3E7D2A4.assets.json index 2220abb680bef..87dfbae32bf67 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/IntegTestDefaultTestDeployAssertE3E7D2A4.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/IntegTestDefaultTestDeployAssertE3E7D2A4.assets.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "36.0.0", "files": { "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { "source": { diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/asset.eb4b352e81f47c1880938665c15111e9aa20db0d42e929187a1b592a593ea713/main b/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/asset.eb4b352e81f47c1880938665c15111e9aa20db0d42e929187a1b592a593ea713/main new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/asset.eb4b352e81f47c1880938665c15111e9aa20db0d42e929187a1b592a593ea713noext b/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/asset.eb4b352e81f47c1880938665c15111e9aa20db0d42e929187a1b592a593ea713noext new file mode 100644 index 0000000000000..9daeafb9864cf --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/asset.eb4b352e81f47c1880938665c15111e9aa20db0d42e929187a1b592a593ea713noext @@ -0,0 +1 @@ +test diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/cdk-integ-assets-bundling.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/cdk-integ-assets-bundling.assets.json index ba0dc94f29d00..a0cfc7c0e297a 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/cdk-integ-assets-bundling.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/cdk-integ-assets-bundling.assets.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "36.0.0", "files": { "68c2f55451f9566ae0c08664249d08921ab032b2aea797bd8ba293885bf00d64": { "source": { @@ -14,7 +14,20 @@ } } }, - "b189049465db589ff992e6ed7347f54d2edf36f0c48c6277ea280abe864b031a": { + "eb4b352e81f47c1880938665c15111e9aa20db0d42e929187a1b592a593ea713": { + "source": { + "path": "asset.eb4b352e81f47c1880938665c15111e9aa20db0d42e929187a1b592a593ea713noext", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "eb4b352e81f47c1880938665c15111e9aa20db0d42e929187a1b592a593ea713", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + }, + "90c0edfc12d01dfffaee94d9f09e061a7db55c839a6105e5a94f9d96dc51e4fd": { "source": { "path": "cdk-integ-assets-bundling.template.json", "packaging": "file" @@ -22,7 +35,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "b189049465db589ff992e6ed7347f54d2edf36f0c48c6277ea280abe864b031a.json", + "objectKey": "90c0edfc12d01dfffaee94d9f09e061a7db55c839a6105e5a94f9d96dc51e4fd.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/cdk.out index 2313ab5436501..1f0068d32659a 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/cdk.out +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/cdk.out @@ -1 +1 @@ -{"version":"34.0.0"} \ No newline at end of file +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/integ.json index e7ed476e08c94..db0feb0706bc8 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/integ.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "36.0.0", "testCases": { "IntegTest/DefaultTest": { "stacks": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/manifest.json index 4c5021f128fe3..a3d452104bce1 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "36.0.0", "artifacts": { "cdk-integ-assets-bundling.assets": { "type": "cdk:asset-manifest", @@ -18,7 +18,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}/b189049465db589ff992e6ed7347f54d2edf36f0c48c6277ea280abe864b031a.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/90c0edfc12d01dfffaee94d9f09e061a7db55c839a6105e5a94f9d96dc51e4fd.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/tree.json index 0550d8f1bdbde..766cc3f80d496 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.js.snapshot/tree.json @@ -66,7 +66,6 @@ "Action": [ "s3:GetBucket*", "s3:GetObject*", - "s3:HeadObject", "s3:List*" ], "Effect": "Allow", @@ -132,6 +131,32 @@ "version": "0.0.0" } }, + "BundledAssetWithoutExtension": { + "id": "BundledAssetWithoutExtension", + "path": "cdk-integ-assets-bundling/BundledAssetWithoutExtension", + "children": { + "Stage": { + "id": "Stage", + "path": "cdk-integ-assets-bundling/BundledAssetWithoutExtension/Stage", + "constructInfo": { + "fqn": "aws-cdk-lib.AssetStaging", + "version": "0.0.0" + } + }, + "AssetBucket": { + "id": "AssetBucket", + "path": "cdk-integ-assets-bundling/BundledAssetWithoutExtension/AssetBucket", + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.BucketBase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3_assets.Asset", + "version": "0.0.0" + } + }, "BootstrapVersion": { "id": "BootstrapVersion", "path": "cdk-integ-assets-bundling/BootstrapVersion", @@ -167,7 +192,7 @@ "path": "IntegTest/DefaultTest/Default", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.2.70" + "version": "10.3.0" } }, "DeployAssert": { @@ -213,7 +238,7 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.2.70" + "version": "10.3.0" } } }, diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.ts index ec89949ca6fa5..a1cc1732141d2 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3-assets/test/integ.assets.file-bundling.lit.ts @@ -26,6 +26,17 @@ class TestStack extends Stack { const user = new iam.User(this, 'MyUser'); asset.grantRead(user); + + new assets.Asset(this, 'BundledAssetWithoutExtension', { + path: path.join(__dirname, 'markdown-asset'), + bundling: { + image: DockerImage.fromBuild(path.join(__dirname, 'alpine-markdown')), + outputType: BundlingOutput.SINGLE_FILE, + command: [ + 'sh', '-c', 'echo 123 > /asset-output/main', + ], + }, + }); } } diff --git a/packages/aws-cdk-lib/core/lib/asset-staging.ts b/packages/aws-cdk-lib/core/lib/asset-staging.ts index 11d8e648ab8f9..49e8c292805dd 100644 --- a/packages/aws-cdk-lib/core/lib/asset-staging.ts +++ b/packages/aws-cdk-lib/core/lib/asset-staging.ts @@ -278,9 +278,10 @@ export class AssetStaging extends Construct { */ private stageByCopying(): StagedAsset { const assetHash = this.calculateHash(this.hashType); - const stagedPath = this.stagingDisabled + const targetPath = this.stagingDisabled ? this.sourcePath : path.resolve(this.assetOutdir, renderAssetFilename(assetHash, getExtension(this.sourcePath))); + const stagedPath = this.renderStagedPath(this.sourcePath, targetPath); if (!this.sourceStats.isDirectory() && !this.sourceStats.isFile()) { throw new Error(`Asset ${this.sourcePath} is expected to be either a directory or a regular file`); @@ -338,7 +339,10 @@ export class AssetStaging extends Construct { // Calculate assetHash afterwards if we still must assetHash = assetHash ?? this.calculateHash(this.hashType, bundling, bundledAsset.path); - const stagedPath = path.resolve(this.assetOutdir, renderAssetFilename(assetHash, bundledAsset.extension)); + const stagedPath = this.renderStagedPath( + bundledAsset.path, + path.resolve(this.assetOutdir, renderAssetFilename(assetHash, bundledAsset.extension)), + ); this.stageAsset(bundledAsset.path, stagedPath, 'move'); @@ -388,7 +392,7 @@ export class AssetStaging extends Construct { } // Moving can be done quickly - if (style == 'move') { + if (style === 'move') { fs.renameSync(sourcePath, targetPath); return; } @@ -511,6 +515,17 @@ export class AssetStaging extends Construct { throw new Error('Unknown asset hash type.'); } } + + private renderStagedPath(sourcePath: string, targetPath: string): string { + // Add a suffix to the asset file name + // because when a file without extension is specified, the source directory name is the same as the staged asset file name. + // But when the hashType is `AssetHashType.OUTPUT`, the source directory name begins with `bundling-temp-` and the staged asset file name is different. + // We only need to add a suffix when the hashType is not `AssetHashType.OUTPUT`. + if (this.hashType !== AssetHashType.OUTPUT && path.dirname(sourcePath) === targetPath) { + targetPath = targetPath + '_noext'; + } + return targetPath; + } } function renderAssetFilename(assetHash: string, extension = '') { diff --git a/packages/aws-cdk-lib/core/test/docker-stub-cp.sh b/packages/aws-cdk-lib/core/test/docker-stub-cp.sh index fec4009896d63..47f29ff36854c 100755 --- a/packages/aws-cdk-lib/core/test/docker-stub-cp.sh +++ b/packages/aws-cdk-lib/core/test/docker-stub-cp.sh @@ -6,6 +6,17 @@ set -euo pipefail echo "$@" >> /tmp/docker-stub-cp.input.concat echo "$@" > /tmp/docker-stub-cp.input +# create a file without extension to emulate created files, fetch the target path from the "docker cp" command +if cat /tmp/docker-stub-cp.input.concat | grep "DOCKER_STUB_SINGLE_FILE_WITHOUT_EXT"; then + if echo "$@" | grep "cp"| grep "/asset-output"; then + outdir=$(echo "$@" | grep cp | grep "/asset-output" | xargs -n1 | grep "cdk.out" | head -n1 | cut -d":" -f1) + if [ -n "$outdir" ]; then + touch "${outdir}/test" # create a file witout extension + exit 0 + fi + fi +fi + # create a fake zip to emulate created files, fetch the target path from the "docker cp" command if echo "$@" | grep "cp"| grep "/asset-output"; then outdir=$(echo "$@" | grep cp | grep "/asset-output" | xargs -n1 | grep "cdk.out" | head -n1 | cut -d":" -f1) diff --git a/packages/aws-cdk-lib/core/test/docker-stub.sh b/packages/aws-cdk-lib/core/test/docker-stub.sh index f3bc37d187136..2bb0934e7d304 100755 --- a/packages/aws-cdk-lib/core/test/docker-stub.sh +++ b/packages/aws-cdk-lib/core/test/docker-stub.sh @@ -37,6 +37,12 @@ if echo "$@" | grep "DOCKER_STUB_SINGLE_ARCHIVE"; then exit 0 fi +if echo "$@" | grep "DOCKER_STUB_SINGLE_FILE_WITHOUT_EXT"; then + outdir=$(echo "$@" | xargs -n1 | grep "/asset-output" | head -n1 | cut -d":" -f1) + touch ${outdir}/test # create a file witout extension + exit 0 +fi + if echo "$@" | grep "DOCKER_STUB_SINGLE_FILE"; then outdir=$(echo "$@" | xargs -n1 | grep "/asset-output" | head -n1 | cut -d":" -f1) touch ${outdir}/test.txt diff --git a/packages/aws-cdk-lib/core/test/staging.test.ts b/packages/aws-cdk-lib/core/test/staging.test.ts index dff63c09a76f8..e7fae0974a940 100644 --- a/packages/aws-cdk-lib/core/test/staging.test.ts +++ b/packages/aws-cdk-lib/core/test/staging.test.ts @@ -20,6 +20,7 @@ enum DockerStubCommand { MULTIPLE_FILES = 'DOCKER_STUB_MULTIPLE_FILES', SINGLE_ARCHIVE = 'DOCKER_STUB_SINGLE_ARCHIVE', SINGLE_FILE = 'DOCKER_STUB_SINGLE_FILE', + SINGLE_FILE_WITHOUT_EXT = 'DOCKER_STUB_SINGLE_FILE_WITHOUT_EXT', VOLUME_SINGLE_ARCHIVE = 'DOCKER_STUB_VOLUME_SINGLE_ARCHIVE', } @@ -1450,6 +1451,68 @@ describe('staging', () => { expect(staging.isArchive).toEqual(false); }); + test('bundling that produces a single file with SINGLE_FILE_WITHOUT_EXT and hash type SOURCE', () => { + // GIVEN + const app = new App({ context: { [cxapi.NEW_STYLE_STACK_SYNTHESIS_CONTEXT]: false } }); + const stack = new Stack(app, 'stack'); + const directory = path.join(__dirname, 'fs', 'fixtures', 'test1'); + + // WHEN + const staging = new AssetStaging(stack, 'Asset', { + sourcePath: directory, + bundling: { + image: DockerImage.fromRegistry('alpine'), + command: [DockerStubCommand.SINGLE_FILE_WITHOUT_EXT], + outputType: BundlingOutput.SINGLE_FILE, + }, + assetHashType: AssetHashType.SOURCE, // default + }); + + // THEN + const assembly = app.synth(); + expect(fs.readdirSync(assembly.directory)).toEqual([ + 'asset.ef734136dc22840a94140575a2f98cbc061074e09535589d1cd2c11a4ac2fd75', + 'asset.ef734136dc22840a94140575a2f98cbc061074e09535589d1cd2c11a4ac2fd75_noext', + 'cdk.out', + 'manifest.json', + 'stack.template.json', + 'tree.json', + ]); + expect(staging.packaging).toEqual(FileAssetPackaging.FILE); + expect(staging.isArchive).toEqual(false); + }); + + test('bundling that produces a single file with SINGLE_FILE_WITHOUT_EXT and hash type CUSTOM', () => { + // GIVEN + const app = new App({ context: { [cxapi.NEW_STYLE_STACK_SYNTHESIS_CONTEXT]: false } }); + const stack = new Stack(app, 'stack'); + const directory = path.join(__dirname, 'fs', 'fixtures', 'test1'); + + // WHEN + const staging = new AssetStaging(stack, 'Asset', { + sourcePath: directory, + bundling: { + image: DockerImage.fromRegistry('alpine'), + command: [DockerStubCommand.SINGLE_FILE_WITHOUT_EXT], + outputType: BundlingOutput.SINGLE_FILE, + }, + assetHashType: AssetHashType.CUSTOM, + assetHash: 'custom', + }); + + // THEN + const assembly = app.synth(); + expect(fs.readdirSync(assembly.directory)).toEqual([ + 'asset.f81c5ba9e81eebb202881a8e61a83ab4b69f6bee261989eb93625c9cf5d35335', + 'asset.f81c5ba9e81eebb202881a8e61a83ab4b69f6bee261989eb93625c9cf5d35335_noext', + 'cdk.out', + 'manifest.json', + 'stack.template.json', + 'tree.json', + ]); + expect(staging.packaging).toEqual(FileAssetPackaging.FILE); + expect(staging.isArchive).toEqual(false); + }); }); describe('staging with docker cp', () => { @@ -1517,6 +1580,71 @@ describe('staging with docker cp', () => { expect.stringContaining('volume rm assetOutput'), ])); }); + + test('bundling that produces a single file with docker image copy variant and hash type SOURCE', () => { + // GIVEN + const app = new App({ context: { [cxapi.NEW_STYLE_STACK_SYNTHESIS_CONTEXT]: false } }); + const stack = new Stack(app, 'stack'); + const directory = path.join(__dirname, 'fs', 'fixtures', 'test1'); + + // WHEN + const staging = new AssetStaging(stack, 'Asset', { + sourcePath: directory, + bundling: { + image: DockerImage.fromRegistry('alpine'), + command: [DockerStubCommand.SINGLE_FILE_WITHOUT_EXT], + outputType: BundlingOutput.SINGLE_FILE, + bundlingFileAccess: BundlingFileAccess.VOLUME_COPY, + }, + assetHashType: AssetHashType.SOURCE, // default + }); + + // THEN + const assembly = app.synth(); + expect(fs.readdirSync(assembly.directory)).toEqual([ + 'asset.93bd4079bff7440a725991ecf249416ae9ad73cb639f4a8d9e8f3ad8d491e89f', + 'asset.93bd4079bff7440a725991ecf249416ae9ad73cb639f4a8d9e8f3ad8d491e89f_noext', + 'cdk.out', + 'manifest.json', + 'stack.template.json', + 'tree.json', + ]); + expect(staging.packaging).toEqual(FileAssetPackaging.FILE); + expect(staging.isArchive).toEqual(false); + }); + + test('bundling that produces a single file with docker image copy variant and hash type CUSTOM', () => { + // GIVEN + const app = new App({ context: { [cxapi.NEW_STYLE_STACK_SYNTHESIS_CONTEXT]: false } }); + const stack = new Stack(app, 'stack'); + const directory = path.join(__dirname, 'fs', 'fixtures', 'test1'); + + // WHEN + const staging = new AssetStaging(stack, 'Asset', { + sourcePath: directory, + bundling: { + image: DockerImage.fromRegistry('alpine'), + command: [DockerStubCommand.SINGLE_FILE_WITHOUT_EXT], + outputType: BundlingOutput.SINGLE_FILE, + bundlingFileAccess: BundlingFileAccess.VOLUME_COPY, + }, + assetHashType: AssetHashType.CUSTOM, + assetHash: 'custom', + }); + + // THEN + const assembly = app.synth(); + expect(fs.readdirSync(assembly.directory)).toEqual([ + 'asset.53a51b4c68874a8e831e24e8982120be2a608f50b2e05edb8501143b3305baa8', + 'asset.53a51b4c68874a8e831e24e8982120be2a608f50b2e05edb8501143b3305baa8_noext', + 'cdk.out', + 'manifest.json', + 'stack.template.json', + 'tree.json', + ]); + expect(staging.packaging).toEqual(FileAssetPackaging.FILE); + expect(staging.isArchive).toEqual(false); + }); }); // Reads a docker stub and cleans the volume paths out of the stub.