diff --git a/render/container.go b/render/container.go index c4bab4fc5..3a2705b32 100644 --- a/render/container.go +++ b/render/container.go @@ -74,8 +74,8 @@ func ShortLivedConnectionJoin(r Renderer, toIPs func(report.Node) []string) Rend return report.Nodes{} } - // Propagate the internet pseudo node - if strings.HasSuffix(n.ID, TheInternetID) { + // Propagate the internet and service pseudo nodes + if strings.HasSuffix(n.ID, TheInternetID) || strings.HasPrefix(n.ID, ServiceNodeIDPrefix) { return report.Nodes{n.ID: n} } @@ -112,7 +112,7 @@ func ShortLivedConnectionJoin(r Renderer, toIPs func(report.Node) []string) Rend return report.Nodes{} } if ip := net.ParseIP(addr); ip != nil && !local.Contains(ip) { - node := theInternetNode(m) + node := toInternetNode(m) return report.Nodes{node.ID: node} } diff --git a/render/detailed/summary.go b/render/detailed/summary.go index ae95a3954..3f68e2fad 100644 --- a/render/detailed/summary.go +++ b/render/detailed/summary.go @@ -115,6 +115,7 @@ func pseudoNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) { base.Pseudo = true base.Rank = n.ID + // try rendering as an internet node if template, ok := templates[n.ID]; ok { base.Label = template.Label base.LabelMinor = template.LabelMinor @@ -122,6 +123,16 @@ func pseudoNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) { return base, true } + // try rendering as a known service node + if strings.HasPrefix(n.ID, render.ServiceNodeIDPrefix) { + serviceID := n.ID[len(render.ServiceNodeIDPrefix):] + base.Label = render.KnownServicesForHumans[serviceID] + base.LabelMinor = "" + // TODO: use custom icons for known services? + base.Shape = report.Cloud + return base, true + } + // try rendering it as an uncontained node if strings.HasPrefix(n.ID, render.MakePseudoNodeID(render.UncontainedID)) { base.Label = render.UncontainedMajor diff --git a/render/filters.go b/render/filters.go index 82e2fc2a1..347ca36f3 100644 --- a/render/filters.go +++ b/render/filters.go @@ -245,9 +245,9 @@ func IsApplication(n report.Node) bool { var IsSystem = Complement(IsApplication) // IsNotPseudo returns true if the node is not a pseudo node -// or the internet nodes. +// or internet/service nodes. func IsNotPseudo(n report.Node) bool { - return n.Topology != Pseudo || strings.HasSuffix(n.ID, TheInternetID) + return n.Topology != Pseudo || strings.HasSuffix(n.ID, TheInternetID) || strings.HasPrefix(n.ID, ServiceNodeIDPrefix) } // IsNamespace checks if the node is a pod/service in the specified namespace diff --git a/render/process.go b/render/process.go index c756fa2c3..0e81261f1 100644 --- a/render/process.go +++ b/render/process.go @@ -96,8 +96,8 @@ func MapEndpoint2Pseudo(n report.Node, local report.Networks) report.Nodes { if ip := net.ParseIP(addr); ip != nil && !local.Contains(ip) { // If the dstNodeAddr is not in a network local to this report, we emit an - // internet node - node = theInternetNode(n) + // external pseudoNode + node = toInternetNode(n) } else { // due to https://github.com/weaveworks/scope/issues/1323 we are dropping // all non-internet pseudo nodes for now. @@ -157,7 +157,16 @@ func MapProcess2Name(n report.Node, _ report.Networks) report.Nodes { return report.Nodes{name: node} } -func theInternetNode(m report.Node) report.Node { +func toInternetNode(m report.Node) report.Node { + // First, check if it's a known service and emit a + // a specific node if it is + hostnames, _ := m.Sets.Lookup(endpoint.ReverseDNSNames) + for _, hostname := range hostnames { + if serviceID, ok := lookupKnownService(hostname); ok { + return NewDerivedPseudoNode(ServiceNodeIDPrefix+serviceID, m) + } + } + // emit one internet node for incoming, one for outgoing if len(m.Adjacency) > 0 { return NewDerivedPseudoNode(IncomingInternetID, m) diff --git a/render/theinternet.go b/render/theinternet.go index 6a3c13739..cb24dcc55 100644 --- a/render/theinternet.go +++ b/render/theinternet.go @@ -2,11 +2,38 @@ package render import ( "net" + "regexp" "github.com/weaveworks/scope/probe/host" "github.com/weaveworks/scope/report" ) +var ( + // ServiceNodeIDPrefix is how the ID all service pseudo nodes begin + ServiceNodeIDPrefix = "service-" + + // KnownServicesForHumans contains a human-readable format of the service Ids + KnownServicesForHumans = map[string]string{ + "aws-dynamo": "AWS Dynamo", + "aws-s3": "AWS S3", + } + + // Correspondence between hostnames and the service id they are part of + knownServicesMatchers = map[*regexp.Regexp]string{ + regexp.MustCompile(`dynamodb.[^.]+.amazonaws.com`): "aws-dynamo", + regexp.MustCompile(`s3-[^.]+.amazonaws.com`): "aws-s3", + } +) + +func lookupKnownService(hostname string) (string, bool) { + for re, id := range knownServicesMatchers { + if re.MatchString(hostname) { + return id, true + } + } + return "", false +} + // LocalNetworks returns a superset of the networks (think: CIDRs) that are // "local" from the perspective of each host represented in the report. It's // used to determine which nodes in the report are "remote", i.e. outside of