This commit is contained in:
Alexander Neumann 2014-08-04 22:46:14 +02:00
parent 022f514b09
commit e8b83e460f
8 changed files with 80 additions and 111 deletions

View File

@ -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
}

View File

@ -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
View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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 {

View File

@ -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

View File

@ -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
}

View File

@ -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)
}