mirror of
https://github.com/caprover/caprover
synced 2026-09-27 17:15:38 +00:00
Create backup tar added
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user