Merge pull request #229 from weaveworks/fix-scoping

Fix node scoping rules
This commit is contained in:
Peter Bourgon
2015-06-15 15:02:37 +02:00
10 changed files with 144 additions and 52 deletions

View File

@@ -32,8 +32,7 @@ func TestAPITopologyApplications(t *testing.T) {
report.MakeEndpointNodeID("hostA", "192.168.1.1", "12345"),
report.MakeEndpointNodeID("hostA", "192.168.1.1", "12346"),
report.MakeHostNodeID("hostA"),
), node.Origins,
)
), node.Origins)
equals(t, "curl", node.LabelMajor)
equals(t, "node-a.local (23128)", node.LabelMinor)
equals(t, "23128", node.Rank)

View File

@@ -55,24 +55,28 @@ func (s StaticReport) Report() report.Report {
},
NodeMetadatas: report.NodeMetadatas{
report.MakeEndpointNodeID("hostA", "192.168.1.1", "12345"): report.NodeMetadata{
"pid": "23128",
"name": "curl",
"domain": "node-a.local",
"pid": "23128",
"name": "curl",
"domain": "node-a.local",
report.HostNodeID: report.MakeHostNodeID("hostA"),
},
report.MakeEndpointNodeID("hostA", "192.168.1.1", "12346"): report.NodeMetadata{ // <-- same as :12345
"pid": "23128",
"name": "curl",
"domain": "node-a.local",
"pid": "23128",
"name": "curl",
"domain": "node-a.local",
report.HostNodeID: report.MakeHostNodeID("hostA"),
},
report.MakeEndpointNodeID("hostA", "192.168.1.1", "8888"): report.NodeMetadata{
"pid": "55100",
"name": "ssh",
"domain": "node-a.local",
"pid": "55100",
"name": "ssh",
"domain": "node-a.local",
report.HostNodeID: report.MakeHostNodeID("hostA"),
},
report.MakeEndpointNodeID("hostB", "192.168.1.2", "80"): report.NodeMetadata{
"pid": "215",
"name": "apache",
"domain": "node-b.local",
"pid": "215",
"name": "apache",
"domain": "node-b.local",
report.HostNodeID: report.MakeHostNodeID("hostB"),
},
},
},
@@ -107,10 +111,12 @@ func (s StaticReport) Report() report.Report {
},
NodeMetadatas: report.NodeMetadatas{
report.MakeAddressNodeID("hostA", "192.168.1.1"): report.NodeMetadata{
"name": "host-a",
"name": "host-a",
report.HostNodeID: report.MakeHostNodeID("hostA"),
},
report.MakeAddressNodeID("hostB", "192.168.1.2"): report.NodeMetadata{
"name": "host-b",
"name": "host-b",
report.HostNodeID: report.MakeHostNodeID("hostB"),
},
},
},

View File

@@ -66,7 +66,13 @@ func main() {
}
defer publisher.Close()
taggers := []tag.Tagger{tag.NewTopologyTagger()}
var (
hostName = hostname()
hostID = hostName // TODO: we should sanitize the hostname
)
taggers := []tag.Tagger{tag.NewTopologyTagger(), tag.NewOriginHostTagger(hostID)}
var dockerTagger *tag.DockerTagger
if *dockerEnabled && runtime.GOOS == linux {
var err error
@@ -84,11 +90,9 @@ func main() {
defer close(quit)
go func() {
var (
hostName = hostname()
hostID = hostName // TODO: we should sanitize the hostname
pubTick = time.Tick(*publishInterval)
spyTick = time.Tick(*spyInterval)
r = report.MakeReport()
pubTick = time.Tick(*publishInterval)
spyTick = time.Tick(*spyInterval)
r = report.MakeReport()
)
for {

View File

@@ -0,0 +1,23 @@
package tag
import (
"github.com/weaveworks/scope/report"
)
type originHostTagger struct{ hostNodeID string }
// NewOriginHostTagger tags each node with a foreign key linking it to its
// origin host in the host topology.
func NewOriginHostTagger(hostID string) Tagger {
return &originHostTagger{hostNodeID: report.MakeHostNodeID(hostID)}
}
func (t originHostTagger) Tag(r report.Report) report.Report {
for _, topology := range r.Topologies() {
md := report.NodeMetadata{report.HostNodeID: t.hostNodeID}
for nodeID := range topology.NodeMetadatas {
topology.NodeMetadatas[nodeID].Merge(md)
}
}
return r
}

View File

@@ -0,0 +1,25 @@
package tag_test
import (
"reflect"
"testing"
"github.com/weaveworks/scope/probe/tag"
"github.com/weaveworks/scope/report"
)
func TestOriginHostTagger(t *testing.T) {
var (
hostID = "foo"
endpointNodeID = report.MakeEndpointNodeID(hostID, "1.2.3.4", "56789") // hostID ignored
nodeMetadata = report.NodeMetadata{"foo": "bar"}
)
r := report.MakeReport()
r.Endpoint.NodeMetadatas[endpointNodeID] = nodeMetadata
want := nodeMetadata.Merge(report.NodeMetadata{report.HostNodeID: report.MakeHostNodeID(hostID)})
have := tag.NewOriginHostTagger(hostID).Tag(r).Endpoint.NodeMetadatas[endpointNodeID].Copy()
if !reflect.DeepEqual(want, have) {
t.Errorf("\nwant %+v\nhave %+v", want, have)
}
}

View File

@@ -13,8 +13,11 @@ func NewTopologyTagger() Tagger {
func (topologyTagger) Tag(r report.Report) report.Report {
for val, topology := range map[string]*report.Topology{
"endpoint": &(r.Endpoint),
"address": &(r.Address),
"endpoint": &(r.Endpoint),
"address": &(r.Address),
"process": &(r.Process),
"container": &(r.Container),
"host": &(r.Host),
} {
md := report.NodeMetadata{"topology": val}
for nodeID := range topology.NodeMetadatas {

View File

@@ -27,7 +27,7 @@ func MakeAdjacencyID(srcNodeID string) string {
return ">" + srcNodeID
}
// ParseAdjacencyID produces a node id from an adjancency id
// ParseAdjacencyID produces a node ID from an adjancency ID.
func ParseAdjacencyID(adjacencyID string) (string, bool) {
if !strings.HasPrefix(adjacencyID, ">") {
return "", false
@@ -56,6 +56,10 @@ func MakeEndpointNodeID(hostID, address, port string) string {
// MakeAddressNodeID produces an address node ID from its composite parts.
func MakeAddressNodeID(hostID, address string) string {
if !isLoopback(address) {
// Only loopback addresses get scoped by hostID.
hostID = ""
}
return hostID + ScopeDelim + address
}
@@ -77,8 +81,9 @@ func MakeContainerNodeID(hostID, containerID string) string {
return hostID + ScopeDelim + containerID
}
// ParseNodeID produces the scope and remainder from a node ID
func ParseNodeID(nodeID string) (string, string, bool) {
// ParseNodeID produces the host ID and remainder (typically an address) from
// a node ID. Note that hostID may be blank.
func ParseNodeID(nodeID string) (hostID string, remainder string, ok bool) {
fields := strings.SplitN(nodeID, ScopeDelim, 2)
if len(fields) != 2 {
return "", "", false
@@ -120,3 +125,8 @@ func AddressIDAddresser(id string) net.IP {
func PanicIDAddresser(id string) net.IP {
panic(fmt.Sprintf("PanicIDAddresser called on %q", id))
}
func isLoopback(address string) bool {
ip := net.ParseIP(address)
return ip != nil && ip.IsLoopback()
}

View File

@@ -33,6 +33,13 @@ type Report struct {
Host Topology
}
const (
// HostNodeID is a metadata foreign key, linking a node in any topology to
// a node in the host topology. That host node is the origin host, where
// the node was originally detected.
HostNodeID = "host_node_id"
)
// RenderableNode is the data type that's yielded to the JavaScript layer as
// an element of a topology. It should contain information that's relevant
// to rendering a node when there are many nodes visible at once.

View File

@@ -88,16 +88,19 @@ func (t Topology) RenderBy(mapFunc MapFunc, pseudoFunc PseudoFunc) RenderableNod
// Build a set of RenderableNodes for all non-pseudo probes, and an
// addressID to nodeID lookup map. Multiple addressIDs can map to the same
// RenderableNodes.
address2mapped := map[string]string{}
var (
source2mapped = map[string]string{} // source node ID -> mapped node ID
source2host = map[string]string{} // source node ID -> origin host ID
)
for nodeID, metadata := range t.NodeMetadatas {
mapped, ok := mapFunc(nodeID, metadata)
if !ok {
continue
}
// mapped.ID needs not be unique over all addressIDs. If not, we just overwrite
// the existing data, on the assumption that the MapFunc returns the same
// data.
// mapped.ID needs not be unique over all addressIDs. If not, we just
// overwrite the existing data, on the assumption that the MapFunc
// returns the same data.
nodes[mapped.ID] = RenderableNode{
ID: mapped.ID,
LabelMajor: mapped.Major,
@@ -107,24 +110,26 @@ func (t Topology) RenderBy(mapFunc MapFunc, pseudoFunc PseudoFunc) RenderableNod
Origins: IDList{nodeID},
Metadata: AggregateMetadata{}, // later
}
address2mapped[nodeID] = mapped.ID
source2mapped[nodeID] = mapped.ID
source2host[nodeID] = metadata[HostNodeID]
}
// Walk the graph and make connections.
for src, dsts := range t.Adjacency {
var (
srcNodeID, ok1 = ParseAdjacencyID(src)
srcOriginHostID, _, ok2 = ParseNodeID(srcNodeID)
srcRenderableID = address2mapped[srcNodeID] // must exist
srcRenderableNode = nodes[srcRenderableID] // must exist
srcNodeID, ok = ParseAdjacencyID(src)
//srcOriginHostID, _, ok2 = ParseNodeID(srcNodeID)
srcHostNodeID = source2host[srcNodeID]
srcRenderableID = source2mapped[srcNodeID] // must exist
srcRenderableNode = nodes[srcRenderableID] // must exist
)
if !ok1 || !ok2 {
if !ok {
log.Printf("bad adjacency ID %q", src)
continue
}
for _, dstNodeID := range dsts {
dstRenderableID, ok := address2mapped[dstNodeID]
dstRenderableID, ok := source2mapped[dstNodeID]
if !ok {
pseudoNode, ok := pseudoFunc(srcNodeID, srcRenderableNode, dstNodeID)
if !ok {
@@ -138,11 +143,11 @@ func (t Topology) RenderBy(mapFunc MapFunc, pseudoFunc PseudoFunc) RenderableNod
Pseudo: true,
Metadata: AggregateMetadata{}, // populated below - or not?
}
address2mapped[dstNodeID] = dstRenderableID
source2mapped[dstNodeID] = dstRenderableID
}
srcRenderableNode.Adjacency = srcRenderableNode.Adjacency.Add(dstRenderableID)
srcRenderableNode.Origins = srcRenderableNode.Origins.Add(MakeHostNodeID(srcOriginHostID))
srcRenderableNode.Origins = srcRenderableNode.Origins.Add(srcHostNodeID)
srcRenderableNode.Origins = srcRenderableNode.Origins.Add(srcNodeID)
edgeID := MakeEdgeID(srcNodeID, dstNodeID)
if md, ok := t.EdgeMetadatas[edgeID]; ok {

View File

@@ -19,6 +19,10 @@ var (
randomHostID = "random.hostname.com"
unknownHostID = ""
clientHostNodeID = MakeHostNodeID(clientHostID)
serverHostNodeID = MakeHostNodeID(serverHostID)
randomHostNodeID = MakeHostNodeID(randomHostID)
client54001 = MakeEndpointNodeID(clientHostID, "10.10.10.20", "54001") // curl (1)
client54002 = MakeEndpointNodeID(clientHostID, "10.10.10.20", "54002") // curl (2)
unknownClient1 = MakeEndpointNodeID(serverHostID, "10.10.10.10", "54010") // we want to ensure two unknown clients, connnected
@@ -45,19 +49,22 @@ var (
// care to test into the fixture. Just be sure to include the bits
// that the mapping funcs extract :)
client54001: NodeMetadata{
"name": "curl",
"domain": "client-54001-domain",
"pid": "10001",
"name": "curl",
"domain": "client-54001-domain",
"pid": "10001",
HostNodeID: clientHostNodeID,
},
client54002: NodeMetadata{
"name": "curl", // should be same as above!
"domain": "client-54002-domain", // may be different than above
"pid": "10001", // should be same as above!
"name": "curl", // should be same as above!
"domain": "client-54002-domain", // may be different than above
"pid": "10001", // should be same as above!
HostNodeID: clientHostNodeID,
},
server80: NodeMetadata{
"name": "apache",
"domain": "server-80-domain",
"pid": "215",
"name": "apache",
"domain": "server-80-domain",
"pid": "215",
HostNodeID: serverHostNodeID,
},
},
EdgeMetadatas: EdgeMetadatas{
@@ -107,13 +114,16 @@ var (
},
NodeMetadatas: NodeMetadatas{
clientIP: NodeMetadata{
"name": "client.hostname.com", // hostname
"name": "client.hostname.com", // hostname
HostNodeID: clientHostNodeID,
},
randomIP: NodeMetadata{
"name": "random.hostname.com", // hostname
"name": "random.hostname.com", // hostname
HostNodeID: randomHostNodeID,
},
serverIP: NodeMetadata{
"name": "server.hostname.com", // hostname
"name": "server.hostname.com", // hostname
HostNodeID: serverHostNodeID,
},
},
EdgeMetadatas: EdgeMetadatas{