Add alerts to kapacitor rule
parent
94d54065f3
commit
658d39dbc3
|
@ -5,6 +5,7 @@ import {
|
||||||
chooseTrigger,
|
chooseTrigger,
|
||||||
updateRuleValues,
|
updateRuleValues,
|
||||||
updateMessage,
|
updateMessage,
|
||||||
|
updateAlerts,
|
||||||
} from 'src/kapacitor/actions/view';
|
} from 'src/kapacitor/actions/view';
|
||||||
|
|
||||||
describe('Kapacitor.Reducers.rules', () => {
|
describe('Kapacitor.Reducers.rules', () => {
|
||||||
|
@ -66,4 +67,19 @@ describe('Kapacitor.Reducers.rules', () => {
|
||||||
const newState = reducer(initialState, updateMessage(ruleID, message));
|
const newState = reducer(initialState, updateMessage(ruleID, message));
|
||||||
expect(newState[ruleID].message).to.equal(message);
|
expect(newState[ruleID].message).to.equal(message);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('can update the alerts', () => {
|
||||||
|
const ruleID = 1;
|
||||||
|
const initialState = {
|
||||||
|
[ruleID]: {
|
||||||
|
id: ruleID,
|
||||||
|
queryID: 988,
|
||||||
|
alerts: [],
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const alerts = ['slack'];
|
||||||
|
const newState = reducer(initialState, updateAlerts(ruleID, alerts));
|
||||||
|
expect(newState[ruleID].alerts).to.equal(alerts);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -68,3 +68,13 @@ export function updateMessage(ruleID, message) {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function updateAlerts(ruleID, alerts) {
|
||||||
|
return {
|
||||||
|
type: 'UPDATE_RULE_ALERTS',
|
||||||
|
payload: {
|
||||||
|
ruleID,
|
||||||
|
alerts,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -92,7 +92,7 @@ const Threshold = React.createClass({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const operators = mapToItems(['greater than', 'less than', 'equal to', 'not equal to'], 'operator');
|
const operators = mapToItems(['greater than', 'equal to or greater', 'equal to or less than', 'less than', 'equal to', 'not equal to'], 'operator');
|
||||||
const relations = mapToItems(['once', 'more than ', 'less than'], 'relation');
|
const relations = mapToItems(['once', 'more than ', 'less than'], 'relation');
|
||||||
const periods = mapToItems(['1m', '5m', '10m', '30m', '1h', '2h', '1h'], 'period');
|
const periods = mapToItems(['1m', '5m', '10m', '30m', '1h', '2h', '1h'], 'period');
|
||||||
|
|
||||||
|
@ -144,7 +144,7 @@ const Relative = React.createClass({
|
||||||
const changes = mapToItems(['change', '% change'], 'change');
|
const changes = mapToItems(['change', '% change'], 'change');
|
||||||
const periods = mapToItems(['1m', '5m', '10m', '30m', '1h', '2h', '1h'], 'period');
|
const periods = mapToItems(['1m', '5m', '10m', '30m', '1h', '2h', '1h'], 'period');
|
||||||
const shifts = mapToItems(['1m', '5m', '10m', '30m', '1h', '2h', '1h'], 'shift');
|
const shifts = mapToItems(['1m', '5m', '10m', '30m', '1h', '2h', '1h'], 'shift');
|
||||||
const operators = mapToItems(['greater than', 'less than', 'equal to', 'not equal to'], 'operator');
|
const operators = mapToItems(['greater than', 'equal to or greater', 'equal to or less than', 'less than', 'equal to', 'not equal to'], 'operator');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="u-flex u-jc-space-around u-ai-center">
|
<div className="u-flex u-jc-space-around u-ai-center">
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import React, {PropTypes} from 'react';
|
import React, {PropTypes} from 'react';
|
||||||
import {connect} from 'react-redux';
|
import {connect} from 'react-redux';
|
||||||
|
import _ from 'lodash';
|
||||||
import DataSection from '../components/DataSection';
|
import DataSection from '../components/DataSection';
|
||||||
import ValuesSection from '../components/ValuesSection';
|
import ValuesSection from '../components/ValuesSection';
|
||||||
import * as kapacitorActionCreators from '../actions/view';
|
import * as kapacitorActionCreators from '../actions/view';
|
||||||
|
@ -9,6 +10,8 @@ import selectStatement from 'src/chronograf/utils/influxql/select';
|
||||||
import AutoRefresh from 'shared/components/AutoRefresh';
|
import AutoRefresh from 'shared/components/AutoRefresh';
|
||||||
import LineGraph from 'shared/components/LineGraph';
|
import LineGraph from 'shared/components/LineGraph';
|
||||||
const RefreshingLineGraph = AutoRefresh(LineGraph);
|
const RefreshingLineGraph = AutoRefresh(LineGraph);
|
||||||
|
import {getKapacitor, getKapacitorConfig} from 'shared/apis/index';
|
||||||
|
import Dropdown from 'shared/components/Dropdown';
|
||||||
|
|
||||||
export const KapacitorRulePage = React.createClass({
|
export const KapacitorRulePage = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
|
@ -27,6 +30,7 @@ export const KapacitorRulePage = React.createClass({
|
||||||
chooseTrigger: PropTypes.func.isRequired,
|
chooseTrigger: PropTypes.func.isRequired,
|
||||||
updateRuleValues: PropTypes.func.isRequired,
|
updateRuleValues: PropTypes.func.isRequired,
|
||||||
updateMessage: PropTypes.func.isRequired,
|
updateMessage: PropTypes.func.isRequired,
|
||||||
|
updateAlerts: PropTypes.func.isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
queryActions: PropTypes.shape({}).isRequired,
|
queryActions: PropTypes.shape({}).isRequired,
|
||||||
params: PropTypes.shape({
|
params: PropTypes.shape({
|
||||||
|
@ -34,6 +38,12 @@ export const KapacitorRulePage = React.createClass({
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getInitialState() {
|
||||||
|
return {
|
||||||
|
enabledAlerts: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const {ruleID} = this.props.params;
|
const {ruleID} = this.props.params;
|
||||||
if (ruleID) {
|
if (ruleID) {
|
||||||
|
@ -41,46 +51,61 @@ export const KapacitorRulePage = React.createClass({
|
||||||
} else {
|
} else {
|
||||||
this.props.kapacitorActions.loadDefaultRule();
|
this.props.kapacitorActions.loadDefaultRule();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getKapacitor(this.props.source).then((kapacitor) => {
|
||||||
|
getKapacitorConfig(kapacitor).then(({data: {sections}}) => {
|
||||||
|
const enabledAlerts = Object.keys(sections).filter((section) => {
|
||||||
|
return _.get(sections, [section, 'elements', '0', 'options', 'enabled'], false);
|
||||||
|
});
|
||||||
|
this.setState({enabledAlerts});
|
||||||
|
}).catch(() => {
|
||||||
|
this.props.addFlashMessage({type: 'failure', message: `There was a problem communicating with Kapacitor`});
|
||||||
|
}).catch(() => {
|
||||||
|
this.props.addFlashMessage({type: 'failure', message: `We couldn't find a configured Kapacitor for this source`});
|
||||||
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
handleSave() {
|
handleSave() {
|
||||||
console.log(this.props.rules); // eslint-disable-line no-console
|
console.log(this.props.rules); // eslint-disable-line no-console
|
||||||
},
|
},
|
||||||
|
|
||||||
|
handleChooseAlert(item) {
|
||||||
|
this.props.kapacitorActions.updateAlerts(item.ruleID, [item.text]);
|
||||||
|
},
|
||||||
|
|
||||||
createUnderlayCallback(rule) {
|
createUnderlayCallback(rule) {
|
||||||
return (canvas, area, dygraph) => {
|
return (canvas, area, dygraph) => {
|
||||||
if (rule.trigger !== 'threshold') {
|
if (rule.trigger !== 'threshold') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const theOnePercent = 0.01;
|
const theOnePercent = 0.01;
|
||||||
let highlightStart = 0;
|
let highlightStart = 0;
|
||||||
let highlightEnd = 0;
|
let highlightEnd = 0;
|
||||||
|
|
||||||
switch (rule.values.operator) {
|
switch (rule.values.operator) {
|
||||||
|
case 'equal to or greater':
|
||||||
case 'greater than': {
|
case 'greater than': {
|
||||||
highlightStart = rule.values.value;
|
highlightStart = rule.values.value;
|
||||||
highlightEnd = dygraph.yAxisRange()[1];
|
highlightEnd = dygraph.yAxisRange()[1];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'equal to or less than':
|
||||||
case 'less than': {
|
case 'less than': {
|
||||||
highlightStart = dygraph.yAxisRange()[0];
|
highlightStart = dygraph.yAxisRange()[0];
|
||||||
highlightEnd = rule.values.value;
|
highlightEnd = rule.values.value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'not equal to':
|
||||||
case 'equal to': {
|
case 'equal to': {
|
||||||
const width = (theOnePercent) * (dygraph.yAxisRange()[1] - dygraph.yAxisRange()[0]);
|
const width = (theOnePercent) * (dygraph.yAxisRange()[1] - dygraph.yAxisRange()[0]);
|
||||||
highlightStart = +rule.values.value - width;
|
highlightStart = +rule.values.value - width;
|
||||||
highlightEnd = +rule.values.value + width;
|
highlightEnd = +rule.values.value + width;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'not equal to': {
|
|
||||||
const width = (theOnePercent) * (dygraph.yAxisRange()[1] - dygraph.yAxisRange()[0]);
|
|
||||||
highlightStart = +rule.values.value - width;
|
|
||||||
highlightEnd = +rule.values.value + width;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const bottom = dygraph.toDomYCoord(highlightStart);
|
const bottom = dygraph.toDomYCoord(highlightStart);
|
||||||
|
@ -147,7 +172,7 @@ export const KapacitorRulePage = React.createClass({
|
||||||
</div>
|
</div>
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-md-12">
|
<div className="col-md-12">
|
||||||
{this.renderAlertsSection()}
|
{this.renderAlertsSection(rule)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -183,15 +208,16 @@ export const KapacitorRulePage = React.createClass({
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
renderAlertsSection() {
|
renderAlertsSection(rule) {
|
||||||
// hit kapacitor config endpoint and filter sections by the "enabled" property
|
const alerts = this.state.enabledAlerts.map((text) => {
|
||||||
const alertOptions = ['Slack', 'VictorOps'].map((destination) => {
|
return {text, ruleID: rule.id};
|
||||||
return <option key={destination}>send to {destination}</option>;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="kapacitor-rule-section">
|
<div className="kapacitor-rule-section">
|
||||||
<h3>Alerts</h3>
|
<h3>Alerts</h3>
|
||||||
<p>The Alert should <select>{alertOptions}</select></p>
|
The Alert should
|
||||||
|
<Dropdown selected={rule.alerts[0] || 'Choose an output'} items={alerts} onChoose={this.handleChooseAlert} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,6 +11,9 @@ export default function rules(state = {}, action) {
|
||||||
trigger: 'threshold',
|
trigger: 'threshold',
|
||||||
values: defaultRuleConfigs.threshold,
|
values: defaultRuleConfigs.threshold,
|
||||||
message: '',
|
message: '',
|
||||||
|
alerts: [],
|
||||||
|
every: '30s',
|
||||||
|
name: 'Random album title',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -51,6 +54,15 @@ export default function rules(state = {}, action) {
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'UPDATE_RULE_ALERTS': {
|
||||||
|
const {ruleID, alerts} = action.payload;
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
[ruleID]: Object.assign({}, state[ruleID], {
|
||||||
|
alerts,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue