From ddd89fd9b789972f39329fe0c4f02184442a2520 Mon Sep 17 00:00:00 2001 From: akitaSummer Date: Thu, 25 Jan 2024 00:35:28 +0800 Subject: [PATCH] fix: fix cr --- Cargo.lock | 1 + integration/fixtures/utils/pids.js | 64 ++++++++++ integration/index.2.test.js | 131 ++++++--------------- packages/cli/bin/rapid.js | 12 +- packages/cli/lib/deamon.js | 162 ++++++++++++++------------ packages/cli/lib/index.js | 8 +- packages/cli/lib/nydusd/fuse_mode.js | 10 +- packages/cli/lib/nydusd/index.js | 8 +- packages/cli/lib/nydusd/nydusd_api.js | 2 + 9 files changed, 212 insertions(+), 186 deletions(-) create mode 100644 integration/fixtures/utils/pids.js diff --git a/Cargo.lock b/Cargo.lock index a548a53..f8171c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2330,6 +2330,7 @@ dependencies = [ "http-body-util", "httparse", "hyper 0.14.14", + "hyper 1.1.0", "hyper-util", "hyperlocal", "lazy_static", diff --git a/integration/fixtures/utils/pids.js b/integration/fixtures/utils/pids.js new file mode 100644 index 0000000..dfe6af7 --- /dev/null +++ b/integration/fixtures/utils/pids.js @@ -0,0 +1,64 @@ +'use strict'; +const execa = require('execa'); + +class Pids { + constructor(nodeModulesDir) { + this.nodeModulesDir = nodeModulesDir; + } + + async getPsSnapshot() { + try { + const { stdout } = await execa.command('ps aux'); + return stdout; + } catch (error) { + throw new Error(`Failed to execute 'ps aux': ${error.message}`, { cause: error }); + } + } + + async getPids() { + const pids = []; + + try { + const snapshot = await this.getPsSnapshot(); + + let overlayPattern; + + let nfsPattern; + + if (process.platform === 'linux') { + overlayPattern = new RegExp(`overlay.*?${this.nodeModulesDir}`, 'i'); + } else if (process.platform === 'darwin') { + overlayPattern = new RegExp(`unionfs.*?${this.nodeModulesDir}`, 'i'); + nfsPattern = new RegExp( + `/usr/local/bin/go-nfsv4.*?${this.nodeModulesDir}`, 'i' + ); + } + console.log('snapshot', snapshot); + + for (const line of snapshot.split('\n')) { + if (overlayPattern.test(line)) { + const fields = line.split(/\s+/); + if (fields.length >= 11) { + const pid = parseInt(fields[1], 10) || 0; + pids.push(pid); + } + } + if (process.platform === 'darwin') { + if (nfsPattern.test(line)) { + const fields = line.split(/\s+/); + if (fields.length >= 11) { + const pid = parseInt(fields[1], 10) || 0; + pids.push(pid); + } + } + } + } + + return pids; + } catch (error) { + throw new Error(`Failed to get PIDs: ${error.message}`, { cause: error }); + } + } +} + +exports.Pids = Pids; \ No newline at end of file diff --git a/integration/index.2.test.js b/integration/index.2.test.js index f9c3b2f..9abc55a 100644 --- a/integration/index.2.test.js +++ b/integration/index.2.test.js @@ -6,10 +6,9 @@ const assert = require('node:assert'); const coffee = require('coffee'); const semver = require('semver'); const execa = require('execa'); -const util = require('node:util'); -const exec = util.promisify(require('node:child_process').exec); -const { execSync } = require('node:child_process'); +const { setTimeout } = require('node:timers/promises'); const rapid = path.join(__dirname, '../node_modules/.bin/rapid'); +const { Pids } = require(path.join(__dirname, './fixtures/utils/pids')); const { clean, } = require('@cnpmjs/rapid'); @@ -18,58 +17,6 @@ const { forceExitDaemon, } = require('@cnpmjs/rapid/lib/nydusd/nydusd_api'); - -class Pids { - constructor(nodeModulesDir) { - this.nodeModulesDir = nodeModulesDir; - } - - async getPsSnapshot() { - try { - const { stdout } = await exec('ps aux'); - return stdout; - } catch (error) { - throw new Error(`Failed to execute 'ps aux': ${error.message}`); - } - } - - async getPids() { - const pids = []; - - try { - const snapshot = await this.getPsSnapshot(); - - const overlayPattern = new RegExp(`overlay.*?${this.nodeModulesDir}`, 'i'); - const nfsPattern = new RegExp( - `/usr/local/bin/go-nfsv4.*?${this.nodeModulesDir}`, 'i' - ); - - snapshot.split('\n').forEach(line => { - if (overlayPattern.test(line)) { - const fields = line.split(/\s+/); - if (fields.length >= 11) { - const pid = parseInt(fields[1], 10) || 0; - pids.push(pid); - } - } - - if (nfsPattern.test(line)) { - const fields = line.split(/\s+/); - if (fields.length >= 11) { - const pid = parseInt(fields[1], 10) || 0; - pids.push(pid); - } - } - }); - - return pids; - } catch (error) { - console.log(error); - throw new Error(`Failed to get PIDs: ${error.message}`); - } - } -} - describe('test/index.v2.test.js', () => { let cwd; @@ -203,44 +150,6 @@ describe('test/index.v2.test.js', () => { assert.strictEqual(require(path.join(cwd, 'node_modules', 'esbuild/package.json')).version, '0.15.14'); }); - it('deamon should working', async () => { - cwd = path.join(__dirname, './fixtures/esbuild'); - await coffee - .fork(rapid, [ - 'install', - '--ignore-scripts', - ], { - cwd, - }) - .debug() - .expect('code', 0) - .end(); - - const dirs = await fs.readdir(path.join(cwd, 'node_modules')); - assert.strictEqual(dirs.filter(dir => dir.includes('esbuild')).length, 2); - await assert.doesNotReject(fs.stat(path.join(cwd, 'node_modules/esbuild'))); - assert.strictEqual(require(path.join(cwd, 'node_modules', 'esbuild/package.json')).version, '0.15.14'); - - - const pidsInstance = new Pids(dirs); - let pids = await pidsInstance.getPids(); - assert(pids.length > 0); - pids.forEach(pid => { - try { - execSync(`kill -9 ${pid}`); - } catch (err) { - console.error(`Failed to kill process with PID ${pid}. Error: ${err.message}`); - } - }); - await new Promise(resolve => { - setTimeout(() => { - resolve(); - }, 10000); - }); - pids = await pidsInstance.getPids(); - assert(pids.length > 0); - }); - it('should install optional deps successfully use npminstall', async () => { cwd = path.join(__dirname, './fixtures/esbuild'); await coffee @@ -296,4 +205,40 @@ describe('test/index.v2.test.js', () => { assert(res.stdout.indexOf('integration/fixtures/esbuild/node_modules') === res.stdout.lastIndexOf('integration/fixtures/esbuild/node_modules')); }); + + describe('deamon', async () => { + it('should work', async () => { + cwd = path.join(__dirname, './fixtures/esbuild'); + await coffee + .fork(rapid, [ + 'install', + '--ignore-scripts', + ], { + cwd, + }) + .debug() + .expect('code', 0) + .end(); + + const dirs = await fs.readdir(path.join(cwd, 'node_modules')); + assert.strictEqual(dirs.filter(dir => dir.includes('esbuild')).length, 2); + await assert.doesNotReject(fs.stat(path.join(cwd, 'node_modules/esbuild'))); + assert.strictEqual(require(path.join(cwd, 'node_modules', 'esbuild/package.json')).version, '0.15.14'); + + const pidsInstance = new Pids('esbuild/node_modules'); + let pids = await pidsInstance.getPids(); + assert(pids.length > 0); + for (const pid of pids) { + await execa.command(`kill -9 ${pid}`); + } + await new Promise(resolve => { + setTimeout(() => { + resolve(); + }, 10000); + }); + pids = await pidsInstance.getPids(); + assert(pids.length > 0); + }); + }); + }); diff --git a/packages/cli/bin/rapid.js b/packages/cli/bin/rapid.js index 4d1add9..b5350a5 100755 --- a/packages/cli/bin/rapid.js +++ b/packages/cli/bin/rapid.js @@ -33,7 +33,7 @@ const argv = yargs type: 'array', default: [], }) - .option('ignore-deamon', { + .option('nodaemon', { describe: 'Will not run deamon', type: 'boolean', }); @@ -42,7 +42,7 @@ const argv = yargs const ignoreScripts = argv['ignore-scripts']; const mode = argv.by || NpmFsMode.NPM; const productionMode = argv.production || argv.omit.includes('dev') || process.env.NODE_ENV === 'production'; - const ignoreDeamon = argv['ignore-deamon']; + const nodaemon = argv.nodaemon; const cwd = process.cwd(); const pkgRes = await util.readPkgJSON(); @@ -62,7 +62,7 @@ const argv = yargs nydusMode: NYDUS_TYPE.FUSE, ignoreScripts, productionMode, - ignoreDeamon, + nodaemon, }); Alert.success('πŸš€ Success', [ @@ -80,15 +80,15 @@ const argv = yargs describe: 'Clean up the project', builder: yargs => { return yargs - .option('ignore-deamon', { + .option('nodaemon', { describe: 'Will not run deamon', type: 'boolean', }); }, handler: async argv => { const cwd = argv.path || process.cwd(); - const ignoreDeamon = argv['ignore-deamon']; - await clean({ nydusMode: NYDUS_TYPE.FUSE, cwd, force: true, ignoreDeamon }); + const nodaemon = argv.nodaemon; + await clean({ nydusMode: NYDUS_TYPE.FUSE, cwd, force: true, nodaemon }); console.log('[rapid] clean finished'); }, }) diff --git a/packages/cli/lib/deamon.js b/packages/cli/lib/deamon.js index 7ce527b..edaf92d 100644 --- a/packages/cli/lib/deamon.js +++ b/packages/cli/lib/deamon.js @@ -1,8 +1,8 @@ const urllib = require('urllib'); +const debug = require('node:util').debuglog('rapid:deamon'); const AutoLaunch = require('auto-launch'); const path = require('node:path'); const fs = require('node:fs/promises'); -const { chmodSync } = require('node:fs'); const { rsBindingPath } = require('@cnpmjs/binding'); const execa = require('execa'); @@ -19,13 +19,15 @@ const deamonDir = path.join(baseRapidModeDir(), 'project'); const metadataDir = path.join(deamonDir, 'metadata'); +// const deamonSocketPath = path.join(deamonDir, 'socket_path'); + const rapidDeamon = rsBindingPath ? path.join(rsBindingPath, 'rapid_deamon') : undefined; const destinationFilePath = path.join(deamonDir, 'rapid_deamon'); - +// TODO const daemonPoint = 'http://localhost:33889'; const aliveUrl = `${daemonPoint}/alive`; const killUrl = `${daemonPoint}/kill`; @@ -36,6 +38,8 @@ const checkDeamonAlive = async () => { try { const result = await urllib.request(`${aliveUrl}`, { method: 'GET', + // socketPath: deamonSocketPath, + timeout: 1000, }); return result.status === 200; } catch (_) { @@ -47,13 +51,20 @@ const delProject = async projectName => { let config; try { await fs.stat(metadataDir); + } catch (error) { + debug('delProject error: ', error); + return false; + } + + try { const configPath = path.join(metadataDir, `${projectName}.json`); const configBuffer = await fs.readFile(configPath); config = JSON.parse(configBuffer.toString()); await fs.rm(`${configPath}`); - } catch (_) { - return true; + } catch (error) { + debug('delProject error: ', error); + return false; } try { @@ -62,9 +73,11 @@ const delProject = async projectName => { data: { projectPath: config.projectPath }, dataType: 'json', contentType: 'json', + // socketPath: deamonSocketPath, }); - return result.status === 200; - } catch (_) { + return result.status === 200 && result.data?.code === 0; + } catch (error) { + debug('delProject error: ', error); return false; } }; @@ -78,43 +91,40 @@ const addProject = async config => { data: config, dataType: 'json', contentType: 'json', + // socketPath: deamonSocketPath, }); - return result.status === 200; + return result.status === 200 && result.data?.code === 0; } catch (_) { return false; } }; + const runDeamon = async () => { - const deamon = execa(destinationFilePath, [], { + execa(destinationFilePath, [], { detached: true, stdio: [ 'ignore', 'pipe', 'pipe' ], }); - await new Promise((resolve, reject) => { - let output = ''; - const expectedMain = 'deamon main is ready'; + let count = 0; - const expectedServer = 'deamon server is ready'; - - deamon.stdout.on('data', data => { - output += data.toString(); - - if (output.includes(expectedMain) && output.includes(expectedServer)) { - resolve(); - } - }); + while (count < 10) { + const res = await checkDeamonAlive(); + if (res) { + return true; + } + count++; + } - deamon.catch(e => { - reject(e); - }); - }); + return false; }; + const killDeamon = async () => { try { const result = await urllib.request(`${killUrl}`, { method: 'GET', + // socketPath: deamonSocketPath, }); return result.status === 200; } catch (_) { @@ -122,6 +132,59 @@ const killDeamon = async () => { } }; +const registerDeamon = async () => { + const nydusConfigPath = path.join(deamonDir, 'nydus_config.json'); + + await fs.writeFile(nydusConfigPath, JSON.stringify({ + nydusdBin: nydusd, + nydusdConfigFile, + nydusdMnt, + socketPath, + nydusdLogFile, + }, null, 2)); + + const logConfigPath = path.join(deamonDir, 'log4rs.yaml'); + + await fs.writeFile(logConfigPath, ` +refresh_rate: 86400 seconds + +appenders: + file: + kind: file + path: "${path.join(deamonDir, '/logs/rapid-deamon-output.log')}" + encoder: + pattern: "{d} - {l} - {m}{n}" + +root: + level: info + appenders: + - file + `); + await fs.copyFile(rapidDeamon, destinationFilePath); + + await fs.chmod(destinationFilePath, '777'); + + const deamonAutoLauncher = new AutoLaunch({ + name: 'rapid_deamon', + path: destinationFilePath, + mac: { + useLaunchAgent: true, + }, + }); + + deamonAutoLauncher.enable(); + + try { + const isEnabled = deamonAutoLauncher.isEnabled(); + if (isEnabled) return; + deamonAutoLauncher.enable(); + } catch (e) { + console.log(e); + } finally { + await runDeamon(); + } +}; + const initDeamon = async () => { const isRunning = await checkDeamonAlive(); if (isRunning) { @@ -136,56 +199,7 @@ const initDeamon = async () => { await fs.stat(destinationFilePath); await runDeamon(); } catch (e) { - const nydusConfigPath = path.join(deamonDir, 'nydus_config.json'); - - await fs.writeFile(nydusConfigPath, JSON.stringify({ - nydusdBin: nydusd, - nydusdConfigFile, - nydusdMnt, - socketPath, - nydusdLogFile, - }, null, 2)); - - const logConfigPath = path.join(deamonDir, 'log4rs.yaml'); - - await fs.writeFile(logConfigPath, ` - refresh_rate: 86400 seconds - - appenders: - file: - kind: file - path: "${path.join(deamonDir, '/logs/rapid-deamon-output.log')}" - encoder: - pattern: "{d} - {l} - {m}{n}" - - root: - level: info - appenders: - - file - `); - await fs.copyFile(rapidDeamon, destinationFilePath); - - chmodSync(destinationFilePath, '777'); - - const deamonAutoLauncher = new AutoLaunch({ - name: 'rapid_deamon', - path: destinationFilePath, - mac: { - useLaunchAgent: true, - }, - }); - - deamonAutoLauncher.enable(); - - try { - const isEnabled = deamonAutoLauncher.isEnabled(); - if (isEnabled) return; - deamonAutoLauncher.enable(); - } catch (e) { - console.log(e); - } finally { - await runDeamon(); - } + await registerDeamon(); } }; diff --git a/packages/cli/lib/index.js b/packages/cli/lib/index.js index a765512..c11e639 100644 --- a/packages/cli/lib/index.js +++ b/packages/cli/lib/index.js @@ -36,7 +36,7 @@ exports.install = async options => { cwd: mountedInfo.mountPoint, pkg: options.pkg, force: true, - ignoreDeamon: options.ignoreDeamon, + nodaemon: options.nodaemon, }); console.timeEnd(`[rapid] ${nodeModulesDir} already mounted, try to clean`); } @@ -58,7 +58,7 @@ exports.install = async options => { await downloadDependency.download(options); assert(Object.keys(packageLock).length, '[rapid] depsJSON invalid.'); - await nydusd.startNydusFs(options.nydusMode, options.cwd, options.pkg, options.ignoreDeamon); + await nydusd.startNydusFs(options.nydusMode, options.cwd, options.pkg, options.nodaemon); await util.ensureAccess(options.cwd, packageLock); @@ -71,7 +71,7 @@ exports.install = async options => { console.timeEnd('[rapid] run lifecycle scripts'); }; -exports.clean = async function clean({ nydusMode = NYDUS_TYPE.FUSE, cwd, force, pkg, ignoreDeamon }) { +exports.clean = async function clean({ nydusMode = NYDUS_TYPE.FUSE, cwd, force, pkg, nodaemon }) { const listInfo = await util.listMountInfo(); if (!listInfo.length) { console.log('[rapid] no mount info found.'); @@ -87,7 +87,7 @@ exports.clean = async function clean({ nydusMode = NYDUS_TYPE.FUSE, cwd, force, pkg = pkgRes.pkg; } - await nydusd.endNydusFs(nydusMode, cwd, pkg, force, ignoreDeamon); + await nydusd.endNydusFs(nydusMode, cwd, pkg, force, nodaemon); }; exports.list = async () => { diff --git a/packages/cli/lib/nydusd/fuse_mode.js b/packages/cli/lib/nydusd/fuse_mode.js index d847c14..2cd2e9d 100644 --- a/packages/cli/lib/nydusd/fuse_mode.js +++ b/packages/cli/lib/nydusd/fuse_mode.js @@ -30,10 +30,10 @@ const getProjectName = cwd => { return hashedFolderName; }; -async function startNydusFs(cwd, pkg, ignoreDeamon) { +async function startNydusFs(cwd, pkg, nodaemon) { await nydusdApi.initDaemon(); - if (!ignoreDeamon) { + if (!nodaemon) { await initDeamon(); } @@ -51,7 +51,7 @@ async function startNydusFs(cwd, pkg, ignoreDeamon) { console.log('[rapid] mount overlay, it may take a few seconds'); await mountOverlay(cwd, pkg, deamonConfig); - if (!ignoreDeamon) { + if (!nodaemon) { await addProject(deamonConfig); } } @@ -183,10 +183,10 @@ ${nodeModulesDir}`; config.overlays = overlays; } -async function endNydusFs(cwd, pkg, force = true, ignoreDeamon) { +async function endNydusFs(cwd, pkg, force = true, nodaemon) { const allPkgs = await getAllPkgPaths(cwd, pkg); const umountCmd = force ? 'umount -f' : 'umount'; - if (!ignoreDeamon) { + if (!nodaemon) { await delProject(getProjectName(cwd)); } await Promise.all(allPkgs.map(async pkgPath => { diff --git a/packages/cli/lib/nydusd/index.js b/packages/cli/lib/nydusd/index.js index 2499a7f..b92515e 100644 --- a/packages/cli/lib/nydusd/index.js +++ b/packages/cli/lib/nydusd/index.js @@ -31,20 +31,20 @@ exports.unregisterMode = function(mode) { fsImplMap[mode] = null; }; -exports.startNydusFs = async function(mode, cwd, pkg, ignoreDeamon) { +exports.startNydusFs = async function(mode, cwd, pkg, nodaemon) { const impl = fsImplMap[mode]; assert(impl, `can not find fs impl for mode: ${mode}`); - await impl.start(cwd, pkg, ignoreDeamon); + await impl.start(cwd, pkg, nodaemon); }; -exports.endNydusFs = async function(mode, cwd, pkg, force, ignoreDeamon) { +exports.endNydusFs = async function(mode, cwd, pkg, force, nodaemon) { if (!mode || mode === NYDUS_TYPE.NATIVE) { console.log('[rapid] nydusd is not running, skip clean'); return; } const impl = fsImplMap[mode]; assert(impl, `can not find fs impl for mode: ${mode}`); - await impl.end(cwd, pkg, force, ignoreDeamon); + await impl.end(cwd, pkg, force, nodaemon); }; exports.getNydusMode = async function(cwd) { diff --git a/packages/cli/lib/nydusd/nydusd_api.js b/packages/cli/lib/nydusd/nydusd_api.js index 470cfb1..7b3adb4 100644 --- a/packages/cli/lib/nydusd/nydusd_api.js +++ b/packages/cli/lib/nydusd/nydusd_api.js @@ -140,6 +140,7 @@ async function checkDaemon() { // δΌ˜ι›…ι€€ε‡Ί nydusd daemon async function exitDaemon() { try { + await killDeamon(); await urllib.request(`${daemonUrl}/exit`, { method: 'PUT', socketPath, @@ -166,6 +167,7 @@ async function forceExitDaemon() { } try { + await killDeamon(); await execa.command('killall -9 nydusd'); } catch (e) { // ignore, nydusd quits with error, but it's ok