@intentius/chant-lexicon-pulumi 0.124.54 → 0.124.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -3
- package/dist/adopt.d.ts +68 -0
- package/dist/adopt.d.ts.map +1 -0
- package/dist/adopt.js +162 -0
- package/dist/capability-plugin.d.ts +3 -0
- package/dist/capability-plugin.d.ts.map +1 -1
- package/dist/capability-plugin.js +8 -1
- package/dist/change-set.d.ts +27 -4
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +15 -5
- package/dist/check-step.d.ts +61 -0
- package/dist/check-step.d.ts.map +1 -0
- package/dist/check-step.js +270 -0
- package/dist/ci-setup.d.ts +53 -0
- package/dist/ci-setup.d.ts.map +1 -0
- package/dist/ci-setup.js +146 -0
- package/dist/composites/pulumi-adopt-op.d.ts +106 -0
- package/dist/composites/pulumi-adopt-op.d.ts.map +1 -0
- package/dist/composites/pulumi-adopt-op.js +132 -0
- package/dist/engine-events.d.ts +222 -0
- package/dist/engine-events.d.ts.map +1 -0
- package/dist/engine-events.js +280 -0
- package/dist/exit-codes.d.ts +53 -0
- package/dist/exit-codes.d.ts.map +1 -0
- package/dist/exit-codes.js +67 -0
- package/dist/index.d.ts +13 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -2
- package/dist/init-templates.d.ts.map +1 -1
- package/dist/init-templates.js +5 -3
- package/dist/integrity.json +3 -3
- package/dist/lint/post-synth/pul015.d.ts +2 -1
- package/dist/lint/post-synth/pul015.d.ts.map +1 -1
- package/dist/lock-rows.d.ts +32 -0
- package/dist/lock-rows.d.ts.map +1 -0
- package/dist/lock-rows.js +36 -0
- package/dist/manifest.json +1 -1
- package/dist/member-engine.d.ts +27 -2
- package/dist/member-engine.d.ts.map +1 -1
- package/dist/member-engine.js +58 -7
- package/dist/member-outputs.d.ts +10 -3
- package/dist/member-outputs.d.ts.map +1 -1
- package/dist/member-outputs.js +15 -7
- package/dist/op/activities/index.d.ts +9 -1
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/index.js +8 -1
- package/dist/op/activities/pulumi-adopt.d.ts +104 -0
- package/dist/op/activities/pulumi-adopt.d.ts.map +1 -0
- package/dist/op/activities/pulumi-adopt.js +216 -0
- package/dist/op/activities/pulumi.d.ts.map +1 -1
- package/dist/op/activities/pulumi.js +2 -1
- package/dist/op/activity-contracts.d.ts +73 -0
- package/dist/op/activity-contracts.d.ts.map +1 -1
- package/dist/op/activity-contracts.js +49 -0
- package/dist/op/builders.d.ts +9 -0
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/builders.js +20 -0
- package/dist/plan-shape.d.ts +47 -0
- package/dist/plan-shape.d.ts.map +1 -0
- package/dist/plan-shape.js +94 -0
- package/dist/planned-output-changes.d.ts +60 -0
- package/dist/planned-output-changes.d.ts.map +1 -0
- package/dist/planned-output-changes.js +94 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +38 -4
- package/dist/plunge-timings.d.ts +43 -0
- package/dist/plunge-timings.d.ts.map +1 -0
- package/dist/plunge-timings.js +106 -0
- package/dist/preview-tips.d.ts +71 -0
- package/dist/preview-tips.d.ts.map +1 -0
- package/dist/preview-tips.js +76 -0
- package/dist/pulumi-up.d.ts +384 -0
- package/dist/pulumi-up.d.ts.map +1 -0
- package/dist/pulumi-up.js +836 -0
- package/dist/release-copy.d.ts +47 -0
- package/dist/release-copy.d.ts.map +1 -0
- package/dist/release-copy.js +131 -0
- package/dist/review-stack.d.ts +92 -0
- package/dist/review-stack.d.ts.map +1 -0
- package/dist/review-stack.js +222 -0
- package/dist/rules/pul015.ts +2 -1
- package/dist/run.d.ts +269 -7
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +473 -26
- package/dist/stack-export-snapshot.d.ts +125 -0
- package/dist/stack-export-snapshot.d.ts.map +1 -0
- package/dist/stack-export-snapshot.js +233 -0
- package/dist/stack-ops.d.ts +175 -0
- package/dist/stack-ops.d.ts.map +1 -0
- package/dist/stack-ops.js +400 -0
- package/dist/stack-references.d.ts +46 -0
- package/dist/stack-references.d.ts.map +1 -0
- package/dist/stack-references.js +88 -0
- package/dist/trace-facts.d.ts +34 -0
- package/dist/trace-facts.d.ts.map +1 -0
- package/dist/trace-facts.js +81 -0
- package/dist/workspace-member.d.ts +13 -2
- package/dist/workspace-member.d.ts.map +1 -1
- package/dist/workspace-member.js +14 -9
- package/package.json +2 -2
- package/src/adopt.ts +202 -0
- package/src/capability-plugin.ts +15 -6
- package/src/change-set.ts +29 -5
- package/src/check-step.test.ts +191 -0
- package/src/check-step.ts +316 -0
- package/src/ci-setup.test.ts +123 -0
- package/src/ci-setup.ts +176 -0
- package/src/composites/pulumi-adopt-op.test.ts +223 -0
- package/src/composites/pulumi-adopt-op.ts +191 -0
- package/src/composites/pulumi-watch-op.test.ts +2 -2
- package/src/engine-events.test.ts +134 -0
- package/src/engine-events.ts +390 -0
- package/src/engine-graph.test.ts +1 -1
- package/src/exit-codes.test.ts +21 -0
- package/src/exit-codes.ts +71 -0
- package/src/index.ts +77 -1
- package/src/init-templates.ts +5 -3
- package/src/lint/post-synth/pul015.ts +2 -1
- package/src/lock-rows.test.ts +60 -0
- package/src/lock-rows.ts +56 -0
- package/src/member-engine.test.ts +112 -0
- package/src/member-engine.ts +73 -6
- package/src/member-outputs.test.ts +30 -8
- package/src/member-outputs.ts +31 -12
- package/src/op/activities/index.ts +19 -1
- package/src/op/activities/pulumi-adopt.ts +334 -0
- package/src/op/activities/pulumi.ts +2 -1
- package/src/op/activity-contracts.ts +75 -0
- package/src/op/builders.ts +37 -0
- package/src/plan-shape.test.ts +91 -0
- package/src/plan-shape.ts +105 -0
- package/src/planned-output-changes.test.ts +85 -0
- package/src/planned-output-changes.ts +141 -0
- package/src/plugin.test.ts +1 -1
- package/src/plugin.ts +42 -4
- package/src/plunge-timings.test.ts +241 -0
- package/src/plunge-timings.ts +115 -0
- package/src/plunge-up-failed.test.ts +54 -0
- package/src/pr-note.test.ts +87 -0
- package/src/preview-tips.test.ts +135 -0
- package/src/preview-tips.ts +128 -0
- package/src/pulumi-up.test.ts +470 -0
- package/src/pulumi-up.ts +1053 -0
- package/src/release-copy.test.ts +74 -0
- package/src/release-copy.ts +142 -0
- package/src/review-stack.test.ts +254 -0
- package/src/review-stack.ts +266 -0
- package/src/run-plan-flags.test.ts +53 -0
- package/src/run.test.ts +176 -3
- package/src/run.ts +544 -30
- package/src/stack-export-snapshot.test.ts +190 -0
- package/src/stack-export-snapshot.ts +312 -0
- package/src/stack-ops.test.ts +286 -0
- package/src/stack-ops.ts +552 -0
- package/src/stack-references.test.ts +96 -0
- package/src/stack-references.ts +110 -0
- package/src/testdata/pulumi-stream/README.md +16 -0
- package/src/testdata/pulumi-stream/preview-stream.ndjson +42 -0
- package/src/testdata/pulumi-stream/up-failed-plunge.ndjson +5 -0
- package/src/trace-facts.test.ts +38 -0
- package/src/trace-facts.ts +83 -0
- package/src/workspace-member.test.ts +2 -1
- package/src/workspace-member.ts +30 -12
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PulumiAdoptOp composite (#4562): adopt stock Pulumi stacks onto plunge's
|
|
3
|
+
* per-resource records, behind a gate, the pulumi twin of the terraform
|
|
4
|
+
* lexicon's `TerraformAdoptOp`. It is the step after `plywood init --engine
|
|
5
|
+
* plunge` (INTENTIUS/plywood#3); plywood supplies the words.
|
|
6
|
+
*
|
|
7
|
+
* plunge refuses a stock checkpoint with no records (`AdoptionRequiredError`)
|
|
8
|
+
* rather than recreate everything, so a stack moves to plunge by `plunge
|
|
9
|
+
* stack adopt`. Adoption writes one record per resource with the
|
|
10
|
+
* checkpoint's IDs and calls no cloud API; the next `plunge up` adds the
|
|
11
|
+
* `plunge-stack` and `plunge-urn` tags in place.
|
|
12
|
+
*
|
|
13
|
+
* Phases:
|
|
14
|
+
*
|
|
15
|
+
* - **Check**: the shared bucket check (#4558) on each stack's backend
|
|
16
|
+
* bucket. A setting that fails, not waived, refuses the run before
|
|
17
|
+
* anything is exported, naming the fix. A `file://` backend is skipped.
|
|
18
|
+
* - **Export**: each stack's stock checkpoint, `pulumi stack export
|
|
19
|
+
* --stack <s>`, kept in a scratch file. It is the back-out copy (`pulumi
|
|
20
|
+
* stack import --file` on a stock backend), and on the route from Pulumi
|
|
21
|
+
* Cloud (`from: { backend }`) or from an export taken by hand (`from: {
|
|
22
|
+
* file }`) it is what plunge adopts with `--file`.
|
|
23
|
+
* - Then one wave per stack, in the order given: **Preview** runs `plunge
|
|
24
|
+
* stack adopt --json` (writes nothing), the **gate** binds that preview's
|
|
25
|
+
* `planDigest`, and **Adopt** runs `plunge stack adopt --json --yes`. A
|
|
26
|
+
* stack whose checkpoint changed after approval previews a new digest,
|
|
27
|
+
* and the old approval does not open the gate for it.
|
|
28
|
+
*
|
|
29
|
+
* Every gate is a fact on the workspace ledger: `chant run` records the
|
|
30
|
+
* pending gate citing the preview's digest and exits gated, `chant approve
|
|
31
|
+
* <op> <gate>` writes the resolution, and the next run walks to the Adopt
|
|
32
|
+
* step. The run itself is a run record on the same ledger (#4551), as for
|
|
33
|
+
* `TerraformAdoptOp`. There is no `gate: "never"`: adoption moves the stack's
|
|
34
|
+
* state onto another backend's records, which nobody should skip reviewing.
|
|
35
|
+
*
|
|
36
|
+
* ## Compensation
|
|
37
|
+
*
|
|
38
|
+
* Refused without a command, as `TerraformAdoptOp` refuses it. Backing out
|
|
39
|
+
* is `plunge stack export --file s.json` then `pulumi stack import --file
|
|
40
|
+
* s.json` on a stock backend, a decision about the stack and its backend
|
|
41
|
+
* rather than something chant should run on its own.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* import { PulumiAdoptOp } from "@intentius/chant-lexicon-pulumi";
|
|
46
|
+
*
|
|
47
|
+
* export const { op } = PulumiAdoptOp({
|
|
48
|
+
* name: "adopt-infra",
|
|
49
|
+
* stacks: [{ member: "infra", dir: "./infra", stack: "dev" }],
|
|
50
|
+
* });
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
import { Op, phase, gate, activity } from "@intentius/chant/op";
|
|
54
|
+
import { pulumiAdopt, pulumiAdoptCheck, pulumiAdoptExport, pulumiAdoptPreview } from "../op/builders.js";
|
|
55
|
+
const slug = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
56
|
+
export function PulumiAdoptOp(config) {
|
|
57
|
+
const compensateCommand = typeof config.compensate === "object" ? config.compensate.command : undefined;
|
|
58
|
+
const compensateImage = typeof config.compensate === "object" ? config.compensate.image : undefined;
|
|
59
|
+
if (config.compensate !== undefined && config.compensate !== false && compensateCommand === undefined) {
|
|
60
|
+
throw new Error(`PulumiAdoptOp "${config.name}": compensate is enabled, but there is no automatic undo for an adoption. ` +
|
|
61
|
+
`Backing out is plunge stack export --file, then pulumi stack import --file on a stock backend, a decision ` +
|
|
62
|
+
`about the stack and its backend. Supply compensate: { command: "..." } with an undo of your own, or set ` +
|
|
63
|
+
`compensate: false.`);
|
|
64
|
+
}
|
|
65
|
+
if (!Array.isArray(config.stacks) || config.stacks.length === 0) {
|
|
66
|
+
throw new Error(`PulumiAdoptOp "${config.name}": stacks is empty; name at least one { member, dir, stack } to adopt`);
|
|
67
|
+
}
|
|
68
|
+
const seen = new Set();
|
|
69
|
+
for (const s of config.stacks) {
|
|
70
|
+
const key = `${s.member}/${s.stack}`;
|
|
71
|
+
if (seen.has(key))
|
|
72
|
+
throw new Error(`PulumiAdoptOp "${config.name}": ${key} is listed twice`);
|
|
73
|
+
seen.add(key);
|
|
74
|
+
}
|
|
75
|
+
const where = config.cwd ? { cwd: config.cwd } : {};
|
|
76
|
+
const single = config.stacks.length === 1;
|
|
77
|
+
const gateBase = config.gateName ?? `approve-${config.name}`;
|
|
78
|
+
const checks = config.stacks.map((s, i) => pulumiAdoptCheck(s.member, {
|
|
79
|
+
dir: s.dir,
|
|
80
|
+
stack: s.stack,
|
|
81
|
+
...where,
|
|
82
|
+
...(s.backend ? { backend: s.backend } : {}),
|
|
83
|
+
...(s.bucket ? { bucket: s.bucket } : {}),
|
|
84
|
+
...(s.namespaces ? { namespaces: s.namespaces } : {}),
|
|
85
|
+
...(s.allowInsecure ? { allowInsecure: s.allowInsecure } : {}),
|
|
86
|
+
id: `check-${i}`,
|
|
87
|
+
}));
|
|
88
|
+
const exports = config.stacks.map((s, i) => pulumiAdoptExport(s.member, {
|
|
89
|
+
dir: s.dir,
|
|
90
|
+
stack: s.stack,
|
|
91
|
+
...where,
|
|
92
|
+
...(s.backend ? { backend: s.backend } : {}),
|
|
93
|
+
...(s.from ? { from: s.from } : {}),
|
|
94
|
+
id: `export-${i}`,
|
|
95
|
+
profile: "longInfra",
|
|
96
|
+
}));
|
|
97
|
+
const waves = config.stacks.flatMap((s, i) => {
|
|
98
|
+
const file = s.from ? { file: exports[i].out.file } : {};
|
|
99
|
+
const preview = pulumiAdoptPreview(s.member, { dir: s.dir, stack: s.stack, ...where, ...file, id: `preview-${i}` });
|
|
100
|
+
preview.outcomeAttribute = { name: single ? "Adoptable" : `Adoptable-${slug(s.member)}-${slug(s.stack)}`, from: "resources" };
|
|
101
|
+
const adopt = pulumiAdopt(s.member, { dir: s.dir, stack: s.stack, ...where, ...file, planDigest: preview.out.planDigest, id: `adopt-${i}` });
|
|
102
|
+
adopt.outcomeAttribute = { name: single ? "Adopted" : `Adopted-${slug(s.member)}-${slug(s.stack)}`, from: "resources" };
|
|
103
|
+
const label = single ? "" : ` ${s.member}/${s.stack}`;
|
|
104
|
+
return [
|
|
105
|
+
phase(`Preview${label}`, [preview]),
|
|
106
|
+
phase(`Gate${label}`, [
|
|
107
|
+
gate(single ? gateBase : `${gateBase}-${slug(s.member)}-${slug(s.stack)}`, {
|
|
108
|
+
plan: preview.out.planDigest,
|
|
109
|
+
...(config.gateTimeout ? { timeout: config.gateTimeout } : {}),
|
|
110
|
+
description: config.gateDescription ??
|
|
111
|
+
`Approve adopting the Pulumi stack ${s.member}/${s.stack} onto plunge's per-resource records. The ` +
|
|
112
|
+
`preview's adopt document is what is approved: each resource gets a record with the checkpoint's ID, ` +
|
|
113
|
+
`no cloud object changes now, and the next plunge up writes the plunge-stack and plunge-urn tags.`,
|
|
114
|
+
}),
|
|
115
|
+
]),
|
|
116
|
+
phase(`Adopt${label}`, [adopt]),
|
|
117
|
+
];
|
|
118
|
+
});
|
|
119
|
+
const op = Op({
|
|
120
|
+
name: config.name,
|
|
121
|
+
overview: `Adopt ${single ? `the Pulumi stack ${config.stacks[0].member}/${config.stacks[0].stack}` : `${config.stacks.length} Pulumi stacks`} onto plunge's per-resource records`,
|
|
122
|
+
labels: {
|
|
123
|
+
Adopt: "true",
|
|
124
|
+
Surface: "pulumi-adopt",
|
|
125
|
+
},
|
|
126
|
+
phases: [phase("Check", checks), phase("Export", exports), ...waves],
|
|
127
|
+
...(compensateCommand
|
|
128
|
+
? { onFailure: [phase("Rollback", [activity("shellCmd", { cmd: compensateCommand, ...(compensateImage ? { image: compensateImage } : {}) })])] }
|
|
129
|
+
: {}),
|
|
130
|
+
});
|
|
131
|
+
return { op };
|
|
132
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The engine event stream of `pulumi up --json`, and of `pulumi preview
|
|
3
|
+
* --json` run with `PULUMI_ENABLE_STREAMING_JSON_PREVIEW=true` (#4541,
|
|
4
|
+
* ruling C): one `apitype.EngineEvent` per line, the same shape for stock
|
|
5
|
+
* Pulumi and plunge (`sdk/go/common/apitype/events.go`).
|
|
6
|
+
*
|
|
7
|
+
* The printed preview document drops policy events
|
|
8
|
+
* (`pkg/backend/display/json.go` `ShowPreviewDigest`), so the pulumi-up
|
|
9
|
+
* capability previews with the stream instead, and reads both the change set
|
|
10
|
+
* and CrossGuard's verdict from one engine run:
|
|
11
|
+
*
|
|
12
|
+
* - {@link pulumiStreamPreview}: the stream turned back into the document
|
|
13
|
+
* `pulumiPreviewPart` and `pulumiPreviewDigest` read (`./change-set.ts`).
|
|
14
|
+
* Steps come from `resourcePreEvent`, filtered as the printed document
|
|
15
|
+
* filters them (`shouldShow`: a non-logical replacement step is left out,
|
|
16
|
+
* and `same` steps are kept, since chant always passes `--show-sames`).
|
|
17
|
+
* Each state is normalized to the printed form: a blinded secret envelope
|
|
18
|
+
* becomes `"[secret]"`, an empty `inputs` or `outputs` map and an empty
|
|
19
|
+
* `id` are left out, and `detailedDiff`'s `diffKind` is read as `kind`.
|
|
20
|
+
* The digest of the result equals the digest of the printed document for
|
|
21
|
+
* the same preview (checked against `testdata/pulumi-stream`).
|
|
22
|
+
* - {@link pulumiPolicyEvents} and {@link pulumiPolicyFindings}: the
|
|
23
|
+
* `policyEvent` lines, as `@intentius/chant/gate-policy` findings. A
|
|
24
|
+
* `mandatory` violation denies; `advisory` ones are warnings; `disabled`
|
|
25
|
+
* ones are dropped.
|
|
26
|
+
* - {@link pulumiStreamSummary}: the `summaryEvent`, and plunge's
|
|
27
|
+
* per-resource timings (INTENTIUS/plunge#55) when the steps carry them.
|
|
28
|
+
* - {@link pulumiStreamTimings}: those timings alone, by URN. plunge puts
|
|
29
|
+
* `metadata.plungeTimings` on each `resOutputsEvent` and
|
|
30
|
+
* `resOpFailedEvent` of `up --json`: one entry per provider call (`kind:
|
|
31
|
+
* "provider"`, `call` one of check, diff, create, read, update, delete)
|
|
32
|
+
* and per write or delete of the resource's record (`kind: "record"`),
|
|
33
|
+
* each with `startedAt` and `durationMs`, and `failed` when it failed.
|
|
34
|
+
* Every call is on exactly one event, and a replacement's create and
|
|
35
|
+
* delete steps split a URN's calls over two events, so the reader joins
|
|
36
|
+
* them. Stock Pulumi prints no such field, and the reader then finds none.
|
|
37
|
+
*
|
|
38
|
+
* During `up`, the engine may first preview: those `resourcePreEvent`s carry
|
|
39
|
+
* `planning: true`. {@link pulumiAppliedSteps} keeps only the steps the update
|
|
40
|
+
* ran, which is what the stock apply compares with the approved shape
|
|
41
|
+
* (ruling A).
|
|
42
|
+
*
|
|
43
|
+
* Like `./change-set.ts`, this module imports only `./values` and types, so it
|
|
44
|
+
* bundles without node's process modules.
|
|
45
|
+
*/
|
|
46
|
+
import type { GatePolicyFindings } from "@intentius/chant/gate-policy";
|
|
47
|
+
import type { PulumiStep } from "./change-set.js";
|
|
48
|
+
import { type Json } from "./values.js";
|
|
49
|
+
/** The env var that makes `preview --json` stream engine events instead of printing one document (`sdk/go/common/env/env.go`). */
|
|
50
|
+
export declare const PULUMI_STREAMING_PREVIEW_ENV = "PULUMI_ENABLE_STREAMING_JSON_PREVIEW";
|
|
51
|
+
/** One line of the stream, as far as this module reads it. */
|
|
52
|
+
export interface PulumiEngineEvent {
|
|
53
|
+
sequence?: number;
|
|
54
|
+
timestamp?: number;
|
|
55
|
+
resourcePreEvent?: {
|
|
56
|
+
metadata?: PulumiEventStep;
|
|
57
|
+
planning?: boolean;
|
|
58
|
+
};
|
|
59
|
+
resOutputsEvent?: {
|
|
60
|
+
metadata?: PulumiEventStep;
|
|
61
|
+
planning?: boolean;
|
|
62
|
+
};
|
|
63
|
+
resOpFailedEvent?: {
|
|
64
|
+
metadata?: PulumiEventStep;
|
|
65
|
+
status?: number;
|
|
66
|
+
steps?: number;
|
|
67
|
+
};
|
|
68
|
+
policyEvent?: PulumiPolicyEvent;
|
|
69
|
+
summaryEvent?: PulumiSummaryEvent;
|
|
70
|
+
diagnosticEvent?: {
|
|
71
|
+
urn?: string;
|
|
72
|
+
message?: string;
|
|
73
|
+
severity?: string;
|
|
74
|
+
ephemeral?: boolean;
|
|
75
|
+
};
|
|
76
|
+
preludeEvent?: {
|
|
77
|
+
config?: Record<string, string>;
|
|
78
|
+
};
|
|
79
|
+
cancelEvent?: Record<string, never>;
|
|
80
|
+
errorEvent?: {
|
|
81
|
+
error?: string;
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/** `apitype.StepEventMetadata`. */
|
|
85
|
+
export interface PulumiEventStep {
|
|
86
|
+
op?: string;
|
|
87
|
+
urn?: string;
|
|
88
|
+
type?: string;
|
|
89
|
+
old?: PulumiEventState | null;
|
|
90
|
+
new?: PulumiEventState | null;
|
|
91
|
+
keys?: string[];
|
|
92
|
+
diffs?: string[];
|
|
93
|
+
detailedDiff?: Record<string, {
|
|
94
|
+
diffKind?: string;
|
|
95
|
+
inputDiff?: boolean;
|
|
96
|
+
}> | null;
|
|
97
|
+
logical?: boolean;
|
|
98
|
+
provider?: string;
|
|
99
|
+
/** plunge's per-resource timings (INTENTIUS/plunge#55), when the fork prints them. Read with {@link plungeTimingOf}. */
|
|
100
|
+
plungeTimings?: unknown[];
|
|
101
|
+
}
|
|
102
|
+
/** What a plunge timing times: a provider call, or a write of the resource's own record. */
|
|
103
|
+
export type PlungeTimingKind = "provider" | "record";
|
|
104
|
+
/** The calls plunge times (INTENTIUS/plunge#55), by kind. */
|
|
105
|
+
export declare const PLUNGE_TIMING_CALLS: Readonly<Record<PlungeTimingKind, readonly string[]>>;
|
|
106
|
+
/** One entry of `metadata.plungeTimings` (INTENTIUS/plunge#55). */
|
|
107
|
+
export interface PlungeTiming {
|
|
108
|
+
kind: PlungeTimingKind;
|
|
109
|
+
/** For `provider`: check, diff, create, read, update or delete. For `record`: write or delete. */
|
|
110
|
+
call: string;
|
|
111
|
+
/** RFC 3339, to the nanosecond. */
|
|
112
|
+
startedAt: string;
|
|
113
|
+
/** Fractional milliseconds. */
|
|
114
|
+
durationMs: number;
|
|
115
|
+
/** Set when the call failed. A conditional record write that lost to another run counts as failed. */
|
|
116
|
+
failed?: boolean;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* `value` as a {@link PlungeTiming}, or undefined when it is not one. A
|
|
120
|
+
* `call` outside the kind's list is kept, so a newer plunge's calls still
|
|
121
|
+
* read; an entry with no kind, no start or a negative duration is dropped.
|
|
122
|
+
*/
|
|
123
|
+
export declare function plungeTimingOf(value: unknown): PlungeTiming | undefined;
|
|
124
|
+
/** `apitype.StepEventStateMetadata`. */
|
|
125
|
+
export interface PulumiEventState {
|
|
126
|
+
type?: string;
|
|
127
|
+
urn?: string;
|
|
128
|
+
custom?: boolean;
|
|
129
|
+
delete?: boolean;
|
|
130
|
+
id?: string;
|
|
131
|
+
parent?: string;
|
|
132
|
+
provider?: string;
|
|
133
|
+
inputs?: Json | null;
|
|
134
|
+
outputs?: Json | null;
|
|
135
|
+
}
|
|
136
|
+
/** `apitype.PolicyEvent`. */
|
|
137
|
+
export interface PulumiPolicyEvent {
|
|
138
|
+
resourceUrn?: string;
|
|
139
|
+
message?: string;
|
|
140
|
+
policyName?: string;
|
|
141
|
+
policyPackName?: string;
|
|
142
|
+
policyPackVersion?: string;
|
|
143
|
+
/** `advisory`, `mandatory`, `remediate` or `disabled`. */
|
|
144
|
+
enforcementLevel?: string;
|
|
145
|
+
severity?: string;
|
|
146
|
+
}
|
|
147
|
+
/** `apitype.SummaryEvent`. */
|
|
148
|
+
export interface PulumiSummaryEvent {
|
|
149
|
+
maybeCorrupt?: boolean;
|
|
150
|
+
durationSeconds?: number;
|
|
151
|
+
resourceChanges?: Record<string, number>;
|
|
152
|
+
isPreview?: boolean;
|
|
153
|
+
result?: string;
|
|
154
|
+
PolicyPacks?: Record<string, string>;
|
|
155
|
+
}
|
|
156
|
+
/** Every line of `stream` that parses as a JSON object. Other lines (a banner, a progress line) are skipped. */
|
|
157
|
+
export declare function pulumiEngineEvents(stream: string): PulumiEngineEvent[];
|
|
158
|
+
/** One event step as a printed preview step (`getPreviewMetadataStep`). */
|
|
159
|
+
export declare function pulumiStepOfEvent(m: PulumiEventStep): PulumiStep;
|
|
160
|
+
/**
|
|
161
|
+
* A streamed preview as the document `pulumi preview --json` prints without
|
|
162
|
+
* the env var: `steps`, `diagnostics` and, from the summary, `changeSummary`.
|
|
163
|
+
* What `pulumiPreviewPart` and `pulumiPreviewDigest` read.
|
|
164
|
+
*/
|
|
165
|
+
export declare function pulumiStreamPreview(stream: string | readonly PulumiEngineEvent[]): {
|
|
166
|
+
steps: PulumiStep[];
|
|
167
|
+
diagnostics: Array<{
|
|
168
|
+
urn?: string;
|
|
169
|
+
message: string;
|
|
170
|
+
severity: string;
|
|
171
|
+
}>;
|
|
172
|
+
changeSummary?: Record<string, number>;
|
|
173
|
+
};
|
|
174
|
+
/** The steps an `up` ran: its `resourcePreEvent`s outside any preview phase (`planning` unset). */
|
|
175
|
+
export declare function pulumiAppliedSteps(stream: string | readonly PulumiEngineEvent[]): PulumiStep[];
|
|
176
|
+
/** The URNs whose operation failed during an `up` (`resOpFailedEvent`), sorted. */
|
|
177
|
+
export declare function pulumiFailedUrns(stream: string | readonly PulumiEngineEvent[]): string[];
|
|
178
|
+
/** The URNs an `up` finished an operation on (`resOutputsEvent` outside a preview phase, `same` left out), sorted. */
|
|
179
|
+
export declare function pulumiLandedUrns(stream: string | readonly PulumiEngineEvent[]): string[];
|
|
180
|
+
/** A CrossGuard violation the stream reported. */
|
|
181
|
+
export interface PulumiPolicyViolation {
|
|
182
|
+
pack: string;
|
|
183
|
+
packVersion?: string;
|
|
184
|
+
policy: string;
|
|
185
|
+
enforcementLevel: string;
|
|
186
|
+
message: string;
|
|
187
|
+
urn?: string;
|
|
188
|
+
severity?: string;
|
|
189
|
+
}
|
|
190
|
+
/** The `policyEvent` lines of a stream, `disabled` ones left out. */
|
|
191
|
+
export declare function pulumiPolicyEvents(stream: string | readonly PulumiEngineEvent[]): PulumiPolicyViolation[];
|
|
192
|
+
/** The rule id a CrossGuard policy is named by in chant's gate and overrides: `crossguard.<pack>.<policy>`. */
|
|
193
|
+
export declare function crossGuardRule(v: Pick<PulumiPolicyViolation, "pack" | "policy">): string;
|
|
194
|
+
/**
|
|
195
|
+
* CrossGuard's violations as gate findings (`@intentius/chant/gate-policy`):
|
|
196
|
+
* a `mandatory` one denies the plan, an `advisory` (or `remediate`) one is
|
|
197
|
+
* a warning. CrossGuard has no hold.
|
|
198
|
+
*/
|
|
199
|
+
export declare function pulumiPolicyFindings(violations: readonly PulumiPolicyViolation[]): GatePolicyFindings;
|
|
200
|
+
/** What an `up` reported at its end, for the step's output. */
|
|
201
|
+
export interface PulumiApplySummary {
|
|
202
|
+
/** Counts by op, `same` included, as the engine reports them. */
|
|
203
|
+
resourceChanges: Record<string, number>;
|
|
204
|
+
durationSeconds?: number;
|
|
205
|
+
/** `succeeded`, `failed` or `cancelled`, when the engine says. */
|
|
206
|
+
result?: string;
|
|
207
|
+
maybeCorrupt?: boolean;
|
|
208
|
+
/** plunge's per-resource timings (INTENTIUS/plunge#55), by URN, in stream order, when the steps carry them. */
|
|
209
|
+
timings?: Record<string, PlungeTiming[]>;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* plunge's per-resource timings in `up --json` (INTENTIUS/plunge#55), by URN:
|
|
213
|
+
* every valid entry of `metadata.plungeTimings` on each `resOutputsEvent`
|
|
214
|
+
* and `resOpFailedEvent`, joined across a URN's events in stream order. A
|
|
215
|
+
* stock Pulumi stream gives `{}`.
|
|
216
|
+
*/
|
|
217
|
+
export declare function pulumiStreamTimings(stream: string | readonly PulumiEngineEvent[]): Record<string, PlungeTiming[]>;
|
|
218
|
+
/** The stream's `summaryEvent`, with plunge's timings when present. Undefined when the stream has no summary. */
|
|
219
|
+
export declare function pulumiStreamSummary(stream: string | readonly PulumiEngineEvent[]): PulumiApplySummary | undefined;
|
|
220
|
+
/** The first error a stream reported (an error diagnostic or an `errorEvent`), if any. */
|
|
221
|
+
export declare function pulumiStreamError(stream: string | readonly PulumiEngineEvent[]): string | undefined;
|
|
222
|
+
//# sourceMappingURL=engine-events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine-events.d.ts","sourceRoot":"","sources":["../src/engine-events.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The engine event stream of `pulumi up --json`, and of `pulumi preview
|
|
3
|
+
* --json` run with `PULUMI_ENABLE_STREAMING_JSON_PREVIEW=true` (#4541,
|
|
4
|
+
* ruling C): one `apitype.EngineEvent` per line, the same shape for stock
|
|
5
|
+
* Pulumi and plunge (`sdk/go/common/apitype/events.go`).
|
|
6
|
+
*
|
|
7
|
+
* The printed preview document drops policy events
|
|
8
|
+
* (`pkg/backend/display/json.go` `ShowPreviewDigest`), so the pulumi-up
|
|
9
|
+
* capability previews with the stream instead, and reads both the change set
|
|
10
|
+
* and CrossGuard's verdict from one engine run:
|
|
11
|
+
*
|
|
12
|
+
* - {@link pulumiStreamPreview}: the stream turned back into the document
|
|
13
|
+
* `pulumiPreviewPart` and `pulumiPreviewDigest` read (`./change-set.ts`).
|
|
14
|
+
* Steps come from `resourcePreEvent`, filtered as the printed document
|
|
15
|
+
* filters them (`shouldShow`: a non-logical replacement step is left out,
|
|
16
|
+
* and `same` steps are kept, since chant always passes `--show-sames`).
|
|
17
|
+
* Each state is normalized to the printed form: a blinded secret envelope
|
|
18
|
+
* becomes `"[secret]"`, an empty `inputs` or `outputs` map and an empty
|
|
19
|
+
* `id` are left out, and `detailedDiff`'s `diffKind` is read as `kind`.
|
|
20
|
+
* The digest of the result equals the digest of the printed document for
|
|
21
|
+
* the same preview (checked against `testdata/pulumi-stream`).
|
|
22
|
+
* - {@link pulumiPolicyEvents} and {@link pulumiPolicyFindings}: the
|
|
23
|
+
* `policyEvent` lines, as `@intentius/chant/gate-policy` findings. A
|
|
24
|
+
* `mandatory` violation denies; `advisory` ones are warnings; `disabled`
|
|
25
|
+
* ones are dropped.
|
|
26
|
+
* - {@link pulumiStreamSummary}: the `summaryEvent`, and plunge's
|
|
27
|
+
* per-resource timings (INTENTIUS/plunge#55) when the steps carry them.
|
|
28
|
+
* - {@link pulumiStreamTimings}: those timings alone, by URN. plunge puts
|
|
29
|
+
* `metadata.plungeTimings` on each `resOutputsEvent` and
|
|
30
|
+
* `resOpFailedEvent` of `up --json`: one entry per provider call (`kind:
|
|
31
|
+
* "provider"`, `call` one of check, diff, create, read, update, delete)
|
|
32
|
+
* and per write or delete of the resource's record (`kind: "record"`),
|
|
33
|
+
* each with `startedAt` and `durationMs`, and `failed` when it failed.
|
|
34
|
+
* Every call is on exactly one event, and a replacement's create and
|
|
35
|
+
* delete steps split a URN's calls over two events, so the reader joins
|
|
36
|
+
* them. Stock Pulumi prints no such field, and the reader then finds none.
|
|
37
|
+
*
|
|
38
|
+
* During `up`, the engine may first preview: those `resourcePreEvent`s carry
|
|
39
|
+
* `planning: true`. {@link pulumiAppliedSteps} keeps only the steps the update
|
|
40
|
+
* ran, which is what the stock apply compares with the approved shape
|
|
41
|
+
* (ruling A).
|
|
42
|
+
*
|
|
43
|
+
* Like `./change-set.ts`, this module imports only `./values` and types, so it
|
|
44
|
+
* bundles without node's process modules.
|
|
45
|
+
*/
|
|
46
|
+
import { isObject, PULUMI_BLINDED_SECRET, PULUMI_SECRET_SIG, PULUMI_SIG_KEY } from "./values.js";
|
|
47
|
+
/** The env var that makes `preview --json` stream engine events instead of printing one document (`sdk/go/common/env/env.go`). */
|
|
48
|
+
export const PULUMI_STREAMING_PREVIEW_ENV = "PULUMI_ENABLE_STREAMING_JSON_PREVIEW";
|
|
49
|
+
/** The calls plunge times (INTENTIUS/plunge#55), by kind. */
|
|
50
|
+
export const PLUNGE_TIMING_CALLS = {
|
|
51
|
+
provider: ["check", "diff", "create", "read", "update", "delete"],
|
|
52
|
+
record: ["write", "delete"],
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* `value` as a {@link PlungeTiming}, or undefined when it is not one. A
|
|
56
|
+
* `call` outside the kind's list is kept, so a newer plunge's calls still
|
|
57
|
+
* read; an entry with no kind, no start or a negative duration is dropped.
|
|
58
|
+
*/
|
|
59
|
+
export function plungeTimingOf(value) {
|
|
60
|
+
if (!isObject(value))
|
|
61
|
+
return undefined;
|
|
62
|
+
const v = value;
|
|
63
|
+
if (v.kind !== "provider" && v.kind !== "record")
|
|
64
|
+
return undefined;
|
|
65
|
+
if (typeof v.call !== "string" || v.call === "")
|
|
66
|
+
return undefined;
|
|
67
|
+
if (typeof v.startedAt !== "string" || !Number.isFinite(Date.parse(v.startedAt)))
|
|
68
|
+
return undefined;
|
|
69
|
+
if (typeof v.durationMs !== "number" || !Number.isFinite(v.durationMs) || v.durationMs < 0)
|
|
70
|
+
return undefined;
|
|
71
|
+
return { kind: v.kind, call: v.call, startedAt: v.startedAt, durationMs: v.durationMs, ...(v.failed === true ? { failed: true } : {}) };
|
|
72
|
+
}
|
|
73
|
+
/** Every line of `stream` that parses as a JSON object. Other lines (a banner, a progress line) are skipped. */
|
|
74
|
+
export function pulumiEngineEvents(stream) {
|
|
75
|
+
const out = [];
|
|
76
|
+
for (const line of stream.split("\n")) {
|
|
77
|
+
const text = line.trim();
|
|
78
|
+
if (!text.startsWith("{"))
|
|
79
|
+
continue;
|
|
80
|
+
try {
|
|
81
|
+
const v = JSON.parse(text);
|
|
82
|
+
if (isObject(v))
|
|
83
|
+
out.push(v);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// A line that is not one event: the stream's events are one per line.
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
/** Ops that are part of a replacement (`deploy.IsReplacementStep`). */
|
|
92
|
+
const REPLACEMENT_OPS = new Set(["replace", "create-replacement", "delete-replaced", "read-replacement", "discard-replaced", "import-replacement"]);
|
|
93
|
+
/** The printed document's filter (`shouldShow`), with `--show-sames` on: a non-logical replacement step is left out. */
|
|
94
|
+
function shown(m) {
|
|
95
|
+
return !(m.logical !== true && REPLACEMENT_OPS.has(m.op ?? ""));
|
|
96
|
+
}
|
|
97
|
+
/** A value with every blinded secret envelope read as the printed document's `"[secret]"`. */
|
|
98
|
+
function blinded(v) {
|
|
99
|
+
if (Array.isArray(v))
|
|
100
|
+
return v.map(blinded);
|
|
101
|
+
if (!isObject(v))
|
|
102
|
+
return v;
|
|
103
|
+
if (v[PULUMI_SIG_KEY] === PULUMI_SECRET_SIG)
|
|
104
|
+
return PULUMI_BLINDED_SECRET;
|
|
105
|
+
return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, blinded(x)]));
|
|
106
|
+
}
|
|
107
|
+
/** A step state in the printed document's form (`apitype.ResourceV3`, empty fields omitted). */
|
|
108
|
+
function stateOf(s) {
|
|
109
|
+
if (!s || typeof s !== "object" || Array.isArray(s))
|
|
110
|
+
return undefined;
|
|
111
|
+
const map = (m) => (isObject(m) && Object.keys(m).length > 0 ? blinded(m) : undefined);
|
|
112
|
+
const inputs = map(s.inputs);
|
|
113
|
+
const outputs = map(s.outputs);
|
|
114
|
+
return {
|
|
115
|
+
...(s.urn ? { urn: s.urn } : {}),
|
|
116
|
+
...(typeof s.custom === "boolean" ? { custom: s.custom } : {}),
|
|
117
|
+
...(s.delete ? { delete: true } : {}),
|
|
118
|
+
...(s.id ? { id: s.id } : {}),
|
|
119
|
+
...(s.type ? { type: s.type } : {}),
|
|
120
|
+
...(s.parent ? { parent: s.parent } : {}),
|
|
121
|
+
...(inputs ? { inputs } : {}),
|
|
122
|
+
...(outputs ? { outputs } : {}),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/** One event step as a printed preview step (`getPreviewMetadataStep`). */
|
|
126
|
+
export function pulumiStepOfEvent(m) {
|
|
127
|
+
const oldState = stateOf(m.old);
|
|
128
|
+
const newState = stateOf(m.new);
|
|
129
|
+
const detailedDiff = isObject(m.detailedDiff)
|
|
130
|
+
? Object.fromEntries(Object.entries(m.detailedDiff).map(([k, d]) => [k, { ...(typeof d?.diffKind === "string" ? { kind: d.diffKind } : {}), ...(typeof d?.inputDiff === "boolean" ? { inputDiff: d.inputDiff } : {}) }]))
|
|
131
|
+
: undefined;
|
|
132
|
+
return {
|
|
133
|
+
...(m.op ? { op: m.op } : {}),
|
|
134
|
+
...(m.urn ? { urn: m.urn } : {}),
|
|
135
|
+
...(m.provider ? { provider: m.provider } : {}),
|
|
136
|
+
...(oldState ? { oldState } : {}),
|
|
137
|
+
...(newState ? { newState } : {}),
|
|
138
|
+
...(m.diffs?.length ? { diffReasons: [...m.diffs] } : {}),
|
|
139
|
+
...(m.keys?.length ? { replaceReasons: [...m.keys] } : {}),
|
|
140
|
+
...(detailedDiff ? { detailedDiff } : {}),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function stepsOf(events, keep) {
|
|
144
|
+
const out = [];
|
|
145
|
+
for (const e of events) {
|
|
146
|
+
const pre = e.resourcePreEvent;
|
|
147
|
+
if (!pre || !isObject(pre.metadata) || typeof pre.metadata.urn !== "string")
|
|
148
|
+
continue;
|
|
149
|
+
if (!keep(pre) || !shown(pre.metadata))
|
|
150
|
+
continue;
|
|
151
|
+
out.push(pulumiStepOfEvent(pre.metadata));
|
|
152
|
+
}
|
|
153
|
+
return out;
|
|
154
|
+
}
|
|
155
|
+
/** The diagnostics a stream printed, as the printed document lists them (ephemeral and debug ones left out). */
|
|
156
|
+
function diagnosticsOf(events) {
|
|
157
|
+
return events
|
|
158
|
+
.map((e) => e.diagnosticEvent)
|
|
159
|
+
.filter((d) => isObject(d) && d.ephemeral !== true && d.severity !== "debug" && typeof d.message === "string")
|
|
160
|
+
.map((d) => ({ ...(d.urn ? { urn: d.urn } : {}), message: d.message, severity: d.severity ?? "info" }));
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* A streamed preview as the document `pulumi preview --json` prints without
|
|
164
|
+
* the env var: `steps`, `diagnostics` and, from the summary, `changeSummary`.
|
|
165
|
+
* What `pulumiPreviewPart` and `pulumiPreviewDigest` read.
|
|
166
|
+
*/
|
|
167
|
+
export function pulumiStreamPreview(stream) {
|
|
168
|
+
const events = typeof stream === "string" ? pulumiEngineEvents(stream) : stream;
|
|
169
|
+
const summary = events.map((e) => e.summaryEvent).find((x) => isObject(x));
|
|
170
|
+
return {
|
|
171
|
+
steps: stepsOf(events, () => true),
|
|
172
|
+
diagnostics: diagnosticsOf(events),
|
|
173
|
+
...(summary?.resourceChanges ? { changeSummary: summary.resourceChanges } : {}),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
/** The steps an `up` ran: its `resourcePreEvent`s outside any preview phase (`planning` unset). */
|
|
177
|
+
export function pulumiAppliedSteps(stream) {
|
|
178
|
+
const events = typeof stream === "string" ? pulumiEngineEvents(stream) : stream;
|
|
179
|
+
return stepsOf(events, (e) => e.planning !== true);
|
|
180
|
+
}
|
|
181
|
+
/** The URNs whose operation failed during an `up` (`resOpFailedEvent`), sorted. */
|
|
182
|
+
export function pulumiFailedUrns(stream) {
|
|
183
|
+
const events = typeof stream === "string" ? pulumiEngineEvents(stream) : stream;
|
|
184
|
+
const urns = events.map((e) => e.resOpFailedEvent?.metadata?.urn).filter((u) => typeof u === "string");
|
|
185
|
+
return [...new Set(urns)].sort();
|
|
186
|
+
}
|
|
187
|
+
/** The URNs an `up` finished an operation on (`resOutputsEvent` outside a preview phase, `same` left out), sorted. */
|
|
188
|
+
export function pulumiLandedUrns(stream) {
|
|
189
|
+
const events = typeof stream === "string" ? pulumiEngineEvents(stream) : stream;
|
|
190
|
+
const urns = events
|
|
191
|
+
.map((e) => e.resOutputsEvent)
|
|
192
|
+
.filter((o) => isObject(o) && o.planning !== true && o.metadata?.op !== "same")
|
|
193
|
+
.map((o) => o.metadata?.urn)
|
|
194
|
+
.filter((u) => typeof u === "string");
|
|
195
|
+
return [...new Set(urns)].sort();
|
|
196
|
+
}
|
|
197
|
+
/** The `policyEvent` lines of a stream, `disabled` ones left out. */
|
|
198
|
+
export function pulumiPolicyEvents(stream) {
|
|
199
|
+
const events = typeof stream === "string" ? pulumiEngineEvents(stream) : stream;
|
|
200
|
+
const out = [];
|
|
201
|
+
for (const e of events) {
|
|
202
|
+
const p = e.policyEvent;
|
|
203
|
+
if (!p || typeof p !== "object" || p.enforcementLevel === "disabled")
|
|
204
|
+
continue;
|
|
205
|
+
out.push({
|
|
206
|
+
pack: p.policyPackName ?? "",
|
|
207
|
+
...(p.policyPackVersion ? { packVersion: p.policyPackVersion } : {}),
|
|
208
|
+
policy: p.policyName ?? "",
|
|
209
|
+
enforcementLevel: p.enforcementLevel ?? "advisory",
|
|
210
|
+
message: (p.message ?? "").trim(),
|
|
211
|
+
...(p.resourceUrn ? { urn: p.resourceUrn } : {}),
|
|
212
|
+
...(p.severity ? { severity: p.severity } : {}),
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
return out;
|
|
216
|
+
}
|
|
217
|
+
/** The rule id a CrossGuard policy is named by in chant's gate and overrides: `crossguard.<pack>.<policy>`. */
|
|
218
|
+
export function crossGuardRule(v) {
|
|
219
|
+
return `crossguard.${v.pack}.${v.policy}`;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* CrossGuard's violations as gate findings (`@intentius/chant/gate-policy`):
|
|
223
|
+
* a `mandatory` one denies the plan, an `advisory` (or `remediate`) one is
|
|
224
|
+
* a warning. CrossGuard has no hold.
|
|
225
|
+
*/
|
|
226
|
+
export function pulumiPolicyFindings(violations) {
|
|
227
|
+
const where = (v) => (v.urn ? ` (${v.urn})` : "");
|
|
228
|
+
return {
|
|
229
|
+
denials: violations.filter((v) => v.enforcementLevel === "mandatory").map((v) => ({ rule: crossGuardRule(v), message: `${v.message}${where(v)}` })),
|
|
230
|
+
holds: [],
|
|
231
|
+
warnings: violations.filter((v) => v.enforcementLevel !== "mandatory").map((v) => `${crossGuardRule(v)}: ${v.message}${where(v)}`),
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* plunge's per-resource timings in `up --json` (INTENTIUS/plunge#55), by URN:
|
|
236
|
+
* every valid entry of `metadata.plungeTimings` on each `resOutputsEvent`
|
|
237
|
+
* and `resOpFailedEvent`, joined across a URN's events in stream order. A
|
|
238
|
+
* stock Pulumi stream gives `{}`.
|
|
239
|
+
*/
|
|
240
|
+
export function pulumiStreamTimings(stream) {
|
|
241
|
+
var _a;
|
|
242
|
+
const events = typeof stream === "string" ? pulumiEngineEvents(stream) : stream;
|
|
243
|
+
const timings = {};
|
|
244
|
+
for (const e of events) {
|
|
245
|
+
for (const m of [e.resOutputsEvent?.metadata, e.resOpFailedEvent?.metadata]) {
|
|
246
|
+
if (!m?.urn || !Array.isArray(m.plungeTimings))
|
|
247
|
+
continue;
|
|
248
|
+
const read = m.plungeTimings.map(plungeTimingOf).filter((t) => t !== undefined);
|
|
249
|
+
if (read.length > 0)
|
|
250
|
+
(timings[_a = m.urn] ?? (timings[_a] = [])).push(...read);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return timings;
|
|
254
|
+
}
|
|
255
|
+
/** The stream's `summaryEvent`, with plunge's timings when present. Undefined when the stream has no summary. */
|
|
256
|
+
export function pulumiStreamSummary(stream) {
|
|
257
|
+
const events = typeof stream === "string" ? pulumiEngineEvents(stream) : stream;
|
|
258
|
+
const s = events.map((e) => e.summaryEvent).find((x) => isObject(x));
|
|
259
|
+
const timings = pulumiStreamTimings(events);
|
|
260
|
+
if (!s)
|
|
261
|
+
return undefined;
|
|
262
|
+
return {
|
|
263
|
+
resourceChanges: isObject(s.resourceChanges) ? s.resourceChanges : {},
|
|
264
|
+
...(typeof s.durationSeconds === "number" ? { durationSeconds: s.durationSeconds } : {}),
|
|
265
|
+
...(typeof s.result === "string" && s.result ? { result: s.result } : {}),
|
|
266
|
+
...(s.maybeCorrupt ? { maybeCorrupt: true } : {}),
|
|
267
|
+
...(Object.keys(timings).length > 0 ? { timings } : {}),
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
/** The first error a stream reported (an error diagnostic or an `errorEvent`), if any. */
|
|
271
|
+
export function pulumiStreamError(stream) {
|
|
272
|
+
const events = typeof stream === "string" ? pulumiEngineEvents(stream) : stream;
|
|
273
|
+
for (const e of events) {
|
|
274
|
+
if (typeof e.errorEvent?.error === "string" && e.errorEvent.error.trim())
|
|
275
|
+
return e.errorEvent.error.trim();
|
|
276
|
+
if (e.diagnosticEvent?.severity === "error" && typeof e.diagnosticEvent.message === "string")
|
|
277
|
+
return e.diagnosticEvent.message.trim();
|
|
278
|
+
}
|
|
279
|
+
return undefined;
|
|
280
|
+
}
|