From 63101ae84f38c0432439fdf12f7d769139d1ce23 Mon Sep 17 00:00:00 2001 From: Jonas Kaninda Date: Tue, 19 Nov 2024 02:54:31 +0100 Subject: [PATCH] refactor: refactoring of code to meet all golangci-lint requirements --- .github/workflows/lint.yml | 23 ++++ .golangci.yml | 44 ++++++++ cmd/backup.go | 10 +- cmd/migrate.go | 7 +- cmd/restore.go | 8 +- cmd/root.go | 4 +- cmd/version.go | 3 +- internal/backup.go | 223 +++++++++++++++++++------------------ internal/config.go | 34 +++--- internal/helper.go | 20 ++-- internal/migrate.go | 23 ++-- internal/restore.go | 80 ++++++------- internal/var.go | 13 --- main.go | 5 +- pkg/logger/logger.go | 73 ++++++++++++ pkg/logger/var.go | 3 + utils/constant.go | 5 - utils/logger.go | 62 ----------- utils/notification.go | 43 +++---- utils/utils.go | 43 +++++-- 20 files changed, 413 insertions(+), 313 deletions(-) create mode 100644 .github/workflows/lint.yml create mode 100644 .golangci.yml create mode 100644 pkg/logger/logger.go create mode 100644 pkg/logger/var.go delete mode 100644 utils/logger.go diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..f40d365 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,23 @@ +name: Lint + +on: + push: + pull_request: + +jobs: + lint: + name: Run on Ubuntu + runs-on: ubuntu-latest + steps: + - name: Clone the code + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: '~1.22' + + - name: Run linter + uses: golangci/golangci-lint-action@v6 + with: + version: v1.61 diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..ae8a9cf --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,44 @@ +run: + timeout: 5m + allow-parallel-runners: true + +issues: + # don't skip warning about doc comments + # don't exclude the default set of lint + exclude-use-default: false + # restore some of the defaults + # (fill in the rest as needed) + exclude-rules: + - path: "internal/*" + linters: + - dupl + - lll + - goimports +linters: + disable-all: true + enable: + - dupl + - errcheck + - copyloopvar + - ginkgolinter + - goconst + - gocyclo + - gofmt + - gosimple + - govet + - ineffassign + # - lll + - misspell + - nakedret + - prealloc + - revive + - staticcheck + - typecheck + - unconvert + - unparam + - unused + +linters-settings: + revive: + rules: + - name: comment-spacings diff --git a/cmd/backup.go b/cmd/backup.go index a9b3fc7..99a7fc5 100644 --- a/cmd/backup.go +++ b/cmd/backup.go @@ -1,13 +1,9 @@ // Package cmd / -/***** -@author Jonas Kaninda -@license MIT License -@Copyright © 2024 Jonas Kaninda -**/ package cmd import ( "github.com/jkaninda/pg-bkup/internal" + "github.com/jkaninda/pg-bkup/pkg/logger" "github.com/jkaninda/pg-bkup/utils" "github.com/spf13/cobra" ) @@ -20,14 +16,14 @@ var BackupCmd = &cobra.Command{ if len(args) == 0 { internal.StartBackup(cmd) } else { - utils.Fatal(`"backup" accepts no argument %q`, args) + logger.Fatal(`"backup" accepts no argument %q`, args) } }, } func init() { - //Backup + // Backup BackupCmd.PersistentFlags().StringP("storage", "s", "local", "Define storage: local, s3, ssh, ftp") BackupCmd.PersistentFlags().StringP("path", "P", "", "AWS S3 path without file name. eg: /custom_path or ssh remote path `/home/foo/backup`") BackupCmd.PersistentFlags().StringP("cron-expression", "", "", "Backup cron expression") diff --git a/cmd/migrate.go b/cmd/migrate.go index 4cf0d34..6f45159 100644 --- a/cmd/migrate.go +++ b/cmd/migrate.go @@ -1,5 +1,6 @@ // Package cmd / -/***** +/* +**** @author Jonas Kaninda @license MIT License @Copyright © 2024 Jonas Kaninda @@ -8,7 +9,7 @@ package cmd import ( "github.com/jkaninda/pg-bkup/internal" - "github.com/jkaninda/pg-bkup/utils" + "github.com/jkaninda/pg-bkup/pkg/logger" "github.com/spf13/cobra" ) @@ -19,7 +20,7 @@ var MigrateCmd = &cobra.Command{ if len(args) == 0 { internal.StartMigration(cmd) } else { - utils.Fatal(`"migrate" accepts no argument %q`, args) + logger.Fatal(`"migrate" accepts no argument %q`, args) } diff --git a/cmd/restore.go b/cmd/restore.go index dc47b79..b944a4b 100644 --- a/cmd/restore.go +++ b/cmd/restore.go @@ -1,5 +1,6 @@ // Package cmd / -/***** +/* +**** @author Jonas Kaninda @license MIT License @Copyright © 2024 Jonas Kaninda @@ -8,6 +9,7 @@ package cmd import ( "github.com/jkaninda/pg-bkup/internal" + "github.com/jkaninda/pg-bkup/pkg/logger" "github.com/jkaninda/pg-bkup/utils" "github.com/spf13/cobra" ) @@ -20,7 +22,7 @@ var RestoreCmd = &cobra.Command{ if len(args) == 0 { internal.StartRestore(cmd) } else { - utils.Fatal(`"restore" accepts no argument %q`, args) + logger.Fatal(`"restore" accepts no argument %q`, args) } @@ -28,7 +30,7 @@ var RestoreCmd = &cobra.Command{ } func init() { - //Restore + // Restore RestoreCmd.PersistentFlags().StringP("file", "f", "", "File name of database") RestoreCmd.PersistentFlags().StringP("storage", "s", "local", "Define storage: local, s3, ssh, ftp") RestoreCmd.PersistentFlags().StringP("path", "P", "", "AWS S3 path without file name. eg: /custom_path or ssh remote path `/home/foo/backup`") diff --git a/cmd/root.go b/cmd/root.go index cb93a0d..7f31ffa 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,5 +1,7 @@ // Package cmd / -/***** +/* +* +*** @author Jonas Kaninda @license MIT License @Copyright © 2024 Jonas Kaninda diff --git a/cmd/version.go b/cmd/version.go index ff9c06d..8b3932a 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -1,5 +1,6 @@ // Package cmd / -/***** +/* +**** @author Jonas Kaninda @license MIT License @Copyright © 2024 Jonas Kaninda diff --git a/internal/backup.go b/internal/backup.go index 93ef84d..c389e96 100644 --- a/internal/backup.go +++ b/internal/backup.go @@ -1,9 +1,3 @@ -// Package internal / -/***** -@author Jonas Kaninda -@license MIT License -@Copyright © 2024 Jonas Kaninda -**/ package internal import ( @@ -13,6 +7,7 @@ import ( "github.com/jkaninda/go-storage/pkg/local" "github.com/jkaninda/go-storage/pkg/s3" "github.com/jkaninda/go-storage/pkg/ssh" + "github.com/jkaninda/pg-bkup/pkg/logger" "github.com/jkaninda/pg-bkup/utils" "github.com/robfig/cron/v3" "github.com/spf13/cobra" @@ -26,9 +21,9 @@ import ( func StartBackup(cmd *cobra.Command) { intro() - //Initialize backup configs + // Initialize backup configs config := initBackupConfig(cmd) - //Load backup configuration file + // Load backup configuration file configFile, err := loadConfigFile() if err != nil { dbConf = initDbConfig(cmd) @@ -38,7 +33,7 @@ func StartBackup(cmd *cobra.Command) { if utils.IsValidCronExpression(config.cronExpression) { scheduledMode(dbConf, config) } else { - utils.Fatal("Cron expression is not valid: %s", config.cronExpression) + logger.Fatal("Cron expression is not valid: %s", config.cronExpression) } } } else { @@ -49,22 +44,22 @@ func StartBackup(cmd *cobra.Command) { // scheduledMode Runs backup 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("The next scheduled time is: %v", utils.CronNextTime(config.cronExpression).Format(timeFormat)) - utils.Info("Storage type %s ", config.storage) + logger.Info("Running in Scheduled mode") + logger.Info("Backup cron expression: %s", config.cronExpression) + logger.Info("The next scheduled time is: %v", utils.CronNextTime(config.cronExpression).Format(timeFormat)) + logger.Info("Storage type %s ", config.storage) - //Test backup - utils.Info("Testing backup configurations...") + // Test backup + logger.Info("Testing backup configurations...") BackupTask(db, config) - utils.Info("Testing backup configurations...done") - utils.Info("Creating backup job...") + logger.Info("Testing backup configurations...done") + logger.Info("Creating backup job...") // Create a new cron instance c := cron.New() _, err := c.AddFunc(config.cronExpression, func() { BackupTask(db, config) - utils.Info("Next backup time is: %v", utils.CronNextTime(config.cronExpression).Format(timeFormat)) + logger.Info("Next backup time is: %v", utils.CronNextTime(config.cronExpression).Format(timeFormat)) }) if err != nil { @@ -72,8 +67,8 @@ func scheduledMode(db *dbConfig, config *BackupConfig) { } // Start the cron scheduler c.Start() - utils.Info("Creating backup job...done") - utils.Info("Backup job started") + logger.Info("Creating backup job...done") + logger.Info("Backup job started") defer c.Stop() select {} } @@ -81,7 +76,7 @@ func scheduledMode(db *dbConfig, config *BackupConfig) { // multiBackupTask backup multi database func multiBackupTask(databases []Database, bkConfig *BackupConfig) { for _, db := range databases { - //Check if path is defined in config file + // Check if path is defined in config file if db.Path != "" { bkConfig.remotePath = db.Path } @@ -91,8 +86,8 @@ func multiBackupTask(databases []Database, bkConfig *BackupConfig) { // BackupTask backups database func BackupTask(db *dbConfig, config *BackupConfig) { - utils.Info("Starting backup task...") - //Generate file name + logger.Info("Starting backup task...") + // Generate file name backupFileName := fmt.Sprintf("%s_%s.sql.gz", db.dbName, time.Now().Format("20060102_150405")) if config.disableCompression { backupFileName = fmt.Sprintf("%s_%s.sql", db.dbName, time.Now().Format("20060102_150405")) @@ -107,18 +102,17 @@ func BackupTask(db *dbConfig, config *BackupConfig) { sshBackup(db, config) case "ftp", "FTP": ftpBackup(db, config) - //utils.Fatal("Not supported storage type: %s", config.storage) default: localBackup(db, config) } } func startMultiBackup(bkConfig *BackupConfig, configFile string) { - utils.Info("Starting backup task...") + logger.Info("Starting backup task...") conf, err := readConf(configFile) if err != nil { - utils.Fatal("Error reading config file: %s", err) + logger.Fatal("Error reading config file: %s", err) } - //Check if cronExpression is defined in config file + // Check if cronExpression is defined in config file if conf.CronExpression != "" { bkConfig.cronExpression = conf.CronExpression } @@ -128,22 +122,22 @@ func startMultiBackup(bkConfig *BackupConfig, configFile string) { } else { // Check if cronExpression is valid if utils.IsValidCronExpression(bkConfig.cronExpression) { - utils.Info("Running backup 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) + logger.Info("Running backup in Scheduled mode") + logger.Info("Backup cron expression: %s", bkConfig.cronExpression) + logger.Info("The next scheduled time is: %v", utils.CronNextTime(bkConfig.cronExpression).Format(timeFormat)) + logger.Info("Storage type %s ", bkConfig.storage) - //Test backup - utils.Info("Testing backup configurations...") + // Test backup + logger.Info("Testing backup configurations...") multiBackupTask(conf.Databases, bkConfig) - utils.Info("Testing backup configurations...done") - utils.Info("Creating backup job...") + logger.Info("Testing backup configurations...done") + logger.Info("Creating backup job...") // Create a new cron instance c := cron.New() _, err := c.AddFunc(bkConfig.cronExpression, func() { multiBackupTask(conf.Databases, bkConfig) - utils.Info("Next backup time is: %v", utils.CronNextTime(bkConfig.cronExpression).Format(timeFormat)) + logger.Info("Next backup time is: %v", utils.CronNextTime(bkConfig.cronExpression).Format(timeFormat)) }) if err != nil { @@ -151,13 +145,13 @@ func startMultiBackup(bkConfig *BackupConfig, configFile string) { } // Start the cron scheduler c.Start() - utils.Info("Creating backup job...done") - utils.Info("Backup job started") + logger.Info("Creating backup job...done") + logger.Info("Backup job started") defer c.Stop() select {} } else { - utils.Fatal("Cron expression is not valid: %s", bkConfig.cronExpression) + logger.Fatal("Cron expression is not valid: %s", bkConfig.cronExpression) } } @@ -168,7 +162,7 @@ func BackupDatabase(db *dbConfig, backupFileName string, disableCompression bool storagePath = os.Getenv("STORAGE_PATH") - utils.Info("Starting database backup...") + logger.Info("Starting database backup...") err := os.Setenv("PGPASSWORD", db.dbPassword) if err != nil { @@ -176,7 +170,7 @@ func BackupDatabase(db *dbConfig, backupFileName string, disableCompression bool } testDatabaseConnection(db) // Backup Database database - utils.Info("Backing up database...") + logger.Info("Backing up database...") // Verify is compression is disabled if disableCompression { @@ -196,7 +190,13 @@ func BackupDatabase(db *dbConfig, backupFileName string, disableCompression bool if err != nil { log.Fatal(err) } - defer file.Close() + defer func(file *os.File) { + err := file.Close() + if err != nil { + return + + } + }(file) _, err = file.Write(output) if err != nil { @@ -219,7 +219,10 @@ func BackupDatabase(db *dbConfig, backupFileName string, disableCompression bool gzipCmd.Stdin = stdout // save output gzipCmd.Stdout, err = os.Create(filepath.Join(tmpPath, backupFileName)) - gzipCmd.Start() + err2 := gzipCmd.Start() + if err2 != nil { + return + } if err != nil { log.Fatal(err) } @@ -231,11 +234,11 @@ func BackupDatabase(db *dbConfig, backupFileName string, disableCompression bool } } - utils.Info("Database has been backed up") + logger.Info("Database has been backed up") } func localBackup(db *dbConfig, config *BackupConfig) { - utils.Info("Backup database to local storage") + logger.Info("Backup database to local storage") startTime = time.Now().Format(utils.TimeFormat()) BackupDatabase(db, config.backupFileName, disableCompression) finalFileName := config.backupFileName @@ -245,20 +248,20 @@ func localBackup(db *dbConfig, config *BackupConfig) { } fileInfo, err := os.Stat(filepath.Join(tmpPath, finalFileName)) if err != nil { - utils.Error("Error: %s", err) + logger.Error("Error: %s", err) } backupSize = fileInfo.Size() - utils.Info("Backup name is %s", finalFileName) + logger.Info("Backup name is %s", finalFileName) localStorage := local.NewStorage(local.Config{ LocalPath: tmpPath, RemotePath: storagePath, }) err = localStorage.Copy(finalFileName) if err != nil { - utils.Fatal("Error copying backup file: %s", err) + logger.Fatal("Error copying backup file: %s", err) } - utils.Info("Backup saved in %s", filepath.Join(storagePath, finalFileName)) - //Send notification + logger.Info("Backup saved in %s", filepath.Join(storagePath, finalFileName)) + // Send notification utils.NotifySuccess(&utils.NotificationData{ File: finalFileName, BackupSize: backupSize, @@ -268,36 +271,36 @@ func localBackup(db *dbConfig, config *BackupConfig) { StartTime: startTime, EndTime: time.Now().Format(utils.TimeFormat()), }) - //Delete old backup + // Delete old backup if config.prune { err = localStorage.Prune(config.backupRetention) if err != nil { - utils.Fatal("Error deleting old backup from %s storage: %s ", config.storage, err) + logger.Fatal("Error deleting old backup from %s storage: %s ", config.storage, err) } } - //Delete temp + // Delete temp deleteTemp() - utils.Info("Backup completed successfully") + logger.Info("Backup completed successfully") } func s3Backup(db *dbConfig, config *BackupConfig) { - utils.Info("Backup database to s3 storage") + logger.Info("Backup database to s3 storage") startTime = time.Now().Format(utils.TimeFormat()) - //Backup database + // 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 ... ") + logger.Info("Uploading backup archive to remote storage S3 ... ") awsConfig := initAWSConfig() if config.remotePath == "" { config.remotePath = awsConfig.remotePath } - utils.Info("Backup name is %s", finalFileName) + logger.Info("Backup name is %s", finalFileName) s3Storage, err := s3.NewStorage(s3.Config{ Endpoint: awsConfig.endpoint, Bucket: awsConfig.bucket, @@ -310,20 +313,20 @@ func s3Backup(db *dbConfig, config *BackupConfig) { LocalPath: tmpPath, }) if err != nil { - utils.Fatal("Error creating s3 storage: %s", err) + logger.Fatal("Error creating s3 storage: %s", err) } err = s3Storage.Copy(finalFileName) if err != nil { - utils.Fatal("Error copying backup file: %s", err) + logger.Fatal("Error copying backup file: %s", err) } - //Get backup info + // Get backup info fileInfo, err := os.Stat(filepath.Join(tmpPath, finalFileName)) if err != nil { - utils.Error("Error: %s", err) + logger.Error("Error: %s", err) } backupSize = fileInfo.Size() - //Delete backup file from tmp folder + // Delete backup file from tmp folder err = utils.DeleteFile(filepath.Join(tmpPath, config.backupFileName)) if err != nil { fmt.Println("Error deleting file: ", err) @@ -333,12 +336,12 @@ func s3Backup(db *dbConfig, config *BackupConfig) { if config.prune { err := s3Storage.Prune(config.backupRetention) if err != nil { - utils.Fatal("Error deleting old backup from %s storage: %s ", config.storage, err) + logger.Fatal("Error deleting old backup from %s storage: %s ", config.storage, err) } } - utils.Info("Backup saved in %s", filepath.Join(config.remotePath, finalFileName)) - utils.Info("Uploading backup archive to remote storage S3 ... done ") - //Send notification + logger.Info("Backup saved in %s", filepath.Join(config.remotePath, finalFileName)) + logger.Info("Uploading backup archive to remote storage S3 ... done ") + // Send notification utils.NotifySuccess(&utils.NotificationData{ File: finalFileName, BackupSize: backupSize, @@ -348,26 +351,26 @@ func s3Backup(db *dbConfig, config *BackupConfig) { StartTime: startTime, EndTime: time.Now().Format(utils.TimeFormat()), }) - //Delete temp + // Delete temp deleteTemp() - utils.Info("Backup completed successfully") + logger.Info("Backup completed successfully") } func sshBackup(db *dbConfig, config *BackupConfig) { - utils.Info("Backup database to Remote server") + logger.Info("Backup database to Remote server") startTime = time.Now().Format(utils.TimeFormat()) - //Backup database + // 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 ... ") - utils.Info("Backup name is %s", finalFileName) + logger.Info("Uploading backup archive to remote storage ... ") + logger.Info("Backup name is %s", finalFileName) sshConfig, err := loadSSHConfig() if err != nil { - utils.Fatal("Error loading ssh config: %s", err) + logger.Fatal("Error loading ssh config: %s", err) } sshStorage, err := ssh.NewStorage(ssh.Config{ @@ -379,35 +382,35 @@ func sshBackup(db *dbConfig, config *BackupConfig) { LocalPath: tmpPath, }) if err != nil { - utils.Fatal("Error creating SSH storage: %s", err) + logger.Fatal("Error creating SSH storage: %s", err) } err = sshStorage.Copy(finalFileName) if err != nil { - utils.Fatal("Error copying backup file: %s", err) + logger.Fatal("Error copying backup file: %s", err) } - //Get backup info + // Get backup info fileInfo, err := os.Stat(filepath.Join(tmpPath, finalFileName)) if err != nil { - utils.Error("Error: %s", err) + logger.Error("Error: %s", err) } backupSize = fileInfo.Size() - utils.Info("Backup saved in %s", filepath.Join(config.remotePath, finalFileName)) + logger.Info("Backup saved in %s", filepath.Join(config.remotePath, finalFileName)) - //Delete backup file from tmp folder + // Delete backup file from tmp folder err = utils.DeleteFile(filepath.Join(tmpPath, finalFileName)) if err != nil { - utils.Error("Error deleting file: %v", err) + logger.Error("Error deleting file: %v", err) } if config.prune { err := sshStorage.Prune(config.backupRetention) if err != nil { - utils.Fatal("Error deleting old backup from %s storage: %s ", config.storage, err) + logger.Fatal("Error deleting old backup from %s storage: %s ", config.storage, err) } } - utils.Info("Uploading backup archive to remote storage ... done ") - //Send notification + logger.Info("Uploading backup archive to remote storage ... done ") + // Send notification utils.NotifySuccess(&utils.NotificationData{ File: finalFileName, BackupSize: backupSize, @@ -417,24 +420,24 @@ func sshBackup(db *dbConfig, config *BackupConfig) { StartTime: startTime, EndTime: time.Now().Format(utils.TimeFormat()), }) - //Delete temp + // Delete temp deleteTemp() - utils.Info("Backup completed successfully") + logger.Info("Backup completed successfully") } func ftpBackup(db *dbConfig, config *BackupConfig) { - utils.Info("Backup database to the remote FTP server") + logger.Info("Backup database to the remote FTP server") startTime = time.Now().Format(utils.TimeFormat()) - //Backup database + // 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 the remote FTP server ... ") - utils.Info("Backup name is %s", finalFileName) + logger.Info("Uploading backup archive to the remote FTP server ... ") + logger.Info("Backup name is %s", finalFileName) ftpConfig := loadFtpConfig() ftpStorage, err := ftp.NewStorage(ftp.Config{ Host: ftpConfig.host, @@ -445,36 +448,36 @@ func ftpBackup(db *dbConfig, config *BackupConfig) { LocalPath: tmpPath, }) if err != nil { - utils.Fatal("Error creating SSH storage: %s", err) + logger.Fatal("Error creating SSH storage: %s", err) } err = ftpStorage.Copy(finalFileName) if err != nil { - utils.Fatal("Error copying backup file: %s", err) + logger.Fatal("Error copying backup file: %s", err) } - utils.Info("Backup saved in %s", filepath.Join(config.remotePath, finalFileName)) - //Get backup info + logger.Info("Backup saved in %s", filepath.Join(config.remotePath, finalFileName)) + // Get backup info fileInfo, err := os.Stat(filepath.Join(tmpPath, finalFileName)) if err != nil { - utils.Error("Error: %s", err) + logger.Error("Error: %s", err) } backupSize = fileInfo.Size() - //Delete backup file from tmp folder + // Delete backup file from tmp folder err = utils.DeleteFile(filepath.Join(tmpPath, finalFileName)) if err != nil { - utils.Error("Error deleting file: %v", err) + logger.Error("Error deleting file: %v", err) } if config.prune { err := ftpStorage.Prune(config.backupRetention) if err != nil { - utils.Fatal("Error deleting old backup from %s storage: %s ", config.storage, err) + logger.Fatal("Error deleting old backup from %s storage: %s ", config.storage, err) } } - utils.Info("Uploading backup archive to the remote FTP server ... done ") + logger.Info("Uploading backup archive to the remote FTP server ... done ") - //Send notification + // Send notification utils.NotifySuccess(&utils.NotificationData{ File: finalFileName, BackupSize: backupSize, @@ -484,36 +487,36 @@ func ftpBackup(db *dbConfig, config *BackupConfig) { StartTime: startTime, EndTime: time.Now().Format(utils.TimeFormat()), }) - //Delete temp + // Delete temp deleteTemp() - utils.Info("Backup completed successfully") + logger.Info("Backup completed successfully") } func encryptBackup(config *BackupConfig) { backupFile, err := os.ReadFile(filepath.Join(tmpPath, config.backupFileName)) outputFile := fmt.Sprintf("%s.%s", filepath.Join(tmpPath, config.backupFileName), gpgExtension) if err != nil { - utils.Fatal("Error reading backup file: %s ", err) + logger.Fatal("Error reading backup file: %s ", err) } if config.usingKey { - utils.Info("Encrypting backup using public key...") + logger.Info("Encrypting backup using public key...") pubKey, err := os.ReadFile(config.publicKey) if err != nil { - utils.Fatal("Error reading public key: %s ", err) + logger.Fatal("Error reading public key: %s ", err) } err = encryptor.EncryptWithPublicKey(backupFile, fmt.Sprintf("%s.%s", filepath.Join(tmpPath, config.backupFileName), gpgExtension), pubKey) if err != nil { - utils.Fatal("Error encrypting backup file: %v ", err) + logger.Fatal("Error encrypting backup file: %v ", err) } - utils.Info("Encrypting backup using public key...done") + logger.Info("Encrypting backup using public key...done") } else if config.passphrase != "" { - utils.Info("Encrypting backup using passphrase...") + logger.Info("Encrypting backup using passphrase...") err := encryptor.Encrypt(backupFile, outputFile, config.passphrase) if err != nil { - utils.Fatal("error during encrypting backup %v", err) + logger.Fatal("error during encrypting backup %v", err) } - utils.Info("Encrypting backup using passphrase...done") + logger.Info("Encrypting backup using passphrase...done") } diff --git a/internal/config.go b/internal/config.go index 0dbe6cb..2d041b0 100644 --- a/internal/config.go +++ b/internal/config.go @@ -1,5 +1,6 @@ // Package internal / -/***** +/* +**** @author Jonas Kaninda @license MIT License @Copyright © 2024 Jonas Kaninda @@ -8,6 +9,7 @@ package internal import ( "fmt" + "github.com/jkaninda/pg-bkup/pkg/logger" "github.com/jkaninda/pg-bkup/utils" "github.com/spf13/cobra" "os" @@ -86,7 +88,7 @@ type AWSConfig struct { } func initDbConfig(cmd *cobra.Command) *dbConfig { - //Set env + // Set env utils.GetEnv(cmd, "dbname", "DB_NAME") dConf := dbConfig{} dConf.dbHost = os.Getenv("DB_HOST") @@ -97,8 +99,8 @@ func initDbConfig(cmd *cobra.Command) *dbConfig { err := utils.CheckEnvVars(dbHVars) if err != nil { - utils.Error("Please make sure all required environment variables for database are set") - utils.Fatal("Error checking environment variables: %s", err) + logger.Error("Please make sure all required environment variables for database are set") + logger.Fatal("Error checking environment variables: %s", err) } return &dConf } @@ -131,7 +133,7 @@ func loadSSHConfig() (*SSHConfig, error) { }, nil } func loadFtpConfig() *FTPConfig { - //Initialize data configs + // Initialize data configs fConfig := FTPConfig{} fConfig.host = utils.GetEnvVariable("FTP_HOST", "FTP_HOST_NAME") fConfig.user = os.Getenv("FTP_USER") @@ -140,13 +142,13 @@ func loadFtpConfig() *FTPConfig { fConfig.remotePath = os.Getenv("REMOTE_PATH") err := utils.CheckEnvVars(ftpVars) if err != nil { - utils.Error("Please make sure all required environment variables for FTP are set") - utils.Fatal("Error missing environment variables: %s", err) + logger.Error("Please make sure all required environment variables for FTP are set") + logger.Fatal("Error missing environment variables: %s", err) } return &fConfig } func initAWSConfig() *AWSConfig { - //Initialize AWS configs + // Initialize AWS configs aConfig := AWSConfig{} aConfig.endpoint = utils.GetEnvVariable("AWS_S3_ENDPOINT", "S3_ENDPOINT") aConfig.accessKey = utils.GetEnvVariable("AWS_ACCESS_KEY", "ACCESS_KEY") @@ -167,8 +169,8 @@ func initAWSConfig() *AWSConfig { aConfig.forcePathStyle = forcePathStyle err = utils.CheckEnvVars(awsVars) if err != nil { - utils.Error("Please make sure all required environment variables for AWS S3 are set") - utils.Fatal("Error checking environment variables: %s", err) + logger.Error("Please make sure all required environment variables for AWS S3 are set") + logger.Fatal("Error checking environment variables: %s", err) } return &aConfig } @@ -176,7 +178,7 @@ func initBackupConfig(cmd *cobra.Command) *BackupConfig { utils.SetEnv("STORAGE_PATH", storagePath) utils.GetEnv(cmd, "cron-expression", "BACKUP_CRON_EXPRESSION") utils.GetEnv(cmd, "path", "REMOTE_PATH") - //Get flag value and set env + // Get flag value and set env remotePath := utils.GetEnvVariable("REMOTE_PATH", "SSH_REMOTE_PATH") storage = utils.GetEnv(cmd, "storage", "STORAGE") prune := false @@ -198,7 +200,7 @@ func initBackupConfig(cmd *cobra.Command) *BackupConfig { encryption = true usingKey = false } - //Initialize backup configs + // Initialize backup configs config := BackupConfig{} config.backupRetention = backupRetention config.disableCompression = disableCompression @@ -228,7 +230,7 @@ func initRestoreConfig(cmd *cobra.Command) *RestoreConfig { utils.SetEnv("STORAGE_PATH", storagePath) utils.GetEnv(cmd, "path", "REMOTE_PATH") - //Get flag value and set env + // Get flag value and set env s3Path := utils.GetEnv(cmd, "path", "AWS_S3_PATH") remotePath := utils.GetEnvVariable("REMOTE_PATH", "SSH_REMOTE_PATH") storage = utils.GetEnv(cmd, "storage", "STORAGE") @@ -242,7 +244,7 @@ func initRestoreConfig(cmd *cobra.Command) *RestoreConfig { usingKey = false } - //Initialize restore configs + // Initialize restore configs rConfig := RestoreConfig{} rConfig.s3Path = s3Path rConfig.remotePath = remotePath @@ -265,8 +267,8 @@ func initTargetDbConfig() *targetDbConfig { err := utils.CheckEnvVars(tdbRVars) if err != nil { - utils.Error("Please make sure all required environment variables for the target database are set") - utils.Fatal("Error checking target database environment variables: %s", err) + logger.Error("Please make sure all required environment variables for the target database are set") + logger.Fatal("Error checking target database environment variables: %s", err) } return &tdbConfig } diff --git a/internal/helper.go b/internal/helper.go index 6865f33..10a9149 100644 --- a/internal/helper.go +++ b/internal/helper.go @@ -1,5 +1,6 @@ // Package internal / -/***** +/* +**** @author Jonas Kaninda @license MIT License @Copyright © 2024 Jonas Kaninda @@ -9,6 +10,7 @@ package internal import ( "bytes" "fmt" + "github.com/jkaninda/pg-bkup/pkg/logger" "github.com/jkaninda/pg-bkup/utils" "gopkg.in/yaml.v3" "os" @@ -18,13 +20,13 @@ import ( ) func intro() { - utils.Info("Starting PostgreSQL Backup...") - utils.Info("Copyright (c) 2024 Jonas Kaninda ") + logger.Info("Starting PostgreSQL Backup...") + logger.Info("Copyright (c) 2024 Jonas Kaninda ") } // copyToTmp copy file to temporary directory func deleteTemp() { - utils.Info("Deleting %s ...", tmpPath) + logger.Info("Deleting %s ...", tmpPath) err := filepath.Walk(tmpPath, func(path string, info os.FileInfo, err error) error { if err != nil { return err @@ -40,16 +42,16 @@ func deleteTemp() { return nil }) if err != nil { - utils.Error("Error deleting files: %v", err) + logger.Error("Error deleting files: %v", err) } else { - utils.Info("Deleting %s ... done", tmpPath) + logger.Info("Deleting %s ... done", tmpPath) } } // TestDatabaseConnection tests the database connection func testDatabaseConnection(db *dbConfig) { - utils.Info("Connecting to %s database ...", db.dbName) + logger.Info("Connecting to %s database ...", db.dbName) // Test database connection query := "SELECT version();" @@ -74,10 +76,10 @@ func testDatabaseConnection(db *dbConfig) { // Run the command and capture any errors err = cmd.Run() if err != nil { - utils.Fatal("Error running psql command: %v\nOutput: %s\n", err, out.String()) + logger.Fatal("Error running psql command: %v\nOutput: %s\n", err, out.String()) return } - utils.Info("Successfully connected to %s database", db.dbName) + logger.Info("Successfully connected to %s database", db.dbName) } diff --git a/internal/migrate.go b/internal/migrate.go index 2e77048..31fb99e 100644 --- a/internal/migrate.go +++ b/internal/migrate.go @@ -1,5 +1,6 @@ // Package internal / -/***** +/* +**** @author Jonas Kaninda @license MIT License @Copyright © 2024 Jonas Kaninda @@ -8,19 +9,19 @@ package internal import ( "fmt" - "github.com/jkaninda/pg-bkup/utils" + "github.com/jkaninda/pg-bkup/pkg/logger" "github.com/spf13/cobra" "time" ) func StartMigration(cmd *cobra.Command) { intro() - utils.Info("Starting database migration...") - //Get DB config + logger.Info("Starting database migration...") + // Get DB config dbConf = initDbConfig(cmd) targetDbConf = initTargetDbConfig() - //Defining the target database variables + // Defining the target database variables newDbConfig := dbConfig{} newDbConfig.dbHost = targetDbConf.targetDbHost newDbConfig.dbPort = targetDbConf.targetDbPort @@ -28,15 +29,15 @@ func StartMigration(cmd *cobra.Command) { newDbConfig.dbUserName = targetDbConf.targetDbUserName newDbConfig.dbPassword = targetDbConf.targetDbPassword - //Generate file name + // Generate file name backupFileName := fmt.Sprintf("%s_%s.sql", dbConf.dbName, time.Now().Format("20060102_150405")) conf := &RestoreConfig{} conf.file = backupFileName - //Backup source Database + // Backup source Database BackupDatabase(dbConf, backupFileName, true) - //Restore source database into target database - utils.Info("Restoring [%s] database into [%s] database...", dbConf.dbName, targetDbConf.targetDbName) + // Restore source database into target database + logger.Info("Restoring [%s] database into [%s] database...", dbConf.dbName, targetDbConf.targetDbName) RestoreDatabase(&newDbConfig, conf) - utils.Info("[%s] database has been restored into [%s] database", dbConf.dbName, targetDbConf.targetDbName) - utils.Info("Database migration completed.") + logger.Info("[%s] database has been restored into [%s] database", dbConf.dbName, targetDbConf.targetDbName) + logger.Info("Database migration completed.") } diff --git a/internal/restore.go b/internal/restore.go index 935090c..64e1c35 100644 --- a/internal/restore.go +++ b/internal/restore.go @@ -1,5 +1,6 @@ // Package internal / -/***** +/* +**** @author Jonas Kaninda @license MIT License @Copyright © 2024 Jonas Kaninda @@ -7,6 +8,7 @@ package internal import ( + "github.com/jkaninda/pg-bkup/pkg/logger" "os" "os/exec" "path/filepath" @@ -39,20 +41,20 @@ func StartRestore(cmd *cobra.Command) { } } func localRestore(dbConf *dbConfig, restoreConf *RestoreConfig) { - utils.Info("Restore database from local") + logger.Info("Restore database from local") localStorage := local.NewStorage(local.Config{ RemotePath: storagePath, LocalPath: tmpPath, }) err := localStorage.CopyFrom(restoreConf.file) if err != nil { - utils.Fatal("Error copying backup file: %s", err) + logger.Fatal("Error copying backup file: %s", err) } RestoreDatabase(dbConf, restoreConf) } func restoreFromS3(db *dbConfig, conf *RestoreConfig) { - utils.Info("Restore database from s3") + logger.Info("Restore database from s3") awsConfig := initAWSConfig() if conf.remotePath == "" { conf.remotePath = awsConfig.remotePath @@ -69,19 +71,19 @@ func restoreFromS3(db *dbConfig, conf *RestoreConfig) { LocalPath: tmpPath, }) if err != nil { - utils.Fatal("Error creating s3 storage: %s", err) + logger.Fatal("Error creating s3 storage: %s", err) } err = s3Storage.CopyFrom(conf.file) if err != nil { - utils.Fatal("Error download file from S3 storage: %s", err) + logger.Fatal("Error download file from S3 storage: %s", err) } RestoreDatabase(db, conf) } func restoreFromRemote(db *dbConfig, conf *RestoreConfig) { - utils.Info("Restore database from remote server") + logger.Info("Restore database from remote server") sshConfig, err := loadSSHConfig() if err != nil { - utils.Fatal("Error loading ssh config: %s", err) + logger.Fatal("Error loading ssh config: %s", err) } sshStorage, err := ssh.NewStorage(ssh.Config{ @@ -94,16 +96,16 @@ func restoreFromRemote(db *dbConfig, conf *RestoreConfig) { LocalPath: tmpPath, }) if err != nil { - utils.Fatal("Error creating SSH storage: %s", err) + logger.Fatal("Error creating SSH storage: %s", err) } err = sshStorage.CopyFrom(conf.file) if err != nil { - utils.Fatal("Error copying backup file: %s", err) + logger.Fatal("Error copying backup file: %s", err) } RestoreDatabase(db, conf) } func restoreFromFTP(db *dbConfig, conf *RestoreConfig) { - utils.Info("Restore database from FTP server") + logger.Info("Restore database from FTP server") ftpConfig := loadFtpConfig() ftpStorage, err := ftp.NewStorage(ftp.Config{ Host: ftpConfig.host, @@ -114,11 +116,11 @@ func restoreFromFTP(db *dbConfig, conf *RestoreConfig) { LocalPath: tmpPath, }) if err != nil { - utils.Fatal("Error creating SSH storage: %s", err) + logger.Fatal("Error creating SSH storage: %s", err) } err = ftpStorage.CopyFrom(conf.file) if err != nil { - utils.Fatal("Error copying backup file: %s", err) + logger.Fatal("Error copying backup file: %s", err) } RestoreDatabase(db, conf) } @@ -126,43 +128,43 @@ func restoreFromFTP(db *dbConfig, conf *RestoreConfig) { // RestoreDatabase restore database func RestoreDatabase(db *dbConfig, conf *RestoreConfig) { if conf.file == "" { - utils.Fatal("Error, file required") + logger.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)) if err != nil { - utils.Fatal("Error reading backup file: %s ", err) + logger.Fatal("Error reading backup file: %s ", err) } 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") + logger.Info("Decrypting backup using private key...") + logger.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) + logger.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) + logger.Fatal("error during decrypting backup %v", err) } - utils.Info("Decrypting backup using private key...done") + logger.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.") + logger.Error("Error, passphrase or private key required") + logger.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 + logger.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) + logger.Fatal("Error decrypting file %s %v", file, err) } - utils.Info("Decrypting backup using passphrase...done") - //Update file name + logger.Info("Decrypting backup using passphrase...done") + // Update file name conf.file = RemoveLastExtension(file) } } @@ -176,7 +178,7 @@ func RestoreDatabase(db *dbConfig, conf *RestoreConfig) { return } testDatabaseConnection(db) - utils.Info("Restoring database...") + logger.Info("Restoring database...") extension := filepath.Ext(conf.file) // Restore from compressed file / .sql.gz @@ -184,29 +186,29 @@ func RestoreDatabase(db *dbConfig, conf *RestoreConfig) { str := "zcat " + filepath.Join(tmpPath, conf.file) + " | psql -h " + db.dbHost + " -p " + db.dbPort + " -U " + db.dbUserName + " -v -d " + db.dbName _, err := exec.Command("sh", "-c", str).Output() if err != nil { - utils.Fatal("Error, in restoring the database %v", err) + logger.Fatal("Error, in restoring the database %v", err) } - utils.Info("Restoring database... done") - utils.Info("Database has been restored") - //Delete temp + logger.Info("Restoring database... done") + logger.Info("Database has been restored") + // Delete temp deleteTemp() } else if extension == ".sql" { - //Restore from sql file + // Restore from sql file str := "cat " + filepath.Join(tmpPath, conf.file) + " | psql -h " + db.dbHost + " -p " + db.dbPort + " -U " + db.dbUserName + " -v -d " + db.dbName _, err := exec.Command("sh", "-c", str).Output() if err != nil { - utils.Fatal("Error in restoring the database %v", err) + logger.Fatal("Error in restoring the database %v", err) } - utils.Info("Restoring database... done") - utils.Info("Database has been restored") - //Delete temp + logger.Info("Restoring database... done") + logger.Info("Database has been restored") + // Delete temp deleteTemp() } else { - utils.Fatal("Unknown file extension: %s", extension) + logger.Fatal("Unknown file extension: %s", extension) } } else { - utils.Fatal("File not found in %s", filepath.Join(tmpPath, conf.file)) + logger.Fatal("File not found in %s", filepath.Join(tmpPath, conf.file)) } } diff --git a/internal/var.go b/internal/var.go index 195efd9..4b16738 100644 --- a/internal/var.go +++ b/internal/var.go @@ -1,14 +1,8 @@ // Package internal / -/***** -@author Jonas Kaninda -@license MIT License -@Copyright © 2024 Jonas Kaninda -**/ package internal const tmpPath = "/tmp/backup" const gpgHome = "/config/gnupg" -const algorithm = "aes256" const gpgExtension = "gpg" const timeFormat = "2006-01-02 at 15:04:05" @@ -44,13 +38,6 @@ var tdbRVars = []string{ var dbConf *dbConfig var targetDbConf *targetDbConfig -// sshVars Required environment variables for SSH remote server storage -var sshVars = []string{ - "SSH_USER", - "SSH_HOST_NAME", - "SSH_PORT", - "REMOTE_PATH", -} var ftpVars = []string{ "FTP_HOST_NAME", "FTP_USER", diff --git a/main.go b/main.go index 810be93..c0ec9c1 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,8 @@ // Package main / -/***** +/* +**** @author Jonas Kaninda -@license MIT License +@license MIT License @Copyright © 2024 Jonas Kaninda **/ package main diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go new file mode 100644 index 0000000..291d7d7 --- /dev/null +++ b/pkg/logger/logger.go @@ -0,0 +1,73 @@ +package logger + +import ( + "fmt" + "log" + "os" + "runtime" + "strings" +) + +// Info returns info log +func Info(msg string, args ...interface{}) { + log.SetOutput(getStd("/dev/stdout")) + logWithCaller("INFO", msg, args...) + +} + +// Warn returns warning log +func Warn(msg string, args ...interface{}) { + log.SetOutput(getStd("/dev/stdout")) + logWithCaller("WARN", msg, args...) + +} + +// Error logs error messages +func Error(msg string, args ...interface{}) { + log.SetOutput(getStd("/dev/stderr")) + logWithCaller("ERROR", msg, args...) +} + +func Fatal(msg string, args ...interface{}) { + log.SetOutput(os.Stdout) + logWithCaller("ERROR", msg, args...) + os.Exit(1) +} + +// Helper function to format and log messages with file and line number +func logWithCaller(level, msg string, args ...interface{}) { + // Format message if there are additional arguments + formattedMessage := msg + if len(args) > 0 { + formattedMessage = fmt.Sprintf(msg, args...) + } + + // Get the caller's file and line number (skip 2 frames) + _, file, line, ok := runtime.Caller(2) + if !ok { + file = "unknown" + line = 0 + } + // Log message with caller information if GOMA_LOG_LEVEL is trace + if strings.ToLower(level) != "off" { + if strings.ToLower(level) == traceLog { + log.Printf("%s: %s (File: %s, Line: %d)\n", level, formattedMessage, file, line) + } else { + log.Printf("%s: %s\n", level, formattedMessage) + } + } +} + +func getStd(out string) *os.File { + switch out { + case "/dev/stdout": + return os.Stdout + case "/dev/stderr": + return os.Stderr + case "/dev/stdin": + return os.Stdin + default: + return os.Stdout + + } +} diff --git a/pkg/logger/var.go b/pkg/logger/var.go new file mode 100644 index 0000000..7ce6c56 --- /dev/null +++ b/pkg/logger/var.go @@ -0,0 +1,3 @@ +package logger + +const traceLog = "trace" diff --git a/utils/constant.go b/utils/constant.go index 36fa603..84ac1d6 100644 --- a/utils/constant.go +++ b/utils/constant.go @@ -1,9 +1,4 @@ // Package utils / -/***** -@author Jonas Kaninda -@license MIT License -@Copyright © 2024 Jonas Kaninda -**/ package utils import "os" diff --git a/utils/logger.go b/utils/logger.go deleted file mode 100644 index d9d1668..0000000 --- a/utils/logger.go +++ /dev/null @@ -1,62 +0,0 @@ -// Package utils / -/***** -@author Jonas Kaninda -@license MIT License -@Copyright © 2024 Jonas Kaninda -**/ -package utils - -import ( - "fmt" - "log" - "os" -) - -// Info message -func Info(msg string, args ...any) { - log.SetOutput(os.Stdout) - formattedMessage := fmt.Sprintf(msg, args...) - if len(args) == 0 { - log.Printf("INFO: %s\n", msg) - } else { - log.Printf("INFO: %s\n", formattedMessage) - } -} - -// Warn a Warning message -func Warn(msg string, args ...any) { - log.SetOutput(os.Stdout) - formattedMessage := fmt.Sprintf(msg, args...) - if len(args) == 0 { - log.Printf("WARN: %s\n", msg) - } else { - log.Printf("WARN: %s\n", formattedMessage) - } -} - -// Error error message -func Error(msg string, args ...any) { - log.SetOutput(os.Stdout) - formattedMessage := fmt.Sprintf(msg, args...) - if len(args) == 0 { - log.Printf("ERROR: %s\n", msg) - } else { - log.Printf("ERROR: %s\n", formattedMessage) - - } -} -func Fatal(msg string, args ...any) { - log.SetOutput(os.Stdout) - // Fatal logs an error message and exits the program. - formattedMessage := fmt.Sprintf(msg, args...) - if len(args) == 0 { - log.Printf("ERROR: %s\n", msg) - NotifyError(msg) - } else { - log.Printf("ERROR: %s\n", formattedMessage) - NotifyError(formattedMessage) - - } - - os.Exit(1) -} diff --git a/utils/notification.go b/utils/notification.go index 108c337..bfbce2d 100644 --- a/utils/notification.go +++ b/utils/notification.go @@ -6,8 +6,9 @@ import ( "encoding/json" "fmt" "github.com/go-mail/mail" + "github.com/jkaninda/pg-bkup/pkg/logger" "html/template" - "io/ioutil" + "io" "net/http" "os" "path/filepath" @@ -31,7 +32,7 @@ func parseTemplate[T any](data T, fileName string) (string, error) { } func SendEmail(subject, body string) error { - Info("Start sending email notification....") + logger.Info("Start sending email notification....") config := loadMailConfig() emails := strings.Split(config.MailTo, ",") m := mail.NewMessage() @@ -43,16 +44,16 @@ func SendEmail(subject, body string) error { d.TLSConfig = &tls.Config{InsecureSkipVerify: config.SkipTls} if err := d.DialAndSend(m); err != nil { - Error("Error could not send email : %v", err) + logger.Error("Error could not send email : %v", err) return err } - Info("Email notification has been sent") + logger.Info("Email notification has been sent") return nil } func sendMessage(msg string) error { - Info("Sending Telegram notification... ") + logger.Info("Sending Telegram notification... ") chatId := os.Getenv("TG_CHAT_ID") body, _ := json.Marshal(map[string]string{ "chat_id": chatId, @@ -72,11 +73,11 @@ func sendMessage(msg string) error { } code := response.StatusCode if code == 200 { - Info("Telegram notification has been sent") + logger.Info("Telegram notification has been sent") return nil } else { - body, _ := ioutil.ReadAll(response.Body) - Error("Error could not send message, error: %s", string(body)) + body, _ := io.ReadAll(response.Body) + logger.Error("Error could not send message, error: %s", string(body)) return fmt.Errorf("error could not send message %s", string(body)) } @@ -96,29 +97,29 @@ func NotifySuccess(notificationData *NotificationData) { "MAIL_TO", } - //Email notification + // Email notification err := CheckEnvVars(mailVars) if err == nil { body, err := parseTemplate(*notificationData, "email.tmpl") if err != nil { - Error("Could not parse email template: %v", err) + logger.Error("Could not parse email template: %v", err) } err = SendEmail(fmt.Sprintf("✅ Database Backup Notification – %s", notificationData.Database), body) if err != nil { - Error("Could not send email: %v", err) + logger.Error("Could not send email: %v", err) } } - //Telegram notification + // Telegram notification err = CheckEnvVars(vars) if err == nil { message, err := parseTemplate(*notificationData, "telegram.tmpl") if err != nil { - Error("Could not parse telegram template: %v", err) + logger.Error("Could not parse telegram template: %v", err) } err = sendMessage(message) if err != nil { - Error("Could not send Telegram message: %v", err) + logger.Error("Could not send Telegram message: %v", err) } } } @@ -136,7 +137,7 @@ func NotifyError(error string) { "MAIL_TO", } - //Email notification + // Email notification err := CheckEnvVars(mailVars) if err == nil { body, err := parseTemplate(ErrorMessage{ @@ -145,14 +146,14 @@ func NotifyError(error string) { BackupReference: os.Getenv("BACKUP_REFERENCE"), }, "email-error.tmpl") if err != nil { - Error("Could not parse error template: %v", err) + logger.Error("Could not parse error template: %v", err) } - err = SendEmail(fmt.Sprintf("🔴 Urgent: Database Backup Failure Notification"), body) + err = SendEmail("🔴 Urgent: Database Backup Failure Notification", body) if err != nil { - Error("Could not send email: %v", err) + logger.Error("Could not send email: %v", err) } } - //Telegram notification + // Telegram notification err = CheckEnvVars(vars) if err == nil { message, err := parseTemplate(ErrorMessage{ @@ -161,13 +162,13 @@ func NotifyError(error string) { BackupReference: os.Getenv("BACKUP_REFERENCE"), }, "telegram-error.tmpl") if err != nil { - Error("Could not parse error template: %v", err) + logger.Error("Could not parse error template: %v", err) } err = sendMessage(message) if err != nil { - Error("Could not send telegram message: %v", err) + logger.Error("Could not send telegram message: %v", err) } } } diff --git a/utils/utils.go b/utils/utils.go index 2898057..c808275 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -1,5 +1,6 @@ // Package utils / -/***** +/* +**** @author Jonas Kaninda @license MIT License @Copyright © 2024 Jonas Kaninda @@ -8,6 +9,7 @@ package utils import ( "fmt" + "github.com/jkaninda/pg-bkup/pkg/logger" "github.com/robfig/cron/v3" "github.com/spf13/cobra" "io" @@ -31,7 +33,13 @@ func WriteToFile(filePath, content string) error { if err != nil { return err } - defer file.Close() + defer func(file *os.File) { + err := file.Close() + if err != nil { + return + + } + }(file) _, err = file.WriteString(content) return err @@ -49,14 +57,25 @@ func CopyFile(src, dst string) error { if err != nil { return fmt.Errorf("failed to open source file: %v", err) } - defer sourceFile.Close() + defer func(sourceFile *os.File) { + err := sourceFile.Close() + if err != nil { + return + } + }(sourceFile) // Create the destination file destinationFile, err := os.Create(dst) if err != nil { return fmt.Errorf("failed to create destination file: %v", err) } - defer destinationFile.Close() + defer func(destinationFile *os.File) { + err := destinationFile.Close() + if err != nil { + return + + } + }(destinationFile) // Copy the content from source to destination _, err = io.Copy(destinationFile, sourceFile) @@ -74,7 +93,7 @@ func CopyFile(src, dst string) error { } func ChangePermission(filePath string, mod int) { if err := os.Chmod(filePath, fs.FileMode(mod)); err != nil { - Fatal("Error changing permissions of %s: %v\n", filePath, err) + logger.Fatal("Error changing permissions of %s: %v\n", filePath, err) } } @@ -83,7 +102,12 @@ func IsDirEmpty(name string) (bool, error) { if err != nil { return false, err } - defer f.Close() + defer func(f *os.File) { + err := f.Close() + if err != nil { + return + } + }(f) _, err = f.Readdirnames(1) if err == nil { @@ -131,7 +155,7 @@ func GetEnvVariable(envName, oldEnvName string) string { if err != nil { return value } - Warn("%s is deprecated, please use %s instead! ", oldEnvName, envName) + logger.Warn("%s is deprecated, please use %s instead! ", oldEnvName, envName) } } return value @@ -178,7 +202,7 @@ func GetIntEnv(envName string) int { } ret, err := strconv.Atoi(val) if err != nil { - Error("Error: %v", err) + logger.Error("Error: %v", err) } return ret } @@ -203,14 +227,13 @@ func CronNextTime(cronExpr string) time.Time { // Parse the cron expression schedule, err := cron.ParseStandard(cronExpr) if err != nil { - Error("Error parsing cron expression: %s", err) + logger.Error("Error parsing cron expression: %s", 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 } func UsageErrorf(cmd *cobra.Command, message string, args ...interface{}) error {