@homeflare/alchemy 0.31.0 → 0.31.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/proxmox/acl-wire.d.ts +26 -0
- package/dist/proxmox/acl-wire.d.ts.map +1 -0
- package/dist/proxmox/acl.d.ts.map +1 -1
- package/dist/proxmox/api-token-form.d.ts +28 -26
- package/dist/proxmox/api-token-form.d.ts.map +1 -1
- package/dist/proxmox/api-token.d.ts +48 -89
- package/dist/proxmox/api-token.d.ts.map +1 -1
- package/dist/proxmox/ceph-fs-wire.d.ts +2 -2
- package/dist/proxmox/ceph-fs-wire.d.ts.map +1 -1
- package/dist/proxmox/ceph-osd-tree.d.ts +1 -1
- package/dist/proxmox/ceph-osd-tree.d.ts.map +1 -1
- package/dist/proxmox/ceph-osd-write.d.ts +2 -2
- package/dist/proxmox/ceph-osd-write.d.ts.map +1 -1
- package/dist/proxmox/ceph-pool-settle.d.ts +1 -1
- package/dist/proxmox/ceph-pool-settle.d.ts.map +1 -1
- package/dist/proxmox/client.d.ts +2 -2
- package/dist/proxmox/client.d.ts.map +1 -1
- package/dist/proxmox/credential-errors.d.ts +27 -0
- package/dist/proxmox/credential-errors.d.ts.map +1 -0
- package/dist/proxmox/credentials.d.ts +0 -38
- package/dist/proxmox/credentials.d.ts.map +1 -1
- package/dist/proxmox/distilled-guard.d.ts +31 -0
- package/dist/proxmox/distilled-guard.d.ts.map +1 -0
- package/dist/proxmox/distilled-pve.d.ts +11 -4
- package/dist/proxmox/distilled-pve.d.ts.map +1 -1
- package/dist/proxmox/generated/constraints/index.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-access.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-access.d.ts.map +1 -1
- package/dist/proxmox/generated/constraints/pve-cluster-backup.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-cluster-ceph.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-cluster-firewall.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-cluster-ha.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-cluster-metrics.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-cluster-notifications.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-cluster-replication.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-cluster-sdn.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-nodes-ceph.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-nodes-disks.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-nodes-lxc.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-nodes-network.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-nodes-qemu.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-pools.d.ts +1 -1
- package/dist/proxmox/generated/constraints/pve-storage.d.ts +1 -1
- package/dist/proxmox/group-wire.d.ts +29 -0
- package/dist/proxmox/group-wire.d.ts.map +1 -0
- package/dist/proxmox/group.d.ts +20 -24
- package/dist/proxmox/group.d.ts.map +1 -1
- package/dist/proxmox/index.js +725 -509
- package/dist/proxmox/index.js.map +40 -33
- package/dist/proxmox/lease-cache.d.ts +1 -1
- package/dist/proxmox/lease-cache.d.ts.map +1 -1
- package/dist/proxmox/lxc-lifecycle.d.ts +5 -5
- package/dist/proxmox/lxc-lifecycle.d.ts.map +1 -1
- package/dist/proxmox/mint.d.ts +50 -0
- package/dist/proxmox/mint.d.ts.map +1 -0
- package/dist/proxmox/network-apply-read.d.ts +2 -2
- package/dist/proxmox/network-apply-read.d.ts.map +1 -1
- package/dist/proxmox/pbs-notification-target-lifecycle.d.ts +2 -2
- package/dist/proxmox/pbs-sync-job-form.d.ts +2 -2
- package/dist/proxmox/provision-cli-fake.d.ts.map +1 -1
- package/dist/proxmox/resource.d.ts +4 -4
- package/dist/proxmox/role-wire.d.ts +28 -0
- package/dist/proxmox/role-wire.d.ts.map +1 -0
- package/dist/proxmox/role.d.ts +36 -52
- package/dist/proxmox/role.d.ts.map +1 -1
- package/dist/proxmox/sdn-apply-read.d.ts +1 -1
- package/dist/proxmox/sdn-apply-read.d.ts.map +1 -1
- package/dist/proxmox/unreadable-read.d.ts +50 -0
- package/dist/proxmox/unreadable-read.d.ts.map +1 -0
- package/dist/proxmox/user-wire.d.ts +18 -0
- package/dist/proxmox/user-wire.d.ts.map +1 -0
- package/dist/proxmox/user.d.ts +25 -32
- package/dist/proxmox/user.d.ts.map +1 -1
- package/dist/proxmox/zfs-pool-write.d.ts +2 -2
- package/dist/proxmox/zfs-pool-write.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/proxmox/acl-wire.ts +83 -0
- package/src/proxmox/acl.ts +29 -82
- package/src/proxmox/adopt-noop.test.ts +18 -0
- package/src/proxmox/api-token-adopt.test.ts +28 -12
- package/src/proxmox/api-token-form.ts +50 -93
- package/src/proxmox/api-token.ts +149 -135
- package/src/proxmox/client.ts +2 -7
- package/src/proxmox/constraints-forms.test.ts +7 -27
- package/src/proxmox/credential-errors.ts +33 -0
- package/src/proxmox/credentials.test.ts +29 -3
- package/src/proxmox/credentials.ts +4 -134
- package/src/proxmox/distilled-guard.ts +42 -0
- package/src/proxmox/distilled-pve.ts +15 -4
- package/src/proxmox/generated/constraints/index.ts +1 -1
- package/src/proxmox/generated/constraints/pve-access.ts +1 -4
- package/src/proxmox/generated/constraints/pve-cluster-backup.ts +1 -1
- package/src/proxmox/generated/constraints/pve-cluster-ceph.ts +1 -1
- package/src/proxmox/generated/constraints/pve-cluster-firewall.ts +1 -1
- package/src/proxmox/generated/constraints/pve-cluster-ha.ts +1 -1
- package/src/proxmox/generated/constraints/pve-cluster-metrics.ts +1 -1
- package/src/proxmox/generated/constraints/pve-cluster-notifications.ts +1 -1
- package/src/proxmox/generated/constraints/pve-cluster-replication.ts +1 -1
- package/src/proxmox/generated/constraints/pve-cluster-sdn.ts +1 -1
- package/src/proxmox/generated/constraints/pve-nodes-ceph.ts +1 -1
- package/src/proxmox/generated/constraints/pve-nodes-disks.ts +1 -1
- package/src/proxmox/generated/constraints/pve-nodes-lxc.ts +1 -1
- package/src/proxmox/generated/constraints/pve-nodes-network.ts +1 -1
- package/src/proxmox/generated/constraints/pve-nodes-qemu.ts +1 -1
- package/src/proxmox/generated/constraints/pve-pools.ts +1 -1
- package/src/proxmox/generated/constraints/pve-storage.ts +1 -1
- package/src/proxmox/group-wire.ts +63 -0
- package/src/proxmox/group.test.ts +110 -0
- package/src/proxmox/group.ts +135 -105
- package/src/proxmox/lease-cache.ts +2 -1
- package/src/proxmox/lxc-lifecycle.ts +2 -1
- package/src/proxmox/mint.ts +158 -0
- package/src/proxmox/network-apply.ts +1 -1
- package/src/proxmox/provision-cli-fake.ts +11 -0
- package/src/proxmox/provision-declare.test.ts +6 -1
- package/src/proxmox/role-wire.ts +49 -0
- package/src/proxmox/role.test.ts +89 -0
- package/src/proxmox/role.ts +147 -106
- package/src/proxmox/unreadable-read.test.ts +83 -0
- package/src/proxmox/unreadable-read.ts +60 -0
- package/src/proxmox/user-wire.ts +112 -0
- package/src/proxmox/user.test.ts +142 -0
- package/src/proxmox/user.ts +132 -129
package/dist/proxmox/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { Resource } from "alchemy";
|
|
|
3
3
|
import { isResolved } from "alchemy/Diff";
|
|
4
4
|
import * as Provider from "alchemy/Provider";
|
|
5
5
|
import * as access from "@distilled.cloud/proxmox/access";
|
|
6
|
-
import * as
|
|
6
|
+
import * as Effect7 from "effect/Effect";
|
|
7
7
|
|
|
8
8
|
// src/proxmox/constraint-guard.ts
|
|
9
9
|
import * as Effect from "effect/Effect";
|
|
@@ -76,9 +76,6 @@ var PVE_ACCESS_CONSTRAINTS = {
|
|
|
76
76
|
password: { maxLength: 64, minLength: 8, type: "string" },
|
|
77
77
|
userid: { format: "pve-userid", maxLength: 64, required: true, type: "string" }
|
|
78
78
|
},
|
|
79
|
-
"pve:POST /access/users/{userid}/token/{tokenid}": {
|
|
80
|
-
expire: { default: "same as user", minimum: 0, type: "integer" }
|
|
81
|
-
},
|
|
82
79
|
"pve:PUT /access/acl": {
|
|
83
80
|
groups: { format: "pve-groupid-list", type: "string" },
|
|
84
81
|
path: { required: true, type: "string" },
|
|
@@ -971,6 +968,40 @@ var guardForm = (key, form, presence) => Effect.suspend(() => {
|
|
|
971
968
|
return found.length === 0 ? Effect.void : Effect.die(new Error(refusal(key, found)));
|
|
972
969
|
});
|
|
973
970
|
|
|
971
|
+
// src/proxmox/acl-wire.ts
|
|
972
|
+
var normalize = (raw) => `/${raw.split("/").filter(Boolean).join("/")}`;
|
|
973
|
+
var SUBJECT_FIELD = { group: "groups", token: "tokens", user: "users" };
|
|
974
|
+
var ENDPOINT = "pve:PUT /access/acl";
|
|
975
|
+
var tuple = (props) => ({
|
|
976
|
+
path: normalize(props.path),
|
|
977
|
+
roles: props.roleid,
|
|
978
|
+
[SUBJECT_FIELD[props.type]]: props.ugid
|
|
979
|
+
});
|
|
980
|
+
var bind = (props) => ({
|
|
981
|
+
...tuple(props),
|
|
982
|
+
propagate: props.propagate === false ? "0" : "1"
|
|
983
|
+
});
|
|
984
|
+
var asForm = (body) => Object.fromEntries(Object.entries(body).filter((e) => e[1] !== undefined));
|
|
985
|
+
var guardWrite = (props) => guardForm(ENDPOINT, asForm(bind(props)), true);
|
|
986
|
+
var identity = (grant) => [normalize(grant.path), grant.type, grant.ugid, grant.roleid].join(" ");
|
|
987
|
+
var find = (rows, props) => rows.find((row) => row.path === normalize(props.path) && row.type === props.type && row.ugid === props.ugid && row.roleid === props.roleid);
|
|
988
|
+
var propagates = (row) => {
|
|
989
|
+
const value = row.propagate;
|
|
990
|
+
return value === undefined || value === 1 || value === true || value === "1";
|
|
991
|
+
};
|
|
992
|
+
var attributesOf = (rows, props) => {
|
|
993
|
+
const row = find(rows, props);
|
|
994
|
+
return {
|
|
995
|
+
bound: row !== undefined,
|
|
996
|
+
path: normalize(props.path),
|
|
997
|
+
propagate: row !== undefined && propagates(row),
|
|
998
|
+
roleid: props.roleid,
|
|
999
|
+
type: props.type,
|
|
1000
|
+
ugid: props.ugid
|
|
1001
|
+
};
|
|
1002
|
+
};
|
|
1003
|
+
var matches = (attributes, props) => attributes.bound && attributes.propagate === (props.propagate !== false);
|
|
1004
|
+
|
|
974
1005
|
// src/proxmox/distilled-pve.ts
|
|
975
1006
|
import * as Effect5 from "effect/Effect";
|
|
976
1007
|
import * as Result2 from "effect/Result";
|
|
@@ -983,13 +1014,24 @@ import * as Duration from "effect/Duration";
|
|
|
983
1014
|
import * as Effect3 from "effect/Effect";
|
|
984
1015
|
import * as Exit from "effect/Exit";
|
|
985
1016
|
|
|
986
|
-
// src/proxmox/
|
|
1017
|
+
// src/proxmox/mint.ts
|
|
987
1018
|
import * as Effect2 from "effect/Effect";
|
|
988
1019
|
import * as Option from "effect/Option";
|
|
989
1020
|
import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
|
|
990
1021
|
import * as Headers from "effect/unstable/http/Headers";
|
|
991
1022
|
import * as HttpClient from "effect/unstable/http/HttpClient";
|
|
992
1023
|
import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest";
|
|
1024
|
+
|
|
1025
|
+
// src/proxmox/credential-errors.ts
|
|
1026
|
+
import * as Data from "effect/Data";
|
|
1027
|
+
|
|
1028
|
+
class PveCredentialDenied extends Data.TaggedError("PveCredentialDenied") {
|
|
1029
|
+
get message() {
|
|
1030
|
+
return `OpenBao GET /v1/${this.mount}/creds/${this.role} -> 403: ${this.detail}. This identity's ` + "AppRole has no grant for this role -- a refused read, not evidence the object is gone.";
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
// src/proxmox/mint.ts
|
|
993
1035
|
var baoVariable = (env, name) => env[`BAO_${name}`] ?? env[`VAULT_${name}`] ?? "";
|
|
994
1036
|
var mint = (target, role, env = process.env) => Effect2.gen(function* () {
|
|
995
1037
|
const path = `${target.mount}/creds/${role}`;
|
|
@@ -1022,7 +1064,11 @@ var mint = (target, role, env = process.env) => Effect2.gen(function* () {
|
|
|
1022
1064
|
}
|
|
1023
1065
|
if (status < 200 || status >= 300) {
|
|
1024
1066
|
const errors = parsed?.errors;
|
|
1025
|
-
const
|
|
1067
|
+
const isOpenBaoBody = Array.isArray(errors);
|
|
1068
|
+
const detail = isOpenBaoBody ? errors.map(String).join("; ") : "not an OpenBao error body";
|
|
1069
|
+
if (status === 403 && isOpenBaoBody) {
|
|
1070
|
+
return yield* Effect2.fail(new PveCredentialDenied({ detail, mount: target.mount, role }));
|
|
1071
|
+
}
|
|
1026
1072
|
return yield* Effect2.fail(refuse(status, detail || "(no errors given)"));
|
|
1027
1073
|
}
|
|
1028
1074
|
const tokenId = parsed?.data?.token_id;
|
|
@@ -1172,49 +1218,24 @@ var runPve = (target, role, isWrite, op) => Effect5.gen(function* () {
|
|
|
1172
1218
|
return yield* runPveWith(target, credential, isWrite, op);
|
|
1173
1219
|
});
|
|
1174
1220
|
|
|
1221
|
+
// src/proxmox/unreadable-read.ts
|
|
1222
|
+
import * as Effect6 from "effect/Effect";
|
|
1223
|
+
var UNREADABLE = Symbol("pve-unreadable");
|
|
1224
|
+
var readOrUnreadable = (effect) => effect.pipe(Effect6.catchTag("PveCredentialDenied", () => Effect6.succeed(UNREADABLE)));
|
|
1225
|
+
var unreadableWarning = (type, identity2) => Effect6.logWarning(`${type} ${identity2}: the read 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.");
|
|
1226
|
+
|
|
1175
1227
|
// src/proxmox/acl.ts
|
|
1176
1228
|
var ProxmoxAcl = Resource("Proxmox.Acl");
|
|
1177
|
-
var
|
|
1178
|
-
var SUBJECT_FIELD = { group: "groups", token: "tokens", user: "users" };
|
|
1179
|
-
var ENDPOINT = "pve:PUT /access/acl";
|
|
1180
|
-
var tuple = (props) => ({
|
|
1181
|
-
path: normalize(props.path),
|
|
1182
|
-
roles: props.roleid,
|
|
1183
|
-
[SUBJECT_FIELD[props.type]]: props.ugid
|
|
1184
|
-
});
|
|
1185
|
-
var bind = (props) => ({
|
|
1186
|
-
...tuple(props),
|
|
1187
|
-
propagate: props.propagate === false ? "0" : "1"
|
|
1188
|
-
});
|
|
1189
|
-
var asForm = (body) => Object.fromEntries(Object.entries(body).filter((e) => e[1] !== undefined));
|
|
1190
|
-
var guardWrite = (props) => guardForm(ENDPOINT, asForm(bind(props)), true);
|
|
1191
|
-
var identity = (grant) => [normalize(grant.path), grant.type, grant.ugid, grant.roleid].join(" ");
|
|
1192
|
-
var find = (rows, props) => rows.find((row) => row.path === normalize(props.path) && row.type === props.type && row.ugid === props.ugid && row.roleid === props.roleid);
|
|
1193
|
-
var propagates = (row) => {
|
|
1194
|
-
const value = row.propagate;
|
|
1195
|
-
return value === undefined || value === 1 || value === true || value === "1";
|
|
1196
|
-
};
|
|
1197
|
-
var attributesOf = (rows, props) => {
|
|
1198
|
-
const row = find(rows, props);
|
|
1199
|
-
return {
|
|
1200
|
-
bound: row !== undefined,
|
|
1201
|
-
path: normalize(props.path),
|
|
1202
|
-
propagate: row !== undefined && propagates(row),
|
|
1203
|
-
roleid: props.roleid,
|
|
1204
|
-
type: props.type,
|
|
1205
|
-
ugid: props.ugid
|
|
1206
|
-
};
|
|
1207
|
-
};
|
|
1208
|
-
var matches = (attributes, props) => attributes.bound && attributes.propagate === (props.propagate !== false);
|
|
1209
|
-
var readAttributes = (props) => runPve(props.target, "provision", false, access.listAccessAcl({})).pipe(Effect6.map((rows) => attributesOf(rows, props)), Effect6.orElseSucceed(() => {
|
|
1229
|
+
var readAttributes = (props) => readOrUnreadable(runPve(props.target, "provision", false, access.listAccessAcl({}))).pipe(Effect7.map((rows) => rows === UNREADABLE ? UNREADABLE : attributesOf(rows, props)), Effect7.orElseSucceed(() => {
|
|
1210
1230
|
return;
|
|
1211
1231
|
}));
|
|
1212
|
-
var
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1232
|
+
var dropUnreadable = (live) => live === UNREADABLE ? undefined : live;
|
|
1233
|
+
var ProxmoxAclProvider = () => Provider.effect(ProxmoxAcl, Effect7.succeed(ProxmoxAcl.Provider.of({
|
|
1234
|
+
list: () => Effect7.succeed([]),
|
|
1235
|
+
read: Effect7.fn(function* ({ olds }) {
|
|
1236
|
+
return dropUnreadable(yield* readAttributes(olds));
|
|
1216
1237
|
}),
|
|
1217
|
-
diff:
|
|
1238
|
+
diff: Effect7.fn(function* ({ news, output }) {
|
|
1218
1239
|
if (output !== undefined && isResolved(news) && identity(news) !== identity(output)) {
|
|
1219
1240
|
return { action: "replace" };
|
|
1220
1241
|
}
|
|
@@ -1224,21 +1245,25 @@ var ProxmoxAclProvider = () => Provider.effect(ProxmoxAcl, Effect6.succeed(Proxm
|
|
|
1224
1245
|
if (output === undefined)
|
|
1225
1246
|
return;
|
|
1226
1247
|
const live = yield* readAttributes(news);
|
|
1248
|
+
if (live === UNREADABLE) {
|
|
1249
|
+
yield* unreadableWarning("Proxmox.Acl", identity(news));
|
|
1250
|
+
return { action: "noop" };
|
|
1251
|
+
}
|
|
1227
1252
|
return live !== undefined && matches(live, news) ? { action: "noop" } : { action: "update" };
|
|
1228
1253
|
}),
|
|
1229
|
-
reconcile:
|
|
1230
|
-
const before = yield* readAttributes(news);
|
|
1254
|
+
reconcile: Effect7.fn(function* ({ news }) {
|
|
1255
|
+
const before = dropUnreadable(yield* readAttributes(news));
|
|
1231
1256
|
yield* guardWrite(news);
|
|
1232
1257
|
if (before === undefined || !matches(before, news)) {
|
|
1233
1258
|
yield* runPve(news.target, "provision", true, access.putAccessAcl(bind(news)));
|
|
1234
1259
|
}
|
|
1235
|
-
const after = yield* readAttributes(news);
|
|
1260
|
+
const after = dropUnreadable(yield* readAttributes(news));
|
|
1236
1261
|
if (after === undefined || !after.bound) {
|
|
1237
|
-
return yield*
|
|
1262
|
+
return yield* Effect7.die(new Error(`access/acl: the write returned no error but ${identity(news)} is still not ` + "bound. Either the PUT did nothing, or the read credential cannot see the " + "grant -- GET /access/acl is filtered to paths you may MODIFY permissions on, " + "so a read-only role is answered [] rather than 403."));
|
|
1238
1263
|
}
|
|
1239
1264
|
return after;
|
|
1240
1265
|
}),
|
|
1241
|
-
delete:
|
|
1266
|
+
delete: Effect7.fn(function* ({ olds }) {
|
|
1242
1267
|
const removal = { ...tuple(olds), delete: "1" };
|
|
1243
1268
|
yield* runPve(olds.target, "provision", true, access.putAccessAcl(removal));
|
|
1244
1269
|
})
|
|
@@ -1295,8 +1320,10 @@ var alertmanagerAlertBody = (options = {}) => {
|
|
|
1295
1320
|
};
|
|
1296
1321
|
// src/proxmox/api-token.ts
|
|
1297
1322
|
import { Resource as Resource2 } from "alchemy";
|
|
1323
|
+
import { isResolved as isResolved2 } from "alchemy/Diff";
|
|
1298
1324
|
import * as Provider2 from "alchemy/Provider";
|
|
1299
|
-
import * as
|
|
1325
|
+
import * as access2 from "@distilled.cloud/proxmox/access";
|
|
1326
|
+
import * as Effect8 from "effect/Effect";
|
|
1300
1327
|
|
|
1301
1328
|
// src/proxmox/values.ts
|
|
1302
1329
|
var num = (value, fallback) => typeof value === "number" ? value : fallback;
|
|
@@ -1340,45 +1367,89 @@ var withClears = (fields, clear) => {
|
|
|
1340
1367
|
};
|
|
1341
1368
|
|
|
1342
1369
|
// src/proxmox/api-token-form.ts
|
|
1370
|
+
var API_TOKEN_UPDATE = "pve:PUT /access/users/{userid}/token/{tokenid}";
|
|
1343
1371
|
var fullTokenid = (props) => `${props.userid}!${props.tokenid}`;
|
|
1344
1372
|
var shape = (props) => ({
|
|
1345
1373
|
comment: props.comment ?? "",
|
|
1346
|
-
expire:
|
|
1347
|
-
privsep: props.privsep ? "1" : "0"
|
|
1374
|
+
expire: String(props.expire),
|
|
1375
|
+
privsep: props.privsep ? "1" : "0",
|
|
1376
|
+
tokenid: props.tokenid,
|
|
1377
|
+
userid: props.userid
|
|
1348
1378
|
});
|
|
1349
|
-
var
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
privsep: bool(privsep, true),
|
|
1361
|
-
tokenid: props.tokenid,
|
|
1362
|
-
userid: props.userid
|
|
1363
|
-
};
|
|
1364
|
-
},
|
|
1365
|
-
collection: (props) => `access/users/${props.userid}/token/${props.tokenid}`,
|
|
1366
|
-
createForm: shape,
|
|
1367
|
-
endpoint: {
|
|
1368
|
-
create: "pve:POST /access/users/{userid}/token/{tokenid}",
|
|
1369
|
-
update: "pve:PUT /access/users/{userid}/token/{tokenid}"
|
|
1370
|
-
},
|
|
1371
|
-
matches: (attributes, props) => attributes.comment === (props.comment ?? "") && attributes.expire === props.expire && attributes.privsep === props.privsep,
|
|
1372
|
-
path: (props) => `access/users/${props.userid}/token/${props.tokenid}`,
|
|
1373
|
-
updateForm: shape
|
|
1379
|
+
var attributesOf2 = (live, props) => {
|
|
1380
|
+
if (live.expire === undefined && live.privsep === undefined)
|
|
1381
|
+
return;
|
|
1382
|
+
return {
|
|
1383
|
+
comment: text(live.comment),
|
|
1384
|
+
expire: int(live.expire, 0),
|
|
1385
|
+
fullTokenid: fullTokenid(props),
|
|
1386
|
+
privsep: bool(live.privsep, true),
|
|
1387
|
+
tokenid: props.tokenid,
|
|
1388
|
+
userid: props.userid
|
|
1389
|
+
};
|
|
1374
1390
|
};
|
|
1391
|
+
var matches2 = (attributes, props) => attributes.comment === (props.comment ?? "") && attributes.expire === props.expire && attributes.privsep === props.privsep;
|
|
1392
|
+
|
|
1393
|
+
// src/proxmox/distilled-guard.ts
|
|
1394
|
+
var asForm2 = (body) => Object.fromEntries(Object.entries(body).filter((e) => typeof e[1] === "string"));
|
|
1395
|
+
var guardWrite2 = (endpoint, body, presence) => guardForm(endpoint, asForm2(body), presence);
|
|
1396
|
+
|
|
1397
|
+
// src/proxmox/api-token.ts
|
|
1398
|
+
var ProxmoxApiToken = Resource2("Proxmox.ApiToken", {
|
|
1399
|
+
defaultRemovalPolicy: "retain"
|
|
1400
|
+
});
|
|
1401
|
+
var readToken = (props) => readOrUnreadable(runPve(props.target, "provision", false, access2.getAccessUserToken(props))).pipe(Effect8.map((live) => live === UNREADABLE ? UNREADABLE : attributesOf2(live, props)), Effect8.orElseSucceed(() => {
|
|
1402
|
+
return;
|
|
1403
|
+
}));
|
|
1404
|
+
var dropUnreadable2 = (live) => live === UNREADABLE ? undefined : live;
|
|
1405
|
+
var ProxmoxApiTokenProvider = () => Provider2.effect(ProxmoxApiToken, Effect8.succeed(ProxmoxApiToken.Provider.of({
|
|
1406
|
+
list: () => Effect8.succeed([]),
|
|
1407
|
+
read: Effect8.fn(function* ({ olds }) {
|
|
1408
|
+
return dropUnreadable2(yield* readToken(olds));
|
|
1409
|
+
}),
|
|
1410
|
+
diff: Effect8.fn(function* ({ news, output }) {
|
|
1411
|
+
if (!isResolved2(news))
|
|
1412
|
+
return;
|
|
1413
|
+
yield* guardWrite2(API_TOKEN_UPDATE, shape(news), false);
|
|
1414
|
+
if (output === undefined)
|
|
1415
|
+
return;
|
|
1416
|
+
const live = yield* readToken(news);
|
|
1417
|
+
if (live === UNREADABLE) {
|
|
1418
|
+
yield* unreadableWarning("Proxmox.ApiToken", `${news.userid}!${news.tokenid}`);
|
|
1419
|
+
return { action: "noop" };
|
|
1420
|
+
}
|
|
1421
|
+
return live !== undefined && matches2(live, news) ? { action: "noop" } : { action: "update" };
|
|
1422
|
+
}),
|
|
1423
|
+
reconcile: Effect8.fn(function* ({ news }) {
|
|
1424
|
+
const before = dropUnreadable2(yield* readToken(news));
|
|
1425
|
+
if (before === undefined) {
|
|
1426
|
+
return yield* Effect8.die(new Error(`${news.userid}!${news.tokenid}: this resource does not create API tokens. PVE ` + "returns the secret only in the create response and it cannot be stored, so a " + "token made here would be a live credential nobody holds. Create it with " + "`pveum user token add` and capture the value, or mint a short-lived one from " + "the OpenBao proxmox mount (`bao read proxmox-c1/creds/<role>`) -- then declare " + "it here to manage it."));
|
|
1427
|
+
}
|
|
1428
|
+
yield* guardWrite2(API_TOKEN_UPDATE, shape(news), false);
|
|
1429
|
+
if (!matches2(before, news)) {
|
|
1430
|
+
yield* runPve(news.target, "provision", true, access2.putAccessUserToken(shape(news)));
|
|
1431
|
+
}
|
|
1432
|
+
const after = dropUnreadable2(yield* readToken(news));
|
|
1433
|
+
if (after === undefined) {
|
|
1434
|
+
return yield* Effect8.die(new Error(`${news.userid}!${news.tokenid}: the write returned no error but the token 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."));
|
|
1435
|
+
}
|
|
1436
|
+
return after;
|
|
1437
|
+
}),
|
|
1438
|
+
delete: Effect8.fn(function* ({ olds }) {
|
|
1439
|
+
yield* runPve(olds.target, "provision", true, access2.deleteAccessUserToken({ tokenid: olds.tokenid, userid: olds.userid }));
|
|
1440
|
+
})
|
|
1441
|
+
})));
|
|
1442
|
+
// src/proxmox/backup-job.ts
|
|
1443
|
+
import { Resource as Resource3 } from "alchemy";
|
|
1444
|
+
import * as Provider3 from "alchemy/Provider";
|
|
1445
|
+
import * as Effect12 from "effect/Effect";
|
|
1375
1446
|
|
|
1376
1447
|
// src/proxmox/resource.ts
|
|
1377
|
-
import { isResolved as
|
|
1378
|
-
import * as
|
|
1448
|
+
import { isResolved as isResolved3 } from "alchemy/Diff";
|
|
1449
|
+
import * as Effect11 from "effect/Effect";
|
|
1379
1450
|
|
|
1380
1451
|
// src/proxmox/client.ts
|
|
1381
|
-
import * as
|
|
1452
|
+
import * as Effect9 from "effect/Effect";
|
|
1382
1453
|
import * as HttpClient5 from "effect/unstable/http/HttpClient";
|
|
1383
1454
|
import * as HttpClientRequest2 from "effect/unstable/http/HttpClientRequest";
|
|
1384
1455
|
class PveError extends Error {
|
|
@@ -1409,69 +1480,69 @@ var buildRequest = (method, apiBase, path, credential, target, form) => {
|
|
|
1409
1480
|
const url = `${apiBase}/${path}`;
|
|
1410
1481
|
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"));
|
|
1411
1482
|
};
|
|
1412
|
-
var executeOne = (target, credential, method, path, form, apiBase) =>
|
|
1483
|
+
var executeOne = (target, credential, method, path, form, apiBase) => Effect9.gen(function* () {
|
|
1413
1484
|
const client = yield* HttpClient5.HttpClient;
|
|
1414
1485
|
const request = buildRequest(method, apiBase, path, credential, target, form);
|
|
1415
|
-
return yield* client.execute(request).pipe(
|
|
1486
|
+
return yield* client.execute(request).pipe(Effect9.mapError((cause) => new PveError(0, method, path, String(cause))));
|
|
1416
1487
|
});
|
|
1417
|
-
var readResponse = (response, method, path) =>
|
|
1488
|
+
var readResponse = (response, method, path) => Effect9.gen(function* () {
|
|
1418
1489
|
if (response.status < 200 || response.status >= 300) {
|
|
1419
|
-
const detail = yield* response.text.pipe(
|
|
1420
|
-
return yield*
|
|
1490
|
+
const detail = yield* response.text.pipe(Effect9.orElseSucceed(() => ""));
|
|
1491
|
+
return yield* Effect9.fail(new PveError(response.status, method, path, detail.slice(0, 300)));
|
|
1421
1492
|
}
|
|
1422
|
-
const body = yield* response.json.pipe(
|
|
1493
|
+
const body = yield* response.json.pipe(Effect9.mapError((cause) => new PveError(0, method, path, String(cause))));
|
|
1423
1494
|
return body.data;
|
|
1424
1495
|
});
|
|
1425
|
-
var pveWith = (target, credential, method, path, form) =>
|
|
1426
|
-
const response = target.scheme === "pve" ? yield* executeOnCluster(target, method, path, (apiBase) => buildRequest(method, apiBase, path, credential, target, form)).pipe(
|
|
1496
|
+
var pveWith = (target, credential, method, path, form) => Effect9.gen(function* () {
|
|
1497
|
+
const response = target.scheme === "pve" ? yield* executeOnCluster(target, method, path, (apiBase) => buildRequest(method, apiBase, path, credential, target, form)).pipe(Effect9.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);
|
|
1427
1498
|
return yield* readResponse(response, method, path);
|
|
1428
1499
|
});
|
|
1429
|
-
var pveEnvelopeWith = (target, credential, method, path) =>
|
|
1430
|
-
const response = yield* executeOnCluster(target, method, path, (apiBase) => buildRequest(method, apiBase, path, credential, target)).pipe(
|
|
1500
|
+
var pveEnvelopeWith = (target, credential, method, path) => Effect9.gen(function* () {
|
|
1501
|
+
const response = yield* executeOnCluster(target, method, path, (apiBase) => buildRequest(method, apiBase, path, credential, target)).pipe(Effect9.mapError((failure) => failure.tag === "cluster" ? clusterExhausted(failure.method, failure.path, failure.attempts) : new PveError(0, method, path, String(failure))));
|
|
1431
1502
|
if (response.status < 200 || response.status >= 300) {
|
|
1432
|
-
const detail = yield* response.text.pipe(
|
|
1433
|
-
return yield*
|
|
1503
|
+
const detail = yield* response.text.pipe(Effect9.orElseSucceed(() => ""));
|
|
1504
|
+
return yield* Effect9.fail(new PveError(response.status, method, path, detail.slice(0, 300)));
|
|
1434
1505
|
}
|
|
1435
|
-
return yield* response.json.pipe(
|
|
1506
|
+
return yield* response.json.pipe(Effect9.mapError((cause) => new PveError(0, method, path, String(cause))));
|
|
1436
1507
|
});
|
|
1437
|
-
var pve = (target, role, method, path, form) =>
|
|
1508
|
+
var pve = (target, role, method, path, form) => Effect9.gen(function* () {
|
|
1438
1509
|
const credential = yield* leased(target, role);
|
|
1439
1510
|
return yield* pveWith(target, credential, method, path, form);
|
|
1440
1511
|
});
|
|
1441
1512
|
|
|
1442
1513
|
// src/proxmox/resource-guard.ts
|
|
1443
|
-
import * as
|
|
1514
|
+
import * as Effect10 from "effect/Effect";
|
|
1444
1515
|
var specGuards = (spec) => {
|
|
1445
1516
|
const endpointsOf = (props) => typeof spec.endpoint === "function" ? spec.endpoint(props) : spec.endpoint;
|
|
1446
|
-
const guardCreate = (props, presence) =>
|
|
1517
|
+
const guardCreate = (props, presence) => Effect10.suspend(() => {
|
|
1447
1518
|
const create = endpointsOf(props)?.create;
|
|
1448
|
-
return create === undefined ?
|
|
1519
|
+
return create === undefined ? Effect10.void : guardForm(create, spec.createForm(props), presence);
|
|
1449
1520
|
});
|
|
1450
|
-
const guardUpdate = (props) =>
|
|
1521
|
+
const guardUpdate = (props) => Effect10.suspend(() => {
|
|
1451
1522
|
const update = endpointsOf(props)?.update;
|
|
1452
1523
|
if (update === undefined)
|
|
1453
|
-
return
|
|
1524
|
+
return Effect10.void;
|
|
1454
1525
|
const form = spec.updateForm?.(props);
|
|
1455
|
-
return form === undefined ?
|
|
1526
|
+
return form === undefined ? Effect10.void : guardForm(update, form, false);
|
|
1456
1527
|
});
|
|
1457
1528
|
return { guardCreate, guardUpdate };
|
|
1458
1529
|
};
|
|
1459
1530
|
|
|
1460
1531
|
// src/proxmox/update-guard.ts
|
|
1461
|
-
var formToSend = (
|
|
1532
|
+
var formToSend = (matches3, live, props, form) => matches3(live, props) || Object.keys(form).length === 0 ? undefined : form;
|
|
1462
1533
|
|
|
1463
1534
|
// src/proxmox/resource.ts
|
|
1464
1535
|
var pveOperations = (spec) => {
|
|
1465
1536
|
const { guardCreate, guardUpdate } = specGuards(spec);
|
|
1466
|
-
const read = (props) => pve(props.target, spec.readRole ?? "read", "GET", spec.path(props)).pipe(
|
|
1537
|
+
const read = (props) => pve(props.target, spec.readRole ?? "read", "GET", spec.path(props)).pipe(Effect11.map((data) => data == null ? undefined : spec.attributes(data, props)), Effect11.orElseSucceed(() => {
|
|
1467
1538
|
return;
|
|
1468
1539
|
}));
|
|
1469
1540
|
return {
|
|
1470
1541
|
guardCreate,
|
|
1471
1542
|
guardUpdate,
|
|
1472
1543
|
read,
|
|
1473
|
-
diff: (news, output) =>
|
|
1474
|
-
if (!
|
|
1544
|
+
diff: (news, output) => Effect11.gen(function* () {
|
|
1545
|
+
if (!isResolved3(news))
|
|
1475
1546
|
return;
|
|
1476
1547
|
yield* guardCreate(news, output === undefined);
|
|
1477
1548
|
yield* guardUpdate(news);
|
|
@@ -1486,7 +1557,7 @@ var pveOperations = (spec) => {
|
|
|
1486
1557
|
return { action: "noop" };
|
|
1487
1558
|
return spec.updateForm === undefined ? { action: "replace" } : { action: "update" };
|
|
1488
1559
|
}),
|
|
1489
|
-
reconcile: (news) =>
|
|
1560
|
+
reconcile: (news) => Effect11.gen(function* () {
|
|
1490
1561
|
const live = yield* read(news);
|
|
1491
1562
|
yield* guardCreate(news, live === undefined);
|
|
1492
1563
|
yield* guardUpdate(news);
|
|
@@ -1500,7 +1571,7 @@ var pveOperations = (spec) => {
|
|
|
1500
1571
|
}
|
|
1501
1572
|
const after = yield* read(news);
|
|
1502
1573
|
if (after === undefined) {
|
|
1503
|
-
return yield*
|
|
1574
|
+
return yield* Effect11.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."));
|
|
1504
1575
|
}
|
|
1505
1576
|
return after;
|
|
1506
1577
|
}),
|
|
@@ -1510,7 +1581,7 @@ var pveOperations = (spec) => {
|
|
|
1510
1581
|
var pveHandlers = (spec) => {
|
|
1511
1582
|
const ops = pveOperations(spec);
|
|
1512
1583
|
return {
|
|
1513
|
-
list: () =>
|
|
1584
|
+
list: () => Effect11.succeed([]),
|
|
1514
1585
|
read: ({ olds }) => ops.read(olds),
|
|
1515
1586
|
diff: ({ news, output }) => ops.diff(news, output),
|
|
1516
1587
|
reconcile: ({ news }) => ops.reconcile(news),
|
|
@@ -1518,26 +1589,7 @@ var pveHandlers = (spec) => {
|
|
|
1518
1589
|
};
|
|
1519
1590
|
};
|
|
1520
1591
|
|
|
1521
|
-
// src/proxmox/api-token.ts
|
|
1522
|
-
var ProxmoxApiToken = Resource2("Proxmox.ApiToken", {
|
|
1523
|
-
defaultRemovalPolicy: "retain"
|
|
1524
|
-
});
|
|
1525
|
-
var ops = pveOperations(apiTokenSpec);
|
|
1526
|
-
var handlers = {
|
|
1527
|
-
...pveHandlers(apiTokenSpec),
|
|
1528
|
-
reconcile: Effect10.fn(function* ({ news }) {
|
|
1529
|
-
const live = yield* ops.read(news);
|
|
1530
|
-
if (live === undefined) {
|
|
1531
|
-
return yield* Effect10.die(new Error(`${news.userid}!${news.tokenid}: this resource does not create API tokens. PVE returns ` + "the secret only in the create response and it cannot be stored, so a token made here " + "would be a live credential nobody holds. Create it with `pveum user token add` and " + "capture the value, or mint a short-lived one from the OpenBao proxmox mount " + "(`bao read proxmox-c1/creds/<role>`) -- then declare it here to manage it."));
|
|
1532
|
-
}
|
|
1533
|
-
return yield* ops.reconcile(news);
|
|
1534
|
-
})
|
|
1535
|
-
};
|
|
1536
|
-
var ProxmoxApiTokenProvider = () => Provider2.effect(ProxmoxApiToken, Effect10.succeed(ProxmoxApiToken.Provider.of(handlers)));
|
|
1537
1592
|
// src/proxmox/backup-job.ts
|
|
1538
|
-
import { Resource as Resource3 } from "alchemy";
|
|
1539
|
-
import * as Provider3 from "alchemy/Provider";
|
|
1540
|
-
import * as Effect11 from "effect/Effect";
|
|
1541
1593
|
var ProxmoxBackupJob = Resource3("Proxmox.BackupJob");
|
|
1542
1594
|
var shape2 = (props) => ({
|
|
1543
1595
|
all: props.all === true ? "1" : "0",
|
|
@@ -1555,7 +1607,7 @@ var shape2 = (props) => ({
|
|
|
1555
1607
|
...props.storage === undefined ? {} : { storage: props.storage },
|
|
1556
1608
|
...props.vmid === undefined ? {} : { vmid: guestList(props.vmid) }
|
|
1557
1609
|
});
|
|
1558
|
-
var
|
|
1610
|
+
var handlers = pveHandlers({
|
|
1559
1611
|
attributes: (live, props) => ({
|
|
1560
1612
|
all: bool(live["all"], false),
|
|
1561
1613
|
comment: text(live["comment"], ""),
|
|
@@ -1581,11 +1633,11 @@ var handlers2 = pveHandlers({
|
|
|
1581
1633
|
path: (props) => `cluster/backup/${props.id}`,
|
|
1582
1634
|
updateForm: shape2
|
|
1583
1635
|
});
|
|
1584
|
-
var ProxmoxBackupJobProvider = () => Provider3.effect(ProxmoxBackupJob,
|
|
1636
|
+
var ProxmoxBackupJobProvider = () => Provider3.effect(ProxmoxBackupJob, Effect12.succeed(ProxmoxBackupJob.Provider.of(handlers)));
|
|
1585
1637
|
// src/proxmox/ceph-daemon.ts
|
|
1586
1638
|
import { Resource as Resource4 } from "alchemy";
|
|
1587
1639
|
import * as Provider4 from "alchemy/Provider";
|
|
1588
|
-
import * as
|
|
1640
|
+
import * as Effect13 from "effect/Effect";
|
|
1589
1641
|
|
|
1590
1642
|
// src/proxmox/ceph-daemon-form.ts
|
|
1591
1643
|
var DAEMON_ENDPOINTS = {
|
|
@@ -1628,7 +1680,7 @@ var daemonAttributes = (live, props) => {
|
|
|
1628
1680
|
var ProxmoxCephDaemon = Resource4("Proxmox.CephDaemon", {
|
|
1629
1681
|
defaultRemovalPolicy: "retain"
|
|
1630
1682
|
});
|
|
1631
|
-
var
|
|
1683
|
+
var handlers2 = pveHandlers({
|
|
1632
1684
|
attributes: daemonAttributes,
|
|
1633
1685
|
collection: daemonPath,
|
|
1634
1686
|
createForm,
|
|
@@ -1636,16 +1688,16 @@ var handlers3 = pveHandlers({
|
|
|
1636
1688
|
matches: () => true,
|
|
1637
1689
|
path: collectionPath
|
|
1638
1690
|
});
|
|
1639
|
-
var ProxmoxCephDaemonProvider = () => Provider4.effect(ProxmoxCephDaemon,
|
|
1640
|
-
...
|
|
1691
|
+
var ProxmoxCephDaemonProvider = () => Provider4.effect(ProxmoxCephDaemon, Effect13.succeed(ProxmoxCephDaemon.Provider.of({
|
|
1692
|
+
...handlers2,
|
|
1641
1693
|
delete: ({ olds }) => pve(olds.target, "provision", "DELETE", daemonPath(olds))
|
|
1642
1694
|
})));
|
|
1643
1695
|
// src/proxmox/ceph-flag.ts
|
|
1644
1696
|
import { Resource as Resource5 } from "alchemy";
|
|
1645
1697
|
import * as Provider5 from "alchemy/Provider";
|
|
1646
|
-
import * as
|
|
1698
|
+
import * as Effect14 from "effect/Effect";
|
|
1647
1699
|
var ProxmoxCephFlag = Resource5("Proxmox.CephFlag");
|
|
1648
|
-
var
|
|
1700
|
+
var handlers3 = pveHandlers({
|
|
1649
1701
|
attributes: (live, props) => ({ flag: props.flag, value: bool(live) }),
|
|
1650
1702
|
collection: () => "cluster/ceph/flags",
|
|
1651
1703
|
createForm: (props) => ({ [props.flag]: flag(props.value) ?? "0" }),
|
|
@@ -1654,25 +1706,25 @@ var handlers4 = pveHandlers({
|
|
|
1654
1706
|
path: (props) => `cluster/ceph/flags/${props.flag}`,
|
|
1655
1707
|
updateForm: (props) => ({ value: flag(props.value) ?? "0" })
|
|
1656
1708
|
});
|
|
1657
|
-
var ProxmoxCephFlagProvider = () => Provider5.effect(ProxmoxCephFlag,
|
|
1658
|
-
...
|
|
1659
|
-
delete: () =>
|
|
1660
|
-
reconcile:
|
|
1661
|
-
const after = yield*
|
|
1709
|
+
var ProxmoxCephFlagProvider = () => Provider5.effect(ProxmoxCephFlag, Effect14.succeed(ProxmoxCephFlag.Provider.of({
|
|
1710
|
+
...handlers3,
|
|
1711
|
+
delete: () => Effect14.void,
|
|
1712
|
+
reconcile: Effect14.fn(function* ({ news }) {
|
|
1713
|
+
const after = yield* handlers3.reconcile({ news });
|
|
1662
1714
|
if (after.value !== news.value) {
|
|
1663
|
-
return yield*
|
|
1715
|
+
return yield* Effect14.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."));
|
|
1664
1716
|
}
|
|
1665
1717
|
return after;
|
|
1666
1718
|
})
|
|
1667
1719
|
})));
|
|
1668
1720
|
// src/proxmox/ceph-fs.ts
|
|
1669
1721
|
import { Resource as Resource6 } from "alchemy";
|
|
1670
|
-
import { isResolved as
|
|
1722
|
+
import { isResolved as isResolved4 } from "alchemy/Diff";
|
|
1671
1723
|
import * as Provider6 from "alchemy/Provider";
|
|
1672
|
-
import * as
|
|
1724
|
+
import * as Effect16 from "effect/Effect";
|
|
1673
1725
|
|
|
1674
1726
|
// src/proxmox/ceph-fs-wire.ts
|
|
1675
|
-
import * as
|
|
1727
|
+
import * as Effect15 from "effect/Effect";
|
|
1676
1728
|
var readRow = (live, props) => {
|
|
1677
1729
|
const row = (Array.isArray(live) ? live : []).filter((entry) => typeof entry === "object" && entry !== null).find((entry) => entry["name"] === props.name);
|
|
1678
1730
|
if (row === undefined)
|
|
@@ -1700,29 +1752,29 @@ var destroyPath = (props) => {
|
|
|
1700
1752
|
].filter((part) => part !== undefined);
|
|
1701
1753
|
return query.length === 0 ? objectPath(props) : `${objectPath(props)}?${query.join("&")}`;
|
|
1702
1754
|
};
|
|
1703
|
-
var settle = (target, node, upid, what) =>
|
|
1755
|
+
var settle = (target, node, upid, what) => Effect15.gen(function* () {
|
|
1704
1756
|
for (let attempt = 0;attempt < POLL_ATTEMPTS; attempt += 1) {
|
|
1705
|
-
const status = yield* pve(target, "read", "GET", `nodes/${node}/tasks/${encodeURIComponent(upid)}/status`).pipe(
|
|
1757
|
+
const status = yield* pve(target, "read", "GET", `nodes/${node}/tasks/${encodeURIComponent(upid)}/status`).pipe(Effect15.orElseSucceed(() => {
|
|
1706
1758
|
return;
|
|
1707
1759
|
}));
|
|
1708
1760
|
if (status?.status === "stopped") {
|
|
1709
1761
|
const exit = text(status.exitstatus);
|
|
1710
1762
|
if (exit === "OK")
|
|
1711
1763
|
return;
|
|
1712
|
-
return yield*
|
|
1764
|
+
return yield* Effect15.die(new Error(`${what}: PVE task ${upid} finished with "${exit === "" ? "no exit status" : exit}". ` + "The write returned HTTP 200 because it only forked the worker -- the real error is " + `in \`pvesh get /nodes/${node}/tasks/${upid}/log\`.`));
|
|
1713
1765
|
}
|
|
1714
|
-
yield*
|
|
1766
|
+
yield* Effect15.sleep(POLL_SECONDS * 1000);
|
|
1715
1767
|
}
|
|
1716
|
-
return yield*
|
|
1768
|
+
return yield* Effect15.die(new Error(`${what}: 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/${node} -- each call mints a new token, so the poller is never the ` + "task owner and is answered 403 rather than told to wait."));
|
|
1717
1769
|
});
|
|
1718
|
-
var createFs = (props) =>
|
|
1770
|
+
var createFs = (props) => Effect15.gen(function* () {
|
|
1719
1771
|
const upid = text(yield* pve(props.target, "provision", "POST", objectPath(props), createForm2(props)));
|
|
1720
1772
|
if (upid === "") {
|
|
1721
|
-
return yield*
|
|
1773
|
+
return yield* Effect15.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.'));
|
|
1722
1774
|
}
|
|
1723
1775
|
yield* settle(props.target, props.node, upid, objectPath(props));
|
|
1724
1776
|
});
|
|
1725
|
-
var destroyFs = (props) =>
|
|
1777
|
+
var destroyFs = (props) => Effect15.gen(function* () {
|
|
1726
1778
|
const upid = text(yield* pve(props.target, "provision", "DELETE", destroyPath(props)));
|
|
1727
1779
|
if (upid !== "")
|
|
1728
1780
|
yield* settle(props.target, props.node, upid, destroyPath(props));
|
|
@@ -1734,7 +1786,7 @@ var notDestroyed = (props) => new Error(`${objectPath(props)}: the destroy task
|
|
|
1734
1786
|
var ProxmoxCephFs = Resource6("Proxmox.CephFs", {
|
|
1735
1787
|
defaultRemovalPolicy: "retain"
|
|
1736
1788
|
});
|
|
1737
|
-
var
|
|
1789
|
+
var ops = pveOperations({
|
|
1738
1790
|
attributes: readRow,
|
|
1739
1791
|
collection: objectPath,
|
|
1740
1792
|
createForm: createForm2,
|
|
@@ -1742,44 +1794,44 @@ var ops2 = pveOperations({
|
|
|
1742
1794
|
matches: () => true,
|
|
1743
1795
|
path: (props) => `nodes/${props.node}/ceph/fs`
|
|
1744
1796
|
});
|
|
1745
|
-
var ProxmoxCephFsProvider = () => Provider6.effect(ProxmoxCephFs,
|
|
1746
|
-
list: () =>
|
|
1747
|
-
read:
|
|
1748
|
-
return yield*
|
|
1797
|
+
var ProxmoxCephFsProvider = () => Provider6.effect(ProxmoxCephFs, Effect16.succeed(ProxmoxCephFs.Provider.of({
|
|
1798
|
+
list: () => Effect16.succeed([]),
|
|
1799
|
+
read: Effect16.fn(function* ({ olds }) {
|
|
1800
|
+
return yield* ops.read(olds);
|
|
1749
1801
|
}),
|
|
1750
|
-
diff:
|
|
1751
|
-
if (output !== undefined &&
|
|
1802
|
+
diff: Effect16.fn(function* ({ news, output }) {
|
|
1803
|
+
if (output !== undefined && isResolved4(news) && news.name !== output.name) {
|
|
1752
1804
|
return { action: "replace" };
|
|
1753
1805
|
}
|
|
1754
|
-
return yield*
|
|
1806
|
+
return yield* ops.diff(news, output);
|
|
1755
1807
|
}),
|
|
1756
|
-
reconcile:
|
|
1757
|
-
const live = yield*
|
|
1808
|
+
reconcile: Effect16.fn(function* ({ news }) {
|
|
1809
|
+
const live = yield* ops.read(news);
|
|
1758
1810
|
if (live !== undefined)
|
|
1759
1811
|
return live;
|
|
1760
1812
|
yield* createFs(news);
|
|
1761
|
-
const after = yield*
|
|
1813
|
+
const after = yield* ops.read(news);
|
|
1762
1814
|
if (after === undefined)
|
|
1763
|
-
return yield*
|
|
1815
|
+
return yield* Effect16.die(notCreated(news));
|
|
1764
1816
|
return after;
|
|
1765
1817
|
}),
|
|
1766
|
-
delete:
|
|
1767
|
-
if ((yield*
|
|
1818
|
+
delete: Effect16.fn(function* ({ olds }) {
|
|
1819
|
+
if ((yield* ops.read(olds)) === undefined)
|
|
1768
1820
|
return;
|
|
1769
1821
|
yield* destroyFs(olds);
|
|
1770
|
-
const after = yield*
|
|
1822
|
+
const after = yield* ops.read(olds);
|
|
1771
1823
|
if (after !== undefined)
|
|
1772
|
-
return yield*
|
|
1824
|
+
return yield* Effect16.die(notDestroyed(olds));
|
|
1773
1825
|
})
|
|
1774
1826
|
})));
|
|
1775
1827
|
// src/proxmox/ceph-osd.ts
|
|
1776
1828
|
import { Resource as Resource7 } from "alchemy";
|
|
1777
|
-
import { isResolved as
|
|
1829
|
+
import { isResolved as isResolved5 } from "alchemy/Diff";
|
|
1778
1830
|
import * as Provider7 from "alchemy/Provider";
|
|
1779
|
-
import * as
|
|
1831
|
+
import * as Effect19 from "effect/Effect";
|
|
1780
1832
|
|
|
1781
1833
|
// src/proxmox/ceph-osd-tree.ts
|
|
1782
|
-
import * as
|
|
1834
|
+
import * as Effect17 from "effect/Effect";
|
|
1783
1835
|
var branches = (node) => (Array.isArray(node["children"]) ? node["children"] : []).filter((child) => typeof child === "object" && child !== null);
|
|
1784
1836
|
var osdLeaves = (tree) => {
|
|
1785
1837
|
const root = tree["root"];
|
|
@@ -1805,29 +1857,29 @@ var osdAttributes = (leaf, props) => ({
|
|
|
1805
1857
|
status: text(leaf["status"]),
|
|
1806
1858
|
total_space: num(leaf["total_space"], 0)
|
|
1807
1859
|
});
|
|
1808
|
-
var readOsd = (props) => pve(props.target, "read", "GET", `nodes/${props.node}/ceph/osd`).pipe(
|
|
1860
|
+
var readOsd = (props) => pve(props.target, "read", "GET", `nodes/${props.node}/ceph/osd`).pipe(Effect17.map((t) => {
|
|
1809
1861
|
const leaf = t === undefined ? undefined : findOsd(t, props.osdid);
|
|
1810
1862
|
return leaf === undefined ? undefined : osdAttributes(leaf, props);
|
|
1811
1863
|
}));
|
|
1812
1864
|
|
|
1813
1865
|
// src/proxmox/ceph-osd-write.ts
|
|
1814
|
-
import * as
|
|
1866
|
+
import * as Effect18 from "effect/Effect";
|
|
1815
1867
|
var OSD_CREATE_ENDPOINT = "pve:POST /nodes/{node}/ceph/osd";
|
|
1816
1868
|
var describe = (props) => `osd.${String(props.osdid)} on ${props.node}`;
|
|
1817
1869
|
var createOsdForm = (props, dev) => ({
|
|
1818
1870
|
dev,
|
|
1819
1871
|
...props.device_class === undefined ? {} : { "crush-device-class": props.device_class }
|
|
1820
1872
|
});
|
|
1821
|
-
var createOsd =
|
|
1873
|
+
var createOsd = Effect18.fn(function* (news) {
|
|
1822
1874
|
if (news.dev === undefined) {
|
|
1823
|
-
return yield*
|
|
1875
|
+
return yield* Effect18.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."));
|
|
1824
1876
|
}
|
|
1825
1877
|
const form = createOsdForm(news, news.dev);
|
|
1826
1878
|
yield* guardForm(OSD_CREATE_ENDPOINT, form, true);
|
|
1827
1879
|
yield* pve(news.target, "provision", "POST", `nodes/${news.node}/ceph/osd`, form);
|
|
1828
1880
|
const created = yield* readOsd(news);
|
|
1829
1881
|
if (created === undefined) {
|
|
1830
|
-
return yield*
|
|
1882
|
+
return yield* Effect18.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."));
|
|
1831
1883
|
}
|
|
1832
1884
|
return created;
|
|
1833
1885
|
});
|
|
@@ -1839,16 +1891,16 @@ var ProxmoxCephOsd = Resource7("Proxmox.CephOsd", {
|
|
|
1839
1891
|
});
|
|
1840
1892
|
var settled = (live, props) => (props.host === undefined || props.host === live.host) && (props.device_class === undefined || props.device_class === live.device_class);
|
|
1841
1893
|
var describe2 = (props) => `osd.${String(props.osdid)} (read via nodes/${props.node}/ceph/osd)`;
|
|
1842
|
-
var
|
|
1843
|
-
list: () =>
|
|
1844
|
-
read:
|
|
1894
|
+
var handlers4 = {
|
|
1895
|
+
list: () => Effect19.succeed([]),
|
|
1896
|
+
read: Effect19.fn(function* ({ olds }) {
|
|
1845
1897
|
return yield* readOsd(olds);
|
|
1846
1898
|
}),
|
|
1847
|
-
diff:
|
|
1899
|
+
diff: Effect19.fn(function* ({
|
|
1848
1900
|
news,
|
|
1849
1901
|
output
|
|
1850
1902
|
}) {
|
|
1851
|
-
if (output === undefined || !
|
|
1903
|
+
if (output === undefined || !isResolved5(news))
|
|
1852
1904
|
return;
|
|
1853
1905
|
const live = yield* readOsd(news);
|
|
1854
1906
|
if (live === undefined)
|
|
@@ -1857,25 +1909,25 @@ var handlers5 = {
|
|
|
1857
1909
|
return { action: "update" };
|
|
1858
1910
|
return settled(live, news) ? { action: "noop" } : { action: "update" };
|
|
1859
1911
|
}),
|
|
1860
|
-
reconcile:
|
|
1912
|
+
reconcile: Effect19.fn(function* ({ news }) {
|
|
1861
1913
|
const live = yield* readOsd(news);
|
|
1862
1914
|
if (live === undefined) {
|
|
1863
1915
|
return yield* createOsd(news);
|
|
1864
1916
|
}
|
|
1865
1917
|
if (!settled(live, news)) {
|
|
1866
|
-
return yield*
|
|
1918
|
+
return yield* Effect19.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."));
|
|
1867
1919
|
}
|
|
1868
1920
|
return live;
|
|
1869
1921
|
}),
|
|
1870
|
-
delete:
|
|
1922
|
+
delete: Effect19.fn(function* ({ olds }) {
|
|
1871
1923
|
yield* destroyOsd(olds);
|
|
1872
1924
|
})
|
|
1873
1925
|
};
|
|
1874
|
-
var ProxmoxCephOsdProvider = () => Provider7.effect(ProxmoxCephOsd,
|
|
1926
|
+
var ProxmoxCephOsdProvider = () => Provider7.effect(ProxmoxCephOsd, Effect19.succeed(ProxmoxCephOsd.Provider.of(handlers4)));
|
|
1875
1927
|
// src/proxmox/ceph-pool.ts
|
|
1876
1928
|
import { Resource as Resource8 } from "alchemy";
|
|
1877
1929
|
import * as Provider8 from "alchemy/Provider";
|
|
1878
|
-
import * as
|
|
1930
|
+
import * as Effect21 from "effect/Effect";
|
|
1879
1931
|
|
|
1880
1932
|
// src/proxmox/ceph-pool-form.ts
|
|
1881
1933
|
var object = (props) => `nodes/${props.node}/ceph/pool/${props.name}`;
|
|
@@ -1904,14 +1956,14 @@ var createBody = (props) => ({
|
|
|
1904
1956
|
});
|
|
1905
1957
|
|
|
1906
1958
|
// src/proxmox/ceph-pool-settle.ts
|
|
1907
|
-
import * as
|
|
1908
|
-
var confirmAbsent = (props, collection2, describe3) => pve(props.target, "read", "GET", collection2).pipe(
|
|
1909
|
-
var settle2 = (props, read, done) =>
|
|
1959
|
+
import * as Effect20 from "effect/Effect";
|
|
1960
|
+
var confirmAbsent = (props, collection2, describe3) => pve(props.target, "read", "GET", collection2).pipe(Effect20.flatMap((rows) => (rows ?? []).some((row) => row.pool_name === props.name) ? Effect20.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.")) : Effect20.void));
|
|
1961
|
+
var settle2 = (props, read, done) => Effect20.gen(function* () {
|
|
1910
1962
|
for (let attempt = 0;attempt < 30; attempt += 1) {
|
|
1911
1963
|
const live = yield* read(props);
|
|
1912
1964
|
if (done(live))
|
|
1913
1965
|
return live;
|
|
1914
|
-
yield*
|
|
1966
|
+
yield* Effect20.sleep("2 seconds");
|
|
1915
1967
|
}
|
|
1916
1968
|
return yield* read(props);
|
|
1917
1969
|
});
|
|
@@ -1952,44 +2004,44 @@ var spec = {
|
|
|
1952
2004
|
path: (props) => `${object(props)}/status?verbose=1`,
|
|
1953
2005
|
updateForm: updateBody
|
|
1954
2006
|
};
|
|
1955
|
-
var
|
|
1956
|
-
var ProxmoxCephPoolProvider = () => Provider8.effect(ProxmoxCephPool,
|
|
1957
|
-
list: () =>
|
|
1958
|
-
read: ({ olds }) =>
|
|
1959
|
-
diff: ({ news, output }) =>
|
|
1960
|
-
reconcile:
|
|
1961
|
-
const live = yield*
|
|
2007
|
+
var ops2 = pveOperations(spec);
|
|
2008
|
+
var ProxmoxCephPoolProvider = () => Provider8.effect(ProxmoxCephPool, Effect21.succeed(ProxmoxCephPool.Provider.of({
|
|
2009
|
+
list: () => Effect21.succeed([]),
|
|
2010
|
+
read: ({ olds }) => ops2.read(olds),
|
|
2011
|
+
diff: ({ news, output }) => ops2.diff(news, output),
|
|
2012
|
+
reconcile: Effect21.fn(function* ({ news }) {
|
|
2013
|
+
const live = yield* ops2.read(news);
|
|
1962
2014
|
let upid;
|
|
1963
2015
|
if (live === undefined) {
|
|
1964
|
-
yield*
|
|
2016
|
+
yield* ops2.guardCreate(news, true);
|
|
1965
2017
|
yield* confirmAbsent(news, spec.collection(news), object(news));
|
|
1966
2018
|
upid = yield* pve(news.target, "provision", "POST", collection(news), createBody(news));
|
|
1967
2019
|
} else {
|
|
1968
2020
|
const form = formToSend(spec.matches, live, news, updateBody(news));
|
|
1969
2021
|
if (form !== undefined) {
|
|
1970
|
-
yield*
|
|
2022
|
+
yield* ops2.guardUpdate(news);
|
|
1971
2023
|
upid = yield* pve(news.target, "provision", "PUT", object(news), form);
|
|
1972
2024
|
}
|
|
1973
2025
|
}
|
|
1974
|
-
const after = yield* settle2(news,
|
|
2026
|
+
const after = yield* settle2(news, ops2.read, (row) => row !== undefined && spec.matches(row, news));
|
|
1975
2027
|
if (after === undefined || !spec.matches(after, news)) {
|
|
1976
|
-
return yield*
|
|
2028
|
+
return yield* Effect21.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."));
|
|
1977
2029
|
}
|
|
1978
2030
|
return after;
|
|
1979
2031
|
}),
|
|
1980
|
-
delete:
|
|
2032
|
+
delete: Effect21.fn(function* ({ olds }) {
|
|
1981
2033
|
yield* pve(olds.target, "provision", "DELETE", object(olds));
|
|
1982
|
-
const left = yield* settle2(olds,
|
|
2034
|
+
const left = yield* settle2(olds, ops2.read, (row) => row === undefined);
|
|
1983
2035
|
if (left !== undefined) {
|
|
1984
|
-
return yield*
|
|
2036
|
+
return yield* Effect21.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."));
|
|
1985
2037
|
}
|
|
1986
2038
|
})
|
|
1987
2039
|
})));
|
|
1988
2040
|
// src/proxmox/firewall-alias.ts
|
|
1989
2041
|
import { Resource as Resource9 } from "alchemy";
|
|
1990
|
-
import { isResolved as
|
|
2042
|
+
import { isResolved as isResolved6 } from "alchemy/Diff";
|
|
1991
2043
|
import * as Provider9 from "alchemy/Provider";
|
|
1992
|
-
import * as
|
|
2044
|
+
import * as Effect22 from "effect/Effect";
|
|
1993
2045
|
|
|
1994
2046
|
// src/proxmox/firewall-alias-form.ts
|
|
1995
2047
|
var fold = (name) => name.trim().toLowerCase();
|
|
@@ -2009,7 +2061,7 @@ var body = (props) => {
|
|
|
2009
2061
|
|
|
2010
2062
|
// src/proxmox/firewall-alias.ts
|
|
2011
2063
|
var ProxmoxFirewallAlias = Resource9("Proxmox.FirewallAlias");
|
|
2012
|
-
var
|
|
2064
|
+
var handlers5 = pveHandlers({
|
|
2013
2065
|
attributes: (live, props) => {
|
|
2014
2066
|
const address = cidr(live["cidr"]);
|
|
2015
2067
|
return address === "" ? undefined : {
|
|
@@ -2029,48 +2081,102 @@ var handlers6 = pveHandlers({
|
|
|
2029
2081
|
path: (props) => `cluster/firewall/aliases/${props.name}`,
|
|
2030
2082
|
updateForm: body
|
|
2031
2083
|
});
|
|
2032
|
-
var ProxmoxFirewallAliasProvider = () => Provider9.effect(ProxmoxFirewallAlias,
|
|
2033
|
-
...
|
|
2034
|
-
diff:
|
|
2035
|
-
if (output !== undefined &&
|
|
2084
|
+
var ProxmoxFirewallAliasProvider = () => Provider9.effect(ProxmoxFirewallAlias, Effect22.succeed(ProxmoxFirewallAlias.Provider.of({
|
|
2085
|
+
...handlers5,
|
|
2086
|
+
diff: Effect22.fn(function* ({ news, output }) {
|
|
2087
|
+
if (output !== undefined && isResolved6(news) && fold(news.name) !== fold(output.name)) {
|
|
2036
2088
|
return { action: "replace" };
|
|
2037
2089
|
}
|
|
2038
|
-
return yield*
|
|
2090
|
+
return yield* handlers5.diff({ news, output });
|
|
2039
2091
|
})
|
|
2040
2092
|
})));
|
|
2041
2093
|
// src/proxmox/group.ts
|
|
2042
2094
|
import { Resource as Resource10 } from "alchemy";
|
|
2095
|
+
import { isResolved as isResolved7 } from "alchemy/Diff";
|
|
2043
2096
|
import * as Provider10 from "alchemy/Provider";
|
|
2044
|
-
import * as
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2097
|
+
import * as access3 from "@distilled.cloud/proxmox/access";
|
|
2098
|
+
import * as Effect23 from "effect/Effect";
|
|
2099
|
+
|
|
2100
|
+
// src/proxmox/group-wire.ts
|
|
2101
|
+
var GROUP_CREATE = "pve:POST /access/groups";
|
|
2102
|
+
var GROUP_UPDATE = "pve:PUT /access/groups/{groupid}";
|
|
2048
2103
|
var storedComment = (comment2) => {
|
|
2049
2104
|
const value = comment2 ?? "";
|
|
2050
2105
|
return value === "0" ? "" : value;
|
|
2051
2106
|
};
|
|
2052
2107
|
var memberList = (live) => {
|
|
2053
|
-
const joined = csv(
|
|
2108
|
+
const joined = csv(live.map((member) => text(member)));
|
|
2054
2109
|
return joined === "" ? [] : joined.split(",");
|
|
2055
2110
|
};
|
|
2056
|
-
var
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2111
|
+
var createForm3 = (props) => ({
|
|
2112
|
+
comment: storedComment(props.comment),
|
|
2113
|
+
groupid: props.groupid
|
|
2114
|
+
});
|
|
2115
|
+
var updateForm = (props) => ({
|
|
2116
|
+
comment: storedComment(props.comment),
|
|
2117
|
+
groupid: props.groupid
|
|
2118
|
+
});
|
|
2119
|
+
var attributesOf3 = (live, props) => ({
|
|
2120
|
+
comment: text(live.comment),
|
|
2121
|
+
groupid: props.groupid,
|
|
2122
|
+
members: memberList(live.members)
|
|
2123
|
+
});
|
|
2124
|
+
var matches3 = (attributes, props) => attributes.comment === storedComment(props.comment);
|
|
2125
|
+
|
|
2126
|
+
// src/proxmox/group.ts
|
|
2127
|
+
var ProxmoxGroup = Resource10("Proxmox.Group", {
|
|
2128
|
+
defaultRemovalPolicy: "retain"
|
|
2129
|
+
});
|
|
2130
|
+
var readGroup = (props) => readOrUnreadable(runPve(props.target, "read", false, access3.getAccessGroup(props))).pipe(Effect23.map((live) => live === UNREADABLE ? UNREADABLE : live.members === undefined ? undefined : attributesOf3(live, props)), Effect23.orElseSucceed(() => {
|
|
2131
|
+
return;
|
|
2132
|
+
}));
|
|
2133
|
+
var dropUnreadable3 = (live) => live === UNREADABLE ? undefined : live;
|
|
2134
|
+
var ProxmoxGroupProvider = () => Provider10.effect(ProxmoxGroup, Effect23.succeed(ProxmoxGroup.Provider.of({
|
|
2135
|
+
list: () => Effect23.succeed([]),
|
|
2136
|
+
read: Effect23.fn(function* ({ olds }) {
|
|
2137
|
+
return dropUnreadable3(yield* readGroup(olds));
|
|
2061
2138
|
}),
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2139
|
+
diff: Effect23.fn(function* ({ news, output }) {
|
|
2140
|
+
if (!isResolved7(news))
|
|
2141
|
+
return;
|
|
2142
|
+
yield* guardWrite2(GROUP_CREATE, createForm3(news), output === undefined);
|
|
2143
|
+
yield* guardWrite2(GROUP_UPDATE, updateForm(news), false);
|
|
2144
|
+
if (output === undefined)
|
|
2145
|
+
return;
|
|
2146
|
+
const live = yield* readGroup(news);
|
|
2147
|
+
if (live === UNREADABLE) {
|
|
2148
|
+
yield* unreadableWarning("Proxmox.Group", news.groupid);
|
|
2149
|
+
return { action: "noop" };
|
|
2150
|
+
}
|
|
2151
|
+
if (live === undefined) {
|
|
2152
|
+
yield* guardWrite2(GROUP_CREATE, createForm3(news), true);
|
|
2153
|
+
return { action: "update" };
|
|
2154
|
+
}
|
|
2155
|
+
return matches3(live, news) ? { action: "noop" } : { action: "update" };
|
|
2156
|
+
}),
|
|
2157
|
+
reconcile: Effect23.fn(function* ({ news }) {
|
|
2158
|
+
const before = dropUnreadable3(yield* readGroup(news));
|
|
2159
|
+
yield* guardWrite2(GROUP_CREATE, createForm3(news), before === undefined);
|
|
2160
|
+
yield* guardWrite2(GROUP_UPDATE, updateForm(news), false);
|
|
2161
|
+
if (before === undefined) {
|
|
2162
|
+
yield* runPve(news.target, "provision", true, access3.createAccessGroup(createForm3(news)));
|
|
2163
|
+
} else if (!matches3(before, news)) {
|
|
2164
|
+
yield* runPve(news.target, "provision", true, access3.putAccessGroup(updateForm(news)));
|
|
2165
|
+
}
|
|
2166
|
+
const after = dropUnreadable3(yield* readGroup(news));
|
|
2167
|
+
if (after === undefined) {
|
|
2168
|
+
return yield* Effect23.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."));
|
|
2169
|
+
}
|
|
2170
|
+
return after;
|
|
2171
|
+
}),
|
|
2172
|
+
delete: Effect23.fn(function* ({ olds }) {
|
|
2173
|
+
yield* runPve(olds.target, "provision", true, access3.deleteAccessGroup({ groupid: olds.groupid }));
|
|
2174
|
+
})
|
|
2175
|
+
})));
|
|
2070
2176
|
// src/proxmox/ha-resource.ts
|
|
2071
2177
|
import { Resource as Resource11 } from "alchemy";
|
|
2072
2178
|
import * as Provider11 from "alchemy/Provider";
|
|
2073
|
-
import * as
|
|
2179
|
+
import * as Effect24 from "effect/Effect";
|
|
2074
2180
|
var ProxmoxHaResource = Resource11("Proxmox.HaResource");
|
|
2075
2181
|
var kind = (sid) => sid.split(":")[0] ?? "";
|
|
2076
2182
|
var shape3 = (props) => ({
|
|
@@ -2079,7 +2185,7 @@ var shape3 = (props) => ({
|
|
|
2079
2185
|
max_restart: String(props.max_restart ?? 1),
|
|
2080
2186
|
state: props.state ?? "started"
|
|
2081
2187
|
});
|
|
2082
|
-
var
|
|
2188
|
+
var handlers6 = pveHandlers({
|
|
2083
2189
|
attributes: (live, props) => ({
|
|
2084
2190
|
comment: typeof live["comment"] === "string" ? live["comment"] : "",
|
|
2085
2191
|
max_relocate: num(live["max_relocate"], 1),
|
|
@@ -2098,11 +2204,11 @@ var handlers8 = pveHandlers({
|
|
|
2098
2204
|
path: (props) => `cluster/ha/resources/${props.sid}`,
|
|
2099
2205
|
updateForm: shape3
|
|
2100
2206
|
});
|
|
2101
|
-
var ProxmoxHaResourceProvider = () => Provider11.effect(ProxmoxHaResource,
|
|
2207
|
+
var ProxmoxHaResourceProvider = () => Provider11.effect(ProxmoxHaResource, Effect24.succeed(ProxmoxHaResource.Provider.of(handlers6)));
|
|
2102
2208
|
// src/proxmox/ha-rule.ts
|
|
2103
2209
|
import { Resource as Resource12 } from "alchemy";
|
|
2104
2210
|
import * as Provider12 from "alchemy/Provider";
|
|
2105
|
-
import * as
|
|
2211
|
+
import * as Effect25 from "effect/Effect";
|
|
2106
2212
|
|
|
2107
2213
|
// src/proxmox/ha-rule-form.ts
|
|
2108
2214
|
var nodeList = (value) => {
|
|
@@ -2150,7 +2256,7 @@ var clearList = (props) => Object.entries(optional(props)).filter(([, value]) =>
|
|
|
2150
2256
|
|
|
2151
2257
|
// src/proxmox/ha-rule.ts
|
|
2152
2258
|
var ProxmoxHaRule = Resource12("Proxmox.HaRule");
|
|
2153
|
-
var
|
|
2259
|
+
var handlers7 = pveHandlers({
|
|
2154
2260
|
attributes: (live, props) => {
|
|
2155
2261
|
const liveType = text(live["type"]);
|
|
2156
2262
|
if (liveType !== "" && liveType !== props.type)
|
|
@@ -2179,43 +2285,43 @@ var handlers9 = pveHandlers({
|
|
|
2179
2285
|
return withClears(fields, clear);
|
|
2180
2286
|
}
|
|
2181
2287
|
});
|
|
2182
|
-
var ProxmoxHaRuleProvider = () => Provider12.effect(ProxmoxHaRule,
|
|
2288
|
+
var ProxmoxHaRuleProvider = () => Provider12.effect(ProxmoxHaRule, Effect25.succeed(ProxmoxHaRule.Provider.of(handlers7)));
|
|
2183
2289
|
// src/proxmox/lxc.ts
|
|
2184
2290
|
import { Resource as Resource13 } from "alchemy";
|
|
2185
|
-
import { isResolved as
|
|
2291
|
+
import { isResolved as isResolved8 } from "alchemy/Diff";
|
|
2186
2292
|
import * as Provider13 from "alchemy/Provider";
|
|
2187
|
-
import * as
|
|
2293
|
+
import * as Effect35 from "effect/Effect";
|
|
2188
2294
|
|
|
2189
2295
|
// src/ownership/adopt.ts
|
|
2190
2296
|
import { AdoptPolicy } from "alchemy/AdoptPolicy";
|
|
2191
2297
|
import { AlchemyContext } from "alchemy/AlchemyContext";
|
|
2192
2298
|
import { Stack as Stack3 } from "alchemy/Stack";
|
|
2193
|
-
import * as
|
|
2299
|
+
import * as Effect29 from "effect/Effect";
|
|
2194
2300
|
import * as Option5 from "effect/Option";
|
|
2195
2301
|
|
|
2196
2302
|
// src/ownership/resume.ts
|
|
2197
2303
|
import { Unowned } from "alchemy/AdoptPolicy";
|
|
2198
2304
|
import { Artifacts } from "alchemy/Artifacts";
|
|
2199
|
-
import * as
|
|
2305
|
+
import * as Effect26 from "effect/Effect";
|
|
2200
2306
|
import * as Option2 from "effect/Option";
|
|
2201
2307
|
var PROVEN = "homeflare/ownership/resumes";
|
|
2202
2308
|
var UNFINISHED = "homeflare/ownership/unfinished";
|
|
2203
|
-
var note = (key, instanceId) =>
|
|
2204
|
-
const bag = yield*
|
|
2309
|
+
var note = (key, instanceId) => Effect26.gen(function* () {
|
|
2310
|
+
const bag = yield* Effect26.serviceOption(Artifacts);
|
|
2205
2311
|
if (Option2.isSome(bag))
|
|
2206
2312
|
yield* bag.value.set(key, instanceId);
|
|
2207
2313
|
});
|
|
2208
|
-
var noted = (key, instanceId) =>
|
|
2209
|
-
const bag = yield*
|
|
2314
|
+
var noted = (key, instanceId) => Effect26.gen(function* () {
|
|
2315
|
+
const bag = yield* Effect26.serviceOption(Artifacts);
|
|
2210
2316
|
if (Option2.isNone(bag))
|
|
2211
2317
|
return false;
|
|
2212
2318
|
return (yield* bag.value.get(key)) === instanceId;
|
|
2213
2319
|
});
|
|
2214
|
-
var noteUnfinished = (instanceId) =>
|
|
2215
|
-
var noteResume = (instanceId, recovered) =>
|
|
2320
|
+
var noteUnfinished = (instanceId) => Effect26.as(note(UNFINISHED, instanceId), undefined);
|
|
2321
|
+
var noteResume = (instanceId, recovered) => Effect26.gen(function* () {
|
|
2216
2322
|
yield* noteUnfinished(instanceId);
|
|
2217
|
-
const unproven = () =>
|
|
2218
|
-
const found = yield* recovered.pipe(
|
|
2323
|
+
const unproven = () => Effect26.succeed(undefined);
|
|
2324
|
+
const found = yield* recovered.pipe(Effect26.catch(unproven), Effect26.catchDefect(unproven));
|
|
2219
2325
|
if (found === undefined || found === null || Unowned.is(found))
|
|
2220
2326
|
return;
|
|
2221
2327
|
yield* note(PROVEN, instanceId);
|
|
@@ -2226,7 +2332,7 @@ var provingResumes = (service) => {
|
|
|
2226
2332
|
const { diff, read } = service;
|
|
2227
2333
|
if (diff === undefined || read === undefined)
|
|
2228
2334
|
return service;
|
|
2229
|
-
const proving = (input) =>
|
|
2335
|
+
const proving = (input) => Effect26.gen(function* () {
|
|
2230
2336
|
if (input.output === undefined) {
|
|
2231
2337
|
yield* noteResume(input.instanceId, read({ ...input, output: undefined }));
|
|
2232
2338
|
}
|
|
@@ -2238,12 +2344,12 @@ var provingResumes = (service) => {
|
|
|
2238
2344
|
// src/ownership/rows.ts
|
|
2239
2345
|
import { Stack as Stack2 } from "alchemy/Stack";
|
|
2240
2346
|
import { State, isActionState } from "alchemy/State/State";
|
|
2241
|
-
import * as
|
|
2347
|
+
import * as Effect28 from "effect/Effect";
|
|
2242
2348
|
import * as Option4 from "effect/Option";
|
|
2243
2349
|
|
|
2244
2350
|
// src/ownership/whole.ts
|
|
2245
2351
|
import { Stack } from "alchemy/Stack";
|
|
2246
|
-
import * as
|
|
2352
|
+
import * as Effect27 from "effect/Effect";
|
|
2247
2353
|
import * as Option3 from "effect/Option";
|
|
2248
2354
|
var absent = (value) => value === undefined || value === null;
|
|
2249
2355
|
var isPlainObject = (value) => {
|
|
@@ -2269,22 +2375,22 @@ var carries = (declared, recorded) => {
|
|
|
2269
2375
|
}
|
|
2270
2376
|
return true;
|
|
2271
2377
|
};
|
|
2272
|
-
var wholeDeclaration = (fqn, props) =>
|
|
2378
|
+
var wholeDeclaration = (fqn, props) => Effect27.map(Effect27.serviceOption(Stack), (stack) => {
|
|
2273
2379
|
const declared = Option3.isSome(stack) ? stack.value.resources[fqn] : undefined;
|
|
2274
2380
|
return declared !== undefined && carries(declared.Props, props);
|
|
2275
2381
|
});
|
|
2276
2382
|
|
|
2277
2383
|
// src/ownership/rows.ts
|
|
2278
|
-
var storeOf =
|
|
2279
|
-
const stack = yield*
|
|
2280
|
-
const state = yield*
|
|
2384
|
+
var storeOf = Effect28.gen(function* () {
|
|
2385
|
+
const stack = yield* Effect28.serviceOption(Stack2);
|
|
2386
|
+
const state = yield* Effect28.serviceOption(State);
|
|
2281
2387
|
if (Option4.isNone(stack) || Option4.isNone(state))
|
|
2282
2388
|
return;
|
|
2283
2389
|
const store = yield* state.value;
|
|
2284
2390
|
const where = { stack: stack.value.name, stage: stack.value.stage };
|
|
2285
2391
|
return { resources: stack.value.resources, store, where };
|
|
2286
2392
|
});
|
|
2287
|
-
var rowAt = (store, where, fqn) => store.get({ ...where, fqn }).pipe(
|
|
2393
|
+
var rowAt = (store, where, fqn) => store.get({ ...where, fqn }).pipe(Effect28.orElseSucceed(() => {
|
|
2288
2394
|
return;
|
|
2289
2395
|
}));
|
|
2290
2396
|
var generationOf = (row, instanceId) => {
|
|
@@ -2297,7 +2403,7 @@ var generationOf = (row, instanceId) => {
|
|
|
2297
2403
|
}
|
|
2298
2404
|
return;
|
|
2299
2405
|
};
|
|
2300
|
-
var recordedGeneration = (fqn, instanceId) =>
|
|
2406
|
+
var recordedGeneration = (fqn, instanceId) => Effect28.gen(function* () {
|
|
2301
2407
|
const found = yield* storeOf;
|
|
2302
2408
|
if (found === undefined)
|
|
2303
2409
|
return;
|
|
@@ -2308,14 +2414,14 @@ var recordedGeneration = (fqn, instanceId) => Effect27.gen(function* () {
|
|
|
2308
2414
|
}
|
|
2309
2415
|
return;
|
|
2310
2416
|
});
|
|
2311
|
-
var recordedInstance = (fqn, instanceId) =>
|
|
2417
|
+
var recordedInstance = (fqn, instanceId) => Effect28.gen(function* () {
|
|
2312
2418
|
const generation = yield* recordedGeneration(fqn, instanceId);
|
|
2313
2419
|
if (generation === undefined)
|
|
2314
2420
|
return "absent";
|
|
2315
2421
|
return (yield* wholeDeclaration(fqn, generation.props)) ? "whole" : "partial";
|
|
2316
2422
|
});
|
|
2317
|
-
var isCreate = (fqn, instanceId) =>
|
|
2318
|
-
var forgetRefusedCreate = (fqn, instanceId) =>
|
|
2423
|
+
var isCreate = (fqn, instanceId) => Effect28.map(recordedGeneration(fqn, instanceId), (generation) => generation?.status === "creating");
|
|
2424
|
+
var forgetRefusedCreate = (fqn, instanceId) => Effect28.gen(function* () {
|
|
2319
2425
|
const found = yield* storeOf;
|
|
2320
2426
|
if (found === undefined)
|
|
2321
2427
|
return;
|
|
@@ -2324,32 +2430,32 @@ var forgetRefusedCreate = (fqn, instanceId) => Effect27.gen(function* () {
|
|
|
2324
2430
|
if (mine?.status !== "creating" || mine.instanceId !== instanceId || mine.attr !== undefined) {
|
|
2325
2431
|
return;
|
|
2326
2432
|
}
|
|
2327
|
-
yield* found.store.delete({ ...found.where, fqn }).pipe(
|
|
2433
|
+
yield* found.store.delete({ ...found.where, fqn }).pipe(Effect28.ignore);
|
|
2328
2434
|
});
|
|
2329
2435
|
|
|
2330
2436
|
// src/ownership/adopt.ts
|
|
2331
|
-
var adoptEnabled = (fqn) =>
|
|
2332
|
-
const stack = yield*
|
|
2437
|
+
var adoptEnabled = (fqn) => Effect29.gen(function* () {
|
|
2438
|
+
const stack = yield* Effect29.serviceOption(Stack3);
|
|
2333
2439
|
const scoped = Option5.isSome(stack) ? stack.value.resources[fqn]?.Adopt : undefined;
|
|
2334
2440
|
if (scoped !== undefined)
|
|
2335
2441
|
return scoped;
|
|
2336
|
-
const policy = yield*
|
|
2442
|
+
const policy = yield* Effect29.serviceOption(AdoptPolicy);
|
|
2337
2443
|
if (Option5.isSome(policy))
|
|
2338
2444
|
return policy.value;
|
|
2339
|
-
const context = yield*
|
|
2445
|
+
const context = yield* Effect29.serviceOption(AlchemyContext);
|
|
2340
2446
|
return Option5.isSome(context) ? context.value.adopt : false;
|
|
2341
2447
|
});
|
|
2342
|
-
var adoptable = (owner) =>
|
|
2448
|
+
var adoptable = (owner) => Effect29.gen(function* () {
|
|
2343
2449
|
if (yield* isCreate(owner.fqn, owner.instanceId))
|
|
2344
2450
|
return true;
|
|
2345
2451
|
return yield* unfinished(owner.instanceId);
|
|
2346
2452
|
});
|
|
2347
|
-
var adoptsAtApply = (owner) =>
|
|
2453
|
+
var adoptsAtApply = (owner) => Effect29.gen(function* () {
|
|
2348
2454
|
if (!(yield* adoptEnabled(owner.fqn)))
|
|
2349
2455
|
return false;
|
|
2350
2456
|
return yield* adoptable(owner);
|
|
2351
2457
|
});
|
|
2352
|
-
var refuseTakeover = (owner, what) =>
|
|
2458
|
+
var refuseTakeover = (owner, what) => Effect29.gen(function* () {
|
|
2353
2459
|
if (owner.output !== undefined)
|
|
2354
2460
|
return;
|
|
2355
2461
|
if (yield* resumes(owner.instanceId))
|
|
@@ -2359,25 +2465,25 @@ var refuseTakeover = (owner, what) => Effect28.gen(function* () {
|
|
|
2359
2465
|
return;
|
|
2360
2466
|
yield* forgetRefusedCreate(owner.fqn, owner.instanceId);
|
|
2361
2467
|
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.";
|
|
2362
|
-
return yield*
|
|
2468
|
+
return yield* Effect29.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}`));
|
|
2363
2469
|
});
|
|
2364
2470
|
var claimFor = (owner) => (what) => refuseTakeover(owner, what);
|
|
2365
2471
|
|
|
2366
2472
|
// src/ownership/probe.ts
|
|
2367
2473
|
import { Unowned as Unowned2 } from "alchemy/AdoptPolicy";
|
|
2368
|
-
import * as
|
|
2369
|
-
var provenOurs = (ask, settled2) =>
|
|
2474
|
+
import * as Effect30 from "effect/Effect";
|
|
2475
|
+
var provenOurs = (ask, settled2) => Effect30.gen(function* () {
|
|
2370
2476
|
const recorded = yield* recordedInstance(ask.fqn, ask.instanceId);
|
|
2371
2477
|
if (recorded === "absent")
|
|
2372
2478
|
return false;
|
|
2373
2479
|
if (recorded === "partial") {
|
|
2374
|
-
yield*
|
|
2480
|
+
yield* Effect30.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");
|
|
2375
2481
|
return false;
|
|
2376
2482
|
}
|
|
2377
|
-
const unproven = (reason) =>
|
|
2378
|
-
return yield* settled2.pipe(
|
|
2483
|
+
const unproven = (reason) => Effect30.as(Effect30.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);
|
|
2484
|
+
return yield* settled2.pipe(Effect30.catch(unproven), Effect30.catchDefect(unproven));
|
|
2379
2485
|
});
|
|
2380
|
-
var ownedRead = (ask, found, settled2) =>
|
|
2486
|
+
var ownedRead = (ask, found, settled2) => Effect30.gen(function* () {
|
|
2381
2487
|
if (found === undefined || ask.output !== undefined)
|
|
2382
2488
|
return found;
|
|
2383
2489
|
return (yield* provenOurs(ask, settled2)) ? found : Unowned2(found);
|
|
@@ -2435,8 +2541,8 @@ var optionMap = (value, family, defaultKey) => {
|
|
|
2435
2541
|
return out;
|
|
2436
2542
|
};
|
|
2437
2543
|
var sameMap = (left, right) => left.size === right.size && [...left].every(([key, value]) => right.get(key) === value);
|
|
2438
|
-
var sortedForm = (
|
|
2439
|
-
var printMap = (
|
|
2544
|
+
var sortedForm = (map9) => [...map9].map(([key, value]) => `${key}=${value}`).sort().join(",");
|
|
2545
|
+
var printMap = (map9, lead = []) => [...lead.filter((key) => map9.has(key)), ...[...map9.keys()].filter((k) => !lead.includes(k))].map((key) => `${key}=${map9.get(key) ?? ""}`).join(",");
|
|
2440
2546
|
var netMaps = (declared, live) => {
|
|
2441
2547
|
const want = optionMap(declared, "net");
|
|
2442
2548
|
const have = optionMap(live, "net");
|
|
@@ -2797,7 +2903,7 @@ var createRefusals = (props) => {
|
|
|
2797
2903
|
}
|
|
2798
2904
|
return refuse;
|
|
2799
2905
|
};
|
|
2800
|
-
var
|
|
2906
|
+
var createForm4 = (props) => {
|
|
2801
2907
|
const form = { vmid: String(props.vmid) };
|
|
2802
2908
|
if (props.ostemplate !== undefined)
|
|
2803
2909
|
form["ostemplate"] = props.ostemplate;
|
|
@@ -2832,11 +2938,11 @@ var identityRefusals = (news, olds, output) => {
|
|
|
2832
2938
|
};
|
|
2833
2939
|
|
|
2834
2940
|
// src/proxmox/lxc-adoption.ts
|
|
2835
|
-
import * as
|
|
2941
|
+
import * as Effect34 from "effect/Effect";
|
|
2836
2942
|
|
|
2837
2943
|
// src/ownership/adopting.ts
|
|
2838
|
-
import * as
|
|
2839
|
-
var adopting = (owner, settled2) =>
|
|
2944
|
+
import * as Effect31 from "effect/Effect";
|
|
2945
|
+
var adopting = (owner, settled2) => Effect31.gen(function* () {
|
|
2840
2946
|
if (owner.output === undefined)
|
|
2841
2947
|
return false;
|
|
2842
2948
|
const generation = yield* recordedGeneration(owner.fqn, owner.instanceId);
|
|
@@ -2848,11 +2954,11 @@ var adopting = (owner, settled2) => Effect30.gen(function* () {
|
|
|
2848
2954
|
});
|
|
2849
2955
|
|
|
2850
2956
|
// src/proxmox/lxc-lifecycle.ts
|
|
2851
|
-
import * as
|
|
2957
|
+
import * as Effect33 from "effect/Effect";
|
|
2852
2958
|
|
|
2853
2959
|
// src/proxmox/network-apply-read.ts
|
|
2854
|
-
import * as
|
|
2855
|
-
var stagedDiff = (target, node) =>
|
|
2960
|
+
import * as Effect32 from "effect/Effect";
|
|
2961
|
+
var stagedDiff = (target, node) => Effect32.gen(function* () {
|
|
2856
2962
|
const path = `nodes/${node}/network`;
|
|
2857
2963
|
const credential = yield* leased(target, "read");
|
|
2858
2964
|
const envelope = yield* pveEnvelopeWith(target, credential, "GET", path);
|
|
@@ -2865,8 +2971,8 @@ var pendingLines = (diff) => {
|
|
|
2865
2971
|
`).filter((line) => (line.startsWith("+") || line.startsWith("-")) && !line.startsWith("+++") && !line.startsWith("---")).length;
|
|
2866
2972
|
return Math.max(changed, 1);
|
|
2867
2973
|
};
|
|
2868
|
-
var pendingCount = (target, node) => stagedDiff(target, node).pipe(
|
|
2869
|
-
var clusterHealth = (target) => pve(target, "read", "GET", "cluster/status").pipe(
|
|
2974
|
+
var pendingCount = (target, node) => stagedDiff(target, node).pipe(Effect32.map(pendingLines));
|
|
2975
|
+
var clusterHealth = (target) => pve(target, "read", "GET", "cluster/status").pipe(Effect32.map((rows) => {
|
|
2870
2976
|
const all = rows ?? [];
|
|
2871
2977
|
const cluster = all.find((row) => row.type === "cluster");
|
|
2872
2978
|
return {
|
|
@@ -2874,21 +2980,21 @@ var clusterHealth = (target) => pve(target, "read", "GET", "cluster/status").pip
|
|
|
2874
2980
|
quorate: cluster === undefined ? true : bool(cluster.quorate)
|
|
2875
2981
|
};
|
|
2876
2982
|
}));
|
|
2877
|
-
var degradedReason = (target) => clusterHealth(target).pipe(
|
|
2983
|
+
var degradedReason = (target) => clusterHealth(target).pipe(Effect32.map((health) => health.quorate && health.offline.length === 0 ? undefined : `quorate=${String(health.quorate)} offline=[${health.offline.join(", ")}]`));
|
|
2878
2984
|
var UNREACHABLE = "the task could not be read -- the node stopped answering, or this lease cannot see it";
|
|
2879
2985
|
var STILL_RUNNING = "still running when this provider stopped waiting";
|
|
2880
2986
|
var ATTEMPTS = 30;
|
|
2881
|
-
var awaitTask = (target, credential, node, upid, attempts = ATTEMPTS) =>
|
|
2987
|
+
var awaitTask = (target, credential, node, upid, attempts = ATTEMPTS) => Effect32.gen(function* () {
|
|
2882
2988
|
const path = `nodes/${node}/tasks/${upid}/status`;
|
|
2883
2989
|
for (let attempt = 0;attempt < attempts; attempt += 1) {
|
|
2884
|
-
const status = yield* pveWith(target, credential, "GET", path).pipe(
|
|
2990
|
+
const status = yield* pveWith(target, credential, "GET", path).pipe(Effect32.orElseSucceed(() => {
|
|
2885
2991
|
return;
|
|
2886
2992
|
}));
|
|
2887
2993
|
if (status === undefined)
|
|
2888
2994
|
return UNREACHABLE;
|
|
2889
2995
|
if (status.status === "stopped")
|
|
2890
2996
|
return text(status.exitstatus, "stopped with no exitstatus");
|
|
2891
|
-
yield*
|
|
2997
|
+
yield* Effect32.sleep("1 second");
|
|
2892
2998
|
}
|
|
2893
2999
|
return STILL_RUNNING;
|
|
2894
3000
|
});
|
|
@@ -2906,43 +3012,43 @@ var LXC_ENDPOINTS = {
|
|
|
2906
3012
|
var CREATE_POLLS = 180;
|
|
2907
3013
|
var TASK_POLLS = 60;
|
|
2908
3014
|
var isServerError = (error) => error instanceof PveError && error.status === 500;
|
|
2909
|
-
var readLive = (where) =>
|
|
2910
|
-
const answer = yield* pve(where.target, "read", "GET", `${guestPath(where)}/config`).pipe(
|
|
3015
|
+
var readLive = (where) => Effect33.gen(function* () {
|
|
3016
|
+
const answer = yield* pve(where.target, "read", "GET", `${guestPath(where)}/config`).pipe(Effect33.map((data) => ({ data, error: undefined })), Effect33.catchIf(isServerError, (error) => Effect33.succeed({ data: undefined, error })));
|
|
2911
3017
|
if (answer.data !== undefined && answer.data !== null)
|
|
2912
3018
|
return answer.data;
|
|
2913
3019
|
const rows = yield* pve(where.target, "read", "GET", "cluster/resources?type=vm");
|
|
2914
3020
|
const found = (rows ?? []).find((row) => Number(row.vmid) === where.vmid);
|
|
2915
3021
|
if (found !== undefined && found.node === where.node && found.type === "lxc") {
|
|
2916
|
-
return yield*
|
|
3022
|
+
return yield* Effect33.fail(answer.error ?? new LxcRefusedError(`CT ${String(where.vmid)} is listed but its config read empty.`));
|
|
2917
3023
|
}
|
|
2918
3024
|
if (found !== undefined) {
|
|
2919
|
-
return yield*
|
|
3025
|
+
return yield* Effect33.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."));
|
|
2920
3026
|
}
|
|
2921
3027
|
return;
|
|
2922
3028
|
});
|
|
2923
|
-
var
|
|
3029
|
+
var attributesOf4 = (where, live) => ({
|
|
2924
3030
|
config: storedConfig(live),
|
|
2925
3031
|
node: where.node,
|
|
2926
3032
|
rawKeys: rawKeysOf(live),
|
|
2927
3033
|
vmid: where.vmid
|
|
2928
3034
|
});
|
|
2929
|
-
var task = (where, method, path, what, polls, form) =>
|
|
3035
|
+
var task = (where, method, path, what, polls, form) => Effect33.gen(function* () {
|
|
2930
3036
|
const credential = yield* mint(where.target, "provision");
|
|
2931
3037
|
const upid = text(yield* pveWith(where.target, credential, method, path, form));
|
|
2932
3038
|
if (upid === "") {
|
|
2933
|
-
return yield*
|
|
3039
|
+
return yield* Effect33.fail(new LxcRefusedError(`${what}: PVE returned no task id, so the write cannot be confirmed and is not claimed.`));
|
|
2934
3040
|
}
|
|
2935
3041
|
const exit = yield* awaitTask(where.target, credential, where.node, upid, polls);
|
|
2936
3042
|
if (exit !== "OK") {
|
|
2937
|
-
return yield*
|
|
3043
|
+
return yield* Effect33.fail(new LxcRefusedError(`${what}: task ${upid} ended "${exit}". Read \`pvesh get /nodes/${where.node}/tasks/` + `${upid}/log\` before running the deploy again.`));
|
|
2938
3044
|
}
|
|
2939
3045
|
});
|
|
2940
|
-
var createGuest = (props) =>
|
|
2941
|
-
const form =
|
|
3046
|
+
var createGuest = (props) => Effect33.gen(function* () {
|
|
3047
|
+
const form = createForm4(props);
|
|
2942
3048
|
yield* guardForm(LXC_ENDPOINTS.create, form, true);
|
|
2943
3049
|
return yield* task(props, "POST", `nodes/${props.node}/lxc`, `create CT ${String(props.vmid)}`, CREATE_POLLS, form);
|
|
2944
3050
|
});
|
|
2945
|
-
var updateGuest = (props, change, digest) =>
|
|
3051
|
+
var updateGuest = (props, change, digest) => Effect33.gen(function* () {
|
|
2946
3052
|
if (Object.keys(change.put).length > 0 || change.clear.length > 0) {
|
|
2947
3053
|
const form = withClears({ ...change.put, ...digest === "" ? {} : { digest } }, change.clear);
|
|
2948
3054
|
yield* guardForm(LXC_ENDPOINTS.update, form, false);
|
|
@@ -2954,47 +3060,47 @@ var updateGuest = (props, change, digest) => Effect32.gen(function* () {
|
|
|
2954
3060
|
yield* task(props, "PUT", `${guestPath(props)}/resize`, `grow ${grow.disk} of CT ${String(props.vmid)} to ${grow.size}`, TASK_POLLS, grown);
|
|
2955
3061
|
}
|
|
2956
3062
|
});
|
|
2957
|
-
var destroyGuest = (where, declared) =>
|
|
3063
|
+
var destroyGuest = (where, declared) => Effect33.gen(function* () {
|
|
2958
3064
|
const live = yield* readLive(where);
|
|
2959
3065
|
if (live === undefined)
|
|
2960
3066
|
return;
|
|
2961
3067
|
const drift = judge(declared, live).drift;
|
|
2962
3068
|
if (drift.length > 0) {
|
|
2963
|
-
return yield*
|
|
3069
|
+
return yield* Effect33.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."));
|
|
2964
3070
|
}
|
|
2965
3071
|
yield* task(where, "DELETE", guestPath(where), `destroy CT ${String(where.vmid)}`, TASK_POLLS);
|
|
2966
3072
|
});
|
|
2967
3073
|
|
|
2968
3074
|
// src/proxmox/lxc-adoption.ts
|
|
2969
3075
|
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.";
|
|
2970
|
-
var refuseAdoptedDrift = (owner, props, live, change) =>
|
|
3076
|
+
var refuseAdoptedDrift = (owner, props, live, change) => Effect34.gen(function* () {
|
|
2971
3077
|
if (change.drift.length === 0)
|
|
2972
3078
|
return;
|
|
2973
|
-
const settled2 = (recorded) =>
|
|
3079
|
+
const settled2 = (recorded) => Effect34.sync(() => judge(recorded, live).drift.length === 0);
|
|
2974
3080
|
if (!(yield* adopting(owner, settled2)))
|
|
2975
3081
|
return;
|
|
2976
|
-
return yield*
|
|
3082
|
+
return yield* Effect34.fail(new LxcRefusedError(adoptionRefusal(props, change.drift)));
|
|
2977
3083
|
});
|
|
2978
3084
|
|
|
2979
3085
|
// src/proxmox/lxc.ts
|
|
2980
3086
|
var ProxmoxLxc = Resource13("Proxmox.Lxc", { defaultRemovalPolicy: "retain" });
|
|
2981
|
-
var refused = (reasons) =>
|
|
3087
|
+
var refused = (reasons) => Effect35.fail(new LxcRefusedError(reasons.join(`
|
|
2982
3088
|
`)));
|
|
2983
|
-
var diff = (owner, news, olds) =>
|
|
3089
|
+
var diff = (owner, news, olds) => Effect35.gen(function* () {
|
|
2984
3090
|
const { output } = owner;
|
|
2985
3091
|
const identity2 = identityRefusals(news, olds, output);
|
|
2986
3092
|
if (identity2.length > 0)
|
|
2987
3093
|
return yield* refused(identity2);
|
|
2988
3094
|
if (output === undefined)
|
|
2989
3095
|
return;
|
|
2990
|
-
if (!
|
|
3096
|
+
if (!isResolved8(news))
|
|
2991
3097
|
return;
|
|
2992
3098
|
const live = yield* readLive(news);
|
|
2993
3099
|
if (live === undefined) {
|
|
2994
3100
|
const refusals = createRefusals(news);
|
|
2995
3101
|
if (refusals.length > 0)
|
|
2996
3102
|
return yield* refused(refusals);
|
|
2997
|
-
yield*
|
|
3103
|
+
yield* Effect35.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.");
|
|
2998
3104
|
return { action: "update" };
|
|
2999
3105
|
}
|
|
3000
3106
|
const change = judge(news, live);
|
|
@@ -3005,10 +3111,10 @@ var diff = (owner, news, olds) => Effect34.gen(function* () {
|
|
|
3005
3111
|
if (change.drift.length === 0 && !moved)
|
|
3006
3112
|
return { action: "noop" };
|
|
3007
3113
|
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.";
|
|
3008
|
-
yield*
|
|
3114
|
+
yield* Effect35.logWarning(`Proxmox.Lxc ${news.node}/${String(news.vmid)}: ${what}`);
|
|
3009
3115
|
return { action: "update" };
|
|
3010
3116
|
});
|
|
3011
|
-
var reconcile = (owner, news) =>
|
|
3117
|
+
var reconcile = (owner, news) => Effect35.gen(function* () {
|
|
3012
3118
|
const { output } = owner;
|
|
3013
3119
|
const identity2 = identityRefusals(news, news, output);
|
|
3014
3120
|
if (identity2.length > 0)
|
|
@@ -3047,22 +3153,22 @@ var reconcile = (owner, news) => Effect34.gen(function* () {
|
|
|
3047
3153
|
`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."
|
|
3048
3154
|
]);
|
|
3049
3155
|
}
|
|
3050
|
-
return
|
|
3156
|
+
return attributesOf4(news, after);
|
|
3051
3157
|
});
|
|
3052
3158
|
var whereOf = (olds, output) => ({
|
|
3053
3159
|
node: output?.node ?? olds.node,
|
|
3054
3160
|
target: olds.target,
|
|
3055
3161
|
vmid: output?.vmid ?? olds.vmid
|
|
3056
3162
|
});
|
|
3057
|
-
var read = (owner, olds) =>
|
|
3163
|
+
var read = (owner, olds) => Effect35.gen(function* () {
|
|
3058
3164
|
const where = whereOf(olds, owner.output);
|
|
3059
3165
|
const live = yield* readLive(where);
|
|
3060
3166
|
if (live === undefined)
|
|
3061
3167
|
return;
|
|
3062
|
-
const settled2 =
|
|
3063
|
-
return yield* ownedRead(owner,
|
|
3168
|
+
const settled2 = Effect35.sync(() => judge(olds, live).drift.length === 0);
|
|
3169
|
+
return yield* ownedRead(owner, attributesOf4(where, live), settled2);
|
|
3064
3170
|
});
|
|
3065
|
-
var
|
|
3171
|
+
var handlers8 = {
|
|
3066
3172
|
delete: ({ olds, output }) => destroyGuest(whereOf(olds, output), olds),
|
|
3067
3173
|
diff: ({
|
|
3068
3174
|
fqn,
|
|
@@ -3071,7 +3177,7 @@ var handlers10 = {
|
|
|
3071
3177
|
olds,
|
|
3072
3178
|
output
|
|
3073
3179
|
}) => diff({ fqn, instanceId, output }, news, olds),
|
|
3074
|
-
list: () =>
|
|
3180
|
+
list: () => Effect35.succeed([]),
|
|
3075
3181
|
read: ({
|
|
3076
3182
|
fqn,
|
|
3077
3183
|
instanceId,
|
|
@@ -3085,11 +3191,11 @@ var handlers10 = {
|
|
|
3085
3191
|
output
|
|
3086
3192
|
}) => reconcile({ fqn, instanceId, output }, news)
|
|
3087
3193
|
};
|
|
3088
|
-
var ProxmoxLxcProvider = () => Provider13.effect(ProxmoxLxc,
|
|
3194
|
+
var ProxmoxLxcProvider = () => Provider13.effect(ProxmoxLxc, Effect35.succeed(ProxmoxLxc.Provider.of(handlers8)).pipe(Effect35.map(provingResumes)));
|
|
3089
3195
|
// src/proxmox/metric-server.ts
|
|
3090
3196
|
import { Resource as Resource14 } from "alchemy";
|
|
3091
3197
|
import * as Provider14 from "alchemy/Provider";
|
|
3092
|
-
import * as
|
|
3198
|
+
import * as Effect36 from "effect/Effect";
|
|
3093
3199
|
|
|
3094
3200
|
// src/proxmox/metric-server-form.ts
|
|
3095
3201
|
var UNSET2 = -1;
|
|
@@ -3169,7 +3275,7 @@ var matchesType = (attributes, props) => {
|
|
|
3169
3275
|
return socket && attributes.path === (props.path ?? "");
|
|
3170
3276
|
return socket && attributes.influxdbproto === (props.influxdbproto ?? "") && attributes.organization === (props.organization ?? "") && attributes.bucket === (props.bucket ?? "") && attributes["api-path-prefix"] === (props["api-path-prefix"] ?? "") && attributes["max-body-size"] === (props["max-body-size"] ?? UNSET2) && attributes["verify-certificate"] === (props["verify-certificate"] !== false);
|
|
3171
3277
|
};
|
|
3172
|
-
var
|
|
3278
|
+
var handlers9 = pveHandlers({
|
|
3173
3279
|
attributes: (live, props) => {
|
|
3174
3280
|
const liveType = text(live["type"]);
|
|
3175
3281
|
if (liveType !== "" && liveType !== props.type)
|
|
@@ -3202,12 +3308,12 @@ var handlers11 = pveHandlers({
|
|
|
3202
3308
|
path: (props) => `cluster/metrics/server/${props.id}`,
|
|
3203
3309
|
updateForm: update
|
|
3204
3310
|
});
|
|
3205
|
-
var ProxmoxMetricServerProvider = () => Provider14.effect(ProxmoxMetricServer,
|
|
3311
|
+
var ProxmoxMetricServerProvider = () => Provider14.effect(ProxmoxMetricServer, Effect36.succeed(ProxmoxMetricServer.Provider.of(handlers9)));
|
|
3206
3312
|
// src/proxmox/network-apply.ts
|
|
3207
3313
|
import { Resource as Resource15 } from "alchemy";
|
|
3208
|
-
import { isResolved as
|
|
3314
|
+
import { isResolved as isResolved9 } from "alchemy/Diff";
|
|
3209
3315
|
import * as Provider15 from "alchemy/Provider";
|
|
3210
|
-
import * as
|
|
3316
|
+
import * as Effect37 from "effect/Effect";
|
|
3211
3317
|
|
|
3212
3318
|
// src/proxmox/apply-endpoints.ts
|
|
3213
3319
|
var NETWORK_APPLY_ENDPOINT = "pve:PUT /nodes/{node}/network";
|
|
@@ -3217,15 +3323,15 @@ var guardSdnApply = guardForm(SDN_APPLY_ENDPOINT, {}, true);
|
|
|
3217
3323
|
|
|
3218
3324
|
// src/proxmox/network-apply.ts
|
|
3219
3325
|
var ProxmoxNetworkApply = Resource15("Proxmox.NetworkApply");
|
|
3220
|
-
var read2 = (props) => pendingCount(props.target, props.node).pipe(
|
|
3221
|
-
var diff2 = (news) =>
|
|
3222
|
-
if (!
|
|
3326
|
+
var read2 = (props) => pendingCount(props.target, props.node).pipe(Effect37.map((pending) => ({ node: props.node, pending })));
|
|
3327
|
+
var diff2 = (news) => Effect37.gen(function* () {
|
|
3328
|
+
if (!isResolved9(news))
|
|
3223
3329
|
return;
|
|
3224
3330
|
const { pending } = yield* read2(news);
|
|
3225
3331
|
return pending === 0 ? { action: "noop" } : { action: "update" };
|
|
3226
3332
|
});
|
|
3227
|
-
var refuse = (node, why) =>
|
|
3228
|
-
var apply = (props) =>
|
|
3333
|
+
var refuse = (node, why) => Effect37.die(new Error(`Proxmox.NetworkApply ${node}: ${why}`));
|
|
3334
|
+
var apply = (props) => Effect37.gen(function* () {
|
|
3229
3335
|
const node = props.node;
|
|
3230
3336
|
const staged = yield* pendingCount(props.target, node);
|
|
3231
3337
|
if (staged === 0)
|
|
@@ -3254,18 +3360,18 @@ var apply = (props) => Effect36.gen(function* () {
|
|
|
3254
3360
|
}
|
|
3255
3361
|
return { node, pending: left };
|
|
3256
3362
|
});
|
|
3257
|
-
var
|
|
3258
|
-
delete: () =>
|
|
3363
|
+
var handlers10 = {
|
|
3364
|
+
delete: () => Effect37.void,
|
|
3259
3365
|
diff: ({ news }) => diff2(news),
|
|
3260
|
-
list: () =>
|
|
3366
|
+
list: () => Effect37.succeed([]),
|
|
3261
3367
|
read: ({ olds }) => read2(olds),
|
|
3262
3368
|
reconcile: ({ news }) => apply(news)
|
|
3263
3369
|
};
|
|
3264
|
-
var ProxmoxNetworkApplyProvider = () => Provider15.effect(ProxmoxNetworkApply,
|
|
3370
|
+
var ProxmoxNetworkApplyProvider = () => Provider15.effect(ProxmoxNetworkApply, Effect37.succeed(ProxmoxNetworkApply.Provider.of(handlers10)));
|
|
3265
3371
|
// src/proxmox/node-network.ts
|
|
3266
3372
|
import { Resource as Resource16 } from "alchemy";
|
|
3267
3373
|
import * as Provider16 from "alchemy/Provider";
|
|
3268
|
-
import * as
|
|
3374
|
+
import * as Effect38 from "effect/Effect";
|
|
3269
3375
|
|
|
3270
3376
|
// src/proxmox/node-network-form.ts
|
|
3271
3377
|
var UNSET3 = -1;
|
|
@@ -3341,7 +3447,7 @@ var readAttributes2 = (live, props) => {
|
|
|
3341
3447
|
var ProxmoxNodeNetwork = Resource16("Proxmox.NodeNetwork", {
|
|
3342
3448
|
defaultRemovalPolicy: "retain"
|
|
3343
3449
|
});
|
|
3344
|
-
var
|
|
3450
|
+
var handlers11 = pveHandlers({
|
|
3345
3451
|
attributes: readAttributes2,
|
|
3346
3452
|
collection: (props) => `nodes/${props.node}/network`,
|
|
3347
3453
|
createForm: createBody3,
|
|
@@ -3353,11 +3459,11 @@ var handlers13 = pveHandlers({
|
|
|
3353
3459
|
path: (props) => `nodes/${props.node}/network/${props.iface}`,
|
|
3354
3460
|
updateForm: updateBody2
|
|
3355
3461
|
});
|
|
3356
|
-
var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork,
|
|
3462
|
+
var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork, Effect38.succeed(ProxmoxNodeNetwork.Provider.of(handlers11)));
|
|
3357
3463
|
// src/proxmox/notification-matcher.ts
|
|
3358
3464
|
import { Resource as Resource17 } from "alchemy";
|
|
3359
3465
|
import * as Provider17 from "alchemy/Provider";
|
|
3360
|
-
import * as
|
|
3466
|
+
import * as Effect39 from "effect/Effect";
|
|
3361
3467
|
|
|
3362
3468
|
// src/proxmox/notification-matcher-form.ts
|
|
3363
3469
|
var list = (value) => Array.isArray(value) ? value.map((item) => String(item)) : [];
|
|
@@ -3435,7 +3541,7 @@ var matcherUpdateForm = (fields) => {
|
|
|
3435
3541
|
|
|
3436
3542
|
// src/proxmox/notification-matcher.ts
|
|
3437
3543
|
var ProxmoxNotificationMatcher = Resource17("Proxmox.NotificationMatcher");
|
|
3438
|
-
var
|
|
3544
|
+
var handlers12 = pveHandlers({
|
|
3439
3545
|
attributes: (live, props) => matcherAttributes(live, props.name),
|
|
3440
3546
|
collection: () => "cluster/notifications/matchers",
|
|
3441
3547
|
createForm: matcherCreateForm,
|
|
@@ -3447,11 +3553,11 @@ var handlers14 = pveHandlers({
|
|
|
3447
3553
|
path: (props) => `cluster/notifications/matchers/${props.name}`,
|
|
3448
3554
|
updateForm: matcherUpdateForm
|
|
3449
3555
|
});
|
|
3450
|
-
var ProxmoxNotificationMatcherProvider = () => Provider17.effect(ProxmoxNotificationMatcher,
|
|
3556
|
+
var ProxmoxNotificationMatcherProvider = () => Provider17.effect(ProxmoxNotificationMatcher, Effect39.succeed(ProxmoxNotificationMatcher.Provider.of(handlers12)));
|
|
3451
3557
|
// src/proxmox/notification-target.ts
|
|
3452
3558
|
import { Resource as Resource18 } from "alchemy";
|
|
3453
3559
|
import * as Provider18 from "alchemy/Provider";
|
|
3454
|
-
import * as
|
|
3560
|
+
import * as Effect40 from "effect/Effect";
|
|
3455
3561
|
|
|
3456
3562
|
// src/proxmox/notification-target-endpoint.ts
|
|
3457
3563
|
var ENDPOINTS = {
|
|
@@ -3507,7 +3613,7 @@ var createShape = (props) => ({
|
|
|
3507
3613
|
|
|
3508
3614
|
// src/proxmox/notification-target.ts
|
|
3509
3615
|
var ProxmoxNotificationTarget = Resource18("Proxmox.NotificationTarget");
|
|
3510
|
-
var
|
|
3616
|
+
var handlers13 = pveHandlers({
|
|
3511
3617
|
attributes: (live, props) => ({
|
|
3512
3618
|
author: text(live["author"]),
|
|
3513
3619
|
body: text(live["body"]),
|
|
@@ -3533,15 +3639,15 @@ var handlers15 = pveHandlers({
|
|
|
3533
3639
|
path: (props) => `cluster/notifications/endpoints/${props.type}/${props.name}`,
|
|
3534
3640
|
updateForm: shape4
|
|
3535
3641
|
});
|
|
3536
|
-
var ProxmoxNotificationTargetProvider = () => Provider18.effect(ProxmoxNotificationTarget,
|
|
3642
|
+
var ProxmoxNotificationTargetProvider = () => Provider18.effect(ProxmoxNotificationTarget, Effect40.succeed(ProxmoxNotificationTarget.Provider.of(handlers13)));
|
|
3537
3643
|
// src/proxmox/pbs-datastore.ts
|
|
3538
3644
|
import { Resource as Resource19 } from "alchemy";
|
|
3539
|
-
import { isResolved as
|
|
3645
|
+
import { isResolved as isResolved10 } from "alchemy/Diff";
|
|
3540
3646
|
import * as Provider19 from "alchemy/Provider";
|
|
3541
|
-
import * as
|
|
3647
|
+
import * as Effect44 from "effect/Effect";
|
|
3542
3648
|
|
|
3543
3649
|
// src/proxmox/pbs-datastore-form.ts
|
|
3544
|
-
import * as
|
|
3650
|
+
import * as Effect41 from "effect/Effect";
|
|
3545
3651
|
var attributes = (live, props) => ({
|
|
3546
3652
|
backend: propertyString(live["backend"]),
|
|
3547
3653
|
comment: text(live["comment"]),
|
|
@@ -3564,7 +3670,7 @@ var attributes = (live, props) => ({
|
|
|
3564
3670
|
"verify-new": bool(live["verify-new"])
|
|
3565
3671
|
});
|
|
3566
3672
|
var same4 = (declared, live) => declared === undefined || declared === live;
|
|
3567
|
-
var
|
|
3673
|
+
var matches4 = (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"]);
|
|
3568
3674
|
var set2 = (key, value) => value === undefined ? {} : { [key]: value };
|
|
3569
3675
|
var count = (value) => value === undefined ? undefined : String(value);
|
|
3570
3676
|
var mutable = (props) => ({
|
|
@@ -3585,56 +3691,56 @@ var mutable = (props) => ({
|
|
|
3585
3691
|
...set2("tuning", props.tuning),
|
|
3586
3692
|
...set2("verify-new", flag(props["verify-new"]))
|
|
3587
3693
|
});
|
|
3588
|
-
var
|
|
3694
|
+
var createForm5 = (props) => ({
|
|
3589
3695
|
...mutable(props),
|
|
3590
3696
|
name: props.name,
|
|
3591
3697
|
path: props.path,
|
|
3592
3698
|
...props.backend === undefined ? {} : { backend: props.backend }
|
|
3593
3699
|
});
|
|
3594
|
-
var
|
|
3700
|
+
var updateForm2 = (props) => mutable(props);
|
|
3595
3701
|
var object2 = (props) => `config/datastore/${props.name}`;
|
|
3596
|
-
var readOne = (props) => pve(props.target, "read", "GET", object2(props)).pipe(
|
|
3702
|
+
var readOne = (props) => pve(props.target, "read", "GET", object2(props)).pipe(Effect41.map((live) => live === undefined || typeof live["name"] !== "string" ? undefined : attributes(live, props)), Effect41.orElseSucceed(() => {
|
|
3597
3703
|
return;
|
|
3598
3704
|
}));
|
|
3599
3705
|
|
|
3600
3706
|
// src/proxmox/pbs-datastore-guard.ts
|
|
3601
|
-
import * as
|
|
3707
|
+
import * as Effect42 from "effect/Effect";
|
|
3602
3708
|
var samePath = (left, right) => {
|
|
3603
3709
|
const trim = (value) => value.length > 1 ? value.replace(/\/+$/, "") : value;
|
|
3604
3710
|
return trim(left) === trim(right);
|
|
3605
3711
|
};
|
|
3606
|
-
var guardPath = (live, props) => live.path === "" || samePath(live.path, props.path) ?
|
|
3607
|
-
var guardBackend = (live, props) => props.backend === undefined || propertyString(props.backend) === live.backend ?
|
|
3608
|
-
var settle3 = (read3, done) =>
|
|
3712
|
+
var guardPath = (live, props) => live.path === "" || samePath(live.path, props.path) ? Effect42.void : Effect42.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."));
|
|
3713
|
+
var guardBackend = (live, props) => props.backend === undefined || propertyString(props.backend) === live.backend ? Effect42.void : Effect42.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."));
|
|
3714
|
+
var settle3 = (read3, done) => Effect42.gen(function* () {
|
|
3609
3715
|
for (let attempt = 0;attempt < 60; attempt += 1) {
|
|
3610
3716
|
const live = yield* read3;
|
|
3611
3717
|
if (done(live))
|
|
3612
3718
|
return live;
|
|
3613
|
-
yield*
|
|
3719
|
+
yield* Effect42.sleep("2 seconds");
|
|
3614
3720
|
}
|
|
3615
3721
|
return yield* read3;
|
|
3616
3722
|
});
|
|
3617
3723
|
|
|
3618
3724
|
// src/proxmox/pbs-datastore-endpoint.ts
|
|
3619
|
-
import * as
|
|
3725
|
+
import * as Effect43 from "effect/Effect";
|
|
3620
3726
|
var DATASTORE_ENDPOINT = {
|
|
3621
3727
|
create: "pbs:POST /config/datastore",
|
|
3622
3728
|
update: "pbs:PUT /config/datastore/{name}"
|
|
3623
3729
|
};
|
|
3624
|
-
var guardDatastoreForms = (props) =>
|
|
3625
|
-
yield* guardForm(DATASTORE_ENDPOINT.create,
|
|
3626
|
-
yield* guardForm(DATASTORE_ENDPOINT.update,
|
|
3730
|
+
var guardDatastoreForms = (props) => Effect43.gen(function* () {
|
|
3731
|
+
yield* guardForm(DATASTORE_ENDPOINT.create, createForm5(props), true);
|
|
3732
|
+
yield* guardForm(DATASTORE_ENDPOINT.update, updateForm2(props), false);
|
|
3627
3733
|
});
|
|
3628
3734
|
|
|
3629
3735
|
// src/proxmox/pbs-datastore.ts
|
|
3630
3736
|
var PbsDatastore = Resource19("Pbs.Datastore", {
|
|
3631
3737
|
defaultRemovalPolicy: "retain"
|
|
3632
3738
|
});
|
|
3633
|
-
var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore,
|
|
3634
|
-
list: () =>
|
|
3739
|
+
var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore, Effect44.succeed(PbsDatastore.Provider.of({
|
|
3740
|
+
list: () => Effect44.succeed([]),
|
|
3635
3741
|
read: ({ olds }) => readOne(olds),
|
|
3636
|
-
diff: ({ news, output }) =>
|
|
3637
|
-
if (!
|
|
3742
|
+
diff: ({ news, output }) => Effect44.gen(function* () {
|
|
3743
|
+
if (!isResolved10(news))
|
|
3638
3744
|
return;
|
|
3639
3745
|
yield* guardDatastoreForms(news);
|
|
3640
3746
|
if (output === undefined)
|
|
@@ -3644,42 +3750,42 @@ var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore, Effect43.succee
|
|
|
3644
3750
|
return { action: "update" };
|
|
3645
3751
|
yield* guardPath(live, news);
|
|
3646
3752
|
yield* guardBackend(live, news);
|
|
3647
|
-
return
|
|
3753
|
+
return matches4(live, news) ? { action: "noop" } : { action: "update" };
|
|
3648
3754
|
}),
|
|
3649
|
-
reconcile:
|
|
3755
|
+
reconcile: Effect44.fn(function* ({ news }) {
|
|
3650
3756
|
yield* guardDatastoreForms(news);
|
|
3651
3757
|
const live = yield* readOne(news);
|
|
3652
3758
|
let upid;
|
|
3653
3759
|
if (live === undefined) {
|
|
3654
|
-
upid = yield* pve(news.target, "provision", "POST", "config/datastore",
|
|
3760
|
+
upid = yield* pve(news.target, "provision", "POST", "config/datastore", createForm5(news));
|
|
3655
3761
|
} else {
|
|
3656
3762
|
yield* guardPath(live, news);
|
|
3657
3763
|
yield* guardBackend(live, news);
|
|
3658
|
-
const form = formToSend(
|
|
3764
|
+
const form = formToSend(matches4, live, news, updateForm2(news));
|
|
3659
3765
|
if (form !== undefined) {
|
|
3660
3766
|
yield* pve(news.target, "provision", "PUT", object2(news), form);
|
|
3661
3767
|
}
|
|
3662
3768
|
}
|
|
3663
|
-
const after = yield* settle3(readOne(news), (row) => row !== undefined &&
|
|
3664
|
-
if (after === undefined || !
|
|
3665
|
-
return yield*
|
|
3769
|
+
const after = yield* settle3(readOne(news), (row) => row !== undefined && matches4(row, news));
|
|
3770
|
+
if (after === undefined || !matches4(after, news)) {
|
|
3771
|
+
return yield* Effect44.die(new Error(`${object2(news)}: the write returned no error but the datastore still does not ` + `match the declaration after 120s. PBS answered with task ${upid ?? "(none)"} ` + "-- 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."));
|
|
3666
3772
|
}
|
|
3667
3773
|
return after;
|
|
3668
3774
|
}),
|
|
3669
|
-
delete:
|
|
3775
|
+
delete: Effect44.fn(function* ({ olds }) {
|
|
3670
3776
|
yield* pve(olds.target, "provision", "DELETE", object2(olds));
|
|
3671
3777
|
const left = yield* settle3(readOne(olds), (row) => row === undefined);
|
|
3672
3778
|
if (left !== undefined) {
|
|
3673
|
-
return yield*
|
|
3779
|
+
return yield* Effect44.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>\`.`));
|
|
3674
3780
|
}
|
|
3675
3781
|
})
|
|
3676
3782
|
})));
|
|
3677
3783
|
// src/proxmox/pbs-notification-matcher.ts
|
|
3678
3784
|
import { Resource as Resource20 } from "alchemy";
|
|
3679
3785
|
import * as Provider20 from "alchemy/Provider";
|
|
3680
|
-
import * as
|
|
3786
|
+
import * as Effect45 from "effect/Effect";
|
|
3681
3787
|
var PbsNotificationMatcher = Resource20("Pbs.NotificationMatcher");
|
|
3682
|
-
var
|
|
3788
|
+
var handlers14 = pveHandlers({
|
|
3683
3789
|
attributes: (live, props) => matcherAttributes(live, props.name),
|
|
3684
3790
|
collection: () => "config/notifications/matchers",
|
|
3685
3791
|
createForm: matcherCreateForm,
|
|
@@ -3691,15 +3797,15 @@ var handlers16 = pveHandlers({
|
|
|
3691
3797
|
path: (props) => `config/notifications/matchers/${props.name}`,
|
|
3692
3798
|
updateForm: matcherUpdateForm
|
|
3693
3799
|
});
|
|
3694
|
-
var PbsNotificationMatcherProvider = () => Provider20.effect(PbsNotificationMatcher,
|
|
3800
|
+
var PbsNotificationMatcherProvider = () => Provider20.effect(PbsNotificationMatcher, Effect45.succeed(PbsNotificationMatcher.Provider.of(handlers14)));
|
|
3695
3801
|
// src/proxmox/pbs-notification-target.ts
|
|
3696
3802
|
import { Resource as Resource21 } from "alchemy";
|
|
3697
3803
|
import * as Provider21 from "alchemy/Provider";
|
|
3698
|
-
import * as
|
|
3804
|
+
import * as Effect47 from "effect/Effect";
|
|
3699
3805
|
|
|
3700
3806
|
// src/proxmox/pbs-notification-target-lifecycle.ts
|
|
3701
|
-
import { isResolved as
|
|
3702
|
-
import * as
|
|
3807
|
+
import { isResolved as isResolved11 } from "alchemy/Diff";
|
|
3808
|
+
import * as Effect46 from "effect/Effect";
|
|
3703
3809
|
|
|
3704
3810
|
// src/proxmox/pbs-notification-target-endpoint.ts
|
|
3705
3811
|
var ENDPOINTS2 = {
|
|
@@ -3976,12 +4082,12 @@ var targetForm = (props, groups, carry, mode) => {
|
|
|
3976
4082
|
|
|
3977
4083
|
// src/proxmox/pbs-notification-target-lifecycle.ts
|
|
3978
4084
|
var path = (props) => `config/notifications/endpoints/${props.type}/${props.name}`;
|
|
3979
|
-
var readLive2 = (props) => pve(props.target, "read", "GET", path(props)).pipe(
|
|
4085
|
+
var readLive2 = (props) => pve(props.target, "read", "GET", path(props)).pipe(Effect46.map((data) => data == null ? undefined : targetAttributes(data, props)), Effect46.orElseSucceed(() => {
|
|
3980
4086
|
return;
|
|
3981
4087
|
}));
|
|
3982
4088
|
var refuse2 = (props) => {
|
|
3983
4089
|
const reasons = refusals(props);
|
|
3984
|
-
return reasons.length === 0 ?
|
|
4090
|
+
return reasons.length === 0 ? Effect46.void : Effect46.die(new Error(`${path(props)}: ${reasons.join("; ")}.`));
|
|
3985
4091
|
};
|
|
3986
4092
|
var toCarry = (live, props, groups, sealed) => {
|
|
3987
4093
|
const header = headerState(live, props, groups) === "stale";
|
|
@@ -3993,17 +4099,17 @@ var requireValues = (props, groups, carry) => {
|
|
|
3993
4099
|
...carry.header ? groups.header.missing : [],
|
|
3994
4100
|
...carry.sealed ? groups.sealed.missing : []
|
|
3995
4101
|
];
|
|
3996
|
-
return missing.length === 0 ?
|
|
4102
|
+
return missing.length === 0 ? Effect46.void : Effect46.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."));
|
|
3997
4103
|
};
|
|
3998
|
-
var
|
|
3999
|
-
list: () =>
|
|
4000
|
-
read: ({ olds, output }) => (output === undefined ? refuse2(olds) :
|
|
4104
|
+
var handlers15 = {
|
|
4105
|
+
list: () => Effect46.succeed([]),
|
|
4106
|
+
read: ({ olds, output }) => (output === undefined ? refuse2(olds) : Effect46.void).pipe(Effect46.andThen(readLive2(olds)), Effect46.map((live) => live === undefined ? undefined : { ...live, sealed: output?.sealed ?? "" })),
|
|
4001
4107
|
diff: ({
|
|
4002
4108
|
news,
|
|
4003
4109
|
olds,
|
|
4004
4110
|
output
|
|
4005
|
-
}) =>
|
|
4006
|
-
if (output === undefined || !
|
|
4111
|
+
}) => Effect46.gen(function* () {
|
|
4112
|
+
if (output === undefined || !isResolved11(news))
|
|
4007
4113
|
return;
|
|
4008
4114
|
if (olds !== undefined && (olds.type !== news.type || olds.name !== news.name)) {
|
|
4009
4115
|
return { action: "replace", deleteFirst: olds.name === news.name };
|
|
@@ -4014,7 +4120,7 @@ var handlers17 = {
|
|
|
4014
4120
|
return { action: "update" };
|
|
4015
4121
|
return toCarry(live, news, resolveGroups(news), output.sealed) === undefined ? { action: "noop" } : { action: "update" };
|
|
4016
4122
|
}),
|
|
4017
|
-
reconcile: ({ news, output }) =>
|
|
4123
|
+
reconcile: ({ news, output }) => Effect46.gen(function* () {
|
|
4018
4124
|
yield* refuse2(news);
|
|
4019
4125
|
const groups = resolveGroups(news);
|
|
4020
4126
|
const live = yield* readLive2(news);
|
|
@@ -4040,7 +4146,7 @@ var handlers17 = {
|
|
|
4040
4146
|
}
|
|
4041
4147
|
const after = yield* readLive2(news);
|
|
4042
4148
|
if (after === undefined) {
|
|
4043
|
-
return yield*
|
|
4149
|
+
return yield* Effect46.die(new Error(`${path(news)}: the write returned no error but the target is still absent.`));
|
|
4044
4150
|
}
|
|
4045
4151
|
return { ...after, sealed };
|
|
4046
4152
|
}),
|
|
@@ -4049,11 +4155,11 @@ var handlers17 = {
|
|
|
4049
4155
|
|
|
4050
4156
|
// src/proxmox/pbs-notification-target.ts
|
|
4051
4157
|
var PbsNotificationTarget = Resource21("Pbs.NotificationTarget");
|
|
4052
|
-
var PbsNotificationTargetProvider = () => Provider21.effect(PbsNotificationTarget,
|
|
4158
|
+
var PbsNotificationTargetProvider = () => Provider21.effect(PbsNotificationTarget, Effect47.succeed(PbsNotificationTarget.Provider.of(handlers15)));
|
|
4053
4159
|
// src/proxmox/pbs-prune-job.ts
|
|
4054
4160
|
import { Resource as Resource22 } from "alchemy";
|
|
4055
4161
|
import * as Provider22 from "alchemy/Provider";
|
|
4056
|
-
import * as
|
|
4162
|
+
import * as Effect48 from "effect/Effect";
|
|
4057
4163
|
|
|
4058
4164
|
// src/proxmox/pbs-prune-job-form.ts
|
|
4059
4165
|
var KEEP_KEYS = [
|
|
@@ -4111,7 +4217,7 @@ var updateBody3 = (props) => {
|
|
|
4111
4217
|
// src/proxmox/pbs-prune-job.ts
|
|
4112
4218
|
var PbsPruneJob = Resource22("Pbs.PruneJob");
|
|
4113
4219
|
var same6 = (declared2, live) => declared2 === undefined || declared2 === live;
|
|
4114
|
-
var
|
|
4220
|
+
var handlers16 = pveHandlers({
|
|
4115
4221
|
attributes: (live, props) => ({
|
|
4116
4222
|
comment: text(live["comment"], ""),
|
|
4117
4223
|
disable: bool(live["disable"], false),
|
|
@@ -4129,11 +4235,11 @@ var handlers18 = pveHandlers({
|
|
|
4129
4235
|
path: (props) => `config/prune/${props.id}`,
|
|
4130
4236
|
updateForm: updateBody3
|
|
4131
4237
|
});
|
|
4132
|
-
var PbsPruneJobProvider = () => Provider22.effect(PbsPruneJob,
|
|
4238
|
+
var PbsPruneJobProvider = () => Provider22.effect(PbsPruneJob, Effect48.succeed(PbsPruneJob.Provider.of(handlers16)));
|
|
4133
4239
|
// src/proxmox/pbs-sync-job.ts
|
|
4134
4240
|
import { Resource as Resource23 } from "alchemy";
|
|
4135
4241
|
import * as Provider23 from "alchemy/Provider";
|
|
4136
|
-
import * as
|
|
4242
|
+
import * as Effect49 from "effect/Effect";
|
|
4137
4243
|
|
|
4138
4244
|
// src/proxmox/pbs-sync-job-form.ts
|
|
4139
4245
|
var UNSET4 = -1;
|
|
@@ -4181,7 +4287,7 @@ var createBody5 = (props) => ({
|
|
|
4181
4287
|
...props["sync-direction"] === undefined ? {} : { "sync-direction": props["sync-direction"] }
|
|
4182
4288
|
});
|
|
4183
4289
|
var updateBody4 = (props) => shape6(props);
|
|
4184
|
-
var
|
|
4290
|
+
var handlers17 = pveHandlers({
|
|
4185
4291
|
attributes: (live, props) => ({
|
|
4186
4292
|
comment: text(live["comment"], ""),
|
|
4187
4293
|
"group-filter": filters(live["group-filter"]),
|
|
@@ -4210,11 +4316,11 @@ var handlers19 = pveHandlers({
|
|
|
4210
4316
|
|
|
4211
4317
|
// src/proxmox/pbs-sync-job.ts
|
|
4212
4318
|
var PbsSyncJob = Resource23("Pbs.SyncJob");
|
|
4213
|
-
var PbsSyncJobProvider = () => Provider23.effect(PbsSyncJob,
|
|
4319
|
+
var PbsSyncJobProvider = () => Provider23.effect(PbsSyncJob, Effect49.succeed(PbsSyncJob.Provider.of(handlers17)));
|
|
4214
4320
|
// src/proxmox/pbs-verify-job.ts
|
|
4215
4321
|
import { Resource as Resource24 } from "alchemy";
|
|
4216
4322
|
import * as Provider24 from "alchemy/Provider";
|
|
4217
|
-
import * as
|
|
4323
|
+
import * as Effect50 from "effect/Effect";
|
|
4218
4324
|
|
|
4219
4325
|
// src/proxmox/pbs-verify-job-form.ts
|
|
4220
4326
|
var rechecks = (ignoreVerified, outdatedAfter) => {
|
|
@@ -4236,7 +4342,7 @@ var shape7 = (props) => ({
|
|
|
4236
4342
|
|
|
4237
4343
|
// src/proxmox/pbs-verify-job.ts
|
|
4238
4344
|
var PbsVerifyJob = Resource24("Pbs.VerifyJob");
|
|
4239
|
-
var
|
|
4345
|
+
var handlers18 = pveHandlers({
|
|
4240
4346
|
attributes: (live, props) => {
|
|
4241
4347
|
const ignoreVerified = bool(live["ignore-verified"], true);
|
|
4242
4348
|
const outdatedAfter = int(live["outdated-after"], -1);
|
|
@@ -4259,11 +4365,11 @@ var handlers20 = pveHandlers({
|
|
|
4259
4365
|
path: (props) => `config/verify/${props.id}`,
|
|
4260
4366
|
updateForm: shape7
|
|
4261
4367
|
});
|
|
4262
|
-
var PbsVerifyJobProvider = () => Provider24.effect(PbsVerifyJob,
|
|
4368
|
+
var PbsVerifyJobProvider = () => Provider24.effect(PbsVerifyJob, Effect50.succeed(PbsVerifyJob.Provider.of(handlers18)));
|
|
4263
4369
|
// src/proxmox/pool.ts
|
|
4264
4370
|
import { Resource as Resource25 } from "alchemy";
|
|
4265
4371
|
import * as Provider25 from "alchemy/Provider";
|
|
4266
|
-
import * as
|
|
4372
|
+
import * as Effect51 from "effect/Effect";
|
|
4267
4373
|
var ProxmoxPool = Resource25("Proxmox.Pool");
|
|
4268
4374
|
var poolCreateForm = (props) => ({
|
|
4269
4375
|
comment: props.comment ?? "",
|
|
@@ -4272,7 +4378,7 @@ var poolCreateForm = (props) => ({
|
|
|
4272
4378
|
var poolUpdateForm = (props) => ({
|
|
4273
4379
|
comment: props.comment ?? ""
|
|
4274
4380
|
});
|
|
4275
|
-
var
|
|
4381
|
+
var handlers19 = pveHandlers({
|
|
4276
4382
|
attributes: (live, props) => {
|
|
4277
4383
|
const row = live;
|
|
4278
4384
|
return {
|
|
@@ -4288,7 +4394,7 @@ var handlers21 = pveHandlers({
|
|
|
4288
4394
|
path: (props) => `pools/${props.poolid}`,
|
|
4289
4395
|
updateForm: poolUpdateForm
|
|
4290
4396
|
});
|
|
4291
|
-
var ProxmoxPoolProvider = () => Provider25.effect(ProxmoxPool,
|
|
4397
|
+
var ProxmoxPoolProvider = () => Provider25.effect(ProxmoxPool, Effect51.succeed(ProxmoxPool.Provider.of(handlers19)));
|
|
4292
4398
|
// src/proxmox/provision-baseline.ts
|
|
4293
4399
|
var PROVISION_PRIVILEGES = Object.freeze([
|
|
4294
4400
|
"Datastore.Allocate",
|
|
@@ -4481,44 +4587,105 @@ echo 'provision baseline: done. Next: point the OpenBao proxmox mount at these u
|
|
|
4481
4587
|
// src/proxmox/provision-declare.ts
|
|
4482
4588
|
import { adopt } from "alchemy/AdoptPolicy";
|
|
4483
4589
|
import * as RemovalPolicy from "alchemy/RemovalPolicy";
|
|
4484
|
-
import * as
|
|
4590
|
+
import * as Effect54 from "effect/Effect";
|
|
4485
4591
|
|
|
4486
4592
|
// src/proxmox/role.ts
|
|
4487
4593
|
import { Resource as Resource26 } from "alchemy";
|
|
4594
|
+
import { isResolved as isResolved12 } from "alchemy/Diff";
|
|
4488
4595
|
import * as Provider26 from "alchemy/Provider";
|
|
4489
|
-
import * as
|
|
4490
|
-
|
|
4596
|
+
import * as access4 from "@distilled.cloud/proxmox/access";
|
|
4597
|
+
import * as Effect52 from "effect/Effect";
|
|
4598
|
+
|
|
4599
|
+
// src/proxmox/role-wire.ts
|
|
4600
|
+
var ROLE_CREATE = "pve:POST /access/roles";
|
|
4601
|
+
var ROLE_UPDATE = "pve:PUT /access/roles/{roleid}";
|
|
4491
4602
|
var canonical2 = (privs) => [...new Set(privs.map((priv) => priv.trim()).filter((priv) => priv.length > 0))].sort();
|
|
4492
|
-
var
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
attributes: (live, props) => ({ privs: livePrivs(live), roleid: props.roleid }),
|
|
4500
|
-
collection: () => "access/roles",
|
|
4501
|
-
createForm: (props) => ({ privs: canonical2(props.privs).join(","), roleid: props.roleid }),
|
|
4502
|
-
endpoint: { create: "pve:POST /access/roles", update: "pve:PUT /access/roles/{roleid}" },
|
|
4503
|
-
matches: (attributes2, props) => canonical2(attributes2.privs).join(",") === canonical2(props.privs).join(","),
|
|
4504
|
-
path: (props) => `access/roles/${props.roleid}`,
|
|
4505
|
-
updateForm: (props) => ({ privs: canonical2(props.privs).join(",") })
|
|
4603
|
+
var createForm6 = (props) => ({
|
|
4604
|
+
privs: canonical2(props.privs).join(","),
|
|
4605
|
+
roleid: props.roleid
|
|
4606
|
+
});
|
|
4607
|
+
var updateForm3 = (props) => ({
|
|
4608
|
+
privs: canonical2(props.privs).join(","),
|
|
4609
|
+
roleid: props.roleid
|
|
4506
4610
|
});
|
|
4507
|
-
var
|
|
4611
|
+
var find2 = (rows, roleid) => rows.find((row) => row.roleid === roleid);
|
|
4612
|
+
var attributesOf5 = (row, props) => ({
|
|
4613
|
+
privs: canonical2(row.privs === undefined ? [] : row.privs.split(",")),
|
|
4614
|
+
roleid: props.roleid
|
|
4615
|
+
});
|
|
4616
|
+
var matches5 = (attributes2, props) => canonical2(attributes2.privs).join(",") === canonical2(props.privs).join(",");
|
|
4617
|
+
|
|
4618
|
+
// src/proxmox/role.ts
|
|
4619
|
+
var ProxmoxRole = Resource26("Proxmox.Role");
|
|
4620
|
+
var readRole = (props) => readOrUnreadable(runPve(props.target, "read", false, access4.listAccessRoles({}))).pipe(Effect52.map((rows) => {
|
|
4621
|
+
if (rows === UNREADABLE)
|
|
4622
|
+
return UNREADABLE;
|
|
4623
|
+
const row = find2(rows, props.roleid);
|
|
4624
|
+
return row === undefined ? undefined : attributesOf5(row, props);
|
|
4625
|
+
}), Effect52.orElseSucceed(() => {
|
|
4626
|
+
return;
|
|
4627
|
+
}));
|
|
4628
|
+
var dropUnreadable4 = (live) => live === UNREADABLE ? undefined : live;
|
|
4629
|
+
var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole, Effect52.succeed(ProxmoxRole.Provider.of({
|
|
4630
|
+
list: () => Effect52.succeed([]),
|
|
4631
|
+
read: Effect52.fn(function* ({ olds }) {
|
|
4632
|
+
return dropUnreadable4(yield* readRole(olds));
|
|
4633
|
+
}),
|
|
4634
|
+
diff: Effect52.fn(function* ({ news, output }) {
|
|
4635
|
+
if (!isResolved12(news))
|
|
4636
|
+
return;
|
|
4637
|
+
yield* guardWrite2(ROLE_CREATE, createForm6(news), output === undefined);
|
|
4638
|
+
yield* guardWrite2(ROLE_UPDATE, updateForm3(news), false);
|
|
4639
|
+
if (output === undefined)
|
|
4640
|
+
return;
|
|
4641
|
+
const live = yield* readRole(news);
|
|
4642
|
+
if (live === UNREADABLE) {
|
|
4643
|
+
yield* unreadableWarning("Proxmox.Role", news.roleid);
|
|
4644
|
+
return { action: "noop" };
|
|
4645
|
+
}
|
|
4646
|
+
if (live === undefined) {
|
|
4647
|
+
yield* guardWrite2(ROLE_CREATE, createForm6(news), true);
|
|
4648
|
+
return { action: "update" };
|
|
4649
|
+
}
|
|
4650
|
+
return matches5(live, news) ? { action: "noop" } : { action: "update" };
|
|
4651
|
+
}),
|
|
4652
|
+
reconcile: Effect52.fn(function* ({ news }) {
|
|
4653
|
+
const before = dropUnreadable4(yield* readRole(news));
|
|
4654
|
+
yield* guardWrite2(ROLE_CREATE, createForm6(news), before === undefined);
|
|
4655
|
+
yield* guardWrite2(ROLE_UPDATE, updateForm3(news), false);
|
|
4656
|
+
if (before === undefined) {
|
|
4657
|
+
yield* runPve(news.target, "provision", true, access4.createAccessRole(createForm6(news)));
|
|
4658
|
+
} else if (!matches5(before, news)) {
|
|
4659
|
+
yield* runPve(news.target, "provision", true, access4.putAccessRole(updateForm3(news)));
|
|
4660
|
+
}
|
|
4661
|
+
const after = dropUnreadable4(yield* readRole(news));
|
|
4662
|
+
if (after === undefined) {
|
|
4663
|
+
return yield* Effect52.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."));
|
|
4664
|
+
}
|
|
4665
|
+
return after;
|
|
4666
|
+
}),
|
|
4667
|
+
delete: Effect52.fn(function* ({ olds }) {
|
|
4668
|
+
yield* runPve(olds.target, "provision", true, access4.deleteAccessRole({ roleid: olds.roleid }));
|
|
4669
|
+
})
|
|
4670
|
+
})));
|
|
4508
4671
|
|
|
4509
4672
|
// src/proxmox/user.ts
|
|
4510
4673
|
import { Resource as Resource27 } from "alchemy";
|
|
4674
|
+
import { isResolved as isResolved13 } from "alchemy/Diff";
|
|
4511
4675
|
import * as Provider27 from "alchemy/Provider";
|
|
4512
|
-
import * as
|
|
4513
|
-
|
|
4514
|
-
|
|
4676
|
+
import * as access5 from "@distilled.cloud/proxmox/access";
|
|
4677
|
+
import * as Effect53 from "effect/Effect";
|
|
4678
|
+
|
|
4679
|
+
// src/proxmox/user-wire.ts
|
|
4680
|
+
var USER_CREATE = "pve:POST /access/users";
|
|
4681
|
+
var USER_UPDATE = "pve:PUT /access/users/{userid}";
|
|
4515
4682
|
var int2 = (value) => {
|
|
4516
4683
|
const parsed = typeof value === "number" ? value : Number(value ?? 0);
|
|
4517
4684
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
4518
4685
|
};
|
|
4519
4686
|
var groupSet = (value) => {
|
|
4520
4687
|
const raw = Array.isArray(value) ? value : typeof value === "string" ? value.split(",") : [];
|
|
4521
|
-
const names2 = raw.map((group) =>
|
|
4688
|
+
const names2 = raw.map((group) => text(group).trim()).filter((group) => group !== "");
|
|
4522
4689
|
return [...new Set(names2)].sort();
|
|
4523
4690
|
};
|
|
4524
4691
|
var tokenNames = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? Object.keys(value).sort() : [];
|
|
@@ -4531,30 +4698,79 @@ var shape8 = (props) => ({
|
|
|
4531
4698
|
groups: groupSet(props.groups).join(","),
|
|
4532
4699
|
lastname: props.lastname ?? ""
|
|
4533
4700
|
});
|
|
4534
|
-
var
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4701
|
+
var createForm7 = (props) => ({
|
|
4702
|
+
...shape8(props),
|
|
4703
|
+
userid: props.userid
|
|
4704
|
+
});
|
|
4705
|
+
var updateForm4 = (props) => ({
|
|
4706
|
+
...shape8(props),
|
|
4707
|
+
userid: props.userid
|
|
4708
|
+
});
|
|
4709
|
+
var attributesOf6 = (live, props) => ({
|
|
4710
|
+
comment: text(live.comment),
|
|
4711
|
+
email: text(live.email),
|
|
4712
|
+
enable: bool(live.enable, true),
|
|
4713
|
+
expire: int2(live.expire),
|
|
4714
|
+
firstname: text(live.firstname),
|
|
4715
|
+
groups: groupSet(live.groups),
|
|
4716
|
+
lastname: text(live.lastname),
|
|
4717
|
+
tokens: tokenNames(live.tokens),
|
|
4718
|
+
userid: props.userid
|
|
4719
|
+
});
|
|
4720
|
+
var matches6 = (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(",");
|
|
4721
|
+
|
|
4722
|
+
// src/proxmox/user.ts
|
|
4723
|
+
var ProxmoxUser = Resource27("Proxmox.User");
|
|
4724
|
+
var readUser = (props) => readOrUnreadable(runPve(props.target, "read", false, access5.getAccessUser(props))).pipe(Effect53.map((live) => live === UNREADABLE ? UNREADABLE : attributesOf6(live, props)), Effect53.orElseSucceed(() => {
|
|
4725
|
+
return;
|
|
4726
|
+
}));
|
|
4727
|
+
var dropUnreadable5 = (live) => live === UNREADABLE ? undefined : live;
|
|
4728
|
+
var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser, Effect53.succeed(ProxmoxUser.Provider.of({
|
|
4729
|
+
list: () => Effect53.succeed([]),
|
|
4730
|
+
read: Effect53.fn(function* ({ olds }) {
|
|
4731
|
+
return dropUnreadable5(yield* readUser(olds));
|
|
4545
4732
|
}),
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
|
|
4733
|
+
diff: Effect53.fn(function* ({ news, output }) {
|
|
4734
|
+
if (!isResolved13(news))
|
|
4735
|
+
return;
|
|
4736
|
+
yield* guardWrite2(USER_CREATE, createForm7(news), output === undefined);
|
|
4737
|
+
yield* guardWrite2(USER_UPDATE, updateForm4(news), false);
|
|
4738
|
+
if (output === undefined)
|
|
4739
|
+
return;
|
|
4740
|
+
const live = yield* readUser(news);
|
|
4741
|
+
if (live === UNREADABLE) {
|
|
4742
|
+
yield* unreadableWarning("Proxmox.User", news.userid);
|
|
4743
|
+
return { action: "noop" };
|
|
4744
|
+
}
|
|
4745
|
+
if (live === undefined) {
|
|
4746
|
+
yield* guardWrite2(USER_CREATE, createForm7(news), true);
|
|
4747
|
+
return { action: "update" };
|
|
4748
|
+
}
|
|
4749
|
+
return matches6(live, news) ? { action: "noop" } : { action: "update" };
|
|
4750
|
+
}),
|
|
4751
|
+
reconcile: Effect53.fn(function* ({ news }) {
|
|
4752
|
+
const before = dropUnreadable5(yield* readUser(news));
|
|
4753
|
+
yield* guardWrite2(USER_CREATE, createForm7(news), before === undefined);
|
|
4754
|
+
yield* guardWrite2(USER_UPDATE, updateForm4(news), false);
|
|
4755
|
+
if (before === undefined) {
|
|
4756
|
+
yield* runPve(news.target, "provision", true, access5.createAccessUser(createForm7(news)));
|
|
4757
|
+
} else if (!matches6(before, news)) {
|
|
4758
|
+
yield* runPve(news.target, "provision", true, access5.putAccessUser(updateForm4(news)));
|
|
4759
|
+
}
|
|
4760
|
+
const after = dropUnreadable5(yield* readUser(news));
|
|
4761
|
+
if (after === undefined) {
|
|
4762
|
+
return yield* Effect53.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."));
|
|
4763
|
+
}
|
|
4764
|
+
return after;
|
|
4765
|
+
}),
|
|
4766
|
+
delete: Effect53.fn(function* ({ olds }) {
|
|
4767
|
+
yield* runPve(olds.target, "provision", true, access5.deleteAccessUser({ userid: olds.userid }));
|
|
4768
|
+
})
|
|
4769
|
+
})));
|
|
4554
4770
|
|
|
4555
4771
|
// src/proxmox/provision-declare.ts
|
|
4556
|
-
var declareProvisionBaseline = (id, target, names2 = {}, options = {}) =>
|
|
4557
|
-
const baseline = yield*
|
|
4772
|
+
var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect54.gen(function* () {
|
|
4773
|
+
const baseline = yield* Effect54.try({
|
|
4558
4774
|
catch: (cause) => cause instanceof Error ? cause : new Error(String(cause)),
|
|
4559
4775
|
try: () => provisionBaseline(names2)
|
|
4560
4776
|
});
|
|
@@ -4568,13 +4784,13 @@ var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect
|
|
|
4568
4784
|
target
|
|
4569
4785
|
}));
|
|
4570
4786
|
const group = yield* owned(ProxmoxGroup(`${id}-mint-group`, { ...baseline.group, target }));
|
|
4571
|
-
const users = yield*
|
|
4787
|
+
const users = yield* Effect54.all(baseline.users.map((user) => owned(ProxmoxUser(`${id}-${user.lane}-user`, {
|
|
4572
4788
|
comment: user.comment,
|
|
4573
4789
|
groups: [...user.groups],
|
|
4574
4790
|
target,
|
|
4575
4791
|
userid: user.userid
|
|
4576
4792
|
}))));
|
|
4577
|
-
const grants = yield*
|
|
4793
|
+
const grants = yield* Effect54.all(baseline.grants.map((grant) => owned(ProxmoxAcl(`${id}-${grant.lane}-grant`, {
|
|
4578
4794
|
path: "/",
|
|
4579
4795
|
propagate: true,
|
|
4580
4796
|
roleid: grant.roleid,
|
|
@@ -4587,9 +4803,9 @@ var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect
|
|
|
4587
4803
|
// src/proxmox/qemu.ts
|
|
4588
4804
|
import { Resource as Resource28 } from "alchemy";
|
|
4589
4805
|
import * as Provider28 from "alchemy/Provider";
|
|
4590
|
-
import * as
|
|
4806
|
+
import * as Effect55 from "effect/Effect";
|
|
4591
4807
|
var ProxmoxVm = Resource28("Proxmox.Vm");
|
|
4592
|
-
var
|
|
4808
|
+
var createForm8 = (props) => ({ ...shape9(props), vmid: String(props.vmid) });
|
|
4593
4809
|
var shape9 = (props) => ({
|
|
4594
4810
|
cores: String(props.cores ?? 1),
|
|
4595
4811
|
memory: String(props.memory ?? 512),
|
|
@@ -4598,7 +4814,7 @@ var shape9 = (props) => ({
|
|
|
4598
4814
|
sockets: String(props.sockets ?? 1),
|
|
4599
4815
|
...props.net0 === undefined ? {} : { net0: props.net0 }
|
|
4600
4816
|
});
|
|
4601
|
-
var
|
|
4817
|
+
var handlers20 = pveHandlers({
|
|
4602
4818
|
attributes: (live, props) => ({
|
|
4603
4819
|
cores: num(live["cores"], 1),
|
|
4604
4820
|
memory: num(live["memory"], 512),
|
|
@@ -4609,7 +4825,7 @@ var handlers24 = pveHandlers({
|
|
|
4609
4825
|
vmid: props.vmid
|
|
4610
4826
|
}),
|
|
4611
4827
|
collection: (props) => `nodes/${props.node}/qemu`,
|
|
4612
|
-
createForm:
|
|
4828
|
+
createForm: createForm8,
|
|
4613
4829
|
endpoint: {
|
|
4614
4830
|
create: "pve:POST /nodes/{node}/qemu",
|
|
4615
4831
|
update: "pve:PUT /nodes/{node}/qemu/{vmid}/config"
|
|
@@ -4618,11 +4834,11 @@ var handlers24 = pveHandlers({
|
|
|
4618
4834
|
path: (props) => `nodes/${props.node}/qemu/${String(props.vmid)}/config`,
|
|
4619
4835
|
updateForm: shape9
|
|
4620
4836
|
});
|
|
4621
|
-
var ProxmoxVmProvider = () => Provider28.effect(ProxmoxVm,
|
|
4837
|
+
var ProxmoxVmProvider = () => Provider28.effect(ProxmoxVm, Effect55.succeed(ProxmoxVm.Provider.of(handlers20)));
|
|
4622
4838
|
// src/proxmox/replication-job.ts
|
|
4623
4839
|
import { Resource as Resource29 } from "alchemy";
|
|
4624
4840
|
import * as Provider29 from "alchemy/Provider";
|
|
4625
|
-
import * as
|
|
4841
|
+
import * as Effect56 from "effect/Effect";
|
|
4626
4842
|
|
|
4627
4843
|
// src/proxmox/replication-job-form.ts
|
|
4628
4844
|
var DEFAULT_SCHEDULE = "*/15";
|
|
@@ -4652,7 +4868,7 @@ var rateOf = (value) => {
|
|
|
4652
4868
|
const parsed = Number.parseFloat(text(value));
|
|
4653
4869
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
4654
4870
|
};
|
|
4655
|
-
var
|
|
4871
|
+
var handlers21 = pveHandlers({
|
|
4656
4872
|
attributes: (live, props) => ({
|
|
4657
4873
|
comment: text(live["comment"], ""),
|
|
4658
4874
|
disable: bool(live["disable"], false),
|
|
@@ -4676,15 +4892,15 @@ var handlers25 = pveHandlers({
|
|
|
4676
4892
|
path: (props) => `cluster/replication/${jobId(props)}`,
|
|
4677
4893
|
updateForm: updateBody5
|
|
4678
4894
|
});
|
|
4679
|
-
var ProxmoxReplicationJobProvider = () => Provider29.effect(ProxmoxReplicationJob,
|
|
4895
|
+
var ProxmoxReplicationJobProvider = () => Provider29.effect(ProxmoxReplicationJob, Effect56.succeed(ProxmoxReplicationJob.Provider.of(handlers21)));
|
|
4680
4896
|
// src/proxmox/sdn-apply.ts
|
|
4681
4897
|
import { Resource as Resource30 } from "alchemy";
|
|
4682
|
-
import { isResolved as
|
|
4898
|
+
import { isResolved as isResolved14 } from "alchemy/Diff";
|
|
4683
4899
|
import * as Provider30 from "alchemy/Provider";
|
|
4684
|
-
import * as
|
|
4900
|
+
import * as Effect58 from "effect/Effect";
|
|
4685
4901
|
|
|
4686
4902
|
// src/proxmox/sdn-apply-read.ts
|
|
4687
|
-
import * as
|
|
4903
|
+
import * as Effect57 from "effect/Effect";
|
|
4688
4904
|
var MARKED = [
|
|
4689
4905
|
"cluster/sdn/zones",
|
|
4690
4906
|
"cluster/sdn/vnets",
|
|
@@ -4693,7 +4909,7 @@ var MARKED = [
|
|
|
4693
4909
|
"cluster/sdn/route-maps"
|
|
4694
4910
|
];
|
|
4695
4911
|
var subsystemAbsent = (error) => error instanceof PveError && error.status === 501;
|
|
4696
|
-
var markedPending = (target) =>
|
|
4912
|
+
var markedPending = (target) => Effect57.all(MARKED.map((collection2) => pve(target, "read", "GET", `${collection2}?pending=1`).pipe(Effect57.map((rows) => (rows ?? []).filter((row) => row.state !== undefined).length), Effect57.catchIf(subsystemAbsent, () => Effect57.succeed(0)))), { concurrency: "unbounded" }).pipe(Effect57.map((counts) => counts.reduce((total, count2) => total + count2, 0)));
|
|
4697
4913
|
var fabricStaged = (staged, running) => {
|
|
4698
4914
|
if (staged === undefined && running === undefined)
|
|
4699
4915
|
return 0;
|
|
@@ -4701,9 +4917,9 @@ var fabricStaged = (staged, running) => {
|
|
|
4701
4917
|
return "inconsistent";
|
|
4702
4918
|
return staged === running ? 0 : 1;
|
|
4703
4919
|
};
|
|
4704
|
-
var fabricPending = (target) =>
|
|
4920
|
+
var fabricPending = (target) => Effect57.all(["pending", "running"].map((view) => pve(target, "read", "GET", `cluster/sdn/fabrics/all?${view}=1`).pipe(Effect57.map(canonical3), Effect57.catchIf(subsystemAbsent, () => Effect57.succeed(undefined)))), { concurrency: "unbounded" }).pipe(Effect57.flatMap(([staged, running]) => {
|
|
4705
4921
|
const verdict = fabricStaged(staged, running);
|
|
4706
|
-
return verdict === "inconsistent" ?
|
|
4922
|
+
return verdict === "inconsistent" ? Effect57.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.")) : Effect57.succeed(verdict);
|
|
4707
4923
|
}));
|
|
4708
4924
|
var canonical3 = (value) => JSON.stringify(sortDeep(value));
|
|
4709
4925
|
var sortDeep = (value) => {
|
|
@@ -4716,23 +4932,23 @@ var sortDeep = (value) => {
|
|
|
4716
4932
|
}
|
|
4717
4933
|
return value;
|
|
4718
4934
|
};
|
|
4719
|
-
var sdnPendingCount = (target) =>
|
|
4935
|
+
var sdnPendingCount = (target) => Effect57.all([markedPending(target), fabricPending(target)], { concurrency: "unbounded" }).pipe(Effect57.map(([marked, fabric]) => marked + fabric));
|
|
4720
4936
|
|
|
4721
4937
|
// src/proxmox/sdn-apply.ts
|
|
4722
4938
|
var ProxmoxSdnApply = Resource30("Proxmox.SdnApply");
|
|
4723
|
-
var read3 = (props) => sdnPendingCount(props.target).pipe(
|
|
4724
|
-
var ProxmoxSdnApplyProvider = () => Provider30.effect(ProxmoxSdnApply,
|
|
4725
|
-
list: () =>
|
|
4726
|
-
read:
|
|
4939
|
+
var read3 = (props) => sdnPendingCount(props.target).pipe(Effect58.map((pending) => ({ pending })));
|
|
4940
|
+
var ProxmoxSdnApplyProvider = () => Provider30.effect(ProxmoxSdnApply, Effect58.succeed(ProxmoxSdnApply.Provider.of({
|
|
4941
|
+
list: () => Effect58.succeed([]),
|
|
4942
|
+
read: Effect58.fn(function* ({ olds }) {
|
|
4727
4943
|
return yield* read3(olds);
|
|
4728
4944
|
}),
|
|
4729
|
-
diff:
|
|
4730
|
-
if (!
|
|
4945
|
+
diff: Effect58.fn(function* ({ news }) {
|
|
4946
|
+
if (!isResolved14(news))
|
|
4731
4947
|
return;
|
|
4732
4948
|
const { pending } = yield* read3(news);
|
|
4733
4949
|
return pending === 0 ? { action: "noop" } : { action: "update" };
|
|
4734
4950
|
}),
|
|
4735
|
-
reconcile:
|
|
4951
|
+
reconcile: Effect58.fn(function* ({ news }) {
|
|
4736
4952
|
const staged = yield* read3(news);
|
|
4737
4953
|
if (staged.pending === 0)
|
|
4738
4954
|
return staged;
|
|
@@ -4740,16 +4956,16 @@ var ProxmoxSdnApplyProvider = () => Provider30.effect(ProxmoxSdnApply, Effect57.
|
|
|
4740
4956
|
yield* pve(news.target, "provision", "PUT", "cluster/sdn");
|
|
4741
4957
|
const after = yield* read3(news);
|
|
4742
4958
|
if (after.pending !== 0) {
|
|
4743
|
-
return yield*
|
|
4959
|
+
return yield* Effect58.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."));
|
|
4744
4960
|
}
|
|
4745
4961
|
return after;
|
|
4746
4962
|
}),
|
|
4747
|
-
delete: () =>
|
|
4963
|
+
delete: () => Effect58.void
|
|
4748
4964
|
})));
|
|
4749
4965
|
// src/proxmox/sdn-subnet.ts
|
|
4750
4966
|
import { Resource as Resource31 } from "alchemy";
|
|
4751
4967
|
import * as Provider31 from "alchemy/Provider";
|
|
4752
|
-
import * as
|
|
4968
|
+
import * as Effect59 from "effect/Effect";
|
|
4753
4969
|
|
|
4754
4970
|
// src/proxmox/sdn-subnet-form.ts
|
|
4755
4971
|
var subnetId = (props) => `${props.zone}-${props.cidr.replace("/", "-")}`;
|
|
@@ -4768,19 +4984,19 @@ var body5 = (props) => ({
|
|
|
4768
4984
|
vnet: props.vnet,
|
|
4769
4985
|
...Object.fromEntries(Object.entries(optional3(props)).filter((entry) => entry[1] !== undefined))
|
|
4770
4986
|
});
|
|
4771
|
-
var
|
|
4987
|
+
var createForm9 = (props) => ({
|
|
4772
4988
|
...body5(props),
|
|
4773
4989
|
subnet: props.cidr,
|
|
4774
4990
|
type: "subnet"
|
|
4775
4991
|
});
|
|
4776
|
-
var
|
|
4992
|
+
var updateForm5 = (props) => {
|
|
4777
4993
|
const clear = Object.entries(optional3(props)).filter(([, value]) => value === undefined).map(([key]) => key);
|
|
4778
4994
|
return withClears(body5(props), clear);
|
|
4779
4995
|
};
|
|
4780
4996
|
|
|
4781
4997
|
// src/proxmox/sdn-subnet.ts
|
|
4782
4998
|
var ProxmoxSdnSubnet = Resource31("Proxmox.SdnSubnet");
|
|
4783
|
-
var
|
|
4999
|
+
var handlers22 = pveHandlers({
|
|
4784
5000
|
attributes: (live, props) => {
|
|
4785
5001
|
const liveVnet = text(live["vnet"]);
|
|
4786
5002
|
if (liveVnet !== "" && liveVnet !== props.vnet)
|
|
@@ -4800,20 +5016,20 @@ var handlers26 = pveHandlers({
|
|
|
4800
5016
|
};
|
|
4801
5017
|
},
|
|
4802
5018
|
collection: (props) => `cluster/sdn/vnets/${props.vnet}/subnets`,
|
|
4803
|
-
createForm:
|
|
5019
|
+
createForm: createForm9,
|
|
4804
5020
|
endpoint: {
|
|
4805
5021
|
create: "pve:POST /cluster/sdn/vnets/{vnet}/subnets",
|
|
4806
5022
|
update: "pve:PUT /cluster/sdn/vnets/{vnet}/subnets/{subnet}"
|
|
4807
5023
|
},
|
|
4808
5024
|
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),
|
|
4809
5025
|
path: (props) => `cluster/sdn/vnets/${props.vnet}/subnets/${subnetId(props)}`,
|
|
4810
|
-
updateForm:
|
|
5026
|
+
updateForm: updateForm5
|
|
4811
5027
|
});
|
|
4812
|
-
var ProxmoxSdnSubnetProvider = () => Provider31.effect(ProxmoxSdnSubnet,
|
|
5028
|
+
var ProxmoxSdnSubnetProvider = () => Provider31.effect(ProxmoxSdnSubnet, Effect59.succeed(ProxmoxSdnSubnet.Provider.of(handlers22)));
|
|
4813
5029
|
// src/proxmox/sdn-vnet.ts
|
|
4814
5030
|
import { Resource as Resource32 } from "alchemy";
|
|
4815
5031
|
import * as Provider32 from "alchemy/Provider";
|
|
4816
|
-
import * as
|
|
5032
|
+
import * as Effect60 from "effect/Effect";
|
|
4817
5033
|
var ProxmoxSdnVnet = Resource32("Proxmox.SdnVnet");
|
|
4818
5034
|
var tagOf = (value) => typeof value === "number" ? value : undefined;
|
|
4819
5035
|
var shape11 = (props) => {
|
|
@@ -4826,14 +5042,14 @@ var shape11 = (props) => {
|
|
|
4826
5042
|
...props.tag === undefined ? {} : { tag: String(props.tag) }
|
|
4827
5043
|
};
|
|
4828
5044
|
};
|
|
4829
|
-
var
|
|
5045
|
+
var updateForm6 = (props) => {
|
|
4830
5046
|
const unset2 = [
|
|
4831
5047
|
...(props.alias ?? "") === "" ? ["alias"] : [],
|
|
4832
5048
|
...props.tag === undefined ? ["tag"] : []
|
|
4833
5049
|
];
|
|
4834
5050
|
return withClears(shape11(props), unset2);
|
|
4835
5051
|
};
|
|
4836
|
-
var
|
|
5052
|
+
var handlers23 = pveHandlers({
|
|
4837
5053
|
readRole: "provision",
|
|
4838
5054
|
attributes: (live, props) => ({
|
|
4839
5055
|
alias: typeof live["alias"] === "string" ? live["alias"] : "",
|
|
@@ -4849,13 +5065,13 @@ var handlers27 = pveHandlers({
|
|
|
4849
5065
|
endpoint: { create: "pve:POST /cluster/sdn/vnets", update: "pve:PUT /cluster/sdn/vnets/{vnet}" },
|
|
4850
5066
|
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),
|
|
4851
5067
|
path: (props) => `cluster/sdn/vnets/${props.vnet}`,
|
|
4852
|
-
updateForm:
|
|
5068
|
+
updateForm: updateForm6
|
|
4853
5069
|
});
|
|
4854
|
-
var ProxmoxSdnVnetProvider = () => Provider32.effect(ProxmoxSdnVnet,
|
|
5070
|
+
var ProxmoxSdnVnetProvider = () => Provider32.effect(ProxmoxSdnVnet, Effect60.succeed(ProxmoxSdnVnet.Provider.of(handlers23)));
|
|
4855
5071
|
// src/proxmox/sdn-zone.ts
|
|
4856
5072
|
import { Resource as Resource33 } from "alchemy";
|
|
4857
5073
|
import * as Provider33 from "alchemy/Provider";
|
|
4858
|
-
import * as
|
|
5074
|
+
import * as Effect61 from "effect/Effect";
|
|
4859
5075
|
var ProxmoxSdnZone = Resource33("Proxmox.SdnZone");
|
|
4860
5076
|
var sameIpam = (live, declared2) => (live === "" ? "pve" : live) === (declared2 === undefined || declared2 === "" ? "pve" : declared2);
|
|
4861
5077
|
var body6 = (props) => ({
|
|
@@ -4876,7 +5092,7 @@ var clearable = {
|
|
|
4876
5092
|
vlan: ["dns", "dnszone", "ipam", "mtu", "nodes"],
|
|
4877
5093
|
vxlan: ["dns", "dnszone", "ipam", "mtu", "nodes"]
|
|
4878
5094
|
};
|
|
4879
|
-
var
|
|
5095
|
+
var handlers24 = pveHandlers({
|
|
4880
5096
|
readRole: "provision",
|
|
4881
5097
|
attributes: (live, props) => {
|
|
4882
5098
|
const liveType = text(live["type"]);
|
|
@@ -4908,15 +5124,15 @@ var handlers28 = pveHandlers({
|
|
|
4908
5124
|
return withClears(fields, clear);
|
|
4909
5125
|
}
|
|
4910
5126
|
});
|
|
4911
|
-
var ProxmoxSdnZoneProvider = () => Provider33.effect(ProxmoxSdnZone,
|
|
5127
|
+
var ProxmoxSdnZoneProvider = () => Provider33.effect(ProxmoxSdnZone, Effect61.succeed(ProxmoxSdnZone.Provider.of(handlers24)));
|
|
4912
5128
|
// src/proxmox/storage.ts
|
|
4913
5129
|
import { Resource as Resource34 } from "alchemy";
|
|
4914
5130
|
import * as Provider34 from "alchemy/Provider";
|
|
4915
|
-
import * as
|
|
5131
|
+
import * as Effect62 from "effect/Effect";
|
|
4916
5132
|
var ProxmoxStorage = Resource34("Proxmox.Storage", {
|
|
4917
5133
|
defaultRemovalPolicy: "retain"
|
|
4918
5134
|
});
|
|
4919
|
-
var
|
|
5135
|
+
var str2 = (value, fallback = "") => typeof value === "string" ? value : fallback;
|
|
4920
5136
|
var field6 = (name, value) => value === undefined ? {} : { [name]: value };
|
|
4921
5137
|
var locatorForm = (locator) => ({ ...locator });
|
|
4922
5138
|
var tokens = (value) => value.split(",").map((part) => part.trim()).filter((part) => part !== "").sort().join(",");
|
|
@@ -4932,19 +5148,19 @@ var mutable2 = (props) => ({
|
|
|
4932
5148
|
...field6("prune-backups", props["prune-backups"]),
|
|
4933
5149
|
...field6("shared", flag(props.shared))
|
|
4934
5150
|
});
|
|
4935
|
-
var
|
|
5151
|
+
var handlers25 = pveHandlers({
|
|
4936
5152
|
readRole: "provision",
|
|
4937
5153
|
attributes: (live, props) => ({
|
|
4938
|
-
comment:
|
|
4939
|
-
content:
|
|
5154
|
+
comment: str2(live["comment"]),
|
|
5155
|
+
content: str2(live["content"]),
|
|
4940
5156
|
disable: bool(live["disable"]),
|
|
4941
5157
|
maxfiles: typeof live["maxfiles"] === "number" ? live["maxfiles"] : -1,
|
|
4942
|
-
nodes:
|
|
4943
|
-
preallocation:
|
|
4944
|
-
"prune-backups":
|
|
5158
|
+
nodes: str2(live["nodes"]),
|
|
5159
|
+
preallocation: str2(live["preallocation"]),
|
|
5160
|
+
"prune-backups": str2(live["prune-backups"]),
|
|
4945
5161
|
shared: bool(live["shared"]),
|
|
4946
5162
|
storage: props.storage,
|
|
4947
|
-
type:
|
|
5163
|
+
type: str2(live["type"], props.type)
|
|
4948
5164
|
}),
|
|
4949
5165
|
collection: () => "storage",
|
|
4950
5166
|
createForm: (props) => ({
|
|
@@ -4958,17 +5174,17 @@ var handlers29 = pveHandlers({
|
|
|
4958
5174
|
path: (props) => `storage/${props.storage}`,
|
|
4959
5175
|
updateForm: mutable2
|
|
4960
5176
|
});
|
|
4961
|
-
var ProxmoxStorageProvider = () => Provider34.effect(ProxmoxStorage,
|
|
5177
|
+
var ProxmoxStorageProvider = () => Provider34.effect(ProxmoxStorage, Effect62.succeed(ProxmoxStorage.Provider.of(handlers25)));
|
|
4962
5178
|
// src/proxmox/zfs-pool.ts
|
|
4963
5179
|
import { Resource as Resource35 } from "alchemy";
|
|
4964
5180
|
import * as Provider35 from "alchemy/Provider";
|
|
4965
|
-
import * as
|
|
5181
|
+
import * as Effect64 from "effect/Effect";
|
|
4966
5182
|
|
|
4967
5183
|
// src/proxmox/zfs-pool-write.ts
|
|
4968
|
-
import * as
|
|
5184
|
+
import * as Effect63 from "effect/Effect";
|
|
4969
5185
|
var ZFS_POOL_ENDPOINT = { create: "pve:POST /nodes/{node}/disks/zfs" };
|
|
4970
5186
|
var zfsPoolEndpoint = (props) => props.devices === undefined || props.raidlevel === undefined ? {} : ZFS_POOL_ENDPOINT;
|
|
4971
|
-
var
|
|
5187
|
+
var createForm10 = (props) => ({
|
|
4972
5188
|
...props.devices === undefined ? {} : { devices: props.devices.join(",") },
|
|
4973
5189
|
name: props.name,
|
|
4974
5190
|
...props.raidlevel === undefined ? {} : { raidlevel: props.raidlevel },
|
|
@@ -4977,24 +5193,24 @@ var createForm7 = (props) => ({
|
|
|
4977
5193
|
...props["draid-config"] === undefined ? {} : { "draid-config": props["draid-config"] }
|
|
4978
5194
|
});
|
|
4979
5195
|
var SETTLE_ATTEMPTS = 30;
|
|
4980
|
-
var createPool = (props, collection2, path2, read4) =>
|
|
5196
|
+
var createPool = (props, collection2, path2, read4) => Effect63.gen(function* () {
|
|
4981
5197
|
const existing = yield* read4(props);
|
|
4982
5198
|
if (existing !== undefined)
|
|
4983
5199
|
return existing;
|
|
4984
5200
|
if (props.devices === undefined || props.raidlevel === undefined) {
|
|
4985
|
-
return yield*
|
|
5201
|
+
return yield* Effect63.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."));
|
|
4986
5202
|
}
|
|
4987
|
-
const upid = yield* pve(props.target, "provision", "POST", collection2,
|
|
4988
|
-
const settle4 = (attempts) =>
|
|
5203
|
+
const upid = yield* pve(props.target, "provision", "POST", collection2, createForm10(props));
|
|
5204
|
+
const settle4 = (attempts) => Effect63.gen(function* () {
|
|
4989
5205
|
const live = yield* read4(props);
|
|
4990
5206
|
if (live !== undefined || attempts <= 0)
|
|
4991
5207
|
return live;
|
|
4992
|
-
yield*
|
|
5208
|
+
yield* Effect63.sleep("2 seconds");
|
|
4993
5209
|
return yield* settle4(attempts - 1);
|
|
4994
5210
|
});
|
|
4995
5211
|
const after = yield* settle4(SETTLE_ATTEMPTS);
|
|
4996
5212
|
if (after === undefined) {
|
|
4997
|
-
return yield*
|
|
5213
|
+
return yield* Effect63.die(new Error(`${path2}: 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."));
|
|
4998
5214
|
}
|
|
4999
5215
|
return after;
|
|
5000
5216
|
});
|
|
@@ -5019,7 +5235,7 @@ var spec2 = {
|
|
|
5019
5235
|
};
|
|
5020
5236
|
},
|
|
5021
5237
|
collection: (props) => `nodes/${props.node}/disks/zfs`,
|
|
5022
|
-
createForm:
|
|
5238
|
+
createForm: createForm10,
|
|
5023
5239
|
endpoint: zfsPoolEndpoint,
|
|
5024
5240
|
matches: () => true,
|
|
5025
5241
|
path: (props) => `nodes/${props.node}/disks/zfs/${props.name}`
|
|
@@ -5029,15 +5245,15 @@ var spec2 = {
|
|
|
5029
5245
|
var ProxmoxZfsPool = Resource35("Proxmox.ZfsPool", {
|
|
5030
5246
|
defaultRemovalPolicy: "retain"
|
|
5031
5247
|
});
|
|
5032
|
-
var
|
|
5033
|
-
var
|
|
5248
|
+
var ops3 = pveOperations(spec2);
|
|
5249
|
+
var handlers26 = {
|
|
5034
5250
|
...pveHandlers(spec2),
|
|
5035
|
-
delete:
|
|
5251
|
+
delete: Effect64.fn(function* ({ olds }) {
|
|
5036
5252
|
yield* destroyPool(olds, spec2.path(olds));
|
|
5037
5253
|
}),
|
|
5038
|
-
reconcile: ({ news }) => createPool(news, spec2.collection(news), spec2.path(news),
|
|
5254
|
+
reconcile: ({ news }) => createPool(news, spec2.collection(news), spec2.path(news), ops3.read)
|
|
5039
5255
|
};
|
|
5040
|
-
var ProxmoxZfsPoolProvider = () => Provider35.effect(ProxmoxZfsPool,
|
|
5256
|
+
var ProxmoxZfsPoolProvider = () => Provider35.effect(ProxmoxZfsPool, Effect64.succeed(ProxmoxZfsPool.Provider.of(handlers26)));
|
|
5041
5257
|
export {
|
|
5042
5258
|
PROVISION_DEFAULTS,
|
|
5043
5259
|
PROVISION_PRIVILEGES,
|
|
@@ -5114,5 +5330,5 @@ export {
|
|
|
5114
5330
|
violations
|
|
5115
5331
|
};
|
|
5116
5332
|
|
|
5117
|
-
//# debugId=
|
|
5333
|
+
//# debugId=71A5FC632D2B3D7F64756E2164756E21
|
|
5118
5334
|
//# sourceMappingURL=index.js.map
|