@milaboratories/pl-deployments 2.4.3 → 2.4.4

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 CHANGED
@@ -1 +1,18 @@
1
- # pl-local
1
+ # pl-deployments
2
+
3
+ ## Test tips
4
+
5
+ ### Regenerate `ssh` keys
6
+ `ssh2` lib has a way to generate private and public ssh keys.
7
+
8
+ `cd` into pl-deployments and then run it in `node` repl:
9
+
10
+ ```js
11
+ let keys = require("ssh2").utils.generateKeyPairSync('ecdsa', { bits: 256, comment: 'node.js rules!', passphrase: 'password', cipher: 'aes256-cbc' });
12
+
13
+ require("fs").writeFileSync('test-assets/public-key.pem', keys.public);
14
+ require("fs").writeFileSync('test-assets/private-key.pem', keys.private);
15
+ ```
16
+
17
+ ### Clean docker containers
18
+ If something went wrong with testcontainers, use `pnpm run cleanup-docker` from pl-deployments root.
@@ -1,13 +1,7 @@
1
1
  import { StartedTestContainer } from 'testcontainers';
2
2
  import { ConnectConfig } from 'ssh2';
3
- export declare const localFileUpload: string;
4
- export declare const localFileDownload: string;
5
- export declare const downloadsFolder: string;
6
- export declare const recUpload: string;
7
- export declare function createTestDirForRecursiveUpload(): Promise<void>;
8
- export declare function getPathForFile(fileName: string): string;
9
- export declare function generateKeys(): void;
10
- export declare function initPrivateKey(): string;
3
+ export declare const testAssetsPath: string;
4
+ export declare function readPrivateKey(): string;
11
5
  export declare function initContainer(name: string): Promise<StartedTestContainer>;
12
6
  export declare function getContainerHostAndPort(container: StartedTestContainer): {
13
7
  port: number;
@@ -1 +1 @@
1
- {"version":3,"file":"common-utils.d.ts","sourceRoot":"","sources":["../../../src/ssh/__tests__/common-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAK3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AAS1C,eAAO,MAAM,eAAe,QAAkC,CAAC;AAC/D,eAAO,MAAM,iBAAiB,QAA2C,CAAC;AAC1E,eAAO,MAAM,eAAe,QAAwE,CAAC;AACrG,eAAO,MAAM,SAAS,QAAsF,CAAC;AAE7G,wBAAsB,+BAA+B,kBAqBpD;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,UAE9C;AAED,wBAAgB,YAAY,SAM3B;AAED,wBAAgB,cAAc,IAAI,MAAM,CAGvC;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA4B/E;AAED,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,oBAAoB;;;EAKtE;AAgBD,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,oBAAoB,EAAE,KAAK,GAAE,OAAe,GAAG,aAAa,CAa1G;AAED,wBAAsB,OAAO,CAAC,SAAS,EAAE,oBAAoB,iBAc5D"}
1
+ {"version":3,"file":"common-utils.d.ts","sourceRoot":"","sources":["../../../src/ssh/__tests__/common-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAI3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AAK1C,eAAO,MAAM,cAAc,QAA2D,CAAC;AASvF,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAgB/E;AAED,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,oBAAoB;;;EAKtE;AAgBD,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,oBAAoB,EAAE,KAAK,GAAE,OAAe,GAAG,aAAa,CAa1G;AAED,wBAAsB,OAAO,CAAC,SAAS,EAAE,oBAAoB,iBAE5D"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@milaboratories/pl-deployments",
3
- "version": "2.4.3",
3
+ "version": "2.4.4",
4
4
  "pl-version": "1.35.2",
5
5
  "description": "MiLaboratories Platforma Backend code service run wrapper",
6
6
  "engines": {
@@ -38,7 +38,7 @@
38
38
  "testcontainers": "^10.18.0",
39
39
  "ts-jest": "^29.2.6",
40
40
  "tsconfig-paths": "^4.2.0",
41
- "typescript": "~5.5.4",
41
+ "typescript": "~5.6.3",
42
42
  "utility-types": "^3.11.0",
43
43
  "vite": "^6.3.5",
44
44
  "vitest": "^2.1.9",
@@ -1,90 +1,41 @@
1
1
  import type { StartedTestContainer } from 'testcontainers';
2
2
  import { GenericContainer } from 'testcontainers';
3
- import { writeFileSync, readFileSync, unlinkSync, existsSync } from 'node:fs';
4
- import { mkdir, rm } from 'node:fs/promises';
3
+ import { readFileSync, existsSync } from 'node:fs';
5
4
  import path from 'node:path';
6
5
  import type { ConnectConfig } from 'ssh2';
7
- import ssh from 'ssh2';
8
6
  import fs from 'node:fs';
9
7
 
10
8
  const SSH_PORT = [22, 3001];
11
9
 
12
- const publicKeyPath = getPathForFile('pub-key.pub');
13
- const privateKeyPath = getPathForFile('private-key.private');
10
+ export const testAssetsPath = path.resolve(__dirname, '..', '..', '..', 'test-assets');
14
11
 
15
- export const localFileUpload = getPathForFile('test-file.txt');
16
- export const localFileDownload = getPathForFile('test-file-download.txt');
17
- export const downloadsFolder = path.resolve(__dirname, '..', '..', '..', 'test-assets', 'downloads');
18
- export const recUpload = path.resolve(__dirname, '..', '..', '..', 'test-assets', 'downloads', 'rec-upload');
19
-
20
- export async function createTestDirForRecursiveUpload() {
21
- const pathBase = path.resolve(__dirname, '..', '..', '..', 'test-assets', 'downloads', 'rec-upload', 'sub-1');
22
- const path2 = path.resolve(__dirname, '..', '..', '..', 'test-assets', 'downloads', 'rec-upload', 'sub-1', 'sub-1-1');
23
-
24
- await mkdir(pathBase, { recursive: true });
25
- await mkdir(path2, { recursive: true });
26
-
27
- for (let i = 0; i < 19; i++) {
28
- const path2 = path.resolve(__dirname, '..', '..', '..', 'test-assets', 'downloads', 'rec-upload', 'sub-1', `sub-1-${i}`);
29
- await mkdir(path2, { recursive: true });
30
-
31
- for (let i = 0; i < 3; i++) {
32
- writeFileSync(path.resolve(path2, `test-${i}.txt`), `test-${i}`);
33
- }
34
- }
35
-
36
- for (let i = 1; i < 100; i++) {
37
- writeFileSync(path.resolve(pathBase, `test-${i}.txt`), `test-${i}`);
38
- }
39
- writeFileSync(path.resolve(pathBase, `test.txt`), `test-1`);
40
- writeFileSync(path.resolve(path2, 'test-5.txt'), 'test-5');
41
- }
42
-
43
- export function getPathForFile(fileName: string) {
44
- return path.resolve(__dirname, '..', '..', '..', 'test-assets', fileName);
12
+ const publicKeyPath = path.resolve(testAssetsPath, 'public-key.pem');
13
+ const privateKeyPath = path.resolve(testAssetsPath, 'private-key.pem');
14
+ if (!existsSync(publicKeyPath) || !existsSync(privateKeyPath)) {
15
+ console.error('SSH keys does not exist, see pl-deployments/README.md and regenerate them');
16
+ process.exit(1);
45
17
  }
46
18
 
47
- export function generateKeys() {
48
- const keys = ssh.utils.generateKeyPairSync('ecdsa', { bits: 256, comment: 'node.js rules!', passphrase: 'password', cipher: 'aes256-cbc' });
49
- if (!existsSync(publicKeyPath) || !existsSync(privateKeyPath)) {
50
- writeFileSync(publicKeyPath, keys.public);
51
- writeFileSync(privateKeyPath, keys.private);
52
- }
53
- }
54
-
55
- export function initPrivateKey(): string {
56
- generateKeys();
19
+ export function readPrivateKey(): string {
57
20
  return readFileSync(privateKeyPath, { encoding: 'utf-8' });
58
21
  }
59
22
 
60
23
  export async function initContainer(name: string): Promise<StartedTestContainer> {
61
- await createTestDirForRecursiveUpload();
62
-
63
24
  const imageName = `pl-ssh-test-container-${name}:1.0.0`;
64
25
  const containerName = `pl-ssh-test-${name}`;
65
26
 
66
- try {
67
- const container = new GenericContainer(imageName)
68
- .withExposedPorts(...SSH_PORT)
69
- .withReuse()
70
- .withName(containerName);
71
-
72
- return await container.start();
73
- } catch {
74
- console.log('No worries, creating a new container');
27
+ console.log('No worries, creating a new container');
75
28
 
76
- generateKeys();
77
- const container = await GenericContainer
78
- .fromDockerfile(path.resolve(__dirname, '..', '..', '..'))
79
- .withCache(true)
80
- .build(imageName, { deleteOnExit: false });
29
+ const container = await GenericContainer
30
+ .fromDockerfile(path.resolve(__dirname, '..', '..', '..'))
31
+ .withCache(true)
32
+ .build(imageName, { deleteOnExit: false });
81
33
 
82
- return await container
83
- .withExposedPorts(...SSH_PORT)
84
- .withReuse()
85
- .withName(containerName)
86
- .start();
87
- }
34
+ return await container
35
+ .withExposedPorts(...SSH_PORT)
36
+ .withReuse()
37
+ .withName(containerName)
38
+ .start();
88
39
  }
89
40
 
90
41
  export function getContainerHostAndPort(container: StartedTestContainer) {
@@ -96,7 +47,7 @@ export function getContainerHostAndPort(container: StartedTestContainer) {
96
47
 
97
48
  function logToFile(message: string) {
98
49
  const logFileName = 'log.txt';
99
- const logFilePath = path.join(__dirname, '..', '..', '..', 'test-assets', logFileName);
50
+ const logFilePath = path.join(testAssetsPath, logFileName);
100
51
 
101
52
  const timestamp = new Date().toISOString();
102
53
  const logMessage = `[${timestamp}] ${message}\n`;
@@ -110,7 +61,7 @@ function logToFile(message: string) {
110
61
 
111
62
  export function getConnectionForSsh(container: StartedTestContainer, debug: boolean = false): ConnectConfig {
112
63
  const hostData = getContainerHostAndPort(container);
113
- const privateKey = initPrivateKey();
64
+ const privateKey = readPrivateKey();
114
65
  const config = {
115
66
  host: hostData.host,
116
67
  port: hostData.port,
@@ -125,16 +76,4 @@ export function getConnectionForSsh(container: StartedTestContainer, debug: bool
125
76
 
126
77
  export async function cleanUp(container: StartedTestContainer) {
127
78
  await container.stop();
128
-
129
- if (existsSync(localFileUpload)) {
130
- unlinkSync(localFileUpload);
131
- }
132
-
133
- if (existsSync(localFileDownload)) {
134
- unlinkSync(localFileDownload);
135
- }
136
-
137
- if (existsSync(recUpload)) {
138
- await rm(recUpload, { recursive: true });
139
- }
140
79
  }
@@ -1,5 +1,5 @@
1
1
  import { describe, it, beforeAll, expect, afterAll } from 'vitest';
2
- import { initContainer, getConnectionForSsh, cleanUp as cleanUpT } from './common-utils';
2
+ import { initContainer, getConnectionForSsh, cleanUp as cleanUpT, testAssetsPath } from './common-utils';
3
3
  import { SshPl } from '../pl';
4
4
  import upath from 'upath';
5
5
  import { getDefaultPlVersion } from '../../common/pl_version';
@@ -10,19 +10,11 @@ import { ConsoleLoggerAdapter } from '@milaboratories/ts-helpers';
10
10
  import * as plpath from '../pl_paths';
11
11
 
12
12
  let sshPl: SshPl;
13
- const testContainer = await initContainer('pl');
14
13
 
15
- const downloadDestination = upath.resolve(__dirname, '..', '..', '..', 'test-assets', 'downloads');
14
+ const testContainer = await initContainer('pl');
16
15
 
17
- async function cleanUp() {
18
- const version = getDefaultPlVersion();
19
- const arch = await sshPl.getArch();
20
- const tgzName = 'supervisord-0.7.3';
16
+ const downloadDestination = upath.resolve(testAssetsPath, 'downloads');
21
17
 
22
- unlinkSync(`${downloadDestination}/pl-${version}-${newArch(arch!.arch)}.tgz`);
23
- unlinkSync(`${downloadDestination}/${tgzName}-${newArch(arch!.arch)}.tgz`);
24
- rmSync(downloadDestination, { recursive: true });
25
- }
26
18
  beforeAll(async () => {
27
19
  const logger = new ConsoleLoggerAdapter();
28
20
  sshPl = await SshPl.init(logger, getConnectionForSsh(testContainer));
@@ -240,6 +232,16 @@ describe('SshPl', async () => {
240
232
  });
241
233
  });
242
234
 
235
+ async function cleanUp() {
236
+ const version = getDefaultPlVersion();
237
+ const arch = await sshPl.getArch();
238
+ const tgzName = 'supervisord-0.7.3';
239
+
240
+ unlinkSync(`${downloadDestination}/pl-${version}-${newArch(arch!.arch)}.tgz`);
241
+ unlinkSync(`${downloadDestination}/${tgzName}-${newArch(arch!.arch)}.tgz`);
242
+ rmSync(downloadDestination, { recursive: true });
243
+ }
244
+
243
245
  afterAll(async () => {
244
246
  await cleanUp();
245
247
  await cleanUpT(testContainer);
@@ -1,20 +1,25 @@
1
1
  import type { StartedTestContainer } from 'testcontainers';
2
2
  import { describe, it, beforeAll, afterAll, expect } from 'vitest';
3
- import { writeFileSync, readFileSync } from 'fs';
3
+ import * as fs from 'node:fs/promises';
4
4
  import { SshClient } from '../ssh';
5
5
  import ssh from 'ssh2';
6
- import { downloadsFolder, cleanUp, getConnectionForSsh, getContainerHostAndPort, initContainer, localFileDownload, localFileUpload } from './common-utils';
6
+ import { cleanUp, getConnectionForSsh, getContainerHostAndPort, initContainer, testAssetsPath } from './common-utils';
7
7
  import { ConsoleLoggerAdapter } from '@milaboratories/ts-helpers';
8
+ import path from 'path';
9
+ import { fileExists } from '@milaboratories/ts-helpers';
8
10
 
9
11
  let client: SshClient;
10
12
  let testContainer: StartedTestContainer;
11
13
 
14
+ export const localFileUpload = path.resolve(testAssetsPath, 'test-file.txt');
15
+ export const localFileDownload = path.resolve(testAssetsPath, 'test-file-download.txt');
16
+ export const downloadsFolder = path.resolve(testAssetsPath, 'downloads');
17
+ export const recUpload = path.resolve(testAssetsPath, 'downloads', 'rec-upload');
18
+
12
19
  beforeAll(async () => {
13
- // FIXME: sometimes it fails with the error here.
14
- // Then we will have a container available in docker ps.
15
- // If it happened, try to retry to build the container the second time here and ask me (Gleb).
16
20
  testContainer = await initContainer('ssh');
17
21
  client = await SshClient.init(new ConsoleLoggerAdapter(), getConnectionForSsh(testContainer));
22
+ await createTestDirForRecursiveUpload();
18
23
  }, 200000);
19
24
 
20
25
  describe('SSH Tests', () => {
@@ -94,7 +99,7 @@ describe('SSH Tests', () => {
94
99
  it('should upload a file to the SSH server', async () => {
95
100
  const data = `Test data ${new Date().getTime()}`;
96
101
  const remoteFile = '/home/pl-doctor/uploaded-file.txt';
97
- writeFileSync(localFileUpload, data);
102
+ await fs.writeFile(localFileUpload, data);
98
103
  const result = await client.uploadFile(localFileUpload, remoteFile);
99
104
  expect(result).toBe(true);
100
105
  const execResult = await testContainer.exec(['cat', remoteFile]);
@@ -107,14 +112,14 @@ describe('SSH Tests', () => {
107
112
  // const localFile = './test-file-upload.txt';
108
113
  const remoteFile = '/home/pl-doctor/uploaded-file.txt';
109
114
 
110
- writeFileSync(localFileDownload, data);
115
+ await fs.writeFile(localFileDownload, data);
111
116
 
112
117
  const uploadResult = await client.uploadFile(localFileDownload, remoteFile);
113
118
  expect(uploadResult).toBe(true);
114
119
 
115
120
  const downloadResult = await client.downloadFile(remoteFile, localFileDownload);
116
121
  expect(downloadResult).toBe(true);
117
- expect(readFileSync(localFileDownload, { encoding: 'utf-8' })).toBe(data);
122
+ expect(await fs.readFile(localFileDownload, { encoding: 'utf-8' })).toBe(data);
118
123
  });
119
124
 
120
125
  it('Simple server should forward remote SSH port to a local port', async () => {
@@ -246,6 +251,44 @@ describe('sshExec', () => {
246
251
  });
247
252
  });
248
253
 
254
+ export async function createTestDirForRecursiveUpload() {
255
+ const pathBase = path.resolve(testAssetsPath, 'downloads', 'rec-upload', 'sub-1');
256
+ const path2 = path.resolve(testAssetsPath, 'downloads', 'rec-upload', 'sub-1', 'sub-1-1');
257
+
258
+ await fs.mkdir(pathBase, { recursive: true });
259
+ await fs.mkdir(path2, { recursive: true });
260
+
261
+ for (let i = 0; i < 19; i++) {
262
+ const path2 = path.resolve(testAssetsPath, 'downloads', 'rec-upload', 'sub-1', `sub-1-${i}`);
263
+ await fs.mkdir(path2, { recursive: true });
264
+
265
+ for (let i = 0; i < 3; i++) {
266
+ await fs.writeFile(path.resolve(path2, `test-${i}.txt`), `test-${i}`);
267
+ }
268
+ }
269
+
270
+ for (let i = 1; i < 100; i++) {
271
+ await fs.writeFile(path.resolve(pathBase, `test-${i}.txt`), `test-${i}`);
272
+ }
273
+ await fs.writeFile(path.resolve(pathBase, `test.txt`), `test-1`);
274
+ await fs.writeFile(path.resolve(path2, 'test-5.txt'), 'test-5');
275
+ }
276
+
277
+ export async function cleanUpTestAssets() {
278
+ if (await fileExists(localFileUpload)) {
279
+ await fs.unlink(localFileUpload);
280
+ }
281
+
282
+ if (await fileExists(localFileDownload)) {
283
+ await fs.unlink(localFileDownload);
284
+ }
285
+
286
+ if (await fileExists(recUpload)) {
287
+ await fs.rm(recUpload, { recursive: true });
288
+ }
289
+ }
290
+
249
291
  afterAll(async () => {
292
+ await cleanUpTestAssets();
250
293
  await cleanUp(testContainer);
251
294
  }, 200000);