mirror of
https://github.com/zrepl/zrepl.git
synced 2025-05-30 22:57:27 +02:00
[#342] endpoint: always create unencrypted placeholder filesystems
This "breaks" the use case of receiving an unencrypted send into an encrypted receiver by setting the receiver's `root_fs`'s `encryption=on`. "breaks" in air-quotes because we have not yet released a version of zrepl with encrypted send support. We will bring back the featured outlined above in a future release. See https://github.com/zrepl/zrepl/issues/342#issuecomment-657231818 and following.
This commit is contained in:
parent
4b8f0ad112
commit
8ff83f2f1a
@ -679,7 +679,7 @@ func (s *Receiver) Receive(ctx context.Context, req *pdu.ReceiveReq, receive io.
|
|||||||
f := zfs.NewDatasetPathForest()
|
f := zfs.NewDatasetPathForest()
|
||||||
f.Add(lp)
|
f.Add(lp)
|
||||||
getLogger(ctx).Debug("begin tree-walk")
|
getLogger(ctx).Debug("begin tree-walk")
|
||||||
f.WalkTopDown(func(v zfs.DatasetPathVisit) (visitChildTree bool) {
|
f.WalkTopDown(func(v *zfs.DatasetPathVisit) (visitChildTree bool) {
|
||||||
if v.Path.Equal(lp) {
|
if v.Path.Equal(lp) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -707,7 +707,7 @@ func (s *Receiver) Receive(ctx context.Context, req *pdu.ReceiveReq, receive io.
|
|||||||
}
|
}
|
||||||
l := getLogger(ctx).WithField("placeholder_fs", v.Path)
|
l := getLogger(ctx).WithField("placeholder_fs", v.Path)
|
||||||
l.Debug("create placeholder filesystem")
|
l.Debug("create placeholder filesystem")
|
||||||
err := zfs.ZFSCreatePlaceholderFilesystem(ctx, v.Path)
|
err := zfs.ZFSCreatePlaceholderFilesystem(ctx, v.Path, v.Parent.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.WithError(err).Error("cannot create placeholder filesystem")
|
l.WithError(err).Error("cannot create placeholder filesystem")
|
||||||
visitErr = err
|
visitErr = err
|
||||||
|
@ -33,9 +33,10 @@ type DatasetPathVisit struct {
|
|||||||
Path *DatasetPath
|
Path *DatasetPath
|
||||||
// If true, the dataset referenced by Path was not in the list of datasets to traverse
|
// If true, the dataset referenced by Path was not in the list of datasets to traverse
|
||||||
FilledIn bool
|
FilledIn bool
|
||||||
|
Parent *DatasetPathVisit
|
||||||
}
|
}
|
||||||
|
|
||||||
type DatasetPathsVisitor func(v DatasetPathVisit) (visitChildTree bool)
|
type DatasetPathsVisitor func(v *DatasetPathVisit) (visitChildTree bool)
|
||||||
|
|
||||||
// Traverse a list of DatasetPaths top down, i.e. given a set of datasets with same
|
// Traverse a list of DatasetPaths top down, i.e. given a set of datasets with same
|
||||||
// path prefix, those with shorter prefix are traversed first.
|
// path prefix, those with shorter prefix are traversed first.
|
||||||
@ -44,7 +45,11 @@ type DatasetPathsVisitor func(v DatasetPathVisit) (visitChildTree bool)
|
|||||||
func (f *DatasetPathForest) WalkTopDown(visitor DatasetPathsVisitor) {
|
func (f *DatasetPathForest) WalkTopDown(visitor DatasetPathsVisitor) {
|
||||||
|
|
||||||
for _, r := range f.roots {
|
for _, r := range f.roots {
|
||||||
r.WalkTopDown([]string{}, visitor)
|
r.WalkTopDown(&DatasetPathVisit{
|
||||||
|
Path: &DatasetPath{nil},
|
||||||
|
FilledIn: true,
|
||||||
|
Parent: nil,
|
||||||
|
}, visitor)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -88,19 +93,21 @@ func (t *datasetPathTree) Add(p []string) bool {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *datasetPathTree) WalkTopDown(parent []string, visitor DatasetPathsVisitor) {
|
func (t *datasetPathTree) WalkTopDown(parent *DatasetPathVisit, visitor DatasetPathsVisitor) {
|
||||||
|
|
||||||
this := append(parent, t.Component)
|
thisVisitPath := parent.Path.Copy()
|
||||||
|
thisVisitPath.Extend(&DatasetPath{[]string{t.Component}})
|
||||||
|
|
||||||
thisVisit := DatasetPathVisit{
|
thisVisit := &DatasetPathVisit{
|
||||||
&DatasetPath{this},
|
thisVisitPath,
|
||||||
t.FilledIn,
|
t.FilledIn,
|
||||||
|
parent,
|
||||||
}
|
}
|
||||||
visitChildTree := visitor(thisVisit)
|
visitChildTree := visitor(thisVisit)
|
||||||
|
|
||||||
if visitChildTree {
|
if visitChildTree {
|
||||||
for _, c := range t.Children {
|
for _, c := range t.Children {
|
||||||
c.WalkTopDown(this, visitor)
|
c.WalkTopDown(thisVisit, visitor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,8 +28,8 @@ func makeVisitRecorder() (v DatasetPathsVisitor, rec *visitRecorder) {
|
|||||||
rec = &visitRecorder{
|
rec = &visitRecorder{
|
||||||
visits: make([]DatasetPathVisit, 0),
|
visits: make([]DatasetPathVisit, 0),
|
||||||
}
|
}
|
||||||
v = func(v DatasetPathVisit) bool {
|
v = func(v *DatasetPathVisit) bool {
|
||||||
rec.visits = append(rec.visits, v)
|
rec.visits = append(rec.visits, *v)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -43,6 +43,27 @@ func buildForest(paths []*DatasetPath) (f *DatasetPathForest) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type expectedDatasetPathVisit struct {
|
||||||
|
expectPath *DatasetPath
|
||||||
|
expectFillIn bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *expectedDatasetPathVisit) AssertEqual(t *testing.T, actual DatasetPathVisit) {
|
||||||
|
assert.Equal(t, e.expectPath, actual.Path)
|
||||||
|
assert.Equal(t, e.expectFillIn, actual.FilledIn)
|
||||||
|
if actual.Parent != nil {
|
||||||
|
assert.Equal(t, actual.Parent.Path.Length()+1, e.expectPath.Length())
|
||||||
|
assert.True(t, e.expectPath.HasPrefix(actual.Parent.Path))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectedDatasetPathVisits(t *testing.T, expected []expectedDatasetPathVisit, actual []DatasetPathVisit) {
|
||||||
|
assert.Equal(t, len(expected), len(actual))
|
||||||
|
for i := range expected {
|
||||||
|
expected[i].AssertEqual(t, actual[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestDatasetPathForestWalkTopDown(t *testing.T) {
|
func TestDatasetPathForestWalkTopDown(t *testing.T) {
|
||||||
|
|
||||||
paths := []*DatasetPath{
|
paths := []*DatasetPath{
|
||||||
@ -56,7 +77,7 @@ func TestDatasetPathForestWalkTopDown(t *testing.T) {
|
|||||||
|
|
||||||
buildForest(paths).WalkTopDown(v)
|
buildForest(paths).WalkTopDown(v)
|
||||||
|
|
||||||
expectedVisits := []DatasetPathVisit{
|
expectedVisits := []expectedDatasetPathVisit{
|
||||||
{toDatasetPath("pool1"), false},
|
{toDatasetPath("pool1"), false},
|
||||||
{toDatasetPath("pool1/foo"), true},
|
{toDatasetPath("pool1/foo"), true},
|
||||||
{toDatasetPath("pool1/foo/bar"), false},
|
{toDatasetPath("pool1/foo/bar"), false},
|
||||||
@ -65,8 +86,7 @@ func TestDatasetPathForestWalkTopDown(t *testing.T) {
|
|||||||
{toDatasetPath("pool2/test"), true},
|
{toDatasetPath("pool2/test"), true},
|
||||||
{toDatasetPath("pool2/test/bar"), false},
|
{toDatasetPath("pool2/test/bar"), false},
|
||||||
}
|
}
|
||||||
assert.Equal(t, expectedVisits, rec.visits)
|
expectedDatasetPathVisits(t, expectedVisits, rec.visits)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDatasetPathWalkTopDownWorksUnordered(t *testing.T) {
|
func TestDatasetPathWalkTopDownWorksUnordered(t *testing.T) {
|
||||||
@ -82,7 +102,7 @@ func TestDatasetPathWalkTopDownWorksUnordered(t *testing.T) {
|
|||||||
|
|
||||||
buildForest(paths).WalkTopDown(v)
|
buildForest(paths).WalkTopDown(v)
|
||||||
|
|
||||||
expectedVisits := []DatasetPathVisit{
|
expectedVisits := []expectedDatasetPathVisit{
|
||||||
{toDatasetPath("pool1"), false},
|
{toDatasetPath("pool1"), false},
|
||||||
{toDatasetPath("pool1/foo"), true},
|
{toDatasetPath("pool1/foo"), true},
|
||||||
{toDatasetPath("pool1/foo/bar"), false},
|
{toDatasetPath("pool1/foo/bar"), false},
|
||||||
@ -91,6 +111,6 @@ func TestDatasetPathWalkTopDownWorksUnordered(t *testing.T) {
|
|||||||
{toDatasetPath("pool1/bang/baz"), false},
|
{toDatasetPath("pool1/bang/baz"), false},
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, expectedVisits, rec.visits)
|
expectedDatasetPathVisits(t, expectedVisits, rec.visits)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
"github.com/zrepl/zrepl/zfs/zfscmd"
|
"github.com/zrepl/zrepl/zfs/zfscmd"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -78,14 +79,23 @@ func ZFSGetFilesystemPlaceholderState(ctx context.Context, p *DatasetPath) (stat
|
|||||||
return state, nil
|
return state, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ZFSCreatePlaceholderFilesystem(ctx context.Context, p *DatasetPath) (err error) {
|
func ZFSCreatePlaceholderFilesystem(ctx context.Context, fs *DatasetPath, parent *DatasetPath) (err error) {
|
||||||
if p.Length() == 1 {
|
if fs.Length() == 1 {
|
||||||
return fmt.Errorf("cannot create %q: pools cannot be created with zfs create", p.ToString())
|
return fmt.Errorf("cannot create %q: pools cannot be created with zfs create", fs.ToString())
|
||||||
}
|
}
|
||||||
cmd := zfscmd.CommandContext(ctx, ZFS_BINARY, "create",
|
|
||||||
|
cmdline := []string{
|
||||||
|
"create",
|
||||||
"-o", fmt.Sprintf("%s=%s", PlaceholderPropertyName, placeholderPropertyOn),
|
"-o", fmt.Sprintf("%s=%s", PlaceholderPropertyName, placeholderPropertyOn),
|
||||||
"-o", "mountpoint=none",
|
"-o", "mountpoint=none",
|
||||||
p.ToString())
|
}
|
||||||
|
if parentEncrypted, err := ZFSGetEncryptionEnabled(ctx, parent.ToString()); err != nil {
|
||||||
|
return errors.Wrap(err, "cannot determine encryption support")
|
||||||
|
} else if parentEncrypted {
|
||||||
|
cmdline = append(cmdline, "-o", "encryption=off")
|
||||||
|
}
|
||||||
|
cmdline = append(cmdline, fs.ToString())
|
||||||
|
cmd := zfscmd.CommandContext(ctx, ZFS_BINARY, cmdline...)
|
||||||
|
|
||||||
stdio, err := cmd.CombinedOutput()
|
stdio, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user