@homeflare/alchemy 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/caddy/admin-guard.d.ts +1 -1
- package/dist/caddy/index.js.map +2 -2
- package/dist/cloudflare/client.d.ts +1 -1
- package/dist/cloudflare/index.js.map +2 -2
- package/dist/cloudflare/lock-rules.d.ts +1 -1
- package/dist/forgejo/branch-protection-form.d.ts +1 -1
- package/dist/forgejo/branch-protection.d.ts +1 -1
- package/dist/forgejo/index.js.map +10 -10
- package/dist/forgejo/org-actions-secrets.d.ts +1 -1
- package/dist/forgejo/org-label.d.ts +1 -1
- package/dist/forgejo/org-team.d.ts +1 -1
- package/dist/forgejo/repo-webhook.d.ts +1 -1
- package/dist/forgejo/repository.d.ts +2 -2
- package/dist/forgejo/repository.d.ts.map +1 -1
- package/dist/forgejo/resource.d.ts +2 -2
- package/dist/forgejo/team-member.d.ts +1 -1
- package/dist/openbao/auth-role.d.ts +1 -1
- package/dist/openbao/bao-http.d.ts +4 -4
- package/dist/openbao/cloudflare-permission-groups.d.ts +1 -1
- package/dist/openbao/cloudflare-role-form.d.ts +2 -2
- package/dist/openbao/cloudflare-role-form.d.ts.map +1 -1
- package/dist/openbao/cloudflare-roles-expand.d.ts +1 -1
- package/dist/openbao/digest.d.ts +1 -1
- package/dist/openbao/forgejo-bootstrap-parse.d.ts +2 -2
- package/dist/openbao/forgejo-bootstrap-parse.d.ts.map +1 -1
- package/dist/openbao/forgejo-bootstrap.d.ts +1 -1
- package/dist/openbao/index.js.map +14 -14
- package/dist/openbao/mount.d.ts +1 -1
- package/dist/openbao/pki-role.d.ts +1 -1
- package/dist/openbao/policy.d.ts +2 -2
- package/dist/openbao/proxmox-role-form.d.ts +2 -2
- package/dist/openbao/proxmox-role-form.d.ts.map +1 -1
- package/dist/openbao/proxmox-role.d.ts +10 -10
- package/dist/openbao/ssh-role.d.ts +3 -3
- package/dist/proxmox/acl.d.ts +2 -2
- package/dist/proxmox/acl.d.ts.map +1 -1
- package/dist/proxmox/alertmanager-body.d.ts +54 -0
- package/dist/proxmox/alertmanager-body.d.ts.map +1 -0
- package/dist/proxmox/api-token-form.d.ts +2 -2
- package/dist/proxmox/api-token.d.ts +11 -11
- package/dist/proxmox/ceph-daemon-form.d.ts +3 -3
- package/dist/proxmox/ceph-daemon.d.ts +19 -19
- package/dist/proxmox/ceph-daemon.d.ts.map +1 -1
- package/dist/proxmox/ceph-flag.d.ts +5 -5
- package/dist/proxmox/ceph-fs-wire.d.ts +5 -5
- package/dist/proxmox/ceph-fs.d.ts +16 -16
- package/dist/proxmox/ceph-osd-tree.d.ts +16 -16
- package/dist/proxmox/ceph-osd.d.ts +5 -5
- package/dist/proxmox/ceph-pool-form.d.ts +13 -13
- package/dist/proxmox/ceph-pool-form.d.ts.map +1 -1
- package/dist/proxmox/ceph-pool.d.ts +3 -3
- package/dist/proxmox/client.d.ts +2 -2
- package/dist/proxmox/credential-literals.d.ts +36 -0
- package/dist/proxmox/credential-literals.d.ts.map +1 -0
- package/dist/proxmox/credentials.d.ts +8 -8
- package/dist/proxmox/credentials.d.ts.map +1 -1
- package/dist/proxmox/fake-pbs-notify.d.ts +23 -0
- package/dist/proxmox/fake-pbs-notify.d.ts.map +1 -0
- package/dist/proxmox/fake-pve.d.ts +5 -0
- package/dist/proxmox/fake-pve.d.ts.map +1 -1
- package/dist/proxmox/firewall-alias-form.d.ts +1 -1
- package/dist/proxmox/firewall-alias.d.ts +1 -1
- package/dist/proxmox/generated/pbs.d.ts +279 -1
- package/dist/proxmox/generated/pbs.d.ts.map +1 -1
- package/dist/proxmox/generated/pve.d.ts +58 -1
- package/dist/proxmox/generated/pve.d.ts.map +1 -1
- package/dist/proxmox/group.d.ts +8 -8
- package/dist/proxmox/ha-resource.d.ts +3 -2
- package/dist/proxmox/ha-resource.d.ts.map +1 -1
- package/dist/proxmox/ha-rule-form.d.ts +5 -5
- package/dist/proxmox/ha-rule.d.ts +3 -3
- package/dist/proxmox/index.d.ts +14 -0
- package/dist/proxmox/index.d.ts.map +1 -1
- package/dist/proxmox/index.js +935 -223
- package/dist/proxmox/index.js.map +69 -56
- package/dist/proxmox/lease-cache.d.ts +4 -4
- package/dist/proxmox/lxc-harness.d.ts +1 -1
- package/dist/proxmox/lxc-props.d.ts +2 -2
- package/dist/proxmox/lxc.d.ts +3 -3
- package/dist/proxmox/members.d.ts +3 -3
- package/dist/proxmox/network-apply-read.d.ts +5 -5
- package/dist/proxmox/network-apply.d.ts +9 -9
- package/dist/proxmox/node-network-form.d.ts +6 -6
- package/dist/proxmox/node-network.d.ts +17 -17
- package/dist/proxmox/node-network.d.ts.map +1 -1
- package/dist/proxmox/notification-matcher-form.d.ts +100 -0
- package/dist/proxmox/notification-matcher-form.d.ts.map +1 -0
- package/dist/proxmox/notification-matcher.d.ts +46 -0
- package/dist/proxmox/notification-matcher.d.ts.map +1 -0
- package/dist/proxmox/notification-target.d.ts +3 -2
- package/dist/proxmox/notification-target.d.ts.map +1 -1
- package/dist/proxmox/pbs-datastore-guard.d.ts +1 -1
- package/dist/proxmox/pbs-datastore.d.ts +10 -10
- package/dist/proxmox/pbs-notification-matcher.d.ts +53 -0
- package/dist/proxmox/pbs-notification-matcher.d.ts.map +1 -0
- package/dist/proxmox/pbs-notification-target-form.d.ts +63 -0
- package/dist/proxmox/pbs-notification-target-form.d.ts.map +1 -0
- package/dist/proxmox/pbs-notification-target-lifecycle.d.ts +100 -0
- package/dist/proxmox/pbs-notification-target-lifecycle.d.ts.map +1 -0
- package/dist/proxmox/pbs-notification-target-wire.d.ts +30 -0
- package/dist/proxmox/pbs-notification-target-wire.d.ts.map +1 -0
- package/dist/proxmox/pbs-notification-target.d.ts +123 -0
- package/dist/proxmox/pbs-notification-target.d.ts.map +1 -0
- package/dist/proxmox/pbs-prune-job.d.ts +2 -2
- package/dist/proxmox/pbs-sync-job.d.ts +4 -4
- package/dist/proxmox/pbs-verify-job.d.ts +1 -1
- package/dist/proxmox/pool.d.ts +4 -4
- package/dist/proxmox/provision-baseline.d.ts +74 -0
- package/dist/proxmox/provision-baseline.d.ts.map +1 -0
- package/dist/proxmox/provision-bootstrap.d.ts +33 -0
- package/dist/proxmox/provision-bootstrap.d.ts.map +1 -0
- package/dist/proxmox/provision-cli-fake.d.ts +38 -0
- package/dist/proxmox/provision-cli-fake.d.ts.map +1 -0
- package/dist/proxmox/provision-declare.d.ts +25 -0
- package/dist/proxmox/provision-declare.d.ts.map +1 -0
- package/dist/proxmox/replication-job.d.ts +9 -8
- package/dist/proxmox/replication-job.d.ts.map +1 -1
- package/dist/proxmox/resource.d.ts +6 -5
- package/dist/proxmox/resource.d.ts.map +1 -1
- package/dist/proxmox/sdn-apply-read.d.ts +7 -7
- package/dist/proxmox/sdn-apply.d.ts +1 -1
- package/dist/proxmox/sdn-subnet-form.d.ts +1 -1
- package/dist/proxmox/sdn-subnet.d.ts +5 -5
- package/dist/proxmox/sdn-vnet.d.ts +5 -6
- package/dist/proxmox/sdn-vnet.d.ts.map +1 -1
- package/dist/proxmox/storage.d.ts +4 -3
- package/dist/proxmox/storage.d.ts.map +1 -1
- package/dist/proxmox/user.d.ts +3 -2
- package/dist/proxmox/user.d.ts.map +1 -1
- package/dist/proxmox/values.d.ts +1 -1
- package/dist/proxmox/write-only.d.ts +35 -0
- package/dist/proxmox/write-only.d.ts.map +1 -0
- package/dist/proxmox/zfs-pool-write.d.ts +1 -1
- package/dist/proxmox/zfs-pool.d.ts +5 -5
- package/dist/talos/credentials.d.ts +1 -1
- package/dist/talos/credentials.d.ts.map +1 -1
- package/dist/talos/index.js.map +1 -1
- package/dist/verify/fake-engine.d.ts +5 -0
- package/dist/verify/fake-engine.d.ts.map +1 -1
- package/docs/adopt-verify.md +21 -5
- package/docs/adopted-deploys.md +9 -1
- package/docs/ownership.md +26 -1
- package/docs/pbs-alertmanager-body.md +73 -0
- package/docs/pbs-notifications.md +163 -0
- package/docs/provision-baseline.md +106 -0
- package/docs/proxmox-lxc-adopt.md +2 -2
- package/docs/proxmox-lxc.md +5 -4
- package/docs/proxmox.md +9 -0
- package/package.json +1 -1
- package/src/caddy/admin-guard.ts +1 -1
- package/src/caddy/local-admin.ts +1 -1
- package/src/cloudflare/client.ts +1 -1
- package/src/cloudflare/lock-rules.ts +1 -1
- package/src/forgejo/branch-protection-form.ts +1 -1
- package/src/forgejo/branch-protection.ts +1 -1
- package/src/forgejo/client.ts +1 -1
- package/src/forgejo/org-actions-secrets.ts +1 -1
- package/src/forgejo/org-label.ts +1 -1
- package/src/forgejo/org-team.ts +1 -1
- package/src/forgejo/repo-webhook.ts +1 -1
- package/src/forgejo/repository.ts +2 -2
- package/src/forgejo/resource.ts +2 -2
- package/src/forgejo/team-member.ts +1 -1
- package/src/openbao/README.md +4 -4
- package/src/openbao/auth-role.ts +1 -1
- package/src/openbao/bao-http.test.ts +2 -2
- package/src/openbao/bao-http.ts +6 -6
- package/src/openbao/bao-status.test.ts +1 -1
- package/src/openbao/bao-status.ts +1 -1
- package/src/openbao/cloudflare-group-scope.test.ts +1 -1
- package/src/openbao/cloudflare-permission-groups.test.ts +1 -1
- package/src/openbao/cloudflare-permission-groups.ts +3 -3
- package/src/openbao/cloudflare-policy.ts +1 -1
- package/src/openbao/cloudflare-role-form.ts +2 -2
- package/src/openbao/cloudflare-roles-expand.ts +1 -1
- package/src/openbao/digest.ts +1 -1
- package/src/openbao/forgejo-bootstrap-parse.test.ts +3 -3
- package/src/openbao/forgejo-bootstrap-parse.ts +2 -2
- package/src/openbao/forgejo-bootstrap.ts +4 -4
- package/src/openbao/kubernetes-role-form.test.ts +5 -5
- package/src/openbao/mount.ts +1 -1
- package/src/openbao/pki-role.ts +1 -1
- package/src/openbao/policy-wire.test.ts +3 -3
- package/src/openbao/policy.ts +2 -2
- package/src/openbao/proxmox-role-form.ts +5 -5
- package/src/openbao/proxmox-role.ts +11 -11
- package/src/openbao/ssh-role.ts +3 -3
- package/src/proxmox/acl.ts +4 -4
- package/src/proxmox/adopt-noop.test.ts +6 -6
- package/src/proxmox/alertmanager-body.ts +125 -0
- package/src/proxmox/api-token-form.ts +4 -4
- package/src/proxmox/api-token.ts +13 -13
- package/src/proxmox/ceph-daemon-form.ts +3 -3
- package/src/proxmox/ceph-daemon.ts +20 -20
- package/src/proxmox/ceph-flag.ts +7 -7
- package/src/proxmox/ceph-fs-wire.ts +7 -7
- package/src/proxmox/ceph-fs.ts +18 -18
- package/src/proxmox/ceph-osd-tree.ts +16 -16
- package/src/proxmox/ceph-osd.ts +6 -6
- package/src/proxmox/ceph-pool-adopt.test.ts +14 -14
- package/src/proxmox/ceph-pool-form.ts +13 -13
- package/src/proxmox/ceph-pool.ts +6 -6
- package/src/proxmox/client.test.ts +3 -3
- package/src/proxmox/client.ts +3 -3
- package/src/proxmox/credential-literals.test.ts +148 -0
- package/src/proxmox/credential-literals.ts +74 -0
- package/src/proxmox/credentials.test.ts +6 -8
- package/src/proxmox/credentials.ts +8 -8
- package/src/proxmox/fake-pbs-notify.ts +108 -0
- package/src/proxmox/fake-pve.ts +12 -2
- package/src/proxmox/firewall-alias-form.ts +1 -1
- package/src/proxmox/firewall-alias.ts +1 -1
- package/src/proxmox/generated/pbs.ts +330 -1
- package/src/proxmox/generated/pve.ts +63 -1
- package/src/proxmox/group.ts +10 -10
- package/src/proxmox/ha-resource.ts +3 -2
- package/src/proxmox/ha-rule-form.ts +5 -5
- package/src/proxmox/ha-rule.ts +3 -3
- package/src/proxmox/index.test.ts +10 -0
- package/src/proxmox/index.ts +32 -0
- package/src/proxmox/lease-cache.test.ts +19 -19
- package/src/proxmox/lease-cache.ts +4 -4
- package/src/proxmox/lxc-adopt.test.ts +3 -3
- package/src/proxmox/lxc-create-form.ts +1 -1
- package/src/proxmox/lxc-guards.test.ts +4 -4
- package/src/proxmox/lxc-harness.ts +3 -3
- package/src/proxmox/lxc-judge.test.ts +5 -5
- package/src/proxmox/lxc-lifecycle.ts +1 -1
- package/src/proxmox/lxc-props.ts +2 -2
- package/src/proxmox/lxc-read.test.ts +3 -3
- package/src/proxmox/lxc-strict-adopt.test.ts +1 -1
- package/src/proxmox/lxc-volume.test.ts +9 -9
- package/src/proxmox/lxc-volume.ts +1 -1
- package/src/proxmox/lxc-wire.ts +1 -1
- package/src/proxmox/lxc.ts +3 -3
- package/src/proxmox/members.test.ts +1 -1
- package/src/proxmox/members.ts +3 -3
- package/src/proxmox/metric-server-otel.ts +2 -2
- package/src/proxmox/network-apply-read.ts +10 -10
- package/src/proxmox/network-apply.ts +10 -10
- package/src/proxmox/node-network-form.ts +7 -7
- package/src/proxmox/node-network.ts +18 -18
- package/src/proxmox/notification-matcher-form.ts +221 -0
- package/src/proxmox/notification-matcher.test.ts +234 -0
- package/src/proxmox/notification-matcher.ts +76 -0
- package/src/proxmox/notification-target.ts +3 -2
- package/src/proxmox/pbs-datastore-guard.ts +1 -1
- package/src/proxmox/pbs-datastore.ts +10 -10
- package/src/proxmox/pbs-notification-matcher.ts +81 -0
- package/src/proxmox/pbs-notification-target-drift.test.ts +83 -0
- package/src/proxmox/pbs-notification-target-form.ts +229 -0
- package/src/proxmox/pbs-notification-target-lifecycle.ts +177 -0
- package/src/proxmox/pbs-notification-target-state.test.ts +191 -0
- package/src/proxmox/pbs-notification-target-wire.ts +112 -0
- package/src/proxmox/pbs-notification-target.test.ts +226 -0
- package/src/proxmox/pbs-notification-target.ts +139 -0
- package/src/proxmox/pbs-notifications-stack.test.ts +94 -0
- package/src/proxmox/pbs-prune-job.ts +2 -2
- package/src/proxmox/pbs-sync-job.ts +4 -4
- package/src/proxmox/pbs-verify-job.ts +1 -1
- package/src/proxmox/pool.test.ts +2 -2
- package/src/proxmox/pool.ts +4 -4
- package/src/proxmox/provision-baseline.test.ts +87 -0
- package/src/proxmox/provision-baseline.ts +162 -0
- package/src/proxmox/provision-bootstrap.test.ts +132 -0
- package/src/proxmox/provision-bootstrap.ts +135 -0
- package/src/proxmox/provision-cli-fake.ts +217 -0
- package/src/proxmox/provision-declare.test.ts +111 -0
- package/src/proxmox/provision-declare.ts +103 -0
- package/src/proxmox/replication-job.ts +9 -8
- package/src/proxmox/resource.ts +9 -9
- package/src/proxmox/sdn-apply-read.test.ts +16 -16
- package/src/proxmox/sdn-apply-read.ts +7 -7
- package/src/proxmox/sdn-apply.ts +3 -3
- package/src/proxmox/sdn-subnet-form.ts +1 -1
- package/src/proxmox/sdn-subnet.ts +5 -5
- package/src/proxmox/sdn-vnet.ts +5 -6
- package/src/proxmox/storage.ts +4 -3
- package/src/proxmox/user.ts +3 -2
- package/src/proxmox/values.test.ts +2 -2
- package/src/proxmox/values.ts +1 -1
- package/src/proxmox/write-only.test.ts +75 -0
- package/src/proxmox/write-only.ts +106 -0
- package/src/proxmox/zfs-pool-write.ts +1 -1
- package/src/proxmox/zfs-pool.ts +6 -6
- package/src/talos/credentials.ts +3 -3
- package/src/talos/values.test.ts +9 -9
- package/src/verify/cli.test.ts +1 -1
- package/src/verify/fake-engine.ts +8 -1
package/dist/proxmox/index.js
CHANGED
|
@@ -230,7 +230,7 @@ var formToSend = (matches, live, props, form) => matches(live, props) || Object.
|
|
|
230
230
|
|
|
231
231
|
// src/proxmox/resource.ts
|
|
232
232
|
var pveOperations = (spec) => {
|
|
233
|
-
const read = (props) => pve(props.target, spec.readRole ?? "read", "GET", spec.path(props)).pipe(Effect5.map((data) => data
|
|
233
|
+
const read = (props) => pve(props.target, spec.readRole ?? "read", "GET", spec.path(props)).pipe(Effect5.map((data) => data == null ? undefined : spec.attributes(data, props)), Effect5.orElseSucceed(() => {
|
|
234
234
|
return;
|
|
235
235
|
}));
|
|
236
236
|
return {
|
|
@@ -336,6 +336,56 @@ var ProxmoxAclProvider = () => Provider.effect(ProxmoxAcl, Effect6.succeed(Proxm
|
|
|
336
336
|
yield* pve(olds.target, "provision", "PUT", "access/acl", removal);
|
|
337
337
|
})
|
|
338
338
|
})));
|
|
339
|
+
// src/proxmox/alertmanager-body.ts
|
|
340
|
+
var literal = (label, value) => {
|
|
341
|
+
if (/[{}\\]/.test(value) || value === "") {
|
|
342
|
+
throw new Error(`alertmanagerAlertBody: ${label} must be non-empty and hold no '{', '}' or '\\'`);
|
|
343
|
+
}
|
|
344
|
+
return JSON.stringify(value);
|
|
345
|
+
};
|
|
346
|
+
var absoluteUrl = (value) => {
|
|
347
|
+
const text = literal("generatorURL", value);
|
|
348
|
+
let protocol = "";
|
|
349
|
+
try {
|
|
350
|
+
protocol = new URL(value).protocol;
|
|
351
|
+
} catch {}
|
|
352
|
+
if (protocol !== "https:" && protocol !== "http:") {
|
|
353
|
+
throw new Error("alertmanagerAlertBody: generatorURL must be an absolute http(s) URL");
|
|
354
|
+
}
|
|
355
|
+
return text;
|
|
356
|
+
};
|
|
357
|
+
var FIELD_LABELS = [
|
|
358
|
+
["job_type", "type"],
|
|
359
|
+
["job_id", "job-id"],
|
|
360
|
+
["datastore", "datastore"],
|
|
361
|
+
["hostname", "hostname"]
|
|
362
|
+
];
|
|
363
|
+
var alertmanagerAlertBody = (options = {}) => {
|
|
364
|
+
const alertname = literal("alertname", options.alertname ?? "PbsNotification");
|
|
365
|
+
const source = literal("source", options.source ?? "pbs");
|
|
366
|
+
const labels = FIELD_LABELS.map(([label, field]) => ` {{#if fields.${field}}}"${label}": {{ json fields.${field} }},{{/if}}`);
|
|
367
|
+
const link = options.generatorURL === undefined ? "" : `,
|
|
368
|
+
"generatorURL": ${absoluteUrl(options.generatorURL)}`;
|
|
369
|
+
return [
|
|
370
|
+
"[",
|
|
371
|
+
" {",
|
|
372
|
+
' "labels": {',
|
|
373
|
+
...labels,
|
|
374
|
+
' "severity": {{ json severity }},',
|
|
375
|
+
` "alertname": ${alertname},`,
|
|
376
|
+
` "source": ${source}`,
|
|
377
|
+
" },",
|
|
378
|
+
' "annotations": {',
|
|
379
|
+
' "summary": {{ json title }},',
|
|
380
|
+
' "description": {{ json message }},',
|
|
381
|
+
' "timestamp": "{{ timestamp }}"',
|
|
382
|
+
` }${link}`,
|
|
383
|
+
" }",
|
|
384
|
+
"]",
|
|
385
|
+
""
|
|
386
|
+
].join(`
|
|
387
|
+
`);
|
|
388
|
+
};
|
|
339
389
|
// src/proxmox/api-token.ts
|
|
340
390
|
import { Resource as Resource2 } from "alchemy";
|
|
341
391
|
import * as Provider2 from "alchemy/Provider";
|
|
@@ -422,7 +472,7 @@ var handlers = {
|
|
|
422
472
|
reconcile: Effect7.fn(function* ({ news }) {
|
|
423
473
|
const live = yield* ops2.read(news);
|
|
424
474
|
if (live === undefined) {
|
|
425
|
-
return yield* Effect7.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-
|
|
475
|
+
return yield* Effect7.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."));
|
|
426
476
|
}
|
|
427
477
|
return yield* ops2.reconcile(news);
|
|
428
478
|
})
|
|
@@ -2184,11 +2234,101 @@ var handlers13 = pveHandlers({
|
|
|
2184
2234
|
updateForm: updateBody2
|
|
2185
2235
|
});
|
|
2186
2236
|
var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork, Effect34.succeed(ProxmoxNodeNetwork.Provider.of(handlers13)));
|
|
2187
|
-
// src/proxmox/notification-
|
|
2237
|
+
// src/proxmox/notification-matcher.ts
|
|
2188
2238
|
import { Resource as Resource17 } from "alchemy";
|
|
2189
2239
|
import * as Provider17 from "alchemy/Provider";
|
|
2190
2240
|
import * as Effect35 from "effect/Effect";
|
|
2191
2241
|
|
|
2242
|
+
// src/proxmox/notification-matcher-form.ts
|
|
2243
|
+
var list = (value) => Array.isArray(value) ? value.map((item) => String(item)) : [];
|
|
2244
|
+
var items = (values, canonical) => values.map((item) => canonical(item.trim())).filter((item) => item !== "");
|
|
2245
|
+
var severity = (item) => item.split(",").map((part) => part.trim()).filter((part) => part !== "").sort().join(",");
|
|
2246
|
+
var field4 = (item) => {
|
|
2247
|
+
if (!item.startsWith("exact:"))
|
|
2248
|
+
return item;
|
|
2249
|
+
const at = item.indexOf("=");
|
|
2250
|
+
if (at < 0)
|
|
2251
|
+
return item;
|
|
2252
|
+
const values = item.slice(at + 1).split(",");
|
|
2253
|
+
return `${item.slice(0, at)}=${values.map((value) => value.trim()).join(",")}`;
|
|
2254
|
+
};
|
|
2255
|
+
var same3 = (item) => item;
|
|
2256
|
+
var canonicalRules = (fields) => ({
|
|
2257
|
+
"match-calendar": items(fields["match-calendar"] ?? [], same3).sort(),
|
|
2258
|
+
"match-field": items(fields["match-field"] ?? [], field4).sort(),
|
|
2259
|
+
"match-severity": items(fields["match-severity"] ?? [], severity).sort(),
|
|
2260
|
+
targets: items(fields.targets, same3).sort()
|
|
2261
|
+
});
|
|
2262
|
+
var matcherAttributes = (live, name) => ({
|
|
2263
|
+
comment: text(live["comment"]),
|
|
2264
|
+
disable: bool(live["disable"]),
|
|
2265
|
+
"invert-match": bool(live["invert-match"]),
|
|
2266
|
+
"match-calendar": items(list(live["match-calendar"]), same3).sort(),
|
|
2267
|
+
"match-field": items(list(live["match-field"]), field4).sort(),
|
|
2268
|
+
"match-severity": items(list(live["match-severity"]), severity).sort(),
|
|
2269
|
+
mode: text(live["mode"], "all") || "all",
|
|
2270
|
+
name,
|
|
2271
|
+
origin: text(live["origin"]),
|
|
2272
|
+
targets: items(list(live["target"]), same3).sort()
|
|
2273
|
+
});
|
|
2274
|
+
var equal = (a, b) => a.length === b.length && a.every((item, index) => item === b[index]);
|
|
2275
|
+
var matcherMatches = (attributes, fields) => {
|
|
2276
|
+
const rules = canonicalRules(fields);
|
|
2277
|
+
return attributes.disable === (fields.disable === true) && attributes["invert-match"] === (fields["invert-match"] === true) && attributes.mode === (fields.mode ?? "all") && equal(attributes.targets, rules.targets) && equal(attributes["match-severity"], rules["match-severity"]) && equal(attributes["match-field"], rules["match-field"]) && equal(attributes["match-calendar"], rules["match-calendar"]) && (fields.comment === undefined || attributes.comment === fields.comment);
|
|
2278
|
+
};
|
|
2279
|
+
var always = (fields) => ({
|
|
2280
|
+
disable: fields.disable === true ? "1" : "0",
|
|
2281
|
+
"invert-match": fields["invert-match"] === true ? "1" : "0",
|
|
2282
|
+
mode: fields.mode ?? "all"
|
|
2283
|
+
});
|
|
2284
|
+
var lists = (fields) => {
|
|
2285
|
+
const rules = canonicalRules(fields);
|
|
2286
|
+
const out = {};
|
|
2287
|
+
if (rules["match-calendar"].length > 0)
|
|
2288
|
+
out["match-calendar"] = rules["match-calendar"];
|
|
2289
|
+
if (rules["match-field"].length > 0)
|
|
2290
|
+
out["match-field"] = rules["match-field"];
|
|
2291
|
+
if (rules["match-severity"].length > 0)
|
|
2292
|
+
out["match-severity"] = rules["match-severity"];
|
|
2293
|
+
if (rules.targets.length > 0)
|
|
2294
|
+
out.target = rules.targets;
|
|
2295
|
+
return out;
|
|
2296
|
+
};
|
|
2297
|
+
var matcherCreateForm = (fields) => ({
|
|
2298
|
+
...always(fields),
|
|
2299
|
+
...lists(fields),
|
|
2300
|
+
...fields.comment === undefined || fields.comment === "" ? {} : { comment: fields.comment },
|
|
2301
|
+
name: fields.name
|
|
2302
|
+
});
|
|
2303
|
+
var matcherUpdateForm = (fields) => {
|
|
2304
|
+
const present = lists(fields);
|
|
2305
|
+
const cleared = ["match-calendar", "match-field", "match-severity", "target"].filter((key) => present[key] === undefined);
|
|
2306
|
+
if (fields.comment === "")
|
|
2307
|
+
cleared.push("comment");
|
|
2308
|
+
return {
|
|
2309
|
+
...always(fields),
|
|
2310
|
+
...present,
|
|
2311
|
+
...fields.comment === undefined || fields.comment === "" ? {} : { comment: fields.comment },
|
|
2312
|
+
...cleared.length === 0 ? {} : { delete: cleared }
|
|
2313
|
+
};
|
|
2314
|
+
};
|
|
2315
|
+
|
|
2316
|
+
// src/proxmox/notification-matcher.ts
|
|
2317
|
+
var ProxmoxNotificationMatcher = Resource17("Proxmox.NotificationMatcher");
|
|
2318
|
+
var handlers14 = pveHandlers({
|
|
2319
|
+
attributes: (live, props) => matcherAttributes(live, props.name),
|
|
2320
|
+
collection: () => "cluster/notifications/matchers",
|
|
2321
|
+
createForm: matcherCreateForm,
|
|
2322
|
+
matches: matcherMatches,
|
|
2323
|
+
path: (props) => `cluster/notifications/matchers/${props.name}`,
|
|
2324
|
+
updateForm: matcherUpdateForm
|
|
2325
|
+
});
|
|
2326
|
+
var ProxmoxNotificationMatcherProvider = () => Provider17.effect(ProxmoxNotificationMatcher, Effect35.succeed(ProxmoxNotificationMatcher.Provider.of(handlers14)));
|
|
2327
|
+
// src/proxmox/notification-target.ts
|
|
2328
|
+
import { Resource as Resource18 } from "alchemy";
|
|
2329
|
+
import * as Provider18 from "alchemy/Provider";
|
|
2330
|
+
import * as Effect36 from "effect/Effect";
|
|
2331
|
+
|
|
2192
2332
|
// src/proxmox/notification-target-form.ts
|
|
2193
2333
|
var REPEATED_KEY = "\x00";
|
|
2194
2334
|
var portOf = (value) => {
|
|
@@ -2217,8 +2357,8 @@ var shape4 = (props) => ({
|
|
|
2217
2357
|
});
|
|
2218
2358
|
|
|
2219
2359
|
// src/proxmox/notification-target.ts
|
|
2220
|
-
var ProxmoxNotificationTarget =
|
|
2221
|
-
var
|
|
2360
|
+
var ProxmoxNotificationTarget = Resource18("Proxmox.NotificationTarget");
|
|
2361
|
+
var handlers15 = pveHandlers({
|
|
2222
2362
|
attributes: (live, props) => ({
|
|
2223
2363
|
author: text(live["author"]),
|
|
2224
2364
|
body: text(live["body"]),
|
|
@@ -2243,15 +2383,15 @@ var handlers14 = pveHandlers({
|
|
|
2243
2383
|
path: (props) => `cluster/notifications/endpoints/${props.type}/${props.name}`,
|
|
2244
2384
|
updateForm: shape4
|
|
2245
2385
|
});
|
|
2246
|
-
var ProxmoxNotificationTargetProvider = () =>
|
|
2386
|
+
var ProxmoxNotificationTargetProvider = () => Provider18.effect(ProxmoxNotificationTarget, Effect36.succeed(ProxmoxNotificationTarget.Provider.of(handlers15)));
|
|
2247
2387
|
// src/proxmox/pbs-datastore.ts
|
|
2248
|
-
import { Resource as
|
|
2388
|
+
import { Resource as Resource19 } from "alchemy";
|
|
2249
2389
|
import { isResolved as isResolved8 } from "alchemy/Diff";
|
|
2250
|
-
import * as
|
|
2251
|
-
import * as
|
|
2390
|
+
import * as Provider19 from "alchemy/Provider";
|
|
2391
|
+
import * as Effect39 from "effect/Effect";
|
|
2252
2392
|
|
|
2253
2393
|
// src/proxmox/pbs-datastore-form.ts
|
|
2254
|
-
import * as
|
|
2394
|
+
import * as Effect37 from "effect/Effect";
|
|
2255
2395
|
var attributes = (live, props) => ({
|
|
2256
2396
|
backend: propertyString(live["backend"]),
|
|
2257
2397
|
comment: text(live["comment"]),
|
|
@@ -2273,8 +2413,8 @@ var attributes = (live, props) => ({
|
|
|
2273
2413
|
tuning: propertyString(live["tuning"]),
|
|
2274
2414
|
"verify-new": bool(live["verify-new"])
|
|
2275
2415
|
});
|
|
2276
|
-
var
|
|
2277
|
-
var matches = (live, props) =>
|
|
2416
|
+
var same4 = (declared, live) => declared === undefined || declared === live;
|
|
2417
|
+
var matches = (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"]);
|
|
2278
2418
|
var set2 = (key, value) => value === undefined ? {} : { [key]: value };
|
|
2279
2419
|
var count = (value) => value === undefined ? undefined : String(value);
|
|
2280
2420
|
var mutable = (props) => ({
|
|
@@ -2303,36 +2443,36 @@ var createForm4 = (props) => ({
|
|
|
2303
2443
|
});
|
|
2304
2444
|
var updateForm = (props) => mutable(props);
|
|
2305
2445
|
var object2 = (props) => `config/datastore/${props.name}`;
|
|
2306
|
-
var readOne = (props) => pve(props.target, "read", "GET", object2(props)).pipe(
|
|
2446
|
+
var readOne = (props) => pve(props.target, "read", "GET", object2(props)).pipe(Effect37.map((live) => live === undefined || typeof live["name"] !== "string" ? undefined : attributes(live, props)), Effect37.orElseSucceed(() => {
|
|
2307
2447
|
return;
|
|
2308
2448
|
}));
|
|
2309
2449
|
|
|
2310
2450
|
// src/proxmox/pbs-datastore-guard.ts
|
|
2311
|
-
import * as
|
|
2451
|
+
import * as Effect38 from "effect/Effect";
|
|
2312
2452
|
var samePath = (left, right) => {
|
|
2313
2453
|
const trim = (value) => value.length > 1 ? value.replace(/\/+$/, "") : value;
|
|
2314
2454
|
return trim(left) === trim(right);
|
|
2315
2455
|
};
|
|
2316
|
-
var guardPath = (live, props) => live.path === "" || samePath(live.path, props.path) ?
|
|
2317
|
-
var guardBackend = (live, props) => props.backend === undefined || propertyString(props.backend) === live.backend ?
|
|
2318
|
-
var settle3 = (read3, done) =>
|
|
2456
|
+
var guardPath = (live, props) => live.path === "" || samePath(live.path, props.path) ? Effect38.void : Effect38.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."));
|
|
2457
|
+
var guardBackend = (live, props) => props.backend === undefined || propertyString(props.backend) === live.backend ? Effect38.void : Effect38.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."));
|
|
2458
|
+
var settle3 = (read3, done) => Effect38.gen(function* () {
|
|
2319
2459
|
for (let attempt = 0;attempt < 60; attempt += 1) {
|
|
2320
2460
|
const live = yield* read3;
|
|
2321
2461
|
if (done(live))
|
|
2322
2462
|
return live;
|
|
2323
|
-
yield*
|
|
2463
|
+
yield* Effect38.sleep("2 seconds");
|
|
2324
2464
|
}
|
|
2325
2465
|
return yield* read3;
|
|
2326
2466
|
});
|
|
2327
2467
|
|
|
2328
2468
|
// src/proxmox/pbs-datastore.ts
|
|
2329
|
-
var PbsDatastore =
|
|
2469
|
+
var PbsDatastore = Resource19("Pbs.Datastore", {
|
|
2330
2470
|
defaultRemovalPolicy: "retain"
|
|
2331
2471
|
});
|
|
2332
|
-
var PbsDatastoreProvider = () =>
|
|
2333
|
-
list: () =>
|
|
2472
|
+
var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore, Effect39.succeed(PbsDatastore.Provider.of({
|
|
2473
|
+
list: () => Effect39.succeed([]),
|
|
2334
2474
|
read: ({ olds }) => readOne(olds),
|
|
2335
|
-
diff: ({ news, output }) =>
|
|
2475
|
+
diff: ({ news, output }) => Effect39.gen(function* () {
|
|
2336
2476
|
if (output === undefined || !isResolved8(news))
|
|
2337
2477
|
return;
|
|
2338
2478
|
const live = yield* readOne(news);
|
|
@@ -2342,7 +2482,7 @@ var PbsDatastoreProvider = () => Provider18.effect(PbsDatastore, Effect38.succee
|
|
|
2342
2482
|
yield* guardBackend(live, news);
|
|
2343
2483
|
return matches(live, news) ? { action: "noop" } : { action: "update" };
|
|
2344
2484
|
}),
|
|
2345
|
-
reconcile:
|
|
2485
|
+
reconcile: Effect39.fn(function* ({ news }) {
|
|
2346
2486
|
const live = yield* readOne(news);
|
|
2347
2487
|
let upid;
|
|
2348
2488
|
if (live === undefined) {
|
|
@@ -2357,22 +2497,373 @@ var PbsDatastoreProvider = () => Provider18.effect(PbsDatastore, Effect38.succee
|
|
|
2357
2497
|
}
|
|
2358
2498
|
const after = yield* settle3(readOne(news), (row) => row !== undefined && matches(row, news));
|
|
2359
2499
|
if (after === undefined || !matches(after, news)) {
|
|
2360
|
-
return yield*
|
|
2500
|
+
return yield* Effect39.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."));
|
|
2361
2501
|
}
|
|
2362
2502
|
return after;
|
|
2363
2503
|
}),
|
|
2364
|
-
delete:
|
|
2504
|
+
delete: Effect39.fn(function* ({ olds }) {
|
|
2365
2505
|
yield* pve(olds.target, "provision", "DELETE", object2(olds));
|
|
2366
2506
|
const left = yield* settle3(readOne(olds), (row) => row === undefined);
|
|
2367
2507
|
if (left !== undefined) {
|
|
2368
|
-
return yield*
|
|
2508
|
+
return yield* Effect39.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>\`.`));
|
|
2369
2509
|
}
|
|
2370
2510
|
})
|
|
2371
2511
|
})));
|
|
2512
|
+
// src/proxmox/pbs-notification-matcher.ts
|
|
2513
|
+
import { Resource as Resource20 } from "alchemy";
|
|
2514
|
+
import * as Provider20 from "alchemy/Provider";
|
|
2515
|
+
import * as Effect40 from "effect/Effect";
|
|
2516
|
+
var PbsNotificationMatcher = Resource20("Pbs.NotificationMatcher");
|
|
2517
|
+
var handlers16 = pveHandlers({
|
|
2518
|
+
attributes: (live, props) => matcherAttributes(live, props.name),
|
|
2519
|
+
collection: () => "config/notifications/matchers",
|
|
2520
|
+
createForm: matcherCreateForm,
|
|
2521
|
+
matches: matcherMatches,
|
|
2522
|
+
path: (props) => `config/notifications/matchers/${props.name}`,
|
|
2523
|
+
updateForm: matcherUpdateForm
|
|
2524
|
+
});
|
|
2525
|
+
var PbsNotificationMatcherProvider = () => Provider20.effect(PbsNotificationMatcher, Effect40.succeed(PbsNotificationMatcher.Provider.of(handlers16)));
|
|
2526
|
+
// src/proxmox/pbs-notification-target.ts
|
|
2527
|
+
import { Resource as Resource21 } from "alchemy";
|
|
2528
|
+
import * as Provider21 from "alchemy/Provider";
|
|
2529
|
+
import * as Effect42 from "effect/Effect";
|
|
2530
|
+
|
|
2531
|
+
// src/proxmox/pbs-notification-target-lifecycle.ts
|
|
2532
|
+
import { isResolved as isResolved9 } from "alchemy/Diff";
|
|
2533
|
+
import * as Effect41 from "effect/Effect";
|
|
2534
|
+
|
|
2535
|
+
// src/proxmox/credential-literals.ts
|
|
2536
|
+
var READS_SECRET = /\{\{[^}]*\bsecrets\./;
|
|
2537
|
+
var CREDENTIAL_WORD = /(^|[-_])(api[-_]?key|apikey|key|token|secret|password|passwd|pwd|pass|auth|signature|sig|code)($|[-_])/i;
|
|
2538
|
+
var CREDENTIAL_HEADER = /^(proxy-authorization|authorization|cookie)$/i;
|
|
2539
|
+
var isCredentialName = (name) => CREDENTIAL_HEADER.test(name) || CREDENTIAL_WORD.test(name);
|
|
2540
|
+
var literal2 = (value) => value !== "" && !READS_SECRET.test(value);
|
|
2541
|
+
var literalCredentialHeaders = (header) => Object.entries(header ?? {}).filter(([name, value]) => typeof value === "string" && isCredentialName(name)).filter(([, value]) => literal2(value)).map(([name]) => name).sort();
|
|
2542
|
+
var literalCredentialsInUrl = (url) => {
|
|
2543
|
+
if (url === undefined)
|
|
2544
|
+
return [];
|
|
2545
|
+
const out = [];
|
|
2546
|
+
const authority = /^[a-z][a-z0-9+.-]*:\/\/([^/?#]*)/i.exec(url)?.[1] ?? "";
|
|
2547
|
+
const at = authority.lastIndexOf("@");
|
|
2548
|
+
if (at >= 0) {
|
|
2549
|
+
const userinfo = authority.slice(0, at);
|
|
2550
|
+
const colon = userinfo.indexOf(":");
|
|
2551
|
+
if (colon >= 0 && literal2(userinfo.slice(colon + 1)))
|
|
2552
|
+
out.push("userinfo");
|
|
2553
|
+
}
|
|
2554
|
+
const params = url.replace(/^[^?#]*/, "");
|
|
2555
|
+
for (const pair of params.split(/[?&#]/)) {
|
|
2556
|
+
const eq = pair.indexOf("=");
|
|
2557
|
+
const name = eq < 0 ? pair : pair.slice(0, eq);
|
|
2558
|
+
if (eq >= 0 && isCredentialName(name) && literal2(pair.slice(eq + 1)))
|
|
2559
|
+
out.push(`?${name}`);
|
|
2560
|
+
}
|
|
2561
|
+
return out;
|
|
2562
|
+
};
|
|
2563
|
+
|
|
2564
|
+
// src/proxmox/pbs-notification-target-wire.ts
|
|
2565
|
+
import { Buffer as Buffer2 } from "node:buffer";
|
|
2566
|
+
|
|
2567
|
+
// src/proxmox/write-only.ts
|
|
2568
|
+
import { Buffer } from "node:buffer";
|
|
2569
|
+
import { randomBytes, scryptSync, timingSafeEqual } from "node:crypto";
|
|
2570
|
+
var resolveAll = (refs, env = process.env) => {
|
|
2571
|
+
const values = {};
|
|
2572
|
+
const missing = [];
|
|
2573
|
+
for (const [key, ref] of Object.entries(refs)) {
|
|
2574
|
+
const value = env[ref.fromEnv];
|
|
2575
|
+
if (value === undefined || value === "")
|
|
2576
|
+
missing.push(ref.fromEnv);
|
|
2577
|
+
else
|
|
2578
|
+
values[key] = value;
|
|
2579
|
+
}
|
|
2580
|
+
return { missing: [...new Set(missing)].sort(), values };
|
|
2581
|
+
};
|
|
2582
|
+
var COST = { N: 16384, maxmem: 64 * 1024 * 1024, p: 1, r: 8 };
|
|
2583
|
+
var PREFIX = "scrypt";
|
|
2584
|
+
var canonical = (values) => JSON.stringify(Object.keys(values).sort().map((key) => [key, values[key]]));
|
|
2585
|
+
var digest = (salt, values) => scryptSync(canonical(values), salt, 32, COST);
|
|
2586
|
+
var seal = (values, salt) => {
|
|
2587
|
+
if (Object.keys(values).length === 0)
|
|
2588
|
+
return "";
|
|
2589
|
+
const bytes = salt === undefined ? randomBytes(16) : Buffer.from(salt, "utf8");
|
|
2590
|
+
return `${PREFIX}:${bytes.toString("base64url")}:${digest(bytes, values).toString("base64url")}`;
|
|
2591
|
+
};
|
|
2592
|
+
var sealMatches = (sealed, values) => {
|
|
2593
|
+
if (sealed === "")
|
|
2594
|
+
return Object.keys(values).length === 0;
|
|
2595
|
+
const [prefix, salt, stored, extra] = sealed.split(":");
|
|
2596
|
+
if (prefix !== PREFIX || salt === undefined || stored === undefined || extra !== undefined) {
|
|
2597
|
+
return false;
|
|
2598
|
+
}
|
|
2599
|
+
const expected = Buffer.from(stored, "base64url");
|
|
2600
|
+
const actual = digest(Buffer.from(salt, "base64url"), values);
|
|
2601
|
+
return expected.length === actual.length && timingSafeEqual(expected, actual);
|
|
2602
|
+
};
|
|
2603
|
+
|
|
2604
|
+
// src/proxmox/pbs-notification-target-wire.ts
|
|
2605
|
+
var base64 = (value) => Buffer2.from(value, "utf8").toString("base64");
|
|
2606
|
+
var unbase64 = (value) => Buffer2.from(value, "base64").toString("utf8");
|
|
2607
|
+
var keyAndValue = (item) => {
|
|
2608
|
+
const parts = {};
|
|
2609
|
+
for (const part of item.split(",")) {
|
|
2610
|
+
const at = part.indexOf("=");
|
|
2611
|
+
if (at > 0)
|
|
2612
|
+
parts[part.slice(0, at).trim()] = part.slice(at + 1).trim();
|
|
2613
|
+
}
|
|
2614
|
+
const value = parts["value"];
|
|
2615
|
+
return value === undefined ? { name: parts["name"] ?? "" } : { name: parts["name"] ?? "", value };
|
|
2616
|
+
};
|
|
2617
|
+
var wireItem = (name, value) => `name=${name},value=${base64(value)}`;
|
|
2618
|
+
var list2 = (value) => Array.isArray(value) ? value.map((item) => String(item)) : [];
|
|
2619
|
+
var names = (keys) => [...keys].filter((key) => key !== "").sort().join(",");
|
|
2620
|
+
var headerDigest = (target, pairs2) => seal(pairs2, `hf:pbs-notification-target:${target}:header`);
|
|
2621
|
+
var targetAttributes = (live, props) => {
|
|
2622
|
+
const headers = {};
|
|
2623
|
+
for (const item of list2(live["header"])) {
|
|
2624
|
+
const { name, value } = keyAndValue(item);
|
|
2625
|
+
if (name !== "")
|
|
2626
|
+
headers[name] = unbase64(value ?? "");
|
|
2627
|
+
}
|
|
2628
|
+
const secrets = list2(live["secret"]).map((item) => keyAndValue(item).name);
|
|
2629
|
+
return {
|
|
2630
|
+
author: text(live["author"]),
|
|
2631
|
+
body: unbase64(text(live["body"])),
|
|
2632
|
+
comment: text(live["comment"]),
|
|
2633
|
+
disable: bool(live["disable"]),
|
|
2634
|
+
"from-address": text(live["from-address"]),
|
|
2635
|
+
header: names(Object.keys(headers)),
|
|
2636
|
+
headerDigest: headerDigest(props.name, headers),
|
|
2637
|
+
mailto: addressList(live["mailto"]),
|
|
2638
|
+
"mailto-user": addressList(live["mailto-user"]),
|
|
2639
|
+
method: text(live["method"]),
|
|
2640
|
+
mode: text(live["mode"]),
|
|
2641
|
+
name: props.name,
|
|
2642
|
+
origin: text(live["origin"]),
|
|
2643
|
+
port: portOf(live["port"]),
|
|
2644
|
+
secret: names(secrets),
|
|
2645
|
+
sealed: "",
|
|
2646
|
+
server: text(live["server"]),
|
|
2647
|
+
type: props.type,
|
|
2648
|
+
url: text(live["url"]),
|
|
2649
|
+
username: text(live["username"])
|
|
2650
|
+
};
|
|
2651
|
+
};
|
|
2652
|
+
|
|
2653
|
+
// src/proxmox/pbs-notification-target-form.ts
|
|
2654
|
+
var OWNED = {
|
|
2655
|
+
author: ["sendmail", "smtp"],
|
|
2656
|
+
body: ["webhook"],
|
|
2657
|
+
"from-address": ["sendmail", "smtp"],
|
|
2658
|
+
header: ["webhook"],
|
|
2659
|
+
mailto: ["sendmail", "smtp"],
|
|
2660
|
+
"mailto-user": ["sendmail", "smtp"],
|
|
2661
|
+
method: ["webhook"],
|
|
2662
|
+
mode: ["smtp"],
|
|
2663
|
+
password: ["smtp"],
|
|
2664
|
+
port: ["smtp"],
|
|
2665
|
+
secret: ["webhook"],
|
|
2666
|
+
server: ["smtp"],
|
|
2667
|
+
url: ["webhook"],
|
|
2668
|
+
username: ["smtp"]
|
|
2669
|
+
};
|
|
2670
|
+
var REQUIRED = {
|
|
2671
|
+
sendmail: [],
|
|
2672
|
+
smtp: ["server", "from-address"],
|
|
2673
|
+
webhook: ["url", "method"]
|
|
2674
|
+
};
|
|
2675
|
+
var CLEAN_NAME = /^[^,=\s]+$/;
|
|
2676
|
+
var WRITE_ONLY = "declare it { fromEnv: 'NAME' }, or keep it in 'secret' and write {{ secrets.<name> }}";
|
|
2677
|
+
var refusals = (props) => {
|
|
2678
|
+
const out = [];
|
|
2679
|
+
for (const [field5, types] of Object.entries(OWNED)) {
|
|
2680
|
+
if (props[field5] !== undefined && !types.includes(props.type)) {
|
|
2681
|
+
out.push(`'${field5}' belongs to ${types.join("/")} targets, not ${props.type}`);
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
for (const field5 of REQUIRED[props.type]) {
|
|
2685
|
+
if (props[field5] === undefined || props[field5] === "")
|
|
2686
|
+
out.push(`${props.type} needs '${field5}'`);
|
|
2687
|
+
}
|
|
2688
|
+
for (const key of [...Object.keys(props.header ?? {}), ...Object.keys(props.secret ?? {})]) {
|
|
2689
|
+
if (!CLEAN_NAME.test(key))
|
|
2690
|
+
out.push(`'${key}' cannot be a header or secret name`);
|
|
2691
|
+
}
|
|
2692
|
+
for (const name of literalCredentialHeaders(props.header)) {
|
|
2693
|
+
out.push(`header '${name}' holds a literal credential; ${WRITE_ONLY}`);
|
|
2694
|
+
}
|
|
2695
|
+
for (const where of literalCredentialsInUrl(props.url)) {
|
|
2696
|
+
out.push(`'url' ${where} holds a literal credential; ${WRITE_ONLY}`);
|
|
2697
|
+
}
|
|
2698
|
+
return out;
|
|
2699
|
+
};
|
|
2700
|
+
var isFromEnv = (value) => typeof value !== "string";
|
|
2701
|
+
var resolveGroups = (props, env = process.env) => {
|
|
2702
|
+
const headerRefs = {};
|
|
2703
|
+
const headers = {};
|
|
2704
|
+
for (const [name, value] of Object.entries(props.header ?? {})) {
|
|
2705
|
+
if (isFromEnv(value))
|
|
2706
|
+
headerRefs[name] = value;
|
|
2707
|
+
else
|
|
2708
|
+
headers[name] = value;
|
|
2709
|
+
}
|
|
2710
|
+
const header = resolveAll(headerRefs, env);
|
|
2711
|
+
const sealedRefs = {};
|
|
2712
|
+
for (const [name, ref] of Object.entries(props.secret ?? {}))
|
|
2713
|
+
sealedRefs[`secret:${name}`] = ref;
|
|
2714
|
+
if (props.password !== undefined)
|
|
2715
|
+
sealedRefs["password"] = props.password;
|
|
2716
|
+
const sealed = resolveAll(sealedRefs, env);
|
|
2717
|
+
return {
|
|
2718
|
+
header: { missing: header.missing, values: { ...headers, ...header.values } },
|
|
2719
|
+
sealed
|
|
2720
|
+
};
|
|
2721
|
+
};
|
|
2722
|
+
var same5 = (declared, live) => declared === undefined || declared === live;
|
|
2723
|
+
var plainMatches = (live, props) => live.disable === (props.disable === true) && same5(props.comment, live.comment) && same5(props.author, live.author) && same5(props["from-address"], live["from-address"]) && same5(props.server, live.server) && (props.port === undefined || props.port === live.port) && same5(props.mode, live.mode) && same5(props.username, live.username) && same5(props.url, live.url) && same5(props.method, live.method) && same5(props.body, live.body) && (props.mailto === undefined || addressList(props.mailto) === live.mailto) && (props["mailto-user"] === undefined || addressList(props["mailto-user"]) === live["mailto-user"]);
|
|
2724
|
+
var headerState = (live, props, groups) => {
|
|
2725
|
+
if (props.header === undefined)
|
|
2726
|
+
return "match";
|
|
2727
|
+
if (names(Object.keys(props.header)) !== live.header)
|
|
2728
|
+
return "stale";
|
|
2729
|
+
if (groups.header.missing.length > 0)
|
|
2730
|
+
return "unknown";
|
|
2731
|
+
return headerDigest(props.name, groups.header.values) === live.headerDigest ? "match" : "stale";
|
|
2732
|
+
};
|
|
2733
|
+
var sealedState = (live, props, groups, sealed) => {
|
|
2734
|
+
if (props.secret === undefined && props.password === undefined)
|
|
2735
|
+
return "match";
|
|
2736
|
+
if (props.secret !== undefined && names(Object.keys(props.secret)) !== live.secret)
|
|
2737
|
+
return "stale";
|
|
2738
|
+
if (groups.sealed.missing.length > 0 || sealed === "")
|
|
2739
|
+
return "unknown";
|
|
2740
|
+
return sealMatches(sealed, groups.sealed.values) ? "match" : "stale";
|
|
2741
|
+
};
|
|
2742
|
+
var secretItems = (props, groups) => Object.keys(props.secret ?? {}).map((name) => wireItem(name, groups.sealed.values[`secret:${name}`] ?? ""));
|
|
2743
|
+
var headerItems = (props, groups) => Object.keys(props.header ?? {}).map((name) => wireItem(name, groups.header.values[name] ?? ""));
|
|
2744
|
+
var plainPairs = (props) => [
|
|
2745
|
+
["author", props.author ?? ""],
|
|
2746
|
+
["body", props.body === undefined ? "" : base64(props.body)],
|
|
2747
|
+
["comment", props.comment ?? ""],
|
|
2748
|
+
["from-address", props["from-address"] ?? ""],
|
|
2749
|
+
["method", props.method ?? ""],
|
|
2750
|
+
["mode", props.mode ?? ""],
|
|
2751
|
+
["port", props.port === undefined ? "" : String(props.port)],
|
|
2752
|
+
["server", props.server ?? ""],
|
|
2753
|
+
["url", props.url ?? ""],
|
|
2754
|
+
["username", props.username ?? ""],
|
|
2755
|
+
["mailto", props.mailto === undefined ? [] : addressList(props.mailto).split(",")],
|
|
2756
|
+
[
|
|
2757
|
+
"mailto-user",
|
|
2758
|
+
props["mailto-user"] === undefined ? [] : addressList(props["mailto-user"]).split(",")
|
|
2759
|
+
]
|
|
2760
|
+
];
|
|
2761
|
+
var declared = (props, key) => props[key] !== undefined;
|
|
2762
|
+
var empty = (value) => typeof value === "string" ? value === "" : value.filter((item) => item !== "").length === 0;
|
|
2763
|
+
var targetForm = (props, groups, carry, mode) => {
|
|
2764
|
+
const form = {
|
|
2765
|
+
disable: props.disable === true ? "1" : "0"
|
|
2766
|
+
};
|
|
2767
|
+
const cleared = [];
|
|
2768
|
+
const put = (key, value) => {
|
|
2769
|
+
if (!empty(value))
|
|
2770
|
+
form[key] = value;
|
|
2771
|
+
else if (mode === "update")
|
|
2772
|
+
cleared.push(key);
|
|
2773
|
+
};
|
|
2774
|
+
for (const [key, value] of plainPairs(props))
|
|
2775
|
+
if (declared(props, key))
|
|
2776
|
+
put(key, value);
|
|
2777
|
+
if (carry.header && props.header !== undefined)
|
|
2778
|
+
put("header", headerItems(props, groups));
|
|
2779
|
+
if (carry.sealed && props.secret !== undefined)
|
|
2780
|
+
put("secret", secretItems(props, groups));
|
|
2781
|
+
if (carry.sealed && props.password !== undefined) {
|
|
2782
|
+
form["password"] = groups.sealed.values["password"] ?? "";
|
|
2783
|
+
}
|
|
2784
|
+
if (mode === "create")
|
|
2785
|
+
form["name"] = props.name;
|
|
2786
|
+
if (cleared.length > 0)
|
|
2787
|
+
form["delete"] = cleared;
|
|
2788
|
+
return form;
|
|
2789
|
+
};
|
|
2790
|
+
|
|
2791
|
+
// src/proxmox/pbs-notification-target-lifecycle.ts
|
|
2792
|
+
var path = (props) => `config/notifications/endpoints/${props.type}/${props.name}`;
|
|
2793
|
+
var readLive2 = (props) => pve(props.target, "read", "GET", path(props)).pipe(Effect41.map((data) => data == null ? undefined : targetAttributes(data, props)), Effect41.orElseSucceed(() => {
|
|
2794
|
+
return;
|
|
2795
|
+
}));
|
|
2796
|
+
var refuse2 = (props) => {
|
|
2797
|
+
const reasons = refusals(props);
|
|
2798
|
+
return reasons.length === 0 ? Effect41.void : Effect41.die(new Error(`${path(props)}: ${reasons.join("; ")}.`));
|
|
2799
|
+
};
|
|
2800
|
+
var toCarry = (live, props, groups, sealed) => {
|
|
2801
|
+
const header = headerState(live, props, groups) === "stale";
|
|
2802
|
+
const sealedStale = sealedState(live, props, groups, sealed) === "stale";
|
|
2803
|
+
return plainMatches(live, props) && !header && !sealedStale ? undefined : { header, sealed: sealedStale };
|
|
2804
|
+
};
|
|
2805
|
+
var requireValues = (props, groups, carry) => {
|
|
2806
|
+
const missing = [
|
|
2807
|
+
...carry.header ? groups.header.missing : [],
|
|
2808
|
+
...carry.sealed ? groups.sealed.missing : []
|
|
2809
|
+
];
|
|
2810
|
+
return missing.length === 0 ? Effect41.void : Effect41.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."));
|
|
2811
|
+
};
|
|
2812
|
+
var handlers17 = {
|
|
2813
|
+
list: () => Effect41.succeed([]),
|
|
2814
|
+
read: ({ olds, output }) => (output === undefined ? refuse2(olds) : Effect41.void).pipe(Effect41.andThen(readLive2(olds)), Effect41.map((live) => live === undefined ? undefined : { ...live, sealed: output?.sealed ?? "" })),
|
|
2815
|
+
diff: ({
|
|
2816
|
+
news,
|
|
2817
|
+
olds,
|
|
2818
|
+
output
|
|
2819
|
+
}) => Effect41.gen(function* () {
|
|
2820
|
+
if (output === undefined || !isResolved9(news))
|
|
2821
|
+
return;
|
|
2822
|
+
if (olds !== undefined && (olds.type !== news.type || olds.name !== news.name)) {
|
|
2823
|
+
return { action: "replace", deleteFirst: olds.name === news.name };
|
|
2824
|
+
}
|
|
2825
|
+
yield* refuse2(news);
|
|
2826
|
+
const live = yield* readLive2(news);
|
|
2827
|
+
if (live === undefined)
|
|
2828
|
+
return { action: "update" };
|
|
2829
|
+
return toCarry(live, news, resolveGroups(news), output.sealed) === undefined ? { action: "noop" } : { action: "update" };
|
|
2830
|
+
}),
|
|
2831
|
+
reconcile: ({ news, output }) => Effect41.gen(function* () {
|
|
2832
|
+
yield* refuse2(news);
|
|
2833
|
+
const groups = resolveGroups(news);
|
|
2834
|
+
const live = yield* readLive2(news);
|
|
2835
|
+
let sealed = output?.sealed ?? "";
|
|
2836
|
+
if (live === undefined) {
|
|
2837
|
+
const carry = { header: true, sealed: true };
|
|
2838
|
+
yield* requireValues(news, groups, carry);
|
|
2839
|
+
const collection2 = `config/notifications/endpoints/${news.type}`;
|
|
2840
|
+
yield* pve(news.target, "provision", "POST", collection2, targetForm(news, groups, carry, "create"));
|
|
2841
|
+
sealed = seal(groups.sealed.values);
|
|
2842
|
+
} else {
|
|
2843
|
+
const carry = toCarry(live, news, groups, sealed);
|
|
2844
|
+
if (carry !== undefined) {
|
|
2845
|
+
yield* requireValues(news, groups, carry);
|
|
2846
|
+
yield* pve(news.target, "provision", "PUT", path(news), targetForm(news, groups, carry, "update"));
|
|
2847
|
+
if (carry.sealed)
|
|
2848
|
+
sealed = seal(groups.sealed.values);
|
|
2849
|
+
}
|
|
2850
|
+
}
|
|
2851
|
+
const after = yield* readLive2(news);
|
|
2852
|
+
if (after === undefined) {
|
|
2853
|
+
return yield* Effect41.die(new Error(`${path(news)}: the write returned no error but the target is still absent.`));
|
|
2854
|
+
}
|
|
2855
|
+
return { ...after, sealed };
|
|
2856
|
+
}),
|
|
2857
|
+
delete: ({ olds }) => pve(olds.target, "provision", "DELETE", path(olds))
|
|
2858
|
+
};
|
|
2859
|
+
|
|
2860
|
+
// src/proxmox/pbs-notification-target.ts
|
|
2861
|
+
var PbsNotificationTarget = Resource21("Pbs.NotificationTarget");
|
|
2862
|
+
var PbsNotificationTargetProvider = () => Provider21.effect(PbsNotificationTarget, Effect42.succeed(PbsNotificationTarget.Provider.of(handlers17)));
|
|
2372
2863
|
// src/proxmox/pbs-prune-job.ts
|
|
2373
|
-
import { Resource as
|
|
2374
|
-
import * as
|
|
2375
|
-
import * as
|
|
2864
|
+
import { Resource as Resource22 } from "alchemy";
|
|
2865
|
+
import * as Provider22 from "alchemy/Provider";
|
|
2866
|
+
import * as Effect43 from "effect/Effect";
|
|
2376
2867
|
|
|
2377
2868
|
// src/proxmox/pbs-prune-job-form.ts
|
|
2378
2869
|
var KEEP_KEYS = [
|
|
@@ -2407,14 +2898,14 @@ var requireKeeps = (props) => {
|
|
|
2407
2898
|
return;
|
|
2408
2899
|
throw new Error(`Pbs.PruneJob ${props.id}: declares no keep-* window, which tells PBS to keep NOTHING on ` + `datastore '${props.store}'. Every keep-* is optional on the wire, so an omission and a ` + 'deliberate "retain nothing" are indistinguishable; declare at least one window (keep-last, ' + "keep-hourly, keep-daily, keep-weekly, keep-monthly or keep-yearly) or delete the job.");
|
|
2409
2900
|
};
|
|
2410
|
-
var
|
|
2901
|
+
var field5 = (name, value) => value === undefined ? {} : { [name]: value };
|
|
2411
2902
|
var integer = (value) => value === undefined ? undefined : String(value);
|
|
2412
2903
|
var shape5 = (props) => ({
|
|
2413
2904
|
...keepForm(props),
|
|
2414
|
-
...
|
|
2415
|
-
...
|
|
2416
|
-
...
|
|
2417
|
-
...
|
|
2905
|
+
...field5("comment", props.comment),
|
|
2906
|
+
...field5("disable", flag(props.disable)),
|
|
2907
|
+
...field5("max-depth", integer(props["max-depth"])),
|
|
2908
|
+
...field5("ns", props.ns),
|
|
2418
2909
|
schedule: props.schedule,
|
|
2419
2910
|
store: props.store
|
|
2420
2911
|
});
|
|
@@ -2428,9 +2919,9 @@ var updateBody3 = (props) => {
|
|
|
2428
2919
|
};
|
|
2429
2920
|
|
|
2430
2921
|
// src/proxmox/pbs-prune-job.ts
|
|
2431
|
-
var PbsPruneJob =
|
|
2432
|
-
var
|
|
2433
|
-
var
|
|
2922
|
+
var PbsPruneJob = Resource22("Pbs.PruneJob");
|
|
2923
|
+
var same6 = (declared2, live) => declared2 === undefined || declared2 === live;
|
|
2924
|
+
var handlers18 = pveHandlers({
|
|
2434
2925
|
attributes: (live, props) => ({
|
|
2435
2926
|
comment: text(live["comment"], ""),
|
|
2436
2927
|
disable: bool(live["disable"], false),
|
|
@@ -2443,15 +2934,15 @@ var handlers15 = pveHandlers({
|
|
|
2443
2934
|
}),
|
|
2444
2935
|
collection: () => "config/prune",
|
|
2445
2936
|
createForm: createBody2,
|
|
2446
|
-
matches: (attributes2, props) => attributes2.schedule === props.schedule && attributes2.store === props.store &&
|
|
2937
|
+
matches: (attributes2, props) => attributes2.schedule === props.schedule && attributes2.store === props.store && same6(props.ns, attributes2.ns) && same6(props["max-depth"], attributes2["max-depth"]) && same6(props.disable, attributes2.disable) && same6(props.comment, attributes2.comment) && keepsMatch(attributes2, props),
|
|
2447
2938
|
path: (props) => `config/prune/${props.id}`,
|
|
2448
2939
|
updateForm: updateBody3
|
|
2449
2940
|
});
|
|
2450
|
-
var PbsPruneJobProvider = () =>
|
|
2941
|
+
var PbsPruneJobProvider = () => Provider22.effect(PbsPruneJob, Effect43.succeed(PbsPruneJob.Provider.of(handlers18)));
|
|
2451
2942
|
// src/proxmox/pbs-sync-job.ts
|
|
2452
|
-
import { Resource as
|
|
2453
|
-
import * as
|
|
2454
|
-
import * as
|
|
2943
|
+
import { Resource as Resource23 } from "alchemy";
|
|
2944
|
+
import * as Provider23 from "alchemy/Provider";
|
|
2945
|
+
import * as Effect44 from "effect/Effect";
|
|
2455
2946
|
|
|
2456
2947
|
// src/proxmox/pbs-sync-job-form.ts
|
|
2457
2948
|
var UNSET4 = -1;
|
|
@@ -2499,7 +2990,7 @@ var createBody3 = (props) => ({
|
|
|
2499
2990
|
...props["sync-direction"] === undefined ? {} : { "sync-direction": props["sync-direction"] }
|
|
2500
2991
|
});
|
|
2501
2992
|
var updateBody4 = (props) => shape6(props);
|
|
2502
|
-
var
|
|
2993
|
+
var handlers19 = pveHandlers({
|
|
2503
2994
|
attributes: (live, props) => ({
|
|
2504
2995
|
comment: text(live["comment"], ""),
|
|
2505
2996
|
"group-filter": filters(live["group-filter"]),
|
|
@@ -2526,12 +3017,12 @@ var handlers16 = pveHandlers({
|
|
|
2526
3017
|
});
|
|
2527
3018
|
|
|
2528
3019
|
// src/proxmox/pbs-sync-job.ts
|
|
2529
|
-
var PbsSyncJob =
|
|
2530
|
-
var PbsSyncJobProvider = () =>
|
|
3020
|
+
var PbsSyncJob = Resource23("Pbs.SyncJob");
|
|
3021
|
+
var PbsSyncJobProvider = () => Provider23.effect(PbsSyncJob, Effect44.succeed(PbsSyncJob.Provider.of(handlers19)));
|
|
2531
3022
|
// src/proxmox/pbs-verify-job.ts
|
|
2532
|
-
import { Resource as
|
|
2533
|
-
import * as
|
|
2534
|
-
import * as
|
|
3023
|
+
import { Resource as Resource24 } from "alchemy";
|
|
3024
|
+
import * as Provider24 from "alchemy/Provider";
|
|
3025
|
+
import * as Effect45 from "effect/Effect";
|
|
2535
3026
|
|
|
2536
3027
|
// src/proxmox/pbs-verify-job-form.ts
|
|
2537
3028
|
var rechecks = (ignoreVerified, outdatedAfter) => {
|
|
@@ -2552,8 +3043,8 @@ var shape7 = (props) => ({
|
|
|
2552
3043
|
});
|
|
2553
3044
|
|
|
2554
3045
|
// src/proxmox/pbs-verify-job.ts
|
|
2555
|
-
var PbsVerifyJob =
|
|
2556
|
-
var
|
|
3046
|
+
var PbsVerifyJob = Resource24("Pbs.VerifyJob");
|
|
3047
|
+
var handlers20 = pveHandlers({
|
|
2557
3048
|
attributes: (live, props) => {
|
|
2558
3049
|
const ignoreVerified = bool(live["ignore-verified"], true);
|
|
2559
3050
|
const outdatedAfter = int(live["outdated-after"], -1);
|
|
@@ -2575,12 +3066,12 @@ var handlers17 = pveHandlers({
|
|
|
2575
3066
|
path: (props) => `config/verify/${props.id}`,
|
|
2576
3067
|
updateForm: shape7
|
|
2577
3068
|
});
|
|
2578
|
-
var PbsVerifyJobProvider = () =>
|
|
3069
|
+
var PbsVerifyJobProvider = () => Provider24.effect(PbsVerifyJob, Effect45.succeed(PbsVerifyJob.Provider.of(handlers20)));
|
|
2579
3070
|
// src/proxmox/pool.ts
|
|
2580
|
-
import { Resource as
|
|
2581
|
-
import * as
|
|
2582
|
-
import * as
|
|
2583
|
-
var ProxmoxPool =
|
|
3071
|
+
import { Resource as Resource25 } from "alchemy";
|
|
3072
|
+
import * as Provider25 from "alchemy/Provider";
|
|
3073
|
+
import * as Effect46 from "effect/Effect";
|
|
3074
|
+
var ProxmoxPool = Resource25("Proxmox.Pool");
|
|
2584
3075
|
var poolCreateForm = (props) => ({
|
|
2585
3076
|
comment: props.comment ?? "",
|
|
2586
3077
|
poolid: props.poolid
|
|
@@ -2588,7 +3079,7 @@ var poolCreateForm = (props) => ({
|
|
|
2588
3079
|
var poolUpdateForm = (props) => ({
|
|
2589
3080
|
comment: props.comment ?? ""
|
|
2590
3081
|
});
|
|
2591
|
-
var
|
|
3082
|
+
var handlers21 = pveHandlers({
|
|
2592
3083
|
attributes: (live, props) => {
|
|
2593
3084
|
const row = live;
|
|
2594
3085
|
return {
|
|
@@ -2603,13 +3094,287 @@ var handlers18 = pveHandlers({
|
|
|
2603
3094
|
path: (props) => `pools/${props.poolid}`,
|
|
2604
3095
|
updateForm: poolUpdateForm
|
|
2605
3096
|
});
|
|
2606
|
-
var ProxmoxPoolProvider = () =>
|
|
2607
|
-
// src/proxmox/
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
3097
|
+
var ProxmoxPoolProvider = () => Provider25.effect(ProxmoxPool, Effect46.succeed(ProxmoxPool.Provider.of(handlers21)));
|
|
3098
|
+
// src/proxmox/provision-baseline.ts
|
|
3099
|
+
var PROVISION_PRIVILEGES = Object.freeze([
|
|
3100
|
+
"Datastore.Allocate",
|
|
3101
|
+
"Datastore.AllocateSpace",
|
|
3102
|
+
"Datastore.Audit",
|
|
3103
|
+
"Group.Allocate",
|
|
3104
|
+
"Mapping.Audit",
|
|
3105
|
+
"Mapping.Modify",
|
|
3106
|
+
"Permissions.Modify",
|
|
3107
|
+
"Pool.Allocate",
|
|
3108
|
+
"Pool.Audit",
|
|
3109
|
+
"Realm.AllocateUser",
|
|
3110
|
+
"SDN.Allocate",
|
|
3111
|
+
"SDN.Audit",
|
|
3112
|
+
"SDN.Use",
|
|
3113
|
+
"Sys.Audit",
|
|
3114
|
+
"Sys.Console",
|
|
3115
|
+
"Sys.Modify",
|
|
3116
|
+
"User.Modify",
|
|
3117
|
+
"VM.Allocate",
|
|
3118
|
+
"VM.Audit",
|
|
3119
|
+
"VM.Backup",
|
|
3120
|
+
"VM.Config.CPU",
|
|
3121
|
+
"VM.Config.Disk",
|
|
3122
|
+
"VM.Config.HWType",
|
|
3123
|
+
"VM.Config.Memory",
|
|
3124
|
+
"VM.Config.Network",
|
|
3125
|
+
"VM.Config.Options",
|
|
3126
|
+
"VM.PowerMgmt"
|
|
3127
|
+
]);
|
|
3128
|
+
var PROVISION_DEFAULTS = Object.freeze({
|
|
3129
|
+
comment: "provision baseline from @homeflare/alchemy - OpenBao mints short-lived tokens for members",
|
|
3130
|
+
mintGroup: "hf-mint",
|
|
3131
|
+
provisionUser: "hf-provision@pve",
|
|
3132
|
+
readRole: "PVEAuditor",
|
|
3133
|
+
readUser: "hf-read@pve",
|
|
3134
|
+
role: "HfProvisioner"
|
|
3135
|
+
});
|
|
3136
|
+
var ID = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
3137
|
+
var USERID = /^[A-Za-z0-9][A-Za-z0-9._-]*@[A-Za-z][A-Za-z0-9._-]*$/;
|
|
3138
|
+
var COMMENT = /^[A-Za-z0-9 ._,()@/+-]*$/;
|
|
3139
|
+
var problems2 = (names2) => {
|
|
3140
|
+
const found = [];
|
|
3141
|
+
if (!ID.test(names2.role))
|
|
3142
|
+
found.push(`role \`${names2.role}\` is not a PVE role id`);
|
|
3143
|
+
if (!ID.test(names2.mintGroup))
|
|
3144
|
+
found.push(`mintGroup \`${names2.mintGroup}\` is not a group id`);
|
|
3145
|
+
if (!ID.test(names2.readRole))
|
|
3146
|
+
found.push(`readRole \`${names2.readRole}\` is not a PVE role id`);
|
|
3147
|
+
for (const [field6, userid] of [
|
|
3148
|
+
["provisionUser", names2.provisionUser],
|
|
3149
|
+
["readUser", names2.readUser]
|
|
3150
|
+
]) {
|
|
3151
|
+
if (userid !== null && !USERID.test(userid)) {
|
|
3152
|
+
found.push(`${field6} \`${userid}\` is not a realm-qualified user id (name@realm)`);
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
if (names2.readUser === names2.provisionUser)
|
|
3156
|
+
found.push("readUser and provisionUser are one user");
|
|
3157
|
+
if (!COMMENT.test(names2.comment))
|
|
3158
|
+
found.push("comment holds a character the bootstrap will not quote");
|
|
3159
|
+
return found;
|
|
3160
|
+
};
|
|
3161
|
+
var provisionBaseline = (names2 = {}) => {
|
|
3162
|
+
const given = Object.entries(names2).filter(([, value]) => value !== undefined);
|
|
3163
|
+
const resolved = {
|
|
3164
|
+
...PROVISION_DEFAULTS,
|
|
3165
|
+
...Object.fromEntries(given)
|
|
3166
|
+
};
|
|
3167
|
+
const found = problems2(resolved);
|
|
3168
|
+
if (found.length > 0)
|
|
3169
|
+
throw new Error(`provision baseline: ${found.join("; ")}`);
|
|
3170
|
+
const lanes = [
|
|
3171
|
+
["provision", resolved.provisionUser, resolved.role]
|
|
3172
|
+
];
|
|
3173
|
+
if (resolved.readUser !== null)
|
|
3174
|
+
lanes.push(["read", resolved.readUser, resolved.readRole]);
|
|
3175
|
+
return {
|
|
3176
|
+
grants: lanes.map(([lane, userid, roleid]) => ({ lane, roleid, userid })),
|
|
3177
|
+
group: { comment: resolved.comment, groupid: resolved.mintGroup },
|
|
3178
|
+
role: { privs: PROVISION_PRIVILEGES, roleid: resolved.role },
|
|
3179
|
+
users: lanes.map(([lane, userid]) => ({
|
|
3180
|
+
comment: resolved.comment,
|
|
3181
|
+
groups: [resolved.mintGroup],
|
|
3182
|
+
lane,
|
|
3183
|
+
userid
|
|
3184
|
+
}))
|
|
3185
|
+
};
|
|
3186
|
+
};
|
|
3187
|
+
// src/proxmox/provision-bootstrap.ts
|
|
3188
|
+
var HEADER = `#!/bin/sh
|
|
3189
|
+
# Provisioning baseline bootstrap, generated by @homeflare/alchemy (provisionBootstrap).
|
|
3190
|
+
# Run ONCE, as root, on one node of a NEW cluster (or a new standalone node). Every step reads
|
|
3191
|
+
# first and writes only what differs; a second run changes nothing. No token, no password.
|
|
3192
|
+
set -eu
|
|
3193
|
+
[ "$(id -u)" -eq 0 ] || { echo 'bootstrap: run this as root' >&2; exit 1; }
|
|
3194
|
+
command -v pvesh >/dev/null 2>&1 || { echo 'bootstrap: no pvesh; run this on a PVE node' >&2; exit 1; }
|
|
3195
|
+
perl -MJSON::PP -e 1 || { echo 'bootstrap: perl cannot load JSON::PP' >&2; exit 1; }
|
|
3196
|
+
|
|
3197
|
+
# get <api-path> <perl>: print <perl> evaluated over the object at <api-path> (as $d).
|
|
3198
|
+
# Fails, silently, when the object does not exist: the empty input is not JSON.
|
|
3199
|
+
get() {
|
|
3200
|
+
pvesh get "$1" --output-format json 2>/dev/null | perl -MJSON::PP -0777 -ne "\\$d = decode_json(\\$_); $2" 2>/dev/null
|
|
3201
|
+
}
|
|
3202
|
+
`;
|
|
3203
|
+
var perlText = (text3) => `q{${text3}}`;
|
|
3204
|
+
var USER_STATE = "my $g = $d->{groups} // []; $g = [split /,/, $g] unless ref $g; " + 'print join(";", "groups=" . join(",", sort @$g), "enable=" . (($d->{enable} // 1) ? 1 : 0), ' + '"expire=" . ($d->{expire} // 0), "email=" . ($d->{email} // ""), ' + '"firstname=" . ($d->{firstname} // ""), "lastname=" . ($d->{lastname} // ""), ' + '"comment=" . ($d->{comment} // ""))';
|
|
3205
|
+
var roleStep = ({ role }) => {
|
|
3206
|
+
const privs = [...role.privs].sort().join(",");
|
|
3207
|
+
return `
|
|
3208
|
+
# Role ${role.roleid}: exactly the provision privileges, on no path yet.
|
|
3209
|
+
want='${privs}'
|
|
3210
|
+
if have=$(get /access/roles/${role.roleid} 'print join(",", sort grep { /\\./ } keys %$d)'); then
|
|
3211
|
+
if [ "$have" = "$want" ]; then echo 'role ${role.roleid}: ok'
|
|
3212
|
+
else echo 'role ${role.roleid}: setting the baseline privileges'; pveum role modify ${role.roleid} --privs "$want"; fi
|
|
3213
|
+
else
|
|
3214
|
+
echo 'role ${role.roleid}: adding'; pveum role add ${role.roleid} --privs "$want"
|
|
3215
|
+
fi
|
|
3216
|
+
`;
|
|
3217
|
+
};
|
|
3218
|
+
var groupStep = ({ group }) => `
|
|
3219
|
+
# Group ${group.groupid}: OpenBao may mint only for its members.
|
|
3220
|
+
if have=$(get /access/groups/${group.groupid} 'print $d->{comment} // ""'); then
|
|
3221
|
+
if [ "$have" = '${group.comment}' ]; then echo 'group ${group.groupid}: ok'
|
|
3222
|
+
else echo 'group ${group.groupid}: setting the comment'; pveum group modify ${group.groupid} --comment '${group.comment}'; fi
|
|
3223
|
+
else
|
|
3224
|
+
echo 'group ${group.groupid}: adding'; pveum group add ${group.groupid} --comment '${group.comment}'
|
|
3225
|
+
fi
|
|
3226
|
+
`;
|
|
3227
|
+
var userStep = (user) => {
|
|
3228
|
+
const groups = [...user.groups].sort().join(",");
|
|
3229
|
+
const want = `groups=${groups};enable=1;expire=0;email=;firstname=;lastname=;comment=${user.comment}`;
|
|
3230
|
+
const fields = `--groups ${groups} --comment '${user.comment}'`;
|
|
3231
|
+
return `
|
|
3232
|
+
# User ${user.userid} (the ${user.lane} lane): in ${groups}, enabled, no password.
|
|
3233
|
+
if have=$(get /access/users/${user.userid} '${USER_STATE}'); then
|
|
3234
|
+
if [ "$have" = '${want}' ]; then echo 'user ${user.userid}: ok'
|
|
3235
|
+
else echo 'user ${user.userid}: setting groups, comment and account fields'
|
|
3236
|
+
pveum user modify ${user.userid} ${fields} --enable 1 --expire 0 --email '' --firstname '' --lastname ''; fi
|
|
3237
|
+
else
|
|
3238
|
+
echo 'user ${user.userid}: adding'; pveum user add ${user.userid} ${fields}
|
|
3239
|
+
fi
|
|
3240
|
+
`;
|
|
3241
|
+
};
|
|
3242
|
+
var grantStep = (grant) => {
|
|
3243
|
+
const match = `print scalar grep { $_->{path} eq "/" && $_->{type} eq "user" && ` + `$_->{ugid} eq ${perlText(grant.userid)} && $_->{roleid} eq ${perlText(grant.roleid)} && ` + `($_->{propagate} // 1) } @$d`;
|
|
3244
|
+
return `
|
|
3245
|
+
# Grant: ${grant.userid} holds ${grant.roleid} on /, propagated (the ${grant.lane} lane).
|
|
3246
|
+
if [ "$(get /access/acl '${match}')" = 1 ]; then echo 'grant ${grant.userid} ${grant.roleid} /: ok'
|
|
3247
|
+
else echo 'grant ${grant.userid} ${grant.roleid} /: adding'; pveum acl modify / --users ${grant.userid} --roles ${grant.roleid} --propagate 1; fi
|
|
3248
|
+
`;
|
|
3249
|
+
};
|
|
3250
|
+
var requireRole = (roleid) => `
|
|
3251
|
+
get /access/roles/${roleid} 'print 1' >/dev/null || { echo 'bootstrap: role ${roleid} does not exist; create it, or name another readRole' >&2; exit 1; }
|
|
3252
|
+
`;
|
|
3253
|
+
var provisionBootstrap = (names2 = {}) => {
|
|
3254
|
+
const baseline = provisionBaseline(names2);
|
|
3255
|
+
const borrowed = baseline.grants.map((grant) => grant.roleid).filter((roleid) => roleid !== baseline.role.roleid);
|
|
3256
|
+
return [
|
|
3257
|
+
HEADER,
|
|
3258
|
+
...[...new Set(borrowed)].map(requireRole),
|
|
3259
|
+
roleStep(baseline),
|
|
3260
|
+
groupStep(baseline),
|
|
3261
|
+
...baseline.users.map(userStep),
|
|
3262
|
+
...baseline.grants.map(grantStep),
|
|
3263
|
+
`
|
|
3264
|
+
echo 'provision baseline: done. Next: point the OpenBao proxmox mount at these users, then deploy the stack that declares this baseline with --adopt.'
|
|
3265
|
+
`
|
|
3266
|
+
].join("");
|
|
3267
|
+
};
|
|
3268
|
+
// src/proxmox/provision-declare.ts
|
|
3269
|
+
import { adopt } from "alchemy/AdoptPolicy";
|
|
3270
|
+
import * as RemovalPolicy from "alchemy/RemovalPolicy";
|
|
3271
|
+
import * as Effect49 from "effect/Effect";
|
|
3272
|
+
|
|
3273
|
+
// src/proxmox/role.ts
|
|
3274
|
+
import { Resource as Resource26 } from "alchemy";
|
|
3275
|
+
import * as Provider26 from "alchemy/Provider";
|
|
3276
|
+
import * as Effect47 from "effect/Effect";
|
|
3277
|
+
var ProxmoxRole = Resource26("Proxmox.Role");
|
|
3278
|
+
var canonical2 = (privs) => [...new Set(privs.map((priv) => priv.trim()).filter((priv) => priv.length > 0))].sort();
|
|
3279
|
+
var livePrivs = (live) => {
|
|
3280
|
+
const listed = live["privs"];
|
|
3281
|
+
if (typeof listed === "string")
|
|
3282
|
+
return canonical2(listed.split(","));
|
|
3283
|
+
return canonical2(Object.keys(live).filter((key) => key.includes(".")));
|
|
3284
|
+
};
|
|
3285
|
+
var handlers22 = pveHandlers({
|
|
3286
|
+
attributes: (live, props) => ({ privs: livePrivs(live), roleid: props.roleid }),
|
|
3287
|
+
collection: () => "access/roles",
|
|
3288
|
+
createForm: (props) => ({ privs: canonical2(props.privs).join(","), roleid: props.roleid }),
|
|
3289
|
+
matches: (attributes2, props) => canonical2(attributes2.privs).join(",") === canonical2(props.privs).join(","),
|
|
3290
|
+
path: (props) => `access/roles/${props.roleid}`,
|
|
3291
|
+
updateForm: (props) => ({ privs: canonical2(props.privs).join(",") })
|
|
3292
|
+
});
|
|
3293
|
+
var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole, Effect47.succeed(ProxmoxRole.Provider.of(handlers22)));
|
|
3294
|
+
|
|
3295
|
+
// src/proxmox/user.ts
|
|
3296
|
+
import { Resource as Resource27 } from "alchemy";
|
|
3297
|
+
import * as Provider27 from "alchemy/Provider";
|
|
3298
|
+
import * as Effect48 from "effect/Effect";
|
|
3299
|
+
var ProxmoxUser = Resource27("Proxmox.User");
|
|
3300
|
+
var str2 = (value) => typeof value === "string" ? value : "";
|
|
3301
|
+
var int2 = (value) => {
|
|
3302
|
+
const parsed = typeof value === "number" ? value : Number(value ?? 0);
|
|
3303
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
3304
|
+
};
|
|
3305
|
+
var groupSet = (value) => {
|
|
3306
|
+
const raw = Array.isArray(value) ? value : typeof value === "string" ? value.split(",") : [];
|
|
3307
|
+
const names2 = raw.map((group) => str2(group).trim()).filter((group) => group !== "");
|
|
3308
|
+
return [...new Set(names2)].sort();
|
|
3309
|
+
};
|
|
3310
|
+
var tokenNames = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? Object.keys(value).sort() : [];
|
|
2612
3311
|
var shape8 = (props) => ({
|
|
3312
|
+
comment: props.comment ?? "",
|
|
3313
|
+
email: props.email ?? "",
|
|
3314
|
+
enable: props.enable === false ? "0" : "1",
|
|
3315
|
+
expire: String(props.expire ?? 0),
|
|
3316
|
+
firstname: props.firstname ?? "",
|
|
3317
|
+
groups: groupSet(props.groups).join(","),
|
|
3318
|
+
lastname: props.lastname ?? ""
|
|
3319
|
+
});
|
|
3320
|
+
var handlers23 = pveHandlers({
|
|
3321
|
+
attributes: (live, props) => ({
|
|
3322
|
+
comment: str2(live["comment"]),
|
|
3323
|
+
email: str2(live["email"]),
|
|
3324
|
+
enable: bool(live["enable"], true),
|
|
3325
|
+
expire: int2(live["expire"]),
|
|
3326
|
+
firstname: str2(live["firstname"]),
|
|
3327
|
+
groups: groupSet(live["groups"]),
|
|
3328
|
+
lastname: str2(live["lastname"]),
|
|
3329
|
+
tokens: tokenNames(live["tokens"]),
|
|
3330
|
+
userid: props.userid
|
|
3331
|
+
}),
|
|
3332
|
+
collection: () => "access/users",
|
|
3333
|
+
createForm: (props) => ({ ...shape8(props), userid: props.userid }),
|
|
3334
|
+
matches: (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(","),
|
|
3335
|
+
path: (props) => `access/users/${props.userid}`,
|
|
3336
|
+
updateForm: shape8
|
|
3337
|
+
});
|
|
3338
|
+
var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser, Effect48.succeed(ProxmoxUser.Provider.of(handlers23)));
|
|
3339
|
+
|
|
3340
|
+
// src/proxmox/provision-declare.ts
|
|
3341
|
+
var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect49.gen(function* () {
|
|
3342
|
+
const baseline = yield* Effect49.try({
|
|
3343
|
+
catch: (cause) => cause instanceof Error ? cause : new Error(String(cause)),
|
|
3344
|
+
try: () => provisionBaseline(names2)
|
|
3345
|
+
});
|
|
3346
|
+
const owned = (resource) => {
|
|
3347
|
+
const kept = resource.pipe(RemovalPolicy.retain());
|
|
3348
|
+
return options.adopt === undefined ? kept : kept.pipe(adopt(options.adopt));
|
|
3349
|
+
};
|
|
3350
|
+
const role = yield* owned(ProxmoxRole(`${id}-role`, {
|
|
3351
|
+
privs: [...baseline.role.privs],
|
|
3352
|
+
roleid: baseline.role.roleid,
|
|
3353
|
+
target
|
|
3354
|
+
}));
|
|
3355
|
+
const group = yield* owned(ProxmoxGroup(`${id}-mint-group`, { ...baseline.group, target }));
|
|
3356
|
+
const users = yield* Effect49.all(baseline.users.map((user) => owned(ProxmoxUser(`${id}-${user.lane}-user`, {
|
|
3357
|
+
comment: user.comment,
|
|
3358
|
+
groups: [...user.groups],
|
|
3359
|
+
target,
|
|
3360
|
+
userid: user.userid
|
|
3361
|
+
}))));
|
|
3362
|
+
const grants = yield* Effect49.all(baseline.grants.map((grant) => owned(ProxmoxAcl(`${id}-${grant.lane}-grant`, {
|
|
3363
|
+
path: "/",
|
|
3364
|
+
propagate: true,
|
|
3365
|
+
roleid: grant.roleid,
|
|
3366
|
+
target,
|
|
3367
|
+
type: "user",
|
|
3368
|
+
ugid: grant.userid
|
|
3369
|
+
}))));
|
|
3370
|
+
return { baseline, grants, group, role, users };
|
|
3371
|
+
});
|
|
3372
|
+
// src/proxmox/qemu.ts
|
|
3373
|
+
import { Resource as Resource28 } from "alchemy";
|
|
3374
|
+
import * as Provider28 from "alchemy/Provider";
|
|
3375
|
+
import * as Effect50 from "effect/Effect";
|
|
3376
|
+
var ProxmoxVm = Resource28("Proxmox.Vm");
|
|
3377
|
+
var shape9 = (props) => ({
|
|
2613
3378
|
cores: String(props.cores ?? 1),
|
|
2614
3379
|
memory: String(props.memory ?? 512),
|
|
2615
3380
|
name: props.name ?? `vm${String(props.vmid)}`,
|
|
@@ -2617,7 +3382,7 @@ var shape8 = (props) => ({
|
|
|
2617
3382
|
sockets: String(props.sockets ?? 1),
|
|
2618
3383
|
...props.net0 === undefined ? {} : { net0: props.net0 }
|
|
2619
3384
|
});
|
|
2620
|
-
var
|
|
3385
|
+
var handlers24 = pveHandlers({
|
|
2621
3386
|
attributes: (live, props) => ({
|
|
2622
3387
|
cores: num(live["cores"], 1),
|
|
2623
3388
|
memory: num(live["memory"], 512),
|
|
@@ -2628,46 +3393,46 @@ var handlers19 = pveHandlers({
|
|
|
2628
3393
|
vmid: props.vmid
|
|
2629
3394
|
}),
|
|
2630
3395
|
collection: (props) => `nodes/${props.node}/qemu`,
|
|
2631
|
-
createForm: (props) => ({ ...
|
|
3396
|
+
createForm: (props) => ({ ...shape9(props), vmid: String(props.vmid) }),
|
|
2632
3397
|
matches: (attributes2, props) => attributes2.name === (props.name ?? `vm${String(props.vmid)}`) && attributes2.memory === (props.memory ?? 512) && attributes2.cores === (props.cores ?? 1) && attributes2.sockets === (props.sockets ?? 1) && attributes2.onboot === (props.onboot === true),
|
|
2633
3398
|
path: (props) => `nodes/${props.node}/qemu/${String(props.vmid)}/config`,
|
|
2634
|
-
updateForm:
|
|
3399
|
+
updateForm: shape9
|
|
2635
3400
|
});
|
|
2636
|
-
var ProxmoxVmProvider = () =>
|
|
3401
|
+
var ProxmoxVmProvider = () => Provider28.effect(ProxmoxVm, Effect50.succeed(ProxmoxVm.Provider.of(handlers24)));
|
|
2637
3402
|
// src/proxmox/replication-job.ts
|
|
2638
|
-
import { Resource as
|
|
2639
|
-
import * as
|
|
2640
|
-
import * as
|
|
3403
|
+
import { Resource as Resource29 } from "alchemy";
|
|
3404
|
+
import * as Provider29 from "alchemy/Provider";
|
|
3405
|
+
import * as Effect51 from "effect/Effect";
|
|
2641
3406
|
|
|
2642
3407
|
// src/proxmox/replication-job-form.ts
|
|
2643
3408
|
var DEFAULT_SCHEDULE = "*/15";
|
|
2644
3409
|
var jobId = (props) => `${String(props.guest)}-${String(props.jobnum)}`;
|
|
2645
|
-
var
|
|
3410
|
+
var shape10 = (props) => ({
|
|
2646
3411
|
comment: props.comment ?? "",
|
|
2647
3412
|
disable: props.disable === true ? "1" : "0",
|
|
2648
3413
|
schedule: props.schedule ?? DEFAULT_SCHEDULE,
|
|
2649
3414
|
...props.rate === undefined ? {} : { rate: String(props.rate) }
|
|
2650
3415
|
});
|
|
2651
3416
|
var createBody4 = (props) => ({
|
|
2652
|
-
...
|
|
3417
|
+
...shape10(props),
|
|
2653
3418
|
id: jobId(props),
|
|
2654
3419
|
target: props.targetNode,
|
|
2655
3420
|
type: "local"
|
|
2656
3421
|
});
|
|
2657
3422
|
var updateBody5 = (props) => ({
|
|
2658
|
-
...
|
|
3423
|
+
...shape10(props),
|
|
2659
3424
|
delete: "remove_job"
|
|
2660
3425
|
});
|
|
2661
3426
|
|
|
2662
3427
|
// src/proxmox/replication-job.ts
|
|
2663
|
-
var ProxmoxReplicationJob =
|
|
3428
|
+
var ProxmoxReplicationJob = Resource29("Proxmox.ReplicationJob");
|
|
2664
3429
|
var rateOf = (value) => {
|
|
2665
3430
|
if (typeof value === "number")
|
|
2666
3431
|
return value;
|
|
2667
3432
|
const parsed = Number.parseFloat(text(value));
|
|
2668
3433
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
2669
3434
|
};
|
|
2670
|
-
var
|
|
3435
|
+
var handlers25 = pveHandlers({
|
|
2671
3436
|
attributes: (live, props) => ({
|
|
2672
3437
|
comment: text(live["comment"], ""),
|
|
2673
3438
|
disable: bool(live["disable"], false),
|
|
@@ -2687,36 +3452,15 @@ var handlers20 = pveHandlers({
|
|
|
2687
3452
|
path: (props) => `cluster/replication/${jobId(props)}`,
|
|
2688
3453
|
updateForm: updateBody5
|
|
2689
3454
|
});
|
|
2690
|
-
var ProxmoxReplicationJobProvider = () =>
|
|
2691
|
-
// src/proxmox/role.ts
|
|
2692
|
-
import { Resource as Resource25 } from "alchemy";
|
|
2693
|
-
import * as Provider25 from "alchemy/Provider";
|
|
2694
|
-
import * as Effect45 from "effect/Effect";
|
|
2695
|
-
var ProxmoxRole = Resource25("Proxmox.Role");
|
|
2696
|
-
var canonical = (privs) => [...new Set(privs.map((priv) => priv.trim()).filter((priv) => priv.length > 0))].sort();
|
|
2697
|
-
var livePrivs = (live) => {
|
|
2698
|
-
const listed = live["privs"];
|
|
2699
|
-
if (typeof listed === "string")
|
|
2700
|
-
return canonical(listed.split(","));
|
|
2701
|
-
return canonical(Object.keys(live).filter((key) => key.includes(".")));
|
|
2702
|
-
};
|
|
2703
|
-
var handlers21 = pveHandlers({
|
|
2704
|
-
attributes: (live, props) => ({ privs: livePrivs(live), roleid: props.roleid }),
|
|
2705
|
-
collection: () => "access/roles",
|
|
2706
|
-
createForm: (props) => ({ privs: canonical(props.privs).join(","), roleid: props.roleid }),
|
|
2707
|
-
matches: (attributes2, props) => canonical(attributes2.privs).join(",") === canonical(props.privs).join(","),
|
|
2708
|
-
path: (props) => `access/roles/${props.roleid}`,
|
|
2709
|
-
updateForm: (props) => ({ privs: canonical(props.privs).join(",") })
|
|
2710
|
-
});
|
|
2711
|
-
var ProxmoxRoleProvider = () => Provider25.effect(ProxmoxRole, Effect45.succeed(ProxmoxRole.Provider.of(handlers21)));
|
|
3455
|
+
var ProxmoxReplicationJobProvider = () => Provider29.effect(ProxmoxReplicationJob, Effect51.succeed(ProxmoxReplicationJob.Provider.of(handlers25)));
|
|
2712
3456
|
// src/proxmox/sdn-apply.ts
|
|
2713
|
-
import { Resource as
|
|
2714
|
-
import { isResolved as
|
|
2715
|
-
import * as
|
|
2716
|
-
import * as
|
|
3457
|
+
import { Resource as Resource30 } from "alchemy";
|
|
3458
|
+
import { isResolved as isResolved10 } from "alchemy/Diff";
|
|
3459
|
+
import * as Provider30 from "alchemy/Provider";
|
|
3460
|
+
import * as Effect53 from "effect/Effect";
|
|
2717
3461
|
|
|
2718
3462
|
// src/proxmox/sdn-apply-read.ts
|
|
2719
|
-
import * as
|
|
3463
|
+
import * as Effect52 from "effect/Effect";
|
|
2720
3464
|
var MARKED = [
|
|
2721
3465
|
"cluster/sdn/zones",
|
|
2722
3466
|
"cluster/sdn/vnets",
|
|
@@ -2725,7 +3469,7 @@ var MARKED = [
|
|
|
2725
3469
|
"cluster/sdn/route-maps"
|
|
2726
3470
|
];
|
|
2727
3471
|
var subsystemAbsent = (error) => error instanceof PveError && error.status === 501;
|
|
2728
|
-
var markedPending = (target) =>
|
|
3472
|
+
var markedPending = (target) => Effect52.all(MARKED.map((collection2) => pve(target, "read", "GET", `${collection2}?pending=1`).pipe(Effect52.map((rows) => (rows ?? []).filter((row) => row.state !== undefined).length), Effect52.catchIf(subsystemAbsent, () => Effect52.succeed(0)))), { concurrency: "unbounded" }).pipe(Effect52.map((counts) => counts.reduce((total, count2) => total + count2, 0)));
|
|
2729
3473
|
var fabricStaged = (staged, running) => {
|
|
2730
3474
|
if (staged === undefined && running === undefined)
|
|
2731
3475
|
return 0;
|
|
@@ -2733,11 +3477,11 @@ var fabricStaged = (staged, running) => {
|
|
|
2733
3477
|
return "inconsistent";
|
|
2734
3478
|
return staged === running ? 0 : 1;
|
|
2735
3479
|
};
|
|
2736
|
-
var fabricPending = (target) =>
|
|
3480
|
+
var fabricPending = (target) => Effect52.all(["pending", "running"].map((view) => pve(target, "read", "GET", `cluster/sdn/fabrics/all?${view}=1`).pipe(Effect52.map(canonical3), Effect52.catchIf(subsystemAbsent, () => Effect52.succeed(undefined)))), { concurrency: "unbounded" }).pipe(Effect52.flatMap(([staged, running]) => {
|
|
2737
3481
|
const verdict = fabricStaged(staged, running);
|
|
2738
|
-
return verdict === "inconsistent" ?
|
|
3482
|
+
return verdict === "inconsistent" ? Effect52.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.")) : Effect52.succeed(verdict);
|
|
2739
3483
|
}));
|
|
2740
|
-
var
|
|
3484
|
+
var canonical3 = (value) => JSON.stringify(sortDeep(value));
|
|
2741
3485
|
var sortDeep = (value) => {
|
|
2742
3486
|
if (Array.isArray(value)) {
|
|
2743
3487
|
return value.map(sortDeep).sort((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right)));
|
|
@@ -2748,39 +3492,39 @@ var sortDeep = (value) => {
|
|
|
2748
3492
|
}
|
|
2749
3493
|
return value;
|
|
2750
3494
|
};
|
|
2751
|
-
var sdnPendingCount = (target) =>
|
|
3495
|
+
var sdnPendingCount = (target) => Effect52.all([markedPending(target), fabricPending(target)], { concurrency: "unbounded" }).pipe(Effect52.map(([marked, fabric]) => marked + fabric));
|
|
2752
3496
|
|
|
2753
3497
|
// src/proxmox/sdn-apply.ts
|
|
2754
|
-
var ProxmoxSdnApply =
|
|
2755
|
-
var read3 = (props) => sdnPendingCount(props.target).pipe(
|
|
2756
|
-
var ProxmoxSdnApplyProvider = () =>
|
|
2757
|
-
list: () =>
|
|
2758
|
-
read:
|
|
3498
|
+
var ProxmoxSdnApply = Resource30("Proxmox.SdnApply");
|
|
3499
|
+
var read3 = (props) => sdnPendingCount(props.target).pipe(Effect53.map((pending) => ({ pending })));
|
|
3500
|
+
var ProxmoxSdnApplyProvider = () => Provider30.effect(ProxmoxSdnApply, Effect53.succeed(ProxmoxSdnApply.Provider.of({
|
|
3501
|
+
list: () => Effect53.succeed([]),
|
|
3502
|
+
read: Effect53.fn(function* ({ olds }) {
|
|
2759
3503
|
return yield* read3(olds);
|
|
2760
3504
|
}),
|
|
2761
|
-
diff:
|
|
2762
|
-
if (!
|
|
3505
|
+
diff: Effect53.fn(function* ({ news }) {
|
|
3506
|
+
if (!isResolved10(news))
|
|
2763
3507
|
return;
|
|
2764
3508
|
const { pending } = yield* read3(news);
|
|
2765
3509
|
return pending === 0 ? { action: "noop" } : { action: "update" };
|
|
2766
3510
|
}),
|
|
2767
|
-
reconcile:
|
|
3511
|
+
reconcile: Effect53.fn(function* ({ news }) {
|
|
2768
3512
|
const staged = yield* read3(news);
|
|
2769
3513
|
if (staged.pending === 0)
|
|
2770
3514
|
return staged;
|
|
2771
3515
|
yield* pve(news.target, "provision", "PUT", "cluster/sdn");
|
|
2772
3516
|
const after = yield* read3(news);
|
|
2773
3517
|
if (after.pending !== 0) {
|
|
2774
|
-
return yield*
|
|
3518
|
+
return yield* Effect53.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."));
|
|
2775
3519
|
}
|
|
2776
3520
|
return after;
|
|
2777
3521
|
}),
|
|
2778
|
-
delete: () =>
|
|
3522
|
+
delete: () => Effect53.void
|
|
2779
3523
|
})));
|
|
2780
3524
|
// src/proxmox/sdn-subnet.ts
|
|
2781
|
-
import { Resource as
|
|
2782
|
-
import * as
|
|
2783
|
-
import * as
|
|
3525
|
+
import { Resource as Resource31 } from "alchemy";
|
|
3526
|
+
import * as Provider31 from "alchemy/Provider";
|
|
3527
|
+
import * as Effect54 from "effect/Effect";
|
|
2784
3528
|
|
|
2785
3529
|
// src/proxmox/sdn-subnet-form.ts
|
|
2786
3530
|
var subnetId = (props) => `${props.zone}-${props.cidr.replace("/", "-")}`;
|
|
@@ -2810,8 +3554,8 @@ var updateForm2 = (props) => {
|
|
|
2810
3554
|
};
|
|
2811
3555
|
|
|
2812
3556
|
// src/proxmox/sdn-subnet.ts
|
|
2813
|
-
var ProxmoxSdnSubnet =
|
|
2814
|
-
var
|
|
3557
|
+
var ProxmoxSdnSubnet = Resource31("Proxmox.SdnSubnet");
|
|
3558
|
+
var handlers26 = pveHandlers({
|
|
2815
3559
|
attributes: (live, props) => {
|
|
2816
3560
|
const liveVnet = text(live["vnet"]);
|
|
2817
3561
|
if (liveVnet !== "" && liveVnet !== props.vnet)
|
|
@@ -2836,14 +3580,14 @@ var handlers22 = pveHandlers({
|
|
|
2836
3580
|
path: (props) => `cluster/sdn/vnets/${props.vnet}/subnets/${subnetId(props)}`,
|
|
2837
3581
|
updateForm: updateForm2
|
|
2838
3582
|
});
|
|
2839
|
-
var ProxmoxSdnSubnetProvider = () =>
|
|
3583
|
+
var ProxmoxSdnSubnetProvider = () => Provider31.effect(ProxmoxSdnSubnet, Effect54.succeed(ProxmoxSdnSubnet.Provider.of(handlers26)));
|
|
2840
3584
|
// src/proxmox/sdn-vnet.ts
|
|
2841
|
-
import { Resource as
|
|
2842
|
-
import * as
|
|
2843
|
-
import * as
|
|
2844
|
-
var ProxmoxSdnVnet =
|
|
3585
|
+
import { Resource as Resource32 } from "alchemy";
|
|
3586
|
+
import * as Provider32 from "alchemy/Provider";
|
|
3587
|
+
import * as Effect55 from "effect/Effect";
|
|
3588
|
+
var ProxmoxSdnVnet = Resource32("Proxmox.SdnVnet");
|
|
2845
3589
|
var tagOf = (value) => typeof value === "number" ? value : undefined;
|
|
2846
|
-
var
|
|
3590
|
+
var shape11 = (props) => {
|
|
2847
3591
|
const alias = props.alias ?? "";
|
|
2848
3592
|
return {
|
|
2849
3593
|
"isolate-ports": props.isolatePorts === true ? "1" : "0",
|
|
@@ -2858,9 +3602,9 @@ var updateForm3 = (props) => {
|
|
|
2858
3602
|
...(props.alias ?? "") === "" ? ["alias"] : [],
|
|
2859
3603
|
...props.tag === undefined ? ["tag"] : []
|
|
2860
3604
|
];
|
|
2861
|
-
return withClears(
|
|
3605
|
+
return withClears(shape11(props), unset2);
|
|
2862
3606
|
};
|
|
2863
|
-
var
|
|
3607
|
+
var handlers27 = pveHandlers({
|
|
2864
3608
|
readRole: "provision",
|
|
2865
3609
|
attributes: (live, props) => ({
|
|
2866
3610
|
alias: typeof live["alias"] === "string" ? live["alias"] : "",
|
|
@@ -2872,18 +3616,18 @@ var handlers23 = pveHandlers({
|
|
|
2872
3616
|
zone: typeof live["zone"] === "string" ? live["zone"] : ""
|
|
2873
3617
|
}),
|
|
2874
3618
|
collection: () => "cluster/sdn/vnets",
|
|
2875
|
-
createForm: (props) => ({ ...
|
|
3619
|
+
createForm: (props) => ({ ...shape11(props), vnet: props.vnet }),
|
|
2876
3620
|
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),
|
|
2877
3621
|
path: (props) => `cluster/sdn/vnets/${props.vnet}`,
|
|
2878
3622
|
updateForm: updateForm3
|
|
2879
3623
|
});
|
|
2880
|
-
var ProxmoxSdnVnetProvider = () =>
|
|
3624
|
+
var ProxmoxSdnVnetProvider = () => Provider32.effect(ProxmoxSdnVnet, Effect55.succeed(ProxmoxSdnVnet.Provider.of(handlers27)));
|
|
2881
3625
|
// src/proxmox/sdn-zone.ts
|
|
2882
|
-
import { Resource as
|
|
2883
|
-
import * as
|
|
2884
|
-
import * as
|
|
2885
|
-
var ProxmoxSdnZone =
|
|
2886
|
-
var sameIpam = (live,
|
|
3626
|
+
import { Resource as Resource33 } from "alchemy";
|
|
3627
|
+
import * as Provider33 from "alchemy/Provider";
|
|
3628
|
+
import * as Effect56 from "effect/Effect";
|
|
3629
|
+
var ProxmoxSdnZone = Resource33("Proxmox.SdnZone");
|
|
3630
|
+
var sameIpam = (live, declared2) => (live === "" ? "pve" : live) === (declared2 === undefined || declared2 === "" ? "pve" : declared2);
|
|
2887
3631
|
var body6 = (props) => ({
|
|
2888
3632
|
...props.bridge === undefined ? {} : { bridge: props.bridge },
|
|
2889
3633
|
...props.dns === undefined ? {} : { dns: props.dns },
|
|
@@ -2902,7 +3646,7 @@ var clearable = {
|
|
|
2902
3646
|
vlan: ["dns", "dnszone", "ipam", "mtu", "nodes"],
|
|
2903
3647
|
vxlan: ["dns", "dnszone", "ipam", "mtu", "nodes"]
|
|
2904
3648
|
};
|
|
2905
|
-
var
|
|
3649
|
+
var handlers28 = pveHandlers({
|
|
2906
3650
|
readRole: "provision",
|
|
2907
3651
|
attributes: (live, props) => {
|
|
2908
3652
|
const liveType = text(live["type"]);
|
|
@@ -2933,43 +3677,43 @@ var handlers24 = pveHandlers({
|
|
|
2933
3677
|
return withClears(fields, clear);
|
|
2934
3678
|
}
|
|
2935
3679
|
});
|
|
2936
|
-
var ProxmoxSdnZoneProvider = () =>
|
|
3680
|
+
var ProxmoxSdnZoneProvider = () => Provider33.effect(ProxmoxSdnZone, Effect56.succeed(ProxmoxSdnZone.Provider.of(handlers28)));
|
|
2937
3681
|
// src/proxmox/storage.ts
|
|
2938
|
-
import { Resource as
|
|
2939
|
-
import * as
|
|
2940
|
-
import * as
|
|
2941
|
-
var ProxmoxStorage =
|
|
3682
|
+
import { Resource as Resource34 } from "alchemy";
|
|
3683
|
+
import * as Provider34 from "alchemy/Provider";
|
|
3684
|
+
import * as Effect57 from "effect/Effect";
|
|
3685
|
+
var ProxmoxStorage = Resource34("Proxmox.Storage", {
|
|
2942
3686
|
defaultRemovalPolicy: "retain"
|
|
2943
3687
|
});
|
|
2944
|
-
var
|
|
2945
|
-
var
|
|
3688
|
+
var str3 = (value, fallback = "") => typeof value === "string" ? value : fallback;
|
|
3689
|
+
var field6 = (name, value) => value === undefined ? {} : { [name]: value };
|
|
2946
3690
|
var locatorForm = (locator) => ({ ...locator });
|
|
2947
3691
|
var tokens = (value) => value.split(",").map((part) => part.trim()).filter((part) => part !== "").sort().join(",");
|
|
2948
|
-
var sameList2 = (
|
|
2949
|
-
var
|
|
3692
|
+
var sameList2 = (declared2, live) => declared2 === undefined || tokens(declared2) === tokens(live);
|
|
3693
|
+
var same7 = (declared2, live) => declared2 === undefined || declared2 === live;
|
|
2950
3694
|
var mutable2 = (props) => ({
|
|
2951
|
-
...
|
|
2952
|
-
...
|
|
2953
|
-
...
|
|
2954
|
-
...
|
|
2955
|
-
...
|
|
2956
|
-
...
|
|
2957
|
-
...
|
|
2958
|
-
...
|
|
2959
|
-
});
|
|
2960
|
-
var
|
|
3695
|
+
...field6("comment", props.comment),
|
|
3696
|
+
...field6("content", props.content),
|
|
3697
|
+
...field6("disable", flag(props.disable)),
|
|
3698
|
+
...field6("maxfiles", props.maxfiles === undefined ? undefined : String(props.maxfiles)),
|
|
3699
|
+
...field6("nodes", props.nodes),
|
|
3700
|
+
...field6("preallocation", props.preallocation),
|
|
3701
|
+
...field6("prune-backups", props["prune-backups"]),
|
|
3702
|
+
...field6("shared", flag(props.shared))
|
|
3703
|
+
});
|
|
3704
|
+
var handlers29 = pveHandlers({
|
|
2961
3705
|
readRole: "provision",
|
|
2962
3706
|
attributes: (live, props) => ({
|
|
2963
|
-
comment:
|
|
2964
|
-
content:
|
|
3707
|
+
comment: str3(live["comment"]),
|
|
3708
|
+
content: str3(live["content"]),
|
|
2965
3709
|
disable: bool(live["disable"]),
|
|
2966
3710
|
maxfiles: typeof live["maxfiles"] === "number" ? live["maxfiles"] : -1,
|
|
2967
|
-
nodes:
|
|
2968
|
-
preallocation:
|
|
2969
|
-
"prune-backups":
|
|
3711
|
+
nodes: str3(live["nodes"]),
|
|
3712
|
+
preallocation: str3(live["preallocation"]),
|
|
3713
|
+
"prune-backups": str3(live["prune-backups"]),
|
|
2970
3714
|
shared: bool(live["shared"]),
|
|
2971
3715
|
storage: props.storage,
|
|
2972
|
-
type:
|
|
3716
|
+
type: str3(live["type"], props.type)
|
|
2973
3717
|
}),
|
|
2974
3718
|
collection: () => "storage",
|
|
2975
3719
|
createForm: (props) => ({
|
|
@@ -2978,62 +3722,18 @@ var handlers25 = pveHandlers({
|
|
|
2978
3722
|
storage: props.storage,
|
|
2979
3723
|
type: props.type
|
|
2980
3724
|
}),
|
|
2981
|
-
matches: (attributes2, props) => sameList2(props.content, attributes2.content) && sameList2(props.nodes, attributes2.nodes) && sameList2(props["prune-backups"], attributes2["prune-backups"]) &&
|
|
3725
|
+
matches: (attributes2, props) => sameList2(props.content, attributes2.content) && sameList2(props.nodes, attributes2.nodes) && sameList2(props["prune-backups"], attributes2["prune-backups"]) && same7(props.disable, attributes2.disable) && same7(props.shared, attributes2.shared) && same7(props.preallocation, attributes2.preallocation) && same7(props.comment, attributes2.comment) && same7(props.maxfiles, attributes2.maxfiles),
|
|
2982
3726
|
path: (props) => `storage/${props.storage}`,
|
|
2983
3727
|
updateForm: mutable2
|
|
2984
3728
|
});
|
|
2985
|
-
var ProxmoxStorageProvider = () =>
|
|
2986
|
-
// src/proxmox/user.ts
|
|
2987
|
-
import { Resource as Resource31 } from "alchemy";
|
|
2988
|
-
import * as Provider31 from "alchemy/Provider";
|
|
2989
|
-
import * as Effect52 from "effect/Effect";
|
|
2990
|
-
var ProxmoxUser = Resource31("Proxmox.User");
|
|
2991
|
-
var str3 = (value) => typeof value === "string" ? value : "";
|
|
2992
|
-
var int2 = (value) => {
|
|
2993
|
-
const parsed = typeof value === "number" ? value : Number(value ?? 0);
|
|
2994
|
-
return Number.isFinite(parsed) ? parsed : 0;
|
|
2995
|
-
};
|
|
2996
|
-
var groupSet = (value) => {
|
|
2997
|
-
const raw = Array.isArray(value) ? value : typeof value === "string" ? value.split(",") : [];
|
|
2998
|
-
const names = raw.map((group) => str3(group).trim()).filter((group) => group !== "");
|
|
2999
|
-
return [...new Set(names)].sort();
|
|
3000
|
-
};
|
|
3001
|
-
var tokenNames = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? Object.keys(value).sort() : [];
|
|
3002
|
-
var shape11 = (props) => ({
|
|
3003
|
-
comment: props.comment ?? "",
|
|
3004
|
-
email: props.email ?? "",
|
|
3005
|
-
enable: props.enable === false ? "0" : "1",
|
|
3006
|
-
expire: String(props.expire ?? 0),
|
|
3007
|
-
firstname: props.firstname ?? "",
|
|
3008
|
-
groups: groupSet(props.groups).join(","),
|
|
3009
|
-
lastname: props.lastname ?? ""
|
|
3010
|
-
});
|
|
3011
|
-
var handlers26 = pveHandlers({
|
|
3012
|
-
attributes: (live, props) => ({
|
|
3013
|
-
comment: str3(live["comment"]),
|
|
3014
|
-
email: str3(live["email"]),
|
|
3015
|
-
enable: bool(live["enable"], true),
|
|
3016
|
-
expire: int2(live["expire"]),
|
|
3017
|
-
firstname: str3(live["firstname"]),
|
|
3018
|
-
groups: groupSet(live["groups"]),
|
|
3019
|
-
lastname: str3(live["lastname"]),
|
|
3020
|
-
tokens: tokenNames(live["tokens"]),
|
|
3021
|
-
userid: props.userid
|
|
3022
|
-
}),
|
|
3023
|
-
collection: () => "access/users",
|
|
3024
|
-
createForm: (props) => ({ ...shape11(props), userid: props.userid }),
|
|
3025
|
-
matches: (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(","),
|
|
3026
|
-
path: (props) => `access/users/${props.userid}`,
|
|
3027
|
-
updateForm: shape11
|
|
3028
|
-
});
|
|
3029
|
-
var ProxmoxUserProvider = () => Provider31.effect(ProxmoxUser, Effect52.succeed(ProxmoxUser.Provider.of(handlers26)));
|
|
3729
|
+
var ProxmoxStorageProvider = () => Provider34.effect(ProxmoxStorage, Effect57.succeed(ProxmoxStorage.Provider.of(handlers29)));
|
|
3030
3730
|
// src/proxmox/zfs-pool.ts
|
|
3031
|
-
import { Resource as
|
|
3032
|
-
import * as
|
|
3033
|
-
import * as
|
|
3731
|
+
import { Resource as Resource35 } from "alchemy";
|
|
3732
|
+
import * as Provider35 from "alchemy/Provider";
|
|
3733
|
+
import * as Effect59 from "effect/Effect";
|
|
3034
3734
|
|
|
3035
3735
|
// src/proxmox/zfs-pool-write.ts
|
|
3036
|
-
import * as
|
|
3736
|
+
import * as Effect58 from "effect/Effect";
|
|
3037
3737
|
var createForm6 = (props) => ({
|
|
3038
3738
|
devices: props.devices.join(","),
|
|
3039
3739
|
name: props.name,
|
|
@@ -3043,28 +3743,28 @@ var createForm6 = (props) => ({
|
|
|
3043
3743
|
...props["draid-config"] === undefined ? {} : { "draid-config": props["draid-config"] }
|
|
3044
3744
|
});
|
|
3045
3745
|
var SETTLE_ATTEMPTS = 30;
|
|
3046
|
-
var createPool = (props, collection2,
|
|
3746
|
+
var createPool = (props, collection2, path2, read4) => Effect58.gen(function* () {
|
|
3047
3747
|
const existing = yield* read4(props);
|
|
3048
3748
|
if (existing !== undefined)
|
|
3049
3749
|
return existing;
|
|
3050
3750
|
const upid = yield* pve(props.target, "provision", "POST", collection2, createForm6(props));
|
|
3051
|
-
const settle4 = (attempts) =>
|
|
3751
|
+
const settle4 = (attempts) => Effect58.gen(function* () {
|
|
3052
3752
|
const live = yield* read4(props);
|
|
3053
3753
|
if (live !== undefined || attempts <= 0)
|
|
3054
3754
|
return live;
|
|
3055
|
-
yield*
|
|
3755
|
+
yield* Effect58.sleep("2 seconds");
|
|
3056
3756
|
return yield* settle4(attempts - 1);
|
|
3057
3757
|
});
|
|
3058
3758
|
const after = yield* settle4(SETTLE_ATTEMPTS);
|
|
3059
3759
|
if (after === undefined) {
|
|
3060
|
-
return yield*
|
|
3760
|
+
return yield* Effect58.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."));
|
|
3061
3761
|
}
|
|
3062
3762
|
return after;
|
|
3063
3763
|
});
|
|
3064
|
-
var destroyPool = (olds,
|
|
3764
|
+
var destroyPool = (olds, path2) => pve(olds.target, "provision", "DELETE", path2);
|
|
3065
3765
|
|
|
3066
3766
|
// src/proxmox/zfs-pool.ts
|
|
3067
|
-
var ProxmoxZfsPool =
|
|
3767
|
+
var ProxmoxZfsPool = Resource35("Proxmox.ZfsPool", {
|
|
3068
3768
|
defaultRemovalPolicy: "retain"
|
|
3069
3769
|
});
|
|
3070
3770
|
var leafDevices = (children) => Array.isArray(children) ? children.flatMap((entry) => {
|
|
@@ -3090,17 +3790,23 @@ var spec2 = {
|
|
|
3090
3790
|
path: (props) => `nodes/${props.node}/disks/zfs/${props.name}`
|
|
3091
3791
|
};
|
|
3092
3792
|
var ops5 = pveOperations(spec2);
|
|
3093
|
-
var
|
|
3793
|
+
var handlers30 = {
|
|
3094
3794
|
...pveHandlers(spec2),
|
|
3095
|
-
delete:
|
|
3795
|
+
delete: Effect59.fn(function* ({ olds }) {
|
|
3096
3796
|
yield* destroyPool(olds, spec2.path(olds));
|
|
3097
3797
|
}),
|
|
3098
3798
|
reconcile: ({ news }) => createPool(news, spec2.collection(news), spec2.path(news), ops5.read)
|
|
3099
3799
|
};
|
|
3100
|
-
var ProxmoxZfsPoolProvider = () =>
|
|
3800
|
+
var ProxmoxZfsPoolProvider = () => Provider35.effect(ProxmoxZfsPool, Effect59.succeed(ProxmoxZfsPool.Provider.of(handlers30)));
|
|
3101
3801
|
export {
|
|
3802
|
+
PROVISION_DEFAULTS,
|
|
3803
|
+
PROVISION_PRIVILEGES,
|
|
3102
3804
|
PbsDatastore,
|
|
3103
3805
|
PbsDatastoreProvider,
|
|
3806
|
+
PbsNotificationMatcher,
|
|
3807
|
+
PbsNotificationMatcherProvider,
|
|
3808
|
+
PbsNotificationTarget,
|
|
3809
|
+
PbsNotificationTargetProvider,
|
|
3104
3810
|
PbsPruneJob,
|
|
3105
3811
|
PbsPruneJobProvider,
|
|
3106
3812
|
PbsSyncJob,
|
|
@@ -3131,6 +3837,8 @@ export {
|
|
|
3131
3837
|
ProxmoxMetricServerProvider,
|
|
3132
3838
|
ProxmoxNetworkApplyProvider,
|
|
3133
3839
|
ProxmoxNodeNetworkProvider,
|
|
3840
|
+
ProxmoxNotificationMatcher,
|
|
3841
|
+
ProxmoxNotificationMatcherProvider,
|
|
3134
3842
|
ProxmoxNotificationTarget,
|
|
3135
3843
|
ProxmoxNotificationTargetProvider,
|
|
3136
3844
|
ProxmoxPool,
|
|
@@ -3151,8 +3859,12 @@ export {
|
|
|
3151
3859
|
ProxmoxUser,
|
|
3152
3860
|
ProxmoxUserProvider,
|
|
3153
3861
|
ProxmoxVmProvider,
|
|
3154
|
-
ProxmoxZfsPoolProvider
|
|
3862
|
+
ProxmoxZfsPoolProvider,
|
|
3863
|
+
alertmanagerAlertBody,
|
|
3864
|
+
declareProvisionBaseline,
|
|
3865
|
+
provisionBaseline,
|
|
3866
|
+
provisionBootstrap
|
|
3155
3867
|
};
|
|
3156
3868
|
|
|
3157
|
-
//# debugId=
|
|
3869
|
+
//# debugId=98691C143D2F953764756E2164756E21
|
|
3158
3870
|
//# sourceMappingURL=index.js.map
|