From 644d3dd06b86c3ae95ca17a4f53d2e2bc255c639 Mon Sep 17 00:00:00 2001 From: Christian Schwarz Date: Mon, 1 May 2017 20:35:04 +0200 Subject: [PATCH] zfs: public ZFSList() function + consolidation of mapping code --- zfs/mapping.go | 35 +++++++++++++++++++++++++++- zfs/zfs.go | 62 ++++++++++++++++--------------------------------- zfs/zfs_test.go | 4 +--- 3 files changed, 55 insertions(+), 46 deletions(-) diff --git a/zfs/mapping.go b/zfs/mapping.go index c73f9a0..5911e38 100644 --- a/zfs/mapping.go +++ b/zfs/mapping.go @@ -12,6 +12,38 @@ type DatasetMapping interface { 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 { PrefixPath DatasetPath TargetRoot DatasetPath @@ -118,7 +150,8 @@ func (m ExecMapping) Map(source DatasetPath) (target DatasetPath, err error) { go func() { err := cmd.Wait() if err != nil { - fmt.Printf("error: %v\n", err) // TODO + panic(err) + // fmt.Printf("error: %v\n", err) // TODO } }() diff --git a/zfs/zfs.go b/zfs/zfs.go index 09a34bb..9daf1d1 100644 --- a/zfs/zfs.go +++ b/zfs/zfs.go @@ -2,11 +2,10 @@ package zfs import ( "bufio" + "bytes" "errors" "fmt" - "github.com/zrepl/zrepl/model" "io" - "io/ioutil" "os/exec" "strings" ) @@ -19,16 +18,6 @@ func IncrementalSend(from, to string) (io.Reader, error) { return nil, nil } -func FilesystemsAtRoot(root string) (fs model.Filesystem, err error) { - - _, _ = zfsList("zroot", func(path DatasetPath) bool { - return true - }) - - return - -} - type DatasetPath []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... 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 { - return strings.Split(s, "/") + p, err := NewDatasetPath(s) + if err != nil { + panic(err) + } + return p } type DatasetFilter func(path DatasetPath) bool @@ -69,32 +62,24 @@ func (e ZFSError) Error() string { 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, 10) + args := make([]string, 0, 4+len(zfsArgs)) args = append(args, - "list", "-H", "-r", - "-t", "filesystem,volume", - "-o", "name") - - if len(root) > 0 { - args = append(args, root) - } + "list", "-H", + "-o", strings.Join(properties, ",")) + args = append(args, zfsArgs...) cmd := exec.Command(ZFS_BINARY, args...) 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 { return } - if stderr, err = cmd.StderrPipe(); err != nil { - return - } - if err = cmd.Start(); err != nil { return } @@ -103,32 +88,25 @@ func zfsList(root string, filter DatasetFilter) (datasets []DatasetPath, err err buf := make([]byte, 1024) s.Buffer(buf, 0) - datasets = make([]DatasetPath, 0) + res = make([][]string, 0) for s.Scan() { - fields := strings.SplitN(s.Text(), "\t", ZFS_LIST_FIELD_COUNT) - if len(fields) != ZFS_LIST_FIELD_COUNT { + fields := strings.SplitN(s.Text(), "\t", len(properties)) + + if len(fields) != len(properties) { err = errors.New("unexpected output") return } - dp := toDatasetPath(fields[0]) - - if filter(dp) { - datasets = append(datasets, dp) - } + res = append(res, fields) } - stderrOutput, err := ioutil.ReadAll(stderr) - if waitErr := cmd.Wait(); waitErr != nil { err := ZFSError{ - Stderr: stderrOutput, + Stderr: stderr.Bytes(), WaitErr: waitErr, } return nil, err } - return - } diff --git a/zfs/zfs_test.go b/zfs/zfs_test.go index 04cd888..784045f 100644 --- a/zfs/zfs_test.go +++ b/zfs/zfs_test.go @@ -10,9 +10,7 @@ func TestZFSListHandlesProducesZFSErrorOnNonZeroExit(t *testing.T) { ZFS_BINARY = "./test_helpers/zfs_failer.sh" - _, err = zfsList("nonexistent/dataset", func(p DatasetPath) bool { - return true - }) + _, err = ZFSList([]string{"fictionalprop"}, "nonexistent/dataset") assert.Error(t, err) zfsError, ok := err.(ZFSError)