diff --git a/v2/bolt_migrate.go b/v2/bolt_migrate.go new file mode 100644 index 0000000..1d72fb2 --- /dev/null +++ b/v2/bolt_migrate.go @@ -0,0 +1,77 @@ +//go:build (386 || amd64 || arm || arm64 || ppc || ppc64 || ppc64le || s390x) && !nobolt +// +build 386 amd64 arm arm64 ppc ppc64 ppc64le s390x +// +build !nobolt + +package raftboltdb + +import ( + "fmt" + "os" + "time" + + v1 "github.com/boltdb/bolt" +) + +// MigrateToV2 reads in the source file path of a BoltDB file +// and outputs all the data migrated to a Bbolt destination file +func MigrateToV2(source, destination string) (*BoltStore, error) { + _, err := os.Stat(destination) + if err == nil { + return nil, fmt.Errorf("file exists in destination %v", destination) + } + + srcDb, err := v1.Open(source, dbFileMode, &v1.Options{ + ReadOnly: true, + Timeout: 1 * time.Minute, + }) + if err != nil { + return nil, fmt.Errorf("failed opening source database: %v", err) + } + + //Start a connection to the source + srctx, err := srcDb.Begin(false) + if err != nil { + return nil, fmt.Errorf("failed connecting to source database: %v", err) + } + defer srctx.Rollback() + + //Create the destination + destDb, err := New(Options{Path: destination}) + if err != nil { + return nil, fmt.Errorf("failed creating destination database: %v", err) + } + //Start a connection to the new + desttx, err := destDb.conn.Begin(true) + if err != nil { + destDb.Close() + os.Remove(destination) + return nil, fmt.Errorf("failed connecting to destination database: %v", err) + } + + defer desttx.Rollback() + + //Loop over both old buckets and set them in the new + buckets := [][]byte{dbConf, dbLogs} + for _, b := range buckets { + srcB := srctx.Bucket(b) + destB := desttx.Bucket(b) + err = srcB.ForEach(func(k, v []byte) error { + return destB.Put(k, v) + }) + if err != nil { + destDb.Close() + os.Remove(destination) + return nil, fmt.Errorf("failed to copy %v bucket: %v", string(b), err) + } + } + + //If the commit fails, clean up + if err := desttx.Commit(); err != nil { + destDb.Close() + os.Remove(destination) + return nil, fmt.Errorf("failed commiting data to destination: %v", err) + } + + return destDb, nil + +} diff --git a/v2/bolt_migrate_nobolt.go b/v2/bolt_migrate_nobolt.go new file mode 100644 index 0000000..feb12af --- /dev/null +++ b/v2/bolt_migrate_nobolt.go @@ -0,0 +1,10 @@ +//go:build (!386 && !amd64 && !arm && !arm64 && !ppc && !ppc64 && !ppc64le && !s390x) || nobolt +// +build !386,!amd64,!arm,!arm64,!ppc,!ppc64,!ppc64le,!s390x nobolt + +package raftboltdb + +// MigrateToV2 reads in the source file path of a BoltDB file +// and outputs all the data migrated to a Bbolt destination file +func MigrateToV2(source, destination string) (*BoltStore, error) { + return nil, ErrNotImplemented +} diff --git a/v2/bolt_migrate_test.go b/v2/bolt_migrate_test.go new file mode 100644 index 0000000..cbd698f --- /dev/null +++ b/v2/bolt_migrate_test.go @@ -0,0 +1,71 @@ +//go:build (386 || amd64 || arm || arm64 || ppc || ppc64 || ppc64le || s390x) && !nobolt +// +build 386 amd64 arm arm64 ppc ppc64 ppc64le s390x +// +build !nobolt + +package raftboltdb + +import ( + "io/ioutil" + "os" + "path/filepath" + "reflect" + "testing" + + "github.com/hashicorp/raft" + v1 "github.com/hashicorp/raft-boltdb" +) + +func TestBoltStore_MigrateToV2(t *testing.T) { + + dir, err := ioutil.TempDir("", t.Name()) + if err != nil { + t.Fatalf("err: %s", err) + } + defer os.RemoveAll(dir) + + srcFile := filepath.Join(dir, "/sourcepath") + destFile := filepath.Join(dir, "/destpath") + + // Successfully creates and returns a store + srcDb, err := v1.NewBoltStore(srcFile) + if err != nil { + t.Fatalf("failed creating source database: %s", err) + } + defer srcDb.Close() + + // Set a mock raft log + logs := []*raft.Log{ + testRaftLog(1, "log1"), + testRaftLog(2, "log2"), + testRaftLog(3, "log3"), + } + + //Store logs source + if err := srcDb.StoreLogs(logs); err != nil { + t.Fatalf("failed storing logs in source database: %s", err) + } + srcResult := new(raft.Log) + if err := srcDb.GetLog(2, srcResult); err != nil { + t.Fatalf("failed getting log from source database: %s", err) + } + + if err := srcDb.Close(); err != nil { + t.Fatalf("failed closing source database: %s", err) + } + + destDb, err := MigrateToV2(srcFile, destFile) + if err != nil { + t.Fatalf("did not migrate successfully, err %v", err) + } + defer destDb.Close() + + destResult := new(raft.Log) + if err := destDb.GetLog(2, destResult); err != nil { + t.Fatalf("failed getting log from destination database: %s", err) + } + + if !reflect.DeepEqual(srcResult, destResult) { + t.Errorf("BoltDB log did not equal Bbolt log, Boltdb %v, Bbolt: %v", srcResult, destResult) + } + +} diff --git a/v2/bolt_store.go b/v2/bolt_store.go index 5414b29..92fe35c 100644 --- a/v2/bolt_store.go +++ b/v2/bolt_store.go @@ -2,12 +2,9 @@ package raftboltdb import ( "errors" - "fmt" - "os" "time" metrics "github.com/armon/go-metrics" - v1 "github.com/boltdb/bolt" "github.com/hashicorp/raft" "go.etcd.io/bbolt" ) @@ -25,6 +22,8 @@ var ( // An error indicating a given key does not exist ErrKeyNotFound = errors.New("not found") + // An error indicating a feature isn't implemented on the current platform + ErrNotImplemented = errors.New("not implemented") ) // BoltStore provides access to Bbolt for Raft to store and retrieve @@ -297,67 +296,3 @@ func (b *BoltStore) GetUint64(key []byte) (uint64, error) { func (b *BoltStore) Sync() error { return b.conn.Sync() } - -// MigrateToV2 reads in the source file path of a BoltDB file -// and outputs all the data migrated to a Bbolt destination file -func MigrateToV2(source, destination string) (*BoltStore, error) { - _, err := os.Stat(destination) - if err == nil { - return nil, fmt.Errorf("file exists in destination %v", destination) - } - - srcDb, err := v1.Open(source, dbFileMode, &v1.Options{ - ReadOnly: true, - Timeout: 1 * time.Minute, - }) - if err != nil { - return nil, fmt.Errorf("failed opening source database: %v", err) - } - - //Start a connection to the source - srctx, err := srcDb.Begin(false) - if err != nil { - return nil, fmt.Errorf("failed connecting to source database: %v", err) - } - defer srctx.Rollback() - - //Create the destination - destDb, err := New(Options{Path: destination}) - if err != nil { - return nil, fmt.Errorf("failed creating destination database: %v", err) - } - //Start a connection to the new - desttx, err := destDb.conn.Begin(true) - if err != nil { - destDb.Close() - os.Remove(destination) - return nil, fmt.Errorf("failed connecting to destination database: %v", err) - } - - defer desttx.Rollback() - - //Loop over both old buckets and set them in the new - buckets := [][]byte{dbConf, dbLogs} - for _, b := range buckets { - srcB := srctx.Bucket(b) - destB := desttx.Bucket(b) - err = srcB.ForEach(func(k, v []byte) error { - return destB.Put(k, v) - }) - if err != nil { - destDb.Close() - os.Remove(destination) - return nil, fmt.Errorf("failed to copy %v bucket: %v", string(b), err) - } - } - - //If the commit fails, clean up - if err := desttx.Commit(); err != nil { - destDb.Close() - os.Remove(destination) - return nil, fmt.Errorf("failed commiting data to destination: %v", err) - } - - return destDb, nil - -} diff --git a/v2/bolt_store_test.go b/v2/bolt_store_test.go index 7a55de0..52b74fe 100644 --- a/v2/bolt_store_test.go +++ b/v2/bolt_store_test.go @@ -4,13 +4,11 @@ import ( "bytes" "io/ioutil" "os" - "path/filepath" "reflect" "testing" "time" "github.com/hashicorp/raft" - v1 "github.com/hashicorp/raft-boltdb" "go.etcd.io/bbolt" ) @@ -416,58 +414,3 @@ func TestBoltStore_SetUint64_GetUint64(t *testing.T) { t.Fatalf("bad: %v", val) } } - -func TestBoltStore_MigrateToV2(t *testing.T) { - - dir, err := ioutil.TempDir("", t.Name()) - if err != nil { - t.Fatalf("err: %s", err) - } - defer os.RemoveAll(dir) - - srcFile := filepath.Join(dir, "/sourcepath") - destFile := filepath.Join(dir, "/destpath") - - // Successfully creates and returns a store - srcDb, err := v1.NewBoltStore(srcFile) - if err != nil { - t.Fatalf("failed creating source database: %s", err) - } - defer srcDb.Close() - - // Set a mock raft log - logs := []*raft.Log{ - testRaftLog(1, "log1"), - testRaftLog(2, "log2"), - testRaftLog(3, "log3"), - } - - //Store logs source - if err := srcDb.StoreLogs(logs); err != nil { - t.Fatalf("failed storing logs in source database: %s", err) - } - srcResult := new(raft.Log) - if err := srcDb.GetLog(2, srcResult); err != nil { - t.Fatalf("failed getting log from source database: %s", err) - } - - if err := srcDb.Close(); err != nil { - t.Fatalf("failed closing source database: %s", err) - } - - destDb, err := MigrateToV2(srcFile, destFile) - if err != nil { - t.Fatalf("did not migrate successfully, err %v", err) - } - defer destDb.Close() - - destResult := new(raft.Log) - if err := destDb.GetLog(2, destResult); err != nil { - t.Fatalf("failed getting log from destination database: %s", err) - } - - if !reflect.DeepEqual(srcResult, destResult) { - t.Errorf("BoltDB log did not equal Bbolt log, Boltdb %v, Bbolt: %v", srcResult, destResult) - } - -}