@terraforge/core 0.0.6 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +3 -2
- package/dist/index.js +111 -86
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -58,12 +58,13 @@ declare const isDataSource: (obj: object) => obj is DataSource;
|
|
|
58
58
|
type ResourceConfig = Config & {
|
|
59
59
|
/** Import an existing resource instead of creating a new resource. */
|
|
60
60
|
import?: string;
|
|
61
|
-
/** If true the resource will be retained in the backing cloud provider during a
|
|
61
|
+
/** If true the resource will be retained in the backing cloud provider during a delete operation. */
|
|
62
62
|
retainOnDelete?: boolean;
|
|
63
|
-
/** Override the default create-after-delete behavior when replacing a resource. */
|
|
64
63
|
/** If set, the provider’s Delete method will not be called for this resource if the specified resource is being deleted as well. */
|
|
65
64
|
/** Declare that changes to certain properties should be treated as forcing a replacement. */
|
|
66
65
|
replaceOnChanges?: string[];
|
|
66
|
+
/** If true, create the replacement before deleting the existing resource. */
|
|
67
|
+
createBeforeReplace?: boolean;
|
|
67
68
|
};
|
|
68
69
|
type ResourceMeta<I extends State = State, O extends State = State> = Meta<'resource', I, O, ResourceConfig>;
|
|
69
70
|
type Resource<I extends State = State, O extends State = State> = O & {
|
package/dist/index.js
CHANGED
|
@@ -420,6 +420,9 @@ var DependencyGraph = class {
|
|
|
420
420
|
this.callbacks.set(urn, callback);
|
|
421
421
|
this.graph.mergeNode(urn);
|
|
422
422
|
for (const dep of deps) {
|
|
423
|
+
if (!dep) {
|
|
424
|
+
throw new Error(`Resource ${urn} has an undefined dependency.`);
|
|
425
|
+
}
|
|
423
426
|
if (willCreateCycle(this.graph, dep, urn)) {
|
|
424
427
|
throw new Error(`There is a circular dependency between ${urn} -> ${dep}`);
|
|
425
428
|
}
|
|
@@ -1030,6 +1033,7 @@ var deployApp = async (app, opt) => {
|
|
|
1030
1033
|
}
|
|
1031
1034
|
const queue = createConcurrencyQueue(opt.concurrency ?? 10);
|
|
1032
1035
|
const graph = new DependencyGraph();
|
|
1036
|
+
const replacementDeletes = /* @__PURE__ */ new Map();
|
|
1033
1037
|
const allNodes = {};
|
|
1034
1038
|
for (const stackState of Object.values(appState.stacks)) {
|
|
1035
1039
|
for (const [urn, nodeState] of entries(stackState.nodes)) {
|
|
@@ -1177,99 +1181,107 @@ var deployApp = async (app, opt) => {
|
|
|
1177
1181
|
let newResourceState;
|
|
1178
1182
|
const ignoreReplace = forcedUpdateDependents.has(meta2.urn);
|
|
1179
1183
|
if (!ignoreReplace && requiresReplacement(nodeState.input, input, meta2.config?.replaceOnChanges ?? [])) {
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
if (!dependentMeta.dependencies.has(meta2.urn)) {
|
|
1186
|
-
continue;
|
|
1187
|
-
}
|
|
1188
|
-
if (plannedDependents.has(dependentUrn)) {
|
|
1189
|
-
continue;
|
|
1190
|
-
}
|
|
1191
|
-
const dependentStackState = stackStates.get(dependentMeta.stack.urn);
|
|
1192
|
-
const dependentState = dependentStackState?.nodes[dependentUrn];
|
|
1193
|
-
if (!dependentStackState || !dependentState) {
|
|
1194
|
-
continue;
|
|
1195
|
-
}
|
|
1196
|
-
const dependencyPaths = findDependencyPaths(dependentMeta.input, meta2.urn);
|
|
1197
|
-
if (dependencyPaths.length === 0) {
|
|
1198
|
-
continue;
|
|
1184
|
+
if (meta2.config?.createBeforeReplace) {
|
|
1185
|
+
const priorState = { ...nodeState };
|
|
1186
|
+
newResourceState = await createResource(node, appState.idempotentToken, input, opt);
|
|
1187
|
+
if (!meta2.config?.retainOnDelete) {
|
|
1188
|
+
replacementDeletes.set(meta2.urn, priorState);
|
|
1199
1189
|
}
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
);
|
|
1205
|
-
if (compareState(dependentState.input, detachedInput)) {
|
|
1206
|
-
continue;
|
|
1207
|
-
}
|
|
1208
|
-
plannedDependents.add(dependentUrn);
|
|
1209
|
-
let dependentRequiresReplacement = false;
|
|
1210
|
-
const dependentProvider = findProvider(opt.providers, dependentMeta.provider);
|
|
1211
|
-
if (dependentProvider.planResourceChange) {
|
|
1212
|
-
try {
|
|
1213
|
-
const dependentPlan = await dependentProvider.planResourceChange({
|
|
1214
|
-
type: dependentMeta.type,
|
|
1215
|
-
priorState: dependentState.output,
|
|
1216
|
-
proposedState: detachedInput
|
|
1217
|
-
});
|
|
1218
|
-
dependentRequiresReplacement = dependentPlan.requiresReplacement;
|
|
1219
|
-
} catch (error) {
|
|
1220
|
-
throw ResourceError.wrap(
|
|
1221
|
-
dependentMeta.urn,
|
|
1222
|
-
dependentMeta.type,
|
|
1223
|
-
"update",
|
|
1224
|
-
error
|
|
1225
|
-
);
|
|
1190
|
+
} else {
|
|
1191
|
+
for (const [dependentUrn, dependentNode] of nodeByUrn.entries()) {
|
|
1192
|
+
if (!isResource(dependentNode)) {
|
|
1193
|
+
continue;
|
|
1226
1194
|
}
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
dependentMeta.config?.replaceOnChanges,
|
|
1231
|
-
dependencyPaths
|
|
1232
|
-
)) {
|
|
1233
|
-
throw ResourceError.wrap(
|
|
1234
|
-
dependentMeta.urn,
|
|
1235
|
-
dependentMeta.type,
|
|
1236
|
-
"update",
|
|
1237
|
-
new Error(
|
|
1238
|
-
`Replacing ${meta2.urn} requires ${dependentMeta.urn} to set replaceOnChanges for its dependency fields.`
|
|
1239
|
-
)
|
|
1240
|
-
);
|
|
1195
|
+
const dependentMeta = getMeta(dependentNode);
|
|
1196
|
+
if (!dependentMeta.dependencies.has(meta2.urn)) {
|
|
1197
|
+
continue;
|
|
1241
1198
|
}
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
)
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
const
|
|
1251
|
-
|
|
1252
|
-
|
|
1199
|
+
if (plannedDependents.has(dependentUrn)) {
|
|
1200
|
+
continue;
|
|
1201
|
+
}
|
|
1202
|
+
const dependentStackState = stackStates.get(dependentMeta.stack.urn);
|
|
1203
|
+
const dependentState = dependentStackState?.nodes[dependentUrn];
|
|
1204
|
+
if (!dependentStackState || !dependentState) {
|
|
1205
|
+
continue;
|
|
1206
|
+
}
|
|
1207
|
+
const dependencyPaths = findDependencyPaths(dependentMeta.input, meta2.urn);
|
|
1208
|
+
if (dependencyPaths.length === 0) {
|
|
1209
|
+
continue;
|
|
1210
|
+
}
|
|
1211
|
+
const detachedInput = stripDependencyInputs(
|
|
1253
1212
|
dependentState.input,
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
opt
|
|
1213
|
+
dependentMeta.input,
|
|
1214
|
+
meta2.urn
|
|
1257
1215
|
);
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1216
|
+
if (compareState(dependentState.input, detachedInput)) {
|
|
1217
|
+
continue;
|
|
1218
|
+
}
|
|
1219
|
+
plannedDependents.add(dependentUrn);
|
|
1220
|
+
let dependentRequiresReplacement = false;
|
|
1221
|
+
const dependentProvider = findProvider(opt.providers, dependentMeta.provider);
|
|
1222
|
+
if (dependentProvider.planResourceChange) {
|
|
1223
|
+
try {
|
|
1224
|
+
const dependentPlan = await dependentProvider.planResourceChange({
|
|
1225
|
+
type: dependentMeta.type,
|
|
1226
|
+
priorState: dependentState.output,
|
|
1227
|
+
proposedState: detachedInput
|
|
1228
|
+
});
|
|
1229
|
+
dependentRequiresReplacement = dependentPlan.requiresReplacement;
|
|
1230
|
+
} catch (error) {
|
|
1231
|
+
throw ResourceError.wrap(
|
|
1232
|
+
dependentMeta.urn,
|
|
1233
|
+
dependentMeta.type,
|
|
1234
|
+
"update",
|
|
1235
|
+
error
|
|
1236
|
+
);
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
if (dependentRequiresReplacement) {
|
|
1240
|
+
if (!allowsDependentReplace(
|
|
1241
|
+
dependentMeta.config?.replaceOnChanges,
|
|
1242
|
+
dependencyPaths
|
|
1243
|
+
)) {
|
|
1244
|
+
throw ResourceError.wrap(
|
|
1245
|
+
dependentMeta.urn,
|
|
1246
|
+
dependentMeta.type,
|
|
1247
|
+
"update",
|
|
1248
|
+
new Error(
|
|
1249
|
+
`Replacing ${meta2.urn} requires ${dependentMeta.urn} to set replaceOnChanges for its dependency fields.`
|
|
1250
|
+
)
|
|
1251
|
+
);
|
|
1252
|
+
}
|
|
1253
|
+
await deleteResource(
|
|
1254
|
+
appState.idempotentToken,
|
|
1255
|
+
dependentUrn,
|
|
1256
|
+
dependentState,
|
|
1257
|
+
opt
|
|
1258
|
+
);
|
|
1259
|
+
delete dependentStackState.nodes[dependentUrn];
|
|
1260
|
+
} else {
|
|
1261
|
+
const updated = await updateResource(
|
|
1262
|
+
dependentNode,
|
|
1263
|
+
appState.idempotentToken,
|
|
1264
|
+
dependentState.input,
|
|
1265
|
+
dependentState.output,
|
|
1266
|
+
detachedInput,
|
|
1267
|
+
opt
|
|
1268
|
+
);
|
|
1269
|
+
Object.assign(dependentState, {
|
|
1270
|
+
input: detachedInput,
|
|
1271
|
+
...updated
|
|
1272
|
+
});
|
|
1273
|
+
forcedUpdateDependents.add(dependentUrn);
|
|
1274
|
+
}
|
|
1263
1275
|
}
|
|
1276
|
+
newResourceState = await replaceResource(
|
|
1277
|
+
node,
|
|
1278
|
+
appState.idempotentToken,
|
|
1279
|
+
nodeState.input,
|
|
1280
|
+
nodeState.output,
|
|
1281
|
+
input,
|
|
1282
|
+
opt
|
|
1283
|
+
);
|
|
1264
1284
|
}
|
|
1265
|
-
newResourceState = await replaceResource(
|
|
1266
|
-
node,
|
|
1267
|
-
appState.idempotentToken,
|
|
1268
|
-
nodeState.input,
|
|
1269
|
-
nodeState.output,
|
|
1270
|
-
input,
|
|
1271
|
-
opt
|
|
1272
|
-
);
|
|
1273
1285
|
} else {
|
|
1274
1286
|
newResourceState = await updateResource(
|
|
1275
1287
|
node,
|
|
@@ -1300,6 +1312,19 @@ var deployApp = async (app, opt) => {
|
|
|
1300
1312
|
}
|
|
1301
1313
|
}
|
|
1302
1314
|
const errors = await graph.run();
|
|
1315
|
+
if (errors.length === 0 && replacementDeletes.size > 0) {
|
|
1316
|
+
for (const [urn, nodeState] of replacementDeletes.entries()) {
|
|
1317
|
+
try {
|
|
1318
|
+
await deleteResource(appState.idempotentToken, urn, nodeState, opt);
|
|
1319
|
+
} catch (error) {
|
|
1320
|
+
if (error instanceof Error) {
|
|
1321
|
+
errors.push(error);
|
|
1322
|
+
} else {
|
|
1323
|
+
errors.push(new Error(`${error}`));
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1303
1328
|
removeEmptyStackStates(appState);
|
|
1304
1329
|
delete appState.idempotentToken;
|
|
1305
1330
|
await opt.backend.state.update(app.urn, appState);
|