@quadnix/octo 0.0.30 → 0.2.0
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/LICENSE +661 -0
- package/README.md +117 -8
- package/dist/app.type.d.ts +277 -19
- package/dist/app.type.js +80 -9
- package/dist/app.type.js.map +1 -1
- package/dist/decorators/action.decorator.d.ts +10 -6
- package/dist/decorators/action.decorator.js +19 -6
- package/dist/decorators/action.decorator.js.map +1 -1
- package/dist/decorators/anchor.decorator.d.ts +12 -5
- package/dist/decorators/anchor.decorator.js +12 -5
- package/dist/decorators/anchor.decorator.js.map +1 -1
- package/dist/decorators/enable-hook.decorator.d.ts +1 -1
- package/dist/decorators/enable-hook.decorator.js +1 -1
- package/dist/decorators/factory.decorator.d.ts +18 -34
- package/dist/decorators/factory.decorator.js +18 -34
- package/dist/decorators/factory.decorator.js.map +1 -1
- package/dist/decorators/model.decorator.d.ts +4 -6
- package/dist/decorators/model.decorator.js +2 -7
- package/dist/decorators/model.decorator.js.map +1 -1
- package/dist/decorators/module.decorator.d.ts +16 -10
- package/dist/decorators/module.decorator.js +16 -10
- package/dist/decorators/module.decorator.js.map +1 -1
- package/dist/decorators/on-event.decorator.d.ts +7 -2
- package/dist/decorators/on-event.decorator.js +5 -0
- package/dist/decorators/on-event.decorator.js.map +1 -1
- package/dist/decorators/resource.decorator.d.ts +50 -1
- package/dist/decorators/resource.decorator.js +38 -1
- package/dist/decorators/resource.decorator.js.map +1 -1
- package/dist/decorators/validate.decorator.d.ts +15 -1
- package/dist/decorators/validate.decorator.js +15 -1
- package/dist/decorators/validate.decorator.js.map +1 -1
- package/dist/errors/container.error.d.ts +18 -0
- package/dist/errors/container.error.js +18 -0
- package/dist/errors/container.error.js.map +1 -1
- package/dist/errors/index.d.ts +1 -0
- package/dist/errors/index.js +1 -0
- package/dist/errors/index.js.map +1 -1
- package/dist/errors/module.error.d.ts +6 -0
- package/dist/errors/module.error.js +6 -0
- package/dist/errors/module.error.js.map +1 -1
- package/dist/errors/node.error.d.ts +56 -0
- package/dist/errors/node.error.js +56 -0
- package/dist/errors/node.error.js.map +1 -1
- package/dist/errors/schema.error.d.ts +14 -1
- package/dist/errors/schema.error.js +11 -1
- package/dist/errors/schema.error.js.map +1 -1
- package/dist/errors/terraform.error.d.ts +20 -0
- package/dist/errors/terraform.error.js +24 -0
- package/dist/errors/terraform.error.js.map +1 -0
- package/dist/errors/transaction.error.d.ts +93 -2
- package/dist/errors/transaction.error.js +83 -0
- package/dist/errors/transaction.error.js.map +1 -1
- package/dist/events/action.event.d.ts +8 -4
- package/dist/events/action.event.js +2 -4
- package/dist/events/action.event.js.map +1 -1
- package/dist/events/command.event.d.ts +43 -0
- package/dist/events/command.event.js +33 -0
- package/dist/events/command.event.js.map +1 -0
- package/dist/events/event.model.d.ts +26 -8
- package/dist/events/event.model.js +15 -7
- package/dist/events/event.model.js.map +1 -1
- package/dist/events/hook/commit.hook.event.d.ts +1 -7
- package/dist/events/hook/commit.hook.event.js +0 -6
- package/dist/events/hook/commit.hook.event.js.map +1 -1
- package/dist/events/hook/hook.event.d.ts +1 -3
- package/dist/events/hook/hook.event.js +0 -2
- package/dist/events/hook/hook.event.js.map +1 -1
- package/dist/events/hook/model.hook.event.d.ts +1 -7
- package/dist/events/hook/model.hook.event.js +0 -6
- package/dist/events/hook/model.hook.event.js.map +1 -1
- package/dist/events/hook/resource.hook.event.d.ts +1 -7
- package/dist/events/hook/resource.hook.event.js +0 -6
- package/dist/events/hook/resource.hook.event.js.map +1 -1
- package/dist/events/index.d.ts +5 -0
- package/dist/events/index.js +5 -0
- package/dist/events/index.js.map +1 -1
- package/dist/events/mode-result.event.d.ts +57 -0
- package/dist/events/mode-result.event.js +24 -0
- package/dist/events/mode-result.event.js.map +1 -0
- package/dist/events/mode.event.d.ts +61 -0
- package/dist/events/mode.event.js +32 -0
- package/dist/events/mode.event.js.map +1 -0
- package/dist/events/module.event.d.ts +7 -5
- package/dist/events/module.event.js +3 -4
- package/dist/events/module.event.js.map +1 -1
- package/dist/events/registration.event.d.ts +19 -15
- package/dist/events/registration.event.js +18 -14
- package/dist/events/registration.event.js.map +1 -1
- package/dist/events/serialization.event.d.ts +16 -13
- package/dist/events/serialization.event.js +15 -12
- package/dist/events/serialization.event.js.map +1 -1
- package/dist/events/state.event.d.ts +102 -0
- package/dist/events/state.event.js +69 -0
- package/dist/events/state.event.js.map +1 -0
- package/dist/events/terraform.event.d.ts +78 -0
- package/dist/events/terraform.event.js +55 -0
- package/dist/events/terraform.event.js.map +1 -0
- package/dist/events/transaction.event.d.ts +3 -40
- package/dist/events/transaction.event.js +1 -29
- package/dist/events/transaction.event.js.map +1 -1
- package/dist/functions/container/container.d.ts +54 -13
- package/dist/functions/container/container.js +64 -19
- package/dist/functions/container/container.js.map +1 -1
- package/dist/functions/container/test-container.d.ts +43 -29
- package/dist/functions/container/test-container.js +71 -43
- package/dist/functions/container/test-container.js.map +1 -1
- package/dist/functions/dependency/dependency.d.ts +84 -23
- package/dist/functions/dependency/dependency.js +84 -14
- package/dist/functions/dependency/dependency.js.map +1 -1
- package/dist/functions/diff/aggregate-resource-actions.d.ts +19 -0
- package/dist/functions/diff/aggregate-resource-actions.js +41 -0
- package/dist/functions/diff/aggregate-resource-actions.js.map +1 -0
- package/dist/functions/diff/diff-metadata.d.ts +21 -3
- package/dist/functions/diff/diff-metadata.js +18 -0
- package/dist/functions/diff/diff-metadata.js.map +1 -1
- package/dist/functions/diff/diff.d.ts +68 -7
- package/dist/functions/diff/diff.js +44 -3
- package/dist/functions/diff/diff.js.map +1 -1
- package/dist/functions/hook/commit.hook.d.ts +16 -1
- package/dist/functions/hook/commit.hook.js +21 -6
- package/dist/functions/hook/commit.hook.js.map +1 -1
- package/dist/functions/hook/model-action.hook.d.ts +16 -1
- package/dist/functions/hook/model-action.hook.js +21 -6
- package/dist/functions/hook/model-action.hook.js.map +1 -1
- package/dist/functions/hook/resource-action.hook.d.ts +16 -1
- package/dist/functions/hook/resource-action.hook.js +21 -6
- package/dist/functions/hook/resource-action.hook.js.map +1 -1
- package/dist/functions/node/node-action.abstract.d.ts +23 -2
- package/dist/functions/node/node-action.abstract.js +22 -8
- package/dist/functions/node/node-action.abstract.js.map +1 -1
- package/dist/functions/node/node.abstract.d.ts +56 -5
- package/dist/functions/node/node.abstract.js +106 -22
- package/dist/functions/node/node.abstract.js.map +1 -1
- package/dist/functions/node/node.interface.d.ts +3 -1
- package/dist/functions/schema/schema.d.ts +7 -0
- package/dist/functions/schema/schema.js +10 -1
- package/dist/functions/schema/schema.js.map +1 -1
- package/dist/index.d.ts +12 -6
- package/dist/index.js +9 -4
- package/dist/index.js.map +1 -1
- package/dist/index.type.d.ts +5 -0
- package/dist/index.type.js +2 -0
- package/dist/index.type.js.map +1 -0
- package/dist/main.d.ts +245 -14
- package/dist/main.js +377 -72
- package/dist/main.js.map +1 -1
- package/dist/models/account/account.model.d.ts +43 -7
- package/dist/models/account/account.model.js +56 -8
- package/dist/models/account/account.model.js.map +1 -1
- package/dist/models/account/account.schema.d.ts +13 -1
- package/dist/models/account/account.schema.js +12 -0
- package/dist/models/account/account.schema.js.map +1 -1
- package/dist/models/app/app.model.d.ts +13 -8
- package/dist/models/app/app.model.js +17 -32
- package/dist/models/app/app.model.js.map +1 -1
- package/dist/models/app/app.schema.d.ts +6 -0
- package/dist/models/app/app.schema.js +6 -0
- package/dist/models/app/app.schema.js.map +1 -1
- package/dist/models/deployment/deployment.model.d.ts +13 -1
- package/dist/models/deployment/deployment.model.js +16 -4
- package/dist/models/deployment/deployment.model.js.map +1 -1
- package/dist/models/deployment/deployment.schema.d.ts +6 -0
- package/dist/models/deployment/deployment.schema.js +6 -0
- package/dist/models/deployment/deployment.schema.js.map +1 -1
- package/dist/models/environment/environment.model.d.ts +15 -3
- package/dist/models/environment/environment.model.js +22 -10
- package/dist/models/environment/environment.model.js.map +1 -1
- package/dist/models/environment/environment.schema.d.ts +2 -0
- package/dist/models/environment/environment.schema.js +2 -0
- package/dist/models/environment/environment.schema.js.map +1 -1
- package/dist/models/execution/execution.model.d.ts +21 -3
- package/dist/models/execution/execution.model.js +47 -29
- package/dist/models/execution/execution.model.js.map +1 -1
- package/dist/models/execution/execution.schema.d.ts +11 -0
- package/dist/models/execution/execution.schema.js +11 -0
- package/dist/models/execution/execution.schema.js.map +1 -1
- package/dist/models/filesystem/filesystem.model.d.ts +27 -1
- package/dist/models/filesystem/filesystem.model.js +30 -4
- package/dist/models/filesystem/filesystem.model.js.map +1 -1
- package/dist/models/filesystem/filesystem.schema.d.ts +5 -0
- package/dist/models/filesystem/filesystem.schema.js +5 -0
- package/dist/models/filesystem/filesystem.schema.js.map +1 -1
- package/dist/models/image/image.model.d.ts +19 -5
- package/dist/models/image/image.model.js +25 -11
- package/dist/models/image/image.model.js.map +1 -1
- package/dist/models/image/image.schema.d.ts +8 -0
- package/dist/models/image/image.schema.js +8 -0
- package/dist/models/image/image.schema.js.map +1 -1
- package/dist/models/model-action.interface.d.ts +17 -4
- package/dist/models/model.abstract.d.ts +96 -1
- package/dist/models/model.abstract.js +107 -8
- package/dist/models/model.abstract.js.map +1 -1
- package/dist/models/model.interface.d.ts +3 -0
- package/dist/models/pipeline/pipeline.model.d.ts +14 -2
- package/dist/models/pipeline/pipeline.model.js +19 -7
- package/dist/models/pipeline/pipeline.model.js.map +1 -1
- package/dist/models/pipeline/pipeline.schema.d.ts +8 -0
- package/dist/models/pipeline/pipeline.schema.js +8 -0
- package/dist/models/pipeline/pipeline.schema.js.map +1 -1
- package/dist/models/region/region.model.d.ts +16 -1
- package/dist/models/region/region.model.js +19 -4
- package/dist/models/region/region.model.js.map +1 -1
- package/dist/models/region/region.schema.d.ts +6 -0
- package/dist/models/region/region.schema.js +6 -0
- package/dist/models/region/region.schema.js.map +1 -1
- package/dist/models/server/server.model.d.ts +15 -3
- package/dist/models/server/server.model.js +18 -6
- package/dist/models/server/server.model.js.map +1 -1
- package/dist/models/server/server.schema.d.ts +3 -1
- package/dist/models/server/server.schema.js +3 -1
- package/dist/models/server/server.schema.js.map +1 -1
- package/dist/models/service/service.model.d.ts +37 -6
- package/dist/models/service/service.model.js +65 -11
- package/dist/models/service/service.model.js.map +1 -1
- package/dist/models/service/service.schema.d.ts +48 -1
- package/dist/models/service/service.schema.js +56 -1
- package/dist/models/service/service.schema.js.map +1 -1
- package/dist/models/subnet/subnet.model.d.ts +27 -3
- package/dist/models/subnet/subnet.model.js +40 -16
- package/dist/models/subnet/subnet.model.js.map +1 -1
- package/dist/models/subnet/subnet.schema.d.ts +9 -0
- package/dist/models/subnet/subnet.schema.js +9 -0
- package/dist/models/subnet/subnet.schema.js.map +1 -1
- package/dist/modes/apply-transaction.d.ts +23 -0
- package/dist/modes/apply-transaction.js +25 -0
- package/dist/modes/apply-transaction.js.map +1 -0
- package/dist/modes/commit.mode.d.ts +54 -0
- package/dist/modes/commit.mode.js +235 -0
- package/dist/modes/commit.mode.js.map +1 -0
- package/dist/modes/generate.mode.d.ts +30 -0
- package/dist/modes/generate.mode.js +50 -0
- package/dist/modes/generate.mode.js.map +1 -0
- package/dist/modes/run-action.mode.d.ts +30 -0
- package/dist/modes/run-action.mode.js +140 -0
- package/dist/modes/run-action.mode.js.map +1 -0
- package/dist/modes/validate.mode.d.ts +186 -0
- package/dist/modes/validate.mode.js +558 -0
- package/dist/modes/validate.mode.js.map +1 -0
- package/dist/modules/module.abstract.d.ts +51 -0
- package/dist/modules/module.abstract.js +36 -0
- package/dist/modules/module.abstract.js.map +1 -1
- package/dist/modules/module.container.d.ts +43 -1
- package/dist/modules/module.container.js +77 -5
- package/dist/modules/module.container.js.map +1 -1
- package/dist/modules/test-module.container.d.ts +139 -25
- package/dist/modules/test-module.container.js +383 -199
- package/dist/modules/test-module.container.js.map +1 -1
- package/dist/overlays/anchor.abstract.d.ts +32 -1
- package/dist/overlays/anchor.abstract.js +42 -11
- package/dist/overlays/anchor.abstract.js.map +1 -1
- package/dist/overlays/anchor.schema.d.ts +21 -0
- package/dist/overlays/anchor.schema.js +21 -0
- package/dist/overlays/anchor.schema.js.map +1 -1
- package/dist/overlays/overlay-data.repository.d.ts +18 -0
- package/dist/overlays/overlay-data.repository.js +18 -0
- package/dist/overlays/overlay-data.repository.js.map +1 -1
- package/dist/overlays/overlay.abstract.d.ts +56 -1
- package/dist/overlays/overlay.abstract.js +79 -20
- package/dist/overlays/overlay.abstract.js.map +1 -1
- package/dist/overlays/overlay.schema.d.ts +20 -0
- package/dist/overlays/overlay.schema.js +20 -0
- package/dist/overlays/overlay.schema.js.map +1 -1
- package/dist/resources/resource-action.interface.d.ts +29 -3
- package/dist/resources/resource-data.repository.d.ts +63 -1
- package/dist/resources/resource-data.repository.js +94 -4
- package/dist/resources/resource-data.repository.js.map +1 -1
- package/dist/resources/resource.abstract.d.ts +163 -6
- package/dist/resources/resource.abstract.js +223 -59
- package/dist/resources/resource.abstract.js.map +1 -1
- package/dist/resources/resource.interface.d.ts +0 -2
- package/dist/resources/resource.schema.d.ts +39 -1
- package/dist/resources/resource.schema.js +38 -0
- package/dist/resources/resource.schema.js.map +1 -1
- package/dist/resources/terraform-resource.abstract.d.ts +44 -0
- package/dist/resources/terraform-resource.abstract.js +45 -0
- package/dist/resources/terraform-resource.abstract.js.map +1 -0
- package/dist/services/event/event.service.d.ts +32 -5
- package/dist/services/event/event.service.js +58 -16
- package/dist/services/event/event.service.js.map +1 -1
- package/dist/services/input/input.service.d.ts +82 -5
- package/dist/services/input/input.service.js +143 -12
- package/dist/services/input/input.service.js.map +1 -1
- package/dist/services/schema-translation/schema-translation.service.d.ts +9 -0
- package/dist/services/schema-translation/schema-translation.service.js +9 -0
- package/dist/services/schema-translation/schema-translation.service.js.map +1 -1
- package/dist/services/serialization/model/model-serialization.service.d.ts +25 -3
- package/dist/services/serialization/model/model-serialization.service.js +113 -62
- package/dist/services/serialization/model/model-serialization.service.js.map +1 -1
- package/dist/services/serialization/resource/resource-serialization.service.d.ts +36 -6
- package/dist/services/serialization/resource/resource-serialization.service.js +77 -28
- package/dist/services/serialization/resource/resource-serialization.service.js.map +1 -1
- package/dist/services/state-management/local.state-provider.d.ts +67 -4
- package/dist/services/state-management/local.state-provider.js +168 -41
- package/dist/services/state-management/local.state-provider.js.map +1 -1
- package/dist/services/state-management/remote.state-provider.d.ts +216 -0
- package/dist/services/state-management/remote.state-provider.js +645 -0
- package/dist/services/state-management/remote.state-provider.js.map +1 -0
- package/dist/services/state-management/state-management.service.d.ts +195 -17
- package/dist/services/state-management/state-management.service.js +259 -10
- package/dist/services/state-management/state-management.service.js.map +1 -1
- package/dist/services/state-management/state-provider.interface.d.ts +98 -3
- package/dist/services/state-management/state-provider.interface.js +14 -1
- package/dist/services/state-management/state-provider.interface.js.map +1 -1
- package/dist/services/state-management/terraform-state.service.d.ts +99 -0
- package/dist/services/state-management/terraform-state.service.js +197 -0
- package/dist/services/state-management/terraform-state.service.js.map +1 -0
- package/dist/services/state-management/test.state-provider.d.ts +42 -4
- package/dist/services/state-management/test.state-provider.js +77 -25
- package/dist/services/state-management/test.state-provider.js.map +1 -1
- package/dist/services/terraform/terraform.service.d.ts +480 -0
- package/dist/services/terraform/terraform.service.js +1082 -0
- package/dist/services/terraform/terraform.service.js.map +1 -0
- package/dist/services/terraform/terraform.type.d.ts +387 -0
- package/dist/services/terraform/terraform.type.js +579 -0
- package/dist/services/terraform/terraform.type.js.map +1 -0
- package/dist/services/transaction/transaction.service.d.ts +110 -10
- package/dist/services/transaction/transaction.service.js +570 -251
- package/dist/services/transaction/transaction.service.js.map +1 -1
- package/dist/services/validation/validation.service.d.ts +15 -3
- package/dist/services/validation/validation.service.js +12 -0
- package/dist/services/validation/validation.service.js.map +1 -1
- package/dist/utilities/array/array.utility.d.ts +3 -0
- package/dist/utilities/array/array.utility.js +3 -0
- package/dist/utilities/array/array.utility.js.map +1 -1
- package/dist/{functions → utilities}/diff/diff.utility.d.ts +19 -3
- package/dist/{functions → utilities}/diff/diff.utility.js +57 -41
- package/dist/utilities/diff/diff.utility.js.map +1 -0
- package/dist/utilities/file/file.utility.d.ts +16 -2
- package/dist/utilities/file/file.utility.js +23 -61
- package/dist/utilities/file/file.utility.js.map +1 -1
- package/dist/utilities/hcl/hcl.utility.d.ts +128 -0
- package/dist/utilities/hcl/hcl.utility.js +402 -0
- package/dist/utilities/hcl/hcl.utility.js.map +1 -0
- package/dist/utilities/http/http.utility.d.ts +57 -0
- package/dist/utilities/http/http.utility.js +98 -0
- package/dist/utilities/http/http.utility.js.map +1 -0
- package/dist/utilities/node/node.utility.d.ts +3 -0
- package/dist/utilities/node/node.utility.js +3 -0
- package/dist/utilities/node/node.utility.js.map +1 -1
- package/dist/utilities/object/object.utility.d.ts +12 -0
- package/dist/utilities/object/object.utility.js +12 -0
- package/dist/utilities/object/object.utility.js.map +1 -1
- package/dist/utilities/project/project.utility.d.ts +28 -0
- package/dist/utilities/project/project.utility.js +35 -0
- package/dist/utilities/project/project.utility.js.map +1 -0
- package/dist/utilities/string/string.utility.d.ts +41 -0
- package/dist/utilities/string/string.utility.js +65 -0
- package/dist/utilities/string/string.utility.js.map +1 -0
- package/dist/utilities/terraform/terraform.utility.d.ts +276 -0
- package/dist/utilities/terraform/terraform.utility.js +623 -0
- package/dist/utilities/terraform/terraform.utility.js.map +1 -0
- package/dist/utilities/test-helpers/test-classes.d.ts +36 -30
- package/dist/utilities/test-helpers/test-classes.js +38 -39
- package/dist/utilities/test-helpers/test-classes.js.map +1 -1
- package/dist/utilities/test-helpers/test-models.d.ts +8 -1
- package/dist/utilities/test-helpers/test-models.js +37 -12
- package/dist/utilities/test-helpers/test-models.js.map +1 -1
- package/dist/utilities/test-helpers/test-modes.d.ts +315 -0
- package/dist/utilities/test-helpers/test-modes.js +486 -0
- package/dist/utilities/test-helpers/test-modes.js.map +1 -0
- package/dist/utilities/test-helpers/test-modules.d.ts +47 -0
- package/dist/utilities/test-helpers/test-modules.js +85 -0
- package/dist/utilities/test-helpers/test-modules.js.map +1 -0
- package/dist/utilities/test-helpers/test-overlays.d.ts +20 -2
- package/dist/utilities/test-helpers/test-overlays.js +56 -6
- package/dist/utilities/test-helpers/test-overlays.js.map +1 -1
- package/dist/utilities/test-helpers/test-resources.d.ts +23 -3
- package/dist/utilities/test-helpers/test-resources.js +83 -23
- package/dist/utilities/test-helpers/test-resources.js.map +1 -1
- package/dist/utilities/validation/validation.utility.d.ts +24 -0
- package/dist/utilities/validation/validation.utility.js +24 -0
- package/dist/utilities/validation/validation.utility.js.map +1 -1
- package/package.json +12 -6
- package/dist/functions/diff/diff.utility.js.map +0 -1
- package/dist/services/capture/capture.service.d.ts +0 -19
- package/dist/services/capture/capture.service.js +0 -43
- package/dist/services/capture/capture.service.js.map +0 -1
- package/dist/services/state-management/local-encryption.state-provider.d.ts +0 -20
- package/dist/services/state-management/local-encryption.state-provider.js +0 -90
- package/dist/services/state-management/local-encryption.state-provider.js.map +0 -1
- package/dist/utilities/cidr/cidr.utility.d.ts +0 -10
- package/dist/utilities/cidr/cidr.utility.js +0 -51
- package/dist/utilities/cidr/cidr.utility.js.map +0 -1
|
@@ -10,35 +10,303 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
10
10
|
import { NodeType, ValidationType, } from '../../app.type.js';
|
|
11
11
|
import { EventSource } from '../../decorators/event-source.decorator.js';
|
|
12
12
|
import { Factory } from '../../decorators/factory.decorator.js';
|
|
13
|
-
import { InputNotFoundTransactionError,
|
|
14
|
-
import { ModelActionRegistrationEvent, ModelActionTransactionEvent, ModelDiffsTransactionEvent, ModelTransactionTransactionEvent, ResourceActionCompletedTransactionEvent,
|
|
13
|
+
import { InputNotFoundTransactionError, ResourceActionExceptionTransactionError, ResourceActionTimeoutTransactionError, TransactionError, ValidationTransactionError, } from '../../errors/index.js';
|
|
14
|
+
import { ModelActionRegistrationEvent, ModelActionTransactionEvent, ModelDiffsTransactionEvent, ModelTransactionTransactionEvent, ResourceActionCompletedTransactionEvent, ResourceActionInitiatedTransactionEvent, ResourceActionRegistrationEvent, ResourceActionSummaryTransactionEvent, ResourceDiffsTransactionEvent, ResourceTransactionTransactionEvent, } from '../../events/index.js';
|
|
15
15
|
import { Container } from '../../functions/container/container.js';
|
|
16
16
|
import { DiffMetadata } from '../../functions/diff/diff-metadata.js';
|
|
17
17
|
import { DiffAction } from '../../functions/diff/diff.js';
|
|
18
|
-
import {
|
|
18
|
+
import { ANodeAction } from '../../functions/node/node-action.abstract.js';
|
|
19
19
|
import { ModuleContainer } from '../../modules/module.container.js';
|
|
20
20
|
import { OverlayDataRepository } from '../../overlays/overlay-data.repository.js';
|
|
21
21
|
import { AOverlay } from '../../overlays/overlay.abstract.js';
|
|
22
22
|
import { ResourceDataRepository } from '../../resources/resource-data.repository.js';
|
|
23
23
|
import { AResource } from '../../resources/resource.abstract.js';
|
|
24
|
-
import {
|
|
24
|
+
import { ATerraformResource } from '../../resources/terraform-resource.abstract.js';
|
|
25
|
+
import { DiffUtility } from '../../utilities/diff/diff.utility.js';
|
|
25
26
|
import { EventService } from '../event/event.service.js';
|
|
26
27
|
import { InputService } from '../input/input.service.js';
|
|
28
|
+
import { ResourceSerializationService } from '../serialization/resource/resource-serialization.service.js';
|
|
29
|
+
import { TerraformService } from '../terraform/terraform.service.js';
|
|
30
|
+
/**
|
|
31
|
+
* No-op resource action backing terraform resource diffs. Terraform manages the resource's lifecycle;
|
|
32
|
+
* octo only carries the diff for review and validation.
|
|
33
|
+
*
|
|
34
|
+
* @internal
|
|
35
|
+
*/
|
|
36
|
+
class TerraformNoopResourceAction extends ANodeAction {
|
|
37
|
+
/**
|
|
38
|
+
* @internal
|
|
39
|
+
*/
|
|
40
|
+
constructor() {
|
|
41
|
+
super();
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* @internal
|
|
45
|
+
*/
|
|
46
|
+
filter() {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* @internal
|
|
51
|
+
*/
|
|
52
|
+
async handle() { }
|
|
53
|
+
}
|
|
27
54
|
/**
|
|
28
55
|
* @internal
|
|
29
56
|
*/
|
|
30
57
|
export class TransactionService {
|
|
31
|
-
|
|
32
|
-
|
|
58
|
+
/**
|
|
59
|
+
* @internal
|
|
60
|
+
*/
|
|
61
|
+
constructor(eventService, inputService, moduleContainer, overlayDataRepository, resourceDataRepository, resourceSerializationService, terraformService) {
|
|
33
62
|
this.eventService = eventService;
|
|
34
63
|
this.inputService = inputService;
|
|
35
64
|
this.moduleContainer = moduleContainer;
|
|
36
65
|
this.overlayDataRepository = overlayDataRepository;
|
|
37
66
|
this.resourceDataRepository = resourceDataRepository;
|
|
67
|
+
this.resourceSerializationService = resourceSerializationService;
|
|
68
|
+
this.terraformService = terraformService;
|
|
38
69
|
this.modelActions = [];
|
|
39
70
|
this.overlayActions = [];
|
|
40
71
|
this.resourceActions = [];
|
|
41
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Runs the delete action of every **external** resource in `resourceDiffs` that is being deleted.
|
|
75
|
+
*
|
|
76
|
+
* Terraform cannot do this. Octo deletes a resource by removing its block from the configuration,
|
|
77
|
+
* and terraform will not run a `when = destroy` provisioner for a resource that is no longer in
|
|
78
|
+
* the configuration - it drops the `null_resource` from state and moves on. So the callback that
|
|
79
|
+
* would have told an external resource to delete itself never fires, and the real thing it created
|
|
80
|
+
* outlives both state files with nothing tracking it.
|
|
81
|
+
*
|
|
82
|
+
* Octo knows the resource was deleted, which is why this can be driven from its own diff instead.
|
|
83
|
+
* {@link commit} calls it before recording anything, so a failed delete leaves octo state untouched
|
|
84
|
+
* and the deletion is retried on the next run rather than being forgotten.
|
|
85
|
+
*
|
|
86
|
+
* Terraform-managed resources are excluded: terraform destroys those itself, and a resource action
|
|
87
|
+
* on one is rejected everywhere else in octo.
|
|
88
|
+
*
|
|
89
|
+
* @returns The diffs whose actions ran, in the order they ran.
|
|
90
|
+
*/
|
|
91
|
+
async applyExternalResourceDeletes(resourceDiffs) {
|
|
92
|
+
const deleteDiffs = this.getExternalResourceDeletes(resourceDiffs);
|
|
93
|
+
if (deleteDiffs.length === 0) {
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
// Ordered among themselves: `applyOrder` is assigned across the whole resource diff set, and
|
|
97
|
+
// this is a subset of it, so the levels it carries can start anywhere. Re-basing them from zero
|
|
98
|
+
// keeps applyResources' level walk terminating.
|
|
99
|
+
const orders = [...new Set(deleteDiffs.map((d) => d.applyOrder))].sort((a, b) => a - b);
|
|
100
|
+
for (const diff of deleteDiffs) {
|
|
101
|
+
diff.applyOrder = orders.indexOf(diff.applyOrder);
|
|
102
|
+
}
|
|
103
|
+
// Stateless, like run-action: the actual resource graph is not touched here. `commit` syncs
|
|
104
|
+
// actual to new immediately afterwards, which is what records the deletion.
|
|
105
|
+
return this.applyResources(deleteDiffs, { skipActualResourceUpdate: true });
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* @internal
|
|
109
|
+
*/
|
|
110
|
+
async *beginTransaction(diffs, { enableResourceValidation = false, filterResourceDiffsByResourceId = undefined, generateTerraform = false, skipActualResourceUpdate = false, yieldModelDiffs = false, yieldModelTransaction = false, yieldResourceDiffs = false, yieldResourceTransaction = false, } = {}) {
|
|
111
|
+
// Diff overlays and add to existing diffs.
|
|
112
|
+
const overlayDiffs = await this.overlayDataRepository.diff();
|
|
113
|
+
diffs.push(...overlayDiffs);
|
|
114
|
+
// Generate diff on models.
|
|
115
|
+
const modelDiffs = diffs.map((d) => {
|
|
116
|
+
if (d.node.constructor.NODE_TYPE === NodeType.OVERLAY) {
|
|
117
|
+
return new DiffMetadata(d, (this.overlayActions.find((a) => a.overlayClass === d.node.constructor)?.actions || []).filter((a) => a.filter(d)));
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
return new DiffMetadata(d, (this.modelActions.find((a) => a.modelClass === d.node.constructor)?.actions || []).filter((a) => a.filter(d)));
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
// Set apply order on model diffs.
|
|
124
|
+
for (const diff of modelDiffs) {
|
|
125
|
+
this.setApplyOrder(diff, modelDiffs);
|
|
126
|
+
}
|
|
127
|
+
this.eventService.emit(new ModelDiffsTransactionEvent(undefined, [modelDiffs]));
|
|
128
|
+
if (yieldModelDiffs) {
|
|
129
|
+
yield [modelDiffs];
|
|
130
|
+
}
|
|
131
|
+
// Apply model diffs.
|
|
132
|
+
const modelTransaction = await this.applyModels(modelDiffs);
|
|
133
|
+
this.eventService.emit(new ModelTransactionTransactionEvent(undefined, modelTransaction));
|
|
134
|
+
if (yieldModelTransaction) {
|
|
135
|
+
yield modelTransaction;
|
|
136
|
+
}
|
|
137
|
+
// Generate resource diffs.
|
|
138
|
+
let newDiffs = await this.resourceDataRepository.diff();
|
|
139
|
+
let dirtyDiffs = await this.resourceDataRepository.diffDirty();
|
|
140
|
+
// Scope diffs to a single resource, e.g. when terraform invokes octo for one resource action.
|
|
141
|
+
if (filterResourceDiffsByResourceId !== undefined) {
|
|
142
|
+
newDiffs = newDiffs.filter((d) => d.node.resourceId === filterResourceDiffsByResourceId);
|
|
143
|
+
dirtyDiffs = dirtyDiffs.filter((d) => d.node.resourceId === filterResourceDiffsByResourceId);
|
|
144
|
+
// Prefer dirty diffs (actual-graph nodes) over equal new diffs (old-graph nodes) for
|
|
145
|
+
// execution: deserialized old resources are frozen and cannot receive injected inputs.
|
|
146
|
+
for (let i = newDiffs.length - 1; i >= 0; i--) {
|
|
147
|
+
const newDiff = newDiffs[i];
|
|
148
|
+
if (dirtyDiffs.some((d) => d.node.getContext() === newDiff.node.getContext() &&
|
|
149
|
+
d.action === newDiff.action &&
|
|
150
|
+
d.field === newDiff.field &&
|
|
151
|
+
DiffUtility.isObjectDeepEquals(d.value, newDiff.value))) {
|
|
152
|
+
newDiffs.splice(i, 1);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (enableResourceValidation && (newDiffs.length > 0 || dirtyDiffs.length > 0)) {
|
|
157
|
+
throw new TransactionError('Cannot run resource validation with pending resource diffs!');
|
|
158
|
+
}
|
|
159
|
+
if (enableResourceValidation) {
|
|
160
|
+
const validationDiffs = this.resourceDataRepository.diffValidate();
|
|
161
|
+
newDiffs.push(...validationDiffs);
|
|
162
|
+
dirtyDiffs.push(...validationDiffs);
|
|
163
|
+
}
|
|
164
|
+
// new diffs = new - old | dirty diffs = new - actual
|
|
165
|
+
// Any new diff that is also not part of dirty diffs should be skipped, as the actual is already in desired state.
|
|
166
|
+
for (let i = newDiffs.length - 1; i >= 0; i--) {
|
|
167
|
+
const newDiff = newDiffs[i];
|
|
168
|
+
if (!dirtyDiffs.some((d) => d.node.getContext() === newDiff.node.getContext() &&
|
|
169
|
+
d.action === newDiff.action &&
|
|
170
|
+
d.field === newDiff.field &&
|
|
171
|
+
DiffUtility.isObjectDeepEquals(d.value, newDiff.value))) {
|
|
172
|
+
newDiffs.splice(i, 1);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
// Ensure any new diffs are not operating on dirty resources.
|
|
176
|
+
this.resourceDataRepository.ensureDiffsNotOperatingOnDirtyResources(newDiffs);
|
|
177
|
+
// Skip processing dirty diffs that are already accounted for in new diffs.
|
|
178
|
+
for (let i = dirtyDiffs.length - 1; i >= 0; i--) {
|
|
179
|
+
const dirtyDiff = dirtyDiffs[i];
|
|
180
|
+
if (newDiffs.some((d) => d.node.getContext() === dirtyDiff.node.getContext() &&
|
|
181
|
+
d.action === dirtyDiff.action &&
|
|
182
|
+
d.field === dirtyDiff.field &&
|
|
183
|
+
DiffUtility.isObjectDeepEquals(d.value, dirtyDiff.value))) {
|
|
184
|
+
dirtyDiffs.splice(i, 1);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// Terraform resources have no resource actions - terraform owns their lifecycle. Their diffs
|
|
188
|
+
// remain part of the transaction as review artifacts, backed by a no-op action.
|
|
189
|
+
const resolveResourceActions = (d) => {
|
|
190
|
+
if (d.node instanceof ATerraformResource) {
|
|
191
|
+
return [new TerraformNoopResourceAction()];
|
|
192
|
+
}
|
|
193
|
+
return (this.resourceActions.find((a) => a.resourceClass === d.node.constructor)?.actions || []).filter((a) => a.filter(d));
|
|
194
|
+
};
|
|
195
|
+
// Generate diff on resources.
|
|
196
|
+
const resourceDiffs = newDiffs.map((d) => new DiffMetadata(d, resolveResourceActions(d)));
|
|
197
|
+
// Set apply order on resource diffs.
|
|
198
|
+
for (const diff of resourceDiffs) {
|
|
199
|
+
this.setApplyOrder(diff, resourceDiffs);
|
|
200
|
+
}
|
|
201
|
+
// Generate diff on dirty resources.
|
|
202
|
+
const dirtyResourceDiffs = dirtyDiffs.map((d) => new DiffMetadata(d, resolveResourceActions(d)));
|
|
203
|
+
// Set apply order on dirty resource diffs.
|
|
204
|
+
for (const diff of dirtyResourceDiffs) {
|
|
205
|
+
this.setApplyOrder(diff, dirtyResourceDiffs);
|
|
206
|
+
}
|
|
207
|
+
// Contribute the full desired resource graph to terraform. Deferred until after resource diffs
|
|
208
|
+
// are computed and ordered, so an author refusal (a throw in diff*) or an ordering/cycle error
|
|
209
|
+
// aborts generation before any HCL is built — diffs gate generation.
|
|
210
|
+
if (generateTerraform) {
|
|
211
|
+
await this.generateTerraform();
|
|
212
|
+
}
|
|
213
|
+
this.eventService.emit(new ResourceDiffsTransactionEvent(undefined, [[resourceDiffs], [dirtyResourceDiffs]]));
|
|
214
|
+
if (yieldResourceDiffs) {
|
|
215
|
+
yield [resourceDiffs, dirtyResourceDiffs];
|
|
216
|
+
}
|
|
217
|
+
// Apply resource diffs.
|
|
218
|
+
const resourceTransaction = await this.applyResources(resourceDiffs, { skipActualResourceUpdate });
|
|
219
|
+
// Apply dirty resource diffs.
|
|
220
|
+
const dirtyResourceTransaction = await this.applyResources(dirtyResourceDiffs, { skipActualResourceUpdate });
|
|
221
|
+
this.eventService.emit(new ResourceTransactionTransactionEvent(undefined, [resourceTransaction, dirtyResourceTransaction]));
|
|
222
|
+
if (yieldResourceTransaction) {
|
|
223
|
+
yield [...resourceTransaction, ...dirtyResourceTransaction];
|
|
224
|
+
}
|
|
225
|
+
return modelTransaction;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* The diffs {@link TransactionService.applyExternalResourceDeletes} runs: the deletion of every
|
|
229
|
+
* external resource in `resourceDiffs`. A caller that has to decide whether to run them at all
|
|
230
|
+
* names them through this rather than repeating the filter.
|
|
231
|
+
*
|
|
232
|
+
* @internal
|
|
233
|
+
*/
|
|
234
|
+
getExternalResourceDeletes(resourceDiffs) {
|
|
235
|
+
return resourceDiffs
|
|
236
|
+
.flat()
|
|
237
|
+
.filter((d) => d.action === DiffAction.DELETE &&
|
|
238
|
+
d.field === 'resourceId' &&
|
|
239
|
+
!(d.node instanceof ATerraformResource) &&
|
|
240
|
+
!d.applied);
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* @internal
|
|
244
|
+
*/
|
|
245
|
+
registerModelActions(forModel, actions) {
|
|
246
|
+
const modelActions = this.modelActions.find((a) => a.modelClass === forModel);
|
|
247
|
+
if (!modelActions) {
|
|
248
|
+
this.modelActions.push({ actions: actions, modelClass: forModel });
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
for (const action of actions) {
|
|
252
|
+
if (modelActions.actions.find((a) => a.constructor.name === action.constructor.name)) {
|
|
253
|
+
throw new Error(`Action "${action.constructor.name}" already registered for model "${forModel.name}"!`);
|
|
254
|
+
}
|
|
255
|
+
modelActions.actions.push(action);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* @internal
|
|
261
|
+
*/
|
|
262
|
+
registerOverlayActions(forOverlay, actions) {
|
|
263
|
+
const overlayActions = this.overlayActions.find((a) => a.overlayClass === forOverlay);
|
|
264
|
+
if (!overlayActions) {
|
|
265
|
+
this.overlayActions.push({ actions: actions, overlayClass: forOverlay });
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
for (const action of actions) {
|
|
269
|
+
if (overlayActions.actions.find((a) => a.constructor.name === action.constructor.name)) {
|
|
270
|
+
throw new Error(`Action "${action.constructor.name}" already registered for overlay "${forOverlay.name}"!`);
|
|
271
|
+
}
|
|
272
|
+
overlayActions.actions.push(action);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* @internal
|
|
278
|
+
*/
|
|
279
|
+
registerResourceActions(forResource, actions) {
|
|
280
|
+
if (forResource.prototype instanceof ATerraformResource) {
|
|
281
|
+
throw new TransactionError(`Cannot register resource actions for terraform resource "${forResource.name}"! ` +
|
|
282
|
+
'Terraform resources are managed by terraform via toHCL().');
|
|
283
|
+
}
|
|
284
|
+
const resourceActions = this.resourceActions.find((a) => a.resourceClass === forResource);
|
|
285
|
+
if (!resourceActions) {
|
|
286
|
+
this.resourceActions.push({ actions: actions, resourceClass: forResource });
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
for (const action of actions) {
|
|
290
|
+
if (resourceActions.actions.find((a) => a.constructor.name === action.constructor.name)) {
|
|
291
|
+
throw new Error(`Action "${action.constructor.name}" already registered for resource "${forResource.name}"!`);
|
|
292
|
+
}
|
|
293
|
+
resourceActions.actions.push(action);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* @internal
|
|
299
|
+
*/
|
|
300
|
+
unregisterModelActions(forModel) {
|
|
301
|
+
const modelActions = this.modelActions.find((a) => a.modelClass === forModel);
|
|
302
|
+
if (!modelActions) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
modelActions.actions = [];
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* @internal
|
|
309
|
+
*/
|
|
42
310
|
async applyModels(diffs) {
|
|
43
311
|
const transaction = [];
|
|
44
312
|
let currentApplyOrder = 0;
|
|
@@ -56,12 +324,11 @@ export class TransactionService {
|
|
|
56
324
|
const diffToProcess = duplicateDiffs[0].diff;
|
|
57
325
|
for (const a of diff.actions) {
|
|
58
326
|
// Find moduleId.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
moduleId = this.inputService.getModuleIdFromModel(diff.node);
|
|
327
|
+
const moduleId = diff.node instanceof AOverlay
|
|
328
|
+
? this.inputService.getModuleIdFromOverlay(diff.node)
|
|
329
|
+
: this.inputService.getModuleIdFromModel(diff.node);
|
|
330
|
+
if (moduleId === undefined) {
|
|
331
|
+
throw new TransactionError(`Node "${diff.node.getContext()}" is not associated with any module!`);
|
|
65
332
|
}
|
|
66
333
|
// Resolve all keys of the module - inputs keys and nodes generated by the module keys.
|
|
67
334
|
const inputs = { inputs: {}, metadata: {}, models: {}, overlays: {}, resources: {} };
|
|
@@ -93,42 +360,39 @@ export class TransactionService {
|
|
|
93
360
|
inputs.resources[keyValue] = inputValue;
|
|
94
361
|
}
|
|
95
362
|
}
|
|
96
|
-
// Apply all actions on the diff, then update diff metadata with inputs and outputs.
|
|
97
|
-
const outputs = await a.handle(diffToProcess, inputs, {});
|
|
363
|
+
// Apply all actions on the diff in the scope of the node, then update diff metadata with inputs and outputs.
|
|
364
|
+
const outputs = await AResource.runInScope(this.getResourceScope(diffToProcess.node), () => a.handle(diffToProcess, inputs, {}));
|
|
98
365
|
for (const resource of Object.values(outputs)) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
366
|
+
const moduleId = diffToProcess.node instanceof AOverlay
|
|
367
|
+
? this.inputService.getModuleIdFromOverlay(diffToProcess.node)
|
|
368
|
+
: this.inputService.getModuleIdFromModel(diffToProcess.node);
|
|
369
|
+
if (moduleId === undefined) {
|
|
370
|
+
throw new TransactionError(`Node "${diffToProcess.node.getContext()}" is not associated with any module!`);
|
|
102
371
|
}
|
|
103
|
-
|
|
104
|
-
|
|
372
|
+
// Record the node whose action produced the resource.
|
|
373
|
+
this.inputService.registerResourceModel(resource, diffToProcess.node);
|
|
374
|
+
// A resource already in the graph is one the action changed, not created: it keeps the module
|
|
375
|
+
// that created it, and that module's tags.
|
|
376
|
+
const existingResource = this.resourceDataRepository.getNewResourceByContext(resource.getContext());
|
|
377
|
+
if (existingResource && existingResource !== resource) {
|
|
378
|
+
throw new TransactionError(`Resource "${resource.getContext()}" already exists, and action "${a.constructor.name}" created it again!`);
|
|
379
|
+
}
|
|
380
|
+
else if (existingResource) {
|
|
381
|
+
continue;
|
|
105
382
|
}
|
|
106
383
|
// Set resource tags.
|
|
107
384
|
const resourceTags = this.inputService.resolve(`${moduleId}.tag.${resource.getContext()}`);
|
|
108
385
|
for (const [key, value] of Object.entries(resourceTags)) {
|
|
109
386
|
resource.tags[key] = value;
|
|
110
387
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
resource.merge(previousResource);
|
|
114
|
-
}
|
|
115
|
-
else {
|
|
116
|
-
this.resourceDataRepository.addNewResource(resource);
|
|
117
|
-
}
|
|
118
|
-
try {
|
|
119
|
-
this.inputService.registerResource(moduleId, resource);
|
|
120
|
-
}
|
|
121
|
-
catch (error) {
|
|
122
|
-
if (!(error instanceof InputRegistrationError)) {
|
|
123
|
-
throw error;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
388
|
+
this.resourceDataRepository.addNewResource(resource);
|
|
389
|
+
this.inputService.registerResource(moduleId, resource, diffToProcess.node);
|
|
126
390
|
}
|
|
127
391
|
duplicateDiffs.forEach((d) => {
|
|
128
392
|
d.updateInputs(inputs);
|
|
129
393
|
d.updateOutputs(outputs);
|
|
130
394
|
});
|
|
131
|
-
this.eventService.emit(new ModelActionTransactionEvent(a.constructor.name));
|
|
395
|
+
this.eventService.emit(new ModelActionTransactionEvent(a.constructor.name, duplicateDiffs[0]));
|
|
132
396
|
}
|
|
133
397
|
// Include the diff to process in the list of diffs processed in the same level.
|
|
134
398
|
diffsProcessedInSameLevel.push(duplicateDiffs[0]);
|
|
@@ -142,34 +406,10 @@ export class TransactionService {
|
|
|
142
406
|
}
|
|
143
407
|
return transaction;
|
|
144
408
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
if (enableResourceCapture) {
|
|
150
|
-
for (const capturedContext of captureContexts) {
|
|
151
|
-
if (!diffs.some((d) => d.node.getContext() === capturedContext)) {
|
|
152
|
-
missingCaptureContexts.push(capturedContext);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
if (missingCaptureContexts.length > 0) {
|
|
157
|
-
throw new TransactionError(`Provided capture contexts do not match any resource diffs! ${missingCaptureContexts.join(', ')}`);
|
|
158
|
-
}
|
|
159
|
-
for (const diff of diffs) {
|
|
160
|
-
const capture = enableResourceCapture && captureContexts.includes(diff.node.getContext())
|
|
161
|
-
? this.captureService.getCapture(diff.node.getContext())
|
|
162
|
-
: undefined;
|
|
163
|
-
for (const a of diff.actions) {
|
|
164
|
-
this.eventService.emit(new ResourceActionInformationTransactionEvent(a.constructor.name, {
|
|
165
|
-
action: { name: a.constructor.name },
|
|
166
|
-
capture: a.mock ? capture : undefined,
|
|
167
|
-
diff,
|
|
168
|
-
}));
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
async applyResources(diffs, { enableResourceCapture = false } = {}) {
|
|
409
|
+
/**
|
|
410
|
+
* @internal
|
|
411
|
+
*/
|
|
412
|
+
async applyResources(diffs, { skipActualResourceUpdate = false } = {}) {
|
|
173
413
|
const transaction = [];
|
|
174
414
|
const validationErrors = [];
|
|
175
415
|
let currentApplyOrder = 0;
|
|
@@ -187,47 +427,66 @@ export class TransactionService {
|
|
|
187
427
|
const diffToProcess = duplicateDiffs[0].diff;
|
|
188
428
|
for (const a of diff.actions) {
|
|
189
429
|
this.eventService.emit(new ResourceActionInitiatedTransactionEvent(a.constructor.name, diff));
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
430
|
+
// Captured before `handle()` mutates the resource, for the summary event below.
|
|
431
|
+
const preHandleSynth = skipActualResourceUpdate
|
|
432
|
+
? this.resourceDataRepository.getNewResourceByContext(diffToProcess.node.getContext())?.synth()
|
|
433
|
+
: undefined;
|
|
434
|
+
let actionTimeoutId;
|
|
435
|
+
try {
|
|
436
|
+
await Promise.race([
|
|
437
|
+
new Promise(async (resolve, reject) => {
|
|
438
|
+
try {
|
|
439
|
+
const response = await a.handle(diffToProcess);
|
|
440
|
+
this.resourceDataRepository
|
|
441
|
+
.getNewResourceByContext(diffToProcess.node.getContext())
|
|
442
|
+
?.setResponse(response);
|
|
443
|
+
resolve();
|
|
444
|
+
}
|
|
445
|
+
catch (error) {
|
|
446
|
+
if (diffToProcess.action === DiffAction.VALIDATE) {
|
|
447
|
+
validationErrors.push(error);
|
|
205
448
|
resolve();
|
|
206
449
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
validationErrors.push(error);
|
|
210
|
-
resolve();
|
|
211
|
-
}
|
|
212
|
-
else {
|
|
213
|
-
reject(new ResourceActionExceptionTransactionError(error.message, error, diffToProcess, a.constructor.name));
|
|
214
|
-
}
|
|
450
|
+
else {
|
|
451
|
+
reject(new ResourceActionExceptionTransactionError(error.message, error, diffToProcess, a.constructor.name));
|
|
215
452
|
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
453
|
+
}
|
|
454
|
+
}),
|
|
455
|
+
new Promise((_resolve, reject) => (actionTimeoutId = setTimeout(() => {
|
|
456
|
+
reject(new ResourceActionTimeoutTransactionError(`Resource action ${a.constructor.name} timed out after ${a.actionTimeoutInMs || 90000}ms!`, diffToProcess, a.constructor.name));
|
|
457
|
+
}, a.actionTimeoutInMs || 90000))),
|
|
458
|
+
]);
|
|
459
|
+
}
|
|
460
|
+
finally {
|
|
461
|
+
if (actionTimeoutId) {
|
|
462
|
+
clearTimeout(actionTimeoutId);
|
|
226
463
|
}
|
|
227
464
|
}
|
|
228
|
-
|
|
465
|
+
if (skipActualResourceUpdate) {
|
|
466
|
+
// Stateless invocation (run-action): the actual resource graph is not maintained;
|
|
467
|
+
// terraform state is the source of truth until the next commit. The summary is therefore
|
|
468
|
+
// derived from the new-graph resource either side of `handle()`.
|
|
469
|
+
const postHandleSynth = diffToProcess.action === DiffAction.DELETE && diffToProcess.field === 'resourceId'
|
|
470
|
+
? {}
|
|
471
|
+
: this.resourceDataRepository.getNewResourceByContext(diffToProcess.node.getContext())?.synth();
|
|
472
|
+
this.eventService.emit(new ResourceActionSummaryTransactionEvent(a.constructor.name, {
|
|
473
|
+
diff,
|
|
474
|
+
values: { current: postHandleSynth ?? {}, previous: preHandleSynth ?? {} },
|
|
475
|
+
}));
|
|
476
|
+
this.eventService.emit(new ResourceActionCompletedTransactionEvent(a.constructor.name, diff));
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
// De-reference from actual resources. The lookup can miss when a diff references a resource
|
|
480
|
+
// this transaction has not applied yet, so name what is missing rather than letting
|
|
481
|
+
// `undefined` surface as a TypeError several frames away.
|
|
229
482
|
const deReferenceResource = async (context) => {
|
|
230
|
-
|
|
483
|
+
const referencedResource = this.resourceDataRepository.getActualResourceByContext(context);
|
|
484
|
+
if (!referencedResource) {
|
|
485
|
+
throw new TransactionError(`Cannot apply "${diffToProcess.action}" on field "${String(diffToProcess.field)}" of resource ` +
|
|
486
|
+
`"${diffToProcess.node.getContext()}": it references "${context}", which is not in the actual ` +
|
|
487
|
+
'resource graph yet. The referenced resource is applied later in this transaction, or not at all.');
|
|
488
|
+
}
|
|
489
|
+
return referencedResource;
|
|
231
490
|
};
|
|
232
491
|
// Incrementally apply diff inverse to the respective actual resource.
|
|
233
492
|
let actualResource = this.resourceDataRepository.getActualResourceByContext(diffToProcess.node.getContext());
|
|
@@ -274,20 +533,223 @@ export class TransactionService {
|
|
|
274
533
|
}
|
|
275
534
|
return transaction;
|
|
276
535
|
}
|
|
536
|
+
/**
|
|
537
|
+
* Refuses two names the `uniqueProperties` of one resource class say must differ: two values of its path,
|
|
538
|
+
* from two resources or from one, sharing the values of its domain.
|
|
539
|
+
*/
|
|
540
|
+
ensureUniqueNames(resources) {
|
|
541
|
+
const describeResource = (resource) => `resource "${resource.getContext()}" of module "${this.inputService.getModuleIdFromResource(resource)}"`;
|
|
542
|
+
const holders = new Map();
|
|
543
|
+
for (const resource of resources) {
|
|
544
|
+
for (const uniqueName of this.getUniqueNames(resource)) {
|
|
545
|
+
const holder = holders.get(uniqueName.key);
|
|
546
|
+
if (holder) {
|
|
547
|
+
const domainName = uniqueName.within.length > 0 ? `within ${uniqueName.within.join(', ')}` : 'across the app';
|
|
548
|
+
throw new TransactionError(`"${uniqueName.path}" "${uniqueName.name}" is not unique ${domainName}: ${describeResource(holder)} and ${describeResource(resource)} both use it!`);
|
|
549
|
+
}
|
|
550
|
+
holders.set(uniqueName.key, resource);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Contributes every resource in the new (desired) resource graph to the shared
|
|
556
|
+
* {@link TerraformService}: terraform resources via `toHCL()`, external resources via
|
|
557
|
+
* `addOctoTerraformExternalResource()`. Always processes the full desired state — terraform
|
|
558
|
+
* computes the actual create/update/delete against its own state.
|
|
559
|
+
*
|
|
560
|
+
* Order does not matter: terraform resources reference each other through deferred refs, and
|
|
561
|
+
* external resources defer their parent-input wiring — both are resolved later, once every
|
|
562
|
+
* resource is registered (see {@link TerraformService}'s wiring phase).
|
|
563
|
+
*/
|
|
564
|
+
async generateTerraform() {
|
|
565
|
+
const resources = this.resourceDataRepository.getNewResourcesByProperties().filter((r) => !r.isMarkedDeleted());
|
|
566
|
+
this.ensureUniqueNames(resources);
|
|
567
|
+
for (const resource of resources) {
|
|
568
|
+
// Every resource is produced by a model action inside a module, so it is always module-bound.
|
|
569
|
+
// A missing attribution means the resource was registered out-of-band — a hard error.
|
|
570
|
+
const moduleId = this.inputService.getModuleIdFromResource(resource);
|
|
571
|
+
if (moduleId === undefined) {
|
|
572
|
+
throw new TransactionError(`Resource "${resource.resourceId}" is not associated with any module!`);
|
|
573
|
+
}
|
|
574
|
+
const scope = this.terraformService.scope(moduleId);
|
|
575
|
+
if (resource instanceof ATerraformResource) {
|
|
576
|
+
await resource.toHCL(scope);
|
|
577
|
+
}
|
|
578
|
+
else {
|
|
579
|
+
scope.addOctoTerraformExternalResource(resource);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
this.moveResources(resources);
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* @internal
|
|
586
|
+
*/
|
|
277
587
|
getDiffsOfSameNode(diff, diffs, diffAction) {
|
|
278
588
|
return diffs.filter((d) => d.node.getContext() === diff.node.getContext() && d.action === diffAction);
|
|
279
589
|
}
|
|
590
|
+
/**
|
|
591
|
+
* @internal
|
|
592
|
+
*/
|
|
280
593
|
getDuplicateDiffs(diff, diffs) {
|
|
281
594
|
return diffs.filter((d) => d.node.getContext() === diff.node.getContext() &&
|
|
282
595
|
d.field === diff.field &&
|
|
283
596
|
d.action === diff.action &&
|
|
284
597
|
DiffUtility.isObjectDeepEquals(d.value, diff.value));
|
|
285
598
|
}
|
|
599
|
+
/**
|
|
600
|
+
* @internal
|
|
601
|
+
*/
|
|
286
602
|
getMatchingDiffs(diff, diffs) {
|
|
287
603
|
return diffs.filter((d) => d.node.getContext() === diff.node.getContext() &&
|
|
288
604
|
d.field === diff.field &&
|
|
289
605
|
DiffUtility.isObjectDeepEquals(d.value, diff.value));
|
|
290
606
|
}
|
|
607
|
+
/**
|
|
608
|
+
* The scope of the resources a node's actions create: the models the node sits under. An overlay
|
|
609
|
+
* sits where the nodes its own module created sit; the nodes it borrows from other modules are what
|
|
610
|
+
* it joins, so joining one more never moves what it created. An overlay joining only borrowed nodes
|
|
611
|
+
* sits where all of them sit. A model another member sits under is left out, since that member's
|
|
612
|
+
* context already names it.
|
|
613
|
+
*/
|
|
614
|
+
getResourceScope(node) {
|
|
615
|
+
const members = new Map();
|
|
616
|
+
const placedNodeContexts = new Set();
|
|
617
|
+
const collectMembers = (placedNode) => {
|
|
618
|
+
if (placedNodeContexts.has(placedNode.getContext())) {
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
placedNodeContexts.add(placedNode.getContext());
|
|
622
|
+
if (placedNode instanceof AOverlay) {
|
|
623
|
+
const moduleId = this.inputService.getModuleIdFromOverlay(placedNode);
|
|
624
|
+
const joinedNodes = placedNode.getAnchors().map((anchor) => anchor.getParent());
|
|
625
|
+
const ownNodes = joinedNodes.filter((joinedNode) => moduleId !== undefined &&
|
|
626
|
+
(joinedNode instanceof AOverlay
|
|
627
|
+
? this.inputService.getModuleIdFromOverlay(joinedNode)
|
|
628
|
+
: this.inputService.getModuleIdFromModel(joinedNode)) === moduleId);
|
|
629
|
+
for (const placingNode of ownNodes.length > 0 ? ownNodes : joinedNodes) {
|
|
630
|
+
collectMembers(placingNode);
|
|
631
|
+
}
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
for (const dependency of Object.values(placedNode.getParents()).flat()) {
|
|
635
|
+
members.set(dependency.to.getContext(), dependency.to);
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
collectMembers(node);
|
|
639
|
+
// Find every model the members sit under.
|
|
640
|
+
const ancestorContexts = new Set();
|
|
641
|
+
const collectAncestors = (model) => {
|
|
642
|
+
for (const dependency of Object.values(model.getParents()).flat()) {
|
|
643
|
+
if (!ancestorContexts.has(dependency.to.getContext())) {
|
|
644
|
+
ancestorContexts.add(dependency.to.getContext());
|
|
645
|
+
collectAncestors(dependency.to);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
};
|
|
649
|
+
for (const member of members.values()) {
|
|
650
|
+
collectAncestors(member);
|
|
651
|
+
}
|
|
652
|
+
return [...members.keys()].filter((context) => !ancestorContexts.has(context)).sort();
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* The names a resource holds that the `uniqueProperties` of its class say the provider keeps unique,
|
|
656
|
+
* each keyed by its class, path and domain. A path with no value holds no name.
|
|
657
|
+
*/
|
|
658
|
+
getUniqueNames(resource) {
|
|
659
|
+
const resourceClass = resource.constructor;
|
|
660
|
+
const uniqueNames = [];
|
|
661
|
+
for (const { path, within } of resourceClass.UNIQUE_PROPERTIES ?? []) {
|
|
662
|
+
// Resolve every name at the path, skipping a branch with no value.
|
|
663
|
+
let names = [resource.properties];
|
|
664
|
+
for (const segment of path.split('.')) {
|
|
665
|
+
const isArray = segment.endsWith('[]');
|
|
666
|
+
const key = isArray ? segment.slice(0, -2) : segment;
|
|
667
|
+
names = names.flatMap((value) => {
|
|
668
|
+
const child = value?.[key];
|
|
669
|
+
if (child === undefined || child === null) {
|
|
670
|
+
return [];
|
|
671
|
+
}
|
|
672
|
+
return isArray ? child : [child];
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
if (names.length === 0) {
|
|
676
|
+
continue;
|
|
677
|
+
}
|
|
678
|
+
// Resolve the domain, which every named resource must have.
|
|
679
|
+
const domain = within.map((property) => {
|
|
680
|
+
const value = resource.properties[property];
|
|
681
|
+
if (value === undefined || value === null) {
|
|
682
|
+
throw new TransactionError(`resource "${resource.getContext()}" of module "${this.inputService.getModuleIdFromResource(resource)}" has no "${property}", which its "${path}" is unique within!`);
|
|
683
|
+
}
|
|
684
|
+
return value;
|
|
685
|
+
});
|
|
686
|
+
for (const name of names) {
|
|
687
|
+
const key = JSON.stringify([resourceClass.NODE_PACKAGE, resourceClass.NODE_NAME, path, ...domain, name]);
|
|
688
|
+
uniqueNames.push({ key, name, path, within });
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
return uniqueNames;
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Hands each committed resource's terraform state to the resource taking it over, so the two are one
|
|
695
|
+
* piece of infrastructure, updated or replaced in place: a resource now rendered into another module's
|
|
696
|
+
* folder, and an added resource taking a name of a deleted resource of its class. Without it terraform
|
|
697
|
+
* creates the new one while the old one is destroyed, and the provider refuses a name still held.
|
|
698
|
+
* A renamed external resource is left out: octo runs its actions itself, and a move would skip them.
|
|
699
|
+
*/
|
|
700
|
+
moveResources(resources) {
|
|
701
|
+
const actualResources = this.resourceDataRepository.getActualResourcesByProperties();
|
|
702
|
+
const actualContexts = new Set(actualResources.map((r) => r.getContext()));
|
|
703
|
+
const newContexts = new Set(resources.map((r) => r.getContext()));
|
|
704
|
+
// Move each resource whose folder changed.
|
|
705
|
+
for (const resource of resources) {
|
|
706
|
+
const moduleId = this.inputService.getModuleIdFromResource(resource);
|
|
707
|
+
const actualModuleId = this.resourceSerializationService.getActualResourceModuleId(resource);
|
|
708
|
+
if (actualModuleId !== undefined && actualModuleId !== moduleId) {
|
|
709
|
+
this.terraformService.moveResource(actualModuleId, resource.resourceId, moduleId, resource.resourceId);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
// Index the names of every deleted terraform resource.
|
|
713
|
+
const deletedResourcesByName = new Map();
|
|
714
|
+
for (const resource of actualResources) {
|
|
715
|
+
if (!(resource instanceof ATerraformResource) || newContexts.has(resource.getContext())) {
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
718
|
+
for (const { key } of this.getUniqueNames(resource)) {
|
|
719
|
+
deletedResourcesByName.set(key, resource);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
if (deletedResourcesByName.size === 0) {
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
// Pair each added terraform resource with the deleted resource holding one of its names.
|
|
726
|
+
const takenBy = new Map();
|
|
727
|
+
for (const resource of resources) {
|
|
728
|
+
if (!(resource instanceof ATerraformResource) || actualContexts.has(resource.getContext())) {
|
|
729
|
+
continue;
|
|
730
|
+
}
|
|
731
|
+
const deletedResources = [
|
|
732
|
+
...new Set(this.getUniqueNames(resource)
|
|
733
|
+
.map(({ key }) => deletedResourcesByName.get(key))
|
|
734
|
+
.filter((r) => r !== undefined)),
|
|
735
|
+
];
|
|
736
|
+
if (deletedResources.length === 0) {
|
|
737
|
+
continue;
|
|
738
|
+
}
|
|
739
|
+
if (deletedResources.length > 1) {
|
|
740
|
+
throw new TransactionError(`Resource "${resource.getContext()}" takes the names of both "${deletedResources[0].getContext()}" and "${deletedResources[1].getContext()}", and can take over only one of them!`);
|
|
741
|
+
}
|
|
742
|
+
const [deletedResource] = deletedResources;
|
|
743
|
+
const otherResource = takenBy.get(deletedResource);
|
|
744
|
+
if (otherResource) {
|
|
745
|
+
throw new TransactionError(`Resources "${otherResource.getContext()}" and "${resource.getContext()}" both take a name of "${deletedResource.getContext()}", and only one can take it over!`);
|
|
746
|
+
}
|
|
747
|
+
takenBy.set(deletedResource, resource);
|
|
748
|
+
const moduleId = this.inputService.getModuleIdFromResource(resource);
|
|
749
|
+
const deletedModuleId = this.resourceSerializationService.getActualResourceModuleId(deletedResource) ?? moduleId;
|
|
750
|
+
this.terraformService.moveResource(deletedModuleId, deletedResource.resourceId, moduleId, resource.resourceId);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
291
753
|
/**
|
|
292
754
|
* Before assigning order to a diff, all its node's parent diffs must be processed.
|
|
293
755
|
* E.g. diff to add environment, depends on region to exist, thus add region diff gets processed first.
|
|
@@ -324,8 +786,9 @@ export class TransactionService {
|
|
|
324
786
|
}
|
|
325
787
|
}
|
|
326
788
|
// Get diffs of same node.
|
|
327
|
-
// Process ADD before UPDATE, and UPDATE before DELETE.
|
|
328
|
-
|
|
789
|
+
// Process ADD before UPDATE/REPLACE, and UPDATE/REPLACE before DELETE. A REPLACE recreates the
|
|
790
|
+
// resource in place (same context), so for ordering it behaves like an UPDATE.
|
|
791
|
+
if (diff.action === DiffAction.UPDATE || diff.action === DiffAction.REPLACE) {
|
|
329
792
|
const diffsOfSameNodeWithAddAction = this.getDiffsOfSameNode(diff, diffs, DiffAction.ADD);
|
|
330
793
|
for (const sameNodeDiffWithAddAction of diffsOfSameNodeWithAddAction) {
|
|
331
794
|
this.setApplyOrder(sameNodeDiffWithAddAction, diffs, [...seen, diff]);
|
|
@@ -341,154 +804,6 @@ export class TransactionService {
|
|
|
341
804
|
}
|
|
342
805
|
diff.applyOrder = Math.max(...dependencyApplyOrders) + 1;
|
|
343
806
|
}
|
|
344
|
-
async *beginTransaction(diffs, { enableResourceCapture = false, enableResourceValidation = false, yieldModelDiffs = false, yieldModelTransaction = false, yieldResourceDiffs = false, yieldResourceTransaction = false, } = {}) {
|
|
345
|
-
// Diff overlays and add to existing diffs.
|
|
346
|
-
const overlayDiffs = await this.overlayDataRepository.diff();
|
|
347
|
-
diffs.push(...overlayDiffs);
|
|
348
|
-
// Generate diff on models.
|
|
349
|
-
const modelDiffs = diffs.map((d) => {
|
|
350
|
-
if (d.node.constructor.NODE_TYPE === NodeType.OVERLAY) {
|
|
351
|
-
return new DiffMetadata(d, (this.overlayActions.find((a) => a.overlayClass === d.node.constructor)?.actions || []).filter((a) => a.filter(d)));
|
|
352
|
-
}
|
|
353
|
-
else {
|
|
354
|
-
return new DiffMetadata(d, (this.modelActions.find((a) => a.modelClass === d.node.constructor)?.actions || []).filter((a) => a.filter(d)));
|
|
355
|
-
}
|
|
356
|
-
});
|
|
357
|
-
// Set apply order on model diffs.
|
|
358
|
-
for (const diff of modelDiffs) {
|
|
359
|
-
this.setApplyOrder(diff, modelDiffs);
|
|
360
|
-
}
|
|
361
|
-
this.eventService.emit(new ModelDiffsTransactionEvent(undefined, [modelDiffs]));
|
|
362
|
-
if (yieldModelDiffs) {
|
|
363
|
-
yield [modelDiffs];
|
|
364
|
-
}
|
|
365
|
-
// Apply model diffs.
|
|
366
|
-
const modelTransaction = await this.applyModels(modelDiffs);
|
|
367
|
-
this.eventService.emit(new ModelTransactionTransactionEvent(undefined, modelTransaction));
|
|
368
|
-
if (yieldModelTransaction) {
|
|
369
|
-
yield modelTransaction;
|
|
370
|
-
}
|
|
371
|
-
// Generate resource diffs.
|
|
372
|
-
const newDiffs = await this.resourceDataRepository.diff();
|
|
373
|
-
const dirtyDiffs = await this.resourceDataRepository.diffDirty();
|
|
374
|
-
if (enableResourceValidation && (newDiffs.length > 0 || dirtyDiffs.length > 0)) {
|
|
375
|
-
throw new TransactionError('Cannot run resource validation with pending resource diffs!');
|
|
376
|
-
}
|
|
377
|
-
if (enableResourceValidation) {
|
|
378
|
-
const validationDiffs = this.resourceDataRepository.diffValidate();
|
|
379
|
-
newDiffs.push(...validationDiffs);
|
|
380
|
-
dirtyDiffs.push(...validationDiffs);
|
|
381
|
-
}
|
|
382
|
-
// new diffs = new - old | dirty diffs = new - actual
|
|
383
|
-
// Any new diff that is also not part of dirty diffs should be skipped, as the actual is already in desired state.
|
|
384
|
-
for (let i = newDiffs.length - 1; i >= 0; i--) {
|
|
385
|
-
const newDiff = newDiffs[i];
|
|
386
|
-
if (!dirtyDiffs.some((d) => d.node.getContext() === newDiff.node.getContext() &&
|
|
387
|
-
d.action === newDiff.action &&
|
|
388
|
-
d.field === newDiff.field &&
|
|
389
|
-
DiffUtility.isObjectDeepEquals(d.value, newDiff.value))) {
|
|
390
|
-
newDiffs.splice(i, 1);
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
// Ensure any new diffs are not operating on dirty resources.
|
|
394
|
-
this.resourceDataRepository.ensureDiffsNotOperatingOnDirtyResources(newDiffs);
|
|
395
|
-
// Skip processing dirty diffs that are already accounted for in new diffs.
|
|
396
|
-
for (let i = dirtyDiffs.length - 1; i >= 0; i--) {
|
|
397
|
-
const dirtyDiff = dirtyDiffs[i];
|
|
398
|
-
if (newDiffs.some((d) => d.node.getContext() === dirtyDiff.node.getContext() &&
|
|
399
|
-
d.action === dirtyDiff.action &&
|
|
400
|
-
d.field === dirtyDiff.field &&
|
|
401
|
-
DiffUtility.isObjectDeepEquals(d.value, dirtyDiff.value))) {
|
|
402
|
-
dirtyDiffs.splice(i, 1);
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
// Generate diff on resources.
|
|
406
|
-
const resourceDiffs = newDiffs.map((d) => new DiffMetadata(d, (this.resourceActions.find((a) => a.resourceClass === d.node.constructor)?.actions || []).filter((a) => a.filter(d))));
|
|
407
|
-
// Set apply order on resource diffs.
|
|
408
|
-
for (const diff of resourceDiffs) {
|
|
409
|
-
this.setApplyOrder(diff, resourceDiffs);
|
|
410
|
-
}
|
|
411
|
-
// Generate diff on dirty resources.
|
|
412
|
-
const dirtyResourceDiffs = dirtyDiffs.map((d) => new DiffMetadata(d, (this.resourceActions.find((a) => a.resourceClass === d.node.constructor)?.actions || []).filter((a) => a.filter(d))));
|
|
413
|
-
// Set apply order on dirty resource diffs.
|
|
414
|
-
for (const diff of dirtyResourceDiffs) {
|
|
415
|
-
this.setApplyOrder(diff, dirtyResourceDiffs);
|
|
416
|
-
}
|
|
417
|
-
this.applyResourcesDryRun([...resourceDiffs, ...dirtyResourceDiffs], { enableResourceCapture });
|
|
418
|
-
this.eventService.emit(new ResourceDiffsTransactionEvent(undefined, [[resourceDiffs], [dirtyResourceDiffs]]));
|
|
419
|
-
if (yieldResourceDiffs) {
|
|
420
|
-
yield [resourceDiffs, dirtyResourceDiffs];
|
|
421
|
-
}
|
|
422
|
-
// Apply resource diffs.
|
|
423
|
-
const resourceTransaction = await this.applyResources(resourceDiffs, {
|
|
424
|
-
enableResourceCapture,
|
|
425
|
-
});
|
|
426
|
-
// Apply dirty resource diffs.
|
|
427
|
-
const dirtyResourceTransaction = await this.applyResources(dirtyResourceDiffs, {
|
|
428
|
-
enableResourceCapture,
|
|
429
|
-
});
|
|
430
|
-
this.eventService.emit(new ResourceTransactionTransactionEvent(undefined, [resourceTransaction, dirtyResourceTransaction]));
|
|
431
|
-
if (yieldResourceTransaction) {
|
|
432
|
-
yield [...resourceTransaction, ...dirtyResourceTransaction];
|
|
433
|
-
}
|
|
434
|
-
return modelTransaction;
|
|
435
|
-
}
|
|
436
|
-
registerModelActions(forModel, actions) {
|
|
437
|
-
const modelActions = this.modelActions.find((a) => a.modelClass === forModel);
|
|
438
|
-
if (!modelActions) {
|
|
439
|
-
this.modelActions.push({ actions: actions, modelClass: forModel });
|
|
440
|
-
}
|
|
441
|
-
else {
|
|
442
|
-
for (const action of actions) {
|
|
443
|
-
if (modelActions.actions.find((a) => a.constructor.name === action.constructor.name)) {
|
|
444
|
-
throw new Error(`Action "${action.constructor.name}" already registered for model "${forModel.name}"!`);
|
|
445
|
-
}
|
|
446
|
-
modelActions.actions.push(action);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
registerOverlayActions(forOverlay, actions) {
|
|
451
|
-
const overlayActions = this.overlayActions.find((a) => a.overlayClass === forOverlay);
|
|
452
|
-
if (!overlayActions) {
|
|
453
|
-
this.overlayActions.push({ actions: actions, overlayClass: forOverlay });
|
|
454
|
-
}
|
|
455
|
-
else {
|
|
456
|
-
for (const action of actions) {
|
|
457
|
-
if (overlayActions.actions.find((a) => a.constructor.name === action.constructor.name)) {
|
|
458
|
-
throw new Error(`Action "${action.constructor.name}" already registered for overlay "${forOverlay.name}"!`);
|
|
459
|
-
}
|
|
460
|
-
overlayActions.actions.push(action);
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
registerResourceActions(forResource, actions) {
|
|
465
|
-
const resourceActions = this.resourceActions.find((a) => a.resourceClass === forResource);
|
|
466
|
-
if (!resourceActions) {
|
|
467
|
-
this.resourceActions.push({ actions: actions, resourceClass: forResource });
|
|
468
|
-
}
|
|
469
|
-
else {
|
|
470
|
-
for (const action of actions) {
|
|
471
|
-
if (resourceActions.actions.find((a) => a.constructor.name === action.constructor.name)) {
|
|
472
|
-
throw new Error(`Action "${action.constructor.name}" already registered for resource "${forResource.name}"!`);
|
|
473
|
-
}
|
|
474
|
-
resourceActions.actions.push(action);
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
unregisterModelActions(forModel) {
|
|
479
|
-
const modelActions = this.modelActions.find((a) => a.modelClass === forModel);
|
|
480
|
-
if (!modelActions) {
|
|
481
|
-
return;
|
|
482
|
-
}
|
|
483
|
-
modelActions.actions = [];
|
|
484
|
-
}
|
|
485
|
-
unregisterOverlayActions(forOverlay) {
|
|
486
|
-
const overlayActions = this.overlayActions.find((a) => a.overlayClass === forOverlay);
|
|
487
|
-
if (!overlayActions) {
|
|
488
|
-
return;
|
|
489
|
-
}
|
|
490
|
-
overlayActions.actions = [];
|
|
491
|
-
}
|
|
492
807
|
}
|
|
493
808
|
__decorate([
|
|
494
809
|
EventSource(ModelActionRegistrationEvent),
|
|
@@ -512,18 +827,22 @@ __decorate([
|
|
|
512
827
|
* @internal
|
|
513
828
|
*/
|
|
514
829
|
let TransactionServiceFactory = class TransactionServiceFactory {
|
|
830
|
+
/**
|
|
831
|
+
* @internal
|
|
832
|
+
*/
|
|
515
833
|
static async create() {
|
|
516
834
|
const container = Container.getInstance();
|
|
517
|
-
const [
|
|
518
|
-
container.get(CaptureService),
|
|
835
|
+
const [eventService, inputService, moduleContainer, overlayDataRepository, resourceDataRepository, resourceSerializationService, terraformService,] = await Promise.all([
|
|
519
836
|
container.get(EventService),
|
|
520
837
|
container.get(InputService),
|
|
521
838
|
container.get(ModuleContainer),
|
|
522
839
|
container.get(OverlayDataRepository),
|
|
523
840
|
container.get(ResourceDataRepository),
|
|
841
|
+
container.get(ResourceSerializationService),
|
|
842
|
+
container.get(TerraformService),
|
|
524
843
|
]);
|
|
525
844
|
if (!this.instance) {
|
|
526
|
-
this.instance = new TransactionService(
|
|
845
|
+
this.instance = new TransactionService(eventService, inputService, moduleContainer, overlayDataRepository, resourceDataRepository, resourceSerializationService, terraformService);
|
|
527
846
|
}
|
|
528
847
|
return this.instance;
|
|
529
848
|
}
|