-
Notifications
You must be signed in to change notification settings - Fork 712
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
642 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"log" | ||
"net/http" | ||
"math/rand" | ||
"sync" | ||
"time" | ||
|
||
"github.com/gorilla/mux" | ||
"github.com/gorilla/websocket" | ||
|
||
"github.com/weaveworks/scope/report" | ||
"github.com/weaveworks/scope/xfer" | ||
) | ||
|
||
const capabilityKey = "capability" | ||
|
||
type CapabilityRouter interface { | ||
HandleCapability(xfer.Reporter, APITopologyDesc, http.ResponseWriter, *http.Request) | ||
HandleProbeWS(w http.ResponseWriter, r *http.Request) | ||
Stop() | ||
} | ||
|
||
type capabilityRouter struct { | ||
sync.Mutex | ||
quit chan struct{} | ||
probes map[string]xfer.CapabilityHandler | ||
} | ||
|
||
func NewCapabilitiyRouter() CapabilityRouter { | ||
return &capabilityRouter{ | ||
probes: map[string]xfer.CapabilityHandler{}, | ||
} | ||
} | ||
|
||
func (cr *capabilityRouter) Stop() { | ||
close(cr.quit) | ||
} | ||
|
||
// Capabilities | ||
func (cr *capabilityRouter) HandleCapability(rep xfer.Reporter, t APITopologyDesc, w http.ResponseWriter, r *http.Request) { | ||
var ( | ||
vars = mux.Vars(r) | ||
nodeID = vars["id"] | ||
node, nodeOK = t.renderer.Render(rep.Report())[nodeID] | ||
arguments = r.URL.Query() | ||
) | ||
if !nodeOK { | ||
http.NotFound(w, r) | ||
return | ||
} | ||
|
||
originHost, ok := node.Metadata[report.HostNodeID] | ||
if !ok { | ||
http.NotFound(w, r) | ||
return | ||
} | ||
|
||
handler, ok := cr.probes[originHost] | ||
if !ok { | ||
http.NotFound(w, r) | ||
return | ||
} | ||
|
||
capabilities, ok := arguments[capabilityKey] | ||
if !ok || len(capabilities) > 1 { | ||
respondWith(w, http.StatusBadRequest, capabilityKey) | ||
return | ||
} | ||
capability := capabilities[0] | ||
delete(arguments, capabilityKey) | ||
|
||
result := handler(xfer.Request{ | ||
ID: rand.Int63(), | ||
NodeID: nodeID, | ||
Capability: capability, | ||
}) | ||
if result.Error != nil { | ||
respondWith(w, http.StatusBadRequest, result.Error) | ||
return | ||
} | ||
respondWith(w, http.StatusOK, result.Value) | ||
} | ||
|
||
func (cr *capabilityRouter) HandleProbeWS(w http.ResponseWriter, r *http.Request) { | ||
probeid := r.Header.Get(xfer.ScopeProbeIDHeader) | ||
if probeid == "" { | ||
respondWith(w, http.StatusBadRequest, xfer.ScopeProbeIDHeader) | ||
return | ||
} | ||
|
||
conn, err := upgrader.Upgrade(w, r, nil) | ||
if err != nil { | ||
log.Printf("Error upgrading to websocket: %v", err) | ||
return | ||
} | ||
defer conn.Close() | ||
|
||
incoming, outgoing := make(chan xfer.Response), make(chan xfer.Request) | ||
quit := make(chan struct{}) | ||
|
||
go func(c *websocket.Conn) { | ||
defer close(quit) | ||
|
||
for { // just discard everything the browser sends | ||
_, reader, err := c.NextReader() | ||
if err != nil { | ||
log.Printf("Error reading message from %s: %v", probeid, err) | ||
return | ||
} | ||
|
||
var response xfer.Response | ||
if err := json.NewDecoder(reader).Decode(&response); err != nil { | ||
log.Printf("Error parsing message from %s: %v", probeid, err) | ||
return | ||
} | ||
|
||
incoming <- response | ||
} | ||
}(conn) | ||
|
||
go func(c *websocket.Conn) { | ||
defer close(quit) | ||
|
||
for { | ||
msg := <-outgoing | ||
if err := conn.SetWriteDeadline(time.Now().Add(websocketTimeout)); err != nil { | ||
log.Printf("Error SetWriteDeadline for %s: %v", probeid, err) | ||
return | ||
} | ||
if err := conn.WriteJSON(msg); err != nil { | ||
log.Printf("Error sending message to %s: %v", probeid, err) | ||
return | ||
} | ||
} | ||
}(conn) | ||
|
||
outstanding := xfer.NewCapabilityMux() | ||
|
||
cr.Lock() | ||
cr.probes[probeid] = func(req xfer.Request) xfer.Response { | ||
outgoing <- req | ||
return outstanding.WaitForResponse(req.ID) | ||
} | ||
cr.Unlock() | ||
|
||
defer func() { | ||
cr.Lock() | ||
defer cr.Unlock() | ||
delete(cr.probes, probeid) | ||
// TODO: cancel outstanding operations | ||
}() | ||
|
||
select { | ||
case <-cr.quit: | ||
return | ||
case <-quit: | ||
return | ||
case res := <-incoming: | ||
if err := outstanding.ResponseReceived(res); err != nil { | ||
log.Printf("Invalid message: %v", err) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package capabilities | ||
|
||
import ( | ||
"fmt" | ||
"sync" | ||
|
||
"github.com/weaveworks/scope/xfer" | ||
) | ||
|
||
var ( | ||
mtx = sync.Mutex{} | ||
handlers = map[string]xfer.CapabilityHandler{} | ||
) | ||
|
||
func HandleCapabilityRequest(req xfer.Request) xfer.Response { | ||
mtx.Lock() | ||
handler, ok := handlers[req.Capability] | ||
mtx.Unlock() | ||
if !ok { | ||
return xfer.Response{ | ||
ID: req.ID, | ||
Error: fmt.Errorf("Capability '%s' not recognised", req.Capability), | ||
} | ||
} | ||
|
||
response := handler(req) | ||
response.ID = req.ID | ||
return response | ||
} | ||
|
||
func Register(capability string, f xfer.CapabilityHandler) { | ||
mtx.Lock() | ||
defer mtx.Unlock() | ||
handlers[capability] = f | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package docker | ||
|
||
import ( | ||
"log" | ||
|
||
"github.com/weaveworks/scope/probe/capabilities" | ||
"github.com/weaveworks/scope/xfer" | ||
) | ||
|
||
const RestartContainer = "docker_restart_container" | ||
|
||
func init() { | ||
capabilities.Register(RestartContainer, func(req xfer.Request) xfer.Response { | ||
log.Printf("Got %s", req.Capability) | ||
return xfer.Response{ | ||
Value: "Hello World", | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package report | ||
|
||
// Capabilities describe the capability tags within the NodeMetadata | ||
type Capabilities map[string]Capability | ||
|
||
// A Capability basically describes an RPC | ||
type Capability struct { | ||
ID string | ||
Human string | ||
Args []Arg | ||
} | ||
|
||
type Arg struct { | ||
Name string | ||
Human string | ||
Type ArgType | ||
} | ||
|
||
type ArgType int | ||
|
||
const ( | ||
Duration ArgType = iota | ||
) | ||
|
||
func (cs Capabilities) Merge(other Capabilities) Capabilities { | ||
result := cs.Copy() | ||
for k, v := range other { | ||
result[k] = v | ||
} | ||
return result | ||
} | ||
|
||
func (cs Capabilities) Copy() Capabilities { | ||
result := Capabilities{} | ||
for k, v := range cs { | ||
result[k] = v | ||
} | ||
return result | ||
} | ||
|
||
func (cs Capabilities) AddCapability(c Capability) { | ||
cs[c.ID] = c | ||
} |
Oops, something went wrong.