mirror of
https://github.com/restic/restic.git
synced 2026-10-25 01:58:54 +00:00
global: split CreateRepository and OpenRepository into smaller functions
This commit is contained in:
1 parent
588c40aaef
commit
b6aef592f5
1 file changed
+120
-45
+120
-45
@@ -3,6 +3,7 @@ package global
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -293,20 +294,54 @@ func OpenRepository(ctx context.Context, gopts Options, printer progress.Printer
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if config is there
|
err = hasRepositoryConfig(ctx, be, repo, gopts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := createRepositoryInstance(be, gopts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = decryptRepository(ctx, s, &gopts, printer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
printRepositoryInfo(s, gopts, printer)
|
||||||
|
|
||||||
|
if gopts.NoCache {
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = setupCache(s, gopts, printer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasRepositoryConfig checks if the repository config file exists and is not empty.
|
||||||
|
func hasRepositoryConfig(ctx context.Context, be backend.Backend, repo string, gopts Options) error {
|
||||||
fi, err := be.Stat(ctx, backend.Handle{Type: restic.ConfigFile})
|
fi, err := be.Stat(ctx, backend.Handle{Type: restic.ConfigFile})
|
||||||
if be.IsNotExist(err) {
|
if be.IsNotExist(err) {
|
||||||
//nolint:staticcheck // capitalized error string is intentional
|
//nolint:staticcheck // capitalized error string is intentional
|
||||||
return nil, fmt.Errorf("Fatal: %w: unable to open config file: %v\nIs there a repository at the following location?\n%v", ErrNoRepository, err, location.StripPassword(gopts.Backends, repo))
|
return fmt.Errorf("Fatal: %w: unable to open config file: %v\nIs there a repository at the following location?\n%v", ErrNoRepository, err, location.StripPassword(gopts.Backends, repo))
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Fatalf("unable to open config file: %v\nIs there a repository at the following location?\n%v", err, location.StripPassword(gopts.Backends, repo))
|
return errors.Fatalf("unable to open config file: %v\n%v", err, location.StripPassword(gopts.Backends, repo))
|
||||||
}
|
}
|
||||||
|
|
||||||
if fi.Size == 0 {
|
if fi.Size == 0 {
|
||||||
return nil, errors.New("config file has zero size, invalid repository?")
|
return errors.New("config file has zero size, invalid repository?")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// createRepositoryInstance creates a new repository instance with the given options.
|
||||||
|
func createRepositoryInstance(be backend.Backend, gopts Options) (*repository.Repository, error) {
|
||||||
s, err := repository.New(be, repository.Options{
|
s, err := repository.New(be, repository.Options{
|
||||||
Compression: gopts.Compression,
|
Compression: gopts.Compression,
|
||||||
PackSize: gopts.PackSize * 1024 * 1024,
|
PackSize: gopts.PackSize * 1024 * 1024,
|
||||||
@@ -315,16 +350,21 @@ func OpenRepository(ctx context.Context, gopts Options, printer progress.Printer
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Fatalf("%s", err)
|
return nil, errors.Fatalf("%s", err)
|
||||||
}
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// decryptRepository handles password reading and decrypts the repository.
|
||||||
|
func decryptRepository(ctx context.Context, s *repository.Repository, gopts *Options, printer progress.Printer) error {
|
||||||
passwordTriesLeft := 1
|
passwordTriesLeft := 1
|
||||||
if gopts.Term.InputIsTerminal() && gopts.Password == "" && !gopts.InsecureNoPassword {
|
if gopts.Term.InputIsTerminal() && gopts.Password == "" && !gopts.InsecureNoPassword {
|
||||||
passwordTriesLeft = 3
|
passwordTriesLeft = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
for ; passwordTriesLeft > 0; passwordTriesLeft-- {
|
for ; passwordTriesLeft > 0; passwordTriesLeft-- {
|
||||||
gopts.Password, err = readPassword(ctx, gopts, "enter password for repository: ")
|
gopts.Password, err = readPassword(ctx, *gopts, "enter password for repository: ")
|
||||||
if ctx.Err() != nil {
|
if ctx.Err() != nil {
|
||||||
return nil, ctx.Err()
|
return ctx.Err()
|
||||||
}
|
}
|
||||||
if err != nil && passwordTriesLeft > 1 {
|
if err != nil && passwordTriesLeft > 1 {
|
||||||
gopts.Password = ""
|
gopts.Password = ""
|
||||||
@@ -342,11 +382,16 @@ func OpenRepository(ctx context.Context, gopts Options, printer progress.Printer
|
|||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.IsFatal(err) || errors.Is(err, repository.ErrNoKeyFound) {
|
if errors.IsFatal(err) || errors.Is(err, repository.ErrNoKeyFound) {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
return nil, errors.Fatalf("%s", err)
|
return errors.Fatalf("%s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// printRepositoryInfo displays the repository ID, version and compression level.
|
||||||
|
func printRepositoryInfo(s *repository.Repository, gopts Options, printer progress.Printer) {
|
||||||
id := s.Config().ID
|
id := s.Config().ID
|
||||||
if len(id) > 8 {
|
if len(id) > 8 {
|
||||||
id = id[:8]
|
id = id[:8]
|
||||||
@@ -356,15 +401,14 @@ func OpenRepository(ctx context.Context, gopts Options, printer progress.Printer
|
|||||||
extra = ", compression level " + gopts.Compression.String()
|
extra = ", compression level " + gopts.Compression.String()
|
||||||
}
|
}
|
||||||
printer.PT("repository %v opened (version %v%s)", id, s.Config().Version, extra)
|
printer.PT("repository %v opened (version %v%s)", id, s.Config().Version, extra)
|
||||||
|
}
|
||||||
|
|
||||||
if gopts.NoCache {
|
// setupCache creates a new cache and removes old cache directories if instructed to do so.
|
||||||
return s, nil
|
func setupCache(s *repository.Repository, gopts Options, printer progress.Printer) error {
|
||||||
}
|
|
||||||
|
|
||||||
c, err := cache.New(s.Config().ID, gopts.CacheDir)
|
c, err := cache.New(s.Config().ID, gopts.CacheDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
printer.E("unable to open cache: %v", err)
|
printer.E("unable to open cache: %v", err)
|
||||||
return s, nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Created {
|
if c.Created {
|
||||||
@@ -381,7 +425,7 @@ func OpenRepository(ctx context.Context, gopts Options, printer progress.Printer
|
|||||||
|
|
||||||
// nothing more to do if no old cache dirs could be found
|
// nothing more to do if no old cache dirs could be found
|
||||||
if len(oldCacheDirs) == 0 {
|
if len(oldCacheDirs) == 0 {
|
||||||
return s, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// cleanup old cache dirs if instructed to do so
|
// cleanup old cache dirs if instructed to do so
|
||||||
@@ -398,24 +442,7 @@ func OpenRepository(ctx context.Context, gopts Options, printer progress.Printer
|
|||||||
printer.PT("found %d old cache directories in %v, run `restic cache --cleanup` to remove them",
|
printer.PT("found %d old cache directories in %v, run `restic cache --cleanup` to remove them",
|
||||||
len(oldCacheDirs), c.Base)
|
len(oldCacheDirs), c.Base)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseConfig(loc location.Location, opts options.Options) (interface{}, error) {
|
|
||||||
cfg := loc.Config
|
|
||||||
if cfg, ok := cfg.(backend.ApplyEnvironmenter); ok {
|
|
||||||
cfg.ApplyEnvironment("")
|
|
||||||
}
|
|
||||||
|
|
||||||
// only apply options for a particular backend here
|
|
||||||
opts = opts.Extract(loc.Scheme)
|
|
||||||
if err := opts.Apply(loc.Scheme, cfg); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
debug.Log("opening %v repository at %#v", loc.Scheme, cfg)
|
|
||||||
return cfg, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateRepository a repository with the given version and chunker polynomial.
|
// CreateRepository a repository with the given version and chunker polynomial.
|
||||||
@@ -441,12 +468,9 @@ func CreateRepository(ctx context.Context, gopts Options, version uint, chunkerP
|
|||||||
return nil, errors.Fatalf("create repository at %s failed: %v", location.StripPassword(gopts.Backends, repo), err)
|
return nil, errors.Fatalf("create repository at %s failed: %v", location.StripPassword(gopts.Backends, repo), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s, err := repository.New(be, repository.Options{
|
s, err := createRepositoryInstance(be, gopts)
|
||||||
Compression: gopts.Compression,
|
|
||||||
PackSize: gopts.PackSize * 1024 * 1024,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Fatalf("%s", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.Init(ctx, version, gopts.Password, chunkerPolynomial)
|
err = s.Init(ctx, version, gopts.Password, chunkerPolynomial)
|
||||||
@@ -459,31 +483,75 @@ func CreateRepository(ctx context.Context, gopts Options, version uint, chunkerP
|
|||||||
|
|
||||||
func innerOpenBackend(ctx context.Context, s string, gopts Options, opts options.Options, create bool, printer progress.Printer) (backend.Backend, error) {
|
func innerOpenBackend(ctx context.Context, s string, gopts Options, opts options.Options, create bool, printer progress.Printer) (backend.Backend, error) {
|
||||||
debug.Log("parsing location %v", location.StripPassword(gopts.Backends, s))
|
debug.Log("parsing location %v", location.StripPassword(gopts.Backends, s))
|
||||||
loc, err := location.Parse(gopts.Backends, s)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Fatalf("parsing repository location failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg, err := parseConfig(loc, opts)
|
scheme, cfg, err := parseConfig(gopts.Backends, s, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rt, lim, err := setupTransport(gopts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
be, err := createOrOpenBackend(ctx, scheme, cfg, rt, lim, gopts, s, create, printer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
be, err = wrapBackend(be, gopts, printer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return be, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseConfig parses the repository location and extended options and returns the scheme and configuration.
|
||||||
|
func parseConfig(backends *location.Registry, s string, opts options.Options) (string, interface{}, error) {
|
||||||
|
loc, err := location.Parse(backends, s)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, errors.Fatalf("parsing repository location failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := loc.Config
|
||||||
|
if cfg, ok := cfg.(backend.ApplyEnvironmenter); ok {
|
||||||
|
cfg.ApplyEnvironment("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// only apply options for a particular backend here
|
||||||
|
opts = opts.Extract(loc.Scheme)
|
||||||
|
if err := opts.Apply(loc.Scheme, cfg); err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
debug.Log("opening %v repository at %#v", loc.Scheme, cfg)
|
||||||
|
return loc.Scheme, cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// setupTransport creates and configures the transport with rate limiting.
|
||||||
|
func setupTransport(gopts Options) (http.RoundTripper, limiter.Limiter, error) {
|
||||||
rt, err := backend.Transport(gopts.TransportOptions)
|
rt, err := backend.Transport(gopts.TransportOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Fatalf("%s", err)
|
return nil, nil, errors.Fatalf("%s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// wrap the transport so that the throughput via HTTP is limited
|
// wrap the transport so that the throughput via HTTP is limited
|
||||||
lim := limiter.NewStaticLimiter(gopts.Limits)
|
lim := limiter.NewStaticLimiter(gopts.Limits)
|
||||||
rt = lim.Transport(rt)
|
rt = lim.Transport(rt)
|
||||||
|
|
||||||
factory := gopts.Backends.Lookup(loc.Scheme)
|
return rt, lim, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// createOrOpenBackend creates or opens a backend using the appropriate factory method.
|
||||||
|
func createOrOpenBackend(ctx context.Context, scheme string, cfg interface{}, rt http.RoundTripper, lim limiter.Limiter, gopts Options, s string, create bool, printer progress.Printer) (backend.Backend, error) {
|
||||||
|
factory := gopts.Backends.Lookup(scheme)
|
||||||
if factory == nil {
|
if factory == nil {
|
||||||
return nil, errors.Fatalf("invalid backend: %q", loc.Scheme)
|
return nil, errors.Fatalf("invalid backend: %q", scheme)
|
||||||
}
|
}
|
||||||
|
|
||||||
var be backend.Backend
|
var be backend.Backend
|
||||||
|
var err error
|
||||||
if create {
|
if create {
|
||||||
be, err = factory.Create(ctx, cfg, rt, lim, printer.E)
|
be, err = factory.Create(ctx, cfg, rt, lim, printer.E)
|
||||||
} else {
|
} else {
|
||||||
@@ -502,11 +570,17 @@ func innerOpenBackend(ctx context.Context, s string, gopts Options, opts options
|
|||||||
return nil, errors.Fatalf("unable to open repository at %v: %v", location.StripPassword(gopts.Backends, s), err)
|
return nil, errors.Fatalf("unable to open repository at %v: %v", location.StripPassword(gopts.Backends, s), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return be, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrapBackend applies debug logging, test hooks, and retry wrapper to the backend.
|
||||||
|
func wrapBackend(be backend.Backend, gopts Options, printer progress.Printer) (backend.Backend, error) {
|
||||||
// wrap with debug logging and connection limiting
|
// wrap with debug logging and connection limiting
|
||||||
be = logger.New(sema.NewBackend(be))
|
be = logger.New(sema.NewBackend(be))
|
||||||
|
|
||||||
// wrap backend if a test specified an inner hook
|
// wrap backend if a test specified an inner hook
|
||||||
if gopts.BackendInnerTestHook != nil {
|
if gopts.BackendInnerTestHook != nil {
|
||||||
|
var err error
|
||||||
be, err = gopts.BackendInnerTestHook(be)
|
be, err = gopts.BackendInnerTestHook(be)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -527,6 +601,7 @@ func innerOpenBackend(ctx context.Context, s string, gopts Options, opts options
|
|||||||
|
|
||||||
// wrap backend if a test specified a hook
|
// wrap backend if a test specified a hook
|
||||||
if gopts.BackendTestHook != nil {
|
if gopts.BackendTestHook != nil {
|
||||||
|
var err error
|
||||||
be, err = gopts.BackendTestHook(be)
|
be, err = gopts.BackendTestHook(be)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
Reference in new issue
Block a user