Files
weave-scope/app/api_topology.go
Alfonso Acosta 0d917b2d8b Use github.com/ugorji/go/codec/ for wiring messages
* New encoding format:
  * Msgpack reports between probe<->app (smaller representation, faster to
    encode/decode).
  * Still use JSON between app<->UI (try to avoid making javascript deal with
    mspack).

  The app still suports publishing reports in both gob and JSON, not braking
  backwards compatibility.

* Use compile-time generated marshallers/unmarshallers for higher performance. In
  order to be able to skip code-generation for certain types, I included
  https://github.com/2opremio/go-1/tree/master/codec/codecgen instead of
  upstream until https://github.com/ugorji/go/pull/139 is merged.

* Encode/decode intermediate types using github.com/ugorji/go/codec.Selfer
  for higher performance and reducing garbage collection (no temporary buffers).
2016-02-16 06:31:31 +00:00

127 lines
2.8 KiB
Go

package app
import (
"net/http"
"time"
log "github.com/Sirupsen/logrus"
"github.com/gorilla/websocket"
"github.com/weaveworks/scope/common/xfer"
"github.com/weaveworks/scope/render"
"github.com/weaveworks/scope/render/detailed"
)
const (
websocketLoop = 1 * time.Second
websocketTimeout = 10 * time.Second
)
// APITopology is returned by the /api/topology/{name} handler.
type APITopology struct {
Nodes render.RenderableNodes `json:"nodes"`
}
// APINode is returned by the /api/topology/{name}/{id} handler.
type APINode struct {
Node detailed.Node `json:"node"`
}
// Full topology.
func handleTopology(rep Reporter, renderer render.Renderer, w http.ResponseWriter, r *http.Request) {
respondWith(w, http.StatusOK, APITopology{
Nodes: renderer.Render(rep.Report()).Prune(),
})
}
// Websocket for the full topology. This route overlaps with the next.
func handleWs(rep Reporter, renderer render.Renderer, w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
respondWith(w, http.StatusInternalServerError, err.Error())
return
}
loop := websocketLoop
if t := r.Form.Get("t"); t != "" {
var err error
if loop, err = time.ParseDuration(t); err != nil {
respondWith(w, http.StatusBadRequest, t)
return
}
}
handleWebsocket(w, r, rep, renderer, loop)
}
// Individual nodes.
func handleNode(nodeID string) func(Reporter, render.Renderer, http.ResponseWriter, *http.Request) {
return func(rep Reporter, renderer render.Renderer, w http.ResponseWriter, r *http.Request) {
var (
rpt = rep.Report()
node, ok = renderer.Render(rep.Report())[nodeID]
)
if !ok {
http.NotFound(w, r)
return
}
respondWith(w, http.StatusOK, APINode{Node: detailed.MakeNode(rpt, node)})
}
}
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool { return true },
}
func handleWebsocket(
w http.ResponseWriter,
r *http.Request,
rep Reporter,
renderer render.Renderer,
loop time.Duration,
) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
// log.Info("Upgrade:", err)
return
}
defer conn.Close()
quit := make(chan struct{})
go func(c *websocket.Conn) {
for { // just discard everything the browser sends
if _, _, err := c.NextReader(); err != nil {
close(quit)
break
}
}
}(conn)
var (
previousTopo render.RenderableNodes
tick = time.Tick(loop)
wait = make(chan struct{}, 1)
)
rep.WaitOn(wait)
defer rep.UnWait(wait)
for {
newTopo := renderer.Render(rep.Report()).Prune()
diff := render.TopoDiff(previousTopo, newTopo)
previousTopo = newTopo
if err := conn.SetWriteDeadline(time.Now().Add(websocketTimeout)); err != nil {
return
}
if err := xfer.WriteJSONtoWS(conn, diff); err != nil {
log.Errorf("cannot serialize topology diff: %s", err)
return
}
select {
case <-wait:
case <-tick:
case <-quit:
return
}
}
}