diff --git a/CHANGES b/CHANGES index 035108bde..04350fb85 100644 --- a/CHANGES +++ b/CHANGES @@ -1743,4 +1743,5 @@ * Add app fqdn to backup progress message * import: add option to import app in-place * import: add option to import app from arbitrary backup config +* Show download progress for rsync backups diff --git a/src/backups.js b/src/backups.js index 221dd398d..6e067c95e 100644 --- a/src/backups.js +++ b/src/backups.js @@ -229,14 +229,14 @@ function createReadStream(sourceFile, key) { stream.on('error', function (error) { debug('createReadStream: read stream error.', error); - ps.emit('error', new BoxError(BoxError.EXTERNAL_ERROR, error.message)); + ps.emit('error', new BoxError(BoxError.FS_ERROR, error.message)); }); if (key !== null) { var encrypt = crypto.createCipher('aes-256-cbc', key); encrypt.on('error', function (error) { debug('createReadStream: encrypt stream error.', error); - ps.emit('error', new BoxError(BoxError.EXTERNAL_ERROR, error.message)); + ps.emit('error', new BoxError(BoxError.CRYPTO_ERROR, error.message)); }); return stream.pipe(encrypt).pipe(ps); } else { @@ -249,17 +249,25 @@ function createWriteStream(destFile, key) { assert(key === null || typeof key === 'string'); var stream = fs.createWriteStream(destFile); + var ps = progressStream({ time: 10000 }); // display a progress every 10 seconds + + stream.on('error', function (error) { + debug('createWriteStream: write stream error.', error); + ps.emit('error', new BoxError(BoxError.FS_ERROR, error.message)); + }); if (key !== null) { var decrypt = crypto.createDecipher('aes-256-cbc', key); decrypt.on('error', function (error) { debug('createWriteStream: decrypt stream error.', error); + ps.emit('error', new BoxError(BoxError.CRYPTO_ERROR, error.message)); }); - decrypt.pipe(stream); - return decrypt; + ps.pipe(decrypt).pipe(stream); } else { - return stream; + ps.pipe(stream); } + + return ps; } function tarPack(dataLayout, key, callback) { @@ -571,6 +579,12 @@ function downloadDir(backupConfig, backupFilePath, dataLayout, progressCallback, async.retry({ times: 5, interval: 20000 }, function (retryCallback) { let destStream = createWriteStream(destFilePath, backupConfig.key || null); + destStream.on('progress', function (progress) { + const transferred = Math.round(progress.transferred/1024/1024), speed = Math.round(progress.speed/1024/1024); + if (!transferred && !speed) return progressCallback({ message: `Downloading ${entry.fullPath}` }); // 0M@0Mbps looks wrong + progressCallback({ message: `Downloading ${entry.fullPath}: ${transferred}M@${speed}Mbps` }); + }); + // protect against multiple errors. must destroy the write stream so that a previous retry does not write let closeAndRetry = once((error) => { if (error) progressCallback({ message: `Download ${entry.fullPath} to ${destFilePath} errored: ${error.message}` });