diff --git a/.env.example b/.env.example index 2f96283..390c3a4 100644 --- a/.env.example +++ b/.env.example @@ -27,6 +27,8 @@ TZ=Europe/Paris ### Backup Cron Expression #BACKUP_CRON_EXPRESSION=@midnight +##Delete old backup created more than specified days ago +#BACKUP_RETENTION_DAYS=7 ####SSH Storage #SSH_HOST_NAME= diff --git a/docs/how-tos/backup-to-s3.md b/docs/how-tos/backup-to-s3.md index 7d72d18..a9ae1ae 100644 --- a/docs/how-tos/backup-to-s3.md +++ b/docs/how-tos/backup-to-s3.md @@ -60,7 +60,7 @@ services: # for a list of available releases. image: jkaninda/pg-bkup container_name: pg-bkup - command: backup --storage s3 -d my-database --mode scheduled --period "0 1 * * *" + command: backup --storage s3 -d my-database" environment: - DB_PORT=5432 - DB_HOST=postgres @@ -74,6 +74,8 @@ services: - AWS_ACCESS_KEY=xxxx - AWS_SECRET_KEY=xxxxx # - BACKUP_CRON_EXPRESSION=0 1 * * * # Optional + #Delete old backup created more than specified days ago + #- BACKUP_RETENTION_DAYS=7 ## In case you are using S3 alternative such as Minio and your Minio instance is not secured, you change it to true - AWS_DISABLE_SSL="false" # pg-bkup container must be connected to the same network with your database diff --git a/docs/how-tos/backup-to-ssh.md b/docs/how-tos/backup-to-ssh.md index a373b21..2085fd0 100644 --- a/docs/how-tos/backup-to-ssh.md +++ b/docs/how-tos/backup-to-ssh.md @@ -78,6 +78,8 @@ services: - SSH_USER=user - REMOTE_PATH=/home/jkaninda/backups - SSH_IDENTIFY_FILE=/tmp/id_ed25519 + #Delete old backup created more than specified days ago + #- BACKUP_RETENTION_DAYS=7 ## We advise you to use a private jey instead of password #- SSH_PASSWORD=password # pg-bkup container must be connected to the same network with your database diff --git a/docs/how-tos/backup.md b/docs/how-tos/backup.md index fc52f33..2cea167 100644 --- a/docs/how-tos/backup.md +++ b/docs/how-tos/backup.md @@ -77,6 +77,8 @@ services: - DB_USERNAME=username - DB_PASSWORD=password - BACKUP_CRON_EXPRESSION=0 1 * * * + #Delete old backup created more than specified days ago + #- BACKUP_RETENTION_DAYS=7 # pg-bkup container must be connected to the same network with your database networks: - web diff --git a/docs/reference/index.md b/docs/reference/index.md index 5fbd341..1f99bb5 100644 --- a/docs/reference/index.md +++ b/docs/reference/index.md @@ -26,8 +26,6 @@ Backup, restore and migrate targets, schedule and retention are configured using | --dbname | -d | Database name | | --port | -p | Database port (default: 5432) | | --disable-compression | | Disable database backup compression | -| --prune | | Delete old backup, default disabled | -| --keep-last | | Delete old backup created more than specified days ago, default 7 days | | --cron-expression | | Backup cron expression, eg: (* * * * *) or @daily | | --help | -h | Print this help message and exit | | --version | -V | Print version information and exit | @@ -52,6 +50,7 @@ Backup, restore and migrate targets, schedule and retention are configured using | GPG_PASSPHRASE | Optional, required to encrypt and restore backup | GPG passphrase | | GPG_PUBLIC_KEY | Optional, required to encrypt backup | GPG public key, used to encrypt backup (/config/public_key.asc) | | BACKUP_CRON_EXPRESSION | Optional if it was provided from the `--cron-expression` flag | Backup cron expression for docker in scheduled mode | +| BACKUP_RETENTION_DAYS | Optional | Delete old backup created more than specified days ago | | SSH_HOST | Optional, required for SSH storage | ssh remote hostname or ip | | SSH_USER | Optional, required for SSH storage | ssh remote user | | SSH_PASSWORD | Optional, required for SSH storage | ssh remote user's password | diff --git a/pkg/backup.go b/pkg/backup.go index f25fa4c..b83f0b3 100644 --- a/pkg/backup.go +++ b/pkg/backup.go @@ -46,6 +46,7 @@ func StartBackup(cmd *cobra.Command) { func scheduledMode(db *dbConfig, config *BackupConfig) { utils.Info("Running in Scheduled mode") utils.Info("Backup cron expression: %s", config.cronExpression) + utils.Info("The next scheduled time is: %v", utils.CronNextTime(config.cronExpression).Format(timeFormat)) utils.Info("Storage type %s ", config.storage) //Test backup @@ -58,6 +59,8 @@ func scheduledMode(db *dbConfig, config *BackupConfig) { _, err := c.AddFunc(config.cronExpression, func() { BackupTask(db, config) + utils.Info("Next backup time is: %v", utils.CronNextTime(config.cronExpression).Format(timeFormat)) + }) if err != nil { return @@ -80,6 +83,8 @@ func multiBackupTask(databases []Database, bkConfig *BackupConfig) { BackupTask(getDatabase(db), bkConfig) } } + +// BackupTask backups database func BackupTask(db *dbConfig, config *BackupConfig) { utils.Info("Starting backup task...") //Generate file name @@ -121,6 +126,7 @@ func startMultiBackup(bkConfig *BackupConfig, configFile string) { if utils.IsValidCronExpression(bkConfig.cronExpression) { utils.Info("Running MultiBackup in Scheduled mode") utils.Info("Backup cron expression: %s", bkConfig.cronExpression) + utils.Info("The next scheduled time is: %v", utils.CronNextTime(bkConfig.cronExpression).Format(timeFormat)) utils.Info("Storage type %s ", bkConfig.storage) //Test backup @@ -132,8 +138,9 @@ func startMultiBackup(bkConfig *BackupConfig, configFile string) { c := cron.New() _, err := c.AddFunc(bkConfig.cronExpression, func() { - // Create a channel multiBackupTask(conf.Databases, bkConfig) + utils.Info("Next backup time is: %v", utils.CronNextTime(bkConfig.cronExpression).Format(timeFormat)) + }) if err != nil { return diff --git a/pkg/config.go b/pkg/config.go index c605889..3a58c65 100644 --- a/pkg/config.go +++ b/pkg/config.go @@ -172,7 +172,6 @@ func initAWSConfig() *AWSConfig { func initBackupConfig(cmd *cobra.Command) *BackupConfig { utils.SetEnv("STORAGE_PATH", storagePath) utils.GetEnv(cmd, "cron-expression", "BACKUP_CRON_EXPRESSION") - utils.GetEnv(cmd, "period", "BACKUP_CRON_EXPRESSION") utils.GetEnv(cmd, "path", "REMOTE_PATH") //Get flag value and set env remotePath := utils.GetEnvVariable("REMOTE_PATH", "SSH_REMOTE_PATH") diff --git a/pkg/var.go b/pkg/var.go index 5264ab5..895ac3f 100644 --- a/pkg/var.go +++ b/pkg/var.go @@ -10,6 +10,7 @@ const tmpPath = "/tmp/backup" const gpgHome = "/config/gnupg" const algorithm = "aes256" const gpgExtension = "gpg" +const timeFormat = "2006-01-02 at 15:04:05" var ( storage = "local" diff --git a/utils/notification.go b/utils/notification.go index 0b8f13a..864f621 100644 --- a/utils/notification.go +++ b/utils/notification.go @@ -6,7 +6,6 @@ import ( "encoding/json" "fmt" "github.com/go-mail/mail" - "github.com/robfig/cron/v3" "html/template" "io/ioutil" "net/http" @@ -177,7 +176,3 @@ 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 -} diff --git a/utils/utils.go b/utils/utils.go index f37189e..c93ee89 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -8,11 +8,13 @@ package utils import ( "fmt" + "github.com/robfig/cron/v3" "github.com/spf13/cobra" "io" "io/fs" "os" "strconv" + "time" ) // FileExists checks if the file does exist @@ -188,3 +190,26 @@ func EnvWithDefault(envName string, defaultValue string) string { } return value } + +// IsValidCronExpression verify cronExpression and returns boolean +func IsValidCronExpression(cronExpr string) bool { + // Parse the cron expression + _, err := cron.ParseStandard(cronExpr) + return err == nil +} + +// CronNextTime returns cronExpression next time +func CronNextTime(cronExpr string) time.Time { + // Parse the cron expression + schedule, err := cron.ParseStandard(cronExpr) + if err != nil { + Error("Error parsing cron expression:", err) + return time.Time{} + } + // Get the current time + now := time.Now() + // Get the next scheduled time + next := schedule.Next(now) + //Info("The next scheduled time is: %v\n", next) + return next +}