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