forked from TrueCloudLab/restic
Refactor
This commit is contained in:
parent
022f514b09
commit
e8b83e460f
8 changed files with 80 additions and 111 deletions
|
@ -24,7 +24,7 @@ func hash(filename string) (khepri.ID, error) {
|
|||
}
|
||||
|
||||
func store_file(repo *khepri.Repository, path string) (khepri.ID, error) {
|
||||
obj, err := repo.NewObject(khepri.TYPE_BLOB)
|
||||
obj, idch, err := repo.Create(khepri.TYPE_BLOB)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ func store_file(repo *khepri.Repository, path string) (khepri.ID, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return obj.ID(), nil
|
||||
return <-idch, nil
|
||||
}
|
||||
|
||||
func archive_dir(repo *khepri.Repository, path string) (khepri.ID, error) {
|
||||
|
@ -92,7 +92,7 @@ func archive_dir(repo *khepri.Repository, path string) (khepri.ID, error) {
|
|||
|
||||
log.Printf(" dir %q: %v entries", path, len(t.Nodes))
|
||||
|
||||
obj, err := repo.NewObject(khepri.TYPE_BLOB)
|
||||
obj, idch, err := repo.Create(khepri.TYPE_BLOB)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("error creating object for tree: %v", err)
|
||||
|
@ -106,7 +106,7 @@ func archive_dir(repo *khepri.Repository, path string) (khepri.ID, error) {
|
|||
|
||||
obj.Close()
|
||||
|
||||
id := obj.ID()
|
||||
id := <-idch
|
||||
log.Printf("tree for %q saved at %s", path, id)
|
||||
|
||||
return id, nil
|
||||
|
@ -124,11 +124,15 @@ func commandBackup(repo *khepri.Repository, args []string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
sn := repo.NewSnapshot(target)
|
||||
sn := khepri.NewSnapshot(target)
|
||||
sn.Tree = id
|
||||
sn.Save()
|
||||
snid, err := sn.Save(repo)
|
||||
|
||||
fmt.Printf("%q archived as %v\n", target, sn.ID())
|
||||
if err != nil {
|
||||
log.Printf("error saving snapshopt: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("%q archived as %v\n", target, snid)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
|
@ -49,7 +50,7 @@ func restore_file(repo *khepri.Repository, node khepri.Node, target string) erro
|
|||
|
||||
func restore_dir(repo *khepri.Repository, id khepri.ID, target string) error {
|
||||
fmt.Printf(" restore dir %q\n", target)
|
||||
rd, err := repo.Get(khepri.TYPE_REF, id)
|
||||
rd, err := repo.Get(khepri.TYPE_BLOB, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -121,7 +122,12 @@ func commandRestore(repo *khepri.Repository, args []string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
err = restore_dir(repo, id, target)
|
||||
sn, err := khepri.LoadSnapshot(repo, id)
|
||||
if err != nil {
|
||||
log.Fatalf("error loading snapshot %s", id)
|
||||
}
|
||||
|
||||
err = restore_dir(repo, sn.Tree, target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
109
object.go
109
object.go
|
@ -1,78 +1,60 @@
|
|||
package khepri
|
||||
|
||||
import "os"
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Object struct {
|
||||
type createObject struct {
|
||||
repo *Repository
|
||||
|
||||
id ID
|
||||
tpe Type
|
||||
|
||||
hw HashingWriter
|
||||
file *os.File
|
||||
|
||||
ch chan ID
|
||||
}
|
||||
|
||||
func (repo *Repository) NewObject(t Type) (*Object, error) {
|
||||
obj := &Object{
|
||||
func (repo *Repository) Create(t Type) (io.WriteCloser, <-chan ID, error) {
|
||||
obj := &createObject{
|
||||
repo: repo,
|
||||
tpe: t,
|
||||
ch: make(chan ID, 1),
|
||||
}
|
||||
|
||||
return obj, obj.open()
|
||||
// save contents to tempfile in repository, hash while writing
|
||||
var err error
|
||||
obj.file, err = obj.repo.tempFile()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// create hashing writer
|
||||
obj.hw = NewHashingWriter(obj.file, obj.repo.hash)
|
||||
|
||||
return obj, obj.ch, nil
|
||||
}
|
||||
|
||||
func (obj *Object) open() error {
|
||||
if obj.isFinal() {
|
||||
panic("object is finalized")
|
||||
}
|
||||
|
||||
if obj.isOpen() {
|
||||
panic("object already open")
|
||||
}
|
||||
|
||||
// create tempfile in repository
|
||||
func (obj *createObject) Write(data []byte) (int, error) {
|
||||
if obj.hw == nil {
|
||||
// save contents to tempfile, hash while writing
|
||||
var err error
|
||||
obj.file, err = obj.repo.tempFile()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create hashing writer
|
||||
obj.hw = NewHashingWriter(obj.file, obj.repo.hash)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (obj *Object) isOpen() bool {
|
||||
return obj.file != nil && obj.hw != nil
|
||||
}
|
||||
|
||||
func (obj *Object) isFinal() bool {
|
||||
return obj.id != nil
|
||||
}
|
||||
|
||||
func (obj *Object) Write(data []byte) (int, error) {
|
||||
if !obj.isOpen() {
|
||||
panic("object not open")
|
||||
panic("createObject: already closed!")
|
||||
}
|
||||
|
||||
return obj.hw.Write(data)
|
||||
}
|
||||
|
||||
func (obj *Object) Close() error {
|
||||
if obj.file == nil || obj.hw == nil {
|
||||
panic("object is not open")
|
||||
func (obj *createObject) Close() error {
|
||||
if obj.hw == nil {
|
||||
panic("createObject: already closed!")
|
||||
}
|
||||
|
||||
obj.file.Close()
|
||||
|
||||
hash := obj.hw.Hash()
|
||||
id := ID(obj.hw.Hash())
|
||||
obj.ch <- id
|
||||
|
||||
// move file to final name using hash of contents
|
||||
id := ID(hash)
|
||||
err := obj.repo.renameFile(obj.file, obj.tpe, id)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -80,40 +62,5 @@ func (obj *Object) Close() error {
|
|||
|
||||
obj.hw = nil
|
||||
obj.file = nil
|
||||
|
||||
obj.id = id
|
||||
return nil
|
||||
}
|
||||
|
||||
func (obj *Object) ID() ID {
|
||||
if !obj.isFinal() {
|
||||
panic("object not finalized")
|
||||
}
|
||||
|
||||
return obj.id
|
||||
}
|
||||
|
||||
func (obj *Object) Type() Type {
|
||||
return obj.tpe
|
||||
}
|
||||
|
||||
func (obj *Object) Remove() error {
|
||||
if obj.id != nil {
|
||||
return obj.repo.Remove(obj.tpe, obj.id)
|
||||
}
|
||||
|
||||
if obj.file != nil {
|
||||
file := obj.file
|
||||
obj.hw = nil
|
||||
obj.file = nil
|
||||
|
||||
err := file.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.Remove(file.Name())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -16,16 +16,18 @@ func TestObjects(t *testing.T) {
|
|||
}()
|
||||
|
||||
for _, test := range TestStrings {
|
||||
obj, err := repo.NewObject(khepri.TYPE_BLOB)
|
||||
obj, ch, err := repo.Create(khepri.TYPE_BLOB)
|
||||
ok(t, err)
|
||||
|
||||
_, err = obj.Write([]byte(test.data))
|
||||
ok(t, err)
|
||||
|
||||
obj.Close()
|
||||
err = obj.Close()
|
||||
ok(t, err)
|
||||
|
||||
id, err := khepri.ParseID(test.id)
|
||||
ok(t, err)
|
||||
|
||||
equals(t, id, obj.ID())
|
||||
equals(t, id, <-ch)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -151,7 +151,7 @@ func (r *Repository) Test(t Type, id ID) (bool, error) {
|
|||
}
|
||||
|
||||
// Get returns a reader for the content stored under the given ID.
|
||||
func (r *Repository) Get(t Type, id ID) (io.Reader, error) {
|
||||
func (r *Repository) Get(t Type, id ID) (io.ReadCloser, error) {
|
||||
// try to open file
|
||||
file, err := os.Open(r.filename(t, id))
|
||||
if err != nil {
|
||||
|
|
|
@ -75,7 +75,7 @@ func TestRepository(t *testing.T) {
|
|||
// add files
|
||||
for _, test := range TestStrings {
|
||||
// store string in repository
|
||||
obj, err := repo.NewObject(test.t)
|
||||
obj, id_ch, err := repo.Create(test.t)
|
||||
ok(t, err)
|
||||
|
||||
_, err = obj.Write([]byte(test.data))
|
||||
|
@ -84,7 +84,7 @@ func TestRepository(t *testing.T) {
|
|||
err = obj.Close()
|
||||
ok(t, err)
|
||||
|
||||
id := obj.ID()
|
||||
id := <-id_ch
|
||||
equals(t, test.id, id.String())
|
||||
|
||||
// try to get it out again
|
||||
|
|
35
snapshot.go
35
snapshot.go
|
@ -15,14 +15,11 @@ type Snapshot struct {
|
|||
Username string `json:"username,omitempty"`
|
||||
UID string `json:"uid,omitempty"`
|
||||
GID string `json:"gid,omitempty"`
|
||||
id ID
|
||||
repo *Repository
|
||||
}
|
||||
|
||||
func (repo *Repository) NewSnapshot(dir string) *Snapshot {
|
||||
func NewSnapshot(dir string) *Snapshot {
|
||||
sn := &Snapshot{
|
||||
Dir: dir,
|
||||
repo: repo,
|
||||
Time: time.Now(),
|
||||
}
|
||||
|
||||
|
@ -41,31 +38,43 @@ func (repo *Repository) NewSnapshot(dir string) *Snapshot {
|
|||
return sn
|
||||
}
|
||||
|
||||
func (sn *Snapshot) Save() error {
|
||||
func (sn *Snapshot) Save(repo *Repository) (ID, error) {
|
||||
if sn.Tree == nil {
|
||||
panic("Snapshot.Save() called with nil tree id")
|
||||
}
|
||||
|
||||
obj, err := sn.repo.NewObject(TYPE_REF)
|
||||
obj, id_ch, err := repo.Create(TYPE_REF)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
enc := json.NewEncoder(obj)
|
||||
err = enc.Encode(sn)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = obj.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sn.id = obj.ID()
|
||||
return nil
|
||||
return <-id_ch, nil
|
||||
}
|
||||
|
||||
func (sn *Snapshot) ID() ID {
|
||||
return sn.id
|
||||
func LoadSnapshot(repo *Repository, id ID) (*Snapshot, error) {
|
||||
rd, err := repo.Get(TYPE_REF, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dec := json.NewDecoder(rd)
|
||||
sn := &Snapshot{}
|
||||
err = dec.Decode(sn)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sn, nil
|
||||
}
|
||||
|
|
|
@ -16,11 +16,12 @@ func TestSnapshot(t *testing.T) {
|
|||
ok(t, err)
|
||||
}()
|
||||
|
||||
sn := repo.NewSnapshot("/home/foobar")
|
||||
sn := khepri.NewSnapshot("/home/foobar")
|
||||
sn.Tree, err = khepri.ParseID("c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2")
|
||||
ok(t, err)
|
||||
sn.Time, err = time.Parse(time.RFC3339Nano, "2014-08-03T17:49:05.378595539+02:00")
|
||||
ok(t, err)
|
||||
|
||||
ok(t, sn.Save())
|
||||
_, err = sn.Save(repo)
|
||||
ok(t, err)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue