From 8b97bdfc759e169bd276a8690a6cac055d5ed755 Mon Sep 17 00:00:00 2001 From: Rico Hermans Date: Mon, 5 Jun 2023 14:30:14 +0200 Subject: [PATCH 1/2] fix(cli): deployment gets stuck deploying stacks with shared assets (#25846) In #25536 we introduced the new work graph orchestration for the CLI which had a bug when the same asset was shared by multiple stacks. #25719 was an attempt at fixing this bug, and while it fixed it for some cases it didn't fix all of them. The issue is that for cosmetic reasons, asset publishing steps inherit the dependencies of the stacks they are publishing for (so that the printed output of asset publishing doesn't interfere with the in-place updated progress bar of stack deployment and make it lose track of the terminal state). There were two bugs: * These extra dependencies could cause cycles (#25719 addressed direct cycles, where `Publish <--> Stack`, but not indirect cycles, where `Publish -> StackA -> StackB -> Publish`). * Publishing nodes would overwrite other nodes with the same ID, and the dependency set they would end up with would be somewhat nondeterministic, making this problem hard to isolate. Fixes #25806. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/aws-cdk/lib/cdk-toolkit.ts | 2 +- .../aws-cdk/lib/util/work-graph-builder.ts | 79 ++++++++------ packages/aws-cdk/lib/util/work-graph.ts | 100 +++++++++++++----- .../aws-cdk/test/work-graph-builder.test.ts | 74 ++++++++----- 4 files changed, 165 insertions(+), 90 deletions(-) diff --git a/packages/aws-cdk/lib/cdk-toolkit.ts b/packages/aws-cdk/lib/cdk-toolkit.ts index 7cf0ccc6b42f1..70d7650f58f7b 100644 --- a/packages/aws-cdk/lib/cdk-toolkit.ts +++ b/packages/aws-cdk/lib/cdk-toolkit.ts @@ -359,7 +359,7 @@ export class CdkToolkit { const graphConcurrency: Concurrency = { 'stack': concurrency, 'asset-build': 1, // This will be CPU-bound/memory bound, mostly matters for Docker builds - 'asset-publish': options.assetParallelism ? 8 : 1, // This will be I/O-bound, 8 in parallel seems reasonable + 'asset-publish': (options.assetParallelism ?? true) ? 8 : 1, // This will be I/O-bound, 8 in parallel seems reasonable }; await workGraph.doParallel(graphConcurrency, { diff --git a/packages/aws-cdk/lib/util/work-graph-builder.ts b/packages/aws-cdk/lib/util/work-graph-builder.ts index 7c5a0d0ff3762..979c68e4d915e 100644 --- a/packages/aws-cdk/lib/util/work-graph-builder.ts +++ b/packages/aws-cdk/lib/util/work-graph-builder.ts @@ -50,8 +50,8 @@ export class WorkGraphBuilder { id: buildId, dependencies: new Set([ ...this.getDepIds(assetArtifact.dependencies), - // If we disable prebuild, then assets inherit dependencies from their parent stack - ...!this.prebuildAssets ? this.getDepIds(parentStack.dependencies) : [], + // If we disable prebuild, then assets inherit (stack) dependencies from their parent stack + ...!this.prebuildAssets ? this.getDepIds(onlyStacks(parentStack.dependencies)) : [], ]), parentStack, assetManifestArtifact: assetArtifact, @@ -66,27 +66,35 @@ export class WorkGraphBuilder { // Always add the publish const publishNodeId = `${this.idPrefix}${asset.id}-publish`; - this.graph.addNodes({ - type: 'asset-publish', - id: publishNodeId, - dependencies: new Set([ - buildId, - // The asset publish step also depends on the stacks that the parent depends on. - // This is purely cosmetic: if we don't do this, the progress printing of asset publishing - // is going to interfere with the progress bar of the stack deployment. We could remove this - // for overall faster deployments if we ever have a better method of progress displaying. - // Note: this may introduce a cycle if one of the parent's dependencies is another stack that - // depends on this asset. To workaround this we remove these cycles once all nodes have - // been added to the graph. - ...this.getDepIds(parentStack.dependencies.filter(cxapi.CloudFormationStackArtifact.isCloudFormationStackArtifact)), - ]), - parentStack, - assetManifestArtifact: assetArtifact, - assetManifest, - asset, - deploymentState: DeploymentState.PENDING, - priority: WorkGraphBuilder.PRIORITIES['asset-publish'], - }); + + const publishNode = this.graph.tryGetNode(publishNodeId); + if (!publishNode) { + this.graph.addNodes({ + type: 'asset-publish', + id: publishNodeId, + dependencies: new Set([ + buildId, + ]), + parentStack, + assetManifestArtifact: assetArtifact, + assetManifest, + asset, + deploymentState: DeploymentState.PENDING, + priority: WorkGraphBuilder.PRIORITIES['asset-publish'], + }); + } + + for (const inheritedDep of this.getDepIds(onlyStacks(parentStack.dependencies))) { + // The asset publish step also depends on the stacks that the parent depends on. + // This is purely cosmetic: if we don't do this, the progress printing of asset publishing + // is going to interfere with the progress bar of the stack deployment. We could remove this + // for overall faster deployments if we ever have a better method of progress displaying. + // Note: this may introduce a cycle if one of the parent's dependencies is another stack that + // depends on this asset. To workaround this we remove these cycles once all nodes have + // been added to the graph. + this.graph.addDependency(publishNodeId, inheritedDep); + } + // This will work whether the stack node has been added yet or not this.graph.addDependency(`${this.idPrefix}${parentStack.id}`, publishNodeId); } @@ -137,20 +145,17 @@ export class WorkGraphBuilder { return ids; } + /** + * We may have accidentally introduced cycles in an attempt to make the messages printed to the + * console not interfere with each other too much. Remove them again. + */ private removeStackPublishCycles() { - const stacks = this.graph.nodesOfType('stack'); - for (const stack of stacks) { - for (const dep of stack.dependencies) { - const node = this.graph.nodes[dep]; - - if (!node || node.type !== 'asset-publish' || !node.dependencies.has(stack.id)) { - continue; + const publishSteps = this.graph.nodesOfType('asset-publish'); + for (const publishStep of publishSteps) { + for (const dep of publishStep.dependencies) { + if (this.graph.reachable(dep, publishStep.id)) { + publishStep.dependencies.delete(dep); } - - // Delete the dependency from the asset-publish onto the stack. - // The publish -> stack dependencies are purely cosmetic to prevent publish output - // from interfering with the progress bar of the stack deployment. - node.dependencies.delete(stack.id); } } } @@ -166,4 +171,8 @@ function stacksFromAssets(artifacts: cxapi.CloudArtifact[]) { } return ret; +} + +function onlyStacks(artifacts: cxapi.CloudArtifact[]) { + return artifacts.filter(cxapi.CloudFormationStackArtifact.isCloudFormationStackArtifact); } \ No newline at end of file diff --git a/packages/aws-cdk/lib/util/work-graph.ts b/packages/aws-cdk/lib/util/work-graph.ts index 4d565ade4be3b..81eab777c27d6 100644 --- a/packages/aws-cdk/lib/util/work-graph.ts +++ b/packages/aws-cdk/lib/util/work-graph.ts @@ -14,6 +14,10 @@ export class WorkGraph { public addNodes(...nodes: WorkNode[]) { for (const node of nodes) { + if (this.nodes[node.id]) { + throw new Error(`Duplicate use of node id: ${node.id}`); + } + const ld = this.lazyDependencies.get(node.id); if (ld) { for (const x of ld) { @@ -72,6 +76,10 @@ export class WorkGraph { lazyDeps.push(toId); } + public tryGetNode(id: string): WorkNode | undefined { + return this.nodes[id]; + } + public node(id: string) { const ret = this.nodes[id]; if (!ret) { @@ -198,9 +206,24 @@ export class WorkGraph { } public toString() { - return Object.entries(this.nodes).map(([id, node]) => - `${id} := ${node.deploymentState} ${node.type} ${node.dependencies.size > 0 ? `(${Array.from(node.dependencies)})` : ''}`.trim(), - ).join(', '); + return [ + 'digraph D {', + ...Object.entries(this.nodes).flatMap(([id, node]) => renderNode(id, node)), + '}', + ].join('\n'); + + function renderNode(id: string, node: WorkNode): string[] { + const ret = []; + if (node.deploymentState === DeploymentState.COMPLETED) { + ret.push(` "${id}" [style=filled,fillcolor=yellow];`); + } else { + ret.push(` "${id}";`); + } + for (const dep of node.dependencies) { + ret.push(` "${id}" -> "${dep}";`); + } + return ret; + } } /** @@ -244,35 +267,28 @@ export class WorkGraph { } private updateReadyPool() { - let activeCount = 0; - let pendingCount = 0; - for (const node of Object.values(this.nodes)) { - switch (node.deploymentState) { - case DeploymentState.DEPLOYING: - activeCount += 1; - break; - case DeploymentState.PENDING: - pendingCount += 1; - if (Array.from(node.dependencies).every((id) => this.node(id).deploymentState === DeploymentState.COMPLETED)) { - node.deploymentState = DeploymentState.QUEUED; - this.readyPool.push(node); - } - break; - } - } + const activeCount = Object.values(this.nodes).filter((x) => x.deploymentState === DeploymentState.DEPLOYING).length; + const pendingCount = Object.values(this.nodes).filter((x) => x.deploymentState === DeploymentState.PENDING).length; - for (let i = 0; i < this.readyPool.length; i++) { - const node = this.readyPool[i]; - if (node.deploymentState !== DeploymentState.QUEUED) { - this.readyPool.splice(i, 1); - } + const newlyReady = Object.values(this.nodes).filter((x) => + x.deploymentState === DeploymentState.PENDING && + Array.from(x.dependencies).every((id) => this.node(id).deploymentState === DeploymentState.COMPLETED)); + + // Add newly available nodes to the ready pool + for (const node of newlyReady) { + node.deploymentState = DeploymentState.QUEUED; + this.readyPool.push(node); } + // Remove nodes from the ready pool that have already started deploying + retainOnly(this.readyPool, (node) => node.deploymentState === DeploymentState.QUEUED); + // Sort by reverse priority this.readyPool.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0)); if (this.readyPool.length === 0 && activeCount === 0 && pendingCount > 0) { - throw new Error(`Unable to make progress anymore, dependency cycle between remaining artifacts: ${this.findCycle().join(' -> ')}`); + const cycle = this.findCycle() ?? ['No cycle found!']; + throw new Error(`Unable to make progress anymore, dependency cycle between remaining artifacts: ${cycle.join(' -> ')}`); } } @@ -289,14 +305,14 @@ export class WorkGraph { * * Not the fastest, but effective and should be rare */ - private findCycle(): string[] { + public findCycle(): string[] | undefined { const seen = new Set(); const self = this; for (const nodeId of Object.keys(this.nodes)) { const cycle = recurse(nodeId, [nodeId]); if (cycle) { return cycle; } } - return ['No cycle found!']; + return undefined; function recurse(nodeId: string, path: string[]): string[] | undefined { if (seen.has(nodeId)) { @@ -319,6 +335,32 @@ export class WorkGraph { } } } + + /** + * Whether the `end` node is reachable from the `start` node, following the dependency arrows + */ + public reachable(start: string, end: string): boolean { + const seen = new Set(); + const self = this; + return recurse(start); + + function recurse(current: string) { + if (seen.has(current)) { + return false; + } + seen.add(current); + + if (current === end) { + return true; + } + for (const dep of self.nodes[current].dependencies) { + if (recurse(dep)) { + return true; + } + } + return false; + } + } } export interface WorkGraphActions { @@ -334,3 +376,7 @@ function sum(xs: number[]) { } return ret; } + +function retainOnly(xs: A[], pred: (x: A) => boolean) { + xs.splice(0, xs.length, ...xs.filter(pred)); +} diff --git a/packages/aws-cdk/test/work-graph-builder.test.ts b/packages/aws-cdk/test/work-graph-builder.test.ts index 39e01cc77c911..e89d8e35e911d 100644 --- a/packages/aws-cdk/test/work-graph-builder.test.ts +++ b/packages/aws-cdk/test/work-graph-builder.test.ts @@ -109,7 +109,7 @@ test('dependencies on unselected artifacts are silently ignored', async () => { })); }); -test('assets with shared contents between dependant stacks', async () => { +describe('tests that use assets', () => { const files = { // Referencing an existing file on disk is important here. // It means these two assets will have the same AssetManifest @@ -121,36 +121,56 @@ test('assets with shared contents between dependant stacks', async () => { }, }, }; + const environment = 'aws://11111/us-east-1'; - addStack(rootBuilder, 'StackA', { - environment: 'aws://11111/us-east-1', - dependencies: ['StackA.assets'], - }); - addAssets(rootBuilder, 'StackA.assets', { files }); + test('assets with shared contents between dependant stacks', async () => { + addStack(rootBuilder, 'StackA', { + environment: 'aws://11111/us-east-1', + dependencies: ['StackA.assets'], + }); + addAssets(rootBuilder, 'StackA.assets', { files }); - addStack(rootBuilder, 'StackB', { - environment: 'aws://11111/us-east-1', - dependencies: ['StackB.assets', 'StackA'], + addStack(rootBuilder, 'StackB', { + environment: 'aws://11111/us-east-1', + dependencies: ['StackB.assets', 'StackA'], + }); + addAssets(rootBuilder, 'StackB.assets', { files }); + + const assembly = rootBuilder.buildAssembly(); + + const traversal: string[] = []; + const graph = new WorkGraphBuilder(true).build(assembly.artifacts); + await graph.doParallel(1, { + deployStack: async (node) => { traversal.push(node.id); }, + buildAsset: async (node) => { traversal.push(node.id); }, + publishAsset: async (node) => { traversal.push(node.id); }, + }); + + expect(traversal).toHaveLength(4); // 1 asset build, 1 asset publish, 2 stacks + expect(traversal).toEqual([ + 'work-graph-builder.test.js:D1-build', + 'work-graph-builder.test.js:D1-publish', + 'StackA', + 'StackB', + ]); }); - addAssets(rootBuilder, 'StackB.assets', { files }); - const assembly = rootBuilder.buildAssembly(); + test('a more complex way to make a cycle', async () => { + // A -> B -> C | A and C share an asset. The asset will have a dependency on B, that is not a *direct* reverse dependency, and will cause a cycle. + addStack(rootBuilder, 'StackA', { environment, dependencies: ['StackA.assets', 'StackB'] }); + addAssets(rootBuilder, 'StackA.assets', { files }); - const traversal: string[] = []; - const graph = new WorkGraphBuilder(true).build(assembly.artifacts); - await graph.doParallel(1, { - deployStack: async (node) => { traversal.push(node.id); }, - buildAsset: async (node) => { traversal.push(node.id); }, - publishAsset: async (node) => { traversal.push(node.id); }, - }); - - expect(traversal).toHaveLength(4); // 1 asset build, 1 asset publish, 2 stacks - expect(traversal).toEqual([ - 'work-graph-builder.test.js:D1-build', - 'work-graph-builder.test.js:D1-publish', - 'StackA', - 'StackB', - ]); + addStack(rootBuilder, 'StackB', { environment, dependencies: ['StackC'] }); + + addStack(rootBuilder, 'StackC', { environment, dependencies: ['StackC.assets'] }); + addAssets(rootBuilder, 'StackC.assets', { files }); + + const assembly = rootBuilder.buildAssembly(); + const graph = new WorkGraphBuilder(true).build(assembly.artifacts); + + // THEN + expect(graph.findCycle()).toBeUndefined(); + }); }); /** @@ -230,4 +250,4 @@ function assertableNode(x: A) { ...x, dependencies: Array.from(x.dependencies), }; -} \ No newline at end of file +} From 437345e2ca72e67714334f4b9cb7da8f23c4a970 Mon Sep 17 00:00:00 2001 From: Tobias Jakubowitz Date: Mon, 5 Jun 2023 15:03:55 +0200 Subject: [PATCH 2/2] feat(cloud9): support setting automaticStopTimeMinutes (#25593) Add parameter automaticStopTimeMinutes to specify the number of minutes until the running instance is shut down after the environment was last used. Closes #25592. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-cloud9-alpha/README.md | 14 + .../aws-cloud9-alpha/lib/environment.ts | 11 + .../rosetta/default.ts-fixture | 2 +- .../test/cloud9.environment.test.ts | 14 + .../C9automaticStopStack.assets.json | 19 + .../C9automaticStopStack.template.json | 432 ++++++++++ .../integ.automatic-stop.js.snapshot/cdk.out | 1 + ...efaultTestDeployAssertBBFEEF89.assets.json | 19 + ...aultTestDeployAssertBBFEEF89.template.json | 36 + .../integ.json | 12 + .../manifest.json | 249 ++++++ .../tree.json | 742 ++++++++++++++++++ .../test/integ.automatic-stop.ts | 35 + 13 files changed, 1585 insertions(+), 1 deletion(-) create mode 100644 packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/C9automaticStopStack.assets.json create mode 100644 packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/C9automaticStopStack.template.json create mode 100644 packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/cloud9automaticstoptimemintuesDefaultTestDeployAssertBBFEEF89.assets.json create mode 100644 packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/cloud9automaticstoptimemintuesDefaultTestDeployAssertBBFEEF89.template.json create mode 100644 packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/integ.json create mode 100644 packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/tree.json create mode 100644 packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.ts diff --git a/packages/@aws-cdk/aws-cloud9-alpha/README.md b/packages/@aws-cdk/aws-cloud9-alpha/README.md index 984af19f0426b..497b932c101e4 100644 --- a/packages/@aws-cdk/aws-cloud9-alpha/README.md +++ b/packages/@aws-cdk/aws-cloud9-alpha/README.md @@ -134,3 +134,17 @@ new cloud9.Ec2Environment(this, 'C9Env', { owner: cloud9.Owner.user(user) }) ``` + +## Auto-Hibernation + +A Cloud9 environemnt can automatically start and stop the associated EC2 instance to reduce costs. + +Use `automaticStop` to specify the number of minutes until the running instance is shut down after the environment was last used. + +```ts +const defaultVpc = ec2.Vpc.fromLookup(this, 'DefaultVPC', { isDefault: true }); +new cloud9.Ec2Environment(this, 'Cloud9Env2', { + vpc: defaultVpc, + imageId: cloud9.ImageId.AMAZON_LINUX_2, + automaticStop: Duration.minutes(30), +}); diff --git a/packages/@aws-cdk/aws-cloud9-alpha/lib/environment.ts b/packages/@aws-cdk/aws-cloud9-alpha/lib/environment.ts index 45afc095f20ff..325dd2374102e 100644 --- a/packages/@aws-cdk/aws-cloud9-alpha/lib/environment.ts +++ b/packages/@aws-cdk/aws-cloud9-alpha/lib/environment.ts @@ -124,6 +124,16 @@ export interface Ec2EnvironmentProps { * */ readonly imageId: ImageId + + /** + * The number of minutes until the running instance is shut down after the + * environment was last used. + * + * Setting a value of 0 means the instance will never be automatically shut down." + * + * @default - The instance will not be shut down automatically. + */ + readonly automaticStop?: cdk.Duration } /** @@ -200,6 +210,7 @@ export class Ec2Environment extends cdk.Resource implements IEc2Environment { })) : undefined, connectionType: props.connectionType ?? ConnectionType.CONNECT_SSH, imageId: props.imageId, + automaticStopTimeMinutes: props.automaticStop?.toMinutes(), }); this.environmentId = c9env.ref; this.ec2EnvironmentArn = c9env.getAtt('Arn').toString(); diff --git a/packages/@aws-cdk/aws-cloud9-alpha/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-cloud9-alpha/rosetta/default.ts-fixture index d5f0a7c1464c8..03ad4fbcf20fb 100644 --- a/packages/@aws-cdk/aws-cloud9-alpha/rosetta/default.ts-fixture +++ b/packages/@aws-cdk/aws-cloud9-alpha/rosetta/default.ts-fixture @@ -1,5 +1,5 @@ // Fixture with packages imported, but nothing else -import { CfnOutput, Stack } from 'aws-cdk-lib'; +import { CfnOutput, Duration, Stack } from 'aws-cdk-lib'; import { Construct } from 'constructs'; import * as cloud9 from '@aws-cdk/aws-cloud9-alpha'; import * as ec2 from 'aws-cdk-lib/aws-ec2'; diff --git a/packages/@aws-cdk/aws-cloud9-alpha/test/cloud9.environment.test.ts b/packages/@aws-cdk/aws-cloud9-alpha/test/cloud9.environment.test.ts index 465c8a947ab35..41879129e907e 100644 --- a/packages/@aws-cdk/aws-cloud9-alpha/test/cloud9.environment.test.ts +++ b/packages/@aws-cdk/aws-cloud9-alpha/test/cloud9.environment.test.ts @@ -145,6 +145,20 @@ test('environment owner can be account root', () => { }); }); +test('can set automaticStop', () => { + // WHEN + const automaticStop = cdk.Duration.minutes(30); + new cloud9.Ec2Environment(stack, 'C9Env', { + vpc, + imageId: cloud9.ImageId.AMAZON_LINUX_2, + automaticStop, + }); + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Cloud9::EnvironmentEC2', { + AutomaticStopTimeMinutes: automaticStop.toMinutes(), + }); +}); + test.each([ [ConnectionType.CONNECT_SSH, 'CONNECT_SSH'], [ConnectionType.CONNECT_SSM, 'CONNECT_SSM'], diff --git a/packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/C9automaticStopStack.assets.json b/packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/C9automaticStopStack.assets.json new file mode 100644 index 0000000000000..923d6eab7313c --- /dev/null +++ b/packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/C9automaticStopStack.assets.json @@ -0,0 +1,19 @@ +{ + "version": "32.0.0", + "files": { + "d302c7f90912c93bef6f8fda089adb33206a67ff3bdb6ce885b1f90a7fac7e25": { + "source": { + "path": "C9automaticStopStack.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "d302c7f90912c93bef6f8fda089adb33206a67ff3bdb6ce885b1f90a7fac7e25.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/C9automaticStopStack.template.json b/packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/C9automaticStopStack.template.json new file mode 100644 index 0000000000000..be10fee8dffe0 --- /dev/null +++ b/packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/C9automaticStopStack.template.json @@ -0,0 +1,432 @@ +{ + "Resources": { + "VPCB9E5F0B4": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "C9automaticStopStack/VPC" + } + ] + } + }, + "VPCPublicSubnet1SubnetB4246D30": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "AvailabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.0.0/18", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "C9automaticStopStack/VPC/PublicSubnet1" + } + ] + } + }, + "VPCPublicSubnet1RouteTableFEE4B781": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "C9automaticStopStack/VPC/PublicSubnet1" + } + ] + } + }, + "VPCPublicSubnet1RouteTableAssociation0B0896DC": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet1RouteTableFEE4B781" + }, + "SubnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + } + } + }, + "VPCPublicSubnet1DefaultRoute91CEF279": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet1RouteTableFEE4B781" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VPCIGWB7E252D3" + } + }, + "DependsOn": [ + "VPCVPCGW99B986DC" + ] + }, + "VPCPublicSubnet1EIP6AD938E8": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "C9automaticStopStack/VPC/PublicSubnet1" + } + ] + } + }, + "VPCPublicSubnet1NATGatewayE0556630": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "SubnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + }, + "AllocationId": { + "Fn::GetAtt": [ + "VPCPublicSubnet1EIP6AD938E8", + "AllocationId" + ] + }, + "Tags": [ + { + "Key": "Name", + "Value": "C9automaticStopStack/VPC/PublicSubnet1" + } + ] + }, + "DependsOn": [ + "VPCPublicSubnet1DefaultRoute91CEF279", + "VPCPublicSubnet1RouteTableAssociation0B0896DC" + ] + }, + "VPCPublicSubnet2Subnet74179F39": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "AvailabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.64.0/18", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "C9automaticStopStack/VPC/PublicSubnet2" + } + ] + } + }, + "VPCPublicSubnet2RouteTable6F1A15F1": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "C9automaticStopStack/VPC/PublicSubnet2" + } + ] + } + }, + "VPCPublicSubnet2RouteTableAssociation5A808732": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet2RouteTable6F1A15F1" + }, + "SubnetId": { + "Ref": "VPCPublicSubnet2Subnet74179F39" + } + } + }, + "VPCPublicSubnet2DefaultRouteB7481BBA": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet2RouteTable6F1A15F1" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VPCIGWB7E252D3" + } + }, + "DependsOn": [ + "VPCVPCGW99B986DC" + ] + }, + "VPCPrivateSubnet1Subnet8BCA10E0": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "AvailabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.128.0/18", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "C9automaticStopStack/VPC/PrivateSubnet1" + } + ] + } + }, + "VPCPrivateSubnet1RouteTableBE8A6027": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "C9automaticStopStack/VPC/PrivateSubnet1" + } + ] + } + }, + "VPCPrivateSubnet1RouteTableAssociation347902D1": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet1RouteTableBE8A6027" + }, + "SubnetId": { + "Ref": "VPCPrivateSubnet1Subnet8BCA10E0" + } + } + }, + "VPCPrivateSubnet1DefaultRouteAE1D6490": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet1RouteTableBE8A6027" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VPCPublicSubnet1NATGatewayE0556630" + } + } + }, + "VPCPrivateSubnet2SubnetCFCDAA7A": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "AvailabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.192.0/18", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "C9automaticStopStack/VPC/PrivateSubnet2" + } + ] + } + }, + "VPCPrivateSubnet2RouteTable0A19E10E": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "C9automaticStopStack/VPC/PrivateSubnet2" + } + ] + } + }, + "VPCPrivateSubnet2RouteTableAssociation0C73D413": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet2RouteTable0A19E10E" + }, + "SubnetId": { + "Ref": "VPCPrivateSubnet2SubnetCFCDAA7A" + } + } + }, + "VPCPrivateSubnet2DefaultRouteF4F5CFD2": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet2RouteTable0A19E10E" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VPCPublicSubnet1NATGatewayE0556630" + } + } + }, + "VPCIGWB7E252D3": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "C9automaticStopStack/VPC" + } + ] + } + }, + "VPCVPCGW99B986DC": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "InternetGatewayId": { + "Ref": "VPCIGWB7E252D3" + } + } + }, + "C9EnvF05FC3BE": { + "Type": "AWS::Cloud9::EnvironmentEC2", + "Properties": { + "InstanceType": "t2.micro", + "AutomaticStopTimeMinutes": 30, + "ConnectionType": "CONNECT_SSH", + "ImageId": "amazonlinux-2-x86_64", + "SubnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + } + } + } + }, + "Outputs": { + "URL": { + "Value": { + "Fn::Join": [ + "", + [ + "https://", + { + "Ref": "AWS::Region" + }, + ".console.aws.amazon.com/cloud9/ide/", + { + "Ref": "C9EnvF05FC3BE" + } + ] + ] + } + }, + "ARN": { + "Value": { + "Fn::GetAtt": [ + "C9EnvF05FC3BE", + "Arn" + ] + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/cdk.out b/packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/cdk.out new file mode 100644 index 0000000000000..f0b901e7c06e5 --- /dev/null +++ b/packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"32.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/cloud9automaticstoptimemintuesDefaultTestDeployAssertBBFEEF89.assets.json b/packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/cloud9automaticstoptimemintuesDefaultTestDeployAssertBBFEEF89.assets.json new file mode 100644 index 0000000000000..c80ab71912cd5 --- /dev/null +++ b/packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/cloud9automaticstoptimemintuesDefaultTestDeployAssertBBFEEF89.assets.json @@ -0,0 +1,19 @@ +{ + "version": "32.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "cloud9automaticstoptimemintuesDefaultTestDeployAssertBBFEEF89.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/cloud9automaticstoptimemintuesDefaultTestDeployAssertBBFEEF89.template.json b/packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/cloud9automaticstoptimemintuesDefaultTestDeployAssertBBFEEF89.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/cloud9automaticstoptimemintuesDefaultTestDeployAssertBBFEEF89.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/integ.json b/packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/integ.json new file mode 100644 index 0000000000000..7a1cf5498f8c2 --- /dev/null +++ b/packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "32.0.0", + "testCases": { + "cloud-9-automatic-stop-time-mintues/DefaultTest": { + "stacks": [ + "C9automaticStopStack" + ], + "assertionStack": "cloud-9-automatic-stop-time-mintues/DefaultTest/DeployAssert", + "assertionStackName": "cloud9automaticstoptimemintuesDefaultTestDeployAssertBBFEEF89" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/manifest.json b/packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/manifest.json new file mode 100644 index 0000000000000..1c33fa8553e24 --- /dev/null +++ b/packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/manifest.json @@ -0,0 +1,249 @@ +{ + "version": "32.0.0", + "artifacts": { + "C9automaticStopStack.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "C9automaticStopStack.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "C9automaticStopStack": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "C9automaticStopStack.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/d302c7f90912c93bef6f8fda089adb33206a67ff3bdb6ce885b1f90a7fac7e25.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "C9automaticStopStack.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "C9automaticStopStack.assets" + ], + "metadata": { + "/C9automaticStopStack/VPC/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCB9E5F0B4" + } + ], + "/C9automaticStopStack/VPC/PublicSubnet1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1SubnetB4246D30" + } + ], + "/C9automaticStopStack/VPC/PublicSubnet1/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1RouteTableFEE4B781" + } + ], + "/C9automaticStopStack/VPC/PublicSubnet1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1RouteTableAssociation0B0896DC" + } + ], + "/C9automaticStopStack/VPC/PublicSubnet1/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1DefaultRoute91CEF279" + } + ], + "/C9automaticStopStack/VPC/PublicSubnet1/EIP": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1EIP6AD938E8" + } + ], + "/C9automaticStopStack/VPC/PublicSubnet1/NATGateway": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1NATGatewayE0556630" + } + ], + "/C9automaticStopStack/VPC/PublicSubnet2/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet2Subnet74179F39" + } + ], + "/C9automaticStopStack/VPC/PublicSubnet2/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet2RouteTable6F1A15F1" + } + ], + "/C9automaticStopStack/VPC/PublicSubnet2/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet2RouteTableAssociation5A808732" + } + ], + "/C9automaticStopStack/VPC/PublicSubnet2/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet2DefaultRouteB7481BBA" + } + ], + "/C9automaticStopStack/VPC/PrivateSubnet1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet1Subnet8BCA10E0" + } + ], + "/C9automaticStopStack/VPC/PrivateSubnet1/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet1RouteTableBE8A6027" + } + ], + "/C9automaticStopStack/VPC/PrivateSubnet1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet1RouteTableAssociation347902D1" + } + ], + "/C9automaticStopStack/VPC/PrivateSubnet1/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet1DefaultRouteAE1D6490" + } + ], + "/C9automaticStopStack/VPC/PrivateSubnet2/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet2SubnetCFCDAA7A" + } + ], + "/C9automaticStopStack/VPC/PrivateSubnet2/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet2RouteTable0A19E10E" + } + ], + "/C9automaticStopStack/VPC/PrivateSubnet2/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet2RouteTableAssociation0C73D413" + } + ], + "/C9automaticStopStack/VPC/PrivateSubnet2/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet2DefaultRouteF4F5CFD2" + } + ], + "/C9automaticStopStack/VPC/IGW": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCIGWB7E252D3" + } + ], + "/C9automaticStopStack/VPC/VPCGW": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCVPCGW99B986DC" + } + ], + "/C9automaticStopStack/C9Env/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "C9EnvF05FC3BE" + } + ], + "/C9automaticStopStack/URL": [ + { + "type": "aws:cdk:logicalId", + "data": "URL" + } + ], + "/C9automaticStopStack/ARN": [ + { + "type": "aws:cdk:logicalId", + "data": "ARN" + } + ], + "/C9automaticStopStack/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/C9automaticStopStack/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "C9automaticStopStack" + }, + "cloud9automaticstoptimemintuesDefaultTestDeployAssertBBFEEF89.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "cloud9automaticstoptimemintuesDefaultTestDeployAssertBBFEEF89.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "cloud9automaticstoptimemintuesDefaultTestDeployAssertBBFEEF89": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "cloud9automaticstoptimemintuesDefaultTestDeployAssertBBFEEF89.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "cloud9automaticstoptimemintuesDefaultTestDeployAssertBBFEEF89.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "cloud9automaticstoptimemintuesDefaultTestDeployAssertBBFEEF89.assets" + ], + "metadata": { + "/cloud-9-automatic-stop-time-mintues/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/cloud-9-automatic-stop-time-mintues/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "cloud-9-automatic-stop-time-mintues/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/tree.json b/packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/tree.json new file mode 100644 index 0000000000000..a05d8f05d2c40 --- /dev/null +++ b/packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.js.snapshot/tree.json @@ -0,0 +1,742 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "C9automaticStopStack": { + "id": "C9automaticStopStack", + "path": "C9automaticStopStack", + "children": { + "VPC": { + "id": "VPC", + "path": "C9automaticStopStack/VPC", + "children": { + "Resource": { + "id": "Resource", + "path": "C9automaticStopStack/VPC/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPC", + "aws:cdk:cloudformation:props": { + "cidrBlock": "10.0.0.0/16", + "enableDnsHostnames": true, + "enableDnsSupport": true, + "instanceTenancy": "default", + "tags": [ + { + "key": "Name", + "value": "C9automaticStopStack/VPC" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnVPC", + "version": "0.0.0" + } + }, + "PublicSubnet1": { + "id": "PublicSubnet1", + "path": "C9automaticStopStack/VPC/PublicSubnet1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "C9automaticStopStack/VPC/PublicSubnet1/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "availabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.0.0/18", + "mapPublicIpOnLaunch": true, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Public" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Public" + }, + { + "key": "Name", + "value": "C9automaticStopStack/VPC/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "C9automaticStopStack/VPC/PublicSubnet1/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "C9automaticStopStack/VPC/PublicSubnet1/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "tags": [ + { + "key": "Name", + "value": "C9automaticStopStack/VPC/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "C9automaticStopStack/VPC/PublicSubnet1/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCPublicSubnet1RouteTableFEE4B781" + }, + "subnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "C9automaticStopStack/VPC/PublicSubnet1/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCPublicSubnet1RouteTableFEE4B781" + }, + "destinationCidrBlock": "0.0.0.0/0", + "gatewayId": { + "Ref": "VPCIGWB7E252D3" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "0.0.0" + } + }, + "EIP": { + "id": "EIP", + "path": "C9automaticStopStack/VPC/PublicSubnet1/EIP", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::EIP", + "aws:cdk:cloudformation:props": { + "domain": "vpc", + "tags": [ + { + "key": "Name", + "value": "C9automaticStopStack/VPC/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnEIP", + "version": "0.0.0" + } + }, + "NATGateway": { + "id": "NATGateway", + "path": "C9automaticStopStack/VPC/PublicSubnet1/NATGateway", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::NatGateway", + "aws:cdk:cloudformation:props": { + "subnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + }, + "allocationId": { + "Fn::GetAtt": [ + "VPCPublicSubnet1EIP6AD938E8", + "AllocationId" + ] + }, + "tags": [ + { + "key": "Name", + "value": "C9automaticStopStack/VPC/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnNatGateway", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PublicSubnet", + "version": "0.0.0" + } + }, + "PublicSubnet2": { + "id": "PublicSubnet2", + "path": "C9automaticStopStack/VPC/PublicSubnet2", + "children": { + "Subnet": { + "id": "Subnet", + "path": "C9automaticStopStack/VPC/PublicSubnet2/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "availabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.64.0/18", + "mapPublicIpOnLaunch": true, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Public" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Public" + }, + { + "key": "Name", + "value": "C9automaticStopStack/VPC/PublicSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "C9automaticStopStack/VPC/PublicSubnet2/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "C9automaticStopStack/VPC/PublicSubnet2/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "tags": [ + { + "key": "Name", + "value": "C9automaticStopStack/VPC/PublicSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "C9automaticStopStack/VPC/PublicSubnet2/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCPublicSubnet2RouteTable6F1A15F1" + }, + "subnetId": { + "Ref": "VPCPublicSubnet2Subnet74179F39" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "C9automaticStopStack/VPC/PublicSubnet2/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCPublicSubnet2RouteTable6F1A15F1" + }, + "destinationCidrBlock": "0.0.0.0/0", + "gatewayId": { + "Ref": "VPCIGWB7E252D3" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PublicSubnet", + "version": "0.0.0" + } + }, + "PrivateSubnet1": { + "id": "PrivateSubnet1", + "path": "C9automaticStopStack/VPC/PrivateSubnet1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "C9automaticStopStack/VPC/PrivateSubnet1/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "availabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.128.0/18", + "mapPublicIpOnLaunch": false, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Private" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Private" + }, + { + "key": "Name", + "value": "C9automaticStopStack/VPC/PrivateSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "C9automaticStopStack/VPC/PrivateSubnet1/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "C9automaticStopStack/VPC/PrivateSubnet1/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "tags": [ + { + "key": "Name", + "value": "C9automaticStopStack/VPC/PrivateSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "C9automaticStopStack/VPC/PrivateSubnet1/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCPrivateSubnet1RouteTableBE8A6027" + }, + "subnetId": { + "Ref": "VPCPrivateSubnet1Subnet8BCA10E0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "C9automaticStopStack/VPC/PrivateSubnet1/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCPrivateSubnet1RouteTableBE8A6027" + }, + "destinationCidrBlock": "0.0.0.0/0", + "natGatewayId": { + "Ref": "VPCPublicSubnet1NATGatewayE0556630" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PrivateSubnet", + "version": "0.0.0" + } + }, + "PrivateSubnet2": { + "id": "PrivateSubnet2", + "path": "C9automaticStopStack/VPC/PrivateSubnet2", + "children": { + "Subnet": { + "id": "Subnet", + "path": "C9automaticStopStack/VPC/PrivateSubnet2/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "availabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.192.0/18", + "mapPublicIpOnLaunch": false, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Private" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Private" + }, + { + "key": "Name", + "value": "C9automaticStopStack/VPC/PrivateSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "C9automaticStopStack/VPC/PrivateSubnet2/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "C9automaticStopStack/VPC/PrivateSubnet2/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "tags": [ + { + "key": "Name", + "value": "C9automaticStopStack/VPC/PrivateSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "C9automaticStopStack/VPC/PrivateSubnet2/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCPrivateSubnet2RouteTable0A19E10E" + }, + "subnetId": { + "Ref": "VPCPrivateSubnet2SubnetCFCDAA7A" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "C9automaticStopStack/VPC/PrivateSubnet2/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCPrivateSubnet2RouteTable0A19E10E" + }, + "destinationCidrBlock": "0.0.0.0/0", + "natGatewayId": { + "Ref": "VPCPublicSubnet1NATGatewayE0556630" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PrivateSubnet", + "version": "0.0.0" + } + }, + "IGW": { + "id": "IGW", + "path": "C9automaticStopStack/VPC/IGW", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::InternetGateway", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "C9automaticStopStack/VPC" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnInternetGateway", + "version": "0.0.0" + } + }, + "VPCGW": { + "id": "VPCGW", + "path": "C9automaticStopStack/VPC/VPCGW", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPCGatewayAttachment", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "internetGatewayId": { + "Ref": "VPCIGWB7E252D3" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnVPCGatewayAttachment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.Vpc", + "version": "0.0.0" + } + }, + "C9Env": { + "id": "C9Env", + "path": "C9automaticStopStack/C9Env", + "children": { + "Resource": { + "id": "Resource", + "path": "C9automaticStopStack/C9Env/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Cloud9::EnvironmentEC2", + "aws:cdk:cloudformation:props": { + "instanceType": "t2.micro", + "automaticStopTimeMinutes": 30, + "connectionType": "CONNECT_SSH", + "imageId": "amazonlinux-2-x86_64", + "subnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_cloud9.CfnEnvironmentEC2", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-cloud9-alpha.Ec2Environment", + "version": "0.0.0" + } + }, + "URL": { + "id": "URL", + "path": "C9automaticStopStack/URL", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnOutput", + "version": "0.0.0" + } + }, + "ARN": { + "id": "ARN", + "path": "C9automaticStopStack/ARN", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnOutput", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "C9automaticStopStack/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "C9automaticStopStack/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "cloud-9-automatic-stop-time-mintues": { + "id": "cloud-9-automatic-stop-time-mintues", + "path": "cloud-9-automatic-stop-time-mintues", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "cloud-9-automatic-stop-time-mintues/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "cloud-9-automatic-stop-time-mintues/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.26" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "cloud-9-automatic-stop-time-mintues/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "cloud-9-automatic-stop-time-mintues/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "cloud-9-automatic-stop-time-mintues/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.26" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.ts b/packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.ts new file mode 100644 index 0000000000000..736aa942b3489 --- /dev/null +++ b/packages/@aws-cdk/aws-cloud9-alpha/test/integ.automatic-stop.ts @@ -0,0 +1,35 @@ +import * as ec2 from 'aws-cdk-lib/aws-ec2'; +import * as cdk from 'aws-cdk-lib'; +import * as integ from '@aws-cdk/integ-tests-alpha'; +import * as constructs from 'constructs'; +import * as cloud9 from '../lib'; + +export class Cloud9Env extends cdk.Stack { + constructor(scope: constructs.Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + const vpc = new ec2.Vpc(this, 'VPC', { + restrictDefaultSecurityGroup: false, + maxAzs: 2, + natGateways: 1, + }); + + // create a cloud9 ec2 environment in a new VPC + const c9env = new cloud9.Ec2Environment(this, 'C9Env', { + vpc, + automaticStop: cdk.Duration.minutes(30), + imageId: cloud9.ImageId.AMAZON_LINUX_2, + }); + new cdk.CfnOutput(this, 'URL', { value: c9env.ideUrl }); + new cdk.CfnOutput(this, 'ARN', { value: c9env.ec2EnvironmentArn }); + } +} + +const app = new cdk.App(); +const stack = new Cloud9Env(app, 'C9automaticStopStack'); + +new integ.IntegTest(app, 'cloud-9-automatic-stop-time-mintues', { + testCases: [stack], +}); + +app.synth();