sermoni

"Service monitor" / cronjob status service
Log | Files | Refs

commit 6c553e5f99e22bbe1284396a6c268a7036fd6e6e
parent d9bb3fc26ddd7fb8213c1decfea5c36e45cbd5b7
Author: Vetle Haflan <vetle@haflan.dev>
Date:   Sat, 11 Apr 2020 11:28:03 +0200

Finish services and events MVP

Will probably be majorly refactored later, but it should be usable
for now

Diffstat:
Mdatabase/database.go | 8++++++++
Mevents/events.go | 33+++++++++++++++++++++++++--------
Mevents/events_test.go | 65++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Mservices/services_test.go | 36+++++++++++++++++++++---------------
4 files changed, 116 insertions(+), 26 deletions(-)

diff --git a/database/database.go b/database/database.go @@ -145,3 +145,11 @@ func BytesToUint64(byteData []byte) uint64 { func Uint64ToBytes(uint64Data uint64) []byte { return []byte(strconv.FormatUint(uint64Data, 10)) } + +// PrintBucket simply prints the K-V pairs in the bucket +func PrintBucket(eb *bbolt.Bucket) error { + return eb.ForEach(func(k, v []byte) error { + fmt.Printf("- %v: %v\n", string(k), string(v)) + return nil + }) +} diff --git a/events/events.go b/events/events.go @@ -2,6 +2,7 @@ package events import ( "errors" + "fmt" "log" "sermoni/database" "sermoni/services" @@ -32,7 +33,7 @@ type Event struct { // GetAll returns all events in the database func GetAll() (events []*Event) { db := database.GetDB() - db.View(func(tx *bbolt.Tx) error { + err := db.View(func(tx *bbolt.Tx) error { b := tx.Bucket(database.BucketKeyEvents) if b == nil { log.Panic("The events bucket does not exist") @@ -40,7 +41,7 @@ func GetAll() (events []*Event) { // ForEach doesn't return buckets (nil instead), so only the key is useful return b.ForEach(func(id, _ []byte) error { eb := b.Bucket(id) - event := &Event{} + event := new(Event) if err := event.fromBucket(eb); err != nil { return err } @@ -48,12 +49,24 @@ func GetAll() (events []*Event) { return nil }) }) + if err != nil { + fmt.Println(err) + } return } -// Delete a service event -func Delete() { - +// Delete event with the given ID. +// Returns error if no such event can be found +func Delete(idInt uint64) error { + db := database.GetDB() + id := database.Uint64ToBytes(idInt) + return db.View(func(tx *bbolt.Tx) error { + b := tx.Bucket(database.BucketKeyEvents) + if b == nil { + log.Panic("The events bucket does notexist") + } + return b.DeleteBucket(id) + }) } // Add a new service event if the token matches any services in the database @@ -92,11 +105,15 @@ func Add(serviceToken string, event *Event) error { func (event *Event) toBucket(eb *bbolt.Bucket) error { var err error id := database.Uint64ToBytes(event.ID) - service := database.Uint64ToBytes(event.Service) + serviceID := database.Uint64ToBytes(event.Service) + timestamp := database.Uint64ToBytes(event.Timestamp) if err = eb.Put(keyEventID, id); err != nil { return err } - if eb.Put(keyEventService, service); err != nil { + if eb.Put(keyEventService, serviceID); err != nil { + return err + } + if eb.Put(keyEventTimestamp, timestamp); err != nil { return err } if eb.Put(keyEventStatus, []byte(event.Status)); err != nil { @@ -119,7 +136,7 @@ func (event *Event) fromBucket(eb *bbolt.Bucket) error { err := errors.New("missing field from database") // Get data from database - if id := eb.Get(keyEventID); id == nil { + if id = eb.Get(keyEventID); id == nil { return err } if service = eb.Get(keyEventService); service == nil { diff --git a/events/events_test.go b/events/events_test.go @@ -1,6 +1,7 @@ package events import ( + "fmt" "os" "sermoni/database" "sermoni/services" @@ -9,11 +10,69 @@ import ( const serviceToken = "test-service" -func TestAddEvent(t *testing.T) { - Add(serviceToken, &Event{ +func (e1 *Event) equals(e2 *Event) bool { + switch { + case e1.ID != e2.ID: + return false + case e1.Service != e2.Service: + return false + case e1.Status != e2.Status: + return false + case e1.Title != e2.Title: + return false + case e1.Details != e2.Details: + return false + default: + return true + } +} + +var testEvents = []*Event{ + { Timestamp: 1586558825515, + Status: "ok", Title: "Backup completed successfully", - }) + }, + { + Timestamp: 1586558838488, + Status: "info", + Title: "SSH login for user vetle", + Details: "User vetle logged in from IP 192.168.10.110", + }, + { + Timestamp: 1586558848488, + Status: "ok", + }, + { + Timestamp: 1586558949488, + Status: "error", + Title: "Backup failed", + Details: "Backup couldn't complete because the disk is full", + }, +} + +func TestAddEvent(t *testing.T) { + for _, event := range testEvents { + if err := Add(serviceToken, event); err != nil { + fmt.Println(err) + t.Fatal("error returned when trying to add event") + } + } + + // Assumes that bbolt starts sequences on 1 + for i, event := range testEvents { + event.ID = uint64(i) + 1 + event.Service = 1 + } +} + +func TestGetAll(t *testing.T) { + events := GetAll() + for i, event := range events { + if !event.equals(testEvents[i]) { + t.Fatal("stored event does not match original") + } + } } func TestMain(m *testing.M) { diff --git a/services/services_test.go b/services/services_test.go @@ -17,20 +17,19 @@ func intID(id []byte) uint64 { return idInt } -func (s1 *Service) equals (s2 *Service) bool { - if s1.ID != s2.ID { +func (s1 *Service) equals(s2 *Service) bool { + switch { + case s1.ID != s2.ID: return false - } - if s1.Name != s2.Name { + case s1.Name != s2.Name: return false - } - if s1.Description != s2.Description { + case s1.Description != s2.Description: return false - } - if s1.ExpectationPeriod != s2.ExpectationPeriod { + case s1.ExpectationPeriod != s2.ExpectationPeriod: return false + default: + return true } - return true } var ( @@ -40,13 +39,13 @@ var ( ) var testServices = []*Service{ - &Service{ + { Name: "tester @ dev-computer", Description: "This describes the service in more detail", ExpectationPeriod: 282342, }, - &Service{Name: "tester2", ExpectationPeriod: 300003}, - &Service{Name: "third @ tester"}, + {Name: "tester2", ExpectationPeriod: 300003}, + {Name: "third @ tester"}, } func TestAddService(t *testing.T) { @@ -69,16 +68,15 @@ func TestAddService(t *testing.T) { // Simulate ID generation for testServices after adding them to DB, to avoid // possible interferrence (shouldn't be a problem, but doesn't hurt to be sure). - // bbolt should always start ID sequences on 1, so this assumes that the service ID + // bbolt should always start ID sequences on 1, so this assumes that the service ID // equals the testService index + 1 for i, service := range testServices { service.ID = uint64(i) + 1 } } - func TestDeleteService(t *testing.T) { - var di uint64 = 1 // Deletion index + var di uint64 = 1 // Deletion index err := Delete(di + 1) if err != nil { fmt.Println(err) @@ -88,6 +86,11 @@ func TestDeleteService(t *testing.T) { t.Fatal("no error returned when trying to delete non-existing service") } + // Assert that the service token is deleted too + if service := GetByToken(token2); service != nil { + t.Fatal("the service token was not deleted") + } + // Delete from testServices too testServices = append(testServices[:di], testServices[di+1:]...) } @@ -122,6 +125,9 @@ func TestGetAll(t *testing.T) { t.Fatal("stored service doesn't match original") } } + if services[0].equals(testServices[1]) { + t.Fatal("unexpected match between two services") + } } func TestMain(m *testing.M) {