topology-utils.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. import { endsWith } from 'lodash';
  2. import { Set as makeSet, List as makeList, Map as makeMap } from 'immutable';
  3. import stableStringify from 'json-stable-stringify';
  4. import { isPausedSelector } from '../selectors/time-travel';
  5. import { isResourceViewModeSelector } from '../selectors/topology';
  6. import { pinnedMetricSelector } from '../selectors/node-metric';
  7. import { shownNodesSelector, shownResourceTopologyIdsSelector } from '../selectors/node-filters';
  8. //
  9. // top priority first
  10. //
  11. const TOPOLOGY_DISPLAY_PRIORITY = [
  12. 'ecs-services',
  13. 'ecs-tasks',
  14. 'kube-controllers',
  15. 'services',
  16. 'replica-sets',
  17. 'pods',
  18. 'containers',
  19. ];
  20. export function getDefaultTopology(topologies) {
  21. const flatTopologies = topologies
  22. .flatMap(t => makeList([t]).concat(t.get('sub_topologies', makeList())));
  23. return flatTopologies
  24. .sortBy((t) => {
  25. const index = TOPOLOGY_DISPLAY_PRIORITY.indexOf(t.get('id'));
  26. return index === -1 ? Infinity : index;
  27. })
  28. .getIn([0, 'id']);
  29. }
  30. /**
  31. * Returns a cache ID based on the topologyId and optionsQuery
  32. * @param {String} topologyId
  33. * @param {object} topologyOptions (optional)
  34. * @return {String}
  35. */
  36. export function buildTopologyCacheId(topologyId, topologyOptions) {
  37. let id = '';
  38. if (topologyId) {
  39. id = topologyId;
  40. if (topologyOptions) {
  41. id += stableStringify(topologyOptions);
  42. }
  43. }
  44. return id;
  45. }
  46. /**
  47. * Returns a topology object from the topology tree
  48. * @param {List} subTree
  49. * @param {String} topologyId
  50. * @return {Map} topology if found
  51. */
  52. export function findTopologyById(subTree, topologyId) {
  53. let foundTopology;
  54. subTree.forEach((topology) => {
  55. if (endsWith(topology.get('url'), topologyId)) {
  56. foundTopology = topology;
  57. }
  58. if (!foundTopology && topology.has('sub_topologies')) {
  59. foundTopology = findTopologyById(topology.get('sub_topologies'), topologyId);
  60. }
  61. });
  62. return foundTopology;
  63. }
  64. export function updateNodeDegrees(nodes, edges) {
  65. return nodes.map((node) => {
  66. const nodeId = node.get('id');
  67. const degree = edges.count(edge => edge.get('source') === nodeId
  68. || edge.get('target') === nodeId);
  69. return node.set('degree', degree);
  70. });
  71. }
  72. /* set topology.id and parentId for sub-topologies in place */
  73. export function updateTopologyIds(topologies, parentId) {
  74. if(topologies) {
  75. return topologies.map((topology) => {
  76. if (topology) {
  77. const result = Object.assign({}, topology);
  78. result.id = topology.url.split('/').pop();
  79. if (parentId) {
  80. result.parentId = parentId;
  81. }
  82. if (topology.sub_topologies) {
  83. result.sub_topologies = updateTopologyIds(topology.sub_topologies, result.id);
  84. }
  85. return result;
  86. }
  87. });
  88. }
  89. }
  90. export function addTopologyFullname(topologies) {
  91. return topologies.map((t) => {
  92. if (!t.sub_topologies) {
  93. return Object.assign({}, t, {fullName: t.name});
  94. }
  95. return Object.assign({}, t, {
  96. fullName: t.name,
  97. sub_topologies: t.sub_topologies.map(st => (
  98. Object.assign({}, st, {fullName: `${t.name} ${st.name}`})
  99. ))
  100. });
  101. });
  102. }
  103. // adds ID field to topology (based on last part of URL path) and save urls in
  104. // map for easy lookup
  105. export function setTopologyUrlsById(topologyUrlsById, topologies) {
  106. let urlMap = topologyUrlsById;
  107. if (topologies) {
  108. topologies.forEach((topology) => {
  109. urlMap = urlMap.set(topology.id, topology.url);
  110. if (topology.sub_topologies) {
  111. topology.sub_topologies.forEach((subTopology) => {
  112. urlMap = urlMap.set(subTopology.id, subTopology.url);
  113. });
  114. }
  115. });
  116. }
  117. return urlMap;
  118. }
  119. export function filterHiddenTopologies(topologies, currentTopology) {
  120. currentTopology = currentTopology || makeMap();
  121. return topologies.filter(t => (!t.hide_if_empty || t.stats.node_count > 0
  122. || t.stats.filtered_nodes > 0 || t.id === currentTopology.get('id')
  123. || t.id === currentTopology.get('parentId')));
  124. }
  125. export function getCurrentTopologyOptions(state) {
  126. return state.getIn(['currentTopology', 'options']);
  127. }
  128. export function isTopologyNodeCountZero(state) {
  129. const nodeCount = state.getIn(['currentTopology', 'stats', 'node_count'], 0);
  130. // If we are browsing the past, assume there would normally be some nodes at different times.
  131. // If we are in the resource view, don't rely on these stats at all (for now).
  132. return nodeCount === 0 && !isPausedSelector(state) && !isResourceViewModeSelector(state);
  133. }
  134. export function isNodesDisplayEmpty(state) {
  135. // Consider a topology in the resource view empty if it has no pinned metric.
  136. if (isResourceViewModeSelector(state)) {
  137. return !pinnedMetricSelector(state) || shownResourceTopologyIdsSelector(state).isEmpty();
  138. }
  139. // Otherwise (in graph and table view), we only look at the nodes content.
  140. return shownNodesSelector(state).isEmpty();
  141. }
  142. export function getAdjacentNodes(state, originNodeId) {
  143. let adjacentNodes = makeSet();
  144. const nodeId = originNodeId || state.get('selectedNodeId');
  145. if (nodeId) {
  146. if (state.hasIn(['nodes', nodeId])) {
  147. adjacentNodes = makeSet(state.getIn(['nodes', nodeId, 'adjacency']));
  148. // fill up set with reverse edges
  149. state.get('nodes').forEach((node, id) => {
  150. if (node.get('adjacency') && node.get('adjacency').includes(nodeId)) {
  151. adjacentNodes = adjacentNodes.add(id);
  152. }
  153. });
  154. }
  155. }
  156. return adjacentNodes;
  157. }
  158. export function hasSelectedNode(state) {
  159. const selectedNodeId = state.get('selectedNodeId');
  160. return state.hasIn(['nodes', selectedNodeId]);
  161. }
  162. export function getCurrentTopologyUrl(state) {
  163. return state.getIn(['currentTopology', 'url']);
  164. }