mirror of
https://github.com/zrepl/zrepl.git
synced 2025-02-17 19:01:12 +01:00
zfs: public ZFSList() function + consolidation of mapping code
This commit is contained in:
parent
28f0b90c25
commit
644d3dd06b
@ -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
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
62
zfs/zfs.go
62
zfs/zfs.go
@ -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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user