Repository: allegro/marathon-consul
Pull request #142 (2016-11-18 16:05:33)

Parse event timestamp and add more events metrics

coveralls: 
[![Coverage Status](https://coveralls.io/builds/8897318/badge)](https://coveralls.io/builds/8897318)

Coverage decreased (-0.4%) to 82.088% when pulling **20b37fa1e2f97db9e284967d890391ea79a8319a on add_logging** into **cf1abbb4aef154b4e048730debe76e34444e6c56 on master**.

coveralls: 
[![Coverage Status](https://coveralls.io/builds/8947184/badge)](https://coveralls.io/builds/8947184)

Coverage decreased (-0.4%) to 82.112% when pulling **3da77c30f89e9e7c03a5126263e51cf5db49638d on add_logging** into **cf1abbb4aef154b4e048730debe76e34444e6c56 on master**.

coveralls: 
[![Coverage Status](https://coveralls.io/builds/9022052/badge)](https://coveralls.io/builds/9022052)

Coverage decreased (-0.2%) to 82.297% when pulling **76f2054b7671acb9b2acdc6d86c684ffc132a384 on add_logging** into **cf1abbb4aef154b4e048730debe76e34444e6c56 on master**.


Patch:
diff --git a/events/events.go b/events/events.go
index 3b23cea..b433f34 100644
--- a/events/events.go
+++ b/events/events.go
@@ -3,27 +3,42 @@ package events
 import (
 	"encoding/json"
 	"errors"
-
-	"github.com/allegro/marathon-consul/apps"
+	"strings"
+	"time"
 )
 
-type Event interface {
-	Apps() []*apps.App
-	GetType() string
+type Timestamp struct {
+	time.Time
+}
+
+func (t *Timestamp) UnmarshalJSON(b []byte) (err error) {
+	s := strings.Trim(string(b), "\"")
+	if s == "null" {
+		t.Time = time.Time{}
+		return
+	}
+	t.Time, err = time.Parse(time.RFC3339Nano, s)
+	return
+}
+
+func (t *Timestamp) String() string {
+	return t.Format(time.RFC3339Nano)
 }
 
-type baseEvent struct {
-	Type string `json:"eventType"`
+type Event struct {
+	Type      string    `json:"eventType"`
+	Timestamp Timestamp `json:"timestamp"`
 }
 
-func EventType(jsonBlob []byte) (string, error) {
-	event := baseEvent{}
+func ParseEvent(jsonBlob []byte) (Event, error) {
+	event := Event{}
 	err := json.Unmarshal(jsonBlob, &event)
 	if err != nil {
-		return "", err
+		return Event{}, err
 	} else if event.Type == "" {
-		return "", errors.New("no event")
-	} else {
-		return event.Type, nil
+		return Event{}, errors.New("Missing event type")
+	} else if event.Timestamp.Unix() == (time.Time{}).Unix() {
+		return Event{}, errors.New("Missing timestamp")
 	}
+	return event, nil
 }
diff --git a/marathon/marathon.go b/marathon/marathon.go
index 0c78418..ed4c6fa 100644
--- a/marathon/marathon.go
+++ b/marathon/marathon.go
@@ -113,6 +113,7 @@ func (m Marathon) get(url string) ([]byte, error) {
 		return nil, err
 	}
 	request.Header.Add("Accept", "application/json")
+	request.Header.Set("User-Agent", "Marathon-Consul")
 
 	log.WithFields(log.Fields{
 		"Uri":      request.URL.RequestURI(),
diff --git a/web/web_handler.go b/web/web_handler.go
index 788e44d..b4b01b5 100644
--- a/web/web_handler.go
+++ b/web/web_handler.go
@@ -48,21 +48,23 @@ func (h *EventHandler) Handle(w http.ResponseWriter, r *http.Request) {
 			return
 		}
 
-		eventType, err := events.EventType(body)
+		e, err := events.ParseEvent(body)
 		if err != nil {
 			drop(err, w)
 			return
 		}
 
-		log.WithField("EventType", eventType).Debug("Received event")
-		metrics.Mark("events.requests." + eventType)
+		metrics.Mark("events.requests." + e.Type)
+		delay := time.Now().Unix() - e.Timestamp.Unix()
+		metrics.UpdateGauge("events.requests.delay.current", delay)
+		log.WithFields(log.Fields{"EventType": e.Type, "OriginalTimestamp": e.Timestamp.String()}).Debug("Received event")
 
-		if eventType != statusUpdateEventType && eventType != healthStatusChangedEventType {
-			drop(fmt.Errorf("%s is not supported", eventType), w)
+		if e.Type != statusUpdateEventType && e.Type != healthStatusChangedEventType {
+			drop(fmt.Errorf("%s is not supported", e.Type), w)
 			return
 		}
 
-		h.eventQueue <- event{eventType: eventType, body: body, timestamp: time.Now()}
+		h.eventQueue <- event{eventType: e.Type, body: body, timestamp: time.Now()}
 		accept(w)
 
 	})


Test patch:
diff --git a/events/events_test.go b/events/events_test.go
index 7c43856..3d0f470 100644
--- a/events/events_test.go
+++ b/events/events_test.go
@@ -8,24 +8,37 @@ import (
 
 func TestEventTypeValid(t *testing.T) {
 	t.Parallel()
-
-	out, err := EventType([]byte(`{"eventType":"test"}`))
+	out, err := ParseEvent([]byte(`{"eventType":"test","timestamp": "2014-03-01T23:29:30.158Z"}`))
 	assert.Nil(t, err)
-	assert.Equal(t, out, "test")
+	assert.Equal(t, "2014-03-01T23:29:30.158Z", out.Timestamp.String())
 }
 
 func TestEventTypeInvalid(t *testing.T) {
 	t.Parallel()
 
-	out, err := EventType([]byte(`{}`))
+	out, err := ParseEvent([]byte(`{}`))
+	assert.Error(t, err)
+	assert.Equal(t, out, Event{})
+}
+
+func TestTimestampInvalid(t *testing.T) {
+	t.Parallel()
+	out, err := ParseEvent([]byte(`{"eventType":"test","timestamp": "invalid"}`))
 	assert.Error(t, err)
-	assert.Equal(t, out, "")
+	assert.Equal(t, out, Event{})
+}
+
+func TestMissingTimestamp(t *testing.T) {
+	t.Parallel()
+	out, err := ParseEvent([]byte(`{"eventType":"test"}`))
+	assert.EqualError(t, err, "Missing timestamp")
+	assert.Equal(t, out, Event{})
 }
 
 func TestEventTypeInvalidJson(t *testing.T) {
 	t.Parallel()
 
-	out, err := EventType([]byte(`not a json`))
+	out, err := ParseEvent([]byte(`not a json`))
 	assert.Error(t, err)
-	assert.Equal(t, out, "")
+	assert.Equal(t, out, Event{})
 }