generated from amazon-archives/__template_Apache-2.0
-
Notifications
You must be signed in to change notification settings - Fork 80
Expand file tree
/
Copy pathcontext-aware-source.ts
More file actions
131 lines (114 loc) · 4.27 KB
/
context-aware-source.ts
File metadata and controls
131 lines (114 loc) · 4.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import type { MissingContext } from '@aws-cdk/cloud-assembly-schema';
import * as cxapi from '@aws-cdk/cx-api';
import { ToolkitServices } from '../../../toolkit/private';
import { type Context, contextproviders, PROJECT_CONTEXT } from '../../aws-cdk';
import { ToolkitError } from '../../errors';
import { ActionAwareIoHost, CODES, debug } from '../../io/private';
import { ICloudAssemblySource } from '../types';
export interface ContextAwareCloudAssemblyProps {
/**
* AWS object (used by contextprovider)
* @deprecated context should be moved to the toolkit itself
*/
readonly services: ToolkitServices;
/**
* Application context
*/
readonly context: Context;
/**
* The file used to store application context in (relative to cwd).
*
* @default "cdk.context.json"
*/
readonly contextFile?: string;
/**
* Enable context lookups.
*
* Producing a `cxapi.CloudAssembly` will fail if this is disabled and context lookups need to be performed.
*
* @default true
*/
readonly lookups?: boolean;
}
/**
* Represent the Cloud Executable and the synthesis we can do on it
*/
export class ContextAwareCloudAssembly implements ICloudAssemblySource {
private canLookup: boolean;
private context: Context;
private contextFile: string;
private ioHost: ActionAwareIoHost;
constructor(private readonly source: ICloudAssemblySource, private readonly props: ContextAwareCloudAssemblyProps) {
this.canLookup = props.lookups ?? true;
this.context = props.context;
this.contextFile = props.contextFile ?? PROJECT_CONTEXT; // @todo new feature not needed right now
this.ioHost = props.services.ioHost;
}
/**
* Produce a Cloud Assembly, i.e. a set of stacks
*/
public async produce(): Promise<cxapi.CloudAssembly> {
// We may need to run the cloud assembly source multiple times in order to satisfy all missing context
// (When the source producer runs, it will tell us about context it wants to use
// but it missing. We'll then look up the context and run the executable again, and
// again, until it doesn't complain anymore or we've stopped making progress).
let previouslyMissingKeys: Set<string> | undefined;
while (true) {
const assembly = await this.source.produce();
if (assembly.manifest.missing && assembly.manifest.missing.length > 0) {
const missingKeys = missingContextKeys(assembly.manifest.missing);
if (!this.canLookup) {
throw new ToolkitError(
'Context lookups have been disabled. '
+ '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. '
+ `Missing context keys: '${Array.from(missingKeys).join(', ')}'`);
}
let tryLookup = true;
if (previouslyMissingKeys && equalSets(missingKeys, previouslyMissingKeys)) {
await this.ioHost.notify(debug('Not making progress trying to resolve environmental context. Giving up.'));
tryLookup = false;
}
previouslyMissingKeys = missingKeys;
if (tryLookup) {
await this.ioHost.notify(debug('Some context information is missing. Fetching...', CODES.CDK_ASSEMBLY_I0241, {
missingKeys: Array.from(missingKeys),
}));
await contextproviders.provideContextValues(
assembly.manifest.missing,
this.context,
this.props.services.sdkProvider,
);
// Cache the new context to disk
await this.ioHost.notify(debug(`Writing updated context to ${this.contextFile}...`, CODES.CDK_ASSEMBLY_I0042, {
contextFile: this.contextFile,
context: this.context.all,
}));
await this.context.save(this.contextFile);
// Execute again
continue;
}
}
return assembly;
}
}
}
/**
* Return all keys of missing context items
*/
function missingContextKeys(missing?: MissingContext[]): Set<string> {
return new Set((missing || []).map(m => m.key));
}
/**
* Are two sets equal to each other
*/
function equalSets<A>(a: Set<A>, b: Set<A>) {
if (a.size !== b.size) {
return false;
}
for (const x of a) {
if (!b.has(x)) {
return false;
}
}
return true;
}