From 7dd4e01641d9b0944b6f75586bb5089d92ec69fe Mon Sep 17 00:00:00 2001 From: Dmitriy Ryajov Date: Tue, 14 Nov 2017 01:28:34 -0800 Subject: [PATCH] feat: make js-ipfs daemon stop with same SIG as go-ipfs (#1067) * feat: pre-start throws if Adresses.Swarm is missing * feat: add SIGTERM handler for go-ipfs compatibility * fix: interupt message * test: adding for Addresses.Swarm empty * test: adding missing daemon tests * fix: exit with 0 on SIGINT & SIGTERM --- src/cli/commands/daemon.js | 10 ++-- src/core/components/pre-start.js | 16 +++--- test/cli/daemon.js | 86 ++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 10 deletions(-) diff --git a/src/cli/commands/daemon.js b/src/cli/commands/daemon.js index 4529e2af54..ba9ca6ad6a 100644 --- a/src/cli/commands/daemon.js +++ b/src/cli/commands/daemon.js @@ -40,14 +40,18 @@ module.exports = { print('Daemon is ready') }) - process.on('SIGINT', () => { - print('Received interrupt signal, shutting down..') + const cleanup = () => { + print(`Received interrupt signal, shutting down..`) httpAPI.stop((err) => { if (err) { throw err } process.exit(0) }) - }) + } + + // listen for graceful termination + process.on('SIGTERM', cleanup) + process.on('SIGINT', cleanup) } } diff --git a/src/core/components/pre-start.js b/src/core/components/pre-start.js index 4534513026..32017bd4f0 100644 --- a/src/core/components/pre-start.js +++ b/src/core/components/pre-start.js @@ -22,15 +22,17 @@ module.exports = function preStart (self) { (config, id, cb) => { self._peerInfo = new PeerInfo(id) - config.Addresses.Swarm.forEach((addr) => { - let ma = multiaddr(addr) + if (config.Addresses && config.Addresses.Swarm) { + config.Addresses.Swarm.forEach((addr) => { + let ma = multiaddr(addr) - if (ma.getPeerId()) { - ma = ma.encapsulate('/ipfs/' + self._peerInfo.id.toB58String()) - } + if (ma.getPeerId()) { + ma = ma.encapsulate('/ipfs/' + self._peerInfo.id.toB58String()) + } - self._peerInfo.multiaddrs.add(ma) - }) + self._peerInfo.multiaddrs.add(ma) + }) + } cb() } diff --git a/test/cli/daemon.js b/test/cli/daemon.js index 265a8892cd..adbaeb4ee2 100644 --- a/test/cli/daemon.js +++ b/test/cli/daemon.js @@ -4,11 +4,66 @@ const expect = require('chai').expect const clean = require('../utils/clean') const ipfsCmd = require('../utils/ipfs-exec') +const pull = require('pull-stream') +const toPull = require('stream-to-pull-stream') +const os = require('os') +const fs = require('fs') +const path = require('path') + +const isWindows = os.platform() === 'win32' + +const checkLock = (repo, cb) => { + // skip on windows + // https://github.com/ipfs/js-ipfsd-ctl/pull/155#issuecomment-326983530 + if (!isWindows) { + if (fs.existsSync(path.join(repo, 'repo.lock'))) { + cb(new Error('repo.lock not removed')) + } + if (fs.existsSync(path.join(repo, 'api'))) { + cb(new Error('api file not removed')) + } + } + cb() +} describe('daemon', () => { let repoPath let ipfs + const killSig = (sig) => { + let proc = null + return ipfs('init').then(() => { + proc = ipfs('daemon') + return new Promise((resolve, reject) => { + pull( + toPull(proc.stdout), + pull.collect((err, res) => { + expect(err).to.not.exist() + const data = res.toString() + if (data.includes(`Daemon is ready`)) { + if (proc.kill(sig)) { + resolve() + } else { + reject(new Error(`Unable to ${sig} process`)) + } + } + }) + ) + + pull( + toPull(proc.stderr), + pull.collect((err, res) => { + expect(err).to.not.exist() + const data = res.toString() + if (data.length > 0) { + reject(new Error(data)) + } + }) + ) + }) + }) + } + beforeEach(() => { repoPath = '/tmp/ipfs-test-not-found-' + Math.random().toString().substring(2, 8) ipfs = ipfsCmd(repoPath) @@ -16,6 +71,37 @@ describe('daemon', () => { afterEach(() => clean(repoPath)) + it(`don't crash if Addresses.Swarm is empty`, function (done) { + this.timeout(20000) + ipfs('init').then(() => { + return ipfs('config', 'Addresses', JSON.stringify({ + API: '/ip4/0.0.0.0/tcp/0', + Gateway: '/ip4/0.0.0.0/tcp/0' + }), '--json') + }).then(() => { + return ipfs('daemon') + }).then((res) => { + expect(res).to.have.string('Daemon is ready') + done() + }).catch((err) => { + done(err) + }) + }) + + it(`should handle SIGINT gracefully`, function (done) { + this.timeout(20000) + killSig('SIGINT').then(() => { + checkLock(repoPath, done) + }).catch(done) + }) + + it(`should handle SIGTERM gracefully`, function (done) { + this.timeout(20000) + killSig('SIGTERM').then(() => { + checkLock(repoPath, done) + }).catch(done) + }) + it('gives error if user hasn\'t run init before', (done) => { const expectedError = 'no initialized ipfs repo found in ' + repoPath