From 9b75f2cab0609b4a57b1b3fb4835f3ffe72460a0 Mon Sep 17 00:00:00 2001 From: Alexander Neumann Date: Tue, 7 Oct 2014 23:19:26 +0200 Subject: [PATCH] Better error handling and annotation --- backend/interface.go | 6 ++++++ backend/local.go | 24 +++++++++++++++++------ backend/sftp.go | 45 ++++++++++++++++++++++---------------------- 3 files changed, 46 insertions(+), 29 deletions(-) diff --git a/backend/interface.go b/backend/interface.go index b6ca94d70..d3787d59b 100644 --- a/backend/interface.go +++ b/backend/interface.go @@ -1,5 +1,7 @@ package backend +import "errors" + type Type string const ( @@ -14,6 +16,10 @@ const ( BackendVersion = 1 ) +var ( + ErrAlreadyPresent = errors.New("blob is already present in backend") +) + type Server interface { Create(Type, []byte) (ID, error) Get(Type, ID) ([]byte, error) diff --git a/backend/local.go b/backend/local.go index 71e28549b..a87bb55f1 100644 --- a/backend/local.go +++ b/backend/local.go @@ -8,6 +8,8 @@ import ( "path/filepath" "strconv" "strings" + + "github.com/juju/arrar" ) const ( @@ -131,7 +133,7 @@ func CreateLocal(dir string) (*Local, error) { return nil, err } - // open repository + // open backend return OpenLocal(dir) } @@ -169,12 +171,23 @@ func (b *Local) dir(t Type) string { 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) { // TODO: make sure that tempfile is removed upon error - // create tempfile in repository - var err error + // check if blob is already present in backend + 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() if err != nil { return nil, err @@ -192,7 +205,6 @@ func (b *Local) Create(t Type, data []byte) (ID, error) { } // return id - id := IDFromData(data) err = b.renameFile(file, t, id) if err != nil { return nil, err @@ -282,7 +294,7 @@ func (b *Local) Version() uint { return b.ver } -// Close closes the repository +// Close closes the backend func (b *Local) Close() error { return nil } diff --git a/backend/sftp.go b/backend/sftp.go index 09290fe69..1e11c2fd5 100644 --- a/backend/sftp.go +++ b/backend/sftp.go @@ -14,6 +14,7 @@ import ( "strconv" "strings" + "github.com/juju/arrar" "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 -// 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. func OpenSFTP(dir string, program string, args ...string) (*SFTP, error) { 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) } - if version != BackendVersion { - return nil, fmt.Errorf("wrong version %d", version) - } - // check version if version != BackendVersion { 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 } - // open repository + // open backend return OpenSFTP(dir, program, args...) } @@ -258,33 +255,38 @@ func (r *SFTP) dir(t Type) string { 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) { // TODO: make sure that tempfile is removed upon error - // create tempfile in repository - var err error + // check if blob is already present in backend + id := IDFromData(data) + if ok, _ := r.Test(t, id); ok { + return id, ErrAlreadyPresent + } + + // create tempfile in backend filename, file, err := r.tempFile() if err != nil { - return nil, err + return nil, arrar.Annotate(err, "create tempfile") } // write data to tempfile _, err = file.Write(data) if err != nil { - return nil, err + return nil, arrar.Annotate(err, "writing data to tempfile") } err = file.Close() if err != nil { - return nil, err + return nil, arrar.Annotate(err, "close tempfile") } // return id - id := IDFromData(data) err = r.renameFile(filename, t, id) if err != nil { - return nil, err + return nil, arrar.Annotate(err, "rename file") } 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. func (r *SFTP) Test(t Type, id ID) (bool, error) { - // try to open file file, err := r.c.Open(r.filename(t, id)) defer func() { - file.Close() + if file != nil { + file.Close() + } }() - if err != nil { - if os.IsNotExist(err) { - return false, nil - } - return false, err + if err == nil { + return true, nil } - - return true, nil + return false, err } // Remove removes the content stored at ID.