diff --git a/packages/@aws-cdk/aws-s3-deployment/README.md b/packages/@aws-cdk/aws-s3-deployment/README.md index 1b86bb4fa92e4..62595042f652a 100644 --- a/packages/@aws-cdk/aws-s3-deployment/README.md +++ b/packages/@aws-cdk/aws-s3-deployment/README.md @@ -49,6 +49,10 @@ The following source types are supported for bucket deployments: - Local .zip file: `s3deploy.Source.asset('/path/to/local/file.zip')` - Local directory: `s3deploy.Source.asset('/path/to/local/directory')` - Another bucket: `s3deploy.Source.bucket(bucket, zipObjectKey)` +- String data: `s3deploy.Source.data('object-key.txt', 'hello, world!')` + (supports [deploy-time values](#data-with-deploy-time-values)) +- JSON data: `s3deploy.Source.jsonData('object-key.json', { json: 'object' })` + (supports [deploy-time values](#data-with-deploy-time-values)) To create a source from a single file, you can pass `AssetOptions` to exclude all but a single file: @@ -268,6 +272,34 @@ new s3deploy.BucketDeployment(this, 'DeployMeWithEfsStorage', { }); ``` +## Data with deploy-time values + +The content passed to `Source.data()` or `Source.jsonData()` can include +references that will get resolved only during deployment. + +For example: + +```ts +import * as sns from '@aws-cdk/aws-sns'; + +declare const destinationBucket: s3.Bucket; +declare const topic: sns.Topic; + +const appConfig = { + topic_arn: topic.topicArn, + base_url: 'https://my-endpoint', +}; + +new s3deploy.BucketDeployment(this, 'BucketDeployment', { + sources: [s3deploy.Source.jsonData('config.json', appConfig)], + destinationBucket, +}); +``` + +The value in `topic.topicArn` is a deploy-time value. It only gets resolved +during deployment by placing a marker in the generated source file and +substituting it when its deployed to the destination with the actual value. + ## Notes - This library uses an AWS CloudFormation custom resource which about 10MiB in @@ -282,7 +314,7 @@ new s3deploy.BucketDeployment(this, 'DeployMeWithEfsStorage', { be good enough: the custom resource will simply not run if the properties don't change. - If you use assets (`s3deploy.Source.asset()`) you don't need to worry - about this: the asset system will make sure that if the files have changed, + about this: the asset system will make sure that if the files have changed, the file name is unique and the deployment will run. ## Development diff --git a/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts b/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts index 76bbae6a4b290..b08011866f7ea 100644 --- a/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts +++ b/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts @@ -323,6 +323,10 @@ export class BucketDeployment extends CoreConstruct { })); } + // to avoid redundant stack updates, only include "SourceMarkers" if one of + // the sources actually has markers. + const hasMarkers = sources.some(source => source.markers); + const crUniqueId = `CustomResource${this.renderUniqueId(props.memoryLimit, props.vpc)}`; const cr = new cdk.CustomResource(this, crUniqueId, { serviceToken: handler.functionArn, @@ -330,6 +334,7 @@ export class BucketDeployment extends CoreConstruct { properties: { SourceBucketNames: sources.map(source => source.bucket.bucketName), SourceObjectKeys: sources.map(source => source.zipObjectKey), + SourceMarkers: hasMarkers ? sources.map(source => source.markers ?? {}) : undefined, DestinationBucketName: props.destinationBucket.bucketName, DestinationBucketKeyPrefix: props.destinationKeyPrefix, RetainOnDelete: props.retainOnDelete, diff --git a/packages/@aws-cdk/aws-s3-deployment/lib/lambda/index.py b/packages/@aws-cdk/aws-s3-deployment/lib/lambda/index.py index 466218b0fa134..14b1cc479e9d8 100644 --- a/packages/@aws-cdk/aws-s3-deployment/lib/lambda/index.py +++ b/packages/@aws-cdk/aws-s3-deployment/lib/lambda/index.py @@ -20,6 +20,7 @@ CFN_SUCCESS = "SUCCESS" CFN_FAILED = "FAILED" ENV_KEY_MOUNT_PATH = "MOUNT_PATH" +ENV_KEY_SKIP_CLEANUP = "SKIP_CLEANUP" CUSTOM_RESOURCE_OWNER_TAG = "aws-cdk:cr-owned" @@ -45,6 +46,7 @@ def cfn_error(message=None): try: source_bucket_names = props['SourceBucketNames'] source_object_keys = props['SourceObjectKeys'] + source_markers = props.get('SourceMarkers', None) dest_bucket_name = props['DestinationBucketName'] dest_bucket_prefix = props.get('DestinationBucketKeyPrefix', '') retain_on_delete = props.get('RetainOnDelete', "true") == "true" @@ -55,6 +57,11 @@ def cfn_error(message=None): exclude = props.get('Exclude', []) include = props.get('Include', []) + # backwards compatibility - if "SourceMarkers" is not specified, + # assume all sources have an empty market map + if source_markers is None: + source_markers = [{} for i in range(len(source_bucket_names))] + default_distribution_path = dest_bucket_prefix if not default_distribution_path.endswith("/"): default_distribution_path += "/" @@ -71,7 +78,7 @@ def cfn_error(message=None): if dest_bucket_prefix == "/": dest_bucket_prefix = "" - s3_source_zips = map(lambda name, key: "s3://%s/%s" % (name, key), source_bucket_names, source_object_keys) + s3_source_zips = list(map(lambda name, key: "s3://%s/%s" % (name, key), source_bucket_names, source_object_keys)) s3_dest = "s3://%s/%s" % (dest_bucket_name, dest_bucket_prefix) old_s3_dest = "s3://%s/%s" % (old_props.get("DestinationBucketName", ""), old_props.get("DestinationBucketKeyPrefix", "")) @@ -106,7 +113,7 @@ def cfn_error(message=None): aws_command("s3", "rm", old_s3_dest, "--recursive") if request_type == "Update" or request_type == "Create": - s3_deploy(s3_source_zips, s3_dest, user_metadata, system_metadata, prune, exclude, include) + s3_deploy(s3_source_zips, s3_dest, user_metadata, system_metadata, prune, exclude, include, source_markers) if distribution_id: cloudfront_invalidate(distribution_id, distribution_paths) @@ -120,7 +127,11 @@ def cfn_error(message=None): #--------------------------------------------------------------------------------------------------- # populate all files from s3_source_zips to a destination bucket -def s3_deploy(s3_source_zips, s3_dest, user_metadata, system_metadata, prune, exclude, include): +def s3_deploy(s3_source_zips, s3_dest, user_metadata, system_metadata, prune, exclude, include, source_markers): + # list lengths are equal + if len(s3_source_zips) != len(source_markers): + raise Exception("'source_markers' and 's3_source_zips' must be the same length") + # create a temporary working directory in /tmp or if enabled an attached efs volume if ENV_KEY_MOUNT_PATH in os.environ: workdir = os.getenv(ENV_KEY_MOUNT_PATH) + "/" + str(uuid4()) @@ -136,13 +147,16 @@ def s3_deploy(s3_source_zips, s3_dest, user_metadata, system_metadata, prune, ex try: # download the archive from the source and extract to "contents" - for s3_source_zip in s3_source_zips: + for i in range(len(s3_source_zips)): + s3_source_zip = s3_source_zips[i] + markers = source_markers[i] + archive=os.path.join(workdir, str(uuid4())) logger.info("archive: %s" % archive) aws_command("s3", "cp", s3_source_zip, archive) logger.info("| extracting archive to: %s\n" % contents_dir) - with ZipFile(archive, "r") as zip: - zip.extractall(contents_dir) + logger.info("| markers: %s" % markers) + extract_and_replace_markers(archive, contents_dir, markers) # sync from "contents" to destination @@ -163,7 +177,8 @@ def s3_deploy(s3_source_zips, s3_dest, user_metadata, system_metadata, prune, ex s3_command.extend(create_metadata_args(user_metadata, system_metadata)) aws_command(*s3_command) finally: - shutil.rmtree(workdir) + if not os.getenv(ENV_KEY_SKIP_CLEANUP): + shutil.rmtree(workdir) #--------------------------------------------------------------------------------------------------- # invalidate files in the CloudFront distribution edge caches @@ -257,3 +272,29 @@ def bucket_owned(bucketName, keyPrefix): logger.info("| error getting tags from bucket") logger.exception(e) return False + +# extract archive and replace markers in output files +def extract_and_replace_markers(archive, contents_dir, markers): + with ZipFile(archive, "r") as zip: + zip.extractall(contents_dir) + + # replace markers for this source + for file in zip.namelist(): + file_path = os.path.join(contents_dir, file) + if os.path.isdir(file_path): continue + replace_markers(file_path, markers) + +def replace_markers(filename, markers): + # convert the dict of string markers to binary markers + replace_tokens = dict([(k.encode('utf-8'), v.encode('utf-8')) for k, v in markers.items()]) + + outfile = filename + '.new' + with open(filename, 'rb') as fi, open(outfile, 'wb') as fo: + for line in fi: + for token in replace_tokens: + line = line.replace(token, replace_tokens[token]) + fo.write(line) + + # # delete the original file and rename the new one to the original + os.remove(filename) + os.rename(outfile, filename) \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3-deployment/lib/render-data.ts b/packages/@aws-cdk/aws-s3-deployment/lib/render-data.ts new file mode 100644 index 0000000000000..fa73c55dc2fba --- /dev/null +++ b/packages/@aws-cdk/aws-s3-deployment/lib/render-data.ts @@ -0,0 +1,81 @@ +import { Stack } from '@aws-cdk/core'; + +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + +export interface Content { + readonly text: string; + readonly markers: Record; +} + +/** + * Renders the given string data as deployable content with markers substituted + * for all "Ref" and "Fn::GetAtt" objects. + * + * @param scope Construct scope + * @param data The input data + * @returns The markered text (`text`) and a map that maps marker names to their + * values (`markers`). + */ +export function renderData(scope: Construct, data: string): Content { + const obj = Stack.of(scope).resolve(data); + if (typeof(obj) === 'string') { + return { text: obj, markers: {} }; + } + + if (typeof(obj) !== 'object') { + throw new Error(`Unexpected: after resolve() data must either be a string or a CloudFormation intrinsic. Got: ${JSON.stringify(obj)}`); + } + + let markerIndex = 0; + const markers: Record = {}; + const result = new Array(); + const fnJoin: FnJoin | undefined = obj['Fn::Join']; + + if (fnJoin) { + const sep = fnJoin[0]; + const parts = fnJoin[1]; + + if (sep !== '') { + throw new Error(`Unexpected "Fn::Join", expecting separator to be an empty string but got "${sep}"`); + } + + for (const part of parts) { + if (typeof (part) === 'string') { + result.push(part); + continue; + } + + if (typeof (part) === 'object') { + addMarker(part); + continue; + } + + throw new Error(`Unexpected "Fn::Join" part, expecting string or object but got ${typeof (part)}`); + } + + } else if (obj.Ref || obj['Fn::GetAtt']) { + addMarker(obj); + } else { + throw new Error('Unexpected: Expecting `resolve()` to return "Fn::Join", "Ref" or "Fn::GetAtt"'); + } + + function addMarker(part: Ref | GetAtt) { + const keys = Object.keys(part); + if (keys.length !== 1 || (keys[0] != 'Ref' && keys[0] != 'Fn::GetAtt')) { + throw new Error(`Invalid CloudFormation reference. "Ref" or "Fn::GetAtt". Got ${JSON.stringify(part)}`); + } + + const marker = `<>`; + result.push(marker); + markers[marker] = part; + } + + return { text: result.join(''), markers }; +} + +type FnJoin = [string, FnJoinPart[]]; +type FnJoinPart = string | Ref | GetAtt; +type Ref = { Ref: string }; +type GetAtt = { 'Fn::GetAtt': [string, string] }; \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3-deployment/lib/source.ts b/packages/@aws-cdk/aws-s3-deployment/lib/source.ts index 8c22f49d791e3..0a02dd4503b01 100644 --- a/packages/@aws-cdk/aws-s3-deployment/lib/source.ts +++ b/packages/@aws-cdk/aws-s3-deployment/lib/source.ts @@ -1,6 +1,10 @@ +import * as fs from 'fs'; +import { join, dirname } from 'path'; import * as iam from '@aws-cdk/aws-iam'; import * as s3 from '@aws-cdk/aws-s3'; import * as s3_assets from '@aws-cdk/aws-s3-assets'; +import { FileSystem, Stack } from '@aws-cdk/core'; +import { renderData } from './render-data'; // keep this import separate from other imports to reduce chance for merge conflicts with v2-main // eslint-disable-next-line no-duplicate-imports, import/order @@ -19,6 +23,12 @@ export interface SourceConfig { * An S3 object key in the source bucket that points to a zip file. */ readonly zipObjectKey: string; + + /** + * A set of markers to substitute in the source content. + * @default - no markers + */ + readonly markers?: Record; } /** @@ -50,6 +60,8 @@ export interface ISource { * Source.bucket(bucket, key) * Source.asset('/local/path/to/directory') * Source.asset('/local/path/to/a/file.zip') + * Source.data('hello/world/file.txt', 'Hello, world!') + * Source.data('config.json', { baz: topic.topicArn }) * */ export class Source { @@ -110,5 +122,51 @@ export class Source { }; } + /** + * Deploys an object with the specified string contents into the bucket. The + * content can include deploy-time values (such as `snsTopic.topicArn`) that + * will get resolved only during deployment. + * + * To store a JSON object use `Source.jsonData()`. + * + * @param objectKey The destination S3 object key (relative to the root of the + * S3 deployment). + * @param data The data to be stored in the object. + */ + public static data(objectKey: string, data: string): ISource { + return { + bind: (scope: Construct, context?: DeploymentSourceContext) => { + const workdir = FileSystem.mkdtemp('s3-deployment'); + const outputPath = join(workdir, objectKey); + const rendered = renderData(scope, data); + fs.mkdirSync(dirname(outputPath), { recursive: true }); + fs.writeFileSync(outputPath, rendered.text); + const asset = this.asset(workdir).bind(scope, context); + return { + bucket: asset.bucket, + zipObjectKey: asset.zipObjectKey, + markers: rendered.markers, + }; + }, + }; + } + + /** + * Deploys an object with the specified JSON object into the bucket. The + * object can include deploy-time values (such as `snsTopic.topicArn`) that + * will get resolved only during deployment. + * + * @param objectKey The destination S3 object key (relative to the root of the + * S3 deployment). + * @param obj A JSON object. + */ + public static jsonData(objectKey: string, obj: any): ISource { + return { + bind: (scope: Construct, context?: DeploymentSourceContext) => { + return Source.data(objectKey, Stack.of(scope).toJsonString(obj)).bind(scope, context); + }, + }; + } + private constructor() { } } diff --git a/packages/@aws-cdk/aws-s3-deployment/test/bucket-deployment.test.ts b/packages/@aws-cdk/aws-s3-deployment/test/bucket-deployment.test.ts index b017aa4e3a535..cff1a509c9b5b 100644 --- a/packages/@aws-cdk/aws-s3-deployment/test/bucket-deployment.test.ts +++ b/packages/@aws-cdk/aws-s3-deployment/test/bucket-deployment.test.ts @@ -1,3 +1,4 @@ +import { readFileSync } from 'fs'; import * as path from 'path'; import { Match, Template } from '@aws-cdk/assertions'; import * as cloudfront from '@aws-cdk/aws-cloudfront'; @@ -1060,3 +1061,82 @@ test('bucket has multiple deployments', () => { ], }); }); + +test('"SourceMarkers" is not included if none of the sources have markers', () => { + const stack = new cdk.Stack(); + const bucket = new s3.Bucket(stack, 'Bucket'); + new s3deploy.BucketDeployment(stack, 'DeployWithVpc3', { + sources: [s3deploy.Source.asset(path.join(__dirname, 'my-website'))], + destinationBucket: bucket, + }); + + const map = Template.fromStack(stack).findResources('Custom::CDKBucketDeployment'); + expect(map).toBeDefined(); + const resource = map[Object.keys(map)[0]]; + expect(Object.keys(resource.Properties)).toStrictEqual([ + 'ServiceToken', + 'SourceBucketNames', + 'SourceObjectKeys', + 'DestinationBucketName', + 'Prune', + ]); +}); + +test('Source.data() can be used to create a file with string contents', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'Test'); + const bucket = new s3.Bucket(stack, 'Bucket'); + + const source = s3deploy.Source.data('my/path.txt', 'hello, world'); + + new s3deploy.BucketDeployment(stack, 'DeployWithVpc3', { + sources: [source], + destinationBucket: bucket, + destinationKeyPrefix: '/x/z', + }); + + const result = app.synth(); + const content = readDataFile(result, 'c5b1c01fc092abf1da35f6772e7c507e566aaa69404025c080ba074c69741755', 'my/path.txt'); + expect(content).toStrictEqual('hello, world'); +}); + +test('Source.jsonData() can be used to create a file with a JSON object', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'Test'); + const bucket = new s3.Bucket(stack, 'Bucket'); + + const config = { + foo: 'bar', + sub: { + hello: bucket.bucketArn, + }, + }; + + new s3deploy.BucketDeployment(stack, 'DeployWithVpc3', { + sources: [s3deploy.Source.jsonData('app-config.json', config)], + destinationBucket: bucket, + }); + + const result = app.synth(); + const obj = JSON.parse(readDataFile(result, '6a9e1763f42401799363d87d16b238c89bf75a56f2a3f67498a3224573062b0c', 'app-config.json')); + expect(obj).toStrictEqual({ + foo: 'bar', + sub: { + hello: '<>', + }, + }); + + // verify marker is mapped to the bucket ARN in the resource props + Template.fromJSON(result.stacks[0].template).hasResourceProperties('Custom::CDKBucketDeployment', { + SourceMarkers: [ + { '<>': { 'Fn::GetAtt': ['Bucket83908E77', 'Arn'] } }, + ], + }); +}); + + +function readDataFile(casm: cxapi.CloudAssembly, assetId: string, filePath: string): string { + const asset = casm.stacks[0].assets.find(a => a.id === assetId); + if (!asset) { throw new Error('Asset not found'); } + return readFileSync(path.join(casm.directory, asset.path, filePath), 'utf-8'); +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3-deployment/test/content.test.ts b/packages/@aws-cdk/aws-s3-deployment/test/content.test.ts new file mode 100644 index 0000000000000..3cc27a2dc7262 --- /dev/null +++ b/packages/@aws-cdk/aws-s3-deployment/test/content.test.ts @@ -0,0 +1,122 @@ +import * as lambda from '@aws-cdk/aws-lambda'; +import * as s3 from '@aws-cdk/aws-s3'; +import { Lazy, Stack } from '@aws-cdk/core'; +import { Source } from '../lib'; +import { renderData } from '../lib/render-data'; + +test('simple string', () => { + const stack = new Stack(); + expect(renderData(stack, 'foo')).toStrictEqual({ + markers: {}, + text: 'foo', + }); +}); + +test('string with a "Ref" token', () => { + const stack = new Stack(); + const bucket = new s3.Bucket(stack, 'Bucket'); + + expect(renderData(stack, `foo-${bucket.bucketName}`)).toStrictEqual({ + text: 'foo-<>', + markers: { '<>': { Ref: 'Bucket83908E77' } }, + }); +}); + +test('string is a single "Ref" token', () => { + const stack = new Stack(); + const bucket = new s3.Bucket(stack, 'Bucket'); + + expect(renderData(stack, bucket.bucketName)).toStrictEqual({ + text: '<>', + markers: { '<>': { Ref: 'Bucket83908E77' } }, + }); +}); + +test('string is a single "Fn::GetAtt" token', () => { + const stack = new Stack(); + const bucket = new s3.Bucket(stack, 'Bucket'); + + expect(renderData(stack, bucket.bucketRegionalDomainName)).toStrictEqual({ + text: '<>', + markers: { '<>': { 'Fn::GetAtt': ['Bucket83908E77', 'RegionalDomainName'] } }, + }); +}); + +test('string with a "Fn::GetAtt" token', () => { + const stack = new Stack(); + const bucket = new s3.Bucket(stack, 'Bucket'); + + expect(renderData(stack, `foo-${bucket.bucketArn}`)).toStrictEqual({ + text: 'foo-<>', + markers: { '<>': { 'Fn::GetAtt': ['Bucket83908E77', 'Arn'] } }, + }); +}); + +test('multiple markers', () => { + const stack = new Stack(); + const bucket = new s3.Bucket(stack, 'Bucket'); + + expect(renderData(stack, `boom-${bucket.bucketName}-bam-${bucket.bucketArn}`)).toStrictEqual({ + text: 'boom-<>-bam-<>', + markers: { + '<>': { Ref: 'Bucket83908E77' }, + '<>': { 'Fn::GetAtt': ['Bucket83908E77', 'Arn'] }, + }, + }); +}); + +test('json-encoded string', () => { + const stack = new Stack(); + const bucket = new s3.Bucket(stack, 'Bucket'); + const json = { + BucketArn: bucket.bucketArn, + BucketName: bucket.bucketName, + }; + + expect(renderData(stack, JSON.stringify(json))).toStrictEqual({ + text: JSON.stringify({ BucketArn: '<>', BucketName: '<>' }), + markers: { + '<>': { 'Fn::GetAtt': ['Bucket83908E77', 'Arn'] }, + '<>': { Ref: 'Bucket83908E77' }, + }, + }); +}); + +test('markers are returned in the source config', () => { + const stack = new Stack(); + const handler = new lambda.Function(stack, 'Handler', { runtime: lambda.Runtime.NODEJS_14_X, code: lambda.Code.fromInline('foo'), handler: 'index.handler' }); + const actual = Source.data('file1.txt', `boom-${stack.account}`).bind(stack, { handlerRole: handler.role! }); + expect(actual.markers).toStrictEqual({ + '<>': { Ref: 'AWS::AccountId' }, + }); +}); + +test('lazy string which can be fully resolved', () => { + const stack = new Stack(); + + expect(renderData(stack, Lazy.string({ produce: () => 'resolved!' }))).toStrictEqual({ + text: 'resolved!', + markers: { }, + }); +}); + +test('lazy within a string which can be fully resolved', () => { + const stack = new Stack(); + const token = Lazy.string({ produce: () => 'resolved!' }); + + expect(renderData(stack, `hello, ${token}`)).toStrictEqual({ + text: 'hello, resolved!', + markers: { }, + }); +}); + +test('lazy string which resolves to something with a deploy-time value', () => { + const stack = new Stack(); + const token = Lazy.string({ produce: () => 'resolved!' }); + + expect(renderData(stack, `hello, ${token}`)).toStrictEqual({ + text: 'hello, resolved!', + markers: { }, + }); + +}); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment-cloudfront.expected.json b/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment-cloudfront.expected.json index e50759499b6cc..54fa70b01ed7f 100644 --- a/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment-cloudfront.expected.json +++ b/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment-cloudfront.expected.json @@ -114,7 +114,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters84e9b89449fe2573e51d08cc143e21116ed4608c6db56afffcb4ad85c8130709S3Bucket2C6C817C" + "Ref": "AssetParametersbe270bbdebe0851c887569796e3997437cca54ce86893ed94788500448e92824S3Bucket09A62232" }, "S3Key": { "Fn::Join": [ @@ -127,7 +127,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters84e9b89449fe2573e51d08cc143e21116ed4608c6db56afffcb4ad85c8130709S3VersionKeyFA215BD6" + "Ref": "AssetParametersbe270bbdebe0851c887569796e3997437cca54ce86893ed94788500448e92824S3VersionKeyA28118BE" } ] } @@ -140,7 +140,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters84e9b89449fe2573e51d08cc143e21116ed4608c6db56afffcb4ad85c8130709S3VersionKeyFA215BD6" + "Ref": "AssetParametersbe270bbdebe0851c887569796e3997437cca54ce86893ed94788500448e92824S3VersionKeyA28118BE" } ] } @@ -230,7 +230,7 @@ "Properties": { "Content": { "S3Bucket": { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7" + "Ref": "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3Bucket59E5CFEF" }, "S3Key": { "Fn::Join": [ @@ -243,7 +243,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" + "Ref": "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3VersionKey7EE70F5C" } ] } @@ -256,7 +256,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" + "Ref": "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3VersionKey7EE70F5C" } ] } @@ -471,7 +471,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters983c442a2fe823a8b4ebb18d241a5150ae15103dacbf3f038c7c6343e565aa4cS3Bucket1BE31DB0" + "Ref": "AssetParameters4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282eS3BucketC3F9EAA2" }, "S3Key": { "Fn::Join": [ @@ -484,7 +484,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters983c442a2fe823a8b4ebb18d241a5150ae15103dacbf3f038c7c6343e565aa4cS3VersionKeyDC38E49C" + "Ref": "AssetParameters4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282eS3VersionKey030ACBFF" } ] } @@ -497,7 +497,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters983c442a2fe823a8b4ebb18d241a5150ae15103dacbf3f038c7c6343e565aa4cS3VersionKeyDC38E49C" + "Ref": "AssetParameters4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282eS3VersionKey030ACBFF" } ] } @@ -529,41 +529,41 @@ } }, "Parameters": { - "AssetParameters84e9b89449fe2573e51d08cc143e21116ed4608c6db56afffcb4ad85c8130709S3Bucket2C6C817C": { + "AssetParametersbe270bbdebe0851c887569796e3997437cca54ce86893ed94788500448e92824S3Bucket09A62232": { "Type": "String", - "Description": "S3 bucket for asset \"84e9b89449fe2573e51d08cc143e21116ed4608c6db56afffcb4ad85c8130709\"" + "Description": "S3 bucket for asset \"be270bbdebe0851c887569796e3997437cca54ce86893ed94788500448e92824\"" }, - "AssetParameters84e9b89449fe2573e51d08cc143e21116ed4608c6db56afffcb4ad85c8130709S3VersionKeyFA215BD6": { + "AssetParametersbe270bbdebe0851c887569796e3997437cca54ce86893ed94788500448e92824S3VersionKeyA28118BE": { "Type": "String", - "Description": "S3 key for asset version \"84e9b89449fe2573e51d08cc143e21116ed4608c6db56afffcb4ad85c8130709\"" + "Description": "S3 key for asset version \"be270bbdebe0851c887569796e3997437cca54ce86893ed94788500448e92824\"" }, - "AssetParameters84e9b89449fe2573e51d08cc143e21116ed4608c6db56afffcb4ad85c8130709ArtifactHash17D48178": { + "AssetParametersbe270bbdebe0851c887569796e3997437cca54ce86893ed94788500448e92824ArtifactHash76F8FCF2": { "Type": "String", - "Description": "Artifact hash for asset \"84e9b89449fe2573e51d08cc143e21116ed4608c6db56afffcb4ad85c8130709\"" + "Description": "Artifact hash for asset \"be270bbdebe0851c887569796e3997437cca54ce86893ed94788500448e92824\"" }, - "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7": { + "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3Bucket59E5CFEF": { "Type": "String", - "Description": "S3 bucket for asset \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" + "Description": "S3 bucket for asset \"187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0\"" }, - "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F": { + "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3VersionKey7EE70F5C": { "Type": "String", - "Description": "S3 key for asset version \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" + "Description": "S3 key for asset version \"187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0\"" }, - "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68ArtifactHashD9A515C3": { + "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0ArtifactHash8F73A2B0": { "Type": "String", - "Description": "Artifact hash for asset \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" + "Description": "Artifact hash for asset \"187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0\"" }, - "AssetParameters983c442a2fe823a8b4ebb18d241a5150ae15103dacbf3f038c7c6343e565aa4cS3Bucket1BE31DB0": { + "AssetParameters4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282eS3BucketC3F9EAA2": { "Type": "String", - "Description": "S3 bucket for asset \"983c442a2fe823a8b4ebb18d241a5150ae15103dacbf3f038c7c6343e565aa4c\"" + "Description": "S3 bucket for asset \"4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282e\"" }, - "AssetParameters983c442a2fe823a8b4ebb18d241a5150ae15103dacbf3f038c7c6343e565aa4cS3VersionKeyDC38E49C": { + "AssetParameters4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282eS3VersionKey030ACBFF": { "Type": "String", - "Description": "S3 key for asset version \"983c442a2fe823a8b4ebb18d241a5150ae15103dacbf3f038c7c6343e565aa4c\"" + "Description": "S3 key for asset version \"4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282e\"" }, - "AssetParameters983c442a2fe823a8b4ebb18d241a5150ae15103dacbf3f038c7c6343e565aa4cArtifactHashBA6352EA": { + "AssetParameters4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282eArtifactHashE8052809": { "Type": "String", - "Description": "Artifact hash for asset \"983c442a2fe823a8b4ebb18d241a5150ae15103dacbf3f038c7c6343e565aa4c\"" + "Description": "Artifact hash for asset \"4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282e\"" }, "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eS3Bucket9CD8B20A": { "Type": "String", diff --git a/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment-data.expected.json b/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment-data.expected.json new file mode 100644 index 0000000000000..51b9a179e65c5 --- /dev/null +++ b/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment-data.expected.json @@ -0,0 +1,544 @@ +{ + "Resources": { + "Bucket83908E77": { + "Type": "AWS::S3::Bucket", + "Properties": { + "Tags": [ + { + "Key": "aws-cdk:cr-owned:deploy/here/:588fbb1f", + "Value": "true" + } + ] + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "DeployMeAwsCliLayer5F9219E9": { + "Type": "AWS::Lambda::LayerVersion", + "Properties": { + "Content": { + "S3Bucket": { + "Ref": "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3Bucket59E5CFEF" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3VersionKey7EE70F5C" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3VersionKey7EE70F5C" + } + ] + } + ] + } + ] + ] + } + }, + "Description": "/opt/awscli/aws" + } + }, + "DeployMeCustomResource4455EE35": { + "Type": "Custom::CDKBucketDeployment", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C81C01536", + "Arn" + ] + }, + "SourceBucketNames": [ + { + "Ref": "AssetParametersd09271be89b6cb0398f793b40c1531fd9b076aa92ba80b5e436914b1808fe18dS3BucketBC52CF96" + }, + { + "Ref": "AssetParameters0f14dedeaf4386031c978375cbda0f65d7b52b29452cabb8873eb8f0d0fa936bS3BucketE46D7C76" + }, + { + "Ref": "AssetParameters0d7be86c2a7d62be64fcbe2cbaa36c912a72d445022cc17c37af4f99f1b97a5aS3Bucket485B8F98" + } + ], + "SourceObjectKeys": [ + { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParametersd09271be89b6cb0398f793b40c1531fd9b076aa92ba80b5e436914b1808fe18dS3VersionKeyED6BBB32" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParametersd09271be89b6cb0398f793b40c1531fd9b076aa92ba80b5e436914b1808fe18dS3VersionKeyED6BBB32" + } + ] + } + ] + } + ] + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters0f14dedeaf4386031c978375cbda0f65d7b52b29452cabb8873eb8f0d0fa936bS3VersionKey9FBF1498" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters0f14dedeaf4386031c978375cbda0f65d7b52b29452cabb8873eb8f0d0fa936bS3VersionKey9FBF1498" + } + ] + } + ] + } + ] + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters0d7be86c2a7d62be64fcbe2cbaa36c912a72d445022cc17c37af4f99f1b97a5aS3VersionKeyC8D63B0C" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters0d7be86c2a7d62be64fcbe2cbaa36c912a72d445022cc17c37af4f99f1b97a5aS3VersionKeyC8D63B0C" + } + ] + } + ] + } + ] + ] + } + ], + "SourceMarkers": [ + {}, + { + "<>": { + "Ref": "Bucket83908E77" + } + }, + { + "<>": { + "Fn::GetAtt": [ + "Bucket83908E77", + "WebsiteURL" + ] + } + } + ], + "DestinationBucketName": { + "Ref": "Bucket83908E77" + }, + "DestinationBucketKeyPrefix": "deploy/here/", + "Prune": true + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRoleDefaultPolicy88902FDF": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::", + { + "Ref": "AssetParametersd09271be89b6cb0398f793b40c1531fd9b076aa92ba80b5e436914b1808fe18dS3BucketBC52CF96" + } + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::", + { + "Ref": "AssetParametersd09271be89b6cb0398f793b40c1531fd9b076aa92ba80b5e436914b1808fe18dS3BucketBC52CF96" + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::", + { + "Ref": "AssetParameters0f14dedeaf4386031c978375cbda0f65d7b52b29452cabb8873eb8f0d0fa936bS3BucketE46D7C76" + } + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::", + { + "Ref": "AssetParameters0f14dedeaf4386031c978375cbda0f65d7b52b29452cabb8873eb8f0d0fa936bS3BucketE46D7C76" + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::", + { + "Ref": "AssetParameters0d7be86c2a7d62be64fcbe2cbaa36c912a72d445022cc17c37af4f99f1b97a5aS3Bucket485B8F98" + } + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::", + { + "Ref": "AssetParameters0d7be86c2a7d62be64fcbe2cbaa36c912a72d445022cc17c37af4f99f1b97a5aS3Bucket485B8F98" + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", + "s3:Abort*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "Bucket83908E77", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "Bucket83908E77", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRoleDefaultPolicy88902FDF", + "Roles": [ + { + "Ref": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265" + } + ] + } + }, + "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C81C01536": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Ref": "AssetParameters4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282eS3BucketC3F9EAA2" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282eS3VersionKey030ACBFF" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282eS3VersionKey030ACBFF" + } + ] + } + ] + } + ] + ] + } + }, + "Role": { + "Fn::GetAtt": [ + "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265", + "Arn" + ] + }, + "Handler": "index.handler", + "Layers": [ + { + "Ref": "DeployMeAwsCliLayer5F9219E9" + } + ], + "Runtime": "python3.7", + "Timeout": 900 + }, + "DependsOn": [ + "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRoleDefaultPolicy88902FDF", + "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265" + ] + } + }, + "Parameters": { + "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3Bucket59E5CFEF": { + "Type": "String", + "Description": "S3 bucket for asset \"187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0\"" + }, + "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3VersionKey7EE70F5C": { + "Type": "String", + "Description": "S3 key for asset version \"187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0\"" + }, + "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0ArtifactHash8F73A2B0": { + "Type": "String", + "Description": "Artifact hash for asset \"187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0\"" + }, + "AssetParameters4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282eS3BucketC3F9EAA2": { + "Type": "String", + "Description": "S3 bucket for asset \"4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282e\"" + }, + "AssetParameters4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282eS3VersionKey030ACBFF": { + "Type": "String", + "Description": "S3 key for asset version \"4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282e\"" + }, + "AssetParameters4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282eArtifactHashE8052809": { + "Type": "String", + "Description": "Artifact hash for asset \"4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282e\"" + }, + "AssetParametersd09271be89b6cb0398f793b40c1531fd9b076aa92ba80b5e436914b1808fe18dS3BucketBC52CF96": { + "Type": "String", + "Description": "S3 bucket for asset \"d09271be89b6cb0398f793b40c1531fd9b076aa92ba80b5e436914b1808fe18d\"" + }, + "AssetParametersd09271be89b6cb0398f793b40c1531fd9b076aa92ba80b5e436914b1808fe18dS3VersionKeyED6BBB32": { + "Type": "String", + "Description": "S3 key for asset version \"d09271be89b6cb0398f793b40c1531fd9b076aa92ba80b5e436914b1808fe18d\"" + }, + "AssetParametersd09271be89b6cb0398f793b40c1531fd9b076aa92ba80b5e436914b1808fe18dArtifactHashF6480042": { + "Type": "String", + "Description": "Artifact hash for asset \"d09271be89b6cb0398f793b40c1531fd9b076aa92ba80b5e436914b1808fe18d\"" + }, + "AssetParameters0f14dedeaf4386031c978375cbda0f65d7b52b29452cabb8873eb8f0d0fa936bS3BucketE46D7C76": { + "Type": "String", + "Description": "S3 bucket for asset \"0f14dedeaf4386031c978375cbda0f65d7b52b29452cabb8873eb8f0d0fa936b\"" + }, + "AssetParameters0f14dedeaf4386031c978375cbda0f65d7b52b29452cabb8873eb8f0d0fa936bS3VersionKey9FBF1498": { + "Type": "String", + "Description": "S3 key for asset version \"0f14dedeaf4386031c978375cbda0f65d7b52b29452cabb8873eb8f0d0fa936b\"" + }, + "AssetParameters0f14dedeaf4386031c978375cbda0f65d7b52b29452cabb8873eb8f0d0fa936bArtifactHash1774126C": { + "Type": "String", + "Description": "Artifact hash for asset \"0f14dedeaf4386031c978375cbda0f65d7b52b29452cabb8873eb8f0d0fa936b\"" + }, + "AssetParameters0d7be86c2a7d62be64fcbe2cbaa36c912a72d445022cc17c37af4f99f1b97a5aS3Bucket485B8F98": { + "Type": "String", + "Description": "S3 bucket for asset \"0d7be86c2a7d62be64fcbe2cbaa36c912a72d445022cc17c37af4f99f1b97a5a\"" + }, + "AssetParameters0d7be86c2a7d62be64fcbe2cbaa36c912a72d445022cc17c37af4f99f1b97a5aS3VersionKeyC8D63B0C": { + "Type": "String", + "Description": "S3 key for asset version \"0d7be86c2a7d62be64fcbe2cbaa36c912a72d445022cc17c37af4f99f1b97a5a\"" + }, + "AssetParameters0d7be86c2a7d62be64fcbe2cbaa36c912a72d445022cc17c37af4f99f1b97a5aArtifactHashA3962596": { + "Type": "String", + "Description": "Artifact hash for asset \"0d7be86c2a7d62be64fcbe2cbaa36c912a72d445022cc17c37af4f99f1b97a5a\"" + } + }, + "Outputs": { + "BucketName": { + "Value": { + "Ref": "Bucket83908E77" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment-data.ts b/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment-data.ts new file mode 100644 index 0000000000000..45db0762527fd --- /dev/null +++ b/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment-data.ts @@ -0,0 +1,21 @@ +import { Bucket } from '@aws-cdk/aws-s3'; +import { App, CfnOutput, Stack } from '@aws-cdk/core'; +import { BucketDeployment, Source } from '../lib'; + +const app = new App(); +const stack = new Stack(app, 'TestBucketDeploymentContent'); +const bucket = new Bucket(stack, 'Bucket'); + +const file1 = Source.data('file1.txt', 'boom'); +const file2 = Source.data('path/to/file2.txt', `bam! ${bucket.bucketName}`); +const file3 = Source.jsonData('my/config.json', { website_url: bucket.bucketWebsiteUrl }); + +new BucketDeployment(stack, 'DeployMe', { + destinationBucket: bucket, + sources: [file1, file2, file3], + destinationKeyPrefix: 'deploy/here/', +}); + +new CfnOutput(stack, 'BucketName', { value: bucket.bucketName }); + +app.synth(); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment.expected.json b/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment.expected.json index 51473300ac82b..bf4c180c9b559 100644 --- a/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment.expected.json +++ b/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment.expected.json @@ -129,7 +129,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters84e9b89449fe2573e51d08cc143e21116ed4608c6db56afffcb4ad85c8130709S3Bucket2C6C817C" + "Ref": "AssetParametersbe270bbdebe0851c887569796e3997437cca54ce86893ed94788500448e92824S3Bucket09A62232" }, "S3Key": { "Fn::Join": [ @@ -142,7 +142,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters84e9b89449fe2573e51d08cc143e21116ed4608c6db56afffcb4ad85c8130709S3VersionKeyFA215BD6" + "Ref": "AssetParametersbe270bbdebe0851c887569796e3997437cca54ce86893ed94788500448e92824S3VersionKeyA28118BE" } ] } @@ -155,7 +155,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters84e9b89449fe2573e51d08cc143e21116ed4608c6db56afffcb4ad85c8130709S3VersionKeyFA215BD6" + "Ref": "AssetParametersbe270bbdebe0851c887569796e3997437cca54ce86893ed94788500448e92824S3VersionKeyA28118BE" } ] } @@ -197,7 +197,7 @@ "Properties": { "Content": { "S3Bucket": { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7" + "Ref": "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3Bucket59E5CFEF" }, "S3Key": { "Fn::Join": [ @@ -210,7 +210,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" + "Ref": "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3VersionKey7EE70F5C" } ] } @@ -223,7 +223,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" + "Ref": "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3VersionKey7EE70F5C" } ] } @@ -498,7 +498,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters983c442a2fe823a8b4ebb18d241a5150ae15103dacbf3f038c7c6343e565aa4cS3Bucket1BE31DB0" + "Ref": "AssetParameters4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282eS3BucketC3F9EAA2" }, "S3Key": { "Fn::Join": [ @@ -511,7 +511,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters983c442a2fe823a8b4ebb18d241a5150ae15103dacbf3f038c7c6343e565aa4cS3VersionKeyDC38E49C" + "Ref": "AssetParameters4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282eS3VersionKey030ACBFF" } ] } @@ -524,7 +524,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters983c442a2fe823a8b4ebb18d241a5150ae15103dacbf3f038c7c6343e565aa4cS3VersionKeyDC38E49C" + "Ref": "AssetParameters4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282eS3VersionKey030ACBFF" } ] } @@ -1073,7 +1073,7 @@ "Properties": { "Content": { "S3Bucket": { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7" + "Ref": "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3Bucket59E5CFEF" }, "S3Key": { "Fn::Join": [ @@ -1086,7 +1086,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" + "Ref": "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3VersionKey7EE70F5C" } ] } @@ -1099,7 +1099,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" + "Ref": "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3VersionKey7EE70F5C" } ] } @@ -1612,7 +1612,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters983c442a2fe823a8b4ebb18d241a5150ae15103dacbf3f038c7c6343e565aa4cS3Bucket1BE31DB0" + "Ref": "AssetParameters4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282eS3BucketC3F9EAA2" }, "S3Key": { "Fn::Join": [ @@ -1625,7 +1625,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters983c442a2fe823a8b4ebb18d241a5150ae15103dacbf3f038c7c6343e565aa4cS3VersionKeyDC38E49C" + "Ref": "AssetParameters4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282eS3VersionKey030ACBFF" } ] } @@ -1638,7 +1638,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters983c442a2fe823a8b4ebb18d241a5150ae15103dacbf3f038c7c6343e565aa4cS3VersionKeyDC38E49C" + "Ref": "AssetParameters4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282eS3VersionKey030ACBFF" } ] } @@ -1818,7 +1818,7 @@ "Properties": { "Content": { "S3Bucket": { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7" + "Ref": "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3Bucket59E5CFEF" }, "S3Key": { "Fn::Join": [ @@ -1831,7 +1831,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" + "Ref": "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3VersionKey7EE70F5C" } ] } @@ -1844,7 +1844,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" + "Ref": "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3VersionKey7EE70F5C" } ] } @@ -2008,7 +2008,7 @@ "Properties": { "Content": { "S3Bucket": { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7" + "Ref": "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3Bucket59E5CFEF" }, "S3Key": { "Fn::Join": [ @@ -2021,7 +2021,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" + "Ref": "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3VersionKey7EE70F5C" } ] } @@ -2034,7 +2034,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" + "Ref": "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3VersionKey7EE70F5C" } ] } @@ -2119,7 +2119,7 @@ "Properties": { "Content": { "S3Bucket": { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7" + "Ref": "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3Bucket59E5CFEF" }, "S3Key": { "Fn::Join": [ @@ -2132,7 +2132,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" + "Ref": "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3VersionKey7EE70F5C" } ] } @@ -2145,7 +2145,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" + "Ref": "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3VersionKey7EE70F5C" } ] } @@ -2221,7 +2221,7 @@ "Properties": { "Content": { "S3Bucket": { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7" + "Ref": "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3Bucket59E5CFEF" }, "S3Key": { "Fn::Join": [ @@ -2234,7 +2234,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" + "Ref": "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3VersionKey7EE70F5C" } ] } @@ -2247,7 +2247,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F" + "Ref": "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3VersionKey7EE70F5C" } ] } @@ -2323,41 +2323,41 @@ } }, "Parameters": { - "AssetParameters84e9b89449fe2573e51d08cc143e21116ed4608c6db56afffcb4ad85c8130709S3Bucket2C6C817C": { + "AssetParametersbe270bbdebe0851c887569796e3997437cca54ce86893ed94788500448e92824S3Bucket09A62232": { "Type": "String", - "Description": "S3 bucket for asset \"84e9b89449fe2573e51d08cc143e21116ed4608c6db56afffcb4ad85c8130709\"" + "Description": "S3 bucket for asset \"be270bbdebe0851c887569796e3997437cca54ce86893ed94788500448e92824\"" }, - "AssetParameters84e9b89449fe2573e51d08cc143e21116ed4608c6db56afffcb4ad85c8130709S3VersionKeyFA215BD6": { + "AssetParametersbe270bbdebe0851c887569796e3997437cca54ce86893ed94788500448e92824S3VersionKeyA28118BE": { "Type": "String", - "Description": "S3 key for asset version \"84e9b89449fe2573e51d08cc143e21116ed4608c6db56afffcb4ad85c8130709\"" + "Description": "S3 key for asset version \"be270bbdebe0851c887569796e3997437cca54ce86893ed94788500448e92824\"" }, - "AssetParameters84e9b89449fe2573e51d08cc143e21116ed4608c6db56afffcb4ad85c8130709ArtifactHash17D48178": { + "AssetParametersbe270bbdebe0851c887569796e3997437cca54ce86893ed94788500448e92824ArtifactHash76F8FCF2": { "Type": "String", - "Description": "Artifact hash for asset \"84e9b89449fe2573e51d08cc143e21116ed4608c6db56afffcb4ad85c8130709\"" + "Description": "Artifact hash for asset \"be270bbdebe0851c887569796e3997437cca54ce86893ed94788500448e92824\"" }, - "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7": { + "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3Bucket59E5CFEF": { "Type": "String", - "Description": "S3 bucket for asset \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" + "Description": "S3 bucket for asset \"187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0\"" }, - "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F": { + "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0S3VersionKey7EE70F5C": { "Type": "String", - "Description": "S3 key for asset version \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" + "Description": "S3 key for asset version \"187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0\"" }, - "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68ArtifactHashD9A515C3": { + "AssetParameters187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0ArtifactHash8F73A2B0": { "Type": "String", - "Description": "Artifact hash for asset \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\"" + "Description": "Artifact hash for asset \"187e7a21dd5d55d36f1f45007ff6bbc5713cb0866ca86224c0f1f86b3d1e76a0\"" }, - "AssetParameters983c442a2fe823a8b4ebb18d241a5150ae15103dacbf3f038c7c6343e565aa4cS3Bucket1BE31DB0": { + "AssetParameters4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282eS3BucketC3F9EAA2": { "Type": "String", - "Description": "S3 bucket for asset \"983c442a2fe823a8b4ebb18d241a5150ae15103dacbf3f038c7c6343e565aa4c\"" + "Description": "S3 bucket for asset \"4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282e\"" }, - "AssetParameters983c442a2fe823a8b4ebb18d241a5150ae15103dacbf3f038c7c6343e565aa4cS3VersionKeyDC38E49C": { + "AssetParameters4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282eS3VersionKey030ACBFF": { "Type": "String", - "Description": "S3 key for asset version \"983c442a2fe823a8b4ebb18d241a5150ae15103dacbf3f038c7c6343e565aa4c\"" + "Description": "S3 key for asset version \"4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282e\"" }, - "AssetParameters983c442a2fe823a8b4ebb18d241a5150ae15103dacbf3f038c7c6343e565aa4cArtifactHashBA6352EA": { + "AssetParameters4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282eArtifactHashE8052809": { "Type": "String", - "Description": "Artifact hash for asset \"983c442a2fe823a8b4ebb18d241a5150ae15103dacbf3f038c7c6343e565aa4c\"" + "Description": "Artifact hash for asset \"4e09e63403b235ffda9db09367996f2d4c9fe1f7aa19b402908d8221614a282e\"" }, "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eS3Bucket9CD8B20A": { "Type": "String", diff --git a/packages/@aws-cdk/aws-s3-deployment/test/lambda/.gitignore b/packages/@aws-cdk/aws-s3-deployment/test/lambda/.gitignore new file mode 100644 index 0000000000000..3a5f4f826d408 --- /dev/null +++ b/packages/@aws-cdk/aws-s3-deployment/test/lambda/.gitignore @@ -0,0 +1,4 @@ +# symlinked by debug.sh +index.py +__pycache__ +aws.out \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3-deployment/test/lambda/Dockerfile.debug b/packages/@aws-cdk/aws-s3-deployment/test/lambda/Dockerfile.debug new file mode 100644 index 0000000000000..791343ea39cf8 --- /dev/null +++ b/packages/@aws-cdk/aws-s3-deployment/test/lambda/Dockerfile.debug @@ -0,0 +1,6 @@ +FROM public.ecr.aws/lambda/python:latest + +# install boto3, which is available on Lambda +RUN pip3 install boto3 + +ENTRYPOINT [ "/bin/bash" ] \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3-deployment/test/lambda/debug.sh b/packages/@aws-cdk/aws-s3-deployment/test/lambda/debug.sh new file mode 100755 index 0000000000000..459116e55ab84 --- /dev/null +++ b/packages/@aws-cdk/aws-s3-deployment/test/lambda/debug.sh @@ -0,0 +1,10 @@ +#!/bin/sh +# starts a debugging container for the python lambda function and tests + +tag="s3-deployment-test-environment" +docker build -f Dockerfile.debug -t $tag . + +echo "To iterate, run python3 ./test.py inside the container (source code is mapped into the container)." + +ln -fs /opt/lambda/index.py index.py +docker run -v $PWD:/opt/awscli -v $PWD/../../lib/lambda:/opt/lambda --workdir /opt/awscli -it $tag \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3-deployment/test/lambda/test.py b/packages/@aws-cdk/aws-s3-deployment/test/lambda/test.py index 30b59c26374f3..b999aa5a4e797 100644 --- a/packages/@aws-cdk/aws-s3-deployment/test/lambda/test.py +++ b/packages/@aws-cdk/aws-s3-deployment/test/lambda/test.py @@ -7,6 +7,7 @@ import traceback import logging import botocore +import tempfile from botocore.vendored import requests from botocore.exceptions import ClientError from unittest.mock import MagicMock @@ -584,7 +585,45 @@ def mock_make_api_call(self, operation_name, kwarg): self.assertAwsCommands( ["s3", "rm", "s3:///", "--recursive"] ) + + def test_replace_markers(self): + index.extract_and_replace_markers("test.zip", "/tmp/out", { + "_marker2_": "boom-marker2-replaced", + "_marker1_": "<>", + }) + + # assert that markers were replaced in the output + with open("/tmp/out/subfolder/boom.txt", "r") as file: + self.assertEqual(file.read().rstrip(), "Another <> file with boom-marker2-replaced hey!\nLine 2 with <> again :-)") + + with open("/tmp/out/test.txt") as file: + self.assertEqual(file.read().rstrip(), "Hello, <> world") + def test_marker_substitution(self): + outdir = tempfile.mkdtemp() + + invoke_handler("Create", { + "SourceBucketNames": ["", ""], + "SourceObjectKeys": ["", ""], + "DestinationBucketName": "", + "Prune": "false", + "SourceMarkers": [ + { "_marker1_": "value1-source1", "_marker2_": "value2-source1" }, + { "_marker1_": "value1-source2" }, + ], + }, outdir=outdir) + + # outdir is expected to have a single directory that contains the workdir + files = os.listdir(outdir) + self.assertEqual(len(files), 1) # defensive + + workdir = os.path.join(outdir, files[0], "contents") + + with open(os.path.join(workdir, "test.txt"), "r") as file: + self.assertEqual(file.read().rstrip(), "Hello, value1-source2 world") + + with open(os.path.join(workdir, "subfolder", "boom.txt"), "r") as file: + self.assertEqual(file.read().rstrip(), "Another value1-source2 file with _marker2_ hey!\nLine 2 with value1-source2 again :-)") # asserts that a given list of "aws xxx" commands have been invoked (in order) @@ -609,7 +648,7 @@ def read_aws_out(): # requestType: CloudFormation request type ("Create", "Update", "Delete") # resourceProps: map to pass to "ResourceProperties" # expected_status: "SUCCESS" or "FAILED" -def invoke_handler(requestType, resourceProps, old_resource_props=None, physical_id=None, expected_status='SUCCESS'): +def invoke_handler(requestType, resourceProps, old_resource_props=None, physical_id=None, expected_status='SUCCESS', outdir=None): response_url = 'http://' event={ @@ -636,6 +675,11 @@ class ResponseMock: context = ContextMock() index.urlopen = MagicMock(return_value=ResponseMock()) + # control the output directory and skip cleanup so we can examine the output + if outdir: + os.environ[index.ENV_KEY_MOUNT_PATH] = outdir + os.environ[index.ENV_KEY_SKIP_CLEANUP] = "1" + #-------------------- # invoke the handler #-------------------- diff --git a/packages/@aws-cdk/aws-s3-deployment/test/lambda/test.sh b/packages/@aws-cdk/aws-s3-deployment/test/lambda/test.sh index a094c8ae16cfa..8f0c0d2b473b7 100755 --- a/packages/@aws-cdk/aws-s3-deployment/test/lambda/test.sh +++ b/packages/@aws-cdk/aws-s3-deployment/test/lambda/test.sh @@ -6,6 +6,9 @@ set -e scriptdir=$(cd $(dirname $0) && pwd) +rm -f ${scriptdir}/index.py +rm -fr ${scriptdir}/__pycache__ + # prepare staging directory staging=$(mktemp -d) mkdir -p ${staging} diff --git a/packages/@aws-cdk/aws-s3-deployment/test/lambda/test.zip b/packages/@aws-cdk/aws-s3-deployment/test/lambda/test.zip index 56829f65a2681..784486f7d04cd 100644 Binary files a/packages/@aws-cdk/aws-s3-deployment/test/lambda/test.zip and b/packages/@aws-cdk/aws-s3-deployment/test/lambda/test.zip differ