123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382 |
- /* eslint react/jsx-no-bind: "off" */
- import React from 'react';
- import { connect } from 'react-redux';
- import {
- sampleSize, sample, random, range, flattenDeep, times
- } from 'lodash';
- import { fromJS, Set as makeSet } from 'immutable';
- import { hsl } from 'd3-color';
- import debug from 'debug';
- import ActionTypes from '../constants/action-types';
- import { receiveNodesDelta } from '../actions/app-actions';
- import { getNodeColor, getNodeColorDark, text2degree } from '../utils/color-utils';
- import { availableMetricsSelector } from '../selectors/node-metric';
- const SHAPES = ['square', 'hexagon', 'heptagon', 'circle'];
- const STACK_VARIANTS = [false, true];
- const METRIC_FILLS = [0, 0.1, 50, 99.9, 100];
- const NETWORKS = [
- 'be', 'fe', 'zb', 'db', 're', 'gh', 'jk', 'lol', 'nw'
- ].map(n => ({ colorKey: n, id: n, label: n }));
- const INTERNET = 'the-internet';
- const LOREM = `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
- incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
- ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in
- voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
- proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`;
- const sampleArray = (collection, n = 4) => sampleSize(collection, random(n));
- const log = debug('scope:debug-panel');
- const shapeTypes = {
- circle: ['Host', 'Hosts'],
- heptagon: ['Pod', 'Pods'],
- hexagon: ['Container', 'Containers'],
- square: ['Process', 'Processes']
- };
- const LABEL_PREFIXES = range('A'.charCodeAt(), 'Z'.charCodeAt() + 1)
- .map(n => String.fromCharCode(n));
- const deltaAdd = (name, adjacency = [], shape = 'circle', stack = false, networks = NETWORKS) => ({
- adjacency,
- controls: {},
- id: name,
- label: name,
- labelMinor: name,
- latest: {},
- networks,
- origins: [],
- rank: name,
- shape,
- stack
- });
- function addMetrics(availableMetrics, node, v) {
- const metrics = availableMetrics.size > 0 ? availableMetrics : fromJS([
- { id: 'host_cpu_usage_percent', label: 'CPU' }
- ]);
- return Object.assign({}, node, {
- metrics: metrics.map(m => Object.assign({}, m, {
- id: 'zing', label: 'zing', max: 100, value: v
- })).toJS()
- });
- }
- function label(shape, stacked) {
- const type = shapeTypes[shape];
- return stacked ? `Group of ${type[1]}` : type[0];
- }
- function addAllVariants(dispatch) {
- const newNodes = flattenDeep(STACK_VARIANTS.map(stack => (SHAPES.map((s) => {
- if (!stack) return [deltaAdd(label(s, stack), [], s, stack)];
- return times(3).map(() => deltaAdd(label(s, stack), [], s, stack));
- }))));
- dispatch(receiveNodesDelta({
- add: newNodes
- }));
- }
- function addAllMetricVariants(availableMetrics) {
- const newNodes = flattenDeep(METRIC_FILLS.map((v, i) => (
- SHAPES.map(s => [addMetrics(availableMetrics, deltaAdd(label(s) + i, [], s), v)])
- )));
- return (dispatch) => {
- dispatch(receiveNodesDelta({
- add: newNodes
- }));
- };
- }
- export function showingDebugToolbar() {
- return (('debugToolbar' in localStorage && JSON.parse(localStorage.debugToolbar))
- || window.location.pathname.indexOf('debug') > -1);
- }
- export function toggleDebugToolbar() {
- if ('debugToolbar' in localStorage) {
- localStorage.debugToolbar = !showingDebugToolbar();
- }
- }
- function enableLog(ns) {
- debug.enable(`scope:${ns}`);
- window.location.reload();
- }
- function disableLog() {
- debug.disable();
- window.location.reload();
- }
- function setAppState(fn) {
- return (dispatch) => {
- dispatch({
- fn,
- type: ActionTypes.DEBUG_TOOLBAR_INTERFERING
- });
- };
- }
- class DebugToolbar extends React.Component {
- constructor(props, context) {
- super(props, context);
- this.onChange = this.onChange.bind(this);
- this.toggleColors = this.toggleColors.bind(this);
- this.addNodes = this.addNodes.bind(this);
- this.intermittentTimer = null;
- this.intermittentNodes = makeSet();
- this.shortLivedTimer = null;
- this.shortLivedNodes = makeSet();
- this.state = {
- nodesToAdd: 30,
- showColors: false
- };
- }
- onChange(ev) {
- this.setState({ nodesToAdd: parseInt(ev.target.value, 10) });
- }
- toggleColors() {
- this.setState(prevState => ({
- showColors: !prevState.showColors
- }));
- }
- asyncDispatch(v) {
- setTimeout(() => this.props.dispatch(v), 0);
- }
- setLoading(loading) {
- this.asyncDispatch(setAppState(state => state.set('topologiesLoaded', !loading)));
- }
- setIntermittent() {
- // simulate epheremal nodes
- if (this.intermittentTimer) {
- clearInterval(this.intermittentTimer);
- this.intermittentTimer = null;
- } else {
- this.intermittentTimer = setInterval(() => {
- // add new node
- this.addNodes(1);
- // remove random node
- const ns = this.props.nodes;
- const nodeNames = ns.keySeq().toJS();
- const randomNode = sample(nodeNames);
- this.asyncDispatch(receiveNodesDelta({
- remove: [randomNode]
- }));
- }, 1000);
- }
- }
- setShortLived() {
- // simulate nodes with same ID popping in and out
- if (this.shortLivedTimer) {
- clearInterval(this.shortLivedTimer);
- this.shortLivedTimer = null;
- } else {
- this.shortLivedTimer = setInterval(() => {
- // filter random node
- const ns = this.props.nodes;
- const nodeNames = ns.keySeq().toJS();
- const randomNode = sample(nodeNames);
- if (randomNode) {
- let nextNodes = ns.setIn([randomNode, 'filtered'], true);
- this.shortLivedNodes = this.shortLivedNodes.add(randomNode);
- // bring nodes back after a bit
- if (this.shortLivedNodes.size > 5) {
- const returningNode = this.shortLivedNodes.first();
- this.shortLivedNodes = this.shortLivedNodes.rest();
- nextNodes = nextNodes.setIn([returningNode, 'filtered'], false);
- }
- this.asyncDispatch(setAppState(state => state.set('nodes', nextNodes)));
- }
- }, 1000);
- }
- }
- updateAdjacencies() {
- const ns = this.props.nodes;
- const nodeNames = ns.keySeq().toJS();
- this.asyncDispatch(receiveNodesDelta({
- add: this.createRandomNodes(7),
- remove: this.randomExistingNode(),
- update: sampleArray(nodeNames).map(n => ({
- adjacency: sampleArray(nodeNames),
- id: n,
- }), nodeNames.length),
- }));
- }
- createRandomNodes(n, prefix = 'zing') {
- const ns = this.props.nodes;
- const nodeNames = ns.keySeq().toJS();
- const newNodeNames = range(ns.size, ns.size + n).map(i => (
- // `${randomLetter()}${randomLetter()}-zing`
- `${prefix}${i}`
- ));
- const allNodes = nodeNames.concat(newNodeNames);
- return newNodeNames.map(name => deltaAdd(
- name,
- sampleArray(allNodes),
- sample(SHAPES),
- sample(STACK_VARIANTS),
- sampleArray(NETWORKS, 10)
- ));
- }
- addInternetNode() {
- setTimeout(() => {
- this.asyncDispatch(receiveNodesDelta({
- add: [{
- id: INTERNET, label: INTERNET, labelMinor: 'Outgoing packets', pseudo: true, shape: 'cloud'
- }]
- }));
- }, 0);
- }
- addNodes(n, prefix = 'zing') {
- setTimeout(() => {
- this.asyncDispatch(receiveNodesDelta({
- add: this.createRandomNodes(n, prefix)
- }));
- log('added nodes', n);
- }, 0);
- }
- randomExistingNode() {
- const ns = this.props.nodes;
- const nodeNames = ns.keySeq().toJS();
- return [nodeNames[random(nodeNames.length - 1)]];
- }
- removeNode() {
- this.asyncDispatch(receiveNodesDelta({
- remove: this.randomExistingNode()
- }));
- }
- render() {
- const { availableMetrics } = this.props;
- return (
- <div className="debug-panel">
- <div>
- <strong>Add nodes </strong>
- <button type="button" onClick={() => this.addNodes(1)}>+1</button>
- <button type="button" onClick={() => this.addNodes(10)}>+10</button>
- <input type="number" onChange={this.onChange} value={this.state.nodesToAdd} />
- <button type="button" onClick={() => this.addNodes(this.state.nodesToAdd)}>+</button>
- <button type="button" onClick={() => this.asyncDispatch(addAllVariants)}>
- Variants
- </button>
- <button
- type="button"
- onClick={() => this.asyncDispatch(addAllMetricVariants(availableMetrics))}>
- Metric Variants
- </button>
- <button type="button" onClick={() => this.addNodes(1, LOREM)}>Long name</button>
- <button type="button" onClick={() => this.addInternetNode()}>Internet</button>
- <button type="button" onClick={() => this.removeNode()}>Remove node</button>
- <button type="button" onClick={() => this.updateAdjacencies()}>Update adj.</button>
- </div>
- <div>
- <strong>Logging </strong>
- <button type="button" onClick={() => enableLog('*')}>scope:*</button>
- <button type="button" onClick={() => enableLog('dispatcher')}>scope:dispatcher</button>
- <button type="button" onClick={() => enableLog('app-key-press')}>
- scope:app-key-press
- </button>
- <button type="button" onClick={() => enableLog('terminal')}>scope:terminal</button>
- <button type="button" onClick={() => disableLog()}>Disable log</button>
- </div>
- <div>
- <strong>Colors </strong>
- <button type="button" onClick={this.toggleColors}>toggle</button>
- </div>
- {this.state.showColors
- && (
- <table>
- <tbody>
- {LABEL_PREFIXES.map(r => (
- <tr key={r}>
- <td
- title={`${r}`}
- style={{ backgroundColor: hsl(text2degree(r), 0.5, 0.5).toString() }} />
- </tr>
- ))}
- </tbody>
- </table>
- )}
- {this.state.showColors && [getNodeColor, getNodeColorDark].map(fn => (
- <table key={fn}>
- <tbody>
- {LABEL_PREFIXES.map(r => (
- <tr key={r}>
- {LABEL_PREFIXES.map(c => (
- <td key={c} title={`(${r}, ${c})`} style={{ backgroundColor: fn(r, c) }} />
- ))}
- </tr>
- ))}
- </tbody>
- </table>
- ))}
- <div>
- <strong>State </strong>
- <button type="button" onClick={() => this.setLoading(true)}>
- Set doing initial load
- </button>
- <button type="button" onClick={() => this.setLoading(false)}>Stop</button>
- </div>
- <div>
- <strong>Short-lived nodes </strong>
- <button type="button" onClick={() => this.setShortLived()}>
- Toggle short-lived nodes
- </button>
- <button type="button" onClick={() => this.setIntermittent()}>
- Toggle intermittent nodes
- </button>
- </div>
- </div>
- );
- }
- }
- function mapStateToProps(state) {
- return {
- availableMetrics: availableMetricsSelector(state),
- nodes: state.get('nodes'),
- };
- }
- export default connect(mapStateToProps)(DebugToolbar);
|