2024-11-04 09:02:41 +01:00
|
|
|
// Package internal /
|
|
|
|
|
package internal
|
2024-01-19 05:31:30 +01:00
|
|
|
|
2024-12-06 03:25:38 +01:00
|
|
|
/*
|
|
|
|
|
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.
|
|
|
|
|
*/
|
2024-01-19 05:31:30 +01:00
|
|
|
import (
|
2024-10-13 14:33:54 +02:00
|
|
|
"github.com/jkaninda/encryptor"
|
2024-10-23 04:04:38 +02:00
|
|
|
"github.com/jkaninda/go-storage/pkg/ftp"
|
|
|
|
|
"github.com/jkaninda/go-storage/pkg/local"
|
|
|
|
|
"github.com/jkaninda/go-storage/pkg/s3"
|
|
|
|
|
"github.com/jkaninda/go-storage/pkg/ssh"
|
2024-12-06 14:21:55 +01:00
|
|
|
"github.com/jkaninda/mysql-bkup/pkg/logger"
|
2024-01-19 05:31:30 +01:00
|
|
|
"github.com/jkaninda/mysql-bkup/utils"
|
2024-01-20 13:04:39 +01:00
|
|
|
"github.com/spf13/cobra"
|
2024-01-19 05:31:30 +01:00
|
|
|
"os"
|
|
|
|
|
"os/exec"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
)
|
|
|
|
|
|
2024-01-20 13:04:39 +01:00
|
|
|
func StartRestore(cmd *cobra.Command) {
|
2024-09-11 04:37:02 +02:00
|
|
|
intro()
|
2024-09-28 07:26:33 +02:00
|
|
|
dbConf = initDbConfig(cmd)
|
|
|
|
|
restoreConf := initRestoreConfig(cmd)
|
2024-08-30 19:58:12 +02:00
|
|
|
|
2024-09-28 09:18:58 +02:00
|
|
|
switch restoreConf.storage {
|
2024-08-03 16:03:17 +02:00
|
|
|
case "local":
|
2024-10-22 17:21:01 +02:00
|
|
|
localRestore(dbConf, restoreConf)
|
2024-10-03 18:17:48 +02:00
|
|
|
case "s3", "S3":
|
2024-10-08 23:02:46 +02:00
|
|
|
restoreFromS3(dbConf, restoreConf)
|
|
|
|
|
case "ssh", "SSH", "remote":
|
|
|
|
|
restoreFromRemote(dbConf, restoreConf)
|
2024-10-03 18:17:48 +02:00
|
|
|
case "ftp", "FTP":
|
2024-10-08 23:02:46 +02:00
|
|
|
restoreFromFTP(dbConf, restoreConf)
|
2024-08-03 16:03:17 +02:00
|
|
|
default:
|
2024-10-22 17:21:01 +02:00
|
|
|
localRestore(dbConf, restoreConf)
|
2024-08-03 16:03:17 +02:00
|
|
|
}
|
|
|
|
|
}
|
2024-10-22 17:21:01 +02:00
|
|
|
func localRestore(dbConf *dbConfig, restoreConf *RestoreConfig) {
|
2024-12-06 14:21:55 +01:00
|
|
|
logger.Info("Restore database from local")
|
2024-10-22 17:21:01 +02:00
|
|
|
localStorage := local.NewStorage(local.Config{
|
|
|
|
|
RemotePath: storagePath,
|
|
|
|
|
LocalPath: tmpPath,
|
|
|
|
|
})
|
|
|
|
|
err := localStorage.CopyFrom(restoreConf.file)
|
|
|
|
|
if err != nil {
|
2024-12-06 14:21:55 +01:00
|
|
|
logger.Fatal("Error copying backup file: %s", err)
|
2024-10-22 17:21:01 +02:00
|
|
|
}
|
|
|
|
|
RestoreDatabase(dbConf, restoreConf)
|
2024-01-20 13:04:39 +01:00
|
|
|
|
2024-10-22 17:21:01 +02:00
|
|
|
}
|
2024-10-08 23:02:46 +02:00
|
|
|
func restoreFromS3(db *dbConfig, conf *RestoreConfig) {
|
2024-12-06 14:21:55 +01:00
|
|
|
logger.Info("Restore database from s3")
|
2024-10-22 17:21:01 +02:00
|
|
|
awsConfig := initAWSConfig()
|
|
|
|
|
if conf.remotePath == "" {
|
|
|
|
|
conf.remotePath = awsConfig.remotePath
|
|
|
|
|
}
|
|
|
|
|
s3Storage, err := s3.NewStorage(s3.Config{
|
|
|
|
|
Endpoint: awsConfig.endpoint,
|
|
|
|
|
Bucket: awsConfig.bucket,
|
|
|
|
|
AccessKey: awsConfig.accessKey,
|
|
|
|
|
SecretKey: awsConfig.secretKey,
|
|
|
|
|
Region: awsConfig.region,
|
|
|
|
|
DisableSsl: awsConfig.disableSsl,
|
|
|
|
|
ForcePathStyle: awsConfig.forcePathStyle,
|
|
|
|
|
RemotePath: awsConfig.remotePath,
|
|
|
|
|
LocalPath: tmpPath,
|
|
|
|
|
})
|
2024-08-03 16:03:17 +02:00
|
|
|
if err != nil {
|
2024-12-06 14:21:55 +01:00
|
|
|
logger.Fatal("Error creating s3 storage: %s", err)
|
2024-10-22 17:21:01 +02:00
|
|
|
}
|
|
|
|
|
err = s3Storage.CopyFrom(conf.file)
|
|
|
|
|
if err != nil {
|
2024-12-06 14:21:55 +01:00
|
|
|
logger.Fatal("Error download file from S3 storage: %s", err)
|
2024-01-20 13:04:39 +01:00
|
|
|
}
|
2024-10-08 23:02:46 +02:00
|
|
|
RestoreDatabase(db, conf)
|
2024-08-03 16:03:17 +02:00
|
|
|
}
|
2024-10-08 23:02:46 +02:00
|
|
|
func restoreFromRemote(db *dbConfig, conf *RestoreConfig) {
|
2024-12-06 14:21:55 +01:00
|
|
|
logger.Info("Restore database from remote server")
|
2024-10-22 17:21:01 +02:00
|
|
|
sshConfig, err := loadSSHConfig()
|
2024-08-03 16:03:17 +02:00
|
|
|
if err != nil {
|
2024-12-06 14:21:55 +01:00
|
|
|
logger.Fatal("Error loading ssh config: %s", err)
|
2024-10-22 17:21:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sshStorage, err := ssh.NewStorage(ssh.Config{
|
2024-12-06 14:21:55 +01:00
|
|
|
Host: sshConfig.hostName,
|
|
|
|
|
Port: sshConfig.port,
|
|
|
|
|
User: sshConfig.user,
|
|
|
|
|
Password: sshConfig.password,
|
|
|
|
|
IdentifyFile: sshConfig.identifyFile,
|
|
|
|
|
RemotePath: conf.remotePath,
|
|
|
|
|
LocalPath: tmpPath,
|
2024-10-22 17:21:01 +02:00
|
|
|
})
|
|
|
|
|
if err != nil {
|
2024-12-06 14:21:55 +01:00
|
|
|
logger.Fatal("Error creating SSH storage: %s", err)
|
2024-10-22 17:21:01 +02:00
|
|
|
}
|
|
|
|
|
err = sshStorage.CopyFrom(conf.file)
|
|
|
|
|
if err != nil {
|
2024-12-06 14:21:55 +01:00
|
|
|
logger.Fatal("Error copying backup file: %s", err)
|
2024-08-03 16:03:17 +02:00
|
|
|
}
|
2024-10-08 23:02:46 +02:00
|
|
|
RestoreDatabase(db, conf)
|
2024-01-20 13:04:39 +01:00
|
|
|
}
|
2024-10-08 23:02:46 +02:00
|
|
|
func restoreFromFTP(db *dbConfig, conf *RestoreConfig) {
|
2024-12-06 14:21:55 +01:00
|
|
|
logger.Info("Restore database from FTP server")
|
2024-10-22 17:21:01 +02:00
|
|
|
ftpConfig := loadFtpConfig()
|
|
|
|
|
ftpStorage, err := ftp.NewStorage(ftp.Config{
|
|
|
|
|
Host: ftpConfig.host,
|
|
|
|
|
Port: ftpConfig.port,
|
|
|
|
|
User: ftpConfig.user,
|
|
|
|
|
Password: ftpConfig.password,
|
|
|
|
|
RemotePath: conf.remotePath,
|
|
|
|
|
LocalPath: tmpPath,
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
2024-12-06 14:21:55 +01:00
|
|
|
logger.Fatal("Error creating SSH storage: %s", err)
|
2024-10-22 17:21:01 +02:00
|
|
|
}
|
|
|
|
|
err = ftpStorage.CopyFrom(conf.file)
|
2024-09-30 00:40:35 +02:00
|
|
|
if err != nil {
|
2024-12-06 14:21:55 +01:00
|
|
|
logger.Fatal("Error copying backup file: %s", err)
|
2024-09-30 00:40:35 +02:00
|
|
|
}
|
2024-10-08 23:02:46 +02:00
|
|
|
RestoreDatabase(db, conf)
|
2024-09-30 00:40:35 +02:00
|
|
|
}
|
2024-01-20 13:04:39 +01:00
|
|
|
|
2024-01-19 06:56:19 +01:00
|
|
|
// RestoreDatabase restore database
|
2024-10-08 23:02:46 +02:00
|
|
|
func RestoreDatabase(db *dbConfig, conf *RestoreConfig) {
|
|
|
|
|
if conf.file == "" {
|
2024-12-06 14:21:55 +01:00
|
|
|
logger.Fatal("Error, file required")
|
2024-01-20 13:04:39 +01:00
|
|
|
}
|
2024-10-08 23:02:46 +02:00
|
|
|
extension := filepath.Ext(filepath.Join(tmpPath, conf.file))
|
2024-10-13 14:33:54 +02:00
|
|
|
rFile, err := os.ReadFile(filepath.Join(tmpPath, conf.file))
|
|
|
|
|
outputFile := RemoveLastExtension(filepath.Join(tmpPath, conf.file))
|
|
|
|
|
if err != nil {
|
2024-12-06 14:21:55 +01:00
|
|
|
logger.Fatal("Error reading backup file: %s ", err)
|
2024-10-13 14:33:54 +02:00
|
|
|
}
|
|
|
|
|
|
2024-08-03 16:03:17 +02:00
|
|
|
if extension == ".gpg" {
|
2024-01-19 05:31:30 +01:00
|
|
|
|
2024-10-08 23:02:46 +02:00
|
|
|
if conf.usingKey {
|
2024-12-06 14:21:55 +01:00
|
|
|
logger.Info("Decrypting backup using private key...")
|
|
|
|
|
logger.Warn("Backup decryption using a private key is not fully supported")
|
2024-10-13 14:33:54 +02:00
|
|
|
prKey, err := os.ReadFile(conf.privateKey)
|
|
|
|
|
if err != nil {
|
2024-12-06 14:21:55 +01:00
|
|
|
logger.Fatal("Error reading public key: %s ", err)
|
2024-10-13 14:33:54 +02:00
|
|
|
}
|
|
|
|
|
err = encryptor.DecryptWithPrivateKey(rFile, outputFile, prKey, conf.passphrase)
|
2024-08-03 16:03:17 +02:00
|
|
|
if err != nil {
|
2024-12-06 14:21:55 +01:00
|
|
|
logger.Fatal("error during decrypting backup %v", err)
|
2024-10-08 23:02:46 +02:00
|
|
|
}
|
2024-12-06 14:21:55 +01:00
|
|
|
logger.Info("Decrypting backup using private key...done")
|
2024-10-08 23:02:46 +02:00
|
|
|
} else {
|
|
|
|
|
if conf.passphrase == "" {
|
2024-12-06 14:21:55 +01:00
|
|
|
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.")
|
2024-10-08 23:02:46 +02:00
|
|
|
} else {
|
2024-12-06 14:21:55 +01:00
|
|
|
logger.Info("Decrypting backup using passphrase...")
|
|
|
|
|
// decryptWithGPG file
|
2024-10-13 14:33:54 +02:00
|
|
|
err := encryptor.Decrypt(rFile, outputFile, conf.passphrase)
|
2024-10-08 23:02:46 +02:00
|
|
|
if err != nil {
|
2024-12-06 14:21:55 +01:00
|
|
|
logger.Fatal("Error decrypting file %s %v", file, err)
|
2024-10-08 23:02:46 +02:00
|
|
|
}
|
2024-12-06 14:21:55 +01:00
|
|
|
logger.Info("Decrypting backup using passphrase...done")
|
|
|
|
|
// Update file name
|
2024-10-08 23:02:46 +02:00
|
|
|
conf.file = RemoveLastExtension(file)
|
2024-08-03 16:03:17 +02:00
|
|
|
}
|
|
|
|
|
}
|
2024-01-19 05:31:30 +01:00
|
|
|
|
2024-08-03 16:03:17 +02:00
|
|
|
}
|
2024-01-19 05:31:30 +01:00
|
|
|
|
2024-10-09 12:23:14 +02:00
|
|
|
if utils.FileExists(filepath.Join(tmpPath, conf.file)) {
|
2024-10-08 23:02:46 +02:00
|
|
|
err := os.Setenv("MYSQL_PWD", db.dbPassword)
|
2024-09-28 02:25:42 +02:00
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
2024-08-30 19:58:12 +02:00
|
|
|
testDatabaseConnection(db)
|
2024-12-06 14:21:55 +01:00
|
|
|
logger.Info("Restoring database...")
|
2024-01-19 05:31:30 +01:00
|
|
|
|
2024-10-08 23:02:46 +02:00
|
|
|
extension := filepath.Ext(filepath.Join(tmpPath, conf.file))
|
2024-08-03 16:03:17 +02:00
|
|
|
// Restore from compressed file / .sql.gz
|
|
|
|
|
if extension == ".gz" {
|
2024-10-08 23:02:46 +02:00
|
|
|
str := "zcat " + filepath.Join(tmpPath, conf.file) + " | mysql -h " + db.dbHost + " -P " + db.dbPort + " -u " + db.dbUserName + " " + db.dbName
|
2024-09-30 00:40:35 +02:00
|
|
|
_, err := exec.Command("sh", "-c", str).Output()
|
2024-08-03 16:03:17 +02:00
|
|
|
if err != nil {
|
2024-12-06 14:21:55 +01:00
|
|
|
logger.Fatal("Error, in restoring the database %v", err)
|
2024-01-19 05:31:30 +01:00
|
|
|
}
|
2024-12-06 14:21:55 +01:00
|
|
|
logger.Info("Restoring database... done")
|
|
|
|
|
logger.Info("Database has been restored")
|
|
|
|
|
// Delete temp
|
2024-08-11 09:38:31 +02:00
|
|
|
deleteTemp()
|
2024-01-19 05:31:30 +01:00
|
|
|
|
2024-08-03 16:03:17 +02:00
|
|
|
} else if extension == ".sql" {
|
2024-12-06 14:21:55 +01:00
|
|
|
// Restore from sql file
|
2024-10-08 23:02:46 +02:00
|
|
|
str := "cat " + filepath.Join(tmpPath, conf.file) + " | mysql -h " + db.dbHost + " -P " + db.dbPort + " -u " + db.dbUserName + " " + db.dbName
|
2024-09-30 00:40:35 +02:00
|
|
|
_, err := exec.Command("sh", "-c", str).Output()
|
2024-08-03 16:03:17 +02:00
|
|
|
if err != nil {
|
2024-12-06 14:21:55 +01:00
|
|
|
logger.Fatal("Error in restoring the database %v", err)
|
2024-08-03 16:03:17 +02:00
|
|
|
}
|
2024-12-06 14:21:55 +01:00
|
|
|
logger.Info("Restoring database... done")
|
|
|
|
|
logger.Info("Database has been restored")
|
|
|
|
|
// Delete temp
|
2024-08-11 09:38:31 +02:00
|
|
|
deleteTemp()
|
2024-01-19 05:31:30 +01:00
|
|
|
} else {
|
2024-12-06 14:21:55 +01:00
|
|
|
logger.Fatal("Unknown file extension %s", extension)
|
2024-01-19 05:31:30 +01:00
|
|
|
}
|
|
|
|
|
|
2024-08-03 16:03:17 +02:00
|
|
|
} else {
|
2024-12-06 14:21:55 +01:00
|
|
|
logger.Fatal("File not found in %s", filepath.Join(tmpPath, conf.file))
|
2024-01-19 05:31:30 +01:00
|
|
|
}
|
|
|
|
|
}
|