revamped how vault configuration gets stored

This commit is contained in:
Jarek Kowalski
2016-08-22 22:20:08 -07:00
parent cd012d8fbc
commit b3a611ffba
6 changed files with 77 additions and 78 deletions

View File

@@ -2,6 +2,7 @@
import (
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"log"
@@ -10,8 +11,11 @@
"runtime"
"strings"
"github.com/kopia/kopia/storage/logging"
"github.com/bgentry/speakeasy"
"github.com/kopia/kopia/repo"
"github.com/kopia/kopia/storage"
"github.com/kopia/kopia/vault"
)
@@ -59,7 +63,7 @@ func repositoryOptionsFromFlags(extraOptions []repo.RepositoryOption) []repo.Rep
}
if *traceStorage {
opts = append(opts, repo.EnableLogging())
opts = append(opts, repo.EnableLogging(logging.Prefix("[REPOSITORY] ")))
}
return opts
}
@@ -84,7 +88,7 @@ func vaultConfigFileName() string {
}
func persistVaultConfig(v *vault.Vault) error {
cfg, err := v.Token()
cfg, err := v.Config()
if err != nil {
return err
}
@@ -95,22 +99,50 @@ func persistVaultConfig(v *vault.Vault) error {
return err
}
return ioutil.WriteFile(fname, []byte(cfg), 0600)
f, err := os.Create(fname)
if err != nil {
return err
}
defer f.Close()
return json.NewEncoder(f).Encode(cfg)
}
func getPersistedVaultConfig() string {
f, err := ioutil.ReadFile(vaultConfigFileName())
func getPersistedVaultConfig() *vault.Config {
f, err := os.Open(vaultConfigFileName())
if err != nil {
return ""
return nil
}
defer f.Close()
vc := &vault.Config{}
if err := json.NewDecoder(f).Decode(vc); err != nil {
return nil
}
return string(f)
return vc
}
func openVault() (*vault.Vault, error) {
vc := getPersistedVaultConfig()
if vc != "" {
return vault.OpenWithToken(vc)
if vc != nil {
st, err := storage.NewStorage(vc.ConnectionInfo)
if err != nil {
return nil, fmt.Errorf("cannot open vault storage: %v", err)
}
if *traceStorage {
st = logging.NewWrapper(st, logging.Prefix("[VAULT] "))
}
creds, err := vault.MasterKey(vc.Key)
if err != nil {
st.Close()
return nil, fmt.Errorf("invalid vault config")
}
return vault.Open(st, creds)
}
if *vaultPath == "" {

View File

@@ -167,9 +167,9 @@ func WriteBack(workerCount int) RepositoryOption {
}
// EnableLogging is an RepositoryOption that causes all storage access to be logged.
func EnableLogging() RepositoryOption {
func EnableLogging(options ...logging.Option) RepositoryOption {
return func(o *repository) error {
o.storage = logging.NewWrapper(o.storage)
o.storage = logging.NewWrapper(o.storage, options...)
return nil
}
}

View File

@@ -8,9 +8,8 @@
"os"
"strings"
"github.com/kopia/kopia/storage"
"github.com/kopia/kopia/internal/storagetesting"
"github.com/kopia/kopia/storage"
"github.com/kopia/kopia/storage/logging"
"testing"
@@ -79,7 +78,7 @@ func TestCache(t *testing.T) {
master := storagetesting.NewMapStorage(masterData)
var tr tracer
master = logging.NewWrapperWithLogger(master, &tr)
master = logging.NewWrapper(master, logging.Output(tr.Printf))
cache, err := NewWrapper(master, Options{CacheDir: tmpdir})
defer cache.Close()

View File

@@ -11,13 +11,14 @@
type loggingStorage struct {
base storage.Storage
printf func(string, ...interface{})
prefix string
}
func (s *loggingStorage) BlockExists(id string) (bool, error) {
t0 := time.Now()
result, err := s.base.BlockExists(id)
dt := time.Since(t0)
s.printf(s.prefix()+"BlockExists(%#v)=%#v,%#v took %v", id, result, err, dt)
s.printf(s.prefix+"BlockExists(%#v)=%#v,%#v took %v", id, result, err, dt)
return result, err
}
@@ -26,9 +27,9 @@ func (s *loggingStorage) GetBlock(id string) ([]byte, error) {
result, err := s.base.GetBlock(id)
dt := time.Since(t0)
if len(result) < 20 {
s.printf(s.prefix()+"GetBlock(%#v)=(%#v, %#v) took %v", id, result, err, dt)
s.printf(s.prefix+"GetBlock(%#v)=(%#v, %#v) took %v", id, result, err, dt)
} else {
s.printf(s.prefix()+"GetBlock(%#v)=({%#v bytes}, %#v) took %v", id, len(result), err, dt)
s.printf(s.prefix+"GetBlock(%#v)=({%#v bytes}, %#v) took %v", id, len(result), err, dt)
}
return result, err
}
@@ -38,7 +39,7 @@ func (s *loggingStorage) PutBlock(id string, data storage.ReaderWithLength, opti
l := data.Len()
err := s.base.PutBlock(id, data, options)
dt := time.Since(t0)
s.printf(s.prefix()+"PutBlock(%#v, options=%v, len=%v)=%#v took %v", id, options, l, err, dt)
s.printf(s.prefix+"PutBlock(%#v, options=%v, len=%v)=%#v took %v", id, options, l, err, dt)
return err
}
@@ -46,7 +47,7 @@ func (s *loggingStorage) DeleteBlock(id string) error {
t0 := time.Now()
err := s.base.DeleteBlock(id)
dt := time.Since(t0)
s.printf(s.prefix()+"DeleteBlock(%#v)=%#v took %v", id, err, dt)
s.printf(s.prefix+"DeleteBlock(%#v)=%#v took %v", id, err, dt)
return err
}
@@ -54,7 +55,7 @@ func (s *loggingStorage) ListBlocks(prefix string) chan (storage.BlockMetadata)
t0 := time.Now()
ch := s.base.ListBlocks(prefix)
dt := time.Since(t0)
s.printf(s.prefix()+"ListBlocks(%#v) took %v", prefix, dt)
s.printf(s.prefix+"ListBlocks(%#v) took %v", prefix, dt)
return ch
}
@@ -62,7 +63,7 @@ func (s *loggingStorage) Flush() error {
t0 := time.Now()
err := s.base.Flush()
dt := time.Since(t0)
s.printf(s.prefix()+"Flush()=%#v took %v", err, dt)
s.printf(s.prefix+"Flush()=%#v took %v", err, dt)
return err
}
@@ -70,14 +71,11 @@ func (s *loggingStorage) Close() error {
t0 := time.Now()
err := s.base.Close()
dt := time.Since(t0)
s.printf(s.prefix()+"Close()=%#v took %v", err, dt)
s.printf(s.prefix+"Close()=%#v took %v", err, dt)
return err
}
func (s *loggingStorage) prefix() string {
return "[STORAGE] "
}
// Option modifies the behavior of logging storage wrapper.
type Option func(s *loggingStorage)
// NewWrapper returns a Storage wrapper that logs all storage commands.
@@ -86,6 +84,7 @@ func NewWrapper(wrapped storage.Storage, options ...Option) storage.Storage {
for _, o := range options {
o(s)
}
return s
}
@@ -95,3 +94,10 @@ func Output(outputFunc func(fmt string, args ...interface{})) Option {
s.printf = outputFunc
}
}
// Prefix specifies prefix to be prepended to all log output.
func Prefix(prefix string) Option {
return func(s *loggingStorage) {
s.prefix = prefix
}
}

View File

@@ -7,7 +7,6 @@
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
@@ -262,32 +261,26 @@ func (v *Vault) Close() error {
return nil
}
type vaultConfig struct {
// Config represents JSON-compatible configuration of the vault connection, including vault key.
type Config struct {
ConnectionInfo storage.ConnectionInfo `json:"connection"`
Key []byte `json:"key,omitempty"`
}
// Token returns a persistent opaque string that encodes the configuration of vault storage
// and its credentials in a way that can be later used to open the vault.
func (v *Vault) Token() (string, error) {
// Config returns a configuration of vault storage its credentials that's suitable
// for storing in configuration file.
func (v *Vault) Config() (*Config, error) {
cip, ok := v.storage.(storage.ConnectionInfoProvider)
if !ok {
return "", errors.New("repository does not support persisting configuration")
return nil, errors.New("repository does not support persisting configuration")
}
ci := cip.ConnectionInfo()
vc := vaultConfig{
return &Config{
ConnectionInfo: ci,
Key: v.masterKey,
}
b, err := json.Marshal(&vc)
if err != nil {
return "", err
}
return base64.RawURLEncoding.EncodeToString(b), nil
}, nil
}
// Remove deletes the specified vault item.
@@ -394,32 +387,6 @@ func Open(vaultStorage storage.Storage, vaultCreds Credentials) (*Vault, error)
return &v, nil
}
// OpenWithToken opens a vault with storage configuration and credentials in the specified token.
func OpenWithToken(token string) (*Vault, error) {
b, err := base64.RawURLEncoding.DecodeString(token)
if err != nil {
return nil, fmt.Errorf("invalid vault base64 token: %v", err)
}
var vc vaultConfig
err = json.Unmarshal(b, &vc)
if err != nil {
return nil, fmt.Errorf("invalid vault json token: %v", err)
}
st, err := storage.NewStorage(vc.ConnectionInfo)
if err != nil {
return nil, fmt.Errorf("cannot open vault storage: %v", err)
}
creds, err := MasterKey(vc.Key)
if err != nil {
return nil, fmt.Errorf("invalid vault token")
}
return Open(st, creds)
}
func isReservedName(itemID string) bool {
switch itemID {
case formatBlockID, repositoryConfigBlockID:

View File

@@ -86,23 +86,18 @@ func verifyVault(t *testing.T, vaultPath string, repoPath string) {
return
}
tok1, err := v1.Token()
cfg, err := v1.Config()
if err != nil {
t.Errorf("error getting token1 %v", err)
}
tok2, err := v2.Token()
cfg2, err := v2.Config()
if err != nil {
t.Errorf("error getting token2 %v", err)
}
v3, err := OpenWithToken(tok1)
if err != nil {
t.Errorf("can't open vault with token: %v", err)
return
}
if tok1 != tok2 {
t.Errorf("tokens don't match: %v vs %v", tok1, tok2)
if !reflect.DeepEqual(cfg, cfg2) {
t.Errorf("configurations are different: %+v vs %+v", cfg, cfg2)
}
_, err = Open(vaultStorage, otherVaultCreds)
@@ -165,7 +160,7 @@ func verifyVault(t *testing.T, vaultPath string, repoPath string) {
}
// Verify contents of vault items for both created and opened vault.
for _, v := range []*Vault{v1, v2, v3} {
for _, v := range []*Vault{v1, v2} {
rf := v.RepositoryFormat()
if !reflect.DeepEqual(rf, repoFormat) {
t.Errorf("invalid repository format: %v, but got %v", repoFormat, rf)