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