diff --git a/pkg/donutbox/donutbox.go b/pkg/donutbox/donutbox.go index 49d40a3eb..822d94442 100644 --- a/pkg/donutbox/donutbox.go +++ b/pkg/donutbox/donutbox.go @@ -9,15 +9,15 @@ type DonutBox interface { // bucket operations CreateBucket(bucket string) error - ListObjects(bucket, prefix string) ([]string, error) - GetBucketMetadata(bucket, name string) (io.Reader, error) - SetBucketMetadata(bucket, name string, metadata io.Reader) error + ListObjectsInBucket(bucket, prefix string) ([]string, error) + GetBucketMetadata(bucket string) (map[string]string, error) + SetBucketMetadata(bucket string, metadata map[string]string) error // object operations GetObjectWriter(bucket, object string, column, blockSize uint) *io.PipeWriter GetObjectReader(bucket, object string, column int) (io.Reader, error) - StoreObjectMetadata(bucket, object, name string, reader io.Reader) error - GetObjectMetadata(bucket, object, name string) (io.Reader, error) + SetObjectMetadata(bucket, object string, metadata map[string]string) error + GetObjectMetadata(bucket, object string) (map[string]string, error) } // Result is a result for async tasks diff --git a/pkg/donutbox/donutmem/donutmem.go b/pkg/donutbox/donutmem/donutmem.go index 67b871f22..2c9c76f7e 100644 --- a/pkg/donutbox/donutmem/donutmem.go +++ b/pkg/donutbox/donutmem/donutmem.go @@ -39,11 +39,19 @@ func NewDonutMem() donutbox.DonutBox { // system operations func (donutMem donutMem) ListBuckets() ([]string, error) { - return nil, errors.New("Not Implemented") + donutMem.lock.RLock() + defer donutMem.lock.RUnlock() + buckets := make([]string, 0) + for k, _ := range donutMem.buckets { + buckets = append(buckets, k) + } + return buckets, nil } // bucket operations func (donutMem donutMem) CreateBucket(b string) error { + donutMem.lock.Lock() + defer donutMem.lock.Unlock() b = strings.ToLower(b) if _, ok := donutMem.buckets[b]; ok { return errors.New("Bucket Exists") @@ -60,16 +68,53 @@ func (donutMem donutMem) CreateBucket(b string) error { return nil } -func (donutMem donutMem) ListObjects(bucket, prefix string) ([]string, error) { - return nil, errors.New("Not Implemented") +func (donutMem donutMem) ListObjectsInBucket(bucketKey, prefixKey string) ([]string, error) { + donutMem.lock.RLock() + defer donutMem.lock.RUnlock() + if curBucket, ok := donutMem.buckets[bucketKey]; ok { + curBucket.lock.RLock() + defer curBucket.lock.RUnlock() + objects := make([]string, 0) + for objectKey, _ := range curBucket.objects { + if strings.HasPrefix(objectKey, prefixKey) { + objects = append(objects, objectKey) + } + } + return objects, nil + } + return nil, errors.New("Bucket does not exist") } -func (donutMem donutMem) GetBucketMetadata(bucket, name string) (io.Reader, error) { - return nil, errors.New("Not Implemented") +func (donutMem donutMem) GetBucketMetadata(bucketKey string) (map[string]string, error) { + donutMem.lock.RLock() + defer donutMem.lock.RUnlock() + + if curBucket, ok := donutMem.buckets[bucketKey]; ok { + curBucket.lock.RLock() + defer curBucket.lock.RUnlock() + result := make(map[string]string) + for k, v := range curBucket.metadata { + result[k] = v + } + return result, nil + } + return nil, errors.New("Bucket not found") } -func (donutMem donutMem) SetBucketMetadata(bucket, name string, metadata io.Reader) error { - return errors.New("Not Implemented") +func (donutMem donutMem) SetBucketMetadata(bucketKey string, metadata map[string]string) error { + donutMem.lock.RLock() + defer donutMem.lock.RUnlock() + if curBucket, ok := donutMem.buckets[bucketKey]; ok { + curBucket.lock.Lock() + defer curBucket.lock.Unlock() + newMetadata := make(map[string]string) + for k, v := range metadata { + newMetadata[k] = v + } + curBucket.metadata = newMetadata + return nil + } + return errors.New("Bucket not found") } // object operations @@ -137,10 +182,44 @@ func (donutMem donutMem) GetObjectReader(bucket, key string, column int) (io.Rea return nil, errors.New("Bucket not found") } -func (donutMem donutMem) StoreObjectMetadata(bucket, object, name string, reader io.Reader) error { - return errors.New("Not Implemented") +func (donutMem donutMem) SetObjectMetadata(bucketKey, objectKey string, metadata map[string]string) error { + donutMem.lock.RLock() + defer donutMem.lock.RUnlock() + if curBucket, ok := donutMem.buckets[bucketKey]; ok { + curBucket.lock.RLock() + defer curBucket.lock.RUnlock() + if curObject, ok := curBucket.objects[objectKey]; ok { + curObject.lock.Lock() + defer curObject.lock.Unlock() + newMetadata := make(map[string]string) + for k, v := range metadata { + newMetadata[k] = v + } + curObject.metadata = newMetadata + return nil + } + return errors.New("Object not found") + } + return errors.New("Bucket not found") } -func (donutMem donutMem) GetObjectMetadata(bucket, object, name string) (io.Reader, error) { - return nil, errors.New("Not Implemented") +func (donutMem donutMem) GetObjectMetadata(bucketKey, objectKey string) (map[string]string, error) { + donutMem.lock.RLock() + defer donutMem.lock.RUnlock() + + if curBucket, ok := donutMem.buckets[bucketKey]; ok { + curBucket.lock.RLock() + defer curBucket.lock.RUnlock() + if curObject, ok := curBucket.objects[objectKey]; ok { + curObject.lock.RLock() + defer curObject.lock.RUnlock() + result := make(map[string]string) + for k, v := range curObject.metadata { + result[k] = v + } + return result, nil + } + return nil, errors.New("Object not found") + } + return nil, errors.New("Bucket not found") } diff --git a/pkg/donutbox/donutmem/donutmem_test.go b/pkg/donutbox/donutmem/donutmem_test.go index e3ecadd5a..e657aca17 100644 --- a/pkg/donutbox/donutmem/donutmem_test.go +++ b/pkg/donutbox/donutmem/donutmem_test.go @@ -5,6 +5,8 @@ import ( . "gopkg.in/check.v1" "io/ioutil" + "sort" + "strconv" ) func Test(t *testing.T) { TestingT(t) } @@ -13,7 +15,7 @@ type MySuite struct{} var _ = Suite(&MySuite{}) -func (s *MySuite) TestAPISuite(c *C) { +func (s *MySuite) TestCreateAndReadObject(c *C) { data := "Hello World" donut := NewDonutMem() @@ -52,5 +54,95 @@ func (s *MySuite) TestAPISuite(c *C) { c.Assert(err, IsNil) result, err = ioutil.ReadAll(reader) c.Assert(result, DeepEquals, []byte(data)) +} + +func (s *MySuite) TestBucketList(c *C) { + donut := NewDonutMem() + + results, err := donut.ListBuckets() + c.Assert(len(results), Equals, 0) + + buckets := make([]string, 0) + for i := 0; i < 10; i++ { + bucket := "foo" + strconv.Itoa(i) + buckets = append(buckets, bucket) + err := donut.CreateBucket(bucket) + c.Assert(err, IsNil) + } + sort.Strings(buckets) + results, err = donut.ListBuckets() + c.Assert(err, IsNil) + sort.Strings(results) + c.Assert(results, DeepEquals, buckets) +} + +func (s *MySuite) TestObjectList(c *C) { + donut := NewDonutMem() + donut.CreateBucket("foo") + + results, err := donut.ListObjectsInBucket("foo", "") + c.Assert(len(results), Equals, 0) + + objects := make([]string, 0) + for i := 0; i < 10; i++ { + object := "foo" + strconv.Itoa(i) + objects = append(objects, object) + writer := donut.GetObjectWriter("foo", object, 0, 2) + writer.Write([]byte(object)) + writer.Close() + c.Assert(err, IsNil) + } + sort.Strings(objects) + results, err = donut.ListObjectsInBucket("foo", "") + c.Assert(err, IsNil) + c.Assert(len(results), Equals, 10) + sort.Strings(results) + c.Assert(results, DeepEquals, objects) +} + +func (s *MySuite) TestBucketMetadata(c *C) { + donut := NewDonutMem() + donut.CreateBucket("foo") + + metadata := make(map[string]string) + + metadata["hello"] = "world" + metadata["foo"] = "bar" + + err := donut.SetBucketMetadata("foo", metadata) + c.Assert(err, IsNil) + + result, err := donut.GetBucketMetadata("foo") + c.Assert(result, DeepEquals, metadata) +} + +func (s *MySuite) TestObjectMetadata(c *C) { + donut := NewDonutMem() + donut.CreateBucket("foo") + + metadata := make(map[string]string) + + metadata["hello"] = "world" + metadata["foo"] = "bar" + + err := donut.SetObjectMetadata("foo", "bar", metadata) + c.Assert(err, Not(IsNil)) + + result, err := donut.GetObjectMetadata("foo", "bar") + c.Assert(result, IsNil) + c.Assert(err, Not(IsNil)) + + writer := donut.GetObjectWriter("foo", "bar", 0, 2) + _, err = writer.Write([]byte("Hello World")) + c.Assert(err, IsNil) + err = writer.Close() + c.Assert(err, IsNil) + + err = donut.SetObjectMetadata("foo", "bar", metadata) + c.Assert(err, IsNil) + + result, err = donut.GetObjectMetadata("foo", "bar") + c.Assert(err, IsNil) + c.Assert(result, DeepEquals, metadata) } diff --git a/pkg/storage/encoded/encoded.go b/pkg/storage/encoded/encoded.go index 78c327b6a..e68fd5c78 100644 --- a/pkg/storage/encoded/encoded.go +++ b/pkg/storage/encoded/encoded.go @@ -29,10 +29,11 @@ type StorageDriver struct { } // Start a single disk subsystem -func Start() (chan<- string, <-chan error, storage.Storage) { +func Start(donutBox donutbox.DonutBox) (chan<- string, <-chan error, storage.Storage) { ctrlChannel := make(chan string) errorChannel := make(chan error) s := new(StorageDriver) + s.donutBox = donutBox go start(ctrlChannel, errorChannel, s) return ctrlChannel, errorChannel, s } diff --git a/pkg/storage/encoded/encoded_test.go b/pkg/storage/encoded/encoded_test.go index ca9b57149..aaa17ec03 100644 --- a/pkg/storage/encoded/encoded_test.go +++ b/pkg/storage/encoded/encoded_test.go @@ -23,6 +23,7 @@ import ( mstorage "github.com/minio-io/minio/pkg/storage" + "github.com/minio-io/minio/pkg/donutbox/donutmem" . "gopkg.in/check.v1" ) @@ -33,13 +34,13 @@ type MySuite struct{} var _ = Suite(&MySuite{}) func (s *MySuite) TestAPISuite(c *C) { - c.Skip("Not implemented yet.") + //c.Skip("Not implemented yet.") var storageList []string create := func() mstorage.Storage { path, err := ioutil.TempDir(os.TempDir(), "minio-fs-") c.Check(err, IsNil) storageList = append(storageList, path) - _, _, store := Start() // TODO Make InMemory driver + _, _, store := Start(donutmem.NewDonutMem()) // TODO Make InMemory driver return store } mstorage.APITestSuite(c, create)