diff --git a/Dockerfile b/Dockerfile index 0d0ced5..02c1848 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,9 +3,10 @@ FROM alpine:3.17 RUN apk add --no-cache \ bash~=5 \ consul~=1.14 \ - nomad~=1.4 \ mariadb-client~=10.6 \ mariadb-connector-c~=3.3 \ + nomad~=1.4 \ + postgresql15-client~=15.3 \ rclone~=1.60 \ redis~=7.0 \ restic~=0.14 \ diff --git a/itest/bootstrap-tests.sh b/itest/bootstrap-tests.sh index e4dbf64..3acffd1 100755 --- a/itest/bootstrap-tests.sh +++ b/itest/bootstrap-tests.sh @@ -8,8 +8,8 @@ echo "Hello" > /data/test.txt touch /data/test_database.db sqlite3 /data/test_database.db <<-EOF CREATE TABLE test_table ( - id integer PRIMARY KEY, - data text NOT NULL + id INTEGER PRIMARY KEY, + data TEXT NOT NULL ); INSERT INTO test_table(data) @@ -22,10 +22,25 @@ until mysql --host "$MYSQL_HOST" --user "$MYSQL_USER" --password="$MYSQL_PWD" -- done mysql --host "$MYSQL_HOST" --user "$MYSQL_USER" --password="$MYSQL_PWD" main < 0 && t.Database == "" { + return fmt.Errorf( + "task %s is invalid. Must specify a database to use tables: %w", + t.Name, + ErrMissingField, + ) + } + + return nil +} + +//nolint:cyclop +func (t JobTaskPostgres) GetPreTask() ExecutableTask { + command := []string{"pg_dump"} + if t.Database == "" { + command = []string{"pg_dumpall"} + } + + command = append(command, "--file", t.DumpToPath) + + if t.Hostname != "" { + command = append(command, "--host", t.Hostname) + } + + if t.Port != 0 { + command = append(command, "--port", fmt.Sprintf("%d", t.Port)) + } + + if t.Username != "" { + command = append(command, "--username", t.Username) + } + + if t.NoTablespaces { + command = append(command, "--no-tablespaces") + } + + if t.Clean { + command = append(command, "--clean") + } + + if t.Create { + command = append(command, "--create") + } + + for _, table := range t.Tables { + command = append(command, "--table", table) + } + + if t.Database != "" { + command = append(command, t.Database) + } + + env := map[string]string{} + if t.Password != "" { + env["PGPASSWORD"] = t.Password + } + + return JobTaskScript{ + name: t.Name, + env: env, + Cwd: ".", + OnBackup: strings.Join(command, " "), + OnRestore: "", + } +} + +func (t JobTaskPostgres) GetPostTask() ExecutableTask { + command := []string{"psql"} + + if t.Hostname != "" { + command = append(command, "--host", t.Hostname) + } + + if t.Port != 0 { + command = append(command, "--port", fmt.Sprintf("%d", t.Port)) + } + + if t.Username != "" { + command = append(command, "--username", t.Username) + } + + if t.Database != "" { + command = append(command, t.Database) + } + + command = append(command, "<", t.DumpToPath) + + env := map[string]string{} + if t.Password != "" { + env["PGPASSWORD"] = t.Password + } + + return JobTaskScript{ + name: t.Name, + env: env, + Cwd: ".", + OnBackup: "", + OnRestore: strings.Join(command, " "), + } +} + // JobTaskSqlite is a sqlite backup task that performs required pre and post tasks. type JobTaskSqlite struct { Name string `hcl:"name,label"` @@ -299,11 +437,12 @@ func (t *BackupFilesTask) Validate() error { // JobTask represents a single task within a backup job. type JobTask struct { - Name string `hcl:"name,label"` - PreScripts []JobTaskScript `hcl:"pre_script,block"` - PostScripts []JobTaskScript `hcl:"post_script,block"` - MySQL []JobTaskMySQL `hcl:"mysql,block"` - Sqlite []JobTaskSqlite `hcl:"sqlite,block"` + Name string `hcl:"name,label"` + PreScripts []JobTaskScript `hcl:"pre_script,block"` + PostScripts []JobTaskScript `hcl:"post_script,block"` + MySQL []JobTaskMySQL `hcl:"mysql,block"` + Postgres []JobTaskPostgres `hcl:"postgres,block"` + Sqlite []JobTaskSqlite `hcl:"sqlite,block"` } func (t JobTask) Validate() error { diff --git a/tasks_test.go b/tasks_test.go index 185dfba..06c3a77 100644 --- a/tasks_test.go +++ b/tasks_test.go @@ -165,6 +165,28 @@ func TestJobTaskSql(t *testing.T) { preRestore: "", postRestore: "mysql --host host --port 3306 --user user --password=pass db < ./simple.sql", }, + { + name: "psql all", + task: main.JobTaskPostgres{ + Name: "simple", + Hostname: "host", + Port: 6543, + Username: "user", + Password: "pass", + Database: "db", + NoTablespaces: true, + Create: true, + Clean: true, + Tables: []string{"table1", "table2"}, + DumpToPath: "./simple.sql", + }, + validationErr: nil, + preBackup: "pg_dump --file ./simple.sql --host host --port 6543 --username user --no-tablespaces" + + " --clean --create --table table1 --table table2 db", + postBackup: "", + preRestore: "", + postRestore: "psql --host host --port 6543 --username user db < ./simple.sql", + }, // Sqlite { name: "sqlite simple",