diff --git a/Makefile b/Makefile index 86f4f24..b6757e8 100644 --- a/Makefile +++ b/Makefile @@ -40,6 +40,11 @@ test-integration: kind-cluster-create kind --name backup-restore-sidecar load docker-image ghcr.io/metal-stack/backup-restore-sidecar:latest KUBECONFIG=$(KUBECONFIG) go test $(GO_RUN_ARG) -tags=integration -count 1 -v -p 1 -timeout 30m ./... +.PHONY: test-integration-postgres +test-integration-postgres: kind-cluster-create + kind --name backup-restore-sidecar load docker-image ghcr.io/metal-stack/backup-restore-sidecar:latest + KUBECONFIG=$(KUBECONFIG) go test $(GO_RUN_ARG) -tags=integration -count 1 -v -p 1 -timeout 10m -run "Test_Postgres" ./... + .PHONY: test-integration-valkey test-integration-valkey: kind-cluster-create kind --name backup-restore-sidecar load docker-image ghcr.io/metal-stack/backup-restore-sidecar:latest diff --git a/api/v1/backup.pb.go b/api/v1/backup.pb.go index 01ccf4e..96670c7 100644 --- a/api/v1/backup.pb.go +++ b/api/v1/backup.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.6 +// protoc-gen-go v1.36.10 // protoc (unknown) // source: v1/backup.proto diff --git a/api/v1/database.pb.go b/api/v1/database.pb.go index cd47017..1b1f31b 100644 --- a/api/v1/database.pb.go +++ b/api/v1/database.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.6 +// protoc-gen-go v1.36.10 // protoc (unknown) // source: v1/database.proto diff --git a/api/v1/initializer.pb.go b/api/v1/initializer.pb.go index acb0d95..caa06eb 100644 --- a/api/v1/initializer.pb.go +++ b/api/v1/initializer.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.6 +// protoc-gen-go v1.36.10 // protoc (unknown) // source: v1/initializer.proto diff --git a/cmd/internal/database/postgres/postgres.go b/cmd/internal/database/postgres/postgres.go index ddc5294..365d614 100644 --- a/cmd/internal/database/postgres/postgres.go +++ b/cmd/internal/database/postgres/postgres.go @@ -6,8 +6,11 @@ import ( "fmt" "log/slog" "os" + "os/exec" + "os/user" "path" "strconv" + "syscall" "github.com/metal-stack/backup-restore-sidecar/cmd/internal/utils" "github.com/metal-stack/backup-restore-sidecar/pkg/constants" @@ -56,6 +59,31 @@ func (db *Postgres) Check(_ context.Context) (bool, error) { return true, err } + pgUser, err := user.Lookup("postgres") + if err != nil { + return false, err + } + uid, err := strconv.Atoi(pgUser.Uid) + if err != nil { + return false, err + } + + // calling pg_checksums --pgdaata db.datadir --check + checksumsCommandArgs := []string{"--check", "--pgdata", db.datadir} + db.log.Info("running", "command", postgresChecksumsCmd, "args", checksumsCommandArgs) + cmd := exec.Command(postgresChecksumsCmd, checksumsCommandArgs...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Env = os.Environ() + cmd.SysProcAttr = &syscall.SysProcAttr{ + Credential: &syscall.Credential{Uid: uint32(uid)}, // nolint:gosec + } + err = cmd.Run() + if err != nil { + db.log.Error("pg_checksums reported errors", "args", checksumsCommandArgs, "error", err) + return true, err + } + return false, nil } diff --git a/cmd/internal/database/postgres/upgrade.go b/cmd/internal/database/postgres/upgrade.go index edce54e..ddca96b 100644 --- a/cmd/internal/database/postgres/upgrade.go +++ b/cmd/internal/database/postgres/upgrade.go @@ -25,6 +25,7 @@ const ( postgresConfigCmd = "pg_config" postgresUpgradeCmd = "pg_upgrade" postgresInitDBCmd = "initdb" + postgresChecksumsCmd = "pg_checksums" postgresVersionFile = "PG_VERSION" postgresBinBackupPrefix = "pg-bin-v" ) @@ -80,6 +81,10 @@ func (db *Postgres) Upgrade(ctx context.Context) error { db.log.Error("database is newer than postgres binary, aborting", "database-version", pgVersion, "binary-version", binaryVersionMajor) return fmt.Errorf("database is newer than postgres binary") } + if pgVersion < 17 && binaryVersionMajor == 18 { + db.log.Error("you must not skip a major version before upgrading to v18, skipping upgrade", "old database", pgVersion, "binary-version", binaryVersionMajor) + return nil + } oldPostgresBinDir := path.Join(db.datadir, fmt.Sprintf("%s%d", postgresBinBackupPrefix, pgVersion)) @@ -133,7 +138,13 @@ func (db *Postgres) Upgrade(ctx context.Context) error { } // initdb -D /data/postgres-new - cmd := exec.Command(postgresInitDBCmd, "-D", newDataDirTemp) + // This is enabled by default since v18 + initdbCommandArgs := []string{"-D", newDataDirTemp} + if oldBinaryVersionMajor == 17 { + initdbCommandArgs = append(initdbCommandArgs, "--no-data-checksums") + } + db.log.Info("running", "initdb with args", initdbCommandArgs) + cmd := exec.Command(postgresInitDBCmd, initdbCommandArgs...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Env = os.Environ() @@ -234,6 +245,23 @@ func (db *Postgres) Upgrade(ctx context.Context) error { return fmt.Errorf("unable to rename upgraded datadir to destination, a full restore is required: %w", err) } + // TODO check if we should enable checksum with earlier versions + if oldBinaryVersionMajor == 17 { + checksumsCommandArgs := []string{"--enable", "--pgdata", db.datadir} + db.log.Info("running", "command", postgresChecksumsCmd, "args", checksumsCommandArgs) + cmd := exec.Command(postgresChecksumsCmd, checksumsCommandArgs...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Env = os.Environ() + cmd.SysProcAttr = &syscall.SysProcAttr{ + Credential: &syscall.Credential{Uid: uint32(uid)}, // nolint:gosec + } + err = cmd.Run() + if err != nil { + db.log.Warn("unable to run checksums on new new datadir, ignoring", "error", err) + } + } + db.log.Info("pg_upgrade done and new data in place", "took", time.Since(start).String()) return nil diff --git a/integration/postgres_test.go b/integration/postgres_test.go index 5a55112..afdd62e 100644 --- a/integration/postgres_test.go +++ b/integration/postgres_test.go @@ -56,6 +56,7 @@ func Test_Postgres_Upgrade(t *testing.T) { "postgres:14.18-alpine", "postgres:15-alpine", "postgres:17-alpine", + "postgres:18-alpine", }, }) }