featurebase/ctl/backup_tar_test.go
Lory Cloutier a8996a149d
Prevent file corruption when writing tar backup to stdout (#2344)
* Prevent file corruption when writing tar backup to stdout

FB-1794

Tar backups written to stdout were coming out corrupt. This turned
out to be due to log messages getting written to stdout and ending
up in the tar file. We now check to see if the tar file and the log
are both going to stdout, and if they are, send the logs to stderr
instead.

Testing did not have any kind of consistency or validity check. We
now compare a tar file sent to a file and a tar file sent to stdout
to make sure they're the same. This does not guarantee correctness
but does at least catch this form of corruption.

* trying different index name

Co-authored-by: tgruben <tgruben@gmail.com>
Co-authored-by: Todd Gruben <todd@molecula.com>
2022-12-13 13:03:16 -06:00

68 lines
2 KiB
Go

package ctl
import (
"bytes"
"context"
"os"
"path/filepath"
"testing"
pilosa "github.com/molecula/featurebase/v3"
"github.com/molecula/featurebase/v3/test"
)
func TestBackupTarCommand_Run(t *testing.T) {
cluster := test.MustRunCluster(t, 1)
defer cluster.Close()
cmd := cluster.GetNode(0)
indexName := "backuptar"
// this might produce some annoying spam in tests but we need to make sure log messages to
// stdout are being redirected properly when the tarfile is also going to stdout
cm := NewBackupTarCommand(os.Stdout)
hostport := cmd.API.Node().URI.HostPort()
cm.Host = hostport
dir := t.TempDir()
cm.OutputPath = filepath.Join(dir, "backuptest.tar")
_, err := cmd.API.CreateIndex(context.Background(), indexName, pilosa.IndexOptions{Keys: true, TrackExistence: true})
if err != nil {
t.Fatalf("creating test index: %v", err)
}
_, err = cmd.API.CreateField(context.Background(), indexName, "f", pilosa.OptFieldKeys())
if err != nil {
t.Fatalf("creating test field: %v", err)
}
cm.Index = indexName
if err := cm.Run(context.Background()); err != nil {
t.Fatalf("BackupTarCommand Run error: %s", err)
}
oldpath := cm.OutputPath
cm.OutputPath = "-"
cfpath := filepath.Join(dir, "stdouttest.tar") //capture file
cf, err := os.Create(cfpath)
if err != nil {
t.Fatalf("opening file to compare file and stdout outputs: %v", err)
}
defer cf.Close()
// I don't like this at all but it's all i'm really finding for capturing os.Stdout
old := os.Stdout
defer func() { os.Stdout = old }()
os.Stdout = cf
if err := cm.Run(context.Background()); err != nil {
t.Fatalf("BackupTarCommand Run error: %s", err)
}
fdata, err := os.ReadFile(oldpath)
if err != nil {
t.Fatalf("unable to read from direct-to-file backup: %v", err)
}
cdata, err := os.ReadFile(cfpath)
if err != nil {
t.Fatalf("unable to read from captured stdout backup: %v", err)
}
if !bytes.Equal(fdata, cdata) {
t.Fatalf("backing up to file and to stdout produced different results")
}
}