diff --git a/Makefile b/Makefile index 4c97719..db4d2a7 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ # go install -all: package.pb.go +all: package.pb.go machine.pb.go make -C example vet: lint @@ -36,3 +36,9 @@ package.pb.go: package.proto cd ~/go/src && protoc --go_out=. --proto_path=go.wit.com/lib/protobuf/zoopb \ --go_opt=Mpackage.proto=go.wit.com/lib/protobuf/zoopb \ package.proto + +machine.pb.go: machine.proto + cd ~/go/src && protoc --go_out=. --proto_path=go.wit.com/lib/protobuf/zoopb \ + --go_opt=Mpackage.proto=go.wit.com/lib/protobuf/zoopb \ + --go_opt=Mmachine.proto=go.wit.com/lib/protobuf/zoopb \ + machine.proto diff --git a/helpers.go b/helpers.go index 618bec1..6bbb1f7 100644 --- a/helpers.go +++ b/helpers.go @@ -42,3 +42,12 @@ func (p *Packages) Unmarshal(data []byte) error { return proto.Unmarshal(data, p) } +// marshal to wire +func (m *Machine) Marshal() ([]byte, error) { + return proto.Marshal(m) +} + +// unmarshal from wire +func (m *Machine) Unmarshal(data []byte) error { + return proto.Unmarshal(data, m) +} diff --git a/machine.proto b/machine.proto new file mode 100644 index 0000000..55772aa --- /dev/null +++ b/machine.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; + +package gitpb; + +import "package.proto"; +import "google/protobuf/timestamp.proto"; // Import the well-known type for Timestamp + +message Machine { + string hostname = 1; + int64 memory = 2; + int64 cpus = 3; + Packages packages = 4; + google.protobuf.Timestamp laststamp = 5; // the last time we heard anything from this machine +} + +message Machines { + string uuid = 1; // I guess why not just have this on each file + string version = 2; // maybe can be used for protobuf schema change violations + repeated Machine machines = 3; +} diff --git a/machines.go b/machines.go new file mode 100644 index 0000000..52a37e7 --- /dev/null +++ b/machines.go @@ -0,0 +1,135 @@ +package zoopb + +// this is becoming a standard format +// todo: autogenerate this from the .proto file? + +import ( + "fmt" + "os" + "sort" + sync "sync" + "time" +) + +// bad global lock until I figure out some other plan +var machinesLock sync.RWMutex + +type MachineIterator struct { + sync.RWMutex + + packs []*Machine + index int +} + +// NewMachineIterator initializes a new iterator. +func NewMachineIterator(packs []*Machine) *MachineIterator { + return &MachineIterator{packs: packs} +} + +// Scan moves to the next element and returns false if there are no more packs. +func (it *MachineIterator) Scan() bool { + if it.index >= len(it.packs) { + return false + } + it.index++ + return true +} + +// Machine returns the current repo. +func (it *MachineIterator) Machine() *Machine { + if it.packs[it.index-1] == nil { + for i, d := range it.packs { + fmt.Println("i =", i, d) + } + fmt.Println("len =", len(it.packs)) + fmt.Println("repo == nil", it.index, it.index-1) + os.Exit(-1) + } + return it.packs[it.index-1] +} + +// Use Scan() in a loop, similar to a while loop +// +// for iterator.Scan() { +// d := iterator.Machine() +// fmt.Println("Machine UUID:", d.Uuid) +// } + +func (r *Machines) All() *MachineIterator { + repoPointers := r.selectAllMachines() + + iterator := NewMachineIterator(repoPointers) + return iterator +} + +func (r *Machines) SortByName() *MachineIterator { + packs := r.selectAllMachines() + + sort.Sort(ByMachineName(packs)) + + iterator := NewMachineIterator(packs) + return iterator +} + +// enforces no duplicate package names +func (r *Machines) Append(newP *Machine) bool { + machinesLock.Lock() + defer machinesLock.Unlock() + + for _, p := range r.Machines { + if p.Hostname == newP.Hostname { + return false + } + } + + r.Machines = append(r.Machines, newP) + return true +} + +// returns time.Duration since last Update() +func (r *Machine) Age(newP *Machine) time.Duration { + t := time.Since(r.Laststamp.AsTime()) + return t +} + +// find a package by name +func (r *Machines) FindByName(name string) *Machine { + machinesLock.RLock() + defer machinesLock.RUnlock() + + for _, p := range r.Machines { + if p.Hostname == name { + return p + } + } + + return nil +} + +func (r *Machines) Len() int { + machinesLock.RLock() + defer machinesLock.RUnlock() + + return len(r.Machines) +} + +type ByMachineName []*Machine + +func (a ByMachineName) Len() int { return len(a) } +func (a ByMachineName) Less(i, j int) bool { return a[i].Hostname < a[j].Hostname } +func (a ByMachineName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +// safely returns a slice of pointers to the Machine protobufs +func (r *Machines) selectAllMachines() []*Machine { + machinesLock.RLock() + defer machinesLock.RUnlock() + + // Create a new slice to hold pointers to each Machine + var allPacks []*Machine + allPacks = make([]*Machine, len(r.Machines)) + for i, p := range r.Machines { + allPacks[i] = p // Copy pointers for safe iteration + } + + return allPacks +}