Files
pg-bkup/pkg/backup.go

310 lines
8.0 KiB
Go
Raw Normal View History

2024-09-10 22:59:28 +02:00
// Package pkg /
/*****
@author Jonas Kaninda
@license MIT License <https://opensource.org/licenses/MIT>
@Copyright © 2024 Jonas Kaninda
**/
package pkg
import (
"fmt"
"github.com/jkaninda/pg-bkup/utils"
"github.com/robfig/cron/v3"
"github.com/spf13/cobra"
"log"
"os"
"os/exec"
"path/filepath"
"time"
)
func StartBackup(cmd *cobra.Command) {
intro()
dbConf = initDbConfig(cmd)
//Initialize backup configs
config := initBackupConfig(cmd)
if config.cronExpression == "" {
BackupTask(dbConf, config)
} else {
if utils.IsValidCronExpression(config.cronExpression) {
scheduledMode(dbConf, config)
} else {
utils.Fatal("Cron expression is not valid: %s", config.cronExpression)
}
}
}
// Run in scheduled mode
func scheduledMode(db *dbConfig, config *BackupConfig) {
utils.Info("Running in Scheduled mode")
utils.Info("Backup cron expression: %s", config.cronExpression)
utils.Info("Storage type %s ", config.storage)
//Test backup
utils.Info("Testing backup configurations...")
BackupTask(db, config)
utils.Info("Testing backup configurations...done")
utils.Info("Creating backup job...")
// Create a new cron instance
c := cron.New()
_, err := c.AddFunc(config.cronExpression, func() {
BackupTask(db, config)
})
2024-07-28 16:59:26 +02:00
if err != nil {
return
2024-07-28 16:59:26 +02:00
}
// Start the cron scheduler
c.Start()
utils.Info("Creating backup job...done")
utils.Info("Backup job started")
defer c.Stop()
select {}
}
func BackupTask(db *dbConfig, config *BackupConfig) {
utils.Info("Starting backup task...")
//Generate file name
2024-09-30 01:59:28 +02:00
backupFileName := fmt.Sprintf("%s_%s.sql.gz", db.dbName, time.Now().Format("20060102_150405"))
if config.disableCompression {
2024-09-30 01:59:28 +02:00
backupFileName = fmt.Sprintf("%s_%s.sql", db.dbName, time.Now().Format("20060102_150405"))
2024-07-28 16:59:26 +02:00
}
config.backupFileName = backupFileName
switch config.storage {
case "local":
localBackup(db, config)
case "s3", "S3":
s3Backup(db, config)
case "ssh", "SSH", "remote":
sshBackup(db, config)
case "ftp", "FTP":
2024-09-29 23:53:19 +02:00
ftpBackup(db, config)
//utils.Fatal("Not supported storage type: %s", config.storage)
default:
localBackup(db, config)
}
}
func intro() {
2024-09-12 06:17:13 +02:00
utils.Info("Starting PostgreSQL Backup...")
utils.Info("Copyright (c) 2024 Jonas Kaninda ")
}
2024-01-19 06:49:29 +01:00
// BackupDatabase backup database
func BackupDatabase(db *dbConfig, backupFileName string, disableCompression bool) {
storagePath = os.Getenv("STORAGE_PATH")
2024-07-28 16:59:26 +02:00
utils.Info("Starting database backup...")
2024-08-04 01:20:30 +02:00
err := os.Setenv("PGPASSWORD", db.dbPassword)
2024-08-04 01:20:30 +02:00
if err != nil {
return
}
testDatabaseConnection(db)
2024-08-04 01:20:30 +02:00
// Backup Database database
utils.Info("Backing up database...")
// Verify is compression is disabled
if disableCompression {
// Execute pg_dump
cmd := exec.Command("pg_dump",
"-h", db.dbHost,
"-p", db.dbPort,
"-U", db.dbUserName,
"-d", db.dbName,
2024-08-04 01:20:30 +02:00
)
output, err := cmd.Output()
if err != nil {
2024-08-04 01:20:30 +02:00
log.Fatal(err)
}
2024-08-04 01:20:30 +02:00
// save output
file, err := os.Create(fmt.Sprintf("%s/%s", tmpPath, backupFileName))
if err != nil {
log.Fatal(err)
}
defer file.Close()
2024-08-04 01:20:30 +02:00
_, err = file.Write(output)
if err != nil {
log.Fatal(err)
}
2024-08-04 01:20:30 +02:00
} else {
// Execute pg_dump
cmd := exec.Command("pg_dump",
"-h", db.dbHost,
"-p", db.dbPort,
"-U", db.dbUserName,
"-d", db.dbName,
2024-08-04 01:20:30 +02:00
)
stdout, err := cmd.StdoutPipe()
if err != nil {
log.Fatal(err)
}
gzipCmd := exec.Command("gzip")
gzipCmd.Stdin = stdout
// save output
gzipCmd.Stdout, err = os.Create(fmt.Sprintf("%s/%s", tmpPath, backupFileName))
gzipCmd.Start()
if err != nil {
log.Fatal(err)
}
if err := cmd.Run(); err != nil {
log.Fatal(err)
}
if err := gzipCmd.Wait(); err != nil {
log.Fatal(err)
2024-02-20 07:55:55 +01:00
}
}
2024-08-04 01:20:30 +02:00
utils.Info("Database has been backed up")
}
func localBackup(db *dbConfig, config *BackupConfig) {
utils.Info("Backup database to local storage")
BackupDatabase(db, config.backupFileName, disableCompression)
finalFileName := config.backupFileName
if config.encryption {
encryptBackup(config)
finalFileName = fmt.Sprintf("%s.%s", config.backupFileName, gpgExtension)
2024-07-29 07:33:26 +02:00
}
2024-08-04 01:20:30 +02:00
utils.Info("Backup name is %s", finalFileName)
moveToBackup(finalFileName, storagePath)
2024-09-10 22:59:28 +02:00
//Send notification
utils.NotifySuccess(finalFileName)
//Delete old backup
if config.prune {
deleteOldBackup(config.backupRetention)
2024-07-29 07:33:26 +02:00
}
//Delete temp
deleteTemp()
2024-07-29 07:33:26 +02:00
}
func s3Backup(db *dbConfig, config *BackupConfig) {
bucket := utils.GetEnvVariable("AWS_S3_BUCKET_NAME", "BUCKET_NAME")
s3Path := utils.GetEnvVariable("AWS_S3_PATH", "S3_PATH")
2024-07-30 07:02:18 +02:00
utils.Info("Backup database to s3 storage")
//Backup database
BackupDatabase(db, config.backupFileName, disableCompression)
finalFileName := config.backupFileName
if config.encryption {
encryptBackup(config)
finalFileName = fmt.Sprintf("%s.%s", config.backupFileName, "gpg")
}
utils.Info("Uploading backup archive to remote storage S3 ... ")
2024-08-04 01:20:30 +02:00
utils.Info("Backup name is %s", finalFileName)
2024-09-29 23:53:19 +02:00
err := UploadFileToS3(tmpPath, finalFileName, bucket, s3Path)
2024-07-29 07:33:26 +02:00
if err != nil {
utils.Fatal("Error uploading backup archive to S3: %s ", err)
2024-07-29 07:33:26 +02:00
}
//Delete backup file from tmp folder
err = utils.DeleteFile(filepath.Join(tmpPath, config.backupFileName))
2024-07-29 07:33:26 +02:00
if err != nil {
2024-08-04 01:20:30 +02:00
fmt.Println("Error deleting file: ", err)
2024-07-29 07:33:26 +02:00
}
// Delete old backup
if config.prune {
2024-09-29 23:53:19 +02:00
err := DeleteOldBackup(bucket, s3Path, config.backupRetention)
if err != nil {
2024-08-04 01:20:30 +02:00
utils.Fatal("Error deleting old backup from S3: %s ", err)
}
}
utils.Done("Uploading backup archive to remote storage S3 ... done ")
2024-09-10 22:59:28 +02:00
//Send notification
utils.NotifySuccess(finalFileName)
//Delete temp
deleteTemp()
}
func sshBackup(db *dbConfig, config *BackupConfig) {
2024-07-30 19:18:34 +02:00
utils.Info("Backup database to Remote server")
//Backup database
BackupDatabase(db, config.backupFileName, disableCompression)
finalFileName := config.backupFileName
if config.encryption {
encryptBackup(config)
finalFileName = fmt.Sprintf("%s.%s", config.backupFileName, "gpg")
2024-07-30 19:18:34 +02:00
}
utils.Info("Uploading backup archive to remote storage ... ")
utils.Info("Backup name is %s", finalFileName)
err := CopyToRemote(finalFileName, config.remotePath)
2024-07-30 19:18:34 +02:00
if err != nil {
2024-08-04 01:20:30 +02:00
utils.Fatal("Error uploading file to the remote server: %s ", err)
2024-07-30 19:18:34 +02:00
}
2024-07-30 19:18:34 +02:00
//Delete backup file from tmp folder
err = utils.DeleteFile(filepath.Join(tmpPath, finalFileName))
if err != nil {
utils.Error("Error deleting file: %v", err)
2024-07-30 19:18:34 +02:00
}
if config.prune {
2024-07-30 19:18:34 +02:00
//TODO: Delete old backup from remote server
utils.Info("Deleting old backup from a remote server is not implemented yet")
}
utils.Done("Uploading backup archive to remote storage ... done ")
2024-09-10 22:59:28 +02:00
//Send notification
utils.NotifySuccess(finalFileName)
//Delete temp
deleteTemp()
}
2024-09-29 23:53:19 +02:00
func ftpBackup(db *dbConfig, config *BackupConfig) {
utils.Info("Backup database to the remote FTP server")
//Backup database
BackupDatabase(db, config.backupFileName, disableCompression)
finalFileName := config.backupFileName
if config.encryption {
encryptBackup(config)
2024-09-29 23:53:19 +02:00
finalFileName = fmt.Sprintf("%s.%s", config.backupFileName, "gpg")
}
utils.Info("Uploading backup archive to the remote FTP server ... ")
utils.Info("Backup name is %s", finalFileName)
err := CopyToFTP(finalFileName, config.remotePath)
if err != nil {
utils.Fatal("Error uploading file to the remote FTP server: %s ", err)
}
//Delete backup file from tmp folder
err = utils.DeleteFile(filepath.Join(tmpPath, finalFileName))
if err != nil {
utils.Error("Error deleting file: %v", err)
}
if config.prune {
//TODO: Delete old backup from remote server
utils.Info("Deleting old backup from a remote server is not implemented yet")
}
utils.Done("Uploading backup archive to the remote FTP server ... done ")
//Send notification
utils.NotifySuccess(finalFileName)
//Delete temp
deleteTemp()
}
func encryptBackup(config *BackupConfig) {
if config.usingKey {
err := encrypt(filepath.Join(tmpPath, config.backupFileName), config.publicKey)
if err != nil {
utils.Fatal("Error during encrypting backup %v", err)
}
} else if config.passphrase != "" {
err := Encrypt(filepath.Join(tmpPath, config.backupFileName), config.passphrase)
if err != nil {
utils.Fatal("Error during encrypting backup %v", err)
}
}
}