Skip to content

Commit 359dd62

Browse files
richardschneiderdaviddias
authored andcommitted
feat: graceful stop, fix stop for Windows (#205)
1 parent 4cc69a0 commit 359dd62

9 files changed

+68
-94
lines changed

Diff for: .gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,6 @@ dist
3838
docs
3939

4040
.idea
41+
42+
.nyc_output
43+
.vscode

Diff for: package.json

+12-12
Original file line numberDiff line numberDiff line change
@@ -74,42 +74,42 @@
7474
"license": "MIT",
7575
"dependencies": {
7676
"async": "^2.6.0",
77-
"boom": "^7.1.1",
77+
"boom": "^7.2.0",
7878
"debug": "^3.1.0",
7979
"detect-node": "^2.0.3",
8080
"hapi": "^16.6.2",
8181
"hat": "0.0.3",
82-
"ipfs-api": "^18.1.1",
83-
"ipfs-repo": "^0.18.5",
84-
"joi": "^13.0.2",
82+
"ipfs-api": "^18.1.2",
83+
"ipfs-repo": "^0.18.7",
84+
"joi": "^13.1.2",
8585
"lodash.clone": "^4.5.0",
8686
"lodash.defaults": "^4.2.0",
8787
"lodash.defaultsdeep": "^4.6.0",
8888
"multiaddr": "^3.0.2",
8989
"once": "^1.4.0",
90-
"readable-stream": "^2.3.3",
90+
"readable-stream": "^2.3.5",
9191
"rimraf": "^2.6.2",
9292
"safe-json-parse": "^4.0.0",
93-
"safe-json-stringify": "^1.0.4",
93+
"safe-json-stringify": "^1.1.0",
9494
"shutdown": "^0.3.0",
95-
"stream-http": "^2.7.2",
95+
"stream-http": "^2.8.0",
9696
"subcomandante": "^1.0.5",
9797
"superagent": "^3.8.2",
9898
"truthy": "0.0.1"
9999
},
100100
"devDependencies": {
101-
"aegir": "^12.4.0",
101+
"aegir": "^13.0.6",
102102
"chai": "^4.1.2",
103-
"cross-env": "^5.1.3",
103+
"cross-env": "^5.1.4",
104104
"detect-port": "^1.2.2",
105105
"dirty-chai": "^2.0.1",
106106
"go-ipfs-dep": "0.4.13",
107-
"ipfs": "^0.27.5",
107+
"ipfs": "~0.28.2",
108108
"is-running": "1.0.5",
109109
"mkdirp": "^0.5.1",
110110
"pre-commit": "^1.2.2",
111-
"proxyquire": "^1.8.0",
112-
"sinon": "^4.1.3",
111+
"proxyquire": "^2.0.0",
112+
"sinon": "^4.4.5",
113113
"superagent-mocker": "^0.5.2",
114114
"supertest": "^3.0.0"
115115
},

Diff for: src/factory-in-proc.js

+14-14
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,10 @@ class FactoryInProc {
6565
callback = options
6666
options = {}
6767
}
68-
const IPFS = this.exec
69-
new IPFS(options).version((err, _version) => {
70-
if (err) { callback(err) }
71-
callback(null, _version)
68+
69+
const node = new Node(options)
70+
node.once('ready', () => {
71+
node.version(callback)
7272
})
7373
}
7474

@@ -97,7 +97,6 @@ class FactoryInProc {
9797
}
9898

9999
const options = defaults({}, opts, defaultOptions)
100-
101100
options.init = typeof options.init !== 'undefined'
102101
? options.init
103102
: true
@@ -129,15 +128,16 @@ class FactoryInProc {
129128
}
130129

131130
const node = new Node(options)
132-
133-
series([
134-
(cb) => options.init
135-
? node.init(cb)
136-
: cb(),
137-
(cb) => options.start
138-
? node.start(options.args, cb)
139-
: cb()
140-
], (err) => callback(err, node))
131+
node.once('ready', () => {
132+
series([
133+
(cb) => options.init
134+
? node.init(cb)
135+
: cb(),
136+
(cb) => options.start
137+
? node.start(options.args, cb)
138+
: cb()
139+
], (err) => callback(err, node))
140+
})
141141
}
142142
}
143143

Diff for: src/ipfsd-daemon.js

+16-14
Original file line numberDiff line numberDiff line change
@@ -148,14 +148,13 @@ class Daemon {
148148
return callback(err)
149149
}
150150

151-
const self = this
152151
waterfall([
153152
(cb) => this.getConfig(cb),
154153
(conf, cb) => this.replaceConfig(defaults({}, this.opts.config, conf), cb)
155154
], (err) => {
156155
if (err) { return callback(err) }
157-
self.clean = false
158-
self.initialized = true
156+
this.clean = false
157+
this.initialized = true
159158
return callback()
160159
})
161160
})
@@ -270,8 +269,9 @@ class Daemon {
270269
/**
271270
* Kill the `ipfs daemon` process.
272271
*
273-
* First `SIGTERM` is sent, after 10.5 seconds `SIGKILL` is sent
274-
* if the process hasn't exited yet.
272+
* If the HTTP API is established, then send 'shutdown' command; otherwise,
273+
* process.kill(`SIGTERM`) is used. In either case, if the process
274+
* does not exit after 10.5 seconds then a `SIGKILL` is used.
275275
*
276276
* @param {function()} callback - Called when the process was killed.
277277
* @returns {undefined}
@@ -282,24 +282,26 @@ class Daemon {
282282
const timeout = setTimeout(() => {
283283
log('kill timeout, using SIGKILL', subprocess.pid)
284284
subprocess.kill('SIGKILL')
285-
callback()
286285
}, GRACE_PERIOD)
287286

288-
const disposable = this.disposable
289-
const clean = this.cleanup.bind(this)
290-
subprocess.once('close', () => {
287+
subprocess.once('exit', () => {
291288
log('killed', subprocess.pid)
292289
clearTimeout(timeout)
293290
this.subprocess = null
294291
this._started = false
295-
if (disposable) {
296-
return clean(callback)
292+
if (this.disposable) {
293+
return this.cleanup(callback)
297294
}
298-
callback()
295+
setImmediate(callback)
299296
})
300297

301-
log('killing', subprocess.pid)
302-
subprocess.kill('SIGTERM')
298+
if (this.api) {
299+
log('kill via api', subprocess.pid)
300+
this.api.shutdown(() => null)
301+
} else {
302+
log('killing', subprocess.pid)
303+
subprocess.kill('SIGTERM')
304+
}
303305
this.subprocess = null
304306
}
305307

Diff for: src/ipfsd-in-proc.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@ const createRepo = require('./utils/repo/create-nodejs')
66
const defaults = require('lodash.defaults')
77
const waterfall = require('async/waterfall')
88
const debug = require('debug')
9+
const EventEmitter = require('events')
910

1011
const log = debug('ipfsd-ctl:in-proc')
1112

13+
let IPFS = null
14+
1215
/**
1316
* ipfsd for a js-ipfs instance (aka in-process IPFS node)
1417
*/
15-
class Node {
18+
class Node extends EventEmitter {
1619
/**
1720
* Create a new node.
1821
*
@@ -21,9 +24,10 @@ class Node {
2124
* @returns {Node}
2225
*/
2326
constructor (opts) {
27+
super()
2428
this.opts = opts || {}
2529

26-
const IPFS = this.opts.exec
30+
IPFS = this.opts.exec
2731

2832
this.opts.args = this.opts.args || []
2933
this.path = this.opts.repoPath
@@ -68,6 +72,8 @@ class Node {
6872
EXPERIMENTAL: this.opts.EXPERIMENTAL,
6973
libp2p: this.opts.libp2p
7074
})
75+
76+
this.exec.once('ready', () => this.emit('ready'))
7177
}
7278

7379
/**

Diff for: test/add-retrieve.spec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ describe('data can be put and fetched', () => {
2121
let ipfsd
2222

2323
before(function (done) {
24-
this.timeout(20 * 1000)
24+
this.timeout(30 * 1000)
2525

2626
const f = IPFSFactory.create(dfOpts)
2727

Diff for: test/api.spec.js

+3-11
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,7 @@ const series = require('async/series')
1111
const multiaddr = require('multiaddr')
1212
const path = require('path')
1313
const DaemonFactory = require('../src')
14-
const os = require('os')
15-
1614
const isNode = require('detect-node')
17-
const isWindows = os.platform() === 'win32'
1815

1916
const tests = [
2017
{ type: 'go' },
@@ -40,15 +37,10 @@ describe('ipfsd.api for Daemons', () => {
4037
})
4138

4239
it('test the ipfsd.api', function (done) {
43-
this.timeout(20 * 1000)
40+
this.timeout(50 * 1000)
4441

4542
// TODO skip in browser - can we avoid using file system operations here?
46-
if (!isNode) { this.skip() }
47-
48-
// TODO: fix on Windows
49-
// - https://github.com/ipfs/js-ipfsd-ctl/pull/155#issuecomment-326970190
50-
// - https://github.com/ipfs/js-ipfs-api/issues/408
51-
if (isWindows) { return this.skip() }
43+
if (!isNode) { return this.skip() }
5244

5345
let ipfsd
5446
let api
@@ -112,7 +104,7 @@ describe('ipfsd.api for Daemons', () => {
112104
})
113105

114106
it('check if API and Gateway addrs are correct', function (done) {
115-
this.timeout(20 * 1000)
107+
this.timeout(50 * 1000)
116108

117109
df.spawn({
118110
config: config,

Diff for: test/spawn-options.spec.js

+8-18
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ const isNode = require('detect-node')
1313
const hat = require('hat')
1414
const IPFSFactory = require('../src')
1515
const JSIPFS = require('ipfs')
16-
const os = require('os')
17-
18-
const isWindows = os.platform() === 'win32'
1916

2017
const tests = [
2118
{ type: 'go', bits: 1024 },
@@ -30,7 +27,9 @@ const versions = {
3027
proc: jsVersion
3128
}
3229

33-
describe('Spawn options', () => {
30+
describe('Spawn options', function () {
31+
this.timeout(20 * 1000)
32+
3433
tests.forEach((fOpts) => describe(`${fOpts.type}`, () => {
3534
const VERSION_STRING = versions[fOpts.type]
3635
let f
@@ -43,7 +42,7 @@ describe('Spawn options', () => {
4342
it('f.version', function (done) {
4443
this.timeout(20 * 1000)
4544

46-
f.version({ type: fOpts.type }, (err, version) => {
45+
f.version({ type: fOpts.type, exec: fOpts.exec }, (err, version) => {
4746
expect(err).to.not.exist()
4847
if (fOpts.type === 'proc') { version = version.version }
4948
expect(version).to.be.eql(VERSION_STRING)
@@ -109,7 +108,7 @@ describe('Spawn options', () => {
109108
})
110109

111110
it('ipfsd.stop', function (done) {
112-
this.timeout(10 * 1000)
111+
this.timeout(20 * 1000)
113112

114113
ipfsd.stop(done)
115114
})
@@ -124,9 +123,6 @@ describe('Spawn options', () => {
124123
let ipfsd
125124

126125
it('f.spawn', function (done) {
127-
// TODO: wont work on windows until we get `/shutdown` implemented in js-ipfs
128-
if (isWindows) { this.skip() }
129-
130126
this.timeout(20 * 1000)
131127

132128
const options = {
@@ -147,9 +143,6 @@ describe('Spawn options', () => {
147143
})
148144

149145
it('ipfsd.start', function (done) {
150-
// TODO: wont work on windows until we get `/shutdown` implemented in js-ipfs
151-
if (isWindows) { this.skip() }
152-
153146
this.timeout(20 * 1000)
154147

155148
ipfsd.start((err, api) => {
@@ -161,9 +154,6 @@ describe('Spawn options', () => {
161154
})
162155

163156
it('ipfsd.stop', function (done) {
164-
// TODO: wont work on windows until we get `/shutdown` implemented in js-ipfs
165-
if (isWindows) { this.skip() }
166-
167157
this.timeout(20 * 1000)
168158

169159
ipfsd.stop(done)
@@ -199,7 +189,7 @@ describe('Spawn options', () => {
199189

200190
describe('custom config options', () => {
201191
it('custom config', function (done) {
202-
this.timeout(30 * 1000)
192+
this.timeout(50 * 1000)
203193

204194
const addr = '/ip4/127.0.0.1/tcp/5678'
205195
const swarmAddr1 = '/ip4/127.0.0.1/tcp/35666'
@@ -311,7 +301,7 @@ describe('Spawn options', () => {
311301
let ipfsd
312302

313303
it('spawn with pubsub', function (done) {
314-
this.timeout(30 * 1000)
304+
this.timeout(20 * 1000)
315305

316306
const options = {
317307
args: ['--enable-pubsub-experiment'],
@@ -344,7 +334,7 @@ describe('Spawn options', () => {
344334
})
345335

346336
it('ipfsd.stop', function (done) {
347-
this.timeout(10 * 1000)
337+
this.timeout(20 * 1000)
348338
ipfsd.stop(done)
349339
})
350340
})

0 commit comments

Comments
 (0)