@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
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The expansion forms #4489 counts, and the rollup reason on every block the
|
|
3
|
+
* declared graph still leaves as one node. The fixture is
|
|
4
|
+
* `src/__fixtures__/instance-forms/app/`, built as `chant graph --format ir`
|
|
5
|
+
* builds a terraform root's graph.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { dirname, join } from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
import { beforeAll, describe, expect, it } from "vitest";
|
|
11
|
+
import { buildGraphIr, withInstanceNodes, type GraphIR, type IRNode } from "@intentius/chant/graph-ir";
|
|
12
|
+
import { INSTANCE_ROLLUP_CODES } from "@intentius/chant/instance-nodes";
|
|
13
|
+
import type { Declarable } from "@intentius/chant/declarable";
|
|
14
|
+
import { renderTerraformRoots } from "./roots";
|
|
15
|
+
|
|
16
|
+
const TREE = join(dirname(fileURLToPath(import.meta.url)), "..", "__fixtures__", "instance-forms");
|
|
17
|
+
|
|
18
|
+
let entities: Map<string, Declarable>;
|
|
19
|
+
let ir: GraphIR;
|
|
20
|
+
|
|
21
|
+
beforeAll(async () => {
|
|
22
|
+
const rendered = await renderTerraformRoots({ projectRoot: TREE, roots: { estate: { dir: "./app" } } });
|
|
23
|
+
entities = rendered.entities;
|
|
24
|
+
ir = withInstanceNodes(buildGraphIr(entities), entities);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
function instancesOf(block: string): string[] {
|
|
28
|
+
return ir.nodes.filter((n) => n.instanceOf === block).map((n) => n.id).sort();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function node(id: string): IRNode {
|
|
32
|
+
const n = ir.nodes.find((x) => x.id === id);
|
|
33
|
+
if (!n) throw new Error(`no node ${id}`);
|
|
34
|
+
return n;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
describe("forms counted since #4489", () => {
|
|
38
|
+
it("counts `count = local.x` over a literal local", () => {
|
|
39
|
+
expect(instancesOf("estate/aws_eip.local_count")).toEqual(["estate/aws_eip.local_count[0]", "estate/aws_eip.local_count[1]"]);
|
|
40
|
+
expect(node("estate/aws_eip.local_count").instanceRollup).toBeUndefined();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("counts `count = length(local.x)`", () => {
|
|
44
|
+
expect(instancesOf("estate/aws_sqs_queue.local_length")).toHaveLength(3);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("keys `for_each = toset(local.l)` and `for_each = local.m`", () => {
|
|
48
|
+
expect(instancesOf("estate/aws_s3_bucket.local_set")).toEqual(['estate/aws_s3_bucket.local_set["p"]', 'estate/aws_s3_bucket.local_set["q"]']);
|
|
49
|
+
expect(instancesOf("estate/aws_lb_listener.local_map")).toEqual(['estate/aws_lb_listener.local_map["http"]', 'estate/aws_lb_listener.local_map["https"]']);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("counts `var.on ? 1 : 0` over a known bool, and a local that reads one", () => {
|
|
53
|
+
expect(instancesOf("estate/aws_sns_topic.enabled")).toEqual(["estate/aws_sns_topic.enabled[0]"]);
|
|
54
|
+
expect(instancesOf("estate/aws_sns_topic.disabled")).toEqual([]);
|
|
55
|
+
expect(node("estate/aws_sns_topic.disabled").instanceRollup).toBeUndefined();
|
|
56
|
+
expect(instancesOf("estate/aws_sns_topic.local_flag")).toHaveLength(2);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe("forms still rolled up, with the node's reason", () => {
|
|
61
|
+
it.each([
|
|
62
|
+
["estate/aws_sns_topic.flag_unknown", "instances-value-unknown", "count", "var.flag ? 1 : 0"],
|
|
63
|
+
["estate/aws_sns_topic.local_unknown", "instances-value-unknown", "count", "local.computed"],
|
|
64
|
+
["estate/aws_iam_group.each", "instances-each-value", "for_each", "each.value"],
|
|
65
|
+
["estate/aws_iam_policy.arith", "instances-form-unread", "count", "local.replicas + 1"],
|
|
66
|
+
["estate/aws_eip.too_many", "instances-over-limit", "count", "300"],
|
|
67
|
+
["estate/module.svc", "instances-value-unknown", "count", "var.unknown"],
|
|
68
|
+
])("%s is one node with %s", (id, code, argument, expression) => {
|
|
69
|
+
expect(instancesOf(id)).toEqual([]);
|
|
70
|
+
expect(node(id).instanceRollup).toEqual({ code, argument, expression });
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("names merge() and a for expression", () => {
|
|
74
|
+
expect(node("estate/aws_iam_user.merged").instanceRollup).toMatchObject({ code: "instances-merge", argument: "for_each" });
|
|
75
|
+
expect(node("estate/aws_iam_user.merged").instanceRollup?.expression).toMatch(/^merge\(/);
|
|
76
|
+
expect(node("estate/aws_iam_role.for_expr").instanceRollup).toMatchObject({ code: "instances-form-unread", argument: "for_each" });
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("names the uncounted module call on a block under it", () => {
|
|
80
|
+
expect(instancesOf("estate/module.svc/aws_iam_role.runner")).toEqual([]);
|
|
81
|
+
expect(node("estate/module.svc/aws_iam_role.runner").instanceRollup).toEqual({
|
|
82
|
+
code: "instances-module-uncounted",
|
|
83
|
+
argument: "count",
|
|
84
|
+
expression: "var.unknown",
|
|
85
|
+
call: "module.svc",
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("uses only the listed codes", () => {
|
|
90
|
+
for (const n of ir.nodes) {
|
|
91
|
+
if (n.instanceRollup) expect(INSTANCE_ROLLUP_CODES).toContain(n.instanceRollup.code);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
});
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Instance nodes counted from variables, and under expanded module calls
|
|
3
|
+
* (chant #4486).
|
|
4
|
+
*
|
|
5
|
+
* The fixture is `src/__fixtures__/instance-vars/app/`, rendered through
|
|
6
|
+
* `renderTerraformRoots`, then core's `buildGraphIr` and `withInstanceNodes`,
|
|
7
|
+
* which is how `chant graph --format ir` builds a terraform root's graph.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { dirname, join } from "node:path";
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
12
|
+
import { beforeAll, describe, expect, it } from "vitest";
|
|
13
|
+
import { buildGraphIr, withInstanceNodes, type GraphIR } from "@intentius/chant/graph-ir";
|
|
14
|
+
import { graphNodeFor } from "@intentius/chant/change-set-graph";
|
|
15
|
+
import { terraformInstancePath } from "@intentius/chant/instance-nodes";
|
|
16
|
+
import type { Declarable } from "@intentius/chant/declarable";
|
|
17
|
+
import { renderTerraformRoots } from "./roots";
|
|
18
|
+
|
|
19
|
+
const TREE = join(dirname(fileURLToPath(import.meta.url)), "..", "__fixtures__", "instance-vars");
|
|
20
|
+
|
|
21
|
+
let entities: Map<string, Declarable>;
|
|
22
|
+
let ir: GraphIR;
|
|
23
|
+
|
|
24
|
+
beforeAll(async () => {
|
|
25
|
+
const rendered = await renderTerraformRoots({ projectRoot: TREE, roots: { estate: { dir: "./app" } } });
|
|
26
|
+
expect(rendered.warnings).toEqual([]);
|
|
27
|
+
entities = rendered.entities;
|
|
28
|
+
ir = withInstanceNodes(buildGraphIr(entities), entities);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
/** The instance nodes under one block node, as `[id, instanceKey, instanceScope]`. */
|
|
32
|
+
function instancesOf(block: string): Array<[string, string | null, string | null]> {
|
|
33
|
+
return ir.nodes
|
|
34
|
+
.filter((n) => n.instanceOf === block)
|
|
35
|
+
.map((n) => [n.id, n.instanceKey ?? null, n.instanceScope ?? null] as [string, string | null, string | null])
|
|
36
|
+
.sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
describe("count and for_each resolved from variables", () => {
|
|
40
|
+
it("counts `count = var.n` from terraform.tfvars over the default", () => {
|
|
41
|
+
expect(instancesOf("estate/aws_eip.pool")).toEqual([
|
|
42
|
+
["estate/aws_eip.pool[0]", "[0]", null],
|
|
43
|
+
["estate/aws_eip.pool[1]", "[1]", null],
|
|
44
|
+
["estate/aws_eip.pool[2]", "[2]", null],
|
|
45
|
+
]);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("keys `for_each = var.names` over a set variable's default", () => {
|
|
49
|
+
expect(instancesOf("estate/aws_s3_bucket.named").map((i) => i[0])).toEqual([
|
|
50
|
+
'estate/aws_s3_bucket.named["a"]',
|
|
51
|
+
'estate/aws_s3_bucket.named["b"]',
|
|
52
|
+
]);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("counts `count = length(var.zones)`", () => {
|
|
56
|
+
expect(instancesOf("estate/aws_sqs_queue.zoned").map((i) => i[0])).toEqual([
|
|
57
|
+
"estate/aws_sqs_queue.zoned[0]",
|
|
58
|
+
"estate/aws_sqs_queue.zoned[1]",
|
|
59
|
+
]);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("leaves a block whose variable has no known value as one node", () => {
|
|
63
|
+
expect(instancesOf("estate/aws_sns_topic.uncounted")).toEqual([]);
|
|
64
|
+
expect(ir.nodes.some((n) => n.id === "estate/aws_sns_topic.uncounted")).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe("instances under an expanded module call", () => {
|
|
69
|
+
it("gives the module call its own instance nodes", () => {
|
|
70
|
+
expect(instancesOf("estate/module.app")).toEqual([
|
|
71
|
+
["estate/module.app[0]", "[0]", null],
|
|
72
|
+
["estate/module.app[1]", "[1]", null],
|
|
73
|
+
]);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("puts the module key on the call segment and the block key at the end", () => {
|
|
77
|
+
// `size = var.n` passes the root's 3 into the child's `count = var.size`.
|
|
78
|
+
expect(instancesOf("estate/module.app/aws_instance.web")).toEqual([
|
|
79
|
+
["estate/module.app[0]/aws_instance.web[0]", "[0]", "module.app[0]"],
|
|
80
|
+
["estate/module.app[0]/aws_instance.web[1]", "[1]", "module.app[0]"],
|
|
81
|
+
["estate/module.app[0]/aws_instance.web[2]", "[2]", "module.app[0]"],
|
|
82
|
+
["estate/module.app[1]/aws_instance.web[0]", "[0]", "module.app[1]"],
|
|
83
|
+
["estate/module.app[1]/aws_instance.web[1]", "[1]", "module.app[1]"],
|
|
84
|
+
["estate/module.app[1]/aws_instance.web[2]", "[2]", "module.app[1]"],
|
|
85
|
+
]);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("gives a block that is not expanded itself one instance per module instance, with no instanceKey", () => {
|
|
89
|
+
expect(instancesOf("estate/module.app/aws_iam_role.runner")).toEqual([
|
|
90
|
+
["estate/module.app[0]/aws_iam_role.runner", null, "module.app[0]"],
|
|
91
|
+
["estate/module.app[1]/aws_iam_role.runner", null, "module.app[1]"],
|
|
92
|
+
]);
|
|
93
|
+
expect(instancesOf("estate/module.edge/aws_route53_record.front")).toEqual([
|
|
94
|
+
['estate/module.edge["east"]/aws_route53_record.front', null, 'module.edge["east"]'],
|
|
95
|
+
['estate/module.edge["west"]/aws_route53_record.front', null, 'module.edge["west"]'],
|
|
96
|
+
]);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("keeps the block node as the rollup, in the same groups", () => {
|
|
100
|
+
expect(ir.nodes.some((n) => n.id === "estate/module.app/aws_instance.web" && !n.instanceOf)).toBe(true);
|
|
101
|
+
expect(ir.groups.byStack?.estate).toEqual(
|
|
102
|
+
expect.arrayContaining(["estate/module.app/aws_instance.web", "estate/module.app[1]/aws_instance.web[2]"]),
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("places a plan address on the same id the graph lists", () => {
|
|
107
|
+
const graph = { member: null, at: null, ids: ir.nodes.map((n) => n.id) };
|
|
108
|
+
expect(graphNodeFor({ member: "estate", planner: "terraform", address: "module.app[1].aws_instance.web[2]" }, graph)).toEqual({
|
|
109
|
+
node: "estate/module.app[1]/aws_instance.web[2]",
|
|
110
|
+
instanceOf: "estate/module.app/aws_instance.web",
|
|
111
|
+
});
|
|
112
|
+
expect(graphNodeFor({ member: "estate", planner: "tofu", address: 'module.edge["east"].aws_route53_record.front' }, graph)).toEqual({
|
|
113
|
+
node: 'estate/module.edge["east"]/aws_route53_record.front',
|
|
114
|
+
instanceOf: "estate/module.edge/aws_route53_record.front",
|
|
115
|
+
});
|
|
116
|
+
for (const n of ir.nodes.filter((x) => x.instanceOf)) expect(graph.ids).toContain(n.id);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe("terraformInstancePath", () => {
|
|
121
|
+
it("maps an address to its block key and instance key", () => {
|
|
122
|
+
expect(terraformInstancePath("aws_eip.pool[1]")).toEqual({ block: "aws_eip.pool", instance: "aws_eip.pool[1]", key: "[1]" });
|
|
123
|
+
expect(terraformInstancePath("aws_eip.pool")).toBeUndefined();
|
|
124
|
+
expect(terraformInstancePath("module.app[0].aws_eip.pool[1]")).toEqual({
|
|
125
|
+
block: "module.app/aws_eip.pool",
|
|
126
|
+
instance: "module.app[0]/aws_eip.pool[1]",
|
|
127
|
+
key: "[1]",
|
|
128
|
+
scope: "module.app[0]",
|
|
129
|
+
});
|
|
130
|
+
expect(terraformInstancePath('module.a["x.y"].module.b.data.http.site')).toEqual({
|
|
131
|
+
block: "module.a/module.b/data.http.site",
|
|
132
|
+
instance: 'module.a["x.y"]/module.b/data.http.site',
|
|
133
|
+
scope: 'module.a["x.y"].module.b',
|
|
134
|
+
});
|
|
135
|
+
expect(terraformInstancePath("module.app[0]")).toEqual({ block: "module.app", instance: "module.app[0]", key: "[0]" });
|
|
136
|
+
});
|
|
137
|
+
});
|
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Instance nodes the declared graph can count from variables, and instance
|
|
3
|
+
* nodes under expanded module calls (chant #4486).
|
|
4
|
+
*
|
|
5
|
+
* `./parse.ts` gives a block its `instanceKeys` when its `count` or
|
|
6
|
+
* `for_each` is a literal (#4454). This pass runs once a root and its child
|
|
7
|
+
* modules are parsed and adds two cases that need the whole root:
|
|
8
|
+
*
|
|
9
|
+
* - An expansion that reads a variable, `count = var.n`, gets its keys when
|
|
10
|
+
* the variable's value is known from the files a plan would read: the
|
|
11
|
+
* `variable` block's `default`, then `terraform.tfvars`,
|
|
12
|
+
* `terraform.tfvars.json`, `*.auto.tfvars(.json)` in filename order, then
|
|
13
|
+
* `terraform.roots.<name>.varFiles`, later over earlier. In a child module
|
|
14
|
+
* the value is the call's argument, when it is a literal or a `var.x`
|
|
15
|
+
* known in the caller, else the child variable's default when the call
|
|
16
|
+
* passes nothing. `TF_CLI_ARGS` is not read, so the declared graph does not
|
|
17
|
+
* change with the environment the build runs in.
|
|
18
|
+
* - A block under a module call that is itself expanded gets one instance
|
|
19
|
+
* per module instance (times its own instances), with the module key on
|
|
20
|
+
* the call's segment: `estate/module.app[0]/aws_eip.pool[1]`. The entity
|
|
21
|
+
* lists those as `instances` (full ids), which core's `withInstanceNodes`
|
|
22
|
+
* reads in place of `instanceKeys` (`@intentius/chant/instance-nodes`
|
|
23
|
+
* holds the id rule). A module call with a countable expansion gets
|
|
24
|
+
* instance nodes of its own, `estate/module.app[0]`.
|
|
25
|
+
*
|
|
26
|
+
* The forms read are `var.n`, `length(var.x)`, `var.m` (a map, or a set
|
|
27
|
+
* variable) for `for_each`, and `toset(var.l)`; since #4489 the same with
|
|
28
|
+
* `local.<x>` in place of `var.<x>`, where the local's value is a literal or
|
|
29
|
+
* one of the `var` forms in its scope, and `var.on ? 1 : 0` (any two whole
|
|
30
|
+
* numbers, and `local.on` too) over a known bool. Anything else, or any
|
|
31
|
+
* value that is unknown, leaves the block as one node, which is what it
|
|
32
|
+
* was, and the entity gets an `instanceRollup` saying why (#4489, the codes
|
|
33
|
+
* in `@intentius/chant/instance-nodes`). `each.value` and `merge()` stay
|
|
34
|
+
* rolled up by ruling. A block under an expanded call whose own expansion
|
|
35
|
+
* or whose call's expansion cannot be counted gets no instances at all,
|
|
36
|
+
* since a partial list would name ids the plan never prints.
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
40
|
+
import { join, resolve } from "node:path";
|
|
41
|
+
import { isResourceDeclarable, type Declarable } from "@intentius/chant/declarable";
|
|
42
|
+
import type { InstanceRollup, InstanceRollupCode } from "@intentius/chant/instance-nodes";
|
|
43
|
+
import { loadHcl2json, type Hcl2Json } from "@intentius/chant/terraform/hcl2json";
|
|
44
|
+
import {
|
|
45
|
+
DATA_TYPE,
|
|
46
|
+
LOCALS_TYPE,
|
|
47
|
+
MODULE_TYPE,
|
|
48
|
+
RESOURCE_TYPE,
|
|
49
|
+
VARIABLE_TYPE,
|
|
50
|
+
callersOfKey,
|
|
51
|
+
literalInstanceKeys,
|
|
52
|
+
scopeOfKey,
|
|
53
|
+
type BlockBody,
|
|
54
|
+
type TerraformEntity,
|
|
55
|
+
} from "./parse";
|
|
56
|
+
|
|
57
|
+
/** The most instance nodes one block gets; past it the block draws alone. */
|
|
58
|
+
const MAX_INSTANCES = 256;
|
|
59
|
+
|
|
60
|
+
const NAME = "[A-Za-z_][A-Za-z0-9_-]*";
|
|
61
|
+
const INTERPOLATION_RE = /^\$\{\s*([\s\S]*?)\s*\}$/;
|
|
62
|
+
/** `var.<x>` or `local.<x>` (#4489): the kind and the name. */
|
|
63
|
+
const REF = `(var|local)\\.(${NAME})`;
|
|
64
|
+
const REF_RE = new RegExp(`^${REF}$`);
|
|
65
|
+
const LENGTH_RE = new RegExp(`^length\\(\\s*${REF}\\s*\\)$`);
|
|
66
|
+
const TOSET_RE = new RegExp(`^toset\\(\\s*${REF}\\s*\\)$`);
|
|
67
|
+
/** `var.on ? 1 : 0` (#4489): a bool reference choosing between two whole numbers. */
|
|
68
|
+
const COND_RE = new RegExp(`^${REF}\\s*\\?\\s*(\\d+)\\s*:\\s*(\\d+)$`);
|
|
69
|
+
const EACH_RE = /\beach\.(?:value|key)\b/;
|
|
70
|
+
const MERGE_RE = /\bmerge\s*\(/;
|
|
71
|
+
|
|
72
|
+
/** A variable's known value, and whether it is declared as a set. */
|
|
73
|
+
interface VarValue {
|
|
74
|
+
value: unknown;
|
|
75
|
+
set: boolean;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Variable name to known value, for one module scope. A name that is absent is unknown. */
|
|
79
|
+
type ScopeVars = Map<string, VarValue>;
|
|
80
|
+
|
|
81
|
+
/** A scope's known `var` and `local` values (#4489). */
|
|
82
|
+
interface Scope {
|
|
83
|
+
vars: ScopeVars;
|
|
84
|
+
locals: ScopeVars;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const NO_LOCALS: ScopeVars = new Map();
|
|
88
|
+
|
|
89
|
+
/** An instance node an entity lists: full id, its own key and its scope, as core's `entityInstances` reads them. */
|
|
90
|
+
interface ListedInstance {
|
|
91
|
+
id: string;
|
|
92
|
+
key?: string;
|
|
93
|
+
scope?: string;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface ResolveInstancesOptions {
|
|
97
|
+
/** The root's directory, where its tfvars files sit. */
|
|
98
|
+
dir: string;
|
|
99
|
+
/** `terraform.roots.<name>.varFiles`, relative to `dir`. */
|
|
100
|
+
varFiles?: readonly string[];
|
|
101
|
+
hcl2json?: Hcl2Json;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Whether a value still holds an expression a read of the files cannot evaluate. */
|
|
105
|
+
function holdsExpression(value: unknown): boolean {
|
|
106
|
+
if (typeof value === "string") return value.includes("${");
|
|
107
|
+
if (Array.isArray(value)) return value.some(holdsExpression);
|
|
108
|
+
if (typeof value === "object" && value !== null) {
|
|
109
|
+
return Object.entries(value).some(([k, v]) => k.includes("${") || holdsExpression(v));
|
|
110
|
+
}
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** A literal value as it stands, or undefined when it holds an expression. */
|
|
115
|
+
function literal(value: unknown): unknown {
|
|
116
|
+
return value === undefined || holdsExpression(value) ? undefined : value;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Evaluate an argument in a scope: a literal, a bare `var.x` or `local.x`,
|
|
121
|
+
* `length(...)` or `toset(...)` of one, or `var.on ? 1 : 0`. Undefined when
|
|
122
|
+
* unknown.
|
|
123
|
+
*/
|
|
124
|
+
function evaluate(value: unknown, vars: ScopeVars, locals: ScopeVars = NO_LOCALS): VarValue | undefined {
|
|
125
|
+
if (typeof value !== "string" || !value.includes("${")) {
|
|
126
|
+
const v = literal(value);
|
|
127
|
+
return v === undefined ? undefined : { value: v, set: false };
|
|
128
|
+
}
|
|
129
|
+
const m = INTERPOLATION_RE.exec(value.trim());
|
|
130
|
+
if (!m) return undefined;
|
|
131
|
+
const inner = m[1]!;
|
|
132
|
+
if (/^\d+$/.test(inner)) return { value: Number(inner), set: false };
|
|
133
|
+
const ref = (kind: string, name: string): VarValue | undefined => (kind === "var" ? vars : locals).get(name);
|
|
134
|
+
let r: RegExpExecArray | null;
|
|
135
|
+
if ((r = REF_RE.exec(inner))) return ref(r[1]!, r[2]!);
|
|
136
|
+
if ((r = COND_RE.exec(inner))) {
|
|
137
|
+
const on = ref(r[1]!, r[2]!)?.value;
|
|
138
|
+
return typeof on === "boolean" ? { value: Number(on ? r[3] : r[4]), set: false } : undefined;
|
|
139
|
+
}
|
|
140
|
+
if ((r = LENGTH_RE.exec(inner))) {
|
|
141
|
+
const v = ref(r[1]!, r[2]!)?.value;
|
|
142
|
+
if (Array.isArray(v)) return { value: v.length, set: false };
|
|
143
|
+
if (typeof v === "object" && v !== null) return { value: Object.keys(v).length, set: false };
|
|
144
|
+
if (typeof v === "string") return { value: [...v].length, set: false };
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
if ((r = TOSET_RE.exec(inner))) {
|
|
148
|
+
const v = ref(r[1]!, r[2]!)?.value;
|
|
149
|
+
return Array.isArray(v) ? { value: v, set: true } : undefined;
|
|
150
|
+
}
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** The expression a `count` or `for_each` holds, as written, without the `${ }` around it. */
|
|
155
|
+
function expressionText(value: unknown): string {
|
|
156
|
+
if (typeof value === "string") return (INTERPOLATION_RE.exec(value.trim())?.[1] ?? value).trim();
|
|
157
|
+
return JSON.stringify(value) ?? String(value);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Why an expansion the graph could not count stays rolled up (#4489). */
|
|
161
|
+
function rollupCodeOf(value: unknown): InstanceRollupCode {
|
|
162
|
+
if (typeof value !== "string" || !value.includes("${")) return "instances-value-unknown";
|
|
163
|
+
const text = expressionText(value);
|
|
164
|
+
if (EACH_RE.test(text)) return "instances-each-value";
|
|
165
|
+
if (MERGE_RE.test(text)) return "instances-merge";
|
|
166
|
+
const m = INTERPOLATION_RE.exec(value.trim());
|
|
167
|
+
if (m && [/^\d+$/, REF_RE, LENGTH_RE, TOSET_RE, COND_RE].some((re) => re.test(m[1]!))) return "instances-value-unknown";
|
|
168
|
+
return "instances-form-unread";
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* A block's expansion in a scope: its instance keys as Terraform prints them,
|
|
173
|
+
* `null` when the block is not expanded, or why the keys are not known.
|
|
174
|
+
*/
|
|
175
|
+
export function expansionIn(body: BlockBody, scope: Scope): { keys: string[] } | { rollup: InstanceRollup } | null {
|
|
176
|
+
if (!("count" in body) && !("for_each" in body)) return null;
|
|
177
|
+
const argument = "count" in body ? "count" : "for_each";
|
|
178
|
+
const expr = body[argument];
|
|
179
|
+
const rolled = (code: InstanceRollupCode): { rollup: InstanceRollup } => ({ rollup: { code, argument, expression: expressionText(expr) } });
|
|
180
|
+
const lit = literalInstanceKeys(body);
|
|
181
|
+
if (lit) return { keys: lit };
|
|
182
|
+
let keys: string[] | undefined;
|
|
183
|
+
if (argument === "count") {
|
|
184
|
+
const v = evaluate(expr, scope.vars, scope.locals)?.value;
|
|
185
|
+
if (typeof v === "number" && Number.isInteger(v) && v >= 0) keys = Array.from({ length: v }, (_, i) => `[${i}]`);
|
|
186
|
+
} else {
|
|
187
|
+
const v = evaluate(expr, scope.vars, scope.locals);
|
|
188
|
+
if (v && Array.isArray(v.value)) {
|
|
189
|
+
if (v.set && v.value.every((s) => typeof s === "string")) {
|
|
190
|
+
keys = [...new Set(v.value as string[])].sort().map((k) => `[${JSON.stringify(k)}]`);
|
|
191
|
+
}
|
|
192
|
+
} else if (v && typeof v.value === "object" && v.value !== null) {
|
|
193
|
+
keys = Object.keys(v.value).sort().map((k) => `[${JSON.stringify(k)}]`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (keys === undefined) return rolled(rollupCodeOf(expr));
|
|
197
|
+
if (keys.length > MAX_INSTANCES) return rolled("instances-over-limit");
|
|
198
|
+
return { keys };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* The instance keys a block's `count` or `for_each` gives in `vars`, as
|
|
203
|
+
* Terraform prints them. `null` when the block is not expanded, undefined
|
|
204
|
+
* when it is and the keys are not known.
|
|
205
|
+
*/
|
|
206
|
+
export function instanceKeysIn(body: BlockBody, vars: ScopeVars, locals: ScopeVars = NO_LOCALS): string[] | null | undefined {
|
|
207
|
+
const e = expansionIn(body, { vars, locals });
|
|
208
|
+
return e === null ? null : "keys" in e ? e.keys : undefined;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** A root's tfvars files, in the order a plan reads them, later over earlier. */
|
|
212
|
+
function tfvarsFiles(dir: string, varFiles: readonly string[]): string[] {
|
|
213
|
+
let names: string[] = [];
|
|
214
|
+
try {
|
|
215
|
+
names = readdirSync(dir).filter((n) => statSync(join(dir, n)).isFile());
|
|
216
|
+
} catch {
|
|
217
|
+
return [...varFiles];
|
|
218
|
+
}
|
|
219
|
+
return [
|
|
220
|
+
...["terraform.tfvars", "terraform.tfvars.json"].filter((n) => names.includes(n)),
|
|
221
|
+
...names.filter((n) => n.endsWith(".auto.tfvars") || n.endsWith(".auto.tfvars.json")).sort(),
|
|
222
|
+
...varFiles,
|
|
223
|
+
];
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** The values a root's tfvars files set, by variable name. A file that cannot be read or parsed sets nothing. */
|
|
227
|
+
async function rootTfvars(opts: ResolveInstancesOptions): Promise<Map<string, unknown>> {
|
|
228
|
+
const out = new Map<string, unknown>();
|
|
229
|
+
let parser: Hcl2Json | undefined = opts.hcl2json;
|
|
230
|
+
for (const file of tfvarsFiles(opts.dir, opts.varFiles ?? [])) {
|
|
231
|
+
let text: string;
|
|
232
|
+
try {
|
|
233
|
+
text = readFileSync(resolve(opts.dir, file), "utf-8");
|
|
234
|
+
} catch {
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
let tree: Record<string, unknown>;
|
|
238
|
+
try {
|
|
239
|
+
if (file.endsWith(".json")) tree = JSON.parse(text) as Record<string, unknown>;
|
|
240
|
+
else {
|
|
241
|
+
parser ??= await loadHcl2json();
|
|
242
|
+
tree = (await parser.parse(file, text)) as Record<string, unknown>;
|
|
243
|
+
}
|
|
244
|
+
} catch {
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
for (const [k, v] of Object.entries(tree)) out.set(k, v);
|
|
248
|
+
}
|
|
249
|
+
return out;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function bodyOf(entity: TerraformEntity): BlockBody {
|
|
253
|
+
const body = (entity.props as Partial<TerraformEntity["props"]>).body;
|
|
254
|
+
return (typeof body === "object" && body !== null ? body : {}) as BlockBody;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function isSetType(body: BlockBody): boolean {
|
|
258
|
+
return typeof body.type === "string" && /^\$\{\s*set\(/.test(body.type);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Add instance nodes the declared graph can count to one root's entities, in
|
|
263
|
+
* place (#4486). Entities of other roots in the map are left alone.
|
|
264
|
+
*/
|
|
265
|
+
export async function resolveInstanceNodes(
|
|
266
|
+
entities: Map<string, Declarable>,
|
|
267
|
+
root: string,
|
|
268
|
+
opts: ResolveInstancesOptions,
|
|
269
|
+
): Promise<void> {
|
|
270
|
+
const own = [...entities].filter(
|
|
271
|
+
([key, e]) => (key === root || key.startsWith(`${root}/`)) && isResourceDeclarable(e),
|
|
272
|
+
) as Array<[string, TerraformEntity]>;
|
|
273
|
+
if (own.length === 0) return;
|
|
274
|
+
|
|
275
|
+
// Variable declarations per scope.
|
|
276
|
+
const declared = new Map<string, Map<string, TerraformEntity>>();
|
|
277
|
+
for (const [key, e] of own) {
|
|
278
|
+
if (e.entityType !== VARIABLE_TYPE) continue;
|
|
279
|
+
const address = e.props.address;
|
|
280
|
+
if (!address.startsWith("var.")) continue;
|
|
281
|
+
const scope = scopeOfKey(key);
|
|
282
|
+
(declared.get(scope) ?? declared.set(scope, new Map()).get(scope)!).set(address.slice(4), e);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Local values per scope (#4489): every `locals` block's names.
|
|
286
|
+
const localBodies = new Map<string, Map<string, unknown>>();
|
|
287
|
+
for (const [key, e] of own) {
|
|
288
|
+
if (e.entityType !== LOCALS_TYPE) continue;
|
|
289
|
+
const scope = scopeOfKey(key);
|
|
290
|
+
const into = localBodies.get(scope) ?? localBodies.set(scope, new Map()).get(scope)!;
|
|
291
|
+
for (const [name, value] of Object.entries(bodyOf(e))) into.set(name, value);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const tfvars = await rootTfvars(opts);
|
|
295
|
+
const scopes = new Map<string, ScopeVars>();
|
|
296
|
+
const localScopes = new Map<string, ScopeVars>();
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* A scope's known local values: a literal, or a `var` form over the
|
|
300
|
+
* scope's variables. A local that reads another local is unknown.
|
|
301
|
+
*/
|
|
302
|
+
const localsOf = (scope: string): ScopeVars => {
|
|
303
|
+
const cached = localScopes.get(scope);
|
|
304
|
+
if (cached) return cached;
|
|
305
|
+
const out: ScopeVars = new Map();
|
|
306
|
+
localScopes.set(scope, out);
|
|
307
|
+
for (const [name, value] of localBodies.get(scope) ?? []) {
|
|
308
|
+
const v = evaluate(value, varsOf(scope));
|
|
309
|
+
if (v !== undefined) out.set(name, v);
|
|
310
|
+
}
|
|
311
|
+
return out;
|
|
312
|
+
};
|
|
313
|
+
const scopeOf = (scope: string): Scope => ({ vars: varsOf(scope), locals: localsOf(scope) });
|
|
314
|
+
|
|
315
|
+
/** A scope's known variable values, computed once, outermost first. */
|
|
316
|
+
const varsOf = (scope: string): ScopeVars => {
|
|
317
|
+
const cached = scopes.get(scope);
|
|
318
|
+
if (cached) return cached;
|
|
319
|
+
const vars: ScopeVars = new Map();
|
|
320
|
+
scopes.set(scope, vars);
|
|
321
|
+
const decls = declared.get(scope) ?? new Map<string, TerraformEntity>();
|
|
322
|
+
if (scope === root) {
|
|
323
|
+
for (const [name, decl] of decls) {
|
|
324
|
+
const body = bodyOf(decl);
|
|
325
|
+
const set = isSetType(body);
|
|
326
|
+
const v = tfvars.has(name) ? literal(tfvars.get(name)) : literal(body.default);
|
|
327
|
+
if (v !== undefined) vars.set(name, { value: v, set });
|
|
328
|
+
}
|
|
329
|
+
return vars;
|
|
330
|
+
}
|
|
331
|
+
const call = entities.get(scope) as TerraformEntity | undefined;
|
|
332
|
+
const parent = scopeOfKey(scope);
|
|
333
|
+
const callBody = call ? bodyOf(call) : {};
|
|
334
|
+
const parentVars = varsOf(parent);
|
|
335
|
+
for (const [name, decl] of decls) {
|
|
336
|
+
const body = bodyOf(decl);
|
|
337
|
+
const set = isSetType(body);
|
|
338
|
+
if (name in callBody) {
|
|
339
|
+
const v = evaluate(callBody[name], parentVars, localsOf(parent));
|
|
340
|
+
if (v !== undefined) vars.set(name, { value: v.value, set: set || v.set });
|
|
341
|
+
} else {
|
|
342
|
+
const v = literal(body.default);
|
|
343
|
+
if (v !== undefined) vars.set(name, { value: v, set });
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return vars;
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
/** A module call's expansion, by the call entity's key; a call that is not a module call is not known. */
|
|
350
|
+
const callExpansion = (callKey: string): ReturnType<typeof expansionIn> | undefined => {
|
|
351
|
+
const call = entities.get(callKey) as TerraformEntity | undefined;
|
|
352
|
+
if (!call || call.entityType !== MODULE_TYPE) return undefined;
|
|
353
|
+
return expansionIn(bodyOf(call), scopeOf(scopeOfKey(callKey)));
|
|
354
|
+
};
|
|
355
|
+
const keysOf = (e: ReturnType<typeof expansionIn> | undefined): string[] | null | undefined =>
|
|
356
|
+
e === undefined ? undefined : e === null ? null : "keys" in e ? e.keys : undefined;
|
|
357
|
+
|
|
358
|
+
for (const [key, entity] of own) {
|
|
359
|
+
if (entity.entityType !== RESOURCE_TYPE && entity.entityType !== DATA_TYPE && entity.entityType !== MODULE_TYPE) continue;
|
|
360
|
+
const callers = callersOfKey(key);
|
|
361
|
+
const calls = callers.map((_, i) => callExpansion(`${root}/${callers.slice(0, i + 1).join("/")}`));
|
|
362
|
+
const chain: Array<string[] | null | undefined> = calls.map(keysOf);
|
|
363
|
+
const own = expansionIn(bodyOf(entity), scopeOf(scopeOfKey(key)));
|
|
364
|
+
const ownKeys = keysOf(own);
|
|
365
|
+
const ownRollup = own !== null && "rollup" in own ? own.rollup : undefined;
|
|
366
|
+
const expandedChain = chain.some((k) => k !== null);
|
|
367
|
+
|
|
368
|
+
if (!expandedChain) {
|
|
369
|
+
if (ownKeys && !entity.instanceKeys) entities.set(key, { ...entity, instanceKeys: ownKeys } as TerraformEntity);
|
|
370
|
+
else if (ownRollup) entities.set(key, { ...entity, instanceRollup: ownRollup } as TerraformEntity);
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Under an expanded module call the literal `instanceKeys` would name ids
|
|
375
|
+
// under the unexpanded call, which no plan prints: the listed instances
|
|
376
|
+
// replace them, or nothing does.
|
|
377
|
+
const { instanceKeys: _dropped, ...rest } = entity;
|
|
378
|
+
if (chain.some((k) => k === undefined) || ownKeys === undefined) {
|
|
379
|
+
// The block's own reason first, else the first call on the way that could not be counted.
|
|
380
|
+
const at = chain.findIndex((k) => k === undefined);
|
|
381
|
+
const callRollup = at === -1 ? undefined : calls[at];
|
|
382
|
+
const rollup: InstanceRollup | undefined = ownRollup
|
|
383
|
+
?? (callRollup && "rollup" in callRollup
|
|
384
|
+
? { ...callRollup.rollup, code: "instances-module-uncounted", call: callers.slice(0, at + 1).join(".") }
|
|
385
|
+
: undefined);
|
|
386
|
+
entities.set(key, (rollup ? { ...rest, instanceRollup: rollup } : rest) as TerraformEntity);
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
let combos: string[][] = [[]];
|
|
390
|
+
for (const keys of chain) combos = combos.flatMap((c) => (keys ?? [""]).map((k) => [...c, k]));
|
|
391
|
+
const tails = ownKeys ?? [""];
|
|
392
|
+
if (combos.length * tails.length > MAX_INSTANCES) {
|
|
393
|
+
// Named on the block's own expansion, or on the first expanded call when the block has none.
|
|
394
|
+
const body = bodyOf(entity);
|
|
395
|
+
const at = chain.findIndex((k) => k !== null);
|
|
396
|
+
const on = ownKeys !== null || at === -1 ? body : bodyOf(entities.get(`${root}/${callers.slice(0, at + 1).join("/")}`) as TerraformEntity);
|
|
397
|
+
const argument = "count" in on ? "count" : "for_each";
|
|
398
|
+
const rollup: InstanceRollup = {
|
|
399
|
+
code: "instances-over-limit",
|
|
400
|
+
argument,
|
|
401
|
+
expression: expressionText(on[argument]),
|
|
402
|
+
...(on === body ? {} : { call: callers.slice(0, at + 1).join(".") }),
|
|
403
|
+
};
|
|
404
|
+
entities.set(key, { ...rest, instanceRollup: rollup } as TerraformEntity);
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
const address = entity.props.address;
|
|
408
|
+
const instances: ListedInstance[] = [];
|
|
409
|
+
for (const combo of combos) {
|
|
410
|
+
const segments = callers.map((c, i) => `${c}${combo[i]}`);
|
|
411
|
+
const scope = segments.join(".");
|
|
412
|
+
for (const tail of tails) {
|
|
413
|
+
instances.push({
|
|
414
|
+
id: `${root}/${segments.join("/")}/${address}${tail}`,
|
|
415
|
+
...(tail ? { key: tail } : {}),
|
|
416
|
+
scope,
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
entities.set(key, { ...rest, instances } as TerraformEntity);
|
|
421
|
+
}
|
|
422
|
+
}
|