@homeflare/alchemy 0.37.6 → 0.37.7
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/proxmox/firewall-alias-config.d.ts +36 -0
- package/dist/proxmox/firewall-alias-config.d.ts.map +1 -0
- package/dist/proxmox/firewall-alias-form.d.ts +3 -1
- package/dist/proxmox/firewall-alias-form.d.ts.map +1 -1
- package/dist/proxmox/firewall-alias-lifecycle.d.ts +43 -0
- package/dist/proxmox/firewall-alias-lifecycle.d.ts.map +1 -0
- package/dist/proxmox/firewall-alias.d.ts +2 -8
- package/dist/proxmox/firewall-alias.d.ts.map +1 -1
- package/dist/proxmox/index.js +795 -526
- package/dist/proxmox/index.js.map +23 -10
- package/dist/proxmox/lxc-errors.d.ts +12 -0
- package/dist/proxmox/lxc-errors.d.ts.map +1 -0
- package/dist/proxmox/lxc-lifecycle.d.ts +9 -31
- package/dist/proxmox/lxc-lifecycle.d.ts.map +1 -1
- package/dist/proxmox/lxc-read.d.ts +4 -0
- package/dist/proxmox/lxc-read.d.ts.map +1 -0
- package/dist/proxmox/lxc-task.d.ts +15 -0
- package/dist/proxmox/lxc-task.d.ts.map +1 -0
- package/dist/proxmox/network-apply-read.d.ts +7 -14
- package/dist/proxmox/network-apply-read.d.ts.map +1 -1
- package/dist/proxmox/network-apply-run.d.ts +7 -0
- package/dist/proxmox/network-apply-run.d.ts.map +1 -0
- package/dist/proxmox/network-apply.d.ts +2 -3
- package/dist/proxmox/network-apply.d.ts.map +1 -1
- package/dist/proxmox/qemu-errors.d.ts +5 -0
- package/dist/proxmox/qemu-errors.d.ts.map +1 -0
- package/dist/proxmox/qemu-form.d.ts +22 -0
- package/dist/proxmox/qemu-form.d.ts.map +1 -0
- package/dist/proxmox/qemu-lifecycle.d.ts +26 -0
- package/dist/proxmox/qemu-lifecycle.d.ts.map +1 -0
- package/dist/proxmox/qemu-read.d.ts +5 -0
- package/dist/proxmox/qemu-read.d.ts.map +1 -0
- package/dist/proxmox/qemu-task.d.ts +9 -0
- package/dist/proxmox/qemu-task.d.ts.map +1 -0
- package/dist/proxmox/qemu.d.ts +9 -14
- package/dist/proxmox/qemu.d.ts.map +1 -1
- package/dist/proxmox/replication-firewall-fixtures.d.ts +30 -0
- package/dist/proxmox/replication-firewall-fixtures.d.ts.map +1 -0
- package/dist/proxmox/replication-job-config.d.ts +56 -0
- package/dist/proxmox/replication-job-config.d.ts.map +1 -0
- package/dist/proxmox/replication-job-lifecycle.d.ts +62 -0
- package/dist/proxmox/replication-job-lifecycle.d.ts.map +1 -0
- package/dist/proxmox/replication-job.d.ts +5 -6
- package/dist/proxmox/replication-job.d.ts.map +1 -1
- package/dist/release/archive.d.ts.map +1 -1
- package/dist/release/index.js +3 -2
- package/dist/release/index.js.map +3 -3
- package/docs/proxmox-lxc-distilled.md +56 -0
- package/docs/proxmox-replication-firewall-distilled.md +53 -0
- package/docs/proxmox-sdk-migration.md +23 -18
- package/package.json +2 -2
- package/src/proxmox/firewall-alias-config.ts +39 -0
- package/src/proxmox/firewall-alias-form.ts +1 -1
- package/src/proxmox/firewall-alias-lifecycle.ts +100 -0
- package/src/proxmox/firewall-alias.ts +4 -67
- package/src/proxmox/lxc-create.test.ts +4 -1
- package/src/proxmox/lxc-distilled.test.ts +239 -0
- package/src/proxmox/lxc-errors.ts +9 -0
- package/src/proxmox/lxc-lifecycle.ts +31 -113
- package/src/proxmox/lxc-read.test.ts +29 -6
- package/src/proxmox/lxc-read.ts +88 -0
- package/src/proxmox/lxc-task.ts +71 -0
- package/src/proxmox/network-apply-distilled.test.ts +76 -0
- package/src/proxmox/network-apply-read.ts +18 -39
- package/src/proxmox/network-apply-run.ts +97 -0
- package/src/proxmox/network-apply.ts +5 -94
- package/src/proxmox/qemu-errors.ts +7 -0
- package/src/proxmox/qemu-form.ts +15 -0
- package/src/proxmox/qemu-lifecycle.test.ts +171 -0
- package/src/proxmox/qemu-lifecycle.ts +134 -0
- package/src/proxmox/qemu-read.ts +73 -0
- package/src/proxmox/qemu-task.ts +64 -0
- package/src/proxmox/qemu.ts +11 -44
- package/src/proxmox/replication-firewall-fixtures.ts +71 -0
- package/src/proxmox/replication-firewall-lifecycle.test.ts +109 -0
- package/src/proxmox/replication-firewall-wire.test.ts +173 -0
- package/src/proxmox/replication-job-config.ts +79 -0
- package/src/proxmox/replication-job-lifecycle.ts +113 -0
- package/src/proxmox/replication-job.ts +7 -84
- package/src/release/archive.ts +4 -1
package/dist/proxmox/index.js
CHANGED
|
@@ -1774,14 +1774,6 @@ var pveWith = (target, credential, method, path, form) => Effect14.gen(function*
|
|
|
1774
1774
|
const response = target.scheme === "pve" ? yield* executeOnCluster(target, method, path, (apiBase) => buildRequest(method, apiBase, path, credential, target, form)).pipe(Effect14.mapError((failure) => failure.tag === "cluster" ? clusterExhausted(failure.method, failure.path, failure.attempts) : new PveError(0, method, path, String(failure)))) : yield* executeOne(target, credential, method, path, form, target.api);
|
|
1775
1775
|
return yield* readResponse(response, method, path);
|
|
1776
1776
|
});
|
|
1777
|
-
var pveEnvelopeWith = (target, credential, method, path) => Effect14.gen(function* () {
|
|
1778
|
-
const response = yield* executeOnCluster(target, method, path, (apiBase) => buildRequest(method, apiBase, path, credential, target)).pipe(Effect14.mapError((failure) => failure.tag === "cluster" ? clusterExhausted(failure.method, failure.path, failure.attempts) : new PveError(0, method, path, String(failure))));
|
|
1779
|
-
if (response.status < 200 || response.status >= 300) {
|
|
1780
|
-
const detail = yield* response.text.pipe(Effect14.orElseSucceed(() => ""));
|
|
1781
|
-
return yield* Effect14.fail(new PveError(response.status, method, path, detail.slice(0, 300)));
|
|
1782
|
-
}
|
|
1783
|
-
return yield* response.json.pipe(Effect14.mapError((cause) => new PveError(0, method, path, String(cause))));
|
|
1784
|
-
});
|
|
1785
1777
|
var pve = (target, role, method, path, form) => Effect14.gen(function* () {
|
|
1786
1778
|
const credential = yield* leased(target, role);
|
|
1787
1779
|
return yield* pveWith(target, credential, method, path, form);
|
|
@@ -2231,8 +2223,13 @@ var ProxmoxCephPoolProvider = () => Provider8.effect(ProxmoxCephPool, Effect24.s
|
|
|
2231
2223
|
})));
|
|
2232
2224
|
// src/proxmox/firewall-alias.ts
|
|
2233
2225
|
import { Resource as Resource9 } from "alchemy";
|
|
2234
|
-
import { isResolved as isResolved9 } from "alchemy/Diff";
|
|
2235
2226
|
import * as Provider9 from "alchemy/Provider";
|
|
2227
|
+
import * as Effect26 from "effect/Effect";
|
|
2228
|
+
|
|
2229
|
+
// src/proxmox/firewall-alias-lifecycle.ts
|
|
2230
|
+
import { isResolved as isResolved9 } from "alchemy/Diff";
|
|
2231
|
+
import * as cluster3 from "@distilled.cloud/proxmox/cluster";
|
|
2232
|
+
import { ProxmoxParseError as ProxmoxParseError2 } from "@distilled.cloud/proxmox/Errors";
|
|
2236
2233
|
import * as Effect25 from "effect/Effect";
|
|
2237
2234
|
|
|
2238
2235
|
// src/proxmox/firewall-alias-form.ts
|
|
@@ -2251,12 +2248,11 @@ var body = (props) => {
|
|
|
2251
2248
|
return { cidr: cidr(props.cidr), ...note === "" ? {} : { comment: note } };
|
|
2252
2249
|
};
|
|
2253
2250
|
|
|
2254
|
-
// src/proxmox/firewall-alias.ts
|
|
2255
|
-
var
|
|
2256
|
-
var handlers3 = pveHandlers({
|
|
2251
|
+
// src/proxmox/firewall-alias-config.ts
|
|
2252
|
+
var firewallAliasSpec = {
|
|
2257
2253
|
attributes: (live, props) => {
|
|
2258
2254
|
const address = cidr(live["cidr"]);
|
|
2259
|
-
return
|
|
2255
|
+
return {
|
|
2260
2256
|
cidr: address,
|
|
2261
2257
|
comment: comment(live["comment"]),
|
|
2262
2258
|
ipversion: int(live["ipversion"], 0),
|
|
@@ -2272,26 +2268,74 @@ var handlers3 = pveHandlers({
|
|
|
2272
2268
|
matches: (attributes, props) => attributes.cidr === cidr(props.cidr) && attributes.comment === comment(props.comment),
|
|
2273
2269
|
path: (props) => `cluster/firewall/aliases/${props.name}`,
|
|
2274
2270
|
updateForm: body
|
|
2275
|
-
}
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2271
|
+
};
|
|
2272
|
+
|
|
2273
|
+
// src/proxmox/firewall-alias-lifecycle.ts
|
|
2274
|
+
var { guardCreate: guardCreate2, guardUpdate: guardUpdate2 } = specGuards(firewallAliasSpec);
|
|
2275
|
+
var readFirewallAlias = (props) => runPve(props.target, "read", false, cluster3.getClusterFirewallAlias({ name: props.name })).pipe(Effect25.flatMap((live) => {
|
|
2276
|
+
if (live === null || typeof live !== "object" || Array.isArray(live) || !("name" in live) || typeof live.name !== "string" || fold(live.name) !== fold(props.name) || !("cidr" in live) || typeof live.cidr !== "string" || cidr(live.cidr) === "") {
|
|
2277
|
+
return Effect25.fail(new ProxmoxParseError2({
|
|
2278
|
+
body: undefined,
|
|
2279
|
+
cause: "Firewall alias response does not match the requested vendor identity"
|
|
2280
|
+
}));
|
|
2281
|
+
}
|
|
2282
|
+
return Effect25.succeed(firewallAliasSpec.attributes({ ...live }, props));
|
|
2283
|
+
}), Effect25.catchTag("FirewallAliasNotFound", () => Effect25.succeed(undefined)));
|
|
2284
|
+
var deleteFirewallAlias = (props) => runPve(props.target, "provision", true, cluster3.deleteClusterFirewallAlias({ name: props.name }));
|
|
2285
|
+
var firewallAliasHandlers = {
|
|
2286
|
+
list: () => Effect25.succeed([]),
|
|
2287
|
+
read: ({ olds }) => readFirewallAlias(olds),
|
|
2288
|
+
diff: Effect25.fn(function* ({
|
|
2289
|
+
news,
|
|
2290
|
+
output
|
|
2291
|
+
}) {
|
|
2292
|
+
if (!isResolved9(news))
|
|
2293
|
+
return;
|
|
2294
|
+
yield* guardCreate2(news, output === undefined);
|
|
2295
|
+
yield* guardUpdate2(news);
|
|
2296
|
+
if (output === undefined)
|
|
2297
|
+
return;
|
|
2298
|
+
if (fold(news.name) !== fold(output.name))
|
|
2280
2299
|
return { action: "replace" };
|
|
2300
|
+
const live = yield* readFirewallAlias(news);
|
|
2301
|
+
if (live === undefined) {
|
|
2302
|
+
yield* guardCreate2(news, true);
|
|
2303
|
+
return { action: "update" };
|
|
2281
2304
|
}
|
|
2282
|
-
return
|
|
2283
|
-
})
|
|
2284
|
-
})
|
|
2305
|
+
return { action: firewallAliasSpec.matches(live, news) ? "noop" : "update" };
|
|
2306
|
+
}),
|
|
2307
|
+
reconcile: Effect25.fn(function* ({ news }) {
|
|
2308
|
+
const live = yield* readFirewallAlias(news);
|
|
2309
|
+
yield* guardCreate2(news, live === undefined);
|
|
2310
|
+
yield* guardUpdate2(news);
|
|
2311
|
+
if (live === undefined) {
|
|
2312
|
+
yield* runPve(news.target, "provision", true, cluster3.createClusterFirewallAlias(firewallAliasSpec.createForm(news)));
|
|
2313
|
+
} else {
|
|
2314
|
+
const form = formToSend(firewallAliasSpec.matches, live, news, firewallAliasSpec.updateForm(news));
|
|
2315
|
+
if (form !== undefined)
|
|
2316
|
+
yield* runPve(news.target, "provision", true, cluster3.putClusterFirewallAlias({ ...form, name: news.name }));
|
|
2317
|
+
}
|
|
2318
|
+
const after = yield* readFirewallAlias(news);
|
|
2319
|
+
if (after === undefined)
|
|
2320
|
+
return yield* Effect25.fail(new Error(`${firewallAliasSpec.path(news)}: write returned success but the resource is still absent`));
|
|
2321
|
+
return after;
|
|
2322
|
+
}),
|
|
2323
|
+
delete: ({ olds }) => deleteFirewallAlias(olds)
|
|
2324
|
+
};
|
|
2325
|
+
|
|
2326
|
+
// src/proxmox/firewall-alias.ts
|
|
2327
|
+
var ProxmoxFirewallAlias = Resource9("Proxmox.FirewallAlias");
|
|
2328
|
+
var ProxmoxFirewallAliasProvider = () => Provider9.effect(ProxmoxFirewallAlias, Effect26.succeed(ProxmoxFirewallAlias.Provider.of(firewallAliasHandlers)));
|
|
2285
2329
|
// src/proxmox/group.ts
|
|
2286
2330
|
import { Resource as Resource10 } from "alchemy";
|
|
2287
2331
|
import { isResolved as isResolved10 } from "alchemy/Diff";
|
|
2288
2332
|
import * as Provider10 from "alchemy/Provider";
|
|
2289
2333
|
import * as access4 from "@distilled.cloud/proxmox/access";
|
|
2290
|
-
import * as
|
|
2334
|
+
import * as Effect28 from "effect/Effect";
|
|
2291
2335
|
|
|
2292
2336
|
// src/proxmox/group-wire.ts
|
|
2293
2337
|
import * as access3 from "@distilled.cloud/proxmox/access";
|
|
2294
|
-
import * as
|
|
2338
|
+
import * as Effect27 from "effect/Effect";
|
|
2295
2339
|
var GROUP_CREATE = "pve:POST /access/groups";
|
|
2296
2340
|
var GROUP_UPDATE = "pve:PUT /access/groups/{groupid}";
|
|
2297
2341
|
var storedComment = (comment2) => {
|
|
@@ -2316,20 +2360,20 @@ var attributesOf4 = (live, props) => ({
|
|
|
2316
2360
|
members: memberList(live.members)
|
|
2317
2361
|
});
|
|
2318
2362
|
var matches4 = (attributes, props) => attributes.comment === storedComment(props.comment);
|
|
2319
|
-
var readGroupOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, access3.getAccessGroup({ groupid: props.groupid }))).pipe(
|
|
2320
|
-
var readGroup = (props) => readGroupOrFail(props).pipe(
|
|
2363
|
+
var readGroupOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, access3.getAccessGroup({ groupid: props.groupid }))).pipe(Effect27.map((live) => live === UNREADABLE ? UNREADABLE : live.members === undefined ? undefined : attributesOf4(live, props)));
|
|
2364
|
+
var readGroup = (props) => readGroupOrFail(props).pipe(Effect27.catchTag("GroupNotFound", () => Effect27.succeed(undefined)));
|
|
2321
2365
|
var dropUnreadable3 = (live) => live === UNREADABLE ? undefined : live;
|
|
2322
2366
|
|
|
2323
2367
|
// src/proxmox/group.ts
|
|
2324
2368
|
var ProxmoxGroup = Resource10("Proxmox.Group", {
|
|
2325
2369
|
defaultRemovalPolicy: "retain"
|
|
2326
2370
|
});
|
|
2327
|
-
var ProxmoxGroupProvider = () => Provider10.effect(ProxmoxGroup,
|
|
2328
|
-
list: () =>
|
|
2329
|
-
read:
|
|
2371
|
+
var ProxmoxGroupProvider = () => Provider10.effect(ProxmoxGroup, Effect28.succeed(ProxmoxGroup.Provider.of({
|
|
2372
|
+
list: () => Effect28.succeed([]),
|
|
2373
|
+
read: Effect28.fn(function* ({ olds, output }) {
|
|
2330
2374
|
return dropUnreadable3(yield* output === undefined ? readGroup(olds) : readGroupOrFail(olds));
|
|
2331
2375
|
}),
|
|
2332
|
-
diff:
|
|
2376
|
+
diff: Effect28.fn(function* ({ news, output }) {
|
|
2333
2377
|
if (!isResolved10(news))
|
|
2334
2378
|
return;
|
|
2335
2379
|
yield* guardWrite2(GROUP_CREATE, createForm3(news), output === undefined);
|
|
@@ -2347,7 +2391,7 @@ var ProxmoxGroupProvider = () => Provider10.effect(ProxmoxGroup, Effect27.succee
|
|
|
2347
2391
|
}
|
|
2348
2392
|
return matches4(live, news) ? { action: "noop" } : { action: "update" };
|
|
2349
2393
|
}),
|
|
2350
|
-
reconcile:
|
|
2394
|
+
reconcile: Effect28.fn(function* ({ news }) {
|
|
2351
2395
|
const before = dropUnreadable3(yield* readGroup(news));
|
|
2352
2396
|
yield* guardWrite2(GROUP_CREATE, createForm3(news), before === undefined);
|
|
2353
2397
|
yield* guardWrite2(GROUP_UPDATE, updateForm(news), false);
|
|
@@ -2358,18 +2402,18 @@ var ProxmoxGroupProvider = () => Provider10.effect(ProxmoxGroup, Effect27.succee
|
|
|
2358
2402
|
}
|
|
2359
2403
|
const after = dropUnreadable3(yield* readGroup(news));
|
|
2360
2404
|
if (after === undefined) {
|
|
2361
|
-
return yield*
|
|
2405
|
+
return yield* Effect28.die(new Error(`access/groups/${news.groupid}: the write returned no error but the group is ` + 'still absent. PVE wraps every answer in {"data":...} and can report success ' + "on a call that did nothing -- read back rather than trusting the status code."));
|
|
2362
2406
|
}
|
|
2363
2407
|
return after;
|
|
2364
2408
|
}),
|
|
2365
|
-
delete:
|
|
2409
|
+
delete: Effect28.fn(function* ({ olds }) {
|
|
2366
2410
|
yield* runPve(olds.target, "provision", true, access4.deleteAccessGroup({ groupid: olds.groupid }));
|
|
2367
2411
|
})
|
|
2368
2412
|
})));
|
|
2369
2413
|
// src/proxmox/ha-resource.ts
|
|
2370
2414
|
import { Resource as Resource11 } from "alchemy";
|
|
2371
2415
|
import * as Provider11 from "alchemy/Provider";
|
|
2372
|
-
import * as
|
|
2416
|
+
import * as Effect29 from "effect/Effect";
|
|
2373
2417
|
var ProxmoxHaResource = Resource11("Proxmox.HaResource");
|
|
2374
2418
|
var kind = (sid) => sid.split(":")[0] ?? "";
|
|
2375
2419
|
var shape3 = (props) => ({
|
|
@@ -2378,7 +2422,7 @@ var shape3 = (props) => ({
|
|
|
2378
2422
|
max_restart: String(props.max_restart ?? 1),
|
|
2379
2423
|
state: props.state ?? "started"
|
|
2380
2424
|
});
|
|
2381
|
-
var
|
|
2425
|
+
var handlers3 = pveHandlers({
|
|
2382
2426
|
attributes: (live, props) => ({
|
|
2383
2427
|
comment: typeof live["comment"] === "string" ? live["comment"] : "",
|
|
2384
2428
|
max_relocate: num(live["max_relocate"], 1),
|
|
@@ -2397,11 +2441,11 @@ var handlers4 = pveHandlers({
|
|
|
2397
2441
|
path: (props) => `cluster/ha/resources/${props.sid}`,
|
|
2398
2442
|
updateForm: shape3
|
|
2399
2443
|
});
|
|
2400
|
-
var ProxmoxHaResourceProvider = () => Provider11.effect(ProxmoxHaResource,
|
|
2444
|
+
var ProxmoxHaResourceProvider = () => Provider11.effect(ProxmoxHaResource, Effect29.succeed(ProxmoxHaResource.Provider.of(handlers3)));
|
|
2401
2445
|
// src/proxmox/ha-rule.ts
|
|
2402
2446
|
import { Resource as Resource12 } from "alchemy";
|
|
2403
2447
|
import * as Provider12 from "alchemy/Provider";
|
|
2404
|
-
import * as
|
|
2448
|
+
import * as Effect30 from "effect/Effect";
|
|
2405
2449
|
|
|
2406
2450
|
// src/proxmox/ha-rule-form.ts
|
|
2407
2451
|
var nodeList = (value) => {
|
|
@@ -2449,7 +2493,7 @@ var clearList = (props) => Object.entries(optional(props)).filter(([, value]) =>
|
|
|
2449
2493
|
|
|
2450
2494
|
// src/proxmox/ha-rule.ts
|
|
2451
2495
|
var ProxmoxHaRule = Resource12("Proxmox.HaRule");
|
|
2452
|
-
var
|
|
2496
|
+
var handlers4 = pveHandlers({
|
|
2453
2497
|
attributes: (live, props) => {
|
|
2454
2498
|
const liveType = text(live["type"]);
|
|
2455
2499
|
if (liveType !== "" && liveType !== props.type)
|
|
@@ -2478,43 +2522,43 @@ var handlers5 = pveHandlers({
|
|
|
2478
2522
|
return withClears(fields, clear);
|
|
2479
2523
|
}
|
|
2480
2524
|
});
|
|
2481
|
-
var ProxmoxHaRuleProvider = () => Provider12.effect(ProxmoxHaRule,
|
|
2525
|
+
var ProxmoxHaRuleProvider = () => Provider12.effect(ProxmoxHaRule, Effect30.succeed(ProxmoxHaRule.Provider.of(handlers4)));
|
|
2482
2526
|
// src/proxmox/lxc.ts
|
|
2483
2527
|
import { Resource as Resource13 } from "alchemy";
|
|
2484
2528
|
import { isResolved as isResolved11 } from "alchemy/Diff";
|
|
2485
2529
|
import * as Provider13 from "alchemy/Provider";
|
|
2486
|
-
import * as
|
|
2530
|
+
import * as Effect41 from "effect/Effect";
|
|
2487
2531
|
|
|
2488
2532
|
// src/ownership/adopt.ts
|
|
2489
2533
|
import { AdoptPolicy } from "alchemy/AdoptPolicy";
|
|
2490
2534
|
import { AlchemyContext } from "alchemy/AlchemyContext";
|
|
2491
2535
|
import { Stack as Stack3 } from "alchemy/Stack";
|
|
2492
|
-
import * as
|
|
2536
|
+
import * as Effect34 from "effect/Effect";
|
|
2493
2537
|
import * as Option5 from "effect/Option";
|
|
2494
2538
|
|
|
2495
2539
|
// src/ownership/resume.ts
|
|
2496
2540
|
import { Unowned } from "alchemy/AdoptPolicy";
|
|
2497
2541
|
import { Artifacts } from "alchemy/Artifacts";
|
|
2498
|
-
import * as
|
|
2542
|
+
import * as Effect31 from "effect/Effect";
|
|
2499
2543
|
import * as Option2 from "effect/Option";
|
|
2500
2544
|
var PROVEN = "homeflare/ownership/resumes";
|
|
2501
2545
|
var UNFINISHED = "homeflare/ownership/unfinished";
|
|
2502
|
-
var note = (key, instanceId) =>
|
|
2503
|
-
const bag = yield*
|
|
2546
|
+
var note = (key, instanceId) => Effect31.gen(function* () {
|
|
2547
|
+
const bag = yield* Effect31.serviceOption(Artifacts);
|
|
2504
2548
|
if (Option2.isSome(bag))
|
|
2505
2549
|
yield* bag.value.set(key, instanceId);
|
|
2506
2550
|
});
|
|
2507
|
-
var noted = (key, instanceId) =>
|
|
2508
|
-
const bag = yield*
|
|
2551
|
+
var noted = (key, instanceId) => Effect31.gen(function* () {
|
|
2552
|
+
const bag = yield* Effect31.serviceOption(Artifacts);
|
|
2509
2553
|
if (Option2.isNone(bag))
|
|
2510
2554
|
return false;
|
|
2511
2555
|
return (yield* bag.value.get(key)) === instanceId;
|
|
2512
2556
|
});
|
|
2513
|
-
var noteUnfinished = (instanceId) =>
|
|
2514
|
-
var noteResume = (instanceId, recovered) =>
|
|
2557
|
+
var noteUnfinished = (instanceId) => Effect31.as(note(UNFINISHED, instanceId), undefined);
|
|
2558
|
+
var noteResume = (instanceId, recovered) => Effect31.gen(function* () {
|
|
2515
2559
|
yield* noteUnfinished(instanceId);
|
|
2516
|
-
const unproven = () =>
|
|
2517
|
-
const found = yield* recovered.pipe(
|
|
2560
|
+
const unproven = () => Effect31.succeed(undefined);
|
|
2561
|
+
const found = yield* recovered.pipe(Effect31.catch(unproven), Effect31.catchDefect(unproven));
|
|
2518
2562
|
if (found === undefined || found === null || Unowned.is(found))
|
|
2519
2563
|
return;
|
|
2520
2564
|
yield* note(PROVEN, instanceId);
|
|
@@ -2525,7 +2569,7 @@ var provingResumes = (service) => {
|
|
|
2525
2569
|
const { diff, read } = service;
|
|
2526
2570
|
if (diff === undefined || read === undefined)
|
|
2527
2571
|
return service;
|
|
2528
|
-
const proving = (input) =>
|
|
2572
|
+
const proving = (input) => Effect31.gen(function* () {
|
|
2529
2573
|
if (input.output === undefined) {
|
|
2530
2574
|
yield* noteResume(input.instanceId, read({ ...input, output: undefined }));
|
|
2531
2575
|
}
|
|
@@ -2537,12 +2581,12 @@ var provingResumes = (service) => {
|
|
|
2537
2581
|
// src/ownership/rows.ts
|
|
2538
2582
|
import { Stack as Stack2 } from "alchemy/Stack";
|
|
2539
2583
|
import { State, isActionState } from "alchemy/State/State";
|
|
2540
|
-
import * as
|
|
2584
|
+
import * as Effect33 from "effect/Effect";
|
|
2541
2585
|
import * as Option4 from "effect/Option";
|
|
2542
2586
|
|
|
2543
2587
|
// src/ownership/whole.ts
|
|
2544
2588
|
import { Stack } from "alchemy/Stack";
|
|
2545
|
-
import * as
|
|
2589
|
+
import * as Effect32 from "effect/Effect";
|
|
2546
2590
|
import * as Option3 from "effect/Option";
|
|
2547
2591
|
var absent = (value) => value === undefined || value === null;
|
|
2548
2592
|
var isPlainObject = (value) => {
|
|
@@ -2568,22 +2612,22 @@ var carries = (declared, recorded) => {
|
|
|
2568
2612
|
}
|
|
2569
2613
|
return true;
|
|
2570
2614
|
};
|
|
2571
|
-
var wholeDeclaration = (fqn, props) =>
|
|
2615
|
+
var wholeDeclaration = (fqn, props) => Effect32.map(Effect32.serviceOption(Stack), (stack) => {
|
|
2572
2616
|
const declared = Option3.isSome(stack) ? stack.value.resources[fqn] : undefined;
|
|
2573
2617
|
return declared !== undefined && carries(declared.Props, props);
|
|
2574
2618
|
});
|
|
2575
2619
|
|
|
2576
2620
|
// src/ownership/rows.ts
|
|
2577
|
-
var storeOf =
|
|
2578
|
-
const stack = yield*
|
|
2579
|
-
const state = yield*
|
|
2621
|
+
var storeOf = Effect33.gen(function* () {
|
|
2622
|
+
const stack = yield* Effect33.serviceOption(Stack2);
|
|
2623
|
+
const state = yield* Effect33.serviceOption(State);
|
|
2580
2624
|
if (Option4.isNone(stack) || Option4.isNone(state))
|
|
2581
2625
|
return;
|
|
2582
2626
|
const store = yield* state.value;
|
|
2583
2627
|
const where = { stack: stack.value.name, stage: stack.value.stage };
|
|
2584
2628
|
return { resources: stack.value.resources, store, where };
|
|
2585
2629
|
});
|
|
2586
|
-
var rowAt = (store, where, fqn) => store.get({ ...where, fqn }).pipe(
|
|
2630
|
+
var rowAt = (store, where, fqn) => store.get({ ...where, fqn }).pipe(Effect33.orElseSucceed(() => {
|
|
2587
2631
|
return;
|
|
2588
2632
|
}));
|
|
2589
2633
|
var generationOf = (row, instanceId) => {
|
|
@@ -2596,7 +2640,7 @@ var generationOf = (row, instanceId) => {
|
|
|
2596
2640
|
}
|
|
2597
2641
|
return;
|
|
2598
2642
|
};
|
|
2599
|
-
var recordedGeneration = (fqn, instanceId) =>
|
|
2643
|
+
var recordedGeneration = (fqn, instanceId) => Effect33.gen(function* () {
|
|
2600
2644
|
const found = yield* storeOf;
|
|
2601
2645
|
if (found === undefined)
|
|
2602
2646
|
return;
|
|
@@ -2607,14 +2651,14 @@ var recordedGeneration = (fqn, instanceId) => Effect32.gen(function* () {
|
|
|
2607
2651
|
}
|
|
2608
2652
|
return;
|
|
2609
2653
|
});
|
|
2610
|
-
var recordedInstance = (fqn, instanceId) =>
|
|
2654
|
+
var recordedInstance = (fqn, instanceId) => Effect33.gen(function* () {
|
|
2611
2655
|
const generation = yield* recordedGeneration(fqn, instanceId);
|
|
2612
2656
|
if (generation === undefined)
|
|
2613
2657
|
return "absent";
|
|
2614
2658
|
return (yield* wholeDeclaration(fqn, generation.props)) ? "whole" : "partial";
|
|
2615
2659
|
});
|
|
2616
|
-
var isCreate = (fqn, instanceId) =>
|
|
2617
|
-
var forgetRefusedCreate = (fqn, instanceId) =>
|
|
2660
|
+
var isCreate = (fqn, instanceId) => Effect33.map(recordedGeneration(fqn, instanceId), (generation) => generation?.status === "creating");
|
|
2661
|
+
var forgetRefusedCreate = (fqn, instanceId) => Effect33.gen(function* () {
|
|
2618
2662
|
const found = yield* storeOf;
|
|
2619
2663
|
if (found === undefined)
|
|
2620
2664
|
return;
|
|
@@ -2623,32 +2667,32 @@ var forgetRefusedCreate = (fqn, instanceId) => Effect32.gen(function* () {
|
|
|
2623
2667
|
if (mine?.status !== "creating" || mine.instanceId !== instanceId || mine.attr !== undefined) {
|
|
2624
2668
|
return;
|
|
2625
2669
|
}
|
|
2626
|
-
yield* found.store.delete({ ...found.where, fqn }).pipe(
|
|
2670
|
+
yield* found.store.delete({ ...found.where, fqn }).pipe(Effect33.ignore);
|
|
2627
2671
|
});
|
|
2628
2672
|
|
|
2629
2673
|
// src/ownership/adopt.ts
|
|
2630
|
-
var adoptEnabled = (fqn) =>
|
|
2631
|
-
const stack = yield*
|
|
2674
|
+
var adoptEnabled = (fqn) => Effect34.gen(function* () {
|
|
2675
|
+
const stack = yield* Effect34.serviceOption(Stack3);
|
|
2632
2676
|
const scoped = Option5.isSome(stack) ? stack.value.resources[fqn]?.Adopt : undefined;
|
|
2633
2677
|
if (scoped !== undefined)
|
|
2634
2678
|
return scoped;
|
|
2635
|
-
const policy = yield*
|
|
2679
|
+
const policy = yield* Effect34.serviceOption(AdoptPolicy);
|
|
2636
2680
|
if (Option5.isSome(policy))
|
|
2637
2681
|
return policy.value;
|
|
2638
|
-
const context = yield*
|
|
2682
|
+
const context = yield* Effect34.serviceOption(AlchemyContext);
|
|
2639
2683
|
return Option5.isSome(context) ? context.value.adopt : false;
|
|
2640
2684
|
});
|
|
2641
|
-
var adoptable = (owner) =>
|
|
2685
|
+
var adoptable = (owner) => Effect34.gen(function* () {
|
|
2642
2686
|
if (yield* isCreate(owner.fqn, owner.instanceId))
|
|
2643
2687
|
return true;
|
|
2644
2688
|
return yield* unfinished(owner.instanceId);
|
|
2645
2689
|
});
|
|
2646
|
-
var adoptsAtApply = (owner) =>
|
|
2690
|
+
var adoptsAtApply = (owner) => Effect34.gen(function* () {
|
|
2647
2691
|
if (!(yield* adoptEnabled(owner.fqn)))
|
|
2648
2692
|
return false;
|
|
2649
2693
|
return yield* adoptable(owner);
|
|
2650
2694
|
});
|
|
2651
|
-
var refuseTakeover = (owner, what) =>
|
|
2695
|
+
var refuseTakeover = (owner, what) => Effect34.gen(function* () {
|
|
2652
2696
|
if (owner.output !== undefined)
|
|
2653
2697
|
return;
|
|
2654
2698
|
if (yield* resumes(owner.instanceId))
|
|
@@ -2658,25 +2702,25 @@ var refuseTakeover = (owner, what) => Effect33.gen(function* () {
|
|
|
2658
2702
|
return;
|
|
2659
2703
|
yield* forgetRefusedCreate(owner.fqn, owner.instanceId);
|
|
2660
2704
|
const remedy = may ? "Deploy with --adopt (or wrap the resource in adopt(true)) to take it over, or remove it first." : "It is the new identity of a replace, which --adopt does not cover: remove it first.";
|
|
2661
|
-
return yield*
|
|
2705
|
+
return yield* Effect34.die(new Error(`${what}: already exists, and this stack holds no state for it — the plan could not ask ` + "first (a prop was still an Output, a replace moved onto it, or it appeared after the " + `plan). Nothing was written. ${remedy}`));
|
|
2662
2706
|
});
|
|
2663
2707
|
var claimFor = (owner) => (what) => refuseTakeover(owner, what);
|
|
2664
2708
|
|
|
2665
2709
|
// src/ownership/probe.ts
|
|
2666
2710
|
import { Unowned as Unowned2 } from "alchemy/AdoptPolicy";
|
|
2667
|
-
import * as
|
|
2668
|
-
var provenOurs = (ask, settled2) =>
|
|
2711
|
+
import * as Effect35 from "effect/Effect";
|
|
2712
|
+
var provenOurs = (ask, settled2) => Effect35.gen(function* () {
|
|
2669
2713
|
const recorded = yield* recordedInstance(ask.fqn, ask.instanceId);
|
|
2670
2714
|
if (recorded === "absent")
|
|
2671
2715
|
return false;
|
|
2672
2716
|
if (recorded === "partial") {
|
|
2673
|
-
yield*
|
|
2717
|
+
yield* Effect35.logWarning(`${ask.fqn}: the interrupted create's state row lacks part of the declaration (a prop ` + "was still an Output when it was written), so the live object is not proven ours");
|
|
2674
2718
|
return false;
|
|
2675
2719
|
}
|
|
2676
|
-
const unproven = (reason) =>
|
|
2677
|
-
return yield* settled2.pipe(
|
|
2720
|
+
const unproven = (reason) => Effect35.as(Effect35.logWarning(`${ask.fqn}: the interrupted create's object could not be compared with its props ` + `(${reason instanceof Error ? reason.message : String(reason)}), so it is not treated as ours`), false);
|
|
2721
|
+
return yield* settled2.pipe(Effect35.catch(unproven), Effect35.catchDefect(unproven));
|
|
2678
2722
|
});
|
|
2679
|
-
var ownedRead = (ask, found, settled2) =>
|
|
2723
|
+
var ownedRead = (ask, found, settled2) => Effect35.gen(function* () {
|
|
2680
2724
|
if (found === undefined || ask.output !== undefined)
|
|
2681
2725
|
return found;
|
|
2682
2726
|
return (yield* provenOurs(ask, settled2)) ? found : Unowned2(found);
|
|
@@ -3131,11 +3175,11 @@ var identityRefusals = (news, olds, output) => {
|
|
|
3131
3175
|
};
|
|
3132
3176
|
|
|
3133
3177
|
// src/proxmox/lxc-adoption.ts
|
|
3134
|
-
import * as
|
|
3178
|
+
import * as Effect40 from "effect/Effect";
|
|
3135
3179
|
|
|
3136
3180
|
// src/ownership/adopting.ts
|
|
3137
|
-
import * as
|
|
3138
|
-
var adopting = (owner, settled2) =>
|
|
3181
|
+
import * as Effect36 from "effect/Effect";
|
|
3182
|
+
var adopting = (owner, settled2) => Effect36.gen(function* () {
|
|
3139
3183
|
if (owner.output === undefined)
|
|
3140
3184
|
return false;
|
|
3141
3185
|
const generation = yield* recordedGeneration(owner.fqn, owner.instanceId);
|
|
@@ -3147,56 +3191,70 @@ var adopting = (owner, settled2) => Effect35.gen(function* () {
|
|
|
3147
3191
|
});
|
|
3148
3192
|
|
|
3149
3193
|
// src/proxmox/lxc-lifecycle.ts
|
|
3150
|
-
import * as
|
|
3194
|
+
import * as Effect39 from "effect/Effect";
|
|
3195
|
+
import * as nodes10 from "@distilled.cloud/proxmox/nodes";
|
|
3151
3196
|
|
|
3152
|
-
// src/proxmox/
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3197
|
+
// src/proxmox/lxc-errors.ts
|
|
3198
|
+
class LxcRefusedError extends Error {
|
|
3199
|
+
name = "LxcRefusedError";
|
|
3200
|
+
}
|
|
3201
|
+
|
|
3202
|
+
// src/proxmox/lxc-read.ts
|
|
3203
|
+
import * as cluster4 from "@distilled.cloud/proxmox/cluster";
|
|
3204
|
+
import * as nodes8 from "@distilled.cloud/proxmox/nodes";
|
|
3205
|
+
import * as Effect37 from "effect/Effect";
|
|
3206
|
+
var readLive = (where) => Effect37.gen(function* () {
|
|
3207
|
+
const credential = yield* leased(where.target, "read");
|
|
3208
|
+
const answer = yield* runPveWith(where.target, credential, false, nodes8.getNodeLxcConfig({ node: where.node, vmid: String(where.vmid) })).pipe(Effect37.map((data) => ({ data, error: undefined })), Effect37.catchTag("LxcConfigNotFound", (error) => Effect37.succeed({ data: undefined, error })));
|
|
3209
|
+
if (answer.error === undefined) {
|
|
3210
|
+
const live = answer.data;
|
|
3211
|
+
if (live === null || typeof live !== "object" || Array.isArray(live) || !("digest" in live) || typeof live.digest !== "string" || live.digest === "") {
|
|
3212
|
+
return yield* Effect37.fail(new LxcRefusedError(`CT ${String(where.vmid)}: config read did not return a valid digest-bearing config.`));
|
|
3213
|
+
}
|
|
3214
|
+
return live;
|
|
3215
|
+
}
|
|
3216
|
+
const rows = yield* runPveWith(where.target, credential, false, cluster4.listClusterResources({ type: "vm" }));
|
|
3217
|
+
if (!Array.isArray(rows) || rows.some((row) => !Number.isSafeInteger(Number(row?.vmid)) || Number(row?.vmid) <= 0 || typeof row?.node !== "string" || row.node === "" || !["lxc", "qemu"].includes(row.type))) {
|
|
3218
|
+
return yield* Effect37.fail(new LxcRefusedError("The cluster guest index did not identify its rows."));
|
|
3219
|
+
}
|
|
3220
|
+
const found = rows.find((row) => Number(row.vmid) === where.vmid);
|
|
3221
|
+
if (found !== undefined && found.node === where.node && found.type === "lxc") {
|
|
3222
|
+
return yield* Effect37.fail(answer.error);
|
|
3223
|
+
}
|
|
3224
|
+
if (found !== undefined) {
|
|
3225
|
+
return yield* Effect37.fail(new LxcRefusedError(`vmid ${String(where.vmid)} is a ${text(found.type, "guest")} on ` + `${text(found.node, "?")}, not a container on ${where.node}. A migration is not an ` + "update and vmids are cluster-wide: declare the node it is on, or pick a free vmid."));
|
|
3226
|
+
}
|
|
3227
|
+
return;
|
|
3159
3228
|
});
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
offline: all.filter((row) => row.type === "node" && !bool(row.online)).map((row) => text(row.name, "<unnamed>")),
|
|
3173
|
-
quorate: cluster3 === undefined ? true : bool(cluster3.quorate)
|
|
3174
|
-
};
|
|
3175
|
-
}));
|
|
3176
|
-
var degradedReason = (target) => clusterHealth(target).pipe(Effect36.map((health) => health.quorate && health.offline.length === 0 ? undefined : `quorate=${String(health.quorate)} offline=[${health.offline.join(", ")}]`));
|
|
3177
|
-
var UNREACHABLE = "the task could not be read -- the node stopped answering, or this lease cannot see it";
|
|
3178
|
-
var STILL_RUNNING = "still running when this provider stopped waiting";
|
|
3179
|
-
var ATTEMPTS = 30;
|
|
3180
|
-
var awaitTask = (target, credential, node, upid, attempts = ATTEMPTS) => Effect36.gen(function* () {
|
|
3181
|
-
const path = `nodes/${node}/tasks/${upid}/status`;
|
|
3182
|
-
for (let attempt = 0;attempt < attempts; attempt += 1) {
|
|
3183
|
-
const status = yield* pveWith(target, credential, "GET", path).pipe(Effect36.orElseSucceed(() => {
|
|
3229
|
+
|
|
3230
|
+
// src/proxmox/lxc-task.ts
|
|
3231
|
+
import * as nodes9 from "@distilled.cloud/proxmox/nodes";
|
|
3232
|
+
import * as Effect38 from "effect/Effect";
|
|
3233
|
+
var lxcTask = (where, operation, what, polls) => Effect38.gen(function* () {
|
|
3234
|
+
const credential = yield* mint(where.target, "provision");
|
|
3235
|
+
const upid = text(yield* runPveWith(where.target, credential, true, operation));
|
|
3236
|
+
if (upid === "")
|
|
3237
|
+
return yield* Effect38.fail(new LxcRefusedError(`${what}: PVE returned no task id, so the write cannot be confirmed and is not claimed.`));
|
|
3238
|
+
let exit = "still running when this provider stopped waiting";
|
|
3239
|
+
for (let attempt = 0;attempt < polls; attempt++) {
|
|
3240
|
+
const status = yield* runPveWith(where.target, credential, false, nodes9.getNodeTaskStatus({ node: where.node, upid })).pipe(Effect38.orElseSucceed(() => {
|
|
3184
3241
|
return;
|
|
3185
3242
|
}));
|
|
3186
|
-
if (status === undefined)
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3243
|
+
if (status === undefined || status.status === undefined) {
|
|
3244
|
+
exit = "the task could not be read -- the node stopped answering, or this lease cannot see it";
|
|
3245
|
+
break;
|
|
3246
|
+
}
|
|
3247
|
+
if (status.status === "stopped") {
|
|
3248
|
+
exit = text(status.exitstatus, "stopped with no exitstatus");
|
|
3249
|
+
break;
|
|
3250
|
+
}
|
|
3251
|
+
yield* Effect38.sleep("1 second");
|
|
3191
3252
|
}
|
|
3192
|
-
|
|
3253
|
+
if (exit !== "OK")
|
|
3254
|
+
return yield* Effect38.fail(new LxcRefusedError(`${what}: task ${upid} ended "${exit}". Read \`pvesh get /nodes/${where.node}/tasks/` + `${upid}/log\` before running the deploy again.`));
|
|
3193
3255
|
});
|
|
3194
3256
|
|
|
3195
3257
|
// src/proxmox/lxc-lifecycle.ts
|
|
3196
|
-
class LxcRefusedError extends Error {
|
|
3197
|
-
name = "LxcRefusedError";
|
|
3198
|
-
}
|
|
3199
|
-
var guestPath = (where) => `nodes/${where.node}/lxc/${String(where.vmid)}`;
|
|
3200
3258
|
var LXC_ENDPOINTS = {
|
|
3201
3259
|
create: "pve:POST /nodes/{node}/lxc",
|
|
3202
3260
|
resize: "pve:PUT /nodes/{node}/lxc/{vmid}/resize",
|
|
@@ -3204,82 +3262,61 @@ var LXC_ENDPOINTS = {
|
|
|
3204
3262
|
};
|
|
3205
3263
|
var CREATE_POLLS = 180;
|
|
3206
3264
|
var TASK_POLLS = 60;
|
|
3207
|
-
var isServerError = (error) => error instanceof PveError && error.status === 500;
|
|
3208
|
-
var readLive = (where) => Effect37.gen(function* () {
|
|
3209
|
-
const answer = yield* pve(where.target, "read", "GET", `${guestPath(where)}/config`).pipe(Effect37.map((data) => ({ data, error: undefined })), Effect37.catchIf(isServerError, (error) => Effect37.succeed({ data: undefined, error })));
|
|
3210
|
-
if (answer.data !== undefined && answer.data !== null)
|
|
3211
|
-
return answer.data;
|
|
3212
|
-
const rows = yield* pve(where.target, "read", "GET", "cluster/resources?type=vm");
|
|
3213
|
-
const found = (rows ?? []).find((row) => Number(row.vmid) === where.vmid);
|
|
3214
|
-
if (found !== undefined && found.node === where.node && found.type === "lxc") {
|
|
3215
|
-
return yield* Effect37.fail(answer.error ?? new LxcRefusedError(`CT ${String(where.vmid)} is listed but its config read empty.`));
|
|
3216
|
-
}
|
|
3217
|
-
if (found !== undefined) {
|
|
3218
|
-
return yield* Effect37.fail(new LxcRefusedError(`vmid ${String(where.vmid)} is a ${text(found.type, "guest")} on ` + `${text(found.node, "?")}, not a container on ${where.node}. A migration is not an ` + "update and vmids are cluster-wide: declare the node it is on, or pick a free vmid."));
|
|
3219
|
-
}
|
|
3220
|
-
return;
|
|
3221
|
-
});
|
|
3222
3265
|
var attributesOf5 = (where, live) => ({
|
|
3223
3266
|
config: storedConfig(live),
|
|
3224
3267
|
node: where.node,
|
|
3225
3268
|
rawKeys: rawKeysOf(live),
|
|
3226
3269
|
vmid: where.vmid
|
|
3227
3270
|
});
|
|
3228
|
-
var
|
|
3229
|
-
const credential = yield* mint(where.target, "provision");
|
|
3230
|
-
const upid = text(yield* pveWith(where.target, credential, method, path, form));
|
|
3231
|
-
if (upid === "") {
|
|
3232
|
-
return yield* Effect37.fail(new LxcRefusedError(`${what}: PVE returned no task id, so the write cannot be confirmed and is not claimed.`));
|
|
3233
|
-
}
|
|
3234
|
-
const exit = yield* awaitTask(where.target, credential, where.node, upid, polls);
|
|
3235
|
-
if (exit !== "OK") {
|
|
3236
|
-
return yield* Effect37.fail(new LxcRefusedError(`${what}: task ${upid} ended "${exit}". Read \`pvesh get /nodes/${where.node}/tasks/` + `${upid}/log\` before running the deploy again.`));
|
|
3237
|
-
}
|
|
3238
|
-
});
|
|
3239
|
-
var createGuest = (props) => Effect37.gen(function* () {
|
|
3271
|
+
var createGuest = (props) => Effect39.gen(function* () {
|
|
3240
3272
|
const form = createForm4(props);
|
|
3241
3273
|
yield* guardForm(LXC_ENDPOINTS.create, form, true);
|
|
3242
|
-
return yield*
|
|
3274
|
+
return yield* lxcTask(props, nodes10.createNodeLxc({
|
|
3275
|
+
...form,
|
|
3276
|
+
node: props.node,
|
|
3277
|
+
vmid: String(props.vmid),
|
|
3278
|
+
ostemplate: text(form["ostemplate"])
|
|
3279
|
+
}), `create CT ${String(props.vmid)}`, CREATE_POLLS);
|
|
3243
3280
|
});
|
|
3244
|
-
var updateGuest = (props, change, digest) =>
|
|
3281
|
+
var updateGuest = (props, change, digest) => Effect39.gen(function* () {
|
|
3245
3282
|
if (Object.keys(change.put).length > 0 || change.clear.length > 0) {
|
|
3246
3283
|
const form = withClears({ ...change.put, ...digest === "" ? {} : { digest } }, change.clear);
|
|
3247
3284
|
yield* guardForm(LXC_ENDPOINTS.update, form, false);
|
|
3248
|
-
yield*
|
|
3285
|
+
yield* runPve(props.target, "provision", true, nodes10.putNodeLxcConfig({ ...form, node: props.node, vmid: String(props.vmid) }));
|
|
3249
3286
|
}
|
|
3250
3287
|
for (const grow of change.resize) {
|
|
3251
3288
|
const grown = { disk: grow.disk, size: grow.size };
|
|
3252
3289
|
yield* guardForm(LXC_ENDPOINTS.resize, grown, true);
|
|
3253
|
-
yield*
|
|
3290
|
+
yield* lxcTask(props, nodes10.putNodeLxcResize({ ...grown, node: props.node, vmid: String(props.vmid) }), `grow ${grow.disk} of CT ${String(props.vmid)} to ${grow.size}`, TASK_POLLS);
|
|
3254
3291
|
}
|
|
3255
3292
|
});
|
|
3256
|
-
var destroyGuest = (where, declared) =>
|
|
3293
|
+
var destroyGuest = (where, declared) => Effect39.gen(function* () {
|
|
3257
3294
|
const live = yield* readLive(where);
|
|
3258
3295
|
if (live === undefined)
|
|
3259
3296
|
return;
|
|
3260
3297
|
const drift = judge(declared, live).drift;
|
|
3261
3298
|
if (drift.length > 0) {
|
|
3262
|
-
return yield*
|
|
3299
|
+
return yield* Effect39.fail(new LxcRefusedError(`destroy CT ${String(where.vmid)}: the guest on ${where.node} no longer matches its last ` + `declaration (${drift.join(", ")}), so it may not be the guest this stack made. ` + "Nothing was deleted. Delete it by hand if it should go, or declare it again."));
|
|
3263
3300
|
}
|
|
3264
|
-
yield*
|
|
3301
|
+
yield* lxcTask(where, nodes10.deleteNodeLxc({ node: where.node, vmid: String(where.vmid) }), `destroy CT ${String(where.vmid)}`, TASK_POLLS);
|
|
3265
3302
|
});
|
|
3266
3303
|
|
|
3267
3304
|
// src/proxmox/lxc-adoption.ts
|
|
3268
3305
|
var adoptionRefusal = (props, drift) => `CT ${String(props.vmid)} on ${props.node}: adopting it would change ${drift.join(", ")}. An ` + "adoption never changes a guest, so nothing is written. Declare what is live for these keys " + "(the plan names keys, never values) and plan again; change them after the adoption, as an " + "update.";
|
|
3269
|
-
var refuseAdoptedDrift = (owner, props, live, change) =>
|
|
3306
|
+
var refuseAdoptedDrift = (owner, props, live, change) => Effect40.gen(function* () {
|
|
3270
3307
|
if (change.drift.length === 0)
|
|
3271
3308
|
return;
|
|
3272
|
-
const settled2 = (recorded) =>
|
|
3309
|
+
const settled2 = (recorded) => Effect40.sync(() => judge(recorded, live).drift.length === 0);
|
|
3273
3310
|
if (!(yield* adopting(owner, settled2)))
|
|
3274
3311
|
return;
|
|
3275
|
-
return yield*
|
|
3312
|
+
return yield* Effect40.fail(new LxcRefusedError(adoptionRefusal(props, change.drift)));
|
|
3276
3313
|
});
|
|
3277
3314
|
|
|
3278
3315
|
// src/proxmox/lxc.ts
|
|
3279
3316
|
var ProxmoxLxc = Resource13("Proxmox.Lxc", { defaultRemovalPolicy: "retain" });
|
|
3280
|
-
var refused = (reasons) =>
|
|
3317
|
+
var refused = (reasons) => Effect41.fail(new LxcRefusedError(reasons.join(`
|
|
3281
3318
|
`)));
|
|
3282
|
-
var diff = (owner, news, olds) =>
|
|
3319
|
+
var diff = (owner, news, olds) => Effect41.gen(function* () {
|
|
3283
3320
|
const { output } = owner;
|
|
3284
3321
|
const identity2 = identityRefusals(news, olds, output);
|
|
3285
3322
|
if (identity2.length > 0)
|
|
@@ -3293,7 +3330,7 @@ var diff = (owner, news, olds) => Effect39.gen(function* () {
|
|
|
3293
3330
|
const refusals = createRefusals(news);
|
|
3294
3331
|
if (refusals.length > 0)
|
|
3295
3332
|
return yield* refused(refusals);
|
|
3296
|
-
yield*
|
|
3333
|
+
yield* Effect41.logWarning(`Proxmox.Lxc ${news.node}/${String(news.vmid)}: the cluster lists no guest with this ` + "vmid -- a deploy CREATES it again from ostemplate, with new, empty volumes.");
|
|
3297
3334
|
return { action: "update" };
|
|
3298
3335
|
}
|
|
3299
3336
|
const change = judge(news, live);
|
|
@@ -3304,10 +3341,10 @@ var diff = (owner, news, olds) => Effect39.gen(function* () {
|
|
|
3304
3341
|
if (change.drift.length === 0 && !moved)
|
|
3305
3342
|
return { action: "noop" };
|
|
3306
3343
|
const what = change.drift.length === 0 ? `the guest is on ${news.node} now, not ${output.node} -- a deploy re-records it, ` + "writing nothing." : `live config differs from the declaration in ${change.drift.join(", ")} -- a ` + "deploy writes these.";
|
|
3307
|
-
yield*
|
|
3344
|
+
yield* Effect41.logWarning(`Proxmox.Lxc ${news.node}/${String(news.vmid)}: ${what}`);
|
|
3308
3345
|
return { action: "update" };
|
|
3309
3346
|
});
|
|
3310
|
-
var reconcile = (owner, news) =>
|
|
3347
|
+
var reconcile = (owner, news) => Effect41.gen(function* () {
|
|
3311
3348
|
const { output } = owner;
|
|
3312
3349
|
const identity2 = identityRefusals(news, news, output);
|
|
3313
3350
|
if (identity2.length > 0)
|
|
@@ -3353,15 +3390,15 @@ var whereOf = (olds, output) => ({
|
|
|
3353
3390
|
target: olds.target,
|
|
3354
3391
|
vmid: output?.vmid ?? olds.vmid
|
|
3355
3392
|
});
|
|
3356
|
-
var read = (owner, olds) =>
|
|
3393
|
+
var read = (owner, olds) => Effect41.gen(function* () {
|
|
3357
3394
|
const where = whereOf(olds, owner.output);
|
|
3358
3395
|
const live = yield* readLive(where);
|
|
3359
3396
|
if (live === undefined)
|
|
3360
3397
|
return;
|
|
3361
|
-
const settled2 =
|
|
3398
|
+
const settled2 = Effect41.sync(() => judge(olds, live).drift.length === 0);
|
|
3362
3399
|
return yield* ownedRead(owner, attributesOf5(where, live), settled2);
|
|
3363
3400
|
});
|
|
3364
|
-
var
|
|
3401
|
+
var handlers5 = {
|
|
3365
3402
|
delete: ({ olds, output }) => destroyGuest(whereOf(olds, output), olds),
|
|
3366
3403
|
diff: ({
|
|
3367
3404
|
fqn,
|
|
@@ -3370,7 +3407,7 @@ var handlers6 = {
|
|
|
3370
3407
|
olds,
|
|
3371
3408
|
output
|
|
3372
3409
|
}) => diff({ fqn, instanceId, output }, news, olds),
|
|
3373
|
-
list: () =>
|
|
3410
|
+
list: () => Effect41.succeed([]),
|
|
3374
3411
|
read: ({
|
|
3375
3412
|
fqn,
|
|
3376
3413
|
instanceId,
|
|
@@ -3384,11 +3421,11 @@ var handlers6 = {
|
|
|
3384
3421
|
output
|
|
3385
3422
|
}) => reconcile({ fqn, instanceId, output }, news)
|
|
3386
3423
|
};
|
|
3387
|
-
var ProxmoxLxcProvider = () => Provider13.effect(ProxmoxLxc,
|
|
3424
|
+
var ProxmoxLxcProvider = () => Provider13.effect(ProxmoxLxc, Effect41.succeed(ProxmoxLxc.Provider.of(handlers5)).pipe(Effect41.map(provingResumes)));
|
|
3388
3425
|
// src/proxmox/metric-server.ts
|
|
3389
3426
|
import { Resource as Resource14 } from "alchemy";
|
|
3390
3427
|
import { isResolved as isResolved12 } from "alchemy/Diff";
|
|
3391
|
-
import * as
|
|
3428
|
+
import * as cluster6 from "@distilled.cloud/proxmox/cluster";
|
|
3392
3429
|
|
|
3393
3430
|
// src/proxmox/metric-server-form.ts
|
|
3394
3431
|
var UNSET2 = -1;
|
|
@@ -3502,15 +3539,15 @@ var metricServerSpec = {
|
|
|
3502
3539
|
};
|
|
3503
3540
|
|
|
3504
3541
|
// src/proxmox/metric-server-distilled.ts
|
|
3505
|
-
import * as
|
|
3506
|
-
import { ProxmoxParseError as
|
|
3507
|
-
import * as
|
|
3542
|
+
import * as cluster5 from "@distilled.cloud/proxmox/cluster";
|
|
3543
|
+
import { ProxmoxParseError as ProxmoxParseError3 } from "@distilled.cloud/proxmox/Errors";
|
|
3544
|
+
import * as Effect42 from "effect/Effect";
|
|
3508
3545
|
import * as Schema2 from "effect/Schema";
|
|
3509
|
-
var readMetricServerRow = (props) => runPve(props.target, "read", false,
|
|
3546
|
+
var readMetricServerRow = (props) => runPve(props.target, "read", false, cluster5.getClusterMetricsServer({ id: props.id })).pipe(Effect42.flatMap((body4) => Schema2.decodeUnknownEffect(Schema2.Record(Schema2.String, Schema2.Unknown))(body4).pipe(Effect42.mapError(() => new ProxmoxParseError3({
|
|
3510
3547
|
body: undefined,
|
|
3511
3548
|
cause: "Metric server response must be an object"
|
|
3512
|
-
})))),
|
|
3513
|
-
var readMetricServer = (props) => readMetricServerRow(props).pipe(
|
|
3549
|
+
})))), Effect42.catchTag("MetricServerNotFound", () => Effect42.succeed(undefined)), Effect42.catchTag("NotFound", () => Effect42.succeed(undefined)));
|
|
3550
|
+
var readMetricServer = (props) => readMetricServerRow(props).pipe(Effect42.map((row) => row === undefined ? undefined : metricServerSpec.attributes(row, props)));
|
|
3514
3551
|
var metricServerRequest = (props, form) => ({
|
|
3515
3552
|
...asForm2({
|
|
3516
3553
|
api_path_prefix: form["api-path-prefix"],
|
|
@@ -3535,51 +3572,51 @@ var metricServerRequest = (props, form) => ({
|
|
|
3535
3572
|
port: String(props.port),
|
|
3536
3573
|
server: props.server
|
|
3537
3574
|
});
|
|
3538
|
-
var deleteMetricServer = (props) =>
|
|
3575
|
+
var deleteMetricServer = (props) => Effect42.gen(function* () {
|
|
3539
3576
|
if ((yield* readMetricServerRow(props)) === undefined)
|
|
3540
3577
|
return;
|
|
3541
|
-
yield* runPve(props.target, "provision", true,
|
|
3578
|
+
yield* runPve(props.target, "provision", true, cluster5.deleteClusterMetricsServer({ id: props.id }));
|
|
3542
3579
|
});
|
|
3543
3580
|
|
|
3544
3581
|
// src/proxmox/metric-server.ts
|
|
3545
3582
|
import * as Provider14 from "alchemy/Provider";
|
|
3546
|
-
import * as
|
|
3583
|
+
import * as Effect43 from "effect/Effect";
|
|
3547
3584
|
var ProxmoxMetricServer = Resource14("Proxmox.MetricServer");
|
|
3548
|
-
var { guardCreate:
|
|
3549
|
-
var ProxmoxMetricServerProvider = () => Provider14.effect(ProxmoxMetricServer,
|
|
3550
|
-
list: () =>
|
|
3585
|
+
var { guardCreate: guardCreate3, guardUpdate: guardUpdate3 } = specGuards(metricServerSpec);
|
|
3586
|
+
var ProxmoxMetricServerProvider = () => Provider14.effect(ProxmoxMetricServer, Effect43.succeed(ProxmoxMetricServer.Provider.of({
|
|
3587
|
+
list: () => Effect43.succeed([]),
|
|
3551
3588
|
read: ({ olds }) => readMetricServer(olds),
|
|
3552
|
-
diff:
|
|
3589
|
+
diff: Effect43.fn(function* ({ news, output }) {
|
|
3553
3590
|
if (!isResolved12(news))
|
|
3554
3591
|
return;
|
|
3555
|
-
yield*
|
|
3556
|
-
yield*
|
|
3592
|
+
yield* guardCreate3(news, output === undefined);
|
|
3593
|
+
yield* guardUpdate3(news);
|
|
3557
3594
|
if (output === undefined)
|
|
3558
3595
|
return;
|
|
3559
3596
|
const live = yield* readMetricServer(news);
|
|
3560
3597
|
if (live === undefined) {
|
|
3561
|
-
yield*
|
|
3598
|
+
yield* guardCreate3(news, true);
|
|
3562
3599
|
return { action: "update" };
|
|
3563
3600
|
}
|
|
3564
3601
|
return { action: metricServerSpec.matches(live, news) ? "noop" : "update" };
|
|
3565
3602
|
}),
|
|
3566
|
-
reconcile:
|
|
3603
|
+
reconcile: Effect43.fn(function* ({ news }) {
|
|
3567
3604
|
const live = yield* readMetricServer(news);
|
|
3568
|
-
yield*
|
|
3569
|
-
yield*
|
|
3605
|
+
yield* guardCreate3(news, live === undefined);
|
|
3606
|
+
yield* guardUpdate3(news);
|
|
3570
3607
|
if (live === undefined) {
|
|
3571
|
-
yield* runPve(news.target, "provision", true,
|
|
3608
|
+
yield* runPve(news.target, "provision", true, cluster6.updateClusterMetricsServer({
|
|
3572
3609
|
...metricServerRequest(news, metricServerSpec.createForm(news)),
|
|
3573
3610
|
type: news.type
|
|
3574
3611
|
}));
|
|
3575
3612
|
} else {
|
|
3576
3613
|
const form = formToSend(metricServerSpec.matches, live, news, metricServerSpec.updateForm(news));
|
|
3577
3614
|
if (form !== undefined)
|
|
3578
|
-
yield* runPve(news.target, "provision", true,
|
|
3615
|
+
yield* runPve(news.target, "provision", true, cluster6.putClusterMetricsServer(metricServerRequest(news, form)));
|
|
3579
3616
|
}
|
|
3580
3617
|
const after = yield* readMetricServer(news);
|
|
3581
3618
|
if (after === undefined)
|
|
3582
|
-
return yield*
|
|
3619
|
+
return yield* Effect43.fail(new Error(`${metricServerSpec.path(news)}: write returned success but the resource is still absent`));
|
|
3583
3620
|
return after;
|
|
3584
3621
|
}),
|
|
3585
3622
|
delete: ({ olds }) => deleteMetricServer(olds)
|
|
@@ -3588,7 +3625,11 @@ var ProxmoxMetricServerProvider = () => Provider14.effect(ProxmoxMetricServer, E
|
|
|
3588
3625
|
import { Resource as Resource15 } from "alchemy";
|
|
3589
3626
|
import { isResolved as isResolved13 } from "alchemy/Diff";
|
|
3590
3627
|
import * as Provider15 from "alchemy/Provider";
|
|
3591
|
-
import * as
|
|
3628
|
+
import * as Effect46 from "effect/Effect";
|
|
3629
|
+
|
|
3630
|
+
// src/proxmox/network-apply-run.ts
|
|
3631
|
+
import * as nodes12 from "@distilled.cloud/proxmox/nodes";
|
|
3632
|
+
import * as Effect45 from "effect/Effect";
|
|
3592
3633
|
|
|
3593
3634
|
// src/proxmox/apply-endpoints.ts
|
|
3594
3635
|
var NETWORK_APPLY_ENDPOINT = "pve:PUT /nodes/{node}/network";
|
|
@@ -3596,17 +3637,52 @@ var SDN_APPLY_ENDPOINT = "pve:PUT /cluster/sdn";
|
|
|
3596
3637
|
var guardNetworkApply = guardForm(NETWORK_APPLY_ENDPOINT, {}, true);
|
|
3597
3638
|
var guardSdnApply = guardForm(SDN_APPLY_ENDPOINT, {}, true);
|
|
3598
3639
|
|
|
3599
|
-
// src/proxmox/network-apply.ts
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
const
|
|
3606
|
-
return
|
|
3640
|
+
// src/proxmox/network-apply-read.ts
|
|
3641
|
+
import * as cluster7 from "@distilled.cloud/proxmox/cluster";
|
|
3642
|
+
import * as nodes11 from "@distilled.cloud/proxmox/nodes";
|
|
3643
|
+
import * as Effect44 from "effect/Effect";
|
|
3644
|
+
var stagedDiff = (target, node) => Effect44.gen(function* () {
|
|
3645
|
+
const credential = yield* leased(target, "read");
|
|
3646
|
+
const live = yield* runPveWith(target, credential, false, nodes11.listNodeNetwork({ node }));
|
|
3647
|
+
return Array.isArray(live) ? "" : live.changes;
|
|
3648
|
+
});
|
|
3649
|
+
var pendingLines = (diff2) => {
|
|
3650
|
+
if (diff2 === "")
|
|
3651
|
+
return 0;
|
|
3652
|
+
const changed = diff2.split(`
|
|
3653
|
+
`).filter((line) => (line.startsWith("+") || line.startsWith("-")) && !line.startsWith("+++") && !line.startsWith("---")).length;
|
|
3654
|
+
return Math.max(changed, 1);
|
|
3655
|
+
};
|
|
3656
|
+
var pendingCount = (target, node) => stagedDiff(target, node).pipe(Effect44.map(pendingLines));
|
|
3657
|
+
var clusterHealth = (target) => runPve(target, "read", false, cluster7.listClusterStatus({})).pipe(Effect44.map((rows) => {
|
|
3658
|
+
const all = rows ?? [];
|
|
3659
|
+
const cluster8 = all.find((row) => row.type === "cluster");
|
|
3660
|
+
return {
|
|
3661
|
+
offline: all.filter((row) => row.type === "node" && !bool(row.online)).map((row) => text(row.name, "<unnamed>")),
|
|
3662
|
+
quorate: cluster8 === undefined ? true : bool(cluster8.quorate)
|
|
3663
|
+
};
|
|
3664
|
+
}));
|
|
3665
|
+
var degradedReason = (target) => clusterHealth(target).pipe(Effect44.map((health) => health.quorate && health.offline.length === 0 ? undefined : `quorate=${String(health.quorate)} offline=[${health.offline.join(", ")}]`));
|
|
3666
|
+
var UNREACHABLE = "the task could not be read -- the node stopped answering, or this lease cannot see it";
|
|
3667
|
+
var STILL_RUNNING = "still running when this provider stopped waiting";
|
|
3668
|
+
var ATTEMPTS = 30;
|
|
3669
|
+
var awaitTask = (target, credential, node, upid, attempts = ATTEMPTS) => Effect44.gen(function* () {
|
|
3670
|
+
for (let attempt = 0;attempt < attempts; attempt += 1) {
|
|
3671
|
+
const status = yield* runPveWith(target, credential, false, nodes11.getNodeTaskStatus({ node, upid })).pipe(Effect44.orElseSucceed(() => {
|
|
3672
|
+
return;
|
|
3673
|
+
}));
|
|
3674
|
+
if (status === undefined)
|
|
3675
|
+
return UNREACHABLE;
|
|
3676
|
+
if (status.status === "stopped")
|
|
3677
|
+
return text(status.exitstatus, "stopped with no exitstatus");
|
|
3678
|
+
yield* Effect44.sleep("1 second");
|
|
3679
|
+
}
|
|
3680
|
+
return STILL_RUNNING;
|
|
3607
3681
|
});
|
|
3608
|
-
|
|
3609
|
-
|
|
3682
|
+
|
|
3683
|
+
// src/proxmox/network-apply-run.ts
|
|
3684
|
+
var refuse = (node, why) => Effect45.die(new Error(`Proxmox.NetworkApply ${node}: ${why}`));
|
|
3685
|
+
var applyNetwork = (props) => Effect45.gen(function* () {
|
|
3610
3686
|
const node = props.node;
|
|
3611
3687
|
const staged = yield* pendingCount(props.target, node);
|
|
3612
3688
|
if (staged === 0)
|
|
@@ -3617,8 +3693,8 @@ var apply = (props) => Effect42.gen(function* () {
|
|
|
3617
3693
|
}
|
|
3618
3694
|
yield* guardNetworkApply;
|
|
3619
3695
|
const credential = yield* mint(props.target, "provision");
|
|
3620
|
-
const upid = yield*
|
|
3621
|
-
if (upid ===
|
|
3696
|
+
const upid = text(yield* runPveWith(props.target, credential, true, nodes12.putNodeNetwork({ node })));
|
|
3697
|
+
if (upid === "") {
|
|
3622
3698
|
return yield* refuse(node, 'the apply returned no UPID. PVE wraps every answer in {"data":...} and can report success ' + "on a call that did nothing -- the reload cannot be confirmed, so it is not claimed.");
|
|
3623
3699
|
}
|
|
3624
3700
|
const exitstatus = yield* awaitTask(props.target, credential, node, upid);
|
|
@@ -3635,20 +3711,30 @@ var apply = (props) => Effect42.gen(function* () {
|
|
|
3635
3711
|
}
|
|
3636
3712
|
return { node, pending: left };
|
|
3637
3713
|
});
|
|
3638
|
-
|
|
3639
|
-
|
|
3714
|
+
|
|
3715
|
+
// src/proxmox/network-apply.ts
|
|
3716
|
+
var ProxmoxNetworkApply = Resource15("Proxmox.NetworkApply");
|
|
3717
|
+
var read2 = (props) => pendingCount(props.target, props.node).pipe(Effect46.map((pending) => ({ node: props.node, pending })));
|
|
3718
|
+
var diff2 = (news) => Effect46.gen(function* () {
|
|
3719
|
+
if (!isResolved13(news))
|
|
3720
|
+
return;
|
|
3721
|
+
const { pending } = yield* read2(news);
|
|
3722
|
+
return pending === 0 ? { action: "noop" } : { action: "update" };
|
|
3723
|
+
});
|
|
3724
|
+
var handlers6 = {
|
|
3725
|
+
delete: () => Effect46.void,
|
|
3640
3726
|
diff: ({ news }) => diff2(news),
|
|
3641
|
-
list: () =>
|
|
3727
|
+
list: () => Effect46.succeed([]),
|
|
3642
3728
|
read: ({ olds }) => read2(olds),
|
|
3643
|
-
reconcile: ({ news }) =>
|
|
3729
|
+
reconcile: ({ news }) => applyNetwork(news)
|
|
3644
3730
|
};
|
|
3645
|
-
var ProxmoxNetworkApplyProvider = () => Provider15.effect(ProxmoxNetworkApply,
|
|
3731
|
+
var ProxmoxNetworkApplyProvider = () => Provider15.effect(ProxmoxNetworkApply, Effect46.succeed(ProxmoxNetworkApply.Provider.of(handlers6)));
|
|
3646
3732
|
// src/proxmox/node-network.ts
|
|
3647
3733
|
import { Resource as Resource16 } from "alchemy";
|
|
3648
3734
|
import { isResolved as isResolved14 } from "alchemy/Diff";
|
|
3649
3735
|
import * as Provider16 from "alchemy/Provider";
|
|
3650
|
-
import * as
|
|
3651
|
-
import * as
|
|
3736
|
+
import * as nodes14 from "@distilled.cloud/proxmox/nodes";
|
|
3737
|
+
import * as Effect48 from "effect/Effect";
|
|
3652
3738
|
|
|
3653
3739
|
// src/proxmox/node-network-form.ts
|
|
3654
3740
|
var NODE_NETWORK_CREATE = "pve:POST /nodes/{node}/network";
|
|
@@ -3690,8 +3776,8 @@ var updateForm2 = (props) => {
|
|
|
3690
3776
|
};
|
|
3691
3777
|
|
|
3692
3778
|
// src/proxmox/node-network-wire.ts
|
|
3693
|
-
import * as
|
|
3694
|
-
import * as
|
|
3779
|
+
import * as nodes13 from "@distilled.cloud/proxmox/nodes";
|
|
3780
|
+
import * as Effect47 from "effect/Effect";
|
|
3695
3781
|
var attributesOf6 = (live, props) => {
|
|
3696
3782
|
const data = live;
|
|
3697
3783
|
const liveType = text(data["type"]);
|
|
@@ -3725,12 +3811,12 @@ var attributesOf6 = (live, props) => {
|
|
|
3725
3811
|
"vlan-raw-device": text(data["vlan-raw-device"])
|
|
3726
3812
|
};
|
|
3727
3813
|
};
|
|
3728
|
-
var readInterfaceOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false,
|
|
3814
|
+
var readInterfaceOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, nodes13.getNodeNetwork({ iface: props.iface, node: props.node })).pipe(Effect47.catchTag("NetworkInterfaceNotFound", () => Effect47.succeed(undefined)))).pipe(Effect47.map((live) => {
|
|
3729
3815
|
if (live === UNREADABLE)
|
|
3730
3816
|
return UNREADABLE;
|
|
3731
3817
|
return live === undefined ? undefined : attributesOf6(live, props);
|
|
3732
3818
|
}));
|
|
3733
|
-
var readInterface = (props) => readInterfaceOrFail(props).pipe(
|
|
3819
|
+
var readInterface = (props) => readInterfaceOrFail(props).pipe(Effect47.orElseSucceed(() => {
|
|
3734
3820
|
return;
|
|
3735
3821
|
}));
|
|
3736
3822
|
var dropUnreadable4 = (live) => live === UNREADABLE ? undefined : live;
|
|
@@ -3755,12 +3841,12 @@ var toDistilledUpdate2 = (props) => ({
|
|
|
3755
3841
|
iface: props.iface,
|
|
3756
3842
|
node: props.node
|
|
3757
3843
|
});
|
|
3758
|
-
var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork,
|
|
3759
|
-
list: () =>
|
|
3760
|
-
read:
|
|
3844
|
+
var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork, Effect48.succeed(ProxmoxNodeNetwork.Provider.of({
|
|
3845
|
+
list: () => Effect48.succeed([]),
|
|
3846
|
+
read: Effect48.fn(function* ({ olds, output }) {
|
|
3761
3847
|
return dropUnreadable4(yield* output === undefined ? readInterface(olds) : readInterfaceOrFail(olds));
|
|
3762
3848
|
}),
|
|
3763
|
-
diff:
|
|
3849
|
+
diff: Effect48.fn(function* ({ news, output }) {
|
|
3764
3850
|
if (!isResolved14(news))
|
|
3765
3851
|
return;
|
|
3766
3852
|
yield* guardWrite2(NODE_NETWORK_CREATE, createForm5(news), output === undefined);
|
|
@@ -3778,33 +3864,33 @@ var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork, Eff
|
|
|
3778
3864
|
}
|
|
3779
3865
|
return matches5(live, news) ? { action: "noop" } : { action: "update" };
|
|
3780
3866
|
}),
|
|
3781
|
-
reconcile:
|
|
3867
|
+
reconcile: Effect48.fn(function* ({ news }) {
|
|
3782
3868
|
const before = dropUnreadable4(yield* readInterface(news));
|
|
3783
3869
|
yield* guardWrite2(NODE_NETWORK_CREATE, createForm5(news), before === undefined);
|
|
3784
3870
|
yield* guardWrite2(NODE_NETWORK_UPDATE, updateForm2(news), false);
|
|
3785
3871
|
if (before === undefined) {
|
|
3786
|
-
yield* runPve(news.target, "provision", true,
|
|
3872
|
+
yield* runPve(news.target, "provision", true, nodes14.createNodeNetwork(toDistilledCreate3(news)));
|
|
3787
3873
|
} else if (!matches5(before, news)) {
|
|
3788
|
-
yield* runPve(news.target, "provision", true,
|
|
3874
|
+
yield* runPve(news.target, "provision", true, nodes14.putNodeNetwork2(toDistilledUpdate2(news)));
|
|
3789
3875
|
}
|
|
3790
3876
|
const after = dropUnreadable4(yield* readInterface(news));
|
|
3791
3877
|
if (after === undefined) {
|
|
3792
|
-
return yield*
|
|
3878
|
+
return yield* Effect48.die(new Error(`nodes/${news.node}/network/${news.iface}: wrote with no error, but the ` + 'interface is still absent -- PVE wraps every answer in {"data":...} and can ' + "report success on a call that did nothing, so read back rather than trust it."));
|
|
3793
3879
|
}
|
|
3794
3880
|
return after;
|
|
3795
3881
|
}),
|
|
3796
|
-
delete:
|
|
3797
|
-
yield* runPve(olds.target, "provision", true,
|
|
3882
|
+
delete: Effect48.fn(function* ({ olds }) {
|
|
3883
|
+
yield* runPve(olds.target, "provision", true, nodes14.deleteNodeNetwork2({ iface: olds.iface, node: olds.node }));
|
|
3798
3884
|
})
|
|
3799
3885
|
})));
|
|
3800
3886
|
// src/proxmox/notification-matcher.ts
|
|
3801
3887
|
import { Resource as Resource17 } from "alchemy";
|
|
3802
3888
|
import * as Provider17 from "alchemy/Provider";
|
|
3803
|
-
import * as
|
|
3889
|
+
import * as Effect52 from "effect/Effect";
|
|
3804
3890
|
|
|
3805
3891
|
// src/proxmox/notification-matcher-lifecycle.ts
|
|
3806
3892
|
import { isResolved as isResolved15 } from "alchemy/Diff";
|
|
3807
|
-
import * as
|
|
3893
|
+
import * as Effect51 from "effect/Effect";
|
|
3808
3894
|
|
|
3809
3895
|
// src/proxmox/notification-matcher-form.ts
|
|
3810
3896
|
var list = (value) => Array.isArray(value) ? value.map((item) => String(item)) : [];
|
|
@@ -3881,49 +3967,49 @@ var matcherUpdateForm = (fields) => {
|
|
|
3881
3967
|
};
|
|
3882
3968
|
|
|
3883
3969
|
// src/proxmox/notification-matcher-distilled.ts
|
|
3884
|
-
import * as
|
|
3885
|
-
import * as
|
|
3970
|
+
import * as cluster8 from "@distilled.cloud/proxmox/cluster";
|
|
3971
|
+
import * as Effect50 from "effect/Effect";
|
|
3886
3972
|
|
|
3887
3973
|
// src/proxmox/notification-sdk-input.ts
|
|
3888
|
-
import { ProxmoxParseError as
|
|
3974
|
+
import { ProxmoxParseError as ProxmoxParseError4 } from "@distilled.cloud/proxmox/Errors";
|
|
3889
3975
|
import * as Data2 from "effect/Data";
|
|
3890
|
-
import * as
|
|
3976
|
+
import * as Effect49 from "effect/Effect";
|
|
3891
3977
|
import * as Schema3 from "effect/Schema";
|
|
3892
3978
|
|
|
3893
3979
|
class NotificationRequestInvalid extends Data2.TaggedError("NotificationRequestInvalid") {
|
|
3894
3980
|
}
|
|
3895
|
-
var notificationRequest = (schema, fields) => Schema3.decodeUnknownEffect(Schema3.toType(schema), { onExcessProperty: "error" })(Object.fromEntries(Object.entries(fields).map(([key, value]) => [key.replaceAll("-", "_"), value]))).pipe(
|
|
3896
|
-
var notificationResponse = (schema) => (body5) => Schema3.decodeUnknownEffect(Schema3.toType(schema))(body5).pipe(
|
|
3981
|
+
var notificationRequest = (schema, fields) => Schema3.decodeUnknownEffect(Schema3.toType(schema), { onExcessProperty: "error" })(Object.fromEntries(Object.entries(fields).map(([key, value]) => [key.replaceAll("-", "_"), value]))).pipe(Effect49.mapError(() => new NotificationRequestInvalid({ operation: "PVE notification write" })));
|
|
3982
|
+
var notificationResponse = (schema) => (body5) => Schema3.decodeUnknownEffect(Schema3.toType(schema))(body5).pipe(Effect49.as(body5), Effect49.mapError(() => new ProxmoxParseError4({
|
|
3897
3983
|
body: undefined,
|
|
3898
3984
|
cause: "PVE notification response does not match its SDK schema"
|
|
3899
3985
|
})));
|
|
3900
3986
|
|
|
3901
3987
|
// src/proxmox/notification-matcher-distilled.ts
|
|
3902
|
-
var readMatcher = (props) => runPve(props.target, "read", false,
|
|
3988
|
+
var readMatcher = (props) => runPve(props.target, "read", false, cluster8.getClusterNotificationMatcher({ name: props.name })).pipe(Effect50.flatMap(notificationResponse(cluster8.GetClusterNotificationMatcherResponse)), Effect50.map((live) => matcherAttributes({
|
|
3903
3989
|
...live,
|
|
3904
3990
|
"invert-match": live.invert_match,
|
|
3905
3991
|
"match-calendar": live.match_calendar,
|
|
3906
3992
|
"match-field": live.match_field,
|
|
3907
3993
|
"match-severity": live.match_severity
|
|
3908
|
-
}, props.name)),
|
|
3909
|
-
var createMatcher = (props) =>
|
|
3910
|
-
const input = yield* notificationRequest(
|
|
3911
|
-
yield* runPve(props.target, "provision", true,
|
|
3994
|
+
}, props.name)), Effect50.catchTag("NotFound", () => Effect50.succeed(undefined)));
|
|
3995
|
+
var createMatcher = (props) => Effect50.gen(function* () {
|
|
3996
|
+
const input = yield* notificationRequest(cluster8.CreateClusterNotificationMatcherRequest, matcherCreateForm(props));
|
|
3997
|
+
yield* runPve(props.target, "provision", true, cluster8.createClusterNotificationMatcher(input));
|
|
3912
3998
|
});
|
|
3913
|
-
var updateMatcher = (props) =>
|
|
3914
|
-
const input = yield* notificationRequest(
|
|
3999
|
+
var updateMatcher = (props) => Effect50.gen(function* () {
|
|
4000
|
+
const input = yield* notificationRequest(cluster8.PutClusterNotificationMatcherRequest, {
|
|
3915
4001
|
...matcherUpdateForm(props),
|
|
3916
4002
|
name: props.name
|
|
3917
4003
|
});
|
|
3918
|
-
yield* runPve(props.target, "provision", true,
|
|
4004
|
+
yield* runPve(props.target, "provision", true, cluster8.putClusterNotificationMatcher(input));
|
|
3919
4005
|
});
|
|
3920
|
-
var deleteMatcher = (props) => runPve(props.target, "provision", true,
|
|
4006
|
+
var deleteMatcher = (props) => runPve(props.target, "provision", true, cluster8.deleteClusterNotificationMatcher({ name: props.name })).pipe(Effect50.catchTag("NotFound", () => Effect50.void));
|
|
3921
4007
|
|
|
3922
4008
|
// src/proxmox/notification-matcher-lifecycle.ts
|
|
3923
|
-
var guard = (props, create) => guardForm("pve:POST /cluster/notifications/matchers", matcherCreateForm(props), create).pipe(
|
|
4009
|
+
var guard = (props, create) => guardForm("pve:POST /cluster/notifications/matchers", matcherCreateForm(props), create).pipe(Effect51.andThen(guardForm("pve:PUT /cluster/notifications/matchers/{name}", matcherUpdateForm(props), false)));
|
|
3924
4010
|
var matcherHandlers = {
|
|
3925
|
-
list: () =>
|
|
3926
|
-
read: ({ olds }) =>
|
|
4011
|
+
list: () => Effect51.succeed([]),
|
|
4012
|
+
read: ({ olds }) => Effect51.gen(function* () {
|
|
3927
4013
|
const live = yield* readMatcher(olds);
|
|
3928
4014
|
yield* guard(olds, live === undefined);
|
|
3929
4015
|
return live;
|
|
@@ -3932,7 +4018,7 @@ var matcherHandlers = {
|
|
|
3932
4018
|
news,
|
|
3933
4019
|
olds,
|
|
3934
4020
|
output
|
|
3935
|
-
}) =>
|
|
4021
|
+
}) => Effect51.gen(function* () {
|
|
3936
4022
|
if (!isResolved15(news))
|
|
3937
4023
|
return;
|
|
3938
4024
|
if (olds.name !== news.name)
|
|
@@ -3947,7 +4033,7 @@ var matcherHandlers = {
|
|
|
3947
4033
|
}
|
|
3948
4034
|
return { action: matcherMatches(live, news) ? "noop" : "update" };
|
|
3949
4035
|
}),
|
|
3950
|
-
reconcile: ({ news }) =>
|
|
4036
|
+
reconcile: ({ news }) => Effect51.gen(function* () {
|
|
3951
4037
|
const live = yield* readMatcher(news);
|
|
3952
4038
|
yield* guard(news, live === undefined);
|
|
3953
4039
|
if (live === undefined)
|
|
@@ -3956,7 +4042,7 @@ var matcherHandlers = {
|
|
|
3956
4042
|
yield* updateMatcher(news);
|
|
3957
4043
|
const after = yield* readMatcher(news);
|
|
3958
4044
|
if (after === undefined)
|
|
3959
|
-
return yield*
|
|
4045
|
+
return yield* Effect51.die(new Error(`Notification matcher ${news.name}: write returned but read-back is absent.`));
|
|
3960
4046
|
return after;
|
|
3961
4047
|
}),
|
|
3962
4048
|
delete: ({ olds }) => deleteMatcher(olds)
|
|
@@ -3964,15 +4050,15 @@ var matcherHandlers = {
|
|
|
3964
4050
|
|
|
3965
4051
|
// src/proxmox/notification-matcher.ts
|
|
3966
4052
|
var ProxmoxNotificationMatcher = Resource17("Proxmox.NotificationMatcher");
|
|
3967
|
-
var ProxmoxNotificationMatcherProvider = () => Provider17.effect(ProxmoxNotificationMatcher,
|
|
4053
|
+
var ProxmoxNotificationMatcherProvider = () => Provider17.effect(ProxmoxNotificationMatcher, Effect52.succeed(ProxmoxNotificationMatcher.Provider.of(matcherHandlers)));
|
|
3968
4054
|
// src/proxmox/notification-target.ts
|
|
3969
4055
|
import { Resource as Resource18 } from "alchemy";
|
|
3970
4056
|
import * as Provider18 from "alchemy/Provider";
|
|
3971
|
-
import * as
|
|
4057
|
+
import * as Effect55 from "effect/Effect";
|
|
3972
4058
|
|
|
3973
4059
|
// src/proxmox/notification-target-lifecycle.ts
|
|
3974
4060
|
import { isResolved as isResolved16 } from "alchemy/Diff";
|
|
3975
|
-
import * as
|
|
4061
|
+
import * as Effect54 from "effect/Effect";
|
|
3976
4062
|
|
|
3977
4063
|
// src/proxmox/notification-target-endpoint.ts
|
|
3978
4064
|
var ENDPOINTS = {
|
|
@@ -4055,30 +4141,30 @@ var targetSpec = {
|
|
|
4055
4141
|
};
|
|
4056
4142
|
|
|
4057
4143
|
// src/proxmox/notification-target-distilled.ts
|
|
4058
|
-
import * as
|
|
4059
|
-
import * as
|
|
4144
|
+
import * as cluster9 from "@distilled.cloud/proxmox/cluster";
|
|
4145
|
+
import * as Effect53 from "effect/Effect";
|
|
4060
4146
|
var read3 = (props) => {
|
|
4061
4147
|
const request = { name: props.name };
|
|
4062
4148
|
switch (props.type) {
|
|
4063
4149
|
case "gotify":
|
|
4064
|
-
return
|
|
4150
|
+
return cluster9.getClusterNotificationEndpointGotify(request).pipe(Effect53.flatMap(notificationResponse(cluster9.GetClusterNotificationEndpointGotifyResponse)), Effect53.map((row) => ({ ...row })));
|
|
4065
4151
|
case "sendmail":
|
|
4066
|
-
return
|
|
4152
|
+
return cluster9.getClusterNotificationEndpointSendmail(request).pipe(Effect53.flatMap(notificationResponse(cluster9.GetClusterNotificationEndpointSendmailResponse)), Effect53.map((row) => ({
|
|
4067
4153
|
...row,
|
|
4068
4154
|
"from-address": row.from_address,
|
|
4069
4155
|
"mailto-user": row.mailto_user
|
|
4070
4156
|
})));
|
|
4071
4157
|
case "smtp":
|
|
4072
|
-
return
|
|
4158
|
+
return cluster9.getClusterNotificationEndpointSmtp(request).pipe(Effect53.flatMap(notificationResponse(cluster9.GetClusterNotificationEndpointSmtpResponse)), Effect53.map((row) => ({
|
|
4073
4159
|
...row,
|
|
4074
4160
|
"from-address": row.from_address,
|
|
4075
4161
|
"mailto-user": row.mailto_user
|
|
4076
4162
|
})));
|
|
4077
4163
|
case "webhook":
|
|
4078
|
-
return
|
|
4164
|
+
return cluster9.getClusterNotificationEndpointWebhook(request).pipe(Effect53.flatMap(notificationResponse(cluster9.GetClusterNotificationEndpointWebhookResponse)), Effect53.map((row) => ({ ...row })));
|
|
4079
4165
|
}
|
|
4080
4166
|
};
|
|
4081
|
-
var readTarget = (props) => runPve(props.target, "read", false, read3(props)).pipe(
|
|
4167
|
+
var readTarget = (props) => runPve(props.target, "read", false, read3(props)).pipe(Effect53.map((row) => targetSpec.attributes(row, props)), Effect53.catchTag("NotFound", () => Effect53.succeed(undefined)));
|
|
4082
4168
|
var fields = (props, create) => {
|
|
4083
4169
|
const form = {
|
|
4084
4170
|
...create ? createShape(props) : shape4(props),
|
|
@@ -4112,40 +4198,40 @@ var fields = (props, create) => {
|
|
|
4112
4198
|
};
|
|
4113
4199
|
var validateTargetWrite = (props, create) => {
|
|
4114
4200
|
const schema = create ? {
|
|
4115
|
-
sendmail:
|
|
4116
|
-
smtp:
|
|
4117
|
-
gotify:
|
|
4118
|
-
webhook:
|
|
4201
|
+
sendmail: cluster9.CreateClusterNotificationEndpointSendmailRequest,
|
|
4202
|
+
smtp: cluster9.CreateClusterNotificationEndpointSmtpRequest,
|
|
4203
|
+
gotify: cluster9.CreateClusterNotificationEndpointGotifyRequest,
|
|
4204
|
+
webhook: cluster9.CreateClusterNotificationEndpointWebhookRequest
|
|
4119
4205
|
}[props.type] : {
|
|
4120
|
-
sendmail:
|
|
4121
|
-
smtp:
|
|
4122
|
-
gotify:
|
|
4123
|
-
webhook:
|
|
4206
|
+
sendmail: cluster9.PutClusterNotificationEndpointSendmailRequest,
|
|
4207
|
+
smtp: cluster9.PutClusterNotificationEndpointSmtpRequest,
|
|
4208
|
+
gotify: cluster9.PutClusterNotificationEndpointGotifyRequest,
|
|
4209
|
+
webhook: cluster9.PutClusterNotificationEndpointWebhookRequest
|
|
4124
4210
|
}[props.type];
|
|
4125
|
-
return notificationRequest(schema, fields(props, create)).pipe(
|
|
4211
|
+
return notificationRequest(schema, fields(props, create)).pipe(Effect53.asVoid);
|
|
4126
4212
|
};
|
|
4127
4213
|
var createTarget = (props) => {
|
|
4128
4214
|
const form = fields(props, true);
|
|
4129
|
-
const operation = props.type === "sendmail" ? notificationRequest(
|
|
4215
|
+
const operation = props.type === "sendmail" ? notificationRequest(cluster9.CreateClusterNotificationEndpointSendmailRequest, form).pipe(Effect53.flatMap(cluster9.createClusterNotificationEndpointSendmail)) : props.type === "smtp" ? notificationRequest(cluster9.CreateClusterNotificationEndpointSmtpRequest, form).pipe(Effect53.flatMap(cluster9.createClusterNotificationEndpointSmtp)) : props.type === "gotify" ? notificationRequest(cluster9.CreateClusterNotificationEndpointGotifyRequest, form).pipe(Effect53.flatMap(cluster9.createClusterNotificationEndpointGotify)) : notificationRequest(cluster9.CreateClusterNotificationEndpointWebhookRequest, form).pipe(Effect53.flatMap(cluster9.createClusterNotificationEndpointWebhook));
|
|
4130
4216
|
return runPve(props.target, "provision", true, operation);
|
|
4131
4217
|
};
|
|
4132
4218
|
var updateTarget = (props) => {
|
|
4133
4219
|
const form = fields(props, false);
|
|
4134
|
-
const operation = props.type === "sendmail" ? notificationRequest(
|
|
4220
|
+
const operation = props.type === "sendmail" ? notificationRequest(cluster9.PutClusterNotificationEndpointSendmailRequest, form).pipe(Effect53.flatMap(cluster9.putClusterNotificationEndpointSendmail)) : props.type === "smtp" ? notificationRequest(cluster9.PutClusterNotificationEndpointSmtpRequest, form).pipe(Effect53.flatMap(cluster9.putClusterNotificationEndpointSmtp)) : props.type === "gotify" ? notificationRequest(cluster9.PutClusterNotificationEndpointGotifyRequest, form).pipe(Effect53.flatMap(cluster9.putClusterNotificationEndpointGotify)) : notificationRequest(cluster9.PutClusterNotificationEndpointWebhookRequest, form).pipe(Effect53.flatMap(cluster9.putClusterNotificationEndpointWebhook));
|
|
4135
4221
|
return runPve(props.target, "provision", true, operation);
|
|
4136
4222
|
};
|
|
4137
4223
|
var deleteTarget = (props) => {
|
|
4138
4224
|
const request = { name: props.name };
|
|
4139
|
-
const operation = props.type === "sendmail" ?
|
|
4140
|
-
return runPve(props.target, "provision", true, operation).pipe(
|
|
4225
|
+
const operation = props.type === "sendmail" ? cluster9.deleteClusterNotificationEndpointSendmail(request) : props.type === "smtp" ? cluster9.deleteClusterNotificationEndpointSmtp(request) : props.type === "gotify" ? cluster9.deleteClusterNotificationEndpointGotify(request) : cluster9.deleteClusterNotificationEndpointWebhook(request);
|
|
4226
|
+
return runPve(props.target, "provision", true, operation).pipe(Effect53.catchTag("NotFound", () => Effect53.void));
|
|
4141
4227
|
};
|
|
4142
4228
|
|
|
4143
4229
|
// src/proxmox/notification-target-lifecycle.ts
|
|
4144
|
-
var { guardCreate:
|
|
4145
|
-
var guard2 = (props, create) =>
|
|
4230
|
+
var { guardCreate: guardCreate4, guardUpdate: guardUpdate4 } = specGuards(targetSpec);
|
|
4231
|
+
var guard2 = (props, create) => guardCreate4(props, create).pipe(Effect54.andThen(guardUpdate4(props)));
|
|
4146
4232
|
var targetHandlers = {
|
|
4147
|
-
list: () =>
|
|
4148
|
-
read: ({ olds }) =>
|
|
4233
|
+
list: () => Effect54.succeed([]),
|
|
4234
|
+
read: ({ olds }) => Effect54.gen(function* () {
|
|
4149
4235
|
const live = yield* readTarget(olds);
|
|
4150
4236
|
yield* guard2(olds, live === undefined);
|
|
4151
4237
|
return live;
|
|
@@ -4154,7 +4240,7 @@ var targetHandlers = {
|
|
|
4154
4240
|
news,
|
|
4155
4241
|
olds,
|
|
4156
4242
|
output
|
|
4157
|
-
}) =>
|
|
4243
|
+
}) => Effect54.gen(function* () {
|
|
4158
4244
|
if (!isResolved16(news))
|
|
4159
4245
|
return;
|
|
4160
4246
|
if (olds.name !== news.name || olds.type !== news.type) {
|
|
@@ -4173,7 +4259,7 @@ var targetHandlers = {
|
|
|
4173
4259
|
}
|
|
4174
4260
|
return { action: targetSpec.matches(live, news) ? "noop" : "update" };
|
|
4175
4261
|
}),
|
|
4176
|
-
reconcile: ({ news }) =>
|
|
4262
|
+
reconcile: ({ news }) => Effect54.gen(function* () {
|
|
4177
4263
|
const live = yield* readTarget(news);
|
|
4178
4264
|
yield* guard2(news, live === undefined);
|
|
4179
4265
|
if (live === undefined)
|
|
@@ -4182,7 +4268,7 @@ var targetHandlers = {
|
|
|
4182
4268
|
yield* updateTarget(news);
|
|
4183
4269
|
const after = yield* readTarget(news);
|
|
4184
4270
|
if (after === undefined)
|
|
4185
|
-
return yield*
|
|
4271
|
+
return yield* Effect54.die(new Error(`Notification target ${news.name}: write returned but read-back is absent.`));
|
|
4186
4272
|
return after;
|
|
4187
4273
|
}),
|
|
4188
4274
|
delete: ({ olds }) => deleteTarget(olds)
|
|
@@ -4190,15 +4276,15 @@ var targetHandlers = {
|
|
|
4190
4276
|
|
|
4191
4277
|
// src/proxmox/notification-target.ts
|
|
4192
4278
|
var ProxmoxNotificationTarget = Resource18("Proxmox.NotificationTarget");
|
|
4193
|
-
var ProxmoxNotificationTargetProvider = () => Provider18.effect(ProxmoxNotificationTarget,
|
|
4279
|
+
var ProxmoxNotificationTargetProvider = () => Provider18.effect(ProxmoxNotificationTarget, Effect55.succeed(ProxmoxNotificationTarget.Provider.of(targetHandlers)));
|
|
4194
4280
|
// src/proxmox/pbs-datastore.ts
|
|
4195
4281
|
import { Resource as Resource19 } from "alchemy";
|
|
4196
4282
|
import * as Provider19 from "alchemy/Provider";
|
|
4197
|
-
import * as
|
|
4283
|
+
import * as Effect61 from "effect/Effect";
|
|
4198
4284
|
|
|
4199
4285
|
// src/proxmox/pbs-datastore-lifecycle.ts
|
|
4200
4286
|
import { isResolved as isResolved17 } from "alchemy/Diff";
|
|
4201
|
-
import * as
|
|
4287
|
+
import * as Effect60 from "effect/Effect";
|
|
4202
4288
|
|
|
4203
4289
|
// src/proxmox/pbs-datastore-form.ts
|
|
4204
4290
|
var attributes = (live, props) => ({
|
|
@@ -4255,18 +4341,18 @@ var object2 = (props) => `config/datastore/${props.name}`;
|
|
|
4255
4341
|
|
|
4256
4342
|
// src/proxmox/pbs-datastore-wire.ts
|
|
4257
4343
|
import * as config from "@distilled.cloud/proxmox-backup/config";
|
|
4258
|
-
import * as
|
|
4344
|
+
import * as Effect57 from "effect/Effect";
|
|
4259
4345
|
|
|
4260
4346
|
// src/proxmox/distilled-pbs.ts
|
|
4261
4347
|
import { credentials } from "@distilled.cloud/proxmox-backup/Credentials";
|
|
4262
4348
|
import * as Retry from "@distilled.cloud/proxmox-backup/Retry";
|
|
4263
|
-
import * as
|
|
4264
|
-
var runPbsWith = (target, credential, op, timeout5 = MEMBER_TIMEOUT) => op.pipe(Retry.none,
|
|
4349
|
+
import * as Effect56 from "effect/Effect";
|
|
4350
|
+
var runPbsWith = (target, credential, op, timeout5 = MEMBER_TIMEOUT) => op.pipe(Retry.none, Effect56.provide(credentials({
|
|
4265
4351
|
baseUrl: target.api,
|
|
4266
4352
|
secret: credential.secret,
|
|
4267
4353
|
tokenId: credential.tokenId
|
|
4268
|
-
})),
|
|
4269
|
-
var runPbs = (target, role, op) =>
|
|
4354
|
+
})), Effect56.timeout(timeout5));
|
|
4355
|
+
var runPbs = (target, role, op) => Effect56.gen(function* () {
|
|
4270
4356
|
const credential = yield* leased(target, role);
|
|
4271
4357
|
return yield* runPbsWith(target, credential, op);
|
|
4272
4358
|
});
|
|
@@ -4316,52 +4402,52 @@ var wire = (row) => ({
|
|
|
4316
4402
|
"prune-schedule": row.prune_schedule,
|
|
4317
4403
|
"verify-new": row.verify_new
|
|
4318
4404
|
});
|
|
4319
|
-
var readOne = (props) => runPbs(props.target, "read", config.getConfigDatastore({ name: props.name })).pipe(
|
|
4405
|
+
var readOne = (props) => runPbs(props.target, "read", config.getConfigDatastore({ name: props.name })).pipe(Effect57.map((live) => attributes(wire(live), props)), Effect57.catchTag("DatastoreNotFound", () => Effect57.succeed(undefined)));
|
|
4320
4406
|
var createOne = (props) => runPbs(props.target, "provision", config.createConfigDatastore(createRequest(props)));
|
|
4321
4407
|
var updateOne = (props) => runPbs(props.target, "provision", config.putConfigDatastore(updateRequest(props)));
|
|
4322
|
-
var deleteOne = (props) =>
|
|
4408
|
+
var deleteOne = (props) => Effect57.gen(function* () {
|
|
4323
4409
|
if ((yield* readOne(props)) === undefined)
|
|
4324
4410
|
return;
|
|
4325
4411
|
return yield* runPbs(props.target, "provision", config.deleteConfigDatastore({ name: props.name }));
|
|
4326
4412
|
});
|
|
4327
4413
|
|
|
4328
4414
|
// src/proxmox/pbs-datastore-guard.ts
|
|
4329
|
-
import * as
|
|
4415
|
+
import * as Effect58 from "effect/Effect";
|
|
4330
4416
|
var samePath = (left, right) => {
|
|
4331
4417
|
const trim = (value) => value.length > 1 ? value.replace(/\/+$/, "") : value;
|
|
4332
4418
|
return trim(left) === trim(right);
|
|
4333
4419
|
};
|
|
4334
|
-
var guardPath = (live, props) => live.path === "" || samePath(live.path, props.path) ?
|
|
4335
|
-
var guardBackend = (live, props) => props.backend === undefined || propertyString(props.backend) === live.backend ?
|
|
4336
|
-
var settle2 = (read4, done) =>
|
|
4420
|
+
var guardPath = (live, props) => live.path === "" || samePath(live.path, props.path) ? Effect58.void : Effect58.die(new Error(`${object2(props)}: declared path ${props.path} but PBS holds ${live.path}. \`path\` ` + "is create-only, so this cannot be reconciled and will NOT be planned as a silent " + "noop: backups would keep landing in the old directory behind a green plan. Correct " + "the declaration, or remove the datastore and declare a new one -- the contents live, " + "because `destroy-data` is never sent."));
|
|
4421
|
+
var guardBackend = (live, props) => props.backend === undefined || propertyString(props.backend) === live.backend ? Effect58.void : Effect58.die(new Error(`${object2(props)}: declared backend ${props.backend} but PBS holds ` + `${live.backend === "" ? "(none — a local datastore)" : live.backend}. \`backend\` ` + "is create-only: PBS has no update for it, so this cannot be reconciled and will NOT " + "be planned as a silent noop. Correct the declaration to match what is really there, " + "or remove the datastore and declare a new one -- the contents live, because " + "`destroy-data` is never sent."));
|
|
4422
|
+
var settle2 = (read4, done) => Effect58.gen(function* () {
|
|
4337
4423
|
for (let attempt = 0;attempt < 60; attempt += 1) {
|
|
4338
4424
|
const live = yield* read4;
|
|
4339
4425
|
if (done(live))
|
|
4340
4426
|
return live;
|
|
4341
|
-
yield*
|
|
4427
|
+
yield* Effect58.sleep("2 seconds");
|
|
4342
4428
|
}
|
|
4343
4429
|
return yield* read4;
|
|
4344
4430
|
});
|
|
4345
4431
|
|
|
4346
4432
|
// src/proxmox/pbs-datastore-endpoint.ts
|
|
4347
|
-
import * as
|
|
4433
|
+
import * as Effect59 from "effect/Effect";
|
|
4348
4434
|
var DATASTORE_ENDPOINT = {
|
|
4349
4435
|
create: "pbs:POST /config/datastore",
|
|
4350
4436
|
update: "pbs:PUT /config/datastore/{name}"
|
|
4351
4437
|
};
|
|
4352
|
-
var guardDatastoreForms = (props) =>
|
|
4438
|
+
var guardDatastoreForms = (props) => Effect59.gen(function* () {
|
|
4353
4439
|
yield* guardForm(DATASTORE_ENDPOINT.create, createForm6(props), true);
|
|
4354
4440
|
yield* guardForm(DATASTORE_ENDPOINT.update, updateForm3(props), false);
|
|
4355
4441
|
});
|
|
4356
4442
|
|
|
4357
4443
|
// src/proxmox/pbs-datastore-lifecycle.ts
|
|
4358
|
-
var
|
|
4359
|
-
list: () =>
|
|
4444
|
+
var handlers7 = {
|
|
4445
|
+
list: () => Effect60.succeed([]),
|
|
4360
4446
|
read: ({ olds }) => readOne(olds),
|
|
4361
4447
|
diff: ({
|
|
4362
4448
|
news,
|
|
4363
4449
|
output
|
|
4364
|
-
}) =>
|
|
4450
|
+
}) => Effect60.gen(function* () {
|
|
4365
4451
|
if (!isResolved17(news))
|
|
4366
4452
|
return;
|
|
4367
4453
|
yield* guardDatastoreForms(news);
|
|
@@ -4374,7 +4460,7 @@ var handlers8 = {
|
|
|
4374
4460
|
yield* guardBackend(live, news);
|
|
4375
4461
|
return matches6(live, news) ? { action: "noop" } : { action: "update" };
|
|
4376
4462
|
}),
|
|
4377
|
-
reconcile:
|
|
4463
|
+
reconcile: Effect60.fn(function* ({ news }) {
|
|
4378
4464
|
yield* guardDatastoreForms(news);
|
|
4379
4465
|
const live = yield* readOne(news);
|
|
4380
4466
|
if (live === undefined) {
|
|
@@ -4389,15 +4475,15 @@ var handlers8 = {
|
|
|
4389
4475
|
}
|
|
4390
4476
|
const after = yield* settle2(readOne(news), (row) => row !== undefined && matches6(row, news));
|
|
4391
4477
|
if (after === undefined || !matches6(after, news)) {
|
|
4392
|
-
return yield*
|
|
4478
|
+
return yield* Effect60.die(new Error(`${object2(news)}: the write returned no error but the datastore still does not ` + "match the declaration after 120s. Check the PBS task list " + "-- read it with `proxmox-backup-manager task log <upid>`. A slow disk is a " + "plausible cause (see `settle`); a create over an existing chunk store is not, " + "because PBS refuses that outright rather than timing out."));
|
|
4393
4479
|
}
|
|
4394
4480
|
return after;
|
|
4395
4481
|
}),
|
|
4396
|
-
delete:
|
|
4482
|
+
delete: Effect60.fn(function* ({ olds }) {
|
|
4397
4483
|
yield* deleteOne(olds);
|
|
4398
4484
|
const left = yield* settle2(readOne(olds), (row) => row === undefined);
|
|
4399
4485
|
if (left !== undefined) {
|
|
4400
|
-
return yield*
|
|
4486
|
+
return yield* Effect60.die(new Error(`${object2(olds)}: the DELETE returned no error but the datastore is still in ` + "datastore.cfg after 120s. Either a backup, verify or GC task still holds it, " + "or the provision role lacks Datastore.Allocate on /datastore/" + `${olds.name}. Read the task log with \`proxmox-backup-manager task log <upid>\`.`));
|
|
4401
4487
|
}
|
|
4402
4488
|
})
|
|
4403
4489
|
};
|
|
@@ -4406,36 +4492,36 @@ var handlers8 = {
|
|
|
4406
4492
|
var PbsDatastore = Resource19("Pbs.Datastore", {
|
|
4407
4493
|
defaultRemovalPolicy: "retain"
|
|
4408
4494
|
});
|
|
4409
|
-
var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore,
|
|
4495
|
+
var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore, Effect61.succeed(PbsDatastore.Provider.of(handlers7)));
|
|
4410
4496
|
// src/proxmox/pbs-notification-matcher.ts
|
|
4411
4497
|
import { Resource as Resource20 } from "alchemy";
|
|
4412
4498
|
import * as Provider20 from "alchemy/Provider";
|
|
4413
4499
|
import { isResolved as isResolved18 } from "alchemy/Diff";
|
|
4414
|
-
import * as
|
|
4500
|
+
import * as Effect64 from "effect/Effect";
|
|
4415
4501
|
|
|
4416
4502
|
// src/proxmox/pbs-notification-matcher-distilled.ts
|
|
4417
4503
|
import * as config2 from "@distilled.cloud/proxmox-backup/config";
|
|
4418
|
-
import * as
|
|
4504
|
+
import * as Effect63 from "effect/Effect";
|
|
4419
4505
|
|
|
4420
4506
|
// src/proxmox/pbs-notification-input.ts
|
|
4421
4507
|
import * as Data3 from "effect/Data";
|
|
4422
|
-
import * as
|
|
4508
|
+
import * as Effect62 from "effect/Effect";
|
|
4423
4509
|
import * as Schema4 from "effect/Schema";
|
|
4424
4510
|
|
|
4425
4511
|
class PbsNotificationInputRefusal extends Data3.TaggedError("PbsNotificationInputRefusal") {
|
|
4426
4512
|
}
|
|
4427
|
-
var notificationInput = (schema, fields2) => Schema4.decodeUnknownEffect(Schema4.toType(schema))(fields2).pipe(
|
|
4513
|
+
var notificationInput = (schema, fields2) => Schema4.decodeUnknownEffect(Schema4.toType(schema))(fields2).pipe(Effect62.mapError(() => new PbsNotificationInputRefusal({
|
|
4428
4514
|
message: "PBS notification fields do not match the SDK request schema; no PBS request was sent."
|
|
4429
4515
|
})));
|
|
4430
4516
|
|
|
4431
4517
|
// src/proxmox/pbs-notification-matcher-distilled.ts
|
|
4432
|
-
var readMatcher2 = (props) => runPbs(props.target, "read", config2.getConfigNotificationMatcher({ name: props.name })).pipe(
|
|
4518
|
+
var readMatcher2 = (props) => runPbs(props.target, "read", config2.getConfigNotificationMatcher({ name: props.name })).pipe(Effect63.map((row) => matcherAttributes({
|
|
4433
4519
|
...row,
|
|
4434
4520
|
"invert-match": row.invert_match,
|
|
4435
4521
|
"match-calendar": row.match_calendar,
|
|
4436
4522
|
"match-field": row.match_field,
|
|
4437
4523
|
"match-severity": row.match_severity
|
|
4438
|
-
}, props.name)),
|
|
4524
|
+
}, props.name)), Effect63.catchTag("NotFound", () => Effect63.succeed(undefined)));
|
|
4439
4525
|
var requestFields = (props, form) => ({
|
|
4440
4526
|
...form,
|
|
4441
4527
|
invert_match: form["invert-match"],
|
|
@@ -4444,65 +4530,65 @@ var requestFields = (props, form) => ({
|
|
|
4444
4530
|
match_severity: form["match-severity"],
|
|
4445
4531
|
name: props.name
|
|
4446
4532
|
});
|
|
4447
|
-
var createMatcher2 = (props, form) => runPbs(props.target, "provision", notificationInput(config2.CreateConfigNotificationMatcherRequest, requestFields(props, form)).pipe(
|
|
4448
|
-
var updateMatcher2 = (props, form) => runPbs(props.target, "provision", notificationInput(config2.PutConfigNotificationMatcherRequest, requestFields(props, form)).pipe(
|
|
4449
|
-
var deleteMatcher2 = (props) => runPbs(props.target, "provision", config2.deleteConfigNotificationMatcher({ name: props.name })).pipe(
|
|
4533
|
+
var createMatcher2 = (props, form) => runPbs(props.target, "provision", notificationInput(config2.CreateConfigNotificationMatcherRequest, requestFields(props, form)).pipe(Effect63.flatMap(config2.createConfigNotificationMatcher)));
|
|
4534
|
+
var updateMatcher2 = (props, form) => runPbs(props.target, "provision", notificationInput(config2.PutConfigNotificationMatcherRequest, requestFields(props, form)).pipe(Effect63.flatMap(config2.putConfigNotificationMatcher)));
|
|
4535
|
+
var deleteMatcher2 = (props) => runPbs(props.target, "provision", config2.deleteConfigNotificationMatcher({ name: props.name })).pipe(Effect63.catchTag("NotFound", () => Effect63.void));
|
|
4450
4536
|
|
|
4451
4537
|
// src/proxmox/pbs-notification-matcher.ts
|
|
4452
4538
|
var PbsNotificationMatcher = Resource20("Pbs.NotificationMatcher");
|
|
4453
|
-
var
|
|
4454
|
-
var
|
|
4455
|
-
var
|
|
4456
|
-
list: () =>
|
|
4539
|
+
var guardCreate5 = (props, requirePresence) => guardForm("pbs:POST /config/notifications/matchers", matcherCreateForm(props), requirePresence);
|
|
4540
|
+
var guardUpdate5 = (props) => guardForm("pbs:PUT /config/notifications/matchers/{name}", matcherUpdateForm(props), false);
|
|
4541
|
+
var handlers8 = {
|
|
4542
|
+
list: () => Effect64.succeed([]),
|
|
4457
4543
|
read: ({ olds }) => readMatcher2(olds),
|
|
4458
4544
|
diff: ({
|
|
4459
4545
|
news,
|
|
4460
4546
|
olds,
|
|
4461
4547
|
output
|
|
4462
|
-
}) =>
|
|
4548
|
+
}) => Effect64.gen(function* () {
|
|
4463
4549
|
if (!isResolved18(news))
|
|
4464
4550
|
return;
|
|
4465
|
-
yield*
|
|
4466
|
-
yield*
|
|
4551
|
+
yield* guardCreate5(news, output === undefined);
|
|
4552
|
+
yield* guardUpdate5(news);
|
|
4467
4553
|
if (output === undefined)
|
|
4468
4554
|
return;
|
|
4469
4555
|
if (olds.name !== news.name)
|
|
4470
4556
|
return { action: "replace" };
|
|
4471
4557
|
const live = yield* readMatcher2(news);
|
|
4472
4558
|
if (live === undefined) {
|
|
4473
|
-
yield*
|
|
4559
|
+
yield* guardCreate5(news, true);
|
|
4474
4560
|
return { action: "update" };
|
|
4475
4561
|
}
|
|
4476
4562
|
return { action: matcherMatches(live, news) ? "noop" : "update" };
|
|
4477
4563
|
}),
|
|
4478
|
-
reconcile: ({ news }) =>
|
|
4564
|
+
reconcile: ({ news }) => Effect64.gen(function* () {
|
|
4479
4565
|
const live = yield* readMatcher2(news);
|
|
4480
|
-
yield*
|
|
4481
|
-
yield*
|
|
4566
|
+
yield* guardCreate5(news, live === undefined);
|
|
4567
|
+
yield* guardUpdate5(news);
|
|
4482
4568
|
if (live === undefined)
|
|
4483
4569
|
yield* createMatcher2(news, matcherCreateForm(news));
|
|
4484
4570
|
else if (!matcherMatches(live, news))
|
|
4485
4571
|
yield* updateMatcher2(news, matcherUpdateForm(news));
|
|
4486
4572
|
const after = yield* readMatcher2(news);
|
|
4487
4573
|
if (after === undefined)
|
|
4488
|
-
return yield*
|
|
4574
|
+
return yield* Effect64.fail(new Error(`config/notifications/matchers/${news.name}: the write returned no error but the matcher is still absent.`));
|
|
4489
4575
|
return after;
|
|
4490
4576
|
}),
|
|
4491
4577
|
delete: ({ olds }) => deleteMatcher2(olds)
|
|
4492
4578
|
};
|
|
4493
|
-
var PbsNotificationMatcherProvider = () => Provider20.effect(PbsNotificationMatcher,
|
|
4579
|
+
var PbsNotificationMatcherProvider = () => Provider20.effect(PbsNotificationMatcher, Effect64.succeed(PbsNotificationMatcher.Provider.of(handlers8)));
|
|
4494
4580
|
// src/proxmox/pbs-notification-target.ts
|
|
4495
4581
|
import { Resource as Resource21 } from "alchemy";
|
|
4496
4582
|
import * as Provider21 from "alchemy/Provider";
|
|
4497
|
-
import * as
|
|
4583
|
+
import * as Effect67 from "effect/Effect";
|
|
4498
4584
|
|
|
4499
4585
|
// src/proxmox/pbs-notification-target-lifecycle.ts
|
|
4500
4586
|
import { isResolved as isResolved19 } from "alchemy/Diff";
|
|
4501
|
-
import * as
|
|
4587
|
+
import * as Effect66 from "effect/Effect";
|
|
4502
4588
|
|
|
4503
4589
|
// src/proxmox/pbs-notification-target-distilled.ts
|
|
4504
4590
|
import * as config3 from "@distilled.cloud/proxmox-backup/config";
|
|
4505
|
-
import * as
|
|
4591
|
+
import * as Effect65 from "effect/Effect";
|
|
4506
4592
|
|
|
4507
4593
|
// src/proxmox/pbs-notification-target-wire.ts
|
|
4508
4594
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
@@ -4603,22 +4689,22 @@ var requestFields2 = (props, form) => ({
|
|
|
4603
4689
|
var read4 = (props) => {
|
|
4604
4690
|
switch (props.type) {
|
|
4605
4691
|
case "sendmail":
|
|
4606
|
-
return config3.getConfigNotificationEndpointSendmail({ name: props.name }).pipe(
|
|
4692
|
+
return config3.getConfigNotificationEndpointSendmail({ name: props.name }).pipe(Effect65.map((row) => ({
|
|
4607
4693
|
...row,
|
|
4608
4694
|
"from-address": row.from_address,
|
|
4609
4695
|
"mailto-user": row.mailto_user
|
|
4610
4696
|
})));
|
|
4611
4697
|
case "smtp":
|
|
4612
|
-
return config3.getConfigNotificationEndpointSmtp({ name: props.name }).pipe(
|
|
4698
|
+
return config3.getConfigNotificationEndpointSmtp({ name: props.name }).pipe(Effect65.map((row) => ({
|
|
4613
4699
|
...row,
|
|
4614
4700
|
"from-address": row.from_address,
|
|
4615
4701
|
"mailto-user": row.mailto_user
|
|
4616
4702
|
})));
|
|
4617
4703
|
case "webhook":
|
|
4618
|
-
return config3.getConfigNotificationEndpointWebhook({ name: props.name }).pipe(
|
|
4704
|
+
return config3.getConfigNotificationEndpointWebhook({ name: props.name }).pipe(Effect65.map((row) => ({ ...row })));
|
|
4619
4705
|
}
|
|
4620
4706
|
};
|
|
4621
|
-
var readTarget2 = (props) => runPbs(props.target, "read", read4(props)).pipe(
|
|
4707
|
+
var readTarget2 = (props) => runPbs(props.target, "read", read4(props)).pipe(Effect65.map((row) => targetAttributes(row, props)), Effect65.catchTag("NotFound", () => Effect65.succeed(undefined)));
|
|
4622
4708
|
var validateTargetWrite2 = (props, form, mode) => {
|
|
4623
4709
|
const schema = mode === "create" ? {
|
|
4624
4710
|
sendmail: config3.CreateConfigNotificationEndpointSendmailRequest,
|
|
@@ -4629,22 +4715,22 @@ var validateTargetWrite2 = (props, form, mode) => {
|
|
|
4629
4715
|
smtp: config3.PutConfigNotificationEndpointSmtpRequest,
|
|
4630
4716
|
webhook: config3.PutConfigNotificationEndpointWebhookRequest
|
|
4631
4717
|
}[props.type];
|
|
4632
|
-
return notificationInput(schema, requestFields2(props, form)).pipe(
|
|
4718
|
+
return notificationInput(schema, requestFields2(props, form)).pipe(Effect65.asVoid);
|
|
4633
4719
|
};
|
|
4634
4720
|
var createTarget2 = (props, form) => {
|
|
4635
4721
|
const fields2 = requestFields2(props, form);
|
|
4636
|
-
const operation = props.type === "sendmail" ? notificationInput(config3.CreateConfigNotificationEndpointSendmailRequest, fields2).pipe(
|
|
4722
|
+
const operation = props.type === "sendmail" ? notificationInput(config3.CreateConfigNotificationEndpointSendmailRequest, fields2).pipe(Effect65.flatMap(config3.createConfigNotificationEndpointSendmail)) : props.type === "smtp" ? notificationInput(config3.CreateConfigNotificationEndpointSmtpRequest, fields2).pipe(Effect65.flatMap(config3.createConfigNotificationEndpointSmtp)) : notificationInput(config3.CreateConfigNotificationEndpointWebhookRequest, fields2).pipe(Effect65.flatMap(config3.createConfigNotificationEndpointWebhook));
|
|
4637
4723
|
return runPbs(props.target, "provision", operation);
|
|
4638
4724
|
};
|
|
4639
4725
|
var updateTarget2 = (props, form) => {
|
|
4640
4726
|
const fields2 = requestFields2(props, form);
|
|
4641
|
-
const operation = props.type === "sendmail" ? notificationInput(config3.PutConfigNotificationEndpointSendmailRequest, fields2).pipe(
|
|
4727
|
+
const operation = props.type === "sendmail" ? notificationInput(config3.PutConfigNotificationEndpointSendmailRequest, fields2).pipe(Effect65.flatMap(config3.putConfigNotificationEndpointSendmail)) : props.type === "smtp" ? notificationInput(config3.PutConfigNotificationEndpointSmtpRequest, fields2).pipe(Effect65.flatMap(config3.putConfigNotificationEndpointSmtp)) : notificationInput(config3.PutConfigNotificationEndpointWebhookRequest, fields2).pipe(Effect65.flatMap(config3.putConfigNotificationEndpointWebhook));
|
|
4642
4728
|
return runPbs(props.target, "provision", operation);
|
|
4643
4729
|
};
|
|
4644
4730
|
var deleteTarget2 = (props) => {
|
|
4645
4731
|
const request = { name: props.name };
|
|
4646
4732
|
const operation = props.type === "sendmail" ? config3.deleteConfigNotificationEndpointSendmail(request) : props.type === "smtp" ? config3.deleteConfigNotificationEndpointSmtp(request) : config3.deleteConfigNotificationEndpointWebhook(request);
|
|
4647
|
-
return runPbs(props.target, "provision", operation).pipe(
|
|
4733
|
+
return runPbs(props.target, "provision", operation).pipe(Effect65.catchTag("NotFound", () => Effect65.void));
|
|
4648
4734
|
};
|
|
4649
4735
|
|
|
4650
4736
|
// src/proxmox/pbs-notification-target-endpoint.ts
|
|
@@ -4835,7 +4921,7 @@ var targetForm = (props, groups, carry, mode) => {
|
|
|
4835
4921
|
var path = (props) => `config/notifications/endpoints/${props.type}/${props.name}`;
|
|
4836
4922
|
var refuse2 = (props) => {
|
|
4837
4923
|
const reasons = refusals(props);
|
|
4838
|
-
return reasons.length === 0 ?
|
|
4924
|
+
return reasons.length === 0 ? Effect66.void : Effect66.die(new Error(`${path(props)}: ${reasons.join("; ")}.`));
|
|
4839
4925
|
};
|
|
4840
4926
|
var toCarry = (live, props, groups, sealed) => {
|
|
4841
4927
|
const header = headerState(live, props, groups) === "stale";
|
|
@@ -4847,16 +4933,16 @@ var requireValues = (props, groups, carry) => {
|
|
|
4847
4933
|
...carry.header ? groups.header.missing : [],
|
|
4848
4934
|
...carry.sealed ? groups.sealed.missing : []
|
|
4849
4935
|
];
|
|
4850
|
-
return missing.length === 0 ?
|
|
4936
|
+
return missing.length === 0 ? Effect66.void : Effect66.die(new Error(`${path(props)}: this write must send write-only values, and ${missing.join(", ")} ` + "is unset or empty in the deploying environment. Export it and deploy again."));
|
|
4851
4937
|
};
|
|
4852
|
-
var
|
|
4853
|
-
list: () =>
|
|
4854
|
-
read: ({ olds, output }) => (output === undefined ? refuse2(olds) :
|
|
4938
|
+
var handlers9 = {
|
|
4939
|
+
list: () => Effect66.succeed([]),
|
|
4940
|
+
read: ({ olds, output }) => (output === undefined ? refuse2(olds) : Effect66.void).pipe(Effect66.andThen(readTarget2(olds)), Effect66.map((live) => live === undefined ? undefined : { ...live, sealed: output?.sealed ?? "" })),
|
|
4855
4941
|
diff: ({
|
|
4856
4942
|
news,
|
|
4857
4943
|
olds,
|
|
4858
4944
|
output
|
|
4859
|
-
}) =>
|
|
4945
|
+
}) => Effect66.gen(function* () {
|
|
4860
4946
|
if (output === undefined || !isResolved19(news))
|
|
4861
4947
|
return;
|
|
4862
4948
|
yield* refuse2(news);
|
|
@@ -4878,7 +4964,7 @@ var handlers10 = {
|
|
|
4878
4964
|
return { action: "update" };
|
|
4879
4965
|
return toCarry(live, news, resolveGroups(news), output.sealed) === undefined ? { action: "noop" } : { action: "update" };
|
|
4880
4966
|
}),
|
|
4881
|
-
reconcile: ({ news, output }) =>
|
|
4967
|
+
reconcile: ({ news, output }) => Effect66.gen(function* () {
|
|
4882
4968
|
yield* refuse2(news);
|
|
4883
4969
|
const groups = resolveGroups(news);
|
|
4884
4970
|
const live = yield* readTarget2(news);
|
|
@@ -4903,7 +4989,7 @@ var handlers10 = {
|
|
|
4903
4989
|
}
|
|
4904
4990
|
const after = yield* readTarget2(news);
|
|
4905
4991
|
if (after === undefined) {
|
|
4906
|
-
return yield*
|
|
4992
|
+
return yield* Effect66.die(new Error(`${path(news)}: the write returned no error but the target is still absent.`));
|
|
4907
4993
|
}
|
|
4908
4994
|
return { ...after, sealed };
|
|
4909
4995
|
}),
|
|
@@ -4912,15 +4998,15 @@ var handlers10 = {
|
|
|
4912
4998
|
|
|
4913
4999
|
// src/proxmox/pbs-notification-target.ts
|
|
4914
5000
|
var PbsNotificationTarget = Resource21("Pbs.NotificationTarget");
|
|
4915
|
-
var PbsNotificationTargetProvider = () => Provider21.effect(PbsNotificationTarget,
|
|
5001
|
+
var PbsNotificationTargetProvider = () => Provider21.effect(PbsNotificationTarget, Effect67.succeed(PbsNotificationTarget.Provider.of(handlers9)));
|
|
4916
5002
|
// src/proxmox/pbs-prune-job.ts
|
|
4917
5003
|
import { Resource as Resource22 } from "alchemy";
|
|
4918
5004
|
import * as Provider22 from "alchemy/Provider";
|
|
4919
|
-
import * as
|
|
5005
|
+
import * as Effect70 from "effect/Effect";
|
|
4920
5006
|
|
|
4921
5007
|
// src/proxmox/pbs-prune-job-lifecycle.ts
|
|
4922
5008
|
import { isResolved as isResolved20 } from "alchemy/Diff";
|
|
4923
|
-
import * as
|
|
5009
|
+
import * as Effect69 from "effect/Effect";
|
|
4924
5010
|
|
|
4925
5011
|
// src/proxmox/pbs-prune-job-form.ts
|
|
4926
5012
|
var KEEP_KEYS = [
|
|
@@ -4998,7 +5084,7 @@ var spec = {
|
|
|
4998
5084
|
|
|
4999
5085
|
// src/proxmox/pbs-prune-job-wire.ts
|
|
5000
5086
|
import * as config4 from "@distilled.cloud/proxmox-backup/config";
|
|
5001
|
-
import * as
|
|
5087
|
+
import * as Effect68 from "effect/Effect";
|
|
5002
5088
|
var members2 = (form) => ({
|
|
5003
5089
|
...form["comment"] === undefined ? {} : { comment: form["comment"] },
|
|
5004
5090
|
...form["disable"] === undefined ? {} : { disable: form["disable"] },
|
|
@@ -5033,48 +5119,48 @@ var wire2 = (row) => ({
|
|
|
5033
5119
|
"keep-yearly": row.keep_yearly,
|
|
5034
5120
|
"max-depth": row.max_depth
|
|
5035
5121
|
});
|
|
5036
|
-
var readOne2 = (props) => runPbs(props.target, "read", config4.getConfigPrune({ id: props.id })).pipe(
|
|
5122
|
+
var readOne2 = (props) => runPbs(props.target, "read", config4.getConfigPrune({ id: props.id })).pipe(Effect68.map((row) => spec.attributes(wire2(row), props)), Effect68.catchTag("PruneJobNotFound", () => Effect68.succeed(undefined)));
|
|
5037
5123
|
var createOne2 = (props) => runPbs(props.target, "provision", config4.createConfigPrune(createRequest2(props)));
|
|
5038
5124
|
var updateOne2 = (props) => runPbs(props.target, "provision", config4.putConfigPrune(updateRequest2(props)));
|
|
5039
|
-
var deleteOne2 = (props) =>
|
|
5125
|
+
var deleteOne2 = (props) => Effect68.gen(function* () {
|
|
5040
5126
|
if ((yield* readOne2(props)) === undefined)
|
|
5041
5127
|
return;
|
|
5042
5128
|
return yield* runPbs(props.target, "provision", config4.deleteConfigPrune({ id: props.id }));
|
|
5043
5129
|
});
|
|
5044
5130
|
|
|
5045
5131
|
// src/proxmox/pbs-prune-job-lifecycle.ts
|
|
5046
|
-
var { guardCreate:
|
|
5047
|
-
var
|
|
5048
|
-
list: () =>
|
|
5132
|
+
var { guardCreate: guardCreate6, guardUpdate: guardUpdate6 } = specGuards(spec);
|
|
5133
|
+
var handlers10 = {
|
|
5134
|
+
list: () => Effect69.succeed([]),
|
|
5049
5135
|
read: ({ olds }) => readOne2(olds),
|
|
5050
5136
|
diff: ({
|
|
5051
5137
|
news,
|
|
5052
5138
|
output
|
|
5053
|
-
}) =>
|
|
5139
|
+
}) => Effect69.gen(function* () {
|
|
5054
5140
|
if (!isResolved20(news))
|
|
5055
5141
|
return;
|
|
5056
|
-
yield*
|
|
5057
|
-
yield*
|
|
5142
|
+
yield* guardCreate6(news, output === undefined);
|
|
5143
|
+
yield* guardUpdate6(news);
|
|
5058
5144
|
if (output === undefined)
|
|
5059
5145
|
return;
|
|
5060
5146
|
const live = yield* readOne2(news);
|
|
5061
5147
|
if (live === undefined) {
|
|
5062
|
-
yield*
|
|
5148
|
+
yield* guardCreate6(news, true);
|
|
5063
5149
|
return { action: "update" };
|
|
5064
5150
|
}
|
|
5065
5151
|
return { action: spec.matches(live, news) ? "noop" : "update" };
|
|
5066
5152
|
}),
|
|
5067
|
-
reconcile: ({ news }) =>
|
|
5153
|
+
reconcile: ({ news }) => Effect69.gen(function* () {
|
|
5068
5154
|
const live = yield* readOne2(news);
|
|
5069
|
-
yield*
|
|
5070
|
-
yield*
|
|
5155
|
+
yield* guardCreate6(news, live === undefined);
|
|
5156
|
+
yield* guardUpdate6(news);
|
|
5071
5157
|
if (live === undefined)
|
|
5072
5158
|
yield* createOne2(news);
|
|
5073
5159
|
else if (!spec.matches(live, news))
|
|
5074
5160
|
yield* updateOne2(news);
|
|
5075
5161
|
const after = yield* readOne2(news);
|
|
5076
5162
|
if (after === undefined) {
|
|
5077
|
-
return yield*
|
|
5163
|
+
return yield* Effect69.fail(new Error(`${spec.path(news)}: write succeeded but job is absent.`));
|
|
5078
5164
|
}
|
|
5079
5165
|
return after;
|
|
5080
5166
|
}),
|
|
@@ -5083,15 +5169,15 @@ var handlers11 = {
|
|
|
5083
5169
|
|
|
5084
5170
|
// src/proxmox/pbs-prune-job.ts
|
|
5085
5171
|
var PbsPruneJob = Resource22("Pbs.PruneJob");
|
|
5086
|
-
var PbsPruneJobProvider = () => Provider22.effect(PbsPruneJob,
|
|
5172
|
+
var PbsPruneJobProvider = () => Provider22.effect(PbsPruneJob, Effect70.succeed(PbsPruneJob.Provider.of(handlers10)));
|
|
5087
5173
|
// src/proxmox/pbs-sync-job.ts
|
|
5088
5174
|
import { Resource as Resource23 } from "alchemy";
|
|
5089
5175
|
import * as Provider23 from "alchemy/Provider";
|
|
5090
|
-
import * as
|
|
5176
|
+
import * as Effect73 from "effect/Effect";
|
|
5091
5177
|
|
|
5092
5178
|
// src/proxmox/pbs-sync-job-lifecycle.ts
|
|
5093
5179
|
import { isResolved as isResolved21 } from "alchemy/Diff";
|
|
5094
|
-
import * as
|
|
5180
|
+
import * as Effect72 from "effect/Effect";
|
|
5095
5181
|
|
|
5096
5182
|
// src/proxmox/pbs-sync-job-form.ts
|
|
5097
5183
|
var UNSET4 = -1;
|
|
@@ -5168,7 +5254,7 @@ var spec2 = {
|
|
|
5168
5254
|
|
|
5169
5255
|
// src/proxmox/pbs-sync-job-wire.ts
|
|
5170
5256
|
import * as config5 from "@distilled.cloud/proxmox-backup/config";
|
|
5171
|
-
import * as
|
|
5257
|
+
import * as Effect71 from "effect/Effect";
|
|
5172
5258
|
var members3 = (form) => ({
|
|
5173
5259
|
...form["remote-store"] === undefined ? {} : { remote_store: form["remote-store"] },
|
|
5174
5260
|
...form["remove-vanished"] === undefined ? {} : { remove_vanished: form["remove-vanished"] },
|
|
@@ -5207,45 +5293,45 @@ var wire3 = (row) => ({
|
|
|
5207
5293
|
"sync-direction": row.sync_direction,
|
|
5208
5294
|
"group-filter": row.group_filter
|
|
5209
5295
|
});
|
|
5210
|
-
var readOne3 = (props) => runPbs(props.target, "read", config5.getConfigSync({ id: props.id })).pipe(
|
|
5296
|
+
var readOne3 = (props) => runPbs(props.target, "read", config5.getConfigSync({ id: props.id })).pipe(Effect71.map((row) => spec2.attributes(wire3(row), props)), Effect71.catchTag("SyncJobNotFound", () => Effect71.succeed(undefined)));
|
|
5211
5297
|
var createOne3 = (props) => runPbs(props.target, "provision", config5.syncConfig(createRequest3(props)));
|
|
5212
5298
|
var updateOne3 = (props) => runPbs(props.target, "provision", config5.putConfigSync(updateRequest3(props)));
|
|
5213
|
-
var deleteOne3 = (props) =>
|
|
5299
|
+
var deleteOne3 = (props) => Effect71.gen(function* () {
|
|
5214
5300
|
if ((yield* readOne3(props)) === undefined)
|
|
5215
5301
|
return;
|
|
5216
5302
|
return yield* runPbs(props.target, "provision", config5.deleteConfigSync({ id: props.id }));
|
|
5217
5303
|
});
|
|
5218
5304
|
|
|
5219
5305
|
// src/proxmox/pbs-sync-job-lifecycle.ts
|
|
5220
|
-
var { guardCreate:
|
|
5221
|
-
var
|
|
5222
|
-
list: () =>
|
|
5306
|
+
var { guardCreate: guardCreate7, guardUpdate: guardUpdate7 } = specGuards(spec2);
|
|
5307
|
+
var handlers11 = {
|
|
5308
|
+
list: () => Effect72.succeed([]),
|
|
5223
5309
|
read: ({ olds }) => readOne3(olds),
|
|
5224
|
-
diff: ({ news, output }) =>
|
|
5310
|
+
diff: ({ news, output }) => Effect72.gen(function* () {
|
|
5225
5311
|
if (!isResolved21(news))
|
|
5226
5312
|
return;
|
|
5227
|
-
yield*
|
|
5228
|
-
yield*
|
|
5313
|
+
yield* guardCreate7(news, output === undefined);
|
|
5314
|
+
yield* guardUpdate7(news);
|
|
5229
5315
|
if (output === undefined)
|
|
5230
5316
|
return;
|
|
5231
5317
|
const live = yield* readOne3(news);
|
|
5232
5318
|
if (live === undefined) {
|
|
5233
|
-
yield*
|
|
5319
|
+
yield* guardCreate7(news, true);
|
|
5234
5320
|
return { action: "update" };
|
|
5235
5321
|
}
|
|
5236
5322
|
return { action: spec2.matches(live, news) ? "noop" : "update" };
|
|
5237
5323
|
}),
|
|
5238
|
-
reconcile: ({ news }) =>
|
|
5324
|
+
reconcile: ({ news }) => Effect72.gen(function* () {
|
|
5239
5325
|
const live = yield* readOne3(news);
|
|
5240
|
-
yield*
|
|
5241
|
-
yield*
|
|
5326
|
+
yield* guardCreate7(news, live === undefined);
|
|
5327
|
+
yield* guardUpdate7(news);
|
|
5242
5328
|
if (live === undefined)
|
|
5243
5329
|
yield* createOne3(news);
|
|
5244
5330
|
else if (!spec2.matches(live, news))
|
|
5245
5331
|
yield* updateOne3(news);
|
|
5246
5332
|
const after = yield* readOne3(news);
|
|
5247
5333
|
if (after === undefined) {
|
|
5248
|
-
return yield*
|
|
5334
|
+
return yield* Effect72.fail(new Error(`${spec2.path(news)}: write succeeded but job is absent.`));
|
|
5249
5335
|
}
|
|
5250
5336
|
return after;
|
|
5251
5337
|
}),
|
|
@@ -5254,15 +5340,15 @@ var handlers12 = {
|
|
|
5254
5340
|
|
|
5255
5341
|
// src/proxmox/pbs-sync-job.ts
|
|
5256
5342
|
var PbsSyncJob = Resource23("Pbs.SyncJob");
|
|
5257
|
-
var PbsSyncJobProvider = () => Provider23.effect(PbsSyncJob,
|
|
5343
|
+
var PbsSyncJobProvider = () => Provider23.effect(PbsSyncJob, Effect73.succeed(PbsSyncJob.Provider.of(handlers11)));
|
|
5258
5344
|
// src/proxmox/pbs-verify-job.ts
|
|
5259
5345
|
import { Resource as Resource24 } from "alchemy";
|
|
5260
5346
|
import * as Provider24 from "alchemy/Provider";
|
|
5261
|
-
import * as
|
|
5347
|
+
import * as Effect76 from "effect/Effect";
|
|
5262
5348
|
|
|
5263
5349
|
// src/proxmox/pbs-verify-job-lifecycle.ts
|
|
5264
5350
|
import { isResolved as isResolved22 } from "alchemy/Diff";
|
|
5265
|
-
import * as
|
|
5351
|
+
import * as Effect75 from "effect/Effect";
|
|
5266
5352
|
|
|
5267
5353
|
// src/proxmox/pbs-verify-job-form.ts
|
|
5268
5354
|
var rechecks = (ignoreVerified, outdatedAfter) => {
|
|
@@ -5309,7 +5395,7 @@ var spec3 = {
|
|
|
5309
5395
|
|
|
5310
5396
|
// src/proxmox/pbs-verify-job-wire.ts
|
|
5311
5397
|
import * as config6 from "@distilled.cloud/proxmox-backup/config";
|
|
5312
|
-
import * as
|
|
5398
|
+
import * as Effect74 from "effect/Effect";
|
|
5313
5399
|
var members4 = (form) => ({
|
|
5314
5400
|
...form["ignore-verified"] === undefined ? {} : { ignore_verified: form["ignore-verified"] },
|
|
5315
5401
|
...form["store"] === undefined ? {} : { store: form["store"] },
|
|
@@ -5334,48 +5420,48 @@ var wire4 = (row) => ({
|
|
|
5334
5420
|
"max-depth": row.max_depth,
|
|
5335
5421
|
"outdated-after": row.outdated_after
|
|
5336
5422
|
});
|
|
5337
|
-
var readOne4 = (props) => runPbs(props.target, "read", config6.getConfigVerify({ id: props.id })).pipe(
|
|
5423
|
+
var readOne4 = (props) => runPbs(props.target, "read", config6.getConfigVerify({ id: props.id })).pipe(Effect74.map((row) => spec3.attributes(wire4(row), props)), Effect74.catchTag("VerifyJobNotFound", () => Effect74.succeed(undefined)));
|
|
5338
5424
|
var createOne4 = (props) => runPbs(props.target, "provision", config6.verifyConfig(createRequest4(props)));
|
|
5339
5425
|
var updateOne4 = (props) => runPbs(props.target, "provision", config6.putConfigVerify(updateRequest4(props)));
|
|
5340
|
-
var deleteOne4 = (props) =>
|
|
5426
|
+
var deleteOne4 = (props) => Effect74.gen(function* () {
|
|
5341
5427
|
if ((yield* readOne4(props)) === undefined)
|
|
5342
5428
|
return;
|
|
5343
5429
|
return yield* runPbs(props.target, "provision", config6.deleteConfigVerify({ id: props.id }));
|
|
5344
5430
|
});
|
|
5345
5431
|
|
|
5346
5432
|
// src/proxmox/pbs-verify-job-lifecycle.ts
|
|
5347
|
-
var { guardCreate:
|
|
5348
|
-
var
|
|
5349
|
-
list: () =>
|
|
5433
|
+
var { guardCreate: guardCreate8, guardUpdate: guardUpdate8 } = specGuards(spec3);
|
|
5434
|
+
var handlers12 = {
|
|
5435
|
+
list: () => Effect75.succeed([]),
|
|
5350
5436
|
read: ({ olds }) => readOne4(olds),
|
|
5351
5437
|
diff: ({
|
|
5352
5438
|
news,
|
|
5353
5439
|
output
|
|
5354
|
-
}) =>
|
|
5440
|
+
}) => Effect75.gen(function* () {
|
|
5355
5441
|
if (!isResolved22(news))
|
|
5356
5442
|
return;
|
|
5357
|
-
yield*
|
|
5358
|
-
yield*
|
|
5443
|
+
yield* guardCreate8(news, output === undefined);
|
|
5444
|
+
yield* guardUpdate8(news);
|
|
5359
5445
|
if (output === undefined)
|
|
5360
5446
|
return;
|
|
5361
5447
|
const live = yield* readOne4(news);
|
|
5362
5448
|
if (live === undefined) {
|
|
5363
|
-
yield*
|
|
5449
|
+
yield* guardCreate8(news, true);
|
|
5364
5450
|
return { action: "update" };
|
|
5365
5451
|
}
|
|
5366
5452
|
return { action: spec3.matches(live, news) ? "noop" : "update" };
|
|
5367
5453
|
}),
|
|
5368
|
-
reconcile: ({ news }) =>
|
|
5454
|
+
reconcile: ({ news }) => Effect75.gen(function* () {
|
|
5369
5455
|
const live = yield* readOne4(news);
|
|
5370
|
-
yield*
|
|
5371
|
-
yield*
|
|
5456
|
+
yield* guardCreate8(news, live === undefined);
|
|
5457
|
+
yield* guardUpdate8(news);
|
|
5372
5458
|
if (live === undefined)
|
|
5373
5459
|
yield* createOne4(news);
|
|
5374
5460
|
else if (!spec3.matches(live, news))
|
|
5375
5461
|
yield* updateOne4(news);
|
|
5376
5462
|
const after = yield* readOne4(news);
|
|
5377
5463
|
if (after === undefined) {
|
|
5378
|
-
return yield*
|
|
5464
|
+
return yield* Effect75.fail(new Error(`${spec3.path(news)}: write succeeded but job is absent.`));
|
|
5379
5465
|
}
|
|
5380
5466
|
return after;
|
|
5381
5467
|
}),
|
|
@@ -5384,15 +5470,15 @@ var handlers13 = {
|
|
|
5384
5470
|
|
|
5385
5471
|
// src/proxmox/pbs-verify-job.ts
|
|
5386
5472
|
var PbsVerifyJob = Resource24("Pbs.VerifyJob");
|
|
5387
|
-
var PbsVerifyJobProvider = () => Provider24.effect(PbsVerifyJob,
|
|
5473
|
+
var PbsVerifyJobProvider = () => Provider24.effect(PbsVerifyJob, Effect76.succeed(PbsVerifyJob.Provider.of(handlers12)));
|
|
5388
5474
|
// src/proxmox/pool.ts
|
|
5389
5475
|
import { Resource as Resource25 } from "alchemy";
|
|
5390
5476
|
import { isResolved as isResolved23 } from "alchemy/Diff";
|
|
5391
5477
|
import * as Provider25 from "alchemy/Provider";
|
|
5392
5478
|
import * as pools from "@distilled.cloud/proxmox/pools";
|
|
5393
|
-
import { ProxmoxParseError as
|
|
5479
|
+
import { ProxmoxParseError as ProxmoxParseError5 } from "@distilled.cloud/proxmox/Errors";
|
|
5394
5480
|
import * as Schema5 from "effect/Schema";
|
|
5395
|
-
import * as
|
|
5481
|
+
import * as Effect77 from "effect/Effect";
|
|
5396
5482
|
var ProxmoxPool = Resource25("Proxmox.Pool");
|
|
5397
5483
|
var poolCreateForm = (props) => ({
|
|
5398
5484
|
comment: props.comment ?? "",
|
|
@@ -5417,33 +5503,33 @@ var spec4 = {
|
|
|
5417
5503
|
path: (props) => `pools/${props.poolid}`,
|
|
5418
5504
|
updateForm: poolUpdateForm
|
|
5419
5505
|
};
|
|
5420
|
-
var { guardCreate:
|
|
5421
|
-
var readPool = (props) => runPve(props.target, "read", false, pools.getPool({ poolid: props.poolid })).pipe(
|
|
5506
|
+
var { guardCreate: guardCreate9, guardUpdate: guardUpdate9 } = specGuards(spec4);
|
|
5507
|
+
var readPool = (props) => runPve(props.target, "read", false, pools.getPool({ poolid: props.poolid })).pipe(Effect77.flatMap((live) => Schema5.decodeUnknownEffect(Schema5.toType(pools.GetPoolResponse))(live).pipe(Effect77.mapError(() => new ProxmoxParseError5({
|
|
5422
5508
|
body: undefined,
|
|
5423
5509
|
cause: "Pool response does not match its vendor schema"
|
|
5424
|
-
})),
|
|
5425
|
-
var deletePool2 = (props) => runPve(props.target, "provision", true, pools.deletePool({ poolid: props.poolid })).pipe(
|
|
5426
|
-
var ProxmoxPoolProvider = () => Provider25.effect(ProxmoxPool,
|
|
5427
|
-
list: () =>
|
|
5510
|
+
})), Effect77.as(live))), Effect77.map((live) => spec4.attributes({ ...live }, props)), Effect77.catchTag("PoolNotFound", () => Effect77.succeed(undefined)), Effect77.catchTag("NotFound", () => Effect77.succeed(undefined)));
|
|
5511
|
+
var deletePool2 = (props) => runPve(props.target, "provision", true, pools.deletePool({ poolid: props.poolid })).pipe(Effect77.catchTag("PoolNotFound", () => Effect77.void), Effect77.catchTag("NotFound", () => Effect77.void));
|
|
5512
|
+
var ProxmoxPoolProvider = () => Provider25.effect(ProxmoxPool, Effect77.succeed(ProxmoxPool.Provider.of({
|
|
5513
|
+
list: () => Effect77.succeed([]),
|
|
5428
5514
|
read: ({ olds }) => readPool(olds),
|
|
5429
|
-
diff:
|
|
5515
|
+
diff: Effect77.fn(function* ({ news, output }) {
|
|
5430
5516
|
if (!isResolved23(news))
|
|
5431
5517
|
return;
|
|
5432
|
-
yield*
|
|
5433
|
-
yield*
|
|
5518
|
+
yield* guardCreate9(news, output === undefined);
|
|
5519
|
+
yield* guardUpdate9(news);
|
|
5434
5520
|
if (output === undefined)
|
|
5435
5521
|
return;
|
|
5436
5522
|
const live = yield* readPool(news);
|
|
5437
5523
|
if (live === undefined) {
|
|
5438
|
-
yield*
|
|
5524
|
+
yield* guardCreate9(news, true);
|
|
5439
5525
|
return { action: "update" };
|
|
5440
5526
|
}
|
|
5441
5527
|
return { action: spec4.matches(live, news) ? "noop" : "update" };
|
|
5442
5528
|
}),
|
|
5443
|
-
reconcile:
|
|
5529
|
+
reconcile: Effect77.fn(function* ({ news }) {
|
|
5444
5530
|
const live = yield* readPool(news);
|
|
5445
|
-
yield*
|
|
5446
|
-
yield*
|
|
5531
|
+
yield* guardCreate9(news, live === undefined);
|
|
5532
|
+
yield* guardUpdate9(news);
|
|
5447
5533
|
if (live === undefined) {
|
|
5448
5534
|
yield* runPve(news.target, "provision", true, pools.createPool(poolCreateForm(news)));
|
|
5449
5535
|
} else {
|
|
@@ -5456,7 +5542,7 @@ var ProxmoxPoolProvider = () => Provider25.effect(ProxmoxPool, Effect73.succeed(
|
|
|
5456
5542
|
}
|
|
5457
5543
|
const after = yield* readPool(news);
|
|
5458
5544
|
if (after === undefined)
|
|
5459
|
-
return yield*
|
|
5545
|
+
return yield* Effect77.fail(new Error(`pools/${news.poolid}: write returned success but the pool is still absent`));
|
|
5460
5546
|
return after;
|
|
5461
5547
|
}),
|
|
5462
5548
|
delete: ({ olds }) => deletePool2(olds)
|
|
@@ -5653,14 +5739,14 @@ echo 'provision baseline: done. Next: point the OpenBao proxmox mount at these u
|
|
|
5653
5739
|
// src/proxmox/provision-declare.ts
|
|
5654
5740
|
import { adopt } from "alchemy/AdoptPolicy";
|
|
5655
5741
|
import * as RemovalPolicy from "alchemy/RemovalPolicy";
|
|
5656
|
-
import * as
|
|
5742
|
+
import * as Effect81 from "effect/Effect";
|
|
5657
5743
|
|
|
5658
5744
|
// src/proxmox/role.ts
|
|
5659
5745
|
import { Resource as Resource26 } from "alchemy";
|
|
5660
5746
|
import { isResolved as isResolved24 } from "alchemy/Diff";
|
|
5661
5747
|
import * as Provider26 from "alchemy/Provider";
|
|
5662
5748
|
import * as access5 from "@distilled.cloud/proxmox/access";
|
|
5663
|
-
import * as
|
|
5749
|
+
import * as Effect78 from "effect/Effect";
|
|
5664
5750
|
|
|
5665
5751
|
// src/proxmox/role-wire.ts
|
|
5666
5752
|
var ROLE_CREATE = "pve:POST /access/roles";
|
|
@@ -5683,19 +5769,19 @@ var matches7 = (attributes2, props) => canonical2(attributes2.privs).join(",") =
|
|
|
5683
5769
|
|
|
5684
5770
|
// src/proxmox/role.ts
|
|
5685
5771
|
var ProxmoxRole = Resource26("Proxmox.Role");
|
|
5686
|
-
var readRole = (props) => readOrUnreadable(runPve(props.target, "read", false, access5.listAccessRoles({}))).pipe(
|
|
5772
|
+
var readRole = (props) => readOrUnreadable(runPve(props.target, "read", false, access5.listAccessRoles({}))).pipe(Effect78.map((rows) => {
|
|
5687
5773
|
if (rows === UNREADABLE)
|
|
5688
5774
|
return UNREADABLE;
|
|
5689
5775
|
const row = find2(rows, props.roleid);
|
|
5690
5776
|
return row === undefined ? undefined : attributesOf7(row, props);
|
|
5691
5777
|
}));
|
|
5692
5778
|
var dropUnreadable5 = (live) => live === UNREADABLE ? undefined : live;
|
|
5693
|
-
var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole,
|
|
5694
|
-
list: () =>
|
|
5695
|
-
read:
|
|
5779
|
+
var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole, Effect78.succeed(ProxmoxRole.Provider.of({
|
|
5780
|
+
list: () => Effect78.succeed([]),
|
|
5781
|
+
read: Effect78.fn(function* ({ olds }) {
|
|
5696
5782
|
return dropUnreadable5(yield* readRole(olds));
|
|
5697
5783
|
}),
|
|
5698
|
-
diff:
|
|
5784
|
+
diff: Effect78.fn(function* ({ news, output }) {
|
|
5699
5785
|
if (!isResolved24(news))
|
|
5700
5786
|
return;
|
|
5701
5787
|
yield* guardWrite2(ROLE_CREATE, createForm7(news), output === undefined);
|
|
@@ -5713,7 +5799,7 @@ var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole, Effect74.succeed(
|
|
|
5713
5799
|
}
|
|
5714
5800
|
return matches7(live, news) ? { action: "noop" } : { action: "update" };
|
|
5715
5801
|
}),
|
|
5716
|
-
reconcile:
|
|
5802
|
+
reconcile: Effect78.fn(function* ({ news }) {
|
|
5717
5803
|
const before = dropUnreadable5(yield* readRole(news));
|
|
5718
5804
|
yield* guardWrite2(ROLE_CREATE, createForm7(news), before === undefined);
|
|
5719
5805
|
yield* guardWrite2(ROLE_UPDATE, updateForm4(news), false);
|
|
@@ -5724,11 +5810,11 @@ var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole, Effect74.succeed(
|
|
|
5724
5810
|
}
|
|
5725
5811
|
const after = dropUnreadable5(yield* readRole(news));
|
|
5726
5812
|
if (after === undefined) {
|
|
5727
|
-
return yield*
|
|
5813
|
+
return yield* Effect78.die(new Error(`access/roles/${news.roleid}: the write returned no error but the role is ` + 'still absent. PVE wraps every answer in {"data":...} and can report success ' + "on a call that did nothing -- read back rather than trusting the status code."));
|
|
5728
5814
|
}
|
|
5729
5815
|
return after;
|
|
5730
5816
|
}),
|
|
5731
|
-
delete:
|
|
5817
|
+
delete: Effect78.fn(function* ({ olds }) {
|
|
5732
5818
|
yield* runPve(olds.target, "provision", true, access5.deleteAccessRole({ roleid: olds.roleid }));
|
|
5733
5819
|
})
|
|
5734
5820
|
})));
|
|
@@ -5738,11 +5824,11 @@ import { Resource as Resource27 } from "alchemy";
|
|
|
5738
5824
|
import { isResolved as isResolved25 } from "alchemy/Diff";
|
|
5739
5825
|
import * as Provider27 from "alchemy/Provider";
|
|
5740
5826
|
import * as access7 from "@distilled.cloud/proxmox/access";
|
|
5741
|
-
import * as
|
|
5827
|
+
import * as Effect80 from "effect/Effect";
|
|
5742
5828
|
|
|
5743
5829
|
// src/proxmox/user-wire.ts
|
|
5744
5830
|
import * as access6 from "@distilled.cloud/proxmox/access";
|
|
5745
|
-
import * as
|
|
5831
|
+
import * as Effect79 from "effect/Effect";
|
|
5746
5832
|
var USER_CREATE = "pve:POST /access/users";
|
|
5747
5833
|
var USER_UPDATE = "pve:PUT /access/users/{userid}";
|
|
5748
5834
|
var int2 = (value) => {
|
|
@@ -5784,18 +5870,18 @@ var attributesOf8 = (live, props) => ({
|
|
|
5784
5870
|
userid: props.userid
|
|
5785
5871
|
});
|
|
5786
5872
|
var matches8 = (attributes2, props) => attributes2.comment === (props.comment ?? "") && attributes2.email === (props.email ?? "") && attributes2.enable === (props.enable !== false) && attributes2.expire === (props.expire ?? 0) && attributes2.firstname === (props.firstname ?? "") && attributes2.lastname === (props.lastname ?? "") && attributes2.groups.join(",") === groupSet(props.groups).join(",");
|
|
5787
|
-
var readUserOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, access6.getAccessUser({ userid: props.userid }))).pipe(
|
|
5788
|
-
var readUser = (props) => readUserOrFail(props).pipe(
|
|
5873
|
+
var readUserOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, access6.getAccessUser({ userid: props.userid }))).pipe(Effect79.map((live) => live === UNREADABLE ? UNREADABLE : attributesOf8(live, props)));
|
|
5874
|
+
var readUser = (props) => readUserOrFail(props).pipe(Effect79.catchTag("UserNotFound", () => Effect79.succeed(undefined)));
|
|
5789
5875
|
var dropUnreadable6 = (live) => live === UNREADABLE ? undefined : live;
|
|
5790
5876
|
|
|
5791
5877
|
// src/proxmox/user.ts
|
|
5792
5878
|
var ProxmoxUser = Resource27("Proxmox.User");
|
|
5793
|
-
var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser,
|
|
5794
|
-
list: () =>
|
|
5795
|
-
read:
|
|
5879
|
+
var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser, Effect80.succeed(ProxmoxUser.Provider.of({
|
|
5880
|
+
list: () => Effect80.succeed([]),
|
|
5881
|
+
read: Effect80.fn(function* ({ olds, output }) {
|
|
5796
5882
|
return dropUnreadable6(yield* output === undefined ? readUser(olds) : readUserOrFail(olds));
|
|
5797
5883
|
}),
|
|
5798
|
-
diff:
|
|
5884
|
+
diff: Effect80.fn(function* ({ news, output }) {
|
|
5799
5885
|
if (!isResolved25(news))
|
|
5800
5886
|
return;
|
|
5801
5887
|
yield* guardWrite2(USER_CREATE, createForm8(news), output === undefined);
|
|
@@ -5813,7 +5899,7 @@ var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser, Effect76.succeed(
|
|
|
5813
5899
|
}
|
|
5814
5900
|
return matches8(live, news) ? { action: "noop" } : { action: "update" };
|
|
5815
5901
|
}),
|
|
5816
|
-
reconcile:
|
|
5902
|
+
reconcile: Effect80.fn(function* ({ news }) {
|
|
5817
5903
|
const before = dropUnreadable6(yield* readUser(news));
|
|
5818
5904
|
yield* guardWrite2(USER_CREATE, createForm8(news), before === undefined);
|
|
5819
5905
|
yield* guardWrite2(USER_UPDATE, updateForm5(news), false);
|
|
@@ -5824,18 +5910,18 @@ var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser, Effect76.succeed(
|
|
|
5824
5910
|
}
|
|
5825
5911
|
const after = dropUnreadable6(yield* readUser(news));
|
|
5826
5912
|
if (after === undefined) {
|
|
5827
|
-
return yield*
|
|
5913
|
+
return yield* Effect80.die(new Error(`access/users/${news.userid}: the write returned no error but the account is ` + 'still absent. PVE wraps every answer in {"data":...} and can report success ' + "on a call that did nothing -- read back rather than trusting the status code."));
|
|
5828
5914
|
}
|
|
5829
5915
|
return after;
|
|
5830
5916
|
}),
|
|
5831
|
-
delete:
|
|
5917
|
+
delete: Effect80.fn(function* ({ olds }) {
|
|
5832
5918
|
yield* runPve(olds.target, "provision", true, access7.deleteAccessUser({ userid: olds.userid }));
|
|
5833
5919
|
})
|
|
5834
5920
|
})));
|
|
5835
5921
|
|
|
5836
5922
|
// src/proxmox/provision-declare.ts
|
|
5837
|
-
var declareProvisionBaseline = (id, target, names2 = {}, options = {}) =>
|
|
5838
|
-
const baseline = yield*
|
|
5923
|
+
var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect81.gen(function* () {
|
|
5924
|
+
const baseline = yield* Effect81.try({
|
|
5839
5925
|
catch: (cause) => cause instanceof Error ? cause : new Error(String(cause)),
|
|
5840
5926
|
try: () => provisionBaseline(names2)
|
|
5841
5927
|
});
|
|
@@ -5849,13 +5935,13 @@ var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect
|
|
|
5849
5935
|
target
|
|
5850
5936
|
}));
|
|
5851
5937
|
const group = yield* owned(ProxmoxGroup(`${id}-mint-group`, { ...baseline.group, target }));
|
|
5852
|
-
const users = yield*
|
|
5938
|
+
const users = yield* Effect81.all(baseline.users.map((user) => owned(ProxmoxUser(`${id}-${user.lane}-user`, {
|
|
5853
5939
|
comment: user.comment,
|
|
5854
5940
|
groups: [...user.groups],
|
|
5855
5941
|
target,
|
|
5856
5942
|
userid: user.userid
|
|
5857
5943
|
}))));
|
|
5858
|
-
const grants = yield*
|
|
5944
|
+
const grants = yield* Effect81.all(baseline.grants.map((grant) => owned(ProxmoxAcl(`${id}-${grant.lane}-grant`, {
|
|
5859
5945
|
path: "/",
|
|
5860
5946
|
propagate: true,
|
|
5861
5947
|
roleid: grant.roleid,
|
|
@@ -5868,9 +5954,14 @@ var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect
|
|
|
5868
5954
|
// src/proxmox/qemu.ts
|
|
5869
5955
|
import { Resource as Resource28 } from "alchemy";
|
|
5870
5956
|
import * as Provider28 from "alchemy/Provider";
|
|
5871
|
-
import * as
|
|
5872
|
-
|
|
5873
|
-
|
|
5957
|
+
import * as Effect85 from "effect/Effect";
|
|
5958
|
+
|
|
5959
|
+
// src/proxmox/qemu-lifecycle.ts
|
|
5960
|
+
import { isResolved as isResolved26 } from "alchemy/Diff";
|
|
5961
|
+
import * as nodes17 from "@distilled.cloud/proxmox/nodes";
|
|
5962
|
+
import * as Effect84 from "effect/Effect";
|
|
5963
|
+
|
|
5964
|
+
// src/proxmox/qemu-form.ts
|
|
5874
5965
|
var shape9 = (props) => ({
|
|
5875
5966
|
cores: String(props.cores ?? 1),
|
|
5876
5967
|
memory: String(props.memory ?? 512),
|
|
@@ -5879,31 +5970,155 @@ var shape9 = (props) => ({
|
|
|
5879
5970
|
sockets: String(props.sockets ?? 1),
|
|
5880
5971
|
...props.net0 === undefined ? {} : { net0: props.net0 }
|
|
5881
5972
|
});
|
|
5882
|
-
var
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5973
|
+
var createForm9 = (props) => ({ ...shape9(props), vmid: String(props.vmid) });
|
|
5974
|
+
|
|
5975
|
+
// src/proxmox/qemu-errors.ts
|
|
5976
|
+
class QemuRefusedError extends Error {
|
|
5977
|
+
constructor(message) {
|
|
5978
|
+
super(message);
|
|
5979
|
+
this.name = "QemuRefusedError";
|
|
5980
|
+
}
|
|
5981
|
+
}
|
|
5982
|
+
|
|
5983
|
+
// src/proxmox/qemu-read.ts
|
|
5984
|
+
import * as cluster10 from "@distilled.cloud/proxmox/cluster";
|
|
5985
|
+
import * as nodes15 from "@distilled.cloud/proxmox/nodes";
|
|
5986
|
+
import * as Effect82 from "effect/Effect";
|
|
5987
|
+
var guestTypes = ["lxc", "qemu"];
|
|
5988
|
+
var readVm = (props) => Effect82.gen(function* () {
|
|
5989
|
+
const credential = yield* leased(props.target, "read");
|
|
5990
|
+
const answer = yield* runPveWith(props.target, credential, false, nodes15.getNodeQemuConfig({ node: props.node, vmid: String(props.vmid) })).pipe(Effect82.map((data) => ({ data, error: undefined })), Effect82.catchTag("QemuConfigNotFound", (error) => Effect82.succeed({ data: undefined, error })));
|
|
5991
|
+
if (answer.error === undefined) {
|
|
5992
|
+
const live = answer.data;
|
|
5993
|
+
if (typeof live.digest !== "string" || live.digest === "") {
|
|
5994
|
+
return yield* Effect82.fail(new QemuRefusedError(`VM ${String(props.vmid)}: config read did not return a digest-bearing config.`));
|
|
5995
|
+
}
|
|
5996
|
+
return live;
|
|
5997
|
+
}
|
|
5998
|
+
const rows = yield* runPveWith(props.target, credential, false, cluster10.listClusterResources({ type: "vm" }));
|
|
5999
|
+
if (!Array.isArray(rows) || rows.some((row) => !Number.isSafeInteger(Number(row?.vmid)) || Number(row?.vmid) <= 0 || typeof row?.node !== "string" || row.node === "" || !guestTypes.includes(row.type))) {
|
|
6000
|
+
return yield* Effect82.fail(new QemuRefusedError("The cluster guest index did not identify its rows."));
|
|
6001
|
+
}
|
|
6002
|
+
const found = rows.find((row) => Number(row.vmid) === props.vmid);
|
|
6003
|
+
if (found === undefined)
|
|
6004
|
+
return;
|
|
6005
|
+
if (found.node === props.node && found.type === "qemu")
|
|
6006
|
+
return yield* Effect82.fail(answer.error);
|
|
6007
|
+
return yield* Effect82.fail(new QemuRefusedError(`vmid ${String(props.vmid)} is a ${text(found.type, "guest")} on ` + `${text(found.node, "?")}, not a VM on ${props.node}. A migration is not an ` + "update and vmids are cluster-wide: declare the node it is on, or pick a free vmid."));
|
|
6008
|
+
});
|
|
6009
|
+
|
|
6010
|
+
// src/proxmox/qemu-task.ts
|
|
6011
|
+
import * as nodes16 from "@distilled.cloud/proxmox/nodes";
|
|
6012
|
+
import * as Effect83 from "effect/Effect";
|
|
6013
|
+
var qemuTask = (props, operation, what, polls) => Effect83.gen(function* () {
|
|
6014
|
+
const credential = yield* mint(props.target, "provision");
|
|
6015
|
+
const upid = text(yield* runPveWith(props.target, credential, true, operation));
|
|
6016
|
+
if (upid === "") {
|
|
6017
|
+
return yield* Effect83.fail(new QemuRefusedError(`${what}: PVE returned no task id, so the write cannot be confirmed and is not claimed.`));
|
|
6018
|
+
}
|
|
6019
|
+
let exit = "still running when this provider stopped waiting";
|
|
6020
|
+
for (let attempt = 0;attempt < polls; attempt++) {
|
|
6021
|
+
const status = yield* runPveWith(props.target, credential, false, nodes16.getNodeTaskStatus({ node: props.node, upid })).pipe(Effect83.orElseSucceed(() => {
|
|
6022
|
+
return;
|
|
6023
|
+
}));
|
|
6024
|
+
if (status === undefined || status.status === undefined) {
|
|
6025
|
+
exit = "the task could not be read";
|
|
6026
|
+
break;
|
|
6027
|
+
}
|
|
6028
|
+
if (status.status === "stopped") {
|
|
6029
|
+
exit = text(status.exitstatus, "stopped with no exitstatus");
|
|
6030
|
+
break;
|
|
6031
|
+
}
|
|
6032
|
+
yield* Effect83.sleep("1 second");
|
|
6033
|
+
}
|
|
6034
|
+
if (exit !== "OK") {
|
|
6035
|
+
return yield* Effect83.fail(new QemuRefusedError(`${what}: task ${upid} ended "${exit}". Read the task log before running the deploy again.`));
|
|
6036
|
+
}
|
|
6037
|
+
});
|
|
6038
|
+
|
|
6039
|
+
// src/proxmox/qemu-lifecycle.ts
|
|
6040
|
+
var CREATE_POLLS2 = 180;
|
|
6041
|
+
var DELETE_POLLS = 60;
|
|
6042
|
+
var attributesOf9 = (live, props) => ({
|
|
6043
|
+
cores: int(live.cores, 1),
|
|
6044
|
+
memory: int(live.memory, 512),
|
|
6045
|
+
name: typeof live.name === "string" ? live.name : "",
|
|
6046
|
+
node: props.node,
|
|
6047
|
+
onboot: live.onboot === 1 || live.onboot === true,
|
|
6048
|
+
sockets: int(live.sockets, 1),
|
|
6049
|
+
vmid: props.vmid
|
|
6050
|
+
});
|
|
6051
|
+
var matches9 = (attributes2, props) => attributes2.name === (props.name ?? `vm${String(props.vmid)}`) && attributes2.memory === (props.memory ?? 512) && attributes2.cores === (props.cores ?? 1) && attributes2.sockets === (props.sockets ?? 1) && attributes2.onboot === (props.onboot === true);
|
|
6052
|
+
var spec5 = {
|
|
6053
|
+
attributes: attributesOf9,
|
|
5892
6054
|
collection: (props) => `nodes/${props.node}/qemu`,
|
|
5893
6055
|
createForm: createForm9,
|
|
5894
6056
|
endpoint: {
|
|
5895
6057
|
create: "pve:POST /nodes/{node}/qemu",
|
|
5896
6058
|
update: "pve:PUT /nodes/{node}/qemu/{vmid}/config"
|
|
5897
6059
|
},
|
|
5898
|
-
matches:
|
|
6060
|
+
matches: matches9,
|
|
5899
6061
|
path: (props) => `nodes/${props.node}/qemu/${String(props.vmid)}/config`,
|
|
5900
6062
|
updateForm: shape9
|
|
5901
|
-
}
|
|
5902
|
-
var
|
|
6063
|
+
};
|
|
6064
|
+
var { guardCreate: guardCreate10, guardUpdate: guardUpdate10 } = specGuards(spec5);
|
|
6065
|
+
var readQemu = (props) => readVm(props).pipe(Effect84.map((live) => live === undefined ? undefined : attributesOf9(live, props)));
|
|
6066
|
+
var deleteQemu = (props) => qemuTask(props, nodes17.deleteNodeQemu({ node: props.node, vmid: String(props.vmid) }), `destroy VM ${String(props.vmid)}`, DELETE_POLLS).pipe(Effect84.catchTag("QemuConfigNotFound", () => Effect84.void));
|
|
6067
|
+
var qemuHandlers = {
|
|
6068
|
+
list: () => Effect84.succeed([]),
|
|
6069
|
+
read: ({ olds }) => readQemu(olds),
|
|
6070
|
+
diff: Effect84.fn(function* ({
|
|
6071
|
+
news,
|
|
6072
|
+
output
|
|
6073
|
+
}) {
|
|
6074
|
+
if (!isResolved26(news))
|
|
6075
|
+
return;
|
|
6076
|
+
yield* guardCreate10(news, output === undefined);
|
|
6077
|
+
yield* guardUpdate10(news);
|
|
6078
|
+
if (output === undefined)
|
|
6079
|
+
return;
|
|
6080
|
+
const live = yield* readQemu(news);
|
|
6081
|
+
if (live === undefined) {
|
|
6082
|
+
yield* guardCreate10(news, true);
|
|
6083
|
+
return { action: "update" };
|
|
6084
|
+
}
|
|
6085
|
+
return { action: spec5.matches(live, news) ? "noop" : "update" };
|
|
6086
|
+
}),
|
|
6087
|
+
reconcile: Effect84.fn(function* ({ news }) {
|
|
6088
|
+
const live = yield* readQemu(news);
|
|
6089
|
+
yield* guardCreate10(news, live === undefined);
|
|
6090
|
+
yield* guardUpdate10(news);
|
|
6091
|
+
if (live === undefined) {
|
|
6092
|
+
yield* qemuTask(news, nodes17.createNodeQemu({ ...createForm9(news), node: news.node }), `create VM ${String(news.vmid)}`, CREATE_POLLS2);
|
|
6093
|
+
} else {
|
|
6094
|
+
const form = formToSend(spec5.matches, live, news, spec5.updateForm(news));
|
|
6095
|
+
if (form !== undefined) {
|
|
6096
|
+
yield* runPve(news.target, "provision", true, nodes17.putNodeQemuConfig({ ...form, node: news.node, vmid: String(news.vmid) }));
|
|
6097
|
+
}
|
|
6098
|
+
}
|
|
6099
|
+
const after = yield* readQemu(news);
|
|
6100
|
+
if (after === undefined) {
|
|
6101
|
+
return yield* Effect84.fail(new QemuRefusedError(`${spec5.path(news)}: write returned success but the VM is still absent`));
|
|
6102
|
+
}
|
|
6103
|
+
return after;
|
|
6104
|
+
}),
|
|
6105
|
+
delete: ({ olds }) => deleteQemu(olds)
|
|
6106
|
+
};
|
|
6107
|
+
|
|
6108
|
+
// src/proxmox/qemu.ts
|
|
6109
|
+
var ProxmoxVm = Resource28("Proxmox.Vm");
|
|
6110
|
+
var ProxmoxVmProvider = () => Provider28.effect(ProxmoxVm, Effect85.succeed(ProxmoxVm.Provider.of(qemuHandlers)));
|
|
5903
6111
|
// src/proxmox/replication-job.ts
|
|
5904
6112
|
import { Resource as Resource29 } from "alchemy";
|
|
5905
6113
|
import * as Provider29 from "alchemy/Provider";
|
|
5906
|
-
import * as
|
|
6114
|
+
import * as Effect87 from "effect/Effect";
|
|
6115
|
+
|
|
6116
|
+
// src/proxmox/replication-job-lifecycle.ts
|
|
6117
|
+
import { isResolved as isResolved27 } from "alchemy/Diff";
|
|
6118
|
+
import * as cluster11 from "@distilled.cloud/proxmox/cluster";
|
|
6119
|
+
import { ProxmoxParseError as ProxmoxParseError6 } from "@distilled.cloud/proxmox/Errors";
|
|
6120
|
+
import * as Effect86 from "effect/Effect";
|
|
6121
|
+
import * as Schema6 from "effect/Schema";
|
|
5907
6122
|
|
|
5908
6123
|
// src/proxmox/replication-job-form.ts
|
|
5909
6124
|
var DEFAULT_SCHEDULE = "*/15";
|
|
@@ -5925,15 +6140,14 @@ var updateBody4 = (props) => ({
|
|
|
5925
6140
|
delete: "remove_job"
|
|
5926
6141
|
});
|
|
5927
6142
|
|
|
5928
|
-
// src/proxmox/replication-job.ts
|
|
5929
|
-
var ProxmoxReplicationJob = Resource29("Proxmox.ReplicationJob");
|
|
6143
|
+
// src/proxmox/replication-job-config.ts
|
|
5930
6144
|
var rateOf = (value) => {
|
|
5931
6145
|
if (typeof value === "number")
|
|
5932
6146
|
return value;
|
|
5933
6147
|
const parsed = Number.parseFloat(text(value));
|
|
5934
6148
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
5935
6149
|
};
|
|
5936
|
-
var
|
|
6150
|
+
var replicationJobSpec = {
|
|
5937
6151
|
attributes: (live, props) => ({
|
|
5938
6152
|
comment: text(live["comment"], ""),
|
|
5939
6153
|
disable: bool(live["disable"], false),
|
|
@@ -5956,16 +6170,71 @@ var handlers15 = pveHandlers({
|
|
|
5956
6170
|
matches: (attributes2, props) => attributes2.remove_job === "" && attributes2.schedule === (props.schedule ?? DEFAULT_SCHEDULE) && attributes2.disable === (props.disable === true) && attributes2.comment === (props.comment ?? "") && (props.rate === undefined || attributes2.rate === props.rate),
|
|
5957
6171
|
path: (props) => `cluster/replication/${jobId(props)}`,
|
|
5958
6172
|
updateForm: updateBody4
|
|
6173
|
+
};
|
|
6174
|
+
|
|
6175
|
+
// src/proxmox/replication-job-lifecycle.ts
|
|
6176
|
+
var { guardCreate: guardCreate11, guardUpdate: guardUpdate11 } = specGuards(replicationJobSpec);
|
|
6177
|
+
var malformed = () => new ProxmoxParseError6({
|
|
6178
|
+
body: undefined,
|
|
6179
|
+
cause: "Replication job response does not match the requested vendor identity"
|
|
5959
6180
|
});
|
|
5960
|
-
var
|
|
6181
|
+
var readReplicationJob = (props) => runPve(props.target, "read", false, cluster11.getClusterReplication({ id: jobId(props) })).pipe(Effect86.flatMap((live) => Schema6.decodeUnknownEffect(Schema6.toType(cluster11.GetClusterReplicationResponse))({
|
|
6182
|
+
...live,
|
|
6183
|
+
guest: int(live.guest, Number.NaN),
|
|
6184
|
+
jobnum: int(live.jobnum, Number.NaN),
|
|
6185
|
+
rate: undefined
|
|
6186
|
+
}).pipe(Effect86.mapError(malformed), Effect86.as(live))), Effect86.flatMap((live) => live.id === jobId(props) && int(live.guest, Number.NaN) === props.guest && int(live.jobnum, Number.NaN) === props.jobnum ? Effect86.succeed(replicationJobSpec.attributes({ ...live }, props)) : Effect86.fail(malformed())), Effect86.catchTag("ReplicationJobNotFound", () => Effect86.succeed(undefined)));
|
|
6187
|
+
var deleteReplicationJob = (props) => runPve(props.target, "provision", true, cluster11.deleteClusterReplication({ id: jobId(props) })).pipe(Effect86.catchTag("ReplicationJobNotFound", () => Effect86.void));
|
|
6188
|
+
var replicationJobHandlers = {
|
|
6189
|
+
list: () => Effect86.succeed([]),
|
|
6190
|
+
read: ({ olds }) => readReplicationJob(olds),
|
|
6191
|
+
diff: Effect86.fn(function* ({
|
|
6192
|
+
news,
|
|
6193
|
+
output
|
|
6194
|
+
}) {
|
|
6195
|
+
if (!isResolved27(news))
|
|
6196
|
+
return;
|
|
6197
|
+
yield* guardCreate11(news, output === undefined);
|
|
6198
|
+
yield* guardUpdate11(news);
|
|
6199
|
+
if (output === undefined)
|
|
6200
|
+
return;
|
|
6201
|
+
const live = yield* readReplicationJob(news);
|
|
6202
|
+
if (live === undefined) {
|
|
6203
|
+
yield* guardCreate11(news, true);
|
|
6204
|
+
return { action: "update" };
|
|
6205
|
+
}
|
|
6206
|
+
return { action: replicationJobSpec.matches(live, news) ? "noop" : "update" };
|
|
6207
|
+
}),
|
|
6208
|
+
reconcile: Effect86.fn(function* ({ news }) {
|
|
6209
|
+
const live = yield* readReplicationJob(news);
|
|
6210
|
+
yield* guardCreate11(news, live === undefined);
|
|
6211
|
+
yield* guardUpdate11(news);
|
|
6212
|
+
if (live === undefined) {
|
|
6213
|
+
yield* runPve(news.target, "provision", true, cluster11.createClusterReplication(replicationJobSpec.createForm(news)));
|
|
6214
|
+
} else {
|
|
6215
|
+
const form = formToSend(replicationJobSpec.matches, live, news, replicationJobSpec.updateForm(news));
|
|
6216
|
+
if (form !== undefined)
|
|
6217
|
+
yield* runPve(news.target, "provision", true, cluster11.putClusterReplication({ ...form, id: jobId(news) }));
|
|
6218
|
+
}
|
|
6219
|
+
const after = yield* readReplicationJob(news);
|
|
6220
|
+
if (after === undefined)
|
|
6221
|
+
return yield* Effect86.fail(new Error(`${replicationJobSpec.path(news)}: write returned success but the resource is still absent`));
|
|
6222
|
+
return after;
|
|
6223
|
+
}),
|
|
6224
|
+
delete: ({ olds }) => deleteReplicationJob(olds)
|
|
6225
|
+
};
|
|
6226
|
+
|
|
6227
|
+
// src/proxmox/replication-job.ts
|
|
6228
|
+
var ProxmoxReplicationJob = Resource29("Proxmox.ReplicationJob");
|
|
6229
|
+
var ProxmoxReplicationJobProvider = () => Provider29.effect(ProxmoxReplicationJob, Effect87.succeed(ProxmoxReplicationJob.Provider.of(replicationJobHandlers)));
|
|
5961
6230
|
// src/proxmox/sdn-apply.ts
|
|
5962
6231
|
import { Resource as Resource30 } from "alchemy";
|
|
5963
|
-
import { isResolved as
|
|
6232
|
+
import { isResolved as isResolved28 } from "alchemy/Diff";
|
|
5964
6233
|
import * as Provider30 from "alchemy/Provider";
|
|
5965
|
-
import * as
|
|
6234
|
+
import * as Effect89 from "effect/Effect";
|
|
5966
6235
|
|
|
5967
6236
|
// src/proxmox/sdn-apply-read.ts
|
|
5968
|
-
import * as
|
|
6237
|
+
import * as Effect88 from "effect/Effect";
|
|
5969
6238
|
var MARKED = [
|
|
5970
6239
|
"cluster/sdn/zones",
|
|
5971
6240
|
"cluster/sdn/vnets",
|
|
@@ -5974,7 +6243,7 @@ var MARKED = [
|
|
|
5974
6243
|
"cluster/sdn/route-maps"
|
|
5975
6244
|
];
|
|
5976
6245
|
var subsystemAbsent = (error) => error instanceof PveError && error.status === 501;
|
|
5977
|
-
var markedPending = (target) =>
|
|
6246
|
+
var markedPending = (target) => Effect88.all(MARKED.map((collection) => pve(target, "read", "GET", `${collection}?pending=1`).pipe(Effect88.map((rows) => (rows ?? []).filter((row) => row.state !== undefined).length), Effect88.catchIf(subsystemAbsent, () => Effect88.succeed(0)))), { concurrency: "unbounded" }).pipe(Effect88.map((counts) => counts.reduce((total, count2) => total + count2, 0)));
|
|
5978
6247
|
var fabricStaged = (staged, running) => {
|
|
5979
6248
|
if (staged === undefined && running === undefined)
|
|
5980
6249
|
return 0;
|
|
@@ -5982,9 +6251,9 @@ var fabricStaged = (staged, running) => {
|
|
|
5982
6251
|
return "inconsistent";
|
|
5983
6252
|
return staged === running ? 0 : 1;
|
|
5984
6253
|
};
|
|
5985
|
-
var fabricPending = (target) =>
|
|
6254
|
+
var fabricPending = (target) => Effect88.all(["pending", "running"].map((view) => pve(target, "read", "GET", `cluster/sdn/fabrics/all?${view}=1`).pipe(Effect88.map(canonical3), Effect88.catchIf(subsystemAbsent, () => Effect88.succeed(undefined)))), { concurrency: "unbounded" }).pipe(Effect88.flatMap(([staged, running]) => {
|
|
5986
6255
|
const verdict = fabricStaged(staged, running);
|
|
5987
|
-
return verdict === "inconsistent" ?
|
|
6256
|
+
return verdict === "inconsistent" ? Effect88.die(new Error("cluster/sdn/fabrics/all answered 501 for one of ?pending=1 and ?running=1 and not " + "the other. Refusing to read that as a staged fabric change.")) : Effect88.succeed(verdict);
|
|
5988
6257
|
}));
|
|
5989
6258
|
var canonical3 = (value) => JSON.stringify(sortDeep(value));
|
|
5990
6259
|
var sortDeep = (value) => {
|
|
@@ -5997,23 +6266,23 @@ var sortDeep = (value) => {
|
|
|
5997
6266
|
}
|
|
5998
6267
|
return value;
|
|
5999
6268
|
};
|
|
6000
|
-
var sdnPendingCount = (target) =>
|
|
6269
|
+
var sdnPendingCount = (target) => Effect88.all([markedPending(target), fabricPending(target)], { concurrency: "unbounded" }).pipe(Effect88.map(([marked, fabric]) => marked + fabric));
|
|
6001
6270
|
|
|
6002
6271
|
// src/proxmox/sdn-apply.ts
|
|
6003
6272
|
var ProxmoxSdnApply = Resource30("Proxmox.SdnApply");
|
|
6004
|
-
var read5 = (props) => sdnPendingCount(props.target).pipe(
|
|
6005
|
-
var ProxmoxSdnApplyProvider = () => Provider30.effect(ProxmoxSdnApply,
|
|
6006
|
-
list: () =>
|
|
6007
|
-
read:
|
|
6273
|
+
var read5 = (props) => sdnPendingCount(props.target).pipe(Effect89.map((pending) => ({ pending })));
|
|
6274
|
+
var ProxmoxSdnApplyProvider = () => Provider30.effect(ProxmoxSdnApply, Effect89.succeed(ProxmoxSdnApply.Provider.of({
|
|
6275
|
+
list: () => Effect89.succeed([]),
|
|
6276
|
+
read: Effect89.fn(function* ({ olds }) {
|
|
6008
6277
|
return yield* read5(olds);
|
|
6009
6278
|
}),
|
|
6010
|
-
diff:
|
|
6011
|
-
if (!
|
|
6279
|
+
diff: Effect89.fn(function* ({ news }) {
|
|
6280
|
+
if (!isResolved28(news))
|
|
6012
6281
|
return;
|
|
6013
6282
|
const { pending } = yield* read5(news);
|
|
6014
6283
|
return pending === 0 ? { action: "noop" } : { action: "update" };
|
|
6015
6284
|
}),
|
|
6016
|
-
reconcile:
|
|
6285
|
+
reconcile: Effect89.fn(function* ({ news }) {
|
|
6017
6286
|
const staged = yield* read5(news);
|
|
6018
6287
|
if (staged.pending === 0)
|
|
6019
6288
|
return staged;
|
|
@@ -6021,16 +6290,16 @@ var ProxmoxSdnApplyProvider = () => Provider30.effect(ProxmoxSdnApply, Effect81.
|
|
|
6021
6290
|
yield* pve(news.target, "provision", "PUT", "cluster/sdn");
|
|
6022
6291
|
const after = yield* read5(news);
|
|
6023
6292
|
if (after.pending !== 0) {
|
|
6024
|
-
return yield*
|
|
6293
|
+
return yield* Effect89.die(new Error(`PUT /cluster/sdn returned no error but ${String(after.pending)} SDN object(s) ` + "are still staged. The running config does not match the declared one -- check " + "`pvesh get /cluster/sdn/zones --pending 1` for the objects PVE refused."));
|
|
6025
6294
|
}
|
|
6026
6295
|
return after;
|
|
6027
6296
|
}),
|
|
6028
|
-
delete: () =>
|
|
6297
|
+
delete: () => Effect89.void
|
|
6029
6298
|
})));
|
|
6030
6299
|
// src/proxmox/sdn-subnet.ts
|
|
6031
6300
|
import { Resource as Resource31 } from "alchemy";
|
|
6032
6301
|
import * as Provider31 from "alchemy/Provider";
|
|
6033
|
-
import * as
|
|
6302
|
+
import * as Effect90 from "effect/Effect";
|
|
6034
6303
|
|
|
6035
6304
|
// src/proxmox/sdn-subnet-form.ts
|
|
6036
6305
|
var subnetId = (props) => `${props.zone}-${props.cidr.replace("/", "-")}`;
|
|
@@ -6061,7 +6330,7 @@ var updateForm6 = (props) => {
|
|
|
6061
6330
|
|
|
6062
6331
|
// src/proxmox/sdn-subnet.ts
|
|
6063
6332
|
var ProxmoxSdnSubnet = Resource31("Proxmox.SdnSubnet");
|
|
6064
|
-
var
|
|
6333
|
+
var handlers13 = pveHandlers({
|
|
6065
6334
|
attributes: (live, props) => {
|
|
6066
6335
|
const liveVnet = text(live["vnet"]);
|
|
6067
6336
|
if (liveVnet !== "" && liveVnet !== props.vnet)
|
|
@@ -6090,11 +6359,11 @@ var handlers16 = pveHandlers({
|
|
|
6090
6359
|
path: (props) => `cluster/sdn/vnets/${props.vnet}/subnets/${subnetId(props)}`,
|
|
6091
6360
|
updateForm: updateForm6
|
|
6092
6361
|
});
|
|
6093
|
-
var ProxmoxSdnSubnetProvider = () => Provider31.effect(ProxmoxSdnSubnet,
|
|
6362
|
+
var ProxmoxSdnSubnetProvider = () => Provider31.effect(ProxmoxSdnSubnet, Effect90.succeed(ProxmoxSdnSubnet.Provider.of(handlers13)));
|
|
6094
6363
|
// src/proxmox/sdn-vnet.ts
|
|
6095
6364
|
import { Resource as Resource32 } from "alchemy";
|
|
6096
6365
|
import * as Provider32 from "alchemy/Provider";
|
|
6097
|
-
import * as
|
|
6366
|
+
import * as Effect91 from "effect/Effect";
|
|
6098
6367
|
var ProxmoxSdnVnet = Resource32("Proxmox.SdnVnet");
|
|
6099
6368
|
var tagOf = (value) => typeof value === "number" ? value : undefined;
|
|
6100
6369
|
var shape11 = (props) => {
|
|
@@ -6114,7 +6383,7 @@ var updateForm7 = (props) => {
|
|
|
6114
6383
|
];
|
|
6115
6384
|
return withClears(shape11(props), unset2);
|
|
6116
6385
|
};
|
|
6117
|
-
var
|
|
6386
|
+
var handlers14 = pveHandlers({
|
|
6118
6387
|
readRole: "provision",
|
|
6119
6388
|
attributes: (live, props) => ({
|
|
6120
6389
|
alias: typeof live["alias"] === "string" ? live["alias"] : "",
|
|
@@ -6132,11 +6401,11 @@ var handlers17 = pveHandlers({
|
|
|
6132
6401
|
path: (props) => `cluster/sdn/vnets/${props.vnet}`,
|
|
6133
6402
|
updateForm: updateForm7
|
|
6134
6403
|
});
|
|
6135
|
-
var ProxmoxSdnVnetProvider = () => Provider32.effect(ProxmoxSdnVnet,
|
|
6404
|
+
var ProxmoxSdnVnetProvider = () => Provider32.effect(ProxmoxSdnVnet, Effect91.succeed(ProxmoxSdnVnet.Provider.of(handlers14)));
|
|
6136
6405
|
// src/proxmox/sdn-zone.ts
|
|
6137
6406
|
import { Resource as Resource33 } from "alchemy";
|
|
6138
6407
|
import * as Provider33 from "alchemy/Provider";
|
|
6139
|
-
import * as
|
|
6408
|
+
import * as Effect92 from "effect/Effect";
|
|
6140
6409
|
var ProxmoxSdnZone = Resource33("Proxmox.SdnZone");
|
|
6141
6410
|
var sameIpam = (live, declared2) => (live === "" ? "pve" : live) === (declared2 === undefined || declared2 === "" ? "pve" : declared2);
|
|
6142
6411
|
var body6 = (props) => ({
|
|
@@ -6157,7 +6426,7 @@ var clearable = {
|
|
|
6157
6426
|
vlan: ["dns", "dnszone", "ipam", "mtu", "nodes"],
|
|
6158
6427
|
vxlan: ["dns", "dnszone", "ipam", "mtu", "nodes"]
|
|
6159
6428
|
};
|
|
6160
|
-
var
|
|
6429
|
+
var handlers15 = pveHandlers({
|
|
6161
6430
|
readRole: "provision",
|
|
6162
6431
|
attributes: (live, props) => {
|
|
6163
6432
|
const liveType = text(live["type"]);
|
|
@@ -6189,13 +6458,13 @@ var handlers18 = pveHandlers({
|
|
|
6189
6458
|
return withClears(fields2, clear);
|
|
6190
6459
|
}
|
|
6191
6460
|
});
|
|
6192
|
-
var ProxmoxSdnZoneProvider = () => Provider33.effect(ProxmoxSdnZone,
|
|
6461
|
+
var ProxmoxSdnZoneProvider = () => Provider33.effect(ProxmoxSdnZone, Effect92.succeed(ProxmoxSdnZone.Provider.of(handlers15)));
|
|
6193
6462
|
// src/proxmox/storage.ts
|
|
6194
6463
|
import { Resource as Resource34 } from "alchemy";
|
|
6195
|
-
import { isResolved as
|
|
6464
|
+
import { isResolved as isResolved29 } from "alchemy/Diff";
|
|
6196
6465
|
import * as Provider34 from "alchemy/Provider";
|
|
6197
6466
|
import * as storage2 from "@distilled.cloud/proxmox/storage";
|
|
6198
|
-
import * as
|
|
6467
|
+
import * as Effect94 from "effect/Effect";
|
|
6199
6468
|
|
|
6200
6469
|
// src/proxmox/storage-form.ts
|
|
6201
6470
|
var STORAGE_CREATE = "pve:POST /storage";
|
|
@@ -6227,12 +6496,12 @@ var toDistilledUpdate3 = (props) => underscored(updateForm8(props));
|
|
|
6227
6496
|
|
|
6228
6497
|
// src/proxmox/storage-wire.ts
|
|
6229
6498
|
import * as storage from "@distilled.cloud/proxmox/storage";
|
|
6230
|
-
import * as
|
|
6499
|
+
import * as Effect93 from "effect/Effect";
|
|
6231
6500
|
var str2 = (value, fallback = "") => typeof value === "string" ? value : fallback;
|
|
6232
6501
|
var tokens = (value) => value.split(",").map((part) => part.trim()).filter((part) => part !== "").sort().join(",");
|
|
6233
6502
|
var sameList2 = (declared2, live) => declared2 === undefined || tokens(declared2) === tokens(live);
|
|
6234
6503
|
var same7 = (declared2, live) => declared2 === undefined || declared2 === live;
|
|
6235
|
-
var
|
|
6504
|
+
var attributesOf10 = (live, props) => {
|
|
6236
6505
|
const data = live;
|
|
6237
6506
|
return {
|
|
6238
6507
|
comment: str2(data["comment"]),
|
|
@@ -6246,22 +6515,22 @@ var attributesOf9 = (live, props) => {
|
|
|
6246
6515
|
type: str2(data["type"], props.type)
|
|
6247
6516
|
};
|
|
6248
6517
|
};
|
|
6249
|
-
var readStorageOrFail = (props) => readOrUnreadable(runPve(props.target, "provision", false, storage.getStorage({ storage: props.storage }))).pipe(
|
|
6250
|
-
var readStorage = (props) => readStorageOrFail(props).pipe(
|
|
6518
|
+
var readStorageOrFail = (props) => readOrUnreadable(runPve(props.target, "provision", false, storage.getStorage({ storage: props.storage }))).pipe(Effect93.map((live) => live === UNREADABLE ? UNREADABLE : attributesOf10(live, props)));
|
|
6519
|
+
var readStorage = (props) => readStorageOrFail(props).pipe(Effect93.catchTag("StorageNotFound", () => Effect93.succeed(undefined)));
|
|
6251
6520
|
var dropUnreadable7 = (live) => live === UNREADABLE ? undefined : live;
|
|
6252
|
-
var
|
|
6521
|
+
var matches10 = (attributes2, props) => sameList2(props.content, attributes2.content) && sameList2(props.nodes, attributes2.nodes) && sameList2(props["prune-backups"], attributes2["prune-backups"]) && same7(props.disable, attributes2.disable) && same7(props.shared, attributes2.shared) && same7(props.preallocation, attributes2.preallocation) && same7(props.comment, attributes2.comment);
|
|
6253
6522
|
|
|
6254
6523
|
// src/proxmox/storage.ts
|
|
6255
6524
|
var ProxmoxStorage = Resource34("Proxmox.Storage", {
|
|
6256
6525
|
defaultRemovalPolicy: "retain"
|
|
6257
6526
|
});
|
|
6258
|
-
var ProxmoxStorageProvider = () => Provider34.effect(ProxmoxStorage,
|
|
6259
|
-
list: () =>
|
|
6260
|
-
read:
|
|
6527
|
+
var ProxmoxStorageProvider = () => Provider34.effect(ProxmoxStorage, Effect94.succeed(ProxmoxStorage.Provider.of({
|
|
6528
|
+
list: () => Effect94.succeed([]),
|
|
6529
|
+
read: Effect94.fn(function* ({ olds, output }) {
|
|
6261
6530
|
return dropUnreadable7(yield* output === undefined ? readStorage(olds) : readStorageOrFail(olds));
|
|
6262
6531
|
}),
|
|
6263
|
-
diff:
|
|
6264
|
-
if (!
|
|
6532
|
+
diff: Effect94.fn(function* ({ news, output }) {
|
|
6533
|
+
if (!isResolved29(news))
|
|
6265
6534
|
return;
|
|
6266
6535
|
yield* guardWrite2(STORAGE_CREATE, createForm11(news), output === undefined);
|
|
6267
6536
|
yield* guardWrite2(STORAGE_UPDATE, updateForm8(news), false);
|
|
@@ -6276,61 +6545,61 @@ var ProxmoxStorageProvider = () => Provider34.effect(ProxmoxStorage, Effect86.su
|
|
|
6276
6545
|
yield* guardWrite2(STORAGE_CREATE, createForm11(news), true);
|
|
6277
6546
|
return { action: "update" };
|
|
6278
6547
|
}
|
|
6279
|
-
return
|
|
6548
|
+
return matches10(live, news) ? { action: "noop" } : { action: "update" };
|
|
6280
6549
|
}),
|
|
6281
|
-
reconcile:
|
|
6550
|
+
reconcile: Effect94.fn(function* ({ news }) {
|
|
6282
6551
|
const before = dropUnreadable7(yield* readStorage(news));
|
|
6283
6552
|
yield* guardWrite2(STORAGE_CREATE, createForm11(news), before === undefined);
|
|
6284
6553
|
yield* guardWrite2(STORAGE_UPDATE, updateForm8(news), false);
|
|
6285
6554
|
if (before === undefined) {
|
|
6286
6555
|
yield* runPve(news.target, "provision", true, storage2.createStorage(toDistilledCreate4(news)));
|
|
6287
|
-
} else if (!
|
|
6556
|
+
} else if (!matches10(before, news)) {
|
|
6288
6557
|
yield* runPve(news.target, "provision", true, storage2.putStorage(toDistilledUpdate3(news)));
|
|
6289
6558
|
}
|
|
6290
6559
|
const after = dropUnreadable7(yield* readStorage(news));
|
|
6291
6560
|
if (after === undefined) {
|
|
6292
|
-
return yield*
|
|
6561
|
+
return yield* Effect94.die(new Error(`storage/${news.storage}: the write returned no error but the storage is still ` + 'absent. PVE wraps every answer in {"data":...} and can report success on a ' + "call that did nothing -- read back rather than trusting the status code."));
|
|
6293
6562
|
}
|
|
6294
6563
|
return after;
|
|
6295
6564
|
}),
|
|
6296
|
-
delete:
|
|
6565
|
+
delete: Effect94.fn(function* ({ olds }) {
|
|
6297
6566
|
yield* runPve(olds.target, "provision", true, storage2.deleteStorage({ storage: olds.storage }));
|
|
6298
6567
|
})
|
|
6299
6568
|
})));
|
|
6300
6569
|
// src/proxmox/zfs-pool.ts
|
|
6301
6570
|
import { Resource as Resource35 } from "alchemy";
|
|
6302
|
-
import { isResolved as
|
|
6571
|
+
import { isResolved as isResolved30 } from "alchemy/Diff";
|
|
6303
6572
|
import * as Provider35 from "alchemy/Provider";
|
|
6304
|
-
import * as
|
|
6573
|
+
import * as Effect97 from "effect/Effect";
|
|
6305
6574
|
|
|
6306
6575
|
// src/proxmox/zfs-pool-wire.ts
|
|
6307
|
-
import * as
|
|
6308
|
-
import * as
|
|
6576
|
+
import * as nodes18 from "@distilled.cloud/proxmox/nodes";
|
|
6577
|
+
import * as Effect95 from "effect/Effect";
|
|
6309
6578
|
var leafDevices = (children) => Array.isArray(children) ? children.flatMap((entry) => {
|
|
6310
6579
|
const vdev = entry;
|
|
6311
6580
|
const nested = leafDevices(vdev.children);
|
|
6312
6581
|
return nested.length > 0 ? nested : [text(vdev.name)].filter((name) => name !== "");
|
|
6313
6582
|
}) : [];
|
|
6314
|
-
var
|
|
6583
|
+
var attributesOf11 = (live, props) => ({
|
|
6315
6584
|
devices: leafDevices(live.children).join(","),
|
|
6316
6585
|
errors: text(live.errors, "unknown"),
|
|
6317
6586
|
name: props.name,
|
|
6318
6587
|
node: props.node,
|
|
6319
6588
|
state: text(live.state, "unknown")
|
|
6320
6589
|
});
|
|
6321
|
-
var readPoolOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false,
|
|
6590
|
+
var readPoolOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, nodes18.getNodeDiskZfs({ name: props.name, node: props.node }))).pipe(Effect95.map((live) => {
|
|
6322
6591
|
if (live === UNREADABLE)
|
|
6323
6592
|
return UNREADABLE;
|
|
6324
|
-
return text(live.name) === "" ? undefined :
|
|
6593
|
+
return text(live.name) === "" ? undefined : attributesOf11(live, props);
|
|
6325
6594
|
}));
|
|
6326
|
-
var readPool2 = (props) => readPoolOrFail(props).pipe(
|
|
6595
|
+
var readPool2 = (props) => readPoolOrFail(props).pipe(Effect95.orElseSucceed(() => {
|
|
6327
6596
|
return;
|
|
6328
6597
|
}));
|
|
6329
6598
|
var dropUnreadable8 = (live) => live === UNREADABLE ? undefined : live;
|
|
6330
6599
|
|
|
6331
6600
|
// src/proxmox/zfs-pool-form.ts
|
|
6332
|
-
import * as
|
|
6333
|
-
import * as
|
|
6601
|
+
import * as Effect96 from "effect/Effect";
|
|
6602
|
+
import * as nodes19 from "@distilled.cloud/proxmox/nodes";
|
|
6334
6603
|
var ZFS_POOL_CREATE = "pve:POST /nodes/{node}/disks/zfs";
|
|
6335
6604
|
var zfsPoolCreateEndpoint = (props) => props.devices === undefined || props.raidlevel === undefined ? undefined : ZFS_POOL_CREATE;
|
|
6336
6605
|
var createForm12 = (props) => ({
|
|
@@ -6350,41 +6619,41 @@ var toDistilledCreate5 = (props) => {
|
|
|
6350
6619
|
};
|
|
6351
6620
|
};
|
|
6352
6621
|
var SETTLE_ATTEMPTS = 30;
|
|
6353
|
-
var createPool2 = (props, read6) =>
|
|
6622
|
+
var createPool2 = (props, read6) => Effect96.gen(function* () {
|
|
6354
6623
|
const existing = yield* read6(props);
|
|
6355
6624
|
if (existing !== undefined)
|
|
6356
6625
|
return existing;
|
|
6357
6626
|
if (props.devices === undefined || props.raidlevel === undefined) {
|
|
6358
|
-
return yield*
|
|
6627
|
+
return yield* Effect96.die(new Error(`${props.node}/${props.name}: this is an adopt-only declaration (no \`devices\` or ` + "`raidlevel`) and the pool is not there to adopt. An adopt-only Proxmox.ZfsPool never " + "creates -- declare both fields for a pool this resource should build, or point this " + "declaration at a pool that already exists on this node."));
|
|
6359
6628
|
}
|
|
6360
6629
|
yield* guardForm(zfsPoolCreateEndpoint(props), asForm2(createForm12(props)), true);
|
|
6361
|
-
const upid = yield* runPve(props.target, "provision", true,
|
|
6362
|
-
const settle3 = (attempts) =>
|
|
6630
|
+
const upid = yield* runPve(props.target, "provision", true, nodes19.createNodeDiskZfs(toDistilledCreate5(props)));
|
|
6631
|
+
const settle3 = (attempts) => Effect96.gen(function* () {
|
|
6363
6632
|
const live = yield* read6(props);
|
|
6364
6633
|
if (live !== undefined || attempts <= 0)
|
|
6365
6634
|
return live;
|
|
6366
|
-
yield*
|
|
6635
|
+
yield* Effect96.sleep("2 seconds");
|
|
6367
6636
|
return yield* settle3(attempts - 1);
|
|
6368
6637
|
});
|
|
6369
6638
|
const after = yield* settle3(SETTLE_ATTEMPTS);
|
|
6370
6639
|
if (after === undefined) {
|
|
6371
|
-
return yield*
|
|
6640
|
+
return yield* Effect96.die(new Error(`nodes/${props.node}/disks/zfs/${props.name}: POST returned ${text(upid, "(no UPID)")} ` + "but the pool has not appeared. That POST only forks a `zfscreate` worker, so the task " + `holds the real error -- read it with \`pvesh get /nodes/${props.node}/tasks/<upid>/log\`. ` + "NOTHING WAS RETRIED AND NOTHING WAS CLEANED UP: if the worker reached `zpool create`, " + "the declared devices have already been written to. DO NOT RE-RUN THIS DEPLOY BEFORE " + 'READING THAT LOG: `read` cannot tell "the worker is still running" from "the pool is ' + 'genuinely gone", so a re-run this soon can POST a SECOND `zpool create` at the SAME ' + "devices while the first one is still mid-write -- confirm the task finished (one way " + "or the other) before trying again."));
|
|
6372
6641
|
}
|
|
6373
6642
|
return after;
|
|
6374
6643
|
});
|
|
6375
|
-
var destroyPool = (props) => runPve(props.target, "provision", true,
|
|
6644
|
+
var destroyPool = (props) => runPve(props.target, "provision", true, nodes19.deleteNodeDiskZfs({ name: props.name, node: props.node }));
|
|
6376
6645
|
|
|
6377
6646
|
// src/proxmox/zfs-pool.ts
|
|
6378
6647
|
var ProxmoxZfsPool = Resource35("Proxmox.ZfsPool", {
|
|
6379
6648
|
defaultRemovalPolicy: "retain"
|
|
6380
6649
|
});
|
|
6381
|
-
var ProxmoxZfsPoolProvider = () => Provider35.effect(ProxmoxZfsPool,
|
|
6382
|
-
list: () =>
|
|
6383
|
-
read:
|
|
6650
|
+
var ProxmoxZfsPoolProvider = () => Provider35.effect(ProxmoxZfsPool, Effect97.succeed(ProxmoxZfsPool.Provider.of({
|
|
6651
|
+
list: () => Effect97.succeed([]),
|
|
6652
|
+
read: Effect97.fn(function* ({ olds, output }) {
|
|
6384
6653
|
return dropUnreadable8(yield* output === undefined ? readPool2(olds) : readPoolOrFail(olds));
|
|
6385
6654
|
}),
|
|
6386
|
-
diff:
|
|
6387
|
-
if (!
|
|
6655
|
+
diff: Effect97.fn(function* ({ news, output }) {
|
|
6656
|
+
if (!isResolved30(news))
|
|
6388
6657
|
return;
|
|
6389
6658
|
yield* guardForm(zfsPoolCreateEndpoint(news), asForm2(createForm12(news)), output === undefined);
|
|
6390
6659
|
if (output === undefined)
|
|
@@ -6400,10 +6669,10 @@ var ProxmoxZfsPoolProvider = () => Provider35.effect(ProxmoxZfsPool, Effect89.su
|
|
|
6400
6669
|
}
|
|
6401
6670
|
return { action: "noop" };
|
|
6402
6671
|
}),
|
|
6403
|
-
reconcile:
|
|
6404
|
-
return yield* createPool2(news, (props) => readPool2(props).pipe(
|
|
6672
|
+
reconcile: Effect97.fn(function* ({ news }) {
|
|
6673
|
+
return yield* createPool2(news, (props) => readPool2(props).pipe(Effect97.map(dropUnreadable8)));
|
|
6405
6674
|
}),
|
|
6406
|
-
delete:
|
|
6675
|
+
delete: Effect97.fn(function* ({ olds }) {
|
|
6407
6676
|
yield* destroyPool(olds);
|
|
6408
6677
|
})
|
|
6409
6678
|
})));
|
|
@@ -6483,5 +6752,5 @@ export {
|
|
|
6483
6752
|
violations
|
|
6484
6753
|
};
|
|
6485
6754
|
|
|
6486
|
-
//# debugId=
|
|
6755
|
+
//# debugId=B5C51EB76E4323A364756E2164756E21
|
|
6487
6756
|
//# sourceMappingURL=index.js.map
|