Skip to content

Commit

Permalink
Merge pull request #229 from weaveworks/fix-scoping
Browse files Browse the repository at this point in the history
Fix node scoping rules
  • Loading branch information
peterbourgon committed Jun 15, 2015
2 parents feb2cae + 20eb649 commit 5fa5a72
Show file tree
Hide file tree
Showing 10 changed files with 144 additions and 52 deletions.
3 changes: 1 addition & 2 deletions app/api_topology_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
34 changes: 20 additions & 14 deletions app/mock_reporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
},
},
},
Expand Down Expand Up @@ -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"),
},
},
},
Expand Down
16 changes: 10 additions & 6 deletions probe/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand Down
23 changes: 23 additions & 0 deletions probe/tag/origin_host_tagger.go
Original file line number Diff line number Diff line change
@@ -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
}
25 changes: 25 additions & 0 deletions probe/tag/origin_host_tagger_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
7 changes: 5 additions & 2 deletions probe/tag/topology_tagger.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
16 changes: 13 additions & 3 deletions report/id.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}

Expand All @@ -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
Expand Down Expand Up @@ -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()
}
7 changes: 7 additions & 0 deletions report/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
31 changes: 18 additions & 13 deletions report/topology.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 {
Expand All @@ -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 {
Expand Down
34 changes: 22 additions & 12 deletions report/topology_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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{
Expand Down Expand Up @@ -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{
Expand Down

0 comments on commit 5fa5a72

Please sign in to comment.