forked from TrueCloudLab/restic
Better error handling and annotation
This commit is contained in:
parent
cbee80fc6d
commit
9b75f2cab0
3 changed files with 46 additions and 29 deletions
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in a new issue