Snapshots and snapshot collection.

This commit is contained in:
Andrey Smirnov
2013-12-21 18:45:52 +04:00
parent 54bcab6a30
commit 9ff59a9650
2 changed files with 200 additions and 0 deletions

114
debian/snapshot.go vendored
View File

@@ -1,8 +1,12 @@
package debian
import (
"bytes"
"code.google.com/p/go-uuid/uuid"
"fmt"
"github.com/smira/aptly/database"
"github.com/ugorji/go/codec"
"log"
"time"
)
@@ -41,6 +45,11 @@ func NewSnapshotFromRepository(name string, repo *RemoteRepo) *Snapshot {
}
}
// String returns string representation of snapshot
func (s *Snapshot) String() string {
return fmt.Sprintf("[%s]: %s", s.Name, s.Description)
}
// NumPackages returns number of packages in snapshot
func (s *Snapshot) NumPackages() int {
return s.packageRefs.Len()
@@ -50,3 +59,108 @@ func (s *Snapshot) NumPackages() int {
func (s *Snapshot) Key() []byte {
return []byte("S" + s.UUID)
}
// RefKey is a unique id for package reference list
func (s *Snapshot) RefKey() []byte {
return []byte("E" + s.UUID)
}
// Encode does msgpack encoding of Snapshot
func (s *Snapshot) Encode() []byte {
var buf bytes.Buffer
encoder := codec.NewEncoder(&buf, &codec.MsgpackHandle{})
encoder.Encode(s)
return buf.Bytes()
}
// Decode decodes msgpack representation into Snapshot
func (s *Snapshot) Decode(input []byte) error {
decoder := codec.NewDecoderBytes(input, &codec.MsgpackHandle{})
return decoder.Decode(s)
}
// SnapshotCollection does listing, updating/adding/deleting of Snapshots
type SnapshotCollection struct {
db database.Storage
list []*Snapshot
}
// NewSnapshotCollection loads Snapshots from DB and makes up collection
func NewSnapshotCollection(db database.Storage) *SnapshotCollection {
result := &SnapshotCollection{
db: db,
}
blobs := db.FetchByPrefix([]byte("S"))
result.list = make([]*Snapshot, 0, len(blobs))
for _, blob := range blobs {
s := &Snapshot{}
if err := s.Decode(blob); err != nil {
log.Printf("Error decoding snapshot: %s\n", err)
} else {
result.list = append(result.list, s)
}
}
return result
}
// Add appends new repo to collection and saves it
func (collection *SnapshotCollection) Add(snapshot *Snapshot) error {
for _, s := range collection.list {
if s.Name == snapshot.Name {
return fmt.Errorf("snapshot with name %s already exists", snapshot.Name)
}
}
err := collection.Update(snapshot)
if err != nil {
return err
}
collection.list = append(collection.list, snapshot)
return nil
}
// Update stores updated information about repo in DB
func (collection *SnapshotCollection) Update(snapshot *Snapshot) error {
err := collection.db.Put(snapshot.Key(), snapshot.Encode())
if err != nil {
return err
}
return collection.db.Put(snapshot.RefKey(), snapshot.packageRefs.Encode())
}
// LoadComplete loads additional information about snapshot
func (collection *SnapshotCollection) LoadComplete(snapshot *Snapshot) error {
encoded, err := collection.db.Get(snapshot.RefKey())
if err == database.ErrNotFound {
return nil
}
if err != nil {
return err
}
snapshot.packageRefs = &PackageRefList{}
return snapshot.packageRefs.Decode(encoded)
}
// ByName looks up snapshot by name
func (collection *SnapshotCollection) ByName(name string) (*Snapshot, error) {
for _, s := range collection.list {
if s.Name == name {
return s, nil
}
}
return nil, fmt.Errorf("snapshot with name %s not found", name)
}
// ForEach runs method for each snapshot
func (collection *SnapshotCollection) ForEach(handler func(*Snapshot)) {
for _, s := range collection.list {
handler(s)
}
}

View File

@@ -1,6 +1,7 @@
package debian
import (
"github.com/smira/aptly/database"
. "launchpad.net/gocheck"
)
@@ -31,3 +32,88 @@ func (s *SnapshotSuite) TestKey(c *C) {
c.Assert(len(snapshot.Key()), Equals, 37)
c.Assert(snapshot.Key()[0], Equals, byte('S'))
}
func (s *SnapshotSuite) TestRefKey(c *C) {
snapshot := NewSnapshotFromRepository("snap1", s.repo)
c.Assert(len(snapshot.RefKey()), Equals, 37)
c.Assert(snapshot.RefKey()[0], Equals, byte('E'))
c.Assert(snapshot.RefKey()[1:], DeepEquals, snapshot.Key()[1:])
}
func (s *SnapshotSuite) TestEncodeDecode(c *C) {
snapshot := NewSnapshotFromRepository("snap1", s.repo)
s.repo.packageRefs = s.reflist
snapshot2 := &Snapshot{}
c.Assert(snapshot2.Decode(snapshot.Encode()), IsNil)
c.Assert(snapshot2.Name, Equals, snapshot.Name)
c.Assert(snapshot2.packageRefs, IsNil)
}
type SnapshotCollectionSuite struct {
PackageListMixinSuite
db database.Storage
repo1, repo2 *RemoteRepo
snapshot1, snapshot2 *Snapshot
collection *SnapshotCollection
}
var _ = Suite(&SnapshotCollectionSuite{})
func (s *SnapshotCollectionSuite) SetUpTest(c *C) {
s.db, _ = database.OpenDB(c.MkDir())
s.collection = NewSnapshotCollection(s.db)
s.SetUpPackages()
s.repo1, _ = NewRemoteRepo("yandex", "http://mirror.yandex.ru/debian/", "squeeze", []string{"main"}, []string{})
s.repo1.packageRefs = s.reflist
s.snapshot1 = NewSnapshotFromRepository("snap1", s.repo1)
s.repo2, _ = NewRemoteRepo("android", "http://mirror.yandex.ru/debian/", "lenny", []string{"main"}, []string{})
s.repo2.packageRefs = s.reflist
s.snapshot2 = NewSnapshotFromRepository("snap2", s.repo2)
}
func (s *SnapshotCollectionSuite) TearDownTest(c *C) {
s.db.Close()
}
func (s *SnapshotCollectionSuite) TestAddByName(c *C) {
snapshot, err := s.collection.ByName("snap1")
c.Assert(err, ErrorMatches, "*.not found")
c.Assert(s.collection.Add(s.snapshot1), IsNil)
c.Assert(s.collection.Add(s.snapshot1), ErrorMatches, ".*already exists")
c.Assert(s.collection.Add(s.snapshot2), IsNil)
snapshot, err = s.collection.ByName("snap1")
c.Assert(err, IsNil)
c.Assert(snapshot.String(), Equals, s.snapshot1.String())
collection := NewSnapshotCollection(s.db)
snapshot, err = collection.ByName("snap1")
c.Assert(err, IsNil)
c.Assert(snapshot.String(), Equals, s.snapshot1.String())
}
func (s *SnapshotCollectionSuite) TestUpdateLoadComplete(c *C) {
c.Assert(s.collection.Update(s.snapshot1), IsNil)
collection := NewSnapshotCollection(s.db)
snapshot, err := collection.ByName("snap1")
c.Assert(err, IsNil)
c.Assert(snapshot.packageRefs, IsNil)
c.Assert(s.collection.LoadComplete(snapshot), IsNil)
c.Assert(snapshot.NumPackages(), Equals, 3)
}
func (s *SnapshotCollectionSuite) TestForEach(c *C) {
s.collection.Add(s.snapshot1)
s.collection.Add(s.snapshot2)
count := 0
s.collection.ForEach(func(*Snapshot) { count++ })
c.Assert(count, Equals, 2)
}