Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce the number of places topologies are explicitly listed #2436

Merged
merged 10 commits into from
Apr 14, 2017
28 changes: 4 additions & 24 deletions probe/appclient/app_client_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,30 +73,10 @@ func TestAppClientPublish(t *testing.T) {
// marshalling->unmarshaling is not idempotent due to `json:"omitempty"`
// tags, transforming empty slices into nils. So, we make DeepEqual
// happy by setting empty `json:"omitempty"` entries to nil
rpt.Endpoint = report.MakeTopology()
rpt.Process = report.MakeTopology()
rpt.Container = report.MakeTopology()
rpt.ContainerImage = report.MakeTopology()
rpt.Pod = report.MakeTopology()
rpt.Service = report.MakeTopology()
rpt.Deployment = report.MakeTopology()
rpt.ReplicaSet = report.MakeTopology()
rpt.Host = report.MakeTopology()
rpt.Overlay = report.MakeTopology()
rpt.ECSTask = report.MakeTopology()
rpt.ECSService = report.MakeTopology()
rpt.Endpoint.Controls = nil
rpt.Process.Controls = nil
rpt.Container.Controls = nil
rpt.ContainerImage.Controls = nil
rpt.Pod.Controls = nil
rpt.Service.Controls = nil
rpt.Deployment.Controls = nil
rpt.ReplicaSet.Controls = nil
rpt.Host.Controls = nil
rpt.Overlay.Controls = nil
rpt.ECSTask.Controls = nil
rpt.ECSService.Controls = nil
rpt.WalkTopologies(func(to *report.Topology) {
*to = report.MakeTopology()
to.Controls = nil
})

s := dummyServer(t, token, id, version, rpt, done)
defer s.Close()
Expand Down
2 changes: 2 additions & 0 deletions probe/awsecs/reporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,8 @@ func (r Reporter) Tag(rpt report.Report) (report.Report, error) {
if serviceName, ok := ecsInfo.TaskServiceMap[taskArn]; ok {
serviceID := report.MakeECSServiceNodeID(cluster, serviceName)
parentsSets = parentsSets.Add(report.ECSService, report.MakeStringSet(serviceID))
// in addition, make service parent of task
rpt.ECSTask.Nodes[taskID] = rpt.ECSTask.Nodes[taskID].WithParents(report.MakeSets().Add(report.ECSService, report.MakeStringSet(serviceID)))
}
for _, containerID := range info.ContainerIDs {
if containerNode, ok := rpt.Container.Nodes[containerID]; ok {
Expand Down
15 changes: 3 additions & 12 deletions probe/probe_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,18 +81,9 @@ func TestProbe(t *testing.T) {
// tags, transforming empty slices into nils. So, we make DeepEqual
// happy by setting empty `json:"omitempty"` entries to nil
node.Metrics = nil
want.Endpoint.Controls = nil
want.Process.Controls = nil
want.Container.Controls = nil
want.ContainerImage.Controls = nil
want.Pod.Controls = nil
want.Service.Controls = nil
want.Deployment.Controls = nil
want.ReplicaSet.Controls = nil
want.Host.Controls = nil
want.Overlay.Controls = nil
want.ECSTask.Controls = nil
want.ECSService.Controls = nil
want.WalkTopologies(func(t *report.Topology) {
t.Controls = nil
})
want.Endpoint.AddNode(node)

pub := mockPublisher{make(chan report.Report, 10)}
Expand Down
13 changes: 1 addition & 12 deletions probe/topology_tagger.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,7 @@ func (topologyTagger) Name() string { return "Topology" }

// Tag implements Tagger
func (topologyTagger) Tag(r report.Report) (report.Report, error) {
for name, t := range map[string]*report.Topology{
report.Endpoint: &(r.Endpoint),
report.Process: &(r.Process),
report.Container: &(r.Container),
report.ContainerImage: &(r.ContainerImage),
report.Pod: &(r.Pod),
report.Service: &(r.Service),
report.ECSTask: &(r.ECSTask),
report.ECSService: &(r.ECSService),
report.Host: &(r.Host),
report.Overlay: &(r.Overlay),
} {
for name, t := range r.TopologyMap() {
for _, node := range t.Nodes {
t.AddNode(node.WithTopology(name))
}
Expand Down
63 changes: 42 additions & 21 deletions render/detailed/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,7 @@ var (
{
topologyID: report.Host,
NodeSummaryGroup: NodeSummaryGroup{
TopologyID: "hosts",
Label: "Hosts",
Label: "Hosts",
Columns: []Column{
{ID: host.CPUUsage, Label: "CPU", Datatype: "number"},
{ID: host.MemoryUsage, Label: "Memory", Datatype: "number"},
Expand All @@ -145,8 +144,7 @@ var (
{
topologyID: report.Service,
NodeSummaryGroup: NodeSummaryGroup{
TopologyID: "services",
Label: "Services",
Label: "Services",
Columns: []Column{
{ID: report.Pod, Label: "# Pods", Datatype: "number"},
{ID: kubernetes.IP, Label: "IP", Datatype: "ip"},
Expand All @@ -156,8 +154,7 @@ var (
{
topologyID: report.ReplicaSet,
NodeSummaryGroup: NodeSummaryGroup{
TopologyID: "replica-sets",
Label: "Replica Sets",
Label: "Replica Sets",
Columns: []Column{
{ID: report.Pod, Label: "# Pods", Datatype: "number"},
{ID: kubernetes.ObservedGeneration, Label: "Observed Gen.", Datatype: "number"},
Expand All @@ -167,8 +164,7 @@ var (
{
topologyID: report.Pod,
NodeSummaryGroup: NodeSummaryGroup{
TopologyID: "pods",
Label: "Pods",
Label: "Pods",

Columns: []Column{
{ID: kubernetes.State, Label: "State"},
Expand All @@ -180,8 +176,7 @@ var (
{
topologyID: report.ECSService,
NodeSummaryGroup: NodeSummaryGroup{
TopologyID: "ecs-services",
Label: "Services",
Label: "Services",
Columns: []Column{
{ID: awsecs.ServiceRunningCount, Label: "Running", Datatype: "number"},
{ID: awsecs.ServiceDesiredCount, Label: "Desired", Datatype: "number"},
Expand All @@ -191,8 +186,7 @@ var (
{
topologyID: report.ECSTask,
NodeSummaryGroup: NodeSummaryGroup{
TopologyID: "ecs-tasks",
Label: "Tasks",
Label: "Tasks",
Columns: []Column{
{ID: awsecs.CreatedAt, Label: "Created At", Datatype: "datetime"},
},
Expand All @@ -201,8 +195,7 @@ var (
{
topologyID: report.Container,
NodeSummaryGroup: NodeSummaryGroup{
TopologyID: "containers",
Label: "Containers", Columns: []Column{
Label: "Containers", Columns: []Column{
{ID: docker.CPUTotalUsage, Label: "CPU", Datatype: "number"},
{ID: docker.MemoryUsage, Label: "Memory", Datatype: "number"},
},
Expand All @@ -211,8 +204,7 @@ var (
{
topologyID: report.Process,
NodeSummaryGroup: NodeSummaryGroup{
TopologyID: "processes",
Label: "Processes", Columns: []Column{
Label: "Processes", Columns: []Column{
{ID: process.PID, Label: "PID", Datatype: "number"},
{ID: process.CPUUsage, Label: "CPU", Datatype: "number"},
{ID: process.MemoryUsage, Label: "Memory", Datatype: "number"},
Expand Down Expand Up @@ -246,13 +238,42 @@ func children(r report.Report, n report.Node) []NodeSummaryGroup {
})

nodeSummaryGroups := []NodeSummaryGroup{}
// Apply specific group specs in the order they're listed
for _, spec := range nodeSummaryGroupSpecs {
if len(summaries[spec.topologyID]) > 0 {
sort.Sort(nodeSummariesByID(summaries[spec.TopologyID]))
group := spec.NodeSummaryGroup
group.Nodes = summaries[spec.topologyID]
nodeSummaryGroups = append(nodeSummaryGroups, group)
if len(summaries[spec.topologyID]) == 0 {
continue
}
apiTopology, ok := primaryAPITopology[spec.topologyID]
if !ok {
continue
}
sort.Sort(nodeSummariesByID(summaries[spec.topologyID]))
group := spec.NodeSummaryGroup
group.Nodes = summaries[spec.topologyID]
group.TopologyID = apiTopology
nodeSummaryGroups = append(nodeSummaryGroups, group)
delete(summaries, spec.topologyID)
}
// As a fallback, in case a topology has no group spec defined, add any remaining at the end
for topologyID, nodeSummaries := range summaries {
if len(nodeSummaries) == 0 {
continue
}
topology, ok := r.Topology(topologyID)
if !ok {
continue
}
apiTopology, ok := primaryAPITopology[topologyID]
if !ok {
continue
}
sort.Sort(nodeSummariesByID(nodeSummaries))
group := NodeSummaryGroup{
TopologyID: apiTopology,
Label: topology.LabelPlural,
Columns: []Column{},
}
nodeSummaryGroups = append(nodeSummaryGroups, group)
}

return nodeSummaryGroups
Expand Down
127 changes: 46 additions & 81 deletions render/detailed/parents.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,117 +16,82 @@ type Parent struct {
TopologyID string `json:"topologyId"`
}

func node(t report.Topology) func(string) (report.Node, bool) {
return func(id string) (report.Node, bool) {
n, ok := t.Nodes[id]
return n, ok
}
}
var (
kubernetesParentLabel = latestLookup(kubernetes.Name)

func fake(id string) (report.Node, bool) {
return report.MakeNode(id), true
}
getLabelForTopology = map[string]func(report.Node) string{
report.Container: getRenderableContainerName,
report.Pod: kubernetesParentLabel,
report.ReplicaSet: kubernetesParentLabel,
report.Deployment: kubernetesParentLabel,
report.Service: kubernetesParentLabel,
report.ECSTask: latestLookup(awsecs.TaskFamily),
report.ECSService: ecsServiceParentLabel,
report.ContainerImage: containerImageParentLabel,
report.Host: latestLookup(host.HostName),
}
)

// Parents renders the parents of this report.Node, which have been aggregated
// from the probe reports.
func Parents(r report.Report, n report.Node) (result []Parent) {
topologies := map[string]struct {
node func(id string) (report.Node, bool)
render func(report.Node) Parent
}{
report.Container: {node(r.Container), containerParent},
report.Pod: {node(r.Pod), podParent},
report.ReplicaSet: {node(r.ReplicaSet), replicaSetParent},
report.Deployment: {node(r.Deployment), deploymentParent},
report.Service: {node(r.Service), serviceParent},
report.ECSTask: {node(r.ECSTask), ecsTaskParent},
report.ECSService: {node(r.ECSService), ecsServiceParent},
report.ContainerImage: {fake, containerImageParent},
report.Host: {node(r.Host), hostParent},
}
topologyIDs := []string{}
for topologyID := range topologies {
for topologyID := range getLabelForTopology {
topologyIDs = append(topologyIDs, topologyID)
}
sort.Strings(topologyIDs)
for _, topologyID := range topologyIDs {
t := topologies[topologyID]
getLabel := getLabelForTopology[topologyID]
topology, ok := r.Topology(topologyID)
if !ok {
continue
}
parents, _ := n.Parents.Lookup(topologyID)
for _, id := range parents {
if topologyID == n.Topology && id == n.ID {
continue
}

parent, ok := t.node(id)
var parentNode report.Node
// Special case: container image parents should be empty nodes for some reason
if topologyID == report.ContainerImage {
parentNode = report.MakeNode(id)
} else {
if parent, ok := topology.Nodes[id]; ok {
parentNode = parent
} else {
continue
}
}

apiTopologyID, ok := primaryAPITopology[topologyID]
if !ok {
continue
}

result = append(result, t.render(parent))
result = append(result, Parent{
ID: id,
Label: getLabel(parentNode),
TopologyID: apiTopologyID,
})
}
}
return result
}

func containerParent(n report.Node) Parent {
label := getRenderableContainerName(n)
return Parent{
ID: n.ID,
Label: label,
TopologyID: "containers",
func latestLookup(key string) func(report.Node) string {
return func(n report.Node) string {
value, _ := n.Latest.Lookup(key)
return value
}
}

var (
podParent = kubernetesParent("pods")
replicaSetParent = kubernetesParent("replica-sets")
deploymentParent = kubernetesParent("deployments")
serviceParent = kubernetesParent("services")
)

func kubernetesParent(topology string) func(report.Node) Parent {
return func(n report.Node) Parent {
name, _ := n.Latest.Lookup(kubernetes.Name)
return Parent{
ID: n.ID,
Label: name,
TopologyID: topology,
}
}
}

func ecsTaskParent(n report.Node) Parent {
family, _ := n.Latest.Lookup(awsecs.TaskFamily)
return Parent{
ID: n.ID,
Label: family,
TopologyID: "ecs-tasks",
}
}

func ecsServiceParent(n report.Node) Parent {
func ecsServiceParentLabel(n report.Node) string {
_, name, _ := report.ParseECSServiceNodeID(n.ID)
return Parent{
ID: n.ID,
Label: name,
TopologyID: "ecs-services",
}
return name
}

func containerImageParent(n report.Node) Parent {
func containerImageParentLabel(n report.Node) string {
name, _ := report.ParseContainerImageNodeID(n.ID)
return Parent{
ID: n.ID,
Label: name,
TopologyID: "containers-by-image",
}
}

func hostParent(n report.Node) Parent {
hostName, _ := n.Latest.Lookup(host.HostName)
return Parent{
ID: n.ID,
Label: hostName,
TopologyID: "hosts",
}
return name
}
Loading