Skip to content
This repository was archived by the owner on Feb 12, 2024. It is now read-only.

Add config profile endpoint and CLI #2165

Merged
merged 21 commits into from
Oct 6, 2019
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@
"ipfs-bitswap": "~0.25.1",
"ipfs-block": "~0.8.1",
"ipfs-block-service": "~0.15.2",
"ipfs-http-client": "^34.0.0",
"ipfs-http-client": "^35.1.0",
"ipfs-http-response": "~0.3.1",
"ipfs-mfs": "~0.12.0",
"ipfs-multipart": "~0.1.1",
Expand All @@ -120,6 +120,7 @@
"is-pull-stream": "~0.0.0",
"is-stream": "^2.0.0",
"iso-url": "~0.4.6",
"jsondiffpatch": "~0.3.11",
"just-safe-set": "^2.1.0",
"kind-of": "^6.0.2",
"libp2p": "~0.26.1",
Expand Down
15 changes: 15 additions & 0 deletions src/cli/commands/config/profile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use strict'

module.exports = {
command: 'profile <command>',

description: 'Interact with config profiles.',

builder (yargs) {
return yargs
.commandDir('profile')
},

handler (argv) {
}
}
25 changes: 25 additions & 0 deletions src/cli/commands/config/profile/apply.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use strict'

const JSONDiff = require('jsondiffpatch')

module.exports = {
command: 'apply <profile>',

describe: 'Apply profile to config',

builder: {
'dry-run': {
type: 'boolean',
describe: 'print difference between the current config and the config that would be generated.'
}
},

handler (argv) {
argv.resolve((async () => {
const ipfs = await argv.getIpfs()
const diff = await ipfs.config.profile(argv.profile, { dryRun: argv.dryRun })
const delta = JSONDiff.diff(diff.oldCfg, diff.newCfg)
return JSONDiff.formatters.console.format(delta, diff.oldCfg)
})())
}
}
17 changes: 17 additions & 0 deletions src/cli/commands/config/profile/ls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use strict'

const { profiles } = require('../../../../core/components/config')

module.exports = {
command: 'ls',

describe: 'List available config profiles',

builder: {},

handler (argv) {
argv.resolve((async () => {
return profiles.map(p => p.name + ':\n ' + p.description).join('\n')
})())
}
}
13 changes: 11 additions & 2 deletions src/cli/commands/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ const { ipfsPathHelp } = require('../utils')

module.exports = {
command: 'init [config] [options]',
describe: 'Initialize a local IPFS node',
describe: 'Initialize a local IPFS node\n\n' +
'If you are going to run IPFS in a server environment, you may want to ' +
`initialize it using the 'server' profile.\n\n` +
'For the list of available profiles run `jsipfs config profile ls`',
builder (yargs) {
return yargs
.epilog(ipfsPathHelp)
.positional('config', {
describe: 'Node config, this should JSON and will be merged with the default config. Check https://github.com/ipfs/js-ipfs#optionsconfig',
describe: 'Node config, this should be JSON and will be merged with the default config. See https://github.com/ipfs/js-ipfs#optionsconfig',
type: 'string'
})
.option('bits', {
Expand All @@ -28,6 +31,11 @@ module.exports = {
type: 'string',
describe: 'Pre-generated private key to use for the repo'
})
.option('profile', {
alias: 'p',
type: 'string',
describe: `Apply profile settings to config. Multiple profiles can be separated by ','`
})
},

handler (argv) {
Expand All @@ -52,6 +60,7 @@ module.exports = {
bits: argv.bits,
privateKey: argv.privateKey,
emptyRepo: argv.emptyRepo,
profile: argv.profile,
pass: argv.pass,
log: argv.print
})
Expand Down
91 changes: 90 additions & 1 deletion src/core/components/config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict'

const promisify = require('promisify-es6')
const defaultConfig = require('../runtime/config-nodejs.js')()

module.exports = function config (self) {
return {
Expand All @@ -17,6 +18,94 @@ module.exports = function config (self) {
}),
replace: promisify((config, callback) => {
self._repo.config.set(config, callback)
})
}),
profile: promisify(applyProfile)
}

async function applyProfile (profileName, opts, callback) {
if (typeof opts === 'function') {
callback = opts
opts = {}
}
const { dryRun } = opts

const profile = profiles.find(p => p.name === profileName)
if (!profile) {
return callback(new Error(`No profile with name '${profileName}' exists`))
}

try {
const oldCfg = await self.config.get()
const newCfg = JSON.parse(JSON.stringify(oldCfg)) // clone
profile.transform(newCfg)
if (!dryRun) {
await self.config.replace(newCfg)
}

// Scrub private key from output
delete oldCfg.Identity.PrivKey
delete newCfg.Identity.PrivKey

callback(null, { oldCfg, newCfg })
} catch (err) {
callback(new Error(`Could not apply profile '${profileName}' to config: ${err.message}`))
}
}
}

const profiles = [{
name: 'server',
description: 'Disables local host discovery - recommended when running IPFS on machines with public IPv4 addresses.',
transform: (config) => {
config.Discovery.MDNS.Enabled = false
config.Discovery.webRTCStar.Enabled = false
}
}, {
name: 'local-discovery',
description: 'Enables local host discovery - inverse of "server" profile.',
transform: (config) => {
config.Discovery.MDNS.Enabled = true
config.Discovery.webRTCStar.Enabled = true
}
}, {
name: 'test',
description: 'Reduces external interference of IPFS daemon - for running the daemon in test environments.',
transform: (config) => {
config.Addresses.API = defaultConfig.Addresses.API ? '/ip4/127.0.0.1/tcp/0' : ''
config.Addresses.Gateway = defaultConfig.Addresses.Gateway ? '/ip4/127.0.0.1/tcp/0' : ''
config.Addresses.Swarm = defaultConfig.Addresses.Swarm.length ? ['/ip4/127.0.0.1/tcp/0'] : []
config.Bootstrap = []
config.Discovery.MDNS.Enabled = false
config.Discovery.webRTCStar.Enabled = false
}
}, {
name: 'default-networking',
description: 'Restores default network settings - inverse of "test" profile.',
transform: (config) => {
console.log('applying default-networking')
console.log('setting to', defaultConfig.Addresses)
config.Addresses.API = defaultConfig.Addresses.API
config.Addresses.Gateway = defaultConfig.Addresses.Gateway
config.Addresses.Swarm = defaultConfig.Addresses.Swarm
config.Bootstrap = defaultConfig.Bootstrap
config.Discovery.MDNS.Enabled = defaultConfig.Discovery.MDNS.Enabled
config.Discovery.webRTCStar.Enabled = defaultConfig.Discovery.webRTCStar.Enabled
}
}, {
name: 'lowpower',
description: 'Reduces daemon overhead on the system - recommended for low power systems.',
transform: (config) => {
config.Swarm = config.Swarm || {}
config.Swarm.ConnMgr = config.Swarm.ConnMgr || {}
config.Swarm.ConnMgr.LowWater = 20
config.Swarm.ConnMgr.HighWater = 40
}
}, {
name: 'default-power',
description: 'Inverse of "lowpower" profile.',
transform: (config) => {
config.Swarm = defaultConfig.Swarm
}
}]

module.exports.profiles = profiles
16 changes: 16 additions & 0 deletions src/core/components/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const IPNS = require('../ipns')
const OfflineDatastore = require('../ipns/routing/offline-datastore')

const addDefaultAssets = require('./init-assets')
const { profiles } = require('./config')

module.exports = function init (self) {
return promisify((opts, callback) => {
Expand Down Expand Up @@ -61,6 +62,21 @@ module.exports = function init (self) {
opts.log = opts.log || function () {}

const config = mergeOptions(defaultConfig(), self._options.config)

// Apply profiles (eg "server,lowpower") to config
if (opts.profile) {
const profileNames = opts.profile.split(',')
for (const profileName of profileNames) {
const profile = profiles.find(p => p.name === profileName)
if (!profile) {
return done(new Error(`Could not find profile with name '${profileName}'`))
}

self.log(`applying profile ${profileName}`)
profile.transform(config)
}
}

let privateKey

waterfall([
Expand Down
38 changes: 38 additions & 0 deletions src/http/api/resources/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ const log = debug('ipfs:http-api:config')
log.error = debug('ipfs:http-api:config:error')
const multipart = require('ipfs-multipart')
const Boom = require('@hapi/boom')
const Joi = require('@hapi/joi')
const { profiles } = require('../../../core/components/config')

exports.getOrSet = {
// pre request handler that parses the args and returns `key` & `value` which are assigned to `request.pre.args`
Expand Down Expand Up @@ -160,3 +162,39 @@ exports.replace = {
return h.response()
}
}

exports.profile = {
validate: {
query: Joi.object().keys({
'dry-run': Joi.boolean().default(false)
}).unknown()
},

// pre request handler that parses the args and returns `profile` which is assigned to `request.pre.args`
parseArgs: async function (request, h) {
if (!request.query.arg) {
throw Boom.badRequest("Argument 'profile' is required")
}

if (!profiles.find(p => p.name === request.query.arg)) {
throw Boom.badRequest("Argument 'profile' is not a valid profile name")
}

return { profile: request.query.arg }
},

handler: async function (request, h) {
const { ipfs } = request.server.app
const { profile } = request.pre.args
const dryRun = request.query['dry-run']

let diff
try {
diff = await ipfs.config.profile(profile, { dryRun })
} catch (err) {
throw Boom.boomify(err, { message: 'Failed to apply profile' })
}

return h.response({ OldCfg: diff.oldCfg, NewCfg: diff.newCfg })
}
}
11 changes: 11 additions & 0 deletions src/http/api/routes/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,16 @@ module.exports = [
]
},
handler: resources.config.replace.handler
},
{
method: '*',
path: '/api/v0/config/profile/apply',
options: {
pre: [
{ method: resources.config.profile.parseArgs, assign: 'args' }
],
validate: resources.config.profile.validate
},
handler: resources.config.profile.handler
}
]
2 changes: 1 addition & 1 deletion test/cli/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
const expect = require('chai').expect
const runOnAndOff = require('../utils/on-and-off')

const commandCount = 95
const commandCount = 98
describe('commands', () => runOnAndOff((thing) => {
let ipfs

Expand Down
Loading