Merge pull request #2702 from influxdata/testable-kapacitor-configs

Testable kapacitor configs
pull/2715/merge
Deniz Kusefoglu 2018-01-12 13:52:22 -08:00 committed by GitHub
commit dd8062e322
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 402 additions and 81 deletions

View File

@ -1,5 +1,7 @@
## v1.4.1.0 [unreleased]
### Features
1. [#2409](https://github.com/influxdata/chronograf/pull/2409): Allow adding multiple event handlers to a rule
1. [#2709](https://github.com/influxdata/chronograf/pull/2709): Add "send test alert" button to test kapacitor alert configurations"
### UI Improvements
### Bug Fixes
1. [#2689](https://github.com/influxdata/chronograf/pull/2689): Allow insecure (self-signed) certificates for kapacitor and influxdb
@ -103,7 +105,6 @@
1. [#2460](https://github.com/influxdata/chronograf/pull/2460): Update kapacitor alerts to cast to float before sending to influx
1. [#2479](https://github.com/influxdata/chronograf/pull/2479): Support authentication for Enterprise Meta Nodes
1. [#2477](https://github.com/influxdata/chronograf/pull/2477): Improve performance of hoverline rendering
1. [#2409](https://github.com/influxdata/chronograf/pull/2409): Add multiple event handlers to rules
### UI Improvements

View File

@ -2,7 +2,11 @@ import React, {Component, PropTypes} from 'react'
import _ from 'lodash'
import {Tab, Tabs, TabPanel, TabPanels, TabList} from 'shared/components/Tabs'
import {getKapacitorConfig, updateKapacitorConfigSection} from 'shared/apis'
import {
getKapacitorConfig,
updateKapacitorConfigSection,
testAlertOutput,
} from 'shared/apis'
import {
AlertaConfig,
@ -76,18 +80,35 @@ class AlertTabs extends Component {
this.refreshKapacitorConfig(this.props.kapacitor)
this.props.addFlashMessage({
type: 'success',
text: `Alert for ${section} successfully saved`,
text: `Alert configuration for ${section} successfully saved.`,
})
})
.catch(() => {
this.props.addFlashMessage({
type: 'error',
text: 'There was an error saving the kapacitor config',
text: `There was an error saving the alert configuration for ${section}.`,
})
})
}
}
handleTestConfig = section => async e => {
e.preventDefault()
try {
await testAlertOutput(this.props.kapacitor, section)
this.props.addFlashMessage({
type: 'success',
text: `Successfully triggered an alert to ${section}. If the alert does not reach its destination, please check your configuration settings.`,
})
} catch (error) {
this.props.addFlashMessage({
type: 'error',
text: `There was an error sending an alert to ${section}.`,
})
}
}
sanitizeProperties = (section, properties) => {
const cleanProps = {...properties, enabled: true}
const {redacted} = this.getSection(this.state.configSections, section)
@ -116,6 +137,8 @@ class AlertTabs extends Component {
<AlertaConfig
onSave={this.handleSaveConfig('alerta')}
config={this.getSection(configSections, 'alerta')}
onTest={this.handleTestConfig('alerta')}
enabled={this.getEnabled(configSections, 'alerta')}
/>,
},
hipchat: {
@ -125,6 +148,8 @@ class AlertTabs extends Component {
<HipChatConfig
onSave={this.handleSaveConfig('hipchat')}
config={this.getSection(configSections, 'hipchat')}
onTest={this.handleTestConfig('hipchat')}
enabled={this.getEnabled(configSections, 'hipchat')}
/>,
},
opsgenie: {
@ -134,6 +159,8 @@ class AlertTabs extends Component {
<OpsGenieConfig
onSave={this.handleSaveConfig('opsgenie')}
config={this.getSection(configSections, 'opsgenie')}
onTest={this.handleTestConfig('opsgenie')}
enabled={this.getEnabled(configSections, 'opsgenie')}
/>,
},
pagerduty: {
@ -143,6 +170,8 @@ class AlertTabs extends Component {
<PagerDutyConfig
onSave={this.handleSaveConfig('pagerduty')}
config={this.getSection(configSections, 'pagerduty')}
onTest={this.handleTestConfig('pagerduty')}
enabled={this.getEnabled(configSections, 'pagerduty')}
/>,
},
pushover: {
@ -152,6 +181,8 @@ class AlertTabs extends Component {
<PushoverConfig
onSave={this.handleSaveConfig('pushover')}
config={this.getSection(configSections, 'pushover')}
onTest={this.handleTestConfig('pushover')}
enabled={this.getEnabled(configSections, 'pushover')}
/>,
},
sensu: {
@ -161,6 +192,8 @@ class AlertTabs extends Component {
<SensuConfig
onSave={this.handleSaveConfig('sensu')}
config={this.getSection(configSections, 'sensu')}
onTest={this.handleTestConfig('sensu')}
enabled={this.getEnabled(configSections, 'sensu')}
/>,
},
slack: {
@ -170,6 +203,8 @@ class AlertTabs extends Component {
<SlackConfig
onSave={this.handleSaveConfig('slack')}
config={this.getSection(configSections, 'slack')}
onTest={this.handleTestConfig('slack')}
enabled={this.getEnabled(configSections, 'slack')}
/>,
},
smtp: {
@ -179,6 +214,8 @@ class AlertTabs extends Component {
<SMTPConfig
onSave={this.handleSaveConfig('smtp')}
config={this.getSection(configSections, 'smtp')}
onTest={this.handleTestConfig('smtp')}
enabled={this.getEnabled(configSections, 'smtp')}
/>,
},
talk: {
@ -188,6 +225,8 @@ class AlertTabs extends Component {
<TalkConfig
onSave={this.handleSaveConfig('talk')}
config={this.getSection(configSections, 'talk')}
onTest={this.handleTestConfig('talk')}
enabled={this.getEnabled(configSections, 'talk')}
/>,
},
telegram: {
@ -197,6 +236,8 @@ class AlertTabs extends Component {
<TelegramConfig
onSave={this.handleSaveConfig('telegram')}
config={this.getSection(configSections, 'telegram')}
onTest={this.handleTestConfig('telegram')}
enabled={this.getEnabled(configSections, 'telegram')}
/>,
},
victorops: {
@ -206,6 +247,8 @@ class AlertTabs extends Component {
<VictorOpsConfig
onSave={this.handleSaveConfig('victorops')}
config={this.getSection(configSections, 'victorops')}
onTest={this.handleTestConfig('victorops')}
enabled={this.getEnabled(configSections, 'victorops')}
/>,
},
}

View File

@ -6,8 +6,7 @@ import FancyScrollbar from 'shared/components/FancyScrollbar'
class KapacitorForm extends Component {
render() {
const {onInputChange, onReset, kapacitor, onSubmit, exists} = this.props
const {url, name, username, password} = kapacitor
const {url: kapaUrl, name, username, password} = kapacitor
return (
<div className="page">
<div className="page-header">
@ -29,13 +28,13 @@ class KapacitorForm extends Component {
<form onSubmit={onSubmit}>
<div>
<div className="form-group">
<label htmlFor="url">Kapacitor URL</label>
<label htmlFor="kapaUrl">Kapacitor URL</label>
<input
className="form-control"
id="url"
name="url"
placeholder={url}
value={url}
id="kapaUrl"
name="kapaUrl"
placeholder={kapaUrl}
value={kapaUrl}
onChange={onInputChange}
spellCheck="false"
/>
@ -60,7 +59,7 @@ class KapacitorForm extends Component {
id="username"
name="username"
placeholder="username"
value={username}
value={username || ''}
onChange={onInputChange}
spellCheck="false"
/>
@ -73,7 +72,7 @@ class KapacitorForm extends Component {
type="password"
name="password"
placeholder="password"
value={password}
value={password || ''}
onChange={onInputChange}
spellCheck="false"
/>

View File

@ -5,9 +5,12 @@ import RedactedInput from './RedactedInput'
class AlertaConfig extends Component {
constructor(props) {
super(props)
this.state = {
testEnabled: this.props.enabled,
}
}
handleSaveAlert = e => {
handleSubmit = e => {
e.preventDefault()
const properties = {
@ -18,6 +21,11 @@ class AlertaConfig extends Component {
}
this.props.onSave(properties)
this.setState({testEnabled: true})
}
disableTest = () => {
this.setState({testEnabled: false})
}
handleTokenRef = r => (this.token = r)
@ -26,7 +34,7 @@ class AlertaConfig extends Component {
const {environment, origin, token, url} = this.props.config.options
return (
<form onSubmit={this.handleSaveAlert}>
<form onSubmit={this.handleSubmit}>
<div className="form-group col-xs-12">
<label htmlFor="environment">Environment</label>
<input
@ -35,6 +43,7 @@ class AlertaConfig extends Component {
type="text"
ref={r => (this.environment = r)}
defaultValue={environment || ''}
onChange={this.disableTest}
/>
</div>
@ -46,6 +55,7 @@ class AlertaConfig extends Component {
type="text"
ref={r => (this.origin = r)}
defaultValue={origin || ''}
onChange={this.disableTest}
/>
</div>
@ -55,6 +65,7 @@ class AlertaConfig extends Component {
defaultValue={token}
id="token"
refFunc={this.handleTokenRef}
disableTest={this.disableTest}
/>
</div>
@ -66,12 +77,26 @@ class AlertaConfig extends Component {
type="text"
ref={r => (this.url = r)}
defaultValue={url || ''}
onChange={this.disableTest}
/>
</div>
<div className="form-group-submit col-xs-12 text-center">
<button className="btn btn-primary" type="submit">
Update Alerta Config
<button
className="btn btn-primary"
type="submit"
disabled={this.state.testEnabled}
>
<span className="icon checkmark" />
Save Changes
</button>
<button
className="btn btn-primary"
disabled={!this.state.testEnabled}
onClick={this.props.onTest}
>
<span className="icon pulse-c" />
Send Test Alert
</button>
</div>
</form>
@ -91,6 +116,8 @@ AlertaConfig.propTypes = {
}).isRequired,
}).isRequired,
onSave: func.isRequired,
onTest: func.isRequired,
enabled: bool.isRequired,
}
export default AlertaConfig

View File

@ -7,9 +7,12 @@ import RedactedInput from './RedactedInput'
class HipchatConfig extends Component {
constructor(props) {
super(props)
this.state = {
testEnabled: this.props.enabled,
}
}
handleSaveAlert = e => {
handleSubmit = e => {
e.preventDefault()
const properties = {
@ -19,6 +22,11 @@ class HipchatConfig extends Component {
}
this.props.onSave(properties)
this.setState({testEnabled: true})
}
disableTest = () => {
this.setState({testEnabled: false})
}
handleTokenRef = r => (this.token = r)
@ -32,7 +40,7 @@ class HipchatConfig extends Component {
.replace('.hipchat.com/v2/room', '')
return (
<form onSubmit={this.handleSaveAlert}>
<form onSubmit={this.handleSubmit}>
<div className="form-group col-xs-12">
<label htmlFor="url">Subdomain</label>
<input
@ -42,6 +50,7 @@ class HipchatConfig extends Component {
placeholder="your-subdomain"
ref={r => (this.url = r)}
defaultValue={subdomain && subdomain.length ? subdomain : ''}
onChange={this.disableTest}
/>
</div>
@ -54,6 +63,7 @@ class HipchatConfig extends Component {
placeholder="your-hipchat-room"
ref={r => (this.room = r)}
defaultValue={room || ''}
onChange={this.disableTest}
/>
</div>
@ -66,12 +76,26 @@ class HipchatConfig extends Component {
defaultValue={token}
id="token"
refFunc={this.handleTokenRef}
disableTest={this.disableTest}
/>
</div>
<div className="form-group-submit col-xs-12 text-center">
<button className="btn btn-primary" type="submit">
Update HipChat Config
<button
className="btn btn-primary"
type="submit"
disabled={this.state.testEnabled}
>
<span className="icon checkmark" />
Save Changes
</button>
<button
className="btn btn-primary"
disabled={!this.state.testEnabled}
onClick={this.props.onTest}
>
<span className="icon pulse-c" />
Send Test Alert
</button>
</div>
</form>
@ -90,6 +114,8 @@ HipchatConfig.propTypes = {
}).isRequired,
}).isRequired,
onSave: func.isRequired,
onTest: func.isRequired,
enabled: bool.isRequired,
}
export default HipchatConfig

View File

@ -12,10 +12,11 @@ class OpsGenieConfig extends Component {
this.state = {
currentTeams: teams || [],
currentRecipients: recipients || [],
testEnabled: this.props.enabled,
}
}
handleSaveAlert = e => {
handleSubmit = e => {
e.preventDefault()
const properties = {
@ -25,6 +26,11 @@ class OpsGenieConfig extends Component {
}
this.props.onSave(properties)
this.setState({testEnabled: true})
}
disableTest = () => {
this.setState({testEnabled: false})
}
handleAddTeam = team => {
@ -59,13 +65,14 @@ class OpsGenieConfig extends Component {
const {currentTeams, currentRecipients} = this.state
return (
<form onSubmit={this.handleSaveAlert}>
<form onSubmit={this.handleSubmit}>
<div className="form-group col-xs-12">
<label htmlFor="api-key">API Key</label>
<RedactedInput
defaultValue={apiKey}
id="api-key"
refFunc={this.handleApiKeyRef}
disableTest={this.disableTest}
/>
</div>
@ -74,17 +81,32 @@ class OpsGenieConfig extends Component {
onAddTag={this.handleAddTeam}
onDeleteTag={this.handleDeleteTeam}
tags={currentTeams}
disableTest={this.disableTest}
/>
<TagInput
title="Recipients"
onAddTag={this.handleAddRecipient}
onDeleteTag={this.handleDeleteRecipient}
tags={currentRecipients}
disableTest={this.disableTest}
/>
<div className="form-group-submit col-xs-12 text-center">
<button className="btn btn-primary" type="submit">
Update OpsGenie Config
<button
className="btn btn-primary"
type="submit"
disabled={this.state.testEnabled}
>
<span className="icon checkmark" />
Save Changes
</button>
<button
className="btn btn-primary"
disabled={!this.state.testEnabled}
onClick={this.props.onTest}
>
<span className="icon pulse-c" />
Send Test Alert
</button>
</div>
</form>
@ -103,6 +125,8 @@ OpsGenieConfig.propTypes = {
}).isRequired,
}).isRequired,
onSave: func.isRequired,
onTest: func.isRequired,
enabled: bool.isRequired,
}
class TagInput extends Component {
@ -121,6 +145,7 @@ class TagInput extends Component {
this.input.value = ''
onAddTag(newItem)
this.props.disableTest()
}
}
@ -156,6 +181,7 @@ TagInput.propTypes = {
onDeleteTag: func.isRequired,
tags: arrayOf(string).isRequired,
title: string.isRequired,
disableTest: func.isRequired,
}
const Tags = ({tags, onDeleteTag}) =>

View File

@ -4,9 +4,12 @@ import RedactedInput from './RedactedInput'
class PagerDutyConfig extends Component {
constructor(props) {
super(props)
this.state = {
testEnabled: this.props.enabled,
}
}
handleSaveAlert = e => {
handleSubmit = e => {
e.preventDefault()
const properties = {
@ -15,6 +18,11 @@ class PagerDutyConfig extends Component {
}
this.props.onSave(properties)
this.setState({testEnabled: true})
}
disableTest = () => {
this.setState({testEnabled: false})
}
render() {
@ -23,13 +31,14 @@ class PagerDutyConfig extends Component {
const serviceKey = options['service-key']
const refFunc = r => (this.serviceKey = r)
return (
<form onSubmit={this.handleSaveAlert}>
<form onSubmit={this.handleSubmit}>
<div className="form-group col-xs-12">
<label htmlFor="service-key">Service Key</label>
<RedactedInput
defaultValue={serviceKey || ''}
id="service-key"
refFunc={refFunc}
disableTest={this.disableTest}
/>
</div>
@ -41,12 +50,26 @@ class PagerDutyConfig extends Component {
type="text"
ref={r => (this.url = r)}
defaultValue={url || ''}
onChange={this.disableTest}
/>
</div>
<div className="form-group-submit col-xs-12 text-center">
<button className="btn btn-primary" type="submit">
Update PagerDuty Config
<button
className="btn btn-primary"
type="submit"
disabled={this.state.testEnabled}
>
<span className="icon checkmark" />
Save Changes
</button>
<button
className="btn btn-primary"
disabled={!this.state.testEnabled}
onClick={this.props.onTest}
>
<span className="icon pulse-c" />
Send Test Alert
</button>
</div>
</form>
@ -64,6 +87,8 @@ PagerDutyConfig.propTypes = {
}).isRequired,
}).isRequired,
onSave: func.isRequired,
onTest: func.isRequired,
enabled: bool.isRequired,
}
export default PagerDutyConfig

View File

@ -8,9 +8,12 @@ import {PUSHOVER_DOCS_LINK} from 'src/kapacitor/copy'
class PushoverConfig extends Component {
constructor(props) {
super(props)
this.state = {
testEnabled: this.props.enabled,
}
}
handleSaveAlert = e => {
handleSubmit = e => {
e.preventDefault()
const properties = {
@ -20,6 +23,11 @@ class PushoverConfig extends Component {
}
this.props.onSave(properties)
this.setState({testEnabled: true})
}
disableTest = () => {
this.setState({testEnabled: false})
}
handleUserKeyRef = r => (this.userKey = r)
@ -32,7 +40,7 @@ class PushoverConfig extends Component {
const userKey = options['user-key']
return (
<form onSubmit={this.handleSaveAlert}>
<form onSubmit={this.handleSubmit}>
<div className="form-group col-xs-12">
<label htmlFor="user-key">
User Key
@ -45,6 +53,7 @@ class PushoverConfig extends Component {
defaultValue={userKey}
id="user-key"
refFunc={this.handleUserKeyRef}
disableTest={this.disableTest}
/>
</div>
@ -60,6 +69,7 @@ class PushoverConfig extends Component {
defaultValue={token}
id="token"
refFunc={this.handleTokenRef}
disableTest={this.disableTest}
/>
</div>
@ -71,12 +81,26 @@ class PushoverConfig extends Component {
type="text"
ref={r => (this.url = r)}
defaultValue={url || ''}
onChange={this.disableTest}
/>
</div>
<div className="form-group-submit col-xs-12 text-center">
<button className="btn btn-primary" type="submit">
Update Pushover Config
<button
className="btn btn-primary"
type="submit"
disabled={this.state.testEnabled}
>
<span className="icon checkmark" />
Save Changes
</button>
<button
className="btn btn-primary"
disabled={!this.state.testEnabled}
onClick={this.props.onTest}
>
<span className="icon pulse-c" />
Send Test Alert
</button>
</div>
</form>
@ -95,6 +119,8 @@ PushoverConfig.propTypes = {
}).isRequired,
}).isRequired,
onSave: func.isRequired,
onTest: func.isRequired,
enabled: bool.isRequired,
}
export default PushoverConfig

View File

@ -13,7 +13,7 @@ class RedactedInput extends Component {
}
render() {
const {defaultValue, id, refFunc} = this.props
const {defaultValue, id, refFunc, disableTest} = this.props
const {editing} = this.state
if (defaultValue === true && !editing) {
@ -43,6 +43,7 @@ class RedactedInput extends Component {
type="text"
ref={refFunc}
defaultValue={''}
onChange={disableTest}
/>
)
}
@ -54,6 +55,7 @@ RedactedInput.propTypes = {
id: string.isRequired,
defaultValue: bool,
refFunc: func.isRequired,
disableTest: func,
}
export default RedactedInput

View File

@ -3,9 +3,12 @@ import React, {PropTypes, Component} from 'react'
class SMTPConfig extends Component {
constructor(props) {
super(props)
this.state = {
testEnabled: this.props.enabled,
}
}
handleSaveAlert = e => {
handleSubmit = e => {
e.preventDefault()
const properties = {
@ -17,13 +20,18 @@ class SMTPConfig extends Component {
}
this.props.onSave(properties)
this.setState({testEnabled: true})
}
disableTest = () => {
this.setState({testEnabled: false})
}
render() {
const {host, port, from, username, password} = this.props.config.options
return (
<form onSubmit={this.handleSaveAlert}>
<form onSubmit={this.handleSubmit}>
<div className="form-group col-xs-12 col-md-6">
<label htmlFor="smtp-host">SMTP Host</label>
<input
@ -32,6 +40,7 @@ class SMTPConfig extends Component {
type="text"
ref={r => (this.host = r)}
defaultValue={host || ''}
onChange={this.disableTest}
/>
</div>
@ -43,6 +52,7 @@ class SMTPConfig extends Component {
type="text"
ref={r => (this.port = r)}
defaultValue={port || ''}
onChange={this.disableTest}
/>
</div>
@ -55,6 +65,7 @@ class SMTPConfig extends Component {
type="text"
ref={r => (this.from = r)}
defaultValue={from || ''}
onChange={this.disableTest}
/>
</div>
@ -66,6 +77,7 @@ class SMTPConfig extends Component {
type="text"
ref={r => (this.username = r)}
defaultValue={username || ''}
onChange={this.disableTest}
/>
</div>
@ -77,12 +89,26 @@ class SMTPConfig extends Component {
type="password"
ref={r => (this.password = r)}
defaultValue={`${password}`}
onChange={this.disableTest}
/>
</div>
<div className="form-group-submit col-xs-12 text-center">
<button className="btn btn-primary" type="submit">
Update SMTP Config
<button
className="btn btn-primary"
type="submit"
disabled={this.state.testEnabled}
>
<span className="icon checkmark" />
Save Changes
</button>
<button
className="btn btn-primary"
disabled={!this.state.testEnabled}
onClick={this.props.onTest}
>
<span className="icon pulse-c" />
Send Test Alert
</button>
</div>
</form>
@ -103,6 +129,8 @@ SMTPConfig.propTypes = {
}).isRequired,
}).isRequired,
onSave: func.isRequired,
onTest: func.isRequired,
enabled: bool.isRequired,
}
export default SMTPConfig

View File

@ -3,9 +3,12 @@ import React, {PropTypes, Component} from 'react'
class SensuConfig extends Component {
constructor(props) {
super(props)
this.state = {
testEnabled: this.props.enabled,
}
}
handleSaveAlert = e => {
handleSubmit = e => {
e.preventDefault()
const properties = {
@ -14,13 +17,18 @@ class SensuConfig extends Component {
}
this.props.onSave(properties)
this.setState({testEnabled: true})
}
disableTest = () => {
this.setState({testEnabled: false})
}
render() {
const {source, addr} = this.props.config.options
return (
<form onSubmit={this.handleSaveAlert}>
<form onSubmit={this.handleSubmit}>
<div className="form-group col-xs-12 col-md-6">
<label htmlFor="source">Source</label>
<input
@ -29,6 +37,7 @@ class SensuConfig extends Component {
type="text"
ref={r => (this.source = r)}
defaultValue={source || ''}
onChange={this.disableTest}
/>
</div>
@ -40,12 +49,26 @@ class SensuConfig extends Component {
type="text"
ref={r => (this.addr = r)}
defaultValue={addr || ''}
onChange={this.disableTest}
/>
</div>
<div className="form-group-submit col-xs-12 text-center">
<button className="btn btn-primary" type="submit">
Update Sensu Config
<button
className="btn btn-primary"
type="submit"
disabled={this.state.testEnabled}
>
<span className="icon checkmark" />
Save Changes
</button>
<button
className="btn btn-primary"
disabled={!this.state.testEnabled}
onClick={this.props.onTest}
>
<span className="icon pulse-c" />
Send Test Alert
</button>
</div>
</form>
@ -53,7 +76,7 @@ class SensuConfig extends Component {
}
}
const {func, shape, string} = PropTypes
const {bool, func, shape, string} = PropTypes
SensuConfig.propTypes = {
config: shape({
@ -63,6 +86,8 @@ SensuConfig.propTypes = {
}).isRequired,
}).isRequired,
onSave: func.isRequired,
onTest: func.isRequired,
enabled: bool.isRequired,
}
export default SensuConfig

View File

@ -6,25 +6,21 @@ class SlackConfig extends Component {
constructor(props) {
super(props)
this.state = {
testEnabled: !!this.props.config.options.url,
testEnabled: this.props.enabled,
}
}
componentWillReceiveProps(nextProps) {
this.setState({
testEnabled: !!nextProps.config.options.url,
})
}
handleSaveAlert = e => {
handleSubmit = e => {
e.preventDefault()
const properties = {
url: this.url.value,
channel: this.channel.value,
}
this.props.onSave(properties)
this.setState({testEnabled: true})
}
disableTest = () => {
this.setState({testEnabled: false})
}
handleUrlRef = r => (this.url = r)
@ -33,7 +29,7 @@ class SlackConfig extends Component {
const {url, channel} = this.props.config.options
return (
<form onSubmit={this.handleSaveAlert}>
<form onSubmit={this.handleSubmit}>
<div className="form-group col-xs-12">
<label htmlFor="slack-url">
Slack Webhook URL (
@ -46,6 +42,7 @@ class SlackConfig extends Component {
defaultValue={url}
id="url"
refFunc={this.handleUrlRef}
disableTest={this.disableTest}
/>
</div>
@ -58,14 +55,30 @@ class SlackConfig extends Component {
placeholder="#alerts"
ref={r => (this.channel = r)}
defaultValue={channel || ''}
onChange={this.disableTest}
/>
</div>
<div className="form-group-submit col-xs-12 text-center">
<button className="btn btn-primary" type="submit">
Update Slack Config
<button
className="btn btn-primary"
type="submit"
disabled={this.state.testEnabled}
>
<span className="icon checkmark" />
Save Changes
</button>
<button
className="btn btn-primary"
disabled={!this.state.testEnabled}
onClick={this.props.onTest}
>
<span className="icon pulse-c" />
Send Test Alert
</button>
</div>
<br />
<br />
</form>
)
}
@ -81,6 +94,8 @@ SlackConfig.propTypes = {
}).isRequired,
}).isRequired,
onSave: func.isRequired,
onTest: func.isRequired,
enabled: bool.isRequired,
}
export default SlackConfig

View File

@ -5,9 +5,12 @@ import RedactedInput from './RedactedInput'
class TalkConfig extends Component {
constructor(props) {
super(props)
this.state = {
testEnabled: this.props.enabled,
}
}
handleSaveAlert = e => {
handleSubmit = e => {
e.preventDefault()
const properties = {
@ -16,6 +19,11 @@ class TalkConfig extends Component {
}
this.props.onSave(properties)
this.setState({testEnabled: true})
}
disableTest = () => {
this.setState({testEnabled: false})
}
handleUrlRef = r => (this.url = r)
@ -24,13 +32,14 @@ class TalkConfig extends Component {
const {url, author_name: author} = this.props.config.options
return (
<form onSubmit={this.handleSaveAlert}>
<form onSubmit={this.handleSubmit}>
<div className="form-group col-xs-12">
<label htmlFor="url">URL</label>
<RedactedInput
defaultValue={url}
id="url"
refFunc={this.handleUrlRef}
disableTest={this.disableTest}
/>
</div>
@ -42,12 +51,26 @@ class TalkConfig extends Component {
type="text"
ref={r => (this.author = r)}
defaultValue={author || ''}
onChange={this.disableTest}
/>
</div>
<div className="form-group-submit col-xs-12 text-center">
<button className="btn btn-primary" type="submit">
Update Talk Config
<button
className="btn btn-primary"
type="submit"
disabled={this.state.testEnabled}
>
<span className="icon checkmark" />
Save Changes
</button>
<button
className="btn btn-primary"
disabled={!this.state.testEnabled}
onClick={this.props.onTest}
>
<span className="icon pulse-c" />
Send Test Alert
</button>
</div>
</form>
@ -65,6 +88,8 @@ TalkConfig.propTypes = {
}).isRequired,
}).isRequired,
onSave: func.isRequired,
onTest: func.isRequired,
enabled: bool.isRequired,
}
export default TalkConfig

View File

@ -7,9 +7,12 @@ import RedactedInput from './RedactedInput'
class TelegramConfig extends Component {
constructor(props) {
super(props)
this.state = {
testEnabled: this.props.enabled,
}
}
handleSaveAlert = e => {
handleSubmit = e => {
e.preventDefault()
let parseMode
@ -29,6 +32,11 @@ class TelegramConfig extends Component {
}
this.props.onSave(properties)
this.setState({testEnabled: true})
}
disableTest = () => {
this.setState({testEnabled: false})
}
handleTokenRef = r => (this.token = r)
@ -42,7 +50,7 @@ class TelegramConfig extends Component {
const parseMode = options['parse-mode']
return (
<form onSubmit={this.handleSaveAlert}>
<form onSubmit={this.handleSubmit}>
<div className="form-group col-xs-12">
<div className="alert alert-warning alert-icon no-user-select">
<span className="icon triangle" />
@ -68,6 +76,7 @@ class TelegramConfig extends Component {
defaultValue={token}
id="token"
refFunc={this.handleTokenRef}
disableTest={this.disableTest}
/>
</div>
@ -86,6 +95,7 @@ class TelegramConfig extends Component {
placeholder="your-telegram-chat-id"
ref={r => (this.chatID = r)}
defaultValue={chatID || ''}
onChange={this.disableTest}
/>
</div>
@ -100,6 +110,7 @@ class TelegramConfig extends Component {
value="markdown"
defaultChecked={parseMode !== 'HTML'}
ref={r => (this.parseModeMarkdown = r)}
onChange={this.disableTest}
/>
<label htmlFor="parseModeMarkdown">Markdown</label>
</div>
@ -111,6 +122,7 @@ class TelegramConfig extends Component {
value="html"
defaultChecked={parseMode === 'HTML'}
ref={r => (this.parseModeHTML = r)}
onChange={this.disableTest}
/>
<label htmlFor="parseModeHTML">HTML</label>
</div>
@ -124,6 +136,7 @@ class TelegramConfig extends Component {
type="checkbox"
defaultChecked={disableWebPagePreview}
ref={r => (this.disableWebPagePreview = r)}
onChange={this.disableTest}
/>
<label htmlFor="disableWebPagePreview">
Disable{' '}
@ -142,6 +155,7 @@ class TelegramConfig extends Component {
type="checkbox"
defaultChecked={disableNotification}
ref={r => (this.disableNotification = r)}
onChange={this.disableTest}
/>
<label htmlFor="disableNotification">
Disable notifications on iOS devices and disable sounds on Android
@ -151,8 +165,21 @@ class TelegramConfig extends Component {
</div>
<div className="form-group-submit col-xs-12 text-center">
<button className="btn btn-primary" type="submit">
Update Telegram Config
<button
className="btn btn-primary"
type="submit"
disabled={this.state.testEnabled}
>
<span className="icon checkmark" />
Save Changes
</button>
<button
className="btn btn-primary"
disabled={!this.state.testEnabled}
onClick={this.props.onTest}
>
<span className="icon pulse-c" />
Send Test Alert
</button>
</div>
</form>
@ -173,6 +200,8 @@ TelegramConfig.propTypes = {
}).isRequired,
}).isRequired,
onSave: func.isRequired,
onTest: func.isRequired,
enabled: bool.isRequired,
}
export default TelegramConfig

View File

@ -5,9 +5,12 @@ import RedactedInput from './RedactedInput'
class VictorOpsConfig extends Component {
constructor(props) {
super(props)
this.state = {
testEnabled: this.props.enabled,
}
}
handleSaveAlert = e => {
handleSubmit = e => {
e.preventDefault()
const properties = {
@ -17,6 +20,11 @@ class VictorOpsConfig extends Component {
}
this.props.onSave(properties)
this.setState({testEnabled: true})
}
disableTest = () => {
this.setState({testEnabled: false})
}
handleApiRef = r => (this.apiKey = r)
@ -28,13 +36,14 @@ class VictorOpsConfig extends Component {
const {url} = options
return (
<form onSubmit={this.handleSaveAlert}>
<form onSubmit={this.handleSubmit}>
<div className="form-group col-xs-12">
<label htmlFor="api-key">API Key</label>
<RedactedInput
defaultValue={apiKey}
id="api-key"
refFunc={this.handleApiRef}
disableTest={this.disableTest}
/>
</div>
@ -46,6 +55,7 @@ class VictorOpsConfig extends Component {
type="text"
ref={r => (this.routingKey = r)}
defaultValue={routingKey || ''}
onChange={this.disableTest}
/>
</div>
@ -57,12 +67,26 @@ class VictorOpsConfig extends Component {
type="text"
ref={r => (this.url = r)}
defaultValue={url || ''}
onChange={this.disableTest}
/>
</div>
<div className="form-group-submit col-xs-12 text-center">
<button className="btn btn-primary" type="submit">
Update VictorOps Config
<button
className="btn btn-primary"
type="submit"
disabled={this.state.testEnabled}
>
<span className="icon checkmark" />
Save Changes
</button>
<button
className="btn btn-primary"
disabled={!this.state.testEnabled}
onClick={this.props.onTest}
>
<span className="icon pulse-c" />
Send Test Alert
</button>
</div>
</form>
@ -81,6 +105,8 @@ VictorOpsConfig.propTypes = {
}).isRequired,
}).isRequired,
onSave: func.isRequired,
onTest: func.isRequired,
enabled: bool.isRequired,
}
export default VictorOpsConfig

View File

@ -152,20 +152,18 @@ export function updateKapacitorConfigSection(kapacitor, section, properties) {
})
}
export function testAlertOutput(kapacitor, outputName, properties) {
return kapacitorProxy(
kapacitor,
'GET',
'/kapacitor/v1/service-tests'
).then(({data: {services}}) => {
const service = services.find(s => s.name === outputName)
return kapacitorProxy(
export const testAlertOutput = async (kapacitor, outputName) => {
try {
const {data: {services}} = await kapacitorProxy(
kapacitor,
'POST',
service.link.href,
Object.assign({}, service.options, properties)
'GET',
'/kapacitor/v1/service-tests'
)
})
const service = services.find(s => s.name === outputName)
return kapacitorProxy(kapacitor, 'POST', service.link.href, {})
} catch (error) {
console.error(error)
}
}
export function createKapacitorTask(kapacitor, id, type, dbrps, script) {

View File

@ -6,7 +6,7 @@
margin: 0 0 0 4px;
}
.text-center .btn {
margin: 0 2px;
margin: 0 6px;
}
.default-source-label {
display: inline-block;