@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,455 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drift-record adapters for the terraform family (#4451): a stock root's
|
|
3
|
+
* state read and plan, and a choudoufu live root's `live-plan -json`,
|
|
4
|
+
* `live-ls -json` and plan, each into `@intentius/chant/drift-record`
|
|
5
|
+
* records.
|
|
6
|
+
*
|
|
7
|
+
* | source | read | status |
|
|
8
|
+
* |---|---|---|
|
|
9
|
+
* | `show -json` over state | `state` | in state: recorded. Declared and not in state: pending |
|
|
10
|
+
* | `show -json` of a saved plan | `plan` | no `resource_drift` row: in-sync. Drift update: drifted with paths. Drift delete: missing. Create with no prior row: pending. Delete with no drift: undeclared |
|
|
11
|
+
* | `live-plan -json` | `ownership` | `bound[]`: in-sync, owned by its `source`. `unowned[]` and `adoptable[]`: unclaimed. `ABSENT`: pending. Any other omission: not-observed. `foreign[]`: undeclared, foreign. `controller_held[]`: runtime |
|
|
12
|
+
* | `live-ls -json` | `ownership` | `declared: false`: undeclared. A gap no live-plan row answers: not-observed |
|
|
13
|
+
* | live-plan and a plan together | `plan` | bound with a planned update or replace: drifted with paths, the live-plan ownership kept |
|
|
14
|
+
*
|
|
15
|
+
* A state read never says in-sync or drifted: `driftRecord` refuses it.
|
|
16
|
+
*
|
|
17
|
+
* An omission keeps its token in `evidence.engineCode`, and so does the
|
|
18
|
+
* `UNOWNED` omission that has no `unowned[]` row: chant reads it as
|
|
19
|
+
* not-observed (`read-failed`), not as unclaimed, since nothing in the
|
|
20
|
+
* document says what holds the identity.
|
|
21
|
+
*
|
|
22
|
+
* ## Values
|
|
23
|
+
*
|
|
24
|
+
* Attribute values come from `./plan-observation.ts`, which already replaced
|
|
25
|
+
* every value the planner flagged, or that equals a flagged string, with
|
|
26
|
+
* `(sensitive)`. Here a `(sensitive)` side makes the attribute `sensitive:
|
|
27
|
+
* true` with neither value, and the record constructor's key-name pass runs
|
|
28
|
+
* after. `before` is what was expected and `after` what the read found, so
|
|
29
|
+
* a stock root's drift row reads state then refreshed object, and a live
|
|
30
|
+
* root's planned update reads declared then live (the plan's after, then
|
|
31
|
+
* its before).
|
|
32
|
+
*
|
|
33
|
+
* ## The import graph
|
|
34
|
+
*
|
|
35
|
+
* Like `./change-set.ts`, this imports chant's `drift-record` subpath, the
|
|
36
|
+
* plan projection and the omission table, never the lexicon's entry point
|
|
37
|
+
* or `./describe-resources.ts`, so it bundles without TypeScript, node's
|
|
38
|
+
* process modules or the lint rules (#3421).
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
import {
|
|
42
|
+
driftRecord,
|
|
43
|
+
placeDriftRecord,
|
|
44
|
+
type DriftAdapterBase,
|
|
45
|
+
type DriftAttribute,
|
|
46
|
+
type DriftEvidence,
|
|
47
|
+
type DriftOwnership,
|
|
48
|
+
type DriftOwnershipBy,
|
|
49
|
+
type DriftRecord,
|
|
50
|
+
} from "@intentius/chant/drift-record";
|
|
51
|
+
import { LIVE_PLAN_OMISSION_REASONS, readsAsCredentials } from "./live-plan-reasons";
|
|
52
|
+
import { blockAddressOf, indexPlan, KNOWN_AFTER_APPLY, SENSITIVE_VALUE, type PlanValueChange } from "./plan-observation";
|
|
53
|
+
|
|
54
|
+
type Json = Record<string, unknown>;
|
|
55
|
+
|
|
56
|
+
const isObject = (v: unknown): v is Json => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
57
|
+
const asArray = (v: unknown): unknown[] => (Array.isArray(v) ? v : []);
|
|
58
|
+
const asString = (v: unknown): string | undefined => (typeof v === "string" && v.length > 0 ? v : undefined);
|
|
59
|
+
const byString = (a: string, b: string): number => (a < b ? -1 : a > b ? 1 : 0);
|
|
60
|
+
const byAddress = (a: DriftRecord, b: DriftRecord): number => byString(a.address, b.address);
|
|
61
|
+
|
|
62
|
+
/** True for an address inside a `data` block, which has no drift. */
|
|
63
|
+
function isDataAddress(address: string): boolean {
|
|
64
|
+
return /(^|\.)data\./.test(blockAddressOf(address));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function common(input: DriftAdapterBase, engine: DriftRecord["engine"], read: DriftRecord["read"], address: string) {
|
|
68
|
+
return {
|
|
69
|
+
member: input.member,
|
|
70
|
+
engine,
|
|
71
|
+
address,
|
|
72
|
+
...placeDriftRecord(input, engine, address),
|
|
73
|
+
observedAt: input.observedAt,
|
|
74
|
+
read,
|
|
75
|
+
...(input.fellBackFrom !== undefined ? { fellBackFrom: input.fellBackFrom } : {}),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** One projected plan change as a drift attribute. `swap` reads the change's after as expected. */
|
|
80
|
+
function attributeOf(change: PlanValueChange, swap: boolean): DriftAttribute {
|
|
81
|
+
const path = change.path;
|
|
82
|
+
if (change.before === SENSITIVE_VALUE || change.after === SENSITIVE_VALUE) return { path, sensitive: true };
|
|
83
|
+
const expected = swap ? change.after : change.before;
|
|
84
|
+
const found = swap ? change.before : change.after;
|
|
85
|
+
return {
|
|
86
|
+
path,
|
|
87
|
+
...(expected !== null && expected !== undefined && expected !== KNOWN_AFTER_APPLY ? { before: expected } : {}),
|
|
88
|
+
...(found !== null && found !== undefined && found !== KNOWN_AFTER_APPLY ? { after: found } : {}),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ── state ──────────────────────────────────────────────────────────────────────
|
|
93
|
+
|
|
94
|
+
/** Every managed resource row of a `show -json` state document: address and type. */
|
|
95
|
+
function stateRows(showJson: unknown): Map<string, { type?: string }> {
|
|
96
|
+
const rows = new Map<string, { type?: string }>();
|
|
97
|
+
const walk = (module: unknown, prefix: string): void => {
|
|
98
|
+
const node = isObject(module) ? module : {};
|
|
99
|
+
for (const entry of asArray(node.resources)) {
|
|
100
|
+
if (!isObject(entry)) continue;
|
|
101
|
+
const raw = asString(entry.address);
|
|
102
|
+
if (!raw || entry.mode === "data") continue;
|
|
103
|
+
const address = prefix && !raw.startsWith(`${prefix}.`) ? `${prefix}.${raw}` : raw;
|
|
104
|
+
rows.set(address, asString(entry.type) ? { type: asString(entry.type) } : {});
|
|
105
|
+
}
|
|
106
|
+
for (const child of asArray(node.child_modules)) {
|
|
107
|
+
if (isObject(child)) walk(child, asString(child.address) ?? prefix);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
const doc = isObject(showJson) ? showJson : {};
|
|
111
|
+
walk(isObject(doc.values) ? doc.values.root_module : undefined, "");
|
|
112
|
+
return rows;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface TerraformStateDriftInput extends DriftAdapterBase {
|
|
116
|
+
engine?: "terraform" | "tofu";
|
|
117
|
+
/** `terraform show -json` over the root's state, parsed. Absent when the read failed. */
|
|
118
|
+
state?: unknown;
|
|
119
|
+
/**
|
|
120
|
+
* The root's declared resource blocks, by terraform address
|
|
121
|
+
* (`module.cdn.null_resource.edge`). A block with no instance in state is
|
|
122
|
+
* pending. Omit to report only what state holds.
|
|
123
|
+
*/
|
|
124
|
+
declared?: readonly string[];
|
|
125
|
+
/** The read failed: every declared block is not-observed with this reason. */
|
|
126
|
+
failed?: { reason: "read-failed" | "no-credentials" | "no-binding"; detail?: string };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* A stock root's records from its state: every managed instance in state is
|
|
131
|
+
* recorded, every declared block with no instance there is pending, and a
|
|
132
|
+
* failed read makes every declared block not-observed. Never in-sync or
|
|
133
|
+
* drifted: state says what the last apply recorded, not what the account
|
|
134
|
+
* holds.
|
|
135
|
+
*/
|
|
136
|
+
export function terraformStateDriftRecords(input: TerraformStateDriftInput): DriftRecord[] {
|
|
137
|
+
const engine = input.engine ?? "terraform";
|
|
138
|
+
const evidence: DriftEvidence = { tool: engine, ...(input.version ? { version: input.version } : {}), command: `${engine} show -json` };
|
|
139
|
+
if (input.failed) {
|
|
140
|
+
return (input.declared ?? [])
|
|
141
|
+
.map((address) =>
|
|
142
|
+
driftRecord({
|
|
143
|
+
...common(input, engine, "state", address),
|
|
144
|
+
status: "not-observed",
|
|
145
|
+
unobserved: { reason: input.failed!.reason, ...(input.failed!.detail ? { detail: input.failed!.detail } : {}) },
|
|
146
|
+
evidence,
|
|
147
|
+
}),
|
|
148
|
+
)
|
|
149
|
+
.sort(byAddress);
|
|
150
|
+
}
|
|
151
|
+
const rows = stateRows(input.state);
|
|
152
|
+
const out: DriftRecord[] = [];
|
|
153
|
+
for (const [address, row] of rows) {
|
|
154
|
+
out.push(driftRecord({ ...common(input, engine, "state", address), ...(row.type ? { resourceType: row.type } : {}), status: "recorded", ownership: { verdict: "owned", by: "state" }, evidence }));
|
|
155
|
+
}
|
|
156
|
+
const blocks = new Set([...rows.keys()].map(blockAddressOf));
|
|
157
|
+
for (const address of input.declared ?? []) {
|
|
158
|
+
if (blocks.has(address) || rows.has(address)) continue;
|
|
159
|
+
out.push(driftRecord({ ...common(input, engine, "state", address), status: "pending", evidence }));
|
|
160
|
+
}
|
|
161
|
+
return out.sort(byAddress);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ── plan ───────────────────────────────────────────────────────────────────────
|
|
165
|
+
|
|
166
|
+
export interface TerraformPlanDriftInput extends DriftAdapterBase {
|
|
167
|
+
engine?: "terraform" | "tofu";
|
|
168
|
+
/** `terraform show -json <planfile>`, parsed. */
|
|
169
|
+
plan: unknown;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* A stock root's records from a saved plan's `show -json`, one per managed
|
|
174
|
+
* instance the plan names. A `resource_drift[]` row decides: update is
|
|
175
|
+
* drifted with its paths (before is state, after the refreshed object),
|
|
176
|
+
* delete is missing. With no drift row, a create with no prior-state row is
|
|
177
|
+
* pending, a delete or forget the configuration no longer declares is
|
|
178
|
+
* undeclared, and anything else is in-sync, since a planned change from
|
|
179
|
+
* configuration is the change set's to show and not drift.
|
|
180
|
+
*/
|
|
181
|
+
export function terraformPlanDriftRecords(input: TerraformPlanDriftInput): DriftRecord[] {
|
|
182
|
+
const engine = input.engine ?? "terraform";
|
|
183
|
+
const doc = isObject(input.plan) ? input.plan : {};
|
|
184
|
+
const version = input.version ?? asString(doc.terraform_version);
|
|
185
|
+
const evidence: DriftEvidence = { tool: engine, ...(version ? { version } : {}), command: `${engine} show -json` };
|
|
186
|
+
const index = indexPlan(input.plan);
|
|
187
|
+
const prior = stateRows({ values: isObject(index.priorState) ? index.priorState.values : undefined });
|
|
188
|
+
const types = new Map<string, string>();
|
|
189
|
+
for (const entry of asArray(doc.resource_changes)) {
|
|
190
|
+
if (isObject(entry) && asString(entry.address) && asString(entry.type)) types.set(entry.address as string, entry.type as string);
|
|
191
|
+
}
|
|
192
|
+
const addresses = [...new Set([...index.changes.keys(), ...index.drift.keys()])].filter((a) => !isDataAddress(a)).sort(byString);
|
|
193
|
+
return addresses.map((address) => {
|
|
194
|
+
const type = types.get(address) ?? prior.get(address)?.type;
|
|
195
|
+
const at = { ...common(input, engine, "plan", address), ...(type ? { resourceType: type } : {}), evidence };
|
|
196
|
+
const drift = index.drift.get(address);
|
|
197
|
+
if (drift?.actions.includes("delete")) return driftRecord({ ...at, status: "missing" });
|
|
198
|
+
if (drift && drift.actions.includes("update")) {
|
|
199
|
+
const attributes = drift.changes.map((c) => attributeOf(c, false));
|
|
200
|
+
return driftRecord({ ...at, status: "drifted", ...(attributes.length > 0 ? { attributes } : {}) });
|
|
201
|
+
}
|
|
202
|
+
const actions = index.changes.get(address)?.actions ?? [];
|
|
203
|
+
if (actions.length === 1 && actions[0] === "create" && !prior.has(address)) return driftRecord({ ...at, status: "pending" });
|
|
204
|
+
if (actions.length === 1 && (actions[0] === "delete" || actions[0] === "forget")) return driftRecord({ ...at, status: "undeclared" });
|
|
205
|
+
return driftRecord({ ...at, status: "in-sync" });
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// ── choudoufu live-plan ────────────────────────────────────────────────────────
|
|
210
|
+
|
|
211
|
+
const BOUND_SOURCES: readonly DriftOwnershipBy[] = ["marker", "record", "derived", "cache"];
|
|
212
|
+
|
|
213
|
+
export interface ChoudoufuLivePlanDriftInput extends DriftAdapterBase {
|
|
214
|
+
/** `choudoufu live-plan -json`, parsed. */
|
|
215
|
+
livePlan: unknown;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* A live root's records from `live-plan -json`, read `ownership`, one per
|
|
220
|
+
* address the document names.
|
|
221
|
+
*
|
|
222
|
+
* - `bound[]`: in-sync, `owned` by the row's `source`, the marker on the
|
|
223
|
+
* object exactly when the source is `marker`.
|
|
224
|
+
* - `unowned[]`: unclaimed, `adoptable` when the row carries the adopting
|
|
225
|
+
* tag values and `foreign` otherwise, with the estate it does carry. It
|
|
226
|
+
* answers before the paired `UNOWNED` omission.
|
|
227
|
+
* - `adoptable[]`: unclaimed, `adoptable`, the paired omission's token kept.
|
|
228
|
+
* - an `ABSENT` omission: pending.
|
|
229
|
+
* - any other omission, `UNOWNED` with no row included: not-observed with
|
|
230
|
+
* the reason `LIVE_PLAN_OMISSION_REASONS` gives (`VERIFIED_NOT_IMPORTABLE`
|
|
231
|
+
* read-failed, `OUT_OF_SCOPE` filtered), a credentials failure named as
|
|
232
|
+
* such.
|
|
233
|
+
* - `foreign[]`: undeclared, `foreign`, addressed by its identity.
|
|
234
|
+
* - `controller_held[]`: runtime, `foreign`, held by its controller,
|
|
235
|
+
* addressed by the marker's address when it has one and its identity
|
|
236
|
+
* otherwise.
|
|
237
|
+
*/
|
|
238
|
+
export function choudoufuLivePlanDriftRecords(input: ChoudoufuLivePlanDriftInput): DriftRecord[] {
|
|
239
|
+
const doc = isObject(input.livePlan) ? input.livePlan : {};
|
|
240
|
+
const version = input.version ?? asString(doc.choudoufu_version);
|
|
241
|
+
const evidence = (engineCode?: string): DriftEvidence => ({
|
|
242
|
+
tool: "choudoufu",
|
|
243
|
+
...(version ? { version } : {}),
|
|
244
|
+
command: "choudoufu live-plan -json",
|
|
245
|
+
...(engineCode ? { engineCode } : {}),
|
|
246
|
+
});
|
|
247
|
+
const out = new Map<string, DriftRecord>();
|
|
248
|
+
const put = (address: string, record: Omit<DriftRecord, keyof ReturnType<typeof common>>) => {
|
|
249
|
+
if (!out.has(address)) out.set(address, driftRecord({ ...common(input, "choudoufu", "ownership", address), ...record }));
|
|
250
|
+
};
|
|
251
|
+
const typed = (row: Json) => (asString(row.type) ? { resourceType: asString(row.type)! } : {});
|
|
252
|
+
|
|
253
|
+
const omissions = new Map<string, { reason: string; detail: string }>();
|
|
254
|
+
for (const row of asArray(doc.omissions).filter(isObject)) {
|
|
255
|
+
const addr = asString(row.addr);
|
|
256
|
+
if (addr) omissions.set(addr, { reason: asString(row.reason) ?? "", detail: asString(row.detail) ?? "" });
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
for (const row of asArray(doc.unowned).filter(isObject)) {
|
|
260
|
+
const addr = asString(row.addr);
|
|
261
|
+
if (!addr) continue;
|
|
262
|
+
const heldBy = asString(row.tofu_estate);
|
|
263
|
+
const adopt = asString(row.adopt_tofu_address);
|
|
264
|
+
const ownership: DriftOwnership = adopt || asString(row.adopt_tofu_estate)
|
|
265
|
+
? { verdict: "adoptable", markerOnObject: heldBy !== undefined, ...(heldBy ? { by: "marker", ownedBy: heldBy } : {}), ...(adopt ? { adopt } : {}) }
|
|
266
|
+
: { verdict: "foreign", markerOnObject: heldBy !== undefined, ...(heldBy ? { by: "marker", ownedBy: heldBy } : {}) };
|
|
267
|
+
put(addr, { ...typed(row), status: "unclaimed", ownership, evidence: evidence(omissions.get(addr)?.reason || "UNOWNED") });
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
for (const row of asArray(doc.bound).filter(isObject)) {
|
|
271
|
+
const addr = asString(row.addr);
|
|
272
|
+
if (!addr) continue;
|
|
273
|
+
const source = asString(row.source);
|
|
274
|
+
const by = BOUND_SOURCES.find((s) => s === source);
|
|
275
|
+
put(addr, { ...typed(row), status: "in-sync", ownership: { verdict: "owned", ...(by ? { by } : {}), markerOnObject: source === "marker" }, evidence: evidence() });
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
for (const row of asArray(doc.adoptable).filter(isObject)) {
|
|
279
|
+
const addr = asString(row.addr);
|
|
280
|
+
if (!addr) continue;
|
|
281
|
+
const adopt = asString(row.adopt_tofu_address);
|
|
282
|
+
put(addr, {
|
|
283
|
+
...typed(row),
|
|
284
|
+
status: "unclaimed",
|
|
285
|
+
ownership: { verdict: "adoptable", markerOnObject: false, ...(adopt ? { adopt } : {}) },
|
|
286
|
+
evidence: evidence(omissions.get(addr)?.reason),
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
for (const [addr, row] of omissions) {
|
|
291
|
+
const mapped = LIVE_PLAN_OMISSION_REASONS[row.reason] ?? "read-failed";
|
|
292
|
+
if (mapped === "absent") {
|
|
293
|
+
put(addr, { status: "pending", evidence: evidence(row.reason) });
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
const reason = mapped === "read-failed" && readsAsCredentials(row.detail) ? "no-credentials" : mapped;
|
|
297
|
+
put(addr, {
|
|
298
|
+
status: "not-observed",
|
|
299
|
+
unobserved: { reason, ...(row.detail ? { detail: row.detail } : {}) },
|
|
300
|
+
evidence: evidence(row.reason || undefined),
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
for (const row of asArray(doc.foreign).filter(isObject)) {
|
|
305
|
+
const identity = asString(row.identity);
|
|
306
|
+
if (!identity) continue;
|
|
307
|
+
const heldBy = asString(row.tofu_estate);
|
|
308
|
+
put(identity, {
|
|
309
|
+
...typed(row),
|
|
310
|
+
status: "undeclared",
|
|
311
|
+
ownership: { verdict: "foreign", markerOnObject: heldBy !== undefined, ...(heldBy ? { by: "marker", ownedBy: heldBy } : {}) },
|
|
312
|
+
evidence: evidence(),
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
for (const row of asArray(doc.controller_held).filter(isObject)) {
|
|
317
|
+
const address = asString(row.addr) ?? asString(row.identity);
|
|
318
|
+
if (!address) continue;
|
|
319
|
+
const heldBy = asString(row.held_by) ?? asString(row.controller);
|
|
320
|
+
put(address, {
|
|
321
|
+
...typed(row),
|
|
322
|
+
status: "runtime",
|
|
323
|
+
ownership: { verdict: "foreign", by: "label", ...(heldBy ? { ownedBy: heldBy } : {}) },
|
|
324
|
+
evidence: evidence(asString(row.controller)),
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return [...out.values()].sort(byAddress);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// ── choudoufu live-ls ──────────────────────────────────────────────────────────
|
|
332
|
+
|
|
333
|
+
export interface ChoudoufuLiveLsDriftInput extends DriftAdapterBase {
|
|
334
|
+
/** `choudoufu live-ls -json`, parsed. */
|
|
335
|
+
liveLs: unknown;
|
|
336
|
+
/**
|
|
337
|
+
* Addresses another read already answered (the live-plan records'), so a
|
|
338
|
+
* gap on one of them is not reported: the gap says the listing cannot
|
|
339
|
+
* reach the instance, and live-plan did.
|
|
340
|
+
*/
|
|
341
|
+
answered?: Iterable<string>;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* A live root's records from `live-ls -json`, read `ownership`. An item with
|
|
346
|
+
* `declared: false` is undeclared, addressed by its marker's address or else
|
|
347
|
+
* its identity: `owned` when its marker names this estate, held by the
|
|
348
|
+
* controller (runtime) when it has `held_by`, `foreign` when it names
|
|
349
|
+
* another estate. A declared item is live-plan's to answer and is left out.
|
|
350
|
+
* A gap is not-observed `unsupported-kind` with its rung as the token,
|
|
351
|
+
* unless `answered` holds its address.
|
|
352
|
+
*/
|
|
353
|
+
export function choudoufuLiveLsDriftRecords(input: ChoudoufuLiveLsDriftInput): DriftRecord[] {
|
|
354
|
+
const doc = isObject(input.liveLs) ? input.liveLs : {};
|
|
355
|
+
const estate = asString(doc.estate);
|
|
356
|
+
const answered = new Set(input.answered ?? []);
|
|
357
|
+
const evidence = (engineCode?: string): DriftEvidence => ({
|
|
358
|
+
tool: "choudoufu",
|
|
359
|
+
...(input.version ? { version: input.version } : {}),
|
|
360
|
+
command: "choudoufu live-ls -json",
|
|
361
|
+
...(engineCode ? { engineCode } : {}),
|
|
362
|
+
});
|
|
363
|
+
const out = new Map<string, DriftRecord>();
|
|
364
|
+
for (const row of asArray(doc.items).filter(isObject)) {
|
|
365
|
+
if (row.declared !== false) continue;
|
|
366
|
+
const address = asString(row.address) ?? asString(row.id);
|
|
367
|
+
if (!address || out.has(address)) continue;
|
|
368
|
+
const tags = isObject(row.tags) ? row.tags : {};
|
|
369
|
+
const marker = asString(tags["tofu-estate"]);
|
|
370
|
+
const heldBy = asString(row.held_by);
|
|
371
|
+
const ownership: DriftOwnership = heldBy
|
|
372
|
+
? { verdict: "foreign", by: "label", ownedBy: heldBy }
|
|
373
|
+
: marker === undefined
|
|
374
|
+
? { verdict: "unknown" }
|
|
375
|
+
: marker === estate
|
|
376
|
+
? { verdict: "owned", by: "marker", markerOnObject: true }
|
|
377
|
+
: { verdict: "foreign", by: "marker", markerOnObject: true, ownedBy: marker };
|
|
378
|
+
out.set(
|
|
379
|
+
address,
|
|
380
|
+
driftRecord({
|
|
381
|
+
...common(input, "choudoufu", "ownership", address),
|
|
382
|
+
...(asString(row.type) ? { resourceType: asString(row.type)! } : {}),
|
|
383
|
+
status: heldBy ? "runtime" : "undeclared",
|
|
384
|
+
ownership,
|
|
385
|
+
evidence: evidence(),
|
|
386
|
+
}),
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
for (const row of asArray(doc.gaps).filter(isObject)) {
|
|
390
|
+
const address = asString(row.address);
|
|
391
|
+
if (!address || answered.has(address) || out.has(address)) continue;
|
|
392
|
+
out.set(
|
|
393
|
+
address,
|
|
394
|
+
driftRecord({
|
|
395
|
+
...common(input, "choudoufu", "ownership", address),
|
|
396
|
+
...(asString(row.type) ? { resourceType: asString(row.type)! } : {}),
|
|
397
|
+
status: "not-observed",
|
|
398
|
+
unobserved: { reason: "unsupported-kind", ...(asString(row.detail) ? { detail: asString(row.detail)! } : {}) },
|
|
399
|
+
evidence: evidence(asString(row.rung)),
|
|
400
|
+
}),
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
return [...out.values()].sort(byAddress);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/** live-plan's records, then live-ls's undeclared items and the gaps live-plan did not answer. */
|
|
407
|
+
export function choudoufuLiveDriftRecords(input: DriftAdapterBase & { livePlan: unknown; liveLs?: unknown }): DriftRecord[] {
|
|
408
|
+
const plan = choudoufuLivePlanDriftRecords(input);
|
|
409
|
+
if (input.liveLs === undefined) return plan;
|
|
410
|
+
const answered = new Set(plan.map((r) => r.address));
|
|
411
|
+
const ls = choudoufuLiveLsDriftRecords({ ...input, liveLs: input.liveLs, answered }).filter((r) => !answered.has(r.address));
|
|
412
|
+
return [...plan, ...ls].sort(byAddress);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// ── choudoufu live+plan ────────────────────────────────────────────────────────
|
|
416
|
+
|
|
417
|
+
export interface ChoudoufuLivePlusPlanDriftInput extends DriftAdapterBase {
|
|
418
|
+
/** `choudoufu live-plan -json`, parsed. */
|
|
419
|
+
livePlan: unknown;
|
|
420
|
+
/** `choudoufu show -json <planfile>` of the same root, parsed. */
|
|
421
|
+
plan: unknown;
|
|
422
|
+
/** `choudoufu live-ls -json`, parsed, when it was read too. */
|
|
423
|
+
liveLs?: unknown;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* A live root at the plan depth. Every bound instance is read `plan`: a
|
|
428
|
+
* planned update or replace is drifted with the plan's paths, since a live
|
|
429
|
+
* root's prior state is the account itself, and anything else is in-sync.
|
|
430
|
+
* Its ownership facet is live-plan's. Every other address keeps its
|
|
431
|
+
* live-plan or live-ls record, read `ownership`, because the plan does not
|
|
432
|
+
* say who holds it.
|
|
433
|
+
*/
|
|
434
|
+
export function choudoufuLivePlusPlanDriftRecords(input: ChoudoufuLivePlusPlanDriftInput): DriftRecord[] {
|
|
435
|
+
const live = choudoufuLiveDriftRecords(input);
|
|
436
|
+
const index = indexPlan(input.plan);
|
|
437
|
+
const doc = isObject(input.plan) ? input.plan : {};
|
|
438
|
+
const version = input.version ?? asString(doc.terraform_version);
|
|
439
|
+
return live
|
|
440
|
+
.map((r) => {
|
|
441
|
+
if (r.read !== "ownership" || r.status !== "in-sync" || r.ownership?.verdict !== "owned") return r;
|
|
442
|
+
const change = index.changes.get(r.address);
|
|
443
|
+
const actions = change?.actions ?? [];
|
|
444
|
+
const moves = actions.includes("update") || (actions.includes("create") && actions.includes("delete"));
|
|
445
|
+
const attributes = moves ? change!.changes.map((c) => attributeOf(c, true)) : [];
|
|
446
|
+
return driftRecord({
|
|
447
|
+
...r,
|
|
448
|
+
read: "plan",
|
|
449
|
+
status: moves ? "drifted" : "in-sync",
|
|
450
|
+
...(attributes.length > 0 ? { attributes } : {}),
|
|
451
|
+
evidence: { tool: "choudoufu", ...(version ? { version } : {}), command: "choudoufu live-plan -json, choudoufu show -json" },
|
|
452
|
+
});
|
|
453
|
+
})
|
|
454
|
+
.sort(byAddress);
|
|
455
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drift rows (#4580) over two hand-written plans: a stock root's
|
|
3
|
+
* refresh-only plan with drift and a create row, and a full plan of a root
|
|
4
|
+
* under live resource markers. The count and names are checked against a
|
|
5
|
+
* copy of terragucci's own rules (packages/terragucci/src/report/drift.ts
|
|
6
|
+
* at integration/ci-products), so a product that moves onto these rows
|
|
7
|
+
* reports the same numbers.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { readFileSync } from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { describe, expect, test } from "vitest";
|
|
13
|
+
import { terraformPlanDriftCount, terraformPlanDriftNames, terraformPlanDriftRows } from "./drift-rows";
|
|
14
|
+
|
|
15
|
+
const FIX = join(import.meta.dirname, "__fixtures__", "drift-rows");
|
|
16
|
+
const read = (name: string): unknown => JSON.parse(readFileSync(join(FIX, name), "utf-8"));
|
|
17
|
+
|
|
18
|
+
// ── terragucci's rules, copied as they are, for the parity checks ─────────────
|
|
19
|
+
|
|
20
|
+
type Json = Record<string, unknown>;
|
|
21
|
+
const FLIP: Record<string, string> = { create: "delete", delete: "create" };
|
|
22
|
+
|
|
23
|
+
function liveDrift(plan: unknown): unknown {
|
|
24
|
+
if (plan === null || typeof plan !== "object" || Array.isArray(plan)) return plan;
|
|
25
|
+
const p = plan as Json;
|
|
26
|
+
const changes = Array.isArray(p.resource_changes) ? (p.resource_changes as Json[]) : [];
|
|
27
|
+
const resource_drift = changes
|
|
28
|
+
.filter((r) => r.mode !== "data")
|
|
29
|
+
.flatMap((r) => {
|
|
30
|
+
const ch = (r.change ?? {}) as Json;
|
|
31
|
+
const actions = Array.isArray(ch.actions) ? (ch.actions as string[]) : [];
|
|
32
|
+
if (actions.length === 0 || actions.every((a) => a === "no-op" || a === "read")) return [];
|
|
33
|
+
const action = actions.length === 1 ? (FLIP[actions[0]!] ?? actions[0]!) : "update";
|
|
34
|
+
const live = (ch.before ?? null) as Json | null;
|
|
35
|
+
let code = (ch.after ?? null) as Json | null;
|
|
36
|
+
const unknown = ch.after_unknown !== null && typeof ch.after_unknown === "object" ? (ch.after_unknown as Json) : {};
|
|
37
|
+
if (code && live) code = { ...code, ...Object.fromEntries(Object.keys(unknown).filter((k) => unknown[k] === true && k in live).map((k) => [k, live[k]])) };
|
|
38
|
+
return [{
|
|
39
|
+
...r,
|
|
40
|
+
change: { actions: [action], before: code, after: live, before_sensitive: ch.after_sensitive ?? false, after_sensitive: ch.before_sensitive ?? false },
|
|
41
|
+
}];
|
|
42
|
+
});
|
|
43
|
+
return { ...p, resource_drift };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function driftCount(plan: unknown): number {
|
|
47
|
+
const list = plan !== null && typeof plan === "object" ? (plan as Json).resource_drift : undefined;
|
|
48
|
+
return Array.isArray(list) ? list.length : 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function driftNames(plan: unknown): Map<string, string> {
|
|
52
|
+
const out = new Map<string, string>();
|
|
53
|
+
const list = plan !== null && typeof plan === "object" ? (plan as Json).resource_drift : undefined;
|
|
54
|
+
if (!Array.isArray(list)) return out;
|
|
55
|
+
for (const r of list as Json[]) {
|
|
56
|
+
const ch = (r.change ?? {}) as Json;
|
|
57
|
+
const values = [ch.after, ch.before].filter((v): v is Json => v !== null && typeof v === "object");
|
|
58
|
+
for (const key of ["name", "id", "bucket", "function_name", "arn"]) {
|
|
59
|
+
const v = values.map((x) => x[key]).find((x) => typeof x === "string" && x !== "");
|
|
60
|
+
if (typeof v === "string") {
|
|
61
|
+
out.set(String(r.address), v);
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ── the rows ──────────────────────────────────────────────────────────────────
|
|
70
|
+
|
|
71
|
+
describe("a stock root's refresh-only plan", () => {
|
|
72
|
+
const plan = read("refresh-plan.json");
|
|
73
|
+
const rows = terraformPlanDriftRows(plan);
|
|
74
|
+
|
|
75
|
+
test("every resource_drift entry is a row, creates and data blocks included", () => {
|
|
76
|
+
expect(rows).toEqual([
|
|
77
|
+
{ address: "aws_s3_bucket.logs", action: "update", type: "aws_s3_bucket", name: "acme-logs" },
|
|
78
|
+
{ address: "aws_lambda_function.worker", action: "delete", type: "aws_lambda_function", name: "acme-worker" },
|
|
79
|
+
{ address: "aws_sqs_queue.jobs", action: "create", type: "aws_sqs_queue", name: "acme-jobs" },
|
|
80
|
+
{ address: "aws_iam_role.runner", action: "update", type: "aws_iam_role", name: "arn:aws:iam::000000000000:role/acme-runner" },
|
|
81
|
+
{ address: "data.aws_caller_identity.me", action: "update", type: "aws_caller_identity" },
|
|
82
|
+
]);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("rows carry no attribute values", () => {
|
|
86
|
+
for (const row of rows) expect(Object.keys(row).every((k) => ["address", "action", "type", "name"].includes(k))).toBe(true);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("the count and names match terragucci's report", () => {
|
|
90
|
+
expect(terraformPlanDriftCount(plan)).toBe(driftCount(plan));
|
|
91
|
+
expect(terraformPlanDriftCount(plan)).toBe(5);
|
|
92
|
+
expect(terraformPlanDriftNames(plan)).toEqual(driftNames(plan));
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("a delete-and-create pair is a replace", () => {
|
|
96
|
+
const pair = { resource_drift: [{ address: "a.b", change: { actions: ["delete", "create"], before: { id: "x" }, after: { id: "y" } } }] };
|
|
97
|
+
expect(terraformPlanDriftRows(pair)).toEqual([{ address: "a.b", action: "replace", name: "y" }]);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe("a live root's full plan", () => {
|
|
102
|
+
const plan = read("live-root-plan.json");
|
|
103
|
+
const rows = terraformPlanDriftRows(plan, { live: true });
|
|
104
|
+
|
|
105
|
+
test("changes read as drift from the other side, data, no-op and resource_drift left out", () => {
|
|
106
|
+
expect(rows).toEqual([
|
|
107
|
+
{ address: "aws_s3_bucket.assets", action: "delete", type: "aws_s3_bucket", name: "acme-assets" },
|
|
108
|
+
{ address: "aws_sqs_queue.stray", action: "create", type: "aws_sqs_queue", name: "acme-stray" },
|
|
109
|
+
{ address: "aws_lambda_function.api", action: "update", type: "aws_lambda_function", name: "acme-api" },
|
|
110
|
+
{ address: "aws_iam_role.deployer", action: "update", type: "aws_iam_role", name: "acme-deployer-old" },
|
|
111
|
+
{ address: "aws_security_group.edge", action: "update", type: "aws_security_group", name: "sg-0001" },
|
|
112
|
+
]);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("the count and names match terragucci's report after liveDrift", () => {
|
|
116
|
+
const drifted = liveDrift(plan);
|
|
117
|
+
expect(terraformPlanDriftCount(plan, { live: true })).toBe(driftCount(drifted));
|
|
118
|
+
expect(terraformPlanDriftCount(plan, { live: true })).toBe(5);
|
|
119
|
+
expect(terraformPlanDriftNames(plan, { live: true })).toEqual(driftNames(drifted));
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("read as a stock plan, the same document counts only its resource_drift", () => {
|
|
123
|
+
expect(terraformPlanDriftCount(plan)).toBe(1);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe("documents with nothing to read", () => {
|
|
128
|
+
test("a missing or malformed plan has no rows", () => {
|
|
129
|
+
for (const plan of [undefined, null, "plan", [], {}, { resource_drift: "x" }]) {
|
|
130
|
+
expect(terraformPlanDriftRows(plan)).toEqual([]);
|
|
131
|
+
expect(terraformPlanDriftRows(plan, { live: true })).toEqual([]);
|
|
132
|
+
expect(terraformPlanDriftCount(plan)).toBe(driftCount(plan));
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
});
|