@intentius/chant 0.52.2 → 0.53.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/cdk/advise.d.ts +29 -0
- package/dist/cdk/advise.d.ts.map +1 -0
- package/dist/cdk/assembly.d.ts +38 -0
- package/dist/cdk/assembly.d.ts.map +1 -0
- package/dist/cdk/graph.d.ts +68 -0
- package/dist/cdk/graph.d.ts.map +1 -0
- package/dist/cdk/tier-map.d.ts +44 -0
- package/dist/cdk/tier-map.d.ts.map +1 -0
- package/dist/cdk/types.d.ts +114 -0
- package/dist/cdk/types.d.ts.map +1 -0
- package/dist/cli/commands/carve-apply.d.ts.map +1 -1
- package/dist/cli/commands/carve-bridge.d.ts.map +1 -1
- package/dist/cli/commands/carve-emit.d.ts.map +1 -1
- package/dist/cli/commands/carve.d.ts +48 -6
- package/dist/cli/commands/carve.d.ts.map +1 -1
- package/dist/cli/handlers/carve-emit.d.ts.map +1 -1
- package/dist/cli/handlers/carve.d.ts +5 -4
- package/dist/cli/handlers/carve.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.d.ts +10 -0
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/identity.d.ts +196 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/lexicon.d.ts +41 -0
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/terraform/adopt-state.d.ts +17 -66
- package/dist/terraform/adopt-state.d.ts.map +1 -1
- package/dist/terraform/aws-resources.d.ts +1 -1
- package/dist/terraform/carve-provider.d.ts +142 -0
- package/dist/terraform/carve-provider.d.ts.map +1 -0
- package/dist/terraform/carve.d.ts +36 -3
- package/dist/terraform/carve.d.ts.map +1 -1
- package/dist/terraform/emit-source.d.ts +25 -0
- package/dist/terraform/emit-source.d.ts.map +1 -0
- package/dist/terraform/graduate.d.ts +11 -1
- package/dist/terraform/graduate.d.ts.map +1 -1
- package/dist/terraform/providers/aws.d.ts +19 -0
- package/dist/terraform/providers/aws.d.ts.map +1 -0
- package/dist/terraform/providers/gcp.d.ts +41 -0
- package/dist/terraform/providers/gcp.d.ts.map +1 -0
- package/dist/terraform/providers/index.d.ts +15 -0
- package/dist/terraform/providers/index.d.ts.map +1 -0
- package/dist/terraform/providers/kubernetes.d.ts +29 -0
- package/dist/terraform/providers/kubernetes.d.ts.map +1 -0
- package/dist/terraform/score.d.ts +70 -4
- package/dist/terraform/score.d.ts.map +1 -1
- package/dist/terraform/tier-map.d.ts +30 -35
- package/dist/terraform/tier-map.d.ts.map +1 -1
- package/dist/terraform/types.d.ts +6 -0
- package/dist/terraform/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/cdk/__fixtures__/cdk.out/AppStack.template.json +171 -0
- package/src/cdk/__fixtures__/cdk.out/DataStack.template.json +90 -0
- package/src/cdk/__fixtures__/cdk.out/cdk.out +1 -0
- package/src/cdk/__fixtures__/cdk.out/manifest.json +30 -0
- package/src/cdk/__fixtures__/cdk.out/tree.json +201 -0
- package/src/cdk/__fixtures__/cdk.out-dummy/LookupStack.template.json +29 -0
- package/src/cdk/__fixtures__/cdk.out-dummy/manifest.json +26 -0
- package/src/cdk/advise.test.ts +208 -0
- package/src/cdk/advise.ts +44 -0
- package/src/cdk/assembly.ts +133 -0
- package/src/cdk/graph.test.ts +206 -0
- package/src/cdk/graph.ts +525 -0
- package/src/cdk/tier-map.ts +71 -0
- package/src/cdk/types.ts +115 -0
- package/src/cli/commands/carve-apply.ts +20 -4
- package/src/cli/commands/carve-bridge.ts +9 -5
- package/src/cli/commands/carve-emit-k8s.test.ts +262 -0
- package/src/cli/commands/carve-emit-provider.test.ts +207 -0
- package/src/cli/commands/carve-emit.ts +28 -24
- package/src/cli/commands/carve.ts +139 -36
- package/src/cli/handlers/carve-emit.ts +8 -3
- package/src/cli/handlers/carve.ts +8 -7
- package/src/cli/handlers/lifecycle.test.ts +187 -1
- package/src/cli/handlers/lifecycle.ts +125 -1
- package/src/cli/main.ts +12 -5
- package/src/identity.test.ts +199 -0
- package/src/identity.ts +346 -0
- package/src/index.ts +1 -0
- package/src/lexicon.ts +53 -0
- package/src/terraform/__fixtures__/gcp-estate/main.tf +60 -0
- package/src/terraform/adopt-state.test.ts +131 -0
- package/src/terraform/adopt-state.ts +19 -167
- package/src/terraform/aws-resources.test.ts +38 -22
- package/src/terraform/aws-resources.ts +1 -1
- package/src/terraform/carve-provider.test.ts +155 -0
- package/src/terraform/carve-provider.ts +237 -0
- package/src/terraform/carve.test.ts +55 -1
- package/src/terraform/carve.ts +0 -0
- package/src/terraform/emit-source.ts +39 -0
- package/src/terraform/graduate.test.ts +37 -0
- package/src/terraform/graduate.ts +55 -7
- package/src/terraform/graph.ts +3 -3
- package/src/terraform/providers/aws.ts +169 -0
- package/src/terraform/providers/gcp.test.ts +228 -0
- package/src/terraform/providers/gcp.ts +329 -0
- package/src/terraform/providers/index.ts +21 -0
- package/src/terraform/providers/kubernetes.ts +224 -0
- package/src/terraform/score.ts +111 -25
- package/src/terraform/tier-map.ts +45 -107
- package/src/terraform/types.ts +6 -0
package/src/terraform/score.ts
CHANGED
|
@@ -18,13 +18,20 @@
|
|
|
18
18
|
* expression in a block that manages no infrastructure, the same order of work
|
|
19
19
|
* as recording an outbound deferred input. Hence 4, not 12.
|
|
20
20
|
*
|
|
21
|
-
* Sub-resources that inline into a parent (see `
|
|
21
|
+
* Sub-resources that inline into a parent (see `foldParentOf`) are folded into the
|
|
22
22
|
* parent's carve set: they are removed from the ranking and their edge to the
|
|
23
23
|
* parent is not counted as inbound — inlining them is free, not boundary work.
|
|
24
|
+
*
|
|
25
|
+
* The model itself is not Terraform-specific — the CDK cloud-assembly advisor
|
|
26
|
+
* (#1056) scores construct units with the same arithmetic. What differs per
|
|
27
|
+
* source is the type→tier lookup and the extra signals that source can see, so
|
|
28
|
+
* both are injectable via `ScoreOptions` rather than forked into a second
|
|
29
|
+
* scorer. Everything else — the six penalty terms, the clamp, the bands — is
|
|
30
|
+
* shared verbatim.
|
|
24
31
|
*/
|
|
25
32
|
|
|
26
33
|
import { inboundEdges, outboundEdges } from "./graph";
|
|
27
|
-
import {
|
|
34
|
+
import { foldParentOf, resolveTier, type TierInfo } from "./tier-map";
|
|
28
35
|
import type { TfGraph, TfNode } from "./types";
|
|
29
36
|
|
|
30
37
|
export type PeelabilityBand = "clean leaf" | "carvable w/ edits" | "leave in Terraform";
|
|
@@ -38,7 +45,12 @@ export interface PeelabilityBreakdown {
|
|
|
38
45
|
tier: 1 | 2 | 3 | null;
|
|
39
46
|
hasDynamic: boolean;
|
|
40
47
|
instances: number;
|
|
41
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* Signed penalty contributions, for report transparency. The six shared terms
|
|
50
|
+
* are always present; a source-specific scorer may add its own keys (the CDK
|
|
51
|
+
* advisor's `asset`, say). A reader that sums the values reproduces the score;
|
|
52
|
+
* a reader that only knows the six named terms still reads those correctly.
|
|
53
|
+
*/
|
|
42
54
|
penalties: {
|
|
43
55
|
inbound: number;
|
|
44
56
|
outbound: number;
|
|
@@ -46,9 +58,28 @@ export interface PeelabilityBreakdown {
|
|
|
46
58
|
tier: number;
|
|
47
59
|
dynamic: number;
|
|
48
60
|
instances: number;
|
|
61
|
+
[term: string]: number;
|
|
49
62
|
};
|
|
50
63
|
}
|
|
51
64
|
|
|
65
|
+
/**
|
|
66
|
+
* One unit of source a scored node stands for, when the node is a fold of
|
|
67
|
+
* several (#1056). A CDK construct emits 1..n CloudFormation resources and
|
|
68
|
+
* ranks once; this names the resources that carve with it. Absent on the
|
|
69
|
+
* Terraform path, where a fold is expressed by the child's absence from the
|
|
70
|
+
* ranking instead.
|
|
71
|
+
*/
|
|
72
|
+
export interface CarveUnitMember {
|
|
73
|
+
/** Identifier within its template/estate — a CFN logical ID. */
|
|
74
|
+
id: string;
|
|
75
|
+
/** Resource type, e.g. `AWS::IAM::Role`. */
|
|
76
|
+
type: string;
|
|
77
|
+
/** The template/stack the member lives in. */
|
|
78
|
+
stack?: string;
|
|
79
|
+
/** The member's own source path, e.g. a CDK construct path. */
|
|
80
|
+
path?: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
52
83
|
export interface Peelability {
|
|
53
84
|
address: string;
|
|
54
85
|
kind: "resource" | "module";
|
|
@@ -57,10 +88,53 @@ export interface Peelability {
|
|
|
57
88
|
/** Native spec type a carve would target; undefined for modules/unsupported. */
|
|
58
89
|
mapsTo?: string;
|
|
59
90
|
breakdown: PeelabilityBreakdown;
|
|
91
|
+
/**
|
|
92
|
+
* Source-specific facts about this node that the six penalty terms do not
|
|
93
|
+
* carry — why it was disqualified, what folded into it, what it is. Plain
|
|
94
|
+
* prose, for the report's benefit; nothing keys on it.
|
|
95
|
+
*/
|
|
96
|
+
notes?: string[];
|
|
97
|
+
/** The source units folded into this node (#1056). */
|
|
98
|
+
members?: CarveUnitMember[];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Source-specific scoring input for one node, layered on the shared model.
|
|
103
|
+
* Everything here is optional: with no signals a node scores exactly as the
|
|
104
|
+
* Terraform path scores it.
|
|
105
|
+
*/
|
|
106
|
+
export interface ScoreSignals {
|
|
107
|
+
/** Extra penalty terms, merged into `breakdown.penalties` and the total. */
|
|
108
|
+
penalties?: Record<string, number>;
|
|
109
|
+
notes?: string[];
|
|
110
|
+
members?: CarveUnitMember[];
|
|
111
|
+
/**
|
|
112
|
+
* When set, the node scores 0 and this is the stated reason. For claims the
|
|
113
|
+
* arithmetic cannot express — a CDK assembly synthesized from dummy lookup
|
|
114
|
+
* values is not a faithful picture of anything, so no score it produces is
|
|
115
|
+
* worth reading.
|
|
116
|
+
*/
|
|
117
|
+
disqualified?: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface ScoreOptions {
|
|
121
|
+
/**
|
|
122
|
+
* Resource type → native tier. Defaults to the Terraform tier map; the CDK
|
|
123
|
+
* advisor passes a CloudFormation-keyed one. Modules keep the built-in
|
|
124
|
+
* {@link MODULE_TIER} treatment either way.
|
|
125
|
+
*/
|
|
126
|
+
tierOf?: (type: string) => TierInfo | null;
|
|
127
|
+
signalsFor?: (node: TfNode) => ScoreSignals;
|
|
60
128
|
}
|
|
61
129
|
|
|
62
130
|
const clamp = (n: number): number => Math.max(0, Math.min(100, n));
|
|
63
131
|
|
|
132
|
+
/**
|
|
133
|
+
* `-12 * 0` is `-0`, and JSON has no notation for it — a report written to a
|
|
134
|
+
* file would not equal the one held in memory. A term that costs nothing is 0.
|
|
135
|
+
*/
|
|
136
|
+
const term = (n: number): number => (n === 0 ? 0 : n);
|
|
137
|
+
|
|
64
138
|
export function bandFor(score: number): PeelabilityBand {
|
|
65
139
|
if (score >= 80) return "clean leaf";
|
|
66
140
|
if (score >= 50) return "carvable w/ edits";
|
|
@@ -81,7 +155,7 @@ function computeFolds(graph: TfGraph): { folded: Set<string>; childrenOf: Map<st
|
|
|
81
155
|
const childrenOf = new Map<string, Set<string>>();
|
|
82
156
|
for (const node of graph.nodes) {
|
|
83
157
|
if (node.kind !== "resource" || !node.type) continue;
|
|
84
|
-
const parentType =
|
|
158
|
+
const parentType = foldParentOf(node.type);
|
|
85
159
|
if (!parentType) continue;
|
|
86
160
|
const parentAddress = `${parentType}.${node.name}`;
|
|
87
161
|
if (!byAddress.has(parentAddress)) continue; // sub-resource without its parent → score it on its own
|
|
@@ -92,7 +166,12 @@ function computeFolds(graph: TfGraph): { folded: Set<string>; childrenOf: Map<st
|
|
|
92
166
|
return { folded, childrenOf };
|
|
93
167
|
}
|
|
94
168
|
|
|
95
|
-
function scoreNode(
|
|
169
|
+
function scoreNode(
|
|
170
|
+
node: TfNode,
|
|
171
|
+
graph: TfGraph,
|
|
172
|
+
foldedChildren: Set<string>,
|
|
173
|
+
opts: ScoreOptions,
|
|
174
|
+
): Peelability {
|
|
96
175
|
// Inbound excludes edges from this node's own folded sub-resources, and
|
|
97
176
|
// counts output blocks separately — they cost less to bridge (#1638).
|
|
98
177
|
const inboundAll = inboundEdges(graph, node.address).filter((e) => !foldedChildren.has(e.from));
|
|
@@ -100,45 +179,51 @@ function scoreNode(node: TfNode, graph: TfGraph, foldedChildren: Set<string>): P
|
|
|
100
179
|
const outputs = inboundAll.length - inbound;
|
|
101
180
|
const outbound = outboundEdges(graph, node.address).length;
|
|
102
181
|
|
|
103
|
-
const
|
|
182
|
+
const signals = opts.signalsFor?.(node) ?? {};
|
|
183
|
+
const extra = { ...(signals.penalties ?? {}) };
|
|
184
|
+
const trimmings = {
|
|
185
|
+
...(signals.notes?.length ? { notes: signals.notes } : {}),
|
|
186
|
+
...(signals.members?.length ? { members: signals.members } : {}),
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const tierOf = opts.tierOf ?? resolveTier;
|
|
190
|
+
const tierInfo = node.kind === "module" ? { tier: MODULE_TIER, mapsTo: undefined } : node.type ? tierOf(node.type) : null;
|
|
104
191
|
const tier = tierInfo ? tierInfo.tier : null;
|
|
105
192
|
|
|
106
|
-
// Unsupported provider/type → 0, no
|
|
107
|
-
|
|
193
|
+
// Unsupported provider/type, or a source-specific disqualification → 0, no
|
|
194
|
+
// partial credit. The extra penalties are dropped with the rest of the
|
|
195
|
+
// arithmetic: there is no score left for them to reduce.
|
|
196
|
+
if (tier === null || signals.disqualified) {
|
|
108
197
|
return {
|
|
109
198
|
address: node.address,
|
|
110
199
|
kind: node.kind,
|
|
111
200
|
score: 0,
|
|
112
201
|
band: "leave in Terraform",
|
|
202
|
+
...(tier === null ? {} : { mapsTo: tierInfo && "mapsTo" in tierInfo ? tierInfo.mapsTo : undefined }),
|
|
113
203
|
breakdown: {
|
|
114
204
|
inbound,
|
|
115
205
|
outbound,
|
|
116
206
|
outputs,
|
|
117
|
-
tier
|
|
207
|
+
tier,
|
|
118
208
|
hasDynamic: node.hasDynamic,
|
|
119
209
|
instances: node.instances,
|
|
120
210
|
penalties: { inbound: 0, outbound: 0, outputs: 0, tier: 0, dynamic: 0, instances: 0 },
|
|
121
211
|
},
|
|
212
|
+
...trimmings,
|
|
213
|
+
...(signals.disqualified ? { notes: [signals.disqualified, ...(signals.notes ?? [])] } : {}),
|
|
122
214
|
};
|
|
123
215
|
}
|
|
124
216
|
|
|
125
217
|
const penalties = {
|
|
126
|
-
inbound: -12 * inbound,
|
|
127
|
-
outbound: -4 * outbound,
|
|
128
|
-
outputs: -4 * outputs,
|
|
129
|
-
tier: -15 * (tier - 1),
|
|
218
|
+
inbound: term(-12 * inbound),
|
|
219
|
+
outbound: term(-4 * outbound),
|
|
220
|
+
outputs: term(-4 * outputs),
|
|
221
|
+
tier: term(-15 * (tier - 1)),
|
|
130
222
|
dynamic: node.hasDynamic ? -10 : 0,
|
|
131
|
-
instances: -3 * Math.max(0, node.instances - 1),
|
|
223
|
+
instances: term(-3 * Math.max(0, node.instances - 1)),
|
|
224
|
+
...Object.fromEntries(Object.entries(extra).map(([key, value]) => [key, term(value)])),
|
|
132
225
|
};
|
|
133
|
-
const score = clamp(
|
|
134
|
-
100 +
|
|
135
|
-
penalties.inbound +
|
|
136
|
-
penalties.outbound +
|
|
137
|
-
penalties.outputs +
|
|
138
|
-
penalties.tier +
|
|
139
|
-
penalties.dynamic +
|
|
140
|
-
penalties.instances,
|
|
141
|
-
);
|
|
226
|
+
const score = clamp(100 + Object.values(penalties).reduce((sum, n) => sum + n, 0));
|
|
142
227
|
|
|
143
228
|
return {
|
|
144
229
|
address: node.address,
|
|
@@ -155,6 +240,7 @@ function scoreNode(node: TfNode, graph: TfGraph, foldedChildren: Set<string>): P
|
|
|
155
240
|
instances: node.instances,
|
|
156
241
|
penalties,
|
|
157
242
|
},
|
|
243
|
+
...trimmings,
|
|
158
244
|
};
|
|
159
245
|
}
|
|
160
246
|
|
|
@@ -162,10 +248,10 @@ function scoreNode(node: TfNode, graph: TfGraph, foldedChildren: Set<string>): P
|
|
|
162
248
|
* Score every carvable node in the estate, ranked most-peelable first. Folded
|
|
163
249
|
* sub-resources are omitted (they carve with their parent, not on their own).
|
|
164
250
|
*/
|
|
165
|
-
export function scoreEstate(graph: TfGraph): Peelability[] {
|
|
251
|
+
export function scoreEstate(graph: TfGraph, opts: ScoreOptions = {}): Peelability[] {
|
|
166
252
|
const { folded, childrenOf } = computeFolds(graph);
|
|
167
253
|
return graph.nodes
|
|
168
254
|
.filter((n) => !folded.has(n.address))
|
|
169
|
-
.map((n) => scoreNode(n, graph, childrenOf.get(n.address) ?? new Set()))
|
|
255
|
+
.map((n) => scoreNode(n, graph, childrenOf.get(n.address) ?? new Set(), opts))
|
|
170
256
|
.sort((a, b) => b.score - a.score || (a.address < b.address ? -1 : a.address > b.address ? 1 : 0));
|
|
171
257
|
}
|
|
@@ -1,139 +1,77 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Terraform-type → native-spec
|
|
2
|
+
* Terraform-type → native-spec ranking for the carve-out advisor (#214 T3),
|
|
3
|
+
* resolved through the carve provider registry (`carve-provider.ts`, #2016).
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
* (`aws-resources.ts`), so the
|
|
6
|
-
* can produce — no advise↔emit
|
|
7
|
-
*
|
|
5
|
+
* Every entry comes from a registered provider: the AWS ones from the single
|
|
6
|
+
* AWS carve-out table (`providers/aws.ts` over `aws-resources.ts`), so the
|
|
7
|
+
* advisor ranks exactly the AWS types `carve emit` can produce — no advise↔emit
|
|
8
|
+
* cliff — and the Kubernetes ones from `providers/kubernetes.ts`, which emits
|
|
9
|
+
* `kubernetes_manifest` and ranks the typed provider resources it does not.
|
|
8
10
|
*
|
|
9
11
|
* tier 1 — a clean 1:1 native resource
|
|
10
12
|
* tier 2 — maps, but with reshaping
|
|
11
13
|
* tier 3 — a hard/partial map
|
|
12
14
|
* null — no known native mapping (unsupported provider/type) → score 0
|
|
15
|
+
*
|
|
16
|
+
* These are functions, not frozen constants: the registry is open, so a
|
|
17
|
+
* provider registered after module load has to be visible here.
|
|
13
18
|
*/
|
|
14
19
|
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
20
|
+
import {
|
|
21
|
+
carveFoldParent,
|
|
22
|
+
carveIdentityAttr,
|
|
23
|
+
carveTierMap,
|
|
24
|
+
resolveEmitProvider,
|
|
25
|
+
type TierInfo,
|
|
26
|
+
} from "./carve-provider";
|
|
22
27
|
|
|
23
|
-
|
|
24
|
-
* Kubernetes provider types with a native k8s-lexicon target. `kubernetes_manifest`
|
|
25
|
-
* IS the manifest (tier 1). The typed provider resources (`kubernetes_deployment`,
|
|
26
|
-
* ...) reshape their HCL schema back into a manifest, so they rank tier 2; the
|
|
27
|
-
* common `_v1` aliases share their base type's entry.
|
|
28
|
-
*/
|
|
29
|
-
const K8S_TIER_BASE: Record<string, TierInfo> = {
|
|
30
|
-
kubernetes_manifest: { tier: 1, mapsTo: "k8s:manifest" },
|
|
31
|
-
kubernetes_namespace: { tier: 2, mapsTo: "k8s:Namespace" },
|
|
32
|
-
kubernetes_config_map: { tier: 2, mapsTo: "k8s:ConfigMap" },
|
|
33
|
-
kubernetes_secret: { tier: 2, mapsTo: "k8s:Secret" },
|
|
34
|
-
kubernetes_service: { tier: 2, mapsTo: "k8s:Service" },
|
|
35
|
-
kubernetes_service_account: { tier: 2, mapsTo: "k8s:ServiceAccount" },
|
|
36
|
-
kubernetes_deployment: { tier: 2, mapsTo: "k8s:Deployment" },
|
|
37
|
-
kubernetes_stateful_set: { tier: 2, mapsTo: "k8s:StatefulSet" },
|
|
38
|
-
kubernetes_daemon_set: { tier: 2, mapsTo: "k8s:DaemonSet" },
|
|
39
|
-
kubernetes_job: { tier: 2, mapsTo: "k8s:Job" },
|
|
40
|
-
kubernetes_cron_job: { tier: 2, mapsTo: "k8s:CronJob" },
|
|
41
|
-
kubernetes_ingress: { tier: 2, mapsTo: "k8s:Ingress" },
|
|
42
|
-
kubernetes_network_policy: { tier: 2, mapsTo: "k8s:NetworkPolicy" },
|
|
43
|
-
kubernetes_persistent_volume_claim: { tier: 2, mapsTo: "k8s:PersistentVolumeClaim" },
|
|
44
|
-
kubernetes_role: { tier: 2, mapsTo: "k8s:Role" },
|
|
45
|
-
kubernetes_role_binding: { tier: 2, mapsTo: "k8s:RoleBinding" },
|
|
46
|
-
kubernetes_cluster_role: { tier: 2, mapsTo: "k8s:ClusterRole" },
|
|
47
|
-
kubernetes_cluster_role_binding: { tier: 2, mapsTo: "k8s:ClusterRoleBinding" },
|
|
48
|
-
kubernetes_resource_quota: { tier: 2, mapsTo: "k8s:ResourceQuota" },
|
|
49
|
-
kubernetes_limit_range: { tier: 2, mapsTo: "k8s:LimitRange" },
|
|
50
|
-
kubernetes_priority_class: { tier: 2, mapsTo: "k8s:PriorityClass" },
|
|
51
|
-
kubernetes_pod_disruption_budget: { tier: 2, mapsTo: "k8s:PodDisruptionBudget" },
|
|
52
|
-
kubernetes_horizontal_pod_autoscaler: { tier: 2, mapsTo: "k8s:HorizontalPodAutoscaler" },
|
|
53
|
-
};
|
|
28
|
+
export { carveEmitTypes, type TierInfo } from "./carve-provider";
|
|
54
29
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
);
|
|
60
|
-
// The one provider type whose current alias is _v2, not _v1.
|
|
61
|
-
K8S_TIER.kubernetes_horizontal_pod_autoscaler_v2 = K8S_TIER_BASE.kubernetes_horizontal_pod_autoscaler;
|
|
30
|
+
/** TF resource type → native tier, over every registered provider. Absent = unsupported (score 0). */
|
|
31
|
+
export function tierMap(): Readonly<Record<string, TierInfo>> {
|
|
32
|
+
return carveTierMap();
|
|
33
|
+
}
|
|
62
34
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
...Object.fromEntries(AWS_CARVE_TYPES.map((t) => [t.tfType, { tier: t.tier, mapsTo: t.nativeType }])),
|
|
67
|
-
// Kubernetes — ranked by advise; emit support is a separate target.
|
|
68
|
-
...K8S_TIER,
|
|
69
|
-
};
|
|
35
|
+
export function resolveTier(tfType: string): TierInfo | null {
|
|
36
|
+
return carveTierMap()[tfType] ?? null;
|
|
37
|
+
}
|
|
70
38
|
|
|
71
39
|
/**
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* carve set: it is not ranked on its own, and its edge to the parent does not
|
|
76
|
-
* count as inbound boundary work — inlining it is free.
|
|
77
|
-
*
|
|
78
|
-
* Maps sub-resource TF type → parent TF type.
|
|
40
|
+
* The HCL attribute carrying a resource's physical name. A dotted entry is a
|
|
41
|
+
* path into nested blocks (`manifest.metadata.name`). Used for the graph's
|
|
42
|
+
* identity and the live-import hint. Absent → fall back to the TF logical name.
|
|
79
43
|
*/
|
|
80
|
-
export
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
aws_s3_bucket_policy: "aws_s3_bucket",
|
|
84
|
-
aws_s3_bucket_public_access_block: "aws_s3_bucket",
|
|
85
|
-
aws_s3_bucket_server_side_encryption_configuration: "aws_s3_bucket",
|
|
86
|
-
aws_s3_bucket_lifecycle_configuration: "aws_s3_bucket",
|
|
87
|
-
aws_s3_bucket_website_configuration: "aws_s3_bucket",
|
|
88
|
-
aws_s3_bucket_cors_configuration: "aws_s3_bucket",
|
|
89
|
-
aws_s3_bucket_logging: "aws_s3_bucket",
|
|
90
|
-
aws_s3_bucket_ownership_controls: "aws_s3_bucket",
|
|
91
|
-
aws_s3_bucket_notification: "aws_s3_bucket",
|
|
92
|
-
aws_s3_bucket_accelerate_configuration: "aws_s3_bucket",
|
|
93
|
-
aws_s3_bucket_request_payment_configuration: "aws_s3_bucket",
|
|
94
|
-
aws_s3_bucket_intelligent_tiering_configuration: "aws_s3_bucket",
|
|
95
|
-
aws_ecr_lifecycle_policy: "aws_ecr_repository",
|
|
96
|
-
aws_ecr_repository_policy: "aws_ecr_repository",
|
|
97
|
-
};
|
|
44
|
+
export function identityAttrOf(tfType: string): string | undefined {
|
|
45
|
+
return carveIdentityAttr(tfType);
|
|
46
|
+
}
|
|
98
47
|
|
|
99
48
|
/**
|
|
100
|
-
* The
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* identity and the live-import hint. Absent → fall back to the TF logical name.
|
|
49
|
+
* The parent Terraform type a sub-resource folds into, if any. Terraform splits
|
|
50
|
+
* configuration the native spec keeps in one resource; a sub-resource sharing
|
|
51
|
+
* its parent's name carves with the parent rather than being ranked on its own.
|
|
104
52
|
*/
|
|
105
|
-
export
|
|
106
|
-
|
|
107
|
-
kubernetes_manifest: "manifest.metadata.name",
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
export function resolveTier(tfType: string): TierInfo | null {
|
|
111
|
-
return TIER_MAP[tfType] ?? null;
|
|
53
|
+
export function foldParentOf(tfType: string): string | undefined {
|
|
54
|
+
return carveFoldParent(tfType);
|
|
112
55
|
}
|
|
113
56
|
|
|
114
57
|
/**
|
|
115
58
|
* Can `chant carve emit` produce chant source for this type? Narrower than
|
|
116
|
-
* `resolveTier`:
|
|
117
|
-
*
|
|
118
|
-
* Both emit paths — `--state` and `--env` — gate on this, so a
|
|
119
|
-
* command refuses is refused by the other with the same message.
|
|
59
|
+
* `resolveTier`: providers also rank types no emit path can adopt (the typed
|
|
60
|
+
* `kubernetes_*` resources, whose manifest only the provider schema knows how
|
|
61
|
+
* to reassemble). Both emit paths — `--state` and `--env` — gate on this, so a
|
|
62
|
+
* type either command refuses is refused by the other with the same message.
|
|
120
63
|
*/
|
|
121
64
|
export function canCarveEmit(tfType: string): boolean {
|
|
122
|
-
return
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/** The types `canCarveEmit` accepts, sorted, for user-facing hints. */
|
|
126
|
-
export function carveEmitTypes(): string[] {
|
|
127
|
-
return AWS_CARVE_TYPES.map((t) => t.tfType).sort();
|
|
65
|
+
return resolveEmitProvider(tfType) !== undefined;
|
|
128
66
|
}
|
|
129
67
|
|
|
130
68
|
/**
|
|
131
69
|
* Can `chant carve bridge` render a Terraform `data` source for this type? A
|
|
132
|
-
* dotted
|
|
133
|
-
* is flat `attr = value` — `manifest.metadata.name = "x"` is not valid
|
|
134
|
-
* An absent entry is fine: the bridge writes a TODO comment
|
|
70
|
+
* dotted identity attribute is a path into nested blocks, and a data source
|
|
71
|
+
* body is flat `attr = value` — `manifest.metadata.name = "x"` is not valid
|
|
72
|
+
* HCL. An absent entry is fine: the bridge writes a TODO comment instead.
|
|
135
73
|
*/
|
|
136
74
|
export function canBridge(tfType: string): boolean {
|
|
137
|
-
const attr =
|
|
75
|
+
const attr = carveIdentityAttr(tfType);
|
|
138
76
|
return attr === undefined || !attr.includes(".");
|
|
139
77
|
}
|
package/src/terraform/types.ts
CHANGED
|
@@ -64,6 +64,12 @@ export interface TfEdge {
|
|
|
64
64
|
* parameter. Empty when the reference came from a non-object block shape.
|
|
65
65
|
*/
|
|
66
66
|
via: string[];
|
|
67
|
+
/**
|
|
68
|
+
* The two ends live in different templates — a CloudFormation
|
|
69
|
+
* `Fn::ImportValue` resolved through the exporting stack's `Outputs` (#1056).
|
|
70
|
+
* Never set on the Terraform path, where a single estate is one namespace.
|
|
71
|
+
*/
|
|
72
|
+
crossStack?: boolean;
|
|
67
73
|
}
|
|
68
74
|
|
|
69
75
|
export interface TfGraph {
|