@milaboratories/pl-deployments 2.3.3 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/ssh/pl.ts CHANGED
@@ -10,9 +10,9 @@ import { getDefaultPlVersion } from '../common/pl_version';
10
10
 
11
11
  import net from 'node:net';
12
12
  import type { PlConfig, PlLicenseMode, SshPlConfigGenerationResult } from '@milaboratories/pl-config';
13
- import { generateSshPlConfigs, getFreePort } from '@milaboratories/pl-config';
13
+ import { getFreePort, generateSshPlConfigs } from '@milaboratories/pl-config';
14
14
  import type { SupervisorStatus } from './supervisord';
15
- import { supervisorStatus, supervisorStop as supervisorCtlShutdown, generateSupervisordConfig, supervisorCtlStart } from './supervisord';
15
+ import { supervisorStatus, supervisorStop as supervisorCtlShutdown, generateSupervisordConfigWithMinio, supervisorCtlStart, isSupervisordRunning, generateSupervisordConfig, isAllAlive } from './supervisord';
16
16
  import type { ConnectionInfo, SshPlPorts } from './connection_info';
17
17
  import { newConnectionInfo, parseConnectionInfo, stringifyConnectionInfo } from './connection_info';
18
18
  import type { PlBinarySourceDownload } from '../common/pl_binary';
@@ -20,7 +20,7 @@ import type { PlBinarySourceDownload } from '../common/pl_binary';
20
20
  const minRequiredGlibcVersion = 2.28;
21
21
 
22
22
  export class SshPl {
23
- private initState: PlatformaInitState = {};
23
+ private initState: PlatformaInitState = { step: 'init' };
24
24
  constructor(
25
25
  public readonly logger: MiLogger,
26
26
  public readonly sshClient: SshClient,
@@ -57,16 +57,16 @@ export class SshPl {
57
57
 
58
58
  /** Starts all the services on the server.
59
59
  * Idempotent semantic: we could call it several times. */
60
- public async start() {
60
+ public async start(shouldUseMinio: boolean) {
61
61
  const arch = await this.getArch();
62
62
  const remoteHome = await this.getUserHomeDirectory();
63
63
 
64
64
  try {
65
- if (!(await this.isAlive()).allAlive) {
65
+ if (!isAllAlive(await this.isAlive(), shouldUseMinio)) {
66
66
  await supervisorCtlStart(this.sshClient, remoteHome, arch.arch);
67
67
 
68
68
  // We are waiting for Platforma to run to ensure that it has started.
69
- return await this.checkIsAliveWithInterval();
69
+ return await this.checkIsAliveWithInterval(shouldUseMinio);
70
70
  }
71
71
  } catch (e: unknown) {
72
72
  const msg = `SshPl.start: ${e}`;
@@ -82,9 +82,12 @@ export class SshPl {
82
82
  const remoteHome = await this.getUserHomeDirectory();
83
83
 
84
84
  try {
85
- if ((await this.isAlive()).allAlive) {
85
+ const alive = await this.isAlive();
86
+ if (isSupervisordRunning(alive)) {
86
87
  await supervisorCtlShutdown(this.sshClient, remoteHome, arch.arch);
87
- return await this.checkIsAliveWithInterval(undefined, undefined, false);
88
+ // Check if Minio is running by looking at the alive status
89
+ const shouldUseMinio = alive.minio === true;
90
+ return await this.checkIsAliveWithInterval(shouldUseMinio, 1000, 15, false);
88
91
  }
89
92
  } catch (e: unknown) {
90
93
  const msg = `PlSsh.stop: ${e}`;
@@ -115,161 +118,266 @@ export class SshPl {
115
118
  * generates all the configs, creates necessary dirs,
116
119
  * and finally starts all the services. */
117
120
  public async platformaInit(options: SshPlConfig): Promise<ConnectionInfo> {
118
- const state: PlatformaInitState = { localWorkdir: options.localWorkdir };
121
+ const state: PlatformaInitState = { localWorkdir: options.localWorkdir, step: 'init' };
119
122
 
120
123
  const { onProgress } = options;
121
124
 
122
- try {
123
- // merge options with default ops.
124
- const ops: SshPlConfig = {
125
- ...defaultSshPlConfig,
126
- ...options,
127
- };
128
- state.plBinaryOps = ops.plBinary;
129
-
130
- await onProgress?.('Detecting server architecture...');
131
- state.arch = await this.getArch();
132
- await onProgress?.('Server architecture detected.');
133
-
134
- await onProgress?.('Fetching user home directory...');
135
- state.remoteHome = await this.getUserHomeDirectory();
136
- await onProgress?.('User home directory retrieved.');
125
+ // merge options with default ops.
126
+ const ops: SshPlConfig = {
127
+ ...defaultSshPlConfig,
128
+ ...options,
129
+ };
130
+ state.plBinaryOps = ops.plBinary;
137
131
 
138
- await onProgress?.('Checking platform status...');
139
- state.alive = await this.isAlive();
132
+ try {
133
+ await this.doStepDetectArch(state, onProgress);
134
+ await this.doStepDetectHome(state, onProgress);
140
135
 
141
- if (state.alive.allAlive) {
142
- await onProgress?.('All required services are running.');
136
+ const needRestartPlatforma = await this.doStepReadExistedConfig(state, ops, onProgress);
137
+ if (!needRestartPlatforma) {
138
+ await onProgress?.('Platforma is already running. Skipping initialization.');
139
+ return state.existedSettings!;
143
140
  }
141
+ await this.doStepStopExistedPlatforma(state, onProgress);
144
142
 
145
- if (state.alive.allAlive) {
146
- state.userCredentials = await this.getUserCredentials(state.remoteHome);
147
- if (!state.userCredentials) {
148
- throw new Error(`SshPl.platformaInit: platforma is alive but userCredentials are not found`);
149
- }
150
- const sameGA = state.userCredentials.useGlobalAccess == ops.useGlobalAccess;
151
- const samePlVersion = state.userCredentials.plVersion == ops.plBinary!.version;
152
- state.needRestart = !(sameGA && samePlVersion);
153
- this.logger.info(`SshPl.platformaInit: need restart? ${state.needRestart}`);
154
-
155
- if (!state.needRestart) {
156
- await onProgress?.('Server setup completed.');
157
- return state.userCredentials;
158
- }
143
+ await onProgress?.('Installation platforma...');
159
144
 
160
- await onProgress?.('Stopping services...');
161
- await this.stop();
162
- }
145
+ await this.doStepDownloadBinaries(state, onProgress, ops);
146
+ await this.doStepFetchPorts(state);
147
+ await this.doStepGenerateNewConfig(state, onProgress, ops);
148
+ await this.doStepCreateFoldersAndSaveFiles(state, onProgress);
149
+ await this.doStepConfigureSupervisord(state, onProgress);
150
+ await this.doStepSaveNewConnectionInfo(state, onProgress, ops);
151
+ await this.doStepStartPlatforma(state, onProgress);
163
152
 
164
- await onProgress?.('Downloading and uploading required binaries...');
153
+ return state.connectionInfo!;
154
+ } catch (e: unknown) {
155
+ const msg = `SshPl.platformaInit: ${e}, state: ${JSON.stringify(this.removeSensitiveData(state))}`;
156
+ this.logger.error(msg);
165
157
 
166
- const glibcVersion = await getGlibcVersion(this.logger, this.sshClient);
167
- if (glibcVersion < minRequiredGlibcVersion)
168
- throw new Error(`glibc version ${glibcVersion} is too old. Version ${minRequiredGlibcVersion} or higher is required for Platforma.`);
158
+ throw new Error(msg);
159
+ }
160
+ }
169
161
 
170
- const downloadRes = await this.downloadBinariesAndUploadToTheServer(
171
- ops.localWorkdir, ops.plBinary!, state.remoteHome, state.arch,
172
- );
173
- await onProgress?.('All required binaries have been downloaded and uploaded.');
162
+ private async doStepStopExistedPlatforma(state: PlatformaInitState, onProgress: ((...args: any) => Promise<any>) | undefined) {
163
+ state.step = 'stopExistedPlatforma';
164
+ if (!isAllAlive(state.alive!, state.shouldUseMinio ?? false)) {
165
+ return;
166
+ }
174
167
 
175
- state.binPaths = { ...downloadRes, history: undefined };
176
- state.downloadedBinaries = downloadRes.history;
168
+ await onProgress?.('Stopping services...');
169
+ await this.stop();
170
+ }
177
171
 
178
- state.ports = await this.fetchPorts(state.remoteHome, state.arch);
172
+ private removeSensitiveData(state: PlatformaInitState): PlatformaInitState {
173
+ const stateCopy = { ...state };
174
+ stateCopy.generatedConfig = { ...stateCopy.generatedConfig, filesToCreate: { skipped: 'sanitized' } } as SshPlConfigGenerationResult;
175
+ return stateCopy;
176
+ }
179
177
 
180
- if (!state.ports.debug.remote || !state.ports.grpc.remote || !state.ports.minioPort.remote || !state.ports.minioConsolePort.remote || !state.ports.monitoring.remote) {
181
- throw new Error(`SshPl.platformaInit: remote ports are not defined`);
182
- }
178
+ private async doStepStartPlatforma(state: PlatformaInitState, onProgress: ((...args: any) => Promise<any>) | undefined) {
179
+ state.step = 'startPlatforma';
180
+ await onProgress?.('Starting Platforma on the server...');
181
+ await this.start(state.shouldUseMinio ?? false);
182
+ state.started = true;
183
+ this.initState = state;
183
184
 
184
- await onProgress?.('Generating server configuration...');
185
- const config = await generateSshPlConfigs({
186
- logger: this.logger,
187
- workingDir: plpath.workDir(state.remoteHome),
188
- portsMode: {
189
- type: 'customWithMinio',
190
- ports: {
191
- debug: state.ports.debug.remote,
192
- grpc: state.ports.grpc.remote,
193
- minio: state.ports.minioPort.remote,
194
- minioConsole: state.ports.minioConsolePort.remote,
195
- monitoring: state.ports.monitoring.remote,
196
-
197
- grpcLocal: state.ports.grpc.local,
198
- minioLocal: state.ports.minioPort.local,
199
- },
200
- },
201
- licenseMode: ops.license,
202
- useGlobalAccess: notEmpty(ops.useGlobalAccess),
203
- plConfigPostprocessing: ops.plConfigPostprocessing,
204
- });
205
- state.generatedConfig = { ...config, filesToCreate: { skipped: 'it is too wordy' } };
185
+ await onProgress?.('Platforma has been started successfully.');
186
+ }
206
187
 
207
- await onProgress?.('Server configuration generated.');
188
+ private async doStepSaveNewConnectionInfo(state: PlatformaInitState, onProgress: ((...args: any) => Promise<any>) | undefined, ops: SshPlConfig) {
189
+ state.step = 'saveNewConnectionInfo';
190
+ const config = state.generatedConfig!;
191
+ await onProgress?.('Saving connection information...');
192
+ state.connectionInfo = newConnectionInfo(
193
+ config.plUser,
194
+ config.plPassword,
195
+ state.ports!,
196
+ notEmpty(ops.useGlobalAccess),
197
+ ops.plBinary!.version,
198
+ state.shouldUseMinio ?? false,
199
+ );
200
+ await this.sshClient.writeFileOnTheServer(
201
+ plpath.connectionInfo(state.remoteHome!),
202
+ stringifyConnectionInfo(state.connectionInfo),
203
+ );
204
+ await onProgress?.('Connection information saved.');
205
+ }
208
206
 
209
- await onProgress?.('Generating folder structure...');
210
- for (const [filePath, content] of Object.entries(config.filesToCreate)) {
211
- await this.sshClient.writeFileOnTheServer(filePath, content);
212
- this.logger.info(`Created file ${filePath}`);
213
- }
207
+ private async doStepConfigureSupervisord(state: PlatformaInitState, onProgress: ((...args: any) => Promise<any>) | undefined) {
208
+ await onProgress?.('Writing supervisord configuration...');
209
+ state.step = 'configureSupervisord';
214
210
 
215
- for (const dir of config.dirsToCreate) {
216
- await this.sshClient.ensureRemoteDirCreated(dir);
217
- this.logger.info(`Created directory ${dir}`);
218
- }
219
- await onProgress?.('Folder structure created.');
211
+ const config = state.generatedConfig!;
220
212
 
221
- await onProgress?.('Writing supervisord configuration...');
222
- const supervisorConfig = generateSupervisordConfig(
213
+ let supervisorConfig: string;
214
+ if (state.shouldUseMinio) {
215
+ supervisorConfig = generateSupervisordConfigWithMinio(
223
216
  config.minioConfig.storageDir,
224
217
  config.minioConfig.envs,
225
- await this.getFreePortForPlatformaOnServer(state.remoteHome, state.arch),
218
+ await this.getFreePortForPlatformaOnServer(state.remoteHome!, state.arch!),
226
219
  config.workingDir,
227
220
  config.plConfig.configPath,
228
- state.binPaths.minioRelPath,
229
- state.binPaths.downloadedPl,
230
- );
231
-
232
- const writeResult = await this.sshClient.writeFileOnTheServer(plpath.supervisorConf(state.remoteHome), supervisorConfig);
233
- if (!writeResult) {
234
- throw new Error(`Can not write supervisord config on the server ${plpath.workDir(state.remoteHome)}`);
235
- }
236
- await onProgress?.('Supervisord configuration written.');
237
-
238
- await onProgress?.('Saving connection information...');
239
- state.connectionInfo = newConnectionInfo(
240
- config.plUser,
241
- config.plPassword,
242
- state.ports,
243
- notEmpty(ops.useGlobalAccess),
244
- ops.plBinary!.version,
221
+ state.binPaths!.minioRelPath!,
222
+ state.binPaths!.downloadedPl,
245
223
  );
246
- await this.sshClient.writeFileOnTheServer(
247
- plpath.connectionInfo(state.remoteHome),
248
- stringifyConnectionInfo(state.connectionInfo),
224
+ } else {
225
+ supervisorConfig = generateSupervisordConfig(
226
+ await this.getFreePortForPlatformaOnServer(state.remoteHome!, state.arch!),
227
+ config.workingDir,
228
+ config.plConfig.configPath,
229
+ state.binPaths!.downloadedPl,
249
230
  );
250
- await onProgress?.('Connection information saved.');
231
+ }
251
232
 
252
- await onProgress?.('Starting Platforma on the server...');
253
- await this.start();
254
- state.started = true;
255
- this.initState = state;
233
+ const writeResult = await this.sshClient.writeFileOnTheServer(plpath.supervisorConf(state.remoteHome!), supervisorConfig);
234
+ if (!writeResult) {
235
+ throw new Error(`Can not write supervisord config on the server ${plpath.workDir(state.remoteHome!)}`);
236
+ }
237
+ await onProgress?.('Supervisord configuration written.');
238
+ }
256
239
 
257
- await onProgress?.('Platforma has been started successfully.');
240
+ private async doStepCreateFoldersAndSaveFiles(state: PlatformaInitState, onProgress: ((...args: any) => Promise<any>) | undefined) {
241
+ state.step = 'createFoldersAndSaveFiles';
242
+ const config = state.generatedConfig!;
243
+ await onProgress?.('Generating folder structure...');
244
+ for (const [filePath, content] of Object.entries(config.filesToCreate)) {
245
+ await this.sshClient.writeFileOnTheServer(filePath, content);
246
+ this.logger.info(`Created file ${filePath}`);
247
+ }
258
248
 
259
- return state.connectionInfo;
260
- } catch (e: unknown) {
261
- const msg = `SshPl.platformaInit: ${e}, state: ${JSON.stringify(state)}`;
262
- this.logger.error(msg);
249
+ for (const dir of config.dirsToCreate) {
250
+ await this.sshClient.ensureRemoteDirCreated(dir);
251
+ this.logger.info(`Created directory ${dir}`);
252
+ }
253
+ await onProgress?.('Folder structure created.');
254
+ }
263
255
 
264
- throw new Error(msg);
256
+ private async doStepGenerateNewConfig(state: PlatformaInitState, onProgress: ((...args: any) => Promise<any>) | undefined, ops: SshPlConfig) {
257
+ state.step = 'generateNewConfig';
258
+
259
+ await onProgress?.('Generating new config...');
260
+ const config = await generateSshPlConfigs({
261
+ logger: this.logger,
262
+ workingDir: plpath.workDir(state.remoteHome!),
263
+ portsMode: {
264
+ type: 'customWithMinio',
265
+ ports: {
266
+ debug: state.ports!.debug.remote,
267
+ grpc: state.ports!.grpc.remote,
268
+ http: state.ports!.http!.remote,
269
+ minio: state.ports!.minioPort.remote,
270
+ minioConsole: state.ports!.minioConsolePort.remote,
271
+ monitoring: state.ports!.monitoring.remote,
272
+
273
+ httpLocal: state.ports!.http!.local,
274
+ grpcLocal: state.ports!.grpc.local,
275
+ minioLocal: state.ports!.minioPort.local,
276
+ },
277
+ },
278
+ licenseMode: ops.license,
279
+ useGlobalAccess: notEmpty(ops.useGlobalAccess),
280
+ plConfigPostprocessing: ops.plConfigPostprocessing,
281
+ useMinio: state.shouldUseMinio ?? false,
282
+ });
283
+ state.generatedConfig = { ...config };
284
+ await onProgress?.('New config generated');
285
+ }
286
+
287
+ private async doStepFetchPorts(state: PlatformaInitState) {
288
+ state.step = 'fetchPorts';
289
+ state.ports = await this.fetchPorts(state.remoteHome!, state.arch!);
290
+
291
+ if (!state.ports.debug.remote
292
+ || !state.ports.grpc.remote
293
+ || !state.ports.minioPort.remote
294
+ || !state.ports.minioConsolePort.remote
295
+ || !state.ports.monitoring.remote
296
+ || !state.ports.http?.remote) {
297
+ throw new Error(`SshPl.platformaInit: remote ports are not defined`);
265
298
  }
266
299
  }
267
300
 
301
+ private async doStepDownloadBinaries(state: PlatformaInitState, onProgress: ((...args: any) => Promise<any>) | undefined, ops: SshPlConfig) {
302
+ state.step = 'downloadBinaries';
303
+ await onProgress?.('Downloading and uploading required binaries...');
304
+
305
+ const glibcVersion = await getGlibcVersion(this.logger, this.sshClient);
306
+ if (glibcVersion < minRequiredGlibcVersion)
307
+ throw new Error(`glibc version ${glibcVersion} is too old. Version ${minRequiredGlibcVersion} or higher is required for Platforma.`);
308
+
309
+ const downloadRes = await this.downloadBinariesAndUploadToTheServer(
310
+ ops.localWorkdir, ops.plBinary!, state.remoteHome!, state.arch!, state.shouldUseMinio ?? false,
311
+ );
312
+ await onProgress?.('All required binaries have been downloaded and uploaded.');
313
+
314
+ state.binPaths = { ...downloadRes, history: undefined };
315
+ state.downloadedBinaries = downloadRes.history;
316
+ }
317
+
318
+ private async doStepDetectArch(state: PlatformaInitState, onProgress: ((...args: any) => Promise<any>) | undefined) {
319
+ state.step = 'detectArch';
320
+ await onProgress?.('Detecting server architecture...');
321
+ state.arch = await this.getArch();
322
+ await onProgress?.('Server architecture detected.');
323
+ }
324
+
325
+ private async doStepDetectHome(state: PlatformaInitState, onProgress: ((...args: any) => Promise<any>) | undefined) {
326
+ state.step = 'detectHome';
327
+ await onProgress?.('Fetching user home directory...');
328
+ state.remoteHome = await this.getUserHomeDirectory();
329
+ await onProgress?.('User home directory retrieved.');
330
+ }
331
+
332
+ private async doStepReadExistedConfig(
333
+ state: PlatformaInitState,
334
+ ops: SshPlConfig,
335
+ onProgress: ((...args: any) => Promise<any>) | undefined,
336
+ ): Promise<boolean> {
337
+ state.step = 'checkAlive';
338
+ await onProgress?.('Checking platform status...');
339
+ state.alive = await this.isAlive();
340
+
341
+ if (!state.alive?.platforma) {
342
+ return true;
343
+ }
344
+
345
+ await onProgress?.('All required services are running.');
346
+
347
+ state.existedSettings = await this.readExistedConfig(state.remoteHome!);
348
+ if (!state.existedSettings) {
349
+ throw new Error(`SshPl.platformaInit: platforma is alive but existed settings are not found`);
350
+ }
351
+
352
+ const sameGA = state.existedSettings.useGlobalAccess == ops.useGlobalAccess;
353
+ const samePlVersion = state.existedSettings.plVersion == ops.plBinary!.version;
354
+ state.needRestart = !(sameGA && samePlVersion);
355
+ this.logger.info(`SshPl.platformaInit: need restart? ${state.needRestart}`);
356
+
357
+ state.shouldUseMinio = state.existedSettings.minioIsUsed;
358
+ if (state.shouldUseMinio) {
359
+ this.logger.info(`SshPl.platformaInit: minio is used`);
360
+ } else {
361
+ this.logger.info(`SshPl.platformaInit: minio is not used`);
362
+ }
363
+
364
+ if (!state.needRestart) {
365
+ await onProgress?.('Server setup completed.');
366
+ return false;
367
+ }
368
+
369
+ await onProgress?.('Stopping services...');
370
+ await this.stop();
371
+
372
+ return true;
373
+ }
374
+
268
375
  public async downloadBinariesAndUploadToTheServer(
269
376
  localWorkdir: string,
270
377
  plBinary: PlBinarySourceDownload,
271
378
  remoteHome: string,
272
379
  arch: Arch,
380
+ shouldUseMinio: boolean,
273
381
  ) {
274
382
  const state: DownloadAndUntarState[] = [];
275
383
  try {
@@ -286,16 +394,18 @@ export class SshPl {
286
394
  state.push(supervisor);
287
395
 
288
396
  const minioPath = plpath.minioBin(remoteHome, arch.arch);
289
- const minio = await this.downloadAndUntar(
290
- localWorkdir, remoteHome, arch,
291
- 'minio', plpath.minioDirName,
292
- );
293
- state.push(minio);
294
- await this.sshClient.chmod(minioPath, 0o750);
397
+ if (shouldUseMinio) {
398
+ const minio = await this.downloadAndUntar(
399
+ localWorkdir, remoteHome, arch,
400
+ 'minio', plpath.minioDirName,
401
+ );
402
+ state.push(minio);
403
+ await this.sshClient.chmod(minioPath, 0o750);
404
+ }
295
405
 
296
406
  return {
297
407
  history: state,
298
- minioRelPath: minioPath,
408
+ minioRelPath: shouldUseMinio ? minioPath : undefined,
299
409
  downloadedPl: plpath.platformaBin(remoteHome, arch.arch),
300
410
  };
301
411
  } catch (e: unknown) {
@@ -385,12 +495,12 @@ export class SshPl {
385
495
  return false;
386
496
  }
387
497
 
388
- public async checkIsAliveWithInterval(interval: number = 1000, count = 15, shouldStart = true): Promise<void> {
498
+ public async checkIsAliveWithInterval(shouldUseMinio: boolean, interval: number = 1000, count = 15, shouldStart = true): Promise<void> {
389
499
  const maxMs = count * interval;
390
500
 
391
501
  let total = 0;
392
502
  let alive = await this.isAlive();
393
- while (shouldStart ? !alive.allAlive : alive.allAlive) {
503
+ while (shouldStart ? !isAllAlive(alive, shouldUseMinio) : isAllAlive(alive, shouldUseMinio)) {
394
504
  await sleep(interval);
395
505
  total += interval;
396
506
  if (total > maxMs) {
@@ -400,7 +510,7 @@ export class SshPl {
400
510
  }
401
511
  }
402
512
 
403
- public async getUserCredentials(remoteHome: string): Promise<ConnectionInfo> {
513
+ public async readExistedConfig(remoteHome: string): Promise<ConnectionInfo> {
404
514
  const connectionInfo = await this.sshClient.readFile(plpath.connectionInfo(remoteHome));
405
515
  return parseConnectionInfo(connectionInfo);
406
516
  }
@@ -419,6 +529,10 @@ export class SshPl {
419
529
  local: await getFreePort(),
420
530
  remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),
421
531
  },
532
+ http: {
533
+ local: await getFreePort(),
534
+ remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),
535
+ },
422
536
  minioPort: {
423
537
  local: await getFreePort(),
424
538
  remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),
@@ -506,7 +620,7 @@ const defaultSshPlConfig: Pick<
506
620
 
507
621
  type BinPaths = {
508
622
  history?: DownloadAndUntarState[];
509
- minioRelPath: string;
623
+ minioRelPath?: string;
510
624
  downloadedPl: string;
511
625
  };
512
626
 
@@ -523,14 +637,30 @@ type DownloadAndUntarState = {
523
637
  untarDone?: boolean;
524
638
  };
525
639
 
640
+ type PlatformaInitStep =
641
+ 'init'
642
+ | 'detectArch'
643
+ | 'detectHome'
644
+ | 'checkAlive'
645
+ | 'stopExistedPlatforma'
646
+ | 'downloadBinaries'
647
+ | 'fetchPorts'
648
+ | 'generateNewConfig'
649
+ | 'createFoldersAndSaveFiles'
650
+ | 'configureSupervisord'
651
+ | 'saveNewConnectionInfo'
652
+ | 'startPlatforma';
653
+
526
654
  type PlatformaInitState = {
655
+ step: PlatformaInitStep;
527
656
  localWorkdir?: string;
528
657
  plBinaryOps?: PlBinarySourceDownload;
529
658
  arch?: Arch;
530
659
  remoteHome?: string;
531
660
  alive?: SupervisorStatus;
532
- userCredentials?: ConnectionInfo;
661
+ existedSettings?: ConnectionInfo;
533
662
  needRestart?: boolean;
663
+ shouldUseMinio?: boolean;
534
664
  downloadedBinaries?: DownloadAndUntarState[];
535
665
  binPaths?: BinPaths;
536
666
  ports?: SshPlPorts;
@@ -32,11 +32,22 @@ export async function supervisorStop(
32
32
  export type SupervisorStatus = {
33
33
  platforma?: boolean;
34
34
  minio?: boolean;
35
- allAlive: boolean; // true when both pl and minio are alive.
36
35
  rawResult?: SshExecResult;
37
36
  execError?: string;
38
37
  };
39
38
 
39
+ export function isAllAlive(status: SupervisorStatus, shouldUseMinio: boolean) {
40
+ if (shouldUseMinio) {
41
+ return status.platforma && status.minio;
42
+ }
43
+
44
+ return status.platforma;
45
+ }
46
+
47
+ export function isSupervisordRunning(status: SupervisorStatus) {
48
+ return status.execError === undefined;
49
+ }
50
+
40
51
  export async function supervisorStatus(
41
52
  logger: MiLogger,
42
53
  sshClient: SshClient,
@@ -46,13 +57,13 @@ export async function supervisorStatus(
46
57
  try {
47
58
  result = await supervisorExec(sshClient, remoteHome, arch, 'ctl status');
48
59
  } catch (e: unknown) {
49
- return { execError: String(e), allAlive: false };
60
+ return { execError: String(e) };
50
61
  }
51
62
 
52
63
  if (result.stderr) {
53
64
  logger.info(`supervisord ctl status: stderr occurred: ${result.stderr}, stdout: ${result.stdout}`);
54
65
 
55
- return { rawResult: result, allAlive: false };
66
+ return { rawResult: result };
56
67
  }
57
68
 
58
69
  const platforma = isProgramRunning(result.stdout, 'platforma');
@@ -61,13 +72,8 @@ export async function supervisorStatus(
61
72
  rawResult: result,
62
73
  platforma,
63
74
  minio,
64
- allAlive: platforma && minio,
65
75
  };
66
76
 
67
- if (status.allAlive) {
68
- return status;
69
- }
70
-
71
77
  if (!status.minio) {
72
78
  logger.warn('Minio is not running on the server');
73
79
  }
@@ -80,6 +86,39 @@ export async function supervisorStatus(
80
86
  }
81
87
 
82
88
  export function generateSupervisordConfig(
89
+ supervisorRemotePort: number,
90
+ remoteWorkDir: string,
91
+ platformaConfigPath: string,
92
+ plPath: string,
93
+ ) {
94
+ const password = randomBytes(16).toString('hex');
95
+ const freePort = supervisorRemotePort;
96
+
97
+ return `
98
+ [supervisord]
99
+ logfile=${remoteWorkDir}/supervisord.log
100
+ loglevel=info
101
+ pidfile=${remoteWorkDir}/supervisord.pid
102
+
103
+ [inet_http_server]
104
+ port=127.0.0.1:${freePort}
105
+ username=default-user
106
+ password=${password}
107
+
108
+ [supervisorctl]
109
+ serverurl=http://127.0.0.1:${freePort}
110
+ username=default-user
111
+ password=${password}
112
+
113
+ [program:platforma]
114
+ autostart=true
115
+ command=${plPath} --config ${platformaConfigPath}
116
+ directory=${remoteWorkDir}
117
+ autorestart=true
118
+ `;
119
+ }
120
+
121
+ export function generateSupervisordConfigWithMinio(
83
122
  minioStorageDir: string,
84
123
  minioEnvs: Record<string, string>,
85
124
  supervisorRemotePort: number,