From c553a57e0d5eb04495d21c8f67fa640b3b8fb1b5 Mon Sep 17 00:00:00 2001 From: Alexander Neumann Date: Thu, 2 Jul 2015 22:36:31 +0200 Subject: [PATCH] repository: Refactor Config --- repository/config.go | 87 +++++++++++++++++++++++++++++++++++++++ repository/config_test.go | 53 ++++++++++++++++++++++++ repository/repository.go | 60 +++------------------------ 3 files changed, 146 insertions(+), 54 deletions(-) create mode 100644 repository/config.go create mode 100644 repository/config_test.go diff --git a/repository/config.go b/repository/config.go new file mode 100644 index 000000000..46a9e778c --- /dev/null +++ b/repository/config.go @@ -0,0 +1,87 @@ +package repository + +import ( + "crypto/rand" + "crypto/sha256" + "encoding/hex" + "errors" + "io" + + "github.com/restic/restic/backend" + "github.com/restic/restic/chunker" + "github.com/restic/restic/debug" +) + +// Config contains the configuration for a repository. +type Config struct { + Version uint `json:"version"` + ID string `json:"id"` + ChunkerPolynomial chunker.Pol `json:"chunker_polynomial"` +} + +// repositoryIDSize is the length of the ID chosen at random for a new repository. +const repositoryIDSize = sha256.Size + +// RepoVersion is the version that is written to the config when a repository +// is newly created with Init(). +const RepoVersion = 1 + +// JSONUnpackedSaver saves unpacked JSON. +type JSONUnpackedSaver interface { + SaveJSONUnpacked(backend.Type, interface{}) (backend.ID, error) +} + +// JSONUnpackedLoader loads unpacked JSON. +type JSONUnpackedLoader interface { + LoadJSONUnpacked(backend.Type, backend.ID, interface{}) error +} + +// CreateConfig creates a config file with a randomly selected polynomial and +// ID and saves the config in the repository. +func CreateConfig(r JSONUnpackedSaver) (Config, error) { + var ( + err error + cfg Config + ) + + cfg.ChunkerPolynomial, err = chunker.RandomPolynomial() + if err != nil { + return Config{}, err + } + + newID := make([]byte, repositoryIDSize) + _, err = io.ReadFull(rand.Reader, newID) + if err != nil { + return Config{}, err + } + + cfg.ID = hex.EncodeToString(newID) + cfg.Version = RepoVersion + + debug.Log("Repo.CreateConfig", "New config: %#v", cfg) + + _, err = r.SaveJSONUnpacked(backend.Config, cfg) + return cfg, err +} + +// LoadConfig returns loads, checks and returns the config for a repository. +func LoadConfig(r JSONUnpackedLoader) (Config, error) { + var ( + cfg Config + ) + + err := r.LoadJSONUnpacked(backend.Config, nil, &cfg) + if err != nil { + return Config{}, err + } + + if cfg.Version != RepoVersion { + return Config{}, errors.New("unsupported repository version") + } + + if !cfg.ChunkerPolynomial.Irreducible() { + return Config{}, errors.New("invalid chunker polynomial") + } + + return cfg, nil +} diff --git a/repository/config_test.go b/repository/config_test.go new file mode 100644 index 000000000..3b5dc381d --- /dev/null +++ b/repository/config_test.go @@ -0,0 +1,53 @@ +package repository_test + +import ( + "testing" + + "github.com/restic/restic/backend" + "github.com/restic/restic/repository" + . "github.com/restic/restic/test" +) + +type saver func(backend.Type, interface{}) (backend.ID, error) + +func (s saver) SaveJSONUnpacked(t backend.Type, arg interface{}) (backend.ID, error) { + return s(t, arg) +} + +type loader func(backend.Type, backend.ID, interface{}) error + +func (l loader) LoadJSONUnpacked(t backend.Type, id backend.ID, arg interface{}) error { + return l(t, id, arg) +} + +func TestConfig(t *testing.T) { + resultConfig := repository.Config{} + save := func(tpe backend.Type, arg interface{}) (backend.ID, error) { + Assert(t, tpe == backend.Config, + "wrong backend type: got %v, wanted %v", + tpe, backend.Config) + + cfg := arg.(repository.Config) + resultConfig = cfg + return backend.ID{}, nil + } + + cfg1, err := repository.CreateConfig(saver(save)) + OK(t, err) + + load := func(tpe backend.Type, id backend.ID, arg interface{}) error { + Assert(t, tpe == backend.Config, + "wrong backend type: got %v, wanted %v", + tpe, backend.Config) + + cfg := arg.(*repository.Config) + *cfg = resultConfig + return nil + } + + cfg2, err := repository.LoadConfig(loader(load)) + OK(t, err) + + Assert(t, cfg1 == cfg2, + "configs aren't equal: %v != %v", cfg1, cfg2) +} diff --git a/repository/repository.go b/repository/repository.go index 5817fb622..616295f2c 100644 --- a/repository/repository.go +++ b/repository/repository.go @@ -2,9 +2,7 @@ package repository import ( "bytes" - "crypto/rand" "crypto/sha256" - "encoding/hex" "encoding/json" "errors" "fmt" @@ -19,13 +17,6 @@ import ( "github.com/restic/restic/pack" ) -// Config contains the configuration for a repository. -type Config struct { - Version uint `json:"version"` - ID string `json:"id"` - ChunkerPolynomial chunker.Pol `json:"chunker_polynomial"` -} - // Repository is used to access a repository in a backend. type Repository struct { be backend.Backend @@ -526,47 +517,6 @@ func (r *Repository) loadIndex(id string) error { return nil } -const repositoryIDSize = sha256.Size -const RepoVersion = 1 - -func createConfig(r *Repository) (err error) { - r.Config.ChunkerPolynomial, err = chunker.RandomPolynomial() - if err != nil { - return err - } - - newID := make([]byte, repositoryIDSize) - _, err = io.ReadFull(rand.Reader, newID) - if err != nil { - return err - } - - r.Config.ID = hex.EncodeToString(newID) - r.Config.Version = RepoVersion - - debug.Log("Repo.createConfig", "New config: %#v", r.Config) - - _, err = r.SaveJSONUnpacked(backend.Config, r.Config) - return err -} - -func (r *Repository) loadConfig(cfg *Config) error { - err := r.LoadJSONUnpacked(backend.Config, nil, cfg) - if err != nil { - return err - } - - if cfg.Version != RepoVersion { - return errors.New("unsupported repository version") - } - - if !cfg.ChunkerPolynomial.Irreducible() { - return errors.New("invalid chunker polynomial") - } - - return nil -} - // SearchKey finds a key with the supplied password, afterwards the config is // read and parsed. func (r *Repository) SearchKey(password string) error { @@ -577,11 +527,12 @@ func (r *Repository) SearchKey(password string) error { r.key = key.master r.keyName = key.Name() - return r.loadConfig(&r.Config) + r.Config, err = LoadConfig(r) + return err } -// Init creates a new master key with the supplied password and initializes the -// repository config. +// Init creates a new master key with the supplied password, initializes and +// saves the repository config. func (r *Repository) Init(password string) error { has, err := r.be.Test(backend.Config, "") if err != nil { @@ -598,7 +549,8 @@ func (r *Repository) Init(password string) error { r.key = key.master r.keyName = key.Name() - return createConfig(r) + r.Config, err = CreateConfig(r) + return err } func (r *Repository) Decrypt(ciphertext []byte) ([]byte, error) {