From 29a58aa26d31fc28ea8608767edbc6f14bb1419c Mon Sep 17 00:00:00 2001 From: Jonas Kaninda Date: Sat, 28 Sep 2024 04:45:03 +0200 Subject: [PATCH] chore: add cron expression verification --- cmd/backup.go | 2 +- go.mod | 1 + go.sum | 2 ++ pkg/backup.go | 19 ++++++++++++++----- utils/utils.go | 9 +++++++-- 5 files changed, 25 insertions(+), 8 deletions(-) diff --git a/cmd/backup.go b/cmd/backup.go index ad19ba0..0eeff19 100644 --- a/cmd/backup.go +++ b/cmd/backup.go @@ -30,7 +30,7 @@ func init() { BackupCmd.PersistentFlags().StringP("storage", "s", "local", "Storage. local or s3") BackupCmd.PersistentFlags().StringP("path", "P", "", "AWS S3 path without file name. eg: /custom_path or ssh remote path `/home/foo/backup`") BackupCmd.PersistentFlags().StringP("mode", "m", "default", "Execution mode. default or scheduled") - BackupCmd.PersistentFlags().StringP("period", "", "0 1 * * *", "Schedule period time") + BackupCmd.PersistentFlags().StringP("period", "", "", "Schedule period time") BackupCmd.PersistentFlags().BoolP("prune", "", false, "Delete old backup, default disabled") BackupCmd.PersistentFlags().IntP("keep-last", "", 7, "Delete files created more than specified days ago, default 7 days") BackupCmd.PersistentFlags().BoolP("disable-compression", "", false, "Disable backup compression") diff --git a/go.mod b/go.mod index 2ef1395..2e1e200 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,7 @@ require ( require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/robfig/cron/v3 v3.0.1 // indirect golang.org/x/sys v0.22.0 // indirect gopkg.in/fsnotify.v1 v1.4.7 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect diff --git a/go.sum b/go.sum index d9de32f..375550e 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,8 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= diff --git a/pkg/backup.go b/pkg/backup.go index e6d34df..96fe23f 100644 --- a/pkg/backup.go +++ b/pkg/backup.go @@ -34,6 +34,7 @@ func StartBackup(cmd *cobra.Command) { executionMode, _ = cmd.Flags().GetString("mode") gpqPassphrase := os.Getenv("GPG_PASSPHRASE") _ = utils.GetEnv(cmd, "path", "AWS_S3_PATH") + cronExpression := os.Getenv("BACKUP_CRON_EXPRESSION") dbConf = getDbConfig(cmd) @@ -48,7 +49,7 @@ func StartBackup(cmd *cobra.Command) { backupFileName = fmt.Sprintf("%s_%s.sql", dbConf.dbName, time.Now().Format("20060102_150405")) } - if executionMode == "default" { + if cronExpression == "" { switch storage { case "s3": s3Backup(dbConf, backupFileName, disableCompression, prune, backupRetention, encryption) @@ -62,10 +63,12 @@ func StartBackup(cmd *cobra.Command) { localBackup(dbConf, backupFileName, disableCompression, prune, backupRetention, encryption) } - } else if executionMode == "scheduled" { - scheduledMode(dbConf, storage) } else { - utils.Fatal("Error, unknown execution mode!") + if utils.IsValidCronExpression(cronExpression) { + scheduledMode(dbConf, storage) + } else { + utils.Fatal("Cron expression is not valid: %s", cronExpression) + } } } @@ -87,11 +90,17 @@ func scheduledMode(db *dbConfig, storage string) { utils.Info("Creating backup job...") CreateCrontabScript(disableCompression, storage) + //Set BACKUP_CRON_EXPRESSION to nil + err := os.Setenv("BACKUP_CRON_EXPRESSION", "") + if err != nil { + return + } + supervisorConfig := "/etc/supervisor/supervisord.conf" // Start Supervisor cmd := exec.Command("supervisord", "-c", supervisorConfig) - err := cmd.Start() + err = cmd.Start() if err != nil { utils.Fatal(fmt.Sprintf("Failed to start supervisord: %v", err)) } diff --git a/utils/utils.go b/utils/utils.go index 548bdc7..caf07ed 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -10,6 +10,7 @@ import ( "bytes" "encoding/json" "fmt" + "github.com/robfig/cron/v3" "github.com/spf13/cobra" "io" "io/fs" @@ -224,7 +225,7 @@ func NotifySuccess(fileName string) { //Telegram notification err := CheckEnvVars(vars) if err == nil { - message := "MySQL Backup \n" + + message := "[✅ MySQL Backup ]\n" + "Database has been backed up \n" + "Backup name is " + fileName sendMessage(message) @@ -239,7 +240,7 @@ func NotifyError(error string) { //Telegram notification err := CheckEnvVars(vars) if err == nil { - message := "MySQL Backup \n" + + message := "[🔴MySQL Backup ]\n" + "An error occurred during database backup \n" + "Error: " + error sendMessage(message) @@ -250,3 +251,7 @@ func getTgUrl() string { return fmt.Sprintf("https://api.telegram.org/bot%s", os.Getenv("TG_TOKEN")) } +func IsValidCronExpression(cronExpr string) bool { + _, err := cron.ParseStandard(cronExpr) + return err == nil +}