Skip to content

Commit 0cba976

Browse files
corymhallmrgrain
andauthored
feat(toolkit-lib): fromAssemblyDirectory can ignore missing context (#611)
Previously `fromAssemblyDirectory` created a `ContextAwareCloudAssemblySource` with lookups disabled. This would always fail if there was any missing context in the assembly, because you can't perform lookups with just the CloudAssembly directory. This PR updates the logic to use a `CachedCloudAssembly` instead and check for missing context. The missing context check can be disabled using the new `failOnMissingContext` option (defaults to fail). Resolves #549 --- By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license --------- Co-authored-by: Momo Kornher <[email protected]> Co-authored-by: Momo Kornher <[email protected]>
1 parent 76ac13c commit 0cba976

File tree

11 files changed

+495
-30
lines changed

11 files changed

+495
-30
lines changed

packages/@aws-cdk/toolkit-lib/lib/api/cloud-assembly/private/context-aware-source.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import type { MissingContext } from '@aws-cdk/cloud-assembly-schema';
21
import * as contextproviders from '../../../context-providers';
32
import type { ToolkitServices } from '../../../toolkit/private';
43
import { ToolkitError } from '../../../toolkit/toolkit-error';
54
import type { IoHelper } from '../../io/private';
65
import { IO } from '../../io/private';
76
import type { IContextStore } from '../context-store';
87
import type { ICloudAssemblySource, IReadableCloudAssembly } from '../types';
8+
import { missingContextKeys } from './helpers';
99

1010
export interface ContextAwareCloudAssemblyProps {
1111
/**
@@ -115,13 +115,6 @@ export class ContextAwareCloudAssemblySource implements ICloudAssemblySource {
115115
}
116116
}
117117

118-
/**
119-
* Return all keys of missing context items
120-
*/
121-
function missingContextKeys(missing?: MissingContext[]): Set<string> {
122-
return new Set((missing || []).map(m => m.key));
123-
}
124-
125118
/**
126119
* Are two sets equal to each other
127120
*/

packages/@aws-cdk/toolkit-lib/lib/api/cloud-assembly/private/helpers.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { MissingContext } from '@aws-cdk/cloud-assembly-schema';
12
import { ToolkitError } from '../../../toolkit/toolkit-error';
23

34
/**
@@ -42,3 +43,10 @@ function detectSynthvarConflicts<A extends object>(obj: A) {
4243
},
4344
});
4445
}
46+
47+
/**
48+
* Return all keys of missing context items
49+
*/
50+
export function missingContextKeys(missing?: MissingContext[]): Set<string> {
51+
return new Set((missing || []).map((m) => m.key));
52+
}

packages/@aws-cdk/toolkit-lib/lib/api/cloud-assembly/source-builder.ts

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import * as cxapi from '@aws-cdk/cx-api';
55
import * as fs from 'fs-extra';
66
import { CdkAppMultiContext, MemoryContext, type IContextStore } from './context-store';
77
import { RWLock } from '../rwlock';
8+
import { CachedCloudAssembly } from './cached-source';
89
import type { ContextAwareCloudAssemblyProps } from './private/context-aware-source';
910
import { ContextAwareCloudAssemblySource } from './private/context-aware-source';
1011
import { execInChildProcess } from './private/exec';
@@ -15,7 +16,7 @@ import type { ToolkitServices } from '../../toolkit/private';
1516
import { ToolkitError, AssemblyError } from '../../toolkit/toolkit-error';
1617
import { noUndefined } from '../../util';
1718
import { IO } from '../io/private';
18-
import { temporarilyWriteEnv } from './private/helpers';
19+
import { missingContextKeys, temporarilyWriteEnv } from './private/helpers';
1920

2021
/**
2122
* Properties the builder function receives.
@@ -61,6 +62,14 @@ export interface AssemblyDirectoryProps {
6162
* Options to configure loading of the assembly after it has been synthesized
6263
*/
6364
readonly loadAssemblyOptions?: LoadAssemblyOptions;
65+
66+
/**
67+
* Whether or not to fail if the synthesized assembly contains
68+
* missing context
69+
*
70+
* @default true
71+
*/
72+
readonly failOnMissingContext?: boolean;
6473
}
6574

6675
/**
@@ -379,30 +388,33 @@ export abstract class CloudAssemblySourceBuilder {
379388
*/
380389
public async fromAssemblyDirectory(directory: string, props: AssemblyDirectoryProps = {}): Promise<ICloudAssemblySource> {
381390
const services: ToolkitServices = await this.sourceBuilderServices();
382-
const contextAssemblyProps: ContextAwareCloudAssemblyProps = {
383-
services,
384-
contextStore: new MemoryContext(), // @todo We shouldn't be using a `ContextAwareCloudAssemblySource` at all.
385-
lookups: false,
386-
};
387391

388-
return new ContextAwareCloudAssemblySource(
389-
{
390-
produce: async () => {
391-
// @todo build
392-
await services.ioHelper.notify(IO.CDK_ASSEMBLY_I0150.msg('--app points to a cloud assembly, so we bypass synth'));
393-
394-
const readLock = await new RWLock(directory).acquireRead();
395-
try {
396-
const asm = await assemblyFromDirectory(directory, services.ioHelper, props.loadAssemblyOptions);
397-
return new ReadableCloudAssembly(asm, readLock, { deleteOnDispose: false });
398-
} catch (e) {
399-
await readLock.release();
400-
throw e;
392+
return {
393+
async produce() {
394+
await services.ioHelper.notify(IO.CDK_ASSEMBLY_I0150.msg('--app points to a cloud assembly, so we bypass synth'));
395+
const readLock = await new RWLock(directory).acquireRead();
396+
try {
397+
const asm = await assemblyFromDirectory(directory, services.ioHelper, props.loadAssemblyOptions);
398+
const assembly = new ReadableCloudAssembly(asm, readLock, { deleteOnDispose: false });
399+
if (assembly.cloudAssembly.manifest.missing && assembly.cloudAssembly.manifest.missing.length > 0) {
400+
if (props.failOnMissingContext ?? true) {
401+
const missingKeysSet = missingContextKeys(assembly.cloudAssembly.manifest.missing);
402+
const missingKeys = Array.from(missingKeysSet);
403+
throw AssemblyError.withCause(
404+
'Assembly contains missing context. ' +
405+
"Make sure all necessary context is already in 'cdk.context.json' by running 'cdk synth' on a machine with sufficient AWS credentials and committing the result. " +
406+
`Missing context keys: '${missingKeys.join(', ')}'`,
407+
'Error producing assembly',
408+
);
409+
}
401410
}
402-
},
411+
return new CachedCloudAssembly(assembly);
412+
} catch (e) {
413+
await readLock.release();
414+
throw e;
415+
}
403416
},
404-
contextAssemblyProps,
405-
);
417+
};
406418
}
407419
/**
408420
* Use a directory containing an AWS CDK app as source.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"version": "40.0.0",
3+
"files": {
4+
"7f4fb64a3afca08edbdbaa369e00317cb253697278406a83c78e849e89045f9d": {
5+
"source": {
6+
"path": "StageStack130339B27.template.json",
7+
"packaging": "file"
8+
},
9+
"destinations": {
10+
"current_account-current_region": {
11+
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
12+
"objectKey": "7f4fb64a3afca08edbdbaa369e00317cb253697278406a83c78e849e89045f9d.json",
13+
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
14+
}
15+
}
16+
}
17+
},
18+
"dockerImages": {}
19+
}

0 commit comments

Comments
 (0)