@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,105 @@
|
|
|
1
|
+
import type { LockHolder, StateStore } from "./store.js";
|
|
2
|
+
/** What a lock says holds it: the operation and a line of info, as the binary prints them when it finds the lock held. */
|
|
3
|
+
export interface LockOperation {
|
|
4
|
+
operation: string;
|
|
5
|
+
info: string;
|
|
6
|
+
}
|
|
7
|
+
/** The lock info a run writes, in the shape the binary writes and prints when it finds a lock held. */
|
|
8
|
+
export declare function lockInfo(path: string, now: string, op: LockOperation, who?: string): string;
|
|
9
|
+
/** Who holds a lock, from the lock info it left: what is there of it. */
|
|
10
|
+
export interface LockHolderInfo {
|
|
11
|
+
id: string;
|
|
12
|
+
who?: string;
|
|
13
|
+
operation?: string;
|
|
14
|
+
info?: string;
|
|
15
|
+
created?: string;
|
|
16
|
+
}
|
|
17
|
+
/** The holder a lock info names. */
|
|
18
|
+
export declare function holderOf(held: LockHolder): LockHolderInfo;
|
|
19
|
+
/** A lock held by another run stopped the take; nothing was pulled. */
|
|
20
|
+
export declare class StateLockedError extends Error {
|
|
21
|
+
readonly root: string;
|
|
22
|
+
readonly lockLocation: string;
|
|
23
|
+
readonly holder?: LockHolderInfo | undefined;
|
|
24
|
+
constructor(root: string, lockLocation: string, holder?: LockHolderInfo | undefined);
|
|
25
|
+
}
|
|
26
|
+
/** What taking a lock needs of a store: a caller with a store of its own (terragucci's, which also reads versions) passes that. */
|
|
27
|
+
export type LockStore = Pick<StateStore, "location" | "lockLocation" | "heldLock" | "lock" | "unlock" | "adopt">;
|
|
28
|
+
/** A lock taken, on one root's state. */
|
|
29
|
+
export interface HeldLock<S extends LockStore = StateStore> {
|
|
30
|
+
root: string;
|
|
31
|
+
store: S;
|
|
32
|
+
/** The lock was another run's, kept on purpose with a state it could not write, and this take took it over. */
|
|
33
|
+
adopted: boolean;
|
|
34
|
+
release: () => Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Take the lock of each state, in the order given. Releases what it took and
|
|
38
|
+
* throws StateLockedError when one is held and `adopt` does not take it over.
|
|
39
|
+
* `adopt` sees a held lock and says whether this take takes it over (a lock
|
|
40
|
+
* an earlier run of the same operation left held on purpose).
|
|
41
|
+
*/
|
|
42
|
+
export declare function takeLocks<S extends LockStore = StateStore>(stores: readonly {
|
|
43
|
+
root: string;
|
|
44
|
+
store: S;
|
|
45
|
+
}[], now: string, op: LockOperation, adopt?: (root: string, store: S, held: LockHolder) => Promise<boolean>, who?: string): Promise<HeldLock<S>[]>;
|
|
46
|
+
/**
|
|
47
|
+
* The lock of a workspace a state is read from, taken through the
|
|
48
|
+
* workspace's own API (Terraform Cloud's `actions/lock`), not a lock object
|
|
49
|
+
* beside a state. `lock` takes it with a reason the workspace shows, and
|
|
50
|
+
* returns its release; it throws when another run holds it.
|
|
51
|
+
*/
|
|
52
|
+
export interface SourceLock {
|
|
53
|
+
root: string;
|
|
54
|
+
/** The workspace, for messages. */
|
|
55
|
+
location: string;
|
|
56
|
+
lock(reason: string): Promise<() => Promise<void>>;
|
|
57
|
+
}
|
|
58
|
+
/** A source workspace's lock, taken. */
|
|
59
|
+
export interface HeldSourceLock {
|
|
60
|
+
root: string;
|
|
61
|
+
location: string;
|
|
62
|
+
release: () => Promise<void>;
|
|
63
|
+
}
|
|
64
|
+
/** A source workspace's lock could not be taken: another run holds it, or the API refused. Nothing was read. */
|
|
65
|
+
export declare class SourceLockedError extends Error {
|
|
66
|
+
readonly root: string;
|
|
67
|
+
readonly location: string;
|
|
68
|
+
readonly reason: string;
|
|
69
|
+
constructor(root: string, location: string, reason: string);
|
|
70
|
+
}
|
|
71
|
+
/** The reason a source workspace's lock shows: the operation and its info, as a state lock's info names them. */
|
|
72
|
+
export declare const sourceLockReason: (op: LockOperation) => string;
|
|
73
|
+
/**
|
|
74
|
+
* Take each source workspace's lock, in the order given, so no run writes a
|
|
75
|
+
* state while it is read. Releases what it took and throws SourceLockedError
|
|
76
|
+
* when one cannot be taken.
|
|
77
|
+
*/
|
|
78
|
+
export declare function takeSourceLocks(sources: readonly SourceLock[], op: LockOperation): Promise<HeldSourceLock[]>;
|
|
79
|
+
export interface LocksWithSourcesInput<S extends LockStore> {
|
|
80
|
+
/** The workspaces states are read from, locked first. */
|
|
81
|
+
sources: readonly SourceLock[];
|
|
82
|
+
/** The states written, locked after the sources, in the order given. */
|
|
83
|
+
stores: readonly {
|
|
84
|
+
root: string;
|
|
85
|
+
store: S;
|
|
86
|
+
}[];
|
|
87
|
+
now: string;
|
|
88
|
+
/** What the state locks name. */
|
|
89
|
+
op: LockOperation;
|
|
90
|
+
/** What a source workspace's lock names; default `op`. A run that only reads the source says so here. */
|
|
91
|
+
sourceOp?: LockOperation;
|
|
92
|
+
adopt?: (root: string, store: S, held: LockHolder) => Promise<boolean>;
|
|
93
|
+
who?: string;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Every lock a run that reads from source workspaces and writes states
|
|
97
|
+
* holds: the source workspaces' first, then the states'. When any cannot be
|
|
98
|
+
* taken, every lock taken so far is released, and the error is
|
|
99
|
+
* SourceLockedError or StateLockedError.
|
|
100
|
+
*/
|
|
101
|
+
export declare function takeLocksWithSources<S extends LockStore = StateStore>(input: LocksWithSourcesInput<S>): Promise<{
|
|
102
|
+
sources: HeldSourceLock[];
|
|
103
|
+
locks: HeldLock<S>[];
|
|
104
|
+
}>;
|
|
105
|
+
//# sourceMappingURL=locks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locks.d.ts","sourceRoot":"","sources":["../../src/state/locks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Taking a set of state locks in one order, each the way its backend takes
|
|
3
|
+
* it, with lock info that names the operation and the run. A lock another
|
|
4
|
+
* run holds stops the take: every lock taken so far is released, and the
|
|
5
|
+
* error names the holder from the lock info it left.
|
|
6
|
+
*
|
|
7
|
+
* A run can also hold the lock of a workspace a state is read from, which
|
|
8
|
+
* no backend block of the root names (a Terraform Cloud or Enterprise
|
|
9
|
+
* workspace a state migration moves from, terragucci#1274): the workspace's
|
|
10
|
+
* own lock, taken through its API before any state lock (`takeSourceLocks`,
|
|
11
|
+
* `takeLocksWithSources`).
|
|
12
|
+
*
|
|
13
|
+
* Lifted from terragucci's `migrate.ts` (c493e0e): `lockInfo`,
|
|
14
|
+
* `LockOperation`, `HeldLock` and `takeLocks`, and the source-workspace lock
|
|
15
|
+
* of its `takeLocks` (lines 1045 to 1056).
|
|
16
|
+
*/
|
|
17
|
+
import { randomUUID } from "node:crypto";
|
|
18
|
+
import { hostname } from "node:os";
|
|
19
|
+
/** The lock info a run writes, in the shape the binary writes and prints when it finds a lock held. */
|
|
20
|
+
export function lockInfo(path, now, op, who = `chant@${hostname()}`) {
|
|
21
|
+
return JSON.stringify({ ID: randomUUID(), Operation: op.operation, Info: op.info, Who: who, Version: "", Created: now, Path: path });
|
|
22
|
+
}
|
|
23
|
+
/** The holder a lock info names. */
|
|
24
|
+
export function holderOf(held) {
|
|
25
|
+
let v = {};
|
|
26
|
+
try {
|
|
27
|
+
v = JSON.parse(held.info);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// Lock info that is not JSON names no one.
|
|
31
|
+
}
|
|
32
|
+
const s = (k) => (typeof v[k] === "string" && v[k] !== "" ? v[k] : undefined);
|
|
33
|
+
return { id: held.id, ...(s("Who") ? { who: s("Who") } : {}), ...(s("Operation") ? { operation: s("Operation") } : {}), ...(s("Info") ? { info: s("Info") } : {}), ...(s("Created") ? { created: s("Created") } : {}) };
|
|
34
|
+
}
|
|
35
|
+
/** A lock held by another run stopped the take; nothing was pulled. */
|
|
36
|
+
export class StateLockedError extends Error {
|
|
37
|
+
constructor(root, lockLocation, holder) {
|
|
38
|
+
const by = holder ? [holder.who && `by ${holder.who}`, holder.operation, holder.info && `(${holder.info})`, holder.created && `since ${holder.created}`].filter(Boolean).join(" ") : "";
|
|
39
|
+
super(`${root}: its state is locked at ${lockLocation}${by ? ` ${by}` : ""}, so nothing was pulled`);
|
|
40
|
+
this.root = root;
|
|
41
|
+
this.lockLocation = lockLocation;
|
|
42
|
+
this.holder = holder;
|
|
43
|
+
this.name = "StateLockedError";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Take the lock of each state, in the order given. Releases what it took and
|
|
48
|
+
* throws StateLockedError when one is held and `adopt` does not take it over.
|
|
49
|
+
* `adopt` sees a held lock and says whether this take takes it over (a lock
|
|
50
|
+
* an earlier run of the same operation left held on purpose).
|
|
51
|
+
*/
|
|
52
|
+
export async function takeLocks(stores, now, op, adopt, who) {
|
|
53
|
+
const held = [];
|
|
54
|
+
const releaseAll = async () => {
|
|
55
|
+
for (const h of held.reverse())
|
|
56
|
+
await h.release().catch(() => { });
|
|
57
|
+
};
|
|
58
|
+
for (const { root, store } of stores) {
|
|
59
|
+
let got;
|
|
60
|
+
try {
|
|
61
|
+
got = await store.lock(lockInfo(store.location.replace(/^[a-z0-9]+:\/\//, ""), now, op, who));
|
|
62
|
+
}
|
|
63
|
+
catch (e) {
|
|
64
|
+
await releaseAll();
|
|
65
|
+
throw new Error(`${root}: the lock ${store.lockLocation} could not be taken: ${e.message}`);
|
|
66
|
+
}
|
|
67
|
+
if (got) {
|
|
68
|
+
held.push({ root, store, adopted: false, release: () => store.unlock() });
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const info = await store.heldLock().catch(() => undefined);
|
|
72
|
+
if (info && adopt && (await adopt(root, store, info).catch(() => false))) {
|
|
73
|
+
store.adopt(info);
|
|
74
|
+
held.push({ root, store, adopted: true, release: () => store.unlock() });
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
await releaseAll();
|
|
78
|
+
throw new StateLockedError(root, store.lockLocation, info ? holderOf(info) : undefined);
|
|
79
|
+
}
|
|
80
|
+
return held;
|
|
81
|
+
}
|
|
82
|
+
/** A source workspace's lock could not be taken: another run holds it, or the API refused. Nothing was read. */
|
|
83
|
+
export class SourceLockedError extends Error {
|
|
84
|
+
constructor(root, location, reason) {
|
|
85
|
+
super(`${root}: the lock of the workspace it is read from (${location}) could not be taken: ${reason}`);
|
|
86
|
+
this.root = root;
|
|
87
|
+
this.location = location;
|
|
88
|
+
this.reason = reason;
|
|
89
|
+
this.name = "SourceLockedError";
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/** The reason a source workspace's lock shows: the operation and its info, as a state lock's info names them. */
|
|
93
|
+
export const sourceLockReason = (op) => `${op.operation}: ${op.info}`;
|
|
94
|
+
/**
|
|
95
|
+
* Take each source workspace's lock, in the order given, so no run writes a
|
|
96
|
+
* state while it is read. Releases what it took and throws SourceLockedError
|
|
97
|
+
* when one cannot be taken.
|
|
98
|
+
*/
|
|
99
|
+
export async function takeSourceLocks(sources, op) {
|
|
100
|
+
const held = [];
|
|
101
|
+
for (const s of sources) {
|
|
102
|
+
try {
|
|
103
|
+
held.push({ root: s.root, location: s.location, release: await s.lock(sourceLockReason(op)) });
|
|
104
|
+
}
|
|
105
|
+
catch (e) {
|
|
106
|
+
for (const h of held.reverse())
|
|
107
|
+
await h.release().catch(() => { });
|
|
108
|
+
throw new SourceLockedError(s.root, s.location, e.message);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return held;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Every lock a run that reads from source workspaces and writes states
|
|
115
|
+
* holds: the source workspaces' first, then the states'. When any cannot be
|
|
116
|
+
* taken, every lock taken so far is released, and the error is
|
|
117
|
+
* SourceLockedError or StateLockedError.
|
|
118
|
+
*/
|
|
119
|
+
export async function takeLocksWithSources(input) {
|
|
120
|
+
const sources = await takeSourceLocks(input.sources, input.sourceOp ?? input.op);
|
|
121
|
+
try {
|
|
122
|
+
const locks = await takeLocks(input.stores, input.now, input.op, input.adopt, input.who);
|
|
123
|
+
return { sources, locks };
|
|
124
|
+
}
|
|
125
|
+
catch (e) {
|
|
126
|
+
for (const h of sources.reverse())
|
|
127
|
+
await h.release().catch(() => { });
|
|
128
|
+
throw e;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the state stores share: the fetch they call, their errors, and how a
|
|
3
|
+
* key goes into a URL. Since #4554 these are chant core's object-store
|
|
4
|
+
* definitions (`@intentius/chant/object-store`), so a store error thrown by
|
|
5
|
+
* a core client is the one a state caller catches.
|
|
6
|
+
*/
|
|
7
|
+
export { defaultFetch, encodePath, encodeStrict, StoreConflict, StoreError, xmlText, type StoreCondition, type StoreFetch } from "@intentius/chant/object-store";
|
|
8
|
+
//# sourceMappingURL=object-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"object-store.d.ts","sourceRoot":"","sources":["../../src/state/object-store.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,UAAU,EAAE,MAAM,+BAA+B,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the state stores share: the fetch they call, their errors, and how a
|
|
3
|
+
* key goes into a URL. Since #4554 these are chant core's object-store
|
|
4
|
+
* definitions (`@intentius/chant/object-store`), so a store error thrown by
|
|
5
|
+
* a core client is the one a state caller catches.
|
|
6
|
+
*/
|
|
7
|
+
export { defaultFetch, encodePath, encodeStrict, StoreConflict, StoreError, xmlText } from "@intentius/chant/object-store";
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { type LockableState, type StateObject, type StateRefusalKind } from "./backend.js";
|
|
2
|
+
import { type BinaryExec, type StateFile } from "./exec.js";
|
|
3
|
+
import { type LockOperation } from "./locks.js";
|
|
4
|
+
import type { StoreFetch } from "./object-store.js";
|
|
5
|
+
import { type StateStore } from "./store.js";
|
|
6
|
+
/** The suffix of the object beside a root's state that holds a state a run could not write. */
|
|
7
|
+
export declare const UNWRITTEN_SUFFIX = ".chant-unwritten";
|
|
8
|
+
/** The suffix terragucci keeps an unwritten state under: read and removed, never written. */
|
|
9
|
+
export declare const LEGACY_UNWRITTEN_SUFFIX = ".terragucci-unwritten";
|
|
10
|
+
/** The operation terragucci's composed run names in its locks. */
|
|
11
|
+
export declare const LEGACY_COMPOSE_OPERATION = "terragucci compose";
|
|
12
|
+
/** Where a kept state goes under the job's report directory when its own backend could not take it. */
|
|
13
|
+
export declare const UNWRITTEN_DIR = "unwritten";
|
|
14
|
+
/** The operation a composed run's locks name. */
|
|
15
|
+
export declare const COMPOSE_LOCK: LockOperation;
|
|
16
|
+
/** How long to wait before each retry of a state's write: five retries, inside the lock timeout of 5 minutes. */
|
|
17
|
+
export declare const DEFAULT_RETRY_DELAYS: number[];
|
|
18
|
+
/** One root whose state the run holds. */
|
|
19
|
+
export interface RootState {
|
|
20
|
+
root: string;
|
|
21
|
+
/** The root's directory, where the binary runs. */
|
|
22
|
+
dir: string;
|
|
23
|
+
/** The root's environment for the binary. */
|
|
24
|
+
env: NodeJS.ProcessEnv;
|
|
25
|
+
/** Where its state is; read from what `init` recorded, compared with the root's code, when absent. */
|
|
26
|
+
object?: StateObject;
|
|
27
|
+
/** `terraform.roots.<name>.backendConfig`, which the comparison with what init recorded reads (chant#4478). */
|
|
28
|
+
backendConfig?: Readonly<Record<string, string>>;
|
|
29
|
+
/** `terraform.roots.<name>.backendConfigFiles`, whose keys the same comparison reads (chant#4459). */
|
|
30
|
+
backendConfigFiles?: readonly string[];
|
|
31
|
+
}
|
|
32
|
+
export interface RootStatesOptions {
|
|
33
|
+
/** The roots, in the order their locks are taken. */
|
|
34
|
+
roots: RootState[];
|
|
35
|
+
/** The binary's path. */
|
|
36
|
+
binary: string;
|
|
37
|
+
exec?: BinaryExec;
|
|
38
|
+
/** The operation the locks name. Default COMPOSE_LOCK. */
|
|
39
|
+
op?: LockOperation;
|
|
40
|
+
/** Names the run in the locks it takes. */
|
|
41
|
+
runId?: string;
|
|
42
|
+
/** A directory of the job's own for the state files `state push` reads. */
|
|
43
|
+
work: string;
|
|
44
|
+
/** The job's report directory, where a kept state goes when its backend cannot take it. */
|
|
45
|
+
reportDir: string;
|
|
46
|
+
retryDelays?: number[];
|
|
47
|
+
sleep?: (ms: number) => Promise<void>;
|
|
48
|
+
fetch?: StoreFetch;
|
|
49
|
+
/** Opens a root's store; a test's fake stands in here. */
|
|
50
|
+
openStore?: (root: RootState, object: LockableState) => StateStore;
|
|
51
|
+
now?: () => string;
|
|
52
|
+
/** Who the lock info names. */
|
|
53
|
+
who?: string;
|
|
54
|
+
log?: (line: string) => void;
|
|
55
|
+
}
|
|
56
|
+
/** Why a root's state is not locked. */
|
|
57
|
+
export interface RootRefusal {
|
|
58
|
+
root: string;
|
|
59
|
+
backend: string;
|
|
60
|
+
refused: StateRefusalKind;
|
|
61
|
+
reason: string;
|
|
62
|
+
}
|
|
63
|
+
/** The roots whose state chant does not lock: the take stops before any lock. */
|
|
64
|
+
export declare class StateRefusedError extends Error {
|
|
65
|
+
readonly refusals: RootRefusal[];
|
|
66
|
+
constructor(refusals: RootRefusal[]);
|
|
67
|
+
}
|
|
68
|
+
/** How a write ended: written, or kept with the lock held. */
|
|
69
|
+
export type WriteOutcome = {
|
|
70
|
+
written: true;
|
|
71
|
+
attempts: number;
|
|
72
|
+
} | {
|
|
73
|
+
written: false;
|
|
74
|
+
attempts: number;
|
|
75
|
+
error: string;
|
|
76
|
+
kept: string;
|
|
77
|
+
lockHeld: true;
|
|
78
|
+
};
|
|
79
|
+
export declare class RootStates {
|
|
80
|
+
private readonly o;
|
|
81
|
+
private readonly exec;
|
|
82
|
+
private readonly op;
|
|
83
|
+
private readonly objects;
|
|
84
|
+
private locks;
|
|
85
|
+
private readonly heldOnPurpose;
|
|
86
|
+
constructor(o: RootStatesOptions);
|
|
87
|
+
private log;
|
|
88
|
+
private rootOf;
|
|
89
|
+
/** Where a root's state is, or why it is not locked. */
|
|
90
|
+
object(root: string): StateObject;
|
|
91
|
+
/** Every root whose state chant does not lock, with why. */
|
|
92
|
+
refusals(): RootRefusal[];
|
|
93
|
+
/** The locks held now, by root. */
|
|
94
|
+
held(): string[];
|
|
95
|
+
private lockOf;
|
|
96
|
+
/**
|
|
97
|
+
* Take every root's lock, in order. A root chant does not lock stops the
|
|
98
|
+
* take before any lock (StateRefusedError); a lock another run holds
|
|
99
|
+
* releases what was taken and throws StateLockedError, naming the holder.
|
|
100
|
+
* A lock an earlier run of this operation left held with a kept state is
|
|
101
|
+
* taken over, and every kept state is written before this returns.
|
|
102
|
+
*/
|
|
103
|
+
take(): Promise<void>;
|
|
104
|
+
/** The state kept beside a store's state and the suffix it is under, chant's name first; undefined when none is kept. */
|
|
105
|
+
private kept;
|
|
106
|
+
/** A state an earlier run kept beside a root's state: write it, then remove what was kept. */
|
|
107
|
+
private writeKept;
|
|
108
|
+
/** A root's state as `state pull` gives it, or null when it has none. */
|
|
109
|
+
pull(root: string): Promise<StateFile | null>;
|
|
110
|
+
/** Write one root's state under its lock: the store's own write (azurerm's lease), else `state push -lock=false`. */
|
|
111
|
+
private writeState;
|
|
112
|
+
/**
|
|
113
|
+
* Write one root's state, retrying after each delay while the lock is
|
|
114
|
+
* held. A state still unwritten after the last retry is kept, and its lock
|
|
115
|
+
* stays held: `release` leaves it.
|
|
116
|
+
*/
|
|
117
|
+
write(root: string, state: StateFile): Promise<WriteOutcome>;
|
|
118
|
+
/** Keep a state that could not be written: beside it in its own backend, else as a file of the job's report directory. Returns where. */
|
|
119
|
+
private keep;
|
|
120
|
+
/**
|
|
121
|
+
* Keep a root's lock held past {@link release}, as a kept state's is: for a
|
|
122
|
+
* root whose state the run could neither write nor keep, so no other run
|
|
123
|
+
* plans against a state that may not hold what applied.
|
|
124
|
+
*/
|
|
125
|
+
hold(root: string): void;
|
|
126
|
+
/** The roots whose lock stays held past {@link release}. */
|
|
127
|
+
heldOnPurposeRoots(): string[];
|
|
128
|
+
/** Release a root's lock, or every lock but those held on purpose for a kept state. */
|
|
129
|
+
release(root?: string): Promise<void>;
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=root-states.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"root-states.d.ts","sourceRoot":"","sources":["../../src/state/root-states.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The states of a set of roots, held for one run: take every lock (in the
|
|
3
|
+
* order given), pull each state, write each state back with retries, and
|
|
4
|
+
* release each lock.
|
|
5
|
+
*
|
|
6
|
+
* A state whose write still fails after the retries is kept beside the state
|
|
7
|
+
* in its own backend as `<key>.chant-unwritten`, and its lock stays held,
|
|
8
|
+
* naming the run. The next take of that lock finds the kept state, takes
|
|
9
|
+
* the lock over, writes the kept state, removes it, and only then returns.
|
|
10
|
+
* When the backend cannot take the kept copy either, it goes under the job's
|
|
11
|
+
* report directory (`<reportDir>/unwritten/<root>.tfstate`), never the
|
|
12
|
+
* reports bucket.
|
|
13
|
+
*
|
|
14
|
+
* A take also reads a state terragucci kept as `<key>.terragucci-unwritten`
|
|
15
|
+
* under a terragucci composed run's lock, so a repo that moves to chant
|
|
16
|
+
* mid-incident keeps it. That name is read and removed, never written.
|
|
17
|
+
*
|
|
18
|
+
* Lifted from terragucci's `compose.ts` (c493e0e): `ComposedRun.plan`'s lock
|
|
19
|
+
* take and kept write, `writeState`, `writeWithRetries`, `keep`,
|
|
20
|
+
* `releaseOne` and `release`.
|
|
21
|
+
*/
|
|
22
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
23
|
+
import { dirname, join } from "node:path";
|
|
24
|
+
import { isRefused, stateObject } from "./backend.js";
|
|
25
|
+
import { lastLines, pullState, runBinary } from "./exec.js";
|
|
26
|
+
import { takeLocks } from "./locks.js";
|
|
27
|
+
import { stateStore } from "./store.js";
|
|
28
|
+
/** The suffix of the object beside a root's state that holds a state a run could not write. */
|
|
29
|
+
export const UNWRITTEN_SUFFIX = ".chant-unwritten";
|
|
30
|
+
/** The suffix terragucci keeps an unwritten state under: read and removed, never written. */
|
|
31
|
+
export const LEGACY_UNWRITTEN_SUFFIX = ".terragucci-unwritten";
|
|
32
|
+
/** The operation terragucci's composed run names in its locks. */
|
|
33
|
+
export const LEGACY_COMPOSE_OPERATION = "terragucci compose";
|
|
34
|
+
/** Where a kept state goes under the job's report directory when its own backend could not take it. */
|
|
35
|
+
export const UNWRITTEN_DIR = "unwritten";
|
|
36
|
+
/** The operation a composed run's locks name. */
|
|
37
|
+
export const COMPOSE_LOCK = { operation: "chant terraform.compose", info: "a composed run holds this state while it plans, applies and writes it" };
|
|
38
|
+
/** How long to wait before each retry of a state's write: five retries, inside the lock timeout of 5 minutes. */
|
|
39
|
+
export const DEFAULT_RETRY_DELAYS = [5000, 15000, 30000, 60000, 120000];
|
|
40
|
+
/** The roots whose state chant does not lock: the take stops before any lock. */
|
|
41
|
+
export class StateRefusedError extends Error {
|
|
42
|
+
constructor(refusals) {
|
|
43
|
+
super(refusals.map((r) => `${r.root}: ${r.reason}`).join("\n"));
|
|
44
|
+
this.refusals = refusals;
|
|
45
|
+
this.name = "StateRefusedError";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const sleepMs = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
49
|
+
const safeName = (root) => (root === "." ? "root" : root.replace(/[^\w.-]/g, "_"));
|
|
50
|
+
const body = (s) => JSON.stringify(s, null, 2) + "\n";
|
|
51
|
+
export class RootStates {
|
|
52
|
+
constructor(o) {
|
|
53
|
+
this.o = o;
|
|
54
|
+
this.objects = new Map();
|
|
55
|
+
this.locks = [];
|
|
56
|
+
this.heldOnPurpose = new Set();
|
|
57
|
+
this.exec = o.exec ?? runBinary;
|
|
58
|
+
this.op = o.op ?? COMPOSE_LOCK;
|
|
59
|
+
if (o.runId)
|
|
60
|
+
this.op = { operation: this.op.operation, info: `${this.op.info} (run ${o.runId})` };
|
|
61
|
+
}
|
|
62
|
+
log(line) {
|
|
63
|
+
this.o.log?.(line);
|
|
64
|
+
}
|
|
65
|
+
rootOf(root) {
|
|
66
|
+
const r = this.o.roots.find((x) => x.root === root);
|
|
67
|
+
if (!r)
|
|
68
|
+
throw new Error(`${root} is not one of this run's roots`);
|
|
69
|
+
return r;
|
|
70
|
+
}
|
|
71
|
+
/** Where a root's state is, or why it is not locked. */
|
|
72
|
+
object(root) {
|
|
73
|
+
let o = this.objects.get(root);
|
|
74
|
+
if (!o) {
|
|
75
|
+
const r = this.rootOf(root);
|
|
76
|
+
o = r.object ?? stateObject(r.dir, r.env, undefined, this.o.binary, {
|
|
77
|
+
...(r.backendConfig ? { backendConfig: r.backendConfig } : {}),
|
|
78
|
+
...(r.backendConfigFiles ? { backendConfigFiles: r.backendConfigFiles } : {}),
|
|
79
|
+
});
|
|
80
|
+
this.objects.set(root, o);
|
|
81
|
+
}
|
|
82
|
+
return o;
|
|
83
|
+
}
|
|
84
|
+
/** Every root whose state chant does not lock, with why. */
|
|
85
|
+
refusals() {
|
|
86
|
+
return this.o.roots.flatMap((r) => {
|
|
87
|
+
const o = this.object(r.root);
|
|
88
|
+
return isRefused(o) ? [{ root: r.root, backend: o.backend, refused: o.refused, reason: o.reason }] : [];
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/** The locks held now, by root. */
|
|
92
|
+
held() {
|
|
93
|
+
return this.locks.map((l) => l.root);
|
|
94
|
+
}
|
|
95
|
+
lockOf(root) {
|
|
96
|
+
return this.locks.find((l) => l.root === root);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Take every root's lock, in order. A root chant does not lock stops the
|
|
100
|
+
* take before any lock (StateRefusedError); a lock another run holds
|
|
101
|
+
* releases what was taken and throws StateLockedError, naming the holder.
|
|
102
|
+
* A lock an earlier run of this operation left held with a kept state is
|
|
103
|
+
* taken over, and every kept state is written before this returns.
|
|
104
|
+
*/
|
|
105
|
+
async take() {
|
|
106
|
+
const refused = this.refusals();
|
|
107
|
+
if (refused.length)
|
|
108
|
+
throw new StateRefusedError(refused);
|
|
109
|
+
const stores = this.o.roots.map((r) => {
|
|
110
|
+
const object = this.object(r.root);
|
|
111
|
+
return { root: r.root, store: this.o.openStore ? this.o.openStore(r, object) : stateStore(object, r.dir, this.o.fetch) };
|
|
112
|
+
});
|
|
113
|
+
const now = this.o.now?.() ?? new Date().toISOString();
|
|
114
|
+
this.locks = await takeLocks(stores, now, this.op, async (root, store, held) => {
|
|
115
|
+
// A lock an earlier composed run left held for a state it could not write: this run writes that state first.
|
|
116
|
+
const ours = held.info.includes(this.op.operation) || held.info.includes(LEGACY_COMPOSE_OPERATION);
|
|
117
|
+
if (!ours || !(await this.kept(store)))
|
|
118
|
+
return false;
|
|
119
|
+
this.log(`${root}: its lock is held by an earlier run that kept a state it could not write; this run takes the lock over and writes that state first`);
|
|
120
|
+
return true;
|
|
121
|
+
}, this.o.who);
|
|
122
|
+
for (const l of this.locks) {
|
|
123
|
+
try {
|
|
124
|
+
await this.writeKept(l);
|
|
125
|
+
}
|
|
126
|
+
catch (e) {
|
|
127
|
+
// The kept state is still unwritten: its lock stays held, the others are released.
|
|
128
|
+
this.heldOnPurpose.add(l.root);
|
|
129
|
+
await this.release();
|
|
130
|
+
throw new Error(`${l.root}: the state an earlier run kept for it could not be written: ${e.message}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/** The state kept beside a store's state and the suffix it is under, chant's name first; undefined when none is kept. */
|
|
135
|
+
async kept(store) {
|
|
136
|
+
for (const suffix of [UNWRITTEN_SUFFIX, LEGACY_UNWRITTEN_SUFFIX]) {
|
|
137
|
+
const text = await store.readBeside(suffix).catch(() => undefined);
|
|
138
|
+
if (text?.trim())
|
|
139
|
+
return { suffix, text };
|
|
140
|
+
}
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
/** A state an earlier run kept beside a root's state: write it, then remove what was kept. */
|
|
144
|
+
async writeKept(lock) {
|
|
145
|
+
const kept = await this.kept(lock.store);
|
|
146
|
+
if (!kept)
|
|
147
|
+
return;
|
|
148
|
+
await this.writeState(lock.root, JSON.parse(kept.text));
|
|
149
|
+
await lock.store.removeBeside(kept.suffix);
|
|
150
|
+
this.log(`${lock.root}: wrote the state an earlier run kept at ${lock.store.besideLocation(kept.suffix)}`);
|
|
151
|
+
}
|
|
152
|
+
/** A root's state as `state pull` gives it, or null when it has none. */
|
|
153
|
+
pull(root) {
|
|
154
|
+
const r = this.rootOf(root);
|
|
155
|
+
return pullState(this.exec, this.o.binary, root, r.dir, r.env);
|
|
156
|
+
}
|
|
157
|
+
/** Write one root's state under its lock: the store's own write (azurerm's lease), else `state push -lock=false`. */
|
|
158
|
+
async writeState(root, state) {
|
|
159
|
+
const lock = this.lockOf(root);
|
|
160
|
+
if (!lock)
|
|
161
|
+
throw new Error(`${root}: its state is written under its lock, and none is held`);
|
|
162
|
+
if (lock.store.write) {
|
|
163
|
+
await lock.store.write(body(state));
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const r = this.rootOf(root);
|
|
167
|
+
const file = join(this.o.work, `${safeName(root)}.tfstate`);
|
|
168
|
+
mkdirSync(this.o.work, { recursive: true });
|
|
169
|
+
writeFileSync(file, body(state));
|
|
170
|
+
const push = await this.exec(this.o.binary, ["state", "push", "-lock=false", file], r.dir, r.env);
|
|
171
|
+
if (push.code !== 0)
|
|
172
|
+
throw new Error(`state push failed: ${lastLines(push.out, 3)}`);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Write one root's state, retrying after each delay while the lock is
|
|
176
|
+
* held. A state still unwritten after the last retry is kept, and its lock
|
|
177
|
+
* stays held: `release` leaves it.
|
|
178
|
+
*/
|
|
179
|
+
async write(root, state) {
|
|
180
|
+
const delays = this.o.retryDelays ?? DEFAULT_RETRY_DELAYS;
|
|
181
|
+
const sleep = this.o.sleep ?? sleepMs;
|
|
182
|
+
let last = "";
|
|
183
|
+
for (let i = 0; i <= delays.length; i++) {
|
|
184
|
+
try {
|
|
185
|
+
await this.writeState(root, state);
|
|
186
|
+
return { written: true, attempts: i + 1 };
|
|
187
|
+
}
|
|
188
|
+
catch (e) {
|
|
189
|
+
last = e.message;
|
|
190
|
+
if (i < delays.length) {
|
|
191
|
+
this.log(`${root}: the write of its state failed (${last}); trying again in ${Math.round(delays[i] / 1000)}s`);
|
|
192
|
+
await sleep(delays[i]);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const kept = await this.keep(root, state);
|
|
197
|
+
this.heldOnPurpose.add(root);
|
|
198
|
+
this.log(`${root}: its state could not be written (${last}); kept at ${kept}, and its lock stays held until a later run writes it`);
|
|
199
|
+
return { written: false, attempts: delays.length + 1, error: last, kept, lockHeld: true };
|
|
200
|
+
}
|
|
201
|
+
/** Keep a state that could not be written: beside it in its own backend, else as a file of the job's report directory. Returns where. */
|
|
202
|
+
async keep(root, state) {
|
|
203
|
+
const lock = this.lockOf(root);
|
|
204
|
+
try {
|
|
205
|
+
if (lock) {
|
|
206
|
+
await lock.store.writeBeside(UNWRITTEN_SUFFIX, body(state));
|
|
207
|
+
return lock.store.besideLocation(UNWRITTEN_SUFFIX);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
catch (e) {
|
|
211
|
+
this.log(`${root}: the state could not be kept beside its backend's state either (${e.message}); it is kept under the job's report directory`);
|
|
212
|
+
}
|
|
213
|
+
const file = join(this.o.reportDir, UNWRITTEN_DIR, `${safeName(root)}.tfstate`);
|
|
214
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
215
|
+
writeFileSync(file, body(state));
|
|
216
|
+
return file;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Keep a root's lock held past {@link release}, as a kept state's is: for a
|
|
220
|
+
* root whose state the run could neither write nor keep, so no other run
|
|
221
|
+
* plans against a state that may not hold what applied.
|
|
222
|
+
*/
|
|
223
|
+
hold(root) {
|
|
224
|
+
if (this.lockOf(root))
|
|
225
|
+
this.heldOnPurpose.add(root);
|
|
226
|
+
}
|
|
227
|
+
/** The roots whose lock stays held past {@link release}. */
|
|
228
|
+
heldOnPurposeRoots() {
|
|
229
|
+
return [...this.heldOnPurpose].filter((r) => this.lockOf(r)).sort();
|
|
230
|
+
}
|
|
231
|
+
/** Release a root's lock, or every lock but those held on purpose for a kept state. */
|
|
232
|
+
async release(root) {
|
|
233
|
+
const which = root ? [root] : [...new Set(this.locks.map((l) => l.root))].filter((r) => !this.heldOnPurpose.has(r));
|
|
234
|
+
for (const r of which) {
|
|
235
|
+
const mine = this.locks.filter((l) => l.root === r);
|
|
236
|
+
this.locks = this.locks.filter((l) => l.root !== r);
|
|
237
|
+
for (const l of mine)
|
|
238
|
+
await l.release().catch((e) => this.log(`${r}: its lock could not be released: ${e.message}`));
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The S3 client a state's lock and write use: chant core's (#4554,
|
|
3
|
+
* `@intentius/chant/object-store`), which signs with chant's one AWS
|
|
4
|
+
* signer. Get, put, put only when absent, head and delete one object; with
|
|
5
|
+
* no static keys, a role is assumed with the job's OIDC token through STS
|
|
6
|
+
* AssumeRoleWithWebIdentity.
|
|
7
|
+
*/
|
|
8
|
+
import { type S3Target } from "@intentius/chant/object-store";
|
|
9
|
+
export { assumeRoleWithWebIdentity, S3Client, sign, type S3Credentials, type S3Location, type S3Target, type S3WebIdentity } from "@intentius/chant/object-store";
|
|
10
|
+
/**
|
|
11
|
+
* The target for `bucket` from the AWS environment variables. Credentials,
|
|
12
|
+
* first match wins: static keys in `AWS_ACCESS_KEY_ID` and
|
|
13
|
+
* `AWS_SECRET_ACCESS_KEY`; `AWS_ROLE_ARN` assumed with the token in
|
|
14
|
+
* `AWS_WEB_IDENTITY_TOKEN_FILE`, in a session named `chant-state` unless
|
|
15
|
+
* AWS_ROLE_SESSION_NAME names one.
|
|
16
|
+
*/
|
|
17
|
+
export declare function s3FromEnv(where: {
|
|
18
|
+
bucket: string;
|
|
19
|
+
endpoint?: string;
|
|
20
|
+
}, env?: NodeJS.ProcessEnv): S3Target;
|
|
21
|
+
//# sourceMappingURL=s3.d.ts.map
|