@homeflare/alchemy 0.41.0 → 0.43.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/litellm/budget-errors.d.ts +36 -0
- package/dist/litellm/budget-errors.d.ts.map +1 -0
- package/dist/litellm/budget-form.d.ts +50 -0
- package/dist/litellm/budget-form.d.ts.map +1 -0
- package/dist/litellm/budget-operations.d.ts +20 -0
- package/dist/litellm/budget-operations.d.ts.map +1 -0
- package/dist/litellm/budget.d.ts +61 -0
- package/dist/litellm/budget.d.ts.map +1 -0
- package/dist/litellm/fake-litellm.d.ts +5 -0
- package/dist/litellm/fake-litellm.d.ts.map +1 -1
- package/dist/litellm/index.d.ts +2 -0
- package/dist/litellm/index.d.ts.map +1 -1
- package/dist/litellm/index.js +125 -2
- package/dist/litellm/index.js.map +9 -5
- package/dist/litellm/operations.d.ts +22 -0
- package/dist/litellm/operations.d.ts.map +1 -1
- package/dist/litellm/providers.d.ts +1 -1
- package/dist/litellm/providers.d.ts.map +1 -1
- package/dist/openbao/auth-role-form.d.ts +9 -0
- package/dist/openbao/auth-role-form.d.ts.map +1 -1
- package/dist/openbao/auth-role-wire.d.ts.map +1 -1
- package/dist/openbao/fake-engines-roles.d.ts.map +1 -1
- package/dist/openbao/index.js +9 -6
- package/dist/openbao/index.js.map +4 -4
- package/dist/postgres/connection.d.ts +20 -2
- package/dist/postgres/connection.d.ts.map +1 -1
- package/dist/postgres/database-sql.d.ts +1 -1
- package/dist/postgres/database-sql.d.ts.map +1 -1
- package/dist/postgres/database.d.ts +1 -1
- package/dist/postgres/database.d.ts.map +1 -1
- package/dist/postgres/index.d.ts +5 -3
- package/dist/postgres/index.d.ts.map +1 -1
- package/dist/postgres/index.js +124 -38
- package/dist/postgres/index.js.map +8 -7
- package/dist/postgres/providers.d.ts +4 -2
- package/dist/postgres/providers.d.ts.map +1 -1
- package/dist/postgres/psql-executor.d.ts +21 -0
- package/dist/postgres/psql-executor.d.ts.map +1 -0
- package/dist/proxmox/acl.d.ts.map +1 -1
- package/dist/proxmox/api-token.d.ts.map +1 -1
- package/dist/proxmox/ceph-flag-types.d.ts +57 -0
- package/dist/proxmox/ceph-flag-types.d.ts.map +1 -0
- package/dist/proxmox/ceph-flag-wire.d.ts +77 -0
- package/dist/proxmox/ceph-flag-wire.d.ts.map +1 -0
- package/dist/proxmox/ceph-flag.d.ts +15 -50
- package/dist/proxmox/ceph-flag.d.ts.map +1 -1
- package/dist/proxmox/ceph-osd-write.d.ts +10 -13
- package/dist/proxmox/ceph-osd-write.d.ts.map +1 -1
- package/dist/proxmox/credential-errors.d.ts +18 -4
- package/dist/proxmox/credential-errors.d.ts.map +1 -1
- package/dist/proxmox/credentials.d.ts +35 -1
- package/dist/proxmox/credentials.d.ts.map +1 -1
- package/dist/proxmox/group.d.ts.map +1 -1
- package/dist/proxmox/index.js +805 -757
- package/dist/proxmox/index.js.map +21 -19
- package/dist/proxmox/lease-cache.d.ts +67 -5
- package/dist/proxmox/lease-cache.d.ts.map +1 -1
- package/dist/proxmox/mint.d.ts +4 -2
- package/dist/proxmox/mint.d.ts.map +1 -1
- package/dist/proxmox/node-network.d.ts.map +1 -1
- package/dist/proxmox/role.d.ts.map +1 -1
- package/dist/proxmox/storage.d.ts.map +1 -1
- package/dist/proxmox/unreadable-read.d.ts +8 -1
- package/dist/proxmox/unreadable-read.d.ts.map +1 -1
- package/dist/proxmox/user.d.ts.map +1 -1
- package/dist/proxmox/zfs-pool.d.ts.map +1 -1
- package/docs/credentials.md +14 -1
- package/docs/litellm-budget.md +55 -0
- package/docs/litellm.md +7 -6
- package/docs/postgres.md +22 -0
- package/docs/proxmox-sdk-migration.md +45 -0
- package/package.json +3 -3
- package/src/litellm/budget-errors.ts +38 -0
- package/src/litellm/budget-form.ts +94 -0
- package/src/litellm/budget-operations.ts +51 -0
- package/src/litellm/budget.test.ts +105 -0
- package/src/litellm/budget.ts +127 -0
- package/src/litellm/fake-litellm.ts +50 -1
- package/src/litellm/index.ts +13 -0
- package/src/litellm/operations.ts +1 -1
- package/src/litellm/providers.ts +4 -1
- package/src/openbao/auth-role-form.test.ts +37 -0
- package/src/openbao/auth-role-form.ts +12 -1
- package/src/openbao/auth-role-wire.test.ts +1 -0
- package/src/openbao/auth-role-wire.ts +13 -1
- package/src/openbao/distilled-retry.test.ts +1 -0
- package/src/openbao/fake-engines-roles.ts +7 -1
- package/src/postgres/connection.ts +37 -3
- package/src/postgres/database-sql.ts +4 -3
- package/src/postgres/database.ts +3 -2
- package/src/postgres/index.ts +10 -3
- package/src/postgres/providers.ts +15 -2
- package/src/postgres/psql-executor.test.ts +106 -0
- package/src/postgres/psql-executor.ts +130 -0
- package/src/proxmox/acl.ts +7 -1
- package/src/proxmox/api-token.ts +7 -1
- package/src/proxmox/ceph-flag-types.ts +70 -0
- package/src/proxmox/ceph-flag-wire.test.ts +72 -0
- package/src/proxmox/ceph-flag-wire.ts +123 -0
- package/src/proxmox/ceph-flag.ts +77 -130
- package/src/proxmox/ceph-osd-write.ts +10 -13
- package/src/proxmox/credential-errors.ts +19 -5
- package/src/proxmox/credentials.test.ts +73 -1
- package/src/proxmox/credentials.ts +72 -1
- package/src/proxmox/group.ts +7 -1
- package/src/proxmox/lease-cache-mint.test.ts +70 -0
- package/src/proxmox/lease-cache.test.ts +83 -7
- package/src/proxmox/lease-cache.ts +74 -10
- package/src/proxmox/mint.ts +12 -5
- package/src/proxmox/node-network.ts +7 -1
- package/src/proxmox/role.ts +7 -1
- package/src/proxmox/storage.ts +7 -1
- package/src/proxmox/unreadable-read.ts +16 -4
- package/src/proxmox/user.ts +7 -1
- package/src/proxmox/zfs-pool.ts +7 -1
package/dist/proxmox/index.js
CHANGED
|
@@ -1025,6 +1025,20 @@ import * as Duration from "effect/Duration";
|
|
|
1025
1025
|
import * as Effect3 from "effect/Effect";
|
|
1026
1026
|
import * as Exit from "effect/Exit";
|
|
1027
1027
|
|
|
1028
|
+
// src/proxmox/credentials.ts
|
|
1029
|
+
var VALID_TIER = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
1030
|
+
var mintTier = (target, role) => {
|
|
1031
|
+
if (target.scheme !== "pve")
|
|
1032
|
+
return role;
|
|
1033
|
+
const tier = target.roles?.[role];
|
|
1034
|
+
if (tier === undefined)
|
|
1035
|
+
return role;
|
|
1036
|
+
if (!VALID_TIER.test(tier)) {
|
|
1037
|
+
throw new Error(`PveTarget.roles.${role} = ${JSON.stringify(tier)} is not a valid OpenBao tier name -- ` + 'it would be interpolated straight into "<mount>/creds/<tier>" and must be a plain name ' + '(letters, digits, ".", "_", "-", not starting with one of those and not "..").');
|
|
1038
|
+
}
|
|
1039
|
+
return tier;
|
|
1040
|
+
};
|
|
1041
|
+
|
|
1028
1042
|
// src/proxmox/mint.ts
|
|
1029
1043
|
import * as Effect2 from "effect/Effect";
|
|
1030
1044
|
import * as Option from "effect/Option";
|
|
@@ -1038,14 +1052,15 @@ import * as Data from "effect/Data";
|
|
|
1038
1052
|
|
|
1039
1053
|
class PveCredentialDenied extends Data.TaggedError("PveCredentialDenied") {
|
|
1040
1054
|
get message() {
|
|
1041
|
-
return `OpenBao GET /v1/${this.mount}/creds/${this.
|
|
1055
|
+
return `OpenBao GET /v1/${this.mount}/creds/${this.tier} -> 403: ${this.detail}. This identity's ` + "AppRole has no grant for this role -- a refused read, not evidence the object is gone.";
|
|
1042
1056
|
}
|
|
1043
1057
|
}
|
|
1044
1058
|
|
|
1045
1059
|
// src/proxmox/mint.ts
|
|
1046
1060
|
var baoVariable = (env, name) => env[`BAO_${name}`] ?? env[`VAULT_${name}`] ?? "";
|
|
1047
1061
|
var mint = (target, role, env = process.env) => Effect2.gen(function* () {
|
|
1048
|
-
const
|
|
1062
|
+
const tier = mintTier(target, role);
|
|
1063
|
+
const path = `${target.mount}/creds/${tier}`;
|
|
1049
1064
|
const refuse = (status2, detail) => new Error(`OpenBao GET /v1/${path} -> ${status2 === 0 ? "no response" : status2}: ${detail}`);
|
|
1050
1065
|
const address = baoVariable(env, "AGENT_ADDR") || baoVariable(env, "ADDR") || "https://127.0.0.1:8200";
|
|
1051
1066
|
const socket = address.startsWith("unix://") ? address.slice("unix://".length) : undefined;
|
|
@@ -1078,14 +1093,14 @@ var mint = (target, role, env = process.env) => Effect2.gen(function* () {
|
|
|
1078
1093
|
const isOpenBaoBody = Array.isArray(errors);
|
|
1079
1094
|
const detail = isOpenBaoBody ? errors.map(String).join("; ") : "not an OpenBao error body";
|
|
1080
1095
|
if (status === 403 && isOpenBaoBody) {
|
|
1081
|
-
return yield* Effect2.fail(new PveCredentialDenied({ detail, mount: target.mount,
|
|
1096
|
+
return yield* Effect2.fail(new PveCredentialDenied({ detail, mount: target.mount, tier }));
|
|
1082
1097
|
}
|
|
1083
1098
|
return yield* Effect2.fail(refuse(status, detail || "(no errors given)"));
|
|
1084
1099
|
}
|
|
1085
1100
|
const tokenId = parsed?.data?.token_id;
|
|
1086
1101
|
const secret = parsed?.data?.secret;
|
|
1087
1102
|
if (typeof tokenId !== "string" || typeof secret !== "string") {
|
|
1088
|
-
return yield* Effect2.fail(new Error(`${target.mount}/creds/${
|
|
1103
|
+
return yield* Effect2.fail(new Error(`${target.mount}/creds/${tier} returned no token_id/secret. Either this approle lacks ` + `the grant, or the mount is not ${target.mount}. Say which mount and role you needed ` + `than falling back to a stored credential -- every stored PVE token is read-only.`));
|
|
1089
1104
|
}
|
|
1090
1105
|
const lease = parsed?.lease_duration;
|
|
1091
1106
|
const leaseSeconds = typeof lease === "number" ? lease : 0;
|
|
@@ -1097,13 +1112,14 @@ var authorization = (credential, target) => target.scheme === "pbs" ? `PBSAPITok
|
|
|
1097
1112
|
var REMINT_MARGIN_SECONDS = 60;
|
|
1098
1113
|
var keyOf = (target, role) => ({
|
|
1099
1114
|
mount: target.mount,
|
|
1100
|
-
|
|
1101
|
-
|
|
1115
|
+
scheme: target.scheme,
|
|
1116
|
+
tier: mintTier(target, role)
|
|
1102
1117
|
});
|
|
1103
1118
|
var timeToLive = (exit) => Exit.isSuccess(exit) && exit.value.leaseSeconds > REMINT_MARGIN_SECONDS ? Duration.seconds(exit.value.leaseSeconds - REMINT_MARGIN_SECONDS) : Duration.zero;
|
|
1104
1119
|
var makeLeases = (mintFor) => Cache.makeWith(mintFor, { capacity: 64, requireServicesAt: "lookup", timeToLive });
|
|
1105
|
-
var targetForKey = (key) => key.scheme === "pbs" ? { api: "https://pbs.invalid/api2/json", mount: key.mount, scheme: "pbs" } : { members: [], mount: key.mount, scheme: "pve" };
|
|
1106
|
-
var
|
|
1120
|
+
var targetForKey = (key) => key.scheme === "pbs" ? { api: "https://pbs.invalid/api2/json", mount: key.mount, scheme: "pbs" } : { members: [], mount: key.mount, roles: { read: key.tier }, scheme: "pve" };
|
|
1121
|
+
var mintForKey = (key, env = process.env) => mint(targetForKey(key), key.scheme === "pbs" ? key.tier : "read", env);
|
|
1122
|
+
var leases = Effect3.runSync(makeLeases(mintForKey));
|
|
1107
1123
|
var leased = (target, role) => Cache.get(leases, keyOf(target, role));
|
|
1108
1124
|
|
|
1109
1125
|
// src/proxmox/members.ts
|
|
@@ -1233,7 +1249,7 @@ var runPve = (target, role, isWrite, op) => Effect5.gen(function* () {
|
|
|
1233
1249
|
import * as Effect6 from "effect/Effect";
|
|
1234
1250
|
var UNREADABLE = Symbol("pve-unreadable");
|
|
1235
1251
|
var readOrUnreadable = (effect) => effect.pipe(Effect6.catchTag("PveCredentialDenied", () => Effect6.succeed(UNREADABLE)));
|
|
1236
|
-
var unreadableWarning = (type, identity2) => Effect6.logWarning(`${type} ${identity2}: the read was refused (no grant for this credential's role) --
|
|
1252
|
+
var unreadableWarning = (type, identity2, mount, tier) => Effect6.logWarning(`${type} ${identity2}: the read of ${mount}/creds/${tier} was refused (no grant for this ` + "credential's role) -- reporting noop rather than forcing an update with nothing compared. " + "Re-plan on a lane that can read this object before trusting this row.");
|
|
1237
1253
|
|
|
1238
1254
|
// src/proxmox/acl.ts
|
|
1239
1255
|
var ProxmoxAcl = Resource("Proxmox.Acl");
|
|
@@ -1255,7 +1271,7 @@ var ProxmoxAclProvider = () => Provider.effect(ProxmoxAcl, Effect7.succeed(Proxm
|
|
|
1255
1271
|
return;
|
|
1256
1272
|
const live = yield* readAttributes(news);
|
|
1257
1273
|
if (live === UNREADABLE) {
|
|
1258
|
-
yield* unreadableWarning("Proxmox.Acl", identity(news));
|
|
1274
|
+
yield* unreadableWarning("Proxmox.Acl", identity(news), news.target.mount, mintTier(news.target, "provision"));
|
|
1259
1275
|
return { action: "noop" };
|
|
1260
1276
|
}
|
|
1261
1277
|
return live !== undefined && matches(live, news) ? { action: "noop" } : { action: "update" };
|
|
@@ -1425,7 +1441,7 @@ var ProxmoxApiTokenProvider = () => Provider2.effect(ProxmoxApiToken, Effect8.su
|
|
|
1425
1441
|
return;
|
|
1426
1442
|
const live = yield* readToken(news);
|
|
1427
1443
|
if (live === UNREADABLE) {
|
|
1428
|
-
yield* unreadableWarning("Proxmox.ApiToken", `${news.userid}!${news.tokenid}
|
|
1444
|
+
yield* unreadableWarning("Proxmox.ApiToken", `${news.userid}!${news.tokenid}`, news.target.mount, mintTier(news.target, "provision"));
|
|
1429
1445
|
return { action: "noop" };
|
|
1430
1446
|
}
|
|
1431
1447
|
return live !== undefined && matches2(live, news) ? { action: "noop" } : { action: "update" };
|
|
@@ -1729,143 +1745,57 @@ var ProxmoxCephDaemonProvider = () => Provider4.effect(ProxmoxCephDaemon, Effect
|
|
|
1729
1745
|
})));
|
|
1730
1746
|
// src/proxmox/ceph-flag.ts
|
|
1731
1747
|
import { Resource as Resource5 } from "alchemy";
|
|
1732
|
-
import * as Provider5 from "alchemy/Provider";
|
|
1733
|
-
import * as Effect16 from "effect/Effect";
|
|
1734
|
-
|
|
1735
|
-
// src/proxmox/resource.ts
|
|
1736
1748
|
import { isResolved as isResolved5 } from "alchemy/Diff";
|
|
1749
|
+
import * as Provider5 from "alchemy/Provider";
|
|
1737
1750
|
import * as Effect15 from "effect/Effect";
|
|
1738
1751
|
|
|
1739
|
-
// src/proxmox/
|
|
1752
|
+
// src/proxmox/ceph-flag-wire.ts
|
|
1753
|
+
import * as cluster3 from "@distilled.cloud/proxmox/cluster";
|
|
1740
1754
|
import * as Effect14 from "effect/Effect";
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
method;
|
|
1746
|
-
path;
|
|
1747
|
-
constructor(status, method, path, detail) {
|
|
1748
|
-
super(`PVE ${method} ${path} -> ${String(status)}: ${detail}`);
|
|
1749
|
-
this.status = status;
|
|
1750
|
-
this.method = method;
|
|
1751
|
-
this.path = path;
|
|
1752
|
-
this.name = "PveError";
|
|
1753
|
-
}
|
|
1754
|
-
}
|
|
1755
|
-
var clusterExhausted = (method, path, attempts) => new PveError(0, method, path, `all members failed: ${attempts.map((a) => `${a.member} (${a.why})`).join("; ")}`);
|
|
1756
|
-
var encode = (form) => {
|
|
1757
|
-
const params = new URLSearchParams;
|
|
1758
|
-
for (const [key, value] of Object.entries(form)) {
|
|
1759
|
-
if (typeof value === "string")
|
|
1760
|
-
params.append(key, value);
|
|
1761
|
-
else
|
|
1762
|
-
for (const item of value)
|
|
1763
|
-
params.append(key, item);
|
|
1764
|
-
}
|
|
1765
|
-
return params.toString();
|
|
1766
|
-
};
|
|
1767
|
-
var buildRequest = (method, apiBase, path, credential, target, form) => {
|
|
1768
|
-
const url = `${apiBase}/${path}`;
|
|
1769
|
-
return HttpClientRequest2.make(method)(url).pipe(HttpClientRequest2.setHeaders({ Authorization: authorization(credential, target) }), form === undefined ? (self) => self : HttpClientRequest2.bodyText(encode(form), "application/x-www-form-urlencoded"));
|
|
1770
|
-
};
|
|
1771
|
-
var executeOne = (target, credential, method, path, form, apiBase) => Effect14.gen(function* () {
|
|
1772
|
-
const client = yield* HttpClient5.HttpClient;
|
|
1773
|
-
const request = buildRequest(method, apiBase, path, credential, target, form);
|
|
1774
|
-
return yield* client.execute(request).pipe(Effect14.mapError((cause) => new PveError(0, method, path, String(cause))));
|
|
1775
|
-
});
|
|
1776
|
-
var readResponse = (response, method, path) => Effect14.gen(function* () {
|
|
1777
|
-
if (response.status < 200 || response.status >= 300) {
|
|
1778
|
-
const detail = yield* response.text.pipe(Effect14.orElseSucceed(() => ""));
|
|
1779
|
-
return yield* Effect14.fail(new PveError(response.status, method, path, detail.slice(0, 300)));
|
|
1780
|
-
}
|
|
1781
|
-
const body = yield* response.json.pipe(Effect14.mapError((cause) => new PveError(0, method, path, String(cause))));
|
|
1782
|
-
return body.data;
|
|
1783
|
-
});
|
|
1784
|
-
var pveWith = (target, credential, method, path, form) => Effect14.gen(function* () {
|
|
1785
|
-
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);
|
|
1786
|
-
return yield* readResponse(response, method, path);
|
|
1755
|
+
var CEPH_FLAG_UPDATE = "pve:PUT /cluster/ceph/flags/{flag}";
|
|
1756
|
+
var attributesOf3 = (live, props) => ({
|
|
1757
|
+
flag: props.flag,
|
|
1758
|
+
value: bool(live)
|
|
1787
1759
|
});
|
|
1788
|
-
var
|
|
1789
|
-
const
|
|
1790
|
-
return
|
|
1791
|
-
});
|
|
1792
|
-
|
|
1793
|
-
// src/proxmox/resource.ts
|
|
1794
|
-
var pveOperations = (spec) => {
|
|
1795
|
-
const { guardCreate: guardCreate2, guardUpdate: guardUpdate2 } = specGuards(spec);
|
|
1796
|
-
const read = (props) => pve(props.target, spec.readRole ?? "read", "GET", spec.path(props)).pipe(Effect15.map((data) => data == null ? undefined : spec.attributes(data, props)), Effect15.orElseSucceed(() => {
|
|
1797
|
-
return;
|
|
1798
|
-
}));
|
|
1799
|
-
return {
|
|
1800
|
-
guardCreate: guardCreate2,
|
|
1801
|
-
guardUpdate: guardUpdate2,
|
|
1802
|
-
read,
|
|
1803
|
-
diff: (news, output) => Effect15.gen(function* () {
|
|
1804
|
-
if (!isResolved5(news))
|
|
1805
|
-
return;
|
|
1806
|
-
yield* guardCreate2(news, output === undefined);
|
|
1807
|
-
yield* guardUpdate2(news);
|
|
1808
|
-
if (output === undefined)
|
|
1809
|
-
return;
|
|
1810
|
-
const live = yield* read(news);
|
|
1811
|
-
if (live === undefined) {
|
|
1812
|
-
yield* guardCreate2(news, true);
|
|
1813
|
-
return { action: "update" };
|
|
1814
|
-
}
|
|
1815
|
-
if (spec.matches(live, news))
|
|
1816
|
-
return { action: "noop" };
|
|
1817
|
-
return spec.updateForm === undefined ? { action: "replace" } : { action: "update" };
|
|
1818
|
-
}),
|
|
1819
|
-
reconcile: (news) => Effect15.gen(function* () {
|
|
1820
|
-
const live = yield* read(news);
|
|
1821
|
-
yield* guardCreate2(news, live === undefined);
|
|
1822
|
-
yield* guardUpdate2(news);
|
|
1823
|
-
if (live === undefined) {
|
|
1824
|
-
yield* pve(news.target, "provision", "POST", spec.collection(news), spec.createForm(news));
|
|
1825
|
-
} else if (spec.updateForm !== undefined) {
|
|
1826
|
-
const form = formToSend(spec.matches, live, news, spec.updateForm(news));
|
|
1827
|
-
if (form !== undefined) {
|
|
1828
|
-
yield* pve(news.target, "provision", "PUT", spec.path(news), form);
|
|
1829
|
-
}
|
|
1830
|
-
}
|
|
1831
|
-
const after = yield* read(news);
|
|
1832
|
-
if (after === undefined) {
|
|
1833
|
-
return yield* Effect15.die(new Error(`${spec.path(news)}: the write returned no error but the object 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."));
|
|
1834
|
-
}
|
|
1835
|
-
return after;
|
|
1836
|
-
}),
|
|
1837
|
-
destroy: (olds) => pve(olds.target, "provision", "DELETE", spec.path(olds))
|
|
1838
|
-
};
|
|
1839
|
-
};
|
|
1840
|
-
var pveHandlers = (spec) => {
|
|
1841
|
-
const ops = pveOperations(spec);
|
|
1842
|
-
return {
|
|
1843
|
-
list: () => Effect15.succeed([]),
|
|
1844
|
-
read: ({ olds }) => ops.read(olds),
|
|
1845
|
-
diff: ({ news, output }) => ops.diff(news, output),
|
|
1846
|
-
reconcile: ({ news }) => ops.reconcile(news),
|
|
1847
|
-
delete: ({ olds }) => ops.destroy(olds)
|
|
1848
|
-
};
|
|
1760
|
+
var scalarFlagOrDie = (live, props) => {
|
|
1761
|
+
const scalar = typeof live === "boolean" || live === 0 || live === 1 || live === "0" || live === "1";
|
|
1762
|
+
return scalar ? Effect14.succeed(live) : Effect14.die(new Error(`cluster/ceph/flags/${props.flag}: GET answered ${JSON.stringify(live)}, not a bare ` + "boolean. This endpoint's success is a scalar; an empty object is what the protocol " + 'substitutes for `{"data":null}`. Check `ceph osd dump | head -1` and the cluster ' + "log rather than trust a coerced `false`."));
|
|
1849
1763
|
};
|
|
1764
|
+
var readClusterCephFlag = (props) => runPve(props.target, "read", false, cluster3.getClusterCephFlag({ flag: props.flag })).pipe(Effect14.flatMap((live) => scalarFlagOrDie(live, props)), Effect14.map((live) => attributesOf3(live, props)));
|
|
1765
|
+
var readClusterCephFlagForDiff = (props) => readOrUnreadable(readClusterCephFlag(props));
|
|
1766
|
+
var updateForm = (props) => ({ value: flag(props.value) ?? "0" });
|
|
1767
|
+
var writeClusterCephFlag = (props) => runPve(props.target, "provision", true, cluster3.putClusterCephFlag({ flag: props.flag, value: updateForm(props).value }));
|
|
1850
1768
|
|
|
1851
1769
|
// src/proxmox/ceph-flag.ts
|
|
1852
1770
|
var ProxmoxCephFlag = Resource5("Proxmox.CephFlag");
|
|
1853
|
-
var
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1771
|
+
var matches3 = (attributes, props) => attributes.value === props.value;
|
|
1772
|
+
var ProxmoxCephFlagProvider = () => Provider5.effect(ProxmoxCephFlag, Effect15.succeed(ProxmoxCephFlag.Provider.of({
|
|
1773
|
+
list: () => Effect15.succeed([]),
|
|
1774
|
+
read: ({ olds }) => readClusterCephFlag(olds),
|
|
1775
|
+
diff: Effect15.fn(function* ({ news, output }) {
|
|
1776
|
+
if (!isResolved5(news))
|
|
1777
|
+
return;
|
|
1778
|
+
yield* guardWrite2(CEPH_FLAG_UPDATE, updateForm(news), false);
|
|
1779
|
+
if (output === undefined)
|
|
1780
|
+
return;
|
|
1781
|
+
const live = yield* readClusterCephFlagForDiff(news);
|
|
1782
|
+
if (live === UNREADABLE) {
|
|
1783
|
+
yield* unreadableWarning("Proxmox.CephFlag", news.flag, news.target.mount, mintTier(news.target, "read"));
|
|
1784
|
+
return { action: "noop" };
|
|
1785
|
+
}
|
|
1786
|
+
return { action: matches3(live, news) ? "noop" : "update" };
|
|
1787
|
+
}),
|
|
1788
|
+
delete: () => Effect15.void,
|
|
1789
|
+
reconcile: Effect15.fn(function* ({ news }) {
|
|
1790
|
+
const live = yield* readClusterCephFlag(news);
|
|
1791
|
+
const form = formToSend(matches3, live, news, updateForm(news));
|
|
1792
|
+
if (form !== undefined) {
|
|
1793
|
+
yield* guardWrite2(CEPH_FLAG_UPDATE, updateForm(news), false);
|
|
1794
|
+
yield* writeClusterCephFlag(news);
|
|
1795
|
+
}
|
|
1796
|
+
const after = yield* readClusterCephFlag(news);
|
|
1867
1797
|
if (after.value !== news.value) {
|
|
1868
|
-
return yield*
|
|
1798
|
+
return yield* Effect15.die(new Error(`cluster/ceph/flags/${news.flag}: the PUT returned no error but ceph still ` + `reports the flag ${after.value ? "set" : "clear"} rather than ` + `${news.value ? "set" : "clear"}. This endpoint's success is documented as a ` + "null body, so the status code proves nothing -- check `ceph osd dump | " + "head -1` and the cluster log. `pause` in particular is two ceph flags read " + "as one; see the header."));
|
|
1869
1799
|
}
|
|
1870
1800
|
return after;
|
|
1871
1801
|
})
|
|
@@ -1874,11 +1804,11 @@ var ProxmoxCephFlagProvider = () => Provider5.effect(ProxmoxCephFlag, Effect16.s
|
|
|
1874
1804
|
import { Resource as Resource6 } from "alchemy";
|
|
1875
1805
|
import { isResolved as isResolved6 } from "alchemy/Diff";
|
|
1876
1806
|
import * as Provider6 from "alchemy/Provider";
|
|
1877
|
-
import * as
|
|
1807
|
+
import * as Effect17 from "effect/Effect";
|
|
1878
1808
|
|
|
1879
1809
|
// src/proxmox/ceph-fs-distilled.ts
|
|
1880
1810
|
import * as nodes2 from "@distilled.cloud/proxmox/nodes";
|
|
1881
|
-
import * as
|
|
1811
|
+
import * as Effect16 from "effect/Effect";
|
|
1882
1812
|
|
|
1883
1813
|
// src/proxmox/ceph-fs-wire.ts
|
|
1884
1814
|
var readRow = (live, props) => {
|
|
@@ -1906,40 +1836,40 @@ var notCreated2 = (props) => new Error(`${objectPath(props)}: the create task fi
|
|
|
1906
1836
|
var notDestroyed = (props) => new Error(`${objectPath(props)}: the destroy task finished but the filesystem is still in ` + `GET nodes/${props.node}/ceph/fs. PVE refuses to destroy a CephFS while a PVE storage entry ` + "of type cephfs still references it and is not disabled.");
|
|
1907
1837
|
|
|
1908
1838
|
// src/proxmox/ceph-fs-distilled.ts
|
|
1909
|
-
var readFs = (props) => runPve(props.target, "read", false, nodes2.listNodeCephFs({ node: props.node })).pipe(
|
|
1839
|
+
var readFs = (props) => runPve(props.target, "read", false, nodes2.listNodeCephFs({ node: props.node })).pipe(Effect16.map((rows) => readRow(rows, props)));
|
|
1910
1840
|
var toDistilledCreate = (props) => ({
|
|
1911
1841
|
...createForm2(props),
|
|
1912
1842
|
name: props.name,
|
|
1913
1843
|
node: props.node
|
|
1914
1844
|
});
|
|
1915
|
-
var settleTask = (props, upid) =>
|
|
1845
|
+
var settleTask = (props, upid) => Effect16.gen(function* () {
|
|
1916
1846
|
for (let attempt = 0;attempt < POLL_ATTEMPTS; attempt += 1) {
|
|
1917
|
-
const status = yield* runPve(props.target, "read", false, nodes2.getNodeTaskStatus({ node: props.node, upid })).pipe(
|
|
1847
|
+
const status = yield* runPve(props.target, "read", false, nodes2.getNodeTaskStatus({ node: props.node, upid })).pipe(Effect16.orElseSucceed(() => {
|
|
1918
1848
|
return;
|
|
1919
1849
|
}));
|
|
1920
1850
|
if (status?.status === "stopped") {
|
|
1921
1851
|
if (status.exitstatus === "OK")
|
|
1922
1852
|
return;
|
|
1923
|
-
return yield*
|
|
1853
|
+
return yield* Effect16.die(new Error(`${objectPath(props)}: PVE task ${upid} finished with "${status.exitstatus ?? "no exit status"}". ` + "The write returned HTTP 200 because it only forked the worker -- the real error is " + `in \`pvesh get /nodes/${props.node}/tasks/${upid}/log\`.`));
|
|
1924
1854
|
}
|
|
1925
|
-
yield*
|
|
1855
|
+
yield* Effect16.sleep(POLL_SECONDS * 1000);
|
|
1926
1856
|
}
|
|
1927
|
-
return yield*
|
|
1857
|
+
return yield* Effect16.die(new Error(`${objectPath(props)}: PVE task ${upid} was still running after ` + `${String(POLL_ATTEMPTS * POLL_SECONDS)}s, or its status could not be read. Check the ` + "task log, and check that the `read` role holds Sys.Audit on " + `/nodes/${props.node} -- each call mints a new token, so the poller is never the task ` + "owner and is answered 403 rather than told to wait."));
|
|
1928
1858
|
});
|
|
1929
|
-
var createFs = (props) =>
|
|
1859
|
+
var createFs = (props) => Effect16.gen(function* () {
|
|
1930
1860
|
const upid = yield* runPve(props.target, "provision", true, nodes2.updateNodeCephFs(toDistilledCreate(props)));
|
|
1931
1861
|
if (upid === "") {
|
|
1932
|
-
return yield*
|
|
1862
|
+
return yield* Effect16.die(new Error(`${objectPath(props)}: POST returned no task id. PVE wraps every answer in ` + '{"data":...} and can report success on a call that did nothing.'));
|
|
1933
1863
|
}
|
|
1934
1864
|
yield* settleTask(props, upid);
|
|
1935
1865
|
});
|
|
1936
|
-
var destroyFs = (props) =>
|
|
1866
|
+
var destroyFs = (props) => Effect16.gen(function* () {
|
|
1937
1867
|
const upid = yield* runPve(props.target, "provision", true, nodes2.deleteNodeCephFs({
|
|
1938
1868
|
node: props.node,
|
|
1939
1869
|
name: props.name,
|
|
1940
1870
|
...props["remove-pools"] === true ? { remove_pools: "1" } : {},
|
|
1941
1871
|
...props["remove-storages"] === true ? { remove_storages: "1" } : {}
|
|
1942
|
-
})).pipe(
|
|
1872
|
+
})).pipe(Effect16.catchTag("CephFsNotFound", () => Effect16.succeed("")));
|
|
1943
1873
|
if (upid !== "")
|
|
1944
1874
|
yield* settleTask(props, upid);
|
|
1945
1875
|
});
|
|
@@ -1948,12 +1878,12 @@ var destroyFs = (props) => Effect17.gen(function* () {
|
|
|
1948
1878
|
var ProxmoxCephFs = Resource6("Proxmox.CephFs", {
|
|
1949
1879
|
defaultRemovalPolicy: "retain"
|
|
1950
1880
|
});
|
|
1951
|
-
var ProxmoxCephFsProvider = () => Provider6.effect(ProxmoxCephFs,
|
|
1952
|
-
list: () =>
|
|
1953
|
-
read:
|
|
1881
|
+
var ProxmoxCephFsProvider = () => Provider6.effect(ProxmoxCephFs, Effect17.succeed(ProxmoxCephFs.Provider.of({
|
|
1882
|
+
list: () => Effect17.succeed([]),
|
|
1883
|
+
read: Effect17.fn(function* ({ olds }) {
|
|
1954
1884
|
return yield* readFs(olds);
|
|
1955
1885
|
}),
|
|
1956
|
-
diff:
|
|
1886
|
+
diff: Effect17.fn(function* ({ news, output }) {
|
|
1957
1887
|
if (!isResolved6(news))
|
|
1958
1888
|
return;
|
|
1959
1889
|
yield* guardWrite2(CEPH_FS_CREATE, createForm2(news), output === undefined);
|
|
@@ -1969,34 +1899,34 @@ var ProxmoxCephFsProvider = () => Provider6.effect(ProxmoxCephFs, Effect18.succe
|
|
|
1969
1899
|
}
|
|
1970
1900
|
return { action: "noop" };
|
|
1971
1901
|
}),
|
|
1972
|
-
reconcile:
|
|
1902
|
+
reconcile: Effect17.fn(function* ({ news }) {
|
|
1973
1903
|
const live = yield* readFs(news);
|
|
1974
1904
|
if (live !== undefined)
|
|
1975
1905
|
return live;
|
|
1976
1906
|
yield* createFs(news);
|
|
1977
1907
|
const after = yield* readFs(news);
|
|
1978
1908
|
if (after === undefined)
|
|
1979
|
-
return yield*
|
|
1909
|
+
return yield* Effect17.die(notCreated2(news));
|
|
1980
1910
|
return after;
|
|
1981
1911
|
}),
|
|
1982
|
-
delete:
|
|
1912
|
+
delete: Effect17.fn(function* ({ olds }) {
|
|
1983
1913
|
if ((yield* readFs(olds)) === undefined)
|
|
1984
1914
|
return;
|
|
1985
1915
|
yield* destroyFs(olds);
|
|
1986
1916
|
const after = yield* readFs(olds);
|
|
1987
1917
|
if (after !== undefined)
|
|
1988
|
-
return yield*
|
|
1918
|
+
return yield* Effect17.die(notDestroyed(olds));
|
|
1989
1919
|
})
|
|
1990
1920
|
})));
|
|
1991
1921
|
// src/proxmox/ceph-osd.ts
|
|
1992
1922
|
import { Resource as Resource7 } from "alchemy";
|
|
1993
1923
|
import { isResolved as isResolved7 } from "alchemy/Diff";
|
|
1994
1924
|
import * as Provider7 from "alchemy/Provider";
|
|
1995
|
-
import * as
|
|
1925
|
+
import * as Effect20 from "effect/Effect";
|
|
1996
1926
|
|
|
1997
1927
|
// src/proxmox/ceph-osd-tree.ts
|
|
1998
1928
|
import * as nodes3 from "@distilled.cloud/proxmox/nodes";
|
|
1999
|
-
import * as
|
|
1929
|
+
import * as Effect18 from "effect/Effect";
|
|
2000
1930
|
var branches = (node) => (Array.isArray(node["children"]) ? node["children"] : []).filter((child) => typeof child === "object" && child !== null);
|
|
2001
1931
|
var osdLeaves = (tree) => {
|
|
2002
1932
|
const root = tree["root"];
|
|
@@ -2022,23 +1952,23 @@ var osdAttributes = (leaf, props) => ({
|
|
|
2022
1952
|
status: text(leaf["status"]),
|
|
2023
1953
|
total_space: num(leaf["total_space"], 0)
|
|
2024
1954
|
});
|
|
2025
|
-
var readOsd = (props) => runPve(props.target, "read", false, nodes3.getNodeCephOsd({ node: props.node })).pipe(
|
|
1955
|
+
var readOsd = (props) => runPve(props.target, "read", false, nodes3.getNodeCephOsd({ node: props.node })).pipe(Effect18.map((t) => {
|
|
2026
1956
|
const leaf = findOsd(t, props.osdid);
|
|
2027
1957
|
return leaf === undefined ? undefined : osdAttributes(leaf, props);
|
|
2028
1958
|
}));
|
|
2029
1959
|
|
|
2030
1960
|
// src/proxmox/ceph-osd-write.ts
|
|
2031
1961
|
import * as nodes4 from "@distilled.cloud/proxmox/nodes";
|
|
2032
|
-
import * as
|
|
1962
|
+
import * as Effect19 from "effect/Effect";
|
|
2033
1963
|
var OSD_CREATE_ENDPOINT = "pve:POST /nodes/{node}/ceph/osd";
|
|
2034
1964
|
var describe = (props) => `osd.${String(props.osdid)} on ${props.node}`;
|
|
2035
1965
|
var createOsdForm = (props, dev) => ({
|
|
2036
1966
|
dev,
|
|
2037
1967
|
...props.device_class === undefined ? {} : { "crush-device-class": props.device_class }
|
|
2038
1968
|
});
|
|
2039
|
-
var createOsd =
|
|
1969
|
+
var createOsd = Effect19.fn(function* (news) {
|
|
2040
1970
|
if (news.dev === undefined) {
|
|
2041
|
-
return yield*
|
|
1971
|
+
return yield* Effect19.die(new Error(`${describe(news)}: no such OSD in the CRUSH tree, and no \`dev\` was declared. ` + "Without a device this resource is an ASSERTION over an OSD that must already " + "exist. Declare `dev` to let it create one -- which WIPES that device -- or fix " + "the declaration; if the disk was there yesterday, a replica is missing and Ceph " + "is degraded."));
|
|
2042
1972
|
}
|
|
2043
1973
|
const form = createOsdForm(news, news.dev);
|
|
2044
1974
|
yield* guardForm(OSD_CREATE_ENDPOINT, form, true);
|
|
@@ -2049,7 +1979,7 @@ var createOsd = Effect20.fn(function* (news) {
|
|
|
2049
1979
|
}));
|
|
2050
1980
|
const created = yield* readOsd(news);
|
|
2051
1981
|
if (created === undefined) {
|
|
2052
|
-
return yield*
|
|
1982
|
+
return yield* Effect19.die(new Error(`${describe(news)}: the create returned no error but osd.${String(news.osdid)} is ` + "still absent. Ceph allocates OSD ids and cannot be told which to use, so it most " + "likely assigned a different number -- check `ceph osd tree`, and declare the id it " + "actually gave the device rather than deploying again."));
|
|
2053
1983
|
}
|
|
2054
1984
|
return created;
|
|
2055
1985
|
});
|
|
@@ -2065,12 +1995,12 @@ var ProxmoxCephOsd = Resource7("Proxmox.CephOsd", {
|
|
|
2065
1995
|
});
|
|
2066
1996
|
var settled = (live, props) => (props.host === undefined || props.host === live.host) && (props.device_class === undefined || props.device_class === live.device_class);
|
|
2067
1997
|
var describe2 = (props) => `osd.${String(props.osdid)} (read via nodes/${props.node}/ceph/osd)`;
|
|
2068
|
-
var
|
|
2069
|
-
list: () =>
|
|
2070
|
-
read:
|
|
1998
|
+
var handlers = {
|
|
1999
|
+
list: () => Effect20.succeed([]),
|
|
2000
|
+
read: Effect20.fn(function* ({ olds }) {
|
|
2071
2001
|
return yield* readOsd(olds);
|
|
2072
2002
|
}),
|
|
2073
|
-
diff:
|
|
2003
|
+
diff: Effect20.fn(function* ({
|
|
2074
2004
|
news,
|
|
2075
2005
|
output
|
|
2076
2006
|
}) {
|
|
@@ -2083,27 +2013,27 @@ var handlers2 = {
|
|
|
2083
2013
|
return { action: "update" };
|
|
2084
2014
|
return settled(live, news) ? { action: "noop" } : { action: "update" };
|
|
2085
2015
|
}),
|
|
2086
|
-
reconcile:
|
|
2016
|
+
reconcile: Effect20.fn(function* ({ news }) {
|
|
2087
2017
|
const live = yield* readOsd(news);
|
|
2088
2018
|
if (live === undefined) {
|
|
2089
2019
|
return yield* createOsd(news);
|
|
2090
2020
|
}
|
|
2091
2021
|
if (!settled(live, news)) {
|
|
2092
|
-
return yield*
|
|
2022
|
+
return yield* Effect20.die(new Error(`${describe2(news)}: exists but not as declared -- live host=${live.host} ` + `device_class=${live.device_class}. Nothing here moves an OSD between hosts or ` + "reclassifies it; fix the declaration, or move it deliberately with `ceph osd crush " + "move` / `ceph osd crush set-device-class` and deploy again."));
|
|
2093
2023
|
}
|
|
2094
2024
|
return live;
|
|
2095
2025
|
}),
|
|
2096
|
-
delete:
|
|
2026
|
+
delete: Effect20.fn(function* ({ olds }) {
|
|
2097
2027
|
yield* destroyOsd(olds);
|
|
2098
2028
|
})
|
|
2099
2029
|
};
|
|
2100
|
-
var ProxmoxCephOsdProvider = () => Provider7.effect(ProxmoxCephOsd,
|
|
2030
|
+
var ProxmoxCephOsdProvider = () => Provider7.effect(ProxmoxCephOsd, Effect20.succeed(ProxmoxCephOsd.Provider.of(handlers)));
|
|
2101
2031
|
// src/proxmox/ceph-pool.ts
|
|
2102
2032
|
import { Resource as Resource8 } from "alchemy";
|
|
2103
2033
|
import { isResolved as isResolved8 } from "alchemy/Diff";
|
|
2104
2034
|
import * as Provider8 from "alchemy/Provider";
|
|
2105
2035
|
import * as nodes7 from "@distilled.cloud/proxmox/nodes";
|
|
2106
|
-
import * as
|
|
2036
|
+
import * as Effect23 from "effect/Effect";
|
|
2107
2037
|
|
|
2108
2038
|
// src/proxmox/ceph-pool-form.ts
|
|
2109
2039
|
var object = (props) => `nodes/${props.node}/ceph/pool/${props.name}`;
|
|
@@ -2132,22 +2062,22 @@ var createBody = (props) => ({
|
|
|
2132
2062
|
|
|
2133
2063
|
// src/proxmox/ceph-pool-settle.ts
|
|
2134
2064
|
import * as nodes5 from "@distilled.cloud/proxmox/nodes";
|
|
2135
|
-
import * as
|
|
2136
|
-
var confirmAbsent = (props, describe3) => runPve(props.target, "read", false, nodes5.listNodeCephPool({ node: props.node })).pipe(
|
|
2137
|
-
var settle = (props, read, done) =>
|
|
2065
|
+
import * as Effect21 from "effect/Effect";
|
|
2066
|
+
var confirmAbsent = (props, describe3) => runPve(props.target, "read", false, nodes5.listNodeCephPool({ node: props.node })).pipe(Effect21.flatMap((rows) => rows.some((row) => row.pool_name === props.name) ? Effect21.die(new Error(`${describe3}: the cluster lists this pool but its status could not be read, ` + "so this deploy will NOT create over it. Check that the node is up and that the " + "read role holds Sys.Audit or Datastore.Audit on / -- a create here would apply " + "pg_num to a live pool and start a PG merge.")) : Effect21.void));
|
|
2067
|
+
var settle = (props, read, done) => Effect21.gen(function* () {
|
|
2138
2068
|
for (let attempt = 0;attempt < 30; attempt += 1) {
|
|
2139
2069
|
const live = yield* read(props);
|
|
2140
2070
|
if (done(live))
|
|
2141
2071
|
return live;
|
|
2142
|
-
yield*
|
|
2072
|
+
yield* Effect21.sleep("2 seconds");
|
|
2143
2073
|
}
|
|
2144
2074
|
return yield* read(props);
|
|
2145
2075
|
});
|
|
2146
2076
|
|
|
2147
2077
|
// src/proxmox/ceph-pool-wire.ts
|
|
2148
2078
|
import * as nodes6 from "@distilled.cloud/proxmox/nodes";
|
|
2149
|
-
import * as
|
|
2150
|
-
var
|
|
2079
|
+
import * as Effect22 from "effect/Effect";
|
|
2080
|
+
var attributesOf4 = (live, props) => {
|
|
2151
2081
|
const row = live;
|
|
2152
2082
|
if (typeof row["name"] !== "string")
|
|
2153
2083
|
return;
|
|
@@ -2169,7 +2099,7 @@ var attributesOf3 = (live, props) => {
|
|
|
2169
2099
|
target_size_ratio: num(row["target_size_ratio"], UNSET)
|
|
2170
2100
|
};
|
|
2171
2101
|
};
|
|
2172
|
-
var readPoolStatus = (props) => runPve(props.target, "read", false, nodes6.getNodeCephPoolStatus({ name: props.name, node: props.node, verbose: "1" })).pipe(
|
|
2102
|
+
var readPoolStatus = (props) => runPve(props.target, "read", false, nodes6.getNodeCephPoolStatus({ name: props.name, node: props.node, verbose: "1" })).pipe(Effect22.map((live) => attributesOf4(live, props)), Effect22.catchTag("CephPoolNotFound", () => Effect22.succeed(undefined)));
|
|
2173
2103
|
var CEPH_POOL_CREATE = "pve:POST /nodes/{node}/ceph/pool";
|
|
2174
2104
|
var CEPH_POOL_UPDATE = "pve:PUT /nodes/{node}/ceph/pool/{name}";
|
|
2175
2105
|
var toDistilledCreate2 = (props) => ({
|
|
@@ -2186,11 +2116,11 @@ var toDistilledUpdate = (props) => ({
|
|
|
2186
2116
|
var ProxmoxCephPool = Resource8("Proxmox.CephPool", {
|
|
2187
2117
|
defaultRemovalPolicy: "retain"
|
|
2188
2118
|
});
|
|
2189
|
-
var
|
|
2190
|
-
var ProxmoxCephPoolProvider = () => Provider8.effect(ProxmoxCephPool,
|
|
2191
|
-
list: () =>
|
|
2119
|
+
var matches4 = (attributes, props) => same(props.size, attributes.size, attributes.nosizechange) && same(props.min_size, attributes.min_size, attributes.nosizechange) && same(props.pg_autoscale_mode, attributes.pg_autoscale_mode) && same(hint(props.pg_num_min), attributes.pg_num_min, attributes.nopgchange) && same(props.crush_rule, attributes.crush_rule, attributes.crush_rule === "") && same(hint(props.target_size), attributes.target_size);
|
|
2120
|
+
var ProxmoxCephPoolProvider = () => Provider8.effect(ProxmoxCephPool, Effect23.succeed(ProxmoxCephPool.Provider.of({
|
|
2121
|
+
list: () => Effect23.succeed([]),
|
|
2192
2122
|
read: ({ olds }) => readPoolStatus(olds),
|
|
2193
|
-
diff:
|
|
2123
|
+
diff: Effect23.fn(function* ({ news, output }) {
|
|
2194
2124
|
if (!isResolved8(news))
|
|
2195
2125
|
return;
|
|
2196
2126
|
yield* guardWrite2(CEPH_POOL_CREATE, createBody(news), output === undefined);
|
|
@@ -2202,9 +2132,9 @@ var ProxmoxCephPoolProvider = () => Provider8.effect(ProxmoxCephPool, Effect24.s
|
|
|
2202
2132
|
yield* guardWrite2(CEPH_POOL_CREATE, createBody(news), true);
|
|
2203
2133
|
return { action: "update" };
|
|
2204
2134
|
}
|
|
2205
|
-
return
|
|
2135
|
+
return matches4(live, news) ? { action: "noop" } : { action: "update" };
|
|
2206
2136
|
}),
|
|
2207
|
-
reconcile:
|
|
2137
|
+
reconcile: Effect23.fn(function* ({ news }) {
|
|
2208
2138
|
const live = yield* readPoolStatus(news);
|
|
2209
2139
|
let upid;
|
|
2210
2140
|
if (live === undefined) {
|
|
@@ -2212,36 +2142,36 @@ var ProxmoxCephPoolProvider = () => Provider8.effect(ProxmoxCephPool, Effect24.s
|
|
|
2212
2142
|
yield* confirmAbsent(news, object(news));
|
|
2213
2143
|
upid = yield* runPve(news.target, "provision", true, nodes7.createNodeCephPool(toDistilledCreate2(news)));
|
|
2214
2144
|
} else {
|
|
2215
|
-
const form = formToSend(
|
|
2145
|
+
const form = formToSend(matches4, live, news, updateBody(news));
|
|
2216
2146
|
if (form !== undefined) {
|
|
2217
2147
|
yield* guardWrite2(CEPH_POOL_UPDATE, updateBody(news), false);
|
|
2218
2148
|
upid = yield* runPve(news.target, "provision", true, nodes7.putNodeCephPool(toDistilledUpdate(news)));
|
|
2219
2149
|
}
|
|
2220
2150
|
}
|
|
2221
|
-
const after = yield* settle(news, readPoolStatus, (row) => row !== undefined &&
|
|
2222
|
-
if (after === undefined || !
|
|
2223
|
-
return yield*
|
|
2151
|
+
const after = yield* settle(news, readPoolStatus, (row) => row !== undefined && matches4(row, news));
|
|
2152
|
+
if (after === undefined || !matches4(after, news)) {
|
|
2153
|
+
return yield* Effect23.die(new Error(`${object(news)}: the write returned no error but the pool still does not match ` + `the declaration after 60s. PVE answered with task ${upid ?? "(none)"} -- read ` + "its log with `pvesh get /nodes/<node>/tasks/<upid>/log` to see what Ceph " + "refused."));
|
|
2224
2154
|
}
|
|
2225
2155
|
return after;
|
|
2226
2156
|
}),
|
|
2227
|
-
delete:
|
|
2157
|
+
delete: Effect23.fn(function* ({ olds }) {
|
|
2228
2158
|
yield* runPve(olds.target, "provision", true, nodes7.deleteNodeCephPool({ name: olds.name, node: olds.node }));
|
|
2229
2159
|
const left = yield* settle(olds, readPoolStatus, (row) => row === undefined);
|
|
2230
2160
|
if (left !== undefined) {
|
|
2231
|
-
return yield*
|
|
2161
|
+
return yield* Effect23.die(new Error(`${object(olds)}: the DELETE returned no error but the pool is still there after ` + "60s. Either a storage on it still holds RBD images, or the mons are running " + "with mon_allow_pool_delete false."));
|
|
2232
2162
|
}
|
|
2233
2163
|
})
|
|
2234
2164
|
})));
|
|
2235
2165
|
// src/proxmox/firewall-alias.ts
|
|
2236
2166
|
import { Resource as Resource9 } from "alchemy";
|
|
2237
2167
|
import * as Provider9 from "alchemy/Provider";
|
|
2238
|
-
import * as
|
|
2168
|
+
import * as Effect25 from "effect/Effect";
|
|
2239
2169
|
|
|
2240
2170
|
// src/proxmox/firewall-alias-lifecycle.ts
|
|
2241
2171
|
import { isResolved as isResolved9 } from "alchemy/Diff";
|
|
2242
|
-
import * as
|
|
2172
|
+
import * as cluster4 from "@distilled.cloud/proxmox/cluster";
|
|
2243
2173
|
import { ProxmoxParseError as ProxmoxParseError2 } from "@distilled.cloud/proxmox/Errors";
|
|
2244
|
-
import * as
|
|
2174
|
+
import * as Effect24 from "effect/Effect";
|
|
2245
2175
|
|
|
2246
2176
|
// src/proxmox/firewall-alias-form.ts
|
|
2247
2177
|
var fold = (name) => name.trim().toLowerCase();
|
|
@@ -2283,20 +2213,20 @@ var firewallAliasSpec = {
|
|
|
2283
2213
|
|
|
2284
2214
|
// src/proxmox/firewall-alias-lifecycle.ts
|
|
2285
2215
|
var { guardCreate: guardCreate2, guardUpdate: guardUpdate2 } = specGuards(firewallAliasSpec);
|
|
2286
|
-
var readFirewallAlias = (props) => runPve(props.target, "read", false,
|
|
2216
|
+
var readFirewallAlias = (props) => runPve(props.target, "read", false, cluster4.getClusterFirewallAlias({ name: props.name })).pipe(Effect24.flatMap((live) => {
|
|
2287
2217
|
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) === "") {
|
|
2288
|
-
return
|
|
2218
|
+
return Effect24.fail(new ProxmoxParseError2({
|
|
2289
2219
|
body: undefined,
|
|
2290
2220
|
cause: "Firewall alias response does not match the requested vendor identity"
|
|
2291
2221
|
}));
|
|
2292
2222
|
}
|
|
2293
|
-
return
|
|
2294
|
-
}),
|
|
2295
|
-
var deleteFirewallAlias = (props) => runPve(props.target, "provision", true,
|
|
2223
|
+
return Effect24.succeed(firewallAliasSpec.attributes({ ...live }, props));
|
|
2224
|
+
}), Effect24.catchTag("FirewallAliasNotFound", () => Effect24.succeed(undefined)));
|
|
2225
|
+
var deleteFirewallAlias = (props) => runPve(props.target, "provision", true, cluster4.deleteClusterFirewallAlias({ name: props.name }));
|
|
2296
2226
|
var firewallAliasHandlers = {
|
|
2297
|
-
list: () =>
|
|
2227
|
+
list: () => Effect24.succeed([]),
|
|
2298
2228
|
read: ({ olds }) => readFirewallAlias(olds),
|
|
2299
|
-
diff:
|
|
2229
|
+
diff: Effect24.fn(function* ({
|
|
2300
2230
|
news,
|
|
2301
2231
|
output
|
|
2302
2232
|
}) {
|
|
@@ -2315,20 +2245,20 @@ var firewallAliasHandlers = {
|
|
|
2315
2245
|
}
|
|
2316
2246
|
return { action: firewallAliasSpec.matches(live, news) ? "noop" : "update" };
|
|
2317
2247
|
}),
|
|
2318
|
-
reconcile:
|
|
2248
|
+
reconcile: Effect24.fn(function* ({ news }) {
|
|
2319
2249
|
const live = yield* readFirewallAlias(news);
|
|
2320
2250
|
yield* guardCreate2(news, live === undefined);
|
|
2321
2251
|
yield* guardUpdate2(news);
|
|
2322
2252
|
if (live === undefined) {
|
|
2323
|
-
yield* runPve(news.target, "provision", true,
|
|
2253
|
+
yield* runPve(news.target, "provision", true, cluster4.createClusterFirewallAlias(firewallAliasSpec.createForm(news)));
|
|
2324
2254
|
} else {
|
|
2325
2255
|
const form = formToSend(firewallAliasSpec.matches, live, news, firewallAliasSpec.updateForm(news));
|
|
2326
2256
|
if (form !== undefined)
|
|
2327
|
-
yield* runPve(news.target, "provision", true,
|
|
2257
|
+
yield* runPve(news.target, "provision", true, cluster4.putClusterFirewallAlias({ ...form, name: news.name }));
|
|
2328
2258
|
}
|
|
2329
2259
|
const after = yield* readFirewallAlias(news);
|
|
2330
2260
|
if (after === undefined)
|
|
2331
|
-
return yield*
|
|
2261
|
+
return yield* Effect24.fail(new Error(`${firewallAliasSpec.path(news)}: write returned success but the resource is still absent`));
|
|
2332
2262
|
return after;
|
|
2333
2263
|
}),
|
|
2334
2264
|
delete: ({ olds }) => deleteFirewallAlias(olds)
|
|
@@ -2336,17 +2266,17 @@ var firewallAliasHandlers = {
|
|
|
2336
2266
|
|
|
2337
2267
|
// src/proxmox/firewall-alias.ts
|
|
2338
2268
|
var ProxmoxFirewallAlias = Resource9("Proxmox.FirewallAlias");
|
|
2339
|
-
var ProxmoxFirewallAliasProvider = () => Provider9.effect(ProxmoxFirewallAlias,
|
|
2269
|
+
var ProxmoxFirewallAliasProvider = () => Provider9.effect(ProxmoxFirewallAlias, Effect25.succeed(ProxmoxFirewallAlias.Provider.of(firewallAliasHandlers)));
|
|
2340
2270
|
// src/proxmox/group.ts
|
|
2341
2271
|
import { Resource as Resource10 } from "alchemy";
|
|
2342
2272
|
import { isResolved as isResolved10 } from "alchemy/Diff";
|
|
2343
2273
|
import * as Provider10 from "alchemy/Provider";
|
|
2344
2274
|
import * as access4 from "@distilled.cloud/proxmox/access";
|
|
2345
|
-
import * as
|
|
2275
|
+
import * as Effect27 from "effect/Effect";
|
|
2346
2276
|
|
|
2347
2277
|
// src/proxmox/group-wire.ts
|
|
2348
2278
|
import * as access3 from "@distilled.cloud/proxmox/access";
|
|
2349
|
-
import * as
|
|
2279
|
+
import * as Effect26 from "effect/Effect";
|
|
2350
2280
|
var GROUP_CREATE = "pve:POST /access/groups";
|
|
2351
2281
|
var GROUP_UPDATE = "pve:PUT /access/groups/{groupid}";
|
|
2352
2282
|
var storedComment = (comment2) => {
|
|
@@ -2361,70 +2291,188 @@ var createForm3 = (props) => ({
|
|
|
2361
2291
|
comment: storedComment(props.comment),
|
|
2362
2292
|
groupid: props.groupid
|
|
2363
2293
|
});
|
|
2364
|
-
var
|
|
2294
|
+
var updateForm2 = (props) => ({
|
|
2365
2295
|
comment: storedComment(props.comment),
|
|
2366
2296
|
groupid: props.groupid
|
|
2367
2297
|
});
|
|
2368
|
-
var
|
|
2298
|
+
var attributesOf5 = (live, props) => ({
|
|
2369
2299
|
comment: text(live.comment),
|
|
2370
2300
|
groupid: props.groupid,
|
|
2371
2301
|
members: memberList(live.members)
|
|
2372
2302
|
});
|
|
2373
|
-
var
|
|
2374
|
-
var readGroupOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, access3.getAccessGroup({ groupid: props.groupid }))).pipe(
|
|
2375
|
-
var readGroup = (props) => readGroupOrFail(props).pipe(
|
|
2303
|
+
var matches5 = (attributes, props) => attributes.comment === storedComment(props.comment);
|
|
2304
|
+
var readGroupOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, access3.getAccessGroup({ groupid: props.groupid }))).pipe(Effect26.map((live) => live === UNREADABLE ? UNREADABLE : live.members === undefined ? undefined : attributesOf5(live, props)));
|
|
2305
|
+
var readGroup = (props) => readGroupOrFail(props).pipe(Effect26.catchTag("GroupNotFound", () => Effect26.succeed(undefined)));
|
|
2376
2306
|
var dropUnreadable3 = (live) => live === UNREADABLE ? undefined : live;
|
|
2377
2307
|
|
|
2378
2308
|
// src/proxmox/group.ts
|
|
2379
2309
|
var ProxmoxGroup = Resource10("Proxmox.Group", {
|
|
2380
2310
|
defaultRemovalPolicy: "retain"
|
|
2381
2311
|
});
|
|
2382
|
-
var ProxmoxGroupProvider = () => Provider10.effect(ProxmoxGroup,
|
|
2383
|
-
list: () =>
|
|
2384
|
-
read:
|
|
2312
|
+
var ProxmoxGroupProvider = () => Provider10.effect(ProxmoxGroup, Effect27.succeed(ProxmoxGroup.Provider.of({
|
|
2313
|
+
list: () => Effect27.succeed([]),
|
|
2314
|
+
read: Effect27.fn(function* ({ olds, output }) {
|
|
2385
2315
|
return dropUnreadable3(yield* output === undefined ? readGroup(olds) : readGroupOrFail(olds));
|
|
2386
2316
|
}),
|
|
2387
|
-
diff:
|
|
2317
|
+
diff: Effect27.fn(function* ({ news, output }) {
|
|
2388
2318
|
if (!isResolved10(news))
|
|
2389
2319
|
return;
|
|
2390
2320
|
yield* guardWrite2(GROUP_CREATE, createForm3(news), output === undefined);
|
|
2391
|
-
yield* guardWrite2(GROUP_UPDATE,
|
|
2321
|
+
yield* guardWrite2(GROUP_UPDATE, updateForm2(news), false);
|
|
2392
2322
|
if (output === undefined)
|
|
2393
2323
|
return;
|
|
2394
2324
|
const live = yield* readGroupOrFail(news);
|
|
2395
2325
|
if (live === UNREADABLE) {
|
|
2396
|
-
yield* unreadableWarning("Proxmox.Group", news.groupid);
|
|
2326
|
+
yield* unreadableWarning("Proxmox.Group", news.groupid, news.target.mount, mintTier(news.target, "read"));
|
|
2397
2327
|
return { action: "noop" };
|
|
2398
2328
|
}
|
|
2399
2329
|
if (live === undefined) {
|
|
2400
2330
|
yield* guardWrite2(GROUP_CREATE, createForm3(news), true);
|
|
2401
2331
|
return { action: "update" };
|
|
2402
2332
|
}
|
|
2403
|
-
return
|
|
2333
|
+
return matches5(live, news) ? { action: "noop" } : { action: "update" };
|
|
2404
2334
|
}),
|
|
2405
|
-
reconcile:
|
|
2335
|
+
reconcile: Effect27.fn(function* ({ news }) {
|
|
2406
2336
|
const before = dropUnreadable3(yield* readGroup(news));
|
|
2407
2337
|
yield* guardWrite2(GROUP_CREATE, createForm3(news), before === undefined);
|
|
2408
|
-
yield* guardWrite2(GROUP_UPDATE,
|
|
2338
|
+
yield* guardWrite2(GROUP_UPDATE, updateForm2(news), false);
|
|
2409
2339
|
if (before === undefined) {
|
|
2410
2340
|
yield* runPve(news.target, "provision", true, access4.createAccessGroup(createForm3(news)));
|
|
2411
|
-
} else if (!
|
|
2412
|
-
yield* runPve(news.target, "provision", true, access4.putAccessGroup(
|
|
2341
|
+
} else if (!matches5(before, news)) {
|
|
2342
|
+
yield* runPve(news.target, "provision", true, access4.putAccessGroup(updateForm2(news)));
|
|
2413
2343
|
}
|
|
2414
2344
|
const after = dropUnreadable3(yield* readGroup(news));
|
|
2415
2345
|
if (after === undefined) {
|
|
2416
|
-
return yield*
|
|
2346
|
+
return yield* Effect27.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."));
|
|
2417
2347
|
}
|
|
2418
2348
|
return after;
|
|
2419
2349
|
}),
|
|
2420
|
-
delete:
|
|
2350
|
+
delete: Effect27.fn(function* ({ olds }) {
|
|
2421
2351
|
yield* runPve(olds.target, "provision", true, access4.deleteAccessGroup({ groupid: olds.groupid }));
|
|
2422
2352
|
})
|
|
2423
2353
|
})));
|
|
2424
2354
|
// src/proxmox/ha-resource.ts
|
|
2425
2355
|
import { Resource as Resource11 } from "alchemy";
|
|
2426
2356
|
import * as Provider11 from "alchemy/Provider";
|
|
2357
|
+
import * as Effect30 from "effect/Effect";
|
|
2358
|
+
|
|
2359
|
+
// src/proxmox/resource.ts
|
|
2360
|
+
import { isResolved as isResolved11 } from "alchemy/Diff";
|
|
2427
2361
|
import * as Effect29 from "effect/Effect";
|
|
2362
|
+
|
|
2363
|
+
// src/proxmox/client.ts
|
|
2364
|
+
import * as Effect28 from "effect/Effect";
|
|
2365
|
+
import * as HttpClient5 from "effect/unstable/http/HttpClient";
|
|
2366
|
+
import * as HttpClientRequest2 from "effect/unstable/http/HttpClientRequest";
|
|
2367
|
+
class PveError extends Error {
|
|
2368
|
+
status;
|
|
2369
|
+
method;
|
|
2370
|
+
path;
|
|
2371
|
+
constructor(status, method, path, detail) {
|
|
2372
|
+
super(`PVE ${method} ${path} -> ${String(status)}: ${detail}`);
|
|
2373
|
+
this.status = status;
|
|
2374
|
+
this.method = method;
|
|
2375
|
+
this.path = path;
|
|
2376
|
+
this.name = "PveError";
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
var clusterExhausted = (method, path, attempts) => new PveError(0, method, path, `all members failed: ${attempts.map((a) => `${a.member} (${a.why})`).join("; ")}`);
|
|
2380
|
+
var encode = (form) => {
|
|
2381
|
+
const params = new URLSearchParams;
|
|
2382
|
+
for (const [key, value] of Object.entries(form)) {
|
|
2383
|
+
if (typeof value === "string")
|
|
2384
|
+
params.append(key, value);
|
|
2385
|
+
else
|
|
2386
|
+
for (const item of value)
|
|
2387
|
+
params.append(key, item);
|
|
2388
|
+
}
|
|
2389
|
+
return params.toString();
|
|
2390
|
+
};
|
|
2391
|
+
var buildRequest = (method, apiBase, path, credential, target, form) => {
|
|
2392
|
+
const url = `${apiBase}/${path}`;
|
|
2393
|
+
return HttpClientRequest2.make(method)(url).pipe(HttpClientRequest2.setHeaders({ Authorization: authorization(credential, target) }), form === undefined ? (self) => self : HttpClientRequest2.bodyText(encode(form), "application/x-www-form-urlencoded"));
|
|
2394
|
+
};
|
|
2395
|
+
var executeOne = (target, credential, method, path, form, apiBase) => Effect28.gen(function* () {
|
|
2396
|
+
const client = yield* HttpClient5.HttpClient;
|
|
2397
|
+
const request = buildRequest(method, apiBase, path, credential, target, form);
|
|
2398
|
+
return yield* client.execute(request).pipe(Effect28.mapError((cause) => new PveError(0, method, path, String(cause))));
|
|
2399
|
+
});
|
|
2400
|
+
var readResponse = (response, method, path) => Effect28.gen(function* () {
|
|
2401
|
+
if (response.status < 200 || response.status >= 300) {
|
|
2402
|
+
const detail = yield* response.text.pipe(Effect28.orElseSucceed(() => ""));
|
|
2403
|
+
return yield* Effect28.fail(new PveError(response.status, method, path, detail.slice(0, 300)));
|
|
2404
|
+
}
|
|
2405
|
+
const body2 = yield* response.json.pipe(Effect28.mapError((cause) => new PveError(0, method, path, String(cause))));
|
|
2406
|
+
return body2.data;
|
|
2407
|
+
});
|
|
2408
|
+
var pveWith = (target, credential, method, path, form) => Effect28.gen(function* () {
|
|
2409
|
+
const response = target.scheme === "pve" ? yield* executeOnCluster(target, method, path, (apiBase) => buildRequest(method, apiBase, path, credential, target, form)).pipe(Effect28.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);
|
|
2410
|
+
return yield* readResponse(response, method, path);
|
|
2411
|
+
});
|
|
2412
|
+
var pve = (target, role, method, path, form) => Effect28.gen(function* () {
|
|
2413
|
+
const credential = yield* leased(target, role);
|
|
2414
|
+
return yield* pveWith(target, credential, method, path, form);
|
|
2415
|
+
});
|
|
2416
|
+
|
|
2417
|
+
// src/proxmox/resource.ts
|
|
2418
|
+
var pveOperations = (spec) => {
|
|
2419
|
+
const { guardCreate: guardCreate3, guardUpdate: guardUpdate3 } = specGuards(spec);
|
|
2420
|
+
const read = (props) => pve(props.target, spec.readRole ?? "read", "GET", spec.path(props)).pipe(Effect29.map((data) => data == null ? undefined : spec.attributes(data, props)), Effect29.orElseSucceed(() => {
|
|
2421
|
+
return;
|
|
2422
|
+
}));
|
|
2423
|
+
return {
|
|
2424
|
+
guardCreate: guardCreate3,
|
|
2425
|
+
guardUpdate: guardUpdate3,
|
|
2426
|
+
read,
|
|
2427
|
+
diff: (news, output) => Effect29.gen(function* () {
|
|
2428
|
+
if (!isResolved11(news))
|
|
2429
|
+
return;
|
|
2430
|
+
yield* guardCreate3(news, output === undefined);
|
|
2431
|
+
yield* guardUpdate3(news);
|
|
2432
|
+
if (output === undefined)
|
|
2433
|
+
return;
|
|
2434
|
+
const live = yield* read(news);
|
|
2435
|
+
if (live === undefined) {
|
|
2436
|
+
yield* guardCreate3(news, true);
|
|
2437
|
+
return { action: "update" };
|
|
2438
|
+
}
|
|
2439
|
+
if (spec.matches(live, news))
|
|
2440
|
+
return { action: "noop" };
|
|
2441
|
+
return spec.updateForm === undefined ? { action: "replace" } : { action: "update" };
|
|
2442
|
+
}),
|
|
2443
|
+
reconcile: (news) => Effect29.gen(function* () {
|
|
2444
|
+
const live = yield* read(news);
|
|
2445
|
+
yield* guardCreate3(news, live === undefined);
|
|
2446
|
+
yield* guardUpdate3(news);
|
|
2447
|
+
if (live === undefined) {
|
|
2448
|
+
yield* pve(news.target, "provision", "POST", spec.collection(news), spec.createForm(news));
|
|
2449
|
+
} else if (spec.updateForm !== undefined) {
|
|
2450
|
+
const form = formToSend(spec.matches, live, news, spec.updateForm(news));
|
|
2451
|
+
if (form !== undefined) {
|
|
2452
|
+
yield* pve(news.target, "provision", "PUT", spec.path(news), form);
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2455
|
+
const after = yield* read(news);
|
|
2456
|
+
if (after === undefined) {
|
|
2457
|
+
return yield* Effect29.die(new Error(`${spec.path(news)}: the write returned no error but the object 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."));
|
|
2458
|
+
}
|
|
2459
|
+
return after;
|
|
2460
|
+
}),
|
|
2461
|
+
destroy: (olds) => pve(olds.target, "provision", "DELETE", spec.path(olds))
|
|
2462
|
+
};
|
|
2463
|
+
};
|
|
2464
|
+
var pveHandlers = (spec) => {
|
|
2465
|
+
const ops = pveOperations(spec);
|
|
2466
|
+
return {
|
|
2467
|
+
list: () => Effect29.succeed([]),
|
|
2468
|
+
read: ({ olds }) => ops.read(olds),
|
|
2469
|
+
diff: ({ news, output }) => ops.diff(news, output),
|
|
2470
|
+
reconcile: ({ news }) => ops.reconcile(news),
|
|
2471
|
+
delete: ({ olds }) => ops.destroy(olds)
|
|
2472
|
+
};
|
|
2473
|
+
};
|
|
2474
|
+
|
|
2475
|
+
// src/proxmox/ha-resource.ts
|
|
2428
2476
|
var ProxmoxHaResource = Resource11("Proxmox.HaResource");
|
|
2429
2477
|
var kind = (sid) => sid.split(":")[0] ?? "";
|
|
2430
2478
|
var shape3 = (props) => ({
|
|
@@ -2433,7 +2481,7 @@ var shape3 = (props) => ({
|
|
|
2433
2481
|
max_restart: String(props.max_restart ?? 1),
|
|
2434
2482
|
state: props.state ?? "started"
|
|
2435
2483
|
});
|
|
2436
|
-
var
|
|
2484
|
+
var handlers2 = pveHandlers({
|
|
2437
2485
|
attributes: (live, props) => ({
|
|
2438
2486
|
comment: typeof live["comment"] === "string" ? live["comment"] : "",
|
|
2439
2487
|
max_relocate: num(live["max_relocate"], 1),
|
|
@@ -2452,11 +2500,11 @@ var handlers3 = pveHandlers({
|
|
|
2452
2500
|
path: (props) => `cluster/ha/resources/${props.sid}`,
|
|
2453
2501
|
updateForm: shape3
|
|
2454
2502
|
});
|
|
2455
|
-
var ProxmoxHaResourceProvider = () => Provider11.effect(ProxmoxHaResource,
|
|
2503
|
+
var ProxmoxHaResourceProvider = () => Provider11.effect(ProxmoxHaResource, Effect30.succeed(ProxmoxHaResource.Provider.of(handlers2)));
|
|
2456
2504
|
// src/proxmox/ha-rule.ts
|
|
2457
2505
|
import { Resource as Resource12 } from "alchemy";
|
|
2458
2506
|
import * as Provider12 from "alchemy/Provider";
|
|
2459
|
-
import * as
|
|
2507
|
+
import * as Effect31 from "effect/Effect";
|
|
2460
2508
|
|
|
2461
2509
|
// src/proxmox/ha-rule-form.ts
|
|
2462
2510
|
var nodeList = (value) => {
|
|
@@ -2504,7 +2552,7 @@ var clearList = (props) => Object.entries(optional(props)).filter(([, value]) =>
|
|
|
2504
2552
|
|
|
2505
2553
|
// src/proxmox/ha-rule.ts
|
|
2506
2554
|
var ProxmoxHaRule = Resource12("Proxmox.HaRule");
|
|
2507
|
-
var
|
|
2555
|
+
var handlers3 = pveHandlers({
|
|
2508
2556
|
attributes: (live, props) => {
|
|
2509
2557
|
const liveType = text(live["type"]);
|
|
2510
2558
|
if (liveType !== "" && liveType !== props.type)
|
|
@@ -2533,43 +2581,43 @@ var handlers4 = pveHandlers({
|
|
|
2533
2581
|
return withClears(fields, clear);
|
|
2534
2582
|
}
|
|
2535
2583
|
});
|
|
2536
|
-
var ProxmoxHaRuleProvider = () => Provider12.effect(ProxmoxHaRule,
|
|
2584
|
+
var ProxmoxHaRuleProvider = () => Provider12.effect(ProxmoxHaRule, Effect31.succeed(ProxmoxHaRule.Provider.of(handlers3)));
|
|
2537
2585
|
// src/proxmox/lxc.ts
|
|
2538
2586
|
import { Resource as Resource13 } from "alchemy";
|
|
2539
|
-
import { isResolved as
|
|
2587
|
+
import { isResolved as isResolved12 } from "alchemy/Diff";
|
|
2540
2588
|
import * as Provider13 from "alchemy/Provider";
|
|
2541
|
-
import * as
|
|
2589
|
+
import * as Effect42 from "effect/Effect";
|
|
2542
2590
|
|
|
2543
2591
|
// src/ownership/adopt.ts
|
|
2544
2592
|
import { AdoptPolicy } from "alchemy/AdoptPolicy";
|
|
2545
2593
|
import { AlchemyContext } from "alchemy/AlchemyContext";
|
|
2546
2594
|
import { Stack as Stack3 } from "alchemy/Stack";
|
|
2547
|
-
import * as
|
|
2595
|
+
import * as Effect35 from "effect/Effect";
|
|
2548
2596
|
import * as Option5 from "effect/Option";
|
|
2549
2597
|
|
|
2550
2598
|
// src/ownership/resume.ts
|
|
2551
2599
|
import { Unowned } from "alchemy/AdoptPolicy";
|
|
2552
2600
|
import { Artifacts } from "alchemy/Artifacts";
|
|
2553
|
-
import * as
|
|
2601
|
+
import * as Effect32 from "effect/Effect";
|
|
2554
2602
|
import * as Option2 from "effect/Option";
|
|
2555
2603
|
var PROVEN = "homeflare/ownership/resumes";
|
|
2556
2604
|
var UNFINISHED = "homeflare/ownership/unfinished";
|
|
2557
|
-
var note = (key, instanceId) =>
|
|
2558
|
-
const bag = yield*
|
|
2605
|
+
var note = (key, instanceId) => Effect32.gen(function* () {
|
|
2606
|
+
const bag = yield* Effect32.serviceOption(Artifacts);
|
|
2559
2607
|
if (Option2.isSome(bag))
|
|
2560
2608
|
yield* bag.value.set(key, instanceId);
|
|
2561
2609
|
});
|
|
2562
|
-
var noted = (key, instanceId) =>
|
|
2563
|
-
const bag = yield*
|
|
2610
|
+
var noted = (key, instanceId) => Effect32.gen(function* () {
|
|
2611
|
+
const bag = yield* Effect32.serviceOption(Artifacts);
|
|
2564
2612
|
if (Option2.isNone(bag))
|
|
2565
2613
|
return false;
|
|
2566
2614
|
return (yield* bag.value.get(key)) === instanceId;
|
|
2567
2615
|
});
|
|
2568
|
-
var noteUnfinished = (instanceId) =>
|
|
2569
|
-
var noteResume = (instanceId, recovered) =>
|
|
2616
|
+
var noteUnfinished = (instanceId) => Effect32.as(note(UNFINISHED, instanceId), undefined);
|
|
2617
|
+
var noteResume = (instanceId, recovered) => Effect32.gen(function* () {
|
|
2570
2618
|
yield* noteUnfinished(instanceId);
|
|
2571
|
-
const unproven = () =>
|
|
2572
|
-
const found = yield* recovered.pipe(
|
|
2619
|
+
const unproven = () => Effect32.succeed(undefined);
|
|
2620
|
+
const found = yield* recovered.pipe(Effect32.catch(unproven), Effect32.catchDefect(unproven));
|
|
2573
2621
|
if (found === undefined || found === null || Unowned.is(found))
|
|
2574
2622
|
return;
|
|
2575
2623
|
yield* note(PROVEN, instanceId);
|
|
@@ -2580,7 +2628,7 @@ var provingResumes = (service) => {
|
|
|
2580
2628
|
const { diff, read } = service;
|
|
2581
2629
|
if (diff === undefined || read === undefined)
|
|
2582
2630
|
return service;
|
|
2583
|
-
const proving = (input) =>
|
|
2631
|
+
const proving = (input) => Effect32.gen(function* () {
|
|
2584
2632
|
if (input.output === undefined) {
|
|
2585
2633
|
yield* noteResume(input.instanceId, read({ ...input, output: undefined }));
|
|
2586
2634
|
}
|
|
@@ -2592,12 +2640,12 @@ var provingResumes = (service) => {
|
|
|
2592
2640
|
// src/ownership/rows.ts
|
|
2593
2641
|
import { Stack as Stack2 } from "alchemy/Stack";
|
|
2594
2642
|
import { State, isActionState } from "alchemy/State/State";
|
|
2595
|
-
import * as
|
|
2643
|
+
import * as Effect34 from "effect/Effect";
|
|
2596
2644
|
import * as Option4 from "effect/Option";
|
|
2597
2645
|
|
|
2598
2646
|
// src/ownership/whole.ts
|
|
2599
2647
|
import { Stack } from "alchemy/Stack";
|
|
2600
|
-
import * as
|
|
2648
|
+
import * as Effect33 from "effect/Effect";
|
|
2601
2649
|
import * as Option3 from "effect/Option";
|
|
2602
2650
|
var absent = (value) => value === undefined || value === null;
|
|
2603
2651
|
var isPlainObject = (value) => {
|
|
@@ -2623,22 +2671,22 @@ var carries = (declared, recorded) => {
|
|
|
2623
2671
|
}
|
|
2624
2672
|
return true;
|
|
2625
2673
|
};
|
|
2626
|
-
var wholeDeclaration = (fqn, props) =>
|
|
2674
|
+
var wholeDeclaration = (fqn, props) => Effect33.map(Effect33.serviceOption(Stack), (stack) => {
|
|
2627
2675
|
const declared = Option3.isSome(stack) ? stack.value.resources[fqn] : undefined;
|
|
2628
2676
|
return declared !== undefined && carries(declared.Props, props);
|
|
2629
2677
|
});
|
|
2630
2678
|
|
|
2631
2679
|
// src/ownership/rows.ts
|
|
2632
|
-
var storeOf =
|
|
2633
|
-
const stack = yield*
|
|
2634
|
-
const state = yield*
|
|
2680
|
+
var storeOf = Effect34.gen(function* () {
|
|
2681
|
+
const stack = yield* Effect34.serviceOption(Stack2);
|
|
2682
|
+
const state = yield* Effect34.serviceOption(State);
|
|
2635
2683
|
if (Option4.isNone(stack) || Option4.isNone(state))
|
|
2636
2684
|
return;
|
|
2637
2685
|
const store = yield* state.value;
|
|
2638
2686
|
const where = { stack: stack.value.name, stage: stack.value.stage };
|
|
2639
2687
|
return { resources: stack.value.resources, store, where };
|
|
2640
2688
|
});
|
|
2641
|
-
var rowAt = (store, where, fqn) => store.get({ ...where, fqn }).pipe(
|
|
2689
|
+
var rowAt = (store, where, fqn) => store.get({ ...where, fqn }).pipe(Effect34.orElseSucceed(() => {
|
|
2642
2690
|
return;
|
|
2643
2691
|
}));
|
|
2644
2692
|
var generationOf = (row, instanceId) => {
|
|
@@ -2651,7 +2699,7 @@ var generationOf = (row, instanceId) => {
|
|
|
2651
2699
|
}
|
|
2652
2700
|
return;
|
|
2653
2701
|
};
|
|
2654
|
-
var recordedGeneration = (fqn, instanceId) =>
|
|
2702
|
+
var recordedGeneration = (fqn, instanceId) => Effect34.gen(function* () {
|
|
2655
2703
|
const found = yield* storeOf;
|
|
2656
2704
|
if (found === undefined)
|
|
2657
2705
|
return;
|
|
@@ -2662,14 +2710,14 @@ var recordedGeneration = (fqn, instanceId) => Effect33.gen(function* () {
|
|
|
2662
2710
|
}
|
|
2663
2711
|
return;
|
|
2664
2712
|
});
|
|
2665
|
-
var recordedInstance = (fqn, instanceId) =>
|
|
2713
|
+
var recordedInstance = (fqn, instanceId) => Effect34.gen(function* () {
|
|
2666
2714
|
const generation = yield* recordedGeneration(fqn, instanceId);
|
|
2667
2715
|
if (generation === undefined)
|
|
2668
2716
|
return "absent";
|
|
2669
2717
|
return (yield* wholeDeclaration(fqn, generation.props)) ? "whole" : "partial";
|
|
2670
2718
|
});
|
|
2671
|
-
var isCreate = (fqn, instanceId) =>
|
|
2672
|
-
var forgetRefusedCreate = (fqn, instanceId) =>
|
|
2719
|
+
var isCreate = (fqn, instanceId) => Effect34.map(recordedGeneration(fqn, instanceId), (generation) => generation?.status === "creating");
|
|
2720
|
+
var forgetRefusedCreate = (fqn, instanceId) => Effect34.gen(function* () {
|
|
2673
2721
|
const found = yield* storeOf;
|
|
2674
2722
|
if (found === undefined)
|
|
2675
2723
|
return;
|
|
@@ -2678,32 +2726,32 @@ var forgetRefusedCreate = (fqn, instanceId) => Effect33.gen(function* () {
|
|
|
2678
2726
|
if (mine?.status !== "creating" || mine.instanceId !== instanceId || mine.attr !== undefined) {
|
|
2679
2727
|
return;
|
|
2680
2728
|
}
|
|
2681
|
-
yield* found.store.delete({ ...found.where, fqn }).pipe(
|
|
2729
|
+
yield* found.store.delete({ ...found.where, fqn }).pipe(Effect34.ignore);
|
|
2682
2730
|
});
|
|
2683
2731
|
|
|
2684
2732
|
// src/ownership/adopt.ts
|
|
2685
|
-
var adoptEnabled = (fqn) =>
|
|
2686
|
-
const stack = yield*
|
|
2733
|
+
var adoptEnabled = (fqn) => Effect35.gen(function* () {
|
|
2734
|
+
const stack = yield* Effect35.serviceOption(Stack3);
|
|
2687
2735
|
const scoped = Option5.isSome(stack) ? stack.value.resources[fqn]?.Adopt : undefined;
|
|
2688
2736
|
if (scoped !== undefined)
|
|
2689
2737
|
return scoped;
|
|
2690
|
-
const policy = yield*
|
|
2738
|
+
const policy = yield* Effect35.serviceOption(AdoptPolicy);
|
|
2691
2739
|
if (Option5.isSome(policy))
|
|
2692
2740
|
return policy.value;
|
|
2693
|
-
const context = yield*
|
|
2741
|
+
const context = yield* Effect35.serviceOption(AlchemyContext);
|
|
2694
2742
|
return Option5.isSome(context) ? context.value.adopt : false;
|
|
2695
2743
|
});
|
|
2696
|
-
var adoptable = (owner) =>
|
|
2744
|
+
var adoptable = (owner) => Effect35.gen(function* () {
|
|
2697
2745
|
if (yield* isCreate(owner.fqn, owner.instanceId))
|
|
2698
2746
|
return true;
|
|
2699
2747
|
return yield* unfinished(owner.instanceId);
|
|
2700
2748
|
});
|
|
2701
|
-
var adoptsAtApply = (owner) =>
|
|
2749
|
+
var adoptsAtApply = (owner) => Effect35.gen(function* () {
|
|
2702
2750
|
if (!(yield* adoptEnabled(owner.fqn)))
|
|
2703
2751
|
return false;
|
|
2704
2752
|
return yield* adoptable(owner);
|
|
2705
2753
|
});
|
|
2706
|
-
var refuseTakeover = (owner, what) =>
|
|
2754
|
+
var refuseTakeover = (owner, what) => Effect35.gen(function* () {
|
|
2707
2755
|
if (owner.output !== undefined)
|
|
2708
2756
|
return;
|
|
2709
2757
|
if (yield* resumes(owner.instanceId))
|
|
@@ -2713,25 +2761,25 @@ var refuseTakeover = (owner, what) => Effect34.gen(function* () {
|
|
|
2713
2761
|
return;
|
|
2714
2762
|
yield* forgetRefusedCreate(owner.fqn, owner.instanceId);
|
|
2715
2763
|
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.";
|
|
2716
|
-
return yield*
|
|
2764
|
+
return yield* Effect35.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}`));
|
|
2717
2765
|
});
|
|
2718
2766
|
var claimFor = (owner) => (what) => refuseTakeover(owner, what);
|
|
2719
2767
|
|
|
2720
2768
|
// src/ownership/probe.ts
|
|
2721
2769
|
import { Unowned as Unowned2 } from "alchemy/AdoptPolicy";
|
|
2722
|
-
import * as
|
|
2723
|
-
var provenOurs = (ask, settled2) =>
|
|
2770
|
+
import * as Effect36 from "effect/Effect";
|
|
2771
|
+
var provenOurs = (ask, settled2) => Effect36.gen(function* () {
|
|
2724
2772
|
const recorded = yield* recordedInstance(ask.fqn, ask.instanceId);
|
|
2725
2773
|
if (recorded === "absent")
|
|
2726
2774
|
return false;
|
|
2727
2775
|
if (recorded === "partial") {
|
|
2728
|
-
yield*
|
|
2776
|
+
yield* Effect36.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");
|
|
2729
2777
|
return false;
|
|
2730
2778
|
}
|
|
2731
|
-
const unproven = (reason) =>
|
|
2732
|
-
return yield* settled2.pipe(
|
|
2779
|
+
const unproven = (reason) => Effect36.as(Effect36.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);
|
|
2780
|
+
return yield* settled2.pipe(Effect36.catch(unproven), Effect36.catchDefect(unproven));
|
|
2733
2781
|
});
|
|
2734
|
-
var ownedRead = (ask, found, settled2) =>
|
|
2782
|
+
var ownedRead = (ask, found, settled2) => Effect36.gen(function* () {
|
|
2735
2783
|
if (found === undefined || ask.output !== undefined)
|
|
2736
2784
|
return found;
|
|
2737
2785
|
return (yield* provenOurs(ask, settled2)) ? found : Unowned2(found);
|
|
@@ -2789,8 +2837,8 @@ var optionMap = (value, family, defaultKey) => {
|
|
|
2789
2837
|
return out;
|
|
2790
2838
|
};
|
|
2791
2839
|
var sameMap = (left, right) => left.size === right.size && [...left].every(([key, value]) => right.get(key) === value);
|
|
2792
|
-
var sortedForm = (
|
|
2793
|
-
var printMap = (
|
|
2840
|
+
var sortedForm = (map14) => [...map14].map(([key, value]) => `${key}=${value}`).sort().join(",");
|
|
2841
|
+
var printMap = (map14, lead = []) => [...lead.filter((key) => map14.has(key)), ...[...map14.keys()].filter((k) => !lead.includes(k))].map((key) => `${key}=${map14.get(key) ?? ""}`).join(",");
|
|
2794
2842
|
var netMaps = (declared, live) => {
|
|
2795
2843
|
const want = optionMap(declared, "net");
|
|
2796
2844
|
const have = optionMap(live, "net");
|
|
@@ -3186,11 +3234,11 @@ var identityRefusals = (news, olds, output) => {
|
|
|
3186
3234
|
};
|
|
3187
3235
|
|
|
3188
3236
|
// src/proxmox/lxc-adoption.ts
|
|
3189
|
-
import * as
|
|
3237
|
+
import * as Effect41 from "effect/Effect";
|
|
3190
3238
|
|
|
3191
3239
|
// src/ownership/adopting.ts
|
|
3192
|
-
import * as
|
|
3193
|
-
var adopting = (owner, settled2) =>
|
|
3240
|
+
import * as Effect37 from "effect/Effect";
|
|
3241
|
+
var adopting = (owner, settled2) => Effect37.gen(function* () {
|
|
3194
3242
|
if (owner.output === undefined)
|
|
3195
3243
|
return false;
|
|
3196
3244
|
const generation = yield* recordedGeneration(owner.fqn, owner.instanceId);
|
|
@@ -3202,7 +3250,7 @@ var adopting = (owner, settled2) => Effect36.gen(function* () {
|
|
|
3202
3250
|
});
|
|
3203
3251
|
|
|
3204
3252
|
// src/proxmox/lxc-lifecycle.ts
|
|
3205
|
-
import * as
|
|
3253
|
+
import * as Effect40 from "effect/Effect";
|
|
3206
3254
|
import * as nodes10 from "@distilled.cloud/proxmox/nodes";
|
|
3207
3255
|
|
|
3208
3256
|
// src/proxmox/lxc-errors.ts
|
|
@@ -3211,44 +3259,44 @@ class LxcRefusedError extends Error {
|
|
|
3211
3259
|
}
|
|
3212
3260
|
|
|
3213
3261
|
// src/proxmox/lxc-read.ts
|
|
3214
|
-
import * as
|
|
3262
|
+
import * as cluster5 from "@distilled.cloud/proxmox/cluster";
|
|
3215
3263
|
import * as nodes8 from "@distilled.cloud/proxmox/nodes";
|
|
3216
|
-
import * as
|
|
3217
|
-
var readLive = (where) =>
|
|
3264
|
+
import * as Effect38 from "effect/Effect";
|
|
3265
|
+
var readLive = (where) => Effect38.gen(function* () {
|
|
3218
3266
|
const credential = yield* leased(where.target, "read");
|
|
3219
|
-
const answer = yield* runPveWith(where.target, credential, false, nodes8.getNodeLxcConfig({ node: where.node, vmid: String(where.vmid) })).pipe(
|
|
3267
|
+
const answer = yield* runPveWith(where.target, credential, false, nodes8.getNodeLxcConfig({ node: where.node, vmid: String(where.vmid) })).pipe(Effect38.map((data) => ({ data, error: undefined })), Effect38.catchTag("LxcConfigNotFound", (error) => Effect38.succeed({ data: undefined, error })));
|
|
3220
3268
|
if (answer.error === undefined) {
|
|
3221
3269
|
const live = answer.data;
|
|
3222
3270
|
if (live === null || typeof live !== "object" || Array.isArray(live) || !("digest" in live) || typeof live.digest !== "string" || live.digest === "") {
|
|
3223
|
-
return yield*
|
|
3271
|
+
return yield* Effect38.fail(new LxcRefusedError(`CT ${String(where.vmid)}: config read did not return a valid digest-bearing config.`));
|
|
3224
3272
|
}
|
|
3225
3273
|
return live;
|
|
3226
3274
|
}
|
|
3227
|
-
const rows = yield* runPveWith(where.target, credential, false,
|
|
3275
|
+
const rows = yield* runPveWith(where.target, credential, false, cluster5.listClusterResources({ type: "vm" }));
|
|
3228
3276
|
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))) {
|
|
3229
|
-
return yield*
|
|
3277
|
+
return yield* Effect38.fail(new LxcRefusedError("The cluster guest index did not identify its rows."));
|
|
3230
3278
|
}
|
|
3231
3279
|
const found = rows.find((row) => Number(row.vmid) === where.vmid);
|
|
3232
3280
|
if (found !== undefined && found.node === where.node && found.type === "lxc") {
|
|
3233
|
-
return yield*
|
|
3281
|
+
return yield* Effect38.fail(answer.error);
|
|
3234
3282
|
}
|
|
3235
3283
|
if (found !== undefined) {
|
|
3236
|
-
return yield*
|
|
3284
|
+
return yield* Effect38.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."));
|
|
3237
3285
|
}
|
|
3238
3286
|
return;
|
|
3239
3287
|
});
|
|
3240
3288
|
|
|
3241
3289
|
// src/proxmox/lxc-task.ts
|
|
3242
3290
|
import * as nodes9 from "@distilled.cloud/proxmox/nodes";
|
|
3243
|
-
import * as
|
|
3244
|
-
var lxcTask = (where, operation, what, polls) =>
|
|
3291
|
+
import * as Effect39 from "effect/Effect";
|
|
3292
|
+
var lxcTask = (where, operation, what, polls) => Effect39.gen(function* () {
|
|
3245
3293
|
const credential = yield* mint(where.target, "provision");
|
|
3246
3294
|
const upid = text(yield* runPveWith(where.target, credential, true, operation));
|
|
3247
3295
|
if (upid === "")
|
|
3248
|
-
return yield*
|
|
3296
|
+
return yield* Effect39.fail(new LxcRefusedError(`${what}: PVE returned no task id, so the write cannot be confirmed and is not claimed.`));
|
|
3249
3297
|
let exit = "still running when this provider stopped waiting";
|
|
3250
3298
|
for (let attempt = 0;attempt < polls; attempt++) {
|
|
3251
|
-
const status = yield* runPveWith(where.target, credential, false, nodes9.getNodeTaskStatus({ node: where.node, upid })).pipe(
|
|
3299
|
+
const status = yield* runPveWith(where.target, credential, false, nodes9.getNodeTaskStatus({ node: where.node, upid })).pipe(Effect39.orElseSucceed(() => {
|
|
3252
3300
|
return;
|
|
3253
3301
|
}));
|
|
3254
3302
|
if (status === undefined || status.status === undefined) {
|
|
@@ -3259,10 +3307,10 @@ var lxcTask = (where, operation, what, polls) => Effect38.gen(function* () {
|
|
|
3259
3307
|
exit = text(status.exitstatus, "stopped with no exitstatus");
|
|
3260
3308
|
break;
|
|
3261
3309
|
}
|
|
3262
|
-
yield*
|
|
3310
|
+
yield* Effect39.sleep("1 second");
|
|
3263
3311
|
}
|
|
3264
3312
|
if (exit !== "OK")
|
|
3265
|
-
return yield*
|
|
3313
|
+
return yield* Effect39.fail(new LxcRefusedError(`${what}: task ${upid} ended "${exit}". Read \`pvesh get /nodes/${where.node}/tasks/` + `${upid}/log\` before running the deploy again.`));
|
|
3266
3314
|
});
|
|
3267
3315
|
|
|
3268
3316
|
// src/proxmox/lxc-lifecycle.ts
|
|
@@ -3273,13 +3321,13 @@ var LXC_ENDPOINTS = {
|
|
|
3273
3321
|
};
|
|
3274
3322
|
var CREATE_POLLS = 180;
|
|
3275
3323
|
var TASK_POLLS = 60;
|
|
3276
|
-
var
|
|
3324
|
+
var attributesOf6 = (where, live) => ({
|
|
3277
3325
|
config: storedConfig(live),
|
|
3278
3326
|
node: where.node,
|
|
3279
3327
|
rawKeys: rawKeysOf(live),
|
|
3280
3328
|
vmid: where.vmid
|
|
3281
3329
|
});
|
|
3282
|
-
var createGuest = (props) =>
|
|
3330
|
+
var createGuest = (props) => Effect40.gen(function* () {
|
|
3283
3331
|
const form = createForm4(props);
|
|
3284
3332
|
yield* guardForm(LXC_ENDPOINTS.create, form, true);
|
|
3285
3333
|
return yield* lxcTask(props, nodes10.createNodeLxc({
|
|
@@ -3289,7 +3337,7 @@ var createGuest = (props) => Effect39.gen(function* () {
|
|
|
3289
3337
|
ostemplate: text(form["ostemplate"])
|
|
3290
3338
|
}), `create CT ${String(props.vmid)}`, CREATE_POLLS);
|
|
3291
3339
|
});
|
|
3292
|
-
var updateGuest = (props, change, digest) =>
|
|
3340
|
+
var updateGuest = (props, change, digest) => Effect40.gen(function* () {
|
|
3293
3341
|
if (Object.keys(change.put).length > 0 || change.clear.length > 0) {
|
|
3294
3342
|
const form = withClears({ ...change.put, ...digest === "" ? {} : { digest } }, change.clear);
|
|
3295
3343
|
yield* guardForm(LXC_ENDPOINTS.update, form, false);
|
|
@@ -3301,47 +3349,47 @@ var updateGuest = (props, change, digest) => Effect39.gen(function* () {
|
|
|
3301
3349
|
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);
|
|
3302
3350
|
}
|
|
3303
3351
|
});
|
|
3304
|
-
var destroyGuest = (where, declared) =>
|
|
3352
|
+
var destroyGuest = (where, declared) => Effect40.gen(function* () {
|
|
3305
3353
|
const live = yield* readLive(where);
|
|
3306
3354
|
if (live === undefined)
|
|
3307
3355
|
return;
|
|
3308
3356
|
const drift = judge(declared, live).drift;
|
|
3309
3357
|
if (drift.length > 0) {
|
|
3310
|
-
return yield*
|
|
3358
|
+
return yield* Effect40.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."));
|
|
3311
3359
|
}
|
|
3312
3360
|
yield* lxcTask(where, nodes10.deleteNodeLxc({ node: where.node, vmid: String(where.vmid) }), `destroy CT ${String(where.vmid)}`, TASK_POLLS);
|
|
3313
3361
|
});
|
|
3314
3362
|
|
|
3315
3363
|
// src/proxmox/lxc-adoption.ts
|
|
3316
3364
|
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.";
|
|
3317
|
-
var refuseAdoptedDrift = (owner, props, live, change) =>
|
|
3365
|
+
var refuseAdoptedDrift = (owner, props, live, change) => Effect41.gen(function* () {
|
|
3318
3366
|
if (change.drift.length === 0)
|
|
3319
3367
|
return;
|
|
3320
|
-
const settled2 = (recorded) =>
|
|
3368
|
+
const settled2 = (recorded) => Effect41.sync(() => judge(recorded, live).drift.length === 0);
|
|
3321
3369
|
if (!(yield* adopting(owner, settled2)))
|
|
3322
3370
|
return;
|
|
3323
|
-
return yield*
|
|
3371
|
+
return yield* Effect41.fail(new LxcRefusedError(adoptionRefusal(props, change.drift)));
|
|
3324
3372
|
});
|
|
3325
3373
|
|
|
3326
3374
|
// src/proxmox/lxc.ts
|
|
3327
3375
|
var ProxmoxLxc = Resource13("Proxmox.Lxc", { defaultRemovalPolicy: "retain" });
|
|
3328
|
-
var refused = (reasons) =>
|
|
3376
|
+
var refused = (reasons) => Effect42.fail(new LxcRefusedError(reasons.join(`
|
|
3329
3377
|
`)));
|
|
3330
|
-
var diff = (owner, news, olds) =>
|
|
3378
|
+
var diff = (owner, news, olds) => Effect42.gen(function* () {
|
|
3331
3379
|
const { output } = owner;
|
|
3332
3380
|
const identity2 = identityRefusals(news, olds, output);
|
|
3333
3381
|
if (identity2.length > 0)
|
|
3334
3382
|
return yield* refused(identity2);
|
|
3335
3383
|
if (output === undefined)
|
|
3336
3384
|
return;
|
|
3337
|
-
if (!
|
|
3385
|
+
if (!isResolved12(news))
|
|
3338
3386
|
return;
|
|
3339
3387
|
const live = yield* readLive(news);
|
|
3340
3388
|
if (live === undefined) {
|
|
3341
3389
|
const refusals = createRefusals(news);
|
|
3342
3390
|
if (refusals.length > 0)
|
|
3343
3391
|
return yield* refused(refusals);
|
|
3344
|
-
yield*
|
|
3392
|
+
yield* Effect42.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.");
|
|
3345
3393
|
return { action: "update" };
|
|
3346
3394
|
}
|
|
3347
3395
|
const change = judge(news, live);
|
|
@@ -3352,10 +3400,10 @@ var diff = (owner, news, olds) => Effect41.gen(function* () {
|
|
|
3352
3400
|
if (change.drift.length === 0 && !moved)
|
|
3353
3401
|
return { action: "noop" };
|
|
3354
3402
|
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.";
|
|
3355
|
-
yield*
|
|
3403
|
+
yield* Effect42.logWarning(`Proxmox.Lxc ${news.node}/${String(news.vmid)}: ${what}`);
|
|
3356
3404
|
return { action: "update" };
|
|
3357
3405
|
});
|
|
3358
|
-
var reconcile = (owner, news) =>
|
|
3406
|
+
var reconcile = (owner, news) => Effect42.gen(function* () {
|
|
3359
3407
|
const { output } = owner;
|
|
3360
3408
|
const identity2 = identityRefusals(news, news, output);
|
|
3361
3409
|
if (identity2.length > 0)
|
|
@@ -3394,22 +3442,22 @@ var reconcile = (owner, news) => Effect41.gen(function* () {
|
|
|
3394
3442
|
`CT ${String(news.vmid)}: after the write, ${left.join(", ")} still differ from the ` + "declaration. PVE may have normalised the value (tags are lower-cased by default) -- " + "declare what it stores."
|
|
3395
3443
|
]);
|
|
3396
3444
|
}
|
|
3397
|
-
return
|
|
3445
|
+
return attributesOf6(news, after);
|
|
3398
3446
|
});
|
|
3399
3447
|
var whereOf = (olds, output) => ({
|
|
3400
3448
|
node: output?.node ?? olds.node,
|
|
3401
3449
|
target: olds.target,
|
|
3402
3450
|
vmid: output?.vmid ?? olds.vmid
|
|
3403
3451
|
});
|
|
3404
|
-
var read = (owner, olds) =>
|
|
3452
|
+
var read = (owner, olds) => Effect42.gen(function* () {
|
|
3405
3453
|
const where = whereOf(olds, owner.output);
|
|
3406
3454
|
const live = yield* readLive(where);
|
|
3407
3455
|
if (live === undefined)
|
|
3408
3456
|
return;
|
|
3409
|
-
const settled2 =
|
|
3410
|
-
return yield* ownedRead(owner,
|
|
3457
|
+
const settled2 = Effect42.sync(() => judge(olds, live).drift.length === 0);
|
|
3458
|
+
return yield* ownedRead(owner, attributesOf6(where, live), settled2);
|
|
3411
3459
|
});
|
|
3412
|
-
var
|
|
3460
|
+
var handlers4 = {
|
|
3413
3461
|
delete: ({ olds, output }) => destroyGuest(whereOf(olds, output), olds),
|
|
3414
3462
|
diff: ({
|
|
3415
3463
|
fqn,
|
|
@@ -3418,7 +3466,7 @@ var handlers5 = {
|
|
|
3418
3466
|
olds,
|
|
3419
3467
|
output
|
|
3420
3468
|
}) => diff({ fqn, instanceId, output }, news, olds),
|
|
3421
|
-
list: () =>
|
|
3469
|
+
list: () => Effect42.succeed([]),
|
|
3422
3470
|
read: ({
|
|
3423
3471
|
fqn,
|
|
3424
3472
|
instanceId,
|
|
@@ -3432,11 +3480,11 @@ var handlers5 = {
|
|
|
3432
3480
|
output
|
|
3433
3481
|
}) => reconcile({ fqn, instanceId, output }, news)
|
|
3434
3482
|
};
|
|
3435
|
-
var ProxmoxLxcProvider = () => Provider13.effect(ProxmoxLxc,
|
|
3483
|
+
var ProxmoxLxcProvider = () => Provider13.effect(ProxmoxLxc, Effect42.succeed(ProxmoxLxc.Provider.of(handlers4)).pipe(Effect42.map(provingResumes)));
|
|
3436
3484
|
// src/proxmox/metric-server.ts
|
|
3437
3485
|
import { Resource as Resource14 } from "alchemy";
|
|
3438
|
-
import { isResolved as
|
|
3439
|
-
import * as
|
|
3486
|
+
import { isResolved as isResolved13 } from "alchemy/Diff";
|
|
3487
|
+
import * as cluster7 from "@distilled.cloud/proxmox/cluster";
|
|
3440
3488
|
|
|
3441
3489
|
// src/proxmox/metric-server-form.ts
|
|
3442
3490
|
var UNSET2 = -1;
|
|
@@ -3550,15 +3598,15 @@ var metricServerSpec = {
|
|
|
3550
3598
|
};
|
|
3551
3599
|
|
|
3552
3600
|
// src/proxmox/metric-server-distilled.ts
|
|
3553
|
-
import * as
|
|
3601
|
+
import * as cluster6 from "@distilled.cloud/proxmox/cluster";
|
|
3554
3602
|
import { ProxmoxParseError as ProxmoxParseError3 } from "@distilled.cloud/proxmox/Errors";
|
|
3555
|
-
import * as
|
|
3603
|
+
import * as Effect43 from "effect/Effect";
|
|
3556
3604
|
import * as Schema2 from "effect/Schema";
|
|
3557
|
-
var readMetricServerRow = (props) => runPve(props.target, "read", false,
|
|
3605
|
+
var readMetricServerRow = (props) => runPve(props.target, "read", false, cluster6.getClusterMetricsServer({ id: props.id })).pipe(Effect43.flatMap((body4) => Schema2.decodeUnknownEffect(Schema2.Record(Schema2.String, Schema2.Unknown))(body4).pipe(Effect43.mapError(() => new ProxmoxParseError3({
|
|
3558
3606
|
body: undefined,
|
|
3559
3607
|
cause: "Metric server response must be an object"
|
|
3560
|
-
})))),
|
|
3561
|
-
var readMetricServer = (props) => readMetricServerRow(props).pipe(
|
|
3608
|
+
})))), Effect43.catchTag("MetricServerNotFound", () => Effect43.succeed(undefined)), Effect43.catchTag("NotFound", () => Effect43.succeed(undefined)));
|
|
3609
|
+
var readMetricServer = (props) => readMetricServerRow(props).pipe(Effect43.map((row) => row === undefined ? undefined : metricServerSpec.attributes(row, props)));
|
|
3562
3610
|
var metricServerRequest = (props, form) => ({
|
|
3563
3611
|
...asForm2({
|
|
3564
3612
|
api_path_prefix: form["api-path-prefix"],
|
|
@@ -3583,22 +3631,22 @@ var metricServerRequest = (props, form) => ({
|
|
|
3583
3631
|
port: String(props.port),
|
|
3584
3632
|
server: props.server
|
|
3585
3633
|
});
|
|
3586
|
-
var deleteMetricServer = (props) =>
|
|
3634
|
+
var deleteMetricServer = (props) => Effect43.gen(function* () {
|
|
3587
3635
|
if ((yield* readMetricServerRow(props)) === undefined)
|
|
3588
3636
|
return;
|
|
3589
|
-
yield* runPve(props.target, "provision", true,
|
|
3637
|
+
yield* runPve(props.target, "provision", true, cluster6.deleteClusterMetricsServer({ id: props.id }));
|
|
3590
3638
|
});
|
|
3591
3639
|
|
|
3592
3640
|
// src/proxmox/metric-server.ts
|
|
3593
3641
|
import * as Provider14 from "alchemy/Provider";
|
|
3594
|
-
import * as
|
|
3642
|
+
import * as Effect44 from "effect/Effect";
|
|
3595
3643
|
var ProxmoxMetricServer = Resource14("Proxmox.MetricServer");
|
|
3596
3644
|
var { guardCreate: guardCreate3, guardUpdate: guardUpdate3 } = specGuards(metricServerSpec);
|
|
3597
|
-
var ProxmoxMetricServerProvider = () => Provider14.effect(ProxmoxMetricServer,
|
|
3598
|
-
list: () =>
|
|
3645
|
+
var ProxmoxMetricServerProvider = () => Provider14.effect(ProxmoxMetricServer, Effect44.succeed(ProxmoxMetricServer.Provider.of({
|
|
3646
|
+
list: () => Effect44.succeed([]),
|
|
3599
3647
|
read: ({ olds }) => readMetricServer(olds),
|
|
3600
|
-
diff:
|
|
3601
|
-
if (!
|
|
3648
|
+
diff: Effect44.fn(function* ({ news, output }) {
|
|
3649
|
+
if (!isResolved13(news))
|
|
3602
3650
|
return;
|
|
3603
3651
|
yield* guardCreate3(news, output === undefined);
|
|
3604
3652
|
yield* guardUpdate3(news);
|
|
@@ -3611,36 +3659,36 @@ var ProxmoxMetricServerProvider = () => Provider14.effect(ProxmoxMetricServer, E
|
|
|
3611
3659
|
}
|
|
3612
3660
|
return { action: metricServerSpec.matches(live, news) ? "noop" : "update" };
|
|
3613
3661
|
}),
|
|
3614
|
-
reconcile:
|
|
3662
|
+
reconcile: Effect44.fn(function* ({ news }) {
|
|
3615
3663
|
const live = yield* readMetricServer(news);
|
|
3616
3664
|
yield* guardCreate3(news, live === undefined);
|
|
3617
3665
|
yield* guardUpdate3(news);
|
|
3618
3666
|
if (live === undefined) {
|
|
3619
|
-
yield* runPve(news.target, "provision", true,
|
|
3667
|
+
yield* runPve(news.target, "provision", true, cluster7.updateClusterMetricsServer({
|
|
3620
3668
|
...metricServerRequest(news, metricServerSpec.createForm(news)),
|
|
3621
3669
|
type: news.type
|
|
3622
3670
|
}));
|
|
3623
3671
|
} else {
|
|
3624
3672
|
const form = formToSend(metricServerSpec.matches, live, news, metricServerSpec.updateForm(news));
|
|
3625
3673
|
if (form !== undefined)
|
|
3626
|
-
yield* runPve(news.target, "provision", true,
|
|
3674
|
+
yield* runPve(news.target, "provision", true, cluster7.putClusterMetricsServer(metricServerRequest(news, form)));
|
|
3627
3675
|
}
|
|
3628
3676
|
const after = yield* readMetricServer(news);
|
|
3629
3677
|
if (after === undefined)
|
|
3630
|
-
return yield*
|
|
3678
|
+
return yield* Effect44.fail(new Error(`${metricServerSpec.path(news)}: write returned success but the resource is still absent`));
|
|
3631
3679
|
return after;
|
|
3632
3680
|
}),
|
|
3633
3681
|
delete: ({ olds }) => deleteMetricServer(olds)
|
|
3634
3682
|
})));
|
|
3635
3683
|
// src/proxmox/network-apply.ts
|
|
3636
3684
|
import { Resource as Resource15 } from "alchemy";
|
|
3637
|
-
import { isResolved as
|
|
3685
|
+
import { isResolved as isResolved14 } from "alchemy/Diff";
|
|
3638
3686
|
import * as Provider15 from "alchemy/Provider";
|
|
3639
|
-
import * as
|
|
3687
|
+
import * as Effect47 from "effect/Effect";
|
|
3640
3688
|
|
|
3641
3689
|
// src/proxmox/network-apply-run.ts
|
|
3642
3690
|
import * as nodes12 from "@distilled.cloud/proxmox/nodes";
|
|
3643
|
-
import * as
|
|
3691
|
+
import * as Effect46 from "effect/Effect";
|
|
3644
3692
|
|
|
3645
3693
|
// src/proxmox/apply-endpoints.ts
|
|
3646
3694
|
var NETWORK_APPLY_ENDPOINT = "pve:PUT /nodes/{node}/network";
|
|
@@ -3649,10 +3697,10 @@ var guardNetworkApply = guardForm(NETWORK_APPLY_ENDPOINT, {}, true);
|
|
|
3649
3697
|
var guardSdnApply = guardForm(SDN_APPLY_ENDPOINT, {}, true);
|
|
3650
3698
|
|
|
3651
3699
|
// src/proxmox/network-apply-read.ts
|
|
3652
|
-
import * as
|
|
3700
|
+
import * as cluster8 from "@distilled.cloud/proxmox/cluster";
|
|
3653
3701
|
import * as nodes11 from "@distilled.cloud/proxmox/nodes";
|
|
3654
|
-
import * as
|
|
3655
|
-
var stagedDiff = (target, node) =>
|
|
3702
|
+
import * as Effect45 from "effect/Effect";
|
|
3703
|
+
var stagedDiff = (target, node) => Effect45.gen(function* () {
|
|
3656
3704
|
const credential = yield* leased(target, "read");
|
|
3657
3705
|
const live = yield* runPveWith(target, credential, false, nodes11.listNodeNetwork({ node }));
|
|
3658
3706
|
return Array.isArray(live) ? "" : live.changes;
|
|
@@ -3664,36 +3712,36 @@ var pendingLines = (diff2) => {
|
|
|
3664
3712
|
`).filter((line) => (line.startsWith("+") || line.startsWith("-")) && !line.startsWith("+++") && !line.startsWith("---")).length;
|
|
3665
3713
|
return Math.max(changed, 1);
|
|
3666
3714
|
};
|
|
3667
|
-
var pendingCount = (target, node) => stagedDiff(target, node).pipe(
|
|
3668
|
-
var clusterHealth = (target) => runPve(target, "read", false,
|
|
3715
|
+
var pendingCount = (target, node) => stagedDiff(target, node).pipe(Effect45.map(pendingLines));
|
|
3716
|
+
var clusterHealth = (target) => runPve(target, "read", false, cluster8.listClusterStatus({})).pipe(Effect45.map((rows) => {
|
|
3669
3717
|
const all = rows ?? [];
|
|
3670
|
-
const
|
|
3718
|
+
const cluster9 = all.find((row) => row.type === "cluster");
|
|
3671
3719
|
return {
|
|
3672
3720
|
offline: all.filter((row) => row.type === "node" && !bool(row.online)).map((row) => text(row.name, "<unnamed>")),
|
|
3673
|
-
quorate:
|
|
3721
|
+
quorate: cluster9 === undefined ? true : bool(cluster9.quorate)
|
|
3674
3722
|
};
|
|
3675
3723
|
}));
|
|
3676
|
-
var degradedReason = (target) => clusterHealth(target).pipe(
|
|
3724
|
+
var degradedReason = (target) => clusterHealth(target).pipe(Effect45.map((health) => health.quorate && health.offline.length === 0 ? undefined : `quorate=${String(health.quorate)} offline=[${health.offline.join(", ")}]`));
|
|
3677
3725
|
var UNREACHABLE = "the task could not be read -- the node stopped answering, or this lease cannot see it";
|
|
3678
3726
|
var STILL_RUNNING = "still running when this provider stopped waiting";
|
|
3679
3727
|
var ATTEMPTS = 30;
|
|
3680
|
-
var awaitTask = (target, credential, node, upid, attempts = ATTEMPTS) =>
|
|
3728
|
+
var awaitTask = (target, credential, node, upid, attempts = ATTEMPTS) => Effect45.gen(function* () {
|
|
3681
3729
|
for (let attempt = 0;attempt < attempts; attempt += 1) {
|
|
3682
|
-
const status = yield* runPveWith(target, credential, false, nodes11.getNodeTaskStatus({ node, upid })).pipe(
|
|
3730
|
+
const status = yield* runPveWith(target, credential, false, nodes11.getNodeTaskStatus({ node, upid })).pipe(Effect45.orElseSucceed(() => {
|
|
3683
3731
|
return;
|
|
3684
3732
|
}));
|
|
3685
3733
|
if (status === undefined)
|
|
3686
3734
|
return UNREACHABLE;
|
|
3687
3735
|
if (status.status === "stopped")
|
|
3688
3736
|
return text(status.exitstatus, "stopped with no exitstatus");
|
|
3689
|
-
yield*
|
|
3737
|
+
yield* Effect45.sleep("1 second");
|
|
3690
3738
|
}
|
|
3691
3739
|
return STILL_RUNNING;
|
|
3692
3740
|
});
|
|
3693
3741
|
|
|
3694
3742
|
// src/proxmox/network-apply-run.ts
|
|
3695
|
-
var refuse = (node, why) =>
|
|
3696
|
-
var applyNetwork = (props) =>
|
|
3743
|
+
var refuse = (node, why) => Effect46.die(new Error(`Proxmox.NetworkApply ${node}: ${why}`));
|
|
3744
|
+
var applyNetwork = (props) => Effect46.gen(function* () {
|
|
3697
3745
|
const node = props.node;
|
|
3698
3746
|
const staged = yield* pendingCount(props.target, node);
|
|
3699
3747
|
if (staged === 0)
|
|
@@ -3725,27 +3773,27 @@ var applyNetwork = (props) => Effect45.gen(function* () {
|
|
|
3725
3773
|
|
|
3726
3774
|
// src/proxmox/network-apply.ts
|
|
3727
3775
|
var ProxmoxNetworkApply = Resource15("Proxmox.NetworkApply");
|
|
3728
|
-
var read2 = (props) => pendingCount(props.target, props.node).pipe(
|
|
3729
|
-
var diff2 = (news) =>
|
|
3730
|
-
if (!
|
|
3776
|
+
var read2 = (props) => pendingCount(props.target, props.node).pipe(Effect47.map((pending) => ({ node: props.node, pending })));
|
|
3777
|
+
var diff2 = (news) => Effect47.gen(function* () {
|
|
3778
|
+
if (!isResolved14(news))
|
|
3731
3779
|
return;
|
|
3732
3780
|
const { pending } = yield* read2(news);
|
|
3733
3781
|
return pending === 0 ? { action: "noop" } : { action: "update" };
|
|
3734
3782
|
});
|
|
3735
|
-
var
|
|
3736
|
-
delete: () =>
|
|
3783
|
+
var handlers5 = {
|
|
3784
|
+
delete: () => Effect47.void,
|
|
3737
3785
|
diff: ({ news }) => diff2(news),
|
|
3738
|
-
list: () =>
|
|
3786
|
+
list: () => Effect47.succeed([]),
|
|
3739
3787
|
read: ({ olds }) => read2(olds),
|
|
3740
3788
|
reconcile: ({ news }) => applyNetwork(news)
|
|
3741
3789
|
};
|
|
3742
|
-
var ProxmoxNetworkApplyProvider = () => Provider15.effect(ProxmoxNetworkApply,
|
|
3790
|
+
var ProxmoxNetworkApplyProvider = () => Provider15.effect(ProxmoxNetworkApply, Effect47.succeed(ProxmoxNetworkApply.Provider.of(handlers5)));
|
|
3743
3791
|
// src/proxmox/node-network.ts
|
|
3744
3792
|
import { Resource as Resource16 } from "alchemy";
|
|
3745
|
-
import { isResolved as
|
|
3793
|
+
import { isResolved as isResolved15 } from "alchemy/Diff";
|
|
3746
3794
|
import * as Provider16 from "alchemy/Provider";
|
|
3747
3795
|
import * as nodes14 from "@distilled.cloud/proxmox/nodes";
|
|
3748
|
-
import * as
|
|
3796
|
+
import * as Effect49 from "effect/Effect";
|
|
3749
3797
|
|
|
3750
3798
|
// src/proxmox/node-network-form.ts
|
|
3751
3799
|
var NODE_NETWORK_CREATE = "pve:POST /nodes/{node}/network";
|
|
@@ -3778,7 +3826,7 @@ var createForm5 = (props) => ({
|
|
|
3778
3826
|
...body4(props),
|
|
3779
3827
|
iface: props.iface
|
|
3780
3828
|
});
|
|
3781
|
-
var
|
|
3829
|
+
var updateForm3 = (props) => {
|
|
3782
3830
|
const clear = [
|
|
3783
3831
|
...props.cidr === undefined ? ["cidr"] : [],
|
|
3784
3832
|
...props.bridge_vlan_aware === false ? ["bridge_vlan_aware"] : []
|
|
@@ -3788,8 +3836,8 @@ var updateForm2 = (props) => {
|
|
|
3788
3836
|
|
|
3789
3837
|
// src/proxmox/node-network-wire.ts
|
|
3790
3838
|
import * as nodes13 from "@distilled.cloud/proxmox/nodes";
|
|
3791
|
-
import * as
|
|
3792
|
-
var
|
|
3839
|
+
import * as Effect48 from "effect/Effect";
|
|
3840
|
+
var attributesOf7 = (live, props) => {
|
|
3793
3841
|
const data = live;
|
|
3794
3842
|
const liveType = text(data["type"]);
|
|
3795
3843
|
if (liveType !== "" && liveType !== props.type)
|
|
@@ -3822,16 +3870,16 @@ var attributesOf6 = (live, props) => {
|
|
|
3822
3870
|
"vlan-raw-device": text(data["vlan-raw-device"])
|
|
3823
3871
|
};
|
|
3824
3872
|
};
|
|
3825
|
-
var readInterfaceOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, nodes13.getNodeNetwork({ iface: props.iface, node: props.node })).pipe(
|
|
3873
|
+
var readInterfaceOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, nodes13.getNodeNetwork({ iface: props.iface, node: props.node })).pipe(Effect48.catchTag("NetworkInterfaceNotFound", () => Effect48.succeed(undefined)))).pipe(Effect48.map((live) => {
|
|
3826
3874
|
if (live === UNREADABLE)
|
|
3827
3875
|
return UNREADABLE;
|
|
3828
|
-
return live === undefined ? undefined :
|
|
3876
|
+
return live === undefined ? undefined : attributesOf7(live, props);
|
|
3829
3877
|
}));
|
|
3830
|
-
var readInterface = (props) => readInterfaceOrFail(props).pipe(
|
|
3878
|
+
var readInterface = (props) => readInterfaceOrFail(props).pipe(Effect48.orElseSucceed(() => {
|
|
3831
3879
|
return;
|
|
3832
3880
|
}));
|
|
3833
3881
|
var dropUnreadable4 = (live) => live === UNREADABLE ? undefined : live;
|
|
3834
|
-
var
|
|
3882
|
+
var matches6 = (attributes, props) => attributes.cidr === (props.cidr ?? "") && same2(props.gateway, attributes.gateway) && same2(props.autostart, attributes.autostart) && same2(props.mtu, attributes.mtu) && sameComment(props.comments, attributes.comments) && sameList(props.bridge_ports, attributes.bridge_ports) && sameList(props.bridge_vids, attributes.bridge_vids) && same2(props.bridge_vlan_aware, attributes.bridge_vlan_aware) && sameList(props.slaves, attributes.slaves) && same2(props.bond_mode, attributes.bond_mode) && same2(props.bond_xmit_hash_policy, attributes.bond_xmit_hash_policy) && same2(props["bond-primary"], attributes["bond-primary"]) && same2(props["vlan-id"], attributes["vlan-id"]) && same2(props["vlan-raw-device"], attributes["vlan-raw-device"]);
|
|
3835
3883
|
|
|
3836
3884
|
// src/proxmox/node-network.ts
|
|
3837
3885
|
var ProxmoxNodeNetwork = Resource16("Proxmox.NodeNetwork", {
|
|
@@ -3848,60 +3896,60 @@ var toDistilledCreate3 = (props) => ({
|
|
|
3848
3896
|
node: props.node
|
|
3849
3897
|
});
|
|
3850
3898
|
var toDistilledUpdate2 = (props) => ({
|
|
3851
|
-
...toDistilled(
|
|
3899
|
+
...toDistilled(updateForm3(props)),
|
|
3852
3900
|
iface: props.iface,
|
|
3853
3901
|
node: props.node
|
|
3854
3902
|
});
|
|
3855
|
-
var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork,
|
|
3856
|
-
list: () =>
|
|
3857
|
-
read:
|
|
3903
|
+
var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork, Effect49.succeed(ProxmoxNodeNetwork.Provider.of({
|
|
3904
|
+
list: () => Effect49.succeed([]),
|
|
3905
|
+
read: Effect49.fn(function* ({ olds, output }) {
|
|
3858
3906
|
return dropUnreadable4(yield* output === undefined ? readInterface(olds) : readInterfaceOrFail(olds));
|
|
3859
3907
|
}),
|
|
3860
|
-
diff:
|
|
3861
|
-
if (!
|
|
3908
|
+
diff: Effect49.fn(function* ({ news, output }) {
|
|
3909
|
+
if (!isResolved15(news))
|
|
3862
3910
|
return;
|
|
3863
3911
|
yield* guardWrite2(NODE_NETWORK_CREATE, createForm5(news), output === undefined);
|
|
3864
|
-
yield* guardWrite2(NODE_NETWORK_UPDATE,
|
|
3912
|
+
yield* guardWrite2(NODE_NETWORK_UPDATE, updateForm3(news), false);
|
|
3865
3913
|
if (output === undefined)
|
|
3866
3914
|
return;
|
|
3867
3915
|
const live = yield* readInterfaceOrFail(news);
|
|
3868
3916
|
if (live === UNREADABLE) {
|
|
3869
|
-
yield* unreadableWarning("Proxmox.NodeNetwork", `${news.node}/${news.iface}
|
|
3917
|
+
yield* unreadableWarning("Proxmox.NodeNetwork", `${news.node}/${news.iface}`, news.target.mount, mintTier(news.target, "read"));
|
|
3870
3918
|
return { action: "noop" };
|
|
3871
3919
|
}
|
|
3872
3920
|
if (live === undefined) {
|
|
3873
3921
|
yield* guardWrite2(NODE_NETWORK_CREATE, createForm5(news), true);
|
|
3874
3922
|
return { action: "update" };
|
|
3875
3923
|
}
|
|
3876
|
-
return
|
|
3924
|
+
return matches6(live, news) ? { action: "noop" } : { action: "update" };
|
|
3877
3925
|
}),
|
|
3878
|
-
reconcile:
|
|
3926
|
+
reconcile: Effect49.fn(function* ({ news }) {
|
|
3879
3927
|
const before = dropUnreadable4(yield* readInterface(news));
|
|
3880
3928
|
yield* guardWrite2(NODE_NETWORK_CREATE, createForm5(news), before === undefined);
|
|
3881
|
-
yield* guardWrite2(NODE_NETWORK_UPDATE,
|
|
3929
|
+
yield* guardWrite2(NODE_NETWORK_UPDATE, updateForm3(news), false);
|
|
3882
3930
|
if (before === undefined) {
|
|
3883
3931
|
yield* runPve(news.target, "provision", true, nodes14.createNodeNetwork(toDistilledCreate3(news)));
|
|
3884
|
-
} else if (!
|
|
3932
|
+
} else if (!matches6(before, news)) {
|
|
3885
3933
|
yield* runPve(news.target, "provision", true, nodes14.putNodeNetwork2(toDistilledUpdate2(news)));
|
|
3886
3934
|
}
|
|
3887
3935
|
const after = dropUnreadable4(yield* readInterface(news));
|
|
3888
3936
|
if (after === undefined) {
|
|
3889
|
-
return yield*
|
|
3937
|
+
return yield* Effect49.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."));
|
|
3890
3938
|
}
|
|
3891
3939
|
return after;
|
|
3892
3940
|
}),
|
|
3893
|
-
delete:
|
|
3941
|
+
delete: Effect49.fn(function* ({ olds }) {
|
|
3894
3942
|
yield* runPve(olds.target, "provision", true, nodes14.deleteNodeNetwork2({ iface: olds.iface, node: olds.node }));
|
|
3895
3943
|
})
|
|
3896
3944
|
})));
|
|
3897
3945
|
// src/proxmox/notification-matcher.ts
|
|
3898
3946
|
import { Resource as Resource17 } from "alchemy";
|
|
3899
3947
|
import * as Provider17 from "alchemy/Provider";
|
|
3900
|
-
import * as
|
|
3948
|
+
import * as Effect53 from "effect/Effect";
|
|
3901
3949
|
|
|
3902
3950
|
// src/proxmox/notification-matcher-lifecycle.ts
|
|
3903
|
-
import { isResolved as
|
|
3904
|
-
import * as
|
|
3951
|
+
import { isResolved as isResolved16 } from "alchemy/Diff";
|
|
3952
|
+
import * as Effect52 from "effect/Effect";
|
|
3905
3953
|
|
|
3906
3954
|
// src/proxmox/notification-matcher-form.ts
|
|
3907
3955
|
var list = (value) => Array.isArray(value) ? value.map((item) => String(item)) : [];
|
|
@@ -3978,49 +4026,49 @@ var matcherUpdateForm = (fields) => {
|
|
|
3978
4026
|
};
|
|
3979
4027
|
|
|
3980
4028
|
// src/proxmox/notification-matcher-distilled.ts
|
|
3981
|
-
import * as
|
|
3982
|
-
import * as
|
|
4029
|
+
import * as cluster9 from "@distilled.cloud/proxmox/cluster";
|
|
4030
|
+
import * as Effect51 from "effect/Effect";
|
|
3983
4031
|
|
|
3984
4032
|
// src/proxmox/notification-sdk-input.ts
|
|
3985
4033
|
import { ProxmoxParseError as ProxmoxParseError4 } from "@distilled.cloud/proxmox/Errors";
|
|
3986
4034
|
import * as Data2 from "effect/Data";
|
|
3987
|
-
import * as
|
|
4035
|
+
import * as Effect50 from "effect/Effect";
|
|
3988
4036
|
import * as Schema3 from "effect/Schema";
|
|
3989
4037
|
|
|
3990
4038
|
class NotificationRequestInvalid extends Data2.TaggedError("NotificationRequestInvalid") {
|
|
3991
4039
|
}
|
|
3992
|
-
var notificationRequest = (schema, fields) => Schema3.decodeUnknownEffect(Schema3.toType(schema), { onExcessProperty: "error" })(Object.fromEntries(Object.entries(fields).map(([key, value]) => [key.replaceAll("-", "_"), value]))).pipe(
|
|
3993
|
-
var notificationResponse = (schema) => (body5) => Schema3.decodeUnknownEffect(Schema3.toType(schema))(body5).pipe(
|
|
4040
|
+
var notificationRequest = (schema, fields) => Schema3.decodeUnknownEffect(Schema3.toType(schema), { onExcessProperty: "error" })(Object.fromEntries(Object.entries(fields).map(([key, value]) => [key.replaceAll("-", "_"), value]))).pipe(Effect50.mapError(() => new NotificationRequestInvalid({ operation: "PVE notification write" })));
|
|
4041
|
+
var notificationResponse = (schema) => (body5) => Schema3.decodeUnknownEffect(Schema3.toType(schema))(body5).pipe(Effect50.as(body5), Effect50.mapError(() => new ProxmoxParseError4({
|
|
3994
4042
|
body: undefined,
|
|
3995
4043
|
cause: "PVE notification response does not match its SDK schema"
|
|
3996
4044
|
})));
|
|
3997
4045
|
|
|
3998
4046
|
// src/proxmox/notification-matcher-distilled.ts
|
|
3999
|
-
var readMatcher = (props) => runPve(props.target, "read", false,
|
|
4047
|
+
var readMatcher = (props) => runPve(props.target, "read", false, cluster9.getClusterNotificationMatcher({ name: props.name })).pipe(Effect51.flatMap(notificationResponse(cluster9.GetClusterNotificationMatcherResponse)), Effect51.map((live) => matcherAttributes({
|
|
4000
4048
|
...live,
|
|
4001
4049
|
"invert-match": live.invert_match,
|
|
4002
4050
|
"match-calendar": live.match_calendar,
|
|
4003
4051
|
"match-field": live.match_field,
|
|
4004
4052
|
"match-severity": live.match_severity
|
|
4005
|
-
}, props.name)),
|
|
4006
|
-
var createMatcher = (props) =>
|
|
4007
|
-
const input = yield* notificationRequest(
|
|
4008
|
-
yield* runPve(props.target, "provision", true,
|
|
4053
|
+
}, props.name)), Effect51.catchTag("NotFound", () => Effect51.succeed(undefined)));
|
|
4054
|
+
var createMatcher = (props) => Effect51.gen(function* () {
|
|
4055
|
+
const input = yield* notificationRequest(cluster9.CreateClusterNotificationMatcherRequest, matcherCreateForm(props));
|
|
4056
|
+
yield* runPve(props.target, "provision", true, cluster9.createClusterNotificationMatcher(input));
|
|
4009
4057
|
});
|
|
4010
|
-
var updateMatcher = (props) =>
|
|
4011
|
-
const input = yield* notificationRequest(
|
|
4058
|
+
var updateMatcher = (props) => Effect51.gen(function* () {
|
|
4059
|
+
const input = yield* notificationRequest(cluster9.PutClusterNotificationMatcherRequest, {
|
|
4012
4060
|
...matcherUpdateForm(props),
|
|
4013
4061
|
name: props.name
|
|
4014
4062
|
});
|
|
4015
|
-
yield* runPve(props.target, "provision", true,
|
|
4063
|
+
yield* runPve(props.target, "provision", true, cluster9.putClusterNotificationMatcher(input));
|
|
4016
4064
|
});
|
|
4017
|
-
var deleteMatcher = (props) => runPve(props.target, "provision", true,
|
|
4065
|
+
var deleteMatcher = (props) => runPve(props.target, "provision", true, cluster9.deleteClusterNotificationMatcher({ name: props.name })).pipe(Effect51.catchTag("NotFound", () => Effect51.void));
|
|
4018
4066
|
|
|
4019
4067
|
// src/proxmox/notification-matcher-lifecycle.ts
|
|
4020
|
-
var guard = (props, create) => guardForm("pve:POST /cluster/notifications/matchers", matcherCreateForm(props), create).pipe(
|
|
4068
|
+
var guard = (props, create) => guardForm("pve:POST /cluster/notifications/matchers", matcherCreateForm(props), create).pipe(Effect52.andThen(guardForm("pve:PUT /cluster/notifications/matchers/{name}", matcherUpdateForm(props), false)));
|
|
4021
4069
|
var matcherHandlers = {
|
|
4022
|
-
list: () =>
|
|
4023
|
-
read: ({ olds }) =>
|
|
4070
|
+
list: () => Effect52.succeed([]),
|
|
4071
|
+
read: ({ olds }) => Effect52.gen(function* () {
|
|
4024
4072
|
const live = yield* readMatcher(olds);
|
|
4025
4073
|
yield* guard(olds, live === undefined);
|
|
4026
4074
|
return live;
|
|
@@ -4029,8 +4077,8 @@ var matcherHandlers = {
|
|
|
4029
4077
|
news,
|
|
4030
4078
|
olds,
|
|
4031
4079
|
output
|
|
4032
|
-
}) =>
|
|
4033
|
-
if (!
|
|
4080
|
+
}) => Effect52.gen(function* () {
|
|
4081
|
+
if (!isResolved16(news))
|
|
4034
4082
|
return;
|
|
4035
4083
|
if (olds.name !== news.name)
|
|
4036
4084
|
return { action: "replace" };
|
|
@@ -4044,7 +4092,7 @@ var matcherHandlers = {
|
|
|
4044
4092
|
}
|
|
4045
4093
|
return { action: matcherMatches(live, news) ? "noop" : "update" };
|
|
4046
4094
|
}),
|
|
4047
|
-
reconcile: ({ news }) =>
|
|
4095
|
+
reconcile: ({ news }) => Effect52.gen(function* () {
|
|
4048
4096
|
const live = yield* readMatcher(news);
|
|
4049
4097
|
yield* guard(news, live === undefined);
|
|
4050
4098
|
if (live === undefined)
|
|
@@ -4053,7 +4101,7 @@ var matcherHandlers = {
|
|
|
4053
4101
|
yield* updateMatcher(news);
|
|
4054
4102
|
const after = yield* readMatcher(news);
|
|
4055
4103
|
if (after === undefined)
|
|
4056
|
-
return yield*
|
|
4104
|
+
return yield* Effect52.die(new Error(`Notification matcher ${news.name}: write returned but read-back is absent.`));
|
|
4057
4105
|
return after;
|
|
4058
4106
|
}),
|
|
4059
4107
|
delete: ({ olds }) => deleteMatcher(olds)
|
|
@@ -4061,15 +4109,15 @@ var matcherHandlers = {
|
|
|
4061
4109
|
|
|
4062
4110
|
// src/proxmox/notification-matcher.ts
|
|
4063
4111
|
var ProxmoxNotificationMatcher = Resource17("Proxmox.NotificationMatcher");
|
|
4064
|
-
var ProxmoxNotificationMatcherProvider = () => Provider17.effect(ProxmoxNotificationMatcher,
|
|
4112
|
+
var ProxmoxNotificationMatcherProvider = () => Provider17.effect(ProxmoxNotificationMatcher, Effect53.succeed(ProxmoxNotificationMatcher.Provider.of(matcherHandlers)));
|
|
4065
4113
|
// src/proxmox/notification-target.ts
|
|
4066
4114
|
import { Resource as Resource18 } from "alchemy";
|
|
4067
4115
|
import * as Provider18 from "alchemy/Provider";
|
|
4068
|
-
import * as
|
|
4116
|
+
import * as Effect56 from "effect/Effect";
|
|
4069
4117
|
|
|
4070
4118
|
// src/proxmox/notification-target-lifecycle.ts
|
|
4071
|
-
import { isResolved as
|
|
4072
|
-
import * as
|
|
4119
|
+
import { isResolved as isResolved17 } from "alchemy/Diff";
|
|
4120
|
+
import * as Effect55 from "effect/Effect";
|
|
4073
4121
|
|
|
4074
4122
|
// src/proxmox/notification-target-endpoint.ts
|
|
4075
4123
|
var ENDPOINTS = {
|
|
@@ -4152,30 +4200,30 @@ var targetSpec = {
|
|
|
4152
4200
|
};
|
|
4153
4201
|
|
|
4154
4202
|
// src/proxmox/notification-target-distilled.ts
|
|
4155
|
-
import * as
|
|
4156
|
-
import * as
|
|
4203
|
+
import * as cluster10 from "@distilled.cloud/proxmox/cluster";
|
|
4204
|
+
import * as Effect54 from "effect/Effect";
|
|
4157
4205
|
var read3 = (props) => {
|
|
4158
4206
|
const request = { name: props.name };
|
|
4159
4207
|
switch (props.type) {
|
|
4160
4208
|
case "gotify":
|
|
4161
|
-
return
|
|
4209
|
+
return cluster10.getClusterNotificationEndpointGotify(request).pipe(Effect54.flatMap(notificationResponse(cluster10.GetClusterNotificationEndpointGotifyResponse)), Effect54.map((row) => ({ ...row })));
|
|
4162
4210
|
case "sendmail":
|
|
4163
|
-
return
|
|
4211
|
+
return cluster10.getClusterNotificationEndpointSendmail(request).pipe(Effect54.flatMap(notificationResponse(cluster10.GetClusterNotificationEndpointSendmailResponse)), Effect54.map((row) => ({
|
|
4164
4212
|
...row,
|
|
4165
4213
|
"from-address": row.from_address,
|
|
4166
4214
|
"mailto-user": row.mailto_user
|
|
4167
4215
|
})));
|
|
4168
4216
|
case "smtp":
|
|
4169
|
-
return
|
|
4217
|
+
return cluster10.getClusterNotificationEndpointSmtp(request).pipe(Effect54.flatMap(notificationResponse(cluster10.GetClusterNotificationEndpointSmtpResponse)), Effect54.map((row) => ({
|
|
4170
4218
|
...row,
|
|
4171
4219
|
"from-address": row.from_address,
|
|
4172
4220
|
"mailto-user": row.mailto_user
|
|
4173
4221
|
})));
|
|
4174
4222
|
case "webhook":
|
|
4175
|
-
return
|
|
4223
|
+
return cluster10.getClusterNotificationEndpointWebhook(request).pipe(Effect54.flatMap(notificationResponse(cluster10.GetClusterNotificationEndpointWebhookResponse)), Effect54.map((row) => ({ ...row })));
|
|
4176
4224
|
}
|
|
4177
4225
|
};
|
|
4178
|
-
var readTarget = (props) => runPve(props.target, "read", false, read3(props)).pipe(
|
|
4226
|
+
var readTarget = (props) => runPve(props.target, "read", false, read3(props)).pipe(Effect54.map((row) => targetSpec.attributes(row, props)), Effect54.catchTag("NotFound", () => Effect54.succeed(undefined)));
|
|
4179
4227
|
var fields = (props, create) => {
|
|
4180
4228
|
const form = {
|
|
4181
4229
|
...create ? createShape(props) : shape4(props),
|
|
@@ -4209,40 +4257,40 @@ var fields = (props, create) => {
|
|
|
4209
4257
|
};
|
|
4210
4258
|
var validateTargetWrite = (props, create) => {
|
|
4211
4259
|
const schema = create ? {
|
|
4212
|
-
sendmail:
|
|
4213
|
-
smtp:
|
|
4214
|
-
gotify:
|
|
4215
|
-
webhook:
|
|
4260
|
+
sendmail: cluster10.CreateClusterNotificationEndpointSendmailRequest,
|
|
4261
|
+
smtp: cluster10.CreateClusterNotificationEndpointSmtpRequest,
|
|
4262
|
+
gotify: cluster10.CreateClusterNotificationEndpointGotifyRequest,
|
|
4263
|
+
webhook: cluster10.CreateClusterNotificationEndpointWebhookRequest
|
|
4216
4264
|
}[props.type] : {
|
|
4217
|
-
sendmail:
|
|
4218
|
-
smtp:
|
|
4219
|
-
gotify:
|
|
4220
|
-
webhook:
|
|
4265
|
+
sendmail: cluster10.PutClusterNotificationEndpointSendmailRequest,
|
|
4266
|
+
smtp: cluster10.PutClusterNotificationEndpointSmtpRequest,
|
|
4267
|
+
gotify: cluster10.PutClusterNotificationEndpointGotifyRequest,
|
|
4268
|
+
webhook: cluster10.PutClusterNotificationEndpointWebhookRequest
|
|
4221
4269
|
}[props.type];
|
|
4222
|
-
return notificationRequest(schema, fields(props, create)).pipe(
|
|
4270
|
+
return notificationRequest(schema, fields(props, create)).pipe(Effect54.asVoid);
|
|
4223
4271
|
};
|
|
4224
4272
|
var createTarget = (props) => {
|
|
4225
4273
|
const form = fields(props, true);
|
|
4226
|
-
const operation = props.type === "sendmail" ? notificationRequest(
|
|
4274
|
+
const operation = props.type === "sendmail" ? notificationRequest(cluster10.CreateClusterNotificationEndpointSendmailRequest, form).pipe(Effect54.flatMap(cluster10.createClusterNotificationEndpointSendmail)) : props.type === "smtp" ? notificationRequest(cluster10.CreateClusterNotificationEndpointSmtpRequest, form).pipe(Effect54.flatMap(cluster10.createClusterNotificationEndpointSmtp)) : props.type === "gotify" ? notificationRequest(cluster10.CreateClusterNotificationEndpointGotifyRequest, form).pipe(Effect54.flatMap(cluster10.createClusterNotificationEndpointGotify)) : notificationRequest(cluster10.CreateClusterNotificationEndpointWebhookRequest, form).pipe(Effect54.flatMap(cluster10.createClusterNotificationEndpointWebhook));
|
|
4227
4275
|
return runPve(props.target, "provision", true, operation);
|
|
4228
4276
|
};
|
|
4229
4277
|
var updateTarget = (props) => {
|
|
4230
4278
|
const form = fields(props, false);
|
|
4231
|
-
const operation = props.type === "sendmail" ? notificationRequest(
|
|
4279
|
+
const operation = props.type === "sendmail" ? notificationRequest(cluster10.PutClusterNotificationEndpointSendmailRequest, form).pipe(Effect54.flatMap(cluster10.putClusterNotificationEndpointSendmail)) : props.type === "smtp" ? notificationRequest(cluster10.PutClusterNotificationEndpointSmtpRequest, form).pipe(Effect54.flatMap(cluster10.putClusterNotificationEndpointSmtp)) : props.type === "gotify" ? notificationRequest(cluster10.PutClusterNotificationEndpointGotifyRequest, form).pipe(Effect54.flatMap(cluster10.putClusterNotificationEndpointGotify)) : notificationRequest(cluster10.PutClusterNotificationEndpointWebhookRequest, form).pipe(Effect54.flatMap(cluster10.putClusterNotificationEndpointWebhook));
|
|
4232
4280
|
return runPve(props.target, "provision", true, operation);
|
|
4233
4281
|
};
|
|
4234
4282
|
var deleteTarget = (props) => {
|
|
4235
4283
|
const request = { name: props.name };
|
|
4236
|
-
const operation = props.type === "sendmail" ?
|
|
4237
|
-
return runPve(props.target, "provision", true, operation).pipe(
|
|
4284
|
+
const operation = props.type === "sendmail" ? cluster10.deleteClusterNotificationEndpointSendmail(request) : props.type === "smtp" ? cluster10.deleteClusterNotificationEndpointSmtp(request) : props.type === "gotify" ? cluster10.deleteClusterNotificationEndpointGotify(request) : cluster10.deleteClusterNotificationEndpointWebhook(request);
|
|
4285
|
+
return runPve(props.target, "provision", true, operation).pipe(Effect54.catchTag("NotFound", () => Effect54.void));
|
|
4238
4286
|
};
|
|
4239
4287
|
|
|
4240
4288
|
// src/proxmox/notification-target-lifecycle.ts
|
|
4241
4289
|
var { guardCreate: guardCreate4, guardUpdate: guardUpdate4 } = specGuards(targetSpec);
|
|
4242
|
-
var guard2 = (props, create) => guardCreate4(props, create).pipe(
|
|
4290
|
+
var guard2 = (props, create) => guardCreate4(props, create).pipe(Effect55.andThen(guardUpdate4(props)));
|
|
4243
4291
|
var targetHandlers = {
|
|
4244
|
-
list: () =>
|
|
4245
|
-
read: ({ olds }) =>
|
|
4292
|
+
list: () => Effect55.succeed([]),
|
|
4293
|
+
read: ({ olds }) => Effect55.gen(function* () {
|
|
4246
4294
|
const live = yield* readTarget(olds);
|
|
4247
4295
|
yield* guard2(olds, live === undefined);
|
|
4248
4296
|
return live;
|
|
@@ -4251,8 +4299,8 @@ var targetHandlers = {
|
|
|
4251
4299
|
news,
|
|
4252
4300
|
olds,
|
|
4253
4301
|
output
|
|
4254
|
-
}) =>
|
|
4255
|
-
if (!
|
|
4302
|
+
}) => Effect55.gen(function* () {
|
|
4303
|
+
if (!isResolved17(news))
|
|
4256
4304
|
return;
|
|
4257
4305
|
if (olds.name !== news.name || olds.type !== news.type) {
|
|
4258
4306
|
const destination = yield* readTarget(news);
|
|
@@ -4270,7 +4318,7 @@ var targetHandlers = {
|
|
|
4270
4318
|
}
|
|
4271
4319
|
return { action: targetSpec.matches(live, news) ? "noop" : "update" };
|
|
4272
4320
|
}),
|
|
4273
|
-
reconcile: ({ news }) =>
|
|
4321
|
+
reconcile: ({ news }) => Effect55.gen(function* () {
|
|
4274
4322
|
const live = yield* readTarget(news);
|
|
4275
4323
|
yield* guard2(news, live === undefined);
|
|
4276
4324
|
if (live === undefined)
|
|
@@ -4279,7 +4327,7 @@ var targetHandlers = {
|
|
|
4279
4327
|
yield* updateTarget(news);
|
|
4280
4328
|
const after = yield* readTarget(news);
|
|
4281
4329
|
if (after === undefined)
|
|
4282
|
-
return yield*
|
|
4330
|
+
return yield* Effect55.die(new Error(`Notification target ${news.name}: write returned but read-back is absent.`));
|
|
4283
4331
|
return after;
|
|
4284
4332
|
}),
|
|
4285
4333
|
delete: ({ olds }) => deleteTarget(olds)
|
|
@@ -4287,15 +4335,15 @@ var targetHandlers = {
|
|
|
4287
4335
|
|
|
4288
4336
|
// src/proxmox/notification-target.ts
|
|
4289
4337
|
var ProxmoxNotificationTarget = Resource18("Proxmox.NotificationTarget");
|
|
4290
|
-
var ProxmoxNotificationTargetProvider = () => Provider18.effect(ProxmoxNotificationTarget,
|
|
4338
|
+
var ProxmoxNotificationTargetProvider = () => Provider18.effect(ProxmoxNotificationTarget, Effect56.succeed(ProxmoxNotificationTarget.Provider.of(targetHandlers)));
|
|
4291
4339
|
// src/proxmox/pbs-datastore.ts
|
|
4292
4340
|
import { Resource as Resource19 } from "alchemy";
|
|
4293
4341
|
import * as Provider19 from "alchemy/Provider";
|
|
4294
|
-
import * as
|
|
4342
|
+
import * as Effect62 from "effect/Effect";
|
|
4295
4343
|
|
|
4296
4344
|
// src/proxmox/pbs-datastore-lifecycle.ts
|
|
4297
|
-
import { isResolved as
|
|
4298
|
-
import * as
|
|
4345
|
+
import { isResolved as isResolved18 } from "alchemy/Diff";
|
|
4346
|
+
import * as Effect61 from "effect/Effect";
|
|
4299
4347
|
|
|
4300
4348
|
// src/proxmox/pbs-datastore-form.ts
|
|
4301
4349
|
var attributes = (live, props) => ({
|
|
@@ -4320,7 +4368,7 @@ var attributes = (live, props) => ({
|
|
|
4320
4368
|
"verify-new": bool(live["verify-new"])
|
|
4321
4369
|
});
|
|
4322
4370
|
var same4 = (declared, live) => declared === undefined || declared === live;
|
|
4323
|
-
var
|
|
4371
|
+
var matches7 = (live, props) => same4(props.comment, live.comment) && same4(props["gc-schedule"]?.trim(), live["gc-schedule"]) && same4(props["prune-schedule"]?.trim(), live["prune-schedule"]) && same4(props["keep-last"], live["keep-last"]) && same4(props["keep-hourly"], live["keep-hourly"]) && same4(props["keep-daily"], live["keep-daily"]) && same4(props["keep-weekly"], live["keep-weekly"]) && same4(props["keep-monthly"], live["keep-monthly"]) && same4(props["keep-yearly"], live["keep-yearly"]) && same4(props["notify-user"], live["notify-user"]) && same4(props["counter-reset-schedule"]?.trim(), live["counter-reset-schedule"]) && (props["notification-thresholds"] === undefined || propertyString(props["notification-thresholds"]) === live["notification-thresholds"]) && same4(props["verify-new"], live["verify-new"]) && (props.notify === undefined || propertyString(props.notify) === live.notify) && (props.tuning === undefined || propertyString(props.tuning) === live.tuning) && (props["maintenance-mode"] === undefined || propertyString(props["maintenance-mode"], "type") === live["maintenance-mode"]);
|
|
4324
4372
|
var set2 = (key, value) => value === undefined ? {} : { [key]: value };
|
|
4325
4373
|
var count = (value) => value === undefined ? undefined : String(value);
|
|
4326
4374
|
var mutable = (props) => ({
|
|
@@ -4347,23 +4395,23 @@ var createForm6 = (props) => ({
|
|
|
4347
4395
|
path: props.path,
|
|
4348
4396
|
...props.backend === undefined ? {} : { backend: props.backend }
|
|
4349
4397
|
});
|
|
4350
|
-
var
|
|
4398
|
+
var updateForm4 = (props) => mutable(props);
|
|
4351
4399
|
var object2 = (props) => `config/datastore/${props.name}`;
|
|
4352
4400
|
|
|
4353
4401
|
// src/proxmox/pbs-datastore-wire.ts
|
|
4354
4402
|
import * as config from "@distilled.cloud/proxmox-backup/config";
|
|
4355
|
-
import * as
|
|
4403
|
+
import * as Effect58 from "effect/Effect";
|
|
4356
4404
|
|
|
4357
4405
|
// src/proxmox/distilled-pbs.ts
|
|
4358
4406
|
import { credentials } from "@distilled.cloud/proxmox-backup/Credentials";
|
|
4359
4407
|
import * as Retry from "@distilled.cloud/proxmox-backup/Retry";
|
|
4360
|
-
import * as
|
|
4361
|
-
var runPbsWith = (target, credential, op, timeout5 = MEMBER_TIMEOUT) => op.pipe(Retry.none,
|
|
4408
|
+
import * as Effect57 from "effect/Effect";
|
|
4409
|
+
var runPbsWith = (target, credential, op, timeout5 = MEMBER_TIMEOUT) => op.pipe(Retry.none, Effect57.provide(credentials({
|
|
4362
4410
|
baseUrl: target.api,
|
|
4363
4411
|
secret: credential.secret,
|
|
4364
4412
|
tokenId: credential.tokenId
|
|
4365
|
-
})),
|
|
4366
|
-
var runPbs = (target, role, op) =>
|
|
4413
|
+
})), Effect57.timeout(timeout5));
|
|
4414
|
+
var runPbs = (target, role, op) => Effect57.gen(function* () {
|
|
4367
4415
|
const credential = yield* leased(target, role);
|
|
4368
4416
|
return yield* runPbsWith(target, credential, op);
|
|
4369
4417
|
});
|
|
@@ -4394,7 +4442,7 @@ var createRequest = (props) => ({
|
|
|
4394
4442
|
...props.backend === undefined ? {} : { backend: props.backend }
|
|
4395
4443
|
});
|
|
4396
4444
|
var updateRequest = (props) => ({
|
|
4397
|
-
...members(
|
|
4445
|
+
...members(updateForm4(props)),
|
|
4398
4446
|
name: props.name
|
|
4399
4447
|
});
|
|
4400
4448
|
var wire = (row) => ({
|
|
@@ -4413,53 +4461,53 @@ var wire = (row) => ({
|
|
|
4413
4461
|
"prune-schedule": row.prune_schedule,
|
|
4414
4462
|
"verify-new": row.verify_new
|
|
4415
4463
|
});
|
|
4416
|
-
var readOne = (props) => runPbs(props.target, "read", config.getConfigDatastore({ name: props.name })).pipe(
|
|
4464
|
+
var readOne = (props) => runPbs(props.target, "read", config.getConfigDatastore({ name: props.name })).pipe(Effect58.map((live) => attributes(wire(live), props)), Effect58.catchTag("DatastoreNotFound", () => Effect58.succeed(undefined)));
|
|
4417
4465
|
var createOne = (props) => runPbs(props.target, "provision", config.createConfigDatastore(createRequest(props)));
|
|
4418
4466
|
var updateOne = (props) => runPbs(props.target, "provision", config.putConfigDatastore(updateRequest(props)));
|
|
4419
|
-
var deleteOne = (props) =>
|
|
4467
|
+
var deleteOne = (props) => Effect58.gen(function* () {
|
|
4420
4468
|
if ((yield* readOne(props)) === undefined)
|
|
4421
4469
|
return;
|
|
4422
4470
|
return yield* runPbs(props.target, "provision", config.deleteConfigDatastore({ name: props.name }));
|
|
4423
4471
|
});
|
|
4424
4472
|
|
|
4425
4473
|
// src/proxmox/pbs-datastore-guard.ts
|
|
4426
|
-
import * as
|
|
4474
|
+
import * as Effect59 from "effect/Effect";
|
|
4427
4475
|
var samePath = (left, right) => {
|
|
4428
4476
|
const trim = (value) => value.length > 1 ? value.replace(/\/+$/, "") : value;
|
|
4429
4477
|
return trim(left) === trim(right);
|
|
4430
4478
|
};
|
|
4431
|
-
var guardPath = (live, props) => live.path === "" || samePath(live.path, props.path) ?
|
|
4432
|
-
var guardBackend = (live, props) => props.backend === undefined || propertyString(props.backend) === live.backend ?
|
|
4433
|
-
var settle2 = (read4, done) =>
|
|
4479
|
+
var guardPath = (live, props) => live.path === "" || samePath(live.path, props.path) ? Effect59.void : Effect59.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."));
|
|
4480
|
+
var guardBackend = (live, props) => props.backend === undefined || propertyString(props.backend) === live.backend ? Effect59.void : Effect59.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."));
|
|
4481
|
+
var settle2 = (read4, done) => Effect59.gen(function* () {
|
|
4434
4482
|
for (let attempt = 0;attempt < 60; attempt += 1) {
|
|
4435
4483
|
const live = yield* read4;
|
|
4436
4484
|
if (done(live))
|
|
4437
4485
|
return live;
|
|
4438
|
-
yield*
|
|
4486
|
+
yield* Effect59.sleep("2 seconds");
|
|
4439
4487
|
}
|
|
4440
4488
|
return yield* read4;
|
|
4441
4489
|
});
|
|
4442
4490
|
|
|
4443
4491
|
// src/proxmox/pbs-datastore-endpoint.ts
|
|
4444
|
-
import * as
|
|
4492
|
+
import * as Effect60 from "effect/Effect";
|
|
4445
4493
|
var DATASTORE_ENDPOINT = {
|
|
4446
4494
|
create: "pbs:POST /config/datastore",
|
|
4447
4495
|
update: "pbs:PUT /config/datastore/{name}"
|
|
4448
4496
|
};
|
|
4449
|
-
var guardDatastoreForms = (props) =>
|
|
4497
|
+
var guardDatastoreForms = (props) => Effect60.gen(function* () {
|
|
4450
4498
|
yield* guardForm(DATASTORE_ENDPOINT.create, createForm6(props), true);
|
|
4451
|
-
yield* guardForm(DATASTORE_ENDPOINT.update,
|
|
4499
|
+
yield* guardForm(DATASTORE_ENDPOINT.update, updateForm4(props), false);
|
|
4452
4500
|
});
|
|
4453
4501
|
|
|
4454
4502
|
// src/proxmox/pbs-datastore-lifecycle.ts
|
|
4455
|
-
var
|
|
4456
|
-
list: () =>
|
|
4503
|
+
var handlers6 = {
|
|
4504
|
+
list: () => Effect61.succeed([]),
|
|
4457
4505
|
read: ({ olds }) => readOne(olds),
|
|
4458
4506
|
diff: ({
|
|
4459
4507
|
news,
|
|
4460
4508
|
output
|
|
4461
|
-
}) =>
|
|
4462
|
-
if (!
|
|
4509
|
+
}) => Effect61.gen(function* () {
|
|
4510
|
+
if (!isResolved18(news))
|
|
4463
4511
|
return;
|
|
4464
4512
|
yield* guardDatastoreForms(news);
|
|
4465
4513
|
if (output === undefined)
|
|
@@ -4469,9 +4517,9 @@ var handlers7 = {
|
|
|
4469
4517
|
return { action: "update" };
|
|
4470
4518
|
yield* guardPath(live, news);
|
|
4471
4519
|
yield* guardBackend(live, news);
|
|
4472
|
-
return
|
|
4520
|
+
return matches7(live, news) ? { action: "noop" } : { action: "update" };
|
|
4473
4521
|
}),
|
|
4474
|
-
reconcile:
|
|
4522
|
+
reconcile: Effect61.fn(function* ({ news }) {
|
|
4475
4523
|
yield* guardDatastoreForms(news);
|
|
4476
4524
|
const live = yield* readOne(news);
|
|
4477
4525
|
if (live === undefined) {
|
|
@@ -4479,22 +4527,22 @@ var handlers7 = {
|
|
|
4479
4527
|
} else {
|
|
4480
4528
|
yield* guardPath(live, news);
|
|
4481
4529
|
yield* guardBackend(live, news);
|
|
4482
|
-
const form = formToSend(
|
|
4530
|
+
const form = formToSend(matches7, live, news, updateForm4(news));
|
|
4483
4531
|
if (form !== undefined) {
|
|
4484
4532
|
yield* updateOne(news);
|
|
4485
4533
|
}
|
|
4486
4534
|
}
|
|
4487
|
-
const after = yield* settle2(readOne(news), (row) => row !== undefined &&
|
|
4488
|
-
if (after === undefined || !
|
|
4489
|
-
return yield*
|
|
4535
|
+
const after = yield* settle2(readOne(news), (row) => row !== undefined && matches7(row, news));
|
|
4536
|
+
if (after === undefined || !matches7(after, news)) {
|
|
4537
|
+
return yield* Effect61.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."));
|
|
4490
4538
|
}
|
|
4491
4539
|
return after;
|
|
4492
4540
|
}),
|
|
4493
|
-
delete:
|
|
4541
|
+
delete: Effect61.fn(function* ({ olds }) {
|
|
4494
4542
|
yield* deleteOne(olds);
|
|
4495
4543
|
const left = yield* settle2(readOne(olds), (row) => row === undefined);
|
|
4496
4544
|
if (left !== undefined) {
|
|
4497
|
-
return yield*
|
|
4545
|
+
return yield* Effect61.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>\`.`));
|
|
4498
4546
|
}
|
|
4499
4547
|
})
|
|
4500
4548
|
};
|
|
@@ -4503,36 +4551,36 @@ var handlers7 = {
|
|
|
4503
4551
|
var PbsDatastore = Resource19("Pbs.Datastore", {
|
|
4504
4552
|
defaultRemovalPolicy: "retain"
|
|
4505
4553
|
});
|
|
4506
|
-
var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore,
|
|
4554
|
+
var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore, Effect62.succeed(PbsDatastore.Provider.of(handlers6)));
|
|
4507
4555
|
// src/proxmox/pbs-notification-matcher.ts
|
|
4508
4556
|
import { Resource as Resource20 } from "alchemy";
|
|
4509
4557
|
import * as Provider20 from "alchemy/Provider";
|
|
4510
|
-
import { isResolved as
|
|
4511
|
-
import * as
|
|
4558
|
+
import { isResolved as isResolved19 } from "alchemy/Diff";
|
|
4559
|
+
import * as Effect65 from "effect/Effect";
|
|
4512
4560
|
|
|
4513
4561
|
// src/proxmox/pbs-notification-matcher-distilled.ts
|
|
4514
4562
|
import * as config2 from "@distilled.cloud/proxmox-backup/config";
|
|
4515
|
-
import * as
|
|
4563
|
+
import * as Effect64 from "effect/Effect";
|
|
4516
4564
|
|
|
4517
4565
|
// src/proxmox/pbs-notification-input.ts
|
|
4518
4566
|
import * as Data3 from "effect/Data";
|
|
4519
|
-
import * as
|
|
4567
|
+
import * as Effect63 from "effect/Effect";
|
|
4520
4568
|
import * as Schema4 from "effect/Schema";
|
|
4521
4569
|
|
|
4522
4570
|
class PbsNotificationInputRefusal extends Data3.TaggedError("PbsNotificationInputRefusal") {
|
|
4523
4571
|
}
|
|
4524
|
-
var notificationInput = (schema, fields2) => Schema4.decodeUnknownEffect(Schema4.toType(schema))(fields2).pipe(
|
|
4572
|
+
var notificationInput = (schema, fields2) => Schema4.decodeUnknownEffect(Schema4.toType(schema))(fields2).pipe(Effect63.mapError(() => new PbsNotificationInputRefusal({
|
|
4525
4573
|
message: "PBS notification fields do not match the SDK request schema; no PBS request was sent."
|
|
4526
4574
|
})));
|
|
4527
4575
|
|
|
4528
4576
|
// src/proxmox/pbs-notification-matcher-distilled.ts
|
|
4529
|
-
var readMatcher2 = (props) => runPbs(props.target, "read", config2.getConfigNotificationMatcher({ name: props.name })).pipe(
|
|
4577
|
+
var readMatcher2 = (props) => runPbs(props.target, "read", config2.getConfigNotificationMatcher({ name: props.name })).pipe(Effect64.map((row) => matcherAttributes({
|
|
4530
4578
|
...row,
|
|
4531
4579
|
"invert-match": row.invert_match,
|
|
4532
4580
|
"match-calendar": row.match_calendar,
|
|
4533
4581
|
"match-field": row.match_field,
|
|
4534
4582
|
"match-severity": row.match_severity
|
|
4535
|
-
}, props.name)),
|
|
4583
|
+
}, props.name)), Effect64.catchTag("NotFound", () => Effect64.succeed(undefined)));
|
|
4536
4584
|
var requestFields = (props, form) => ({
|
|
4537
4585
|
...form,
|
|
4538
4586
|
invert_match: form["invert-match"],
|
|
@@ -4541,23 +4589,23 @@ var requestFields = (props, form) => ({
|
|
|
4541
4589
|
match_severity: form["match-severity"],
|
|
4542
4590
|
name: props.name
|
|
4543
4591
|
});
|
|
4544
|
-
var createMatcher2 = (props, form) => runPbs(props.target, "provision", notificationInput(config2.CreateConfigNotificationMatcherRequest, requestFields(props, form)).pipe(
|
|
4545
|
-
var updateMatcher2 = (props, form) => runPbs(props.target, "provision", notificationInput(config2.PutConfigNotificationMatcherRequest, requestFields(props, form)).pipe(
|
|
4546
|
-
var deleteMatcher2 = (props) => runPbs(props.target, "provision", config2.deleteConfigNotificationMatcher({ name: props.name })).pipe(
|
|
4592
|
+
var createMatcher2 = (props, form) => runPbs(props.target, "provision", notificationInput(config2.CreateConfigNotificationMatcherRequest, requestFields(props, form)).pipe(Effect64.flatMap(config2.createConfigNotificationMatcher)));
|
|
4593
|
+
var updateMatcher2 = (props, form) => runPbs(props.target, "provision", notificationInput(config2.PutConfigNotificationMatcherRequest, requestFields(props, form)).pipe(Effect64.flatMap(config2.putConfigNotificationMatcher)));
|
|
4594
|
+
var deleteMatcher2 = (props) => runPbs(props.target, "provision", config2.deleteConfigNotificationMatcher({ name: props.name })).pipe(Effect64.catchTag("NotFound", () => Effect64.void));
|
|
4547
4595
|
|
|
4548
4596
|
// src/proxmox/pbs-notification-matcher.ts
|
|
4549
4597
|
var PbsNotificationMatcher = Resource20("Pbs.NotificationMatcher");
|
|
4550
4598
|
var guardCreate5 = (props, requirePresence) => guardForm("pbs:POST /config/notifications/matchers", matcherCreateForm(props), requirePresence);
|
|
4551
4599
|
var guardUpdate5 = (props) => guardForm("pbs:PUT /config/notifications/matchers/{name}", matcherUpdateForm(props), false);
|
|
4552
|
-
var
|
|
4553
|
-
list: () =>
|
|
4600
|
+
var handlers7 = {
|
|
4601
|
+
list: () => Effect65.succeed([]),
|
|
4554
4602
|
read: ({ olds }) => readMatcher2(olds),
|
|
4555
4603
|
diff: ({
|
|
4556
4604
|
news,
|
|
4557
4605
|
olds,
|
|
4558
4606
|
output
|
|
4559
|
-
}) =>
|
|
4560
|
-
if (!
|
|
4607
|
+
}) => Effect65.gen(function* () {
|
|
4608
|
+
if (!isResolved19(news))
|
|
4561
4609
|
return;
|
|
4562
4610
|
yield* guardCreate5(news, output === undefined);
|
|
4563
4611
|
yield* guardUpdate5(news);
|
|
@@ -4572,7 +4620,7 @@ var handlers8 = {
|
|
|
4572
4620
|
}
|
|
4573
4621
|
return { action: matcherMatches(live, news) ? "noop" : "update" };
|
|
4574
4622
|
}),
|
|
4575
|
-
reconcile: ({ news }) =>
|
|
4623
|
+
reconcile: ({ news }) => Effect65.gen(function* () {
|
|
4576
4624
|
const live = yield* readMatcher2(news);
|
|
4577
4625
|
yield* guardCreate5(news, live === undefined);
|
|
4578
4626
|
yield* guardUpdate5(news);
|
|
@@ -4582,24 +4630,24 @@ var handlers8 = {
|
|
|
4582
4630
|
yield* updateMatcher2(news, matcherUpdateForm(news));
|
|
4583
4631
|
const after = yield* readMatcher2(news);
|
|
4584
4632
|
if (after === undefined)
|
|
4585
|
-
return yield*
|
|
4633
|
+
return yield* Effect65.fail(new Error(`config/notifications/matchers/${news.name}: the write returned no error but the matcher is still absent.`));
|
|
4586
4634
|
return after;
|
|
4587
4635
|
}),
|
|
4588
4636
|
delete: ({ olds }) => deleteMatcher2(olds)
|
|
4589
4637
|
};
|
|
4590
|
-
var PbsNotificationMatcherProvider = () => Provider20.effect(PbsNotificationMatcher,
|
|
4638
|
+
var PbsNotificationMatcherProvider = () => Provider20.effect(PbsNotificationMatcher, Effect65.succeed(PbsNotificationMatcher.Provider.of(handlers7)));
|
|
4591
4639
|
// src/proxmox/pbs-notification-target.ts
|
|
4592
4640
|
import { Resource as Resource21 } from "alchemy";
|
|
4593
4641
|
import * as Provider21 from "alchemy/Provider";
|
|
4594
|
-
import * as
|
|
4642
|
+
import * as Effect68 from "effect/Effect";
|
|
4595
4643
|
|
|
4596
4644
|
// src/proxmox/pbs-notification-target-lifecycle.ts
|
|
4597
|
-
import { isResolved as
|
|
4598
|
-
import * as
|
|
4645
|
+
import { isResolved as isResolved20 } from "alchemy/Diff";
|
|
4646
|
+
import * as Effect67 from "effect/Effect";
|
|
4599
4647
|
|
|
4600
4648
|
// src/proxmox/pbs-notification-target-distilled.ts
|
|
4601
4649
|
import * as config3 from "@distilled.cloud/proxmox-backup/config";
|
|
4602
|
-
import * as
|
|
4650
|
+
import * as Effect66 from "effect/Effect";
|
|
4603
4651
|
|
|
4604
4652
|
// src/proxmox/pbs-notification-target-wire.ts
|
|
4605
4653
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
@@ -4700,22 +4748,22 @@ var requestFields2 = (props, form) => ({
|
|
|
4700
4748
|
var read4 = (props) => {
|
|
4701
4749
|
switch (props.type) {
|
|
4702
4750
|
case "sendmail":
|
|
4703
|
-
return config3.getConfigNotificationEndpointSendmail({ name: props.name }).pipe(
|
|
4751
|
+
return config3.getConfigNotificationEndpointSendmail({ name: props.name }).pipe(Effect66.map((row) => ({
|
|
4704
4752
|
...row,
|
|
4705
4753
|
"from-address": row.from_address,
|
|
4706
4754
|
"mailto-user": row.mailto_user
|
|
4707
4755
|
})));
|
|
4708
4756
|
case "smtp":
|
|
4709
|
-
return config3.getConfigNotificationEndpointSmtp({ name: props.name }).pipe(
|
|
4757
|
+
return config3.getConfigNotificationEndpointSmtp({ name: props.name }).pipe(Effect66.map((row) => ({
|
|
4710
4758
|
...row,
|
|
4711
4759
|
"from-address": row.from_address,
|
|
4712
4760
|
"mailto-user": row.mailto_user
|
|
4713
4761
|
})));
|
|
4714
4762
|
case "webhook":
|
|
4715
|
-
return config3.getConfigNotificationEndpointWebhook({ name: props.name }).pipe(
|
|
4763
|
+
return config3.getConfigNotificationEndpointWebhook({ name: props.name }).pipe(Effect66.map((row) => ({ ...row })));
|
|
4716
4764
|
}
|
|
4717
4765
|
};
|
|
4718
|
-
var readTarget2 = (props) => runPbs(props.target, "read", read4(props)).pipe(
|
|
4766
|
+
var readTarget2 = (props) => runPbs(props.target, "read", read4(props)).pipe(Effect66.map((row) => targetAttributes(row, props)), Effect66.catchTag("NotFound", () => Effect66.succeed(undefined)));
|
|
4719
4767
|
var validateTargetWrite2 = (props, form, mode) => {
|
|
4720
4768
|
const schema = mode === "create" ? {
|
|
4721
4769
|
sendmail: config3.CreateConfigNotificationEndpointSendmailRequest,
|
|
@@ -4726,22 +4774,22 @@ var validateTargetWrite2 = (props, form, mode) => {
|
|
|
4726
4774
|
smtp: config3.PutConfigNotificationEndpointSmtpRequest,
|
|
4727
4775
|
webhook: config3.PutConfigNotificationEndpointWebhookRequest
|
|
4728
4776
|
}[props.type];
|
|
4729
|
-
return notificationInput(schema, requestFields2(props, form)).pipe(
|
|
4777
|
+
return notificationInput(schema, requestFields2(props, form)).pipe(Effect66.asVoid);
|
|
4730
4778
|
};
|
|
4731
4779
|
var createTarget2 = (props, form) => {
|
|
4732
4780
|
const fields2 = requestFields2(props, form);
|
|
4733
|
-
const operation = props.type === "sendmail" ? notificationInput(config3.CreateConfigNotificationEndpointSendmailRequest, fields2).pipe(
|
|
4781
|
+
const operation = props.type === "sendmail" ? notificationInput(config3.CreateConfigNotificationEndpointSendmailRequest, fields2).pipe(Effect66.flatMap(config3.createConfigNotificationEndpointSendmail)) : props.type === "smtp" ? notificationInput(config3.CreateConfigNotificationEndpointSmtpRequest, fields2).pipe(Effect66.flatMap(config3.createConfigNotificationEndpointSmtp)) : notificationInput(config3.CreateConfigNotificationEndpointWebhookRequest, fields2).pipe(Effect66.flatMap(config3.createConfigNotificationEndpointWebhook));
|
|
4734
4782
|
return runPbs(props.target, "provision", operation);
|
|
4735
4783
|
};
|
|
4736
4784
|
var updateTarget2 = (props, form) => {
|
|
4737
4785
|
const fields2 = requestFields2(props, form);
|
|
4738
|
-
const operation = props.type === "sendmail" ? notificationInput(config3.PutConfigNotificationEndpointSendmailRequest, fields2).pipe(
|
|
4786
|
+
const operation = props.type === "sendmail" ? notificationInput(config3.PutConfigNotificationEndpointSendmailRequest, fields2).pipe(Effect66.flatMap(config3.putConfigNotificationEndpointSendmail)) : props.type === "smtp" ? notificationInput(config3.PutConfigNotificationEndpointSmtpRequest, fields2).pipe(Effect66.flatMap(config3.putConfigNotificationEndpointSmtp)) : notificationInput(config3.PutConfigNotificationEndpointWebhookRequest, fields2).pipe(Effect66.flatMap(config3.putConfigNotificationEndpointWebhook));
|
|
4739
4787
|
return runPbs(props.target, "provision", operation);
|
|
4740
4788
|
};
|
|
4741
4789
|
var deleteTarget2 = (props) => {
|
|
4742
4790
|
const request = { name: props.name };
|
|
4743
4791
|
const operation = props.type === "sendmail" ? config3.deleteConfigNotificationEndpointSendmail(request) : props.type === "smtp" ? config3.deleteConfigNotificationEndpointSmtp(request) : config3.deleteConfigNotificationEndpointWebhook(request);
|
|
4744
|
-
return runPbs(props.target, "provision", operation).pipe(
|
|
4792
|
+
return runPbs(props.target, "provision", operation).pipe(Effect66.catchTag("NotFound", () => Effect66.void));
|
|
4745
4793
|
};
|
|
4746
4794
|
|
|
4747
4795
|
// src/proxmox/pbs-notification-target-endpoint.ts
|
|
@@ -4932,7 +4980,7 @@ var targetForm = (props, groups, carry, mode) => {
|
|
|
4932
4980
|
var path = (props) => `config/notifications/endpoints/${props.type}/${props.name}`;
|
|
4933
4981
|
var refuse2 = (props) => {
|
|
4934
4982
|
const reasons = refusals(props);
|
|
4935
|
-
return reasons.length === 0 ?
|
|
4983
|
+
return reasons.length === 0 ? Effect67.void : Effect67.die(new Error(`${path(props)}: ${reasons.join("; ")}.`));
|
|
4936
4984
|
};
|
|
4937
4985
|
var toCarry = (live, props, groups, sealed) => {
|
|
4938
4986
|
const header = headerState(live, props, groups) === "stale";
|
|
@@ -4944,17 +4992,17 @@ var requireValues = (props, groups, carry) => {
|
|
|
4944
4992
|
...carry.header ? groups.header.missing : [],
|
|
4945
4993
|
...carry.sealed ? groups.sealed.missing : []
|
|
4946
4994
|
];
|
|
4947
|
-
return missing.length === 0 ?
|
|
4995
|
+
return missing.length === 0 ? Effect67.void : Effect67.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."));
|
|
4948
4996
|
};
|
|
4949
|
-
var
|
|
4950
|
-
list: () =>
|
|
4951
|
-
read: ({ olds, output }) => (output === undefined ? refuse2(olds) :
|
|
4997
|
+
var handlers8 = {
|
|
4998
|
+
list: () => Effect67.succeed([]),
|
|
4999
|
+
read: ({ olds, output }) => (output === undefined ? refuse2(olds) : Effect67.void).pipe(Effect67.andThen(readTarget2(olds)), Effect67.map((live) => live === undefined ? undefined : { ...live, sealed: output?.sealed ?? "" })),
|
|
4952
5000
|
diff: ({
|
|
4953
5001
|
news,
|
|
4954
5002
|
olds,
|
|
4955
5003
|
output
|
|
4956
|
-
}) =>
|
|
4957
|
-
if (output === undefined || !
|
|
5004
|
+
}) => Effect67.gen(function* () {
|
|
5005
|
+
if (output === undefined || !isResolved20(news))
|
|
4958
5006
|
return;
|
|
4959
5007
|
yield* refuse2(news);
|
|
4960
5008
|
if (olds !== undefined && (olds.type !== news.type || olds.name !== news.name)) {
|
|
@@ -4975,7 +5023,7 @@ var handlers9 = {
|
|
|
4975
5023
|
return { action: "update" };
|
|
4976
5024
|
return toCarry(live, news, resolveGroups(news), output.sealed) === undefined ? { action: "noop" } : { action: "update" };
|
|
4977
5025
|
}),
|
|
4978
|
-
reconcile: ({ news, output }) =>
|
|
5026
|
+
reconcile: ({ news, output }) => Effect67.gen(function* () {
|
|
4979
5027
|
yield* refuse2(news);
|
|
4980
5028
|
const groups = resolveGroups(news);
|
|
4981
5029
|
const live = yield* readTarget2(news);
|
|
@@ -5000,7 +5048,7 @@ var handlers9 = {
|
|
|
5000
5048
|
}
|
|
5001
5049
|
const after = yield* readTarget2(news);
|
|
5002
5050
|
if (after === undefined) {
|
|
5003
|
-
return yield*
|
|
5051
|
+
return yield* Effect67.die(new Error(`${path(news)}: the write returned no error but the target is still absent.`));
|
|
5004
5052
|
}
|
|
5005
5053
|
return { ...after, sealed };
|
|
5006
5054
|
}),
|
|
@@ -5009,15 +5057,15 @@ var handlers9 = {
|
|
|
5009
5057
|
|
|
5010
5058
|
// src/proxmox/pbs-notification-target.ts
|
|
5011
5059
|
var PbsNotificationTarget = Resource21("Pbs.NotificationTarget");
|
|
5012
|
-
var PbsNotificationTargetProvider = () => Provider21.effect(PbsNotificationTarget,
|
|
5060
|
+
var PbsNotificationTargetProvider = () => Provider21.effect(PbsNotificationTarget, Effect68.succeed(PbsNotificationTarget.Provider.of(handlers8)));
|
|
5013
5061
|
// src/proxmox/pbs-prune-job.ts
|
|
5014
5062
|
import { Resource as Resource22 } from "alchemy";
|
|
5015
5063
|
import * as Provider22 from "alchemy/Provider";
|
|
5016
|
-
import * as
|
|
5064
|
+
import * as Effect71 from "effect/Effect";
|
|
5017
5065
|
|
|
5018
5066
|
// src/proxmox/pbs-prune-job-lifecycle.ts
|
|
5019
|
-
import { isResolved as
|
|
5020
|
-
import * as
|
|
5067
|
+
import { isResolved as isResolved21 } from "alchemy/Diff";
|
|
5068
|
+
import * as Effect70 from "effect/Effect";
|
|
5021
5069
|
|
|
5022
5070
|
// src/proxmox/pbs-prune-job-form.ts
|
|
5023
5071
|
var KEEP_KEYS = [
|
|
@@ -5095,7 +5143,7 @@ var spec = {
|
|
|
5095
5143
|
|
|
5096
5144
|
// src/proxmox/pbs-prune-job-wire.ts
|
|
5097
5145
|
import * as config4 from "@distilled.cloud/proxmox-backup/config";
|
|
5098
|
-
import * as
|
|
5146
|
+
import * as Effect69 from "effect/Effect";
|
|
5099
5147
|
var members2 = (form) => ({
|
|
5100
5148
|
...form["comment"] === undefined ? {} : { comment: form["comment"] },
|
|
5101
5149
|
...form["disable"] === undefined ? {} : { disable: form["disable"] },
|
|
@@ -5130,10 +5178,10 @@ var wire2 = (row) => ({
|
|
|
5130
5178
|
"keep-yearly": row.keep_yearly,
|
|
5131
5179
|
"max-depth": row.max_depth
|
|
5132
5180
|
});
|
|
5133
|
-
var readOne2 = (props) => runPbs(props.target, "read", config4.getConfigPrune({ id: props.id })).pipe(
|
|
5181
|
+
var readOne2 = (props) => runPbs(props.target, "read", config4.getConfigPrune({ id: props.id })).pipe(Effect69.map((row) => spec.attributes(wire2(row), props)), Effect69.catchTag("PruneJobNotFound", () => Effect69.succeed(undefined)));
|
|
5134
5182
|
var createOne2 = (props) => runPbs(props.target, "provision", config4.createConfigPrune(createRequest2(props)));
|
|
5135
5183
|
var updateOne2 = (props) => runPbs(props.target, "provision", config4.putConfigPrune(updateRequest2(props)));
|
|
5136
|
-
var deleteOne2 = (props) =>
|
|
5184
|
+
var deleteOne2 = (props) => Effect69.gen(function* () {
|
|
5137
5185
|
if ((yield* readOne2(props)) === undefined)
|
|
5138
5186
|
return;
|
|
5139
5187
|
return yield* runPbs(props.target, "provision", config4.deleteConfigPrune({ id: props.id }));
|
|
@@ -5141,14 +5189,14 @@ var deleteOne2 = (props) => Effect68.gen(function* () {
|
|
|
5141
5189
|
|
|
5142
5190
|
// src/proxmox/pbs-prune-job-lifecycle.ts
|
|
5143
5191
|
var { guardCreate: guardCreate6, guardUpdate: guardUpdate6 } = specGuards(spec);
|
|
5144
|
-
var
|
|
5145
|
-
list: () =>
|
|
5192
|
+
var handlers9 = {
|
|
5193
|
+
list: () => Effect70.succeed([]),
|
|
5146
5194
|
read: ({ olds }) => readOne2(olds),
|
|
5147
5195
|
diff: ({
|
|
5148
5196
|
news,
|
|
5149
5197
|
output
|
|
5150
|
-
}) =>
|
|
5151
|
-
if (!
|
|
5198
|
+
}) => Effect70.gen(function* () {
|
|
5199
|
+
if (!isResolved21(news))
|
|
5152
5200
|
return;
|
|
5153
5201
|
yield* guardCreate6(news, output === undefined);
|
|
5154
5202
|
yield* guardUpdate6(news);
|
|
@@ -5161,7 +5209,7 @@ var handlers10 = {
|
|
|
5161
5209
|
}
|
|
5162
5210
|
return { action: spec.matches(live, news) ? "noop" : "update" };
|
|
5163
5211
|
}),
|
|
5164
|
-
reconcile: ({ news }) =>
|
|
5212
|
+
reconcile: ({ news }) => Effect70.gen(function* () {
|
|
5165
5213
|
const live = yield* readOne2(news);
|
|
5166
5214
|
yield* guardCreate6(news, live === undefined);
|
|
5167
5215
|
yield* guardUpdate6(news);
|
|
@@ -5171,7 +5219,7 @@ var handlers10 = {
|
|
|
5171
5219
|
yield* updateOne2(news);
|
|
5172
5220
|
const after = yield* readOne2(news);
|
|
5173
5221
|
if (after === undefined) {
|
|
5174
|
-
return yield*
|
|
5222
|
+
return yield* Effect70.fail(new Error(`${spec.path(news)}: write succeeded but job is absent.`));
|
|
5175
5223
|
}
|
|
5176
5224
|
return after;
|
|
5177
5225
|
}),
|
|
@@ -5180,15 +5228,15 @@ var handlers10 = {
|
|
|
5180
5228
|
|
|
5181
5229
|
// src/proxmox/pbs-prune-job.ts
|
|
5182
5230
|
var PbsPruneJob = Resource22("Pbs.PruneJob");
|
|
5183
|
-
var PbsPruneJobProvider = () => Provider22.effect(PbsPruneJob,
|
|
5231
|
+
var PbsPruneJobProvider = () => Provider22.effect(PbsPruneJob, Effect71.succeed(PbsPruneJob.Provider.of(handlers9)));
|
|
5184
5232
|
// src/proxmox/pbs-sync-job.ts
|
|
5185
5233
|
import { Resource as Resource23 } from "alchemy";
|
|
5186
5234
|
import * as Provider23 from "alchemy/Provider";
|
|
5187
|
-
import * as
|
|
5235
|
+
import * as Effect74 from "effect/Effect";
|
|
5188
5236
|
|
|
5189
5237
|
// src/proxmox/pbs-sync-job-lifecycle.ts
|
|
5190
|
-
import { isResolved as
|
|
5191
|
-
import * as
|
|
5238
|
+
import { isResolved as isResolved22 } from "alchemy/Diff";
|
|
5239
|
+
import * as Effect73 from "effect/Effect";
|
|
5192
5240
|
|
|
5193
5241
|
// src/proxmox/pbs-sync-job-form.ts
|
|
5194
5242
|
var UNSET4 = -1;
|
|
@@ -5265,7 +5313,7 @@ var spec2 = {
|
|
|
5265
5313
|
|
|
5266
5314
|
// src/proxmox/pbs-sync-job-wire.ts
|
|
5267
5315
|
import * as config5 from "@distilled.cloud/proxmox-backup/config";
|
|
5268
|
-
import * as
|
|
5316
|
+
import * as Effect72 from "effect/Effect";
|
|
5269
5317
|
var members3 = (form) => ({
|
|
5270
5318
|
...form["remote-store"] === undefined ? {} : { remote_store: form["remote-store"] },
|
|
5271
5319
|
...form["remove-vanished"] === undefined ? {} : { remove_vanished: form["remove-vanished"] },
|
|
@@ -5304,10 +5352,10 @@ var wire3 = (row) => ({
|
|
|
5304
5352
|
"sync-direction": row.sync_direction,
|
|
5305
5353
|
"group-filter": row.group_filter
|
|
5306
5354
|
});
|
|
5307
|
-
var readOne3 = (props) => runPbs(props.target, "read", config5.getConfigSync({ id: props.id })).pipe(
|
|
5355
|
+
var readOne3 = (props) => runPbs(props.target, "read", config5.getConfigSync({ id: props.id })).pipe(Effect72.map((row) => spec2.attributes(wire3(row), props)), Effect72.catchTag("SyncJobNotFound", () => Effect72.succeed(undefined)));
|
|
5308
5356
|
var createOne3 = (props) => runPbs(props.target, "provision", config5.syncConfig(createRequest3(props)));
|
|
5309
5357
|
var updateOne3 = (props) => runPbs(props.target, "provision", config5.putConfigSync(updateRequest3(props)));
|
|
5310
|
-
var deleteOne3 = (props) =>
|
|
5358
|
+
var deleteOne3 = (props) => Effect72.gen(function* () {
|
|
5311
5359
|
if ((yield* readOne3(props)) === undefined)
|
|
5312
5360
|
return;
|
|
5313
5361
|
return yield* runPbs(props.target, "provision", config5.deleteConfigSync({ id: props.id }));
|
|
@@ -5315,11 +5363,11 @@ var deleteOne3 = (props) => Effect71.gen(function* () {
|
|
|
5315
5363
|
|
|
5316
5364
|
// src/proxmox/pbs-sync-job-lifecycle.ts
|
|
5317
5365
|
var { guardCreate: guardCreate7, guardUpdate: guardUpdate7 } = specGuards(spec2);
|
|
5318
|
-
var
|
|
5319
|
-
list: () =>
|
|
5366
|
+
var handlers10 = {
|
|
5367
|
+
list: () => Effect73.succeed([]),
|
|
5320
5368
|
read: ({ olds }) => readOne3(olds),
|
|
5321
|
-
diff: ({ news, output }) =>
|
|
5322
|
-
if (!
|
|
5369
|
+
diff: ({ news, output }) => Effect73.gen(function* () {
|
|
5370
|
+
if (!isResolved22(news))
|
|
5323
5371
|
return;
|
|
5324
5372
|
yield* guardCreate7(news, output === undefined);
|
|
5325
5373
|
yield* guardUpdate7(news);
|
|
@@ -5332,7 +5380,7 @@ var handlers11 = {
|
|
|
5332
5380
|
}
|
|
5333
5381
|
return { action: spec2.matches(live, news) ? "noop" : "update" };
|
|
5334
5382
|
}),
|
|
5335
|
-
reconcile: ({ news }) =>
|
|
5383
|
+
reconcile: ({ news }) => Effect73.gen(function* () {
|
|
5336
5384
|
const live = yield* readOne3(news);
|
|
5337
5385
|
yield* guardCreate7(news, live === undefined);
|
|
5338
5386
|
yield* guardUpdate7(news);
|
|
@@ -5342,7 +5390,7 @@ var handlers11 = {
|
|
|
5342
5390
|
yield* updateOne3(news);
|
|
5343
5391
|
const after = yield* readOne3(news);
|
|
5344
5392
|
if (after === undefined) {
|
|
5345
|
-
return yield*
|
|
5393
|
+
return yield* Effect73.fail(new Error(`${spec2.path(news)}: write succeeded but job is absent.`));
|
|
5346
5394
|
}
|
|
5347
5395
|
return after;
|
|
5348
5396
|
}),
|
|
@@ -5351,15 +5399,15 @@ var handlers11 = {
|
|
|
5351
5399
|
|
|
5352
5400
|
// src/proxmox/pbs-sync-job.ts
|
|
5353
5401
|
var PbsSyncJob = Resource23("Pbs.SyncJob");
|
|
5354
|
-
var PbsSyncJobProvider = () => Provider23.effect(PbsSyncJob,
|
|
5402
|
+
var PbsSyncJobProvider = () => Provider23.effect(PbsSyncJob, Effect74.succeed(PbsSyncJob.Provider.of(handlers10)));
|
|
5355
5403
|
// src/proxmox/pbs-verify-job.ts
|
|
5356
5404
|
import { Resource as Resource24 } from "alchemy";
|
|
5357
5405
|
import * as Provider24 from "alchemy/Provider";
|
|
5358
|
-
import * as
|
|
5406
|
+
import * as Effect77 from "effect/Effect";
|
|
5359
5407
|
|
|
5360
5408
|
// src/proxmox/pbs-verify-job-lifecycle.ts
|
|
5361
|
-
import { isResolved as
|
|
5362
|
-
import * as
|
|
5409
|
+
import { isResolved as isResolved23 } from "alchemy/Diff";
|
|
5410
|
+
import * as Effect76 from "effect/Effect";
|
|
5363
5411
|
|
|
5364
5412
|
// src/proxmox/pbs-verify-job-form.ts
|
|
5365
5413
|
var rechecks = (ignoreVerified, outdatedAfter) => {
|
|
@@ -5406,7 +5454,7 @@ var spec3 = {
|
|
|
5406
5454
|
|
|
5407
5455
|
// src/proxmox/pbs-verify-job-wire.ts
|
|
5408
5456
|
import * as config6 from "@distilled.cloud/proxmox-backup/config";
|
|
5409
|
-
import * as
|
|
5457
|
+
import * as Effect75 from "effect/Effect";
|
|
5410
5458
|
var members4 = (form) => ({
|
|
5411
5459
|
...form["ignore-verified"] === undefined ? {} : { ignore_verified: form["ignore-verified"] },
|
|
5412
5460
|
...form["store"] === undefined ? {} : { store: form["store"] },
|
|
@@ -5431,10 +5479,10 @@ var wire4 = (row) => ({
|
|
|
5431
5479
|
"max-depth": row.max_depth,
|
|
5432
5480
|
"outdated-after": row.outdated_after
|
|
5433
5481
|
});
|
|
5434
|
-
var readOne4 = (props) => runPbs(props.target, "read", config6.getConfigVerify({ id: props.id })).pipe(
|
|
5482
|
+
var readOne4 = (props) => runPbs(props.target, "read", config6.getConfigVerify({ id: props.id })).pipe(Effect75.map((row) => spec3.attributes(wire4(row), props)), Effect75.catchTag("VerifyJobNotFound", () => Effect75.succeed(undefined)));
|
|
5435
5483
|
var createOne4 = (props) => runPbs(props.target, "provision", config6.verifyConfig(createRequest4(props)));
|
|
5436
5484
|
var updateOne4 = (props) => runPbs(props.target, "provision", config6.putConfigVerify(updateRequest4(props)));
|
|
5437
|
-
var deleteOne4 = (props) =>
|
|
5485
|
+
var deleteOne4 = (props) => Effect75.gen(function* () {
|
|
5438
5486
|
if ((yield* readOne4(props)) === undefined)
|
|
5439
5487
|
return;
|
|
5440
5488
|
return yield* runPbs(props.target, "provision", config6.deleteConfigVerify({ id: props.id }));
|
|
@@ -5442,14 +5490,14 @@ var deleteOne4 = (props) => Effect74.gen(function* () {
|
|
|
5442
5490
|
|
|
5443
5491
|
// src/proxmox/pbs-verify-job-lifecycle.ts
|
|
5444
5492
|
var { guardCreate: guardCreate8, guardUpdate: guardUpdate8 } = specGuards(spec3);
|
|
5445
|
-
var
|
|
5446
|
-
list: () =>
|
|
5493
|
+
var handlers11 = {
|
|
5494
|
+
list: () => Effect76.succeed([]),
|
|
5447
5495
|
read: ({ olds }) => readOne4(olds),
|
|
5448
5496
|
diff: ({
|
|
5449
5497
|
news,
|
|
5450
5498
|
output
|
|
5451
|
-
}) =>
|
|
5452
|
-
if (!
|
|
5499
|
+
}) => Effect76.gen(function* () {
|
|
5500
|
+
if (!isResolved23(news))
|
|
5453
5501
|
return;
|
|
5454
5502
|
yield* guardCreate8(news, output === undefined);
|
|
5455
5503
|
yield* guardUpdate8(news);
|
|
@@ -5462,7 +5510,7 @@ var handlers12 = {
|
|
|
5462
5510
|
}
|
|
5463
5511
|
return { action: spec3.matches(live, news) ? "noop" : "update" };
|
|
5464
5512
|
}),
|
|
5465
|
-
reconcile: ({ news }) =>
|
|
5513
|
+
reconcile: ({ news }) => Effect76.gen(function* () {
|
|
5466
5514
|
const live = yield* readOne4(news);
|
|
5467
5515
|
yield* guardCreate8(news, live === undefined);
|
|
5468
5516
|
yield* guardUpdate8(news);
|
|
@@ -5472,7 +5520,7 @@ var handlers12 = {
|
|
|
5472
5520
|
yield* updateOne4(news);
|
|
5473
5521
|
const after = yield* readOne4(news);
|
|
5474
5522
|
if (after === undefined) {
|
|
5475
|
-
return yield*
|
|
5523
|
+
return yield* Effect76.fail(new Error(`${spec3.path(news)}: write succeeded but job is absent.`));
|
|
5476
5524
|
}
|
|
5477
5525
|
return after;
|
|
5478
5526
|
}),
|
|
@@ -5481,15 +5529,15 @@ var handlers12 = {
|
|
|
5481
5529
|
|
|
5482
5530
|
// src/proxmox/pbs-verify-job.ts
|
|
5483
5531
|
var PbsVerifyJob = Resource24("Pbs.VerifyJob");
|
|
5484
|
-
var PbsVerifyJobProvider = () => Provider24.effect(PbsVerifyJob,
|
|
5532
|
+
var PbsVerifyJobProvider = () => Provider24.effect(PbsVerifyJob, Effect77.succeed(PbsVerifyJob.Provider.of(handlers11)));
|
|
5485
5533
|
// src/proxmox/pool.ts
|
|
5486
5534
|
import { Resource as Resource25 } from "alchemy";
|
|
5487
|
-
import { isResolved as
|
|
5535
|
+
import { isResolved as isResolved24 } from "alchemy/Diff";
|
|
5488
5536
|
import * as Provider25 from "alchemy/Provider";
|
|
5489
5537
|
import * as pools from "@distilled.cloud/proxmox/pools";
|
|
5490
5538
|
import { ProxmoxParseError as ProxmoxParseError5 } from "@distilled.cloud/proxmox/Errors";
|
|
5491
5539
|
import * as Schema5 from "effect/Schema";
|
|
5492
|
-
import * as
|
|
5540
|
+
import * as Effect78 from "effect/Effect";
|
|
5493
5541
|
var ProxmoxPool = Resource25("Proxmox.Pool");
|
|
5494
5542
|
var poolCreateForm = (props) => ({
|
|
5495
5543
|
comment: props.comment ?? "",
|
|
@@ -5515,16 +5563,16 @@ var spec4 = {
|
|
|
5515
5563
|
updateForm: poolUpdateForm
|
|
5516
5564
|
};
|
|
5517
5565
|
var { guardCreate: guardCreate9, guardUpdate: guardUpdate9 } = specGuards(spec4);
|
|
5518
|
-
var readPool = (props) => runPve(props.target, "read", false, pools.getPool({ poolid: props.poolid })).pipe(
|
|
5566
|
+
var readPool = (props) => runPve(props.target, "read", false, pools.getPool({ poolid: props.poolid })).pipe(Effect78.flatMap((live) => Schema5.decodeUnknownEffect(Schema5.toType(pools.GetPoolResponse))(live).pipe(Effect78.mapError(() => new ProxmoxParseError5({
|
|
5519
5567
|
body: undefined,
|
|
5520
5568
|
cause: "Pool response does not match its vendor schema"
|
|
5521
|
-
})),
|
|
5522
|
-
var deletePool2 = (props) => runPve(props.target, "provision", true, pools.deletePool({ poolid: props.poolid })).pipe(
|
|
5523
|
-
var ProxmoxPoolProvider = () => Provider25.effect(ProxmoxPool,
|
|
5524
|
-
list: () =>
|
|
5569
|
+
})), Effect78.as(live))), Effect78.map((live) => spec4.attributes({ ...live }, props)), Effect78.catchTag("PoolNotFound", () => Effect78.succeed(undefined)), Effect78.catchTag("NotFound", () => Effect78.succeed(undefined)));
|
|
5570
|
+
var deletePool2 = (props) => runPve(props.target, "provision", true, pools.deletePool({ poolid: props.poolid })).pipe(Effect78.catchTag("PoolNotFound", () => Effect78.void), Effect78.catchTag("NotFound", () => Effect78.void));
|
|
5571
|
+
var ProxmoxPoolProvider = () => Provider25.effect(ProxmoxPool, Effect78.succeed(ProxmoxPool.Provider.of({
|
|
5572
|
+
list: () => Effect78.succeed([]),
|
|
5525
5573
|
read: ({ olds }) => readPool(olds),
|
|
5526
|
-
diff:
|
|
5527
|
-
if (!
|
|
5574
|
+
diff: Effect78.fn(function* ({ news, output }) {
|
|
5575
|
+
if (!isResolved24(news))
|
|
5528
5576
|
return;
|
|
5529
5577
|
yield* guardCreate9(news, output === undefined);
|
|
5530
5578
|
yield* guardUpdate9(news);
|
|
@@ -5537,7 +5585,7 @@ var ProxmoxPoolProvider = () => Provider25.effect(ProxmoxPool, Effect77.succeed(
|
|
|
5537
5585
|
}
|
|
5538
5586
|
return { action: spec4.matches(live, news) ? "noop" : "update" };
|
|
5539
5587
|
}),
|
|
5540
|
-
reconcile:
|
|
5588
|
+
reconcile: Effect78.fn(function* ({ news }) {
|
|
5541
5589
|
const live = yield* readPool(news);
|
|
5542
5590
|
yield* guardCreate9(news, live === undefined);
|
|
5543
5591
|
yield* guardUpdate9(news);
|
|
@@ -5553,7 +5601,7 @@ var ProxmoxPoolProvider = () => Provider25.effect(ProxmoxPool, Effect77.succeed(
|
|
|
5553
5601
|
}
|
|
5554
5602
|
const after = yield* readPool(news);
|
|
5555
5603
|
if (after === undefined)
|
|
5556
|
-
return yield*
|
|
5604
|
+
return yield* Effect78.fail(new Error(`pools/${news.poolid}: write returned success but the pool is still absent`));
|
|
5557
5605
|
return after;
|
|
5558
5606
|
}),
|
|
5559
5607
|
delete: ({ olds }) => deletePool2(olds)
|
|
@@ -5750,14 +5798,14 @@ echo 'provision baseline: done. Next: point the OpenBao proxmox mount at these u
|
|
|
5750
5798
|
// src/proxmox/provision-declare.ts
|
|
5751
5799
|
import { adopt } from "alchemy/AdoptPolicy";
|
|
5752
5800
|
import * as RemovalPolicy from "alchemy/RemovalPolicy";
|
|
5753
|
-
import * as
|
|
5801
|
+
import * as Effect82 from "effect/Effect";
|
|
5754
5802
|
|
|
5755
5803
|
// src/proxmox/role.ts
|
|
5756
5804
|
import { Resource as Resource26 } from "alchemy";
|
|
5757
|
-
import { isResolved as
|
|
5805
|
+
import { isResolved as isResolved25 } from "alchemy/Diff";
|
|
5758
5806
|
import * as Provider26 from "alchemy/Provider";
|
|
5759
5807
|
import * as access5 from "@distilled.cloud/proxmox/access";
|
|
5760
|
-
import * as
|
|
5808
|
+
import * as Effect79 from "effect/Effect";
|
|
5761
5809
|
|
|
5762
5810
|
// src/proxmox/role-wire.ts
|
|
5763
5811
|
var ROLE_CREATE = "pve:POST /access/roles";
|
|
@@ -5767,79 +5815,79 @@ var createForm7 = (props) => ({
|
|
|
5767
5815
|
privs: canonical2(props.privs).join(","),
|
|
5768
5816
|
roleid: props.roleid
|
|
5769
5817
|
});
|
|
5770
|
-
var
|
|
5818
|
+
var updateForm5 = (props) => ({
|
|
5771
5819
|
privs: canonical2(props.privs).join(","),
|
|
5772
5820
|
roleid: props.roleid
|
|
5773
5821
|
});
|
|
5774
5822
|
var find2 = (rows, roleid) => rows.find((row) => row.roleid === roleid);
|
|
5775
|
-
var
|
|
5823
|
+
var attributesOf8 = (row, props) => ({
|
|
5776
5824
|
privs: canonical2(row.privs === undefined ? [] : row.privs.split(",")),
|
|
5777
5825
|
roleid: props.roleid
|
|
5778
5826
|
});
|
|
5779
|
-
var
|
|
5827
|
+
var matches8 = (attributes2, props) => canonical2(attributes2.privs).join(",") === canonical2(props.privs).join(",");
|
|
5780
5828
|
|
|
5781
5829
|
// src/proxmox/role.ts
|
|
5782
5830
|
var ProxmoxRole = Resource26("Proxmox.Role");
|
|
5783
|
-
var readRole = (props) => readOrUnreadable(runPve(props.target, "read", false, access5.listAccessRoles({}))).pipe(
|
|
5831
|
+
var readRole = (props) => readOrUnreadable(runPve(props.target, "read", false, access5.listAccessRoles({}))).pipe(Effect79.map((rows) => {
|
|
5784
5832
|
if (rows === UNREADABLE)
|
|
5785
5833
|
return UNREADABLE;
|
|
5786
5834
|
const row = find2(rows, props.roleid);
|
|
5787
|
-
return row === undefined ? undefined :
|
|
5835
|
+
return row === undefined ? undefined : attributesOf8(row, props);
|
|
5788
5836
|
}));
|
|
5789
5837
|
var dropUnreadable5 = (live) => live === UNREADABLE ? undefined : live;
|
|
5790
|
-
var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole,
|
|
5791
|
-
list: () =>
|
|
5792
|
-
read:
|
|
5838
|
+
var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole, Effect79.succeed(ProxmoxRole.Provider.of({
|
|
5839
|
+
list: () => Effect79.succeed([]),
|
|
5840
|
+
read: Effect79.fn(function* ({ olds }) {
|
|
5793
5841
|
return dropUnreadable5(yield* readRole(olds));
|
|
5794
5842
|
}),
|
|
5795
|
-
diff:
|
|
5796
|
-
if (!
|
|
5843
|
+
diff: Effect79.fn(function* ({ news, output }) {
|
|
5844
|
+
if (!isResolved25(news))
|
|
5797
5845
|
return;
|
|
5798
5846
|
yield* guardWrite2(ROLE_CREATE, createForm7(news), output === undefined);
|
|
5799
|
-
yield* guardWrite2(ROLE_UPDATE,
|
|
5847
|
+
yield* guardWrite2(ROLE_UPDATE, updateForm5(news), false);
|
|
5800
5848
|
if (output === undefined)
|
|
5801
5849
|
return;
|
|
5802
5850
|
const live = yield* readRole(news);
|
|
5803
5851
|
if (live === UNREADABLE) {
|
|
5804
|
-
yield* unreadableWarning("Proxmox.Role", news.roleid);
|
|
5852
|
+
yield* unreadableWarning("Proxmox.Role", news.roleid, news.target.mount, mintTier(news.target, "read"));
|
|
5805
5853
|
return { action: "noop" };
|
|
5806
5854
|
}
|
|
5807
5855
|
if (live === undefined) {
|
|
5808
5856
|
yield* guardWrite2(ROLE_CREATE, createForm7(news), true);
|
|
5809
5857
|
return { action: "update" };
|
|
5810
5858
|
}
|
|
5811
|
-
return
|
|
5859
|
+
return matches8(live, news) ? { action: "noop" } : { action: "update" };
|
|
5812
5860
|
}),
|
|
5813
|
-
reconcile:
|
|
5861
|
+
reconcile: Effect79.fn(function* ({ news }) {
|
|
5814
5862
|
const before = dropUnreadable5(yield* readRole(news));
|
|
5815
5863
|
yield* guardWrite2(ROLE_CREATE, createForm7(news), before === undefined);
|
|
5816
|
-
yield* guardWrite2(ROLE_UPDATE,
|
|
5864
|
+
yield* guardWrite2(ROLE_UPDATE, updateForm5(news), false);
|
|
5817
5865
|
if (before === undefined) {
|
|
5818
5866
|
yield* runPve(news.target, "provision", true, access5.createAccessRole(createForm7(news)));
|
|
5819
|
-
} else if (!
|
|
5820
|
-
yield* runPve(news.target, "provision", true, access5.putAccessRole(
|
|
5867
|
+
} else if (!matches8(before, news)) {
|
|
5868
|
+
yield* runPve(news.target, "provision", true, access5.putAccessRole(updateForm5(news)));
|
|
5821
5869
|
}
|
|
5822
5870
|
const after = dropUnreadable5(yield* readRole(news));
|
|
5823
5871
|
if (after === undefined) {
|
|
5824
|
-
return yield*
|
|
5872
|
+
return yield* Effect79.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."));
|
|
5825
5873
|
}
|
|
5826
5874
|
return after;
|
|
5827
5875
|
}),
|
|
5828
|
-
delete:
|
|
5876
|
+
delete: Effect79.fn(function* ({ olds }) {
|
|
5829
5877
|
yield* runPve(olds.target, "provision", true, access5.deleteAccessRole({ roleid: olds.roleid }));
|
|
5830
5878
|
})
|
|
5831
5879
|
})));
|
|
5832
5880
|
|
|
5833
5881
|
// src/proxmox/user.ts
|
|
5834
5882
|
import { Resource as Resource27 } from "alchemy";
|
|
5835
|
-
import { isResolved as
|
|
5883
|
+
import { isResolved as isResolved26 } from "alchemy/Diff";
|
|
5836
5884
|
import * as Provider27 from "alchemy/Provider";
|
|
5837
5885
|
import * as access7 from "@distilled.cloud/proxmox/access";
|
|
5838
|
-
import * as
|
|
5886
|
+
import * as Effect81 from "effect/Effect";
|
|
5839
5887
|
|
|
5840
5888
|
// src/proxmox/user-wire.ts
|
|
5841
5889
|
import * as access6 from "@distilled.cloud/proxmox/access";
|
|
5842
|
-
import * as
|
|
5890
|
+
import * as Effect80 from "effect/Effect";
|
|
5843
5891
|
var USER_CREATE = "pve:POST /access/users";
|
|
5844
5892
|
var USER_UPDATE = "pve:PUT /access/users/{userid}";
|
|
5845
5893
|
var int2 = (value) => {
|
|
@@ -5865,11 +5913,11 @@ var createForm8 = (props) => ({
|
|
|
5865
5913
|
...shape8(props),
|
|
5866
5914
|
userid: props.userid
|
|
5867
5915
|
});
|
|
5868
|
-
var
|
|
5916
|
+
var updateForm6 = (props) => ({
|
|
5869
5917
|
...shape8(props),
|
|
5870
5918
|
userid: props.userid
|
|
5871
5919
|
});
|
|
5872
|
-
var
|
|
5920
|
+
var attributesOf9 = (live, props) => ({
|
|
5873
5921
|
comment: text(live.comment),
|
|
5874
5922
|
email: text(live.email),
|
|
5875
5923
|
enable: bool(live.enable, true),
|
|
@@ -5880,59 +5928,59 @@ var attributesOf8 = (live, props) => ({
|
|
|
5880
5928
|
tokens: tokenNames(live.tokens),
|
|
5881
5929
|
userid: props.userid
|
|
5882
5930
|
});
|
|
5883
|
-
var
|
|
5884
|
-
var readUserOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, access6.getAccessUser({ userid: props.userid }))).pipe(
|
|
5885
|
-
var readUser = (props) => readUserOrFail(props).pipe(
|
|
5931
|
+
var matches9 = (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(",");
|
|
5932
|
+
var readUserOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, access6.getAccessUser({ userid: props.userid }))).pipe(Effect80.map((live) => live === UNREADABLE ? UNREADABLE : attributesOf9(live, props)));
|
|
5933
|
+
var readUser = (props) => readUserOrFail(props).pipe(Effect80.catchTag("UserNotFound", () => Effect80.succeed(undefined)));
|
|
5886
5934
|
var dropUnreadable6 = (live) => live === UNREADABLE ? undefined : live;
|
|
5887
5935
|
|
|
5888
5936
|
// src/proxmox/user.ts
|
|
5889
5937
|
var ProxmoxUser = Resource27("Proxmox.User");
|
|
5890
|
-
var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser,
|
|
5891
|
-
list: () =>
|
|
5892
|
-
read:
|
|
5938
|
+
var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser, Effect81.succeed(ProxmoxUser.Provider.of({
|
|
5939
|
+
list: () => Effect81.succeed([]),
|
|
5940
|
+
read: Effect81.fn(function* ({ olds, output }) {
|
|
5893
5941
|
return dropUnreadable6(yield* output === undefined ? readUser(olds) : readUserOrFail(olds));
|
|
5894
5942
|
}),
|
|
5895
|
-
diff:
|
|
5896
|
-
if (!
|
|
5943
|
+
diff: Effect81.fn(function* ({ news, output }) {
|
|
5944
|
+
if (!isResolved26(news))
|
|
5897
5945
|
return;
|
|
5898
5946
|
yield* guardWrite2(USER_CREATE, createForm8(news), output === undefined);
|
|
5899
|
-
yield* guardWrite2(USER_UPDATE,
|
|
5947
|
+
yield* guardWrite2(USER_UPDATE, updateForm6(news), false);
|
|
5900
5948
|
if (output === undefined)
|
|
5901
5949
|
return;
|
|
5902
5950
|
const live = yield* readUserOrFail(news);
|
|
5903
5951
|
if (live === UNREADABLE) {
|
|
5904
|
-
yield* unreadableWarning("Proxmox.User", news.userid);
|
|
5952
|
+
yield* unreadableWarning("Proxmox.User", news.userid, news.target.mount, mintTier(news.target, "read"));
|
|
5905
5953
|
return { action: "noop" };
|
|
5906
5954
|
}
|
|
5907
5955
|
if (live === undefined) {
|
|
5908
5956
|
yield* guardWrite2(USER_CREATE, createForm8(news), true);
|
|
5909
5957
|
return { action: "update" };
|
|
5910
5958
|
}
|
|
5911
|
-
return
|
|
5959
|
+
return matches9(live, news) ? { action: "noop" } : { action: "update" };
|
|
5912
5960
|
}),
|
|
5913
|
-
reconcile:
|
|
5961
|
+
reconcile: Effect81.fn(function* ({ news }) {
|
|
5914
5962
|
const before = dropUnreadable6(yield* readUser(news));
|
|
5915
5963
|
yield* guardWrite2(USER_CREATE, createForm8(news), before === undefined);
|
|
5916
|
-
yield* guardWrite2(USER_UPDATE,
|
|
5964
|
+
yield* guardWrite2(USER_UPDATE, updateForm6(news), false);
|
|
5917
5965
|
if (before === undefined) {
|
|
5918
5966
|
yield* runPve(news.target, "provision", true, access7.createAccessUser(createForm8(news)));
|
|
5919
|
-
} else if (!
|
|
5920
|
-
yield* runPve(news.target, "provision", true, access7.putAccessUser(
|
|
5967
|
+
} else if (!matches9(before, news)) {
|
|
5968
|
+
yield* runPve(news.target, "provision", true, access7.putAccessUser(updateForm6(news)));
|
|
5921
5969
|
}
|
|
5922
5970
|
const after = dropUnreadable6(yield* readUser(news));
|
|
5923
5971
|
if (after === undefined) {
|
|
5924
|
-
return yield*
|
|
5972
|
+
return yield* Effect81.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."));
|
|
5925
5973
|
}
|
|
5926
5974
|
return after;
|
|
5927
5975
|
}),
|
|
5928
|
-
delete:
|
|
5976
|
+
delete: Effect81.fn(function* ({ olds }) {
|
|
5929
5977
|
yield* runPve(olds.target, "provision", true, access7.deleteAccessUser({ userid: olds.userid }));
|
|
5930
5978
|
})
|
|
5931
5979
|
})));
|
|
5932
5980
|
|
|
5933
5981
|
// src/proxmox/provision-declare.ts
|
|
5934
|
-
var declareProvisionBaseline = (id, target, names2 = {}, options = {}) =>
|
|
5935
|
-
const baseline = yield*
|
|
5982
|
+
var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect82.gen(function* () {
|
|
5983
|
+
const baseline = yield* Effect82.try({
|
|
5936
5984
|
catch: (cause) => cause instanceof Error ? cause : new Error(String(cause)),
|
|
5937
5985
|
try: () => provisionBaseline(names2)
|
|
5938
5986
|
});
|
|
@@ -5946,13 +5994,13 @@ var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect
|
|
|
5946
5994
|
target
|
|
5947
5995
|
}));
|
|
5948
5996
|
const group = yield* owned(ProxmoxGroup(`${id}-mint-group`, { ...baseline.group, target }));
|
|
5949
|
-
const users = yield*
|
|
5997
|
+
const users = yield* Effect82.all(baseline.users.map((user) => owned(ProxmoxUser(`${id}-${user.lane}-user`, {
|
|
5950
5998
|
comment: user.comment,
|
|
5951
5999
|
groups: [...user.groups],
|
|
5952
6000
|
target,
|
|
5953
6001
|
userid: user.userid
|
|
5954
6002
|
}))));
|
|
5955
|
-
const grants = yield*
|
|
6003
|
+
const grants = yield* Effect82.all(baseline.grants.map((grant) => owned(ProxmoxAcl(`${id}-${grant.lane}-grant`, {
|
|
5956
6004
|
path: "/",
|
|
5957
6005
|
propagate: true,
|
|
5958
6006
|
roleid: grant.roleid,
|
|
@@ -5965,12 +6013,12 @@ var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect
|
|
|
5965
6013
|
// src/proxmox/qemu.ts
|
|
5966
6014
|
import { Resource as Resource28 } from "alchemy";
|
|
5967
6015
|
import * as Provider28 from "alchemy/Provider";
|
|
5968
|
-
import * as
|
|
6016
|
+
import * as Effect86 from "effect/Effect";
|
|
5969
6017
|
|
|
5970
6018
|
// src/proxmox/qemu-lifecycle.ts
|
|
5971
|
-
import { isResolved as
|
|
6019
|
+
import { isResolved as isResolved27 } from "alchemy/Diff";
|
|
5972
6020
|
import * as nodes17 from "@distilled.cloud/proxmox/nodes";
|
|
5973
|
-
import * as
|
|
6021
|
+
import * as Effect85 from "effect/Effect";
|
|
5974
6022
|
|
|
5975
6023
|
// src/proxmox/qemu-props.ts
|
|
5976
6024
|
var NOT_CONFIG2 = new Set(["node", "target", "vmid"]);
|
|
@@ -6031,7 +6079,7 @@ var createForm9 = (props) => ({
|
|
|
6031
6079
|
...form(props),
|
|
6032
6080
|
vmid: String(props.vmid)
|
|
6033
6081
|
});
|
|
6034
|
-
var
|
|
6082
|
+
var updateForm7 = form;
|
|
6035
6083
|
|
|
6036
6084
|
// src/proxmox/qemu-errors.ts
|
|
6037
6085
|
class QemuRefusedError extends Error {
|
|
@@ -6126,7 +6174,7 @@ var netPairs = (value) => new Map(value.split(",").map((part) => part.trim()).fi
|
|
|
6126
6174
|
const at = part.indexOf("=");
|
|
6127
6175
|
return at === -1 ? [part, ""] : [part.slice(0, at), part.slice(at + 1)];
|
|
6128
6176
|
}));
|
|
6129
|
-
var modelKey = (
|
|
6177
|
+
var modelKey = (map32) => NIC_MODELS.find((model) => map32.has(model));
|
|
6130
6178
|
var netMaps2 = (declared2, live) => {
|
|
6131
6179
|
const want = netPairs(declared2);
|
|
6132
6180
|
const have = netPairs(live);
|
|
@@ -6197,44 +6245,44 @@ var judge2 = (props, config7) => {
|
|
|
6197
6245
|
var identityRefusal = (vmid, was) => was === undefined || vmid === was ? undefined : `vmid: ${String(was)} -> ${String(vmid)} is a different VM. Declare it as a new resource; ` + "this one is retained when its declaration goes.";
|
|
6198
6246
|
|
|
6199
6247
|
// src/proxmox/qemu-read.ts
|
|
6200
|
-
import * as
|
|
6248
|
+
import * as cluster11 from "@distilled.cloud/proxmox/cluster";
|
|
6201
6249
|
import * as nodes15 from "@distilled.cloud/proxmox/nodes";
|
|
6202
|
-
import * as
|
|
6250
|
+
import * as Effect83 from "effect/Effect";
|
|
6203
6251
|
var guestTypes = ["lxc", "qemu"];
|
|
6204
|
-
var readVm = (props) =>
|
|
6252
|
+
var readVm = (props) => Effect83.gen(function* () {
|
|
6205
6253
|
const credential = yield* leased(props.target, "read");
|
|
6206
|
-
const answer = yield* runPveWith(props.target, credential, false, nodes15.getNodeQemuConfig({ node: props.node, vmid: String(props.vmid) })).pipe(
|
|
6254
|
+
const answer = yield* runPveWith(props.target, credential, false, nodes15.getNodeQemuConfig({ node: props.node, vmid: String(props.vmid) })).pipe(Effect83.map((data) => ({ data, error: undefined })), Effect83.catchTag("QemuConfigNotFound", (error) => Effect83.succeed({ data: undefined, error })));
|
|
6207
6255
|
if (answer.error === undefined) {
|
|
6208
6256
|
const live = answer.data;
|
|
6209
6257
|
if (typeof live.digest !== "string" || live.digest === "") {
|
|
6210
|
-
return yield*
|
|
6258
|
+
return yield* Effect83.fail(new QemuRefusedError(`VM ${String(props.vmid)}: config read did not return a digest-bearing config.`));
|
|
6211
6259
|
}
|
|
6212
6260
|
return live;
|
|
6213
6261
|
}
|
|
6214
|
-
const rows = yield* runPveWith(props.target, credential, false,
|
|
6262
|
+
const rows = yield* runPveWith(props.target, credential, false, cluster11.listClusterResources({ type: "vm" }));
|
|
6215
6263
|
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))) {
|
|
6216
|
-
return yield*
|
|
6264
|
+
return yield* Effect83.fail(new QemuRefusedError("The cluster guest index did not identify its rows."));
|
|
6217
6265
|
}
|
|
6218
6266
|
const found = rows.find((row) => Number(row.vmid) === props.vmid);
|
|
6219
6267
|
if (found === undefined)
|
|
6220
6268
|
return;
|
|
6221
6269
|
if (found.node === props.node && found.type === "qemu")
|
|
6222
|
-
return yield*
|
|
6223
|
-
return yield*
|
|
6270
|
+
return yield* Effect83.fail(answer.error);
|
|
6271
|
+
return yield* Effect83.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."));
|
|
6224
6272
|
});
|
|
6225
6273
|
|
|
6226
6274
|
// src/proxmox/qemu-task.ts
|
|
6227
6275
|
import * as nodes16 from "@distilled.cloud/proxmox/nodes";
|
|
6228
|
-
import * as
|
|
6229
|
-
var qemuTask = (props, operation, what, polls) =>
|
|
6276
|
+
import * as Effect84 from "effect/Effect";
|
|
6277
|
+
var qemuTask = (props, operation, what, polls) => Effect84.gen(function* () {
|
|
6230
6278
|
const credential = yield* mint(props.target, "provision");
|
|
6231
6279
|
const upid = text(yield* runPveWith(props.target, credential, true, operation));
|
|
6232
6280
|
if (upid === "") {
|
|
6233
|
-
return yield*
|
|
6281
|
+
return yield* Effect84.fail(new QemuRefusedError(`${what}: PVE returned no task id, so the write cannot be confirmed and is not claimed.`));
|
|
6234
6282
|
}
|
|
6235
6283
|
let exit = "still running when this provider stopped waiting";
|
|
6236
6284
|
for (let attempt = 0;attempt < polls; attempt++) {
|
|
6237
|
-
const status = yield* runPveWith(props.target, credential, false, nodes16.getNodeTaskStatus({ node: props.node, upid })).pipe(
|
|
6285
|
+
const status = yield* runPveWith(props.target, credential, false, nodes16.getNodeTaskStatus({ node: props.node, upid })).pipe(Effect84.orElseSucceed(() => {
|
|
6238
6286
|
return;
|
|
6239
6287
|
}));
|
|
6240
6288
|
if (status === undefined || status.status === undefined) {
|
|
@@ -6245,66 +6293,66 @@ var qemuTask = (props, operation, what, polls) => Effect83.gen(function* () {
|
|
|
6245
6293
|
exit = text(status.exitstatus, "stopped with no exitstatus");
|
|
6246
6294
|
break;
|
|
6247
6295
|
}
|
|
6248
|
-
yield*
|
|
6296
|
+
yield* Effect84.sleep("1 second");
|
|
6249
6297
|
}
|
|
6250
6298
|
if (exit !== "OK") {
|
|
6251
|
-
return yield*
|
|
6299
|
+
return yield* Effect84.fail(new QemuRefusedError(`${what}: task ${upid} ended "${exit}". Read the task log before running the deploy again.`));
|
|
6252
6300
|
}
|
|
6253
6301
|
});
|
|
6254
6302
|
|
|
6255
6303
|
// src/proxmox/qemu-lifecycle.ts
|
|
6256
6304
|
var CREATE_POLLS2 = 180;
|
|
6257
6305
|
var DELETE_POLLS = 60;
|
|
6258
|
-
var
|
|
6306
|
+
var attributesOf10 = (live, props) => ({
|
|
6259
6307
|
config: storedConfig2(live),
|
|
6260
6308
|
node: props.node,
|
|
6261
6309
|
vmid: props.vmid
|
|
6262
6310
|
});
|
|
6263
|
-
var
|
|
6311
|
+
var matches10 = (attributes2, props) => declaredKeys2(props).filter(isManagedKey2).every((key) => attributes2.config[key] === wireValue2(declaredValue2(props, key)));
|
|
6264
6312
|
var spec5 = {
|
|
6265
|
-
attributes:
|
|
6313
|
+
attributes: attributesOf10,
|
|
6266
6314
|
collection: (props) => `nodes/${props.node}/qemu`,
|
|
6267
6315
|
createForm: createForm9,
|
|
6268
6316
|
endpoint: {
|
|
6269
6317
|
create: "pve:POST /nodes/{node}/qemu",
|
|
6270
6318
|
update: "pve:PUT /nodes/{node}/qemu/{vmid}/config"
|
|
6271
6319
|
},
|
|
6272
|
-
matches:
|
|
6320
|
+
matches: matches10,
|
|
6273
6321
|
path: (props) => `nodes/${props.node}/qemu/${String(props.vmid)}/config`,
|
|
6274
|
-
updateForm:
|
|
6322
|
+
updateForm: updateForm7
|
|
6275
6323
|
};
|
|
6276
6324
|
var { guardCreate: guardCreate10, guardUpdate: guardUpdate10 } = specGuards(spec5);
|
|
6277
|
-
var readQemu = (props) => readVm(props).pipe(
|
|
6278
|
-
var deleteQemu = (props) => qemuTask(props, nodes17.deleteNodeQemu({ node: props.node, vmid: String(props.vmid) }), `destroy VM ${String(props.vmid)}`, DELETE_POLLS).pipe(
|
|
6325
|
+
var readQemu = (props) => readVm(props).pipe(Effect85.map((live) => live === undefined ? undefined : attributesOf10(live, props)));
|
|
6326
|
+
var deleteQemu = (props) => qemuTask(props, nodes17.deleteNodeQemu({ node: props.node, vmid: String(props.vmid) }), `destroy VM ${String(props.vmid)}`, DELETE_POLLS).pipe(Effect85.catchTag("QemuConfigNotFound", () => Effect85.void));
|
|
6279
6327
|
var refuseUnmanaged = (props) => {
|
|
6280
6328
|
const refused2 = formRefusals(props);
|
|
6281
|
-
return refused2.length === 0 ?
|
|
6329
|
+
return refused2.length === 0 ? Effect85.void : Effect85.fail(new QemuRefusedError(refused2.join(`
|
|
6282
6330
|
`)));
|
|
6283
6331
|
};
|
|
6284
|
-
var refuseChange = (change) => change.refuse.length === 0 ?
|
|
6332
|
+
var refuseChange = (change) => change.refuse.length === 0 ? Effect85.void : Effect85.fail(new QemuRefusedError(change.refuse.join(`
|
|
6285
6333
|
`)));
|
|
6286
6334
|
var qemuHandlers = {
|
|
6287
|
-
list: () =>
|
|
6288
|
-
read:
|
|
6335
|
+
list: () => Effect85.succeed([]),
|
|
6336
|
+
read: Effect85.fn(function* ({
|
|
6289
6337
|
fqn,
|
|
6290
6338
|
instanceId,
|
|
6291
6339
|
olds,
|
|
6292
6340
|
output
|
|
6293
6341
|
}) {
|
|
6294
6342
|
const found = yield* readQemu(olds);
|
|
6295
|
-
const settled2 =
|
|
6343
|
+
const settled2 = Effect85.sync(() => found !== undefined && judge2(olds, found.config).drift.length === 0);
|
|
6296
6344
|
return yield* ownedRead({ fqn, instanceId, output }, found, settled2);
|
|
6297
6345
|
}),
|
|
6298
|
-
diff:
|
|
6346
|
+
diff: Effect85.fn(function* ({
|
|
6299
6347
|
news,
|
|
6300
6348
|
olds,
|
|
6301
6349
|
output
|
|
6302
6350
|
}) {
|
|
6303
|
-
if (!
|
|
6351
|
+
if (!isResolved27(news))
|
|
6304
6352
|
return;
|
|
6305
6353
|
const identity2 = identityRefusal(news.vmid, output?.vmid ?? olds.vmid);
|
|
6306
6354
|
if (identity2 !== undefined)
|
|
6307
|
-
return yield*
|
|
6355
|
+
return yield* Effect85.fail(new QemuRefusedError(identity2));
|
|
6308
6356
|
yield* refuseUnmanaged(news);
|
|
6309
6357
|
yield* guardCreate10(news, output === undefined);
|
|
6310
6358
|
yield* guardUpdate10(news);
|
|
@@ -6312,7 +6360,7 @@ var qemuHandlers = {
|
|
|
6312
6360
|
return;
|
|
6313
6361
|
const live = yield* readQemu(news);
|
|
6314
6362
|
if (live === undefined) {
|
|
6315
|
-
yield*
|
|
6363
|
+
yield* Effect85.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.");
|
|
6316
6364
|
yield* guardCreate10(news, true);
|
|
6317
6365
|
return { action: "update" };
|
|
6318
6366
|
}
|
|
@@ -6320,7 +6368,7 @@ var qemuHandlers = {
|
|
|
6320
6368
|
yield* refuseChange(change);
|
|
6321
6369
|
return { action: change.drift.length === 0 ? "noop" : "update" };
|
|
6322
6370
|
}),
|
|
6323
|
-
reconcile:
|
|
6371
|
+
reconcile: Effect85.fn(function* ({
|
|
6324
6372
|
fqn,
|
|
6325
6373
|
instanceId,
|
|
6326
6374
|
news,
|
|
@@ -6329,7 +6377,7 @@ var qemuHandlers = {
|
|
|
6329
6377
|
}) {
|
|
6330
6378
|
const identity2 = identityRefusal(news.vmid, output?.vmid ?? olds?.vmid);
|
|
6331
6379
|
if (identity2 !== undefined)
|
|
6332
|
-
return yield*
|
|
6380
|
+
return yield* Effect85.fail(new QemuRefusedError(identity2));
|
|
6333
6381
|
yield* refuseUnmanaged(news);
|
|
6334
6382
|
const live = yield* readQemu(news);
|
|
6335
6383
|
yield* guardCreate10(news, live === undefined);
|
|
@@ -6346,11 +6394,11 @@ var qemuHandlers = {
|
|
|
6346
6394
|
}
|
|
6347
6395
|
const after = yield* readQemu(news);
|
|
6348
6396
|
if (after === undefined) {
|
|
6349
|
-
return yield*
|
|
6397
|
+
return yield* Effect85.fail(new QemuRefusedError(`${spec5.path(news)}: write returned success but the VM is still absent`));
|
|
6350
6398
|
}
|
|
6351
6399
|
const left = judge2(news, after.config);
|
|
6352
6400
|
if (left.drift.length > 0) {
|
|
6353
|
-
return yield*
|
|
6401
|
+
return yield* Effect85.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."));
|
|
6354
6402
|
}
|
|
6355
6403
|
return after;
|
|
6356
6404
|
}),
|
|
@@ -6359,16 +6407,16 @@ var qemuHandlers = {
|
|
|
6359
6407
|
|
|
6360
6408
|
// src/proxmox/qemu.ts
|
|
6361
6409
|
var ProxmoxVm = Resource28("Proxmox.Vm", { defaultRemovalPolicy: "retain" });
|
|
6362
|
-
var ProxmoxVmProvider = () => Provider28.effect(ProxmoxVm,
|
|
6410
|
+
var ProxmoxVmProvider = () => Provider28.effect(ProxmoxVm, Effect86.succeed(ProxmoxVm.Provider.of(qemuHandlers)));
|
|
6363
6411
|
// src/proxmox/storage-download.ts
|
|
6364
6412
|
import { Resource as Resource29 } from "alchemy";
|
|
6365
6413
|
import * as Provider29 from "alchemy/Provider";
|
|
6366
|
-
import * as
|
|
6414
|
+
import * as Effect89 from "effect/Effect";
|
|
6367
6415
|
|
|
6368
6416
|
// src/proxmox/storage-download-lifecycle.ts
|
|
6369
|
-
import { isResolved as
|
|
6417
|
+
import { isResolved as isResolved28 } from "alchemy/Diff";
|
|
6370
6418
|
import * as nodes19 from "@distilled.cloud/proxmox/nodes";
|
|
6371
|
-
import * as
|
|
6419
|
+
import * as Effect88 from "effect/Effect";
|
|
6372
6420
|
|
|
6373
6421
|
// src/proxmox/storage-download-identity.ts
|
|
6374
6422
|
var PINNED = [
|
|
@@ -6403,16 +6451,16 @@ class StorageDownloadRefusedError extends Error {
|
|
|
6403
6451
|
|
|
6404
6452
|
// src/proxmox/storage-download-task.ts
|
|
6405
6453
|
import * as nodes18 from "@distilled.cloud/proxmox/nodes";
|
|
6406
|
-
import * as
|
|
6407
|
-
var storageDownloadTask = (where, operation, what, polls) =>
|
|
6454
|
+
import * as Effect87 from "effect/Effect";
|
|
6455
|
+
var storageDownloadTask = (where, operation, what, polls) => Effect87.gen(function* () {
|
|
6408
6456
|
const credential = yield* mint(where.target, "provision");
|
|
6409
6457
|
const upid = text(yield* runPveWith(where.target, credential, true, operation));
|
|
6410
6458
|
if (upid === "") {
|
|
6411
|
-
return yield*
|
|
6459
|
+
return yield* Effect87.fail(new StorageDownloadRefusedError(`${what}: PVE returned no task id, so the write cannot be confirmed and is not claimed.`));
|
|
6412
6460
|
}
|
|
6413
6461
|
let exit = "still running when this provider stopped waiting";
|
|
6414
6462
|
for (let attempt = 0;attempt < polls; attempt++) {
|
|
6415
|
-
const status = yield* runPveWith(where.target, credential, false, nodes18.getNodeTaskStatus({ node: where.node, upid })).pipe(
|
|
6463
|
+
const status = yield* runPveWith(where.target, credential, false, nodes18.getNodeTaskStatus({ node: where.node, upid })).pipe(Effect87.orElseSucceed(() => {
|
|
6416
6464
|
return;
|
|
6417
6465
|
}));
|
|
6418
6466
|
if (status === undefined || status.status === undefined) {
|
|
@@ -6423,10 +6471,10 @@ var storageDownloadTask = (where, operation, what, polls) => Effect86.gen(functi
|
|
|
6423
6471
|
exit = text(status.exitstatus, "stopped with no exitstatus");
|
|
6424
6472
|
break;
|
|
6425
6473
|
}
|
|
6426
|
-
yield*
|
|
6474
|
+
yield* Effect87.sleep("1 second");
|
|
6427
6475
|
}
|
|
6428
6476
|
if (exit !== "OK") {
|
|
6429
|
-
return yield*
|
|
6477
|
+
return yield* Effect87.fail(new StorageDownloadRefusedError(`${what}: task ${upid} ended "${exit}". Read the task log before running the deploy again.`));
|
|
6430
6478
|
}
|
|
6431
6479
|
});
|
|
6432
6480
|
|
|
@@ -6452,14 +6500,14 @@ var createRefusals2 = (props) => {
|
|
|
6452
6500
|
}
|
|
6453
6501
|
return refuse3.concat(formViolations(DOWNLOAD_ENDPOINT, downloadForm(props), true).map((why) => `${props.filename}: ${why}`));
|
|
6454
6502
|
};
|
|
6455
|
-
var readDownload = (props) =>
|
|
6503
|
+
var readDownload = (props) => Effect88.gen(function* () {
|
|
6456
6504
|
const rows = yield* runPve(props.target, "read", false, nodes19.listNodeStorageContent({
|
|
6457
6505
|
content: "import",
|
|
6458
6506
|
node: props.node,
|
|
6459
6507
|
storage: props.storage
|
|
6460
6508
|
}));
|
|
6461
6509
|
if (!Array.isArray(rows)) {
|
|
6462
|
-
return yield*
|
|
6510
|
+
return yield* Effect88.fail(new StorageDownloadRefusedError(`${props.storage}: the content list did not come back as a list.`));
|
|
6463
6511
|
}
|
|
6464
6512
|
const found = rows.find((row) => row.volid.endsWith(`/${props.filename}`));
|
|
6465
6513
|
if (found === undefined)
|
|
@@ -6473,7 +6521,7 @@ var readDownload = (props) => Effect87.gen(function* () {
|
|
|
6473
6521
|
volid: text(found.volid, "")
|
|
6474
6522
|
};
|
|
6475
6523
|
});
|
|
6476
|
-
var destroyDownload = (props) =>
|
|
6524
|
+
var destroyDownload = (props) => Effect88.gen(function* () {
|
|
6477
6525
|
const live = yield* readDownload(props);
|
|
6478
6526
|
if (live === undefined)
|
|
6479
6527
|
return;
|
|
@@ -6484,26 +6532,26 @@ var destroyDownload = (props) => Effect87.gen(function* () {
|
|
|
6484
6532
|
}), `delete ${live.volid}`, DELETE_POLLS2);
|
|
6485
6533
|
});
|
|
6486
6534
|
var storageDownloadHandlers = {
|
|
6487
|
-
list: () =>
|
|
6488
|
-
read:
|
|
6535
|
+
list: () => Effect88.succeed([]),
|
|
6536
|
+
read: Effect88.fn(function* ({
|
|
6489
6537
|
fqn,
|
|
6490
6538
|
instanceId,
|
|
6491
6539
|
olds,
|
|
6492
6540
|
output
|
|
6493
6541
|
}) {
|
|
6494
6542
|
const found = yield* readDownload(olds);
|
|
6495
|
-
return yield* ownedRead({ fqn, instanceId, output }, found,
|
|
6543
|
+
return yield* ownedRead({ fqn, instanceId, output }, found, Effect88.succeed(true));
|
|
6496
6544
|
}),
|
|
6497
|
-
diff:
|
|
6545
|
+
diff: Effect88.fn(function* ({
|
|
6498
6546
|
news,
|
|
6499
6547
|
olds,
|
|
6500
6548
|
output
|
|
6501
6549
|
}) {
|
|
6502
|
-
if (!
|
|
6550
|
+
if (!isResolved28(news))
|
|
6503
6551
|
return;
|
|
6504
6552
|
const refused2 = createRefusals2(news);
|
|
6505
6553
|
if (refused2.length > 0) {
|
|
6506
|
-
return yield*
|
|
6554
|
+
return yield* Effect88.fail(new StorageDownloadRefusedError(refused2.join(`
|
|
6507
6555
|
`)));
|
|
6508
6556
|
}
|
|
6509
6557
|
if (output === undefined)
|
|
@@ -6513,11 +6561,11 @@ var storageDownloadHandlers = {
|
|
|
6513
6561
|
return { action: "replace" };
|
|
6514
6562
|
const live = yield* readDownload(news);
|
|
6515
6563
|
if (identity2.kind === "pin-changed" && live !== undefined) {
|
|
6516
|
-
return yield*
|
|
6564
|
+
return yield* Effect88.fail(new StorageDownloadRefusedError(identity2.refuse));
|
|
6517
6565
|
}
|
|
6518
6566
|
return { action: live === undefined ? "update" : "noop" };
|
|
6519
6567
|
}),
|
|
6520
|
-
reconcile:
|
|
6568
|
+
reconcile: Effect88.fn(function* ({
|
|
6521
6569
|
fqn,
|
|
6522
6570
|
instanceId,
|
|
6523
6571
|
news,
|
|
@@ -6525,7 +6573,7 @@ var storageDownloadHandlers = {
|
|
|
6525
6573
|
}) {
|
|
6526
6574
|
const refused2 = createRefusals2(news);
|
|
6527
6575
|
if (refused2.length > 0) {
|
|
6528
|
-
return yield*
|
|
6576
|
+
return yield* Effect88.fail(new StorageDownloadRefusedError(refused2.join(`
|
|
6529
6577
|
`)));
|
|
6530
6578
|
}
|
|
6531
6579
|
const live = yield* readDownload(news);
|
|
@@ -6536,7 +6584,7 @@ var storageDownloadHandlers = {
|
|
|
6536
6584
|
}
|
|
6537
6585
|
const after = yield* readDownload(news);
|
|
6538
6586
|
if (after === undefined) {
|
|
6539
|
-
return yield*
|
|
6587
|
+
return yield* Effect88.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."));
|
|
6540
6588
|
}
|
|
6541
6589
|
return after;
|
|
6542
6590
|
}),
|
|
@@ -6545,17 +6593,17 @@ var storageDownloadHandlers = {
|
|
|
6545
6593
|
|
|
6546
6594
|
// src/proxmox/storage-download.ts
|
|
6547
6595
|
var ProxmoxStorageDownload = Resource29("Proxmox.StorageDownload");
|
|
6548
|
-
var ProxmoxStorageDownloadProvider = () => Provider29.effect(ProxmoxStorageDownload,
|
|
6596
|
+
var ProxmoxStorageDownloadProvider = () => Provider29.effect(ProxmoxStorageDownload, Effect89.succeed(ProxmoxStorageDownload.Provider.of(storageDownloadHandlers)));
|
|
6549
6597
|
// src/proxmox/replication-job.ts
|
|
6550
6598
|
import { Resource as Resource30 } from "alchemy";
|
|
6551
6599
|
import * as Provider30 from "alchemy/Provider";
|
|
6552
|
-
import * as
|
|
6600
|
+
import * as Effect91 from "effect/Effect";
|
|
6553
6601
|
|
|
6554
6602
|
// src/proxmox/replication-job-lifecycle.ts
|
|
6555
|
-
import { isResolved as
|
|
6556
|
-
import * as
|
|
6603
|
+
import { isResolved as isResolved29 } from "alchemy/Diff";
|
|
6604
|
+
import * as cluster12 from "@distilled.cloud/proxmox/cluster";
|
|
6557
6605
|
import { ProxmoxParseError as ProxmoxParseError6 } from "@distilled.cloud/proxmox/Errors";
|
|
6558
|
-
import * as
|
|
6606
|
+
import * as Effect90 from "effect/Effect";
|
|
6559
6607
|
import * as Schema6 from "effect/Schema";
|
|
6560
6608
|
|
|
6561
6609
|
// src/proxmox/replication-job-form.ts
|
|
@@ -6616,21 +6664,21 @@ var malformed = () => new ProxmoxParseError6({
|
|
|
6616
6664
|
body: undefined,
|
|
6617
6665
|
cause: "Replication job response does not match the requested vendor identity"
|
|
6618
6666
|
});
|
|
6619
|
-
var readReplicationJob = (props) => runPve(props.target, "read", false,
|
|
6667
|
+
var readReplicationJob = (props) => runPve(props.target, "read", false, cluster12.getClusterReplication({ id: jobId(props) })).pipe(Effect90.flatMap((live) => Schema6.decodeUnknownEffect(Schema6.toType(cluster12.GetClusterReplicationResponse))({
|
|
6620
6668
|
...live,
|
|
6621
6669
|
guest: int(live.guest, Number.NaN),
|
|
6622
6670
|
jobnum: int(live.jobnum, Number.NaN),
|
|
6623
6671
|
rate: undefined
|
|
6624
|
-
}).pipe(
|
|
6625
|
-
var deleteReplicationJob = (props) => runPve(props.target, "provision", true,
|
|
6672
|
+
}).pipe(Effect90.mapError(malformed), Effect90.as(live))), Effect90.flatMap((live) => live.id === jobId(props) && int(live.guest, Number.NaN) === props.guest && int(live.jobnum, Number.NaN) === props.jobnum ? Effect90.succeed(replicationJobSpec.attributes({ ...live }, props)) : Effect90.fail(malformed())), Effect90.catchTag("ReplicationJobNotFound", () => Effect90.succeed(undefined)));
|
|
6673
|
+
var deleteReplicationJob = (props) => runPve(props.target, "provision", true, cluster12.deleteClusterReplication({ id: jobId(props) })).pipe(Effect90.catchTag("ReplicationJobNotFound", () => Effect90.void));
|
|
6626
6674
|
var replicationJobHandlers = {
|
|
6627
|
-
list: () =>
|
|
6675
|
+
list: () => Effect90.succeed([]),
|
|
6628
6676
|
read: ({ olds }) => readReplicationJob(olds),
|
|
6629
|
-
diff:
|
|
6677
|
+
diff: Effect90.fn(function* ({
|
|
6630
6678
|
news,
|
|
6631
6679
|
output
|
|
6632
6680
|
}) {
|
|
6633
|
-
if (!
|
|
6681
|
+
if (!isResolved29(news))
|
|
6634
6682
|
return;
|
|
6635
6683
|
yield* guardCreate11(news, output === undefined);
|
|
6636
6684
|
yield* guardUpdate11(news);
|
|
@@ -6643,20 +6691,20 @@ var replicationJobHandlers = {
|
|
|
6643
6691
|
}
|
|
6644
6692
|
return { action: replicationJobSpec.matches(live, news) ? "noop" : "update" };
|
|
6645
6693
|
}),
|
|
6646
|
-
reconcile:
|
|
6694
|
+
reconcile: Effect90.fn(function* ({ news }) {
|
|
6647
6695
|
const live = yield* readReplicationJob(news);
|
|
6648
6696
|
yield* guardCreate11(news, live === undefined);
|
|
6649
6697
|
yield* guardUpdate11(news);
|
|
6650
6698
|
if (live === undefined) {
|
|
6651
|
-
yield* runPve(news.target, "provision", true,
|
|
6699
|
+
yield* runPve(news.target, "provision", true, cluster12.createClusterReplication(replicationJobSpec.createForm(news)));
|
|
6652
6700
|
} else {
|
|
6653
6701
|
const form2 = formToSend(replicationJobSpec.matches, live, news, replicationJobSpec.updateForm(news));
|
|
6654
6702
|
if (form2 !== undefined)
|
|
6655
|
-
yield* runPve(news.target, "provision", true,
|
|
6703
|
+
yield* runPve(news.target, "provision", true, cluster12.putClusterReplication({ ...form2, id: jobId(news) }));
|
|
6656
6704
|
}
|
|
6657
6705
|
const after = yield* readReplicationJob(news);
|
|
6658
6706
|
if (after === undefined)
|
|
6659
|
-
return yield*
|
|
6707
|
+
return yield* Effect90.fail(new Error(`${replicationJobSpec.path(news)}: write returned success but the resource is still absent`));
|
|
6660
6708
|
return after;
|
|
6661
6709
|
}),
|
|
6662
6710
|
delete: ({ olds }) => deleteReplicationJob(olds)
|
|
@@ -6664,15 +6712,15 @@ var replicationJobHandlers = {
|
|
|
6664
6712
|
|
|
6665
6713
|
// src/proxmox/replication-job.ts
|
|
6666
6714
|
var ProxmoxReplicationJob = Resource30("Proxmox.ReplicationJob");
|
|
6667
|
-
var ProxmoxReplicationJobProvider = () => Provider30.effect(ProxmoxReplicationJob,
|
|
6715
|
+
var ProxmoxReplicationJobProvider = () => Provider30.effect(ProxmoxReplicationJob, Effect91.succeed(ProxmoxReplicationJob.Provider.of(replicationJobHandlers)));
|
|
6668
6716
|
// src/proxmox/sdn-apply.ts
|
|
6669
6717
|
import { Resource as Resource31 } from "alchemy";
|
|
6670
|
-
import { isResolved as
|
|
6718
|
+
import { isResolved as isResolved30 } from "alchemy/Diff";
|
|
6671
6719
|
import * as Provider31 from "alchemy/Provider";
|
|
6672
|
-
import * as
|
|
6720
|
+
import * as Effect93 from "effect/Effect";
|
|
6673
6721
|
|
|
6674
6722
|
// src/proxmox/sdn-apply-read.ts
|
|
6675
|
-
import * as
|
|
6723
|
+
import * as Effect92 from "effect/Effect";
|
|
6676
6724
|
var MARKED = [
|
|
6677
6725
|
"cluster/sdn/zones",
|
|
6678
6726
|
"cluster/sdn/vnets",
|
|
@@ -6681,7 +6729,7 @@ var MARKED = [
|
|
|
6681
6729
|
"cluster/sdn/route-maps"
|
|
6682
6730
|
];
|
|
6683
6731
|
var subsystemAbsent = (error) => error instanceof PveError && error.status === 501;
|
|
6684
|
-
var markedPending = (target) =>
|
|
6732
|
+
var markedPending = (target) => Effect92.all(MARKED.map((collection) => pve(target, "read", "GET", `${collection}?pending=1`).pipe(Effect92.map((rows) => (rows ?? []).filter((row) => row.state !== undefined).length), Effect92.catchIf(subsystemAbsent, () => Effect92.succeed(0)))), { concurrency: "unbounded" }).pipe(Effect92.map((counts) => counts.reduce((total, count2) => total + count2, 0)));
|
|
6685
6733
|
var fabricStaged = (staged, running) => {
|
|
6686
6734
|
if (staged === undefined && running === undefined)
|
|
6687
6735
|
return 0;
|
|
@@ -6689,9 +6737,9 @@ var fabricStaged = (staged, running) => {
|
|
|
6689
6737
|
return "inconsistent";
|
|
6690
6738
|
return staged === running ? 0 : 1;
|
|
6691
6739
|
};
|
|
6692
|
-
var fabricPending = (target) =>
|
|
6740
|
+
var fabricPending = (target) => Effect92.all(["pending", "running"].map((view) => pve(target, "read", "GET", `cluster/sdn/fabrics/all?${view}=1`).pipe(Effect92.map(canonical3), Effect92.catchIf(subsystemAbsent, () => Effect92.succeed(undefined)))), { concurrency: "unbounded" }).pipe(Effect92.flatMap(([staged, running]) => {
|
|
6693
6741
|
const verdict = fabricStaged(staged, running);
|
|
6694
|
-
return verdict === "inconsistent" ?
|
|
6742
|
+
return verdict === "inconsistent" ? Effect92.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.")) : Effect92.succeed(verdict);
|
|
6695
6743
|
}));
|
|
6696
6744
|
var canonical3 = (value) => JSON.stringify(sortDeep(value));
|
|
6697
6745
|
var sortDeep = (value) => {
|
|
@@ -6704,23 +6752,23 @@ var sortDeep = (value) => {
|
|
|
6704
6752
|
}
|
|
6705
6753
|
return value;
|
|
6706
6754
|
};
|
|
6707
|
-
var sdnPendingCount = (target) =>
|
|
6755
|
+
var sdnPendingCount = (target) => Effect92.all([markedPending(target), fabricPending(target)], { concurrency: "unbounded" }).pipe(Effect92.map(([marked, fabric]) => marked + fabric));
|
|
6708
6756
|
|
|
6709
6757
|
// src/proxmox/sdn-apply.ts
|
|
6710
6758
|
var ProxmoxSdnApply = Resource31("Proxmox.SdnApply");
|
|
6711
|
-
var read5 = (props) => sdnPendingCount(props.target).pipe(
|
|
6712
|
-
var ProxmoxSdnApplyProvider = () => Provider31.effect(ProxmoxSdnApply,
|
|
6713
|
-
list: () =>
|
|
6714
|
-
read:
|
|
6759
|
+
var read5 = (props) => sdnPendingCount(props.target).pipe(Effect93.map((pending) => ({ pending })));
|
|
6760
|
+
var ProxmoxSdnApplyProvider = () => Provider31.effect(ProxmoxSdnApply, Effect93.succeed(ProxmoxSdnApply.Provider.of({
|
|
6761
|
+
list: () => Effect93.succeed([]),
|
|
6762
|
+
read: Effect93.fn(function* ({ olds }) {
|
|
6715
6763
|
return yield* read5(olds);
|
|
6716
6764
|
}),
|
|
6717
|
-
diff:
|
|
6718
|
-
if (!
|
|
6765
|
+
diff: Effect93.fn(function* ({ news }) {
|
|
6766
|
+
if (!isResolved30(news))
|
|
6719
6767
|
return;
|
|
6720
6768
|
const { pending } = yield* read5(news);
|
|
6721
6769
|
return pending === 0 ? { action: "noop" } : { action: "update" };
|
|
6722
6770
|
}),
|
|
6723
|
-
reconcile:
|
|
6771
|
+
reconcile: Effect93.fn(function* ({ news }) {
|
|
6724
6772
|
const staged = yield* read5(news);
|
|
6725
6773
|
if (staged.pending === 0)
|
|
6726
6774
|
return staged;
|
|
@@ -6728,16 +6776,16 @@ var ProxmoxSdnApplyProvider = () => Provider31.effect(ProxmoxSdnApply, Effect92.
|
|
|
6728
6776
|
yield* pve(news.target, "provision", "PUT", "cluster/sdn");
|
|
6729
6777
|
const after = yield* read5(news);
|
|
6730
6778
|
if (after.pending !== 0) {
|
|
6731
|
-
return yield*
|
|
6779
|
+
return yield* Effect93.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."));
|
|
6732
6780
|
}
|
|
6733
6781
|
return after;
|
|
6734
6782
|
}),
|
|
6735
|
-
delete: () =>
|
|
6783
|
+
delete: () => Effect93.void
|
|
6736
6784
|
})));
|
|
6737
6785
|
// src/proxmox/sdn-subnet.ts
|
|
6738
6786
|
import { Resource as Resource32 } from "alchemy";
|
|
6739
6787
|
import * as Provider32 from "alchemy/Provider";
|
|
6740
|
-
import * as
|
|
6788
|
+
import * as Effect94 from "effect/Effect";
|
|
6741
6789
|
|
|
6742
6790
|
// src/proxmox/sdn-subnet-form.ts
|
|
6743
6791
|
var subnetId = (props) => `${props.zone}-${props.cidr.replace("/", "-")}`;
|
|
@@ -6761,14 +6809,14 @@ var createForm10 = (props) => ({
|
|
|
6761
6809
|
subnet: props.cidr,
|
|
6762
6810
|
type: "subnet"
|
|
6763
6811
|
});
|
|
6764
|
-
var
|
|
6812
|
+
var updateForm8 = (props) => {
|
|
6765
6813
|
const clear = Object.entries(optional3(props)).filter(([, value]) => value === undefined).map(([key]) => key);
|
|
6766
6814
|
return withClears(body5(props), clear);
|
|
6767
6815
|
};
|
|
6768
6816
|
|
|
6769
6817
|
// src/proxmox/sdn-subnet.ts
|
|
6770
6818
|
var ProxmoxSdnSubnet = Resource32("Proxmox.SdnSubnet");
|
|
6771
|
-
var
|
|
6819
|
+
var handlers12 = pveHandlers({
|
|
6772
6820
|
attributes: (live, props) => {
|
|
6773
6821
|
const liveVnet = text(live["vnet"]);
|
|
6774
6822
|
if (liveVnet !== "" && liveVnet !== props.vnet)
|
|
@@ -6795,13 +6843,13 @@ var handlers13 = pveHandlers({
|
|
|
6795
6843
|
},
|
|
6796
6844
|
matches: (attributes2, props) => attributes2.gateway === (props.gateway ?? "") && attributes2.snat === (props.snat === true) && attributes2.dnszoneprefix === (props.dnszoneprefix ?? "") && attributes2.dhcpDnsServer === (props.dhcpDnsServer ?? "") && attributes2.dhcpRange === declaredRanges(props.dhcpRange),
|
|
6797
6845
|
path: (props) => `cluster/sdn/vnets/${props.vnet}/subnets/${subnetId(props)}`,
|
|
6798
|
-
updateForm:
|
|
6846
|
+
updateForm: updateForm8
|
|
6799
6847
|
});
|
|
6800
|
-
var ProxmoxSdnSubnetProvider = () => Provider32.effect(ProxmoxSdnSubnet,
|
|
6848
|
+
var ProxmoxSdnSubnetProvider = () => Provider32.effect(ProxmoxSdnSubnet, Effect94.succeed(ProxmoxSdnSubnet.Provider.of(handlers12)));
|
|
6801
6849
|
// src/proxmox/sdn-vnet.ts
|
|
6802
6850
|
import { Resource as Resource33 } from "alchemy";
|
|
6803
6851
|
import * as Provider33 from "alchemy/Provider";
|
|
6804
|
-
import * as
|
|
6852
|
+
import * as Effect95 from "effect/Effect";
|
|
6805
6853
|
var ProxmoxSdnVnet = Resource33("Proxmox.SdnVnet");
|
|
6806
6854
|
var tagOf = (value) => typeof value === "number" ? value : undefined;
|
|
6807
6855
|
var shape10 = (props) => {
|
|
@@ -6814,14 +6862,14 @@ var shape10 = (props) => {
|
|
|
6814
6862
|
...props.tag === undefined ? {} : { tag: String(props.tag) }
|
|
6815
6863
|
};
|
|
6816
6864
|
};
|
|
6817
|
-
var
|
|
6865
|
+
var updateForm9 = (props) => {
|
|
6818
6866
|
const unset2 = [
|
|
6819
6867
|
...(props.alias ?? "") === "" ? ["alias"] : [],
|
|
6820
6868
|
...props.tag === undefined ? ["tag"] : []
|
|
6821
6869
|
];
|
|
6822
6870
|
return withClears(shape10(props), unset2);
|
|
6823
6871
|
};
|
|
6824
|
-
var
|
|
6872
|
+
var handlers13 = pveHandlers({
|
|
6825
6873
|
readRole: "provision",
|
|
6826
6874
|
attributes: (live, props) => ({
|
|
6827
6875
|
alias: typeof live["alias"] === "string" ? live["alias"] : "",
|
|
@@ -6837,13 +6885,13 @@ var handlers14 = pveHandlers({
|
|
|
6837
6885
|
endpoint: { create: "pve:POST /cluster/sdn/vnets", update: "pve:PUT /cluster/sdn/vnets/{vnet}" },
|
|
6838
6886
|
matches: (attributes2, props) => attributes2.zone === props.zone && attributes2.alias === (props.alias ?? "") && attributes2.tag === props.tag && attributes2.vlanaware === (props.vlanaware === true) && attributes2.isolatePorts === (props.isolatePorts === true),
|
|
6839
6887
|
path: (props) => `cluster/sdn/vnets/${props.vnet}`,
|
|
6840
|
-
updateForm:
|
|
6888
|
+
updateForm: updateForm9
|
|
6841
6889
|
});
|
|
6842
|
-
var ProxmoxSdnVnetProvider = () => Provider33.effect(ProxmoxSdnVnet,
|
|
6890
|
+
var ProxmoxSdnVnetProvider = () => Provider33.effect(ProxmoxSdnVnet, Effect95.succeed(ProxmoxSdnVnet.Provider.of(handlers13)));
|
|
6843
6891
|
// src/proxmox/sdn-zone.ts
|
|
6844
6892
|
import { Resource as Resource34 } from "alchemy";
|
|
6845
6893
|
import * as Provider34 from "alchemy/Provider";
|
|
6846
|
-
import * as
|
|
6894
|
+
import * as Effect96 from "effect/Effect";
|
|
6847
6895
|
var ProxmoxSdnZone = Resource34("Proxmox.SdnZone");
|
|
6848
6896
|
var sameIpam = (live, declared2) => (live === "" ? "pve" : live) === (declared2 === undefined || declared2 === "" ? "pve" : declared2);
|
|
6849
6897
|
var body6 = (props) => ({
|
|
@@ -6864,7 +6912,7 @@ var clearable = {
|
|
|
6864
6912
|
vlan: ["dns", "dnszone", "ipam", "mtu", "nodes"],
|
|
6865
6913
|
vxlan: ["dns", "dnszone", "ipam", "mtu", "nodes"]
|
|
6866
6914
|
};
|
|
6867
|
-
var
|
|
6915
|
+
var handlers14 = pveHandlers({
|
|
6868
6916
|
readRole: "provision",
|
|
6869
6917
|
attributes: (live, props) => {
|
|
6870
6918
|
const liveType = text(live["type"]);
|
|
@@ -6896,13 +6944,13 @@ var handlers15 = pveHandlers({
|
|
|
6896
6944
|
return withClears(fields2, clear);
|
|
6897
6945
|
}
|
|
6898
6946
|
});
|
|
6899
|
-
var ProxmoxSdnZoneProvider = () => Provider34.effect(ProxmoxSdnZone,
|
|
6947
|
+
var ProxmoxSdnZoneProvider = () => Provider34.effect(ProxmoxSdnZone, Effect96.succeed(ProxmoxSdnZone.Provider.of(handlers14)));
|
|
6900
6948
|
// src/proxmox/storage.ts
|
|
6901
6949
|
import { Resource as Resource35 } from "alchemy";
|
|
6902
|
-
import { isResolved as
|
|
6950
|
+
import { isResolved as isResolved31 } from "alchemy/Diff";
|
|
6903
6951
|
import * as Provider35 from "alchemy/Provider";
|
|
6904
6952
|
import * as storage2 from "@distilled.cloud/proxmox/storage";
|
|
6905
|
-
import * as
|
|
6953
|
+
import * as Effect98 from "effect/Effect";
|
|
6906
6954
|
|
|
6907
6955
|
// src/proxmox/storage-plugin-options.ts
|
|
6908
6956
|
var SHARED_ACCEPTED_TYPES = new Set(["dir", "lvm", "btrfs", "esxi"]);
|
|
@@ -6910,12 +6958,12 @@ var sharedAccepted = (type) => SHARED_ACCEPTED_TYPES.has(type);
|
|
|
6910
6958
|
|
|
6911
6959
|
// src/proxmox/storage-wire.ts
|
|
6912
6960
|
import * as storage from "@distilled.cloud/proxmox/storage";
|
|
6913
|
-
import * as
|
|
6961
|
+
import * as Effect97 from "effect/Effect";
|
|
6914
6962
|
var str2 = (value, fallback = "") => typeof value === "string" ? value : fallback;
|
|
6915
6963
|
var tokens = (value) => value.split(",").map((part) => part.trim()).filter((part) => part !== "").sort().join(",");
|
|
6916
6964
|
var sameList2 = (declared2, live) => declared2 === undefined || tokens(declared2) === tokens(live);
|
|
6917
6965
|
var same7 = (declared2, live) => declared2 === undefined || declared2 === live;
|
|
6918
|
-
var
|
|
6966
|
+
var attributesOf11 = (live, props) => {
|
|
6919
6967
|
const data = live;
|
|
6920
6968
|
return {
|
|
6921
6969
|
comment: str2(data["comment"]),
|
|
@@ -6929,8 +6977,8 @@ var attributesOf10 = (live, props) => {
|
|
|
6929
6977
|
type: str2(data["type"], props.type)
|
|
6930
6978
|
};
|
|
6931
6979
|
};
|
|
6932
|
-
var readStorageOrFail = (props) => readOrUnreadable(runPve(props.target, "provision", false, storage.getStorage({ storage: props.storage }))).pipe(
|
|
6933
|
-
var readStorage = (props) => readStorageOrFail(props).pipe(
|
|
6980
|
+
var readStorageOrFail = (props) => readOrUnreadable(runPve(props.target, "provision", false, storage.getStorage({ storage: props.storage }))).pipe(Effect97.map((live) => live === UNREADABLE ? UNREADABLE : attributesOf11(live, props)));
|
|
6981
|
+
var readStorage = (props) => readStorageOrFail(props).pipe(Effect97.catchTag("StorageNotFound", () => Effect97.succeed(undefined)));
|
|
6934
6982
|
var dropUnreadable7 = (live) => live === UNREADABLE ? undefined : live;
|
|
6935
6983
|
var changedProps = (live, props) => ({
|
|
6936
6984
|
...sameList2(props.content, live.content) ? {} : { content: props.content },
|
|
@@ -6941,7 +6989,7 @@ var changedProps = (live, props) => ({
|
|
|
6941
6989
|
...same7(props.preallocation, live.preallocation) ? {} : { preallocation: props.preallocation },
|
|
6942
6990
|
...same7(props.comment, live.comment) ? {} : { comment: props.comment }
|
|
6943
6991
|
});
|
|
6944
|
-
var
|
|
6992
|
+
var matches11 = (attributes2, props) => !sharedAccepted(props.type) && !same7(props.shared, attributes2.shared) ? Effect97.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.")) : Effect97.succeed(Object.keys(changedProps(attributes2, props)).length === 0);
|
|
6945
6993
|
|
|
6946
6994
|
// src/proxmox/storage-form.ts
|
|
6947
6995
|
var STORAGE_CREATE = "pve:POST /storage";
|
|
@@ -6964,7 +7012,7 @@ var createForm11 = (props) => ({
|
|
|
6964
7012
|
storage: props.storage,
|
|
6965
7013
|
type: props.type
|
|
6966
7014
|
});
|
|
6967
|
-
var
|
|
7015
|
+
var updateForm10 = (props, live) => ({
|
|
6968
7016
|
...mutable2(props.type, live === undefined ? props : changedProps(live, props)),
|
|
6969
7017
|
storage: props.storage
|
|
6970
7018
|
});
|
|
@@ -6975,82 +7023,82 @@ var toDistilledUpdate3 = (form2) => underscored(form2);
|
|
|
6975
7023
|
var ProxmoxStorage = Resource35("Proxmox.Storage", {
|
|
6976
7024
|
defaultRemovalPolicy: "retain"
|
|
6977
7025
|
});
|
|
6978
|
-
var ProxmoxStorageProvider = () => Provider35.effect(ProxmoxStorage,
|
|
6979
|
-
list: () =>
|
|
6980
|
-
read:
|
|
7026
|
+
var ProxmoxStorageProvider = () => Provider35.effect(ProxmoxStorage, Effect98.succeed(ProxmoxStorage.Provider.of({
|
|
7027
|
+
list: () => Effect98.succeed([]),
|
|
7028
|
+
read: Effect98.fn(function* ({ olds, output }) {
|
|
6981
7029
|
return dropUnreadable7(yield* output === undefined ? readStorage(olds) : readStorageOrFail(olds));
|
|
6982
7030
|
}),
|
|
6983
|
-
diff:
|
|
6984
|
-
if (!
|
|
7031
|
+
diff: Effect98.fn(function* ({ news, output }) {
|
|
7032
|
+
if (!isResolved31(news))
|
|
6985
7033
|
return;
|
|
6986
7034
|
yield* guardWrite2(STORAGE_CREATE, createForm11(news), output === undefined);
|
|
6987
|
-
yield* guardWrite2(STORAGE_UPDATE,
|
|
7035
|
+
yield* guardWrite2(STORAGE_UPDATE, updateForm10(news, undefined), false);
|
|
6988
7036
|
if (output === undefined)
|
|
6989
7037
|
return;
|
|
6990
7038
|
const live = yield* readStorageOrFail(news);
|
|
6991
7039
|
if (live === UNREADABLE) {
|
|
6992
|
-
yield* unreadableWarning("Proxmox.Storage", news.storage);
|
|
7040
|
+
yield* unreadableWarning("Proxmox.Storage", news.storage, news.target.mount, mintTier(news.target, "provision"));
|
|
6993
7041
|
return { action: "noop" };
|
|
6994
7042
|
}
|
|
6995
7043
|
if (live === undefined) {
|
|
6996
7044
|
yield* guardWrite2(STORAGE_CREATE, createForm11(news), true);
|
|
6997
7045
|
return { action: "update" };
|
|
6998
7046
|
}
|
|
6999
|
-
return (yield*
|
|
7047
|
+
return (yield* matches11(live, news)) ? { action: "noop" } : { action: "update" };
|
|
7000
7048
|
}),
|
|
7001
|
-
reconcile:
|
|
7049
|
+
reconcile: Effect98.fn(function* ({ news }) {
|
|
7002
7050
|
const before = dropUnreadable7(yield* readStorage(news));
|
|
7003
7051
|
yield* guardWrite2(STORAGE_CREATE, createForm11(news), before === undefined);
|
|
7004
|
-
const update2 =
|
|
7052
|
+
const update2 = updateForm10(news, before);
|
|
7005
7053
|
yield* guardWrite2(STORAGE_UPDATE, update2, false);
|
|
7006
7054
|
if (before === undefined) {
|
|
7007
7055
|
yield* runPve(news.target, "provision", true, storage2.createStorage(toDistilledCreate4(news)));
|
|
7008
|
-
} else if (!(yield*
|
|
7056
|
+
} else if (!(yield* matches11(before, news))) {
|
|
7009
7057
|
yield* runPve(news.target, "provision", true, storage2.putStorage(toDistilledUpdate3(update2)));
|
|
7010
7058
|
}
|
|
7011
7059
|
const after = dropUnreadable7(yield* readStorage(news));
|
|
7012
7060
|
if (after === undefined) {
|
|
7013
|
-
return yield*
|
|
7061
|
+
return yield* Effect98.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."));
|
|
7014
7062
|
}
|
|
7015
7063
|
return after;
|
|
7016
7064
|
}),
|
|
7017
|
-
delete:
|
|
7065
|
+
delete: Effect98.fn(function* ({ olds }) {
|
|
7018
7066
|
yield* runPve(olds.target, "provision", true, storage2.deleteStorage({ storage: olds.storage }));
|
|
7019
7067
|
})
|
|
7020
7068
|
})));
|
|
7021
7069
|
// src/proxmox/zfs-pool.ts
|
|
7022
7070
|
import { Resource as Resource36 } from "alchemy";
|
|
7023
|
-
import { isResolved as
|
|
7071
|
+
import { isResolved as isResolved32 } from "alchemy/Diff";
|
|
7024
7072
|
import * as Provider36 from "alchemy/Provider";
|
|
7025
|
-
import * as
|
|
7073
|
+
import * as Effect101 from "effect/Effect";
|
|
7026
7074
|
|
|
7027
7075
|
// src/proxmox/zfs-pool-wire.ts
|
|
7028
7076
|
import * as nodes20 from "@distilled.cloud/proxmox/nodes";
|
|
7029
|
-
import * as
|
|
7077
|
+
import * as Effect99 from "effect/Effect";
|
|
7030
7078
|
var leafDevices = (children) => Array.isArray(children) ? children.flatMap((entry) => {
|
|
7031
7079
|
const vdev = entry;
|
|
7032
7080
|
const nested = leafDevices(vdev.children);
|
|
7033
7081
|
return nested.length > 0 ? nested : [text(vdev.name)].filter((name) => name !== "");
|
|
7034
7082
|
}) : [];
|
|
7035
|
-
var
|
|
7083
|
+
var attributesOf12 = (live, props) => ({
|
|
7036
7084
|
devices: leafDevices(live.children).join(","),
|
|
7037
7085
|
errors: text(live.errors, "unknown"),
|
|
7038
7086
|
name: props.name,
|
|
7039
7087
|
node: props.node,
|
|
7040
7088
|
state: text(live.state, "unknown")
|
|
7041
7089
|
});
|
|
7042
|
-
var readPoolOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, nodes20.getNodeDiskZfs({ name: props.name, node: props.node }))).pipe(
|
|
7090
|
+
var readPoolOrFail = (props) => readOrUnreadable(runPve(props.target, "read", false, nodes20.getNodeDiskZfs({ name: props.name, node: props.node }))).pipe(Effect99.map((live) => {
|
|
7043
7091
|
if (live === UNREADABLE)
|
|
7044
7092
|
return UNREADABLE;
|
|
7045
|
-
return text(live.name) === "" ? undefined :
|
|
7093
|
+
return text(live.name) === "" ? undefined : attributesOf12(live, props);
|
|
7046
7094
|
}));
|
|
7047
|
-
var readPool2 = (props) => readPoolOrFail(props).pipe(
|
|
7095
|
+
var readPool2 = (props) => readPoolOrFail(props).pipe(Effect99.orElseSucceed(() => {
|
|
7048
7096
|
return;
|
|
7049
7097
|
}));
|
|
7050
7098
|
var dropUnreadable8 = (live) => live === UNREADABLE ? undefined : live;
|
|
7051
7099
|
|
|
7052
7100
|
// src/proxmox/zfs-pool-form.ts
|
|
7053
|
-
import * as
|
|
7101
|
+
import * as Effect100 from "effect/Effect";
|
|
7054
7102
|
import * as nodes21 from "@distilled.cloud/proxmox/nodes";
|
|
7055
7103
|
var ZFS_POOL_CREATE = "pve:POST /nodes/{node}/disks/zfs";
|
|
7056
7104
|
var zfsPoolCreateEndpoint = (props) => props.devices === undefined || props.raidlevel === undefined ? undefined : ZFS_POOL_CREATE;
|
|
@@ -7071,25 +7119,25 @@ var toDistilledCreate5 = (props) => {
|
|
|
7071
7119
|
};
|
|
7072
7120
|
};
|
|
7073
7121
|
var SETTLE_ATTEMPTS = 30;
|
|
7074
|
-
var createPool2 = (props, read6) =>
|
|
7122
|
+
var createPool2 = (props, read6) => Effect100.gen(function* () {
|
|
7075
7123
|
const existing = yield* read6(props);
|
|
7076
7124
|
if (existing !== undefined)
|
|
7077
7125
|
return existing;
|
|
7078
7126
|
if (props.devices === undefined || props.raidlevel === undefined) {
|
|
7079
|
-
return yield*
|
|
7127
|
+
return yield* Effect100.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."));
|
|
7080
7128
|
}
|
|
7081
7129
|
yield* guardForm(zfsPoolCreateEndpoint(props), asForm2(createForm12(props)), true);
|
|
7082
7130
|
const upid = yield* runPve(props.target, "provision", true, nodes21.createNodeDiskZfs(toDistilledCreate5(props)));
|
|
7083
|
-
const settle3 = (attempts) =>
|
|
7131
|
+
const settle3 = (attempts) => Effect100.gen(function* () {
|
|
7084
7132
|
const live = yield* read6(props);
|
|
7085
7133
|
if (live !== undefined || attempts <= 0)
|
|
7086
7134
|
return live;
|
|
7087
|
-
yield*
|
|
7135
|
+
yield* Effect100.sleep("2 seconds");
|
|
7088
7136
|
return yield* settle3(attempts - 1);
|
|
7089
7137
|
});
|
|
7090
7138
|
const after = yield* settle3(SETTLE_ATTEMPTS);
|
|
7091
7139
|
if (after === undefined) {
|
|
7092
|
-
return yield*
|
|
7140
|
+
return yield* Effect100.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."));
|
|
7093
7141
|
}
|
|
7094
7142
|
return after;
|
|
7095
7143
|
});
|
|
@@ -7099,20 +7147,20 @@ var destroyPool = (props) => runPve(props.target, "provision", true, nodes21.del
|
|
|
7099
7147
|
var ProxmoxZfsPool = Resource36("Proxmox.ZfsPool", {
|
|
7100
7148
|
defaultRemovalPolicy: "retain"
|
|
7101
7149
|
});
|
|
7102
|
-
var ProxmoxZfsPoolProvider = () => Provider36.effect(ProxmoxZfsPool,
|
|
7103
|
-
list: () =>
|
|
7104
|
-
read:
|
|
7150
|
+
var ProxmoxZfsPoolProvider = () => Provider36.effect(ProxmoxZfsPool, Effect101.succeed(ProxmoxZfsPool.Provider.of({
|
|
7151
|
+
list: () => Effect101.succeed([]),
|
|
7152
|
+
read: Effect101.fn(function* ({ olds, output }) {
|
|
7105
7153
|
return dropUnreadable8(yield* output === undefined ? readPool2(olds) : readPoolOrFail(olds));
|
|
7106
7154
|
}),
|
|
7107
|
-
diff:
|
|
7108
|
-
if (!
|
|
7155
|
+
diff: Effect101.fn(function* ({ news, output }) {
|
|
7156
|
+
if (!isResolved32(news))
|
|
7109
7157
|
return;
|
|
7110
7158
|
yield* guardForm(zfsPoolCreateEndpoint(news), asForm2(createForm12(news)), output === undefined);
|
|
7111
7159
|
if (output === undefined)
|
|
7112
7160
|
return;
|
|
7113
7161
|
const live = yield* readPoolOrFail(news);
|
|
7114
7162
|
if (live === UNREADABLE) {
|
|
7115
|
-
yield* unreadableWarning("Proxmox.ZfsPool", `${news.node}/${news.name}
|
|
7163
|
+
yield* unreadableWarning("Proxmox.ZfsPool", `${news.node}/${news.name}`, news.target.mount, mintTier(news.target, "read"));
|
|
7116
7164
|
return { action: "noop" };
|
|
7117
7165
|
}
|
|
7118
7166
|
if (live === undefined) {
|
|
@@ -7121,10 +7169,10 @@ var ProxmoxZfsPoolProvider = () => Provider36.effect(ProxmoxZfsPool, Effect100.s
|
|
|
7121
7169
|
}
|
|
7122
7170
|
return { action: "noop" };
|
|
7123
7171
|
}),
|
|
7124
|
-
reconcile:
|
|
7125
|
-
return yield* createPool2(news, (props) => readPool2(props).pipe(
|
|
7172
|
+
reconcile: Effect101.fn(function* ({ news }) {
|
|
7173
|
+
return yield* createPool2(news, (props) => readPool2(props).pipe(Effect101.map(dropUnreadable8)));
|
|
7126
7174
|
}),
|
|
7127
|
-
delete:
|
|
7175
|
+
delete: Effect101.fn(function* ({ olds }) {
|
|
7128
7176
|
yield* destroyPool(olds);
|
|
7129
7177
|
})
|
|
7130
7178
|
})));
|
|
@@ -7207,5 +7255,5 @@ export {
|
|
|
7207
7255
|
violations
|
|
7208
7256
|
};
|
|
7209
7257
|
|
|
7210
|
-
//# debugId=
|
|
7258
|
+
//# debugId=267899A4B122300564756E2164756E21
|
|
7211
7259
|
//# sourceMappingURL=index.js.map
|