@intentius/terragucci 0.4.3 → 0.4.4
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 +1 -0
- package/dist/audit.schema.json +3 -3
- package/dist/dora.schema.json +104 -0
- package/dist/estate.schema.json +89 -1
- package/dist/report-index.schema.json +11 -1
- package/dist/report.schema.json +99 -2
- package/dist/run.schema.json +88 -0
- package/dist/state-versions.schema.json +42 -0
- package/dist/terragucci.mjs +613 -432
- package/dist/terragucci.mjs.map +4 -4
- package/dist/types.d.ts +95 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,6 +23,7 @@ A root is a directory whose Terraform files declare a backend or configure a pro
|
|
|
23
23
|
|---|---|
|
|
24
24
|
| `terragucci init` | sets one repo up; `--forge` and `--binary` override detection, `--dry-run` writes nothing |
|
|
25
25
|
| `terragucci reconcile --config <file>` | from a control repo, previews every project's pipeline; `--mode apply` opens a pull request in each project that changes |
|
|
26
|
+
| `terragucci generate` | writes each root's backend, provider and version files from the `generate` key; `--check` fails on one that differs |
|
|
26
27
|
| `terragucci plan` | plans this repo's roots in apply order; `--root <glob>` narrows it |
|
|
27
28
|
|
|
28
29
|
| Exit code | Means |
|
package/dist/audit.schema.json
CHANGED
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://intentius.io/terragucci/schemas/audit/v1/audit.schema.json",
|
|
4
4
|
"title": "terragucci.audit/v1",
|
|
5
|
-
"description": "One line of audit.jsonl at the top of a reports prefix: an approval, apply, policy override or
|
|
5
|
+
"description": "One line of audit.jsonl at the top of a reports prefix: an approval, apply, policy override, refused wave or state migration. Within v1 fields are only added, and an existing field never changes meaning.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"required": ["schema", "id", "kind", "project", "at", "who", "what", "digest", "result", "evidence"],
|
|
8
8
|
"properties": {
|
|
9
9
|
"schema": { "const": "terragucci.audit/v1" },
|
|
10
10
|
"id": { "type": "string", "description": "sha256: over where the entry came from; the same records give the same id." },
|
|
11
|
-
"kind": { "enum": ["approval-requested", "approval", "approval-revoked", "override-requested", "override", "override-revoked", "apply", "refused"] },
|
|
11
|
+
"kind": { "enum": ["approval-requested", "approval", "approval-revoked", "override-requested", "override", "override-revoked", "apply", "refused", "migration", "unlock"] },
|
|
12
12
|
"project": { "type": "string", "description": "<host>/<path> of the repo." },
|
|
13
13
|
"at": { "type": "string", "description": "When it happened, ISO 8601." },
|
|
14
14
|
"who": { "type": ["string", "null"], "description": "The approver, the overrider, whoever removed the line, or for an apply the approver it applied under. Null when nobody did." },
|
|
15
|
-
"what": { "type": "string", "description": "The gate (wave-<k
|
|
15
|
+
"what": { "type": "string", "description": "The gate (wave-<k>, or a migration's name) or, for an override, the root." },
|
|
16
16
|
"digest": { "type": ["string", "null"], "description": "The wave's set digest, or the digest an override binds. Null when a root failed to plan." },
|
|
17
17
|
"result": { "type": "string" },
|
|
18
18
|
"evidence": {
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://intentius.io/terragucci/schemas/dora/v1/dora.schema.json",
|
|
4
|
+
"title": "terragucci.dora/v1",
|
|
5
|
+
"description": "dora.json at the top of a reports prefix, beside estate.json: the four DORA metrics per project and for the estate, which terragucci estate builds from audit.jsonl and each project's index.json. Within v1 fields are only added, and an existing field never changes meaning.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["schema", "generated", "weeks", "window", "drift_window_seconds", "audit", "estate", "projects"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"schema": { "const": "terragucci.dora/v1" },
|
|
10
|
+
"generated": { "type": "string", "description": "When the metrics were built, ISO 8601." },
|
|
11
|
+
"weeks": { "type": "integer", "description": "How many weeks the metrics cover, the current one included." },
|
|
12
|
+
"window": {
|
|
13
|
+
"type": "object",
|
|
14
|
+
"required": ["from", "to"],
|
|
15
|
+
"properties": {
|
|
16
|
+
"from": { "type": "string", "description": "The Monday, 00:00 UTC, the window starts on." },
|
|
17
|
+
"to": { "type": "string", "description": "When it ends: generated." }
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"drift_window_seconds": { "type": "integer", "description": "An applied wave whose roots drift within this long counts as a failed change." },
|
|
21
|
+
"audit": { "type": "boolean", "description": "Whether audit.jsonl was read. Without it no apply counts." },
|
|
22
|
+
"estate": { "$ref": "#/$defs/metrics" },
|
|
23
|
+
"projects": {
|
|
24
|
+
"type": "array",
|
|
25
|
+
"items": {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"required": ["project", "deployments", "per_week", "lead_time", "change_failure", "restore", "trend"],
|
|
28
|
+
"properties": {
|
|
29
|
+
"project": { "type": "string" },
|
|
30
|
+
"deployments": { "type": "integer" },
|
|
31
|
+
"per_week": { "type": "number" },
|
|
32
|
+
"lead_time": { "$ref": "#/$defs/lead_time" },
|
|
33
|
+
"change_failure": { "$ref": "#/$defs/change_failure" },
|
|
34
|
+
"restore": { "$ref": "#/$defs/restore" },
|
|
35
|
+
"trend": { "$ref": "#/$defs/trend" }
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"$defs": {
|
|
41
|
+
"seconds": { "type": ["integer", "null"], "description": "A median in seconds; null when nothing counts." },
|
|
42
|
+
"metrics": {
|
|
43
|
+
"type": "object",
|
|
44
|
+
"required": ["deployments", "per_week", "lead_time", "change_failure", "restore", "trend"],
|
|
45
|
+
"properties": {
|
|
46
|
+
"deployments": { "type": "integer", "description": "Applied waves in the window." },
|
|
47
|
+
"per_week": { "type": "number", "description": "Deployment frequency: applied waves per week over the window." },
|
|
48
|
+
"lead_time": { "$ref": "#/$defs/lead_time" },
|
|
49
|
+
"change_failure": { "$ref": "#/$defs/change_failure" },
|
|
50
|
+
"restore": { "$ref": "#/$defs/restore" },
|
|
51
|
+
"trend": { "$ref": "#/$defs/trend" }
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"lead_time": {
|
|
55
|
+
"type": "object",
|
|
56
|
+
"description": "From a change's first plan to its wave applied.",
|
|
57
|
+
"required": ["changes", "median_seconds", "before_gate_seconds", "at_gate_seconds", "after_gate_seconds"],
|
|
58
|
+
"properties": {
|
|
59
|
+
"changes": { "type": "integer", "description": "Applied waves whose first plan was found." },
|
|
60
|
+
"median_seconds": { "$ref": "#/$defs/seconds" },
|
|
61
|
+
"before_gate_seconds": { "$ref": "#/$defs/seconds" },
|
|
62
|
+
"at_gate_seconds": { "$ref": "#/$defs/seconds" },
|
|
63
|
+
"after_gate_seconds": { "$ref": "#/$defs/seconds" }
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"change_failure": {
|
|
67
|
+
"type": "object",
|
|
68
|
+
"required": ["applies", "failed", "drifted", "rate"],
|
|
69
|
+
"properties": {
|
|
70
|
+
"applies": { "type": "integer", "description": "Waves that applied or failed. A refused or waiting wave is not an apply." },
|
|
71
|
+
"failed": { "type": "integer" },
|
|
72
|
+
"drifted": { "type": "integer", "description": "Applied waves whose roots drifted within drift_window_seconds." },
|
|
73
|
+
"rate": { "type": ["number", "null"], "description": "(failed + drifted) / applies; null with no applies." }
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"restore": {
|
|
77
|
+
"type": "object",
|
|
78
|
+
"required": ["restored", "open", "median_seconds", "apply_restored", "drift_restored"],
|
|
79
|
+
"properties": {
|
|
80
|
+
"restored": { "type": "integer", "description": "Failed applies and drift restored in the window." },
|
|
81
|
+
"open": { "type": "integer", "description": "Failed roots no apply has restored yet, and drift no check has cleared." },
|
|
82
|
+
"median_seconds": { "$ref": "#/$defs/seconds" },
|
|
83
|
+
"apply_restored": { "type": "integer" },
|
|
84
|
+
"drift_restored": { "type": "integer" }
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"trend": {
|
|
88
|
+
"type": "array",
|
|
89
|
+
"description": "One row per week, oldest first.",
|
|
90
|
+
"items": {
|
|
91
|
+
"type": "object",
|
|
92
|
+
"required": ["week", "deployments", "applies", "failures", "lead_time_seconds", "restore_seconds"],
|
|
93
|
+
"properties": {
|
|
94
|
+
"week": { "type": "string", "description": "The Monday the week starts on, YYYY-MM-DD." },
|
|
95
|
+
"deployments": { "type": "integer" },
|
|
96
|
+
"applies": { "type": "integer" },
|
|
97
|
+
"failures": { "type": "integer", "description": "Failed applies and applied waves followed by drift." },
|
|
98
|
+
"lead_time_seconds": { "$ref": "#/$defs/seconds" },
|
|
99
|
+
"restore_seconds": { "$ref": "#/$defs/seconds" }
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
package/dist/estate.schema.json
CHANGED
|
@@ -43,9 +43,58 @@
|
|
|
43
43
|
"resources": { "type": "integer", "description": "Addresses with at least one apply." },
|
|
44
44
|
"generated": { "type": "string" }
|
|
45
45
|
}
|
|
46
|
+
},
|
|
47
|
+
"dora": {
|
|
48
|
+
"type": "object",
|
|
49
|
+
"description": "The DORA metrics beside the page (dora.json, terragucci.dora/v1).",
|
|
50
|
+
"required": ["file", "generated", "deployments"],
|
|
51
|
+
"properties": {
|
|
52
|
+
"file": { "type": "string" },
|
|
53
|
+
"generated": { "type": "string" },
|
|
54
|
+
"deployments": { "type": "integer", "description": "The estate's applied waves in the metrics' window." }
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"graph": {
|
|
58
|
+
"type": "object",
|
|
59
|
+
"description": "The dependency graph, from the run view of each project's newest applied commit: every root by wave, and an edge from each root to each root that reads its state through terraform_remote_state. An edge between two projects matched a read of a state outside the reader's project to the root of the other that holds it. Absent when no project has a run view.",
|
|
60
|
+
"required": ["nodes", "edges"],
|
|
61
|
+
"properties": {
|
|
62
|
+
"nodes": {
|
|
63
|
+
"type": "array",
|
|
64
|
+
"items": {
|
|
65
|
+
"type": "object",
|
|
66
|
+
"required": ["project", "root", "wave"],
|
|
67
|
+
"properties": {
|
|
68
|
+
"project": { "type": "string" },
|
|
69
|
+
"root": { "type": "string" },
|
|
70
|
+
"wave": { "type": "integer" }
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"edges": {
|
|
75
|
+
"type": "array",
|
|
76
|
+
"description": "Each edge: to reads the state of from.",
|
|
77
|
+
"items": {
|
|
78
|
+
"type": "object",
|
|
79
|
+
"required": ["from", "to"],
|
|
80
|
+
"properties": {
|
|
81
|
+
"from": { "$ref": "#/$defs/graphRoot" },
|
|
82
|
+
"to": { "$ref": "#/$defs/graphRoot" }
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
46
87
|
}
|
|
47
88
|
},
|
|
48
89
|
"$defs": {
|
|
90
|
+
"graphRoot": {
|
|
91
|
+
"type": "object",
|
|
92
|
+
"required": ["project", "root"],
|
|
93
|
+
"properties": {
|
|
94
|
+
"project": { "type": "string" },
|
|
95
|
+
"root": { "type": "string" }
|
|
96
|
+
}
|
|
97
|
+
},
|
|
49
98
|
"totals": {
|
|
50
99
|
"type": "object",
|
|
51
100
|
"required": ["create", "update", "replace", "delete"],
|
|
@@ -65,6 +114,7 @@
|
|
|
65
114
|
"commit": { "type": "string" },
|
|
66
115
|
"stage": { "enum": ["tf-plan", "tf-apply", "tf-drift"] },
|
|
67
116
|
"wave": { "type": "integer" },
|
|
117
|
+
"share": { "type": "integer", "description": "The share of a tf-apply wave split across jobs." },
|
|
68
118
|
"finished": { "type": "string" },
|
|
69
119
|
"roots": { "type": "integer" },
|
|
70
120
|
"changed": { "type": "integer" },
|
|
@@ -117,7 +167,45 @@
|
|
|
117
167
|
"drifted": { "type": "integer", "description": "Roots that drifted in the latest drift check." },
|
|
118
168
|
"failed": { "type": "integer", "description": "Roots that failed in the latest plan, drift check and apply waves." },
|
|
119
169
|
"overridden": { "type": "integer", "description": "Roots the newest commit's apply waves applied under a policy override. Absent when none." },
|
|
120
|
-
"inventory": { "$ref": "#/$defs/inventory", "description": "The resources its roots hold, from its inventory.json. Absent until an apply records them." }
|
|
170
|
+
"inventory": { "$ref": "#/$defs/inventory", "description": "The resources its roots hold, from its inventory.json. Absent until an apply records them." },
|
|
171
|
+
"states": { "type": "array", "description": "Each root's state versions, from its states.json. Absent until an apply records them.", "items": { "$ref": "#/$defs/stateRoot" } },
|
|
172
|
+
"run_view": {
|
|
173
|
+
"type": "object",
|
|
174
|
+
"description": "The run view of the project's newest applied commit, which the dependency graph read: its commit, when a wave last wrote it, and its run.html when the page can link it. Absent until a wave writes one.",
|
|
175
|
+
"required": ["commit", "updated"],
|
|
176
|
+
"properties": {
|
|
177
|
+
"commit": { "type": "string" },
|
|
178
|
+
"updated": { "type": "string" },
|
|
179
|
+
"page": { "type": "string" }
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
"stateRoot": {
|
|
185
|
+
"type": "object",
|
|
186
|
+
"description": "One root's state: where it is, whether its backend keeps versions, and the version ids its applies left, newest first. Never the state's contents.",
|
|
187
|
+
"required": ["root", "backend", "versioning", "checked", "versions"],
|
|
188
|
+
"properties": {
|
|
189
|
+
"root": { "type": "string" },
|
|
190
|
+
"backend": { "type": "string" },
|
|
191
|
+
"location": { "type": "string" },
|
|
192
|
+
"versioning": { "enum": ["on", "off", "unknown"] },
|
|
193
|
+
"note": { "type": "string" },
|
|
194
|
+
"checked": { "type": "string", "description": "When the newest apply that recorded the root finished." },
|
|
195
|
+
"versions": {
|
|
196
|
+
"type": "array",
|
|
197
|
+
"items": {
|
|
198
|
+
"type": "object",
|
|
199
|
+
"required": ["version_id", "commit", "finished"],
|
|
200
|
+
"properties": {
|
|
201
|
+
"version_id": { "type": "string" },
|
|
202
|
+
"commit": { "type": "string" },
|
|
203
|
+
"finished": { "type": "string" },
|
|
204
|
+
"wave": { "type": "integer" },
|
|
205
|
+
"report": { "type": "string", "description": "The wave's report.html, when the page can link it." }
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
121
209
|
}
|
|
122
210
|
},
|
|
123
211
|
"inventory": {
|
|
@@ -32,8 +32,9 @@
|
|
|
32
32
|
"commit": { "type": "string" },
|
|
33
33
|
"stage": { "enum": ["tf-plan", "tf-apply", "tf-drift"] },
|
|
34
34
|
"wave": { "type": "integer", "description": "The wave of a tf-apply run." },
|
|
35
|
+
"share": { "type": "integer", "description": "The share of a tf-apply wave split across jobs." },
|
|
35
36
|
"finished": { "type": "string", "description": "When the run finished, ISO 8601." },
|
|
36
|
-
"path": { "type": "string", "description": "The run's directory, relative to this index: <yyyy>/<mm>/<commit>/<stage>[-wave-N] in a project's index, prefixed with the project in the top index." },
|
|
37
|
+
"path": { "type": "string", "description": "The run's directory, relative to this index: <yyyy>/<mm>/<commit>/<stage>[-wave-N[-share-S]] in a project's index, prefixed with the project in the top index." },
|
|
37
38
|
"roots": { "type": "integer" },
|
|
38
39
|
"groups": { "type": "integer" },
|
|
39
40
|
"totals": { "$ref": "#/$defs/totals" },
|
|
@@ -44,6 +45,15 @@
|
|
|
44
45
|
"waiting_since": { "type": "string", "description": "When a waiting wave began waiting for an approval of its digest." },
|
|
45
46
|
"applied": { "type": "string", "description": "When a tf-apply wave finished applying." },
|
|
46
47
|
"overridden": { "type": "integer", "description": "Roots the policy denied that a recorded override let through. Absent when none." },
|
|
48
|
+
"wave_digests": { "type": "array", "items": { "type": "string" }, "description": "A tf-plan row: each wave's set digest and review digest, the digests an apply of the same plans binds. Absent when no wave has one." },
|
|
49
|
+
"drifted_roots": { "type": "array", "items": { "type": "string" }, "description": "A tf-drift row: the roots that drifted, at most 50. Absent when none drifted." },
|
|
50
|
+
"drift_since": { "type": "string", "description": "A tf-drift row that found drift: when the project's open drift was first found, by this run or an earlier one." },
|
|
51
|
+
"drift_cleared": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"description": "A tf-drift row that found none after a row that found some: when that drift was first found, and its roots.",
|
|
54
|
+
"required": ["since", "roots"],
|
|
55
|
+
"properties": { "since": { "type": "string" }, "roots": { "type": "array", "items": { "type": "string" } } }
|
|
56
|
+
},
|
|
47
57
|
"destroys": { "type": "array", "items": { "type": "string" }, "description": "Destroys and replacements as `<root>: <address>`, at most 50." },
|
|
48
58
|
"destroys_total": { "type": "integer", "description": "How many destroys and replacements there are, when there are more than the row lists." },
|
|
49
59
|
"commit_url": { "type": "string" },
|
package/dist/report.schema.json
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"base": { "type": "string" },
|
|
18
18
|
"stage": { "enum": ["tf-plan", "tf-apply", "tf-drift"] },
|
|
19
19
|
"wave": { "type": "integer" },
|
|
20
|
+
"share": { "type": "integer", "minimum": 1, "description": "On a tf-apply wave split across jobs (waves.jobs), the share this report applied; waves[0].roots are its roots (minor 20)." },
|
|
20
21
|
"binary": { "type": "string" },
|
|
21
22
|
"runtime": { "type": "string" },
|
|
22
23
|
"started": { "type": "string" },
|
|
@@ -70,6 +71,16 @@
|
|
|
70
71
|
"required": ["path", "status", "plan_digest", "counts", "plan", "changes", "highlights", "fold", "why"],
|
|
71
72
|
"properties": {
|
|
72
73
|
"path": { "type": "string" },
|
|
74
|
+
"binary": {
|
|
75
|
+
"description": "The binary the root ran, its version, and where the root pinned that version when it did (minor 18).",
|
|
76
|
+
"type": "object",
|
|
77
|
+
"required": ["name"],
|
|
78
|
+
"properties": {
|
|
79
|
+
"name": { "type": "string" },
|
|
80
|
+
"version": { "type": "string" },
|
|
81
|
+
"pin": { "type": "string" }
|
|
82
|
+
}
|
|
83
|
+
},
|
|
73
84
|
"terragrunt": {
|
|
74
85
|
"description": "Set when the root is a Terragrunt unit (minor 1).",
|
|
75
86
|
"type": "object",
|
|
@@ -189,6 +200,48 @@
|
|
|
189
200
|
"previous_address": { "type": "string" }
|
|
190
201
|
}
|
|
191
202
|
}
|
|
203
|
+
},
|
|
204
|
+
"state": {
|
|
205
|
+
"description": "On a tf-apply wave, a root that applied or had nothing to apply: the state version its backend holds afterwards, read from the object's metadata, never its contents (minor 20).",
|
|
206
|
+
"type": "object",
|
|
207
|
+
"required": ["backend", "versioning"],
|
|
208
|
+
"properties": {
|
|
209
|
+
"backend": { "type": "string" },
|
|
210
|
+
"location": { "type": "string" },
|
|
211
|
+
"version_id": { "type": "string" },
|
|
212
|
+
"versioning": { "enum": ["on", "off", "unknown"] },
|
|
213
|
+
"note": { "type": "string" }
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
"steps": {
|
|
217
|
+
"description": "The steps that ran for the root, in the order they ran (minor 17). failed: the root failed; approval: an on_failure: approve step failed, so the root's wave waits for an approval.",
|
|
218
|
+
"type": "array",
|
|
219
|
+
"items": {
|
|
220
|
+
"type": "object",
|
|
221
|
+
"required": ["name", "when", "status", "exit", "seconds"],
|
|
222
|
+
"properties": {
|
|
223
|
+
"name": { "type": "string" },
|
|
224
|
+
"when": { "enum": ["before-init", "after-init", "before-plan", "after-plan", "before-apply", "after-apply", "before-drift", "after-drift"] },
|
|
225
|
+
"status": { "enum": ["passed", "failed", "approval"] },
|
|
226
|
+
"exit": { "type": ["integer", "null"] },
|
|
227
|
+
"seconds": { "type": "number" }
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
"reads": {
|
|
232
|
+
"description": "The roots whose state the root reads through terraform_remote_state, and which outputs it planned on: planned, the upstream's plan in this run, with the outputs known only once it applies under unknown; applied, its state as it stands (minor 21).",
|
|
233
|
+
"type": "array",
|
|
234
|
+
"items": {
|
|
235
|
+
"type": "object",
|
|
236
|
+
"required": ["upstream", "data", "outputs"],
|
|
237
|
+
"properties": {
|
|
238
|
+
"upstream": { "type": "string" },
|
|
239
|
+
"data": { "type": "string" },
|
|
240
|
+
"outputs": { "enum": ["planned", "applied"] },
|
|
241
|
+
"unknown": { "type": "array", "items": { "type": "string" } },
|
|
242
|
+
"why": { "type": "string" }
|
|
243
|
+
}
|
|
244
|
+
}
|
|
192
245
|
}
|
|
193
246
|
}
|
|
194
247
|
}
|
|
@@ -230,7 +283,29 @@
|
|
|
230
283
|
"pull_request": { "type": "integer" },
|
|
231
284
|
"url": { "type": "string" }
|
|
232
285
|
}
|
|
233
|
-
}
|
|
286
|
+
},
|
|
287
|
+
"held_by_steps": {
|
|
288
|
+
"type": "array",
|
|
289
|
+
"items": { "type": "string" },
|
|
290
|
+
"description": "The roots whose on_failure: approve step failed: the gate holds the wave whatever the gate policy says, when it changes anything (minor 17)."
|
|
291
|
+
},
|
|
292
|
+
"cost": {
|
|
293
|
+
"type": "object",
|
|
294
|
+
"description": "The wave's monthly cost, when cost is on (minor 19): the sums over its roots estimated, and with cost.approve_above at base whether the change is over it, so the wave waits for an approval whatever the gate.",
|
|
295
|
+
"required": ["currency", "monthly_delta", "monthly_total", "past_monthly_total"],
|
|
296
|
+
"properties": {
|
|
297
|
+
"currency": { "type": "string" },
|
|
298
|
+
"monthly_delta": { "type": ["number", "null"] },
|
|
299
|
+
"monthly_total": { "type": ["number", "null"] },
|
|
300
|
+
"past_monthly_total": { "type": ["number", "null"] },
|
|
301
|
+
"unestimated": { "type": "array", "items": { "type": "string" } },
|
|
302
|
+
"approve_above": { "type": "number" },
|
|
303
|
+
"over": { "type": "boolean" }
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
"state": { "enum": ["planned", "waiting", "applying", "applied", "refused", "failed"], "description": "Where the wave stands (minor 21): a tf-plan wave is planned; a tf-apply wave waiting, applying, applied, refused or failed." },
|
|
307
|
+
"reads": { "type": "array", "items": { "type": "integer" }, "description": "The waves whose roots' state its roots read (minor 21)." },
|
|
308
|
+
"replans_after": { "type": "array", "items": { "type": "integer" }, "description": "A tf-plan wave whose plans read outputs known only once these waves apply: it plans again after they apply and waits for an approval of that plan; its review digest is null (minor 21)." }
|
|
234
309
|
}
|
|
235
310
|
}
|
|
236
311
|
},
|
|
@@ -278,8 +353,30 @@
|
|
|
278
353
|
"overriders": { "type": "array", "items": { "type": "string" }, "description": "Who may override a denial, from policy.override at base (minor 10)." }
|
|
279
354
|
}
|
|
280
355
|
},
|
|
356
|
+
"blast": {
|
|
357
|
+
"description": "A tf-plan run's blast radius (minor 22): the roots whose plan changes a resource or an output, and every root of the repo that reads their state through terraform_remote_state, followed through, nearest first. Absent when no root's plan changes anything, on a Terragrunt repo, and on other stages.",
|
|
358
|
+
"type": "object",
|
|
359
|
+
"required": ["roots", "downstream"],
|
|
360
|
+
"properties": {
|
|
361
|
+
"roots": { "type": "array", "items": { "type": "string" } },
|
|
362
|
+
"downstream": {
|
|
363
|
+
"type": "array",
|
|
364
|
+
"items": {
|
|
365
|
+
"type": "object",
|
|
366
|
+
"required": ["root", "reads", "depth", "planned"],
|
|
367
|
+
"properties": {
|
|
368
|
+
"root": { "type": "string" },
|
|
369
|
+
"reads": { "type": "array", "items": { "type": "string" }, "description": "The roots in the radius whose state it reads." },
|
|
370
|
+
"depth": { "type": "integer", "minimum": 1, "description": "1 when it reads a changed root's state itself, 2 when it reads such a reader's, and so on." },
|
|
371
|
+
"wave": { "type": "integer", "description": "The apply wave it is in." },
|
|
372
|
+
"planned": { "type": "boolean", "description": "Whether this run planned it." }
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
},
|
|
281
378
|
"cost": {
|
|
282
|
-
"description": "The cost estimate of a tf-plan run, when cost is on (minor 12): each root's monthly cost from the estimator, and the sums over the roots estimated.",
|
|
379
|
+
"description": "The cost estimate of a tf-plan run, or of a tf-apply wave's plans (minor 19), when cost is on (minor 12): each root's monthly cost from the estimator, and the sums over the roots estimated.",
|
|
283
380
|
"type": "object",
|
|
284
381
|
"required": ["estimator", "currency", "monthly_delta", "monthly_total", "past_monthly_total", "roots"],
|
|
285
382
|
"properties": {
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://intentius.io/terragucci/schemas/run/v1/run.schema.json",
|
|
4
|
+
"title": "terragucci.run/v1",
|
|
5
|
+
"description": "run.json under <prefix>/<project>/runs/<commit>/: the apply of one commit, every wave of it, the roots in each and the roots whose state each reads, and where each wave stands at its gate. Each tf-apply wave job rewrites its own row when it starts applying and when it ends. Within v1 fields are only added, and an existing field never changes meaning.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["schema", "project", "commit", "updated", "roots", "waves"],
|
|
8
|
+
"$defs": {
|
|
9
|
+
"state": {
|
|
10
|
+
"type": "object",
|
|
11
|
+
"required": ["key"],
|
|
12
|
+
"properties": {
|
|
13
|
+
"bucket": { "type": "string" },
|
|
14
|
+
"key": { "type": "string" }
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"properties": {
|
|
19
|
+
"schema": { "const": "terragucci.run/v1" },
|
|
20
|
+
"project": { "type": "string" },
|
|
21
|
+
"commit": { "type": "string" },
|
|
22
|
+
"updated": { "type": "string", "description": "When a wave job last wrote it." },
|
|
23
|
+
"roots": {
|
|
24
|
+
"type": "array",
|
|
25
|
+
"description": "Each root, its wave, and the roots whose state it reads through terraform_remote_state. Empty for a Terragrunt repo.",
|
|
26
|
+
"items": {
|
|
27
|
+
"type": "object",
|
|
28
|
+
"required": ["root", "wave", "reads"],
|
|
29
|
+
"properties": {
|
|
30
|
+
"root": { "type": "string" },
|
|
31
|
+
"wave": { "type": "integer" },
|
|
32
|
+
"reads": { "type": "array", "items": { "type": "string" } },
|
|
33
|
+
"state": { "$ref": "#/$defs/state", "description": "The state its backend block names, which a root of another project may read." },
|
|
34
|
+
"external": {
|
|
35
|
+
"type": "array",
|
|
36
|
+
"description": "Its terraform_remote_state reads of a state no root of the project holds: the block's label and the state it names. The estate page matches them to other projects' roots.",
|
|
37
|
+
"items": {
|
|
38
|
+
"type": "object",
|
|
39
|
+
"required": ["data", "key"],
|
|
40
|
+
"properties": {
|
|
41
|
+
"data": { "type": "string" },
|
|
42
|
+
"bucket": { "type": "string" },
|
|
43
|
+
"key": { "type": "string" }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"waves": {
|
|
51
|
+
"type": "array",
|
|
52
|
+
"items": {
|
|
53
|
+
"type": "object",
|
|
54
|
+
"required": ["number", "roots", "reads", "state", "gate"],
|
|
55
|
+
"properties": {
|
|
56
|
+
"number": { "type": "integer" },
|
|
57
|
+
"roots": { "type": "array", "items": { "type": "string" } },
|
|
58
|
+
"reads": { "type": "array", "items": { "type": "integer" }, "description": "The waves whose roots' state its roots read." },
|
|
59
|
+
"state": { "enum": ["not-started", "planned", "waiting", "applying", "applied", "refused", "failed"] },
|
|
60
|
+
"gate": { "type": "string", "description": "Its gate on the ledger, wave-<n>." },
|
|
61
|
+
"policy": { "type": "string", "description": "The gate policy it ran under: always, on-destroy or never." },
|
|
62
|
+
"approval": { "enum": ["not-requested", "waiting", "approved", "not-required"] },
|
|
63
|
+
"digest": { "type": ["string", "null"], "description": "The set digest its gate decided on." },
|
|
64
|
+
"command": { "type": "string", "description": "The command that approves it, while it waits." },
|
|
65
|
+
"report": { "type": "string", "description": "Its report's directory, relative to the project's index.json." },
|
|
66
|
+
"shares": { "type": "integer", "description": "A wave split across jobs: how many share jobs apply it." },
|
|
67
|
+
"shares_applied": { "type": "array", "items": { "type": "integer" }, "description": "The shares that applied; the wave is applied once every share has." },
|
|
68
|
+
"changed": { "type": "array", "items": { "type": "string" }, "description": "The roots whose plan in this wave changes a resource or an output: where the run's blast radius starts." },
|
|
69
|
+
"spans": {
|
|
70
|
+
"type": "array",
|
|
71
|
+
"description": "The wave's time, oldest first: each plan, each wait at its gate (no end while it waits) and each apply (no end while it applies). At most 24.",
|
|
72
|
+
"items": {
|
|
73
|
+
"type": "object",
|
|
74
|
+
"required": ["phase", "start"],
|
|
75
|
+
"properties": {
|
|
76
|
+
"phase": { "enum": ["plan", "gate", "apply"] },
|
|
77
|
+
"start": { "type": "string" },
|
|
78
|
+
"end": { "type": "string" },
|
|
79
|
+
"share": { "type": "integer", "description": "The share of a wave split across jobs that planned or applied." }
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"updated": { "type": "string" }
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://intentius.io/terragucci/schemas/state-versions/v1/state-versions.schema.json",
|
|
4
|
+
"title": "terragucci.state-versions/v1",
|
|
5
|
+
"description": "states.json in a project's directory of a reports prefix: for each root, where its state is, whether its backend keeps versions, and the version ids its tf-apply waves left, newest first. Version ids, never a state's contents. Within v1 fields are only added, and an existing field never changes meaning.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["schema", "roots"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"schema": { "const": "terragucci.state-versions/v1" },
|
|
10
|
+
"roots": {
|
|
11
|
+
"type": "array",
|
|
12
|
+
"description": "By root path.",
|
|
13
|
+
"items": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"required": ["root", "backend", "versioning", "checked", "versions"],
|
|
16
|
+
"properties": {
|
|
17
|
+
"root": { "type": "string" },
|
|
18
|
+
"backend": { "type": "string", "description": "The backend the root initialised: s3, local, gcs and so on." },
|
|
19
|
+
"location": { "type": "string", "description": "s3://<bucket>/<key>, or the local file's path in the root." },
|
|
20
|
+
"versioning": { "enum": ["on", "off", "unknown"], "description": "As the newest apply found it." },
|
|
21
|
+
"note": { "type": "string", "description": "Why the version could not be read, or what off means for this backend." },
|
|
22
|
+
"checked": { "type": "string", "description": "When the newest apply that recorded the root finished." },
|
|
23
|
+
"versions": {
|
|
24
|
+
"type": "array",
|
|
25
|
+
"description": "Newest first, at most 20.",
|
|
26
|
+
"items": {
|
|
27
|
+
"type": "object",
|
|
28
|
+
"required": ["version_id", "commit", "finished", "path"],
|
|
29
|
+
"properties": {
|
|
30
|
+
"version_id": { "type": "string" },
|
|
31
|
+
"commit": { "type": "string" },
|
|
32
|
+
"finished": { "type": "string", "description": "When the wave that recorded the version finished." },
|
|
33
|
+
"wave": { "type": "integer" },
|
|
34
|
+
"path": { "type": "string", "description": "The wave's run directory, relative to the project's index.json." }
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|