@milaboratories/pl-deployments 2.15.17 → 2.15.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_virtual/_rolldown/runtime.cjs +29 -0
- package/dist/common/os_and_arch.cjs +22 -28
- package/dist/common/os_and_arch.cjs.map +1 -1
- package/dist/common/os_and_arch.js +23 -26
- package/dist/common/os_and_arch.js.map +1 -1
- package/dist/common/pl_binary.cjs +35 -33
- package/dist/common/pl_binary.cjs.map +1 -1
- package/dist/common/pl_binary.d.ts +13 -18
- package/dist/common/pl_binary.js +33 -31
- package/dist/common/pl_binary.js.map +1 -1
- package/dist/common/pl_binary_download.cjs +143 -155
- package/dist/common/pl_binary_download.cjs.map +1 -1
- package/dist/common/pl_binary_download.d.ts +15 -48
- package/dist/common/pl_binary_download.js +138 -133
- package/dist/common/pl_binary_download.js.map +1 -1
- package/dist/common/pl_version.cjs +5 -6
- package/dist/common/pl_version.cjs.map +1 -1
- package/dist/common/pl_version.d.ts +4 -1
- package/dist/common/pl_version.js +5 -4
- package/dist/common/pl_version.js.map +1 -1
- package/dist/index.cjs +21 -25
- package/dist/index.d.ts +6 -6
- package/dist/index.js +7 -6
- package/dist/local/pid.cjs +14 -13
- package/dist/local/pid.cjs.map +1 -1
- package/dist/local/pid.js +11 -11
- package/dist/local/pid.js.map +1 -1
- package/dist/local/pl.cjs +194 -223
- package/dist/local/pl.cjs.map +1 -1
- package/dist/local/pl.d.ts +65 -65
- package/dist/local/pl.js +190 -202
- package/dist/local/pl.js.map +1 -1
- package/dist/local/process.cjs +37 -59
- package/dist/local/process.cjs.map +1 -1
- package/dist/local/process.d.ts +10 -10
- package/dist/local/process.js +36 -57
- package/dist/local/process.js.map +1 -1
- package/dist/local/trace.cjs +14 -17
- package/dist/local/trace.cjs.map +1 -1
- package/dist/local/trace.d.ts +6 -7
- package/dist/local/trace.js +15 -15
- package/dist/local/trace.js.map +1 -1
- package/dist/package.cjs +12 -0
- package/dist/package.cjs.map +1 -0
- package/dist/package.js +6 -0
- package/dist/package.js.map +1 -0
- package/dist/ssh/connection_info.cjs +36 -53
- package/dist/ssh/connection_info.cjs.map +1 -1
- package/dist/ssh/connection_info.d.ts +691 -713
- package/dist/ssh/connection_info.js +35 -51
- package/dist/ssh/connection_info.js.map +1 -1
- package/dist/ssh/pl.cjs +551 -638
- package/dist/ssh/pl.cjs.map +1 -1
- package/dist/ssh/pl.d.ts +120 -117
- package/dist/ssh/pl.js +548 -636
- package/dist/ssh/pl.js.map +1 -1
- package/dist/ssh/pl_paths.cjs +22 -24
- package/dist/ssh/pl_paths.cjs.map +1 -1
- package/dist/ssh/pl_paths.js +21 -19
- package/dist/ssh/pl_paths.js.map +1 -1
- package/dist/ssh/ssh.cjs +554 -618
- package/dist/ssh/ssh.cjs.map +1 -1
- package/dist/ssh/ssh.d.ts +139 -136
- package/dist/ssh/ssh.js +548 -616
- package/dist/ssh/ssh.js.map +1 -1
- package/dist/ssh/ssh_errors.cjs +45 -60
- package/dist/ssh/ssh_errors.cjs.map +1 -1
- package/dist/ssh/ssh_errors.js +45 -58
- package/dist/ssh/ssh_errors.js.map +1 -1
- package/dist/ssh/supervisord.cjs +50 -68
- package/dist/ssh/supervisord.cjs.map +1 -1
- package/dist/ssh/supervisord.d.ts +11 -21
- package/dist/ssh/supervisord.js +50 -66
- package/dist/ssh/supervisord.js.map +1 -1
- package/package.json +10 -10
- package/dist/common/os_and_arch.d.ts +0 -9
- package/dist/common/os_and_arch.d.ts.map +0 -1
- package/dist/common/pl_binary.d.ts.map +0 -1
- package/dist/common/pl_binary_download.d.ts.map +0 -1
- package/dist/common/pl_version.d.ts.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/local/options.d.ts +0 -31
- package/dist/local/options.d.ts.map +0 -1
- package/dist/local/pid.d.ts +0 -4
- package/dist/local/pid.d.ts.map +0 -1
- package/dist/local/pl.d.ts.map +0 -1
- package/dist/local/process.d.ts.map +0 -1
- package/dist/local/trace.d.ts.map +0 -1
- package/dist/package.json.cjs +0 -8
- package/dist/package.json.cjs.map +0 -1
- package/dist/package.json.js +0 -6
- package/dist/package.json.js.map +0 -1
- package/dist/ssh/__tests__/common-utils.d.ts +0 -12
- package/dist/ssh/__tests__/common-utils.d.ts.map +0 -1
- package/dist/ssh/connection_info.d.ts.map +0 -1
- package/dist/ssh/pl.d.ts.map +0 -1
- package/dist/ssh/pl_paths.d.ts +0 -20
- package/dist/ssh/pl_paths.d.ts.map +0 -1
- package/dist/ssh/ssh.d.ts.map +0 -1
- package/dist/ssh/ssh_errors.d.ts +0 -29
- package/dist/ssh/ssh_errors.d.ts.map +0 -1
- package/dist/ssh/supervisord.d.ts.map +0 -1
package/dist/ssh/pl.cjs
CHANGED
|
@@ -1,650 +1,563 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
|
|
2
|
+
const require_pl_binary_download = require('../common/pl_binary_download.cjs');
|
|
3
|
+
const require_pl_version = require('../common/pl_version.cjs');
|
|
4
|
+
const require_ssh = require('./ssh.cjs');
|
|
5
|
+
const require_pl_paths = require('./pl_paths.cjs');
|
|
6
|
+
const require_supervisord = require('./supervisord.cjs');
|
|
7
|
+
const require_connection_info = require('./connection_info.cjs');
|
|
8
|
+
let _milaboratories_ts_helpers = require("@milaboratories/ts-helpers");
|
|
9
|
+
let upath = require("upath");
|
|
10
|
+
upath = require_runtime.__toESM(upath);
|
|
11
|
+
let _milaboratories_pl_http = require("@milaboratories/pl-http");
|
|
12
|
+
let node_net = require("node:net");
|
|
13
|
+
node_net = require_runtime.__toESM(node_net);
|
|
14
|
+
let _milaboratories_pl_config = require("@milaboratories/pl-config");
|
|
14
15
|
|
|
16
|
+
//#region src/ssh/pl.ts
|
|
15
17
|
const minRequiredGlibcVersion = 2.28;
|
|
16
|
-
class SshPl {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
return false;
|
|
531
|
-
}
|
|
532
|
-
async checkIsAliveWithInterval(shouldUseMinio, interval = 1000, count = 15, shouldStart = true) {
|
|
533
|
-
const maxMs = count * interval;
|
|
534
|
-
let total = 0;
|
|
535
|
-
let alive = await this.isAlive();
|
|
536
|
-
while (shouldStart ? !supervisord.isAllAlive(alive, shouldUseMinio) : supervisord.isAllAlive(alive, shouldUseMinio)) {
|
|
537
|
-
await tsHelpers.sleep(interval);
|
|
538
|
-
total += interval;
|
|
539
|
-
if (total > maxMs) {
|
|
540
|
-
throw new Error(`isAliveWithInterval: The process did not ${shouldStart ? "started" : "stopped"} after ${maxMs} ms. Live status: ${JSON.stringify(alive)}`);
|
|
541
|
-
}
|
|
542
|
-
alive = await this.isAlive();
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
async readExistedConfig(remoteHome) {
|
|
546
|
-
const connectionInfo = await this.sshClient.readFile(pl_paths.connectionInfo(remoteHome));
|
|
547
|
-
return connection_info.parseConnectionInfo(connectionInfo);
|
|
548
|
-
}
|
|
549
|
-
async fetchPorts(remoteHome, arch) {
|
|
550
|
-
const ports = {
|
|
551
|
-
grpc: {
|
|
552
|
-
local: await plConfig.getFreePort(),
|
|
553
|
-
remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),
|
|
554
|
-
},
|
|
555
|
-
monitoring: {
|
|
556
|
-
local: await plConfig.getFreePort(),
|
|
557
|
-
remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),
|
|
558
|
-
},
|
|
559
|
-
debug: {
|
|
560
|
-
local: await plConfig.getFreePort(),
|
|
561
|
-
remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),
|
|
562
|
-
},
|
|
563
|
-
http: {
|
|
564
|
-
local: await plConfig.getFreePort(),
|
|
565
|
-
remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),
|
|
566
|
-
},
|
|
567
|
-
minioPort: {
|
|
568
|
-
local: await plConfig.getFreePort(),
|
|
569
|
-
remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),
|
|
570
|
-
},
|
|
571
|
-
minioConsolePort: {
|
|
572
|
-
local: await plConfig.getFreePort(),
|
|
573
|
-
remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),
|
|
574
|
-
},
|
|
575
|
-
};
|
|
576
|
-
return ports;
|
|
577
|
-
}
|
|
578
|
-
async getLocalFreePort() {
|
|
579
|
-
return new Promise((res) => {
|
|
580
|
-
const srv = net.createServer();
|
|
581
|
-
srv.listen(0, () => {
|
|
582
|
-
const port = srv.address().port;
|
|
583
|
-
srv.close((_) => res(port));
|
|
584
|
-
});
|
|
585
|
-
});
|
|
586
|
-
}
|
|
587
|
-
async getFreePortForPlatformaOnServer(remoteHome, arch) {
|
|
588
|
-
const freePortBin = pl_paths.platformaFreePortBin(remoteHome, arch.arch);
|
|
589
|
-
const { stdout, stderr } = await this.sshClient.exec(`${freePortBin}`);
|
|
590
|
-
if (stderr) {
|
|
591
|
-
throw new Error(`getFreePortForPlatformaOnServer: stderr is not empty: ${stderr}, stdout: ${stdout}`);
|
|
592
|
-
}
|
|
593
|
-
return +stdout;
|
|
594
|
-
}
|
|
595
|
-
async getArch() {
|
|
596
|
-
const { stdout, stderr } = await this.sshClient.exec("uname -s && uname -m");
|
|
597
|
-
if (stderr)
|
|
598
|
-
throw new Error(`getArch: stderr is not empty: ${stderr}, stdout: ${stdout}`);
|
|
599
|
-
const arr = stdout.split("\n");
|
|
600
|
-
return {
|
|
601
|
-
platform: arr[0],
|
|
602
|
-
arch: arr[1],
|
|
603
|
-
};
|
|
604
|
-
}
|
|
605
|
-
async getUserHomeDirectory() {
|
|
606
|
-
const { stdout, stderr } = await this.sshClient.exec("echo $HOME");
|
|
607
|
-
if (stderr) {
|
|
608
|
-
const home = `/home/${this.username}`;
|
|
609
|
-
console.warn(`getUserHomeDirectory: stderr is not empty: ${stderr}, stdout: ${stdout}, will get a default home: ${home}`);
|
|
610
|
-
return home;
|
|
611
|
-
}
|
|
612
|
-
return stdout.trim();
|
|
613
|
-
}
|
|
614
|
-
}
|
|
18
|
+
var SshPl = class SshPl {
|
|
19
|
+
initState = { step: "init" };
|
|
20
|
+
constructor(logger, sshClient, username) {
|
|
21
|
+
this.logger = logger;
|
|
22
|
+
this.sshClient = sshClient;
|
|
23
|
+
this.username = username;
|
|
24
|
+
}
|
|
25
|
+
info() {
|
|
26
|
+
return {
|
|
27
|
+
username: this.username,
|
|
28
|
+
initState: this.initState
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
static async init(logger, config) {
|
|
32
|
+
try {
|
|
33
|
+
return new SshPl(logger, await require_ssh.SshClient.init(logger, config), (0, _milaboratories_ts_helpers.notEmpty)(config.username));
|
|
34
|
+
} catch (e) {
|
|
35
|
+
logger.error(`Connection error in SshClient.init: ${e}`);
|
|
36
|
+
throw e;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
cleanUp() {
|
|
40
|
+
this.sshClient.close();
|
|
41
|
+
}
|
|
42
|
+
/** Provides an info if the platforma and minio are running along with the debug info. */
|
|
43
|
+
async isAlive() {
|
|
44
|
+
const arch = await this.getArch();
|
|
45
|
+
const remoteHome = await this.getUserHomeDirectory();
|
|
46
|
+
return await require_supervisord.supervisorStatus(this.logger, this.sshClient, remoteHome, arch.arch);
|
|
47
|
+
}
|
|
48
|
+
/** Starts all the services on the server.
|
|
49
|
+
* Idempotent semantic: we could call it several times. */
|
|
50
|
+
async start(shouldUseMinio) {
|
|
51
|
+
const arch = await this.getArch();
|
|
52
|
+
const remoteHome = await this.getUserHomeDirectory();
|
|
53
|
+
try {
|
|
54
|
+
if (!require_supervisord.isAllAlive(await this.isAlive(), shouldUseMinio)) {
|
|
55
|
+
await require_supervisord.supervisorCtlStart(this.sshClient, remoteHome, arch.arch);
|
|
56
|
+
return await this.checkIsAliveWithInterval(shouldUseMinio);
|
|
57
|
+
}
|
|
58
|
+
} catch (e) {
|
|
59
|
+
let msg = `SshPl.start: ${e}`;
|
|
60
|
+
let logs = "";
|
|
61
|
+
try {
|
|
62
|
+
logs = await this.sshClient.readFile(require_pl_paths.platformaCliLogs(remoteHome));
|
|
63
|
+
msg += `, platforma cli logs: ${logs}`;
|
|
64
|
+
} catch (e) {
|
|
65
|
+
msg += `, Can not read platforma cli logs: ${e}`;
|
|
66
|
+
}
|
|
67
|
+
this.logger.error(msg);
|
|
68
|
+
throw new Error(msg);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** Stops all the services on the server.
|
|
72
|
+
* Idempotent semantic: we could call it several times. */
|
|
73
|
+
async stop() {
|
|
74
|
+
const arch = await this.getArch();
|
|
75
|
+
const remoteHome = await this.getUserHomeDirectory();
|
|
76
|
+
try {
|
|
77
|
+
const alive = await this.isAlive();
|
|
78
|
+
if (require_supervisord.isSupervisordRunning(alive)) {
|
|
79
|
+
await require_supervisord.supervisorStop(this.sshClient, remoteHome, arch.arch);
|
|
80
|
+
const shouldUseMinio = alive.minio === true;
|
|
81
|
+
return await this.checkIsAliveWithInterval(shouldUseMinio, 1e3, 15, false);
|
|
82
|
+
}
|
|
83
|
+
} catch (e) {
|
|
84
|
+
const msg = `PlSsh.stop: ${e}`;
|
|
85
|
+
this.logger.error(msg);
|
|
86
|
+
throw new Error(msg);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** Stops the services, deletes a directory with the state and closes SSH connection. */
|
|
90
|
+
async reset() {
|
|
91
|
+
await this.stopAndClean();
|
|
92
|
+
this.cleanUp();
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
/** Stops platforma and deletes its state. */
|
|
96
|
+
async stopAndClean() {
|
|
97
|
+
const remoteHome = await this.getUserHomeDirectory();
|
|
98
|
+
this.logger.info(`pl.reset: Stop Platforma on the server`);
|
|
99
|
+
await this.stop();
|
|
100
|
+
this.logger.info(`pl.reset: Deleting Platforma workDir ${require_pl_paths.workDir(remoteHome)} on the server`);
|
|
101
|
+
await this.sshClient.deleteFolder(require_pl_paths.workDir(remoteHome));
|
|
102
|
+
}
|
|
103
|
+
/** Downloads binaries and untar them on the server,
|
|
104
|
+
* generates all the configs, creates necessary dirs,
|
|
105
|
+
* and finally starts all the services. */
|
|
106
|
+
async platformaInit(options) {
|
|
107
|
+
const state = {
|
|
108
|
+
localWorkdir: options.localWorkdir,
|
|
109
|
+
step: "init"
|
|
110
|
+
};
|
|
111
|
+
const { onProgress } = options;
|
|
112
|
+
const ops = {
|
|
113
|
+
...defaultSshPlConfig,
|
|
114
|
+
...options
|
|
115
|
+
};
|
|
116
|
+
state.plBinaryOps = ops.plBinary;
|
|
117
|
+
try {
|
|
118
|
+
await this.doStepDetectArch(state, onProgress);
|
|
119
|
+
await this.doStepDetectHome(state, onProgress);
|
|
120
|
+
if (!await this.doStepReadExistedConfig(state, ops, onProgress)) {
|
|
121
|
+
await onProgress?.("Platforma is already running. Skipping initialization.");
|
|
122
|
+
return state.existedSettings;
|
|
123
|
+
}
|
|
124
|
+
await this.doStepStopExistedPlatforma(state, onProgress);
|
|
125
|
+
await this.doStepCheckDbLock(state, onProgress);
|
|
126
|
+
await onProgress?.("Installation platforma...");
|
|
127
|
+
await this.doStepDownloadBinaries(state, onProgress, ops);
|
|
128
|
+
await this.doStepFetchPorts(state);
|
|
129
|
+
await this.doStepGenerateNewConfig(state, onProgress, ops);
|
|
130
|
+
await this.doStepCreateFoldersAndSaveFiles(state, onProgress);
|
|
131
|
+
await this.doStepConfigureSupervisord(state, onProgress);
|
|
132
|
+
await this.doStepSaveNewConnectionInfo(state, onProgress, ops);
|
|
133
|
+
await this.doStepStartPlatforma(state, onProgress);
|
|
134
|
+
return state.connectionInfo;
|
|
135
|
+
} catch (e) {
|
|
136
|
+
const msg = `SshPl.platformaInit: ${e}, state: ${JSON.stringify(this.removeSensitiveData(state))}`;
|
|
137
|
+
this.logger.error(msg);
|
|
138
|
+
throw new Error(msg);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async doStepStopExistedPlatforma(state, onProgress) {
|
|
142
|
+
state.step = "stopExistedPlatforma";
|
|
143
|
+
if (!require_supervisord.isAllAlive(state.alive, state.shouldUseMinio ?? false)) return;
|
|
144
|
+
await onProgress?.("Stopping services...");
|
|
145
|
+
await this.stop();
|
|
146
|
+
}
|
|
147
|
+
removeSensitiveData(state) {
|
|
148
|
+
const stateCopy = { ...state };
|
|
149
|
+
stateCopy.generatedConfig = {
|
|
150
|
+
...stateCopy.generatedConfig,
|
|
151
|
+
filesToCreate: { skipped: "sanitized" }
|
|
152
|
+
};
|
|
153
|
+
return stateCopy;
|
|
154
|
+
}
|
|
155
|
+
async doStepStartPlatforma(state, onProgress) {
|
|
156
|
+
state.step = "startPlatforma";
|
|
157
|
+
await onProgress?.("Starting Platforma on the server...");
|
|
158
|
+
await this.start(state.shouldUseMinio ?? false);
|
|
159
|
+
state.started = true;
|
|
160
|
+
this.initState = state;
|
|
161
|
+
await onProgress?.("Platforma has been started successfully.");
|
|
162
|
+
}
|
|
163
|
+
async doStepSaveNewConnectionInfo(state, onProgress, ops) {
|
|
164
|
+
state.step = "saveNewConnectionInfo";
|
|
165
|
+
const config = state.generatedConfig;
|
|
166
|
+
await onProgress?.("Saving connection information...");
|
|
167
|
+
state.connectionInfo = require_connection_info.newConnectionInfo(config.plUser, config.plPassword, state.ports, (0, _milaboratories_ts_helpers.notEmpty)(ops.useGlobalAccess), ops.plBinary.version, state.shouldUseMinio ?? false);
|
|
168
|
+
await this.sshClient.writeFileOnTheServer(require_pl_paths.connectionInfo(state.remoteHome), require_connection_info.stringifyConnectionInfo(state.connectionInfo));
|
|
169
|
+
await onProgress?.("Connection information saved.");
|
|
170
|
+
}
|
|
171
|
+
async doStepCheckDbLock(state, onProgress) {
|
|
172
|
+
const removeLockFile = async (lockFilePath) => {
|
|
173
|
+
try {
|
|
174
|
+
await this.sshClient.exec(`rm -f ${lockFilePath}`);
|
|
175
|
+
this.logger.info(`Removed stale lock file ${lockFilePath}`);
|
|
176
|
+
} catch (e) {
|
|
177
|
+
const msg = `Failed to remove stale lock file ${lockFilePath}: ${e}`;
|
|
178
|
+
this.logger.error(msg);
|
|
179
|
+
throw new Error(msg);
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
state.step = "checkDbLock";
|
|
183
|
+
await onProgress?.("Checking for DB lock...");
|
|
184
|
+
const lockFilePath = require_pl_paths.platformaDbLock(state.remoteHome);
|
|
185
|
+
if (!await this.sshClient.checkFileExists(lockFilePath)) {
|
|
186
|
+
await onProgress?.("No DB lock found. Proceeding...");
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
this.logger.info(`DB lock file found at ${lockFilePath}. Checking which process holds it...`);
|
|
190
|
+
const lockProcessInfo = await this.findLockHolder(lockFilePath);
|
|
191
|
+
if (!lockProcessInfo) {
|
|
192
|
+
this.logger.warn("Lock file exists but no process is holding it. Removing stale lock file...");
|
|
193
|
+
await removeLockFile(lockFilePath);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
this.logger.info(`Found process ${lockProcessInfo.pid} (user: ${lockProcessInfo.user}) holding DB lock`);
|
|
197
|
+
if (lockProcessInfo.user !== this.username) {
|
|
198
|
+
const msg = `DB lock is held by process ${lockProcessInfo.pid} owned by user '${lockProcessInfo.user}', but current user is '${this.username}'. Cannot kill process owned by different user.`;
|
|
199
|
+
this.logger.error(msg);
|
|
200
|
+
throw new Error(msg);
|
|
201
|
+
}
|
|
202
|
+
this.logger.info(`Process ${lockProcessInfo.pid} belongs to current user ${this.username}. Killing it...`);
|
|
203
|
+
await this.killRemoteProcess(lockProcessInfo.pid);
|
|
204
|
+
this.logger.info("Process holding DB lock has been terminated.");
|
|
205
|
+
if (await this.sshClient.checkFileExists(lockFilePath)) await removeLockFile(lockFilePath);
|
|
206
|
+
}
|
|
207
|
+
async doStepConfigureSupervisord(state, onProgress) {
|
|
208
|
+
await onProgress?.("Writing supervisord configuration...");
|
|
209
|
+
state.step = "configureSupervisord";
|
|
210
|
+
const config = state.generatedConfig;
|
|
211
|
+
let supervisorConfig;
|
|
212
|
+
if (state.shouldUseMinio) supervisorConfig = require_supervisord.generateSupervisordConfigWithMinio(config.minioConfig.storageDir, config.minioConfig.envs, await this.getFreePortForPlatformaOnServer(state.remoteHome, state.arch), config.workingDir, config.plConfig.configPath, state.binPaths.minioRelPath, state.binPaths.downloadedPl);
|
|
213
|
+
else supervisorConfig = require_supervisord.generateSupervisordConfig(await this.getFreePortForPlatformaOnServer(state.remoteHome, state.arch), config.workingDir, config.plConfig.configPath, state.binPaths.downloadedPl);
|
|
214
|
+
if (!await this.sshClient.writeFileOnTheServer(require_pl_paths.supervisorConf(state.remoteHome), supervisorConfig)) throw new Error(`Can not write supervisord config on the server ${require_pl_paths.workDir(state.remoteHome)}`);
|
|
215
|
+
await onProgress?.("Supervisord configuration written.");
|
|
216
|
+
}
|
|
217
|
+
async doStepCreateFoldersAndSaveFiles(state, onProgress) {
|
|
218
|
+
state.step = "createFoldersAndSaveFiles";
|
|
219
|
+
const config = state.generatedConfig;
|
|
220
|
+
await onProgress?.("Generating folder structure...");
|
|
221
|
+
for (const [filePath, content] of Object.entries(config.filesToCreate)) {
|
|
222
|
+
await this.sshClient.writeFileOnTheServer(filePath, content);
|
|
223
|
+
this.logger.info(`Created file ${filePath}`);
|
|
224
|
+
}
|
|
225
|
+
for (const dir of config.dirsToCreate) {
|
|
226
|
+
await this.sshClient.ensureRemoteDirCreated(dir);
|
|
227
|
+
this.logger.info(`Created directory ${dir}`);
|
|
228
|
+
}
|
|
229
|
+
await onProgress?.("Folder structure created.");
|
|
230
|
+
}
|
|
231
|
+
async doStepGenerateNewConfig(state, onProgress, ops) {
|
|
232
|
+
state.step = "generateNewConfig";
|
|
233
|
+
await onProgress?.("Generating new config...");
|
|
234
|
+
state.generatedConfig = { ...await (0, _milaboratories_pl_config.generateSshPlConfigs)({
|
|
235
|
+
logger: this.logger,
|
|
236
|
+
workingDir: require_pl_paths.workDir(state.remoteHome),
|
|
237
|
+
portsMode: {
|
|
238
|
+
type: "customWithMinio",
|
|
239
|
+
ports: {
|
|
240
|
+
debug: state.ports.debug.remote,
|
|
241
|
+
grpc: state.ports.grpc.remote,
|
|
242
|
+
http: state.ports.http.remote,
|
|
243
|
+
minio: state.ports.minioPort.remote,
|
|
244
|
+
minioConsole: state.ports.minioConsolePort.remote,
|
|
245
|
+
monitoring: state.ports.monitoring.remote,
|
|
246
|
+
httpLocal: state.ports.http.local,
|
|
247
|
+
grpcLocal: state.ports.grpc.local,
|
|
248
|
+
minioLocal: state.ports.minioPort.local
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
licenseMode: ops.license,
|
|
252
|
+
useGlobalAccess: (0, _milaboratories_ts_helpers.notEmpty)(ops.useGlobalAccess),
|
|
253
|
+
plConfigPostprocessing: ops.plConfigPostprocessing,
|
|
254
|
+
useMinio: state.shouldUseMinio ?? false
|
|
255
|
+
}) };
|
|
256
|
+
await onProgress?.("New config generated");
|
|
257
|
+
}
|
|
258
|
+
async doStepFetchPorts(state) {
|
|
259
|
+
state.step = "fetchPorts";
|
|
260
|
+
state.ports = await this.fetchPorts(state.remoteHome, state.arch);
|
|
261
|
+
if (!state.ports.debug.remote || !state.ports.grpc.remote || !state.ports.minioPort.remote || !state.ports.minioConsolePort.remote || !state.ports.monitoring.remote || !state.ports.http?.remote) throw new Error(`SshPl.platformaInit: remote ports are not defined`);
|
|
262
|
+
}
|
|
263
|
+
async doStepDownloadBinaries(state, onProgress, ops) {
|
|
264
|
+
state.step = "downloadBinaries";
|
|
265
|
+
await onProgress?.("Downloading and uploading required binaries...");
|
|
266
|
+
const glibcVersion = await getGlibcVersion(this.logger, this.sshClient);
|
|
267
|
+
if (glibcVersion < minRequiredGlibcVersion) throw new Error(`glibc version ${glibcVersion} is too old. Version ${minRequiredGlibcVersion} or higher is required for Platforma.`);
|
|
268
|
+
const downloadRes = await this.downloadBinariesAndUploadToTheServer(ops.localWorkdir, ops.plBinary, state.remoteHome, state.arch, state.shouldUseMinio ?? false, ops.proxy);
|
|
269
|
+
await onProgress?.("All required binaries have been downloaded and uploaded.");
|
|
270
|
+
state.binPaths = {
|
|
271
|
+
...downloadRes,
|
|
272
|
+
history: void 0
|
|
273
|
+
};
|
|
274
|
+
state.downloadedBinaries = downloadRes.history;
|
|
275
|
+
}
|
|
276
|
+
async doStepDetectArch(state, onProgress) {
|
|
277
|
+
state.step = "detectArch";
|
|
278
|
+
await onProgress?.("Detecting server architecture...");
|
|
279
|
+
state.arch = await this.getArch();
|
|
280
|
+
await onProgress?.("Server architecture detected.");
|
|
281
|
+
}
|
|
282
|
+
async doStepDetectHome(state, onProgress) {
|
|
283
|
+
state.step = "detectHome";
|
|
284
|
+
await onProgress?.("Fetching user home directory...");
|
|
285
|
+
state.remoteHome = await this.getUserHomeDirectory();
|
|
286
|
+
await onProgress?.("User home directory retrieved.");
|
|
287
|
+
}
|
|
288
|
+
async doStepReadExistedConfig(state, ops, onProgress) {
|
|
289
|
+
state.step = "checkAlive";
|
|
290
|
+
await onProgress?.("Checking platform status...");
|
|
291
|
+
state.alive = await this.isAlive();
|
|
292
|
+
if (!state.alive?.platforma) return true;
|
|
293
|
+
await onProgress?.("All required services are running.");
|
|
294
|
+
state.existedSettings = await this.readExistedConfig(state.remoteHome);
|
|
295
|
+
if (!state.existedSettings) throw new Error(`SshPl.platformaInit: platforma is alive but existed settings are not found`);
|
|
296
|
+
const sameGA = state.existedSettings.useGlobalAccess == ops.useGlobalAccess;
|
|
297
|
+
const samePlVersion = state.existedSettings.plVersion == ops.plBinary.version;
|
|
298
|
+
state.needRestart = !(sameGA && samePlVersion);
|
|
299
|
+
this.logger.info(`SshPl.platformaInit: need restart? ${state.needRestart}`);
|
|
300
|
+
state.shouldUseMinio = state.existedSettings.minioIsUsed;
|
|
301
|
+
if (state.shouldUseMinio) this.logger.info(`SshPl.platformaInit: minio is used`);
|
|
302
|
+
else this.logger.info(`SshPl.platformaInit: minio is not used`);
|
|
303
|
+
if (!state.needRestart) {
|
|
304
|
+
await onProgress?.("Server setup completed.");
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
await onProgress?.("Stopping services...");
|
|
308
|
+
await this.stop();
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
async downloadBinariesAndUploadToTheServer(localWorkdir, plBinary, remoteHome, arch, shouldUseMinio, proxy) {
|
|
312
|
+
const state = [];
|
|
313
|
+
const dispatcher = (0, _milaboratories_pl_http.defaultHttpDispatcher)(proxy);
|
|
314
|
+
try {
|
|
315
|
+
const pl = await this.downloadAndUntar(localWorkdir, remoteHome, arch, "pl", `pl-${plBinary.version}`, dispatcher);
|
|
316
|
+
state.push(pl);
|
|
317
|
+
const supervisor = await this.downloadAndUntar(localWorkdir, remoteHome, arch, "supervisord", require_pl_paths.supervisordDirName, dispatcher);
|
|
318
|
+
state.push(supervisor);
|
|
319
|
+
const minioPath = require_pl_paths.minioBin(remoteHome, arch.arch);
|
|
320
|
+
if (shouldUseMinio) {
|
|
321
|
+
const minio = await this.downloadAndUntar(localWorkdir, remoteHome, arch, "minio", require_pl_paths.minioDirName, dispatcher);
|
|
322
|
+
state.push(minio);
|
|
323
|
+
await this.sshClient.chmod(minioPath, 488);
|
|
324
|
+
}
|
|
325
|
+
return {
|
|
326
|
+
history: state,
|
|
327
|
+
minioRelPath: shouldUseMinio ? minioPath : void 0,
|
|
328
|
+
downloadedPl: require_pl_paths.platformaBin(remoteHome, arch.arch)
|
|
329
|
+
};
|
|
330
|
+
} catch (e) {
|
|
331
|
+
const msg = `SshPl.downloadBinariesAndUploadToServer: ${e}, state: ${JSON.stringify(state)}`;
|
|
332
|
+
this.logger.error(msg);
|
|
333
|
+
throw e;
|
|
334
|
+
} finally {
|
|
335
|
+
await dispatcher.close();
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
async findLockHolderWithLsof(lockFilePath) {
|
|
339
|
+
try {
|
|
340
|
+
const { stdout } = await this.sshClient.exec(`lsof ${lockFilePath} 2>/dev/null || true`);
|
|
341
|
+
const output = stdout.trim();
|
|
342
|
+
if (!output) return null;
|
|
343
|
+
const lines = output.split("\n");
|
|
344
|
+
if (lines.length <= 1) return null;
|
|
345
|
+
const parts = lines[1].trim().split(/\s+/);
|
|
346
|
+
if (parts.length < 3) return null;
|
|
347
|
+
const pid = Number.parseInt(parts[1], 10);
|
|
348
|
+
const user = parts[2];
|
|
349
|
+
return Number.isNaN(pid) || !user ? null : {
|
|
350
|
+
pid,
|
|
351
|
+
user
|
|
352
|
+
};
|
|
353
|
+
} catch (e) {
|
|
354
|
+
this.logger.warn(`Failed to use lsof to check lock: ${e}`);
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
async findLockHolderWithFuser(lockFilePath) {
|
|
359
|
+
try {
|
|
360
|
+
const { stdout } = await this.sshClient.exec(`fuser ${lockFilePath} 2>/dev/null || true`);
|
|
361
|
+
const output = stdout.trim();
|
|
362
|
+
if (!output) return null;
|
|
363
|
+
const match = output.match(/: (\d+)/);
|
|
364
|
+
if (!match) return null;
|
|
365
|
+
const pid = Number.parseInt(match[1], 10);
|
|
366
|
+
if (Number.isNaN(pid)) return null;
|
|
367
|
+
try {
|
|
368
|
+
const user = (await this.sshClient.exec(`ps -o user= -p ${pid} 2>/dev/null || true`)).stdout.trim();
|
|
369
|
+
return user ? {
|
|
370
|
+
pid,
|
|
371
|
+
user
|
|
372
|
+
} : null;
|
|
373
|
+
} catch (e) {
|
|
374
|
+
this.logger.warn(`Failed to get user for PID ${pid}: ${e}`);
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
} catch (e) {
|
|
378
|
+
this.logger.warn(`Failed to use fuser to check lock: ${e}`);
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
async findLockHolder(lockFilePath) {
|
|
383
|
+
const viaLsof = await this.findLockHolderWithLsof(lockFilePath);
|
|
384
|
+
if (viaLsof) return viaLsof;
|
|
385
|
+
return this.findLockHolderWithFuser(lockFilePath);
|
|
386
|
+
}
|
|
387
|
+
async killRemoteProcess(pid) {
|
|
388
|
+
this.logger.info(`Killing process ${pid}...`);
|
|
389
|
+
try {
|
|
390
|
+
await this.sshClient.exec(`kill ${pid} 2>/dev/null || true`);
|
|
391
|
+
await (0, _milaboratories_ts_helpers.sleep)(1e3);
|
|
392
|
+
try {
|
|
393
|
+
await this.sshClient.exec(`kill -0 ${pid} 2>/dev/null`);
|
|
394
|
+
this.logger.warn(`Process ${pid} still alive after SIGTERM, forcing kill...`);
|
|
395
|
+
await this.sshClient.exec(`kill -9 ${pid} 2>/dev/null || true`);
|
|
396
|
+
await (0, _milaboratories_ts_helpers.sleep)(500);
|
|
397
|
+
} catch {}
|
|
398
|
+
} catch (e) {
|
|
399
|
+
const msg = `Failed to kill process ${pid}: ${e}`;
|
|
400
|
+
this.logger.error(msg);
|
|
401
|
+
throw new Error(msg);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
/** We have to extract pl in the remote server,
|
|
405
|
+
* because Windows doesn't support symlinks
|
|
406
|
+
* that are found in Linux pl binaries tgz archive.
|
|
407
|
+
* For this reason, we extract all to the remote server.
|
|
408
|
+
* It requires `tar` to be installed on the server
|
|
409
|
+
* (it's not installed for Rocky Linux for example). */
|
|
410
|
+
async downloadAndUntar(localWorkdir, remoteHome, arch, softwareName, tgzName, dispatcher) {
|
|
411
|
+
const state = {};
|
|
412
|
+
state.binBasePath = require_pl_paths.binariesDir(remoteHome);
|
|
413
|
+
await this.sshClient.ensureRemoteDirCreated(state.binBasePath);
|
|
414
|
+
state.binBasePathCreated = true;
|
|
415
|
+
let downloadBinaryResult = null;
|
|
416
|
+
const attempts = 5;
|
|
417
|
+
for (let i = 1; i <= attempts; i++) try {
|
|
418
|
+
downloadBinaryResult = await require_pl_binary_download.downloadBinaryNoExtract({
|
|
419
|
+
logger: this.logger,
|
|
420
|
+
baseDir: localWorkdir,
|
|
421
|
+
softwareName,
|
|
422
|
+
tgzName,
|
|
423
|
+
arch: arch.arch,
|
|
424
|
+
platform: arch.platform,
|
|
425
|
+
dispatcher
|
|
426
|
+
});
|
|
427
|
+
break;
|
|
428
|
+
} catch (e) {
|
|
429
|
+
await (0, _milaboratories_ts_helpers.sleep)(300);
|
|
430
|
+
if (i == attempts) throw new Error(`downloadAndUntar: ${attempts} attempts, last error: ${e}`);
|
|
431
|
+
}
|
|
432
|
+
state.downloadResult = (0, _milaboratories_ts_helpers.notEmpty)(downloadBinaryResult);
|
|
433
|
+
state.localArchivePath = upath.default.resolve(state.downloadResult.archivePath);
|
|
434
|
+
state.remoteDir = upath.default.join(state.binBasePath, state.downloadResult.baseName);
|
|
435
|
+
state.remoteArchivePath = state.remoteDir + ".tgz";
|
|
436
|
+
await this.sshClient.ensureRemoteDirCreated(state.remoteDir);
|
|
437
|
+
await this.sshClient.uploadFile(state.localArchivePath, state.remoteArchivePath);
|
|
438
|
+
state.uploadDone = true;
|
|
439
|
+
try {
|
|
440
|
+
await this.sshClient.exec("hash tar");
|
|
441
|
+
} catch {
|
|
442
|
+
throw new Error(`tar is not installed on the server. Please install it before running Platforma.`);
|
|
443
|
+
}
|
|
444
|
+
const untarResult = await this.sshClient.exec(`tar --warning=no-all -xvf ${state.remoteArchivePath} --directory=${state.remoteDir}`);
|
|
445
|
+
if (untarResult.stderr) throw new Error(`downloadAndUntar: untar: stderr occurred: ${untarResult.stderr}, stdout: ${untarResult.stdout}`);
|
|
446
|
+
state.untarDone = true;
|
|
447
|
+
return state;
|
|
448
|
+
}
|
|
449
|
+
async needDownload(remoteHome, arch) {
|
|
450
|
+
const checkPathSupervisor = require_pl_paths.supervisorBin(remoteHome, arch.arch);
|
|
451
|
+
const checkPathMinio = require_pl_paths.minioDir(remoteHome, arch.arch);
|
|
452
|
+
const checkPathPlatforma = require_pl_paths.platformaBin(remoteHome, arch.arch);
|
|
453
|
+
if (!await this.sshClient.checkFileExists(checkPathPlatforma) || !await this.sshClient.checkFileExists(checkPathMinio) || !await this.sshClient.checkFileExists(checkPathSupervisor)) return true;
|
|
454
|
+
return false;
|
|
455
|
+
}
|
|
456
|
+
async checkIsAliveWithInterval(shouldUseMinio, interval = 1e3, count = 15, shouldStart = true) {
|
|
457
|
+
const maxMs = count * interval;
|
|
458
|
+
let total = 0;
|
|
459
|
+
let alive = await this.isAlive();
|
|
460
|
+
while (shouldStart ? !require_supervisord.isAllAlive(alive, shouldUseMinio) : require_supervisord.isAllAlive(alive, shouldUseMinio)) {
|
|
461
|
+
await (0, _milaboratories_ts_helpers.sleep)(interval);
|
|
462
|
+
total += interval;
|
|
463
|
+
if (total > maxMs) throw new Error(`isAliveWithInterval: The process did not ${shouldStart ? "started" : "stopped"} after ${maxMs} ms. Live status: ${JSON.stringify(alive)}`);
|
|
464
|
+
alive = await this.isAlive();
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
async readExistedConfig(remoteHome) {
|
|
468
|
+
return require_connection_info.parseConnectionInfo(await this.sshClient.readFile(require_pl_paths.connectionInfo(remoteHome)));
|
|
469
|
+
}
|
|
470
|
+
async fetchPorts(remoteHome, arch) {
|
|
471
|
+
return {
|
|
472
|
+
grpc: {
|
|
473
|
+
local: await (0, _milaboratories_pl_config.getFreePort)(),
|
|
474
|
+
remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch)
|
|
475
|
+
},
|
|
476
|
+
monitoring: {
|
|
477
|
+
local: await (0, _milaboratories_pl_config.getFreePort)(),
|
|
478
|
+
remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch)
|
|
479
|
+
},
|
|
480
|
+
debug: {
|
|
481
|
+
local: await (0, _milaboratories_pl_config.getFreePort)(),
|
|
482
|
+
remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch)
|
|
483
|
+
},
|
|
484
|
+
http: {
|
|
485
|
+
local: await (0, _milaboratories_pl_config.getFreePort)(),
|
|
486
|
+
remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch)
|
|
487
|
+
},
|
|
488
|
+
minioPort: {
|
|
489
|
+
local: await (0, _milaboratories_pl_config.getFreePort)(),
|
|
490
|
+
remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch)
|
|
491
|
+
},
|
|
492
|
+
minioConsolePort: {
|
|
493
|
+
local: await (0, _milaboratories_pl_config.getFreePort)(),
|
|
494
|
+
remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch)
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
async getLocalFreePort() {
|
|
499
|
+
return new Promise((res) => {
|
|
500
|
+
const srv = node_net.default.createServer();
|
|
501
|
+
srv.listen(0, () => {
|
|
502
|
+
const port = srv.address().port;
|
|
503
|
+
srv.close((_) => res(port));
|
|
504
|
+
});
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
async getFreePortForPlatformaOnServer(remoteHome, arch) {
|
|
508
|
+
const freePortBin = require_pl_paths.platformaFreePortBin(remoteHome, arch.arch);
|
|
509
|
+
const { stdout, stderr } = await this.sshClient.exec(`${freePortBin}`);
|
|
510
|
+
if (stderr) throw new Error(`getFreePortForPlatformaOnServer: stderr is not empty: ${stderr}, stdout: ${stdout}`);
|
|
511
|
+
return +stdout;
|
|
512
|
+
}
|
|
513
|
+
async getArch() {
|
|
514
|
+
const { stdout, stderr } = await this.sshClient.exec("uname -s && uname -m");
|
|
515
|
+
if (stderr) throw new Error(`getArch: stderr is not empty: ${stderr}, stdout: ${stdout}`);
|
|
516
|
+
const arr = stdout.split("\n");
|
|
517
|
+
return {
|
|
518
|
+
platform: arr[0],
|
|
519
|
+
arch: arr[1]
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
async getUserHomeDirectory() {
|
|
523
|
+
const { stdout, stderr } = await this.sshClient.exec("echo $HOME");
|
|
524
|
+
if (stderr) {
|
|
525
|
+
const home = `/home/${this.username}`;
|
|
526
|
+
console.warn(`getUserHomeDirectory: stderr is not empty: ${stderr}, stdout: ${stdout}, will get a default home: ${home}`);
|
|
527
|
+
return home;
|
|
528
|
+
}
|
|
529
|
+
return stdout.trim();
|
|
530
|
+
}
|
|
531
|
+
};
|
|
615
532
|
const defaultSshPlConfig = {
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
533
|
+
useGlobalAccess: false,
|
|
534
|
+
plBinary: {
|
|
535
|
+
type: "Download",
|
|
536
|
+
version: require_pl_version.getDefaultPlVersion()
|
|
537
|
+
}
|
|
621
538
|
};
|
|
622
539
|
/**
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
540
|
+
* Gets the glibc version on the remote system
|
|
541
|
+
* @returns The glibc version as a number
|
|
542
|
+
* @throws Error if version cannot be determined
|
|
543
|
+
*/
|
|
627
544
|
async function getGlibcVersion(logger, sshClient) {
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
logger.error(`glibc version check failed: ${e}`);
|
|
637
|
-
throw e;
|
|
638
|
-
}
|
|
545
|
+
try {
|
|
546
|
+
const { stdout, stderr } = await sshClient.exec("ldd --version | head -n 1");
|
|
547
|
+
if (stderr) throw new Error(`Failed to check glibc version: ${stderr}`);
|
|
548
|
+
return parseGlibcVersion(stdout);
|
|
549
|
+
} catch (e) {
|
|
550
|
+
logger.error(`glibc version check failed: ${e}`);
|
|
551
|
+
throw e;
|
|
552
|
+
}
|
|
639
553
|
}
|
|
640
554
|
function parseGlibcVersion(output) {
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
}
|
|
645
|
-
return parseFloat(versionMatch[0]);
|
|
555
|
+
const versionMatch = output.match(/\d+\.\d+/);
|
|
556
|
+
if (!versionMatch) throw new Error(`Could not parse glibc version from: ${output}`);
|
|
557
|
+
return parseFloat(versionMatch[0]);
|
|
646
558
|
}
|
|
647
559
|
|
|
560
|
+
//#endregion
|
|
648
561
|
exports.SshPl = SshPl;
|
|
649
562
|
exports.parseGlibcVersion = parseGlibcVersion;
|
|
650
|
-
//# sourceMappingURL=pl.cjs.map
|
|
563
|
+
//# sourceMappingURL=pl.cjs.map
|