repository: extract Load/StoreJSONUnpacked
A Load/Store method for each data type is much clearer. As a result the repository no longer needs a method to load / store json.
This commit is contained in:
parent
fbcbd5318c
commit
89d3ce852b
22 changed files with 130 additions and 127 deletions
|
@ -79,7 +79,7 @@ func runCat(gopts GlobalOptions, args []string) error {
|
|||
Println(string(buf))
|
||||
return nil
|
||||
case "index":
|
||||
buf, err := repo.LoadUnpacked(gopts.ctx, nil, restic.IndexFile, id)
|
||||
buf, err := repo.LoadUnpacked(gopts.ctx, restic.IndexFile, id, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -87,13 +87,12 @@ func runCat(gopts GlobalOptions, args []string) error {
|
|||
Println(string(buf))
|
||||
return nil
|
||||
case "snapshot":
|
||||
sn := &restic.Snapshot{}
|
||||
err = repo.LoadJSONUnpacked(gopts.ctx, restic.SnapshotFile, id, sn)
|
||||
sn, err := restic.LoadSnapshot(gopts.ctx, repo, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf, err := json.MarshalIndent(&sn, "", " ")
|
||||
buf, err := json.MarshalIndent(sn, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -154,7 +154,7 @@ func runCopy(opts CopyOptions, gopts GlobalOptions, args []string) error {
|
|||
if sn.Original == nil {
|
||||
sn.Original = sn.ID()
|
||||
}
|
||||
newID, err := dstRepo.SaveJSONUnpacked(ctx, restic.SnapshotFile, sn)
|
||||
newID, err := restic.SaveSnapshot(ctx, dstRepo, sn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -166,7 +166,7 @@ func createSnapshot(ctx context.Context, name, hostname string, tags []string, r
|
|||
|
||||
sn.Tree = tree
|
||||
|
||||
id, err := repo.SaveJSONUnpacked(ctx, restic.SnapshotFile, sn)
|
||||
id, err := restic.SaveSnapshot(ctx, repo, sn)
|
||||
if err != nil {
|
||||
return errors.Fatalf("unable to save snapshot: %v", err)
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ func changeTags(ctx context.Context, repo *repository.Repository, sn *restic.Sna
|
|||
}
|
||||
|
||||
// Save the new snapshot.
|
||||
id, err := repo.SaveJSONUnpacked(ctx, restic.SnapshotFile, sn)
|
||||
id, err := restic.SaveSnapshot(ctx, repo, sn)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
)
|
||||
|
||||
// FindFilteredSnapshots yields Snapshots, either given explicitly by `snapshotIDs` or filtered from the list of all snapshots.
|
||||
func FindFilteredSnapshots(ctx context.Context, be restic.Lister, loader restic.LoadJSONUnpackeder, hosts []string, tags []restic.TagList, paths []string, snapshotIDs []string) <-chan *restic.Snapshot {
|
||||
func FindFilteredSnapshots(ctx context.Context, be restic.Lister, loader restic.LoaderUnpacked, hosts []string, tags []restic.TagList, paths []string, snapshotIDs []string) <-chan *restic.Snapshot {
|
||||
out := make(chan *restic.Snapshot)
|
||||
go func() {
|
||||
defer close(out)
|
||||
|
|
|
@ -863,7 +863,7 @@ func (arch *Archiver) Snapshot(ctx context.Context, targets []string, opts Snaps
|
|||
}
|
||||
sn.Tree = &rootTreeID
|
||||
|
||||
id, err := arch.Repo.SaveJSONUnpacked(ctx, restic.SnapshotFile, sn)
|
||||
id, err := restic.SaveSnapshot(ctx, arch.Repo, sn)
|
||||
if err != nil {
|
||||
return nil, restic.ID{}, err
|
||||
}
|
||||
|
|
|
@ -519,7 +519,7 @@ func TestCheckerBlobTypeConfusion(t *testing.T) {
|
|||
|
||||
snapshot.Tree = &rootID
|
||||
|
||||
snapID, err := repo.SaveJSONUnpacked(ctx, restic.SnapshotFile, snapshot)
|
||||
snapID, err := restic.SaveSnapshot(ctx, repo, snapshot)
|
||||
test.OK(t, err)
|
||||
|
||||
t.Logf("saved snapshot %v", snapID.Str())
|
||||
|
@ -600,8 +600,7 @@ func benchmarkSnapshotScaling(t *testing.B, newSnapshots int) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var sn2 restic.Snapshot
|
||||
err = repo.LoadJSONUnpacked(context.TODO(), restic.SnapshotFile, snID, &sn2)
|
||||
sn2, err := restic.LoadSnapshot(context.TODO(), repo, snID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -615,7 +614,7 @@ func benchmarkSnapshotScaling(t *testing.B, newSnapshots int) {
|
|||
}
|
||||
sn.Tree = treeID
|
||||
|
||||
_, err = repo.SaveJSONUnpacked(context.TODO(), restic.SnapshotFile, sn)
|
||||
_, err = restic.SaveSnapshot(context.TODO(), repo, sn)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ func (*UpgradeRepoV2) upgrade(ctx context.Context, repo restic.Repository) error
|
|||
cfg := repo.Config()
|
||||
cfg.Version = 2
|
||||
|
||||
_, err := repo.SaveJSONUnpacked(ctx, restic.ConfigFile, cfg)
|
||||
err := restic.SaveConfig(ctx, repo, cfg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("save new config file failed: %w", err)
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ func ForAllIndexes(ctx context.Context, repo restic.Repository,
|
|||
var idx *Index
|
||||
oldFormat := false
|
||||
|
||||
buf, err = repo.LoadUnpacked(ctx, buf[:0], restic.IndexFile, fi.ID)
|
||||
buf, err = repo.LoadUnpacked(ctx, restic.IndexFile, fi.ID, buf[:0])
|
||||
if err == nil {
|
||||
idx, oldFormat, err = DecodeIndex(buf, fi.ID)
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
@ -153,7 +152,7 @@ func (r *Repository) PrefixLength(ctx context.Context, t restic.FileType) (int,
|
|||
// LoadUnpacked loads and decrypts the file with the given type and ID, using
|
||||
// the supplied buffer (which must be empty). If the buffer is nil, a new
|
||||
// buffer will be allocated and returned.
|
||||
func (r *Repository) LoadUnpacked(ctx context.Context, buf []byte, t restic.FileType, id restic.ID) ([]byte, error) {
|
||||
func (r *Repository) LoadUnpacked(ctx context.Context, t restic.FileType, id restic.ID, buf []byte) ([]byte, error) {
|
||||
if len(buf) != 0 {
|
||||
panic("buf is not empty")
|
||||
}
|
||||
|
@ -315,17 +314,6 @@ func (r *Repository) LoadBlob(ctx context.Context, t restic.BlobType, id restic.
|
|||
return nil, errors.Errorf("loading blob %v from %v packs failed", id.Str(), len(blobs))
|
||||
}
|
||||
|
||||
// LoadJSONUnpacked decrypts the data and afterwards calls json.Unmarshal on
|
||||
// the item.
|
||||
func (r *Repository) LoadJSONUnpacked(ctx context.Context, t restic.FileType, id restic.ID, item interface{}) (err error) {
|
||||
buf, err := r.LoadUnpacked(ctx, nil, t, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return json.Unmarshal(buf, item)
|
||||
}
|
||||
|
||||
// LookupBlobSize returns the size of blob id.
|
||||
func (r *Repository) LookupBlobSize(id restic.ID, tpe restic.BlobType) (uint, bool) {
|
||||
return r.idx.LookupSize(restic.BlobHandle{ID: id, Type: tpe})
|
||||
|
@ -419,18 +407,6 @@ func (r *Repository) saveAndEncrypt(ctx context.Context, t restic.BlobType, data
|
|||
return pm.SaveBlob(ctx, t, id, ciphertext, uncompressedLength)
|
||||
}
|
||||
|
||||
// SaveJSONUnpacked serialises item as JSON and encrypts and saves it in the
|
||||
// backend as type t, without a pack. It returns the storage hash.
|
||||
func (r *Repository) SaveJSONUnpacked(ctx context.Context, t restic.FileType, item interface{}) (restic.ID, error) {
|
||||
debug.Log("save new blob %v", t)
|
||||
plaintext, err := json.Marshal(item)
|
||||
if err != nil {
|
||||
return restic.ID{}, errors.Wrap(err, "json.Marshal")
|
||||
}
|
||||
|
||||
return r.SaveUnpacked(ctx, t, plaintext)
|
||||
}
|
||||
|
||||
func (r *Repository) compressUnpacked(p []byte) ([]byte, error) {
|
||||
// compression is only available starting from version 2
|
||||
if r.cfg.Version < 2 {
|
||||
|
@ -762,8 +738,7 @@ func (r *Repository) init(ctx context.Context, password string, cfg restic.Confi
|
|||
r.key = key.master
|
||||
r.keyName = key.Name()
|
||||
r.setConfig(cfg)
|
||||
_, err = r.SaveJSONUnpacked(ctx, restic.ConfigFile, cfg)
|
||||
return err
|
||||
return restic.SaveConfig(ctx, r, cfg)
|
||||
}
|
||||
|
||||
// Key returns the current master key.
|
||||
|
|
|
@ -238,7 +238,7 @@ func benchmarkLoadUnpacked(b *testing.B, version uint) {
|
|||
b.SetBytes(int64(length))
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
data, err := repo.LoadUnpacked(context.TODO(), nil, restic.PackFile, storageID)
|
||||
data, err := repo.LoadUnpacked(context.TODO(), restic.PackFile, storageID, nil)
|
||||
rtest.OK(b, err)
|
||||
|
||||
// See comment in BenchmarkLoadBlob.
|
||||
|
@ -255,44 +255,6 @@ func benchmarkLoadUnpacked(b *testing.B, version uint) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestLoadJSONUnpacked(t *testing.T) {
|
||||
repository.TestAllVersions(t, testLoadJSONUnpacked)
|
||||
}
|
||||
|
||||
func testLoadJSONUnpacked(t *testing.T, version uint) {
|
||||
repo, cleanup := repository.TestRepositoryWithVersion(t, version)
|
||||
defer cleanup()
|
||||
|
||||
if rtest.BenchArchiveDirectory == "" {
|
||||
t.Skip("benchdir not set, skipping")
|
||||
}
|
||||
|
||||
// archive a snapshot
|
||||
sn := restic.Snapshot{}
|
||||
sn.Hostname = "foobar"
|
||||
sn.Username = "test!"
|
||||
|
||||
id, err := repo.SaveJSONUnpacked(context.TODO(), restic.SnapshotFile, &sn)
|
||||
rtest.OK(t, err)
|
||||
|
||||
var sn2 restic.Snapshot
|
||||
|
||||
// restore
|
||||
err = repo.LoadJSONUnpacked(context.TODO(), restic.SnapshotFile, id, &sn2)
|
||||
rtest.OK(t, err)
|
||||
|
||||
rtest.Equals(t, sn.Hostname, sn2.Hostname)
|
||||
rtest.Equals(t, sn.Username, sn2.Username)
|
||||
|
||||
var cf restic.Config
|
||||
|
||||
// load and check Config
|
||||
err = repo.LoadJSONUnpacked(context.TODO(), restic.ConfigFile, id, &cf)
|
||||
rtest.OK(t, err)
|
||||
|
||||
rtest.Equals(t, cf.ChunkerPolynomial, repository.TestChunkerPol)
|
||||
}
|
||||
|
||||
var repoFixture = filepath.Join("testdata", "test-repo.tar.gz")
|
||||
|
||||
func TestRepositoryLoadIndex(t *testing.T) {
|
||||
|
@ -305,7 +267,7 @@ func TestRepositoryLoadIndex(t *testing.T) {
|
|||
|
||||
// loadIndex loads the index id from backend and returns it.
|
||||
func loadIndex(ctx context.Context, repo restic.Repository, id restic.ID) (*repository.Index, error) {
|
||||
buf, err := repo.LoadUnpacked(ctx, nil, restic.IndexFile, id)
|
||||
buf, err := repo.LoadUnpacked(ctx, restic.IndexFile, id, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -76,12 +76,12 @@ func TestDisableCheckPolynomial(t testing.TB) {
|
|||
}
|
||||
|
||||
// LoadConfig returns loads, checks and returns the config for a repository.
|
||||
func LoadConfig(ctx context.Context, r JSONUnpackedLoader) (Config, error) {
|
||||
func LoadConfig(ctx context.Context, r LoaderUnpacked) (Config, error) {
|
||||
var (
|
||||
cfg Config
|
||||
)
|
||||
|
||||
err := r.LoadJSONUnpacked(ctx, ConfigFile, ID{}, &cfg)
|
||||
err := LoadJSONUnpacked(ctx, r, ConfigFile, ID{}, &cfg)
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
|
@ -98,3 +98,8 @@ func LoadConfig(ctx context.Context, r JSONUnpackedLoader) (Config, error) {
|
|||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func SaveConfig(ctx context.Context, r SaverUnpacked, cfg Config) error {
|
||||
_, err := SaveJSONUnpacked(ctx, r, ConfigFile, cfg)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -8,47 +8,56 @@ import (
|
|||
rtest "github.com/restic/restic/internal/test"
|
||||
)
|
||||
|
||||
type saver func(restic.FileType, interface{}) (restic.ID, error)
|
||||
|
||||
func (s saver) SaveJSONUnpacked(t restic.FileType, arg interface{}) (restic.ID, error) {
|
||||
return s(t, arg)
|
||||
type saver struct {
|
||||
fn func(restic.FileType, []byte) (restic.ID, error)
|
||||
}
|
||||
|
||||
type loader func(context.Context, restic.FileType, restic.ID, interface{}) error
|
||||
func (s saver) SaveUnpacked(ctx context.Context, t restic.FileType, buf []byte) (restic.ID, error) {
|
||||
return s.fn(t, buf)
|
||||
}
|
||||
|
||||
func (l loader) LoadJSONUnpacked(ctx context.Context, t restic.FileType, id restic.ID, arg interface{}) error {
|
||||
return l(ctx, t, id, arg)
|
||||
func (s saver) Connections() uint {
|
||||
return 2
|
||||
}
|
||||
|
||||
type loader struct {
|
||||
fn func(restic.FileType, restic.ID, []byte) ([]byte, error)
|
||||
}
|
||||
|
||||
func (l loader) LoadUnpacked(ctx context.Context, t restic.FileType, id restic.ID, buf []byte) (data []byte, err error) {
|
||||
return l.fn(t, id, buf)
|
||||
}
|
||||
|
||||
func (l loader) Connections() uint {
|
||||
return 2
|
||||
}
|
||||
|
||||
func TestConfig(t *testing.T) {
|
||||
resultConfig := restic.Config{}
|
||||
save := func(tpe restic.FileType, arg interface{}) (restic.ID, error) {
|
||||
var resultBuf []byte
|
||||
save := func(tpe restic.FileType, buf []byte) (restic.ID, error) {
|
||||
rtest.Assert(t, tpe == restic.ConfigFile,
|
||||
"wrong backend type: got %v, wanted %v",
|
||||
tpe, restic.ConfigFile)
|
||||
|
||||
cfg := arg.(restic.Config)
|
||||
resultConfig = cfg
|
||||
resultBuf = buf
|
||||
return restic.ID{}, nil
|
||||
}
|
||||
|
||||
cfg1, err := restic.CreateConfig(restic.MaxRepoVersion)
|
||||
rtest.OK(t, err)
|
||||
|
||||
_, err = saver(save).SaveJSONUnpacked(restic.ConfigFile, cfg1)
|
||||
err = restic.SaveConfig(context.TODO(), saver{save}, cfg1)
|
||||
rtest.OK(t, err)
|
||||
|
||||
load := func(ctx context.Context, tpe restic.FileType, id restic.ID, arg interface{}) error {
|
||||
load := func(tpe restic.FileType, id restic.ID, in []byte) ([]byte, error) {
|
||||
rtest.Assert(t, tpe == restic.ConfigFile,
|
||||
"wrong backend type: got %v, wanted %v",
|
||||
tpe, restic.ConfigFile)
|
||||
|
||||
cfg := arg.(*restic.Config)
|
||||
*cfg = resultConfig
|
||||
return nil
|
||||
return resultBuf, nil
|
||||
}
|
||||
|
||||
cfg2, err := restic.LoadConfig(context.TODO(), loader(load))
|
||||
cfg2, err := restic.LoadConfig(context.TODO(), loader{load})
|
||||
rtest.OK(t, err)
|
||||
|
||||
rtest.Assert(t, cfg1 == cfg2,
|
||||
|
|
32
internal/restic/json.go
Normal file
32
internal/restic/json.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package restic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/restic/restic/internal/debug"
|
||||
"github.com/restic/restic/internal/errors"
|
||||
)
|
||||
|
||||
// LoadJSONUnpacked decrypts the data and afterwards calls json.Unmarshal on
|
||||
// the item.
|
||||
func LoadJSONUnpacked(ctx context.Context, repo LoaderUnpacked, t FileType, id ID, item interface{}) (err error) {
|
||||
buf, err := repo.LoadUnpacked(ctx, t, id, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return json.Unmarshal(buf, item)
|
||||
}
|
||||
|
||||
// SaveJSONUnpacked serialises item as JSON and encrypts and saves it in the
|
||||
// backend as type t, without a pack. It returns the storage hash.
|
||||
func SaveJSONUnpacked(ctx context.Context, repo SaverUnpacked, t FileType, item interface{}) (ID, error) {
|
||||
debug.Log("save new blob %v", t)
|
||||
plaintext, err := json.Marshal(item)
|
||||
if err != nil {
|
||||
return ID{}, errors.Wrap(err, "json.Marshal")
|
||||
}
|
||||
|
||||
return repo.SaveUnpacked(ctx, t, plaintext)
|
||||
}
|
|
@ -158,7 +158,7 @@ func (l *Lock) checkForOtherLocks(ctx context.Context) error {
|
|||
|
||||
// createLock acquires the lock by creating a file in the repository.
|
||||
func (l *Lock) createLock(ctx context.Context) (ID, error) {
|
||||
id, err := l.repo.SaveJSONUnpacked(ctx, LockFile, l)
|
||||
id, err := SaveJSONUnpacked(ctx, l.repo, LockFile, l)
|
||||
if err != nil {
|
||||
return ID{}, err
|
||||
}
|
||||
|
@ -255,7 +255,7 @@ func init() {
|
|||
// LoadLock loads and unserializes a lock from a repository.
|
||||
func LoadLock(ctx context.Context, repo Repository, id ID) (*Lock, error) {
|
||||
lock := &Lock{}
|
||||
if err := repo.LoadJSONUnpacked(ctx, LockFile, id, lock); err != nil {
|
||||
if err := LoadJSONUnpacked(ctx, repo, LockFile, id, lock); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lock.lockID = &id
|
||||
|
|
|
@ -99,7 +99,7 @@ func createFakeLock(repo restic.Repository, t time.Time, pid int) (restic.ID, er
|
|||
}
|
||||
|
||||
newLock := &restic.Lock{Time: t, PID: pid, Hostname: hostname}
|
||||
return repo.SaveJSONUnpacked(context.TODO(), restic.LockFile, &newLock)
|
||||
return restic.SaveJSONUnpacked(context.TODO(), repo, restic.LockFile, &newLock)
|
||||
}
|
||||
|
||||
func removeLock(repo restic.Repository, id restic.ID) error {
|
||||
|
|
|
@ -37,24 +37,20 @@ type Repository interface {
|
|||
// the the pack header.
|
||||
ListPack(context.Context, ID, int64) ([]Blob, uint32, error)
|
||||
|
||||
LoadBlob(context.Context, BlobType, ID, []byte) ([]byte, error)
|
||||
SaveBlob(context.Context, BlobType, []byte, ID, bool) (ID, bool, int, error)
|
||||
|
||||
// StartPackUploader start goroutines to upload new pack files. The errgroup
|
||||
// is used to immediately notify about an upload error. Flush() will also return
|
||||
// that error.
|
||||
StartPackUploader(ctx context.Context, wg *errgroup.Group)
|
||||
Flush(context.Context) error
|
||||
|
||||
SaveJSONUnpacked(context.Context, FileType, interface{}) (ID, error)
|
||||
|
||||
LoadJSONUnpacked(ctx context.Context, t FileType, id ID, dest interface{}) error
|
||||
|
||||
// LoadUnpacked loads and decrypts the file with the given type and ID,
|
||||
// using the supplied buffer (which must be empty). If the buffer is nil, a
|
||||
// new buffer will be allocated and returned.
|
||||
LoadUnpacked(ctx context.Context, buf []byte, t FileType, id ID) (data []byte, err error)
|
||||
LoadUnpacked(ctx context.Context, t FileType, id ID, buf []byte) (data []byte, err error)
|
||||
SaveUnpacked(context.Context, FileType, []byte) (ID, error)
|
||||
|
||||
LoadBlob(context.Context, BlobType, ID, []byte) ([]byte, error)
|
||||
SaveBlob(context.Context, BlobType, []byte, ID, bool) (ID, bool, int, error)
|
||||
}
|
||||
|
||||
// Lister allows listing files in a backend.
|
||||
|
@ -62,16 +58,11 @@ type Lister interface {
|
|||
List(context.Context, FileType, func(FileInfo) error) error
|
||||
}
|
||||
|
||||
// LoadJSONUnpackeder allows loading a JSON file not stored in a pack file
|
||||
type LoadJSONUnpackeder interface {
|
||||
// Connections returns the maximum number of concurrent backend operations
|
||||
Connections() uint
|
||||
LoadJSONUnpacked(ctx context.Context, t FileType, id ID, dest interface{}) error
|
||||
}
|
||||
|
||||
// LoaderUnpacked allows loading a blob not stored in a pack file
|
||||
type LoaderUnpacked interface {
|
||||
LoadUnpacked(ctx context.Context, buf []byte, t FileType, id ID) (data []byte, err error)
|
||||
// Connections returns the maximum number of concurrent backend operations
|
||||
Connections() uint
|
||||
LoadUnpacked(ctx context.Context, t FileType, id ID, buf []byte) (data []byte, err error)
|
||||
}
|
||||
|
||||
// SaverUnpacked allows saving a blob not stored in a pack file
|
||||
|
|
|
@ -59,9 +59,9 @@ func NewSnapshot(paths []string, tags []string, hostname string, time time.Time)
|
|||
}
|
||||
|
||||
// LoadSnapshot loads the snapshot with the id and returns it.
|
||||
func LoadSnapshot(ctx context.Context, loader LoadJSONUnpackeder, id ID) (*Snapshot, error) {
|
||||
func LoadSnapshot(ctx context.Context, loader LoaderUnpacked, id ID) (*Snapshot, error) {
|
||||
sn := &Snapshot{id: &id}
|
||||
err := loader.LoadJSONUnpacked(ctx, SnapshotFile, id, sn)
|
||||
err := LoadJSONUnpacked(ctx, loader, SnapshotFile, id, sn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -69,12 +69,17 @@ func LoadSnapshot(ctx context.Context, loader LoadJSONUnpackeder, id ID) (*Snaps
|
|||
return sn, nil
|
||||
}
|
||||
|
||||
// SaveSnapshot saves the snapshot sn and returns its ID.
|
||||
func SaveSnapshot(ctx context.Context, repo SaverUnpacked, sn *Snapshot) (ID, error) {
|
||||
return SaveJSONUnpacked(ctx, repo, SnapshotFile, sn)
|
||||
}
|
||||
|
||||
// ForAllSnapshots reads all snapshots in parallel and calls the
|
||||
// given function. It is guaranteed that the function is not run concurrently.
|
||||
// If the called function returns an error, this function is cancelled and
|
||||
// also returns this error.
|
||||
// If a snapshot ID is in excludeIDs, it will be ignored.
|
||||
func ForAllSnapshots(ctx context.Context, be Lister, loader LoadJSONUnpackeder, excludeIDs IDSet, fn func(ID, *Snapshot, error) error) error {
|
||||
func ForAllSnapshots(ctx context.Context, be Lister, loader LoaderUnpacked, excludeIDs IDSet, fn func(ID, *Snapshot, error) error) error {
|
||||
var m sync.Mutex
|
||||
|
||||
// track spawned goroutines using wg, create a new context which is
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
var ErrNoSnapshotFound = errors.New("no snapshot found")
|
||||
|
||||
// FindLatestSnapshot finds latest snapshot with optional target/directory, tags, hostname, and timestamp filters.
|
||||
func FindLatestSnapshot(ctx context.Context, be Lister, loader LoadJSONUnpackeder, targets []string,
|
||||
func FindLatestSnapshot(ctx context.Context, be Lister, loader LoaderUnpacked, targets []string,
|
||||
tagLists []TagList, hostnames []string, timeStampLimit *time.Time) (ID, error) {
|
||||
|
||||
var err error
|
||||
|
@ -92,7 +92,7 @@ func FindSnapshot(ctx context.Context, be Lister, s string) (ID, error) {
|
|||
|
||||
// FindFilteredSnapshots yields Snapshots filtered from the list of all
|
||||
// snapshots.
|
||||
func FindFilteredSnapshots(ctx context.Context, be Lister, loader LoadJSONUnpackeder, hosts []string, tags []TagList, paths []string) (Snapshots, error) {
|
||||
func FindFilteredSnapshots(ctx context.Context, be Lister, loader LoaderUnpacked, hosts []string, tags []TagList, paths []string) (Snapshots, error) {
|
||||
results := make(Snapshots, 0, 20)
|
||||
|
||||
err := ForAllSnapshots(ctx, be, loader, nil, func(id ID, sn *Snapshot, err error) error {
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package restic_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/restic/restic/internal/repository"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
)
|
||||
|
@ -24,3 +26,27 @@ func TestTagList(t *testing.T) {
|
|||
r := sn.HasTags(tags)
|
||||
rtest.Assert(t, r, "Failed to match untagged snapshot")
|
||||
}
|
||||
|
||||
func TestLoadJSONUnpacked(t *testing.T) {
|
||||
repository.TestAllVersions(t, testLoadJSONUnpacked)
|
||||
}
|
||||
|
||||
func testLoadJSONUnpacked(t *testing.T, version uint) {
|
||||
repo, cleanup := repository.TestRepositoryWithVersion(t, version)
|
||||
defer cleanup()
|
||||
|
||||
// archive a snapshot
|
||||
sn := restic.Snapshot{}
|
||||
sn.Hostname = "foobar"
|
||||
sn.Username = "test!"
|
||||
|
||||
id, err := restic.SaveSnapshot(context.TODO(), repo, &sn)
|
||||
rtest.OK(t, err)
|
||||
|
||||
// restore
|
||||
sn2, err := restic.LoadSnapshot(context.TODO(), repo, id)
|
||||
rtest.OK(t, err)
|
||||
|
||||
rtest.Equals(t, sn.Hostname, sn2.Hostname)
|
||||
rtest.Equals(t, sn.Username, sn2.Username)
|
||||
}
|
||||
|
|
|
@ -181,7 +181,7 @@ func TestCreateSnapshot(t testing.TB, repo Repository, at time.Time, depth int,
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
id, err := repo.SaveJSONUnpacked(context.TODO(), SnapshotFile, snapshot)
|
||||
id, err := SaveSnapshot(context.TODO(), repo, snapshot)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -137,7 +137,7 @@ func saveSnapshot(t testing.TB, repo restic.Repository, snapshot Snapshot) (*res
|
|||
}
|
||||
|
||||
sn.Tree = &treeID
|
||||
id, err := repo.SaveJSONUnpacked(ctx, restic.SnapshotFile, sn)
|
||||
id, err := restic.SaveSnapshot(ctx, repo, sn)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue