@highstate/backend 0.19.1 → 0.21.1
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/chunk-b05q6fm2.js +37 -0
- package/dist/{chunk-V2NILDHS.js → chunk-gxjwa93h.js} +704 -604
- package/dist/{chunk-X2WG3WGL.js → chunk-vzdz6chj.js} +18 -15
- package/dist/highstate.manifest.json +4 -4
- package/dist/index.js +7350 -3514
- package/dist/library/package-resolution-worker.js +121 -10
- package/dist/library/worker/main.js +31 -17
- package/dist/shared/index.js +254 -4
- package/package.json +19 -20
- package/prisma/backend/_schema/object.prisma +12 -0
- package/prisma/backend/sqlite/migrations/20260222113554_add_object_tracking/migration.sql +7 -0
- package/prisma/project/artifact.prisma +3 -0
- package/prisma/project/entity.prisma +125 -0
- package/prisma/project/instance.prisma +6 -0
- package/prisma/project/migrations/20260301210131_add_entity_tracking/migration.sql +70 -0
- package/prisma/project/migrations/20260302212734_add_resource_hooks_flag/migration.sql +1 -0
- package/prisma/project/operation.prisma +3 -0
- package/src/artifact/factory.ts +3 -2
- package/src/business/artifact.test.ts +22 -2
- package/src/business/artifact.ts +7 -1
- package/src/business/entity-snapshot.test.ts +684 -0
- package/src/business/entity-snapshot.ts +904 -0
- package/src/business/evaluation.test.ts +56 -0
- package/src/business/evaluation.ts +102 -22
- package/src/business/global-search.test.ts +344 -0
- package/src/business/global-search.ts +902 -0
- package/src/business/index.ts +4 -0
- package/src/business/instance-lock.ts +58 -74
- package/src/business/instance-state.test.ts +15 -1
- package/src/business/instance-state.ts +37 -14
- package/src/business/object-ref-index.test.ts +140 -0
- package/src/business/object-ref-index.ts +193 -0
- package/src/business/operation.test.ts +15 -1
- package/src/business/operation.ts +4 -0
- package/src/business/project-model.ts +154 -13
- package/src/business/project-unlock.ts +25 -2
- package/src/business/project.ts +9 -0
- package/src/business/secret.test.ts +35 -2
- package/src/business/secret.ts +32 -9
- package/src/business/settings.ts +761 -0
- package/src/business/unit-output.test.ts +477 -0
- package/src/business/unit-output.ts +461 -0
- package/src/business/worker.ts +55 -4
- package/src/database/_generated/backend/postgresql/browser.ts +6 -0
- package/src/database/_generated/backend/postgresql/client.ts +6 -0
- package/src/database/_generated/backend/postgresql/internal/class.ts +23 -5
- package/src/database/_generated/backend/postgresql/internal/prismaNamespace.ts +89 -5
- package/src/database/_generated/backend/postgresql/internal/prismaNamespaceBrowser.ts +9 -0
- package/src/database/_generated/backend/postgresql/models/Object.ts +1076 -0
- package/src/database/_generated/backend/postgresql/models.ts +1 -0
- package/src/database/_generated/backend/sqlite/browser.ts +6 -0
- package/src/database/_generated/backend/sqlite/client.ts +6 -0
- package/src/database/_generated/backend/sqlite/internal/class.ts +23 -5
- package/src/database/_generated/backend/sqlite/internal/prismaNamespace.ts +89 -5
- package/src/database/_generated/backend/sqlite/internal/prismaNamespaceBrowser.ts +9 -0
- package/src/database/_generated/backend/sqlite/models/Object.ts +1074 -0
- package/src/database/_generated/backend/sqlite/models.ts +1 -0
- package/src/database/_generated/project/browser.ts +23 -0
- package/src/database/_generated/project/client.ts +23 -0
- package/src/database/_generated/project/commonInputTypes.ts +87 -53
- package/src/database/_generated/project/enums.ts +8 -0
- package/src/database/_generated/project/internal/class.ts +53 -5
- package/src/database/_generated/project/internal/prismaNamespace.ts +367 -13
- package/src/database/_generated/project/internal/prismaNamespaceBrowser.ts +48 -1
- package/src/database/_generated/project/models/Artifact.ts +199 -11
- package/src/database/_generated/project/models/Entity.ts +1274 -0
- package/src/database/_generated/project/models/EntitySnapshot.ts +2389 -0
- package/src/database/_generated/project/models/EntitySnapshotContent.ts +1260 -0
- package/src/database/_generated/project/models/EntitySnapshotReference.ts +1449 -0
- package/src/database/_generated/project/models/InstanceState.ts +361 -1
- package/src/database/_generated/project/models/Operation.ts +148 -3
- package/src/database/_generated/project/models/OperationLog.ts +0 -4
- package/src/database/_generated/project/models.ts +4 -0
- package/src/database/migration.ts +3 -0
- package/src/library/find-package-json.test.ts +77 -0
- package/src/library/find-package-json.ts +149 -0
- package/src/library/package-resolution-worker.ts +7 -3
- package/src/library/worker/evaluator.ts +7 -1
- package/src/orchestrator/manager.ts +7 -0
- package/src/orchestrator/operation-context.captured-outputs.test.ts +118 -0
- package/src/orchestrator/operation-context.ts +154 -16
- package/src/orchestrator/operation-plan.destroy.test.md +33 -12
- package/src/orchestrator/operation-plan.destroy.test.ts +140 -2
- package/src/orchestrator/operation-plan.fixtures.ts +2 -0
- package/src/orchestrator/operation-plan.md +4 -1
- package/src/orchestrator/operation-plan.ts +286 -92
- package/src/orchestrator/operation-plan.update.test.md +286 -11
- package/src/orchestrator/operation-plan.update.test.ts +656 -5
- package/src/orchestrator/operation-workset.ts +72 -22
- package/src/orchestrator/operation.cancel.test.ts +4 -0
- package/src/orchestrator/operation.composite.test.ts +341 -0
- package/src/orchestrator/operation.destroy.test.ts +4 -0
- package/src/orchestrator/operation.output-validation.failure.test.ts +124 -0
- package/src/orchestrator/operation.preview.test.ts +4 -0
- package/src/orchestrator/operation.refresh.test.ts +4 -0
- package/src/orchestrator/operation.test-utils.ts +52 -13
- package/src/orchestrator/operation.ts +230 -68
- package/src/orchestrator/operation.update.failure.test.ts +4 -0
- package/src/orchestrator/operation.update.skip.test.ts +196 -0
- package/src/orchestrator/operation.update.test.ts +4 -0
- package/src/orchestrator/plan-test-builder.ts +1 -0
- package/src/orchestrator/unit-input-values.test.ts +450 -0
- package/src/orchestrator/unit-input-values.ts +281 -0
- package/src/pubsub/manager.ts +3 -0
- package/src/runner/abstractions.ts +23 -54
- package/src/runner/factory.ts +3 -3
- package/src/runner/force-abort.ts +7 -2
- package/src/runner/local.ts +116 -87
- package/src/runner/pulumi.ts +3 -5
- package/src/services.ts +53 -2
- package/src/shared/models/prisma.ts +1 -0
- package/src/shared/models/project/entity.ts +121 -0
- package/src/shared/models/project/index.ts +1 -0
- package/src/shared/models/project/operation.ts +61 -3
- package/src/shared/models/project/state.ts +10 -0
- package/src/shared/models/project/worker.ts +7 -0
- package/src/shared/resolvers/effective-output-type.test.ts +494 -0
- package/src/shared/resolvers/effective-output-type.ts +162 -0
- package/src/shared/resolvers/index.ts +1 -0
- package/src/shared/resolvers/input.ts +59 -9
- package/src/shared/utils/index.ts +1 -0
- package/src/shared/utils/stable-json.ts +41 -0
- package/src/terminal/manager.ts +6 -0
- package/src/terminal/run.sh.ts +9 -4
- package/src/worker/manager.ts +97 -1
- package/LICENSE +0 -21
- package/dist/chunk-I7BWSAN6.js +0 -49
- package/dist/chunk-I7BWSAN6.js.map +0 -1
- package/dist/chunk-V2NILDHS.js.map +0 -1
- package/dist/chunk-X2WG3WGL.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/library/package-resolution-worker.js.map +0 -1
- package/dist/library/worker/main.js.map +0 -1
- package/dist/shared/index.js.map +0 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Logger } from "pino"
|
|
2
2
|
import type { ArtifactService } from "../artifact"
|
|
3
3
|
import type {
|
|
4
|
+
EntitySnapshotService,
|
|
4
5
|
InstanceLockService,
|
|
5
6
|
InstanceStatePatch,
|
|
6
7
|
InstanceStateService,
|
|
@@ -8,6 +9,7 @@ import type {
|
|
|
8
9
|
ProjectModelService,
|
|
9
10
|
SecretService,
|
|
10
11
|
UnitExtraService,
|
|
12
|
+
UnitOutputService,
|
|
11
13
|
} from "../business"
|
|
12
14
|
import type { Operation, OperationUpdateInput, Project } from "../database"
|
|
13
15
|
import type { LibraryBackend } from "../library"
|
|
@@ -18,7 +20,7 @@ import {
|
|
|
18
20
|
parseInstanceId,
|
|
19
21
|
type TriggerInvocation,
|
|
20
22
|
type UnitConfig,
|
|
21
|
-
type
|
|
23
|
+
type UnitInputValue,
|
|
22
24
|
type VersionedName,
|
|
23
25
|
} from "@highstate/contract"
|
|
24
26
|
import { createId } from "@paralleldrive/cuid2"
|
|
@@ -30,15 +32,18 @@ import {
|
|
|
30
32
|
type OperationPhase,
|
|
31
33
|
PromiseTracker,
|
|
32
34
|
type ResolvedInstanceInput,
|
|
35
|
+
resolveEffectiveOutputType,
|
|
33
36
|
waitAll,
|
|
34
37
|
} from "../shared"
|
|
35
38
|
import { OperationContext } from "./operation-context"
|
|
36
39
|
import { createOperationPlan } from "./operation-plan"
|
|
37
40
|
import { OperationWorkset } from "./operation-workset"
|
|
41
|
+
import { resolveUnitInputValues } from "./unit-input-values"
|
|
38
42
|
|
|
39
43
|
export class RuntimeOperation {
|
|
40
44
|
private readonly instancePromiseMap = new Map<InstanceId, Promise<void>>()
|
|
41
45
|
private readonly promiseTracker = new PromiseTracker()
|
|
46
|
+
private readonly pendingGhostDeletionIds = new Set<InstanceId>()
|
|
42
47
|
|
|
43
48
|
private workset!: OperationWorkset
|
|
44
49
|
private context!: OperationContext
|
|
@@ -57,6 +62,8 @@ export class RuntimeOperation {
|
|
|
57
62
|
private readonly instanceStateService: InstanceStateService,
|
|
58
63
|
private readonly projectModelService: ProjectModelService,
|
|
59
64
|
private readonly unitExtraService: UnitExtraService,
|
|
65
|
+
private readonly entitySnapshotService: EntitySnapshotService,
|
|
66
|
+
private readonly unitOutputService: UnitOutputService,
|
|
60
67
|
private readonly logger: Logger,
|
|
61
68
|
) {}
|
|
62
69
|
|
|
@@ -92,17 +99,21 @@ export class RuntimeOperation {
|
|
|
92
99
|
}
|
|
93
100
|
|
|
94
101
|
this.logger.error({ error }, "an error occurred while running the operation")
|
|
102
|
+
console.error(error)
|
|
95
103
|
|
|
96
104
|
await this.updateOperation({ status: "failed" })
|
|
97
105
|
await this.writeOperationLog(errorToString(error))
|
|
98
106
|
} finally {
|
|
99
107
|
try {
|
|
108
|
+
this.promiseTracker.track(this.flushGhostInstanceDeletions())
|
|
100
109
|
this.promiseTracker.track(this.ensureInstancesUnlocked())
|
|
101
110
|
this.promiseTracker.track(this.ensureOperationStatesFinalized())
|
|
102
111
|
|
|
103
112
|
// ensure that all promises are resolved even if the operation failed
|
|
104
113
|
await this.promiseTracker.waitForAll()
|
|
105
114
|
} catch (error) {
|
|
115
|
+
console.error(error)
|
|
116
|
+
|
|
106
117
|
this.logger.error(
|
|
107
118
|
{ error },
|
|
108
119
|
"one of the tracked promises failed after the operation failed",
|
|
@@ -111,6 +122,17 @@ export class RuntimeOperation {
|
|
|
111
122
|
}
|
|
112
123
|
}
|
|
113
124
|
|
|
125
|
+
private async flushGhostInstanceDeletions(): Promise<void> {
|
|
126
|
+
if (this.pendingGhostDeletionIds.size === 0) {
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const instanceIds = Array.from(this.pendingGhostDeletionIds)
|
|
131
|
+
this.pendingGhostDeletionIds.clear()
|
|
132
|
+
|
|
133
|
+
this.instanceStateService.publishGhostInstanceDeletion(this.project.id, instanceIds)
|
|
134
|
+
}
|
|
135
|
+
|
|
114
136
|
private async operate(): Promise<void> {
|
|
115
137
|
this.logger.info("starting operation")
|
|
116
138
|
|
|
@@ -120,6 +142,7 @@ export class RuntimeOperation {
|
|
|
120
142
|
this.libraryBackend,
|
|
121
143
|
this.instanceStateService,
|
|
122
144
|
this.projectModelService,
|
|
145
|
+
this.entitySnapshotService,
|
|
123
146
|
this.logger,
|
|
124
147
|
)
|
|
125
148
|
|
|
@@ -143,6 +166,7 @@ export class RuntimeOperation {
|
|
|
143
166
|
this.workset = new OperationWorkset(
|
|
144
167
|
this.project,
|
|
145
168
|
this.operation.id,
|
|
169
|
+
this.operation.type,
|
|
146
170
|
plan,
|
|
147
171
|
this.context,
|
|
148
172
|
this.instanceStateService,
|
|
@@ -205,7 +229,7 @@ export class RuntimeOperation {
|
|
|
205
229
|
|
|
206
230
|
// lauch all instances in this phase
|
|
207
231
|
for (const instanceId of this.workset.phaseAffectedInstanceIds) {
|
|
208
|
-
const instance = this.
|
|
232
|
+
const instance = this.getPhaseInstance(instanceId)
|
|
209
233
|
const state = this.context.getState(instanceId)
|
|
210
234
|
const promise = this.getInstancePromiseForOperation(instance, state)
|
|
211
235
|
|
|
@@ -291,7 +315,7 @@ export class RuntimeOperation {
|
|
|
291
315
|
const secrets = await this.secretService.getInstanceSecretValues(this.project.id, state.id)
|
|
292
316
|
signal.throwIfAborted()
|
|
293
317
|
|
|
294
|
-
const config = this.prepareUnitConfig(instance, secrets)
|
|
318
|
+
const config = this.prepareUnitConfig(instance, state.id, secrets)
|
|
295
319
|
const artifactIds = this.collectArtifactIdsForInstance(instance)
|
|
296
320
|
const artifacts = await this.artifactService.getArtifactsByIds(this.project.id, artifactIds)
|
|
297
321
|
|
|
@@ -299,13 +323,13 @@ export class RuntimeOperation {
|
|
|
299
323
|
|
|
300
324
|
await this.runnerBackend.preview({
|
|
301
325
|
projectId: this.project.id,
|
|
326
|
+
operationId: this.operation.id,
|
|
302
327
|
libraryId: this.project.libraryId,
|
|
303
328
|
stateId: state.id,
|
|
304
329
|
instanceType: instance.type,
|
|
305
330
|
instanceName: instance.name,
|
|
306
331
|
config,
|
|
307
332
|
refresh: this.operation.options.refresh,
|
|
308
|
-
secrets,
|
|
309
333
|
artifacts,
|
|
310
334
|
signal,
|
|
311
335
|
forceSignal,
|
|
@@ -351,7 +375,7 @@ export class RuntimeOperation {
|
|
|
351
375
|
for (const child of children) {
|
|
352
376
|
logger.debug(`waiting for child "%s"`, child)
|
|
353
377
|
|
|
354
|
-
const instance = this.
|
|
378
|
+
const instance = this.getPhaseInstance(child)
|
|
355
379
|
const state = this.context.getState(child)
|
|
356
380
|
const promise = this.getInstancePromiseForOperation(instance, state)
|
|
357
381
|
|
|
@@ -401,12 +425,15 @@ export class RuntimeOperation {
|
|
|
401
425
|
state.status === "deployed" &&
|
|
402
426
|
state.selfHash != null &&
|
|
403
427
|
state.dependencyOutputHash != null &&
|
|
428
|
+
// do not short-circuit after destroy phase in recreate operations
|
|
429
|
+
state.lastOperationState?.status !== "destroyed" &&
|
|
404
430
|
// ignore explicitly requested updates
|
|
405
431
|
!this.operation.requestedInstanceIds.includes(instance.id) &&
|
|
406
432
|
// ignore when side effects are requested
|
|
407
433
|
!this.operation.options.refresh &&
|
|
408
434
|
!this.operation.options.deleteUnreachableResources &&
|
|
409
|
-
!this.operation.options.forceUpdateDependencies
|
|
435
|
+
!this.operation.options.forceUpdateDependencies &&
|
|
436
|
+
!this.operation.options.forceUpdateChildren
|
|
410
437
|
) {
|
|
411
438
|
const expected = await this.context.getUpToDateInputHashOutput(instance)
|
|
412
439
|
|
|
@@ -426,6 +453,7 @@ export class RuntimeOperation {
|
|
|
426
453
|
},
|
|
427
454
|
instanceState: {
|
|
428
455
|
inputHash: expected.inputHash,
|
|
456
|
+
parentId: instance.parentId ? this.context.getState(instance.parentId).id : null,
|
|
429
457
|
},
|
|
430
458
|
})
|
|
431
459
|
|
|
@@ -448,7 +476,7 @@ export class RuntimeOperation {
|
|
|
448
476
|
|
|
449
477
|
signal.throwIfAborted()
|
|
450
478
|
|
|
451
|
-
const config = this.prepareUnitConfig(instance, secrets)
|
|
479
|
+
const config = this.prepareUnitConfig(instance, state.id, secrets)
|
|
452
480
|
|
|
453
481
|
// collect artifacts authorized for this instance
|
|
454
482
|
const artifactIds = this.collectArtifactIdsForInstance(instance)
|
|
@@ -458,6 +486,7 @@ export class RuntimeOperation {
|
|
|
458
486
|
|
|
459
487
|
await this.runnerBackend.update({
|
|
460
488
|
projectId: this.project.id,
|
|
489
|
+
operationId: this.operation.id,
|
|
461
490
|
libraryId: this.project.libraryId,
|
|
462
491
|
stateId: state.id,
|
|
463
492
|
instanceType: instance.type,
|
|
@@ -465,7 +494,6 @@ export class RuntimeOperation {
|
|
|
465
494
|
config,
|
|
466
495
|
refresh: this.operation.options.refresh,
|
|
467
496
|
deleteUnreachable: this.operation.options.deleteUnreachableResources,
|
|
468
|
-
secrets,
|
|
469
497
|
artifacts,
|
|
470
498
|
signal,
|
|
471
499
|
forceSignal,
|
|
@@ -541,14 +569,14 @@ export class RuntimeOperation {
|
|
|
541
569
|
|
|
542
570
|
await this.runnerBackend.update({
|
|
543
571
|
projectId: this.project.id,
|
|
572
|
+
operationId: this.operation.id,
|
|
544
573
|
stateId: state.id,
|
|
545
574
|
libraryId: this.project.libraryId,
|
|
546
575
|
instanceType: instance.type,
|
|
547
576
|
instanceName: instance.name,
|
|
548
|
-
config: this.prepareUnitConfig(instance, secrets, invokedTriggers),
|
|
577
|
+
config: this.prepareUnitConfig(instance, state.id, secrets, invokedTriggers),
|
|
549
578
|
refresh: this.operation.options.refresh,
|
|
550
579
|
deleteUnreachable: this.operation.options.deleteUnreachableResources,
|
|
551
|
-
secrets,
|
|
552
580
|
signal,
|
|
553
581
|
forceSignal,
|
|
554
582
|
debug: this.operation.options.debug,
|
|
@@ -571,7 +599,7 @@ export class RuntimeOperation {
|
|
|
571
599
|
continue
|
|
572
600
|
}
|
|
573
601
|
|
|
574
|
-
const instance = this.
|
|
602
|
+
const instance = this.getPhaseInstance(dependent.instanceId)
|
|
575
603
|
dependentPromises.push(this.getInstancePromiseForOperation(instance, dependent))
|
|
576
604
|
}
|
|
577
605
|
|
|
@@ -604,6 +632,7 @@ export class RuntimeOperation {
|
|
|
604
632
|
|
|
605
633
|
await this.runnerBackend.destroy({
|
|
606
634
|
projectId: this.project.id,
|
|
635
|
+
operationId: this.operation.id,
|
|
607
636
|
stateId: state.id,
|
|
608
637
|
libraryId: this.project.libraryId,
|
|
609
638
|
instanceType: type,
|
|
@@ -613,6 +642,7 @@ export class RuntimeOperation {
|
|
|
613
642
|
forceSignal,
|
|
614
643
|
deleteUnreachable: this.operation.options.deleteUnreachableResources,
|
|
615
644
|
forceDeleteState: this.operation.options.forceDeleteState,
|
|
645
|
+
hasResourceHooks: state.hasResourceHooks ?? false,
|
|
616
646
|
debug: this.operation.options.debug,
|
|
617
647
|
})
|
|
618
648
|
|
|
@@ -639,6 +669,7 @@ export class RuntimeOperation {
|
|
|
639
669
|
|
|
640
670
|
await this.runnerBackend.refresh({
|
|
641
671
|
projectId: this.project.id,
|
|
672
|
+
operationId: this.operation.id,
|
|
642
673
|
stateId: state.id,
|
|
643
674
|
libraryId: this.project.libraryId,
|
|
644
675
|
instanceType: type,
|
|
@@ -663,6 +694,7 @@ export class RuntimeOperation {
|
|
|
663
694
|
): Promise<void> {
|
|
664
695
|
const stream = this.runnerBackend.watch({
|
|
665
696
|
projectId: this.project.id,
|
|
697
|
+
operationId: this.operation.id,
|
|
666
698
|
stateId: state.id,
|
|
667
699
|
libraryId: this.project.libraryId,
|
|
668
700
|
instanceType,
|
|
@@ -673,12 +705,18 @@ export class RuntimeOperation {
|
|
|
673
705
|
let update: UnitStateUpdate | undefined
|
|
674
706
|
|
|
675
707
|
for await (update of stream) {
|
|
708
|
+
let handlerError: Error | null = null
|
|
709
|
+
|
|
676
710
|
try {
|
|
677
|
-
await this.handleUnitStateUpdate(update, state)
|
|
711
|
+
handlerError = await this.handleUnitStateUpdate(update, state)
|
|
678
712
|
} catch (error) {
|
|
679
713
|
logger.error({ error }, "failed to handle unit state update")
|
|
680
714
|
}
|
|
681
715
|
|
|
716
|
+
if (handlerError) {
|
|
717
|
+
throw handlerError
|
|
718
|
+
}
|
|
719
|
+
|
|
682
720
|
if (update.type === "error") {
|
|
683
721
|
// rethrow the error to stop the execution of dependent units
|
|
684
722
|
throw new Error(
|
|
@@ -698,80 +736,121 @@ export class RuntimeOperation {
|
|
|
698
736
|
|
|
699
737
|
private prepareUnitConfig(
|
|
700
738
|
instance: InstanceModel,
|
|
739
|
+
stateId: string,
|
|
701
740
|
secrets: Record<string, unknown>,
|
|
702
741
|
invokedTriggers: TriggerInvocation[] = [],
|
|
703
742
|
): UnitConfig {
|
|
704
743
|
const resolvedInputs = this.context.getResolvedInputs(instance.id)
|
|
744
|
+
const component = this.context.library.components[instance.type]!
|
|
745
|
+
|
|
746
|
+
const unfoldedInputs = mapValues(resolvedInputs ?? {}, (input, inputName) =>
|
|
747
|
+
input.flatMap(value => this.getUnitInputValues(inputName, value)),
|
|
748
|
+
)
|
|
749
|
+
|
|
750
|
+
for (const [inputName, inputSpec] of Object.entries(component.inputs)) {
|
|
751
|
+
if (inputSpec.multiple) {
|
|
752
|
+
continue
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
const values = unfoldedInputs[inputName] ?? []
|
|
756
|
+
if (values.length > 1) {
|
|
757
|
+
throw new Error(
|
|
758
|
+
`Input "${inputName}" of instance "${instance.id}" expects a single value, but ${values.length} values were resolved after unfolding.`,
|
|
759
|
+
)
|
|
760
|
+
}
|
|
761
|
+
}
|
|
705
762
|
|
|
706
763
|
return {
|
|
707
764
|
instanceId: instance.id,
|
|
765
|
+
stateId,
|
|
708
766
|
args: instance.args ?? {},
|
|
709
|
-
inputs:
|
|
710
|
-
input.map(value => this.getUnitInputRef(inputName, value)),
|
|
711
|
-
),
|
|
767
|
+
inputs: unfoldedInputs,
|
|
712
768
|
invokedTriggers,
|
|
713
|
-
|
|
714
|
-
stateIdMap: this.context.getInstanceIdToStateIdMap(instance.id),
|
|
769
|
+
secretValues: secrets,
|
|
715
770
|
importBasePath: this.libraryBackend.importPath,
|
|
716
771
|
}
|
|
717
772
|
}
|
|
718
773
|
|
|
719
|
-
private
|
|
720
|
-
const
|
|
721
|
-
const
|
|
774
|
+
private getUnitInputValues(inputName: string, input: ResolvedInstanceInput): UnitInputValue[] {
|
|
775
|
+
const dependencyInstance = this.context.getInstance(input.input.instanceId)
|
|
776
|
+
const captured = this.context.getCapturedOutputValues(
|
|
777
|
+
input.input.instanceId,
|
|
778
|
+
input.input.output,
|
|
779
|
+
)
|
|
722
780
|
|
|
723
|
-
const
|
|
724
|
-
|
|
725
|
-
throw new Error(
|
|
726
|
-
`Output "${input.input.output}" is not defined on component "${instance.type}"`,
|
|
727
|
-
)
|
|
728
|
-
}
|
|
781
|
+
const dependencyComponent = this.context.library.components[dependencyInstance.type]
|
|
782
|
+
const fallbackType = dependencyComponent?.outputs[input.input.output]?.type ?? input.type
|
|
729
783
|
|
|
730
|
-
const
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
784
|
+
const getInstanceContext = (instanceId: string) => {
|
|
785
|
+
const resolvedOutput = this.context.inputResolver.outputs.get(`instance:${instanceId}`)
|
|
786
|
+
if (resolvedOutput && resolvedOutput.kind === "instance") {
|
|
787
|
+
return {
|
|
788
|
+
instance: resolvedOutput.instance,
|
|
789
|
+
component: resolvedOutput.component,
|
|
790
|
+
entities: resolvedOutput.entities,
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
try {
|
|
795
|
+
const instance = this.context.getInstance(instanceId as InstanceId)
|
|
796
|
+
const component = this.context.library.components[instance.type]
|
|
734
797
|
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
798
|
+
if (!component) {
|
|
799
|
+
return undefined
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
return {
|
|
803
|
+
instance,
|
|
804
|
+
component,
|
|
805
|
+
entities: this.context.library.entities,
|
|
806
|
+
}
|
|
807
|
+
} catch {
|
|
808
|
+
return undefined
|
|
740
809
|
}
|
|
741
810
|
}
|
|
742
811
|
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
)
|
|
749
|
-
}
|
|
812
|
+
const effectiveOutputType = resolveEffectiveOutputType({
|
|
813
|
+
input: input.input,
|
|
814
|
+
fallbackType,
|
|
815
|
+
getInstanceContext,
|
|
816
|
+
})
|
|
750
817
|
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
818
|
+
const effectiveRootOutputType = resolveEffectiveOutputType({
|
|
819
|
+
input: {
|
|
820
|
+
...input.input,
|
|
821
|
+
path: undefined,
|
|
822
|
+
},
|
|
823
|
+
fallbackType,
|
|
824
|
+
getInstanceContext,
|
|
825
|
+
})
|
|
826
|
+
|
|
827
|
+
return resolveUnitInputValues({
|
|
828
|
+
library: this.context.library,
|
|
829
|
+
inputName,
|
|
830
|
+
resolvedInput: input,
|
|
831
|
+
dependencyInstanceType: dependencyInstance.type,
|
|
832
|
+
captured,
|
|
833
|
+
effectiveOutputType,
|
|
834
|
+
effectiveRootOutputType,
|
|
835
|
+
})
|
|
756
836
|
}
|
|
757
837
|
|
|
758
838
|
private async handleUnitStateUpdate(
|
|
759
839
|
update: UnitStateUpdate,
|
|
760
840
|
state: InstanceState,
|
|
761
|
-
): Promise<
|
|
841
|
+
): Promise<Error | null> {
|
|
762
842
|
switch (update.type) {
|
|
763
843
|
case "message":
|
|
764
844
|
this.handleUnitMessage(update, state)
|
|
765
|
-
return
|
|
845
|
+
return null
|
|
766
846
|
case "progress":
|
|
767
847
|
await this.handleUnitProgress(update)
|
|
768
|
-
return
|
|
848
|
+
return null
|
|
769
849
|
case "error":
|
|
770
850
|
await this.handleUnitError(update, state)
|
|
771
|
-
return
|
|
851
|
+
return null
|
|
772
852
|
case "completion":
|
|
773
|
-
await this.handleUnitCompletion(update, state)
|
|
774
|
-
return
|
|
853
|
+
return await this.handleUnitCompletion(update, state)
|
|
775
854
|
}
|
|
776
855
|
}
|
|
777
856
|
|
|
@@ -811,7 +890,7 @@ export class RuntimeOperation {
|
|
|
811
890
|
private async handleUnitCompletion(
|
|
812
891
|
update: TypedUnitStateUpdate<"completion">,
|
|
813
892
|
state: InstanceState,
|
|
814
|
-
): Promise<
|
|
893
|
+
): Promise<Error | null> {
|
|
815
894
|
if (this.operation.type === "preview") {
|
|
816
895
|
await this.workset.updateState(update.unitId, {
|
|
817
896
|
operationState: {
|
|
@@ -819,24 +898,75 @@ export class RuntimeOperation {
|
|
|
819
898
|
finishedAt: new Date(),
|
|
820
899
|
},
|
|
821
900
|
})
|
|
822
|
-
return
|
|
901
|
+
return null
|
|
823
902
|
}
|
|
824
903
|
|
|
825
|
-
const instance = this.
|
|
904
|
+
const instance = this.getPhaseInstance(update.unitId)
|
|
905
|
+
|
|
906
|
+
if (update.rawOutputs && update.operationType !== "destroy") {
|
|
907
|
+
this.context.updateCapturedOutputValuesFromUnitOutputs({
|
|
908
|
+
instanceId: instance.id,
|
|
909
|
+
instanceType: instance.type,
|
|
910
|
+
outputs: update.rawOutputs,
|
|
911
|
+
})
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
const parsed = update.rawOutputs
|
|
915
|
+
? await this.unitOutputService.parseUnitOutputs({
|
|
916
|
+
libraryId: this.context.project.libraryId,
|
|
917
|
+
instanceType: instance.type,
|
|
918
|
+
outputs: update.rawOutputs,
|
|
919
|
+
})
|
|
920
|
+
: {
|
|
921
|
+
outputHash: null,
|
|
922
|
+
statusFields: null,
|
|
923
|
+
terminals: null,
|
|
924
|
+
pages: null,
|
|
925
|
+
triggers: null,
|
|
926
|
+
secrets: null,
|
|
927
|
+
workers: null,
|
|
928
|
+
exportedArtifactIds: null,
|
|
929
|
+
hasResourceHooks: false,
|
|
930
|
+
entitySnapshotError: null,
|
|
931
|
+
entitySnapshotPayload: null,
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
if (parsed.entitySnapshotError) {
|
|
935
|
+
await this.operationService.appendLog(
|
|
936
|
+
this.project.id,
|
|
937
|
+
this.operation.id,
|
|
938
|
+
state.id,
|
|
939
|
+
`Failed to parse unit outputs: ${parsed.entitySnapshotError}`,
|
|
940
|
+
)
|
|
941
|
+
|
|
942
|
+
await this.workset.updateState(update.unitId, {
|
|
943
|
+
instanceState: {
|
|
944
|
+
status: "failed",
|
|
945
|
+
},
|
|
946
|
+
operationState: {
|
|
947
|
+
status: "failed",
|
|
948
|
+
finishedAt: new Date(),
|
|
949
|
+
},
|
|
950
|
+
})
|
|
951
|
+
|
|
952
|
+
return new Error(
|
|
953
|
+
`Failed to parse unit outputs for unit "${instance.id}": ${parsed.entitySnapshotError}`,
|
|
954
|
+
)
|
|
955
|
+
}
|
|
826
956
|
|
|
827
957
|
const data: InstanceStatePatch = {
|
|
828
958
|
status: this.workset.getNextStableInstanceStatus(instance.id),
|
|
829
|
-
statusFields:
|
|
959
|
+
statusFields: parsed.statusFields,
|
|
830
960
|
}
|
|
831
961
|
|
|
832
|
-
const artifactIds =
|
|
833
|
-
? Object.values(
|
|
962
|
+
const artifactIds = parsed.exportedArtifactIds
|
|
963
|
+
? Object.values(parsed.exportedArtifactIds).flat()
|
|
834
964
|
: []
|
|
835
965
|
|
|
836
966
|
if (update.operationType !== "destroy") {
|
|
837
967
|
// давайте еще больше усложним и без того сложную штуку
|
|
838
968
|
// set output hash before calculating input hash to capture up-to-date output hash for dependencies
|
|
839
|
-
state.outputHash =
|
|
969
|
+
state.outputHash = parsed.outputHash
|
|
840
970
|
|
|
841
971
|
// recalculate the input and output hashes for the instance
|
|
842
972
|
const { selfHash, inputHash, dependencyOutputHash } =
|
|
@@ -845,9 +975,13 @@ export class RuntimeOperation {
|
|
|
845
975
|
data.selfHash = selfHash
|
|
846
976
|
data.inputHash = inputHash
|
|
847
977
|
data.dependencyOutputHash = dependencyOutputHash
|
|
848
|
-
data.outputHash =
|
|
978
|
+
data.outputHash = parsed.outputHash
|
|
849
979
|
|
|
850
|
-
data.exportedArtifactIds =
|
|
980
|
+
data.exportedArtifactIds = parsed.exportedArtifactIds
|
|
981
|
+
|
|
982
|
+
if (update.rawOutputs) {
|
|
983
|
+
data.hasResourceHooks = parsed.hasResourceHooks
|
|
984
|
+
}
|
|
851
985
|
|
|
852
986
|
// also update the parent ID
|
|
853
987
|
if (instance.parentId) {
|
|
@@ -865,6 +999,7 @@ export class RuntimeOperation {
|
|
|
865
999
|
data.model = null
|
|
866
1000
|
data.resolvedInputs = null
|
|
867
1001
|
data.exportedArtifactIds = null
|
|
1002
|
+
data.hasResourceHooks = false
|
|
868
1003
|
}
|
|
869
1004
|
|
|
870
1005
|
// update the operation state
|
|
@@ -883,23 +1018,34 @@ export class RuntimeOperation {
|
|
|
883
1018
|
// also do not write unit extra data for non-last phases of the instance
|
|
884
1019
|
unitExtra: this.workset.isLastPhaseForInstance(instance.id)
|
|
885
1020
|
? {
|
|
886
|
-
pages:
|
|
887
|
-
terminals:
|
|
888
|
-
triggers:
|
|
889
|
-
workers:
|
|
890
|
-
secrets:
|
|
1021
|
+
pages: parsed.pages ?? [],
|
|
1022
|
+
terminals: parsed.terminals ?? [],
|
|
1023
|
+
triggers: parsed.triggers ?? [],
|
|
1024
|
+
workers: parsed.workers ?? [],
|
|
1025
|
+
secrets: parsed.secrets ?? {},
|
|
891
1026
|
artifactIds,
|
|
892
1027
|
}
|
|
893
1028
|
: undefined,
|
|
894
1029
|
})
|
|
895
1030
|
|
|
1031
|
+
if (update.operationType !== "destroy" && parsed.entitySnapshotPayload) {
|
|
1032
|
+
await this.entitySnapshotService.persistUnitEntitySnapshots({
|
|
1033
|
+
projectId: this.project.id,
|
|
1034
|
+
operationId: this.operation.id,
|
|
1035
|
+
stateId: state.id,
|
|
1036
|
+
payload: parsed.entitySnapshotPayload,
|
|
1037
|
+
})
|
|
1038
|
+
}
|
|
1039
|
+
|
|
896
1040
|
if (
|
|
897
1041
|
update.operationType === "destroy" &&
|
|
898
1042
|
this.workset.isLastPhaseForInstance(instance.id) &&
|
|
899
1043
|
this.context.isGhostInstance(instance.id)
|
|
900
1044
|
) {
|
|
901
|
-
this.
|
|
1045
|
+
this.pendingGhostDeletionIds.add(instance.id)
|
|
902
1046
|
}
|
|
1047
|
+
|
|
1048
|
+
return null
|
|
903
1049
|
}
|
|
904
1050
|
|
|
905
1051
|
private getInstancePromise(
|
|
@@ -1055,4 +1201,20 @@ export class RuntimeOperation {
|
|
|
1055
1201
|
|
|
1056
1202
|
return Array.from(artifactIds)
|
|
1057
1203
|
}
|
|
1204
|
+
|
|
1205
|
+
private getPhaseInstance(instanceId: InstanceId): InstanceModel {
|
|
1206
|
+
const modelInstance = this.context.tryGetInstance(instanceId)
|
|
1207
|
+
if (modelInstance) {
|
|
1208
|
+
return modelInstance
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
if (this.workset.currentPhase === "destroy") {
|
|
1212
|
+
const destroyInstance = this.context.tryGetInstanceForDestroy(instanceId)
|
|
1213
|
+
if (destroyInstance) {
|
|
1214
|
+
return destroyInstance
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
throw new Error(`Instance with ID ${instanceId} not found in the operation context`)
|
|
1219
|
+
}
|
|
1058
1220
|
}
|
|
@@ -18,6 +18,8 @@ describe("Operation - Update Failure", () => {
|
|
|
18
18
|
instanceStateService,
|
|
19
19
|
projectModelService,
|
|
20
20
|
unitExtraService,
|
|
21
|
+
entitySnapshotService,
|
|
22
|
+
unitOutputService,
|
|
21
23
|
createUnit,
|
|
22
24
|
createDeployedUnitState,
|
|
23
25
|
createOperation,
|
|
@@ -60,6 +62,8 @@ describe("Operation - Update Failure", () => {
|
|
|
60
62
|
instanceStateService,
|
|
61
63
|
projectModelService,
|
|
62
64
|
unitExtraService,
|
|
65
|
+
entitySnapshotService,
|
|
66
|
+
unitOutputService,
|
|
63
67
|
logger,
|
|
64
68
|
)
|
|
65
69
|
|