Compare commits

..

7 Commits
v1.0 ... v1.2.0

16 changed files with 240 additions and 108 deletions

32
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
name: Build
on:
push:
branches: ['develop']
env:
BUILDKIT_IMAGE: jkaninda/mysql-bkup
jobs:
docker:
runs-on: ubuntu-latest
steps:
-
name: Set up QEMU
uses: docker/setup-qemu-action@v3
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
-
name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
uses: docker/build-push-action@v3
with:
push: true
file: "./docker/Dockerfile"
platforms: linux/amd64,linux/arm64,linux/arm/v7
tags: |
"${{env.BUILDKIT_IMAGE}}:develop-${{ github.sha }}"

View File

@@ -27,7 +27,7 @@ docker-run-scheduled: docker-build
docker-run-scheduled-s3: docker-build
docker run --rm --network web --user 1000:1000 --name mysql-bkup -v "./backup:/backup" -e "DB_HOST=${DB_HOST}" -e "DB_NAME=${DB_NAME}" -e "DB_USERNAME=${DB_USERNAME}" -e "DB_PASSWORD=${DB_PASSWORD}" -e "ACCESS_KEY=${ACCESS_KEY}" -e "SECRET_KEY=${SECRET_KEY}" -e "BUCKET_NAME=${BUCKET_NAME}" -e "S3_ENDPOINT=${S3_ENDPOINT}" -e "GPG_PASSPHRASE=${GPG_PASSPHRASE}" jkaninda/mysql-bkup bkup backup --storage s3 --mode scheduled --path /custom-path --period "* * * * *"
docker run --rm --network web --name mysql-bkup -v "./backup:/backup" -e "DB_HOST=${DB_HOST}" -e "DB_NAME=${DB_NAME}" -e "DB_USERNAME=${DB_USERNAME}" -e "DB_PASSWORD=${DB_PASSWORD}" -e "ACCESS_KEY=${ACCESS_KEY}" -e "SECRET_KEY=${SECRET_KEY}" -e "BUCKET_NAME=${BUCKET_NAME}" -e "S3_ENDPOINT=${AWS_S3_ENDPOINT}" -e "GPG_PASSPHRASE=${GPG_PASSPHRASE}" jkaninda/mysql-bkup bkup backup --storage s3 --mode scheduled --path /custom-path --period "* * * * *"
docker-run-s3: docker-build
docker run --rm --network web --name mysql-bkup -e "DB_HOST=${DB_HOST}" -e "DB_NAME=${DB_NAME}" -e "DB_USERNAME=${DB_USERNAME}" -e "DB_PASSWORD=${DB_PASSWORD}" -e "ACCESS_KEY=${ACCESS_KEY}" -e "SECRET_KEY=${SECRET_KEY}" -e "AWS_S3_BUCKET_NAME=${AWS_S3_BUCKET_NAME}" -e "AWS_S3_ENDPOINT=${AWS_S3_ENDPOINT}" -e "AWS_REGION=eu2" -e "GPG_PASSPHRASE=${GPG_PASSPHRASE}" jkaninda/mysql-bkup bkup backup --storage s3 --path /custom-path
@@ -37,7 +37,7 @@ docker-restore-s3: docker-build
docker run --rm --network web --name mysql-bkup -e "DB_HOST=${DB_HOST}" -e "DB_NAME=${DB_NAME}" -e "DB_USERNAME=${DB_USERNAME}" -e "DB_PASSWORD=${DB_PASSWORD}" -e "ACCESS_KEY=${ACCESS_KEY}" -e "SECRET_KEY=${SECRET_KEY}" -e "BUCKET_NAME=${AWS_S3_BUCKET_NAME}" -e "S3_ENDPOINT=${AWS_S3_ENDPOINT}" -e "AWS_REGION=eu2" -e "GPG_PASSPHRASE=${GPG_PASSPHRASE}" jkaninda/mysql-bkup bkup restore --storage s3 -f ${FILE_NAME} --path /custom-path
docker-run-ssh: docker-build
docker run --rm --network web -v "${SSH_IDENTIFY_FILE_LOCAL}:" --name mysql-bkup -e "DB_HOST=${DB_HOST}" -e "DB_NAME=${DB_NAME}" -e "DB_USERNAME=${DB_USERNAME}" -e "DB_PASSWORD=${DB_PASSWORD}" -e "SSH_USER=${SSH_USER}" -e "SSH_HOST_NAME=${SSH_HOST_NAME}" -e "SSH_REMOTE_PATH=${SSH_REMOTE_PATH}" -e "SSH_PASSWORD=${SSH_PASSWORD}" -e "SSH_PORT=${SSH_PORT}" -e "SSH_IDENTIFY_FILE=${SSH_IDENTIFY_FILE}" -e "GPG_PASSPHRASE=${GPG_PASSPHRASE}" jkaninda/mysql-bkup bkup backup --storage ssh
docker run --rm --network web --name mysql-bkup -e "DB_HOST=${DB_HOST}" -e "DB_NAME=${DB_NAME}" -e "DB_USERNAME=${DB_USERNAME}" -e "DB_PASSWORD=${DB_PASSWORD}" -e "SSH_USER=${SSH_USER}" -e "SSH_HOST_NAME=${SSH_HOST_NAME}" -e "SSH_REMOTE_PATH=${SSH_REMOTE_PATH}" -e "SSH_PASSWORD=${SSH_PASSWORD}" -e "SSH_PORT=${SSH_PORT}" -e "SSH_IDENTIFY_FILE=${SSH_IDENTIFY_FILE}" -e "GPG_PASSPHRASE=${GPG_PASSPHRASE}" jkaninda/mysql-bkup bkup backup --storage ssh
docker-restore-ssh: docker-build
docker run --rm --network web --name mysql-bkup -e "DB_HOST=${DB_HOST}" -e "DB_NAME=${DB_NAME}" -e "DB_USERNAME=${DB_USERNAME}" -e "DB_PASSWORD=${DB_PASSWORD}" -e "SSH_USER=${SSH_USER}" -e "SSH_HOST_NAME=${SSH_HOST_NAME}" -e "SSH_REMOTE_PATH=${SSH_REMOTE_PATH}" -e "SSH_PASSWORD=${SSH_PASSWORD}" -e "SSH_PORT=${SSH_PORT}" -e "GPG_PASSPHRASE=${GPG_PASSPHRASE}" -e "SSH_IDENTIFY_FILE=${SSH_IDENTIFY_FILE}" jkaninda/mysql-bkup bkup restore --storage ssh -f ${FILE_NAME}

View File

@@ -21,6 +21,7 @@ ENV AWS_S3_BUCKET_NAME=""
ENV AWS_ACCESS_KEY=""
ENV AWS_SECRET_KEY=""
ENV AWS_REGION="us-west-2"
ENV AWS_S3_PATH=""
ENV AWS_DISABLE_SSL="false"
ENV GPG_PASSPHRASE=""
ENV SSH_USER=""

View File

@@ -32,7 +32,7 @@ services:
environment:
- DB_PORT=3306
- DB_HOST=mysql
- DB_NAME=database
#- DB_NAME=database
- DB_USERNAME=username
- DB_PASSWORD=password
## SSH config
@@ -69,7 +69,7 @@ services:
command:
- /bin/sh
- -c
- mysql-bkup backup -d database --storage s3 --mode scheduled --period "0 1 * * *"
- mysql-bkup backup -d database --storage ssh --mode scheduled --period "0 1 * * *"
volumes:
- ./id_ed25519:/tmp/id_ed25519"
environment:
@@ -117,7 +117,7 @@ spec:
command:
- /bin/sh
- -c
- mysql-bkup backup -s s3 --path /custom_path
- mysql-bkup backup -s ssh
env:
- name: DB_PORT
value: "3306"
@@ -141,6 +141,6 @@ spec:
- name: AWS_ACCESS_KEY
value: "xxxx"
- name: SSH_IDENTIFY_FILE
value: "/home/jkaninda/backups"
restartPolicy: OnFailure
value: "/tmp/id_ed25519"
restartPolicy: Never
```

View File

@@ -48,4 +48,54 @@ services:
- web
networks:
web:
```
## Restore on Kubernetes
### Simple Kubernetes CronJob usage:
```yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: bkup-job
spec:
schedule: "0 1 * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: mysql-bkup
image: jkaninda/mysql-bkup
command:
- /bin/sh
- -c
- mysql-bkup restore -s s3 --path /custom_path -f store_20231219_022941.sql.gz
env:
- name: DB_PORT
value: "3306"
- name: DB_HOST
value: ""
- name: DB_NAME
value: ""
- name: DB_USERNAME
value: ""
# Please use secret!
- name: DB_PASSWORD
value: ""
- name: AWS_S3_ENDPOINT
value: "https://s3.amazonaws.com"
- name: AWS_S3_BUCKET_NAME
value: "xxx"
- name: AWS_REGION
value: "us-west-2"
- name: AWS_ACCESS_KEY
value: "xxxx"
- name: AWS_SECRET_KEY
value: "xxxx"
- name: AWS_DISABLE_SSL
value: "false"
restartPolicy: OnFailure
```

View File

@@ -47,4 +47,52 @@ services:
- web
networks:
web:
```
## Restore on Kubernetes
Simple Kubernetes CronJob usage:
```yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: bkup-job
spec:
schedule: "0 1 * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: mysql-bkup
image: jkaninda/mysql-bkup
command:
- /bin/sh
- -c
- mysql-bkup restore -s ssh -f store_20231219_022941.sql.gz
env:
- name: DB_PORT
value: "3306"
- name: DB_HOST
value: ""
- name: DB_NAME
value: ""
- name: DB_USERNAME
value: ""
# Please use secret!
- name: DB_PASSWORD
value: ""
- name: SSH_HOST_NAME
value: ""
- name: SSH_PORT
value: "22"
- name: SSH_USER
value: "xxx"
- name: SSH_REMOTE_PATH
value: "/home/jkaninda/backups"
- name: AWS_ACCESS_KEY
value: "xxxx"
- name: SSH_IDENTIFY_FILE
value: "/tmp/id_ed25519"
restartPolicy: Never
```

1
go.mod
View File

@@ -10,7 +10,6 @@ require (
github.com/hpcloud/tail v1.0.0
github.com/spf13/cobra v1.8.0
golang.org/x/crypto v0.18.0
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
)
require (

View File

@@ -25,7 +25,6 @@ func StartBackup(cmd *cobra.Command) {
utils.GetEnv(cmd, "period", "SCHEDULE_PERIOD")
//Get flag value and set env
s3Path := utils.GetEnv(cmd, "path", "AWS_S3_PATH")
remotePath := utils.GetEnv(cmd, "path", "SSH_REMOTE_PATH")
storage = utils.GetEnv(cmd, "storage", "STORAGE")
file = utils.GetEnv(cmd, "file", "FILE_NAME")
@@ -35,6 +34,8 @@ func StartBackup(cmd *cobra.Command) {
executionMode, _ = cmd.Flags().GetString("mode")
dbName = os.Getenv("DB_NAME")
gpqPassphrase := os.Getenv("GPG_PASSPHRASE")
_ = utils.GetEnv(cmd, "path", "AWS_S3_PATH")
//
if gpqPassphrase != "" {
encryption = true
@@ -49,7 +50,7 @@ func StartBackup(cmd *cobra.Command) {
if executionMode == "default" {
switch storage {
case "s3":
s3Backup(backupFileName, s3Path, disableCompression, prune, backupRetention, encryption)
s3Backup(backupFileName, disableCompression, prune, backupRetention, encryption)
case "local":
localBackup(backupFileName, disableCompression, prune, backupRetention, encryption)
case "ssh", "remote":
@@ -61,7 +62,7 @@ func StartBackup(cmd *cobra.Command) {
}
} else if executionMode == "scheduled" {
scheduledMode()
scheduledMode(storage)
} else {
utils.Fatal("Error, unknown execution mode!")
}
@@ -69,14 +70,15 @@ func StartBackup(cmd *cobra.Command) {
}
// Run in scheduled mode
func scheduledMode() {
func scheduledMode(storage string) {
fmt.Println()
fmt.Println("**********************************")
fmt.Println(" Starting MySQL Bkup... ")
fmt.Println("***********************************")
utils.Info("Running in Scheduled mode")
utils.Info("Execution period ", os.Getenv("SCHEDULE_PERIOD"))
utils.Info("Execution period %s", os.Getenv("SCHEDULE_PERIOD"))
utils.Info("Storage type %s ", storage)
//Test database connexion
utils.TestDatabaseConnection()
@@ -123,13 +125,6 @@ func BackupDatabase(backupFileName string, disableCompression bool) {
dbPort = os.Getenv("DB_PORT")
storagePath = os.Getenv("STORAGE_PATH")
// dbHVars Required environment variables for database
var dbHVars = []string{
"DB_HOST",
"DB_PASSWORD",
"DB_USERNAME",
"DB_NAME",
}
err := utils.CheckEnvVars(dbHVars)
if err != nil {
utils.Error("Please make sure all required environment variables for database are set")
@@ -210,8 +205,9 @@ func localBackup(backupFileName string, disableCompression bool, prune bool, bac
}
}
func s3Backup(backupFileName string, s3Path string, disableCompression bool, prune bool, backupRetention int, encrypt bool) {
func s3Backup(backupFileName string, disableCompression bool, prune bool, backupRetention int, encrypt bool) {
bucket := utils.GetEnvVariable("AWS_S3_BUCKET_NAME", "BUCKET_NAME")
s3Path := utils.GetEnvVariable("AWS_S3_PATH", "S3_PATH")
utils.Info("Backup database to s3 storage")
//Backup database
BackupDatabase(backupFileName, disableCompression)

View File

@@ -1,7 +1,6 @@
package pkg
import (
"fmt"
"github.com/jkaninda/mysql-bkup/utils"
"os"
"os/exec"
@@ -16,7 +15,6 @@ func Decrypt(inputFile string, passphrase string) error {
err := cmd.Run()
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
return err
}
@@ -32,7 +30,6 @@ func Encrypt(inputFile string, passphrase string) error {
err := cmd.Run()
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
return err
}

View File

@@ -68,13 +68,7 @@ func RestoreDatabase(file string) {
if file == "" {
utils.Fatal("Error, file required")
}
// dbHVars Required environment variables for database
var dbHVars = []string{
"DB_HOST",
"DB_PASSWORD",
"DB_USERNAME",
"DB_NAME",
}
err := utils.CheckEnvVars(dbHVars)
if err != nil {
utils.Error("Please make sure all required environment variables for database are set")
@@ -90,7 +84,7 @@ func RestoreDatabase(file string) {
//Decrypt file
err := Decrypt(filepath.Join(tmpPath, file), gpgPassphrase)
if err != nil {
utils.Fatal("Error decrypting file ", file, err)
utils.Fatal("Error decrypting file %s %v", file, err)
}
//Update file name
file = RemoveLastExtension(file)
@@ -99,11 +93,6 @@ func RestoreDatabase(file string) {
}
if utils.FileExists(fmt.Sprintf("%s/%s", tmpPath, file)) {
err := os.Setenv("mysqlPASSWORD", dbPassword)
if err != nil {
return
}
utils.TestDatabaseConnection()
extension := filepath.Ext(fmt.Sprintf("%s/%s", tmpPath, file))
@@ -112,7 +101,7 @@ func RestoreDatabase(file string) {
str := "zcat " + fmt.Sprintf("%s/%s", tmpPath, file) + " | mysql -h " + os.Getenv("DB_HOST") + " -P " + os.Getenv("DB_PORT") + " -u " + os.Getenv("DB_USERNAME") + " --password=" + os.Getenv("DB_PASSWORD") + " " + os.Getenv("DB_NAME")
_, err := exec.Command("bash", "-c", str).Output()
if err != nil {
utils.Fatal(fmt.Sprintf("Error, in restoring the database %s", err))
utils.Fatal("Error, in restoring the database %v", err)
}
utils.Done("Database has been restored")

View File

@@ -8,7 +8,6 @@ import (
"github.com/bramvdbogaerde/go-scp/auth"
"github.com/jkaninda/mysql-bkup/utils"
"golang.org/x/crypto/ssh"
"golang.org/x/exp/slog"
"os"
"path/filepath"
)
@@ -20,16 +19,9 @@ func CopyToRemote(fileName, remotePath string) error {
sshPort := os.Getenv("SSH_PORT")
sshIdentifyFile := os.Getenv("SSH_IDENTIFY_FILE")
// SSSHVars Required environment variables for SSH remote server storage
var sshHVars = []string{
"SSH_USER",
"SSH_REMOTE_PATH",
"SSH_HOST_NAME",
"SSH_PORT",
}
err := utils.CheckEnvVars(sshHVars)
if err != nil {
slog.Error(fmt.Sprintf("Error checking environment variables\n: %s", err))
utils.Error("Error checking environment variables: %s", err)
os.Exit(1)
}
@@ -39,9 +31,9 @@ func CopyToRemote(fileName, remotePath string) error {
} else {
if sshPassword == "" {
return errors.New("SSH_PASSWORD environment variable is required if SSH_IDENTIFY_FILE is empty\n")
return errors.New("SSH_PASSWORD environment variable is required if SSH_IDENTIFY_FILE is empty")
}
slog.Warn("Accessing the remote server using password, password is not recommended\n")
utils.Warn("Accessing the remote server using password, password is not recommended")
clientConfig, _ = auth.PasswordKey(sshUser, sshPassword, ssh.InsecureIgnoreHostKey())
}
@@ -51,7 +43,7 @@ func CopyToRemote(fileName, remotePath string) error {
// Connect to the remote server
err = client.Connect()
if err != nil {
return errors.New("Couldn't establish a connection to the remote server\n")
return errors.New("Couldn't establish a connection to the remote server")
}
// Open a file
@@ -77,6 +69,12 @@ func CopyFromRemote(fileName, remotePath string) error {
sshPort := os.Getenv("SSH_PORT")
sshIdentifyFile := os.Getenv("SSH_IDENTIFY_FILE")
err := utils.CheckEnvVars(sshHVars)
if err != nil {
utils.Error("Error checking environment variables\n: %s", err)
os.Exit(1)
}
clientConfig, _ := auth.PasswordKey(sshUser, sshPassword, ssh.InsecureIgnoreHostKey())
if sshIdentifyFile != "" && utils.FileExists(sshIdentifyFile) {
clientConfig, _ = auth.PrivateKey(sshUser, sshIdentifyFile, ssh.InsecureIgnoreHostKey())
@@ -85,7 +83,7 @@ func CopyFromRemote(fileName, remotePath string) error {
if sshPassword == "" {
return errors.New("SSH_PASSWORD environment variable is required if SSH_IDENTIFY_FILE is empty\n")
}
slog.Warn("Accessing the remote server using password, password is not recommended\n")
utils.Warn("Accessing the remote server using password, password is not recommended")
clientConfig, _ = auth.PasswordKey(sshUser, sshPassword, ssh.InsecureIgnoreHostKey())
}
@@ -93,7 +91,7 @@ func CopyFromRemote(fileName, remotePath string) error {
client := scp.NewClient(fmt.Sprintf("%s:%s", sshHostName, sshPort), &clientConfig)
// Connect to the remote server
err := client.Connect()
err = client.Connect()
if err != nil {
return errors.New("Couldn't establish a connection to the remote server\n")
}

View File

@@ -22,19 +22,10 @@ func CreateCrontabScript(disableCompression bool, storage string) {
disableC = "--disable-compression"
}
var scriptContent string
if storage == "s3" {
scriptContent = fmt.Sprintf(`#!/usr/bin/env bash
scriptContent := fmt.Sprintf(`#!/usr/bin/env bash
set -e
bkup backup --dbname %s --port %s --storage s3 --path %s %v
`, os.Getenv("DB_NAME"), os.Getenv("DB_PORT"), os.Getenv("S3_PATH"), disableC)
} else {
scriptContent = fmt.Sprintf(`#!/usr/bin/env bash
set -e
bkup backup --dbname %s --port %s %v
`, os.Getenv("DB_NAME"), os.Getenv("DB_PORT"), disableC)
}
bkup backup --dbname %s --port %s --storage %s %v
`, os.Getenv("DB_NAME"), os.Getenv("DB_PORT"), storage, disableC)
if err := utils.WriteToFile(backupCronFile, scriptContent); err != nil {
utils.Fatal("Error writing to %s: %v\n", backupCronFile, err)

View File

@@ -19,3 +19,19 @@ var (
disableCompression = false
encryption = false
)
// dbHVars Required environment variables for database
var dbHVars = []string{
"DB_HOST",
"DB_PASSWORD",
"DB_USERNAME",
"DB_NAME",
}
// sshHVars Required environment variables for SSH remote server storage
var sshHVars = []string{
"SSH_USER",
"SSH_REMOTE_PATH",
"SSH_HOST_NAME",
"SSH_PORT",
}

56
utils/logger.go Normal file
View File

@@ -0,0 +1,56 @@
package utils
import (
"fmt"
"os"
"time"
)
var currentTime = time.Now().Format("2006/01/02 15:04:05")
func Info(msg string, args ...any) {
formattedMessage := fmt.Sprintf(msg, args...)
if len(args) == 0 {
fmt.Printf("%s INFO: %s\n", currentTime, msg)
} else {
fmt.Printf("%s INFO: %s\n", currentTime, formattedMessage)
}
}
// Warn warning message
func Warn(msg string, args ...any) {
formattedMessage := fmt.Sprintf(msg, args...)
if len(args) == 0 {
fmt.Printf("%s WARN: %s\n", currentTime, msg)
} else {
fmt.Printf("%s WARN: %s\n", currentTime, formattedMessage)
}
}
func Error(msg string, args ...any) {
formattedMessage := fmt.Sprintf(msg, args...)
if len(args) == 0 {
fmt.Printf("%s ERROR: %s\n", currentTime, msg)
} else {
fmt.Printf("%s ERROR: %s\n", currentTime, formattedMessage)
}
}
func Done(msg string, args ...any) {
formattedMessage := fmt.Sprintf(msg, args...)
if len(args) == 0 {
fmt.Printf("%s INFO: %s\n", currentTime, msg)
} else {
fmt.Printf("%s INFO: %s\n", currentTime, formattedMessage)
}
}
// Fatal logs an error message and exits the program
func Fatal(msg string, args ...any) {
// Fatal logs an error message and exits the program.
formattedMessage := fmt.Sprintf(msg, args...)
if len(args) == 0 {
fmt.Printf("%s ERROR: %s\n", currentTime, msg)
} else {
fmt.Printf("%s ERROR: %s\n", currentTime, formattedMessage)
}
os.Exit(1)
}

View File

@@ -8,7 +8,6 @@ import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go/service/s3/s3manager"
"golang.org/x/exp/slog"
"log"
"net/http"
"os"
@@ -43,8 +42,7 @@ func CreateSession() (*session.Session, error) {
err = CheckEnvVars(awsVars)
if err != nil {
slog.Error(fmt.Sprintf("Error checking environment variables\n: %s", err))
os.Exit(1)
Fatal("Error checking environment variables\n: %s", err)
}
// S3 Config
s3Config := &aws.Config{

View File

@@ -10,51 +10,12 @@ import (
"bytes"
"fmt"
"github.com/spf13/cobra"
"golang.org/x/exp/slog"
"io"
"io/fs"
"os"
"os/exec"
)
func Info(msg string, args ...any) {
if len(args) == 0 {
slog.Info(msg)
} else {
slog.Info(fmt.Sprintf(msg, args...))
}
}
func Worn(msg string, args ...any) {
if len(args) == 0 {
slog.Warn(msg)
} else {
slog.Warn(fmt.Sprintf(msg, args...))
}
}
func Error(msg string, args ...any) {
if len(args) == 0 {
slog.Error(msg)
} else {
slog.Error(fmt.Sprintf(msg, args...))
}
}
func Done(msg string, args ...any) {
if len(args) == 0 {
slog.Info(msg)
} else {
slog.Info(fmt.Sprintf(msg, args...))
}
}
func Fatal(msg string, args ...any) {
// Fatal logs an error message and exits the program.
if len(args) == 0 {
slog.Error(msg)
} else {
slog.Error(fmt.Sprintf(msg, args...))
}
os.Exit(1)
}
func FileExists(filename string) bool {
info, err := os.Stat(filename)
if os.IsNotExist(err) {
@@ -150,7 +111,7 @@ func TestDatabaseConnection() {
cmd.Stderr = &out
err := cmd.Run()
if err != nil {
slog.Error(fmt.Sprintf("Error testing database connection: %v\nOutput: %s\n", err, out.String()))
Error("Error testing database connection: %v\nOutput: %s", err, out.String())
os.Exit(1)
}
@@ -196,7 +157,7 @@ func GetEnvVariable(envName, oldEnvName string) string {
if err != nil {
return value
}
Worn("%s is deprecated, please use %s instead!\n", oldEnvName, envName)
Warn("%s is deprecated, please use %s instead!", oldEnvName, envName)
}
}