@intentius/chant-lexicon-terraform 0.124.52 → 0.124.55
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 +14 -10
- 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/ci-setup.d.ts +11 -0
- package/dist/ci-setup.d.ts.map +1 -0
- package/dist/ci-setup.js +41 -0
- package/dist/commands.d.ts.map +1 -1
- package/dist/commands.js +4 -3
- package/dist/components/capability-plugin.d.ts +105 -9
- package/dist/components/capability-plugin.d.ts.map +1 -1
- package/dist/components/capability-plugin.js +357 -36
- 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/components/release-inputs.d.ts +29 -0
- package/dist/components/release-inputs.d.ts.map +1 -0
- package/dist/components/release-inputs.js +57 -0
- package/dist/components/trace-facts.d.ts +16 -0
- package/dist/components/trace-facts.d.ts.map +1 -0
- package/dist/components/trace-facts.js +19 -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/cone.d.ts +53 -0
- package/dist/cone.d.ts.map +1 -0
- package/dist/cone.js +119 -0
- package/dist/config.d.ts +59 -13
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +43 -9
- package/dist/cost-hold.d.ts +27 -0
- package/dist/cost-hold.d.ts.map +1 -0
- package/dist/cost-hold.js +21 -0
- 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/drift-rows.d.ts +61 -0
- package/dist/drift-rows.d.ts.map +1 -0
- package/dist/drift-rows.js +108 -0
- package/dist/exit-codes.d.ts +55 -0
- package/dist/exit-codes.d.ts.map +1 -0
- package/dist/exit-codes.js +48 -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 +13 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -2
- package/dist/integrity.json +8 -5
- package/dist/lint/audit-catalog.d.ts.map +1 -1
- package/dist/lint/audit-catalog.js +27 -3
- package/dist/lint/audit-lineage.d.ts +14 -2
- package/dist/lint/audit-lineage.d.ts.map +1 -1
- package/dist/lint/post-synth/index.d.ts.map +1 -1
- package/dist/lint/post-synth/index.js +4 -0
- package/dist/lint/post-synth/record-store.d.ts +54 -0
- package/dist/lint/post-synth/record-store.d.ts.map +1 -0
- package/dist/lint/post-synth/record-store.js +104 -0
- package/dist/lint/post-synth/tf001.d.ts +2 -2
- package/dist/lint/post-synth/tf001.js +2 -2
- package/dist/lint/post-synth/tf024.d.ts +35 -11
- package/dist/lint/post-synth/tf024.d.ts.map +1 -1
- package/dist/lint/post-synth/tf024.js +92 -20
- package/dist/lint/post-synth/tf046.d.ts +64 -0
- package/dist/lint/post-synth/tf046.d.ts.map +1 -0
- package/dist/lint/post-synth/tf046.js +175 -0
- package/dist/lint/post-synth/tf047.d.ts +36 -0
- package/dist/lint/post-synth/tf047.d.ts.map +1 -0
- package/dist/lint/post-synth/tf047.js +94 -0
- 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/lock-rows.d.ts +36 -0
- package/dist/lock-rows.d.ts.map +1 -0
- package/dist/lock-rows.js +60 -0
- package/dist/lock-timeout.d.ts +22 -0
- package/dist/lock-timeout.d.ts.map +1 -0
- package/dist/lock-timeout.js +32 -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/module-catalog/index.d.ts +69 -0
- package/dist/module-catalog/index.d.ts.map +1 -0
- package/dist/module-catalog/index.js +110 -0
- package/dist/module-catalog/refresh-cli.d.ts +4 -0
- package/dist/module-catalog/refresh-cli.d.ts.map +1 -0
- package/dist/module-catalog/refresh-cli.js +64 -0
- package/dist/module-catalog/static-tier.d.ts +6 -0
- package/dist/module-catalog/static-tier.d.ts.map +1 -0
- package/dist/module-catalog/static-tier.js +1650 -0
- package/dist/module-catalog/types.d.ts +65 -0
- package/dist/module-catalog/types.d.ts.map +1 -0
- package/dist/module-catalog/types.js +33 -0
- package/dist/okf/index.md +3 -1
- package/dist/okf/rules/TF024.md +2 -2
- package/dist/okf/rules/TF046.md +11 -0
- package/dist/okf/rules/TF047.md +11 -0
- 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 +182 -5
- package/dist/op/activities/terraform.d.ts.map +1 -1
- package/dist/op/activities/terraform.js +224 -24
- 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/consumers.d.ts +35 -0
- package/dist/pin/consumers.d.ts.map +1 -0
- package/dist/pin/consumers.js +70 -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 +37 -18
- package/dist/registry/address.d.ts +63 -0
- package/dist/registry/address.d.ts.map +1 -0
- package/dist/registry/address.js +65 -0
- package/dist/registry/catalog-read.d.ts +14 -0
- package/dist/registry/catalog-read.d.ts.map +1 -0
- package/dist/registry/catalog-read.js +58 -0
- package/dist/registry/deprecated.d.ts +37 -0
- package/dist/registry/deprecated.d.ts.map +1 -0
- package/dist/registry/deprecated.js +43 -0
- package/dist/registry/docs.d.ts +96 -0
- package/dist/registry/docs.d.ts.map +1 -0
- package/dist/registry/docs.js +210 -0
- package/dist/registry/edge/edge.d.ts +46 -0
- package/dist/registry/edge/edge.d.ts.map +1 -0
- package/dist/registry/edge/edge.js +194 -0
- package/dist/registry/edge/index.d.ts +11 -0
- package/dist/registry/edge/index.d.ts.map +1 -0
- package/dist/registry/edge/index.js +10 -0
- package/dist/registry/edge/lambda-edge.d.ts +76 -0
- package/dist/registry/edge/lambda-edge.d.ts.map +1 -0
- package/dist/registry/edge/lambda-edge.js +95 -0
- package/dist/registry/edge/sigv4-web.d.ts +33 -0
- package/dist/registry/edge/sigv4-web.d.ts.map +1 -0
- package/dist/registry/edge/sigv4-web.js +56 -0
- package/dist/registry/edge/token-source.d.ts +73 -0
- package/dist/registry/edge/token-source.d.ts.map +1 -0
- package/dist/registry/edge/token-source.js +115 -0
- package/dist/registry/edge/worker.d.ts +45 -0
- package/dist/registry/edge/worker.d.ts.map +1 -0
- package/dist/registry/edge/worker.js +26 -0
- package/dist/registry/handler.d.ts +25 -0
- package/dist/registry/handler.d.ts.map +1 -0
- package/dist/registry/handler.js +75 -0
- package/dist/registry/headers-file.d.ts +21 -0
- package/dist/registry/headers-file.d.ts.map +1 -0
- package/dist/registry/headers-file.js +57 -0
- package/dist/registry/index.d.ts +31 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +30 -0
- package/dist/registry/mirror-paths.d.ts +9 -0
- package/dist/registry/mirror-paths.d.ts.map +1 -0
- package/dist/registry/mirror-paths.js +8 -0
- package/dist/registry/mirror.d.ts +130 -0
- package/dist/registry/mirror.d.ts.map +1 -0
- package/dist/registry/mirror.js +319 -0
- package/dist/registry/proxies.d.ts +73 -0
- package/dist/registry/proxies.d.ts.map +1 -0
- package/dist/registry/proxies.js +111 -0
- package/dist/registry/reader.d.ts +57 -0
- package/dist/registry/reader.d.ts.map +1 -0
- package/dist/registry/reader.js +198 -0
- package/dist/registry/semver.d.ts +16 -0
- package/dist/registry/semver.d.ts.map +1 -0
- package/dist/registry/semver.js +24 -0
- package/dist/registry/serve.d.ts +24 -0
- package/dist/registry/serve.d.ts.map +1 -0
- package/dist/registry/serve.js +64 -0
- package/dist/registry/source-forms.d.ts +79 -0
- package/dist/registry/source-forms.d.ts.map +1 -0
- package/dist/registry/source-forms.js +202 -0
- package/dist/registry/targets/artifactory.d.ts +28 -0
- package/dist/registry/targets/artifactory.d.ts.map +1 -0
- package/dist/registry/targets/artifactory.js +206 -0
- package/dist/registry/targets/fake-artifactory.d.ts +30 -0
- package/dist/registry/targets/fake-artifactory.d.ts.map +1 -0
- package/dist/registry/targets/fake-artifactory.js +142 -0
- package/dist/registry/targets/fake-gitlab.d.ts +39 -0
- package/dist/registry/targets/fake-gitlab.d.ts.map +1 -0
- package/dist/registry/targets/fake-gitlab.js +136 -0
- package/dist/registry/targets/fake-hcp.d.ts +50 -0
- package/dist/registry/targets/fake-hcp.d.ts.map +1 -0
- package/dist/registry/targets/fake-hcp.js +202 -0
- package/dist/registry/targets/fake-http.d.ts +31 -0
- package/dist/registry/targets/fake-http.d.ts.map +1 -0
- package/dist/registry/targets/fake-http.js +87 -0
- package/dist/registry/targets/fake-nexus.d.ts +27 -0
- package/dist/registry/targets/fake-nexus.d.ts.map +1 -0
- package/dist/registry/targets/fake-nexus.js +137 -0
- package/dist/registry/targets/gitlab.d.ts +53 -0
- package/dist/registry/targets/gitlab.d.ts.map +1 -0
- package/dist/registry/targets/gitlab.js +187 -0
- package/dist/registry/targets/hcp.d.ts +29 -0
- package/dist/registry/targets/hcp.d.ts.map +1 -0
- package/dist/registry/targets/hcp.js +310 -0
- package/dist/registry/targets/hosted.d.ts +62 -0
- package/dist/registry/targets/hosted.d.ts.map +1 -0
- package/dist/registry/targets/hosted.js +74 -0
- package/dist/registry/targets/index.d.ts +18 -0
- package/dist/registry/targets/index.d.ts.map +1 -0
- package/dist/registry/targets/index.js +17 -0
- package/dist/registry/targets/module-target.d.ts +68 -0
- package/dist/registry/targets/module-target.d.ts.map +1 -0
- package/dist/registry/targets/module-target.js +63 -0
- package/dist/registry/targets/nexus.d.ts +43 -0
- package/dist/registry/targets/nexus.d.ts.map +1 -0
- package/dist/registry/targets/nexus.js +250 -0
- package/dist/registry/targets/zip.d.ts +55 -0
- package/dist/registry/targets/zip.d.ts.map +1 -0
- package/dist/registry/targets/zip.js +278 -0
- package/dist/registry/writer.d.ts +101 -0
- package/dist/registry/writer.d.ts.map +1 -0
- package/dist/registry/writer.js +109 -0
- package/dist/report.d.ts +15 -6
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +28 -55
- package/dist/rules/record-store.ts +122 -0
- package/dist/rules/tf001.ts +2 -2
- package/dist/rules/tf024.ts +105 -22
- package/dist/rules/tf046.ts +204 -0
- package/dist/rules/tf047.ts +114 -0
- package/dist/skills/chant-terraform.md +1 -1
- package/dist/state/azure-blob.d.ts +9 -0
- package/dist/state/azure-blob.d.ts.map +1 -0
- package/dist/state/azure-blob.js +8 -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 +8 -0
- package/dist/state/gcs.d.ts.map +1 -0
- package/dist/state/gcs.js +7 -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 +8 -0
- package/dist/state/object-store.d.ts.map +1 -0
- package/dist/state/object-store.js +7 -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 +21 -0
- package/dist/state/s3.d.ts.map +1 -0
- package/dist/state/s3.js +19 -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/state-ops.d.ts +255 -0
- package/dist/state-ops.d.ts.map +1 -0
- package/dist/state-ops.js +305 -0
- package/dist/state-reads.d.ts +57 -0
- package/dist/state-reads.d.ts.map +1 -0
- package/dist/state-reads.js +104 -0
- package/dist/terragrunt/affected.d.ts.map +1 -1
- package/dist/terragrunt/affected.js +2 -1
- package/dist/used-sources.d.ts +54 -0
- package/dist/used-sources.d.ts.map +1 -0
- package/dist/used-sources.js +157 -0
- package/package.json +24 -4
- 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 +16 -0
- package/src/__fixtures__/drift-record/live-plan-sections.json +27 -0
- package/src/__fixtures__/drift-rows/live-root-plan.json +70 -0
- package/src/__fixtures__/drift-rows/refresh-plan.json +61 -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/__fixtures__/used-sources/root/extra.tofu +1 -0
- package/src/__fixtures__/used-sources/root/main.tf +31 -0
- package/src/__fixtures__/used-sources/root/modules/net/main.tf +14 -0
- package/src/__fixtures__/used-sources/root/modules/net/versions.tf.json +7 -0
- package/src/__fixtures__/used-sources/unit/terragrunt.hcl +3 -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/ci-setup.ts +42 -0
- package/src/commands.ts +4 -3
- package/src/components/capability-plugin.test.ts +198 -19
- package/src/components/capability-plugin.ts +383 -35
- 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 +113 -57
- package/src/components/release-inputs.ts +69 -0
- package/src/components/trace-facts.test.ts +21 -0
- package/src/components/trace-facts.ts +27 -0
- 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/cone.ts +138 -0
- package/src/config-file-listers.test.ts +265 -0
- package/src/config.ts +44 -9
- package/src/cost-hold.ts +29 -0
- package/src/describe-resources.engine-facts.test.ts +411 -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/drift-rows.test.ts +135 -0
- package/src/drift-rows.ts +137 -0
- package/src/exit-codes.test.ts +23 -0
- package/src/exit-codes.ts +59 -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 +79 -0
- package/src/lint/audit-catalog.ts +29 -3
- package/src/lint/audit-lineage.ts +14 -2
- package/src/lint/post-synth/fixtures/TF024/live-with-record-store/main.tf +18 -0
- package/src/lint/post-synth/fixtures/TF024/sidecar-with-backend/estate.chdf.hcl +1 -0
- package/src/lint/post-synth/fixtures/TF024/sidecar-with-backend/main.tf +10 -0
- package/src/lint/post-synth/fixtures/TF046/azurerm-no-resource-group/main.tf +15 -0
- package/src/lint/post-synth/fixtures/TF046/backend-config-only/main.tf +14 -0
- package/src/lint/post-synth/fixtures/TF046/derived-s3/backend.tf +7 -0
- package/src/lint/post-synth/fixtures/TF046/derived-s3/main.tf +9 -0
- package/src/lint/post-synth/fixtures/TF046/explicit-local/main.tf +16 -0
- package/src/lint/post-synth/fixtures/TF046/explicit-s3-beside-local-backend/main.tf +15 -0
- package/src/lint/post-synth/fixtures/TF046/implied-local/main.tf +9 -0
- package/src/lint/post-synth/fixtures/TF046/sidecar-implied/estate.chdf.hcl +1 -0
- package/src/lint/post-synth/fixtures/TF046/sidecar-implied/main.tf +5 -0
- package/src/lint/post-synth/fixtures/TF047/clear/main.tf +12 -0
- package/src/lint/post-synth/fixtures/TF047/refused/main.tf +12 -0
- package/src/lint/post-synth/fixtures/TF047/unknown-module/main.tf +12 -0
- package/src/lint/post-synth/fixtures/TF047/unknown-version/main.tf +12 -0
- package/src/lint/post-synth/index.ts +4 -0
- package/src/lint/post-synth/record-store.ts +122 -0
- package/src/lint/post-synth/tf001.ts +2 -2
- package/src/lint/post-synth/tf024.test.ts +26 -5
- package/src/lint/post-synth/tf024.ts +105 -22
- package/src/lint/post-synth/tf046.test.ts +137 -0
- package/src/lint/post-synth/tf046.ts +204 -0
- package/src/lint/post-synth/tf047.test.ts +56 -0
- package/src/lint/post-synth/tf047.ts +114 -0
- package/src/live-plan-reasons.ts +54 -0
- package/src/lock-rows.test.ts +70 -0
- package/src/lock-rows.ts +82 -0
- package/src/lock-timeout.ts +34 -0
- package/src/lsp/completions.test.ts +1 -1
- package/src/lsp/option-keys.ts +2 -0
- package/src/module-catalog/index.test.ts +93 -0
- package/src/module-catalog/index.ts +159 -0
- package/src/module-catalog/refresh-cli.ts +68 -0
- package/src/module-catalog/static-tier.ts +1659 -0
- package/src/module-catalog/types.ts +88 -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 +312 -27
- 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/op-wave-step.test.ts +109 -0
- package/src/pin/__fixtures__/consumers/live/dev/main.tf +4 -0
- package/src/pin/__fixtures__/consumers/live/prod/main.tf +21 -0
- package/src/pin/__fixtures__/consumers/units/vpc/terragrunt.hcl +7 -0
- package/src/pin/bundle.test.ts +2 -0
- package/src/pin/consumers.test.ts +57 -0
- package/src/pin/consumers.ts +95 -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 +39 -19
- package/src/registry/__fixtures__/docs/documented/.terraform-docs.yml +3 -0
- package/src/registry/__fixtures__/docs/documented/README.md +1 -0
- package/src/registry/__fixtures__/docs/documented/main.tf +8 -0
- package/src/registry/__fixtures__/docs/json/main.tf.json +14 -0
- package/src/registry/__fixtures__/docs/vpc/README.md +3 -0
- package/src/registry/__fixtures__/docs/vpc/examples/complete/main.tf +5 -0
- package/src/registry/__fixtures__/docs/vpc/examples/simple/README.md +1 -0
- package/src/registry/__fixtures__/docs/vpc/examples/simple/main.tf +4 -0
- package/src/registry/__fixtures__/docs/vpc/main.tf +14 -0
- package/src/registry/__fixtures__/docs/vpc/outputs.tf +9 -0
- package/src/registry/__fixtures__/docs/vpc/variables.tf +16 -0
- package/src/registry/__fixtures__/proxied-root/main.tf +12 -0
- package/src/registry/address.ts +100 -0
- package/src/registry/catalog-read.ts +56 -0
- package/src/registry/deprecated.test.ts +139 -0
- package/src/registry/deprecated.ts +76 -0
- package/src/registry/docs.test.ts +140 -0
- package/src/registry/docs.ts +299 -0
- package/src/registry/edge/bundle.test.ts +50 -0
- package/src/registry/edge/edge.test.ts +176 -0
- package/src/registry/edge/edge.ts +211 -0
- package/src/registry/edge/index.ts +10 -0
- package/src/registry/edge/lambda-edge.ts +127 -0
- package/src/registry/edge/sigv4-web.ts +79 -0
- package/src/registry/edge/token-source.test.ts +258 -0
- package/src/registry/edge/token-source.ts +169 -0
- package/src/registry/edge/worker.ts +61 -0
- package/src/registry/handler.ts +89 -0
- package/src/registry/headers-file.ts +61 -0
- package/src/registry/index.ts +31 -0
- package/src/registry/mirror-paths.ts +10 -0
- package/src/registry/mirror.test.ts +198 -0
- package/src/registry/mirror.ts +395 -0
- package/src/registry/proxies.test.ts +153 -0
- package/src/registry/proxies.ts +132 -0
- package/src/registry/reader.ts +225 -0
- package/src/registry/registry.test.ts +262 -0
- package/src/registry/semver.ts +34 -0
- package/src/registry/serve.ts +82 -0
- package/src/registry/source-forms.test.ts +87 -0
- package/src/registry/source-forms.ts +216 -0
- package/src/registry/targets/artifactory.test.ts +248 -0
- package/src/registry/targets/artifactory.ts +257 -0
- package/src/registry/targets/fake-artifactory.ts +158 -0
- package/src/registry/targets/fake-gitlab.ts +161 -0
- package/src/registry/targets/fake-hcp.ts +233 -0
- package/src/registry/targets/fake-http.ts +97 -0
- package/src/registry/targets/fake-nexus.ts +152 -0
- package/src/registry/targets/gitlab.test.ts +208 -0
- package/src/registry/targets/gitlab.ts +223 -0
- package/src/registry/targets/hcp.test.ts +230 -0
- package/src/registry/targets/hcp.ts +360 -0
- package/src/registry/targets/hosted.ts +117 -0
- package/src/registry/targets/index.ts +17 -0
- package/src/registry/targets/module-target.ts +131 -0
- package/src/registry/targets/nexus.test.ts +220 -0
- package/src/registry/targets/nexus.ts +314 -0
- package/src/registry/targets/zip.test.ts +65 -0
- package/src/registry/targets/zip.ts +287 -0
- package/src/registry/writer.ts +155 -0
- package/src/report.test.ts +82 -1
- package/src/report.ts +31 -54
- package/src/signal-only-root.test.ts +72 -0
- package/src/skills/chant-terraform.md +1 -1
- package/src/state/azure-blob.ts +8 -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 +7 -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 +7 -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 +21 -0
- package/src/state/store.ts +225 -0
- package/src/state-ops.test.ts +182 -0
- package/src/state-ops.ts +462 -0
- package/src/state-reads.ts +116 -0
- package/src/terragrunt/affected.ts +2 -1
- package/src/used-sources.test.ts +75 -0
- package/src/used-sources.ts +204 -0
- 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
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* system and refuses with exit 3 when its fresh plan differs (choudoufu
|
|
23
23
|
* #878). Either refusal fails the step. choudoufu's refusal of a plan the
|
|
24
24
|
* wave handed `run` throws {@link TerraformPlanMovedError}, so the command
|
|
25
|
-
* that applied it exits
|
|
25
|
+
* that applied it exits 4, the table's moved code (#3997, #4553). terraform's and tofu's stale-plan error
|
|
26
26
|
* is not classified and exits 1.
|
|
27
27
|
*
|
|
28
28
|
* Run without a wave plan (`chant run --components`, or a fan-out with no
|
|
@@ -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,66 @@
|
|
|
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.
|
|
103
|
+
*
|
|
104
|
+
* ## Op waves (#4545)
|
|
105
|
+
*
|
|
106
|
+
* The step is what an op wave's `step` names to plan and apply each root
|
|
107
|
+
* through chant (`chant run wave`), so terragucci's waves and plywood's run
|
|
108
|
+
* the engine in chant. Three inputs come from terragucci's wave callbacks:
|
|
109
|
+
*
|
|
110
|
+
* - `lockTimeout` (default `5m`, `false` for none): `plan` and `apply` wait
|
|
111
|
+
* that long for another run's state lock, unless `TF_CLI_ARGS` sets
|
|
112
|
+
* `-lock-timeout` already (`../lock-timeout.ts`).
|
|
113
|
+
* - `cone`: a revision. A live choudoufu root plans the cone of the change
|
|
114
|
+
* since it (`plan -cone`, `../cone.ts`), and the cone's targets join the
|
|
115
|
+
* plan digest, so an approval of one cone counts for no other. Such a root
|
|
116
|
+
* plans with `plan`, never as a one-root set.
|
|
117
|
+
* - `reads`: vars read from another root's outputs, Atmos's
|
|
118
|
+
* `!terraform.state` (`../state-reads.ts`). An upstream with no such output
|
|
119
|
+
* yet makes the member a hole naming it, so the root is never planned on a
|
|
120
|
+
* stand-in.
|
|
121
|
+
*
|
|
122
|
+
* Every plan also writes its `show -json` beside the plan file and names it
|
|
123
|
+
* as `artifact.planJson`, the document a wave's cost hold prices
|
|
124
|
+
* (`../cost-hold.ts`).
|
|
66
125
|
*/
|
|
67
126
|
|
|
127
|
+
import { rmSync, writeFileSync } from "node:fs";
|
|
128
|
+
import { dirname, join, resolve } from "node:path";
|
|
68
129
|
import type { Capability, DeployContext, CapabilityPlan, PlannedOutputs } from "@intentius/chant/components/capability";
|
|
130
|
+
import { loadChantConfigUpward } from "@intentius/chant/config";
|
|
131
|
+
import {
|
|
132
|
+
PlanShapeMovedError,
|
|
133
|
+
planShapeDifferences,
|
|
134
|
+
planShapeDigest,
|
|
135
|
+
type PlanShapeArtifact,
|
|
136
|
+
} from "@intentius/chant/components/covered-round";
|
|
69
137
|
import type { ChangeSetPart } from "@intentius/chant/change-set";
|
|
70
138
|
import { computePlanDigest } from "@intentius/chant/lifecycle/plan-digest";
|
|
71
139
|
import { ownPackageVersion, type CapabilityPlugin } from "@intentius/chant/components/capability-plugin";
|
|
140
|
+
import type { EngineMemberRef, EngineOutputSource, EnginePlannedOutputs } from "@intentius/chant/components/verbs/member-outputs";
|
|
72
141
|
import {
|
|
142
|
+
choudoufuMemberOutputs,
|
|
143
|
+
choudoufuMemberPlanSet,
|
|
73
144
|
choudoufuPlanSet,
|
|
74
145
|
choudoufuWaveApply,
|
|
75
146
|
choudoufuWaveRoot,
|
|
@@ -78,6 +149,22 @@ import {
|
|
|
78
149
|
terraformOutputs,
|
|
79
150
|
terraformPlan,
|
|
80
151
|
} from "../op/activities/terraform";
|
|
152
|
+
import { terraformPlanMoved } from "../exit-codes";
|
|
153
|
+
import { terraformPlanShape } from "../plan-shape";
|
|
154
|
+
import { plannerForBinary, terraformChangeSetPart } from "../change-set";
|
|
155
|
+
import { terraformDefaultLockGrain, terraformLockGrainRefusal, terraformLockRows, terraformLockRowsFromPart } from "../lock-rows";
|
|
156
|
+
import type { LockRow } from "@intentius/chant/lifecycle/lock-rows";
|
|
157
|
+
import type { TerraformConfig } from "../config";
|
|
158
|
+
import { rootTexts } from "../compose/hcl";
|
|
159
|
+
import { declaredVariables } from "../compose/root";
|
|
160
|
+
import { terraformComposeGroup } from "./compose-group";
|
|
161
|
+
import { markPlaceholderDriven, placeholderVars, waitingHole } from "./placeholder-preview";
|
|
162
|
+
import { terraformReleaseInputs } from "./release-inputs";
|
|
163
|
+
import { terraformTraceFacts } from "./trace-facts";
|
|
164
|
+
import { DEFAULT_LOCK_TIMEOUT, isLockTimeout } from "../lock-timeout";
|
|
165
|
+
import { conePlanDigest, type PlanCone } from "../cone";
|
|
166
|
+
import { fillStateReads, stateReadsProblems, waitingReadsReason, type TerraformStateRead } from "../state-reads";
|
|
167
|
+
import { terraformStateOpCapabilities } from "../state-ops";
|
|
81
168
|
|
|
82
169
|
/** The `terraform-apply` step's input. */
|
|
83
170
|
export interface TerraformApplyStepInput {
|
|
@@ -87,6 +174,14 @@ export interface TerraformApplyStepInput {
|
|
|
87
174
|
vars?: Record<string, unknown>;
|
|
88
175
|
/** Where the `chant.config.*` search starts. Default: the current directory. */
|
|
89
176
|
cwd?: string;
|
|
177
|
+
/** The root's files from here instead of its entry's `dir` (#4542): a promote under a release sets it to the recorded copy. */
|
|
178
|
+
dir?: string;
|
|
179
|
+
/** How long `plan` and `apply` wait for a state lock (#4545). Default `"5m"`; `false` passes no flag. */
|
|
180
|
+
lockTimeout?: string | false;
|
|
181
|
+
/** choudoufu live roots (#4545): plan the cone of the change since this revision. */
|
|
182
|
+
cone?: string;
|
|
183
|
+
/** Vars read from other roots' outputs (#4545), Atmos's `!terraform.state`. The step's `vars` win over them. */
|
|
184
|
+
reads?: TerraformStateRead[];
|
|
90
185
|
}
|
|
91
186
|
|
|
92
187
|
/** What `terraform-apply` returns. `outputs` is what a dependent's `stackOutput()` reads. */
|
|
@@ -103,9 +198,53 @@ export interface TerraformApplyStepOutput {
|
|
|
103
198
|
interface PlannedRoot {
|
|
104
199
|
planFile: string;
|
|
105
200
|
planDigest: string;
|
|
201
|
+
/** The plan's `show -json`, beside the plan file (#4545), for a cost hold to price. */
|
|
202
|
+
planJson?: string;
|
|
203
|
+
/** The cone the plan covered, when asked for one (#4545). */
|
|
204
|
+
cone?: PlanCone;
|
|
205
|
+
/** A choudoufu root's rows on the resource lock grain (#4552), from the plan's `show -json`. */
|
|
206
|
+
lockRows?: LockRow[];
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** The lock timeout a step's plan and apply pass (#4545). */
|
|
210
|
+
function lockTimeoutOf(input: TerraformApplyStepInput): { lockTimeout?: string } {
|
|
211
|
+
if (input.lockTimeout === false) return {};
|
|
212
|
+
const value = input.lockTimeout ?? DEFAULT_LOCK_TIMEOUT;
|
|
213
|
+
if (!isLockTimeout(value)) throw new Error(`terraform-apply: root "${input.root}": lockTimeout "${String(value)}" is not a duration such as "5m"`);
|
|
214
|
+
return { lockTimeout: value };
|
|
106
215
|
}
|
|
107
216
|
|
|
108
|
-
|
|
217
|
+
/**
|
|
218
|
+
* The step's input with its `reads` filled (#4545): each upstream's outputs
|
|
219
|
+
* read once, the values under the step's own `vars`. `waiting` when an
|
|
220
|
+
* upstream has no such output yet. Throws when a state cannot be read.
|
|
221
|
+
*/
|
|
222
|
+
async function withReads(input: TerraformApplyStepInput): Promise<{ input: TerraformApplyStepInput } | { waiting: string; upstreams: string[] }> {
|
|
223
|
+
const reads = input.reads ?? [];
|
|
224
|
+
if (reads.length === 0) return { input };
|
|
225
|
+
const problems = stateReadsProblems(reads);
|
|
226
|
+
if (problems.length > 0) throw new Error(`terraform-apply: root "${input.root}": ${problems.join("; ")}`);
|
|
227
|
+
const filled = await fillStateReads(reads, async (root) => {
|
|
228
|
+
const at = { root, ...(input.cwd ? { cwd: input.cwd } : {}) };
|
|
229
|
+
await terraformInit(at);
|
|
230
|
+
return terraformOutputs(at);
|
|
231
|
+
});
|
|
232
|
+
if (filled.errors.length > 0) throw new Error(`terraform-apply: root "${input.root}": ${filled.errors.join("; ")}`);
|
|
233
|
+
if (filled.waiting.length > 0) return { waiting: waitingReadsReason(filled.waiting), upstreams: [...new Set(filled.waiting.map((w) => w.read.root))].sort() };
|
|
234
|
+
return { input: { ...input, vars: { ...filled.vars, ...(input.vars ?? {}) } } };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** A root whose reads wait on an upstream (#4545): a hole naming it, and a digest over the root and those upstreams. */
|
|
238
|
+
function waitingOnReads(ctx: DeployContext, input: TerraformApplyStepInput, waiting: { waiting: string; upstreams: string[] }): CapabilityPlan {
|
|
239
|
+
const planDigest = computePlanDigest("terraform-apply-reads-waiting", { env: ctx.env, root: input.root, upstreams: waiting.upstreams });
|
|
240
|
+
const changeSet: ChangeSetPart = {
|
|
241
|
+
member: { member: input.root, lexicon: "terraform", planner: "chant", status: "planned", planDigest, holes: [{ address: input.root, type: "terraform_root", reason: waiting.waiting }] },
|
|
242
|
+
entries: [],
|
|
243
|
+
};
|
|
244
|
+
return { member: input.root, planDigest, changeSet };
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const rootArgs = (input: TerraformApplyStepInput) => ({ root: input.root, ...(input.cwd ? { cwd: input.cwd } : {}), ...(input.dir ? { dir: input.dir } : {}) });
|
|
109
248
|
|
|
110
249
|
function requireRoot(input: TerraformApplyStepInput): void {
|
|
111
250
|
if (typeof input.root !== "string" || input.root === "") {
|
|
@@ -116,15 +255,34 @@ function requireRoot(input: TerraformApplyStepInput): void {
|
|
|
116
255
|
async function planRoot(input: TerraformApplyStepInput, targets?: string[]): Promise<PlannedRoot & { changeSet?: ChangeSetPart; json?: unknown }> {
|
|
117
256
|
requireRoot(input);
|
|
118
257
|
await terraformInit(rootArgs(input));
|
|
258
|
+
// One `show -json` per plan (#4479): its document is the digest, the change
|
|
259
|
+
// set, the planned outputs and the shape. Nothing here reads the
|
|
260
|
+
// `-no-color` render, so the plan skips it.
|
|
119
261
|
const planned = await terraformPlan({
|
|
120
262
|
...rootArgs(input),
|
|
121
263
|
...(input.vars ? { vars: input.vars } : {}),
|
|
122
264
|
...(targets && targets.length > 0 ? { targets } : {}),
|
|
265
|
+
...lockTimeoutOf(input),
|
|
266
|
+
...(input.cone && !(targets && targets.length > 0) ? { cone: input.cone } : {}),
|
|
267
|
+
text: false,
|
|
123
268
|
});
|
|
269
|
+
// #4545: the cone's targets join the digest, so an approval of this cone counts for no other.
|
|
270
|
+
const planDigest = conePlanDigest(planned.planDigest, planned.cone);
|
|
271
|
+
const changeSet = planned.changeSet && planDigest !== planned.planDigest ? { ...planned.changeSet, member: { ...planned.changeSet.member, planDigest } } : planned.changeSet;
|
|
272
|
+
// #4545: the plan document beside the plan file, which a wave's cost hold prices.
|
|
273
|
+
const planJson = join(planned.dir, `${planned.planFile}.json`);
|
|
274
|
+
let wrote = true;
|
|
275
|
+
try {
|
|
276
|
+
writeFileSync(planJson, JSON.stringify(planned.json));
|
|
277
|
+
} catch {
|
|
278
|
+
wrote = false;
|
|
279
|
+
}
|
|
124
280
|
return {
|
|
125
281
|
planFile: planned.planFile,
|
|
126
|
-
planDigest
|
|
127
|
-
...(
|
|
282
|
+
planDigest,
|
|
283
|
+
...(wrote ? { planJson } : {}),
|
|
284
|
+
...(planned.cone ? { cone: planned.cone } : {}),
|
|
285
|
+
...(changeSet ? { changeSet } : {}),
|
|
128
286
|
json: planned.json,
|
|
129
287
|
};
|
|
130
288
|
}
|
|
@@ -164,12 +322,39 @@ export function terraformPlannedOutputs(json: unknown): PlannedOutputs {
|
|
|
164
322
|
}
|
|
165
323
|
|
|
166
324
|
/**
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
325
|
+
* What a choudoufu member's set plan says about its outputs (#4455), for
|
|
326
|
+
* `member-outputs`: {@link terraformPlannedOutputs} of the embedded plan,
|
|
327
|
+
* chant's digest of that plan, and whether it has changes to apply.
|
|
328
|
+
*/
|
|
329
|
+
export function choudoufuMemberPlannedOutputs(set: { planDigest: string; changed: boolean; json: unknown }): EnginePlannedOutputs {
|
|
330
|
+
return { ...terraformPlannedOutputs(set.json), planDigest: set.planDigest, changes: set.changed };
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const memberArgs = (ref: EngineMemberRef) => ({ member: ref.member, dir: ref.dir, projectRoot: ref.workspaceRoot });
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* The choudoufu engine's member outputs (#4455): what core's
|
|
337
|
+
* `member-outputs` reads a workspace member of kind `choudoufu` through.
|
|
338
|
+
* Applied values from the member's root outputs (the record store, for a
|
|
339
|
+
* live estate); planned values from its one-root set plan.
|
|
170
340
|
*/
|
|
171
|
-
|
|
172
|
-
|
|
341
|
+
export const choudoufuOutputSource: EngineOutputSource = {
|
|
342
|
+
kinds: ["choudoufu"],
|
|
343
|
+
applied: (_ctx, ref) => choudoufuMemberOutputs(memberArgs(ref)),
|
|
344
|
+
planned: async (_ctx, ref) => choudoufuMemberPlannedOutputs(await choudoufuMemberPlanSet(memberArgs(ref))),
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
const resolvedVars = (input: TerraformApplyStepInput): Record<string, unknown> =>
|
|
348
|
+
Object.fromEntries(Object.entries(input.vars ?? {}).map(([k, v]) => [k, v === undefined ? null : v]));
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* The member of a root whose `vars` read outputs not known yet and that
|
|
352
|
+
* could not be previewed (#3999, #4463): no plan, a hole naming whose outputs
|
|
353
|
+
* it waits on and, when a preview was tried, why it was not possible, and a
|
|
354
|
+
* digest over the root, the vars as resolved and those names.
|
|
355
|
+
*/
|
|
356
|
+
function waitingRoot(ctx: DeployContext, input: TerraformApplyStepInput, unresolved: string[], noPreview?: string): CapabilityPlan {
|
|
357
|
+
const vars = resolvedVars(input);
|
|
173
358
|
const planDigest = computePlanDigest("terraform-apply-unresolved", { env: ctx.env, root: input.root, vars, unresolved });
|
|
174
359
|
const changeSet: ChangeSetPart = {
|
|
175
360
|
member: {
|
|
@@ -178,19 +363,112 @@ function waitingRoot(ctx: DeployContext, input: TerraformApplyStepInput, unresol
|
|
|
178
363
|
planner: "chant",
|
|
179
364
|
status: "planned",
|
|
180
365
|
planDigest,
|
|
181
|
-
holes: [
|
|
366
|
+
holes: [waitingHole(input.root, unresolved, noPreview)],
|
|
182
367
|
},
|
|
183
368
|
entries: [],
|
|
184
369
|
};
|
|
185
370
|
return { member: input.root, planDigest, changeSet, ...(ctx.targets?.length ? { targets: ctx.targets } : {}) };
|
|
186
371
|
}
|
|
187
372
|
|
|
373
|
+
/** The plan file a preview writes, removed once it is read: a plan made with stand-ins must never apply. */
|
|
374
|
+
export const PREVIEW_PLAN_FILE = ".chant-preview.tfplan";
|
|
375
|
+
|
|
376
|
+
/** What a previewed root hands `run` (#4463): the shape its plan had with stand-in values. Never a plan file. */
|
|
377
|
+
export interface TerraformShapeArtifact extends PlanShapeArtifact {
|
|
378
|
+
kind: "terraform-apply-shape";
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function isShapeArtifact(value: unknown): value is TerraformShapeArtifact {
|
|
382
|
+
const v = value as Partial<TerraformShapeArtifact> | undefined;
|
|
383
|
+
return v?.kind === "terraform-apply-shape" && typeof v.digest === "string" && typeof v.shape === "object" && v.shape !== null;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/** The root's directory, as `terraform.roots` names it from the nearest `chant.config.*`, and the configured binary. */
|
|
387
|
+
async function rootDir(input: TerraformApplyStepInput): Promise<{ dir: string; binary?: string } | undefined> {
|
|
388
|
+
const start = resolve(input.cwd ?? process.cwd());
|
|
389
|
+
const { config, configPath } = await loadChantConfigUpward(start);
|
|
390
|
+
const terraform = (config as { terraform?: TerraformConfig }).terraform;
|
|
391
|
+
const declared = terraform?.roots?.[input.root]?.dir;
|
|
392
|
+
if (declared === undefined) return undefined;
|
|
393
|
+
const dir = input.dir ?? declared;
|
|
394
|
+
return { dir: resolve(configPath ? dirname(configPath) : start, dir), ...(terraform?.binary ? { binary: terraform.binary } : {}) };
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const firstLines = (err: unknown): string => {
|
|
398
|
+
const text = (err instanceof Error ? err.message : String(err)).trim();
|
|
399
|
+
const lines = text.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
400
|
+
return lines.slice(0, 4).join(" ").slice(0, 600);
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Preview a root whose `vars` read outputs not known yet (#4463): plan it
|
|
405
|
+
* with a typed stand-in per such variable, mark what the stand-ins drive
|
|
406
|
+
* unknown, and answer the change set, the planned outputs and the shape of
|
|
407
|
+
* that plan, with the shape as the artifact. The plan file is removed. When
|
|
408
|
+
* no preview can be made, the member stays a hole saying why.
|
|
409
|
+
*/
|
|
410
|
+
async function previewRoot(ctx: DeployContext, input: TerraformApplyStepInput, unresolved: string[], targets?: string[]): Promise<CapabilityPlan> {
|
|
411
|
+
const root = await rootDir(input);
|
|
412
|
+
if (root === undefined) return waitingRoot(ctx, input, unresolved, `no root named "${input.root}" in terraform.roots to read its variables from`);
|
|
413
|
+
const { dir } = root;
|
|
414
|
+
// #4457: the same files the binary reads (`.tofu` only for tofu and choudoufu).
|
|
415
|
+
const stand = placeholderVars(input.vars, declaredVariables(rootTexts(dir, root.binary)));
|
|
416
|
+
if (typeof stand === "string") return waitingRoot(ctx, input, unresolved, stand);
|
|
417
|
+
let planned: Awaited<ReturnType<typeof terraformPlan>>;
|
|
418
|
+
try {
|
|
419
|
+
await terraformInit(rootArgs(input));
|
|
420
|
+
planned = await terraformPlan({
|
|
421
|
+
...rootArgs(input),
|
|
422
|
+
vars: stand.vars,
|
|
423
|
+
planFile: PREVIEW_PLAN_FILE,
|
|
424
|
+
...(targets ? { targets } : {}),
|
|
425
|
+
text: false,
|
|
426
|
+
});
|
|
427
|
+
} catch (err) {
|
|
428
|
+
return waitingRoot(ctx, input, unresolved, `the plan with stand-in values failed: ${firstLines(err)}`);
|
|
429
|
+
} finally {
|
|
430
|
+
rmSync(join(dir, PREVIEW_PLAN_FILE), { force: true });
|
|
431
|
+
}
|
|
432
|
+
const marked = markPlaceholderDriven(planned.json, stand.placeholders);
|
|
433
|
+
if ("refused" in marked) return waitingRoot(ctx, input, unresolved, marked.refused);
|
|
434
|
+
const placeholders = stand.placeholders.map((name) => `var.${name}`);
|
|
435
|
+
const shape = terraformPlanShape(marked.plan, input.root, { unknownInputs: unresolved });
|
|
436
|
+
const artifact: TerraformShapeArtifact = { kind: "terraform-apply-shape", member: input.root, shape, digest: planShapeDigest(shape) };
|
|
437
|
+
const part = terraformChangeSetPart({
|
|
438
|
+
member: input.root,
|
|
439
|
+
plan: marked.plan,
|
|
440
|
+
...(planned.changeSet?.member.planner ? { planner: planned.changeSet.member.planner } : {}),
|
|
441
|
+
...(planned.changeSet?.member.scope ? { scope: planned.changeSet.member.scope } : {}),
|
|
442
|
+
});
|
|
443
|
+
// The stand-ins' values are not real, so the digest is over the preview's shape and entries, never the raw plan.
|
|
444
|
+
const planDigest = computePlanDigest("terraform-apply-preview", {
|
|
445
|
+
env: ctx.env,
|
|
446
|
+
root: input.root,
|
|
447
|
+
vars: resolvedVars(input),
|
|
448
|
+
unresolved,
|
|
449
|
+
placeholders,
|
|
450
|
+
shape: artifact.digest,
|
|
451
|
+
entries: part.entries,
|
|
452
|
+
});
|
|
453
|
+
const changeSet: ChangeSetPart = { ...part, member: { ...part.member, planDigest, placeholders } };
|
|
454
|
+
return {
|
|
455
|
+
member: input.root,
|
|
456
|
+
planDigest,
|
|
457
|
+
artifact,
|
|
458
|
+
changeSet,
|
|
459
|
+
outputs: terraformPlannedOutputs(marked.plan),
|
|
460
|
+
shape,
|
|
461
|
+
...(targets ? { targets } : {}),
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
|
|
188
465
|
/**
|
|
189
|
-
* choudoufu refused the plan `run` was handed by its wave (its exit 3)
|
|
190
|
-
* the live system moved since the plan
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
466
|
+
* choudoufu refused the plan `run` was handed by its wave (its exit 3)
|
|
467
|
+
* because the live system moved since the plan. Nothing was applied.
|
|
468
|
+
* `planMoved` is core's mark for a moved plan (`isPlanMovedError`), so the
|
|
469
|
+
* command that applied it exits `CHANT_EXIT.moved`, 4, as for the other
|
|
470
|
+
* capabilities that refuse a moved plan (#3997, #4553). A plan made against
|
|
471
|
+
* another estate is a plain failure (`terraformExitOf` in ../exit-codes).
|
|
194
472
|
*/
|
|
195
473
|
export class TerraformPlanMovedError extends Error {
|
|
196
474
|
readonly planMoved = true;
|
|
@@ -200,9 +478,6 @@ export class TerraformPlanMovedError extends Error {
|
|
|
200
478
|
}
|
|
201
479
|
}
|
|
202
480
|
|
|
203
|
-
/** `live-wave-apply`'s exit status for a set whose plan moved (`CHOUDOUFU_WAVE_EXIT.setMoved`). */
|
|
204
|
-
const CHOUDOUFU_SET_MOVED_EXIT = 3;
|
|
205
|
-
|
|
206
481
|
function isPlannedRoot(value: unknown): value is PlannedRoot {
|
|
207
482
|
const v = value as Partial<PlannedRoot> | undefined;
|
|
208
483
|
return typeof v?.planFile === "string" && typeof v.planDigest === "string";
|
|
@@ -212,6 +487,8 @@ function isPlannedRoot(value: unknown): value is PlannedRoot {
|
|
|
212
487
|
interface ChoudoufuWavePlannedRoot {
|
|
213
488
|
planDigest: string;
|
|
214
489
|
choudoufu: { planSetFile: string; setDigest: string };
|
|
490
|
+
/** The root's rows on the resource lock grain (#4552), from its embedded plan. */
|
|
491
|
+
lockRows?: LockRow[];
|
|
215
492
|
}
|
|
216
493
|
|
|
217
494
|
function isChoudoufuWavePlan(value: unknown): value is ChoudoufuWavePlannedRoot {
|
|
@@ -238,13 +515,28 @@ async function runChoudoufuWave(
|
|
|
238
515
|
if (applied.carry) ctx.carry?.(input.root, applied.carry);
|
|
239
516
|
if (!applied.landed) {
|
|
240
517
|
const message = `terraform-apply: root "${input.root}": ${applied.message}`;
|
|
241
|
-
if (applied.exitCode
|
|
518
|
+
if (terraformPlanMoved({ command: "live-wave-apply", code: applied.exitCode })) throw new TerraformPlanMovedError(input.root, message);
|
|
242
519
|
throw new Error(message);
|
|
243
520
|
}
|
|
244
521
|
const outputs = await terraformOutputs(rootArgs(input));
|
|
245
522
|
return { root: input.root, planDigest: planned.planDigest, fromWavePlan: true, outputs };
|
|
246
523
|
}
|
|
247
524
|
|
|
525
|
+
/** The input a `run` that plans for itself plans with (#4545): its reads filled, or an error naming the upstream to apply first. */
|
|
526
|
+
async function readsForRun(input: TerraformApplyStepInput): Promise<TerraformApplyStepInput> {
|
|
527
|
+
const read = await withReads(input);
|
|
528
|
+
if ("waiting" in read) throw new Error(`terraform-apply: root "${input.root}": ${read.waiting}`);
|
|
529
|
+
return read.input;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/** The engine a root plans with (#4552): the configured binary's, else the plan's planner. */
|
|
533
|
+
async function plannerOf(input: TerraformApplyStepInput, plan?: CapabilityPlan): Promise<"terraform" | "tofu" | "choudoufu" | undefined> {
|
|
534
|
+
const binary = (await rootDir(input).catch(() => undefined))?.binary;
|
|
535
|
+
if (binary !== undefined) return plannerForBinary(binary) as "terraform" | "tofu" | "choudoufu";
|
|
536
|
+
const planner = plan?.changeSet?.member.planner;
|
|
537
|
+
return planner === "terraform" || planner === "tofu" || planner === "choudoufu" ? planner : "terraform";
|
|
538
|
+
}
|
|
539
|
+
|
|
248
540
|
export const terraformApplyCapability: Capability<TerraformApplyStepInput, TerraformApplyStepOutput> = {
|
|
249
541
|
kind: "terraform-apply",
|
|
250
542
|
// A root's apply has no undo short of applying the previous configuration,
|
|
@@ -252,28 +544,36 @@ export const terraformApplyCapability: Capability<TerraformApplyStepInput, Terra
|
|
|
252
544
|
rollbackPolicy: "needs-opt-out",
|
|
253
545
|
// #4300: `--target` passes through as `-target`, and the saved plan holds those addresses alone.
|
|
254
546
|
targets: "accept",
|
|
255
|
-
async plan(ctx: DeployContext,
|
|
256
|
-
requireRoot(
|
|
547
|
+
async plan(ctx: DeployContext, given: TerraformApplyStepInput): Promise<CapabilityPlan> {
|
|
548
|
+
requireRoot(given);
|
|
549
|
+
// #4545: a root reading an upstream's output that is not there yet is a hole naming the upstream.
|
|
550
|
+
const read = await withReads(given);
|
|
551
|
+
if ("waiting" in read) return waitingOnReads(ctx, given, read);
|
|
552
|
+
const { input } = read;
|
|
257
553
|
const unresolved = [...(ctx.unresolvedInputs ?? [])].sort();
|
|
258
|
-
if (unresolved.length > 0) return waitingRoot(ctx, input, unresolved);
|
|
259
554
|
const targets = ctx.targets && ctx.targets.length > 0 ? ctx.targets : undefined;
|
|
260
|
-
|
|
261
|
-
|
|
555
|
+
if (unresolved.length > 0) return previewRoot(ctx, input, unresolved, targets);
|
|
556
|
+
// A targeted root plans with `plan -target`, and a cone plan with `plan -cone` (#4545), never as a choudoufu one-root set.
|
|
557
|
+
const wave = targets || input.cone ? { wave: false } : await choudoufuWaveRoot({ ...rootArgs(input), ...(input.vars ? { vars: input.vars } : {}) });
|
|
262
558
|
if (wave.wave) {
|
|
263
559
|
const set = await choudoufuPlanSet(rootArgs(input));
|
|
264
560
|
const artifact: ChoudoufuWavePlannedRoot = {
|
|
265
561
|
planDigest: set.planDigest,
|
|
266
562
|
choudoufu: { planSetFile: set.planSetFile, setDigest: set.setDigest },
|
|
563
|
+
lockRows: terraformLockRows(input.root, set.changeSet.member.scope, set.json),
|
|
267
564
|
};
|
|
268
|
-
return { member: input.root, planDigest: set.planDigest, artifact, changeSet: set.changeSet, outputs: terraformPlannedOutputs(set.json) };
|
|
565
|
+
return { member: input.root, planDigest: set.planDigest, artifact, changeSet: set.changeSet, outputs: terraformPlannedOutputs(set.json), shape: terraformPlanShape(set.json, input.root) };
|
|
269
566
|
}
|
|
270
567
|
const { changeSet, json, ...planned } = await planRoot(input, targets);
|
|
568
|
+
// #4552: a choudoufu root's rows, keyed from the plan document while it is at hand.
|
|
569
|
+
const lockRows = changeSet?.member.planner === "choudoufu" ? terraformLockRows(input.root, changeSet.member.scope, json) : undefined;
|
|
271
570
|
return {
|
|
272
571
|
member: input.root,
|
|
273
572
|
planDigest: planned.planDigest,
|
|
274
|
-
artifact: planned,
|
|
573
|
+
artifact: { ...planned, ...(lockRows ? { lockRows } : {}) },
|
|
275
574
|
...(changeSet ? { changeSet } : {}),
|
|
276
575
|
outputs: terraformPlannedOutputs(json),
|
|
576
|
+
shape: terraformPlanShape(json, input.root),
|
|
277
577
|
...(targets ? { targets } : {}),
|
|
278
578
|
};
|
|
279
579
|
},
|
|
@@ -284,20 +584,62 @@ export const terraformApplyCapability: Capability<TerraformApplyStepInput, Terra
|
|
|
284
584
|
await terraformInit(rootArgs(input));
|
|
285
585
|
return terraformOutputs(rootArgs(input));
|
|
286
586
|
},
|
|
587
|
+
// #4447 (ws-107): a group core forms of components that read each other plans and applies as one composed run
|
|
588
|
+
// when `terraform.compose.enabled` is set (./compose-group.ts). Delegates, so the methods are read when called.
|
|
589
|
+
groupReads: (ctx, members) => terraformComposeGroup.groupReads(ctx, members),
|
|
590
|
+
// #4542: a member pointed at a release's recorded copy plans alone, since a composition reads the declared roots.
|
|
591
|
+
groupFallback: async (ctx, members) =>
|
|
592
|
+
members.some((m) => typeof m.step.dir === "string")
|
|
593
|
+
? { kind: "release-copy", reason: "a member deploys a release's recorded copy of its root, which a composition does not read" }
|
|
594
|
+
: terraformComposeGroup.groupFallback(ctx, members),
|
|
595
|
+
// #4542: the root's files, which a release cut records and a promote under it plans.
|
|
596
|
+
releaseInputs: (input, ctx) => terraformReleaseInputs(input, ctx),
|
|
597
|
+
// #4552: resource under choudoufu, the root under Terraform, tofu and Terragrunt, which refuse resource by name.
|
|
598
|
+
async defaultLockGrain(input, plan) {
|
|
599
|
+
return terraformDefaultLockGrain(await plannerOf(input, plan));
|
|
600
|
+
},
|
|
601
|
+
async lockGrainRefusal(input, grain, plan) {
|
|
602
|
+
if (grain !== "resource") return undefined;
|
|
603
|
+
const binary = (await rootDir(input).catch(() => undefined))?.binary;
|
|
604
|
+
return terraformLockGrainRefusal(grain, await plannerOf(input, plan), binary);
|
|
605
|
+
},
|
|
606
|
+
lockRows(input, plan) {
|
|
607
|
+
const kept = (plan.artifact as { lockRows?: LockRow[] } | undefined)?.lockRows;
|
|
608
|
+
if (Array.isArray(kept)) return kept;
|
|
609
|
+
return plan.changeSet ? terraformLockRowsFromPart(input.root, plan.changeSet) : [];
|
|
610
|
+
},
|
|
611
|
+
// #4555: the root and the binary, for the step's span and run record.
|
|
612
|
+
traceFacts: async (input, outcome) => terraformTraceFacts(input, outcome, (await rootDir(input).catch(() => undefined))?.binary),
|
|
613
|
+
planGroup: (ctx, members) => terraformComposeGroup.planGroup(ctx, members),
|
|
614
|
+
runGroup: (ctx, run, members) => terraformComposeGroup.runGroup(ctx, run, members),
|
|
287
615
|
async run(ctx: DeployContext, input: TerraformApplyStepInput): Promise<TerraformApplyStepOutput> {
|
|
288
616
|
const fromWave = ctx.plans?.[input.root];
|
|
289
617
|
if (isChoudoufuWavePlan(fromWave)) return runChoudoufuWave(ctx, input, fromWave);
|
|
618
|
+
if (isShapeArtifact(fromWave)) {
|
|
619
|
+
// A root previewed with stand-ins (#4463): plan it now with the real
|
|
620
|
+
// values, and apply that plan only when it is within the preview's shape.
|
|
621
|
+
const fresh = await planRoot(await readsForRun(input), ctx.targets);
|
|
622
|
+
const differences = planShapeDifferences(fromWave.shape, terraformPlanShape(fresh.json, input.root));
|
|
623
|
+
if (differences.length > 0) throw new PlanShapeMovedError(input.root, differences);
|
|
624
|
+
const applied = await terraformApply({ ...rootArgs(input), planFile: fresh.planFile, ...lockTimeoutOf(input) });
|
|
625
|
+
if (!applied.applied) {
|
|
626
|
+
throw new Error(`terraform-apply: root "${input.root}" refused the plan it was given (${applied.refused ?? "refused"}). ${applied.refusal ?? ""}`);
|
|
627
|
+
}
|
|
628
|
+
const outputs = await terraformOutputs(rootArgs(input));
|
|
629
|
+
return { root: input.root, planDigest: fresh.planDigest, fromWavePlan: false, outputs };
|
|
630
|
+
}
|
|
290
631
|
const planned: PlannedRoot = isPlannedRoot(fromWave)
|
|
291
632
|
? fromWave
|
|
292
|
-
: await planRoot(input, ctx.targets).then(({ planFile, planDigest }) => ({ planFile, planDigest }));
|
|
293
|
-
const applied = await terraformApply({ ...rootArgs(input), planFile: planned.planFile });
|
|
633
|
+
: await planRoot(await readsForRun(input), ctx.targets).then(({ planFile, planDigest }) => ({ planFile, planDigest }));
|
|
634
|
+
const applied = await terraformApply({ ...rootArgs(input), planFile: planned.planFile, ...lockTimeoutOf(input) });
|
|
294
635
|
if (!applied.applied) {
|
|
295
636
|
const message =
|
|
296
637
|
`terraform-apply: root "${input.root}" refused the plan it was given (${applied.refused ?? "refused"}). ` +
|
|
297
638
|
(applied.refusal ?? "");
|
|
298
|
-
// A plan the wave approved that choudoufu refuses
|
|
299
|
-
//
|
|
300
|
-
|
|
639
|
+
// A plan the wave approved that choudoufu refuses as no longer matching
|
|
640
|
+
// moved since the approval (#4553: one made against another estate is a
|
|
641
|
+
// failure). One this run made itself is planned afresh by the next run.
|
|
642
|
+
if (applied.refused === "approval-mismatch" && isPlannedRoot(fromWave)) throw new TerraformPlanMovedError(input.root, message);
|
|
301
643
|
throw new Error(message);
|
|
302
644
|
}
|
|
303
645
|
const outputs = await terraformOutputs(rootArgs(input));
|
|
@@ -307,6 +649,9 @@ export const terraformApplyCapability: Capability<TerraformApplyStepInput, Terra
|
|
|
307
649
|
|
|
308
650
|
export const TERRAFORM_VERB_FAMILIES = {
|
|
309
651
|
apply: ["terraform-apply"],
|
|
652
|
+
// #4618: the binary runs outside waves, each a fixed operation; the two that change state on their own run under a gated request.
|
|
653
|
+
state: ["terraform-state-unlock", "terraform-state-push"],
|
|
654
|
+
copy: ["terraform-copy"],
|
|
310
655
|
} as const;
|
|
311
656
|
|
|
312
657
|
export const terraformCapabilityPlugin: CapabilityPlugin = {
|
|
@@ -315,9 +660,12 @@ export const terraformCapabilityPlugin: CapabilityPlugin = {
|
|
|
315
660
|
return ownPackageVersion(import.meta.url);
|
|
316
661
|
},
|
|
317
662
|
capabilities(): Array<Capability<never, unknown>> {
|
|
318
|
-
return [terraformApplyCapability as Capability<never, unknown>];
|
|
663
|
+
return [terraformApplyCapability as Capability<never, unknown>, ...terraformStateOpCapabilities.map((c) => c as unknown as Capability<never, unknown>)];
|
|
319
664
|
},
|
|
320
665
|
families(): Record<string, readonly string[]> {
|
|
321
666
|
return TERRAFORM_VERB_FAMILIES;
|
|
322
667
|
},
|
|
668
|
+
engineOutputSources(): EngineOutputSource[] {
|
|
669
|
+
return [choudoufuOutputSource];
|
|
670
|
+
},
|
|
323
671
|
};
|
|
@@ -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
|
});
|