diff --git a/allowed-breaking-changes.txt b/allowed-breaking-changes.txt index b958112512f2e..a2441dc2607d1 100644 --- a/allowed-breaking-changes.txt +++ b/allowed-breaking-changes.txt @@ -23,3 +23,7 @@ incompatible-argument:@aws-cdk/aws-apigateway.ProxyResource.addProxy incompatible-argument:@aws-cdk/aws-apigateway.Resource.addProxy incompatible-argument:@aws-cdk/aws-apigateway.ResourceBase.addProxy incompatible-argument:@aws-cdk/aws-apigateway.IResource.addProxy +removed:@aws-cdk/core.ConstructNode.addReference +removed:@aws-cdk/core.ConstructNode.references +removed:@aws-cdk/core.OutgoingReference + diff --git a/packages/@aws-cdk/assert/lib/expect.ts b/packages/@aws-cdk/assert/lib/expect.ts index 23b8b128dc4ed..466062c21fa0e 100644 --- a/packages/@aws-cdk/assert/lib/expect.ts +++ b/packages/@aws-cdk/assert/lib/expect.ts @@ -5,6 +5,6 @@ import { SynthUtils } from './synth-utils'; export function expect(stack: api.CloudFormationStackArtifact | cdk.Stack, skipValidation = false): StackInspector { // if this is already a synthesized stack, then just inspect it. - const artifact = stack instanceof api.CloudFormationStackArtifact ? stack : SynthUtils.synthesize(stack, { skipValidation }); + const artifact = stack instanceof api.CloudFormationStackArtifact ? stack : SynthUtils._synthesizeWithNested(stack, { skipValidation }); return new StackInspector(artifact); } diff --git a/packages/@aws-cdk/assert/lib/inspector.ts b/packages/@aws-cdk/assert/lib/inspector.ts index e450c2e20ee39..586670e1383c0 100644 --- a/packages/@aws-cdk/assert/lib/inspector.ts +++ b/packages/@aws-cdk/assert/lib/inspector.ts @@ -27,11 +27,20 @@ export abstract class Inspector { } export class StackInspector extends Inspector { - constructor(public readonly stack: api.CloudFormationStackArtifact) { + + private readonly template: { [key: string]: any }; + + constructor(public readonly stack: api.CloudFormationStackArtifact | object) { super(); + + this.template = stack instanceof api.CloudFormationStackArtifact ? stack.template : stack; } public at(path: string | string[]): StackPathInspector { + if (!(this.stack instanceof api.CloudFormationStackArtifact)) { + throw new Error(`Cannot use "expect(stack).at(path)" for a raw template, only CloudFormationStackArtifact`); + } + const strPath = typeof path === 'string' ? path : path.join('/'); return new StackPathInspector(this.stack, strPath); } @@ -41,7 +50,7 @@ export class StackInspector extends Inspector { } public get value(): { [key: string]: any } { - return this.stack.template; + return this.template; } } diff --git a/packages/@aws-cdk/assert/lib/synth-utils.ts b/packages/@aws-cdk/assert/lib/synth-utils.ts index 6fe64e3b25cba..330a7936874fd 100644 --- a/packages/@aws-cdk/assert/lib/synth-utils.ts +++ b/packages/@aws-cdk/assert/lib/synth-utils.ts @@ -1,14 +1,16 @@ -import { ConstructNode, Stack, SynthesisOptions } from '@aws-cdk/core'; +import { App, ConstructNode, Stack, SynthesisOptions } from '@aws-cdk/core'; import cxapi = require('@aws-cdk/cx-api'); +import fs = require('fs'); +import path = require('path'); export class SynthUtils { - /** - * Synthesizes the stack and returns a `CloudFormationStackArtifact` which can be inspected. - */ public static synthesize(stack: Stack, options: SynthesisOptions = { }): cxapi.CloudFormationStackArtifact { // always synthesize against the root (be it an App or whatever) so all artifacts will be included const root = stack.node.root; - const assembly = ConstructNode.synth(root.node, options); + + // if the root is an app, invoke "synth" to avoid double synthesis + const assembly = root instanceof App ? root.synth() : ConstructNode.synth(root.node, options); + return assembly.getStack(stack.stackName); } @@ -16,14 +18,19 @@ export class SynthUtils { * Synthesizes the stack and returns the resulting CloudFormation template. */ public static toCloudFormation(stack: Stack, options: SynthesisOptions = { }): any { - return this.synthesize(stack, options).template; + const synth = this._synthesizeWithNested(stack, options); + if (synth instanceof cxapi.CloudFormationStackArtifact) { + return synth.template; + } else { + return synth; + } } /** * @returns Returns a subset of the synthesized CloudFormation template (only specific resource types). */ public static subset(stack: Stack, options: SubsetOptions): any { - const template = SynthUtils.synthesize(stack).template; + const template = this.toCloudFormation(stack); if (template.Resources) { for (const [key, resource] of Object.entries(template.Resources)) { if (options.resourceTypes && !options.resourceTypes.includes((resource as any).Type)) { @@ -34,6 +41,28 @@ export class SynthUtils { return template; } + + /** + * Synthesizes the stack and returns a `CloudFormationStackArtifact` which can be inspected. + * Supports nested stacks as well as normal stacks. + * + * @return CloudFormationStackArtifact for normal stacks or the actual template for nested stacks + * @internal + */ + public static _synthesizeWithNested(stack: Stack, options: SynthesisOptions = { }): cxapi.CloudFormationStackArtifact | object { + // always synthesize against the root (be it an App or whatever) so all artifacts will be included + const root = stack.node.root; + + // if the root is an app, invoke "synth" to avoid double synthesis + const assembly = root instanceof App ? root.synth() : ConstructNode.synth(root.node, options); + + // if this is a nested stack (it has a parent), then just read the template as a string + if (stack.parentStack) { + return JSON.parse(fs.readFileSync(path.join(assembly.directory, stack.templateFile)).toString('utf-8')); + } + + return assembly.getStack(stack.stackName); + } } export interface SubsetOptions { diff --git a/packages/@aws-cdk/aws-cloudformation/README.md b/packages/@aws-cdk/aws-cloudformation/README.md index 0acccd7a6a676..9fe486f1b168c 100644 --- a/packages/@aws-cdk/aws-cloudformation/README.md +++ b/packages/@aws-cdk/aws-cloudformation/README.md @@ -51,3 +51,44 @@ See the following section of the docs on details to write Custom Resources: * [Introduction](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-custom-resources.html) * [Reference](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/crpg-ref.html) * [Code Reference](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html) + +### Nested Stacks + +[Nested stacks](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-nested-stacks.html) are stacks created as part of other stacks. You create a nested stack within another stack by using the `NestedStack` construct. + +As your infrastructure grows, common patterns can emerge in which you declare the same components in multiple templates. You can separate out these common components and create dedicated templates for them. Then use the resource in your template to reference other templates, creating nested stacks. + +For example, assume that you have a load balancer configuration that you use for most of your stacks. Instead of copying and pasting the same configurations into your templates, you can create a dedicated template for the load balancer. Then, you just use the resource to reference that template from within other templates. + +The following example will define a single top-level stack that contains two nested stacks: each one with a single Amazon S3 bucket: + +```ts +import { Stack, Construct, StackProps } from '@aws-cdk/core'; +import cfn = require('@aws-cdk/aws-cloudformation'); +import s3 = require('@aws-cdk/aws-s3'); + +class MyNestedStack extends cfn.NestedStack { + constructor(scope: Construct, id: string, props: cfn.NestedStackProps) { + super(scope, id, props); + + new s3.Bucket(this, 'NestedBucket'); + } +} + +class MyParentStack extends Stack { + constructor(scope: Construct, id: string, props: StackProps) { + super(scope, id, props); + + new MyNestedStack(scope, 'Nested1'); + new MyNestedStack(scope, 'Nested2'); + } +} +``` + +Resources references across nested/parent boundaries (even with multiple levels of nesting) will be wired by the AWS CDK +through CloudFormation parameters and outputs. When a resource from a parent stack is referenced by a nested stack, +a CloudFormation parameter will automatically be added to the nested stack and assigned from the parent; when a resource +from a nested stack is referenced by a parent stack, a CloudFormation output will be automatically be added to the +nested stack and referenced using `Fn::GetAtt "Outputs.Xxx"` from the parent. + +Nested stacks also support the use of Docker image and file assets. diff --git a/packages/@aws-cdk/aws-cloudformation/lib/index.ts b/packages/@aws-cdk/aws-cloudformation/lib/index.ts index 3d4971d16542b..7768daaaa624e 100644 --- a/packages/@aws-cdk/aws-cloudformation/lib/index.ts +++ b/packages/@aws-cdk/aws-cloudformation/lib/index.ts @@ -1,5 +1,6 @@ export * from './cloud-formation-capabilities'; export * from './custom-resource'; +export * from './nested-stack'; // AWS::CloudFormation CloudFormation Resources: export * from './cloudformation.generated'; diff --git a/packages/@aws-cdk/aws-cloudformation/lib/nested-stack.ts b/packages/@aws-cdk/aws-cloudformation/lib/nested-stack.ts new file mode 100644 index 0000000000000..1b1061fcfe7fc --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/lib/nested-stack.ts @@ -0,0 +1,228 @@ +import sns = require('@aws-cdk/aws-sns'); +import { Aws, CfnOutput, CfnParameter, Construct, Duration, Fn, IResolvable, IResolveContext, Lazy, Reference, Stack, Token } from '@aws-cdk/core'; +import { CfnStack } from './cloudformation.generated'; + +const NESTED_STACK_SYMBOL = Symbol.for('@aws-cdk/aws-cloudformation.NestedStack'); + +/** + * @experimental + */ +export interface NestedStackProps { + + /** + * The set value pairs that represent the parameters passed to CloudFormation + * when this nested stack is created. Each parameter has a name corresponding + * to a parameter defined in the embedded template and a value representing + * the value that you want to set for the parameter. + * + * The nested stack construct will automatically synthesize parameters in order + * to bind references from the parent stack(s) into the nested stack. + * + * @default - no user-defined parameters are passed to the nested stack + */ + readonly parameters?: { [key: string]: string }; + + /** + * The length of time that CloudFormation waits for the nested stack to reach + * the CREATE_COMPLETE state. + * + * When CloudFormation detects that the nested stack has reached the + * CREATE_COMPLETE state, it marks the nested stack resource as + * CREATE_COMPLETE in the parent stack and resumes creating the parent stack. + * If the timeout period expires before the nested stack reaches + * CREATE_COMPLETE, CloudFormation marks the nested stack as failed and rolls + * back both the nested stack and parent stack. + * + * @default - no timeout + */ + readonly timeout?: Duration; + + /** + * The Simple Notification Service (SNS) topics to publish stack related + * events. + * + * @default - notifications are not sent for this stack. + */ + readonly notifications?: sns.ITopic[]; +} + +/** + * A CloudFormation nested stack. + * + * When you apply template changes to update a top-level stack, CloudFormation + * updates the top-level stack and initiates an update to its nested stacks. + * CloudFormation updates the resources of modified nested stacks, but does not + * update the resources of unmodified nested stacks. + * + * Furthermore, this stack will not be treated as an independent deployment + * artifact (won't be listed in "cdk list" or deployable through "cdk deploy"), + * but rather only synthesized as a template and uploaded as an asset to S3. + * + * Cross references of resource attributes between the parent stack and the + * nested stack will automatically be translated to stack parameters and + * outputs. + * + * @experimental + */ +export class NestedStack extends Stack { + + /** + * Checks if `x` is an object of type `NestedStack`. + */ + public static isNestedStack(x: any): x is NestedStack { + return x != null && typeof(x) === 'object' && NESTED_STACK_SYMBOL in x; + } + + public readonly templateFile: string; + public readonly parentStack?: Stack; + + private readonly parameters: { [name: string]: string }; + private readonly resource: CfnStack; + private readonly _contextualStackId: string; + private readonly _contextualStackName: string; + + constructor(scope: Construct, id: string, props: NestedStackProps = { }) { + const parentStack = findParentStack(scope); + + super(scope, id, { env: { account: parentStack.account, region: parentStack.region } }); + + this.parentStack = parentStack; + + const parentScope = new Construct(scope, id + '.NestedStack'); + + Object.defineProperty(this, NESTED_STACK_SYMBOL, { value: true }); + + // this is the file name of the synthesized template file within the cloud assembly + this.templateFile = `${this.node.uniqueId}.nested.template.json`; + + this.parameters = props.parameters || {}; + + this.resource = new CfnStack(parentScope, `${id}.NestedStackResource`, { + templateUrl: this.templateUrl, + parameters: Lazy.anyValue({ produce: () => Object.keys(this.parameters).length > 0 ? this.parameters : undefined }), + notificationArns: props.notifications ? props.notifications.map(n => n.topicArn) : undefined, + timeoutInMinutes: props.timeout ? props.timeout.toMinutes() : undefined, + }); + + // context-aware stack name: if resolved from within this stack, return AWS::StackName + // if resolved from the outer stack, use the { Ref } of the AWS::CloudFormation::Stack resource + // which resolves the ARN of the stack. We need to extract the stack name, which is the second + // component after splitting by "/" + this._contextualStackName = this.contextualAttribute(Aws.STACK_NAME, Fn.select(1, Fn.split('/', this.resource.ref))); + this._contextualStackId = this.contextualAttribute(Aws.STACK_ID, this.resource.ref); + } + + /** + * An attribute that represents the name of the nested stack. + * + * This is a context aware attribute: + * - If this is referenced from the parent stack, it will return a token that parses the name from the stack ID. + * - If this is referenced from the context of the nested stack, it will return `{ "Ref": "AWS::StackName" }` + * + * @example mystack-mynestedstack-sggfrhxhum7w + * @attribute + */ + public get stackName() { + return this._contextualStackName; + } + + /** + * An attribute that represents the ID of the stack. + * + * This is a context aware attribute: + * - If this is referenced from the parent stack, it will return `{ "Ref": "LogicalIdOfNestedStackResource" }`. + * - If this is referenced from the context of the nested stack, it will return `{ "Ref": "AWS::StackId" }` + * + * @example arn:aws:cloudformation:us-east-2:123456789012:stack/mystack-mynestedstack-sggfrhxhum7w/f449b250-b969-11e0-a185-5081d0136786 + * @attribute + */ + public get stackId() { + return this._contextualStackId; + } + + /** + * Called by the base "prepare" method when a reference is found. + */ + protected prepareCrossReference(sourceStack: Stack, reference: Reference): IResolvable { + const targetStack = Stack.of(reference.target); + + // the nested stack references a resource from the parent stack: + // we pass it through a as a cloudformation parameter + if (targetStack === sourceStack.parentStack) { + const paramId = `reference-to-${reference.target.node.uniqueId}.${reference.displayName}`; + let param = this.node.tryFindChild(paramId) as CfnParameter; + if (!param) { + param = new CfnParameter(this, paramId, { type: 'String' }); + this.parameters[param.logicalId] = Token.asString(reference); + } + + return param.value; + } + + // parent stack references a resource from the nested stack: + // we output it from the nested stack and use "Fn::GetAtt" as the reference value + if (targetStack === this && targetStack.parentStack === sourceStack) { + return this.getCreateOutputForReference(reference); + } + + // sibling nested stacks (same parent): + // output from one and pass as parameter to the other + if (targetStack.parentStack && targetStack.parentStack === sourceStack.parentStack) { + const outputValue = this.getCreateOutputForReference(reference); + return (sourceStack as NestedStack).prepareCrossReference(sourceStack, outputValue); + } + + // nested stack references a value from some other non-nested stack: + // normal export/import, with dependency between the parents + if (sourceStack.parentStack && sourceStack.parentStack !== targetStack) { + return super.prepareCrossReference(sourceStack, reference); + } + + // some non-nested stack (that is not the parent) references a resource inside the nested stack: + // we output the value and let our parent export it + if (!sourceStack.parentStack && targetStack.parentStack && targetStack.parentStack !== sourceStack) { + const outputValue = this.getCreateOutputForReference(reference); + return (targetStack.parentStack as NestedStack).prepareCrossReference(sourceStack, outputValue); + } + + throw new Error('unexpected nested stack cross reference'); + } + + private getCreateOutputForReference(reference: Reference) { + const outputId = `${reference.target.node.uniqueId}${reference.displayName}`; + let output = this.node.tryFindChild(outputId) as CfnOutput; + if (!output) { + output = new CfnOutput(this, outputId, { value: Token.asString(reference) }); + } + + return this.resource.getAtt(`Outputs.${outputId}`); + } + + private contextualAttribute(innerValue: string, outerValue: string) { + return Token.asString({ + resolve: (context: IResolveContext) => { + if (Stack.of(context.scope) === this) { + return innerValue; + } else { + return outerValue; + } + } + }); + } +} + +/** + * Validates the scope for a nested stack. Nested stacks must be defined within the scope of another `Stack`. + */ +function findParentStack(scope: Construct): Stack { + if (!scope) { + throw new Error(`Nested stacks cannot be defined as a root construct`); + } + + const parentStack = scope.node.scopes.reverse().find(p => Stack.isStack(p)); + if (!parentStack) { + throw new Error(`Nested stacks must be defined within scope of another non-nested stack`); + } + + return parentStack as Stack; +} diff --git a/packages/@aws-cdk/aws-cloudformation/package.json b/packages/@aws-cdk/aws-cloudformation/package.json index 0d76d2b5a4d41..a3be13f857c23 100644 --- a/packages/@aws-cdk/aws-cloudformation/package.json +++ b/packages/@aws-cdk/aws-cloudformation/package.json @@ -66,6 +66,9 @@ "@aws-cdk/assert": "^1.11.0", "@aws-cdk/aws-events": "^1.11.0", "@aws-cdk/aws-ssm": "^1.11.0", + "@aws-cdk/aws-s3-assets": "^1.11.0", + "@aws-cdk/aws-sns-subscriptions": "^1.11.0", + "@aws-cdk/aws-sqs": "^1.11.0", "@types/aws-lambda": "^8.10.33", "cdk-build-tools": "file:../../../tools/cdk-build-tools", "cdk-integ-tools": "file:../../../tools/cdk-integ-tools", @@ -75,12 +78,16 @@ "dependencies": { "@aws-cdk/aws-iam": "^1.11.0", "@aws-cdk/aws-lambda": "^1.11.0", + "@aws-cdk/cx-api": "^1.11.0", + "@aws-cdk/aws-s3": "^1.11.0", "@aws-cdk/aws-sns": "^1.11.0", "@aws-cdk/core": "^1.11.0" }, "homepage": "https://github.com/aws/aws-cdk", "peerDependencies": { "@aws-cdk/aws-iam": "^1.11.0", + "@aws-cdk/cx-api": "^1.11.0", + "@aws-cdk/aws-s3": "^1.11.0", "@aws-cdk/aws-lambda": "^1.11.0", "@aws-cdk/aws-sns": "^1.11.0", "@aws-cdk/core": "^1.11.0" @@ -98,4 +105,4 @@ ] }, "stability": "stable" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-cloudformation/test/asset-directory-fixture/index.ts b/packages/@aws-cdk/aws-cloudformation/test/asset-directory-fixture/index.ts new file mode 100644 index 0000000000000..3da16e9b84b6c --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/asset-directory-fixture/index.ts @@ -0,0 +1,6 @@ +// tslint:disable:no-console + +exports.handler = async (evt: any) => { + console.error(JSON.stringify(evt, undefined, 2)); + return 'hello, world!'; +}; diff --git a/packages/@aws-cdk/aws-cloudformation/test/asset-docker-fixture/Dockerfile b/packages/@aws-cdk/aws-cloudformation/test/asset-docker-fixture/Dockerfile new file mode 100644 index 0000000000000..67fd379018917 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/asset-docker-fixture/Dockerfile @@ -0,0 +1 @@ +FROM alpine diff --git a/packages/@aws-cdk/aws-cloudformation/test/asset-fixture.txt b/packages/@aws-cdk/aws-cloudformation/test/asset-fixture.txt new file mode 100644 index 0000000000000..f5d20301be436 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/asset-fixture.txt @@ -0,0 +1 @@ +Hello, asset! diff --git a/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stack.expected.json b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stack.expected.json new file mode 100644 index 0000000000000..66914582300b1 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stack.expected.json @@ -0,0 +1,282 @@ +{ + "Resources": { + "SubscriberQueueC193DC66": { + "Type": "AWS::SQS::Queue" + }, + "SubscriberQueuePolicy25A0799E": { + "Type": "AWS::SQS::QueuePolicy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sqs:SendMessage", + "Condition": { + "ArnEquals": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "NestedStack1NestedStackNestedStack1NestedStackResource7E64AD52", + "Outputs.nestedstackstestNestedStack1topic02C2D1254Ref" + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "sns.amazonaws.com" + }, + "Resource": { + "Fn::GetAtt": [ + "SubscriberQueueC193DC66", + "Arn" + ] + } + }, + { + "Action": "sqs:SendMessage", + "Condition": { + "ArnEquals": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "NestedStack1NestedStackNestedStack1NestedStackResource7E64AD52", + "Outputs.nestedstackstestNestedStack1topic1474E5389Ref" + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "sns.amazonaws.com" + }, + "Resource": { + "Fn::GetAtt": [ + "SubscriberQueueC193DC66", + "Arn" + ] + } + }, + { + "Action": "sqs:SendMessage", + "Condition": { + "ArnEquals": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "NestedStack1NestedStackNestedStack1NestedStackResource7E64AD52", + "Outputs.nestedstackstestNestedStack1topic22C710DC4Ref" + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "sns.amazonaws.com" + }, + "Resource": { + "Fn::GetAtt": [ + "SubscriberQueueC193DC66", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "Queues": [ + { + "Ref": "SubscriberQueueC193DC66" + } + ] + } + }, + "SubscriberQueuenestedstackstestNestedStack1topic089C5EB1396F65087": { + "Type": "AWS::SNS::Subscription", + "Properties": { + "Protocol": "sqs", + "TopicArn": { + "Fn::GetAtt": [ + "NestedStack1NestedStackNestedStack1NestedStackResource7E64AD52", + "Outputs.nestedstackstestNestedStack1topic02C2D1254Ref" + ] + }, + "Endpoint": { + "Fn::GetAtt": [ + "SubscriberQueueC193DC66", + "Arn" + ] + } + } + }, + "SubscriberQueuenestedstackstestNestedStack1topic1150E1A929A2C267E": { + "Type": "AWS::SNS::Subscription", + "Properties": { + "Protocol": "sqs", + "TopicArn": { + "Fn::GetAtt": [ + "NestedStack1NestedStackNestedStack1NestedStackResource7E64AD52", + "Outputs.nestedstackstestNestedStack1topic1474E5389Ref" + ] + }, + "Endpoint": { + "Fn::GetAtt": [ + "SubscriberQueueC193DC66", + "Arn" + ] + } + } + }, + "SubscriberQueuenestedstackstestNestedStack1topic209B8719858511914": { + "Type": "AWS::SNS::Subscription", + "Properties": { + "Protocol": "sqs", + "TopicArn": { + "Fn::GetAtt": [ + "NestedStack1NestedStackNestedStack1NestedStackResource7E64AD52", + "Outputs.nestedstackstestNestedStack1topic22C710DC4Ref" + ] + }, + "Endpoint": { + "Fn::GetAtt": [ + "SubscriberQueueC193DC66", + "Arn" + ] + } + } + }, + "NestedStack1NestedStackNestedStack1NestedStackResource7E64AD52": { + "Type": "AWS::CloudFormation::Stack", + "Properties": { + "TemplateURL": { + "Fn::Join": [ + "", + [ + "https://s3.", + { + "Ref": "AWS::Region" + }, + ".", + { + "Ref": "AWS::URLSuffix" + }, + "/", + { + "Ref": "AssetParameters3d1024a4bdf2d2a5447520356aafce4068b52bbc6f71c3d0a5230c032f632902S3Bucket92C29C04" + }, + "/", + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters3d1024a4bdf2d2a5447520356aafce4068b52bbc6f71c3d0a5230c032f632902S3VersionKeyB63C774A" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters3d1024a4bdf2d2a5447520356aafce4068b52bbc6f71c3d0a5230c032f632902S3VersionKeyB63C774A" + } + ] + } + ] + } + ] + ] + }, + "Parameters": { + "TopicNamePrefix": "Prefix1", + "referencetonestedstackstestSubscriberQueue39409787Ref": { + "Ref": "SubscriberQueueC193DC66" + } + } + } + }, + "NestedStack2NestedStackNestedStack2NestedStackResourceFDF82E43": { + "Type": "AWS::CloudFormation::Stack", + "Properties": { + "TemplateURL": { + "Fn::Join": [ + "", + [ + "https://s3.", + { + "Ref": "AWS::Region" + }, + ".", + { + "Ref": "AWS::URLSuffix" + }, + "/", + { + "Ref": "AssetParameters0d0404717d8867c09534f2cf382e8e24531ff64a968afa2efd7f071ad65a22dfS3BucketB322F951" + }, + "/", + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters0d0404717d8867c09534f2cf382e8e24531ff64a968afa2efd7f071ad65a22dfS3VersionKeyAA9C5AF4" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters0d0404717d8867c09534f2cf382e8e24531ff64a968afa2efd7f071ad65a22dfS3VersionKeyAA9C5AF4" + } + ] + } + ] + } + ] + ] + }, + "Parameters": { + "TopicNamePrefix": "Prefix2" + } + } + } + }, + "Parameters": { + "AssetParameters0d0404717d8867c09534f2cf382e8e24531ff64a968afa2efd7f071ad65a22dfS3BucketB322F951": { + "Type": "String", + "Description": "S3 bucket for asset \"0d0404717d8867c09534f2cf382e8e24531ff64a968afa2efd7f071ad65a22df\"" + }, + "AssetParameters0d0404717d8867c09534f2cf382e8e24531ff64a968afa2efd7f071ad65a22dfS3VersionKeyAA9C5AF4": { + "Type": "String", + "Description": "S3 key for asset version \"0d0404717d8867c09534f2cf382e8e24531ff64a968afa2efd7f071ad65a22df\"" + }, + "AssetParameters0d0404717d8867c09534f2cf382e8e24531ff64a968afa2efd7f071ad65a22dfArtifactHash5D335705": { + "Type": "String", + "Description": "Artifact hash for asset \"0d0404717d8867c09534f2cf382e8e24531ff64a968afa2efd7f071ad65a22df\"" + }, + "AssetParameters3d1024a4bdf2d2a5447520356aafce4068b52bbc6f71c3d0a5230c032f632902S3Bucket92C29C04": { + "Type": "String", + "Description": "S3 bucket for asset \"3d1024a4bdf2d2a5447520356aafce4068b52bbc6f71c3d0a5230c032f632902\"" + }, + "AssetParameters3d1024a4bdf2d2a5447520356aafce4068b52bbc6f71c3d0a5230c032f632902S3VersionKeyB63C774A": { + "Type": "String", + "Description": "S3 key for asset version \"3d1024a4bdf2d2a5447520356aafce4068b52bbc6f71c3d0a5230c032f632902\"" + }, + "AssetParameters3d1024a4bdf2d2a5447520356aafce4068b52bbc6f71c3d0a5230c032f632902ArtifactHashF5652BAD": { + "Type": "String", + "Description": "Artifact hash for asset \"3d1024a4bdf2d2a5447520356aafce4068b52bbc6f71c3d0a5230c032f632902\"" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stack.ts b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stack.ts new file mode 100644 index 0000000000000..cacc2fd3e7b53 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stack.ts @@ -0,0 +1,64 @@ +import lambda = require('@aws-cdk/aws-lambda'); +import sns = require('@aws-cdk/aws-sns'); +import sns_subscriptions = require('@aws-cdk/aws-sns-subscriptions'); +import sqs = require('@aws-cdk/aws-sqs'); +import { App, CfnParameter, Construct, Stack } from '@aws-cdk/core'; +import cfn = require('../lib'); + +interface MyNestedStackProps { + readonly subscriber?: sqs.Queue; + readonly siblingTopic?: sns.Topic; // a topic defined in a sibling nested stack + readonly topicCount: number; + readonly topicNamePrefix: string; +} + +class MyNestedStack extends cfn.NestedStack { + constructor(scope: Construct, id: string, props: MyNestedStackProps) { + const topicNamePrefixLogicalId = 'TopicNamePrefix'; + + super(scope, id, { + parameters: { + [topicNamePrefixLogicalId]: props.topicNamePrefix // pass in a parameter to the nested stack + } + }); + + const topicNamePrefixParameter = new CfnParameter(this, 'TopicNamePrefix', { type: 'String' }); + + for (let i = 0; i < props.topicCount; ++i) { + const topic = new sns.Topic(this, `topic-${i}`, { displayName: `${topicNamePrefixParameter.valueAsString}-${i}`}); + + // since the subscription resources are defined in the subscriber's stack, this + // will add an SNS subscription resource to the parent stack that reference this topic. + if (props.subscriber) { + topic.addSubscription(new sns_subscriptions.SqsSubscription(props.subscriber)); + } + } + + if (props.subscriber) { + new lambda.Function(this, 'fn', { + runtime: lambda.Runtime.NODEJS_8_10, + code: lambda.Code.inline('console.error("hi")'), + handler: 'index.handler', + environment: { + TOPIC_ARN: props.siblingTopic ? props.siblingTopic.topicArn : '', + QUEUE_URL: props.subscriber.queueUrl // nested stack references a resource in the parent + } + }); + } + } +} + +class MyTestStack extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + + const queue = new sqs.Queue(this, 'SubscriberQueue'); + + new MyNestedStack(this, 'NestedStack1', { topicCount: 3, topicNamePrefix: 'Prefix1', subscriber: queue }); + new MyNestedStack(this, 'NestedStack2', { topicCount: 2, topicNamePrefix: 'Prefix2' }); + } +} + +const app = new App(); +new MyTestStack(app, 'nested-stacks-test'); +app.synth(); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-assets.expected.json b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-assets.expected.json new file mode 100644 index 0000000000000..d18d72744be00 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-assets.expected.json @@ -0,0 +1,89 @@ +{ + "Resources": { + "NestedNestedStackNestedNestedStackResourceDEFDAA4D": { + "Type": "AWS::CloudFormation::Stack", + "Properties": { + "TemplateURL": { + "Fn::Join": [ + "", + [ + "https://s3.", + { + "Ref": "AWS::Region" + }, + ".", + { + "Ref": "AWS::URLSuffix" + }, + "/", + { + "Ref": "AssetParameters28e3582cfc7c551f42435f44110c499fb2c415fe0f02a02a77139cf43edd1145S3BucketFDBB032A" + }, + "/", + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters28e3582cfc7c551f42435f44110c499fb2c415fe0f02a02a77139cf43edd1145S3VersionKey58263016" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters28e3582cfc7c551f42435f44110c499fb2c415fe0f02a02a77139cf43edd1145S3VersionKey58263016" + } + ] + } + ] + } + ] + ] + }, + "Parameters": { + "referencetonestedstacksassetsAssetParametersb13aad60258df1fbe5fb1312a7b2f8f25c03b3e07113782f7c12f00e023e148aS3BucketE2268D38Ref": { + "Ref": "AssetParametersb13aad60258df1fbe5fb1312a7b2f8f25c03b3e07113782f7c12f00e023e148aS3BucketC5E2D427" + }, + "referencetonestedstacksassetsAssetParametersb13aad60258df1fbe5fb1312a7b2f8f25c03b3e07113782f7c12f00e023e148aS3VersionKeyD31C6796Ref": { + "Ref": "AssetParametersb13aad60258df1fbe5fb1312a7b2f8f25c03b3e07113782f7c12f00e023e148aS3VersionKey31422F11" + } + } + } + } + }, + "Parameters": { + "AssetParametersb13aad60258df1fbe5fb1312a7b2f8f25c03b3e07113782f7c12f00e023e148aS3BucketC5E2D427": { + "Type": "String", + "Description": "S3 bucket for asset \"b13aad60258df1fbe5fb1312a7b2f8f25c03b3e07113782f7c12f00e023e148a\"" + }, + "AssetParametersb13aad60258df1fbe5fb1312a7b2f8f25c03b3e07113782f7c12f00e023e148aS3VersionKey31422F11": { + "Type": "String", + "Description": "S3 key for asset version \"b13aad60258df1fbe5fb1312a7b2f8f25c03b3e07113782f7c12f00e023e148a\"" + }, + "AssetParametersb13aad60258df1fbe5fb1312a7b2f8f25c03b3e07113782f7c12f00e023e148aArtifactHash2897446E": { + "Type": "String", + "Description": "Artifact hash for asset \"b13aad60258df1fbe5fb1312a7b2f8f25c03b3e07113782f7c12f00e023e148a\"" + }, + "AssetParameters28e3582cfc7c551f42435f44110c499fb2c415fe0f02a02a77139cf43edd1145S3BucketFDBB032A": { + "Type": "String", + "Description": "S3 bucket for asset \"28e3582cfc7c551f42435f44110c499fb2c415fe0f02a02a77139cf43edd1145\"" + }, + "AssetParameters28e3582cfc7c551f42435f44110c499fb2c415fe0f02a02a77139cf43edd1145S3VersionKey58263016": { + "Type": "String", + "Description": "S3 key for asset version \"28e3582cfc7c551f42435f44110c499fb2c415fe0f02a02a77139cf43edd1145\"" + }, + "AssetParameters28e3582cfc7c551f42435f44110c499fb2c415fe0f02a02a77139cf43edd1145ArtifactHash592B4471": { + "Type": "String", + "Description": "Artifact hash for asset \"28e3582cfc7c551f42435f44110c499fb2c415fe0f02a02a77139cf43edd1145\"" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-assets.ts b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-assets.ts new file mode 100644 index 0000000000000..b523c235bf66c --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-assets.ts @@ -0,0 +1,28 @@ +import lambda = require('@aws-cdk/aws-lambda'); +import { App, Construct, Stack } from '@aws-cdk/core'; +import path = require('path'); +import cfn = require('../lib'); + +class NestedStack extends cfn.NestedStack { + constructor(scope: Construct, id: string) { + super(scope, id); + + new lambda.Function(this, 'Handler', { + code: lambda.Code.asset(path.join(__dirname, 'asset-directory-fixture')), + runtime: lambda.Runtime.NODEJS_10_X, + handler: 'index.handler' + }); + } +} + +class ParentStack extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + + new NestedStack(this, 'Nested'); + } +} + +const app = new App(); +new ParentStack(app, 'nested-stacks-assets'); +app.synth(); diff --git a/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-multi.expected.json b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-multi.expected.json new file mode 100644 index 0000000000000..6a10c9252c79a --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-multi.expected.json @@ -0,0 +1,89 @@ +{ + "Resources": { + "NestedStackNestedStackNestedStackNestedStackResourceB70834FD": { + "Type": "AWS::CloudFormation::Stack", + "Properties": { + "TemplateURL": { + "Fn::Join": [ + "", + [ + "https://s3.", + { + "Ref": "AWS::Region" + }, + ".", + { + "Ref": "AWS::URLSuffix" + }, + "/", + { + "Ref": "AssetParametersf94d2259295147a175eeb7f8b31856e4beb5d4d4654f0bf956ee51d5e2ee5d8eS3Bucket0211CC54" + }, + "/", + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParametersf94d2259295147a175eeb7f8b31856e4beb5d4d4654f0bf956ee51d5e2ee5d8eS3VersionKey5D85E7DD" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParametersf94d2259295147a175eeb7f8b31856e4beb5d4d4654f0bf956ee51d5e2ee5d8eS3VersionKey5D85E7DD" + } + ] + } + ] + } + ] + ] + }, + "Parameters": { + "referencetonestedstacksmultiAssetParameterse3410ccec04414535f1c8035ce0ea42f59eedf66d0e6d0eec8bc435c4a4e809dS3Bucket41B3528FRef": { + "Ref": "AssetParameterse3410ccec04414535f1c8035ce0ea42f59eedf66d0e6d0eec8bc435c4a4e809dS3Bucket9165C850" + }, + "referencetonestedstacksmultiAssetParameterse3410ccec04414535f1c8035ce0ea42f59eedf66d0e6d0eec8bc435c4a4e809dS3VersionKey2F819BA6Ref": { + "Ref": "AssetParameterse3410ccec04414535f1c8035ce0ea42f59eedf66d0e6d0eec8bc435c4a4e809dS3VersionKey8C8E79CA" + } + } + } + } + }, + "Parameters": { + "AssetParameterse3410ccec04414535f1c8035ce0ea42f59eedf66d0e6d0eec8bc435c4a4e809dS3Bucket9165C850": { + "Type": "String", + "Description": "S3 bucket for asset \"e3410ccec04414535f1c8035ce0ea42f59eedf66d0e6d0eec8bc435c4a4e809d\"" + }, + "AssetParameterse3410ccec04414535f1c8035ce0ea42f59eedf66d0e6d0eec8bc435c4a4e809dS3VersionKey8C8E79CA": { + "Type": "String", + "Description": "S3 key for asset version \"e3410ccec04414535f1c8035ce0ea42f59eedf66d0e6d0eec8bc435c4a4e809d\"" + }, + "AssetParameterse3410ccec04414535f1c8035ce0ea42f59eedf66d0e6d0eec8bc435c4a4e809dArtifactHash1839B0E9": { + "Type": "String", + "Description": "Artifact hash for asset \"e3410ccec04414535f1c8035ce0ea42f59eedf66d0e6d0eec8bc435c4a4e809d\"" + }, + "AssetParametersf94d2259295147a175eeb7f8b31856e4beb5d4d4654f0bf956ee51d5e2ee5d8eS3Bucket0211CC54": { + "Type": "String", + "Description": "S3 bucket for asset \"f94d2259295147a175eeb7f8b31856e4beb5d4d4654f0bf956ee51d5e2ee5d8e\"" + }, + "AssetParametersf94d2259295147a175eeb7f8b31856e4beb5d4d4654f0bf956ee51d5e2ee5d8eS3VersionKey5D85E7DD": { + "Type": "String", + "Description": "S3 key for asset version \"f94d2259295147a175eeb7f8b31856e4beb5d4d4654f0bf956ee51d5e2ee5d8e\"" + }, + "AssetParametersf94d2259295147a175eeb7f8b31856e4beb5d4d4654f0bf956ee51d5e2ee5d8eArtifactHashD0519824": { + "Type": "String", + "Description": "Artifact hash for asset \"f94d2259295147a175eeb7f8b31856e4beb5d4d4654f0bf956ee51d5e2ee5d8e\"" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-multi.ts b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-multi.ts new file mode 100644 index 0000000000000..1086ceeb3e43c --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-multi.ts @@ -0,0 +1,27 @@ +import sns = require('@aws-cdk/aws-sns'); +import { App, Construct, Stack } from '@aws-cdk/core'; +import cfn = require('../lib'); + +class YourNestedStack extends cfn.NestedStack { + constructor(scope: Construct, id: string) { + super(scope, id); + + new sns.Topic(this, 'YourResource'); + } +} + +class MyNestedStack extends cfn.NestedStack { + constructor(scope: Construct, id: string) { + super(scope, id); + + new sns.Topic(this, 'MyResource'); + + new YourNestedStack(this, 'NestedChild'); + } +} + +const app = new App(); +const stack = new Stack(app, 'nested-stacks-multi'); +new MyNestedStack(stack, 'NestedStack'); + +app.synth(); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs1.expected.json b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs1.expected.json new file mode 100644 index 0000000000000..cf2e508a0f8eb --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs1.expected.json @@ -0,0 +1,91 @@ +[ + { + "Resources": { + "MyTopic86869434": { + "Type": "AWS::SNS::Topic" + } + }, + "Outputs": { + "ExportsOutputFnGetAttMyTopic86869434TopicNameFCC96FA2": { + "Value": { + "Fn::GetAtt": [ + "MyTopic86869434", + "TopicName" + ] + }, + "Export": { + "Name": "nest-stacks-refs1-producer:ExportsOutputFnGetAttMyTopic86869434TopicNameFCC96FA2" + } + } + } + }, + { + "Resources": { + "Nested1NestedStackNested1NestedStackResourceCD0AD36B": { + "Type": "AWS::CloudFormation::Stack", + "Properties": { + "TemplateURL": { + "Fn::Join": [ + "", + [ + "https://s3.", + { + "Ref": "AWS::Region" + }, + ".", + { + "Ref": "AWS::URLSuffix" + }, + "/", + { + "Ref": "AssetParametersf780d24543a81dc89296e718fbad166c6cc0223026f7a142a0ad9cb21de3ac46S3BucketE30948C8" + }, + "/", + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParametersf780d24543a81dc89296e718fbad166c6cc0223026f7a142a0ad9cb21de3ac46S3VersionKeyDEA9E5AE" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParametersf780d24543a81dc89296e718fbad166c6cc0223026f7a142a0ad9cb21de3ac46S3VersionKeyDEA9E5AE" + } + ] + } + ] + } + ] + ] + } + } + } + }, + "Parameters": { + "AssetParametersf780d24543a81dc89296e718fbad166c6cc0223026f7a142a0ad9cb21de3ac46S3BucketE30948C8": { + "Type": "String", + "Description": "S3 bucket for asset \"f780d24543a81dc89296e718fbad166c6cc0223026f7a142a0ad9cb21de3ac46\"" + }, + "AssetParametersf780d24543a81dc89296e718fbad166c6cc0223026f7a142a0ad9cb21de3ac46S3VersionKeyDEA9E5AE": { + "Type": "String", + "Description": "S3 key for asset version \"f780d24543a81dc89296e718fbad166c6cc0223026f7a142a0ad9cb21de3ac46\"" + }, + "AssetParametersf780d24543a81dc89296e718fbad166c6cc0223026f7a142a0ad9cb21de3ac46ArtifactHashFCB5E601": { + "Type": "String", + "Description": "Artifact hash for asset \"f780d24543a81dc89296e718fbad166c6cc0223026f7a142a0ad9cb21de3ac46\"" + } + } + } +] \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs1.ts b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs1.ts new file mode 100644 index 0000000000000..e1e6f7951a9f6 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs1.ts @@ -0,0 +1,39 @@ +/// !cdk-integ * + +// nested stack references a resource from a non-nested non-parent stack + +import sns = require('@aws-cdk/aws-sns'); +import { App, Construct, Stack } from '@aws-cdk/core'; +import cfn = require('../lib'); + +class ConsumerNestedStack extends cfn.NestedStack { + constructor(scope: Construct, id: string, topic: sns.Topic) { + super(scope, id); + + new sns.Topic(this, 'ConsumerTopic', { + displayName: `Consumer of ${topic.topicName}` + }); + } +} + +class ProducerStack extends Stack { + public readonly topic: sns.Topic; + constructor(scope: Construct, id: string) { + super(scope, id); + + this.topic = new sns.Topic(this, 'MyTopic'); + } +} + +class ParentStack extends Stack { + constructor(scope: Construct, id: string, topic: sns.Topic) { + super(scope, id); + + new ConsumerNestedStack(this, 'Nested1', topic); + } +} + +const app = new App(); +const producer = new ProducerStack(app, 'nest-stacks-refs1-producer'); +new ParentStack(app, 'nested-stacks-refs1-parent-with-consumer', producer.topic); +app.synth(); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs2.expected.json b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs2.expected.json new file mode 100644 index 0000000000000..ee7be9113bdcb --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs2.expected.json @@ -0,0 +1,114 @@ +[ + { + "Resources": { + "Nested1NestedStackNested1NestedStackResourceCD0AD36B": { + "Type": "AWS::CloudFormation::Stack", + "Properties": { + "TemplateURL": { + "Fn::Join": [ + "", + [ + "https://s3.", + { + "Ref": "AWS::Region" + }, + ".", + { + "Ref": "AWS::URLSuffix" + }, + "/", + { + "Ref": "AssetParameters89ace718d1ec985253be4d688a4632c5f15e28247cedbf99a756c83096315883S3Bucket88569758" + }, + "/", + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters89ace718d1ec985253be4d688a4632c5f15e28247cedbf99a756c83096315883S3VersionKey2661E5B6" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters89ace718d1ec985253be4d688a4632c5f15e28247cedbf99a756c83096315883S3VersionKey2661E5B6" + } + ] + } + ] + } + ] + ] + } + } + } + }, + "Outputs": { + "ExportsOutputFnGetAttNested1NestedStackNested1NestedStackResourceCD0AD36BOutputsnestedstacksrefs2parentwithproducerNested1MyTopic9E26C363TopicNameF4BCEDAF": { + "Value": { + "Fn::GetAtt": [ + "Nested1NestedStackNested1NestedStackResourceCD0AD36B", + "Outputs.nestedstacksrefs2parentwithproducerNested1MyTopic9E26C363TopicName" + ] + }, + "Export": { + "Name": "nested-stacks-refs2-parent-with-producer:ExportsOutputFnGetAttNested1NestedStackNested1NestedStackResourceCD0AD36BOutputsnestedstacksrefs2parentwithproducerNested1MyTopic9E26C363TopicNameF4BCEDAF" + } + } + }, + "Parameters": { + "AssetParameters89ace718d1ec985253be4d688a4632c5f15e28247cedbf99a756c83096315883S3Bucket88569758": { + "Type": "String", + "Description": "S3 bucket for asset \"89ace718d1ec985253be4d688a4632c5f15e28247cedbf99a756c83096315883\"" + }, + "AssetParameters89ace718d1ec985253be4d688a4632c5f15e28247cedbf99a756c83096315883S3VersionKey2661E5B6": { + "Type": "String", + "Description": "S3 key for asset version \"89ace718d1ec985253be4d688a4632c5f15e28247cedbf99a756c83096315883\"" + }, + "AssetParameters89ace718d1ec985253be4d688a4632c5f15e28247cedbf99a756c83096315883ArtifactHashF75589EB": { + "Type": "String", + "Description": "Artifact hash for asset \"89ace718d1ec985253be4d688a4632c5f15e28247cedbf99a756c83096315883\"" + } + } + }, + { + "Resources": { + "ConsumerTopic6F402371": { + "Type": "AWS::SNS::Topic", + "Properties": { + "DisplayName": { + "Fn::Join": [ + "", + [ + "Consuming ", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "-", + { + "Fn::ImportValue": "nested-stacks-refs2-parent-with-producer:ExportsOutputFnGetAttNested1NestedStackNested1NestedStackResourceCD0AD36BOutputsnestedstacksrefs2parentwithproducerNested1MyTopic9E26C363TopicNameF4BCEDAF" + } + ] + } + ] + } + ] + ] + } + } + } + } + } +] \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs2.ts b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs2.ts new file mode 100644 index 0000000000000..a0a05b661b4aa --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs2.ts @@ -0,0 +1,42 @@ +/// !cdk-integ * +import sns = require('@aws-cdk/aws-sns'); +import { App, Construct, Fn, Stack } from '@aws-cdk/core'; +import cfn = require('../lib'); + +// non-nested non-parent stack consumes a resource from a nested stack + +class ProducerNestedStack extends cfn.NestedStack { + public readonly topic: sns.Topic; + + constructor(scope: Construct, id: string) { + super(scope, id); + + this.topic = new sns.Topic(this, 'MyTopic'); + } +} + +class ParentStack extends Stack { + public readonly topic: sns.Topic; + + constructor(scope: Construct, id: string) { + super(scope, id); + + const nested = new ProducerNestedStack(this, 'Nested1'); + this.topic = nested.topic; + } +} + +class ConsumerStack extends Stack { + constructor(scope: Construct, id: string, topic: sns.Topic) { + super(scope, id); + + new sns.Topic(this, 'ConsumerTopic', { + displayName: `Consuming ${Fn.select(2, Fn.split('-', topic.topicName))}` // just shorten because display name is limited + }); + } +} + +const app = new App(); +const parent = new ParentStack(app, 'nested-stacks-refs2-parent-with-producer'); +new ConsumerStack(app, 'nested-stacks-refs2-consumer', parent.topic); +app.synth(); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs3.expected.json b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs3.expected.json new file mode 100644 index 0000000000000..25bb05cdf6ab9 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs3.expected.json @@ -0,0 +1,140 @@ +{ + "Resources": { + "Nested1NestedStackNested1NestedStackResourceCD0AD36B": { + "Type": "AWS::CloudFormation::Stack", + "Properties": { + "TemplateURL": { + "Fn::Join": [ + "", + [ + "https://s3.", + { + "Ref": "AWS::Region" + }, + ".", + { + "Ref": "AWS::URLSuffix" + }, + "/", + { + "Ref": "AssetParameters008e281fb3039601b8fbef60e255afe78cb00a09611d1aa7342f56328aef7d9aS3Bucket3AC5D089" + }, + "/", + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters008e281fb3039601b8fbef60e255afe78cb00a09611d1aa7342f56328aef7d9aS3VersionKeyB0263A92" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters008e281fb3039601b8fbef60e255afe78cb00a09611d1aa7342f56328aef7d9aS3VersionKeyB0263A92" + } + ] + } + ] + } + ] + ] + } + } + }, + "Nested2NestedStackNested2NestedStackResource877A1112": { + "Type": "AWS::CloudFormation::Stack", + "Properties": { + "TemplateURL": { + "Fn::Join": [ + "", + [ + "https://s3.", + { + "Ref": "AWS::Region" + }, + ".", + { + "Ref": "AWS::URLSuffix" + }, + "/", + { + "Ref": "AssetParameters2e7ce09a9e0721d268d734287b72d071ed542a05451e3b53dfcb5ae4e76cc583S3Bucket72E4418F" + }, + "/", + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters2e7ce09a9e0721d268d734287b72d071ed542a05451e3b53dfcb5ae4e76cc583S3VersionKeyC46A55B6" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters2e7ce09a9e0721d268d734287b72d071ed542a05451e3b53dfcb5ae4e76cc583S3VersionKeyC46A55B6" + } + ] + } + ] + } + ] + ] + }, + "Parameters": { + "referencetonestedstacksrefs3siblingsNested1NestedStackNested1NestedStackResourceE58B6825Outputsnestedstacksrefs3siblingsNested1MyTopic12458558TopicName": { + "Fn::GetAtt": [ + "Nested1NestedStackNested1NestedStackResourceCD0AD36B", + "Outputs.nestedstacksrefs3siblingsNested1MyTopic12458558TopicName" + ] + } + } + } + } + }, + "Parameters": { + "AssetParameters2e7ce09a9e0721d268d734287b72d071ed542a05451e3b53dfcb5ae4e76cc583S3Bucket72E4418F": { + "Type": "String", + "Description": "S3 bucket for asset \"2e7ce09a9e0721d268d734287b72d071ed542a05451e3b53dfcb5ae4e76cc583\"" + }, + "AssetParameters2e7ce09a9e0721d268d734287b72d071ed542a05451e3b53dfcb5ae4e76cc583S3VersionKeyC46A55B6": { + "Type": "String", + "Description": "S3 key for asset version \"2e7ce09a9e0721d268d734287b72d071ed542a05451e3b53dfcb5ae4e76cc583\"" + }, + "AssetParameters2e7ce09a9e0721d268d734287b72d071ed542a05451e3b53dfcb5ae4e76cc583ArtifactHashDF52341B": { + "Type": "String", + "Description": "Artifact hash for asset \"2e7ce09a9e0721d268d734287b72d071ed542a05451e3b53dfcb5ae4e76cc583\"" + }, + "AssetParameters008e281fb3039601b8fbef60e255afe78cb00a09611d1aa7342f56328aef7d9aS3Bucket3AC5D089": { + "Type": "String", + "Description": "S3 bucket for asset \"008e281fb3039601b8fbef60e255afe78cb00a09611d1aa7342f56328aef7d9a\"" + }, + "AssetParameters008e281fb3039601b8fbef60e255afe78cb00a09611d1aa7342f56328aef7d9aS3VersionKeyB0263A92": { + "Type": "String", + "Description": "S3 key for asset version \"008e281fb3039601b8fbef60e255afe78cb00a09611d1aa7342f56328aef7d9a\"" + }, + "AssetParameters008e281fb3039601b8fbef60e255afe78cb00a09611d1aa7342f56328aef7d9aArtifactHashEF790DCB": { + "Type": "String", + "Description": "Artifact hash for asset \"008e281fb3039601b8fbef60e255afe78cb00a09611d1aa7342f56328aef7d9a\"" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs3.ts b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs3.ts new file mode 100644 index 0000000000000..14bb4a4868093 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs3.ts @@ -0,0 +1,41 @@ +/// !cdk-integ * +import sns = require('@aws-cdk/aws-sns'); +import { App, Construct, Fn, Stack } from '@aws-cdk/core'; +import cfn = require('../lib'); + +// references between siblings + +class ProducerNestedStack extends cfn.NestedStack { + public readonly topic: sns.Topic; + + constructor(scope: Construct, id: string) { + super(scope, id); + + this.topic = new sns.Topic(this, 'MyTopic'); + } +} + +class ConsumerNestedStack extends cfn.NestedStack { + constructor(scope: Construct, id: string, topic: sns.Topic) { + super(scope, id); + + new sns.Topic(this, 'ConsumerTopic', { + displayName: `Consuming ${Fn.select(2, Fn.split('-', topic.topicName))}` // just shorten because display name is limited + }); + } +} + +class ParentStack extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + + const nested = new ProducerNestedStack(this, 'Nested1'); + new ConsumerNestedStack(this, 'Nested2', nested.topic); + } +} + +const app = new App(); + +new ParentStack(app, 'nested-stacks-refs3-siblings'); + +app.synth(); diff --git a/packages/@aws-cdk/aws-cloudformation/test/integ.trivial-lambda-resource.expected.json b/packages/@aws-cdk/aws-cloudformation/test/integ.trivial-lambda-resource.expected.json index a820be59e26c3..f033100eeec5a 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/integ.trivial-lambda-resource.expected.json +++ b/packages/@aws-cdk/aws-cloudformation/test/integ.trivial-lambda-resource.expected.json @@ -11,8 +11,8 @@ }, "Message": "CustomResource says hello" }, - "DeletionPolicy": "Delete", - "UpdateReplacePolicy": "Delete" + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" }, "SingletonLambdaf7d4f7304ee111e89c2dfa7ae01bbebcServiceRoleFE9ABB04": { "Type": "AWS::IAM::Role", @@ -77,4 +77,4 @@ } } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/test.nested-stack.ts b/packages/@aws-cdk/aws-cloudformation/test/test.nested-stack.ts new file mode 100644 index 0000000000000..c1357f1273c55 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/test.nested-stack.ts @@ -0,0 +1,807 @@ +import { expect, haveResource, SynthUtils } from '@aws-cdk/assert'; +import s3_assets = require('@aws-cdk/aws-s3-assets'); +import sns = require('@aws-cdk/aws-sns'); +import { App, CfnParameter, CfnResource, Construct, Stack } from '@aws-cdk/core'; +import fs = require('fs'); +import { Test } from 'nodeunit'; +import path = require('path'); +import { NestedStack } from '../lib/nested-stack'; + +// tslint:disable:max-line-length + +export = { + 'fails if defined as a root'(test: Test) { + // THEN + test.throws(() => new NestedStack(undefined as any, 'boom'), /Nested stacks cannot be defined as a root construct/); + test.done(); + }, + + 'fails if defined without a parent stack'(test: Test) { + // GIVEN + const app = new App(); + const group = new Construct(app, 'group'); + + // THEN + test.throws(() => new NestedStack(app, 'boom'), /must be defined within scope of another non-nested stack/); + test.throws(() => new NestedStack(group, 'bam'), /must be defined within scope of another non-nested stack/); + test.done(); + }, + + 'can be defined as a direct child or an indirect child of a Stack'(test: Test) { + // GIVEN + const parent = new Stack(); + + // THEN + new NestedStack(parent, 'direct'); + new NestedStack(new Construct(parent, 'group'), 'indirect'); + test.done(); + }, + + 'nested stack is not synthesized as a stack artifact into the assembly'(test: Test) { + // GIVEN + const app = new App(); + const parentStack = new Stack(app, 'parent-stack'); + new NestedStack(parentStack, 'nested-stack'); + + // WHEN + const assembly = app.synth(); + + // THEN + test.deepEqual(assembly.artifacts.length, 2); + test.done(); + }, + + 'the template of the nested stack is synthesized into the cloud assembly'(test: Test) { + // GIVEN + const app = new App(); + const parent = new Stack(app, 'parent-stack'); + const nested = new NestedStack(parent, 'nested-stack'); + new CfnResource(nested, 'ResourceInNestedStack', { type: 'AWS::Resource::Nested' }); + + // WHEN + const assembly = app.synth(); + + // THEN + const template = JSON.parse(fs.readFileSync(path.join(assembly.directory, `${nested.node.uniqueId}.nested.template.json`), 'utf-8')); + test.deepEqual(template, { + Resources: { + ResourceInNestedStack: { + Type: 'AWS::Resource::Nested' + } + } + }); + test.done(); + }, + + 'file asset metadata is associated with the parent stack'(test: Test) { + // GIVEN + const app = new App(); + const parent = new Stack(app, 'parent-stack'); + const nested = new NestedStack(parent, 'nested-stack'); + new CfnResource(nested, 'ResourceInNestedStack', { type: 'AWS::Resource::Nested' }); + + // WHEN + const assembly = app.synth(); + + // THEN + test.deepEqual(assembly.getStack(parent.stackName).assets, [{ + path: 'parentstacknestedstack844892C0.nested.template.json', + id: 'c639c0a5e7320758aa22589669ecebc98f185b711300b074f53998c8f9a45096', + packaging: 'file', + sourceHash: 'c639c0a5e7320758aa22589669ecebc98f185b711300b074f53998c8f9a45096', + s3BucketParameter: 'AssetParametersc639c0a5e7320758aa22589669ecebc98f185b711300b074f53998c8f9a45096S3BucketDA8C3345', + s3KeyParameter: 'AssetParametersc639c0a5e7320758aa22589669ecebc98f185b711300b074f53998c8f9a45096S3VersionKey09D03EE6', + artifactHashParameter: 'AssetParametersc639c0a5e7320758aa22589669ecebc98f185b711300b074f53998c8f9a45096ArtifactHash8DE450C7' + }]); + test.done(); + }, + + 'aws::cloudformation::stack is synthesized in the parent scope'(test: Test) { + // GIVEN + const app = new App(); + const parent = new Stack(app, 'parent-stack'); + + // WHEN + const nested = new NestedStack(parent, 'nested-stack'); + new CfnResource(nested, 'ResourceInNestedStack', { type: 'AWS::Resource::Nested' }); + + // THEN + const assembly = app.synth(); + + // assembly has one stack (the parent) + test.deepEqual(assembly.stacks.length, 1); + + // but this stack has an asset that points to the synthesized template + test.deepEqual(assembly.stacks[0].assets[0].path, 'parentstacknestedstack844892C0.nested.template.json'); + + // the template includes our resource + const filePath = path.join(assembly.directory, assembly.stacks[0].assets[0].path); + test.deepEqual(JSON.parse(fs.readFileSync(filePath).toString('utf-8')), { + Resources: { ResourceInNestedStack: { Type: 'AWS::Resource::Nested' } } + }); + + // the parent template includes the parameters and the nested stack resource which points to the s3 url + expect(parent).toMatch({ + Resources: { + nestedstackNestedStacknestedstackNestedStackResource71CDD241: { + Type: "AWS::CloudFormation::Stack", + Properties: { + TemplateURL: { + "Fn::Join": [ + "", + [ + "https://s3.", + { + Ref: "AWS::Region" + }, + ".", + { + Ref: "AWS::URLSuffix" + }, + "/", + { + Ref: "AssetParametersc639c0a5e7320758aa22589669ecebc98f185b711300b074f53998c8f9a45096S3BucketDA8C3345" + }, + "/", + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + Ref: "AssetParametersc639c0a5e7320758aa22589669ecebc98f185b711300b074f53998c8f9a45096S3VersionKey09D03EE6" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + Ref: "AssetParametersc639c0a5e7320758aa22589669ecebc98f185b711300b074f53998c8f9a45096S3VersionKey09D03EE6" + } + ] + } + ] + } + ] + ] + } + } + } + }, + Parameters: { + AssetParametersc639c0a5e7320758aa22589669ecebc98f185b711300b074f53998c8f9a45096S3BucketDA8C3345: { + Type: "String", + Description: "S3 bucket for asset \"c639c0a5e7320758aa22589669ecebc98f185b711300b074f53998c8f9a45096\"" + }, + AssetParametersc639c0a5e7320758aa22589669ecebc98f185b711300b074f53998c8f9a45096S3VersionKey09D03EE6: { + Type: "String", + Description: "S3 key for asset version \"c639c0a5e7320758aa22589669ecebc98f185b711300b074f53998c8f9a45096\"" + }, + AssetParametersc639c0a5e7320758aa22589669ecebc98f185b711300b074f53998c8f9a45096ArtifactHash8DE450C7: { + Type: "String", + Description: "Artifact hash for asset \"c639c0a5e7320758aa22589669ecebc98f185b711300b074f53998c8f9a45096\"" + } + } + }); + test.done(); + }, + + 'Stack.of()'(test: Test) { + class MyNestedStack extends NestedStack { + public readonly stackOfChild: Stack; + + constructor(scope: Construct, id: string) { + super(scope, id); + + const param = new CfnParameter(this, 'param', { type: 'String' }); + this.stackOfChild = Stack.of(param); + } + } + + const parent = new Stack(); + const nested = new MyNestedStack(parent, 'nested'); + + test.ok(nested.stackOfChild === nested); + test.ok(Stack.of(nested) === nested); + test.done(); + }, + + 'references within the nested stack are not reported as cross stack references'(test: Test) { + class MyNestedStack extends NestedStack { + constructor(scope: Construct, id: string) { + super(scope, id); + + const param = new CfnParameter(this, 'param', { type: 'String' }); + new CfnResource(this, 'resource', { + type: 'My::Resource', + properties: { + SomeProp: param.valueAsString + } + }); + } + } + + const app = new App(); + const parent = new Stack(app, 'parent'); + + new MyNestedStack(parent, 'nested'); + + // references are added during "prepare" + const assembly = app.synth(); + + test.deepEqual(assembly.stacks.length, 1); + test.deepEqual(assembly.stacks[0].dependencies, []); + test.done(); + }, + + 'references to a resource from the parent stack in a nested stack is translated into a cfn parameter'(test: Test) { + // WHEN + class MyNestedStack extends NestedStack { + + constructor(scope: Construct, id: string, resourceFromParent: CfnResource) { + super(scope, id); + + new CfnResource(this, 'resource', { + type: 'AWS::Child::Resource', + properties: { + ReferenceToResourceInParentStack: resourceFromParent.ref + } + }); + + new CfnResource(this, 'resource2', { + type: 'My::Resource::2', + properties: { + Prop1: resourceFromParent.getAtt('Attr'), + Prop2: resourceFromParent.ref, + } + }); + } + } + + const app = new App(); + const parentStack = new Stack(app, 'parent'); + + const resource = new CfnResource(parentStack, 'parent-resource', { type: 'AWS::Parent::Resource' }); + + const nested = new MyNestedStack(parentStack, 'nested', resource); + + // THEN + app.synth(); + + // nested template should use a parameter to reference the resource from the parent stack + expect(nested).toMatch({ + Resources: + { + resource: + { + Type: 'AWS::Child::Resource', + Properties: + { ReferenceToResourceInParentStack: { Ref: 'referencetoparentparentresourceD56EA8F7Ref' } } + }, + resource2: + { + Type: 'My::Resource::2', + Properties: + { + Prop1: { Ref: 'referencetoparentparentresourceD56EA8F7Attr' }, + Prop2: { Ref: 'referencetoparentparentresourceD56EA8F7Ref' } + } + } + }, + Parameters: + { + referencetoparentparentresourceD56EA8F7Ref: { Type: 'String' }, + referencetoparentparentresourceD56EA8F7Attr: { Type: 'String' } + } + }); + + // parent template should pass in the value through the parameter + expect(parentStack).to(haveResource('AWS::CloudFormation::Stack', { + Parameters: { + referencetoparentparentresourceD56EA8F7Ref: { + Ref: "parentresource" + }, + referencetoparentparentresourceD56EA8F7Attr: { + "Fn::GetAtt": [ + "parentresource", + "Attr" + ] + } + }, + })); + + test.done(); + }, + + 'references to a resource in the nested stack in the parent is translated into a cfn output'(test: Test) { + class MyNestedStack extends NestedStack { + public readonly resourceFromChild: CfnResource; + + constructor(scope: Construct, id: string) { + super(scope, id); + + this.resourceFromChild = new CfnResource(this, 'resource', { + type: 'AWS::Child::Resource', + }); + } + } + + const app = new App(); + const parentStack = new Stack(app, 'parent'); + + const nested = new MyNestedStack(parentStack, 'nested'); + + new CfnResource(parentStack, 'another-parent-resource', { + type: 'AWS::Parent::Resource', + properties: { + RefToResourceInNestedStack: nested.resourceFromChild.ref + } + }); + + // references are added during "prepare" + app.synth(); + + // nested template should use a parameter to reference the resource from the parent stack + expect(nested).toMatch({ + Resources: { + resource: { Type: 'AWS::Child::Resource' } + }, + Outputs: { + parentnestedresource4D680677Ref: { Value: { Ref: 'resource' } } + } + }); + + // parent template should pass in the value through the parameter + expect(parentStack).to(haveResource('AWS::Parent::Resource', { + RefToResourceInNestedStack: { + "Fn::GetAtt": [ + "nestedNestedStacknestedNestedStackResource3DD143BF", + "Outputs.parentnestedresource4D680677Ref" + ] + } + })); + + test.done(); + }, + + 'nested stack references a resource from another non-nested stack (not the parent)'(test: Test) { + // GIVEN + const app = new App(); + const stack1 = new Stack(app, 'Stack1'); + const stack2 = new Stack(app, 'Stack2'); + const nestedUnderStack1 = new NestedStack(stack1, 'NestedUnderStack1'); + const resourceInStack2 = new CfnResource(stack2, 'ResourceInStack2', { type: 'MyResource' }); + + // WHEN + new CfnResource(nestedUnderStack1, 'ResourceInNestedStack1', { + type: 'Nested::Resource', + properties: { + RefToSibling: resourceInStack2.getAtt('MyAttribute') + } + }); + + // THEN + const assembly = app.synth(); + + // producing stack should have an export + expect(stack2).toMatch({ + Resources: { + ResourceInStack2: { Type: "MyResource" } + }, + Outputs: { + ExportsOutputFnGetAttResourceInStack2MyAttributeC15F1009: { + Value: { "Fn::GetAtt": ["ResourceInStack2", "MyAttribute"] }, + Export: { Name: "Stack2:ExportsOutputFnGetAttResourceInStack2MyAttributeC15F1009" } + } + } + }); + + // nested stack uses Fn::ImportValue like normal + expect(nestedUnderStack1).toMatch({ + Resources: { + ResourceInNestedStack1: { + Type: "Nested::Resource", + Properties: { + RefToSibling: { + "Fn::ImportValue": "Stack2:ExportsOutputFnGetAttResourceInStack2MyAttributeC15F1009" + } + } + } + } + }); + + // verify a depedency was established between the parents + const stack1Artifact = assembly.getStack(stack1.stackName); + const stack2Artifact = assembly.getStack(stack2.stackName); + test.deepEqual(stack1Artifact.dependencies.length, 1); + test.deepEqual(stack2Artifact.dependencies.length, 0); + test.same(stack1Artifact.dependencies[0], stack2Artifact); + test.done(); + }, + + 'another non-nested stack takes a reference on a resource within the nested stack (the parent exports)'(test: Test) { + // GIVEN + const app = new App(); + const stack1 = new Stack(app, 'Stack1'); + const stack2 = new Stack(app, 'Stack2'); + const nestedUnderStack1 = new NestedStack(stack1, 'NestedUnderStack1'); + const resourceInNestedStack = new CfnResource(nestedUnderStack1, 'ResourceInNestedStack', { type: 'MyResource' }); + + // WHEN + new CfnResource(stack2, 'ResourceInStack2', { + type: 'JustResource', + properties: { + RefToSibling: resourceInNestedStack.getAtt('MyAttribute') + } + }); + + // THEN + const assembly = app.synth(); + + // nested stack should output this value as if it was referenced by the parent (without the export) + expect(nestedUnderStack1).toMatch({ + Resources: { + ResourceInNestedStack: { + Type: "MyResource" + } + }, + Outputs: { + Stack1NestedUnderStack1ResourceInNestedStack6EE9DCD2MyAttribute: { + Value: { + "Fn::GetAtt": [ + "ResourceInNestedStack", + "MyAttribute" + ] + } + } + } + }); + + // parent stack (stack1) should export this value + test.deepEqual(assembly.getStack(stack1.stackName).template.Outputs, { + ExportsOutputFnGetAttNestedUnderStack1NestedStackNestedUnderStack1NestedStackResourceF616305BOutputsStack1NestedUnderStack1ResourceInNestedStack6EE9DCD2MyAttribute564EECF3: { + Value: { 'Fn::GetAtt': ['NestedUnderStack1NestedStackNestedUnderStack1NestedStackResourceF616305B', 'Outputs.Stack1NestedUnderStack1ResourceInNestedStack6EE9DCD2MyAttribute'] }, + Export: { Name: 'Stack1:ExportsOutputFnGetAttNestedUnderStack1NestedStackNestedUnderStack1NestedStackResourceF616305BOutputsStack1NestedUnderStack1ResourceInNestedStack6EE9DCD2MyAttribute564EECF3' } + } + }); + + // consuming stack should use ImportValue to import the value from the parent stack + expect(stack2).toMatch({ + Resources: { + ResourceInStack2: { + Type: "JustResource", + Properties: { + RefToSibling: { + "Fn::ImportValue": "Stack1:ExportsOutputFnGetAttNestedUnderStack1NestedStackNestedUnderStack1NestedStackResourceF616305BOutputsStack1NestedUnderStack1ResourceInNestedStack6EE9DCD2MyAttribute564EECF3" + } + } + } + } + }); + + test.deepEqual(assembly.stacks.length, 2); + const stack1Artifact = assembly.getStack(stack1.stackName); + const stack2Artifact = assembly.getStack(stack2.stackName); + test.deepEqual(stack1Artifact.dependencies.length, 0); + test.deepEqual(stack2Artifact.dependencies.length, 1); + test.same(stack2Artifact.dependencies[0], stack1Artifact); + test.done(); + }, + + 'references between sibling nested stacks should output from one and getAtt from the other'(test: Test) { + // GIVEN + const app = new App(); + const parent = new Stack(app, 'Parent'); + const nested1 = new NestedStack(parent, 'Nested1'); + const nested2 = new NestedStack(parent, 'Nested2'); + const resource1 = new CfnResource(nested1, 'Resource1', { type: 'Resource1' }); + + // WHEN + new CfnResource(nested2, 'Resource2', { + type: 'Resource2', + properties: { + RefToResource1: resource1.ref + } + }); + + // THEN + app.synth(); + + // producing nested stack + expect(nested1).toMatch({ + Resources: { + Resource1: { + Type: "Resource1" + } + }, + Outputs: { + ParentNested1Resource15F3F0657Ref: { + Value: { + Ref: "Resource1" + } + } + } + }); + + // consuming nested stack + expect(nested2).toMatch({ + Resources: { + Resource2: { + Type: "Resource2", + Properties: { + RefToResource1: { + Ref: "referencetoParentNested1NestedStackNested1NestedStackResource9C05342COutputsParentNested1Resource15F3F0657Ref" + } + } + } + }, + Parameters: { + referencetoParentNested1NestedStackNested1NestedStackResource9C05342COutputsParentNested1Resource15F3F0657Ref: { + Type: "String" + } + } + }); + + // parent + expect(parent).to(haveResource('AWS::CloudFormation::Stack', { + Parameters: { + referencetoParentNested1NestedStackNested1NestedStackResource9C05342COutputsParentNested1Resource15F3F0657Ref: { + "Fn::GetAtt": [ + "Nested1NestedStackNested1NestedStackResourceCD0AD36B", + "Outputs.ParentNested1Resource15F3F0657Ref" + ] + } + } + })); + + test.done(); + }, + + 'stackId returns AWS::StackId when referenced from the context of the nested stack'(test: Test) { + // GIVEN + const parent = new Stack(); + const nested = new NestedStack(parent, 'NestedStack'); + + // WHEN + new CfnResource(nested, 'NestedResource', { + type: 'Nested::Resource', + properties: { MyStackId: nested.stackId } + }); + + // THEN + expect(nested).to(haveResource('Nested::Resource', { + MyStackId: { Ref: "AWS::StackId" } + })); + + test.done(); + }, + + 'stackId returns the REF of the CloudFormation::Stack resource when referenced from the parent stack'(test: Test) { + // GIVEN + const parent = new Stack(); + const nested = new NestedStack(parent, 'NestedStack'); + + // WHEN + new CfnResource(parent, 'ParentResource', { + type: 'Parent::Resource', + properties: { NestedStackId: nested.stackId } + }); + + // THEN + expect(parent).to(haveResource('Parent::Resource', { + NestedStackId: { Ref: "NestedStackNestedStackNestedStackNestedStackResourceB70834FD" } + })); + + test.done(); + }, + + 'stackName returns AWS::StackName when referenced from the context of the nested stack'(test: Test) { + // GIVEN + const parent = new Stack(); + const nested = new NestedStack(parent, 'NestedStack'); + + // WHEN + new CfnResource(nested, 'NestedResource', { + type: 'Nested::Resource', + properties: { MyStackName: nested.stackName } + }); + + // THEN + expect(nested).to(haveResource('Nested::Resource', { + MyStackName: { Ref: "AWS::StackName" } + })); + + test.done(); + }, + + 'stackName returns the REF of the CloudFormation::Stack resource when referenced from the parent stack'(test: Test) { + // GIVEN + const parent = new Stack(); + const nested = new NestedStack(parent, 'NestedStack'); + + // WHEN + new CfnResource(parent, 'ParentResource', { + type: 'Parent::Resource', + properties: { NestedStackName: nested.stackName } + }); + + // THEN + expect(parent).to(haveResource('Parent::Resource', { + NestedStackName: { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "/", + { + Ref: "NestedStackNestedStackNestedStackNestedStackResourceB70834FD" + } + ] + } + ] + } + })); + + test.done(); + }, + + '"account", "region" and "environment" are all derived from the parent'(test: Test) { + // GIVEN + const app = new App(); + const parent = new Stack(app, 'ParentStack', { env: { account: '1234account', region: 'us-east-44' } }); + + // WHEN + const nested = new NestedStack(parent, 'NestedStack'); + + // THEN + test.deepEqual(nested.environment, parent.environment); + test.deepEqual(nested.account, parent.account); + test.deepEqual(nested.region, parent.region); + test.done(); + }, + + 'double-nested stack'(test: Test) { + // GIVEN + const app = new App(); + const parent = new Stack(app, 'stack'); + + // WHEN + const nested1 = new NestedStack(parent, 'Nested1'); + const nested2 = new NestedStack(nested1, 'Nested2'); + + new CfnResource(nested1, 'Resource1', { type: 'Resource::1' }); + new CfnResource(nested2, 'Resource2', { type: 'Resource::2' }); + + // THEN + const assembly = app.synth(); + + // nested2 is a "leaf", so it's just the resource + expect(nested2).toMatch({ + Resources: { + Resource2: { Type: "Resource::2" } + } + }); + + // nested1 wires the nested2 template through parameters, so we expect those + expect(nested1).to(haveResource('Resource::1')); + const nested2Template = SynthUtils.toCloudFormation(nested1); + test.deepEqual(nested2Template.Parameters, { + referencetostackAssetParameters8169c6f8aaeaf5e2e8620f5f895ffe2099202ccb4b6889df48fe0967a894235cS3BucketE8768F5CRef: { Type: 'String' }, + referencetostackAssetParameters8169c6f8aaeaf5e2e8620f5f895ffe2099202ccb4b6889df48fe0967a894235cS3VersionKey49DD83A2Ref: { Type: 'String' }, + }); + + // parent stack should have two sets of parameters. one for the first nested stack and the second + // for the second nested stack, passed in as parameters to the first + const template = SynthUtils.toCloudFormation(parent); + test.deepEqual(template.Parameters, { + AssetParameters8169c6f8aaeaf5e2e8620f5f895ffe2099202ccb4b6889df48fe0967a894235cS3BucketDE3B88D6: { Type: 'String', Description: 'S3 bucket for asset "8169c6f8aaeaf5e2e8620f5f895ffe2099202ccb4b6889df48fe0967a894235c"' }, + AssetParameters8169c6f8aaeaf5e2e8620f5f895ffe2099202ccb4b6889df48fe0967a894235cS3VersionKey3A62EFEA: { Type: 'String', Description: 'S3 key for asset version "8169c6f8aaeaf5e2e8620f5f895ffe2099202ccb4b6889df48fe0967a894235c"' }, + AssetParameters8169c6f8aaeaf5e2e8620f5f895ffe2099202ccb4b6889df48fe0967a894235cArtifactHash7DC546E0: { Type: 'String', Description: 'Artifact hash for asset "8169c6f8aaeaf5e2e8620f5f895ffe2099202ccb4b6889df48fe0967a894235c"' }, + AssetParameters8b50795a950cca6b01352f162c45d9d274dee6bc409f2f2b2ed029ad6828b3bfS3Bucket76ACFB38: { Type: 'String', Description: 'S3 bucket for asset "8b50795a950cca6b01352f162c45d9d274dee6bc409f2f2b2ed029ad6828b3bf"' }, + AssetParameters8b50795a950cca6b01352f162c45d9d274dee6bc409f2f2b2ed029ad6828b3bfS3VersionKey04162EF1: { Type: 'String', Description: 'S3 key for asset version "8b50795a950cca6b01352f162c45d9d274dee6bc409f2f2b2ed029ad6828b3bf"' }, + AssetParameters8b50795a950cca6b01352f162c45d9d274dee6bc409f2f2b2ed029ad6828b3bfArtifactHashF227ADD3: { Type: 'String', Description: 'Artifact hash for asset "8b50795a950cca6b01352f162c45d9d274dee6bc409f2f2b2ed029ad6828b3bf"' } + }); + + // proxy asset params to nested stack + expect(parent).to(haveResource('AWS::CloudFormation::Stack', { + Parameters: { + referencetostackAssetParameters8169c6f8aaeaf5e2e8620f5f895ffe2099202ccb4b6889df48fe0967a894235cS3BucketE8768F5CRef: { Ref: "AssetParameters8169c6f8aaeaf5e2e8620f5f895ffe2099202ccb4b6889df48fe0967a894235cS3BucketDE3B88D6" }, + referencetostackAssetParameters8169c6f8aaeaf5e2e8620f5f895ffe2099202ccb4b6889df48fe0967a894235cS3VersionKey49DD83A2Ref: { Ref: "AssetParameters8169c6f8aaeaf5e2e8620f5f895ffe2099202ccb4b6889df48fe0967a894235cS3VersionKey3A62EFEA" } + } + })); + + // parent stack should have 2 assets + test.deepEqual(assembly.getStack(parent.stackName).assets.length, 2); + test.done(); + }, + + 'assets within nested stacks are proxied from the parent'(test: Test) { + // GIVEN + const app = new App(); + const parent = new Stack(app, 'ParentStack'); + const nested = new NestedStack(parent, 'NestedStack'); + + // WHEN + const asset = new s3_assets.Asset(nested, 'asset', { + path: path.join(__dirname, 'asset-fixture.txt') + }); + + new CfnResource(nested, 'NestedResource', { + type: 'Nested::Resource', + properties: { + AssetBucket: asset.s3BucketName, + AssetKey: asset.s3ObjectKey + } + }); + + // THEN + const assembly = app.synth(); + const template = SynthUtils.toCloudFormation(parent); + + // two sets of asset parameters: one for the nested stack itself and one as a proxy for the asset within the stack + test.deepEqual(template.Parameters, { + AssetParametersdb01ee2eb7adc7915e364dc410d861e569543f9be3761d535a68d5c2cc181281S3BucketC188F637: { Type: 'String', Description: 'S3 bucket for asset "db01ee2eb7adc7915e364dc410d861e569543f9be3761d535a68d5c2cc181281"' }, + AssetParametersdb01ee2eb7adc7915e364dc410d861e569543f9be3761d535a68d5c2cc181281S3VersionKeyC7F4DBF2: { Type: 'String', Description: 'S3 key for asset version "db01ee2eb7adc7915e364dc410d861e569543f9be3761d535a68d5c2cc181281"' }, + AssetParametersdb01ee2eb7adc7915e364dc410d861e569543f9be3761d535a68d5c2cc181281ArtifactHash373B14D2: { Type: 'String', Description: 'Artifact hash for asset "db01ee2eb7adc7915e364dc410d861e569543f9be3761d535a68d5c2cc181281"' }, + AssetParameters46b107d6db798ca46046b8669d057a4debcbdbaaddb6170400748c2f9e4f9d71S3Bucket3C4265E9: { Type: 'String', Description: 'S3 bucket for asset "46b107d6db798ca46046b8669d057a4debcbdbaaddb6170400748c2f9e4f9d71"' }, + AssetParameters46b107d6db798ca46046b8669d057a4debcbdbaaddb6170400748c2f9e4f9d71S3VersionKey8E981535: { Type: 'String', Description: 'S3 key for asset version "46b107d6db798ca46046b8669d057a4debcbdbaaddb6170400748c2f9e4f9d71"' }, + AssetParameters46b107d6db798ca46046b8669d057a4debcbdbaaddb6170400748c2f9e4f9d71ArtifactHash45A28583: { Type: 'String', Description: 'Artifact hash for asset "46b107d6db798ca46046b8669d057a4debcbdbaaddb6170400748c2f9e4f9d71"' } + }); + + // asset proxy parameters are passed to the nested stack + expect(parent).to(haveResource('AWS::CloudFormation::Stack', { + Parameters: { + referencetoParentStackAssetParametersdb01ee2eb7adc7915e364dc410d861e569543f9be3761d535a68d5c2cc181281S3Bucket82C55B96Ref: { Ref: "AssetParametersdb01ee2eb7adc7915e364dc410d861e569543f9be3761d535a68d5c2cc181281S3BucketC188F637" }, + referencetoParentStackAssetParametersdb01ee2eb7adc7915e364dc410d861e569543f9be3761d535a68d5c2cc181281S3VersionKeyA43C3CC6Ref: { Ref: "AssetParametersdb01ee2eb7adc7915e364dc410d861e569543f9be3761d535a68d5c2cc181281S3VersionKeyC7F4DBF2" }, + } + })); + + // parent stack should have 2 assets + test.deepEqual(assembly.getStack(parent.stackName).assets.length, 2); + test.done(); + }, + + 'docker image assets are wired through the top-level stack'(test: Test) { + // GIVEN + const app = new App(); + const parent = new Stack(app, 'my-stack'); + const nested = new NestedStack(parent, 'nested-stack'); + + // WHEN + const location = nested.addDockerImageAsset({ + directoryName: 'my-image', + dockerBuildArgs: { key: 'value', boom: 'bam' }, + dockerBuildTarget: 'buildTarget', + sourceHash: 'hash-of-source', + }); + + // use the asset, so the parameters will be wired. + new sns.Topic(nested, 'MyTopic', { + displayName: `image location is ${location.imageUri}` + }); + + // THEN + const parentParams = SynthUtils.toCloudFormation(parent).Parameters; + const nestedParams = SynthUtils.toCloudFormation(nested).Parameters; + test.ok(parentParams.AssetParametershashofsourceImageName1CFB7817); + test.ok(nestedParams.referencetomystackAssetParametershashofsourceImageName7D5F0882Ref); + + // verify parameter is passed to nested stack + expect(parent).to(haveResource('AWS::CloudFormation::Stack', { + Parameters: { + referencetomystackAssetParametershashofsourceImageName7D5F0882Ref: { + Ref: "AssetParametershashofsourceImageName1CFB7817" + } + } + })); + + test.done(); + } +}; diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.docker-asset.lit.expected.json b/packages/@aws-cdk/aws-codebuild/test/integ.docker-asset.lit.expected.json index 909d693e427b2..6312a5806c50e 100644 --- a/packages/@aws-cdk/aws-codebuild/test/integ.docker-asset.lit.expected.json +++ b/packages/@aws-cdk/aws-codebuild/test/integ.docker-asset.lit.expected.json @@ -1,22 +1,4 @@ { - "Parameters": { - "MyImageImageName953AD232": { - "Type": "String", - "Description": "ECR repository name and tag asset \"test-codebuild-docker-asset/MyImage\"" - }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3Bucket92AB06B6": { - "Type": "String", - "Description": "S3 bucket for asset \"test-codebuild-docker-asset/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" - }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276": { - "Type": "String", - "Description": "S3 key for asset version \"test-codebuild-docker-asset/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" - }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeArtifactHash8BCBAA49": { - "Type": "String", - "Description": "Artifact hash for asset \"test-codebuild-docker-asset/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" - } - }, "Resources": { "MyImageAdoptRepository6CA902F6": { "Type": "Custom::ECRAdoptedRepository", @@ -34,7 +16,7 @@ "Fn::Split": [ "@sha256:", { - "Ref": "MyImageImageName953AD232" + "Ref": "AssetParameters90a0be42035e43667ec935f49efc23c6bd6c9ec566b92dae96a94f505f091f2bImageName2105B480" } ] } @@ -117,7 +99,7 @@ "Fn::Split": [ "@sha256:", { - "Ref": "MyImageImageName953AD232" + "Ref": "AssetParameters90a0be42035e43667ec935f49efc23c6bd6c9ec566b92dae96a94f505f091f2bImageName2105B480" } ] } @@ -143,7 +125,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3Bucket92AB06B6" + "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB" }, "S3Key": { "Fn::Join": [ @@ -156,7 +138,7 @@ "Fn::Split": [ "||", { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276" + "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" } ] } @@ -169,7 +151,7 @@ "Fn::Split": [ "||", { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276" + "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" } ] } @@ -336,77 +318,11 @@ "", [ { - "Fn::Select": [ - 4, - { - "Fn::Split": [ - ":", - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ecr:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":repository/", - { - "Fn::GetAtt": [ - "MyImageAdoptRepository6CA902F6", - "RepositoryName" - ] - } - ] - ] - } - ] - } - ] + "Ref": "AWS::AccountId" }, ".dkr.ecr.", { - "Fn::Select": [ - 3, - { - "Fn::Split": [ - ":", - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ecr:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":repository/", - { - "Fn::GetAtt": [ - "MyImageAdoptRepository6CA902F6", - "RepositoryName" - ] - } - ] - ] - } - ] - } - ] + "Ref": "AWS::Region" }, ".", { @@ -414,9 +330,16 @@ }, "/", { - "Fn::GetAtt": [ - "MyImageAdoptRepository6CA902F6", - "RepositoryName" + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "@sha256:", + { + "Ref": "AssetParameters90a0be42035e43667ec935f49efc23c6bd6c9ec566b92dae96a94f505f091f2bImageName2105B480" + } + ] + } ] }, "@sha256:", @@ -427,7 +350,7 @@ "Fn::Split": [ "@sha256:", { - "Ref": "MyImageImageName953AD232" + "Ref": "AssetParameters90a0be42035e43667ec935f49efc23c6bd6c9ec566b92dae96a94f505f091f2bImageName2105B480" } ] } @@ -452,5 +375,23 @@ } } } + }, + "Parameters": { + "AssetParameters90a0be42035e43667ec935f49efc23c6bd6c9ec566b92dae96a94f505f091f2bImageName2105B480": { + "Type": "String", + "Description": "ECR repository name and tag for asset \"90a0be42035e43667ec935f49efc23c6bd6c9ec566b92dae96a94f505f091f2b\"" + }, + "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB": { + "Type": "String", + "Description": "S3 bucket for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" + }, + "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7": { + "Type": "String", + "Description": "S3 key for asset version \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" + }, + "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1ArtifactHash7E5AE478": { + "Type": "String", + "Description": "Artifact hash for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" + } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-group.expected.json b/packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-group.expected.json index f52de9c603ce4..4953bbe92f195 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-group.expected.json +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-group.expected.json @@ -36,7 +36,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "HandlerCodeS3Bucket8DD11ED9" + "Ref": "AssetParametersedb7466707eb899fbaee22c1e67f9443e9edcc2eeda0b58d8448f7c4157746b3S3BucketD9AF62D9" }, "S3Key": { "Fn::Join": [ @@ -49,7 +49,7 @@ "Fn::Split": [ "||", { - "Ref": "HandlerCodeS3VersionKey0BB5191E" + "Ref": "AssetParametersedb7466707eb899fbaee22c1e67f9443e9edcc2eeda0b58d8448f7c4157746b3S3VersionKey3ACE0CAC" } ] } @@ -62,7 +62,7 @@ "Fn::Split": [ "||", { - "Ref": "HandlerCodeS3VersionKey0BB5191E" + "Ref": "AssetParametersedb7466707eb899fbaee22c1e67f9443e9edcc2eeda0b58d8448f7c4157746b3S3VersionKey3ACE0CAC" } ] } @@ -207,7 +207,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "PreHookCodeS3BucketE2616D65" + "Ref": "AssetParameters87da351d4c9de5eead78cb80dde66bcbb6c42c418c3368747f32a4e9c013a2e1S3Bucket1D1783A4" }, "S3Key": { "Fn::Join": [ @@ -220,7 +220,7 @@ "Fn::Split": [ "||", { - "Ref": "PreHookCodeS3VersionKey292C5372" + "Ref": "AssetParameters87da351d4c9de5eead78cb80dde66bcbb6c42c418c3368747f32a4e9c013a2e1S3VersionKey88595F37" } ] } @@ -233,7 +233,7 @@ "Fn::Split": [ "||", { - "Ref": "PreHookCodeS3VersionKey292C5372" + "Ref": "AssetParameters87da351d4c9de5eead78cb80dde66bcbb6c42c418c3368747f32a4e9c013a2e1S3VersionKey88595F37" } ] } @@ -340,7 +340,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "PostHookCodeS3BucketECF09EB8" + "Ref": "AssetParameters93dbd8c02dbfca9077c9d83cb6d3a94659988c7d143988da4a554033a58f963cS3Bucket7EF62972" }, "S3Key": { "Fn::Join": [ @@ -353,7 +353,7 @@ "Fn::Split": [ "||", { - "Ref": "PostHookCodeS3VersionKey53451C7E" + "Ref": "AssetParameters93dbd8c02dbfca9077c9d83cb6d3a94659988c7d143988da4a554033a58f963cS3VersionKeyBF9A490E" } ] } @@ -366,7 +366,7 @@ "Fn::Split": [ "||", { - "Ref": "PostHookCodeS3VersionKey53451C7E" + "Ref": "AssetParameters93dbd8c02dbfca9077c9d83cb6d3a94659988c7d143988da4a554033a58f963cS3VersionKeyBF9A490E" } ] } @@ -395,7 +395,6 @@ "Properties": { "ComparisonOperator": "GreaterThanThreshold", "EvaluationPeriods": 1, - "Threshold": 1, "Dimensions": [ { "Name": "FunctionName", @@ -421,7 +420,8 @@ "MetricName": "Errors", "Namespace": "AWS/Lambda", "Period": 300, - "Statistic": "Sum" + "Statistic": "Sum", + "Threshold": 1 } }, "BlueGreenDeploymentApplication36C892C0": { @@ -549,41 +549,41 @@ } }, "Parameters": { - "HandlerCodeS3Bucket8DD11ED9": { + "AssetParametersedb7466707eb899fbaee22c1e67f9443e9edcc2eeda0b58d8448f7c4157746b3S3BucketD9AF62D9": { "Type": "String", - "Description": "S3 bucket for asset \"aws-cdk-codedeploy-lambda/Handler/Code\"" + "Description": "S3 bucket for asset \"edb7466707eb899fbaee22c1e67f9443e9edcc2eeda0b58d8448f7c4157746b3\"" }, - "HandlerCodeS3VersionKey0BB5191E": { + "AssetParametersedb7466707eb899fbaee22c1e67f9443e9edcc2eeda0b58d8448f7c4157746b3S3VersionKey3ACE0CAC": { "Type": "String", - "Description": "S3 key for asset version \"aws-cdk-codedeploy-lambda/Handler/Code\"" + "Description": "S3 key for asset version \"edb7466707eb899fbaee22c1e67f9443e9edcc2eeda0b58d8448f7c4157746b3\"" }, - "HandlerCodeArtifactHashD7814EF8": { + "AssetParametersedb7466707eb899fbaee22c1e67f9443e9edcc2eeda0b58d8448f7c4157746b3ArtifactHash20E29DAB": { "Type": "String", - "Description": "Artifact hash for asset \"aws-cdk-codedeploy-lambda/Handler/Code\"" + "Description": "Artifact hash for asset \"edb7466707eb899fbaee22c1e67f9443e9edcc2eeda0b58d8448f7c4157746b3\"" }, - "PreHookCodeS3BucketE2616D65": { + "AssetParameters87da351d4c9de5eead78cb80dde66bcbb6c42c418c3368747f32a4e9c013a2e1S3Bucket1D1783A4": { "Type": "String", - "Description": "S3 bucket for asset \"aws-cdk-codedeploy-lambda/PreHook/Code\"" + "Description": "S3 bucket for asset \"87da351d4c9de5eead78cb80dde66bcbb6c42c418c3368747f32a4e9c013a2e1\"" }, - "PreHookCodeS3VersionKey292C5372": { + "AssetParameters87da351d4c9de5eead78cb80dde66bcbb6c42c418c3368747f32a4e9c013a2e1S3VersionKey88595F37": { "Type": "String", - "Description": "S3 key for asset version \"aws-cdk-codedeploy-lambda/PreHook/Code\"" + "Description": "S3 key for asset version \"87da351d4c9de5eead78cb80dde66bcbb6c42c418c3368747f32a4e9c013a2e1\"" }, - "PreHookCodeArtifactHash540B37CB": { + "AssetParameters87da351d4c9de5eead78cb80dde66bcbb6c42c418c3368747f32a4e9c013a2e1ArtifactHash38509A01": { "Type": "String", - "Description": "Artifact hash for asset \"aws-cdk-codedeploy-lambda/PreHook/Code\"" + "Description": "Artifact hash for asset \"87da351d4c9de5eead78cb80dde66bcbb6c42c418c3368747f32a4e9c013a2e1\"" }, - "PostHookCodeS3BucketECF09EB8": { + "AssetParameters93dbd8c02dbfca9077c9d83cb6d3a94659988c7d143988da4a554033a58f963cS3Bucket7EF62972": { "Type": "String", - "Description": "S3 bucket for asset \"aws-cdk-codedeploy-lambda/PostHook/Code\"" + "Description": "S3 bucket for asset \"93dbd8c02dbfca9077c9d83cb6d3a94659988c7d143988da4a554033a58f963c\"" }, - "PostHookCodeS3VersionKey53451C7E": { + "AssetParameters93dbd8c02dbfca9077c9d83cb6d3a94659988c7d143988da4a554033a58f963cS3VersionKeyBF9A490E": { "Type": "String", - "Description": "S3 key for asset version \"aws-cdk-codedeploy-lambda/PostHook/Code\"" + "Description": "S3 key for asset version \"93dbd8c02dbfca9077c9d83cb6d3a94659988c7d143988da4a554033a58f963c\"" }, - "PostHookCodeArtifactHash73D72B37": { + "AssetParameters93dbd8c02dbfca9077c9d83cb6d3a94659988c7d143988da4a554033a58f963cArtifactHashBF6C1240": { "Type": "String", - "Description": "Artifact hash for asset \"aws-cdk-codedeploy-lambda/PostHook/Code\"" + "Description": "Artifact hash for asset \"93dbd8c02dbfca9077c9d83cb6d3a94659988c7d143988da4a554033a58f963c\"" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-dynamodb-global/test/integ.dynamodb.global.expected.json b/packages/@aws-cdk/aws-dynamodb-global/test/integ.dynamodb.global.expected.json index aeef6408afa31..2085cc872af7f 100644 --- a/packages/@aws-cdk/aws-dynamodb-global/test/integ.dynamodb.global.expected.json +++ b/packages/@aws-cdk/aws-dynamodb-global/test/integ.dynamodb.global.expected.json @@ -25,8 +25,8 @@ }, "TableName": "integrationtest" }, - "DeletionPolicy": "Delete", - "UpdateReplacePolicy": "Delete" + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" } } }, @@ -56,8 +56,8 @@ }, "TableName": "integrationtest" }, - "DeletionPolicy": "Delete", - "UpdateReplacePolicy": "Delete" + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" } } }, @@ -87,8 +87,8 @@ }, "TableName": "integrationtest" }, - "DeletionPolicy": "Delete", - "UpdateReplacePolicy": "Delete" + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" } } }, @@ -160,7 +160,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "SingletonLambdaD38B65A66B544FB69BAD9CD40A6DAC12CodeS3BucketF66FB543" + "Ref": "AssetParameters94575de3fd18404e41e94a6c1d4b7a1eec2985fd23d2c11999d095e09667ba3aS3Bucket66843878" }, "S3Key": { "Fn::Join": [ @@ -173,7 +173,7 @@ "Fn::Split": [ "||", { - "Ref": "SingletonLambdaD38B65A66B544FB69BAD9CD40A6DAC12CodeS3VersionKey59DB89A0" + "Ref": "AssetParameters94575de3fd18404e41e94a6c1d4b7a1eec2985fd23d2c11999d095e09667ba3aS3VersionKey14680708" } ] } @@ -186,7 +186,7 @@ "Fn::Split": [ "||", { - "Ref": "SingletonLambdaD38B65A66B544FB69BAD9CD40A6DAC12CodeS3VersionKey59DB89A0" + "Ref": "AssetParameters94575de3fd18404e41e94a6c1d4b7a1eec2985fd23d2c11999d095e09667ba3aS3VersionKey14680708" } ] } @@ -229,23 +229,23 @@ "ResourceType": "Custom::DynamoGlobalTableCoordinator", "TableName": "integrationtest" }, - "DeletionPolicy": "Delete", - "UpdateReplacePolicy": "Delete" + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" } }, "Parameters": { - "SingletonLambdaD38B65A66B544FB69BAD9CD40A6DAC12CodeS3BucketF66FB543": { + "AssetParameters94575de3fd18404e41e94a6c1d4b7a1eec2985fd23d2c11999d095e09667ba3aS3Bucket66843878": { "Type": "String", - "Description": "S3 bucket for asset \"globdynamodbinteg-CustomResource/SingletonLambdaD38B65A66B544FB69BAD9CD40A6DAC12/Code\"" + "Description": "S3 bucket for asset \"94575de3fd18404e41e94a6c1d4b7a1eec2985fd23d2c11999d095e09667ba3a\"" }, - "SingletonLambdaD38B65A66B544FB69BAD9CD40A6DAC12CodeS3VersionKey59DB89A0": { + "AssetParameters94575de3fd18404e41e94a6c1d4b7a1eec2985fd23d2c11999d095e09667ba3aS3VersionKey14680708": { "Type": "String", - "Description": "S3 key for asset version \"globdynamodbinteg-CustomResource/SingletonLambdaD38B65A66B544FB69BAD9CD40A6DAC12/Code\"" + "Description": "S3 key for asset version \"94575de3fd18404e41e94a6c1d4b7a1eec2985fd23d2c11999d095e09667ba3a\"" }, - "SingletonLambdaD38B65A66B544FB69BAD9CD40A6DAC12CodeArtifactHashCE92982B": { + "AssetParameters94575de3fd18404e41e94a6c1d4b7a1eec2985fd23d2c11999d095e09667ba3aArtifactHash5687D31D": { "Type": "String", - "Description": "Artifact hash for asset \"globdynamodbinteg-CustomResource/SingletonLambdaD38B65A66B544FB69BAD9CD40A6DAC12/Code\"" + "Description": "Artifact hash for asset \"94575de3fd18404e41e94a6c1d4b7a1eec2985fd23d2c11999d095e09667ba3a\"" } } } -] +] \ No newline at end of file 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 51fe3e18638d0..1c0746edbefe3 100644 --- a/packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts +++ b/packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts @@ -1,8 +1,6 @@ import assets = require('@aws-cdk/assets'); import ecr = require('@aws-cdk/aws-ecr'); -import cdk = require('@aws-cdk/core'); -import { Token } from '@aws-cdk/core'; -import cxapi = require('@aws-cdk/cx-api'); +import { Construct, Stack, Token } from '@aws-cdk/core'; import fs = require('fs'); import path = require('path'); import { AdoptedRepository } from './adopted-repository'; @@ -48,7 +46,7 @@ export interface DockerImageAssetProps extends assets.CopyOptions { * * The image will be created in build time and uploaded to an ECR repository. */ -export class DockerImageAsset extends cdk.Construct implements assets.IAsset { +export class DockerImageAsset extends Construct implements assets.IAsset { /** * The full URI of the image (including a tag). Use this reference to pull * the asset. @@ -62,12 +60,7 @@ export class DockerImageAsset extends cdk.Construct implements assets.IAsset { public readonly sourceHash: string; - /** - * Directory where the source files are stored - */ - private readonly directory: string; - - constructor(scope: cdk.Construct, id: string, props: DockerImageAssetProps) { + constructor(scope: Construct, id: string, props: DockerImageAssetProps) { super(scope, id); // none of the properties use tokens @@ -96,32 +89,16 @@ export class DockerImageAsset extends cdk.Construct implements assets.IAsset { sourcePath: dir }); - this.directory = staging.stagedPath; this.sourceHash = staging.sourceHash; - const imageNameParameter = new cdk.CfnParameter(this, 'ImageName', { - type: 'String', - description: `ECR repository name and tag asset "${this.node.path}"`, - }); - - const asset: cxapi.ContainerImageAssetMetadataEntry = { - id: this.node.uniqueId, - packaging: 'container-image', - path: this.directory, - sourceHash: this.sourceHash, - imageNameParameter: imageNameParameter.logicalId, + const stack = Stack.of(this); + const location = stack.addDockerImageAsset({ + directoryName: staging.stagedPath, + dockerBuildArgs: props.buildArgs, + dockerBuildTarget: props.target, repositoryName: props.repositoryName, - buildArgs: props.buildArgs, - target: props.target - }; - - this.node.addMetadata(cxapi.ASSET_METADATA, asset); - - // Parse repository name and tag from the parameter (@sha256:) - // Example: cdk/cdkexampleimageb2d7f504@sha256:72c4f956379a43b5623d529ddd969f6826dde944d6221f445ff3e7add9875500 - const components = cdk.Fn.split('@sha256:', imageNameParameter.valueAsString); - const repositoryName = cdk.Fn.select(0, components).toString(); - const imageSha = cdk.Fn.select(1, components).toString(); + sourceHash: staging.sourceHash + }); // Require that repository adoption happens first, so we route the // input ARN into the Custom Resource and then get the URI which we use to @@ -129,8 +106,8 @@ export class DockerImageAsset extends cdk.Construct implements assets.IAsset { // // If adoption fails (because the repository might be twice-adopted), we // haven't already started using the image. - this.repository = new AdoptedRepository(this, 'AdoptRepository', { repositoryName }); - this.imageUri = `${this.repository.repositoryUri}@sha256:${imageSha}`; + this.repository = new AdoptedRepository(this, 'AdoptRepository', { repositoryName: location.repositoryName }); + this.imageUri = location.imageUri; } } diff --git a/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.expected.json b/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.expected.json index 900b9db64236f..e12ce012b88ef 100644 --- a/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.expected.json +++ b/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.expected.json @@ -1,22 +1,4 @@ { - "Parameters": { - "DockerImageImageName266E5998": { - "Type": "String", - "Description": "ECR repository name and tag asset \"integ-assets-docker/DockerImage\"" - }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3Bucket92AB06B6": { - "Type": "String", - "Description": "S3 bucket for asset \"integ-assets-docker/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" - }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276": { - "Type": "String", - "Description": "S3 key for asset version \"integ-assets-docker/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" - }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeArtifactHash8BCBAA49": { - "Type": "String", - "Description": "Artifact hash for asset \"integ-assets-docker/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" - } - }, "Resources": { "DockerImageAdoptRepositoryA86481BC": { "Type": "Custom::ECRAdoptedRepository", @@ -34,7 +16,7 @@ "Fn::Split": [ "@sha256:", { - "Ref": "DockerImageImageName266E5998" + "Ref": "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" } ] } @@ -117,7 +99,7 @@ "Fn::Split": [ "@sha256:", { - "Ref": "DockerImageImageName266E5998" + "Ref": "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" } ] } @@ -143,7 +125,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3Bucket92AB06B6" + "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB" }, "S3Key": { "Fn::Join": [ @@ -156,7 +138,7 @@ "Fn::Split": [ "||", { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276" + "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" } ] } @@ -169,7 +151,7 @@ "Fn::Split": [ "||", { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276" + "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" } ] } @@ -195,6 +177,24 @@ ] } }, + "Parameters": { + "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3": { + "Type": "String", + "Description": "ECR repository name and tag for asset \"1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439c\"" + }, + "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB": { + "Type": "String", + "Description": "S3 bucket for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" + }, + "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7": { + "Type": "String", + "Description": "S3 key for asset version \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" + }, + "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1ArtifactHash7E5AE478": { + "Type": "String", + "Description": "Artifact hash for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" + } + }, "Outputs": { "ImageUri": { "Value": { @@ -202,77 +202,11 @@ "", [ { - "Fn::Select": [ - 4, - { - "Fn::Split": [ - ":", - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ecr:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":repository/", - { - "Fn::GetAtt": [ - "DockerImageAdoptRepositoryA86481BC", - "RepositoryName" - ] - } - ] - ] - } - ] - } - ] + "Ref": "AWS::AccountId" }, ".dkr.ecr.", { - "Fn::Select": [ - 3, - { - "Fn::Split": [ - ":", - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ecr:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":repository/", - { - "Fn::GetAtt": [ - "DockerImageAdoptRepositoryA86481BC", - "RepositoryName" - ] - } - ] - ] - } - ] - } - ] + "Ref": "AWS::Region" }, ".", { @@ -280,9 +214,16 @@ }, "/", { - "Fn::GetAtt": [ - "DockerImageAdoptRepositoryA86481BC", - "RepositoryName" + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "@sha256:", + { + "Ref": "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" + } + ] + } ] }, "@sha256:", @@ -293,7 +234,7 @@ "Fn::Split": [ "@sha256:", { - "Ref": "DockerImageImageName266E5998" + "Ref": "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" } ] } @@ -304,4 +245,4 @@ } } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.expected.json b/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.expected.json new file mode 100644 index 0000000000000..a4e3e9c555c02 --- /dev/null +++ b/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.expected.json @@ -0,0 +1,96 @@ +{ + "Resources": { + "nestedstackwithimageNestedStacknestedstackwithimageNestedStackResourceDF784FD5": { + "Type": "AWS::CloudFormation::Stack", + "Properties": { + "TemplateURL": { + "Fn::Join": [ + "", + [ + "https://s3.", + { + "Ref": "AWS::Region" + }, + ".", + { + "Ref": "AWS::URLSuffix" + }, + "/", + { + "Ref": "AssetParameterse86ce5b2b85b89ce0737d48793770b4769ce64275b617643b8f9ed700e404b42S3BucketAA0C9A06" + }, + "/", + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameterse86ce5b2b85b89ce0737d48793770b4769ce64275b617643b8f9ed700e404b42S3VersionKeyA1BC2F52" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameterse86ce5b2b85b89ce0737d48793770b4769ce64275b617643b8f9ed700e404b42S3VersionKeyA1BC2F52" + } + ] + } + ] + } + ] + ] + }, + "Parameters": { + "referencetonestedstacksdockerAssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName6A10D714Ref": { + "Ref": "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" + }, + "referencetonestedstacksdockerAssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketDC016D33Ref": { + "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB" + }, + "referencetonestedstacksdockerAssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey3A8B3E45Ref": { + "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" + } + } + } + } + }, + "Parameters": { + "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3": { + "Type": "String", + "Description": "ECR repository name and tag for asset \"1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439c\"" + }, + "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB": { + "Type": "String", + "Description": "S3 bucket for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" + }, + "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7": { + "Type": "String", + "Description": "S3 key for asset version \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" + }, + "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1ArtifactHash7E5AE478": { + "Type": "String", + "Description": "Artifact hash for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" + }, + "AssetParameterse86ce5b2b85b89ce0737d48793770b4769ce64275b617643b8f9ed700e404b42S3BucketAA0C9A06": { + "Type": "String", + "Description": "S3 bucket for asset \"e86ce5b2b85b89ce0737d48793770b4769ce64275b617643b8f9ed700e404b42\"" + }, + "AssetParameterse86ce5b2b85b89ce0737d48793770b4769ce64275b617643b8f9ed700e404b42S3VersionKeyA1BC2F52": { + "Type": "String", + "Description": "S3 key for asset version \"e86ce5b2b85b89ce0737d48793770b4769ce64275b617643b8f9ed700e404b42\"" + }, + "AssetParameterse86ce5b2b85b89ce0737d48793770b4769ce64275b617643b8f9ed700e404b42ArtifactHashBB07F2FE": { + "Type": "String", + "Description": "Artifact hash for asset \"e86ce5b2b85b89ce0737d48793770b4769ce64275b617643b8f9ed700e404b42\"" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.ts b/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.ts new file mode 100644 index 0000000000000..ec801717c2e9c --- /dev/null +++ b/packages/@aws-cdk/aws-ecr-assets/test/integ.nested-stacks-docker.ts @@ -0,0 +1,28 @@ +import cfn = require('@aws-cdk/aws-cloudformation'); +import { App, CfnOutput, Construct, Stack, StackProps } from '@aws-cdk/core'; +import path = require('path'); +import ecr_assets = require('../lib'); + +class TheNestedStack extends cfn.NestedStack { + constructor(scope: Construct, id: string, props?: cfn.NestedStackProps) { + super(scope, id, props); + + const asset = new ecr_assets.DockerImageAsset(this, 'my-image', { + directory: path.join(__dirname, 'demo-image') + }); + + new CfnOutput(this, 'output', { value: asset.imageUri }); + } +} + +class TheParentStack extends Stack { + constructor(scope: Construct, id: string, props?: StackProps) { + super(scope, id, props); + + new TheNestedStack(this, 'nested-stack-with-image'); + } +} + +const app = new App(); +new TheParentStack(app, 'nested-stacks-docker'); +app.synth(); diff --git a/packages/@aws-cdk/aws-ecr-assets/test/test.image-asset.ts b/packages/@aws-cdk/aws-ecr-assets/test/test.image-asset.ts index 29f0458a9aaff..62b742a6e6df5 100644 --- a/packages/@aws-cdk/aws-ecr-assets/test/test.image-asset.ts +++ b/packages/@aws-cdk/aws-ecr-assets/test/test.image-asset.ts @@ -20,10 +20,9 @@ export = { // THEN const template = SynthUtils.synthesize(stack).template; - - test.deepEqual(template.Parameters.ImageImageName5E684353, { + test.deepEqual(template.Parameters.AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3, { Type: 'String', - Description: 'ECR repository name and tag asset "Image"' + Description: 'ECR repository name and tag for asset "1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439c"' }); test.done(); @@ -34,7 +33,7 @@ export = { const stack = new Stack(); // WHEN - const asset = new DockerImageAsset(stack, 'Image', { + new DockerImageAsset(stack, 'Image', { directory: path.join(__dirname, 'demo-image'), buildArgs: { a: 'b' @@ -42,7 +41,7 @@ export = { }); // THEN - const assetMetadata = asset.node.metadata.find(({ type }) => type === 'aws:cdk:asset'); + const assetMetadata = stack.node.metadata.find(({ type }) => type === 'aws:cdk:asset'); test.deepEqual(assetMetadata && assetMetadata.data.buildArgs, { a: 'b' }); test.done(); }, @@ -52,7 +51,7 @@ export = { const stack = new Stack(); // WHEN - const asset = new DockerImageAsset(stack, 'Image', { + new DockerImageAsset(stack, 'Image', { directory: path.join(__dirname, 'demo-image'), buildArgs: { a: 'b' @@ -61,7 +60,7 @@ export = { }); // THEN - const assetMetadata = asset.node.metadata.find(({ type }) => type === 'aws:cdk:asset'); + const assetMetadata = stack.node.metadata.find(({ type }) => type === 'aws:cdk:asset'); test.deepEqual(assetMetadata && assetMetadata.data.target, 'a-target'); test.done(); }, @@ -99,7 +98,7 @@ export = { ":", { "Ref": "AWS::AccountId" }, ":repository/", - { "Fn::GetAtt": [ "ImageAdoptRepositoryE1E84E35", "RepositoryName" ] } + { "Fn::GetAtt": ["ImageAdoptRepositoryE1E84E35", "RepositoryName"] } ] ] } @@ -139,7 +138,17 @@ export = { // THEN expect(stack).to(haveResource('Custom::ECRAdoptedRepository', { "RepositoryName": { - "Fn::Select": [ 0, { "Fn::Split": [ "@sha256:", { "Ref": "ImageImageName5E684353" } ] } ] + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "@sha256:", + { + "Ref": "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" + } + ] + } + ] }, "PolicyDocument": { "Statement": [ diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.asset-image.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.asset-image.expected.json index ac7622dfc9778..dbbb544f7d2a4 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.asset-image.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.asset-image.expected.json @@ -469,77 +469,11 @@ "", [ { - "Fn::Select": [ - 4, - { - "Fn::Split": [ - ":", - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ecr:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":repository/", - { - "Fn::GetAtt": [ - "FargateServiceTaskDefwebAssetImageAdoptRepositoryCDAFD419", - "RepositoryName" - ] - } - ] - ] - } - ] - } - ] + "Ref": "AWS::AccountId" }, ".dkr.ecr.", { - "Fn::Select": [ - 3, - { - "Fn::Split": [ - ":", - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ecr:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":repository/", - { - "Fn::GetAtt": [ - "FargateServiceTaskDefwebAssetImageAdoptRepositoryCDAFD419", - "RepositoryName" - ] - } - ] - ] - } - ] - } - ] + "Ref": "AWS::Region" }, ".", { @@ -547,9 +481,16 @@ }, "/", { - "Fn::GetAtt": [ - "FargateServiceTaskDefwebAssetImageAdoptRepositoryCDAFD419", - "RepositoryName" + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "@sha256:", + { + "Ref": "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" + } + ] + } ] }, "@sha256:", @@ -560,7 +501,7 @@ "Fn::Split": [ "@sha256:", { - "Ref": "FargateServiceTaskDefwebAssetImageImageNameDC0B98D5" + "Ref": "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" } ] } @@ -627,7 +568,7 @@ "Fn::Split": [ "@sha256:", { - "Ref": "FargateServiceTaskDefwebAssetImageImageNameDC0B98D5" + "Ref": "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" } ] } @@ -745,6 +686,7 @@ "MinimumHealthyPercent": 50 }, "DesiredCount": 1, + "EnableECSManagedTags": false, "HealthCheckGracePeriodSeconds": 60, "LaunchType": "FARGATE", "LoadBalancers": [ @@ -776,8 +718,7 @@ } ] } - }, - "EnableECSManagedTags": false + } }, "DependsOn": [ "FargateServiceLBPublicListenerECSGroupBE57E081", @@ -890,7 +831,7 @@ "Fn::Split": [ "@sha256:", { - "Ref": "FargateServiceTaskDefwebAssetImageImageNameDC0B98D5" + "Ref": "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" } ] } @@ -916,7 +857,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3Bucket92AB06B6" + "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB" }, "S3Key": { "Fn::Join": [ @@ -929,7 +870,7 @@ "Fn::Split": [ "||", { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276" + "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" } ] } @@ -942,7 +883,7 @@ "Fn::Split": [ "||", { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276" + "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" } ] } @@ -1003,21 +944,21 @@ } }, "Parameters": { - "FargateServiceTaskDefwebAssetImageImageNameDC0B98D5": { + "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3": { "Type": "String", - "Description": "ECR repository name and tag asset \"aws-ecs-integ/FargateService/TaskDef/web/AssetImage\"" + "Description": "ECR repository name and tag for asset \"1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439c\"" }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3Bucket92AB06B6": { + "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB": { "Type": "String", - "Description": "S3 bucket for asset \"aws-ecs-integ/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" + "Description": "S3 bucket for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276": { + "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7": { "Type": "String", - "Description": "S3 key for asset version \"aws-ecs-integ/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" + "Description": "S3 key for asset version \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeArtifactHash8BCBAA49": { + "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1ArtifactHash7E5AE478": { "Type": "String", - "Description": "Artifact hash for asset \"aws-ecs-integ/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" + "Description": "Artifact hash for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.https-fargate-service.lit.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.https-fargate-service.lit.expected.json index a5351e28f5f6e..1ed992dbe55c5 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.https-fargate-service.lit.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.https-fargate-service.lit.expected.json @@ -381,7 +381,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "HttpsServiceCertificateCertificateRequestorFunctionCodeS3Bucket7CB97C4F" + "Ref": "AssetParameters0b9723d54b7db3fbfc1a143398b02392e5fe080a68535480782a949b4372d0f0S3Bucket0997A4A0" }, "S3Key": { "Fn::Join": [ @@ -394,7 +394,7 @@ "Fn::Split": [ "||", { - "Ref": "HttpsServiceCertificateCertificateRequestorFunctionCodeS3VersionKey6CF8F6B8" + "Ref": "AssetParameters0b9723d54b7db3fbfc1a143398b02392e5fe080a68535480782a949b4372d0f0S3VersionKey83D9C166" } ] } @@ -407,7 +407,7 @@ "Fn::Split": [ "||", { - "Ref": "HttpsServiceCertificateCertificateRequestorFunctionCodeS3VersionKey6CF8F6B8" + "Ref": "AssetParameters0b9723d54b7db3fbfc1a143398b02392e5fe080a68535480782a949b4372d0f0S3VersionKey83D9C166" } ] } @@ -679,18 +679,9 @@ } }, "Parameters": { - "HttpsServiceCertificateCertificateRequestorFunctionCodeS3Bucket7CB97C4F": { - "Type": "String", - "Description": "S3 bucket for asset \"aws-fargate-https-integ/HttpsService/Certificate/CertificateRequestorFunction/Code\"" - }, - "HttpsServiceCertificateCertificateRequestorFunctionCodeS3VersionKey6CF8F6B8": { - "Type": "String", - "Description": "S3 key for asset version \"aws-fargate-https-integ/HttpsService/Certificate/CertificateRequestorFunction/Code\"" - }, - "HttpsServiceCertificateCertificateRequestorFunctionCodeArtifactHashC69E3CE8": { - "Type": "String", - "Description": "Artifact hash for asset \"aws-fargate-https-integ/HttpsService/Certificate/CertificateRequestorFunction/Code\"" - } + "AssetParameters0b9723d54b7db3fbfc1a143398b02392e5fe080a68535480782a949b4372d0f0S3Bucket0997A4A0": {"Type":"String","Description":"S3 bucket for asset \"0b9723d54b7db3fbfc1a143398b02392e5fe080a68535480782a949b4372d0f0\""}, + "AssetParameters0b9723d54b7db3fbfc1a143398b02392e5fe080a68535480782a949b4372d0f0S3VersionKey83D9C166": {"Type":"String","Description":"S3 key for asset version \"0b9723d54b7db3fbfc1a143398b02392e5fe080a68535480782a949b4372d0f0\""}, + "AssetParameters0b9723d54b7db3fbfc1a143398b02392e5fe080a68535480782a949b4372d0f0ArtifactHashF8F836D1": {"Type":"String","Description":"Artifact hash for asset \"0b9723d54b7db3fbfc1a143398b02392e5fe080a68535480782a949b4372d0f0\""} }, "Outputs": { "HttpsServiceLoadBalancerDNSE2E79469": { diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.scheduled-fargate-task.lit.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.scheduled-fargate-task.lit.expected.json index 73f99b321b515..689ede71b6331 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.scheduled-fargate-task.lit.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/fargate/integ.scheduled-fargate-task.lit.expected.json @@ -277,77 +277,11 @@ "", [ { - "Fn::Select": [ - 4, - { - "Fn::Split": [ - ":", - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ecr:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":repository/", - { - "Fn::GetAtt": [ - "ScheduledFargateTaskScheduledTaskDefScheduledContainerAssetImageAdoptRepository49B45957", - "RepositoryName" - ] - } - ] - ] - } - ] - } - ] + "Ref": "AWS::AccountId" }, ".dkr.ecr.", { - "Fn::Select": [ - 3, - { - "Fn::Split": [ - ":", - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ecr:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":repository/", - { - "Fn::GetAtt": [ - "ScheduledFargateTaskScheduledTaskDefScheduledContainerAssetImageAdoptRepository49B45957", - "RepositoryName" - ] - } - ] - ] - } - ] - } - ] + "Ref": "AWS::Region" }, ".", { @@ -355,9 +289,16 @@ }, "/", { - "Fn::GetAtt": [ - "ScheduledFargateTaskScheduledTaskDefScheduledContainerAssetImageAdoptRepository49B45957", - "RepositoryName" + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "@sha256:", + { + "Ref": "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" + } + ] + } ] }, "@sha256:", @@ -368,7 +309,7 @@ "Fn::Split": [ "@sha256:", { - "Ref": "ScheduledFargateTaskScheduledTaskDefScheduledContainerAssetImageImageName5B6DCF27" + "Ref": "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" } ] } @@ -429,7 +370,7 @@ "Fn::Split": [ "@sha256:", { - "Ref": "ScheduledFargateTaskScheduledTaskDefScheduledContainerAssetImageImageName5B6DCF27" + "Ref": "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" } ] } @@ -688,7 +629,7 @@ "Fn::Split": [ "@sha256:", { - "Ref": "ScheduledFargateTaskScheduledTaskDefScheduledContainerAssetImageImageName5B6DCF27" + "Ref": "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" } ] } @@ -714,7 +655,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3Bucket92AB06B6" + "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB" }, "S3Key": { "Fn::Join": [ @@ -727,7 +668,7 @@ "Fn::Split": [ "||", { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276" + "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" } ] } @@ -740,7 +681,7 @@ "Fn::Split": [ "||", { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276" + "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" } ] } @@ -767,21 +708,21 @@ } }, "Parameters": { - "ScheduledFargateTaskScheduledTaskDefScheduledContainerAssetImageImageName5B6DCF27": { + "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3": { "Type": "String", - "Description": "ECR repository name and tag asset \"aws-fargate-integ/ScheduledFargateTask/ScheduledTaskDef/ScheduledContainer/AssetImage\"" + "Description": "ECR repository name and tag for asset \"1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439c\"" }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3Bucket92AB06B6": { + "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB": { "Type": "String", - "Description": "S3 bucket for asset \"aws-fargate-integ/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" + "Description": "S3 bucket for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276": { + "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7": { "Type": "String", - "Description": "S3 key for asset version \"aws-fargate-integ/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" + "Description": "S3 key for asset version \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeArtifactHash8BCBAA49": { + "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1ArtifactHash7E5AE478": { "Type": "String", - "Description": "Artifact hash for asset \"aws-fargate-integ/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" + "Description": "Artifact hash for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-task-definition.ts b/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-task-definition.ts index 4743c10ed2ad6..0fe9efb5daa74 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-task-definition.ts +++ b/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-task-definition.ts @@ -222,12 +222,12 @@ export = { command: ['CMD env'], dnsSearchDomains: ['0.0.0.0'], dnsServers: ['1.1.1.1'], - dockerLabels: {LABEL: 'label'}, + dockerLabels: { LABEL: 'label' }, dockerSecurityOptions: ['ECS_SELINUX_CAPABLE=true'], entryPoint: ["/app/node_modules/.bin/cdk"], - environment: {TEST_ENVIRONMENT_VARIABLE: "test environment variable value"}, + environment: { TEST_ENVIRONMENT_VARIABLE: "test environment variable value" }, essential: true, - extraHosts: {EXTRAHOST: 'extra host'}, + extraHosts: { EXTRAHOST: 'extra host' }, healthCheck: { command: ["curl localhost:8000"], interval: cdk.Duration.seconds(20), @@ -524,116 +524,59 @@ export = { // THEN expect(stack).to(haveResource("AWS::ECS::TaskDefinition", { Family: "Ec2TaskDef", - ContainerDefinitions: [{ - Essential: true, - Memory: 512, - Image: { - "Fn::Join": [ - "", - [ - { - "Fn::Select": [ - 4, - { - "Fn::Split": [ - ":", - { - "Fn::Join": [ - "", - [ - "arn:", - { - Ref: "AWS::Partition" - }, - ":ecr:", - { - Ref: "AWS::Region" - }, - ":", - { - Ref: "AWS::AccountId" - }, - ":repository/", - { - "Fn::GetAtt": [ - "Ec2TaskDefwebAssetImageAdoptRepositoryEA698962", - "RepositoryName" - ] - } - ] - ] - } - ] - } - ] - }, - ".dkr.ecr.", - { - "Fn::Select": [ - 3, - { - "Fn::Split": [ - ":", - { - "Fn::Join": [ - "", - [ - "arn:", - { - Ref: "AWS::Partition" - }, - ":ecr:", - { - Ref: "AWS::Region" - }, - ":", - { - Ref: "AWS::AccountId" - }, - ":repository/", - { - "Fn::GetAtt": [ - "Ec2TaskDefwebAssetImageAdoptRepositoryEA698962", - "RepositoryName" - ] - } - ] - ] - } - ] - } - ] - }, - ".", - { - Ref: "AWS::URLSuffix" - }, - "/", - { - "Fn::GetAtt": [ - "Ec2TaskDefwebAssetImageAdoptRepositoryEA698962", - "RepositoryName" - ] - }, - "@sha256:", - { - "Fn::Select": [ - 1, - { - "Fn::Split": [ - "@sha256:", - { - Ref: "Ec2TaskDefwebAssetImageImageNameCBACAA57" - } - ] - } - ] - } + ContainerDefinitions: [ + { + Essential: true, + Image: { + "Fn::Join": [ + "", + [ + { + Ref: "AWS::AccountId" + }, + ".dkr.ecr.", + { + Ref: "AWS::Region" + }, + ".", + { + Ref: "AWS::URLSuffix" + }, + "/", + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "@sha256:", + { + Ref: "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" + } + ] + } + ] + }, + "@sha256:", + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "@sha256:", + { + Ref: "AssetParameters1a17a141505ac69144931fe263d130f4612251caa4bbbdaf68a44ed0f405439cImageName1ADCADB3" + } + ] + } + ] + } + ] ] - ] - }, - Name: "web" - }], + }, + Memory: 512, + Name: "web" + } + ], })); test.done(); @@ -647,7 +590,7 @@ export = { taskDefinition.addContainer("web", { image: ecs.ContainerImage.fromAsset(path.join(__dirname, '..', 'demo-image'), { - buildArgs: {HTTP_PROXY: 'http://10.20.30.2:1234'} + buildArgs: { HTTP_PROXY: 'http://10.20.30.2:1234' } }), memoryLimitMiB: 512 }); @@ -717,10 +660,10 @@ export = { container.addContainerDependencies({ container: dependency1 }, - { - container: dependency2, - condition: ecs.ContainerDependencyCondition.SUCCESS - } + { + container: dependency2, + condition: ecs.ContainerDependencyCondition.SUCCESS + } ); // THEN @@ -735,13 +678,13 @@ export = { { Name: "web", DependsOn: [{ - Condition: "HEALTHY", - ContainerName: "dependency1" - }, - { - Condition: "SUCCESS", - ContainerName: "dependency2" - }] + Condition: "HEALTHY", + ContainerName: "dependency1" + }, + { + Condition: "SUCCESS", + ContainerName: "dependency2" + }] }] })); diff --git a/packages/@aws-cdk/aws-eks/test/integ.eks-cluster.defaults.expected.json b/packages/@aws-cdk/aws-eks/test/integ.eks-cluster.defaults.expected.json index 260d944cd3d28..dc8d10a52066d 100644 --- a/packages/@aws-cdk/aws-eks/test/integ.eks-cluster.defaults.expected.json +++ b/packages/@aws-cdk/aws-eks/test/integ.eks-cluster.defaults.expected.json @@ -672,7 +672,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "ClusterResourceHandlerCodeS3Bucket1A051297" + "Ref": "AssetParametersc58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128S3BucketA2C12383" }, "S3Key": { "Fn::Join": [ @@ -685,7 +685,7 @@ "Fn::Split": [ "||", { - "Ref": "ClusterResourceHandlerCodeS3VersionKeyD4764A45" + "Ref": "AssetParametersc58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128S3VersionKeyB5F0BEF8" } ] } @@ -698,7 +698,7 @@ "Fn::Split": [ "||", { - "Ref": "ClusterResourceHandlerCodeS3VersionKeyD4764A45" + "Ref": "AssetParametersc58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128S3VersionKeyB5F0BEF8" } ] } @@ -788,7 +788,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "ClusterKubernetesResourceHandlerCodeS3Bucket8DF6F33F" + "Ref": "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444S3Bucket919126CB" }, "S3Key": { "Fn::Join": [ @@ -801,7 +801,7 @@ "Fn::Split": [ "||", { - "Ref": "ClusterKubernetesResourceHandlerCodeS3VersionKeyE1F5124A" + "Ref": "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444S3VersionKey529BEF54" } ] } @@ -814,7 +814,7 @@ "Fn::Split": [ "||", { - "Ref": "ClusterKubernetesResourceHandlerCodeS3VersionKeyE1F5124A" + "Ref": "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444S3VersionKey529BEF54" } ] } @@ -1186,30 +1186,12 @@ } }, "Parameters": { - "ClusterResourceHandlerCodeS3Bucket1A051297": { - "Type": "String", - "Description": "S3 bucket for asset \"eks-integ-defaults/Cluster/Resource/ResourceHandler/Code\"" - }, - "ClusterResourceHandlerCodeS3VersionKeyD4764A45": { - "Type": "String", - "Description": "S3 key for asset version \"eks-integ-defaults/Cluster/Resource/ResourceHandler/Code\"" - }, - "ClusterResourceHandlerCodeArtifactHashA32F2E77": { - "Type": "String", - "Description": "Artifact hash for asset \"eks-integ-defaults/Cluster/Resource/ResourceHandler/Code\"" - }, - "ClusterKubernetesResourceHandlerCodeS3Bucket8DF6F33F": { - "Type": "String", - "Description": "S3 bucket for asset \"eks-integ-defaults/Cluster/KubernetesResourceHandler/Code\"" - }, - "ClusterKubernetesResourceHandlerCodeS3VersionKeyE1F5124A": { - "Type": "String", - "Description": "S3 key for asset version \"eks-integ-defaults/Cluster/KubernetesResourceHandler/Code\"" - }, - "ClusterKubernetesResourceHandlerCodeArtifactHash27D4393F": { - "Type": "String", - "Description": "Artifact hash for asset \"eks-integ-defaults/Cluster/KubernetesResourceHandler/Code\"" - }, + "AssetParametersc58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128S3BucketA2C12383": {"Type":"String","Description":"S3 bucket for asset \"c58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128\""}, + "AssetParametersc58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128S3VersionKeyB5F0BEF8": {"Type":"String","Description":"S3 key for asset version \"c58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128\""}, + "AssetParametersc58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128ArtifactHash8E5121DE": {"Type":"String","Description":"Artifact hash for asset \"c58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128\""}, + "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444S3Bucket919126CB": {"Type":"String","Description":"S3 bucket for asset \"640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444\""}, + "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444S3VersionKey529BEF54": {"Type":"String","Description":"S3 key for asset version \"640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444\""}, + "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444ArtifactHash606C8127": {"Type":"String","Description":"Artifact hash for asset \"640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444\""}, "SsmParameterValueawsserviceeksoptimizedami114amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { "Type": "AWS::SSM::Parameter::Value", "Default": "/aws/service/eks/optimized-ami/1.14/amazon-linux-2/recommended/image_id" diff --git a/packages/@aws-cdk/aws-eks/test/integ.eks-cluster.lit.expected.json b/packages/@aws-cdk/aws-eks/test/integ.eks-cluster.lit.expected.json index 62c42b4130521..68630e9dce88f 100644 --- a/packages/@aws-cdk/aws-eks/test/integ.eks-cluster.lit.expected.json +++ b/packages/@aws-cdk/aws-eks/test/integ.eks-cluster.lit.expected.json @@ -672,7 +672,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "EKSClusterResourceHandlerCodeS3BucketCE5CFBAC" + "Ref": "AssetParametersc58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128S3BucketA2C12383" }, "S3Key": { "Fn::Join": [ @@ -685,7 +685,7 @@ "Fn::Split": [ "||", { - "Ref": "EKSClusterResourceHandlerCodeS3VersionKey343D88E8" + "Ref": "AssetParametersc58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128S3VersionKeyB5F0BEF8" } ] } @@ -698,7 +698,7 @@ "Fn::Split": [ "||", { - "Ref": "EKSClusterResourceHandlerCodeS3VersionKey343D88E8" + "Ref": "AssetParametersc58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128S3VersionKeyB5F0BEF8" } ] } @@ -788,7 +788,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "EKSClusterKubernetesResourceHandlerCodeS3Bucket5B1A2C93" + "Ref": "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444S3Bucket919126CB" }, "S3Key": { "Fn::Join": [ @@ -801,7 +801,7 @@ "Fn::Split": [ "||", { - "Ref": "EKSClusterKubernetesResourceHandlerCodeS3VersionKeyB2871B51" + "Ref": "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444S3VersionKey529BEF54" } ] } @@ -814,7 +814,7 @@ "Fn::Split": [ "||", { - "Ref": "EKSClusterKubernetesResourceHandlerCodeS3VersionKeyB2871B51" + "Ref": "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444S3VersionKey529BEF54" } ] } @@ -1186,30 +1186,12 @@ } }, "Parameters": { - "EKSClusterResourceHandlerCodeS3BucketCE5CFBAC": { - "Type": "String", - "Description": "S3 bucket for asset \"eks-integ-test-basic/EKSCluster/Resource/ResourceHandler/Code\"" - }, - "EKSClusterResourceHandlerCodeS3VersionKey343D88E8": { - "Type": "String", - "Description": "S3 key for asset version \"eks-integ-test-basic/EKSCluster/Resource/ResourceHandler/Code\"" - }, - "EKSClusterResourceHandlerCodeArtifactHashAECCA27B": { - "Type": "String", - "Description": "Artifact hash for asset \"eks-integ-test-basic/EKSCluster/Resource/ResourceHandler/Code\"" - }, - "EKSClusterKubernetesResourceHandlerCodeS3Bucket5B1A2C93": { - "Type": "String", - "Description": "S3 bucket for asset \"eks-integ-test-basic/EKSCluster/KubernetesResourceHandler/Code\"" - }, - "EKSClusterKubernetesResourceHandlerCodeS3VersionKeyB2871B51": { - "Type": "String", - "Description": "S3 key for asset version \"eks-integ-test-basic/EKSCluster/KubernetesResourceHandler/Code\"" - }, - "EKSClusterKubernetesResourceHandlerCodeArtifactHashA13FF2C6": { - "Type": "String", - "Description": "Artifact hash for asset \"eks-integ-test-basic/EKSCluster/KubernetesResourceHandler/Code\"" - }, + "AssetParametersc58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128S3BucketA2C12383": {"Type":"String","Description":"S3 bucket for asset \"c58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128\""}, + "AssetParametersc58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128S3VersionKeyB5F0BEF8": {"Type":"String","Description":"S3 key for asset version \"c58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128\""}, + "AssetParametersc58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128ArtifactHash8E5121DE": {"Type":"String","Description":"Artifact hash for asset \"c58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128\""}, + "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444S3Bucket919126CB": {"Type":"String","Description":"S3 bucket for asset \"640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444\""}, + "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444S3VersionKey529BEF54": {"Type":"String","Description":"S3 key for asset version \"640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444\""}, + "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444ArtifactHash606C8127": {"Type":"String","Description":"Artifact hash for asset \"640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444\""}, "SsmParameterValueawsserviceeksoptimizedami114amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { "Type": "AWS::SSM::Parameter::Value", "Default": "/aws/service/eks/optimized-ami/1.14/amazon-linux-2/recommended/image_id" diff --git a/packages/@aws-cdk/aws-eks/test/integ.eks-kubectl.lit.expected.json b/packages/@aws-cdk/aws-eks/test/integ.eks-kubectl.lit.expected.json index a4a46043e1b8c..a94f755bc4777 100644 --- a/packages/@aws-cdk/aws-eks/test/integ.eks-kubectl.lit.expected.json +++ b/packages/@aws-cdk/aws-eks/test/integ.eks-kubectl.lit.expected.json @@ -558,7 +558,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "cluster22ResourceHandlerCodeS3Bucket9D2D8D69" + "Ref": "AssetParametersc58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128S3BucketA2C12383" }, "S3Key": { "Fn::Join": [ @@ -571,7 +571,7 @@ "Fn::Split": [ "||", { - "Ref": "cluster22ResourceHandlerCodeS3VersionKeyB44B9CDC" + "Ref": "AssetParametersc58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128S3VersionKeyB5F0BEF8" } ] } @@ -584,7 +584,7 @@ "Fn::Split": [ "||", { - "Ref": "cluster22ResourceHandlerCodeS3VersionKeyB44B9CDC" + "Ref": "AssetParametersc58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128S3VersionKeyB5F0BEF8" } ] } @@ -668,7 +668,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "cluster22KubernetesResourceHandlerCodeS3Bucket73E6A860" + "Ref": "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444S3Bucket919126CB" }, "S3Key": { "Fn::Join": [ @@ -681,7 +681,7 @@ "Fn::Split": [ "||", { - "Ref": "cluster22KubernetesResourceHandlerCodeS3VersionKey7C559451" + "Ref": "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444S3VersionKey529BEF54" } ] } @@ -694,7 +694,7 @@ "Fn::Split": [ "||", { - "Ref": "cluster22KubernetesResourceHandlerCodeS3VersionKey7C559451" + "Ref": "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444S3VersionKey529BEF54" } ] } @@ -1111,36 +1111,6 @@ } } }, - "Parameters": { - "cluster22ResourceHandlerCodeS3Bucket9D2D8D69": { - "Type": "String", - "Description": "S3 bucket for asset \"k8s-cluster/cluster22/Resource/ResourceHandler/Code\"" - }, - "cluster22ResourceHandlerCodeS3VersionKeyB44B9CDC": { - "Type": "String", - "Description": "S3 key for asset version \"k8s-cluster/cluster22/Resource/ResourceHandler/Code\"" - }, - "cluster22ResourceHandlerCodeArtifactHash26F3BC3F": { - "Type": "String", - "Description": "Artifact hash for asset \"k8s-cluster/cluster22/Resource/ResourceHandler/Code\"" - }, - "cluster22KubernetesResourceHandlerCodeS3Bucket73E6A860": { - "Type": "String", - "Description": "S3 bucket for asset \"k8s-cluster/cluster22/KubernetesResourceHandler/Code\"" - }, - "cluster22KubernetesResourceHandlerCodeS3VersionKey7C559451": { - "Type": "String", - "Description": "S3 key for asset version \"k8s-cluster/cluster22/KubernetesResourceHandler/Code\"" - }, - "cluster22KubernetesResourceHandlerCodeArtifactHash8138542A": { - "Type": "String", - "Description": "Artifact hash for asset \"k8s-cluster/cluster22/KubernetesResourceHandler/Code\"" - }, - "SsmParameterValueawsserviceeksoptimizedami114amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { - "Type": "AWS::SSM::Parameter::Value", - "Default": "/aws/service/eks/optimized-ami/1.14/amazon-linux-2/recommended/image_id" - } - }, "Outputs": { "cluster22ConfigCommand96B20279": { "Value": { @@ -1170,6 +1140,36 @@ ] } } + }, + "Parameters": { + "AssetParametersc58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128S3BucketA2C12383": { + "Type": "String", + "Description": "S3 bucket for asset \"c58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128\"" + }, + "AssetParametersc58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128S3VersionKeyB5F0BEF8": { + "Type": "String", + "Description": "S3 key for asset version \"c58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128\"" + }, + "AssetParametersc58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128ArtifactHash8E5121DE": { + "Type": "String", + "Description": "Artifact hash for asset \"c58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128\"" + }, + "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444S3Bucket919126CB": { + "Type": "String", + "Description": "S3 bucket for asset \"640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444\"" + }, + "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444S3VersionKey529BEF54": { + "Type": "String", + "Description": "S3 key for asset version \"640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444\"" + }, + "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444ArtifactHash606C8127": { + "Type": "String", + "Description": "Artifact hash for asset \"640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444\"" + }, + "SsmParameterValueawsserviceeksoptimizedami114amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/aws/service/eks/optimized-ami/1.14/amazon-linux-2/recommended/image_id" + } } } ] \ No newline at end of file diff --git a/packages/@aws-cdk/aws-eks/test/integ.eks-spot.expected.json b/packages/@aws-cdk/aws-eks/test/integ.eks-spot.expected.json index 58261229a2a4b..92adde90ad68d 100644 --- a/packages/@aws-cdk/aws-eks/test/integ.eks-spot.expected.json +++ b/packages/@aws-cdk/aws-eks/test/integ.eks-spot.expected.json @@ -532,7 +532,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "myClusterResourceHandlerCodeS3Bucket2CFC8C11" + "Ref": "AssetParametersc58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128S3BucketA2C12383" }, "S3Key": { "Fn::Join": [ @@ -545,7 +545,7 @@ "Fn::Split": [ "||", { - "Ref": "myClusterResourceHandlerCodeS3VersionKey0ADD9228" + "Ref": "AssetParametersc58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128S3VersionKeyB5F0BEF8" } ] } @@ -558,7 +558,7 @@ "Fn::Split": [ "||", { - "Ref": "myClusterResourceHandlerCodeS3VersionKey0ADD9228" + "Ref": "AssetParametersc58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128S3VersionKeyB5F0BEF8" } ] } @@ -642,7 +642,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "myClusterKubernetesResourceHandlerCodeS3BucketDDA8FDDC" + "Ref": "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444S3Bucket919126CB" }, "S3Key": { "Fn::Join": [ @@ -655,7 +655,7 @@ "Fn::Split": [ "||", { - "Ref": "myClusterKubernetesResourceHandlerCodeS3VersionKeyF5CD1996" + "Ref": "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444S3VersionKey529BEF54" } ] } @@ -668,7 +668,7 @@ "Fn::Split": [ "||", { - "Ref": "myClusterKubernetesResourceHandlerCodeS3VersionKeyF5CD1996" + "Ref": "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444S3VersionKey529BEF54" } ] } @@ -1345,30 +1345,12 @@ } }, "Parameters": { - "myClusterResourceHandlerCodeS3Bucket2CFC8C11": { - "Type": "String", - "Description": "S3 bucket for asset \"integ-eks-spot/myCluster/Resource/ResourceHandler/Code\"" - }, - "myClusterResourceHandlerCodeS3VersionKey0ADD9228": { - "Type": "String", - "Description": "S3 key for asset version \"integ-eks-spot/myCluster/Resource/ResourceHandler/Code\"" - }, - "myClusterResourceHandlerCodeArtifactHashB28336E7": { - "Type": "String", - "Description": "Artifact hash for asset \"integ-eks-spot/myCluster/Resource/ResourceHandler/Code\"" - }, - "myClusterKubernetesResourceHandlerCodeS3BucketDDA8FDDC": { - "Type": "String", - "Description": "S3 bucket for asset \"integ-eks-spot/myCluster/KubernetesResourceHandler/Code\"" - }, - "myClusterKubernetesResourceHandlerCodeS3VersionKeyF5CD1996": { - "Type": "String", - "Description": "S3 key for asset version \"integ-eks-spot/myCluster/KubernetesResourceHandler/Code\"" - }, - "myClusterKubernetesResourceHandlerCodeArtifactHashAEE3F837": { - "Type": "String", - "Description": "Artifact hash for asset \"integ-eks-spot/myCluster/KubernetesResourceHandler/Code\"" - }, + "AssetParametersc58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128S3BucketA2C12383": {"Type":"String","Description":"S3 bucket for asset \"c58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128\""}, + "AssetParametersc58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128S3VersionKeyB5F0BEF8": {"Type":"String","Description":"S3 key for asset version \"c58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128\""}, + "AssetParametersc58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128ArtifactHash8E5121DE": {"Type":"String","Description":"Artifact hash for asset \"c58ce740cd907e33a1af503069821ee4befab4fec4075707d67e86d2a0dbf128\""}, + "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444S3Bucket919126CB": {"Type":"String","Description":"S3 bucket for asset \"640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444\""}, + "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444S3VersionKey529BEF54": {"Type":"String","Description":"S3 key for asset version \"640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444\""}, + "AssetParameters640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444ArtifactHash606C8127": {"Type":"String","Description":"Artifact hash for asset \"640847533c8a00b3133aeb128edcac41fb7b60349c9e18764fcf7ea4af14d444\""}, "SsmParameterValueawsserviceeksoptimizedami114amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { "Type": "AWS::SSM::Parameter::Value", "Default": "/aws/service/eks/optimized-ami/1.14/amazon-linux-2/recommended/image_id" diff --git a/packages/@aws-cdk/aws-events-targets/test/aws-api/integ.aws-api.expected.json b/packages/@aws-cdk/aws-events-targets/test/aws-api/integ.aws-api.expected.json index 3e33f0b36ec7f..9a9169e68a2b3 100644 --- a/packages/@aws-cdk/aws-events-targets/test/aws-api/integ.aws-api.expected.json +++ b/packages/@aws-cdk/aws-events-targets/test/aws-api/integ.aws-api.expected.json @@ -96,7 +96,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AWSb4cf1abd4e4f4bc699441af7ccd9ec37CodeS3BucketC1F8355D" + "Ref": "AssetParameters7f96a9f879348c0e50ac55d67a74edede0cc96cf0615355938c45801708cd063S3Bucket05714AA7" }, "S3Key": { "Fn::Join": [ @@ -109,7 +109,7 @@ "Fn::Split": [ "||", { - "Ref": "AWSb4cf1abd4e4f4bc699441af7ccd9ec37CodeS3VersionKeyBA3A7136" + "Ref": "AssetParameters7f96a9f879348c0e50ac55d67a74edede0cc96cf0615355938c45801708cd063S3VersionKey84F37CC1" } ] } @@ -122,7 +122,7 @@ "Fn::Split": [ "||", { - "Ref": "AWSb4cf1abd4e4f4bc699441af7ccd9ec37CodeS3VersionKeyBA3A7136" + "Ref": "AssetParameters7f96a9f879348c0e50ac55d67a74edede0cc96cf0615355938c45801708cd063S3VersionKey84F37CC1" } ] } @@ -219,17 +219,17 @@ } }, "Parameters": { - "AWSb4cf1abd4e4f4bc699441af7ccd9ec37CodeS3BucketC1F8355D": { + "AssetParameters7f96a9f879348c0e50ac55d67a74edede0cc96cf0615355938c45801708cd063S3Bucket05714AA7": { "Type": "String", - "Description": "S3 bucket for asset \"aws-cdk-aws-api-target-integ/AWSb4cf1abd4e4f4bc699441af7ccd9ec37/Code\"" + "Description": "S3 bucket for asset \"7f96a9f879348c0e50ac55d67a74edede0cc96cf0615355938c45801708cd063\"" }, - "AWSb4cf1abd4e4f4bc699441af7ccd9ec37CodeS3VersionKeyBA3A7136": { + "AssetParameters7f96a9f879348c0e50ac55d67a74edede0cc96cf0615355938c45801708cd063S3VersionKey84F37CC1": { "Type": "String", - "Description": "S3 key for asset version \"aws-cdk-aws-api-target-integ/AWSb4cf1abd4e4f4bc699441af7ccd9ec37/Code\"" + "Description": "S3 key for asset version \"7f96a9f879348c0e50ac55d67a74edede0cc96cf0615355938c45801708cd063\"" }, - "AWSb4cf1abd4e4f4bc699441af7ccd9ec37CodeArtifactHash9D3A0C2E": { + "AssetParameters7f96a9f879348c0e50ac55d67a74edede0cc96cf0615355938c45801708cd063ArtifactHashF670187B": { "Type": "String", - "Description": "Artifact hash for asset \"aws-cdk-aws-api-target-integ/AWSb4cf1abd4e4f4bc699441af7ccd9ec37/Code\"" + "Description": "Artifact hash for asset \"7f96a9f879348c0e50ac55d67a74edede0cc96cf0615355938c45801708cd063\"" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.expected.json b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.expected.json index 9d8e3e050dd17..37040897a8d86 100644 --- a/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.expected.json +++ b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.expected.json @@ -655,77 +655,11 @@ "", [ { - "Fn::Select": [ - 4, - { - "Fn::Split": [ - ":", - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ecr:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":repository/", - { - "Fn::GetAtt": [ - "TaskDefTheContainerAssetImageAdoptRepository997406C3", - "RepositoryName" - ] - } - ] - ] - } - ] - } - ] + "Ref": "AWS::AccountId" }, ".dkr.ecr.", { - "Fn::Select": [ - 3, - { - "Fn::Split": [ - ":", - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ecr:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":repository/", - { - "Fn::GetAtt": [ - "TaskDefTheContainerAssetImageAdoptRepository997406C3", - "RepositoryName" - ] - } - ] - ] - } - ] - } - ] + "Ref": "AWS::Region" }, ".", { @@ -733,9 +667,16 @@ }, "/", { - "Fn::GetAtt": [ - "TaskDefTheContainerAssetImageAdoptRepository997406C3", - "RepositoryName" + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "@sha256:", + { + "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" + } + ] + } ] }, "@sha256:", @@ -746,7 +687,7 @@ "Fn::Split": [ "@sha256:", { - "Ref": "TaskDefTheContainerAssetImageImageName92ECAC22" + "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" } ] } @@ -806,7 +747,7 @@ "Fn::Split": [ "@sha256:", { - "Ref": "TaskDefTheContainerAssetImageImageName92ECAC22" + "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" } ] } @@ -1039,7 +980,7 @@ "Fn::Split": [ "@sha256:", { - "Ref": "TaskDefTheContainerAssetImageImageName92ECAC22" + "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" } ] } @@ -1065,7 +1006,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3Bucket92AB06B6" + "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB" }, "S3Key": { "Fn::Join": [ @@ -1078,7 +1019,7 @@ "Fn::Split": [ "||", { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276" + "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" } ] } @@ -1091,7 +1032,7 @@ "Fn::Split": [ "||", { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276" + "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" } ] } @@ -1153,21 +1094,21 @@ "Type": "AWS::SSM::Parameter::Value", "Default": "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id" }, - "TaskDefTheContainerAssetImageImageName92ECAC22": { + "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A": { "Type": "String", - "Description": "ECR repository name and tag asset \"aws-ecs-integ-ecs/TaskDef/TheContainer/AssetImage\"" + "Description": "ECR repository name and tag for asset \"849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3\"" }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3Bucket92AB06B6": { + "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB": { "Type": "String", - "Description": "S3 bucket for asset \"aws-ecs-integ-ecs/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" + "Description": "S3 bucket for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276": { + "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7": { "Type": "String", - "Description": "S3 key for asset version \"aws-ecs-integ-ecs/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" + "Description": "S3 key for asset version \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeArtifactHash8BCBAA49": { + "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1ArtifactHash7E5AE478": { "Type": "String", - "Description": "Artifact hash for asset \"aws-ecs-integ-ecs/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" + "Description": "Artifact hash for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-fargate-task.expected.json b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-fargate-task.expected.json index 2d37bb5896991..5f3a16e62d4ce 100644 --- a/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-fargate-task.expected.json +++ b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-fargate-task.expected.json @@ -221,77 +221,11 @@ "", [ { - "Fn::Select": [ - 4, - { - "Fn::Split": [ - ":", - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ecr:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":repository/", - { - "Fn::GetAtt": [ - "TaskDefTheContainerAssetImageAdoptRepository997406C3", - "RepositoryName" - ] - } - ] - ] - } - ] - } - ] + "Ref": "AWS::AccountId" }, ".dkr.ecr.", { - "Fn::Select": [ - 3, - { - "Fn::Split": [ - ":", - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ecr:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":repository/", - { - "Fn::GetAtt": [ - "TaskDefTheContainerAssetImageAdoptRepository997406C3", - "RepositoryName" - ] - } - ] - ] - } - ] - } - ] + "Ref": "AWS::Region" }, ".", { @@ -299,9 +233,16 @@ }, "/", { - "Fn::GetAtt": [ - "TaskDefTheContainerAssetImageAdoptRepository997406C3", - "RepositoryName" + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "@sha256:", + { + "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" + } + ] + } ] }, "@sha256:", @@ -312,7 +253,7 @@ "Fn::Split": [ "@sha256:", { - "Ref": "TaskDefTheContainerAssetImageImageName92ECAC22" + "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" } ] } @@ -373,7 +314,7 @@ "Fn::Split": [ "@sha256:", { - "Ref": "TaskDefTheContainerAssetImageImageName92ECAC22" + "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" } ] } @@ -632,7 +573,7 @@ "Fn::Split": [ "@sha256:", { - "Ref": "TaskDefTheContainerAssetImageImageName92ECAC22" + "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" } ] } @@ -658,7 +599,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3Bucket92AB06B6" + "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB" }, "S3Key": { "Fn::Join": [ @@ -671,7 +612,7 @@ "Fn::Split": [ "||", { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276" + "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" } ] } @@ -684,7 +625,7 @@ "Fn::Split": [ "||", { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276" + "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" } ] } @@ -761,21 +702,21 @@ } }, "Parameters": { - "TaskDefTheContainerAssetImageImageName92ECAC22": { + "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A": { "Type": "String", - "Description": "ECR repository name and tag asset \"aws-ecs-integ-fargate/TaskDef/TheContainer/AssetImage\"" + "Description": "ECR repository name and tag for asset \"849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3\"" }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3Bucket92AB06B6": { + "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB": { "Type": "String", - "Description": "S3 bucket for asset \"aws-ecs-integ-fargate/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" + "Description": "S3 bucket for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276": { + "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7": { "Type": "String", - "Description": "S3 key for asset version \"aws-ecs-integ-fargate/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" + "Description": "S3 key for asset version \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeArtifactHash8BCBAA49": { + "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1ArtifactHash7E5AE478": { "Type": "String", - "Description": "Artifact hash for asset \"aws-ecs-integ-fargate/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" + "Description": "Artifact hash for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/lib/code.ts b/packages/@aws-cdk/aws-lambda/lib/code.ts index 7118335462fa9..57f2cf238a88d 100644 --- a/packages/@aws-cdk/aws-lambda/lib/code.ts +++ b/packages/@aws-cdk/aws-lambda/lib/code.ts @@ -40,8 +40,8 @@ export abstract class Code { * Loads the function code from a local disk asset. * @param path Either a directory with the Lambda code bundle or a .zip file */ - public static fromAsset(path: string): AssetCode { - return new AssetCode(path); + public static fromAsset(path: string, options?: s3_assets.AssetOptions): AssetCode { + return new AssetCode(path, options); } /** @@ -170,14 +170,17 @@ export class AssetCode extends Code { /** * @param path The path to the asset file or directory. */ - constructor(public readonly path: string) { + constructor(public readonly path: string, private readonly options: s3_assets.AssetOptions = { }) { super(); } public bind(scope: cdk.Construct): CodeConfig { // If the same AssetCode is used multiple times, retain only the first instantiation. if (!this.asset) { - this.asset = new s3_assets.Asset(scope, 'Code', { path: this.path }); + this.asset = new s3_assets.Asset(scope, 'Code', { + path: this.path, + ...this.options + }); } if (!this.asset.isZipArchive) { diff --git a/packages/@aws-cdk/aws-lambda/test/integ.assets.file.expected.json b/packages/@aws-cdk/aws-lambda/test/integ.assets.file.expected.json index a29705f32d7d7..b0f69e554e57c 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.assets.file.expected.json +++ b/packages/@aws-cdk/aws-lambda/test/integ.assets.file.expected.json @@ -36,7 +36,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "MyLambdaCodeS3BucketC82A5870" + "Ref": "AssetParametersa37d3ef54c18e7738fe5dc008504591bd3b1f14c6a09ee91eac6d55f7ca5ba5fS3Bucket66527C9E" }, "S3Key": { "Fn::Join": [ @@ -49,7 +49,7 @@ "Fn::Split": [ "||", { - "Ref": "MyLambdaCodeS3VersionKey47762537" + "Ref": "AssetParametersa37d3ef54c18e7738fe5dc008504591bd3b1f14c6a09ee91eac6d55f7ca5ba5fS3VersionKey4FEF0FAB" } ] } @@ -62,7 +62,7 @@ "Fn::Split": [ "||", { - "Ref": "MyLambdaCodeS3VersionKey47762537" + "Ref": "AssetParametersa37d3ef54c18e7738fe5dc008504591bd3b1f14c6a09ee91eac6d55f7ca5ba5fS3VersionKey4FEF0FAB" } ] } @@ -87,17 +87,17 @@ } }, "Parameters": { - "MyLambdaCodeS3BucketC82A5870": { + "AssetParametersa37d3ef54c18e7738fe5dc008504591bd3b1f14c6a09ee91eac6d55f7ca5ba5fS3Bucket66527C9E": { "Type": "String", - "Description": "S3 bucket for asset \"lambda-test-assets-file/MyLambda/Code\"" + "Description": "S3 bucket for asset \"a37d3ef54c18e7738fe5dc008504591bd3b1f14c6a09ee91eac6d55f7ca5ba5f\"" }, - "MyLambdaCodeS3VersionKey47762537": { + "AssetParametersa37d3ef54c18e7738fe5dc008504591bd3b1f14c6a09ee91eac6d55f7ca5ba5fS3VersionKey4FEF0FAB": { "Type": "String", - "Description": "S3 key for asset version \"lambda-test-assets-file/MyLambda/Code\"" + "Description": "S3 key for asset version \"a37d3ef54c18e7738fe5dc008504591bd3b1f14c6a09ee91eac6d55f7ca5ba5f\"" }, - "MyLambdaCodeArtifactHashF5E94E30": { + "AssetParametersa37d3ef54c18e7738fe5dc008504591bd3b1f14c6a09ee91eac6d55f7ca5ba5fArtifactHashCF3E1D8E": { "Type": "String", - "Description": "Artifact hash for asset \"lambda-test-assets-file/MyLambda/Code\"" + "Description": "Artifact hash for asset \"a37d3ef54c18e7738fe5dc008504591bd3b1f14c6a09ee91eac6d55f7ca5ba5f\"" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/test/integ.assets.lit.expected.json b/packages/@aws-cdk/aws-lambda/test/integ.assets.lit.expected.json index 471b6c52a3932..f8f14e0c24a9d 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.assets.lit.expected.json +++ b/packages/@aws-cdk/aws-lambda/test/integ.assets.lit.expected.json @@ -36,7 +36,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "MyLambdaCodeS3BucketC82A5870" + "Ref": "AssetParameters9678c34eca93259d11f2d714177347afd66c50116e1e08996eff893d3ca81232S3Bucket1354C645" }, "S3Key": { "Fn::Join": [ @@ -49,7 +49,7 @@ "Fn::Split": [ "||", { - "Ref": "MyLambdaCodeS3VersionKey47762537" + "Ref": "AssetParameters9678c34eca93259d11f2d714177347afd66c50116e1e08996eff893d3ca81232S3VersionKey5D873FAC" } ] } @@ -62,7 +62,7 @@ "Fn::Split": [ "||", { - "Ref": "MyLambdaCodeS3VersionKey47762537" + "Ref": "AssetParameters9678c34eca93259d11f2d714177347afd66c50116e1e08996eff893d3ca81232S3VersionKey5D873FAC" } ] } @@ -87,17 +87,17 @@ } }, "Parameters": { - "MyLambdaCodeS3BucketC82A5870": { + "AssetParameters9678c34eca93259d11f2d714177347afd66c50116e1e08996eff893d3ca81232S3Bucket1354C645": { "Type": "String", - "Description": "S3 bucket for asset \"lambda-test-assets/MyLambda/Code\"" + "Description": "S3 bucket for asset \"9678c34eca93259d11f2d714177347afd66c50116e1e08996eff893d3ca81232\"" }, - "MyLambdaCodeS3VersionKey47762537": { + "AssetParameters9678c34eca93259d11f2d714177347afd66c50116e1e08996eff893d3ca81232S3VersionKey5D873FAC": { "Type": "String", - "Description": "S3 key for asset version \"lambda-test-assets/MyLambda/Code\"" + "Description": "S3 key for asset version \"9678c34eca93259d11f2d714177347afd66c50116e1e08996eff893d3ca81232\"" }, - "MyLambdaCodeArtifactHashF5E94E30": { + "AssetParameters9678c34eca93259d11f2d714177347afd66c50116e1e08996eff893d3ca81232ArtifactHashFA16AACF": { "Type": "String", - "Description": "Artifact hash for asset \"lambda-test-assets/MyLambda/Code\"" + "Description": "Artifact hash for asset \"9678c34eca93259d11f2d714177347afd66c50116e1e08996eff893d3ca81232\"" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.expected.json b/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.expected.json index c660e83fac680..075c44e067f3b 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.expected.json +++ b/packages/@aws-cdk/aws-lambda/test/integ.layer-version.lit.expected.json @@ -1,25 +1,11 @@ { - "Parameters": { - "MyLayerCodeS3Bucket20DB8EB9": { - "Type": "String", - "Description": "S3 bucket for asset \"aws-cdk-layer-version-1/MyLayer/Code\"" - }, - "MyLayerCodeS3VersionKeyA45254EC": { - "Type": "String", - "Description": "S3 key for asset version \"aws-cdk-layer-version-1/MyLayer/Code\"" - }, - "MyLayerCodeArtifactHashCCFB62E9": { - "Type": "String", - "Description": "Artifact hash for asset \"aws-cdk-layer-version-1/MyLayer/Code\"" - } - }, "Resources": { "MyLayer38944FA5": { "Type": "AWS::Lambda::LayerVersion", "Properties": { "Content": { "S3Bucket": { - "Ref": "MyLayerCodeS3Bucket20DB8EB9" + "Ref": "AssetParameters45f085ecc03a1a22cf003fba3fab28e660c92bcfcd4d0c01b62c7cd191070a2dS3Bucket34E3DBD0" }, "S3Key": { "Fn::Join": [ @@ -32,7 +18,7 @@ "Fn::Split": [ "||", { - "Ref": "MyLayerCodeS3VersionKeyA45254EC" + "Ref": "AssetParameters45f085ecc03a1a22cf003fba3fab28e660c92bcfcd4d0c01b62c7cd191070a2dS3VersionKey585C4BED" } ] } @@ -45,7 +31,7 @@ "Fn::Split": [ "||", { - "Ref": "MyLayerCodeS3VersionKeyA45254EC" + "Ref": "AssetParameters45f085ecc03a1a22cf003fba3fab28e660c92bcfcd4d0c01b62c7cd191070a2dS3VersionKey585C4BED" } ] } @@ -129,5 +115,19 @@ "MyLayeredLambdaServiceRole1A7DC118" ] } + }, + "Parameters": { + "AssetParameters45f085ecc03a1a22cf003fba3fab28e660c92bcfcd4d0c01b62c7cd191070a2dS3Bucket34E3DBD0": { + "Type": "String", + "Description": "S3 bucket for asset \"45f085ecc03a1a22cf003fba3fab28e660c92bcfcd4d0c01b62c7cd191070a2d\"" + }, + "AssetParameters45f085ecc03a1a22cf003fba3fab28e660c92bcfcd4d0c01b62c7cd191070a2dS3VersionKey585C4BED": { + "Type": "String", + "Description": "S3 key for asset version \"45f085ecc03a1a22cf003fba3fab28e660c92bcfcd4d0c01b62c7cd191070a2d\"" + }, + "AssetParameters45f085ecc03a1a22cf003fba3fab28e660c92bcfcd4d0c01b62c7cd191070a2dArtifactHash20CDD3D4": { + "Type": "String", + "Description": "Artifact hash for asset \"45f085ecc03a1a22cf003fba3fab28e660c92bcfcd4d0c01b62c7cd191070a2d\"" + } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/test/integ.log-retention.expected.json b/packages/@aws-cdk/aws-lambda/test/integ.log-retention.expected.json index 600c143f3f049..4df4d323253e9 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.log-retention.expected.json +++ b/packages/@aws-cdk/aws-lambda/test/integ.log-retention.expected.json @@ -133,7 +133,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aCodeS3BucketB81211B5" + "Ref": "AssetParametersb23e818e172b6771c5f4fde08381b8d0904b5760fcfcceee68523a83fff99c42S3Bucket773E39E4" }, "S3Key": { "Fn::Join": [ @@ -146,7 +146,7 @@ "Fn::Split": [ "||", { - "Ref": "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aCodeS3VersionKey10C1B354" + "Ref": "AssetParametersb23e818e172b6771c5f4fde08381b8d0904b5760fcfcceee68523a83fff99c42S3VersionKey96D611D4" } ] } @@ -159,7 +159,7 @@ "Fn::Split": [ "||", { - "Ref": "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aCodeS3VersionKey10C1B354" + "Ref": "AssetParametersb23e818e172b6771c5f4fde08381b8d0904b5760fcfcceee68523a83fff99c42S3VersionKey96D611D4" } ] } @@ -331,17 +331,17 @@ } }, "Parameters": { - "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aCodeS3BucketB81211B5": { + "AssetParametersb23e818e172b6771c5f4fde08381b8d0904b5760fcfcceee68523a83fff99c42S3Bucket773E39E4": { "Type": "String", - "Description": "S3 bucket for asset \"aws-cdk-lambda-log-retention/LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a/Code\"" + "Description": "S3 bucket for asset \"b23e818e172b6771c5f4fde08381b8d0904b5760fcfcceee68523a83fff99c42\"" }, - "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aCodeS3VersionKey10C1B354": { + "AssetParametersb23e818e172b6771c5f4fde08381b8d0904b5760fcfcceee68523a83fff99c42S3VersionKey96D611D4": { "Type": "String", - "Description": "S3 key for asset version \"aws-cdk-lambda-log-retention/LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a/Code\"" + "Description": "S3 key for asset version \"b23e818e172b6771c5f4fde08381b8d0904b5760fcfcceee68523a83fff99c42\"" }, - "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aCodeArtifactHash327647CC": { + "AssetParametersb23e818e172b6771c5f4fde08381b8d0904b5760fcfcceee68523a83fff99c42ArtifactHashD6C3B3DD": { "Type": "String", - "Description": "Artifact hash for asset \"aws-cdk-lambda-log-retention/LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a/Code\"" + "Description": "Artifact hash for asset \"b23e818e172b6771c5f4fde08381b8d0904b5760fcfcceee68523a83fff99c42\"" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/test/test.code.ts b/packages/@aws-cdk/aws-lambda/test/test.code.ts index 257ec0016cbd6..a5d18e226c5d0 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.code.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.code.ts @@ -55,12 +55,7 @@ export = { const synthesized = assembly.stacks[0]; // Func1 has an asset, Func2 does not - const metadata = synthesized.manifest.metadata || {}; - test.ok(metadata['/MyStack/Func1/Code']); - test.deepEqual(metadata['/MyStack/Func1/Code'].length, 1); - test.deepEqual(metadata['/MyStack/Func1/Code'][0].type, 'aws:cdk:asset'); - test.deepEqual(metadata['/MyStack/Func2/Code'], undefined); - + test.deepEqual(synthesized.assets.length, 1); test.done(); }, diff --git a/packages/@aws-cdk/aws-lambda/test/test.lambda.ts b/packages/@aws-cdk/aws-lambda/test/test.lambda.ts index f7b66d9f04949..28f58d5dd4d69 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.lambda.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.lambda.ts @@ -20,29 +20,44 @@ export = { runtime: lambda.Runtime.NODEJS_8_10, }); - expect(stack).toMatch({ Resources: - { MyLambdaServiceRole4539ECB6: - { Type: 'AWS::IAM::Role', - Properties: - { AssumeRolePolicyDocument: - { Statement: - [ { Action: 'sts:AssumeRole', - Effect: 'Allow', - Principal: { Service: "lambda.amazonaws.com" } } ], - Version: '2012-10-17' }, - ManagedPolicyArns: - // arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole - // tslint:disable-next-line:max-line-length - [{'Fn::Join': ['', ['arn:', {Ref: 'AWS::Partition'}, ':iam::aws:policy/service-role/AWSLambdaBasicExecutionRole']]}], - }}, + expect(stack).toMatch({ + Resources: + { + MyLambdaServiceRole4539ECB6: + { + Type: 'AWS::IAM::Role', + Properties: + { + AssumeRolePolicyDocument: + { + Statement: + [{ + Action: 'sts:AssumeRole', + Effect: 'Allow', + Principal: { Service: "lambda.amazonaws.com" } + }], + Version: '2012-10-17' + }, + ManagedPolicyArns: + // arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole + // tslint:disable-next-line:max-line-length + [{ 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':iam::aws:policy/service-role/AWSLambdaBasicExecutionRole']] }], + } + }, MyLambdaCCE802FB: - { Type: 'AWS::Lambda::Function', - Properties: - { Code: { ZipFile: 'foo' }, - Handler: 'index.handler', - Role: { 'Fn::GetAtt': [ 'MyLambdaServiceRole4539ECB6', 'Arn' ] }, - Runtime: 'nodejs8.10' }, - DependsOn: [ 'MyLambdaServiceRole4539ECB6' ] } } }); + { + Type: 'AWS::Lambda::Function', + Properties: + { + Code: { ZipFile: 'foo' }, + Handler: 'index.handler', + Role: { 'Fn::GetAtt': ['MyLambdaServiceRole4539ECB6', 'Arn'] }, + Runtime: 'nodejs8.10' + }, + DependsOn: ['MyLambdaServiceRole4539ECB6'] + } + } + }); test.done(); }, @@ -52,51 +67,66 @@ export = { code: new lambda.InlineCode('foo'), handler: 'index.handler', runtime: lambda.Runtime.NODEJS_8_10, - initialPolicy: [new iam.PolicyStatement({ actions: ["*"], resources: ["*"] })] + initialPolicy: [new iam.PolicyStatement({ actions: ["*"], resources: ["*"] })] }); - expect(stack).toMatch({ Resources: - { MyLambdaServiceRole4539ECB6: - { Type: 'AWS::IAM::Role', - Properties: - { AssumeRolePolicyDocument: - { Statement: - [ { Action: 'sts:AssumeRole', - Effect: 'Allow', - Principal: { Service: "lambda.amazonaws.com" } } ], - Version: '2012-10-17' }, - ManagedPolicyArns: - // tslint:disable-next-line:max-line-length - [{'Fn::Join': ['', ['arn:', {Ref: 'AWS::Partition'}, ':iam::aws:policy/service-role/AWSLambdaBasicExecutionRole']]}], - }}, + expect(stack).toMatch({ + Resources: + { + MyLambdaServiceRole4539ECB6: + { + Type: 'AWS::IAM::Role', + Properties: + { + AssumeRolePolicyDocument: + { + Statement: + [{ + Action: 'sts:AssumeRole', + Effect: 'Allow', + Principal: { Service: "lambda.amazonaws.com" } + }], + Version: '2012-10-17' + }, + ManagedPolicyArns: + // tslint:disable-next-line:max-line-length + [{ 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':iam::aws:policy/service-role/AWSLambdaBasicExecutionRole']] }], + } + }, MyLambdaServiceRoleDefaultPolicy5BBC6F68: { Type: "AWS::IAM::Policy", Properties: { PolicyDocument: { - Statement: [ - { - Action: "*", - Effect: "Allow", - Resource: "*" - } - ], - Version: "2012-10-17" + Statement: [ + { + Action: "*", + Effect: "Allow", + Resource: "*" + } + ], + Version: "2012-10-17" }, PolicyName: "MyLambdaServiceRoleDefaultPolicy5BBC6F68", Roles: [ - { - Ref: "MyLambdaServiceRole4539ECB6" - } + { + Ref: "MyLambdaServiceRole4539ECB6" + } ] } }, MyLambdaCCE802FB: - { Type: 'AWS::Lambda::Function', - Properties: - { Code: { ZipFile: 'foo' }, - Handler: 'index.handler', - Role: { 'Fn::GetAtt': [ 'MyLambdaServiceRole4539ECB6', 'Arn' ] }, - Runtime: 'nodejs8.10' }, - DependsOn: [ 'MyLambdaServiceRoleDefaultPolicy5BBC6F68', 'MyLambdaServiceRole4539ECB6' ] } } } ); + { + Type: 'AWS::Lambda::Function', + Properties: + { + Code: { ZipFile: 'foo' }, + Handler: 'index.handler', + Role: { 'Fn::GetAtt': ['MyLambdaServiceRole4539ECB6', 'Arn'] }, + Runtime: 'nodejs8.10' + }, + DependsOn: ['MyLambdaServiceRoleDefaultPolicy5BBC6F68', 'MyLambdaServiceRole4539ECB6'] + } + } + }); test.done(); }, @@ -125,63 +155,63 @@ export = { expect(stack).toMatch({ "Resources": { - "MyLambdaServiceRole4539ECB6": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "lambda.amazonaws.com" - } + "MyLambdaServiceRole4539ECB6": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": + // tslint:disable-next-line:max-line-length + [{ 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':iam::aws:policy/service-role/AWSLambdaBasicExecutionRole']] }], } - ], - "Version": "2012-10-17" }, - "ManagedPolicyArns": - // tslint:disable-next-line:max-line-length - [{'Fn::Join': ['', ['arn:', {Ref: 'AWS::Partition'}, ':iam::aws:policy/service-role/AWSLambdaBasicExecutionRole']]}], - } - }, - "MyLambdaCCE802FB": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "ZipFile": "foo" - }, - "Handler": "bar", - "Role": { - "Fn::GetAtt": [ - "MyLambdaServiceRole4539ECB6", - "Arn" - ] - }, - "Runtime": "python2.7" - }, - "DependsOn": [ - "MyLambdaServiceRole4539ECB6" - ] - }, - "MyLambdaS3Permission99D0EA08": { - "Type": "AWS::Lambda::Permission", - "Properties": { - "Action": "lambda:*", - "FunctionName": { - "Fn::GetAtt": [ - "MyLambdaCCE802FB", - "Arn" + "MyLambdaCCE802FB": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "foo" + }, + "Handler": "bar", + "Role": { + "Fn::GetAtt": [ + "MyLambdaServiceRole4539ECB6", + "Arn" + ] + }, + "Runtime": "python2.7" + }, + "DependsOn": [ + "MyLambdaServiceRole4539ECB6" ] }, - "Principal": "s3.amazonaws.com", - "SourceAccount": { - "Ref": "AWS::AccountId" - }, - "SourceArn": "arn:aws:s3:::my_bucket" + "MyLambdaS3Permission99D0EA08": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:*", + "FunctionName": { + "Fn::GetAtt": [ + "MyLambdaCCE802FB", + "Arn" + ] + }, + "Principal": "s3.amazonaws.com", + "SourceAccount": { + "Ref": "AWS::AccountId" + }, + "SourceArn": "arn:aws:s3:::my_bucket" + } } } - } }); test.done(); @@ -269,18 +299,20 @@ export = { expect(stack).to(haveResource('AWS::Lambda::Function', { "Code": { "S3Bucket": { - "Ref": "MyLambdaCodeS3BucketC82A5870" + "Ref": "AssetParameters9678c34eca93259d11f2d714177347afd66c50116e1e08996eff893d3ca81232S3Bucket1354C645" }, - "S3Key": { "Fn::Join": [ "", [ - {"Fn::Select": [0, {"Fn::Split": ["||", {"Ref": "MyLambdaCodeS3VersionKey47762537"}]}]}, - {"Fn::Select": [1, {"Fn::Split": ["||", {"Ref": "MyLambdaCodeS3VersionKey47762537"}]}]}, - ]]} + "S3Key": { + "Fn::Join": ["", [ + { "Fn::Select": [0, { "Fn::Split": ["||", { "Ref": "AssetParameters9678c34eca93259d11f2d714177347afd66c50116e1e08996eff893d3ca81232S3VersionKey5D873FAC" }] }] }, + { "Fn::Select": [1, { "Fn::Split": ["||", { "Ref": "AssetParameters9678c34eca93259d11f2d714177347afd66c50116e1e08996eff893d3ca81232S3VersionKey5D873FAC" }] }] }, + ]] + } }, "Handler": "index.handler", "Role": { "Fn::GetAtt": [ - "MyLambdaServiceRole4539ECB6", - "Arn" + "MyLambdaServiceRole4539ECB6", + "Arn" ] }, "Runtime": "python3.6" @@ -304,99 +336,99 @@ export = { { "Resources": { "MyLambdaServiceRole4539ECB6": { - "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:", + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ { - "Ref": "AWS::Partition" - }, - ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ] + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } ] } - ] - } }, "MyLambdaServiceRoleDefaultPolicy5BBC6F68": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": "sqs:SendMessage", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "MyLambdaDeadLetterQueue399EEA2D", - "Arn" - ] - } - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "MyLambdaServiceRoleDefaultPolicy5BBC6F68", - "Roles": [ - { - "Ref": "MyLambdaServiceRole4539ECB6" + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sqs:SendMessage", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "MyLambdaDeadLetterQueue399EEA2D", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "MyLambdaServiceRoleDefaultPolicy5BBC6F68", + "Roles": [ + { + "Ref": "MyLambdaServiceRole4539ECB6" + } + ] } - ] - } }, "MyLambdaDeadLetterQueue399EEA2D": { - "Type": "AWS::SQS::Queue", - "Properties": { - "MessageRetentionPeriod": 1209600 - } + "Type": "AWS::SQS::Queue", + "Properties": { + "MessageRetentionPeriod": 1209600 + } }, "MyLambdaCCE802FB": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "ZipFile": "foo" + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "foo" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyLambdaServiceRole4539ECB6", + "Arn" + ] + }, + "Runtime": "nodejs8.10", + "DeadLetterConfig": { + "TargetArn": { + "Fn::GetAtt": [ + "MyLambdaDeadLetterQueue399EEA2D", + "Arn" + ] + } + }, + "FunctionName": "OneFunctionToRuleThemAll" }, - "Handler": "index.handler", - "Role": { - "Fn::GetAtt": [ - "MyLambdaServiceRole4539ECB6", - "Arn" + "DependsOn": [ + "MyLambdaServiceRoleDefaultPolicy5BBC6F68", + "MyLambdaServiceRole4539ECB6" ] - }, - "Runtime": "nodejs8.10", - "DeadLetterConfig": { - "TargetArn": { - "Fn::GetAtt": [ - "MyLambdaDeadLetterQueue399EEA2D", - "Arn" - ] - } - }, - "FunctionName": "OneFunctionToRuleThemAll" - }, - "DependsOn": [ - "MyLambdaServiceRoleDefaultPolicy5BBC6F68", - "MyLambdaServiceRole4539ECB6" - ] } } - } + } ); test.done(); }, @@ -415,98 +447,98 @@ export = { { "Resources": { "MyLambdaServiceRole4539ECB6": { - "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:", + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ { - "Ref": "AWS::Partition" - }, - ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ] + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } ] } - ] - } }, "MyLambdaServiceRoleDefaultPolicy5BBC6F68": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": "sqs:SendMessage", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "MyLambdaDeadLetterQueue399EEA2D", - "Arn" - ] - } - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "MyLambdaServiceRoleDefaultPolicy5BBC6F68", - "Roles": [ - { - "Ref": "MyLambdaServiceRole4539ECB6" + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sqs:SendMessage", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "MyLambdaDeadLetterQueue399EEA2D", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "MyLambdaServiceRoleDefaultPolicy5BBC6F68", + "Roles": [ + { + "Ref": "MyLambdaServiceRole4539ECB6" + } + ] } - ] - } }, "MyLambdaDeadLetterQueue399EEA2D": { - "Type": "AWS::SQS::Queue", - "Properties": { - "MessageRetentionPeriod": 1209600 - } + "Type": "AWS::SQS::Queue", + "Properties": { + "MessageRetentionPeriod": 1209600 + } }, "MyLambdaCCE802FB": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "ZipFile": "foo" + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "foo" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyLambdaServiceRole4539ECB6", + "Arn" + ] + }, + "Runtime": "nodejs8.10", + "DeadLetterConfig": { + "TargetArn": { + "Fn::GetAtt": [ + "MyLambdaDeadLetterQueue399EEA2D", + "Arn" + ] + } + } }, - "Handler": "index.handler", - "Role": { - "Fn::GetAtt": [ - "MyLambdaServiceRole4539ECB6", - "Arn" + "DependsOn": [ + "MyLambdaServiceRoleDefaultPolicy5BBC6F68", + "MyLambdaServiceRole4539ECB6" ] - }, - "Runtime": "nodejs8.10", - "DeadLetterConfig": { - "TargetArn": { - "Fn::GetAtt": [ - "MyLambdaDeadLetterQueue399EEA2D", - "Arn" - ] - } - } - }, - "DependsOn": [ - "MyLambdaServiceRoleDefaultPolicy5BBC6F68", - "MyLambdaServiceRole4539ECB6" - ] } } - } + } ); test.done(); }, @@ -524,56 +556,56 @@ export = { expect(stack).toMatch( { "Resources": { - "MyLambdaServiceRole4539ECB6": { - "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" + "MyLambdaServiceRole4539ECB6": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" }, - ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } ] - ] } - ] - } - }, - "MyLambdaCCE802FB": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "ZipFile": "foo" }, - "Handler": "index.handler", - "Role": { - "Fn::GetAtt": [ - "MyLambdaServiceRole4539ECB6", - "Arn" + "MyLambdaCCE802FB": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "foo" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyLambdaServiceRole4539ECB6", + "Arn" + ] + }, + "Runtime": "nodejs8.10" + }, + "DependsOn": [ + "MyLambdaServiceRole4539ECB6" ] - }, - "Runtime": "nodejs8.10" - }, - "DependsOn": [ - "MyLambdaServiceRole4539ECB6" - ] - } + } } } ); @@ -599,93 +631,93 @@ export = { { "Resources": { "MyLambdaServiceRole4539ECB6": { - "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:", + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ { - "Ref": "AWS::Partition" - }, - ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ] + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } ] } - ] - } }, "MyLambdaServiceRoleDefaultPolicy5BBC6F68": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": "sqs:SendMessage", - "Effect": "Allow", - "Resource": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sqs:SendMessage", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "DeadLetterQueue9F481546", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "MyLambdaServiceRoleDefaultPolicy5BBC6F68", + "Roles": [ + { + "Ref": "MyLambdaServiceRole4539ECB6" + } + ] + } + }, + "MyLambdaCCE802FB": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "foo" + }, + "Handler": "index.handler", + "Role": { "Fn::GetAtt": [ - "DeadLetterQueue9F481546", - "Arn" + "MyLambdaServiceRole4539ECB6", + "Arn" ] + }, + "Runtime": "nodejs8.10", + "DeadLetterConfig": { + "TargetArn": { + "Fn::GetAtt": [ + "DeadLetterQueue9F481546", + "Arn" + ] + } } - } - ], - "Version": "2012-10-17" }, - "PolicyName": "MyLambdaServiceRoleDefaultPolicy5BBC6F68", - "Roles": [ - { - "Ref": "MyLambdaServiceRole4539ECB6" - } + "DependsOn": [ + "MyLambdaServiceRoleDefaultPolicy5BBC6F68", + "MyLambdaServiceRole4539ECB6" ] } - }, - "MyLambdaCCE802FB": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "ZipFile": "foo" - }, - "Handler": "index.handler", - "Role": { - "Fn::GetAtt": [ - "MyLambdaServiceRole4539ECB6", - "Arn" - ] - }, - "Runtime": "nodejs8.10", - "DeadLetterConfig": { - "TargetArn": { - "Fn::GetAtt": [ - "DeadLetterQueue9F481546", - "Arn" - ] - } - } - }, - "DependsOn": [ - "MyLambdaServiceRoleDefaultPolicy5BBC6F68", - "MyLambdaServiceRole4539ECB6" - ] - } - } } - , MatchStyle.SUPERSET); + } + , MatchStyle.SUPERSET); test.done(); }, @@ -708,94 +740,94 @@ export = { expect(stack).toMatch( { "Resources": { - "MyLambdaServiceRole4539ECB6": { - "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" + "MyLambdaServiceRole4539ECB6": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" }, - ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ] - ] - } - ] - } - }, - "MyLambdaServiceRoleDefaultPolicy5BBC6F68": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": "sqs:SendMessage", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "DeadLetterQueue9F481546", - "Arn" + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } ] - } } - ], - "Version": "2012-10-17" }, - "PolicyName": "MyLambdaServiceRoleDefaultPolicy5BBC6F68", - "Roles": [ - { - "Ref": "MyLambdaServiceRole4539ECB6" + "MyLambdaServiceRoleDefaultPolicy5BBC6F68": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sqs:SendMessage", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "DeadLetterQueue9F481546", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "MyLambdaServiceRoleDefaultPolicy5BBC6F68", + "Roles": [ + { + "Ref": "MyLambdaServiceRole4539ECB6" + } + ] } - ] - } - }, - "MyLambdaCCE802FB": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "ZipFile": "foo" }, - "Handler": "index.handler", - "Role": { - "Fn::GetAtt": [ - "MyLambdaServiceRole4539ECB6", - "Arn" - ] - }, - "Runtime": "nodejs8.10", - "DeadLetterConfig": { - "TargetArn": { - "Fn::GetAtt": [ - "DeadLetterQueue9F481546", - "Arn" + "MyLambdaCCE802FB": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "foo" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyLambdaServiceRole4539ECB6", + "Arn" + ] + }, + "Runtime": "nodejs8.10", + "DeadLetterConfig": { + "TargetArn": { + "Fn::GetAtt": [ + "DeadLetterQueue9F481546", + "Arn" + ] + } + } + }, + "DependsOn": [ + "MyLambdaServiceRoleDefaultPolicy5BBC6F68", + "MyLambdaServiceRole4539ECB6", ] - } } - }, - "DependsOn": [ - "MyLambdaServiceRoleDefaultPolicy5BBC6F68", - "MyLambdaServiceRole4539ECB6", - ] - } } } - , MatchStyle.SUPERSET); + , MatchStyle.SUPERSET); test.done(); }, @@ -808,11 +840,11 @@ export = { }); test.throws(() => new lambda.Function(stack, 'MyLambda', { - code: new lambda.InlineCode('foo'), - handler: 'index.handler', - runtime: lambda.Runtime.NODEJS_8_10, - deadLetterQueueEnabled: false, - deadLetterQueue: dlQueue, + code: new lambda.InlineCode('foo'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_8_10, + deadLetterQueueEnabled: false, + deadLetterQueue: dlQueue, }), /deadLetterQueue defined but deadLetterQueueEnabled explicitly set to false/); test.done(); @@ -829,48 +861,48 @@ export = { }); expect(stack).to(haveResource('AWS::IAM::Policy', { - "PolicyDocument": { - "Statement": [ - { - "Action": [ - "xray:PutTraceSegments", - "xray:PutTelemetryRecords" + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "xray:PutTraceSegments", + "xray:PutTelemetryRecords" + ], + "Effect": "Allow", + "Resource": "*" + } ], - "Effect": "Allow", - "Resource": "*" - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "MyLambdaServiceRoleDefaultPolicy5BBC6F68", - "Roles": [ - { - "Ref": "MyLambdaServiceRole4539ECB6" - } - ] + "Version": "2012-10-17" + }, + "PolicyName": "MyLambdaServiceRoleDefaultPolicy5BBC6F68", + "Roles": [ + { + "Ref": "MyLambdaServiceRole4539ECB6" + } + ] })); expect(stack).to(haveResource('AWS::Lambda::Function', { - "Properties": { - "Code": { - "ZipFile": "foo" + "Properties": { + "Code": { + "ZipFile": "foo" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyLambdaServiceRole4539ECB6", + "Arn" + ] + }, + "Runtime": "nodejs8.10", + "TracingConfig": { + "Mode": "Active" + } }, - "Handler": "index.handler", - "Role": { - "Fn::GetAtt": [ + "DependsOn": [ + "MyLambdaServiceRoleDefaultPolicy5BBC6F68", "MyLambdaServiceRole4539ECB6", - "Arn" ] - }, - "Runtime": "nodejs8.10", - "TracingConfig": { - "Mode": "Active" - } - }, - "DependsOn": [ - "MyLambdaServiceRoleDefaultPolicy5BBC6F68", - "MyLambdaServiceRole4539ECB6", - ] }, ResourcePart.CompleteDefinition)); test.done(); @@ -887,48 +919,48 @@ export = { }); expect(stack).to(haveResource('AWS::IAM::Policy', { - "PolicyDocument": { - "Statement": [ - { - "Action": [ - "xray:PutTraceSegments", - "xray:PutTelemetryRecords" + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "xray:PutTraceSegments", + "xray:PutTelemetryRecords" + ], + "Effect": "Allow", + "Resource": "*" + } ], - "Effect": "Allow", - "Resource": "*" - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "MyLambdaServiceRoleDefaultPolicy5BBC6F68", - "Roles": [ - { - "Ref": "MyLambdaServiceRole4539ECB6" - } - ] + "Version": "2012-10-17" + }, + "PolicyName": "MyLambdaServiceRoleDefaultPolicy5BBC6F68", + "Roles": [ + { + "Ref": "MyLambdaServiceRole4539ECB6" + } + ] })); expect(stack).to(haveResource('AWS::Lambda::Function', { - "Properties": { - "Code": { - "ZipFile": "foo" + "Properties": { + "Code": { + "ZipFile": "foo" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyLambdaServiceRole4539ECB6", + "Arn" + ] + }, + "Runtime": "nodejs8.10", + "TracingConfig": { + "Mode": "PassThrough" + } }, - "Handler": "index.handler", - "Role": { - "Fn::GetAtt": [ + "DependsOn": [ + "MyLambdaServiceRoleDefaultPolicy5BBC6F68", "MyLambdaServiceRole4539ECB6", - "Arn" ] - }, - "Runtime": "nodejs8.10", - "TracingConfig": { - "Mode": "PassThrough" - } - }, - "DependsOn": [ - "MyLambdaServiceRoleDefaultPolicy5BBC6F68", - "MyLambdaServiceRole4539ECB6", - ] }, ResourcePart.CompleteDefinition)); test.done(); @@ -945,44 +977,44 @@ export = { }); expect(stack).notTo(haveResource('AWS::IAM::Policy', { - "PolicyDocument": { - "Statement": [ - { - "Action": [ - "xray:PutTraceSegments", - "xray:PutTelemetryRecords" + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "xray:PutTraceSegments", + "xray:PutTelemetryRecords" + ], + "Effect": "Allow", + "Resource": "*" + } ], - "Effect": "Allow", - "Resource": "*" - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "MyLambdaServiceRoleDefaultPolicy5BBC6F68", - "Roles": [ - { - "Ref": "MyLambdaServiceRole4539ECB6" - } - ] + "Version": "2012-10-17" + }, + "PolicyName": "MyLambdaServiceRoleDefaultPolicy5BBC6F68", + "Roles": [ + { + "Ref": "MyLambdaServiceRole4539ECB6" + } + ] })); expect(stack).to(haveResource('AWS::Lambda::Function', { - "Properties": { - "Code": { - "ZipFile": "foo" + "Properties": { + "Code": { + "ZipFile": "foo" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyLambdaServiceRole4539ECB6", + "Arn" + ] + }, + "Runtime": "nodejs8.10" }, - "Handler": "index.handler", - "Role": { - "Fn::GetAtt": [ - "MyLambdaServiceRole4539ECB6", - "Arn" + "DependsOn": [ + "MyLambdaServiceRole4539ECB6" ] - }, - "Runtime": "nodejs8.10" - }, - "DependsOn": [ - "MyLambdaServiceRole4539ECB6" - ] }, ResourcePart.CompleteDefinition)); test.done(); @@ -1011,7 +1043,7 @@ export = { { Action: 'lambda:InvokeFunction', Effect: 'Allow', - Resource: { "Fn::GetAtt": [ "Function76856677", "Arn" ] } + Resource: { "Fn::GetAtt": ["Function76856677", "Arn"] } } ] } @@ -1115,7 +1147,7 @@ export = { // THEN test.deepEqual(stack.resolve(fn.metricErrors()), { - dimensions: { FunctionName: { Ref: 'Function76856677' }}, + dimensions: { FunctionName: { Ref: 'Function76856677' } }, namespace: 'AWS/Lambda', metricName: 'Errors', period: cdk.Duration.minutes(5), @@ -1158,29 +1190,44 @@ export = { runtime: lambda.Runtime.RUBY_2_5, }); - expect(stack).toMatch({ Resources: - { MyLambdaServiceRole4539ECB6: - { Type: 'AWS::IAM::Role', + expect(stack).toMatch({ + Resources: + { + MyLambdaServiceRole4539ECB6: + { + Type: 'AWS::IAM::Role', Properties: - { AssumeRolePolicyDocument: - { Statement: - [ { Action: 'sts:AssumeRole', - Effect: 'Allow', - Principal: { Service: "lambda.amazonaws.com" } } ], - Version: '2012-10-17' }, - ManagedPolicyArns: - // arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole - // tslint:disable-next-line:max-line-length - [{'Fn::Join': ['', ['arn:', {Ref: 'AWS::Partition'}, ':iam::aws:policy/service-role/AWSLambdaBasicExecutionRole']]}], - }}, + { + AssumeRolePolicyDocument: + { + Statement: + [{ + Action: 'sts:AssumeRole', + Effect: 'Allow', + Principal: { Service: "lambda.amazonaws.com" } + }], + Version: '2012-10-17' + }, + ManagedPolicyArns: + // arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole + // tslint:disable-next-line:max-line-length + [{ 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':iam::aws:policy/service-role/AWSLambdaBasicExecutionRole']] }], + } + }, MyLambdaCCE802FB: - { Type: 'AWS::Lambda::Function', + { + Type: 'AWS::Lambda::Function', Properties: - { Code: { ZipFile: 'foo' }, - Handler: 'index.handler', - Role: { 'Fn::GetAtt': [ 'MyLambdaServiceRole4539ECB6', 'Arn' ] }, - Runtime: 'ruby2.5' }, - DependsOn: [ 'MyLambdaServiceRole4539ECB6' ] } } }); + { + Code: { ZipFile: 'foo' }, + Handler: 'index.handler', + Role: { 'Fn::GetAtt': ['MyLambdaServiceRole4539ECB6', 'Arn'] }, + Runtime: 'ruby2.5' + }, + DependsOn: ['MyLambdaServiceRole4539ECB6'] + } + } + }); test.done(); }, @@ -1194,12 +1241,12 @@ export = { // THEN test.throws(() => new lambda.Function(stack, 'Function', { - layers: [layer], - runtime: lambda.Runtime.NODEJS_6_10, - code: lambda.Code.fromInline('exports.main = function() { console.log("DONE"); }'), - handler: 'index.main' - }), - /nodejs6.10 is not in \[nodejs8.10\]/); + layers: [layer], + runtime: lambda.Runtime.NODEJS_6_10, + code: lambda.Code.fromInline('exports.main = function() { console.log("DONE"); }'), + handler: 'index.main' + }), + /nodejs6.10 is not in \[nodejs8.10\]/); test.done(); }, @@ -1214,19 +1261,19 @@ export = { // THEN test.throws(() => new lambda.Function(stack, 'Function', { - layers, - runtime: lambda.Runtime.NODEJS_8_10, - code: lambda.Code.fromInline('exports.main = function() { console.log("DONE"); }'), - handler: 'index.main' - }), - /Unable to add layer:/); + layers, + runtime: lambda.Runtime.NODEJS_8_10, + code: lambda.Code.fromInline('exports.main = function() { console.log("DONE"); }'), + handler: 'index.main' + }), + /Unable to add layer:/); test.done(); }, 'environment variables are prohibited in China'(test: Test) { // GIVEN - const stack = new cdk.Stack(undefined, undefined, { env: { region: 'cn-north-1' }}); + const stack = new cdk.Stack(undefined, undefined, { env: { region: 'cn-north-1' } }); // WHEN test.throws(() => { @@ -1280,30 +1327,45 @@ export = { reservedConcurrentExecutions: 10 }); - expect(stack).toMatch({ Resources: - { MyLambdaServiceRole4539ECB6: - { Type: 'AWS::IAM::Role', + expect(stack).toMatch({ + Resources: + { + MyLambdaServiceRole4539ECB6: + { + Type: 'AWS::IAM::Role', Properties: - { AssumeRolePolicyDocument: - { Statement: - [ { Action: 'sts:AssumeRole', - Effect: 'Allow', - Principal: { Service: "lambda.amazonaws.com" } } ], - Version: '2012-10-17' }, - ManagedPolicyArns: - // arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole - // tslint:disable-next-line:max-line-length - [{'Fn::Join': ['', ['arn:', {Ref: 'AWS::Partition'}, ':iam::aws:policy/service-role/AWSLambdaBasicExecutionRole']]}], - }}, + { + AssumeRolePolicyDocument: + { + Statement: + [{ + Action: 'sts:AssumeRole', + Effect: 'Allow', + Principal: { Service: "lambda.amazonaws.com" } + }], + Version: '2012-10-17' + }, + ManagedPolicyArns: + // arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole + // tslint:disable-next-line:max-line-length + [{ 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':iam::aws:policy/service-role/AWSLambdaBasicExecutionRole']] }], + } + }, MyLambdaCCE802FB: - { Type: 'AWS::Lambda::Function', + { + Type: 'AWS::Lambda::Function', Properties: - { Code: { ZipFile: 'foo' }, - Handler: 'index.handler', - ReservedConcurrentExecutions: 10, - Role: { 'Fn::GetAtt': [ 'MyLambdaServiceRole4539ECB6', 'Arn' ] }, - Runtime: 'nodejs' }, - DependsOn: [ 'MyLambdaServiceRole4539ECB6' ] } } }); + { + Code: { ZipFile: 'foo' }, + Handler: 'index.handler', + ReservedConcurrentExecutions: 10, + Role: { 'Fn::GetAtt': ['MyLambdaServiceRole4539ECB6', 'Arn'] }, + Runtime: 'nodejs' + }, + DependsOn: ['MyLambdaServiceRole4539ECB6'] + } + } + }); test.done(); }, @@ -1373,9 +1435,9 @@ export = { })); test.done(); - }, + }, - 'imported lambda with imported security group and allowAllOutbound set to false'(test: Test) { + 'imported lambda with imported security group and allowAllOutbound set to false'(test: Test) { // GIVEN const stack = new cdk.Stack(); diff --git a/packages/@aws-cdk/aws-rds/test/integ.instance.lit.expected.json b/packages/@aws-cdk/aws-rds/test/integ.instance.lit.expected.json index 3c16e8da49566..c55db883facac 100644 --- a/packages/@aws-cdk/aws-rds/test/integ.instance.lit.expected.json +++ b/packages/@aws-cdk/aws-rds/test/integ.instance.lit.expected.json @@ -947,7 +947,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aCodeS3BucketB81211B5" + "Ref": "AssetParametersb23e818e172b6771c5f4fde08381b8d0904b5760fcfcceee68523a83fff99c42S3Bucket773E39E4" }, "S3Key": { "Fn::Join": [ @@ -960,7 +960,7 @@ "Fn::Split": [ "||", { - "Ref": "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aCodeS3VersionKey10C1B354" + "Ref": "AssetParametersb23e818e172b6771c5f4fde08381b8d0904b5760fcfcceee68523a83fff99c42S3VersionKey96D611D4" } ] } @@ -973,7 +973,7 @@ "Fn::Split": [ "||", { - "Ref": "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aCodeS3VersionKey10C1B354" + "Ref": "AssetParametersb23e818e172b6771c5f4fde08381b8d0904b5760fcfcceee68523a83fff99c42S3VersionKey96D611D4" } ] } @@ -1088,17 +1088,8 @@ } }, "Parameters": { - "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aCodeS3BucketB81211B5": { - "Type": "String", - "Description": "S3 bucket for asset \"aws-cdk-rds-instance/LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a/Code\"" - }, - "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aCodeS3VersionKey10C1B354": { - "Type": "String", - "Description": "S3 key for asset version \"aws-cdk-rds-instance/LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a/Code\"" - }, - "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aCodeArtifactHash327647CC": { - "Type": "String", - "Description":"Artifact hash for asset \"aws-cdk-rds-instance/LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a/Code\"" - } + "AssetParametersb23e818e172b6771c5f4fde08381b8d0904b5760fcfcceee68523a83fff99c42S3Bucket773E39E4": {"Type":"String","Description":"S3 bucket for asset \"b23e818e172b6771c5f4fde08381b8d0904b5760fcfcceee68523a83fff99c42\""}, + "AssetParametersb23e818e172b6771c5f4fde08381b8d0904b5760fcfcceee68523a83fff99c42S3VersionKey96D611D4": {"Type":"String","Description":"S3 key for asset version \"b23e818e172b6771c5f4fde08381b8d0904b5760fcfcceee68523a83fff99c42\""}, + "AssetParametersb23e818e172b6771c5f4fde08381b8d0904b5760fcfcceee68523a83fff99c42ArtifactHashD6C3B3DD": {"Type":"String","Description":"Artifact hash for asset \"b23e818e172b6771c5f4fde08381b8d0904b5760fcfcceee68523a83fff99c42\""} } } diff --git a/packages/@aws-cdk/aws-s3-assets/lib/asset.ts b/packages/@aws-cdk/aws-s3-assets/lib/asset.ts index 14a35bf8cee2f..f7d2d23be0ccd 100644 --- a/packages/@aws-cdk/aws-s3-assets/lib/asset.ts +++ b/packages/@aws-cdk/aws-s3-assets/lib/asset.ts @@ -8,15 +8,7 @@ import path = require('path'); const ARCHIVE_EXTENSIONS = [ '.zip', '.jar' ]; -export interface AssetProps extends assets.CopyOptions { - /** - * The disk location of the asset. - * - * The path should refer to one of the following: - * - A regular file or a .zip file, in which case the file will be uploaded as-is to S3. - * - A directory, in which case it will be archived into a .zip file and uploaded to S3. - */ - readonly path: string; +export interface AssetOptions extends assets.CopyOptions { /** * A list of principals that should be able to read this asset from S3. @@ -25,6 +17,33 @@ export interface AssetProps extends assets.CopyOptions { * @default - No principals that can read file asset. */ readonly readers?: iam.IGrantable[]; + + /** + * Custom source hash to use when identifying the specific version of the asset. + * + * NOTE: the source hash is used in order to identify a specific revision of the asset, + * and used for optimizing and caching deployment activities related to this asset such as + * packaging, uploading to Amazon S3, etc. If you chose to customize the source hash, + * you will need to make sure it is updated every time the source changes, or otherwise + * it is possible that some deployments will not be invalidated. + * + * @default - automatically calculate source hash based on the contents + * of the source file or directory. + * + * @experimental + */ + readonly sourceHash?: string; +} + +export interface AssetProps extends AssetOptions { + /** + * The disk location of the asset. + * + * The path should refer to one of the following: + * - A regular file or a .zip file, in which case the file will be uploaded as-is to S3. + * - A directory, in which case it will be archived into a .zip file and uploaded to S3. + */ + readonly path: string; } /** @@ -69,11 +88,6 @@ export class Asset extends cdk.Construct implements assets.IAsset { public readonly sourceHash: string; - /** - * The S3 prefix where all different versions of this asset are stored - */ - private readonly s3Prefix: string; - constructor(scope: cdk.Construct, id: string, props: AssetProps) { super(scope, id); @@ -82,62 +96,31 @@ export class Asset extends cdk.Construct implements assets.IAsset { ...props, sourcePath: path.resolve(props.path), }); - this.sourceHash = staging.sourceHash; + + this.sourceHash = props.sourceHash || staging.sourceHash; this.assetPath = staging.stagedPath; const packaging = determinePackaging(staging.sourcePath); // sets isZipArchive based on the type of packaging and file extension - this.isZipArchive = packaging === AssetPackaging.ZIP_DIRECTORY + this.isZipArchive = packaging === cdk.FileAssetPackaging.ZIP_DIRECTORY ? true : ARCHIVE_EXTENSIONS.some(ext => staging.sourcePath.toLowerCase().endsWith(ext)); - // add parameters for s3 bucket and s3 key. those will be set by - // the toolkit or by CI/CD when the stack is deployed and will include - // the name of the bucket and the S3 key where the code lives. - - const bucketParam = new cdk.CfnParameter(this, 'S3Bucket', { - type: 'String', - description: `S3 bucket for asset "${this.node.path}"`, - }); - - const keyParam = new cdk.CfnParameter(this, 'S3VersionKey', { - type: 'String', - description: `S3 key for asset version "${this.node.path}"` - }); - - const hashParam = new cdk.CfnParameter(this, 'ArtifactHash', { - description: `Artifact hash for asset "${this.node.path}"`, - type: 'String', - }); - - this.s3BucketName = bucketParam.valueAsString; - this.s3Prefix = cdk.Fn.select(0, cdk.Fn.split(cxapi.ASSET_PREFIX_SEPARATOR, keyParam.valueAsString)).toString(); - const s3Filename = cdk.Fn.select(1, cdk.Fn.split(cxapi.ASSET_PREFIX_SEPARATOR, keyParam.valueAsString)).toString(); - this.s3ObjectKey = `${this.s3Prefix}${s3Filename}`; - - this.bucket = s3.Bucket.fromBucketName(this, 'AssetBucket', this.s3BucketName); - - // form the s3 URL of the object key - this.s3Url = this.bucket.urlForObject(this.s3ObjectKey); + const stack = cdk.Stack.of(this); - // attach metadata to the lambda function which includes information - // for tooling to be able to package and upload a directory to the - // s3 bucket and plug in the bucket name and key in the correct - // parameters. - const asset: cxapi.FileAssetMetadataEntry = { - path: this.assetPath, - id: this.node.uniqueId, + const location = stack.addFileAsset({ packaging, sourceHash: this.sourceHash, + fileName: staging.stagedPath + }); - s3BucketParameter: bucketParam.logicalId, - s3KeyParameter: keyParam.logicalId, - artifactHashParameter: hashParam.logicalId, - }; + this.s3BucketName = location.bucketName; + this.s3ObjectKey = location.objectKey; + this.s3Url = location.s3Url; - this.node.addMetadata(cxapi.ASSET_METADATA, asset); + this.bucket = s3.Bucket.fromBucketName(this, 'AssetBucket', this.s3BucketName); for (const reader of (props.readers || [])) { this.grantRead(reader); @@ -173,45 +156,28 @@ export class Asset extends cdk.Construct implements assets.IAsset { } /** - * Grants read permissions to the principal on the asset's S3 object. + * Grants read permissions to the principal on the assets bucket. */ public grantRead(grantee: iam.IGrantable) { - // We give permissions on all files with the same prefix. Presumably - // different versions of the same file will have the same prefix - // and we don't want to accidentally revoke permission on old versions - // when deploying a new version. - this.bucket.grantRead(grantee, `${this.s3Prefix}*`); + // we give permissions on all files in the bucket since we don't want to + // accidentally revoke permission on old versions when deploying a new + // version (for example, when using Lambda traffic shifting). + this.bucket.grantRead(grantee); } } -function determinePackaging(assetPath: string): AssetPackaging { +function determinePackaging(assetPath: string): cdk.FileAssetPackaging { if (!fs.existsSync(assetPath)) { throw new Error(`Cannot find asset at ${assetPath}`); } if (fs.statSync(assetPath).isDirectory()) { - return AssetPackaging.ZIP_DIRECTORY; + return cdk.FileAssetPackaging.ZIP_DIRECTORY; } if (fs.statSync(assetPath).isFile()) { - return AssetPackaging.FILE; + return cdk.FileAssetPackaging.FILE; } throw new Error(`Asset ${assetPath} is expected to be either a directory or a regular file`); } - -/** - * Defines the way an asset is packaged before it is uploaded to S3. - */ -enum AssetPackaging { - /** - * Path refers to a directory on disk, the contents of the directory is - * archived into a .zip. - */ - ZIP_DIRECTORY = 'zip', - - /** - * Path refers to a single file on disk. The file is uploaded as-is. - */ - FILE = 'file', -} diff --git a/packages/@aws-cdk/aws-s3-assets/test/integ.assets.directory.lit.expected.json b/packages/@aws-cdk/aws-s3-assets/test/integ.assets.directory.lit.expected.json index b399c6fa041c6..867d0a2430be4 100644 --- a/packages/@aws-cdk/aws-s3-assets/test/integ.assets.directory.lit.expected.json +++ b/packages/@aws-cdk/aws-s3-assets/test/integ.assets.directory.lit.expected.json @@ -1,16 +1,16 @@ { "Parameters": { - "SampleAssetS3BucketE6B2908E": { + "AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3Bucket50B5A10B": { "Type": "String", - "Description": "S3 bucket for asset \"aws-cdk-asset-test/SampleAsset\"" + "Description": "S3 bucket for asset \"6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2\"" }, - "SampleAssetS3VersionKey3E106D34": { + "AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3VersionKey1F7D75F9": { "Type": "String", - "Description": "S3 key for asset version \"aws-cdk-asset-test/SampleAsset\"" + "Description": "S3 key for asset version \"6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2\"" }, - "SampleAssetArtifactHashE80944C9": { + "AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2ArtifactHash220DE9BD": { "Type": "String", - "Description": "Artifact hash for asset \"aws-cdk-asset-test/SampleAsset\"" + "Description": "Artifact hash for asset \"6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2\"" } }, "Resources": { @@ -40,7 +40,7 @@ }, ":s3:::", { - "Ref": "SampleAssetS3BucketE6B2908E" + "Ref": "AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3Bucket50B5A10B" } ] ] @@ -55,23 +55,9 @@ }, ":s3:::", { - "Ref": "SampleAssetS3BucketE6B2908E" + "Ref": "AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3Bucket50B5A10B" }, - "/", - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "||", - { - "Ref": "SampleAssetS3VersionKey3E106D34" - } - ] - } - ] - }, - "*" + "/*" ] ] } diff --git a/packages/@aws-cdk/aws-s3-assets/test/integ.assets.file.lit.expected.json b/packages/@aws-cdk/aws-s3-assets/test/integ.assets.file.lit.expected.json index 759ea473c59b4..4548468d9a80a 100644 --- a/packages/@aws-cdk/aws-s3-assets/test/integ.assets.file.lit.expected.json +++ b/packages/@aws-cdk/aws-s3-assets/test/integ.assets.file.lit.expected.json @@ -1,16 +1,16 @@ { "Parameters": { - "SampleAssetS3BucketE6B2908E": { + "AssetParameters78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197S3Bucket2C60F94A": { "Type": "String", - "Description": "S3 bucket for asset \"aws-cdk-asset-file-test/SampleAsset\"" + "Description": "S3 bucket for asset \"78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197\"" }, - "SampleAssetS3VersionKey3E106D34": { + "AssetParameters78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197S3VersionKey9482DC35": { "Type": "String", - "Description": "S3 key for asset version \"aws-cdk-asset-file-test/SampleAsset\"" + "Description": "S3 key for asset version \"78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197\"" }, - "SampleAssetArtifactHashE80944C9": { + "AssetParameters78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197ArtifactHash22BFFA67": { "Type": "String", - "Description": "Artifact hash for asset \"aws-cdk-asset-file-test/SampleAsset\"" + "Description": "Artifact hash for asset \"78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197\"" } }, "Resources": { @@ -40,7 +40,7 @@ }, ":s3:::", { - "Ref": "SampleAssetS3BucketE6B2908E" + "Ref": "AssetParameters78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197S3Bucket2C60F94A" } ] ] @@ -55,23 +55,9 @@ }, ":s3:::", { - "Ref": "SampleAssetS3BucketE6B2908E" + "Ref": "AssetParameters78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197S3Bucket2C60F94A" }, - "/", - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "||", - { - "Ref": "SampleAssetS3VersionKey3E106D34" - } - ] - } - ] - }, - "*" + "/*" ] ] } diff --git a/packages/@aws-cdk/aws-s3-assets/test/integ.assets.permissions.lit.expected.json b/packages/@aws-cdk/aws-s3-assets/test/integ.assets.permissions.lit.expected.json index 0f1462339cfc6..df2a4a5f73e9b 100644 --- a/packages/@aws-cdk/aws-s3-assets/test/integ.assets.permissions.lit.expected.json +++ b/packages/@aws-cdk/aws-s3-assets/test/integ.assets.permissions.lit.expected.json @@ -1,16 +1,16 @@ { "Parameters": { - "MyFileS3BucketACE13C36": { + "AssetParameters78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197S3Bucket2C60F94A": { "Type": "String", - "Description": "S3 bucket for asset \"aws-cdk-asset-refs/MyFile\"" + "Description": "S3 bucket for asset \"78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197\"" }, - "MyFileS3VersionKey568C3C9F": { + "AssetParameters78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197S3VersionKey9482DC35": { "Type": "String", - "Description": "S3 key for asset version \"aws-cdk-asset-refs/MyFile\"" + "Description": "S3 key for asset version \"78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197\"" }, - "MyFileArtifactHashAB5F44E1": { + "AssetParameters78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197ArtifactHash22BFFA67": { "Type": "String", - "Description": "Artifact hash for asset \"aws-cdk-asset-refs/MyFile\"" + "Description": "Artifact hash for asset \"78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197\"" } }, "Resources": { @@ -40,7 +40,7 @@ }, ":s3:::", { - "Ref": "MyFileS3BucketACE13C36" + "Ref": "AssetParameters78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197S3Bucket2C60F94A" } ] ] @@ -55,23 +55,9 @@ }, ":s3:::", { - "Ref": "MyFileS3BucketACE13C36" + "Ref": "AssetParameters78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197S3Bucket2C60F94A" }, - "/", - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "||", - { - "Ref": "MyFileS3VersionKey568C3C9F" - } - ] - } - ] - }, - "*" + "/*" ] ] } diff --git a/packages/@aws-cdk/aws-s3-assets/test/integ.assets.refs.lit.expected.json b/packages/@aws-cdk/aws-s3-assets/test/integ.assets.refs.lit.expected.json index e80d3171ab4c7..6b25a2e5cdc63 100644 --- a/packages/@aws-cdk/aws-s3-assets/test/integ.assets.refs.lit.expected.json +++ b/packages/@aws-cdk/aws-s3-assets/test/integ.assets.refs.lit.expected.json @@ -1,22 +1,22 @@ { "Parameters": { - "SampleAssetS3BucketE6B2908E": { + "AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3Bucket50B5A10B": { "Type": "String", - "Description": "S3 bucket for asset \"aws-cdk-asset-refs/SampleAsset\"" + "Description": "S3 bucket for asset \"6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2\"" }, - "SampleAssetS3VersionKey3E106D34": { + "AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3VersionKey1F7D75F9": { "Type": "String", - "Description": "S3 key for asset version \"aws-cdk-asset-refs/SampleAsset\"" + "Description": "S3 key for asset version \"6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2\"" }, - "SampleAssetArtifactHashE80944C9": { + "AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2ArtifactHash220DE9BD": { "Type": "String", - "Description": "Artifact hash for asset \"aws-cdk-asset-refs/SampleAsset\"" + "Description": "Artifact hash for asset \"6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2\"" } }, "Outputs": { "S3BucketName": { "Value": { - "Ref": "SampleAssetS3BucketE6B2908E" + "Ref": "AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3Bucket50B5A10B" } }, "S3ObjectKey": { @@ -31,7 +31,7 @@ "Fn::Split": [ "||", { - "Ref": "SampleAssetS3VersionKey3E106D34" + "Ref": "AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3VersionKey1F7D75F9" } ] } @@ -44,7 +44,7 @@ "Fn::Split": [ "||", { - "Ref": "SampleAssetS3VersionKey3E106D34" + "Ref": "AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3VersionKey1F7D75F9" } ] } @@ -69,7 +69,7 @@ }, "/", { - "Ref": "SampleAssetS3BucketE6B2908E" + "Ref": "AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3Bucket50B5A10B" }, "/", { @@ -79,7 +79,7 @@ "Fn::Split": [ "||", { - "Ref": "SampleAssetS3VersionKey3E106D34" + "Ref": "AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3VersionKey1F7D75F9" } ] } @@ -92,7 +92,7 @@ "Fn::Split": [ "||", { - "Ref": "SampleAssetS3VersionKey3E106D34" + "Ref": "AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3VersionKey1F7D75F9" } ] } @@ -130,7 +130,7 @@ }, ":s3:::", { - "Ref": "SampleAssetS3BucketE6B2908E" + "Ref": "AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3Bucket50B5A10B" } ] ] @@ -145,23 +145,9 @@ }, ":s3:::", { - "Ref": "SampleAssetS3BucketE6B2908E" + "Ref": "AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3Bucket50B5A10B" }, - "/", - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "||", - { - "Ref": "SampleAssetS3VersionKey3E106D34" - } - ] - } - ] - }, - "*" + "/*" ] ] } diff --git a/packages/@aws-cdk/aws-s3-assets/test/integ.multi-assets.expected.json b/packages/@aws-cdk/aws-s3-assets/test/integ.multi-assets.expected.json index e4cdd9326ecc4..87143fe5af7ee 100644 --- a/packages/@aws-cdk/aws-s3-assets/test/integ.multi-assets.expected.json +++ b/packages/@aws-cdk/aws-s3-assets/test/integ.multi-assets.expected.json @@ -5,29 +5,17 @@ } }, "Parameters": { - "SampleAsset1S3Bucket469E18FF": { + "AssetParameters78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197S3Bucket2C60F94A": { "Type": "String", - "Description": "S3 bucket for asset \"aws-cdk-multi-assets/SampleAsset1\"" + "Description": "S3 bucket for asset \"78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197\"" }, - "SampleAsset1S3VersionKey63A628F0": { + "AssetParameters78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197S3VersionKey9482DC35": { "Type": "String", - "Description": "S3 key for asset version \"aws-cdk-multi-assets/SampleAsset1\"" + "Description": "S3 key for asset version \"78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197\"" }, - "SampleAsset1ArtifactHash9E24B5F0": { + "AssetParameters78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197ArtifactHash22BFFA67": { "Type": "String", - "Description": "Artifact hash for asset \"aws-cdk-multi-assets/SampleAsset1\"" - }, - "SampleAsset2S3BucketC94C651A": { - "Type": "String", - "Description": "S3 bucket for asset \"aws-cdk-multi-assets/SampleAsset2\"" - }, - "SampleAsset2S3VersionKey3A7E2CC4": { - "Type": "String", - "Description": "S3 key for asset version \"aws-cdk-multi-assets/SampleAsset2\"" - }, - "SampleAsset2ArtifactHash62F55C83": { - "Type": "String", - "Description": "Artifact hash for asset \"aws-cdk-multi-assets/SampleAsset2\"" + "Description": "Artifact hash for asset \"78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197\"" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3-assets/test/test.asset.ts b/packages/@aws-cdk/aws-s3-assets/test/test.asset.ts index e3039560f0e44..9d5832ee54ee6 100644 --- a/packages/@aws-cdk/aws-s3-assets/test/test.asset.ts +++ b/packages/@aws-cdk/aws-s3-assets/test/test.asset.ts @@ -9,6 +9,8 @@ import os = require('os'); import path = require('path'); import { Asset } from '../lib/asset'; +// tslint:disable:max-line-length + const SAMPLE_ASSET_DIR = path.join(__dirname, 'sample-asset-directory'); export = { @@ -19,13 +21,13 @@ export = { } }); const stack = new cdk.Stack(app, 'MyStack'); - const asset = new Asset(stack, 'MyAsset', { + new Asset(stack, 'MyAsset', { path: SAMPLE_ASSET_DIR }); // verify that metadata contains an "aws:cdk:asset" entry with // the correct information - const entry = asset.node.metadata.find(m => m.type === 'aws:cdk:asset'); + const entry = stack.node.metadata.find(m => m.type === 'aws:cdk:asset'); test.ok(entry, 'found metadata entry'); // verify that now the template contains parameters for this asset @@ -33,17 +35,18 @@ export = { test.deepEqual(stack.resolve(entry!.data), { path: SAMPLE_ASSET_DIR, - id: 'MyStackMyAssetBDDF29E3', + id: '6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2', packaging: 'zip', sourceHash: '6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2', - s3BucketParameter: 'MyAssetS3Bucket68C9B344', - s3KeyParameter: 'MyAssetS3VersionKey68E1A45D', - artifactHashParameter: 'MyAssetArtifactHashF518BDDE', + s3BucketParameter: 'AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3Bucket50B5A10B', + s3KeyParameter: 'AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3VersionKey1F7D75F9', + artifactHashParameter: 'AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2ArtifactHash220DE9BD', }); const template = JSON.parse(fs.readFileSync(path.join(session.directory, 'MyStack.template.json'), 'utf-8')); - test.equal(template.Parameters.MyAssetS3Bucket68C9B344.Type, 'String'); - test.equal(template.Parameters.MyAssetS3VersionKey68E1A45D.Type, 'String'); + + test.equal(template.Parameters.AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3Bucket50B5A10B.Type, 'String'); + test.equal(template.Parameters.AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3VersionKey1F7D75F9.Type, 'String'); test.done(); }, @@ -59,16 +62,16 @@ export = { const synth = app.synth().getStack(stack.stackName); const meta = synth.manifest.metadata || {}; - test.ok(meta['/my-stack/MyAsset']); - test.ok(meta['/my-stack/MyAsset'][0]); - test.deepEqual(meta['/my-stack/MyAsset'][0].data, { + test.ok(meta['/my-stack']); + test.ok(meta['/my-stack'][0]); + test.deepEqual(meta['/my-stack'][0].data, { path: 'asset.6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2', - id: "mystackMyAssetD6B1B593", + id: "6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2", packaging: "zip", sourceHash: '6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2', - s3BucketParameter: "MyAssetS3Bucket68C9B344", - s3KeyParameter: "MyAssetS3VersionKey68E1A45D", - artifactHashParameter: 'MyAssetArtifactHashF518BDDE', + s3BucketParameter: "AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3Bucket50B5A10B", + s3KeyParameter: "AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3VersionKey1F7D75F9", + artifactHashParameter: 'AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2ArtifactHash220DE9BD', }); test.done(); @@ -77,8 +80,8 @@ export = { '"file" assets'(test: Test) { const stack = new cdk.Stack(); const filePath = path.join(__dirname, 'file-asset.txt'); - const asset = new Asset(stack, 'MyAsset', { path: filePath }); - const entry = asset.node.metadata.find(m => m.type === 'aws:cdk:asset'); + new Asset(stack, 'MyAsset', { path: filePath }); + const entry = stack.node.metadata.find(m => m.type === 'aws:cdk:asset'); test.ok(entry, 'found metadata entry'); // synthesize first so "prepare" is called @@ -87,16 +90,16 @@ export = { test.deepEqual(stack.resolve(entry!.data), { path: 'asset.78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197.txt', packaging: 'file', - id: 'MyAsset', + id: '78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197', sourceHash: '78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197', - s3BucketParameter: 'MyAssetS3Bucket68C9B344', - s3KeyParameter: 'MyAssetS3VersionKey68E1A45D', - artifactHashParameter: 'MyAssetArtifactHashF518BDDE', + s3BucketParameter: 'AssetParameters78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197S3Bucket2C60F94A', + s3KeyParameter: 'AssetParameters78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197S3VersionKey9482DC35', + artifactHashParameter: 'AssetParameters78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197ArtifactHash22BFFA67', }); // verify that now the template contains parameters for this asset - test.equal(template.Parameters.MyAssetS3Bucket68C9B344.Type, 'String'); - test.equal(template.Parameters.MyAssetS3VersionKey68E1A45D.Type, 'String'); + test.equal(template.Parameters.AssetParameters78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197S3Bucket2C60F94A.Type, 'String'); + test.equal(template.Parameters.AssetParameters78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197S3VersionKey9482DC35.Type, 'String'); test.done(); }, @@ -121,15 +124,8 @@ export = { Action: ["s3:GetObject*", "s3:GetBucket*", "s3:List*"], Effect: 'Allow', Resource: [ - { "Fn::Join": ["", ["arn:", {Ref: "AWS::Partition"}, ":s3:::", {Ref: "MyAssetS3Bucket68C9B344"}]] }, - { "Fn::Join": ["", - [ - "arn:", {Ref: "AWS::Partition"}, ":s3:::", {Ref: "MyAssetS3Bucket68C9B344"}, - "/", - { "Fn::Select": [0, { "Fn::Split": [ "||", { Ref: "MyAssetS3VersionKey68E1A45D" }] }] }, - "*" - ] - ] } + { "Fn::Join": ["", ["arn:", {Ref: "AWS::Partition"}, ":s3:::", {Ref: "AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3Bucket50B5A10B"} ] ] }, + { "Fn::Join": ["", [ "arn:", {Ref: "AWS::Partition"}, ":s3:::", {Ref: "AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3Bucket50B5A10B"}, "/*" ] ] } ] } ] 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 844677e2e1202..3b02de39cb930 100644 --- a/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts +++ b/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts @@ -5,10 +5,13 @@ import lambda = require('@aws-cdk/aws-lambda'); import s3 = require('@aws-cdk/aws-s3'); import cdk = require('@aws-cdk/core'); import { Token } from '@aws-cdk/core'; +import crypto = require('crypto'); +import fs = require('fs'); import path = require('path'); import { ISource, SourceConfig } from './source'; const handlerCodeBundle = path.join(__dirname, '..', 'lambda', 'bundle.zip'); +const handlerSourceDirectory = path.join(__dirname, '..', 'lambda', 'src'); export interface BucketDeploymentProps { /** @@ -77,9 +80,13 @@ export class BucketDeployment extends cdk.Construct { throw new Error("Distribution must be specified if distribution paths are specified"); } + const sourceHash = calcSourceHash(handlerSourceDirectory); + // tslint:disable-next-line: no-console + console.error({sourceHash}); + const handler = new lambda.SingletonFunction(this, 'CustomResourceHandler', { uuid: this.renderSingletonUuid(props.memoryLimit), - code: lambda.Code.fromAsset(handlerCodeBundle), + code: lambda.Code.fromAsset(handlerCodeBundle, { sourceHash }), runtime: lambda.Runtime.PYTHON_3_6, handler: 'index.handler', lambdaPurpose: 'Custom::CDKBucketDeployment', @@ -131,3 +138,22 @@ export class BucketDeployment extends cdk.Construct { return uuid; } } + +/** + * We need a custom source hash calculation since the bundle.zip file + * contains python dependencies installed during build and results in a + * non-deterministic behavior. + * + * So we just take the `src/` directory of our custom resoruce code. + */ +function calcSourceHash(srcDir: string): string { + const sha = crypto.createHash('sha256'); + for (const file of fs.readdirSync(srcDir)) { + const data = fs.readFileSync(path.join(srcDir, file)); + sha.update(``); + sha.update(data); + sha.update(''); + } + + return sha.digest('hex'); +} \ 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 52f2376f08a13..5b4b57dbcf703 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 @@ -1,5 +1,119 @@ { "Resources": { + "Destination3E3DC043D": { + "Type": "AWS::S3::Bucket", + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "DistributionCFDistribution882A7313": { + "Type": "AWS::CloudFront::Distribution", + "Properties": { + "DistributionConfig": { + "DefaultCacheBehavior": { + "AllowedMethods": [ + "GET", + "HEAD" + ], + "CachedMethods": [ + "GET", + "HEAD" + ], + "Compress": true, + "ForwardedValues": { + "Cookies": { + "Forward": "none" + }, + "QueryString": false + }, + "TargetOriginId": "origin1", + "ViewerProtocolPolicy": "redirect-to-https" + }, + "DefaultRootObject": "index.html", + "Enabled": true, + "HttpVersion": "http2", + "IPV6Enabled": true, + "Origins": [ + { + "DomainName": { + "Fn::GetAtt": [ + "Destination3E3DC043D", + "RegionalDomainName" + ] + }, + "Id": "origin1", + "S3OriginConfig": {} + } + ], + "PriceClass": "PriceClass_100", + "ViewerCertificate": { + "CloudFrontDefaultCertificate": true + } + } + } + }, + "DeployWithInvalidationCustomResourceE3FF7455": { + "Type": "Custom::CDKBucketDeployment", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C81C01536", + "Arn" + ] + }, + "SourceBucketNames": [ + { + "Ref": "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eS3Bucket9CD8B20A" + } + ], + "SourceObjectKeys": [ + { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eS3VersionKeyA58D380C" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eS3VersionKeyA58D380C" + } + ] + } + ] + } + ] + ] + } + ], + "DestinationBucketName": { + "Ref": "Destination3E3DC043D" + }, + "RetainOnDelete": false, + "DistributionId": { + "Ref": "DistributionCFDistribution882A7313" + }, + "DistributionPaths": [ + "/images/*.png" + ] + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265": { "Type": "AWS::IAM::Role", "Properties": { @@ -54,7 +168,7 @@ }, ":s3:::", { - "Ref": "DeployWithInvalidationAsset1S3BucketB5658DC9" + "Ref": "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eS3Bucket9CD8B20A" } ] ] @@ -69,7 +183,7 @@ }, ":s3:::", { - "Ref": "DeployWithInvalidationAsset1S3BucketB5658DC9" + "Ref": "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eS3Bucket9CD8B20A" }, "/*" ] @@ -134,7 +248,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CCodeS3Bucket6E5FB2B7" + "Ref": "AssetParameters6d6f96613f83f126b1dd5b0eb73ef86ad4d3e4487a4cd406c15e5071520c65bfS3Bucket123CF081" }, "S3Key": { "Fn::Join": [ @@ -147,7 +261,7 @@ "Fn::Split": [ "||", { - "Ref": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CCodeS3VersionKey426156C0" + "Ref": "AssetParameters6d6f96613f83f126b1dd5b0eb73ef86ad4d3e4487a4cd406c15e5071520c65bfS3VersionKey7C83FB30" } ] } @@ -160,7 +274,7 @@ "Fn::Split": [ "||", { - "Ref": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CCodeS3VersionKey426156C0" + "Ref": "AssetParameters6d6f96613f83f126b1dd5b0eb73ef86ad4d3e4487a4cd406c15e5071520c65bfS3VersionKey7C83FB30" } ] } @@ -184,142 +298,32 @@ "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRoleDefaultPolicy88902FDF", "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265" ] - }, - "Destination3E3DC043D": { - "Type": "AWS::S3::Bucket", - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, - "DistributionCFDistribution882A7313": { - "Type": "AWS::CloudFront::Distribution", - "Properties": { - "DistributionConfig": { - "DefaultCacheBehavior": { - "AllowedMethods": [ - "GET", - "HEAD" - ], - "CachedMethods": [ - "GET", - "HEAD" - ], - "ForwardedValues": { - "Cookies": { - "Forward": "none" - }, - "QueryString": false - }, - "TargetOriginId": "origin1", - "ViewerProtocolPolicy": "redirect-to-https", - "Compress": true - }, - "DefaultRootObject": "index.html", - "Enabled": true, - "HttpVersion": "http2", - "IPV6Enabled": true, - "Origins": [ - { - "DomainName": { - "Fn::GetAtt": [ - "Destination3E3DC043D", - "RegionalDomainName" - ] - }, - "Id": "origin1", - "S3OriginConfig": {} - } - ], - "PriceClass": "PriceClass_100", - "ViewerCertificate": { - "CloudFrontDefaultCertificate": true - } - } - } - }, - "DeployWithInvalidationCustomResourceE3FF7455": { - "Type": "Custom::CDKBucketDeployment", - "Properties": { - "ServiceToken": { - "Fn::GetAtt": [ - "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C81C01536", - "Arn" - ] - }, - "SourceBucketNames": [ - {"Ref": "DeployWithInvalidationAsset1S3BucketB5658DC9"} - ], - "SourceObjectKeys": [{ - "Fn::Join": [ - "", - [ - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "||", - { - "Ref": "DeployWithInvalidationAsset1S3VersionKeyF91F6A51" - } - ] - } - ] - }, - { - "Fn::Select": [ - 1, - { - "Fn::Split": [ - "||", - { - "Ref": "DeployWithInvalidationAsset1S3VersionKeyF91F6A51" - } - ] - } - ] - } - ] - ] - }], - "DestinationBucketName": { - "Ref": "Destination3E3DC043D" - }, - "RetainOnDelete": false, - "DistributionId": { - "Ref": "DistributionCFDistribution882A7313" - }, - "DistributionPaths": [ - "/images/*.png" - ] - }, - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" } }, "Parameters": { - "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CCodeS3Bucket6E5FB2B7": { + "AssetParameters6d6f96613f83f126b1dd5b0eb73ef86ad4d3e4487a4cd406c15e5071520c65bfS3Bucket123CF081": { "Type": "String", - "Description": "S3 bucket for asset \"test-bucket-deployments-1/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/Code\"" + "Description": "S3 bucket for asset \"6d6f96613f83f126b1dd5b0eb73ef86ad4d3e4487a4cd406c15e5071520c65bf\"" }, - "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CCodeS3VersionKey426156C0": { + "AssetParameters6d6f96613f83f126b1dd5b0eb73ef86ad4d3e4487a4cd406c15e5071520c65bfS3VersionKey7C83FB30": { "Type": "String", - "Description": "S3 key for asset version \"test-bucket-deployments-1/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/Code\"" + "Description": "S3 key for asset version \"6d6f96613f83f126b1dd5b0eb73ef86ad4d3e4487a4cd406c15e5071520c65bf\"" }, - "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CCodeArtifactHashEF37AD24": { + "AssetParameters6d6f96613f83f126b1dd5b0eb73ef86ad4d3e4487a4cd406c15e5071520c65bfArtifactHashAB0385B6": { "Type": "String", - "Description": "Artifact hash for asset \"test-bucket-deployments-1/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/Code\"" + "Description": "Artifact hash for asset \"6d6f96613f83f126b1dd5b0eb73ef86ad4d3e4487a4cd406c15e5071520c65bf\"" }, - "DeployWithInvalidationAsset1S3BucketB5658DC9": { + "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eS3Bucket9CD8B20A": { "Type": "String", - "Description": "S3 bucket for asset \"test-bucket-deployments-1/DeployWithInvalidation/Asset1\"" + "Description": "S3 bucket for asset \"fc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222e\"" }, - "DeployWithInvalidationAsset1S3VersionKeyF91F6A51": { + "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eS3VersionKeyA58D380C": { "Type": "String", - "Description": "S3 key for asset version \"test-bucket-deployments-1/DeployWithInvalidation/Asset1\"" + "Description": "S3 key for asset version \"fc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222e\"" }, - "DeployWithInvalidationAsset1ArtifactHash1EB4DA88": { + "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eArtifactHash0E426313": { "Type": "String", - "Description": "Artifact hash for asset \"test-bucket-deployments-1/DeployWithInvalidation/Asset1\"" + "Description": "Artifact hash for asset \"fc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222e\"" } } -} +} \ 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 9f79adad5b639..23b4bc77ebbe5 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 @@ -7,8 +7,8 @@ "IndexDocument": "index.html" } }, - "DeletionPolicy": "Delete", - "UpdateReplacePolicy": "Delete" + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" }, "DestinationPolicy7982387E": { "Type": "AWS::S3::BucketPolicy", @@ -51,49 +51,53 @@ "Arn" ] }, - "SourceBucketNames": [{ - "Ref": "DeployMeAsset1S3Bucket8ED384CC" - }], - "SourceObjectKeys": [{ - "Fn::Join": [ - "", - [ - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "||", - { - "Ref": "DeployMeAsset1S3VersionKeyA278D1EB" - } - ] - } - ] - }, - { - "Fn::Select": [ - 1, - { - "Fn::Split": [ - "||", - { - "Ref": "DeployMeAsset1S3VersionKeyA278D1EB" - } - ] - } - ] - } + "SourceBucketNames": [ + { + "Ref": "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eS3Bucket9CD8B20A" + } + ], + "SourceObjectKeys": [ + { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eS3VersionKeyA58D380C" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eS3VersionKeyA58D380C" + } + ] + } + ] + } + ] ] - ] - }], + } + ], "DestinationBucketName": { "Ref": "Destination920A3C57" }, "RetainOnDelete": false }, - "DeletionPolicy": "Delete", - "UpdateReplacePolicy": "Delete" + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" }, "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265": { "Type": "AWS::IAM::Role", @@ -105,7 +109,7 @@ "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" - } + } } ], "Version": "2012-10-17" @@ -149,7 +153,7 @@ }, ":s3:::", { - "Ref": "DeployMeAsset1S3Bucket8ED384CC" + "Ref": "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eS3Bucket9CD8B20A" } ] ] @@ -164,7 +168,7 @@ }, ":s3:::", { - "Ref": "DeployMeAsset1S3Bucket8ED384CC" + "Ref": "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eS3Bucket9CD8B20A" }, "/*" ] @@ -205,47 +209,6 @@ } ] }, - { - "Action": [ - "s3:GetObject*", - "s3:GetBucket*", - "s3:List*" - ], - "Effect": "Allow", - "Resource": [ - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":s3:::", - { - "Ref": "DeployWithPrefixAsset1S3BucketB954F7C6" - } - ] - ] - }, - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":s3:::", - { - "Ref": "DeployWithPrefixAsset1S3BucketB954F7C6" - }, - "/*" - ] - ] - } - ] - }, { "Action": [ "s3:GetObject*", @@ -295,7 +258,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CCodeS3Bucket6E5FB2B7" + "Ref": "AssetParameters6d6f96613f83f126b1dd5b0eb73ef86ad4d3e4487a4cd406c15e5071520c65bfS3Bucket123CF081" }, "S3Key": { "Fn::Join": [ @@ -308,7 +271,7 @@ "Fn::Split": [ "||", { - "Ref": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CCodeS3VersionKey426156C0" + "Ref": "AssetParameters6d6f96613f83f126b1dd5b0eb73ef86ad4d3e4487a4cd406c15e5071520c65bfS3VersionKey7C83FB30" } ] } @@ -321,7 +284,7 @@ "Fn::Split": [ "||", { - "Ref": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CCodeS3VersionKey426156C0" + "Ref": "AssetParameters6d6f96613f83f126b1dd5b0eb73ef86ad4d3e4487a4cd406c15e5071520c65bfS3VersionKey7C83FB30" } ] } @@ -348,8 +311,8 @@ }, "Destination281A09BDF": { "Type": "AWS::S3::Bucket", - "DeletionPolicy": "Retain", - "UpdateReplacePolicy": "Retain" + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" }, "DeployWithPrefixCustomResource9CF7C694": { "Type": "Custom::CDKBucketDeployment", @@ -360,88 +323,80 @@ "Arn" ] }, - "SourceBucketNames": [{ - "Ref": "DeployWithPrefixAsset1S3BucketB954F7C6" - }], - "SourceObjectKeys": [{ - "Fn::Join": [ - "", - [ - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "||", - { - "Ref": "DeployWithPrefixAsset1S3VersionKeyC9498C3B" - } - ] - } - ] - }, - { - "Fn::Select": [ - 1, - { - "Fn::Split": [ - "||", - { - "Ref": "DeployWithPrefixAsset1S3VersionKeyC9498C3B" - } - ] - } - ] - } + "SourceBucketNames": [ + { + "Ref": "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eS3Bucket9CD8B20A" + } + ], + "SourceObjectKeys": [ + { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eS3VersionKeyA58D380C" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eS3VersionKeyA58D380C" + } + ] + } + ] + } + ] ] - ] - }], + } + ], "DestinationBucketName": { "Ref": "Destination281A09BDF" }, "DestinationBucketKeyPrefix": "deploy/here/", "RetainOnDelete": false }, - "DeletionPolicy": "Delete", - "UpdateReplacePolicy": "Delete" + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" } }, "Parameters": { - "DeployMeAsset1S3Bucket8ED384CC": { - "Type": "String", - "Description": "S3 bucket for asset \"test-bucket-deployments-1/DeployMe/Asset1\"" - }, - "DeployMeAsset1S3VersionKeyA278D1EB": { - "Type": "String", - "Description": "S3 key for asset version \"test-bucket-deployments-1/DeployMe/Asset1\"" - }, - "DeployMeAsset1ArtifactHashCB325942": { - "Type": "String", - "Description": "Artifact hash for asset \"test-bucket-deployments-1/DeployMe/Asset1\"" - }, - "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CCodeS3Bucket6E5FB2B7": { + "AssetParameters6d6f96613f83f126b1dd5b0eb73ef86ad4d3e4487a4cd406c15e5071520c65bfS3Bucket123CF081": { "Type": "String", - "Description": "S3 bucket for asset \"test-bucket-deployments-1/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/Code\"" + "Description": "S3 bucket for asset \"6d6f96613f83f126b1dd5b0eb73ef86ad4d3e4487a4cd406c15e5071520c65bf\"" }, - "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CCodeS3VersionKey426156C0": { + "AssetParameters6d6f96613f83f126b1dd5b0eb73ef86ad4d3e4487a4cd406c15e5071520c65bfS3VersionKey7C83FB30": { "Type": "String", - "Description": "S3 key for asset version \"test-bucket-deployments-1/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/Code\"" + "Description": "S3 key for asset version \"6d6f96613f83f126b1dd5b0eb73ef86ad4d3e4487a4cd406c15e5071520c65bf\"" }, - "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CCodeArtifactHashEF37AD24": { + "AssetParameters6d6f96613f83f126b1dd5b0eb73ef86ad4d3e4487a4cd406c15e5071520c65bfArtifactHashAB0385B6": { "Type": "String", - "Description": "Artifact hash for asset \"test-bucket-deployments-1/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/Code\"" + "Description": "Artifact hash for asset \"6d6f96613f83f126b1dd5b0eb73ef86ad4d3e4487a4cd406c15e5071520c65bf\"" }, - "DeployWithPrefixAsset1S3BucketB954F7C6": { + "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eS3Bucket9CD8B20A": { "Type": "String", - "Description": "S3 bucket for asset \"test-bucket-deployments-1/DeployWithPrefix/Asset1\"" + "Description": "S3 bucket for asset \"fc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222e\"" }, - "DeployWithPrefixAsset1S3VersionKeyC9498C3B": { + "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eS3VersionKeyA58D380C": { "Type": "String", - "Description": "S3 key for asset version \"test-bucket-deployments-1/DeployWithPrefix/Asset1\"" + "Description": "S3 key for asset version \"fc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222e\"" }, - "DeployWithPrefixAsset1ArtifactHash5137152F": { + "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eArtifactHash0E426313": { "Type": "String", - "Description": "Artifact hash for asset \"test-bucket-deployments-1/DeployWithPrefix/Asset1\"" + "Description": "Artifact hash for asset \"fc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222e\"" } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment.ts b/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment.ts index 8b9756b17d87b..a13e182ceb670 100644 --- a/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment.ts +++ b/packages/@aws-cdk/aws-s3-deployment/test/integ.bucket-deployment.ts @@ -32,6 +32,6 @@ class TestBucketDeployment extends cdk.Stack { const app = new cdk.App(); -new TestBucketDeployment(app, 'test-bucket-deployments-1'); +new TestBucketDeployment(app, 'test-bucket-deployments-2'); app.synth(); diff --git a/packages/@aws-cdk/aws-s3-deployment/test/test.bucket-deployment.ts b/packages/@aws-cdk/aws-s3-deployment/test/test.bucket-deployment.ts index a8b611abd5aee..a2a8d9af50824 100644 --- a/packages/@aws-cdk/aws-s3-deployment/test/test.bucket-deployment.ts +++ b/packages/@aws-cdk/aws-s3-deployment/test/test.bucket-deployment.ts @@ -30,7 +30,7 @@ export = { ] }, "SourceBucketNames": [{ - "Ref": "DeployAsset1S3BucketC03B223F" + "Ref": "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eS3Bucket9CD8B20A" }], "SourceObjectKeys": [{ "Fn::Join": [ @@ -43,7 +43,7 @@ export = { "Fn::Split": [ "||", { - "Ref": "DeployAsset1S3VersionKey7642D9E0" + "Ref": "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eS3VersionKeyA58D380C" } ] } @@ -56,7 +56,7 @@ export = { "Fn::Split": [ "||", { - "Ref": "DeployAsset1S3VersionKey7642D9E0" + "Ref": "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eS3VersionKeyA58D380C" } ] } @@ -96,10 +96,10 @@ export = { }, "SourceBucketNames": [ { - "Ref": "DeployAsset1S3BucketC03B223F" + "Ref": "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eS3Bucket9CD8B20A" }, { - "Ref": "DeployAsset2S3Bucket155AFD20" + "Ref": "AssetParametersa94977ede0211fd3b45efa33d6d8d1d7bbe0c5a96d977139d8b16abfa96fe9cbS3Bucket99793559" } ], "SourceObjectKeys": [ @@ -114,7 +114,7 @@ export = { "Fn::Split": [ "||", { - "Ref": "DeployAsset1S3VersionKey7642D9E0" + "Ref": "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eS3VersionKeyA58D380C" } ] } @@ -127,7 +127,7 @@ export = { "Fn::Split": [ "||", { - "Ref": "DeployAsset1S3VersionKey7642D9E0" + "Ref": "AssetParametersfc4481abf279255619ff7418faa5d24456fef3432ea0da59c95542578ff0222eS3VersionKeyA58D380C" } ] } @@ -147,7 +147,7 @@ export = { "Fn::Split": [ "||", { - "Ref": "DeployAsset2S3VersionKey8324D51E" + "Ref": "AssetParametersa94977ede0211fd3b45efa33d6d8d1d7bbe0c5a96d977139d8b16abfa96fe9cbS3VersionKeyD9ACE665" } ] } @@ -160,7 +160,7 @@ export = { "Fn::Split": [ "||", { - "Ref": "DeployAsset2S3VersionKey8324D51E" + "Ref": "AssetParametersa94977ede0211fd3b45efa33d6d8d1d7bbe0c5a96d977139d8b16abfa96fe9cbS3VersionKeyD9ACE665" } ] } diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.ec2-task.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.ec2-task.expected.json index 28b5de28ab8a6..803a5cad378ef 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.ec2-task.expected.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.ec2-task.expected.json @@ -454,74 +454,24 @@ "Fn::Join": [ "", [ + "12345678.dkr.ecr.test-region.", { - "Fn::Select": [ - 4, - { - "Fn::Split": [ - ":", - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ecr:test-region:12345678:repository/", - { - "Fn::GetAtt": [ - "TaskDefTheContainerAssetImageAdoptRepository997406C3", - "RepositoryName" - ] - } - ] - ] - } - ] - } - ] + "Ref": "AWS::URLSuffix" }, - ".dkr.ecr.", + "/", { "Fn::Select": [ - 3, + 0, { "Fn::Split": [ - ":", + "@sha256:", { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ecr:test-region:12345678:repository/", - { - "Fn::GetAtt": [ - "TaskDefTheContainerAssetImageAdoptRepository997406C3", - "RepositoryName" - ] - } - ] - ] + "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" } ] } ] }, - ".", - { - "Ref": "AWS::URLSuffix" - }, - "/", - { - "Fn::GetAtt": [ - "TaskDefTheContainerAssetImageAdoptRepository997406C3", - "RepositoryName" - ] - }, "@sha256:", { "Fn::Select": [ @@ -530,7 +480,7 @@ "Fn::Split": [ "@sha256:", { - "Ref": "TaskDefTheContainerAssetImageImageName92ECAC22" + "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" } ] } @@ -588,7 +538,7 @@ "Fn::Split": [ "@sha256:", { - "Ref": "TaskDefTheContainerAssetImageImageName92ECAC22" + "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" } ] } @@ -745,7 +695,7 @@ "Fn::Split": [ "@sha256:", { - "Ref": "TaskDefTheContainerAssetImageImageName92ECAC22" + "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" } ] } @@ -771,7 +721,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3Bucket92AB06B6" + "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB" }, "S3Key": { "Fn::Join": [ @@ -784,7 +734,7 @@ "Fn::Split": [ "||", { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276" + "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" } ] } @@ -797,7 +747,7 @@ "Fn::Split": [ "||", { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276" + "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" } ] } @@ -944,21 +894,21 @@ "Type": "AWS::SSM::Parameter::Value", "Default": "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id" }, - "TaskDefTheContainerAssetImageImageName92ECAC22": { + "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A": { "Type": "String", - "Description": "ECR repository name and tag asset \"aws-ecs-integ2/TaskDef/TheContainer/AssetImage\"" + "Description": "ECR repository name and tag for asset \"849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3\"" }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3Bucket92AB06B6": { + "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB": { "Type": "String", - "Description": "S3 bucket for asset \"aws-ecs-integ2/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" + "Description": "S3 bucket for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276": { + "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7": { "Type": "String", - "Description": "S3 key for asset version \"aws-ecs-integ2/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" + "Description": "S3 key for asset version \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeArtifactHash8BCBAA49": { + "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1ArtifactHash7E5AE478": { "Type": "String", - "Description": "Artifact hash for asset \"aws-ecs-integ2/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" + "Description": "Artifact hash for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.fargate-task.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.fargate-task.expected.json index 04cb056d62690..66af2f5913397 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.fargate-task.expected.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.fargate-task.expected.json @@ -30,74 +30,24 @@ "Fn::Join": [ "", [ + "12345678.dkr.ecr.test-region.", { - "Fn::Select": [ - 4, - { - "Fn::Split": [ - ":", - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ecr:test-region:12345678:repository/", - { - "Fn::GetAtt": [ - "TaskDefTheContainerAssetImageAdoptRepository997406C3", - "RepositoryName" - ] - } - ] - ] - } - ] - } - ] + "Ref": "AWS::URLSuffix" }, - ".dkr.ecr.", + "/", { "Fn::Select": [ - 3, + 0, { "Fn::Split": [ - ":", + "@sha256:", { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ecr:test-region:12345678:repository/", - { - "Fn::GetAtt": [ - "TaskDefTheContainerAssetImageAdoptRepository997406C3", - "RepositoryName" - ] - } - ] - ] + "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" } ] } ] }, - ".", - { - "Ref": "AWS::URLSuffix" - }, - "/", - { - "Fn::GetAtt": [ - "TaskDefTheContainerAssetImageAdoptRepository997406C3", - "RepositoryName" - ] - }, "@sha256:", { "Fn::Select": [ @@ -106,7 +56,7 @@ "Fn::Split": [ "@sha256:", { - "Ref": "TaskDefTheContainerAssetImageImageName92ECAC22" + "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" } ] } @@ -166,7 +116,7 @@ "Fn::Split": [ "@sha256:", { - "Ref": "TaskDefTheContainerAssetImageImageName92ECAC22" + "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" } ] } @@ -323,7 +273,7 @@ "Fn::Split": [ "@sha256:", { - "Ref": "TaskDefTheContainerAssetImageImageName92ECAC22" + "Ref": "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A" } ] } @@ -349,7 +299,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3Bucket92AB06B6" + "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB" }, "S3Key": { "Fn::Join": [ @@ -362,7 +312,7 @@ "Fn::Split": [ "||", { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276" + "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" } ] } @@ -375,7 +325,7 @@ "Fn::Split": [ "||", { - "Ref": "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276" + "Ref": "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7" } ] } @@ -539,21 +489,21 @@ } }, "Parameters": { - "TaskDefTheContainerAssetImageImageName92ECAC22": { + "AssetParameters849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3ImageName999B381A": { "Type": "String", - "Description": "ECR repository name and tag asset \"aws-ecs-integ2/TaskDef/TheContainer/AssetImage\"" + "Description": "ECR repository name and tag for asset \"849429eca8deea448d1c953aea3403a3d05d598c09880305dde8e99010d05db3\"" }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3Bucket92AB06B6": { + "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3BucketE797C7BB": { "Type": "String", - "Description": "S3 bucket for asset \"aws-ecs-integ2/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" + "Description": "S3 bucket for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeS3VersionKey393B7276": { + "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1S3VersionKey56C3F6D7": { "Type": "String", - "Description": "S3 key for asset version \"aws-ecs-integ2/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" + "Description": "S3 key for asset version \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" }, - "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cCodeArtifactHash8BCBAA49": { + "AssetParametersea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1ArtifactHash7E5AE478": { "Type": "String", - "Description": "Artifact hash for asset \"aws-ecs-integ2/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" + "Description": "Artifact hash for asset \"ea7034d81c091be1158bcd85b4958dc86ec6672c345be27607d68fdfcf26b1c1\"" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.invoke-function.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.invoke-function.expected.json index 5442217cf60a3..af5ffe6b651d8 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.invoke-function.expected.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.invoke-function.expected.json @@ -36,7 +36,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "HandlerCodeS3Bucket8DD11ED9" + "Ref": "AssetParameters9678c34eca93259d11f2d714177347afd66c50116e1e08996eff893d3ca81232S3Bucket1354C645" }, "S3Key": { "Fn::Join": [ @@ -49,7 +49,7 @@ "Fn::Split": [ "||", { - "Ref": "HandlerCodeS3VersionKey0BB5191E" + "Ref": "AssetParameters9678c34eca93259d11f2d714177347afd66c50116e1e08996eff893d3ca81232S3VersionKey5D873FAC" } ] } @@ -62,7 +62,7 @@ "Fn::Split": [ "||", { - "Ref": "HandlerCodeS3VersionKey0BB5191E" + "Ref": "AssetParameters9678c34eca93259d11f2d714177347afd66c50116e1e08996eff893d3ca81232S3VersionKey5D873FAC" } ] } @@ -121,7 +121,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "CallbackHandlerCodeS3Bucket806D7490" + "Ref": "AssetParameters9678c34eca93259d11f2d714177347afd66c50116e1e08996eff893d3ca81232S3Bucket1354C645" }, "S3Key": { "Fn::Join": [ @@ -134,7 +134,7 @@ "Fn::Split": [ "||", { - "Ref": "CallbackHandlerCodeS3VersionKeyDD40A461" + "Ref": "AssetParameters9678c34eca93259d11f2d714177347afd66c50116e1e08996eff893d3ca81232S3VersionKey5D873FAC" } ] } @@ -147,7 +147,7 @@ "Fn::Split": [ "||", { - "Ref": "CallbackHandlerCodeS3VersionKeyDD40A461" + "Ref": "AssetParameters9678c34eca93259d11f2d714177347afd66c50116e1e08996eff893d3ca81232S3VersionKey5D873FAC" } ] } @@ -266,29 +266,17 @@ } }, "Parameters": { - "HandlerCodeS3Bucket8DD11ED9": { + "AssetParameters9678c34eca93259d11f2d714177347afd66c50116e1e08996eff893d3ca81232S3Bucket1354C645": { "Type": "String", - "Description": "S3 bucket for asset \"aws-stepfunctions-integ/Handler/Code\"" + "Description": "S3 bucket for asset \"9678c34eca93259d11f2d714177347afd66c50116e1e08996eff893d3ca81232\"" }, - "HandlerCodeS3VersionKey0BB5191E": { + "AssetParameters9678c34eca93259d11f2d714177347afd66c50116e1e08996eff893d3ca81232S3VersionKey5D873FAC": { "Type": "String", - "Description": "S3 key for asset version \"aws-stepfunctions-integ/Handler/Code\"" + "Description": "S3 key for asset version \"9678c34eca93259d11f2d714177347afd66c50116e1e08996eff893d3ca81232\"" }, - "HandlerCodeArtifactHashD7814EF8": { + "AssetParameters9678c34eca93259d11f2d714177347afd66c50116e1e08996eff893d3ca81232ArtifactHashFA16AACF": { "Type": "String", - "Description": "Artifact hash for asset \"aws-stepfunctions-integ/Handler/Code\"" - }, - "CallbackHandlerCodeS3Bucket806D7490": { - "Type": "String", - "Description": "S3 bucket for asset \"aws-stepfunctions-integ/CallbackHandler/Code\"" - }, - "CallbackHandlerCodeS3VersionKeyDD40A461": { - "Type": "String", - "Description": "S3 key for asset version \"aws-stepfunctions-integ/CallbackHandler/Code\"" - }, - "CallbackHandlerCodeArtifactHash2D279BFF": { - "Type": "String", - "Description": "Artifact hash for asset \"aws-stepfunctions-integ/CallbackHandler/Code\"" + "Description": "Artifact hash for asset \"9678c34eca93259d11f2d714177347afd66c50116e1e08996eff893d3ca81232\"" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/core/lib/assets.ts b/packages/@aws-cdk/core/lib/assets.ts new file mode 100644 index 0000000000000..55d11ce02571a --- /dev/null +++ b/packages/@aws-cdk/core/lib/assets.ts @@ -0,0 +1,126 @@ +/** + * Represents the source for a file asset. + */ +export interface FileAssetSource { + /** + * A hash on the content source. This hash is used to uniquely identify this + * asset throughout the system. If this value doesn't change, the asset will + * not be rebuilt or republished. + */ + readonly sourceHash: string; + + /** + * The path, relative to the root of the cloud assembly, in which this asset + * source resides. This can be a path to a file or a directory, dependning on the + * packaging type. + */ + readonly fileName: string; + + /** + * Which type of packaging to perform. + */ + readonly packaging: FileAssetPackaging; +} + +export interface DockerImageAssetSource { + /** + * The hash of the contents of the docker build context. This hash is used + * throughout the system to identify this image and avoid duplicate work + * in case the source did not change. + * + * NOTE: this means that if you wish to update your docker image, you + * must make a modification to the source (e.g. add some metadata to your Dockerfile). + */ + readonly sourceHash: string; + + /** + * The directory where the Dockerfile is stored, must be relative + * to the cloud assembly root. + */ + readonly directoryName: string; + + /** + * Build args to pass to the `docker build` command. + * + * Since Docker build arguments are resolved before deployment, keys and + * values cannot refer to unresolved tokens (such as `lambda.functionArn` or + * `queue.queueUrl`). + * + * @default - no build args are passed + */ + readonly dockerBuildArgs?: { [key: string]: string }; + + /** + * Docker target to build to + * + * @default - no target + */ + readonly dockerBuildTarget?: string; + + /** + * ECR repository name + * + * Specify this property if you need to statically address the image, e.g. + * from a Kubernetes Pod. Note, this is only the repository name, without the + * registry and the tag parts. + * + * @default - automatically derived from the asset's ID. + */ + readonly repositoryName?: string; +} + +/** + * Packaging modes for file assets. + */ +export enum FileAssetPackaging { + /** + * The asset source path points to a directory, which should be archived using + * zip and and then uploaded to Amazon S3. + */ + ZIP_DIRECTORY = 'zip', + + /** + * The asset source path points to a single file, which should be uploaded + * to Amazon S3. + */ + FILE = 'file' +} + +/** + * The location of the published file asset. This is where the asset + * can be consumed at runtime. + */ +export interface FileAssetLocation { + /** + * The name of the Amazon S3 bucket. + */ + readonly bucketName: string; + + /** + * The Amazon S3 object key. + */ + readonly objectKey: string; + + /** + * The HTTP URL of this asset on Amazon S3. + * + * @example https://s3-us-east-1.amazonaws.com/mybucket/myobject + */ + readonly s3Url: string; +} + +/** + * The location of the published docker image. This is where the image can be + * consumed at runtime. + */ +export interface DockerImageAssetLocation { + /** + * The URI of the image in Amazon ECR. + */ + readonly imageUri: string; + + /** + * The name of the ECR repository. + */ + readonly repositoryName: string; +} diff --git a/packages/@aws-cdk/core/lib/cfn-element.ts b/packages/@aws-cdk/core/lib/cfn-element.ts index 67cc73263f7ae..bcab44358e391 100644 --- a/packages/@aws-cdk/core/lib/cfn-element.ts +++ b/packages/@aws-cdk/core/lib/cfn-element.ts @@ -118,26 +118,6 @@ export abstract class CfnElement extends Construct { */ public abstract _toCloudFormation(): object; - /** - * Automatically detect references in this CfnElement - */ - protected prepare() { - try { - // Note: it might be that the properties of the CFN object aren't valid. - // This will usually be preventatively caught in a construct's validate() - // and turned into a nicely descriptive error, but we're running prepare() - // before validate(). Swallow errors that occur because the CFN layer - // doesn't validate completely. - // - // This does make the assumption that the error will not be rectified, - // but the error will be thrown later on anyway. If the error doesn't - // get thrown down the line, we may miss references. - this.node.addReference(...findTokens(this, () => this._toCloudFormation())); - } catch (e) { - if (e.type !== 'CfnSynthesisError') { throw e; } - } - } - /** * Called during synthesize to render the logical ID of this element. If * `overrideLogicalId` was it will be used, otherwise, we will allocate the @@ -180,5 +160,4 @@ function notTooLong(x: string) { } import { CfnReference } from "./private/cfn-reference"; -import { findTokens } from "./private/resolve"; import { Stack } from './stack'; diff --git a/packages/@aws-cdk/core/lib/cfn-resource.ts b/packages/@aws-cdk/core/lib/cfn-resource.ts index c958f45b6001d..90360b57e094f 100644 --- a/packages/@aws-cdk/core/lib/cfn-resource.ts +++ b/packages/@aws-cdk/core/lib/cfn-resource.ts @@ -6,8 +6,8 @@ import { CfnRefElement } from './cfn-element'; import { CfnCreationPolicy, CfnDeletionPolicy, CfnUpdatePolicy } from './cfn-resource-policy'; import { Construct, IConstruct } from './construct'; import { CfnReference } from './private/cfn-reference'; +import { Reference } from './reference'; import { RemovalPolicy, RemovalPolicyOptions } from './removal-policy'; -import { IResolvable } from './resolvable'; import { TagManager } from './tag-manager'; import { capitalizePropertyNames, ignoreEmpty, PostResolveToken } from './util'; @@ -131,7 +131,7 @@ export class CfnResource extends CfnRefElement { * in case there is no generated attribute. * @param attributeName The name of the attribute. */ - public getAtt(attributeName: string): IResolvable { + public getAtt(attributeName: string): Reference { return CfnReference.for(this, attributeName); } diff --git a/packages/@aws-cdk/core/lib/construct.ts b/packages/@aws-cdk/core/lib/construct.ts index de7251a64b988..025d97e23afef 100644 --- a/packages/@aws-cdk/core/lib/construct.ts +++ b/packages/@aws-cdk/core/lib/construct.ts @@ -2,7 +2,6 @@ import cxapi = require('@aws-cdk/cx-api'); import { IAspect } from './aspect'; import { DependableTrait, IDependable } from './dependency'; import { makeUniqueId } from './private/uniqueid'; -import { IResolvable } from './resolvable'; import { captureStackTrace } from './stack-trace'; import { Token } from './token'; @@ -40,26 +39,24 @@ export class ConstructNode { // prepare this.prepare(root); - // do not allow adding children after this stage - root._lock(); - - try { - // validate - const validate = options.skipValidation === undefined ? true : !options.skipValidation; - if (validate) { - const errors = this.validate(root); - if (errors.length > 0) { - const errorList = errors.map(e => `[${e.source.node.path}] ${e.message}`).join('\n '); - throw new Error(`Validation failed with the following errors:\n ${errorList}`); - } + // validate + const validate = options.skipValidation === undefined ? true : !options.skipValidation; + if (validate) { + const errors = this.validate(root); + if (errors.length > 0) { + const errorList = errors.map(e => `[${e.source.node.path}] ${e.message}`).join('\n '); + throw new Error(`Validation failed with the following errors:\n ${errorList}`); } + } - // synthesize (leaves first) - for (const construct of root.findAll(ConstructOrder.POSTORDER)) { + // synthesize (leaves first) + for (const construct of root.findAll(ConstructOrder.POSTORDER)) { + try { + construct.node._lock(); (construct as any).synthesize({ assembly: builder }); // "as any" is needed because we want to keep "synthesize" protected + } finally { + construct.node._unlock(); } - } finally { - root._unlock(); } // write session manifest and lock store @@ -122,7 +119,6 @@ export class ConstructNode { private readonly _children: { [id: string]: IConstruct } = { }; private readonly _context: { [key: string]: any } = { }; private readonly _metadata = new Array(); - private readonly _references = new Set(); private readonly _dependencies = new Set(); private readonly invokedAspects: IAspect[] = []; private _defaultChild: IConstruct | undefined; @@ -406,38 +402,6 @@ export class ConstructNode { return false; } - /** - * Record a reference originating from this construct node - */ - public addReference(...refs: IResolvable[]) { - for (const ref of refs) { - if (Reference.isReference(ref)) { - this._references.add(ref); - } - } - } - - /** - * Return all references originating from this node or any of its children - */ - public get references(): OutgoingReference[] { - const ret = new Set(); - - function recurse(node: ConstructNode) { - for (const reference of node._references) { - ret.add({ source: node.host, reference }); - } - - for (const child of node.children) { - recurse(child.node); - } - } - - recurse(this); - - return Array.from(ret); - } - /** * Add an ordering dependency on another Construct. * @@ -662,21 +626,6 @@ export interface Dependency { readonly target: IConstruct; } -/** - * Represents a reference that originates from a specific construct. - */ -export interface OutgoingReference { - /** - * The originating construct. - */ - readonly source: IConstruct; - - /** - * The reference. - */ - readonly reference: Reference; -} - /** * Represents a single session of synthesis. Passed into `Construct.synthesize()` methods. */ @@ -709,9 +658,6 @@ function ignore(_x: any) { } // Import this _after_ everything else to help node work the classes out in the correct order... - -import { Reference } from './reference'; - const PATH_SEP_REGEX = new RegExp(`${ConstructNode.PATH_SEP}`, 'g'); /** diff --git a/packages/@aws-cdk/core/lib/index.ts b/packages/@aws-cdk/core/lib/index.ts index 604fde23ad45a..9c766c4b522d9 100644 --- a/packages/@aws-cdk/core/lib/index.ts +++ b/packages/@aws-cdk/core/lib/index.ts @@ -38,6 +38,7 @@ export * from './secret-value'; export * from './resource'; export * from './physical-name'; +export * from './assets'; // WARNING: Should not be exported, but currently is because of a bug. See the // class description for more information. diff --git a/packages/@aws-cdk/core/lib/private/asset-parameters.ts b/packages/@aws-cdk/core/lib/private/asset-parameters.ts new file mode 100644 index 0000000000000..cc74067bccc37 --- /dev/null +++ b/packages/@aws-cdk/core/lib/private/asset-parameters.ts @@ -0,0 +1,44 @@ +import { CfnParameter } from '../cfn-parameter'; +import { Construct } from '../construct'; + +export class FileAssetParameters extends Construct { + public readonly bucketNameParameter: CfnParameter; + public readonly objectKeyParameter: CfnParameter; + public readonly artifactHashParameter: CfnParameter; + + constructor(scope: Construct, id: string) { + super(scope, id); + + // add parameters for s3 bucket and s3 key. those will be set by + // the toolkit or by CI/CD when the stack is deployed and will include + // the name of the bucket and the S3 key where the code lives. + + this.bucketNameParameter = new CfnParameter(this, 'S3Bucket', { + type: 'String', + description: `S3 bucket for asset "${id}"`, + }); + + this.objectKeyParameter = new CfnParameter(this, 'S3VersionKey', { + type: 'String', + description: `S3 key for asset version "${id}"` + }); + + this.artifactHashParameter = new CfnParameter(this, 'ArtifactHash', { + description: `Artifact hash for asset "${id}"`, + type: 'String', + }); + } +} + +export class DockerImageAssetParameters extends Construct { + public readonly imageNameParameter: CfnParameter; + + constructor(scope: Construct, id: string) { + super(scope, id); + + this.imageNameParameter = new CfnParameter(this, 'ImageName', { + type: 'String', + description: `ECR repository name and tag for asset "${id}"`, + }); + } +} diff --git a/packages/@aws-cdk/core/lib/private/cfn-reference.ts b/packages/@aws-cdk/core/lib/private/cfn-reference.ts index df10cfdb9a6cc..a6173b8551449 100644 --- a/packages/@aws-cdk/core/lib/private/cfn-reference.ts +++ b/packages/@aws-cdk/core/lib/private/cfn-reference.ts @@ -1,5 +1,4 @@ import { Reference } from "../reference"; -import { makeUniqueId } from './uniqueid'; const CFN_REFERENCE_SYMBOL = Symbol.for('@aws-cdk/core.CfnReference'); @@ -74,27 +73,17 @@ export class CfnReference extends Reference { return ref; } - /** - * What stack this Token is pointing to - */ - private readonly producingStack?: Stack; - /** * The Tokens that should be returned for each consuming stack (as decided by the producing Stack) */ private readonly replacementTokens: Map; - private readonly originalDisplayName: string; - private readonly humanReadableDesc: string; - - protected constructor(value: any, private readonly displayName: string, target: IConstruct) { + protected constructor(value: any, displayName: string, target: IConstruct) { // prepend scope path to display name - super(value, target); - this.originalDisplayName = displayName; + super(value, target, displayName); + this.replacementTokens = new Map(); - this.humanReadableDesc = `target = ${target.node.path}`; - this.producingStack = Stack.of(target); Object.defineProperty(this, CFN_REFERENCE_SYMBOL, { value: true }); } @@ -103,10 +92,11 @@ export class CfnReference extends Reference { // we are in the same stack. const consumingStack = Stack.of(context.scope); const token = this.replacementTokens.get(consumingStack); - if (!token && this.isCrossStackReference(consumingStack) && !context.preparing) { - // tslint:disable-next-line:max-line-length - throw new Error(`Cross-stack reference (${context.scope.node.path} -> ${this.target.node.path}) has not been assigned a value--call prepare() first`); - } + + // if (!token && this.isCrossStackReference(consumingStack) && !context.preparing) { + // tslint:disable-next-line:max-line-length + // throw new Error(`Cross-stack reference (${context.scope.node.path} -> ${this.target.node.path}) has not been assigned a value--call prepare() first`); + // } if (token) { return token.resolve(context); @@ -115,24 +105,17 @@ export class CfnReference extends Reference { } } - /** - * Register a stack this references is being consumed from. - */ - public consumeFromStack(consumingStack: Stack, consumingConstruct: IConstruct) { - if (this.producingStack && consumingStack.node.root !== this.producingStack.node.root) { - throw this.newError( - `Cannot reference across apps. ` + - `Consuming and producing stacks must be defined within the same CDK app.`); - } + public hasValueForStack(stack: Stack) { + return this.replacementTokens.has(stack); + } - // tslint:disable-next-line:max-line-length - if (!this.replacementTokens.has(consumingStack) && this.isCrossStackReference(consumingStack)) { - // We're trying to resolve a cross-stack reference - consumingStack.addDependency(this.producingStack!, `${consumingConstruct.node.path} -> ${this.target.node.path}.${this.originalDisplayName}`); - this.replacementTokens.set(consumingStack, this.exportValue(consumingStack)); + public assignValueForStack(stack: Stack, value: IResolvable) { + if (this.hasValueForStack(stack)) { + throw new Error(`Cannot assign a reference value twice to the same stack. Use hasValueForStack to check first`); } - } + this.replacementTokens.set(stack, value); + } /** * Implementation of toString() that will use the display name */ @@ -141,61 +124,10 @@ export class CfnReference extends Reference { displayHint: `${this.target.node.id}.${this.displayName}` }); } - - /** - * Export a Token value for use in another stack - * - * Works by mutating the producing stack in-place. - */ - private exportValue(consumingStack: Stack): IResolvable { - const producingStack = this.producingStack!; - - if (producingStack.environment !== consumingStack.environment) { - throw this.newError(`Can only reference cross stacks in the same region and account. ${this.humanReadableDesc}`); - } - - // Ensure a singleton "Exports" scoping Construct - // This mostly exists to trigger LogicalID munging, which would be - // disabled if we parented constructs directly under Stack. - // Also it nicely prevents likely construct name clashes - - const exportsName = 'Exports'; - let stackExports = producingStack.node.tryFindChild(exportsName) as Construct; - if (stackExports === undefined) { - stackExports = new Construct(producingStack, exportsName); - } - - // Ensure a singleton CfnOutput for this value - const resolved = producingStack.resolve(this); - const id = 'Output' + JSON.stringify(resolved); - const exportName = this.generateExportName(stackExports, id); - let output = stackExports.node.tryFindChild(id) as CfnOutput; - if (!output) { - output = new CfnOutput(stackExports, id, { value: Token.asString(this), exportName }); - } - - // We want to return an actual FnImportValue Token here, but Fn.importValue() returns a 'string', - // so construct one in-place. - return new Intrinsic({ 'Fn::ImportValue': exportName }); - } - - private generateExportName(stackExports: Construct, id: string) { - const stack = Stack.of(stackExports); - const components = [...stackExports.node.scopes.slice(2).map(c => c.node.id), id]; - const prefix = stack.stackName ? stack.stackName + ':' : ''; - const exportName = prefix + makeUniqueId(components); - return exportName; - } - - private isCrossStackReference(consumingStack: Stack) { - return this.producingStack && this.producingStack !== consumingStack; - } } import { CfnElement } from "../cfn-element"; -import { CfnOutput } from "../cfn-output"; import { Construct, IConstruct } from "../construct"; import { IResolvable, IResolveContext } from "../resolvable"; import { Stack } from "../stack"; import { Token } from "../token"; -import { Intrinsic } from "./intrinsic"; diff --git a/packages/@aws-cdk/core/lib/private/resolve.ts b/packages/@aws-cdk/core/lib/private/resolve.ts index 2661dad601a5c..d18ea357b0e3b 100644 --- a/packages/@aws-cdk/core/lib/private/resolve.ts +++ b/packages/@aws-cdk/core/lib/private/resolve.ts @@ -210,4 +210,4 @@ function resolveNumberToken(x: number, context: IResolveContext): any { const token = TokenMap.instance().lookupNumberToken(x); if (token === undefined) { return x; } return context.resolve(token); -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/core/lib/reference.ts b/packages/@aws-cdk/core/lib/reference.ts index cdc3417243aa7..022b507a82b35 100644 --- a/packages/@aws-cdk/core/lib/reference.ts +++ b/packages/@aws-cdk/core/lib/reference.ts @@ -16,12 +16,14 @@ export abstract class Reference extends Intrinsic { } public readonly target: IConstruct; + public readonly displayName: string; - constructor(value: any, target: IConstruct) { + constructor(value: any, target: IConstruct, displayName?: string) { super(value); - this.target = target; Object.defineProperty(this, REFERENCE_SYMBOL, { value: true }); + this.target = target; + this.displayName = displayName || 'Reference'; } } -import { IConstruct } from "./construct"; +import { IConstruct } from './construct'; diff --git a/packages/@aws-cdk/core/lib/stack.ts b/packages/@aws-cdk/core/lib/stack.ts index 4e3778c3ad864..1fc83b214bf28 100644 --- a/packages/@aws-cdk/core/lib/stack.ts +++ b/packages/@aws-cdk/core/lib/stack.ts @@ -1,13 +1,16 @@ import cxapi = require('@aws-cdk/cx-api'); import { EnvironmentUtils } from '@aws-cdk/cx-api'; +import crypto = require('crypto'); import fs = require('fs'); import path = require('path'); +import { DockerImageAssetLocation, DockerImageAssetSource, FileAssetLocation , FileAssetPackaging, FileAssetSource } from './assets'; import { Construct, ConstructNode, IConstruct, ISynthesisSession } from './construct'; import { ContextProvider } from './context-provider'; import { Environment } from './environment'; +import { DockerImageAssetParameters, FileAssetParameters } from './private/asset-parameters'; import { CLOUDFORMATION_TOKEN_RESOLVER, CloudFormationLang } from './private/cloudformation-lang'; import { LogicalIDs } from './private/logical-id'; -import { resolve } from './private/resolve'; +import { findTokens , resolve } from './private/resolve'; import { makeUniqueId } from './private/uniqueid'; const STACK_SYMBOL = Symbol.for('@aws-cdk/core.Stack'); @@ -80,20 +83,6 @@ export class Stack extends Construct implements ITaggable { */ public readonly templateOptions: ITemplateOptions = {}; - /** - * The concrete CloudFormation physical stack name. - * - * This is either the name defined explicitly in the `stackName` prop or - * allocated based on the stack's location in the construct tree. Stacks that - * are directly defined under the app use their construct `id` as their stack - * name. Stacks that are defined deeper within the tree will use a hashed naming - * scheme based on the construct path to ensure uniqueness. - * - * If you wish to obtain the deploy-time AWS::StackName intrinsic, - * you can use `Aws.stackName` directly. - */ - public readonly stackName: string; - /** * The AWS region into which this stack will be deployed (e.g. `us-west-2`). * @@ -153,6 +142,21 @@ export class Stack extends Construct implements ITaggable { */ public readonly environment: string; + /** + * Returns the parent stack if this stack is nested. + * + * @experimental + */ + public readonly parentStack?: Stack; + + /** + * An attribute (late-bound) that represents the URL of the template file + * in the deployment bucket. + * + * @experimental + */ + public readonly templateUrl: string; + /** * The name of the CloudFormation template file emitted to the output * directory during synthesis. @@ -178,6 +182,14 @@ export class Stack extends Construct implements ITaggable { */ private readonly _missingContext = new Array(); + /** + * Includes all parameters synthesized for assets (lazy). + */ + private _assetParameters?: Construct; + + private _templateUrl?: string; + private readonly _stackName: string; + /** * Creates a new stack. * @@ -199,7 +211,7 @@ export class Stack extends Construct implements ITaggable { this.region = region; this.environment = environment; - this.stackName = props.stackName !== undefined ? props.stackName : this.calculateStackName(); + this._stackName = props.stackName !== undefined ? props.stackName : this.calculateStackName(); this.tags = new TagManager(TagType.KEY_VALUE, 'aws:cdk:stack', props.tags); if (!VALID_STACK_NAME_REGEX.test(this.stackName)) { @@ -207,6 +219,7 @@ export class Stack extends Construct implements ITaggable { } this.templateFile = `${this.stackName}.template.json`; + this.templateUrl = Lazy.stringValue({ produce: () => this._templateUrl || '' }); } /** @@ -283,6 +296,11 @@ export class Stack extends Construct implements ITaggable { throw new Error(`'${stack.node.path}' depends on '${this.node.path}' (${dep.join(', ')}). Adding this dependency (${reason}) would create a cyclic reference.`); } this._stackDependencies.add({ stack, reason }); + + if (process.env.CDK_DEBUG_DEPS) { + // tslint:disable-next-line:no-console + console.error(`[CDK_DEBUG_DEPS] stack "${this.node.path}" depends on "${stack.node.path}" because: ${reason}`); + } } /** @@ -292,6 +310,22 @@ export class Stack extends Construct implements ITaggable { return Array.from(this._stackDependencies.values()).map(d => d.stack); } + /** + * The concrete CloudFormation physical stack name. + * + * This is either the name defined explicitly in the `stackName` prop or + * allocated based on the stack's location in the construct tree. Stacks that + * are directly defined under the app use their construct `id` as their stack + * name. Stacks that are defined deeper within the tree will use a hashed naming + * scheme based on the construct path to ensure uniqueness. + * + * If you wish to obtain the deploy-time AWS::StackName intrinsic, + * you can use `Aws.stackName` directly. + */ + public get stackName(): string { + return this._stackName; + } + /** * The partition in which this stack is defined */ @@ -326,6 +360,13 @@ export class Stack extends Construct implements ITaggable { return new ScopedAws(this).notificationArns; } + /** + * Indicates if this is a nested stack, in which case `parentStack` will include a reference to it's parent. + */ + public get nested(): boolean { + return this.parentStack !== undefined; + } + /** * Creates an ARN from components. * @@ -426,6 +467,80 @@ export class Stack extends Construct implements ITaggable { return value; } + public addFileAsset(asset: FileAssetSource): FileAssetLocation { + + // assets are always added at the top-level stack + if (this.parentStack) { + return this.parentStack.addFileAsset(asset); + } + + let params = this.assetParameters.node.tryFindChild(asset.sourceHash) as FileAssetParameters; + if (!params) { + params = new FileAssetParameters(this.assetParameters, asset.sourceHash); + + const metadata: cxapi.FileAssetMetadataEntry = { + path: asset.fileName, + id: asset.sourceHash, + packaging: asset.packaging, + sourceHash: asset.sourceHash, + + s3BucketParameter: params.bucketNameParameter.logicalId, + s3KeyParameter: params.objectKeyParameter.logicalId, + artifactHashParameter: params.artifactHashParameter.logicalId, + }; + + this.node.addMetadata(cxapi.ASSET_METADATA, metadata); + } + + const bucketName = params.bucketNameParameter.valueAsString; + + // key is prefix|postfix + const encodedKey = params.objectKeyParameter.valueAsString; + + const s3Prefix = Fn.select(0, Fn.split(cxapi.ASSET_PREFIX_SEPARATOR, encodedKey)); + const s3Filename = Fn.select(1, Fn.split(cxapi.ASSET_PREFIX_SEPARATOR, encodedKey)); + const objectKey = `${s3Prefix}${s3Filename}`; + + const s3Url = `https://s3.${this.region}.${this.urlSuffix}/${bucketName}/${objectKey}`; + + return { bucketName, objectKey, s3Url }; + } + + public addDockerImageAsset(asset: DockerImageAssetSource): DockerImageAssetLocation { + if (this.parentStack) { + return this.parentStack.addDockerImageAsset(asset); + } + + let params = this.assetParameters.node.tryFindChild(asset.sourceHash) as DockerImageAssetParameters; + if (!params) { + params = new DockerImageAssetParameters(this.assetParameters, asset.sourceHash); + + const metadata: cxapi.ContainerImageAssetMetadataEntry = { + id: this.node.uniqueId, + packaging: 'container-image', + path: asset.directoryName, + sourceHash: asset.sourceHash, + imageNameParameter: params.imageNameParameter.logicalId, + repositoryName: asset.repositoryName, + buildArgs: asset.dockerBuildArgs, + target: asset.dockerBuildTarget + }; + + this.node.addMetadata(cxapi.ASSET_METADATA, metadata); + } + + // Parse repository name and tag from the parameter (@sha256:) + // Example: cdk/cdkexampleimageb2d7f504@sha256:72c4f956379a43b5623d529ddd969f6826dde944d6221f445ff3e7add9875500 + const components = Fn.split('@sha256:', params.imageNameParameter.valueAsString); + const repositoryName = Fn.select(0, components).toString(); + const imageSha = Fn.select(1, components).toString(); + const imageUri = `${this.account}.dkr.ecr.${this.region}.${this.urlSuffix}/${repositoryName}@sha256:${imageSha}`; + + return { + imageUri, repositoryName + }; + } + /** * Returns the naming scheme used to allocate logical IDs. By default, uses * the `HashedAddressingScheme` but this method can be overridden to customize @@ -495,18 +610,41 @@ export class Stack extends Construct implements ITaggable { * Find all dependencies as well and add the appropriate DependsOn fields. */ protected prepare() { - // References - for (const ref of this.node.references) { - if (CfnReference.isCfnReference(ref.reference)) { - ref.reference.consumeFromStack(this, ref.source); + const tokens = this.findTokens(); + + // References (originating from this stack) + for (const reference of tokens) { + + // skip if this is not a CfnReference + if (!CfnReference.isCfnReference(reference)) { + continue; + } + + const targetStack = Stack.of(reference.target); + + // skip if this is not a cross-stack reference + if (targetStack === this) { + continue; + } + + // determine which stack should create the cross reference + const factory = this.determineCrossReferenceFactory(targetStack); + + // if one side is a nested stack (has "parentStack"), we let it create the reference + // since it has more knowledge about the world. + const consumedValue = factory.prepareCrossReference(this, reference); + + // if the reference has already been assigned a value for the consuming stack, carry on. + if (!reference.hasValueForStack(this)) { + reference.assignValueForStack(this, consumedValue); } } // Resource dependencies for (const dependency of this.node.dependencies) { const theirStack = Stack.of(dependency.target); - if (theirStack !== undefined && theirStack !== this) { - this.addDependency(theirStack); + if (theirStack !== undefined && theirStack !== this && Stack.of(dependency.source) === this) { + this.addDependency(theirStack, `"${dependency.source.node.path}" depends on "${dependency.target.node.path}"`); } else { for (const target of findResources([dependency.target])) { for (const source of findResources([dependency.source])) { @@ -519,6 +657,22 @@ export class Stack extends Construct implements ITaggable { if (this.tags.hasTags()) { this.node.addMetadata(cxapi.STACK_TAGS_METADATA_KEY, this.tags.renderTags()); } + + if (this.parentStack) { + // add the nested stack template as an asset + const cfn = JSON.stringify(this._toCloudFormation()); + const templateHash = crypto.createHash('sha256').update(cfn).digest('hex'); + const parent = this.parentStack; + const templateLocation = parent.addFileAsset({ + packaging: FileAssetPackaging.FILE, + sourceHash: templateHash, + fileName: this.templateFile + }); + + // if bucketName/objectKey are cfn parameters from a stack other than the parent stack, they will + // be resolved as cross-stack references like any other (see "multi" tests). + this._templateUrl = `https://s3.${parent.region}.${parent.urlSuffix}/${templateLocation.bucketName}/${templateLocation.objectKey}`; + } } protected synthesize(session: ISynthesisSession): void { @@ -526,7 +680,13 @@ export class Stack extends Construct implements ITaggable { // write the CloudFormation template as a JSON file const outPath = path.join(builder.outdir, this.templateFile); - fs.writeFileSync(outPath, JSON.stringify(this._toCloudFormation(), undefined, 2)); + const text = JSON.stringify(this._toCloudFormation(), undefined, 2); + fs.writeFileSync(outPath, text); + + // if this is a nested stack, do not emit it as a cloud assembly artifact (it will be registered as an s3 asset instead) + if (this.nested) { + return; + } const deps = this.dependencies.map(s => s.stackName); const meta = this.collectMetadata(); @@ -589,6 +749,50 @@ export class Stack extends Construct implements ITaggable { return ret; } + /** + * Exports a resolvable value for use in another stack. + * + * @returns a token that can be used to reference the value from the producing stack. + */ + protected prepareCrossReference(sourceStack: Stack, reference: Reference): IResolvable { + const targetStack = Stack.of(reference.target); + + // Ensure a singleton "Exports" scoping Construct + // This mostly exists to trigger LogicalID munging, which would be + // disabled if we parented constructs directly under Stack. + // Also it nicely prevents likely construct name clashes + const exportsScope = targetStack.getCreateExportsScope(); + + // Ensure a singleton CfnOutput for this value + const resolved = targetStack.resolve(reference); + const id = 'Output' + JSON.stringify(resolved); + const exportName = targetStack.generateExportName(exportsScope, id); + const output = exportsScope.node.tryFindChild(id) as CfnOutput; + if (!output) { + new CfnOutput(exportsScope, id, { value: Token.asString(reference), exportName }); + } + + // add a dependency on the producing stack - it has to be deployed before this stack can consume the exported value + // if the producing stack is a nested stack (i.e. has a parent), the dependency is taken on the parent. + const producerDependency = targetStack.parentStack ? targetStack.parentStack : targetStack; + const consumerDependency = sourceStack.parentStack ? sourceStack.parentStack : sourceStack; + consumerDependency.addDependency(producerDependency, `${sourceStack.node.path} -> ${reference.target.node.path}.${reference.displayName}`); + + // We want to return an actual FnImportValue Token here, but Fn.importValue() returns a 'string', + // so construct one in-place. + return new Intrinsic({ 'Fn::ImportValue': exportName }); + } + + private getCreateExportsScope() { + const exportsName = 'Exports'; + let stackExports = this.node.tryFindChild(exportsName) as Construct; + if (stackExports === undefined) { + stackExports = new Construct(this, exportsName); + } + + return stackExports; + } + /** * Determine the various stack environment attributes. * @@ -672,6 +876,73 @@ export class Stack extends Construct implements ITaggable { return makeUniqueId(ids); } + + private generateExportName(stackExports: Construct, id: string) { + const stack = Stack.of(stackExports); + const components = [...stackExports.node.scopes.slice(2).map(c => c.node.id), id]; + const prefix = stack.stackName ? stack.stackName + ':' : ''; + const exportName = prefix + makeUniqueId(components); + return exportName; + } + + private get assetParameters() { + if (!this._assetParameters) { + this._assetParameters = new Construct(this, 'AssetParameters'); + } + return this._assetParameters; + } + + private determineCrossReferenceFactory(target: Stack) { + // unsupported: stacks from different apps + if (target.node.root !== this.node.root) { + throw new Error( + `Cannot reference across apps. ` + + `Consuming and producing stacks must be defined within the same CDK app.`); + } + + // unsupported: stacks are not in the same environment + if (target.environment !== this.environment) { + throw new Error( + `Stack "${this.node.path}" cannot consume a cross reference from stack "${target.node.path}". ` + + `Cross stack references are only supported for stacks deployed to the same environment or between nested stacks and their parent stack`); + } + + // if one of the stacks is a nested stack, go ahead and give it the right to make the cross reference + if (target.nested) { return target; } + if (this.nested) { return this; } + + // both stacks are top-level (non-nested), the taret (producing stack) gets to make the reference + return target; + } + + /** + * Returns all the tokens used within the scope of the current stack. + */ + private findTokens() { + const tokens = new Array(); + + for (const element of cfnElements(this)) { + try { + tokens.push(...findTokens(element, () => element._toCloudFormation())); + } catch (e) { + // Note: it might be that the properties of the CFN object aren't valid. + // This will usually be preventatively caught in a construct's validate() + // and turned into a nicely descriptive error, but we're running prepare() + // before validate(). Swallow errors that occur because the CFN layer + // doesn't validate completely. + // + // This does make the assumption that the error will not be rectified, + // but the error will be thrown later on anyway. If the error doesn't + // get thrown down the line, we may miss references. + if (e.type === 'CfnSynthesisError') { + continue; + } + + throw e; + } + } + return tokens; + } } function merge(template: any, part: any) { @@ -753,9 +1024,14 @@ function cfnElements(node: IConstruct, into: CfnElement[] = []): CfnElement[] { import { Arn, ArnComponents } from './arn'; import { CfnElement } from './cfn-element'; import { Fn } from './cfn-fn'; +import { CfnOutput } from './cfn-output'; import { Aws, ScopedAws } from './cfn-pseudo'; import { CfnResource, TagType } from './cfn-resource'; +import { Lazy } from './lazy'; import { CfnReference } from './private/cfn-reference'; +import { Intrinsic } from './private/intrinsic'; +import { Reference } from './reference'; +import { IResolvable } from './resolvable'; import { ITaggable, TagManager } from './tag-manager'; import { Token } from './token'; diff --git a/packages/@aws-cdk/core/test/test.stack.ts b/packages/@aws-cdk/core/test/test.stack.ts index ca41ef04a07b9..da16e3c7d18c2 100644 --- a/packages/@aws-cdk/core/test/test.stack.ts +++ b/packages/@aws-cdk/core/test/test.stack.ts @@ -1,6 +1,8 @@ import { Test } from 'nodeunit'; import { App, CfnCondition, CfnInclude, CfnOutput, CfnParameter, CfnResource, Construct, ConstructNode, Lazy, ScopedAws, Stack } from '../lib'; +import { validateString } from '../lib'; import { Intrinsic } from '../lib/private/intrinsic'; +import { PostResolveToken } from '../lib/util'; import { toCloudFormation } from './util'; export = { @@ -284,6 +286,29 @@ export = { test.done(); }, + 'CfnSynthesisError is ignored when preparing cross references'(test: Test) { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'my-stack'); + + // WHEN + class CfnTest extends CfnResource { + public _toCloudFormation() { + return new PostResolveToken({ + xoo: 1234 + }, props => { + validateString(props).assertSuccess(); + }); + } + } + + new CfnTest(stack, 'MyThing', { type: 'AWS::Type' }); + + // THEN + ConstructNode.prepare(stack.node); + test.done(); + }, + 'Stacks can be children of other stacks (substack) and they will be synthesized separately'(test: Test) { // GIVEN const app = new App(); @@ -431,7 +456,7 @@ export = { test.throws(() => { ConstructNode.prepare(app.node); - }, /Can only reference cross stacks in the same region and account/); + }, /Stack "Stack2" cannot consume a cross reference from stack "Stack1"/); test.done(); }, diff --git a/packages/@aws-cdk/custom-resources/test/integ.aws-custom-resource.expected.json b/packages/@aws-cdk/custom-resources/test/integ.aws-custom-resource.expected.json index 8d9ce3f22c6fe..211281f25bfa5 100644 --- a/packages/@aws-cdk/custom-resources/test/integ.aws-custom-resource.expected.json +++ b/packages/@aws-cdk/custom-resources/test/integ.aws-custom-resource.expected.json @@ -39,8 +39,8 @@ } } }, - "DeletionPolicy": "Delete", - "UpdateReplacePolicy": "Delete" + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" }, "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2": { "Type": "AWS::IAM::Role", @@ -109,7 +109,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AWS679f53fac002430cb0da5b7982bd2287CodeS3BucketF55839B6" + "Ref": "AssetParameters44223b510152d51e381791e0347041da535987f7d6f1703b343f9c06f6075059S3Bucket2360388A" }, "S3Key": { "Fn::Join": [ @@ -122,7 +122,7 @@ "Fn::Split": [ "||", { - "Ref": "AWS679f53fac002430cb0da5b7982bd2287CodeS3VersionKey3C45B02F" + "Ref": "AssetParameters44223b510152d51e381791e0347041da535987f7d6f1703b343f9c06f6075059S3VersionKey012A5944" } ] } @@ -135,7 +135,7 @@ "Fn::Split": [ "||", { - "Ref": "AWS679f53fac002430cb0da5b7982bd2287CodeS3VersionKey3C45B02F" + "Ref": "AssetParameters44223b510152d51e381791e0347041da535987f7d6f1703b343f9c06f6075059S3VersionKey012A5944" } ] } @@ -182,8 +182,8 @@ "DependsOn": [ "TopicBFC7AF6E" ], - "DeletionPolicy": "Delete", - "UpdateReplacePolicy": "Delete" + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" }, "DummyParameter53662B67": { "Type": "AWS::SSM::Parameter", @@ -224,22 +224,22 @@ "physicalResourceIdPath": "Parameter.ARN" } }, - "DeletionPolicy": "Delete", - "UpdateReplacePolicy": "Delete" + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" } }, "Parameters": { - "AWS679f53fac002430cb0da5b7982bd2287CodeS3BucketF55839B6": { + "AssetParameters44223b510152d51e381791e0347041da535987f7d6f1703b343f9c06f6075059S3Bucket2360388A": { "Type": "String", - "Description": "S3 bucket for asset \"aws-cdk-sdk-js/AWS679f53fac002430cb0da5b7982bd2287/Code\"" + "Description": "S3 bucket for asset \"44223b510152d51e381791e0347041da535987f7d6f1703b343f9c06f6075059\"" }, - "AWS679f53fac002430cb0da5b7982bd2287CodeS3VersionKey3C45B02F": { + "AssetParameters44223b510152d51e381791e0347041da535987f7d6f1703b343f9c06f6075059S3VersionKey012A5944": { "Type": "String", - "Description": "S3 key for asset version \"aws-cdk-sdk-js/AWS679f53fac002430cb0da5b7982bd2287/Code\"" + "Description": "S3 key for asset version \"44223b510152d51e381791e0347041da535987f7d6f1703b343f9c06f6075059\"" }, - "AWS679f53fac002430cb0da5b7982bd2287CodeArtifactHash49FACC2E": { + "AssetParameters44223b510152d51e381791e0347041da535987f7d6f1703b343f9c06f6075059ArtifactHashE1243C2C": { "Type": "String", - "Description": "Artifact hash for asset \"aws-cdk-sdk-js/AWS679f53fac002430cb0da5b7982bd2287/Code\"" + "Description": "Artifact hash for asset \"44223b510152d51e381791e0347041da535987f7d6f1703b343f9c06f6075059\"" } }, "Outputs": { @@ -268,4 +268,4 @@ } } } -} +} \ No newline at end of file diff --git a/packages/decdk/test/__snapshots__/synth.test.js.snap b/packages/decdk/test/__snapshots__/synth.test.js.snap index 53668cca3364e..17ba586b28fdf 100644 --- a/packages/decdk/test/__snapshots__/synth.test.js.snap +++ b/packages/decdk/test/__snapshots__/synth.test.js.snap @@ -31,16 +31,16 @@ Object { }, }, "Parameters": Object { - "HelloLambdaCodeArtifactHashBB927E34": Object { - "Description": "Artifact hash for asset \\"apigw/HelloLambda/Code\\"", + "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cArtifactHash47CFB54E": Object { + "Description": "Artifact hash for asset \\"a6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30c\\"", "Type": "String", }, - "HelloLambdaCodeS3BucketB83F7900": Object { - "Description": "S3 bucket for asset \\"apigw/HelloLambda/Code\\"", + "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3Bucket0D05C9AA": Object { + "Description": "S3 bucket for asset \\"a6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30c\\"", "Type": "String", }, - "HelloLambdaCodeS3VersionKey2936C385": Object { - "Description": "S3 key for asset version \\"apigw/HelloLambda/Code\\"", + "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3VersionKeyDC0BC7B3": Object { + "Description": "S3 key for asset version \\"a6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30c\\"", "Type": "String", }, }, @@ -173,7 +173,7 @@ Object { "Properties": Object { "Code": Object { "S3Bucket": Object { - "Ref": "HelloLambdaCodeS3BucketB83F7900", + "Ref": "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3Bucket0D05C9AA", }, "S3Key": Object { "Fn::Join": Array [ @@ -186,7 +186,7 @@ Object { "Fn::Split": Array [ "||", Object { - "Ref": "HelloLambdaCodeS3VersionKey2936C385", + "Ref": "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3VersionKeyDC0BC7B3", }, ], }, @@ -199,7 +199,7 @@ Object { "Fn::Split": Array [ "||", Object { - "Ref": "HelloLambdaCodeS3VersionKey2936C385", + "Ref": "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3VersionKeyDC0BC7B3", }, ], }, @@ -927,16 +927,16 @@ Object { }, }, "Parameters": Object { - "HelloWorldFunctionCodeArtifactHashEF4E01C5": Object { - "Description": "Artifact hash for asset \\"lambda-events/HelloWorldFunction/Code\\"", + "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cArtifactHash47CFB54E": Object { + "Description": "Artifact hash for asset \\"a6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30c\\"", "Type": "String", }, - "HelloWorldFunctionCodeS3BucketF87BE172": Object { - "Description": "S3 bucket for asset \\"lambda-events/HelloWorldFunction/Code\\"", + "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3Bucket0D05C9AA": Object { + "Description": "S3 bucket for asset \\"a6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30c\\"", "Type": "String", }, - "HelloWorldFunctionCodeS3VersionKeyF84D6469": Object { - "Description": "S3 key for asset version \\"lambda-events/HelloWorldFunction/Code\\"", + "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3VersionKeyDC0BC7B3": Object { + "Description": "S3 key for asset version \\"a6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30c\\"", "Type": "String", }, }, @@ -965,7 +965,7 @@ Object { "Properties": Object { "Code": Object { "S3Bucket": Object { - "Ref": "HelloWorldFunctionCodeS3BucketF87BE172", + "Ref": "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3Bucket0D05C9AA", }, "S3Key": Object { "Fn::Join": Array [ @@ -978,7 +978,7 @@ Object { "Fn::Split": Array [ "||", Object { - "Ref": "HelloWorldFunctionCodeS3VersionKeyF84D6469", + "Ref": "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3VersionKeyDC0BC7B3", }, ], }, @@ -991,7 +991,7 @@ Object { "Fn::Split": Array [ "||", Object { - "Ref": "HelloWorldFunctionCodeS3VersionKeyF84D6469", + "Ref": "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3VersionKeyDC0BC7B3", }, ], }, @@ -1478,16 +1478,16 @@ Object { exports[`lambda-topic.json: lambda-topic 1`] = ` Object { "Parameters": Object { - "LambdaCodeArtifactHash305E64BB": Object { - "Description": "Artifact hash for asset \\"lambda-topic/Lambda/Code\\"", + "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cArtifactHash47CFB54E": Object { + "Description": "Artifact hash for asset \\"a6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30c\\"", "Type": "String", }, - "LambdaCodeS3Bucket65766E44": Object { - "Description": "S3 bucket for asset \\"lambda-topic/Lambda/Code\\"", + "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3Bucket0D05C9AA": Object { + "Description": "S3 bucket for asset \\"a6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30c\\"", "Type": "String", }, - "LambdaCodeS3VersionKey10FC11BE": Object { - "Description": "S3 key for asset version \\"lambda-topic/Lambda/Code\\"", + "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3VersionKeyDC0BC7B3": Object { + "Description": "S3 key for asset version \\"a6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30c\\"", "Type": "String", }, }, @@ -1515,7 +1515,7 @@ Object { "Properties": Object { "Code": Object { "S3Bucket": Object { - "Ref": "LambdaCodeS3Bucket65766E44", + "Ref": "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3Bucket0D05C9AA", }, "S3Key": Object { "Fn::Join": Array [ @@ -1528,7 +1528,7 @@ Object { "Fn::Split": Array [ "||", Object { - "Ref": "LambdaCodeS3VersionKey10FC11BE", + "Ref": "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3VersionKeyDC0BC7B3", }, ], }, @@ -1541,7 +1541,7 @@ Object { "Fn::Split": Array [ "||", Object { - "Ref": "LambdaCodeS3VersionKey10FC11BE", + "Ref": "AssetParametersa6017def6c75b1a9adc95dfc1b2d7bb3dc0dad8ef9e71da67cc1b7c3826fe30cS3VersionKeyDC0BC7B3", }, ], },