@intentius/chant-lexicon-terraform 0.57.0 → 0.58.0
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/dist/composites/terraform-adopt-op.d.ts +126 -0
- package/dist/composites/terraform-adopt-op.d.ts.map +1 -0
- package/dist/composites/terraform-apply-op.d.ts +63 -16
- package/dist/composites/terraform-apply-op.d.ts.map +1 -1
- package/dist/composites/terraform-watch-op.d.ts +80 -36
- package/dist/composites/terraform-watch-op.d.ts.map +1 -1
- package/dist/config.d.ts +49 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/describe-resources.d.ts +256 -16
- package/dist/describe-resources.d.ts.map +1 -1
- package/dist/hcl/descend.d.ts +98 -0
- package/dist/hcl/descend.d.ts.map +1 -0
- package/dist/hcl/marker.d.ts +130 -0
- package/dist/hcl/marker.d.ts.map +1 -0
- package/dist/hcl/parse.d.ts +142 -9
- package/dist/hcl/parse.d.ts.map +1 -1
- package/dist/hcl/references.d.ts +76 -0
- package/dist/hcl/references.d.ts.map +1 -0
- package/dist/hcl/roots.d.ts +22 -3
- package/dist/hcl/roots.d.ts.map +1 -1
- package/dist/hcl/suppressions.d.ts +54 -0
- package/dist/hcl/suppressions.d.ts.map +1 -0
- package/dist/hcl/value.d.ts +77 -0
- package/dist/hcl/value.d.ts.map +1 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/integrity.json +32 -5
- package/dist/lint/audit-catalog.d.ts +3 -2
- package/dist/lint/audit-catalog.d.ts.map +1 -1
- package/dist/lint/audit-lineage.d.ts +68 -0
- package/dist/lint/audit-lineage.d.ts.map +1 -1
- package/dist/lint/post-synth/blocks.d.ts +53 -0
- package/dist/lint/post-synth/blocks.d.ts.map +1 -0
- package/dist/lint/post-synth/fixtures/load.d.ts +37 -0
- package/dist/lint/post-synth/fixtures/load.d.ts.map +1 -0
- package/dist/lint/post-synth/index.d.ts.map +1 -1
- package/dist/lint/post-synth/module-source.d.ts +38 -0
- package/dist/lint/post-synth/module-source.d.ts.map +1 -0
- package/dist/lint/post-synth/scope.d.ts +65 -0
- package/dist/lint/post-synth/scope.d.ts.map +1 -0
- package/dist/lint/post-synth/tf001.d.ts +18 -0
- package/dist/lint/post-synth/tf001.d.ts.map +1 -1
- package/dist/lint/post-synth/tf002.d.ts +32 -0
- package/dist/lint/post-synth/tf002.d.ts.map +1 -0
- package/dist/lint/post-synth/tf003.d.ts +22 -0
- package/dist/lint/post-synth/tf003.d.ts.map +1 -0
- package/dist/lint/post-synth/tf004.d.ts +22 -0
- package/dist/lint/post-synth/tf004.d.ts.map +1 -0
- package/dist/lint/post-synth/tf005.d.ts +28 -0
- package/dist/lint/post-synth/tf005.d.ts.map +1 -0
- package/dist/lint/post-synth/tf006.d.ts +21 -0
- package/dist/lint/post-synth/tf006.d.ts.map +1 -0
- package/dist/lint/post-synth/tf007.d.ts +21 -0
- package/dist/lint/post-synth/tf007.d.ts.map +1 -0
- package/dist/lint/post-synth/tf008.d.ts +22 -0
- package/dist/lint/post-synth/tf008.d.ts.map +1 -0
- package/dist/lint/post-synth/tf009.d.ts +24 -0
- package/dist/lint/post-synth/tf009.d.ts.map +1 -0
- package/dist/lint/post-synth/tf010.d.ts +19 -0
- package/dist/lint/post-synth/tf010.d.ts.map +1 -0
- package/dist/lint/post-synth/tf011.d.ts +18 -0
- package/dist/lint/post-synth/tf011.d.ts.map +1 -0
- package/dist/lint/post-synth/tf012.d.ts +17 -0
- package/dist/lint/post-synth/tf012.d.ts.map +1 -0
- package/dist/lint/post-synth/tf013.d.ts +21 -0
- package/dist/lint/post-synth/tf013.d.ts.map +1 -0
- package/dist/lint/post-synth/tf014.d.ts +25 -0
- package/dist/lint/post-synth/tf014.d.ts.map +1 -0
- package/dist/lint/post-synth/tf015.d.ts +21 -0
- package/dist/lint/post-synth/tf015.d.ts.map +1 -0
- package/dist/lint/post-synth/tf016.d.ts +30 -0
- package/dist/lint/post-synth/tf016.d.ts.map +1 -0
- package/dist/lint/post-synth/tf017.d.ts +19 -0
- package/dist/lint/post-synth/tf017.d.ts.map +1 -0
- package/dist/lint/post-synth/tf018.d.ts +20 -0
- package/dist/lint/post-synth/tf018.d.ts.map +1 -0
- package/dist/lint/post-synth/tf019.d.ts +25 -0
- package/dist/lint/post-synth/tf019.d.ts.map +1 -0
- package/dist/lint/post-synth/tf020.d.ts +38 -0
- package/dist/lint/post-synth/tf020.d.ts.map +1 -0
- package/dist/lint/post-synth/tf021.d.ts +41 -0
- package/dist/lint/post-synth/tf021.d.ts.map +1 -0
- package/dist/lint/post-synth/tf022.d.ts +24 -0
- package/dist/lint/post-synth/tf022.d.ts.map +1 -0
- package/dist/lint/post-synth/tf024.d.ts +24 -0
- package/dist/lint/post-synth/tf024.d.ts.map +1 -0
- package/dist/lint/post-synth/tf025.d.ts +29 -0
- package/dist/lint/post-synth/tf025.d.ts.map +1 -0
- package/dist/lint/post-synth/tf026.d.ts +21 -0
- package/dist/lint/post-synth/tf026.d.ts.map +1 -0
- package/dist/lint/rules/fixtures/load.d.ts +13 -0
- package/dist/lint/rules/fixtures/load.d.ts.map +1 -0
- package/dist/lint/rules/plan-before-apply.d.ts +11 -0
- package/dist/lint/rules/plan-before-apply.d.ts.map +1 -1
- package/dist/lint/secret-shape.d.ts +68 -0
- package/dist/lint/secret-shape.d.ts.map +1 -0
- package/dist/live-ownership.d.ts +55 -0
- package/dist/live-ownership.d.ts.map +1 -0
- package/dist/lsp/option-keys.d.ts.map +1 -1
- package/dist/manifest.json +1 -1
- package/dist/okf/index.md +24 -0
- package/dist/okf/rules/TF002.md +11 -0
- package/dist/okf/rules/TF003.md +11 -0
- package/dist/okf/rules/TF004.md +11 -0
- package/dist/okf/rules/TF005.md +11 -0
- package/dist/okf/rules/TF006.md +11 -0
- package/dist/okf/rules/TF007.md +11 -0
- package/dist/okf/rules/TF008.md +11 -0
- package/dist/okf/rules/TF009.md +11 -0
- package/dist/okf/rules/TF010.md +11 -0
- package/dist/okf/rules/TF011.md +11 -0
- package/dist/okf/rules/TF012.md +11 -0
- package/dist/okf/rules/TF013.md +11 -0
- package/dist/okf/rules/TF014.md +11 -0
- package/dist/okf/rules/TF015.md +11 -0
- package/dist/okf/rules/TF016.md +11 -0
- package/dist/okf/rules/TF017.md +11 -0
- package/dist/okf/rules/TF018.md +11 -0
- package/dist/okf/rules/TF019.md +11 -0
- package/dist/okf/rules/TF020.md +11 -0
- package/dist/okf/rules/TF021.md +11 -0
- package/dist/okf/rules/TF022.md +11 -0
- package/dist/okf/rules/TF024.md +11 -0
- package/dist/okf/rules/TF025.md +11 -0
- package/dist/okf/rules/TF026.md +11 -0
- package/dist/op/activities/index.d.ts +13 -6
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/live-detect.d.ts +51 -0
- package/dist/op/activities/live-detect.d.ts.map +1 -0
- package/dist/op/activities/terraform.d.ts +445 -17
- package/dist/op/activities/terraform.d.ts.map +1 -1
- package/dist/op/activity-contracts.d.ts +148 -0
- package/dist/op/activity-contracts.d.ts.map +1 -0
- package/dist/op/adoption.d.ts +118 -0
- package/dist/op/adoption.d.ts.map +1 -0
- package/dist/op/builders.d.ts +46 -6
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/resolve-root-mode.d.ts +45 -0
- package/dist/op/resolve-root-mode.d.ts.map +1 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/rules/blocks.ts +109 -0
- package/dist/rules/module-source.ts +121 -0
- package/dist/rules/plan-before-apply.ts +11 -0
- package/dist/rules/scope.ts +107 -0
- package/dist/rules/tf001.ts +32 -8
- package/dist/rules/tf002.ts +151 -0
- package/dist/rules/tf003.ts +71 -0
- package/dist/rules/tf004.ts +63 -0
- package/dist/rules/tf005.ts +76 -0
- package/dist/rules/tf006.ts +53 -0
- package/dist/rules/tf007.ts +78 -0
- package/dist/rules/tf008.ts +75 -0
- package/dist/rules/tf009.ts +59 -0
- package/dist/rules/tf010.ts +51 -0
- package/dist/rules/tf011.ts +49 -0
- package/dist/rules/tf012.ts +47 -0
- package/dist/rules/tf013.ts +63 -0
- package/dist/rules/tf014.ts +72 -0
- package/dist/rules/tf015.ts +63 -0
- package/dist/rules/tf016.ts +75 -0
- package/dist/rules/tf017.ts +50 -0
- package/dist/rules/tf018.ts +72 -0
- package/dist/rules/tf019.ts +72 -0
- package/dist/rules/tf020.ts +123 -0
- package/dist/rules/tf021.ts +126 -0
- package/dist/rules/tf022.ts +61 -0
- package/dist/rules/tf024.ts +70 -0
- package/dist/rules/tf025.ts +101 -0
- package/dist/rules/tf026.ts +80 -0
- package/dist/skills/chant-terraform.md +1 -1
- package/package.json +7 -2
- package/src/__fixtures__/live/main.tf +28 -0
- package/src/__fixtures__/live-adopt/estate.chdf.hcl +6 -0
- package/src/__fixtures__/live-adopt/main.tf +21 -0
- package/src/__fixtures__/live-estate/README.md +58 -0
- package/src/__fixtures__/live-estate/compute.tf +10 -0
- package/src/__fixtures__/live-estate/estate.chdf.hcl +1 -0
- package/src/__fixtures__/live-estate/logs.tf +8 -0
- package/src/__fixtures__/live-estate/network.tf +24 -0
- package/src/__fixtures__/live-estate/rule.tf +11 -0
- package/src/__fixtures__/live-estate/unowned.tf +21 -0
- package/src/__fixtures__/live-estate/versions.tf +31 -0
- package/src/__fixtures__/live-ls.json +98 -0
- package/src/__fixtures__/live-plan.json +285 -0
- package/src/__fixtures__/live-sidecar/estate.chdf.hcl +3 -0
- package/src/__fixtures__/live-sidecar/main.tf +24 -0
- package/src/__fixtures__/module-tree/README.md +16 -0
- package/src/__fixtures__/module-tree/cycle/main.tf +3 -0
- package/src/__fixtures__/module-tree/cycle/modules/a/main.tf +9 -0
- package/src/__fixtures__/module-tree/cycle/modules/b/main.tf +9 -0
- package/src/__fixtures__/module-tree/missing/main.tf +9 -0
- package/src/__fixtures__/module-tree/outside/root/main.tf +9 -0
- package/src/__fixtures__/module-tree/root/main.tf +28 -0
- package/src/__fixtures__/module-tree/root/modules/cdn/main.tf +15 -0
- package/src/__fixtures__/module-tree/root/modules/cdn/modules/bucket/main.tf +11 -0
- package/src/__fixtures__/module-tree/shared/main.tf +5 -0
- package/src/__fixtures__/references/main.tf +56 -0
- package/src/codegen/lint-rules-page.test.ts +28 -0
- package/src/composites/terraform-adopt-op.acceptance.test.ts +166 -0
- package/src/composites/terraform-adopt-op.test.ts +170 -0
- package/src/composites/terraform-adopt-op.ts +197 -0
- package/src/composites/terraform-apply-op.acceptance.test.ts +183 -5
- package/src/composites/terraform-apply-op.test.ts +167 -15
- package/src/composites/terraform-apply-op.ts +105 -28
- package/src/composites/terraform-watch-op.test.ts +85 -26
- package/src/composites/terraform-watch-op.ts +130 -73
- package/src/config.ts +32 -3
- package/src/describe-resources.live.test.ts +598 -0
- package/src/describe-resources.test.ts +53 -0
- package/src/describe-resources.ts +811 -28
- package/src/hcl/descend.test.ts +162 -0
- package/src/hcl/descend.ts +273 -0
- package/src/hcl/marker.test.ts +147 -0
- package/src/hcl/marker.ts +235 -0
- package/src/hcl/parse.test.ts +69 -0
- package/src/hcl/parse.ts +249 -27
- package/src/hcl/references.test.ts +116 -0
- package/src/hcl/references.ts +172 -0
- package/src/hcl/roots.ts +59 -5
- package/src/hcl/suppressions.test.ts +147 -0
- package/src/hcl/suppressions.ts +204 -0
- package/src/hcl/value.test.ts +73 -0
- package/src/hcl/value.ts +106 -0
- package/src/index.ts +31 -1
- package/src/lint/audit-catalog.ts +264 -3
- package/src/lint/audit-lineage.ts +319 -1
- package/src/lint/audit.test.ts +40 -1
- package/src/lint/post-synth/blocks.ts +109 -0
- package/src/lint/post-synth/fixtures/TF001/negative.tf +28 -0
- package/src/lint/post-synth/fixtures/TF001/positive.tf +24 -0
- package/src/lint/post-synth/fixtures/TF002/negative.tf +17 -0
- package/src/lint/post-synth/fixtures/TF002/positive-legacy.tf +10 -0
- package/src/lint/post-synth/fixtures/TF002/positive-no-version.tf +12 -0
- package/src/lint/post-synth/fixtures/TF002/positive.tf +7 -0
- package/src/lint/post-synth/fixtures/TF003/negative.tf +10 -0
- package/src/lint/post-synth/fixtures/TF003/positive.tf +8 -0
- package/src/lint/post-synth/fixtures/TF004/negative-git.tf +3 -0
- package/src/lint/post-synth/fixtures/TF004/negative-local.tf +3 -0
- package/src/lint/post-synth/fixtures/TF004/negative.tf +4 -0
- package/src/lint/post-synth/fixtures/TF004/positive.tf +3 -0
- package/src/lint/post-synth/fixtures/TF005/negative-local.tf +3 -0
- package/src/lint/post-synth/fixtures/TF005/negative-registry.tf +3 -0
- package/src/lint/post-synth/fixtures/TF005/negative-sha.tf +3 -0
- package/src/lint/post-synth/fixtures/TF005/negative.tf +3 -0
- package/src/lint/post-synth/fixtures/TF005/positive-branch.tf +3 -0
- package/src/lint/post-synth/fixtures/TF005/positive-non-semver.tf +3 -0
- package/src/lint/post-synth/fixtures/TF005/positive.tf +3 -0
- package/src/lint/post-synth/fixtures/TF006/negative.tf +5 -0
- package/src/lint/post-synth/fixtures/TF006/positive-empty-default.tf +11 -0
- package/src/lint/post-synth/fixtures/TF006/positive.tf +6 -0
- package/src/lint/post-synth/fixtures/TF007/negative.tf +10 -0
- package/src/lint/post-synth/fixtures/TF007/positive.tf +9 -0
- package/src/lint/post-synth/fixtures/TF008/negative-variable.tf +10 -0
- package/src/lint/post-synth/fixtures/TF008/negative.tf +8 -0
- package/src/lint/post-synth/fixtures/TF008/positive-other-provider.tf +10 -0
- package/src/lint/post-synth/fixtures/TF008/positive.tf +5 -0
- package/src/lint/post-synth/fixtures/TF009/negative.tf +10 -0
- package/src/lint/post-synth/fixtures/TF009/positive.tf +4 -0
- package/src/lint/post-synth/fixtures/TF010/negative.tf +5 -0
- package/src/lint/post-synth/fixtures/TF010/positive.tf +4 -0
- package/src/lint/post-synth/fixtures/TF011/negative.tf +5 -0
- package/src/lint/post-synth/fixtures/TF011/positive-blank.tf +5 -0
- package/src/lint/post-synth/fixtures/TF011/positive.tf +4 -0
- package/src/lint/post-synth/fixtures/TF012/negative.tf +4 -0
- package/src/lint/post-synth/fixtures/TF012/positive.tf +3 -0
- package/src/lint/post-synth/fixtures/TF013/negative.tf +8 -0
- package/src/lint/post-synth/fixtures/TF013/positive-data.tf +8 -0
- package/src/lint/post-synth/fixtures/TF013/positive.tf +8 -0
- package/src/lint/post-synth/fixtures/TF014/negative/main.tf +26 -0
- package/src/lint/post-synth/fixtures/TF014/negative/modules/cdn/main.tf +19 -0
- package/src/lint/post-synth/fixtures/TF014/positive/main.tf +16 -0
- package/src/lint/post-synth/fixtures/TF014/positive/modules/cdn/main.tf +15 -0
- package/src/lint/post-synth/fixtures/TF015/negative/main.tf +13 -0
- package/src/lint/post-synth/fixtures/TF015/negative/modules/cdn/main.tf +16 -0
- package/src/lint/post-synth/fixtures/TF015/positive/main.tf +13 -0
- package/src/lint/post-synth/fixtures/TF015/positive/modules/cdn/main.tf +14 -0
- package/src/lint/post-synth/fixtures/TF016/negative-template.tf +7 -0
- package/src/lint/post-synth/fixtures/TF016/negative.tf +16 -0
- package/src/lint/post-synth/fixtures/TF016/positive-shared-file.tf +12 -0
- package/src/lint/post-synth/fixtures/TF016/positive.tf +13 -0
- package/src/lint/post-synth/fixtures/TF017/negative.tf +4 -0
- package/src/lint/post-synth/fixtures/TF017/positive.tf +5 -0
- package/src/lint/post-synth/fixtures/TF018/negative.tf +9 -0
- package/src/lint/post-synth/fixtures/TF018/positive-data.tf +9 -0
- package/src/lint/post-synth/fixtures/TF018/positive.tf +4 -0
- package/src/lint/post-synth/fixtures/TF019/negative.tf +14 -0
- package/src/lint/post-synth/fixtures/TF019/positive-output.tf +5 -0
- package/src/lint/post-synth/fixtures/TF019/positive.tf +16 -0
- package/src/lint/post-synth/fixtures/TF020/negative.tf +43 -0
- package/src/lint/post-synth/fixtures/TF020/positive.tf +32 -0
- package/src/lint/post-synth/fixtures/TF020/scoped/main.tf +21 -0
- package/src/lint/post-synth/fixtures/TF020/scoped/modules/cdn/main.tf +13 -0
- package/src/lint/post-synth/fixtures/TF021/negative.tf +31 -0
- package/src/lint/post-synth/fixtures/TF021/positive-length.tf +8 -0
- package/src/lint/post-synth/fixtures/TF021/positive.tf +8 -0
- package/src/lint/post-synth/fixtures/TF022/negative-locator.tf +7 -0
- package/src/lint/post-synth/fixtures/TF022/negative.tf +10 -0
- package/src/lint/post-synth/fixtures/TF022/positive-nested.tf +10 -0
- package/src/lint/post-synth/fixtures/TF022/positive.tf +6 -0
- package/src/lint/post-synth/fixtures/TF023/negative/main.tf +15 -0
- package/src/lint/post-synth/fixtures/TF023/positive/main.tf +9 -0
- package/src/lint/post-synth/fixtures/TF023/positive/state.json +24 -0
- package/src/lint/post-synth/fixtures/TF024/live-clean/main.tf +9 -0
- package/src/lint/post-synth/fixtures/TF024/live-with-backend/main.tf +14 -0
- package/src/lint/post-synth/fixtures/TF024/live-with-cloud/main.tf +16 -0
- package/src/lint/post-synth/fixtures/TF025/live-clean/main.tf +13 -0
- package/src/lint/post-synth/fixtures/TF025/live-with-workspace-ref/main.tf +13 -0
- package/src/lint/post-synth/fixtures/TF026/live-default/main.tf +9 -0
- package/src/lint/post-synth/fixtures/TF026/live-explicit-delete/main.tf +13 -0
- package/src/lint/post-synth/fixtures/TF026/live-keep/main.tf +13 -0
- package/src/lint/post-synth/fixtures/load.ts +108 -0
- package/src/lint/post-synth/index.ts +48 -0
- package/src/lint/post-synth/module-rules.test.ts +153 -0
- package/src/lint/post-synth/module-source.test.ts +75 -0
- package/src/lint/post-synth/module-source.ts +121 -0
- package/src/lint/post-synth/post-synth.test.ts +14 -15
- package/src/lint/post-synth/scope.ts +107 -0
- package/src/lint/post-synth/single-block.test.ts +263 -0
- package/src/lint/post-synth/suppression.test.ts +110 -0
- package/src/lint/post-synth/tf001.ts +32 -8
- package/src/lint/post-synth/tf002.test.ts +93 -0
- package/src/lint/post-synth/tf002.ts +151 -0
- package/src/lint/post-synth/tf003.test.ts +75 -0
- package/src/lint/post-synth/tf003.ts +71 -0
- package/src/lint/post-synth/tf004.test.ts +47 -0
- package/src/lint/post-synth/tf004.ts +63 -0
- package/src/lint/post-synth/tf005.test.ts +57 -0
- package/src/lint/post-synth/tf005.ts +76 -0
- package/src/lint/post-synth/tf006.ts +53 -0
- package/src/lint/post-synth/tf007.ts +78 -0
- package/src/lint/post-synth/tf008.ts +75 -0
- package/src/lint/post-synth/tf009.ts +59 -0
- package/src/lint/post-synth/tf010.ts +51 -0
- package/src/lint/post-synth/tf011.ts +49 -0
- package/src/lint/post-synth/tf012.ts +47 -0
- package/src/lint/post-synth/tf013.ts +63 -0
- package/src/lint/post-synth/tf014.ts +72 -0
- package/src/lint/post-synth/tf015.ts +63 -0
- package/src/lint/post-synth/tf016.ts +75 -0
- package/src/lint/post-synth/tf017.ts +50 -0
- package/src/lint/post-synth/tf018.ts +72 -0
- package/src/lint/post-synth/tf019.ts +72 -0
- package/src/lint/post-synth/tf020.ts +123 -0
- package/src/lint/post-synth/tf021.ts +126 -0
- package/src/lint/post-synth/tf022.ts +61 -0
- package/src/lint/post-synth/tf024.test.ts +42 -0
- package/src/lint/post-synth/tf024.ts +70 -0
- package/src/lint/post-synth/tf025.test.ts +46 -0
- package/src/lint/post-synth/tf025.ts +101 -0
- package/src/lint/post-synth/tf026.test.ts +56 -0
- package/src/lint/post-synth/tf026.ts +80 -0
- package/src/lint/presets.test.ts +72 -0
- package/src/lint/rules/fixtures/TF101/negative-op.ts +2 -0
- package/src/lint/rules/fixtures/TF101/positive-op.ts +2 -0
- package/src/lint/rules/fixtures/load.ts +53 -0
- package/src/lint/rules/plan-before-apply.test.ts +112 -7
- package/src/lint/rules/plan-before-apply.ts +11 -0
- package/src/lint/secret-shape.test.ts +150 -0
- package/src/lint/secret-shape.ts +159 -0
- package/src/live-ownership.ts +60 -0
- package/src/lsp/completions.test.ts +3 -2
- package/src/lsp/option-keys.ts +10 -5
- package/src/op/activities/choudoufu.acceptance.test.ts +143 -0
- package/src/op/activities/choudoufu.test.ts +693 -0
- package/src/op/activities/index.ts +40 -4
- package/src/op/activities/live-detect.test.ts +143 -0
- package/src/op/activities/live-detect.ts +162 -0
- package/src/op/activities/registry.test.ts +11 -3
- package/src/op/activities/terraform.test.ts +7 -3
- package/src/op/activities/terraform.ts +823 -69
- package/src/op/activity-contracts.test.ts +147 -0
- package/src/op/activity-contracts.ts +178 -0
- package/src/op/adoption.test.ts +170 -0
- package/src/op/adoption.ts +241 -0
- package/src/op/builders.test.ts +60 -5
- package/src/op/builders.ts +93 -5
- package/src/op/resolve-root-mode.test.ts +85 -0
- package/src/op/resolve-root-mode.ts +72 -0
- package/src/plugin.test.ts +68 -0
- package/src/plugin.ts +75 -9
- package/src/skills/chant-terraform.md +1 -1
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { describe, test, expect } from "vitest";
|
|
2
|
+
import { isActivityContract, validateActivitySteps, validateStepOutputRefs, stepOutput, type ActivityContract } from "@intentius/chant/op";
|
|
3
|
+
import * as contracts from "./activity-contracts";
|
|
4
|
+
import * as activities from "./activities";
|
|
5
|
+
|
|
6
|
+
const CONTRACTS: Map<string, ActivityContract> = new Map(
|
|
7
|
+
Object.values(contracts).filter(isActivityContract).map((c) => [c.name, c]),
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The activities this lexicon contributes to the run-time registry — every
|
|
12
|
+
* exported function that is one, as `collectActivities` keys them.
|
|
13
|
+
* `terraformInitCommand` and friends are pure builders the tests assert on,
|
|
14
|
+
* not activities a step can name, so the list is spelled out rather than
|
|
15
|
+
* derived from the module's exports.
|
|
16
|
+
*/
|
|
17
|
+
const ACTIVITY_NAMES = [
|
|
18
|
+
"terraformInit",
|
|
19
|
+
"terraformPlan",
|
|
20
|
+
"terraformApply",
|
|
21
|
+
"terraformShow",
|
|
22
|
+
"choudoufuLivePlan",
|
|
23
|
+
"choudoufuLiveLs",
|
|
24
|
+
"choudoufuLiveCheck",
|
|
25
|
+
"choudoufuAdopt",
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
describe("terraform activity contracts (#2101)", () => {
|
|
29
|
+
test("every activity this lexicon ships has a contract", () => {
|
|
30
|
+
for (const name of ACTIVITY_NAMES) {
|
|
31
|
+
expect(CONTRACTS.get(name), `no contract declared for ${name}`).toBeDefined();
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("each name in ACTIVITY_NAMES really is an exported activity", () => {
|
|
36
|
+
for (const name of ACTIVITY_NAMES) {
|
|
37
|
+
expect(typeof (activities as unknown as Record<string, unknown>)[name]).toBe("function");
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("the module exports nothing but contracts", () => {
|
|
42
|
+
for (const [key, value] of Object.entries(contracts)) {
|
|
43
|
+
expect(isActivityContract(value), `${key} is not an ActivityContract`).toBe(true);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("every contract declares a return schema — OPS013 needs one to validate a reference", () => {
|
|
48
|
+
for (const name of ACTIVITY_NAMES) {
|
|
49
|
+
expect(CONTRACTS.get(name)?.returns, `${name} declares no return schema`).toBeDefined();
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("`root` is declared as the entity every step touches (#2022)", () => {
|
|
54
|
+
for (const name of ACTIVITY_NAMES) {
|
|
55
|
+
expect(CONTRACTS.get(name)?.entities).toEqual(["root"]);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// ── The failure classes the contracts exist to catch ───────────────────
|
|
60
|
+
|
|
61
|
+
const op = (steps: unknown[]) => ({
|
|
62
|
+
name: "app-apply",
|
|
63
|
+
phases: [{ name: "Phase", steps: steps as never[] }],
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("a misspelled arg key is an error, not a silently dropped field", () => {
|
|
67
|
+
const issues = validateActivitySteps(
|
|
68
|
+
op([{ kind: "activity", fn: "terraformPlan", args: { root: "app", planfile: "chant.tfplan" } }]),
|
|
69
|
+
CONTRACTS,
|
|
70
|
+
);
|
|
71
|
+
expect(issues.some((i) => i.message.includes("planfile"))).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("a missing `root` is an error", () => {
|
|
75
|
+
const issues = validateActivitySteps(
|
|
76
|
+
op([{ kind: "activity", fn: "terraformApply", args: {} }]),
|
|
77
|
+
CONTRACTS,
|
|
78
|
+
);
|
|
79
|
+
expect(issues.length).toBeGreaterThan(0);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("the args the examples' composites build validate clean", () => {
|
|
83
|
+
const issues = validateActivitySteps(
|
|
84
|
+
op([
|
|
85
|
+
{ kind: "activity", fn: "terraformInit", args: { root: "app" }, profile: "longInfra" },
|
|
86
|
+
{ kind: "activity", fn: "terraformPlan", args: { root: "app", planFile: "chant.tfplan" }, id: "plan", profile: "longInfra" },
|
|
87
|
+
{ kind: "activity", fn: "terraformShow", args: { root: "app", planFile: stepOutput("plan", "planFile") }, profile: "fastIdempotent" },
|
|
88
|
+
{ kind: "activity", fn: "terraformApply", args: { root: "app", planFile: stepOutput("plan", "planFile") }, profile: "longInfra" },
|
|
89
|
+
]),
|
|
90
|
+
CONTRACTS,
|
|
91
|
+
);
|
|
92
|
+
expect(issues).toEqual([]);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("`plan.out.planFile` resolves against terraformPlan's declared return type", () => {
|
|
96
|
+
const issues = validateStepOutputRefs(
|
|
97
|
+
op([
|
|
98
|
+
{ kind: "activity", fn: "terraformPlan", args: { root: "app" }, id: "plan" },
|
|
99
|
+
{ kind: "activity", fn: "terraformApply", args: { root: "app", planFile: stepOutput("plan", "planFile") } },
|
|
100
|
+
]),
|
|
101
|
+
CONTRACTS,
|
|
102
|
+
);
|
|
103
|
+
expect(issues).toEqual([]);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test("`plan.out.text` — the field a finding mode posts — resolves too", () => {
|
|
107
|
+
const issues = validateStepOutputRefs(
|
|
108
|
+
op([
|
|
109
|
+
{ kind: "activity", fn: "terraformPlan", args: { root: "app" }, id: "plan" },
|
|
110
|
+
{ kind: "activity", fn: "reconcilePr", args: { env: "app", body: stepOutput("plan", "text") } },
|
|
111
|
+
]),
|
|
112
|
+
CONTRACTS,
|
|
113
|
+
);
|
|
114
|
+
expect(issues).toEqual([]);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test("a live watch's three outcome attributes exist on choudoufuLivePlan's return type", () => {
|
|
118
|
+
const issues = validateActivitySteps(
|
|
119
|
+
op([
|
|
120
|
+
{
|
|
121
|
+
kind: "activity",
|
|
122
|
+
fn: "choudoufuLivePlan",
|
|
123
|
+
args: { root: "app" },
|
|
124
|
+
id: "plan",
|
|
125
|
+
outcomeAttribute: [
|
|
126
|
+
{ name: "Drift", from: "drift" },
|
|
127
|
+
{ name: "Unowned", from: "unowned" },
|
|
128
|
+
{ name: "Adoptable", from: "adoptable" },
|
|
129
|
+
],
|
|
130
|
+
},
|
|
131
|
+
]),
|
|
132
|
+
CONTRACTS,
|
|
133
|
+
);
|
|
134
|
+
expect(issues).toEqual([]);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("a reference into terraform's own `-json` document does not resolve, by design", () => {
|
|
138
|
+
const issues = validateStepOutputRefs(
|
|
139
|
+
op([
|
|
140
|
+
{ kind: "activity", fn: "terraformPlan", args: { root: "app" }, id: "plan" },
|
|
141
|
+
{ kind: "activity", fn: "reconcilePr", args: { env: "app", body: stepOutput("plan", "json.resource_changes") } },
|
|
142
|
+
]),
|
|
143
|
+
CONTRACTS,
|
|
144
|
+
);
|
|
145
|
+
expect(issues.some((i) => i.message.includes("does not exist"))).toBe(true);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Activity contracts for this lexicon's own `op/activities` (chant #2101).
|
|
3
|
+
*
|
|
4
|
+
* The schema-shaped sibling of `./activities/index.ts`: that module is
|
|
5
|
+
* resolved by convention at `@intentius/chant-lexicon-terraform/op/activities`
|
|
6
|
+
* for the run-time registry, and this one at
|
|
7
|
+
* `@intentius/chant-lexicon-terraform/op/activity-contracts` for build-time
|
|
8
|
+
* validation. `loadActivityContracts`
|
|
9
|
+
* (`packages/core/src/op/activity-contract-registry.ts`) merges what it finds
|
|
10
|
+
* here into the map core's own OPS012 and OPS013 validate every `Chant::Op`
|
|
11
|
+
* step against. Since #2122 moved those checks into core they fire on every
|
|
12
|
+
* project that declares an Op, so an Op built out of terraform steps is
|
|
13
|
+
* checked wherever it is built. Before this existed, both examples that carry
|
|
14
|
+
* an Op had to keep it outside `src/` where `chant dev check-lexicon`'s
|
|
15
|
+
* example build could not see it.
|
|
16
|
+
*
|
|
17
|
+
* Each contract's `args` schema mirrors the corresponding `*Args` interface in
|
|
18
|
+
* `./activities/terraform.ts`, and each `returns` schema the corresponding
|
|
19
|
+
* `*Result`. Authored with `z.strictObject(...)`, never `z.object(...)`: the
|
|
20
|
+
* default form silently drops an unrecognized key instead of rejecting it, so
|
|
21
|
+
* a misspelled `planfile` would vanish rather than fail the build. The
|
|
22
|
+
* `returns` schemas are the half OPS013 needs — `plan.out.planFile` feeding an
|
|
23
|
+
* Apply step, `plan.out.text` feeding a `reconcilePr` body — so a field a step
|
|
24
|
+
* references has to be spelled here for the reference to validate.
|
|
25
|
+
*
|
|
26
|
+
* `json` is `z.unknown()` throughout: it is terraform's own `-json` document,
|
|
27
|
+
* whose shape is the provider's, not chant's. A dot-path reference into it
|
|
28
|
+
* therefore does not resolve, which is deliberate. Reference `text`, or one of
|
|
29
|
+
* the counts projected out of the document, instead of reaching into
|
|
30
|
+
* terraform's JSON from an Op.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { z } from "zod";
|
|
34
|
+
import { activityContract } from "@intentius/chant/op";
|
|
35
|
+
|
|
36
|
+
/** `root` and `cwd`, the two fields every activity in this lexicon takes ({@link TerraformRootArgs}). */
|
|
37
|
+
const rootArgs = { root: z.string(), cwd: z.string().optional() };
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* `adds`/`changes`/`destroys`, the counts projected out of a plan's
|
|
41
|
+
* `resource_changes` ({@link PlanChangeCounts}).
|
|
42
|
+
*/
|
|
43
|
+
const planChangeCounts = { adds: z.number(), changes: z.number(), destroys: z.number() };
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* One adoption candidate as `../adoption.ts` models it. Loose on purpose: an
|
|
47
|
+
* Op never reaches into a candidate's fields, it hands the whole array from a
|
|
48
|
+
* live-plan step to `choudoufuAdopt`, and a whole-value reference is validated
|
|
49
|
+
* without walking the shape.
|
|
50
|
+
*/
|
|
51
|
+
const adoptionCandidate = z.object({}).loose();
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The `root` arg names the terraform root module the step acts on — the
|
|
55
|
+
* estate-side thing every activity here touches — so op.json's IR resolves it
|
|
56
|
+
* into the step's `entities` (#2022) and a renderer can join the step to the
|
|
57
|
+
* root instead of guessing off `cwd`, which is only where the config search
|
|
58
|
+
* starts.
|
|
59
|
+
*/
|
|
60
|
+
const rootEntity = { entities: ["root"] };
|
|
61
|
+
|
|
62
|
+
export const terraformInitContract = activityContract(
|
|
63
|
+
"terraformInit",
|
|
64
|
+
z.strictObject({
|
|
65
|
+
...rootArgs,
|
|
66
|
+
upgrade: z.boolean().optional(),
|
|
67
|
+
reconfigure: z.boolean().optional(),
|
|
68
|
+
}),
|
|
69
|
+
z.object({ dir: z.string(), workspace: z.string().optional() }),
|
|
70
|
+
rootEntity,
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
export const terraformPlanContract = activityContract(
|
|
74
|
+
"terraformPlan",
|
|
75
|
+
z.strictObject({
|
|
76
|
+
...rootArgs,
|
|
77
|
+
planFile: z.string().optional(),
|
|
78
|
+
destroy: z.boolean().optional(),
|
|
79
|
+
}),
|
|
80
|
+
z.object({
|
|
81
|
+
...planChangeCounts,
|
|
82
|
+
changed: z.boolean(),
|
|
83
|
+
planFile: z.string(),
|
|
84
|
+
dir: z.string(),
|
|
85
|
+
json: z.unknown(),
|
|
86
|
+
text: z.string(),
|
|
87
|
+
}),
|
|
88
|
+
rootEntity,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
export const terraformApplyContract = activityContract(
|
|
92
|
+
"terraformApply",
|
|
93
|
+
z.strictObject({ ...rootArgs, planFile: z.string().optional() }),
|
|
94
|
+
z.object({
|
|
95
|
+
planFile: z.string().optional(),
|
|
96
|
+
dir: z.string(),
|
|
97
|
+
applied: z.boolean(),
|
|
98
|
+
refused: z.enum(["approval-mismatch", "wrong-estate"]).optional(),
|
|
99
|
+
refusal: z.string().optional(),
|
|
100
|
+
}),
|
|
101
|
+
rootEntity,
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
export const terraformShowContract = activityContract(
|
|
105
|
+
"terraformShow",
|
|
106
|
+
z.strictObject({ ...rootArgs, planFile: z.string().optional() }),
|
|
107
|
+
z.object({
|
|
108
|
+
...planChangeCounts,
|
|
109
|
+
source: z.enum(["plan", "state"]),
|
|
110
|
+
json: z.unknown(),
|
|
111
|
+
text: z.string(),
|
|
112
|
+
dir: z.string(),
|
|
113
|
+
planFile: z.string().optional(),
|
|
114
|
+
}),
|
|
115
|
+
rootEntity,
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
export const choudoufuLivePlanContract = activityContract(
|
|
119
|
+
"choudoufuLivePlan",
|
|
120
|
+
z.strictObject({
|
|
121
|
+
...rootArgs,
|
|
122
|
+
estate: z.string().optional(),
|
|
123
|
+
adoptionOnly: z.boolean().optional(),
|
|
124
|
+
}),
|
|
125
|
+
z.object({
|
|
126
|
+
...planChangeCounts,
|
|
127
|
+
unowned: z.number(),
|
|
128
|
+
adoptable: z.number(),
|
|
129
|
+
drift: z.boolean(),
|
|
130
|
+
json: z.unknown(),
|
|
131
|
+
text: z.string(),
|
|
132
|
+
ledger: z.string(),
|
|
133
|
+
finding: z.string(),
|
|
134
|
+
adoptions: z.array(adoptionCandidate),
|
|
135
|
+
contested: z.array(adoptionCandidate),
|
|
136
|
+
ambiguous: z.number(),
|
|
137
|
+
dir: z.string(),
|
|
138
|
+
documentPath: z.string(),
|
|
139
|
+
estate: z.string(),
|
|
140
|
+
}),
|
|
141
|
+
rootEntity,
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
export const choudoufuLiveLsContract = activityContract(
|
|
145
|
+
"choudoufuLiveLs",
|
|
146
|
+
z.strictObject({
|
|
147
|
+
...rootArgs,
|
|
148
|
+
estate: z.string().optional(),
|
|
149
|
+
consistent: z.boolean().optional(),
|
|
150
|
+
}),
|
|
151
|
+
z.object({ json: z.unknown(), dir: z.string(), estate: z.string() }),
|
|
152
|
+
rootEntity,
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
export const choudoufuLiveCheckContract = activityContract(
|
|
156
|
+
"choudoufuLiveCheck",
|
|
157
|
+
z.strictObject({ ...rootArgs }),
|
|
158
|
+
z.object({ refused: z.boolean(), json: z.unknown(), text: z.string(), dir: z.string() }),
|
|
159
|
+
rootEntity,
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
export const choudoufuAdoptContract = activityContract(
|
|
163
|
+
"choudoufuAdopt",
|
|
164
|
+
z.strictObject({
|
|
165
|
+
...rootArgs,
|
|
166
|
+
adoptions: z.array(adoptionCandidate),
|
|
167
|
+
contested: z.array(adoptionCandidate).optional(),
|
|
168
|
+
}),
|
|
169
|
+
z.object({
|
|
170
|
+
mechanism: z.literal("tag-write"),
|
|
171
|
+
adopted: z.array(z.string()),
|
|
172
|
+
adoptedCount: z.number(),
|
|
173
|
+
refused: z.array(z.object({ addr: z.string(), identity: z.string(), reason: z.string() })),
|
|
174
|
+
ambiguous: z.number(),
|
|
175
|
+
dir: z.string(),
|
|
176
|
+
}),
|
|
177
|
+
rootEntity,
|
|
178
|
+
);
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The adoption ledger (#2105): reading `live-plan -json`'s `unowned` section,
|
|
3
|
+
* pairing it with the commands `-adoption-only` printed, and rendering it.
|
|
4
|
+
*
|
|
5
|
+
* The document fragments below are the shape choudoufu actually emits, taken
|
|
6
|
+
* from `views.StatelessUnowned`'s json tags (`internal/command/live_plan.go`)
|
|
7
|
+
* and checked against the recorded document #2104 captured off the emulator:
|
|
8
|
+
* an adoptable row carries `adopt_tofu_estate`/`adopt_tofu_address`, and a row
|
|
9
|
+
* held by somebody else carries `tofu_estate` and neither of the other two.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { describe, test, expect } from "vitest";
|
|
13
|
+
import { parseAdoptionCommands, readAdoptionLedger, renderAdoptionLedger } from "./adoption";
|
|
14
|
+
|
|
15
|
+
const adoptable = (addr: string, identity: string, type = "aws_vpc") => ({
|
|
16
|
+
addr,
|
|
17
|
+
type,
|
|
18
|
+
identity,
|
|
19
|
+
adopt_tofu_estate: "prod-networking",
|
|
20
|
+
adopt_tofu_address: addr,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe("readAdoptionLedger (#2105)", () => {
|
|
24
|
+
test("a row carrying both marker values is adoptable", () => {
|
|
25
|
+
const ledger = readAdoptionLedger({ unowned: [adoptable("aws_vpc.main", "vpc-0abc")] });
|
|
26
|
+
expect(ledger.adoptions).toEqual([
|
|
27
|
+
{
|
|
28
|
+
addr: "aws_vpc.main",
|
|
29
|
+
type: "aws_vpc",
|
|
30
|
+
identity: "vpc-0abc",
|
|
31
|
+
markerEstate: "prod-networking",
|
|
32
|
+
markerAddress: "aws_vpc.main",
|
|
33
|
+
},
|
|
34
|
+
]);
|
|
35
|
+
expect(ledger.contested).toEqual([]);
|
|
36
|
+
expect(ledger.ambiguous).toBe(0);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("a row held by another estate is neither adoptable nor contested", () => {
|
|
40
|
+
// Adoption was not this run's to offer, so there is no tag write to refuse.
|
|
41
|
+
const ledger = readAdoptionLedger({
|
|
42
|
+
unowned: [
|
|
43
|
+
{
|
|
44
|
+
addr: "aws_cloudwatch_log_group.held_elsewhere",
|
|
45
|
+
type: "aws_cloudwatch_log_group",
|
|
46
|
+
identity: "/estate/held-elsewhere",
|
|
47
|
+
tofu_estate: "other-estate",
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
});
|
|
51
|
+
expect(ledger.adoptions).toEqual([]);
|
|
52
|
+
expect(ledger.contested).toEqual([]);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("two live resources at one declared address are contested, never adoptable", () => {
|
|
56
|
+
const ledger = readAdoptionLedger({
|
|
57
|
+
unowned: [
|
|
58
|
+
adoptable("aws_vpc.main", "vpc-0abc"),
|
|
59
|
+
adoptable("aws_vpc.main", "vpc-0def"),
|
|
60
|
+
adoptable("aws_subnet.app", "subnet-01", "aws_subnet"),
|
|
61
|
+
],
|
|
62
|
+
});
|
|
63
|
+
expect(ledger.adoptions.map((c) => c.addr)).toEqual(["aws_subnet.app"]);
|
|
64
|
+
expect(ledger.contested.map((c) => c.identity)).toEqual(["vpc-0abc", "vpc-0def"]);
|
|
65
|
+
// One address is contested, and both of its candidates are listed, so the
|
|
66
|
+
// report shows what the choice is between.
|
|
67
|
+
expect(ledger.ambiguous).toBe(1);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("a command from the -adoption-only render rides on the matching candidate", () => {
|
|
71
|
+
const commands = new Map([["aws_vpc.main", "aws ec2 create-tags --resources 'vpc-0abc'"]]);
|
|
72
|
+
const ledger = readAdoptionLedger(
|
|
73
|
+
{ unowned: [adoptable("aws_vpc.main", "vpc-0abc"), adoptable("aws_subnet.app", "subnet-01", "aws_subnet")] },
|
|
74
|
+
commands,
|
|
75
|
+
);
|
|
76
|
+
expect(ledger.adoptions.find((c) => c.addr === "aws_vpc.main")?.command).toBe(
|
|
77
|
+
"aws ec2 create-tags --resources 'vpc-0abc'",
|
|
78
|
+
);
|
|
79
|
+
expect(ledger.adoptions.find((c) => c.addr === "aws_subnet.app")?.command).toBeUndefined();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("a document with no unowned section, or none at all, is an empty ledger", () => {
|
|
83
|
+
expect(readAdoptionLedger({ bound: [] })).toEqual({ adoptions: [], contested: [], ambiguous: 0 });
|
|
84
|
+
expect(readAdoptionLedger(undefined)).toEqual({ adoptions: [], contested: [], ambiguous: 0 });
|
|
85
|
+
expect(readAdoptionLedger({ unowned: "not a list" })).toEqual({ adoptions: [], contested: [], ambiguous: 0 });
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe("parseAdoptionCommands (#2105)", () => {
|
|
90
|
+
// The row shape `StatelessAdoptionHuman.adoptionSection` prints: the address
|
|
91
|
+
// line, then indented detail lines.
|
|
92
|
+
const RENDER = [
|
|
93
|
+
"",
|
|
94
|
+
"Adoptable now: 2 resource instances",
|
|
95
|
+
"",
|
|
96
|
+
"Each of these is a live resource this run found at a declared resource's identity.",
|
|
97
|
+
"",
|
|
98
|
+
" aws_vpc.main <- aws_vpc vpc-0abc",
|
|
99
|
+
" matched on: cidr_block=10.0.0.0/16",
|
|
100
|
+
" adopt with: aws ec2 create-tags --resources 'vpc-0abc' --tags 'Key=tofu-estate,Value=prod'",
|
|
101
|
+
" or write: tofu-estate=prod tofu-address=aws_vpc.main",
|
|
102
|
+
" aws_iam_role.app <- aws_iam_role app-role",
|
|
103
|
+
" or write: tofu-estate=prod tofu-address=aws_iam_role.app",
|
|
104
|
+
"",
|
|
105
|
+
].join("\n");
|
|
106
|
+
|
|
107
|
+
test("pairs each address with the command printed under it", () => {
|
|
108
|
+
expect(parseAdoptionCommands(RENDER).get("aws_vpc.main")).toBe(
|
|
109
|
+
"aws ec2 create-tags --resources 'vpc-0abc' --tags 'Key=tofu-estate,Value=prod'",
|
|
110
|
+
);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("an address with no printed command is absent, not empty", () => {
|
|
114
|
+
// IAM has its own tagging call, which choudoufu does not spell out.
|
|
115
|
+
const commands = parseAdoptionCommands(RENDER);
|
|
116
|
+
expect(commands.has("aws_iam_role.app")).toBe(false);
|
|
117
|
+
expect(commands.size).toBe(1);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("a render with no adoptable section yields nothing", () => {
|
|
121
|
+
expect(parseAdoptionCommands("Adoption: 3 declared resource instances\n\nNo changes.\n").size).toBe(0);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe("renderAdoptionLedger (#2105)", () => {
|
|
126
|
+
test("one line per adoptable match, carrying the address, identity and both marker values", () => {
|
|
127
|
+
const text = renderAdoptionLedger(
|
|
128
|
+
readAdoptionLedger({ unowned: [adoptable("aws_vpc.main", "vpc-0abc")] }),
|
|
129
|
+
"prod-networking",
|
|
130
|
+
);
|
|
131
|
+
expect(text).toContain(
|
|
132
|
+
" aws_vpc.main <- aws_vpc vpc-0abc write: tofu-estate=prod-networking tofu-address=aws_vpc.main",
|
|
133
|
+
);
|
|
134
|
+
expect(text).toContain('Adoptable now: 1 live resource, estate "prod-networking"');
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("an empty ledger still says so, rather than rendering nothing", () => {
|
|
138
|
+
const text = renderAdoptionLedger(readAdoptionLedger({ unowned: [] }), "prod-networking");
|
|
139
|
+
expect(text).toContain("Adoptable now: nothing");
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("contested addresses are listed under their own heading with no marker values offered", () => {
|
|
143
|
+
const text = renderAdoptionLedger(
|
|
144
|
+
readAdoptionLedger({ unowned: [adoptable("aws_vpc.main", "vpc-0abc"), adoptable("aws_vpc.main", "vpc-0def")] }),
|
|
145
|
+
"prod-networking",
|
|
146
|
+
);
|
|
147
|
+
expect(text).toContain("Ambiguous: 1 declared address with more than one candidate");
|
|
148
|
+
expect(text).toContain(" aws_vpc.main <- aws_vpc vpc-0abc");
|
|
149
|
+
expect(text).toContain(" aws_vpc.main <- aws_vpc vpc-0def");
|
|
150
|
+
// Nothing in the contested block offers a write: there is no single one.
|
|
151
|
+
const contestedBlock = text.slice(text.indexOf("Ambiguous:"));
|
|
152
|
+
expect(contestedBlock).not.toContain("write: tofu-estate=");
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("nothing outside the unowned section reaches the rendering", () => {
|
|
156
|
+
// The document carries every declared instance the plan bound and every
|
|
157
|
+
// one it could not read, with identities and reasons. The ledger is a
|
|
158
|
+
// projection of `unowned` alone, and this is what says so.
|
|
159
|
+
const document = {
|
|
160
|
+
unowned: [adoptable("aws_vpc.main", "vpc-0abc")],
|
|
161
|
+
bound: [{ addr: "aws_db_instance.prod", identity: "prod-db-0f1e2d3c" }],
|
|
162
|
+
omissions: [{ addr: "aws_kms_key.signing", detail: "provider error" }],
|
|
163
|
+
};
|
|
164
|
+
const text = renderAdoptionLedger(readAdoptionLedger(document), "prod-networking");
|
|
165
|
+
expect(text).not.toContain("aws_db_instance.prod");
|
|
166
|
+
expect(text).not.toContain("prod-db-0f1e2d3c");
|
|
167
|
+
expect(text).not.toContain("aws_kms_key.signing");
|
|
168
|
+
expect(text).not.toContain("adopt_tofu_estate");
|
|
169
|
+
});
|
|
170
|
+
});
|