sermoni

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

events.go (3656B)


      1 package http
      2 
      3 import (
      4 	"encoding/json"
      5 	"fmt"
      6 	"io/ioutil"
      7 	"log"
      8 	"net/http"
      9 	"sermoni/internal/events"
     10 	"sermoni/internal/services"
     11 	"strconv"
     12 	"time"
     13 
     14 	"github.com/gorilla/mux"
     15 )
     16 
     17 const headerServiceToken = "Service-Token"
     18 
     19 func now() uint64 {
     20 	return uint64(time.Now().UnixNano() / 1e6)
     21 }
     22 
     23 // getEvents fetches all events in the database and checks timestamp for the last report
     24 // from each service. If the time since last report is more than the expected report interval,
     25 // a "live" event is generated to inform that no report has been received
     26 func getEvents(w http.ResponseWriter, r *http.Request) {
     27 	// Create mappings from service id to the actual service and to the last service report
     28 	serviceIDMap := make(map[uint64]*services.Service)
     29 	serviceIsLate := make(map[uint64]bool)
     30 	servs := services.GetAll()
     31 	for _, service := range servs {
     32 		serviceIDMap[service.ID] = service
     33 		serviceIsLate[service.ID] = service.ExpectationPeriod != 0
     34 	}
     35 
     36 	var s *services.Service
     37 	events := events.GetAll()
     38 	for _, e := range events {
     39 		sid := e.Service
     40 		s = serviceIDMap[sid]
     41 		e.ServiceName = s.Name
     42 		if serviceIsLate[sid] && now()-e.Timestamp < s.ExpectationPeriod {
     43 			serviceIsLate[sid] = false
     44 		}
     45 	}
     46 	for sid, late := range serviceIsLate {
     47 		if late {
     48 			s := serviceIDMap[sid]
     49 			events = append(events, generateLateEvent(s))
     50 		}
     51 	}
     52 	b, _ := json.Marshal(events)
     53 	w.Write(b)
     54 }
     55 
     56 // TODO: This is still a placeholder!
     57 func deleteEvent(w http.ResponseWriter, r *http.Request) {
     58 	vars := mux.Vars(r)
     59 	id, _ := strconv.ParseUint(vars["id"], 10, 64)
     60 	err := events.Delete(id)
     61 	if err != nil {
     62 		// TODO: Non-existing error is not an internal server error
     63 		fmt.Println(err)
     64 		w.WriteHeader(http.StatusInternalServerError)
     65 	}
     66 }
     67 
     68 func reportEvent(w http.ResponseWriter, r *http.Request) {
     69 	tokens := r.Header[headerServiceToken]
     70 	if len(tokens) == 0 {
     71 		w.WriteHeader(http.StatusUnauthorized)
     72 		msg := fmt.Sprintf("%v: No service token given\n", http.StatusUnauthorized)
     73 		w.Write([]byte(msg))
     74 		return
     75 	}
     76 	service := services.GetByToken(tokens[0])
     77 	if service == nil {
     78 		msg := fmt.Sprintf("%v: No service for the given token\n", http.StatusUnauthorized)
     79 		w.Write([]byte(msg))
     80 		return
     81 	}
     82 	content, err := ioutil.ReadAll(r.Body)
     83 	check(err)
     84 	// TODO: This should deffo not panic on error!
     85 	event := new(events.Event)
     86 	err = json.Unmarshal(content, event)
     87 	check(err)
     88 	event.Service = service.ID
     89 	event.Timestamp = now()
     90 	err = events.Add(event)
     91 	check(err)
     92 	log.Printf("New event registered, id = %v\n", event.ID)
     93 
     94 	if service.MaxNumberEvents < 1 {
     95 		return
     96 	}
     97 	// Find whether MaxNumberEvents is reached and delete the first event if so
     98 	var numEvents, firstEventID uint64
     99 	es := events.GetAll()
    100 	for _, e := range es {
    101 		if e.Service == service.ID {
    102 			if firstEventID == 0 {
    103 				firstEventID = e.ID
    104 			}
    105 			numEvents++
    106 		}
    107 	}
    108 	if numEvents > service.MaxNumberEvents {
    109 		events.Delete(firstEventID)
    110 		log.Printf("MaxNumberEvents reached for service %v. Deleting first event, %v", service.ID, firstEventID)
    111 	}
    112 	w.WriteHeader(http.StatusCreated)
    113 }
    114 
    115 func generateLateEvent(s *services.Service) *events.Event {
    116 	return &events.Event{
    117 		ID:        0,
    118 		Service:   s.ID,
    119 		Timestamp: now(),
    120 		Status:    "late",
    121 		Title:     "Expectation not met",
    122 		Details: s.Name + " has failed to report within the expected interval. " +
    123 			"Something is probably wrong.",
    124 		ServiceName: s.Name,
    125 	}
    126 }
    127 
    128 // For later reference:
    129 // Instead of anonymous structs for each JSON object to be written,
    130 // a simple map can be used instead, something like (untested!):
    131 //	b, _ := json.Marshal(&map[string]string{
    132 //	 	"message": http.StatusUnauthorized + ": No valid token"
    133 //	})