Merge pull request #2590 from weaveworks/release-1.5

Release 1.5.0
This commit is contained in:
Alfonso Acosta
2017-06-19 12:13:26 +02:00
committed by GitHub
12 changed files with 116 additions and 43 deletions
+61
View File
@@ -1,3 +1,64 @@
## Release 1.5.0
Highlights:
- More accurate and cheaper connection tracking with eBPF, which now is enabled by default.
- Bug fixes and performance improvements.
New features and enhancements:
- Enable eBPF tracking by default
[#2535](https://github.com/weaveworks/scope/pull/2535)
- Elide url passwords in cli arguments
[#2568](https://github.com/weaveworks/scope/pull/2568)
Performance improvements:
- drop addr and port from Endpoint.Latest map
[#2581](https://github.com/weaveworks/scope/pull/2581)
- parallel reduce
[#2561](https://github.com/weaveworks/scope/pull/2561)
- don't read all of /proc when probe.proc.spy=false
[#2557](https://github.com/weaveworks/scope/pull/2557)
- optimise: don't sort in NodeSet.ForEach
[#2548](https://github.com/weaveworks/scope/pull/2548)
- encode empty ps.Maps as nil
[#2547](https://github.com/weaveworks/scope/pull/2547)
Bug fixes:
- re-target app clients when name resolution changes
[#2579](https://github.com/weaveworks/scope/pull/2579)
- correct type for "Observed Gen."
[#2572](https://github.com/weaveworks/scope/pull/2572)
- Back off upon errored kubernetes api requests
[#2562](https://github.com/weaveworks/scope/pull/2562)
- Close eBPF tracker cleanly
[#2541](https://github.com/weaveworks/scope/pull/2541)
- Simplify connection tracker init and fix procfs scan fallback
[#2539](https://github.com/weaveworks/scope/pull/2539)
- Guard against null DaemonSet store
[#2538](https://github.com/weaveworks/scope/pull/2538)
Internal improvements and cleanup:
- es6ify server.js and include in eslint
[#2560](https://github.com/weaveworks/scope/pull/2560)
- Fix prog/main_test.go
[#2567](https://github.com/weaveworks/scope/pull/2567)
- Fix incomplete dependencies for `make scope/prog`
[#2563](https://github.com/weaveworks/scope/pull/2563)
- bump package.json version to current scope version
[#2555](https://github.com/weaveworks/scope/pull/2555)
- simplify connection join
[#2559](https://github.com/weaveworks/scope/pull/2559)
- Use map helpers
[#2546](https://github.com/weaveworks/scope/pull/2546)
- add copyreport utility
[#2542](https://github.com/weaveworks/scope/pull/2542)
Weave Cloud related changes:
- Time travel control
[#2524](https://github.com/weaveworks/scope/pull/2524)
- Add app capabilities to /api endpoint
[#2575](https://github.com/weaveworks/scope/pull/2575)
## Release 1.4.0
Highlights:
+4 -2
View File
@@ -635,13 +635,15 @@ function updateFromNodesDeltaBuffer(dispatch, state) {
}
export function clickResumeUpdate() {
return (dispatch, getServiceState) => {
return (dispatch, getState) => {
dispatch({
type: ActionTypes.CLICK_RESUME_UPDATE
});
// TODO: Find a better way to do this (see the comment above).
const state = getState().scope || getState();
// Periodically merge buffered nodes deltas until the buffer is emptied.
nodesDeltaBufferUpdateTimer = setInterval(
() => updateFromNodesDeltaBuffer(dispatch, getServiceState().scope),
() => updateFromNodesDeltaBuffer(dispatch, state),
NODES_DELTA_BUFFER_FEED_INTERVAL,
);
};
+2
View File
@@ -2,6 +2,7 @@ import React from 'react';
import { connect } from 'react-redux';
import Plugins from './plugins';
import PauseButton from './pause-button';
import { trackMixpanelEvent } from '../utils/tracking-utils';
import {
clickDownloadGraph,
@@ -65,6 +66,7 @@ class Footer extends React.Component {
</div>
<div className="footer-tools">
<PauseButton />
<a
className="footer-icon"
onClick={this.handleRelayoutClick}
+8 -8
View File
@@ -11,7 +11,6 @@ import { Loading, getNodeType } from './loading';
import {
isTopologyNodeCountZero,
isNodesDisplayEmpty,
isTopologyEmpty,
} from '../utils/topology-utils';
import {
isGraphViewModeSelector,
@@ -24,11 +23,12 @@ import { TOPOLOGY_LOADER_DELAY } from '../constants/timer';
// TODO: The information that we already have available on the frontend should enable
// us to determine which of these cases exactly is preventing us from seeing the nodes.
const NODE_COUNT_ZERO_CAUSES = [
const NODES_STATS_COUNT_ZERO_CAUSES = [
'We haven\'t received any reports from probes recently. Are the probes properly connected?',
'Containers view only: you\'re not running Docker, or you don\'t have any containers',
];
const NODES_DISPLAY_EMPTY_CAUSES = [
const NODES_NOT_DISPLAYED_CAUSES = [
'There are nodes, but they\'ve been filtered out by pinned searches in the top-left corner.',
'There are nodes, but they\'re currently hidden. Check the view options in the bottom-left if they allow for showing hidden nodes.',
'There are no nodes for this particular moment in time. Use the time travel feature at the bottom-right corner to explore different times.',
];
@@ -39,13 +39,14 @@ const renderCauses = causes => (
class Nodes extends React.Component {
renderConditionalEmptyTopologyError() {
const { topologyNodeCountZero, nodesDisplayEmpty, topologyEmpty } = this.props;
const { topologyNodeCountZero, nodesDisplayEmpty } = this.props;
return (
<NodesError faIconClass="fa-circle-thin" hidden={!topologyEmpty}>
<NodesError faIconClass="fa-circle-thin" hidden={!nodesDisplayEmpty}>
<div className="heading">Nothing to show. This can have any of these reasons:</div>
{topologyNodeCountZero && renderCauses(NODE_COUNT_ZERO_CAUSES)}
{!topologyNodeCountZero && nodesDisplayEmpty && renderCauses(NODES_DISPLAY_EMPTY_CAUSES)}
{topologyNodeCountZero ?
renderCauses(NODES_STATS_COUNT_ZERO_CAUSES) :
renderCauses(NODES_NOT_DISPLAYED_CAUSES)}
</NodesError>
);
}
@@ -84,7 +85,6 @@ function mapStateToProps(state) {
isResourceViewMode: isResourceViewModeSelector(state),
topologyNodeCountZero: isTopologyNodeCountZero(state),
nodesDisplayEmpty: isNodesDisplayEmpty(state),
topologyEmpty: isTopologyEmpty(state),
websocketTransitioning: state.get('websocketTransitioning'),
currentTopology: state.get('currentTopology'),
nodesLoaded: state.get('nodesLoaded'),
@@ -60,14 +60,14 @@ class PauseButton extends React.Component {
}
}
function mapStateToProps({ scope }) {
function mapStateToProps(state) {
return {
hasUpdates: !scope.get('nodesDeltaBuffer').isEmpty(),
updateCount: scope.get('nodesDeltaBuffer').size,
updatePausedAt: scope.get('updatePausedAt'),
topologyViewMode: scope.get('topologyViewMode'),
currentTopology: scope.get('currentTopology'),
isPaused: isPausedSelector(scope),
hasUpdates: !state.get('nodesDeltaBuffer').isEmpty(),
updateCount: state.get('nodesDeltaBuffer').size,
updatePausedAt: state.get('updatePausedAt'),
topologyViewMode: state.get('topologyViewMode'),
currentTopology: state.get('currentTopology'),
isPaused: isPausedSelector(state),
};
}
+3 -3
View File
@@ -8,7 +8,7 @@ import { searchMatchCountByTopologySelector } from '../selectors/search';
import { isResourceViewModeSelector } from '../selectors/topology';
import { slugify } from '../utils/string-utils';
import { parseQuery } from '../utils/search-utils';
import { isTopologyEmpty } from '../utils/topology-utils';
import { isTopologyNodeCountZero } from '../utils/topology-utils';
import { trackMixpanelEvent } from '../utils/tracking-utils';
import SearchItem from './search-item';
import { ENTER_KEY_CODE } from '../constants/key-codes';
@@ -129,7 +129,7 @@ class Search extends React.Component {
const { nodes, pinnedSearches, searchFocused, searchMatchCountByTopology,
isResourceViewMode, searchQuery, topologiesLoaded, inputId = 'search' } = this.props;
const hidden = !topologiesLoaded || isResourceViewMode;
const disabled = this.props.isTopologyEmpty && !hidden;
const disabled = this.props.isTopologyNodeCountZero && !hidden;
const matchCount = searchMatchCountByTopology
.reduce((count, topologyMatchCount) => count + topologyMatchCount, 0);
const showPinnedSearches = pinnedSearches.size > 0;
@@ -180,7 +180,7 @@ export default connect(
nodes: state.get('nodes'),
topologyViewMode: state.get('topologyViewMode'),
isResourceViewMode: isResourceViewModeSelector(state),
isTopologyEmpty: isTopologyEmpty(state),
isTopologyNodeCountZero: isTopologyNodeCountZero(state),
currentTopology: state.get('currentTopology'),
topologiesLoaded: state.get('topologiesLoaded'),
pinnedSearches: state.get('pinnedSearches'),
+1 -3
View File
@@ -5,7 +5,6 @@ import classNames from 'classnames';
import { connect } from 'react-redux';
import { debounce } from 'lodash';
import PauseButton from './pause-button';
import TimeTravelTimestamp from './time-travel-timestamp';
import { trackMixpanelEvent } from '../utils/tracking-utils';
import {
@@ -260,7 +259,6 @@ class TimeTravel extends React.Component {
selected={showSliderPanel}
/>
{!isCurrent && this.renderJumpToNowButton()}
<PauseButton />
</div>
</div>
);
@@ -271,7 +269,7 @@ function mapStateToProps({ scope, root }, { params }) {
const cloudInstance = root.instances[params.orgId] || {};
const featureFlags = cloudInstance.featureFlags || [];
return {
hasTimeTravel: featureFlags.includes('timeline-control'),
hasTimeTravel: featureFlags.includes('time-travel'),
websocketTransitioning: scope.get('websocketTransitioning'),
topologyViewMode: scope.get('topologyViewMode'),
currentTopology: scope.get('currentTopology'),
@@ -16,7 +16,8 @@ describe('RootReducer', () => {
// TODO maybe extract those to topology-utils tests?
const activeTopologyOptionsSelector = topologySelectors.activeTopologyOptionsSelector;
const getAdjacentNodes = topologyUtils.getAdjacentNodes;
const isTopologyEmpty = topologyUtils.isTopologyEmpty;
const isNodesDisplayEmpty = topologyUtils.isNodesDisplayEmpty;
const isTopologyNodeCountZero = topologyUtils.isTopologyNodeCountZero;
const getUrlState = require('../../utils/router-utils').getUrlState;
// fixtures
@@ -534,24 +535,36 @@ describe('RootReducer', () => {
// empty topology
it('detects that the topology is empty', () => {
it('detects that the nodes display is empty', () => {
let nextState = initialState;
nextState = reducer(nextState, ReceiveTopologiesAction);
nextState = reducer(nextState, ClickTopologyAction);
expect(isTopologyEmpty(nextState)).toBeTruthy();
expect(isNodesDisplayEmpty(nextState)).toBeTruthy();
nextState = reducer(nextState, ReceiveNodesDeltaAction);
expect(isTopologyEmpty(nextState)).toBeFalsy();
expect(isNodesDisplayEmpty(nextState)).toBeFalsy();
nextState = reducer(nextState, ClickTopology2Action);
expect(isNodesDisplayEmpty(nextState)).toBeTruthy();
nextState = reducer(nextState, ReceiveNodesDeltaAction);
expect(isTopologyEmpty(nextState)).toBeTruthy();
expect(isNodesDisplayEmpty(nextState)).toBeFalsy();
});
it('detects that the topo stats are empty', () => {
let nextState = initialState;
nextState = reducer(nextState, ReceiveTopologiesAction);
nextState = reducer(nextState, ClickTopologyAction);
expect(isTopologyNodeCountZero(nextState)).toBeFalsy();
nextState = reducer(nextState, ReceiveNodesDeltaAction);
expect(isTopologyNodeCountZero(nextState)).toBeFalsy();
nextState = reducer(nextState, ClickTopology2Action);
expect(isTopologyNodeCountZero(nextState)).toBeTruthy();
nextState = reducer(nextState, ClickTopologyAction);
expect(isTopologyEmpty(nextState)).toBeTruthy();
nextState = reducer(nextState, ReceiveNodesDeltaAction);
expect(isTopologyEmpty(nextState)).toBeFalsy();
expect(isTopologyNodeCountZero(nextState)).toBeFalsy();
});
// selection of relatives
+2 -5
View File
@@ -4,6 +4,7 @@ import { Set as makeSet, List as makeList } from 'immutable';
import { isWebsocketQueryingCurrentSelector } from '../selectors/time-travel';
import { isResourceViewModeSelector } from '../selectors/topology';
import { pinnedMetricSelector } from '../selectors/node-metric';
import { shownNodesSelector } from '../selectors/node-filters';
//
// top priority first
@@ -145,11 +146,7 @@ export function isNodesDisplayEmpty(state) {
return !pinnedMetricSelector(state);
}
// Otherwise (in graph and table view), we only look at the nodes content.
return state.get('nodes').isEmpty();
}
export function isTopologyEmpty(state) {
return isTopologyNodeCountZero(state) || isNodesDisplayEmpty(state);
return shownNodesSelector(state).isEmpty();
}
export function getAdjacentNodes(state, originNodeId) {
+1 -1
View File
@@ -184,7 +184,7 @@
margin-right: 1em;
}
&-label {
&-label, .pause-text {
text-transform: uppercase;
margin: 0 0.25em;
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "weave-scope",
"version": "1.4.0",
"version": "1.5.0",
"description": "SPA JS app for Weave Scope visualising the application network.",
"repository": "weaveworks/scope",
"license": "Apache-2.0",
+4 -4
View File
@@ -125,7 +125,7 @@ After its been launched, open your browser to `http://localhost:4040`.
**Docker Compose Format Version 1:**
scope:
image: weaveworks/scope:1.3.0
image: weaveworks/scope:1.5.0
net: "host"
pid: "host"
privileged: true
@@ -141,7 +141,7 @@ After its been launched, open your browser to `http://localhost:4040`.
version: '2'
services:
scope:
image: weaveworks/scope:1.3.0
image: weaveworks/scope:1.5.0
network_mode: "host"
pid: "host"
privileged: true
@@ -165,7 +165,7 @@ Version 2 of this YAML file supports networks and volumes as defined by any plug
**Docker Compose Format Version 1:**
probe:
image: weaveworks/scope:1.3.0
image: weaveworks/scope:1.5.0
net: "host"
pid: "host"
privileged: true
@@ -182,7 +182,7 @@ Version 2 of this YAML file supports networks and volumes as defined by any plug
version: '2'
services:
probe:
image: weaveworks/scope:1.3.0
image: weaveworks/scope:1.5.0
network_mode: "host"
pid: "host"
privileged: true