@intentius/chant-lexicon-terraform 0.124.51 → 0.124.54
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/README.md +7 -6
- package/dist/__fixtures__/pr-loop/estate.component.d.ts +7 -0
- package/dist/__fixtures__/pr-loop/estate.component.d.ts.map +1 -0
- package/dist/__fixtures__/pr-loop/estate.component.js +7 -0
- package/dist/__fixtures__/report/generate.d.ts +26 -0
- package/dist/__fixtures__/report/generate.d.ts.map +1 -0
- package/dist/__fixtures__/report/generate.js +58 -0
- package/dist/applied-state.d.ts +27 -0
- package/dist/applied-state.d.ts.map +1 -0
- package/dist/applied-state.js +44 -0
- package/dist/backend-config.d.ts +53 -0
- package/dist/backend-config.d.ts.map +1 -0
- package/dist/backend-config.js +136 -0
- package/dist/behaviour/kinds.d.ts.map +1 -1
- package/dist/behaviour/kinds.js +4 -0
- package/dist/behaviour/predict.d.ts.map +1 -1
- package/dist/behaviour/predict.js +6 -2
- package/dist/behaviour/reference-catalog.d.ts +16 -5
- package/dist/behaviour/reference-catalog.d.ts.map +1 -1
- package/dist/behaviour/reference-catalog.js +16 -5
- package/dist/behaviour/request.d.ts.map +1 -1
- package/dist/behaviour/request.js +3 -2
- package/dist/changed-roots.d.ts +6 -4
- package/dist/changed-roots.d.ts.map +1 -1
- package/dist/changed-roots.js +14 -18
- package/dist/commands.d.ts +5 -0
- package/dist/commands.d.ts.map +1 -1
- package/dist/commands.js +87 -4
- package/dist/components/capability-plugin.d.ts +65 -3
- package/dist/components/capability-plugin.d.ts.map +1 -1
- package/dist/components/capability-plugin.js +197 -13
- package/dist/components/compose-group.d.ts +145 -0
- package/dist/components/compose-group.d.ts.map +1 -0
- package/dist/components/compose-group.js +407 -0
- package/dist/components/placeholder-preview.d.ts +78 -0
- package/dist/components/placeholder-preview.d.ts.map +1 -0
- package/dist/components/placeholder-preview.js +254 -0
- package/dist/compose/apply-events.d.ts +46 -0
- package/dist/compose/apply-events.d.ts.map +1 -0
- package/dist/compose/apply-events.js +77 -0
- package/dist/compose/estate.d.ts +194 -0
- package/dist/compose/estate.d.ts.map +1 -0
- package/dist/compose/estate.js +363 -0
- package/dist/compose/hcl.d.ts +99 -0
- package/dist/compose/hcl.d.ts.map +1 -0
- package/dist/compose/hcl.js +302 -0
- package/dist/compose/module-address.d.ts +24 -0
- package/dist/compose/module-address.d.ts.map +1 -0
- package/dist/compose/module-address.js +43 -0
- package/dist/compose/root.d.ts +127 -0
- package/dist/compose/root.d.ts.map +1 -0
- package/dist/compose/root.js +586 -0
- package/dist/compose/run.d.ts +183 -0
- package/dist/compose/run.d.ts.map +1 -0
- package/dist/compose/run.js +447 -0
- package/dist/compose/slice.d.ts +102 -0
- package/dist/compose/slice.d.ts.map +1 -0
- package/dist/compose/slice.js +249 -0
- package/dist/compose/state-address.d.ts +28 -0
- package/dist/compose/state-address.d.ts.map +1 -0
- package/dist/compose/state-address.js +154 -0
- package/dist/compose/state-merge.d.ts +52 -0
- package/dist/compose/state-merge.d.ts.map +1 -0
- package/dist/compose/state-merge.js +135 -0
- package/dist/compose.d.ts +179 -0
- package/dist/compose.d.ts.map +1 -0
- package/dist/compose.js +482 -0
- package/dist/composites/terraform-apply-op.d.ts.map +1 -1
- package/dist/composites/terraform-apply-op.js +7 -3
- package/dist/config.d.ts +59 -13
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +43 -9
- package/dist/describe-resources.d.ts +74 -12
- package/dist/describe-resources.d.ts.map +1 -1
- package/dist/describe-resources.js +374 -66
- package/dist/drift-read.d.ts +33 -0
- package/dist/drift-read.d.ts.map +1 -0
- package/dist/drift-read.js +243 -0
- package/dist/drift-record.d.ts +147 -0
- package/dist/drift-record.d.ts.map +1 -0
- package/dist/drift-record.js +383 -0
- package/dist/hcl/edges.d.ts.map +1 -1
- package/dist/hcl/edges.js +24 -1
- package/dist/hcl/files.d.ts +61 -0
- package/dist/hcl/files.d.ts.map +1 -0
- package/dist/hcl/files.js +221 -0
- package/dist/hcl/instances.d.ts +82 -0
- package/dist/hcl/instances.d.ts.map +1 -0
- package/dist/hcl/instances.js +390 -0
- package/dist/hcl/parse.d.ts +81 -9
- package/dist/hcl/parse.d.ts.map +1 -1
- package/dist/hcl/parse.js +114 -16
- package/dist/hcl/roots.d.ts.map +1 -1
- package/dist/hcl/roots.js +5 -0
- package/dist/hcl/suppressions.d.ts.map +1 -1
- package/dist/hcl/suppressions.js +8 -2
- package/dist/index.d.ts +11 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -2
- package/dist/integrity.json +3 -3
- package/dist/live-plan-reasons.d.ts +21 -0
- package/dist/live-plan-reasons.d.ts.map +1 -0
- package/dist/live-plan-reasons.js +47 -0
- package/dist/lsp/option-keys.d.ts.map +1 -1
- package/dist/lsp/option-keys.js +2 -0
- package/dist/manifest.json +1 -1
- package/dist/op/activities/index.d.ts +6 -2
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/index.js +5 -1
- package/dist/op/activities/live-detect.d.ts +4 -3
- package/dist/op/activities/live-detect.d.ts.map +1 -1
- package/dist/op/activities/live-detect.js +57 -32
- package/dist/op/activities/pin-rollout.d.ts +2 -0
- package/dist/op/activities/pin-rollout.d.ts.map +1 -1
- package/dist/op/activities/pin-rollout.js +1 -0
- package/dist/op/activities/terraform.d.ts +153 -5
- package/dist/op/activities/terraform.d.ts.map +1 -1
- package/dist/op/activities/terraform.js +205 -20
- package/dist/op/activity-contracts.d.ts +27 -0
- package/dist/op/activity-contracts.d.ts.map +1 -1
- package/dist/op/activity-contracts.js +19 -1
- package/dist/op/builders.d.ts +7 -1
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/builders.js +9 -0
- package/dist/pin/edit.d.ts.map +1 -1
- package/dist/pin/edit.js +35 -0
- package/dist/pin/rollout.d.ts +2 -0
- package/dist/pin/rollout.d.ts.map +1 -1
- package/dist/pin/rollout.js +14 -11
- package/dist/plan-shape.d.ts +30 -0
- package/dist/plan-shape.d.ts.map +1 -0
- package/dist/plan-shape.js +125 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +32 -18
- package/dist/report.d.ts +216 -0
- package/dist/report.d.ts.map +1 -0
- package/dist/report.js +666 -0
- package/dist/skills/chant-terraform.md +1 -1
- package/dist/state/azure-blob.d.ts +68 -0
- package/dist/state/azure-blob.d.ts.map +1 -0
- package/dist/state/azure-blob.js +201 -0
- package/dist/state/backend.d.ts +158 -0
- package/dist/state/backend.d.ts.map +1 -0
- package/dist/state/backend.js +406 -0
- package/dist/state/exec.d.ts +39 -0
- package/dist/state/exec.d.ts.map +1 -0
- package/dist/state/exec.js +80 -0
- package/dist/state/gcs.d.ts +62 -0
- package/dist/state/gcs.d.ts.map +1 -0
- package/dist/state/gcs.js +224 -0
- package/dist/state/index.d.ts +15 -0
- package/dist/state/index.d.ts.map +1 -0
- package/dist/state/index.js +14 -0
- package/dist/state/locks.d.ts +105 -0
- package/dist/state/locks.d.ts.map +1 -0
- package/dist/state/locks.js +130 -0
- package/dist/state/object-store.d.ts +37 -0
- package/dist/state/object-store.d.ts.map +1 -0
- package/dist/state/object-store.js +20 -0
- package/dist/state/root-states.d.ts +131 -0
- package/dist/state/root-states.d.ts.map +1 -0
- package/dist/state/root-states.js +241 -0
- package/dist/state/s3.d.ts +68 -0
- package/dist/state/s3.d.ts.map +1 -0
- package/dist/state/s3.js +170 -0
- package/dist/state/store.d.ts +51 -0
- package/dist/state/store.d.ts.map +1 -0
- package/dist/state/store.js +185 -0
- package/dist/terragrunt/affected.d.ts.map +1 -1
- package/dist/terragrunt/affected.js +2 -1
- package/package.json +7 -3
- package/src/__fixtures__/ACCEPTANCE.md +69 -7
- package/src/__fixtures__/compose/apply-failed-in-app.jsonl +12 -0
- package/src/__fixtures__/compose/state-app.json +66 -0
- package/src/__fixtures__/compose/state-edge.json +40 -0
- package/src/__fixtures__/compose/state-net.json +58 -0
- package/src/__fixtures__/compose/three-app.plan.json +184 -0
- package/src/__fixtures__/compose/three-composed.plan.json +509 -0
- package/src/__fixtures__/compose/three-edge.plan.json +144 -0
- package/src/__fixtures__/compose/three-net.plan.json +174 -0
- package/src/__fixtures__/compose/unknown-composed.plan.json +228 -0
- package/src/__fixtures__/compose/working-after-apply.json +145 -0
- package/src/__fixtures__/config-blocks/app/main.tf +61 -0
- package/src/__fixtures__/config-blocks/app/modules/network/main.tf +4 -0
- package/src/__fixtures__/config-blocks/app-json/main.tf.json +39 -0
- package/src/__fixtures__/config-files/shadow/main.tf +3 -0
- package/src/__fixtures__/config-files/shadow/main.tofu +3 -0
- package/src/__fixtures__/config-files/shadow/other.tf +3 -0
- package/src/__fixtures__/config-files/tf/main.tf +11 -0
- package/src/__fixtures__/config-files/tf-json/main.tf.json +18 -0
- package/src/__fixtures__/config-files/tofu/main.tofu +11 -0
- package/src/__fixtures__/config-files/tofu-json/main.tofu.json +18 -0
- package/src/__fixtures__/config-files/tofu-only/main.tofu +3 -0
- package/src/__fixtures__/drift-record/README.md +11 -0
- package/src/__fixtures__/drift-record/live-plan-sections.json +27 -0
- package/src/__fixtures__/instance-forms/app/main.tf +110 -0
- package/src/__fixtures__/instance-forms/app/modules/svc/main.tf +3 -0
- package/src/__fixtures__/instance-vars/app/main.tf +52 -0
- package/src/__fixtures__/instance-vars/app/modules/app/main.tf +12 -0
- package/src/__fixtures__/instance-vars/app/modules/edge/main.tf +3 -0
- package/src/__fixtures__/instance-vars/app/terraform.tfvars +1 -0
- package/src/__fixtures__/live-module-keyed/live-graph.json +259 -0
- package/src/__fixtures__/live-module-keyed/live-plan.json +87 -0
- package/src/__fixtures__/live-module-keyed/root/estate.chdf.hcl +1 -0
- package/src/__fixtures__/live-module-keyed/root/main.tf +21 -0
- package/src/__fixtures__/live-module-keyed/root/modules/app/main.tf +17 -0
- package/src/__fixtures__/live-module-keyed/root/modules/edge/main.tf +7 -0
- package/src/__fixtures__/live-module-keyed/root/versions.tf +30 -0
- package/src/__fixtures__/live-module-keyed/show-state.json +146 -0
- package/src/__fixtures__/pr-loop/README.md +50 -0
- package/src/__fixtures__/pr-loop/chant.config.json +13 -0
- package/src/__fixtures__/pr-loop/estate.component.ts +10 -0
- package/src/__fixtures__/pr-loop/roots/a/main.tf +11 -0
- package/src/__fixtures__/pr-loop/roots/app/main.tf +19 -0
- package/src/__fixtures__/pr-loop/roots/b/main.tf +7 -0
- package/src/__fixtures__/pr-loop/roots/dns/main.tf +3 -0
- package/src/__fixtures__/pr-loop/roots/net/main.tf +7 -0
- package/src/__fixtures__/report/applied.show.json +51 -0
- package/src/__fixtures__/report/generate.ts +63 -0
- package/src/__fixtures__/report/plan.show.json +200 -0
- package/src/__fixtures__/report/tf-apply/change-set.json +170 -0
- package/src/__fixtures__/report/tf-apply/report.json +338 -0
- package/src/__fixtures__/report/tf-plan/change-set.json +170 -0
- package/src/__fixtures__/report/tf-plan/report.json +320 -0
- package/src/__fixtures__/three-cloud-azure.graph.json +290 -0
- package/src/applied-state.ts +52 -0
- package/src/backend-config.test.ts +105 -0
- package/src/backend-config.ts +133 -0
- package/src/behaviour/kinds.ts +4 -0
- package/src/behaviour/predict.ts +5 -2
- package/src/behaviour/reference-catalog.plugin.test.ts +65 -0
- package/src/behaviour/reference-catalog.ts +16 -5
- package/src/behaviour/request.ts +3 -2
- package/src/change-set-instances.test.ts +98 -0
- package/src/changed-roots.ts +14 -16
- package/src/commands.ts +84 -4
- package/src/components/capability-plugin.test.ts +172 -5
- package/src/components/capability-plugin.ts +212 -11
- package/src/components/change-set-graph.e2e.test.ts +11 -5
- package/src/components/compose-group.test.ts +486 -0
- package/src/components/compose-group.ts +489 -0
- package/src/components/demo-composed-roots.e2e.test.ts +440 -0
- package/src/components/member-outputs.test.ts +76 -0
- package/src/components/placeholder-preview.test.ts +147 -0
- package/src/components/placeholder-preview.ts +257 -0
- package/src/components/pr-loop-compose.tofu.e2e.test.ts +363 -0
- package/src/components/pr-loop.tofu.test.ts +112 -56
- package/src/compose/apply-events.test.ts +62 -0
- package/src/compose/apply-events.ts +100 -0
- package/src/compose/estate.choudoufu.e2e.test.ts +112 -0
- package/src/compose/estate.test.ts +421 -0
- package/src/compose/estate.ts +443 -0
- package/src/compose/hcl.ts +311 -0
- package/src/compose/module-address.ts +49 -0
- package/src/compose/root.test.ts +416 -0
- package/src/compose/root.ts +592 -0
- package/src/compose/run.test.ts +440 -0
- package/src/compose/run.tofu.test.ts +176 -0
- package/src/compose/run.ts +527 -0
- package/src/compose/slice.test.ts +184 -0
- package/src/compose/slice.ts +290 -0
- package/src/compose/state-address.ts +157 -0
- package/src/compose/state-merge.test.ts +131 -0
- package/src/compose/state-merge.ts +148 -0
- package/src/compose.test.ts +248 -0
- package/src/compose.ts +627 -0
- package/src/composites/terraform-apply-op.test.ts +9 -0
- package/src/composites/terraform-apply-op.ts +7 -3
- package/src/composites/terraform-pin-rollout-op.test.ts +2 -2
- package/src/config-file-listers.test.ts +265 -0
- package/src/config.ts +44 -9
- package/src/describe-resources.engine-facts.test.ts +412 -0
- package/src/describe-resources.live-module-keyed.test.ts +290 -0
- package/src/describe-resources.ts +416 -70
- package/src/drift-read.ts +284 -0
- package/src/drift-record.test.ts +223 -0
- package/src/drift-record.ts +455 -0
- package/src/hcl/config-blocks.test.ts +174 -0
- package/src/hcl/edges.ts +21 -1
- package/src/hcl/files.ts +234 -0
- package/src/hcl/instance-forms.test.ts +94 -0
- package/src/hcl/instances.test.ts +137 -0
- package/src/hcl/instances.ts +422 -0
- package/src/hcl/parse.test.ts +139 -1
- package/src/hcl/parse.ts +149 -16
- package/src/hcl/roots.ts +6 -0
- package/src/hcl/suppressions.ts +8 -2
- package/src/index.ts +76 -0
- package/src/live-plan-reasons.ts +54 -0
- package/src/lsp/completions.test.ts +1 -1
- package/src/lsp/option-keys.ts +2 -0
- package/src/op/activities/choudoufu-plan-set-op.test.ts +111 -0
- package/src/op/activities/index.ts +9 -0
- package/src/op/activities/live-detect.ts +55 -26
- package/src/op/activities/pin-rollout.ts +3 -0
- package/src/op/activities/registry.test.ts +2 -1
- package/src/op/activities/terraform.test.ts +55 -0
- package/src/op/activities/terraform.ts +266 -22
- package/src/op/activity-contracts.test.ts +1 -0
- package/src/op/activity-contracts.ts +24 -1
- package/src/op/builders.ts +14 -0
- package/src/pin/bundle.test.ts +2 -0
- package/src/pin/edit.ts +30 -0
- package/src/pin/rollout.ts +16 -11
- package/src/plan-shape.test.ts +148 -0
- package/src/plan-shape.ts +128 -0
- package/src/plugin.ts +33 -19
- package/src/report.test.ts +423 -0
- package/src/report.ts +752 -0
- package/src/signal-only-root.test.ts +72 -0
- package/src/skills/chant-terraform.md +1 -1
- package/src/state/azure-blob.ts +223 -0
- package/src/state/backend-verdict.test.ts +187 -0
- package/src/state/backend.test.ts +127 -0
- package/src/state/backend.ts +488 -0
- package/src/state/exec.ts +111 -0
- package/src/state/gcs.ts +254 -0
- package/src/state/index.ts +15 -0
- package/src/state/locks.test.ts +84 -0
- package/src/state/locks.ts +201 -0
- package/src/state/object-store.ts +35 -0
- package/src/state/root-states-floci.e2e.test.ts +66 -0
- package/src/state/root-states.test.ts +462 -0
- package/src/state/root-states.ts +309 -0
- package/src/state/s3.ts +193 -0
- package/src/state/store.ts +225 -0
- package/src/terragrunt/affected.ts +2 -1
- package/src/workspace-kinds.test.ts +4 -17
- package/workspace-kinds.json +1 -24
- package/dist/workspace-member.d.ts +0 -30
- package/dist/workspace-member.d.ts.map +0 -1
- package/dist/workspace-member.js +0 -91
- package/schemas/pulumi.schema.json +0 -34
- package/src/testdata/pulumi-member/Pulumi.yaml +0 -2
- package/src/testdata/pulumi-member/stack-export.json +0 -53
- package/src/testdata/pulumi-unexported/Pulumi.yaml +0 -2
- package/src/workspace-member.test.ts +0 -89
- package/src/workspace-member.ts +0 -126
|
@@ -38,9 +38,27 @@
|
|
|
38
38
|
* when both roots are in the change.
|
|
39
39
|
*
|
|
40
40
|
* A root whose `vars` read an output not known yet (`DeployContext.unresolvedInputs`)
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
41
|
+
* cannot be planned as it stands: terraform takes no unknown variable. It is
|
|
42
|
+
* previewed instead (#4463, `./placeholder-preview.ts`): planned with a
|
|
43
|
+
* stand-in per variable that holds no value, typed from the root's
|
|
44
|
+
* `variable` block, with every attribute and output a stand-in drives marked
|
|
45
|
+
* unknown after apply. The member lists the variables under `placeholders`,
|
|
46
|
+
* and its artifact is the preview's shape, never a plan file. The apply plans
|
|
47
|
+
* the root again once the upstream applied, and core covers that round with
|
|
48
|
+
* the first approval when its shape is within the preview's; a `run` handed
|
|
49
|
+
* the shape itself holds the fresh plan to it. When no preview can be made
|
|
50
|
+
* (an undeclared variable, a `count` or `for_each` over a stand-in, a
|
|
51
|
+
* placeholder plan that fails) the member stays a hole naming the roots it
|
|
52
|
+
* waits on, and the hole's reason says why there is no preview.
|
|
53
|
+
*
|
|
54
|
+
* ## The plan's shape (#4436)
|
|
55
|
+
*
|
|
56
|
+
* `plan` also answers `shape`, `terraformPlanShape` over `show -json`: each
|
|
57
|
+
* resource the plan changes with its action, type, replacement, the
|
|
58
|
+
* attributes that differ and the ones unknown after apply. Core's
|
|
59
|
+
* `coveredRound` compares it with the shape of a later plan of the root. A
|
|
60
|
+
* previewed root's shape lists the upstreams it read as unknown inputs; a
|
|
61
|
+
* root that stays a hole has no plan, so no shape, and stays uncovered.
|
|
44
62
|
*
|
|
45
63
|
* ## choudoufu roots in a gated wave (#3459)
|
|
46
64
|
*
|
|
@@ -63,13 +81,44 @@
|
|
|
63
81
|
*
|
|
64
82
|
* chant's waves stay the order. Each root is its own one-root set, so
|
|
65
83
|
* choudoufu's wave over it is always wave 1.
|
|
84
|
+
*
|
|
85
|
+
* ## Composed runs (#4447)
|
|
86
|
+
*
|
|
87
|
+
* With `terraform.compose.enabled`, a group core forms of `terraform-apply`
|
|
88
|
+
* components that read each other (ws-107) plans as one plan and applies as
|
|
89
|
+
* one apply of a composed root, and a member's read of another member's new
|
|
90
|
+
* output is not a hole: the apply passes the value on. `groupReads`,
|
|
91
|
+
* `groupFallback`, `planGroup` and `runGroup` are in `./compose-group.ts`.
|
|
92
|
+
*
|
|
93
|
+
* ## A choudoufu member's outputs, for another member (#4455)
|
|
94
|
+
*
|
|
95
|
+
* The plugin also contributes {@link choudoufuOutputSource} to core's
|
|
96
|
+
* `member-outputs` verb (`CapabilityPlugin.engineOutputSources`), which is
|
|
97
|
+
* how a chant member's parameters read a choudoufu member of the same
|
|
98
|
+
* workspace: `choudoufu output -json` in the member's directory for the
|
|
99
|
+
* applied values, and `live-plan-set -json` over it for the planned ones,
|
|
100
|
+
* read the way `plan` reads a root's (`terraformPlannedOutputs`). The member
|
|
101
|
+
* is not a `terraform.roots` entry of the reading project; its directory
|
|
102
|
+
* comes from the workspace declaration.
|
|
66
103
|
*/
|
|
67
104
|
|
|
105
|
+
import { rmSync } from "node:fs";
|
|
106
|
+
import { dirname, join, resolve } from "node:path";
|
|
68
107
|
import type { Capability, DeployContext, CapabilityPlan, PlannedOutputs } from "@intentius/chant/components/capability";
|
|
108
|
+
import { loadChantConfigUpward } from "@intentius/chant/config";
|
|
109
|
+
import {
|
|
110
|
+
PlanShapeMovedError,
|
|
111
|
+
planShapeDifferences,
|
|
112
|
+
planShapeDigest,
|
|
113
|
+
type PlanShapeArtifact,
|
|
114
|
+
} from "@intentius/chant/components/covered-round";
|
|
69
115
|
import type { ChangeSetPart } from "@intentius/chant/change-set";
|
|
70
116
|
import { computePlanDigest } from "@intentius/chant/lifecycle/plan-digest";
|
|
71
117
|
import { ownPackageVersion, type CapabilityPlugin } from "@intentius/chant/components/capability-plugin";
|
|
118
|
+
import type { EngineMemberRef, EngineOutputSource, EnginePlannedOutputs } from "@intentius/chant/components/verbs/member-outputs";
|
|
72
119
|
import {
|
|
120
|
+
choudoufuMemberOutputs,
|
|
121
|
+
choudoufuMemberPlanSet,
|
|
73
122
|
choudoufuPlanSet,
|
|
74
123
|
choudoufuWaveApply,
|
|
75
124
|
choudoufuWaveRoot,
|
|
@@ -78,6 +127,13 @@ import {
|
|
|
78
127
|
terraformOutputs,
|
|
79
128
|
terraformPlan,
|
|
80
129
|
} from "../op/activities/terraform";
|
|
130
|
+
import { terraformPlanShape } from "../plan-shape";
|
|
131
|
+
import { terraformChangeSetPart } from "../change-set";
|
|
132
|
+
import type { TerraformConfig } from "../config";
|
|
133
|
+
import { rootTexts } from "../compose/hcl";
|
|
134
|
+
import { declaredVariables } from "../compose/root";
|
|
135
|
+
import { terraformComposeGroup } from "./compose-group";
|
|
136
|
+
import { markPlaceholderDriven, placeholderVars, waitingHole } from "./placeholder-preview";
|
|
81
137
|
|
|
82
138
|
/** The `terraform-apply` step's input. */
|
|
83
139
|
export interface TerraformApplyStepInput {
|
|
@@ -116,10 +172,14 @@ function requireRoot(input: TerraformApplyStepInput): void {
|
|
|
116
172
|
async function planRoot(input: TerraformApplyStepInput, targets?: string[]): Promise<PlannedRoot & { changeSet?: ChangeSetPart; json?: unknown }> {
|
|
117
173
|
requireRoot(input);
|
|
118
174
|
await terraformInit(rootArgs(input));
|
|
175
|
+
// One `show -json` per plan (#4479): its document is the digest, the change
|
|
176
|
+
// set, the planned outputs and the shape. Nothing here reads the
|
|
177
|
+
// `-no-color` render, so the plan skips it.
|
|
119
178
|
const planned = await terraformPlan({
|
|
120
179
|
...rootArgs(input),
|
|
121
180
|
...(input.vars ? { vars: input.vars } : {}),
|
|
122
181
|
...(targets && targets.length > 0 ? { targets } : {}),
|
|
182
|
+
text: false,
|
|
123
183
|
});
|
|
124
184
|
return {
|
|
125
185
|
planFile: planned.planFile,
|
|
@@ -164,12 +224,39 @@ export function terraformPlannedOutputs(json: unknown): PlannedOutputs {
|
|
|
164
224
|
}
|
|
165
225
|
|
|
166
226
|
/**
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
227
|
+
* What a choudoufu member's set plan says about its outputs (#4455), for
|
|
228
|
+
* `member-outputs`: {@link terraformPlannedOutputs} of the embedded plan,
|
|
229
|
+
* chant's digest of that plan, and whether it has changes to apply.
|
|
230
|
+
*/
|
|
231
|
+
export function choudoufuMemberPlannedOutputs(set: { planDigest: string; changed: boolean; json: unknown }): EnginePlannedOutputs {
|
|
232
|
+
return { ...terraformPlannedOutputs(set.json), planDigest: set.planDigest, changes: set.changed };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const memberArgs = (ref: EngineMemberRef) => ({ member: ref.member, dir: ref.dir, projectRoot: ref.workspaceRoot });
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* The choudoufu engine's member outputs (#4455): what core's
|
|
239
|
+
* `member-outputs` reads a workspace member of kind `choudoufu` through.
|
|
240
|
+
* Applied values from the member's root outputs (the record store, for a
|
|
241
|
+
* live estate); planned values from its one-root set plan.
|
|
242
|
+
*/
|
|
243
|
+
export const choudoufuOutputSource: EngineOutputSource = {
|
|
244
|
+
kinds: ["choudoufu"],
|
|
245
|
+
applied: (_ctx, ref) => choudoufuMemberOutputs(memberArgs(ref)),
|
|
246
|
+
planned: async (_ctx, ref) => choudoufuMemberPlannedOutputs(await choudoufuMemberPlanSet(memberArgs(ref))),
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
const resolvedVars = (input: TerraformApplyStepInput): Record<string, unknown> =>
|
|
250
|
+
Object.fromEntries(Object.entries(input.vars ?? {}).map(([k, v]) => [k, v === undefined ? null : v]));
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* The member of a root whose `vars` read outputs not known yet and that
|
|
254
|
+
* could not be previewed (#3999, #4463): no plan, a hole naming whose outputs
|
|
255
|
+
* it waits on and, when a preview was tried, why it was not possible, and a
|
|
256
|
+
* digest over the root, the vars as resolved and those names.
|
|
170
257
|
*/
|
|
171
|
-
function waitingRoot(ctx: DeployContext, input: TerraformApplyStepInput, unresolved: string[]): CapabilityPlan {
|
|
172
|
-
const vars =
|
|
258
|
+
function waitingRoot(ctx: DeployContext, input: TerraformApplyStepInput, unresolved: string[], noPreview?: string): CapabilityPlan {
|
|
259
|
+
const vars = resolvedVars(input);
|
|
173
260
|
const planDigest = computePlanDigest("terraform-apply-unresolved", { env: ctx.env, root: input.root, vars, unresolved });
|
|
174
261
|
const changeSet: ChangeSetPart = {
|
|
175
262
|
member: {
|
|
@@ -178,13 +265,104 @@ function waitingRoot(ctx: DeployContext, input: TerraformApplyStepInput, unresol
|
|
|
178
265
|
planner: "chant",
|
|
179
266
|
status: "planned",
|
|
180
267
|
planDigest,
|
|
181
|
-
holes: [
|
|
268
|
+
holes: [waitingHole(input.root, unresolved, noPreview)],
|
|
182
269
|
},
|
|
183
270
|
entries: [],
|
|
184
271
|
};
|
|
185
272
|
return { member: input.root, planDigest, changeSet, ...(ctx.targets?.length ? { targets: ctx.targets } : {}) };
|
|
186
273
|
}
|
|
187
274
|
|
|
275
|
+
/** The plan file a preview writes, removed once it is read: a plan made with stand-ins must never apply. */
|
|
276
|
+
export const PREVIEW_PLAN_FILE = ".chant-preview.tfplan";
|
|
277
|
+
|
|
278
|
+
/** What a previewed root hands `run` (#4463): the shape its plan had with stand-in values. Never a plan file. */
|
|
279
|
+
export interface TerraformShapeArtifact extends PlanShapeArtifact {
|
|
280
|
+
kind: "terraform-apply-shape";
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function isShapeArtifact(value: unknown): value is TerraformShapeArtifact {
|
|
284
|
+
const v = value as Partial<TerraformShapeArtifact> | undefined;
|
|
285
|
+
return v?.kind === "terraform-apply-shape" && typeof v.digest === "string" && typeof v.shape === "object" && v.shape !== null;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/** The root's directory, as `terraform.roots` names it from the nearest `chant.config.*`, and the configured binary. */
|
|
289
|
+
async function rootDir(input: TerraformApplyStepInput): Promise<{ dir: string; binary?: string } | undefined> {
|
|
290
|
+
const start = resolve(input.cwd ?? process.cwd());
|
|
291
|
+
const { config, configPath } = await loadChantConfigUpward(start);
|
|
292
|
+
const terraform = (config as { terraform?: TerraformConfig }).terraform;
|
|
293
|
+
const dir = terraform?.roots?.[input.root]?.dir;
|
|
294
|
+
if (dir === undefined) return undefined;
|
|
295
|
+
return { dir: resolve(configPath ? dirname(configPath) : start, dir), ...(terraform?.binary ? { binary: terraform.binary } : {}) };
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const firstLines = (err: unknown): string => {
|
|
299
|
+
const text = (err instanceof Error ? err.message : String(err)).trim();
|
|
300
|
+
const lines = text.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
301
|
+
return lines.slice(0, 4).join(" ").slice(0, 600);
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Preview a root whose `vars` read outputs not known yet (#4463): plan it
|
|
306
|
+
* with a typed stand-in per such variable, mark what the stand-ins drive
|
|
307
|
+
* unknown, and answer the change set, the planned outputs and the shape of
|
|
308
|
+
* that plan, with the shape as the artifact. The plan file is removed. When
|
|
309
|
+
* no preview can be made, the member stays a hole saying why.
|
|
310
|
+
*/
|
|
311
|
+
async function previewRoot(ctx: DeployContext, input: TerraformApplyStepInput, unresolved: string[], targets?: string[]): Promise<CapabilityPlan> {
|
|
312
|
+
const root = await rootDir(input);
|
|
313
|
+
if (root === undefined) return waitingRoot(ctx, input, unresolved, `no root named "${input.root}" in terraform.roots to read its variables from`);
|
|
314
|
+
const { dir } = root;
|
|
315
|
+
// #4457: the same files the binary reads (`.tofu` only for tofu and choudoufu).
|
|
316
|
+
const stand = placeholderVars(input.vars, declaredVariables(rootTexts(dir, root.binary)));
|
|
317
|
+
if (typeof stand === "string") return waitingRoot(ctx, input, unresolved, stand);
|
|
318
|
+
let planned: Awaited<ReturnType<typeof terraformPlan>>;
|
|
319
|
+
try {
|
|
320
|
+
await terraformInit(rootArgs(input));
|
|
321
|
+
planned = await terraformPlan({
|
|
322
|
+
...rootArgs(input),
|
|
323
|
+
vars: stand.vars,
|
|
324
|
+
planFile: PREVIEW_PLAN_FILE,
|
|
325
|
+
...(targets ? { targets } : {}),
|
|
326
|
+
text: false,
|
|
327
|
+
});
|
|
328
|
+
} catch (err) {
|
|
329
|
+
return waitingRoot(ctx, input, unresolved, `the plan with stand-in values failed: ${firstLines(err)}`);
|
|
330
|
+
} finally {
|
|
331
|
+
rmSync(join(dir, PREVIEW_PLAN_FILE), { force: true });
|
|
332
|
+
}
|
|
333
|
+
const marked = markPlaceholderDriven(planned.json, stand.placeholders);
|
|
334
|
+
if ("refused" in marked) return waitingRoot(ctx, input, unresolved, marked.refused);
|
|
335
|
+
const placeholders = stand.placeholders.map((name) => `var.${name}`);
|
|
336
|
+
const shape = terraformPlanShape(marked.plan, input.root, { unknownInputs: unresolved });
|
|
337
|
+
const artifact: TerraformShapeArtifact = { kind: "terraform-apply-shape", member: input.root, shape, digest: planShapeDigest(shape) };
|
|
338
|
+
const part = terraformChangeSetPart({
|
|
339
|
+
member: input.root,
|
|
340
|
+
plan: marked.plan,
|
|
341
|
+
...(planned.changeSet?.member.planner ? { planner: planned.changeSet.member.planner } : {}),
|
|
342
|
+
...(planned.changeSet?.member.scope ? { scope: planned.changeSet.member.scope } : {}),
|
|
343
|
+
});
|
|
344
|
+
// The stand-ins' values are not real, so the digest is over the preview's shape and entries, never the raw plan.
|
|
345
|
+
const planDigest = computePlanDigest("terraform-apply-preview", {
|
|
346
|
+
env: ctx.env,
|
|
347
|
+
root: input.root,
|
|
348
|
+
vars: resolvedVars(input),
|
|
349
|
+
unresolved,
|
|
350
|
+
placeholders,
|
|
351
|
+
shape: artifact.digest,
|
|
352
|
+
entries: part.entries,
|
|
353
|
+
});
|
|
354
|
+
const changeSet: ChangeSetPart = { ...part, member: { ...part.member, planDigest, placeholders } };
|
|
355
|
+
return {
|
|
356
|
+
member: input.root,
|
|
357
|
+
planDigest,
|
|
358
|
+
artifact,
|
|
359
|
+
changeSet,
|
|
360
|
+
outputs: terraformPlannedOutputs(marked.plan),
|
|
361
|
+
shape,
|
|
362
|
+
...(targets ? { targets } : {}),
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
|
|
188
366
|
/**
|
|
189
367
|
* choudoufu refused the plan `run` was handed by its wave (its exit 3):
|
|
190
368
|
* the live system moved since the plan, or the plan names another estate.
|
|
@@ -255,8 +433,8 @@ export const terraformApplyCapability: Capability<TerraformApplyStepInput, Terra
|
|
|
255
433
|
async plan(ctx: DeployContext, input: TerraformApplyStepInput): Promise<CapabilityPlan> {
|
|
256
434
|
requireRoot(input);
|
|
257
435
|
const unresolved = [...(ctx.unresolvedInputs ?? [])].sort();
|
|
258
|
-
if (unresolved.length > 0) return waitingRoot(ctx, input, unresolved);
|
|
259
436
|
const targets = ctx.targets && ctx.targets.length > 0 ? ctx.targets : undefined;
|
|
437
|
+
if (unresolved.length > 0) return previewRoot(ctx, input, unresolved, targets);
|
|
260
438
|
// A targeted root plans with `plan -target`, never as a choudoufu one-root set.
|
|
261
439
|
const wave = targets ? { wave: false } : await choudoufuWaveRoot({ ...rootArgs(input), ...(input.vars ? { vars: input.vars } : {}) });
|
|
262
440
|
if (wave.wave) {
|
|
@@ -265,7 +443,7 @@ export const terraformApplyCapability: Capability<TerraformApplyStepInput, Terra
|
|
|
265
443
|
planDigest: set.planDigest,
|
|
266
444
|
choudoufu: { planSetFile: set.planSetFile, setDigest: set.setDigest },
|
|
267
445
|
};
|
|
268
|
-
return { member: input.root, planDigest: set.planDigest, artifact, changeSet: set.changeSet, outputs: terraformPlannedOutputs(set.json) };
|
|
446
|
+
return { member: input.root, planDigest: set.planDigest, artifact, changeSet: set.changeSet, outputs: terraformPlannedOutputs(set.json), shape: terraformPlanShape(set.json, input.root) };
|
|
269
447
|
}
|
|
270
448
|
const { changeSet, json, ...planned } = await planRoot(input, targets);
|
|
271
449
|
return {
|
|
@@ -274,6 +452,7 @@ export const terraformApplyCapability: Capability<TerraformApplyStepInput, Terra
|
|
|
274
452
|
artifact: planned,
|
|
275
453
|
...(changeSet ? { changeSet } : {}),
|
|
276
454
|
outputs: terraformPlannedOutputs(json),
|
|
455
|
+
shape: terraformPlanShape(json, input.root),
|
|
277
456
|
...(targets ? { targets } : {}),
|
|
278
457
|
};
|
|
279
458
|
},
|
|
@@ -284,9 +463,28 @@ export const terraformApplyCapability: Capability<TerraformApplyStepInput, Terra
|
|
|
284
463
|
await terraformInit(rootArgs(input));
|
|
285
464
|
return terraformOutputs(rootArgs(input));
|
|
286
465
|
},
|
|
466
|
+
// #4447 (ws-107): a group core forms of components that read each other plans and applies as one composed run
|
|
467
|
+
// when `terraform.compose.enabled` is set (./compose-group.ts). Delegates, so the methods are read when called.
|
|
468
|
+
groupReads: (ctx, members) => terraformComposeGroup.groupReads(ctx, members),
|
|
469
|
+
groupFallback: (ctx, members) => terraformComposeGroup.groupFallback(ctx, members),
|
|
470
|
+
planGroup: (ctx, members) => terraformComposeGroup.planGroup(ctx, members),
|
|
471
|
+
runGroup: (ctx, run, members) => terraformComposeGroup.runGroup(ctx, run, members),
|
|
287
472
|
async run(ctx: DeployContext, input: TerraformApplyStepInput): Promise<TerraformApplyStepOutput> {
|
|
288
473
|
const fromWave = ctx.plans?.[input.root];
|
|
289
474
|
if (isChoudoufuWavePlan(fromWave)) return runChoudoufuWave(ctx, input, fromWave);
|
|
475
|
+
if (isShapeArtifact(fromWave)) {
|
|
476
|
+
// A root previewed with stand-ins (#4463): plan it now with the real
|
|
477
|
+
// values, and apply that plan only when it is within the preview's shape.
|
|
478
|
+
const fresh = await planRoot(input, ctx.targets);
|
|
479
|
+
const differences = planShapeDifferences(fromWave.shape, terraformPlanShape(fresh.json, input.root));
|
|
480
|
+
if (differences.length > 0) throw new PlanShapeMovedError(input.root, differences);
|
|
481
|
+
const applied = await terraformApply({ ...rootArgs(input), planFile: fresh.planFile });
|
|
482
|
+
if (!applied.applied) {
|
|
483
|
+
throw new Error(`terraform-apply: root "${input.root}" refused the plan it was given (${applied.refused ?? "refused"}). ${applied.refusal ?? ""}`);
|
|
484
|
+
}
|
|
485
|
+
const outputs = await terraformOutputs(rootArgs(input));
|
|
486
|
+
return { root: input.root, planDigest: fresh.planDigest, fromWavePlan: false, outputs };
|
|
487
|
+
}
|
|
290
488
|
const planned: PlannedRoot = isPlannedRoot(fromWave)
|
|
291
489
|
? fromWave
|
|
292
490
|
: await planRoot(input, ctx.targets).then(({ planFile, planDigest }) => ({ planFile, planDigest }));
|
|
@@ -320,4 +518,7 @@ export const terraformCapabilityPlugin: CapabilityPlugin = {
|
|
|
320
518
|
families(): Record<string, readonly string[]> {
|
|
321
519
|
return TERRAFORM_VERB_FAMILIES;
|
|
322
520
|
},
|
|
521
|
+
engineOutputSources(): EngineOutputSource[] {
|
|
522
|
+
return [choudoufuOutputSource];
|
|
523
|
+
},
|
|
323
524
|
};
|
|
@@ -16,7 +16,7 @@ import { tmpdir } from "node:os";
|
|
|
16
16
|
import { join } from "node:path";
|
|
17
17
|
import { afterAll, beforeAll, describe, expect, test } from "vitest";
|
|
18
18
|
import { composeChangeSet } from "@intentius/chant/change-set";
|
|
19
|
-
import { buildGraphIr } from "@intentius/chant/graph-ir";
|
|
19
|
+
import { buildGraphIr, withInstanceNodes } from "@intentius/chant/graph-ir";
|
|
20
20
|
import { readGraphIds, storeGateChangeSet } from "@intentius/chant/lifecycle/gate-change-set";
|
|
21
21
|
import { readStoredChangeSet } from "@intentius/chant/lifecycle/change-set-store";
|
|
22
22
|
import { terraformChangeSetPart } from "../change-set";
|
|
@@ -59,10 +59,11 @@ describe("a terraform change set mapped onto the spawned chant graph", () => {
|
|
|
59
59
|
const spawned = await readGraphIds(dir);
|
|
60
60
|
if ("error" in spawned) throw new Error(spawned.error);
|
|
61
61
|
const rendered = await renderTerraformRoots({ projectRoot: dir, roots: { estate: { dir: "estate" } } });
|
|
62
|
-
|
|
62
|
+
// `chant graph` adds the instance nodes of `count = 2` (#4454).
|
|
63
|
+
expect(withInstanceNodes(buildGraphIr(rendered.entities), rendered.entities).nodes.map((n) => n.id).sort()).toEqual([...spawned.ids].sort());
|
|
63
64
|
}, TIMEOUT);
|
|
64
65
|
|
|
65
|
-
test("each entry is stored on the
|
|
66
|
+
test("each entry is stored on the node the graph names, an instance on its instance node, and a deleted block has no node", async () => {
|
|
66
67
|
const plan = JSON.parse(readFileSync(join(FIXTURES, "terraform.plan.json"), "utf8"));
|
|
67
68
|
const doc = composeChangeSet([terraformChangeSetPart({ member: "estate", plan })]);
|
|
68
69
|
const head = git("rev-parse", "HEAD");
|
|
@@ -72,13 +73,18 @@ describe("a terraform change set mapped onto the spawned chant graph", () => {
|
|
|
72
73
|
const read = await readStoredChangeSet(doc.digest, { cwd: dir });
|
|
73
74
|
if (read.status !== "found") throw new Error(read.status);
|
|
74
75
|
expect(read.document.entries.map((e) => [e.address, e.node])).toEqual([
|
|
75
|
-
["module.app.terraform_data.instance[0]", "estate/module.app/terraform_data.instance"],
|
|
76
|
-
["module.app.terraform_data.instance[1]", "estate/module.app/terraform_data.instance"],
|
|
76
|
+
["module.app.terraform_data.instance[0]", "estate/module.app/terraform_data.instance[0]"],
|
|
77
|
+
["module.app.terraform_data.instance[1]", "estate/module.app/terraform_data.instance[1]"],
|
|
77
78
|
["terraform_data.config", "estate/terraform_data.config"],
|
|
78
79
|
["terraform_data.keep", "estate/terraform_data.keep"],
|
|
79
80
|
["terraform_data.old", null],
|
|
80
81
|
["terraform_data.secret", "estate/terraform_data.secret"],
|
|
81
82
|
["terraform_data.worker", "estate/terraform_data.worker"],
|
|
82
83
|
]);
|
|
84
|
+
// The instances name their block node, the rollup (#4454).
|
|
85
|
+
expect(read.document.entries.filter((e) => e.instanceOf).map((e) => e.instanceOf)).toEqual([
|
|
86
|
+
"estate/module.app/terraform_data.instance",
|
|
87
|
+
"estate/module.app/terraform_data.instance",
|
|
88
|
+
]);
|
|
83
89
|
}, TIMEOUT);
|
|
84
90
|
});
|