Skip to content

Commit

Permalink
Merge branch 'main' into huijbers/sync-flow
Browse files Browse the repository at this point in the history
  • Loading branch information
mergify[bot] committed Jan 10, 2024
2 parents c9b2e5e + 0cada61 commit 43b07cc
Show file tree
Hide file tree
Showing 11 changed files with 172 additions and 35 deletions.
58 changes: 29 additions & 29 deletions CONTRIBUTING.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,9 @@ switch (stackSet) {
stage.synth({ validateOnSynthesis: true });
break;

case 'stage-with-no-stacks':
break;

default:
throw new Error(`Unrecognized INTEG_STACK_SET: '${stackSet}'`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,25 @@ integTest('deploy stack without resource', withDefaultFixture(async (fixture) =>
.rejects.toThrow('conditional-resource does not exist');
}));

integTest('deploy no stacks with --ignore-no-stacks', withDefaultFixture(async (fixture) => {
// empty array for stack names
await fixture.cdkDeploy([], {
options: ['--ignore-no-stacks'],
modEnv: {
INTEG_STACK_SET: 'stage-with-no-stacks',
},
});
}));

integTest('deploy no stacks error', withDefaultFixture(async (fixture) => {
// empty array for stack names
await expect(fixture.cdkDeploy([], {
modEnv: {
INTEG_STACK_SET: 'stage-with-no-stacks',
},
})).rejects.toThrow('exited with error');
}));

integTest('IAM diff', withDefaultFixture(async (fixture) => {
const output = await fixture.cdk(['diff', fixture.fullStackName('iam-test')]);

Expand Down
32 changes: 31 additions & 1 deletion packages/aws-cdk-lib/aws-route53/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ new route53.ARecord(this, 'ARecord', {
### Cross Account Zone Delegation

If you want to have your root domain hosted zone in one account and your subdomain hosted
zone in a diferent one, you can use `CrossAccountZoneDelegationRecord` to set up delegation
zone in a different one, you can use `CrossAccountZoneDelegationRecord` to set up delegation
between them.

In the account containing the parent hosted zone:
Expand All @@ -196,6 +196,36 @@ const crossAccountRole = new iam.Role(this, 'CrossAccountRole', {
roleName: 'MyDelegationRole',
// The other account
assumedBy: new iam.AccountPrincipal('12345678901'),
// You can scope down this role policy to be least privileged.
// If you want the other account to be able to manage specific records,
// you can scope down by resource and/or normalized record names
inlinePolicies: {
crossAccountPolicy: new iam.PolicyDocument({
statements: [
new iam.PolicyStatement({
sid: 'ListHostedZonesByName',
effect: iam.Effect.ALLOW,
actions: ['route53:ListHostedZonesByName'],
resources: ['*'],
}),
new iam.PolicyStatement({
sid: 'GetHostedZoneAndChangeResourceRecordSet',
effect: iam.Effect.ALLOW,
actions: ['route53:GetHostedZone', 'route53:ChangeResourceRecordSet'],
// This example assumes the RecordSet subdomain.somexample.com
// is contained in the HostedZone
resources: ['arn:aws:route53:::hostedzone/HZID00000000000000000'],
conditions: {
'ForAllValues:StringLike': {
'route53:ChangeResourceRecordSetsNormalizedRecordNames': [
'subdomain.someexample.com',
],
},
},
}),
],
}),
},
});
parentZone.grantDelegation(crossAccountRole);
```
Expand Down
14 changes: 14 additions & 0 deletions packages/aws-cdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,20 @@ $ cdk deploy --method=prepare-change-set --change-set-name MyChangeSetName
For more control over when stack changes are deployed, the CDK can generate a
CloudFormation change set but not execute it.

#### Ignore No Stacks

You may have an app with multiple environments, e.g., dev and prod. When starting
development, your prod app may not have any resources or the resources are commented
out. In this scenario, you will receive an error message stating that the app has no
stacks.

To bypass this error messages, you can pass the `--ignore-no-stacks` flag to the
`deploy` command:

```console
$ cdk deploy --ignore-no-stacks
```

#### Hotswap deployments for faster development

You can pass the `--hotswap` flag to the `deploy` command:
Expand Down
12 changes: 11 additions & 1 deletion packages/aws-cdk/lib/api/cxapp/cloud-assembly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,16 @@ export interface SelectStacksOptions {
extend?: ExtendedStackSelection;

/**
* The behavior if if no selectors are privided.
* The behavior if if no selectors are provided.
*/
defaultBehavior: DefaultSelection;

/**
* Whether to deploy if the app contains no stacks.
*
* @default false
*/
ignoreNoStacks?: boolean;
}

/**
Expand Down Expand Up @@ -100,6 +107,9 @@ export class CloudAssembly {
const patterns = sanitizePatterns(selector.patterns);

if (stacks.length === 0) {
if (options.ignoreNoStacks) {
return new StackCollection(this, []);
}
throw new Error('This app contains no stacks');
}

Expand Down
7 changes: 7 additions & 0 deletions packages/aws-cdk/lib/api/deployments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,13 @@ export interface DeployStackOptions {
* @default true To remain backward compatible.
*/
readonly assetParallelism?: boolean;

/**
* Whether to deploy if the app contains no stacks.
*
* @default false
*/
ignoreNoStacks?: boolean;
}

interface AssetOptions {
Expand Down
17 changes: 14 additions & 3 deletions packages/aws-cdk/lib/cdk-toolkit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,8 @@ export class CdkToolkit {
}

const startSynthTime = new Date().getTime();
const stackCollection = await this.selectStacksForDeploy(options.selector, options.exclusively, options.cacheCloudAssembly);
const stackCollection = await this.selectStacksForDeploy(options.selector, options.exclusively,
options.cacheCloudAssembly, options.ignoreNoStacks);
const elapsedSynthTime = new Date().getTime() - startSynthTime;
print('\n✨ Synthesis time: %ss\n', formatTime(elapsedSynthTime));

Expand Down Expand Up @@ -317,6 +318,7 @@ export class CdkToolkit {
hotswap: options.hotswap,
extraUserAgent: options.extraUserAgent,
assetParallelism: options.assetParallelism,
ignoreNoStacks: options.ignoreNoStacks,
});

const message = result.noOp
Expand Down Expand Up @@ -491,7 +493,7 @@ export class CdkToolkit {
}

public async import(options: ImportOptions) {
const stacks = await this.selectStacksForDeploy(options.selector, true, true);
const stacks = await this.selectStacksForDeploy(options.selector, true, true, false);

if (stacks.stackCount > 1) {
throw new Error(`Stack selection is ambiguous, please choose a specific stack for import [${stacks.stackArtifacts.map(x => x.id).join(', ')}]`);
Expand Down Expand Up @@ -741,11 +743,13 @@ export class CdkToolkit {
return stacks;
}

private async selectStacksForDeploy(selector: StackSelector, exclusively?: boolean, cacheCloudAssembly?: boolean): Promise<StackCollection> {
private async selectStacksForDeploy(selector: StackSelector, exclusively?: boolean,
cacheCloudAssembly?: boolean, ignoreNoStacks?: boolean): Promise<StackCollection> {
const assembly = await this.assembly(cacheCloudAssembly);
const stacks = await assembly.selectStacks(selector, {
extend: exclusively ? ExtendedStackSelection.None : ExtendedStackSelection.Upstream,
defaultBehavior: DefaultSelection.OnlySingle,
ignoreNoStacks,
});

this.validateStacksSelected(stacks, selector.patterns);
Expand Down Expand Up @@ -1159,6 +1163,13 @@ export interface DeployOptions extends CfnDeployOptions, WatchOptions {
* @default AssetBuildTime.ALL_BEFORE_DEPLOY
*/
readonly assetBuildTime?: AssetBuildTime;

/**
* Whether to deploy if the app contains no stacks.
*
* @default false
*/
readonly ignoreNoStacks?: boolean;
}

export interface ImportOptions extends CfnDeployOptions {
Expand Down
4 changes: 3 additions & 1 deletion packages/aws-cdk/lib/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,8 @@ async function parseCommandLineArguments(args: string[]) {
})
.option('concurrency', { type: 'number', desc: 'Maximum number of simultaneous deployments (dependency permitting) to execute.', default: 1, requiresArg: true })
.option('asset-parallelism', { type: 'boolean', desc: 'Whether to build/publish assets in parallel' })
.option('asset-prebuild', { type: 'boolean', desc: 'Whether to build all assets before deploying the first stack (useful for failing Docker builds)', default: true }),
.option('asset-prebuild', { type: 'boolean', desc: 'Whether to build all assets before deploying the first stack (useful for failing Docker builds)', default: true })
.option('ignore-no-stacks', { type: 'boolean', desc: 'Whether to deploy if the app contains no stacks', default: false }),
)
.command('import [STACK]', 'Import existing resource(s) into the given STACK', (yargs: Argv) => yargs
.option('execute', { type: 'boolean', desc: 'Whether to execute ChangeSet (--no-execute will NOT execute the ChangeSet)', default: true })
Expand Down Expand Up @@ -585,6 +586,7 @@ export async function exec(args: string[], synthesizer?: Synthesizer): Promise<n
concurrency: args.concurrency,
assetParallelism: configuration.settings.get(['assetParallelism']),
assetBuildTime: configuration.settings.get(['assetPrebuild']) ? AssetBuildTime.ALL_BEFORE_DEPLOY : AssetBuildTime.JUST_IN_TIME,
ignoreNoStacks: args.ignoreNoStacks,
});

case 'import':
Expand Down
1 change: 1 addition & 0 deletions packages/aws-cdk/lib/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ export class Settings {
notices: argv.notices,
assetParallelism: argv['asset-parallelism'],
assetPrebuild: argv['asset-prebuild'],
ignoreNoStacks: argv['ignore-no-stacks'],
});
}

Expand Down
40 changes: 40 additions & 0 deletions packages/aws-cdk/test/api/cloud-assembly.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,38 @@ test('select behavior with nested assemblies: repeat', async() => {
expect(x.stackCount).toBe(2);
});

test('select behavior with no stacks and ignore stacks option', async() => {
// GIVEN
const cxasm = await testCloudAssemblyNoStacks();

// WHEN
const x = await cxasm.selectStacks({ patterns: [] }, {
defaultBehavior: DefaultSelection.AllStacks,
ignoreNoStacks: true,
});

// THEN
expect(x.stackCount).toBe(0);
});

test('select behavior with no stacks and no ignore stacks option', async() => {
// GIVEN
const cxasm = await testCloudAssemblyNoStacks();

// WHEN & THEN
await expect(cxasm.selectStacks({ patterns: [] }, { defaultBehavior: DefaultSelection.AllStacks, ignoreNoStacks: false }))
.rejects.toThrow('This app contains no stacks');
});

test('select behavior with no stacks and default ignore stacks options (false)', async() => {
// GIVEN
const cxasm = await testCloudAssemblyNoStacks();

// WHEN & THEN
await expect(cxasm.selectStacks({ patterns: [] }, { defaultBehavior: DefaultSelection.AllStacks }))
.rejects.toThrow('This app contains no stacks');
});

async function testCloudAssembly({ env }: { env?: string, versionReporting?: boolean } = {}) {
const cloudExec = new MockCloudExecutable({
stacks: [{
Expand All @@ -182,6 +214,14 @@ async function testCloudAssembly({ env }: { env?: string, versionReporting?: boo
return cloudExec.synthesize();
}

async function testCloudAssemblyNoStacks() {
const cloudExec = new MockCloudExecutable({
stacks: [],
});

return cloudExec.synthesize();
}

async function testNestedCloudAssembly({ env }: { env?: string, versionReporting?: boolean } = {}) {
const cloudExec = new MockCloudExecutable({
stacks: [{
Expand Down

0 comments on commit 43b07cc

Please sign in to comment.