Add Stat() method to backend interface

This commit is contained in:
Alexander Neumann 2016-01-23 23:27:58 +01:00
parent 10b03eee27
commit 919b40c6cf
7 changed files with 97 additions and 0 deletions

View file

@ -43,6 +43,9 @@ type Backend interface {
// Load returns the data stored in the backend for h at the given offset // 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. // and saves it in p. Load has the same semantics as io.ReaderAt.
Load(h Handle, p []byte, off int64) (int, error) 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. // Lister implements listing data items stored in a backend.
@ -59,6 +62,11 @@ type Deleter interface {
Delete() error Delete() error
} }
// BlobInfo is returned by Stat() and contains information about a stored blob.
type BlobInfo struct {
Size int64
}
// Blob is old. // Blob is old.
type Blob interface { type Blob interface {
io.Writer io.Writer

View file

@ -252,6 +252,20 @@ func (b *Local) Load(h backend.Handle, p []byte, off int64) (n int, err error) {
return io.ReadFull(f, p) 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. // 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) { func (b *Local) Test(t backend.Type, name string) (bool, error) {
_, err := os.Stat(filename(b.p, t, name)) _, err := os.Stat(filename(b.p, t, name))

View file

@ -49,6 +49,10 @@ func New() *MemoryBackend {
return memLoad(be, h, p, off) 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 { be.MockBackend.RemoveFn = func(t backend.Type, name string) error {
return memRemove(be, t, name) 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 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 { func memRemove(be *MemoryBackend, t backend.Type, name string) error {
be.m.Lock() be.m.Lock()
defer be.m.Unlock() defer be.m.Unlock()

View file

@ -11,6 +11,7 @@ type MockBackend struct {
CloseFn func() error CloseFn func() error
CreateFn func() (Blob, error) CreateFn func() (Blob, error)
LoadFn func(h Handle, p []byte, off int64) (int, 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) GetReaderFn func(Type, string, uint, uint) (io.ReadCloser, error)
ListFn func(Type, <-chan struct{}) <-chan string ListFn func(Type, <-chan struct{}) <-chan string
RemoveFn func(Type, string) error 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) 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) { func (m *MockBackend) GetReader(t Type, name string, offset, len uint) (io.ReadCloser, error) {
if m.GetReaderFn == nil { if m.GetReaderFn == nil {
return nil, errors.New("not implemented") return nil, errors.New("not implemented")

View file

@ -191,6 +191,25 @@ func (be S3Backend) Load(h backend.Handle, p []byte, off int64) (int, error) {
return io.ReadFull(obj, p) 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. // 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) { func (be *S3Backend) Test(t backend.Type, name string) (bool, error) {
found := false found := false

View file

@ -395,6 +395,20 @@ func (r *SFTP) Load(h backend.Handle, p []byte, off int64) (n int, err error) {
return io.ReadFull(f, p) 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. // 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) { func (r *SFTP) Test(t backend.Type, name string) (bool, error) {
_, err := r.c.Lstat(r.filename(t, name)) _, err := r.c.Lstat(r.filename(t, name))

View file

@ -374,6 +374,13 @@ func TestWrite(t testing.TB) {
t.Fatalf("data not equal") 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) err = b.Remove(backend.Data, name)
if err != nil { if err != nil {
t.Fatalf("error removing item: %v", err) t.Fatalf("error removing item: %v", err)