Skip to content

Commit

Permalink
Remove dependency from toolkit on CDK (#352)
Browse files Browse the repository at this point in the history
Due to a cyclic dependency caused by the need for CDK libraries to depend on the toolkit for testing purposes, we decided not to use the CDK to define the "toolkit stack". It's a very simple stack that essentially contains a single bucket.

Therefore, we just embed the template in the toolkit code, and finally we don't have cycles anymore.

Tested this manually by:
* Bootstrapping the toolkit stack in my dev account
* Updating the toolkit stack
* Verifying that "deploy" can discover the bucket and upload a template

The empty "WaitHandle" stack also used the CDK, so this was also embedded inline.

--reject-cycles is enabled now

Fixes #293
  • Loading branch information
Elad Ben-Israel authored Jul 17, 2018
1 parent e021793 commit e1c61fb
Show file tree
Hide file tree
Showing 10 changed files with 222 additions and 292 deletions.
2 changes: 1 addition & 1 deletion install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ export PATH=node_modules/.bin:$PATH

echo "============================================================================================="
echo "bootstrapping..."
lerna bootstrap # --reject-cycles
lerna bootstrap --reject-cycles
44 changes: 11 additions & 33 deletions packages/@aws-cdk/cdk-cfnspec/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 35 additions & 6 deletions packages/aws-cdk/lib/api/bootstrap-environment.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,41 @@
import { App } from '@aws-cdk/core';
import { Environment } from '@aws-cdk/cx-api';
import { Environment, SynthesizedStack } from '@aws-cdk/cx-api';
import { deployStack, DeployStackResult } from './deploy-stack';
import { SDK } from './util/sdk';
import { ToolkitStack } from './util/toolkit-stack';

// tslint:disable:max-line-length

export const BUCKET_NAME_OUTPUT = 'BucketName';
export const BUCKET_DOMAIN_NAME_OUTPUT = 'BucketDomainName';

export async function bootstrapEnvironment(environment: Environment, aws: SDK, toolkitStackName: string): Promise<DeployStackResult> {
const app = new App();
const stack = new ToolkitStack(app, toolkitStackName, { env: environment });
const synthesizedStack = await app.synthesizeStack(stack.name);
const synthesizedStack: SynthesizedStack = {
environment,
metadata: { },
template: {
Description: "The CDK Toolkit Stack. It cas created by `cdk bootstrap` and manages resources necessary for managing your Cloud Applications with AWS CDK.",
Resources: {
StagingBucket: {
Type: "AWS::S3::Bucket",
Properties: {
AccessControl: "Private",
BucketEncryption: { ServerSideEncryptionConfiguration: [ { ServerSideEncryptionByDefault: { SSEAlgorithm: "aws:kms" } } ] }
}
}
},
Outputs: {
[BUCKET_NAME_OUTPUT]: {
Description: "The name of the S3 bucket owned by the CDK toolkit stack",
Value: { Ref: "StagingBucket" },
Export: { Name: "CDKToolkit:BucketName" }
},
[BUCKET_DOMAIN_NAME_OUTPUT]: {
Description: "The domain name of the S3 bucket owned by the CDK toolkit stack",
Value: { "Fn::GetAtt": [ "StagingBucket", "DomainName" ] },
Export: { Name: "CDKToolkit:BucketDomainName" }
}
}
},
name: toolkitStackName,
};
return await deployStack(synthesizedStack, aws);
}
15 changes: 8 additions & 7 deletions packages/aws-cdk/lib/api/deploy-stack.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { cloudformation } from '@aws-cdk/cloudformation';
import { App, Stack } from '@aws-cdk/core';
import { StackInfo, SynthesizedStack } from '@aws-cdk/cx-api';
import { CloudFormation } from 'aws-sdk';
import * as colors from 'colors/safe';
Expand Down Expand Up @@ -87,11 +85,14 @@ async function getStackOutputs(cfn: CloudFormation, stackName: string): Promise<

async function createEmptyStack(cfn: CloudFormation, stackName: string, quiet: boolean): Promise<void> {
debug('Creating new empty stack named %s', stackName);
const app = new App();
const stack = new Stack(app, stackName);
stack.templateOptions.description = 'This is an empty stack created by AWS CDK during a deployment attempt';
new cloudformation.WaitConditionHandleResource(stack, 'WaitCondition');
const template = (await app.synthesizeStack(stackName)).template;

const template = {
Resources: {
WaitCondition: {
Type: 'AWS::CloudFormation::WaitConditionHandle'
}
}
};

const response = await cfn.createStack({ StackName: stackName, TemplateBody: JSON.stringify(template, null, 2) }).promise();
debug('CreateStack response: %j', response);
Expand Down
15 changes: 6 additions & 9 deletions packages/aws-cdk/lib/api/toolkit-info.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { App, Output } from '@aws-cdk/core';
import { Environment } from '@aws-cdk/cx-api';
import { CloudFormation } from 'aws-sdk';
import * as colors from 'colors/safe';
import { debug } from '../logging';
import { Mode } from './aws-auth/credentials';
import { BUCKET_DOMAIN_NAME_OUTPUT, BUCKET_NAME_OUTPUT } from './bootstrap-environment';
import { waitForStack } from './util/cloudformation';
import { SDK } from './util/sdk';
import { ToolkitStack } from './util/toolkit-stack';

export interface ToolkitInfo {
bucketName: string
Expand All @@ -21,22 +20,20 @@ export async function loadToolkitInfo(environment: Environment, sdk: SDK, stackN
environment.name, stackName, colors.blue(`cdk bootstrap "${environment.name}"`));
return undefined;
}
const stackModel = new ToolkitStack(new App(), 'ToolkitStack', { env: environment });
return {
bucketName: getOutputValue(stack, stackModel.bucketNameOutput),
bucketEndpoint: getOutputValue(stack, stackModel.bucketDomainNameOutput)
bucketName: getOutputValue(stack, BUCKET_NAME_OUTPUT),
bucketEndpoint: getOutputValue(stack, BUCKET_DOMAIN_NAME_OUTPUT)
};
}

function getOutputValue(stack: CloudFormation.Stack, output: Output): string {
const name = output.name;
function getOutputValue(stack: CloudFormation.Stack, output: string): string {
let result: string | undefined;
if (stack.Outputs) {
const found = stack.Outputs.find(o => o.OutputKey === name);
const found = stack.Outputs.find(o => o.OutputKey === output);
result = found && found.OutputValue;
}
if (result === undefined) {
throw new Error(`The CDK toolkit stack (${stack.StackName}) does not have an output named ${name}. Use 'cdk bootstrap' to correct this.`);
throw new Error(`The CDK toolkit stack (${stack.StackName}) does not have an output named ${output}. Use 'cdk bootstrap' to correct this.`);
}
return result;
}
36 changes: 0 additions & 36 deletions packages/aws-cdk/lib/api/util/toolkit-stack.ts

This file was deleted.

3 changes: 0 additions & 3 deletions packages/aws-cdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,8 @@
"cdk-build-tools": "^0.7.3-beta"
},
"dependencies": {
"@aws-cdk/cloudformation": "^0.7.3-beta",
"@aws-cdk/cloudformation-diff": "^0.7.3-beta",
"@aws-cdk/core": "^0.7.3-beta",
"@aws-cdk/cx-api": "^0.7.3-beta",
"@aws-cdk/s3": "^0.7.3-beta",
"@aws-cdk/util": "^0.7.3-beta",
"aws-sdk": "^2.259.1",
"camelcase": "^5.0.0",
Expand Down
Loading

0 comments on commit e1c61fb

Please sign in to comment.