Better error handling and annotation

This commit is contained in:
Alexander Neumann 2014-10-07 23:19:26 +02:00
parent cbee80fc6d
commit 9b75f2cab0
3 changed files with 46 additions and 29 deletions

View file

@ -1,5 +1,7 @@
package backend package backend
import "errors"
type Type string type Type string
const ( const (
@ -14,6 +16,10 @@ const (
BackendVersion = 1 BackendVersion = 1
) )
var (
ErrAlreadyPresent = errors.New("blob is already present in backend")
)
type Server interface { type Server interface {
Create(Type, []byte) (ID, error) Create(Type, []byte) (ID, error)
Get(Type, ID) ([]byte, error) Get(Type, ID) ([]byte, error)

View file

@ -8,6 +8,8 @@ import (
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
"github.com/juju/arrar"
) )
const ( const (
@ -131,7 +133,7 @@ func CreateLocal(dir string) (*Local, error) {
return nil, err return nil, err
} }
// open repository // open backend
return OpenLocal(dir) return OpenLocal(dir)
} }
@ -169,12 +171,23 @@ func (b *Local) dir(t Type) string {
return filepath.Join(b.p, n) return filepath.Join(b.p, n)
} }
// Create stores new content of type t and data and returns the ID. // Create stores new content of type t and data and returns the ID. If the blob
// is already present, returns ErrAlreadyPresent and the blob's ID.
func (b *Local) Create(t Type, data []byte) (ID, error) { func (b *Local) Create(t Type, data []byte) (ID, error) {
// TODO: make sure that tempfile is removed upon error // TODO: make sure that tempfile is removed upon error
// create tempfile in repository // check if blob is already present in backend
var err error id := IDFromData(data)
res, err := b.Test(t, id)
if err != nil {
return nil, arrar.Annotate(err, "test for presence")
}
if res {
return id, ErrAlreadyPresent
}
// create tempfile in backend
file, err := b.tempFile() file, err := b.tempFile()
if err != nil { if err != nil {
return nil, err return nil, err
@ -192,7 +205,6 @@ func (b *Local) Create(t Type, data []byte) (ID, error) {
} }
// return id // return id
id := IDFromData(data)
err = b.renameFile(file, t, id) err = b.renameFile(file, t, id)
if err != nil { if err != nil {
return nil, err return nil, err
@ -282,7 +294,7 @@ func (b *Local) Version() uint {
return b.ver return b.ver
} }
// Close closes the repository // Close closes the backend
func (b *Local) Close() error { func (b *Local) Close() error {
return nil return nil
} }

View file

@ -14,6 +14,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/juju/arrar"
"github.com/pkg/sftp" "github.com/pkg/sftp"
) )
@ -71,7 +72,7 @@ func start_client(program string, args ...string) (*SFTP, error) {
} }
// OpenSFTP opens an sftp backend. When the command is started via // OpenSFTP opens an sftp backend. When the command is started via
// exec.Command, it is expected to speak sftp on stdin/stdout. The repository // exec.Command, it is expected to speak sftp on stdin/stdout. The backend
// is expected at the given path. // is expected at the given path.
func OpenSFTP(dir string, program string, args ...string) (*SFTP, error) { func OpenSFTP(dir string, program string, args ...string) (*SFTP, error) {
sftp, err := start_client(program, args...) sftp, err := start_client(program, args...)
@ -117,10 +118,6 @@ func OpenSFTP(dir string, program string, args ...string) (*SFTP, error) {
return nil, fmt.Errorf("unable to convert version to integer: %v\n", err) return nil, fmt.Errorf("unable to convert version to integer: %v\n", err)
} }
if version != BackendVersion {
return nil, fmt.Errorf("wrong version %d", version)
}
// check version // check version
if version != BackendVersion { if version != BackendVersion {
return nil, fmt.Errorf("wrong version %d", version) return nil, fmt.Errorf("wrong version %d", version)
@ -201,7 +198,7 @@ func CreateSFTP(dir string, program string, args ...string) (*SFTP, error) {
return nil, err return nil, err
} }
// open repository // open backend
return OpenSFTP(dir, program, args...) return OpenSFTP(dir, program, args...)
} }
@ -258,33 +255,38 @@ func (r *SFTP) dir(t Type) string {
return filepath.Join(r.p, n) return filepath.Join(r.p, n)
} }
// Create stores new content of type t and data and returns the ID. // Create stores new content of type t and data and returns the ID. If the blob
// is already present, returns ErrAlreadyPresent and the blob's ID.
func (r *SFTP) Create(t Type, data []byte) (ID, error) { func (r *SFTP) Create(t Type, data []byte) (ID, error) {
// TODO: make sure that tempfile is removed upon error // TODO: make sure that tempfile is removed upon error
// create tempfile in repository // check if blob is already present in backend
var err error id := IDFromData(data)
if ok, _ := r.Test(t, id); ok {
return id, ErrAlreadyPresent
}
// create tempfile in backend
filename, file, err := r.tempFile() filename, file, err := r.tempFile()
if err != nil { if err != nil {
return nil, err return nil, arrar.Annotate(err, "create tempfile")
} }
// write data to tempfile // write data to tempfile
_, err = file.Write(data) _, err = file.Write(data)
if err != nil { if err != nil {
return nil, err return nil, arrar.Annotate(err, "writing data to tempfile")
} }
err = file.Close() err = file.Close()
if err != nil { if err != nil {
return nil, err return nil, arrar.Annotate(err, "close tempfile")
} }
// return id // return id
id := IDFromData(data)
err = r.renameFile(filename, t, id) err = r.renameFile(filename, t, id)
if err != nil { if err != nil {
return nil, err return nil, arrar.Annotate(err, "rename file")
} }
return id, nil return id, nil
@ -315,20 +317,17 @@ func (r *SFTP) Get(t Type, id ID) ([]byte, error) {
// Test returns true if a blob of the given type and ID exists in the backend. // Test returns true if a blob of the given type and ID exists in the backend.
func (r *SFTP) Test(t Type, id ID) (bool, error) { func (r *SFTP) Test(t Type, id ID) (bool, error) {
// try to open file
file, err := r.c.Open(r.filename(t, id)) file, err := r.c.Open(r.filename(t, id))
defer func() { defer func() {
file.Close() if file != nil {
file.Close()
}
}() }()
if err != nil { if err == nil {
if os.IsNotExist(err) { return true, nil
return false, nil
}
return false, err
} }
return false, err
return true, nil
} }
// Remove removes the content stored at ID. // Remove removes the content stored at ID.