@intentius/chant-lexicon-terraform 0.124.44 → 0.124.46
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/__fixtures__/capabilities/chant.config.d.ts +26 -0
- package/dist/__fixtures__/capabilities/chant.config.d.ts.map +1 -0
- package/dist/__fixtures__/capabilities/chant.config.js +13 -0
- package/dist/__fixtures__/effective-topology/chant.config.d.ts +13 -0
- package/dist/__fixtures__/effective-topology/chant.config.d.ts.map +1 -0
- package/dist/__fixtures__/effective-topology/chant.config.js +9 -0
- package/dist/__fixtures__/kind-type/chant.config.d.ts +13 -0
- package/dist/__fixtures__/kind-type/chant.config.d.ts.map +1 -0
- package/dist/__fixtures__/kind-type/chant.config.js +9 -0
- package/dist/__fixtures__/search-roots/chant.config.d.ts +13 -0
- package/dist/__fixtures__/search-roots/chant.config.d.ts.map +1 -0
- package/dist/__fixtures__/search-roots/chant.config.js +9 -0
- package/dist/__fixtures__/sensitive-default/chant.config.d.ts +13 -0
- package/dist/__fixtures__/sensitive-default/chant.config.d.ts.map +1 -0
- package/dist/__fixtures__/sensitive-default/chant.config.js +9 -0
- package/dist/capabilities/adapter.d.ts +9 -0
- package/dist/capabilities/adapter.d.ts.map +1 -0
- package/dist/capabilities/adapter.js +25 -0
- package/dist/capabilities/mapping.json +166 -0
- package/dist/config.d.ts +52 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +23 -0
- package/dist/describe-resources.d.ts +101 -8
- package/dist/describe-resources.d.ts.map +1 -1
- package/dist/describe-resources.js +395 -56
- package/dist/hcl/parse.d.ts +10 -0
- package/dist/hcl/parse.d.ts.map +1 -1
- package/dist/hcl/parse.js +20 -2
- package/dist/hcl/redact.d.ts +26 -0
- package/dist/hcl/redact.d.ts.map +1 -0
- package/dist/hcl/redact.js +228 -0
- package/dist/integrity.json +2 -2
- package/dist/manifest.json +1 -1
- package/dist/op/activities/terraform.d.ts +109 -4
- package/dist/op/activities/terraform.d.ts.map +1 -1
- package/dist/op/activities/terraform.js +148 -7
- package/dist/plan-observation.d.ts +153 -0
- package/dist/plan-observation.d.ts.map +1 -0
- package/dist/plan-observation.js +354 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +2 -0
- package/package.json +2 -2
- package/src/__fixtures__/ACCEPTANCE.md +53 -1
- package/src/__fixtures__/capabilities/catalog/main.tf +105 -0
- package/src/__fixtures__/capabilities/chant.config.ts +15 -0
- package/src/__fixtures__/capabilities/findings/main.tf +225 -0
- package/src/__fixtures__/capabilities/ledger/main.tf +265 -0
- package/src/__fixtures__/capabilities/package.json +1 -0
- package/src/__fixtures__/effective-topology/chant.config.ts +11 -0
- package/src/__fixtures__/effective-topology/net/main.tf +176 -0
- package/src/__fixtures__/effective-topology/package.json +6 -0
- package/src/__fixtures__/kind-type/app/main.tf +35 -0
- package/src/__fixtures__/kind-type/chant.config.ts +11 -0
- package/src/__fixtures__/kind-type/package.json +6 -0
- package/src/__fixtures__/live-plan-depth/README.md +59 -0
- package/src/__fixtures__/live-plan-depth/live-plan.json +110 -0
- package/src/__fixtures__/live-plan-depth/show-plan-sensitive.json +1 -0
- package/src/__fixtures__/live-plan-depth/show-plan.json +1 -0
- package/src/__fixtures__/plan-read/main.tf +46 -0
- package/src/__fixtures__/plan-read/next.tfvars +1 -0
- package/src/__fixtures__/plan-read-drift/README.md +33 -0
- package/src/__fixtures__/plan-read-drift/main.tf +63 -0
- package/src/__fixtures__/plan-read-drift/next.tfvars +1 -0
- package/src/__fixtures__/search-roots/chant.config.ts +11 -0
- package/src/__fixtures__/search-roots/net/main.tf +73 -0
- package/src/__fixtures__/search-roots/package.json +6 -0
- package/src/__fixtures__/sensitive-default/chant.config.ts +11 -0
- package/src/__fixtures__/sensitive-default/net/main.tf +38 -0
- package/src/__fixtures__/sensitive-default/package.json +6 -0
- package/src/__fixtures__/show-plan.json +663 -0
- package/src/capabilities/adapter.ts +32 -0
- package/src/capabilities/mapping.json +166 -0
- package/src/capabilities-workspace.e2e.test.ts +251 -0
- package/src/capabilities.test.ts +264 -0
- package/src/config.ts +26 -0
- package/src/describe-resources.live-plan.test.ts +302 -0
- package/src/describe-resources.live-root-unchanged.test.ts +126 -0
- package/src/describe-resources.plan.test.ts +437 -0
- package/src/describe-resources.ts +445 -52
- package/src/effective-topology.test.ts +134 -0
- package/src/hcl/parse.ts +30 -2
- package/src/hcl/redact.ts +181 -0
- package/src/live-plan-depth.acceptance.e2e.test.ts +193 -0
- package/src/op/activities/terraform.test.ts +118 -1
- package/src/op/activities/terraform.ts +213 -9
- package/src/plan-observation.ts +428 -0
- package/src/plan-read.acceptance.test.ts +184 -0
- package/src/plugin.ts +2 -0
- package/src/redact-ir.e2e.test.ts +79 -0
- package/src/redact.test.ts +111 -0
- package/src/search-build-roots.test.ts +125 -0
- package/src/search-count.test.ts +144 -0
- package/src/search-kind-type.test.ts +136 -0
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
* {@link MIN_CHOUDOUFU_VERSION}.
|
|
40
40
|
*/
|
|
41
41
|
import { exec } from "node:child_process";
|
|
42
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
42
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
43
|
+
import { tmpdir } from "node:os";
|
|
43
44
|
import { promisify } from "node:util";
|
|
44
45
|
import { resolve, dirname, join, relative, sep } from "node:path";
|
|
45
46
|
import { loadChantConfigUpward } from "@intentius/chant/config";
|
|
@@ -59,7 +60,7 @@ const MAX_BUFFER = 64 * 1024 * 1024;
|
|
|
59
60
|
export const DEFAULT_PLAN_FILE = "chant.tfplan";
|
|
60
61
|
/** Binary used when the `terraform` namespace records no preference. */
|
|
61
62
|
export const DEFAULT_TERRAFORM_BINARY = "terraform";
|
|
62
|
-
/** Where {@link choudoufuLivePlan} writes GitHub issue #788's JSON document, relative to the root directory. */
|
|
63
|
+
/** Where the {@link choudoufuLivePlan} Op activity writes GitHub issue #788's JSON document, relative to the root directory. */
|
|
63
64
|
export const DEFAULT_LIVE_PLAN_DOCUMENT_FILE = "chant.live-plan.json";
|
|
64
65
|
/**
|
|
65
66
|
* choudoufu below this refuses. v0.12.0 brought `live-plan -json`, `live-ls`
|
|
@@ -927,6 +928,133 @@ export async function terraformShow(args, signal) {
|
|
|
927
928
|
...(planFile ? countPlanChanges(json) : { adds: 0, changes: 0, destroys: 0 }),
|
|
928
929
|
};
|
|
929
930
|
}
|
|
931
|
+
// ── The plan read (#4358) ───────────────────────────────────────────────────
|
|
932
|
+
/** Prefix of the scratch directory a plan read writes its plan file into, under the OS temp directory. */
|
|
933
|
+
export const PLAN_READ_DIR_PREFIX = "chant-terraform-plan-";
|
|
934
|
+
/** The plan file's name inside that scratch directory. */
|
|
935
|
+
export const PLAN_READ_FILE = "plan.tfplan";
|
|
936
|
+
/**
|
|
937
|
+
* `terraform plan` for an observation read (#4358): `-input=false -lock=false
|
|
938
|
+
* -out=<file>`, plus the root's own `-var-file`s.
|
|
939
|
+
*
|
|
940
|
+
* `-lock=false` is the point. A read must never take the backend's state lock,
|
|
941
|
+
* or a teammate's apply that starts while chant is reading would fail on
|
|
942
|
+
* "Error acquiring the state lock". `-detailed-exitcode` is not passed: the
|
|
943
|
+
* read wants the document, not the boolean, so exit 0 is the only success.
|
|
944
|
+
* The `-out` path is always outside the root (see {@link terraformPlanRead}).
|
|
945
|
+
*/
|
|
946
|
+
export function terraformPlanReadCommand(opts) {
|
|
947
|
+
const parts = [opts.binary, "plan", "-input=false", "-lock=false"];
|
|
948
|
+
for (const varFile of opts.varFiles ?? [])
|
|
949
|
+
parts.push(`-var-file=${quoteArg(varFile)}`);
|
|
950
|
+
parts.push(`-out=${quoteArg(opts.planFile)}`);
|
|
951
|
+
return parts.join(" ");
|
|
952
|
+
}
|
|
953
|
+
/**
|
|
954
|
+
* The one line a person runs to initialise a root chant refuses to plan,
|
|
955
|
+
* with the root's `backendConfig` carried, so it can be pasted as printed.
|
|
956
|
+
*/
|
|
957
|
+
export function terraformInitLine(opts) {
|
|
958
|
+
const init = terraformInitCommand({
|
|
959
|
+
binary: opts.binary,
|
|
960
|
+
...(opts.backendConfig ? { backendConfig: opts.backendConfig } : {}),
|
|
961
|
+
});
|
|
962
|
+
return init.replace(`${opts.binary} init`, `${opts.binary} -chdir=${quoteArg(opts.dir)} init`);
|
|
963
|
+
}
|
|
964
|
+
/** A plan read that did not produce a document. `kind` decides the fallback. */
|
|
965
|
+
export class TerraformPlanReadError extends Error {
|
|
966
|
+
constructor(kind, message, dir, initLine) {
|
|
967
|
+
super(message);
|
|
968
|
+
this.name = "TerraformPlanReadError";
|
|
969
|
+
this.kind = kind;
|
|
970
|
+
this.dir = dir;
|
|
971
|
+
if (initLine !== undefined)
|
|
972
|
+
this.initLine = initLine;
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* Sort a failed plan's own output into a {@link TerraformPlanReadFailureKind}.
|
|
977
|
+
* Matched on terraform's and OpenTofu's own error summaries. Order matters:
|
|
978
|
+
* an S3 backend without credentials says both "backend" and "credentials",
|
|
979
|
+
* and it is the backend that failed, so the backend patterns are read first.
|
|
980
|
+
*/
|
|
981
|
+
export function classifyPlanReadFailure(output) {
|
|
982
|
+
if (/Backend initialization required|Inconsistent dependency lock file|Required plugins are not installed|Module not installed|Missing required provider|please run "(terraform|tofu|choudoufu) init"|Run "(terraform|tofu|choudoufu) init"|Backend configuration changed/i.test(output)) {
|
|
983
|
+
return "not-initialised";
|
|
984
|
+
}
|
|
985
|
+
if (/error loading state|Failed to load state|Error refreshing state|Failed to get existing workspaces|error configuring (the )?\S+ backend|Error: Failed to (read|retrieve) state|Error retrieving state|Error acquiring the state lock|state data in S3|NoSuchBucket|failed to (get|read) (remote )?state|Unable to access object|\bbackend\b.*(credential|denied|unauthori[sz]ed|forbidden)/i.test(output)) {
|
|
986
|
+
return "backend";
|
|
987
|
+
}
|
|
988
|
+
return "plan";
|
|
989
|
+
}
|
|
990
|
+
/**
|
|
991
|
+
* Whether a root has been initialised: `.terraform` (or `TF_DATA_DIR`, when
|
|
992
|
+
* the environment points elsewhere) exists. A root whose providers or modules
|
|
993
|
+
* have since changed passes this check and is caught by
|
|
994
|
+
* {@link classifyPlanReadFailure} on the plan's own error instead.
|
|
995
|
+
*/
|
|
996
|
+
export function rootIsInitialised(dir, env = process.env) {
|
|
997
|
+
const dataDir = env.TF_DATA_DIR ? resolve(dir, env.TF_DATA_DIR) : join(dir, ".terraform");
|
|
998
|
+
return existsSync(dataDir);
|
|
999
|
+
}
|
|
1000
|
+
/**
|
|
1001
|
+
* Read a saved plan for one root without writing anything in it (#4358):
|
|
1002
|
+
* `plan -input=false -lock=false -out=<tmp>/plan.tfplan` in the root, then
|
|
1003
|
+
* `show -json <tmp>/plan.tfplan`, then remove `<tmp>`.
|
|
1004
|
+
*
|
|
1005
|
+
* Three rules, each a lesson behold already learned reading choudoufu
|
|
1006
|
+
* members (behold `src/choudoufu-plan.ts`):
|
|
1007
|
+
*
|
|
1008
|
+
* - No lock. `-lock=false` on the plan, always ({@link terraformPlanReadCommand}).
|
|
1009
|
+
* - Nothing in the root. `<tmp>` is a fresh `mkdtemp` under the OS temp
|
|
1010
|
+
* directory and is removed in a `finally`, failure or not. `init` is never
|
|
1011
|
+
* run: an uninitialised root throws a {@link TerraformPlanReadError} of
|
|
1012
|
+
* kind `"not-initialised"` carrying the line to run.
|
|
1013
|
+
* - A failure says which kind it is, so the observation can fall back by
|
|
1014
|
+
* depth ({@link classifyPlanReadFailure}).
|
|
1015
|
+
*
|
|
1016
|
+
* Uses the longInfra profile, like {@link terraformPlan}: a plan refreshes
|
|
1017
|
+
* every resource against its provider.
|
|
1018
|
+
*/
|
|
1019
|
+
export async function terraformPlanRead(args, signal) {
|
|
1020
|
+
const { binary, root, dir } = await resolveRoot(args, signal);
|
|
1021
|
+
const env = terraformEnvironment(root);
|
|
1022
|
+
const initLine = terraformInitLine({ binary, dir, ...(root.backendConfig ? { backendConfig: root.backendConfig } : {}) });
|
|
1023
|
+
const notInitialised = (detail) => new TerraformPlanReadError("not-initialised", `terraform.roots.${args.root} (${dir}) ${detail}: run \`${initLine}\` first. chant never runs init on a read, since init writes into the root.`, dir, initLine);
|
|
1024
|
+
if (!rootIsInitialised(dir, { ...process.env, ...env }))
|
|
1025
|
+
throw notInitialised("has never been initialised");
|
|
1026
|
+
const planDir = mkdtempSync(join(tmpdir(), PLAN_READ_DIR_PREFIX));
|
|
1027
|
+
const planFile = join(planDir, PLAN_READ_FILE);
|
|
1028
|
+
const command = terraformPlanReadCommand({ binary, planFile, ...(root.varFiles ? { varFiles: root.varFiles } : {}) });
|
|
1029
|
+
try {
|
|
1030
|
+
try {
|
|
1031
|
+
const { stderr } = await run(command, dir, env, signal);
|
|
1032
|
+
// The plan's own stdout is the human render, which this read never
|
|
1033
|
+
// uses; stderr carries warnings worth a human's eye.
|
|
1034
|
+
if (stderr)
|
|
1035
|
+
console.error(stderr);
|
|
1036
|
+
}
|
|
1037
|
+
catch (err) {
|
|
1038
|
+
const failure = err;
|
|
1039
|
+
if (typeof failure.code !== "number")
|
|
1040
|
+
throw err;
|
|
1041
|
+
const output = `${failure.stderr ?? ""}\n${failure.stdout ?? ""}`.replace(/\x1b\[[0-9;]*m/g, "");
|
|
1042
|
+
const kind = classifyPlanReadFailure(output);
|
|
1043
|
+
const line = output
|
|
1044
|
+
.split("\n")
|
|
1045
|
+
.map((l) => l.replace(/^[\s│╷╵]+/, "").trim())
|
|
1046
|
+
.find((l) => /^Error:/.test(l)) ?? `exit ${failure.code}`;
|
|
1047
|
+
if (kind === "not-initialised")
|
|
1048
|
+
throw notInitialised(`needs init (${line})`);
|
|
1049
|
+
throw new TerraformPlanReadError(kind, `${binary} plan failed in ${dir}: ${line}`, dir);
|
|
1050
|
+
}
|
|
1051
|
+
const shown = await run(terraformShowCommand({ binary, json: true, planFile }), dir, env, signal);
|
|
1052
|
+
return { json: JSON.parse(shown.stdout), dir, planDir, command };
|
|
1053
|
+
}
|
|
1054
|
+
finally {
|
|
1055
|
+
rmSync(planDir, { recursive: true, force: true });
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
930
1058
|
/**
|
|
931
1059
|
* The estate a choudoufu-only activity runs against, in precedence order:
|
|
932
1060
|
* `args.estate` when the step names one, then the root's own `estate`
|
|
@@ -1003,14 +1131,16 @@ export function liveDocumentFrom(stdout) {
|
|
|
1003
1131
|
* the root's environment. The graph command runs in such a scope, so its
|
|
1004
1132
|
* observation and its prediction see one `live-plan` between them, and the
|
|
1005
1133
|
* graph's statuses and the prediction's figures are statements about the
|
|
1006
|
-
* same read. The document
|
|
1134
|
+
* same read. The document is returned, not written: the root is left
|
|
1135
|
+
* byte-for-byte as it was (#4379). A caller that wants the file passes
|
|
1136
|
+
* `documentPath` (relative to the root) and it is written there on request.
|
|
1007
1137
|
*/
|
|
1008
1138
|
export async function choudoufuLivePlanDocument(args, signal) {
|
|
1009
1139
|
const resolved = await resolveRoot(args, signal);
|
|
1010
1140
|
const { binary, dir } = resolved;
|
|
1011
1141
|
const estate = resolveEstate(args, resolved, "choudoufuLivePlan");
|
|
1012
1142
|
const env = terraformEnvironment(resolved.root);
|
|
1013
|
-
const documentPath = args.documentPath
|
|
1143
|
+
const documentPath = args.documentPath;
|
|
1014
1144
|
// A root that declares its own estate is run without `-estate`: choudoufu
|
|
1015
1145
|
// refuses the flag beside a declaration and, since v0.14.0, settles the name
|
|
1016
1146
|
// from the declaration itself. See {@link choudoufuLivePlanCommand}.
|
|
@@ -1049,11 +1179,22 @@ export async function choudoufuLivePlanDocument(args, signal) {
|
|
|
1049
1179
|
// the identity on a supported binary. See {@link liveDocumentFrom}.
|
|
1050
1180
|
const document = liveDocumentFrom(jsonStdout);
|
|
1051
1181
|
const json = JSON.parse(document);
|
|
1052
|
-
|
|
1053
|
-
|
|
1182
|
+
if (documentPath !== undefined)
|
|
1183
|
+
writeFileSync(join(dir, documentPath), document);
|
|
1184
|
+
return {
|
|
1185
|
+
drift,
|
|
1186
|
+
json,
|
|
1187
|
+
dir,
|
|
1188
|
+
...(documentPath === undefined ? {} : { documentPath }),
|
|
1189
|
+
estate,
|
|
1190
|
+
...countLivePlanUnowned(json),
|
|
1191
|
+
};
|
|
1054
1192
|
}
|
|
1055
1193
|
export async function choudoufuLivePlan(args, signal) {
|
|
1056
|
-
|
|
1194
|
+
// The Op activity is the one caller that wants the file: `documentPath` is a
|
|
1195
|
+
// declared `.out` ref a later step or reviewer opens (#2103), so it asks.
|
|
1196
|
+
const documentPath = args.documentPath ?? DEFAULT_LIVE_PLAN_DOCUMENT_FILE;
|
|
1197
|
+
const { drift, json, dir, estate, ...unowned } = await choudoufuLivePlanDocument({ ...args, documentPath }, signal);
|
|
1057
1198
|
const resolved = await resolveRoot(args, signal);
|
|
1058
1199
|
const { binary } = resolved;
|
|
1059
1200
|
const env = terraformEnvironment(resolved.root);
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The plan depth's projection (#4358): a saved plan's `show -json` document,
|
|
3
|
+
* turned into the attributes an observed terraform block carries.
|
|
4
|
+
*
|
|
5
|
+
* The state read (`terraform show -json` over state) says what the last apply
|
|
6
|
+
* recorded. A plan says what the next apply would do, and four facts exist
|
|
7
|
+
* only there:
|
|
8
|
+
*
|
|
9
|
+
* | plan document | IR attribute |
|
|
10
|
+
* |---|---|
|
|
11
|
+
* | `resource_changes[].change.actions` | `planAction`, `planActions` |
|
|
12
|
+
* | `resource_changes[].change.replace_paths`, `resource_changes[].action_reason` | `planReplacePaths`, `planActionReason` |
|
|
13
|
+
* | `resource_changes[].change.after_unknown` | `planAfterUnknown` |
|
|
14
|
+
* | `resource_drift[]`, `relevant_attributes[]` | `planDrift`, `planDriftAction`, `planRelevantAttributes` |
|
|
15
|
+
*
|
|
16
|
+
* plus `planChanges`, the attribute-level before and after of each planned
|
|
17
|
+
* update or replace. `PLAN_ATTRIBUTE_NAMES` lists them for a consumer.
|
|
18
|
+
*
|
|
19
|
+
* ## Paths
|
|
20
|
+
*
|
|
21
|
+
* Every attribute path is one string: object keys joined with `.`, list
|
|
22
|
+
* indexes and keys that are not identifiers in brackets, so
|
|
23
|
+
* `lifecycle_commands[0].read`, `tags["kubernetes.io/role"]`. A `chant search
|
|
24
|
+
* attr:planReplacePaths=triggers_replace` term matches by substring over that.
|
|
25
|
+
*
|
|
26
|
+
* ## Sensitive values never leave this module
|
|
27
|
+
*
|
|
28
|
+
* `before_sensitive` and `after_sensitive` are trees parallel to `before` and
|
|
29
|
+
* `after`: `true` at a node marks that node and everything under it. A value
|
|
30
|
+
* under a `true` node is replaced with `(sensitive)` before it is put on an
|
|
31
|
+
* attribute, at any depth, so a sensitive value nested in a map is caught
|
|
32
|
+
* exactly like a sensitive top-level attribute. Only leaves are ever emitted,
|
|
33
|
+
* never a whole object, so no unwalked subtree can carry a secret past the
|
|
34
|
+
* check.
|
|
35
|
+
*
|
|
36
|
+
* A flag on either side redacts both sides of that path. On a stock root the
|
|
37
|
+
* two sides are flagged alike, because state records which paths were
|
|
38
|
+
* sensitive. On a choudoufu live root (#4371) they are not: prior state is
|
|
39
|
+
* rebuilt from the account on every run and carries no marks from the
|
|
40
|
+
* configuration, so an attribute made sensitive by a `sensitive` variable is
|
|
41
|
+
* flagged on `after` only, while `before` holds the live value, which is the
|
|
42
|
+
* previous secret. Recorded in `__fixtures__/live-plan-depth/`.
|
|
43
|
+
*
|
|
44
|
+
* Flags alone are not enough, as a real plan shows: `terraform_data` copies
|
|
45
|
+
* `input` to `output`, and the plan flags `input.token` sensitive while
|
|
46
|
+
* leaving `output.token` unflagged with the same value in it. So every string
|
|
47
|
+
* found under a sensitive flag anywhere in the document is collected first,
|
|
48
|
+
* and any emitted value equal to one of them is redacted too. That catches a
|
|
49
|
+
* copy; it cannot catch a secret a provider transformed (hashed, embedded in a
|
|
50
|
+
* longer string), and the observation page says so.
|
|
51
|
+
*
|
|
52
|
+
* Nothing from `prior_state` is surfaced but each row's `id`, which is the
|
|
53
|
+
* state read's rule too.
|
|
54
|
+
*/
|
|
55
|
+
/** What stands in for a value flagged sensitive, or equal to one that was. */
|
|
56
|
+
export declare const SENSITIVE_VALUE = "(sensitive)";
|
|
57
|
+
/** OpenTofu's and Terraform's own phrase for a value the plan cannot know yet. */
|
|
58
|
+
export declare const KNOWN_AFTER_APPLY = "(known after apply)";
|
|
59
|
+
/**
|
|
60
|
+
* Every attribute the plan depth adds to an observed block, beside the state
|
|
61
|
+
* read's `address`, `root`, `resourceType`, `mode` and `provider`. A block the
|
|
62
|
+
* plan does not touch carries `planAction: "no-op"` and none of the optional
|
|
63
|
+
* ones.
|
|
64
|
+
*/
|
|
65
|
+
export declare const PLAN_ATTRIBUTE_NAMES: readonly ["planAction", "planActions", "planActionReason", "planReplacePaths", "planAfterUnknown", "planChanges", "planDrift", "planDriftAction", "planRelevantAttributes"];
|
|
66
|
+
/** One attribute that differs between two sides of a change, at leaf level. */
|
|
67
|
+
export interface PlanValueChange {
|
|
68
|
+
/** Attribute path, see the module doc. */
|
|
69
|
+
path: string;
|
|
70
|
+
/** Before value, or `(sensitive)`. `null` where the side had no value. */
|
|
71
|
+
before: unknown;
|
|
72
|
+
/** After value, `(sensitive)`, or `(known after apply)`. */
|
|
73
|
+
after: unknown;
|
|
74
|
+
/** The instance this change belongs to, when the block has more than one. */
|
|
75
|
+
instance?: string;
|
|
76
|
+
/** On a drift entry: the plan reads this attribute (`relevant_attributes`). */
|
|
77
|
+
relevant?: boolean;
|
|
78
|
+
}
|
|
79
|
+
/** One `resource_changes[]` entry, projected. */
|
|
80
|
+
export interface PlanInstanceChange {
|
|
81
|
+
address: string;
|
|
82
|
+
actions: string[];
|
|
83
|
+
actionReason?: string;
|
|
84
|
+
replacePaths: string[];
|
|
85
|
+
afterUnknown: string[];
|
|
86
|
+
changes: PlanValueChange[];
|
|
87
|
+
}
|
|
88
|
+
/** One `resource_drift[]` entry, projected. */
|
|
89
|
+
export interface PlanInstanceDrift {
|
|
90
|
+
address: string;
|
|
91
|
+
actions: string[];
|
|
92
|
+
changes: PlanValueChange[];
|
|
93
|
+
}
|
|
94
|
+
/** A plan document, indexed for the plan depth. */
|
|
95
|
+
export interface PlanIndex {
|
|
96
|
+
/**
|
|
97
|
+
* `prior_state`, as the document carries it. The reader indexes it exactly
|
|
98
|
+
* as the state read indexes state, so presence and ownership come from here.
|
|
99
|
+
*/
|
|
100
|
+
priorState: unknown;
|
|
101
|
+
/** `resource_changes[]`, by instance address. */
|
|
102
|
+
changes: Map<string, PlanInstanceChange>;
|
|
103
|
+
/** `resource_drift[]`, by instance address. */
|
|
104
|
+
drift: Map<string, PlanInstanceDrift>;
|
|
105
|
+
/** `relevant_attributes[]`, by instance address: the attribute paths the plan read. */
|
|
106
|
+
relevant: Map<string, string[]>;
|
|
107
|
+
}
|
|
108
|
+
/** Render one path segment list as a single string. */
|
|
109
|
+
export declare function renderPath(segments: ReadonlyArray<string | number>): string;
|
|
110
|
+
/**
|
|
111
|
+
* The leaf-level differences between two sides of one change. Exported for
|
|
112
|
+
* the redaction tests; see the module doc for the rules.
|
|
113
|
+
*/
|
|
114
|
+
export declare function diffChange(change: {
|
|
115
|
+
before?: unknown;
|
|
116
|
+
after?: unknown;
|
|
117
|
+
before_sensitive?: unknown;
|
|
118
|
+
after_sensitive?: unknown;
|
|
119
|
+
after_unknown?: unknown;
|
|
120
|
+
}, secrets: ReadonlySet<string>): PlanValueChange[];
|
|
121
|
+
/**
|
|
122
|
+
* Index a `show -json <planfile>` document. Unknown fields are ignored and a
|
|
123
|
+
* missing section reads as empty, the same tolerance the state read has.
|
|
124
|
+
*/
|
|
125
|
+
export declare function indexPlan(document: unknown): PlanIndex;
|
|
126
|
+
/**
|
|
127
|
+
* The block address an instance address belongs to: every instance key
|
|
128
|
+
* removed, on the resource and on each module call, so `module.a[0].x.y["k"]`
|
|
129
|
+
* belongs to `module.a.x.y`.
|
|
130
|
+
*/
|
|
131
|
+
export declare function blockAddressOf(instance: string): string;
|
|
132
|
+
/** One action list as one word: `replace` for either order of delete and create. */
|
|
133
|
+
export declare function actionWord(actions: readonly string[]): string;
|
|
134
|
+
/**
|
|
135
|
+
* The plan attributes of one declared block, aggregated over its instances.
|
|
136
|
+
* `instances` are the block's instance addresses in `prior_state`; a block in
|
|
137
|
+
* the plan that has none there is a planned create, which the reader reports
|
|
138
|
+
* absent and annotates with {@link plannedAttributesFor} instead.
|
|
139
|
+
*/
|
|
140
|
+
export declare function planAttributesFor(address: string, index: PlanIndex): Record<string, unknown>;
|
|
141
|
+
/**
|
|
142
|
+
* The plan attributes of a block that has no row in `prior_state` (#4372),
|
|
143
|
+
* or undefined when the plan has no change for it either. The usual case is
|
|
144
|
+
* a planned create: `planAction: "create"`, `planActions`, and
|
|
145
|
+
* `planAfterUnknown` naming what stays unknown until apply (its `id` among
|
|
146
|
+
* them). A data block read during apply comes back as `planAction: "read"`.
|
|
147
|
+
*
|
|
148
|
+
* The reader puts these on the observation's `annotations`, not on a
|
|
149
|
+
* `resources` row, so the block stays absent and `lifecycle plan` still
|
|
150
|
+
* proposes the create.
|
|
151
|
+
*/
|
|
152
|
+
export declare function plannedAttributesFor(address: string, index: PlanIndex): Record<string, unknown> | undefined;
|
|
153
|
+
//# sourceMappingURL=plan-observation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-observation.d.ts","sourceRoot":"","sources":["../src/plan-observation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAEH,8EAA8E;AAC9E,eAAO,MAAM,eAAe,gBAAgB,CAAC;AAE7C,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,wBAAwB,CAAC;AAEvD;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,YAC/B,YAAY,EACZ,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,aAAa,EACb,WAAW,EACX,iBAAiB,EACjB,wBAAwB,CAChB,CAAC;AAEX,+EAA+E;AAC/E,MAAM,WAAW,eAAe;IAC9B,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,MAAM,EAAE,OAAO,CAAC;IAChB,4DAA4D;IAC5D,KAAK,EAAE,OAAO,CAAC;IACf,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,iDAAiD;AACjD,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,OAAO,EAAE,eAAe,EAAE,CAAC;CAC5B;AAED,+CAA+C;AAC/C,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,eAAe,EAAE,CAAC;CAC5B;AAED,mDAAmD;AACnD,MAAM,WAAW,SAAS;IACxB;;;OAGG;IACH,UAAU,EAAE,OAAO,CAAC;IACpB,iDAAiD;IACjD,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IACzC,+CAA+C;IAC/C,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IACtC,uFAAuF;IACvF,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CACjC;AAYD,uDAAuD;AACvD,wBAAgB,UAAU,CAAC,QAAQ,EAAE,aAAa,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,MAAM,CAQ3E;AA4DD;;;GAGG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE;IACN,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,EACD,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,GAC3B,eAAe,EAAE,CAgEnB;AAMD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,OAAO,GAAG,SAAS,CAmEtD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,oFAAoF;AACpF,wBAAgB,UAAU,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAI7D;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA6B5F;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAG3G"}
|