From a34d9c97b8100b36ddb24dbf75a715374f9919ec Mon Sep 17 00:00:00 2001 From: Simon Howe Date: Thu, 14 Jan 2016 19:56:22 +0100 Subject: [PATCH 01/10] Adds node-shapes to the canvas - circle - rect (w/ radius) - fluffy cloud shape - hexagons - stacks --- .../app/scripts/charts/node-shape-circle.js | 14 ++++++ client/app/scripts/charts/node-shape-cloud.js | 41 ++++++++++++++++ client/app/scripts/charts/node-shape-hex.js | 44 +++++++++++++++++ .../charts/node-shape-rounded-square.js | 10 ++++ .../app/scripts/charts/node-shape-square.js | 24 ++++++++++ client/app/scripts/charts/node-shape-stack.js | 19 ++++++++ client/app/scripts/charts/node.js | 47 +++++++++++++++++-- client/app/scripts/charts/nodes-chart.js | 1 + client/app/styles/main.less | 41 +++++++++------- 9 files changed, 219 insertions(+), 22 deletions(-) create mode 100644 client/app/scripts/charts/node-shape-circle.js create mode 100644 client/app/scripts/charts/node-shape-cloud.js create mode 100644 client/app/scripts/charts/node-shape-hex.js create mode 100644 client/app/scripts/charts/node-shape-rounded-square.js create mode 100644 client/app/scripts/charts/node-shape-square.js create mode 100644 client/app/scripts/charts/node-shape-stack.js diff --git a/client/app/scripts/charts/node-shape-circle.js b/client/app/scripts/charts/node-shape-circle.js new file mode 100644 index 000000000..0d4f53839 --- /dev/null +++ b/client/app/scripts/charts/node-shape-circle.js @@ -0,0 +1,14 @@ +import React from 'react'; + +export default function NodeShapeCircle({highlighted, size, color}) { + return ( + + {highlighted && + } + + + + + + ); +} diff --git a/client/app/scripts/charts/node-shape-cloud.js b/client/app/scripts/charts/node-shape-cloud.js new file mode 100644 index 000000000..770fc588b --- /dev/null +++ b/client/app/scripts/charts/node-shape-cloud.js @@ -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 ( + + {highlighted && + } + + + + + ); +} diff --git a/client/app/scripts/charts/node-shape-hex.js b/client/app/scripts/charts/node-shape-hex.js new file mode 100644 index 000000000..5878544ed --- /dev/null +++ b/client/app/scripts/charts/node-shape-hex.js @@ -0,0 +1,44 @@ +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({highlighted, size, color}) { + const pathProps = (v) => { + return { + d: getPoints(size * v * 2), + transform: `rotate(90) translate(-${size * getWidth(v)}, -${size * v})` + }; + }; + + return ( + + {highlighted && + } + + + + + ); +} diff --git a/client/app/scripts/charts/node-shape-rounded-square.js b/client/app/scripts/charts/node-shape-rounded-square.js new file mode 100644 index 000000000..4542595b7 --- /dev/null +++ b/client/app/scripts/charts/node-shape-rounded-square.js @@ -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 ( + + ); +} diff --git a/client/app/scripts/charts/node-shape-square.js b/client/app/scripts/charts/node-shape-square.js new file mode 100644 index 000000000..286fa176d --- /dev/null +++ b/client/app/scripts/charts/node-shape-square.js @@ -0,0 +1,24 @@ +import React from 'react'; + +export default function NodeShapeSquare({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})` + }; + }; + + return ( + + {highlighted && + } + + + + + + ); +} diff --git a/client/app/scripts/charts/node-shape-stack.js b/client/app/scripts/charts/node-shape-stack.js new file mode 100644 index 000000000..031fa50cb --- /dev/null +++ b/client/app/scripts/charts/node-shape-stack.js @@ -0,0 +1,19 @@ +import React from 'react'; +import _ from 'lodash'; + +export default function NodeShapeCircleStack(props) { + const propsNoHighlight = _.clone(props); + const Shape = props.shape; + delete propsNoHighlight.highlighted; + return ( + + + + + + + + + + ); +} diff --git a/client/app/scripts/charts/node.js b/client/app/scripts/charts/node.js index f78f9f4ab..b1c1b7940 100644 --- a/client/app/scripts/charts/node.js +++ b/client/app/scripts/charts/node.js @@ -5,6 +5,42 @@ 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 = { + 'hosts': NodeShapeCircle, + 'containers': NodeShapeHex, + 'containers-by-hostname': stackedShape(NodeShapeHex), + 'containers-by-image': stackedShape(NodeShapeHex), + 'applications': NodeShapeRoundedSquare, + 'applications-by-name': stackedShape(NodeShapeRoundedSquare) +}; + +function isTheInternet(id) { + return id === 'theinternet'; +} + +function getNodeShape({id, pseudo, topologyId}) { + if (isTheInternet(id)) { + return NodeShapeCloud; + } else if (pseudo) { + return NodeShapeCircle; + } + return nodeShapes[topologyId]; +} + export default class Node extends React.Component { constructor(props, context) { super(props, context); @@ -52,8 +88,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 ( - {props.highlighted && } - - - + {label} diff --git a/client/app/scripts/charts/nodes-chart.js b/client/app/scripts/charts/nodes-chart.js index adb804830..e61c44fcc 100644 --- a/client/app/scripts/charts/nodes-chart.js +++ b/client/app/scripts/charts/nodes-chart.js @@ -142,6 +142,7 @@ export default class NodesChart extends React.Component { blurred={node.get('blurred')} focused={node.get('focused')} highlighted={node.get('highlighted')} + topologyId={this.props.topologyId} onClick={onNodeClick} key={node.get('id')} id={node.get('id')} diff --git a/client/app/styles/main.less b/client/app/styles/main.less index bafac4127..1391d1ab7 100644 --- a/client/app/styles/main.less +++ b/client/app/styles/main.less @@ -303,6 +303,10 @@ h2 { } } + g.stack g.shape .border { + stroke-width: 2px; + } + 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 { From 3f7f55e87e4ab29e5b0167b4bda83bcfda8dd333 Mon Sep 17 00:00:00 2001 From: Tom Wilkie Date: Mon, 1 Feb 2016 17:12:17 -0800 Subject: [PATCH 02/10] Backend for node shapes --- render/expected/expected.go | 44 ++++++++++++++++++ render/mapping.go | 63 ++++++++++++++++++++------ render/renderable_node.go | 18 ++++++++ render/short_lived_connections_test.go | 2 + 4 files changed, 112 insertions(+), 15 deletions(-) diff --git a/render/expected/expected.go b/render/expected/expected.go index b4946d7a9..b06079e7b 100644 --- a/render/expected/expected.go +++ b/render/expected/expected.go @@ -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), diff --git a/render/mapping.go b/render/mapping.go index a94f95069..be6526634 100644 --- a/render/mapping.go +++ b/render/mapping.go @@ -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} } diff --git a/render/renderable_node.go b/render/renderable_node.go index e79657137..c35357cd8 100644 --- a/render/renderable_node.go +++ b/render/renderable_node.go @@ -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, } } diff --git a/render/short_lived_connections_test.go b/render/short_lived_connections_test.go index 1ac2f08fc..e3efd476b 100644 --- a/render/short_lived_connections_test.go +++ b/render/short_lived_connections_test.go @@ -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, }, From 476b7917e43a241389770b8716e9ae877d7b592e Mon Sep 17 00:00:00 2001 From: David Kaltschmidt Date: Fri, 5 Feb 2016 15:07:50 +0100 Subject: [PATCH 03/10] Let shape be determined by backend --- client/app/scripts/charts/node.js | 25 +++++++++--------------- client/app/scripts/charts/nodes-chart.js | 4 ++++ client/app/scripts/stores/app-store.js | 2 ++ 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/client/app/scripts/charts/node.js b/client/app/scripts/charts/node.js index b1c1b7940..13c24b402 100644 --- a/client/app/scripts/charts/node.js +++ b/client/app/scripts/charts/node.js @@ -20,25 +20,18 @@ function stackedShape(Shape) { } const nodeShapes = { - 'hosts': NodeShapeCircle, - 'containers': NodeShapeHex, - 'containers-by-hostname': stackedShape(NodeShapeHex), - 'containers-by-image': stackedShape(NodeShapeHex), - 'applications': NodeShapeRoundedSquare, - 'applications-by-name': stackedShape(NodeShapeRoundedSquare) + 'circle': NodeShapeCircle, + 'hexagon': NodeShapeHex, + 'square': NodeShapeRoundedSquare, + 'cloud': NodeShapeCloud }; -function isTheInternet(id) { - return id === 'theinternet'; -} - -function getNodeShape({id, pseudo, topologyId}) { - if (isTheInternet(id)) { - return NodeShapeCloud; - } else if (pseudo) { - return NodeShapeCircle; +function getNodeShape({shape, stack}) { + const nodeShape = nodeShapes[shape]; + if (!nodeShape) { + throw new Error(`Unkown shape: ${shape}!`); } - return nodeShapes[topologyId]; + return stack ? stackedShape(nodeShape) : nodeShape; } export default class Node extends React.Component { diff --git a/client/app/scripts/charts/nodes-chart.js b/client/app/scripts/charts/nodes-chart.js index e61c44fcc..7e40cff90 100644 --- a/client/app/scripts/charts/nodes-chart.js +++ b/client/app/scripts/charts/nodes-chart.js @@ -143,6 +143,8 @@ export default class NodesChart extends React.Component { 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')} @@ -245,6 +247,8 @@ export default class NodesChart extends React.Component { pseudo: node.get('pseudo'), subLabel: node.get('label_minor'), rank: node.get('rank'), + shape: node.get('shape'), + stack: node.get('stack'), x: 0, y: 0 }); diff --git a/client/app/scripts/stores/app-store.js b/client/app/scripts/stores/app-store.js index 58c261b66..6471ebccc 100644 --- a/client/app/scripts/stores/app-store.js +++ b/client/app/scripts/stores/app-store.js @@ -41,6 +41,8 @@ function makeNode(node) { label_minor: node.label_minor, rank: node.rank, pseudo: node.pseudo, + stack: node.stack, + shape: node.shape, adjacency: node.adjacency }; } From d3283555b93d6398eb9e05cc84896792eacce9fa Mon Sep 17 00:00:00 2001 From: Simon Howe Date: Wed, 10 Feb 2016 18:56:53 +0700 Subject: [PATCH 04/10] Testing out more shapes. --- client/app/scripts/charts/node-shape-cog.js | 30 +++++++++++++++++++ .../charts/node-shape-rounded-square.js | 2 +- client/app/scripts/charts/node.js | 7 +++-- 3 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 client/app/scripts/charts/node-shape-cog.js diff --git a/client/app/scripts/charts/node-shape-cog.js b/client/app/scripts/charts/node-shape-cog.js new file mode 100644 index 000000000..a9b08216e --- /dev/null +++ b/client/app/scripts/charts/node-shape-cog.js @@ -0,0 +1,30 @@ +import React from 'react'; + +const COG_PATH = 'M61.321,26.585l-5.292-1.769c-0.645-2.729-1.718-5.295-3.15-7.615l2.486-4.989c-1.706-2.251-3.717-4.26-5.968-5.968l-4.989,2.488c-2.321-1.432-4.885-2.508-7.615-3.152l-1.767-5.289C33.643,0.102,32.235,0,30.806,0c-1.435,0-2.84,0.102-4.221,0.291L24.816,5.58c-2.731,0.645-5.297,1.721-7.618,3.152l-4.986-2.488c-2.251,1.708-4.263,3.717-5.968,5.968l2.485,4.989c-1.432,2.32-2.505,4.887-3.152,7.615l-5.286,1.769C0.099,27.964,0,29.371,0,30.806c0,1.433,0.099,2.84,0.291,4.219l5.286,1.771c0.647,2.729,1.721,5.295,3.152,7.616l-2.485,4.986c1.705,2.251,3.717,4.263,5.968,5.97l4.986-2.488c2.321,1.435,4.887,2.508,7.618,3.152l1.766,5.289c1.384,0.189,2.789,0.289,4.222,0.289c1.432,0,2.839-0.1,4.221-0.289l1.769-5.289c2.73-0.645,5.294-1.718,7.615-3.152l4.986,2.488c2.254-1.707,4.265-3.719,5.971-5.97l-2.488-4.986c1.435-2.321,2.508-4.888,3.152-7.616l5.289-1.771c0.191-1.379,0.291-2.786,0.291-4.219C61.609,29.371,61.51,27.964,61.321,26.585z'; + +export default function NodeShapeCog({highlighted, size, color}) { + const width = -61.609; + const height = -61.609; + const cx = Math.abs(width) / 2; + const cy = Math.abs(height) / 2; + const pathSize = Math.abs((width + height) / 2); + const baseScale = (size * 2.1) / pathSize; + + const pathProps = (v) => { + return { + d: COG_PATH, + transform: `scale(-${v * baseScale}) translate(-${cx},-${cy})`, + style: {strokeWidth: 3 / baseScale} + }; + }; + + return ( + + {highlighted && + } + + + + + ); +} diff --git a/client/app/scripts/charts/node-shape-rounded-square.js b/client/app/scripts/charts/node-shape-rounded-square.js index 4542595b7..6064ec5f2 100644 --- a/client/app/scripts/charts/node-shape-rounded-square.js +++ b/client/app/scripts/charts/node-shape-rounded-square.js @@ -5,6 +5,6 @@ import NodeShapeSquare from './node-shape-square'; export default function NodeShapeRoundedSquare(props) { return ( - + ); } diff --git a/client/app/scripts/charts/node.js b/client/app/scripts/charts/node.js index 13c24b402..45fefed7f 100644 --- a/client/app/scripts/charts/node.js +++ b/client/app/scripts/charts/node.js @@ -8,7 +8,8 @@ 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 NodeShapeCog from './node-shape-cog'; +// import NodeShapeHex from './node-shape-hex'; import NodeShapeCloud from './node-shape-cloud'; function stackedShape(Shape) { @@ -21,8 +22,8 @@ function stackedShape(Shape) { const nodeShapes = { 'circle': NodeShapeCircle, - 'hexagon': NodeShapeHex, - 'square': NodeShapeRoundedSquare, + 'hexagon': NodeShapeRoundedSquare, + 'square': NodeShapeCog, 'cloud': NodeShapeCloud }; From 54988c1101bf2872b5dee419fcdcc15f1ee787a4 Mon Sep 17 00:00:00 2001 From: Simon Howe Date: Thu, 11 Feb 2016 17:59:07 +0700 Subject: [PATCH 05/10] Revert "Testing out more shapes." This reverts commit 9a2504119def6b9afb1ac5897655793a84754152. --- client/app/scripts/charts/node-shape-cog.js | 30 ------------------- .../charts/node-shape-rounded-square.js | 2 +- client/app/scripts/charts/node.js | 7 ++--- 3 files changed, 4 insertions(+), 35 deletions(-) delete mode 100644 client/app/scripts/charts/node-shape-cog.js diff --git a/client/app/scripts/charts/node-shape-cog.js b/client/app/scripts/charts/node-shape-cog.js deleted file mode 100644 index a9b08216e..000000000 --- a/client/app/scripts/charts/node-shape-cog.js +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react'; - -const COG_PATH = 'M61.321,26.585l-5.292-1.769c-0.645-2.729-1.718-5.295-3.15-7.615l2.486-4.989c-1.706-2.251-3.717-4.26-5.968-5.968l-4.989,2.488c-2.321-1.432-4.885-2.508-7.615-3.152l-1.767-5.289C33.643,0.102,32.235,0,30.806,0c-1.435,0-2.84,0.102-4.221,0.291L24.816,5.58c-2.731,0.645-5.297,1.721-7.618,3.152l-4.986-2.488c-2.251,1.708-4.263,3.717-5.968,5.968l2.485,4.989c-1.432,2.32-2.505,4.887-3.152,7.615l-5.286,1.769C0.099,27.964,0,29.371,0,30.806c0,1.433,0.099,2.84,0.291,4.219l5.286,1.771c0.647,2.729,1.721,5.295,3.152,7.616l-2.485,4.986c1.705,2.251,3.717,4.263,5.968,5.97l4.986-2.488c2.321,1.435,4.887,2.508,7.618,3.152l1.766,5.289c1.384,0.189,2.789,0.289,4.222,0.289c1.432,0,2.839-0.1,4.221-0.289l1.769-5.289c2.73-0.645,5.294-1.718,7.615-3.152l4.986,2.488c2.254-1.707,4.265-3.719,5.971-5.97l-2.488-4.986c1.435-2.321,2.508-4.888,3.152-7.616l5.289-1.771c0.191-1.379,0.291-2.786,0.291-4.219C61.609,29.371,61.51,27.964,61.321,26.585z'; - -export default function NodeShapeCog({highlighted, size, color}) { - const width = -61.609; - const height = -61.609; - const cx = Math.abs(width) / 2; - const cy = Math.abs(height) / 2; - const pathSize = Math.abs((width + height) / 2); - const baseScale = (size * 2.1) / pathSize; - - const pathProps = (v) => { - return { - d: COG_PATH, - transform: `scale(-${v * baseScale}) translate(-${cx},-${cy})`, - style: {strokeWidth: 3 / baseScale} - }; - }; - - return ( - - {highlighted && - } - - - - - ); -} diff --git a/client/app/scripts/charts/node-shape-rounded-square.js b/client/app/scripts/charts/node-shape-rounded-square.js index 6064ec5f2..4542595b7 100644 --- a/client/app/scripts/charts/node-shape-rounded-square.js +++ b/client/app/scripts/charts/node-shape-rounded-square.js @@ -5,6 +5,6 @@ import NodeShapeSquare from './node-shape-square'; export default function NodeShapeRoundedSquare(props) { return ( - + ); } diff --git a/client/app/scripts/charts/node.js b/client/app/scripts/charts/node.js index 45fefed7f..13c24b402 100644 --- a/client/app/scripts/charts/node.js +++ b/client/app/scripts/charts/node.js @@ -8,8 +8,7 @@ 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 NodeShapeCog from './node-shape-cog'; -// import NodeShapeHex from './node-shape-hex'; +import NodeShapeHex from './node-shape-hex'; import NodeShapeCloud from './node-shape-cloud'; function stackedShape(Shape) { @@ -22,8 +21,8 @@ function stackedShape(Shape) { const nodeShapes = { 'circle': NodeShapeCircle, - 'hexagon': NodeShapeRoundedSquare, - 'square': NodeShapeCog, + 'hexagon': NodeShapeHex, + 'square': NodeShapeRoundedSquare, 'cloud': NodeShapeCloud }; From 24d06d534f0fdfa98d16ef831b74bbd079c5bc96 Mon Sep 17 00:00:00 2001 From: Simon Howe Date: Thu, 11 Feb 2016 21:46:09 +0700 Subject: [PATCH 06/10] Gentle perspective in the nodestacks. - They get a little bit smaller as they go down. - Small tweak for shape stacks + high contrast mode. --- client/app/scripts/charts/node-shape-hex.js | 15 +++++++++++--- .../app/scripts/charts/node-shape-square.js | 16 +++++++++++---- client/app/scripts/charts/node-shape-stack.js | 20 ++++++++++++++----- client/app/styles/main.less | 2 +- 4 files changed, 40 insertions(+), 13 deletions(-) diff --git a/client/app/scripts/charts/node-shape-hex.js b/client/app/scripts/charts/node-shape-hex.js index 5878544ed..71b351b10 100644 --- a/client/app/scripts/charts/node-shape-hex.js +++ b/client/app/scripts/charts/node-shape-hex.js @@ -24,7 +24,7 @@ function getPoints(h) { } -export default function NodeShapeHex({highlighted, size, color}) { +export default function NodeShapeHex({onlyHighlight, highlighted, size, color}) { const pathProps = (v) => { return { d: getPoints(size * v * 2), @@ -32,10 +32,19 @@ export default function NodeShapeHex({highlighted, size, color}) { }; }; + const hightlightNode = ; + + if (onlyHighlight) { + return ( + + {highlighted && hightlightNode} + + ); + } + return ( - {highlighted && - } + {highlighted && hightlightNode} diff --git a/client/app/scripts/charts/node-shape-square.js b/client/app/scripts/charts/node-shape-square.js index 286fa176d..ea1c1507e 100644 --- a/client/app/scripts/charts/node-shape-square.js +++ b/client/app/scripts/charts/node-shape-square.js @@ -1,6 +1,6 @@ import React from 'react'; -export default function NodeShapeSquare({highlighted, size, color, rx = 0, ry = 0}) { +export default function NodeShapeSquare({onlyHighlight, highlighted, size, color, rx = 0, ry = 0}) { const rectProps = (v) => { return { width: v * size * 2, @@ -11,11 +11,19 @@ export default function NodeShapeSquare({highlighted, size, color, rx = 0, ry = }; }; + const hightlightNode = ; + + if (onlyHighlight) { + return ( + + {highlighted && hightlightNode} + + ); + } + return ( - {highlighted && - } - + {highlighted && hightlightNode} diff --git a/client/app/scripts/charts/node-shape-stack.js b/client/app/scripts/charts/node-shape-stack.js index 031fa50cb..92df91031 100644 --- a/client/app/scripts/charts/node-shape-stack.js +++ b/client/app/scripts/charts/node-shape-stack.js @@ -1,19 +1,29 @@ import React from 'react'; import _ from 'lodash'; -export default function NodeShapeCircleStack(props) { +export default function NodeShapeStack(props) { const propsNoHighlight = _.clone(props); const Shape = props.shape; delete propsNoHighlight.highlighted; + const propsOnlyHighlight = Object.assign({}, props, {onlyHighlight: true}); + const [dx, dy] = [0, 6]; + const ds = 0.075; + const dsx = (props.size * 2 + dx) / (props.size * 2); + const dsy = (props.size * 2 + dy) / (props.size * 2); + const hls = [dsx, dsy]; + return ( - - + + - - + + + + + ); } diff --git a/client/app/styles/main.less b/client/app/styles/main.less index 1391d1ab7..62dffa8b9 100644 --- a/client/app/styles/main.less +++ b/client/app/styles/main.less @@ -304,7 +304,7 @@ h2 { } g.stack g.shape .border { - stroke-width: 2px; + stroke-width: @node-border-stroke-width - 1; } g.node { From 2c626776a19141e3ffd68ddf7b8ce54c93c84fea Mon Sep 17 00:00:00 2001 From: Simon Howe Date: Tue, 16 Feb 2016 11:30:00 +0100 Subject: [PATCH 07/10] Grey out bottom of stack when there aren't 3 nodes in the stack E.g. greying out the bottom 2/3 visual nodes if the stack actually only has a single node. - Server returns "node_count" for agg nodes. --- .../app/scripts/charts/node-shape-circle.js | 22 +++++++++---- client/app/scripts/charts/node-shape-stack.js | 22 +++++++++---- client/app/scripts/charts/nodes-chart.js | 2 ++ .../app/scripts/components/debug-toolbar.js | 32 +++++++++++++++++-- client/app/scripts/stores/app-store.js | 1 + render/expected/expected.go | 8 +++++ render/mapping.go | 3 ++ render/renderable_node.go | 10 ++++++ tools/lint | 2 +- 9 files changed, 86 insertions(+), 16 deletions(-) diff --git a/client/app/scripts/charts/node-shape-circle.js b/client/app/scripts/charts/node-shape-circle.js index 0d4f53839..e4c32ade8 100644 --- a/client/app/scripts/charts/node-shape-circle.js +++ b/client/app/scripts/charts/node-shape-circle.js @@ -1,14 +1,22 @@ import React from 'react'; -export default function NodeShapeCircle({highlighted, size, color}) { +export default function NodeShapeCircle({onlyHighlight, highlighted, size, color}) { + const hightlightNode = ; + + if (onlyHighlight) { + return ( + + {highlighted && hightlightNode} + + ); + } + return ( - {highlighted && - } - - - - + {highlighted && hightlightNode} + + + ); } diff --git a/client/app/scripts/charts/node-shape-stack.js b/client/app/scripts/charts/node-shape-stack.js index 92df91031..9d13f87c8 100644 --- a/client/app/scripts/charts/node-shape-stack.js +++ b/client/app/scripts/charts/node-shape-stack.js @@ -1,29 +1,39 @@ 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 = _.clone(props); - const Shape = props.shape; - delete propsNoHighlight.highlighted; + const propsNoHighlight = dissoc(props, 'highlighted'); const propsOnlyHighlight = Object.assign({}, props, {onlyHighlight: true}); + + const Shape = props.shape; + const nodeCount = props.nodeCount; const [dx, dy] = [0, 6]; const ds = 0.075; const dsx = (props.size * 2 + dx) / (props.size * 2); const dsy = (props.size * 2 + dy) / (props.size * 2); const hls = [dsx, dsy]; + const propsWithGrey = Object.assign({}, propsNoHighlight, {color: '#aaa', className: 'mock'}); + const propsForIndex = i => (nodeCount < i ? propsWithGrey : propsNoHighlight); + return ( - + - + - + ); } diff --git a/client/app/scripts/charts/nodes-chart.js b/client/app/scripts/charts/nodes-chart.js index 7e40cff90..91030f316 100644 --- a/client/app/scripts/charts/nodes-chart.js +++ b/client/app/scripts/charts/nodes-chart.js @@ -150,6 +150,7 @@ export default class NodesChart extends React.Component { 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} @@ -246,6 +247,7 @@ 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'), diff --git a/client/app/scripts/components/debug-toolbar.js b/client/app/scripts/components/debug-toolbar.js index 1d11e62fe..8c5e139f2 100644 --- a/client/app/scripts/components/debug-toolbar.js +++ b/client/app/scripts/components/debug-toolbar.js @@ -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,23 @@ 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); + }); + }); + })); + + console.log(newNodes); + + receiveNodesDelta({ + add: newNodes + }); +} + function addNodes(n) { const ns = AppStore.getNodes(); const nodeNames = ns.keySeq().toJS(); @@ -33,7 +56,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 +92,7 @@ export class DebugToolbar extends React.Component { + ); } diff --git a/client/app/scripts/stores/app-store.js b/client/app/scripts/stores/app-store.js index 6471ebccc..fa6b4036d 100644 --- a/client/app/scripts/stores/app-store.js +++ b/client/app/scripts/stores/app-store.js @@ -39,6 +39,7 @@ 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, diff --git a/render/expected/expected.go b/render/expected/expected.go index b06079e7b..da27bcfeb 100644 --- a/render/expected/expected.go +++ b/render/expected/expected.go @@ -131,6 +131,7 @@ var ( Pseudo: false, Shape: square, Stack: true, + NodeCount: 2, Children: report.MakeNodeSet( fixture.Report.Process.Nodes[fixture.ClientProcess1NodeID], fixture.Report.Process.Nodes[fixture.ClientProcess2NodeID], @@ -149,6 +150,7 @@ var ( Pseudo: false, Shape: square, Stack: true, + NodeCount: 1, Children: report.MakeNodeSet( fixture.Report.Process.Nodes[fixture.ServerProcessNodeID], ), @@ -166,6 +168,7 @@ var ( Pseudo: false, Shape: square, Stack: true, + NodeCount: 1, Children: report.MakeNodeSet( fixture.Report.Process.Nodes[fixture.NonContainerProcessNodeID], ), @@ -245,6 +248,7 @@ var ( Pseudo: false, Shape: hexagon, Stack: true, + NodeCount: 1, Children: report.MakeNodeSet( fixture.Report.Process.Nodes[fixture.ClientProcess1NodeID], fixture.Report.Process.Nodes[fixture.ClientProcess2NodeID], @@ -264,6 +268,7 @@ var ( Pseudo: false, Shape: hexagon, Stack: true, + NodeCount: 1, Children: report.MakeNodeSet( fixture.Report.Process.Nodes[fixture.ServerProcessNodeID], fixture.Report.Container.Nodes[fixture.ServerContainerNodeID], @@ -375,6 +380,7 @@ var ( Rank: "ping/pong-a", Pseudo: false, Shape: pentagon, + NodeCount: 1, Children: report.MakeNodeSet( fixture.Report.Process.Nodes[fixture.ClientProcess1NodeID], fixture.Report.Process.Nodes[fixture.ClientProcess2NodeID], @@ -395,6 +401,7 @@ var ( Rank: "ping/pong-b", Pseudo: false, Shape: pentagon, + NodeCount: 1, Children: report.MakeNodeSet( fixture.Report.Process.Nodes[fixture.ServerProcessNodeID], fixture.Report.Container.Nodes[fixture.ServerContainerNodeID], @@ -445,6 +452,7 @@ var ( Pseudo: false, Shape: pentagon, Stack: true, + NodeCount: 2, Children: report.MakeNodeSet( fixture.Report.Process.Nodes[fixture.ClientProcess1NodeID], fixture.Report.Process.Nodes[fixture.ClientProcess2NodeID], diff --git a/render/mapping.go b/render/mapping.go index be6526634..0391b834d 100644 --- a/render/mapping.go +++ b/render/mapping.go @@ -514,6 +514,7 @@ func MapCountProcessName(n RenderableNode, _ report.Networks) RenderableNodes { } processes, _ := n.Node.Counters.Lookup(processesKey) + n.NodeCount = processes if processes == 1 { n.LabelMinor = "1 process" } else { @@ -744,6 +745,7 @@ func MapCountContainers(n RenderableNode, _ report.Networks) RenderableNodes { } containers, _ := n.Node.Counters.Lookup(ContainersKey) + n.NodeCount = containers if containers == 1 { n.LabelMinor = "1 container" } else { @@ -760,6 +762,7 @@ func MapCountPods(n RenderableNode, _ report.Networks) RenderableNodes { } pods, _ := n.Node.Counters.Lookup(podsKey) + n.NodeCount = pods if pods == 1 { n.LabelMinor = "1 pod" } else { diff --git a/render/renderable_node.go b/render/renderable_node.go index c35357cd8..56413b952 100644 --- a/render/renderable_node.go +++ b/render/renderable_node.go @@ -17,6 +17,7 @@ type RenderableNode struct { 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? + NodeCount int `json:"node_count,omitempty"` // Number of nodes represented by this stack. report.EdgeMetadata `json:"metadata"` // Numeric sums report.Node @@ -42,6 +43,7 @@ func NewRenderableNode(id string) RenderableNode { EdgeMetadata: report.EdgeMetadata{}, Node: report.MakeNode(), Shape: Circle, + NodeCount: 0, } } @@ -57,6 +59,7 @@ func NewRenderableNodeWith(id, major, minor, rank string, node RenderableNode) R EdgeMetadata: node.EdgeMetadata.Copy(), Node: node.Node.Copy(), Shape: Circle, + NodeCount: 0, } } @@ -73,6 +76,7 @@ func NewDerivedNode(id string, node RenderableNode) RenderableNode { Node: node.Node.Copy(), ControlNode: "", // Do not propagate ControlNode when making a derived node! Shape: Circle, + NodeCount: 0, } } @@ -87,6 +91,7 @@ func newDerivedPseudoNode(id, major string, node RenderableNode) RenderableNode EdgeMetadata: node.EdgeMetadata.Copy(), Node: node.Node.Copy(), Shape: Circle, + NodeCount: 0, } } @@ -128,6 +133,10 @@ func (rn RenderableNode) Merge(other RenderableNode) RenderableNode { panic(result.ID) } + if result.NodeCount == 0 { + result.NodeCount = other.NodeCount + } + result.Stack = result.Stack || rn.Stack result.Children = rn.Children.Merge(other.Children) result.EdgeMetadata = rn.EdgeMetadata.Merge(other.EdgeMetadata) @@ -150,6 +159,7 @@ func (rn RenderableNode) Copy() RenderableNode { ControlNode: rn.ControlNode, Shape: rn.Shape, Stack: rn.Stack, + NodeCount: rn.NodeCount, } } diff --git a/tools/lint b/tools/lint index 89d534ba8..a2c405797 100755 --- a/tools/lint +++ b/tools/lint @@ -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 From 46e29c3f1541c5d008cf5e91214ca4f61d5b9fc5 Mon Sep 17 00:00:00 2001 From: Simon Howe Date: Thu, 18 Feb 2016 14:49:41 +0100 Subject: [PATCH 08/10] No grey stacks for now - May give the feeling something is not there when it should be, or incomplete. - They create visual noise --- client/app/scripts/charts/node-shape-stack.js | 10 +++------- client/app/scripts/components/debug-toolbar.js | 2 -- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/client/app/scripts/charts/node-shape-stack.js b/client/app/scripts/charts/node-shape-stack.js index 9d13f87c8..4f39e5d79 100644 --- a/client/app/scripts/charts/node-shape-stack.js +++ b/client/app/scripts/charts/node-shape-stack.js @@ -12,28 +12,24 @@ export default function NodeShapeStack(props) { const propsOnlyHighlight = Object.assign({}, props, {onlyHighlight: true}); const Shape = props.shape; - const nodeCount = props.nodeCount; const [dx, dy] = [0, 6]; const ds = 0.075; const dsx = (props.size * 2 + dx) / (props.size * 2); const dsy = (props.size * 2 + dy) / (props.size * 2); const hls = [dsx, dsy]; - const propsWithGrey = Object.assign({}, propsNoHighlight, {color: '#aaa', className: 'mock'}); - const propsForIndex = i => (nodeCount < i ? propsWithGrey : propsNoHighlight); - return ( - + - + - + ); } diff --git a/client/app/scripts/components/debug-toolbar.js b/client/app/scripts/components/debug-toolbar.js index 8c5e139f2..88853b0dc 100644 --- a/client/app/scripts/components/debug-toolbar.js +++ b/client/app/scripts/components/debug-toolbar.js @@ -42,8 +42,6 @@ function addAllVariants() { }); })); - console.log(newNodes); - receiveNodesDelta({ add: newNodes }); From 96c34507d6bcecd52b9408bf8937cd6f9501c4c8 Mon Sep 17 00:00:00 2001 From: Simon Howe Date: Fri, 19 Feb 2016 15:25:05 +0100 Subject: [PATCH 09/10] Back to a parallel proj. for stacks --- client/app/scripts/charts/node-shape-stack.js | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/client/app/scripts/charts/node-shape-stack.js b/client/app/scripts/charts/node-shape-stack.js index 4f39e5d79..70d8ff5b9 100644 --- a/client/app/scripts/charts/node-shape-stack.js +++ b/client/app/scripts/charts/node-shape-stack.js @@ -12,23 +12,22 @@ export default function NodeShapeStack(props) { const propsOnlyHighlight = Object.assign({}, props, {onlyHighlight: true}); const Shape = props.shape; - const [dx, dy] = [0, 6]; - const ds = 0.075; - const dsx = (props.size * 2 + dx) / (props.size * 2); - const dsy = (props.size * 2 + dy) / (props.size * 2); + 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 ( - - - - - - - + + + + + + + ); From dca7408ec16ac97b2ed86f7a3300f52f83383ea9 Mon Sep 17 00:00:00 2001 From: Simon Howe Date: Mon, 22 Feb 2016 12:14:26 +0100 Subject: [PATCH 10/10] Rollback aggNode.nodeCount which was used for the stack shape. We're not using greyed out lower stacks for now. --- render/expected/expected.go | 8 -------- render/mapping.go | 3 --- render/renderable_node.go | 10 ---------- 3 files changed, 21 deletions(-) diff --git a/render/expected/expected.go b/render/expected/expected.go index da27bcfeb..b06079e7b 100644 --- a/render/expected/expected.go +++ b/render/expected/expected.go @@ -131,7 +131,6 @@ var ( Pseudo: false, Shape: square, Stack: true, - NodeCount: 2, Children: report.MakeNodeSet( fixture.Report.Process.Nodes[fixture.ClientProcess1NodeID], fixture.Report.Process.Nodes[fixture.ClientProcess2NodeID], @@ -150,7 +149,6 @@ var ( Pseudo: false, Shape: square, Stack: true, - NodeCount: 1, Children: report.MakeNodeSet( fixture.Report.Process.Nodes[fixture.ServerProcessNodeID], ), @@ -168,7 +166,6 @@ var ( Pseudo: false, Shape: square, Stack: true, - NodeCount: 1, Children: report.MakeNodeSet( fixture.Report.Process.Nodes[fixture.NonContainerProcessNodeID], ), @@ -248,7 +245,6 @@ var ( Pseudo: false, Shape: hexagon, Stack: true, - NodeCount: 1, Children: report.MakeNodeSet( fixture.Report.Process.Nodes[fixture.ClientProcess1NodeID], fixture.Report.Process.Nodes[fixture.ClientProcess2NodeID], @@ -268,7 +264,6 @@ var ( Pseudo: false, Shape: hexagon, Stack: true, - NodeCount: 1, Children: report.MakeNodeSet( fixture.Report.Process.Nodes[fixture.ServerProcessNodeID], fixture.Report.Container.Nodes[fixture.ServerContainerNodeID], @@ -380,7 +375,6 @@ var ( Rank: "ping/pong-a", Pseudo: false, Shape: pentagon, - NodeCount: 1, Children: report.MakeNodeSet( fixture.Report.Process.Nodes[fixture.ClientProcess1NodeID], fixture.Report.Process.Nodes[fixture.ClientProcess2NodeID], @@ -401,7 +395,6 @@ var ( Rank: "ping/pong-b", Pseudo: false, Shape: pentagon, - NodeCount: 1, Children: report.MakeNodeSet( fixture.Report.Process.Nodes[fixture.ServerProcessNodeID], fixture.Report.Container.Nodes[fixture.ServerContainerNodeID], @@ -452,7 +445,6 @@ var ( Pseudo: false, Shape: pentagon, Stack: true, - NodeCount: 2, Children: report.MakeNodeSet( fixture.Report.Process.Nodes[fixture.ClientProcess1NodeID], fixture.Report.Process.Nodes[fixture.ClientProcess2NodeID], diff --git a/render/mapping.go b/render/mapping.go index 0391b834d..be6526634 100644 --- a/render/mapping.go +++ b/render/mapping.go @@ -514,7 +514,6 @@ func MapCountProcessName(n RenderableNode, _ report.Networks) RenderableNodes { } processes, _ := n.Node.Counters.Lookup(processesKey) - n.NodeCount = processes if processes == 1 { n.LabelMinor = "1 process" } else { @@ -745,7 +744,6 @@ func MapCountContainers(n RenderableNode, _ report.Networks) RenderableNodes { } containers, _ := n.Node.Counters.Lookup(ContainersKey) - n.NodeCount = containers if containers == 1 { n.LabelMinor = "1 container" } else { @@ -762,7 +760,6 @@ func MapCountPods(n RenderableNode, _ report.Networks) RenderableNodes { } pods, _ := n.Node.Counters.Lookup(podsKey) - n.NodeCount = pods if pods == 1 { n.LabelMinor = "1 pod" } else { diff --git a/render/renderable_node.go b/render/renderable_node.go index 56413b952..c35357cd8 100644 --- a/render/renderable_node.go +++ b/render/renderable_node.go @@ -17,7 +17,6 @@ type RenderableNode struct { 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? - NodeCount int `json:"node_count,omitempty"` // Number of nodes represented by this stack. report.EdgeMetadata `json:"metadata"` // Numeric sums report.Node @@ -43,7 +42,6 @@ func NewRenderableNode(id string) RenderableNode { EdgeMetadata: report.EdgeMetadata{}, Node: report.MakeNode(), Shape: Circle, - NodeCount: 0, } } @@ -59,7 +57,6 @@ func NewRenderableNodeWith(id, major, minor, rank string, node RenderableNode) R EdgeMetadata: node.EdgeMetadata.Copy(), Node: node.Node.Copy(), Shape: Circle, - NodeCount: 0, } } @@ -76,7 +73,6 @@ func NewDerivedNode(id string, node RenderableNode) RenderableNode { Node: node.Node.Copy(), ControlNode: "", // Do not propagate ControlNode when making a derived node! Shape: Circle, - NodeCount: 0, } } @@ -91,7 +87,6 @@ func newDerivedPseudoNode(id, major string, node RenderableNode) RenderableNode EdgeMetadata: node.EdgeMetadata.Copy(), Node: node.Node.Copy(), Shape: Circle, - NodeCount: 0, } } @@ -133,10 +128,6 @@ func (rn RenderableNode) Merge(other RenderableNode) RenderableNode { panic(result.ID) } - if result.NodeCount == 0 { - result.NodeCount = other.NodeCount - } - result.Stack = result.Stack || rn.Stack result.Children = rn.Children.Merge(other.Children) result.EdgeMetadata = rn.EdgeMetadata.Merge(other.EdgeMetadata) @@ -159,7 +150,6 @@ func (rn RenderableNode) Copy() RenderableNode { ControlNode: rn.ControlNode, Shape: rn.Shape, Stack: rn.Stack, - NodeCount: rn.NodeCount, } }