From e8b83e460fc15064d172039e068ce3f0b97de17a Mon Sep 17 00:00:00 2001 From: Alexander Neumann Date: Mon, 4 Aug 2014 22:46:14 +0200 Subject: [PATCH] Refactor --- cmd/khepri/cmd_backup.go | 18 ++++--- cmd/khepri/cmd_restore.go | 10 +++- object.go | 109 ++++++++++---------------------------- object_test.go | 8 +-- repository.go | 2 +- repository_test.go | 4 +- snapshot.go | 35 +++++++----- snapshot_test.go | 5 +- 8 files changed, 80 insertions(+), 111 deletions(-) diff --git a/cmd/khepri/cmd_backup.go b/cmd/khepri/cmd_backup.go index a774c67be..6785d12be 100644 --- a/cmd/khepri/cmd_backup.go +++ b/cmd/khepri/cmd_backup.go @@ -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 } diff --git a/cmd/khepri/cmd_restore.go b/cmd/khepri/cmd_restore.go index cbc69fba3..0aef4bedc 100644 --- a/cmd/khepri/cmd_restore.go +++ b/cmd/khepri/cmd_restore.go @@ -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 } diff --git a/object.go b/object.go index 03349e498..d5b722f74 100644 --- a/object.go +++ b/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 } diff --git a/object_test.go b/object_test.go index 62afe9fcd..e92e84d7e 100644 --- a/object_test.go +++ b/object_test.go @@ -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) } } diff --git a/repository.go b/repository.go index f78f97933..68e1065cb 100644 --- a/repository.go +++ b/repository.go @@ -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 { diff --git a/repository_test.go b/repository_test.go index dd47eea3f..d13da4a06 100644 --- a/repository_test.go +++ b/repository_test.go @@ -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 diff --git a/snapshot.go b/snapshot.go index f55f00551..514e29cfe 100644 --- a/snapshot.go +++ b/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 } diff --git a/snapshot_test.go b/snapshot_test.go index c3cc001aa..611f9512f 100644 --- a/snapshot_test.go +++ b/snapshot_test.go @@ -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) }