From df05f82d6602fa2ec9cdd52d8cee5d3cf2833709 Mon Sep 17 00:00:00 2001 From: Matt Lyons Date: Wed, 21 Feb 2018 17:48:34 -0700 Subject: [PATCH] Fix incorrect snapshot count when incomplete backup can't be deleted Fix exiting prematurely with code 0 if script pre-hook file does not exist --- README.md | 3 +++ lib/Incrementer.js | 46 ++++++++++++++++++++++++++++++--------------- lib/LogGenerator.js | 3 +-- 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index cdc6219..470aaf5 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,9 @@ See [Do It Yourself Backup System Using Rsync](http://www.sanitarium.net/golug/r - It is recommended to schedule this command to run regularly in cron or alike - When scheduling this script run it is best to update `rsync-snapshot` regularly - Execute `npm update -g rsync-snapshot` to update to latest **minor** version +- Backups will be in a folder named by time and date (ex: `2018-02-21.19-06-26`) + - Anything may be appended (manually) to folder names to add user friendly info (ex: `2018-02-21.19-06-26.createdDatabase`) as long as `.incomplete` is not appended (which is reserved for backups in progress, failed or canceled) + - `ls -1 | sort -r` can be used to sort backups (most recent to least recent) #### Parameters *Note: To wrap strings double quotes must be used. Ex: `--shell "ssh -p 2222"` must be used to specify ssh parameters. Single quotes will not be parsed correctly.* diff --git a/lib/Incrementer.js b/lib/Incrementer.js index ecfb36e..0381568 100644 --- a/lib/Incrementer.js +++ b/lib/Incrementer.js @@ -4,6 +4,7 @@ const debug = require('debug')('RsyncSnapshot:lib:Incrementer'); const spawn = require('child_process').spawn; const execFile = require('child_process').execFile; const path = require('path'); +const fs = require('fs'); class Incrementer{ constructor(logger, shell, rsyncDest){ @@ -89,13 +90,15 @@ class Incrementer{ let bashCommand = `mkdir -p '${this.escapeQuotes(this.tempDest)}';`; //Make Temp Dir (even though it will be deleted parent dirs won't be) bashCommand+= `cd '${this.escapeQuotes(this.remoteDirectory)}';`; bashCommand+= `find . -maxdepth 1 -type d -name '*.incomplete' | xargs rm -rf;`; //Cleanup old incomplete backups - bashCommand+= `ls -1 | wc -l;`; //Count snapshots (for potential snapshot deletion after backup) + bashCommand+= `ls -1 | wc -l;`; //Folders (So we know when we got all info from ls) bashCommand+= `ls -1 | sort -r;`; //Print Snapshots 1 on a line most recent to least recent this.generator.logger.log('stdout')({msgType: 'progress', status: 'Deleting Incomplete Backups'}); let hasError = false; - let ssh = this.runCommand(bashCommand, (error) => { + let folderCount = 0; + let foldersFound = 0; + let ssh = this.runCommand(bashCommand, (error) => { error = error.toString(); this.generator.logger.log('stderr')('An error occurred connecting to server while preparing for backup:'); this.generator.logger.log('stderr')(error); @@ -105,15 +108,20 @@ class Incrementer{ for(let line of output){ if(!line) { - // Whatever - } else if(line.match(/^\d*\s*$/g)){ //Folder Count - try { - this.snapshotCount = Number(line.trim()); - } catch(e){ - this.generator.logger.log('stderr')(`Error determining number of snapshots, found: ${line}`); - } + continue; + } else if(line.match(/^\d*\s*$/g) && !folderCount){ //Folder Count + folderCount = Number(line.trim()); } else if(line.match(/^\d{4}(-\d{2}){2}.(-?\d{2}){3}(?!.incomplete$)/g)){ //If folder matches the naming format (and is not incomplete) - this.linkDest = path.join(this.remoteDirectory, line); + if(!this.linkDest) + this.linkDest = path.join(this.remoteDirectory, line); + + foldersFound++; + this.snapshotCount++; + } else { //Folder but not snapshot folder + foldersFound++; + } + + if(folderCount === foldersFound){ resolve(true); return; } @@ -154,15 +162,23 @@ class Incrementer{ } executeScriptHook(executablePath){ - return new Promise((resolve, reject) => { + return new Promise(async (resolve, reject) => { executablePath = path.resolve(executablePath); let scriptName = path.basename(executablePath); - this.generator.logger.log('stdout')({msgType: 'progress', status: `${scriptName} Started`}); - - let proc; try { - proc = execFile(executablePath); + await new Promise((resolve, reject) => { //Throw exception if can't execute file + fs.access(executablePath, fs.constants.X_OK, (err) => { + if(err) + reject(err); + else + resolve(); + }); + }); + + this.generator.logger.log('stdout')({msgType: 'progress', status: `${scriptName} Started`}); + + let proc = execFile(executablePath); proc.stdout.on('data', (data) => { let str = data.toString(); diff --git a/lib/LogGenerator.js b/lib/LogGenerator.js index 7a84255..d95820b 100644 --- a/lib/LogGenerator.js +++ b/lib/LogGenerator.js @@ -65,8 +65,7 @@ class LogGenerator { async setOutputFile(file, level) { //Will reject if file is not writable if (file) { - if (!path.isAbsolute(file)) //Convert relative path to absolute - file = path.resolve(file); + file = path.resolve(file); //Will throw exception if can't write await new Promise((resolve, reject) => {