Skip to content

Commit

Permalink
fix: fix non-disposable and normalises behaviour (#379)
Browse files Browse the repository at this point in the history
This PR reworks daemon spawn options and handling of start/init, it also makes sure spawned daemons attach themselves to running daemons on the default/provided repo.
Some dependencies were also removed.

closes #305
closes #276
closes #354
closes #330
closes #329
  • Loading branch information
hugomrdias authored Sep 18, 2019
1 parent e23796a commit b502bd4
Show file tree
Hide file tree
Showing 21 changed files with 587 additions and 816 deletions.
2 changes: 1 addition & 1 deletion .aegir.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const createServer = require('./src').createServer

const server = createServer() // using defaults
module.exports = {
bundlesize: { maxSize: '281kB' },
bundlesize: { maxSize: '256kB' },
karma: {
files: [{
pattern: 'test/fixtures/**/*',
Expand Down
6 changes: 3 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ os:
- osx
- windows

script: npx nyc -s npm run test:node -- --bail
script: npx nyc -s npm run test:node -- --bail --timeout 30000
after_success: npx nyc report --reporter=text-lcov > coverage.lcov && npx codecov

jobs:
Expand All @@ -31,14 +31,14 @@ jobs:
addons:
chrome: stable
script:
- npx aegir test -t browser -t webworker
- npx aegir test -t browser -t webworker --bail --timeout 30000

- stage: test
name: firefox
addons:
firefox: latest
script:
- npx aegir test -t browser -t webworker -- --browsers FirefoxHeadless
- npx aegir test -t browser -t webworker --bail --timeout 30000 -- --browsers FirefoxHeadless

notifications:
email: false
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ await server.stop()
`ipfsd-ctl` can spawn `disposable` and `non-disposable` daemons.

- `disposable`- Creates on a temporary repo which will be optionally initialized and started (the default), as well cleaned up on process exit. Great for tests.
- `non-disposable` - Requires the user to initialize and start the node, as well as stop and cleanup after wards. Additionally, a non-disposable will allow you to pass a custom repo using the `repoPath` option, if the `repoPath` is not defined, it will use the default repo for the node type (`$HOME/.ipfs` or `$HOME/.jsipfs`). The `repoPath` parameter is ignored for disposable nodes, as there is a risk of deleting a live repo.
- `non-disposable` - Non disposable daemons will by default attach to any nodes running on the default or the supplied repo. Requires the user to initialize and start the node, as well as stop and cleanup afterwards. Additionally, a non-disposable will allow you to pass a custom repo using the `repoPath` option, if the `repoPath` is not defined, it will use the default repo for the node type (`$HOME/.ipfs` or `$HOME/.jsipfs`). The `repoPath` parameter is ignored for disposable nodes, as there is a risk of deleting a live repo.

## Batteries not included. Bring your own IPFS executable.

Expand Down
11 changes: 4 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,21 +66,18 @@
"@hapi/hapi": "^18.3.2",
"@hapi/joi": "^15.1.1",
"debug": "^4.1.1",
"dexie": "^2.0.4",
"execa": "^2.0.4",
"fs-extra": "^8.1.0",
"hat": "~0.0.3",
"ipfs-http-client": "^36.0.0",
"ipfs-http-client": "^37.0.1",
"ipfs-utils": "^0.3.0",
"lodash.clone": "^4.5.0",
"lodash.defaults": "^4.2.0",
"lodash.defaultsdeep": "^4.6.1",
"multiaddr": "^7.1.0",
"merge-options": "^1.0.1",
"multiaddr": "^7.0.0",
"safe-json-stringify": "^1.2.0",
"superagent": "^5.0.5"
},
"devDependencies": {
"aegir": "^20.1.0",
"aegir": "^20.2.0",
"chai": "^4.2.0",
"delay": "^4.3.0",
"detect-port": "^1.3.0",
Expand Down
6 changes: 0 additions & 6 deletions src/defaults/options.json

This file was deleted.

56 changes: 23 additions & 33 deletions src/endpoint/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
const hat = require('hat')
const Joi = require('@hapi/joi')
const boom = require('@hapi/boom')
const defaults = require('lodash.defaultsdeep')
const merge = require('merge-options')
const FactoryDaemon = require('../factory-daemon')
const tmpDir = require('../utils/tmp-dir')

Expand All @@ -27,11 +27,13 @@ module.exports = (server) => {
server.route({
method: 'GET',
path: '/util/tmp-dir',
handler: (request) => {
handler: async (request) => {
const type = request.query.type || 'go'
const path = tmpDir(type === 'js')

return { tmpDir: path }
try {
return { tmpDir: await tmpDir(type === 'js') }
} catch (err) {
throw boom.badRequest(err.message)
}
}
})

Expand Down Expand Up @@ -68,28 +70,18 @@ module.exports = (server) => {
const f = new FactoryDaemon({ type: payload.type })

try {
const ipfsd = await f.spawn(payload.options)
const ipfsd = await f.spawn(payload)
const id = hat()
const initialized = ipfsd.initialized
nodes[id] = ipfsd

let api = null

if (nodes[id].started) {
api = {
apiAddr: nodes[id].apiAddr
? nodes[id].apiAddr.toString()
: '',
gatewayAddr: nodes[id].gatewayAddr
? nodes[id].gatewayAddr.toString()
: ''
}
}

return {
id,
api,
initialized
_id: id,
apiAddr: ipfsd.apiAddr ? ipfsd.apiAddr.toString() : '',
gatewayAddr: ipfsd.gatewayAddr ? ipfsd.gatewayAddr.toString() : '',
initialized: ipfsd.initialized,
started: ipfsd.started,
_env: ipfsd._env,
path: ipfsd.path
}
} catch (err) {
throw boom.badRequest(err.message)
Expand Down Expand Up @@ -135,10 +127,8 @@ module.exports = (server) => {
await nodes[id].start(flags)

return {
api: {
apiAddr: nodes[id].apiAddr.toString(),
gatewayAddr: nodes[id].gatewayAddr.toString()
}
apiAddr: nodes[id].apiAddr.toString(),
gatewayAddr: nodes[id].gatewayAddr ? nodes[id].gatewayAddr.toString() : ''
}
} catch (err) {
throw boom.badRequest(err.message)
Expand Down Expand Up @@ -206,7 +196,7 @@ module.exports = (server) => {
path: '/stop',
handler: async (request, h) => {
const id = request.query.id
const timeout = request.payload.timeout
const timeout = request.payload && request.payload.timeout

try {
await nodes[id].stop(timeout)
Expand All @@ -230,7 +220,7 @@ module.exports = (server) => {
path: '/kill',
handler: async (request, h) => {
const id = request.query.id
const timeout = request.payload.timeout
const timeout = request.payload && request.payload.timeout

try {
await nodes[id].killProcess(timeout)
Expand Down Expand Up @@ -277,13 +267,13 @@ module.exports = (server) => {
throw boom.badRequest(err.message)
}
},
config: defaults({}, {
config: merge(routeConfig, {
validate: {
query: {
key: Joi.string().optional()
}
}
}, routeConfig)
})
})

/*
Expand All @@ -305,13 +295,13 @@ module.exports = (server) => {

return h.response().code(200)
},
config: defaults({}, {
config: merge(routeConfig, {
validate: {
payload: {
key: Joi.string(),
value: Joi.any()
}
}
}, routeConfig)
})
})
}
33 changes: 23 additions & 10 deletions src/factory-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

const request = require('superagent')
const DaemonClient = require('./ipfsd-client')
const merge = require('merge-options')
const defaultConfig = require('./defaults/config.json')

/** @ignore @typedef {import("./index").SpawnOptions} SpawnOptions */

Expand All @@ -23,7 +25,7 @@ class FactoryClient {
this.options = options

if (options.type === 'proc') {
throw new Error(`'proc' is not supported in client mode`)
throw new Error('\'proc\' is not supported in client mode')
}

this.baseUrl = `${options.secure ? 'https://' : 'http://'}${options.host}:${options.port}`
Expand All @@ -33,12 +35,14 @@ class FactoryClient {
* Utility method to get a temporary directory
* useful in browsers to be able to generate temp
* repos manually
* @param {boolean} isJS
*
* @returns {Promise}
*/
async tmpDir () {
async tmpDir (isJS) {
const res = await request
.get(`${this.baseUrl}/util/tmp-dir`)
.query({ type: isJS ? 'js' : 'go' })

return res.body.tmpDir
}
Expand Down Expand Up @@ -66,19 +70,28 @@ class FactoryClient {
* @return {Promise}
*/
async spawn (options = {}) {
const daemonOptions = merge({
exec: this.options.exec,
type: this.options.type,
IpfsClient: this.options.IpfsClient,
disposable: true,
start: options.disposable !== false,
init: options.disposable !== false,
config: defaultConfig
}, options)

if (options.defaultAddrs) {
delete daemonOptions.config.Addresses
}

const res = await request
.post(`${this.baseUrl}/spawn`)
.send({ options: options, type: this.options.type })
const apiAddr = res.body.api ? res.body.api.apiAddr : ''
const gatewayAddr = res.body.api ? res.body.api.gatewayAddr : ''
.send(daemonOptions)

const ipfsd = new DaemonClient(
this.baseUrl,
res.body.id,
res.body.initialized,
apiAddr,
gatewayAddr,
{ IpfsClient: this.options.IpfsClient }
res.body,
daemonOptions
)

return ipfsd
Expand Down
67 changes: 19 additions & 48 deletions src/factory-daemon.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
'use strict'

const defaultsDeep = require('lodash.defaultsdeep')
const clone = require('lodash.clone')
const path = require('path')
const tmpDir = require('./utils/tmp-dir')
const Daemon = require('./ipfsd-daemon')
const merge = require('merge-options')
const defaultConfig = require('./defaults/config.json')
const defaultOptions = require('./defaults/options.json')

/** @ignore @typedef {import("./index").SpawnOptions} SpawnOptions */

Expand All @@ -22,7 +19,7 @@ class FactoryDaemon {
if (options && options.type === 'proc') {
throw new Error('This Factory does not know how to spawn in proc nodes')
}
this.options = Object.assign({}, { type: 'go' }, options)
this.options = merge({ type: 'go' }, options)
}

/**
Expand All @@ -36,7 +33,7 @@ class FactoryDaemon {
* @returns {Promise}
*/
tmpDir (type) {
return tmpDir(type === 'js')
return Promise.resolve(tmpDir(type === 'js'))
}

/**
Expand Down Expand Up @@ -68,57 +65,31 @@ class FactoryDaemon {
* Spawn an IPFS node, either js-ipfs or go-ipfs
*
* @param {SpawnOptions} [options={}] - Various config options and ipfs config parameters
* @param {function(Error, Daemon): void} callback - Callback receives Error or a Daemon instance, Daemon has a `api` property which is an `ipfs-http-client` instance.
* @returns {void}
* @returns {Promise<Daemon>}
*/
async spawn (options = {}) {
// TODO this options parsing is daunting. Refactor and move to a separate
// func documenting what it is trying to do.
options = defaultsDeep(
{ IpfsClient: this.options.IpfsClient },
options,
defaultOptions
)

options.init = typeof options.init !== 'undefined'
? options.init
: true

if (!options.disposable) {
const nonDisposableConfig = clone(defaultConfig)
options.init = false
options.start = false

const defaultRepo = path.join(
process.env.HOME || process.env.USERPROFILE,
options.isJs
? '.jsipfs'
: '.ipfs'
)

options.repoPath = options.repoPath ||
(process.env.IPFS_PATH || defaultRepo)
options.config = defaultsDeep({}, options.config, nonDisposableConfig)
} else {
options.config = defaultsDeep({}, options.config, defaultConfig)
}
const daemonOptions = merge({
exec: this.options.exec,
type: this.options.type,
IpfsClient: this.options.IpfsClient,
disposable: true,
start: options.disposable !== false,
init: options.disposable !== false,
config: defaultConfig
}, options)

if (options.defaultAddrs) {
delete options.config.Addresses
delete daemonOptions.config.Addresses
}

options.type = this.options.type
options.exec = options.exec || this.options.exec
options.initOptions = defaultsDeep({}, this.options.initOptions, options.initOptions)

const node = new Daemon(options)
const node = new Daemon(daemonOptions)

if (options.init) {
await node.init(options.initOptions)
if (daemonOptions.init) {
await node.init(daemonOptions.initOptions)
}

if (options.start) {
await node.start(options.args)
if (daemonOptions.start) {
await node.start(daemonOptions.args)
}

return node
Expand Down
Loading

0 comments on commit b502bd4

Please sign in to comment.