@homeflare/alchemy 0.45.0 → 0.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/proxmox/generated/constraints/index.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-access.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-cluster-backup.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-cluster-ceph.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-cluster-firewall.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-cluster-ha.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-cluster-metrics.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-cluster-notifications.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-cluster-replication.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-cluster-sdn.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-nodes-ceph.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-nodes-disks.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-nodes-lxc.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-nodes-network.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-nodes-qemu.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-nodes-qemu.d.ts.map +1 -1
- package/dist/proxmox/generated/constraints/pve-nodes-storage.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-pools.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-storage.d.ts +1 -1
- package/dist/proxmox/index.d.ts +1 -0
- package/dist/proxmox/index.d.ts.map +1 -1
- package/dist/proxmox/index.js +283 -130
- package/dist/proxmox/index.js.map +31 -27
- package/dist/proxmox/qemu-dns.d.ts +2 -0
- package/dist/proxmox/qemu-dns.d.ts.map +1 -0
- package/dist/proxmox/qemu-errors.d.ts +1 -0
- package/dist/proxmox/qemu-errors.d.ts.map +1 -1
- package/dist/proxmox/qemu-form.d.ts.map +1 -1
- package/dist/proxmox/qemu-judge.d.ts.map +1 -1
- package/dist/proxmox/qemu-lifecycle.d.ts +15 -10
- package/dist/proxmox/qemu-lifecycle.d.ts.map +1 -1
- package/dist/proxmox/qemu-props.d.ts +10 -0
- package/dist/proxmox/qemu-props.d.ts.map +1 -1
- package/dist/proxmox/qemu-resize.d.ts +10 -0
- package/dist/proxmox/qemu-resize.d.ts.map +1 -0
- package/dist/proxmox/qemu-size.d.ts +20 -0
- package/dist/proxmox/qemu-size.d.ts.map +1 -0
- package/dist/proxmox/qemu-spec.d.ts +25 -0
- package/dist/proxmox/qemu-spec.d.ts.map +1 -0
- package/dist/verify/fake-engine.d.ts +4 -2
- package/dist/verify/fake-engine.d.ts.map +1 -1
- package/docs/proxmox-qemu-resize.md +62 -0
- package/package.json +1 -1
- package/src/proxmox/generated/constraints/index.ts +1 -1
- package/src/proxmox/generated/constraints/pve-access.ts +1 -1
- package/src/proxmox/generated/constraints/pve-cluster-backup.ts +1 -1
- package/src/proxmox/generated/constraints/pve-cluster-ceph.ts +1 -1
- package/src/proxmox/generated/constraints/pve-cluster-firewall.ts +1 -1
- package/src/proxmox/generated/constraints/pve-cluster-ha.ts +1 -1
- package/src/proxmox/generated/constraints/pve-cluster-metrics.ts +1 -1
- package/src/proxmox/generated/constraints/pve-cluster-notifications.ts +1 -1
- package/src/proxmox/generated/constraints/pve-cluster-replication.ts +1 -1
- package/src/proxmox/generated/constraints/pve-cluster-sdn.ts +1 -1
- package/src/proxmox/generated/constraints/pve-nodes-ceph.ts +1 -1
- package/src/proxmox/generated/constraints/pve-nodes-disks.ts +1 -1
- package/src/proxmox/generated/constraints/pve-nodes-lxc.ts +1 -1
- package/src/proxmox/generated/constraints/pve-nodes-network.ts +1 -1
- package/src/proxmox/generated/constraints/pve-nodes-qemu.ts +6 -1
- package/src/proxmox/generated/constraints/pve-nodes-storage.ts +1 -1
- package/src/proxmox/generated/constraints/pve-pools.ts +1 -1
- package/src/proxmox/generated/constraints/pve-storage.ts +1 -1
- package/src/proxmox/index.ts +1 -0
- package/src/proxmox/qemu-dns.test.ts +73 -0
- package/src/proxmox/qemu-dns.ts +32 -0
- package/src/proxmox/qemu-errors.ts +1 -0
- package/src/proxmox/qemu-form.ts +6 -3
- package/src/proxmox/qemu-judge.ts +2 -1
- package/src/proxmox/qemu-lifecycle.ts +28 -47
- package/src/proxmox/qemu-props.ts +14 -2
- package/src/proxmox/qemu-resize-lock.test.ts +121 -0
- package/src/proxmox/qemu-resize-recovery.test.ts +81 -0
- package/src/proxmox/qemu-resize.test.ts +199 -0
- package/src/proxmox/qemu-resize.ts +86 -0
- package/src/proxmox/qemu-size.test.ts +64 -0
- package/src/proxmox/qemu-size.ts +95 -0
- package/src/proxmox/qemu-spec.ts +44 -0
- package/src/proxmox/qemu-volume.ts +5 -5
- package/src/verify/fake-engine.ts +6 -3
package/dist/proxmox/index.js
CHANGED
|
@@ -674,6 +674,11 @@ var PVE_NODES_QEMU_CONSTRAINTS = {
|
|
|
674
674
|
vmgenid: { default: "1 (autogenerated)", pattern: "^(?:[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}|[01])\\n?$", patternSource: "(?:[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}|[01])", type: "string" },
|
|
675
675
|
vmstatestorage: { format: "pve-storage-id", type: "string" },
|
|
676
676
|
watchdog: { format: "pve-qm-watchdog", type: "string" }
|
|
677
|
+
},
|
|
678
|
+
"pve:PUT /nodes/{node}/qemu/{vmid}/resize": {
|
|
679
|
+
digest: { maxLength: 40, type: "string" },
|
|
680
|
+
disk: { enum: ["ide0", "ide1", "ide2", "ide3", "scsi0", "scsi1", "scsi2", "scsi3", "scsi4", "scsi5", "scsi6", "scsi7", "scsi8", "scsi9", "scsi10", "scsi11", "scsi12", "scsi13", "scsi14", "scsi15", "scsi16", "scsi17", "scsi18", "scsi19", "scsi20", "scsi21", "scsi22", "scsi23", "scsi24", "scsi25", "scsi26", "scsi27", "scsi28", "scsi29", "scsi30", "virtio0", "virtio1", "virtio2", "virtio3", "virtio4", "virtio5", "virtio6", "virtio7", "virtio8", "virtio9", "virtio10", "virtio11", "virtio12", "virtio13", "virtio14", "virtio15", "sata0", "sata1", "sata2", "sata3", "sata4", "sata5", "efidisk0", "tpmstate0"], required: true, type: "string" },
|
|
681
|
+
size: { pattern: "^\\+?\\d+(\\.\\d+)?[KMGT]?\\n?$", patternSource: "\\+?\\d+(\\.\\d+)?[KMGT]?", required: true, type: "string" }
|
|
677
682
|
}
|
|
678
683
|
};
|
|
679
684
|
|
|
@@ -6047,15 +6052,15 @@ var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect
|
|
|
6047
6052
|
// src/proxmox/qemu.ts
|
|
6048
6053
|
import { Resource as Resource28 } from "alchemy";
|
|
6049
6054
|
import * as Provider28 from "alchemy/Provider";
|
|
6050
|
-
import * as
|
|
6055
|
+
import * as Effect88 from "effect/Effect";
|
|
6051
6056
|
|
|
6052
6057
|
// src/proxmox/qemu-lifecycle.ts
|
|
6053
6058
|
import { isResolved as isResolved27 } from "alchemy/Diff";
|
|
6054
|
-
import * as
|
|
6055
|
-
import * as
|
|
6059
|
+
import * as nodes18 from "@distilled.cloud/proxmox/nodes";
|
|
6060
|
+
import * as Effect87 from "effect/Effect";
|
|
6056
6061
|
|
|
6057
6062
|
// src/proxmox/qemu-props.ts
|
|
6058
|
-
var NOT_CONFIG2 = new Set(["node", "target", "vmid"]);
|
|
6063
|
+
var NOT_CONFIG2 = new Set(["node", "target", "vmid", "diskSizesGiB"]);
|
|
6059
6064
|
var INDEXED2 = {
|
|
6060
6065
|
ide: 4,
|
|
6061
6066
|
ipconfig: 32,
|
|
@@ -6078,8 +6083,10 @@ var SCALAR_KEYS = new Set([
|
|
|
6078
6083
|
"cpu",
|
|
6079
6084
|
"memory",
|
|
6080
6085
|
"name",
|
|
6086
|
+
"nameserver",
|
|
6081
6087
|
"onboot",
|
|
6082
6088
|
"scsihw",
|
|
6089
|
+
"searchdomain",
|
|
6083
6090
|
"sockets"
|
|
6084
6091
|
]);
|
|
6085
6092
|
var isManagedKey2 = (key) => SCALAR_KEYS.has(key) || indexedKey2(key) !== undefined;
|
|
@@ -6098,8 +6105,28 @@ var storedConfig2 = (live) => {
|
|
|
6098
6105
|
return out;
|
|
6099
6106
|
};
|
|
6100
6107
|
|
|
6108
|
+
// src/proxmox/qemu-dns.ts
|
|
6109
|
+
import { isIP } from "node:net";
|
|
6110
|
+
var addressList2 = (value) => {
|
|
6111
|
+
const values = value.includes("\x00") ? value.split("\x00") : value.replace(/[,;]/g, " ").trimStart().split(/\s+/);
|
|
6112
|
+
while (values.at(-1) === "")
|
|
6113
|
+
values.pop();
|
|
6114
|
+
const label = "[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?";
|
|
6115
|
+
const dns = new RegExp(`^(?:${label}\\.)*${label}(?![\\s\\S])`);
|
|
6116
|
+
return values.every((value2) => isIP(value2) !== 0 && !value2.includes("%") || dns.test(value2));
|
|
6117
|
+
};
|
|
6118
|
+
var dnsRefusals = (nameserver) => {
|
|
6119
|
+
if (nameserver === undefined)
|
|
6120
|
+
return [];
|
|
6121
|
+
const rule = constraintsFor("pve:PUT /nodes/{node}/qemu/{vmid}/config")["nameserver"];
|
|
6122
|
+
return rule?.format === "address-list" && typeof nameserver === "string" && addressList2(nameserver) ? [] : ["nameserver: must satisfy the vendor address-list format"];
|
|
6123
|
+
};
|
|
6124
|
+
|
|
6101
6125
|
// src/proxmox/qemu-form.ts
|
|
6102
|
-
var formRefusals = (props) =>
|
|
6126
|
+
var formRefusals = (props) => [
|
|
6127
|
+
...declaredKeys2(props).filter((key) => !isManagedKey2(key)).map((key) => `${key}: not a config key this resource manages (vmid ${String(props.vmid)}).`),
|
|
6128
|
+
...dnsRefusals(props.nameserver)
|
|
6129
|
+
];
|
|
6103
6130
|
var form = (props) => {
|
|
6104
6131
|
const out = {};
|
|
6105
6132
|
for (const key of declaredKeys2(props)) {
|
|
@@ -6117,6 +6144,7 @@ var updateForm7 = form;
|
|
|
6117
6144
|
|
|
6118
6145
|
// src/proxmox/qemu-errors.ts
|
|
6119
6146
|
class QemuRefusedError extends Error {
|
|
6147
|
+
_tag = "QemuRefusedError";
|
|
6120
6148
|
constructor(message) {
|
|
6121
6149
|
super(message);
|
|
6122
6150
|
this.name = "QemuRefusedError";
|
|
@@ -6180,7 +6208,7 @@ var judgeDisk = (key, declared2, live) => {
|
|
|
6180
6208
|
}
|
|
6181
6209
|
if (want.size !== undefined && have.size !== undefined && want.size !== have.size) {
|
|
6182
6210
|
return {
|
|
6183
|
-
refuse: `${key}: declared size ${want.size} differs from the live ${have.size}. This resource does ` + "not resize a disk
|
|
6211
|
+
refuse: `${key}: declared size ${want.size} differs from the live ${have.size}. This resource does ` + "not resize a disk through config -- omit size= and declare a diskSizesGiB target instead."
|
|
6184
6212
|
};
|
|
6185
6213
|
}
|
|
6186
6214
|
if (sameMap(want.options, have.options))
|
|
@@ -6234,11 +6262,72 @@ var netWrite2 = (declared2, live) => {
|
|
|
6234
6262
|
return [...want].map(([k, v]) => v === "" ? k : `${k}=${v}`).join(",");
|
|
6235
6263
|
};
|
|
6236
6264
|
|
|
6265
|
+
// src/proxmox/qemu-size.ts
|
|
6266
|
+
import * as Data4 from "effect/Data";
|
|
6267
|
+
import * as Effect83 from "effect/Effect";
|
|
6268
|
+
class QemuDiskResizeRefused extends Data4.TaggedError("QemuDiskResizeRefused") {
|
|
6269
|
+
}
|
|
6270
|
+
var diskBytes = (config7) => {
|
|
6271
|
+
const size = config7 === undefined ? undefined : parseDisk(config7).size;
|
|
6272
|
+
const match = size === undefined ? null : /^(\d+(?:\.\d+)?)([KMGT])?$/.exec(size);
|
|
6273
|
+
if (match === null)
|
|
6274
|
+
return;
|
|
6275
|
+
const scale = { K: 1024, M: 1024 ** 2, G: 1024 ** 3, T: 1024 ** 4 };
|
|
6276
|
+
const bytes2 = Number(match[1]) * (scale[match[2]] ?? 1);
|
|
6277
|
+
return Number.isFinite(bytes2) && bytes2 > 0 ? bytes2 : undefined;
|
|
6278
|
+
};
|
|
6279
|
+
var validateDiskSizes = (props, olds) => Effect83.gen(function* () {
|
|
6280
|
+
const sizes = props.diskSizesGiB;
|
|
6281
|
+
if (sizes === undefined)
|
|
6282
|
+
return;
|
|
6283
|
+
if (sizes === null || typeof sizes !== "object" || Array.isArray(sizes)) {
|
|
6284
|
+
return yield* Effect83.fail(new QemuDiskResizeRefused({
|
|
6285
|
+
disk: "diskSizesGiB",
|
|
6286
|
+
message: "diskSizesGiB must be a map of disk slots to GiB targets"
|
|
6287
|
+
}));
|
|
6288
|
+
}
|
|
6289
|
+
for (const [disk, size] of Object.entries(sizes)) {
|
|
6290
|
+
if (size === undefined)
|
|
6291
|
+
continue;
|
|
6292
|
+
const family = indexedKey2(disk)?.[0];
|
|
6293
|
+
const value = declaredValue2(props, disk);
|
|
6294
|
+
const previous = olds?.diskSizesGiB?.[disk];
|
|
6295
|
+
let reason;
|
|
6296
|
+
if (family !== "scsi" && family !== "ide" || typeof value !== "string") {
|
|
6297
|
+
reason = "declare a supported scsi/ide disk slot before assigning a resize target";
|
|
6298
|
+
} else if (!Number.isSafeInteger(size) || size <= 0 || !Number.isSafeInteger(size * 1024 ** 3)) {
|
|
6299
|
+
reason = "the target must be a positive, safely representable integer GiB size";
|
|
6300
|
+
} else if (value.split(",")[0]?.endsWith(":cloudinit") === true || parseDisk(value).options.get("media") === "cdrom") {
|
|
6301
|
+
reason = "cloud-init and CD-ROM drives cannot be resized";
|
|
6302
|
+
} else if (previous !== undefined && size < previous) {
|
|
6303
|
+
reason = `refusing shrink from declared ${previous} GiB to ${size} GiB`;
|
|
6304
|
+
}
|
|
6305
|
+
if (reason !== undefined) {
|
|
6306
|
+
return yield* Effect83.fail(new QemuDiskResizeRefused({ disk, message: `${disk}: ${reason}` }));
|
|
6307
|
+
}
|
|
6308
|
+
}
|
|
6309
|
+
});
|
|
6310
|
+
var diskSizeDrift = (props, config7) => Object.entries(props.diskSizesGiB ?? {}).filter(([disk, size]) => size !== undefined && (diskBytes(config7[disk]) ?? 0) < size * 1024 ** 3).map(([disk]) => `diskSizesGiB.${disk}`);
|
|
6311
|
+
var checkLiveDiskSizes = (props, config7, allowMissing = false) => Effect83.gen(function* () {
|
|
6312
|
+
for (const [disk, size] of Object.entries(props.diskSizesGiB ?? {})) {
|
|
6313
|
+
if (size === undefined)
|
|
6314
|
+
continue;
|
|
6315
|
+
if (allowMissing && config7[disk] === undefined)
|
|
6316
|
+
continue;
|
|
6317
|
+
if (diskBytes(config7[disk]) === undefined || parseDisk(config7[disk] ?? "").options.get("media") === "cdrom") {
|
|
6318
|
+
return yield* Effect83.fail(new QemuDiskResizeRefused({
|
|
6319
|
+
disk,
|
|
6320
|
+
message: `${disk}: the live disk has no readable positive size or is a CD-ROM`
|
|
6321
|
+
}));
|
|
6322
|
+
}
|
|
6323
|
+
}
|
|
6324
|
+
});
|
|
6325
|
+
|
|
6237
6326
|
// src/proxmox/qemu-judge.ts
|
|
6238
6327
|
var judge2 = (props, config7) => {
|
|
6239
6328
|
const put = {};
|
|
6240
6329
|
const refuse3 = [];
|
|
6241
|
-
const drift = new Set;
|
|
6330
|
+
const drift = new Set(diskSizeDrift(props, config7));
|
|
6242
6331
|
for (const key of declaredKeys2(props)) {
|
|
6243
6332
|
const want = wireValue2(declaredValue2(props, key));
|
|
6244
6333
|
const have = config7[key];
|
|
@@ -6281,42 +6370,42 @@ var identityRefusal = (vmid, was) => was === undefined || vmid === was ? undefin
|
|
|
6281
6370
|
// src/proxmox/qemu-read.ts
|
|
6282
6371
|
import * as cluster11 from "@distilled.cloud/proxmox/cluster";
|
|
6283
6372
|
import * as nodes15 from "@distilled.cloud/proxmox/nodes";
|
|
6284
|
-
import * as
|
|
6373
|
+
import * as Effect84 from "effect/Effect";
|
|
6285
6374
|
var guestTypes = ["lxc", "qemu"];
|
|
6286
|
-
var readVm = (props) =>
|
|
6375
|
+
var readVm = (props) => Effect84.gen(function* () {
|
|
6287
6376
|
const credential = yield* leased(props.target, "read");
|
|
6288
|
-
const answer = yield* runPveWith(props.target, credential, false, nodes15.getNodeQemuConfig({ node: props.node, vmid: String(props.vmid) })).pipe(
|
|
6377
|
+
const answer = yield* runPveWith(props.target, credential, false, nodes15.getNodeQemuConfig({ node: props.node, vmid: String(props.vmid) })).pipe(Effect84.map((data) => ({ data, error: undefined })), Effect84.catchTag("QemuConfigNotFound", (error) => Effect84.succeed({ data: undefined, error })));
|
|
6289
6378
|
if (answer.error === undefined) {
|
|
6290
6379
|
const live = answer.data;
|
|
6291
6380
|
if (typeof live.digest !== "string" || live.digest === "") {
|
|
6292
|
-
return yield*
|
|
6381
|
+
return yield* Effect84.fail(new QemuRefusedError(`VM ${String(props.vmid)}: config read did not return a digest-bearing config.`));
|
|
6293
6382
|
}
|
|
6294
6383
|
return live;
|
|
6295
6384
|
}
|
|
6296
6385
|
const rows = yield* runPveWith(props.target, credential, false, cluster11.listClusterResources({ type: "vm" }));
|
|
6297
6386
|
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))) {
|
|
6298
|
-
return yield*
|
|
6387
|
+
return yield* Effect84.fail(new QemuRefusedError("The cluster guest index did not identify its rows."));
|
|
6299
6388
|
}
|
|
6300
6389
|
const found = rows.find((row) => Number(row.vmid) === props.vmid);
|
|
6301
6390
|
if (found === undefined)
|
|
6302
6391
|
return;
|
|
6303
6392
|
if (found.node === props.node && found.type === "qemu")
|
|
6304
|
-
return yield*
|
|
6305
|
-
return yield*
|
|
6393
|
+
return yield* Effect84.fail(answer.error);
|
|
6394
|
+
return yield* Effect84.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."));
|
|
6306
6395
|
});
|
|
6307
6396
|
|
|
6308
6397
|
// src/proxmox/qemu-task.ts
|
|
6309
6398
|
import * as nodes16 from "@distilled.cloud/proxmox/nodes";
|
|
6310
|
-
import * as
|
|
6311
|
-
var qemuTask = (props, operation, what, polls) =>
|
|
6399
|
+
import * as Effect85 from "effect/Effect";
|
|
6400
|
+
var qemuTask = (props, operation, what, polls) => Effect85.gen(function* () {
|
|
6312
6401
|
const credential = yield* mint(props.target, "provision");
|
|
6313
6402
|
const upid = text(yield* runPveWith(props.target, credential, true, operation));
|
|
6314
6403
|
if (upid === "") {
|
|
6315
|
-
return yield*
|
|
6404
|
+
return yield* Effect85.fail(new QemuRefusedError(`${what}: PVE returned no task id, so the write cannot be confirmed and is not claimed.`));
|
|
6316
6405
|
}
|
|
6317
6406
|
let exit = "still running when this provider stopped waiting";
|
|
6318
6407
|
for (let attempt = 0;attempt < polls; attempt++) {
|
|
6319
|
-
const status = yield* runPveWith(props.target, credential, false, nodes16.getNodeTaskStatus({ node: props.node, upid })).pipe(
|
|
6408
|
+
const status = yield* runPveWith(props.target, credential, false, nodes16.getNodeTaskStatus({ node: props.node, upid })).pipe(Effect85.orElseSucceed(() => {
|
|
6320
6409
|
return;
|
|
6321
6410
|
}));
|
|
6322
6411
|
if (status === undefined || status.status === undefined) {
|
|
@@ -6327,16 +6416,14 @@ var qemuTask = (props, operation, what, polls) => Effect84.gen(function* () {
|
|
|
6327
6416
|
exit = text(status.exitstatus, "stopped with no exitstatus");
|
|
6328
6417
|
break;
|
|
6329
6418
|
}
|
|
6330
|
-
yield*
|
|
6419
|
+
yield* Effect85.sleep("1 second");
|
|
6331
6420
|
}
|
|
6332
6421
|
if (exit !== "OK") {
|
|
6333
|
-
return yield*
|
|
6422
|
+
return yield* Effect85.fail(new QemuRefusedError(`${what}: task ${upid} ended "${exit}". Read the task log before running the deploy again.`));
|
|
6334
6423
|
}
|
|
6335
6424
|
});
|
|
6336
6425
|
|
|
6337
|
-
// src/proxmox/qemu-
|
|
6338
|
-
var CREATE_POLLS2 = 180;
|
|
6339
|
-
var DELETE_POLLS = 60;
|
|
6426
|
+
// src/proxmox/qemu-spec.ts
|
|
6340
6427
|
var attributesOf10 = (live, props) => ({
|
|
6341
6428
|
config: storedConfig2(live),
|
|
6342
6429
|
node: props.node,
|
|
@@ -6356,28 +6443,88 @@ var spec5 = {
|
|
|
6356
6443
|
updateForm: updateForm7
|
|
6357
6444
|
};
|
|
6358
6445
|
var { guardCreate: guardCreate10, guardUpdate: guardUpdate10 } = specGuards(spec5);
|
|
6359
|
-
|
|
6360
|
-
|
|
6446
|
+
|
|
6447
|
+
// src/proxmox/qemu-resize.ts
|
|
6448
|
+
import * as nodes17 from "@distilled.cloud/proxmox/nodes";
|
|
6449
|
+
import * as Effect86 from "effect/Effect";
|
|
6450
|
+
var RESIZE_POLLS = 60;
|
|
6451
|
+
var unlockedConfig = (props, disk) => Effect86.gen(function* () {
|
|
6452
|
+
for (let attempt = 0;; attempt++) {
|
|
6453
|
+
const live = yield* readVm(props);
|
|
6454
|
+
if (live === undefined)
|
|
6455
|
+
return yield* Effect86.fail(new QemuDiskResizeRefused({ disk, message: "VM disappeared before resize" }));
|
|
6456
|
+
if (live.lock === undefined)
|
|
6457
|
+
return {
|
|
6458
|
+
config: storedConfig2(live),
|
|
6459
|
+
polls: RESIZE_POLLS - attempt
|
|
6460
|
+
};
|
|
6461
|
+
if (attempt === RESIZE_POLLS - 1)
|
|
6462
|
+
return yield* Effect86.fail(new QemuDiskResizeRefused({
|
|
6463
|
+
disk,
|
|
6464
|
+
message: `${disk}: VM ${props.vmid} remains locked (${live.lock}) after ${RESIZE_POLLS} observations; refusing resize`
|
|
6465
|
+
}));
|
|
6466
|
+
yield* Effect86.sleep("1 second");
|
|
6467
|
+
}
|
|
6468
|
+
});
|
|
6469
|
+
var resizeDisks = (props) => Effect86.gen(function* () {
|
|
6470
|
+
const targets = Object.entries(props.diskSizesGiB ?? {}).filter((entry) => entry[1] !== undefined);
|
|
6471
|
+
const first = targets[0];
|
|
6472
|
+
if (first === undefined)
|
|
6473
|
+
return;
|
|
6474
|
+
const live = yield* readVm(props);
|
|
6475
|
+
if (live === undefined)
|
|
6476
|
+
return yield* Effect86.fail(new QemuDiskResizeRefused({
|
|
6477
|
+
disk: first[0],
|
|
6478
|
+
message: "VM disappeared before resize"
|
|
6479
|
+
}));
|
|
6480
|
+
const observed = storedConfig2(live);
|
|
6481
|
+
yield* checkLiveDiskSizes(props, observed);
|
|
6482
|
+
for (const [disk, size] of targets) {
|
|
6483
|
+
if ((diskBytes(observed[disk]) ?? 0) >= size * 1024 ** 3)
|
|
6484
|
+
continue;
|
|
6485
|
+
const { config: config7, polls } = yield* unlockedConfig(props, disk);
|
|
6486
|
+
yield* checkLiveDiskSizes(props, config7);
|
|
6487
|
+
if ((diskBytes(config7[disk]) ?? 0) >= size * 1024 ** 3)
|
|
6488
|
+
continue;
|
|
6489
|
+
const form2 = { disk, size: `${size}G` };
|
|
6490
|
+
const refused2 = formViolations("pve:PUT /nodes/{node}/qemu/{vmid}/resize", form2, true);
|
|
6491
|
+
if (refused2.length > 0) {
|
|
6492
|
+
return yield* Effect86.fail(new QemuDiskResizeRefused({ disk, message: refused2.join(`
|
|
6493
|
+
`) }));
|
|
6494
|
+
}
|
|
6495
|
+
yield* qemuTask(props, nodes17.putNodeQemuResize({
|
|
6496
|
+
node: props.node,
|
|
6497
|
+
vmid: String(props.vmid),
|
|
6498
|
+
...form2
|
|
6499
|
+
}), `resize VM ${props.vmid} ${disk} to ${size} GiB`, polls);
|
|
6500
|
+
}
|
|
6501
|
+
});
|
|
6502
|
+
|
|
6503
|
+
// src/proxmox/qemu-lifecycle.ts
|
|
6504
|
+
var CREATE_POLLS2 = 180;
|
|
6505
|
+
var DELETE_POLLS = 60;
|
|
6506
|
+
var readQemu = (props) => readVm(props).pipe(Effect87.map((live) => live === undefined ? undefined : attributesOf10(live, props)));
|
|
6507
|
+
var deleteQemu = (props) => qemuTask(props, nodes18.deleteNodeQemu({ node: props.node, vmid: String(props.vmid) }), `destroy VM ${String(props.vmid)}`, DELETE_POLLS).pipe(Effect87.catchTag("QemuConfigNotFound", () => Effect87.void));
|
|
6361
6508
|
var refuseUnmanaged = (props) => {
|
|
6362
6509
|
const refused2 = formRefusals(props);
|
|
6363
|
-
return refused2.length === 0 ?
|
|
6510
|
+
return refused2.length === 0 ? Effect87.void : Effect87.fail(new QemuRefusedError(refused2.join(`
|
|
6364
6511
|
`)));
|
|
6365
6512
|
};
|
|
6366
|
-
var refuseChange = (change) => change.refuse.length === 0 ?
|
|
6513
|
+
var refuseChange = (change) => change.refuse.length === 0 ? Effect87.void : Effect87.fail(new QemuRefusedError(change.refuse.join(`
|
|
6367
6514
|
`)));
|
|
6368
6515
|
var qemuHandlers = {
|
|
6369
|
-
list: () =>
|
|
6370
|
-
read:
|
|
6516
|
+
list: () => Effect87.succeed([]),
|
|
6517
|
+
read: Effect87.fn(function* ({
|
|
6371
6518
|
fqn,
|
|
6372
6519
|
instanceId,
|
|
6373
6520
|
olds,
|
|
6374
6521
|
output
|
|
6375
6522
|
}) {
|
|
6376
6523
|
const found = yield* readQemu(olds);
|
|
6377
|
-
const settled2 =
|
|
6524
|
+
const settled2 = Effect87.sync(() => found !== undefined && judge2(olds, found.config).drift.every((key) => key.startsWith("diskSizesGiB.")));
|
|
6378
6525
|
return yield* ownedRead({ fqn, instanceId, output }, found, settled2);
|
|
6379
6526
|
}),
|
|
6380
|
-
diff:
|
|
6527
|
+
diff: Effect87.fn(function* ({
|
|
6381
6528
|
news,
|
|
6382
6529
|
olds,
|
|
6383
6530
|
output
|
|
@@ -6386,23 +6533,25 @@ var qemuHandlers = {
|
|
|
6386
6533
|
return;
|
|
6387
6534
|
const identity2 = identityRefusal(news.vmid, output?.vmid ?? olds.vmid);
|
|
6388
6535
|
if (identity2 !== undefined)
|
|
6389
|
-
return yield*
|
|
6536
|
+
return yield* Effect87.fail(new QemuRefusedError(identity2));
|
|
6390
6537
|
yield* refuseUnmanaged(news);
|
|
6538
|
+
yield* validateDiskSizes(news, olds);
|
|
6391
6539
|
yield* guardCreate10(news, output === undefined);
|
|
6392
6540
|
yield* guardUpdate10(news);
|
|
6393
6541
|
if (output === undefined)
|
|
6394
6542
|
return;
|
|
6395
6543
|
const live = yield* readQemu(news);
|
|
6396
6544
|
if (live === undefined) {
|
|
6397
|
-
yield*
|
|
6545
|
+
yield* Effect87.logWarning(`Proxmox.Vm ${news.node}/${String(news.vmid)}: the cluster lists no VM with this vmid -- ` + "a deploy CREATES it again, with new, empty volumes.");
|
|
6398
6546
|
yield* guardCreate10(news, true);
|
|
6399
6547
|
return { action: "update" };
|
|
6400
6548
|
}
|
|
6549
|
+
yield* checkLiveDiskSizes(news, live.config, true);
|
|
6401
6550
|
const change = judge2(news, live.config);
|
|
6402
6551
|
yield* refuseChange(change);
|
|
6403
6552
|
return { action: change.drift.length === 0 ? "noop" : "update" };
|
|
6404
6553
|
}),
|
|
6405
|
-
reconcile:
|
|
6554
|
+
reconcile: Effect87.fn(function* ({
|
|
6406
6555
|
fqn,
|
|
6407
6556
|
instanceId,
|
|
6408
6557
|
news,
|
|
@@ -6411,28 +6560,31 @@ var qemuHandlers = {
|
|
|
6411
6560
|
}) {
|
|
6412
6561
|
const identity2 = identityRefusal(news.vmid, output?.vmid ?? olds?.vmid);
|
|
6413
6562
|
if (identity2 !== undefined)
|
|
6414
|
-
return yield*
|
|
6563
|
+
return yield* Effect87.fail(new QemuRefusedError(identity2));
|
|
6415
6564
|
yield* refuseUnmanaged(news);
|
|
6565
|
+
yield* validateDiskSizes(news, olds);
|
|
6416
6566
|
const live = yield* readQemu(news);
|
|
6417
6567
|
yield* guardCreate10(news, live === undefined);
|
|
6418
6568
|
yield* guardUpdate10(news);
|
|
6419
6569
|
if (live === undefined) {
|
|
6420
|
-
yield* qemuTask(news,
|
|
6570
|
+
yield* qemuTask(news, nodes18.createNodeQemu({ ...createForm9(news), node: news.node }), `create VM ${String(news.vmid)}`, CREATE_POLLS2);
|
|
6421
6571
|
} else {
|
|
6422
6572
|
yield* refuseTakeover({ fqn, instanceId, output }, `Proxmox.Vm VM ${String(news.vmid)} on ${news.node}`);
|
|
6573
|
+
yield* checkLiveDiskSizes(news, live.config, true);
|
|
6423
6574
|
const change = judge2(news, live.config);
|
|
6424
6575
|
yield* refuseChange(change);
|
|
6425
6576
|
if (Object.keys(change.put).length > 0) {
|
|
6426
|
-
yield* runPve(news.target, "provision", true,
|
|
6577
|
+
yield* runPve(news.target, "provision", true, nodes18.putNodeQemuConfig({ ...change.put, node: news.node, vmid: String(news.vmid) }));
|
|
6427
6578
|
}
|
|
6428
6579
|
}
|
|
6580
|
+
yield* resizeDisks(news);
|
|
6429
6581
|
const after = yield* readQemu(news);
|
|
6430
6582
|
if (after === undefined) {
|
|
6431
|
-
return yield*
|
|
6583
|
+
return yield* Effect87.fail(new QemuRefusedError(`${spec5.path(news)}: write returned success but the VM is still absent`));
|
|
6432
6584
|
}
|
|
6433
6585
|
const left = judge2(news, after.config);
|
|
6434
6586
|
if (left.drift.length > 0) {
|
|
6435
|
-
return yield*
|
|
6587
|
+
return yield* Effect87.fail(new QemuRefusedError(`${spec5.path(news)}: after the write, ${left.drift.join(", ")} still differ from the ` + "declaration. PVE may have normalised the value -- declare what it stores."));
|
|
6436
6588
|
}
|
|
6437
6589
|
return after;
|
|
6438
6590
|
}),
|
|
@@ -6441,16 +6593,16 @@ var qemuHandlers = {
|
|
|
6441
6593
|
|
|
6442
6594
|
// src/proxmox/qemu.ts
|
|
6443
6595
|
var ProxmoxVm = Resource28("Proxmox.Vm", { defaultRemovalPolicy: "retain" });
|
|
6444
|
-
var ProxmoxVmProvider = () => Provider28.effect(ProxmoxVm,
|
|
6596
|
+
var ProxmoxVmProvider = () => Provider28.effect(ProxmoxVm, Effect88.succeed(ProxmoxVm.Provider.of(qemuHandlers)));
|
|
6445
6597
|
// src/proxmox/storage-download.ts
|
|
6446
6598
|
import { Resource as Resource29 } from "alchemy";
|
|
6447
6599
|
import * as Provider29 from "alchemy/Provider";
|
|
6448
|
-
import * as
|
|
6600
|
+
import * as Effect91 from "effect/Effect";
|
|
6449
6601
|
|
|
6450
6602
|
// src/proxmox/storage-download-lifecycle.ts
|
|
6451
6603
|
import { isResolved as isResolved28 } from "alchemy/Diff";
|
|
6452
|
-
import * as
|
|
6453
|
-
import * as
|
|
6604
|
+
import * as nodes20 from "@distilled.cloud/proxmox/nodes";
|
|
6605
|
+
import * as Effect90 from "effect/Effect";
|
|
6454
6606
|
|
|
6455
6607
|
// src/proxmox/storage-download-identity.ts
|
|
6456
6608
|
var PINNED = [
|
|
@@ -6484,17 +6636,17 @@ class StorageDownloadRefusedError extends Error {
|
|
|
6484
6636
|
}
|
|
6485
6637
|
|
|
6486
6638
|
// src/proxmox/storage-download-task.ts
|
|
6487
|
-
import * as
|
|
6488
|
-
import * as
|
|
6489
|
-
var storageDownloadTask = (where, operation, what, polls) =>
|
|
6639
|
+
import * as nodes19 from "@distilled.cloud/proxmox/nodes";
|
|
6640
|
+
import * as Effect89 from "effect/Effect";
|
|
6641
|
+
var storageDownloadTask = (where, operation, what, polls) => Effect89.gen(function* () {
|
|
6490
6642
|
const credential = yield* mint(where.target, "provision");
|
|
6491
6643
|
const upid = text(yield* runPveWith(where.target, credential, true, operation));
|
|
6492
6644
|
if (upid === "") {
|
|
6493
|
-
return yield*
|
|
6645
|
+
return yield* Effect89.fail(new StorageDownloadRefusedError(`${what}: PVE returned no task id, so the write cannot be confirmed and is not claimed.`));
|
|
6494
6646
|
}
|
|
6495
6647
|
let exit = "still running when this provider stopped waiting";
|
|
6496
6648
|
for (let attempt = 0;attempt < polls; attempt++) {
|
|
6497
|
-
const status = yield* runPveWith(where.target, credential, false,
|
|
6649
|
+
const status = yield* runPveWith(where.target, credential, false, nodes19.getNodeTaskStatus({ node: where.node, upid })).pipe(Effect89.orElseSucceed(() => {
|
|
6498
6650
|
return;
|
|
6499
6651
|
}));
|
|
6500
6652
|
if (status === undefined || status.status === undefined) {
|
|
@@ -6505,10 +6657,10 @@ var storageDownloadTask = (where, operation, what, polls) => Effect87.gen(functi
|
|
|
6505
6657
|
exit = text(status.exitstatus, "stopped with no exitstatus");
|
|
6506
6658
|
break;
|
|
6507
6659
|
}
|
|
6508
|
-
yield*
|
|
6660
|
+
yield* Effect89.sleep("1 second");
|
|
6509
6661
|
}
|
|
6510
6662
|
if (exit !== "OK") {
|
|
6511
|
-
return yield*
|
|
6663
|
+
return yield* Effect89.fail(new StorageDownloadRefusedError(`${what}: task ${upid} ended "${exit}". Read the task log before running the deploy again.`));
|
|
6512
6664
|
}
|
|
6513
6665
|
});
|
|
6514
6666
|
|
|
@@ -6534,14 +6686,14 @@ var createRefusals2 = (props) => {
|
|
|
6534
6686
|
}
|
|
6535
6687
|
return refuse3.concat(formViolations(DOWNLOAD_ENDPOINT, downloadForm(props), true).map((why) => `${props.filename}: ${why}`));
|
|
6536
6688
|
};
|
|
6537
|
-
var readDownload = (props) =>
|
|
6538
|
-
const rows = yield* runPve(props.target, "read", false,
|
|
6689
|
+
var readDownload = (props) => Effect90.gen(function* () {
|
|
6690
|
+
const rows = yield* runPve(props.target, "read", false, nodes20.listNodeStorageContent({
|
|
6539
6691
|
content: "import",
|
|
6540
6692
|
node: props.node,
|
|
6541
6693
|
storage: props.storage
|
|
6542
6694
|
}));
|
|
6543
6695
|
if (!Array.isArray(rows)) {
|
|
6544
|
-
return yield*
|
|
6696
|
+
return yield* Effect90.fail(new StorageDownloadRefusedError(`${props.storage}: the content list did not come back as a list.`));
|
|
6545
6697
|
}
|
|
6546
6698
|
const found = rows.find((row) => row.volid.endsWith(`/${props.filename}`));
|
|
6547
6699
|
if (found === undefined)
|
|
@@ -6555,28 +6707,28 @@ var readDownload = (props) => Effect88.gen(function* () {
|
|
|
6555
6707
|
volid: text(found.volid, "")
|
|
6556
6708
|
};
|
|
6557
6709
|
});
|
|
6558
|
-
var destroyDownload = (props) =>
|
|
6710
|
+
var destroyDownload = (props) => Effect90.gen(function* () {
|
|
6559
6711
|
const live = yield* readDownload(props);
|
|
6560
6712
|
if (live === undefined)
|
|
6561
6713
|
return;
|
|
6562
|
-
yield* storageDownloadTask(props,
|
|
6714
|
+
yield* storageDownloadTask(props, nodes20.deleteNodeStorageContent({
|
|
6563
6715
|
node: props.node,
|
|
6564
6716
|
storage: props.storage,
|
|
6565
6717
|
volume: live.volid
|
|
6566
6718
|
}), `delete ${live.volid}`, DELETE_POLLS2);
|
|
6567
6719
|
});
|
|
6568
6720
|
var storageDownloadHandlers = {
|
|
6569
|
-
list: () =>
|
|
6570
|
-
read:
|
|
6721
|
+
list: () => Effect90.succeed([]),
|
|
6722
|
+
read: Effect90.fn(function* ({
|
|
6571
6723
|
fqn,
|
|
6572
6724
|
instanceId,
|
|
6573
6725
|
olds,
|
|
6574
6726
|
output
|
|
6575
6727
|
}) {
|
|
6576
6728
|
const found = yield* readDownload(olds);
|
|
6577
|
-
return yield* ownedRead({ fqn, instanceId, output }, found,
|
|
6729
|
+
return yield* ownedRead({ fqn, instanceId, output }, found, Effect90.succeed(true));
|
|
6578
6730
|
}),
|
|
6579
|
-
diff:
|
|
6731
|
+
diff: Effect90.fn(function* ({
|
|
6580
6732
|
news,
|
|
6581
6733
|
olds,
|
|
6582
6734
|
output
|
|
@@ -6585,7 +6737,7 @@ var storageDownloadHandlers = {
|
|
|
6585
6737
|
return;
|
|
6586
6738
|
const refused2 = createRefusals2(news);
|
|
6587
6739
|
if (refused2.length > 0) {
|
|
6588
|
-
return yield*
|
|
6740
|
+
return yield* Effect90.fail(new StorageDownloadRefusedError(refused2.join(`
|
|
6589
6741
|
`)));
|
|
6590
6742
|
}
|
|
6591
6743
|
if (output === undefined)
|
|
@@ -6595,11 +6747,11 @@ var storageDownloadHandlers = {
|
|
|
6595
6747
|
return { action: "replace" };
|
|
6596
6748
|
const live = yield* readDownload(news);
|
|
6597
6749
|
if (identity2.kind === "pin-changed" && live !== undefined) {
|
|
6598
|
-
return yield*
|
|
6750
|
+
return yield* Effect90.fail(new StorageDownloadRefusedError(identity2.refuse));
|
|
6599
6751
|
}
|
|
6600
6752
|
return { action: live === undefined ? "update" : "noop" };
|
|
6601
6753
|
}),
|
|
6602
|
-
reconcile:
|
|
6754
|
+
reconcile: Effect90.fn(function* ({
|
|
6603
6755
|
fqn,
|
|
6604
6756
|
instanceId,
|
|
6605
6757
|
news,
|
|
@@ -6607,18 +6759,18 @@ var storageDownloadHandlers = {
|
|
|
6607
6759
|
}) {
|
|
6608
6760
|
const refused2 = createRefusals2(news);
|
|
6609
6761
|
if (refused2.length > 0) {
|
|
6610
|
-
return yield*
|
|
6762
|
+
return yield* Effect90.fail(new StorageDownloadRefusedError(refused2.join(`
|
|
6611
6763
|
`)));
|
|
6612
6764
|
}
|
|
6613
6765
|
const live = yield* readDownload(news);
|
|
6614
6766
|
if (live === undefined) {
|
|
6615
|
-
yield* storageDownloadTask(news,
|
|
6767
|
+
yield* storageDownloadTask(news, nodes20.downloadNodesStorageUrl(downloadForm(news)), `download ${news.filename} onto ${news.storage}`, CREATE_POLLS3);
|
|
6616
6768
|
} else {
|
|
6617
6769
|
yield* refuseTakeover({ fqn, instanceId, output }, `Proxmox.StorageDownload ${news.filename} on ${news.storage}`);
|
|
6618
6770
|
}
|
|
6619
6771
|
const after = yield* readDownload(news);
|
|
6620
6772
|
if (after === undefined) {
|
|
6621
|
-
return yield*
|
|
6773
|
+
return yield* Effect90.fail(new StorageDownloadRefusedError(`${news.filename}: the write returned no error but ${news.storage} still has no ` + "matching content — PVE may have normalised the filename; read it back by hand."));
|
|
6622
6774
|
}
|
|
6623
6775
|
return after;
|
|
6624
6776
|
}),
|
|
@@ -6627,17 +6779,17 @@ var storageDownloadHandlers = {
|
|
|
6627
6779
|
|
|
6628
6780
|
// src/proxmox/storage-download.ts
|
|
6629
6781
|
var ProxmoxStorageDownload = Resource29("Proxmox.StorageDownload");
|
|
6630
|
-
var ProxmoxStorageDownloadProvider = () => Provider29.effect(ProxmoxStorageDownload,
|
|
6782
|
+
var ProxmoxStorageDownloadProvider = () => Provider29.effect(ProxmoxStorageDownload, Effect91.succeed(ProxmoxStorageDownload.Provider.of(storageDownloadHandlers)));
|
|
6631
6783
|
// src/proxmox/replication-job.ts
|
|
6632
6784
|
import { Resource as Resource30 } from "alchemy";
|
|
6633
6785
|
import * as Provider30 from "alchemy/Provider";
|
|
6634
|
-
import * as
|
|
6786
|
+
import * as Effect93 from "effect/Effect";
|
|
6635
6787
|
|
|
6636
6788
|
// src/proxmox/replication-job-lifecycle.ts
|
|
6637
6789
|
import { isResolved as isResolved29 } from "alchemy/Diff";
|
|
6638
6790
|
import * as cluster12 from "@distilled.cloud/proxmox/cluster";
|
|
6639
6791
|
import { ProxmoxParseError as ProxmoxParseError6 } from "@distilled.cloud/proxmox/Errors";
|
|
6640
|
-
import * as
|
|
6792
|
+
import * as Effect92 from "effect/Effect";
|
|
6641
6793
|
import * as Schema6 from "effect/Schema";
|
|
6642
6794
|
|
|
6643
6795
|
// src/proxmox/replication-job-form.ts
|
|
@@ -6698,17 +6850,17 @@ var malformed = () => new ProxmoxParseError6({
|
|
|
6698
6850
|
body: undefined,
|
|
6699
6851
|
cause: "Replication job response does not match the requested vendor identity"
|
|
6700
6852
|
});
|
|
6701
|
-
var readReplicationJob = (props) => runPve(props.target, "read", false, cluster12.getClusterReplication({ id: jobId(props) })).pipe(
|
|
6853
|
+
var readReplicationJob = (props) => runPve(props.target, "read", false, cluster12.getClusterReplication({ id: jobId(props) })).pipe(Effect92.flatMap((live) => Schema6.decodeUnknownEffect(Schema6.toType(cluster12.GetClusterReplicationResponse))({
|
|
6702
6854
|
...live,
|
|
6703
6855
|
guest: int(live.guest, Number.NaN),
|
|
6704
6856
|
jobnum: int(live.jobnum, Number.NaN),
|
|
6705
6857
|
rate: undefined
|
|
6706
|
-
}).pipe(
|
|
6707
|
-
var deleteReplicationJob = (props) => runPve(props.target, "provision", true, cluster12.deleteClusterReplication({ id: jobId(props) })).pipe(
|
|
6858
|
+
}).pipe(Effect92.mapError(malformed), Effect92.as(live))), Effect92.flatMap((live) => live.id === jobId(props) && int(live.guest, Number.NaN) === props.guest && int(live.jobnum, Number.NaN) === props.jobnum ? Effect92.succeed(replicationJobSpec.attributes({ ...live }, props)) : Effect92.fail(malformed())), Effect92.catchTag("ReplicationJobNotFound", () => Effect92.succeed(undefined)));
|
|
6859
|
+
var deleteReplicationJob = (props) => runPve(props.target, "provision", true, cluster12.deleteClusterReplication({ id: jobId(props) })).pipe(Effect92.catchTag("ReplicationJobNotFound", () => Effect92.void));
|
|
6708
6860
|
var replicationJobHandlers = {
|
|
6709
|
-
list: () =>
|
|
6861
|
+
list: () => Effect92.succeed([]),
|
|
6710
6862
|
read: ({ olds }) => readReplicationJob(olds),
|
|
6711
|
-
diff:
|
|
6863
|
+
diff: Effect92.fn(function* ({
|
|
6712
6864
|
news,
|
|
6713
6865
|
output
|
|
6714
6866
|
}) {
|
|
@@ -6725,7 +6877,7 @@ var replicationJobHandlers = {
|
|
|
6725
6877
|
}
|
|
6726
6878
|
return { action: replicationJobSpec.matches(live, news) ? "noop" : "update" };
|
|
6727
6879
|
}),
|
|
6728
|
-
reconcile:
|
|
6880
|
+
reconcile: Effect92.fn(function* ({ news }) {
|
|
6729
6881
|
const live = yield* readReplicationJob(news);
|
|
6730
6882
|
yield* guardCreate11(news, live === undefined);
|
|
6731
6883
|
yield* guardUpdate11(news);
|
|
@@ -6738,7 +6890,7 @@ var replicationJobHandlers = {
|
|
|
6738
6890
|
}
|
|
6739
6891
|
const after = yield* readReplicationJob(news);
|
|
6740
6892
|
if (after === undefined)
|
|
6741
|
-
return yield*
|
|
6893
|
+
return yield* Effect92.fail(new Error(`${replicationJobSpec.path(news)}: write returned success but the resource is still absent`));
|
|
6742
6894
|
return after;
|
|
6743
6895
|
}),
|
|
6744
6896
|
delete: ({ olds }) => deleteReplicationJob(olds)
|
|
@@ -6746,15 +6898,15 @@ var replicationJobHandlers = {
|
|
|
6746
6898
|
|
|
6747
6899
|
// src/proxmox/replication-job.ts
|
|
6748
6900
|
var ProxmoxReplicationJob = Resource30("Proxmox.ReplicationJob");
|
|
6749
|
-
var ProxmoxReplicationJobProvider = () => Provider30.effect(ProxmoxReplicationJob,
|
|
6901
|
+
var ProxmoxReplicationJobProvider = () => Provider30.effect(ProxmoxReplicationJob, Effect93.succeed(ProxmoxReplicationJob.Provider.of(replicationJobHandlers)));
|
|
6750
6902
|
// src/proxmox/sdn-apply.ts
|
|
6751
6903
|
import { Resource as Resource31 } from "alchemy";
|
|
6752
6904
|
import { isResolved as isResolved30 } from "alchemy/Diff";
|
|
6753
6905
|
import * as Provider31 from "alchemy/Provider";
|
|
6754
|
-
import * as
|
|
6906
|
+
import * as Effect95 from "effect/Effect";
|
|
6755
6907
|
|
|
6756
6908
|
// src/proxmox/sdn-apply-read.ts
|
|
6757
|
-
import * as
|
|
6909
|
+
import * as Effect94 from "effect/Effect";
|
|
6758
6910
|
var MARKED = [
|
|
6759
6911
|
"cluster/sdn/zones",
|
|
6760
6912
|
"cluster/sdn/vnets",
|
|
@@ -6763,7 +6915,7 @@ var MARKED = [
|
|
|
6763
6915
|
"cluster/sdn/route-maps"
|
|
6764
6916
|
];
|
|
6765
6917
|
var subsystemAbsent = (error) => error instanceof PveError && error.status === 501;
|
|
6766
|
-
var markedPending = (target) =>
|
|
6918
|
+
var markedPending = (target) => Effect94.all(MARKED.map((collection) => pve(target, "read", "GET", `${collection}?pending=1`).pipe(Effect94.map((rows) => (rows ?? []).filter((row) => row.state !== undefined).length), Effect94.catchIf(subsystemAbsent, () => Effect94.succeed(0)))), { concurrency: "unbounded" }).pipe(Effect94.map((counts) => counts.reduce((total, count2) => total + count2, 0)));
|
|
6767
6919
|
var fabricStaged = (staged, running) => {
|
|
6768
6920
|
if (staged === undefined && running === undefined)
|
|
6769
6921
|
return 0;
|
|
@@ -6771,9 +6923,9 @@ var fabricStaged = (staged, running) => {
|
|
|
6771
6923
|
return "inconsistent";
|
|
6772
6924
|
return staged === running ? 0 : 1;
|
|
6773
6925
|
};
|
|
6774
|
-
var fabricPending = (target) =>
|
|
6926
|
+
var fabricPending = (target) => Effect94.all(["pending", "running"].map((view) => pve(target, "read", "GET", `cluster/sdn/fabrics/all?${view}=1`).pipe(Effect94.map(canonical3), Effect94.catchIf(subsystemAbsent, () => Effect94.succeed(undefined)))), { concurrency: "unbounded" }).pipe(Effect94.flatMap(([staged, running]) => {
|
|
6775
6927
|
const verdict = fabricStaged(staged, running);
|
|
6776
|
-
return verdict === "inconsistent" ?
|
|
6928
|
+
return verdict === "inconsistent" ? Effect94.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.")) : Effect94.succeed(verdict);
|
|
6777
6929
|
}));
|
|
6778
6930
|
var canonical3 = (value) => JSON.stringify(sortDeep(value));
|
|
6779
6931
|
var sortDeep = (value) => {
|
|
@@ -6786,23 +6938,23 @@ var sortDeep = (value) => {
|
|
|
6786
6938
|
}
|
|
6787
6939
|
return value;
|
|
6788
6940
|
};
|
|
6789
|
-
var sdnPendingCount = (target) =>
|
|
6941
|
+
var sdnPendingCount = (target) => Effect94.all([markedPending(target), fabricPending(target)], { concurrency: "unbounded" }).pipe(Effect94.map(([marked, fabric]) => marked + fabric));
|
|
6790
6942
|
|
|
6791
6943
|
// src/proxmox/sdn-apply.ts
|
|
6792
6944
|
var ProxmoxSdnApply = Resource31("Proxmox.SdnApply");
|
|
6793
|
-
var read5 = (props) => sdnPendingCount(props.target).pipe(
|
|
6794
|
-
var ProxmoxSdnApplyProvider = () => Provider31.effect(ProxmoxSdnApply,
|
|
6795
|
-
list: () =>
|
|
6796
|
-
read:
|
|
6945
|
+
var read5 = (props) => sdnPendingCount(props.target).pipe(Effect95.map((pending) => ({ pending })));
|
|
6946
|
+
var ProxmoxSdnApplyProvider = () => Provider31.effect(ProxmoxSdnApply, Effect95.succeed(ProxmoxSdnApply.Provider.of({
|
|
6947
|
+
list: () => Effect95.succeed([]),
|
|
6948
|
+
read: Effect95.fn(function* ({ olds }) {
|
|
6797
6949
|
return yield* read5(olds);
|
|
6798
6950
|
}),
|
|
6799
|
-
diff:
|
|
6951
|
+
diff: Effect95.fn(function* ({ news }) {
|
|
6800
6952
|
if (!isResolved30(news))
|
|
6801
6953
|
return;
|
|
6802
6954
|
const { pending } = yield* read5(news);
|
|
6803
6955
|
return pending === 0 ? { action: "noop" } : { action: "update" };
|
|
6804
6956
|
}),
|
|
6805
|
-
reconcile:
|
|
6957
|
+
reconcile: Effect95.fn(function* ({ news }) {
|
|
6806
6958
|
const staged = yield* read5(news);
|
|
6807
6959
|
if (staged.pending === 0)
|
|
6808
6960
|
return staged;
|
|
@@ -6810,16 +6962,16 @@ var ProxmoxSdnApplyProvider = () => Provider31.effect(ProxmoxSdnApply, Effect93.
|
|
|
6810
6962
|
yield* pve(news.target, "provision", "PUT", "cluster/sdn");
|
|
6811
6963
|
const after = yield* read5(news);
|
|
6812
6964
|
if (after.pending !== 0) {
|
|
6813
|
-
return yield*
|
|
6965
|
+
return yield* Effect95.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."));
|
|
6814
6966
|
}
|
|
6815
6967
|
return after;
|
|
6816
6968
|
}),
|
|
6817
|
-
delete: () =>
|
|
6969
|
+
delete: () => Effect95.void
|
|
6818
6970
|
})));
|
|
6819
6971
|
// src/proxmox/sdn-subnet.ts
|
|
6820
6972
|
import { Resource as Resource32 } from "alchemy";
|
|
6821
6973
|
import * as Provider32 from "alchemy/Provider";
|
|
6822
|
-
import * as
|
|
6974
|
+
import * as Effect96 from "effect/Effect";
|
|
6823
6975
|
|
|
6824
6976
|
// src/proxmox/sdn-subnet-form.ts
|
|
6825
6977
|
var subnetId = (props) => `${props.zone}-${props.cidr.replace("/", "-")}`;
|
|
@@ -6879,11 +7031,11 @@ var handlers12 = pveHandlers({
|
|
|
6879
7031
|
path: (props) => `cluster/sdn/vnets/${props.vnet}/subnets/${subnetId(props)}`,
|
|
6880
7032
|
updateForm: updateForm8
|
|
6881
7033
|
});
|
|
6882
|
-
var ProxmoxSdnSubnetProvider = () => Provider32.effect(ProxmoxSdnSubnet,
|
|
7034
|
+
var ProxmoxSdnSubnetProvider = () => Provider32.effect(ProxmoxSdnSubnet, Effect96.succeed(ProxmoxSdnSubnet.Provider.of(handlers12)));
|
|
6883
7035
|
// src/proxmox/sdn-vnet.ts
|
|
6884
7036
|
import { Resource as Resource33 } from "alchemy";
|
|
6885
7037
|
import * as Provider33 from "alchemy/Provider";
|
|
6886
|
-
import * as
|
|
7038
|
+
import * as Effect97 from "effect/Effect";
|
|
6887
7039
|
var ProxmoxSdnVnet = Resource33("Proxmox.SdnVnet");
|
|
6888
7040
|
var tagOf = (value) => typeof value === "number" ? value : undefined;
|
|
6889
7041
|
var shape10 = (props) => {
|
|
@@ -6921,11 +7073,11 @@ var handlers13 = pveHandlers({
|
|
|
6921
7073
|
path: (props) => `cluster/sdn/vnets/${props.vnet}`,
|
|
6922
7074
|
updateForm: updateForm9
|
|
6923
7075
|
});
|
|
6924
|
-
var ProxmoxSdnVnetProvider = () => Provider33.effect(ProxmoxSdnVnet,
|
|
7076
|
+
var ProxmoxSdnVnetProvider = () => Provider33.effect(ProxmoxSdnVnet, Effect97.succeed(ProxmoxSdnVnet.Provider.of(handlers13)));
|
|
6925
7077
|
// src/proxmox/sdn-zone.ts
|
|
6926
7078
|
import { Resource as Resource34 } from "alchemy";
|
|
6927
7079
|
import * as Provider34 from "alchemy/Provider";
|
|
6928
|
-
import * as
|
|
7080
|
+
import * as Effect98 from "effect/Effect";
|
|
6929
7081
|
var ProxmoxSdnZone = Resource34("Proxmox.SdnZone");
|
|
6930
7082
|
var sameIpam = (live, declared2) => (live === "" ? "pve" : live) === (declared2 === undefined || declared2 === "" ? "pve" : declared2);
|
|
6931
7083
|
var body6 = (props) => ({
|
|
@@ -6978,13 +7130,13 @@ var handlers14 = pveHandlers({
|
|
|
6978
7130
|
return withClears(fields2, clear);
|
|
6979
7131
|
}
|
|
6980
7132
|
});
|
|
6981
|
-
var ProxmoxSdnZoneProvider = () => Provider34.effect(ProxmoxSdnZone,
|
|
7133
|
+
var ProxmoxSdnZoneProvider = () => Provider34.effect(ProxmoxSdnZone, Effect98.succeed(ProxmoxSdnZone.Provider.of(handlers14)));
|
|
6982
7134
|
// src/proxmox/storage.ts
|
|
6983
7135
|
import { Resource as Resource35 } from "alchemy";
|
|
6984
7136
|
import { isResolved as isResolved31 } from "alchemy/Diff";
|
|
6985
7137
|
import * as Provider35 from "alchemy/Provider";
|
|
6986
7138
|
import * as storage2 from "@distilled.cloud/proxmox/storage";
|
|
6987
|
-
import * as
|
|
7139
|
+
import * as Effect100 from "effect/Effect";
|
|
6988
7140
|
|
|
6989
7141
|
// src/proxmox/storage-plugin-options.ts
|
|
6990
7142
|
var SHARED_ACCEPTED_TYPES = new Set(["dir", "lvm", "btrfs", "esxi"]);
|
|
@@ -6992,7 +7144,7 @@ var sharedAccepted = (type) => SHARED_ACCEPTED_TYPES.has(type);
|
|
|
6992
7144
|
|
|
6993
7145
|
// src/proxmox/storage-wire.ts
|
|
6994
7146
|
import * as storage from "@distilled.cloud/proxmox/storage";
|
|
6995
|
-
import * as
|
|
7147
|
+
import * as Effect99 from "effect/Effect";
|
|
6996
7148
|
var str2 = (value, fallback = "") => typeof value === "string" ? value : fallback;
|
|
6997
7149
|
var tokens = (value) => value.split(",").map((part) => part.trim()).filter((part) => part !== "").sort().join(",");
|
|
6998
7150
|
var sameList2 = (declared2, live) => declared2 === undefined || tokens(declared2) === tokens(live);
|
|
@@ -7011,8 +7163,8 @@ var attributesOf11 = (live, props) => {
|
|
|
7011
7163
|
type: str2(data["type"], props.type)
|
|
7012
7164
|
};
|
|
7013
7165
|
};
|
|
7014
|
-
var readStorageOrFail = (props) => readOrUnreadable(runPve(props.target, "provision", false, storage.getStorage({ storage: props.storage }))).pipe(
|
|
7015
|
-
var readStorage = (props) => readStorageOrFail(props).pipe(
|
|
7166
|
+
var readStorageOrFail = (props) => readOrUnreadable(runPve(props.target, "provision", false, storage.getStorage({ storage: props.storage }))).pipe(Effect99.map((live) => live === UNREADABLE ? UNREADABLE : attributesOf11(live, props)));
|
|
7167
|
+
var readStorage = (props) => readStorageOrFail(props).pipe(Effect99.catchTag("StorageNotFound", () => Effect99.succeed(undefined)));
|
|
7016
7168
|
var dropUnreadable7 = (live) => live === UNREADABLE ? undefined : live;
|
|
7017
7169
|
var changedProps = (live, props) => ({
|
|
7018
7170
|
...sameList2(props.content, live.content) ? {} : { content: props.content },
|
|
@@ -7023,7 +7175,7 @@ var changedProps = (live, props) => ({
|
|
|
7023
7175
|
...same7(props.preallocation, live.preallocation) ? {} : { preallocation: props.preallocation },
|
|
7024
7176
|
...same7(props.comment, live.comment) ? {} : { comment: props.comment }
|
|
7025
7177
|
});
|
|
7026
|
-
var matches11 = (attributes2, props) => !sharedAccepted(props.type) && !same7(props.shared, attributes2.shared) ?
|
|
7178
|
+
var matches11 = (attributes2, props) => !sharedAccepted(props.type) && !same7(props.shared, attributes2.shared) ? Effect99.die(new Error(`storage/${props.storage}: declared shared: ${String(props.shared)}, but PVE reports ` + `${String(attributes2.shared)} and '${props.type}' storages never accept a 'shared' ` + "write (storage-plugin-options.ts) -- remove the declared shared or set it to match " + "what PVE already reports; this value can never be applied.")) : Effect99.succeed(Object.keys(changedProps(attributes2, props)).length === 0);
|
|
7027
7179
|
|
|
7028
7180
|
// src/proxmox/storage-form.ts
|
|
7029
7181
|
var STORAGE_CREATE = "pve:POST /storage";
|
|
@@ -7057,12 +7209,12 @@ var toDistilledUpdate3 = (form2) => underscored(form2);
|
|
|
7057
7209
|
var ProxmoxStorage = Resource35("Proxmox.Storage", {
|
|
7058
7210
|
defaultRemovalPolicy: "retain"
|
|
7059
7211
|
});
|
|
7060
|
-
var ProxmoxStorageProvider = () => Provider35.effect(ProxmoxStorage,
|
|
7061
|
-
list: () =>
|
|
7062
|
-
read:
|
|
7212
|
+
var ProxmoxStorageProvider = () => Provider35.effect(ProxmoxStorage, Effect100.succeed(ProxmoxStorage.Provider.of({
|
|
7213
|
+
list: () => Effect100.succeed([]),
|
|
7214
|
+
read: Effect100.fn(function* ({ olds, output }) {
|
|
7063
7215
|
return dropUnreadable7(yield* output === undefined ? readStorage(olds) : readStorageOrFail(olds));
|
|
7064
7216
|
}),
|
|
7065
|
-
diff:
|
|
7217
|
+
diff: Effect100.fn(function* ({ news, output }) {
|
|
7066
7218
|
if (!isResolved31(news))
|
|
7067
7219
|
return;
|
|
7068
7220
|
yield* guardWrite2(STORAGE_CREATE, createForm11(news), output === undefined);
|
|
@@ -7080,7 +7232,7 @@ var ProxmoxStorageProvider = () => Provider35.effect(ProxmoxStorage, Effect98.su
|
|
|
7080
7232
|
}
|
|
7081
7233
|
return (yield* matches11(live, news)) ? { action: "noop" } : { action: "update" };
|
|
7082
7234
|
}),
|
|
7083
|
-
reconcile:
|
|
7235
|
+
reconcile: Effect100.fn(function* ({ news }) {
|
|
7084
7236
|
const before = dropUnreadable7(yield* readStorage(news));
|
|
7085
7237
|
yield* guardWrite2(STORAGE_CREATE, createForm11(news), before === undefined);
|
|
7086
7238
|
const update2 = updateForm10(news, before);
|
|
@@ -7092,11 +7244,11 @@ var ProxmoxStorageProvider = () => Provider35.effect(ProxmoxStorage, Effect98.su
|
|
|
7092
7244
|
}
|
|
7093
7245
|
const after = dropUnreadable7(yield* readStorage(news));
|
|
7094
7246
|
if (after === undefined) {
|
|
7095
|
-
return yield*
|
|
7247
|
+
return yield* Effect100.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."));
|
|
7096
7248
|
}
|
|
7097
7249
|
return after;
|
|
7098
7250
|
}),
|
|
7099
|
-
delete:
|
|
7251
|
+
delete: Effect100.fn(function* ({ olds }) {
|
|
7100
7252
|
yield* runPve(olds.target, "provision", true, storage2.deleteStorage({ storage: olds.storage }));
|
|
7101
7253
|
})
|
|
7102
7254
|
})));
|
|
@@ -7104,11 +7256,11 @@ var ProxmoxStorageProvider = () => Provider35.effect(ProxmoxStorage, Effect98.su
|
|
|
7104
7256
|
import { Resource as Resource36 } from "alchemy";
|
|
7105
7257
|
import { isResolved as isResolved32 } from "alchemy/Diff";
|
|
7106
7258
|
import * as Provider36 from "alchemy/Provider";
|
|
7107
|
-
import * as
|
|
7259
|
+
import * as Effect103 from "effect/Effect";
|
|
7108
7260
|
|
|
7109
7261
|
// src/proxmox/zfs-pool-wire.ts
|
|
7110
|
-
import * as
|
|
7111
|
-
import * as
|
|
7262
|
+
import * as nodes21 from "@distilled.cloud/proxmox/nodes";
|
|
7263
|
+
import * as Effect101 from "effect/Effect";
|
|
7112
7264
|
var leafDevices = (children) => Array.isArray(children) ? children.flatMap((entry) => {
|
|
7113
7265
|
const vdev = entry;
|
|
7114
7266
|
const nested = leafDevices(vdev.children);
|
|
@@ -7121,19 +7273,19 @@ var attributesOf12 = (live, props) => ({
|
|
|
7121
7273
|
node: props.node,
|
|
7122
7274
|
state: text(live.state, "unknown")
|
|
7123
7275
|
});
|
|
7124
|
-
var readPoolOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false,
|
|
7276
|
+
var readPoolOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, nodes21.getNodeDiskZfs({ name: props.name, node: props.node }))).pipe(Effect101.map((live) => {
|
|
7125
7277
|
if (live === UNREADABLE)
|
|
7126
7278
|
return UNREADABLE;
|
|
7127
7279
|
return text(live.name) === "" ? undefined : attributesOf12(live, props);
|
|
7128
7280
|
}));
|
|
7129
|
-
var readPool2 = (props) => readPoolOrFail(props).pipe(
|
|
7281
|
+
var readPool2 = (props) => readPoolOrFail(props).pipe(Effect101.orElseSucceed(() => {
|
|
7130
7282
|
return;
|
|
7131
7283
|
}));
|
|
7132
7284
|
var dropUnreadable8 = (live) => live === UNREADABLE ? undefined : live;
|
|
7133
7285
|
|
|
7134
7286
|
// src/proxmox/zfs-pool-form.ts
|
|
7135
|
-
import * as
|
|
7136
|
-
import * as
|
|
7287
|
+
import * as Effect102 from "effect/Effect";
|
|
7288
|
+
import * as nodes22 from "@distilled.cloud/proxmox/nodes";
|
|
7137
7289
|
var ZFS_POOL_CREATE = "pve:POST /nodes/{node}/disks/zfs";
|
|
7138
7290
|
var zfsPoolCreateEndpoint = (props) => props.devices === undefined || props.raidlevel === undefined ? undefined : ZFS_POOL_CREATE;
|
|
7139
7291
|
var createForm12 = (props) => ({
|
|
@@ -7153,40 +7305,40 @@ var toDistilledCreate5 = (props) => {
|
|
|
7153
7305
|
};
|
|
7154
7306
|
};
|
|
7155
7307
|
var SETTLE_ATTEMPTS = 30;
|
|
7156
|
-
var createPool2 = (props, read6) =>
|
|
7308
|
+
var createPool2 = (props, read6) => Effect102.gen(function* () {
|
|
7157
7309
|
const existing = yield* read6(props);
|
|
7158
7310
|
if (existing !== undefined)
|
|
7159
7311
|
return existing;
|
|
7160
7312
|
if (props.devices === undefined || props.raidlevel === undefined) {
|
|
7161
|
-
return yield*
|
|
7313
|
+
return yield* Effect102.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."));
|
|
7162
7314
|
}
|
|
7163
7315
|
yield* guardForm(zfsPoolCreateEndpoint(props), asForm2(createForm12(props)), true);
|
|
7164
|
-
const upid = yield* runPve(props.target, "provision", true,
|
|
7165
|
-
const settle3 = (attempts) =>
|
|
7316
|
+
const upid = yield* runPve(props.target, "provision", true, nodes22.createNodeDiskZfs(toDistilledCreate5(props)));
|
|
7317
|
+
const settle3 = (attempts) => Effect102.gen(function* () {
|
|
7166
7318
|
const live = yield* read6(props);
|
|
7167
7319
|
if (live !== undefined || attempts <= 0)
|
|
7168
7320
|
return live;
|
|
7169
|
-
yield*
|
|
7321
|
+
yield* Effect102.sleep("2 seconds");
|
|
7170
7322
|
return yield* settle3(attempts - 1);
|
|
7171
7323
|
});
|
|
7172
7324
|
const after = yield* settle3(SETTLE_ATTEMPTS);
|
|
7173
7325
|
if (after === undefined) {
|
|
7174
|
-
return yield*
|
|
7326
|
+
return yield* Effect102.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."));
|
|
7175
7327
|
}
|
|
7176
7328
|
return after;
|
|
7177
7329
|
});
|
|
7178
|
-
var destroyPool = (props) => runPve(props.target, "provision", true,
|
|
7330
|
+
var destroyPool = (props) => runPve(props.target, "provision", true, nodes22.deleteNodeDiskZfs({ name: props.name, node: props.node }));
|
|
7179
7331
|
|
|
7180
7332
|
// src/proxmox/zfs-pool.ts
|
|
7181
7333
|
var ProxmoxZfsPool = Resource36("Proxmox.ZfsPool", {
|
|
7182
7334
|
defaultRemovalPolicy: "retain"
|
|
7183
7335
|
});
|
|
7184
|
-
var ProxmoxZfsPoolProvider = () => Provider36.effect(ProxmoxZfsPool,
|
|
7185
|
-
list: () =>
|
|
7186
|
-
read:
|
|
7336
|
+
var ProxmoxZfsPoolProvider = () => Provider36.effect(ProxmoxZfsPool, Effect103.succeed(ProxmoxZfsPool.Provider.of({
|
|
7337
|
+
list: () => Effect103.succeed([]),
|
|
7338
|
+
read: Effect103.fn(function* ({ olds, output }) {
|
|
7187
7339
|
return dropUnreadable8(yield* output === undefined ? readPool2(olds) : readPoolOrFail(olds));
|
|
7188
7340
|
}),
|
|
7189
|
-
diff:
|
|
7341
|
+
diff: Effect103.fn(function* ({ news, output }) {
|
|
7190
7342
|
if (!isResolved32(news))
|
|
7191
7343
|
return;
|
|
7192
7344
|
yield* guardForm(zfsPoolCreateEndpoint(news), asForm2(createForm12(news)), output === undefined);
|
|
@@ -7203,10 +7355,10 @@ var ProxmoxZfsPoolProvider = () => Provider36.effect(ProxmoxZfsPool, Effect101.s
|
|
|
7203
7355
|
}
|
|
7204
7356
|
return { action: "noop" };
|
|
7205
7357
|
}),
|
|
7206
|
-
reconcile:
|
|
7207
|
-
return yield* createPool2(news, (props) => readPool2(props).pipe(
|
|
7358
|
+
reconcile: Effect103.fn(function* ({ news }) {
|
|
7359
|
+
return yield* createPool2(news, (props) => readPool2(props).pipe(Effect103.map(dropUnreadable8)));
|
|
7208
7360
|
}),
|
|
7209
|
-
delete:
|
|
7361
|
+
delete: Effect103.fn(function* ({ olds }) {
|
|
7210
7362
|
yield* destroyPool(olds);
|
|
7211
7363
|
})
|
|
7212
7364
|
})));
|
|
@@ -7280,6 +7432,7 @@ export {
|
|
|
7280
7432
|
ProxmoxVmProvider,
|
|
7281
7433
|
ProxmoxZfsPool,
|
|
7282
7434
|
ProxmoxZfsPoolProvider,
|
|
7435
|
+
QemuDiskResizeRefused,
|
|
7283
7436
|
alertmanagerAlertBody,
|
|
7284
7437
|
constraintsFor,
|
|
7285
7438
|
declareProvisionBaseline,
|
|
@@ -7289,5 +7442,5 @@ export {
|
|
|
7289
7442
|
violations
|
|
7290
7443
|
};
|
|
7291
7444
|
|
|
7292
|
-
//# debugId=
|
|
7445
|
+
//# debugId=D9ECAE97C44C44E264756E2164756E21
|
|
7293
7446
|
//# sourceMappingURL=index.js.map
|