@dxos/phoenix 0.1.58-main.88ec9da → 0.1.58-main.935caa2
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/lib/node/index.cjs +241 -91
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/types/src/daemon-manager.d.ts +17 -0
- package/dist/types/src/daemon-manager.d.ts.map +1 -0
- package/dist/types/src/daemon-manager.test.d.ts +2 -0
- package/dist/types/src/daemon-manager.test.d.ts.map +1 -0
- package/dist/types/src/defs.d.ts +1 -3
- package/dist/types/src/defs.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/utils.d.ts +3 -3
- package/dist/types/src/utils.d.ts.map +1 -1
- package/dist/types/src/watchdog.d.ts +24 -5
- package/dist/types/src/watchdog.d.ts.map +1 -1
- package/package.json +12 -9
- package/src/{phoenix.test.ts → daemon-manager.test.ts} +21 -17
- package/src/daemon-manager.ts +138 -0
- package/src/defs.ts +1 -3
- package/src/index.ts +1 -1
- package/src/utils.ts +18 -15
- package/src/watchdog.test.ts +10 -8
- package/src/watchdog.ts +109 -29
- package/dist/types/src/phoenix.d.ts +0 -16
- package/dist/types/src/phoenix.d.ts.map +0 -1
- package/dist/types/src/phoenix.test.d.ts +0 -2
- package/dist/types/src/phoenix.test.d.ts.map +0 -1
- package/src/phoenix.ts +0 -94
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon-manager.test.d.ts","sourceRoot":"","sources":["../../../src/daemon-manager.test.ts"],"names":[],"mappings":""}
|
package/dist/types/src/defs.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
export declare const LOCK_TIMEOUT = 1000;
|
|
2
2
|
export declare const LOCK_CHECK_INTERVAL = 50;
|
|
3
|
-
export declare const
|
|
4
|
-
export declare const WATCHDOG_STOP_TIMEOUT = 1000;
|
|
5
|
-
export declare const WATCHDOG_CHECK_INTERVAL = 50;
|
|
3
|
+
export declare const DAEMON_START_TIMEOUT = 10000;
|
|
6
4
|
//# sourceMappingURL=defs.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defs.d.ts","sourceRoot":"","sources":["../../../src/defs.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,YAAY,OAAQ,CAAC;AAClC,eAAO,MAAM,mBAAmB,KAAK,CAAC;AACtC,eAAO,MAAM,
|
|
1
|
+
{"version":3,"file":"defs.d.ts","sourceRoot":"","sources":["../../../src/defs.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,YAAY,OAAQ,CAAC;AAClC,eAAO,MAAM,mBAAmB,KAAK,CAAC;AACtC,eAAO,MAAM,oBAAoB,QAAS,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAQA,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAQA,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const
|
|
2
|
-
export declare const
|
|
3
|
-
export declare const
|
|
1
|
+
export declare const waitForLockAcquisition: (lockFile: string) => Promise<boolean>;
|
|
2
|
+
export declare const waitForLockFileBeingFilledWithInfo: (lockFile: string) => Promise<boolean>;
|
|
3
|
+
export declare const waitForLockRelease: (lockFile: string) => Promise<boolean>;
|
|
4
4
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/utils.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,sBAAsB,aAAoB,MAAM,qBAMzD,CAAC;AAEL,eAAO,MAAM,kCAAkC,aAAoB,MAAM,qBAMrE,CAAC;AAEL,eAAO,MAAM,kBAAkB,aAAoB,MAAM,qBAMrD,CAAC"}
|
|
@@ -1,36 +1,55 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
+
export type DaemonInfo = {
|
|
3
|
+
pid: number;
|
|
4
|
+
command: string;
|
|
5
|
+
args: string[];
|
|
6
|
+
cwd: string;
|
|
7
|
+
timestamp: number;
|
|
8
|
+
};
|
|
2
9
|
export type ProcessInfo = WatchDogParams & {
|
|
3
10
|
pid?: number;
|
|
4
|
-
|
|
11
|
+
timestamp?: number;
|
|
5
12
|
restarts?: number;
|
|
6
13
|
running?: boolean;
|
|
7
14
|
};
|
|
8
|
-
export type
|
|
9
|
-
|
|
10
|
-
|
|
15
|
+
export type Lock = {
|
|
16
|
+
lockFile: string;
|
|
17
|
+
};
|
|
18
|
+
export type Logs = {
|
|
11
19
|
logFile: string;
|
|
12
20
|
errFile: string;
|
|
21
|
+
};
|
|
22
|
+
export type ChildParams = {
|
|
23
|
+
uid: string;
|
|
13
24
|
maxRestarts?: number | undefined;
|
|
14
25
|
killTree?: boolean | undefined;
|
|
15
|
-
command
|
|
26
|
+
command?: string;
|
|
16
27
|
args?: string[] | undefined;
|
|
17
28
|
env?: NodeJS.ProcessEnv | undefined;
|
|
18
29
|
cwd?: string | undefined;
|
|
19
30
|
shell?: boolean | undefined;
|
|
20
31
|
};
|
|
32
|
+
export type WatchDogParams = ChildParams & Lock & Logs;
|
|
21
33
|
export declare class WatchDog {
|
|
22
34
|
private readonly _params;
|
|
23
35
|
private _lock?;
|
|
24
36
|
private _child?;
|
|
25
37
|
private _restarts;
|
|
38
|
+
private _processCtx?;
|
|
26
39
|
constructor(_params: WatchDogParams);
|
|
27
40
|
start(): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Sends SIGINT to the child process and the tree it spawned (if `killTree` param is `true`).
|
|
43
|
+
*/
|
|
44
|
+
stop(): Promise<void>;
|
|
28
45
|
/**
|
|
29
46
|
* Sends SIGKILL to the child process and the tree it spawned (if `killTree` param is `true`).
|
|
30
47
|
*/
|
|
31
48
|
kill(): Promise<void>;
|
|
32
49
|
restart(): Promise<void>;
|
|
33
50
|
_killWithSignal(signal: number | NodeJS.Signals): Promise<void>;
|
|
51
|
+
private _acquireLock;
|
|
52
|
+
private _releaseLock;
|
|
34
53
|
private _log;
|
|
35
54
|
private _err;
|
|
36
55
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watchdog.d.ts","sourceRoot":"","sources":["../../../src/watchdog.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"watchdog.d.ts","sourceRoot":"","sources":["../../../src/watchdog.ts"],"names":[],"mappings":";AAkBA,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,cAAc,GAAG;IACzC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,IAAI,GAAG;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,IAAI,GAAG;IAIjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,MAAM,CAAC;IAKZ,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAM/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAM5B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GAAG,SAAS,CAAC;IACpC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,IAAI,GAAG,IAAI,CAAC;AAEvD,qBAAa,QAAQ;IAMP,OAAO,CAAC,QAAQ,CAAC,OAAO;IALpC,OAAO,CAAC,KAAK,CAAC,CAAa;IAC3B,OAAO,CAAC,MAAM,CAAC,CAAiC;IAChD,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,WAAW,CAAC,CAAU;gBAED,OAAO,EAAE,cAAc;IAG9C,KAAK;IAmDX;;OAEG;IAEG,IAAI;IAUV;;OAEG;IAEG,IAAI;IAUJ,OAAO;IAWP,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,OAAO;YAuBvC,YAAY;YAQZ,YAAY;IAS1B,OAAO,CAAC,IAAI;IAOZ,OAAO,CAAC,IAAI;CAOb"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/phoenix",
|
|
3
|
-
"version": "0.1.58-main.
|
|
3
|
+
"version": "0.1.58-main.935caa2",
|
|
4
4
|
"description": "Basic node daemon.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -13,17 +13,20 @@
|
|
|
13
13
|
"src"
|
|
14
14
|
],
|
|
15
15
|
"dependencies": {
|
|
16
|
+
"fs-extra": "^8.1.0",
|
|
16
17
|
"pkg-up": "^3.1.0",
|
|
17
|
-
"
|
|
18
|
-
"@dxos/
|
|
19
|
-
"@dxos/
|
|
20
|
-
"@dxos/
|
|
21
|
-
"@dxos/
|
|
22
|
-
"@dxos/
|
|
23
|
-
"@dxos/
|
|
24
|
-
"@dxos/
|
|
18
|
+
"ps-tree": "^1.2.0",
|
|
19
|
+
"@dxos/async": "0.1.58-main.935caa2",
|
|
20
|
+
"@dxos/context": "0.1.58-main.935caa2",
|
|
21
|
+
"@dxos/invariant": "0.1.58-main.935caa2",
|
|
22
|
+
"@dxos/keys": "0.1.58-main.935caa2",
|
|
23
|
+
"@dxos/lock-file": "0.1.58-main.935caa2",
|
|
24
|
+
"@dxos/log": "0.1.58-main.935caa2",
|
|
25
|
+
"@dxos/node-std": "0.1.58-main.935caa2",
|
|
26
|
+
"@dxos/util": "0.1.58-main.935caa2"
|
|
25
27
|
},
|
|
26
28
|
"devDependencies": {
|
|
29
|
+
"@types/fs-extra": "^9.0.4",
|
|
27
30
|
"@types/node": "^18.11.9",
|
|
28
31
|
"@types/ps-tree": "^1.1.2"
|
|
29
32
|
},
|
|
@@ -3,16 +3,18 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { expect } from 'chai';
|
|
6
|
+
import fse from 'fs-extra';
|
|
6
7
|
import { spawn } from 'node:child_process';
|
|
7
8
|
import { existsSync, readFileSync } from 'node:fs';
|
|
8
9
|
import { join } from 'node:path';
|
|
9
10
|
import waitForExpect from 'wait-for-expect';
|
|
10
11
|
|
|
11
12
|
import { Trigger } from '@dxos/async';
|
|
13
|
+
import { PublicKey } from '@dxos/keys';
|
|
12
14
|
import { afterTest, describe, test } from '@dxos/test';
|
|
13
15
|
|
|
14
|
-
import {
|
|
15
|
-
import { TEST_DIR,
|
|
16
|
+
import { DaemonManager } from './daemon-manager';
|
|
17
|
+
import { TEST_DIR, neverEndingProcess } from './testing-utils';
|
|
16
18
|
|
|
17
19
|
describe('DaemonManager', () => {
|
|
18
20
|
test('kill process by pid', async () => {
|
|
@@ -28,23 +30,21 @@ describe('DaemonManager', () => {
|
|
|
28
30
|
});
|
|
29
31
|
|
|
30
32
|
// Fails on CI
|
|
31
|
-
test('start/stop detached watchdog', async () => {
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
test.skip('start/stop detached watchdog', async () => {
|
|
34
|
+
const uid = `test-${PublicKey.random().toHex()}`;
|
|
35
|
+
const root = join(TEST_DIR, uid);
|
|
36
|
+
afterTest(() => {
|
|
37
|
+
fse.removeSync(root);
|
|
38
|
+
});
|
|
37
39
|
|
|
38
40
|
// Start
|
|
39
41
|
{
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
+
const manager = new DaemonManager(root);
|
|
43
|
+
const params = await manager.start({
|
|
44
|
+
uid,
|
|
42
45
|
command: 'node',
|
|
43
46
|
args: ['-e', `(${neverEndingProcess.toString()})()`],
|
|
44
47
|
maxRestarts: 0,
|
|
45
|
-
pidFile,
|
|
46
|
-
logFile,
|
|
47
|
-
errFile,
|
|
48
48
|
});
|
|
49
49
|
|
|
50
50
|
await waitForExpect(() => {
|
|
@@ -56,13 +56,17 @@ describe('DaemonManager', () => {
|
|
|
56
56
|
|
|
57
57
|
// Stop
|
|
58
58
|
{
|
|
59
|
-
const
|
|
60
|
-
|
|
59
|
+
const manager = new DaemonManager(root);
|
|
60
|
+
const info = await manager.list();
|
|
61
|
+
expect(info.length).to.equal(1);
|
|
62
|
+
expect(info[0].running).to.be.true;
|
|
63
|
+
expect(info[0].uid).to.equal(uid);
|
|
64
|
+
expect(await manager.isRunning(uid)).to.be.true;
|
|
61
65
|
|
|
62
|
-
await
|
|
66
|
+
const params = await manager.stop(uid);
|
|
63
67
|
|
|
64
68
|
await waitForExpect(() => {
|
|
65
|
-
const logs = readFileSync(logFile, { encoding: 'utf-8' });
|
|
69
|
+
const logs = readFileSync(params.logFile, { encoding: 'utf-8' });
|
|
66
70
|
expect(logs).to.contain('Stopped with exit code');
|
|
67
71
|
}, 1000);
|
|
68
72
|
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { fork } from 'node:child_process';
|
|
6
|
+
import { existsSync, mkdirSync, readFileSync, unlinkSync } from 'node:fs';
|
|
7
|
+
import { readdir } from 'node:fs/promises';
|
|
8
|
+
import { dirname, join } from 'node:path';
|
|
9
|
+
import pkgUp from 'pkg-up';
|
|
10
|
+
|
|
11
|
+
import { invariant } from '@dxos/invariant';
|
|
12
|
+
import { LockFile } from '@dxos/lock-file';
|
|
13
|
+
import { log } from '@dxos/log';
|
|
14
|
+
|
|
15
|
+
import { waitForLockAcquisition, waitForLockFileBeingFilledWithInfo, waitForLockRelease } from './utils';
|
|
16
|
+
import { ChildParams, Logs, Lock, ProcessInfo, WatchDogParams } from './watchdog';
|
|
17
|
+
|
|
18
|
+
const LOCK_FILE_NAME = 'lockfile';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Params to start a daemon.
|
|
22
|
+
* User have no control over the lock file.
|
|
23
|
+
*/
|
|
24
|
+
export type StartParams = ChildParams & Partial<Logs>;
|
|
25
|
+
|
|
26
|
+
export class DaemonManager {
|
|
27
|
+
constructor(private readonly _rootPath: string) {
|
|
28
|
+
if (!existsSync(join(_rootPath, 'profile'))) {
|
|
29
|
+
mkdirSync(join(_rootPath, 'profile'), { recursive: true });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private _getConfigFiles(uid: string): Logs & Lock {
|
|
34
|
+
const defaultConfigDir = join(this._rootPath, 'profile', uid);
|
|
35
|
+
if (!existsSync(defaultConfigDir)) {
|
|
36
|
+
mkdirSync(defaultConfigDir, { recursive: true });
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
lockFile: join(defaultConfigDir, LOCK_FILE_NAME),
|
|
40
|
+
logFile: join(defaultConfigDir, 'file.log'),
|
|
41
|
+
errFile: join(defaultConfigDir, 'err.log'),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async start(params: StartParams) {
|
|
46
|
+
invariant(params.command, 'command is required');
|
|
47
|
+
const watchDogParams: WatchDogParams = {
|
|
48
|
+
...this._getConfigFiles(params.uid),
|
|
49
|
+
...params,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
{
|
|
53
|
+
// Create log folders.
|
|
54
|
+
mkdirSync(dirname(watchDogParams.logFile), { recursive: true });
|
|
55
|
+
mkdirSync(dirname(watchDogParams.errFile), { recursive: true });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
{
|
|
59
|
+
// Clear stale lock file if process is not running.
|
|
60
|
+
if (await LockFile.isLocked(watchDogParams.lockFile)) {
|
|
61
|
+
throw new Error('Lock file is already locked.');
|
|
62
|
+
}
|
|
63
|
+
unlinkSync(watchDogParams.lockFile);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const watchdogPath = join(dirname(pkgUp.sync({ cwd: __dirname })!), 'bin', 'watchdog');
|
|
67
|
+
|
|
68
|
+
const watchDog = fork(watchdogPath, [JSON.stringify(watchDogParams)], {
|
|
69
|
+
detached: true,
|
|
70
|
+
cwd: __dirname,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
watchDog.on('exit', (code, signal) => {
|
|
74
|
+
if (code && code !== 0) {
|
|
75
|
+
log.error('Monitor died unexpectedly', { code, signal });
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
await waitForLockAcquisition(watchDogParams.lockFile);
|
|
80
|
+
await waitForLockFileBeingFilledWithInfo(watchDogParams.lockFile);
|
|
81
|
+
|
|
82
|
+
watchDog.disconnect();
|
|
83
|
+
watchDog.unref();
|
|
84
|
+
|
|
85
|
+
return this.getInfo(params.uid);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async stop(uid: string, force?: boolean) {
|
|
89
|
+
const lockFile = this._getConfigFiles(uid).lockFile;
|
|
90
|
+
const processInfo = JSON.parse(readFileSync(lockFile, { encoding: 'utf-8' }));
|
|
91
|
+
try {
|
|
92
|
+
if (force) {
|
|
93
|
+
process.kill(processInfo.pid, 'SIGKILL');
|
|
94
|
+
} else {
|
|
95
|
+
process.kill(processInfo.pid, 'SIGINT');
|
|
96
|
+
}
|
|
97
|
+
} catch (err) {
|
|
98
|
+
invariant(err instanceof Error, 'Invalid error type.');
|
|
99
|
+
if (!err.name.includes('ESRCH') && !err.message.includes('ESRCH')) {
|
|
100
|
+
throw err;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
await waitForLockRelease(lockFile);
|
|
104
|
+
|
|
105
|
+
return this.getInfo(uid);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async list(): Promise<ProcessInfo[]> {
|
|
109
|
+
const uids = (await readdir(join(this._rootPath, 'profile'))).filter((uid) => !uid.startsWith('.'));
|
|
110
|
+
|
|
111
|
+
return Promise.all(
|
|
112
|
+
uids.map(async (uid) => {
|
|
113
|
+
return this.getInfo(uid);
|
|
114
|
+
}),
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async getInfo(uid: string): Promise<ProcessInfo> {
|
|
119
|
+
const files = this._getConfigFiles(uid);
|
|
120
|
+
|
|
121
|
+
let info: ProcessInfo = { running: await LockFile.isLocked(files.lockFile), uid, ...files };
|
|
122
|
+
if (existsSync(files.lockFile)) {
|
|
123
|
+
try {
|
|
124
|
+
info = {
|
|
125
|
+
...info,
|
|
126
|
+
...JSON.parse(readFileSync(files.lockFile, { encoding: 'utf-8' })),
|
|
127
|
+
};
|
|
128
|
+
} catch (err) {}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return info;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async isRunning(uid: string): Promise<boolean> {
|
|
135
|
+
const lockFile = this._getConfigFiles(uid).lockFile;
|
|
136
|
+
return LockFile.isLocked(lockFile);
|
|
137
|
+
}
|
|
138
|
+
}
|
package/src/defs.ts
CHANGED
package/src/index.ts
CHANGED
package/src/utils.ts
CHANGED
|
@@ -2,30 +2,33 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { readFileSync } from 'node:fs';
|
|
6
6
|
|
|
7
7
|
import { waitForCondition } from '@dxos/async';
|
|
8
|
+
import { LockFile } from '@dxos/lock-file';
|
|
8
9
|
|
|
9
|
-
import {
|
|
10
|
+
import { LOCK_CHECK_INTERVAL, LOCK_TIMEOUT } from './defs';
|
|
10
11
|
|
|
11
|
-
export const
|
|
12
|
+
export const waitForLockAcquisition = async (lockFile: string) =>
|
|
12
13
|
waitForCondition({
|
|
13
|
-
condition: () =>
|
|
14
|
-
timeout:
|
|
15
|
-
interval:
|
|
14
|
+
condition: async () => await LockFile.isLocked(lockFile),
|
|
15
|
+
timeout: LOCK_TIMEOUT,
|
|
16
|
+
interval: LOCK_CHECK_INTERVAL,
|
|
17
|
+
error: new Error('Lock file is not being acquired.'),
|
|
16
18
|
});
|
|
17
19
|
|
|
18
|
-
export const
|
|
20
|
+
export const waitForLockFileBeingFilledWithInfo = async (lockFile: string) =>
|
|
19
21
|
waitForCondition({
|
|
20
|
-
condition: () =>
|
|
21
|
-
timeout:
|
|
22
|
-
interval:
|
|
22
|
+
condition: () => readFileSync(lockFile, { encoding: 'utf-8' }).includes('pid'),
|
|
23
|
+
timeout: LOCK_TIMEOUT,
|
|
24
|
+
interval: LOCK_CHECK_INTERVAL,
|
|
25
|
+
error: new Error('Lock file is not being propagated with info.'),
|
|
23
26
|
});
|
|
24
27
|
|
|
25
|
-
export const
|
|
28
|
+
export const waitForLockRelease = async (lockFile: string) =>
|
|
26
29
|
waitForCondition({
|
|
27
|
-
condition: () =>
|
|
28
|
-
timeout:
|
|
29
|
-
interval:
|
|
30
|
-
error: new Error('Lock file is not being
|
|
30
|
+
condition: async () => !(await LockFile.isLocked(lockFile)),
|
|
31
|
+
timeout: LOCK_TIMEOUT,
|
|
32
|
+
interval: LOCK_CHECK_INTERVAL,
|
|
33
|
+
error: new Error('Lock file is not being released.'),
|
|
31
34
|
});
|
package/src/watchdog.test.ts
CHANGED
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { expect } from 'chai';
|
|
6
|
-
import { existsSync } from 'node:fs';
|
|
7
6
|
import { join } from 'node:path';
|
|
8
7
|
|
|
8
|
+
import { asyncTimeout } from '@dxos/async';
|
|
9
|
+
import { LockFile } from '@dxos/lock-file';
|
|
9
10
|
import { afterTest, describe, test } from '@dxos/test';
|
|
10
11
|
|
|
11
12
|
import { TEST_DIR, clearFiles, neverEndingProcess } from './testing-utils';
|
|
@@ -14,25 +15,26 @@ import { WatchDog } from './watchdog';
|
|
|
14
15
|
describe('WatchDog', () => {
|
|
15
16
|
test('Start/stop process', async () => {
|
|
16
17
|
const runId = Math.random();
|
|
17
|
-
const
|
|
18
|
+
const lockFile = join(TEST_DIR, `lock-${runId}.lock`);
|
|
18
19
|
const logFile = join(TEST_DIR, `file-${runId}.log`);
|
|
19
20
|
const errFile = join(TEST_DIR, `err-${runId}.log`);
|
|
20
|
-
afterTest(() => clearFiles(
|
|
21
|
+
afterTest(() => clearFiles(lockFile, logFile, errFile));
|
|
21
22
|
|
|
22
23
|
const watchDog = new WatchDog({
|
|
24
|
+
uid: 'test',
|
|
23
25
|
command: 'node',
|
|
24
26
|
args: ['-e', `(${neverEndingProcess.toString()})()`],
|
|
25
|
-
|
|
27
|
+
lockFile,
|
|
26
28
|
logFile,
|
|
27
29
|
errFile,
|
|
28
30
|
});
|
|
29
31
|
|
|
30
|
-
expect(
|
|
32
|
+
expect(await asyncTimeout(LockFile.isLocked(lockFile), 1000)).to.be.false;
|
|
31
33
|
await watchDog.start();
|
|
32
34
|
|
|
33
|
-
expect(
|
|
35
|
+
expect(await asyncTimeout(LockFile.isLocked(lockFile), 1000)).to.be.true;
|
|
34
36
|
|
|
35
|
-
await watchDog.
|
|
36
|
-
expect(
|
|
37
|
+
await watchDog.stop();
|
|
38
|
+
expect(await asyncTimeout(LockFile.isLocked(lockFile), 1000)).to.be.false;
|
|
37
39
|
});
|
|
38
40
|
});
|