@intentius/chant-lexicon-aws 0.18.26 → 0.18.27
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/generated/index.d.ts +13 -4
- package/dist/generated/index.d.ts.map +1 -1
- package/dist/integrity.json +4 -4
- package/dist/manifest.json +1 -1
- package/dist/meta.json +212 -20
- package/dist/plugin.d.ts.map +1 -1
- package/dist/types/index.d.ts +193 -29
- package/package.json +3 -3
- package/src/generated/index.d.ts +193 -29
- package/src/generated/index.ts +13 -4
- package/src/generated/lexicon-aws.json +212 -20
- package/src/lifecycle-integration.test.ts +34 -0
- package/src/plugin.ts +29 -1
|
@@ -102,4 +102,38 @@ describe("aws lifecycle integration (#163)", () => {
|
|
|
102
102
|
});
|
|
103
103
|
expect(cs2.entries.find((e) => e.name === "MyBucket")!.action).toBe("noop");
|
|
104
104
|
});
|
|
105
|
+
|
|
106
|
+
describe("describeStackStatus (#57 — per-component stack presence)", () => {
|
|
107
|
+
const err = (stderr: string) => ({ stdout: "", stderr, exitCode: 255 });
|
|
108
|
+
|
|
109
|
+
test("present + healthy for a terminal-success stack", async () => {
|
|
110
|
+
spawnMock.mockResolvedValue(ok(JSON.stringify({ Stacks: [{ StackStatus: "CREATE_COMPLETE" }] })));
|
|
111
|
+
const obs = await awsPlugin.describeStackStatus!({ environment: "local", stack: "loom-local-a-loom-db" });
|
|
112
|
+
expect(obs).toEqual({ stack: "loom-local-a-loom-db", present: true, status: "CREATE_COMPLETE", healthy: true });
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("present but not healthy for a rollback/failed state", async () => {
|
|
116
|
+
spawnMock.mockResolvedValue(ok(JSON.stringify({ Stacks: [{ StackStatus: "ROLLBACK_COMPLETE" }] })));
|
|
117
|
+
const obs = await awsPlugin.describeStackStatus!({ environment: "local", stack: "s" });
|
|
118
|
+
expect(obs).toMatchObject({ present: true, status: "ROLLBACK_COMPLETE", healthy: false });
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("in-progress is present but not yet healthy", async () => {
|
|
122
|
+
spawnMock.mockResolvedValue(ok(JSON.stringify({ Stacks: [{ StackStatus: "UPDATE_IN_PROGRESS" }] })));
|
|
123
|
+
const obs = await awsPlugin.describeStackStatus!({ environment: "local", stack: "s" });
|
|
124
|
+
expect(obs).toMatchObject({ present: true, healthy: false });
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("absent (does-not-exist) reports present: false, not an error", async () => {
|
|
128
|
+
spawnMock.mockResolvedValue(err("ValidationError: Stack with id s does not exist"));
|
|
129
|
+
const obs = await awsPlugin.describeStackStatus!({ environment: "local", stack: "s" });
|
|
130
|
+
expect(obs).toEqual({ stack: "s", present: false });
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("any other CLI failure is indeterminate → null (never a false 'gone')", async () => {
|
|
134
|
+
spawnMock.mockResolvedValue(err("Unable to locate credentials"));
|
|
135
|
+
const obs = await awsPlugin.describeStackStatus!({ environment: "local", stack: "s" });
|
|
136
|
+
expect(obs).toBeNull();
|
|
137
|
+
});
|
|
138
|
+
});
|
|
105
139
|
});
|
package/src/plugin.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createRequire } from "module";
|
|
2
2
|
import { detectTemplate } from "./detect";
|
|
3
|
-
import type { LexiconPlugin, IntrinsicDef, ResourceMetadata, ExportedTemplate, ResourceSelector, InitTemplateSet } from "@intentius/chant/lexicon";
|
|
3
|
+
import type { LexiconPlugin, IntrinsicDef, ResourceMetadata, ExportedTemplate, ResourceSelector, InitTemplateSet, StackStatusObservation } from "@intentius/chant/lexicon";
|
|
4
4
|
const require = createRequire(import.meta.url);
|
|
5
5
|
import type { LintRule } from "@intentius/chant/lint/rule";
|
|
6
6
|
import type { TemplateParser } from "@intentius/chant/import/parser";
|
|
@@ -577,6 +577,34 @@ aws cloudformation wait stack-update-complete --stack-name my-app-prod`,
|
|
|
577
577
|
return resources;
|
|
578
578
|
},
|
|
579
579
|
|
|
580
|
+
async describeStackStatus(options: { environment: string; stack: string }): Promise<StackStatusObservation | null> {
|
|
581
|
+
const { getRuntime } = await import("@intentius/chant/runtime-adapter");
|
|
582
|
+
const rt = getRuntime();
|
|
583
|
+
|
|
584
|
+
const result = await rt.spawn(applyAwsEndpointArgv([
|
|
585
|
+
"aws", "cloudformation", "describe-stacks",
|
|
586
|
+
"--stack-name", options.stack,
|
|
587
|
+
"--output", "json",
|
|
588
|
+
], process.env.AWS_ENDPOINT_URL));
|
|
589
|
+
|
|
590
|
+
if (result.exitCode !== 0) {
|
|
591
|
+
// A stack that doesn't exist yet is the pre-first-apply state (absent, not
|
|
592
|
+
// an error). Any other failure is indeterminate → null, so the caller
|
|
593
|
+
// degrades rather than reporting a healthy stack as gone.
|
|
594
|
+
if (stackDoesNotExist(result.stderr)) return { stack: options.stack, present: false };
|
|
595
|
+
return null;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
const parsed = JSON.parse(result.stdout) as { Stacks?: Array<{ StackStatus?: string }> };
|
|
599
|
+
const status = parsed.Stacks?.[0]?.StackStatus;
|
|
600
|
+
if (!status) return { stack: options.stack, present: false };
|
|
601
|
+
// Healthy = a terminal *success* apply. Rollback/failed/in-progress/delete
|
|
602
|
+
// states are present-but-not-healthy, so a renderer can distinguish
|
|
603
|
+
// deployed-green from mid-deploy or broken.
|
|
604
|
+
const healthy = /^(CREATE|UPDATE|IMPORT)_COMPLETE$/.test(status);
|
|
605
|
+
return { stack: options.stack, present: true, status, healthy };
|
|
606
|
+
},
|
|
607
|
+
|
|
580
608
|
async exportResources(options: {
|
|
581
609
|
environment: string;
|
|
582
610
|
stack?: string;
|