Select Git revision
actions_test.go
repository_restic_test.go 5.04 KiB
package tabacco
import (
"bytes"
"context"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"testing"
"git.autistici.org/ai3/tools/tabacco/jobs"
)
// Create a temporary directory with two subdirs: 'data', for
// the test backup data, and 'repo' to store the (remote)
// repository. Populate 'data' with two tiny files.
//
// nolint
func createTempDirWithData(t *testing.T) string {
tmpdir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatal(err)
}
for _, d := range []string{"data", "repo", "restore"} {
os.Mkdir(filepath.Join(tmpdir, d), 0700)
}
ioutil.WriteFile(
filepath.Join(tmpdir, "data", "file1"),
[]byte("this is file number one."),
0600,
)
ioutil.WriteFile(
filepath.Join(tmpdir, "data", "file2"),
[]byte("this is file number two."),
0600,
)
return tmpdir
}
// nolint: gocyclo
func runResticTest(t *testing.T, tmpdir string, source *SourceSpec, restorePattern string, checkFn func(testing.TB, string)) {
// Check that we can actually run restic.
resticVersion, err := getResticVersion("restic")
if err != nil {
t.Skip("can't find restic: ", err)
}
if resticVersion.LessThan(resticMinGoodVersion) {
t.Skip("restic version ", resticVersion,
" is older than the minimum supported version ",
resticMinGoodVersion)
}
// Temporary cache dir.
cacheDir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(cacheDir)
store := &dummyMetadataStore{}
repoSpec := RepositorySpec{
Name: "main",
Type: "restic",
Params: map[string]interface{}{
"uri": tmpdir + "/repo",
"password": "testpass",
"cache_dir": cacheDir,
},
}
handlerSpecs := []*HandlerSpec{
// 'file' is predefined.
&HandlerSpec{
Name: "data",
Type: "pipe",
Params: map[string]interface{}{
"backup_command": "echo data",
// The restore command also verifies the data.
"restore_command": "read row ; test \"x$$row\" = xdata",
},
},
}
queueSpec := &jobs.QueueSpec{
Concurrency: 2,
}
sourceSpecs := []*SourceSpec{source}
// Run the backup.
configMgr, err := NewConfigManager(&Config{
Queue: queueSpec,
Repository: repoSpec,
HandlerSpecs: handlerSpecs,
SourceSpecs: sourceSpecs,
})
if err != nil {
t.Fatal(err)
}
defer configMgr.Close()
m, err := NewManager(context.TODO(), configMgr, store)
if err != nil {
t.Fatal(err)
}
defer m.Close()
backup, err := m.Backup(context.TODO(), configMgr.current().SourceSpecs()[0])
if err != nil {
t.Fatal(err)
}
if backup.ID == "" || backup.Host == "" {
t.Fatalf("empty fields in backup: %+v", backup)
}
// Check the 'restic snapshots' output.
output, err := exec.Command("env", "RESTIC_REPOSITORY=", "RESTIC_PASSWORD_FILE=", "RESTIC_PASSWORD=testpass", "restic", "-r", tmpdir+"/repo", "snapshots", "--json").Output()
if err != nil {
t.Fatalf("'restic snapshots' failed: %v", err)
}
snaps, err := parseResticSnapshots(output)
if err != nil {
t.Fatalf("parsing restic snaphots output: %v, output:\n%s", err, string(output))
}
if len(snaps) != 1 {
t.Fatalf("wrong number of snapshots: %+v", snaps)
}
snap := snaps[0]
if len(snap.Tags) != 3 {
t.Fatalf("woops, bad number of tags: %+v", snap)
}
// Now try to restore.
err = m.Restore(
context.TODO(),
&FindRequest{Pattern: restorePattern},
tmpdir+"/restore",
)
if err != nil {
t.Fatal("Restore", err)
}
if checkFn != nil {
checkFn(t, tmpdir)
}
}
func checkRestoredData(t testing.TB, tmpdir string) {
data, err := ioutil.ReadFile(filepath.Join(tmpdir, "restore", tmpdir, "data", "file1"))
if err != nil {
t.Fatalf("data/file1 has not been restored: %v", err)
}
if !bytes.Equal(data, []byte("this is file number one.")) {
t.Fatalf("data/file1 has bad restored contents: %s", string(data))
}
}
func TestRestic(t *testing.T) {
tmpdir := createTempDirWithData(t)
defer os.RemoveAll(tmpdir)
runResticTest(
t, tmpdir,
&SourceSpec{
Name: "source1",
Handler: "file",
Schedule: "@random_every 1h",
Params: map[string]interface{}{
"path": filepath.Join(tmpdir, "data"),
},
Datasets: []*DatasetSpec{
&DatasetSpec{
Atoms: []Atom{
{
Name: "f1",
Path: "file1",
},
{
Name: "f2",
Path: "file2",
},
},
},
},
},
"source1/*",
checkRestoredData,
)
}
func TestRestic_Stream(t *testing.T) {
tmpdir := createTempDirWithData(t)
defer os.RemoveAll(tmpdir)
runResticTest(
t, tmpdir,
&SourceSpec{
Name: "source1",
Handler: "data",
Schedule: "@random_every 1h",
Datasets: []*DatasetSpec{
&DatasetSpec{
Atoms: []Atom{
{
Name: "f1",
},
},
},
},
},
"source1/*",
nil,
)
}
func TestRestic_Stream_Compress(t *testing.T) {
tmpdir := createTempDirWithData(t)
defer os.RemoveAll(tmpdir)
runResticTest(
t, tmpdir,
&SourceSpec{
Name: "source1",
Handler: "data",
Schedule: "@random_every 1h",
Datasets: []*DatasetSpec{
&DatasetSpec{
Atoms: []Atom{
{
Name: "f1",
},
},
},
},
Params: map[string]interface{}{
"compress": true,
},
},
"source1/*",
nil,
)
}