mirror of
https://github.com/weaveworks/scope.git
synced 2026-03-05 19:21:46 +00:00
* Hacky working prototype. * Operate with time.Duration offset instead of fixed timestamp. * Polished the backend code. * Made a nicer UI component. * Small refactorings of the websockets code. * Fixed the backend tests. * Better websocketing and smoother transitions * Small styling refactoring. * Detecting empty topologies. * Improved error messaging. * Addressed some of David's comments. * Moved nodesDeltaBuffer to a global state to fix the paused status rendering bug. * Small styling changes * Changed the websocket global state variables a bit. * Polishing & refactoring. * More polishing. * Final refactoring. * Addressed a couple of bugs. * Hidden the timeline control behind Cloud context and a feature flag. * Addressed most of @davkal's comments. * Added mixpanel tracking.
182 lines
5.1 KiB
JavaScript
182 lines
5.1 KiB
JavaScript
import { endsWith } from 'lodash';
|
|
import { Set as makeSet, List as makeList } from 'immutable';
|
|
|
|
import { isWebsocketQueryingCurrentSelector } from '../selectors/time-travel';
|
|
import { isResourceViewModeSelector } from '../selectors/topology';
|
|
import { pinnedMetricSelector } from '../selectors/node-metric';
|
|
|
|
//
|
|
// top priority first
|
|
//
|
|
const TOPOLOGY_DISPLAY_PRIORITY = [
|
|
'ecs-services',
|
|
'ecs-tasks',
|
|
'services',
|
|
'deployments',
|
|
'replica-sets',
|
|
'pods',
|
|
'containers',
|
|
];
|
|
|
|
|
|
export function getDefaultTopology(topologies) {
|
|
const flatTopologies = topologies
|
|
.flatMap(t => makeList([t]).concat(t.get('sub_topologies', makeList())));
|
|
|
|
return flatTopologies
|
|
.sortBy((t) => {
|
|
const index = TOPOLOGY_DISPLAY_PRIORITY.indexOf(t.get('id'));
|
|
return index === -1 ? Infinity : index;
|
|
})
|
|
.getIn([0, 'id']);
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns a cache ID based on the topologyId and optionsQuery
|
|
* @param {String} topologyId
|
|
* @param {object} topologyOptions (optional)
|
|
* @return {String}
|
|
*/
|
|
export function buildTopologyCacheId(topologyId, topologyOptions) {
|
|
let id = '';
|
|
if (topologyId) {
|
|
id = topologyId;
|
|
if (topologyOptions) {
|
|
id += JSON.stringify(topologyOptions);
|
|
}
|
|
}
|
|
return id;
|
|
}
|
|
|
|
/**
|
|
* Returns a topology object from the topology tree
|
|
* @param {List} subTree
|
|
* @param {String} topologyId
|
|
* @return {Map} topology if found
|
|
*/
|
|
export function findTopologyById(subTree, topologyId) {
|
|
let foundTopology;
|
|
|
|
subTree.forEach((topology) => {
|
|
if (endsWith(topology.get('url'), topologyId)) {
|
|
foundTopology = topology;
|
|
}
|
|
if (!foundTopology && topology.has('sub_topologies')) {
|
|
foundTopology = findTopologyById(topology.get('sub_topologies'), topologyId);
|
|
}
|
|
});
|
|
|
|
return foundTopology;
|
|
}
|
|
|
|
export function updateNodeDegrees(nodes, edges) {
|
|
return nodes.map((node) => {
|
|
const nodeId = node.get('id');
|
|
const degree = edges.count(edge => edge.get('source') === nodeId
|
|
|| edge.get('target') === nodeId);
|
|
return node.set('degree', degree);
|
|
});
|
|
}
|
|
|
|
/* set topology.id and parentId for sub-topologies in place */
|
|
export function updateTopologyIds(topologies, parentId) {
|
|
return topologies.map((topology) => {
|
|
const result = Object.assign({}, topology);
|
|
result.id = topology.url.split('/').pop();
|
|
if (parentId) {
|
|
result.parentId = parentId;
|
|
}
|
|
if (topology.sub_topologies) {
|
|
result.sub_topologies = updateTopologyIds(topology.sub_topologies, result.id);
|
|
}
|
|
return result;
|
|
});
|
|
}
|
|
|
|
export function addTopologyFullname(topologies) {
|
|
return topologies.map((t) => {
|
|
if (!t.sub_topologies) {
|
|
return Object.assign({}, t, {fullName: t.name});
|
|
}
|
|
return Object.assign({}, t, {
|
|
fullName: t.name,
|
|
sub_topologies: t.sub_topologies.map(st => (
|
|
Object.assign({}, st, {fullName: `${t.name} ${st.name}`})
|
|
))
|
|
});
|
|
});
|
|
}
|
|
|
|
// adds ID field to topology (based on last part of URL path) and save urls in
|
|
// map for easy lookup
|
|
export function setTopologyUrlsById(topologyUrlsById, topologies) {
|
|
let urlMap = topologyUrlsById;
|
|
if (topologies) {
|
|
topologies.forEach((topology) => {
|
|
urlMap = urlMap.set(topology.id, topology.url);
|
|
if (topology.sub_topologies) {
|
|
topology.sub_topologies.forEach((subTopology) => {
|
|
urlMap = urlMap.set(subTopology.id, subTopology.url);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
return urlMap;
|
|
}
|
|
|
|
export function filterHiddenTopologies(topologies) {
|
|
return topologies.filter(t => (!t.hide_if_empty || t.stats.node_count > 0 ||
|
|
t.stats.filtered_nodes > 0));
|
|
}
|
|
|
|
export function getCurrentTopologyOptions(state) {
|
|
return state.getIn(['currentTopology', 'options']);
|
|
}
|
|
|
|
export function isTopologyNodeCountZero(state) {
|
|
const nodeCount = state.getIn(['currentTopology', 'stats', 'node_count'], 0);
|
|
return nodeCount === 0 && isWebsocketQueryingCurrentSelector(state);
|
|
}
|
|
|
|
export function isNodesDisplayEmpty(state) {
|
|
// Consider a topology in the resource view empty if it has no pinned metric.
|
|
if (isResourceViewModeSelector(state)) {
|
|
return !pinnedMetricSelector(state);
|
|
}
|
|
// Otherwise (in graph and table view), we only look at the nodes content.
|
|
return state.get('nodes').isEmpty();
|
|
}
|
|
|
|
export function isTopologyEmpty(state) {
|
|
return isTopologyNodeCountZero(state) || isNodesDisplayEmpty(state);
|
|
}
|
|
|
|
export function getAdjacentNodes(state, originNodeId) {
|
|
let adjacentNodes = makeSet();
|
|
const nodeId = originNodeId || state.get('selectedNodeId');
|
|
|
|
if (nodeId) {
|
|
if (state.hasIn(['nodes', nodeId])) {
|
|
adjacentNodes = makeSet(state.getIn(['nodes', nodeId, 'adjacency']));
|
|
// fill up set with reverse edges
|
|
state.get('nodes').forEach((node, id) => {
|
|
if (node.get('adjacency') && node.get('adjacency').includes(nodeId)) {
|
|
adjacentNodes = adjacentNodes.add(id);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
return adjacentNodes;
|
|
}
|
|
|
|
export function hasSelectedNode(state) {
|
|
const selectedNodeId = state.get('selectedNodeId');
|
|
return state.hasIn(['nodes', selectedNodeId]);
|
|
}
|
|
|
|
export function getCurrentTopologyUrl(state) {
|
|
return state.getIn(['currentTopology', 'url']);
|
|
}
|