Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(toolkit-lib): bootstrap action #63

Open
wants to merge 44 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
dba2c09
bootstrap action
iankhou Feb 19, 2025
6e3e58a
import ordering
iankhou Feb 19, 2025
29973ef
bootstrap template YAML
iankhou Feb 19, 2025
73d8aee
fix the sdk provider mock
iankhou Feb 19, 2025
f2769f5
modified .projenrc.ts to add a post-compile task that copies the boot…
iankhou Feb 19, 2025
e972af1
delete copied bootstrap-template.yaml file
iankhou Feb 19, 2025
91b9ae8
some import optimizations
iankhou Feb 19, 2025
35a789b
use toolkit's toolkitStackName
iankhou Feb 19, 2025
f8d8f4e
new api and tests for custom bootstrap template
iankhou Feb 19, 2025
8ff4156
chore: self mutation
invalid-email-address Feb 19, 2025
4e14148
bootstrap scoped to environment level
iankhou Feb 20, 2025
52c9154
remove extra comment
iankhou Feb 20, 2025
e48ae89
chore: self mutation
invalid-email-address Feb 20, 2025
71e8b71
Simple refactoring, removed usage of deprecated .success() method, us…
iankhou Feb 21, 2025
9e43a8f
remove unnecessary files from cli-lib-alpha
iankhou Feb 21, 2025
b7f757f
copy bootstrap templates at build time instead of as a post-compile step
iankhou Feb 21, 2025
7ab7957
BootstrapEnvironment class enables running bootstrap with either cx o…
iankhou Feb 21, 2025
997540a
Merge branch 'main' into iankhou-toolkit-bootstrap
iankhou Feb 21, 2025
2fcf944
some renaming
iankhou Feb 21, 2025
e454a86
remove console log
iankhou Feb 21, 2025
792f145
chore: self mutation
invalid-email-address Feb 21, 2025
f6497a2
bootstrap parameter changes
iankhou Feb 21, 2025
f0dd065
line too long
iankhou Feb 23, 2025
28ecc6a
chore: self mutation
invalid-email-address Feb 23, 2025
536d2f7
Merge branch 'main' into iankhou-toolkit-bootstrap
iankhou Feb 24, 2025
64880df
merge changes
iankhou Feb 24, 2025
103f7cd
chore: self mutation
invalid-email-address Feb 24, 2025
fa8e75b
chore: self mutation
invalid-email-address Feb 24, 2025
9450502
chore: self mutation
invalid-email-address Feb 24, 2025
dbcb73e
modifed wrong projen file
iankhou Feb 24, 2025
849f7f9
tests for parameters
iankhou Feb 24, 2025
bacd260
chore: self mutation
invalid-email-address Feb 24, 2025
1a7588a
Merge branch 'main' into iankhou-toolkit-bootstrap
iankhou Feb 25, 2025
1b3100d
merge conflicts
iankhou Feb 25, 2025
89acec0
use CodeInfo code instead of string codes for bootstrap action and tests
iankhou Feb 25, 2025
d86a8b7
missed a status code
iankhou Feb 25, 2025
d2fd48b
code registry update and used string codes in tests
iankhou Feb 25, 2025
70e7082
linting
iankhou Feb 25, 2025
4e941da
move bootstrap example custom template to toolkit-lib test directory
iankhou Feb 27, 2025
95df4bf
Merge branch 'main' into iankhou-toolkit-bootstrap
iankhou Feb 28, 2025
5021e60
Revert bundle.mjs
iankhou Feb 28, 2025
a77119a
fix unbound method
iankhou Feb 28, 2025
7fab3ae
use allowed example account number
iankhou Feb 28, 2025
6024708
Merge branch 'main' into iankhou-toolkit-bootstrap
iankhou Feb 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/@aws-cdk/toolkit-lib/CODE_REGISTRY.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
| CDK_TOOLKIT_I7010 | Confirm destroy stacks | info | n/a |
| CDK_TOOLKIT_E7010 | Action was aborted due to negative confirmation of request | error | n/a |
| CDK_TOOLKIT_E7900 | Stack deletion failed | error | n/a |
| CDK_TOOLKIT_I9000 | Provides bootstrap times | info | n/a |
| CDK_TOOLKIT_I9900 | Bootstrap results on success | info | n/a |
| CDK_TOOLKIT_E9900 | Bootstrap failed | error | n/a |
| CDK_ASSEMBLY_I0042 | Writing updated context | debug | n/a |
| CDK_ASSEMBLY_I0241 | Fetching missing context | debug | n/a |
| CDK_ASSEMBLY_I1000 | Cloud assembly output starts | debug | n/a |
Expand Down
210 changes: 210 additions & 0 deletions packages/@aws-cdk/toolkit-lib/lib/actions/bootstrap/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
import { Tag } from '../../api/aws-cdk';

/**
* Options for Bootstrap
*/
export interface BootstrapOptions {

/**
* Bootstrap environment parameters for CloudFormation used when deploying the bootstrap stack
* @default BootstrapEnvironmentParameters.onlyExisting()
*/
readonly parameters?: BootstrapEnvironmentParameters;

/**
* The template source of the bootstrap stack
*
* @default BootstrapSource.default()
*/
readonly source?: BootstrapSource;

/**
* Whether to execute the changeset or only create it and leave it in review
* @default true
*/
readonly execute?: boolean;

/**
* Tags for cdktoolkit stack
*
* @default []
*/
readonly tags?: Tag[];

/**
* Whether the stacks created by the bootstrap process should be protected from termination
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-protect-stacks.html
* @default true
*/
readonly terminationProtection?: boolean;

/**
* Use previous values for unspecified parameters
*
* If not set, all parameters must be specified for every deployment
*
* @default true
*/
usePreviousParameters?: boolean;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually this is what I meant to roll into BootstrappingParameters

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed it to model how we handle this in deploy

}

/**
* Parameter values for the bootstrapping template
*/
export interface BootstrapParameterValues {
/**
* The name to be given to the CDK Bootstrap bucket
* By default, a name is generated by CloudFormation
*
* @default - No value, optional argument
*/
readonly bucketName?: string;

/**
* The ID of an existing KMS key to be used for encrypting items in the bucket
* By default, the default KMS key is used
*
* @default - No value, optional argument
*/
readonly kmsKeyId?: string;

/**
* Whether or not to create a new customer master key (CMK)
*
* Only applies to modern bootstrapping
* Legacy bootstrapping will never create a CMK, only use the default S3 key
*
* @default false
*/
readonly createCustomerMasterKey?: boolean;

/**
* The list of AWS account IDs that are trusted to deploy into the environment being bootstrapped
*
* @default []
*/
readonly trustedAccounts?: string[];

/**
* The list of AWS account IDs that are trusted to look up values in the environment being bootstrapped
*
* @default []
*/
readonly trustedAccountsForLookup?: string[];

/**
* The list of AWS account IDs that should not be trusted by the bootstrapped environment
* If these accounts are already trusted, they will be removed on bootstrapping
*
* @default []
*/
readonly untrustedAccounts?: string[];

/**
* The ARNs of the IAM managed policies that should be attached to the role performing CloudFormation deployments
* In most cases, this will be the AdministratorAccess policy
* At least one policy is required if `trustedAccounts` were passed
*
* @default []
*/
readonly cloudFormationExecutionPolicies?: string[];

/**
* Identifier to distinguish multiple bootstrapped environments
* The default qualifier is an arbitrary but unique string
*
* @default - 'hnb659fds'
*/
readonly qualifier?: string;

/**
* Whether or not to enable S3 Staging Bucket Public Access Block Configuration
*
* @default true
*/
readonly publicAccessBlockConfiguration?: boolean;

/**
* Flag for using the default permissions boundary for bootstrapping
*
* @default - No value, optional argument
*/
readonly examplePermissionsBoundary?: boolean;

/**
* Name for the customer's custom permissions boundary for bootstrapping
*
* @default - No value, optional argument
*/
readonly customPermissionsBoundary?: string;
}

/**
* Parameters for the bootstrapping template with flexible configuration options
*/
export class BootstrapEnvironmentParameters {
/**
* Use only existing parameters on the stack.
*/
public static onlyExisting() {
return new BootstrapEnvironmentParameters({}, true);
}

/**
* Use exactly these parameters and remove any other existing parameters from the stack.
*/
public static exactly(params: BootstrapParameterValues) {
return new BootstrapEnvironmentParameters(params, false);
}

/**
* Define additional parameters for the stack, while keeping existing parameters for unspecified values.
*/
public static withExisting(params: BootstrapParameterValues) {
return new BootstrapEnvironmentParameters(params, true);
}

/**
* The parameters as a Map for easy access and manipulation
*/
public readonly parameters?: BootstrapParameterValues;
public readonly keepExistingParameters: boolean;

private constructor(params?: BootstrapParameterValues, usePreviousParameters = true) {
this.keepExistingParameters = usePreviousParameters;
this.parameters = params;
}
}

/**
* Source configuration for bootstrap operations
*/
export class BootstrapSource {
/**
* Use the default bootstrap template
*/
static default(): BootstrapSource {
return new BootstrapSource('default');
}

/**
* Use a custom bootstrap template
*/
static customTemplate(templateFile: string): BootstrapSource {
return new BootstrapSource('custom', templateFile);
}

private readonly source: 'default' | 'custom';
private readonly templateFile?: string;
Comment on lines +197 to +198
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private readonly source: 'default' | 'custom';
private readonly templateFile?: string;
public readonly source: 'default' | 'custom';
public readonly templateFile?: string;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why make it public? That would allow the user to get the source and templateFile without going through the render function. i.e. code in bootstrap that looks like source.render() could be converted to source.source.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the code in Bootstrap would be just source then.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This causes some Typescript errors:

Argument of type 'BootstrapSource' is not assignable to parameter of type 'BootstrapSource | undefined'.
  Type 'BootstrapSource' is not assignable to type '{ source: "default"; } | { source: "custom"; templateFile: string; }'.
    Type 'BootstrapSource' is not assignable to type '{ source: "custom"; templateFile: string; }'.
      Types of property 'source' are incompatible.
        Type '"default" | "custom"' is not assignable to type '"custom"'.
          Type '"default"' is not assignable to type '"custom"'.ts(2345)

There are two valid options for BootstrapSource:

  • { source: "default" }
  • { source: "custom"; templateFile: string }

But removing render() and exposing source and templateFile as public instance variables makes TypeScript think that we could have something like this:

  • { source: "default"; templateFile: string }

Given our static methods, this is not possible, but TypeScript doesn't know that.

private constructor(source: 'default' | 'custom', templateFile?: string) {
this.source = source;
this.templateFile = templateFile;
}

public render() {
return {
source: this.source,
...(this.templateFile ? { templateFile: this.templateFile } : {}),
} as { source: 'default' } | { source: 'custom'; templateFile: string };
}
Comment on lines +204 to +209
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you make the props public, I think you don't need the render helper

Suggested change
public render() {
return {
source: this.source,
...(this.templateFile ? { templateFile: this.templateFile } : {}),
} as { source: 'default' } | { source: 'custom'; templateFile: string };
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as cxapi from '@aws-cdk/cx-api';
import { ToolkitError } from '../../../api/errors';

/**
* Given a set of "<account>/<region>" strings, construct environments for them
*/
export function environmentsFromDescriptors(envSpecs: string[]): cxapi.Environment[] {
const ret = new Array<cxapi.Environment>();

for (const spec of envSpecs) {
const parts = spec.replace(/^aws:\/\//, '').split('/');
if (parts.length !== 2) {
throw new ToolkitError(`Expected environment name in format 'aws://<account>/<region>', got: ${spec}`);
}

ret.push({
name: spec,
account: parts[0],
region: parts[1],
});
}

return ret;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './helpers';
3 changes: 2 additions & 1 deletion packages/@aws-cdk/toolkit-lib/lib/api/aws-cdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ export { formatSdkLoggerContent, SdkProvider } from '../../../../aws-cdk/lib/api
export { Context, PROJECT_CONTEXT } from '../../../../aws-cdk/lib/api/context';
export { Deployments, type SuccessfulDeployStackResult } from '../../../../aws-cdk/lib/api/deployments';
export { Settings } from '../../../../aws-cdk/lib/api/settings';
export { tagsForStack, Tag } from '../../../../aws-cdk/lib/api/tags';
export { type Tag, tagsForStack } from '../../../../aws-cdk/lib/api/tags';
export { DEFAULT_TOOLKIT_STACK_NAME } from '../../../../aws-cdk/lib/api/toolkit-info';
export { ResourceMigrator } from '../../../../aws-cdk/lib/api/resource-import';
export { CloudWatchLogEventMonitor, findCloudWatchLogGroups } from '../../../../aws-cdk/lib/api/logs';
export { type WorkGraph, WorkGraphBuilder, AssetBuildNode, AssetPublishNode, StackNode, Concurrency } from '../../../../aws-cdk/lib/api/work-graph';
export { Bootstrapper } from '../../../../aws-cdk/lib/api/bootstrap';

// Context Providers
export * as contextproviders from '../../../../aws-cdk/lib/context-providers';
Expand Down
15 changes: 15 additions & 0 deletions packages/@aws-cdk/toolkit-lib/lib/api/io/private/codes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,21 @@ export const CODES = {
}),

// 9: Bootstrap
CDK_TOOLKIT_I9000: codeInfo({
code: 'CDK_TOOLKIT_I9000',
description: 'Provides bootstrap times',
level: 'info',
}),
CDK_TOOLKIT_I9900: codeInfo({
code: 'CDK_TOOLKIT_I9900',
description: 'Bootstrap results on success',
level: 'info',
}),
CDK_TOOLKIT_E9900: codeInfo({
code: 'CDK_TOOLKIT_E9900',
description: 'Bootstrap failed',
level: 'error',
}),

// Assembly codes
CDK_ASSEMBLY_I0042: codeInfo({
Expand Down
5 changes: 3 additions & 2 deletions packages/@aws-cdk/toolkit-lib/lib/api/io/private/timer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class Timer {
* Ends the current timer as a specified timing and notifies the IoHost.
* @returns the elapsed time
*/
public async endAs(ioHost: ActionAwareIoHost, type: 'synth' | 'deploy' | 'rollback' | 'destroy') {
public async endAs(ioHost: ActionAwareIoHost, type: 'synth' | 'deploy' | 'rollback' | 'destroy' | 'bootstrap') {
const duration = this.end();
const { code, text } = timerMessageProps(type);

Expand All @@ -49,7 +49,7 @@ export class Timer {
}
}

function timerMessageProps(type: 'synth' | 'deploy' | 'rollback'| 'destroy'): {
function timerMessageProps(type: 'synth' | 'deploy' | 'rollback'| 'destroy' | 'bootstrap'): {
code: CodeInfo;
text: string;
} {
Expand All @@ -58,5 +58,6 @@ function timerMessageProps(type: 'synth' | 'deploy' | 'rollback'| 'destroy'): {
case 'deploy': return { code: CODES.CDK_TOOLKIT_I5000, text: 'Deployment' };
case 'rollback': return { code: CODES.CDK_TOOLKIT_I6000, text: 'Rollback' };
case 'destroy': return { code: CODES.CDK_TOOLKIT_I7000, text: 'Destroy' };
case 'bootstrap': return { code: CODES.CDK_TOOLKIT_I9000, text: 'Bootstrap' };
}
}
Loading
Loading