@viraatdas/rudder 0.7.28 → 0.7.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -2
- package/dist/backends.js +28 -3
- package/dist/backends.js.map +1 -1
- package/dist/brain.js +2 -3
- package/dist/brain.js.map +1 -1
- package/dist/cloud.d.ts +1 -0
- package/dist/cloud.js +367 -27
- package/dist/cloud.js.map +1 -1
- package/dist/main.js +27 -4
- package/dist/main.js.map +1 -1
- package/dist/native/rudder-native +0 -0
- package/dist/native-agents.js +29 -5
- package/dist/native-agents.js.map +1 -1
- package/dist/run-manager.js +8 -7
- package/dist/run-manager.js.map +1 -1
- package/dist/state.js +8 -1
- package/dist/state.js.map +1 -1
- package/dist/task-summary.d.ts +5 -0
- package/dist/task-summary.js +128 -0
- package/dist/task-summary.js.map +1 -0
- package/dist/tmux-dashboard.js +11 -21
- package/dist/tmux-dashboard.js.map +1 -1
- package/dist/tui.js +25 -27
- package/dist/tui.js.map +1 -1
- package/dist/types.d.ts +3 -0
- package/package.json +1 -1
package/dist/cloud.js
CHANGED
|
@@ -4,7 +4,7 @@ import os from "node:os";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { currentBranch, currentCommit, findRepoRoot } from "./git.js";
|
|
6
6
|
import { cloudAuthPath } from "./state.js";
|
|
7
|
-
import { ensureDir, expandHome, newRunId, nowIso, pathExists, promptSecret, readJson, runCommand, shortenHome, writeJson, } from "./util.js";
|
|
7
|
+
import { ensureDir, commandExists, expandHome, newRunId, nowIso, pathExists, promptText, promptSelect, promptSecret, readJson, runCommand, shortenHome, shellQuote, writeJson, } from "./util.js";
|
|
8
8
|
const DEFAULT_LOGIN_INTERVAL_MS = 2000;
|
|
9
9
|
const DEFAULT_LOGIN_TIMEOUT_MS = 5 * 60 * 1000;
|
|
10
10
|
const DEFAULT_CLOUD_URL = "https://mpd2pmnpep.us-east-1.awsapprunner.com";
|
|
@@ -81,11 +81,16 @@ export async function runCloudCommand(command, args, options = {}) {
|
|
|
81
81
|
case "login":
|
|
82
82
|
await login(options);
|
|
83
83
|
return;
|
|
84
|
+
case "launch":
|
|
85
|
+
await launch(rest, options, "task");
|
|
86
|
+
return;
|
|
84
87
|
case "sail":
|
|
85
88
|
await launch(rest, options);
|
|
86
89
|
return;
|
|
87
|
-
case "
|
|
88
|
-
|
|
90
|
+
case "vm":
|
|
91
|
+
case "byoc":
|
|
92
|
+
case "byo-vm":
|
|
93
|
+
await launch(rest, options, "task", "byo-vm");
|
|
89
94
|
return;
|
|
90
95
|
case "list":
|
|
91
96
|
case "ls":
|
|
@@ -94,6 +99,9 @@ export async function runCloudCommand(command, args, options = {}) {
|
|
|
94
99
|
case "onload":
|
|
95
100
|
await onload(rest, options);
|
|
96
101
|
return;
|
|
102
|
+
case "bootstrap":
|
|
103
|
+
await bootstrap(rest, options);
|
|
104
|
+
return;
|
|
97
105
|
case "pause":
|
|
98
106
|
await mutateSail("pause", rest, options);
|
|
99
107
|
return;
|
|
@@ -106,6 +114,28 @@ export async function runCloudCommand(command, args, options = {}) {
|
|
|
106
114
|
case "setup-google":
|
|
107
115
|
await setupOAuthProvider("google", rest, options);
|
|
108
116
|
return;
|
|
117
|
+
case "setup-byoc":
|
|
118
|
+
await setupByoc(rest, options);
|
|
119
|
+
return;
|
|
120
|
+
case "setup-vm":
|
|
121
|
+
await setupByoc(rest, options);
|
|
122
|
+
return;
|
|
123
|
+
case "setup-fly":
|
|
124
|
+
await configureDefaultRuntime("fly", options);
|
|
125
|
+
return;
|
|
126
|
+
case "setup":
|
|
127
|
+
if (rest[0] === "byoc" || rest[0] === "vm" || rest[0] === "byo-vm") {
|
|
128
|
+
await setupByoc(rest.slice(1), options);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (rest[0] === "fly") {
|
|
132
|
+
await configureDefaultRuntime("fly", options);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
throw new Error("Usage: rudder cloud setup byoc | rudder cloud setup fly");
|
|
136
|
+
case "runtime":
|
|
137
|
+
await runtime(rest, options);
|
|
138
|
+
return;
|
|
109
139
|
default:
|
|
110
140
|
await launch(command === "sail" ? args : [subcommand, ...rest], options);
|
|
111
141
|
return;
|
|
@@ -248,10 +278,15 @@ async function githubOAuthRequest(url, body) {
|
|
|
248
278
|
return parsed;
|
|
249
279
|
}
|
|
250
280
|
async function saveCloudLogin(client, login, token, options, source) {
|
|
281
|
+
const previous = await loadCloudAuth();
|
|
282
|
+
const previousRuntime = previous?.cloudUrl === client.baseUrl ? parseCloudRuntime(previous.defaultRuntime) : undefined;
|
|
283
|
+
const previousByocHost = previous?.cloudUrl === client.baseUrl ? previous.byocSshHost : undefined;
|
|
251
284
|
await saveCloudAuth({
|
|
252
285
|
version: 1,
|
|
253
286
|
token,
|
|
254
287
|
cloudUrl: client.baseUrl,
|
|
288
|
+
defaultRuntime: previousRuntime,
|
|
289
|
+
byocSshHost: previousByocHost,
|
|
255
290
|
accountId: login.accountId,
|
|
256
291
|
email: login.email,
|
|
257
292
|
expiresAt: login.expiresAt ?? (login.expiresIn ? new Date(Date.now() + login.expiresIn * 1000).toISOString() : undefined),
|
|
@@ -271,16 +306,15 @@ async function saveCloudLogin(client, login, token, options, source) {
|
|
|
271
306
|
console.log(`Logged in to ${client.baseUrl}${login.email ? ` as ${login.email}` : ""} via ${source}.`);
|
|
272
307
|
}
|
|
273
308
|
}
|
|
274
|
-
async function launch(args, options, mode = "name") {
|
|
309
|
+
async function launch(args, options, mode = "name", explicitRuntime) {
|
|
275
310
|
const raw = args.join(" ").trim();
|
|
276
|
-
const name = mode === "task"
|
|
277
|
-
? cloudNameFromTask(raw)
|
|
278
|
-
: raw || randomCloudName();
|
|
279
|
-
const task = mode === "task" ? raw : "";
|
|
280
311
|
const repoRoot = findRepoRoot();
|
|
281
312
|
const snapshot = await createSnapshot(repoRoot, options.homePaths ?? []);
|
|
282
313
|
try {
|
|
283
314
|
const client = await cloudClient({ requireToken: true });
|
|
315
|
+
const runtime = await selectedCloudRuntime(explicitRuntime);
|
|
316
|
+
const task = mode === "task" || runtime === "byo-vm" ? raw : "";
|
|
317
|
+
const name = task ? cloudNameFromTask(task) : raw || randomCloudName();
|
|
284
318
|
const body = {
|
|
285
319
|
repoName: path.basename(repoRoot),
|
|
286
320
|
name,
|
|
@@ -291,6 +325,9 @@ async function launch(args, options, mode = "name") {
|
|
|
291
325
|
manifest: snapshot.manifest,
|
|
292
326
|
},
|
|
293
327
|
};
|
|
328
|
+
if (runtime !== "fly") {
|
|
329
|
+
body.runtime = runtime;
|
|
330
|
+
}
|
|
294
331
|
if (task) {
|
|
295
332
|
body.task = task;
|
|
296
333
|
}
|
|
@@ -298,7 +335,7 @@ async function launch(args, options, mode = "name") {
|
|
|
298
335
|
method: "POST",
|
|
299
336
|
body,
|
|
300
337
|
});
|
|
301
|
-
printResult(result, options);
|
|
338
|
+
await printResult(result, options);
|
|
302
339
|
}
|
|
303
340
|
finally {
|
|
304
341
|
await fsp.rm(snapshot.tempDir, { recursive: true, force: true });
|
|
@@ -321,12 +358,14 @@ async function onload(args, options) {
|
|
|
321
358
|
const snapshot = await createSnapshot(snapshotRoot, options.homePaths ?? []);
|
|
322
359
|
try {
|
|
323
360
|
const client = await cloudClient({ requireToken: true });
|
|
361
|
+
const runtime = await selectedCloudRuntime();
|
|
324
362
|
const result = await client.request("/api/rudder/sail/onload", {
|
|
325
363
|
method: "POST",
|
|
326
364
|
body: {
|
|
327
365
|
runId,
|
|
328
366
|
repoName: path.basename(repoRoot),
|
|
329
367
|
run: runRecord ?? null,
|
|
368
|
+
...(runtime !== "fly" ? { runtime } : {}),
|
|
330
369
|
snapshot: {
|
|
331
370
|
name: path.basename(snapshot.archivePath),
|
|
332
371
|
contentType: "application/gzip",
|
|
@@ -335,7 +374,7 @@ async function onload(args, options) {
|
|
|
335
374
|
},
|
|
336
375
|
},
|
|
337
376
|
});
|
|
338
|
-
printResult(result, options);
|
|
377
|
+
await printResult(result, options);
|
|
339
378
|
}
|
|
340
379
|
finally {
|
|
341
380
|
await fsp.rm(snapshot.tempDir, { recursive: true, force: true });
|
|
@@ -344,7 +383,19 @@ async function onload(args, options) {
|
|
|
344
383
|
async function listSails(options) {
|
|
345
384
|
const client = await cloudClient({ requireToken: true });
|
|
346
385
|
const result = await client.request("/api/rudder/sail", { method: "GET" });
|
|
347
|
-
printResult(result, options);
|
|
386
|
+
await printResult(result, options);
|
|
387
|
+
}
|
|
388
|
+
async function bootstrap(args, options) {
|
|
389
|
+
const sailId = args[0];
|
|
390
|
+
if (!sailId) {
|
|
391
|
+
throw new Error("Missing sail id. Usage: rudder cloud bootstrap <id>");
|
|
392
|
+
}
|
|
393
|
+
const client = await cloudClient({ requireToken: true });
|
|
394
|
+
const result = await client.request(`/api/rudder/sail/${encodeURIComponent(sailId)}/bootstrap`, {
|
|
395
|
+
method: "POST",
|
|
396
|
+
body: {},
|
|
397
|
+
});
|
|
398
|
+
await printResult(result, options);
|
|
348
399
|
}
|
|
349
400
|
async function mutateSail(action, args, options) {
|
|
350
401
|
const sailId = args[0];
|
|
@@ -356,7 +407,7 @@ async function mutateSail(action, args, options) {
|
|
|
356
407
|
method: "POST",
|
|
357
408
|
body: args.length > 1 ? { args: args.slice(1) } : {},
|
|
358
409
|
});
|
|
359
|
-
printResult(result, options);
|
|
410
|
+
await printResult(result, options);
|
|
360
411
|
}
|
|
361
412
|
async function setupOAuthProvider(provider, args, options) {
|
|
362
413
|
const envPrefix = provider === "github" ? "RUDDER_GITHUB" : "RUDDER_GOOGLE";
|
|
@@ -379,7 +430,236 @@ async function setupOAuthProvider(provider, args, options) {
|
|
|
379
430
|
clientSecret,
|
|
380
431
|
},
|
|
381
432
|
});
|
|
382
|
-
printResult(result, options);
|
|
433
|
+
await printResult(result, options);
|
|
434
|
+
}
|
|
435
|
+
async function setupByoc(args, options) {
|
|
436
|
+
const sshConfigPath = path.join(os.homedir(), ".ssh", "config");
|
|
437
|
+
const configuredHosts = await listSshConfigHosts(sshConfigPath);
|
|
438
|
+
const host = (options.sshHost ?? args.join(" ").trim()) || await chooseByocHost(configuredHosts);
|
|
439
|
+
if (!host) {
|
|
440
|
+
throw new Error([
|
|
441
|
+
"Missing BYOC SSH host.",
|
|
442
|
+
"Add your workstation/server to ~/.ssh/config, then run:",
|
|
443
|
+
"",
|
|
444
|
+
" rudder cloud setup-byoc <ssh-host>",
|
|
445
|
+
"",
|
|
446
|
+
"Example ~/.ssh/config:",
|
|
447
|
+
" Host rudder-workstation",
|
|
448
|
+
" HostName 203.0.113.10",
|
|
449
|
+
" User ubuntu",
|
|
450
|
+
" IdentityFile ~/.ssh/id_ed25519",
|
|
451
|
+
"",
|
|
452
|
+
configuredHosts.length
|
|
453
|
+
? `Detected SSH hosts: ${configuredHosts.slice(0, 12).join(", ")}`
|
|
454
|
+
: `No usable hosts found in ${shortenHome(sshConfigPath)}.`,
|
|
455
|
+
].join("\n"));
|
|
456
|
+
}
|
|
457
|
+
const configMentionsHost = configuredHosts.includes(host) || await sshConfigMentions(sshConfigPath, host);
|
|
458
|
+
const diagnostics = await checkByocHost(host);
|
|
459
|
+
await configureDefaultRuntime("byo-vm", options, host);
|
|
460
|
+
if (options.json) {
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
if (!configMentionsHost) {
|
|
464
|
+
console.log(`\nNote: ${shortenHome(sshConfigPath)} does not appear to define Host ${host}.`);
|
|
465
|
+
console.log("Rudder can still use it if SSH resolves it, but a ~/.ssh/config entry is recommended:");
|
|
466
|
+
console.log(` Host ${host}`);
|
|
467
|
+
console.log(" HostName <server-ip-or-dns>");
|
|
468
|
+
console.log(" User <user>");
|
|
469
|
+
console.log(" IdentityFile ~/.ssh/<private-key>");
|
|
470
|
+
}
|
|
471
|
+
if (diagnostics.ok) {
|
|
472
|
+
console.log(`SSH check passed for ${host}. Docker is available on the BYOC host.`);
|
|
473
|
+
}
|
|
474
|
+
else {
|
|
475
|
+
console.log(`\nSSH check did not fully pass for ${host}: ${diagnostics.message}`);
|
|
476
|
+
console.log("Fix SSH/Docker before launching, or run the printed Docker command manually on that host.");
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
async function chooseByocHost(hosts) {
|
|
480
|
+
if (hosts.length === 0) {
|
|
481
|
+
return await promptText("SSH host from ~/.ssh/config");
|
|
482
|
+
}
|
|
483
|
+
return await promptSelect("Choose a BYOC SSH host from ~/.ssh/config", hosts.slice(0, 24).map((host) => ({ value: host, label: host })), hosts[0]);
|
|
484
|
+
}
|
|
485
|
+
async function configureDefaultRuntime(runtime, options, byocSshHost) {
|
|
486
|
+
const client = await cloudClient({ requireToken: true });
|
|
487
|
+
const state = await loadCloudAuth();
|
|
488
|
+
if (!state || state.cloudUrl !== client.baseUrl) {
|
|
489
|
+
throw new Error("Not logged in to this Rudder Cloud control plane. Run `rudder login` first.");
|
|
490
|
+
}
|
|
491
|
+
await saveCloudAuth({
|
|
492
|
+
...state,
|
|
493
|
+
defaultRuntime: runtime,
|
|
494
|
+
byocSshHost: runtime === "byo-vm" ? byocSshHost ?? state.byocSshHost : undefined,
|
|
495
|
+
updatedAt: nowIso(),
|
|
496
|
+
});
|
|
497
|
+
const result = {
|
|
498
|
+
ok: true,
|
|
499
|
+
cloudUrl: client.baseUrl,
|
|
500
|
+
defaultRuntime: runtime,
|
|
501
|
+
};
|
|
502
|
+
const savedByocHost = byocSshHost ?? state.byocSshHost;
|
|
503
|
+
if (runtime === "byo-vm" && savedByocHost) {
|
|
504
|
+
result.byocSshHost = savedByocHost;
|
|
505
|
+
}
|
|
506
|
+
const envRuntime = envCloudRuntime();
|
|
507
|
+
if (envRuntime) {
|
|
508
|
+
result.envOverride = envRuntime;
|
|
509
|
+
}
|
|
510
|
+
if (options.json) {
|
|
511
|
+
printJson(result);
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
console.log(`Rudder Cloud runtime set to ${runtime}.`);
|
|
515
|
+
if (runtime === "byo-vm") {
|
|
516
|
+
const host = byocSshHost ?? state.byocSshHost;
|
|
517
|
+
console.log("Future `rudder cloud <task>` and `/sail <task>` launches will prepare a BYOC worker instead of creating a Fly Machine.");
|
|
518
|
+
console.log(host
|
|
519
|
+
? `Rudder will try to start the worker over SSH on ${host}.`
|
|
520
|
+
: "Run `rudder cloud setup-byoc <ssh-host>` to let Rudder start workers over SSH.");
|
|
521
|
+
}
|
|
522
|
+
else {
|
|
523
|
+
console.log("Future `rudder cloud <task>` and `/sail <task>` launches will create Fly Machines.");
|
|
524
|
+
}
|
|
525
|
+
if (envRuntime) {
|
|
526
|
+
console.log(`RUDDER_CLOUD_RUNTIME=${envRuntime} is set and will override this saved default.`);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
async function runtime(args, options) {
|
|
530
|
+
const next = args[0] ? parseCloudRuntime(args[0]) : undefined;
|
|
531
|
+
if (args[0] && !next) {
|
|
532
|
+
throw new Error("Runtime must be `fly`, `byoc`, or `byo-vm`.");
|
|
533
|
+
}
|
|
534
|
+
if (next) {
|
|
535
|
+
await configureDefaultRuntime(next, options);
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
const client = await cloudClient({ requireToken: true });
|
|
539
|
+
const current = await selectedCloudRuntime();
|
|
540
|
+
const state = await loadCloudAuth();
|
|
541
|
+
const savedRuntime = parseCloudRuntime(state?.defaultRuntime);
|
|
542
|
+
const result = {
|
|
543
|
+
cloudUrl: client.baseUrl,
|
|
544
|
+
runtime: current,
|
|
545
|
+
};
|
|
546
|
+
const envRuntime = envCloudRuntime();
|
|
547
|
+
if (state?.cloudUrl === client.baseUrl && savedRuntime) {
|
|
548
|
+
result.savedDefaultRuntime = savedRuntime;
|
|
549
|
+
}
|
|
550
|
+
if (state?.cloudUrl === client.baseUrl && state.byocSshHost) {
|
|
551
|
+
result.byocSshHost = state.byocSshHost;
|
|
552
|
+
}
|
|
553
|
+
if (envRuntime) {
|
|
554
|
+
result.envOverride = envRuntime;
|
|
555
|
+
}
|
|
556
|
+
if (options.json) {
|
|
557
|
+
printJson(result);
|
|
558
|
+
}
|
|
559
|
+
else {
|
|
560
|
+
console.log(`Rudder Cloud runtime: ${current}`);
|
|
561
|
+
if (envRuntime) {
|
|
562
|
+
console.log(`Set by RUDDER_CLOUD_RUNTIME=${envRuntime}.`);
|
|
563
|
+
}
|
|
564
|
+
else if (state?.cloudUrl === client.baseUrl && savedRuntime) {
|
|
565
|
+
console.log("Set in local Rudder Cloud config.");
|
|
566
|
+
}
|
|
567
|
+
else {
|
|
568
|
+
console.log("Using default Fly Machines runtime.");
|
|
569
|
+
}
|
|
570
|
+
if (state?.cloudUrl === client.baseUrl && state.byocSshHost) {
|
|
571
|
+
console.log(`BYOC SSH host: ${state.byocSshHost}`);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
async function sshConfigMentions(configPath, host) {
|
|
576
|
+
const text = await fsp.readFile(configPath, "utf8").catch(() => "");
|
|
577
|
+
if (!text.trim()) {
|
|
578
|
+
return false;
|
|
579
|
+
}
|
|
580
|
+
const target = host.toLowerCase();
|
|
581
|
+
for (const line of text.split(/\r?\n/)) {
|
|
582
|
+
const trimmed = line.trim();
|
|
583
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
584
|
+
continue;
|
|
585
|
+
}
|
|
586
|
+
const match = /^Host\s+(.+)$/i.exec(trimmed);
|
|
587
|
+
if (!match) {
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
|
+
const patterns = match[1].split(/\s+/).map((part) => part.toLowerCase());
|
|
591
|
+
if (patterns.includes(target)) {
|
|
592
|
+
return true;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
return false;
|
|
596
|
+
}
|
|
597
|
+
async function listSshConfigHosts(configPath) {
|
|
598
|
+
const text = await fsp.readFile(configPath, "utf8").catch(() => "");
|
|
599
|
+
const hosts = [];
|
|
600
|
+
const seen = new Set();
|
|
601
|
+
for (const line of text.split(/\r?\n/)) {
|
|
602
|
+
const trimmed = line.trim();
|
|
603
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
604
|
+
continue;
|
|
605
|
+
}
|
|
606
|
+
const match = /^Host\s+(.+)$/i.exec(trimmed);
|
|
607
|
+
if (!match) {
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
for (const host of match[1].split(/\s+/)) {
|
|
611
|
+
if (!host || host.includes("*") || host.includes("?") || host.startsWith("!")) {
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
if (seen.has(host)) {
|
|
615
|
+
continue;
|
|
616
|
+
}
|
|
617
|
+
seen.add(host);
|
|
618
|
+
hosts.push(host);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
return hosts;
|
|
622
|
+
}
|
|
623
|
+
async function checkByocHost(host) {
|
|
624
|
+
if (!commandExists("ssh")) {
|
|
625
|
+
return { ok: false, message: "ssh is not installed or not on PATH" };
|
|
626
|
+
}
|
|
627
|
+
const result = await runCommand("ssh", [
|
|
628
|
+
"-o",
|
|
629
|
+
"BatchMode=yes",
|
|
630
|
+
"-o",
|
|
631
|
+
"ConnectTimeout=8",
|
|
632
|
+
host,
|
|
633
|
+
"command -v docker >/dev/null && docker info >/dev/null 2>&1",
|
|
634
|
+
], { allowFailure: true });
|
|
635
|
+
if (result.code === 0) {
|
|
636
|
+
return { ok: true, message: "ok" };
|
|
637
|
+
}
|
|
638
|
+
const detail = (result.stderr || result.stdout || `ssh exited ${result.code}`).trim();
|
|
639
|
+
return { ok: false, message: detail };
|
|
640
|
+
}
|
|
641
|
+
async function startByocWorkerOverSsh(host, bootstrapCommand) {
|
|
642
|
+
if (!commandExists("ssh")) {
|
|
643
|
+
throw new Error("ssh is not installed or not on PATH");
|
|
644
|
+
}
|
|
645
|
+
const remoteCommand = [
|
|
646
|
+
"mkdir -p ~/.rudder/byoc",
|
|
647
|
+
`nohup sh -lc ${shellQuote(nonInteractiveDockerCommand(bootstrapCommand))} > ~/.rudder/byoc/worker.log 2>&1 < /dev/null &`,
|
|
648
|
+
].join(" && ");
|
|
649
|
+
await runCommand("ssh", [
|
|
650
|
+
"-o",
|
|
651
|
+
"BatchMode=yes",
|
|
652
|
+
"-o",
|
|
653
|
+
"ConnectTimeout=10",
|
|
654
|
+
host,
|
|
655
|
+
remoteCommand,
|
|
656
|
+
]);
|
|
657
|
+
}
|
|
658
|
+
function nonInteractiveDockerCommand(command) {
|
|
659
|
+
return command
|
|
660
|
+
.replace(/\bdocker run --rm -it\b/g, "docker run --rm")
|
|
661
|
+
.replace(/\bdocker run --rm -i -t\b/g, "docker run --rm")
|
|
662
|
+
.replace(/\bdocker run --rm -t -i\b/g, "docker run --rm");
|
|
383
663
|
}
|
|
384
664
|
async function cloudClient(options) {
|
|
385
665
|
const baseUrl = normalizeCloudUrl(process.env.RUDDER_CLOUD_URL);
|
|
@@ -439,6 +719,39 @@ function normalizeCloudUrl(raw) {
|
|
|
439
719
|
throw new Error("RUDDER_CLOUD_URL must be a valid http(s) URL.");
|
|
440
720
|
}
|
|
441
721
|
}
|
|
722
|
+
async function selectedCloudRuntime(explicit) {
|
|
723
|
+
if (explicit) {
|
|
724
|
+
return explicit;
|
|
725
|
+
}
|
|
726
|
+
const envRuntime = envCloudRuntime();
|
|
727
|
+
if (envRuntime) {
|
|
728
|
+
return envRuntime;
|
|
729
|
+
}
|
|
730
|
+
const baseUrl = normalizeCloudUrl(process.env.RUDDER_CLOUD_URL);
|
|
731
|
+
const state = await loadCloudAuth();
|
|
732
|
+
const savedRuntime = parseCloudRuntime(state?.defaultRuntime);
|
|
733
|
+
return state?.cloudUrl === baseUrl && savedRuntime ? savedRuntime : "fly";
|
|
734
|
+
}
|
|
735
|
+
function parseCloudRuntime(raw) {
|
|
736
|
+
const value = raw?.trim().toLowerCase();
|
|
737
|
+
if (!value) {
|
|
738
|
+
return undefined;
|
|
739
|
+
}
|
|
740
|
+
if (value === "fly" || value === "fly-machine" || value === "fly-machines") {
|
|
741
|
+
return "fly";
|
|
742
|
+
}
|
|
743
|
+
if (value === "byo" || value === "byoc" || value === "byo-vm" || value === "manual" || value === "self-hosted" || value === "vm") {
|
|
744
|
+
return "byo-vm";
|
|
745
|
+
}
|
|
746
|
+
return undefined;
|
|
747
|
+
}
|
|
748
|
+
function envCloudRuntime() {
|
|
749
|
+
const runtime = parseCloudRuntime(process.env.RUDDER_CLOUD_RUNTIME);
|
|
750
|
+
if (process.env.RUDDER_CLOUD_RUNTIME?.trim() && !runtime) {
|
|
751
|
+
throw new Error("RUDDER_CLOUD_RUNTIME must be `fly`, `byoc`, or `byo-vm`.");
|
|
752
|
+
}
|
|
753
|
+
return runtime;
|
|
754
|
+
}
|
|
442
755
|
async function pollLogin(client, pollPath, deviceCode) {
|
|
443
756
|
if (pollPath.startsWith("http://") || pollPath.startsWith("https://") || !deviceCode) {
|
|
444
757
|
return await client.request(pollPath, { method: "GET" });
|
|
@@ -636,7 +949,7 @@ function responseErrorMessage(value) {
|
|
|
636
949
|
? record.message
|
|
637
950
|
: undefined;
|
|
638
951
|
}
|
|
639
|
-
function printResult(result, options) {
|
|
952
|
+
async function printResult(result, options) {
|
|
640
953
|
if (options.json) {
|
|
641
954
|
printJson(result);
|
|
642
955
|
return;
|
|
@@ -647,21 +960,41 @@ function printResult(result, options) {
|
|
|
647
960
|
}
|
|
648
961
|
if (result && typeof result === "object" && !Array.isArray(result)) {
|
|
649
962
|
const record = result;
|
|
963
|
+
if (typeof record.bootstrapCommand === "string") {
|
|
964
|
+
const id = typeof record.id === "string" ? record.id : "BYOC sail";
|
|
965
|
+
const status = typeof record.status === "string" ? record.status : undefined;
|
|
966
|
+
const state = await loadCloudAuth();
|
|
967
|
+
const host = options.sshHost ?? state?.byocSshHost;
|
|
968
|
+
console.log(`${id}${status ? ` (${status})` : ""} is ready for BYOC.`);
|
|
969
|
+
if (host && process.env.RUDDER_BYOC_AUTOSTART !== "0") {
|
|
970
|
+
try {
|
|
971
|
+
await startByocWorkerOverSsh(host, record.bootstrapCommand);
|
|
972
|
+
console.log(`Started BYOC worker over SSH on ${host}.`);
|
|
973
|
+
console.log(`Remote log: ssh ${host} 'tail -f ~/.rudder/byoc/worker.log'`);
|
|
974
|
+
}
|
|
975
|
+
catch (error) {
|
|
976
|
+
console.log(`Could not start BYOC worker over SSH on ${host}: ${error instanceof Error ? error.message : String(error)}`);
|
|
977
|
+
console.log("Run this manually on your workstation/server:");
|
|
978
|
+
console.log(record.bootstrapCommand);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
else {
|
|
982
|
+
console.log("Run this on your workstation/server:");
|
|
983
|
+
console.log(record.bootstrapCommand);
|
|
984
|
+
if (!host) {
|
|
985
|
+
console.log("\nTip: run `rudder cloud setup-byoc <ssh-host>` to have Rudder start this over SSH next time.");
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
if (typeof record.updatedAt === "string") {
|
|
989
|
+
console.log(`\nIf the command expires, run: rudder cloud bootstrap ${id}`);
|
|
990
|
+
}
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
650
993
|
const sails = record.sails ?? record.items;
|
|
651
994
|
if (Array.isArray(sails)) {
|
|
652
995
|
printSailList(sails);
|
|
653
996
|
return;
|
|
654
997
|
}
|
|
655
|
-
if (typeof record.id === "string" && typeof record.status === "string") {
|
|
656
|
-
const parts = [
|
|
657
|
-
"cloud",
|
|
658
|
-
record.id,
|
|
659
|
-
record.status,
|
|
660
|
-
typeof record.task === "string" && record.task ? record.task : undefined,
|
|
661
|
-
].filter(Boolean);
|
|
662
|
-
console.log(parts.join(" "));
|
|
663
|
-
return;
|
|
664
|
-
}
|
|
665
998
|
}
|
|
666
999
|
console.log(JSON.stringify(result, null, 2));
|
|
667
1000
|
}
|
|
@@ -679,6 +1012,7 @@ function printSailList(items) {
|
|
|
679
1012
|
console.log([
|
|
680
1013
|
sail.id,
|
|
681
1014
|
sail.status,
|
|
1015
|
+
sail.runtime,
|
|
682
1016
|
typeof sail.task === "string" && sail.task ? sail.task : undefined,
|
|
683
1017
|
typeof sail.repoName === "string" && sail.repoName ? sail.repoName : undefined,
|
|
684
1018
|
sail.branch,
|
|
@@ -699,11 +1033,16 @@ function printCloudHelp() {
|
|
|
699
1033
|
Usage:
|
|
700
1034
|
rudder cloud login
|
|
701
1035
|
rudder cloud help
|
|
702
|
-
rudder cloud [name]
|
|
703
|
-
rudder cloud list
|
|
1036
|
+
rudder cloud [name or task]
|
|
704
1037
|
rudder cloud launch [--home-path <path>] ["task"]
|
|
1038
|
+
rudder cloud byoc ["task"]
|
|
1039
|
+
rudder cloud list
|
|
705
1040
|
rudder cloud onload <runId>
|
|
706
|
-
rudder
|
|
1041
|
+
rudder cloud bootstrap <id>
|
|
1042
|
+
rudder cloud runtime [fly|byoc]
|
|
1043
|
+
rudder cloud setup-byoc <ssh-host>
|
|
1044
|
+
rudder cloud setup-fly
|
|
1045
|
+
rudder sail [name or task]
|
|
707
1046
|
rudder sail list
|
|
708
1047
|
rudder sail pause <id>
|
|
709
1048
|
rudder sail resume <id>
|
|
@@ -712,6 +1051,7 @@ Usage:
|
|
|
712
1051
|
|
|
713
1052
|
Environment:
|
|
714
1053
|
RUDDER_CLOUD_URL Cloud control plane URL (defaults to ${DEFAULT_CLOUD_URL})
|
|
1054
|
+
RUDDER_CLOUD_RUNTIME fly, byoc, or byo-vm (overrides saved local default)
|
|
715
1055
|
RUDDER_CLOUD_HOME_PATHS Extra comma-separated HOME paths to include in snapshots
|
|
716
1056
|
RUDDER_GITHUB_CLIENT_ID GitHub App OAuth client ID for setup-github
|
|
717
1057
|
RUDDER_GITHUB_CLIENT_SECRET GitHub App OAuth client secret for setup-github
|