@intentius/chant 0.124.52 → 0.124.54
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/audit/discover.d.ts.map +1 -1
- package/dist/audit/discover.js +3 -1
- package/dist/behaviour.d.ts +1 -1
- package/dist/behaviour.d.ts.map +1 -1
- package/dist/capabilities/templates.d.ts +65 -0
- package/dist/capabilities/templates.d.ts.map +1 -0
- package/dist/capabilities/templates.js +193 -0
- package/dist/capabilities/templates.json +145 -0
- package/dist/change-set-graph.d.ts +35 -3
- package/dist/change-set-graph.d.ts.map +1 -1
- package/dist/change-set-graph.js +60 -9
- package/dist/change-set.d.ts +65 -7
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +23 -12
- package/dist/ci-identity.d.ts +7 -2
- package/dist/ci-identity.d.ts.map +1 -1
- package/dist/ci-identity.js +4 -1
- package/dist/cli/commands/import-index.d.ts +52 -0
- package/dist/cli/commands/import-index.d.ts.map +1 -0
- package/dist/cli/commands/import-index.js +226 -0
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/lint.js +4 -2
- package/dist/cli/handlers/components.d.ts.map +1 -1
- package/dist/cli/handlers/components.js +11 -2
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.js +16 -3
- package/dist/cli/handlers/graph.d.ts.map +1 -1
- package/dist/cli/handlers/graph.js +22 -4
- package/dist/cli/handlers/index-cloud.d.ts +14 -0
- package/dist/cli/handlers/index-cloud.d.ts.map +1 -0
- package/dist/cli/handlers/index-cloud.js +22 -0
- package/dist/cli/handlers/index-diff.d.ts +18 -0
- package/dist/cli/handlers/index-diff.d.ts.map +1 -0
- package/dist/cli/handlers/index-diff.js +142 -0
- package/dist/cli/handlers/lifecycle-drift.d.ts +25 -0
- package/dist/cli/handlers/lifecycle-drift.d.ts.map +1 -0
- package/dist/cli/handlers/lifecycle-drift.js +64 -0
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.js +188 -28
- package/dist/cli/handlers/misc.d.ts +9 -0
- package/dist/cli/handlers/misc.d.ts.map +1 -1
- package/dist/cli/handlers/misc.js +77 -0
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +15 -2
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/promote.js +89 -15
- package/dist/cli/handlers/query.d.ts.map +1 -1
- package/dist/cli/handlers/query.js +28 -4
- package/dist/cli/handlers/scenario.d.ts.map +1 -1
- package/dist/cli/handlers/scenario.js +8 -7
- package/dist/cli/handlers/search.d.ts +34 -4
- package/dist/cli/handlers/search.d.ts.map +1 -1
- package/dist/cli/handlers/search.js +216 -36
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +55 -1
- package/dist/cli/mcp/lifecycle-tools.d.ts.map +1 -1
- package/dist/cli/mcp/lifecycle-tools.js +3 -2
- package/dist/cli/mcp/resource-handlers.d.ts.map +1 -1
- package/dist/cli/mcp/resource-handlers.js +4 -3
- package/dist/cli/mcp/workspace-tools.js +1 -1
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +55 -2
- package/dist/cli/registry.d.ts +19 -1
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/cloud-lister/aws.d.ts +35 -0
- package/dist/cloud-lister/aws.d.ts.map +1 -0
- package/dist/cloud-lister/aws.js +203 -0
- package/dist/cloud-lister/azure.d.ts +34 -0
- package/dist/cloud-lister/azure.d.ts.map +1 -0
- package/dist/cloud-lister/azure.js +129 -0
- package/dist/cloud-lister/common.d.ts +40 -0
- package/dist/cloud-lister/common.d.ts.map +1 -0
- package/dist/cloud-lister/common.js +83 -0
- package/dist/cloud-lister/credentials.d.ts +50 -0
- package/dist/cloud-lister/credentials.d.ts.map +1 -0
- package/dist/cloud-lister/credentials.js +86 -0
- package/dist/cloud-lister/fakes.d.ts +27 -0
- package/dist/cloud-lister/fakes.d.ts.map +1 -0
- package/dist/cloud-lister/fakes.js +50 -0
- package/dist/cloud-lister/gcp.d.ts +42 -0
- package/dist/cloud-lister/gcp.d.ts.map +1 -0
- package/dist/cloud-lister/gcp.js +143 -0
- package/dist/cloud-lister/guard.d.ts +49 -0
- package/dist/cloud-lister/guard.d.ts.map +1 -0
- package/dist/cloud-lister/guard.js +103 -0
- package/dist/cloud-lister/ids.d.ts +23 -0
- package/dist/cloud-lister/ids.d.ts.map +1 -0
- package/dist/cloud-lister/ids.js +42 -0
- package/dist/cloud-lister/index.d.ts +67 -0
- package/dist/cloud-lister/index.d.ts.map +1 -0
- package/dist/cloud-lister/index.js +102 -0
- package/dist/cloud-lister/kubernetes.d.ts +64 -0
- package/dist/cloud-lister/kubernetes.d.ts.map +1 -0
- package/dist/cloud-lister/kubernetes.js +276 -0
- package/dist/cloud-lister/markers.d.ts +31 -0
- package/dist/cloud-lister/markers.d.ts.map +1 -0
- package/dist/cloud-lister/markers.js +73 -0
- package/dist/cloud-lister/mask.d.ts +31 -0
- package/dist/cloud-lister/mask.d.ts.map +1 -0
- package/dist/cloud-lister/mask.js +74 -0
- package/dist/cloud-lister/sigv4.d.ts +29 -0
- package/dist/cloud-lister/sigv4.d.ts.map +1 -0
- package/dist/cloud-lister/sigv4.js +54 -0
- package/dist/cloud-lister/transport.d.ts +55 -0
- package/dist/cloud-lister/transport.d.ts.map +1 -0
- package/dist/cloud-lister/transport.js +86 -0
- package/dist/cloud-lister/types.d.ts +121 -0
- package/dist/cloud-lister/types.d.ts.map +1 -0
- package/dist/cloud-lister/types.js +24 -0
- package/dist/components/capability-plugin-loader.d.ts.map +1 -1
- package/dist/components/capability-plugin-loader.js +6 -0
- package/dist/components/capability-plugin.d.ts +9 -0
- package/dist/components/capability-plugin.d.ts.map +1 -1
- package/dist/components/capability.d.ts +215 -0
- package/dist/components/capability.d.ts.map +1 -1
- package/dist/components/cli-support.d.ts +21 -0
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/cli-support.js +58 -14
- package/dist/components/declared-reads.d.ts +38 -0
- package/dist/components/declared-reads.d.ts.map +1 -0
- package/dist/components/declared-reads.js +131 -0
- package/dist/components/deploy-units.d.ts.map +1 -1
- package/dist/components/deploy-units.js +4 -0
- package/dist/components/driver.d.ts +34 -1
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/driver.js +66 -3
- package/dist/components/fan-out-run.d.ts +23 -1
- package/dist/components/fan-out-run.d.ts.map +1 -1
- package/dist/components/fan-out-run.js +82 -14
- package/dist/components/fan-out-support.d.ts +6 -0
- package/dist/components/fan-out-support.d.ts.map +1 -1
- package/dist/components/fan-out-support.js +4 -1
- package/dist/components/fan-out.d.ts +28 -0
- package/dist/components/fan-out.d.ts.map +1 -1
- package/dist/components/groups.d.ts +229 -0
- package/dist/components/groups.d.ts.map +1 -0
- package/dist/components/groups.js +760 -0
- package/dist/components/index.d.ts +6 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +4 -0
- package/dist/components/plan-shape-conformance.d.ts +48 -0
- package/dist/components/plan-shape-conformance.d.ts.map +1 -0
- package/dist/components/plan-shape-conformance.js +122 -0
- package/dist/components/pr-run.d.ts +41 -4
- package/dist/components/pr-run.d.ts.map +1 -1
- package/dist/components/pr-run.js +209 -39
- package/dist/components/promote-rounds.d.ts +9 -0
- package/dist/components/promote-rounds.d.ts.map +1 -1
- package/dist/components/promote-rounds.js +23 -2
- package/dist/components/promote.d.ts +17 -0
- package/dist/components/promote.d.ts.map +1 -1
- package/dist/components/promote.js +36 -6
- package/dist/components/release-gate.d.ts +23 -1
- package/dist/components/release-gate.d.ts.map +1 -1
- package/dist/components/release-gate.js +97 -33
- package/dist/components/run-groups.d.ts +58 -0
- package/dist/components/run-groups.d.ts.map +1 -0
- package/dist/components/run-groups.js +78 -0
- package/dist/components/starter-plugin.d.ts +1 -0
- package/dist/components/starter-plugin.d.ts.map +1 -1
- package/dist/components/starter-plugin.js +6 -1
- package/dist/components/verbs/index.d.ts +1 -0
- package/dist/components/verbs/index.d.ts.map +1 -1
- package/dist/components/verbs/index.js +1 -0
- package/dist/components/verbs/member-outputs.d.ts +187 -0
- package/dist/components/verbs/member-outputs.d.ts.map +1 -0
- package/dist/components/verbs/member-outputs.js +218 -0
- package/dist/config.d.ts +60 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +27 -0
- package/dist/deep-observation.d.ts +1 -13
- package/dist/deep-observation.d.ts.map +1 -1
- package/dist/deep-observation.js +5 -22
- package/dist/drift-record.d.ts +542 -0
- package/dist/drift-record.d.ts.map +1 -0
- package/dist/drift-record.js +886 -0
- package/dist/engine-facts.d.ts +144 -0
- package/dist/engine-facts.d.ts.map +1 -0
- package/dist/engine-facts.js +14 -0
- package/dist/graph-ir.d.ts +153 -2
- package/dist/graph-ir.d.ts.map +1 -1
- package/dist/graph-ir.js +224 -6
- package/dist/graph-refs.d.ts +145 -0
- package/dist/graph-refs.d.ts.map +1 -1
- package/dist/graph-refs.js +305 -0
- package/dist/graph-schema/fixtures/adopt.json +15 -0
- package/dist/graph-schema/fixtures/choudoufu.engine-graph.json +169 -0
- package/dist/graph-schema/fixtures/index.json +68 -0
- package/dist/graph-schema/fixtures/plunge.engine-graph.json +158 -0
- package/dist/graph-schema/fixtures.d.ts +23 -0
- package/dist/graph-schema/fixtures.d.ts.map +1 -0
- package/dist/graph-schema/fixtures.js +246 -0
- package/dist/graph-schema/generate.d.ts +15 -0
- package/dist/graph-schema/generate.d.ts.map +1 -0
- package/dist/graph-schema/generate.js +267 -0
- package/dist/graph-schema/graph.schema.json +1387 -0
- package/dist/health.schema.json +2 -1
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/instance-nodes.d.ts +159 -0
- package/dist/instance-nodes.d.ts.map +1 -0
- package/dist/instance-nodes.js +231 -0
- package/dist/lexicon.d.ts +72 -0
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/adoption-file.d.ts +56 -0
- package/dist/lifecycle/adoption-file.d.ts.map +1 -0
- package/dist/lifecycle/adoption-file.js +123 -0
- package/dist/lifecycle/cloud-index.d.ts +186 -0
- package/dist/lifecycle/cloud-index.d.ts.map +1 -0
- package/dist/lifecycle/cloud-index.js +618 -0
- package/dist/lifecycle/derive-edges.d.ts +59 -0
- package/dist/lifecycle/derive-edges.d.ts.map +1 -0
- package/dist/lifecycle/derive-edges.js +126 -0
- package/dist/lifecycle/git.d.ts +10 -9
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/git.js +7 -1
- package/dist/lifecycle/identity-match.d.ts +133 -0
- package/dist/lifecycle/identity-match.d.ts.map +1 -0
- package/dist/lifecycle/identity-match.js +147 -0
- package/dist/lifecycle/index-diff.d.ts +224 -0
- package/dist/lifecycle/index-diff.d.ts.map +1 -0
- package/dist/lifecycle/index-diff.js +327 -0
- package/dist/lifecycle/index-document.d.ts +130 -0
- package/dist/lifecycle/index-document.d.ts.map +1 -0
- package/dist/lifecycle/index-document.js +189 -0
- package/dist/lifecycle/index-drift.d.ts +121 -0
- package/dist/lifecycle/index-drift.d.ts.map +1 -0
- package/dist/lifecycle/index-drift.js +178 -0
- package/dist/lifecycle/index-import.d.ts +168 -0
- package/dist/lifecycle/index-import.d.ts.map +1 -0
- package/dist/lifecycle/index-import.js +515 -0
- package/dist/lifecycle/index-join.d.ts +129 -0
- package/dist/lifecycle/index-join.d.ts.map +1 -0
- package/dist/lifecycle/index-join.js +248 -0
- package/dist/lifecycle/index.d.ts +4 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/index.js +4 -0
- package/dist/lifecycle/observation-baseline.d.ts +61 -21
- package/dist/lifecycle/observation-baseline.d.ts.map +1 -1
- package/dist/lifecycle/observation-baseline.js +144 -19
- package/dist/lifecycle/observe.d.ts.map +1 -1
- package/dist/lifecycle/observe.js +6 -1
- package/dist/lifecycle/query.d.ts +45 -4
- package/dist/lifecycle/query.d.ts.map +1 -1
- package/dist/lifecycle/query.js +89 -7
- package/dist/lifecycle/replay.d.ts +10 -3
- package/dist/lifecycle/replay.d.ts.map +1 -1
- package/dist/lifecycle/replay.js +10 -6
- package/dist/lifecycle/scenario.d.ts +4 -3
- package/dist/lifecycle/scenario.d.ts.map +1 -1
- package/dist/lifecycle/snapshot-store-fake.d.ts +9 -0
- package/dist/lifecycle/snapshot-store-fake.d.ts.map +1 -0
- package/dist/lifecycle/snapshot-store-fake.js +21 -0
- package/dist/lifecycle/snapshot-store-s3.d.ts +102 -0
- package/dist/lifecycle/snapshot-store-s3.d.ts.map +1 -0
- package/dist/lifecycle/snapshot-store-s3.js +254 -0
- package/dist/lifecycle/snapshot-store.d.ts +170 -0
- package/dist/lifecycle/snapshot-store.d.ts.map +1 -0
- package/dist/lifecycle/snapshot-store.js +832 -0
- package/dist/lifecycle/snapshot.d.ts +11 -0
- package/dist/lifecycle/snapshot.d.ts.map +1 -1
- package/dist/lifecycle/snapshot.js +18 -5
- package/dist/lifecycle/type-listing.d.ts +148 -0
- package/dist/lifecycle/type-listing.d.ts.map +1 -0
- package/dist/lifecycle/type-listing.js +166 -0
- package/dist/lint/component-checks.d.ts +15 -1
- package/dist/lint/component-checks.d.ts.map +1 -1
- package/dist/lint/component-checks.js +1 -0
- package/dist/lint/presets/relaxed.json +2 -1
- package/dist/lint/presets/strict.json +2 -1
- package/dist/lint/rules/__fixtures__/comp/comp008/fail/dns.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp008/fail/dns.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp008/fail/dns.component.js +13 -0
- package/dist/lint/rules/__fixtures__/comp/comp008/pass/dns.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp008/pass/dns.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp008/pass/dns.component.js +8 -0
- package/dist/lint/rules/comp/comp008-applies-signal-only-root.d.ts +33 -0
- package/dist/lint/rules/comp/comp008-applies-signal-only-root.d.ts.map +1 -0
- package/dist/lint/rules/comp/comp008-applies-signal-only-root.js +123 -0
- package/dist/lint/rules/comp/index.d.ts +2 -1
- package/dist/lint/rules/comp/index.d.ts.map +1 -1
- package/dist/lint/rules/comp/index.js +4 -1
- package/dist/observation.d.ts +19 -2
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +18 -1
- package/dist/op/activities/activity-contracts.d.ts +1 -1
- package/dist/op/activities/activity-contracts.js +1 -1
- package/dist/op/activities/change-set.d.ts +6 -4
- package/dist/op/activities/change-set.d.ts.map +1 -1
- package/dist/op/activities/change-set.js +4 -2
- package/dist/op/activity-failure.d.ts +27 -0
- package/dist/op/activity-failure.d.ts.map +1 -1
- package/dist/op/activity-failure.js +31 -0
- package/dist/op/index.d.ts +2 -1
- package/dist/op/index.d.ts.map +1 -1
- package/dist/op/index.js +1 -1
- package/dist/op/local-executor.d.ts +5 -1
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/local-executor.js +22 -8
- package/dist/pr-loop.d.ts +56 -1
- package/dist/pr-loop.d.ts.map +1 -1
- package/dist/pr-loop.js +51 -1
- package/dist/pulumi-urn.d.ts +101 -0
- package/dist/pulumi-urn.d.ts.map +1 -0
- package/dist/pulumi-urn.js +131 -0
- package/dist/release-floors.d.ts +23 -0
- package/dist/release-floors.d.ts.map +1 -0
- package/dist/release-floors.js +22 -0
- package/dist/sensitive-key.d.ts +23 -0
- package/dist/sensitive-key.d.ts.map +1 -0
- package/dist/sensitive-key.js +31 -0
- package/dist/wave-runtime.mjs +234 -234
- package/dist/workspace/adoption-report.d.ts +53 -0
- package/dist/workspace/adoption-report.d.ts.map +1 -0
- package/dist/workspace/adoption-report.js +76 -0
- package/dist/workspace/capabilities-contract.d.ts +7 -0
- package/dist/workspace/capabilities-contract.d.ts.map +1 -1
- package/dist/workspace/capabilities-contract.js +2 -1
- package/dist/workspace/capabilities.schema.json +98 -0
- package/dist/workspace/change-set.schema.json +42 -3
- package/dist/workspace/ci-cloud-identity.d.ts +13 -1
- package/dist/workspace/ci-cloud-identity.d.ts.map +1 -1
- package/dist/workspace/ci-cloud-identity.js +30 -1
- package/dist/workspace/compose-graph.d.ts +10 -1
- package/dist/workspace/compose-graph.d.ts.map +1 -1
- package/dist/workspace/compose-graph.js +6 -1
- package/dist/workspace/conformance/index.d.ts +1 -1
- package/dist/workspace/conformance/index.d.ts.map +1 -1
- package/dist/workspace/conformance/index.js +6 -1
- package/dist/workspace/drift-cli.d.ts +81 -0
- package/dist/workspace/drift-cli.d.ts.map +1 -0
- package/dist/workspace/drift-cli.js +317 -0
- package/dist/workspace/drift.schema.json +672 -0
- package/dist/workspace/graph-cli.d.ts +2 -0
- package/dist/workspace/graph-cli.d.ts.map +1 -1
- package/dist/workspace/graph-cli.js +3 -0
- package/dist/workspace/graph.schema.json +72 -0
- package/dist/workspace/init.d.ts +5 -1
- package/dist/workspace/init.d.ts.map +1 -1
- package/dist/workspace/init.js +55 -9
- package/dist/workspace/ledger.schema.json +1 -1
- package/dist/workspace/nested-graph.d.ts.map +1 -1
- package/dist/workspace/nested-graph.js +2 -0
- package/dist/workspace/pr-report.schema.json +51 -2
- package/dist/workspace/promote-waves.d.ts +112 -0
- package/dist/workspace/promote-waves.d.ts.map +1 -0
- package/dist/workspace/promote-waves.js +290 -0
- package/dist/workspace/promote.d.ts +84 -2
- package/dist/workspace/promote.d.ts.map +1 -1
- package/dist/workspace/promote.js +347 -61
- package/dist/workspace/reason-codes.d.ts +12 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +14 -0
- package/dist/workspace/search-contract.d.ts +3 -3
- package/dist/workspace/search-contract.d.ts.map +1 -1
- package/dist/workspace/search-contract.js +2 -0
- package/dist/workspace/search.schema.json +103 -5
- package/package.json +14 -1
- package/src/__fixtures__/drift-record/README.md +58 -0
- package/src/__fixtures__/drift-record/choudoufu-cone.ndjson +4 -0
- package/src/__fixtures__/drift-record/choudoufu-resource.ndjson +3 -0
- package/src/__fixtures__/drift-record/component-status-ledger.json +65 -0
- package/src/__fixtures__/drift-record/component-status.json +91 -0
- package/src/__fixtures__/drift-record/deep-diff.json +259 -0
- package/src/__fixtures__/drift-record/lifecycle-diff-live.json +825 -0
- package/src/__fixtures__/drift-record/live-diff.json +125 -0
- package/src/audit/discover.e2e.test.ts +14 -0
- package/src/audit/discover.ts +3 -1
- package/src/behaviour.ts +1 -1
- package/src/capabilities/templates.json +145 -0
- package/src/capabilities/templates.test.ts +212 -0
- package/src/capabilities/templates.ts +204 -0
- package/src/change-set-bundle.test.ts +83 -0
- package/src/change-set-graph.ts +80 -9
- package/src/change-set.test.ts +24 -0
- package/src/change-set.ts +77 -14
- package/src/ci-identity-targets.test.ts +23 -0
- package/src/ci-identity.ts +10 -4
- package/src/cli/commands/import-index.test.ts +117 -0
- package/src/cli/commands/import-index.ts +265 -0
- package/src/cli/commands/lint.ts +4 -2
- package/src/cli/handlers/components.test.ts +9 -0
- package/src/cli/handlers/components.ts +12 -2
- package/src/cli/handlers/fan-out.ts +17 -3
- package/src/cli/handlers/graph.test.ts +42 -0
- package/src/cli/handlers/graph.ts +26 -4
- package/src/cli/handlers/index-cloud.ts +32 -0
- package/src/cli/handlers/index-diff.test.ts +68 -0
- package/src/cli/handlers/index-diff.ts +146 -0
- package/src/cli/handlers/ledger-root-member.test.ts +161 -0
- package/src/cli/handlers/lifecycle-drift.ts +68 -0
- package/src/cli/handlers/lifecycle.test.ts +33 -11
- package/src/cli/handlers/lifecycle.ts +205 -30
- package/src/cli/handlers/misc.ts +73 -0
- package/src/cli/handlers/pr-waves-blocked.test.ts +37 -2
- package/src/cli/handlers/pr.ts +14 -2
- package/src/cli/handlers/promote.test.ts +64 -0
- package/src/cli/handlers/promote.ts +81 -6
- package/src/cli/handlers/query.ts +27 -4
- package/src/cli/handlers/run-declared-reads-cli.test.ts +187 -0
- package/src/cli/handlers/run-groups-cli.test.ts +229 -0
- package/src/cli/handlers/scenario.test.ts +13 -0
- package/src/cli/handlers/scenario.ts +8 -7
- package/src/cli/handlers/search-index.test.ts +255 -0
- package/src/cli/handlers/search-json.test.ts +1 -1
- package/src/cli/handlers/search-ownership.test.ts +194 -0
- package/src/cli/handlers/search.ts +271 -38
- package/src/cli/main.test.ts +7 -0
- package/src/cli/main.ts +56 -1
- package/src/cli/mcp/lifecycle-tools.ts +3 -2
- package/src/cli/mcp/resource-handlers.ts +4 -3
- package/src/cli/mcp/workspace-tools.ts +1 -1
- package/src/cli/parse-args.ts +40 -2
- package/src/cli/registry.ts +19 -1
- package/src/cloud-lister/aws.test.ts +202 -0
- package/src/cloud-lister/aws.ts +226 -0
- package/src/cloud-lister/azure.test.ts +132 -0
- package/src/cloud-lister/azure.ts +142 -0
- package/src/cloud-lister/common.ts +115 -0
- package/src/cloud-lister/credentials.ts +110 -0
- package/src/cloud-lister/fakes.ts +66 -0
- package/src/cloud-lister/gcp.test.ts +121 -0
- package/src/cloud-lister/gcp.ts +159 -0
- package/src/cloud-lister/guard.test.ts +198 -0
- package/src/cloud-lister/guard.ts +111 -0
- package/src/cloud-lister/ids.ts +54 -0
- package/src/cloud-lister/index.ts +134 -0
- package/src/cloud-lister/kubernetes.test.ts +160 -0
- package/src/cloud-lister/kubernetes.ts +284 -0
- package/src/cloud-lister/markers.ts +74 -0
- package/src/cloud-lister/mask.ts +77 -0
- package/src/cloud-lister/shape.test.ts +81 -0
- package/src/cloud-lister/sigv4.ts +70 -0
- package/src/cloud-lister/transport.ts +118 -0
- package/src/cloud-lister/types.ts +135 -0
- package/src/components/capability-plugin-loader.ts +4 -0
- package/src/components/capability-plugin.ts +10 -0
- package/src/components/capability.ts +229 -0
- package/src/components/cli-support.ts +70 -14
- package/src/components/declared-reads.test.ts +103 -0
- package/src/components/declared-reads.ts +134 -0
- package/src/components/deploy-units.ts +4 -0
- package/src/components/driver.ts +89 -4
- package/src/components/fan-out-run.ts +95 -15
- package/src/components/fan-out-support.ts +10 -1
- package/src/components/fan-out.ts +30 -0
- package/src/components/groups.test.ts +320 -0
- package/src/components/groups.ts +880 -0
- package/src/components/index.ts +42 -0
- package/src/components/plan-shape-conformance.ts +130 -0
- package/src/components/pr-env-run-bundle.test.ts +2 -0
- package/src/components/pr-run.test.ts +516 -3
- package/src/components/pr-run.ts +231 -41
- package/src/components/promote-rounds.ts +25 -2
- package/src/components/promote.ts +55 -5
- package/src/components/registry.test.ts +1 -1
- package/src/components/release-gate.ts +115 -30
- package/src/components/release-groups.test.ts +362 -0
- package/src/components/run-groups.ts +113 -0
- package/src/components/starter-plugin.ts +6 -0
- package/src/components/verbs/index.ts +1 -0
- package/src/components/verbs/member-outputs.test.ts +205 -0
- package/src/components/verbs/member-outputs.ts +308 -0
- package/src/config.ts +44 -0
- package/src/deep-observation.ts +5 -23
- package/src/drift-record.test.ts +596 -0
- package/src/drift-record.ts +1206 -0
- package/src/engine-facts.guard.test.ts +111 -0
- package/src/engine-facts.test.ts +178 -0
- package/src/engine-facts.ts +154 -0
- package/src/gated-waves-bundle.test.ts +2 -0
- package/src/graph-ir-live.test.ts +92 -0
- package/src/graph-ir.ts +317 -7
- package/src/graph-refs.ts +416 -0
- package/src/graph-schema/fixtures/adopt.json +15 -0
- package/src/graph-schema/fixtures/choudoufu.engine-graph.json +169 -0
- package/src/graph-schema/fixtures/index.json +68 -0
- package/src/graph-schema/fixtures/plunge.engine-graph.json +158 -0
- package/src/graph-schema/fixtures.ts +267 -0
- package/src/graph-schema/generate.ts +263 -0
- package/src/graph-schema/graph-schema.test.ts +153 -0
- package/src/graph-schema/graph.schema.json +1387 -0
- package/src/health.schema.json +2 -1
- package/src/identity.ts +2 -0
- package/src/index.ts +2 -0
- package/src/instance-nodes.ts +288 -0
- package/src/lexicon.ts +76 -0
- package/src/lifecycle/__fixtures__/index-diff/20261026T120000Z-aaaaaaaaaaaa.json +149 -0
- package/src/lifecycle/__fixtures__/index-diff/20261026T130000Z-bbbbbbbbbbbb.json +127 -0
- package/src/lifecycle/adoption-file.test.ts +100 -0
- package/src/lifecycle/adoption-file.ts +154 -0
- package/src/lifecycle/cloud-index.test.ts +517 -0
- package/src/lifecycle/cloud-index.ts +736 -0
- package/src/lifecycle/derive-edges.test.ts +193 -0
- package/src/lifecycle/derive-edges.ts +182 -0
- package/src/lifecycle/git.ts +10 -4
- package/src/lifecycle/identity-match.test.ts +308 -0
- package/src/lifecycle/identity-match.ts +257 -0
- package/src/lifecycle/index-diff.test.ts +198 -0
- package/src/lifecycle/index-diff.ts +476 -0
- package/src/lifecycle/index-document.test.ts +64 -0
- package/src/lifecycle/index-document.ts +299 -0
- package/src/lifecycle/index-drift.test.ts +191 -0
- package/src/lifecycle/index-drift.ts +304 -0
- package/src/lifecycle/index-import.test.ts +255 -0
- package/src/lifecycle/index-import.ts +634 -0
- package/src/lifecycle/index-join.test.ts +250 -0
- package/src/lifecycle/index-join.ts +304 -0
- package/src/lifecycle/index-store-load-bearing.test.ts +187 -0
- package/src/lifecycle/index.ts +4 -0
- package/src/lifecycle/member-ledger.test.ts +25 -7
- package/src/lifecycle/observation-baseline.test.ts +72 -1
- package/src/lifecycle/observation-baseline.ts +190 -27
- package/src/lifecycle/observe.ts +6 -1
- package/src/lifecycle/query.test.ts +69 -1
- package/src/lifecycle/query.ts +103 -10
- package/src/lifecycle/replay.test.ts +9 -5
- package/src/lifecycle/replay.ts +9 -5
- package/src/lifecycle/scenario.ts +4 -3
- package/src/lifecycle/snapshot-store-fake.ts +29 -0
- package/src/lifecycle/snapshot-store-s3.ts +316 -0
- package/src/lifecycle/snapshot-store.test.ts +385 -0
- package/src/lifecycle/snapshot-store.ts +981 -0
- package/src/lifecycle/snapshot.test.ts +13 -0
- package/src/lifecycle/snapshot.ts +23 -6
- package/src/lifecycle/type-listing.test.ts +166 -0
- package/src/lifecycle/type-listing.ts +309 -0
- package/src/lint/component-checks.ts +17 -1
- package/src/lint/config.test.ts +3 -0
- package/src/lint/presets/relaxed.json +2 -1
- package/src/lint/presets/strict.json +2 -1
- package/src/lint/rules/__fixtures__/comp/comp008/fail/dns.component.ts +15 -0
- package/src/lint/rules/__fixtures__/comp/comp008/pass/dns.component.ts +10 -0
- package/src/lint/rules/comp/comp.test.ts +2 -1
- package/src/lint/rules/comp/comp008-applies-signal-only-root.ts +121 -0
- package/src/lint/rules/comp/comp008.test.ts +77 -0
- package/src/lint/rules/comp/index.ts +4 -1
- package/src/observation-edges.test.ts +33 -0
- package/src/observation.ts +36 -1
- package/src/op/activities/activity-contracts.ts +1 -1
- package/src/op/activities/change-set.ts +8 -5
- package/src/op/activity-failure.ts +36 -0
- package/src/op/index.ts +2 -1
- package/src/op/local-executor.test.ts +80 -0
- package/src/op/local-executor.ts +27 -9
- package/src/pr-loop-bundle.test.ts +5 -2
- package/src/pr-loop.test.ts +43 -0
- package/src/pr-loop.ts +111 -2
- package/src/pulumi-urn.ts +151 -0
- package/src/release-floors.ts +22 -0
- package/src/sensitive-key.ts +34 -0
- package/src/workspace/adoption-report.test.ts +61 -0
- package/src/workspace/adoption-report.ts +103 -0
- package/src/workspace/capabilities-contract.test.ts +4 -0
- package/src/workspace/capabilities-contract.ts +9 -1
- package/src/workspace/capabilities.schema.json +98 -0
- package/src/workspace/change-set.schema.json +42 -3
- package/src/workspace/ci-cloud-identity.ts +31 -1
- package/src/workspace/compose-graph.test.ts +12 -0
- package/src/workspace/compose-graph.ts +12 -1
- package/src/workspace/conformance/conformance.e2e.test.ts +2 -1
- package/src/workspace/conformance/index.ts +6 -1
- package/src/workspace/drift-cli.test.ts +65 -0
- package/src/workspace/drift-cli.ts +365 -0
- package/src/workspace/drift.schema.json +672 -0
- package/src/workspace/graph-cli.ts +5 -0
- package/src/workspace/graph.schema.json +72 -0
- package/src/workspace/init.test.ts +38 -1
- package/src/workspace/init.ts +50 -8
- package/src/workspace/intent-gaps.test.ts +18 -2
- package/src/workspace/ledger.schema.json +1 -1
- package/src/workspace/nested-graph.ts +1 -0
- package/src/workspace/pr-report.schema.json +51 -2
- package/src/workspace/promote-waves.test.ts +296 -0
- package/src/workspace/promote-waves.ts +340 -0
- package/src/workspace/promote.test.ts +158 -4
- package/src/workspace/promote.ts +398 -57
- package/src/workspace/reason-codes.test.ts +4 -1
- package/src/workspace/reason-codes.ts +14 -0
- package/src/workspace/search-contract.ts +2 -0
- package/src/workspace/search.schema.json +103 -5
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* behold#1029: Kubernetes, through API discovery and then one LIST per
|
|
3
|
+
* served kind, paged with `continue`.
|
|
4
|
+
*
|
|
5
|
+
* The lister does not go through `@intentius/chant-k8s-client`. That client
|
|
6
|
+
* builds a full read and write client, and its only network seam
|
|
7
|
+
* (`requestLayer`) replaces the whole HTTP send, so the read-only guard could
|
|
8
|
+
* not sit in front of its real transport. Discovery and LIST are plain GETs,
|
|
9
|
+
* so they go through the lister's own guarded transport like every other
|
|
10
|
+
* substrate, with the cluster's server, token and certificates passed
|
|
11
|
+
* explicitly. {@link kubeTargetFromKubeconfig} reads those from a kubeconfig
|
|
12
|
+
* the caller names, and refuses an exec plugin or an auth provider: those
|
|
13
|
+
* are credential chains, and the lister takes static credentials only.
|
|
14
|
+
*
|
|
15
|
+
* Locations are namespaces. A cluster-scoped object's location is `cluster`;
|
|
16
|
+
* it is listed when the scope's locations are `*` or include `cluster`. With
|
|
17
|
+
* types `*`, events and leases are left out (they change on every read, so
|
|
18
|
+
* every version's digest would differ) and a note says so.
|
|
19
|
+
*
|
|
20
|
+
* kind (the pinned `kindest/node` in choudoufu's live/kind-node-image) runs a
|
|
21
|
+
* real API server, so discovery and LIST are served as on any cluster.
|
|
22
|
+
*/
|
|
23
|
+
import { readFileSync } from "node:fs";
|
|
24
|
+
import { parseYAML } from "../yaml.js";
|
|
25
|
+
import { buildResource, describeFailure, listedVerdict, noteTagFilter, withThrottleRetry } from "./common.js";
|
|
26
|
+
import { ListerConfigError } from "./credentials.js";
|
|
27
|
+
import { maskKubernetesObject } from "./mask.js";
|
|
28
|
+
import { jsonBody } from "./transport.js";
|
|
29
|
+
const PAGE_SIZE = 500;
|
|
30
|
+
const MAX_PAGES = 10000;
|
|
31
|
+
const LABEL_PART = /^[A-Za-z0-9_.\-\/]+$/;
|
|
32
|
+
const CLUSTER_LOCATION = "cluster";
|
|
33
|
+
/** Left out of `types: "*"`: they change on every read. */
|
|
34
|
+
const CHURNING_TYPES = new Set(["v1/Event", "events.k8s.io/v1/Event", "coordination.k8s.io/v1/Lease"]);
|
|
35
|
+
export function listedTypeOf(group, version, kind) {
|
|
36
|
+
return group === "" ? `${version}/${kind}` : `${group}/${version}/${kind}`;
|
|
37
|
+
}
|
|
38
|
+
function apiUrl(target, path, query) {
|
|
39
|
+
const base = target.server.replace(/\/+$/, "");
|
|
40
|
+
const url = new URL(base + path);
|
|
41
|
+
for (const [k, v] of Object.entries(query ?? {}))
|
|
42
|
+
url.searchParams.set(k, v);
|
|
43
|
+
return url.toString();
|
|
44
|
+
}
|
|
45
|
+
function headersFor(target) {
|
|
46
|
+
return { accept: "application/json", ...(target.token ? { authorization: `Bearer ${target.token}` } : {}) };
|
|
47
|
+
}
|
|
48
|
+
async function get(ctx, target, path, query, retryDelays) {
|
|
49
|
+
const url = apiUrl(target, path, query);
|
|
50
|
+
return withThrottleRetry(() => ctx.transport.send({ substrate: "kubernetes", method: "GET", url, headers: headersFor(target) }), (r) => r.status === 429, retryDelays);
|
|
51
|
+
}
|
|
52
|
+
function k8sMessage(res) {
|
|
53
|
+
const body = jsonBody(res);
|
|
54
|
+
return describeFailure(res, body?.reason, body?.message);
|
|
55
|
+
}
|
|
56
|
+
function resourcesOf(body, group, version) {
|
|
57
|
+
const list = body?.resources ?? [];
|
|
58
|
+
const out = [];
|
|
59
|
+
for (const r of list) {
|
|
60
|
+
if (!r.name || !r.kind || r.name.includes("/"))
|
|
61
|
+
continue;
|
|
62
|
+
if (!(r.verbs ?? []).includes("list"))
|
|
63
|
+
continue;
|
|
64
|
+
out.push({ group, version, kind: r.kind, plural: r.name, namespaced: r.namespaced === true, listedType: listedTypeOf(group, version, r.kind) });
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Every listable kind the cluster serves: the core group's `v1`, and for each
|
|
70
|
+
* other group its preferred version plus any version a requested type names.
|
|
71
|
+
*/
|
|
72
|
+
export async function discover(ctx, target, requested, retryDelays) {
|
|
73
|
+
const kinds = [];
|
|
74
|
+
const core = await get(ctx, target, "/api/v1", undefined, retryDelays);
|
|
75
|
+
if (core.status >= 400) {
|
|
76
|
+
ctx.notes.push({ code: core.status === 401 || core.status === 403 ? "no-credentials" : "read-failed", message: `discovery of the core group failed: ${k8sMessage(core)}` });
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
kinds.push(...resourcesOf(jsonBody(core), "", "v1"));
|
|
80
|
+
}
|
|
81
|
+
const groups = await get(ctx, target, "/apis", undefined, retryDelays);
|
|
82
|
+
if (groups.status >= 400) {
|
|
83
|
+
ctx.notes.push({ code: groups.status === 401 || groups.status === 403 ? "no-credentials" : "read-failed", message: `discovery of API groups failed: ${k8sMessage(groups)}` });
|
|
84
|
+
return kinds;
|
|
85
|
+
}
|
|
86
|
+
const list = jsonBody(groups)?.groups ?? [];
|
|
87
|
+
for (const g of list) {
|
|
88
|
+
if (!g.name)
|
|
89
|
+
continue;
|
|
90
|
+
const versions = new Set();
|
|
91
|
+
if (g.preferredVersion?.version)
|
|
92
|
+
versions.add(g.preferredVersion.version);
|
|
93
|
+
if (requested) {
|
|
94
|
+
for (const v of g.versions ?? []) {
|
|
95
|
+
if (v.version && [...requested].some((t) => t.startsWith(`${g.name}/${v.version}/`)))
|
|
96
|
+
versions.add(v.version);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
for (const version of versions) {
|
|
100
|
+
const res = await get(ctx, target, `/apis/${g.name}/${version}`, undefined, retryDelays);
|
|
101
|
+
if (res.status >= 400) {
|
|
102
|
+
ctx.notes.push({ code: "read-failed", message: `discovery of ${g.name}/${version} failed: ${k8sMessage(res)}` });
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
kinds.push(...resourcesOf(jsonBody(res), g.name, version));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return kinds;
|
|
109
|
+
}
|
|
110
|
+
/** `labelSelector` for a tag filter, or undefined. Exported for tests. */
|
|
111
|
+
export function labelSelector(filter) {
|
|
112
|
+
const entries = Object.entries(filter ?? {});
|
|
113
|
+
if (entries.length === 0)
|
|
114
|
+
return undefined;
|
|
115
|
+
return entries
|
|
116
|
+
.map(([k, v]) => {
|
|
117
|
+
if (!LABEL_PART.test(k) || !LABEL_PART.test(v))
|
|
118
|
+
throw new ListerConfigError(`kubernetes label filter ${k}=${v} is not a plain label key and value`);
|
|
119
|
+
return `${k}=${v}`;
|
|
120
|
+
})
|
|
121
|
+
.join(",");
|
|
122
|
+
}
|
|
123
|
+
export async function listKubernetes(ctx, target, retryDelays) {
|
|
124
|
+
const { scope } = ctx;
|
|
125
|
+
const requested = scope.types === "*" ? undefined : new Set(scope.types);
|
|
126
|
+
const selector = labelSelector(scope.tagFilter);
|
|
127
|
+
noteTagFilter(ctx, "as a labelSelector on every LIST");
|
|
128
|
+
const served = await discover(ctx, target, requested, retryDelays);
|
|
129
|
+
let toList;
|
|
130
|
+
if (requested) {
|
|
131
|
+
const byType = new Map(served.map((k) => [k.listedType, k]));
|
|
132
|
+
toList = [];
|
|
133
|
+
for (const t of requested) {
|
|
134
|
+
const k = byType.get(t);
|
|
135
|
+
if (k) {
|
|
136
|
+
toList.push(k);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
ctx.coverage.push({ listedType: t, location: "*", verdict: "unsupported-kind", detail: "the cluster's discovery serves no listable resource of this type" });
|
|
140
|
+
ctx.notes.push({ code: "no-lister", listedType: t, message: `${t} is not a listable resource on this cluster (not served by discovery, or no list verb)` });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
toList = served.filter((k) => !CHURNING_TYPES.has(k.listedType));
|
|
146
|
+
const skipped = served.filter((k) => CHURNING_TYPES.has(k.listedType)).map((k) => k.listedType);
|
|
147
|
+
if (skipped.length > 0) {
|
|
148
|
+
ctx.notes.push({ code: "default-types", message: `types "*" left out ${skipped.join(", ")}, which change on every read; name them to list them` });
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const namespaces = scope.locations === "*" ? undefined : new Set(scope.locations);
|
|
152
|
+
const wantCluster = scope.locations === "*" || scope.locations.includes(CLUSTER_LOCATION);
|
|
153
|
+
const nsCoverage = scope.locations === "*" ? ["*"] : scope.locations.filter((l) => l !== CLUSTER_LOCATION);
|
|
154
|
+
for (const k of toList) {
|
|
155
|
+
if (!k.namespaced && !wantCluster)
|
|
156
|
+
continue;
|
|
157
|
+
if (k.namespaced && nsCoverage.length === 0)
|
|
158
|
+
continue;
|
|
159
|
+
const coverageLocations = k.namespaced ? nsCoverage : [CLUSTER_LOCATION];
|
|
160
|
+
const path = k.group === "" ? `/api/v1/${k.plural}` : `/apis/${k.group}/${k.version}/${k.plural}`;
|
|
161
|
+
const found = [];
|
|
162
|
+
let cont;
|
|
163
|
+
let failed;
|
|
164
|
+
for (let page = 0; page < MAX_PAGES; page++) {
|
|
165
|
+
const query = { limit: String(PAGE_SIZE) };
|
|
166
|
+
if (cont)
|
|
167
|
+
query.continue = cont;
|
|
168
|
+
if (selector)
|
|
169
|
+
query.labelSelector = selector;
|
|
170
|
+
const res = await get(ctx, target, path, query, retryDelays);
|
|
171
|
+
const observedAt = ctx.now().toISOString();
|
|
172
|
+
if (res.status >= 400) {
|
|
173
|
+
failed = res;
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
const body = jsonBody(res);
|
|
177
|
+
for (const item of body?.items ?? []) {
|
|
178
|
+
const meta = (item.metadata ?? {});
|
|
179
|
+
if (!meta.name)
|
|
180
|
+
continue;
|
|
181
|
+
const ns = meta.namespace;
|
|
182
|
+
if (k.namespaced && namespaces && (!ns || !namespaces.has(ns)))
|
|
183
|
+
continue;
|
|
184
|
+
const location = k.namespaced ? ns ?? "" : CLUSTER_LOCATION;
|
|
185
|
+
const obj = { ...item, apiVersion: k.group === "" ? k.version : `${k.group}/${k.version}`, kind: k.kind };
|
|
186
|
+
const { managedFields: _mf, ...restMeta } = meta;
|
|
187
|
+
obj.metadata = restMeta;
|
|
188
|
+
const tags = {};
|
|
189
|
+
for (const [lk, lv] of Object.entries(meta.labels ?? {}))
|
|
190
|
+
tags[lk] = String(lv);
|
|
191
|
+
found.push({
|
|
192
|
+
listedType: k.listedType,
|
|
193
|
+
location,
|
|
194
|
+
nativeId: k.namespaced ? `${ns}/${meta.name}` : meta.name,
|
|
195
|
+
cloudIds: meta.uid ? [{ kind: "k8s-uid", value: meta.uid }] : [],
|
|
196
|
+
tags,
|
|
197
|
+
properties: maskKubernetesObject(obj),
|
|
198
|
+
observedAt,
|
|
199
|
+
labels: true,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
cont = body?.metadata?.continue || undefined;
|
|
203
|
+
if (!cont)
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
if (failed) {
|
|
207
|
+
const detail = k8sMessage(failed);
|
|
208
|
+
const verdict = failed.status === 401 || failed.status === 403 ? "no-credentials" : failed.status === 404 ? "unsupported-kind" : "read-failed";
|
|
209
|
+
for (const l of coverageLocations)
|
|
210
|
+
ctx.coverage.push({ listedType: k.listedType, location: l, verdict, detail });
|
|
211
|
+
ctx.notes.push({
|
|
212
|
+
code: verdict === "no-credentials" ? "no-credentials" : verdict === "unsupported-kind" ? "no-lister" : "read-failed",
|
|
213
|
+
listedType: k.listedType,
|
|
214
|
+
message: `LIST ${k.listedType} failed: ${detail}`,
|
|
215
|
+
});
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
for (const r of found)
|
|
219
|
+
ctx.resources.push(buildResource(ctx, r));
|
|
220
|
+
for (const l of coverageLocations)
|
|
221
|
+
ctx.coverage.push({ listedType: k.listedType, location: l, ...listedVerdict(scope) });
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* A {@link KubernetesTarget} from a kubeconfig the caller names (its text,
|
|
226
|
+
* and the directory relative file paths resolve against). Static credentials
|
|
227
|
+
* only: a token, a token file, or a client certificate and key. An exec
|
|
228
|
+
* plugin or an auth provider is refused.
|
|
229
|
+
*/
|
|
230
|
+
export function kubeTargetFromKubeconfig(text, opts = {}) {
|
|
231
|
+
const doc = parseYAML(text);
|
|
232
|
+
const named = (list, name, field) => {
|
|
233
|
+
for (const e of Array.isArray(list) ? list : []) {
|
|
234
|
+
const o = e;
|
|
235
|
+
if (o?.name === name)
|
|
236
|
+
return o[field];
|
|
237
|
+
}
|
|
238
|
+
return undefined;
|
|
239
|
+
};
|
|
240
|
+
const contextName = opts.context ?? (typeof doc["current-context"] === "string" ? doc["current-context"] : undefined);
|
|
241
|
+
if (!contextName)
|
|
242
|
+
throw new ListerConfigError("the kubeconfig names no context and none was given");
|
|
243
|
+
const context = named(doc.contexts, contextName, "context");
|
|
244
|
+
if (!context)
|
|
245
|
+
throw new ListerConfigError(`the kubeconfig has no context ${contextName}`);
|
|
246
|
+
const clusterName = String(context.cluster ?? "");
|
|
247
|
+
const userName = String(context.user ?? "");
|
|
248
|
+
const cluster = named(doc.clusters, clusterName, "cluster");
|
|
249
|
+
if (!cluster || typeof cluster.server !== "string")
|
|
250
|
+
throw new ListerConfigError(`the kubeconfig's cluster ${clusterName} has no server`);
|
|
251
|
+
const user = named(doc.users, userName, "user") ?? {};
|
|
252
|
+
if (user.exec || user["auth-provider"]) {
|
|
253
|
+
throw new ListerConfigError(`the kubeconfig user ${userName} authenticates through an exec plugin or auth provider; the lister takes static credentials only (a token, or a client certificate and key)`);
|
|
254
|
+
}
|
|
255
|
+
const file = (p) => {
|
|
256
|
+
if (typeof p !== "string" || !p)
|
|
257
|
+
return undefined;
|
|
258
|
+
const path = p.startsWith("/") || !opts.baseDir ? p : `${opts.baseDir}/${p}`;
|
|
259
|
+
return readFileSync(path, "utf8");
|
|
260
|
+
};
|
|
261
|
+
const b64 = (d) => (typeof d === "string" && d ? Buffer.from(d, "base64").toString("utf8") : undefined);
|
|
262
|
+
const tls = {};
|
|
263
|
+
const ca = b64(cluster["certificate-authority-data"]) ?? file(cluster["certificate-authority"]);
|
|
264
|
+
if (ca)
|
|
265
|
+
tls.ca = ca;
|
|
266
|
+
const cert = b64(user["client-certificate-data"]) ?? file(user["client-certificate"]);
|
|
267
|
+
if (cert)
|
|
268
|
+
tls.cert = cert;
|
|
269
|
+
const key = b64(user["client-key-data"]) ?? file(user["client-key"]);
|
|
270
|
+
if (key)
|
|
271
|
+
tls.key = key;
|
|
272
|
+
if (cluster["insecure-skip-tls-verify"] === true)
|
|
273
|
+
tls.insecureSkipVerify = true;
|
|
274
|
+
const token = typeof user.token === "string" && user.token ? user.token : file(user.tokenFile)?.trim();
|
|
275
|
+
return { server: cluster.server, ...(token ? { token } : {}), tls, contextName, clusterName };
|
|
276
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* behold#1029: ownership markers read off a listed object's tags or labels,
|
|
3
|
+
* in chant#4520's `EngineFacts.markers` shape with `source: "live"`.
|
|
4
|
+
*
|
|
5
|
+
* The key sets the lister knows (#1025 "What a cloud source can and cannot
|
|
6
|
+
* say about engine facts"):
|
|
7
|
+
* - choudoufu: `tofu-estate` and `tofu-address`, the address escaped
|
|
8
|
+
* (choudoufu internal/live/markers/markers.go:40,43);
|
|
9
|
+
* - plunge: `plunge-stack` and `plunge-urn` (continued in `plunge-urn-2`
|
|
10
|
+
* to `-4`), plain on tags and digests on labels (chant#4520's
|
|
11
|
+
* `markerScheme` for plunge);
|
|
12
|
+
* - chant: the lexicons' `ChannelKeys` with managed-by `chant`
|
|
13
|
+
* (packages/core/src/ownership.ts; aws `chant:*`, azure `chant-*`, gcp
|
|
14
|
+
* `managed-by` and `chant-stack`, the shared label convention for
|
|
15
|
+
* Kubernetes).
|
|
16
|
+
*
|
|
17
|
+
* `markers` is one object, so an object carrying several engines' markers
|
|
18
|
+
* reports the first found in that order. Every tag stays in `tags`, so
|
|
19
|
+
* nothing is lost. A marker key is matched case-insensitively, because Azure
|
|
20
|
+
* tag names are case-insensitive. The lister never derives ownership from a
|
|
21
|
+
* marker: that is relative to an estate or stack it does not read.
|
|
22
|
+
*/
|
|
23
|
+
import type { MarkerFact } from "./types.js";
|
|
24
|
+
/**
|
|
25
|
+
* The marker fact for one object. `labels` says the values came from GCP or
|
|
26
|
+
* Kubernetes labels, where plunge writes digests rather than plain values.
|
|
27
|
+
*/
|
|
28
|
+
export declare function readMarkers(tags: Record<string, string>, opts: {
|
|
29
|
+
labels: boolean;
|
|
30
|
+
}): MarkerFact | undefined;
|
|
31
|
+
//# sourceMappingURL=markers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markers.d.ts","sourceRoot":"","sources":["../../src/cloud-lister/markers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAgB1C;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GAAG,UAAU,GAAG,SAAS,CA8B3G"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* behold#1029: ownership markers read off a listed object's tags or labels,
|
|
3
|
+
* in chant#4520's `EngineFacts.markers` shape with `source: "live"`.
|
|
4
|
+
*
|
|
5
|
+
* The key sets the lister knows (#1025 "What a cloud source can and cannot
|
|
6
|
+
* say about engine facts"):
|
|
7
|
+
* - choudoufu: `tofu-estate` and `tofu-address`, the address escaped
|
|
8
|
+
* (choudoufu internal/live/markers/markers.go:40,43);
|
|
9
|
+
* - plunge: `plunge-stack` and `plunge-urn` (continued in `plunge-urn-2`
|
|
10
|
+
* to `-4`), plain on tags and digests on labels (chant#4520's
|
|
11
|
+
* `markerScheme` for plunge);
|
|
12
|
+
* - chant: the lexicons' `ChannelKeys` with managed-by `chant`
|
|
13
|
+
* (packages/core/src/ownership.ts; aws `chant:*`, azure `chant-*`, gcp
|
|
14
|
+
* `managed-by` and `chant-stack`, the shared label convention for
|
|
15
|
+
* Kubernetes).
|
|
16
|
+
*
|
|
17
|
+
* `markers` is one object, so an object carrying several engines' markers
|
|
18
|
+
* reports the first found in that order. Every tag stays in `tags`, so
|
|
19
|
+
* nothing is lost. A marker key is matched case-insensitively, because Azure
|
|
20
|
+
* tag names are case-insensitive. The lister never derives ownership from a
|
|
21
|
+
* marker: that is relative to an estate or stack it does not read.
|
|
22
|
+
*/
|
|
23
|
+
function lookup(tags, key) {
|
|
24
|
+
if (key in tags)
|
|
25
|
+
return tags[key];
|
|
26
|
+
const lower = key.toLowerCase();
|
|
27
|
+
for (const [k, v] of Object.entries(tags))
|
|
28
|
+
if (k.toLowerCase() === lower)
|
|
29
|
+
return v;
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
const CHANT_KEY_SETS = [
|
|
33
|
+
{ managedBy: "chant:managed-by", stack: "chant:stack" },
|
|
34
|
+
{ managedBy: "chant-managed-by", stack: "chant-stack" },
|
|
35
|
+
{ managedBy: "managed-by", stack: "chant-stack" },
|
|
36
|
+
{ managedBy: "app.kubernetes.io/managed-by", stack: "chant.intentius.io/stack" },
|
|
37
|
+
];
|
|
38
|
+
/**
|
|
39
|
+
* The marker fact for one object. `labels` says the values came from GCP or
|
|
40
|
+
* Kubernetes labels, where plunge writes digests rather than plain values.
|
|
41
|
+
*/
|
|
42
|
+
export function readMarkers(tags, opts) {
|
|
43
|
+
const estate = lookup(tags, "tofu-estate");
|
|
44
|
+
if (estate !== undefined) {
|
|
45
|
+
const address = lookup(tags, "tofu-address");
|
|
46
|
+
return { owner: estate, ...(address !== undefined ? { address } : {}), encoding: "escaped", source: "live" };
|
|
47
|
+
}
|
|
48
|
+
const stack = lookup(tags, "plunge-stack");
|
|
49
|
+
if (stack !== undefined) {
|
|
50
|
+
let urn = lookup(tags, "plunge-urn");
|
|
51
|
+
if (urn !== undefined && !opts.labels) {
|
|
52
|
+
for (let i = 2; i <= 4; i++) {
|
|
53
|
+
const more = lookup(tags, `plunge-urn-${i}`);
|
|
54
|
+
if (more === undefined)
|
|
55
|
+
break;
|
|
56
|
+
urn += more;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
owner: stack,
|
|
61
|
+
...(urn !== undefined ? { address: urn } : {}),
|
|
62
|
+
encoding: opts.labels ? "digest" : "plain",
|
|
63
|
+
source: "live",
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
for (const keys of CHANT_KEY_SETS) {
|
|
67
|
+
if (lookup(tags, keys.managedBy) === "chant") {
|
|
68
|
+
const owner = lookup(tags, keys.stack);
|
|
69
|
+
return { ...(owner !== undefined ? { owner } : {}), encoding: "plain", source: "live" };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* behold#1029: secret-bearing values are masked in the lister, before a
|
|
3
|
+
* `ListingResult` exists, so no later stage (chant#4521's typing, the store,
|
|
4
|
+
* LadybugDB) ever holds one (#1025 "Source interface").
|
|
5
|
+
*
|
|
6
|
+
* The key list is chant's `SENSITIVE_KEY_PATTERNS` (../sensitive-key.ts), the
|
|
7
|
+
* one list chant's thin and deep observation paths share. behold carried a
|
|
8
|
+
* copy while the lister lived there; in chant it is the same list.
|
|
9
|
+
*
|
|
10
|
+
* Beyond key names, two things are masked whole because their values are
|
|
11
|
+
* secrets whatever their keys are called:
|
|
12
|
+
* - a Kubernetes Secret's `data` and `stringData`, and its
|
|
13
|
+
* `kubectl.kubernetes.io/last-applied-configuration` annotation, which
|
|
14
|
+
* repeats them;
|
|
15
|
+
* - any tag or label whose key matches the patterns.
|
|
16
|
+
*/
|
|
17
|
+
import { isSensitiveKey, SENSITIVE_KEY_PATTERNS } from "../sensitive-key.js";
|
|
18
|
+
export { isSensitiveKey, SENSITIVE_KEY_PATTERNS };
|
|
19
|
+
/** What a masked value becomes. A fixed string, so a masked value never diffs. */
|
|
20
|
+
export declare const MASKED = "<masked>";
|
|
21
|
+
/**
|
|
22
|
+
* A deep copy of `value` with every value under a sensitive key replaced by
|
|
23
|
+
* {@link MASKED}. Arrays of `{Key, Value}` / `{key, value}` pairs (AWS tags,
|
|
24
|
+
* some ARM shapes) are masked by the pair's key as well.
|
|
25
|
+
*/
|
|
26
|
+
export declare function maskValue(value: unknown): unknown;
|
|
27
|
+
export declare function maskProperties(props: Record<string, unknown>): Record<string, unknown>;
|
|
28
|
+
export declare function maskTags(tags: Record<string, string>): Record<string, string>;
|
|
29
|
+
/** A Kubernetes object, masked: key names everywhere, and a Secret's payload whole. */
|
|
30
|
+
export declare function maskKubernetesObject(obj: Record<string, unknown>): Record<string, unknown>;
|
|
31
|
+
//# sourceMappingURL=mask.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mask.d.ts","sourceRoot":"","sources":["../../src/cloud-lister/mask.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAE1E,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,CAAC;AAElD,kFAAkF;AAClF,eAAO,MAAM,MAAM,aAAa,CAAC;AAEjC;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAgBjD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEtF;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAI7E;AAID,uFAAuF;AACvF,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAgB1F"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* behold#1029: secret-bearing values are masked in the lister, before a
|
|
3
|
+
* `ListingResult` exists, so no later stage (chant#4521's typing, the store,
|
|
4
|
+
* LadybugDB) ever holds one (#1025 "Source interface").
|
|
5
|
+
*
|
|
6
|
+
* The key list is chant's `SENSITIVE_KEY_PATTERNS` (../sensitive-key.ts), the
|
|
7
|
+
* one list chant's thin and deep observation paths share. behold carried a
|
|
8
|
+
* copy while the lister lived there; in chant it is the same list.
|
|
9
|
+
*
|
|
10
|
+
* Beyond key names, two things are masked whole because their values are
|
|
11
|
+
* secrets whatever their keys are called:
|
|
12
|
+
* - a Kubernetes Secret's `data` and `stringData`, and its
|
|
13
|
+
* `kubectl.kubernetes.io/last-applied-configuration` annotation, which
|
|
14
|
+
* repeats them;
|
|
15
|
+
* - any tag or label whose key matches the patterns.
|
|
16
|
+
*/
|
|
17
|
+
import { isSensitiveKey, SENSITIVE_KEY_PATTERNS } from "../sensitive-key.js";
|
|
18
|
+
export { isSensitiveKey, SENSITIVE_KEY_PATTERNS };
|
|
19
|
+
/** What a masked value becomes. A fixed string, so a masked value never diffs. */
|
|
20
|
+
export const MASKED = "<masked>";
|
|
21
|
+
/**
|
|
22
|
+
* A deep copy of `value` with every value under a sensitive key replaced by
|
|
23
|
+
* {@link MASKED}. Arrays of `{Key, Value}` / `{key, value}` pairs (AWS tags,
|
|
24
|
+
* some ARM shapes) are masked by the pair's key as well.
|
|
25
|
+
*/
|
|
26
|
+
export function maskValue(value) {
|
|
27
|
+
if (Array.isArray(value))
|
|
28
|
+
return value.map((v) => maskValue(v));
|
|
29
|
+
if (value === null || typeof value !== "object")
|
|
30
|
+
return value;
|
|
31
|
+
const obj = value;
|
|
32
|
+
const pairKey = typeof obj.Key === "string" ? obj.Key : typeof obj.key === "string" ? obj.key : undefined;
|
|
33
|
+
const out = {};
|
|
34
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
35
|
+
if (isSensitiveKey(k)) {
|
|
36
|
+
out[k] = MASKED;
|
|
37
|
+
}
|
|
38
|
+
else if (pairKey !== undefined && isSensitiveKey(pairKey) && (k === "Value" || k === "value")) {
|
|
39
|
+
out[k] = MASKED;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
out[k] = maskValue(v);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
export function maskProperties(props) {
|
|
48
|
+
return maskValue(props);
|
|
49
|
+
}
|
|
50
|
+
export function maskTags(tags) {
|
|
51
|
+
const out = {};
|
|
52
|
+
for (const [k, v] of Object.entries(tags))
|
|
53
|
+
out[k] = isSensitiveKey(k) ? MASKED : v;
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
const LAST_APPLIED = "kubectl.kubernetes.io/last-applied-configuration";
|
|
57
|
+
/** A Kubernetes object, masked: key names everywhere, and a Secret's payload whole. */
|
|
58
|
+
export function maskKubernetesObject(obj) {
|
|
59
|
+
const masked = maskProperties(obj);
|
|
60
|
+
if (obj.kind === "Secret") {
|
|
61
|
+
for (const field of ["data", "stringData"]) {
|
|
62
|
+
const payload = masked[field];
|
|
63
|
+
if (payload && typeof payload === "object") {
|
|
64
|
+
masked[field] = Object.fromEntries(Object.keys(payload).map((k) => [k, MASKED]));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const metadata = masked.metadata;
|
|
69
|
+
const annotations = metadata?.annotations;
|
|
70
|
+
if (annotations && LAST_APPLIED in annotations && obj.kind === "Secret") {
|
|
71
|
+
annotations[LAST_APPLIED] = MASKED;
|
|
72
|
+
}
|
|
73
|
+
return masked;
|
|
74
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* behold#1029: AWS Signature Version 4 for the lister's one AWS call.
|
|
3
|
+
*
|
|
4
|
+
* Signed by hand with node:crypto rather than through the AWS SDK, so the
|
|
5
|
+
* lister builds no SDK client (no client to hold a mutating command, no
|
|
6
|
+
* default provider chain) and adds no dependency. Only what Cloud Control's
|
|
7
|
+
* JSON protocol needs: a POST to `/` with no query string.
|
|
8
|
+
*/
|
|
9
|
+
import type { AwsStaticCredentials } from "./credentials.js";
|
|
10
|
+
/** `20261026T120000Z` and `20261026` for a date. */
|
|
11
|
+
export declare function amzDates(now: Date): {
|
|
12
|
+
amzDate: string;
|
|
13
|
+
dateStamp: string;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* The headers to send, `Authorization` included. `headers` must not already
|
|
17
|
+
* hold `host` or `x-amz-date`; they are added here.
|
|
18
|
+
*/
|
|
19
|
+
export declare function signV4(input: {
|
|
20
|
+
method: string;
|
|
21
|
+
url: string;
|
|
22
|
+
headers: Record<string, string>;
|
|
23
|
+
body: string;
|
|
24
|
+
region: string;
|
|
25
|
+
service: string;
|
|
26
|
+
credentials: AwsStaticCredentials;
|
|
27
|
+
now: Date;
|
|
28
|
+
}): Record<string, string>;
|
|
29
|
+
//# sourceMappingURL=sigv4.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sigv4.d.ts","sourceRoot":"","sources":["../../src/cloud-lister/sigv4.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAU1D,oDAAoD;AACpD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,IAAI,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAG1E;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,oBAAoB,CAAC;IAClC,GAAG,EAAE,IAAI,CAAC;CACX,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA8BzB"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* behold#1029: AWS Signature Version 4 for the lister's one AWS call.
|
|
3
|
+
*
|
|
4
|
+
* Signed by hand with node:crypto rather than through the AWS SDK, so the
|
|
5
|
+
* lister builds no SDK client (no client to hold a mutating command, no
|
|
6
|
+
* default provider chain) and adds no dependency. Only what Cloud Control's
|
|
7
|
+
* JSON protocol needs: a POST to `/` with no query string.
|
|
8
|
+
*/
|
|
9
|
+
import { createHash, createHmac } from "node:crypto";
|
|
10
|
+
function sha256Hex(data) {
|
|
11
|
+
return createHash("sha256").update(data, "utf8").digest("hex");
|
|
12
|
+
}
|
|
13
|
+
function hmac(key, data) {
|
|
14
|
+
return createHmac("sha256", key).update(data, "utf8").digest();
|
|
15
|
+
}
|
|
16
|
+
/** `20261026T120000Z` and `20261026` for a date. */
|
|
17
|
+
export function amzDates(now) {
|
|
18
|
+
const iso = now.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}/, "");
|
|
19
|
+
return { amzDate: iso, dateStamp: iso.slice(0, 8) };
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The headers to send, `Authorization` included. `headers` must not already
|
|
23
|
+
* hold `host` or `x-amz-date`; they are added here.
|
|
24
|
+
*/
|
|
25
|
+
export function signV4(input) {
|
|
26
|
+
const url = new URL(input.url);
|
|
27
|
+
const { amzDate, dateStamp } = amzDates(input.now);
|
|
28
|
+
const headers = {};
|
|
29
|
+
for (const [k, v] of Object.entries(input.headers))
|
|
30
|
+
headers[k.toLowerCase()] = v.trim();
|
|
31
|
+
headers.host = url.host;
|
|
32
|
+
headers["x-amz-date"] = amzDate;
|
|
33
|
+
if (input.credentials.sessionToken)
|
|
34
|
+
headers["x-amz-security-token"] = input.credentials.sessionToken;
|
|
35
|
+
const payloadHash = sha256Hex(input.body);
|
|
36
|
+
const names = Object.keys(headers).sort();
|
|
37
|
+
const canonicalHeaders = names.map((n) => `${n}:${headers[n]}\n`).join("");
|
|
38
|
+
const signedHeaders = names.join(";");
|
|
39
|
+
const canonicalQuery = [...url.searchParams.entries()]
|
|
40
|
+
.map(([k, v]) => [encodeURIComponent(k), encodeURIComponent(v)])
|
|
41
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
42
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
43
|
+
.join("&");
|
|
44
|
+
const canonicalRequest = [input.method, url.pathname || "/", canonicalQuery, canonicalHeaders, signedHeaders, payloadHash].join("\n");
|
|
45
|
+
const scope = `${dateStamp}/${input.region}/${input.service}/aws4_request`;
|
|
46
|
+
const stringToSign = ["AWS4-HMAC-SHA256", amzDate, scope, sha256Hex(canonicalRequest)].join("\n");
|
|
47
|
+
const kDate = hmac(`AWS4${input.credentials.secretAccessKey}`, dateStamp);
|
|
48
|
+
const kRegion = hmac(kDate, input.region);
|
|
49
|
+
const kService = hmac(kRegion, input.service);
|
|
50
|
+
const kSigning = hmac(kService, "aws4_request");
|
|
51
|
+
const signature = createHmac("sha256", kSigning).update(stringToSign, "utf8").digest("hex");
|
|
52
|
+
headers.authorization = `AWS4-HMAC-SHA256 Credential=${input.credentials.accessKeyId}/${scope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;
|
|
53
|
+
return headers;
|
|
54
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* behold#1029: the lister's one way onto the network.
|
|
3
|
+
*
|
|
4
|
+
* {@link Transport.send} runs, in order: the read-only check
|
|
5
|
+
* (packages/core/src/cloud-lister/guard.ts), the origin pin, then the fetcher. The pin is
|
|
6
|
+
* the goal's never-public-cloud rule in code: when a run names its own
|
|
7
|
+
* endpoint (an emulator), every call must go to that endpoint's origin, so a
|
|
8
|
+
* URL built wrong can never reach a public cloud host. A test passes its own
|
|
9
|
+
* {@link Fetcher}; production uses {@link nodeFetcher}, which is node's own
|
|
10
|
+
* http/https so a cluster's CA and client certificate can be passed
|
|
11
|
+
* explicitly.
|
|
12
|
+
*/
|
|
13
|
+
import { type WireCall } from "./guard.js";
|
|
14
|
+
export interface WireResponse {
|
|
15
|
+
status: number;
|
|
16
|
+
headers: Record<string, string>;
|
|
17
|
+
body: string;
|
|
18
|
+
}
|
|
19
|
+
/** TLS material for one target, passed explicitly; nothing is read from the environment. */
|
|
20
|
+
export interface TlsOptions {
|
|
21
|
+
ca?: string;
|
|
22
|
+
cert?: string;
|
|
23
|
+
key?: string;
|
|
24
|
+
/** Only for a local emulator whose certificate the caller chose not to pin. */
|
|
25
|
+
insecureSkipVerify?: boolean;
|
|
26
|
+
}
|
|
27
|
+
export type Fetcher = (call: WireCall, tls: TlsOptions | undefined) => Promise<WireResponse>;
|
|
28
|
+
export declare class OriginRefused extends Error {
|
|
29
|
+
readonly url: string;
|
|
30
|
+
readonly allowed: string;
|
|
31
|
+
constructor(url: string, allowed: string);
|
|
32
|
+
}
|
|
33
|
+
export interface TransportOptions {
|
|
34
|
+
fetcher?: Fetcher;
|
|
35
|
+
/** When set, every call must go to this origin (an explicit endpoint). */
|
|
36
|
+
pinnedOrigin?: string;
|
|
37
|
+
tls?: TlsOptions;
|
|
38
|
+
/** Called with each call that passed the guard, before it is sent (tests record with it). */
|
|
39
|
+
onSend?: (call: WireCall, operation: string) => void;
|
|
40
|
+
}
|
|
41
|
+
export declare class Transport {
|
|
42
|
+
private readonly options;
|
|
43
|
+
readonly calls: Array<{
|
|
44
|
+
call: WireCall;
|
|
45
|
+
operation: string;
|
|
46
|
+
}>;
|
|
47
|
+
private readonly fetcher;
|
|
48
|
+
constructor(options?: TransportOptions);
|
|
49
|
+
send(call: WireCall): Promise<WireResponse>;
|
|
50
|
+
}
|
|
51
|
+
/** node's http/https, one request, the whole body read as text. */
|
|
52
|
+
export declare const nodeFetcher: Fetcher;
|
|
53
|
+
/** The body parsed as JSON, or `undefined` when it is not JSON. */
|
|
54
|
+
export declare function jsonBody(res: WireResponse): unknown;
|
|
55
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/cloud-lister/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,4FAA4F;AAC5F,MAAM,WAAW,UAAU;IACzB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,+EAA+E;IAC/E,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,MAAM,OAAO,GAAG,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,UAAU,GAAG,SAAS,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;AAE7F,qBAAa,aAAc,SAAQ,KAAK;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM;IAA1D,YAAqB,GAAG,EAAE,MAAM,EAAW,OAAO,EAAE,MAAM,EAGzD;CACF;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,6FAA6F;IAC7F,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CACtD;AAED,qBAAa,SAAS;IAIR,OAAO,CAAC,QAAQ,CAAC,OAAO;IAHpC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAM;IAClE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAElC,YAA6B,OAAO,GAAE,gBAAqB,EAE1D;IAEK,IAAI,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,YAAY,CAAC,CAShD;CACF;AAED,mEAAmE;AACnE,eAAO,MAAM,WAAW,EAAE,OAqCtB,CAAC;AAEL,mEAAmE;AACnE,wBAAgB,QAAQ,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAMnD"}
|