@quadnix/octo 0.0.29 → 0.1.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 +25 -3
- package/dist/app.type.d.ts +143 -4
- 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 +1 -1
- package/dist/decorators/model.decorator.js +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 +5 -0
- package/dist/decorators/on-event.decorator.js +5 -0
- package/dist/decorators/on-event.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 +6 -0
- package/dist/errors/schema.error.js +6 -0
- package/dist/errors/schema.error.js.map +1 -1
- package/dist/errors/terraform.error.d.ts +19 -0
- package/dist/errors/terraform.error.js +23 -0
- package/dist/errors/terraform.error.js.map +1 -0
- package/dist/errors/transaction.error.d.ts +77 -0
- package/dist/errors/transaction.error.js +83 -0
- package/dist/errors/transaction.error.js.map +1 -1
- package/dist/events/action.event.d.ts +0 -2
- package/dist/events/action.event.js +0 -2
- package/dist/events/action.event.js.map +1 -1
- package/dist/events/event.model.d.ts +21 -5
- package/dist/events/event.model.js +11 -5
- package/dist/events/event.model.js.map +1 -1
- package/dist/events/hook/commit.hook.event.d.ts +0 -6
- 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 +0 -2
- 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 +0 -6
- 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 +0 -6
- 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 +3 -0
- package/dist/events/index.js +3 -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 +57 -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 +0 -2
- package/dist/events/module.event.js +0 -2
- package/dist/events/module.event.js.map +1 -1
- package/dist/events/registration.event.d.ts +18 -14
- package/dist/events/registration.event.js +18 -14
- package/dist/events/registration.event.js.map +1 -1
- package/dist/events/serialization.event.d.ts +15 -12
- package/dist/events/serialization.event.js +15 -12
- package/dist/events/serialization.event.js.map +1 -1
- package/dist/events/terraform.event.d.ts +42 -0
- package/dist/events/terraform.event.js +31 -0
- package/dist/events/terraform.event.js.map +1 -0
- package/dist/events/transaction.event.d.ts +0 -37
- package/dist/events/transaction.event.js +0 -28
- package/dist/events/transaction.event.js.map +1 -1
- package/dist/functions/container/container.d.ts +54 -13
- package/dist/functions/container/container.js +63 -18
- 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 +82 -8
- package/dist/functions/dependency/dependency.js +80 -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 +55 -1
- package/dist/functions/diff/diff.js +44 -3
- package/dist/functions/diff/diff.js.map +1 -1
- package/dist/functions/diff/diff.utility.d.ts +17 -2
- package/dist/functions/diff/diff.utility.js +53 -38
- package/dist/functions/diff/diff.utility.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 -0
- package/dist/functions/node/node-action.abstract.js +20 -0
- 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 +85 -12
- package/dist/functions/node/node.abstract.js.map +1 -1
- package/dist/functions/node/node.interface.d.ts +1 -0
- package/dist/functions/schema/schema.d.ts +7 -0
- package/dist/functions/schema/schema.js +9 -0
- package/dist/functions/schema/schema.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts +244 -14
- package/dist/main.js +361 -72
- package/dist/main.js.map +1 -1
- package/dist/models/account/account.model.d.ts +37 -7
- package/dist/models/account/account.model.js +40 -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 +16 -1
- package/dist/models/app/app.model.js +19 -4
- 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 +2 -0
- package/dist/models/deployment/deployment.schema.js +2 -0
- package/dist/models/deployment/deployment.schema.js.map +1 -1
- package/dist/models/environment/environment.model.d.ts +13 -1
- package/dist/models/environment/environment.model.js +20 -8
- 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 +20 -2
- package/dist/models/execution/execution.model.js +46 -28
- 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 +17 -4
- package/dist/models/image/image.model.js +20 -7
- 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 +106 -7
- 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 +13 -1
- package/dist/models/pipeline/pipeline.model.js +18 -6
- 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 +13 -1
- package/dist/models/server/server.model.js +16 -4
- 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 +15 -1
- package/dist/models/service/service.model.js +15 -1
- package/dist/models/service/service.model.js.map +1 -1
- package/dist/models/service/service.schema.d.ts +4 -1
- package/dist/models/service/service.schema.js +4 -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 +29 -0
- package/dist/modes/generate.mode.js +49 -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 +162 -0
- package/dist/modes/validate.mode.js +480 -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 +36 -0
- package/dist/modules/module.container.js +36 -0
- package/dist/modules/module.container.js.map +1 -1
- package/dist/modules/test-module.container.d.ts +139 -24
- package/dist/modules/test-module.container.js +410 -163
- 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 +26 -2
- 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 +118 -3
- package/dist/resources/resource.abstract.js +166 -33
- package/dist/resources/resource.abstract.js.map +1 -1
- package/dist/resources/resource.schema.d.ts +29 -1
- package/dist/resources/resource.schema.js +28 -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 +30 -3
- 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 +52 -3
- package/dist/services/input/input.service.js +72 -8
- 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 +26 -2
- package/dist/services/serialization/model/model-serialization.service.js +110 -62
- package/dist/services/serialization/model/model-serialization.service.js.map +1 -1
- package/dist/services/serialization/resource/resource-serialization.service.d.ts +33 -4
- package/dist/services/serialization/resource/resource-serialization.service.js +64 -28
- package/dist/services/serialization/resource/resource-serialization.service.js.map +1 -1
- package/dist/services/state-management/file-app-lock.utility.d.ts +43 -0
- package/dist/services/state-management/file-app-lock.utility.js +124 -0
- package/dist/services/state-management/file-app-lock.utility.js.map +1 -0
- package/dist/services/state-management/local-encryption.state-provider.d.ts +53 -2
- package/dist/services/state-management/local-encryption.state-provider.js +75 -49
- package/dist/services/state-management/local-encryption.state-provider.js.map +1 -1
- package/dist/services/state-management/local.state-provider.d.ts +49 -2
- package/dist/services/state-management/local.state-provider.js +70 -49
- package/dist/services/state-management/local.state-provider.js.map +1 -1
- package/dist/services/state-management/state-management.service.d.ts +141 -6
- package/dist/services/state-management/state-management.service.js +149 -4
- package/dist/services/state-management/state-management.service.js.map +1 -1
- package/dist/services/state-management/state-provider.interface.d.ts +55 -1
- package/dist/services/state-management/terraform-state.service.d.ts +76 -0
- package/dist/services/state-management/terraform-state.service.js +154 -0
- package/dist/services/state-management/terraform-state.service.js.map +1 -0
- package/dist/services/state-management/test.state-provider.d.ts +38 -1
- package/dist/services/state-management/test.state-provider.js +52 -21
- package/dist/services/state-management/test.state-provider.js.map +1 -1
- package/dist/services/terraform/terraform.service.d.ts +426 -0
- package/dist/services/terraform/terraform.service.js +995 -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 +86 -10
- package/dist/services/transaction/transaction.service.js +387 -232
- package/dist/services/transaction/transaction.service.js.map +1 -1
- package/dist/services/validation/validation.service.d.ts +13 -1
- 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/utilities/file/file.utility.d.ts +15 -0
- package/dist/utilities/file/file.utility.js +18 -1
- package/dist/utilities/file/file.utility.js.map +1 -1
- package/dist/utilities/hcl/hcl.utility.d.ts +61 -0
- package/dist/utilities/hcl/hcl.utility.js +229 -0
- package/dist/utilities/hcl/hcl.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 +21 -0
- package/dist/utilities/string/string.utility.js +39 -0
- package/dist/utilities/string/string.utility.js.map +1 -0
- package/dist/utilities/terraform/terraform.utility.d.ts +227 -0
- package/dist/utilities/terraform/terraform.utility.js +434 -0
- package/dist/utilities/terraform/terraform.utility.js.map +1 -0
- package/dist/utilities/test-helpers/test-classes.d.ts +18 -31
- package/dist/utilities/test-helpers/test-classes.js +18 -41
- package/dist/utilities/test-helpers/test-classes.js.map +1 -1
- package/dist/utilities/test-helpers/test-models.js +12 -0
- 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 +463 -0
- package/dist/utilities/test-helpers/test-modes.js.map +1 -0
- package/dist/utilities/test-helpers/test-modules.d.ts +41 -0
- package/dist/utilities/test-helpers/test-modules.js +79 -0
- package/dist/utilities/test-helpers/test-modules.js.map +1 -0
- package/dist/utilities/test-helpers/test-overlays.d.ts +17 -1
- package/dist/utilities/test-helpers/test-overlays.js +49 -5
- package/dist/utilities/test-helpers/test-overlays.js.map +1 -1
- package/dist/utilities/test-helpers/test-resources.d.ts +19 -1
- package/dist/utilities/test-helpers/test-resources.js +61 -3
- 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 +6 -5
- 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/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
|
@@ -11,7 +11,7 @@ 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
13
|
import { InputNotFoundTransactionError, InputRegistrationError, ResourceActionExceptionTransactionError, ResourceActionTimeoutTransactionError, TransactionError, ValidationTransactionError, } from '../../errors/index.js';
|
|
14
|
-
import { ModelActionRegistrationEvent, ModelActionTransactionEvent, ModelDiffsTransactionEvent, ModelTransactionTransactionEvent, ResourceActionCompletedTransactionEvent,
|
|
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';
|
|
@@ -21,24 +21,293 @@ 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
25
|
import { EventService } from '../event/event.service.js';
|
|
26
26
|
import { InputService } from '../input/input.service.js';
|
|
27
|
+
import { TerraformService } from '../terraform/terraform.service.js';
|
|
28
|
+
/**
|
|
29
|
+
* No-op resource action backing terraform resource diffs. Terraform manages the resource's lifecycle;
|
|
30
|
+
* octo only carries the diff for review and validation.
|
|
31
|
+
*
|
|
32
|
+
* @internal
|
|
33
|
+
*/
|
|
34
|
+
class TerraformNoopResourceAction {
|
|
35
|
+
/**
|
|
36
|
+
* @internal
|
|
37
|
+
*/
|
|
38
|
+
filter() {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* @internal
|
|
43
|
+
*/
|
|
44
|
+
async handle() { }
|
|
45
|
+
}
|
|
27
46
|
/**
|
|
28
47
|
* @internal
|
|
29
48
|
*/
|
|
30
49
|
export class TransactionService {
|
|
31
|
-
|
|
32
|
-
|
|
50
|
+
/**
|
|
51
|
+
* @internal
|
|
52
|
+
*/
|
|
53
|
+
constructor(eventService, inputService, moduleContainer, overlayDataRepository, resourceDataRepository, terraformService) {
|
|
33
54
|
this.eventService = eventService;
|
|
34
55
|
this.inputService = inputService;
|
|
35
56
|
this.moduleContainer = moduleContainer;
|
|
36
57
|
this.overlayDataRepository = overlayDataRepository;
|
|
37
58
|
this.resourceDataRepository = resourceDataRepository;
|
|
59
|
+
this.terraformService = terraformService;
|
|
38
60
|
this.modelActions = [];
|
|
39
61
|
this.overlayActions = [];
|
|
40
62
|
this.resourceActions = [];
|
|
41
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Runs the delete action of every **external** resource in `resourceDiffs` that is being deleted.
|
|
66
|
+
*
|
|
67
|
+
* Terraform cannot do this. Octo deletes a resource by removing its block from the configuration,
|
|
68
|
+
* and terraform will not run a `when = destroy` provisioner for a resource that is no longer in
|
|
69
|
+
* the configuration - it drops the `null_resource` from state and moves on. So the callback that
|
|
70
|
+
* would have told an external resource to delete itself never fires, and the real thing it created
|
|
71
|
+
* outlives both state files with nothing tracking it.
|
|
72
|
+
*
|
|
73
|
+
* Octo knows the resource was deleted, which is why this can be driven from its own diff instead.
|
|
74
|
+
* {@link commit} calls it before recording anything, so a failed delete leaves octo state untouched
|
|
75
|
+
* and the deletion is retried on the next run rather than being forgotten.
|
|
76
|
+
*
|
|
77
|
+
* Terraform-managed resources are excluded: terraform destroys those itself, and a resource action
|
|
78
|
+
* on one is rejected everywhere else in octo.
|
|
79
|
+
*
|
|
80
|
+
* @returns The diffs whose actions ran, in the order they ran.
|
|
81
|
+
*/
|
|
82
|
+
async applyExternalResourceDeletes(resourceDiffs) {
|
|
83
|
+
const deleteDiffs = this.getExternalResourceDeletes(resourceDiffs);
|
|
84
|
+
if (deleteDiffs.length === 0) {
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
// Ordered among themselves: `applyOrder` is assigned across the whole resource diff set, and
|
|
88
|
+
// this is a subset of it, so the levels it carries can start anywhere. Re-basing them from zero
|
|
89
|
+
// keeps applyResources' level walk terminating.
|
|
90
|
+
const orders = [...new Set(deleteDiffs.map((d) => d.applyOrder))].sort((a, b) => a - b);
|
|
91
|
+
for (const diff of deleteDiffs) {
|
|
92
|
+
diff.applyOrder = orders.indexOf(diff.applyOrder);
|
|
93
|
+
}
|
|
94
|
+
// Stateless, like run-action: the actual resource graph is not touched here. `commit` syncs
|
|
95
|
+
// actual to new immediately afterwards, which is what records the deletion.
|
|
96
|
+
return this.applyResources(deleteDiffs, { skipActualResourceUpdate: true });
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* @internal
|
|
100
|
+
*/
|
|
101
|
+
async *beginTransaction(diffs, { enableResourceValidation = false, filterResourceDiffsByResourceId = undefined, generateTerraform = false, skipActualResourceUpdate = false, yieldModelDiffs = false, yieldModelTransaction = false, yieldResourceDiffs = false, yieldResourceTransaction = false, } = {}) {
|
|
102
|
+
// Diff overlays and add to existing diffs.
|
|
103
|
+
const overlayDiffs = await this.overlayDataRepository.diff();
|
|
104
|
+
diffs.push(...overlayDiffs);
|
|
105
|
+
// Generate diff on models.
|
|
106
|
+
const modelDiffs = diffs.map((d) => {
|
|
107
|
+
if (d.node.constructor.NODE_TYPE === NodeType.OVERLAY) {
|
|
108
|
+
return new DiffMetadata(d, (this.overlayActions.find((a) => a.overlayClass === d.node.constructor)?.actions || []).filter((a) => a.filter(d)));
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
return new DiffMetadata(d, (this.modelActions.find((a) => a.modelClass === d.node.constructor)?.actions || []).filter((a) => a.filter(d)));
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
// Set apply order on model diffs.
|
|
115
|
+
for (const diff of modelDiffs) {
|
|
116
|
+
this.setApplyOrder(diff, modelDiffs);
|
|
117
|
+
}
|
|
118
|
+
this.eventService.emit(new ModelDiffsTransactionEvent(undefined, [modelDiffs]));
|
|
119
|
+
if (yieldModelDiffs) {
|
|
120
|
+
yield [modelDiffs];
|
|
121
|
+
}
|
|
122
|
+
// Apply model diffs.
|
|
123
|
+
const modelTransaction = await this.applyModels(modelDiffs);
|
|
124
|
+
this.eventService.emit(new ModelTransactionTransactionEvent(undefined, modelTransaction));
|
|
125
|
+
if (yieldModelTransaction) {
|
|
126
|
+
yield modelTransaction;
|
|
127
|
+
}
|
|
128
|
+
// Generate resource diffs.
|
|
129
|
+
let newDiffs = await this.resourceDataRepository.diff();
|
|
130
|
+
let dirtyDiffs = await this.resourceDataRepository.diffDirty();
|
|
131
|
+
// Scope diffs to a single resource, e.g. when terraform invokes octo for one resource action.
|
|
132
|
+
if (filterResourceDiffsByResourceId !== undefined) {
|
|
133
|
+
newDiffs = newDiffs.filter((d) => d.node.resourceId === filterResourceDiffsByResourceId);
|
|
134
|
+
dirtyDiffs = dirtyDiffs.filter((d) => d.node.resourceId === filterResourceDiffsByResourceId);
|
|
135
|
+
// Prefer dirty diffs (actual-graph nodes) over equal new diffs (old-graph nodes) for
|
|
136
|
+
// execution: deserialized old resources are frozen and cannot receive injected inputs.
|
|
137
|
+
for (let i = newDiffs.length - 1; i >= 0; i--) {
|
|
138
|
+
const newDiff = newDiffs[i];
|
|
139
|
+
if (dirtyDiffs.some((d) => d.node.getContext() === newDiff.node.getContext() &&
|
|
140
|
+
d.action === newDiff.action &&
|
|
141
|
+
d.field === newDiff.field &&
|
|
142
|
+
DiffUtility.isObjectDeepEquals(d.value, newDiff.value))) {
|
|
143
|
+
newDiffs.splice(i, 1);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (enableResourceValidation && (newDiffs.length > 0 || dirtyDiffs.length > 0)) {
|
|
148
|
+
throw new TransactionError('Cannot run resource validation with pending resource diffs!');
|
|
149
|
+
}
|
|
150
|
+
if (enableResourceValidation) {
|
|
151
|
+
const validationDiffs = this.resourceDataRepository.diffValidate();
|
|
152
|
+
newDiffs.push(...validationDiffs);
|
|
153
|
+
dirtyDiffs.push(...validationDiffs);
|
|
154
|
+
}
|
|
155
|
+
// new diffs = new - old | dirty diffs = new - actual
|
|
156
|
+
// Any new diff that is also not part of dirty diffs should be skipped, as the actual is already in desired state.
|
|
157
|
+
for (let i = newDiffs.length - 1; i >= 0; i--) {
|
|
158
|
+
const newDiff = newDiffs[i];
|
|
159
|
+
if (!dirtyDiffs.some((d) => d.node.getContext() === newDiff.node.getContext() &&
|
|
160
|
+
d.action === newDiff.action &&
|
|
161
|
+
d.field === newDiff.field &&
|
|
162
|
+
DiffUtility.isObjectDeepEquals(d.value, newDiff.value))) {
|
|
163
|
+
newDiffs.splice(i, 1);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// Ensure any new diffs are not operating on dirty resources.
|
|
167
|
+
this.resourceDataRepository.ensureDiffsNotOperatingOnDirtyResources(newDiffs);
|
|
168
|
+
// Skip processing dirty diffs that are already accounted for in new diffs.
|
|
169
|
+
for (let i = dirtyDiffs.length - 1; i >= 0; i--) {
|
|
170
|
+
const dirtyDiff = dirtyDiffs[i];
|
|
171
|
+
if (newDiffs.some((d) => d.node.getContext() === dirtyDiff.node.getContext() &&
|
|
172
|
+
d.action === dirtyDiff.action &&
|
|
173
|
+
d.field === dirtyDiff.field &&
|
|
174
|
+
DiffUtility.isObjectDeepEquals(d.value, dirtyDiff.value))) {
|
|
175
|
+
dirtyDiffs.splice(i, 1);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// Terraform resources have no resource actions - terraform owns their lifecycle. Their diffs
|
|
179
|
+
// remain part of the transaction as review artifacts, backed by a no-op action.
|
|
180
|
+
const resolveResourceActions = (d) => {
|
|
181
|
+
if (d.node instanceof ATerraformResource) {
|
|
182
|
+
return [new TerraformNoopResourceAction()];
|
|
183
|
+
}
|
|
184
|
+
return (this.resourceActions.find((a) => a.resourceClass === d.node.constructor)?.actions || []).filter((a) => a.filter(d));
|
|
185
|
+
};
|
|
186
|
+
// Generate diff on resources.
|
|
187
|
+
const resourceDiffs = newDiffs.map((d) => new DiffMetadata(d, resolveResourceActions(d)));
|
|
188
|
+
// Set apply order on resource diffs.
|
|
189
|
+
for (const diff of resourceDiffs) {
|
|
190
|
+
this.setApplyOrder(diff, resourceDiffs);
|
|
191
|
+
}
|
|
192
|
+
// Generate diff on dirty resources.
|
|
193
|
+
const dirtyResourceDiffs = dirtyDiffs.map((d) => new DiffMetadata(d, resolveResourceActions(d)));
|
|
194
|
+
// Set apply order on dirty resource diffs.
|
|
195
|
+
for (const diff of dirtyResourceDiffs) {
|
|
196
|
+
this.setApplyOrder(diff, dirtyResourceDiffs);
|
|
197
|
+
}
|
|
198
|
+
// Contribute the full desired resource graph to terraform. Deferred until after resource diffs
|
|
199
|
+
// are computed and ordered, so an author refusal (a throw in diff*) or an ordering/cycle error
|
|
200
|
+
// aborts generation before any HCL is built — diffs gate generation.
|
|
201
|
+
if (generateTerraform) {
|
|
202
|
+
await this.generateTerraform();
|
|
203
|
+
}
|
|
204
|
+
this.eventService.emit(new ResourceDiffsTransactionEvent(undefined, [[resourceDiffs], [dirtyResourceDiffs]]));
|
|
205
|
+
if (yieldResourceDiffs) {
|
|
206
|
+
yield [resourceDiffs, dirtyResourceDiffs];
|
|
207
|
+
}
|
|
208
|
+
// Apply resource diffs.
|
|
209
|
+
const resourceTransaction = await this.applyResources(resourceDiffs, { skipActualResourceUpdate });
|
|
210
|
+
// Apply dirty resource diffs.
|
|
211
|
+
const dirtyResourceTransaction = await this.applyResources(dirtyResourceDiffs, { skipActualResourceUpdate });
|
|
212
|
+
this.eventService.emit(new ResourceTransactionTransactionEvent(undefined, [resourceTransaction, dirtyResourceTransaction]));
|
|
213
|
+
if (yieldResourceTransaction) {
|
|
214
|
+
yield [...resourceTransaction, ...dirtyResourceTransaction];
|
|
215
|
+
}
|
|
216
|
+
return modelTransaction;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* The diffs {@link TransactionService.applyExternalResourceDeletes} runs: the deletion of every
|
|
220
|
+
* external resource in `resourceDiffs`. A caller that has to decide whether to run them at all
|
|
221
|
+
* names them through this rather than repeating the filter.
|
|
222
|
+
*
|
|
223
|
+
* @internal
|
|
224
|
+
*/
|
|
225
|
+
getExternalResourceDeletes(resourceDiffs) {
|
|
226
|
+
return resourceDiffs
|
|
227
|
+
.flat()
|
|
228
|
+
.filter((d) => d.action === DiffAction.DELETE &&
|
|
229
|
+
d.field === 'resourceId' &&
|
|
230
|
+
!(d.node instanceof ATerraformResource) &&
|
|
231
|
+
!d.applied);
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* @internal
|
|
235
|
+
*/
|
|
236
|
+
registerModelActions(forModel, actions) {
|
|
237
|
+
const modelActions = this.modelActions.find((a) => a.modelClass === forModel);
|
|
238
|
+
if (!modelActions) {
|
|
239
|
+
this.modelActions.push({ actions: actions, modelClass: forModel });
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
for (const action of actions) {
|
|
243
|
+
if (modelActions.actions.find((a) => a.constructor.name === action.constructor.name)) {
|
|
244
|
+
throw new Error(`Action "${action.constructor.name}" already registered for model "${forModel.name}"!`);
|
|
245
|
+
}
|
|
246
|
+
modelActions.actions.push(action);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* @internal
|
|
252
|
+
*/
|
|
253
|
+
registerOverlayActions(forOverlay, actions) {
|
|
254
|
+
const overlayActions = this.overlayActions.find((a) => a.overlayClass === forOverlay);
|
|
255
|
+
if (!overlayActions) {
|
|
256
|
+
this.overlayActions.push({ actions: actions, overlayClass: forOverlay });
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
for (const action of actions) {
|
|
260
|
+
if (overlayActions.actions.find((a) => a.constructor.name === action.constructor.name)) {
|
|
261
|
+
throw new Error(`Action "${action.constructor.name}" already registered for overlay "${forOverlay.name}"!`);
|
|
262
|
+
}
|
|
263
|
+
overlayActions.actions.push(action);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* @internal
|
|
269
|
+
*/
|
|
270
|
+
registerResourceActions(forResource, actions) {
|
|
271
|
+
if (forResource.prototype instanceof ATerraformResource) {
|
|
272
|
+
throw new TransactionError(`Cannot register resource actions for terraform resource "${forResource.name}"! ` +
|
|
273
|
+
'Terraform resources are managed by terraform via toHCL().');
|
|
274
|
+
}
|
|
275
|
+
const resourceActions = this.resourceActions.find((a) => a.resourceClass === forResource);
|
|
276
|
+
if (!resourceActions) {
|
|
277
|
+
this.resourceActions.push({ actions: actions, resourceClass: forResource });
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
for (const action of actions) {
|
|
281
|
+
if (resourceActions.actions.find((a) => a.constructor.name === action.constructor.name)) {
|
|
282
|
+
throw new Error(`Action "${action.constructor.name}" already registered for resource "${forResource.name}"!`);
|
|
283
|
+
}
|
|
284
|
+
resourceActions.actions.push(action);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* @internal
|
|
290
|
+
*/
|
|
291
|
+
unregisterModelActions(forModel) {
|
|
292
|
+
const modelActions = this.modelActions.find((a) => a.modelClass === forModel);
|
|
293
|
+
if (!modelActions) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
modelActions.actions = [];
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* @internal
|
|
300
|
+
*/
|
|
301
|
+
unregisterOverlayActions(forOverlay) {
|
|
302
|
+
const overlayActions = this.overlayActions.find((a) => a.overlayClass === forOverlay);
|
|
303
|
+
if (!overlayActions) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
overlayActions.actions = [];
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* @internal
|
|
310
|
+
*/
|
|
42
311
|
async applyModels(diffs) {
|
|
43
312
|
const transaction = [];
|
|
44
313
|
let currentApplyOrder = 0;
|
|
@@ -56,12 +325,11 @@ export class TransactionService {
|
|
|
56
325
|
const diffToProcess = duplicateDiffs[0].diff;
|
|
57
326
|
for (const a of diff.actions) {
|
|
58
327
|
// Find moduleId.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
moduleId = this.inputService.getModuleIdFromModel(diff.node);
|
|
328
|
+
const moduleId = diff.node instanceof AOverlay
|
|
329
|
+
? this.inputService.getModuleIdFromOverlay(diff.node)
|
|
330
|
+
: this.inputService.getModuleIdFromModel(diff.node);
|
|
331
|
+
if (moduleId === undefined) {
|
|
332
|
+
throw new TransactionError(`Node "${diff.node.getContext()}" is not associated with any module!`);
|
|
65
333
|
}
|
|
66
334
|
// Resolve all keys of the module - inputs keys and nodes generated by the module keys.
|
|
67
335
|
const inputs = { inputs: {}, metadata: {}, models: {}, overlays: {}, resources: {} };
|
|
@@ -96,12 +364,11 @@ export class TransactionService {
|
|
|
96
364
|
// Apply all actions on the diff, then update diff metadata with inputs and outputs.
|
|
97
365
|
const outputs = await a.handle(diffToProcess, inputs, {});
|
|
98
366
|
for (const resource of Object.values(outputs)) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
moduleId = this.inputService.getModuleIdFromModel(diffToProcess.node);
|
|
367
|
+
const moduleId = diffToProcess.node instanceof AOverlay
|
|
368
|
+
? this.inputService.getModuleIdFromOverlay(diffToProcess.node)
|
|
369
|
+
: this.inputService.getModuleIdFromModel(diffToProcess.node);
|
|
370
|
+
if (moduleId === undefined) {
|
|
371
|
+
throw new TransactionError(`Node "${diffToProcess.node.getContext()}" is not associated with any module!`);
|
|
105
372
|
}
|
|
106
373
|
// Set resource tags.
|
|
107
374
|
const resourceTags = this.inputService.resolve(`${moduleId}.tag.${resource.getContext()}`);
|
|
@@ -142,34 +409,10 @@ export class TransactionService {
|
|
|
142
409
|
}
|
|
143
410
|
return transaction;
|
|
144
411
|
}
|
|
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 } = {}) {
|
|
412
|
+
/**
|
|
413
|
+
* @internal
|
|
414
|
+
*/
|
|
415
|
+
async applyResources(diffs, { skipActualResourceUpdate = false } = {}) {
|
|
173
416
|
const transaction = [];
|
|
174
417
|
const validationErrors = [];
|
|
175
418
|
let currentApplyOrder = 0;
|
|
@@ -187,47 +430,66 @@ export class TransactionService {
|
|
|
187
430
|
const diffToProcess = duplicateDiffs[0].diff;
|
|
188
431
|
for (const a of diff.actions) {
|
|
189
432
|
this.eventService.emit(new ResourceActionInitiatedTransactionEvent(a.constructor.name, diff));
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
433
|
+
// Captured before `handle()` mutates the resource, for the summary event below.
|
|
434
|
+
const preHandleSynth = skipActualResourceUpdate
|
|
435
|
+
? this.resourceDataRepository.getNewResourceByContext(diffToProcess.node.getContext())?.synth()
|
|
436
|
+
: undefined;
|
|
437
|
+
let actionTimeoutId;
|
|
438
|
+
try {
|
|
439
|
+
await Promise.race([
|
|
440
|
+
new Promise(async (resolve, reject) => {
|
|
441
|
+
try {
|
|
442
|
+
const response = await a.handle(diffToProcess);
|
|
443
|
+
this.resourceDataRepository
|
|
444
|
+
.getNewResourceByContext(diffToProcess.node.getContext())
|
|
445
|
+
?.setResponse(response);
|
|
446
|
+
resolve();
|
|
447
|
+
}
|
|
448
|
+
catch (error) {
|
|
449
|
+
if (diffToProcess.action === DiffAction.VALIDATE) {
|
|
450
|
+
validationErrors.push(error);
|
|
205
451
|
resolve();
|
|
206
452
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
validationErrors.push(error);
|
|
210
|
-
resolve();
|
|
211
|
-
}
|
|
212
|
-
else {
|
|
213
|
-
reject(new ResourceActionExceptionTransactionError(error.message, error, diffToProcess, a.constructor.name));
|
|
214
|
-
}
|
|
453
|
+
else {
|
|
454
|
+
reject(new ResourceActionExceptionTransactionError(error.message, error, diffToProcess, a.constructor.name));
|
|
215
455
|
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
456
|
+
}
|
|
457
|
+
}),
|
|
458
|
+
new Promise((_resolve, reject) => (actionTimeoutId = setTimeout(() => {
|
|
459
|
+
reject(new ResourceActionTimeoutTransactionError(`Resource action ${a.constructor.name} timed out after ${a.actionTimeoutInMs || 90000}ms!`, diffToProcess, a.constructor.name));
|
|
460
|
+
}, a.actionTimeoutInMs || 90000))),
|
|
461
|
+
]);
|
|
462
|
+
}
|
|
463
|
+
finally {
|
|
464
|
+
if (actionTimeoutId) {
|
|
465
|
+
clearTimeout(actionTimeoutId);
|
|
226
466
|
}
|
|
227
467
|
}
|
|
228
|
-
|
|
468
|
+
if (skipActualResourceUpdate) {
|
|
469
|
+
// Stateless invocation (run-action): the actual resource graph is not maintained;
|
|
470
|
+
// terraform state is the source of truth until the next commit. The summary is therefore
|
|
471
|
+
// derived from the new-graph resource either side of `handle()`.
|
|
472
|
+
const postHandleSynth = diffToProcess.action === DiffAction.DELETE && diffToProcess.field === 'resourceId'
|
|
473
|
+
? {}
|
|
474
|
+
: this.resourceDataRepository.getNewResourceByContext(diffToProcess.node.getContext())?.synth();
|
|
475
|
+
this.eventService.emit(new ResourceActionSummaryTransactionEvent(a.constructor.name, {
|
|
476
|
+
diff,
|
|
477
|
+
values: { current: postHandleSynth ?? {}, previous: preHandleSynth ?? {} },
|
|
478
|
+
}));
|
|
479
|
+
this.eventService.emit(new ResourceActionCompletedTransactionEvent(a.constructor.name, diff));
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
// De-reference from actual resources. The lookup can miss when a diff references a resource
|
|
483
|
+
// this transaction has not applied yet, so name what is missing rather than letting
|
|
484
|
+
// `undefined` surface as a TypeError several frames away.
|
|
229
485
|
const deReferenceResource = async (context) => {
|
|
230
|
-
|
|
486
|
+
const referencedResource = this.resourceDataRepository.getActualResourceByContext(context);
|
|
487
|
+
if (!referencedResource) {
|
|
488
|
+
throw new TransactionError(`Cannot apply "${diffToProcess.action}" on field "${String(diffToProcess.field)}" of resource ` +
|
|
489
|
+
`"${diffToProcess.node.getContext()}": it references "${context}", which is not in the actual ` +
|
|
490
|
+
'resource graph yet. The referenced resource is applied later in this transaction, or not at all.');
|
|
491
|
+
}
|
|
492
|
+
return referencedResource;
|
|
231
493
|
};
|
|
232
494
|
// Incrementally apply diff inverse to the respective actual resource.
|
|
233
495
|
let actualResource = this.resourceDataRepository.getActualResourceByContext(diffToProcess.node.getContext());
|
|
@@ -274,15 +536,52 @@ export class TransactionService {
|
|
|
274
536
|
}
|
|
275
537
|
return transaction;
|
|
276
538
|
}
|
|
539
|
+
/**
|
|
540
|
+
* Contributes every resource in the new (desired) resource graph to the shared
|
|
541
|
+
* {@link TerraformService}: terraform resources via `toHCL()`, external resources via
|
|
542
|
+
* `addOctoTerraformExternalResource()`. Always processes the full desired state — terraform
|
|
543
|
+
* computes the actual create/update/delete against its own state.
|
|
544
|
+
*
|
|
545
|
+
* Order does not matter: terraform resources reference each other through deferred refs, and
|
|
546
|
+
* external resources defer their parent-input wiring — both are resolved later, once every
|
|
547
|
+
* resource is registered (see {@link TerraformService}'s wiring phase).
|
|
548
|
+
*/
|
|
549
|
+
async generateTerraform() {
|
|
550
|
+
const resources = this.resourceDataRepository.getNewResourcesByProperties().filter((r) => !r.isMarkedDeleted());
|
|
551
|
+
for (const resource of resources) {
|
|
552
|
+
// Every resource is produced by a model action inside a module, so it is always module-bound.
|
|
553
|
+
// A missing attribution means the resource was registered out-of-band — a hard error.
|
|
554
|
+
const moduleId = this.inputService.getModuleIdFromResource(resource);
|
|
555
|
+
if (moduleId === undefined) {
|
|
556
|
+
throw new TransactionError(`Resource "${resource.resourceId}" is not associated with any module!`);
|
|
557
|
+
}
|
|
558
|
+
const scope = this.terraformService.scope(moduleId);
|
|
559
|
+
if (resource instanceof ATerraformResource) {
|
|
560
|
+
await resource.toHCL(scope);
|
|
561
|
+
}
|
|
562
|
+
else {
|
|
563
|
+
scope.addOctoTerraformExternalResource(resource);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* @internal
|
|
569
|
+
*/
|
|
277
570
|
getDiffsOfSameNode(diff, diffs, diffAction) {
|
|
278
571
|
return diffs.filter((d) => d.node.getContext() === diff.node.getContext() && d.action === diffAction);
|
|
279
572
|
}
|
|
573
|
+
/**
|
|
574
|
+
* @internal
|
|
575
|
+
*/
|
|
280
576
|
getDuplicateDiffs(diff, diffs) {
|
|
281
577
|
return diffs.filter((d) => d.node.getContext() === diff.node.getContext() &&
|
|
282
578
|
d.field === diff.field &&
|
|
283
579
|
d.action === diff.action &&
|
|
284
580
|
DiffUtility.isObjectDeepEquals(d.value, diff.value));
|
|
285
581
|
}
|
|
582
|
+
/**
|
|
583
|
+
* @internal
|
|
584
|
+
*/
|
|
286
585
|
getMatchingDiffs(diff, diffs) {
|
|
287
586
|
return diffs.filter((d) => d.node.getContext() === diff.node.getContext() &&
|
|
288
587
|
d.field === diff.field &&
|
|
@@ -324,8 +623,9 @@ export class TransactionService {
|
|
|
324
623
|
}
|
|
325
624
|
}
|
|
326
625
|
// Get diffs of same node.
|
|
327
|
-
// Process ADD before UPDATE, and UPDATE before DELETE.
|
|
328
|
-
|
|
626
|
+
// Process ADD before UPDATE/REPLACE, and UPDATE/REPLACE before DELETE. A REPLACE recreates the
|
|
627
|
+
// resource in place (same context), so for ordering it behaves like an UPDATE.
|
|
628
|
+
if (diff.action === DiffAction.UPDATE || diff.action === DiffAction.REPLACE) {
|
|
329
629
|
const diffsOfSameNodeWithAddAction = this.getDiffsOfSameNode(diff, diffs, DiffAction.ADD);
|
|
330
630
|
for (const sameNodeDiffWithAddAction of diffsOfSameNodeWithAddAction) {
|
|
331
631
|
this.setApplyOrder(sameNodeDiffWithAddAction, diffs, [...seen, diff]);
|
|
@@ -341,154 +641,6 @@ export class TransactionService {
|
|
|
341
641
|
}
|
|
342
642
|
diff.applyOrder = Math.max(...dependencyApplyOrders) + 1;
|
|
343
643
|
}
|
|
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
644
|
}
|
|
493
645
|
__decorate([
|
|
494
646
|
EventSource(ModelActionRegistrationEvent),
|
|
@@ -512,18 +664,21 @@ __decorate([
|
|
|
512
664
|
* @internal
|
|
513
665
|
*/
|
|
514
666
|
let TransactionServiceFactory = class TransactionServiceFactory {
|
|
667
|
+
/**
|
|
668
|
+
* @internal
|
|
669
|
+
*/
|
|
515
670
|
static async create() {
|
|
516
671
|
const container = Container.getInstance();
|
|
517
|
-
const [
|
|
518
|
-
container.get(CaptureService),
|
|
672
|
+
const [eventService, inputService, moduleContainer, overlayDataRepository, resourceDataRepository, terraformService,] = await Promise.all([
|
|
519
673
|
container.get(EventService),
|
|
520
674
|
container.get(InputService),
|
|
521
675
|
container.get(ModuleContainer),
|
|
522
676
|
container.get(OverlayDataRepository),
|
|
523
677
|
container.get(ResourceDataRepository),
|
|
678
|
+
container.get(TerraformService),
|
|
524
679
|
]);
|
|
525
680
|
if (!this.instance) {
|
|
526
|
-
this.instance = new TransactionService(
|
|
681
|
+
this.instance = new TransactionService(eventService, inputService, moduleContainer, overlayDataRepository, resourceDataRepository, terraformService);
|
|
527
682
|
}
|
|
528
683
|
return this.instance;
|
|
529
684
|
}
|