mirror of
https://github.com/weaveworks/scope.git
synced 2026-02-14 10:00:13 +00:00
281 lines
7.4 KiB
Go
281 lines
7.4 KiB
Go
package detailed
|
|
|
|
import (
|
|
"sort"
|
|
|
|
"github.com/ugorji/go/codec"
|
|
|
|
"github.com/weaveworks/scope/probe/awsecs"
|
|
"github.com/weaveworks/scope/probe/docker"
|
|
"github.com/weaveworks/scope/probe/kubernetes"
|
|
"github.com/weaveworks/scope/probe/process"
|
|
"github.com/weaveworks/scope/report"
|
|
)
|
|
|
|
// Node is the data type that's yielded to the JavaScript layer when
|
|
// we want deep information about an individual node.
|
|
type Node struct {
|
|
NodeSummary
|
|
Controls []ControlInstance `json:"controls"`
|
|
Children []NodeSummaryGroup `json:"children,omitempty"`
|
|
Connections []ConnectionsSummary `json:"connections,omitempty"`
|
|
}
|
|
|
|
// ControlInstance contains a control description, and all the info
|
|
// needed to execute it.
|
|
type ControlInstance struct {
|
|
ProbeID string
|
|
NodeID string
|
|
Control report.Control
|
|
}
|
|
|
|
// MarshalJSON shouldn't be used, use CodecEncodeSelf instead
|
|
func (ControlInstance) MarshalJSON() ([]byte, error) {
|
|
panic("MarshalJSON shouldn't be used, use CodecEncodeSelf instead")
|
|
}
|
|
|
|
// UnmarshalJSON shouldn't be used, use CodecDecodeSelf instead
|
|
func (*ControlInstance) UnmarshalJSON(b []byte) error {
|
|
panic("UnmarshalJSON shouldn't be used, use CodecDecodeSelf instead")
|
|
}
|
|
|
|
type wiredControlInstance struct {
|
|
ProbeID string `json:"probeId"`
|
|
NodeID string `json:"nodeId"`
|
|
ID string `json:"id"`
|
|
Human string `json:"human"`
|
|
Icon string `json:"icon"`
|
|
Confirmation string `json:"confirmation,omitempty"`
|
|
Rank int `json:"rank"`
|
|
}
|
|
|
|
// CodecEncodeSelf marshals this ControlInstance. It takes the basic Metric
|
|
// rendering, then adds some row-specific fields.
|
|
func (c *ControlInstance) CodecEncodeSelf(encoder *codec.Encoder) {
|
|
encoder.Encode(wiredControlInstance{
|
|
ProbeID: c.ProbeID,
|
|
NodeID: c.NodeID,
|
|
ID: c.Control.ID,
|
|
Human: c.Control.Human,
|
|
Icon: c.Control.Icon,
|
|
Confirmation: c.Control.Confirmation,
|
|
Rank: c.Control.Rank,
|
|
})
|
|
}
|
|
|
|
// CodecDecodeSelf implements codec.Selfer
|
|
func (c *ControlInstance) CodecDecodeSelf(decoder *codec.Decoder) {
|
|
var in wiredControlInstance
|
|
decoder.Decode(&in)
|
|
*c = ControlInstance{
|
|
ProbeID: in.ProbeID,
|
|
NodeID: in.NodeID,
|
|
Control: report.Control{
|
|
ID: in.ID,
|
|
Human: in.Human,
|
|
Icon: in.Icon,
|
|
Confirmation: in.Confirmation,
|
|
Rank: in.Rank,
|
|
},
|
|
}
|
|
}
|
|
|
|
// RenderContext carries contextual data that is needed when rendering parts of the report.
|
|
type RenderContext struct {
|
|
report.Report
|
|
MetricsGraphURL string
|
|
}
|
|
|
|
// MakeNode transforms a renderable node to a detailed node. It uses
|
|
// aggregate metadata, plus the set of origin node IDs, to produce tables.
|
|
func MakeNode(topologyID string, rc RenderContext, ns report.Nodes, n report.Node) Node {
|
|
summary, _ := MakeNodeSummary(rc, n)
|
|
return Node{
|
|
NodeSummary: summary,
|
|
Controls: controls(rc.Report, n),
|
|
Children: children(rc, n),
|
|
Connections: []ConnectionsSummary{
|
|
incomingConnectionsSummary(topologyID, rc.Report, n, ns),
|
|
outgoingConnectionsSummary(topologyID, rc.Report, n, ns),
|
|
},
|
|
}
|
|
}
|
|
|
|
func controlsFor(topology report.Topology, nodeID string) []ControlInstance {
|
|
result := []ControlInstance{}
|
|
node, ok := topology.Nodes[nodeID]
|
|
if !ok {
|
|
return result
|
|
}
|
|
probeID, ok := node.Latest.Lookup(report.ControlProbeID)
|
|
if !ok {
|
|
return result
|
|
}
|
|
for _, controlID := range node.ActiveControls() {
|
|
if control, ok := topology.Controls[controlID]; ok {
|
|
if control.ProbeID != "" { // does this Control have an override for the node probe?
|
|
probeID = control.ProbeID
|
|
}
|
|
result = append(result, ControlInstance{
|
|
ProbeID: probeID,
|
|
NodeID: nodeID,
|
|
Control: control,
|
|
})
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func controls(r report.Report, n report.Node) []ControlInstance {
|
|
if t, ok := r.Topology(n.Topology); ok {
|
|
return controlsFor(t, n.ID)
|
|
}
|
|
return []ControlInstance{}
|
|
}
|
|
|
|
// We only need to include topologies here where the nodes may appear
|
|
// as children of other nodes in some topology.
|
|
var nodeSummaryGroupSpecs = []struct {
|
|
topologyID string
|
|
NodeSummaryGroup
|
|
}{
|
|
{
|
|
topologyID: report.Pod,
|
|
NodeSummaryGroup: NodeSummaryGroup{
|
|
Label: "Pods",
|
|
Columns: []Column{
|
|
{ID: kubernetes.State, Label: "State"},
|
|
{ID: report.Container, Label: "# Containers", Datatype: report.Number},
|
|
{ID: kubernetes.IP, Label: "IP", Datatype: report.IP},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
topologyID: report.ECSTask,
|
|
NodeSummaryGroup: NodeSummaryGroup{
|
|
Label: "Tasks",
|
|
Columns: []Column{
|
|
{ID: awsecs.CreatedAt, Label: "Created At", Datatype: report.DateTime},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
topologyID: report.Container,
|
|
NodeSummaryGroup: NodeSummaryGroup{
|
|
Label: "Containers",
|
|
Columns: []Column{
|
|
{ID: docker.CPUTotalUsage, Label: "CPU", Datatype: report.Number},
|
|
{ID: docker.MemoryUsage, Label: "Memory", Datatype: report.Number},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
topologyID: report.Process,
|
|
NodeSummaryGroup: NodeSummaryGroup{
|
|
Label: "Processes",
|
|
Columns: []Column{
|
|
{ID: process.PID, Label: "PID", Datatype: report.Number},
|
|
{ID: process.CPUUsage, Label: "CPU", Datatype: report.Number},
|
|
{ID: process.MemoryUsage, Label: "Memory", Datatype: report.Number},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
topologyID: report.ContainerImage,
|
|
NodeSummaryGroup: NodeSummaryGroup{
|
|
Label: "Container images",
|
|
Columns: []Column{},
|
|
},
|
|
},
|
|
{
|
|
topologyID: report.PersistentVolume,
|
|
NodeSummaryGroup: NodeSummaryGroup{
|
|
Label: "Persistent Volumes",
|
|
Columns: []Column{},
|
|
},
|
|
},
|
|
{
|
|
topologyID: report.PersistentVolumeClaim,
|
|
NodeSummaryGroup: NodeSummaryGroup{
|
|
Label: "Persistent Volume Claims",
|
|
Columns: []Column{},
|
|
},
|
|
},
|
|
{
|
|
topologyID: report.StorageClass,
|
|
NodeSummaryGroup: NodeSummaryGroup{
|
|
Label: "Storage Classes",
|
|
Columns: []Column{},
|
|
},
|
|
},
|
|
{
|
|
topologyID: report.VolumeSnapshot,
|
|
NodeSummaryGroup: NodeSummaryGroup{
|
|
Label: "Volume Snapshots",
|
|
Columns: []Column{},
|
|
},
|
|
},
|
|
{
|
|
topologyID: report.VolumeSnapshotData,
|
|
NodeSummaryGroup: NodeSummaryGroup{
|
|
Label: "Volume Snapshot Data",
|
|
Columns: []Column{},
|
|
},
|
|
},
|
|
}
|
|
|
|
func children(rc RenderContext, n report.Node) []NodeSummaryGroup {
|
|
summaries := map[string][]NodeSummary{}
|
|
n.Children.ForEach(func(child report.Node) {
|
|
if child.ID == n.ID {
|
|
return
|
|
}
|
|
summary, ok := MakeNodeSummary(rc, child)
|
|
if !ok {
|
|
return
|
|
}
|
|
summaries[child.Topology] = append(summaries[child.Topology], summary.SummarizeMetrics())
|
|
})
|
|
|
|
nodeSummaryGroups := []NodeSummaryGroup{}
|
|
// Apply specific group specs in the order they're listed
|
|
for _, spec := range nodeSummaryGroupSpecs {
|
|
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 := rc.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
|
|
}
|