PR feedback

This commit is contained in:
Fabrice Marsaud 2016-01-22 15:37:23 +01:00
parent c304d9cc62
commit e8380b8a12
6 changed files with 122 additions and 106 deletions

View File

@ -493,13 +493,6 @@ exports.snapshot = snapshot
#---------------------------------------------------------------------
rollingDeltaBackup = $coroutine ({vm, remote, tag, depth}) ->
_remote = yield @getRemote remote
if not _remote?.path?
throw new Error "No such Remote #{remote}"
if not _remote.enabled
throw new Error "Backup remote #{remote} is disabled"
if _remote.type == 'smb'
throw new Error "Delta Backup is not supported for smb remotes"
return yield @rollingDeltaVmBackup({
vm,
remoteId: remote,
@ -628,11 +621,6 @@ exports.importBackup = importBackup
#---------------------------------------------------------------------
rollingBackup = $coroutine ({vm, remoteId, tag, depth, compress, onlyMetadata}) ->
remote = yield @getRemote remoteId
if not remote?.path?
throw new Error "No such Remote #{remoteId}"
if not remote.enabled
throw new Error "Backup remote #{remoteId} is disabled"
return yield @rollingBackupVm({
vm,
remoteId,

View File

@ -1,3 +1,5 @@
import eventToPromise from 'event-to-promise'
export default class RemoteHandlerAbstract {
constructor (remote) {
this._remote = remote
@ -8,42 +10,92 @@ export default class RemoteHandlerAbstract {
}
async sync () {
return await this._sync()
}
async _sync () {
throw new Error('Not implemented')
}
async forget () {
return await this._forget()
}
async _forget () {
throw new Error('Not implemented')
}
async outputFile (file, data, options) {
throw new Error('Not implemented')
return await this._outputFile(file, data, options)
}
async _outputFile (file, data, options) {
const stream = this.createOutputStream(file)
const promise = eventToPromise(stream, 'finish')
stream.end(data)
return promise
}
async readFile (file, options) {
throw new Error('Not implemented')
return await this._readFile(file, options)
}
async _readFile (file, options) {
const stream = this.createReadStream(file, options)
let data = ''
stream.on('data', d => data += d)
await eventToPromise(stream, 'end')
return data
}
async rename (oldPath, newPath) {
return await this._rename(oldPath, newPath)
}
async _rename (oldPath, newPath) {
throw new Error('Not implemented')
}
async list (dir = undefined) {
async list (dir = '.') {
return await this._list(dir)
}
async _list (dir = '.') {
throw new Error('Not implemented')
}
async createReadStream (file) {
async createReadStream (file, options) {
const length = await this.getSize(file)
const stream = await this._createReadStream(file)
stream.length = length
return stream
}
async _createReadStream (file, options) {
throw new Error('Not implemented')
}
async createOutputStream (file) {
async createOutputStream (file, options) {
return await this._createOutputStream(file)
}
async _createOutputStream (file, options) {
throw new Error('Not implemented')
}
async unlink (file) {
return await this._unlink(file)
}
async _unlink (file) {
throw new Error('Not implemented')
}
async getSize (file) {
return await this._getSize(file)
}
async _getSize (file) {
throw new Error('Not implement')
}
}

View File

@ -1,23 +1,23 @@
import fs from 'fs-promise'
import RemoteHandlerAbstract from './abstract'
import {dirname} from 'path'
import startsWith from 'lodash.startswith'
import {noop} from '../utils'
import {resolve} from 'path'
export default class LocalHandler extends RemoteHandlerAbstract {
constructor (remote) {
super(remote)
this.forget = noop
}
_getFilePath (file) {
const parts = [this._remote.path]
if (file) {
parts.push(file)
}
return parts.join('/')
const path = resolve.apply(null, parts)
if (!startsWith(path, this._remote.path)) {
throw new Error('Remote path is unavailable')
}
return path
}
async sync () {
async _sync () {
if (this._remote.enabled) {
try {
await fs.ensureDir(this._remote.path)
@ -30,39 +30,39 @@ export default class LocalHandler extends RemoteHandlerAbstract {
return this._remote
}
async outputFile (file, data, options) {
const path = this._getFilePath(file)
await fs.ensureDir(dirname(path))
await fs.writeFile(path, data, options)
async _forget () {
return noop()
}
async readFile (file, options) {
async _outputFile (file, data, options) {
await fs.outputFile(this._getFilePath(file), data, options)
}
async _readFile (file, options) {
return await fs.readFile(this._getFilePath(file), options)
}
async rename (oldPath, newPath) {
async _rename (oldPath, newPath) {
return await fs.rename(this._getFilePath(oldPath), this._getFilePath(newPath))
}
async list (dir = undefined) {
async _list (dir = '.') {
return await fs.readdir(this._getFilePath(dir))
}
async createReadStream (file) {
return fs.createReadStream(this._getFilePath(file))
async _createReadStream (file, options) {
return await fs.createReadStream(this._getFilePath(file), options)
}
async createOutputStream (file, options) {
const path = this._getFilePath(file)
await fs.ensureDir(dirname(path))
return fs.createWriteStream(path, options)
async _createOutputStream (file, options) {
return await fs.createOutputStream(this._getFilePath(file), options)
}
async unlink (file) {
return fs.unlink(this._getFilePath(file))
async _unlink (file) {
return await fs.unlink(this._getFilePath(file))
}
async getSize (file) {
async _getSize (file) {
const stats = await fs.stat(this._getFilePath(file))
return stats.size
}

View File

@ -1,20 +1,11 @@
import fs from 'fs-promise'
import RemoteHandlerAbstract from './abstract'
import {dirname} from 'path'
import LocalHandler from './local'
import {exec} from 'child_process'
import {forEach, promisify} from '../utils'
const execAsync = promisify(exec)
export default class NfsHandler extends RemoteHandlerAbstract {
_getFilePath (file) {
const parts = [this._remote.path]
if (file) {
parts.push(file)
}
return parts.join('/')
}
export default class NfsHandler extends LocalHandler {
async _loadRealMounts () {
let stdout
try {
@ -48,7 +39,7 @@ export default class NfsHandler extends RemoteHandlerAbstract {
return await execAsync(`mount -t nfs ${remote.host}:${remote.share} ${remote.path}`)
}
async sync () {
async _sync () {
await this._loadRealMounts()
if (this._matchesRealMount(this._remote) && !this._remote.enabled) {
try {
@ -68,7 +59,7 @@ export default class NfsHandler extends RemoteHandlerAbstract {
return this._remote
}
async forget () {
async _forget () {
try {
await this._umount(this._remote)
} catch (_) {
@ -79,41 +70,4 @@ export default class NfsHandler extends RemoteHandlerAbstract {
async _umount (remote) {
await execAsync(`umount ${remote.path}`)
}
async outputFile (file, data, options) {
const path = this._getFilePath(file)
await fs.ensureDir(dirname(path))
await fs.writeFile(path, data, options)
}
async readFile (file, options) {
return await fs.readFile(this._getFilePath(file), options)
}
async rename (oldPath, newPath) {
return await fs.rename(this._getFilePath(oldPath), this._getFilePath(newPath))
}
async list (dir = undefined) {
return await fs.readdir(this._getFilePath(dir))
}
async createReadStream (file) {
return fs.createReadStream(this._getFilePath(file))
}
async createOutputStream (file, options) {
const path = this._getFilePath(file)
await fs.ensureDir(dirname(path))
return fs.createWriteStream(path, options)
}
async unlink (file) {
return fs.unlink(this._getFilePath(file))
}
async getSize (file) {
const stats = await fs.stat(this._getFilePath(file))
return stats.size
}
}

View File

@ -5,7 +5,7 @@ import RemoteHandlerAbstract from './abstract'
export default class SmbHandler extends RemoteHandlerAbstract {
constructor (remote) {
super(remote)
this.forget = noop
this._forget = noop
}
_getClient (remote) {
@ -19,6 +19,9 @@ export default class SmbHandler extends RemoteHandlerAbstract {
}
_getFilePath (file) {
if (file === '.') {
file = undefined
}
const parts = []
if (this._remote.path !== '') {
parts.push(this._remote.path)
@ -29,13 +32,13 @@ export default class SmbHandler extends RemoteHandlerAbstract {
return parts.join('\\')
}
_getDirname (file) {
_dirname (file) {
const parts = file.split('\\')
parts.pop()
return parts.join('\\')
}
async sync () {
async _sync () {
if (this._remote.enabled) {
try {
// Check access (smb2 does not expose connect in public so far...)
@ -48,10 +51,10 @@ export default class SmbHandler extends RemoteHandlerAbstract {
return this._remote
}
async outputFile (file, data, options) {
async _outputFile (file, data, options) {
const client = this._getClient(this._remote)
const path = this._getFilePath(file)
const dir = this._getDirname(path)
const dir = this._dirname(path)
try {
if (dir) {
await client.ensureDir(dir)
@ -62,7 +65,7 @@ export default class SmbHandler extends RemoteHandlerAbstract {
}
}
async readFile (file, options) {
async _readFile (file, options) {
const client = this._getClient(this._remote)
try {
return await client.readFile(this._getFilePath(file), options)
@ -71,7 +74,7 @@ export default class SmbHandler extends RemoteHandlerAbstract {
}
}
async rename (oldPath, newPath) {
async _rename (oldPath, newPath) {
const client = this._getClient(this._remote)
try {
return await client.rename(this._getFilePath(oldPath), this._getFilePath(newPath))
@ -80,7 +83,7 @@ export default class SmbHandler extends RemoteHandlerAbstract {
}
}
async list (dir = undefined) {
async _list (dir = '.') {
const client = this._getClient(this._remote)
try {
return await client.readdir(this._getFilePath(dir))
@ -89,23 +92,23 @@ export default class SmbHandler extends RemoteHandlerAbstract {
}
}
async createReadStream (file) {
async _createReadStream (file, options) {
const client = this._getClient(this._remote)
const stream = await client.createReadStream(this._getFilePath(file))
const stream = await client.createReadStream(this._getFilePath(file), options) // FIXME ensure that options are properly handled by @marsaud/smb2
stream.on('end', () => client.close())
return stream
}
async createOutputStream (file, options) {
async _createOutputStream (file, options) {
const client = this._getClient(this._remote)
const path = this._getFilePath(file)
const dir = this._getDirname(path)
const dir = this._dirname(path)
let stream
try {
if (dir) {
await client.ensureDir(dir)
}
stream = await client.createWriteStream(path, options/* , { flags: 'wx' }*/) // TODO ensure that wx flag is properly handled by @marsaud/smb2
stream = await client.createWriteStream(path, options) // FIXME ensure that options are properly handled by @marsaud/smb2
} catch (err) {
client.close()
throw err
@ -114,7 +117,7 @@ export default class SmbHandler extends RemoteHandlerAbstract {
return stream
}
async unlink (file) {
async _unlink (file) {
const client = this._getClient(this._remote)
try {
return await client.unlink(this._getFilePath(file))
@ -123,7 +126,7 @@ export default class SmbHandler extends RemoteHandlerAbstract {
}
}
async getSize (file) {
async _getSize (file) {
const client = await this._getClient(this._remote)
try {
return await client.getSize(this._getFilePath(file))

View File

@ -378,6 +378,17 @@ export default class {
async rollingDeltaVmBackup ({vm, remoteId, tag, depth}) {
const remote = await this._xo.getRemote(remoteId)
if (!remote) {
throw new Error(`No such Remote ${remoteId}`)
}
if (!remote.enabled) {
throw new Error(`Remote ${remoteId} is disabled`)
}
if (remote.type === 'smb') {
throw new Error('Delta Backup is not supported for smb remotes')
}
const dir = `vm_delta_${tag}_${vm.uuid}`
const info = {
@ -589,6 +600,14 @@ export default class {
async rollingBackupVm ({vm, remoteId, tag, depth, compress, onlyMetadata}) {
const remote = await this._xo.getRemote(remoteId)
if (!remote) {
throw new Error(`No such Remote s{remoteId}`)
}
if (!remote.enabled) {
throw new Error(`Backup remote ${remoteId} is disabled`)
}
const handler = this._xo.getRemoteHandler(remote)
const files = await handler.list()