forked from TrueCloudLab/restic
s3: Fix backend for Google Cloud Storage
This commit is contained in:
parent
ea66ae0811
commit
6a948d5afd
2 changed files with 55 additions and 18 deletions
|
@ -4,8 +4,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"restic"
|
"restic"
|
||||||
|
@ -16,7 +14,6 @@ import (
|
||||||
"restic/errors"
|
"restic/errors"
|
||||||
|
|
||||||
"github.com/minio/minio-go"
|
"github.com/minio/minio-go"
|
||||||
"github.com/minio/minio-go/pkg/s3utils"
|
|
||||||
|
|
||||||
"restic/debug"
|
"restic/debug"
|
||||||
)
|
)
|
||||||
|
@ -166,17 +163,51 @@ func (be *Backend) Path() string {
|
||||||
return be.cfg.Prefix
|
return be.cfg.Prefix
|
||||||
}
|
}
|
||||||
|
|
||||||
func (be *Backend) isGoogleCloudStorage() bool {
|
// nopCloserFile wraps *os.File and overwrites the Close() method with method
|
||||||
scheme := "https://"
|
// that does nothing. In addition, the method Len() is implemented, which
|
||||||
if be.cfg.UseHTTP {
|
// returns the size of the file (filesize - current offset).
|
||||||
scheme = "http://"
|
type nopCloserFile struct {
|
||||||
}
|
*os.File
|
||||||
url, err := url.Parse(scheme + be.cfg.Endpoint)
|
}
|
||||||
|
|
||||||
|
func (f nopCloserFile) Close() error {
|
||||||
|
debug.Log("prevented Close()")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the remaining length of the file (filesize - current offset).
|
||||||
|
func (f nopCloserFile) Len() int {
|
||||||
|
debug.Log("Len() called")
|
||||||
|
fi, err := f.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return s3utils.IsGoogleEndpoint(*url)
|
pos, err := f.Seek(0, io.SeekCurrent)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
size := fi.Size() - pos
|
||||||
|
debug.Log("returning file size %v", size)
|
||||||
|
return int(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
type lenner interface {
|
||||||
|
Len() int
|
||||||
|
io.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
// nopCloserLenner wraps a lenner and overwrites the Close() method with method
|
||||||
|
// that does nothing. In addition, the method Size() is implemented, which
|
||||||
|
// returns the size of the file (filesize - current offset).
|
||||||
|
type nopCloserLenner struct {
|
||||||
|
lenner
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *nopCloserLenner) Close() error {
|
||||||
|
debug.Log("prevented Close()")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save stores data in the backend at the handle.
|
// Save stores data in the backend at the handle.
|
||||||
|
@ -196,9 +227,15 @@ func (be *Backend) Save(ctx context.Context, h restic.Handle, rd io.Reader) (err
|
||||||
return errors.New("key already exists")
|
return errors.New("key already exists")
|
||||||
}
|
}
|
||||||
|
|
||||||
// prevent GCS from closing the file
|
// prevent the HTTP client from closing a file
|
||||||
if be.isGoogleCloudStorage() {
|
if f, ok := rd.(*os.File); ok {
|
||||||
rd = ioutil.NopCloser(rd)
|
debug.Log("reader is %#T, using nopCloserFile{}", rd)
|
||||||
|
rd = nopCloserFile{f}
|
||||||
|
} else if l, ok := rd.(lenner); ok {
|
||||||
|
debug.Log("reader is %#T, using nopCloserLenner{}", rd)
|
||||||
|
rd = nopCloserLenner{l}
|
||||||
|
} else {
|
||||||
|
debug.Log("reader is %#T, no specific workaround enabled", rd)
|
||||||
}
|
}
|
||||||
|
|
||||||
be.sem.GetToken()
|
be.sem.GetToken()
|
||||||
|
|
|
@ -241,8 +241,8 @@ func (s *Suite) TestLoad(t *testing.T) {
|
||||||
|
|
||||||
type errorCloser struct {
|
type errorCloser struct {
|
||||||
io.Reader
|
io.Reader
|
||||||
size int64
|
l int
|
||||||
t testing.TB
|
t testing.TB
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec errorCloser) Close() error {
|
func (ec errorCloser) Close() error {
|
||||||
|
@ -250,8 +250,8 @@ func (ec errorCloser) Close() error {
|
||||||
return errors.New("forbidden method close was called")
|
return errors.New("forbidden method close was called")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec errorCloser) Size() int64 {
|
func (ec errorCloser) Len() int {
|
||||||
return ec.size
|
return ec.l
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestSave tests saving data in the backend.
|
// TestSave tests saving data in the backend.
|
||||||
|
@ -325,7 +325,7 @@ func (s *Suite) TestSave(t *testing.T) {
|
||||||
|
|
||||||
// wrap the tempfile in an errorCloser, so we can detect if the backend
|
// wrap the tempfile in an errorCloser, so we can detect if the backend
|
||||||
// closes the reader
|
// closes the reader
|
||||||
err = b.Save(context.TODO(), h, errorCloser{t: t, size: int64(length), Reader: tmpfile})
|
err = b.Save(context.TODO(), h, errorCloser{t: t, l: length, Reader: tmpfile})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue