Add host context in the details pane

In situations when the details can refer to entities from multiple hosts
(e.g. container image) make explicit to what host we are referring to.
This commit is contained in:
Alfonso Acosta
2015-08-21 15:19:26 +00:00
parent a8eded41ea
commit 10199737e4
4 changed files with 65 additions and 23 deletions

View File

@@ -68,7 +68,7 @@ func handleNode(rep xfer.Reporter, t topologyView, w http.ResponseWriter, r *htt
http.NotFound(w, r)
return
}
respondWith(w, http.StatusOK, APINode{Node: render.MakeDetailedNode(rpt, node)})
respondWith(w, http.StatusOK, APINode{Node: render.MakeDetailedNode(rpt, node, t.isMultiHost)})
}
// Individual edges.

View File

@@ -107,9 +107,10 @@ var topologyRegistry = map[string]topologyView{
renderer: render.FilterUnconnected{Renderer: render.ProcessWithContainerNameRenderer{}},
},
"applications-by-name": {
human: "by name",
parent: "applications",
renderer: render.FilterUnconnected{Renderer: render.ProcessNameRenderer},
human: "by name",
parent: "applications",
renderer: render.FilterUnconnected{Renderer: render.ProcessNameRenderer},
isMultiHost: true,
},
"containers": {
human: "Containers",
@@ -117,9 +118,10 @@ var topologyRegistry = map[string]topologyView{
renderer: render.ContainerRenderer,
},
"containers-by-image": {
human: "by image",
parent: "containers",
renderer: render.ContainerImageRenderer,
human: "by image",
parent: "containers",
renderer: render.ContainerImageRenderer,
isMultiHost: true,
},
"hosts": {
human: "Hosts",
@@ -129,7 +131,8 @@ var topologyRegistry = map[string]topologyView{
}
type topologyView struct {
human string
parent string
renderer render.Renderer
human string
parent string
renderer render.Renderer
isMultiHost bool // does the view involve information from multiple hosts?
}

View File

@@ -72,7 +72,7 @@ func (t tables) Less(i, j int) bool { return t[i].Rank > t[j].Rank }
// MakeDetailedNode transforms a renderable node to a detailed node. It uses
// aggregate metadata, plus the set of origin node IDs, to produce tables.
func MakeDetailedNode(r report.Report, n RenderableNode) DetailedNode {
func MakeDetailedNode(r report.Report, n RenderableNode, addHostTags bool) DetailedNode {
tables := tables{}
// RenderableNode may be the result of merge operation(s), and so may have
// multiple origins. The ultimate goal here is to generate tables to view
@@ -80,7 +80,7 @@ func MakeDetailedNode(r report.Report, n RenderableNode) DetailedNode {
// add them later.
connections := []Row{}
for _, id := range n.Origins {
if table, ok := OriginTable(r, id); ok {
if table, ok := OriginTable(r, id, addHostTags); ok {
tables = append(tables, table)
} else if _, ok := r.Endpoint.NodeMetadatas[id]; ok {
connections = append(connections, connectionDetailsRows(r.Endpoint, id)...)
@@ -155,12 +155,12 @@ func addConnectionsTable(tables *tables, connections []Row, r report.Report, n R
// OriginTable produces a table (to be consumed directly by the UI) based on
// an origin ID, which is (optimistically) a node ID in one of our topologies.
func OriginTable(r report.Report, originID string) (Table, bool) {
func OriginTable(r report.Report, originID string, addHostTags bool) (Table, bool) {
if nmd, ok := r.Process.NodeMetadatas[originID]; ok {
return processOriginTable(nmd)
return processOriginTable(nmd, addHostTags)
}
if nmd, ok := r.Container.NodeMetadatas[originID]; ok {
return containerOriginTable(nmd)
return containerOriginTable(nmd, addHostTags)
}
if nmd, ok := r.ContainerImage.NodeMetadatas[originID]; ok {
return containerImageOriginTable(nmd)
@@ -224,7 +224,7 @@ func connectionDetailsRows(topology report.Topology, originID string) []Row {
return rows
}
func processOriginTable(nmd report.NodeMetadata) (Table, bool) {
func processOriginTable(nmd report.NodeMetadata, addHostTag bool) (Table, bool) {
rows := []Row{}
for _, tuple := range []struct{ key, human string }{
{process.Comm, "Name"},
@@ -237,7 +237,9 @@ func processOriginTable(nmd report.NodeMetadata) (Table, bool) {
rows = append(rows, Row{Key: tuple.human, ValueMajor: val, ValueMinor: ""})
}
}
if addHostTag {
rows = append([]Row{{Key: "Host", ValueMajor: report.ExtractHostID(nmd)}}, rows...)
}
return Table{
Title: "Origin Process",
Numeric: false,
@@ -246,7 +248,7 @@ func processOriginTable(nmd report.NodeMetadata) (Table, bool) {
}, len(rows) > 0
}
func containerOriginTable(nmd report.NodeMetadata) (Table, bool) {
func containerOriginTable(nmd report.NodeMetadata, addHostTag bool) (Table, bool) {
rows := []Row{}
for _, tuple := range []struct{ key, human string }{
{docker.ContainerID, "ID"},
@@ -268,7 +270,9 @@ func containerOriginTable(nmd report.NodeMetadata) (Table, bool) {
rows = append(rows, Row{Key: "Memory Usage (MB):", ValueMajor: memoryStr, ValueMinor: ""})
}
}
if addHostTag {
rows = append([]Row{{Key: "Host", ValueMajor: report.ExtractHostID(nmd)}}, rows...)
}
return Table{
Title: "Origin Container",
Numeric: false,

View File

@@ -10,7 +10,7 @@ import (
)
func TestOriginTable(t *testing.T) {
if _, ok := render.OriginTable(test.Report, "not-found"); ok {
if _, ok := render.OriginTable(test.Report, "not-found", false); ok {
t.Errorf("unknown origin ID gave unexpected success")
}
for originID, want := range map[string]render.Table{
@@ -34,7 +34,7 @@ func TestOriginTable(t *testing.T) {
},
},
} {
have, ok := render.OriginTable(test.Report, originID)
have, ok := render.OriginTable(test.Report, originID, false)
if !ok {
t.Errorf("%q: not OK", originID)
continue
@@ -43,11 +43,46 @@ func TestOriginTable(t *testing.T) {
t.Errorf("%q: %s", originID, test.Diff(want, have))
}
}
// Test host tags
for originID, want := range map[string]render.Table{
test.ServerProcessNodeID: {
Title: "Origin Process",
Numeric: false,
Rank: 2,
Rows: []render.Row{
{"Host", test.ServerHostID, "", false},
{"Name", "apache", "", false},
{"PID", test.ServerPID, "", false},
},
},
test.ServerContainerNodeID: {
Title: "Origin Container",
Numeric: false,
Rank: 3,
Rows: []render.Row{
{"Host", test.ServerHostID, "", false},
{"ID", test.ServerContainerID, "", false},
{"Name", "server", "", false},
{"Image ID", test.ServerContainerImageID, "", false},
},
},
} {
have, ok := render.OriginTable(test.Report, originID, true)
if !ok {
t.Errorf("%q: not OK", originID)
continue
}
if !reflect.DeepEqual(want, have) {
t.Errorf("%q: %s", originID, test.Diff(want, have))
}
}
}
func TestMakeDetailedHostNode(t *testing.T) {
renderableNode := render.HostRenderer.Render(test.Report)[render.MakeHostID(test.ClientHostID)]
have := render.MakeDetailedNode(test.Report, renderableNode)
have := render.MakeDetailedNode(test.Report, renderableNode, false)
want := render.DetailedNode{
ID: render.MakeHostID(test.ClientHostID),
LabelMajor: "client",
@@ -109,7 +144,7 @@ func TestMakeDetailedHostNode(t *testing.T) {
func TestMakeDetailedContainerNode(t *testing.T) {
renderableNode := render.ContainerRenderer.Render(test.Report)[test.ServerContainerID]
have := render.MakeDetailedNode(test.Report, renderableNode)
have := render.MakeDetailedNode(test.Report, renderableNode, false)
want := render.DetailedNode{
ID: test.ServerContainerID,
LabelMajor: "server",