Skip to content
This repository has been archived by the owner on Jul 7, 2024. It is now read-only.
/ Orion Public archive

Add check for already running daemon and custom ports for own daemon #92

Merged
merged 15 commits into from
Apr 29, 2018
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
11 changes: 7 additions & 4 deletions app/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import pjson from '../package.json'
import gateways from './gateways.json'

import Settings from 'electron-settings'
import { getMultiAddrIPFSDaemon } from './daemon'
import { getApiMultiAddress } from './daemon'

export const ERROR_IPFS_UNAVAILABLE = 'IPFS NOT AVAILABLE'
export const ERROR_IPFS_TIMEOUT = 'TIMEOUT'
Expand All @@ -33,9 +33,12 @@ export function initIPFSClient () {
}
}

const apiMultiaddr = getMultiAddrIPFSDaemon()
setClientInstance(ipfsAPI(apiMultiaddr))
return Promise.resolve(IPFS_CLIENT)
// this fails because of the repo lock
return getApiMultiAddress()
.then(apiMultiaddr => {
setClientInstance(ipfsAPI(apiMultiaddr))
return Promise.resolve(IPFS_CLIENT)
})
}

/**
Expand Down
4 changes: 2 additions & 2 deletions app/api.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import pjson from '../package'

jest.mock('./daemon', () => {
return {
getMultiAddrIPFSDaemon: jest.fn().mockReturnValue('my-address')
getApiMultiAddress: jest.fn().mockReturnValue(Promise.resolve('my-address'))
}
})
jest.mock('ipfs-api', () => {
Expand Down Expand Up @@ -53,7 +53,7 @@ describe('api.js', () => {
return api.initIPFSClient()
.then(result => {
// assert
expect(daemon.getMultiAddrIPFSDaemon).toHaveBeenCalled()
expect(daemon.getApiMultiAddress).toHaveBeenCalled()
expect(ipfsApi).toBeCalledWith('my-address')
expect(result).toBe('new-instance')
})
Expand Down
146 changes: 113 additions & 33 deletions app/daemon.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,66 @@ import { app, dialog } from 'electron'
import pjson from '../package.json'
import { get as getAppRoot } from 'app-root-dir'

const CUSTOM_API_PORT = 5101
const CUSTOM_GATEWAY_PORT = 8180
const CUSTOM_SWARM_PORT = 4101

export let binaryPath = `${getAppRoot()}/go-ipfs/ipfs`
let skipRepo = false

export function skipRepoPath () {
skipRepo = true
}

export function getRepoPath () {
return pathJoin(app.getPath('userData'), '.ipfs')
}

export function setCustomBinaryPath (path) {
binaryPath = path
}

export function executeIPFSCommand (command) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add some documentation to this method at least, as it will run cmd :D

const env = skipRepo ? '' : `IPFS_PATH=${getRepoPath()}`
return exec(`${env} ${binaryPath} ${command}`)
}

export function spawnIPFSCommand (command) {
const options = skipRepo ? undefined : { env: { IPFS_PATH: getRepoPath() } }
return spawn(binaryPath, [command], options)
}

/**
* getPathIPFSBinary will return the IPFS default path
*
* Returns the version of the currently running API.
* If no API is available returns `null`.
*
* Example: 'v0.4.14'
* @returns Promise<string>
*/
export function getPathIPFSBinary () {
return `${getAppRoot()}/go-ipfs/ipfs`
export function getAPIVersion () {
return request({
// what if it's running on a different port?
uri: 'http://localhost:5001/api/v0/version',
headers: { 'User-Agent': `Orion/${pjson.version}` }
})
.then(res => {
/**
* ApiVersionResult {
* Version: "0.4.14",
* Commit: "",
* Repo: "6",
* System: "amd64/linux",
* Golang: "go1.10"
* }
*/
res = JSON.parse(res)
return Promise.resolve(`v${res.Version}`)
})
.catch(err => {
console.error('API not available', err.message)
return Promise.resolve(null)
})
}

/**
Expand All @@ -21,19 +76,18 @@ export function getPathIPFSBinary () {
*/
export function startIPFSDaemon () {
return new Promise((resolve, reject) => {
const binaryPath = getPathIPFSBinary()
const ipfsProcess = spawn(binaryPath, ['daemon'])
const ipfsProcess = spawnIPFSCommand('daemon')

// Prepare temporary file for logging:
const tmpLog = tmpFileSync({keep: true})
const tmpLog = tmpFileSync({ keep: true })
const tmpLogPipe = createWriteStream(tmpLog.name)

console.log(`Logging IPFS logs in: ${tmpLog.name}`)
console.log(`Logging IPFS Daemon logs in: ${tmpLog.name}`)

ipfsProcess.stdout.on('data', (data) => console.log(`IPFS: ${data}`))
ipfsProcess.stdout.on('data', (data) => console.log(`IPFS Daemon: ${data}`))
ipfsProcess.stdout.pipe(tmpLogPipe)

ipfsProcess.stderr.on('data', (data) => console.log(`IPFS Error: ${data}`))
ipfsProcess.stderr.on('data', (data) => console.log(`IPFS Daemon Error: ${data}`))
ipfsProcess.stderr.pipe(tmpLogPipe)

ipfsProcess.on('close', (exit) => {
Expand All @@ -48,17 +102,19 @@ export function startIPFSDaemon () {
console.log(`IPFS Closed: ${exit}`)
})

// Resolves the process after 1 second
setTimeout(() => { resolve(ipfsProcess) }, 1 * 1000)
resolve(ipfsProcess)
})
}

/**
* isIPFSInitialised returns a boolean if the repository config file is present
* in the default path (~/.ipfs/config)
* in the default path (i.e. userData)
* (~/.config/Electron/.ipfs/config) in development
*
* https://github.com/electron/electron/blob/master/docs/api/app.md#appgetpathname
*/
export function isIPFSInitialised () {
const confFile = pathJoin(app.getPath('home'), '.ipfs', 'config')
const confFile = pathJoin(getRepoPath(), 'config')
return existsSync(confFile)
}

Expand All @@ -70,11 +126,9 @@ export function ensuresIPFSInitialised () {
if (isIPFSInitialised()) return Promise.resolve()
console.log('Initialising IPFS repository...')
return new Promise((resolve, reject) => {
const binaryPath = getPathIPFSBinary()
const ipfsProcess = spawn(binaryPath, ['init'])

const ipfsProcess = spawnIPFSCommand('init')
// Prepare temporary file for logging:
const tmpLog = tmpFileSync({keep: true})
const tmpLog = tmpFileSync({ keep: true })
const tmpLogPipe = createWriteStream(tmpLog.name)

console.log(`Logging IPFS init logs in: ${tmpLog.name}`)
Expand Down Expand Up @@ -102,23 +156,32 @@ export function ensuresIPFSInitialised () {
}

/**
* Returns the multiAddr usable to connect to the local dameon via API
* 5001 -> 5101
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add few lines about ensureAddressesConfigured, why this should be called? what does it do?

* 8080 -> 8180
* 4001 -> 4101
*/
export function getMultiAddrIPFSDaemon () {
// Other option: ask the binary wich one to use
// const binaryPath = getPathIPFSBinary()
// const multiAddr = execSync(`${binaryPath} config Addresses.API`)
return '/ip4/127.0.0.1/tcp/5001'
export function ensureAddressesConfigured (customPorts = false) {
let apiPort = '5001'
let gatewayPort = '8080'
let swarmPort = '4001'

if (customPorts) {
console.log('Setting custom addresses for API, Gateway and Swarm')
apiPort = CUSTOM_API_PORT
gatewayPort = CUSTOM_GATEWAY_PORT
swarmPort = CUSTOM_SWARM_PORT
}

return executeIPFSCommand(`config Addresses.API /ip4/127.0.0.1/tcp/${apiPort}`)
.then(() => executeIPFSCommand(`config Addresses.Gateway /ip4/127.0.0.1/tcp/${gatewayPort}`))
.then(() => executeIPFSCommand(`config Addresses.Swarm --json '["/ip4/0.0.0.0/tcp/${swarmPort}", "/ip6/::/tcp/${swarmPort}"]'`))
}

/**
* Set the multiAddr usable to connect to the local dameon via API.
* It restores it to /ip4/127.0.0.1/tcp/5001
* returns a promise.
* Returns the multiAddr usable to connect to the local dameon via API
*/
export function setMultiAddrIPFSDaemon () {
const binaryPath = getPathIPFSBinary()
return exec(`${binaryPath} config Addresses.API /ip4/127.0.0.1/tcp/5001`)
export function getApiMultiAddress () {
return executeIPFSCommand(`config Addresses.API`)
}

/**
Expand All @@ -127,8 +190,7 @@ export function setMultiAddrIPFSDaemon () {
* returns a promise
*/
export function connectToCMD (strMultiddr) {
const binaryPath = getPathIPFSBinary()
return exec(`${binaryPath} swarm connect ${strMultiddr}`)
return executeIPFSCommand(`swarm connect ${strMultiddr}`)
}

/**
Expand All @@ -137,8 +199,7 @@ export function connectToCMD (strMultiddr) {
* returns a promise
*/
export function addBootstrapAddr (strMultiddr) {
const binaryPath = getPathIPFSBinary()
return exec(`${binaryPath} bootstrap add ${strMultiddr}`)
return executeIPFSCommand(`bootstrap add ${strMultiddr}`)
}

/**
Expand All @@ -158,3 +219,22 @@ export function getSiderusPeers () {
return Promise.resolve(peers)
})
}

export function promiseRepoUnlocked (timeout = 30) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add docs here as well :)

let iID // interval id
let trial = 0
return new Promise((resolve, reject) => {
iID = setInterval(() => {
trial++
if (trial >= timeout) {
clearInterval(iID)
return reject('TIMEOUT')
}

return getApiMultiAddress().then(() => {
clearInterval(iID)
return resolve()
}).catch(() => { }) // do nothing in case of errors
}, 1000) // every second
})
}
2 changes: 1 addition & 1 deletion app/daemon.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ describe('daemon.js', () => {
it('should always return the default value', () => {
// arrange
// act
const path = daemon.getPathIPFSBinary()
const path = daemon.binaryPath
// assert
expect(path).toBe('root-dir/go-ipfs/ipfs')
})
Expand Down
77 changes: 64 additions & 13 deletions app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@ import './report'
import {
startIPFSDaemon,
ensuresIPFSInitialised,
ensureAddressesConfigured,
getSiderusPeers,
connectToCMD,
addBootstrapAddr
addBootstrapAddr,
promiseRepoUnlocked,
getAPIVersion,
setCustomBinaryPath,
skipRepoPath
} from './daemon'

import {
Expand Down Expand Up @@ -56,18 +61,57 @@ app.on('ready', () => {
})
// Set up crash reports.
// Set up the needed stuff as the app launches.
ensuresIPFSInitialised()
.then(startIPFSDaemon)
.then((process) => {
console.log('IPFS Daemon started')
global.IPFS_PROCESS = process
loadingWindow.webContents.send('set-progress', {
text: 'Initializing IPFS client...',
percentage: 20
})
return Promise.resolve()
})

getAPIVersion()
.then(apiVersion => {
let customPorts = false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't we move this in another function with a promise returning true/false based on the answer of the user?


// An api is already available on port 5001
if (apiVersion !== null) {
let alertMessage = 'An IPFS instance is already up!'
alertMessage += '\n\nWould you like Orion to connect to the available node, instead of using its own?'

if (apiVersion !== pjson.ipfsVersion) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice :) well done with this!

alertMessage += `\n\nPlease note: Orion was design with IPFS ${pjson.ipfsVersion} in mind, `
alertMessage += `while the available API is running ${apiVersion}.`
}

const btnId = dialog.showMessageBox({
type: 'info',
message: alertMessage,
buttons: ['No', 'Yes'],
cancelId: 0,
defaultId: 1
})
if (btnId === 1) {
// Use running node, skip starting the daemon
// Set repository path to null so it's ignored and not passed as IPFS_PATH
// Set binary path to `ipfs` to use the client's binary
skipRepoPath()
setCustomBinaryPath('ipfs')
return Promise.resolve()
} else {
// Use our own daemon, but on different ports
customPorts = true
}
}

return ensuresIPFSInitialised()
.then(() => ensureAddressesConfigured(customPorts))
.then(startIPFSDaemon)
.then((process) => {
console.log('IPFS Daemon: Starting')
global.IPFS_PROCESS = process
loadingWindow.webContents.send('set-progress', {
text: 'Initializing the IPFS Daemon...',
percentage: 20
})
return Promise.resolve()
})
// Wait for the repo to be unlocked
// (usually ipfs daemon needs some time before it releases the lock)
.then(promiseRepoUnlocked)
})
// Start the IPFS API Client
.then(initIPFSClient)
.then(client => {
Expand Down Expand Up @@ -127,7 +171,14 @@ app.on('ready', () => {
})
// Catch errors
.catch(err => {
const message = typeof err === 'string' ? err : JSON.stringify(err)
let message
if (typeof err === 'string') {
message = err
} else if (err.message) {
message = err.message
} else {
message = JSON.stringify(err)
}
dialog.showMessageBox({ type: 'warning', message })
app.quit()
})
Expand Down