@intentius/chant-lexicon-fly 0.124.67 → 0.124.68
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 -1
- package/dist/components/fly-release.d.ts +22 -3
- package/dist/components/fly-release.d.ts.map +1 -1
- package/dist/components/fly-release.js +86 -38
- package/dist/describe-resources.d.ts.map +1 -1
- package/dist/describe-resources.js +8 -3
- package/dist/integrity.json +6 -6
- package/dist/lint/audit-catalog.js +1 -1
- package/dist/lint/post-synth/fly011-mount-references-declared-volume.d.ts +4 -0
- package/dist/lint/post-synth/fly011-mount-references-declared-volume.d.ts.map +1 -1
- package/dist/lint/post-synth/fly011-mount-references-declared-volume.js +10 -2
- package/dist/manifest.json +1 -1
- package/dist/okf/index.md +1 -1
- package/dist/okf/rules/FLY011.md +2 -2
- package/dist/okf/types/Machine.md +1 -1
- package/dist/okf/types/Volume.md +1 -1
- package/dist/op/activities/fly-apply.d.ts +91 -1
- package/dist/op/activities/fly-apply.d.ts.map +1 -1
- package/dist/op/activities/fly-apply.js +165 -7
- package/dist/op/activities/machine-release.d.ts +105 -4
- package/dist/op/activities/machine-release.d.ts.map +1 -1
- package/dist/op/activities/machine-release.js +147 -8
- package/dist/op/activities/machines-fake.d.ts +30 -0
- package/dist/op/activities/machines-fake.d.ts.map +1 -1
- package/dist/op/activities/machines-fake.js +60 -3
- package/dist/rules/fly011-mount-references-declared-volume.ts +9 -2
- package/dist/skills/chant-fly-ops.md +26 -2
- package/dist/skills/chant-fly-patterns.md +4 -2
- package/dist/skills/chant-fly.md +1 -1
- package/dist/volume-id.d.ts +9 -0
- package/dist/volume-id.d.ts.map +1 -0
- package/dist/volume-id.js +10 -0
- package/package.json +3 -3
- package/src/components/fly-release.test.ts +231 -0
- package/src/components/fly-release.ts +106 -39
- package/src/describe-resources.test.ts +27 -0
- package/src/describe-resources.ts +9 -2
- package/src/lint/audit-catalog.ts +1 -1
- package/src/lint/post-synth/fly011-mount-references-declared-volume.test.ts +31 -0
- package/src/lint/post-synth/fly011-mount-references-declared-volume.ts +9 -2
- package/src/op/activities/fly-apply.test.ts +170 -2
- package/src/op/activities/fly-apply.ts +229 -9
- package/src/op/activities/machine-release.test.ts +175 -1
- package/src/op/activities/machine-release.ts +222 -12
- package/src/op/activities/machines-fake.ts +89 -3
- package/src/op/activities/machines-local.test.ts +1 -0
- package/src/skills/chant-fly-ops.md +26 -2
- package/src/skills/chant-fly-patterns.md +4 -2
- package/src/skills/chant-fly.md +1 -1
- package/src/volume-id.ts +11 -0
package/README.md
CHANGED
|
@@ -85,7 +85,7 @@ Lint rules run during `chant build`, before anything reaches the API:
|
|
|
85
85
|
|
|
86
86
|
- `region` must be a real Fly region.
|
|
87
87
|
- Guest sizing (`cpu_kind` / `cpus` / `memory_mb`) must be a valid combination.
|
|
88
|
-
- Every machine mount must reference a `Volume` declared in the stack, checked across files.
|
|
88
|
+
- Every machine mount must reference a `Volume` declared in the stack, checked across files, or give an existing volume's id (`vol_...`).
|
|
89
89
|
- Secret values may not be written inline; they belong in a `Secret` or a reference.
|
|
90
90
|
|
|
91
91
|
## The deploy Op
|
|
@@ -29,8 +29,19 @@
|
|
|
29
29
|
* 3. verify: the Machine is started with this release, and its health
|
|
30
30
|
* endpoint answers when a URL is given;
|
|
31
31
|
* 4. on a failure after the Machine changed, restore: the config it replaced
|
|
32
|
-
* goes back (or, on a first release, the Machine is stopped), and
|
|
33
|
-
*
|
|
32
|
+
* goes back (or, on a first release, the Machine is stopped), and so does
|
|
33
|
+
* the old config of each other Machine the release updated, and the step
|
|
34
|
+
* fails. A failure partway through the apply restores the Machines it had
|
|
35
|
+
* already updated the same way.
|
|
36
|
+
*
|
|
37
|
+
* Given `checks` (#4719), the apply rolls the plan's Machines one at a time on
|
|
38
|
+
* their Fly checks: each Machine it starts must pass its checks
|
|
39
|
+
* (`checks[].status` on the Machine, reported since its update) before the
|
|
40
|
+
* next is updated, within `checks.timeoutMs` (default five minutes). A Machine
|
|
41
|
+
* with no checks is done once started. Verify then also needs every Machine
|
|
42
|
+
* the release started to pass its checks, and a failure restores the Machines
|
|
43
|
+
* the release changed last changed first, each restore waiting for its
|
|
44
|
+
* Machine's checks. Without `checks`, only `started` is waited for.
|
|
34
45
|
*
|
|
35
46
|
* A scheduled Machine (`config.schedule`, #4696) is released without being
|
|
36
47
|
* started, whether it is the one that serves or another Machine in the plan:
|
|
@@ -53,7 +64,7 @@
|
|
|
53
64
|
*/
|
|
54
65
|
import type { Capability, DeployContext } from "@intentius/chant/components/capability";
|
|
55
66
|
import type { ReceiptStore, EffectReceiptRef } from "@intentius/chant/op/receipt-store";
|
|
56
|
-
import { type MachineFile, type MachineRelease } from "../op/activities/machine-release.js";
|
|
67
|
+
import { type MachineChecksOpts, type MachineFile, type MachineRelease } from "../op/activities/machine-release.js";
|
|
57
68
|
import { type FlyHttp, type WaitOpts } from "../op/activities/fly-apply.js";
|
|
58
69
|
import type { MachineConfigStore } from "../release-store.js";
|
|
59
70
|
import { type ReleaseIdentity } from "@intentius/chant/telemetry-attribution";
|
|
@@ -126,6 +137,14 @@ export interface FlyReleaseInput {
|
|
|
126
137
|
/** Migrations, in order. */
|
|
127
138
|
migrations?: FlyMigration[];
|
|
128
139
|
verify?: FlyVerify;
|
|
140
|
+
/**
|
|
141
|
+
* Roll the Machines one at a time on their Fly checks (#4719): each Machine
|
|
142
|
+
* the release starts must pass its checks before the next is updated, and
|
|
143
|
+
* verify reads every one's checks. `true` takes the defaults: five minutes
|
|
144
|
+
* per Machine, polled every second. A Machine with no checks configured is
|
|
145
|
+
* done once started. Without it, only `started` is waited for.
|
|
146
|
+
*/
|
|
147
|
+
checks?: MachineChecksOpts | true;
|
|
129
148
|
/** flaps endpoint override. Default: `FLY_FLAPS_BASE_URL`, else real Fly. */
|
|
130
149
|
endpoint?: string;
|
|
131
150
|
wait?: WaitOpts;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fly-release.d.ts","sourceRoot":"","sources":["../../src/components/fly-release.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"fly-release.d.ts","sourceRoot":"","sources":["../../src/components/fly-release.ts"],"names":[],"mappings":"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"}
|
|
@@ -29,8 +29,19 @@
|
|
|
29
29
|
* 3. verify: the Machine is started with this release, and its health
|
|
30
30
|
* endpoint answers when a URL is given;
|
|
31
31
|
* 4. on a failure after the Machine changed, restore: the config it replaced
|
|
32
|
-
* goes back (or, on a first release, the Machine is stopped), and
|
|
33
|
-
*
|
|
32
|
+
* goes back (or, on a first release, the Machine is stopped), and so does
|
|
33
|
+
* the old config of each other Machine the release updated, and the step
|
|
34
|
+
* fails. A failure partway through the apply restores the Machines it had
|
|
35
|
+
* already updated the same way.
|
|
36
|
+
*
|
|
37
|
+
* Given `checks` (#4719), the apply rolls the plan's Machines one at a time on
|
|
38
|
+
* their Fly checks: each Machine it starts must pass its checks
|
|
39
|
+
* (`checks[].status` on the Machine, reported since its update) before the
|
|
40
|
+
* next is updated, within `checks.timeoutMs` (default five minutes). A Machine
|
|
41
|
+
* with no checks is done once started. Verify then also needs every Machine
|
|
42
|
+
* the release started to pass its checks, and a failure restores the Machines
|
|
43
|
+
* the release changed last changed first, each restore waiting for its
|
|
44
|
+
* Machine's checks. Without `checks`, only `started` is waited for.
|
|
34
45
|
*
|
|
35
46
|
* A scheduled Machine (`config.schedule`, #4696) is released without being
|
|
36
47
|
* started, whether it is the one that serves or another Machine in the plan:
|
|
@@ -52,7 +63,7 @@
|
|
|
52
63
|
* call and refuses a recorded config that does not carry exactly that tree.
|
|
53
64
|
*/
|
|
54
65
|
import { createHash } from "node:crypto";
|
|
55
|
-
import { findMachine, flyMachineExec, flyMachineRelease, flyMachineRestart, flyMachineRestore, flyMachineStop, flyMachineVerify, readMachineRelease, releaseTarget, } from "../op/activities/machine-release.js";
|
|
66
|
+
import { findMachine, flyMachineChecks, flyMachineExec, flyMachineRelease, flyMachineRestart, flyMachineRestore, flyMachineStop, flyMachineVerify, readMachineRelease, releaseTarget, MachineReleaseError, } from "../op/activities/machine-release.js";
|
|
56
67
|
import { defaultFlyHttp, machineSchedule, parsePlan, resolveEndpoint, skipsLaunch } from "../op/activities/fly-apply.js";
|
|
57
68
|
import { readFileSync } from "node:fs";
|
|
58
69
|
import { releaseAttributesSuffix } from "@intentius/chant/telemetry-attribution";
|
|
@@ -139,17 +150,36 @@ const uriOf = (app, machine) => `fly://${app}/${machine}`;
|
|
|
139
150
|
export function createFlyReleaseCapability(deps = {}) {
|
|
140
151
|
const log = deps.log ?? ((line) => console.error(line));
|
|
141
152
|
const httpFor = () => deps.http ?? defaultFlyHttp();
|
|
142
|
-
/**
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
153
|
+
/**
|
|
154
|
+
* On a failed release, put each Machine it changed back. The serving
|
|
155
|
+
* Machine gets its old config, or is stopped when it served nothing before;
|
|
156
|
+
* another Machine it updated gets its old config.
|
|
157
|
+
*
|
|
158
|
+
* Without `checks` this is the order and the calls fly-release made before
|
|
159
|
+
* #4719: the serving Machine first, then the others in plan order. With
|
|
160
|
+
* `checks`, the last Machine changed goes back first, and each restore
|
|
161
|
+
* waits for its Machine's checks before the next.
|
|
162
|
+
*/
|
|
163
|
+
async function restoreApplied(applied, input, http, rolling, servingWas) {
|
|
164
|
+
const order = rolling ? [...applied].reverse() : [...applied.filter((m) => m.serving), ...applied.filter((m) => !m.serving)];
|
|
165
|
+
for (const m of order) {
|
|
166
|
+
if (m.serving ? rolling && m.action === "noop" : m.action !== "updated" || !m.previous)
|
|
146
167
|
continue;
|
|
168
|
+
const where = { app: m.app, machine: m.name, endpoint: input.endpoint, wait: input.wait };
|
|
147
169
|
try {
|
|
148
|
-
|
|
149
|
-
|
|
170
|
+
if (m.previous) {
|
|
171
|
+
await flyMachineRestore({ ...where, config: m.previous, ...(rolling ? { checks: rolling } : {}) }, undefined, http);
|
|
172
|
+
const was = m.serving ? (servingWas === undefined ? readMachineRelease(m.previous.metadata)?.digest : (servingWas ?? undefined)) : undefined;
|
|
173
|
+
log(`fly-release: restored ${m.app}/${m.name} to ${was ?? "its previous config"}`);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
await flyMachineStop(where, undefined, http);
|
|
177
|
+
log(`fly-release: nothing had been released on ${m.app}/${m.name}; stopped it`);
|
|
178
|
+
}
|
|
150
179
|
}
|
|
151
180
|
catch (restoreErr) {
|
|
152
|
-
|
|
181
|
+
const why = restoreErr instanceof Error ? restoreErr.message : String(restoreErr);
|
|
182
|
+
log(m.serving && !rolling ? `fly-release: restore failed: ${why}` : `fly-release: restore of ${m.app}/${m.name} failed: ${why}`);
|
|
153
183
|
}
|
|
154
184
|
}
|
|
155
185
|
}
|
|
@@ -203,17 +233,29 @@ export function createFlyReleaseCapability(deps = {}) {
|
|
|
203
233
|
...(attributes !== undefined ? { OTEL_RESOURCE_ATTRIBUTES: attributes } : {}),
|
|
204
234
|
...(input.env ?? {}),
|
|
205
235
|
};
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
236
|
+
const rolling = input.checks === true ? {} : input.checks;
|
|
237
|
+
let released;
|
|
238
|
+
try {
|
|
239
|
+
released = await flyMachineRelease({
|
|
240
|
+
plan,
|
|
241
|
+
machine: input.machine,
|
|
242
|
+
release: { digest: input.digest, gitSha, ...(input.release ? { release: input.release } : {}) },
|
|
243
|
+
image: input.image,
|
|
244
|
+
...(input.images ? { images: input.images } : {}),
|
|
245
|
+
env,
|
|
246
|
+
...(source ? { files: source.files, cmd: source.cmd } : {}),
|
|
247
|
+
endpoint: input.endpoint,
|
|
248
|
+
wait: input.wait,
|
|
249
|
+
...(rolling ? { checks: rolling } : {}),
|
|
250
|
+
}, undefined, http);
|
|
251
|
+
}
|
|
252
|
+
catch (err) {
|
|
253
|
+
// A Machine failed to settle or to pass its checks partway through the
|
|
254
|
+
// roll: put back every Machine changed so far, then fail.
|
|
255
|
+
if (err instanceof MachineReleaseError)
|
|
256
|
+
await restoreApplied(err.applied, input, http, rolling);
|
|
257
|
+
throw err;
|
|
258
|
+
}
|
|
217
259
|
const where = { app: released.app, machine: released.machine.name, endpoint: input.endpoint };
|
|
218
260
|
log(`fly-release: ${released.app}/${released.machine.name} ${released.action}, serving ${input.digest}${released.unlaunched ? ", stopped until its schedule starts it" : ""}`);
|
|
219
261
|
for (const other of released.others) {
|
|
@@ -250,25 +292,31 @@ export function createFlyReleaseCapability(deps = {}) {
|
|
|
250
292
|
if (migrations.some((m) => m.fired))
|
|
251
293
|
await flyMachineRestart({ ...where, wait: input.wait }, undefined, http);
|
|
252
294
|
}
|
|
253
|
-
|
|
295
|
+
const serving = released.applied.find((m) => m.serving);
|
|
296
|
+
await flyMachineVerify({
|
|
297
|
+
...where,
|
|
298
|
+
digest: input.digest,
|
|
299
|
+
...(input.verify ?? {}),
|
|
300
|
+
...(rolling ? { checks: true, ...(serving?.updatedAt ? { checksSince: serving.updatedAt } : {}) } : {}),
|
|
301
|
+
}, undefined, http, deps.fetch);
|
|
302
|
+
// With checks, every other Machine the release started serves too: started, with its checks passing.
|
|
303
|
+
for (const m of rolling ? released.applied : []) {
|
|
304
|
+
if (m.serving || m.action === "noop" || m.unlaunched)
|
|
305
|
+
continue;
|
|
306
|
+
await flyMachineChecks({
|
|
307
|
+
app: m.app,
|
|
308
|
+
machine: m.name,
|
|
309
|
+
endpoint: input.endpoint,
|
|
310
|
+
timeoutMs: input.verify?.timeoutMs ?? 120000,
|
|
311
|
+
intervalMs: input.verify?.intervalMs,
|
|
312
|
+
...(m.updatedAt ? { since: m.updatedAt } : {}),
|
|
313
|
+
}, undefined, http);
|
|
314
|
+
}
|
|
254
315
|
}
|
|
255
316
|
catch (err) {
|
|
256
|
-
// Restore: the
|
|
257
|
-
// when it served nothing), then fail the step.
|
|
258
|
-
|
|
259
|
-
if (released.previous) {
|
|
260
|
-
await flyMachineRestore({ ...where, config: released.previous.config, wait: input.wait }, undefined, http);
|
|
261
|
-
log(`fly-release: restored ${released.app}/${released.machine.name} to ${previousRelease?.digest ?? "its previous config"}`);
|
|
262
|
-
}
|
|
263
|
-
else {
|
|
264
|
-
await flyMachineStop({ ...where, wait: input.wait }, undefined, http);
|
|
265
|
-
log(`fly-release: nothing had been released on ${released.app}/${released.machine.name}; stopped it`);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
catch (restoreErr) {
|
|
269
|
-
log(`fly-release: restore failed: ${restoreErr instanceof Error ? restoreErr.message : String(restoreErr)}`);
|
|
270
|
-
}
|
|
271
|
-
await restoreOthers(released, input, http);
|
|
317
|
+
// Restore: the Machines changed, so put back what each served (or stop
|
|
318
|
+
// the serving one when it served nothing), then fail the step.
|
|
319
|
+
await restoreApplied(released.applied, input, http, rolling, previousRelease?.digest ?? null);
|
|
272
320
|
throw err;
|
|
273
321
|
}
|
|
274
322
|
await configs.write({ app: released.app, machine: released.machine.name, digest: input.digest }, released.config);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"describe-resources.d.ts","sourceRoot":"","sources":["../src/describe-resources.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAoB,MAAM,0BAA0B,CAAC;AAKtG,OAAO,EAGL,KAAK,SAAS,EACf,MAAM,uCAAuC,CAAC;AAC/C,OAAO,
|
|
1
|
+
{"version":3,"file":"describe-resources.d.ts","sourceRoot":"","sources":["../src/describe-resources.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAoB,MAAM,0BAA0B,CAAC;AAKtG,OAAO,EAGL,KAAK,SAAS,EACf,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAuBL,KAAK,OAAO,EAIb,MAAM,2BAA2B,CAAC;AAcnC,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;IAC9E,gDAAgD;IAChD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AA2BD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,GAC5E,MAAM,CA8BR;AAED;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,wBAAwB,EACjC,IAAI,GAAE,OAA0B,EAChC,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,iBAAiB,CAAC,CA2S5B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,OAAO,CAC3B,OAAO,EAAE,wBAAwB,GAAG;IAClC,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CACjD,EACD,IAAI,GAAE,OAA0B,EAChC,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC;IAAE,SAAS,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAuCrD"}
|
|
@@ -31,7 +31,7 @@ import { readOwnership } from "@intentius/chant/ownership";
|
|
|
31
31
|
import { FLY_METADATA_OWNERSHIP_KEYS } from "./ownership.js";
|
|
32
32
|
import { readMachineRelease } from "./release-metadata.js";
|
|
33
33
|
import { buildChangeSet, renderChangeSet, } from "@intentius/chant/lifecycle/change-set";
|
|
34
|
-
import { resolveEndpoint, defaultFlyHttp, parsePlan, isAppRequest, isMachineRequest, isVolumeRequest, isIpRequest, isCertRequest, appNameFromRequest, machineAppSegment, resourceAppSegment, resolveApp, isChantOwned, configEqual, listMachines, listVolumes, listIps, listCerts, ipType, declaredIpType, } from "./op/activities/fly-apply.js";
|
|
34
|
+
import { resolveEndpoint, defaultFlyHttp, parsePlan, isAppRequest, isMachineRequest, isVolumeRequest, isIpRequest, isCertRequest, appNameFromRequest, machineAppSegment, resourceAppSegment, resolveApp, isChantOwned, configEqual, alignMountsWithLive, volumeAliases, listMachines, listVolumes, listIps, listCerts, ipType, declaredIpType, } from "./op/activities/fly-apply.js";
|
|
35
35
|
/** The chant entity types the fly serializer emits, for the metadata `type` field. */
|
|
36
36
|
const TYPE = {
|
|
37
37
|
app: "Fly::Machines::App",
|
|
@@ -419,8 +419,11 @@ export async function flyPlan(options, http = defaultFlyHttp(), signal) {
|
|
|
419
419
|
});
|
|
420
420
|
// Declared config per entity, for the machine update/noop refinement.
|
|
421
421
|
const declaredConfig = new Map();
|
|
422
|
+
let aliases = new Map();
|
|
422
423
|
try {
|
|
423
|
-
|
|
424
|
+
const plan = parsePlan(options.buildOutput);
|
|
425
|
+
aliases = volumeAliases(plan);
|
|
426
|
+
for (const [entityName, req] of Object.entries(plan)) {
|
|
424
427
|
if (isMachineRequest(req))
|
|
425
428
|
declaredConfig.set(entityName, req.body.config);
|
|
426
429
|
}
|
|
@@ -435,7 +438,9 @@ export async function flyPlan(options, http = defaultFlyHttp(), signal) {
|
|
|
435
438
|
if (declaredCfg === undefined)
|
|
436
439
|
continue; // not a declared machine
|
|
437
440
|
const liveCfg = observedNow[entry.name]?.attributes?.config;
|
|
438
|
-
|
|
441
|
+
// A declared mount names its volume; the live one has the id flyApply
|
|
442
|
+
// looked up, and the name beside it (#4717).
|
|
443
|
+
if (!configEqual(alignMountsWithLive(declaredCfg, liveCfg, aliases), liveCfg)) {
|
|
439
444
|
entry.action = "update";
|
|
440
445
|
}
|
|
441
446
|
}
|
package/dist/integrity.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"algorithm": "sha256",
|
|
3
3
|
"artifacts": {
|
|
4
|
-
"manifest.json": "
|
|
4
|
+
"manifest.json": "63239012af86d5a710a882477741546f0533f5397908b9a38c6fc4c47acdacdc",
|
|
5
5
|
"meta.json": "273f2a523b66f08ca91642dba2592c44765ab59ddf7329998884a0b08c6c3f24",
|
|
6
6
|
"types/index.d.ts": "75c29d3ba9faa7c2792a0a25c51dc13df011906ffbd4c9e3ccc80023f5f32667",
|
|
7
7
|
"rules/guest-sizing.ts": "8dd2db79a47ceab3d488f84f560aed1b1e4a66b4e36bf11f3baf6a8544d6e126",
|
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
"rules/valid-region.ts": "238afb252ffb59ef131d070a705a49a1ac87b98f6a86c029a69dc82200e1aabf",
|
|
10
10
|
"rules/fly-helpers.ts": "dd29d4b520727210a56ef604a89df380f91f8b35770bc28f5c51d0f99ca92dff",
|
|
11
11
|
"rules/fly010-machine-requires-image.ts": "dd9b70ee604867a9f02334013476afcf44a78b49bbba3d66296b8ccc2b02c8a0",
|
|
12
|
-
"rules/fly011-mount-references-declared-volume.ts": "
|
|
12
|
+
"rules/fly011-mount-references-declared-volume.ts": "b54c4bb4d29cdd13ca3ff5e9e6e9b2870703ccbb23856c2be8f6983f6833b61e",
|
|
13
13
|
"rules/fly012-machine-schedule.ts": "f94c75d64dd3888797c89da4e5dd11d95c47d45ff19a69a725cf098f705003b1",
|
|
14
|
-
"skills/chant-fly.md": "
|
|
15
|
-
"skills/chant-fly-patterns.md": "
|
|
16
|
-
"skills/chant-fly-ops.md": "
|
|
14
|
+
"skills/chant-fly.md": "a81a2f60b593f311b6553a1b4aa2578ac843f1539f9496ba58a8309ed0357196",
|
|
15
|
+
"skills/chant-fly-patterns.md": "25156b507116c988a304f77900f3c6afabf24dce45b7a9d121a8d23950d7e6cc",
|
|
16
|
+
"skills/chant-fly-ops.md": "cf25cf3ef9fb68fdb320a35c411d893d57bbf0ad4216f571aa9ce42eaebacd24",
|
|
17
17
|
"skills/chant-fly-sprites.md": "3d100680422151b58d082c3a7b30a286edfc52faa4887995c9ad5553873bab95"
|
|
18
18
|
},
|
|
19
|
-
"composite": "
|
|
19
|
+
"composite": "6970212370c06267bda474921a0900b82b43dfc1bb20bca8b21f21b3dfe0bf99"
|
|
20
20
|
}
|
|
@@ -22,7 +22,7 @@ function rule(id, tier, category, title, remediation) {
|
|
|
22
22
|
}
|
|
23
23
|
export const flyAuditCatalog = {
|
|
24
24
|
FLY010: rule("FLY010", "merge-worthy", "correctness", "Machine config has no image", "Set `config.image` on the Machine — a Machine cannot boot without one."),
|
|
25
|
-
FLY011: rule("FLY011", "merge-worthy", "correctness", "Machine mount references an undeclared volume", "Declare the Volume in the same stack,
|
|
25
|
+
FLY011: rule("FLY011", "merge-worthy", "correctness", "Machine mount references an undeclared volume", "Declare the Volume in the same stack, point the mount at one that is, or mount an existing volume by its id (vol_...)."),
|
|
26
26
|
FLY012: rule("FLY012", "merge-worthy", "correctness", "Machine schedule is not one Fly runs", "Set `config.schedule` to `hourly`, `daily`, `weekly` or `monthly`; the Machines API takes no cron expression."),
|
|
27
27
|
};
|
|
28
28
|
// Prior art credits live beside the rules in ./audit-lineage.ts (see core audit/prior-art.ts).
|
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
* apply time, so catch it at synth. This is a whole-stack check: it reads all
|
|
7
7
|
* entities in the build (`ctx.entities`), so the mount and the Volume can be
|
|
8
8
|
* declared in different files.
|
|
9
|
+
*
|
|
10
|
+
* A mount given a volume id (`vol_...`) names a volume that already exists on
|
|
11
|
+
* Fly, which the stack need not declare, so it passes (#4717). `flyApply`
|
|
12
|
+
* sends it as it is, and looks a name up to send its id.
|
|
9
13
|
*/
|
|
10
14
|
import type { PostSynthCheck } from "@intentius/chant/lint/post-synth";
|
|
11
15
|
export declare const fly011: PostSynthCheck;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fly011-mount-references-declared-volume.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/fly011-mount-references-declared-volume.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"fly011-mount-references-declared-volume.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/fly011-mount-references-declared-volume.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAyC,MAAM,kCAAkC,CAAC;AAO9G,eAAO,MAAM,MAAM,EAAE,cA8CpB,CAAC"}
|
|
@@ -6,13 +6,18 @@
|
|
|
6
6
|
* apply time, so catch it at synth. This is a whole-stack check: it reads all
|
|
7
7
|
* entities in the build (`ctx.entities`), so the mount and the Volume can be
|
|
8
8
|
* declared in different files.
|
|
9
|
+
*
|
|
10
|
+
* A mount given a volume id (`vol_...`) names a volume that already exists on
|
|
11
|
+
* Fly, which the stack need not declare, so it passes (#4717). `flyApply`
|
|
12
|
+
* sends it as it is, and looks a name up to send its id.
|
|
9
13
|
*/
|
|
10
14
|
import { readProps, entityTypeOf } from "./fly-helpers.js";
|
|
15
|
+
import { isVolumeId } from "../../volume-id.js";
|
|
11
16
|
const MACHINE_ENTITY_TYPE = "Fly::Machines::Machine";
|
|
12
17
|
const VOLUME_ENTITY_TYPE = "Fly::Machines::Volume";
|
|
13
18
|
export const fly011 = {
|
|
14
19
|
id: "FLY011",
|
|
15
|
-
description: "A machine mount's volume must resolve to a Volume declared in the stack",
|
|
20
|
+
description: "A machine mount's volume must resolve to a Volume declared in the stack, or be a volume id (vol_...)",
|
|
16
21
|
check(ctx) {
|
|
17
22
|
const diagnostics = [];
|
|
18
23
|
// A mount can name a declared volume by the Volume's `name` or by its
|
|
@@ -42,11 +47,14 @@ export const fly011 = {
|
|
|
42
47
|
// never dangles — skip it to avoid false positives.
|
|
43
48
|
if (typeof volume !== "string" || volume.length === 0)
|
|
44
49
|
continue;
|
|
50
|
+
// A volume id names a volume that exists outside the stack.
|
|
51
|
+
if (isVolumeId(volume))
|
|
52
|
+
continue;
|
|
45
53
|
if (!declaredVolumes.has(volume)) {
|
|
46
54
|
diagnostics.push({
|
|
47
55
|
checkId: "FLY011",
|
|
48
56
|
severity: "error",
|
|
49
|
-
message: `Machine "${machineName}" mounts volume "${volume}", which is not declared as a Volume in the stack.`,
|
|
57
|
+
message: `Machine "${machineName}" mounts volume "${volume}", which is not declared as a Volume in the stack. Declare the Volume, or mount an existing volume by its id (vol_...).`,
|
|
50
58
|
entity: machineName,
|
|
51
59
|
lexicon: "fly",
|
|
52
60
|
});
|
package/dist/manifest.json
CHANGED
package/dist/okf/index.md
CHANGED
|
@@ -17,5 +17,5 @@ okf_version: '0.2'
|
|
|
17
17
|
* [FLY002](/rules/FLY002.md) - Sane guest sizing
|
|
18
18
|
* [FLY004](/rules/FLY004.md) - No secret literals in machine config
|
|
19
19
|
* [FLY010](/rules/FLY010.md) - Machine config must specify an image — a Machine cannot boot without one
|
|
20
|
-
* [FLY011](/rules/FLY011.md) - A machine mount's volume must resolve to a Volume declared in the stack
|
|
20
|
+
* [FLY011](/rules/FLY011.md) - A machine mount's volume must resolve to a Volume declared in the stack, or be a volume id (vol_...)
|
|
21
21
|
* [FLY012](/rules/FLY012.md) - Machine schedule must be hourly, daily, weekly or monthly
|
package/dist/okf/rules/FLY011.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
type: post-synth-check
|
|
3
3
|
title: FLY011
|
|
4
|
-
description: A machine mount's volume must resolve to a Volume declared in the stack
|
|
4
|
+
description: A machine mount's volume must resolve to a Volume declared in the stack, or be a volume id (vol_...)
|
|
5
5
|
id: FLY011
|
|
6
6
|
severity: error
|
|
7
7
|
category: post-synth
|
|
8
8
|
lexicon: fly
|
|
9
9
|
docs: https://intentius.io/chant/lexicons/fly/rules/
|
|
10
10
|
---
|
|
11
|
-
A machine mount's volume must resolve to a Volume declared in the stack
|
|
11
|
+
A machine mount's volume must resolve to a Volume declared in the stack, or be a volume id (vol_...)
|
|
12
12
|
|
|
13
13
|
## Applies to
|
|
14
14
|
|
|
@@ -22,5 +22,5 @@ resource_type: Fly::Machines::Machine
|
|
|
22
22
|
## Governed by
|
|
23
23
|
|
|
24
24
|
- [FLY010](/rules/FLY010.md): Machine config must specify an image — a Machine cannot boot without one
|
|
25
|
-
- [FLY011](/rules/FLY011.md): A machine mount's volume must resolve to a Volume declared in the stack
|
|
25
|
+
- [FLY011](/rules/FLY011.md): A machine mount's volume must resolve to a Volume declared in the stack, or be a volume id (vol_...)
|
|
26
26
|
- [FLY012](/rules/FLY012.md): Machine schedule must be hourly, daily, weekly or monthly
|
package/dist/okf/types/Volume.md
CHANGED
|
@@ -26,4 +26,4 @@ resource_type: Fly::Machines::Volume
|
|
|
26
26
|
|
|
27
27
|
## Governed by
|
|
28
28
|
|
|
29
|
-
- [FLY011](/rules/FLY011.md): A machine mount's volume must resolve to a Volume declared in the stack
|
|
29
|
+
- [FLY011](/rules/FLY011.md): A machine mount's volume must resolve to a Volume declared in the stack, or be a volume id (vol_...)
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* `FLY_FLAPS_BASE_URL` (or an explicit `endpoint` arg), so the same code points
|
|
22
22
|
* at real Fly or at mudflaps (`:4280`).
|
|
23
23
|
*/
|
|
24
|
+
export { isVolumeId } from "../../volume-id.js";
|
|
24
25
|
import { type ApplyResult } from "@intentius/chant/apply";
|
|
25
26
|
/** Default flaps host when neither an `endpoint` arg nor `FLY_FLAPS_BASE_URL` is set. */
|
|
26
27
|
export declare const DEFAULT_FLAPS_BASE_URL = "https://api.machines.dev";
|
|
@@ -52,9 +53,34 @@ export interface FlapsMachine {
|
|
|
52
53
|
name: string;
|
|
53
54
|
state: string;
|
|
54
55
|
instance_id: string;
|
|
56
|
+
region?: string;
|
|
55
57
|
config?: {
|
|
56
58
|
metadata?: Record<string, string>;
|
|
57
59
|
} & Record<string, unknown>;
|
|
60
|
+
/** When flaps last changed the Machine, by its own clock (RFC 3339). */
|
|
61
|
+
updated_at?: string;
|
|
62
|
+
/** The status of each of the Machine's Fly checks, as the Machines API reports them (#4719). */
|
|
63
|
+
checks?: FlapsCheckStatus[];
|
|
64
|
+
}
|
|
65
|
+
/** One of a Machine's Fly checks as the Machines API reports it: `passing`, `warning` or `critical`. */
|
|
66
|
+
export interface FlapsCheckStatus {
|
|
67
|
+
name: string;
|
|
68
|
+
status: string;
|
|
69
|
+
output?: string;
|
|
70
|
+
updated_at?: string;
|
|
71
|
+
}
|
|
72
|
+
/** One Machine {@link flyApply} has just created, updated or left alone. */
|
|
73
|
+
export interface AppliedMachine {
|
|
74
|
+
app: string;
|
|
75
|
+
/** The plan entity that declares it. */
|
|
76
|
+
entity: string;
|
|
77
|
+
name: string;
|
|
78
|
+
id: string;
|
|
79
|
+
action: "created" | "updated" | "noop";
|
|
80
|
+
/** True when it was left unstarted (a scheduled Machine, #4696). */
|
|
81
|
+
unlaunched: boolean;
|
|
82
|
+
/** The Machine's `updated_at` as the create or update call answered it, when it changed and flaps gave one. */
|
|
83
|
+
updatedAt?: string;
|
|
58
84
|
}
|
|
59
85
|
/**
|
|
60
86
|
* Resolve the flaps base URL (D3): an explicit `endpoint` arg wins, then the
|
|
@@ -214,11 +240,18 @@ export declare function listMachines(ctx: ApplyCtx, app: string, http: FlyHttp,
|
|
|
214
240
|
*
|
|
215
241
|
* A Machine that {@link skipsLaunch} (a scheduled one, #4696) is created and
|
|
216
242
|
* updated with `skip_launch: true`, and the wait is for it to rest unstarted.
|
|
243
|
+
*
|
|
244
|
+
* A mount that names a volume is sent with the volume's id (#4717), looked up
|
|
245
|
+
* among the app's live volumes ({@link pickVolume}); the comparison with the
|
|
246
|
+
* live config is made after the lookup, so an unchanged Machine stays a no-op.
|
|
217
247
|
*/
|
|
218
|
-
export declare function applyMachine(ctx: ApplyCtx, app: string, entityName: string, req: FlapsRequest, http: FlyHttp, signal?: AbortSignal, opts?: WaitOpts
|
|
248
|
+
export declare function applyMachine(ctx: ApplyCtx, app: string, entityName: string, req: FlapsRequest, http: FlyHttp, signal?: AbortSignal, opts?: WaitOpts & {
|
|
249
|
+
volumeAliases?: ReadonlyMap<string, string>;
|
|
250
|
+
}): Promise<{
|
|
219
251
|
action: "created" | "updated" | "noop";
|
|
220
252
|
id: string;
|
|
221
253
|
name: string;
|
|
254
|
+
updatedAt?: string;
|
|
222
255
|
}>;
|
|
223
256
|
/** How {@link destroyMachine} treats a machine that is still running. */
|
|
224
257
|
export interface DestroyOpts extends WaitOpts {
|
|
@@ -266,6 +299,9 @@ export interface FlapsVolume {
|
|
|
266
299
|
id: string;
|
|
267
300
|
name: string;
|
|
268
301
|
state?: string;
|
|
302
|
+
region?: string;
|
|
303
|
+
/** The Machine the volume is attached to; empty or absent when it is free. */
|
|
304
|
+
attached_machine_id?: string | null;
|
|
269
305
|
}
|
|
270
306
|
/** A live IP assignment as flaps lists it. */
|
|
271
307
|
export interface FlapsIp {
|
|
@@ -291,6 +327,53 @@ export declare function ipType(shared: boolean | undefined, address: string): st
|
|
|
291
327
|
export declare function declaredIpType(type: unknown): string;
|
|
292
328
|
/** List an app's live volumes (flaps returns a bare array). */
|
|
293
329
|
export declare function listVolumes(ctx: ApplyCtx, app: string, http: FlyHttp, signal?: AbortSignal): Promise<FlapsVolume[]>;
|
|
330
|
+
/** The Machine a mount is resolved for: its id once it exists, and its region. */
|
|
331
|
+
export interface MountTarget {
|
|
332
|
+
machineId?: string;
|
|
333
|
+
region?: string;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Plan entity names that name a volume by its declaration, mapped to the
|
|
337
|
+
* volume's Fly name. A mount written as a `Volume` reference serializes to the
|
|
338
|
+
* declaration's name, which can differ from the volume's `name`. An entity
|
|
339
|
+
* name that is also some declared volume's `name` is left out, so a mount
|
|
340
|
+
* naming it means that volume. Pure.
|
|
341
|
+
*/
|
|
342
|
+
export declare function volumeAliases(plan: FlyPlan): Map<string, string>;
|
|
343
|
+
/** True when a config has a mount that names its volume rather than giving its id. Pure. */
|
|
344
|
+
export declare function mountsNeedResolving(config: unknown): boolean;
|
|
345
|
+
/**
|
|
346
|
+
* The volume a mount naming `name` means, for the Machine `target`. Fly lets
|
|
347
|
+
* several volumes share a name (one per Machine, often across regions), so
|
|
348
|
+
* the pick is: the volume of that name already attached to this Machine, else
|
|
349
|
+
* the one in the Machine's region that is attached to no Machine. A volume
|
|
350
|
+
* whose region or attachment flaps doesn't report counts as matching. No
|
|
351
|
+
* volume of that name, none that fits, or more than one that fits, is refused
|
|
352
|
+
* naming the candidates. Pure.
|
|
353
|
+
*/
|
|
354
|
+
export declare function pickVolume(app: string, name: string, volumes: FlapsVolume[], target: MountTarget): FlapsVolume;
|
|
355
|
+
/**
|
|
356
|
+
* A Machine config with each mount that names a volume given that volume's id
|
|
357
|
+
* in `volume` and its name in `name` (a `name` the mount already has is
|
|
358
|
+
* kept). A mount with an id goes through as it is. `aliases` maps a plan's
|
|
359
|
+
* volume declaration names to volume names ({@link volumeAliases}). Returns
|
|
360
|
+
* `config` itself when no mount names a volume. Pure.
|
|
361
|
+
*/
|
|
362
|
+
export declare function resolveMountsWith<T>(app: string, config: T, volumes: FlapsVolume[], target: MountTarget, aliases?: ReadonlyMap<string, string>): T;
|
|
363
|
+
/**
|
|
364
|
+
* {@link resolveMountsWith} over the app's live volumes, listed only when a
|
|
365
|
+
* mount names a volume. Volumes are listed per call, so a volume an earlier
|
|
366
|
+
* Machine in the same apply took is seen as attached.
|
|
367
|
+
*/
|
|
368
|
+
export declare function resolveMounts<T>(ctx: ApplyCtx, app: string, config: T, target: MountTarget, http: FlyHttp, signal?: AbortSignal, aliases?: ReadonlyMap<string, string>): Promise<T>;
|
|
369
|
+
/**
|
|
370
|
+
* A declared config with each mount that names a volume given the id the live
|
|
371
|
+
* config mounts at the same path, when the live mount carries that name. A
|
|
372
|
+
* read-only plan uses it to compare a declared config with a live one the way
|
|
373
|
+
* {@link applyMachine} will after it resolves the names, without listing
|
|
374
|
+
* volumes. Pure.
|
|
375
|
+
*/
|
|
376
|
+
export declare function alignMountsWithLive<T>(declared: T, live: unknown, aliases?: ReadonlyMap<string, string>): T;
|
|
294
377
|
/** List an app's live IP assignments (`{ ips: [...] }`). */
|
|
295
378
|
export declare function listIps(ctx: ApplyCtx, app: string, http: FlyHttp, signal?: AbortSignal): Promise<FlapsIp[]>;
|
|
296
379
|
/**
|
|
@@ -411,6 +494,13 @@ export interface FlyApplyArgs {
|
|
|
411
494
|
force?: boolean;
|
|
412
495
|
/** Wait-loop tuning (mainly for tests). */
|
|
413
496
|
wait?: WaitOpts;
|
|
497
|
+
/**
|
|
498
|
+
* Called after each Machine is applied and settled, before the next one
|
|
499
|
+
* (#4719). `fly-release` waits here for the Machine's Fly checks, so a
|
|
500
|
+
* group of serving Machines rolls one at a time. A throw stops the apply
|
|
501
|
+
* there and fails it. For callers in code only; without it nothing changes.
|
|
502
|
+
*/
|
|
503
|
+
afterMachine?: (machine: AppliedMachine) => Promise<void>;
|
|
414
504
|
}
|
|
415
505
|
export declare function toApplyResult(result: {
|
|
416
506
|
apps: Array<{
|