Skip to content

Commit

Permalink
Merge pull request #86 from SpectoLabs/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
Karolis Rusenas committed Jan 27, 2016
2 parents 84e29cc + e233642 commit 0ea52c1
Show file tree
Hide file tree
Showing 17 changed files with 679 additions and 92 deletions.
8 changes: 6 additions & 2 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
[submodule "statik"]
path = vendor/github.com/rakyll/statik
url = https://github.com/rakyll/statik


[submodule "websocket"]
path = vendor/github.com/gorilla/websocket
url = https://github.com/gorilla/websocket
[submodule "go-metrics"]
path = vendor/github.com/rcrowley/go-metrics
url = https://github.com/rcrowley/go-metrics

106 changes: 103 additions & 3 deletions admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"io/ioutil"
"net/http"
"time"

// static assets
_ "github.com/SpectoLabs/hoverfly/statik"
Expand All @@ -14,6 +15,7 @@ import (
log "github.com/Sirupsen/logrus"
"github.com/codegangsta/negroni"
"github.com/go-zoo/bone"
"github.com/gorilla/websocket"
"github.com/meatballhat/negroni-logrus"
)

Expand All @@ -26,6 +28,11 @@ type recordsCount struct {
Count int `json:"count"`
}

type statsResponse struct {
Stats HoverflyStats `json:"stats"`
RecordsCount int `json:"recordsCount"`
}

type stateRequest struct {
Mode string `json:"mode"`
Destination string `json:"destination"`
Expand Down Expand Up @@ -74,11 +81,17 @@ func getBoneRouter(d DBClient) *bone.Mux {
mux.Post("/records", http.HandlerFunc(d.ImportRecordsHandler))

mux.Get("/count", http.HandlerFunc(d.RecordsCount))
mux.Get("/stats", http.HandlerFunc(d.StatsHandler))
mux.Get("/statsws", http.HandlerFunc(d.StatsWSHandler))

mux.Get("/state", http.HandlerFunc(d.CurrentStateHandler))
mux.Post("/state", http.HandlerFunc(d.StateHandler))

mux.Handle("/*", http.FileServer(statikFS))
if d.cfg.development {
mux.Handle("/*", http.FileServer(http.Dir("static/dist")))
} else {
mux.Handle("/*", http.FileServer(statikFS))
}

return mux
}
Expand Down Expand Up @@ -115,14 +128,14 @@ func (d *DBClient) AllRecordsHandler(w http.ResponseWriter, req *http.Request) {

// RecordsCount returns number of captured requests as a JSON payload
func (d *DBClient) RecordsCount(w http.ResponseWriter, req *http.Request) {
records, err := d.cache.GetAllRequests()
count, err := d.cache.RecordsCount()

if err == nil {

w.Header().Set("Content-Type", "application/json")

var response recordsCount
response.Count = len(records)
response.Count = count
b, err := json.Marshal(response)

if err != nil {
Expand All @@ -143,6 +156,93 @@ func (d *DBClient) RecordsCount(w http.ResponseWriter, req *http.Request) {
}
}

// StatsHandler - returns current stats about Hoverfly (request counts, record count)
func (d *DBClient) StatsHandler(w http.ResponseWriter, req *http.Request) {
stats := d.counter.Flush()

count, err := d.cache.RecordsCount()

if err != nil {
log.Error(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
}

var sr statsResponse
sr.Stats = stats
sr.RecordsCount = count

w.Header().Set("Content-Type", "application/json")

b, err := json.Marshal(sr)

if err != nil {
log.Error(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
} else {
w.Write(b)
return
}

}

var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool {
return true
},
}

// StatsWSHandler - returns current stats about Hoverfly (request counts, record count) through the websocket
func (d *DBClient) StatsWSHandler(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println(err)
return
}

for {
messageType, p, err := conn.ReadMessage()
if err != nil {
return
}
log.WithFields(log.Fields{
"message": string(p),
}).Info("Got message...")

for _ = range time.Tick(1 * time.Second) {

count, err := d.cache.RecordsCount()

if err != nil {
log.WithFields(log.Fields{
"message": p,
"error": err.Error(),
}).Error("got error while trying to get records count")
return
}

stats := d.counter.Flush()

var sr statsResponse
sr.Stats = stats
sr.RecordsCount = count

b, err := json.Marshal(sr)

if err = conn.WriteMessage(messageType, b); err != nil {
log.WithFields(log.Fields{
"message": p,
"error": err.Error(),
}).Error("Got error when writing message...")
return
}
}

}

}

// ImportRecordsHandler - accepts JSON payload and saves it to cache
func (d *DBClient) ImportRecordsHandler(w http.ResponseWriter, req *http.Request) {

Expand Down
159 changes: 158 additions & 1 deletion admin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ func TestSetNoBody(t *testing.T) {
// setting mode to virtualize
dbClient.cfg.SetMode("virtualize")

// deleting through handler
// setting state
req, err := http.NewRequest("POST", "/state", nil)
expect(t, err, nil)
//The response recorder used to record HTTP responses
Expand All @@ -401,3 +401,160 @@ func TestSetNoBody(t *testing.T) {
// checking mode, should not have changed
expect(t, dbClient.cfg.GetMode(), "virtualize")
}

func TestStatsHandler(t *testing.T) {
server, dbClient := testTools(200, `{'message': 'here'}`)
defer server.Close()
defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
m := getBoneRouter(*dbClient)

// deleting through handler
req, err := http.NewRequest("GET", "/stats", nil)

expect(t, err, nil)
//The response recorder used to record HTTP responses
rec := httptest.NewRecorder()

m.ServeHTTP(rec, req)
expect(t, rec.Code, http.StatusOK)
}

func TestStatsHandlerVirtualizeMetrics(t *testing.T) {
// test metrics, increases virtualize count by 1 and then checks through stats
// handler whether it is visible through /stats handler
server, dbClient := testTools(200, `{'message': 'here'}`)
defer server.Close()
defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
m := getBoneRouter(*dbClient)

dbClient.counter.counterVirtualize.Inc(1)

req, err := http.NewRequest("GET", "/stats", nil)

expect(t, err, nil)
//The response recorder used to record HTTP responses
rec := httptest.NewRecorder()

m.ServeHTTP(rec, req)
expect(t, rec.Code, http.StatusOK)

body, err := ioutil.ReadAll(rec.Body)

sr := statsResponse{}
err = json.Unmarshal(body, &sr)

expect(t, int(sr.Stats.Counters[VirtualizeMode]), 1)
}

func TestStatsHandlerCaptureMetrics(t *testing.T) {
// test metrics, increases capture count by 1 and then checks through stats
// handler whether it is visible through /stats handler
server, dbClient := testTools(200, `{'message': 'here'}`)
defer server.Close()
defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
m := getBoneRouter(*dbClient)

dbClient.counter.counterCapture.Inc(1)

req, err := http.NewRequest("GET", "/stats", nil)

expect(t, err, nil)
//The response recorder used to record HTTP responses
rec := httptest.NewRecorder()

m.ServeHTTP(rec, req)
expect(t, rec.Code, http.StatusOK)

body, err := ioutil.ReadAll(rec.Body)

sr := statsResponse{}
err = json.Unmarshal(body, &sr)

expect(t, int(sr.Stats.Counters[CaptureMode]), 1)
}

func TestStatsHandlerModifyMetrics(t *testing.T) {
// test metrics, increases modify count by 1 and then checks through stats
// handler whether it is visible through /stats handler
server, dbClient := testTools(200, `{'message': 'here'}`)
defer server.Close()
defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
m := getBoneRouter(*dbClient)

dbClient.counter.counterModify.Inc(1)

req, err := http.NewRequest("GET", "/stats", nil)

expect(t, err, nil)
//The response recorder used to record HTTP responses
rec := httptest.NewRecorder()

m.ServeHTTP(rec, req)
expect(t, rec.Code, http.StatusOK)

body, err := ioutil.ReadAll(rec.Body)

sr := statsResponse{}
err = json.Unmarshal(body, &sr)

expect(t, int(sr.Stats.Counters[ModifyMode]), 1)
}

func TestStatsHandlerSynthesizeMetrics(t *testing.T) {
// test metrics, increases synthesize count by 1 and then checks through stats
// handler whether it is visible through /stats handler
server, dbClient := testTools(200, `{'message': 'here'}`)
defer server.Close()
defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
m := getBoneRouter(*dbClient)

dbClient.counter.counterSynthesize.Inc(1)

req, err := http.NewRequest("GET", "/stats", nil)

expect(t, err, nil)
//The response recorder used to record HTTP responses
rec := httptest.NewRecorder()

m.ServeHTTP(rec, req)
expect(t, rec.Code, http.StatusOK)

body, err := ioutil.ReadAll(rec.Body)

sr := statsResponse{}
err = json.Unmarshal(body, &sr)

expect(t, int(sr.Stats.Counters[SynthesizeMode]), 1)
}

func TestStatsHandlerRecordCountMetrics(t *testing.T) {
// test metrics, adds 5 new requests and then checks through stats
// handler whether it is visible through /stats handler
server, dbClient := testTools(200, `{'message': 'here'}`)
defer server.Close()
defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
m := getBoneRouter(*dbClient)

// inserting some payloads
for i := 0; i < 5; i++ {
req, err := http.NewRequest("GET", fmt.Sprintf("http://example.com/q=%d", i), nil)
expect(t, err, nil)
dbClient.captureRequest(req)
}

req, err := http.NewRequest("GET", "/stats", nil)

expect(t, err, nil)
//The response recorder used to record HTTP responses
rec := httptest.NewRecorder()

m.ServeHTTP(rec, req)
expect(t, rec.Code, http.StatusOK)

body, err := ioutil.ReadAll(rec.Body)

sr := statsResponse{}
err = json.Unmarshal(body, &sr)

expect(t, int(sr.RecordsCount), 5)
}
16 changes: 16 additions & 0 deletions cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,22 @@ func (c *Cache) GetAllRequests() (payloads []Payload, err error) {
return
}

// RecordsCount - returns records count
func (c *Cache) RecordsCount() (count int, err error) {
err = c.db.View(func(tx *bolt.Tx) error {
b := tx.Bucket(c.requestsBucket)
if b == nil {
// bucket doesn't exist
return nil
}

count = b.Stats().KeyN

return nil
})
return
}

// DeleteBucket - deletes bucket with all saved data
func (c *Cache) DeleteBucket(name []byte) (err error) {
err = c.db.Update(func(tx *bolt.Tx) error {
Expand Down
2 changes: 2 additions & 0 deletions glide.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ import:
- package: github.com/go-zoo/bone
- package: github.com/boltdb/bolt
- package: github.com/rakyll/statik
- package: github.com/rcrowley/go-metrics
- package: github.com/gorilla/websocket
Loading

0 comments on commit 0ea52c1

Please sign in to comment.