Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions helpers.go

This file was deleted.

53 changes: 53 additions & 0 deletions internal/command/command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package command

import (
"fmt"
"os/exec"
"path/filepath"
"runtime"

"github.com/orgrim/pg_back/internal/logger"
)

func ExecPath(binDir, prog string) string {
binFile := prog
if runtime.GOOS == "windows" {
binFile = fmt.Sprintf("%s.exe", prog)
}

if binDir != "" {
return filepath.Join(binDir, binFile)
}

return binFile
}

func PgToolVersion(logger *logger.LevelLog, binDir, tool string) int {
vs, err := exec.Command(ExecPath(binDir, tool), "--version").Output()
if err != nil {
logger.Warnf("failed to retrieve version of %s: %s", tool, err)
return 0
}

var maj, min, rev, numver int
n, _ := fmt.Sscanf(string(vs), tool+" (PostgreSQL) %d.%d.%d", &maj, &min, &rev)

switch n {
case 3:
// Before PostgreSQL 10, the format si MAJ.MIN.REV
numver = (maj*100+min)*100 + rev
case 2:
// From PostgreSQL 10, the format si MAJ.REV, so the rev ends
// up in min with the scan
numver = maj*10000 + min
default:
// We have the special case of the development version, where the
// format is MAJdevel
fmt.Sscanf(string(vs), tool+" (PostgreSQL) %ddevel", &maj)
numver = maj * 10000
}

logger.Verboseln(tool, "version is:", numver)

return numver
}
45 changes: 45 additions & 0 deletions internal/command/command_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package command

import (
"fmt"
"runtime"
"testing"
)

func TestExecPath(t *testing.T) {
var tests []struct {
dir string
prog string
want string
}

if runtime.GOOS != "windows" {
tests = []struct {
dir string
prog string
want string
}{
{"", "pg_dump", "pg_dump"},
{"/path/to/bin", "prog", "/path/to/bin/prog"},
}
} else {
tests = []struct {
dir string
prog string
want string
}{
{"", "pg_dump", "pg_dump.exe"},
{"C:\\path\\to\\bin", "prog", "C:\\path\\to\\bin\\prog.exe"},
}
}

for i, st := range tests {
t.Run(fmt.Sprintf("%v", i), func(t *testing.T) {
binDir := st.dir
got := ExecPath(binDir, st.prog)
if got != st.want {
t.Errorf("expected %q, got %q\n", st.want, got)
}
})
}
}
29 changes: 15 additions & 14 deletions hook.go → internal/command/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package main
package command

import (
"fmt"
Expand All @@ -32,14 +32,15 @@ import (
"strings"

"github.com/anmitsu/go-shlex"
"github.com/orgrim/pg_back/internal/logger"
)

func hookCommand(cmd string, logPrefix string) error {
func hookCommand(logger *logger.LevelLog, cmd string, logPrefix string) error {
if cmd == "" {
return fmt.Errorf("unable to run an empty command")
}

l.Verboseln("parsing hook command")
logger.Verboseln("parsing hook command")
words, err := shlex.Split(cmd, true)
if err != nil {
return fmt.Errorf("unable to parse hook command: %s", err)
Expand All @@ -48,43 +49,43 @@ func hookCommand(cmd string, logPrefix string) error {
prog := words[0]
args := words[1:]

l.Verboseln("running:", prog, args)
logger.Verboseln("running:", prog, args)
c := exec.Command(prog, args...)
stdoutStderr, err := c.CombinedOutput()
if err != nil {
for line := range strings.SplitSeq(string(stdoutStderr), "\n") {
if line != "" {
l.Errorln(logPrefix, line)
logger.Errorln(logPrefix, line)
}
}
return err
}
if len(stdoutStderr) > 0 {
for line := range strings.SplitSeq(string(stdoutStderr), "\n") {
if line != "" {
l.Infoln(logPrefix, line)
logger.Infoln(logPrefix, line)
}
}
}
return nil
}

func preBackupHook(cmd string) error {
func PreBackupHook(logger *logger.LevelLog, cmd string) error {
if cmd != "" {
l.Infoln("running pre-backup command:", cmd)
if err := hookCommand(cmd, "pre-backup:"); err != nil {
l.Fatalln("hook command failed:", err)
logger.Infoln("running pre-backup command:", cmd)
if err := hookCommand(logger, cmd, "pre-backup:"); err != nil {
logger.Fatalln("hook command failed:", err)
return err
}
}
return nil
}

func postBackupHook(cmd string) {
func PostBackupHook(logger *logger.LevelLog, cmd string) {
if cmd != "" {
l.Infoln("running post-backup command:", cmd)
if err := hookCommand(cmd, "post-backup:"); err != nil {
l.Fatalln("hook command failed:", err)
logger.Infoln("running post-backup command:", cmd)
if err := hookCommand(logger, cmd, "post-backup:"); err != nil {
logger.Fatalln("hook command failed:", err)
os.Exit(1)
}
}
Expand Down
28 changes: 16 additions & 12 deletions hook_test.go → internal/command/hook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

//go:build !windows

package main
package command

import (
"bytes"
Expand All @@ -35,6 +35,8 @@ import (
"regexp"
"strings"
"testing"

"github.com/orgrim/pg_back/internal/logger"
)

func TestHookCommand(t *testing.T) {
Expand All @@ -58,14 +60,14 @@ func TestHookCommand(t *testing.T) {
`^\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} ERROR: test: test\n\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} ERROR: exit status 1\n$`,
},
}

logger := logger.NewLevelLog()
for i, subt := range tests {
t.Run(fmt.Sprintf("%v", i), func(t *testing.T) {
buf := new(bytes.Buffer)
l.logger.SetOutput(buf)
logger.Logger.SetOutput(buf)

if err := hookCommand(subt.cmd, "test:"); err != nil {
l.Errorln(err)
if err := hookCommand(logger, subt.cmd, "test:"); err != nil {
logger.Errorln(err)
}

lines := strings.ReplaceAll(buf.String(), "\r", "")
Expand All @@ -76,7 +78,7 @@ func TestHookCommand(t *testing.T) {
if !matched {
t.Errorf("expected a match of %q, got %q\n", subt.re, lines)
}
l.logger.SetOutput(os.Stderr)
logger.Logger.SetOutput(os.Stderr)
})
}
}
Expand All @@ -99,12 +101,13 @@ func TestPreBackupHook(t *testing.T) {
true,
},
}
logger := logger.NewLevelLog()
for i, subt := range tests {
t.Run(fmt.Sprintf("%v", i), func(t *testing.T) {
buf := new(bytes.Buffer)
l.logger.SetOutput(buf)
logger.Logger.SetOutput(buf)

if err := preBackupHook(subt.cmd); err != nil {
if err := PreBackupHook(logger, subt.cmd); err != nil {
if !subt.fails {
t.Errorf("function test must not fail, got error: %q\n", err)
}
Expand All @@ -122,15 +125,16 @@ func TestPreBackupHook(t *testing.T) {
if !matched {
t.Errorf("expected a match of %q, got %q\n", subt.re, lines)
}
l.logger.SetOutput(os.Stderr)
logger.Logger.SetOutput(os.Stderr)
})
}
}

func TestPostBackupHook(t *testing.T) {
logger := logger.NewLevelLog()
t.Run("0", func(t *testing.T) {
if os.Getenv("_TEST_HOOK") == "1" {
postBackupHook("false")
PostBackupHook(logger, "false")
return
}
cmd := exec.Command(os.Args[0], "-test.run=TestPostBackupHook")
Expand All @@ -144,8 +148,8 @@ func TestPostBackupHook(t *testing.T) {

t.Run("1", func(t *testing.T) {
buf := new(bytes.Buffer)
l.logger.SetOutput(buf)
postBackupHook("")
logger.Logger.SetOutput(buf)
PostBackupHook(logger, "")
lines := buf.String()
if len(lines) != 0 {
t.Errorf("did not expect any output, got %q\n", lines)
Expand Down
Loading
Loading