@milaboratories/pl-deployments 1.1.5 → 1.1.7

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.
@@ -1,8 +1,10 @@
1
1
  import type { MiLogger } from '@milaboratories/ts-helpers';
2
2
  import { assertNever } from '@milaboratories/ts-helpers';
3
- import { downloadPlBinary, DownloadBinaryResult } from './pl_binary_download';
3
+ import { downloadBinary } from './pl_binary_download';
4
4
  import { getDefaultPlVersion } from './pl_version';
5
5
  import os from 'os';
6
+ import upath from 'upath';
7
+ import { newOs, OSType } from './os_and_arch';
6
8
 
7
9
  /** Shows how the binary should be got. */
8
10
  export type PlBinarySource = PlBinarySourceDownload | PlBinarySourceLocal;
@@ -28,7 +30,8 @@ export async function resolveLocalPlBinaryPath(
28
30
  ): Promise<string> {
29
31
  switch (src.type) {
30
32
  case 'Download':
31
- return (await downloadPlBinary(logger, downloadDir, src.version, os.arch(), os.platform())).binaryPath!;
33
+ const ops = await downloadBinary(logger, downloadDir, 'pl', `pl-${src.version}`, os.arch(), os.platform());
34
+ return upath.join(ops.baseName, 'binaries', osToBinaryName[newOs(os.platform())]);
32
35
 
33
36
  case 'Local':
34
37
  return src.path;
@@ -37,3 +40,9 @@ export async function resolveLocalPlBinaryPath(
37
40
  assertNever(src);
38
41
  }
39
42
  }
43
+
44
+ export const osToBinaryName: Record<OSType, string> = {
45
+ linux: 'platforma',
46
+ macos: 'platforma',
47
+ windows: 'platforma.exe',
48
+ };
@@ -11,13 +11,19 @@ import decompress from 'decompress';
11
11
  import type { ArchType, OSType } from './os_and_arch';
12
12
  import { newOs, newArch } from './os_and_arch';
13
13
 
14
+ const cdn = 'https://cdn.platforma.bio/software';
15
+ // We'll download things from Global Access if downloading from CDN has failed
16
+ // (it might be that it's blocked from the company's network.)
17
+ const gaCdn = 'https://cdn-ga.pl-open.science/software';
18
+
14
19
  export type DownloadBinaryResult = {
15
20
  archiveUrl: string;
21
+ alternativeArchiveGAUrl: string;
22
+ wasDownloadedFrom?: string;
16
23
  archivePath: string;
17
24
  archiveType: ArchiveType;
18
25
  targetFolder: string;
19
26
  baseName: string;
20
- binaryPath?: string;
21
27
  };
22
28
 
23
29
  export async function downloadBinaryNoExtract(
@@ -29,9 +35,15 @@ export async function downloadBinaryNoExtract(
29
35
  platform: string,
30
36
  ): Promise<DownloadBinaryResult> {
31
37
  const opts = getPathsForDownload(softwareName, tgzName, baseDir, newArch(arch), newOs(platform));
32
- const { archiveUrl, archivePath } = opts;
38
+ const { archiveUrl, alternativeArchiveGAUrl, archivePath } = opts;
33
39
 
34
- await downloadArchive(logger, archiveUrl, archivePath);
40
+ try {
41
+ await downloadArchive(logger, archiveUrl, archivePath);
42
+ opts.wasDownloadedFrom = archiveUrl;
43
+ } catch (e: unknown) {
44
+ await downloadArchive(logger, alternativeArchiveGAUrl, archivePath);
45
+ opts.wasDownloadedFrom = alternativeArchiveGAUrl;
46
+ }
35
47
 
36
48
  return opts;
37
49
  }
@@ -45,25 +57,16 @@ export async function downloadBinary(
45
57
  platform: string,
46
58
  ): Promise<DownloadBinaryResult> {
47
59
  const opts = getPathsForDownload(softwareName, archiveName, baseDir, newArch(arch), newOs(platform));
48
- const { archiveUrl, archivePath, archiveType, targetFolder, baseName } = opts;
49
-
50
- await downloadArchive(logger, archiveUrl, archivePath);
51
- await extractArchive(logger, archivePath, archiveType, targetFolder);
60
+ const { archiveUrl, alternativeArchiveGAUrl, archivePath, archiveType, targetFolder, baseName } = opts;
52
61
 
53
- return opts;
54
- }
55
-
56
- export async function downloadPlBinary(
57
- logger: MiLogger,
58
- baseDir: string,
59
- plVersion: string,
60
- arch: string,
61
- platform: string,
62
- ): Promise<DownloadBinaryResult> {
63
- const opts = localDownloadPlOptions(plVersion, baseDir, newArch(arch), newOs(platform));
64
- const { archiveUrl, archivePath, archiveType, targetFolder, binaryPath } = opts;
62
+ try {
63
+ await downloadArchive(logger, archiveUrl, archivePath);
64
+ opts.wasDownloadedFrom = archiveUrl;
65
+ } catch (e: unknown) {
66
+ await downloadArchive(logger, alternativeArchiveGAUrl, archivePath);
67
+ opts.wasDownloadedFrom = alternativeArchiveGAUrl;
68
+ }
65
69
 
66
- await downloadArchive(logger, archiveUrl, archivePath);
67
70
  await extractArchive(logger, archivePath, archiveType, targetFolder);
68
71
 
69
72
  return opts;
@@ -80,13 +83,15 @@ function getPathsForDownload(
80
83
  const archiveType = osToArchiveType[os];
81
84
 
82
85
  const archiveFileName = `${baseName}.${archiveType}`;
83
- const archiveUrl = `https://cdn.platforma.bio/software/${softwareName}/${os}/${archiveFileName}`;
86
+ const archiveUrl = `${cdn}/${softwareName}/${os}/${archiveFileName}`;
87
+ const alternativeArchiveGAUrl = `${gaCdn}/${softwareName}/${os}/${archiveFileName}`;
84
88
  const archivePath = upath.join(baseDir, archiveFileName);
85
89
  // folder where binary distribution of pl will be unpacked
86
90
  const targetFolder = upath.join(baseDir, baseName);
87
91
 
88
92
  return {
89
93
  archiveUrl,
94
+ alternativeArchiveGAUrl,
90
95
  archivePath,
91
96
  archiveType,
92
97
  targetFolder,
@@ -94,34 +99,6 @@ function getPathsForDownload(
94
99
  };
95
100
  }
96
101
 
97
- export function localDownloadPlOptions(
98
- plVersion: string,
99
- baseDir: string,
100
- arch: ArchType,
101
- os: OSType,
102
- ): DownloadBinaryResult {
103
- const baseName = `pl-${plVersion}-${arch}`;
104
- const archiveType = osToArchiveType[os];
105
-
106
- const archiveFileName = `${baseName}.${archiveType}`;
107
- const archiveUrl = `https://cdn.platforma.bio/software/pl/${os}/${archiveFileName}`;
108
- const archivePath = upath.join(baseDir, archiveFileName);
109
-
110
- // folder where binary distribution of pl will be unpacked
111
- const targetFolder = upath.join(baseDir, baseName);
112
-
113
- const binaryPath = upath.join(baseName, 'binaries', osToBinaryName[os]);
114
-
115
- return {
116
- archiveUrl,
117
- archivePath,
118
- archiveType,
119
- targetFolder,
120
- binaryPath,
121
- baseName,
122
- };
123
- }
124
-
125
102
  export type DownloadInfo = {
126
103
  dstArchive?: string;
127
104
  fileExisted?: boolean;
@@ -250,9 +227,3 @@ const osToArchiveType: Record<OSType, ArchiveType> = {
250
227
  macos: 'tgz',
251
228
  windows: 'zip',
252
229
  };
253
-
254
- const osToBinaryName: Record<OSType, string> = {
255
- linux: 'platforma',
256
- macos: 'platforma',
257
- windows: 'platforma.exe',
258
- };
package/src/local/pl.ts CHANGED
@@ -162,7 +162,9 @@ export async function localPlatformaInit(logger: MiLogger, _ops: LocalPlOptions)
162
162
  logger.info(`writing configuration '${configPath}'...`);
163
163
  await fsp.writeFile(configPath, ops.config);
164
164
 
165
- const baseBinaryPath = await resolveLocalPlBinaryPath(logger, upath.join(workDir, 'binaries'), ops.plBinary);
165
+ const plBinPath = upath.join(workDir, 'binaries');
166
+ const baseBinaryPath = await resolveLocalPlBinaryPath(logger, plBinPath, ops.plBinary);
167
+
166
168
  const binaryPath = trace('binaryPath', upath.join('binaries', baseBinaryPath));
167
169
 
168
170
  const processOpts: ProcessOptions = {
@@ -5,7 +5,7 @@ import upath from 'upath';
5
5
  import { getDefaultPlVersion } from '../../common/pl_version';
6
6
  import { existsSync, unlinkSync, rmSync } from 'fs';
7
7
  import { newArch } from '../../common/os_and_arch';
8
- import { downloadBinary, downloadPlBinary } from '../../common/pl_binary_download';
8
+ import { downloadBinary } from '../../common/pl_binary_download';
9
9
  import { ConsoleLoggerAdapter } from '@milaboratories/ts-helpers';
10
10
  import * as plpath from '../pl_paths';
11
11
 
@@ -84,10 +84,10 @@ describe('SshPl', async () => {
84
84
  it('Transfer Platforma to server', async () => {
85
85
  const arch = await sshPl.getArch();
86
86
 
87
- const plPath = await downloadPlBinary(
87
+ const plPath = await downloadBinary(
88
88
  new ConsoleLoggerAdapter(),
89
89
  downloadDestination,
90
- getDefaultPlVersion(),
90
+ 'pl', `pl-${getDefaultPlVersion()}`,
91
91
  arch.arch,
92
92
  arch.platform,
93
93
  );
@@ -154,10 +154,10 @@ describe('SshPl download binaries', async () => {
154
154
  it('Download pl. We have archive and extracted data', async () => {
155
155
  const arch = await sshPl.getArch();
156
156
 
157
- const result = await downloadPlBinary(
157
+ const result = await downloadBinary(
158
158
  new ConsoleLoggerAdapter(),
159
159
  downloadDestination,
160
- getDefaultPlVersion(),
160
+ 'pl', `pl-${getDefaultPlVersion()}`,
161
161
  arch.arch,
162
162
  arch.platform,
163
163
  );
package/src/ssh/pl.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type * as ssh from 'ssh2';
2
2
  import { SshClient } from './ssh';
3
3
  import type { MiLogger } from '@milaboratories/ts-helpers';
4
- import { sleep, notEmpty, fileExists } from '@milaboratories/ts-helpers';
4
+ import { sleep, notEmpty } from '@milaboratories/ts-helpers';
5
5
  import type { DownloadBinaryResult } from '../common/pl_binary_download';
6
6
  import { downloadBinaryNoExtract } from '../common/pl_binary_download';
7
7
  import upath from 'upath';
@@ -19,7 +19,7 @@ export class SshPl {
19
19
  public readonly logger: MiLogger,
20
20
  public readonly sshClient: SshClient,
21
21
  private readonly username: string,
22
- ) {}
22
+ ) { }
23
23
 
24
24
  public info() {
25
25
  return {
@@ -38,6 +38,10 @@ export class SshPl {
38
38
  }
39
39
  }
40
40
 
41
+ public cleanUp() {
42
+ this.sshClient.close();
43
+ }
44
+
41
45
  public async isAlive(): Promise<boolean> {
42
46
  const arch = await this.getArch();
43
47
  const remoteHome = await this.getUserHomeDirectory();
@@ -89,6 +93,8 @@ export class SshPl {
89
93
  this.logger.info(`pl.reset: Deleting Platforma workDir ${workDir} on the server`);
90
94
  await this.sshClient.deleteFolder(plpath.workDir(workDir));
91
95
 
96
+ this.cleanUp();
97
+
92
98
  return true;
93
99
  }
94
100
 
package/src/ssh/ssh.ts CHANGED
@@ -23,6 +23,7 @@ export type SshDirContent = {
23
23
  export class SshClient {
24
24
  private config?: ConnectConfig;
25
25
  public homeDir?: string;
26
+ private forwardedServers: net.Server[] = [];
26
27
 
27
28
  constructor(
28
29
  private readonly logger: MiLogger,
@@ -46,6 +47,10 @@ export class SshClient {
46
47
  return client;
47
48
  }
48
49
 
50
+ public getForwardedServers() {
51
+ return this.forwardedServers;
52
+ }
53
+
49
54
  public getFullHostName() {
50
55
  return `${this.config?.host}:${this.config?.port}`;
51
56
  }
@@ -224,6 +229,7 @@ export class SshClient {
224
229
 
225
230
  server.listen(ports.localPort, '127.0.0.1', () => {
226
231
  this.logger.info(`${log}.server: started listening`);
232
+ this.forwardedServers.push(server);
227
233
  resolve({ server });
228
234
  });
229
235
 
@@ -234,10 +240,25 @@ export class SshClient {
234
240
 
235
241
  server.on('close', () => {
236
242
  this.logger.info(`${log}.server: closed ${JSON.stringify(ports)}`);
243
+ this.forwardedServers = this.forwardedServers.filter((s) => s !== server);
237
244
  });
238
245
  });
239
246
  }
240
247
 
248
+ public closeForwardedPorts(): void {
249
+ this.logger.info('[SSH] Closing all forwarded ports...');
250
+ this.forwardedServers.forEach((server) => {
251
+ const rawAddress = server.address();
252
+ if (rawAddress && typeof rawAddress !== 'string') {
253
+ const address: net.AddressInfo = rawAddress;
254
+ this.logger.info(`[SSH] Closing port forward for server ${address.address}:${address.port}`);
255
+ }
256
+
257
+ server.close();
258
+ });
259
+ this.forwardedServers = [];
260
+ }
261
+
241
262
  /**
242
263
  * Checks if a specified host is available by performing a DNS lookup.
243
264
  * @param hostname - The hostname or IP address to check.
@@ -616,9 +637,10 @@ export class SshClient {
616
637
  }
617
638
 
618
639
  /**
619
- * Closes the SSH client connection.
640
+ * Closes the SSH client connection and forwarded ports.
620
641
  */
621
642
  public close(): void {
643
+ this.closeForwardedPorts();
622
644
  this.client.end();
623
645
  }
624
646
  }