Skip to content

Commit

Permalink
Merge pull request #448 from cosmos/matt/372-data-dir
Browse files Browse the repository at this point in the history
Changes to data directory
  • Loading branch information
jbibla authored Feb 12, 2018
2 parents ef8cf6c + 19ef8d8 commit 864925e
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 73 deletions.
61 changes: 17 additions & 44 deletions app/src/main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ let semver = require('semver')
let event = require('event-to-promise')
let toml = require('toml')
let axios = require('axios')
var glob = require('glob')

let pkg = require('../../../package.json')
let relayServer = require('./relayServer.js')
Expand All @@ -26,6 +25,8 @@ let nodeIP
let connecting = false

const root = require('../root.js')
const networkPath = require('../network.js').path

const baseserverHome = join(root, 'baseserver')
const WIN = /^win/.test(process.platform)
const DEV = process.env.NODE_ENV === 'development'
Expand All @@ -41,11 +42,6 @@ const RELAY_PORT = DEV ? config.relay_port : config.relay_port_prod
const LCD_PORT = DEV ? config.lcd_port : config.lcd_port_prod
const NODE = process.env.COSMOS_NODE

// this network gets used if none is specified via the
// COSMOS_NETWORK env var
let DEFAULT_NETWORK = join(__dirname, '../networks/gaia-2')
let networkPath = process.env.COSMOS_NETWORK || DEFAULT_NETWORK

let SERVER_BINARY = 'gaia' + (WIN ? '.exe' : '')

function log (...args) {
Expand Down Expand Up @@ -266,36 +262,6 @@ async function initBaseserver (chainId, home, node) {
await expectCleanExit(child, 'gaia init exited unplanned')
}

async function backupData (root) {
let i = 1
let path
do {
path = `${root}_backup_${i}`
i++
} while (exists(path))

log(`backing up data to "${path}"`)

// ATTENTION: mainLog stream is still open at this point, so we can't move it arround (at least on windows)
fs.copySync(root, path, {
overwrite: false,
errorOnExist: true,
filter: file => file.indexOf('main.log') === -1
})
await new Promise((resolve, reject) => {
glob(root + '/**/*', (err, files) => {
if (err) {
return reject(err)
}

files
.filter(file => file.indexOf('main.log') === -1)
.forEach(file => fs.removeSync(file))
resolve()
})
})
}

/*
* log to file
*/
Expand Down Expand Up @@ -417,33 +383,40 @@ async function main () {
if (rootExists) {
log(`root exists (${root})`)

// NOTE: when changing this code, always make sure the app can never
// overwrite/delete existing data without at least backing it up,
// since it may contain the user's private keys and they might not
// have written down their seed words.
// they might get pretty mad if the app deletes their money!

// check if the existing data came from a compatible app version
// if not, backup the data and re-initialize
// if not, fail with an error
if (consistentConfigDir(appVersionPath, genesisPath, configPath, gaiaVersionPath)) {
let existingVersion = fs.readFileSync(appVersionPath, 'utf8')
let existingVersion = fs.readFileSync(appVersionPath, 'utf8').trim()
let compatible = semver.diff(existingVersion, pkg.version) !== 'major'
if (compatible) {
log('configs are compatible with current app version')
init = false
} else {
await backupData(root)
// TODO: versions of the app with different data formats will need to learn how to
// migrate old data
throw Error(`Data was created with an incompatible app version
data=${existingVersion} app=${pkg.version}`)
}
} else {
await backupData(root)
throw Error(`The data directory (${root}) has missing files`)
}

// check to make sure the genesis.json we want to use matches the one
// we already have. if it has changed, back up the old data
// we already have. if it has changed, exit with an error
if (!init) {
let existingGenesis = fs.readFileSync(genesisPath, 'utf8')
let genesisJSON = JSON.parse(existingGenesis)
// skip this check for local testnet
if (genesisJSON.chain_id !== 'local') {
let specifiedGenesis = fs.readFileSync(join(networkPath, 'genesis.json'), 'utf8')
if (existingGenesis.trim() !== specifiedGenesis.trim()) {
log('genesis has changed')
await backupData(root)
init = true
throw Error('Genesis has changed')
}
}
}
Expand Down
16 changes: 16 additions & 0 deletions app/src/network.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
let { join } = require('path')
let { readFileSync } = require('fs')

// this network gets used if none is specified via the
// COSMOS_NETWORK env var
let DEFAULT_NETWORK = join(__dirname, '../networks/gaia-2')
let networkPath = process.env.COSMOS_NETWORK || DEFAULT_NETWORK

let genesisText = readFileSync(join(networkPath, 'genesis.json'), 'utf8')
let genesis = JSON.parse(genesisText)
let networkName = genesis.chain_id

module.exports = {
path: networkPath,
name: networkName
}
8 changes: 7 additions & 1 deletion app/src/root.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
const { COSMOS_HOME, NODE_ENV } = process.env

if (COSMOS_HOME) {
module.exports = COSMOS_HOME
} else {
const home = require('user-home')
const { join } = require('path')
const networkName = require('./network.js').name

const pkg = require('../package.json')
const DEV = NODE_ENV === 'development'
module.exports = join(home, `.${pkg.name}${DEV ? '-dev' : ''}`)
const appName = pkg.name.toLowerCase()
const appDirName = `.${appName}${DEV ? '-dev' : ''}`

module.exports = join(home, appDirName, networkName)
}
47 changes: 19 additions & 28 deletions test/unit/specs/main.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -266,54 +266,45 @@ describe('Startup Process', () => {
describe('Update app version', function () {
mainSetup()

it('should backup the genesis.json', async function () {
it('should not replace the existing data', async function () {
resetModulesKeepingFS()

// alter the version so the main thread assumes an update
jest.mock(root + 'package.json', () => ({
version: '1.1.1'
}))
await require(appRoot + 'src/main/index.js')

expect(fs.existsSync(testRoot.substr(0, testRoot.length - 1) + '_backup_1/genesis.json')).toBe(true)
})

it('should not backup main log', async function () {
expect(fs.existsSync(testRoot.substr(0, testRoot.length - 1) + '_backup_1/genesis.json')).toBe(true)
})
})

describe('Keep main log on update', function () {
mainSetup()

it('should not backup main log', async function () {
resetModulesKeepingFS()
fs.writeFile(testRoot + 'main.log', 'I AM A LOGFILE')

// alter the version so the main thread assumes an update
jest.mock(root + 'package.json', () => ({
version: '1.1.1'
}))
await require(appRoot + 'src/main/index.js')
let err
try {
await require(appRoot + 'src/main/index.js')
} catch (_err) {
err = _err
}
expect(err.message).toBe(`Data was created with an incompatible app version
data=0.1.0 app=1.1.1`)

expect(fs.existsSync(testRoot.substr(0, testRoot.length - 1) + '_backup_1/main.log')).toBe(false)
expect(fs.readFileSync(testRoot + 'main.log')).toContain('I AM A LOGFILE')
let appVersion = fs.readFileSync(testRoot + 'app_version', 'utf8')
expect(appVersion).toBe('0.1.0')
})
})

describe('Update genesis.json', function () {
mainSetup()

it('should backup the genesis.json', async function () {
it('should error on changed genesis.json', async function () {
resetModulesKeepingFS()

// alter the genesis so the main thread assumes a change
let existingGenesis = JSON.parse(fs.readFileSync(testRoot + 'genesis.json', 'utf8'))
existingGenesis.genesis_time = (new Date()).toString()
fs.writeFileSync(testRoot + 'genesis.json', JSON.stringify(existingGenesis))
await require(appRoot + 'src/main/index.js')

expect(fs.existsSync(testRoot.substr(0, testRoot.length - 1) + '_backup_1/genesis.json')).toBe(true)
let err
try {
await require(appRoot + 'src/main/index.js')
} catch (_err) {
err = _err
}
expect(err.message).toBe('Genesis has changed')
})
})

Expand Down
15 changes: 15 additions & 0 deletions test/unit/specs/root.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const { join } = require('path')
const { homedir } = require('os')

describe('Root UI Directory', () => {
Object.assign(process.env, {
NODE_ENV: 'development',
COSMOS_NETWORK: 'app/networks/gaia-2',
COSMOS_HOME: ''
})

it('should create the correct path', () => {
let root = require('../../../app/src/root.js')
expect(root).toBe(join(homedir(), '.cosmos-dev/gaia-2'))
})
})

0 comments on commit 864925e

Please sign in to comment.