Add explicit group node summariser instead of doing it in the other summaires

This commit is contained in:
Paul Bellamy
2016-04-18 12:40:50 +01:00
parent 8f3402f3e6
commit 43a29db73c
5 changed files with 103 additions and 74 deletions

View File

@@ -29,6 +29,18 @@ const (
MarathonAppIDEnv = "MARATHON_APP_ID"
)
var (
shapesByTopology = map[string]string{
report.Host: Circle,
report.Process: Square,
report.Pod: Heptagon,
report.Service: Heptagon,
report.Container: Hexagon,
report.ContainerImage: Hexagon,
render.Pseudo: Circle,
}
)
// NodeSummaryGroup is a topology-typed group of children for a Node.
type NodeSummaryGroup struct {
ID string `json:"id"`
@@ -89,6 +101,9 @@ func MakeNodeSummary(r report.Report, n report.Node) (NodeSummary, bool) {
if renderer, ok := renderers[n.Topology]; ok {
return renderer(baseNodeSummary(r, n), n)
}
if strings.HasPrefix(n.Topology, "group:") {
return groupNodeSummary(baseNodeSummary(r, n), n)
}
return NodeSummary{}, false
}
@@ -127,9 +142,13 @@ func (n NodeSummary) Copy() NodeSummary {
}
func baseNodeSummary(r report.Report, n report.Node) NodeSummary {
shape, ok := shapesByTopology[n.Topology]
if !ok {
shape = Circle
}
return NodeSummary{
ID: n.ID,
Shape: Circle,
Shape: shape,
Linkable: true,
Metadata: NodeMetadata(r, n),
Metrics: NodeMetrics(r, n),
@@ -142,23 +161,23 @@ func pseudoNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) {
base.Pseudo = true
base.Rank = n.ID
if template, ok := map[string]struct{ Label, LabelMinor, Shape string }{
render.TheInternetID: {render.InboundMajor, "", Cloud},
render.IncomingInternetID: {render.InboundMajor, render.InboundMinor, Cloud},
render.OutgoingInternetID: {render.OutboundMajor, render.OutboundMinor, Cloud},
if template, ok := map[string]struct{ Label, LabelMinor string }{
render.TheInternetID: {render.InboundMajor, ""},
render.IncomingInternetID: {render.InboundMajor, render.InboundMinor},
render.OutgoingInternetID: {render.OutboundMajor, render.OutboundMinor},
}[n.ID]; ok {
base.Label = template.Label
base.LabelMinor = template.LabelMinor
base.Shape = template.Shape
base.Shape = Cloud
return base, true
}
// try rendering it as an uncontained node
if strings.HasPrefix(n.ID, render.MakePseudoNodeID(render.UncontainedID)) {
base.Label = render.UncontainedMajor
base.LabelMinor = report.ExtractHostID(n)
base.Shape = Square
base.Stack = true
base.LabelMinor = report.ExtractHostID(n)
return base, true
}
@@ -174,6 +193,7 @@ func pseudoNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) {
// try rendering it as an endpoint
if addr, ok := n.Latest.Lookup(endpoint.Addr); ok {
base.Label = addr
base.Shape = Circle
return base, true
}
@@ -183,25 +203,15 @@ func pseudoNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) {
func processNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) {
base.Label, _ = n.Latest.Lookup(process.Name)
base.Rank, _ = n.Latest.Lookup(process.Name)
base.Shape = Square
if p, ok := n.Counters.Lookup(report.Process); ok {
base.Stack = true
if p == 1 {
base.LabelMinor = fmt.Sprintf("%d process", p)
} else {
base.LabelMinor = fmt.Sprintf("%d processes", p)
}
pid, ok := n.Latest.Lookup(process.PID)
if !ok {
return NodeSummary{}, false
}
if containerName, ok := n.Latest.Lookup(docker.ContainerName); ok {
base.LabelMinor = fmt.Sprintf("%s (%s:%s)", report.ExtractHostID(n), containerName, pid)
} else {
pid, ok := n.Latest.Lookup(process.PID)
if !ok {
return NodeSummary{}, false
}
if containerName, ok := n.Latest.Lookup(docker.ContainerName); ok {
base.LabelMinor = fmt.Sprintf("%s (%s:%s)", report.ExtractHostID(n), containerName, pid)
} else {
base.LabelMinor = fmt.Sprintf("%s (%s)", report.ExtractHostID(n), pid)
}
base.LabelMinor = fmt.Sprintf("%s (%s)", report.ExtractHostID(n), pid)
}
_, isConnected := n.Latest.Lookup(render.IsConnected)
@@ -211,23 +221,12 @@ func processNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) {
func containerNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) {
base.Label = getRenderableContainerName(n)
if c, ok := n.Counters.Lookup(report.Container); ok {
base.Stack = true
if c == 1 {
base.LabelMinor = fmt.Sprintf("%d container", c)
} else {
base.LabelMinor = fmt.Sprintf("%d containers", c)
}
} else {
base.LabelMinor = report.ExtractHostID(n)
}
base.LabelMinor = report.ExtractHostID(n)
if imageName, ok := n.Latest.Lookup(docker.ImageName); ok {
base.Rank = render.ImageNameWithoutVersion(imageName)
}
base.Shape = Hexagon
return base, true
}
@@ -240,7 +239,6 @@ func containerImageNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bo
imageNameWithoutVersion := render.ImageNameWithoutVersion(imageName)
base.Label = imageNameWithoutVersion
base.Rank = imageNameWithoutVersion
base.Shape = Hexagon
base.Stack = true
if base.Label == ImageNameNone {
@@ -250,13 +248,7 @@ func containerImageNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bo
}
}
if i, ok := n.Counters.Lookup(report.ContainerImage); ok {
if i == 1 {
base.LabelMinor = fmt.Sprintf("%d image", i)
} else {
base.LabelMinor = fmt.Sprintf("%d images", i)
}
} else if c, ok := n.Counters.Lookup(report.Container); ok {
if c, ok := n.Counters.Lookup(report.Container); ok {
if c == 1 {
base.LabelMinor = fmt.Sprintf("%d container", c)
} else {
@@ -269,16 +261,8 @@ func containerImageNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bo
func podNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) {
base.Label, _ = n.Latest.Lookup(kubernetes.PodName)
base.Rank, _ = n.Latest.Lookup(kubernetes.PodID)
base.Shape = Heptagon
if p, ok := n.Counters.Lookup(report.Pod); ok {
base.Stack = true
if p == 1 {
base.LabelMinor = fmt.Sprintf("%d pod", p)
} else {
base.LabelMinor = fmt.Sprintf("%d pods", p)
}
} else if c, ok := n.Counters.Lookup(report.Container); ok {
if c, ok := n.Counters.Lookup(report.Container); ok {
if c == 1 {
base.LabelMinor = fmt.Sprintf("%d container", c)
} else {
@@ -292,7 +276,6 @@ func podNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) {
func serviceNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) {
base.Label, _ = n.Latest.Lookup(kubernetes.ServiceName)
base.Rank, _ = n.Latest.Lookup(kubernetes.ServiceID)
base.Shape = Heptagon
base.Stack = true
// Services are always just a group of pods, so there's no counting multiple
@@ -320,16 +303,38 @@ func hostNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) {
base.Label = hostname
}
if h, ok := n.Counters.Lookup(report.Host); ok {
base.Stack = true
if h == 1 {
base.LabelMinor = fmt.Sprintf("%d host", h)
} else {
base.LabelMinor = fmt.Sprintf("%d hosts", h)
return base, true
}
// groupNodeSummary renders the summary for a group node. n.Topology is
// expected to be of the form: group:container:hostname
func groupNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) {
parts := strings.Split(n.Topology, ":")
if len(parts) != 3 {
return NodeSummary{}, false
}
label, ok := n.Latest.Lookup(parts[2])
if !ok {
return NodeSummary{}, false
}
base.Label, base.Rank = label, label
if count, ok := n.Counters.Lookup(parts[1]); ok {
base.LabelMinor = fmt.Sprintf("%d %s", count, parts[1])
if count != 1 {
if strings.HasSuffix(parts[1], "s") {
base.LabelMinor += "es"
} else {
base.LabelMinor += "s"
}
}
}
base.Shape = Circle
if base.Shape, ok = shapesByTopology[parts[1]]; !ok {
base.Shape = Circle
}
base.Stack = true
return base, true
}

View File

@@ -168,6 +168,20 @@ func TestMakeNodeSummary(t *testing.T) {
Adjacency: report.MakeIDList(fixture.ServerHostNodeID),
},
},
{
name: "group node rendering",
input: expected.RenderedProcessNames[fixture.ServerName],
ok: true,
want: detailed.NodeSummary{
ID: "apache",
Label: "apache",
LabelMinor: "1 process",
Rank: "apache",
Shape: "square",
Stack: true,
Linkable: true,
},
},
}
for _, testcase := range testcases {
have, ok := detailed.MakeNodeSummary(fixture.Report, testcase.input)

View File

@@ -27,14 +27,15 @@ var (
return n
}
}
pseudo = node(render.Pseudo)
endpoint = node(report.Endpoint)
processNode = node(report.Process)
container = node(report.Container)
containerImage = node(report.ContainerImage)
pod = node(report.Pod)
service = node(report.Service)
hostNode = node(report.Host)
pseudo = node(render.Pseudo)
endpoint = node(report.Endpoint)
processNode = node(report.Process)
processNameNode = node(render.MakeGroupNodeTopology(report.Process, process.Name))
container = node(report.Container)
containerImage = node(report.ContainerImage)
pod = node(report.Pod)
service = node(report.Service)
hostNode = node(report.Host)
UnknownPseudoNode1ID = render.MakePseudoNodeID(fixture.UnknownClient1IP)
UnknownPseudoNode2ID = render.MakePseudoNodeID(fixture.UnknownClient3IP)
@@ -111,7 +112,9 @@ var (
}
RenderedProcessNames = report.Nodes{
fixture.Client1Name: processNode(fixture.Client1Name, fixture.ServerName).
fixture.Client1Name: processNameNode(fixture.Client1Name, fixture.ServerName).
WithLatests(map[string]string{process.Name: fixture.Client1Name}).
WithCounters(map[string]int{report.Process: 2}).
WithChildren(report.MakeNodeSet(
RenderedEndpoints[fixture.Client54001NodeID],
RenderedEndpoints[fixture.Client54002NodeID],
@@ -119,13 +122,15 @@ var (
RenderedProcesses[fixture.ClientProcess2NodeID],
)),
fixture.ServerName: processNode(fixture.ServerName).
fixture.ServerName: processNameNode(fixture.ServerName).
WithLatests(map[string]string{process.Name: fixture.ServerName}).
WithCounters(map[string]int{report.Process: 1}).
WithChildren(report.MakeNodeSet(
RenderedEndpoints[fixture.Server80NodeID],
RenderedProcesses[fixture.ServerProcessNodeID],
)),
fixture.NonContainerName: processNode(fixture.NonContainerName, render.OutgoingInternetID).
fixture.NonContainerName: processNameNode(fixture.NonContainerName, render.OutgoingInternetID).
WithChildren(report.MakeNodeSet(
RenderedEndpoints[fixture.NonContainerNodeID],
RenderedProcesses[fixture.NonContainerProcessNodeID],

View File

@@ -8,3 +8,8 @@ import (
func MakePseudoNodeID(parts ...string) string {
return strings.Join(append([]string{"pseudo"}, parts...), ":")
}
// MakeGroupNodeTopology joins the parts of a group topology into the topology of a group node
func MakeGroupNodeTopology(originalTopology, key string) string {
return strings.Join([]string{"group", originalTopology, key}, ":")
}

View File

@@ -265,7 +265,7 @@ func MapProcess2Name(n report.Node, _ report.Networks) report.Nodes {
return report.Nodes{}
}
node := NewDerivedNode(name, n).WithTopology(report.Process)
node := NewDerivedNode(name, n).WithTopology(MakeGroupNodeTopology(n.Topology, process.Name))
node.Latest = node.Latest.Set(process.Name, timestamp, name)
node.Counters = node.Counters.Add(n.Topology, 1)
return report.Nodes{name: node}
@@ -461,7 +461,7 @@ func MapContainer2Hostname(n report.Node, _ report.Networks) report.Nodes {
return report.Nodes{}
}
node := NewDerivedNode(id, n)
node := NewDerivedNode(id, n).WithTopology(MakeGroupNodeTopology(n.Topology, docker.ContainerHostname))
node.Latest = node.Latest.
Set(docker.ContainerHostname, timestamp, id).
Delete(docker.ContainerName) // TODO(paulbellamy): total hack to render these by hostname instead.