node.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. package detailed
  2. import (
  3. "sort"
  4. "github.com/ugorji/go/codec"
  5. "github.com/weaveworks/scope/probe/awsecs"
  6. "github.com/weaveworks/scope/probe/docker"
  7. "github.com/weaveworks/scope/probe/kubernetes"
  8. "github.com/weaveworks/scope/probe/process"
  9. "github.com/weaveworks/scope/report"
  10. )
  11. // Node is the data type that's yielded to the JavaScript layer when
  12. // we want deep information about an individual node.
  13. type Node struct {
  14. NodeSummary
  15. Controls []ControlInstance `json:"controls"`
  16. Children []NodeSummaryGroup `json:"children,omitempty"`
  17. Connections []ConnectionsSummary `json:"connections,omitempty"`
  18. }
  19. // ControlInstance contains a control description, and all the info
  20. // needed to execute it.
  21. type ControlInstance struct {
  22. ProbeID string
  23. NodeID string
  24. Control report.Control
  25. }
  26. // MarshalJSON shouldn't be used, use CodecEncodeSelf instead
  27. func (ControlInstance) MarshalJSON() ([]byte, error) {
  28. panic("MarshalJSON shouldn't be used, use CodecEncodeSelf instead")
  29. }
  30. // UnmarshalJSON shouldn't be used, use CodecDecodeSelf instead
  31. func (*ControlInstance) UnmarshalJSON(b []byte) error {
  32. panic("UnmarshalJSON shouldn't be used, use CodecDecodeSelf instead")
  33. }
  34. type wiredControlInstance struct {
  35. ProbeID string `json:"probeId"`
  36. NodeID string `json:"nodeId"`
  37. ID string `json:"id"`
  38. Human string `json:"human"`
  39. Icon string `json:"icon"`
  40. Confirmation string `json:"confirmation,omitempty"`
  41. Rank int `json:"rank"`
  42. }
  43. // CodecEncodeSelf marshals this ControlInstance. It takes the basic Metric
  44. // rendering, then adds some row-specific fields.
  45. func (c *ControlInstance) CodecEncodeSelf(encoder *codec.Encoder) {
  46. encoder.Encode(wiredControlInstance{
  47. ProbeID: c.ProbeID,
  48. NodeID: c.NodeID,
  49. ID: c.Control.ID,
  50. Human: c.Control.Human,
  51. Icon: c.Control.Icon,
  52. Confirmation: c.Control.Confirmation,
  53. Rank: c.Control.Rank,
  54. })
  55. }
  56. // CodecDecodeSelf implements codec.Selfer
  57. func (c *ControlInstance) CodecDecodeSelf(decoder *codec.Decoder) {
  58. var in wiredControlInstance
  59. decoder.Decode(&in)
  60. *c = ControlInstance{
  61. ProbeID: in.ProbeID,
  62. NodeID: in.NodeID,
  63. Control: report.Control{
  64. ID: in.ID,
  65. Human: in.Human,
  66. Icon: in.Icon,
  67. Confirmation: in.Confirmation,
  68. Rank: in.Rank,
  69. },
  70. }
  71. }
  72. // RenderContext carries contextual data that is needed when rendering parts of the report.
  73. type RenderContext struct {
  74. report.Report
  75. MetricsGraphURL string
  76. }
  77. // MakeNode transforms a renderable node to a detailed node. It uses
  78. // aggregate metadata, plus the set of origin node IDs, to produce tables.
  79. func MakeNode(topologyID string, rc RenderContext, ns report.Nodes, n report.Node) Node {
  80. summary, _ := MakeNodeSummary(rc, n)
  81. return Node{
  82. NodeSummary: summary,
  83. Controls: controls(rc.Report, n),
  84. Children: children(rc, n),
  85. Connections: []ConnectionsSummary{
  86. incomingConnectionsSummary(topologyID, rc.Report, n, ns),
  87. outgoingConnectionsSummary(topologyID, rc.Report, n, ns),
  88. },
  89. }
  90. }
  91. func controlsFor(topology report.Topology, nodeID string) []ControlInstance {
  92. result := []ControlInstance{}
  93. node, ok := topology.Nodes[nodeID]
  94. if !ok {
  95. return result
  96. }
  97. probeID, ok := node.Latest.Lookup(report.ControlProbeID)
  98. if !ok {
  99. return result
  100. }
  101. for _, controlID := range node.ActiveControls() {
  102. if control, ok := topology.Controls[controlID]; ok {
  103. if control.ProbeID != "" { // does this Control have an override for the node probe?
  104. probeID = control.ProbeID
  105. }
  106. result = append(result, ControlInstance{
  107. ProbeID: probeID,
  108. NodeID: nodeID,
  109. Control: control,
  110. })
  111. }
  112. }
  113. return result
  114. }
  115. func controls(r report.Report, n report.Node) []ControlInstance {
  116. if t, ok := r.Topology(n.Topology); ok {
  117. return controlsFor(t, n.ID)
  118. }
  119. return []ControlInstance{}
  120. }
  121. // We only need to include topologies here where the nodes may appear
  122. // as children of other nodes in some topology.
  123. var nodeSummaryGroupSpecs = []struct {
  124. topologyID string
  125. NodeSummaryGroup
  126. }{
  127. {
  128. topologyID: report.Pod,
  129. NodeSummaryGroup: NodeSummaryGroup{
  130. Label: "Pods",
  131. Columns: []Column{
  132. {ID: kubernetes.State, Label: "State"},
  133. {ID: report.Container, Label: "# Containers", Datatype: report.Number},
  134. {ID: kubernetes.IP, Label: "IP", Datatype: report.IP},
  135. },
  136. },
  137. },
  138. {
  139. topologyID: report.ECSTask,
  140. NodeSummaryGroup: NodeSummaryGroup{
  141. Label: "Tasks",
  142. Columns: []Column{
  143. {ID: awsecs.CreatedAt, Label: "Created At", Datatype: report.DateTime},
  144. },
  145. },
  146. },
  147. {
  148. topologyID: report.Container,
  149. NodeSummaryGroup: NodeSummaryGroup{
  150. Label: "Containers",
  151. Columns: []Column{
  152. {ID: docker.CPUTotalUsage, Label: "CPU", Datatype: report.Number},
  153. {ID: docker.MemoryUsage, Label: "Memory", Datatype: report.Number},
  154. },
  155. },
  156. },
  157. {
  158. topologyID: report.Process,
  159. NodeSummaryGroup: NodeSummaryGroup{
  160. Label: "Processes",
  161. Columns: []Column{
  162. {ID: process.PID, Label: "PID", Datatype: report.Number},
  163. {ID: process.CPUUsage, Label: "CPU", Datatype: report.Number},
  164. {ID: process.MemoryUsage, Label: "Memory", Datatype: report.Number},
  165. },
  166. },
  167. },
  168. {
  169. topologyID: report.ContainerImage,
  170. NodeSummaryGroup: NodeSummaryGroup{
  171. Label: "Container images",
  172. Columns: []Column{},
  173. },
  174. },
  175. {
  176. topologyID: report.PersistentVolume,
  177. NodeSummaryGroup: NodeSummaryGroup{
  178. Label: "Persistent Volumes",
  179. Columns: []Column{},
  180. },
  181. },
  182. {
  183. topologyID: report.PersistentVolumeClaim,
  184. NodeSummaryGroup: NodeSummaryGroup{
  185. Label: "Persistent Volume Claims",
  186. Columns: []Column{},
  187. },
  188. },
  189. {
  190. topologyID: report.StorageClass,
  191. NodeSummaryGroup: NodeSummaryGroup{
  192. Label: "Storage Classes",
  193. Columns: []Column{},
  194. },
  195. },
  196. {
  197. topologyID: report.VolumeSnapshot,
  198. NodeSummaryGroup: NodeSummaryGroup{
  199. Label: "Volume Snapshots",
  200. Columns: []Column{},
  201. },
  202. },
  203. {
  204. topologyID: report.VolumeSnapshotData,
  205. NodeSummaryGroup: NodeSummaryGroup{
  206. Label: "Volume Snapshot Data",
  207. Columns: []Column{},
  208. },
  209. },
  210. }
  211. func children(rc RenderContext, n report.Node) []NodeSummaryGroup {
  212. summaries := map[string][]NodeSummary{}
  213. n.Children.ForEach(func(child report.Node) {
  214. if child.ID == n.ID {
  215. return
  216. }
  217. summary, ok := MakeNodeSummary(rc, child)
  218. if !ok {
  219. return
  220. }
  221. summaries[child.Topology] = append(summaries[child.Topology], summary.SummarizeMetrics())
  222. })
  223. nodeSummaryGroups := []NodeSummaryGroup{}
  224. // Apply specific group specs in the order they're listed
  225. for _, spec := range nodeSummaryGroupSpecs {
  226. if len(summaries[spec.topologyID]) == 0 {
  227. continue
  228. }
  229. apiTopology, ok := primaryAPITopology[spec.topologyID]
  230. if !ok {
  231. continue
  232. }
  233. sort.Sort(nodeSummariesByID(summaries[spec.topologyID]))
  234. group := spec.NodeSummaryGroup
  235. group.Nodes = summaries[spec.topologyID]
  236. group.TopologyID = apiTopology
  237. nodeSummaryGroups = append(nodeSummaryGroups, group)
  238. delete(summaries, spec.topologyID)
  239. }
  240. // As a fallback, in case a topology has no group spec defined, add any remaining at the end
  241. for topologyID, nodeSummaries := range summaries {
  242. if len(nodeSummaries) == 0 {
  243. continue
  244. }
  245. topology, ok := rc.Topology(topologyID)
  246. if !ok {
  247. continue
  248. }
  249. apiTopology, ok := primaryAPITopology[topologyID]
  250. if !ok {
  251. continue
  252. }
  253. sort.Sort(nodeSummariesByID(nodeSummaries))
  254. group := NodeSummaryGroup{
  255. TopologyID: apiTopology,
  256. Label: topology.LabelPlural,
  257. Columns: []Column{},
  258. }
  259. nodeSummaryGroups = append(nodeSummaryGroups, group)
  260. }
  261. return nodeSummaryGroups
  262. }