Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: enable peer discovery modules by default #209

Merged
merged 8 commits into from
Jun 29, 2018
Merged
60 changes: 12 additions & 48 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,19 @@

const Joi = require('joi')

const schema = Joi.object({
const ModuleSchema = Joi.alternatives().try(Joi.func(), Joi.object())

const OptionsSchema = Joi.object({
// TODO: create proper validators for the generics
connectionManager: Joi.object(),
peerInfo: Joi.object().required(),
peerBook: Joi.object(),
modules: Joi.object().keys({
transport: Joi.array().items(
Joi.alternatives().try(
Joi.func(),
Joi.object()
)
).min(1).required(),
streamMuxer: Joi.array().items(
Joi.alternatives().try(
Joi.func(),
Joi.object()
)
).allow(null),
connEncryption: Joi.array().items(
Joi.alternatives().try(
Joi.func(),
Joi.object()
)
).allow(null),
peerDiscovery: Joi.array().items(
Joi.alternatives().try(
Joi.func(),
Joi.object()
)
).allow(null),
dht: Joi.alternatives().try(
Joi.func(),
Joi.object()
).allow(null)
transport: Joi.array().items(ModuleSchema).min(1).required(),
streamMuxer: Joi.array().items(ModuleSchema).allow(null),
connEncryption: Joi.array().items(ModuleSchema).allow(null),
peerDiscovery: Joi.array().items(ModuleSchema).allow(null),
dht: ModuleSchema.allow(null)
}).required(),
config: Joi.object().keys({
peerDiscovery: Joi.object().allow(null),
Expand All @@ -57,27 +36,12 @@ const schema = Joi.object({
})

module.exports.validate = (options) => {
let newSchema = schema
// Throw an intial error early for required props
let config = Joi.attempt(options, newSchema)

// Ensure discoveries are properly configured
if (config.modules.peerDiscovery) {
config.modules.peerDiscovery.forEach((discovery) => {
// If it's a function, validate we have configs for it
if (typeof discovery === 'function') {
Joi.reach(schema, 'config.peerDiscovery').keys({
[discovery.tag]: Joi.object().required()
})
}
})
}
options = Joi.attempt(options, OptionsSchema)

// Ensure dht is correct
if (config.config.EXPERIMENTAL && config.config.EXPERIMENTAL.dht) {
newSchema = newSchema.requiredKeys('modules.dht')
if (options.config.EXPERIMENTAL.dht) {
Joi.assert(options.modules.dht, ModuleSchema.required())
}

// Finish validation and return the updated config
return Joi.attempt(config, newSchema)
return options
}
20 changes: 15 additions & 5 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ class Node extends EventEmitter {
}

// enable/disable pubsub
if (this._config.EXPERIMENTAL && this._config.EXPERIMENTAL.pubsub) {
if (this._config.EXPERIMENTAL.pubsub) {
this.pubsub = pubsub(this)
}

Expand Down Expand Up @@ -154,15 +154,25 @@ class Node extends EventEmitter {
}

// all transports need to be setup before discover starts
if (this._modules.peerDiscovery && this._config.peerDiscovery) {
if (this._modules.peerDiscovery) {
each(this._modules.peerDiscovery, (D, _cb) => {
let config = {}

if (D.tag &&
this._config.peerDiscovery &&
this._config.peerDiscovery[D.tag]) {
config = this._config.peerDiscovery[D.tag]
}

// If not configured to be enabled/disabled then enable by default
const enabled = config.enabled == null ? true : config.enabled

// If enabled then start it
if (this._config.peerDiscovery[D.tag].enabled) {
if (enabled) {
let d

if (typeof D === 'function') {
this._config.peerDiscovery[D.tag].peerInfo = this.peerInfo
d = new D(this._config.peerDiscovery[D.tag])
d = new D(Object.assign({}, config, { peerInfo: this.peerInfo }))
} else {
d = D
}
Expand Down
17 changes: 0 additions & 17 deletions test/config.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,21 +113,4 @@ describe('configuration', () => {

expect(() => validateConfig(options)).to.throw()
})

it('should require a non instanced peerDiscovery module to have associated options', () => {
const options = {
peerInfo,
modules: {
transport: [ WS ],
peerDiscover: [ Bootstrap ]
},
config: {
EXPERIMENTAL: {
dht: true
}
}
}

expect(() => validateConfig(options)).to.throw()
})
})
180 changes: 180 additions & 0 deletions test/peer-discovery.node.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
const chai = require('chai')
chai.use(require('dirty-chai'))
const expect = chai.expect
const sinon = require('sinon')
const signalling = require('libp2p-webrtc-star/src/sig-server')
const parallel = require('async/parallel')
const crypto = require('crypto')
Expand Down Expand Up @@ -58,6 +59,185 @@ describe('peer discovery', () => {
})
}

describe('module registration', () => {
it('should enable by default a module passed as an object', (done) => {
const mockDiscovery = {
on: sinon.stub(),
start: sinon.stub().callsArg(0),
stop: sinon.stub().callsArg(0)
}

const options = { modules: { peerDiscovery: [ mockDiscovery ] } }

createNode(['/ip4/0.0.0.0/tcp/0'], options, (err, node) => {
expect(err).to.not.exist()

node.start((err) => {
expect(err).to.not.exist()
expect(mockDiscovery.start.called).to.be.true()
node.stop(done)
})
})
})

it('should enable by default a module passed as a function', (done) => {
const mockDiscovery = {
on: sinon.stub(),
start: sinon.stub().callsArg(0),
stop: sinon.stub().callsArg(0)
}

const MockDiscovery = sinon.stub().returns(mockDiscovery)

const options = { modules: { peerDiscovery: [ MockDiscovery ] } }

createNode(['/ip4/0.0.0.0/tcp/0'], options, (err, node) => {
expect(err).to.not.exist()

node.start((err) => {
expect(err).to.not.exist()
expect(mockDiscovery.start.called).to.be.true()
node.stop(done)
})
})
})

it('should enable module by configutation', (done) => {
const mockDiscovery = {
on: sinon.stub(),
start: sinon.stub().callsArg(0),
stop: sinon.stub().callsArg(0),
tag: 'mockDiscovery'
}

const enabled = sinon.stub().returns(true)

const options = {
modules: { peerDiscovery: [ mockDiscovery ] },
config: {
peerDiscovery: {
mockDiscovery: {
get enabled () {
return enabled()
}
}
}
}
}

createNode(['/ip4/0.0.0.0/tcp/0'], options, (err, node) => {
expect(err).to.not.exist()

node.start((err) => {
expect(err).to.not.exist()
expect(mockDiscovery.start.called).to.be.true()
expect(enabled.called).to.be.true()
node.stop(done)
})
})
})

it('should disable module by configutation', (done) => {
const mockDiscovery = {
on: sinon.stub(),
start: sinon.stub().callsArg(0),
stop: sinon.stub().callsArg(0),
tag: 'mockDiscovery'
}

const disabled = sinon.stub().returns(false)

const options = {
modules: { peerDiscovery: [ mockDiscovery ] },
config: {
peerDiscovery: {
mockDiscovery: {
get enabled () {
return disabled()
}
}
}
}
}

createNode(['/ip4/0.0.0.0/tcp/0'], options, (err, node) => {
expect(err).to.not.exist()

node.start((err) => {
expect(err).to.not.exist()
expect(mockDiscovery.start.called).to.be.false()
expect(disabled.called).to.be.true()
node.stop(done)
})
})
})

it('should register module passed as function', (done) => {
const mockDiscovery = {
on: sinon.stub(),
start: sinon.stub().callsArg(0),
stop: sinon.stub().callsArg(0)
}

const MockDiscovery = sinon.stub().returns(mockDiscovery)
MockDiscovery.tag = 'mockDiscovery'

const options = {
modules: { peerDiscovery: [ MockDiscovery ] },
config: {
peerDiscovery: {
mockDiscovery: {
enabled: true,
time: Date.now()
}
}
}
}

createNode(['/ip4/0.0.0.0/tcp/0'], options, (err, node) => {
expect(err).to.not.exist()

node.start((err) => {
expect(err).to.not.exist()
expect(mockDiscovery.start.called).to.be.true()
expect(MockDiscovery.called).to.be.true()
// Ensure configuration was passed
expect(MockDiscovery.firstCall.args[0])
.to.deep.include(options.config.peerDiscovery.mockDiscovery)
node.stop(done)
})
})
})

it('should register module passed as object', (done) => {
const mockDiscovery = {
on: sinon.stub(),
start: sinon.stub().callsArg(0),
stop: sinon.stub().callsArg(0),
tag: 'mockDiscovery'
}

const options = {
modules: { peerDiscovery: [ mockDiscovery ] },
config: {
peerDiscovery: {
mockDiscovery: { enabled: true }
}
}
}

createNode(['/ip4/0.0.0.0/tcp/0'], options, (err, node) => {
expect(err).to.not.exist()

node.start((err) => {
expect(err).to.not.exist()
expect(mockDiscovery.start.called).to.be.true()
node.stop(done)
})
})
})
})

describe('MulticastDNS', () => {
setup({
config: {
Expand Down