@highstate/backend 0.9.16 → 0.9.19
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-5WVU2AK4.js +1535 -0
- package/dist/chunk-5WVU2AK4.js.map +1 -0
- package/dist/chunk-I7BWSAN6.js +49 -0
- package/dist/chunk-I7BWSAN6.js.map +1 -0
- package/dist/{chunk-RCB4AFGD.js → chunk-VB4YL327.js} +51 -71
- package/dist/chunk-VB4YL327.js.map +1 -0
- package/dist/database/local/prisma.config.js +26 -0
- package/dist/database/local/prisma.config.js.map +1 -0
- package/dist/highstate.manifest.json +5 -4
- package/dist/index.js +7676 -6634
- package/dist/index.js.map +1 -1
- package/dist/library/package-resolution-worker.js +8 -6
- package/dist/library/package-resolution-worker.js.map +1 -1
- package/dist/library/worker/main.js +63 -58
- package/dist/library/worker/main.js.map +1 -1
- package/dist/shared/index.js +3 -216
- package/dist/shared/index.js.map +1 -1
- package/package.json +23 -11
- package/prisma/backend/_schema/layout.prisma +7 -0
- package/prisma/backend/_schema/library.prisma +17 -0
- package/prisma/backend/_schema/project.prisma +101 -0
- package/prisma/backend/_schema/pulumi.prisma +17 -0
- package/prisma/backend/postgresql/main.prisma +17 -0
- package/prisma/backend/sqlite/main.prisma +17 -0
- package/prisma/backend/sqlite/migrations/20250817070609_initiial/migration.sql +34 -0
- package/prisma/backend/sqlite/migrations/20250817104948_add_fields/migration.sql +59 -0
- package/prisma/backend/sqlite/migrations/20250818082732_add_models/migration.sql +41 -0
- package/prisma/backend/sqlite/migrations/20250818083106_a/migration.sql +19 -0
- package/prisma/backend/sqlite/migrations/20250818101945_hi/migration.sql +1 -0
- package/prisma/backend/sqlite/migrations/20250819082315_a/migration.sql +5 -0
- package/prisma/backend/sqlite/migrations/migration_lock.toml +3 -0
- package/prisma/project/api-key.prisma +27 -0
- package/prisma/project/artifact.prisma +52 -0
- package/prisma/project/custom-status.prisma +46 -0
- package/prisma/project/evaluation.prisma +35 -0
- package/prisma/project/instance.prisma +160 -0
- package/prisma/project/layout.prisma +23 -0
- package/prisma/project/lock.prisma +18 -0
- package/prisma/project/main.prisma +17 -0
- package/prisma/project/migrations/20250816081310_initial/migration.sql +300 -0
- package/prisma/project/migrations/20250816082523_test/migration.sql +72 -0
- package/prisma/project/migrations/20250818065643_update/migration.sql +42 -0
- package/prisma/project/migrations/20250818070758_a/migration.sql +8 -0
- package/prisma/project/migrations/20250818070913_a/migration.sql +8 -0
- package/prisma/project/migrations/20250818082720_add_motels/migration.sql +11 -0
- package/prisma/project/migrations/20250818112523_hello/migration.sql +35 -0
- package/prisma/project/migrations/20250819082305_a/migration.sql +14 -0
- package/prisma/project/migrations/20250819165004_add_missing_fields/migration.sql +216 -0
- package/prisma/project/migrations/20250819171309_a/migration.sql +22 -0
- package/prisma/project/migrations/20250820113949_a/migration.sql +66 -0
- package/prisma/project/migrations/20250820144256_b/migration.sql +31 -0
- package/prisma/project/migrations/20250820145547_a/migration.sql +24 -0
- package/prisma/project/migrations/20250820182517_b/migration.sql +2 -0
- package/prisma/project/migrations/20250821172324_a/migration.sql +2 -0
- package/prisma/project/migrations/20250822081339_a/migration.sql +219 -0
- package/prisma/project/migrations/20250822083742_b/migration.sql +1 -0
- package/prisma/project/migrations/20250822105134_boom/migration.sql +1 -0
- package/prisma/project/migrations/20250822141028_b/migration.sql +1 -0
- package/prisma/project/migrations/20250822142342_b/migration.sql +16 -0
- package/prisma/project/migrations/20250824072720_a/migration.sql +1 -0
- package/prisma/project/migrations/20250824093656_b/migration.sql +21 -0
- package/prisma/project/migrations/20250825082518_a/migration.sql +1 -0
- package/prisma/project/migrations/20250825085343_b/migration.sql +1 -0
- package/prisma/project/migrations/20250825091312_a/migration.sql +1 -0
- package/prisma/project/migrations/20250903095431_hi/migration.sql +44 -0
- package/prisma/project/migrations/20250903174255_a/migration.sql +24 -0
- package/prisma/project/migrations/20250908095205_hi/migration.sql +18 -0
- package/prisma/project/migrations/20250909155857_hi/migration.sql +15 -0
- package/prisma/project/migrations/migration_lock.toml +3 -0
- package/prisma/project/model.prisma +37 -0
- package/prisma/project/operation.prisma +148 -0
- package/prisma/project/page.prisma +41 -0
- package/prisma/project/secret.prisma +42 -0
- package/prisma/project/service-account.prisma +36 -0
- package/prisma/project/terminal.prisma +90 -0
- package/prisma/project/trigger.prisma +31 -0
- package/prisma/project/unlock-method.prisma +32 -0
- package/prisma/project/worker.prisma +138 -0
- package/src/artifact/abstractions.ts +13 -13
- package/src/artifact/encryption.ts +31 -15
- package/src/artifact/factory.ts +7 -10
- package/src/artifact/local.ts +33 -50
- package/src/business/api-key.ts +24 -36
- package/src/business/artifact.test.ts +978 -0
- package/src/business/artifact.ts +136 -215
- package/src/business/evaluation.ts +328 -0
- package/src/business/index.ts +5 -1
- package/src/business/instance-lock.test.ts +1060 -0
- package/src/business/instance-lock.ts +387 -77
- package/src/business/instance-state.test.ts +735 -0
- package/src/business/instance-state.ts +604 -217
- package/src/business/operation.test.ts +439 -0
- package/src/business/operation.ts +174 -208
- package/src/business/project-model.ts +258 -0
- package/src/business/project-unlock.ts +172 -112
- package/src/business/project.ts +407 -0
- package/src/business/secret.test.ts +513 -0
- package/src/business/secret.ts +194 -131
- package/src/business/settings.test.ts +695 -0
- package/src/business/settings.ts +855 -0
- package/src/business/terminal-session.ts +90 -0
- package/src/business/unit-extra.test.ts +539 -0
- package/src/business/unit-extra.ts +160 -0
- package/src/business/worker.test.ts +391 -0
- package/src/business/worker.ts +250 -114
- package/src/common/codebase.ts +65 -0
- package/src/common/index.ts +3 -2
- package/src/common/logger.ts +5 -0
- package/src/common/utils.ts +4 -3
- package/src/config.ts +15 -12
- package/src/database/_generated/backend/postgresql/client.ts +72 -0
- package/src/database/_generated/backend/postgresql/commonInputTypes.ts +350 -0
- package/src/database/_generated/backend/postgresql/enums.ts +13 -0
- package/src/database/_generated/backend/postgresql/internal/class.ts +320 -0
- package/src/database/_generated/backend/postgresql/internal/prismaNamespace.ts +1238 -0
- package/src/database/_generated/backend/postgresql/models/Library.ts +1263 -0
- package/src/database/_generated/backend/postgresql/models/Project.ts +2175 -0
- package/src/database/_generated/backend/postgresql/models/ProjectModelStorage.ts +1263 -0
- package/src/database/_generated/backend/postgresql/models/ProjectSpace.ts +1602 -0
- package/src/database/_generated/backend/postgresql/models/PulumiBackend.ts +1263 -0
- package/src/database/_generated/backend/postgresql/models/UserWorkspaseLayout.ts +1065 -0
- package/src/database/_generated/backend/postgresql/models.ts +16 -0
- package/src/database/_generated/backend/postgresql/pjtg.ts +182 -0
- package/src/database/_generated/backend/sqlite/client.ts +72 -0
- package/src/database/_generated/backend/sqlite/commonInputTypes.ts +331 -0
- package/src/database/_generated/backend/sqlite/enums.ts +13 -0
- package/src/database/_generated/backend/sqlite/internal/class.ts +318 -0
- package/src/database/_generated/backend/sqlite/internal/prismaNamespace.ts +1207 -0
- package/src/database/_generated/backend/sqlite/models/Library.ts +1261 -0
- package/src/database/_generated/backend/sqlite/models/Project.ts +2169 -0
- package/src/database/_generated/backend/sqlite/models/ProjectModelStorage.ts +1261 -0
- package/src/database/_generated/backend/sqlite/models/ProjectSpace.ts +1599 -0
- package/src/database/_generated/backend/sqlite/models/PulumiBackend.ts +1261 -0
- package/src/database/_generated/backend/sqlite/models/UserWorkspaseLayout.ts +1063 -0
- package/src/database/_generated/backend/sqlite/models.ts +16 -0
- package/src/database/_generated/backend/sqlite/pjtg.ts +182 -0
- package/src/database/_generated/project/client.ts +204 -0
- package/src/database/_generated/project/commonInputTypes.ts +827 -0
- package/src/database/_generated/project/enums.ts +104 -0
- package/src/database/_generated/project/internal/class.ts +479 -0
- package/src/database/_generated/project/internal/prismaNamespace.ts +2974 -0
- package/src/database/_generated/project/models/ApiKey.ts +1506 -0
- package/src/database/_generated/project/models/Artifact.ts +2051 -0
- package/src/database/_generated/project/models/HubModel.ts +1125 -0
- package/src/database/_generated/project/models/InstanceCustomStatus.ts +1713 -0
- package/src/database/_generated/project/models/InstanceEvaluationState.ts +1312 -0
- package/src/database/_generated/project/models/InstanceLock.ts +1268 -0
- package/src/database/_generated/project/models/InstanceModel.ts +1125 -0
- package/src/database/_generated/project/models/InstanceOperationState.ts +1707 -0
- package/src/database/_generated/project/models/InstanceState.ts +4613 -0
- package/src/database/_generated/project/models/Operation.ts +1647 -0
- package/src/database/_generated/project/models/OperationLog.ts +1455 -0
- package/src/database/_generated/project/models/Page.ts +1838 -0
- package/src/database/_generated/project/models/Secret.ts +1692 -0
- package/src/database/_generated/project/models/ServiceAccount.ts +2165 -0
- package/src/database/_generated/project/models/Terminal.ts +2038 -0
- package/src/database/_generated/project/models/TerminalSession.ts +1454 -0
- package/src/database/_generated/project/models/TerminalSessionLog.ts +1280 -0
- package/src/database/_generated/project/models/Trigger.ts +1430 -0
- package/src/database/_generated/project/models/UnlockMethod.ts +1220 -0
- package/src/database/_generated/project/models/UserCompositeViewport.ts +1280 -0
- package/src/database/_generated/project/models/UserProjectViewport.ts +1059 -0
- package/src/database/_generated/project/models/Worker.ts +1459 -0
- package/src/database/_generated/project/models/WorkerUnitRegistration.ts +1524 -0
- package/src/database/_generated/project/models/WorkerVersion.ts +1974 -0
- package/src/database/_generated/project/models/WorkerVersionLog.ts +1318 -0
- package/src/database/_generated/project/models.ts +35 -0
- package/src/database/_generated/project/pjtg.ts +182 -0
- package/src/database/abstractions.ts +19 -0
- package/src/database/factory.ts +37 -0
- package/src/database/index.ts +6 -0
- package/src/database/local/backend.ts +134 -0
- package/src/database/local/index.ts +3 -0
- package/src/database/local/meta.ts +46 -0
- package/src/database/local/prisma.config.ts +25 -0
- package/src/database/local/project.ts +39 -0
- package/src/database/manager.ts +181 -0
- package/src/database/migrate.ts +35 -0
- package/src/database/prisma.ts +56 -0
- package/src/database/well-known.ts +38 -0
- package/src/index.ts +4 -4
- package/src/library/abstractions.ts +21 -14
- package/src/library/factory.ts +1 -1
- package/src/library/local.ts +86 -38
- package/src/library/package-resolution-worker.ts +1 -1
- package/src/library/worker/evaluator.ts +61 -48
- package/src/library/worker/loader.lite.ts +14 -1
- package/src/library/worker/main.ts +9 -16
- package/src/library/worker/protocol.ts +0 -12
- package/src/lock/manager.ts +12 -7
- package/src/orchestrator/manager.ts +198 -131
- package/src/orchestrator/operation-context.ts +357 -0
- package/src/orchestrator/operation-plan.destroy.test.md +357 -0
- package/src/orchestrator/operation-plan.destroy.test.ts +775 -0
- package/src/orchestrator/operation-plan.fixtures.ts +213 -0
- package/src/orchestrator/operation-plan.md +198 -0
- package/src/orchestrator/operation-plan.refresh.test.md +199 -0
- package/src/orchestrator/operation-plan.refresh.test.ts +367 -0
- package/src/orchestrator/operation-plan.ts +709 -0
- package/src/orchestrator/operation-plan.update.test.md +485 -0
- package/src/orchestrator/operation-plan.update.test.ts +1066 -0
- package/src/orchestrator/operation-workset.ts +235 -583
- package/src/orchestrator/operation.ts +446 -904
- package/src/orchestrator/plan-test-builder.ts +267 -0
- package/src/project-model/abstractions.ts +118 -0
- package/src/project-model/backends/codebase.ts +365 -0
- package/src/project-model/backends/database.ts +440 -0
- package/src/project-model/errors.ts +81 -0
- package/src/project-model/factory.ts +24 -0
- package/src/project-model/index.ts +4 -0
- package/src/project-model/utils.test.ts +544 -0
- package/src/project-model/utils.ts +242 -0
- package/src/pubsub/abstractions.ts +10 -1
- package/src/pubsub/factory.ts +4 -4
- package/src/pubsub/index.ts +1 -0
- package/src/pubsub/manager.ts +49 -25
- package/src/pubsub/memory.ts +31 -0
- package/src/runner/abstractions.ts +38 -26
- package/src/runner/artifact-env.ts +17 -6
- package/src/runner/factory.ts +6 -6
- package/src/runner/force-abort.ts +3 -6
- package/src/runner/local.ts +79 -72
- package/src/runner/pulumi.ts +26 -63
- package/src/services.ts +214 -103
- package/src/shared/models/backend/index.ts +3 -1
- package/src/shared/models/backend/library.ts +12 -4
- package/src/shared/models/backend/project.ts +43 -23
- package/src/shared/models/backend/pulumi.ts +14 -0
- package/src/shared/models/backend/unlock-method.ts +1 -1
- package/src/shared/models/backend/well-known.ts +58 -0
- package/src/shared/models/base.ts +40 -109
- package/src/shared/models/errors.ts +82 -1
- package/src/shared/models/index.ts +3 -2
- package/src/shared/models/prisma.ts +36 -0
- package/src/shared/models/project/api-key.ts +37 -56
- package/src/shared/models/project/artifact.ts +15 -105
- package/src/shared/models/project/custom-status.ts +12 -0
- package/src/shared/models/project/index.ts +9 -9
- package/src/shared/models/project/lock.ts +10 -78
- package/src/shared/models/project/model.ts +32 -0
- package/src/shared/models/project/operation.ts +222 -99
- package/src/shared/models/project/page.ts +37 -48
- package/src/shared/models/project/secret.ts +29 -103
- package/src/shared/models/project/service-account.ts +12 -17
- package/src/shared/models/project/state.ts +100 -390
- package/src/shared/models/project/terminal.ts +75 -89
- package/src/shared/models/project/trigger.ts +13 -49
- package/src/shared/models/project/unlock-method.ts +21 -20
- package/src/shared/models/project/worker.ts +89 -88
- package/src/shared/resolvers/graph-resolver.ts +62 -26
- package/src/shared/resolvers/index.ts +1 -1
- package/src/shared/resolvers/input-hash.ts +24 -14
- package/src/shared/resolvers/input.ts +48 -6
- package/src/shared/resolvers/registry.ts +5 -4
- package/src/shared/resolvers/state.ts +12 -1
- package/src/shared/resolvers/validation.ts +29 -9
- package/src/shared/utils/index.ts +1 -1
- package/src/shared/utils/promise-tracker.ts +30 -3
- package/src/terminal/abstractions.ts +1 -1
- package/src/terminal/docker.ts +3 -3
- package/src/terminal/manager.ts +102 -118
- package/src/test-utils/database.ts +119 -0
- package/src/test-utils/index.ts +2 -0
- package/src/test-utils/services.ts +134 -0
- package/src/unlock/abstractions.ts +31 -0
- package/src/unlock/index.ts +2 -0
- package/src/unlock/memory.ts +27 -0
- package/src/worker/abstractions.ts +7 -4
- package/src/worker/docker.ts +14 -19
- package/src/worker/manager.ts +376 -79
- package/dist/chunk-RCB4AFGD.js.map +0 -1
- package/dist/chunk-WHALQHEZ.js +0 -2017
- package/dist/chunk-WHALQHEZ.js.map +0 -1
- package/src/business/backend-unlock.ts +0 -10
- package/src/common/performance.ts +0 -44
- package/src/hotstate/abstractions.ts +0 -48
- package/src/hotstate/factory.ts +0 -17
- package/src/hotstate/index.ts +0 -3
- package/src/hotstate/manager.ts +0 -192
- package/src/hotstate/memory.ts +0 -100
- package/src/hotstate/validation.ts +0 -101
- package/src/project/abstractions.ts +0 -102
- package/src/project/factory.ts +0 -11
- package/src/project/index.ts +0 -3
- package/src/project/local.ts +0 -469
- package/src/project/manager.ts +0 -574
- package/src/pubsub/local.ts +0 -36
- package/src/pubsub/validation.ts +0 -33
- package/src/shared/models/project/component.ts +0 -45
- package/src/shared/models/project/instance.ts +0 -74
- package/src/state/abstractions.ts +0 -450
- package/src/state/encryption.ts +0 -59
- package/src/state/factory.ts +0 -20
- package/src/state/index.ts +0 -6
- package/src/state/local/backend.ts +0 -299
- package/src/state/local/collection.ts +0 -342
- package/src/state/local/index.ts +0 -2
- package/src/state/manager.ts +0 -819
- package/src/state/repository/index.ts +0 -2
- package/src/state/repository/repository.index.ts +0 -193
- package/src/state/repository/repository.ts +0 -458
- /package/src/{state → database/local}/keyring.ts +0 -0
|
@@ -1,84 +1,71 @@
|
|
|
1
|
-
import type { LibraryBackend } from "../library"
|
|
2
|
-
import type { StateManager } from "../state"
|
|
3
|
-
import type { ProjectBackend } from "../project"
|
|
4
|
-
import type {
|
|
5
|
-
RunnerArtifact,
|
|
6
|
-
RunnerBackend,
|
|
7
|
-
TypedUnitStateUpdate,
|
|
8
|
-
UnitStateUpdate,
|
|
9
|
-
} from "../runner"
|
|
10
1
|
import type { Logger } from "pino"
|
|
2
|
+
import type { ArtifactService } from "../artifact"
|
|
11
3
|
import type {
|
|
12
4
|
InstanceLockService,
|
|
5
|
+
InstanceStatePatch,
|
|
13
6
|
InstanceStateService,
|
|
14
7
|
OperationService,
|
|
8
|
+
ProjectModelService,
|
|
15
9
|
SecretService,
|
|
10
|
+
UnitExtraService,
|
|
16
11
|
} from "../business"
|
|
17
|
-
import type {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
12
|
+
import type { Operation, OperationUpdateInput, Project } from "../database"
|
|
13
|
+
import type { LibraryBackend } from "../library"
|
|
14
|
+
import type { RunnerBackend, TypedUnitStateUpdate, UnitStateUpdate } from "../runner"
|
|
20
15
|
import {
|
|
21
|
-
|
|
22
|
-
parseInstanceId,
|
|
23
|
-
type ComponentModel,
|
|
16
|
+
type InstanceId,
|
|
24
17
|
type InstanceModel,
|
|
18
|
+
parseInstanceId,
|
|
19
|
+
type TriggerInvocation,
|
|
20
|
+
type UnitConfig,
|
|
21
|
+
type VersionedName,
|
|
25
22
|
} from "@highstate/contract"
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
23
|
+
import { createId } from "@paralleldrive/cuid2"
|
|
24
|
+
import { mapValues } from "remeda"
|
|
25
|
+
import { AbortError, errorToString, isAbortErrorLike, waitForAbort } from "../common"
|
|
28
26
|
import {
|
|
29
27
|
type InstanceState,
|
|
30
|
-
|
|
31
|
-
type
|
|
32
|
-
type Page,
|
|
33
|
-
type Trigger,
|
|
34
|
-
type UnitTrigger,
|
|
35
|
-
type UnitTerminal,
|
|
36
|
-
type Terminal,
|
|
37
|
-
type TerminalSpec,
|
|
38
|
-
type TriggerInvocation,
|
|
28
|
+
isTransientInstanceOperationStatus,
|
|
29
|
+
type OperationPhase,
|
|
39
30
|
PromiseTracker,
|
|
40
|
-
|
|
41
|
-
type PageBlock,
|
|
31
|
+
waitAll,
|
|
42
32
|
} from "../shared"
|
|
43
|
-
import {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
isAbortErrorLike,
|
|
47
|
-
PerformanceLogger,
|
|
48
|
-
stringToValue,
|
|
49
|
-
valueToString,
|
|
50
|
-
waitForAbort,
|
|
51
|
-
} from "../common"
|
|
52
|
-
import { OperationWorkset, type OperationPhase } from "./operation-workset"
|
|
33
|
+
import { OperationContext } from "./operation-context"
|
|
34
|
+
import { createOperationPlan } from "./operation-plan"
|
|
35
|
+
import { OperationWorkset } from "./operation-workset"
|
|
53
36
|
|
|
54
37
|
export class RuntimeOperation {
|
|
55
|
-
private readonly
|
|
56
|
-
private readonly forceAbortController = new AbortController()
|
|
57
|
-
|
|
58
|
-
private readonly instanceAbortControllers = new Map<string, AbortController>()
|
|
59
|
-
private readonly instanceForceAbortControllers = new Map<string, AbortController>()
|
|
60
|
-
|
|
61
|
-
private readonly instancePromiseMap = new Map<string, Promise<void>>()
|
|
38
|
+
private readonly instancePromiseMap = new Map<InstanceId, Promise<void>>()
|
|
62
39
|
private readonly promiseTracker = new PromiseTracker()
|
|
63
40
|
|
|
64
41
|
private workset!: OperationWorkset
|
|
42
|
+
private context!: OperationContext
|
|
43
|
+
|
|
44
|
+
private readonly unlockToken = createId()
|
|
65
45
|
|
|
66
46
|
constructor(
|
|
67
|
-
private readonly
|
|
47
|
+
private readonly project: Project,
|
|
68
48
|
private readonly operation: Operation,
|
|
69
49
|
private readonly runnerBackend: RunnerBackend,
|
|
70
50
|
private readonly libraryBackend: LibraryBackend,
|
|
71
|
-
private readonly projectBackend: ProjectBackend,
|
|
72
51
|
private readonly artifactService: ArtifactService,
|
|
73
|
-
private readonly stateManager: StateManager,
|
|
74
52
|
private readonly instanceLockService: InstanceLockService,
|
|
75
53
|
private readonly operationService: OperationService,
|
|
76
54
|
private readonly secretService: SecretService,
|
|
77
55
|
private readonly instanceStateService: InstanceStateService,
|
|
78
|
-
private readonly
|
|
56
|
+
private readonly projectModelService: ProjectModelService,
|
|
57
|
+
private readonly unitExtraService: UnitExtraService,
|
|
79
58
|
private readonly logger: Logger,
|
|
80
59
|
) {}
|
|
81
60
|
|
|
61
|
+
cancel(): void {
|
|
62
|
+
this.workset.cancel()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
cancelInstance(instanceId: InstanceId): void {
|
|
66
|
+
this.workset.cancelInstance(instanceId)
|
|
67
|
+
}
|
|
68
|
+
|
|
82
69
|
async operateSafe(): Promise<void> {
|
|
83
70
|
try {
|
|
84
71
|
await this.operate()
|
|
@@ -88,21 +75,20 @@ export class RuntimeOperation {
|
|
|
88
75
|
} catch (error) {
|
|
89
76
|
if (isAbortErrorLike(error)) {
|
|
90
77
|
this.logger.info("the operation was cancelled")
|
|
91
|
-
this.operation.status = "cancelled"
|
|
92
|
-
this.operation.error = null
|
|
93
78
|
|
|
94
|
-
await this.
|
|
79
|
+
await this.updateOperation({ status: "cancelled" })
|
|
95
80
|
return
|
|
96
81
|
}
|
|
97
82
|
|
|
98
83
|
this.logger.error({ error }, "an error occurred while running the operation")
|
|
99
84
|
|
|
100
|
-
this.
|
|
101
|
-
this.
|
|
102
|
-
|
|
103
|
-
await this.operationService.updateOperation(this.projectId, this.operation)
|
|
85
|
+
await this.updateOperation({ status: "failed" })
|
|
86
|
+
await this.writeOperationLog(errorToString(error))
|
|
104
87
|
} finally {
|
|
105
88
|
try {
|
|
89
|
+
this.promiseTracker.track(this.ensureInstancesUnlocked())
|
|
90
|
+
this.promiseTracker.track(this.ensureOperationStatesFinalized())
|
|
91
|
+
|
|
106
92
|
// ensure that all promises are resolved even if the operation failed
|
|
107
93
|
await this.promiseTracker.waitForAll()
|
|
108
94
|
} catch (error) {
|
|
@@ -117,261 +103,181 @@ export class RuntimeOperation {
|
|
|
117
103
|
private async operate(): Promise<void> {
|
|
118
104
|
this.logger.info("starting operation")
|
|
119
105
|
|
|
120
|
-
// create the
|
|
121
|
-
this.
|
|
122
|
-
this.
|
|
123
|
-
this.operation,
|
|
124
|
-
this.projectBackend,
|
|
106
|
+
// 1. create the context for the operation
|
|
107
|
+
this.context = await OperationContext.load(
|
|
108
|
+
this.project.id,
|
|
125
109
|
this.libraryBackend,
|
|
126
|
-
this.stateManager,
|
|
127
|
-
this.instanceLockService,
|
|
128
110
|
this.instanceStateService,
|
|
111
|
+
this.projectModelService,
|
|
129
112
|
this.logger,
|
|
130
|
-
this.abortController.signal,
|
|
131
113
|
)
|
|
132
114
|
|
|
133
|
-
//
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
115
|
+
// 2. create the plan for the operation (or use provided one)
|
|
116
|
+
let plan: OperationPhase[]
|
|
117
|
+
if (this.operation.phases) {
|
|
118
|
+
plan = this.operation.phases
|
|
119
|
+
} else {
|
|
120
|
+
plan = createOperationPlan(
|
|
121
|
+
this.context,
|
|
122
|
+
this.operation.type,
|
|
123
|
+
this.operation.requestedInstanceIds,
|
|
124
|
+
this.operation.options,
|
|
125
|
+
)
|
|
139
126
|
|
|
140
|
-
|
|
141
|
-
this.
|
|
142
|
-
lockAbortSignal.abort()
|
|
127
|
+
// persist the generated plan
|
|
128
|
+
await this.updateOperation({ phases: plan })
|
|
143
129
|
}
|
|
144
130
|
|
|
145
|
-
//
|
|
146
|
-
|
|
147
|
-
this.
|
|
148
|
-
|
|
131
|
+
// 3. create the workset to track the state of the operation
|
|
132
|
+
this.workset = new OperationWorkset(
|
|
133
|
+
this.project,
|
|
134
|
+
this.operation.id,
|
|
135
|
+
plan,
|
|
136
|
+
this.context,
|
|
137
|
+
this.instanceStateService,
|
|
138
|
+
)
|
|
149
139
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
await this.processOperation()
|
|
153
|
-
} finally {
|
|
154
|
-
lockAbortSignal.abort()
|
|
140
|
+
// 4. create operation states in datbase for all affected instances in all phases
|
|
141
|
+
await this.workset.setupOperationStates()
|
|
155
142
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
await this.workset.restoreAffectedInitialStates()
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
143
|
+
// 5. setup abort controllers for all affected instances in all phases
|
|
144
|
+
this.workset.setupAbortControllersForAllInstances()
|
|
162
145
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
this.abortController.signal.addEventListener("abort", () => abortController.abort())
|
|
166
|
-
this.instanceAbortControllers.set(instanceId, abortController)
|
|
146
|
+
// 6. progressively lock instances and launch them as they get locked
|
|
147
|
+
this.launchLockAcquisitionSequence()
|
|
167
148
|
|
|
168
|
-
|
|
169
|
-
this.
|
|
170
|
-
this.instanceForceAbortControllers.set(instanceId, forceAbortController)
|
|
149
|
+
// 7. run the operation
|
|
150
|
+
await this.processOperation()
|
|
171
151
|
}
|
|
172
152
|
|
|
173
|
-
private async
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
for await (const event of eventStream) {
|
|
177
|
-
if (event.type !== "unlocked") {
|
|
178
|
-
continue
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const instanceIdsToLock = event.instanceIds.filter(
|
|
182
|
-
//
|
|
183
|
-
instanceId => this.workset.instanceIdsToLockIds.has(instanceId),
|
|
184
|
-
)
|
|
185
|
-
|
|
186
|
-
if (instanceIdsToLock.length === 0) {
|
|
187
|
-
try {
|
|
188
|
-
await this.workset.tryLock(instanceIdsToLock)
|
|
189
|
-
} catch (error) {
|
|
190
|
-
this.logger.error({ error }, "failed to lock more instances during operation")
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
if (this.workset.instanceIdsToLockIds.size === 0) {
|
|
195
|
-
// no more instances to lock, stop listening for events
|
|
196
|
-
this.logger.debug("successfully locked all instances for the operation")
|
|
197
|
-
break
|
|
198
|
-
}
|
|
199
|
-
}
|
|
153
|
+
private async updateOperation(patch: OperationUpdateInput): Promise<void> {
|
|
154
|
+
Object.assign(this.operation, patch)
|
|
155
|
+
await this.operationService.updateOperation(this.project.id, this.operation.id, patch)
|
|
200
156
|
}
|
|
201
157
|
|
|
202
|
-
private async
|
|
203
|
-
this.
|
|
204
|
-
|
|
158
|
+
private async writeOperationLog(message: string): Promise<void> {
|
|
159
|
+
this.promiseTracker.track(
|
|
160
|
+
this.operationService.appendLog(this.project.id, this.operation.id, null, message),
|
|
161
|
+
)
|
|
162
|
+
}
|
|
205
163
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
this.
|
|
209
|
-
|
|
164
|
+
private launchLockAcquisitionSequence(): void {
|
|
165
|
+
this.promiseTracker.track(
|
|
166
|
+
this.instanceLockService.lockInstances(
|
|
167
|
+
this.project.id,
|
|
168
|
+
Array.from(this.workset.allAffectedStateIds),
|
|
169
|
+
{
|
|
170
|
+
title: "Operation Lock",
|
|
171
|
+
description: `The instance is locked for the ${this.operation.type} operation "${this.operation.id}".`,
|
|
172
|
+
icon: "mdi:cog-sync",
|
|
173
|
+
},
|
|
174
|
+
async (_tx, newlyLockedStateIds) => {
|
|
175
|
+
// trigger all newly locked instances to start their processing
|
|
176
|
+
for (const stateId of newlyLockedStateIds) {
|
|
177
|
+
this.workset.markInstanceLocked(stateId)
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
true, // allow partial locks to allow independent free branches to run
|
|
181
|
+
this.workset.abortController.signal,
|
|
182
|
+
60_000, // wait up to 60 seconds for unlock events before retrying
|
|
183
|
+
this.unlockToken,
|
|
184
|
+
),
|
|
210
185
|
)
|
|
186
|
+
}
|
|
211
187
|
|
|
212
|
-
|
|
188
|
+
private async processOperation(): Promise<void> {
|
|
213
189
|
const errors: string[] = []
|
|
214
|
-
let hasAbortError = false
|
|
215
190
|
|
|
216
|
-
|
|
217
|
-
this.workset.
|
|
191
|
+
while (this.workset.hasRemainingPhases()) {
|
|
192
|
+
this.workset.nextPhase()
|
|
193
|
+
this.instancePromiseMap.clear()
|
|
218
194
|
|
|
219
|
-
|
|
220
|
-
for (const instanceId of this.workset.
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
// they will be called by their parent instance
|
|
225
|
-
continue
|
|
226
|
-
}
|
|
195
|
+
// lauch all instances in this phase
|
|
196
|
+
for (const instanceId of this.workset.phaseAffectedInstanceIds) {
|
|
197
|
+
const instance = this.context.getInstance(instanceId)
|
|
198
|
+
const state = this.context.getState(instanceId)
|
|
199
|
+
const promise = this.getInstancePromiseForOperation(instance, state)
|
|
227
200
|
|
|
228
|
-
|
|
201
|
+
this.instancePromiseMap.set(instanceId, promise)
|
|
202
|
+
this.promiseTracker.track(promise)
|
|
229
203
|
}
|
|
230
204
|
|
|
231
|
-
|
|
232
|
-
this.
|
|
233
|
-
await this.operationService.updateOperation(this.projectId, this.operation)
|
|
234
|
-
|
|
235
|
-
const phaseResults = await Promise.allSettled(promises)
|
|
236
|
-
for (const result of phaseResults) {
|
|
237
|
-
if (result.status !== "rejected") {
|
|
238
|
-
continue
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
if (isAbortErrorLike(result.reason)) {
|
|
242
|
-
hasAbortError = true
|
|
243
|
-
} else {
|
|
244
|
-
errors.push(errorToString(result.reason))
|
|
245
|
-
}
|
|
246
|
-
}
|
|
205
|
+
// wait for all instances in this phase to complete
|
|
206
|
+
await this.promiseTracker.waitForAll()
|
|
247
207
|
|
|
248
|
-
this.logger.info(`
|
|
208
|
+
this.logger.info(`phase "%s" completed`, this.workset.currentPhase)
|
|
249
209
|
}
|
|
250
210
|
|
|
251
211
|
if (errors.length > 0) {
|
|
252
212
|
this.operation.status = "failed"
|
|
253
|
-
|
|
254
|
-
this.
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
213
|
+
|
|
214
|
+
this.operationService.appendLog(
|
|
215
|
+
this.project.id,
|
|
216
|
+
this.operation.id,
|
|
217
|
+
null,
|
|
218
|
+
`Operation failed with the following errors:\n${errors.join("\n")}`,
|
|
219
|
+
)
|
|
258
220
|
} else {
|
|
259
221
|
this.operation.status = "completed"
|
|
260
|
-
this.operation.error = null
|
|
261
222
|
}
|
|
262
223
|
|
|
263
|
-
await this.operationService.
|
|
224
|
+
await this.operationService.markOperationFinished(
|
|
225
|
+
this.project.id,
|
|
226
|
+
this.operation.id,
|
|
227
|
+
this.operation.status,
|
|
228
|
+
)
|
|
264
229
|
|
|
265
230
|
this.logger.info("operation completed")
|
|
266
231
|
}
|
|
267
232
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
if (!abortController.signal.aborted) {
|
|
275
|
-
this.logger.info(`cancelling operation for instance "${instanceId}"`)
|
|
276
|
-
abortController.abort()
|
|
277
|
-
|
|
278
|
-
// just the UX feature to indicate that we are trying to cancel the operation
|
|
279
|
-
this.promiseTracker.track(
|
|
280
|
-
this.workset.patchState({
|
|
281
|
-
id: instanceId,
|
|
282
|
-
operationStatus: {
|
|
283
|
-
status: "cancelling",
|
|
284
|
-
},
|
|
285
|
-
}),
|
|
286
|
-
)
|
|
287
|
-
|
|
288
|
-
return
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// then try to force cancel the operation
|
|
292
|
-
const forceAbortController = this.instanceForceAbortControllers.get(instanceId)
|
|
293
|
-
if (!forceAbortController) {
|
|
294
|
-
throw new Error(`No force abort controller found for instance "${instanceId}"`)
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
if (!forceAbortController.signal.aborted) {
|
|
298
|
-
this.logger.info(`force cancelling operation for instance "${instanceId}"`)
|
|
299
|
-
forceAbortController.abort()
|
|
300
|
-
return
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
this.logger.warn(`operation for instance "${instanceId}" is already force cancelled`)
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
cancel(): void {
|
|
307
|
-
if (!this.abortController.signal.aborted) {
|
|
308
|
-
this.logger.info("cancelling the operation")
|
|
309
|
-
this.abortController.abort()
|
|
310
|
-
return
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// then try to force cancel the operation
|
|
314
|
-
if (!this.forceAbortController.signal.aborted) {
|
|
315
|
-
this.logger.info("force cancelling the operation")
|
|
316
|
-
this.forceAbortController.abort()
|
|
317
|
-
return
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
this.logger.warn("the operation is already cancelled or force cancelled")
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
private getInstancePromiseForOperation(instanceId: string): Promise<void> {
|
|
324
|
-
const [instanceType] = parseInstanceId(instanceId)
|
|
325
|
-
const component = this.workset.library.components[instanceType]
|
|
326
|
-
|
|
327
|
-
if (isUnitModel(component)) {
|
|
328
|
-
return this.getUnitPromise(instanceId)
|
|
233
|
+
private getInstancePromiseForOperation(
|
|
234
|
+
instance: InstanceModel,
|
|
235
|
+
state: InstanceState,
|
|
236
|
+
): Promise<void> {
|
|
237
|
+
if (instance.kind === "unit") {
|
|
238
|
+
return this.getUnitPromise(instance, state)
|
|
329
239
|
}
|
|
330
240
|
|
|
331
|
-
return this.getCompositePromise(
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
private getOperationPhases(): OperationPhase[] {
|
|
335
|
-
switch (this.operation.type) {
|
|
336
|
-
case "update": {
|
|
337
|
-
if (this.operation.instanceIdsToDestroy.length > 0) {
|
|
338
|
-
return ["destroy", "update"]
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
return ["update"]
|
|
342
|
-
}
|
|
343
|
-
case "preview":
|
|
344
|
-
return ["update"]
|
|
345
|
-
case "recreate":
|
|
346
|
-
return ["destroy", "update"]
|
|
347
|
-
case "destroy":
|
|
348
|
-
return ["destroy"]
|
|
349
|
-
case "refresh":
|
|
350
|
-
return ["refresh"]
|
|
351
|
-
}
|
|
241
|
+
return this.getCompositePromise(instance)
|
|
352
242
|
}
|
|
353
243
|
|
|
354
|
-
private
|
|
244
|
+
private getUnitPromise(instance: InstanceModel, state: InstanceState): Promise<void> {
|
|
355
245
|
switch (this.workset.currentPhase) {
|
|
356
246
|
case "update": {
|
|
357
|
-
return this.updateUnit(
|
|
247
|
+
return this.updateUnit(instance, state)
|
|
358
248
|
}
|
|
359
249
|
case "destroy": {
|
|
360
|
-
return this.destroyUnit(
|
|
250
|
+
return this.destroyUnit(instance, state)
|
|
361
251
|
}
|
|
362
252
|
case "refresh": {
|
|
363
|
-
return this.refreshUnit(
|
|
253
|
+
return this.refreshUnit(instance, state)
|
|
364
254
|
}
|
|
365
255
|
}
|
|
366
256
|
}
|
|
367
257
|
|
|
368
|
-
private getCompositePromise(
|
|
369
|
-
return this.getInstancePromise(
|
|
370
|
-
|
|
258
|
+
private getCompositePromise(instance: InstanceModel): Promise<void> {
|
|
259
|
+
return this.getInstancePromise(instance.id, async logger => {
|
|
260
|
+
let instanceState: InstanceStatePatch | undefined
|
|
371
261
|
|
|
372
|
-
|
|
262
|
+
// set parent ID on update operation
|
|
263
|
+
if (this.workset.currentPhase === "update") {
|
|
264
|
+
if (instance.parentId) {
|
|
265
|
+
const parentState = this.context.getState(instance.parentId)
|
|
266
|
+
instanceState = { parentId: parentState.id }
|
|
267
|
+
} else {
|
|
268
|
+
instanceState = { parentId: null }
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
await this.workset.updateState(instance.id, {
|
|
273
|
+
operationState: {
|
|
274
|
+
status: this.workset.getTransientStatusByOperationPhase(),
|
|
275
|
+
startedAt: new Date(),
|
|
276
|
+
},
|
|
277
|
+
instanceState,
|
|
278
|
+
})
|
|
373
279
|
|
|
374
|
-
const children = this.workset.getAffectedCompositeChildren(
|
|
280
|
+
const children = this.workset.getAffectedCompositeChildren(instance.id)
|
|
375
281
|
const childPromises: Promise<void>[] = []
|
|
376
282
|
|
|
377
283
|
if (children.length) {
|
|
@@ -381,65 +287,37 @@ export class RuntimeOperation {
|
|
|
381
287
|
}
|
|
382
288
|
|
|
383
289
|
for (const child of children) {
|
|
384
|
-
|
|
385
|
-
// skip children that are not affected by the operation
|
|
386
|
-
continue
|
|
387
|
-
}
|
|
290
|
+
logger.debug(`waiting for child "%s"`, child)
|
|
388
291
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
try {
|
|
394
|
-
await Promise.all(childPromises)
|
|
292
|
+
const instance = this.context.getInstance(child)
|
|
293
|
+
const state = this.context.getState(child)
|
|
294
|
+
const promise = this.getInstancePromiseForOperation(instance, state)
|
|
395
295
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
}
|
|
296
|
+
childPromises.push(promise)
|
|
297
|
+
}
|
|
399
298
|
|
|
400
|
-
|
|
401
|
-
await this.clearOperationState(instanceId)
|
|
402
|
-
return
|
|
403
|
-
}
|
|
299
|
+
await waitAll(childPromises)
|
|
404
300
|
|
|
405
|
-
|
|
406
|
-
await this.workset.getUpToDateInputHashOutput(instance)
|
|
301
|
+
logger.debug("all children completed")
|
|
407
302
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
303
|
+
// update the instance and operation state after all children are completed in last phase
|
|
304
|
+
if (this.workset.isLastPhaseForInstance(instance.id)) {
|
|
305
|
+
await this.workset.updateState(instance.id, {
|
|
306
|
+
operationState: {
|
|
307
|
+
status: this.workset.getStableStatusByOperationPhase(),
|
|
308
|
+
finishedAt: new Date(),
|
|
414
309
|
},
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
await this.workset.restoreInitialState(instanceId)
|
|
419
|
-
return
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
await this.workset.patchState({
|
|
423
|
-
id: instanceId,
|
|
424
|
-
operationStatus: {
|
|
425
|
-
status: "error",
|
|
426
|
-
message: errorToString(error),
|
|
310
|
+
instanceState: {
|
|
311
|
+
status: this.workset.getNextStableInstanceStatus(instance.id),
|
|
312
|
+
message: null,
|
|
427
313
|
},
|
|
428
314
|
})
|
|
429
315
|
}
|
|
430
316
|
})
|
|
431
317
|
}
|
|
432
318
|
|
|
433
|
-
private updateUnit(
|
|
434
|
-
return this.getInstancePromise(
|
|
435
|
-
const instance = this.workset.getInstance(instanceId)
|
|
436
|
-
const component = this.workset.library.components[instance.type]
|
|
437
|
-
const perfLogger = new PerformanceLogger(logger)
|
|
438
|
-
perfLogger.log("starting update promise for instance")
|
|
439
|
-
|
|
440
|
-
await this.setInitialOperationStatus(instance)
|
|
441
|
-
perfLogger.log("initial operation status set")
|
|
442
|
-
|
|
319
|
+
private updateUnit(instance: InstanceModel, state: InstanceState): Promise<void> {
|
|
320
|
+
return this.getInstancePromise(instance.id, async (logger, signal, forceSignal) => {
|
|
443
321
|
await Promise.race([
|
|
444
322
|
this.updateUnitDependencies(instance.id, logger),
|
|
445
323
|
|
|
@@ -447,115 +325,80 @@ export class RuntimeOperation {
|
|
|
447
325
|
waitForAbort(signal),
|
|
448
326
|
])
|
|
449
327
|
|
|
450
|
-
if (!signal.aborted) {
|
|
451
|
-
perfLogger.log("dependencies updated")
|
|
452
|
-
}
|
|
453
|
-
|
|
454
328
|
signal.throwIfAborted()
|
|
455
329
|
|
|
456
330
|
logger.info("updating unit")
|
|
457
331
|
|
|
458
|
-
await this.workset.
|
|
459
|
-
|
|
460
|
-
operationStatus: {
|
|
332
|
+
await this.workset.updateState(instance.id, {
|
|
333
|
+
operationState: {
|
|
461
334
|
status: "updating",
|
|
462
|
-
|
|
463
|
-
totalResourceCount: 0,
|
|
335
|
+
startedAt: new Date(),
|
|
464
336
|
},
|
|
465
337
|
})
|
|
466
338
|
|
|
467
|
-
perfLogger.log("set 'updating' operation status")
|
|
468
339
|
signal.throwIfAborted()
|
|
469
340
|
|
|
470
|
-
const secrets = await this.secretService.
|
|
341
|
+
const secrets = await this.secretService.getInstanceSecretValues(this.project.id, state.id)
|
|
471
342
|
|
|
472
|
-
perfLogger.log("secrets loaded")
|
|
473
343
|
signal.throwIfAborted()
|
|
474
344
|
|
|
475
|
-
const config = this.prepareUnitConfig(instance,
|
|
476
|
-
perfLogger.log("unit config prepared")
|
|
477
|
-
|
|
478
|
-
// extract artifact hashes from dependencies based on instance inputs
|
|
479
|
-
const artifactHashes = this.collectArtifactIdsForInstance(instance)
|
|
480
|
-
const artifacts = await this.stateManager
|
|
481
|
-
.getArtifactHashIndexRepository(this.projectId)
|
|
482
|
-
.getManyItems(artifactHashes)
|
|
345
|
+
const config = this.prepareUnitConfig(instance, secrets)
|
|
483
346
|
|
|
484
|
-
|
|
485
|
-
|
|
347
|
+
// collect artifacts authorized for this instance
|
|
348
|
+
const artifactIds = this.collectArtifactIdsForInstance(instance)
|
|
349
|
+
const artifacts = await this.artifactService.getArtifactsByIds(this.project.id, artifactIds)
|
|
486
350
|
|
|
487
|
-
|
|
488
|
-
if (this.operation.type !== "preview" && artifactHashes.length > 0) {
|
|
489
|
-
const state = this.workset.getState(instance.id)
|
|
490
|
-
|
|
491
|
-
// update artifact metadata in the artifact manager
|
|
492
|
-
this.promiseTracker.track(
|
|
493
|
-
this.artifactService.updateUsage(
|
|
494
|
-
this.projectId,
|
|
495
|
-
{ type: "instance", instanceId: instance.id },
|
|
496
|
-
state?.extra?.usedArtifactIds ?? [],
|
|
497
|
-
artifacts.map(artifact => artifact.id),
|
|
498
|
-
),
|
|
499
|
-
)
|
|
500
|
-
|
|
501
|
-
await this.workset.patchState({
|
|
502
|
-
id: instance.id,
|
|
503
|
-
extra: {
|
|
504
|
-
usedArtifactIds: artifacts.map(artifact => artifact.id),
|
|
505
|
-
},
|
|
506
|
-
})
|
|
507
|
-
|
|
508
|
-
perfLogger.log("artifact usage tracked")
|
|
509
|
-
}
|
|
351
|
+
logger.debug({ count: artifactIds.length }, "artifact ids collected from dependencies")
|
|
510
352
|
|
|
511
353
|
await this.runnerBackend[this.operation.type === "preview" ? "preview" : "update"]({
|
|
512
|
-
projectId: this.
|
|
513
|
-
libraryId: this.
|
|
354
|
+
projectId: this.project.id,
|
|
355
|
+
libraryId: this.project.libraryId,
|
|
356
|
+
stateId: state.id,
|
|
514
357
|
instanceType: instance.type,
|
|
515
358
|
instanceName: instance.name,
|
|
516
359
|
config,
|
|
517
360
|
refresh: this.operation.options.refresh,
|
|
518
|
-
secrets
|
|
361
|
+
secrets,
|
|
519
362
|
artifacts,
|
|
520
363
|
signal,
|
|
521
364
|
forceSignal,
|
|
522
365
|
})
|
|
523
366
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
await this.watchStateStream(instance.type, instance.name, logger)
|
|
527
|
-
perfLogger.log("unit update completed")
|
|
367
|
+
await this.watchStateStream(state, instance.type, instance.name, logger)
|
|
528
368
|
logger.info("unit updated")
|
|
529
369
|
})
|
|
530
370
|
}
|
|
531
371
|
|
|
532
|
-
private async updateUnitDependencies(instanceId:
|
|
372
|
+
private async updateUnitDependencies(instanceId: InstanceId, logger: Logger): Promise<void> {
|
|
533
373
|
try {
|
|
534
|
-
const dependencies = this.
|
|
374
|
+
const dependencies = this.context.getDependencies(instanceId)
|
|
535
375
|
const dependencyPromises: Promise<void>[] = []
|
|
536
376
|
|
|
537
|
-
for (const
|
|
538
|
-
if (!this.
|
|
377
|
+
for (const dependency of dependencies) {
|
|
378
|
+
if (!this.workset.phaseAffectedInstanceIds.has(dependency.id)) {
|
|
539
379
|
// skip dependencies that are not affected by the operation
|
|
540
380
|
continue
|
|
541
381
|
}
|
|
542
382
|
|
|
543
|
-
|
|
544
|
-
|
|
383
|
+
const state = this.context.getState(dependency.id)
|
|
384
|
+
|
|
385
|
+
logger.debug(`waiting for dependency "%s"`, dependency.id)
|
|
386
|
+
dependencyPromises.push(this.getInstancePromiseForOperation(dependency, state))
|
|
545
387
|
}
|
|
546
388
|
|
|
547
|
-
await
|
|
389
|
+
await waitAll(dependencyPromises)
|
|
548
390
|
|
|
549
391
|
if (dependencies.length > 0) {
|
|
550
392
|
logger.info("all dependencies completed")
|
|
551
393
|
}
|
|
552
|
-
} catch {
|
|
394
|
+
} catch (error) {
|
|
553
395
|
// abort the instance if any dependency fails
|
|
554
|
-
throw new AbortError()
|
|
396
|
+
throw new AbortError("One of the dependencies failed", { cause: error })
|
|
555
397
|
}
|
|
556
398
|
}
|
|
557
399
|
|
|
558
400
|
private async processBeforeDestroyTriggers(
|
|
401
|
+
instance: InstanceModel,
|
|
559
402
|
state: InstanceState,
|
|
560
403
|
logger: Logger,
|
|
561
404
|
signal: AbortSignal,
|
|
@@ -566,15 +409,7 @@ export class RuntimeOperation {
|
|
|
566
409
|
return
|
|
567
410
|
}
|
|
568
411
|
|
|
569
|
-
const
|
|
570
|
-
const component = this.workset.library.components[instance.type]
|
|
571
|
-
|
|
572
|
-
// fetch triggers from state backend by their IDs
|
|
573
|
-
const triggerIds = Object.values(state.extra?.triggerIds ?? {})
|
|
574
|
-
|
|
575
|
-
const allTriggers = await this.stateManager
|
|
576
|
-
.getTriggerRepository(this.projectId)
|
|
577
|
-
.getManyItems(triggerIds)
|
|
412
|
+
const allTriggers = await this.unitExtraService.getInstanceTriggers(this.project.id, state.id)
|
|
578
413
|
|
|
579
414
|
const beforeDestroyTriggers = allTriggers.filter(
|
|
580
415
|
trigger => trigger.spec.type === "before-destroy",
|
|
@@ -584,96 +419,73 @@ export class RuntimeOperation {
|
|
|
584
419
|
return
|
|
585
420
|
}
|
|
586
421
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
name => state?.extra?.triggerIds?.[name] === trigger.id,
|
|
591
|
-
)
|
|
592
|
-
|
|
593
|
-
return { name: triggerName || "unknown" }
|
|
594
|
-
})
|
|
422
|
+
const invokedTriggers = beforeDestroyTriggers.map(trigger => ({
|
|
423
|
+
name: trigger.name,
|
|
424
|
+
}))
|
|
595
425
|
|
|
596
|
-
await this.workset.
|
|
597
|
-
|
|
598
|
-
operationStatus: {
|
|
599
|
-
status: "processing-triggers",
|
|
600
|
-
},
|
|
426
|
+
await this.workset.updateState(instance.id, {
|
|
427
|
+
operationState: { status: "processing_triggers" },
|
|
601
428
|
})
|
|
602
429
|
|
|
603
430
|
logger.info("updating unit to process before-destroy triggers...")
|
|
604
431
|
|
|
605
|
-
const secrets = await this.secretService.
|
|
432
|
+
const secrets = await this.secretService.getInstanceSecretValues(this.project.id, state.id)
|
|
606
433
|
|
|
607
434
|
await this.runnerBackend.update({
|
|
608
|
-
projectId: this.
|
|
609
|
-
|
|
435
|
+
projectId: this.project.id,
|
|
436
|
+
stateId: state.id,
|
|
437
|
+
libraryId: this.project.libraryId,
|
|
610
438
|
instanceType: instance.type,
|
|
611
439
|
instanceName: instance.name,
|
|
612
|
-
config: this.prepareUnitConfig(instance,
|
|
440
|
+
config: this.prepareUnitConfig(instance, secrets, invokedTriggers),
|
|
613
441
|
refresh: this.operation.options.refresh,
|
|
614
|
-
secrets
|
|
442
|
+
secrets,
|
|
615
443
|
signal,
|
|
616
444
|
forceSignal,
|
|
617
445
|
})
|
|
618
446
|
|
|
619
447
|
logger.debug("unit update requested")
|
|
620
448
|
|
|
621
|
-
await this.watchStateStream(instance.type, instance.name, logger)
|
|
449
|
+
await this.watchStateStream(state, instance.type, instance.name, logger)
|
|
622
450
|
logger.debug("before-destroy triggers processed")
|
|
623
451
|
}
|
|
624
452
|
|
|
625
|
-
private async destroyUnit(
|
|
626
|
-
return this.getInstancePromise(
|
|
627
|
-
await this.workset.patchState({
|
|
628
|
-
id: instanceId,
|
|
629
|
-
operationStatus: {
|
|
630
|
-
status: "pending",
|
|
631
|
-
},
|
|
632
|
-
})
|
|
633
|
-
|
|
634
|
-
signal.throwIfAborted()
|
|
635
|
-
|
|
636
|
-
const state = this.workset.getState(instanceId)
|
|
637
|
-
if (!state) {
|
|
638
|
-
logger.warn("state not found for unit, but destroy was requested")
|
|
639
|
-
return
|
|
640
|
-
}
|
|
641
|
-
|
|
453
|
+
private async destroyUnit(instance: InstanceModel, state: InstanceState): Promise<void> {
|
|
454
|
+
return this.getInstancePromise(instance.id, async (logger, signal, forceSignal) => {
|
|
642
455
|
const dependentPromises: Promise<void>[] = []
|
|
643
|
-
const dependents = this.
|
|
456
|
+
const dependents = this.context.getDependentStates(instance.id)
|
|
644
457
|
|
|
645
458
|
for (const dependent of dependents) {
|
|
646
|
-
if (
|
|
647
|
-
!this.operation.options.destroyDependentInstances &&
|
|
648
|
-
!this.operation.instanceIdsToDestroy.includes(dependent.id)
|
|
649
|
-
) {
|
|
459
|
+
if (!this.workset.phaseAffectedInstanceIds.has(dependent.instanceId)) {
|
|
650
460
|
// skip dependents that are not affected by the operation
|
|
651
461
|
continue
|
|
652
462
|
}
|
|
653
463
|
|
|
654
|
-
|
|
464
|
+
const instance = this.context.getInstance(dependent.instanceId)
|
|
465
|
+
dependentPromises.push(this.getInstancePromiseForOperation(instance, dependent))
|
|
655
466
|
}
|
|
656
467
|
|
|
657
|
-
await
|
|
468
|
+
await waitAll(dependentPromises)
|
|
658
469
|
signal.throwIfAborted()
|
|
659
470
|
|
|
660
|
-
await this.processBeforeDestroyTriggers(state, logger, signal, forceSignal)
|
|
471
|
+
await this.processBeforeDestroyTriggers(instance, state, logger, signal, forceSignal)
|
|
661
472
|
signal.throwIfAborted()
|
|
662
473
|
|
|
663
474
|
logger.info("destroying unit...")
|
|
664
475
|
|
|
665
|
-
await this.workset.
|
|
666
|
-
|
|
667
|
-
operationStatus: {
|
|
476
|
+
await this.workset.updateState(instance.id, {
|
|
477
|
+
operationState: {
|
|
668
478
|
status: "destroying",
|
|
479
|
+
startedAt: new Date(),
|
|
669
480
|
},
|
|
670
481
|
})
|
|
671
482
|
|
|
672
|
-
const [type, name] = parseInstanceId(
|
|
483
|
+
const [type, name] = parseInstanceId(instance.id)
|
|
673
484
|
|
|
674
485
|
await this.runnerBackend.destroy({
|
|
675
|
-
projectId: this.
|
|
676
|
-
|
|
486
|
+
projectId: this.project.id,
|
|
487
|
+
stateId: state.id,
|
|
488
|
+
libraryId: this.project.libraryId,
|
|
677
489
|
instanceType: type,
|
|
678
490
|
instanceName: name,
|
|
679
491
|
refresh: this.operation.options.refresh,
|
|
@@ -685,47 +497,32 @@ export class RuntimeOperation {
|
|
|
685
497
|
|
|
686
498
|
logger.debug("destroy request sent")
|
|
687
499
|
|
|
688
|
-
await this.watchStateStream(type, name, logger)
|
|
500
|
+
await this.watchStateStream(state, type, name, logger)
|
|
689
501
|
|
|
690
502
|
// clean up artifacts after successful destruction
|
|
691
|
-
|
|
692
|
-
const artifactIds = unique([
|
|
693
|
-
...Object.values(state.extra?.ownedArtifactIds ?? {}).flat(),
|
|
694
|
-
...(state.extra?.usedArtifactIds ?? []),
|
|
695
|
-
])
|
|
696
|
-
|
|
697
|
-
await this.artifactService.removeUsages(
|
|
698
|
-
//
|
|
699
|
-
this.projectId,
|
|
700
|
-
artifactIds,
|
|
701
|
-
[{ type: "instance", instanceId: state.id }],
|
|
702
|
-
)
|
|
703
|
-
} catch (error) {
|
|
704
|
-
logger.warn({ error }, "failed to cleanup artifacts for destroyed instance")
|
|
705
|
-
}
|
|
503
|
+
await this.artifactService.clearInstanceArtifactReferences(this.project.id, instance.id)
|
|
706
504
|
|
|
707
505
|
logger.info("unit destroyed")
|
|
708
506
|
})
|
|
709
507
|
}
|
|
710
508
|
|
|
711
|
-
private async refreshUnit(
|
|
712
|
-
return this.getInstancePromise(
|
|
713
|
-
await this.workset.
|
|
714
|
-
|
|
715
|
-
operationStatus: {
|
|
509
|
+
private async refreshUnit(instance: InstanceModel, state: InstanceState): Promise<void> {
|
|
510
|
+
return this.getInstancePromise(instance.id, async (logger, signal, forceSignal) => {
|
|
511
|
+
await this.workset.updateState(instance.id, {
|
|
512
|
+
operationState: {
|
|
716
513
|
status: "refreshing",
|
|
717
|
-
|
|
718
|
-
totalResourceCount: 0,
|
|
514
|
+
startedAt: new Date(),
|
|
719
515
|
},
|
|
720
516
|
})
|
|
721
517
|
|
|
722
518
|
logger.info("refreshing unit...")
|
|
723
519
|
|
|
724
|
-
const [type, name] = parseInstanceId(
|
|
520
|
+
const [type, name] = parseInstanceId(instance.id)
|
|
725
521
|
|
|
726
522
|
await this.runnerBackend.refresh({
|
|
727
|
-
projectId: this.
|
|
728
|
-
|
|
523
|
+
projectId: this.project.id,
|
|
524
|
+
stateId: state.id,
|
|
525
|
+
libraryId: this.project.libraryId,
|
|
729
526
|
instanceType: type,
|
|
730
527
|
instanceName: name,
|
|
731
528
|
signal,
|
|
@@ -734,19 +531,21 @@ export class RuntimeOperation {
|
|
|
734
531
|
|
|
735
532
|
logger.debug("unit refresh requested")
|
|
736
533
|
|
|
737
|
-
await this.watchStateStream(type, name, logger)
|
|
534
|
+
await this.watchStateStream(state, type, name, logger)
|
|
738
535
|
logger.info("unit refreshed")
|
|
739
536
|
})
|
|
740
537
|
}
|
|
741
538
|
|
|
742
539
|
private async watchStateStream(
|
|
743
|
-
|
|
540
|
+
state: InstanceState,
|
|
541
|
+
instanceType: VersionedName,
|
|
744
542
|
instanceName: string,
|
|
745
543
|
logger: Logger,
|
|
746
544
|
): Promise<void> {
|
|
747
545
|
const stream = this.runnerBackend.watch({
|
|
748
|
-
projectId: this.
|
|
749
|
-
|
|
546
|
+
projectId: this.project.id,
|
|
547
|
+
stateId: state.id,
|
|
548
|
+
libraryId: this.project.libraryId,
|
|
750
549
|
instanceType,
|
|
751
550
|
instanceName,
|
|
752
551
|
})
|
|
@@ -755,18 +554,12 @@ export class RuntimeOperation {
|
|
|
755
554
|
|
|
756
555
|
for await (update of stream) {
|
|
757
556
|
try {
|
|
758
|
-
await this.handleUnitStateUpdate(update)
|
|
557
|
+
await this.handleUnitStateUpdate(update, state)
|
|
759
558
|
} catch (error) {
|
|
760
559
|
logger.error({ error }, "failed to handle unit state update")
|
|
761
560
|
}
|
|
762
561
|
|
|
763
562
|
if (update.type === "error") {
|
|
764
|
-
if (isAbortErrorLike(update.message)) {
|
|
765
|
-
// abort the unit if the returned error contains some abort-like pattern
|
|
766
|
-
// generally, this might not be safe, but for now, we assume that
|
|
767
|
-
throw new AbortError()
|
|
768
|
-
}
|
|
769
|
-
|
|
770
563
|
// rethrow the error to stop the execution of dependent units
|
|
771
564
|
throw new Error(`An error occurred while processing the unit "${update.unitId}"`, {
|
|
772
565
|
cause: update.message,
|
|
@@ -785,187 +578,155 @@ export class RuntimeOperation {
|
|
|
785
578
|
|
|
786
579
|
private prepareUnitConfig(
|
|
787
580
|
instance: InstanceModel,
|
|
788
|
-
|
|
581
|
+
secrets: Record<string, unknown>,
|
|
789
582
|
invokedTriggers: TriggerInvocation[] = [],
|
|
790
|
-
):
|
|
791
|
-
const
|
|
792
|
-
|
|
793
|
-
for (const key of Object.keys(component.args)) {
|
|
794
|
-
// explicitly set empty values to remove old values in the config
|
|
795
|
-
const value = instance.args?.[key]
|
|
796
|
-
config[key] = value ? valueToString(value) : ""
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
const instanceInputs = this.workset.resolvedInstanceInputs.get(instance.id) ?? {}
|
|
800
|
-
|
|
801
|
-
for (const key of Object.keys(component.inputs)) {
|
|
802
|
-
const inputs = instanceInputs[key] ?? []
|
|
583
|
+
): UnitConfig {
|
|
584
|
+
const resolvedInputs = this.context.getResolvedInputs(instance.id)
|
|
803
585
|
|
|
804
|
-
|
|
586
|
+
return {
|
|
587
|
+
instanceId: instance.id,
|
|
588
|
+
args: instance.args ?? {},
|
|
589
|
+
inputs: mapValues(resolvedInputs ?? {}, input => input.map(value => value.input)),
|
|
590
|
+
invokedTriggers,
|
|
591
|
+
secretNames: Object.keys(secrets),
|
|
592
|
+
stateIdMap: this.context.getInstanceIdToStateIdMap(),
|
|
805
593
|
}
|
|
806
|
-
|
|
807
|
-
config["$invokedTriggers"] = JSON.stringify(invokedTriggers)
|
|
808
|
-
|
|
809
|
-
return config
|
|
810
594
|
}
|
|
811
595
|
|
|
812
|
-
private async handleUnitStateUpdate(
|
|
596
|
+
private async handleUnitStateUpdate(
|
|
597
|
+
update: UnitStateUpdate,
|
|
598
|
+
state: InstanceState,
|
|
599
|
+
): Promise<void> {
|
|
813
600
|
switch (update.type) {
|
|
814
601
|
case "message":
|
|
815
|
-
this.handleUnitMessage(update)
|
|
602
|
+
this.handleUnitMessage(update, state)
|
|
816
603
|
return
|
|
817
604
|
case "progress":
|
|
818
605
|
await this.handleUnitProgress(update)
|
|
819
606
|
return
|
|
820
607
|
case "error":
|
|
821
|
-
await this.handleUnitError(update)
|
|
608
|
+
await this.handleUnitError(update, state)
|
|
822
609
|
return
|
|
823
610
|
case "completion":
|
|
824
|
-
await this.handleUnitCompletion(update)
|
|
611
|
+
await this.handleUnitCompletion(update, state)
|
|
825
612
|
return
|
|
826
613
|
}
|
|
827
614
|
}
|
|
828
615
|
|
|
829
|
-
private handleUnitMessage(update: TypedUnitStateUpdate<"message"
|
|
830
|
-
//
|
|
831
|
-
const instanceIdsInHierarchy: string[] = []
|
|
832
|
-
let instance: InstanceModel | null = this.workset.getInstance(update.unitId)
|
|
833
|
-
|
|
616
|
+
private handleUnitMessage(update: TypedUnitStateUpdate<"message">, state: InstanceState): void {
|
|
617
|
+
// append logs to the instance and all its parents
|
|
834
618
|
for (;;) {
|
|
835
|
-
|
|
619
|
+
this.promiseTracker.track(
|
|
620
|
+
this.operationService.appendLog(
|
|
621
|
+
this.project.id,
|
|
622
|
+
this.operation.id,
|
|
623
|
+
state.id,
|
|
624
|
+
update.message,
|
|
625
|
+
),
|
|
626
|
+
)
|
|
836
627
|
|
|
837
|
-
if (!
|
|
628
|
+
if (!state.parentInstanceId) {
|
|
838
629
|
break
|
|
839
630
|
}
|
|
840
631
|
|
|
841
|
-
|
|
632
|
+
state = this.context.getState(state.parentInstanceId)
|
|
842
633
|
}
|
|
843
|
-
|
|
844
|
-
// persist the log for all instances in the hierarchy
|
|
845
|
-
// TODO: batch
|
|
846
|
-
this.promiseTracker.track(
|
|
847
|
-
this.operationService.appendLogs(this.projectId, this.operation.id, instanceIdsInHierarchy, [
|
|
848
|
-
update.message,
|
|
849
|
-
]),
|
|
850
|
-
)
|
|
851
|
-
return
|
|
852
634
|
}
|
|
853
635
|
|
|
854
636
|
private async handleUnitProgress(update: TypedUnitStateUpdate<"progress">): Promise<void> {
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
if (update.totalResourceCount !== undefined) {
|
|
863
|
-
patch.operationStatus.totalResourceCount = update.totalResourceCount
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
await this.workset.patchState(patch)
|
|
637
|
+
await this.workset.updateState(update.unitId, {
|
|
638
|
+
operationState: {
|
|
639
|
+
currentResourceCount: update.currentResourceCount,
|
|
640
|
+
totalResourceCount: update.totalResourceCount,
|
|
641
|
+
},
|
|
642
|
+
})
|
|
867
643
|
}
|
|
868
644
|
|
|
869
|
-
private async handleUnitError(
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
status: "error",
|
|
645
|
+
private async handleUnitError(
|
|
646
|
+
update: TypedUnitStateUpdate<"error">,
|
|
647
|
+
state: InstanceState,
|
|
648
|
+
): Promise<void> {
|
|
649
|
+
await this.workset.updateState(update.unitId, {
|
|
650
|
+
instanceState: {
|
|
651
|
+
// keep "deployed" status for initially deployed instances even if the operation was failed or cancelled
|
|
652
|
+
status: state.status === "deployed" ? "deployed" : "failed",
|
|
878
653
|
message: update.message,
|
|
879
654
|
},
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
655
|
+
operationState: {
|
|
656
|
+
status: isAbortErrorLike(update.message) ? "cancelled" : "failed",
|
|
657
|
+
finishedAt: new Date(),
|
|
658
|
+
},
|
|
659
|
+
})
|
|
883
660
|
}
|
|
884
661
|
|
|
885
|
-
private async handleUnitCompletion(
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
const state = this.workset.getState(update.unitId)
|
|
893
|
-
if (!state) {
|
|
894
|
-
throw new Error(
|
|
895
|
-
`Cannot handle completion for unit "${update.unitId}" because its state is not found.`,
|
|
896
|
-
)
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
const instance = this.workset.getInstance(update.unitId)
|
|
662
|
+
private async handleUnitCompletion(
|
|
663
|
+
update: TypedUnitStateUpdate<"completion">,
|
|
664
|
+
state: InstanceState,
|
|
665
|
+
): Promise<void> {
|
|
666
|
+
const instance = this.context.getInstance(update.unitId)
|
|
900
667
|
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
operationStatus: { outputHash: update.outputHash },
|
|
906
|
-
})
|
|
668
|
+
const data: InstanceStatePatch = {
|
|
669
|
+
status: this.workset.getNextStableInstanceStatus(instance.id),
|
|
670
|
+
message: update.message,
|
|
671
|
+
statusFields: update.statusFields ?? null,
|
|
907
672
|
}
|
|
908
673
|
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
id: update.unitId,
|
|
914
|
-
operationStatus: {
|
|
915
|
-
status: "created",
|
|
916
|
-
message:
|
|
917
|
-
update.message ?? "The operation on this instance has been completed successfully.",
|
|
918
|
-
outputHash: update.outputHash ?? undefined,
|
|
919
|
-
inputHash,
|
|
920
|
-
dependencyOutputHash,
|
|
921
|
-
},
|
|
922
|
-
extra: {
|
|
923
|
-
statusFields: update.statusFields,
|
|
674
|
+
if (update.operationType !== "destroy") {
|
|
675
|
+
// давайте еще больше усложним и без того сложную штуку
|
|
676
|
+
// set output hash before calculating input hash to capture up-to-date output hash for dependencies
|
|
677
|
+
state.outputHash = update.outputHash ?? null
|
|
924
678
|
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
679
|
+
// recalculate the input and output hashes for the instance
|
|
680
|
+
const { inputHash, dependencyOutputHash } =
|
|
681
|
+
await this.context.getUpToDateInputHashOutput(instance)
|
|
928
682
|
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
683
|
+
data.inputHash = inputHash
|
|
684
|
+
data.dependencyOutputHash = dependencyOutputHash
|
|
685
|
+
data.outputHash = update.outputHash
|
|
932
686
|
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
687
|
+
// also update the parent ID
|
|
688
|
+
if (instance.parentId) {
|
|
689
|
+
const parentState = this.context.getState(instance.parentId)
|
|
690
|
+
data.parentId = parentState.id
|
|
691
|
+
} else {
|
|
692
|
+
data.parentId = null
|
|
693
|
+
}
|
|
694
|
+
} else {
|
|
695
|
+
data.message = null
|
|
696
|
+
data.inputHash = null
|
|
697
|
+
data.dependencyOutputHash = null
|
|
698
|
+
data.outputHash = null
|
|
699
|
+
data.parentId = null
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// update the operation state
|
|
703
|
+
await this.workset.updateState(instance.id, {
|
|
704
|
+
// TODO: honestly, it is not correct
|
|
705
|
+
// may be we should track operation phases separately
|
|
706
|
+
// or introduce status like "destroyed-before-recreation" (quite ugly though)
|
|
707
|
+
operationState: {
|
|
708
|
+
status: this.workset.getStableStatusByOperationPhase(),
|
|
709
|
+
finishedAt: new Date(),
|
|
940
710
|
},
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
if (update.secrets) {
|
|
944
|
-
const instance = this.workset.getInstance(update.unitId)
|
|
945
|
-
|
|
946
|
-
this.promiseTracker.track(
|
|
947
|
-
this.updateInstanceSecrets(
|
|
948
|
-
instance,
|
|
949
|
-
mapValues(update.secrets, value => stringToValue(value)),
|
|
950
|
-
),
|
|
951
|
-
)
|
|
952
|
-
}
|
|
953
711
|
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
712
|
+
// do not write instance state for non-last phases of the instance
|
|
713
|
+
instanceState: this.workset.isLastPhaseForInstance(instance.id) ? data : undefined,
|
|
714
|
+
|
|
715
|
+
// also do not write unit extra data for non-last phases of the instance
|
|
716
|
+
unitExtra: this.workset.isLastPhaseForInstance(instance.id)
|
|
717
|
+
? {
|
|
718
|
+
pages: update.pages ?? [],
|
|
719
|
+
terminals: update.terminals ?? [],
|
|
720
|
+
triggers: update.triggers ?? [],
|
|
721
|
+
workers: update.workers ?? [],
|
|
722
|
+
secrets: update.secrets ?? {},
|
|
723
|
+
}
|
|
724
|
+
: undefined,
|
|
725
|
+
})
|
|
965
726
|
}
|
|
966
727
|
|
|
967
728
|
private getInstancePromise(
|
|
968
|
-
instanceId:
|
|
729
|
+
instanceId: InstanceId,
|
|
969
730
|
fn: (logger: Logger, signal: AbortSignal, forceSignal: AbortSignal) => Promise<void>,
|
|
970
731
|
): Promise<void> {
|
|
971
732
|
let instancePromise = this.instancePromiseMap.get(instanceId)
|
|
@@ -973,30 +734,54 @@ export class RuntimeOperation {
|
|
|
973
734
|
return instancePromise
|
|
974
735
|
}
|
|
975
736
|
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
737
|
+
// "pending" -> "running" if at least one instance is running
|
|
738
|
+
if (this.operation.status === "pending") {
|
|
739
|
+
this.operation.status = "running"
|
|
740
|
+
this.promiseTracker.track(this.updateOperation({ status: this.operation.status }))
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
const state = this.context.getState(instanceId)
|
|
979
744
|
|
|
980
|
-
|
|
745
|
+
const logger = this.logger.child({ instanceId }, { msgPrefix: `[${instanceId}] ` })
|
|
746
|
+
const abortControllerPair = this.workset.instanceAbortControllers.get(instanceId)
|
|
747
|
+
if (!abortControllerPair) {
|
|
981
748
|
throw new Error(`Abort controllers for instance "${instanceId}" are not initialized`)
|
|
982
749
|
}
|
|
983
750
|
|
|
751
|
+
const { abortController, forceAbortController } = abortControllerPair
|
|
752
|
+
|
|
984
753
|
instancePromise = this.workset
|
|
985
|
-
.
|
|
754
|
+
.waitForInstanceLock(state.id, abortController.signal)
|
|
986
755
|
.then(() => fn(logger, abortController.signal, forceAbortController.signal))
|
|
987
756
|
.catch(error => {
|
|
988
|
-
if (isAbortErrorLike(error)) {
|
|
989
|
-
// if the operation was aborted, restore the initial state of the instance
|
|
990
|
-
this.promiseTracker.track(this.workset.restoreInitialState(instanceId))
|
|
991
|
-
|
|
992
|
-
throw error
|
|
993
|
-
}
|
|
994
|
-
|
|
995
757
|
if (this.operation.status !== "failing") {
|
|
996
758
|
// report the failing status of the operation
|
|
997
759
|
this.operation.status = "failing"
|
|
760
|
+
this.promiseTracker.track(this.updateOperation({ status: this.operation.status }))
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
if (isTransientInstanceOperationStatus(state.lastOperationState?.status)) {
|
|
764
|
+
// if the underlying method did not correctly update the instance status, do it here
|
|
765
|
+
this.promiseTracker.track(
|
|
766
|
+
this.workset.updateState(instanceId, {
|
|
767
|
+
operationState: {
|
|
768
|
+
status: isAbortErrorLike(error) ? "cancelled" : "failed",
|
|
769
|
+
finishedAt: new Date(),
|
|
770
|
+
},
|
|
771
|
+
instanceState: {
|
|
772
|
+
// keep "deployed" status for initially deployed instances even if the operation was failed or cancelled
|
|
773
|
+
status: state.status === "deployed" ? "deployed" : "failed",
|
|
774
|
+
},
|
|
775
|
+
}),
|
|
776
|
+
)
|
|
777
|
+
|
|
998
778
|
this.promiseTracker.track(
|
|
999
|
-
this.operationService.
|
|
779
|
+
this.operationService.appendLog(
|
|
780
|
+
this.project.id,
|
|
781
|
+
this.operation.id,
|
|
782
|
+
state.id,
|
|
783
|
+
errorToString(error),
|
|
784
|
+
),
|
|
1000
785
|
)
|
|
1001
786
|
}
|
|
1002
787
|
|
|
@@ -1004,48 +789,67 @@ export class RuntimeOperation {
|
|
|
1004
789
|
throw error
|
|
1005
790
|
})
|
|
1006
791
|
.finally(() => {
|
|
792
|
+
if (!this.workset.isLastPhaseForInstance(instanceId)) {
|
|
793
|
+
// do not finalize the instance if it has more phases to run
|
|
794
|
+
return
|
|
795
|
+
}
|
|
796
|
+
|
|
1007
797
|
this.instancePromiseMap.delete(instanceId)
|
|
1008
798
|
|
|
1009
799
|
// TODO: ideally we should defer unlocking until all direct dependents are completed,
|
|
1010
800
|
// to ensure that they are received expected inputs from this instance
|
|
1011
801
|
this.promiseTracker.track(
|
|
1012
|
-
this.instanceLockService
|
|
802
|
+
this.instanceLockService
|
|
803
|
+
.unlockInstances(this.project.id, [state.id], this.unlockToken)
|
|
804
|
+
.then(() => this.workset.markInstanceUnlocked(state.id)),
|
|
1013
805
|
)
|
|
806
|
+
|
|
807
|
+
this.logger.debug(`promise for instance "%s" completed`, instanceId)
|
|
1014
808
|
})
|
|
1015
809
|
|
|
1016
810
|
this.instancePromiseMap.set(instanceId, instancePromise)
|
|
811
|
+
this.logger.trace(`created new promise for instance "%s"`, instanceId)
|
|
1017
812
|
|
|
1018
813
|
return instancePromise
|
|
1019
814
|
}
|
|
1020
815
|
|
|
1021
|
-
private async
|
|
1022
|
-
const
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
parentId: instance.parentId,
|
|
1027
|
-
operationStatus: {
|
|
1028
|
-
status: "pending",
|
|
1029
|
-
message: "",
|
|
1030
|
-
dependencyIds: this.getInstanceDependencyIds(instance.id),
|
|
1031
|
-
latestOperationId: this.operation.id,
|
|
1032
|
-
inputHashNonce: state?.operationStatus?.inputHashNonce ?? randomBytes(4).readInt32LE(),
|
|
1033
|
-
args: instance.args,
|
|
1034
|
-
},
|
|
1035
|
-
})
|
|
1036
|
-
}
|
|
816
|
+
private async ensureInstancesUnlocked(): Promise<void> {
|
|
817
|
+
const lockedStateIds = Array.from(this.workset.getLockedStateIds())
|
|
818
|
+
if (lockedStateIds.length === 0) {
|
|
819
|
+
return
|
|
820
|
+
}
|
|
1037
821
|
|
|
1038
|
-
|
|
1039
|
-
const dependencies = new Set<string>()
|
|
1040
|
-
const instanceInputs = this.workset.resolvedInstanceInputs.get(instanceId) ?? {}
|
|
822
|
+
this.logger.warn("unlocking %d locked instances before shutting down", lockedStateIds.length)
|
|
1041
823
|
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
824
|
+
await this.instanceLockService.unlockInstances(
|
|
825
|
+
this.project.id,
|
|
826
|
+
lockedStateIds,
|
|
827
|
+
this.unlockToken,
|
|
828
|
+
)
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
private async ensureOperationStatesFinalized(): Promise<void> {
|
|
832
|
+
const unfinishedStates = this.context.getUnfinishedOperationStates()
|
|
833
|
+
if (unfinishedStates.length === 0) {
|
|
834
|
+
return
|
|
1046
835
|
}
|
|
1047
836
|
|
|
1048
|
-
|
|
837
|
+
this.logger.warn(
|
|
838
|
+
"finalizing %d unfinished operation states before shutting down",
|
|
839
|
+
unfinishedStates.length,
|
|
840
|
+
)
|
|
841
|
+
|
|
842
|
+
for (const state of unfinishedStates) {
|
|
843
|
+
await this.workset.updateState(state.instanceId, {
|
|
844
|
+
operationState: {
|
|
845
|
+
status: "failed",
|
|
846
|
+
finishedAt: new Date(),
|
|
847
|
+
},
|
|
848
|
+
instanceState: {
|
|
849
|
+
status: state.status === "deployed" ? "deployed" : "failed",
|
|
850
|
+
},
|
|
851
|
+
})
|
|
852
|
+
}
|
|
1049
853
|
}
|
|
1050
854
|
|
|
1051
855
|
/**
|
|
@@ -1054,22 +858,22 @@ export class RuntimeOperation {
|
|
|
1054
858
|
*/
|
|
1055
859
|
private collectArtifactIdsForInstance(instance: InstanceModel): string[] {
|
|
1056
860
|
const artifactIds = new Set<string>()
|
|
1057
|
-
const instanceInputs = this.
|
|
861
|
+
const instanceInputs = this.context.getResolvedInputs(instance.id) ?? {}
|
|
1058
862
|
|
|
1059
863
|
for (const inputs of Object.values(instanceInputs)) {
|
|
1060
864
|
for (const input of inputs) {
|
|
1061
|
-
const dependencyState = this.
|
|
1062
|
-
if (!dependencyState
|
|
865
|
+
const dependencyState = this.context.getState(input.input.instanceId)
|
|
866
|
+
if (!dependencyState.exportedArtifactIds) {
|
|
1063
867
|
continue
|
|
1064
868
|
}
|
|
1065
869
|
|
|
1066
870
|
const outputKey = input.input.output
|
|
1067
|
-
const
|
|
1068
|
-
if (!
|
|
871
|
+
const outputArtifactIds = dependencyState.exportedArtifactIds[outputKey]
|
|
872
|
+
if (!outputArtifactIds) {
|
|
1069
873
|
continue
|
|
1070
874
|
}
|
|
1071
875
|
|
|
1072
|
-
for (const hash of
|
|
876
|
+
for (const hash of outputArtifactIds) {
|
|
1073
877
|
artifactIds.add(hash)
|
|
1074
878
|
}
|
|
1075
879
|
}
|
|
@@ -1077,266 +881,4 @@ export class RuntimeOperation {
|
|
|
1077
881
|
|
|
1078
882
|
return Array.from(artifactIds)
|
|
1079
883
|
}
|
|
1080
|
-
|
|
1081
|
-
/**
|
|
1082
|
-
* Updates the secrets for a specific instance.
|
|
1083
|
-
* Processes terminals from instance state update.
|
|
1084
|
-
*
|
|
1085
|
-
* Converts unit terminals to instance terminals and handles cleanup.
|
|
1086
|
-
*/
|
|
1087
|
-
private processTerminals(
|
|
1088
|
-
instanceId: string,
|
|
1089
|
-
unitTerminals: UnitTerminal[],
|
|
1090
|
-
currentTerminalIds: Record<string, string> | undefined,
|
|
1091
|
-
): Record<string, string> {
|
|
1092
|
-
const terminals: Terminal[] = unitTerminals.map(unitTerminal => ({
|
|
1093
|
-
id: currentTerminalIds?.[unitTerminal.name] ?? uuidv7(),
|
|
1094
|
-
instanceId,
|
|
1095
|
-
meta: unitTerminal.meta ?? {},
|
|
1096
|
-
}))
|
|
1097
|
-
|
|
1098
|
-
const terminalSpecEntries: [string, TerminalSpec][] = unitTerminals
|
|
1099
|
-
//
|
|
1100
|
-
.map((unitTerminal, index) => [terminals[index].id, unitTerminal.spec])
|
|
1101
|
-
|
|
1102
|
-
// create mapping from local name to UUID
|
|
1103
|
-
const newTerminalIds: Record<string, string> = {}
|
|
1104
|
-
for (let i = 0; i < unitTerminals.length; i++) {
|
|
1105
|
-
const unitTerminal = unitTerminals[i]
|
|
1106
|
-
const instanceTerminal = terminals[i]
|
|
1107
|
-
newTerminalIds[unitTerminal.name] = instanceTerminal.id
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
|
-
// find terminals that need to be deleted (old terminals not in new list)
|
|
1111
|
-
const terminalsToDelete = Object.values(currentTerminalIds ?? {}).filter(
|
|
1112
|
-
id => !Object.values(newTerminalIds).includes(id),
|
|
1113
|
-
)
|
|
1114
|
-
|
|
1115
|
-
if (terminals.length > 0) {
|
|
1116
|
-
this.promiseTracker.track(this.putTerminals(terminals, terminalSpecEntries))
|
|
1117
|
-
}
|
|
1118
|
-
|
|
1119
|
-
if (terminalsToDelete.length > 0) {
|
|
1120
|
-
this.promiseTracker.track(this.deleteTerminals(terminalsToDelete))
|
|
1121
|
-
}
|
|
1122
|
-
|
|
1123
|
-
return newTerminalIds
|
|
1124
|
-
}
|
|
1125
|
-
|
|
1126
|
-
private async putTerminals(
|
|
1127
|
-
terminals: Terminal[],
|
|
1128
|
-
terminalSpecEntries: [string, TerminalSpec][],
|
|
1129
|
-
): Promise<void> {
|
|
1130
|
-
if (this.operation.type === "preview") {
|
|
1131
|
-
return
|
|
1132
|
-
}
|
|
1133
|
-
|
|
1134
|
-
await using batch = this.stateManager.batch()
|
|
1135
|
-
|
|
1136
|
-
await Promise.all([
|
|
1137
|
-
this.stateManager.getTerminalRepository(this.projectId).putManyItems(terminals, batch),
|
|
1138
|
-
|
|
1139
|
-
this.stateManager
|
|
1140
|
-
.getTerminalSpecRepository(this.projectId)
|
|
1141
|
-
.putMany(terminalSpecEntries, batch),
|
|
1142
|
-
])
|
|
1143
|
-
|
|
1144
|
-
await batch.write()
|
|
1145
|
-
}
|
|
1146
|
-
|
|
1147
|
-
private async deleteTerminals(terminalIds: string[]): Promise<void> {
|
|
1148
|
-
if (this.operation.type === "preview") {
|
|
1149
|
-
return
|
|
1150
|
-
}
|
|
1151
|
-
|
|
1152
|
-
await using batch = this.stateManager.batch()
|
|
1153
|
-
|
|
1154
|
-
await Promise.all([
|
|
1155
|
-
this.stateManager.getTerminalRepository(this.projectId).deleteMany(terminalIds, batch),
|
|
1156
|
-
this.stateManager.getTerminalSpecRepository(this.projectId).deleteMany(terminalIds, batch),
|
|
1157
|
-
])
|
|
1158
|
-
|
|
1159
|
-
await batch.write()
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
/**
|
|
1163
|
-
* Processes pages from instance state update.
|
|
1164
|
-
*
|
|
1165
|
-
* Converts unit pages to instance pages and handles cleanup.
|
|
1166
|
-
*/
|
|
1167
|
-
private processPages(
|
|
1168
|
-
instanceId: string,
|
|
1169
|
-
unitPages: UnitPage[],
|
|
1170
|
-
currentPageIds: Record<string, string> | undefined,
|
|
1171
|
-
): Record<string, string> {
|
|
1172
|
-
const pages: Page[] = unitPages.map(unitPage => ({
|
|
1173
|
-
id: currentPageIds?.[unitPage.name] ?? uuidv7(),
|
|
1174
|
-
instanceId,
|
|
1175
|
-
meta: unitPage.meta,
|
|
1176
|
-
}))
|
|
1177
|
-
|
|
1178
|
-
const pageContentEntries: [string, PageBlock[]][] = unitPages
|
|
1179
|
-
//
|
|
1180
|
-
.map((unitPage, index) => [pages[index].id, unitPage.content])
|
|
1181
|
-
|
|
1182
|
-
// create mapping from local name to UUID
|
|
1183
|
-
const newPageIds: Record<string, string> = {}
|
|
1184
|
-
for (let i = 0; i < unitPages.length; i++) {
|
|
1185
|
-
const unitPage = unitPages[i]
|
|
1186
|
-
const instancePage = pages[i]
|
|
1187
|
-
newPageIds[unitPage.name] = instancePage.id
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
|
-
// find pages that need to be deleted (old pages not in new list)
|
|
1191
|
-
const pagesToDelete = Object.values(currentPageIds ?? {}).filter(
|
|
1192
|
-
id => !Object.values(newPageIds).includes(id),
|
|
1193
|
-
)
|
|
1194
|
-
|
|
1195
|
-
if (pages.length > 0) {
|
|
1196
|
-
this.promiseTracker.track(this.putPages(pages, pageContentEntries))
|
|
1197
|
-
}
|
|
1198
|
-
|
|
1199
|
-
if (pagesToDelete.length > 0) {
|
|
1200
|
-
this.promiseTracker.track(this.deletePages(pagesToDelete))
|
|
1201
|
-
}
|
|
1202
|
-
|
|
1203
|
-
return newPageIds
|
|
1204
|
-
}
|
|
1205
|
-
|
|
1206
|
-
private async putPages(
|
|
1207
|
-
pages: Page[],
|
|
1208
|
-
pageContentEntries: [string, PageBlock[]][],
|
|
1209
|
-
): Promise<void> {
|
|
1210
|
-
if (this.operation.type === "preview") {
|
|
1211
|
-
return
|
|
1212
|
-
}
|
|
1213
|
-
|
|
1214
|
-
const batch = this.stateManager.batch()
|
|
1215
|
-
|
|
1216
|
-
await Promise.all([
|
|
1217
|
-
this.stateManager.getPageRepository(this.projectId).putManyItems(pages, batch),
|
|
1218
|
-
this.stateManager.getPageContentRepository(this.projectId).putMany(pageContentEntries, batch),
|
|
1219
|
-
])
|
|
1220
|
-
|
|
1221
|
-
await batch.write()
|
|
1222
|
-
}
|
|
1223
|
-
|
|
1224
|
-
private async deletePages(pageIds: string[]): Promise<void> {
|
|
1225
|
-
if (this.operation.type === "preview") {
|
|
1226
|
-
return
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1229
|
-
const batch = this.stateManager.batch()
|
|
1230
|
-
|
|
1231
|
-
await Promise.all([
|
|
1232
|
-
this.stateManager.getPageRepository(this.projectId).deleteMany(pageIds, batch),
|
|
1233
|
-
this.stateManager.getPageContentRepository(this.projectId).deleteMany(pageIds, batch),
|
|
1234
|
-
])
|
|
1235
|
-
|
|
1236
|
-
await batch.write()
|
|
1237
|
-
}
|
|
1238
|
-
|
|
1239
|
-
/**
|
|
1240
|
-
* Processes triggers from instance state update.
|
|
1241
|
-
*
|
|
1242
|
-
* Converts unit triggers to instance triggers and handles cleanup.
|
|
1243
|
-
*/
|
|
1244
|
-
private processTriggers(
|
|
1245
|
-
instanceId: string,
|
|
1246
|
-
unitTriggers: UnitTrigger[],
|
|
1247
|
-
currentTriggerIds: Record<string, string> | undefined,
|
|
1248
|
-
): Record<string, string> {
|
|
1249
|
-
const triggers: Trigger[] = unitTriggers.map(unitTrigger => ({
|
|
1250
|
-
id: currentTriggerIds?.[unitTrigger.name] ?? uuidv7(),
|
|
1251
|
-
instanceId,
|
|
1252
|
-
meta: unitTrigger.meta ?? {},
|
|
1253
|
-
spec: unitTrigger.spec,
|
|
1254
|
-
}))
|
|
1255
|
-
|
|
1256
|
-
// create mapping from local name to UUID
|
|
1257
|
-
const newTriggerIds: Record<string, string> = {}
|
|
1258
|
-
for (let i = 0; i < unitTriggers.length; i++) {
|
|
1259
|
-
const unitTrigger = unitTriggers[i]
|
|
1260
|
-
newTriggerIds[unitTrigger.name] = triggers[i].id
|
|
1261
|
-
}
|
|
1262
|
-
|
|
1263
|
-
// find triggers that need to be deleted (old triggers not in new list)
|
|
1264
|
-
const triggersToDelete = Object.values(currentTriggerIds ?? {}).filter(
|
|
1265
|
-
id => !Object.values(newTriggerIds).includes(id),
|
|
1266
|
-
)
|
|
1267
|
-
|
|
1268
|
-
if (triggers.length > 0) {
|
|
1269
|
-
this.promiseTracker.track(this.putTriggers(triggers))
|
|
1270
|
-
}
|
|
1271
|
-
|
|
1272
|
-
if (triggersToDelete.length > 0) {
|
|
1273
|
-
this.promiseTracker.track(this.deleteTriggers(triggersToDelete))
|
|
1274
|
-
}
|
|
1275
|
-
|
|
1276
|
-
return newTriggerIds
|
|
1277
|
-
}
|
|
1278
|
-
|
|
1279
|
-
private async putTriggers(triggers: Trigger[]): Promise<void> {
|
|
1280
|
-
if (this.operation.type === "preview") {
|
|
1281
|
-
return
|
|
1282
|
-
}
|
|
1283
|
-
|
|
1284
|
-
await this.stateManager.getTriggerRepository(this.projectId).putManyItems(triggers)
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
private async deleteTriggers(triggerIds: string[]): Promise<void> {
|
|
1288
|
-
if (this.operation.type === "preview") {
|
|
1289
|
-
return
|
|
1290
|
-
}
|
|
1291
|
-
|
|
1292
|
-
await this.stateManager.getTriggerRepository(this.projectId).deleteMany(triggerIds)
|
|
1293
|
-
}
|
|
1294
|
-
|
|
1295
|
-
/**
|
|
1296
|
-
* Processes artifacts from instance state update.
|
|
1297
|
-
*/
|
|
1298
|
-
private processArtifacts(
|
|
1299
|
-
instanceId: string,
|
|
1300
|
-
newOwnedArtifacts: Record<string, RunnerArtifact[]>,
|
|
1301
|
-
oldOwnedArtifactIds: Record<string, string[]> | undefined,
|
|
1302
|
-
): Record<string, string[]> {
|
|
1303
|
-
if (this.operation.type !== "preview") {
|
|
1304
|
-
// persist the new owned artifacts if this is not a preview operation
|
|
1305
|
-
this.promiseTracker.track(
|
|
1306
|
-
this.artifactService.updateUsage(
|
|
1307
|
-
this.projectId,
|
|
1308
|
-
{ type: "instance", instanceId },
|
|
1309
|
-
unique(Object.values(oldOwnedArtifactIds ?? {}).flat()),
|
|
1310
|
-
unique(
|
|
1311
|
-
Object.values(newOwnedArtifacts).flatMap(artifacts =>
|
|
1312
|
-
artifacts.map(artifact => artifact.id),
|
|
1313
|
-
),
|
|
1314
|
-
),
|
|
1315
|
-
),
|
|
1316
|
-
)
|
|
1317
|
-
}
|
|
1318
|
-
|
|
1319
|
-
return mapValues(newOwnedArtifacts, artifacts => artifacts.map(artifact => artifact.id))
|
|
1320
|
-
}
|
|
1321
|
-
|
|
1322
|
-
/**
|
|
1323
|
-
* Updates the secrets for a specific instance.
|
|
1324
|
-
*/
|
|
1325
|
-
private async updateInstanceSecrets(
|
|
1326
|
-
instance: InstanceModel,
|
|
1327
|
-
secrets: Record<string, unknown>,
|
|
1328
|
-
): Promise<void> {
|
|
1329
|
-
if (this.operation.type === "preview") {
|
|
1330
|
-
// do not update secrets in preview mode
|
|
1331
|
-
return
|
|
1332
|
-
}
|
|
1333
|
-
|
|
1334
|
-
await this.secretService.updateInstanceSecrets(
|
|
1335
|
-
this.projectId,
|
|
1336
|
-
instance.id,
|
|
1337
|
-
secrets,
|
|
1338
|
-
[], // no secrets to delete in this context
|
|
1339
|
-
false, // do not invalidate state even if the secrets were updated
|
|
1340
|
-
)
|
|
1341
|
-
}
|
|
1342
884
|
}
|