123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- 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
- }
|