mirror of
https://github.com/restic/restic.git
synced 2025-10-26 00:39:09 +00:00
global: split CreateRepository and OpenRepository into smaller functions
This commit is contained in:
@@ -3,6 +3,7 @@ package global
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -293,20 +294,54 @@ func OpenRepository(ctx context.Context, gopts Options, printer progress.Printer
|
||||
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})
|
||||
if be.IsNotExist(err) {
|
||||
//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 {
|
||||
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 {
|
||||
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{
|
||||
Compression: gopts.Compression,
|
||||
PackSize: gopts.PackSize * 1024 * 1024,
|
||||
@@ -315,16 +350,21 @@ func OpenRepository(ctx context.Context, gopts Options, printer progress.Printer
|
||||
if err != nil {
|
||||
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
|
||||
if gopts.Term.InputIsTerminal() && gopts.Password == "" && !gopts.InsecureNoPassword {
|
||||
passwordTriesLeft = 3
|
||||
}
|
||||
|
||||
var err error
|
||||
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 {
|
||||
return nil, ctx.Err()
|
||||
return ctx.Err()
|
||||
}
|
||||
if err != nil && passwordTriesLeft > 1 {
|
||||
gopts.Password = ""
|
||||
@@ -342,11 +382,16 @@ func OpenRepository(ctx context.Context, gopts Options, printer progress.Printer
|
||||
}
|
||||
if err != nil {
|
||||
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
|
||||
if len(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()
|
||||
}
|
||||
printer.PT("repository %v opened (version %v%s)", id, s.Config().Version, extra)
|
||||
}
|
||||
|
||||
if gopts.NoCache {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// setupCache creates a new cache and removes old cache directories if instructed to do so.
|
||||
func setupCache(s *repository.Repository, gopts Options, printer progress.Printer) error {
|
||||
c, err := cache.New(s.Config().ID, gopts.CacheDir)
|
||||
if err != nil {
|
||||
printer.E("unable to open cache: %v", err)
|
||||
return s, nil
|
||||
return err
|
||||
}
|
||||
|
||||
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
|
||||
if len(oldCacheDirs) == 0 {
|
||||
return s, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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",
|
||||
len(oldCacheDirs), c.Base)
|
||||
}
|
||||
|
||||
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
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
s, err := repository.New(be, repository.Options{
|
||||
Compression: gopts.Compression,
|
||||
PackSize: gopts.PackSize * 1024 * 1024,
|
||||
})
|
||||
s, err := createRepositoryInstance(be, gopts)
|
||||
if err != nil {
|
||||
return nil, errors.Fatalf("%s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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) {
|
||||
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 {
|
||||
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)
|
||||
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
|
||||
lim := limiter.NewStaticLimiter(gopts.Limits)
|
||||
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 {
|
||||
return nil, errors.Fatalf("invalid backend: %q", loc.Scheme)
|
||||
return nil, errors.Fatalf("invalid backend: %q", scheme)
|
||||
}
|
||||
|
||||
var be backend.Backend
|
||||
var err error
|
||||
if create {
|
||||
be, err = factory.Create(ctx, cfg, rt, lim, printer.E)
|
||||
} 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 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
|
||||
be = logger.New(sema.NewBackend(be))
|
||||
|
||||
// wrap backend if a test specified an inner hook
|
||||
if gopts.BackendInnerTestHook != nil {
|
||||
var err error
|
||||
be, err = gopts.BackendInnerTestHook(be)
|
||||
if err != nil {
|
||||
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
|
||||
if gopts.BackendTestHook != nil {
|
||||
var err error
|
||||
be, err = gopts.BackendTestHook(be)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
Reference in New Issue
Block a user