diff --git a/pkg/meta/backend.go b/pkg/meta/backend.go index 17265ae6be..84358eb4e5 100644 --- a/pkg/meta/backend.go +++ b/pkg/meta/backend.go @@ -24,6 +24,10 @@ type Backend interface { // Path returns the path with the specified key. Path(key string) string + + // Close releases all resources used by the store + // It does not make any changes to store. + Close() error } // Register registers a backend to be daemon's store. diff --git a/pkg/meta/boltdb.go b/pkg/meta/boltdb.go index 45049c24a6..d5f996f31b 100644 --- a/pkg/meta/boltdb.go +++ b/pkg/meta/boltdb.go @@ -160,3 +160,9 @@ func (b *bolt) List(bucket string) ([][]byte, error) { return values, err } + +// Close releases all database resources. +// All transactions must be closed before closing the database. +func (b *bolt) Close() error { + return b.db.Close() +} diff --git a/pkg/meta/local.go b/pkg/meta/local.go index f6cbd1dec6..727d6c3e5e 100644 --- a/pkg/meta/local.go +++ b/pkg/meta/local.go @@ -177,6 +177,11 @@ func (s *localStore) Keys(fileName string) ([]string, error) { return keys, nil } +// Close do nothing in local store +func (s *localStore) Close() error { + return nil +} + func mkdirIfNotExist(dir string) error { if _, err := os.Stat(dir); err != nil { if os.IsNotExist(err) { diff --git a/pkg/meta/store.go b/pkg/meta/store.go index fb2b6afca7..13a034306b 100644 --- a/pkg/meta/store.go +++ b/pkg/meta/store.go @@ -270,3 +270,8 @@ func (s *Store) KeysWithPrefix(prefix string) ([]string, error) { func (s *Store) Path(key string) string { return s.backend.Path(key) } + +// Shutdown releases all resources used by the backend +func (s *Store) Shutdown() error { + return s.backend.Close() +} diff --git a/pkg/meta/store_test.go b/pkg/meta/store_test.go index c5ad138cde..8d3ffd86bd 100644 --- a/pkg/meta/store_test.go +++ b/pkg/meta/store_test.go @@ -1,8 +1,13 @@ package meta import ( + "fmt" + "os" + "path" "reflect" "testing" + + "github.com/alibaba/pouch/pkg/utils" ) type Demo struct { @@ -117,20 +122,41 @@ func (d *Demo3) Key() string { return d.B } -var boltdbStore *Store +func initBoltDBStore(dbFile string) (*Store, error) { + boltdbCfg := Config{ + Driver: "boltdb", + BaseDir: dbFile, + Buckets: []Bucket{ + {"boltdb", reflect.TypeOf(Demo3{})}, + }, + } -var boltdbCfg = Config{ - Driver: "boltdb", - BaseDir: "/tmp/bolt.db", - Buckets: []Bucket{ - {"boltdb", reflect.TypeOf(Demo3{})}, - }, + return NewStore(boltdbCfg) +} + +func ensureFileNotExist(file string) error { + _, err := os.Stat(file) + if err == nil { + os.Remove(file) + return nil + } + + if !os.IsNotExist(err) { + return err + } + + return nil } func TestBoltdbPut(t *testing.T) { // initialize - var err error - boltdbStore, err = NewStore(boltdbCfg) + dbFile := path.Join("/tmp", utils.RandString(8, "TestBoltdbPut", "")) + if err := ensureFileNotExist(dbFile); err != nil { + t.Fatal(err) + } + defer ensureFileNotExist(dbFile) + + boltdbStore, err := initBoltDBStore(dbFile) if err != nil { t.Fatal(err) } @@ -159,6 +185,26 @@ func TestBoltdbPut(t *testing.T) { } func TestBoltdbGet(t *testing.T) { + // initialize + dbFile := path.Join("/tmp", utils.RandString(8, "TestBoltdbGet", "")) + if err := ensureFileNotExist(dbFile); err != nil { + t.Fatal(err) + } + defer ensureFileNotExist(dbFile) + + boltdbStore, err := initBoltDBStore(dbFile) + if err != nil { + t.Fatal(err) + } + + // first put 1 + if err := boltdbStore.Put(&Demo3{ + A: 1, + B: "key", + }); err != nil { + t.Fatal(err) + } + obj, err := boltdbStore.Get("key") if err != nil { t.Fatal(err) @@ -173,6 +219,26 @@ func TestBoltdbGet(t *testing.T) { } func TestBoltdbFetch(t *testing.T) { + // initialize + dbFile := path.Join("/tmp", utils.RandString(8, "TestBoltdbFetch", "")) + if err := ensureFileNotExist(dbFile); err != nil { + t.Fatal(err) + } + defer ensureFileNotExist(dbFile) + + boltdbStore, err := initBoltDBStore(dbFile) + if err != nil { + t.Fatal(err) + } + + // first put 2 + if err := boltdbStore.Put(&Demo3{ + A: 2, + B: "key2", + }); err != nil { + t.Fatal(err) + } + d := &Demo3{} d.B = "key2" @@ -186,21 +252,84 @@ func TestBoltdbFetch(t *testing.T) { } func TestBoltdbList(t *testing.T) { + // initialize + dbFile := path.Join("/tmp", utils.RandString(8, "TestBoltdbList", "")) + if err := ensureFileNotExist(dbFile); err != nil { + t.Fatal(err) + } + defer ensureFileNotExist(dbFile) + + boltdbStore, err := initBoltDBStore(dbFile) + if err != nil { + t.Fatal(err) + } + + // first put 2 + if err := boltdbStore.Put(&Demo3{ + A: 2, + B: "key2", + }); err != nil { + t.Fatal(err) + } + objs, err := boltdbStore.List() if err != nil { t.Fatal(err) } - if len(objs) != 2 { + if len(objs) != 1 { t.Fatalf("failed to list") } } func TestBoltdbRemove(t *testing.T) { + // initialize + dbFile := path.Join("/tmp", utils.RandString(8, "TestBoltdbRemove", "")) + if err := ensureFileNotExist(dbFile); err != nil { + t.Fatal(err) + } + defer ensureFileNotExist(dbFile) + + boltdbStore, err := initBoltDBStore(dbFile) + if err != nil { + t.Fatal(err) + } + + // first put 1 + if err := boltdbStore.Put(&Demo3{ + A: 1, + B: "key", + }); err != nil { + t.Fatal(err) + } + if err := boltdbStore.Remove("key"); err != nil { t.Fatal(err) } } +func TestBoltdbClose(t *testing.T) { + // initialize + dbFile := path.Join("/tmp", utils.RandString(8, "TestBoltdbClose", "")) + if err := ensureFileNotExist(dbFile); err != nil { + t.Fatal(err) + } + defer ensureFileNotExist(dbFile) + + boltdbStore, err := initBoltDBStore(dbFile) + if err != nil { + t.Fatal(err) + } + if err := boltdbStore.Shutdown(); err != nil { + t.Fatal(err) + } + + // test List again, should occur error here + _, err = boltdbStore.List() + if err == nil { + t.Fatal(fmt.Errorf("still can visit the boltdb after execute close db action")) + } +} + type Demo4 struct { A int B string