mirror of
https://github.com/weaveworks/scope.git
synced 2026-08-18 11:56:39 +00:00
@@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function NodeShapeCircle({onlyHighlight, highlighted, size, color}) {
|
||||
const hightlightNode = <circle r={size * 0.7} className="highlighted" />;
|
||||
|
||||
if (onlyHighlight) {
|
||||
return (
|
||||
<g className="shape">
|
||||
{highlighted && hightlightNode}
|
||||
</g>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<g className="shape">
|
||||
{highlighted && hightlightNode}
|
||||
<circle r={size * 0.5} className="border" stroke={color} />
|
||||
<circle r={size * 0.45} className="shadow" />
|
||||
<circle r={Math.max(2, size * 0.125)} className="node" />
|
||||
</g>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
import d3 from 'd3';
|
||||
|
||||
const CLOUD_PATH = 'M 1920,384 Q 1920,225 1807.5,112.5 1695,0 1536,0 H 448 Q 263,0 131.5,131.5 0,263 0,448 0,580 71,689.5 142,799 258,853 q -2,28 -2,43 0,212 150,362 150,150 362,150 158,0 286.5,-88 128.5,-88 187.5,-230 70,62 166,62 106,0 181,-75 75,-75 75,-181 0,-75 -41,-138 129,-30 213,-134.5 84,-104.5 84,-239.5 z';
|
||||
|
||||
function toPoint(stringPair) {
|
||||
return stringPair.split(',').map(p => parseFloat(p, 10));
|
||||
}
|
||||
|
||||
function getExtents(svgPath) {
|
||||
const points = svgPath.split(' ').filter(s => s.length > 1).map(toPoint);
|
||||
return [d3.extent(points, p => p[0]), d3.extent(points, p => p[1])];
|
||||
}
|
||||
|
||||
export default function NodeShapeCloud({highlighted, size, color}) {
|
||||
const [[minx, maxx], [miny, maxy]] = getExtents(CLOUD_PATH);
|
||||
const width = (maxx - minx);
|
||||
const height = (maxy - miny);
|
||||
const cx = width / 2;
|
||||
const cy = height / 2;
|
||||
const pathSize = (width + height) / 2;
|
||||
const baseScale = (size * 2) / pathSize;
|
||||
|
||||
const pathProps = (v) => {
|
||||
return {
|
||||
d: CLOUD_PATH,
|
||||
transform: `scale(-${v * baseScale}) translate(-${cx},-${cy})`,
|
||||
style: {strokeWidth: 1 / baseScale}
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<g className="shape">
|
||||
{highlighted &&
|
||||
<path className="highlighted" {...pathProps(0.7)} />}
|
||||
<path className="border" stroke={color} {...pathProps(0.5)} />
|
||||
<path className="shadow" {...pathProps(0.45)} />
|
||||
<circle className="node" r={Math.max(2, (size * 0.125))} />
|
||||
</g>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import React from 'react';
|
||||
import d3 from 'd3';
|
||||
|
||||
const line = d3.svg.line()
|
||||
.interpolate('cardinal-closed')
|
||||
.tension(0.25);
|
||||
|
||||
function getWidth(h) {
|
||||
return (Math.sqrt(3) / 2) * h;
|
||||
}
|
||||
|
||||
function getPoints(h) {
|
||||
const w = getWidth(h);
|
||||
const points = [
|
||||
[w * 0.5, 0],
|
||||
[w, 0.25 * h],
|
||||
[w, 0.75 * h],
|
||||
[w * 0.5, h],
|
||||
[0, 0.75 * h],
|
||||
[0, 0.25 * h]
|
||||
];
|
||||
|
||||
return line(points);
|
||||
}
|
||||
|
||||
|
||||
export default function NodeShapeHex({onlyHighlight, highlighted, size, color}) {
|
||||
const pathProps = (v) => {
|
||||
return {
|
||||
d: getPoints(size * v * 2),
|
||||
transform: `rotate(90) translate(-${size * getWidth(v)}, -${size * v})`
|
||||
};
|
||||
};
|
||||
|
||||
const hightlightNode = <path className="highlighted" {...pathProps(0.7)} />;
|
||||
|
||||
if (onlyHighlight) {
|
||||
return (
|
||||
<g className="shape">
|
||||
{highlighted && hightlightNode}
|
||||
</g>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<g className="shape">
|
||||
{highlighted && hightlightNode}
|
||||
<path className="border" stroke={color} {...pathProps(0.5)} />
|
||||
<path className="shadow" {...pathProps(0.45)} />
|
||||
<circle className="node" r={Math.max(2, (size * 0.125))} />
|
||||
</g>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
import NodeShapeSquare from './node-shape-square';
|
||||
|
||||
// TODO how to express a cmp in terms of another cmp? (Rather than a sub-cmp as here).
|
||||
|
||||
export default function NodeShapeRoundedSquare(props) {
|
||||
return (
|
||||
<NodeShapeSquare {...props} rx="0.4" ry="0.4" />
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function NodeShapeSquare({onlyHighlight, highlighted, size, color, rx = 0, ry = 0}) {
|
||||
const rectProps = (v) => {
|
||||
return {
|
||||
width: v * size * 2,
|
||||
height: v * size * 2,
|
||||
rx: v * size * rx,
|
||||
ry: v * size * ry,
|
||||
transform: `translate(-${size * v}, -${size * v})`
|
||||
};
|
||||
};
|
||||
|
||||
const hightlightNode = <rect className="highlighted" {...rectProps(0.7)} />;
|
||||
|
||||
if (onlyHighlight) {
|
||||
return (
|
||||
<g className="shape">
|
||||
{highlighted && hightlightNode}
|
||||
</g>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<g className="shape">
|
||||
{highlighted && hightlightNode}
|
||||
<rect className="border" stroke={color} {...rectProps(0.5)} />
|
||||
<rect className="shadow" {...rectProps(0.45)} />
|
||||
<circle className="node" r={Math.max(2, (size * 0.125))} />
|
||||
</g>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
|
||||
function dissoc(obj, key) {
|
||||
const newObj = _.clone(obj);
|
||||
delete newObj[key];
|
||||
return newObj;
|
||||
}
|
||||
|
||||
export default function NodeShapeStack(props) {
|
||||
const propsNoHighlight = dissoc(props, 'highlighted');
|
||||
const propsOnlyHighlight = Object.assign({}, props, {onlyHighlight: true});
|
||||
|
||||
const Shape = props.shape;
|
||||
const [dx, dy] = [0, 4];
|
||||
const dsx = (props.size * 2 + (dx * 2)) / (props.size * 2);
|
||||
const dsy = (props.size * 2 + (dy * 2)) / (props.size * 2);
|
||||
const hls = [dsx, dsy];
|
||||
|
||||
return (
|
||||
<g transform={`translate(${dx * -1}, ${dy * -1})`} className="stack">
|
||||
<g transform={`scale(${hls})translate(${dx}, ${dy}) `} className="stack">
|
||||
<Shape {...propsOnlyHighlight} />
|
||||
</g>
|
||||
<g transform={`translate(${dx * 2}, ${dy * 2})`}>
|
||||
<Shape {...propsNoHighlight} />
|
||||
</g>
|
||||
<g transform={`translate(${dx * 1}, ${dy * 1})`}>
|
||||
<Shape {...propsNoHighlight} />
|
||||
</g>
|
||||
<Shape {...propsNoHighlight} />
|
||||
</g>
|
||||
);
|
||||
}
|
||||
@@ -5,6 +5,35 @@ import { Motion, spring } from 'react-motion';
|
||||
import { clickNode, enterNode, leaveNode } from '../actions/app-actions';
|
||||
import { getNodeColor } from '../utils/color-utils';
|
||||
|
||||
import NodeShapeCircle from './node-shape-circle';
|
||||
import NodeShapeStack from './node-shape-stack';
|
||||
import NodeShapeRoundedSquare from './node-shape-rounded-square';
|
||||
import NodeShapeHex from './node-shape-hex';
|
||||
import NodeShapeCloud from './node-shape-cloud';
|
||||
|
||||
function stackedShape(Shape) {
|
||||
const factory = React.createFactory(NodeShapeStack);
|
||||
|
||||
return function(props) {
|
||||
return factory(Object.assign({}, props, {shape: Shape}));
|
||||
};
|
||||
}
|
||||
|
||||
const nodeShapes = {
|
||||
'circle': NodeShapeCircle,
|
||||
'hexagon': NodeShapeHex,
|
||||
'square': NodeShapeRoundedSquare,
|
||||
'cloud': NodeShapeCloud
|
||||
};
|
||||
|
||||
function getNodeShape({shape, stack}) {
|
||||
const nodeShape = nodeShapes[shape];
|
||||
if (!nodeShape) {
|
||||
throw new Error(`Unkown shape: ${shape}!`);
|
||||
}
|
||||
return stack ? stackedShape(nodeShape) : nodeShape;
|
||||
}
|
||||
|
||||
export default class Node extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
@@ -52,8 +81,11 @@ export default class Node extends React.Component {
|
||||
if (this.props.pseudo) {
|
||||
classNames.push('pseudo');
|
||||
}
|
||||
|
||||
const classes = classNames.join(' ');
|
||||
|
||||
const NodeShapeType = getNodeShape(this.props);
|
||||
|
||||
return (
|
||||
<Motion style={{
|
||||
x: spring(this.props.dx, animConfig),
|
||||
@@ -69,10 +101,10 @@ export default class Node extends React.Component {
|
||||
return (
|
||||
<g className={classes} transform={transform} id={props.id}
|
||||
onClick={onMouseClick} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
|
||||
{props.highlighted && <circle r={nodeScale(0.7 * interpolated.f)} className="highlighted"></circle>}
|
||||
<circle r={nodeScale(0.5 * interpolated.f)} className="border" stroke={color}></circle>
|
||||
<circle r={nodeScale(0.45 * interpolated.f)} className="shadow"></circle>
|
||||
<circle r={Math.max(2, nodeScale(0.125 * interpolated.f))} className="node"></circle>
|
||||
<NodeShapeType
|
||||
size={nodeScale(interpolated.f)}
|
||||
color={color}
|
||||
{...props} />
|
||||
<text className="node-label" textAnchor="middle" style={{fontSize: interpolated.labelFontSize}}
|
||||
x="0" y={interpolated.labelOffsetY + nodeScale(0.5 * interpolated.f)}>
|
||||
{label}
|
||||
|
||||
@@ -142,11 +142,15 @@ export default class NodesChart extends React.Component {
|
||||
blurred={node.get('blurred')}
|
||||
focused={node.get('focused')}
|
||||
highlighted={node.get('highlighted')}
|
||||
topologyId={this.props.topologyId}
|
||||
shape={node.get('shape')}
|
||||
stack={node.get('stack')}
|
||||
onClick={onNodeClick}
|
||||
key={node.get('id')}
|
||||
id={node.get('id')}
|
||||
label={node.get('label')}
|
||||
pseudo={node.get('pseudo')}
|
||||
nodeCount={node.get('nodeCount')}
|
||||
subLabel={node.get('subLabel')}
|
||||
rank={node.get('rank')}
|
||||
selectedNodeScale={selectedNodeScale}
|
||||
@@ -243,7 +247,10 @@ export default class NodesChart extends React.Component {
|
||||
label: node.get('label_major'),
|
||||
pseudo: node.get('pseudo'),
|
||||
subLabel: node.get('label_minor'),
|
||||
nodeCount: node.get('node_count'),
|
||||
rank: node.get('rank'),
|
||||
shape: node.get('shape'),
|
||||
stack: node.get('stack'),
|
||||
x: 0,
|
||||
y: 0
|
||||
});
|
||||
|
||||
@@ -7,15 +7,21 @@ const log = debug('scope:debug-panel');
|
||||
import { receiveNodesDelta } from '../actions/app-actions';
|
||||
import AppStore from '../stores/app-store';
|
||||
|
||||
const SHAPES = ['circle', 'hexagon', 'square'];
|
||||
const NODE_COUNTS = [1, 2, 3];
|
||||
const STACK_VARIANTS = [true, false];
|
||||
|
||||
const sample = function(collection) {
|
||||
return _.range(_.random(4)).map(() => _.sample(collection));
|
||||
};
|
||||
|
||||
const deltaAdd = function(name, adjacency = []) {
|
||||
const deltaAdd = function(name, adjacency = [], shape = 'circle', stack = false, nodeCount = 1) {
|
||||
return {
|
||||
'adjacency': adjacency,
|
||||
'controls': {},
|
||||
'shape': shape,
|
||||
'stack': stack,
|
||||
'node_count': nodeCount,
|
||||
'id': name,
|
||||
'label_major': name,
|
||||
'label_minor': 'weave-1',
|
||||
@@ -26,6 +32,21 @@ const deltaAdd = function(name, adjacency = []) {
|
||||
};
|
||||
};
|
||||
|
||||
function addAllVariants() {
|
||||
const newNodes = _.flattenDeep(SHAPES.map(s => {
|
||||
return STACK_VARIANTS.map(stack => {
|
||||
if (!stack) return [deltaAdd([s, 1, stack].join('-'), [], s, stack, 1)];
|
||||
return NODE_COUNTS.map(n => {
|
||||
return deltaAdd([s, n, stack].join('-'), [], s, stack, n);
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
receiveNodesDelta({
|
||||
add: newNodes
|
||||
});
|
||||
}
|
||||
|
||||
function addNodes(n) {
|
||||
const ns = AppStore.getNodes();
|
||||
const nodeNames = ns.keySeq().toJS();
|
||||
@@ -33,7 +54,11 @@ function addNodes(n) {
|
||||
const allNodes = _(nodeNames).concat(newNodeNames).value();
|
||||
|
||||
receiveNodesDelta({
|
||||
add: newNodeNames.map((name) => deltaAdd(name, sample(allNodes)))
|
||||
add: newNodeNames.map((name) => deltaAdd(name,
|
||||
sample(allNodes)),
|
||||
_.sample(SHAPES),
|
||||
_.sample(STACK_VARIANTS),
|
||||
_.sample(NODE_COUNTS))
|
||||
});
|
||||
}
|
||||
|
||||
@@ -65,6 +90,7 @@ export class DebugToolbar extends React.Component {
|
||||
<button onClick={() => addNodes(10)}>+10</button>
|
||||
<input type="number" onChange={this.onChange} value={this.state.nodesToAdd} />
|
||||
<button onClick={() => addNodes(this.state.nodesToAdd)}>+</button>
|
||||
<button onClick={() => addAllVariants()}>Variants</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -39,8 +39,11 @@ function makeNode(node) {
|
||||
id: node.id,
|
||||
label_major: node.label_major,
|
||||
label_minor: node.label_minor,
|
||||
node_count: node.node_count,
|
||||
rank: node.rank,
|
||||
pseudo: node.pseudo,
|
||||
stack: node.stack,
|
||||
shape: node.shape,
|
||||
adjacency: node.adjacency
|
||||
};
|
||||
}
|
||||
|
||||
+23
-18
@@ -303,6 +303,10 @@ h2 {
|
||||
}
|
||||
}
|
||||
|
||||
g.stack g.shape .border {
|
||||
stroke-width: @node-border-stroke-width - 1;
|
||||
}
|
||||
|
||||
g.node {
|
||||
cursor: pointer;
|
||||
transition: opacity .5s ease-in-out;
|
||||
@@ -357,28 +361,29 @@ h2 {
|
||||
}
|
||||
|
||||
|
||||
circle.border {
|
||||
stroke-width: @node-border-stroke-width;
|
||||
fill: none;
|
||||
}
|
||||
.shape {
|
||||
.border {
|
||||
stroke-width: @node-border-stroke-width;
|
||||
fill: @background-color;
|
||||
}
|
||||
|
||||
circle.shadow {
|
||||
stroke: none;
|
||||
fill: @background-lighter-color;
|
||||
}
|
||||
.shadow {
|
||||
stroke: none;
|
||||
fill: @background-lighter-color;
|
||||
}
|
||||
|
||||
circle.node {
|
||||
fill: @text-color;
|
||||
}
|
||||
.node {
|
||||
fill: @text-color;
|
||||
}
|
||||
|
||||
circle.highlighted {
|
||||
fill: @weave-blue;
|
||||
fill-opacity: @node-highlight-fill-opacity;
|
||||
stroke: @weave-blue;
|
||||
stroke-width: @node-highlight-stroke-width;
|
||||
stroke-opacity: @node-highlight-stroke-opacity;
|
||||
.highlighted {
|
||||
fill: @weave-blue;
|
||||
fill-opacity: @node-highlight-fill-opacity;
|
||||
stroke: @weave-blue;
|
||||
stroke-width: @node-highlight-stroke-width;
|
||||
stroke-opacity: @node-highlight-stroke-opacity;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.details {
|
||||
|
||||
@@ -10,6 +10,12 @@ import (
|
||||
|
||||
// Exported for testing.
|
||||
var (
|
||||
circle = "circle"
|
||||
square = "square"
|
||||
pentagon = "pentagon"
|
||||
hexagon = "hexagon"
|
||||
cloud = "cloud"
|
||||
|
||||
uncontainedServerID = render.MakePseudoNodeID(render.UncontainedID, fixture.ServerHostName)
|
||||
unknownPseudoNode1ID = render.MakePseudoNodeID("10.10.10.10", fixture.ServerIP, "80")
|
||||
unknownPseudoNode2ID = render.MakePseudoNodeID("10.10.10.11", fixture.ServerIP, "80")
|
||||
@@ -18,6 +24,7 @@ var (
|
||||
ID: unknownPseudoNode1ID,
|
||||
LabelMajor: "10.10.10.10",
|
||||
Pseudo: true,
|
||||
Shape: circle,
|
||||
Node: report.MakeNode().WithAdjacent(adjacent),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(70),
|
||||
@@ -30,6 +37,7 @@ var (
|
||||
ID: unknownPseudoNode2ID,
|
||||
LabelMajor: "10.10.10.11",
|
||||
Pseudo: true,
|
||||
Shape: circle,
|
||||
Node: report.MakeNode().WithAdjacent(adjacent),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(50),
|
||||
@@ -42,6 +50,7 @@ var (
|
||||
ID: render.TheInternetID,
|
||||
LabelMajor: render.TheInternetMajor,
|
||||
Pseudo: true,
|
||||
Shape: cloud,
|
||||
Node: report.MakeNode().WithAdjacent(adjacent),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(60),
|
||||
@@ -61,6 +70,7 @@ var (
|
||||
LabelMinor: fmt.Sprintf("%s (%s)", fixture.ClientHostID, fixture.Client1PID),
|
||||
Rank: fixture.Client1Name,
|
||||
Pseudo: false,
|
||||
Shape: square,
|
||||
Node: report.MakeNode().WithAdjacent(ServerProcessID),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(10),
|
||||
@@ -73,6 +83,7 @@ var (
|
||||
LabelMinor: fmt.Sprintf("%s (%s)", fixture.ClientHostID, fixture.Client2PID),
|
||||
Rank: fixture.Client2Name,
|
||||
Pseudo: false,
|
||||
Shape: square,
|
||||
Node: report.MakeNode().WithAdjacent(ServerProcessID),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(20),
|
||||
@@ -85,6 +96,7 @@ var (
|
||||
LabelMinor: fmt.Sprintf("%s (%s)", fixture.ServerHostID, fixture.ServerPID),
|
||||
Rank: fixture.ServerName,
|
||||
Pseudo: false,
|
||||
Shape: square,
|
||||
Node: report.MakeNode(),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
IngressPacketCount: newu64(210),
|
||||
@@ -97,6 +109,7 @@ var (
|
||||
LabelMinor: fmt.Sprintf("%s (%s)", fixture.ServerHostID, fixture.NonContainerPID),
|
||||
Rank: fixture.NonContainerName,
|
||||
Pseudo: false,
|
||||
Shape: square,
|
||||
Node: report.MakeNode().WithAdjacent(render.TheInternetID),
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
},
|
||||
@@ -116,6 +129,8 @@ var (
|
||||
LabelMinor: "2 processes",
|
||||
Rank: fixture.Client1Name,
|
||||
Pseudo: false,
|
||||
Shape: square,
|
||||
Stack: true,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Process.Nodes[fixture.ClientProcess1NodeID],
|
||||
fixture.Report.Process.Nodes[fixture.ClientProcess2NodeID],
|
||||
@@ -132,6 +147,8 @@ var (
|
||||
LabelMinor: "1 process",
|
||||
Rank: fixture.ServerName,
|
||||
Pseudo: false,
|
||||
Shape: square,
|
||||
Stack: true,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Process.Nodes[fixture.ServerProcessNodeID],
|
||||
),
|
||||
@@ -147,6 +164,8 @@ var (
|
||||
LabelMinor: "1 process",
|
||||
Rank: fixture.NonContainerName,
|
||||
Pseudo: false,
|
||||
Shape: square,
|
||||
Stack: true,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Process.Nodes[fixture.NonContainerProcessNodeID],
|
||||
),
|
||||
@@ -168,6 +187,7 @@ var (
|
||||
LabelMinor: fixture.ClientHostName,
|
||||
Rank: fixture.ClientContainerImageName,
|
||||
Pseudo: false,
|
||||
Shape: hexagon,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Process.Nodes[fixture.ClientProcess1NodeID],
|
||||
fixture.Report.Process.Nodes[fixture.ClientProcess2NodeID],
|
||||
@@ -185,6 +205,7 @@ var (
|
||||
LabelMinor: fixture.ServerHostName,
|
||||
Rank: fixture.ServerContainerImageName,
|
||||
Pseudo: false,
|
||||
Shape: hexagon,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Process.Nodes[fixture.ServerProcessNodeID],
|
||||
),
|
||||
@@ -201,6 +222,8 @@ var (
|
||||
LabelMinor: fixture.ServerHostName,
|
||||
Rank: "",
|
||||
Pseudo: true,
|
||||
Shape: square,
|
||||
Stack: true,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Process.Nodes[fixture.NonContainerProcessNodeID],
|
||||
),
|
||||
@@ -220,6 +243,8 @@ var (
|
||||
LabelMinor: "1 container",
|
||||
Rank: fixture.ClientContainerImageName,
|
||||
Pseudo: false,
|
||||
Shape: hexagon,
|
||||
Stack: true,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Process.Nodes[fixture.ClientProcess1NodeID],
|
||||
fixture.Report.Process.Nodes[fixture.ClientProcess2NodeID],
|
||||
@@ -237,6 +262,8 @@ var (
|
||||
LabelMinor: "1 container",
|
||||
Rank: fixture.ServerContainerImageName,
|
||||
Pseudo: false,
|
||||
Shape: hexagon,
|
||||
Stack: true,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Process.Nodes[fixture.ServerProcessNodeID],
|
||||
fixture.Report.Container.Nodes[fixture.ServerContainerNodeID],
|
||||
@@ -253,6 +280,8 @@ var (
|
||||
LabelMinor: fixture.ServerHostName,
|
||||
Rank: "",
|
||||
Pseudo: true,
|
||||
Shape: square,
|
||||
Stack: true,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Process.Nodes[fixture.NonContainerProcessNodeID],
|
||||
),
|
||||
@@ -274,6 +303,7 @@ var (
|
||||
LabelMinor: "hostname.com", // after first .
|
||||
Rank: "hostname.com",
|
||||
Pseudo: false,
|
||||
Shape: circle,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Container.Nodes[fixture.ServerContainerNodeID],
|
||||
fixture.Report.Container.Nodes[fixture.ServerProcessNodeID],
|
||||
@@ -291,6 +321,7 @@ var (
|
||||
LabelMinor: "hostname.com", // after first .
|
||||
Rank: "hostname.com",
|
||||
Pseudo: false,
|
||||
Shape: circle,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Container.Nodes[fixture.ClientContainerNodeID],
|
||||
fixture.Report.Process.Nodes[fixture.ClientProcess1NodeID],
|
||||
@@ -307,6 +338,7 @@ var (
|
||||
ID: pseudoHostID1,
|
||||
LabelMajor: fixture.UnknownClient1IP,
|
||||
Pseudo: true,
|
||||
Shape: circle,
|
||||
Node: report.MakeNode().WithAdjacent(ServerHostRenderedID),
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
Children: report.MakeNodeSet(
|
||||
@@ -318,6 +350,7 @@ var (
|
||||
ID: pseudoHostID2,
|
||||
LabelMajor: fixture.UnknownClient3IP,
|
||||
Pseudo: true,
|
||||
Shape: circle,
|
||||
Node: report.MakeNode().WithAdjacent(ServerHostRenderedID),
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
},
|
||||
@@ -325,6 +358,7 @@ var (
|
||||
ID: render.TheInternetID,
|
||||
LabelMajor: render.TheInternetMajor,
|
||||
Pseudo: true,
|
||||
Shape: cloud,
|
||||
Node: report.MakeNode().WithAdjacent(ServerHostRenderedID),
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
},
|
||||
@@ -340,6 +374,7 @@ var (
|
||||
LabelMinor: "1 container",
|
||||
Rank: "ping/pong-a",
|
||||
Pseudo: false,
|
||||
Shape: pentagon,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Process.Nodes[fixture.ClientProcess1NodeID],
|
||||
fixture.Report.Process.Nodes[fixture.ClientProcess2NodeID],
|
||||
@@ -359,6 +394,7 @@ var (
|
||||
LabelMinor: "1 container",
|
||||
Rank: "ping/pong-b",
|
||||
Pseudo: false,
|
||||
Shape: pentagon,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Process.Nodes[fixture.ServerProcessNodeID],
|
||||
fixture.Report.Container.Nodes[fixture.ServerContainerNodeID],
|
||||
@@ -377,6 +413,8 @@ var (
|
||||
LabelMinor: fixture.ServerHostName,
|
||||
Rank: "",
|
||||
Pseudo: true,
|
||||
Shape: square,
|
||||
Stack: true,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Process.Nodes[fixture.NonContainerProcessNodeID],
|
||||
),
|
||||
@@ -387,6 +425,7 @@ var (
|
||||
ID: render.TheInternetID,
|
||||
LabelMajor: render.TheInternetMajor,
|
||||
Pseudo: true,
|
||||
Shape: cloud,
|
||||
Node: report.MakeNode().WithAdjacent(ServerPodRenderedID),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(60),
|
||||
@@ -404,6 +443,8 @@ var (
|
||||
LabelMinor: "2 pods",
|
||||
Rank: fixture.ServiceID,
|
||||
Pseudo: false,
|
||||
Shape: pentagon,
|
||||
Stack: true,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Process.Nodes[fixture.ClientProcess1NodeID],
|
||||
fixture.Report.Process.Nodes[fixture.ClientProcess2NodeID],
|
||||
@@ -429,6 +470,8 @@ var (
|
||||
LabelMinor: fixture.ServerHostName,
|
||||
Rank: "",
|
||||
Pseudo: true,
|
||||
Shape: square,
|
||||
Stack: true,
|
||||
Children: report.MakeNodeSet(
|
||||
fixture.Report.Process.Nodes[fixture.NonContainerProcessNodeID],
|
||||
),
|
||||
@@ -439,6 +482,7 @@ var (
|
||||
ID: render.TheInternetID,
|
||||
LabelMajor: render.TheInternetMajor,
|
||||
Pseudo: true,
|
||||
Shape: cloud,
|
||||
Node: report.MakeNode().WithAdjacent(ServiceRenderedID),
|
||||
EdgeMetadata: report.EdgeMetadata{
|
||||
EgressPacketCount: newu64(60),
|
||||
|
||||
+48
-15
@@ -39,6 +39,12 @@ const (
|
||||
// If the output is empty, the node shall be omitted from the rendered topology.
|
||||
type MapFunc func(RenderableNode, report.Networks) RenderableNodes
|
||||
|
||||
func theInternetNode(m RenderableNode) RenderableNode {
|
||||
r := newDerivedPseudoNode(TheInternetID, TheInternetMajor, m)
|
||||
r.Shape = Cloud
|
||||
return r
|
||||
}
|
||||
|
||||
// MapEndpointIdentity maps an endpoint topology node to a single endpoint
|
||||
// renderable node. As it is only ever run on endpoint topology nodes, we
|
||||
// expect that certain keys are present.
|
||||
@@ -64,7 +70,7 @@ func MapEndpointIdentity(m RenderableNode, local report.Networks) RenderableNode
|
||||
// If the dstNodeAddr is not in a network local to this report, we emit an
|
||||
// internet node
|
||||
if ip := net.ParseIP(addr); ip != nil && !local.Contains(ip) {
|
||||
return RenderableNodes{TheInternetID: newDerivedPseudoNode(TheInternetID, TheInternetMajor, m)}
|
||||
return RenderableNodes{TheInternetID: theInternetNode(m)}
|
||||
}
|
||||
|
||||
// We are a 'client' pseudo node if the port is in the ephemeral port range.
|
||||
@@ -117,7 +123,9 @@ func MapProcessIdentity(m RenderableNode, _ report.Networks) RenderableNodes {
|
||||
rank, _ = m.Latest.Lookup(process.Name)
|
||||
)
|
||||
|
||||
return RenderableNodes{id: NewRenderableNodeWith(id, major, minor, rank, m)}
|
||||
node := NewRenderableNodeWith(id, major, minor, rank, m)
|
||||
node.Shape = Square
|
||||
return RenderableNodes{id: node}
|
||||
}
|
||||
|
||||
// MapContainerIdentity maps a container topology node to a container
|
||||
@@ -138,6 +146,7 @@ func MapContainerIdentity(m RenderableNode, _ report.Networks) RenderableNodes {
|
||||
|
||||
node := NewRenderableNodeWith(id, major, minor, rank, m)
|
||||
node.ControlNode = m.ID
|
||||
node.Shape = Hexagon
|
||||
return RenderableNodes{id: node}
|
||||
}
|
||||
|
||||
@@ -180,7 +189,10 @@ func MapContainerImageIdentity(m RenderableNode, _ report.Networks) RenderableNo
|
||||
rank = imageID
|
||||
)
|
||||
|
||||
return RenderableNodes{id: NewRenderableNodeWith(id, major, "", rank, m)}
|
||||
node := NewRenderableNodeWith(id, major, "", rank, m)
|
||||
node.Shape = Hexagon
|
||||
node.Stack = true
|
||||
return RenderableNodes{id: node}
|
||||
}
|
||||
|
||||
// MapPodIdentity maps a pod topology node to pod renderable node. As it is
|
||||
@@ -198,7 +210,9 @@ func MapPodIdentity(m RenderableNode, _ report.Networks) RenderableNodes {
|
||||
rank, _ = m.Latest.Lookup(kubernetes.PodID)
|
||||
)
|
||||
|
||||
return RenderableNodes{id: NewRenderableNodeWith(id, major, "", rank, m)}
|
||||
node := NewRenderableNodeWith(id, major, "", rank, m)
|
||||
node.Shape = Pentagon
|
||||
return RenderableNodes{id: node}
|
||||
}
|
||||
|
||||
// MapServiceIdentity maps a service topology node to service renderable node. As it is
|
||||
@@ -216,7 +230,10 @@ func MapServiceIdentity(m RenderableNode, _ report.Networks) RenderableNodes {
|
||||
rank, _ = m.Latest.Lookup(kubernetes.ServiceID)
|
||||
)
|
||||
|
||||
return RenderableNodes{id: NewRenderableNodeWith(id, major, "", rank, m)}
|
||||
node := NewRenderableNodeWith(id, major, "", rank, m)
|
||||
node.Shape = Pentagon
|
||||
node.Stack = true
|
||||
return RenderableNodes{id: node}
|
||||
}
|
||||
|
||||
// MapAddressIdentity maps an address topology node to an address renderable
|
||||
@@ -242,7 +259,7 @@ func MapAddressIdentity(m RenderableNode, local report.Networks) RenderableNodes
|
||||
// If the addr is not in a network local to this report, we emit an
|
||||
// internet node
|
||||
if !local.Contains(net.ParseIP(addr)) {
|
||||
return RenderableNodes{TheInternetID: newDerivedPseudoNode(TheInternetID, TheInternetMajor, m)}
|
||||
return RenderableNodes{TheInternetID: theInternetNode(m)}
|
||||
}
|
||||
|
||||
// Otherwise generate a pseudo node for every
|
||||
@@ -281,7 +298,9 @@ func MapHostIdentity(m RenderableNode, _ report.Networks) RenderableNodes {
|
||||
major = hostname
|
||||
}
|
||||
|
||||
return RenderableNodes{id: NewRenderableNodeWith(id, major, minor, rank, m)}
|
||||
node := NewRenderableNodeWith(id, major, minor, rank, m)
|
||||
node.Shape = Circle
|
||||
return RenderableNodes{id: node}
|
||||
}
|
||||
|
||||
// MapEndpoint2IP maps endpoint nodes to their IP address, for joining
|
||||
@@ -299,7 +318,7 @@ func MapEndpoint2IP(m RenderableNode, local report.Networks) RenderableNodes {
|
||||
return RenderableNodes{}
|
||||
}
|
||||
if ip := net.ParseIP(addr); ip != nil && !local.Contains(ip) {
|
||||
return RenderableNodes{TheInternetID: newDerivedPseudoNode(TheInternetID, TheInternetMajor, m)}
|
||||
return RenderableNodes{TheInternetID: theInternetNode(m)}
|
||||
}
|
||||
|
||||
// We don't always know what port a container is listening on, and
|
||||
@@ -376,8 +395,9 @@ func MapIP2Container(n RenderableNode, _ report.Networks) RenderableNodes {
|
||||
}
|
||||
|
||||
id := MakeContainerID(containerID)
|
||||
|
||||
return RenderableNodes{id: NewDerivedNode(id, n.WithParents(report.EmptySets))}
|
||||
node := NewDerivedNode(id, n.WithParents(report.EmptySets))
|
||||
node.Shape = Hexagon
|
||||
return RenderableNodes{id: node}
|
||||
}
|
||||
|
||||
// MapEndpoint2Process maps endpoint RenderableNodes to process
|
||||
@@ -402,7 +422,9 @@ func MapEndpoint2Process(n RenderableNode, _ report.Networks) RenderableNodes {
|
||||
}
|
||||
|
||||
id := MakeProcessID(report.ExtractHostID(n.Node), pid)
|
||||
return RenderableNodes{id: NewDerivedNode(id, n.WithParents(report.EmptySets))}
|
||||
node := NewDerivedNode(id, n.WithParents(report.EmptySets))
|
||||
node.Shape = Square
|
||||
return RenderableNodes{id: node}
|
||||
}
|
||||
|
||||
// MapProcess2Container maps process RenderableNodes to container
|
||||
@@ -440,12 +462,15 @@ func MapProcess2Container(n RenderableNode, _ report.Networks) RenderableNodes {
|
||||
if containerID, ok := n.Node.Latest.Lookup(docker.ContainerID); ok {
|
||||
id = MakeContainerID(containerID)
|
||||
node = NewDerivedNode(id, n)
|
||||
node.Shape = Hexagon
|
||||
} else {
|
||||
nCopy := n.Copy()
|
||||
nCopy.Node = nCopy.Node.WithID("").WithTopology("") // Wipe the ID so it cannot be rendered.
|
||||
id = MakePseudoNodeID(UncontainedID, hostID)
|
||||
node = newDerivedPseudoNode(id, UncontainedMajor, nCopy)
|
||||
node.LabelMinor = hostID
|
||||
node.Shape = Square
|
||||
node.Stack = true
|
||||
}
|
||||
|
||||
node.Children = node.Children.Add(n.Node)
|
||||
@@ -475,6 +500,8 @@ func MapProcess2Name(n RenderableNode, _ report.Networks) RenderableNodes {
|
||||
node.Node.Topology = "process_name"
|
||||
node.Node.ID = name
|
||||
node.Children = node.Children.Add(n.Node)
|
||||
node.Shape = Square
|
||||
node.Stack = true
|
||||
return RenderableNodes{name: node}
|
||||
}
|
||||
|
||||
@@ -529,6 +556,8 @@ func MapContainer2ContainerImage(n RenderableNode, _ report.Networks) Renderable
|
||||
|
||||
result.Node.Topology = "container_image"
|
||||
result.Node.ID = report.MakeContainerImageNodeID(imageID)
|
||||
result.Shape = Hexagon
|
||||
result.Stack = true
|
||||
return RenderableNodes{id: result}
|
||||
}
|
||||
|
||||
@@ -561,6 +590,8 @@ func MapPod2Service(n RenderableNode, _ report.Networks) RenderableNodes {
|
||||
n := NewDerivedNode(id, n.WithParents(report.EmptySets))
|
||||
n.Node.Counters = n.Node.Counters.Add(podsKey, 1)
|
||||
n.Children = n.Children.Add(n.Node)
|
||||
n.Shape = Pentagon
|
||||
n.Stack = true
|
||||
result[id] = n
|
||||
}
|
||||
return result
|
||||
@@ -600,6 +631,8 @@ func MapContainerImage2Name(n RenderableNode, _ report.Networks) RenderableNodes
|
||||
node.LabelMajor = name
|
||||
node.Rank = name
|
||||
node.Node = n.Node.Copy() // Propagate NMD for container counting.
|
||||
node.Shape = Hexagon
|
||||
node.Stack = true
|
||||
return RenderableNodes{id: node}
|
||||
}
|
||||
|
||||
@@ -624,6 +657,7 @@ func MapX2Host(n RenderableNode, _ report.Networks) RenderableNodes {
|
||||
id := MakeHostID(report.ExtractHostID(n.Node))
|
||||
result := NewDerivedNode(id, n.WithParents(report.EmptySets))
|
||||
result.Children = result.Children.Add(n.Node)
|
||||
result.Shape = Circle
|
||||
return RenderableNodes{id: result}
|
||||
}
|
||||
|
||||
@@ -669,7 +703,7 @@ func MapContainer2Pod(n RenderableNode, _ report.Networks) RenderableNodes {
|
||||
}
|
||||
|
||||
result.Children = result.Children.Add(n.Node)
|
||||
|
||||
result.Shape = Pentagon
|
||||
return RenderableNodes{id: result}
|
||||
}
|
||||
|
||||
@@ -693,12 +727,11 @@ func MapContainer2Hostname(n RenderableNode, _ report.Networks) RenderableNodes
|
||||
|
||||
// Add container id key to the counters, which will later be counted to produce the minor label
|
||||
result.Counters = result.Counters.Add(ContainersKey, 1)
|
||||
|
||||
result.Node.Topology = "container_hostname"
|
||||
result.Node.ID = id
|
||||
|
||||
result.Children = result.Children.Add(n.Node)
|
||||
|
||||
result.Shape = Hexagon
|
||||
result.Stack = true
|
||||
return RenderableNodes{id: result}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,11 +15,22 @@ type RenderableNode struct {
|
||||
Pseudo bool `json:"pseudo,omitempty"` // sort-of a placeholder node, for rendering purposes
|
||||
Children report.NodeSet `json:"children,omitempty"` // Nodes which have been grouped into this one
|
||||
ControlNode string `json:"-"` // ID of node from which to show the controls in the UI
|
||||
Shape string `json:"shape"` // Shape node should be rendered as
|
||||
Stack bool `json:"stack"` // Should UI render this node as a stack?
|
||||
|
||||
report.EdgeMetadata `json:"metadata"` // Numeric sums
|
||||
report.Node
|
||||
}
|
||||
|
||||
// Shapes that are allowed
|
||||
const (
|
||||
Circle = "circle"
|
||||
Square = "square"
|
||||
Pentagon = "pentagon"
|
||||
Hexagon = "hexagon"
|
||||
Cloud = "cloud"
|
||||
)
|
||||
|
||||
// NewRenderableNode makes a new RenderableNode
|
||||
func NewRenderableNode(id string) RenderableNode {
|
||||
return RenderableNode{
|
||||
@@ -30,6 +41,7 @@ func NewRenderableNode(id string) RenderableNode {
|
||||
Pseudo: false,
|
||||
EdgeMetadata: report.EdgeMetadata{},
|
||||
Node: report.MakeNode(),
|
||||
Shape: Circle,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +56,7 @@ func NewRenderableNodeWith(id, major, minor, rank string, node RenderableNode) R
|
||||
Children: node.Children.Copy(),
|
||||
EdgeMetadata: node.EdgeMetadata.Copy(),
|
||||
Node: node.Node.Copy(),
|
||||
Shape: Circle,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +72,7 @@ func NewDerivedNode(id string, node RenderableNode) RenderableNode {
|
||||
EdgeMetadata: node.EdgeMetadata.Copy(),
|
||||
Node: node.Node.Copy(),
|
||||
ControlNode: "", // Do not propagate ControlNode when making a derived node!
|
||||
Shape: Circle,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +86,7 @@ func newDerivedPseudoNode(id, major string, node RenderableNode) RenderableNode
|
||||
Children: node.Children.Copy(),
|
||||
EdgeMetadata: node.EdgeMetadata.Copy(),
|
||||
Node: node.Node.Copy(),
|
||||
Shape: Circle,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,6 +128,7 @@ func (rn RenderableNode) Merge(other RenderableNode) RenderableNode {
|
||||
panic(result.ID)
|
||||
}
|
||||
|
||||
result.Stack = result.Stack || rn.Stack
|
||||
result.Children = rn.Children.Merge(other.Children)
|
||||
result.EdgeMetadata = rn.EdgeMetadata.Merge(other.EdgeMetadata)
|
||||
result.Node = rn.Node.Merge(other.Node)
|
||||
@@ -132,6 +148,8 @@ func (rn RenderableNode) Copy() RenderableNode {
|
||||
EdgeMetadata: rn.EdgeMetadata.Copy(),
|
||||
Node: rn.Node.Copy(),
|
||||
ControlNode: rn.ControlNode,
|
||||
Shape: rn.Shape,
|
||||
Stack: rn.Stack,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -74,6 +74,7 @@ var (
|
||||
ID: render.TheInternetID,
|
||||
LabelMajor: render.TheInternetMajor,
|
||||
Pseudo: true,
|
||||
Shape: "cloud",
|
||||
Node: report.MakeNode().WithAdjacent(render.MakeContainerID(containerID)),
|
||||
},
|
||||
render.MakeContainerID(containerID): {
|
||||
@@ -82,6 +83,7 @@ var (
|
||||
LabelMinor: serverHostID,
|
||||
Rank: "",
|
||||
Pseudo: false,
|
||||
Shape: "hexagon",
|
||||
Node: report.MakeNode(),
|
||||
ControlNode: containerNodeID,
|
||||
},
|
||||
|
||||
+1
-1
@@ -148,7 +148,7 @@ function lint_files {
|
||||
|
||||
function list_files {
|
||||
if [ $# -gt 0 ]; then
|
||||
find "$@" -type f | grep -vE '(^\./\.git|^\./\.pkg|/vendor/)'
|
||||
find "$@" -type f | grep -vE '(^\./\.git|^\./\.pkg|/vendor/|/client/node_modules/)'
|
||||
else
|
||||
git diff --cached --name-only
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user