@highstate/backend 0.9.18 → 0.9.20
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-OU5OQBLB.js → chunk-I7BWSAN6.js} +3 -28
- package/dist/{chunk-OU5OQBLB.js.map → chunk-I7BWSAN6.js.map} +1 -1
- package/dist/chunk-RC6Q3XQQ.js +1547 -0
- package/dist/chunk-RC6Q3XQQ.js.map +1 -0
- package/dist/chunk-VB4YL327.js +139 -0
- 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 +2 -1
- package/dist/index.js +7590 -7289
- package/dist/index.js.map +1 -1
- package/dist/library/package-resolution-worker.js +1 -1
- package/dist/library/package-resolution-worker.js.map +1 -1
- package/dist/library/worker/main.js +35 -29
- package/dist/library/worker/main.js.map +1 -1
- package/dist/shared/index.js +2 -2
- package/package.json +18 -9
- 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 +32 -0
- package/prisma/project/artifact.prisma +52 -0
- package/prisma/project/custom-status.prisma +46 -0
- package/prisma/project/evaluation.prisma +45 -0
- package/prisma/project/instance.prisma +157 -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 +49 -0
- package/prisma/project/secret.prisma +54 -0
- package/prisma/project/service-account.prisma +42 -0
- package/prisma/project/terminal.prisma +107 -0
- package/prisma/project/trigger.prisma +37 -0
- package/prisma/project/unlock-method.prisma +46 -0
- package/prisma/project/worker.prisma +169 -0
- package/src/artifact/abstractions.ts +13 -13
- package/src/artifact/encryption.ts +30 -54
- package/src/artifact/factory.ts +6 -9
- package/src/artifact/local.ts +33 -46
- package/src/business/api-key.ts +24 -36
- package/src/business/artifact.test.ts +978 -0
- package/src/business/artifact.ts +136 -216
- package/src/business/evaluation.ts +328 -0
- package/src/business/index.ts +5 -2
- package/src/business/instance-lock.test.ts +1060 -0
- package/src/business/instance-lock.ts +387 -78
- package/src/business/instance-state.test.ts +735 -0
- package/src/business/instance-state.ts +582 -337
- 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 +168 -126
- package/src/business/project.ts +287 -179
- package/src/business/secret.test.ts +469 -130
- package/src/business/secret.ts +177 -217
- 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 +356 -579
- package/src/business/worker.ts +238 -339
- package/src/common/codebase.ts +65 -0
- package/src/common/index.ts +3 -5
- package/src/common/logger.ts +5 -0
- package/src/common/utils.ts +4 -3
- package/src/config.ts +10 -11
- 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 +3 -5
- package/src/library/factory.ts +1 -1
- package/src/library/local.ts +81 -26
- package/src/library/package-resolution-worker.ts +1 -1
- package/src/library/worker/evaluator.ts +40 -23
- package/src/library/worker/loader.lite.ts +1 -1
- package/src/library/worker/main.ts +3 -10
- package/src/library/worker/protocol.ts +0 -1
- package/src/lock/index.ts +0 -1
- package/src/lock/manager.ts +0 -10
- package/src/orchestrator/manager.ts +190 -104
- 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 +233 -578
- package/src/orchestrator/operation.ts +440 -948
- 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 +29 -13
- package/src/pubsub/memory.ts +31 -0
- package/src/runner/abstractions.ts +40 -41
- package/src/runner/artifact-env.ts +19 -8
- package/src/runner/factory.ts +6 -6
- package/src/runner/force-abort.ts +3 -6
- package/src/runner/local.ts +74 -67
- package/src/runner/pulumi.ts +23 -63
- package/src/services.ts +181 -123
- package/src/shared/models/backend/index.ts +3 -1
- package/src/shared/models/backend/library.ts +9 -1
- package/src/shared/models/backend/project.ts +43 -42
- 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 -26
- 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 -59
- package/src/shared/models/project/artifact.ts +16 -76
- package/src/shared/models/project/custom-status.ts +12 -0
- package/src/shared/models/project/index.ts +8 -7
- package/src/shared/models/project/lock.ts +10 -78
- package/src/shared/models/project/model.ts +19 -1
- package/src/shared/models/project/operation.ts +235 -99
- package/src/shared/models/project/page.ts +37 -48
- package/src/shared/models/project/secret.ts +29 -89
- package/src/shared/models/project/service-account.ts +12 -17
- package/src/shared/models/project/state.ts +100 -407
- package/src/shared/models/project/terminal.ts +75 -88
- package/src/shared/models/project/trigger.ts +13 -49
- package/src/shared/models/project/unlock-method.ts +20 -26
- package/src/shared/models/project/worker.ts +89 -90
- package/src/shared/resolvers/graph-resolver.ts +21 -0
- package/src/shared/resolvers/index.ts +1 -1
- package/src/shared/resolvers/input-hash.ts +24 -14
- package/src/shared/resolvers/input.ts +9 -2
- package/src/shared/resolvers/registry.ts +5 -4
- package/src/shared/resolvers/state.ts +12 -1
- package/src/shared/resolvers/validation.ts +7 -3
- package/src/shared/utils/index.ts +1 -2
- 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 +5 -23
- package/src/unlock/memory.ts +9 -14
- package/src/worker/abstractions.ts +7 -4
- package/src/worker/docker.ts +14 -19
- package/src/worker/manager.ts +366 -97
- package/dist/chunk-NAAIDR4U.js +0 -8499
- package/dist/chunk-NAAIDR4U.js.map +0 -1
- package/dist/chunk-Y7DXREVO.js +0 -1745
- package/dist/chunk-Y7DXREVO.js.map +0 -1
- package/dist/magic-string.es-5ABAC4JN.js +0 -1292
- package/dist/magic-string.es-5ABAC4JN.js.map +0 -1
- package/src/business/__traces__/secret/update-instance-secrets/create-and-delete-secrets-simultaneously.md +0 -356
- package/src/business/__traces__/secret/update-instance-secrets/create-new-secrets-for-instance.md +0 -274
- package/src/business/__traces__/secret/update-instance-secrets/delete-existing-secrets.md +0 -223
- package/src/business/__traces__/secret/update-instance-secrets/no-op-when-no-changes.md +0 -147
- package/src/business/__traces__/secret/update-instance-secrets/update-existing-secrets.md +0 -280
- package/src/business/__traces__/worker/update-unit-registrations/add-new-unit-registration-when-other-exists.md +0 -360
- package/src/business/__traces__/worker/update-unit-registrations/add-new-unit-registration.md +0 -215
- package/src/business/__traces__/worker/update-unit-registrations/create-multiple-workers-with-different-identities.md +0 -427
- package/src/business/__traces__/worker/update-unit-registrations/handle-nonexistent-registration-id-gracefully.md +0 -217
- package/src/business/__traces__/worker/update-unit-registrations/no-op-when-no-changes.md +0 -132
- package/src/business/__traces__/worker/update-unit-registrations/recreate-worker-when-image-changes.md +0 -454
- package/src/business/__traces__/worker/update-unit-registrations/recreate-worker-when-image-version-changes.md +0 -426
- package/src/business/__traces__/worker/update-unit-registrations/recreate-worker-with-same-identity-reuses-service-account.md +0 -372
- package/src/business/__traces__/worker/update-unit-registrations/remove-one-of-multiple-unit-registrations.md +0 -383
- package/src/business/__traces__/worker/update-unit-registrations/remove-unit-registration.md +0 -245
- package/src/business/__traces__/worker/update-unit-registrations/update-existing-unit-registration-when-params-change.md +0 -174
- package/src/business/__traces__/worker/update-unit-registrations/update-params-and-image-simultaneously.md +0 -432
- package/src/business/__traces__/worker/update-unit-registrations/worker-with-multiple-registrations-not-deleted-when-one-removed.md +0 -220
- package/src/business/backend-unlock.ts +0 -10
- package/src/common/clock.ts +0 -18
- package/src/common/performance.ts +0 -44
- package/src/common/random.ts +0 -68
- package/src/common/test/index.ts +0 -2
- package/src/common/test/render.ts +0 -98
- package/src/common/test/tracer.ts +0 -359
- 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 -100
- package/src/lock/test.ts +0 -108
- package/src/project/abstractions.ts +0 -78
- package/src/project/evaluation.ts +0 -248
- package/src/project/factory.ts +0 -11
- package/src/project/index.ts +0 -3
- package/src/project/local.ts +0 -417
- package/src/pubsub/local.ts +0 -36
- package/src/pubsub/validation.ts +0 -33
- package/src/shared/utils/args.ts +0 -25
- package/src/state/abstractions.ts +0 -289
- package/src/state/encryption.ts +0 -98
- package/src/state/factory.ts +0 -20
- package/src/state/index.ts +0 -7
- package/src/state/local/backend.ts +0 -106
- package/src/state/local/collection.ts +0 -361
- package/src/state/local/index.ts +0 -2
- package/src/state/manager.ts +0 -890
- package/src/state/memory/backend.ts +0 -70
- package/src/state/memory/collection.ts +0 -270
- package/src/state/memory/index.ts +0 -2
- package/src/state/repository/index.ts +0 -2
- package/src/state/repository/repository.index.ts +0 -193
- package/src/state/repository/repository.ts +0 -507
- package/src/state/test.ts +0 -457
- /package/src/{state → database/local}/keyring.ts +0 -0
|
@@ -1,434 +1,679 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import type {
|
|
2
|
+
ComponentKind,
|
|
3
|
+
UnitPage,
|
|
4
|
+
UnitTerminal,
|
|
5
|
+
UnitTrigger,
|
|
6
|
+
UnitWorker,
|
|
7
|
+
} from "@highstate/contract"
|
|
5
8
|
import type { Logger } from "pino"
|
|
6
|
-
import type { RunnerBackend } from "../runner"
|
|
7
9
|
import type { ArtifactService } from "../artifact"
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
+
import type { SecretService, UnitExtraService, WorkerService } from "../business"
|
|
11
|
+
import type { PubSubManager } from "../pubsub"
|
|
12
|
+
import type { RunnerBackend } from "../runner"
|
|
13
|
+
import { type InstanceId, parseInstanceId } from "@highstate/contract"
|
|
14
|
+
import { isNonNullish, omit } from "remeda"
|
|
15
|
+
import {
|
|
16
|
+
type DatabaseManager,
|
|
17
|
+
DbNull,
|
|
18
|
+
type InstanceOperationStateCreateManyInput,
|
|
19
|
+
type InstanceOperationStateUpdateInput,
|
|
20
|
+
type InstanceStateInclude,
|
|
21
|
+
type InstanceStateUpdateInput,
|
|
22
|
+
type ProjectTransaction,
|
|
23
|
+
} from "../database"
|
|
10
24
|
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
type
|
|
25
|
+
forSchema,
|
|
26
|
+
type InstanceCustomStatusInput,
|
|
27
|
+
InstanceLockedError,
|
|
28
|
+
type InstanceOperationState,
|
|
15
29
|
type InstanceState,
|
|
16
|
-
|
|
17
|
-
|
|
30
|
+
InstanceStateNotFoundError,
|
|
31
|
+
ProjectNotFoundError,
|
|
32
|
+
projectOutputSchema,
|
|
33
|
+
waitAll,
|
|
18
34
|
} from "../shared"
|
|
19
35
|
|
|
20
|
-
export
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
private readonly artifactManager: ArtifactService,
|
|
28
|
-
private readonly logger: Logger,
|
|
29
|
-
) {}
|
|
36
|
+
export type GetProjectInstancesOptions = {
|
|
37
|
+
/**
|
|
38
|
+
* Whether to include evaluation states in the result.
|
|
39
|
+
*
|
|
40
|
+
* By default, this is false.
|
|
41
|
+
*/
|
|
42
|
+
includeEvaluationState?: boolean
|
|
30
43
|
|
|
31
44
|
/**
|
|
32
|
-
*
|
|
45
|
+
* Include last operation state in the result.
|
|
33
46
|
*
|
|
34
|
-
*
|
|
35
|
-
* @param checkLoaded Whether to check if instance states are loaded in the hot state.
|
|
36
|
-
* Can be set to false if you are sure that the states are already loaded (e.g., if updating states within operation).
|
|
47
|
+
* By default, this is false.
|
|
37
48
|
*/
|
|
38
|
-
|
|
39
|
-
return await this.withLoadedInstanceStates(projectId, checkLoaded, async () => {
|
|
40
|
-
const entries = await this.hotStateManager.hgetall(["instance-states", projectId])
|
|
49
|
+
includeLastOperationState?: boolean
|
|
41
50
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
51
|
+
/**
|
|
52
|
+
* Include the instance ID of the parent instance in the result.
|
|
53
|
+
*
|
|
54
|
+
* By default, this is false.
|
|
55
|
+
*/
|
|
56
|
+
includeParentInstanceId?: boolean
|
|
45
57
|
|
|
46
58
|
/**
|
|
47
|
-
*
|
|
59
|
+
* Whether to include `terminalIds` and `pageIds` in the result.
|
|
48
60
|
*
|
|
49
|
-
*
|
|
50
|
-
* @param instanceId The ID of the instance to retrieve the state for.
|
|
51
|
-
* @param checkLoaded Whether to check if instance states are loaded in the hot state.
|
|
52
|
-
* Can be set to false if you are sure that the states are already loaded (e.g., if updating states within operation).
|
|
61
|
+
* By default, this is false.
|
|
53
62
|
*/
|
|
54
|
-
|
|
55
|
-
projectId: string,
|
|
56
|
-
instanceId: string,
|
|
57
|
-
checkLoaded = true,
|
|
58
|
-
): Promise<InstanceState | null> {
|
|
59
|
-
return await this.withLoadedInstanceStates(projectId, checkLoaded, async () => {
|
|
60
|
-
return await this.hotStateManager.hget(["instance-states", projectId], instanceId)
|
|
61
|
-
})
|
|
62
|
-
}
|
|
63
|
+
includeExtra?: boolean
|
|
63
64
|
|
|
64
65
|
/**
|
|
65
|
-
*
|
|
66
|
+
* Whether to load custom statuses for each instance.
|
|
66
67
|
*
|
|
67
|
-
*
|
|
68
|
-
* @param states The array of instance states to update.
|
|
69
|
-
* @param persistent Whether to persist the changes in the state backend.
|
|
70
|
-
* @param checkLoaded Whether to check if instance states are loaded in the hot state.
|
|
71
|
-
* Can be set to false if you are sure that the states are already loaded (e.g., if updating states within operation).
|
|
68
|
+
* By default, this is false.
|
|
72
69
|
*/
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
states: InstanceState[],
|
|
76
|
-
persistent = false,
|
|
77
|
-
checkLoaded = true,
|
|
78
|
-
): Promise<InstanceState[]> {
|
|
79
|
-
return await this.withLoadedInstanceStates(projectId, checkLoaded, async () => {
|
|
80
|
-
const batch = this.stateManager.batch()
|
|
81
|
-
const promises: Promise<void>[] = []
|
|
82
|
-
|
|
83
|
-
for (const state of states) {
|
|
84
|
-
promises.push(
|
|
85
|
-
this.hotStateManager.hset(["instance-states", projectId], state.id, state),
|
|
86
|
-
this.pubsubManager.publish(["instance-state", projectId], { type: "updated", state }),
|
|
87
|
-
)
|
|
70
|
+
loadCustomStatuses?: boolean
|
|
71
|
+
}
|
|
88
72
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
73
|
+
export type ForgetInstanceStateOptions = {
|
|
74
|
+
/**
|
|
75
|
+
* Whether to delete terminals and their sessions associated with the instance.
|
|
76
|
+
*
|
|
77
|
+
* If `false`, the terminals will be marked as deleted and no new sessions will be allowed to be created,
|
|
78
|
+
* but existing sessions will remain to provide history and logs.
|
|
79
|
+
*
|
|
80
|
+
* By default, this is false.
|
|
81
|
+
*/
|
|
82
|
+
clearTerminalData?: boolean
|
|
95
83
|
|
|
96
|
-
|
|
97
|
-
|
|
84
|
+
/**
|
|
85
|
+
* Whether to delete the secrets associated with the instance.
|
|
86
|
+
*
|
|
87
|
+
* By default, this is false.
|
|
88
|
+
*/
|
|
89
|
+
deleteSecrets?: boolean
|
|
90
|
+
}
|
|
98
91
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
92
|
+
export type InstanceStatePatch = Pick<
|
|
93
|
+
Partial<InstanceState>,
|
|
94
|
+
| "status"
|
|
95
|
+
| "message"
|
|
96
|
+
| "statusFields"
|
|
97
|
+
| "parentId"
|
|
98
|
+
| "lastOperationState"
|
|
99
|
+
| "inputHash"
|
|
100
|
+
| "outputHash"
|
|
101
|
+
| "dependencyOutputHash"
|
|
102
|
+
| "model"
|
|
103
|
+
| "resolvedInputs"
|
|
104
|
+
| "currentResourceCount"
|
|
105
|
+
>
|
|
106
|
+
|
|
107
|
+
export type UpdateOperationStateOptions = {
|
|
108
|
+
/**
|
|
109
|
+
* The operation state to update.
|
|
110
|
+
*/
|
|
111
|
+
operationState: InstanceOperationStateUpdateInput
|
|
104
112
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Instance state patch to update or function to compute patch from current state.
|
|
115
|
+
*/
|
|
116
|
+
instanceState?: InstanceStatePatch
|
|
108
117
|
|
|
109
118
|
/**
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* @param projectId The ID of the project to which the instances belong.
|
|
113
|
-
* @param patches The array of instance state patches to apply.
|
|
114
|
-
* @param checkLoaded Whether to check if instance states are loaded in the hot state.
|
|
115
|
-
* Can be set to false if you are sure that the states are already loaded (e.g., if updating states within operation).
|
|
119
|
+
* Unit-specific extra data to update.
|
|
116
120
|
*/
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
121
|
+
unitExtra?: {
|
|
122
|
+
/**
|
|
123
|
+
* Unit pages to update.
|
|
124
|
+
*/
|
|
125
|
+
pages: UnitPage[]
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Unit terminals to update.
|
|
129
|
+
*/
|
|
130
|
+
terminals: UnitTerminal[]
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Unit triggers to update.
|
|
134
|
+
*/
|
|
135
|
+
triggers: UnitTrigger[]
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Unit workers to update.
|
|
139
|
+
*/
|
|
140
|
+
workers: UnitWorker[]
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Unit secrets to update.
|
|
144
|
+
*/
|
|
145
|
+
secrets: Record<string, unknown>
|
|
146
|
+
}
|
|
147
|
+
}
|
|
133
148
|
|
|
134
|
-
|
|
149
|
+
export function includeForInstanceState(
|
|
150
|
+
options: GetProjectInstancesOptions = {},
|
|
151
|
+
): InstanceStateInclude {
|
|
152
|
+
return {
|
|
153
|
+
secrets: {
|
|
154
|
+
select: { name: true },
|
|
155
|
+
},
|
|
135
156
|
|
|
136
|
-
|
|
157
|
+
parent: options.includeParentInstanceId ? { select: { instanceId: true } } : undefined,
|
|
137
158
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
projectId,
|
|
142
|
-
)
|
|
159
|
+
operationStates: options.includeLastOperationState
|
|
160
|
+
? { take: 1, orderBy: { startedAt: "desc" } }
|
|
161
|
+
: undefined,
|
|
143
162
|
|
|
144
|
-
|
|
145
|
-
})
|
|
146
|
-
}
|
|
163
|
+
evaluationState: options.includeEvaluationState,
|
|
147
164
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
* TODO: delete
|
|
151
|
-
*
|
|
152
|
-
* @param projectId The ID of the project to which the instance belongs.
|
|
153
|
-
* @param patch The patch to apply to the instance state.
|
|
154
|
-
* @param checkLoaded Whether to check if instance states are loaded in the hot state.
|
|
155
|
-
* Can be set to false if you are sure that the states are already loaded (e.g., if updating states within operation).
|
|
156
|
-
*/
|
|
157
|
-
async patchInstanceState(
|
|
158
|
-
projectId: string,
|
|
159
|
-
patch: InstanceStatePatch,
|
|
160
|
-
checkLoaded = true,
|
|
161
|
-
): Promise<InstanceState> {
|
|
162
|
-
return await this.withLoadedInstanceStates(projectId, checkLoaded, async () => {
|
|
163
|
-
return await this.lockManager.acquire(["instance-state", projectId, patch.id], async () => {
|
|
164
|
-
const state = await this.hotStateManager.hget(["instance-states", projectId], patch.id)
|
|
165
|
-
|
|
166
|
-
const patchedState = await this.applyStatePatch(
|
|
167
|
-
projectId,
|
|
168
|
-
state,
|
|
169
|
-
patch,
|
|
170
|
-
patch.operationStatus !== undefined,
|
|
171
|
-
)
|
|
165
|
+
terminals: options.includeExtra ? { select: { id: true } } : undefined,
|
|
166
|
+
pages: options.includeExtra ? { select: { id: true } } : undefined,
|
|
172
167
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
168
|
+
customStatuses: options.loadCustomStatuses
|
|
169
|
+
? {
|
|
170
|
+
orderBy: [{ order: "asc" }, { createdAt: "asc" }],
|
|
171
|
+
}
|
|
172
|
+
: undefined,
|
|
173
|
+
}
|
|
174
|
+
}
|
|
179
175
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
176
|
+
export function mapInstanceStateResult(
|
|
177
|
+
instance: InstanceState & {
|
|
178
|
+
secrets: { name: string | null }[]
|
|
179
|
+
parent?: { instanceId: InstanceId } | null
|
|
180
|
+
operationStates?: InstanceOperationState[]
|
|
181
|
+
terminals?: { id: string }[]
|
|
182
|
+
pages?: { id: string }[]
|
|
183
|
+
customStatuses: InstanceState["customStatuses"]
|
|
184
|
+
},
|
|
185
|
+
): InstanceState {
|
|
186
|
+
return {
|
|
187
|
+
...omit(instance, [
|
|
188
|
+
"secrets",
|
|
189
|
+
"parent",
|
|
190
|
+
"operationStates",
|
|
191
|
+
"terminals",
|
|
192
|
+
"pages",
|
|
193
|
+
"customStatuses",
|
|
194
|
+
]),
|
|
195
|
+
|
|
196
|
+
secretNames: instance.secrets.map(secret => secret.name).filter(isNonNullish),
|
|
197
|
+
parentInstanceId: instance.parent ? (instance.parent?.instanceId ?? null) : undefined,
|
|
198
|
+
lastOperationState: instance.operationStates?.[0],
|
|
199
|
+
evaluationState: instance.evaluationState ?? undefined,
|
|
200
|
+
terminalIds: instance.terminals ? instance.terminals.map(terminal => terminal.id) : undefined,
|
|
201
|
+
pageIds: instance.pages ? instance.pages.map(page => page.id) : undefined,
|
|
202
|
+
customStatuses: instance.customStatuses ?? undefined,
|
|
183
203
|
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export class InstanceStateService {
|
|
207
|
+
constructor(
|
|
208
|
+
private readonly database: DatabaseManager,
|
|
209
|
+
private readonly pubsubManager: PubSubManager,
|
|
210
|
+
private readonly runnerBackend: RunnerBackend,
|
|
211
|
+
private readonly workerService: WorkerService,
|
|
212
|
+
private readonly artifactService: ArtifactService,
|
|
213
|
+
private readonly unitExtraService: UnitExtraService,
|
|
214
|
+
private readonly secretService: SecretService,
|
|
215
|
+
private readonly logger: Logger,
|
|
216
|
+
) {}
|
|
184
217
|
|
|
185
218
|
/**
|
|
186
|
-
*
|
|
219
|
+
* Gets the aggregates of all instances in the project.
|
|
187
220
|
*
|
|
188
|
-
* @param projectId The ID of the project
|
|
189
|
-
* @param
|
|
190
|
-
* @param patch The patch to apply to the instance state.
|
|
221
|
+
* @param projectId The ID of the project for which to retrieve instances.
|
|
222
|
+
* @param options Options to customize the retrieval of instances.
|
|
191
223
|
*/
|
|
192
|
-
async
|
|
224
|
+
async getInstanceStates(
|
|
193
225
|
projectId: string,
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
return await this.lockManager.acquire(["instance-state", projectId, patch.id], async () => {
|
|
198
|
-
const state = await this.hotStateManager.hget(["instance-states", projectId], patch.id)
|
|
199
|
-
|
|
200
|
-
const patchedState = await this.applyStatePatch(
|
|
201
|
-
projectId,
|
|
202
|
-
state,
|
|
203
|
-
patch,
|
|
204
|
-
operation.type !== "preview",
|
|
205
|
-
)
|
|
206
|
-
|
|
207
|
-
this.logger.debug(
|
|
208
|
-
{ projectId, instanceId: patch.id, patch },
|
|
209
|
-
`instance state updated for instance "%s" in project "%s"`,
|
|
210
|
-
patch.id,
|
|
211
|
-
projectId,
|
|
212
|
-
)
|
|
226
|
+
options: GetProjectInstancesOptions = {},
|
|
227
|
+
): Promise<InstanceState[]> {
|
|
228
|
+
const database = await this.database.forProject(projectId)
|
|
213
229
|
|
|
214
|
-
|
|
230
|
+
// load instance states from the database
|
|
231
|
+
const queryResult = await database.instanceState.findMany({
|
|
232
|
+
include: includeForInstanceState(options),
|
|
215
233
|
})
|
|
234
|
+
|
|
235
|
+
// aggregate the results from the database
|
|
236
|
+
return queryResult.map(mapInstanceStateResult)
|
|
216
237
|
}
|
|
217
238
|
|
|
218
239
|
/**
|
|
219
|
-
*
|
|
240
|
+
* Marks an instance state as deleted and cleans up associated resources.
|
|
241
|
+
*
|
|
242
|
+
* This operation:
|
|
243
|
+
* - only allows deletion of instances with operation state null or "destroyed";
|
|
244
|
+
* - prevents deletion of instances that have active locks;
|
|
245
|
+
* - marks the instance state as "deleted" (never actually removes the record);
|
|
246
|
+
* - handles terminals: deletes data if requested, otherwise marks as "unavailable";
|
|
247
|
+
* - handles secrets: deletes if requested, otherwise ignores them;
|
|
248
|
+
* - deletes artifact references for the instance;
|
|
249
|
+
* - recursively handles child instances using parentId relationship;
|
|
250
|
+
* - performs worker cleanup and synchronization;
|
|
251
|
+
* - performs artifact garbage collection.
|
|
220
252
|
*
|
|
221
|
-
* @param projectId The ID of the project
|
|
222
|
-
* @param instanceId The ID of the instance whose state is to be
|
|
223
|
-
* @param
|
|
224
|
-
* @param secretNamesToRemove The list of secret names to remove from the instance state.
|
|
225
|
-
* @param invalidateState Whether to invalidate the state after updating secret names.
|
|
226
|
-
* @param checkLoaded Whether to check if instance states are loaded in the hot state.
|
|
253
|
+
* @param projectId The ID of the project containing the instance.
|
|
254
|
+
* @param instanceId The ID of the instance whose state is to be marked as deleted.
|
|
255
|
+
* @param options Configuration options for terminal and secret handling.
|
|
227
256
|
*/
|
|
228
|
-
async
|
|
257
|
+
async forgetInstanceState(
|
|
229
258
|
projectId: string,
|
|
230
|
-
instanceId:
|
|
231
|
-
|
|
232
|
-
secretNamesToRemove: string[],
|
|
233
|
-
invalidateState = true,
|
|
234
|
-
checkLoaded = true,
|
|
259
|
+
instanceId: InstanceId,
|
|
260
|
+
{ deleteSecrets = false, clearTerminalData = false }: ForgetInstanceStateOptions = {},
|
|
235
261
|
): Promise<void> {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
262
|
+
const database = await this.database.forProject(projectId)
|
|
263
|
+
const project = await this.database.backend.project.findUnique({
|
|
264
|
+
where: { id: projectId },
|
|
265
|
+
select: forSchema(projectOutputSchema),
|
|
266
|
+
})
|
|
239
267
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
}
|
|
268
|
+
if (!project) {
|
|
269
|
+
throw new ProjectNotFoundError(projectId)
|
|
270
|
+
}
|
|
244
271
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
272
|
+
// collect instances to process cleanup after transaction
|
|
273
|
+
const unitInstancesToCleanup: { id: string; instanceId: InstanceId }[] = []
|
|
274
|
+
const updatedStateIds: string[] = []
|
|
248
275
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}
|
|
276
|
+
await database.$transaction(async tx => {
|
|
277
|
+
const state = await tx.instanceState.findUnique({
|
|
278
|
+
where: { instanceId },
|
|
279
|
+
select: { id: true, kind: true, instanceId: true, lock: { select: { stateId: true } } },
|
|
280
|
+
})
|
|
253
281
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
patch.operationStatus = {
|
|
258
|
-
inputHashNonce: randomBytes(4).readInt32LE(),
|
|
259
|
-
}
|
|
260
|
-
}
|
|
282
|
+
if (!state) {
|
|
283
|
+
throw new InstanceStateNotFoundError(projectId, instanceId)
|
|
284
|
+
}
|
|
261
285
|
|
|
262
|
-
|
|
263
|
-
|
|
286
|
+
if (state.lock) {
|
|
287
|
+
throw new InstanceLockedError(projectId, instanceId)
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
await this.processInstanceDeletion(
|
|
291
|
+
tx,
|
|
292
|
+
projectId,
|
|
293
|
+
state,
|
|
294
|
+
{ deleteSecrets, clearTerminalData },
|
|
295
|
+
unitInstancesToCleanup,
|
|
296
|
+
updatedStateIds,
|
|
297
|
+
)
|
|
264
298
|
})
|
|
299
|
+
|
|
300
|
+
// publish state events for all updated instances
|
|
301
|
+
for (const updatedStateId of updatedStateIds) {
|
|
302
|
+
void this.pubsubManager.publish(["instance-state", projectId], {
|
|
303
|
+
type: "patched",
|
|
304
|
+
stateId: updatedStateId,
|
|
305
|
+
patch: {
|
|
306
|
+
status: "undeployed",
|
|
307
|
+
statusFields: null,
|
|
308
|
+
inputHash: null,
|
|
309
|
+
outputHash: null,
|
|
310
|
+
dependencyOutputHash: null,
|
|
311
|
+
message: null,
|
|
312
|
+
currentResourceCount: null,
|
|
313
|
+
model: null,
|
|
314
|
+
resolvedInputs: null,
|
|
315
|
+
secretNames: deleteSecrets ? [] : undefined,
|
|
316
|
+
terminalIds: clearTerminalData ? [] : undefined,
|
|
317
|
+
pageIds: [],
|
|
318
|
+
customStatuses: [],
|
|
319
|
+
triggerIds: [],
|
|
320
|
+
evaluationState: null,
|
|
321
|
+
},
|
|
322
|
+
})
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// process side effects
|
|
326
|
+
try {
|
|
327
|
+
await waitAll([
|
|
328
|
+
this.workerService.cleanupWorkerUsageAndSync(projectId),
|
|
329
|
+
this.artifactService.collectGarbage(projectId),
|
|
330
|
+
...unitInstancesToCleanup.map(async ({ id, instanceId }) => {
|
|
331
|
+
const [instanceType, instanceName] = parseInstanceId(instanceId)
|
|
332
|
+
|
|
333
|
+
await this.runnerBackend.deleteState({
|
|
334
|
+
projectId: project.id,
|
|
335
|
+
stateId: id,
|
|
336
|
+
libraryId: project.libraryId,
|
|
337
|
+
instanceName,
|
|
338
|
+
instanceType,
|
|
339
|
+
})
|
|
340
|
+
}),
|
|
341
|
+
])
|
|
342
|
+
} catch (error) {
|
|
343
|
+
this.logger.warn(
|
|
344
|
+
{ error, projectId, instanceId },
|
|
345
|
+
"failed to perform side effects after forgetting instance state",
|
|
346
|
+
)
|
|
347
|
+
}
|
|
265
348
|
}
|
|
266
349
|
|
|
267
350
|
/**
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
* @param projectId The ID of the project from which to delete the instance state.
|
|
271
|
-
* @param instanceId The ID of the instance whose state is to be deleted.
|
|
351
|
+
* Processes the deletion of an instance within a transaction.
|
|
352
|
+
* Handles validation, deletion logic, and recursive child deletion.
|
|
272
353
|
*/
|
|
273
|
-
async
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
354
|
+
private async processInstanceDeletion(
|
|
355
|
+
tx: ProjectTransaction,
|
|
356
|
+
projectId: string,
|
|
357
|
+
state: { id: string; kind: ComponentKind; instanceId: InstanceId },
|
|
358
|
+
options: ForgetInstanceStateOptions,
|
|
359
|
+
unitInstancesToCleanup: { id: string; instanceId: InstanceId }[],
|
|
360
|
+
updatedStateIds: string[],
|
|
361
|
+
): Promise<void> {
|
|
362
|
+
const { deleteSecrets = false, clearTerminalData = false } = options
|
|
363
|
+
|
|
364
|
+
// always mark instance state as undeployed (never actually delete the record)
|
|
365
|
+
await tx.instanceState.update({
|
|
366
|
+
where: { id: state.id },
|
|
367
|
+
data: {
|
|
368
|
+
status: "undeployed",
|
|
369
|
+
statusFields: DbNull,
|
|
370
|
+
inputHash: null,
|
|
371
|
+
outputHash: null,
|
|
372
|
+
dependencyOutputHash: null,
|
|
373
|
+
message: null,
|
|
374
|
+
currentResourceCount: null,
|
|
375
|
+
model: DbNull,
|
|
376
|
+
resolvedInputs: DbNull,
|
|
377
|
+
},
|
|
378
|
+
})
|
|
279
379
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
380
|
+
// handle terminals
|
|
381
|
+
if (clearTerminalData) {
|
|
382
|
+
await tx.terminal.deleteMany({ where: { stateId: state.id } })
|
|
383
|
+
} else {
|
|
384
|
+
await tx.terminal.updateMany({
|
|
385
|
+
where: { stateId: state.id },
|
|
386
|
+
data: { status: "unavailable" },
|
|
387
|
+
})
|
|
388
|
+
}
|
|
283
389
|
|
|
284
|
-
|
|
285
|
-
|
|
390
|
+
// handle secrets
|
|
391
|
+
if (deleteSecrets) {
|
|
392
|
+
await tx.secret.deleteMany({
|
|
393
|
+
where: { stateId: state.id },
|
|
394
|
+
})
|
|
395
|
+
}
|
|
286
396
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
397
|
+
// delete custom statuses for this instance
|
|
398
|
+
await tx.instanceCustomStatus.deleteMany({
|
|
399
|
+
where: { stateId: state.id },
|
|
400
|
+
})
|
|
290
401
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
402
|
+
// delete other related resources
|
|
403
|
+
await tx.page.deleteMany({ where: { stateId: state.id } })
|
|
404
|
+
await tx.trigger.deleteMany({ where: { stateId: state.id } })
|
|
405
|
+
|
|
406
|
+
// remove artifact references for this instance
|
|
407
|
+
await tx.instanceState.update({
|
|
408
|
+
where: { id: state.id },
|
|
409
|
+
data: {
|
|
410
|
+
artifacts: {
|
|
411
|
+
set: [],
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
})
|
|
294
415
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
416
|
+
// collect unit instances for Pulumi cleanup (to be done outside transaction)
|
|
417
|
+
if (state.kind === "unit") {
|
|
418
|
+
unitInstancesToCleanup.push({ id: state.id, instanceId: state.instanceId })
|
|
419
|
+
}
|
|
299
420
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
batch.write(),
|
|
421
|
+
// track this instance as updated
|
|
422
|
+
updatedStateIds.push(state.id)
|
|
303
423
|
|
|
304
|
-
|
|
305
|
-
this.hotStateManager.hdel(["instance-states", projectId], instanceId),
|
|
424
|
+
this.logger.info({ projectId }, `marked state "%s" as deleted`, state.id)
|
|
306
425
|
|
|
307
|
-
|
|
308
|
-
|
|
426
|
+
// recursively handle child instances using parentId
|
|
427
|
+
if (state.kind === "composite") {
|
|
428
|
+
const childStates = await tx.instanceState.findMany({
|
|
429
|
+
where: {
|
|
430
|
+
parentId: state.id,
|
|
431
|
+
status: { not: "undeployed" }, // don't process undeployed children
|
|
432
|
+
},
|
|
433
|
+
select: { id: true, kind: true, instanceId: true },
|
|
434
|
+
})
|
|
309
435
|
|
|
310
|
-
|
|
311
|
-
|
|
436
|
+
// recursively delete child states (within the same transaction)
|
|
437
|
+
for (const child of childStates) {
|
|
438
|
+
await this.processInstanceDeletion(
|
|
439
|
+
tx,
|
|
312
440
|
projectId,
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
})
|
|
441
|
+
child,
|
|
442
|
+
options,
|
|
443
|
+
unitInstancesToCleanup,
|
|
444
|
+
updatedStateIds,
|
|
445
|
+
)
|
|
446
|
+
}
|
|
447
|
+
}
|
|
321
448
|
}
|
|
322
449
|
|
|
450
|
+
/**
|
|
451
|
+
* Replaces or adds a custom status for an instance in a project.
|
|
452
|
+
*
|
|
453
|
+
* @param projectId The ID of the project containing the instance.
|
|
454
|
+
* @param serviceAccoundtId The ID of the service account owning the instance.
|
|
455
|
+
* @param stateId The ID of the instance state to update.
|
|
456
|
+
* @param status The custom status to replace or add.
|
|
457
|
+
*/
|
|
323
458
|
async updateCustomStatus(
|
|
324
459
|
projectId: string,
|
|
325
|
-
|
|
326
|
-
|
|
460
|
+
stateId: string,
|
|
461
|
+
serviceAccountId: string,
|
|
462
|
+
status: InstanceCustomStatusInput,
|
|
327
463
|
): Promise<void> {
|
|
328
|
-
await this.
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
464
|
+
const database = await this.database.forProject(projectId)
|
|
465
|
+
|
|
466
|
+
const customStatuses = await database.$transaction(async tx => {
|
|
467
|
+
await tx.instanceCustomStatus.upsert({
|
|
468
|
+
where: {
|
|
469
|
+
stateId_serviceAccountId_name: {
|
|
470
|
+
stateId,
|
|
471
|
+
serviceAccountId,
|
|
472
|
+
name: status.name,
|
|
473
|
+
},
|
|
474
|
+
},
|
|
475
|
+
create: {
|
|
476
|
+
stateId: stateId,
|
|
477
|
+
serviceAccountId,
|
|
478
|
+
name: status.name,
|
|
479
|
+
meta: status.meta,
|
|
480
|
+
value: status.value,
|
|
481
|
+
message: status.message ?? null,
|
|
482
|
+
order: status.order ?? 50,
|
|
483
|
+
},
|
|
484
|
+
update: {
|
|
485
|
+
meta: status.meta,
|
|
486
|
+
value: status.value,
|
|
487
|
+
message: status.message ?? null,
|
|
488
|
+
order: status.order ?? 50,
|
|
489
|
+
},
|
|
490
|
+
})
|
|
335
491
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
state.extra.customStatuses ??= []
|
|
342
|
-
state.extra.customStatuses?.push(status)
|
|
343
|
-
}
|
|
492
|
+
return await tx.instanceCustomStatus.findMany({
|
|
493
|
+
where: { stateId, serviceAccountId },
|
|
494
|
+
orderBy: [{ order: "asc" }, { createdAt: "asc" }],
|
|
495
|
+
})
|
|
496
|
+
})
|
|
344
497
|
|
|
345
|
-
|
|
498
|
+
void this.pubsubManager.publish(["instance-state", projectId], {
|
|
499
|
+
type: "patched",
|
|
500
|
+
stateId,
|
|
501
|
+
patch: { customStatuses },
|
|
346
502
|
})
|
|
347
503
|
}
|
|
348
504
|
|
|
349
|
-
|
|
505
|
+
/**
|
|
506
|
+
* Removes a custom status from an instance in a project.
|
|
507
|
+
*
|
|
508
|
+
* @param projectId The ID of the project containing the instance.
|
|
509
|
+
* @param stateId The ID of the instance state to update.
|
|
510
|
+
* @param serviceAccountId The ID of the service account owning the instance.
|
|
511
|
+
* @param statusName The name of the custom status to remove.
|
|
512
|
+
*/
|
|
513
|
+
async removeCustomStatus(
|
|
350
514
|
projectId: string,
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
515
|
+
stateId: string,
|
|
516
|
+
serviceAccountId: string,
|
|
517
|
+
statusName: string,
|
|
518
|
+
): Promise<void> {
|
|
519
|
+
const database = await this.database.forProject(projectId)
|
|
520
|
+
|
|
521
|
+
const customStatuses = await database.$transaction(async tx => {
|
|
522
|
+
await tx.instanceCustomStatus.deleteMany({
|
|
523
|
+
where: {
|
|
524
|
+
stateId,
|
|
525
|
+
serviceAccountId,
|
|
526
|
+
name: statusName,
|
|
527
|
+
},
|
|
528
|
+
})
|
|
363
529
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
}
|
|
530
|
+
return await tx.instanceCustomStatus.findMany({
|
|
531
|
+
where: { stateId, serviceAccountId },
|
|
532
|
+
orderBy: [{ order: "asc" }, { createdAt: "asc" }],
|
|
533
|
+
})
|
|
534
|
+
})
|
|
368
535
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
)
|
|
536
|
+
void this.pubsubManager.publish(["instance-state", projectId], {
|
|
537
|
+
type: "patched",
|
|
538
|
+
stateId,
|
|
539
|
+
patch: { customStatuses },
|
|
540
|
+
})
|
|
541
|
+
}
|
|
376
542
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
543
|
+
/**
|
|
544
|
+
* Creates the provided operation states.
|
|
545
|
+
* Also updates the instance state if provided.
|
|
546
|
+
*
|
|
547
|
+
* @param projectId The ID of the project containing the instances.
|
|
548
|
+
* @param operationStates The tuples of operation state data to create and instance state patch to apply.
|
|
549
|
+
*/
|
|
550
|
+
async createOperationStates(
|
|
551
|
+
projectId: string,
|
|
552
|
+
operationStates: [
|
|
553
|
+
opState: InstanceOperationStateCreateManyInput,
|
|
554
|
+
instanceState: InstanceStatePatch,
|
|
555
|
+
][],
|
|
556
|
+
): Promise<Partial<InstanceState>[]> {
|
|
557
|
+
const database = await this.database.forProject(projectId)
|
|
558
|
+
|
|
559
|
+
const patches = await database.$transaction(async tx => {
|
|
560
|
+
return await Promise.all(
|
|
561
|
+
operationStates.map(async ([opState, instanceState]) => {
|
|
562
|
+
const operationState = await tx.instanceOperationState.create({
|
|
563
|
+
data: opState,
|
|
564
|
+
})
|
|
565
|
+
|
|
566
|
+
const state = await tx.instanceState.update({
|
|
567
|
+
where: { id: opState.stateId },
|
|
568
|
+
data: instanceState as InstanceStateUpdateInput,
|
|
569
|
+
})
|
|
570
|
+
|
|
571
|
+
return { ...state, lastOperationState: operationState }
|
|
572
|
+
}),
|
|
381
573
|
)
|
|
574
|
+
})
|
|
382
575
|
|
|
383
|
-
|
|
384
|
-
|
|
576
|
+
// publish patches after transaction
|
|
577
|
+
for (const patch of patches) {
|
|
578
|
+
void this.pubsubManager.publish(["instance-state", projectId], {
|
|
579
|
+
type: "patched",
|
|
580
|
+
stateId: patch.id,
|
|
581
|
+
patch,
|
|
582
|
+
})
|
|
583
|
+
}
|
|
385
584
|
|
|
386
|
-
|
|
387
|
-
})
|
|
585
|
+
return patches
|
|
388
586
|
}
|
|
389
587
|
|
|
390
|
-
|
|
588
|
+
/**
|
|
589
|
+
* Updates the operation state and instance state for a specific instance.
|
|
590
|
+
*
|
|
591
|
+
* @param projectId The ID of the project containing the instance.
|
|
592
|
+
* @param stateId The ID of the instance state to update.
|
|
593
|
+
* @param operationId The ID of the operation (required if operationState is provided).
|
|
594
|
+
* @param options Update options containing operation state, instance state, and unit-specific data.
|
|
595
|
+
* @return The instance state patch containing the updated operation state and instance state fields.
|
|
596
|
+
*/
|
|
597
|
+
async updateOperationState(
|
|
391
598
|
projectId: string,
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
): Promise<InstanceState
|
|
396
|
-
const
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
type: "deleted",
|
|
413
|
-
instanceId: patchedState.id,
|
|
414
|
-
}),
|
|
415
|
-
)
|
|
599
|
+
stateId: string,
|
|
600
|
+
operationId: string,
|
|
601
|
+
options: UpdateOperationStateOptions,
|
|
602
|
+
): Promise<Partial<InstanceState>> {
|
|
603
|
+
const { operationState, instanceState, unitExtra } = options
|
|
604
|
+
const database = await this.database.forProject(projectId)
|
|
605
|
+
|
|
606
|
+
const result = await database.$transaction(async tx => {
|
|
607
|
+
let unitExtraData = null
|
|
608
|
+
|
|
609
|
+
// update operation state
|
|
610
|
+
const updatedOperationState = await tx.instanceOperationState.update({
|
|
611
|
+
where: {
|
|
612
|
+
operationId_stateId: {
|
|
613
|
+
operationId,
|
|
614
|
+
stateId,
|
|
615
|
+
},
|
|
616
|
+
},
|
|
617
|
+
data: operationState,
|
|
618
|
+
})
|
|
416
619
|
|
|
417
|
-
|
|
418
|
-
|
|
620
|
+
const project = await this.database.backend.project.findUnique({
|
|
621
|
+
where: { id: projectId },
|
|
622
|
+
select: { libraryId: true },
|
|
623
|
+
})
|
|
624
|
+
|
|
625
|
+
if (!project) {
|
|
626
|
+
throw new ProjectNotFoundError(projectId)
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// update unit-specific data if provided
|
|
630
|
+
if (unitExtra) {
|
|
631
|
+
const [pageIds, terminalIds, triggerIds, secretNames] = await Promise.all([
|
|
632
|
+
this.unitExtraService.processUnitPages(tx, stateId, unitExtra.pages),
|
|
633
|
+
this.unitExtraService.processUnitTerminals(tx, stateId, unitExtra.terminals),
|
|
634
|
+
this.unitExtraService.processUnitTriggers(tx, stateId, unitExtra.triggers),
|
|
635
|
+
this.secretService.updateInstanceSecretsCore(
|
|
636
|
+
tx,
|
|
637
|
+
project.libraryId,
|
|
638
|
+
stateId,
|
|
639
|
+
unitExtra.secrets,
|
|
640
|
+
),
|
|
641
|
+
this.workerService.updateUnitRegistrations(tx, projectId, stateId, unitExtra.workers),
|
|
642
|
+
])
|
|
643
|
+
|
|
644
|
+
unitExtraData = { pageIds, terminalIds, triggerIds, secretNames }
|
|
419
645
|
}
|
|
420
|
-
} else {
|
|
421
|
-
promises.push(
|
|
422
|
-
this.pubsubManager.publish(["instance-state", projectId], { type: "patched", patch }),
|
|
423
|
-
)
|
|
424
646
|
|
|
425
|
-
if
|
|
426
|
-
|
|
647
|
+
// update instance state if provided
|
|
648
|
+
if (instanceState) {
|
|
649
|
+
await tx.instanceState.update({
|
|
650
|
+
where: { id: stateId },
|
|
651
|
+
data: instanceState as InstanceStateUpdateInput,
|
|
652
|
+
})
|
|
427
653
|
}
|
|
654
|
+
|
|
655
|
+
return { updatedOperationState, unitExtraData }
|
|
656
|
+
})
|
|
657
|
+
|
|
658
|
+
// build patch combining operation state, instance state, and unit extra data
|
|
659
|
+
const patch: Partial<InstanceState> = {
|
|
660
|
+
...instanceState,
|
|
661
|
+
...result.unitExtraData,
|
|
662
|
+
lastOperationState: result.updatedOperationState,
|
|
428
663
|
}
|
|
429
664
|
|
|
430
|
-
|
|
665
|
+
// emit the patch after transaction
|
|
666
|
+
void this.pubsubManager.publish(["instance-state", projectId], {
|
|
667
|
+
type: "patched",
|
|
668
|
+
stateId,
|
|
669
|
+
patch,
|
|
670
|
+
})
|
|
671
|
+
|
|
672
|
+
this.logger.debug(
|
|
673
|
+
{ projectId, stateId, operationId, options },
|
|
674
|
+
"updated operation state for instance",
|
|
675
|
+
)
|
|
431
676
|
|
|
432
|
-
return
|
|
677
|
+
return patch
|
|
433
678
|
}
|
|
434
679
|
}
|