alasio 4.2.1 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/src/branches.js +295 -0
- package/dist/cli/src/branches.js.map +1 -0
- package/dist/cli/src/commands/branch.js +112 -0
- package/dist/cli/src/commands/branch.js.map +1 -0
- package/dist/cli/src/commands/common.js +27 -8
- package/dist/cli/src/commands/common.js.map +1 -1
- package/dist/cli/src/commands/grafana.js +72 -0
- package/dist/cli/src/commands/grafana.js.map +1 -0
- package/dist/cli/src/commands/init.js +6 -5
- package/dist/cli/src/commands/init.js.map +1 -1
- package/dist/cli/src/commands/lake.js +18 -4
- package/dist/cli/src/commands/lake.js.map +1 -1
- package/dist/cli/src/commands/login.js +12 -7
- package/dist/cli/src/commands/login.js.map +1 -1
- package/dist/cli/src/commands/logs.js +12 -6
- package/dist/cli/src/commands/logs.js.map +1 -1
- package/dist/cli/src/commands/uninstall.js +16 -1
- package/dist/cli/src/commands/uninstall.js.map +1 -1
- package/dist/cli/src/commands.js +3 -1
- package/dist/cli/src/commands.js.map +1 -1
- package/dist/cli/src/kube/api.js +35 -2
- package/dist/cli/src/kube/api.js.map +1 -1
- package/dist/cli/src/kube/apply.js +21 -8
- package/dist/cli/src/kube/apply.js.map +1 -1
- package/dist/cli/src/manifests/alasio.js +143 -74
- package/dist/cli/src/manifests/alasio.js.map +1 -1
- package/dist/cli/src/manifests/branch.js +45 -0
- package/dist/cli/src/manifests/branch.js.map +1 -0
- package/dist/cli/src/manifests/collector.js +199 -0
- package/dist/cli/src/manifests/collector.js.map +1 -0
- package/dist/cli/src/manifests/common.js +44 -11
- package/dist/cli/src/manifests/common.js.map +1 -1
- package/dist/cli/src/manifests/config.js +33 -19
- package/dist/cli/src/manifests/config.js.map +1 -1
- package/dist/cli/src/manifests/grafana.js +81 -0
- package/dist/cli/src/manifests/grafana.js.map +1 -0
- package/dist/cli/src/manifests/index.js +6 -1
- package/dist/cli/src/manifests/index.js.map +1 -1
- package/dist/cli/src/manifests/lake.js +66 -18
- package/dist/cli/src/manifests/lake.js.map +1 -1
- package/dist/cli/src/manifests/neon.js +24 -157
- package/dist/cli/src/manifests/neon.js.map +1 -1
- package/dist/cli/src/manifests/network-policies.js +141 -38
- package/dist/cli/src/manifests/network-policies.js.map +1 -1
- package/dist/cli/src/manifests/workspace-storage.js +98 -29
- package/dist/cli/src/manifests/workspace-storage.js.map +1 -1
- package/dist/cli/src/release.js +6 -5
- package/dist/cli/src/release.js.map +1 -1
- package/dist/neon/control/branches.js +292 -0
- package/dist/neon/control/branches.js.map +1 -0
- package/dist/neon/control/jwt.js +58 -0
- package/dist/neon/control/jwt.js.map +1 -0
- package/dist/neon/control/scram.js +15 -0
- package/dist/neon/control/scram.js.map +1 -0
- package/dist/neon/control/secrets.js +111 -0
- package/dist/neon/control/secrets.js.map +1 -0
- package/dist/src/branch/names.js +18 -0
- package/dist/src/branch/names.js.map +1 -0
- package/dist/src/kube/config.js +14 -1
- package/dist/src/kube/config.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/src/release.js
CHANGED
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
* version and the digests of the images it published here, with tooling/pin-release.ts,
|
|
4
4
|
* before it builds the package; this module holds nothing else.
|
|
5
5
|
*/
|
|
6
|
-
export const VERSION = "
|
|
6
|
+
export const VERSION = "5.0.0";
|
|
7
7
|
/** alasio's own images, tagged with the version and pinned by digest once released. */
|
|
8
8
|
export const IMAGES = {
|
|
9
|
-
alasio: { repository: "ghcr.io/eaucoin/alasio", tag: VERSION, digest: "sha256:
|
|
10
|
-
agent: { repository: "ghcr.io/eaucoin/alasio-agent", tag: VERSION, digest: "sha256:
|
|
11
|
-
lake: { repository: "ghcr.io/eaucoin/alasio-lake", tag: VERSION, digest: "sha256:
|
|
9
|
+
alasio: { repository: "ghcr.io/eaucoin/alasio", tag: VERSION, digest: "sha256:3e9e3ace6f20b94c242126a7ab57b7e210d6292829eb0f3b64f3f4ebafd9b98c" },
|
|
10
|
+
agent: { repository: "ghcr.io/eaucoin/alasio-agent", tag: VERSION, digest: "sha256:0a6671c6899b3df079fe0a0646019485a4205af3a0847c95e2def818c63e0261" },
|
|
11
|
+
lake: { repository: "ghcr.io/eaucoin/alasio-lake", tag: VERSION, digest: "sha256:265421f037f2677f955a4a6852872bee4aeb3a258132ef7bf05ab6e3b2ccf277" },
|
|
12
|
+
grafana: { repository: "ghcr.io/eaucoin/alasio-grafana", tag: VERSION, digest: "sha256:885de54829d3b96e5202384ab55b21c39d358dd44aea88eb51cb15d1942b1ad8" },
|
|
12
13
|
};
|
|
13
14
|
/** The node image of the cluster in Docker: k3s with gVisor (cluster/node), pinned like the others. */
|
|
14
|
-
export const NODE_IMAGE = { repository: "ghcr.io/eaucoin/alasio-node", tag: VERSION, digest: "sha256:
|
|
15
|
+
export const NODE_IMAGE = { repository: "ghcr.io/eaucoin/alasio-node", tag: VERSION, digest: "sha256:5f11e256cdbc506f12c371de2b1a57651f9532d9b3e15f2faca477f6b3baa376" };
|
|
15
16
|
//# sourceMappingURL=release.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"release.js","sourceRoot":"","sources":["../../../src/release.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAE/B,uFAAuF;AACvF,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,MAAM,EAAE,EAAE,UAAU,EAAE,wBAAwB,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,yEAAyE,EAAE;IACjJ,KAAK,EAAE,EAAE,UAAU,EAAE,8BAA8B,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,yEAAyE,EAAE;IACtJ,IAAI,EAAE,EAAE,UAAU,EAAE,6BAA6B,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,yEAAyE,EAAE;
|
|
1
|
+
{"version":3,"file":"release.js","sourceRoot":"","sources":["../../../src/release.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAE/B,uFAAuF;AACvF,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,MAAM,EAAE,EAAE,UAAU,EAAE,wBAAwB,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,yEAAyE,EAAE;IACjJ,KAAK,EAAE,EAAE,UAAU,EAAE,8BAA8B,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,yEAAyE,EAAE;IACtJ,IAAI,EAAE,EAAE,UAAU,EAAE,6BAA6B,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,yEAAyE,EAAE;IACpJ,OAAO,EAAE,EAAE,UAAU,EAAE,gCAAgC,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,yEAAyE,EAAE;CAC3J,CAAC;AAEF,uGAAuG;AACvG,MAAM,CAAC,MAAM,UAAU,GAAG,EAAE,UAAU,EAAE,6BAA6B,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,yEAAyE,EAAE,CAAC"}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* neon-control's branches: alasio's timeline, the branch `main`, and the child timelines
|
|
3
|
+
* branched from it or from each other, each with a compute neon-control serves a spec.
|
|
4
|
+
* What is decided about them, without I/O: names, requests, the record of each, what
|
|
5
|
+
* the storage controller's answers mean, and the spec of a branch's compute. The service
|
|
6
|
+
* (./service.ts) does the I/O.
|
|
7
|
+
*
|
|
8
|
+
* A branch is `creating` from the moment its timeline id is chosen until the storage
|
|
9
|
+
* controller has placed it, `ready` from then, and `deleting` from when its deletion
|
|
10
|
+
* starts until the storage controller has deleted it, when its record goes. Its id and
|
|
11
|
+
* branch point are recorded before the storage controller is first asked, so a creation
|
|
12
|
+
* asked again, after a failure or a restart, asks for exactly the same timeline.
|
|
13
|
+
*
|
|
14
|
+
* A branch's compute has credentials of its own, which whoever asks for the branch makes
|
|
15
|
+
* and neon-control is given only the hashes of: the SCRAM verifier of the password of
|
|
16
|
+
* alasio's role on it, which its spec gives the role, and the SHA-256 of the token it
|
|
17
|
+
* fetches its spec with, which fetches its spec alone. So what a branch is given reaches
|
|
18
|
+
* neither main's compute nor another branch's spec. main's compute keeps the stack's.
|
|
19
|
+
*/
|
|
20
|
+
import { createHash, timingSafeEqual } from "node:crypto";
|
|
21
|
+
import { DATABASE, ROLE } from "./secrets.js";
|
|
22
|
+
/** alasio's own branch: its first timeline, bootstrapped, never deleted. */
|
|
23
|
+
export const MAIN = "main";
|
|
24
|
+
/**
|
|
25
|
+
* The longest name a branch may have. Branch environments name objects
|
|
26
|
+
* `alasio-branch-<name>-<part>`; at this length one with a part of up to 18 characters
|
|
27
|
+
* still fits the 63 characters Kubernetes allows a namespace, a Service or a label value.
|
|
28
|
+
*/
|
|
29
|
+
export const MAX_NAME_LENGTH = 30;
|
|
30
|
+
/**
|
|
31
|
+
* The scope of the lake's token, which lists the branches and does nothing else: Neon's
|
|
32
|
+
* own services do not know it.
|
|
33
|
+
*/
|
|
34
|
+
export const BRANCHES_SCOPE = "branches";
|
|
35
|
+
/**
|
|
36
|
+
* The scopes of the tokens a call of neon-control's API (`method` on `url`) is taken
|
|
37
|
+
* with: the admin's, as the storage controller's hooks bear it, and for a list of the
|
|
38
|
+
* branches the lake's too, which keeps its files while any branch may read them.
|
|
39
|
+
*/
|
|
40
|
+
export function callScopes(method, url) {
|
|
41
|
+
return method === "GET" && url === "/branches" ? ["admin", BRANCHES_SCOPE] : ["admin"];
|
|
42
|
+
}
|
|
43
|
+
/** A refusal for the API's caller: its HTTP status, and what it says. */
|
|
44
|
+
export class BranchError extends Error {
|
|
45
|
+
status;
|
|
46
|
+
constructor(status, message) {
|
|
47
|
+
super(message);
|
|
48
|
+
this.status = status;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** What is wrong with `name` as a branch's name, or null if nothing is. */
|
|
52
|
+
export function nameProblem(name) {
|
|
53
|
+
if (name === MAIN)
|
|
54
|
+
return `${MAIN} is alasio's own branch`;
|
|
55
|
+
if (name.length > MAX_NAME_LENGTH)
|
|
56
|
+
return `a branch's name is at most ${MAX_NAME_LENGTH} characters`;
|
|
57
|
+
// A DNS-1123 label, as the names of the objects named after it must be.
|
|
58
|
+
if (!/^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/u.test(name))
|
|
59
|
+
return "a branch's name is lowercase letters, digits and inner hyphens";
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The branch point `text` names, an LSN written `<high>/<low>` in hexadecimal, as Neon
|
|
64
|
+
* writes it, and aligned up to 8 bytes, as the pageserver branches at it; null if it
|
|
65
|
+
* names none.
|
|
66
|
+
*/
|
|
67
|
+
export function branchPoint(text) {
|
|
68
|
+
const match = /^([0-9a-f]{1,8})\/([0-9a-f]{1,8})$/iu.exec(text);
|
|
69
|
+
if (!match)
|
|
70
|
+
return null;
|
|
71
|
+
const [, high = "", low = ""] = match;
|
|
72
|
+
const aligned = ((BigInt(`0x${high}`) << 32n) + BigInt(`0x${low}`) + 7n) & ~7n;
|
|
73
|
+
return `${(aligned >> 32n).toString(16).toUpperCase()}/${(aligned & 0xffffffffn).toString(16).toUpperCase()}`;
|
|
74
|
+
}
|
|
75
|
+
/** The SHA-256 of `token`, in hexadecimal: what neon-control keeps of a compute's token. */
|
|
76
|
+
export const tokenSha256 = (token) => createHash("sha256").update(token).digest("hex");
|
|
77
|
+
/** The credentials `value` gives a branch's compute, checked. */
|
|
78
|
+
function computeCredentials(value) {
|
|
79
|
+
const { passwordVerifier, tokenSha256: sha, ...rest } = (typeof value === "object" && value !== null ? value : {});
|
|
80
|
+
if (Object.keys(rest).length > 0 || typeof passwordVerifier !== "string" || !passwordVerifier.startsWith("SCRAM-SHA-256$") ||
|
|
81
|
+
typeof sha !== "string" || !/^[0-9a-f]{64}$/u.test(sha)) {
|
|
82
|
+
throw new BranchError(400, "a branch's compute is given as { passwordVerifier, tokenSha256 }: a SCRAM-SHA-256 verifier, and a SHA-256 in hexadecimal");
|
|
83
|
+
}
|
|
84
|
+
return { passwordVerifier, tokenSha256: sha };
|
|
85
|
+
}
|
|
86
|
+
/** The branch `body` asks for: `{ name, parent?, lsn?, compute }`, its parent main if not given. */
|
|
87
|
+
export function branchRequest(body) {
|
|
88
|
+
if (typeof body !== "object" || body === null)
|
|
89
|
+
throw new BranchError(400, "a branch is asked for as { name, parent?, lsn?, compute }");
|
|
90
|
+
const { name, parent = MAIN, lsn = null, compute, ...rest } = body;
|
|
91
|
+
const unknown = Object.keys(rest);
|
|
92
|
+
if (unknown.length > 0)
|
|
93
|
+
throw new BranchError(400, `a branch has no ${unknown.join(", ")}`);
|
|
94
|
+
if (typeof name !== "string")
|
|
95
|
+
throw new BranchError(400, "a branch is asked for by its name");
|
|
96
|
+
const problem = nameProblem(name);
|
|
97
|
+
if (problem)
|
|
98
|
+
throw new BranchError(400, `${name} is no branch's name: ${problem}`);
|
|
99
|
+
if (typeof parent !== "string")
|
|
100
|
+
throw new BranchError(400, "a branch's parent is a branch's name");
|
|
101
|
+
const credentials = computeCredentials(compute);
|
|
102
|
+
if (lsn === null)
|
|
103
|
+
return { name, parent, lsn, compute: credentials };
|
|
104
|
+
const point = typeof lsn === "string" ? branchPoint(lsn) : null;
|
|
105
|
+
if (point === null)
|
|
106
|
+
throw new BranchError(400, `${String(lsn)} is no LSN: an LSN is written <high>/<low>, in hexadecimal`);
|
|
107
|
+
return { name, parent, lsn: point, compute: credentials };
|
|
108
|
+
}
|
|
109
|
+
/** The id of the compute of the branch `name`: `alasio` for main's, `branch-<name>` for any other's. */
|
|
110
|
+
export function computeId(name) {
|
|
111
|
+
return name === MAIN ? "alasio" : `branch-${name}`;
|
|
112
|
+
}
|
|
113
|
+
/** The ready branch whose compute is `id`, if any. */
|
|
114
|
+
export function branchOfCompute(branches, id) {
|
|
115
|
+
return branches.find((branch) => branch.state === "ready" && computeId(branch.name) === id);
|
|
116
|
+
}
|
|
117
|
+
/** The compute whose token `authorization` bears, by its id: main's, the stack's `mainToken`, or a branch's own; null for none. */
|
|
118
|
+
export function computeOfToken(branches, authorization, mainToken) {
|
|
119
|
+
const token = /^Bearer (.+)$/u.exec(authorization ?? "")?.[1];
|
|
120
|
+
if (!token)
|
|
121
|
+
return null;
|
|
122
|
+
const same = (a, b) => a.length === b.length && timingSafeEqual(Buffer.from(a), Buffer.from(b));
|
|
123
|
+
if (mainToken && same(token, mainToken))
|
|
124
|
+
return computeId(MAIN);
|
|
125
|
+
const sha = tokenSha256(token);
|
|
126
|
+
const owner = branches.find((branch) => branch.compute !== null && same(sha, branch.compute.tokenSha256));
|
|
127
|
+
return owner ? computeId(owner.name) : null;
|
|
128
|
+
}
|
|
129
|
+
/** `branch` as the API answers it (BranchView). */
|
|
130
|
+
export function branchView({ compute: _credentials, ...branch }) {
|
|
131
|
+
return { ...branch, computeId: computeId(branch.name) };
|
|
132
|
+
}
|
|
133
|
+
/** The branch named `name`, if any. */
|
|
134
|
+
export const branchNamed = (branches, name) => branches.find((branch) => branch.name === name);
|
|
135
|
+
/** `branches` with `branch` in place of the one of its name, or added after them. */
|
|
136
|
+
export function withBranch(branches, branch) {
|
|
137
|
+
const index = branches.findIndex(({ name }) => name === branch.name);
|
|
138
|
+
return index === -1 ? [...branches, branch] : branches.map((existing, at) => (at === index ? branch : existing));
|
|
139
|
+
}
|
|
140
|
+
/** `branches` without the one named `name`. */
|
|
141
|
+
export const withoutBranch = (branches, name) => branches.filter((branch) => branch.name !== name);
|
|
142
|
+
/**
|
|
143
|
+
* What asking for `request` comes to, given `branches`: the branch of that name already
|
|
144
|
+
* there, ready or still being created, when it is the one asked for; null when there is
|
|
145
|
+
* none, and its parent is ready to branch from. Refuses a name taken by another branch,
|
|
146
|
+
* or one being deleted, and a parent that is not ready.
|
|
147
|
+
*/
|
|
148
|
+
export function existingBranch(branches, request) {
|
|
149
|
+
const existing = branchNamed(branches, request.name);
|
|
150
|
+
if (existing) {
|
|
151
|
+
const same = existing.parent === request.parent && (request.lsn === null || existing.lsn === request.lsn);
|
|
152
|
+
if (existing.state === "deleting")
|
|
153
|
+
throw new BranchError(409, `${request.name} is being deleted`);
|
|
154
|
+
if (!same)
|
|
155
|
+
throw new BranchError(409, `${request.name} is a branch of ${existing.parent} at ${existing.lsn} already`);
|
|
156
|
+
return existing;
|
|
157
|
+
}
|
|
158
|
+
const parent = branchNamed(branches, request.parent);
|
|
159
|
+
if (!parent)
|
|
160
|
+
throw new BranchError(404, `there is no branch ${request.parent} to branch from`);
|
|
161
|
+
if (parent.state !== "ready")
|
|
162
|
+
throw new BranchError(409, `${request.parent} is ${parent.state}, and is branched from only once ready`);
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Starts deleting the branch `name`: `branches` with it `deleting`, which neither serves
|
|
167
|
+
* its compute nor is repaired. Refuses main, a branch there is not, and one that is the
|
|
168
|
+
* parent of another, which the pageserver would refuse to delete.
|
|
169
|
+
*/
|
|
170
|
+
export function deleting(branches, name) {
|
|
171
|
+
if (name === MAIN)
|
|
172
|
+
throw new BranchError(400, `${MAIN} is alasio's own branch, which is never deleted`);
|
|
173
|
+
const branch = branchNamed(branches, name);
|
|
174
|
+
if (!branch)
|
|
175
|
+
throw new BranchError(404, `there is no branch ${name}`);
|
|
176
|
+
const children = branches.filter((child) => child.parent === name).map((child) => child.name);
|
|
177
|
+
if (children.length > 0)
|
|
178
|
+
throw new BranchError(412, `${name} has branches of its own, deleted first: ${children.join(", ")}`);
|
|
179
|
+
return withBranch(branches, { name, parent: branch.parent, timelineId: branch.timelineId, lsn: branch.lsn, createdAt: branch.createdAt, compute: branch.compute, state: "deleting" });
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* The placement of `branch`'s timeline the storage controller created, as its answer
|
|
183
|
+
* says, once that answer is checked to be of the timeline asked for. The request is an
|
|
184
|
+
* untagged union whose last form is a timeline bootstrapped anew, which a request for a
|
|
185
|
+
* branch the controller did not read as one becomes; so a branch's answer must name its
|
|
186
|
+
* parent's timeline and its branch point.
|
|
187
|
+
*/
|
|
188
|
+
export function placementOf(branch, parentTimelineId, created) {
|
|
189
|
+
const lsn = typeof created.ancestor_lsn === "string" ? branchPoint(created.ancestor_lsn) : null;
|
|
190
|
+
const made = `the storage controller made timeline ${String(created.timeline_id)}, of ${String(created.ancestor_timeline_id ?? "no ancestor")} at ${lsn ?? "no LSN"}`;
|
|
191
|
+
if (created.timeline_id !== branch.timelineId || (created.ancestor_timeline_id ?? null) !== parentTimelineId || (parentTimelineId !== null && lsn !== branch.lsn)) {
|
|
192
|
+
throw new BranchError(502, `${made}, not ${branch.name}'s ${branch.timelineId} of ${parentTimelineId ?? "no ancestor"} at ${branch.lsn ?? "no LSN"}`);
|
|
193
|
+
}
|
|
194
|
+
if (!created.safekeepers)
|
|
195
|
+
throw new BranchError(502, `the storage controller placed ${branch.name}'s timeline on no safekeepers`);
|
|
196
|
+
return { generation: created.safekeepers.generation, ids: created.safekeepers.safekeepers.map((sk) => sk.id) };
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* What the storage controller's refusal to create `branch` means, its `status` (null when
|
|
200
|
+
* it did not answer) and `message`: a branch point its parent's history no longer holds,
|
|
201
|
+
* or does not reach, is the caller's to change (406); any other is the caller's to ask
|
|
202
|
+
* again, for the same timeline. The controller answers a pageserver's refusal, other than
|
|
203
|
+
* a 404 or 503, as a 409 that names the pageserver's status.
|
|
204
|
+
*/
|
|
205
|
+
export function creationRefused(branch, status, message) {
|
|
206
|
+
if (status === 406 || (status === 409 && message.includes("406 Not Acceptable"))) {
|
|
207
|
+
return new BranchError(406, `${branch.parent} has no ${branch.lsn} to branch ${branch.name} from: it is below its garbage collection cutoff, or past its end (${message})`);
|
|
208
|
+
}
|
|
209
|
+
const answered = status === null ? message : `${status} ${message}`;
|
|
210
|
+
return new BranchError(503, `the storage controller did not create ${branch.name}, which may be asked for again: ${answered}`);
|
|
211
|
+
}
|
|
212
|
+
/** The notification `body` is, checked. */
|
|
213
|
+
export function safekeepersNotified(body) {
|
|
214
|
+
const { timeline_id, generation, safekeepers } = (typeof body === "object" && body !== null ? body : {});
|
|
215
|
+
const ids = Array.isArray(safekeepers) ? safekeepers.map((sk) => (typeof sk === "object" && sk !== null ? sk["id"] : undefined)) : [];
|
|
216
|
+
if (typeof timeline_id !== "string" || typeof generation !== "number" || ids.length === 0 || !ids.every((id) => typeof id === "number")) {
|
|
217
|
+
throw new BranchError(400, "safekeepers are notified as { tenant_id, timeline_id, generation, safekeepers: [{ id }] }");
|
|
218
|
+
}
|
|
219
|
+
return { timelineId: timeline_id, placement: { generation, ids: ids } };
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* `branches` with the ready branch of the notified timeline placed where it says, when
|
|
223
|
+
* it is of a later generation than its own: what its compute's spec names, and what the
|
|
224
|
+
* safekeepers are repaired with, from then on. Null when no branch changes.
|
|
225
|
+
*/
|
|
226
|
+
export function withNotifiedPlacement(branches, { timelineId, placement }) {
|
|
227
|
+
const branch = branches.find((candidate) => candidate.state === "ready" && candidate.timelineId === timelineId);
|
|
228
|
+
if (!branch || placement.generation <= branch.safekeepers.generation)
|
|
229
|
+
return null;
|
|
230
|
+
return withBranch(branches, { ...branch, safekeepers: placement });
|
|
231
|
+
}
|
|
232
|
+
/** The port every compute serves Postgres on. */
|
|
233
|
+
export const COMPUTE_PORT = 55433;
|
|
234
|
+
function setting(name, value, vartype) {
|
|
235
|
+
return { name, value: String(value), vartype };
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* The spec of `branch`'s compute: a primary on its timeline, at its safekeepers. The role
|
|
239
|
+
* and database are main's, which every branch inherits with its data; the role's password
|
|
240
|
+
* is the branch's own.
|
|
241
|
+
*
|
|
242
|
+
* Its storage token is the tenant's, as the pageserver and safekeepers take no narrower
|
|
243
|
+
* one: a timeline is not a scope of theirs. A branch's compute could read main's timeline
|
|
244
|
+
* with it, which only Neon's storage services take, and only from the stack's pods.
|
|
245
|
+
*/
|
|
246
|
+
export function computeConfig(branch, stack) {
|
|
247
|
+
// The timeline was placed on safekeepers of the stack, so each id is one of theirs.
|
|
248
|
+
const safekeeper = (id) => stack.safekeepers.find((sk) => sk.id === id);
|
|
249
|
+
return {
|
|
250
|
+
spec: {
|
|
251
|
+
format_version: 1.0,
|
|
252
|
+
suspend_timeout_seconds: -1,
|
|
253
|
+
cluster: {
|
|
254
|
+
cluster_id: "alasio",
|
|
255
|
+
name: "alasio",
|
|
256
|
+
roles: [{ name: ROLE, encrypted_password: branch.compute?.passwordVerifier ?? stack.passwordVerifier, options: null }],
|
|
257
|
+
databases: [{ name: DATABASE, owner: ROLE, options: null }],
|
|
258
|
+
settings: [
|
|
259
|
+
setting("listen_addresses", "0.0.0.0", "string"),
|
|
260
|
+
setting("port", COMPUTE_PORT, "integer"),
|
|
261
|
+
setting("max_connections", 100, "integer"),
|
|
262
|
+
setting("shared_buffers", "128MB", "string"),
|
|
263
|
+
setting("password_encryption", "scram-sha-256", "enum"),
|
|
264
|
+
// Durability is the safekeepers': a commit waits for their quorum,
|
|
265
|
+
// and the compute's own disk is rebuilt on every start.
|
|
266
|
+
setting("fsync", "off", "bool"),
|
|
267
|
+
setting("synchronous_standby_names", "walproposer", "string"),
|
|
268
|
+
setting("shared_preload_libraries", "neon", "string"),
|
|
269
|
+
setting("wal_level", "replica", "enum"),
|
|
270
|
+
setting("wal_log_hints", "off", "bool"),
|
|
271
|
+
setting("wal_keep_size", 0, "integer"),
|
|
272
|
+
setting("wal_sender_timeout", "5s", "string"),
|
|
273
|
+
setting("max_wal_senders", 10, "integer"),
|
|
274
|
+
setting("max_replication_slots", 10, "integer"),
|
|
275
|
+
setting("max_replication_write_lag", "15MB", "string"),
|
|
276
|
+
setting("max_replication_flush_lag", "10GB", "string"),
|
|
277
|
+
setting("restart_after_crash", "off", "bool"),
|
|
278
|
+
],
|
|
279
|
+
},
|
|
280
|
+
delta_operations: [],
|
|
281
|
+
tenant_id: stack.tenantId,
|
|
282
|
+
timeline_id: branch.timelineId,
|
|
283
|
+
mode: "Primary",
|
|
284
|
+
pageserver_connstring: `postgresql://no_user@${stack.pageserverHost}:6400`,
|
|
285
|
+
safekeepers_generation: branch.safekeepers.generation,
|
|
286
|
+
safekeeper_connstrings: branch.safekeepers.ids.map((id) => `${safekeeper(id).host}:${safekeeper(id).pgPort}`),
|
|
287
|
+
storage_auth_token: stack.storageAuthToken,
|
|
288
|
+
},
|
|
289
|
+
compute_ctl_config: { jwks: stack.jwks },
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
//# sourceMappingURL=branches.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branches.js","sourceRoot":"","sources":["../../../../neon/control/branches.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The tokens Neon's services authenticate each other with: EdDSA JWTs with a
|
|
3
|
+
* scope, and a tenant for tenant-scoped ones, with no expiry, as Neon issues
|
|
4
|
+
* them (libs/utils/src/auth.rs).
|
|
5
|
+
*/
|
|
6
|
+
import { createPrivateKey, createPublicKey, generateKeyPairSync, sign, verify } from "node:crypto";
|
|
7
|
+
const HEADER = base64url(JSON.stringify({ alg: "EdDSA", typ: "JWT" }));
|
|
8
|
+
function base64url(value) {
|
|
9
|
+
return Buffer.from(value).toString("base64url");
|
|
10
|
+
}
|
|
11
|
+
/** A new Ed25519 keypair, as PEM. */
|
|
12
|
+
export function generateKeyPair() {
|
|
13
|
+
const { privateKey, publicKey } = generateKeyPairSync("ed25519", {
|
|
14
|
+
privateKeyEncoding: { type: "pkcs8", format: "pem" },
|
|
15
|
+
publicKeyEncoding: { type: "spki", format: "pem" },
|
|
16
|
+
});
|
|
17
|
+
return { privateKeyPem: privateKey, publicKeyPem: publicKey };
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A token for `scope` (tenant, pageserverapi, safekeeperdata,
|
|
21
|
+
* generations_api, admin, infra, ...), limited to `tenantId` if given.
|
|
22
|
+
*/
|
|
23
|
+
export function signToken(privateKeyPem, scope, tenantId = null) {
|
|
24
|
+
const claims = tenantId ? { scope, tenant_id: tenantId } : { scope };
|
|
25
|
+
const unsigned = `${HEADER}.${base64url(JSON.stringify(claims))}`;
|
|
26
|
+
const signature = sign(null, Buffer.from(unsigned), createPrivateKey(privateKeyPem));
|
|
27
|
+
return `${unsigned}.${signature.toString("base64url")}`;
|
|
28
|
+
}
|
|
29
|
+
/** The claims of `token` if `publicKeyPem` signed it, else null. */
|
|
30
|
+
export function verifyToken(publicKeyPem, token) {
|
|
31
|
+
const [header, claims, signature, ...rest] = String(token).split(".");
|
|
32
|
+
if (claims === undefined || signature === undefined || rest.length !== 0 || header !== HEADER)
|
|
33
|
+
return null;
|
|
34
|
+
const valid = verify(null, Buffer.from(`${header}.${claims}`), createPublicKey(publicKeyPem), Buffer.from(signature, "base64url"));
|
|
35
|
+
if (!valid)
|
|
36
|
+
return null;
|
|
37
|
+
try {
|
|
38
|
+
// Signed with the stack's key, so made by signToken.
|
|
39
|
+
const parsed = JSON.parse(Buffer.from(claims, "base64url").toString("utf8"));
|
|
40
|
+
return parsed;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** Whether `authorization`, an HTTP Authorization header, bears a token `publicKeyPem` signed for one of `scopes`. */
|
|
47
|
+
export function bearsScope(publicKeyPem, authorization, ...scopes) {
|
|
48
|
+
if (!authorization?.startsWith("Bearer "))
|
|
49
|
+
return false;
|
|
50
|
+
const scope = verifyToken(publicKeyPem, authorization.slice("Bearer ".length))?.scope;
|
|
51
|
+
return scope !== undefined && scopes.includes(scope);
|
|
52
|
+
}
|
|
53
|
+
/** The public key as a JWK set, which compute_ctl verifies its API's tokens with. */
|
|
54
|
+
export function publicJwks(publicKeyPem) {
|
|
55
|
+
const jwk = createPublicKey(publicKeyPem).export({ format: "jwk" });
|
|
56
|
+
return { keys: [{ ...jwk, use: "sig", key_ops: ["verify"], alg: "EdDSA", kid: "alasio-neon" }] };
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=jwt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt.js","sourceRoot":"","sources":["../../../../neon/control/jwt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A Postgres SCRAM-SHA-256 password verifier (RFC 5802, 7677), in the form
|
|
3
|
+
* Postgres stores and compute_ctl passes through as a role's
|
|
4
|
+
* encrypted_password: the password itself never reaches the compute's spec.
|
|
5
|
+
*/
|
|
6
|
+
import { createHash, createHmac, pbkdf2Sync, randomBytes } from "node:crypto";
|
|
7
|
+
const ITERATIONS = 4096;
|
|
8
|
+
export function scramVerifier(password, salt = randomBytes(16)) {
|
|
9
|
+
const salted = pbkdf2Sync(Buffer.from(password, "utf8"), salt, ITERATIONS, 32, "sha256");
|
|
10
|
+
const clientKey = createHmac("sha256", salted).update("Client Key").digest();
|
|
11
|
+
const storedKey = createHash("sha256").update(clientKey).digest();
|
|
12
|
+
const serverKey = createHmac("sha256", salted).update("Server Key").digest();
|
|
13
|
+
return `SCRAM-SHA-256$${ITERATIONS}:${salt.toString("base64")}$${storedKey.toString("base64")}:${serverKey.toString("base64")}`;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=scram.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scram.js","sourceRoot":"","sources":["../../../../neon/control/scram.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE9E,MAAM,UAAU,GAAG,IAAI,CAAC;AAExB,MAAM,UAAU,aAAa,CAAC,QAAgB,EAAE,IAAI,GAAW,WAAW,CAAC,EAAE,CAAC;IAC5E,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAC;IACzF,MAAM,SAAS,GAAG,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,MAAM,EAAE,CAAC;IAC7E,MAAM,SAAS,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,CAAC;IAClE,MAAM,SAAS,GAAG,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,MAAM,EAAE,CAAC;IAC7E,OAAO,iBAAiB,UAAU,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;AAClI,CAAC"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The secrets alasio's Neon runs on, and the configuration rendered from them, for the
|
|
3
|
+
* setup job (./kube-setup.ts): made once and kept, completed when a deployment predates
|
|
4
|
+
* one, never rotated.
|
|
5
|
+
*/
|
|
6
|
+
import { randomBytes, randomUUID } from "node:crypto";
|
|
7
|
+
export const PAGESERVER_ID = 1;
|
|
8
|
+
export const DATABASE = "alasio";
|
|
9
|
+
export const ROLE = "alasio";
|
|
10
|
+
function hexId() {
|
|
11
|
+
return randomUUID().replaceAll("-", "");
|
|
12
|
+
}
|
|
13
|
+
function secret() {
|
|
14
|
+
return randomBytes(24).toString("base64url");
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The stack's secrets, `existing` completed: made on first run and then only added to,
|
|
18
|
+
* never rotated. Each secret has a maker, and any absent is filled in, so a deployment
|
|
19
|
+
* that predates a new secret gains it on the next install or upgrade without disturbing
|
|
20
|
+
* the rest. Returns `{ secrets, changed }`.
|
|
21
|
+
*/
|
|
22
|
+
export function completeSecrets(existing = {}) {
|
|
23
|
+
const stored = structuredClone(existing);
|
|
24
|
+
let changed = false;
|
|
25
|
+
const kept = (value, make) => {
|
|
26
|
+
if (value !== undefined)
|
|
27
|
+
return value;
|
|
28
|
+
changed = true;
|
|
29
|
+
return make();
|
|
30
|
+
};
|
|
31
|
+
// A secret already held keeps its place; one made is added after them, in this order.
|
|
32
|
+
const s3 = kept(stored.s3, () => ({}));
|
|
33
|
+
const secrets = {
|
|
34
|
+
...stored,
|
|
35
|
+
tenantId: kept(stored.tenantId, () => hexId()),
|
|
36
|
+
timelineId: kept(stored.timelineId, () => hexId()),
|
|
37
|
+
s3: {
|
|
38
|
+
...s3,
|
|
39
|
+
neon: kept(s3.neon, () => ({ accessKey: `neon${hexId().slice(0, 12)}`, secretKey: secret() })),
|
|
40
|
+
admin: kept(s3.admin, () => ({ accessKey: `admin${hexId().slice(0, 12)}`, secretKey: secret() })),
|
|
41
|
+
lake: kept(s3.lake, () => ({ accessKey: `lake${hexId().slice(0, 12)}`, secretKey: secret() })),
|
|
42
|
+
lakeReader: kept(s3.lakeReader, () => ({ accessKey: `lakereader${hexId().slice(0, 12)}`, secretKey: secret() })),
|
|
43
|
+
workspaces: kept(s3.workspaces, () => ({ accessKey: `workspaces${hexId().slice(0, 12)}`, secretKey: secret() })),
|
|
44
|
+
},
|
|
45
|
+
controllerDbPassword: kept(stored.controllerDbPassword, () => secret()),
|
|
46
|
+
alasioPassword: kept(stored.alasioPassword, () => secret()),
|
|
47
|
+
lakePassword: kept(stored.lakePassword, () => secret()),
|
|
48
|
+
computeControlToken: kept(stored.computeControlToken, () => secret()),
|
|
49
|
+
valkeyPassword: kept(stored.valkeyPassword, () => secret()),
|
|
50
|
+
lakeReaderPassword: kept(stored.lakeReaderPassword, () => secret()),
|
|
51
|
+
lakeQueryToken: kept(stored.lakeQueryToken, () => secret()),
|
|
52
|
+
grafanaPassword: kept(stored.grafanaPassword, () => secret()),
|
|
53
|
+
grafanaAdminPassword: kept(stored.grafanaAdminPassword, () => secret()),
|
|
54
|
+
grafanaSecretKey: kept(stored.grafanaSecretKey, () => secret()),
|
|
55
|
+
branchForkKey: kept(stored.branchForkKey, () => secret()),
|
|
56
|
+
};
|
|
57
|
+
return { secrets, changed };
|
|
58
|
+
}
|
|
59
|
+
function toml(value) {
|
|
60
|
+
return `'${String(value).replaceAll("'", "")}'`;
|
|
61
|
+
}
|
|
62
|
+
/** Neon's S3 settings, under `prefix` of `bucket` at `endpoint`, as a TOML inline table. */
|
|
63
|
+
export function remoteStorage(prefix, { endpoint, bucket, region = "us-east-1" }) {
|
|
64
|
+
return `{ endpoint="${endpoint}", bucket_name="${bucket}", bucket_region="${region}", prefix_in_bucket="${prefix}" }`;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* SeaweedFS's S3 identities: Neon, the lake and, when it is on, workspace storage, each
|
|
68
|
+
* on their own bucket of `buckets`; the lake's reader, which may only read the lake's;
|
|
69
|
+
* and an admin, which makes the buckets and keeps the backups.
|
|
70
|
+
*/
|
|
71
|
+
export function s3Identities(secrets, buckets) {
|
|
72
|
+
const on = (name) => [`Read:${name}`, `List:${name}`, `Tagging:${name}`, `Write:${name}`];
|
|
73
|
+
return {
|
|
74
|
+
identities: [
|
|
75
|
+
{ name: "neon", credentials: [secrets.s3.neon], actions: on(buckets.neon) },
|
|
76
|
+
{ name: "admin", credentials: [secrets.s3.admin], actions: ["Admin", "Read", "List", "Tagging", "Write"] },
|
|
77
|
+
// Branch environments' lakes are given it too: a branch's lake reads main's files where they are (DuckLake keeps
|
|
78
|
+
// every file's path relative to one data path) and writes beside them, and SeaweedFS's Write, which writing
|
|
79
|
+
// needs, deletes as well, so it cannot be narrowed to a branch's own files (cli/src/branches.ts).
|
|
80
|
+
{ name: "lake", credentials: [secrets.s3.lake], actions: on(buckets.lake) },
|
|
81
|
+
{ name: "lakeReader", credentials: [secrets.s3.lakeReader], actions: [`Read:${buckets.lake}`, `List:${buckets.lake}`] },
|
|
82
|
+
...(buckets.workspaces === null ? [] : [{ name: "workspaces", credentials: [secrets.s3.workspaces], actions: on(buckets.workspaces) }]),
|
|
83
|
+
],
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The pageserver's configuration: where the broker and the storage controller are, its
|
|
88
|
+
* remote storage, and the token it calls the controller with. Its key is read from
|
|
89
|
+
* `publicKeyPath`.
|
|
90
|
+
*/
|
|
91
|
+
export function pageserverToml({ brokerUrl, controllerUrl, token, publicKeyPath, storage }) {
|
|
92
|
+
return [
|
|
93
|
+
`broker_endpoint=${toml(brokerUrl)}`,
|
|
94
|
+
`pg_distrib_dir=${toml("/usr/local/")}`,
|
|
95
|
+
`listen_pg_addr=${toml("0.0.0.0:6400")}`,
|
|
96
|
+
`listen_http_addr=${toml("0.0.0.0:9898")}`,
|
|
97
|
+
`availability_zone=${toml("az-pageserver")}`,
|
|
98
|
+
`control_plane_api=${toml(`${controllerUrl}/upcall/v1/`)}`,
|
|
99
|
+
`control_plane_api_token=${toml(token)}`,
|
|
100
|
+
`http_auth_type='NeonJWT'`,
|
|
101
|
+
`pg_auth_type='NeonJWT'`,
|
|
102
|
+
`auth_validation_public_key_path=${toml(publicKeyPath)}`,
|
|
103
|
+
`remote_storage=${storage}`,
|
|
104
|
+
"",
|
|
105
|
+
].join("\n");
|
|
106
|
+
}
|
|
107
|
+
/** How the storage controller and computes reach the pageserver at `host`; it registers itself with these. */
|
|
108
|
+
export function pageserverMetadata(host) {
|
|
109
|
+
return JSON.stringify({ host, port: 6400, http_host: host, http_port: 9898, availability_zone_id: "az-pageserver" }) + "\n";
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=secrets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../../../neon/control/secrets.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a branch environment is called, and what it is known by, as alasio and its command
|
|
3
|
+
* line both say it: its namespaces, the port its parent forks its sessions on, and its
|
|
4
|
+
* token to ask with (./fork.ts). Free of anything but Node's own, so the command line
|
|
5
|
+
* takes it without the server.
|
|
6
|
+
*/
|
|
7
|
+
import { createHmac } from "node:crypto";
|
|
8
|
+
/** The port an alasio forks its sessions for its branch environments on. */
|
|
9
|
+
export const BRANCH_FORK_PORT = 4319;
|
|
10
|
+
/** The namespace of the branch environment `branch`: its alasio, its compute and its lake, and their Secrets. */
|
|
11
|
+
export const branchNamespace = (branch) => `alasio-branch-${branch}`;
|
|
12
|
+
/** The namespace of the branch environment `branch`'s sessions. */
|
|
13
|
+
export const branchSessionsNamespace = (branch) => `${branchNamespace(branch)}-sessions`;
|
|
14
|
+
/** The token the branch `branch` forks its parent's sessions with: `key`'s HMAC of its name. */
|
|
15
|
+
export function forkToken(key, branch) {
|
|
16
|
+
return createHmac("sha256", key).update(`alasio-branch-fork:${branch}`).digest("base64url");
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=names.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"names.js","sourceRoot":"","sources":["../../../../src/branch/names.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,4EAA4E;AAC5E,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAErC,iHAAiH;AACjH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,MAAc,EAAU,EAAE,CAAC,iBAAiB,MAAM,EAAE,CAAC;AAErF,mEAAmE;AACnE,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,MAAc,EAAU,EAAE,CAAC,GAAG,eAAe,CAAC,MAAM,CAAC,WAAW,CAAC;AAEzG,gGAAgG;AAChG,MAAM,UAAU,SAAS,CAAC,GAAW,EAAE,MAAc;IACnD,OAAO,UAAU,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC,sBAAsB,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;AAC9F,CAAC"}
|
package/dist/src/kube/config.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* {
|
|
7
7
|
* "sessions": { "namespace", "port", "workspaceDir", "egressGate", "fullModeNameservers",
|
|
8
|
-
* "mountPodNamespace", "podTemplate", "volumeClaimTemplates" },
|
|
8
|
+
* "mountPodNamespace", "podTemplate", "volumeClaimTemplates", "clone" },
|
|
9
9
|
* "host": { "namespace", "port", "stateRoot", "podTemplate" }
|
|
10
10
|
* }
|
|
11
11
|
*
|
|
@@ -38,6 +38,17 @@ const Path = said(Schema.String, "must be a path");
|
|
|
38
38
|
*/
|
|
39
39
|
const PodTemplate = said(Schema.declare((value) => Predicate.isObject(value)), "must be an object").pipe(Schema.withDecodingDefaultKey(Effect.succeed({})));
|
|
40
40
|
const VolumeClaimTemplates = said(Schema.Array(Schema.declare((value) => Predicate.isObject(value))), "must be a list of volume claim templates");
|
|
41
|
+
/**
|
|
42
|
+
* How a session's volume is cloned (cloneJob in cli/src/manifests/workspace-storage.ts):
|
|
43
|
+
* the name of the volume's claim template, and the Job that clones one claim of it into
|
|
44
|
+
* another, given the claims' namespaces and names as `SOURCE_NAMESPACE`, `SOURCE_CLAIM`,
|
|
45
|
+
* `DESTINATION_NAMESPACE` and `DESTINATION_CLAIM`. The Job is a Kubernetes object the
|
|
46
|
+
* installation gives, which the API server checks as each is made.
|
|
47
|
+
*/
|
|
48
|
+
const Clone = said(Schema.Struct({
|
|
49
|
+
claimTemplate: said(Schema.NonEmptyString, "must be a claim template's name"),
|
|
50
|
+
job: said(Schema.declare((value) => Predicate.isObject(value)), "must be an object"),
|
|
51
|
+
}), "must be an object");
|
|
41
52
|
/** Whether `podTemplate`, as the installation gives it, runs a container named `bayma`. */
|
|
42
53
|
function runsBayma(podTemplate) {
|
|
43
54
|
const containers = Predicate.hasProperty(podTemplate, "spec") && Predicate.hasProperty(podTemplate.spec, "containers")
|
|
@@ -64,6 +75,8 @@ const SessionsProfile = said(Schema.Struct({
|
|
|
64
75
|
*/
|
|
65
76
|
mountPodNamespace: Schema.optional(Schema.NullOr(Namespace)),
|
|
66
77
|
volumeClaimTemplates: Schema.optional(VolumeClaimTemplates),
|
|
78
|
+
/** How a session's volume is cloned, where its storage can; none where it cannot, and sessions are not forked. */
|
|
79
|
+
clone: Schema.optional(Schema.NullOr(Clone)),
|
|
67
80
|
}), "must be an object").check(Schema.makeFilter((profile) => (runsBayma(profile.podTemplate) ? undefined : baymaIssue)));
|
|
68
81
|
/** The template of folder workspaces' bayma (hostProfile in cli/src/manifests/alasio.ts). */
|
|
69
82
|
const HostProfile = said(Schema.Struct({
|