@homeflare/alchemy 0.27.4 → 0.29.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 +2 -0
- package/dist/caddy/admin-address.d.ts +27 -0
- package/dist/caddy/admin-address.d.ts.map +1 -0
- package/dist/caddy/admin-calls.d.ts +25 -28
- package/dist/caddy/admin-calls.d.ts.map +1 -1
- package/dist/caddy/admin.d.ts +28 -38
- package/dist/caddy/admin.d.ts.map +1 -1
- package/dist/caddy/caddy-http-client.d.ts +25 -0
- package/dist/caddy/caddy-http-client.d.ts.map +1 -0
- package/dist/caddy/config-errors.d.ts +24 -0
- package/dist/caddy/config-errors.d.ts.map +1 -0
- package/dist/caddy/config-lifecycle.d.ts +20 -49
- package/dist/caddy/config-lifecycle.d.ts.map +1 -1
- package/dist/caddy/config-reconcile.d.ts +45 -0
- package/dist/caddy/config-reconcile.d.ts.map +1 -0
- package/dist/caddy/config.d.ts +4 -3
- package/dist/caddy/config.d.ts.map +1 -1
- package/dist/caddy/fake-caddy.d.ts +10 -2
- package/dist/caddy/fake-caddy.d.ts.map +1 -1
- package/dist/caddy/index.d.ts +3 -3
- package/dist/caddy/index.d.ts.map +1 -1
- package/dist/caddy/index.js +487 -444
- package/dist/caddy/index.js.map +16 -12
- package/dist/caddy/local-admin.d.ts +20 -34
- package/dist/caddy/local-admin.d.ts.map +1 -1
- package/dist/caddy/providers.d.ts +19 -3
- package/dist/caddy/providers.d.ts.map +1 -1
- package/dist/cloudflare/mesh-node-token.d.ts +1 -1
- package/dist/cloudflare/mesh-node-token.d.ts.map +1 -1
- package/dist/forgejo/branch-protection.d.ts +4 -4
- package/dist/forgejo/org-actions-secrets.d.ts +4 -4
- package/dist/forgejo/org-label.d.ts +4 -4
- package/dist/forgejo/org-team.d.ts +4 -4
- package/dist/forgejo/repo-webhook.d.ts +4 -4
- package/dist/forgejo/repository.d.ts +4 -4
- package/dist/forgejo/team-member.d.ts +4 -4
- package/dist/github/declare-repo-baseline.d.ts +1 -1
- package/dist/github/declare-repo-baseline.d.ts.map +1 -1
- package/dist/github/index.d.ts +9 -5
- package/dist/github/index.d.ts.map +1 -1
- package/dist/github/index.js +161 -25
- package/dist/github/index.js.map +15 -13
- package/dist/github/repo-baseline-data.d.ts +10 -1
- package/dist/github/repo-baseline-data.d.ts.map +1 -1
- package/dist/github/repo-policy.d.ts +1 -1
- package/dist/github/repo-policy.d.ts.map +1 -1
- package/dist/github/repository-ruleset-errors.d.ts +37 -3
- package/dist/github/repository-ruleset-errors.d.ts.map +1 -1
- package/dist/github/repository-ruleset-form.d.ts.map +1 -1
- package/dist/github/repository-ruleset-guards.d.ts +15 -2
- package/dist/github/repository-ruleset-guards.d.ts.map +1 -1
- package/dist/github/repository-ruleset-narrowing-guards.d.ts +39 -0
- package/dist/github/repository-ruleset-narrowing-guards.d.ts.map +1 -0
- package/dist/github/repository-ruleset-octokit.d.ts +49 -0
- package/dist/github/repository-ruleset-octokit.d.ts.map +1 -1
- package/dist/github/repository-ruleset-probe.d.ts +2 -1
- package/dist/github/repository-ruleset-probe.d.ts.map +1 -1
- package/dist/github/repository-ruleset-props.d.ts +91 -0
- package/dist/github/repository-ruleset-props.d.ts.map +1 -0
- package/dist/github/repository-ruleset-providers.d.ts +42 -0
- package/dist/github/repository-ruleset-providers.d.ts.map +1 -0
- package/dist/github/repository-ruleset-reconcile.d.ts.map +1 -1
- package/dist/github/repository-ruleset.d.ts +28 -34
- package/dist/github/repository-ruleset.d.ts.map +1 -1
- package/dist/litellm/fake-litellm.d.ts +27 -16
- package/dist/litellm/fake-litellm.d.ts.map +1 -1
- package/dist/litellm/fake-stack.d.ts +9 -4
- package/dist/litellm/fake-stack.d.ts.map +1 -1
- package/dist/litellm/index.d.ts +7 -5
- package/dist/litellm/index.d.ts.map +1 -1
- package/dist/litellm/index.js +53 -123
- package/dist/litellm/index.js.map +8 -8
- package/dist/litellm/operations.d.ts +60 -0
- package/dist/litellm/operations.d.ts.map +1 -0
- package/dist/litellm/pass-through-endpoint.d.ts +10 -28
- package/dist/litellm/pass-through-endpoint.d.ts.map +1 -1
- package/dist/litellm/pass-through-errors.d.ts +37 -0
- package/dist/litellm/pass-through-errors.d.ts.map +1 -0
- package/dist/litellm/pass-through-form.d.ts +11 -4
- package/dist/litellm/pass-through-form.d.ts.map +1 -1
- package/dist/litellm/providers.d.ts +7 -5
- package/dist/litellm/providers.d.ts.map +1 -1
- package/dist/netbox/prefix.d.ts +4 -4
- package/dist/paperless/custom-field.d.ts +21 -21
- package/dist/paperless/custom-field.d.ts.map +1 -1
- package/dist/paperless/document-type.d.ts +34 -4
- package/dist/paperless/document-type.d.ts.map +1 -1
- package/dist/paperless/fake-paperless.d.ts +36 -19
- package/dist/paperless/fake-paperless.d.ts.map +1 -1
- package/dist/paperless/index.d.ts +7 -2
- package/dist/paperless/index.d.ts.map +1 -1
- package/dist/paperless/index.js +164 -228
- package/dist/paperless/index.js.map +10 -13
- package/dist/paperless/matching-locate.d.ts +42 -20
- package/dist/paperless/matching-locate.d.ts.map +1 -1
- package/dist/paperless/matching-types.d.ts +47 -18
- package/dist/paperless/matching-types.d.ts.map +1 -1
- package/dist/paperless/matching.d.ts +30 -27
- package/dist/paperless/matching.d.ts.map +1 -1
- package/dist/paperless/providers.d.ts +13 -7
- package/dist/paperless/providers.d.ts.map +1 -1
- package/dist/paperless/storage-path.d.ts +34 -4
- package/dist/paperless/storage-path.d.ts.map +1 -1
- package/dist/paperless/tag.d.ts +43 -8
- package/dist/paperless/tag.d.ts.map +1 -1
- package/dist/proxmox/acl.d.ts +30 -23
- package/dist/proxmox/acl.d.ts.map +1 -1
- package/dist/proxmox/distilled-pve.d.ts +89 -0
- package/dist/proxmox/distilled-pve.d.ts.map +1 -0
- package/dist/proxmox/index.js +663 -609
- package/dist/proxmox/index.js.map +12 -11
- package/dist/proxmox/members.d.ts +29 -10
- package/dist/proxmox/members.d.ts.map +1 -1
- package/docs/caddy-admin.md +1 -1
- package/docs/caddy.md +3 -2
- package/docs/credentials.md +16 -0
- package/docs/litellm.md +21 -13
- package/docs/paperless.md +12 -0
- package/docs/peers.md +5 -0
- package/docs/repo-policy.md +16 -1
- package/docs/repository-ruleset.md +53 -13
- package/docs/upstream-conformance.md +126 -10
- package/package.json +9 -2
- package/src/caddy/admin-address.ts +71 -0
- package/src/caddy/admin-calls.ts +48 -100
- package/src/caddy/admin.ts +37 -41
- package/src/caddy/adopt-scope.test.ts +1 -1
- package/src/caddy/caddy-http-client.ts +229 -0
- package/src/caddy/config-adopt-provider.test.ts +139 -0
- package/src/caddy/config-adopt.test.ts +60 -126
- package/src/caddy/config-errors.ts +47 -0
- package/src/caddy/config-lifecycle.test.ts +55 -42
- package/src/caddy/config-lifecycle.ts +72 -162
- package/src/caddy/config-reconcile.ts +138 -0
- package/src/caddy/config.ts +36 -42
- package/src/caddy/fake-caddy.ts +20 -2
- package/src/caddy/index.ts +3 -8
- package/src/caddy/local-admin.test.ts +35 -21
- package/src/caddy/local-admin.ts +60 -167
- package/src/caddy/providers.test.ts +14 -6
- package/src/caddy/providers.ts +20 -4
- package/src/github/declare-repo-baseline.ts +6 -6
- package/src/github/index.ts +10 -4
- package/src/github/repo-baseline-data.test.ts +13 -0
- package/src/github/repo-baseline-data.ts +11 -2
- package/src/github/repo-policy-stack.test.ts +59 -0
- package/src/github/repo-policy.ts +3 -2
- package/src/github/repository-ruleset-errors.ts +51 -4
- package/src/github/repository-ruleset-exact-adopt-k1.test.ts +109 -0
- package/src/github/repository-ruleset-exact-adopt.test.ts +184 -0
- package/src/github/repository-ruleset-form.ts +7 -3
- package/src/github/repository-ruleset-guards.ts +11 -4
- package/src/github/repository-ruleset-narrowing-guards.ts +141 -0
- package/src/github/repository-ruleset-narrowing.test.ts +210 -0
- package/src/github/repository-ruleset-octokit.test.ts +141 -0
- package/src/github/repository-ruleset-octokit.ts +121 -30
- package/src/github/repository-ruleset-probe.ts +2 -1
- package/src/github/repository-ruleset-props.ts +95 -0
- package/src/github/repository-ruleset-providers.ts +46 -0
- package/src/github/repository-ruleset-reconcile.ts +35 -0
- package/src/github/repository-ruleset-wire.test.ts +38 -27
- package/src/github/repository-ruleset-write-refusals.test.ts +6 -26
- package/src/github/repository-ruleset.ts +53 -43
- package/src/litellm/fake-litellm.ts +83 -78
- package/src/litellm/fake-stack.ts +13 -5
- package/src/litellm/index.ts +7 -21
- package/src/litellm/operations.ts +149 -0
- package/src/litellm/pass-through-endpoint-delete-swallow.test.ts +17 -20
- package/src/litellm/pass-through-endpoint-replace.test.ts +3 -4
- package/src/litellm/pass-through-endpoint.test.ts +8 -8
- package/src/litellm/pass-through-endpoint.ts +21 -31
- package/src/litellm/pass-through-errors.ts +37 -0
- package/src/litellm/pass-through-form.ts +20 -10
- package/src/litellm/providers.ts +8 -7
- package/src/paperless/custom-field.test.ts +49 -40
- package/src/paperless/custom-field.ts +57 -33
- package/src/paperless/document-type.test.ts +48 -0
- package/src/paperless/document-type.ts +51 -22
- package/src/paperless/fake-paperless.ts +63 -72
- package/src/paperless/index.ts +7 -13
- package/src/paperless/matching-locate.test.ts +102 -0
- package/src/paperless/matching-locate.ts +80 -86
- package/src/paperless/matching-types.ts +49 -16
- package/src/paperless/matching.ts +77 -90
- package/src/paperless/providers.ts +14 -10
- package/src/paperless/storage-path.test.ts +56 -0
- package/src/paperless/storage-path.ts +53 -24
- package/src/paperless/tag-identity.test.ts +127 -0
- package/src/paperless/tag.test.ts +100 -0
- package/src/paperless/tag.ts +62 -32
- package/src/proxmox/acl.test.ts +145 -0
- package/src/proxmox/acl.ts +128 -150
- package/src/proxmox/distilled-pve.test.ts +231 -0
- package/src/proxmox/distilled-pve.ts +147 -0
- package/src/proxmox/members-timeout.test.ts +161 -0
- package/src/proxmox/members.test.ts +9 -0
- package/src/proxmox/members.ts +36 -2
- package/dist/litellm/client.d.ts +0 -76
- package/dist/litellm/client.d.ts.map +0 -1
- package/dist/litellm/credentials.d.ts +0 -48
- package/dist/litellm/credentials.d.ts.map +0 -1
- package/dist/litellm/generated/pass-through.d.ts +0 -71
- package/dist/litellm/generated/pass-through.d.ts.map +0 -1
- package/dist/paperless/client.d.ts +0 -37
- package/dist/paperless/client.d.ts.map +0 -1
- package/dist/paperless/credentials.d.ts +0 -40
- package/dist/paperless/credentials.d.ts.map +0 -1
- package/dist/paperless/errors.d.ts +0 -49
- package/dist/paperless/errors.d.ts.map +0 -1
- package/src/litellm/client.ts +0 -195
- package/src/litellm/credentials.ts +0 -73
- package/src/litellm/generated/pass-through.ts +0 -73
- package/src/paperless/client.ts +0 -88
- package/src/paperless/credentials.ts +0 -69
- package/src/paperless/errors.ts +0 -62
- package/src/paperless/matching-identity.test.ts +0 -194
- package/src/paperless/matching.test.ts +0 -128
package/dist/proxmox/index.js
CHANGED
|
@@ -1,235 +1,12 @@
|
|
|
1
1
|
// src/proxmox/acl.ts
|
|
2
2
|
import { Resource } from "alchemy";
|
|
3
|
-
import { isResolved as isResolved2 } from "alchemy/Diff";
|
|
4
|
-
import * as Provider from "alchemy/Provider";
|
|
5
|
-
import * as Effect8 from "effect/Effect";
|
|
6
|
-
|
|
7
|
-
// src/proxmox/client.ts
|
|
8
|
-
import * as Effect4 from "effect/Effect";
|
|
9
|
-
import * as HttpClient5 from "effect/unstable/http/HttpClient";
|
|
10
|
-
import * as HttpClientRequest2 from "effect/unstable/http/HttpClientRequest";
|
|
11
|
-
|
|
12
|
-
// src/proxmox/credentials.ts
|
|
13
|
-
import * as Effect from "effect/Effect";
|
|
14
|
-
import * as Option from "effect/Option";
|
|
15
|
-
import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
|
|
16
|
-
import * as Headers from "effect/unstable/http/Headers";
|
|
17
|
-
import * as HttpClient from "effect/unstable/http/HttpClient";
|
|
18
|
-
import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest";
|
|
19
|
-
var baoVariable = (env, name) => env[`BAO_${name}`] ?? env[`VAULT_${name}`] ?? "";
|
|
20
|
-
var mint = (target, role, env = process.env) => Effect.gen(function* () {
|
|
21
|
-
const path = `${target.mount}/creds/${role}`;
|
|
22
|
-
const refuse = (status2, detail) => new Error(`OpenBao GET /v1/${path} -> ${status2 === 0 ? "no response" : status2}: ${detail}`);
|
|
23
|
-
const address = baoVariable(env, "AGENT_ADDR") || baoVariable(env, "ADDR") || "https://127.0.0.1:8200";
|
|
24
|
-
const socket = address.startsWith("unix://") ? address.slice("unix://".length) : undefined;
|
|
25
|
-
const base = socket === undefined ? address.replace(/\/+$/, "") : "http://localhost";
|
|
26
|
-
const headers = { "X-Vault-Request": "true" };
|
|
27
|
-
const namespace = baoVariable(env, "NAMESPACE");
|
|
28
|
-
if (namespace !== "")
|
|
29
|
-
headers["X-Vault-Namespace"] = namespace;
|
|
30
|
-
const token = baoVariable(env, "TOKEN");
|
|
31
|
-
if (token !== "")
|
|
32
|
-
headers["X-Vault-Token"] = token;
|
|
33
|
-
if (socket !== undefined && !("Bun" in globalThis)) {
|
|
34
|
-
return yield* Effect.fail(refuse(0, "a unix:// address needs Bun fetch; this is not Bun"));
|
|
35
|
-
}
|
|
36
|
-
const client = yield* HttpClient.HttpClient;
|
|
37
|
-
const redacted = yield* Headers.CurrentRedactedNames;
|
|
38
|
-
const defaults = Option.getOrElse(yield* Effect.serviceOption(FetchHttpClient.RequestInit), () => ({}));
|
|
39
|
-
const request = HttpClientRequest.get(`${base}/v1/${path}`).pipe(HttpClientRequest.setHeaders(headers));
|
|
40
|
-
const exchange = client.execute(request).pipe(Effect.flatMap((response) => Effect.map(response.text, (text2) => ({ status: response.status, text: text2 }))), Effect.timeout("60 seconds"), Effect.provideService(Headers.CurrentRedactedNames, [...redacted, "x-vault-token"]));
|
|
41
|
-
const init = { ...defaults, unix: socket };
|
|
42
|
-
const { status, text } = yield* (socket === undefined ? exchange : Effect.provideService(exchange, FetchHttpClient.RequestInit, init)).pipe(Effect.mapError((cause) => refuse(0, cause.message)));
|
|
43
|
-
let parsed;
|
|
44
|
-
try {
|
|
45
|
-
parsed = JSON.parse(text);
|
|
46
|
-
} catch {
|
|
47
|
-
parsed = undefined;
|
|
48
|
-
}
|
|
49
|
-
if (status < 200 || status >= 300) {
|
|
50
|
-
const errors = parsed?.errors;
|
|
51
|
-
const detail = Array.isArray(errors) ? errors.map(String).join("; ") : "not an OpenBao error body";
|
|
52
|
-
return yield* Effect.fail(refuse(status, detail || "(no errors given)"));
|
|
53
|
-
}
|
|
54
|
-
const tokenId = parsed?.data?.token_id;
|
|
55
|
-
const secret = parsed?.data?.secret;
|
|
56
|
-
if (typeof tokenId !== "string" || typeof secret !== "string") {
|
|
57
|
-
return yield* Effect.fail(new Error(`${target.mount}/creds/${role} returned no token_id/secret. Either this approle lacks ` + `the grant, or the mount is not ${target.mount}. Say which mount and role you needed ` + `than falling back to a stored credential -- every stored PVE token is read-only.`));
|
|
58
|
-
}
|
|
59
|
-
const lease = parsed?.lease_duration;
|
|
60
|
-
const leaseSeconds = typeof lease === "number" ? lease : 0;
|
|
61
|
-
return { leaseSeconds, secret, tokenId };
|
|
62
|
-
});
|
|
63
|
-
var authorization = (credential, target) => target.scheme === "pbs" ? `PBSAPIToken=${credential.tokenId}:${credential.secret}` : `PVEAPIToken=${credential.tokenId}=${credential.secret}`;
|
|
64
|
-
|
|
65
|
-
// src/proxmox/lease-cache.ts
|
|
66
|
-
import * as Cache from "effect/Cache";
|
|
67
|
-
import * as Duration from "effect/Duration";
|
|
68
|
-
import * as Effect2 from "effect/Effect";
|
|
69
|
-
import * as Exit from "effect/Exit";
|
|
70
|
-
var REMINT_MARGIN_SECONDS = 60;
|
|
71
|
-
var keyOf = (target, role) => ({
|
|
72
|
-
mount: target.mount,
|
|
73
|
-
role,
|
|
74
|
-
scheme: target.scheme
|
|
75
|
-
});
|
|
76
|
-
var timeToLive = (exit) => Exit.isSuccess(exit) && exit.value.leaseSeconds > REMINT_MARGIN_SECONDS ? Duration.seconds(exit.value.leaseSeconds - REMINT_MARGIN_SECONDS) : Duration.zero;
|
|
77
|
-
var makeLeases = (mintFor) => Cache.makeWith(mintFor, { capacity: 64, requireServicesAt: "lookup", timeToLive });
|
|
78
|
-
var targetForKey = (key) => key.scheme === "pbs" ? { api: "https://pbs.invalid/api2/json", mount: key.mount, scheme: "pbs" } : { members: [], mount: key.mount, scheme: "pve" };
|
|
79
|
-
var leases = Effect2.runSync(makeLeases((key) => mint(targetForKey(key), key.role)));
|
|
80
|
-
var leased = (target, role) => Cache.get(leases, keyOf(target, role));
|
|
81
|
-
|
|
82
|
-
// src/proxmox/members.ts
|
|
83
|
-
import * as Effect3 from "effect/Effect";
|
|
84
|
-
import * as Result from "effect/Result";
|
|
85
|
-
import * as HttpClient3 from "effect/unstable/http/HttpClient";
|
|
86
|
-
import * as HttpClientError from "effect/unstable/http/HttpClientError";
|
|
87
|
-
var PVE_API_PORT = 8006;
|
|
88
|
-
var pveApiBase = (member) => `https://${member}:${String(PVE_API_PORT)}/api2/json`;
|
|
89
|
-
var lastGoodByMount = new Map;
|
|
90
|
-
var noteGoodMember = (target, member) => {
|
|
91
|
-
lastGoodByMount.set(target.mount, member);
|
|
92
|
-
};
|
|
93
|
-
var orderedMembers = (target) => {
|
|
94
|
-
const preferred = lastGoodByMount.get(target.mount);
|
|
95
|
-
if (preferred === undefined || !target.members.includes(preferred))
|
|
96
|
-
return target.members;
|
|
97
|
-
return [preferred, ...target.members.filter((member) => member !== preferred)];
|
|
98
|
-
};
|
|
99
|
-
var errorCode = (cause) => {
|
|
100
|
-
if (cause !== null && typeof cause === "object") {
|
|
101
|
-
if ("code" in cause && typeof cause.code === "string")
|
|
102
|
-
return cause.code;
|
|
103
|
-
if ("cause" in cause)
|
|
104
|
-
return errorCode(cause.cause);
|
|
105
|
-
}
|
|
106
|
-
return;
|
|
107
|
-
};
|
|
108
|
-
var isTimeoutCause = (cause) => {
|
|
109
|
-
const code = errorCode(cause);
|
|
110
|
-
if (code === "AbortError" || code === "ETIMEDOUT" || code === "UND_ERR_CONNECT_TIMEOUT") {
|
|
111
|
-
return true;
|
|
112
|
-
}
|
|
113
|
-
return cause instanceof Error && /timed out|timeout/i.test(cause.message);
|
|
114
|
-
};
|
|
115
|
-
var PRE_SEND_CODES = new Set(["ConnectionRefused", "ECONNREFUSED", "ENOTFOUND", "EAI_AGAIN"]);
|
|
116
|
-
var isCertificateCode = (code) => /^(ERR_TLS_CERT_|CERT_|UNABLE_TO_VERIFY_|DEPTH_ZERO_SELF_SIGNED|SELF_SIGNED_CERT)/.test(code);
|
|
117
|
-
var isPreSendCause = (cause) => {
|
|
118
|
-
const code = errorCode(cause);
|
|
119
|
-
return code !== undefined && (PRE_SEND_CODES.has(code) || isCertificateCode(code));
|
|
120
|
-
};
|
|
121
|
-
var isTransportFailure = (cause) => {
|
|
122
|
-
if (!HttpClientError.isHttpClientError(cause))
|
|
123
|
-
return false;
|
|
124
|
-
const tag = cause.reason._tag;
|
|
125
|
-
return tag === "TransportError" || tag === "InvalidUrlError" || tag === "EncodeError";
|
|
126
|
-
};
|
|
127
|
-
var isPreSendTransport = (cause) => {
|
|
128
|
-
if (!HttpClientError.isHttpClientError(cause))
|
|
129
|
-
return false;
|
|
130
|
-
const { reason } = cause;
|
|
131
|
-
if (reason._tag === "InvalidUrlError" || reason._tag === "EncodeError")
|
|
132
|
-
return true;
|
|
133
|
-
if (reason._tag === "TransportError") {
|
|
134
|
-
if (isTimeoutCause(reason.cause))
|
|
135
|
-
return false;
|
|
136
|
-
return isPreSendCause(reason.cause);
|
|
137
|
-
}
|
|
138
|
-
return false;
|
|
139
|
-
};
|
|
140
|
-
var describeTransport = (cause) => {
|
|
141
|
-
if (HttpClientError.isHttpClientError(cause))
|
|
142
|
-
return cause.message;
|
|
143
|
-
return String(cause);
|
|
144
|
-
};
|
|
145
|
-
var executeOnCluster = (target, method, path, build) => Effect3.gen(function* () {
|
|
146
|
-
const client = yield* HttpClient3.HttpClient;
|
|
147
|
-
const isWrite = method !== "GET";
|
|
148
|
-
const attempts = [];
|
|
149
|
-
for (const member of orderedMembers(target)) {
|
|
150
|
-
const outcome = yield* client.execute(build(pveApiBase(member))).pipe(Effect3.result);
|
|
151
|
-
if (Result.isSuccess(outcome)) {
|
|
152
|
-
noteGoodMember(target, member);
|
|
153
|
-
return outcome.success;
|
|
154
|
-
}
|
|
155
|
-
const why = describeTransport(outcome.failure);
|
|
156
|
-
attempts.push({ member, why });
|
|
157
|
-
const mayRetry = isWrite ? isPreSendTransport(outcome.failure) : isTransportFailure(outcome.failure);
|
|
158
|
-
if (!mayRetry) {
|
|
159
|
-
return yield* Effect3.fail({ attempts, method, path, tag: "cluster" });
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
return yield* Effect3.fail({ attempts, method, path, tag: "cluster" });
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
// src/proxmox/client.ts
|
|
166
|
-
class PveError extends Error {
|
|
167
|
-
status;
|
|
168
|
-
method;
|
|
169
|
-
path;
|
|
170
|
-
constructor(status, method, path, detail) {
|
|
171
|
-
super(`PVE ${method} ${path} -> ${String(status)}: ${detail}`);
|
|
172
|
-
this.status = status;
|
|
173
|
-
this.method = method;
|
|
174
|
-
this.path = path;
|
|
175
|
-
this.name = "PveError";
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
var clusterExhausted = (method, path, attempts) => new PveError(0, method, path, `all members failed: ${attempts.map((a) => `${a.member} (${a.why})`).join("; ")}`);
|
|
179
|
-
var encode = (form) => {
|
|
180
|
-
const params = new URLSearchParams;
|
|
181
|
-
for (const [key, value] of Object.entries(form)) {
|
|
182
|
-
if (typeof value === "string")
|
|
183
|
-
params.append(key, value);
|
|
184
|
-
else
|
|
185
|
-
for (const item of value)
|
|
186
|
-
params.append(key, item);
|
|
187
|
-
}
|
|
188
|
-
return params.toString();
|
|
189
|
-
};
|
|
190
|
-
var buildRequest = (method, apiBase, path, credential, target, form) => {
|
|
191
|
-
const url = `${apiBase}/${path}`;
|
|
192
|
-
return HttpClientRequest2.make(method)(url).pipe(HttpClientRequest2.setHeaders({ Authorization: authorization(credential, target) }), form === undefined ? (self) => self : HttpClientRequest2.bodyText(encode(form), "application/x-www-form-urlencoded"));
|
|
193
|
-
};
|
|
194
|
-
var executeOne = (target, credential, method, path, form, apiBase) => Effect4.gen(function* () {
|
|
195
|
-
const client = yield* HttpClient5.HttpClient;
|
|
196
|
-
const request = buildRequest(method, apiBase, path, credential, target, form);
|
|
197
|
-
return yield* client.execute(request).pipe(Effect4.mapError((cause) => new PveError(0, method, path, String(cause))));
|
|
198
|
-
});
|
|
199
|
-
var readResponse = (response, method, path) => Effect4.gen(function* () {
|
|
200
|
-
if (response.status < 200 || response.status >= 300) {
|
|
201
|
-
const detail = yield* response.text.pipe(Effect4.orElseSucceed(() => ""));
|
|
202
|
-
return yield* Effect4.fail(new PveError(response.status, method, path, detail.slice(0, 300)));
|
|
203
|
-
}
|
|
204
|
-
const body = yield* response.json.pipe(Effect4.mapError((cause) => new PveError(0, method, path, String(cause))));
|
|
205
|
-
return body.data;
|
|
206
|
-
});
|
|
207
|
-
var pveWith = (target, credential, method, path, form) => Effect4.gen(function* () {
|
|
208
|
-
const response = target.scheme === "pve" ? yield* executeOnCluster(target, method, path, (apiBase) => buildRequest(method, apiBase, path, credential, target, form)).pipe(Effect4.mapError((failure) => failure.tag === "cluster" ? clusterExhausted(failure.method, failure.path, failure.attempts) : new PveError(0, method, path, String(failure)))) : yield* executeOne(target, credential, method, path, form, target.api);
|
|
209
|
-
return yield* readResponse(response, method, path);
|
|
210
|
-
});
|
|
211
|
-
var pveEnvelopeWith = (target, credential, method, path) => Effect4.gen(function* () {
|
|
212
|
-
const response = yield* executeOnCluster(target, method, path, (apiBase) => buildRequest(method, apiBase, path, credential, target)).pipe(Effect4.mapError((failure) => failure.tag === "cluster" ? clusterExhausted(failure.method, failure.path, failure.attempts) : new PveError(0, method, path, String(failure))));
|
|
213
|
-
if (response.status < 200 || response.status >= 300) {
|
|
214
|
-
const detail = yield* response.text.pipe(Effect4.orElseSucceed(() => ""));
|
|
215
|
-
return yield* Effect4.fail(new PveError(response.status, method, path, detail.slice(0, 300)));
|
|
216
|
-
}
|
|
217
|
-
return yield* response.json.pipe(Effect4.mapError((cause) => new PveError(0, method, path, String(cause))));
|
|
218
|
-
});
|
|
219
|
-
var pve = (target, role, method, path, form) => Effect4.gen(function* () {
|
|
220
|
-
const credential = yield* leased(target, role);
|
|
221
|
-
return yield* pveWith(target, credential, method, path, form);
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
// src/proxmox/resource.ts
|
|
225
3
|
import { isResolved } from "alchemy/Diff";
|
|
226
|
-
import * as
|
|
227
|
-
|
|
228
|
-
// src/proxmox/resource-guard.ts
|
|
4
|
+
import * as Provider from "alchemy/Provider";
|
|
5
|
+
import * as access from "@distilled.cloud/proxmox/access";
|
|
229
6
|
import * as Effect6 from "effect/Effect";
|
|
230
7
|
|
|
231
8
|
// src/proxmox/constraint-guard.ts
|
|
232
|
-
import * as
|
|
9
|
+
import * as Effect from "effect/Effect";
|
|
233
10
|
|
|
234
11
|
// src/proxmox/constraints.ts
|
|
235
12
|
var lengthOf = (value) => [...value].length;
|
|
@@ -1187,151 +964,283 @@ var constraintsFor = (key) => {
|
|
|
1187
964
|
return table;
|
|
1188
965
|
};
|
|
1189
966
|
var formViolations = (key, form, presence) => violations(constraintsFor(key), form, { presence });
|
|
1190
|
-
var guardForm = (key, form, presence) =>
|
|
967
|
+
var guardForm = (key, form, presence) => Effect.suspend(() => {
|
|
1191
968
|
if (key === undefined)
|
|
1192
|
-
return
|
|
969
|
+
return Effect.void;
|
|
1193
970
|
const found = formViolations(key, form, presence);
|
|
1194
|
-
return found.length === 0 ?
|
|
971
|
+
return found.length === 0 ? Effect.void : Effect.die(new Error(refusal(key, found)));
|
|
1195
972
|
});
|
|
1196
973
|
|
|
1197
|
-
// src/proxmox/
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
return create === undefined ? Effect6.void : guardForm(create, spec.createForm(props), presence);
|
|
1203
|
-
});
|
|
1204
|
-
const guardUpdate = (props) => Effect6.suspend(() => {
|
|
1205
|
-
const update = endpointsOf(props)?.update;
|
|
1206
|
-
if (update === undefined)
|
|
1207
|
-
return Effect6.void;
|
|
1208
|
-
const form = spec.updateForm?.(props);
|
|
1209
|
-
return form === undefined ? Effect6.void : guardForm(update, form, false);
|
|
1210
|
-
});
|
|
1211
|
-
return { guardCreate, guardUpdate };
|
|
1212
|
-
};
|
|
974
|
+
// src/proxmox/distilled-pve.ts
|
|
975
|
+
import * as Effect5 from "effect/Effect";
|
|
976
|
+
import * as Result2 from "effect/Result";
|
|
977
|
+
import { credentials as distilledPveCredentials } from "@distilled.cloud/proxmox/Credentials";
|
|
978
|
+
import * as ProxmoxRetry from "@distilled.cloud/proxmox/Retry";
|
|
1213
979
|
|
|
1214
|
-
// src/proxmox/
|
|
1215
|
-
|
|
980
|
+
// src/proxmox/lease-cache.ts
|
|
981
|
+
import * as Cache from "effect/Cache";
|
|
982
|
+
import * as Duration from "effect/Duration";
|
|
983
|
+
import * as Effect3 from "effect/Effect";
|
|
984
|
+
import * as Exit from "effect/Exit";
|
|
1216
985
|
|
|
1217
|
-
// src/proxmox/
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
986
|
+
// src/proxmox/credentials.ts
|
|
987
|
+
import * as Effect2 from "effect/Effect";
|
|
988
|
+
import * as Option from "effect/Option";
|
|
989
|
+
import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
|
|
990
|
+
import * as Headers from "effect/unstable/http/Headers";
|
|
991
|
+
import * as HttpClient from "effect/unstable/http/HttpClient";
|
|
992
|
+
import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest";
|
|
993
|
+
var baoVariable = (env, name) => env[`BAO_${name}`] ?? env[`VAULT_${name}`] ?? "";
|
|
994
|
+
var mint = (target, role, env = process.env) => Effect2.gen(function* () {
|
|
995
|
+
const path = `${target.mount}/creds/${role}`;
|
|
996
|
+
const refuse = (status2, detail) => new Error(`OpenBao GET /v1/${path} -> ${status2 === 0 ? "no response" : status2}: ${detail}`);
|
|
997
|
+
const address = baoVariable(env, "AGENT_ADDR") || baoVariable(env, "ADDR") || "https://127.0.0.1:8200";
|
|
998
|
+
const socket = address.startsWith("unix://") ? address.slice("unix://".length) : undefined;
|
|
999
|
+
const base = socket === undefined ? address.replace(/\/+$/, "") : "http://localhost";
|
|
1000
|
+
const headers = { "X-Vault-Request": "true" };
|
|
1001
|
+
const namespace = baoVariable(env, "NAMESPACE");
|
|
1002
|
+
if (namespace !== "")
|
|
1003
|
+
headers["X-Vault-Namespace"] = namespace;
|
|
1004
|
+
const token = baoVariable(env, "TOKEN");
|
|
1005
|
+
if (token !== "")
|
|
1006
|
+
headers["X-Vault-Token"] = token;
|
|
1007
|
+
if (socket !== undefined && !("Bun" in globalThis)) {
|
|
1008
|
+
return yield* Effect2.fail(refuse(0, "a unix:// address needs Bun fetch; this is not Bun"));
|
|
1009
|
+
}
|
|
1010
|
+
const client = yield* HttpClient.HttpClient;
|
|
1011
|
+
const redacted = yield* Headers.CurrentRedactedNames;
|
|
1012
|
+
const defaults = Option.getOrElse(yield* Effect2.serviceOption(FetchHttpClient.RequestInit), () => ({}));
|
|
1013
|
+
const request = HttpClientRequest.get(`${base}/v1/${path}`).pipe(HttpClientRequest.setHeaders(headers));
|
|
1014
|
+
const exchange = client.execute(request).pipe(Effect2.flatMap((response) => Effect2.map(response.text, (text2) => ({ status: response.status, text: text2 }))), Effect2.timeout("60 seconds"), Effect2.provideService(Headers.CurrentRedactedNames, [...redacted, "x-vault-token"]));
|
|
1015
|
+
const init = { ...defaults, unix: socket };
|
|
1016
|
+
const { status, text } = yield* (socket === undefined ? exchange : Effect2.provideService(exchange, FetchHttpClient.RequestInit, init)).pipe(Effect2.mapError((cause) => refuse(0, cause.message)));
|
|
1017
|
+
let parsed;
|
|
1018
|
+
try {
|
|
1019
|
+
parsed = JSON.parse(text);
|
|
1020
|
+
} catch {
|
|
1021
|
+
parsed = undefined;
|
|
1022
|
+
}
|
|
1023
|
+
if (status < 200 || status >= 300) {
|
|
1024
|
+
const errors = parsed?.errors;
|
|
1025
|
+
const detail = Array.isArray(errors) ? errors.map(String).join("; ") : "not an OpenBao error body";
|
|
1026
|
+
return yield* Effect2.fail(refuse(status, detail || "(no errors given)"));
|
|
1027
|
+
}
|
|
1028
|
+
const tokenId = parsed?.data?.token_id;
|
|
1029
|
+
const secret = parsed?.data?.secret;
|
|
1030
|
+
if (typeof tokenId !== "string" || typeof secret !== "string") {
|
|
1031
|
+
return yield* Effect2.fail(new Error(`${target.mount}/creds/${role} returned no token_id/secret. Either this approle lacks ` + `the grant, or the mount is not ${target.mount}. Say which mount and role you needed ` + `than falling back to a stored credential -- every stored PVE token is read-only.`));
|
|
1032
|
+
}
|
|
1033
|
+
const lease = parsed?.lease_duration;
|
|
1034
|
+
const leaseSeconds = typeof lease === "number" ? lease : 0;
|
|
1035
|
+
return { leaseSeconds, secret, tokenId };
|
|
1036
|
+
});
|
|
1037
|
+
var authorization = (credential, target) => target.scheme === "pbs" ? `PBSAPIToken=${credential.tokenId}:${credential.secret}` : `PVEAPIToken=${credential.tokenId}=${credential.secret}`;
|
|
1038
|
+
|
|
1039
|
+
// src/proxmox/lease-cache.ts
|
|
1040
|
+
var REMINT_MARGIN_SECONDS = 60;
|
|
1041
|
+
var keyOf = (target, role) => ({
|
|
1042
|
+
mount: target.mount,
|
|
1043
|
+
role,
|
|
1044
|
+
scheme: target.scheme
|
|
1045
|
+
});
|
|
1046
|
+
var timeToLive = (exit) => Exit.isSuccess(exit) && exit.value.leaseSeconds > REMINT_MARGIN_SECONDS ? Duration.seconds(exit.value.leaseSeconds - REMINT_MARGIN_SECONDS) : Duration.zero;
|
|
1047
|
+
var makeLeases = (mintFor) => Cache.makeWith(mintFor, { capacity: 64, requireServicesAt: "lookup", timeToLive });
|
|
1048
|
+
var targetForKey = (key) => key.scheme === "pbs" ? { api: "https://pbs.invalid/api2/json", mount: key.mount, scheme: "pbs" } : { members: [], mount: key.mount, scheme: "pve" };
|
|
1049
|
+
var leases = Effect3.runSync(makeLeases((key) => mint(targetForKey(key), key.role)));
|
|
1050
|
+
var leased = (target, role) => Cache.get(leases, keyOf(target, role));
|
|
1051
|
+
|
|
1052
|
+
// src/proxmox/members.ts
|
|
1053
|
+
import * as Cause from "effect/Cause";
|
|
1054
|
+
import * as Effect4 from "effect/Effect";
|
|
1055
|
+
import * as Result from "effect/Result";
|
|
1056
|
+
import * as HttpClient3 from "effect/unstable/http/HttpClient";
|
|
1057
|
+
import * as HttpClientError from "effect/unstable/http/HttpClientError";
|
|
1058
|
+
var PVE_API_PORT = 8006;
|
|
1059
|
+
var pveApiBase = (member) => `https://${member}:${String(PVE_API_PORT)}/api2/json`;
|
|
1060
|
+
var MEMBER_TIMEOUT = "20 seconds";
|
|
1061
|
+
var lastGoodByMount = new Map;
|
|
1062
|
+
var noteGoodMember = (target, member) => {
|
|
1063
|
+
lastGoodByMount.set(target.mount, member);
|
|
1263
1064
|
};
|
|
1264
|
-
var
|
|
1265
|
-
const
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
diff: ({ news, output }) => ops.diff(news, output),
|
|
1270
|
-
reconcile: ({ news }) => ops.reconcile(news),
|
|
1271
|
-
delete: ({ olds }) => ops.destroy(olds)
|
|
1272
|
-
};
|
|
1065
|
+
var orderedMembers = (target) => {
|
|
1066
|
+
const preferred = lastGoodByMount.get(target.mount);
|
|
1067
|
+
if (preferred === undefined || !target.members.includes(preferred))
|
|
1068
|
+
return target.members;
|
|
1069
|
+
return [preferred, ...target.members.filter((member) => member !== preferred)];
|
|
1273
1070
|
};
|
|
1071
|
+
var errorCode = (cause) => {
|
|
1072
|
+
if (cause !== null && typeof cause === "object") {
|
|
1073
|
+
if ("code" in cause && typeof cause.code === "string")
|
|
1074
|
+
return cause.code;
|
|
1075
|
+
if ("cause" in cause)
|
|
1076
|
+
return errorCode(cause.cause);
|
|
1077
|
+
}
|
|
1078
|
+
return;
|
|
1079
|
+
};
|
|
1080
|
+
var isTimeoutCause = (cause) => {
|
|
1081
|
+
const code = errorCode(cause);
|
|
1082
|
+
if (code === "AbortError" || code === "ETIMEDOUT" || code === "UND_ERR_CONNECT_TIMEOUT") {
|
|
1083
|
+
return true;
|
|
1084
|
+
}
|
|
1085
|
+
return cause instanceof Error && /timed out|timeout/i.test(cause.message);
|
|
1086
|
+
};
|
|
1087
|
+
var PRE_SEND_CODES = new Set(["ConnectionRefused", "ECONNREFUSED", "ENOTFOUND", "EAI_AGAIN"]);
|
|
1088
|
+
var isCertificateCode = (code) => /^(ERR_TLS_CERT_|CERT_|UNABLE_TO_VERIFY_|DEPTH_ZERO_SELF_SIGNED|SELF_SIGNED_CERT)/.test(code);
|
|
1089
|
+
var isPreSendCause = (cause) => {
|
|
1090
|
+
const code = errorCode(cause);
|
|
1091
|
+
return code !== undefined && (PRE_SEND_CODES.has(code) || isCertificateCode(code));
|
|
1092
|
+
};
|
|
1093
|
+
var isTransportFailure = (cause) => {
|
|
1094
|
+
if (Cause.isTimeoutError(cause))
|
|
1095
|
+
return true;
|
|
1096
|
+
if (!HttpClientError.isHttpClientError(cause))
|
|
1097
|
+
return false;
|
|
1098
|
+
const tag = cause.reason._tag;
|
|
1099
|
+
return tag === "TransportError" || tag === "InvalidUrlError" || tag === "EncodeError";
|
|
1100
|
+
};
|
|
1101
|
+
var isPreSendTransport = (cause) => {
|
|
1102
|
+
if (!HttpClientError.isHttpClientError(cause))
|
|
1103
|
+
return false;
|
|
1104
|
+
const { reason } = cause;
|
|
1105
|
+
if (reason._tag === "InvalidUrlError" || reason._tag === "EncodeError")
|
|
1106
|
+
return true;
|
|
1107
|
+
if (reason._tag === "TransportError") {
|
|
1108
|
+
if (isTimeoutCause(reason.cause))
|
|
1109
|
+
return false;
|
|
1110
|
+
return isPreSendCause(reason.cause);
|
|
1111
|
+
}
|
|
1112
|
+
return false;
|
|
1113
|
+
};
|
|
1114
|
+
var describeTransport = (cause) => {
|
|
1115
|
+
if (Cause.isTimeoutError(cause))
|
|
1116
|
+
return `timed out after ${String(MEMBER_TIMEOUT)}`;
|
|
1117
|
+
if (HttpClientError.isHttpClientError(cause))
|
|
1118
|
+
return cause.message;
|
|
1119
|
+
return String(cause);
|
|
1120
|
+
};
|
|
1121
|
+
var executeOnCluster = (target, method, path, build, timeout3 = MEMBER_TIMEOUT) => Effect4.gen(function* () {
|
|
1122
|
+
const client = yield* HttpClient3.HttpClient;
|
|
1123
|
+
const isWrite = method !== "GET";
|
|
1124
|
+
const attempts = [];
|
|
1125
|
+
for (const member of orderedMembers(target)) {
|
|
1126
|
+
const outcome = yield* client.execute(build(pveApiBase(member))).pipe(Effect4.timeout(timeout3), Effect4.result);
|
|
1127
|
+
if (Result.isSuccess(outcome)) {
|
|
1128
|
+
noteGoodMember(target, member);
|
|
1129
|
+
return outcome.success;
|
|
1130
|
+
}
|
|
1131
|
+
const why = describeTransport(outcome.failure);
|
|
1132
|
+
attempts.push({ member, why });
|
|
1133
|
+
const mayRetry = isWrite ? isPreSendTransport(outcome.failure) : isTransportFailure(outcome.failure);
|
|
1134
|
+
if (!mayRetry) {
|
|
1135
|
+
return yield* Effect4.fail({ attempts, method, path, tag: "cluster" });
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
return yield* Effect4.fail({ attempts, method, path, tag: "cluster" });
|
|
1139
|
+
});
|
|
1140
|
+
|
|
1141
|
+
// src/proxmox/distilled-pve.ts
|
|
1142
|
+
class PveClusterExhausted extends Error {
|
|
1143
|
+
attempts;
|
|
1144
|
+
constructor(attempts) {
|
|
1145
|
+
super(`all members failed: ${attempts.map((a) => `${a.member} (${a.why})`).join("; ")}`);
|
|
1146
|
+
this.attempts = attempts;
|
|
1147
|
+
this.name = "PveClusterExhausted";
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
var runPveWith = (target, credential, isWrite, op, timeout4 = MEMBER_TIMEOUT) => Effect5.gen(function* () {
|
|
1151
|
+
const attempts = [];
|
|
1152
|
+
for (const member of orderedMembers(target)) {
|
|
1153
|
+
const credLayer = distilledPveCredentials({
|
|
1154
|
+
baseUrl: pveApiBase(member),
|
|
1155
|
+
secret: credential.secret,
|
|
1156
|
+
tokenId: credential.tokenId
|
|
1157
|
+
});
|
|
1158
|
+
const outcome = yield* op.pipe(ProxmoxRetry.none, Effect5.provide(credLayer), Effect5.timeout(timeout4), Effect5.result);
|
|
1159
|
+
if (Result2.isSuccess(outcome)) {
|
|
1160
|
+
noteGoodMember(target, member);
|
|
1161
|
+
return outcome.success;
|
|
1162
|
+
}
|
|
1163
|
+
const mayRetry = isWrite ? isPreSendTransport(outcome.failure) : isTransportFailure(outcome.failure);
|
|
1164
|
+
if (!mayRetry)
|
|
1165
|
+
return yield* Effect5.fail(outcome.failure);
|
|
1166
|
+
attempts.push({ member, why: describeTransport(outcome.failure) });
|
|
1167
|
+
}
|
|
1168
|
+
return yield* Effect5.fail(new PveClusterExhausted(attempts));
|
|
1169
|
+
});
|
|
1170
|
+
var runPve = (target, role, isWrite, op) => Effect5.gen(function* () {
|
|
1171
|
+
const credential = yield* leased(target, role);
|
|
1172
|
+
return yield* runPveWith(target, credential, isWrite, op);
|
|
1173
|
+
});
|
|
1274
1174
|
|
|
1275
1175
|
// src/proxmox/acl.ts
|
|
1276
1176
|
var ProxmoxAcl = Resource("Proxmox.Acl");
|
|
1277
1177
|
var normalize = (raw) => `/${raw.split("/").filter(Boolean).join("/")}`;
|
|
1278
1178
|
var SUBJECT_FIELD = { group: "groups", token: "tokens", user: "users" };
|
|
1179
|
+
var ENDPOINT = "pve:PUT /access/acl";
|
|
1279
1180
|
var tuple = (props) => ({
|
|
1280
1181
|
path: normalize(props.path),
|
|
1281
1182
|
roles: props.roleid,
|
|
1282
1183
|
[SUBJECT_FIELD[props.type]]: props.ugid
|
|
1283
1184
|
});
|
|
1284
1185
|
var bind = (props) => ({
|
|
1285
|
-
|
|
1286
|
-
|
|
1186
|
+
...tuple(props),
|
|
1187
|
+
propagate: props.propagate === false ? "0" : "1"
|
|
1287
1188
|
});
|
|
1189
|
+
var asForm = (body) => Object.fromEntries(Object.entries(body).filter((e) => e[1] !== undefined));
|
|
1190
|
+
var guardWrite = (props) => guardForm(ENDPOINT, asForm(bind(props)), true);
|
|
1288
1191
|
var identity = (grant) => [normalize(grant.path), grant.type, grant.ugid, grant.roleid].join(" ");
|
|
1289
|
-
var find = (
|
|
1192
|
+
var find = (rows, props) => rows.find((row) => row.path === normalize(props.path) && row.type === props.type && row.ugid === props.ugid && row.roleid === props.roleid);
|
|
1290
1193
|
var propagates = (row) => {
|
|
1291
|
-
const value = row
|
|
1194
|
+
const value = row.propagate;
|
|
1292
1195
|
return value === undefined || value === 1 || value === true || value === "1";
|
|
1293
1196
|
};
|
|
1294
|
-
var
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
});
|
|
1314
|
-
var ProxmoxAclProvider = () => Provider.effect(ProxmoxAcl, Effect8.succeed(ProxmoxAcl.Provider.of({
|
|
1315
|
-
list: () => Effect8.succeed([]),
|
|
1316
|
-
read: Effect8.fn(function* ({ olds }) {
|
|
1317
|
-
return yield* ops.read(olds);
|
|
1197
|
+
var attributesOf = (rows, props) => {
|
|
1198
|
+
const row = find(rows, props);
|
|
1199
|
+
return {
|
|
1200
|
+
bound: row !== undefined,
|
|
1201
|
+
path: normalize(props.path),
|
|
1202
|
+
propagate: row !== undefined && propagates(row),
|
|
1203
|
+
roleid: props.roleid,
|
|
1204
|
+
type: props.type,
|
|
1205
|
+
ugid: props.ugid
|
|
1206
|
+
};
|
|
1207
|
+
};
|
|
1208
|
+
var matches = (attributes, props) => attributes.bound && attributes.propagate === (props.propagate !== false);
|
|
1209
|
+
var readAttributes = (props) => runPve(props.target, "provision", false, access.listAccessAcl({})).pipe(Effect6.map((rows) => attributesOf(rows, props)), Effect6.orElseSucceed(() => {
|
|
1210
|
+
return;
|
|
1211
|
+
}));
|
|
1212
|
+
var ProxmoxAclProvider = () => Provider.effect(ProxmoxAcl, Effect6.succeed(ProxmoxAcl.Provider.of({
|
|
1213
|
+
list: () => Effect6.succeed([]),
|
|
1214
|
+
read: Effect6.fn(function* ({ olds }) {
|
|
1215
|
+
return yield* readAttributes(olds);
|
|
1318
1216
|
}),
|
|
1319
|
-
diff:
|
|
1320
|
-
if (output !== undefined &&
|
|
1217
|
+
diff: Effect6.fn(function* ({ news, output }) {
|
|
1218
|
+
if (output !== undefined && isResolved(news) && identity(news) !== identity(output)) {
|
|
1321
1219
|
return { action: "replace" };
|
|
1322
1220
|
}
|
|
1323
|
-
|
|
1221
|
+
if (!isResolved(news))
|
|
1222
|
+
return;
|
|
1223
|
+
yield* guardWrite(news);
|
|
1224
|
+
if (output === undefined)
|
|
1225
|
+
return;
|
|
1226
|
+
const live = yield* readAttributes(news);
|
|
1227
|
+
return live !== undefined && matches(live, news) ? { action: "noop" } : { action: "update" };
|
|
1324
1228
|
}),
|
|
1325
|
-
reconcile:
|
|
1326
|
-
const
|
|
1327
|
-
|
|
1328
|
-
|
|
1229
|
+
reconcile: Effect6.fn(function* ({ news }) {
|
|
1230
|
+
const before = yield* readAttributes(news);
|
|
1231
|
+
yield* guardWrite(news);
|
|
1232
|
+
if (before === undefined || !matches(before, news)) {
|
|
1233
|
+
yield* runPve(news.target, "provision", true, access.putAccessAcl(bind(news)));
|
|
1234
|
+
}
|
|
1235
|
+
const after = yield* readAttributes(news);
|
|
1236
|
+
if (after === undefined || !after.bound) {
|
|
1237
|
+
return yield* Effect6.die(new Error(`access/acl: the write returned no error but ${identity(news)} is still not ` + "bound. Either the PUT did nothing, or the read credential cannot see the " + "grant -- GET /access/acl is filtered to paths you may MODIFY permissions on, " + "so a read-only role is answered [] rather than 403."));
|
|
1329
1238
|
}
|
|
1330
1239
|
return after;
|
|
1331
1240
|
}),
|
|
1332
|
-
delete:
|
|
1333
|
-
const removal = { delete: "1"
|
|
1334
|
-
yield*
|
|
1241
|
+
delete: Effect6.fn(function* ({ olds }) {
|
|
1242
|
+
const removal = { ...tuple(olds), delete: "1" };
|
|
1243
|
+
yield* runPve(olds.target, "provision", true, access.putAccessAcl(removal));
|
|
1335
1244
|
})
|
|
1336
1245
|
})));
|
|
1337
1246
|
// src/proxmox/alertmanager-body.ts
|
|
@@ -1387,7 +1296,7 @@ var alertmanagerAlertBody = (options = {}) => {
|
|
|
1387
1296
|
// src/proxmox/api-token.ts
|
|
1388
1297
|
import { Resource as Resource2 } from "alchemy";
|
|
1389
1298
|
import * as Provider2 from "alchemy/Provider";
|
|
1390
|
-
import * as
|
|
1299
|
+
import * as Effect10 from "effect/Effect";
|
|
1391
1300
|
|
|
1392
1301
|
// src/proxmox/values.ts
|
|
1393
1302
|
var num = (value, fallback) => typeof value === "number" ? value : fallback;
|
|
@@ -1464,26 +1373,171 @@ var apiTokenSpec = {
|
|
|
1464
1373
|
updateForm: shape
|
|
1465
1374
|
};
|
|
1466
1375
|
|
|
1376
|
+
// src/proxmox/resource.ts
|
|
1377
|
+
import { isResolved as isResolved2 } from "alchemy/Diff";
|
|
1378
|
+
import * as Effect9 from "effect/Effect";
|
|
1379
|
+
|
|
1380
|
+
// src/proxmox/client.ts
|
|
1381
|
+
import * as Effect7 from "effect/Effect";
|
|
1382
|
+
import * as HttpClient5 from "effect/unstable/http/HttpClient";
|
|
1383
|
+
import * as HttpClientRequest2 from "effect/unstable/http/HttpClientRequest";
|
|
1384
|
+
class PveError extends Error {
|
|
1385
|
+
status;
|
|
1386
|
+
method;
|
|
1387
|
+
path;
|
|
1388
|
+
constructor(status, method, path, detail) {
|
|
1389
|
+
super(`PVE ${method} ${path} -> ${String(status)}: ${detail}`);
|
|
1390
|
+
this.status = status;
|
|
1391
|
+
this.method = method;
|
|
1392
|
+
this.path = path;
|
|
1393
|
+
this.name = "PveError";
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
var clusterExhausted = (method, path, attempts) => new PveError(0, method, path, `all members failed: ${attempts.map((a) => `${a.member} (${a.why})`).join("; ")}`);
|
|
1397
|
+
var encode = (form) => {
|
|
1398
|
+
const params = new URLSearchParams;
|
|
1399
|
+
for (const [key, value] of Object.entries(form)) {
|
|
1400
|
+
if (typeof value === "string")
|
|
1401
|
+
params.append(key, value);
|
|
1402
|
+
else
|
|
1403
|
+
for (const item of value)
|
|
1404
|
+
params.append(key, item);
|
|
1405
|
+
}
|
|
1406
|
+
return params.toString();
|
|
1407
|
+
};
|
|
1408
|
+
var buildRequest = (method, apiBase, path, credential, target, form) => {
|
|
1409
|
+
const url = `${apiBase}/${path}`;
|
|
1410
|
+
return HttpClientRequest2.make(method)(url).pipe(HttpClientRequest2.setHeaders({ Authorization: authorization(credential, target) }), form === undefined ? (self) => self : HttpClientRequest2.bodyText(encode(form), "application/x-www-form-urlencoded"));
|
|
1411
|
+
};
|
|
1412
|
+
var executeOne = (target, credential, method, path, form, apiBase) => Effect7.gen(function* () {
|
|
1413
|
+
const client = yield* HttpClient5.HttpClient;
|
|
1414
|
+
const request = buildRequest(method, apiBase, path, credential, target, form);
|
|
1415
|
+
return yield* client.execute(request).pipe(Effect7.mapError((cause) => new PveError(0, method, path, String(cause))));
|
|
1416
|
+
});
|
|
1417
|
+
var readResponse = (response, method, path) => Effect7.gen(function* () {
|
|
1418
|
+
if (response.status < 200 || response.status >= 300) {
|
|
1419
|
+
const detail = yield* response.text.pipe(Effect7.orElseSucceed(() => ""));
|
|
1420
|
+
return yield* Effect7.fail(new PveError(response.status, method, path, detail.slice(0, 300)));
|
|
1421
|
+
}
|
|
1422
|
+
const body = yield* response.json.pipe(Effect7.mapError((cause) => new PveError(0, method, path, String(cause))));
|
|
1423
|
+
return body.data;
|
|
1424
|
+
});
|
|
1425
|
+
var pveWith = (target, credential, method, path, form) => Effect7.gen(function* () {
|
|
1426
|
+
const response = target.scheme === "pve" ? yield* executeOnCluster(target, method, path, (apiBase) => buildRequest(method, apiBase, path, credential, target, form)).pipe(Effect7.mapError((failure) => failure.tag === "cluster" ? clusterExhausted(failure.method, failure.path, failure.attempts) : new PveError(0, method, path, String(failure)))) : yield* executeOne(target, credential, method, path, form, target.api);
|
|
1427
|
+
return yield* readResponse(response, method, path);
|
|
1428
|
+
});
|
|
1429
|
+
var pveEnvelopeWith = (target, credential, method, path) => Effect7.gen(function* () {
|
|
1430
|
+
const response = yield* executeOnCluster(target, method, path, (apiBase) => buildRequest(method, apiBase, path, credential, target)).pipe(Effect7.mapError((failure) => failure.tag === "cluster" ? clusterExhausted(failure.method, failure.path, failure.attempts) : new PveError(0, method, path, String(failure))));
|
|
1431
|
+
if (response.status < 200 || response.status >= 300) {
|
|
1432
|
+
const detail = yield* response.text.pipe(Effect7.orElseSucceed(() => ""));
|
|
1433
|
+
return yield* Effect7.fail(new PveError(response.status, method, path, detail.slice(0, 300)));
|
|
1434
|
+
}
|
|
1435
|
+
return yield* response.json.pipe(Effect7.mapError((cause) => new PveError(0, method, path, String(cause))));
|
|
1436
|
+
});
|
|
1437
|
+
var pve = (target, role, method, path, form) => Effect7.gen(function* () {
|
|
1438
|
+
const credential = yield* leased(target, role);
|
|
1439
|
+
return yield* pveWith(target, credential, method, path, form);
|
|
1440
|
+
});
|
|
1441
|
+
|
|
1442
|
+
// src/proxmox/resource-guard.ts
|
|
1443
|
+
import * as Effect8 from "effect/Effect";
|
|
1444
|
+
var specGuards = (spec) => {
|
|
1445
|
+
const endpointsOf = (props) => typeof spec.endpoint === "function" ? spec.endpoint(props) : spec.endpoint;
|
|
1446
|
+
const guardCreate = (props, presence) => Effect8.suspend(() => {
|
|
1447
|
+
const create = endpointsOf(props)?.create;
|
|
1448
|
+
return create === undefined ? Effect8.void : guardForm(create, spec.createForm(props), presence);
|
|
1449
|
+
});
|
|
1450
|
+
const guardUpdate = (props) => Effect8.suspend(() => {
|
|
1451
|
+
const update = endpointsOf(props)?.update;
|
|
1452
|
+
if (update === undefined)
|
|
1453
|
+
return Effect8.void;
|
|
1454
|
+
const form = spec.updateForm?.(props);
|
|
1455
|
+
return form === undefined ? Effect8.void : guardForm(update, form, false);
|
|
1456
|
+
});
|
|
1457
|
+
return { guardCreate, guardUpdate };
|
|
1458
|
+
};
|
|
1459
|
+
|
|
1460
|
+
// src/proxmox/update-guard.ts
|
|
1461
|
+
var formToSend = (matches2, live, props, form) => matches2(live, props) || Object.keys(form).length === 0 ? undefined : form;
|
|
1462
|
+
|
|
1463
|
+
// src/proxmox/resource.ts
|
|
1464
|
+
var pveOperations = (spec) => {
|
|
1465
|
+
const { guardCreate, guardUpdate } = specGuards(spec);
|
|
1466
|
+
const read = (props) => pve(props.target, spec.readRole ?? "read", "GET", spec.path(props)).pipe(Effect9.map((data) => data == null ? undefined : spec.attributes(data, props)), Effect9.orElseSucceed(() => {
|
|
1467
|
+
return;
|
|
1468
|
+
}));
|
|
1469
|
+
return {
|
|
1470
|
+
guardCreate,
|
|
1471
|
+
guardUpdate,
|
|
1472
|
+
read,
|
|
1473
|
+
diff: (news, output) => Effect9.gen(function* () {
|
|
1474
|
+
if (!isResolved2(news))
|
|
1475
|
+
return;
|
|
1476
|
+
yield* guardCreate(news, output === undefined);
|
|
1477
|
+
yield* guardUpdate(news);
|
|
1478
|
+
if (output === undefined)
|
|
1479
|
+
return;
|
|
1480
|
+
const live = yield* read(news);
|
|
1481
|
+
if (live === undefined) {
|
|
1482
|
+
yield* guardCreate(news, true);
|
|
1483
|
+
return { action: "update" };
|
|
1484
|
+
}
|
|
1485
|
+
if (spec.matches(live, news))
|
|
1486
|
+
return { action: "noop" };
|
|
1487
|
+
return spec.updateForm === undefined ? { action: "replace" } : { action: "update" };
|
|
1488
|
+
}),
|
|
1489
|
+
reconcile: (news) => Effect9.gen(function* () {
|
|
1490
|
+
const live = yield* read(news);
|
|
1491
|
+
yield* guardCreate(news, live === undefined);
|
|
1492
|
+
yield* guardUpdate(news);
|
|
1493
|
+
if (live === undefined) {
|
|
1494
|
+
yield* pve(news.target, "provision", "POST", spec.collection(news), spec.createForm(news));
|
|
1495
|
+
} else if (spec.updateForm !== undefined) {
|
|
1496
|
+
const form = formToSend(spec.matches, live, news, spec.updateForm(news));
|
|
1497
|
+
if (form !== undefined) {
|
|
1498
|
+
yield* pve(news.target, "provision", "PUT", spec.path(news), form);
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
const after = yield* read(news);
|
|
1502
|
+
if (after === undefined) {
|
|
1503
|
+
return yield* Effect9.die(new Error(`${spec.path(news)}: the write returned no error but the object is still absent. ` + 'PVE wraps every answer in {"data":...} and can report success on a call that did ' + "nothing -- read back rather than trusting the status code."));
|
|
1504
|
+
}
|
|
1505
|
+
return after;
|
|
1506
|
+
}),
|
|
1507
|
+
destroy: (olds) => pve(olds.target, "provision", "DELETE", spec.path(olds))
|
|
1508
|
+
};
|
|
1509
|
+
};
|
|
1510
|
+
var pveHandlers = (spec) => {
|
|
1511
|
+
const ops = pveOperations(spec);
|
|
1512
|
+
return {
|
|
1513
|
+
list: () => Effect9.succeed([]),
|
|
1514
|
+
read: ({ olds }) => ops.read(olds),
|
|
1515
|
+
diff: ({ news, output }) => ops.diff(news, output),
|
|
1516
|
+
reconcile: ({ news }) => ops.reconcile(news),
|
|
1517
|
+
delete: ({ olds }) => ops.destroy(olds)
|
|
1518
|
+
};
|
|
1519
|
+
};
|
|
1520
|
+
|
|
1467
1521
|
// src/proxmox/api-token.ts
|
|
1468
1522
|
var ProxmoxApiToken = Resource2("Proxmox.ApiToken", {
|
|
1469
1523
|
defaultRemovalPolicy: "retain"
|
|
1470
1524
|
});
|
|
1471
|
-
var
|
|
1525
|
+
var ops = pveOperations(apiTokenSpec);
|
|
1472
1526
|
var handlers = {
|
|
1473
1527
|
...pveHandlers(apiTokenSpec),
|
|
1474
|
-
reconcile:
|
|
1475
|
-
const live = yield*
|
|
1528
|
+
reconcile: Effect10.fn(function* ({ news }) {
|
|
1529
|
+
const live = yield* ops.read(news);
|
|
1476
1530
|
if (live === undefined) {
|
|
1477
|
-
return yield*
|
|
1531
|
+
return yield* Effect10.die(new Error(`${news.userid}!${news.tokenid}: this resource does not create API tokens. PVE returns ` + "the secret only in the create response and it cannot be stored, so a token made here " + "would be a live credential nobody holds. Create it with `pveum user token add` and " + "capture the value, or mint a short-lived one from the OpenBao proxmox mount " + "(`bao read proxmox-c1/creds/<role>`) -- then declare it here to manage it."));
|
|
1478
1532
|
}
|
|
1479
|
-
return yield*
|
|
1533
|
+
return yield* ops.reconcile(news);
|
|
1480
1534
|
})
|
|
1481
1535
|
};
|
|
1482
|
-
var ProxmoxApiTokenProvider = () => Provider2.effect(ProxmoxApiToken,
|
|
1536
|
+
var ProxmoxApiTokenProvider = () => Provider2.effect(ProxmoxApiToken, Effect10.succeed(ProxmoxApiToken.Provider.of(handlers)));
|
|
1483
1537
|
// src/proxmox/backup-job.ts
|
|
1484
1538
|
import { Resource as Resource3 } from "alchemy";
|
|
1485
1539
|
import * as Provider3 from "alchemy/Provider";
|
|
1486
|
-
import * as
|
|
1540
|
+
import * as Effect11 from "effect/Effect";
|
|
1487
1541
|
var ProxmoxBackupJob = Resource3("Proxmox.BackupJob");
|
|
1488
1542
|
var shape2 = (props) => ({
|
|
1489
1543
|
all: props.all === true ? "1" : "0",
|
|
@@ -1527,11 +1581,11 @@ var handlers2 = pveHandlers({
|
|
|
1527
1581
|
path: (props) => `cluster/backup/${props.id}`,
|
|
1528
1582
|
updateForm: shape2
|
|
1529
1583
|
});
|
|
1530
|
-
var ProxmoxBackupJobProvider = () => Provider3.effect(ProxmoxBackupJob,
|
|
1584
|
+
var ProxmoxBackupJobProvider = () => Provider3.effect(ProxmoxBackupJob, Effect11.succeed(ProxmoxBackupJob.Provider.of(handlers2)));
|
|
1531
1585
|
// src/proxmox/ceph-daemon.ts
|
|
1532
1586
|
import { Resource as Resource4 } from "alchemy";
|
|
1533
1587
|
import * as Provider4 from "alchemy/Provider";
|
|
1534
|
-
import * as
|
|
1588
|
+
import * as Effect12 from "effect/Effect";
|
|
1535
1589
|
|
|
1536
1590
|
// src/proxmox/ceph-daemon-form.ts
|
|
1537
1591
|
var DAEMON_ENDPOINTS = {
|
|
@@ -1582,14 +1636,14 @@ var handlers3 = pveHandlers({
|
|
|
1582
1636
|
matches: () => true,
|
|
1583
1637
|
path: collectionPath
|
|
1584
1638
|
});
|
|
1585
|
-
var ProxmoxCephDaemonProvider = () => Provider4.effect(ProxmoxCephDaemon,
|
|
1639
|
+
var ProxmoxCephDaemonProvider = () => Provider4.effect(ProxmoxCephDaemon, Effect12.succeed(ProxmoxCephDaemon.Provider.of({
|
|
1586
1640
|
...handlers3,
|
|
1587
1641
|
delete: ({ olds }) => pve(olds.target, "provision", "DELETE", daemonPath(olds))
|
|
1588
1642
|
})));
|
|
1589
1643
|
// src/proxmox/ceph-flag.ts
|
|
1590
1644
|
import { Resource as Resource5 } from "alchemy";
|
|
1591
1645
|
import * as Provider5 from "alchemy/Provider";
|
|
1592
|
-
import * as
|
|
1646
|
+
import * as Effect13 from "effect/Effect";
|
|
1593
1647
|
var ProxmoxCephFlag = Resource5("Proxmox.CephFlag");
|
|
1594
1648
|
var handlers4 = pveHandlers({
|
|
1595
1649
|
attributes: (live, props) => ({ flag: props.flag, value: bool(live) }),
|
|
@@ -1600,13 +1654,13 @@ var handlers4 = pveHandlers({
|
|
|
1600
1654
|
path: (props) => `cluster/ceph/flags/${props.flag}`,
|
|
1601
1655
|
updateForm: (props) => ({ value: flag(props.value) ?? "0" })
|
|
1602
1656
|
});
|
|
1603
|
-
var ProxmoxCephFlagProvider = () => Provider5.effect(ProxmoxCephFlag,
|
|
1657
|
+
var ProxmoxCephFlagProvider = () => Provider5.effect(ProxmoxCephFlag, Effect13.succeed(ProxmoxCephFlag.Provider.of({
|
|
1604
1658
|
...handlers4,
|
|
1605
|
-
delete: () =>
|
|
1606
|
-
reconcile:
|
|
1659
|
+
delete: () => Effect13.void,
|
|
1660
|
+
reconcile: Effect13.fn(function* ({ news }) {
|
|
1607
1661
|
const after = yield* handlers4.reconcile({ news });
|
|
1608
1662
|
if (after.value !== news.value) {
|
|
1609
|
-
return yield*
|
|
1663
|
+
return yield* Effect13.die(new Error(`cluster/ceph/flags/${news.flag}: the PUT returned no error but ceph still ` + `reports the flag ${after.value ? "set" : "clear"} rather than ` + `${news.value ? "set" : "clear"}. This endpoint's success is documented as a ` + "null body, so the status code proves nothing -- check `ceph osd dump | " + "head -1` and the cluster log. `pause` in particular is two ceph flags read " + "as one; see the header."));
|
|
1610
1664
|
}
|
|
1611
1665
|
return after;
|
|
1612
1666
|
})
|
|
@@ -1615,10 +1669,10 @@ var ProxmoxCephFlagProvider = () => Provider5.effect(ProxmoxCephFlag, Effect12.s
|
|
|
1615
1669
|
import { Resource as Resource6 } from "alchemy";
|
|
1616
1670
|
import { isResolved as isResolved3 } from "alchemy/Diff";
|
|
1617
1671
|
import * as Provider6 from "alchemy/Provider";
|
|
1618
|
-
import * as
|
|
1672
|
+
import * as Effect15 from "effect/Effect";
|
|
1619
1673
|
|
|
1620
1674
|
// src/proxmox/ceph-fs-wire.ts
|
|
1621
|
-
import * as
|
|
1675
|
+
import * as Effect14 from "effect/Effect";
|
|
1622
1676
|
var readRow = (live, props) => {
|
|
1623
1677
|
const row = (Array.isArray(live) ? live : []).filter((entry) => typeof entry === "object" && entry !== null).find((entry) => entry["name"] === props.name);
|
|
1624
1678
|
if (row === undefined)
|
|
@@ -1646,29 +1700,29 @@ var destroyPath = (props) => {
|
|
|
1646
1700
|
].filter((part) => part !== undefined);
|
|
1647
1701
|
return query.length === 0 ? objectPath(props) : `${objectPath(props)}?${query.join("&")}`;
|
|
1648
1702
|
};
|
|
1649
|
-
var settle = (target, node, upid, what) =>
|
|
1703
|
+
var settle = (target, node, upid, what) => Effect14.gen(function* () {
|
|
1650
1704
|
for (let attempt = 0;attempt < POLL_ATTEMPTS; attempt += 1) {
|
|
1651
|
-
const status = yield* pve(target, "read", "GET", `nodes/${node}/tasks/${encodeURIComponent(upid)}/status`).pipe(
|
|
1705
|
+
const status = yield* pve(target, "read", "GET", `nodes/${node}/tasks/${encodeURIComponent(upid)}/status`).pipe(Effect14.orElseSucceed(() => {
|
|
1652
1706
|
return;
|
|
1653
1707
|
}));
|
|
1654
1708
|
if (status?.status === "stopped") {
|
|
1655
1709
|
const exit = text(status.exitstatus);
|
|
1656
1710
|
if (exit === "OK")
|
|
1657
1711
|
return;
|
|
1658
|
-
return yield*
|
|
1712
|
+
return yield* Effect14.die(new Error(`${what}: PVE task ${upid} finished with "${exit === "" ? "no exit status" : exit}". ` + "The write returned HTTP 200 because it only forked the worker -- the real error is " + `in \`pvesh get /nodes/${node}/tasks/${upid}/log\`.`));
|
|
1659
1713
|
}
|
|
1660
|
-
yield*
|
|
1714
|
+
yield* Effect14.sleep(POLL_SECONDS * 1000);
|
|
1661
1715
|
}
|
|
1662
|
-
return yield*
|
|
1716
|
+
return yield* Effect14.die(new Error(`${what}: PVE task ${upid} was still running after ` + `${String(POLL_ATTEMPTS * POLL_SECONDS)}s, or its ` + "status could not be read. Check the task log, and check that the `read` role holds " + `Sys.Audit on /nodes/${node} -- each call mints a new token, so the poller is never the ` + "task owner and is answered 403 rather than told to wait."));
|
|
1663
1717
|
});
|
|
1664
|
-
var createFs = (props) =>
|
|
1718
|
+
var createFs = (props) => Effect14.gen(function* () {
|
|
1665
1719
|
const upid = text(yield* pve(props.target, "provision", "POST", objectPath(props), createForm2(props)));
|
|
1666
1720
|
if (upid === "") {
|
|
1667
|
-
return yield*
|
|
1721
|
+
return yield* Effect14.die(new Error(`${objectPath(props)}: POST returned no task id. PVE wraps every answer in ` + '{"data":...} and can report success on a call that did nothing.'));
|
|
1668
1722
|
}
|
|
1669
1723
|
yield* settle(props.target, props.node, upid, objectPath(props));
|
|
1670
1724
|
});
|
|
1671
|
-
var destroyFs = (props) =>
|
|
1725
|
+
var destroyFs = (props) => Effect14.gen(function* () {
|
|
1672
1726
|
const upid = text(yield* pve(props.target, "provision", "DELETE", destroyPath(props)));
|
|
1673
1727
|
if (upid !== "")
|
|
1674
1728
|
yield* settle(props.target, props.node, upid, destroyPath(props));
|
|
@@ -1680,7 +1734,7 @@ var notDestroyed = (props) => new Error(`${objectPath(props)}: the destroy task
|
|
|
1680
1734
|
var ProxmoxCephFs = Resource6("Proxmox.CephFs", {
|
|
1681
1735
|
defaultRemovalPolicy: "retain"
|
|
1682
1736
|
});
|
|
1683
|
-
var
|
|
1737
|
+
var ops2 = pveOperations({
|
|
1684
1738
|
attributes: readRow,
|
|
1685
1739
|
collection: objectPath,
|
|
1686
1740
|
createForm: createForm2,
|
|
@@ -1688,44 +1742,44 @@ var ops3 = pveOperations({
|
|
|
1688
1742
|
matches: () => true,
|
|
1689
1743
|
path: (props) => `nodes/${props.node}/ceph/fs`
|
|
1690
1744
|
});
|
|
1691
|
-
var ProxmoxCephFsProvider = () => Provider6.effect(ProxmoxCephFs,
|
|
1692
|
-
list: () =>
|
|
1693
|
-
read:
|
|
1694
|
-
return yield*
|
|
1745
|
+
var ProxmoxCephFsProvider = () => Provider6.effect(ProxmoxCephFs, Effect15.succeed(ProxmoxCephFs.Provider.of({
|
|
1746
|
+
list: () => Effect15.succeed([]),
|
|
1747
|
+
read: Effect15.fn(function* ({ olds }) {
|
|
1748
|
+
return yield* ops2.read(olds);
|
|
1695
1749
|
}),
|
|
1696
|
-
diff:
|
|
1750
|
+
diff: Effect15.fn(function* ({ news, output }) {
|
|
1697
1751
|
if (output !== undefined && isResolved3(news) && news.name !== output.name) {
|
|
1698
1752
|
return { action: "replace" };
|
|
1699
1753
|
}
|
|
1700
|
-
return yield*
|
|
1754
|
+
return yield* ops2.diff(news, output);
|
|
1701
1755
|
}),
|
|
1702
|
-
reconcile:
|
|
1703
|
-
const live = yield*
|
|
1756
|
+
reconcile: Effect15.fn(function* ({ news }) {
|
|
1757
|
+
const live = yield* ops2.read(news);
|
|
1704
1758
|
if (live !== undefined)
|
|
1705
1759
|
return live;
|
|
1706
1760
|
yield* createFs(news);
|
|
1707
|
-
const after = yield*
|
|
1761
|
+
const after = yield* ops2.read(news);
|
|
1708
1762
|
if (after === undefined)
|
|
1709
|
-
return yield*
|
|
1763
|
+
return yield* Effect15.die(notCreated(news));
|
|
1710
1764
|
return after;
|
|
1711
1765
|
}),
|
|
1712
|
-
delete:
|
|
1713
|
-
if ((yield*
|
|
1766
|
+
delete: Effect15.fn(function* ({ olds }) {
|
|
1767
|
+
if ((yield* ops2.read(olds)) === undefined)
|
|
1714
1768
|
return;
|
|
1715
1769
|
yield* destroyFs(olds);
|
|
1716
|
-
const after = yield*
|
|
1770
|
+
const after = yield* ops2.read(olds);
|
|
1717
1771
|
if (after !== undefined)
|
|
1718
|
-
return yield*
|
|
1772
|
+
return yield* Effect15.die(notDestroyed(olds));
|
|
1719
1773
|
})
|
|
1720
1774
|
})));
|
|
1721
1775
|
// src/proxmox/ceph-osd.ts
|
|
1722
1776
|
import { Resource as Resource7 } from "alchemy";
|
|
1723
1777
|
import { isResolved as isResolved4 } from "alchemy/Diff";
|
|
1724
1778
|
import * as Provider7 from "alchemy/Provider";
|
|
1725
|
-
import * as
|
|
1779
|
+
import * as Effect18 from "effect/Effect";
|
|
1726
1780
|
|
|
1727
1781
|
// src/proxmox/ceph-osd-tree.ts
|
|
1728
|
-
import * as
|
|
1782
|
+
import * as Effect16 from "effect/Effect";
|
|
1729
1783
|
var branches = (node) => (Array.isArray(node["children"]) ? node["children"] : []).filter((child) => typeof child === "object" && child !== null);
|
|
1730
1784
|
var osdLeaves = (tree) => {
|
|
1731
1785
|
const root = tree["root"];
|
|
@@ -1751,29 +1805,29 @@ var osdAttributes = (leaf, props) => ({
|
|
|
1751
1805
|
status: text(leaf["status"]),
|
|
1752
1806
|
total_space: num(leaf["total_space"], 0)
|
|
1753
1807
|
});
|
|
1754
|
-
var readOsd = (props) => pve(props.target, "read", "GET", `nodes/${props.node}/ceph/osd`).pipe(
|
|
1808
|
+
var readOsd = (props) => pve(props.target, "read", "GET", `nodes/${props.node}/ceph/osd`).pipe(Effect16.map((t) => {
|
|
1755
1809
|
const leaf = t === undefined ? undefined : findOsd(t, props.osdid);
|
|
1756
1810
|
return leaf === undefined ? undefined : osdAttributes(leaf, props);
|
|
1757
1811
|
}));
|
|
1758
1812
|
|
|
1759
1813
|
// src/proxmox/ceph-osd-write.ts
|
|
1760
|
-
import * as
|
|
1814
|
+
import * as Effect17 from "effect/Effect";
|
|
1761
1815
|
var OSD_CREATE_ENDPOINT = "pve:POST /nodes/{node}/ceph/osd";
|
|
1762
1816
|
var describe = (props) => `osd.${String(props.osdid)} on ${props.node}`;
|
|
1763
1817
|
var createOsdForm = (props, dev) => ({
|
|
1764
1818
|
dev,
|
|
1765
1819
|
...props.device_class === undefined ? {} : { "crush-device-class": props.device_class }
|
|
1766
1820
|
});
|
|
1767
|
-
var createOsd =
|
|
1821
|
+
var createOsd = Effect17.fn(function* (news) {
|
|
1768
1822
|
if (news.dev === undefined) {
|
|
1769
|
-
return yield*
|
|
1823
|
+
return yield* Effect17.die(new Error(`${describe(news)}: no such OSD in the CRUSH tree, and no \`dev\` was declared. ` + "Without a device this resource is an ASSERTION over an OSD that must already " + "exist. Declare `dev` to let it create one -- which WIPES that device -- or fix " + "the declaration; if the disk was there yesterday, a replica is missing and Ceph " + "is degraded."));
|
|
1770
1824
|
}
|
|
1771
1825
|
const form = createOsdForm(news, news.dev);
|
|
1772
1826
|
yield* guardForm(OSD_CREATE_ENDPOINT, form, true);
|
|
1773
1827
|
yield* pve(news.target, "provision", "POST", `nodes/${news.node}/ceph/osd`, form);
|
|
1774
1828
|
const created = yield* readOsd(news);
|
|
1775
1829
|
if (created === undefined) {
|
|
1776
|
-
return yield*
|
|
1830
|
+
return yield* Effect17.die(new Error(`${describe(news)}: the create returned no error but osd.${String(news.osdid)} is ` + "still absent. Ceph allocates OSD ids and cannot be told which to use, so it most " + "likely assigned a different number -- check `ceph osd tree`, and declare the id it " + "actually gave the device rather than deploying again."));
|
|
1777
1831
|
}
|
|
1778
1832
|
return created;
|
|
1779
1833
|
});
|
|
@@ -1786,11 +1840,11 @@ var ProxmoxCephOsd = Resource7("Proxmox.CephOsd", {
|
|
|
1786
1840
|
var settled = (live, props) => (props.host === undefined || props.host === live.host) && (props.device_class === undefined || props.device_class === live.device_class);
|
|
1787
1841
|
var describe2 = (props) => `osd.${String(props.osdid)} (read via nodes/${props.node}/ceph/osd)`;
|
|
1788
1842
|
var handlers5 = {
|
|
1789
|
-
list: () =>
|
|
1790
|
-
read:
|
|
1843
|
+
list: () => Effect18.succeed([]),
|
|
1844
|
+
read: Effect18.fn(function* ({ olds }) {
|
|
1791
1845
|
return yield* readOsd(olds);
|
|
1792
1846
|
}),
|
|
1793
|
-
diff:
|
|
1847
|
+
diff: Effect18.fn(function* ({
|
|
1794
1848
|
news,
|
|
1795
1849
|
output
|
|
1796
1850
|
}) {
|
|
@@ -1803,25 +1857,25 @@ var handlers5 = {
|
|
|
1803
1857
|
return { action: "update" };
|
|
1804
1858
|
return settled(live, news) ? { action: "noop" } : { action: "update" };
|
|
1805
1859
|
}),
|
|
1806
|
-
reconcile:
|
|
1860
|
+
reconcile: Effect18.fn(function* ({ news }) {
|
|
1807
1861
|
const live = yield* readOsd(news);
|
|
1808
1862
|
if (live === undefined) {
|
|
1809
1863
|
return yield* createOsd(news);
|
|
1810
1864
|
}
|
|
1811
1865
|
if (!settled(live, news)) {
|
|
1812
|
-
return yield*
|
|
1866
|
+
return yield* Effect18.die(new Error(`${describe2(news)}: exists but not as declared -- live host=${live.host} ` + `device_class=${live.device_class}. Nothing here moves an OSD between hosts or ` + "reclassifies it; fix the declaration, or move it deliberately with `ceph osd crush " + "move` / `ceph osd crush set-device-class` and deploy again."));
|
|
1813
1867
|
}
|
|
1814
1868
|
return live;
|
|
1815
1869
|
}),
|
|
1816
|
-
delete:
|
|
1870
|
+
delete: Effect18.fn(function* ({ olds }) {
|
|
1817
1871
|
yield* destroyOsd(olds);
|
|
1818
1872
|
})
|
|
1819
1873
|
};
|
|
1820
|
-
var ProxmoxCephOsdProvider = () => Provider7.effect(ProxmoxCephOsd,
|
|
1874
|
+
var ProxmoxCephOsdProvider = () => Provider7.effect(ProxmoxCephOsd, Effect18.succeed(ProxmoxCephOsd.Provider.of(handlers5)));
|
|
1821
1875
|
// src/proxmox/ceph-pool.ts
|
|
1822
1876
|
import { Resource as Resource8 } from "alchemy";
|
|
1823
1877
|
import * as Provider8 from "alchemy/Provider";
|
|
1824
|
-
import * as
|
|
1878
|
+
import * as Effect20 from "effect/Effect";
|
|
1825
1879
|
|
|
1826
1880
|
// src/proxmox/ceph-pool-form.ts
|
|
1827
1881
|
var object = (props) => `nodes/${props.node}/ceph/pool/${props.name}`;
|
|
@@ -1850,14 +1904,14 @@ var createBody = (props) => ({
|
|
|
1850
1904
|
});
|
|
1851
1905
|
|
|
1852
1906
|
// src/proxmox/ceph-pool-settle.ts
|
|
1853
|
-
import * as
|
|
1854
|
-
var confirmAbsent = (props, collection2, describe3) => pve(props.target, "read", "GET", collection2).pipe(
|
|
1855
|
-
var settle2 = (props, read, done) =>
|
|
1907
|
+
import * as Effect19 from "effect/Effect";
|
|
1908
|
+
var confirmAbsent = (props, collection2, describe3) => pve(props.target, "read", "GET", collection2).pipe(Effect19.flatMap((rows) => (rows ?? []).some((row) => row.pool_name === props.name) ? Effect19.die(new Error(`${describe3}: the cluster lists this pool but its status could not be read, ` + "so this deploy will NOT create over it. Check that the node is up and that the " + "read role holds Sys.Audit or Datastore.Audit on / -- a create here would apply " + "pg_num to a live pool and start a PG merge.")) : Effect19.void));
|
|
1909
|
+
var settle2 = (props, read, done) => Effect19.gen(function* () {
|
|
1856
1910
|
for (let attempt = 0;attempt < 30; attempt += 1) {
|
|
1857
1911
|
const live = yield* read(props);
|
|
1858
1912
|
if (done(live))
|
|
1859
1913
|
return live;
|
|
1860
|
-
yield*
|
|
1914
|
+
yield* Effect19.sleep("2 seconds");
|
|
1861
1915
|
}
|
|
1862
1916
|
return yield* read(props);
|
|
1863
1917
|
});
|
|
@@ -1898,36 +1952,36 @@ var spec = {
|
|
|
1898
1952
|
path: (props) => `${object(props)}/status?verbose=1`,
|
|
1899
1953
|
updateForm: updateBody
|
|
1900
1954
|
};
|
|
1901
|
-
var
|
|
1902
|
-
var ProxmoxCephPoolProvider = () => Provider8.effect(ProxmoxCephPool,
|
|
1903
|
-
list: () =>
|
|
1904
|
-
read: ({ olds }) =>
|
|
1905
|
-
diff: ({ news, output }) =>
|
|
1906
|
-
reconcile:
|
|
1907
|
-
const live = yield*
|
|
1955
|
+
var ops3 = pveOperations(spec);
|
|
1956
|
+
var ProxmoxCephPoolProvider = () => Provider8.effect(ProxmoxCephPool, Effect20.succeed(ProxmoxCephPool.Provider.of({
|
|
1957
|
+
list: () => Effect20.succeed([]),
|
|
1958
|
+
read: ({ olds }) => ops3.read(olds),
|
|
1959
|
+
diff: ({ news, output }) => ops3.diff(news, output),
|
|
1960
|
+
reconcile: Effect20.fn(function* ({ news }) {
|
|
1961
|
+
const live = yield* ops3.read(news);
|
|
1908
1962
|
let upid;
|
|
1909
1963
|
if (live === undefined) {
|
|
1910
|
-
yield*
|
|
1964
|
+
yield* ops3.guardCreate(news, true);
|
|
1911
1965
|
yield* confirmAbsent(news, spec.collection(news), object(news));
|
|
1912
1966
|
upid = yield* pve(news.target, "provision", "POST", collection(news), createBody(news));
|
|
1913
1967
|
} else {
|
|
1914
1968
|
const form = formToSend(spec.matches, live, news, updateBody(news));
|
|
1915
1969
|
if (form !== undefined) {
|
|
1916
|
-
yield*
|
|
1970
|
+
yield* ops3.guardUpdate(news);
|
|
1917
1971
|
upid = yield* pve(news.target, "provision", "PUT", object(news), form);
|
|
1918
1972
|
}
|
|
1919
1973
|
}
|
|
1920
|
-
const after = yield* settle2(news,
|
|
1974
|
+
const after = yield* settle2(news, ops3.read, (row) => row !== undefined && spec.matches(row, news));
|
|
1921
1975
|
if (after === undefined || !spec.matches(after, news)) {
|
|
1922
|
-
return yield*
|
|
1976
|
+
return yield* Effect20.die(new Error(`${object(news)}: the write returned no error but the pool still does not match ` + `the declaration after 60s. PVE answered with task ${upid ?? "(none)"} -- read ` + "its log with `pvesh get /nodes/<node>/tasks/<upid>/log` to see what Ceph " + "refused."));
|
|
1923
1977
|
}
|
|
1924
1978
|
return after;
|
|
1925
1979
|
}),
|
|
1926
|
-
delete:
|
|
1980
|
+
delete: Effect20.fn(function* ({ olds }) {
|
|
1927
1981
|
yield* pve(olds.target, "provision", "DELETE", object(olds));
|
|
1928
|
-
const left = yield* settle2(olds,
|
|
1982
|
+
const left = yield* settle2(olds, ops3.read, (row) => row === undefined);
|
|
1929
1983
|
if (left !== undefined) {
|
|
1930
|
-
return yield*
|
|
1984
|
+
return yield* Effect20.die(new Error(`${object(olds)}: the DELETE returned no error but the pool is still there after ` + "60s. Either a storage on it still holds RBD images, or the mons are running " + "with mon_allow_pool_delete false."));
|
|
1931
1985
|
}
|
|
1932
1986
|
})
|
|
1933
1987
|
})));
|
|
@@ -1935,7 +1989,7 @@ var ProxmoxCephPoolProvider = () => Provider8.effect(ProxmoxCephPool, Effect19.s
|
|
|
1935
1989
|
import { Resource as Resource9 } from "alchemy";
|
|
1936
1990
|
import { isResolved as isResolved5 } from "alchemy/Diff";
|
|
1937
1991
|
import * as Provider9 from "alchemy/Provider";
|
|
1938
|
-
import * as
|
|
1992
|
+
import * as Effect21 from "effect/Effect";
|
|
1939
1993
|
|
|
1940
1994
|
// src/proxmox/firewall-alias-form.ts
|
|
1941
1995
|
var fold = (name) => name.trim().toLowerCase();
|
|
@@ -1975,9 +2029,9 @@ var handlers6 = pveHandlers({
|
|
|
1975
2029
|
path: (props) => `cluster/firewall/aliases/${props.name}`,
|
|
1976
2030
|
updateForm: body
|
|
1977
2031
|
});
|
|
1978
|
-
var ProxmoxFirewallAliasProvider = () => Provider9.effect(ProxmoxFirewallAlias,
|
|
2032
|
+
var ProxmoxFirewallAliasProvider = () => Provider9.effect(ProxmoxFirewallAlias, Effect21.succeed(ProxmoxFirewallAlias.Provider.of({
|
|
1979
2033
|
...handlers6,
|
|
1980
|
-
diff:
|
|
2034
|
+
diff: Effect21.fn(function* ({ news, output }) {
|
|
1981
2035
|
if (output !== undefined && isResolved5(news) && fold(news.name) !== fold(output.name)) {
|
|
1982
2036
|
return { action: "replace" };
|
|
1983
2037
|
}
|
|
@@ -1987,7 +2041,7 @@ var ProxmoxFirewallAliasProvider = () => Provider9.effect(ProxmoxFirewallAlias,
|
|
|
1987
2041
|
// src/proxmox/group.ts
|
|
1988
2042
|
import { Resource as Resource10 } from "alchemy";
|
|
1989
2043
|
import * as Provider10 from "alchemy/Provider";
|
|
1990
|
-
import * as
|
|
2044
|
+
import * as Effect22 from "effect/Effect";
|
|
1991
2045
|
var ProxmoxGroup = Resource10("Proxmox.Group", {
|
|
1992
2046
|
defaultRemovalPolicy: "retain"
|
|
1993
2047
|
});
|
|
@@ -2012,11 +2066,11 @@ var handlers7 = pveHandlers({
|
|
|
2012
2066
|
path: (props) => `access/groups/${props.groupid}`,
|
|
2013
2067
|
updateForm: (props) => ({ comment: storedComment(props.comment) })
|
|
2014
2068
|
});
|
|
2015
|
-
var ProxmoxGroupProvider = () => Provider10.effect(ProxmoxGroup,
|
|
2069
|
+
var ProxmoxGroupProvider = () => Provider10.effect(ProxmoxGroup, Effect22.succeed(ProxmoxGroup.Provider.of(handlers7)));
|
|
2016
2070
|
// src/proxmox/ha-resource.ts
|
|
2017
2071
|
import { Resource as Resource11 } from "alchemy";
|
|
2018
2072
|
import * as Provider11 from "alchemy/Provider";
|
|
2019
|
-
import * as
|
|
2073
|
+
import * as Effect23 from "effect/Effect";
|
|
2020
2074
|
var ProxmoxHaResource = Resource11("Proxmox.HaResource");
|
|
2021
2075
|
var kind = (sid) => sid.split(":")[0] ?? "";
|
|
2022
2076
|
var shape3 = (props) => ({
|
|
@@ -2044,11 +2098,11 @@ var handlers8 = pveHandlers({
|
|
|
2044
2098
|
path: (props) => `cluster/ha/resources/${props.sid}`,
|
|
2045
2099
|
updateForm: shape3
|
|
2046
2100
|
});
|
|
2047
|
-
var ProxmoxHaResourceProvider = () => Provider11.effect(ProxmoxHaResource,
|
|
2101
|
+
var ProxmoxHaResourceProvider = () => Provider11.effect(ProxmoxHaResource, Effect23.succeed(ProxmoxHaResource.Provider.of(handlers8)));
|
|
2048
2102
|
// src/proxmox/ha-rule.ts
|
|
2049
2103
|
import { Resource as Resource12 } from "alchemy";
|
|
2050
2104
|
import * as Provider12 from "alchemy/Provider";
|
|
2051
|
-
import * as
|
|
2105
|
+
import * as Effect24 from "effect/Effect";
|
|
2052
2106
|
|
|
2053
2107
|
// src/proxmox/ha-rule-form.ts
|
|
2054
2108
|
var nodeList = (value) => {
|
|
@@ -2125,43 +2179,43 @@ var handlers9 = pveHandlers({
|
|
|
2125
2179
|
return withClears(fields, clear);
|
|
2126
2180
|
}
|
|
2127
2181
|
});
|
|
2128
|
-
var ProxmoxHaRuleProvider = () => Provider12.effect(ProxmoxHaRule,
|
|
2182
|
+
var ProxmoxHaRuleProvider = () => Provider12.effect(ProxmoxHaRule, Effect24.succeed(ProxmoxHaRule.Provider.of(handlers9)));
|
|
2129
2183
|
// src/proxmox/lxc.ts
|
|
2130
2184
|
import { Resource as Resource13 } from "alchemy";
|
|
2131
2185
|
import { isResolved as isResolved6 } from "alchemy/Diff";
|
|
2132
2186
|
import * as Provider13 from "alchemy/Provider";
|
|
2133
|
-
import * as
|
|
2187
|
+
import * as Effect34 from "effect/Effect";
|
|
2134
2188
|
|
|
2135
2189
|
// src/ownership/adopt.ts
|
|
2136
2190
|
import { AdoptPolicy } from "alchemy/AdoptPolicy";
|
|
2137
2191
|
import { AlchemyContext } from "alchemy/AlchemyContext";
|
|
2138
2192
|
import { Stack as Stack3 } from "alchemy/Stack";
|
|
2139
|
-
import * as
|
|
2193
|
+
import * as Effect28 from "effect/Effect";
|
|
2140
2194
|
import * as Option5 from "effect/Option";
|
|
2141
2195
|
|
|
2142
2196
|
// src/ownership/resume.ts
|
|
2143
2197
|
import { Unowned } from "alchemy/AdoptPolicy";
|
|
2144
2198
|
import { Artifacts } from "alchemy/Artifacts";
|
|
2145
|
-
import * as
|
|
2199
|
+
import * as Effect25 from "effect/Effect";
|
|
2146
2200
|
import * as Option2 from "effect/Option";
|
|
2147
2201
|
var PROVEN = "homeflare/ownership/resumes";
|
|
2148
2202
|
var UNFINISHED = "homeflare/ownership/unfinished";
|
|
2149
|
-
var note = (key, instanceId) =>
|
|
2150
|
-
const bag = yield*
|
|
2203
|
+
var note = (key, instanceId) => Effect25.gen(function* () {
|
|
2204
|
+
const bag = yield* Effect25.serviceOption(Artifacts);
|
|
2151
2205
|
if (Option2.isSome(bag))
|
|
2152
2206
|
yield* bag.value.set(key, instanceId);
|
|
2153
2207
|
});
|
|
2154
|
-
var noted = (key, instanceId) =>
|
|
2155
|
-
const bag = yield*
|
|
2208
|
+
var noted = (key, instanceId) => Effect25.gen(function* () {
|
|
2209
|
+
const bag = yield* Effect25.serviceOption(Artifacts);
|
|
2156
2210
|
if (Option2.isNone(bag))
|
|
2157
2211
|
return false;
|
|
2158
2212
|
return (yield* bag.value.get(key)) === instanceId;
|
|
2159
2213
|
});
|
|
2160
|
-
var noteUnfinished = (instanceId) =>
|
|
2161
|
-
var noteResume = (instanceId, recovered) =>
|
|
2214
|
+
var noteUnfinished = (instanceId) => Effect25.as(note(UNFINISHED, instanceId), undefined);
|
|
2215
|
+
var noteResume = (instanceId, recovered) => Effect25.gen(function* () {
|
|
2162
2216
|
yield* noteUnfinished(instanceId);
|
|
2163
|
-
const unproven = () =>
|
|
2164
|
-
const found = yield* recovered.pipe(
|
|
2217
|
+
const unproven = () => Effect25.succeed(undefined);
|
|
2218
|
+
const found = yield* recovered.pipe(Effect25.catch(unproven), Effect25.catchDefect(unproven));
|
|
2165
2219
|
if (found === undefined || found === null || Unowned.is(found))
|
|
2166
2220
|
return;
|
|
2167
2221
|
yield* note(PROVEN, instanceId);
|
|
@@ -2172,7 +2226,7 @@ var provingResumes = (service) => {
|
|
|
2172
2226
|
const { diff, read } = service;
|
|
2173
2227
|
if (diff === undefined || read === undefined)
|
|
2174
2228
|
return service;
|
|
2175
|
-
const proving = (input) =>
|
|
2229
|
+
const proving = (input) => Effect25.gen(function* () {
|
|
2176
2230
|
if (input.output === undefined) {
|
|
2177
2231
|
yield* noteResume(input.instanceId, read({ ...input, output: undefined }));
|
|
2178
2232
|
}
|
|
@@ -2184,12 +2238,12 @@ var provingResumes = (service) => {
|
|
|
2184
2238
|
// src/ownership/rows.ts
|
|
2185
2239
|
import { Stack as Stack2 } from "alchemy/Stack";
|
|
2186
2240
|
import { State, isActionState } from "alchemy/State/State";
|
|
2187
|
-
import * as
|
|
2241
|
+
import * as Effect27 from "effect/Effect";
|
|
2188
2242
|
import * as Option4 from "effect/Option";
|
|
2189
2243
|
|
|
2190
2244
|
// src/ownership/whole.ts
|
|
2191
2245
|
import { Stack } from "alchemy/Stack";
|
|
2192
|
-
import * as
|
|
2246
|
+
import * as Effect26 from "effect/Effect";
|
|
2193
2247
|
import * as Option3 from "effect/Option";
|
|
2194
2248
|
var absent = (value) => value === undefined || value === null;
|
|
2195
2249
|
var isPlainObject = (value) => {
|
|
@@ -2215,22 +2269,22 @@ var carries = (declared, recorded) => {
|
|
|
2215
2269
|
}
|
|
2216
2270
|
return true;
|
|
2217
2271
|
};
|
|
2218
|
-
var wholeDeclaration = (fqn, props) =>
|
|
2272
|
+
var wholeDeclaration = (fqn, props) => Effect26.map(Effect26.serviceOption(Stack), (stack) => {
|
|
2219
2273
|
const declared = Option3.isSome(stack) ? stack.value.resources[fqn] : undefined;
|
|
2220
2274
|
return declared !== undefined && carries(declared.Props, props);
|
|
2221
2275
|
});
|
|
2222
2276
|
|
|
2223
2277
|
// src/ownership/rows.ts
|
|
2224
|
-
var storeOf =
|
|
2225
|
-
const stack = yield*
|
|
2226
|
-
const state = yield*
|
|
2278
|
+
var storeOf = Effect27.gen(function* () {
|
|
2279
|
+
const stack = yield* Effect27.serviceOption(Stack2);
|
|
2280
|
+
const state = yield* Effect27.serviceOption(State);
|
|
2227
2281
|
if (Option4.isNone(stack) || Option4.isNone(state))
|
|
2228
2282
|
return;
|
|
2229
2283
|
const store = yield* state.value;
|
|
2230
2284
|
const where = { stack: stack.value.name, stage: stack.value.stage };
|
|
2231
2285
|
return { resources: stack.value.resources, store, where };
|
|
2232
2286
|
});
|
|
2233
|
-
var rowAt = (store, where, fqn) => store.get({ ...where, fqn }).pipe(
|
|
2287
|
+
var rowAt = (store, where, fqn) => store.get({ ...where, fqn }).pipe(Effect27.orElseSucceed(() => {
|
|
2234
2288
|
return;
|
|
2235
2289
|
}));
|
|
2236
2290
|
var generationOf = (row, instanceId) => {
|
|
@@ -2243,7 +2297,7 @@ var generationOf = (row, instanceId) => {
|
|
|
2243
2297
|
}
|
|
2244
2298
|
return;
|
|
2245
2299
|
};
|
|
2246
|
-
var recordedGeneration = (fqn, instanceId) =>
|
|
2300
|
+
var recordedGeneration = (fqn, instanceId) => Effect27.gen(function* () {
|
|
2247
2301
|
const found = yield* storeOf;
|
|
2248
2302
|
if (found === undefined)
|
|
2249
2303
|
return;
|
|
@@ -2254,14 +2308,14 @@ var recordedGeneration = (fqn, instanceId) => Effect26.gen(function* () {
|
|
|
2254
2308
|
}
|
|
2255
2309
|
return;
|
|
2256
2310
|
});
|
|
2257
|
-
var recordedInstance = (fqn, instanceId) =>
|
|
2311
|
+
var recordedInstance = (fqn, instanceId) => Effect27.gen(function* () {
|
|
2258
2312
|
const generation = yield* recordedGeneration(fqn, instanceId);
|
|
2259
2313
|
if (generation === undefined)
|
|
2260
2314
|
return "absent";
|
|
2261
2315
|
return (yield* wholeDeclaration(fqn, generation.props)) ? "whole" : "partial";
|
|
2262
2316
|
});
|
|
2263
|
-
var isCreate = (fqn, instanceId) =>
|
|
2264
|
-
var forgetRefusedCreate = (fqn, instanceId) =>
|
|
2317
|
+
var isCreate = (fqn, instanceId) => Effect27.map(recordedGeneration(fqn, instanceId), (generation) => generation?.status === "creating");
|
|
2318
|
+
var forgetRefusedCreate = (fqn, instanceId) => Effect27.gen(function* () {
|
|
2265
2319
|
const found = yield* storeOf;
|
|
2266
2320
|
if (found === undefined)
|
|
2267
2321
|
return;
|
|
@@ -2270,32 +2324,32 @@ var forgetRefusedCreate = (fqn, instanceId) => Effect26.gen(function* () {
|
|
|
2270
2324
|
if (mine?.status !== "creating" || mine.instanceId !== instanceId || mine.attr !== undefined) {
|
|
2271
2325
|
return;
|
|
2272
2326
|
}
|
|
2273
|
-
yield* found.store.delete({ ...found.where, fqn }).pipe(
|
|
2327
|
+
yield* found.store.delete({ ...found.where, fqn }).pipe(Effect27.ignore);
|
|
2274
2328
|
});
|
|
2275
2329
|
|
|
2276
2330
|
// src/ownership/adopt.ts
|
|
2277
|
-
var adoptEnabled = (fqn) =>
|
|
2278
|
-
const stack = yield*
|
|
2331
|
+
var adoptEnabled = (fqn) => Effect28.gen(function* () {
|
|
2332
|
+
const stack = yield* Effect28.serviceOption(Stack3);
|
|
2279
2333
|
const scoped = Option5.isSome(stack) ? stack.value.resources[fqn]?.Adopt : undefined;
|
|
2280
2334
|
if (scoped !== undefined)
|
|
2281
2335
|
return scoped;
|
|
2282
|
-
const policy = yield*
|
|
2336
|
+
const policy = yield* Effect28.serviceOption(AdoptPolicy);
|
|
2283
2337
|
if (Option5.isSome(policy))
|
|
2284
2338
|
return policy.value;
|
|
2285
|
-
const context = yield*
|
|
2339
|
+
const context = yield* Effect28.serviceOption(AlchemyContext);
|
|
2286
2340
|
return Option5.isSome(context) ? context.value.adopt : false;
|
|
2287
2341
|
});
|
|
2288
|
-
var adoptable = (owner) =>
|
|
2342
|
+
var adoptable = (owner) => Effect28.gen(function* () {
|
|
2289
2343
|
if (yield* isCreate(owner.fqn, owner.instanceId))
|
|
2290
2344
|
return true;
|
|
2291
2345
|
return yield* unfinished(owner.instanceId);
|
|
2292
2346
|
});
|
|
2293
|
-
var adoptsAtApply = (owner) =>
|
|
2347
|
+
var adoptsAtApply = (owner) => Effect28.gen(function* () {
|
|
2294
2348
|
if (!(yield* adoptEnabled(owner.fqn)))
|
|
2295
2349
|
return false;
|
|
2296
2350
|
return yield* adoptable(owner);
|
|
2297
2351
|
});
|
|
2298
|
-
var refuseTakeover = (owner, what) =>
|
|
2352
|
+
var refuseTakeover = (owner, what) => Effect28.gen(function* () {
|
|
2299
2353
|
if (owner.output !== undefined)
|
|
2300
2354
|
return;
|
|
2301
2355
|
if (yield* resumes(owner.instanceId))
|
|
@@ -2305,25 +2359,25 @@ var refuseTakeover = (owner, what) => Effect27.gen(function* () {
|
|
|
2305
2359
|
return;
|
|
2306
2360
|
yield* forgetRefusedCreate(owner.fqn, owner.instanceId);
|
|
2307
2361
|
const remedy = may ? "Deploy with --adopt (or wrap the resource in adopt(true)) to take it over, or remove it first." : "It is the new identity of a replace, which --adopt does not cover: remove it first.";
|
|
2308
|
-
return yield*
|
|
2362
|
+
return yield* Effect28.die(new Error(`${what}: already exists, and this stack holds no state for it — the plan could not ask ` + "first (a prop was still an Output, a replace moved onto it, or it appeared after the " + `plan). Nothing was written. ${remedy}`));
|
|
2309
2363
|
});
|
|
2310
2364
|
var claimFor = (owner) => (what) => refuseTakeover(owner, what);
|
|
2311
2365
|
|
|
2312
2366
|
// src/ownership/probe.ts
|
|
2313
2367
|
import { Unowned as Unowned2 } from "alchemy/AdoptPolicy";
|
|
2314
|
-
import * as
|
|
2315
|
-
var provenOurs = (ask, settled2) =>
|
|
2368
|
+
import * as Effect29 from "effect/Effect";
|
|
2369
|
+
var provenOurs = (ask, settled2) => Effect29.gen(function* () {
|
|
2316
2370
|
const recorded = yield* recordedInstance(ask.fqn, ask.instanceId);
|
|
2317
2371
|
if (recorded === "absent")
|
|
2318
2372
|
return false;
|
|
2319
2373
|
if (recorded === "partial") {
|
|
2320
|
-
yield*
|
|
2374
|
+
yield* Effect29.logWarning(`${ask.fqn}: the interrupted create's state row lacks part of the declaration (a prop ` + "was still an Output when it was written), so the live object is not proven ours");
|
|
2321
2375
|
return false;
|
|
2322
2376
|
}
|
|
2323
|
-
const unproven = (reason) =>
|
|
2324
|
-
return yield* settled2.pipe(
|
|
2377
|
+
const unproven = (reason) => Effect29.as(Effect29.logWarning(`${ask.fqn}: the interrupted create's object could not be compared with its props ` + `(${reason instanceof Error ? reason.message : String(reason)}), so it is not treated as ours`), false);
|
|
2378
|
+
return yield* settled2.pipe(Effect29.catch(unproven), Effect29.catchDefect(unproven));
|
|
2325
2379
|
});
|
|
2326
|
-
var ownedRead = (ask, found, settled2) =>
|
|
2380
|
+
var ownedRead = (ask, found, settled2) => Effect29.gen(function* () {
|
|
2327
2381
|
if (found === undefined || ask.output !== undefined)
|
|
2328
2382
|
return found;
|
|
2329
2383
|
return (yield* provenOurs(ask, settled2)) ? found : Unowned2(found);
|
|
@@ -2381,8 +2435,8 @@ var optionMap = (value, family, defaultKey) => {
|
|
|
2381
2435
|
return out;
|
|
2382
2436
|
};
|
|
2383
2437
|
var sameMap = (left, right) => left.size === right.size && [...left].every(([key, value]) => right.get(key) === value);
|
|
2384
|
-
var sortedForm = (
|
|
2385
|
-
var printMap = (
|
|
2438
|
+
var sortedForm = (map7) => [...map7].map(([key, value]) => `${key}=${value}`).sort().join(",");
|
|
2439
|
+
var printMap = (map7, lead = []) => [...lead.filter((key) => map7.has(key)), ...[...map7.keys()].filter((k) => !lead.includes(k))].map((key) => `${key}=${map7.get(key) ?? ""}`).join(",");
|
|
2386
2440
|
var netMaps = (declared, live) => {
|
|
2387
2441
|
const want = optionMap(declared, "net");
|
|
2388
2442
|
const have = optionMap(live, "net");
|
|
@@ -2778,11 +2832,11 @@ var identityRefusals = (news, olds, output) => {
|
|
|
2778
2832
|
};
|
|
2779
2833
|
|
|
2780
2834
|
// src/proxmox/lxc-adoption.ts
|
|
2781
|
-
import * as
|
|
2835
|
+
import * as Effect33 from "effect/Effect";
|
|
2782
2836
|
|
|
2783
2837
|
// src/ownership/adopting.ts
|
|
2784
|
-
import * as
|
|
2785
|
-
var adopting = (owner, settled2) =>
|
|
2838
|
+
import * as Effect30 from "effect/Effect";
|
|
2839
|
+
var adopting = (owner, settled2) => Effect30.gen(function* () {
|
|
2786
2840
|
if (owner.output === undefined)
|
|
2787
2841
|
return false;
|
|
2788
2842
|
const generation = yield* recordedGeneration(owner.fqn, owner.instanceId);
|
|
@@ -2794,11 +2848,11 @@ var adopting = (owner, settled2) => Effect29.gen(function* () {
|
|
|
2794
2848
|
});
|
|
2795
2849
|
|
|
2796
2850
|
// src/proxmox/lxc-lifecycle.ts
|
|
2797
|
-
import * as
|
|
2851
|
+
import * as Effect32 from "effect/Effect";
|
|
2798
2852
|
|
|
2799
2853
|
// src/proxmox/network-apply-read.ts
|
|
2800
|
-
import * as
|
|
2801
|
-
var stagedDiff = (target, node) =>
|
|
2854
|
+
import * as Effect31 from "effect/Effect";
|
|
2855
|
+
var stagedDiff = (target, node) => Effect31.gen(function* () {
|
|
2802
2856
|
const path = `nodes/${node}/network`;
|
|
2803
2857
|
const credential = yield* leased(target, "read");
|
|
2804
2858
|
const envelope = yield* pveEnvelopeWith(target, credential, "GET", path);
|
|
@@ -2811,8 +2865,8 @@ var pendingLines = (diff) => {
|
|
|
2811
2865
|
`).filter((line) => (line.startsWith("+") || line.startsWith("-")) && !line.startsWith("+++") && !line.startsWith("---")).length;
|
|
2812
2866
|
return Math.max(changed, 1);
|
|
2813
2867
|
};
|
|
2814
|
-
var pendingCount = (target, node) => stagedDiff(target, node).pipe(
|
|
2815
|
-
var clusterHealth = (target) => pve(target, "read", "GET", "cluster/status").pipe(
|
|
2868
|
+
var pendingCount = (target, node) => stagedDiff(target, node).pipe(Effect31.map(pendingLines));
|
|
2869
|
+
var clusterHealth = (target) => pve(target, "read", "GET", "cluster/status").pipe(Effect31.map((rows) => {
|
|
2816
2870
|
const all = rows ?? [];
|
|
2817
2871
|
const cluster = all.find((row) => row.type === "cluster");
|
|
2818
2872
|
return {
|
|
@@ -2820,21 +2874,21 @@ var clusterHealth = (target) => pve(target, "read", "GET", "cluster/status").pip
|
|
|
2820
2874
|
quorate: cluster === undefined ? true : bool(cluster.quorate)
|
|
2821
2875
|
};
|
|
2822
2876
|
}));
|
|
2823
|
-
var degradedReason = (target) => clusterHealth(target).pipe(
|
|
2877
|
+
var degradedReason = (target) => clusterHealth(target).pipe(Effect31.map((health) => health.quorate && health.offline.length === 0 ? undefined : `quorate=${String(health.quorate)} offline=[${health.offline.join(", ")}]`));
|
|
2824
2878
|
var UNREACHABLE = "the task could not be read -- the node stopped answering, or this lease cannot see it";
|
|
2825
2879
|
var STILL_RUNNING = "still running when this provider stopped waiting";
|
|
2826
2880
|
var ATTEMPTS = 30;
|
|
2827
|
-
var awaitTask = (target, credential, node, upid, attempts = ATTEMPTS) =>
|
|
2881
|
+
var awaitTask = (target, credential, node, upid, attempts = ATTEMPTS) => Effect31.gen(function* () {
|
|
2828
2882
|
const path = `nodes/${node}/tasks/${upid}/status`;
|
|
2829
2883
|
for (let attempt = 0;attempt < attempts; attempt += 1) {
|
|
2830
|
-
const status = yield* pveWith(target, credential, "GET", path).pipe(
|
|
2884
|
+
const status = yield* pveWith(target, credential, "GET", path).pipe(Effect31.orElseSucceed(() => {
|
|
2831
2885
|
return;
|
|
2832
2886
|
}));
|
|
2833
2887
|
if (status === undefined)
|
|
2834
2888
|
return UNREACHABLE;
|
|
2835
2889
|
if (status.status === "stopped")
|
|
2836
2890
|
return text(status.exitstatus, "stopped with no exitstatus");
|
|
2837
|
-
yield*
|
|
2891
|
+
yield* Effect31.sleep("1 second");
|
|
2838
2892
|
}
|
|
2839
2893
|
return STILL_RUNNING;
|
|
2840
2894
|
});
|
|
@@ -2852,43 +2906,43 @@ var LXC_ENDPOINTS = {
|
|
|
2852
2906
|
var CREATE_POLLS = 180;
|
|
2853
2907
|
var TASK_POLLS = 60;
|
|
2854
2908
|
var isServerError = (error) => error instanceof PveError && error.status === 500;
|
|
2855
|
-
var readLive = (where) =>
|
|
2856
|
-
const answer = yield* pve(where.target, "read", "GET", `${guestPath(where)}/config`).pipe(
|
|
2909
|
+
var readLive = (where) => Effect32.gen(function* () {
|
|
2910
|
+
const answer = yield* pve(where.target, "read", "GET", `${guestPath(where)}/config`).pipe(Effect32.map((data) => ({ data, error: undefined })), Effect32.catchIf(isServerError, (error) => Effect32.succeed({ data: undefined, error })));
|
|
2857
2911
|
if (answer.data !== undefined && answer.data !== null)
|
|
2858
2912
|
return answer.data;
|
|
2859
2913
|
const rows = yield* pve(where.target, "read", "GET", "cluster/resources?type=vm");
|
|
2860
2914
|
const found = (rows ?? []).find((row) => Number(row.vmid) === where.vmid);
|
|
2861
2915
|
if (found !== undefined && found.node === where.node && found.type === "lxc") {
|
|
2862
|
-
return yield*
|
|
2916
|
+
return yield* Effect32.fail(answer.error ?? new LxcRefusedError(`CT ${String(where.vmid)} is listed but its config read empty.`));
|
|
2863
2917
|
}
|
|
2864
2918
|
if (found !== undefined) {
|
|
2865
|
-
return yield*
|
|
2919
|
+
return yield* Effect32.fail(new LxcRefusedError(`vmid ${String(where.vmid)} is a ${text(found.type, "guest")} on ` + `${text(found.node, "?")}, not a container on ${where.node}. A migration is not an ` + "update and vmids are cluster-wide: declare the node it is on, or pick a free vmid."));
|
|
2866
2920
|
}
|
|
2867
2921
|
return;
|
|
2868
2922
|
});
|
|
2869
|
-
var
|
|
2923
|
+
var attributesOf2 = (where, live) => ({
|
|
2870
2924
|
config: storedConfig(live),
|
|
2871
2925
|
node: where.node,
|
|
2872
2926
|
rawKeys: rawKeysOf(live),
|
|
2873
2927
|
vmid: where.vmid
|
|
2874
2928
|
});
|
|
2875
|
-
var task = (where, method, path, what, polls, form) =>
|
|
2929
|
+
var task = (where, method, path, what, polls, form) => Effect32.gen(function* () {
|
|
2876
2930
|
const credential = yield* mint(where.target, "provision");
|
|
2877
2931
|
const upid = text(yield* pveWith(where.target, credential, method, path, form));
|
|
2878
2932
|
if (upid === "") {
|
|
2879
|
-
return yield*
|
|
2933
|
+
return yield* Effect32.fail(new LxcRefusedError(`${what}: PVE returned no task id, so the write cannot be confirmed and is not claimed.`));
|
|
2880
2934
|
}
|
|
2881
2935
|
const exit = yield* awaitTask(where.target, credential, where.node, upid, polls);
|
|
2882
2936
|
if (exit !== "OK") {
|
|
2883
|
-
return yield*
|
|
2937
|
+
return yield* Effect32.fail(new LxcRefusedError(`${what}: task ${upid} ended "${exit}". Read \`pvesh get /nodes/${where.node}/tasks/` + `${upid}/log\` before running the deploy again.`));
|
|
2884
2938
|
}
|
|
2885
2939
|
});
|
|
2886
|
-
var createGuest = (props) =>
|
|
2940
|
+
var createGuest = (props) => Effect32.gen(function* () {
|
|
2887
2941
|
const form = createForm3(props);
|
|
2888
2942
|
yield* guardForm(LXC_ENDPOINTS.create, form, true);
|
|
2889
2943
|
return yield* task(props, "POST", `nodes/${props.node}/lxc`, `create CT ${String(props.vmid)}`, CREATE_POLLS, form);
|
|
2890
2944
|
});
|
|
2891
|
-
var updateGuest = (props, change, digest) =>
|
|
2945
|
+
var updateGuest = (props, change, digest) => Effect32.gen(function* () {
|
|
2892
2946
|
if (Object.keys(change.put).length > 0 || change.clear.length > 0) {
|
|
2893
2947
|
const form = withClears({ ...change.put, ...digest === "" ? {} : { digest } }, change.clear);
|
|
2894
2948
|
yield* guardForm(LXC_ENDPOINTS.update, form, false);
|
|
@@ -2900,33 +2954,33 @@ var updateGuest = (props, change, digest) => Effect31.gen(function* () {
|
|
|
2900
2954
|
yield* task(props, "PUT", `${guestPath(props)}/resize`, `grow ${grow.disk} of CT ${String(props.vmid)} to ${grow.size}`, TASK_POLLS, grown);
|
|
2901
2955
|
}
|
|
2902
2956
|
});
|
|
2903
|
-
var destroyGuest = (where, declared) =>
|
|
2957
|
+
var destroyGuest = (where, declared) => Effect32.gen(function* () {
|
|
2904
2958
|
const live = yield* readLive(where);
|
|
2905
2959
|
if (live === undefined)
|
|
2906
2960
|
return;
|
|
2907
2961
|
const drift = judge(declared, live).drift;
|
|
2908
2962
|
if (drift.length > 0) {
|
|
2909
|
-
return yield*
|
|
2963
|
+
return yield* Effect32.fail(new LxcRefusedError(`destroy CT ${String(where.vmid)}: the guest on ${where.node} no longer matches its last ` + `declaration (${drift.join(", ")}), so it may not be the guest this stack made. ` + "Nothing was deleted. Delete it by hand if it should go, or declare it again."));
|
|
2910
2964
|
}
|
|
2911
2965
|
yield* task(where, "DELETE", guestPath(where), `destroy CT ${String(where.vmid)}`, TASK_POLLS);
|
|
2912
2966
|
});
|
|
2913
2967
|
|
|
2914
2968
|
// src/proxmox/lxc-adoption.ts
|
|
2915
2969
|
var adoptionRefusal = (props, drift) => `CT ${String(props.vmid)} on ${props.node}: adopting it would change ${drift.join(", ")}. An ` + "adoption never changes a guest, so nothing is written. Declare what is live for these keys " + "(the plan names keys, never values) and plan again; change them after the adoption, as an " + "update.";
|
|
2916
|
-
var refuseAdoptedDrift = (owner, props, live, change) =>
|
|
2970
|
+
var refuseAdoptedDrift = (owner, props, live, change) => Effect33.gen(function* () {
|
|
2917
2971
|
if (change.drift.length === 0)
|
|
2918
2972
|
return;
|
|
2919
|
-
const settled2 = (recorded) =>
|
|
2973
|
+
const settled2 = (recorded) => Effect33.sync(() => judge(recorded, live).drift.length === 0);
|
|
2920
2974
|
if (!(yield* adopting(owner, settled2)))
|
|
2921
2975
|
return;
|
|
2922
|
-
return yield*
|
|
2976
|
+
return yield* Effect33.fail(new LxcRefusedError(adoptionRefusal(props, change.drift)));
|
|
2923
2977
|
});
|
|
2924
2978
|
|
|
2925
2979
|
// src/proxmox/lxc.ts
|
|
2926
2980
|
var ProxmoxLxc = Resource13("Proxmox.Lxc", { defaultRemovalPolicy: "retain" });
|
|
2927
|
-
var refused = (reasons) =>
|
|
2981
|
+
var refused = (reasons) => Effect34.fail(new LxcRefusedError(reasons.join(`
|
|
2928
2982
|
`)));
|
|
2929
|
-
var diff = (owner, news, olds) =>
|
|
2983
|
+
var diff = (owner, news, olds) => Effect34.gen(function* () {
|
|
2930
2984
|
const { output } = owner;
|
|
2931
2985
|
const identity2 = identityRefusals(news, olds, output);
|
|
2932
2986
|
if (identity2.length > 0)
|
|
@@ -2940,7 +2994,7 @@ var diff = (owner, news, olds) => Effect33.gen(function* () {
|
|
|
2940
2994
|
const refusals = createRefusals(news);
|
|
2941
2995
|
if (refusals.length > 0)
|
|
2942
2996
|
return yield* refused(refusals);
|
|
2943
|
-
yield*
|
|
2997
|
+
yield* Effect34.logWarning(`Proxmox.Lxc ${news.node}/${String(news.vmid)}: the cluster lists no guest with this ` + "vmid -- a deploy CREATES it again from ostemplate, with new, empty volumes.");
|
|
2944
2998
|
return { action: "update" };
|
|
2945
2999
|
}
|
|
2946
3000
|
const change = judge(news, live);
|
|
@@ -2951,10 +3005,10 @@ var diff = (owner, news, olds) => Effect33.gen(function* () {
|
|
|
2951
3005
|
if (change.drift.length === 0 && !moved)
|
|
2952
3006
|
return { action: "noop" };
|
|
2953
3007
|
const what = change.drift.length === 0 ? `the guest is on ${news.node} now, not ${output.node} -- a deploy re-records it, ` + "writing nothing." : `live config differs from the declaration in ${change.drift.join(", ")} -- a ` + "deploy writes these.";
|
|
2954
|
-
yield*
|
|
3008
|
+
yield* Effect34.logWarning(`Proxmox.Lxc ${news.node}/${String(news.vmid)}: ${what}`);
|
|
2955
3009
|
return { action: "update" };
|
|
2956
3010
|
});
|
|
2957
|
-
var reconcile = (owner, news) =>
|
|
3011
|
+
var reconcile = (owner, news) => Effect34.gen(function* () {
|
|
2958
3012
|
const { output } = owner;
|
|
2959
3013
|
const identity2 = identityRefusals(news, news, output);
|
|
2960
3014
|
if (identity2.length > 0)
|
|
@@ -2993,20 +3047,20 @@ var reconcile = (owner, news) => Effect33.gen(function* () {
|
|
|
2993
3047
|
`CT ${String(news.vmid)}: after the write, ${left.join(", ")} still differ from the ` + "declaration. PVE may have normalised the value (tags are lower-cased by default) -- " + "declare what it stores."
|
|
2994
3048
|
]);
|
|
2995
3049
|
}
|
|
2996
|
-
return
|
|
3050
|
+
return attributesOf2(news, after);
|
|
2997
3051
|
});
|
|
2998
3052
|
var whereOf = (olds, output) => ({
|
|
2999
3053
|
node: output?.node ?? olds.node,
|
|
3000
3054
|
target: olds.target,
|
|
3001
3055
|
vmid: output?.vmid ?? olds.vmid
|
|
3002
3056
|
});
|
|
3003
|
-
var read = (owner, olds) =>
|
|
3057
|
+
var read = (owner, olds) => Effect34.gen(function* () {
|
|
3004
3058
|
const where = whereOf(olds, owner.output);
|
|
3005
3059
|
const live = yield* readLive(where);
|
|
3006
3060
|
if (live === undefined)
|
|
3007
3061
|
return;
|
|
3008
|
-
const settled2 =
|
|
3009
|
-
return yield* ownedRead(owner,
|
|
3062
|
+
const settled2 = Effect34.sync(() => judge(olds, live).drift.length === 0);
|
|
3063
|
+
return yield* ownedRead(owner, attributesOf2(where, live), settled2);
|
|
3010
3064
|
});
|
|
3011
3065
|
var handlers10 = {
|
|
3012
3066
|
delete: ({ olds, output }) => destroyGuest(whereOf(olds, output), olds),
|
|
@@ -3017,7 +3071,7 @@ var handlers10 = {
|
|
|
3017
3071
|
olds,
|
|
3018
3072
|
output
|
|
3019
3073
|
}) => diff({ fqn, instanceId, output }, news, olds),
|
|
3020
|
-
list: () =>
|
|
3074
|
+
list: () => Effect34.succeed([]),
|
|
3021
3075
|
read: ({
|
|
3022
3076
|
fqn,
|
|
3023
3077
|
instanceId,
|
|
@@ -3031,11 +3085,11 @@ var handlers10 = {
|
|
|
3031
3085
|
output
|
|
3032
3086
|
}) => reconcile({ fqn, instanceId, output }, news)
|
|
3033
3087
|
};
|
|
3034
|
-
var ProxmoxLxcProvider = () => Provider13.effect(ProxmoxLxc,
|
|
3088
|
+
var ProxmoxLxcProvider = () => Provider13.effect(ProxmoxLxc, Effect34.succeed(ProxmoxLxc.Provider.of(handlers10)).pipe(Effect34.map(provingResumes)));
|
|
3035
3089
|
// src/proxmox/metric-server.ts
|
|
3036
3090
|
import { Resource as Resource14 } from "alchemy";
|
|
3037
3091
|
import * as Provider14 from "alchemy/Provider";
|
|
3038
|
-
import * as
|
|
3092
|
+
import * as Effect35 from "effect/Effect";
|
|
3039
3093
|
|
|
3040
3094
|
// src/proxmox/metric-server-form.ts
|
|
3041
3095
|
var UNSET2 = -1;
|
|
@@ -3148,12 +3202,12 @@ var handlers11 = pveHandlers({
|
|
|
3148
3202
|
path: (props) => `cluster/metrics/server/${props.id}`,
|
|
3149
3203
|
updateForm: update
|
|
3150
3204
|
});
|
|
3151
|
-
var ProxmoxMetricServerProvider = () => Provider14.effect(ProxmoxMetricServer,
|
|
3205
|
+
var ProxmoxMetricServerProvider = () => Provider14.effect(ProxmoxMetricServer, Effect35.succeed(ProxmoxMetricServer.Provider.of(handlers11)));
|
|
3152
3206
|
// src/proxmox/network-apply.ts
|
|
3153
3207
|
import { Resource as Resource15 } from "alchemy";
|
|
3154
3208
|
import { isResolved as isResolved7 } from "alchemy/Diff";
|
|
3155
3209
|
import * as Provider15 from "alchemy/Provider";
|
|
3156
|
-
import * as
|
|
3210
|
+
import * as Effect36 from "effect/Effect";
|
|
3157
3211
|
|
|
3158
3212
|
// src/proxmox/apply-endpoints.ts
|
|
3159
3213
|
var NETWORK_APPLY_ENDPOINT = "pve:PUT /nodes/{node}/network";
|
|
@@ -3163,15 +3217,15 @@ var guardSdnApply = guardForm(SDN_APPLY_ENDPOINT, {}, true);
|
|
|
3163
3217
|
|
|
3164
3218
|
// src/proxmox/network-apply.ts
|
|
3165
3219
|
var ProxmoxNetworkApply = Resource15("Proxmox.NetworkApply");
|
|
3166
|
-
var read2 = (props) => pendingCount(props.target, props.node).pipe(
|
|
3167
|
-
var diff2 = (news) =>
|
|
3220
|
+
var read2 = (props) => pendingCount(props.target, props.node).pipe(Effect36.map((pending) => ({ node: props.node, pending })));
|
|
3221
|
+
var diff2 = (news) => Effect36.gen(function* () {
|
|
3168
3222
|
if (!isResolved7(news))
|
|
3169
3223
|
return;
|
|
3170
3224
|
const { pending } = yield* read2(news);
|
|
3171
3225
|
return pending === 0 ? { action: "noop" } : { action: "update" };
|
|
3172
3226
|
});
|
|
3173
|
-
var refuse = (node, why) =>
|
|
3174
|
-
var apply = (props) =>
|
|
3227
|
+
var refuse = (node, why) => Effect36.die(new Error(`Proxmox.NetworkApply ${node}: ${why}`));
|
|
3228
|
+
var apply = (props) => Effect36.gen(function* () {
|
|
3175
3229
|
const node = props.node;
|
|
3176
3230
|
const staged = yield* pendingCount(props.target, node);
|
|
3177
3231
|
if (staged === 0)
|
|
@@ -3201,17 +3255,17 @@ var apply = (props) => Effect35.gen(function* () {
|
|
|
3201
3255
|
return { node, pending: left };
|
|
3202
3256
|
});
|
|
3203
3257
|
var handlers12 = {
|
|
3204
|
-
delete: () =>
|
|
3258
|
+
delete: () => Effect36.void,
|
|
3205
3259
|
diff: ({ news }) => diff2(news),
|
|
3206
|
-
list: () =>
|
|
3260
|
+
list: () => Effect36.succeed([]),
|
|
3207
3261
|
read: ({ olds }) => read2(olds),
|
|
3208
3262
|
reconcile: ({ news }) => apply(news)
|
|
3209
3263
|
};
|
|
3210
|
-
var ProxmoxNetworkApplyProvider = () => Provider15.effect(ProxmoxNetworkApply,
|
|
3264
|
+
var ProxmoxNetworkApplyProvider = () => Provider15.effect(ProxmoxNetworkApply, Effect36.succeed(ProxmoxNetworkApply.Provider.of(handlers12)));
|
|
3211
3265
|
// src/proxmox/node-network.ts
|
|
3212
3266
|
import { Resource as Resource16 } from "alchemy";
|
|
3213
3267
|
import * as Provider16 from "alchemy/Provider";
|
|
3214
|
-
import * as
|
|
3268
|
+
import * as Effect37 from "effect/Effect";
|
|
3215
3269
|
|
|
3216
3270
|
// src/proxmox/node-network-form.ts
|
|
3217
3271
|
var UNSET3 = -1;
|
|
@@ -3250,7 +3304,7 @@ var updateBody2 = (props) => {
|
|
|
3250
3304
|
const fields = body4(props);
|
|
3251
3305
|
return withClears(fields, clear);
|
|
3252
3306
|
};
|
|
3253
|
-
var
|
|
3307
|
+
var readAttributes2 = (live, props) => {
|
|
3254
3308
|
const liveType = text(live["type"]);
|
|
3255
3309
|
if (liveType !== "" && liveType !== props.type)
|
|
3256
3310
|
return;
|
|
@@ -3288,7 +3342,7 @@ var ProxmoxNodeNetwork = Resource16("Proxmox.NodeNetwork", {
|
|
|
3288
3342
|
defaultRemovalPolicy: "retain"
|
|
3289
3343
|
});
|
|
3290
3344
|
var handlers13 = pveHandlers({
|
|
3291
|
-
attributes:
|
|
3345
|
+
attributes: readAttributes2,
|
|
3292
3346
|
collection: (props) => `nodes/${props.node}/network`,
|
|
3293
3347
|
createForm: createBody3,
|
|
3294
3348
|
endpoint: {
|
|
@@ -3299,11 +3353,11 @@ var handlers13 = pveHandlers({
|
|
|
3299
3353
|
path: (props) => `nodes/${props.node}/network/${props.iface}`,
|
|
3300
3354
|
updateForm: updateBody2
|
|
3301
3355
|
});
|
|
3302
|
-
var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork,
|
|
3356
|
+
var ProxmoxNodeNetworkProvider = () => Provider16.effect(ProxmoxNodeNetwork, Effect37.succeed(ProxmoxNodeNetwork.Provider.of(handlers13)));
|
|
3303
3357
|
// src/proxmox/notification-matcher.ts
|
|
3304
3358
|
import { Resource as Resource17 } from "alchemy";
|
|
3305
3359
|
import * as Provider17 from "alchemy/Provider";
|
|
3306
|
-
import * as
|
|
3360
|
+
import * as Effect38 from "effect/Effect";
|
|
3307
3361
|
|
|
3308
3362
|
// src/proxmox/notification-matcher-form.ts
|
|
3309
3363
|
var list = (value) => Array.isArray(value) ? value.map((item) => String(item)) : [];
|
|
@@ -3393,11 +3447,11 @@ var handlers14 = pveHandlers({
|
|
|
3393
3447
|
path: (props) => `cluster/notifications/matchers/${props.name}`,
|
|
3394
3448
|
updateForm: matcherUpdateForm
|
|
3395
3449
|
});
|
|
3396
|
-
var ProxmoxNotificationMatcherProvider = () => Provider17.effect(ProxmoxNotificationMatcher,
|
|
3450
|
+
var ProxmoxNotificationMatcherProvider = () => Provider17.effect(ProxmoxNotificationMatcher, Effect38.succeed(ProxmoxNotificationMatcher.Provider.of(handlers14)));
|
|
3397
3451
|
// src/proxmox/notification-target.ts
|
|
3398
3452
|
import { Resource as Resource18 } from "alchemy";
|
|
3399
3453
|
import * as Provider18 from "alchemy/Provider";
|
|
3400
|
-
import * as
|
|
3454
|
+
import * as Effect39 from "effect/Effect";
|
|
3401
3455
|
|
|
3402
3456
|
// src/proxmox/notification-target-endpoint.ts
|
|
3403
3457
|
var ENDPOINTS = {
|
|
@@ -3479,15 +3533,15 @@ var handlers15 = pveHandlers({
|
|
|
3479
3533
|
path: (props) => `cluster/notifications/endpoints/${props.type}/${props.name}`,
|
|
3480
3534
|
updateForm: shape4
|
|
3481
3535
|
});
|
|
3482
|
-
var ProxmoxNotificationTargetProvider = () => Provider18.effect(ProxmoxNotificationTarget,
|
|
3536
|
+
var ProxmoxNotificationTargetProvider = () => Provider18.effect(ProxmoxNotificationTarget, Effect39.succeed(ProxmoxNotificationTarget.Provider.of(handlers15)));
|
|
3483
3537
|
// src/proxmox/pbs-datastore.ts
|
|
3484
3538
|
import { Resource as Resource19 } from "alchemy";
|
|
3485
3539
|
import { isResolved as isResolved8 } from "alchemy/Diff";
|
|
3486
3540
|
import * as Provider19 from "alchemy/Provider";
|
|
3487
|
-
import * as
|
|
3541
|
+
import * as Effect43 from "effect/Effect";
|
|
3488
3542
|
|
|
3489
3543
|
// src/proxmox/pbs-datastore-form.ts
|
|
3490
|
-
import * as
|
|
3544
|
+
import * as Effect40 from "effect/Effect";
|
|
3491
3545
|
var attributes = (live, props) => ({
|
|
3492
3546
|
backend: propertyString(live["backend"]),
|
|
3493
3547
|
comment: text(live["comment"]),
|
|
@@ -3510,7 +3564,7 @@ var attributes = (live, props) => ({
|
|
|
3510
3564
|
"verify-new": bool(live["verify-new"])
|
|
3511
3565
|
});
|
|
3512
3566
|
var same4 = (declared, live) => declared === undefined || declared === live;
|
|
3513
|
-
var
|
|
3567
|
+
var matches2 = (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"]);
|
|
3514
3568
|
var set2 = (key, value) => value === undefined ? {} : { [key]: value };
|
|
3515
3569
|
var count = (value) => value === undefined ? undefined : String(value);
|
|
3516
3570
|
var mutable = (props) => ({
|
|
@@ -3539,35 +3593,35 @@ var createForm4 = (props) => ({
|
|
|
3539
3593
|
});
|
|
3540
3594
|
var updateForm = (props) => mutable(props);
|
|
3541
3595
|
var object2 = (props) => `config/datastore/${props.name}`;
|
|
3542
|
-
var readOne = (props) => pve(props.target, "read", "GET", object2(props)).pipe(
|
|
3596
|
+
var readOne = (props) => pve(props.target, "read", "GET", object2(props)).pipe(Effect40.map((live) => live === undefined || typeof live["name"] !== "string" ? undefined : attributes(live, props)), Effect40.orElseSucceed(() => {
|
|
3543
3597
|
return;
|
|
3544
3598
|
}));
|
|
3545
3599
|
|
|
3546
3600
|
// src/proxmox/pbs-datastore-guard.ts
|
|
3547
|
-
import * as
|
|
3601
|
+
import * as Effect41 from "effect/Effect";
|
|
3548
3602
|
var samePath = (left, right) => {
|
|
3549
3603
|
const trim = (value) => value.length > 1 ? value.replace(/\/+$/, "") : value;
|
|
3550
3604
|
return trim(left) === trim(right);
|
|
3551
3605
|
};
|
|
3552
|
-
var guardPath = (live, props) => live.path === "" || samePath(live.path, props.path) ?
|
|
3553
|
-
var guardBackend = (live, props) => props.backend === undefined || propertyString(props.backend) === live.backend ?
|
|
3554
|
-
var settle3 = (read3, done) =>
|
|
3606
|
+
var guardPath = (live, props) => live.path === "" || samePath(live.path, props.path) ? Effect41.void : Effect41.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."));
|
|
3607
|
+
var guardBackend = (live, props) => props.backend === undefined || propertyString(props.backend) === live.backend ? Effect41.void : Effect41.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."));
|
|
3608
|
+
var settle3 = (read3, done) => Effect41.gen(function* () {
|
|
3555
3609
|
for (let attempt = 0;attempt < 60; attempt += 1) {
|
|
3556
3610
|
const live = yield* read3;
|
|
3557
3611
|
if (done(live))
|
|
3558
3612
|
return live;
|
|
3559
|
-
yield*
|
|
3613
|
+
yield* Effect41.sleep("2 seconds");
|
|
3560
3614
|
}
|
|
3561
3615
|
return yield* read3;
|
|
3562
3616
|
});
|
|
3563
3617
|
|
|
3564
3618
|
// src/proxmox/pbs-datastore-endpoint.ts
|
|
3565
|
-
import * as
|
|
3619
|
+
import * as Effect42 from "effect/Effect";
|
|
3566
3620
|
var DATASTORE_ENDPOINT = {
|
|
3567
3621
|
create: "pbs:POST /config/datastore",
|
|
3568
3622
|
update: "pbs:PUT /config/datastore/{name}"
|
|
3569
3623
|
};
|
|
3570
|
-
var guardDatastoreForms = (props) =>
|
|
3624
|
+
var guardDatastoreForms = (props) => Effect42.gen(function* () {
|
|
3571
3625
|
yield* guardForm(DATASTORE_ENDPOINT.create, createForm4(props), true);
|
|
3572
3626
|
yield* guardForm(DATASTORE_ENDPOINT.update, updateForm(props), false);
|
|
3573
3627
|
});
|
|
@@ -3576,10 +3630,10 @@ var guardDatastoreForms = (props) => Effect41.gen(function* () {
|
|
|
3576
3630
|
var PbsDatastore = Resource19("Pbs.Datastore", {
|
|
3577
3631
|
defaultRemovalPolicy: "retain"
|
|
3578
3632
|
});
|
|
3579
|
-
var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore,
|
|
3580
|
-
list: () =>
|
|
3633
|
+
var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore, Effect43.succeed(PbsDatastore.Provider.of({
|
|
3634
|
+
list: () => Effect43.succeed([]),
|
|
3581
3635
|
read: ({ olds }) => readOne(olds),
|
|
3582
|
-
diff: ({ news, output }) =>
|
|
3636
|
+
diff: ({ news, output }) => Effect43.gen(function* () {
|
|
3583
3637
|
if (!isResolved8(news))
|
|
3584
3638
|
return;
|
|
3585
3639
|
yield* guardDatastoreForms(news);
|
|
@@ -3590,9 +3644,9 @@ var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore, Effect42.succee
|
|
|
3590
3644
|
return { action: "update" };
|
|
3591
3645
|
yield* guardPath(live, news);
|
|
3592
3646
|
yield* guardBackend(live, news);
|
|
3593
|
-
return
|
|
3647
|
+
return matches2(live, news) ? { action: "noop" } : { action: "update" };
|
|
3594
3648
|
}),
|
|
3595
|
-
reconcile:
|
|
3649
|
+
reconcile: Effect43.fn(function* ({ news }) {
|
|
3596
3650
|
yield* guardDatastoreForms(news);
|
|
3597
3651
|
const live = yield* readOne(news);
|
|
3598
3652
|
let upid;
|
|
@@ -3601,29 +3655,29 @@ var PbsDatastoreProvider = () => Provider19.effect(PbsDatastore, Effect42.succee
|
|
|
3601
3655
|
} else {
|
|
3602
3656
|
yield* guardPath(live, news);
|
|
3603
3657
|
yield* guardBackend(live, news);
|
|
3604
|
-
const form = formToSend(
|
|
3658
|
+
const form = formToSend(matches2, live, news, updateForm(news));
|
|
3605
3659
|
if (form !== undefined) {
|
|
3606
3660
|
yield* pve(news.target, "provision", "PUT", object2(news), form);
|
|
3607
3661
|
}
|
|
3608
3662
|
}
|
|
3609
|
-
const after = yield* settle3(readOne(news), (row) => row !== undefined &&
|
|
3610
|
-
if (after === undefined || !
|
|
3611
|
-
return yield*
|
|
3663
|
+
const after = yield* settle3(readOne(news), (row) => row !== undefined && matches2(row, news));
|
|
3664
|
+
if (after === undefined || !matches2(after, news)) {
|
|
3665
|
+
return yield* Effect43.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."));
|
|
3612
3666
|
}
|
|
3613
3667
|
return after;
|
|
3614
3668
|
}),
|
|
3615
|
-
delete:
|
|
3669
|
+
delete: Effect43.fn(function* ({ olds }) {
|
|
3616
3670
|
yield* pve(olds.target, "provision", "DELETE", object2(olds));
|
|
3617
3671
|
const left = yield* settle3(readOne(olds), (row) => row === undefined);
|
|
3618
3672
|
if (left !== undefined) {
|
|
3619
|
-
return yield*
|
|
3673
|
+
return yield* Effect43.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>\`.`));
|
|
3620
3674
|
}
|
|
3621
3675
|
})
|
|
3622
3676
|
})));
|
|
3623
3677
|
// src/proxmox/pbs-notification-matcher.ts
|
|
3624
3678
|
import { Resource as Resource20 } from "alchemy";
|
|
3625
3679
|
import * as Provider20 from "alchemy/Provider";
|
|
3626
|
-
import * as
|
|
3680
|
+
import * as Effect44 from "effect/Effect";
|
|
3627
3681
|
var PbsNotificationMatcher = Resource20("Pbs.NotificationMatcher");
|
|
3628
3682
|
var handlers16 = pveHandlers({
|
|
3629
3683
|
attributes: (live, props) => matcherAttributes(live, props.name),
|
|
@@ -3637,15 +3691,15 @@ var handlers16 = pveHandlers({
|
|
|
3637
3691
|
path: (props) => `config/notifications/matchers/${props.name}`,
|
|
3638
3692
|
updateForm: matcherUpdateForm
|
|
3639
3693
|
});
|
|
3640
|
-
var PbsNotificationMatcherProvider = () => Provider20.effect(PbsNotificationMatcher,
|
|
3694
|
+
var PbsNotificationMatcherProvider = () => Provider20.effect(PbsNotificationMatcher, Effect44.succeed(PbsNotificationMatcher.Provider.of(handlers16)));
|
|
3641
3695
|
// src/proxmox/pbs-notification-target.ts
|
|
3642
3696
|
import { Resource as Resource21 } from "alchemy";
|
|
3643
3697
|
import * as Provider21 from "alchemy/Provider";
|
|
3644
|
-
import * as
|
|
3698
|
+
import * as Effect46 from "effect/Effect";
|
|
3645
3699
|
|
|
3646
3700
|
// src/proxmox/pbs-notification-target-lifecycle.ts
|
|
3647
3701
|
import { isResolved as isResolved9 } from "alchemy/Diff";
|
|
3648
|
-
import * as
|
|
3702
|
+
import * as Effect45 from "effect/Effect";
|
|
3649
3703
|
|
|
3650
3704
|
// src/proxmox/pbs-notification-target-endpoint.ts
|
|
3651
3705
|
var ENDPOINTS2 = {
|
|
@@ -3922,12 +3976,12 @@ var targetForm = (props, groups, carry, mode) => {
|
|
|
3922
3976
|
|
|
3923
3977
|
// src/proxmox/pbs-notification-target-lifecycle.ts
|
|
3924
3978
|
var path = (props) => `config/notifications/endpoints/${props.type}/${props.name}`;
|
|
3925
|
-
var readLive2 = (props) => pve(props.target, "read", "GET", path(props)).pipe(
|
|
3979
|
+
var readLive2 = (props) => pve(props.target, "read", "GET", path(props)).pipe(Effect45.map((data) => data == null ? undefined : targetAttributes(data, props)), Effect45.orElseSucceed(() => {
|
|
3926
3980
|
return;
|
|
3927
3981
|
}));
|
|
3928
3982
|
var refuse2 = (props) => {
|
|
3929
3983
|
const reasons = refusals(props);
|
|
3930
|
-
return reasons.length === 0 ?
|
|
3984
|
+
return reasons.length === 0 ? Effect45.void : Effect45.die(new Error(`${path(props)}: ${reasons.join("; ")}.`));
|
|
3931
3985
|
};
|
|
3932
3986
|
var toCarry = (live, props, groups, sealed) => {
|
|
3933
3987
|
const header = headerState(live, props, groups) === "stale";
|
|
@@ -3939,16 +3993,16 @@ var requireValues = (props, groups, carry) => {
|
|
|
3939
3993
|
...carry.header ? groups.header.missing : [],
|
|
3940
3994
|
...carry.sealed ? groups.sealed.missing : []
|
|
3941
3995
|
];
|
|
3942
|
-
return missing.length === 0 ?
|
|
3996
|
+
return missing.length === 0 ? Effect45.void : Effect45.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."));
|
|
3943
3997
|
};
|
|
3944
3998
|
var handlers17 = {
|
|
3945
|
-
list: () =>
|
|
3946
|
-
read: ({ olds, output }) => (output === undefined ? refuse2(olds) :
|
|
3999
|
+
list: () => Effect45.succeed([]),
|
|
4000
|
+
read: ({ olds, output }) => (output === undefined ? refuse2(olds) : Effect45.void).pipe(Effect45.andThen(readLive2(olds)), Effect45.map((live) => live === undefined ? undefined : { ...live, sealed: output?.sealed ?? "" })),
|
|
3947
4001
|
diff: ({
|
|
3948
4002
|
news,
|
|
3949
4003
|
olds,
|
|
3950
4004
|
output
|
|
3951
|
-
}) =>
|
|
4005
|
+
}) => Effect45.gen(function* () {
|
|
3952
4006
|
if (output === undefined || !isResolved9(news))
|
|
3953
4007
|
return;
|
|
3954
4008
|
if (olds !== undefined && (olds.type !== news.type || olds.name !== news.name)) {
|
|
@@ -3960,7 +4014,7 @@ var handlers17 = {
|
|
|
3960
4014
|
return { action: "update" };
|
|
3961
4015
|
return toCarry(live, news, resolveGroups(news), output.sealed) === undefined ? { action: "noop" } : { action: "update" };
|
|
3962
4016
|
}),
|
|
3963
|
-
reconcile: ({ news, output }) =>
|
|
4017
|
+
reconcile: ({ news, output }) => Effect45.gen(function* () {
|
|
3964
4018
|
yield* refuse2(news);
|
|
3965
4019
|
const groups = resolveGroups(news);
|
|
3966
4020
|
const live = yield* readLive2(news);
|
|
@@ -3986,7 +4040,7 @@ var handlers17 = {
|
|
|
3986
4040
|
}
|
|
3987
4041
|
const after = yield* readLive2(news);
|
|
3988
4042
|
if (after === undefined) {
|
|
3989
|
-
return yield*
|
|
4043
|
+
return yield* Effect45.die(new Error(`${path(news)}: the write returned no error but the target is still absent.`));
|
|
3990
4044
|
}
|
|
3991
4045
|
return { ...after, sealed };
|
|
3992
4046
|
}),
|
|
@@ -3995,11 +4049,11 @@ var handlers17 = {
|
|
|
3995
4049
|
|
|
3996
4050
|
// src/proxmox/pbs-notification-target.ts
|
|
3997
4051
|
var PbsNotificationTarget = Resource21("Pbs.NotificationTarget");
|
|
3998
|
-
var PbsNotificationTargetProvider = () => Provider21.effect(PbsNotificationTarget,
|
|
4052
|
+
var PbsNotificationTargetProvider = () => Provider21.effect(PbsNotificationTarget, Effect46.succeed(PbsNotificationTarget.Provider.of(handlers17)));
|
|
3999
4053
|
// src/proxmox/pbs-prune-job.ts
|
|
4000
4054
|
import { Resource as Resource22 } from "alchemy";
|
|
4001
4055
|
import * as Provider22 from "alchemy/Provider";
|
|
4002
|
-
import * as
|
|
4056
|
+
import * as Effect47 from "effect/Effect";
|
|
4003
4057
|
|
|
4004
4058
|
// src/proxmox/pbs-prune-job-form.ts
|
|
4005
4059
|
var KEEP_KEYS = [
|
|
@@ -4075,11 +4129,11 @@ var handlers18 = pveHandlers({
|
|
|
4075
4129
|
path: (props) => `config/prune/${props.id}`,
|
|
4076
4130
|
updateForm: updateBody3
|
|
4077
4131
|
});
|
|
4078
|
-
var PbsPruneJobProvider = () => Provider22.effect(PbsPruneJob,
|
|
4132
|
+
var PbsPruneJobProvider = () => Provider22.effect(PbsPruneJob, Effect47.succeed(PbsPruneJob.Provider.of(handlers18)));
|
|
4079
4133
|
// src/proxmox/pbs-sync-job.ts
|
|
4080
4134
|
import { Resource as Resource23 } from "alchemy";
|
|
4081
4135
|
import * as Provider23 from "alchemy/Provider";
|
|
4082
|
-
import * as
|
|
4136
|
+
import * as Effect48 from "effect/Effect";
|
|
4083
4137
|
|
|
4084
4138
|
// src/proxmox/pbs-sync-job-form.ts
|
|
4085
4139
|
var UNSET4 = -1;
|
|
@@ -4156,11 +4210,11 @@ var handlers19 = pveHandlers({
|
|
|
4156
4210
|
|
|
4157
4211
|
// src/proxmox/pbs-sync-job.ts
|
|
4158
4212
|
var PbsSyncJob = Resource23("Pbs.SyncJob");
|
|
4159
|
-
var PbsSyncJobProvider = () => Provider23.effect(PbsSyncJob,
|
|
4213
|
+
var PbsSyncJobProvider = () => Provider23.effect(PbsSyncJob, Effect48.succeed(PbsSyncJob.Provider.of(handlers19)));
|
|
4160
4214
|
// src/proxmox/pbs-verify-job.ts
|
|
4161
4215
|
import { Resource as Resource24 } from "alchemy";
|
|
4162
4216
|
import * as Provider24 from "alchemy/Provider";
|
|
4163
|
-
import * as
|
|
4217
|
+
import * as Effect49 from "effect/Effect";
|
|
4164
4218
|
|
|
4165
4219
|
// src/proxmox/pbs-verify-job-form.ts
|
|
4166
4220
|
var rechecks = (ignoreVerified, outdatedAfter) => {
|
|
@@ -4205,11 +4259,11 @@ var handlers20 = pveHandlers({
|
|
|
4205
4259
|
path: (props) => `config/verify/${props.id}`,
|
|
4206
4260
|
updateForm: shape7
|
|
4207
4261
|
});
|
|
4208
|
-
var PbsVerifyJobProvider = () => Provider24.effect(PbsVerifyJob,
|
|
4262
|
+
var PbsVerifyJobProvider = () => Provider24.effect(PbsVerifyJob, Effect49.succeed(PbsVerifyJob.Provider.of(handlers20)));
|
|
4209
4263
|
// src/proxmox/pool.ts
|
|
4210
4264
|
import { Resource as Resource25 } from "alchemy";
|
|
4211
4265
|
import * as Provider25 from "alchemy/Provider";
|
|
4212
|
-
import * as
|
|
4266
|
+
import * as Effect50 from "effect/Effect";
|
|
4213
4267
|
var ProxmoxPool = Resource25("Proxmox.Pool");
|
|
4214
4268
|
var poolCreateForm = (props) => ({
|
|
4215
4269
|
comment: props.comment ?? "",
|
|
@@ -4234,7 +4288,7 @@ var handlers21 = pveHandlers({
|
|
|
4234
4288
|
path: (props) => `pools/${props.poolid}`,
|
|
4235
4289
|
updateForm: poolUpdateForm
|
|
4236
4290
|
});
|
|
4237
|
-
var ProxmoxPoolProvider = () => Provider25.effect(ProxmoxPool,
|
|
4291
|
+
var ProxmoxPoolProvider = () => Provider25.effect(ProxmoxPool, Effect50.succeed(ProxmoxPool.Provider.of(handlers21)));
|
|
4238
4292
|
// src/proxmox/provision-baseline.ts
|
|
4239
4293
|
var PROVISION_PRIVILEGES = Object.freeze([
|
|
4240
4294
|
"Datastore.Allocate",
|
|
@@ -4427,12 +4481,12 @@ echo 'provision baseline: done. Next: point the OpenBao proxmox mount at these u
|
|
|
4427
4481
|
// src/proxmox/provision-declare.ts
|
|
4428
4482
|
import { adopt } from "alchemy/AdoptPolicy";
|
|
4429
4483
|
import * as RemovalPolicy from "alchemy/RemovalPolicy";
|
|
4430
|
-
import * as
|
|
4484
|
+
import * as Effect53 from "effect/Effect";
|
|
4431
4485
|
|
|
4432
4486
|
// src/proxmox/role.ts
|
|
4433
4487
|
import { Resource as Resource26 } from "alchemy";
|
|
4434
4488
|
import * as Provider26 from "alchemy/Provider";
|
|
4435
|
-
import * as
|
|
4489
|
+
import * as Effect51 from "effect/Effect";
|
|
4436
4490
|
var ProxmoxRole = Resource26("Proxmox.Role");
|
|
4437
4491
|
var canonical2 = (privs) => [...new Set(privs.map((priv) => priv.trim()).filter((priv) => priv.length > 0))].sort();
|
|
4438
4492
|
var livePrivs = (live) => {
|
|
@@ -4450,12 +4504,12 @@ var handlers22 = pveHandlers({
|
|
|
4450
4504
|
path: (props) => `access/roles/${props.roleid}`,
|
|
4451
4505
|
updateForm: (props) => ({ privs: canonical2(props.privs).join(",") })
|
|
4452
4506
|
});
|
|
4453
|
-
var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole,
|
|
4507
|
+
var ProxmoxRoleProvider = () => Provider26.effect(ProxmoxRole, Effect51.succeed(ProxmoxRole.Provider.of(handlers22)));
|
|
4454
4508
|
|
|
4455
4509
|
// src/proxmox/user.ts
|
|
4456
4510
|
import { Resource as Resource27 } from "alchemy";
|
|
4457
4511
|
import * as Provider27 from "alchemy/Provider";
|
|
4458
|
-
import * as
|
|
4512
|
+
import * as Effect52 from "effect/Effect";
|
|
4459
4513
|
var ProxmoxUser = Resource27("Proxmox.User");
|
|
4460
4514
|
var str2 = (value) => typeof value === "string" ? value : "";
|
|
4461
4515
|
var int2 = (value) => {
|
|
@@ -4496,11 +4550,11 @@ var handlers23 = pveHandlers({
|
|
|
4496
4550
|
path: (props) => `access/users/${props.userid}`,
|
|
4497
4551
|
updateForm: shape8
|
|
4498
4552
|
});
|
|
4499
|
-
var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser,
|
|
4553
|
+
var ProxmoxUserProvider = () => Provider27.effect(ProxmoxUser, Effect52.succeed(ProxmoxUser.Provider.of(handlers23)));
|
|
4500
4554
|
|
|
4501
4555
|
// src/proxmox/provision-declare.ts
|
|
4502
|
-
var declareProvisionBaseline = (id, target, names2 = {}, options = {}) =>
|
|
4503
|
-
const baseline = yield*
|
|
4556
|
+
var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect53.gen(function* () {
|
|
4557
|
+
const baseline = yield* Effect53.try({
|
|
4504
4558
|
catch: (cause) => cause instanceof Error ? cause : new Error(String(cause)),
|
|
4505
4559
|
try: () => provisionBaseline(names2)
|
|
4506
4560
|
});
|
|
@@ -4514,13 +4568,13 @@ var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect
|
|
|
4514
4568
|
target
|
|
4515
4569
|
}));
|
|
4516
4570
|
const group = yield* owned(ProxmoxGroup(`${id}-mint-group`, { ...baseline.group, target }));
|
|
4517
|
-
const users = yield*
|
|
4571
|
+
const users = yield* Effect53.all(baseline.users.map((user) => owned(ProxmoxUser(`${id}-${user.lane}-user`, {
|
|
4518
4572
|
comment: user.comment,
|
|
4519
4573
|
groups: [...user.groups],
|
|
4520
4574
|
target,
|
|
4521
4575
|
userid: user.userid
|
|
4522
4576
|
}))));
|
|
4523
|
-
const grants = yield*
|
|
4577
|
+
const grants = yield* Effect53.all(baseline.grants.map((grant) => owned(ProxmoxAcl(`${id}-${grant.lane}-grant`, {
|
|
4524
4578
|
path: "/",
|
|
4525
4579
|
propagate: true,
|
|
4526
4580
|
roleid: grant.roleid,
|
|
@@ -4533,7 +4587,7 @@ var declareProvisionBaseline = (id, target, names2 = {}, options = {}) => Effect
|
|
|
4533
4587
|
// src/proxmox/qemu.ts
|
|
4534
4588
|
import { Resource as Resource28 } from "alchemy";
|
|
4535
4589
|
import * as Provider28 from "alchemy/Provider";
|
|
4536
|
-
import * as
|
|
4590
|
+
import * as Effect54 from "effect/Effect";
|
|
4537
4591
|
var ProxmoxVm = Resource28("Proxmox.Vm");
|
|
4538
4592
|
var createForm5 = (props) => ({ ...shape9(props), vmid: String(props.vmid) });
|
|
4539
4593
|
var shape9 = (props) => ({
|
|
@@ -4564,11 +4618,11 @@ var handlers24 = pveHandlers({
|
|
|
4564
4618
|
path: (props) => `nodes/${props.node}/qemu/${String(props.vmid)}/config`,
|
|
4565
4619
|
updateForm: shape9
|
|
4566
4620
|
});
|
|
4567
|
-
var ProxmoxVmProvider = () => Provider28.effect(ProxmoxVm,
|
|
4621
|
+
var ProxmoxVmProvider = () => Provider28.effect(ProxmoxVm, Effect54.succeed(ProxmoxVm.Provider.of(handlers24)));
|
|
4568
4622
|
// src/proxmox/replication-job.ts
|
|
4569
4623
|
import { Resource as Resource29 } from "alchemy";
|
|
4570
4624
|
import * as Provider29 from "alchemy/Provider";
|
|
4571
|
-
import * as
|
|
4625
|
+
import * as Effect55 from "effect/Effect";
|
|
4572
4626
|
|
|
4573
4627
|
// src/proxmox/replication-job-form.ts
|
|
4574
4628
|
var DEFAULT_SCHEDULE = "*/15";
|
|
@@ -4622,15 +4676,15 @@ var handlers25 = pveHandlers({
|
|
|
4622
4676
|
path: (props) => `cluster/replication/${jobId(props)}`,
|
|
4623
4677
|
updateForm: updateBody5
|
|
4624
4678
|
});
|
|
4625
|
-
var ProxmoxReplicationJobProvider = () => Provider29.effect(ProxmoxReplicationJob,
|
|
4679
|
+
var ProxmoxReplicationJobProvider = () => Provider29.effect(ProxmoxReplicationJob, Effect55.succeed(ProxmoxReplicationJob.Provider.of(handlers25)));
|
|
4626
4680
|
// src/proxmox/sdn-apply.ts
|
|
4627
4681
|
import { Resource as Resource30 } from "alchemy";
|
|
4628
4682
|
import { isResolved as isResolved10 } from "alchemy/Diff";
|
|
4629
4683
|
import * as Provider30 from "alchemy/Provider";
|
|
4630
|
-
import * as
|
|
4684
|
+
import * as Effect57 from "effect/Effect";
|
|
4631
4685
|
|
|
4632
4686
|
// src/proxmox/sdn-apply-read.ts
|
|
4633
|
-
import * as
|
|
4687
|
+
import * as Effect56 from "effect/Effect";
|
|
4634
4688
|
var MARKED = [
|
|
4635
4689
|
"cluster/sdn/zones",
|
|
4636
4690
|
"cluster/sdn/vnets",
|
|
@@ -4639,7 +4693,7 @@ var MARKED = [
|
|
|
4639
4693
|
"cluster/sdn/route-maps"
|
|
4640
4694
|
];
|
|
4641
4695
|
var subsystemAbsent = (error) => error instanceof PveError && error.status === 501;
|
|
4642
|
-
var markedPending = (target) =>
|
|
4696
|
+
var markedPending = (target) => Effect56.all(MARKED.map((collection2) => pve(target, "read", "GET", `${collection2}?pending=1`).pipe(Effect56.map((rows) => (rows ?? []).filter((row) => row.state !== undefined).length), Effect56.catchIf(subsystemAbsent, () => Effect56.succeed(0)))), { concurrency: "unbounded" }).pipe(Effect56.map((counts) => counts.reduce((total, count2) => total + count2, 0)));
|
|
4643
4697
|
var fabricStaged = (staged, running) => {
|
|
4644
4698
|
if (staged === undefined && running === undefined)
|
|
4645
4699
|
return 0;
|
|
@@ -4647,9 +4701,9 @@ var fabricStaged = (staged, running) => {
|
|
|
4647
4701
|
return "inconsistent";
|
|
4648
4702
|
return staged === running ? 0 : 1;
|
|
4649
4703
|
};
|
|
4650
|
-
var fabricPending = (target) =>
|
|
4704
|
+
var fabricPending = (target) => Effect56.all(["pending", "running"].map((view) => pve(target, "read", "GET", `cluster/sdn/fabrics/all?${view}=1`).pipe(Effect56.map(canonical3), Effect56.catchIf(subsystemAbsent, () => Effect56.succeed(undefined)))), { concurrency: "unbounded" }).pipe(Effect56.flatMap(([staged, running]) => {
|
|
4651
4705
|
const verdict = fabricStaged(staged, running);
|
|
4652
|
-
return verdict === "inconsistent" ?
|
|
4706
|
+
return verdict === "inconsistent" ? Effect56.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.")) : Effect56.succeed(verdict);
|
|
4653
4707
|
}));
|
|
4654
4708
|
var canonical3 = (value) => JSON.stringify(sortDeep(value));
|
|
4655
4709
|
var sortDeep = (value) => {
|
|
@@ -4662,23 +4716,23 @@ var sortDeep = (value) => {
|
|
|
4662
4716
|
}
|
|
4663
4717
|
return value;
|
|
4664
4718
|
};
|
|
4665
|
-
var sdnPendingCount = (target) =>
|
|
4719
|
+
var sdnPendingCount = (target) => Effect56.all([markedPending(target), fabricPending(target)], { concurrency: "unbounded" }).pipe(Effect56.map(([marked, fabric]) => marked + fabric));
|
|
4666
4720
|
|
|
4667
4721
|
// src/proxmox/sdn-apply.ts
|
|
4668
4722
|
var ProxmoxSdnApply = Resource30("Proxmox.SdnApply");
|
|
4669
|
-
var read3 = (props) => sdnPendingCount(props.target).pipe(
|
|
4670
|
-
var ProxmoxSdnApplyProvider = () => Provider30.effect(ProxmoxSdnApply,
|
|
4671
|
-
list: () =>
|
|
4672
|
-
read:
|
|
4723
|
+
var read3 = (props) => sdnPendingCount(props.target).pipe(Effect57.map((pending) => ({ pending })));
|
|
4724
|
+
var ProxmoxSdnApplyProvider = () => Provider30.effect(ProxmoxSdnApply, Effect57.succeed(ProxmoxSdnApply.Provider.of({
|
|
4725
|
+
list: () => Effect57.succeed([]),
|
|
4726
|
+
read: Effect57.fn(function* ({ olds }) {
|
|
4673
4727
|
return yield* read3(olds);
|
|
4674
4728
|
}),
|
|
4675
|
-
diff:
|
|
4729
|
+
diff: Effect57.fn(function* ({ news }) {
|
|
4676
4730
|
if (!isResolved10(news))
|
|
4677
4731
|
return;
|
|
4678
4732
|
const { pending } = yield* read3(news);
|
|
4679
4733
|
return pending === 0 ? { action: "noop" } : { action: "update" };
|
|
4680
4734
|
}),
|
|
4681
|
-
reconcile:
|
|
4735
|
+
reconcile: Effect57.fn(function* ({ news }) {
|
|
4682
4736
|
const staged = yield* read3(news);
|
|
4683
4737
|
if (staged.pending === 0)
|
|
4684
4738
|
return staged;
|
|
@@ -4686,16 +4740,16 @@ var ProxmoxSdnApplyProvider = () => Provider30.effect(ProxmoxSdnApply, Effect56.
|
|
|
4686
4740
|
yield* pve(news.target, "provision", "PUT", "cluster/sdn");
|
|
4687
4741
|
const after = yield* read3(news);
|
|
4688
4742
|
if (after.pending !== 0) {
|
|
4689
|
-
return yield*
|
|
4743
|
+
return yield* Effect57.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."));
|
|
4690
4744
|
}
|
|
4691
4745
|
return after;
|
|
4692
4746
|
}),
|
|
4693
|
-
delete: () =>
|
|
4747
|
+
delete: () => Effect57.void
|
|
4694
4748
|
})));
|
|
4695
4749
|
// src/proxmox/sdn-subnet.ts
|
|
4696
4750
|
import { Resource as Resource31 } from "alchemy";
|
|
4697
4751
|
import * as Provider31 from "alchemy/Provider";
|
|
4698
|
-
import * as
|
|
4752
|
+
import * as Effect58 from "effect/Effect";
|
|
4699
4753
|
|
|
4700
4754
|
// src/proxmox/sdn-subnet-form.ts
|
|
4701
4755
|
var subnetId = (props) => `${props.zone}-${props.cidr.replace("/", "-")}`;
|
|
@@ -4755,11 +4809,11 @@ var handlers26 = pveHandlers({
|
|
|
4755
4809
|
path: (props) => `cluster/sdn/vnets/${props.vnet}/subnets/${subnetId(props)}`,
|
|
4756
4810
|
updateForm: updateForm2
|
|
4757
4811
|
});
|
|
4758
|
-
var ProxmoxSdnSubnetProvider = () => Provider31.effect(ProxmoxSdnSubnet,
|
|
4812
|
+
var ProxmoxSdnSubnetProvider = () => Provider31.effect(ProxmoxSdnSubnet, Effect58.succeed(ProxmoxSdnSubnet.Provider.of(handlers26)));
|
|
4759
4813
|
// src/proxmox/sdn-vnet.ts
|
|
4760
4814
|
import { Resource as Resource32 } from "alchemy";
|
|
4761
4815
|
import * as Provider32 from "alchemy/Provider";
|
|
4762
|
-
import * as
|
|
4816
|
+
import * as Effect59 from "effect/Effect";
|
|
4763
4817
|
var ProxmoxSdnVnet = Resource32("Proxmox.SdnVnet");
|
|
4764
4818
|
var tagOf = (value) => typeof value === "number" ? value : undefined;
|
|
4765
4819
|
var shape11 = (props) => {
|
|
@@ -4797,11 +4851,11 @@ var handlers27 = pveHandlers({
|
|
|
4797
4851
|
path: (props) => `cluster/sdn/vnets/${props.vnet}`,
|
|
4798
4852
|
updateForm: updateForm3
|
|
4799
4853
|
});
|
|
4800
|
-
var ProxmoxSdnVnetProvider = () => Provider32.effect(ProxmoxSdnVnet,
|
|
4854
|
+
var ProxmoxSdnVnetProvider = () => Provider32.effect(ProxmoxSdnVnet, Effect59.succeed(ProxmoxSdnVnet.Provider.of(handlers27)));
|
|
4801
4855
|
// src/proxmox/sdn-zone.ts
|
|
4802
4856
|
import { Resource as Resource33 } from "alchemy";
|
|
4803
4857
|
import * as Provider33 from "alchemy/Provider";
|
|
4804
|
-
import * as
|
|
4858
|
+
import * as Effect60 from "effect/Effect";
|
|
4805
4859
|
var ProxmoxSdnZone = Resource33("Proxmox.SdnZone");
|
|
4806
4860
|
var sameIpam = (live, declared2) => (live === "" ? "pve" : live) === (declared2 === undefined || declared2 === "" ? "pve" : declared2);
|
|
4807
4861
|
var body6 = (props) => ({
|
|
@@ -4854,11 +4908,11 @@ var handlers28 = pveHandlers({
|
|
|
4854
4908
|
return withClears(fields, clear);
|
|
4855
4909
|
}
|
|
4856
4910
|
});
|
|
4857
|
-
var ProxmoxSdnZoneProvider = () => Provider33.effect(ProxmoxSdnZone,
|
|
4911
|
+
var ProxmoxSdnZoneProvider = () => Provider33.effect(ProxmoxSdnZone, Effect60.succeed(ProxmoxSdnZone.Provider.of(handlers28)));
|
|
4858
4912
|
// src/proxmox/storage.ts
|
|
4859
4913
|
import { Resource as Resource34 } from "alchemy";
|
|
4860
4914
|
import * as Provider34 from "alchemy/Provider";
|
|
4861
|
-
import * as
|
|
4915
|
+
import * as Effect61 from "effect/Effect";
|
|
4862
4916
|
var ProxmoxStorage = Resource34("Proxmox.Storage", {
|
|
4863
4917
|
defaultRemovalPolicy: "retain"
|
|
4864
4918
|
});
|
|
@@ -4904,14 +4958,14 @@ var handlers29 = pveHandlers({
|
|
|
4904
4958
|
path: (props) => `storage/${props.storage}`,
|
|
4905
4959
|
updateForm: mutable2
|
|
4906
4960
|
});
|
|
4907
|
-
var ProxmoxStorageProvider = () => Provider34.effect(ProxmoxStorage,
|
|
4961
|
+
var ProxmoxStorageProvider = () => Provider34.effect(ProxmoxStorage, Effect61.succeed(ProxmoxStorage.Provider.of(handlers29)));
|
|
4908
4962
|
// src/proxmox/zfs-pool.ts
|
|
4909
4963
|
import { Resource as Resource35 } from "alchemy";
|
|
4910
4964
|
import * as Provider35 from "alchemy/Provider";
|
|
4911
|
-
import * as
|
|
4965
|
+
import * as Effect63 from "effect/Effect";
|
|
4912
4966
|
|
|
4913
4967
|
// src/proxmox/zfs-pool-write.ts
|
|
4914
|
-
import * as
|
|
4968
|
+
import * as Effect62 from "effect/Effect";
|
|
4915
4969
|
var ZFS_POOL_ENDPOINT = { create: "pve:POST /nodes/{node}/disks/zfs" };
|
|
4916
4970
|
var zfsPoolEndpoint = (props) => props.devices === undefined || props.raidlevel === undefined ? {} : ZFS_POOL_ENDPOINT;
|
|
4917
4971
|
var createForm7 = (props) => ({
|
|
@@ -4923,24 +4977,24 @@ var createForm7 = (props) => ({
|
|
|
4923
4977
|
...props["draid-config"] === undefined ? {} : { "draid-config": props["draid-config"] }
|
|
4924
4978
|
});
|
|
4925
4979
|
var SETTLE_ATTEMPTS = 30;
|
|
4926
|
-
var createPool = (props, collection2, path2, read4) =>
|
|
4980
|
+
var createPool = (props, collection2, path2, read4) => Effect62.gen(function* () {
|
|
4927
4981
|
const existing = yield* read4(props);
|
|
4928
4982
|
if (existing !== undefined)
|
|
4929
4983
|
return existing;
|
|
4930
4984
|
if (props.devices === undefined || props.raidlevel === undefined) {
|
|
4931
|
-
return yield*
|
|
4985
|
+
return yield* Effect62.die(new Error(`${props.node}/${props.name}: this is an adopt-only declaration (no \`devices\` or ` + "`raidlevel`) and the pool is not there to adopt. An adopt-only Proxmox.ZfsPool never " + "creates -- declare both fields for a pool this resource should build, or point this " + "declaration at a pool that already exists on this node."));
|
|
4932
4986
|
}
|
|
4933
4987
|
const upid = yield* pve(props.target, "provision", "POST", collection2, createForm7(props));
|
|
4934
|
-
const settle4 = (attempts) =>
|
|
4988
|
+
const settle4 = (attempts) => Effect62.gen(function* () {
|
|
4935
4989
|
const live = yield* read4(props);
|
|
4936
4990
|
if (live !== undefined || attempts <= 0)
|
|
4937
4991
|
return live;
|
|
4938
|
-
yield*
|
|
4992
|
+
yield* Effect62.sleep("2 seconds");
|
|
4939
4993
|
return yield* settle4(attempts - 1);
|
|
4940
4994
|
});
|
|
4941
4995
|
const after = yield* settle4(SETTLE_ATTEMPTS);
|
|
4942
4996
|
if (after === undefined) {
|
|
4943
|
-
return yield*
|
|
4997
|
+
return yield* Effect62.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."));
|
|
4944
4998
|
}
|
|
4945
4999
|
return after;
|
|
4946
5000
|
});
|
|
@@ -4975,15 +5029,15 @@ var spec2 = {
|
|
|
4975
5029
|
var ProxmoxZfsPool = Resource35("Proxmox.ZfsPool", {
|
|
4976
5030
|
defaultRemovalPolicy: "retain"
|
|
4977
5031
|
});
|
|
4978
|
-
var
|
|
5032
|
+
var ops4 = pveOperations(spec2);
|
|
4979
5033
|
var handlers30 = {
|
|
4980
5034
|
...pveHandlers(spec2),
|
|
4981
|
-
delete:
|
|
5035
|
+
delete: Effect63.fn(function* ({ olds }) {
|
|
4982
5036
|
yield* destroyPool(olds, spec2.path(olds));
|
|
4983
5037
|
}),
|
|
4984
|
-
reconcile: ({ news }) => createPool(news, spec2.collection(news), spec2.path(news),
|
|
5038
|
+
reconcile: ({ news }) => createPool(news, spec2.collection(news), spec2.path(news), ops4.read)
|
|
4985
5039
|
};
|
|
4986
|
-
var ProxmoxZfsPoolProvider = () => Provider35.effect(ProxmoxZfsPool,
|
|
5040
|
+
var ProxmoxZfsPoolProvider = () => Provider35.effect(ProxmoxZfsPool, Effect63.succeed(ProxmoxZfsPool.Provider.of(handlers30)));
|
|
4987
5041
|
export {
|
|
4988
5042
|
PROVISION_DEFAULTS,
|
|
4989
5043
|
PROVISION_PRIVILEGES,
|
|
@@ -5060,5 +5114,5 @@ export {
|
|
|
5060
5114
|
violations
|
|
5061
5115
|
};
|
|
5062
5116
|
|
|
5063
|
-
//# debugId=
|
|
5117
|
+
//# debugId=FBBA36EF988E48B764756E2164756E21
|
|
5064
5118
|
//# sourceMappingURL=index.js.map
|