@intentius/chant 0.52.2 → 0.53.1
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/handlers/op-progress.d.ts +37 -4
- package/dist/cli/handlers/op-progress.d.ts.map +1 -1
- package/dist/cli/handlers/operator.d.ts +61 -1
- package/dist/cli/handlers/operator.d.ts.map +1 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +9 -1
- package/dist/cli/registry.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/lifecycle/converge-ledger.d.ts +81 -1
- package/dist/lifecycle/converge-ledger.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.d.ts +43 -1
- package/dist/lifecycle/gate-ledger.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/handlers/op-progress.test.ts +67 -1
- package/src/cli/handlers/op-progress.ts +70 -14
- package/src/cli/handlers/operator.test.ts +302 -1
- package/src/cli/handlers/operator.ts +193 -5
- package/src/cli/handlers/run.ts +11 -13
- package/src/cli/main.test.ts +19 -0
- package/src/cli/main.ts +38 -10
- package/src/cli/registry.ts +9 -1
- 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/lifecycle/converge-ledger.test.ts +110 -0
- package/src/lifecycle/converge-ledger.ts +110 -2
- package/src/lifecycle/gate-ledger.test.ts +81 -1
- package/src/lifecycle/gate-ledger.ts +64 -1
- 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
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { describe, test, expect } from "vitest";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { cdkNotSupported, isCloudAssembly, readCloudAssembly } from "./assembly";
|
|
4
|
+
import { buildCdkGraph, constructLevel, dummyAssemblyReason } from "./graph";
|
|
5
|
+
import { CFN_TIER_MAP, isScaffoldingParameter, resolveCfnTier } from "./tier-map";
|
|
6
|
+
import { AWS_CARVE_TYPES } from "../terraform/aws-resources";
|
|
7
|
+
|
|
8
|
+
const FIXTURES = join(__dirname, "__fixtures__");
|
|
9
|
+
const ASSEMBLY = join(FIXTURES, "cdk.out");
|
|
10
|
+
const DUMMY = join(FIXTURES, "cdk.out-dummy");
|
|
11
|
+
|
|
12
|
+
describe("cloud assembly detection", () => {
|
|
13
|
+
test("a directory with a manifest and a synthesized template is an assembly", () => {
|
|
14
|
+
expect(isCloudAssembly(ASSEMBLY)).toBe(true);
|
|
15
|
+
expect(isCloudAssembly(DUMMY)).toBe(true);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("a directory that is not one is not claimed", () => {
|
|
19
|
+
// manifest.json alone is far too common a filename to claim on sight; a
|
|
20
|
+
// Terraform estate and a missing directory are both plainly not assemblies.
|
|
21
|
+
expect(isCloudAssembly(join(__dirname, "..", "terraform", "__fixtures__", "sample-estate"))).toBe(false);
|
|
22
|
+
expect(isCloudAssembly(join(FIXTURES, "definitely-not-here"))).toBe(false);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("the Terraform-only phases refuse an assembly by name", () => {
|
|
26
|
+
const message = cdkNotSupported("./cdk.out", "emit");
|
|
27
|
+
expect(message).toContain("carve emit");
|
|
28
|
+
expect(message).toContain("carve advise --from ./cdk.out");
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe("the CloudFormation tier map", () => {
|
|
33
|
+
test("is the AWS carve-out table read from the other end", () => {
|
|
34
|
+
// Every carvable Terraform type names a CloudFormation type, and that type
|
|
35
|
+
// resolves to the same tier from this side. No second table to keep in step.
|
|
36
|
+
for (const entry of AWS_CARVE_TYPES) {
|
|
37
|
+
expect(resolveCfnTier(entry.nativeType)).toEqual({ tier: entry.tier, mapsTo: entry.nativeType });
|
|
38
|
+
}
|
|
39
|
+
expect(Object.keys(CFN_TIER_MAP).length).toBeGreaterThan(70);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("an unmapped CloudFormation type resolves to nothing", () => {
|
|
43
|
+
expect(resolveCfnTier("AWS::CloudFormation::Stack")).toBeNull();
|
|
44
|
+
expect(resolveCfnTier("AWS::S3::BucketPolicy")).toBeNull();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("synthesizer parameters are told apart from an author's", () => {
|
|
48
|
+
expect(isScaffoldingParameter("BootstrapVersion")).toBe(true);
|
|
49
|
+
expect(isScaffoldingParameter("AssetParameters1a2b3c4dS3BucketA1B2C3")).toBe(true);
|
|
50
|
+
expect(isScaffoldingParameter("EnvName")).toBe(false);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe("construct levels", () => {
|
|
55
|
+
test("classify by jsii class", () => {
|
|
56
|
+
expect(constructLevel("aws-cdk-lib.aws_s3.CfnBucket")).toBe("l1");
|
|
57
|
+
expect(constructLevel("aws-cdk-lib.aws_s3.Bucket")).toBe("l2");
|
|
58
|
+
expect(constructLevel("@aws-cdk/aws-s3.Bucket")).toBe("l2");
|
|
59
|
+
expect(constructLevel("constructs.Construct")).toBe("l3");
|
|
60
|
+
expect(constructLevel("my-app.ApiConstruct")).toBe("l3");
|
|
61
|
+
expect(constructLevel("aws-cdk-lib.NestedStack")).toBe("nested");
|
|
62
|
+
expect(constructLevel("aws-cdk-lib.Stack")).toBe("container");
|
|
63
|
+
expect(constructLevel(undefined)).toBe("unknown");
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe("buildCdkGraph", () => {
|
|
68
|
+
const { graph, signals, diagnostics } = buildCdkGraph(readCloudAssembly(ASSEMBLY));
|
|
69
|
+
const addresses = graph.nodes.map((n) => n.address);
|
|
70
|
+
const node = (address: string) => graph.nodes.find((n) => n.address === address);
|
|
71
|
+
|
|
72
|
+
test("ranks constructs, not CloudFormation resources", () => {
|
|
73
|
+
expect(addresses).toEqual([
|
|
74
|
+
"AppStack/Api",
|
|
75
|
+
"AppStack/Handler",
|
|
76
|
+
"AppStack/Legacy",
|
|
77
|
+
"AppStack/Reports.NestedStack",
|
|
78
|
+
"DataStack/Assets",
|
|
79
|
+
"DataStack/Table",
|
|
80
|
+
]);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("one L2 emitting three resources is one node, with its members named", () => {
|
|
84
|
+
expect(node("AppStack/Handler")).toMatchObject({ kind: "resource", type: "AWS::Lambda::Function" });
|
|
85
|
+
expect(signals.get("AppStack/Handler")?.members?.map((m) => m.type)).toEqual([
|
|
86
|
+
"AWS::Lambda::Function",
|
|
87
|
+
"AWS::IAM::Policy",
|
|
88
|
+
"AWS::IAM::Role",
|
|
89
|
+
]);
|
|
90
|
+
// The role and the policy are not separately rankable — they carve with it.
|
|
91
|
+
expect(addresses).not.toContain("AppStack/Handler/ServiceRole");
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("an L3 subtree is one Composite candidate, ranked as a module", () => {
|
|
95
|
+
expect(node("AppStack/Api")).toMatchObject({ kind: "module" });
|
|
96
|
+
expect(node("AppStack/Api")?.type).toBeUndefined();
|
|
97
|
+
expect(signals.get("AppStack/Api")?.members).toHaveLength(3);
|
|
98
|
+
expect(signals.get("AppStack/Api")?.notes?.join(" ")).toContain("Composite candidate");
|
|
99
|
+
expect(addresses).not.toContain("AppStack/Api/Queue");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("a bare L1 stands on its own", () => {
|
|
103
|
+
expect(node("AppStack/Legacy")).toMatchObject({ kind: "resource", type: "AWS::S3::Bucket" });
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test("synthesis scaffolding never appears", () => {
|
|
107
|
+
expect(addresses.some((a) => a.includes("CDKMetadata"))).toBe(false);
|
|
108
|
+
for (const members of [...signals.values()].map((s) => s.members ?? [])) {
|
|
109
|
+
expect(members.some((m) => m.type === "AWS::CDK::Metadata")).toBe(false);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("references inside one construct are not edges", () => {
|
|
114
|
+
// The bucket policy Refs its bucket and the function Refs its own role;
|
|
115
|
+
// both are internal to a carve set, so neither is boundary work.
|
|
116
|
+
const internal = graph.edges.filter((e) => e.from === e.to);
|
|
117
|
+
expect(internal).toHaveLength(0);
|
|
118
|
+
expect(graph.edges.filter((e) => e.from === "DataStack/Assets")).toHaveLength(0);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("an intra-stack Ref is an edge carrying the property it sits in", () => {
|
|
122
|
+
expect(graph.edges).toContainEqual({
|
|
123
|
+
from: "AppStack/Handler",
|
|
124
|
+
to: "AppStack/Api",
|
|
125
|
+
attrs: ["Ref"],
|
|
126
|
+
via: ["Environment"],
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("Fn::ImportValue resolves through the exporting stack's Outputs", () => {
|
|
131
|
+
// Not a dangling export name: the edge lands on the construct that
|
|
132
|
+
// actually produces the value, in the other stack.
|
|
133
|
+
expect(graph.edges).toContainEqual({
|
|
134
|
+
from: "AppStack/Handler",
|
|
135
|
+
to: "DataStack/Assets",
|
|
136
|
+
attrs: ["Export"],
|
|
137
|
+
via: ["PolicyDocument"],
|
|
138
|
+
crossStack: true,
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("an output whose export is imported is not counted a second time", () => {
|
|
143
|
+
// DataStack exports the bucket ARN and AppStack imports it. That is one
|
|
144
|
+
// dependency, already drawn between the two constructs, so the output
|
|
145
|
+
// block does not also charge the bucket.
|
|
146
|
+
const outputs = graph.edges.filter((e) => e.fromKind === "output");
|
|
147
|
+
expect(outputs.map((e) => e.from)).toEqual(["output.AppStack.QueueUrl", "output.DataStack.TableName"]);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test("a stack output nobody imports is an output edge on what it reads", () => {
|
|
151
|
+
expect(graph.edges).toContainEqual({
|
|
152
|
+
from: "output.DataStack.TableName",
|
|
153
|
+
to: "DataStack/Table",
|
|
154
|
+
attrs: ["Ref"],
|
|
155
|
+
via: ["Value"],
|
|
156
|
+
fromKind: "output",
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test("a CloudFormation Condition or an author's parameter marks a construct dynamic", () => {
|
|
161
|
+
expect(node("AppStack/Legacy")?.hasDynamic).toBe(true);
|
|
162
|
+
expect(node("DataStack/Assets")?.hasDynamic).toBe(false);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test("an asset-backed construct carries a penalty and says why", () => {
|
|
166
|
+
expect(signals.get("AppStack/Handler")?.penalties).toEqual({ asset: -10 });
|
|
167
|
+
expect(signals.get("AppStack/Handler")?.notes?.join(" ")).toContain("Asset-backed (Code)");
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test("a nested stack is disqualified rather than scored", () => {
|
|
171
|
+
expect(signals.get("AppStack/Reports.NestedStack")?.disqualified).toContain("Nested stack");
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test("a complete assembly has nothing to report about the read", () => {
|
|
175
|
+
expect(diagnostics).toEqual([]);
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
describe("a dummy-value assembly", () => {
|
|
180
|
+
const assembly = readCloudAssembly(DUMMY);
|
|
181
|
+
|
|
182
|
+
test("is recognized from the manifest's unresolved lookups", () => {
|
|
183
|
+
const reason = dummyAssemblyReason(assembly);
|
|
184
|
+
expect(reason).toContain("unresolved context lookup");
|
|
185
|
+
expect(reason).toContain("vpc-provider");
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test("is recognized from a placeholder left in a template", () => {
|
|
189
|
+
// The same verdict with the manifest's admission removed: the template
|
|
190
|
+
// still holds the value the lookup would have replaced.
|
|
191
|
+
const withoutMissing = { ...assembly, manifest: { ...assembly.manifest, missing: [] } };
|
|
192
|
+
expect(dummyAssemblyReason(withoutMissing)).toContain("vpc-12345678");
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test("disqualifies every construct in it", () => {
|
|
196
|
+
const { signals } = buildCdkGraph(assembly);
|
|
197
|
+
expect(signals.size).toBeGreaterThan(0);
|
|
198
|
+
for (const signal of signals.values()) expect(signal.disqualified).toContain("unresolved context lookup");
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test("without tree.json, grouping falls back to the construct path and says so", () => {
|
|
202
|
+
const { graph, diagnostics } = buildCdkGraph(assembly);
|
|
203
|
+
expect(graph.nodes.map((n) => n.address)).toEqual(["LookupStack/AppBucket", "LookupStack/AppSg"]);
|
|
204
|
+
expect(diagnostics.join(" ")).toContain("No tree.json");
|
|
205
|
+
});
|
|
206
|
+
});
|