replication: refactor driving logic (no more explicit state machine)

This commit is contained in:
Christian Schwarz
2019-02-22 11:40:27 +01:00
parent 0230c6321f
commit 07b43bffa4
33 changed files with 1594 additions and 1515 deletions
File diff suppressed because it is too large Load Diff
+122
View File
@@ -0,0 +1,122 @@
syntax = "proto3";
option go_package = "pdu";
service Replication {
rpc ListFilesystems (ListFilesystemReq) returns (ListFilesystemRes);
rpc ListFilesystemVersions (ListFilesystemVersionsReq) returns (ListFilesystemVersionsRes);
rpc DestroySnapshots (DestroySnapshotsReq) returns (DestroySnapshotsRes);
rpc ReplicationCursor (ReplicationCursorReq) returns (ReplicationCursorRes);
// for Send and Recv, see package rpc
}
message ListFilesystemReq {}
message ListFilesystemRes {
repeated Filesystem Filesystems = 1;
bool Empty = 2;
}
message Filesystem {
string Path = 1;
string ResumeToken = 2;
}
message ListFilesystemVersionsReq {
string Filesystem = 1;
}
message ListFilesystemVersionsRes {
repeated FilesystemVersion Versions = 1;
}
message FilesystemVersion {
enum VersionType {
Snapshot = 0;
Bookmark = 1;
}
VersionType Type = 1;
string Name = 2;
uint64 Guid = 3;
uint64 CreateTXG = 4;
string Creation = 5; // RFC 3339
}
message SendReq {
string Filesystem = 1;
string From = 2;
// May be empty / null to request a full transfer of From
string To = 3;
// If ResumeToken is not empty, the resume token that CAN be tried for 'zfs send' by the sender.
// The sender MUST indicate in SendRes.UsedResumeToken
// If it does not work, the sender SHOULD clear the resume token on their side
// and use From and To instead
// If ResumeToken is not empty, the GUIDs of From and To
// MUST correspond to those encoded in the ResumeToken.
// Otherwise, the Sender MUST return an error.
string ResumeToken = 4;
bool Compress = 5;
bool Dedup = 6;
bool DryRun = 7;
}
message Property {
string Name = 1;
string Value = 2;
}
message SendRes {
// Whether the resume token provided in the request has been used or not.
bool UsedResumeToken = 2;
// Expected stream size determined by dry run, not exact.
// 0 indicates that for the given SendReq, no size estimate could be made.
int64 ExpectedSize = 3;
repeated Property Properties = 4;
}
message ReceiveReq {
string Filesystem = 1; // FIXME should be snapshot name, we can enforce that on recv
// If true, the receiver should clear the resume token before perfoming the zfs recv of the stream in the request
bool ClearResumeToken = 2;
}
message ReceiveRes {}
message DestroySnapshotsReq {
string Filesystem = 1;
// Path to filesystem, snapshot or bookmark to be destroyed
repeated FilesystemVersion Snapshots = 2;
}
message DestroySnapshotRes {
FilesystemVersion Snapshot = 1;
string Error = 2;
}
message DestroySnapshotsRes {
repeated DestroySnapshotRes Results = 1;
}
message ReplicationCursorReq {
string Filesystem = 1;
message GetOp {}
message SetOp {
string Snapshot = 2;
}
oneof op {
GetOp get = 2;
SetOp set = 3;
}
}
message ReplicationCursorRes {
oneof Result {
uint64 Guid = 1;
bool Notexist = 2;
}
}
+76
View File
@@ -0,0 +1,76 @@
package pdu
import (
"fmt"
"github.com/zrepl/zrepl/zfs"
"time"
)
func (v *FilesystemVersion) RelName() string {
zv, err := v.ZFSFilesystemVersion()
if err != nil {
panic(err)
}
return zv.String()
}
func (v FilesystemVersion_VersionType) ZFSVersionType() zfs.VersionType {
switch v {
case FilesystemVersion_Snapshot:
return zfs.Snapshot
case FilesystemVersion_Bookmark:
return zfs.Bookmark
default:
panic(fmt.Sprintf("unexpected v.Type %#v", v))
}
}
func FilesystemVersionFromZFS(fsv *zfs.FilesystemVersion) *FilesystemVersion {
var t FilesystemVersion_VersionType
switch fsv.Type {
case zfs.Bookmark:
t = FilesystemVersion_Bookmark
case zfs.Snapshot:
t = FilesystemVersion_Snapshot
default:
panic("unknown fsv.Type: " + fsv.Type)
}
return &FilesystemVersion{
Type: t,
Name: fsv.Name,
Guid: fsv.Guid,
CreateTXG: fsv.CreateTXG,
Creation: fsv.Creation.Format(time.RFC3339),
}
}
func FilesystemVersionCreation(t time.Time) string {
return t.Format(time.RFC3339)
}
func (v *FilesystemVersion) CreationAsTime() (time.Time, error) {
return time.Parse(time.RFC3339, v.Creation)
}
// implement fsfsm.FilesystemVersion
func (v *FilesystemVersion) SnapshotTime() time.Time {
t, err := v.CreationAsTime()
if err != nil {
panic(err) // FIXME
}
return t
}
func (v *FilesystemVersion) ZFSFilesystemVersion() (*zfs.FilesystemVersion, error) {
ct, err := v.CreationAsTime()
if err != nil {
return nil, err
}
return &zfs.FilesystemVersion{
Type: v.Type.ZFSVersionType(),
Name: v.Name,
Guid: v.Guid,
CreateTXG: v.CreateTXG,
Creation: ct,
}, nil
}
+68
View File
@@ -0,0 +1,68 @@
package pdu
import (
"github.com/stretchr/testify/assert"
"testing"
"time"
)
func TestFilesystemVersion_RelName(t *testing.T) {
type TestCase struct {
In FilesystemVersion
Out string
Panic bool
}
creat := FilesystemVersionCreation(time.Now())
tcs := []TestCase{
{
In: FilesystemVersion{
Type: FilesystemVersion_Snapshot,
Name: "foobar",
Creation: creat,
},
Out: "@foobar",
},
{
In: FilesystemVersion{
Type: FilesystemVersion_Bookmark,
Name: "foobar",
Creation: creat,
},
Out: "#foobar",
},
{
In: FilesystemVersion{
Type: 2342,
Name: "foobar",
Creation: creat,
},
Panic: true,
},
}
for _, tc := range tcs {
if tc.Panic {
assert.Panics(t, func() {
tc.In.RelName()
})
} else {
o := tc.In.RelName()
assert.Equal(t, tc.Out, o)
}
}
}
func TestFilesystemVersion_ZFSFilesystemVersion(t *testing.T) {
empty := &FilesystemVersion{}
_, err:= empty.ZFSFilesystemVersion()
assert.Error(t, err)
dateInvalid := &FilesystemVersion{Creation: "foobar"}
_, err = dateInvalid.ZFSFilesystemVersion()
assert.Error(t, err)
}