mirror of
https://github.com/jkaninda/mysql-bkup.git
synced 2025-12-06 21:49:40 +01:00
Merge pull request #151 from jkaninda/refactor
fix: fatal logger notification
This commit is contained in:
@@ -25,8 +25,7 @@ SOFTWARE.
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/jkaninda/mysql-bkup/internal"
|
||||
"github.com/jkaninda/mysql-bkup/pkg/logger"
|
||||
"github.com/jkaninda/mysql-bkup/pkg"
|
||||
"github.com/jkaninda/mysql-bkup/utils"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -37,9 +36,9 @@ var BackupCmd = &cobra.Command{
|
||||
Example: utils.BackupExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
internal.StartBackup(cmd)
|
||||
pkg.StartBackup(cmd)
|
||||
} else {
|
||||
logger.Fatal(`"backup" accepts no argument %q`, args)
|
||||
utils.Fatal(`"backup" accepts no argument %q`, args)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ SOFTWARE.
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/jkaninda/mysql-bkup/internal"
|
||||
"github.com/jkaninda/mysql-bkup/pkg/logger"
|
||||
"github.com/jkaninda/mysql-bkup/pkg"
|
||||
"github.com/jkaninda/mysql-bkup/utils"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -35,9 +35,9 @@ var MigrateCmd = &cobra.Command{
|
||||
Short: "Migrate database from a source database to a target database",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
internal.StartMigration(cmd)
|
||||
pkg.StartMigration(cmd)
|
||||
} else {
|
||||
logger.Fatal(`"migrate" accepts no argument %q`, args)
|
||||
utils.Fatal(`"migrate" accepts no argument %q`, args)
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -24,8 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
import (
|
||||
"github.com/jkaninda/mysql-bkup/internal"
|
||||
"github.com/jkaninda/mysql-bkup/pkg/logger"
|
||||
"github.com/jkaninda/mysql-bkup/pkg"
|
||||
"github.com/jkaninda/mysql-bkup/utils"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -36,9 +35,9 @@ var RestoreCmd = &cobra.Command{
|
||||
Example: utils.RestoreExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
internal.StartRestore(cmd)
|
||||
pkg.StartRestore(cmd)
|
||||
} else {
|
||||
logger.Fatal(`"restore" accepts no argument %q`, args)
|
||||
utils.Fatal(`"restore" accepts no argument %q`, args)
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jkaninda/mysql-bkup/utils"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
)
|
||||
@@ -41,6 +42,6 @@ var VersionCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
func Version() {
|
||||
fmt.Printf("Version: %s \n", appVersion)
|
||||
fmt.Printf("Version: %s \n", utils.Version)
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
@@ -22,12 +22,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
package internal
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jkaninda/go-storage/pkg/azure"
|
||||
"github.com/jkaninda/mysql-bkup/pkg/logger"
|
||||
"github.com/jkaninda/mysql-bkup/utils"
|
||||
|
||||
"os"
|
||||
@@ -36,7 +35,7 @@ import (
|
||||
)
|
||||
|
||||
func azureBackup(db *dbConfig, config *BackupConfig) {
|
||||
logger.Info("Backup database to the remote FTP server")
|
||||
utils.Info("Backup database to the remote FTP server")
|
||||
startTime = time.Now().Format(utils.TimeFormat())
|
||||
|
||||
// Backup database
|
||||
@@ -46,8 +45,8 @@ func azureBackup(db *dbConfig, config *BackupConfig) {
|
||||
encryptBackup(config)
|
||||
finalFileName = fmt.Sprintf("%s.%s", config.backupFileName, "gpg")
|
||||
}
|
||||
logger.Info("Uploading backup archive to Azure Blob storage ...")
|
||||
logger.Info("Backup name is %s", finalFileName)
|
||||
utils.Info("Uploading backup archive to Azure Blob storage ...")
|
||||
utils.Info("Backup name is %s", finalFileName)
|
||||
azureConfig := loadAzureConfig()
|
||||
azureStorage, err := azure.NewStorage(azure.Config{
|
||||
ContainerName: azureConfig.containerName,
|
||||
@@ -57,34 +56,34 @@ func azureBackup(db *dbConfig, config *BackupConfig) {
|
||||
LocalPath: tmpPath,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Fatal("Error creating SSH storage: %s", err)
|
||||
utils.Fatal("Error creating Azure storage: %s", err)
|
||||
}
|
||||
err = azureStorage.Copy(finalFileName)
|
||||
if err != nil {
|
||||
logger.Fatal("Error copying backup file: %s", err)
|
||||
utils.Fatal("Error copying backup file: %s", err)
|
||||
}
|
||||
logger.Info("Backup saved in %s", filepath.Join(config.remotePath, finalFileName))
|
||||
utils.Info("Backup saved in %s", filepath.Join(config.remotePath, finalFileName))
|
||||
// Get backup info
|
||||
fileInfo, err := os.Stat(filepath.Join(tmpPath, finalFileName))
|
||||
if err != nil {
|
||||
logger.Error("Error: %s", err)
|
||||
utils.Error("Error: %s", err)
|
||||
}
|
||||
backupSize = fileInfo.Size()
|
||||
// Delete backup file from tmp folder
|
||||
err = utils.DeleteFile(filepath.Join(tmpPath, finalFileName))
|
||||
if err != nil {
|
||||
logger.Error("Error deleting file: %v", err)
|
||||
utils.Error("Error deleting file: %v", err)
|
||||
|
||||
}
|
||||
if config.prune {
|
||||
err := azureStorage.Prune(config.backupRetention)
|
||||
if err != nil {
|
||||
logger.Fatal("Error deleting old backup from %s storage: %s ", config.storage, err)
|
||||
utils.Fatal("Error deleting old backup from %s storage: %s ", config.storage, err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
logger.Info("Uploading backup archive to Azure Blob storage ... done ")
|
||||
utils.Info("Uploading backup archive to Azure Blob storage ... done ")
|
||||
|
||||
// Send notification
|
||||
utils.NotifySuccess(&utils.NotificationData{
|
||||
@@ -98,10 +97,10 @@ func azureBackup(db *dbConfig, config *BackupConfig) {
|
||||
})
|
||||
// Delete temp
|
||||
deleteTemp()
|
||||
logger.Info("Backup completed successfully")
|
||||
utils.Info("Backup completed successfully")
|
||||
}
|
||||
func azureRestore(db *dbConfig, conf *RestoreConfig) {
|
||||
logger.Info("Restore database from Azure Blob storage")
|
||||
utils.Info("Restore database from Azure Blob storage")
|
||||
azureConfig := loadAzureConfig()
|
||||
azureStorage, err := azure.NewStorage(azure.Config{
|
||||
ContainerName: azureConfig.containerName,
|
||||
@@ -111,12 +110,12 @@ func azureRestore(db *dbConfig, conf *RestoreConfig) {
|
||||
LocalPath: tmpPath,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Fatal("Error creating SSH storage: %s", err)
|
||||
utils.Fatal("Error creating SSH storage: %s", err)
|
||||
}
|
||||
|
||||
err = azureStorage.CopyFrom(conf.file)
|
||||
if err != nil {
|
||||
logger.Fatal("Error downloading backup file: %s", err)
|
||||
utils.Fatal("Error downloading backup file: %s", err)
|
||||
}
|
||||
RestoreDatabase(db, conf)
|
||||
}
|
||||
@@ -22,13 +22,12 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
package internal
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jkaninda/encryptor"
|
||||
"github.com/jkaninda/go-storage/pkg/local"
|
||||
"github.com/jkaninda/mysql-bkup/pkg/logger"
|
||||
"github.com/jkaninda/mysql-bkup/utils"
|
||||
"github.com/robfig/cron/v3"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -53,7 +52,7 @@ func StartBackup(cmd *cobra.Command) {
|
||||
if utils.IsValidCronExpression(config.cronExpression) {
|
||||
scheduledMode(dbConf, config)
|
||||
} else {
|
||||
logger.Fatal("Cron expression is not valid: %s", config.cronExpression)
|
||||
utils.Fatal("Cron expression is not valid: %s", config.cronExpression)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -64,22 +63,22 @@ func StartBackup(cmd *cobra.Command) {
|
||||
|
||||
// scheduledMode Runs backup in scheduled mode
|
||||
func scheduledMode(db *dbConfig, config *BackupConfig) {
|
||||
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)
|
||||
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
|
||||
logger.Info("Testing backup configurations...")
|
||||
utils.Info("Testing backup configurations...")
|
||||
testDatabaseConnection(db)
|
||||
logger.Info("Testing backup configurations...done")
|
||||
logger.Info("Creating backup job...")
|
||||
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)
|
||||
logger.Info("Next backup time is: %v", utils.CronNextTime(config.cronExpression).Format(timeFormat))
|
||||
utils.Info("Next backup time is: %v", utils.CronNextTime(config.cronExpression).Format(timeFormat))
|
||||
|
||||
})
|
||||
if err != nil {
|
||||
@@ -87,8 +86,8 @@ func scheduledMode(db *dbConfig, config *BackupConfig) {
|
||||
}
|
||||
// Start the cron scheduler
|
||||
c.Start()
|
||||
logger.Info("Creating backup job...done")
|
||||
logger.Info("Backup job started")
|
||||
utils.Info("Creating backup job...done")
|
||||
utils.Info("Backup job started")
|
||||
defer c.Stop()
|
||||
select {}
|
||||
}
|
||||
@@ -106,7 +105,7 @@ func multiBackupTask(databases []Database, bkConfig *BackupConfig) {
|
||||
|
||||
// BackupTask backups database
|
||||
func BackupTask(db *dbConfig, config *BackupConfig) {
|
||||
logger.Info("Starting backup task...")
|
||||
utils.Info("Starting backup task...")
|
||||
// Generate file name
|
||||
backupFileName := fmt.Sprintf("%s_%s.sql.gz", db.dbName, time.Now().Format("20060102_150405"))
|
||||
if config.disableCompression {
|
||||
@@ -129,17 +128,17 @@ func BackupTask(db *dbConfig, config *BackupConfig) {
|
||||
}
|
||||
}
|
||||
func startMultiBackup(bkConfig *BackupConfig, configFile string) {
|
||||
logger.Info("Starting backup task...")
|
||||
utils.Info("Starting backup task...")
|
||||
conf, err := readConf(configFile)
|
||||
if err != nil {
|
||||
logger.Fatal("Error reading config file: %s", err)
|
||||
utils.Fatal("Error reading config file: %s", err)
|
||||
}
|
||||
// Check if cronExpression is defined in config file
|
||||
if conf.CronExpression != "" {
|
||||
bkConfig.cronExpression = conf.CronExpression
|
||||
}
|
||||
if len(conf.Databases) == 0 {
|
||||
logger.Fatal("No databases found")
|
||||
utils.Fatal("No databases found")
|
||||
}
|
||||
// Check if cronExpression is defined
|
||||
if bkConfig.cronExpression == "" {
|
||||
@@ -147,24 +146,24 @@ func startMultiBackup(bkConfig *BackupConfig, configFile string) {
|
||||
} else {
|
||||
// Check if cronExpression is valid
|
||||
if utils.IsValidCronExpression(bkConfig.cronExpression) {
|
||||
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)
|
||||
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)
|
||||
|
||||
// Test backup
|
||||
logger.Info("Testing backup configurations...")
|
||||
utils.Info("Testing backup configurations...")
|
||||
for _, db := range conf.Databases {
|
||||
testDatabaseConnection(getDatabase(db))
|
||||
}
|
||||
logger.Info("Testing backup configurations...done")
|
||||
logger.Info("Creating backup job...")
|
||||
utils.Info("Testing backup configurations...done")
|
||||
utils.Info("Creating backup job...")
|
||||
// Create a new cron instance
|
||||
c := cron.New()
|
||||
|
||||
_, err := c.AddFunc(bkConfig.cronExpression, func() {
|
||||
multiBackupTask(conf.Databases, bkConfig)
|
||||
logger.Info("Next backup time is: %v", utils.CronNextTime(bkConfig.cronExpression).Format(timeFormat))
|
||||
utils.Info("Next backup time is: %v", utils.CronNextTime(bkConfig.cronExpression).Format(timeFormat))
|
||||
|
||||
})
|
||||
if err != nil {
|
||||
@@ -172,13 +171,13 @@ func startMultiBackup(bkConfig *BackupConfig, configFile string) {
|
||||
}
|
||||
// Start the cron scheduler
|
||||
c.Start()
|
||||
logger.Info("Creating backup job...done")
|
||||
logger.Info("Backup job started")
|
||||
utils.Info("Creating backup job...done")
|
||||
utils.Info("Backup job started")
|
||||
defer c.Stop()
|
||||
select {}
|
||||
|
||||
} else {
|
||||
logger.Fatal("Cron expression is not valid: %s", bkConfig.cronExpression)
|
||||
utils.Fatal("Cron expression is not valid: %s", bkConfig.cronExpression)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,7 +187,7 @@ func startMultiBackup(bkConfig *BackupConfig, configFile string) {
|
||||
func BackupDatabase(db *dbConfig, backupFileName string, disableCompression bool) {
|
||||
storagePath = os.Getenv("STORAGE_PATH")
|
||||
|
||||
logger.Info("Starting database backup...")
|
||||
utils.Info("Starting database backup...")
|
||||
|
||||
err := os.Setenv("MYSQL_PWD", db.dbPassword)
|
||||
if err != nil {
|
||||
@@ -196,7 +195,7 @@ func BackupDatabase(db *dbConfig, backupFileName string, disableCompression bool
|
||||
}
|
||||
testDatabaseConnection(db)
|
||||
// Backup Database database
|
||||
logger.Info("Backing up database...")
|
||||
utils.Info("Backing up database...")
|
||||
|
||||
// Verify is compression is disabled
|
||||
if disableCompression {
|
||||
@@ -209,26 +208,26 @@ func BackupDatabase(db *dbConfig, backupFileName string, disableCompression bool
|
||||
)
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
logger.Fatal(err.Error())
|
||||
utils.Fatal(err.Error())
|
||||
}
|
||||
|
||||
// save output
|
||||
file, err := os.Create(filepath.Join(tmpPath, backupFileName))
|
||||
if err != nil {
|
||||
logger.Fatal(err.Error())
|
||||
utils.Fatal(err.Error())
|
||||
}
|
||||
defer func(file *os.File) {
|
||||
err := file.Close()
|
||||
if err != nil {
|
||||
logger.Fatal(err.Error())
|
||||
utils.Fatal(err.Error())
|
||||
}
|
||||
}(file)
|
||||
|
||||
_, err = file.Write(output)
|
||||
if err != nil {
|
||||
logger.Fatal(err.Error())
|
||||
utils.Fatal(err.Error())
|
||||
}
|
||||
logger.Info("Database has been backed up")
|
||||
utils.Info("Database has been backed up")
|
||||
|
||||
} else {
|
||||
// Execute mysqldump
|
||||
@@ -250,12 +249,12 @@ func BackupDatabase(db *dbConfig, backupFileName string, disableCompression bool
|
||||
if err := gzipCmd.Wait(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
logger.Info("Database has been backed up")
|
||||
utils.Info("Database has been backed up")
|
||||
|
||||
}
|
||||
}
|
||||
func localBackup(db *dbConfig, config *BackupConfig) {
|
||||
logger.Info("Backup database to local storage")
|
||||
utils.Info("Backup database to local storage")
|
||||
startTime = time.Now().Format(utils.TimeFormat())
|
||||
BackupDatabase(db, config.backupFileName, disableCompression)
|
||||
finalFileName := config.backupFileName
|
||||
@@ -265,19 +264,19 @@ func localBackup(db *dbConfig, config *BackupConfig) {
|
||||
}
|
||||
fileInfo, err := os.Stat(filepath.Join(tmpPath, finalFileName))
|
||||
if err != nil {
|
||||
logger.Error("Error: %s", err)
|
||||
utils.Error("Error: %s", err)
|
||||
}
|
||||
backupSize = fileInfo.Size()
|
||||
logger.Info("Backup name is %s", finalFileName)
|
||||
utils.Info("Backup name is %s", finalFileName)
|
||||
localStorage := local.NewStorage(local.Config{
|
||||
LocalPath: tmpPath,
|
||||
RemotePath: storagePath,
|
||||
})
|
||||
err = localStorage.Copy(finalFileName)
|
||||
if err != nil {
|
||||
logger.Fatal("Error copying backup file: %s", err)
|
||||
utils.Fatal("Error copying backup file: %s", err)
|
||||
}
|
||||
logger.Info("Backup saved in %s", filepath.Join(storagePath, finalFileName))
|
||||
utils.Info("Backup saved in %s", filepath.Join(storagePath, finalFileName))
|
||||
// Send notification
|
||||
utils.NotifySuccess(&utils.NotificationData{
|
||||
File: finalFileName,
|
||||
@@ -292,40 +291,40 @@ func localBackup(db *dbConfig, config *BackupConfig) {
|
||||
if config.prune {
|
||||
err = localStorage.Prune(config.backupRetention)
|
||||
if err != nil {
|
||||
logger.Fatal("Error deleting old backup from %s storage: %s ", config.storage, err)
|
||||
utils.Fatal("Error deleting old backup from %s storage: %s ", config.storage, err)
|
||||
}
|
||||
|
||||
}
|
||||
// Delete temp
|
||||
deleteTemp()
|
||||
logger.Info("Backup completed successfully")
|
||||
utils.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 {
|
||||
logger.Fatal("Error reading backup file: %s ", err)
|
||||
utils.Fatal("Error reading backup file: %s ", err)
|
||||
}
|
||||
if config.usingKey {
|
||||
logger.Info("Encrypting backup using public key...")
|
||||
utils.Info("Encrypting backup using public key...")
|
||||
pubKey, err := os.ReadFile(config.publicKey)
|
||||
if err != nil {
|
||||
logger.Fatal("Error reading public key: %s ", err)
|
||||
utils.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 {
|
||||
logger.Fatal("Error encrypting backup file: %v ", err)
|
||||
utils.Fatal("Error encrypting backup file: %v ", err)
|
||||
}
|
||||
logger.Info("Encrypting backup using public key...done")
|
||||
utils.Info("Encrypting backup using public key...done")
|
||||
|
||||
} else if config.passphrase != "" {
|
||||
logger.Info("Encrypting backup using passphrase...")
|
||||
utils.Info("Encrypting backup using passphrase...")
|
||||
err := encryptor.Encrypt(backupFile, outputFile, config.passphrase)
|
||||
if err != nil {
|
||||
logger.Fatal("error during encrypting backup %v", err)
|
||||
utils.Fatal("error during encrypting backup %v", err)
|
||||
}
|
||||
logger.Info("Encrypting backup using passphrase...done")
|
||||
utils.Info("Encrypting backup using passphrase...done")
|
||||
|
||||
}
|
||||
|
||||
@@ -22,11 +22,10 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
package internal
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jkaninda/mysql-bkup/pkg/logger"
|
||||
"github.com/jkaninda/mysql-bkup/utils"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
@@ -121,8 +120,8 @@ func initDbConfig(cmd *cobra.Command) *dbConfig {
|
||||
|
||||
err := utils.CheckEnvVars(dbHVars)
|
||||
if err != nil {
|
||||
logger.Error("Please make sure all required environment variables for database are set")
|
||||
logger.Fatal("Error checking environment variables: %s", err)
|
||||
utils.Error("Please make sure all required environment variables for database are set")
|
||||
utils.Fatal("Error checking environment variables: %s", err)
|
||||
}
|
||||
return &dConf
|
||||
}
|
||||
@@ -164,8 +163,8 @@ func loadFtpConfig() *FTPConfig {
|
||||
fConfig.remotePath = os.Getenv("REMOTE_PATH")
|
||||
err := utils.CheckEnvVars(ftpVars)
|
||||
if err != nil {
|
||||
logger.Error("Please make sure all required environment variables for FTP are set")
|
||||
logger.Fatal("Error missing environment variables: %s", err)
|
||||
utils.Error("Please make sure all required environment variables for FTP are set")
|
||||
utils.Fatal("Error missing environment variables: %s", err)
|
||||
}
|
||||
return &fConfig
|
||||
}
|
||||
@@ -178,8 +177,8 @@ func loadAzureConfig() *AzureConfig {
|
||||
|
||||
err := utils.CheckEnvVars(azureVars)
|
||||
if err != nil {
|
||||
logger.Error("Please make sure all required environment variables for Azure Blob storage are set")
|
||||
logger.Fatal("Error missing environment variables: %s", err)
|
||||
utils.Error("Please make sure all required environment variables for Azure Blob storage are set")
|
||||
utils.Fatal("Error missing environment variables: %s", err)
|
||||
}
|
||||
return &aConfig
|
||||
}
|
||||
@@ -206,8 +205,8 @@ func initAWSConfig() *AWSConfig {
|
||||
aConfig.forcePathStyle = forcePathStyle
|
||||
err = utils.CheckEnvVars(awsVars)
|
||||
if err != nil {
|
||||
logger.Error("Please make sure all required environment variables for AWS S3 are set")
|
||||
logger.Fatal("Error checking environment variables: %s", err)
|
||||
utils.Error("Please make sure all required environment variables for AWS S3 are set")
|
||||
utils.Fatal("Error checking environment variables: %s", err)
|
||||
}
|
||||
return &aConfig
|
||||
}
|
||||
@@ -304,8 +303,8 @@ func initTargetDbConfig() *targetDbConfig {
|
||||
|
||||
err := utils.CheckEnvVars(tdbRVars)
|
||||
if err != nil {
|
||||
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)
|
||||
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)
|
||||
}
|
||||
return &tdbConfig
|
||||
}
|
||||
@@ -22,12 +22,11 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
package internal
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/jkaninda/mysql-bkup/pkg/logger"
|
||||
"github.com/jkaninda/mysql-bkup/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
"os"
|
||||
@@ -44,7 +43,7 @@ func intro() {
|
||||
|
||||
// copyToTmp copy file to temporary directory
|
||||
func deleteTemp() {
|
||||
logger.Info("Deleting %s ...", tmpPath)
|
||||
utils.Info("Deleting %s ...", tmpPath)
|
||||
err := filepath.Walk(tmpPath, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -60,9 +59,9 @@ func deleteTemp() {
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
logger.Error("Error deleting files: %v", err)
|
||||
utils.Error("Error deleting files: %v", err)
|
||||
} else {
|
||||
logger.Info("Deleting %s ... done", tmpPath)
|
||||
utils.Info("Deleting %s ... done", tmpPath)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +71,7 @@ func testDatabaseConnection(db *dbConfig) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
logger.Info("Connecting to %s database ...", db.dbName)
|
||||
utils.Info("Connecting to %s database ...", db.dbName)
|
||||
cmd := exec.Command("mysql", "-h", db.dbHost, "-P", db.dbPort, "-u", db.dbUserName, db.dbName, "-e", "quit")
|
||||
// Capture the output
|
||||
var out bytes.Buffer
|
||||
@@ -80,10 +79,10 @@ func testDatabaseConnection(db *dbConfig) {
|
||||
cmd.Stderr = &out
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
logger.Fatal("Error testing database connection: %v\nOutput: %s", err, out.String())
|
||||
utils.Fatal("Error testing database connection: %v\nOutput: %s", err, out.String())
|
||||
|
||||
}
|
||||
logger.Info("Successfully connected to %s database", db.dbName)
|
||||
utils.Info("Successfully connected to %s database", db.dbName)
|
||||
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
package logger
|
||||
|
||||
/*
|
||||
MIT License
|
||||
|
||||
# Copyright (c) 2023 Jonas Kaninda
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
const traceLog = "trace"
|
||||
@@ -22,18 +22,18 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
package internal
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jkaninda/mysql-bkup/pkg/logger"
|
||||
"github.com/jkaninda/mysql-bkup/utils"
|
||||
"github.com/spf13/cobra"
|
||||
"time"
|
||||
)
|
||||
|
||||
func StartMigration(cmd *cobra.Command) {
|
||||
intro()
|
||||
logger.Info("Starting database migration...")
|
||||
utils.Info("Starting database migration...")
|
||||
// Get DB config
|
||||
dbConf = initDbConfig(cmd)
|
||||
targetDbConf = initTargetDbConfig()
|
||||
@@ -53,8 +53,8 @@ func StartMigration(cmd *cobra.Command) {
|
||||
// Backup source Database
|
||||
BackupDatabase(dbConf, backupFileName, true)
|
||||
// Restore source database into target database
|
||||
logger.Info("Restoring [%s] database into [%s] database...", dbConf.dbName, targetDbConf.targetDbName)
|
||||
utils.Info("Restoring [%s] database into [%s] database...", dbConf.dbName, targetDbConf.targetDbName)
|
||||
RestoreDatabase(&newDbConfig, conf)
|
||||
logger.Info("[%s] database has been restored into [%s] database", dbConf.dbName, targetDbConf.targetDbName)
|
||||
logger.Info("Database migration completed.")
|
||||
utils.Info("[%s] database has been restored into [%s] database", dbConf.dbName, targetDbConf.targetDbName)
|
||||
utils.Info("Database migration completed.")
|
||||
}
|
||||
@@ -22,13 +22,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
package internal
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jkaninda/go-storage/pkg/ftp"
|
||||
"github.com/jkaninda/go-storage/pkg/ssh"
|
||||
"github.com/jkaninda/mysql-bkup/pkg/logger"
|
||||
"github.com/jkaninda/mysql-bkup/utils"
|
||||
|
||||
"os"
|
||||
@@ -37,7 +36,7 @@ import (
|
||||
)
|
||||
|
||||
func sshBackup(db *dbConfig, config *BackupConfig) {
|
||||
logger.Info("Backup database to Remote server")
|
||||
utils.Info("Backup database to Remote server")
|
||||
startTime = time.Now().Format(utils.TimeFormat())
|
||||
// Backup database
|
||||
BackupDatabase(db, config.backupFileName, disableCompression)
|
||||
@@ -46,11 +45,11 @@ func sshBackup(db *dbConfig, config *BackupConfig) {
|
||||
encryptBackup(config)
|
||||
finalFileName = fmt.Sprintf("%s.%s", config.backupFileName, "gpg")
|
||||
}
|
||||
logger.Info("Uploading backup archive to remote storage ... ")
|
||||
logger.Info("Backup name is %s", finalFileName)
|
||||
utils.Info("Uploading backup archive to remote storage ... ")
|
||||
utils.Info("Backup name is %s", finalFileName)
|
||||
sshConfig, err := loadSSHConfig()
|
||||
if err != nil {
|
||||
logger.Fatal("Error loading ssh config: %s", err)
|
||||
utils.Fatal("Error loading ssh config: %s", err)
|
||||
}
|
||||
|
||||
sshStorage, err := ssh.NewStorage(ssh.Config{
|
||||
@@ -62,34 +61,34 @@ func sshBackup(db *dbConfig, config *BackupConfig) {
|
||||
LocalPath: tmpPath,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Fatal("Error creating SSH storage: %s", err)
|
||||
utils.Fatal("Error creating SSH storage: %s", err)
|
||||
}
|
||||
err = sshStorage.Copy(finalFileName)
|
||||
if err != nil {
|
||||
logger.Fatal("Error copying backup file: %s", err)
|
||||
utils.Fatal("Error copying backup file: %s", err)
|
||||
}
|
||||
// Get backup info
|
||||
fileInfo, err := os.Stat(filepath.Join(tmpPath, finalFileName))
|
||||
if err != nil {
|
||||
logger.Error("Error: %s", err)
|
||||
utils.Error("Error: %s", err)
|
||||
}
|
||||
backupSize = fileInfo.Size()
|
||||
logger.Info("Backup saved in %s", filepath.Join(config.remotePath, finalFileName))
|
||||
utils.Info("Backup saved in %s", filepath.Join(config.remotePath, finalFileName))
|
||||
|
||||
// Delete backup file from tmp folder
|
||||
err = utils.DeleteFile(filepath.Join(tmpPath, finalFileName))
|
||||
if err != nil {
|
||||
logger.Error("Error deleting file: %v", err)
|
||||
utils.Error("Error deleting file: %v", err)
|
||||
|
||||
}
|
||||
if config.prune {
|
||||
err := sshStorage.Prune(config.backupRetention)
|
||||
if err != nil {
|
||||
logger.Fatal("Error deleting old backup from %s storage: %s ", config.storage, err)
|
||||
utils.Fatal("Error deleting old backup from %s storage: %s ", config.storage, err)
|
||||
}
|
||||
|
||||
}
|
||||
logger.Info("Uploading backup archive to remote storage ... done ")
|
||||
utils.Info("Uploading backup archive to remote storage ... done ")
|
||||
// Send notification
|
||||
utils.NotifySuccess(&utils.NotificationData{
|
||||
File: finalFileName,
|
||||
@@ -102,11 +101,11 @@ func sshBackup(db *dbConfig, config *BackupConfig) {
|
||||
})
|
||||
// Delete temp
|
||||
deleteTemp()
|
||||
logger.Info("Backup completed successfully")
|
||||
utils.Info("Backup completed successfully")
|
||||
|
||||
}
|
||||
func ftpBackup(db *dbConfig, config *BackupConfig) {
|
||||
logger.Info("Backup database to the remote FTP server")
|
||||
utils.Info("Backup database to the remote FTP server")
|
||||
startTime = time.Now().Format(utils.TimeFormat())
|
||||
|
||||
// Backup database
|
||||
@@ -116,8 +115,8 @@ func ftpBackup(db *dbConfig, config *BackupConfig) {
|
||||
encryptBackup(config)
|
||||
finalFileName = fmt.Sprintf("%s.%s", config.backupFileName, "gpg")
|
||||
}
|
||||
logger.Info("Uploading backup archive to the remote FTP server ... ")
|
||||
logger.Info("Backup name is %s", finalFileName)
|
||||
utils.Info("Uploading backup archive to the remote FTP server ... ")
|
||||
utils.Info("Backup name is %s", finalFileName)
|
||||
ftpConfig := loadFtpConfig()
|
||||
ftpStorage, err := ftp.NewStorage(ftp.Config{
|
||||
Host: ftpConfig.host,
|
||||
@@ -128,34 +127,34 @@ func ftpBackup(db *dbConfig, config *BackupConfig) {
|
||||
LocalPath: tmpPath,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Fatal("Error creating SSH storage: %s", err)
|
||||
utils.Fatal("Error creating SSH storage: %s", err)
|
||||
}
|
||||
err = ftpStorage.Copy(finalFileName)
|
||||
if err != nil {
|
||||
logger.Fatal("Error copying backup file: %s", err)
|
||||
utils.Fatal("Error copying backup file: %s", err)
|
||||
}
|
||||
logger.Info("Backup saved in %s", filepath.Join(config.remotePath, finalFileName))
|
||||
utils.Info("Backup saved in %s", filepath.Join(config.remotePath, finalFileName))
|
||||
// Get backup info
|
||||
fileInfo, err := os.Stat(filepath.Join(tmpPath, finalFileName))
|
||||
if err != nil {
|
||||
logger.Error("Error: %s", err)
|
||||
utils.Error("Error: %s", err)
|
||||
}
|
||||
backupSize = fileInfo.Size()
|
||||
// Delete backup file from tmp folder
|
||||
err = utils.DeleteFile(filepath.Join(tmpPath, finalFileName))
|
||||
if err != nil {
|
||||
logger.Error("Error deleting file: %v", err)
|
||||
utils.Error("Error deleting file: %v", err)
|
||||
|
||||
}
|
||||
if config.prune {
|
||||
err := ftpStorage.Prune(config.backupRetention)
|
||||
if err != nil {
|
||||
logger.Fatal("Error deleting old backup from %s storage: %s ", config.storage, err)
|
||||
utils.Fatal("Error deleting old backup from %s storage: %s ", config.storage, err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
logger.Info("Uploading backup archive to the remote FTP server ... done ")
|
||||
utils.Info("Uploading backup archive to the remote FTP server ... done ")
|
||||
|
||||
// Send notification
|
||||
utils.NotifySuccess(&utils.NotificationData{
|
||||
@@ -169,13 +168,13 @@ func ftpBackup(db *dbConfig, config *BackupConfig) {
|
||||
})
|
||||
// Delete temp
|
||||
deleteTemp()
|
||||
logger.Info("Backup completed successfully")
|
||||
utils.Info("Backup completed successfully")
|
||||
}
|
||||
func remoteRestore(db *dbConfig, conf *RestoreConfig) {
|
||||
logger.Info("Restore database from remote server")
|
||||
utils.Info("Restore database from remote server")
|
||||
sshConfig, err := loadSSHConfig()
|
||||
if err != nil {
|
||||
logger.Fatal("Error loading ssh config: %s", err)
|
||||
utils.Fatal("Error loading ssh config: %s", err)
|
||||
}
|
||||
|
||||
sshStorage, err := ssh.NewStorage(ssh.Config{
|
||||
@@ -188,16 +187,16 @@ func remoteRestore(db *dbConfig, conf *RestoreConfig) {
|
||||
LocalPath: tmpPath,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Fatal("Error creating SSH storage: %s", err)
|
||||
utils.Fatal("Error creating SSH storage: %s", err)
|
||||
}
|
||||
err = sshStorage.CopyFrom(conf.file)
|
||||
if err != nil {
|
||||
logger.Fatal("Error copying backup file: %s", err)
|
||||
utils.Fatal("Error copying backup file: %s", err)
|
||||
}
|
||||
RestoreDatabase(db, conf)
|
||||
}
|
||||
func ftpRestore(db *dbConfig, conf *RestoreConfig) {
|
||||
logger.Info("Restore database from FTP server")
|
||||
utils.Info("Restore database from FTP server")
|
||||
ftpConfig := loadFtpConfig()
|
||||
ftpStorage, err := ftp.NewStorage(ftp.Config{
|
||||
Host: ftpConfig.host,
|
||||
@@ -208,11 +207,11 @@ func ftpRestore(db *dbConfig, conf *RestoreConfig) {
|
||||
LocalPath: tmpPath,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Fatal("Error creating SSH storage: %s", err)
|
||||
utils.Fatal("Error creating SSH storage: %s", err)
|
||||
}
|
||||
err = ftpStorage.CopyFrom(conf.file)
|
||||
if err != nil {
|
||||
logger.Fatal("Error copying backup file: %s", err)
|
||||
utils.Fatal("Error copying backup file: %s", err)
|
||||
}
|
||||
RestoreDatabase(db, conf)
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// Package internal /
|
||||
package internal
|
||||
package pkg
|
||||
|
||||
/*
|
||||
MIT License
|
||||
@@ -27,7 +27,6 @@ SOFTWARE.
|
||||
import (
|
||||
"github.com/jkaninda/encryptor"
|
||||
"github.com/jkaninda/go-storage/pkg/local"
|
||||
"github.com/jkaninda/mysql-bkup/pkg/logger"
|
||||
"github.com/jkaninda/mysql-bkup/utils"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
@@ -56,14 +55,14 @@ func StartRestore(cmd *cobra.Command) {
|
||||
}
|
||||
}
|
||||
func localRestore(dbConf *dbConfig, restoreConf *RestoreConfig) {
|
||||
logger.Info("Restore database from local")
|
||||
utils.Info("Restore database from local")
|
||||
localStorage := local.NewStorage(local.Config{
|
||||
RemotePath: storagePath,
|
||||
LocalPath: tmpPath,
|
||||
})
|
||||
err := localStorage.CopyFrom(restoreConf.file)
|
||||
if err != nil {
|
||||
logger.Fatal("Error copying backup file: %s", err)
|
||||
utils.Fatal("Error copying backup file: %s", err)
|
||||
}
|
||||
RestoreDatabase(dbConf, restoreConf)
|
||||
|
||||
@@ -72,41 +71,41 @@ func localRestore(dbConf *dbConfig, restoreConf *RestoreConfig) {
|
||||
// RestoreDatabase restore database
|
||||
func RestoreDatabase(db *dbConfig, conf *RestoreConfig) {
|
||||
if conf.file == "" {
|
||||
logger.Fatal("Error, file required")
|
||||
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))
|
||||
if err != nil {
|
||||
logger.Fatal("Error reading backup file: %s ", err)
|
||||
utils.Fatal("Error reading backup file: %s ", err)
|
||||
}
|
||||
|
||||
if extension == ".gpg" {
|
||||
|
||||
if conf.usingKey {
|
||||
logger.Info("Decrypting backup using private key...")
|
||||
logger.Warn("Backup decryption using a private key is not fully supported")
|
||||
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 {
|
||||
logger.Fatal("Error reading public key: %s ", err)
|
||||
utils.Fatal("Error reading public key: %s ", err)
|
||||
}
|
||||
err = encryptor.DecryptWithPrivateKey(rFile, outputFile, prKey, conf.passphrase)
|
||||
if err != nil {
|
||||
logger.Fatal("error during decrypting backup %v", err)
|
||||
utils.Fatal("error during decrypting backup %v", err)
|
||||
}
|
||||
logger.Info("Decrypting backup using private key...done")
|
||||
utils.Info("Decrypting backup using private key...done")
|
||||
} else {
|
||||
if conf.passphrase == "" {
|
||||
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.")
|
||||
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 {
|
||||
logger.Info("Decrypting backup using passphrase...")
|
||||
utils.Info("Decrypting backup using passphrase...")
|
||||
// decryptWithGPG file
|
||||
err := encryptor.Decrypt(rFile, outputFile, conf.passphrase)
|
||||
if err != nil {
|
||||
logger.Fatal("Error decrypting file %s %v", file, err)
|
||||
utils.Fatal("Error decrypting file %s %v", file, err)
|
||||
}
|
||||
logger.Info("Decrypting backup using passphrase...done")
|
||||
utils.Info("Decrypting backup using passphrase...done")
|
||||
// Update file name
|
||||
conf.file = RemoveLastExtension(file)
|
||||
}
|
||||
@@ -120,7 +119,7 @@ func RestoreDatabase(db *dbConfig, conf *RestoreConfig) {
|
||||
return
|
||||
}
|
||||
testDatabaseConnection(db)
|
||||
logger.Info("Restoring database...")
|
||||
utils.Info("Restoring database...")
|
||||
|
||||
extension := filepath.Ext(filepath.Join(tmpPath, conf.file))
|
||||
// Restore from compressed file / .sql.gz
|
||||
@@ -128,10 +127,10 @@ func RestoreDatabase(db *dbConfig, conf *RestoreConfig) {
|
||||
str := "zcat " + filepath.Join(tmpPath, conf.file) + " | mysql -h " + db.dbHost + " -P " + db.dbPort + " -u " + db.dbUserName + " " + db.dbName
|
||||
_, err := exec.Command("sh", "-c", str).Output()
|
||||
if err != nil {
|
||||
logger.Fatal("Error, in restoring the database %v", err)
|
||||
utils.Fatal("Error, in restoring the database %v", err)
|
||||
}
|
||||
logger.Info("Restoring database... done")
|
||||
logger.Info("Database has been restored")
|
||||
utils.Info("Restoring database... done")
|
||||
utils.Info("Database has been restored")
|
||||
// Delete temp
|
||||
deleteTemp()
|
||||
|
||||
@@ -140,17 +139,17 @@ func RestoreDatabase(db *dbConfig, conf *RestoreConfig) {
|
||||
str := "cat " + filepath.Join(tmpPath, conf.file) + " | mysql -h " + db.dbHost + " -P " + db.dbPort + " -u " + db.dbUserName + " " + db.dbName
|
||||
_, err := exec.Command("sh", "-c", str).Output()
|
||||
if err != nil {
|
||||
logger.Fatal("Error in restoring the database %v", err)
|
||||
utils.Fatal("Error in restoring the database %v", err)
|
||||
}
|
||||
logger.Info("Restoring database... done")
|
||||
logger.Info("Database has been restored")
|
||||
utils.Info("Restoring database... done")
|
||||
utils.Info("Database has been restored")
|
||||
// Delete temp
|
||||
deleteTemp()
|
||||
} else {
|
||||
logger.Fatal("Unknown file extension %s", extension)
|
||||
utils.Fatal("Unknown file extension %s", extension)
|
||||
}
|
||||
|
||||
} else {
|
||||
logger.Fatal("File not found in %s", filepath.Join(tmpPath, conf.file))
|
||||
utils.Fatal("File not found in %s", filepath.Join(tmpPath, conf.file))
|
||||
}
|
||||
}
|
||||
@@ -22,12 +22,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
package internal
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jkaninda/go-storage/pkg/s3"
|
||||
"github.com/jkaninda/mysql-bkup/pkg/logger"
|
||||
"github.com/jkaninda/mysql-bkup/utils"
|
||||
|
||||
"os"
|
||||
@@ -37,7 +36,7 @@ import (
|
||||
|
||||
func s3Backup(db *dbConfig, config *BackupConfig) {
|
||||
|
||||
logger.Info("Backup database to s3 storage")
|
||||
utils.Info("Backup database to s3 storage")
|
||||
startTime = time.Now().Format(utils.TimeFormat())
|
||||
// Backup database
|
||||
BackupDatabase(db, config.backupFileName, disableCompression)
|
||||
@@ -46,12 +45,12 @@ func s3Backup(db *dbConfig, config *BackupConfig) {
|
||||
encryptBackup(config)
|
||||
finalFileName = fmt.Sprintf("%s.%s", config.backupFileName, "gpg")
|
||||
}
|
||||
logger.Info("Uploading backup archive to remote storage S3 ... ")
|
||||
utils.Info("Uploading backup archive to remote storage S3 ... ")
|
||||
awsConfig := initAWSConfig()
|
||||
if config.remotePath == "" {
|
||||
config.remotePath = awsConfig.remotePath
|
||||
}
|
||||
logger.Info("Backup name is %s", finalFileName)
|
||||
utils.Info("Backup name is %s", finalFileName)
|
||||
s3Storage, err := s3.NewStorage(s3.Config{
|
||||
Endpoint: awsConfig.endpoint,
|
||||
Bucket: awsConfig.bucket,
|
||||
@@ -64,16 +63,16 @@ func s3Backup(db *dbConfig, config *BackupConfig) {
|
||||
LocalPath: tmpPath,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Fatal("Error creating s3 storage: %s", err)
|
||||
utils.Fatal("Error creating s3 storage: %s", err)
|
||||
}
|
||||
err = s3Storage.Copy(finalFileName)
|
||||
if err != nil {
|
||||
logger.Fatal("Error copying backup file: %s", err)
|
||||
utils.Fatal("Error copying backup file: %s", err)
|
||||
}
|
||||
// Get backup info
|
||||
fileInfo, err := os.Stat(filepath.Join(tmpPath, finalFileName))
|
||||
if err != nil {
|
||||
logger.Error("Error: %s", err)
|
||||
utils.Error("Error: %s", err)
|
||||
}
|
||||
backupSize = fileInfo.Size()
|
||||
|
||||
@@ -87,11 +86,11 @@ func s3Backup(db *dbConfig, config *BackupConfig) {
|
||||
if config.prune {
|
||||
err := s3Storage.Prune(config.backupRetention)
|
||||
if err != nil {
|
||||
logger.Fatal("Error deleting old backup from %s storage: %s ", config.storage, err)
|
||||
utils.Fatal("Error deleting old backup from %s storage: %s ", config.storage, err)
|
||||
}
|
||||
}
|
||||
logger.Info("Backup saved in %s", filepath.Join(config.remotePath, finalFileName))
|
||||
logger.Info("Uploading backup archive to remote storage S3 ... done ")
|
||||
utils.Info("Backup saved in %s", filepath.Join(config.remotePath, finalFileName))
|
||||
utils.Info("Uploading backup archive to remote storage S3 ... done ")
|
||||
// Send notification
|
||||
utils.NotifySuccess(&utils.NotificationData{
|
||||
File: finalFileName,
|
||||
@@ -104,11 +103,11 @@ func s3Backup(db *dbConfig, config *BackupConfig) {
|
||||
})
|
||||
// Delete temp
|
||||
deleteTemp()
|
||||
logger.Info("Backup completed successfully")
|
||||
utils.Info("Backup completed successfully")
|
||||
|
||||
}
|
||||
func s3Restore(db *dbConfig, conf *RestoreConfig) {
|
||||
logger.Info("Restore database from s3")
|
||||
utils.Info("Restore database from s3")
|
||||
awsConfig := initAWSConfig()
|
||||
if conf.remotePath == "" {
|
||||
conf.remotePath = awsConfig.remotePath
|
||||
@@ -125,11 +124,11 @@ func s3Restore(db *dbConfig, conf *RestoreConfig) {
|
||||
LocalPath: tmpPath,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Fatal("Error creating s3 storage: %s", err)
|
||||
utils.Fatal("Error creating s3 storage: %s", err)
|
||||
}
|
||||
err = s3Storage.CopyFrom(conf.file)
|
||||
if err != nil {
|
||||
logger.Fatal("Error download file from S3 storage: %s", err)
|
||||
utils.Fatal("Error download file from S3 storage: %s", err)
|
||||
}
|
||||
RestoreDatabase(db, conf)
|
||||
}
|
||||
@@ -22,7 +22,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
package internal
|
||||
package pkg
|
||||
|
||||
const tmpPath = "/tmp/backup"
|
||||
const gpgHome = "/config/gnupg"
|
||||
@@ -32,3 +32,4 @@ const BackupExample = "backup --dbname database --disable-compression\n" +
|
||||
const MainExample = "mysql-bkup backup --dbname database --disable-compression\n" +
|
||||
"backup --dbname database --storage s3 --path /custom-path\n" +
|
||||
"restore --dbname database --file db_20231219_022941.sql.gz"
|
||||
const traceLog = "trace"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package logger
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -54,7 +54,13 @@ func Error(msg string, args ...interface{}) {
|
||||
|
||||
func Fatal(msg string, args ...interface{}) {
|
||||
log.SetOutput(os.Stdout)
|
||||
// Format message if there are additional arguments
|
||||
formattedMessage := msg
|
||||
if len(args) > 0 {
|
||||
formattedMessage = fmt.Sprintf(msg, args...)
|
||||
}
|
||||
logWithCaller("ERROR", msg, args...)
|
||||
NotifyError(formattedMessage)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/go-mail/mail"
|
||||
"github.com/jkaninda/mysql-bkup/pkg/logger"
|
||||
"html/template"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -56,7 +55,7 @@ func parseTemplate[T any](data T, fileName string) (string, error) {
|
||||
}
|
||||
|
||||
func SendEmail(subject, body string) error {
|
||||
logger.Info("Start sending email notification....")
|
||||
Info("Start sending email notification....")
|
||||
config := loadMailConfig()
|
||||
emails := strings.Split(config.MailTo, ",")
|
||||
m := mail.NewMessage()
|
||||
@@ -68,16 +67,16 @@ func SendEmail(subject, body string) error {
|
||||
d.TLSConfig = &tls.Config{InsecureSkipVerify: config.SkipTls}
|
||||
|
||||
if err := d.DialAndSend(m); err != nil {
|
||||
logger.Error("Error could not send email : %v", err)
|
||||
Error("Error could not send email : %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Info("Email notification has been sent")
|
||||
Info("Email notification has been sent")
|
||||
return nil
|
||||
|
||||
}
|
||||
func sendMessage(msg string) error {
|
||||
|
||||
logger.Info("Sending Telegram notification... ")
|
||||
Info("Sending Telegram notification... ")
|
||||
chatId := os.Getenv("TG_CHAT_ID")
|
||||
body, _ := json.Marshal(map[string]string{
|
||||
"chat_id": chatId,
|
||||
@@ -97,11 +96,11 @@ func sendMessage(msg string) error {
|
||||
}
|
||||
code := response.StatusCode
|
||||
if code == 200 {
|
||||
logger.Info("Telegram notification has been sent")
|
||||
Info("Telegram notification has been sent")
|
||||
return nil
|
||||
} else {
|
||||
body, _ := io.ReadAll(response.Body)
|
||||
logger.Error("Error could not send message, error: %s", string(body))
|
||||
Error("Error could not send message, error: %s", string(body))
|
||||
return fmt.Errorf("error could not send message %s", string(body))
|
||||
}
|
||||
|
||||
@@ -126,11 +125,11 @@ func NotifySuccess(notificationData *NotificationData) {
|
||||
if err == nil {
|
||||
body, err := parseTemplate(*notificationData, "email.tmpl")
|
||||
if err != nil {
|
||||
logger.Error("Could not parse email template: %v", err)
|
||||
Error("Could not parse email template: %v", err)
|
||||
}
|
||||
err = SendEmail(fmt.Sprintf("✅ Database Backup Notification – %s", notificationData.Database), body)
|
||||
if err != nil {
|
||||
logger.Error("Could not send email: %v", err)
|
||||
Error("Could not send email: %v", err)
|
||||
}
|
||||
}
|
||||
// Telegram notification
|
||||
@@ -138,12 +137,12 @@ func NotifySuccess(notificationData *NotificationData) {
|
||||
if err == nil {
|
||||
message, err := parseTemplate(*notificationData, "telegram.tmpl")
|
||||
if err != nil {
|
||||
logger.Error("Could not parse telegram template: %v", err)
|
||||
Error("Could not parse telegram template: %v", err)
|
||||
}
|
||||
|
||||
err = sendMessage(message)
|
||||
if err != nil {
|
||||
logger.Error("Could not send Telegram message: %v", err)
|
||||
Error("Could not send Telegram message: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -170,11 +169,11 @@ func NotifyError(error string) {
|
||||
BackupReference: os.Getenv("BACKUP_REFERENCE"),
|
||||
}, "email-error.tmpl")
|
||||
if err != nil {
|
||||
logger.Error("Could not parse error template: %v", err)
|
||||
Error("Could not parse error template: %v", err)
|
||||
}
|
||||
err = SendEmail("🔴 Urgent: Database Backup Failure Notification", body)
|
||||
if err != nil {
|
||||
logger.Error("Could not send email: %v", err)
|
||||
Error("Could not send email: %v", err)
|
||||
}
|
||||
}
|
||||
// Telegram notification
|
||||
@@ -186,13 +185,13 @@ func NotifyError(error string) {
|
||||
BackupReference: os.Getenv("BACKUP_REFERENCE"),
|
||||
}, "telegram-error.tmpl")
|
||||
if err != nil {
|
||||
logger.Error("Could not parse error template: %v", err)
|
||||
Error("Could not parse error template: %v", err)
|
||||
|
||||
}
|
||||
|
||||
err = sendMessage(message)
|
||||
if err != nil {
|
||||
logger.Error("Could not send telegram message: %v", err)
|
||||
Error("Could not send telegram message: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jkaninda/mysql-bkup/pkg/logger"
|
||||
"github.com/robfig/cron/v3"
|
||||
"github.com/spf13/cobra"
|
||||
"io"
|
||||
@@ -36,7 +35,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var Version = "development"
|
||||
var Version = ""
|
||||
|
||||
// FileExists checks if the file does exist
|
||||
func FileExists(filename string) bool {
|
||||
@@ -112,7 +111,7 @@ func CopyFile(src, dst string) error {
|
||||
}
|
||||
func ChangePermission(filePath string, mod int) {
|
||||
if err := os.Chmod(filePath, fs.FileMode(mod)); err != nil {
|
||||
logger.Fatal("Error changing permissions of %s: %v\n", filePath, err)
|
||||
Fatal("Error changing permissions of %s: %v\n", filePath, err)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -174,7 +173,7 @@ func GetEnvVariable(envName, oldEnvName string) string {
|
||||
if err != nil {
|
||||
return value
|
||||
}
|
||||
logger.Warn("%s is deprecated, please use %s instead! ", oldEnvName, envName)
|
||||
Warn("%s is deprecated, please use %s instead! ", oldEnvName, envName)
|
||||
}
|
||||
}
|
||||
return value
|
||||
@@ -221,7 +220,7 @@ func GetIntEnv(envName string) int {
|
||||
}
|
||||
ret, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
logger.Error("Error: %v", err)
|
||||
Error("Error: %v", err)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
@@ -246,7 +245,7 @@ func CronNextTime(cronExpr string) time.Time {
|
||||
// Parse the cron expression
|
||||
schedule, err := cron.ParseStandard(cronExpr)
|
||||
if err != nil {
|
||||
logger.Error("Error parsing cron expression: %s", err)
|
||||
Error("Error parsing cron expression: %s", err)
|
||||
return time.Time{}
|
||||
}
|
||||
// Get the current time
|
||||
|
||||
Reference in New Issue
Block a user