From a4880cd017903b2c5a2f2ca5ad69ff97cc63142e Mon Sep 17 00:00:00 2001 From: Fabrice Marsaud Date: Thu, 30 Jun 2016 15:00:00 +0200 Subject: [PATCH] feat(remote.test): perform a write/read test on a remote (#354) See vatesfr/xo-web#1075 --- src/api/remote.js | 18 ++++++++++++++---- src/remote-handlers/abstract.js | 27 +++++++++++++++++++++++++++ src/utils.js | 6 ++++-- src/xo-mixins/remotes.js | 5 +++++ 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/src/api/remote.js b/src/api/remote.js index 671e74fbc..b72f2e223 100644 --- a/src/api/remote.js +++ b/src/api/remote.js @@ -1,12 +1,12 @@ export async function getAll () { - return /* await */ this.getAllRemotes() + return this.getAllRemotes() } getAll.permission = 'admin' getAll.description = 'Gets all existing fs remote points' export async function get ({id}) { - return /* await */ this.getRemote(id) + return this.getRemote(id) } get.permission = 'admin' @@ -15,8 +15,18 @@ get.params = { id: {type: 'string'} } +export async function test ({id}) { + return this.testRemote(id) +} + +test.permission = 'admin' +test.description = 'Performs a read/write matching test on a remote point' +test.params = { + id: {type: 'string'} +} + export async function list ({id}) { - return /* await */ this.listRemoteBackups(id) + return this.listRemoteBackups(id) } list.permission = 'admin' @@ -26,7 +36,7 @@ list.params = { } export async function create ({name, url}) { - return /* await */ this.createRemote({name, url}) + return this.createRemote({name, url}) } create.permission = 'admin' diff --git a/src/remote-handlers/abstract.js b/src/remote-handlers/abstract.js index 3b552f99d..051104acf 100644 --- a/src/remote-handlers/abstract.js +++ b/src/remote-handlers/abstract.js @@ -7,6 +7,7 @@ import { import { addChecksumToReadStream, + getPseudoRandomBytes, noop, pCatch, streamToBuffer, @@ -47,6 +48,32 @@ export default class RemoteHandlerAbstract { throw new Error('Not implemented') } + async test () { + const testFileName = `${Date.now()}.test` + const data = getPseudoRandomBytes(1024 * 1024) + let step = 'write' + try { + await this.outputFile(testFileName, data) + step = 'read' + const read = await this.readFile(testFileName) + if (data.compare(read) !== 0) { + throw new Error('output and input did not match') + } + return { + success: true + } + } catch (error) { + return { + success: false, + step, + file: testFileName, + error: error.message || String(error) + } + } finally { + this.unlink(testFileName).catch(noop) + } + } + async outputFile (file, data, options) { return this._outputFile(file, data, { flags: 'wx', diff --git a/src/utils.js b/src/utils.js index c9af6b4c1..2e8832678 100644 --- a/src/utils.js +++ b/src/utils.js @@ -170,7 +170,7 @@ export function extractProperty (obj, prop) { // ------------------------------------------------------------------- -export const generateUnsecureToken = (n = 32) => { +export const getPseudoRandomBytes = n => { const bytes = new Buffer(n) const odd = n & 1 @@ -182,9 +182,11 @@ export const generateUnsecureToken = (n = 32) => { bytes.writeUInt8(Math.random() * 256 | 0, n - 1) } - return base64url(bytes) + return bytes } +export const generateUnsecureToken = (n = 32) => base64url(getPseudoRandomBytes(n)) + // Generate a secure random Base64 string. export const generateToken = (randomBytes => { return (n = 32) => randomBytes(n).then(base64url) diff --git a/src/xo-mixins/remotes.js b/src/xo-mixins/remotes.js index 902998fa7..5a0673993 100644 --- a/src/xo-mixins/remotes.js +++ b/src/xo-mixins/remotes.js @@ -54,6 +54,11 @@ export default class { return new Handler[type](remote) } + async testRemote (remote) { + const handler = await this.getRemoteHandler(remote) + return handler.test() + } + async getAllRemotes () { return this._remotes.get() }