zfs: public ZFSList() function + consolidation of mapping code

This commit is contained in:
Christian Schwarz 2017-05-01 20:35:04 +02:00
parent 28f0b90c25
commit 644d3dd06b
3 changed files with 55 additions and 46 deletions

View File

@ -12,6 +12,38 @@ type DatasetMapping interface {
Map(source DatasetPath) (target DatasetPath, err error) Map(source DatasetPath) (target DatasetPath, err error)
} }
func ZFSListMapping(mapping DatasetMapping) (datasets []DatasetPath, err error) {
if mapping == nil {
panic("mapping must not be nil")
}
var lines [][]string
lines, err = ZFSList([]string{"name"}, "-r", "-t", "filesystem,volume")
datasets = make([]DatasetPath, len(lines))
for i, line := range lines {
var path DatasetPath
if path, err = NewDatasetPath(line[0]); err != nil {
return
}
_, mapErr := mapping.Map(path)
if mapErr != nil && err != NoMatchError {
return nil, err
}
if mapErr == nil {
datasets[i] = path
}
}
return
}
type GlobMapping struct { type GlobMapping struct {
PrefixPath DatasetPath PrefixPath DatasetPath
TargetRoot DatasetPath TargetRoot DatasetPath
@ -118,7 +150,8 @@ func (m ExecMapping) Map(source DatasetPath) (target DatasetPath, err error) {
go func() { go func() {
err := cmd.Wait() err := cmd.Wait()
if err != nil { if err != nil {
fmt.Printf("error: %v\n", err) // TODO panic(err)
// fmt.Printf("error: %v\n", err) // TODO
} }
}() }()

View File

@ -2,11 +2,10 @@ package zfs
import ( import (
"bufio" "bufio"
"bytes"
"errors" "errors"
"fmt" "fmt"
"github.com/zrepl/zrepl/model"
"io" "io"
"io/ioutil"
"os/exec" "os/exec"
"strings" "strings"
) )
@ -19,16 +18,6 @@ func IncrementalSend(from, to string) (io.Reader, error) {
return nil, nil return nil, nil
} }
func FilesystemsAtRoot(root string) (fs model.Filesystem, err error) {
_, _ = zfsList("zroot", func(path DatasetPath) bool {
return true
})
return
}
type DatasetPath []string type DatasetPath []string
func (p DatasetPath) ToString() string { func (p DatasetPath) ToString() string {
@ -49,11 +38,15 @@ func NewDatasetPath(s string) (p DatasetPath, err error) {
if strings.ContainsAny(s, FORBIDDEN) { // TODO space may be a bit too restrictive... if strings.ContainsAny(s, FORBIDDEN) { // TODO space may be a bit too restrictive...
return nil, errors.New(fmt.Sprintf("path '%s' contains forbidden characters (any of '%s')", s, FORBIDDEN)) return nil, errors.New(fmt.Sprintf("path '%s' contains forbidden characters (any of '%s')", s, FORBIDDEN))
} }
return toDatasetPath(s), nil return strings.Split(s, "/"), nil
} }
func toDatasetPath(s string) DatasetPath { func toDatasetPath(s string) DatasetPath {
return strings.Split(s, "/") p, err := NewDatasetPath(s)
if err != nil {
panic(err)
}
return p
} }
type DatasetFilter func(path DatasetPath) bool type DatasetFilter func(path DatasetPath) bool
@ -69,32 +62,24 @@ func (e ZFSError) Error() string {
var ZFS_BINARY string = "zfs" var ZFS_BINARY string = "zfs"
func zfsList(root string, filter DatasetFilter) (datasets []DatasetPath, err error) { func ZFSList(properties []string, zfsArgs ...string) (res [][]string, err error) {
const ZFS_LIST_FIELD_COUNT = 1 args := make([]string, 0, 4+len(zfsArgs))
args := make([]string, 0, 10)
args = append(args, args = append(args,
"list", "-H", "-r", "list", "-H",
"-t", "filesystem,volume", "-o", strings.Join(properties, ","))
"-o", "name") args = append(args, zfsArgs...)
if len(root) > 0 {
args = append(args, root)
}
cmd := exec.Command(ZFS_BINARY, args...) cmd := exec.Command(ZFS_BINARY, args...)
var stdout io.Reader var stdout io.Reader
var stderr io.Reader stderr := bytes.NewBuffer(make([]byte, 0, 1024))
cmd.Stderr = stderr
if stdout, err = cmd.StdoutPipe(); err != nil { if stdout, err = cmd.StdoutPipe(); err != nil {
return return
} }
if stderr, err = cmd.StderrPipe(); err != nil {
return
}
if err = cmd.Start(); err != nil { if err = cmd.Start(); err != nil {
return return
} }
@ -103,32 +88,25 @@ func zfsList(root string, filter DatasetFilter) (datasets []DatasetPath, err err
buf := make([]byte, 1024) buf := make([]byte, 1024)
s.Buffer(buf, 0) s.Buffer(buf, 0)
datasets = make([]DatasetPath, 0) res = make([][]string, 0)
for s.Scan() { for s.Scan() {
fields := strings.SplitN(s.Text(), "\t", ZFS_LIST_FIELD_COUNT) fields := strings.SplitN(s.Text(), "\t", len(properties))
if len(fields) != ZFS_LIST_FIELD_COUNT {
if len(fields) != len(properties) {
err = errors.New("unexpected output") err = errors.New("unexpected output")
return return
} }
dp := toDatasetPath(fields[0]) res = append(res, fields)
if filter(dp) {
datasets = append(datasets, dp)
}
} }
stderrOutput, err := ioutil.ReadAll(stderr)
if waitErr := cmd.Wait(); waitErr != nil { if waitErr := cmd.Wait(); waitErr != nil {
err := ZFSError{ err := ZFSError{
Stderr: stderrOutput, Stderr: stderr.Bytes(),
WaitErr: waitErr, WaitErr: waitErr,
} }
return nil, err return nil, err
} }
return return
} }

View File

@ -10,9 +10,7 @@ func TestZFSListHandlesProducesZFSErrorOnNonZeroExit(t *testing.T) {
ZFS_BINARY = "./test_helpers/zfs_failer.sh" ZFS_BINARY = "./test_helpers/zfs_failer.sh"
_, err = zfsList("nonexistent/dataset", func(p DatasetPath) bool { _, err = ZFSList([]string{"fictionalprop"}, "nonexistent/dataset")
return true
})
assert.Error(t, err) assert.Error(t, err)
zfsError, ok := err.(ZFSError) zfsError, ok := err.(ZFSError)