feat: add backup all databases

This commit is contained in:
2025-03-14 05:20:54 +01:00
parent cd827a9277
commit 6300a8f2dd
13 changed files with 177 additions and 107 deletions

View File

@@ -39,7 +39,7 @@ func azureBackup(db *dbConfig, config *BackupConfig) {
utils.Info("Backup database to Azure Blob Storage")
// Backup database
err := BackupDatabase(db, config.backupFileName, disableCompression, config.all, config.singleFile)
err := BackupDatabase(db, config.backupFileName, disableCompression, config.all, config.allInOne)
if err != nil {
recoverMode(err, "Error backing up database")
return

View File

@@ -112,7 +112,7 @@ func multiBackupTask(databases []Database, bkConfig *BackupConfig) {
// createBackupTask backup task
func createBackupTask(db *dbConfig, config *BackupConfig) {
if config.all && !config.singleFile {
if config.all && !config.allInOne {
backupAll(db, config)
} else {
backupTask(db, config)
@@ -141,7 +141,7 @@ func backupTask(db *dbConfig, config *BackupConfig) {
utils.Info("Starting backup task...")
startTime = time.Now()
prefix := db.dbName
if config.all && config.singleFile {
if config.all && config.allInOne {
prefix = "all_databases"
}
// Generate file name
@@ -302,7 +302,7 @@ func runCommandWithCompression(command string, args []string, outputPath string)
// localBackup backup database to local storage
func localBackup(db *dbConfig, config *BackupConfig) {
utils.Info("Backup database to local storage")
err := BackupDatabase(db, config.backupFileName, disableCompression, config.all, config.singleFile)
err := BackupDatabase(db, config.backupFileName, disableCompression, config.all, config.allInOne)
if err != nil {
recoverMode(err, "Error backing up database")
return

View File

@@ -78,7 +78,7 @@ type BackupConfig struct {
storage string
cronExpression string
all bool
singleFile bool
allInOne bool
}
type FTPConfig struct {
host string
@@ -253,13 +253,17 @@ func initBackupConfig(cmd *cobra.Command) *BackupConfig {
remotePath := utils.GetEnvVariable("REMOTE_PATH", "SSH_REMOTE_PATH")
storage = utils.GetEnv(cmd, "storage", "STORAGE")
prune := false
configFile := os.Getenv("BACKUP_CONFIG_FILE")
backupRetention := utils.GetIntEnv("BACKUP_RETENTION_DAYS")
if backupRetention > 0 {
prune = true
}
disableCompression, _ = cmd.Flags().GetBool("disable-compression")
all, _ := cmd.Flags().GetBool("all")
singleFile, _ := cmd.Flags().GetBool("single-file")
all, _ := cmd.Flags().GetBool("all-databases")
allInOne, _ := cmd.Flags().GetBool("all-in-one")
if allInOne {
all = true
}
_, _ = cmd.Flags().GetString("mode")
passphrase := os.Getenv("GPG_PASSPHRASE")
_ = utils.GetEnv(cmd, "path", "AWS_S3_PATH")
@@ -273,6 +277,10 @@ func initBackupConfig(cmd *cobra.Command) *BackupConfig {
encryption = true
usingKey = false
}
dbName := os.Getenv("DB_NAME")
if dbName == "" && !all && configFile == "" {
utils.Fatal("Database name is required, use DB_NAME environment variable or -d flag")
}
// Initialize backup configs
config := BackupConfig{}
config.backupRetention = backupRetention
@@ -286,7 +294,7 @@ func initBackupConfig(cmd *cobra.Command) *BackupConfig {
config.usingKey = usingKey
config.cronExpression = cronExpression
config.all = all
config.singleFile = singleFile
config.allInOne = allInOne
return &config
}

View File

@@ -37,7 +37,7 @@ import (
)
func intro() {
fmt.Println("Starting MySQL Backup...")
fmt.Println("Starting MYSQL-BKUP...")
fmt.Printf("Version: %s\n", utils.Version)
fmt.Println("Copyright (c) 2024 Jonas Kaninda")
}

View File

@@ -39,7 +39,7 @@ import (
func sshBackup(db *dbConfig, config *BackupConfig) {
utils.Info("Backup database to Remote server")
// Backup database
err := BackupDatabase(db, config.backupFileName, disableCompression, config.all, config.singleFile)
err := BackupDatabase(db, config.backupFileName, disableCompression, config.all, config.allInOne)
if err != nil {
recoverMode(err, "Error backing up database")
return
@@ -160,7 +160,7 @@ func ftpBackup(db *dbConfig, config *BackupConfig) {
utils.Info("Backup database to the remote FTP server")
// Backup database
err := BackupDatabase(db, config.backupFileName, disableCompression, config.all, config.singleFile)
err := BackupDatabase(db, config.backupFileName, disableCompression, config.all, config.allInOne)
if err != nil {
recoverMode(err, "Error backing up database")
return

View File

@@ -57,11 +57,17 @@ func StartRestore(cmd *cobra.Command) {
}
func localRestore(dbConf *dbConfig, restoreConf *RestoreConfig) {
utils.Info("Restore database from local")
basePath := filepath.Dir(restoreConf.file)
fileName := filepath.Base(restoreConf.file)
restoreConf.file = fileName
if basePath == "" || basePath == "." {
basePath = storagePath
}
localStorage := local.NewStorage(local.Config{
RemotePath: storagePath,
RemotePath: basePath,
LocalPath: tmpPath,
})
err := localStorage.CopyFrom(restoreConf.file)
err := localStorage.CopyFrom(fileName)
if err != nil {
utils.Fatal("Error copying backup file: %s", err)
}
@@ -69,87 +75,79 @@ func localRestore(dbConf *dbConfig, restoreConf *RestoreConfig) {
}
// RestoreDatabase restore database
// RestoreDatabase restores the database from a backup file
func RestoreDatabase(db *dbConfig, conf *RestoreConfig) {
if conf.file == "" {
utils.Fatal("Error, file required")
}
extension := filepath.Ext(filepath.Join(tmpPath, conf.file))
rFile, err := os.ReadFile(filepath.Join(tmpPath, conf.file))
outputFile := RemoveLastExtension(filepath.Join(tmpPath, conf.file))
filePath := filepath.Join(tmpPath, conf.file)
rFile, err := os.ReadFile(filePath)
if err != nil {
utils.Fatal("Error reading backup file: %s ", err)
utils.Fatal("Error reading backup file: %v", err)
}
extension := filepath.Ext(filePath)
outputFile := RemoveLastExtension(filePath)
if extension == ".gpg" {
if conf.usingKey {
utils.Info("Decrypting backup using private key...")
utils.Warn("Backup decryption using a private key is not fully supported")
prKey, err := os.ReadFile(conf.privateKey)
if err != nil {
utils.Fatal("Error reading public key: %s ", err)
}
err = encryptor.DecryptWithPrivateKey(rFile, outputFile, prKey, conf.passphrase)
if err != nil {
utils.Fatal("error during decrypting backup %v", err)
}
utils.Info("Decrypting backup using private key...done")
} else {
if conf.passphrase == "" {
utils.Error("Error, passphrase or private key required")
utils.Fatal("Your file seems to be a GPG file.\nYou need to provide GPG keys. GPG_PASSPHRASE or GPG_PRIVATE_KEY environment variable is required.")
} else {
utils.Info("Decrypting backup using passphrase...")
// decryptWithGPG file
err := encryptor.Decrypt(rFile, outputFile, conf.passphrase)
if err != nil {
utils.Fatal("Error decrypting file %s %v", file, err)
}
utils.Info("Decrypting backup using passphrase...done")
// Update file name
conf.file = RemoveLastExtension(file)
}
}
decryptBackup(conf, rFile, outputFile)
}
if utils.FileExists(filepath.Join(tmpPath, conf.file)) {
err = testDatabaseConnection(db)
restorationFile := filepath.Join(tmpPath, conf.file)
if !utils.FileExists(restorationFile) {
utils.Fatal("File not found: %s", restorationFile)
}
if err := testDatabaseConnection(db); err != nil {
utils.Fatal("Error connecting to the database: %v", err)
}
utils.Info("Restoring database...")
restoreDatabaseFile(db, restorationFile)
}
func decryptBackup(conf *RestoreConfig, rFile []byte, outputFile string) {
if conf.usingKey {
utils.Info("Decrypting backup using private key...")
prKey, err := os.ReadFile(conf.privateKey)
if err != nil {
utils.Fatal("Error connecting to the database %v", err)
utils.Fatal("Error reading private key: %v", err)
}
utils.Info("Restoring database...")
extension := filepath.Ext(filepath.Join(tmpPath, conf.file))
// Restore from a compressed file / .sql.gz
if extension == ".gz" {
str := "zcat " + filepath.Join(tmpPath, conf.file) + " | mariadb " + fmt.Sprintf("--defaults-file=%s", mysqlClientConfig) + " " + db.dbName
output, err := exec.Command("sh", "-c", str).Output()
if err != nil {
utils.Fatal("Error, in restoring the database %v output: %v", err, output)
}
utils.Info("Restoring database... done")
utils.Info("Database has been restored")
// Delete temp
deleteTemp()
} else if extension == ".sql" {
// Restore from SQL file
str := "cat " + filepath.Join(tmpPath, conf.file) + " | mariadb " + fmt.Sprintf("--defaults-file=%s", mysqlClientConfig) + " " + db.dbName
output, err := exec.Command("sh", "-c", str).Output()
if err != nil {
utils.Fatal("Error, in restoring the database %v output: %v", err, output)
}
utils.Info("Restoring database... done")
utils.Info("Database has been restored")
// Delete temp
deleteTemp()
} else {
utils.Fatal("Unknown file extension %s", extension)
if err := encryptor.DecryptWithPrivateKey(rFile, outputFile, prKey, conf.passphrase); err != nil {
utils.Fatal("Error decrypting backup: %v", err)
}
} else {
utils.Fatal("File not found in %s", filepath.Join(tmpPath, conf.file))
if conf.passphrase == "" {
utils.Fatal("Passphrase or private key required for GPG file.")
}
utils.Info("Decrypting backup using passphrase...")
if err := encryptor.Decrypt(rFile, outputFile, conf.passphrase); err != nil {
utils.Fatal("Error decrypting file: %v", err)
}
conf.file = RemoveLastExtension(conf.file)
}
}
func restoreDatabaseFile(db *dbConfig, restorationFile string) {
extension := filepath.Ext(restorationFile)
var cmdStr string
switch extension {
case ".gz":
cmdStr = fmt.Sprintf("zcat %s | mariadb --defaults-file=%s %s", restorationFile, mysqlClientConfig, db.dbName)
case ".sql":
cmdStr = fmt.Sprintf("cat %s | mariadb --defaults-file=%s %s", restorationFile, mysqlClientConfig, db.dbName)
default:
utils.Fatal("Unknown file extension: %s", extension)
}
cmd := exec.Command("sh", "-c", cmdStr)
output, err := cmd.CombinedOutput()
if err != nil {
utils.Fatal("Error restoring database: %v\nOutput: %s", err, string(output))
}
utils.Info("Database has been restored successfully.")
deleteTemp()
}

View File

@@ -39,7 +39,7 @@ func s3Backup(db *dbConfig, config *BackupConfig) {
utils.Info("Backup database to s3 storage")
// Backup database
err := BackupDatabase(db, config.backupFileName, disableCompression, config.all, config.singleFile)
err := BackupDatabase(db, config.backupFileName, disableCompression, config.all, config.allInOne)
if err != nil {
recoverMode(err, "Error backing up database")
return

View File

@@ -54,7 +54,6 @@ var dbHVars = []string{
"DB_HOST",
"DB_PASSWORD",
"DB_USERNAME",
"DB_NAME",
}
var tdbRVars = []string{
"TARGET_DB_HOST",