@intentius/chant-lexicon-terraform 0.75.1 → 0.77.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/behaviour/index.d.ts +1 -1
- package/dist/behaviour/index.d.ts.map +1 -1
- package/dist/behaviour/kinds.d.ts +9 -0
- package/dist/behaviour/kinds.d.ts.map +1 -1
- package/dist/behaviour/predict.d.ts +67 -9
- package/dist/behaviour/predict.d.ts.map +1 -1
- package/dist/behaviour/request.d.ts +1 -1
- package/dist/behaviour/request.d.ts.map +1 -1
- package/dist/describe-resources.d.ts +3 -2
- package/dist/describe-resources.d.ts.map +1 -1
- package/dist/hcl/descend.d.ts +1 -1
- package/dist/hcl/parse.d.ts +2 -2
- package/dist/hcl/roots.d.ts +1 -1
- package/dist/hcl/suppressions.d.ts +1 -1
- package/dist/hcl/value.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/integrity.json +3 -3
- package/dist/lint/post-synth/tf016.d.ts +1 -1
- package/dist/manifest.json +1 -1
- package/dist/op/activities/live-detect.d.ts +1 -1
- package/dist/op/activities/terraform.d.ts +38 -0
- package/dist/op/activities/terraform.d.ts.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/rules/tf016.ts +1 -1
- package/package.json +5 -5
- package/src/behaviour/index.ts +3 -0
- package/src/behaviour/kinds.ts +6 -1
- package/src/behaviour/predict.test.ts +171 -3
- package/src/behaviour/predict.ts +171 -9
- package/src/behaviour/request.test.ts +8 -1
- package/src/behaviour/request.ts +2 -1
- package/src/describe-resources.ts +4 -3
- package/src/hcl/descend.ts +1 -1
- package/src/hcl/parse.ts +2 -2
- package/src/hcl/roots.ts +1 -1
- package/src/hcl/suppressions.ts +1 -1
- package/src/hcl/value.ts +1 -1
- package/src/index.ts +3 -1
- package/src/lint/post-synth/tf016.ts +1 -1
- package/src/op/activities/live-detect.ts +1 -1
- package/src/op/activities/terraform.test.ts +117 -0
- package/src/op/activities/terraform.ts +85 -28
- package/src/plugin.test.ts +23 -1
- package/src/plugin.ts +13 -0
|
@@ -23,6 +23,8 @@ import {
|
|
|
23
23
|
terraformApply,
|
|
24
24
|
terraformShow,
|
|
25
25
|
choudoufuLivePlan,
|
|
26
|
+
choudoufuLivePlanDocument,
|
|
27
|
+
choudoufuLiveLs,
|
|
26
28
|
terraformInitCommand,
|
|
27
29
|
terraformPlanCommand,
|
|
28
30
|
terraformApplyCommand,
|
|
@@ -36,6 +38,7 @@ import {
|
|
|
36
38
|
DEFAULT_PLAN_FILE,
|
|
37
39
|
__resetChoudoufuVersionCheckForTests,
|
|
38
40
|
} from "./terraform";
|
|
41
|
+
import { withLiveReadSession } from "@intentius/chant/live-read-session";
|
|
39
42
|
|
|
40
43
|
// ── The child-process stub ──────────────────────────────────────────────────
|
|
41
44
|
|
|
@@ -601,3 +604,117 @@ describe("every activity forwards the AbortSignal (#2086)", () => {
|
|
|
601
604
|
});
|
|
602
605
|
}
|
|
603
606
|
});
|
|
607
|
+
|
|
608
|
+
describe("one command, one read of the account (chant#2498)", () => {
|
|
609
|
+
const LIVE_PLAN_DOCUMENT = JSON.stringify({ estate: "one-read", bound: [], omissions: [], unowned: [] });
|
|
610
|
+
const LIVE_LS = JSON.stringify({ estate: "one-read", resources: [] });
|
|
611
|
+
|
|
612
|
+
function liveProject(): string {
|
|
613
|
+
const dir = project({ binary: "choudoufu", roots: { estate: { dir: "./infra" } } });
|
|
614
|
+
writeFileSync(join(dir, "infra", "estate.chdf.hcl"), 'estate = "one-read"\n');
|
|
615
|
+
return dir;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
const runs = (fragment: string): number => execCalls.filter((c) => c.cmd.includes(fragment)).length;
|
|
619
|
+
|
|
620
|
+
beforeEach(() => {
|
|
621
|
+
__resetChoudoufuVersionCheckForTests();
|
|
622
|
+
replies.push(
|
|
623
|
+
{ match: "choudoufu version", reply: { stdout: "choudoufu v0.15.0 (based on OpenTofu v1.13.0)\non darwin_arm64", stderr: "" } },
|
|
624
|
+
{ match: "live-plan -detailed-exitcode -json", reply: { stdout: LIVE_PLAN_DOCUMENT, stderr: "" } },
|
|
625
|
+
{ match: "-no-color", reply: { stdout: "No changes.\n\nPlan: 0 to add, 0 to change, 0 to destroy.\n", stderr: "" } },
|
|
626
|
+
{ match: "live-ls", reply: { stdout: LIVE_LS, stderr: "" } },
|
|
627
|
+
);
|
|
628
|
+
vi.spyOn(console, "error").mockImplementation(() => undefined);
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
test("the document alone is one run, with no human render", async () => {
|
|
632
|
+
const dir = liveProject();
|
|
633
|
+
const result = await choudoufuLivePlanDocument({ root: "estate", cwd: dir });
|
|
634
|
+
expect(result.estate).toBe("one-read");
|
|
635
|
+
expect(result.drift).toBe(false);
|
|
636
|
+
expect(result.json).toEqual(JSON.parse(LIVE_PLAN_DOCUMENT));
|
|
637
|
+
expect(runs("live-plan -detailed-exitcode -json")).toBe(1);
|
|
638
|
+
expect(runs("-no-color")).toBe(0);
|
|
639
|
+
expect("text" in result).toBe(false);
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
test("the full activity still makes the two runs, the second for the render", async () => {
|
|
643
|
+
const dir = liveProject();
|
|
644
|
+
const result = await choudoufuLivePlan({ root: "estate", cwd: dir });
|
|
645
|
+
expect(result.text).toContain("No changes.");
|
|
646
|
+
expect(runs("live-plan -detailed-exitcode -json")).toBe(1);
|
|
647
|
+
expect(runs("live-plan -detailed-exitcode -no-color")).toBe(1);
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
test("outside a read session, every call reads", async () => {
|
|
651
|
+
const dir = liveProject();
|
|
652
|
+
await choudoufuLivePlanDocument({ root: "estate", cwd: dir });
|
|
653
|
+
await choudoufuLivePlanDocument({ root: "estate", cwd: dir });
|
|
654
|
+
await choudoufuLiveLs({ root: "estate", cwd: dir, consistent: true });
|
|
655
|
+
await choudoufuLiveLs({ root: "estate", cwd: dir, consistent: true });
|
|
656
|
+
expect(runs("live-plan -detailed-exitcode -json")).toBe(2);
|
|
657
|
+
expect(runs("live-ls")).toBe(2);
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
test("inside one session, the observation's read and the prediction's read are one live-plan and one live-ls", async () => {
|
|
661
|
+
const dir = liveProject();
|
|
662
|
+
await withLiveReadSession(async () => {
|
|
663
|
+
// What describeResources() does, then what predictBehaviour() does,
|
|
664
|
+
// then the full activity a finding mode would run on top.
|
|
665
|
+
const observed = await choudoufuLivePlanDocument({ root: "estate", cwd: dir });
|
|
666
|
+
const listed = await choudoufuLiveLs({ root: "estate", cwd: dir, consistent: true });
|
|
667
|
+
const predicted = await choudoufuLivePlanDocument({ root: "estate", cwd: dir });
|
|
668
|
+
const listedAgain = await choudoufuLiveLs({ root: "estate", cwd: dir, consistent: true });
|
|
669
|
+
const rendered = await choudoufuLivePlan({ root: "estate", cwd: dir });
|
|
670
|
+
expect(predicted.json).toEqual(observed.json);
|
|
671
|
+
expect(listedAgain.json).toEqual(listed.json);
|
|
672
|
+
expect(rendered.json).toEqual(observed.json);
|
|
673
|
+
});
|
|
674
|
+
expect(runs("live-plan -detailed-exitcode -json")).toBe(1);
|
|
675
|
+
expect(runs("live-plan -detailed-exitcode -no-color")).toBe(1);
|
|
676
|
+
expect(runs("live-ls")).toBe(1);
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
test("a different flag is a different read", async () => {
|
|
680
|
+
const dir = liveProject();
|
|
681
|
+
await withLiveReadSession(async () => {
|
|
682
|
+
await choudoufuLivePlanDocument({ root: "estate", cwd: dir });
|
|
683
|
+
await choudoufuLivePlanDocument({ root: "estate", cwd: dir, adoptionOnly: true });
|
|
684
|
+
await choudoufuLiveLs({ root: "estate", cwd: dir, consistent: true });
|
|
685
|
+
await choudoufuLiveLs({ root: "estate", cwd: dir });
|
|
686
|
+
});
|
|
687
|
+
expect(runs("live-plan -detailed-exitcode -json")).toBe(2);
|
|
688
|
+
expect(runs("live-ls -estate=one-read -json -consistent")).toBe(1);
|
|
689
|
+
expect(runs("live-ls")).toBe(2);
|
|
690
|
+
const adoption = execCalls.filter((c) => c.opts.env?.TOFU_LIVE_COLLECT_UNCLAIMED === "1");
|
|
691
|
+
expect(adoption.length).toBe(1);
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
test("a read that failed is not shared: the next caller reads again", async () => {
|
|
695
|
+
const dir = liveProject();
|
|
696
|
+
replies.unshift({ match: "live-plan -detailed-exitcode -json", reply: execError(1, "no credentials") });
|
|
697
|
+
await withLiveReadSession(async () => {
|
|
698
|
+
await expect(choudoufuLivePlanDocument({ root: "estate", cwd: dir })).rejects.toThrow("no credentials");
|
|
699
|
+
replies.shift();
|
|
700
|
+
const result = await choudoufuLivePlanDocument({ root: "estate", cwd: dir });
|
|
701
|
+
expect(result.json).toEqual(JSON.parse(LIVE_PLAN_DOCUMENT));
|
|
702
|
+
});
|
|
703
|
+
expect(runs("live-plan -detailed-exitcode -json")).toBe(2);
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
test("drift on the shared read is the same drift for every caller", async () => {
|
|
707
|
+
const dir = liveProject();
|
|
708
|
+
replies.unshift({
|
|
709
|
+
match: "live-plan -detailed-exitcode -json",
|
|
710
|
+
reply: execError(2, "", LIVE_PLAN_DOCUMENT),
|
|
711
|
+
});
|
|
712
|
+
await withLiveReadSession(async () => {
|
|
713
|
+
const first = await choudoufuLivePlanDocument({ root: "estate", cwd: dir });
|
|
714
|
+
const second = await choudoufuLivePlanDocument({ root: "estate", cwd: dir });
|
|
715
|
+
expect(first.drift).toBe(true);
|
|
716
|
+
expect(second.drift).toBe(true);
|
|
717
|
+
});
|
|
718
|
+
expect(runs("live-plan -detailed-exitcode -json")).toBe(1);
|
|
719
|
+
});
|
|
720
|
+
});
|
|
@@ -44,6 +44,7 @@ import { writeFileSync } from "node:fs";
|
|
|
44
44
|
import { promisify } from "node:util";
|
|
45
45
|
import { resolve, dirname, join } from "node:path";
|
|
46
46
|
import { loadChantConfigUpward } from "@intentius/chant/config";
|
|
47
|
+
import { liveReadKey, memoLiveRead } from "@intentius/chant/live-read-session";
|
|
47
48
|
import { computePlanDigest } from "@intentius/chant/op";
|
|
48
49
|
import type { TerraformConfig, TerraformRootConfig } from "../../config";
|
|
49
50
|
import { detectLiveEstate } from "./live-detect";
|
|
@@ -1173,10 +1174,46 @@ export function liveDocumentFrom(stdout: string): string {
|
|
|
1173
1174
|
return start === -1 ? stdout : lines.slice(start).join("\n");
|
|
1174
1175
|
}
|
|
1175
1176
|
|
|
1176
|
-
|
|
1177
|
+
/**
|
|
1178
|
+
* What {@link choudoufuLivePlanDocument} resolves: the document and the facts
|
|
1179
|
+
* around it, and none of the human render. The subset of
|
|
1180
|
+
* {@link ChoudoufuLivePlanResult} a reader of the document needs.
|
|
1181
|
+
*/
|
|
1182
|
+
export interface ChoudoufuLivePlanDocumentResult extends LivePlanUnownedCounts {
|
|
1183
|
+
/** `true` when `-detailed-exitcode` reported exit 2: the live plan proposes changes. */
|
|
1184
|
+
drift: boolean;
|
|
1185
|
+
/** GitHub issue #788's JSON document, captured whole. */
|
|
1186
|
+
json: unknown;
|
|
1187
|
+
/** Absolute path of the root module directory. */
|
|
1188
|
+
dir: string;
|
|
1189
|
+
/** Where the JSON document was written, relative to `dir`. */
|
|
1190
|
+
documentPath: string;
|
|
1191
|
+
/** The estate this plan ran against. */
|
|
1192
|
+
estate: string;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
/**
|
|
1196
|
+
* `live-plan -detailed-exitcode -json` alone: the document, without the
|
|
1197
|
+
* second run that renders the plan for a human (chant #2498).
|
|
1198
|
+
*
|
|
1199
|
+
* Two of {@link choudoufuLivePlan}'s three callers never read `text`:
|
|
1200
|
+
* `describeResources()` indexes the document and `predictBehaviour()` prices
|
|
1201
|
+
* it. Each was paying for the render anyway, and on a drifted estate whose
|
|
1202
|
+
* provider retries a deleted resource the render costs as much as the
|
|
1203
|
+
* document. This is the read they make instead.
|
|
1204
|
+
*
|
|
1205
|
+
* Inside a `withLiveReadSession` scope (core's `live-read-session.ts`) the
|
|
1206
|
+
* run is shared with every identical read in the same command, keyed on
|
|
1207
|
+
* the root, its resolved directory, the binary, the estate, the flags and
|
|
1208
|
+
* the root's environment. The graph command runs in such a scope, so its
|
|
1209
|
+
* observation and its prediction see one `live-plan` between them, and the
|
|
1210
|
+
* graph's statuses and the prediction's figures are statements about the
|
|
1211
|
+
* same read. The document file is still written on every call.
|
|
1212
|
+
*/
|
|
1213
|
+
export async function choudoufuLivePlanDocument(
|
|
1177
1214
|
args: ChoudoufuLivePlanArgs & { documentPath?: string },
|
|
1178
1215
|
signal?: AbortSignal,
|
|
1179
|
-
): Promise<
|
|
1216
|
+
): Promise<ChoudoufuLivePlanDocumentResult> {
|
|
1180
1217
|
const resolved = await resolveRoot(args, signal);
|
|
1181
1218
|
const { binary, dir } = resolved;
|
|
1182
1219
|
const estate = resolveEstate(args, resolved, "choudoufuLivePlan");
|
|
@@ -1187,8 +1224,6 @@ export async function choudoufuLivePlan(
|
|
|
1187
1224
|
// from the declaration itself. See {@link choudoufuLivePlanCommand}.
|
|
1188
1225
|
const estateFlag: { estate?: string } = resolved.estate === undefined ? { estate } : {};
|
|
1189
1226
|
|
|
1190
|
-
let drift: boolean;
|
|
1191
|
-
let jsonStdout: string;
|
|
1192
1227
|
const planCmd = choudoufuLivePlanCommand({ binary, ...estateFlag, json: true });
|
|
1193
1228
|
// The document's `adoptable` and `swept` sections are populated only on a
|
|
1194
1229
|
// run that asked the estate-wide sweep the account-bounded question, which
|
|
@@ -1198,30 +1233,47 @@ export async function choudoufuLivePlan(
|
|
|
1198
1233
|
// run asks for the same thing. Set only under `adoptionOnly`: the sweep is
|
|
1199
1234
|
// an account-wide list per admitted type, and an observation read has no use
|
|
1200
1235
|
// for it.
|
|
1201
|
-
const
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
const
|
|
1212
|
-
throw
|
|
1213
|
-
|
|
1214
|
-
|
|
1236
|
+
const adoptionOnly = args.adoptionOnly === true;
|
|
1237
|
+
const planEnv = adoptionOnly ? { ...env, TOFU_LIVE_COLLECT_UNCLAIMED: "1" } : env;
|
|
1238
|
+
const key = liveReadKey("live-plan", { root: args.root, dir, binary, estate, adoptionOnly, env });
|
|
1239
|
+
|
|
1240
|
+
const { drift, jsonStdout } = await memoLiveRead(key, async (): Promise<{ drift: boolean; jsonStdout: string }> => {
|
|
1241
|
+
try {
|
|
1242
|
+
const { stdout, stderr } = await run(planCmd, dir, planEnv, signal);
|
|
1243
|
+
report(stdout, stderr);
|
|
1244
|
+
return { drift: false, jsonStdout: stdout };
|
|
1245
|
+
} catch (err) {
|
|
1246
|
+
const failure = err as ExecFailure;
|
|
1247
|
+
if (typeof failure.code !== "number") throw err;
|
|
1248
|
+
if (failure.code !== 2) {
|
|
1249
|
+
const detail = (failure.stderr ?? "").trim() || (failure.stdout ?? "").trim();
|
|
1250
|
+
throw new Error(
|
|
1251
|
+
`${binary} live-plan failed in ${dir} (exit ${failure.code})${detail ? `\n${detail}` : ""}`,
|
|
1252
|
+
);
|
|
1253
|
+
}
|
|
1254
|
+
report(failure.stdout ?? "", failure.stderr ?? "");
|
|
1255
|
+
return { drift: true, jsonStdout: failure.stdout ?? "" };
|
|
1215
1256
|
}
|
|
1216
|
-
|
|
1217
|
-
drift = true;
|
|
1218
|
-
jsonStdout = failure.stdout ?? "";
|
|
1219
|
-
}
|
|
1257
|
+
});
|
|
1220
1258
|
|
|
1221
1259
|
// Belt and braces: v0.14.0 keeps `-json` stdout to the document, so this is
|
|
1222
1260
|
// the identity on a supported binary. See {@link liveDocumentFrom}.
|
|
1223
1261
|
const document = liveDocumentFrom(jsonStdout);
|
|
1224
1262
|
const json: unknown = JSON.parse(document);
|
|
1263
|
+
writeFileSync(join(dir, documentPath), document);
|
|
1264
|
+
return { drift, json, dir, documentPath, estate, ...countLivePlanUnowned(json) };
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
export async function choudoufuLivePlan(
|
|
1268
|
+
args: ChoudoufuLivePlanArgs & { documentPath?: string },
|
|
1269
|
+
signal?: AbortSignal,
|
|
1270
|
+
): Promise<ChoudoufuLivePlanResult> {
|
|
1271
|
+
const { drift, json, dir, documentPath, estate, ...unowned } = await choudoufuLivePlanDocument(args, signal);
|
|
1272
|
+
const resolved = await resolveRoot(args, signal);
|
|
1273
|
+
const { binary } = resolved;
|
|
1274
|
+
const env = terraformEnvironment(resolved.root);
|
|
1275
|
+
const estateFlag: { estate?: string } = resolved.estate === undefined ? { estate } : {};
|
|
1276
|
+
|
|
1225
1277
|
// #788's document carries no render of the human plan itself
|
|
1226
1278
|
// (`views.LivePlanDocument` has no such field), so the human text needs a
|
|
1227
1279
|
// second live read rather than a field on the same response. `-adoption-only`
|
|
@@ -1255,8 +1307,6 @@ export async function choudoufuLivePlan(
|
|
|
1255
1307
|
text = failure.stdout ?? "";
|
|
1256
1308
|
}
|
|
1257
1309
|
|
|
1258
|
-
writeFileSync(join(dir, documentPath), document);
|
|
1259
|
-
|
|
1260
1310
|
// An `adoptable[]` row carries its own tagging command in the document
|
|
1261
1311
|
// (choudoufu #962). An `unowned[]` row does not, and the render is the only
|
|
1262
1312
|
// place one is printed for it, so the parse still runs on an adoption run
|
|
@@ -1279,7 +1329,7 @@ export async function choudoufuLivePlan(
|
|
|
1279
1329
|
dir,
|
|
1280
1330
|
documentPath,
|
|
1281
1331
|
estate,
|
|
1282
|
-
...
|
|
1332
|
+
...unowned,
|
|
1283
1333
|
...parseChoudoufuPlanSummary(text),
|
|
1284
1334
|
};
|
|
1285
1335
|
}
|
|
@@ -1301,9 +1351,16 @@ export async function choudoufuLiveLs(
|
|
|
1301
1351
|
const estate = resolveEstate(args, resolved, "choudoufuLiveLs");
|
|
1302
1352
|
const env = terraformEnvironment(resolved.root);
|
|
1303
1353
|
|
|
1304
|
-
const
|
|
1305
|
-
const {
|
|
1306
|
-
|
|
1354
|
+
const consistent = args.consistent === true;
|
|
1355
|
+
const cmd = choudoufuLiveLsCommand({ binary, estate, ...(consistent ? { consistent: true } : {}) });
|
|
1356
|
+
// Shared with every identical listing in the same read session, the way
|
|
1357
|
+
// {@link choudoufuLivePlanDocument} shares the document (chant #2498).
|
|
1358
|
+
const key = liveReadKey("live-ls", { root: args.root, dir, binary, estate, consistent, env });
|
|
1359
|
+
const stdout = await memoLiveRead(key, async () => {
|
|
1360
|
+
const result = await run(cmd, dir, env, signal);
|
|
1361
|
+
report(result.stdout, result.stderr);
|
|
1362
|
+
return result.stdout;
|
|
1363
|
+
});
|
|
1307
1364
|
|
|
1308
1365
|
return { json: JSON.parse(stdout), dir, estate };
|
|
1309
1366
|
}
|
package/src/plugin.test.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { dirname, join } from "node:path";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { behaviourEngineVariables, isBehaviourRefusalReport } from "@intentius/chant/behaviour";
|
|
4
5
|
import { isLexiconPlugin } from "@intentius/chant/lexicon";
|
|
5
6
|
import { validateLexiconConfig } from "@intentius/chant/lexicon-config";
|
|
6
7
|
import type { ChantConfig } from "@intentius/chant/config";
|
|
@@ -79,6 +80,27 @@ describe("terraform plugin", () => {
|
|
|
79
80
|
});
|
|
80
81
|
});
|
|
81
82
|
|
|
83
|
+
describe("predictBehaviour on the plugin (#2495)", () => {
|
|
84
|
+
afterEach(() => vi.unstubAllEnvs());
|
|
85
|
+
|
|
86
|
+
it("reaches core's engine front with nothing injected, and refuses by name when no engine is set", async () => {
|
|
87
|
+
for (const name of behaviourEngineVariables("chant")) vi.stubEnv(name, "");
|
|
88
|
+
const result = await terraformPlugin.predictBehaviour!({
|
|
89
|
+
environment: "prod",
|
|
90
|
+
buildOutput: "",
|
|
91
|
+
from: "declared",
|
|
92
|
+
traffic: "100 rps, p50",
|
|
93
|
+
entityNames: ["net/aws_vpc.main"],
|
|
94
|
+
entities: new Map([
|
|
95
|
+
["net/aws_vpc.main", { entityType: "Terraform::Resource", props: { root: "net", address: "aws_vpc.main", body: {} } }],
|
|
96
|
+
]),
|
|
97
|
+
edges: [],
|
|
98
|
+
edgeCoverage: { verdict: "unknown" },
|
|
99
|
+
});
|
|
100
|
+
expect(isBehaviourRefusalReport(result) && result.refusal.cause).toBe("no-engine");
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
82
104
|
describe("terraform config schema", () => {
|
|
83
105
|
it("accepts a root with every optional field", () => {
|
|
84
106
|
const parsed = terraformConfigSchema.parse({
|
package/src/plugin.ts
CHANGED
|
@@ -135,6 +135,19 @@ export const terraformPlugin: LexiconPlugin = {
|
|
|
135
135
|
return describeResources(options);
|
|
136
136
|
},
|
|
137
137
|
|
|
138
|
+
/**
|
|
139
|
+
* The prediction, for whichever estate `options.from` names (#2495). The
|
|
140
|
+
* producer is `predictTerraformBehaviour` and this is its second caller, not
|
|
141
|
+
* a second producer: the contract's options are read as its own and handed
|
|
142
|
+
* over, and it runs with the real reads and core's engine front. What that
|
|
143
|
+
* reading has to put back, for a declared graph and for a live one, is on
|
|
144
|
+
* `terraformPredictOptionsFrom`.
|
|
145
|
+
*/
|
|
146
|
+
async predictBehaviour(options) {
|
|
147
|
+
const { predictTerraformBehaviour, terraformPredictOptionsFrom } = await import("./behaviour");
|
|
148
|
+
return predictTerraformBehaviour(await terraformPredictOptionsFrom(options));
|
|
149
|
+
},
|
|
150
|
+
|
|
138
151
|
/**
|
|
139
152
|
* The kinds a live root can enumerate beyond its declaration (#1278,
|
|
140
153
|
* #2104). Empty on a project with no live root, because `live-ls` is the
|