From 083b8a6d7b38a130e28fdd293bdad3de92759eec Mon Sep 17 00:00:00 2001 From: Girish Ramakrishnan Date: Sat, 13 Apr 2019 17:09:15 -0700 Subject: [PATCH] Add preserveSecs to backup entries We want to keep updates automatic and don't want to keep reminding users that apps are getting updated etc (i.e beyong the weekly digest). The reason to remind them is so they can check if the app updated correctly. in some very corner cases, the app is not really checked upon for a while and people forget about them until they check them later. in such cases, it's too late to recover because the backpus gets cleaned up. this preserve seconds fields, let's us mark 'update' backups for preservation for 3 weeks. --- CHANGES | 1 + .../20190413140945-backups-add-preserveSecs.js | 15 +++++++++++++++ migrations/schema.sql | 1 + src/apptask.js | 5 +++-- src/backupdb.js | 2 +- src/backups.js | 18 +++++++++++------- 6 files changed, 32 insertions(+), 10 deletions(-) create mode 100644 migrations/20190413140945-backups-add-preserveSecs.js diff --git a/CHANGES b/CHANGES index 788d8814c..c353eb738 100644 --- a/CHANGES +++ b/CHANGES @@ -1581,4 +1581,5 @@ * (backup) Do not abort archive if file(s) disappear * Show app upstream version in the info dialog * Add Scaleway ObjectStorage backup backend +* Preserve update backups for 3 weeks diff --git a/migrations/20190413140945-backups-add-preserveSecs.js b/migrations/20190413140945-backups-add-preserveSecs.js new file mode 100644 index 000000000..73a78230a --- /dev/null +++ b/migrations/20190413140945-backups-add-preserveSecs.js @@ -0,0 +1,15 @@ +'use strict'; + +exports.up = function(db, callback) { + db.runSql('ALTER TABLE backups ADD COLUMN preserveSecs INTEGER DEFAULT 0', function (error) { + if (error) console.error(error); + callback(error); + }); +}; + +exports.down = function(db, callback) { + db.runSql('ALTER TABLE backups DROP COLUMN preserveSecs', function (error) { + if (error) console.error(error); + callback(error); + }); +}; diff --git a/migrations/schema.sql b/migrations/schema.sql index c14c4ba91..83a7b3e5c 100644 --- a/migrations/schema.sql +++ b/migrations/schema.sql @@ -141,6 +141,7 @@ CREATE TABLE IF NOT EXISTS backups( state VARCHAR(16) NOT NULL, manifestJson TEXT, /* to validate if the app can be installed in this version of box */ format VARCHAR(16) DEFAULT "tgz", + preserveSecs INTEGER DEFAULT 0, PRIMARY KEY (id)); diff --git a/src/apptask.js b/src/apptask.js index 8c92bec86..52332e475 100644 --- a/src/apptask.js +++ b/src/apptask.js @@ -586,7 +586,7 @@ function backup(app, callback) { async.series([ updateApp.bind(null, app, { installationProgress: '10, Backing up' }), - backups.backupApp.bind(null, app, (progress) => updateApp(app, { installationProgress: `30, ${progress.message}` }, NOOP_CALLBACK)), + backups.backupApp.bind(null, app, { /* options */ }, (progress) => updateApp(app, { installationProgress: `30, ${progress.message}` }, NOOP_CALLBACK)), // done! function (callback) { @@ -706,7 +706,8 @@ function update(app, callback) { async.series([ updateApp.bind(null, app, { installationProgress: '15, Backing up app' }), - backups.backupApp.bind(null, app, (progress) => updateApp(app, { installationProgress: `15, Backup - ${progress.message}` }, NOOP_CALLBACK)) + // preserve update backups for 3 weeks + backups.backupApp.bind(null, app, { preserveSecs: 3*7*24*60*60 }, (progress) => updateApp(app, { installationProgress: `15, Backup - ${progress.message}` }, NOOP_CALLBACK)) ], function (error) { if (error) error.backupError = true; next(error); diff --git a/src/backupdb.js b/src/backupdb.js index 0048d8f7d..146659b48 100644 --- a/src/backupdb.js +++ b/src/backupdb.js @@ -6,7 +6,7 @@ var assert = require('assert'), safe = require('safetydance'), util = require('util'); -var BACKUPS_FIELDS = [ 'id', 'creationTime', 'version', 'type', 'dependsOn', 'state', 'manifestJson', 'format' ]; +var BACKUPS_FIELDS = [ 'id', 'creationTime', 'version', 'type', 'dependsOn', 'state', 'manifestJson', 'format', 'preserveSecs' ]; exports = module.exports = { add: add, diff --git a/src/backups.js b/src/backups.js index 8a4d3ebdd..85dabd31b 100644 --- a/src/backups.js +++ b/src/backups.js @@ -867,9 +867,10 @@ function snapshotApp(app, progressCallback, callback) { }); } -function rotateAppBackup(backupConfig, app, timestamp, progressCallback, callback) { +function rotateAppBackup(backupConfig, app, options, timestamp, progressCallback, callback) { assert.strictEqual(typeof backupConfig, 'object'); assert.strictEqual(typeof app, 'object'); + assert.strictEqual(typeof options, 'object'); assert.strictEqual(typeof timestamp, 'string'); assert.strictEqual(typeof progressCallback, 'function'); assert.strictEqual(typeof callback, 'function'); @@ -892,7 +893,7 @@ function rotateAppBackup(backupConfig, app, timestamp, progressCallback, callbac copy.on('done', function (copyBackupError) { const state = copyBackupError ? backupdb.BACKUP_STATE_ERROR : backupdb.BACKUP_STATE_NORMAL; - backupdb.update(backupId, { state: state }, function (error) { + backupdb.update(backupId, { preserveSecs: options.preserveSecs || 0, state: state }, function (error) { if (copyBackupError) return callback(copyBackupError); if (error) return callback(new BackupsError(BackupsError.INTERNAL_ERROR, error)); @@ -933,8 +934,9 @@ function uploadAppSnapshot(backupConfig, app, progressCallback, callback) { }); } -function backupAppWithTimestamp(app, timestamp, progressCallback, callback) { +function backupAppWithTimestamp(app, options, timestamp, progressCallback, callback) { assert.strictEqual(typeof app, 'object'); + assert.strictEqual(typeof options, 'object'); assert.strictEqual(typeof timestamp, 'string'); assert.strictEqual(typeof progressCallback, 'function'); assert.strictEqual(typeof callback, 'function'); @@ -947,13 +949,14 @@ function backupAppWithTimestamp(app, timestamp, progressCallback, callback) { uploadAppSnapshot(backupConfig, app, progressCallback, function (error) { if (error) return callback(error); - rotateAppBackup(backupConfig, app, timestamp, progressCallback, callback); + rotateAppBackup(backupConfig, app, options, timestamp, progressCallback, callback); }); }); } -function backupApp(app, progressCallback, callback) { +function backupApp(app, options, progressCallback, callback) { assert.strictEqual(typeof app, 'object'); + assert.strictEqual(typeof options, 'object'); assert.strictEqual(typeof progressCallback, 'function'); assert.strictEqual(typeof callback, 'function'); @@ -961,7 +964,7 @@ function backupApp(app, progressCallback, callback) { debug(`backupApp - Backing up ${app.fqdn} with timestamp ${timestamp}`); - backupAppWithTimestamp(app, timestamp, progressCallback, callback); + backupAppWithTimestamp(app, options, timestamp, progressCallback, callback); } // this function expects you to have a lock. Unlike other progressCallback this also has a progress field @@ -986,7 +989,7 @@ function backupBoxAndApps(progressCallback, callback) { return iteratorCallback(null, null); // nothing to backup } - backupAppWithTimestamp(app, timestamp, (progress) => progressCallback({ percent: percent, message: progress.message }), function (error, backupId) { + backupAppWithTimestamp(app, { /* options */ }, timestamp, (progress) => progressCallback({ percent: percent, message: progress.message }), function (error, backupId) { if (error && error.reason !== BackupsError.BAD_STATE) { debugApp(app, 'Unable to backup', error); return iteratorCallback(error); @@ -1101,6 +1104,7 @@ function cleanupAppBackups(backupConfig, referencedAppBackups, callback) { async.eachSeries(appBackups, function iterator(appBackup, iteratorDone) { if (referencedAppBackups.indexOf(appBackup.id) !== -1) return iteratorDone(); + if ((now - appBackup.creationTime) < (appBackup.preserveSecs * 1000)) return iteratorDone(); if ((now - appBackup.creationTime) < (backupConfig.retentionSecs * 1000)) return iteratorDone(); debug('cleanupAppBackups: removing %s', appBackup.id);