windmill-cli 1.401.0 → 1.402.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/esm/gen/core/OpenAPI.js +1 -1
- package/esm/gen/services.gen.js +18 -0
- package/esm/instance.js +165 -69
- package/esm/main.js +8 -2
- package/esm/queues.js +117 -0
- package/esm/settings.js +14 -3
- package/esm/worker_groups.js +96 -0
- package/esm/workers.js +79 -0
- package/package.json +1 -1
- package/types/gen/services.gen.d.ts +10 -1
- package/types/gen/services.gen.d.ts.map +1 -1
- package/types/gen/types.gen.d.ts +18 -0
- package/types/gen/types.gen.d.ts.map +1 -1
- package/types/instance.d.ts +17 -14
- package/types/instance.d.ts.map +1 -1
- package/types/main.d.ts +1 -1
- package/types/main.d.ts.map +1 -1
- package/types/queues.d.ts +14 -0
- package/types/queues.d.ts.map +1 -0
- package/types/settings.d.ts.map +1 -1
- package/types/worker_groups.d.ts +24 -0
- package/types/worker_groups.d.ts.map +1 -0
- package/types/workers.d.ts +14 -0
- package/types/workers.d.ts.map +1 -0
package/esm/gen/core/OpenAPI.js
CHANGED
package/esm/gen/services.gen.js
CHANGED
|
@@ -4510,6 +4510,24 @@ export const getDbClock = () => {
|
|
|
4510
4510
|
url: '/jobs/db_clock'
|
|
4511
4511
|
});
|
|
4512
4512
|
};
|
|
4513
|
+
/**
|
|
4514
|
+
* Count jobs by tag
|
|
4515
|
+
* @param data The data for the request.
|
|
4516
|
+
* @param data.horizonSecs Past Time horizon in seconds (when to start the count = now - horizon) (default is 3600)
|
|
4517
|
+
* @param data.workspaceId Specific workspace ID to filter results (optional)
|
|
4518
|
+
* @returns unknown Job counts by tag
|
|
4519
|
+
* @throws ApiError
|
|
4520
|
+
*/
|
|
4521
|
+
export const countJobsByTag = (data = {}) => {
|
|
4522
|
+
return __request(OpenAPI, {
|
|
4523
|
+
method: 'GET',
|
|
4524
|
+
url: '/jobs/completed/count_by_tag',
|
|
4525
|
+
query: {
|
|
4526
|
+
horizon_secs: data.horizonSecs,
|
|
4527
|
+
workspace_id: data.workspaceId
|
|
4528
|
+
}
|
|
4529
|
+
});
|
|
4530
|
+
};
|
|
4513
4531
|
/**
|
|
4514
4532
|
* get job
|
|
4515
4533
|
* @param data The data for the request.
|
package/esm/instance.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as dntShim from "./_dnt.shims.js";
|
|
2
|
-
import { Select, path, Confirm, yamlStringify, yamlParse, Command, setClient, } from "./deps.js";
|
|
2
|
+
import { Select, path, Confirm, yamlStringify, yamlParse, Command, setClient, Table, } from "./deps.js";
|
|
3
3
|
import * as wmill from "./gen/services.gen.js";
|
|
4
4
|
import { Input, colors, log } from "./deps.js";
|
|
5
5
|
import { loginInteractive } from "./login.js";
|
|
@@ -29,31 +29,35 @@ export async function allInstances() {
|
|
|
29
29
|
return [];
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
-
export async function addInstance() {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
32
|
+
export async function addInstance(opts, instanceName, remote, token) {
|
|
33
|
+
if (!remote) {
|
|
34
|
+
remote = await Input.prompt({
|
|
35
|
+
message: "Enter the remote url of this instance",
|
|
36
|
+
default: "https://my.windmill.dev/",
|
|
37
|
+
});
|
|
38
|
+
remote = new URL(remote).toString(); // add trailing slash in all cases!
|
|
39
|
+
}
|
|
40
|
+
if (!instanceName) {
|
|
41
|
+
const defaultName = new URL(remote).hostname.split(".")[0];
|
|
42
|
+
instanceName = await Input.prompt({
|
|
43
|
+
message: "Enter a name for this instance",
|
|
44
|
+
default: defaultName,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
const prefix = instanceName.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
45
48
|
while (!token) {
|
|
46
49
|
token = await loginInteractive(remote);
|
|
47
50
|
}
|
|
48
51
|
await appendInstance({
|
|
49
|
-
name,
|
|
52
|
+
name: instanceName,
|
|
50
53
|
remote,
|
|
51
54
|
token,
|
|
52
55
|
prefix,
|
|
53
56
|
});
|
|
54
|
-
log.info(colors.green.underline(`Added instance ${
|
|
57
|
+
log.info(colors.green.underline(`Added instance ${instanceName} with remote ${remote}!`));
|
|
58
|
+
await switchI({}, instanceName);
|
|
55
59
|
return {
|
|
56
|
-
name,
|
|
60
|
+
name: instanceName,
|
|
57
61
|
remote,
|
|
58
62
|
token,
|
|
59
63
|
prefix,
|
|
@@ -101,31 +105,53 @@ export function compareInstanceObjects(fromObjects, toObjects, idProp, objectNam
|
|
|
101
105
|
}
|
|
102
106
|
return changes;
|
|
103
107
|
}
|
|
104
|
-
async function
|
|
108
|
+
export async function pickInstance(opts, allowNew) {
|
|
105
109
|
const instances = await allInstances();
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
110
|
+
if (opts.baseUrl && opts.token) {
|
|
111
|
+
log.info("Using instance fully defined by --base-url and --token");
|
|
112
|
+
return {
|
|
113
|
+
name: "custom",
|
|
114
|
+
remote: opts.baseUrl,
|
|
115
|
+
token: opts.token,
|
|
116
|
+
prefix: "custom",
|
|
117
|
+
};
|
|
109
118
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
{ name: "Add new instance", value: "new" },
|
|
119
|
-
],
|
|
120
|
-
}));
|
|
121
|
-
if (choice === "new") {
|
|
122
|
-
instance = await addInstance();
|
|
119
|
+
if (!allowNew && instances.length < 1) {
|
|
120
|
+
throw new Error("No instance found, please add one first");
|
|
121
|
+
}
|
|
122
|
+
const instanceName = await getActiveInstance(opts);
|
|
123
|
+
let instance = instances.find((i) => i.name === instanceName);
|
|
124
|
+
if (!instance) {
|
|
125
|
+
if (instances.length < 1) {
|
|
126
|
+
instance = await addInstance({}, undefined, undefined, undefined);
|
|
123
127
|
}
|
|
124
128
|
else {
|
|
125
|
-
|
|
129
|
+
const choice = (await Select.prompt({
|
|
130
|
+
message: "Select an instance",
|
|
131
|
+
options: [
|
|
132
|
+
...instances.map((i) => ({
|
|
133
|
+
name: `${i.name} (${i.remote})`,
|
|
134
|
+
value: i.name,
|
|
135
|
+
})),
|
|
136
|
+
{ name: "Add new instance", value: "new" },
|
|
137
|
+
],
|
|
138
|
+
}));
|
|
139
|
+
if (choice === "new") {
|
|
140
|
+
instance = await addInstance({}, undefined, undefined, undefined);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
instance = instances.find((i) => i.name === choice);
|
|
144
|
+
}
|
|
126
145
|
}
|
|
127
146
|
}
|
|
147
|
+
else {
|
|
148
|
+
log.info(`Selected instance: ${instance.name}`);
|
|
149
|
+
}
|
|
128
150
|
setClient(instance.token, instance.remote.slice(0, instance.remote.length - 1));
|
|
151
|
+
return instance;
|
|
152
|
+
}
|
|
153
|
+
async function instancePull(opts) {
|
|
154
|
+
const instance = await pickInstance(opts, true);
|
|
129
155
|
log.info("Pulling instance-level changes");
|
|
130
156
|
log.info(`remote (${instance.name}) -> local`);
|
|
131
157
|
let uChanges = 0;
|
|
@@ -146,10 +172,13 @@ async function instancePull(opts) {
|
|
|
146
172
|
}
|
|
147
173
|
const totalChanges = uChanges + sChanges + cChanges + gChanges;
|
|
148
174
|
if (totalChanges > 0) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
175
|
+
let confirm = true;
|
|
176
|
+
if (opts.yes !== true) {
|
|
177
|
+
confirm = await Confirm.prompt({
|
|
178
|
+
message: `Do you want to pull these ${totalChanges} instance-level changes?`,
|
|
179
|
+
default: true,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
153
182
|
if (confirm) {
|
|
154
183
|
if (!opts.skipUsers && uChanges > 0) {
|
|
155
184
|
await pullInstanceUsers();
|
|
@@ -227,29 +256,7 @@ async function instancePull(opts) {
|
|
|
227
256
|
}
|
|
228
257
|
async function instancePush(opts) {
|
|
229
258
|
let instances = await allInstances();
|
|
230
|
-
|
|
231
|
-
if (instances.length < 1) {
|
|
232
|
-
instance = await addInstance();
|
|
233
|
-
}
|
|
234
|
-
else {
|
|
235
|
-
const choice = (await Select.prompt({
|
|
236
|
-
message: "Select an instance to push to",
|
|
237
|
-
options: [
|
|
238
|
-
...instances.map((i) => ({
|
|
239
|
-
name: `${i.name} (${i.remote})`,
|
|
240
|
-
value: i.name,
|
|
241
|
-
})),
|
|
242
|
-
{ name: "Add new instance", value: "new" },
|
|
243
|
-
],
|
|
244
|
-
}));
|
|
245
|
-
if (choice === "new") {
|
|
246
|
-
instance = await addInstance();
|
|
247
|
-
}
|
|
248
|
-
else {
|
|
249
|
-
instance = instances.find((i) => i.name === choice);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
setClient(instance.token, instance.remote.slice(0, instance.remote.length - 1));
|
|
259
|
+
const instance = await pickInstance(opts, true);
|
|
253
260
|
log.info("Pushing instance-level changes");
|
|
254
261
|
log.info(`remote (${instance.name}) <- local`);
|
|
255
262
|
let uChanges = 0;
|
|
@@ -270,10 +277,13 @@ async function instancePush(opts) {
|
|
|
270
277
|
}
|
|
271
278
|
const totalChanges = uChanges + sChanges + cChanges + gChanges;
|
|
272
279
|
if (totalChanges > 0) {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
280
|
+
let confirm = true;
|
|
281
|
+
if (opts.yes !== true) {
|
|
282
|
+
confirm = await Confirm.prompt({
|
|
283
|
+
message: `Do you want to apply these ${totalChanges} instance-level changes?`,
|
|
284
|
+
default: true,
|
|
285
|
+
});
|
|
286
|
+
}
|
|
277
287
|
if (confirm) {
|
|
278
288
|
if (!opts.skipUsers && uChanges > 0) {
|
|
279
289
|
await pushInstanceUsers();
|
|
@@ -365,11 +375,92 @@ async function instancePush(opts) {
|
|
|
365
375
|
log.info(colors.green.underline.bold("All workspaces pushed"));
|
|
366
376
|
}
|
|
367
377
|
}
|
|
378
|
+
async function switchI(opts, instanceName) {
|
|
379
|
+
const all = await allInstances();
|
|
380
|
+
if (all.findIndex((x) => x.name === instanceName) === -1) {
|
|
381
|
+
log.info(colors.red.bold(`! This instance ${instanceName} does not exist locally.`));
|
|
382
|
+
log.info("available instances:");
|
|
383
|
+
for (const w of all) {
|
|
384
|
+
log.info(" - " + w.name);
|
|
385
|
+
}
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
await dntShim.Deno.writeTextFile((await getRootStore()) + "/activeInstance", instanceName);
|
|
389
|
+
log.info(colors.green.underline(`Switched to instance ${instanceName}`));
|
|
390
|
+
}
|
|
391
|
+
export async function getActiveInstance(opts) {
|
|
392
|
+
if (opts.instance) {
|
|
393
|
+
return opts.instance;
|
|
394
|
+
}
|
|
395
|
+
try {
|
|
396
|
+
return await dntShim.Deno.readTextFile((await getRootStore()) + "/activeInstance");
|
|
397
|
+
}
|
|
398
|
+
catch {
|
|
399
|
+
return undefined;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
async function whoami(opts) {
|
|
403
|
+
await pickInstance({}, false);
|
|
404
|
+
try {
|
|
405
|
+
const whoamiInfo = await wmill.globalWhoami();
|
|
406
|
+
log.info(colors.green.underline(`global whoami infos:`));
|
|
407
|
+
log.info(JSON.stringify(whoamiInfo, null, 2));
|
|
408
|
+
}
|
|
409
|
+
catch (error) {
|
|
410
|
+
log.error(colors.red(`Failed to retrieve whoami information: ${error.message}`));
|
|
411
|
+
}
|
|
412
|
+
}
|
|
368
413
|
const command = new Command()
|
|
369
414
|
.description("sync local with a remote instance or the opposite (push or pull)")
|
|
370
|
-
.action(() =>
|
|
415
|
+
.action(async () => {
|
|
416
|
+
log.info("4 actions available, add, remove, switch, pull and push. Use -h to display help.");
|
|
417
|
+
const activeInstance = await getActiveInstance({});
|
|
418
|
+
new Table()
|
|
419
|
+
.header(["name", "remote", "token"])
|
|
420
|
+
.padding(2)
|
|
421
|
+
.border(true)
|
|
422
|
+
.body((await allInstances()).map((x) => [
|
|
423
|
+
x.name === activeInstance ? colors.underline(x.name) : x.name,
|
|
424
|
+
x.remote,
|
|
425
|
+
x.token.substring(0, 7) + "***",
|
|
426
|
+
]))
|
|
427
|
+
.render();
|
|
428
|
+
if (activeInstance) {
|
|
429
|
+
log.info(`Selected instance: ${activeInstance}`);
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
log.info("No active instance selected");
|
|
433
|
+
}
|
|
434
|
+
log.info("Use 'wmill instance add' to add a new instance");
|
|
435
|
+
})
|
|
436
|
+
.command("add")
|
|
437
|
+
.description("Add a new instance")
|
|
438
|
+
.action(addInstance)
|
|
439
|
+
.arguments("[instance_name:string] [remote:string] [token:string]")
|
|
440
|
+
.command("remove")
|
|
441
|
+
.description("Remove an instance")
|
|
442
|
+
.complete("instance", async () => (await allInstances()).map((x) => x.name))
|
|
443
|
+
.arguments("<instance:string:instance>")
|
|
444
|
+
.action(async (instance) => {
|
|
445
|
+
const instances = await allInstances();
|
|
446
|
+
const choice = (await Select.prompt({
|
|
447
|
+
message: "Select an instance to remove",
|
|
448
|
+
options: instances.map((i) => ({
|
|
449
|
+
name: `${i.name} (${i.remote})`,
|
|
450
|
+
value: i.name,
|
|
451
|
+
})),
|
|
452
|
+
}));
|
|
453
|
+
await removeInstance(choice);
|
|
454
|
+
log.info(colors.green.underline(`Removed instance ${choice}`));
|
|
455
|
+
})
|
|
456
|
+
.command("switch")
|
|
457
|
+
.complete("instance", async () => (await allInstances()).map((x) => x.name))
|
|
458
|
+
.arguments("<instance:string:instance>")
|
|
459
|
+
.description("Switch the current instance")
|
|
460
|
+
.action(switchI)
|
|
371
461
|
.command("pull")
|
|
372
462
|
.description("Pull instance settings, users, configs, instance groups and overwrite local")
|
|
463
|
+
.option("--yes", "Pull without needing confirmation")
|
|
373
464
|
.option("--skip-users", "Skip pulling users")
|
|
374
465
|
.option("--skip-settings", "Skip pulling settings")
|
|
375
466
|
.option("--skip-configs", "Skip pulling configs (worker groups and SMTP)")
|
|
@@ -378,11 +469,16 @@ const command = new Command()
|
|
|
378
469
|
.action(instancePull)
|
|
379
470
|
.command("push")
|
|
380
471
|
.description("Push instance settings, users, configs, group and overwrite remote")
|
|
472
|
+
.option("--yes", "Push without needing confirmation")
|
|
381
473
|
.option("--skip-users", "Skip pushing users")
|
|
382
474
|
.option("--skip-settings", "Skip pushing settings")
|
|
383
475
|
.option("--skip-configs", "Skip pushing configs (worker groups and SMTP)")
|
|
384
476
|
.option("--skip-groups", "Skip pushing instance groups")
|
|
385
477
|
.option("--include-workspaces", "Also push workspaces")
|
|
386
|
-
.option("--
|
|
387
|
-
.
|
|
478
|
+
.option("--instance", "Name of the instance to push to, override the active instance")
|
|
479
|
+
.option("--base-url", "If used with --token, will be used as the base url for the instance")
|
|
480
|
+
.action(instancePush)
|
|
481
|
+
.command("whoami")
|
|
482
|
+
.description("Display information about the currently logged-in user")
|
|
483
|
+
.action(whoami);
|
|
388
484
|
export default command;
|
package/esm/main.js
CHANGED
|
@@ -15,6 +15,7 @@ import folder from "./folder.js";
|
|
|
15
15
|
import schedule from "./schedule.js";
|
|
16
16
|
import sync from "./sync.js";
|
|
17
17
|
import instance from "./instance.js";
|
|
18
|
+
import workerGroups from "./worker_groups.js";
|
|
18
19
|
import dev from "./dev.js";
|
|
19
20
|
import { fetchVersion } from "./context.js";
|
|
20
21
|
import { OpenAPI } from "./gen/index.js";
|
|
@@ -23,6 +24,8 @@ import { NpmProvider } from "./upgrade.js";
|
|
|
23
24
|
import { pull as hubPull } from "./hub.js";
|
|
24
25
|
import { pull, push } from "./sync.js";
|
|
25
26
|
import { add as workspaceAdd } from "./workspace.js";
|
|
27
|
+
import workers from "./workers.js";
|
|
28
|
+
import queues from "./queues.js";
|
|
26
29
|
export { flow, app, script, workspace, resource, user, variable, hub, folder, schedule, sync, instance, dev, hubPull, pull, push, workspaceAdd, };
|
|
27
30
|
// addEventListener("error", (event) => {
|
|
28
31
|
// if (event.error) {
|
|
@@ -30,11 +33,11 @@ export { flow, app, script, workspace, resource, user, variable, hub, folder, sc
|
|
|
30
33
|
// console.error(JSON.stringify(event.error, null, 4));
|
|
31
34
|
// }
|
|
32
35
|
// });
|
|
33
|
-
export const VERSION = "1.
|
|
36
|
+
export const VERSION = "1.402.0";
|
|
34
37
|
const command = new Command()
|
|
35
38
|
.name("wmill")
|
|
36
39
|
.action(() => log.info(`Welcome to Windmill CLI ${VERSION}. Use -h for help.`))
|
|
37
|
-
.description("
|
|
40
|
+
.description("Windmill CLI")
|
|
38
41
|
.globalOption("--workspace <workspace:string>", "Specify the target workspace. This overrides the default workspace.")
|
|
39
42
|
.globalOption("--debug --verbose", "Show debug/verbose logs")
|
|
40
43
|
.globalOption("--show-diffs", "Show diff informations when syncing (may show sensitive informations)")
|
|
@@ -69,6 +72,9 @@ const command = new Command()
|
|
|
69
72
|
.command("dev", dev)
|
|
70
73
|
.command("sync", sync)
|
|
71
74
|
.command("instance", instance)
|
|
75
|
+
.command("worker-groups", workerGroups)
|
|
76
|
+
.command("workers", workers)
|
|
77
|
+
.command("queues", queues)
|
|
72
78
|
.command("version", "Show version information")
|
|
73
79
|
.action(async (opts) => {
|
|
74
80
|
console.log("CLI build against " + VERSION);
|
package/esm/queues.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { Command, Table } from "./deps.js";
|
|
2
|
+
import { log } from "./deps.js";
|
|
3
|
+
import * as wmill from "./gen/services.gen.js";
|
|
4
|
+
import { pickInstance } from "./instance.js";
|
|
5
|
+
function createRow(tag, data) {
|
|
6
|
+
if (data[tag]) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
data[tag] = {
|
|
11
|
+
count: 0,
|
|
12
|
+
waiting: 0,
|
|
13
|
+
later: 0,
|
|
14
|
+
running: 0,
|
|
15
|
+
suspended: 0,
|
|
16
|
+
rps30s: "",
|
|
17
|
+
rps5min: "",
|
|
18
|
+
rps30min: "",
|
|
19
|
+
rps24h: "",
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
async function displayQueues(opts, workspace) {
|
|
24
|
+
const activeInstance = await pickInstance(opts, true);
|
|
25
|
+
if (activeInstance) {
|
|
26
|
+
try {
|
|
27
|
+
const queuedJobs = await wmill.listQueue({ workspace: workspace ?? 'admins', allWorkspaces: workspace === undefined, perPage: 100000 });
|
|
28
|
+
const jobCounts30s = await wmill.countJobsByTag({
|
|
29
|
+
horizonSecs: 30,
|
|
30
|
+
workspaceId: workspace,
|
|
31
|
+
});
|
|
32
|
+
const nowFromDb = new Date(await wmill.getDbClock());
|
|
33
|
+
const jobCounts5min = await wmill.countJobsByTag({
|
|
34
|
+
horizonSecs: 300,
|
|
35
|
+
workspaceId: workspace,
|
|
36
|
+
});
|
|
37
|
+
const jobCounts30min = await wmill.countJobsByTag({
|
|
38
|
+
horizonSecs: 1800,
|
|
39
|
+
workspaceId: workspace,
|
|
40
|
+
});
|
|
41
|
+
const jobCounts24h = await wmill.countJobsByTag({
|
|
42
|
+
horizonSecs: 86400,
|
|
43
|
+
workspaceId: workspace,
|
|
44
|
+
});
|
|
45
|
+
const data = {};
|
|
46
|
+
for (const job of queuedJobs) {
|
|
47
|
+
createRow(job.tag, data);
|
|
48
|
+
const scheduledFor = new Date(job.scheduled_for ?? "");
|
|
49
|
+
if (job.running) {
|
|
50
|
+
if (job.suspend) {
|
|
51
|
+
data[job.tag].suspended += 1;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
data[job.tag].running += 1;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else if (scheduledFor <= nowFromDb) {
|
|
58
|
+
data[job.tag].waiting += 1;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
data[job.tag].later += 1;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
for (const count of jobCounts30s) {
|
|
65
|
+
const tag = count.tag;
|
|
66
|
+
createRow(tag, data);
|
|
67
|
+
data[tag].rps30s = (count.count / 30).toFixed(3);
|
|
68
|
+
}
|
|
69
|
+
for (const count of jobCounts5min) {
|
|
70
|
+
const tag = count.tag;
|
|
71
|
+
createRow(tag, data);
|
|
72
|
+
data[tag].rps5min = (count.count / 300).toFixed(3);
|
|
73
|
+
}
|
|
74
|
+
for (const count of jobCounts30min) {
|
|
75
|
+
const tag = count.tag;
|
|
76
|
+
createRow(tag, data);
|
|
77
|
+
data[tag].rps30min = (count.count / 1800).toFixed(3);
|
|
78
|
+
}
|
|
79
|
+
for (const count of jobCounts24h) {
|
|
80
|
+
const tag = count.tag;
|
|
81
|
+
createRow(tag, data);
|
|
82
|
+
data[tag].rps24h = (count.count / 86400).toFixed(3);
|
|
83
|
+
}
|
|
84
|
+
const table = new Table();
|
|
85
|
+
table.header([
|
|
86
|
+
"",
|
|
87
|
+
"Running",
|
|
88
|
+
"Waiting",
|
|
89
|
+
"Later",
|
|
90
|
+
"Suspended",
|
|
91
|
+
"RPS (30s)",
|
|
92
|
+
"RPS (5min)",
|
|
93
|
+
"RPS (30min)",
|
|
94
|
+
"RPS (24h)",
|
|
95
|
+
]);
|
|
96
|
+
let body = [];
|
|
97
|
+
for (const tag in data) {
|
|
98
|
+
body.push([tag, data[tag].running, data[tag].waiting, data[tag].later, data[tag].suspended, data[tag].rps30s, data[tag].rps5min, data[tag].rps30min, data[tag].rps24h]);
|
|
99
|
+
}
|
|
100
|
+
table.body(body).render();
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
log.error("Failed to fetch queue metrics:", error);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
log.info("No active instance found");
|
|
108
|
+
log.info("Use 'wmill instance add' to add a new instance");
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const command = new Command()
|
|
112
|
+
.description("List all queues with their metrics")
|
|
113
|
+
.arguments("[workspace:string] the optional workspace to filter by (default to all workspaces)")
|
|
114
|
+
.option("--instance [instance]", "Name of the instance to push to, override the active instance")
|
|
115
|
+
.option("--base-url [baseUrl]", "If used with --token, will be used as the base url for the instance")
|
|
116
|
+
.action(displayQueues);
|
|
117
|
+
export default command;
|
package/esm/settings.js
CHANGED
|
@@ -8,6 +8,7 @@ import { compareInstanceObjects } from "./instance.js";
|
|
|
8
8
|
import { isSuperset } from "./types.js";
|
|
9
9
|
import { deepEqual } from "./utils.js";
|
|
10
10
|
import * as wmill from "./gen/services.gen.js";
|
|
11
|
+
import { removeWorkerPrefix } from "./worker_groups.js";
|
|
11
12
|
export async function pushWorkspaceSettings(workspace, _path, settings, localSettings) {
|
|
12
13
|
try {
|
|
13
14
|
const remoteSettings = await wmill.getSettings({
|
|
@@ -271,7 +272,12 @@ export async function pushInstanceSettings(preview = false, baseUrl) {
|
|
|
271
272
|
}
|
|
272
273
|
}
|
|
273
274
|
export async function pullInstanceConfigs(preview = false) {
|
|
274
|
-
const remoteConfigs = await wmill.listConfigs()
|
|
275
|
+
const remoteConfigs = (await wmill.listConfigs()).map((x) => {
|
|
276
|
+
return {
|
|
277
|
+
...x,
|
|
278
|
+
name: removeWorkerPrefix(x.name),
|
|
279
|
+
};
|
|
280
|
+
});
|
|
275
281
|
if (preview) {
|
|
276
282
|
let localConfigs = [];
|
|
277
283
|
try {
|
|
@@ -287,7 +293,12 @@ export async function pullInstanceConfigs(preview = false) {
|
|
|
287
293
|
}
|
|
288
294
|
}
|
|
289
295
|
export async function pushInstanceConfigs(preview = false) {
|
|
290
|
-
const remoteConfigs = await wmill.listConfigs()
|
|
296
|
+
const remoteConfigs = (await wmill.listConfigs()).map((x) => {
|
|
297
|
+
return {
|
|
298
|
+
...x,
|
|
299
|
+
name: removeWorkerPrefix(x.name),
|
|
300
|
+
};
|
|
301
|
+
});
|
|
291
302
|
const localConfigs = (await dntShim.Deno.readTextFile("instance_configs.yaml")
|
|
292
303
|
.then((raw) => yamlParse(raw))
|
|
293
304
|
.catch(() => []));
|
|
@@ -303,7 +314,7 @@ export async function pushInstanceConfigs(preview = false) {
|
|
|
303
314
|
}
|
|
304
315
|
try {
|
|
305
316
|
await wmill.updateConfig({
|
|
306
|
-
name: config.name
|
|
317
|
+
name: config.name.startsWith('worker__') ? config.name : `worker__${config.name}`,
|
|
307
318
|
requestBody: config.config,
|
|
308
319
|
});
|
|
309
320
|
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { Command, Confirm, setClient, Table } from "./deps.js";
|
|
2
|
+
import { log } from "./deps.js";
|
|
3
|
+
import { allInstances, getActiveInstance, pickInstance } from "./instance.js";
|
|
4
|
+
import * as wmill from "./gen/services.gen.js";
|
|
5
|
+
import { pullInstanceConfigs, pushInstanceConfigs } from "./settings.js";
|
|
6
|
+
export async function getInstance(opts) {
|
|
7
|
+
const instances = await allInstances();
|
|
8
|
+
const instanceName = await getActiveInstance(opts);
|
|
9
|
+
const instance = instances.find((i) => i.name === instanceName);
|
|
10
|
+
if (instance) {
|
|
11
|
+
setClient(instance.token, instance.remote.slice(0, instance.remote.length - 1));
|
|
12
|
+
}
|
|
13
|
+
return instance;
|
|
14
|
+
}
|
|
15
|
+
export function removeWorkerPrefix(name) {
|
|
16
|
+
if (name.startsWith("worker__")) {
|
|
17
|
+
return name.substring(8);
|
|
18
|
+
}
|
|
19
|
+
return name;
|
|
20
|
+
}
|
|
21
|
+
export async function displayWorkerGroups(opts) {
|
|
22
|
+
log.info("2 actions available, pull and push.");
|
|
23
|
+
const activeInstance = await getActiveInstance({});
|
|
24
|
+
if (activeInstance) {
|
|
25
|
+
log.info("Active instance: " + activeInstance);
|
|
26
|
+
const instance = await getInstance({});
|
|
27
|
+
if (instance) {
|
|
28
|
+
const wGroups = await wmill.listWorkerGroups();
|
|
29
|
+
new Table()
|
|
30
|
+
.header(["name", "config"])
|
|
31
|
+
.padding(2)
|
|
32
|
+
.border(true)
|
|
33
|
+
.body(wGroups.map((x) => [removeWorkerPrefix(x.name), JSON.stringify(x.config, null, 2)]))
|
|
34
|
+
.render();
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
log.error(`Instance ${activeInstance} not found`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
log.info("No active instance found");
|
|
42
|
+
log.info("Use 'wmill instance add' to add a new instance");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function pullWorkerGroups(opts) {
|
|
46
|
+
await pickInstance(opts, true);
|
|
47
|
+
const totalChanges = await pullInstanceConfigs(true) ?? 0;
|
|
48
|
+
if (totalChanges === 0) {
|
|
49
|
+
log.info("No changes to apply");
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
let confirm = true;
|
|
53
|
+
if (opts.yes !== true) {
|
|
54
|
+
confirm = await Confirm.prompt({
|
|
55
|
+
message: `Do you want to pul these ${totalChanges} instance-level changes?`,
|
|
56
|
+
default: true,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
if (confirm) {
|
|
60
|
+
await pullInstanceConfigs(false);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async function pushWorkerGroups(opts) {
|
|
64
|
+
await pickInstance(opts, true);
|
|
65
|
+
const totalChanges = await pushInstanceConfigs(true) ?? 0;
|
|
66
|
+
if (totalChanges === 0) {
|
|
67
|
+
log.info("No changes to apply");
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
let confirm = true;
|
|
71
|
+
if (opts.yes !== true) {
|
|
72
|
+
confirm = await Confirm.prompt({
|
|
73
|
+
message: `Do you want to apply these ${totalChanges} instance-level changes?`,
|
|
74
|
+
default: true,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
if (confirm) {
|
|
78
|
+
await pushInstanceConfigs(false);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const command = new Command()
|
|
82
|
+
.description("display worker groups, pull and push worker groups configs")
|
|
83
|
+
.action(displayWorkerGroups)
|
|
84
|
+
.command("pull")
|
|
85
|
+
.description("Pull worker groups (similar to `wmill instance pull --skip-users --skip-settings --skip-groups`)")
|
|
86
|
+
.option("--instance", "Name of the instance to push to, override the active instance")
|
|
87
|
+
.option("--base-url", "Base url to be passed to the instance settings instead of the local one")
|
|
88
|
+
.option("--yes", "Pull without needing confirmation")
|
|
89
|
+
.action(pullWorkerGroups)
|
|
90
|
+
.command("push")
|
|
91
|
+
.description("Push instance settings, users, configs, group and overwrite remote")
|
|
92
|
+
.option("--instance [instance]", "Name of the instance to push to, override the active instance")
|
|
93
|
+
.option("--base-url [baseUrl]", "If used with --token, will be used as the base url for the instance")
|
|
94
|
+
.option("--yes", "Push without needing confirmation")
|
|
95
|
+
.action(pushWorkerGroups);
|
|
96
|
+
export default command;
|