Create backup tar added

This commit is contained in:
Kasra Bigdeli
2019-02-17 18:04:46 -08:00
parent 44835f41bd
commit 03763e839a
5 changed files with 202 additions and 11 deletions
+16
View File
@@ -8,11 +8,27 @@ import SystemRouteSelfHostRegistry = require('./SystemRouteSelfHostRegistry')
import CaptainConstants = require('../../utils/CaptainConstants')
import InjectionExtractor = require('../../injection/InjectionExtractor')
import Utils from '../../utils/Utils'
import * as path from 'path'
const router = express.Router()
router.use('/selfhostregistry/', SystemRouteSelfHostRegistry)
router.post('/createbackup/', function(req, res, next) {
const backupManager = CaptainManager.get().getBackupManager()
Promise.resolve()
.then(function() {
return backupManager.createBackup()
})
.then(function(pathOfBackup) {
res.sendFile(pathOfBackup, {}, function(err) {
backupManager.deleteBackupDirectoryIfExists()
})
})
.catch(ApiStatusCodes.createCatcher(res))
})
router.post('/changerootdomain/', function(req, res, next) {
let requestedCustomDomain = Utils.removeHttpHttps(
(req.body.rootDomain || '').toLowerCase()
+150
View File
@@ -0,0 +1,150 @@
import uuid = require('uuid/v4')
import SshClientImport = require('ssh2')
import request = require('request')
import fs = require('fs-extra')
import CaptainConstants = require('../../utils/CaptainConstants')
import Logger = require('../../utils/Logger')
import LoadBalancerManager = require('./LoadBalancerManager')
import EnvVars = require('../../utils/EnvVars')
import CertbotManager = require('./CertbotManager')
import SelfHostedDockerRegistry = require('./SelfHostedDockerRegistry')
import ApiStatusCodes = require('../../api/ApiStatusCodes')
import DataStoreProvider = require('../../datastore/DataStoreProvider')
import DataStore = require('../../datastore/DataStore')
import DockerApi from '../../docker/DockerApi'
import { IRegistryTypes, IRegistryInfo } from '../../models/IRegistryInfo'
import MigrateCaptainDuckDuck from '../../utils/MigrateCaptainDuckDuck'
import Authenticator = require('../Authenticator')
import * as tar from 'tar'
const BACKUP_JSON = 'backup.json'
export interface IBackupCallbacks {
getNodesInfo: () => Promise<ServerDockerInfo[]>
getCaptainSalt: () => string
}
export default class BackupManager {
private longOperationInProgress: boolean
constructor(
private iBackupCallbacks: IBackupCallbacks,
private certbotManager: CertbotManager
) {
//
}
lock() {
if (this.longOperationInProgress) {
throw ApiStatusCodes.createError(
ApiStatusCodes.STATUS_ERROR_GENERIC,
'Another operation is in process for Certbot. Please wait a few seconds and try again.'
)
}
this.longOperationInProgress = true
}
unlock() {
this.longOperationInProgress = false
}
isRunning() {
return !!this.longOperationInProgress
}
createBackup() {
const self = this
return Promise.resolve() //
.then(function() {
self.certbotManager.lock()
return self
.createBackupInternal()
.then(function(data) {
self.certbotManager.unlock()
return data
})
.catch(function(err) {
self.certbotManager.unlock()
throw err
})
})
}
createBackupInternal() {
const self = this
return Promise.resolve() //
.then(function() {
self.lock()
// Check if exist /captain/temp/backup, delete directory
// Create directory /captain/temp/backup/raw
// Copy /captain/data to .../backup/raw/data
// Ensure .../backup/raw/meta/backup.json
// Create tar file FROM: .../backup/raw/ TO: .../backup/backup.tar
const RAW = CaptainConstants.captainRootDirectoryBackup + '/raw'
return Promise.resolve() //
.then(function() {
return self.deleteBackupDirectoryIfExists()
})
.then(function() {
return fs.ensureDir(RAW)
})
.then(function() {
return fs.copy(
CaptainConstants.captainDataDirectory,
RAW + '/data',
{ preserveTimestamps: true }
)
})
.then(function() {
return self.iBackupCallbacks.getNodesInfo()
})
.then(function(nodes) {
return fs.outputJson(RAW + '/meta/' + BACKUP_JSON, {
salt: self.iBackupCallbacks.getCaptainSalt(),
nodes: nodes,
})
})
.then(function() {
const tarFilePath =
CaptainConstants.captainRootDirectoryBackup +
'/backup.tar'
return tar
.c(
{
file: tarFilePath,
cwd: RAW,
},
['./']
)
.then(function() {
return tarFilePath
})
})
.then(function(tarFilePath) {
self.unlock()
return tarFilePath
})
.catch(function(err) {
self.unlock()
throw err
})
})
}
deleteBackupDirectoryIfExists() {
return Promise.resolve() //
.then(function() {
if (
fs.existsSync(CaptainConstants.captainRootDirectoryBackup)
) {
return fs.remove(
CaptainConstants.captainRootDirectoryBackup
)
}
})
}
}
+13 -1
View File
@@ -15,6 +15,7 @@ import DockerApi from '../../docker/DockerApi'
import { IRegistryTypes, IRegistryInfo } from '../../models/IRegistryInfo'
import MigrateCaptainDuckDuck from '../../utils/MigrateCaptainDuckDuck'
import Authenticator = require('../Authenticator')
import BackupManager from './BackupManager'
const DEBUG_SALT = 'THIS IS NOT A REAL CERTIFICATE'
const SshClient = SshClientImport.Client
@@ -33,6 +34,7 @@ class CaptainManager {
private certbotManager: CertbotManager
private loadBalancerManager: LoadBalancerManager
private dockerRegistry: SelfHostedDockerRegistry
private backupManager: BackupManager
private myNodeId: string | undefined
private inited: boolean
private waitUntilRestarted: boolean
@@ -60,6 +62,7 @@ class CaptainManager {
this.captainSalt = ''
this.consecutiveHealthCheckFailCount = 0
this.healthCheckUuid = uuid()
this.backupManager = new BackupManager(this, this.certbotManager)
}
initialize() {
@@ -405,8 +408,16 @@ class CaptainManager {
return this.healthCheckUuid
}
getBackupManager() {
return this.backupManager
}
isInitialized() {
return this.inited && !this.waitUntilRestarted
return (
this.inited &&
!this.waitUntilRestarted &&
!this.backupManager.isRunning()
)
}
getCaptainImageTags() {
@@ -978,6 +989,7 @@ class CaptainManager {
resetSelf() {
const self = this
Logger.d('Captain is resetting itself!')
self.waitUntilRestarted = true
return new Promise<void>(function(resolve, reject) {
setTimeout(function() {
const promiseToIgnore = self.dockerApi.updateService(
+21 -10
View File
@@ -210,19 +210,27 @@ class CertbotManager {
})
}
lock() {
if (this.isOperationInProcess) {
throw ApiStatusCodes.createError(
ApiStatusCodes.STATUS_ERROR_GENERIC,
'Another operation is in process for Certbot. Please wait a few seconds and try again.'
)
}
this.isOperationInProcess = true
}
unlock() {
this.isOperationInProcess = false
}
runCommand(cmd: string[]) {
const dockerApi = this.dockerApi
const self = this
return Promise.resolve().then(function() {
if (self.isOperationInProcess) {
throw ApiStatusCodes.createError(
ApiStatusCodes.STATUS_ERROR_GENERIC,
'Another operation is in process for Certbot. Please wait a few seconds and try again.'
)
}
self.isOperationInProcess = true
self.lock()
const nonInterActiveCommand = [...cmd, '--non-interactive']
return dockerApi
@@ -231,11 +239,11 @@ class CertbotManager {
nonInterActiveCommand
)
.then(function(data) {
self.isOperationInProcess = false
self.unlock()
return data
})
.catch(function(error) {
self.isOperationInProcess = false
self.unlock()
throw error
})
})
@@ -286,6 +294,9 @@ class CertbotManager {
.then(function(output) {
// Ignore output :)
})
.catch(function(err) {
Logger.e(err)
})
}
init(myNodeId: string) {
+2
View File
@@ -79,6 +79,8 @@ let data = {
captainRootDirectoryTemp: CAPTAIN_ROOT_DIRECTORY_TEMP,
captainRootDirectoryBackup: CAPTAIN_ROOT_DIRECTORY_TEMP + '/backup',
captainRawSourceDirectoryBase: CAPTAIN_ROOT_DIRECTORY_TEMP + '/image_raw',
captainRootDirectoryGenerated: CAPTAIN_ROOT_DIRECTORY_GENERATED,