2024-11-19 02:54:31 +01:00
|
|
|
/*
|
2024-12-06 03:08:17 +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-12-06 21:27:04 +01:00
|
|
|
|
|
|
|
|
package pkg
|
2024-07-29 23:03:28 +02:00
|
|
|
|
|
|
|
|
import (
|
2024-08-29 21:49:35 +02:00
|
|
|
"bytes"
|
2024-10-08 11:04:46 +02:00
|
|
|
"fmt"
|
2024-07-29 23:03:28 +02:00
|
|
|
"github.com/jkaninda/pg-bkup/utils"
|
2024-10-09 08:32:51 +02:00
|
|
|
"gopkg.in/yaml.v3"
|
2024-07-29 23:03:28 +02:00
|
|
|
"os"
|
2024-08-29 21:49:35 +02:00
|
|
|
"os/exec"
|
2024-07-29 23:03:28 +02:00
|
|
|
"path/filepath"
|
2024-10-13 14:20:43 +02:00
|
|
|
"strings"
|
2024-07-29 23:03:28 +02:00
|
|
|
)
|
|
|
|
|
|
2024-10-09 12:05:37 +02:00
|
|
|
func intro() {
|
2024-12-07 03:47:55 +01:00
|
|
|
fmt.Println("Starting PostgresSQL Backup...")
|
2024-12-06 14:25:08 +01:00
|
|
|
fmt.Printf("Version: %s\n", utils.Version)
|
|
|
|
|
fmt.Println("Copyright (c) 2024 Jonas Kaninda")
|
2024-10-09 12:05:37 +02:00
|
|
|
}
|
|
|
|
|
|
2024-10-08 11:04:46 +02:00
|
|
|
// copyToTmp copy file to temporary directory
|
2024-08-11 09:39:44 +02:00
|
|
|
func deleteTemp() {
|
2024-12-06 21:27:04 +01:00
|
|
|
utils.Info("Deleting %s ...", tmpPath)
|
2024-08-11 09:39:44 +02:00
|
|
|
err := filepath.Walk(tmpPath, func(path string, info os.FileInfo, err error) error {
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
// Check if the current item is a file
|
|
|
|
|
if !info.IsDir() {
|
|
|
|
|
// Delete the file
|
|
|
|
|
err = os.Remove(path)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
2024-12-06 21:27:04 +01:00
|
|
|
utils.Error("Error deleting files: %v", err)
|
2024-08-11 09:39:44 +02:00
|
|
|
} else {
|
2024-12-06 21:27:04 +01:00
|
|
|
utils.Info("Deleting %s ... done", tmpPath)
|
2024-08-11 09:39:44 +02:00
|
|
|
}
|
2024-07-29 23:03:28 +02:00
|
|
|
}
|
2024-08-29 21:49:35 +02:00
|
|
|
|
|
|
|
|
// TestDatabaseConnection tests the database connection
|
|
|
|
|
func testDatabaseConnection(db *dbConfig) {
|
|
|
|
|
|
2024-12-06 21:27:04 +01:00
|
|
|
utils.Info("Connecting to %s database ...", db.dbName)
|
2024-08-29 21:49:35 +02:00
|
|
|
// Test database connection
|
|
|
|
|
query := "SELECT version();"
|
|
|
|
|
|
|
|
|
|
// Set the environment variable for the database password
|
|
|
|
|
err := os.Setenv("PGPASSWORD", db.dbPassword)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
// Prepare the psql command
|
|
|
|
|
cmd := exec.Command("psql",
|
|
|
|
|
"-U", db.dbUserName, // database user
|
|
|
|
|
"-d", db.dbName, // database name
|
|
|
|
|
"-h", db.dbHost, // host
|
|
|
|
|
"-p", db.dbPort, // port
|
|
|
|
|
"-c", query, // SQL command to execute
|
|
|
|
|
)
|
|
|
|
|
// Capture the output
|
|
|
|
|
var out bytes.Buffer
|
|
|
|
|
cmd.Stdout = &out
|
|
|
|
|
cmd.Stderr = &out
|
|
|
|
|
|
|
|
|
|
// Run the command and capture any errors
|
|
|
|
|
err = cmd.Run()
|
|
|
|
|
if err != nil {
|
2024-12-06 21:27:04 +01:00
|
|
|
utils.Fatal("Error running psql command: %v\nOutput: %s\n", err, out.String())
|
2024-08-29 21:49:35 +02:00
|
|
|
return
|
|
|
|
|
}
|
2024-12-06 21:27:04 +01:00
|
|
|
utils.Info("Successfully connected to %s database", db.dbName)
|
2024-08-29 21:49:35 +02:00
|
|
|
|
|
|
|
|
}
|
2024-10-09 08:32:51 +02:00
|
|
|
|
|
|
|
|
// checkPubKeyFile checks gpg public key
|
2024-10-08 11:04:46 +02:00
|
|
|
func checkPubKeyFile(pubKey string) (string, error) {
|
|
|
|
|
// Define possible key file names
|
|
|
|
|
keyFiles := []string{filepath.Join(gpgHome, "public_key.asc"), filepath.Join(gpgHome, "public_key.gpg"), pubKey}
|
|
|
|
|
|
|
|
|
|
// Loop through key file names and check if they exist
|
|
|
|
|
for _, keyFile := range keyFiles {
|
|
|
|
|
if _, err := os.Stat(keyFile); err == nil {
|
|
|
|
|
// File exists
|
|
|
|
|
return keyFile, nil
|
|
|
|
|
} else if os.IsNotExist(err) {
|
|
|
|
|
// File does not exist, continue to the next one
|
|
|
|
|
continue
|
|
|
|
|
} else {
|
|
|
|
|
// An unexpected error occurred
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return an error if neither file exists
|
|
|
|
|
return "", fmt.Errorf("no public key file found")
|
|
|
|
|
}
|
2024-10-09 08:32:51 +02:00
|
|
|
|
|
|
|
|
// checkPrKeyFile checks private key
|
2024-10-08 11:04:46 +02:00
|
|
|
func checkPrKeyFile(prKey string) (string, error) {
|
|
|
|
|
// Define possible key file names
|
|
|
|
|
keyFiles := []string{filepath.Join(gpgHome, "private_key.asc"), filepath.Join(gpgHome, "private_key.gpg"), prKey}
|
|
|
|
|
|
|
|
|
|
// Loop through key file names and check if they exist
|
|
|
|
|
for _, keyFile := range keyFiles {
|
|
|
|
|
if _, err := os.Stat(keyFile); err == nil {
|
|
|
|
|
// File exists
|
|
|
|
|
return keyFile, nil
|
|
|
|
|
} else if os.IsNotExist(err) {
|
|
|
|
|
// File does not exist, continue to the next one
|
|
|
|
|
continue
|
|
|
|
|
} else {
|
|
|
|
|
// An unexpected error occurred
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return an error if neither file exists
|
|
|
|
|
return "", fmt.Errorf("no public key file found")
|
|
|
|
|
}
|
2024-10-09 12:05:37 +02:00
|
|
|
|
|
|
|
|
// readConf reads config file and returns Config
|
2024-10-09 08:32:51 +02:00
|
|
|
func readConf(configFile string) (*Config, error) {
|
|
|
|
|
if utils.FileExists(configFile) {
|
|
|
|
|
buf, err := os.ReadFile(configFile)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c := &Config{}
|
|
|
|
|
err = yaml.Unmarshal(buf, c)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("in file %q: %w", configFile, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return c, err
|
|
|
|
|
}
|
|
|
|
|
return nil, fmt.Errorf("config file %q not found", configFile)
|
|
|
|
|
}
|
2024-10-09 12:05:37 +02:00
|
|
|
|
|
|
|
|
// checkConfigFile checks config files and returns one config file
|
2024-10-09 08:32:51 +02:00
|
|
|
func checkConfigFile(filePath string) (string, error) {
|
|
|
|
|
// Define possible config file names
|
|
|
|
|
configFiles := []string{filepath.Join(workingDir, "config.yaml"), filepath.Join(workingDir, "config.yml"), filePath}
|
|
|
|
|
|
|
|
|
|
// Loop through config file names and check if they exist
|
|
|
|
|
for _, configFile := range configFiles {
|
|
|
|
|
if _, err := os.Stat(configFile); err == nil {
|
|
|
|
|
// File exists
|
|
|
|
|
return configFile, nil
|
|
|
|
|
} else if os.IsNotExist(err) {
|
|
|
|
|
// File does not exist, continue to the next one
|
|
|
|
|
continue
|
|
|
|
|
} else {
|
|
|
|
|
// An unexpected error occurred
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return an error if neither file exists
|
|
|
|
|
return "", fmt.Errorf("no config file found")
|
|
|
|
|
}
|
2024-10-13 14:20:43 +02:00
|
|
|
func RemoveLastExtension(filename string) string {
|
|
|
|
|
if idx := strings.LastIndex(filename, "."); idx != -1 {
|
|
|
|
|
return filename[:idx]
|
|
|
|
|
}
|
|
|
|
|
return filename
|
|
|
|
|
}
|