@fnndsc/stratus 0.3.0 → 0.4.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/chris/values-openshift.yaml +59 -0
- package/dist/backend.d.ts +6 -3
- package/dist/backend.js +12 -5
- package/dist/backend.js.map +1 -1
- package/dist/cloud/builder.d.ts +3 -1
- package/dist/cloud/builder.js +1 -1
- package/dist/cloud/builder.js.map +1 -1
- package/dist/cloud/fake/builder.d.ts +4 -1
- package/dist/cloud/fake/builder.js +14 -9
- package/dist/cloud/fake/builder.js.map +1 -1
- package/dist/cloud/fake/driver.d.ts +1 -1
- package/dist/cloud/fake/driver.js +9 -9
- package/dist/cloud/fake/driver.js.map +1 -1
- package/dist/cloud/oci/api.js +15 -15
- package/dist/cloud/oci/api.js.map +1 -1
- package/dist/cloud/oci/buildApi.d.ts +2 -0
- package/dist/cloud/oci/buildApi.js +10 -9
- package/dist/cloud/oci/buildApi.js.map +1 -1
- package/dist/cloud/oci/builder.d.ts +14 -1
- package/dist/cloud/oci/builder.js +46 -14
- package/dist/cloud/oci/builder.js.map +1 -1
- package/dist/cloud/oci/driver.d.ts +1 -1
- package/dist/cloud/oci/driver.js +21 -21
- package/dist/cloud/oci/driver.js.map +1 -1
- package/dist/cloud/oci/errors.d.ts +13 -0
- package/dist/cloud/oci/errors.js +16 -0
- package/dist/cloud/oci/errors.js.map +1 -0
- package/dist/cloud/oci/resourceApi.js +22 -22
- package/dist/cloud/oci/resourceApi.js.map +1 -1
- package/dist/cloud/oci/resourceMap.d.ts +3 -3
- package/dist/cloud/oci/resourceMap.js +3 -3
- package/dist/cloud/oci/resourceMap.js.map +1 -1
- package/dist/cloud/oci/rmApi.js +7 -7
- package/dist/cloud/oci/rmApi.js.map +1 -1
- package/dist/cloud/oci/stackVars.d.ts +1 -1
- package/dist/cloud/oci/stackVars.js +2 -2
- package/dist/cloud/oci/stackVars.js.map +1 -1
- package/dist/cluster/chris.d.ts +156 -0
- package/dist/cluster/chris.js +493 -0
- package/dist/cluster/chris.js.map +1 -0
- package/dist/cluster/fakeKube.d.ts +6 -0
- package/dist/cluster/fakeKube.js +21 -0
- package/dist/cluster/fakeKube.js.map +1 -1
- package/dist/cluster/kubeHttp.d.ts +7 -0
- package/dist/cluster/kubeHttp.js +26 -1
- package/dist/cluster/kubeHttp.js.map +1 -1
- package/dist/commands/build.d.ts +18 -0
- package/dist/commands/build.js +63 -17
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/capacity.d.ts +1 -1
- package/dist/commands/capacity.js +2 -2
- package/dist/commands/capacity.js.map +1 -1
- package/dist/commands/size.js +10 -6
- package/dist/commands/size.js.map +1 -1
- package/dist/commands/verify.js +4 -14
- package/dist/commands/verify.js.map +1 -1
- package/dist/domain/adopt.d.ts +11 -1
- package/dist/domain/adopt.js +26 -2
- package/dist/domain/adopt.js.map +1 -1
- package/dist/domain/readout.d.ts +8 -13
- package/dist/domain/readout.js +26 -24
- package/dist/domain/readout.js.map +1 -1
- package/dist/domain/sizing.d.ts +1 -1
- package/dist/domain/sizing.js +3 -3
- package/dist/domain/sizing.js.map +1 -1
- package/dist/domain/spec.d.ts +1 -1
- package/dist/domain/spec.js +1 -1
- package/dist/domain/spec.js.map +1 -1
- package/dist/domain/state.d.ts +2 -2
- package/dist/domain/state.js +11 -11
- package/dist/domain/state.js.map +1 -1
- package/dist/domain/steps.d.ts +18 -20
- package/dist/domain/steps.js +111 -10
- package/dist/domain/steps.js.map +1 -1
- package/dist/domain/verify.d.ts +39 -0
- package/dist/domain/verify.js +64 -17
- package/dist/domain/verify.js.map +1 -1
- package/dist/domain/view.d.ts +1 -1
- package/dist/domain/view.js +3 -3
- package/dist/domain/view.js.map +1 -1
- package/dist/domains/harborOci.d.ts +1 -1
- package/dist/domains/harborOci.js +1 -1
- package/dist/domains/harborOci.js.map +1 -1
- package/dist/index.d.ts +7 -7
- package/dist/index.js +7 -7
- package/dist/index.js.map +1 -1
- package/dist/names.d.ts +1 -1
- package/dist/names.js +2 -2
- package/dist/names.js.map +1 -1
- package/dist/prompt.d.ts +1 -1
- package/dist/prompt.js +2 -2
- package/dist/prompt.js.map +1 -1
- package/dist/stratus.js +3 -2
- package/dist/stratus.js.map +1 -1
- package/dist/vfs/domains.js +2 -2
- package/dist/vfs/domains.js.map +1 -1
- package/dist/vfs/resources.d.ts +4 -4
- package/dist/vfs/resources.js +14 -14
- package/dist/vfs/resources.js.map +1 -1
- package/dist/vfs/tenancy.d.ts +3 -3
- package/dist/vfs/tenancy.js +10 -10
- package/dist/vfs/tenancy.js.map +1 -1
- package/manifests/tenant.mise +8 -10
- package/package.json +7 -6
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file ChRIS on a domain's cluster: the build's `chris-deploy`, `chris-seed`
|
|
3
|
+
* and `smoke` steps (docs/stratus.adoc, D14), as `harbor-oci` proved them
|
|
4
|
+
* on harbor, over the Kubernetes API where it used `oc`.
|
|
5
|
+
*
|
|
6
|
+
* Two things run on the host, as they did for `harbor-oci`: `helm`, which
|
|
7
|
+
* renders and installs the fnndsc/chris chart, and `uv`, which installs
|
|
8
|
+
* miniChRIS-OKD's `chris_smoke` journey into a virtualenv beside the domain
|
|
9
|
+
* and runs it unchanged. Both read the domain's kubeconfig or passwords from
|
|
10
|
+
* the host; nothing secret leaves it.
|
|
11
|
+
*
|
|
12
|
+
* Every pod that mounts the shared storebase volume, and every plugin job,
|
|
13
|
+
* is pinned to one node: OCI block volumes are ReadWriteOnce and bound to one
|
|
14
|
+
* availability domain, and each node sits in its own. The pin goes to the
|
|
15
|
+
* node with the most unrequested CPU on the first deploy and never moves.
|
|
16
|
+
*
|
|
17
|
+
* @module
|
|
18
|
+
*/
|
|
19
|
+
import { spawn } from 'node:child_process';
|
|
20
|
+
import { randomBytes } from 'node:crypto';
|
|
21
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
22
|
+
import { join } from 'node:path';
|
|
23
|
+
import { fileURLToPath } from 'node:url';
|
|
24
|
+
import { KubeError } from './kube.js';
|
|
25
|
+
import { cpuFree_read } from '../domain/verify.js';
|
|
26
|
+
/** The release every domain gets. */
|
|
27
|
+
export const CHRIS_RELEASE = {
|
|
28
|
+
namespace: 'chris',
|
|
29
|
+
release: 'chris',
|
|
30
|
+
chartVersion: '1.0.8',
|
|
31
|
+
chrisomaticImage: 'ghcr.io/fnndsc/chrisomatic:1.0.0',
|
|
32
|
+
testUser: 'smoke',
|
|
33
|
+
smokeSource: 'git+https://github.com/FNNDSC/miniChRIS-OKD@bc27e67faf76c859f0eb3365a52d58aa02834376#subdirectory=smoke',
|
|
34
|
+
};
|
|
35
|
+
/** The chart's repository. */
|
|
36
|
+
const CHART_REPO = { name: 'fnndsc', url: 'https://fnndsc.github.io/charts' };
|
|
37
|
+
/** The chart's OpenShift values, shipped beside `dist/`. */
|
|
38
|
+
export const CHRIS_VALUES = fileURLToPath(new URL('../../chris/values-openshift.yaml', import.meta.url));
|
|
39
|
+
/** The label a node's name is selected by. */
|
|
40
|
+
const HOSTNAME_LABEL = 'kubernetes.io/hostname';
|
|
41
|
+
/** The cluster-wide mirror that serves the chart's removed Bitnami tags from `bitnamilegacy`. */
|
|
42
|
+
const BITNAMI_MIRROR = { name: 'bitnami-legacy', path: '/apis/config.openshift.io/v1/imagetagmirrorsets' };
|
|
43
|
+
/** The superuser the chart makes, and the compute resource it registers. */
|
|
44
|
+
const CHRIS_SUPERUSER = 'chris';
|
|
45
|
+
const PFCON_NAME = 'innetwork';
|
|
46
|
+
/** The workloads a deploy waits on, heart (the migrations) first. */
|
|
47
|
+
const DEPLOYMENTS = ['heart', 'server', 'worker-mains', 'worker-periodic', 'pfcon'];
|
|
48
|
+
const STATEFULSETS = ['postgresql', 'rabbitmq', 'nats'];
|
|
49
|
+
/** The test user's password, under the domain's `auth/`. */
|
|
50
|
+
export const TEST_PASSWORD_FILE = 'chris-test-password';
|
|
51
|
+
/** The router's CA, under the domain's `auth/`, for the smoke journey's HTTPS. */
|
|
52
|
+
export const ROUTER_CA_FILE = 'router-ca.crt';
|
|
53
|
+
/** `harbor-oci`'s waits. */
|
|
54
|
+
export const CHRIS_WAITS = { pollMs: 5_000, mirrorReactMs: 120_000, mirrorWaitMs: 20 * 60_000, rolloutWaitMs: 45 * 60_000, seedWaitMs: 15 * 60_000 };
|
|
55
|
+
/**
|
|
56
|
+
* Runs a command on the host: no shell, its output streamed by line.
|
|
57
|
+
*
|
|
58
|
+
* @param command - The program.
|
|
59
|
+
* @param args - Its arguments.
|
|
60
|
+
* @param options - Environment, folder, a line reader and a stop.
|
|
61
|
+
* @returns Its exit code and output.
|
|
62
|
+
*/
|
|
63
|
+
export const hostRun_spawn = (command, args, options = {}) => new Promise((resolve, reject) => {
|
|
64
|
+
const child = spawn(command, [...args], { cwd: options.cwd, env: { ...process.env, ...options.env }, stdio: ['ignore', 'pipe', 'pipe'], ...(options.signal !== undefined ? { signal: options.signal } : {}) });
|
|
65
|
+
const out = [];
|
|
66
|
+
const err = [];
|
|
67
|
+
const lines = (into) => {
|
|
68
|
+
let rest = '';
|
|
69
|
+
return (chunk) => {
|
|
70
|
+
const text = rest + chunk.toString('utf-8');
|
|
71
|
+
into.push(chunk.toString('utf-8'));
|
|
72
|
+
const parts = text.split('\n');
|
|
73
|
+
rest = parts.pop() ?? '';
|
|
74
|
+
for (const part of parts)
|
|
75
|
+
if (part.trim() !== '')
|
|
76
|
+
options.line?.(part);
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
child.stdout.on('data', lines(out));
|
|
80
|
+
child.stderr.on('data', lines(err));
|
|
81
|
+
child.on('error', reject);
|
|
82
|
+
child.on('close', (code) => resolve({ code: code ?? 1, stdout: out.join(''), stderr: err.join('') }));
|
|
83
|
+
});
|
|
84
|
+
/**
|
|
85
|
+
* Waits, unless stopped.
|
|
86
|
+
*
|
|
87
|
+
* @param ms - How long.
|
|
88
|
+
* @param signal - Stops it.
|
|
89
|
+
*/
|
|
90
|
+
function wait(ms, signal) {
|
|
91
|
+
if (ms <= 0 || signal.aborted)
|
|
92
|
+
return Promise.resolve();
|
|
93
|
+
return new Promise((resolve) => {
|
|
94
|
+
const timer = setTimeout(resolve, ms);
|
|
95
|
+
signal.addEventListener('abort', () => { clearTimeout(timer); resolve(); }, { once: true });
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* A path's object, or null when the cluster has none.
|
|
100
|
+
*
|
|
101
|
+
* @param kube - The cluster.
|
|
102
|
+
* @param path - The path.
|
|
103
|
+
* @returns The object, or null on 404.
|
|
104
|
+
*/
|
|
105
|
+
async function object_find(kube, path) {
|
|
106
|
+
try {
|
|
107
|
+
return await kube.json_get(path);
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
if (error instanceof KubeError && error.status === 404)
|
|
111
|
+
return null;
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Deletes a path's object; one already gone is no error.
|
|
117
|
+
*
|
|
118
|
+
* @param kube - The cluster.
|
|
119
|
+
* @param path - The path.
|
|
120
|
+
*/
|
|
121
|
+
async function object_remove(kube, path) {
|
|
122
|
+
try {
|
|
123
|
+
await kube.delete(path);
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
if (!(error instanceof KubeError && error.status === 404))
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Runs a host command that must succeed.
|
|
132
|
+
*
|
|
133
|
+
* @param call - The step.
|
|
134
|
+
* @param command - The program.
|
|
135
|
+
* @param args - Its arguments.
|
|
136
|
+
* @param options - As for `HostRun`.
|
|
137
|
+
* @returns Its standard output.
|
|
138
|
+
* @throws Error - When it exits other than 0, with its last words.
|
|
139
|
+
*/
|
|
140
|
+
async function host_need(call, command, args, options = {}) {
|
|
141
|
+
const result = await call.host(command, args, { signal: call.signal, ...options });
|
|
142
|
+
if (result.code !== 0)
|
|
143
|
+
throw new Error(`${command} ${args[0] ?? ''} exited ${result.code}: ${(result.stderr || result.stdout).trim().split('\n').slice(-3).join(' · ')}`);
|
|
144
|
+
return result.stdout;
|
|
145
|
+
}
|
|
146
|
+
/** A resource's conditions, by type. */
|
|
147
|
+
function conditions_map(conditions) {
|
|
148
|
+
return Object.fromEntries((conditions ?? []).map((condition) => [condition.type, condition.status]));
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Makes the Bitnami tag mirror unless it is there, and waits for the nodes
|
|
152
|
+
* to take it (the machine-config pool updates; nodes are not rebooted).
|
|
153
|
+
*
|
|
154
|
+
* @param call - The step.
|
|
155
|
+
*/
|
|
156
|
+
async function mirror_ensure(call) {
|
|
157
|
+
if (await object_find(call.kube, `${BITNAMI_MIRROR.path}/${BITNAMI_MIRROR.name}`) !== null) {
|
|
158
|
+
call.log('bitnami mirror already there');
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
await call.kube.json_post(BITNAMI_MIRROR.path, {
|
|
162
|
+
apiVersion: 'config.openshift.io/v1', kind: 'ImageTagMirrorSet', metadata: { name: BITNAMI_MIRROR.name },
|
|
163
|
+
spec: { imageTagMirrors: [{ source: 'docker.io/bitnami', mirrors: ['docker.io/bitnamilegacy'] }] },
|
|
164
|
+
});
|
|
165
|
+
call.log('bitnami mirror made: docker.io/bitnami pulls fall back to docker.io/bitnamilegacy');
|
|
166
|
+
const pool = '/apis/machineconfiguration.openshift.io/v1/machineconfigpools/master';
|
|
167
|
+
const started = Date.now();
|
|
168
|
+
let updating = false;
|
|
169
|
+
while (!call.signal.aborted && Date.now() - started < call.waits.mirrorWaitMs) {
|
|
170
|
+
const state = conditions_map((await call.kube.json_get(pool)).status?.conditions)['Updating'];
|
|
171
|
+
if (state === 'True' && !updating) {
|
|
172
|
+
updating = true;
|
|
173
|
+
call.log('the nodes are taking the mirror, one at a time');
|
|
174
|
+
}
|
|
175
|
+
if (updating && state === 'False') {
|
|
176
|
+
call.log(`the nodes took the mirror in ${Math.round((Date.now() - started) / 1000)}s`);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (!updating && Date.now() - started >= call.waits.mirrorReactMs)
|
|
180
|
+
return;
|
|
181
|
+
await wait(call.waits.pollMs * 3, call.signal);
|
|
182
|
+
}
|
|
183
|
+
if (updating && !call.signal.aborted)
|
|
184
|
+
throw new Error(`the machine-config pool was still updating after ${Math.round(call.waits.mirrorWaitMs / 60_000)} minutes`);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Makes the ChRIS project unless it is there: a project, not a namespace
|
|
188
|
+
* Helm makes, because OpenShift's project annotations are not Helm's.
|
|
189
|
+
*
|
|
190
|
+
* @param call - The step.
|
|
191
|
+
*/
|
|
192
|
+
async function project_ensure(call) {
|
|
193
|
+
if (await object_find(call.kube, `/api/v1/namespaces/${call.release.namespace}`) !== null)
|
|
194
|
+
return;
|
|
195
|
+
await call.kube.json_post('/apis/project.openshift.io/v1/projectrequests', {
|
|
196
|
+
apiVersion: 'project.openshift.io/v1', kind: 'ProjectRequest', metadata: { name: call.release.namespace }, description: 'ChRIS deployed by stratus',
|
|
197
|
+
});
|
|
198
|
+
call.log(`project ${call.release.namespace} made`);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* The node ChRIS is pinned to: the pin a deploy already made, else the node
|
|
202
|
+
* with the most unrequested CPU.
|
|
203
|
+
*
|
|
204
|
+
* @param call - The step.
|
|
205
|
+
* @returns The node's hostname label.
|
|
206
|
+
*/
|
|
207
|
+
async function pinNode_choose(call) {
|
|
208
|
+
const pfcon = await object_find(call.kube, `/apis/apps/v1/namespaces/${call.release.namespace}/deployments/${call.release.release}-pfcon`);
|
|
209
|
+
const kept = pfcon?.spec?.template?.spec?.nodeSelector?.[HOSTNAME_LABEL];
|
|
210
|
+
if (kept !== undefined) {
|
|
211
|
+
call.log(`pin kept: ${kept} (its volume cannot move)`);
|
|
212
|
+
return kept;
|
|
213
|
+
}
|
|
214
|
+
const free = await cpuFree_read(call.kube);
|
|
215
|
+
const best = [...free].sort((a, b) => b.cores - a.cores)[0];
|
|
216
|
+
if (best === undefined)
|
|
217
|
+
throw new Error('the cluster has no nodes to pin ChRIS to');
|
|
218
|
+
call.log(`pin chosen by unrequested CPU: ${free.map((node) => `${node.hostname} ${node.cores.toFixed(1)}`).join(', ')}`);
|
|
219
|
+
return best.hostname;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* The `helm upgrade --install` arguments: the pinned chart, the OpenShift
|
|
223
|
+
* values, the domain's host, node and the spec's sizes.
|
|
224
|
+
*
|
|
225
|
+
* @param call - The step.
|
|
226
|
+
* @param node - The pinned node.
|
|
227
|
+
* @returns The arguments after `helm`.
|
|
228
|
+
*/
|
|
229
|
+
export function helmInstall_args(call, node) {
|
|
230
|
+
const sizes = [
|
|
231
|
+
['pfcon.storage.size', call.spec.chris.dataGib],
|
|
232
|
+
['postgresql.primary.persistence.size', call.spec.chris.databaseGib],
|
|
233
|
+
['rabbitmq.persistence.size', call.spec.chris.queueGib],
|
|
234
|
+
];
|
|
235
|
+
return [
|
|
236
|
+
`--kubeconfig=${call.kubeconfigPath}`, 'upgrade', '--install', call.release.release, `${CHART_REPO.name}/chris`,
|
|
237
|
+
'--version', call.release.chartVersion, '--namespace', call.release.namespace, '--values', CHRIS_VALUES,
|
|
238
|
+
'--set-string', `route.host=${call.cubeHost}`,
|
|
239
|
+
'--set-string', `pfcon.nodeSelector.kubernetes\\.io/hostname=${node}`,
|
|
240
|
+
'--set-string', `pfcon.pman.extraEnv.NODE_SELECTOR=${HOSTNAME_LABEL}=${node}`,
|
|
241
|
+
...sizes.flatMap(([key, gib]) => ['--set-string', `${key}=${gib}Gi`]),
|
|
242
|
+
];
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Whether a workload has rolled out: its generation seen, every replica
|
|
246
|
+
* updated and ready.
|
|
247
|
+
*
|
|
248
|
+
* @param workload - The deployment or stateful set.
|
|
249
|
+
* @returns True when done.
|
|
250
|
+
*/
|
|
251
|
+
export function rollout_done(workload) {
|
|
252
|
+
if (workload === null)
|
|
253
|
+
return false;
|
|
254
|
+
const want = workload.spec?.replicas ?? 1;
|
|
255
|
+
const status = workload.status ?? {};
|
|
256
|
+
return (status.observedGeneration ?? 0) >= (workload.metadata?.generation ?? 1)
|
|
257
|
+
&& (status.updatedReplicas ?? 0) >= want && (status.readyReplicas ?? 0) >= want;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Waits for heart, then every other workload, to roll out.
|
|
261
|
+
*
|
|
262
|
+
* @param call - The step.
|
|
263
|
+
*/
|
|
264
|
+
async function rollouts_wait(call) {
|
|
265
|
+
const base = `/apis/apps/v1/namespaces/${call.release.namespace}`;
|
|
266
|
+
const pending = [
|
|
267
|
+
...DEPLOYMENTS.map((name) => `deployments/${call.release.release}-${name}`),
|
|
268
|
+
...STATEFULSETS.map((name) => `statefulsets/${call.release.release}-${name}`),
|
|
269
|
+
];
|
|
270
|
+
const deadline = Date.now() + call.waits.rolloutWaitMs;
|
|
271
|
+
while (pending.length > 0 && !call.signal.aborted) {
|
|
272
|
+
const next = pending[0];
|
|
273
|
+
if (rollout_done(await object_find(call.kube, `${base}/${next}`))) {
|
|
274
|
+
call.log(`${next.split('/')[1] ?? next} rolled out`);
|
|
275
|
+
pending.shift();
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
if (Date.now() >= deadline)
|
|
279
|
+
throw new Error(`${pending.map((entry) => entry.split('/')[1] ?? entry).join(', ')} not rolled out after ${Math.round(call.waits.rolloutWaitMs / 60_000)} minutes`);
|
|
280
|
+
await wait(call.waits.pollMs * 2, call.signal);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* A secret's key, decoded.
|
|
285
|
+
*
|
|
286
|
+
* @param kube - The cluster.
|
|
287
|
+
* @param namespace - Its namespace.
|
|
288
|
+
* @param name - The secret.
|
|
289
|
+
* @param key - The key.
|
|
290
|
+
* @returns The value.
|
|
291
|
+
* @throws Error - When it has none.
|
|
292
|
+
*/
|
|
293
|
+
async function secretValue_read(kube, namespace, name, key) {
|
|
294
|
+
const secret = await kube.json_get(`/api/v1/namespaces/${namespace}/secrets/${name}`);
|
|
295
|
+
const value = secret.data?.[key];
|
|
296
|
+
if (value === undefined)
|
|
297
|
+
throw new Error(`secret ${namespace}/${name} has no ${key}`);
|
|
298
|
+
return Buffer.from(value, 'base64').toString('utf-8');
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* The chrisomatic config the seed job runs: the test user, the compute
|
|
302
|
+
* resource the chart registers (re-declared with its own credentials, so a
|
|
303
|
+
* re-run converges), and the smoke journey's plugins. YAML strings are
|
|
304
|
+
* written as JSON strings, which YAML reads the same.
|
|
305
|
+
*
|
|
306
|
+
* @param values - The addresses and credentials.
|
|
307
|
+
* @returns The config's text.
|
|
308
|
+
*/
|
|
309
|
+
export function chrisomaticConfig_render(values) {
|
|
310
|
+
const q = (text) => JSON.stringify(text);
|
|
311
|
+
return [
|
|
312
|
+
'version: 1.2',
|
|
313
|
+
'on:',
|
|
314
|
+
` cube_url: ${q(values.cubeUrl)}`,
|
|
315
|
+
' chris_superuser:',
|
|
316
|
+
` username: ${q(CHRIS_SUPERUSER)}`,
|
|
317
|
+
` password: ${q(values.superuserPassword)}`,
|
|
318
|
+
'cube:',
|
|
319
|
+
' users:',
|
|
320
|
+
` - username: ${q(values.testUser)}`,
|
|
321
|
+
` password: ${q(values.testPassword)}`,
|
|
322
|
+
' compute_resource:',
|
|
323
|
+
` - name: ${q(PFCON_NAME)}`,
|
|
324
|
+
` url: ${q(values.pfconUrl)}`,
|
|
325
|
+
` username: ${q(values.pfconUser)}`,
|
|
326
|
+
` password: ${q(values.pfconPassword)}`,
|
|
327
|
+
` description: ${q('Kubernetes cluster compute resource')}`,
|
|
328
|
+
' innetwork: true',
|
|
329
|
+
' plugins:',
|
|
330
|
+
` - name: ${q('pl-dircopy')}`,
|
|
331
|
+
` version: ${q('3.0.0')}`,
|
|
332
|
+
` - name: ${q('pl-simpledsapp')}`,
|
|
333
|
+
` version: ${q('2.1.5')}`,
|
|
334
|
+
'',
|
|
335
|
+
].join('\n');
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* The seed job: chrisomatic once over the config in its secret.
|
|
339
|
+
*
|
|
340
|
+
* @param release - The release.
|
|
341
|
+
* @param timeoutSeconds - Its deadline.
|
|
342
|
+
* @returns The Job.
|
|
343
|
+
*/
|
|
344
|
+
export function seedJob_build(release, timeoutSeconds) {
|
|
345
|
+
const labels = { 'app.kubernetes.io/name': 'chrisomatic', 'app.kubernetes.io/instance': release.release };
|
|
346
|
+
return {
|
|
347
|
+
apiVersion: 'batch/v1', kind: 'Job',
|
|
348
|
+
metadata: { name: `${release.release}-seed`, namespace: release.namespace, labels: { ...labels, 'app.kubernetes.io/part-of': 'chris' } },
|
|
349
|
+
spec: {
|
|
350
|
+
// chrisomatic is idempotent and cheap: fail once, read why, run the step again.
|
|
351
|
+
backoffLimit: 0,
|
|
352
|
+
activeDeadlineSeconds: timeoutSeconds,
|
|
353
|
+
template: {
|
|
354
|
+
metadata: { labels },
|
|
355
|
+
spec: {
|
|
356
|
+
restartPolicy: 'Never',
|
|
357
|
+
containers: [{
|
|
358
|
+
name: 'chrisomatic', image: release.chrisomaticImage, command: ['chrisomatic', '/etc/chrisomatic/chrisomatic.yml'],
|
|
359
|
+
resources: { requests: { cpu: '100m', memory: '256Mi' }, limits: { cpu: '1', memory: '512Mi' } },
|
|
360
|
+
volumeMounts: [{ name: 'config', mountPath: '/etc/chrisomatic', readOnly: true }],
|
|
361
|
+
}],
|
|
362
|
+
volumes: [{ name: 'config', secret: { secretName: `${release.release}-seed-config` } }],
|
|
363
|
+
},
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* The journey's last line in a few words: `chris_smoke` ends with its verdict
|
|
370
|
+
* as JSON (`pass · 7 steps · 17 s`, or the step that failed); any other line
|
|
371
|
+
* is kept as it is.
|
|
372
|
+
*
|
|
373
|
+
* @param line - The last line it printed.
|
|
374
|
+
* @returns The words.
|
|
375
|
+
*/
|
|
376
|
+
export function smokeVerdict_words(line) {
|
|
377
|
+
let verdict;
|
|
378
|
+
try {
|
|
379
|
+
verdict = JSON.parse(line);
|
|
380
|
+
}
|
|
381
|
+
catch {
|
|
382
|
+
return line;
|
|
383
|
+
}
|
|
384
|
+
if (typeof verdict !== 'object' || verdict === null || typeof verdict.verdict !== 'string')
|
|
385
|
+
return line;
|
|
386
|
+
const steps = verdict.steps?.length ?? 0;
|
|
387
|
+
const failed = typeof verdict.failed_step === 'string' ? ` · failed at ${verdict.failed_step}` : '';
|
|
388
|
+
const seconds = typeof verdict.duration_s === 'number' ? ` · ${Math.round(verdict.duration_s)} s` : '';
|
|
389
|
+
return `${verdict.verdict} · ${steps} step${steps === 1 ? '' : 's'}${failed}${seconds}`;
|
|
390
|
+
}
|
|
391
|
+
/** ChRIS on a real cluster. */
|
|
392
|
+
export const chrisWork_cluster = {
|
|
393
|
+
async deploy(call) {
|
|
394
|
+
await mirror_ensure(call);
|
|
395
|
+
await project_ensure(call);
|
|
396
|
+
const node = await pinNode_choose(call);
|
|
397
|
+
await host_need(call, 'helm', ['repo', 'add', CHART_REPO.name, CHART_REPO.url, '--force-update']);
|
|
398
|
+
await host_need(call, 'helm', helmInstall_args(call, node), { line: (text) => call.log(`helm: ${text}`) });
|
|
399
|
+
call.log(`release ${call.release.release}: fnndsc/chris ${call.release.chartVersion}; waiting for the rollouts (the first pulls images and migrates the database)`);
|
|
400
|
+
await rollouts_wait(call);
|
|
401
|
+
return `fnndsc/chris ${call.release.chartVersion} at https://${call.cubeHost}/api/v1/, pinned to ${node}`;
|
|
402
|
+
},
|
|
403
|
+
async seed(call) {
|
|
404
|
+
const { namespace, release } = call.release;
|
|
405
|
+
let testPassword = call.secret_read(TEST_PASSWORD_FILE)?.trim() ?? null;
|
|
406
|
+
if (testPassword === null || testPassword === '') {
|
|
407
|
+
testPassword = randomBytes(18).toString('base64url');
|
|
408
|
+
call.secret_write(TEST_PASSWORD_FILE, `${testPassword}\n`);
|
|
409
|
+
call.log(`the test user's password is made, on the host (auth/${TEST_PASSWORD_FILE})`);
|
|
410
|
+
}
|
|
411
|
+
const config = chrisomaticConfig_render({
|
|
412
|
+
cubeUrl: `http://${release}-server.${namespace}.svc:8000/api/v1/`,
|
|
413
|
+
superuserPassword: await secretValue_read(call.kube, namespace, `${release}-chris-backend`, 'CHRIS_SUPERUSER_PASSWORD'),
|
|
414
|
+
testUser: call.release.testUser,
|
|
415
|
+
testPassword,
|
|
416
|
+
pfconUrl: `http://${release}-pfcon.${namespace}.svc:5005/api/v1/`,
|
|
417
|
+
pfconUser: await secretValue_read(call.kube, namespace, `${release}-pfcon`, 'PFCON_USER'),
|
|
418
|
+
pfconPassword: await secretValue_read(call.kube, namespace, `${release}-pfcon`, 'PFCON_PASSWORD'),
|
|
419
|
+
});
|
|
420
|
+
// The config carries credentials: it goes into a secret, never to disk.
|
|
421
|
+
const secrets = `/api/v1/namespaces/${namespace}/secrets`;
|
|
422
|
+
await object_remove(call.kube, `${secrets}/${release}-seed-config`);
|
|
423
|
+
await call.kube.json_post(secrets, { apiVersion: 'v1', kind: 'Secret', metadata: { name: `${release}-seed-config` }, stringData: { 'chrisomatic.yml': config } });
|
|
424
|
+
const jobs = `/apis/batch/v1/namespaces/${namespace}/jobs`;
|
|
425
|
+
const job = `${jobs}/${release}-seed`;
|
|
426
|
+
await object_remove(call.kube, `${job}?propagationPolicy=Background`);
|
|
427
|
+
const goneBy = Date.now() + 120_000;
|
|
428
|
+
while (await object_find(call.kube, job) !== null) {
|
|
429
|
+
if (Date.now() >= goneBy)
|
|
430
|
+
throw new Error(`the last seed job ${release}-seed is still there after two minutes`);
|
|
431
|
+
await wait(call.waits.pollMs, call.signal);
|
|
432
|
+
}
|
|
433
|
+
await call.kube.json_post(jobs, seedJob_build(call.release, Math.round(call.waits.seedWaitMs / 1000)));
|
|
434
|
+
call.log('seed job started: chrisomatic makes the test user, the compute resource and the smoke plugins');
|
|
435
|
+
const deadline = Date.now() + call.waits.seedWaitMs;
|
|
436
|
+
let status = {};
|
|
437
|
+
while (!call.signal.aborted && Date.now() < deadline) {
|
|
438
|
+
status = (await call.kube.json_get(job)).status ?? {};
|
|
439
|
+
if ((status.succeeded ?? 0) > 0 || (status.failed ?? 0) > 0)
|
|
440
|
+
break;
|
|
441
|
+
await wait(call.waits.pollMs, call.signal);
|
|
442
|
+
}
|
|
443
|
+
const pods = await call.kube.json_get(`/api/v1/namespaces/${namespace}/pods?labelSelector=${encodeURIComponent(`job-name=${release}-seed`)}`);
|
|
444
|
+
const pod = pods.items?.[0]?.metadata.name;
|
|
445
|
+
const logText = pod === undefined ? '' : await call.kube.text_get(`/api/v1/namespaces/${namespace}/pods/${pod}/log?tailLines=60`).catch(() => '');
|
|
446
|
+
if ((status.succeeded ?? 0) === 0) {
|
|
447
|
+
for (const line of logText.trim().split('\n').slice(-10))
|
|
448
|
+
if (line.trim() !== '')
|
|
449
|
+
call.log(`chrisomatic: ${line}`);
|
|
450
|
+
throw new Error(`the seed job ${(status.failed ?? 0) > 0 ? 'failed' : `did not finish in ${Math.round(call.waits.seedWaitMs / 60_000)} minutes`}`);
|
|
451
|
+
}
|
|
452
|
+
const summary = logText.includes('Summary:') ? (logText.split('Summary:').pop() ?? '').replace(/[─\s]+/g, ' ').trim() : '';
|
|
453
|
+
return `test user ${call.release.testUser}, compute resource ${PFCON_NAME}, pl-dircopy and pl-simpledsapp${summary !== '' ? ` · ${summary}` : ''}`;
|
|
454
|
+
},
|
|
455
|
+
async smoke(call) {
|
|
456
|
+
if (call.secret_read(TEST_PASSWORD_FILE) === null)
|
|
457
|
+
throw new Error('no test user yet: chris-seed has not been done');
|
|
458
|
+
const venv = join(call.folder, 'smoke-venv');
|
|
459
|
+
const python = join(venv, 'bin', 'python');
|
|
460
|
+
const stamp = join(venv, '.stratus-source');
|
|
461
|
+
if (!existsSync(python) || !existsSync(stamp) || readFileSync(stamp, 'utf-8') !== call.release.smokeSource) {
|
|
462
|
+
call.log(`installing chris_smoke from ${call.release.smokeSource}`);
|
|
463
|
+
await host_need(call, 'uv', ['venv', '--quiet', '--allow-existing', venv]);
|
|
464
|
+
await host_need(call, 'uv', ['pip', 'install', '--quiet', '--python', python, call.release.smokeSource]);
|
|
465
|
+
mkdirSync(venv, { recursive: true });
|
|
466
|
+
writeFileSync(stamp, call.release.smokeSource);
|
|
467
|
+
}
|
|
468
|
+
// The router's own CA signs the CUBE's certificate.
|
|
469
|
+
const routerCa = await call.kube.json_get('/api/v1/namespaces/openshift-ingress-operator/secrets/router-ca');
|
|
470
|
+
const ca = routerCa.data?.['tls.crt'];
|
|
471
|
+
if (ca === undefined)
|
|
472
|
+
throw new Error('the router has no CA to trust');
|
|
473
|
+
call.secret_write(ROUTER_CA_FILE, Buffer.from(ca, 'base64').toString('utf-8'));
|
|
474
|
+
const result = await call.host(python, ['-m', 'chris_smoke'], {
|
|
475
|
+
cwd: call.folder,
|
|
476
|
+
signal: call.signal,
|
|
477
|
+
line: (text) => call.log(`smoke: ${text}`),
|
|
478
|
+
env: {
|
|
479
|
+
CUBE_URL: `https://${call.cubeHost}/api/v1/`,
|
|
480
|
+
CHRIS_SMOKE_USER: call.release.testUser,
|
|
481
|
+
CHRIS_SMOKE_PASSWORD_FILE: call.secret_path(TEST_PASSWORD_FILE),
|
|
482
|
+
SMOKE_CA_BUNDLE: call.secret_path(ROUTER_CA_FILE),
|
|
483
|
+
},
|
|
484
|
+
});
|
|
485
|
+
const last = smokeVerdict_words(result.stdout.trim().split('\n').pop() ?? '');
|
|
486
|
+
if (result.code === 1)
|
|
487
|
+
throw new Error(`the smoke journey found ChRIS broken: ${last}`);
|
|
488
|
+
if (result.code !== 0)
|
|
489
|
+
throw new Error(`the smoke journey could not start (exit ${result.code}): ${(result.stderr.trim().split('\n').pop() ?? last)}`);
|
|
490
|
+
return `the smoke journey passes as ${call.release.testUser}${last !== '' ? ` · ${last}` : ''}`;
|
|
491
|
+
},
|
|
492
|
+
};
|
|
493
|
+
//# sourceMappingURL=chris.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chris.js","sourceRoot":"","sources":["../../src/cluster/chris.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type { KubeApi } from './kube.js';
|
|
9
9
|
import type { VerifyReach } from '../commands/verify.js';
|
|
10
|
+
import { type ChrisWork } from './chris.js';
|
|
10
11
|
/** One fake node. */
|
|
11
12
|
export interface FakeNode {
|
|
12
13
|
readonly name: string;
|
|
@@ -41,3 +42,8 @@ export declare class FakeKube implements KubeApi {
|
|
|
41
42
|
* @returns The reach.
|
|
42
43
|
*/
|
|
43
44
|
export declare function fakeVerifyReach(nodes_of: (kubeconfig: string) => FakeNode[]): VerifyReach;
|
|
45
|
+
/**
|
|
46
|
+
* ChRIS in the fake world: each step says what the real one would have done
|
|
47
|
+
* and keeps what the next reads, so a fake build runs through `smoke`.
|
|
48
|
+
*/
|
|
49
|
+
export declare const fakeChrisWork: ChrisWork;
|
package/dist/cluster/fakeKube.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { KubeError } from './kube.js';
|
|
2
|
+
import { TEST_PASSWORD_FILE } from './chris.js';
|
|
2
3
|
/** A fake cluster, keeping what the volume probe makes. */
|
|
3
4
|
export class FakeKube {
|
|
4
5
|
options;
|
|
@@ -83,4 +84,24 @@ export function fakeVerifyReach(nodes_of) {
|
|
|
83
84
|
pollMs: 0,
|
|
84
85
|
};
|
|
85
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* ChRIS in the fake world: each step says what the real one would have done
|
|
89
|
+
* and keeps what the next reads, so a fake build runs through `smoke`.
|
|
90
|
+
*/
|
|
91
|
+
export const fakeChrisWork = {
|
|
92
|
+
async deploy(call) {
|
|
93
|
+
call.log(`release ${call.release.release}: fnndsc/chris ${call.release.chartVersion} (fake)`);
|
|
94
|
+
return `fnndsc/chris ${call.release.chartVersion} at https://${call.cubeHost}/api/v1/ (fake)`;
|
|
95
|
+
},
|
|
96
|
+
async seed(call) {
|
|
97
|
+
if (call.secret_read(TEST_PASSWORD_FILE) === null)
|
|
98
|
+
call.secret_write(TEST_PASSWORD_FILE, 'fake\n');
|
|
99
|
+
return `test user ${call.release.testUser} (fake)`;
|
|
100
|
+
},
|
|
101
|
+
async smoke(call) {
|
|
102
|
+
if (call.secret_read(TEST_PASSWORD_FILE) === null)
|
|
103
|
+
throw new Error('no test user yet: chris-seed has not been done');
|
|
104
|
+
return `the smoke journey passes as ${call.release.testUser} (fake)`;
|
|
105
|
+
},
|
|
106
|
+
};
|
|
86
107
|
//# sourceMappingURL=fakeKube.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fakeKube.js","sourceRoot":"","sources":["../../src/cluster/fakeKube.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"fakeKube.js","sourceRoot":"","sources":["../../src/cluster/fakeKube.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EAAE,kBAAkB,EAAkC,MAAM,YAAY,CAAC;AAkBhF,2DAA2D;AAC3D,MAAM,OAAO,QAAQ;IAKU;IAJpB,IAAI,GAAa,EAAE,CAAC;IACpB,OAAO,GAAa,EAAE,CAAC;IACf,UAAU,GAAgB,IAAI,GAAG,EAAE,CAAC;IAErD,YAA6B,OAA2B;QAA3B,YAAO,GAAP,OAAO,CAAoB;IAAG,CAAC;IAE5D,KAAK,CAAC,QAAQ,CAAC,IAAY;QACzB,IAAI,IAAI,KAAK,sDAAsD,EAAE,CAAC;YACpE,OAAO,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC;QACnJ,CAAC;QACD,IAAI,IAAI,KAAK,eAAe,EAAE,CAAC;YAC7B,OAAO;gBACL,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAc,EAAE,EAAE,CAAC,CAAC;oBACjD,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,kCAAkC,EAAE,IAAI,CAAC,KAAK,EAAE,6BAA6B,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE;oBACnI,IAAI,EAAE,EAAE,UAAU,EAAE,wBAAwB,IAAI,CAAC,IAAI,EAAE,EAAE;oBACzD,MAAM,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,WAAW,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE;iBAC3F,CAAC,CAAC;aACJ,CAAC;QACJ,CAAC;QACD,IAAI,IAAI,KAAK,+CAA+C,EAAE,CAAC;YAC7D,MAAM,KAAK,GAAa,CAAC,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC;YACnG,OAAO;gBACL,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC;oBAClC,QAAQ,EAAE,EAAE,IAAI,EAAE;oBAClB,MAAM,EAAE,EAAE,UAAU,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;4BAChE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;4BACzH,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE;iBAC/H,CAAC,CAAC;aACJ,CAAC;QACJ,CAAC;QACD,IAAI,IAAI,KAAK,wCAAwC,EAAE,CAAC;YACtD,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,EAAE,6CAA6C,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,EAAE,CAAC,EAAE,CAAC;QAC3K,CAAC;QACD,IAAI,IAAI,KAAK,cAAc,EAAE,CAAC;YAC5B,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAc,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,QAAQ,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC;QAC3L,CAAC;QACD,IAAI,IAAI,KAAK,iEAAiE,EAAE,CAAC;YAC/E,OAAO,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,gEAAgE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACnI,CAAC;QACD,MAAM,GAAG,GAA4B,+CAA+C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChG,IAAI,GAAG,KAAK,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAW,CAAC;YAAE,OAAO,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,aAAa,KAAK,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;QACvJ,MAAM,IAAI,SAAS,CAAC,GAAG,EAAE,OAAO,IAAI,sBAAsB,CAAC,CAAC;IAC9D,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,IAAY;QACzB,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QACpC,IAAI,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,WAAW,CAAC;QACzD,MAAM,IAAI,SAAS,CAAC,GAAG,EAAE,OAAO,IAAI,sBAAsB,CAAC,CAAC;IAC9D,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,IAAY,EAAE,IAAa;QACzC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrB,IAAI,IAAI,KAAK,oBAAoB;YAAE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAE,IAAuC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC/G,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,IAAY;QACvB,MAAM,EAAE,GAA4B,kCAAkC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClF,IAAI,EAAE,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAW,CAAC;YAAE,MAAM,IAAI,SAAS,CAAC,GAAG,EAAE,UAAU,IAAI,sBAAsB,CAAC,CAAC;QACzH,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAW,CAAC,CAAC;QACxC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,QAA4C;IAC1E,OAAO;QACL,SAAS,EAAE,CAAC,UAAkB,EAAW,EAAE,CAAC,IAAI,QAAQ,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QACzF,OAAO,EAAE,KAAK,EAAE,IAAY,EAAqB,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC;QACrJ,YAAY,EAAE,KAAK,IAAqB,EAAE,CAAC,GAAG;QAC9C,cAAc,EAAE,IAAI;QACpB,MAAM,EAAE,CAAC;KACV,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAc;IACtC,KAAK,CAAC,MAAM,CAAC,IAAe;QAC1B,IAAI,CAAC,GAAG,CAAC,WAAW,IAAI,CAAC,OAAO,CAAC,OAAO,kBAAkB,IAAI,CAAC,OAAO,CAAC,YAAY,SAAS,CAAC,CAAC;QAC9F,OAAO,gBAAgB,IAAI,CAAC,OAAO,CAAC,YAAY,eAAe,IAAI,CAAC,QAAQ,iBAAiB,CAAC;IAChG,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,IAAe;QACxB,IAAI,IAAI,CAAC,WAAW,CAAC,kBAAkB,CAAC,KAAK,IAAI;YAAE,IAAI,CAAC,YAAY,CAAC,kBAAkB,EAAE,QAAQ,CAAC,CAAC;QACnG,OAAO,aAAa,IAAI,CAAC,OAAO,CAAC,QAAQ,SAAS,CAAC;IACrD,CAAC;IACD,KAAK,CAAC,KAAK,CAAC,IAAe;QACzB,IAAI,IAAI,CAAC,WAAW,CAAC,kBAAkB,CAAC,KAAK,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACrH,OAAO,+BAA+B,IAAI,CAAC,OAAO,CAAC,QAAQ,SAAS,CAAC;IACvE,CAAC;CACF,CAAC"}
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { type KubeAccess, type KubeApi } from './kube.js';
|
|
2
|
+
/**
|
|
3
|
+
* How long a 429 asks to be waited, from its Status (`details.retryAfterSeconds`), within one to ten seconds.
|
|
4
|
+
*
|
|
5
|
+
* @param text - The answer's body.
|
|
6
|
+
* @returns Milliseconds.
|
|
7
|
+
*/
|
|
8
|
+
export declare function busyWait_parse(text: string): number;
|
|
2
9
|
/**
|
|
3
10
|
* The Kubernetes API over HTTPS.
|
|
4
11
|
*
|
package/dist/cluster/kubeHttp.js
CHANGED
|
@@ -9,6 +9,26 @@ import { request } from 'node:https';
|
|
|
9
9
|
import { KubeError } from './kube.js';
|
|
10
10
|
/** How long one call may take, in milliseconds. */
|
|
11
11
|
const KUBE_TIMEOUT_MS = 30_000;
|
|
12
|
+
/** How many times a call answered 429 is made, in all. */
|
|
13
|
+
const BUSY_TRIES = 10;
|
|
14
|
+
/**
|
|
15
|
+
* How long a 429 asks to be waited, from its Status (`details.retryAfterSeconds`), within one to ten seconds.
|
|
16
|
+
*
|
|
17
|
+
* @param text - The answer's body.
|
|
18
|
+
* @returns Milliseconds.
|
|
19
|
+
*/
|
|
20
|
+
export function busyWait_parse(text) {
|
|
21
|
+
let seconds = 2;
|
|
22
|
+
try {
|
|
23
|
+
const asked = JSON.parse(text).details?.retryAfterSeconds;
|
|
24
|
+
if (typeof asked === 'number')
|
|
25
|
+
seconds = asked;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// Not a Status: the default.
|
|
29
|
+
}
|
|
30
|
+
return Math.min(10, Math.max(1, seconds)) * 1000;
|
|
31
|
+
}
|
|
12
32
|
/**
|
|
13
33
|
* One HTTPS call.
|
|
14
34
|
*
|
|
@@ -53,7 +73,12 @@ function call(access, method, path, body) {
|
|
|
53
73
|
*/
|
|
54
74
|
export function kubeApi_make(access) {
|
|
55
75
|
const ok = async (method, path, body) => {
|
|
56
|
-
|
|
76
|
+
let answer = await call(access, method, path, body);
|
|
77
|
+
// A server still settling answers 429 with how long to wait; it is waited out, briefly.
|
|
78
|
+
for (let tries = 1; answer.status === 429 && tries < BUSY_TRIES; tries++) {
|
|
79
|
+
await new Promise((resolve) => { setTimeout(resolve, busyWait_parse(answer.text)); });
|
|
80
|
+
answer = await call(access, method, path, body);
|
|
81
|
+
}
|
|
57
82
|
if (answer.status < 200 || answer.status >= 300)
|
|
58
83
|
throw new KubeError(answer.status, `${method} ${path}: HTTP ${answer.status} ${answer.text.slice(0, 200)}`);
|
|
59
84
|
return answer.text;
|