From 919b40c6cf1cfa919bfdf32b98008509a4687782 Mon Sep 17 00:00:00 2001 From: Alexander Neumann Date: Sat, 23 Jan 2016 23:27:58 +0100 Subject: [PATCH] Add Stat() method to backend interface --- backend/interface.go | 8 ++++++++ backend/local/local.go | 14 ++++++++++++++ backend/mem/mem_backend.go | 26 ++++++++++++++++++++++++++ backend/mock_backend.go | 9 +++++++++ backend/s3/s3.go | 19 +++++++++++++++++++ backend/sftp/sftp.go | 14 ++++++++++++++ backend/test/tests.go | 7 +++++++ 7 files changed, 97 insertions(+) diff --git a/backend/interface.go b/backend/interface.go index 1fdacb4d7..f215c6733 100644 --- a/backend/interface.go +++ b/backend/interface.go @@ -43,6 +43,9 @@ type Backend interface { // Load returns the data stored in the backend for h at the given offset // and saves it in p. Load has the same semantics as io.ReaderAt. Load(h Handle, p []byte, off int64) (int, error) + + // Stat returns information about the blob identified by h. + Stat(h Handle) (BlobInfo, error) } // Lister implements listing data items stored in a backend. @@ -59,6 +62,11 @@ type Deleter interface { Delete() error } +// BlobInfo is returned by Stat() and contains information about a stored blob. +type BlobInfo struct { + Size int64 +} + // Blob is old. type Blob interface { io.Writer diff --git a/backend/local/local.go b/backend/local/local.go index 25ba26779..554710d37 100644 --- a/backend/local/local.go +++ b/backend/local/local.go @@ -252,6 +252,20 @@ func (b *Local) Load(h backend.Handle, p []byte, off int64) (n int, err error) { return io.ReadFull(f, p) } +// Stat returns information about a blob. +func (b *Local) Stat(h backend.Handle) (backend.BlobInfo, error) { + if err := h.Valid(); err != nil { + return backend.BlobInfo{}, err + } + + fi, err := os.Stat(filename(b.p, h.Type, h.Name)) + if err != nil { + return backend.BlobInfo{}, err + } + + return backend.BlobInfo{Size: fi.Size()}, nil +} + // Test returns true if a blob of the given type and name exists in the backend. func (b *Local) Test(t backend.Type, name string) (bool, error) { _, err := os.Stat(filename(b.p, t, name)) diff --git a/backend/mem/mem_backend.go b/backend/mem/mem_backend.go index e231c2ede..6e80735e3 100644 --- a/backend/mem/mem_backend.go +++ b/backend/mem/mem_backend.go @@ -49,6 +49,10 @@ func New() *MemoryBackend { return memLoad(be, h, p, off) } + be.MockBackend.StatFn = func(h backend.Handle) (backend.BlobInfo, error) { + return memStat(be, h) + } + be.MockBackend.RemoveFn = func(t backend.Type, name string) error { return memRemove(be, t, name) } @@ -195,6 +199,28 @@ func memLoad(be *MemoryBackend, h backend.Handle, p []byte, off int64) (int, err return n, nil } +func memStat(be *MemoryBackend, h backend.Handle) (backend.BlobInfo, error) { + be.m.Lock() + defer be.m.Unlock() + + if err := h.Valid(); err != nil { + return backend.BlobInfo{}, err + } + + if h.Type == backend.Config { + h.Name = "" + } + + debug.Log("MemoryBackend.Stat", "stat %v", h) + + e, ok := be.data[entry{h.Type, h.Name}] + if !ok { + return backend.BlobInfo{}, errors.New("no such data") + } + + return backend.BlobInfo{Size: int64(len(e))}, nil +} + func memRemove(be *MemoryBackend, t backend.Type, name string) error { be.m.Lock() defer be.m.Unlock() diff --git a/backend/mock_backend.go b/backend/mock_backend.go index 744cb7902..4d142f846 100644 --- a/backend/mock_backend.go +++ b/backend/mock_backend.go @@ -11,6 +11,7 @@ type MockBackend struct { CloseFn func() error CreateFn func() (Blob, error) LoadFn func(h Handle, p []byte, off int64) (int, error) + StatFn func(h Handle) (BlobInfo, error) GetReaderFn func(Type, string, uint, uint) (io.ReadCloser, error) ListFn func(Type, <-chan struct{}) <-chan string RemoveFn func(Type, string) error @@ -51,6 +52,14 @@ func (m *MockBackend) Load(h Handle, p []byte, off int64) (int, error) { return m.LoadFn(h, p, off) } +func (m *MockBackend) Stat(h Handle) (BlobInfo, error) { + if m.StatFn == nil { + return BlobInfo{}, errors.New("not implemented") + } + + return m.StatFn(h) +} + func (m *MockBackend) GetReader(t Type, name string, offset, len uint) (io.ReadCloser, error) { if m.GetReaderFn == nil { return nil, errors.New("not implemented") diff --git a/backend/s3/s3.go b/backend/s3/s3.go index 2d3f89606..444fe2a73 100644 --- a/backend/s3/s3.go +++ b/backend/s3/s3.go @@ -191,6 +191,25 @@ func (be S3Backend) Load(h backend.Handle, p []byte, off int64) (int, error) { return io.ReadFull(obj, p) } +// Stat returns information about a blob. +func (be S3Backend) Stat(h backend.Handle) (backend.BlobInfo, error) { + debug.Log("s3.Stat", "%v") + path := s3path(h.Type, h.Name) + obj, err := be.client.GetObject(be.bucketname, path) + if err != nil { + debug.Log("s3.Stat", "GetObject() err %v", err) + return backend.BlobInfo{}, err + } + + fi, err := obj.Stat() + if err != nil { + debug.Log("s3.Stat", "Stat() err %v", err) + return backend.BlobInfo{}, err + } + + return backend.BlobInfo{Size: fi.Size}, nil +} + // Test returns true if a blob of the given type and name exists in the backend. func (be *S3Backend) Test(t backend.Type, name string) (bool, error) { found := false diff --git a/backend/sftp/sftp.go b/backend/sftp/sftp.go index a3d32e3d6..f23ec1db5 100644 --- a/backend/sftp/sftp.go +++ b/backend/sftp/sftp.go @@ -395,6 +395,20 @@ func (r *SFTP) Load(h backend.Handle, p []byte, off int64) (n int, err error) { return io.ReadFull(f, p) } +// Stat returns information about a blob. +func (r *SFTP) Stat(h backend.Handle) (backend.BlobInfo, error) { + if err := h.Valid(); err != nil { + return backend.BlobInfo{}, err + } + + fi, err := r.c.Lstat(r.filename(h.Type, h.Name)) + if err != nil { + return backend.BlobInfo{}, err + } + + return backend.BlobInfo{Size: fi.Size()}, nil +} + // Test returns true if a blob of the given type and name exists in the backend. func (r *SFTP) Test(t backend.Type, name string) (bool, error) { _, err := r.c.Lstat(r.filename(t, name)) diff --git a/backend/test/tests.go b/backend/test/tests.go index 2631fe168..bd12fdc4a 100644 --- a/backend/test/tests.go +++ b/backend/test/tests.go @@ -374,6 +374,13 @@ func TestWrite(t testing.TB) { t.Fatalf("data not equal") } + fi, err := b.Stat(backend.Handle{Type: backend.Data, Name: name}) + OK(t, err) + + if fi.Size != int64(len(data)) { + t.Fatalf("Stat() returned different size, want %q, got %d", len(data), fi.Size) + } + err = b.Remove(backend.Data, name) if err != nil { t.Fatalf("error removing item: %v", err)