2
2
mirror of https://github.com/octoleo/restic.git synced 2024-06-07 03:20:49 +00:00

Add REST backend option to use CA root certificate

Closes #1114.
This commit is contained in:
Fabian Wickborn 2017-09-24 20:04:23 +02:00
parent 1dcfd64028
commit 69a6e622d0
12 changed files with 135 additions and 55 deletions

View File

@ -10,6 +10,7 @@ import (
"strings" "strings"
"syscall" "syscall"
"github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/backend/azure" "github.com/restic/restic/internal/backend/azure"
"github.com/restic/restic/internal/backend/b2" "github.com/restic/restic/internal/backend/b2"
"github.com/restic/restic/internal/backend/gs" "github.com/restic/restic/internal/backend/gs"
@ -41,6 +42,7 @@ type GlobalOptions struct {
JSON bool JSON bool
CacheDir string CacheDir string
NoCache bool NoCache bool
CACerts []string
ctx context.Context ctx context.Context
password string password string
@ -73,6 +75,7 @@ func init() {
f.BoolVarP(&globalOptions.JSON, "json", "", false, "set output mode to JSON for commands that support it") f.BoolVarP(&globalOptions.JSON, "json", "", false, "set output mode to JSON for commands that support it")
f.StringVar(&globalOptions.CacheDir, "cache-dir", "", "set the cache directory") f.StringVar(&globalOptions.CacheDir, "cache-dir", "", "set the cache directory")
f.BoolVar(&globalOptions.NoCache, "no-cache", false, "do not use a local cache") f.BoolVar(&globalOptions.NoCache, "no-cache", false, "do not use a local cache")
f.StringSliceVar(&globalOptions.CACerts, "cacert", nil, "path to load root certificates from (default: use system certificates)")
f.StringSliceVarP(&globalOptions.Options, "option", "o", []string{}, "set extended option (`key=value`, can be specified multiple times)") f.StringSliceVarP(&globalOptions.Options, "option", "o", []string{}, "set extended option (`key=value`, can be specified multiple times)")
restoreTerminal() restoreTerminal()
@ -485,23 +488,28 @@ func open(s string, opts options.Options) (restic.Backend, error) {
return nil, err return nil, err
} }
rt, err := backend.Transport(globalOptions.CACerts)
if err != nil {
return nil, err
}
switch loc.Scheme { switch loc.Scheme {
case "local": case "local":
be, err = local.Open(cfg.(local.Config)) be, err = local.Open(cfg.(local.Config))
case "sftp": case "sftp":
be, err = sftp.Open(cfg.(sftp.Config), SuspendSignalHandler, InstallSignalHandler) be, err = sftp.Open(cfg.(sftp.Config), SuspendSignalHandler, InstallSignalHandler)
case "s3": case "s3":
be, err = s3.Open(cfg.(s3.Config)) be, err = s3.Open(cfg.(s3.Config), rt)
case "gs": case "gs":
be, err = gs.Open(cfg.(gs.Config)) be, err = gs.Open(cfg.(gs.Config))
case "azure": case "azure":
be, err = azure.Open(cfg.(azure.Config)) be, err = azure.Open(cfg.(azure.Config), rt)
case "swift": case "swift":
be, err = swift.Open(cfg.(swift.Config)) be, err = swift.Open(cfg.(swift.Config), rt)
case "b2": case "b2":
be, err = b2.Open(cfg.(b2.Config)) be, err = b2.Open(cfg.(b2.Config), rt)
case "rest": case "rest":
be, err = rest.Open(cfg.(rest.Config)) be, err = rest.Open(cfg.(rest.Config), rt)
default: default:
return nil, errors.Fatalf("invalid backend: %q", loc.Scheme) return nil, errors.Fatalf("invalid backend: %q", loc.Scheme)
@ -537,23 +545,28 @@ func create(s string, opts options.Options) (restic.Backend, error) {
return nil, err return nil, err
} }
rt, err := backend.Transport(globalOptions.CACerts)
if err != nil {
return nil, err
}
switch loc.Scheme { switch loc.Scheme {
case "local": case "local":
return local.Create(cfg.(local.Config)) return local.Create(cfg.(local.Config))
case "sftp": case "sftp":
return sftp.Create(cfg.(sftp.Config), SuspendSignalHandler, InstallSignalHandler) return sftp.Create(cfg.(sftp.Config), SuspendSignalHandler, InstallSignalHandler)
case "s3": case "s3":
return s3.Create(cfg.(s3.Config)) return s3.Create(cfg.(s3.Config), rt)
case "gs": case "gs":
return gs.Create(cfg.(gs.Config)) return gs.Create(cfg.(gs.Config))
case "azure": case "azure":
return azure.Create(cfg.(azure.Config)) return azure.Create(cfg.(azure.Config), rt)
case "swift": case "swift":
return swift.Open(cfg.(swift.Config)) return swift.Open(cfg.(swift.Config), rt)
case "b2": case "b2":
return b2.Create(cfg.(b2.Config)) return b2.Create(cfg.(b2.Config), rt)
case "rest": case "rest":
return rest.Create(cfg.(rest.Config)) return rest.Create(cfg.(rest.Config), rt)
} }
debug.Log("invalid repository scheme: %v", s) debug.Log("invalid repository scheme: %v", s)

View File

@ -30,7 +30,7 @@ const defaultListMaxItems = 5000
// make sure that *Backend implements backend.Backend // make sure that *Backend implements backend.Backend
var _ restic.Backend = &Backend{} var _ restic.Backend = &Backend{}
func open(cfg Config) (*Backend, error) { func open(cfg Config, rt http.RoundTripper) (*Backend, error) {
debug.Log("open, config %#v", cfg) debug.Log("open, config %#v", cfg)
client, err := storage.NewBasicClient(cfg.AccountName, cfg.AccountKey) client, err := storage.NewBasicClient(cfg.AccountName, cfg.AccountKey)
@ -38,7 +38,7 @@ func open(cfg Config) (*Backend, error) {
return nil, errors.Wrap(err, "NewBasicClient") return nil, errors.Wrap(err, "NewBasicClient")
} }
client.HTTPClient = &http.Client{Transport: backend.Transport()} client.HTTPClient = &http.Client{Transport: rt}
service := client.GetBlobService() service := client.GetBlobService()
@ -63,14 +63,14 @@ func open(cfg Config) (*Backend, error) {
} }
// Open opens the Azure backend at specified container. // Open opens the Azure backend at specified container.
func Open(cfg Config) (restic.Backend, error) { func Open(cfg Config, rt http.RoundTripper) (restic.Backend, error) {
return open(cfg) return open(cfg, rt)
} }
// Create opens the Azure backend at specified container and creates the container if // Create opens the Azure backend at specified container and creates the container if
// it does not exist yet. // it does not exist yet.
func Create(cfg Config) (restic.Backend, error) { func Create(cfg Config, rt http.RoundTripper) (restic.Backend, error) {
be, err := open(cfg) be, err := open(cfg, rt)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "open") return nil, errors.Wrap(err, "open")

View File

@ -7,6 +7,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/backend/azure" "github.com/restic/restic/internal/backend/azure"
"github.com/restic/restic/internal/backend/test" "github.com/restic/restic/internal/backend/test"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
@ -15,6 +16,11 @@ import (
) )
func newAzureTestSuite(t testing.TB) *test.Suite { func newAzureTestSuite(t testing.TB) *test.Suite {
tr, err := backend.Transport(nil)
if err != nil {
t.Fatalf("cannot create transport for tests: %v", err)
}
return &test.Suite{ return &test.Suite{
// do not use excessive data // do not use excessive data
MinimalData: true, MinimalData: true,
@ -37,7 +43,7 @@ func newAzureTestSuite(t testing.TB) *test.Suite {
Create: func(config interface{}) (restic.Backend, error) { Create: func(config interface{}) (restic.Backend, error) {
cfg := config.(azure.Config) cfg := config.(azure.Config)
be, err := azure.Create(cfg) be, err := azure.Create(cfg, tr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -57,14 +63,15 @@ func newAzureTestSuite(t testing.TB) *test.Suite {
// OpenFn is a function that opens a previously created temporary repository. // OpenFn is a function that opens a previously created temporary repository.
Open: func(config interface{}) (restic.Backend, error) { Open: func(config interface{}) (restic.Backend, error) {
cfg := config.(azure.Config) cfg := config.(azure.Config)
return azure.Open(cfg)
return azure.Open(cfg, tr)
}, },
// CleanupFn removes data created during the tests. // CleanupFn removes data created during the tests.
Cleanup: func(config interface{}) error { Cleanup: func(config interface{}) error {
cfg := config.(azure.Config) cfg := config.(azure.Config)
be, err := azure.Open(cfg) be, err := azure.Open(cfg, tr)
if err != nil { if err != nil {
return err return err
} }

View File

@ -3,6 +3,7 @@ package b2
import ( import (
"context" "context"
"io" "io"
"net/http"
"path" "path"
"strings" "strings"
@ -29,8 +30,8 @@ const defaultListMaxItems = 1000
// ensure statically that *b2Backend implements restic.Backend. // ensure statically that *b2Backend implements restic.Backend.
var _ restic.Backend = &b2Backend{} var _ restic.Backend = &b2Backend{}
func newClient(ctx context.Context, cfg Config) (*b2.Client, error) { func newClient(ctx context.Context, cfg Config, rt http.RoundTripper) (*b2.Client, error) {
opts := []b2.ClientOption{b2.Transport(backend.Transport())} opts := []b2.ClientOption{b2.Transport(rt)}
c, err := b2.NewClient(ctx, cfg.AccountID, cfg.Key, opts...) c, err := b2.NewClient(ctx, cfg.AccountID, cfg.Key, opts...)
if err != nil { if err != nil {
@ -40,13 +41,13 @@ func newClient(ctx context.Context, cfg Config) (*b2.Client, error) {
} }
// Open opens a connection to the B2 service. // Open opens a connection to the B2 service.
func Open(cfg Config) (restic.Backend, error) { func Open(cfg Config, rt http.RoundTripper) (restic.Backend, error) {
debug.Log("cfg %#v", cfg) debug.Log("cfg %#v", cfg)
ctx, cancel := context.WithCancel(context.TODO()) ctx, cancel := context.WithCancel(context.TODO())
defer cancel() defer cancel()
client, err := newClient(ctx, cfg) client, err := newClient(ctx, cfg, rt)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -77,13 +78,13 @@ func Open(cfg Config) (restic.Backend, error) {
// Create opens a connection to the B2 service. If the bucket does not exist yet, // Create opens a connection to the B2 service. If the bucket does not exist yet,
// it is created. // it is created.
func Create(cfg Config) (restic.Backend, error) { func Create(cfg Config, rt http.RoundTripper) (restic.Backend, error) {
debug.Log("cfg %#v", cfg) debug.Log("cfg %#v", cfg)
ctx, cancel := context.WithCancel(context.TODO()) ctx, cancel := context.WithCancel(context.TODO())
defer cancel() defer cancel()
client, err := newClient(ctx, cfg) client, err := newClient(ctx, cfg, rt)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -7,6 +7,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/backend/b2" "github.com/restic/restic/internal/backend/b2"
"github.com/restic/restic/internal/backend/test" "github.com/restic/restic/internal/backend/test"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
@ -15,6 +16,11 @@ import (
) )
func newB2TestSuite(t testing.TB) *test.Suite { func newB2TestSuite(t testing.TB) *test.Suite {
tr, err := backend.Transport(nil)
if err != nil {
t.Fatalf("cannot create transport for tests: %v", err)
}
return &test.Suite{ return &test.Suite{
// do not use excessive data // do not use excessive data
MinimalData: true, MinimalData: true,
@ -39,19 +45,19 @@ func newB2TestSuite(t testing.TB) *test.Suite {
// CreateFn is a function that creates a temporary repository for the tests. // CreateFn is a function that creates a temporary repository for the tests.
Create: func(config interface{}) (restic.Backend, error) { Create: func(config interface{}) (restic.Backend, error) {
cfg := config.(b2.Config) cfg := config.(b2.Config)
return b2.Create(cfg) return b2.Create(cfg, tr)
}, },
// OpenFn is a function that opens a previously created temporary repository. // OpenFn is a function that opens a previously created temporary repository.
Open: func(config interface{}) (restic.Backend, error) { Open: func(config interface{}) (restic.Backend, error) {
cfg := config.(b2.Config) cfg := config.(b2.Config)
return b2.Open(cfg) return b2.Open(cfg, tr)
}, },
// CleanupFn removes data created during the tests. // CleanupFn removes data created during the tests.
Cleanup: func(config interface{}) error { Cleanup: func(config interface{}) error {
cfg := config.(b2.Config) cfg := config.(b2.Config)
be, err := b2.Open(cfg) be, err := b2.Open(cfg, tr)
if err != nil { if err != nil {
return err return err
} }

View File

@ -1,6 +1,10 @@
package backend package backend
import ( import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net" "net"
"net/http" "net/http"
"time" "time"
@ -8,8 +12,10 @@ import (
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
) )
// Transport returns a new http.RoundTripper with default settings applied. // Transport returns a new http.RoundTripper with default settings applied. If
func Transport() http.RoundTripper { // a custom rootCertFilename is non-empty, it must point to a valid PEM file,
// otherwise the function will return an error.
func Transport(rootCertFilenames []string) (http.RoundTripper, error) {
// copied from net/http // copied from net/http
tr := &http.Transport{ tr := &http.Transport{
Proxy: http.ProxyFromEnvironment, Proxy: http.ProxyFromEnvironment,
@ -25,6 +31,28 @@ func Transport() http.RoundTripper {
ExpectContinueTimeout: 1 * time.Second, ExpectContinueTimeout: 1 * time.Second,
} }
if rootCertFilenames == nil {
return debug.RoundTripper(tr), nil
}
p := x509.NewCertPool()
for _, filename := range rootCertFilenames {
if filename == "" {
return nil, fmt.Errorf("empty filename for root certificate supplied")
}
b, err := ioutil.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("unable to read root certificate: %v", err)
}
if ok := p.AppendCertsFromPEM(b); !ok {
return nil, fmt.Errorf("cannot parse root certificate from %q", filename)
}
}
tr.TLSClientConfig = &tls.Config{
RootCAs: p,
}
// wrap in the debug round tripper // wrap in the debug round tripper
return debug.RoundTripper(tr) return debug.RoundTripper(tr), nil
} }

View File

@ -31,8 +31,8 @@ type restBackend struct {
} }
// Open opens the REST backend with the given config. // Open opens the REST backend with the given config.
func Open(cfg Config) (restic.Backend, error) { func Open(cfg Config, rt http.RoundTripper) (restic.Backend, error) {
client := &http.Client{Transport: backend.Transport()} client := &http.Client{Transport: rt}
sem, err := backend.NewSemaphore(cfg.Connections) sem, err := backend.NewSemaphore(cfg.Connections)
if err != nil { if err != nil {
@ -56,8 +56,8 @@ func Open(cfg Config) (restic.Backend, error) {
} }
// Create creates a new REST on server configured in config. // Create creates a new REST on server configured in config.
func Create(cfg Config) (restic.Backend, error) { func Create(cfg Config, rt http.RoundTripper) (restic.Backend, error) {
be, err := Open(cfg) be, err := Open(cfg, rt)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -10,6 +10,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/backend/rest" "github.com/restic/restic/internal/backend/rest"
"github.com/restic/restic/internal/backend/test" "github.com/restic/restic/internal/backend/test"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
@ -61,6 +62,11 @@ func runRESTServer(ctx context.Context, t testing.TB, dir string) func() {
} }
func newTestSuite(ctx context.Context, t testing.TB) *test.Suite { func newTestSuite(ctx context.Context, t testing.TB) *test.Suite {
tr, err := backend.Transport(nil)
if err != nil {
t.Fatalf("cannot create transport for tests: %v", err)
}
return &test.Suite{ return &test.Suite{
// NewConfig returns a config for a new temporary backend that will be used in tests. // NewConfig returns a config for a new temporary backend that will be used in tests.
NewConfig: func() (interface{}, error) { NewConfig: func() (interface{}, error) {
@ -84,13 +90,13 @@ func newTestSuite(ctx context.Context, t testing.TB) *test.Suite {
// CreateFn is a function that creates a temporary repository for the tests. // CreateFn is a function that creates a temporary repository for the tests.
Create: func(config interface{}) (restic.Backend, error) { Create: func(config interface{}) (restic.Backend, error) {
cfg := config.(rest.Config) cfg := config.(rest.Config)
return rest.Create(cfg) return rest.Create(cfg, tr)
}, },
// OpenFn is a function that opens a previously created temporary repository. // OpenFn is a function that opens a previously created temporary repository.
Open: func(config interface{}) (restic.Backend, error) { Open: func(config interface{}) (restic.Backend, error) {
cfg := config.(rest.Config) cfg := config.(rest.Config)
return rest.Open(cfg) return rest.Open(cfg, tr)
}, },
// CleanupFn removes data created during the tests. // CleanupFn removes data created during the tests.

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"io" "io"
"net/http"
"os" "os"
"path" "path"
"strings" "strings"
@ -32,7 +33,7 @@ var _ restic.Backend = &Backend{}
const defaultLayout = "default" const defaultLayout = "default"
func open(cfg Config) (*Backend, error) { func open(cfg Config, rt http.RoundTripper) (*Backend, error) {
debug.Log("open, config %#v", cfg) debug.Log("open, config %#v", cfg)
if cfg.MaxRetries > 0 { if cfg.MaxRetries > 0 {
@ -70,7 +71,7 @@ func open(cfg Config) (*Backend, error) {
cfg: cfg, cfg: cfg,
} }
client.SetCustomTransport(backend.Transport()) client.SetCustomTransport(rt)
l, err := backend.ParseLayout(be, cfg.Layout, defaultLayout, cfg.Prefix) l, err := backend.ParseLayout(be, cfg.Layout, defaultLayout, cfg.Prefix)
if err != nil { if err != nil {
@ -84,14 +85,14 @@ func open(cfg Config) (*Backend, error) {
// Open opens the S3 backend at bucket and region. The bucket is created if it // Open opens the S3 backend at bucket and region. The bucket is created if it
// does not exist yet. // does not exist yet.
func Open(cfg Config) (restic.Backend, error) { func Open(cfg Config, rt http.RoundTripper) (restic.Backend, error) {
return open(cfg) return open(cfg, rt)
} }
// Create opens the S3 backend at bucket and region and creates the bucket if // Create opens the S3 backend at bucket and region and creates the bucket if
// it does not exist yet. // it does not exist yet.
func Create(cfg Config) (restic.Backend, error) { func Create(cfg Config, rt http.RoundTripper) (restic.Backend, error) {
be, err := open(cfg) be, err := open(cfg, rt)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "open") return nil, errors.Wrap(err, "open")
} }

View File

@ -8,12 +8,14 @@ import (
"fmt" "fmt"
"io" "io"
"net" "net"
"net/http"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"testing" "testing"
"time" "time"
"github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/backend/s3" "github.com/restic/restic/internal/backend/s3"
"github.com/restic/restic/internal/backend/test" "github.com/restic/restic/internal/backend/test"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
@ -103,9 +105,9 @@ type MinioTestConfig struct {
stopServer func() stopServer func()
} }
func createS3(t testing.TB, cfg MinioTestConfig) (be restic.Backend, err error) { func createS3(t testing.TB, cfg MinioTestConfig, tr http.RoundTripper) (be restic.Backend, err error) {
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
be, err = s3.Create(cfg.Config) be, err = s3.Create(cfg.Config, tr)
if err != nil { if err != nil {
t.Logf("s3 open: try %d: error %v", i, err) t.Logf("s3 open: try %d: error %v", i, err)
time.Sleep(500 * time.Millisecond) time.Sleep(500 * time.Millisecond)
@ -119,6 +121,11 @@ func createS3(t testing.TB, cfg MinioTestConfig) (be restic.Backend, err error)
} }
func newMinioTestSuite(ctx context.Context, t testing.TB) *test.Suite { func newMinioTestSuite(ctx context.Context, t testing.TB) *test.Suite {
tr, err := backend.Transport(nil)
if err != nil {
t.Fatalf("cannot create transport for tests: %v", err)
}
return &test.Suite{ return &test.Suite{
// NewConfig returns a config for a new temporary backend that will be used in tests. // NewConfig returns a config for a new temporary backend that will be used in tests.
NewConfig: func() (interface{}, error) { NewConfig: func() (interface{}, error) {
@ -142,7 +149,7 @@ func newMinioTestSuite(ctx context.Context, t testing.TB) *test.Suite {
Create: func(config interface{}) (restic.Backend, error) { Create: func(config interface{}) (restic.Backend, error) {
cfg := config.(MinioTestConfig) cfg := config.(MinioTestConfig)
be, err := createS3(t, cfg) be, err := createS3(t, cfg, tr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -162,7 +169,7 @@ func newMinioTestSuite(ctx context.Context, t testing.TB) *test.Suite {
// OpenFn is a function that opens a previously created temporary repository. // OpenFn is a function that opens a previously created temporary repository.
Open: func(config interface{}) (restic.Backend, error) { Open: func(config interface{}) (restic.Backend, error) {
cfg := config.(MinioTestConfig) cfg := config.(MinioTestConfig)
return s3.Open(cfg.Config) return s3.Open(cfg.Config, tr)
}, },
// CleanupFn removes data created during the tests. // CleanupFn removes data created during the tests.
@ -214,6 +221,11 @@ func BenchmarkBackendMinio(t *testing.B) {
} }
func newS3TestSuite(t testing.TB) *test.Suite { func newS3TestSuite(t testing.TB) *test.Suite {
tr, err := backend.Transport(nil)
if err != nil {
t.Fatalf("cannot create transport for tests: %v", err)
}
return &test.Suite{ return &test.Suite{
// do not use excessive data // do not use excessive data
MinimalData: true, MinimalData: true,
@ -236,7 +248,7 @@ func newS3TestSuite(t testing.TB) *test.Suite {
Create: func(config interface{}) (restic.Backend, error) { Create: func(config interface{}) (restic.Backend, error) {
cfg := config.(s3.Config) cfg := config.(s3.Config)
be, err := s3.Create(cfg) be, err := s3.Create(cfg, tr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -256,14 +268,14 @@ func newS3TestSuite(t testing.TB) *test.Suite {
// OpenFn is a function that opens a previously created temporary repository. // OpenFn is a function that opens a previously created temporary repository.
Open: func(config interface{}) (restic.Backend, error) { Open: func(config interface{}) (restic.Backend, error) {
cfg := config.(s3.Config) cfg := config.(s3.Config)
return s3.Open(cfg) return s3.Open(cfg, tr)
}, },
// CleanupFn removes data created during the tests. // CleanupFn removes data created during the tests.
Cleanup: func(config interface{}) error { Cleanup: func(config interface{}) error {
cfg := config.(s3.Config) cfg := config.(s3.Config)
be, err := s3.Open(cfg) be, err := s3.Open(cfg, tr)
if err != nil { if err != nil {
return err return err
} }

View File

@ -34,7 +34,7 @@ var _ restic.Backend = &beSwift{}
// Open opens the swift backend at a container in region. The container is // Open opens the swift backend at a container in region. The container is
// created if it does not exist yet. // created if it does not exist yet.
func Open(cfg Config) (restic.Backend, error) { func Open(cfg Config, rt http.RoundTripper) (restic.Backend, error) {
debug.Log("config %#v", cfg) debug.Log("config %#v", cfg)
sem, err := backend.NewSemaphore(cfg.Connections) sem, err := backend.NewSemaphore(cfg.Connections)
@ -58,7 +58,7 @@ func Open(cfg Config) (restic.Backend, error) {
ConnectTimeout: time.Minute, ConnectTimeout: time.Minute,
Timeout: time.Minute, Timeout: time.Minute,
Transport: backend.Transport(), Transport: rt,
}, },
sem: sem, sem: sem,
container: cfg.Container, container: cfg.Container,

View File

@ -7,6 +7,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/backend/swift" "github.com/restic/restic/internal/backend/swift"
"github.com/restic/restic/internal/backend/test" "github.com/restic/restic/internal/backend/test"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
@ -15,6 +16,11 @@ import (
) )
func newSwiftTestSuite(t testing.TB) *test.Suite { func newSwiftTestSuite(t testing.TB) *test.Suite {
tr, err := backend.Transport(nil)
if err != nil {
t.Fatalf("cannot create transport for tests: %v", err)
}
return &test.Suite{ return &test.Suite{
// do not use excessive data // do not use excessive data
MinimalData: true, MinimalData: true,
@ -55,7 +61,7 @@ func newSwiftTestSuite(t testing.TB) *test.Suite {
Create: func(config interface{}) (restic.Backend, error) { Create: func(config interface{}) (restic.Backend, error) {
cfg := config.(swift.Config) cfg := config.(swift.Config)
be, err := swift.Open(cfg) be, err := swift.Open(cfg, tr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -75,14 +81,14 @@ func newSwiftTestSuite(t testing.TB) *test.Suite {
// OpenFn is a function that opens a previously created temporary repository. // OpenFn is a function that opens a previously created temporary repository.
Open: func(config interface{}) (restic.Backend, error) { Open: func(config interface{}) (restic.Backend, error) {
cfg := config.(swift.Config) cfg := config.(swift.Config)
return swift.Open(cfg) return swift.Open(cfg, tr)
}, },
// CleanupFn removes data created during the tests. // CleanupFn removes data created during the tests.
Cleanup: func(config interface{}) error { Cleanup: func(config interface{}) error {
cfg := config.(swift.Config) cfg := config.(swift.Config)
be, err := swift.Open(cfg) be, err := swift.Open(cfg, tr)
if err != nil { if err != nil {
return err return err
} }