feat(ui): create telegraf config (#16157)

pull/16186/head
Alex Boatwright 2019-12-10 09:07:18 -08:00 committed by GitHub
parent 4f5ff962d6
commit c21e08924b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 818 additions and 110 deletions

View File

@ -168,6 +168,7 @@
"react-dnd-html5-backend": "^2.6.0",
"react-dom": "^16.8.2",
"react-grid-layout": "^0.16.6",
"react-loadable": "^5.5.0",
"react-markdown": "^4.0.3",
"react-monaco-editor": "^0.32.1",
"react-redux": "^5.1.2",

View File

@ -7,7 +7,8 @@ import {Overlay} from '@influxdata/clockface'
import {ErrorHandling} from 'src/shared/decorators/errors'
interface Props {
children: any
children: string | React.ReactNode
footer?: string | React.ReactNode
title: string
maxWidth: number
onDismiss: () => void
@ -20,7 +21,7 @@ class WizardOverlay extends PureComponent<Props> {
}
public render() {
const {title, maxWidth, children, onDismiss} = this.props
const {title, maxWidth, children, footer, onDismiss} = this.props
return (
<Overlay visible={true}>
@ -29,6 +30,7 @@ class WizardOverlay extends PureComponent<Props> {
<Overlay.Body>
<div className="data-loading--overlay">{children}</div>
</Overlay.Body>
{footer && <Overlay.Footer>{footer}</Overlay.Footer>}
</Overlay.Container>
</Overlay>
)

View File

@ -6,14 +6,14 @@ import {
export type PluginAction = ReturnType<typeof setPlugins>
const setPlugins = (plugins: TelegrafEditorPluginState) => ({
export const setPlugins = (plugins: TelegrafEditorPluginState) => ({
type: 'SET_TELEGRAF_EDITOR_PLUGINS' as 'SET_TELEGRAF_EDITOR_PLUGINS',
payload: plugins,
})
export type ActivePluginAction = ReturnType<typeof setActivePlugins>
const setActivePlugins = (plugins: TelegrafEditorActivePluginState) => ({
export const setActivePlugins = (plugins: TelegrafEditorActivePluginState) => ({
type: 'SET_TELEGRAF_EDITOR_ACTIVE_PLUGINS' as 'SET_TELEGRAF_EDITOR_ACTIVE_PLUGINS',
payload: plugins,
})

View File

@ -0,0 +1,31 @@
@import '~src/style/_influx-colors.scss';
.telegraf-editor--plugins {
.display {
color: $g7-graphite;
margin-top: 8px;
padding: 0 12px;
}
.entry {
cursor: pointer;
padding: 0 12px;
padding-left: 24px;
label {
font-size: 12px;
color: $g9-mountain;
display: block;
margin-bottom: 4px;
cursor: pointer;
}
}
.input,
.output,
.processor,
.aggregator,
.bundle {
@extend .entry;
}
}

View File

@ -0,0 +1,125 @@
// Libraries
import React, {PureComponent} from 'react'
import {connect} from 'react-redux'
// Components
import {Grid, Columns} from '@influxdata/clockface'
import TelegrafEditorSidebar from 'src/dataLoaders/components/TelegrafEditorSidebar'
import TelegrafEditorMonaco from 'src/dataLoaders/components/TelegrafEditorMonaco'
// Styles
import './TelegrafEditor.scss'
// Utils
import {ErrorHandling} from 'src/shared/decorators/errors'
// Types
import {AppState} from 'src/types'
import {
TelegrafEditorActivePlugin,
TelegrafEditorPlugin,
TelegrafEditorBasicPlugin,
} from 'src/dataLoaders/reducers/telegrafEditor'
type AllPlugin = TelegrafEditorPlugin | TelegrafEditorActivePlugin
interface StateProps {
pluginHashMap: {[k: string]: AllPlugin}
}
type Props = StateProps
@ErrorHandling
class TelegrafEditor extends PureComponent<Props> {
_editor: any = null
render() {
return (
<Grid style={{height: '100%'}}>
<Grid.Row style={{textAlign: 'center'}}>
<h3 className="wizard-step--title">What do you want to monitor?</h3>
<h5 className="wizard-step--sub-title">
Telegraf is a plugin-based data collection agent which writes
metrics to a bucket in InfluxDB
<br />
Use the editor below to configure as many of the 200+{' '}
<a
href="https://v2.docs.influxdata.com/v2.0/reference/telegraf-plugins/#input-plugins"
target="_blank"
>
plugins
</a>{' '}
as you require
</h5>
</Grid.Row>
<Grid.Row style={{height: 'calc(100% - 128px)'}}>
<TelegrafEditorSidebar
onJump={this.handleJump}
onAdd={this.handleAdd}
/>
<Grid.Column widthXS={Columns.Nine} style={{height: '100%'}}>
<TelegrafEditorMonaco ref={this.connect} />
</Grid.Column>
</Grid.Row>
</Grid>
)
}
private connect = (elem: any) => {
this._editor = elem
}
private handleJump = (which: TelegrafEditorActivePlugin) => {
this._editor.getWrappedInstance().jump(which.line)
}
private handleAdd = (which: TelegrafEditorPlugin) => {
const editor = this._editor.getWrappedInstance()
const line = editor.nextLine()
if (which.type === 'bundle') {
which.include
.filter(
item =>
this.props.pluginHashMap[item] &&
this.props.pluginHashMap[item].type !== 'bundle'
)
.map(
item =>
(
(this.props.pluginHashMap[item] as TelegrafEditorBasicPlugin) ||
{}
).code
)
.filter(i => !!i)
.reverse()
.forEach(item => {
editor.insert(item, line)
})
} else {
editor.insert(which.code || '', line)
}
editor.jump(line)
}
}
const mstp = (state: AppState): StateProps => {
const pluginHashMap = state.telegrafEditorPlugins
.filter(
(a: TelegrafEditorPlugin) => a.type !== 'bundle' || !!a.include.length
)
.reduce((prev, curr) => {
prev[curr.name] = curr
return prev
}, {})
return {
pluginHashMap,
}
}
export default connect<StateProps, {}>(
mstp,
null
)(TelegrafEditor)

View File

@ -0,0 +1,150 @@
import React, {PureComponent} from 'react'
import {connect} from 'react-redux'
import {AppState} from 'src/types'
import Editor from 'src/shared/components/TomlMonacoEditor'
import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api'
import {setText, setActivePlugins} from 'src/dataLoaders/actions/telegrafEditor'
import {TelegrafEditorPluginType} from 'src/dataLoaders/reducers/telegrafEditor'
const PLUGIN_REGEX = /\[\[\s*(inputs|outputs|processors|aggregators)\.(.+)\s*\]\]/
interface DispatchProps {
onSetText: typeof setText
onSetActivePlugins: typeof setActivePlugins
}
interface StateProps {
script: string
}
type Props = StateProps & DispatchProps
interface InterumMatchFormat {
name: string
type: TelegrafEditorPluginType
line: number
}
class TelegrafEditorMonaco extends PureComponent<Props> {
_editor: monacoEditor.editor.IStandaloneCodeEditor = null
render() {
const {script} = this.props
return (
<Editor
script={script}
onChangeScript={this.handleChange}
willMount={this.connect}
/>
)
}
private extractPluginList() {
if (!this._editor) {
return
}
const matches: Array<
monacoEditor.editor.FindMatch
> = this._editor
.getModel()
.findMatches(PLUGIN_REGEX as any, false, true, false, null, true)
const plugins = matches.map(
(m: monacoEditor.editor.FindMatch): InterumMatchFormat => {
return {
type: m.matches[1].slice(0, -1) as TelegrafEditorPluginType,
name: m.matches[2],
line: m.range.startLineNumber,
}
}
)
this.props.onSetActivePlugins(plugins)
}
private connect = (editor: monacoEditor.editor.IStandaloneCodeEditor) => {
this._editor = editor
this.extractPluginList()
}
private handleChange = (evt: string) => {
this.extractPluginList()
this.props.onSetText(evt)
}
public jump(line: number) {
this._editor.revealLineInCenter(line)
}
public nextLine(): number {
const position = this._editor.getPosition()
const matches = this._editor
.getModel()
.findNextMatch(PLUGIN_REGEX as any, position, true, false, null, true)
let lineNumber
if (
position.lineNumber === 1 ||
!matches ||
position.lineNumber > matches.range.startLineNumber
) {
//add it to the bottom
lineNumber = this._editor.getModel().getLineCount()
} else {
lineNumber = matches.range.startLineNumber - 1
}
return lineNumber
}
public insert(text: string, line: number) {
this._editor.setPosition({column: 1, lineNumber: line})
this._editor.executeEdits('', [
{
range: {
startLineNumber: line,
startColumn: 1,
endLineNumber: line,
endColumn: 1,
} as monacoEditor.Range,
text: text,
forceMoveMarkers: true,
},
])
}
}
export {TelegrafEditorMonaco}
const mstp = (state: AppState): StateProps => {
const map = state.telegrafEditorPlugins.reduce((prev, curr) => {
prev[curr.name] = curr
return prev
}, {})
const script =
state.telegrafEditor.text ||
map['__default__'].include
.map((i: string) => {
return map[i].code
})
.join('\n')
return {
script,
}
}
const mdtp: DispatchProps = {
onSetActivePlugins: setActivePlugins,
onSetText: setText,
}
export default connect<StateProps, DispatchProps>(
mstp,
mdtp,
null,
{withRef: true}
)(TelegrafEditorMonaco)

View File

@ -0,0 +1,108 @@
import React, {PureComponent} from 'react'
import FancyScrollbar from 'src/shared/components/fancy_scrollbar/FancyScrollbar'
import {
TelegrafEditorPluginState,
TelegrafEditorActivePluginState,
TelegrafEditorActivePlugin,
TelegrafEditorPlugin,
} from 'src/dataLoaders/reducers/telegrafEditor'
type ListPlugin = TelegrafEditorPlugin | TelegrafEditorActivePlugin
interface InterimListFormat {
category: string
items: Array<ListPlugin>
}
function groupPlugins(plugins: Array<ListPlugin>, pluginFilter: string) {
const map = plugins.reduce(
(prev: {[k: string]: Array<ListPlugin>}, curr: ListPlugin) => {
if (curr.name === '__default__') {
return prev
}
if (!prev.hasOwnProperty(curr.type)) {
prev[curr.type] = []
}
prev[curr.type].push(curr)
return prev
},
{}
)
return ['bundle', 'input', 'output', 'processor', 'aggregator']
.map(
(k: string): InterimListFormat => {
return {
category: k,
items: (map[k] || []).filter((a: ListPlugin) =>
(a.name || '').includes(pluginFilter)
),
}
}
)
.filter((k: InterimListFormat) => k.items.length)
.reduce((prev, curr) => {
prev.push({
type: 'display',
name: '-- ' + curr.category + ' --',
})
const items = curr.items.slice(0).sort((a: ListPlugin, b: ListPlugin) => {
return (a.name || '').localeCompare(b.name || '')
})
prev.push(...items)
return prev
}, [])
}
interface PluginProps {
plugins: TelegrafEditorPluginState | TelegrafEditorActivePluginState
filter: string
onClick: (which: ListPlugin) => void
}
class PluginList extends PureComponent<PluginProps> {
render() {
const {plugins, filter, onClick} = this.props
const list = groupPlugins(plugins, filter).map((k: ListPlugin) => {
if (k.type === 'display') {
return (
<div className={k.type} key={`_plugin_${k.type}.${k.name}`}>
{k.name}
</div>
)
}
let description
// NOTE: written this way to bypass typescript: alex
if (k['description']) {
description = <label>{k['description']}</label>
}
return (
<div
className={k.type}
key={`_plugin_${k.type}.${k.name}`}
onClick={() => onClick(k)}
>
{k.name}
{description}
</div>
)
})
return (
<FancyScrollbar autoHide={false} className="telegraf-editor--plugins">
{list}
</FancyScrollbar>
)
}
}
export default PluginList

View File

@ -0,0 +1,181 @@
import React, {PureComponent, SyntheticEvent, ChangeEvent} from 'react'
import {connect} from 'react-redux'
import PluginList from 'src/dataLoaders/components/TelegrafEditorPluginList'
import BucketDropdown from 'src/dataLoaders/components/BucketsDropdown'
import {AppState, Bucket} from 'src/types'
import {
TelegrafEditorPluginState,
TelegrafEditorActivePluginState,
TelegrafEditorPlugin,
TelegrafEditorActivePlugin,
} from 'src/dataLoaders/reducers/telegrafEditor'
import {
setFilter,
setBucket,
setMode,
} from 'src/dataLoaders/actions/telegrafEditor'
import {
Input,
IconFont,
FormElement,
Grid,
Columns,
Tabs,
ComponentSize,
Orientation,
} from '@influxdata/clockface'
interface PluginStateProps {
plugins: TelegrafEditorPluginState
filter: string
}
interface ActivePluginStateProps {
plugins: TelegrafEditorActivePluginState
filter: string
}
const mstp_1 = (state: AppState): ActivePluginStateProps => {
const plugins = state.telegrafEditorActivePlugins || []
const filter = state.telegrafEditor.filter
return {
plugins,
filter,
}
}
const ActivePluginList = connect<ActivePluginStateProps, {}>(
mstp_1,
null
)(PluginList)
const mstp_2 = (state: AppState): PluginStateProps => {
const plugins = state.telegrafEditorPlugins || []
const filter = state.telegrafEditor.filter
return {
plugins,
filter,
}
}
const AllPluginList = connect<PluginStateProps, {}>(
mstp_2,
null
)(PluginList)
interface StateProps {
buckets: Bucket[]
bucket: Bucket
filter: string
mode: 'adding' | 'indexing'
}
interface DispatchProps {
onSetFilter: typeof setFilter
onSetBucket: typeof setBucket
onSetMode: typeof setMode
}
interface OwnProps {
onJump: (which: TelegrafEditorActivePlugin) => void
onAdd: (which: TelegrafEditorPlugin) => void
}
type TelegrafEditorSidebarProps = StateProps & DispatchProps & OwnProps
class TelegrafEditorSideBar extends PureComponent<TelegrafEditorSidebarProps> {
render() {
const {
bucket,
buckets,
filter,
mode,
onAdd,
onJump,
onSetMode,
onSetBucket,
onSetFilter,
} = this.props
return (
<Grid.Column widthXS={Columns.Three} style={{height: '100%'}}>
<FormElement label="Bucket">
<BucketDropdown
buckets={buckets}
selectedBucketID={bucket.id}
onSelectBucket={onSetBucket}
/>
</FormElement>
<Input
className="wizard-step--filter"
size={ComponentSize.Small}
icon={IconFont.Search}
value={filter}
onBlur={(evt: SyntheticEvent<any>) => {
onSetFilter((evt.target as any).value)
}}
onChange={(evt: ChangeEvent<any>) => {
onSetFilter(evt.target.value)
}}
placeholder="Filter Plugins..."
/>
<Tabs.Container
orientation={Orientation.Horizontal}
style={{height: 'calc(100% - 114px)', marginTop: '18px'}}
>
<Tabs>
<Tabs.Tab
id="lookup"
text="Plugin Lookup"
active={mode === 'indexing'}
onClick={() => {
onSetMode('indexing')
}}
/>
<Tabs.Tab
id="add"
text="Add Plugins"
active={mode === 'adding'}
onClick={() => {
onSetMode('adding')
}}
/>
</Tabs>
<Tabs.TabContents padding={ComponentSize.Small}>
{mode === 'indexing' && <ActivePluginList onClick={onJump} />}
{mode === 'adding' && <AllPluginList onClick={onAdd} />}
</Tabs.TabContents>
</Tabs.Container>
</Grid.Column>
)
}
}
const mstp_3 = (state: AppState): StateProps => {
const filter = state.telegrafEditor.filter
const mode = state.telegrafEditor.mode
const buckets = state.buckets.list || []
const bucket =
state.telegrafEditor.bucket || buckets.length
? buckets[0]
: ({id: null} as Bucket)
return {
buckets,
bucket,
mode,
filter,
}
}
const mdtp_3: DispatchProps = {
onSetMode: setMode,
onSetBucket: setBucket,
onSetFilter: setFilter,
}
export default connect<StateProps, DispatchProps, {}>(
mstp_3,
mdtp_3
)(TelegrafEditorSideBar)

View File

@ -1,13 +1,29 @@
// Libraries
import React, {PureComponent} from 'react'
import Loadable from 'react-loadable'
import {connect} from 'react-redux'
import {withRouter, WithRouterProps} from 'react-router'
import _ from 'lodash'
// Components
import {ErrorHandling} from 'src/shared/decorators/errors'
import WizardOverlay from 'src/clockface/components/wizard/WizardOverlay'
import CollectorsStepSwitcher from 'src/dataLoaders/components/collectorsWizard/CollectorsStepSwitcher'
const spinner = <div />
const TelegrafEditor = Loadable({
loader: () => import('src/dataLoaders/components/TelegrafEditor'),
loading() {
return spinner
},
})
const CollectorsStepSwitcher = Loadable({
loader: () =>
import('src/dataLoaders/components/collectorsWizard/CollectorsStepSwitcher'),
loading() {
return spinner
},
})
import {isFlagEnabled, FeatureFlag} from 'src/shared/utils/featureFlag'
import {ComponentColor, Button} from '@influxdata/clockface'
// Actions
import {notify as notifyAction} from 'src/shared/actions/notifications'
@ -24,11 +40,13 @@ import {
setActiveTelegrafPlugin,
setPluginConfiguration,
} from 'src/dataLoaders/actions/dataLoaders'
import {reset} from 'src/dataLoaders/actions/telegrafEditor'
// Types
import {Links} from 'src/types/links'
import {Substep, TelegrafPlugin} from 'src/types/dataLoaders'
import {AppState, Bucket} from 'src/types'
import {AppState, Bucket, Organization} from 'src/types'
import {downloadTextFile} from 'src/shared/utils/download'
export interface CollectorsStepProps {
currentStepIndex: number
@ -46,6 +64,7 @@ interface DispatchProps {
onSetCurrentStepIndex: typeof setCurrentStepIndex
onClearDataLoaders: typeof clearDataLoaders
onClearSteps: typeof clearSteps
onClearTelegrafEditor: typeof reset
onSetActiveTelegrafPlugin: typeof setActiveTelegrafPlugin
onSetPluginConfiguration: typeof setPluginConfiguration
}
@ -58,6 +77,8 @@ interface StateProps {
substep: Substep
username: string
bucket: string
text: string
org: Organization
}
interface State {
@ -65,10 +86,11 @@ interface State {
}
type Props = StateProps & DispatchProps
type AllProps = Props & WithRouterProps
@ErrorHandling
class CollectorsWizard extends PureComponent<Props & WithRouterProps, State> {
constructor(props) {
class CollectorsWizard extends PureComponent<AllProps, State> {
constructor(props: AllProps) {
super(props)
this.state = {
buckets: [],
@ -86,8 +108,30 @@ class CollectorsWizard extends PureComponent<Props & WithRouterProps, State> {
<WizardOverlay
title="Create a Telegraf Config"
onDismiss={this.handleDismiss}
footer={
<FeatureFlag name="telegrafEditor">
<Button
color={ComponentColor.Secondary}
text="Download Config"
onClick={this.handleDownloadConfig}
/>
<Button
color={ComponentColor.Primary}
text="Save Config"
onClick={this.handleSaveConfig}
/>
</FeatureFlag>
}
>
<CollectorsStepSwitcher stepProps={this.stepProps} buckets={buckets} />
<FeatureFlag name="telegrafEditor">
<TelegrafEditor />
</FeatureFlag>
<FeatureFlag name="telegrafEditor" equals={false}>
<CollectorsStepSwitcher
stepProps={this.stepProps}
buckets={buckets}
/>
</FeatureFlag>
</WizardOverlay>
)
}
@ -101,12 +145,26 @@ class CollectorsWizard extends PureComponent<Props & WithRouterProps, State> {
}
}
private handleDismiss = () => {
const {router, onClearDataLoaders, onClearSteps} = this.props
private handleDownloadConfig = () => {
downloadTextFile(this.props.text, 'telegraf', '.conf')
}
onClearDataLoaders()
onClearSteps()
router.goBack()
private handleSaveConfig = () => {
this.handleDismiss()
}
private handleDismiss = () => {
const {router, org} = this.props
if (isFlagEnabled('telegrafEditor')) {
const {onClearTelegrafEditor} = this.props
onClearTelegrafEditor()
} else {
const {onClearDataLoaders, onClearSteps} = this.props
onClearDataLoaders()
onClearSteps()
}
router.push(`/orgs/${org.id}/load-data/telegrafs`)
}
private get stepProps(): CollectorsStepProps {
@ -135,14 +193,18 @@ const mstp = ({
steps: {currentStep, substep, bucket},
},
me: {name},
orgs: {org},
telegrafEditor,
}: AppState): StateProps => ({
links,
telegrafPlugins,
text: telegrafEditor.text,
currentStepIndex: currentStep,
substep,
username: name,
bucket,
buckets: buckets.list,
org: org,
})
const mdtp: DispatchProps = {
@ -153,6 +215,7 @@ const mdtp: DispatchProps = {
onSetCurrentStepIndex: setCurrentStepIndex,
onClearDataLoaders: clearDataLoaders,
onClearSteps: clearSteps,
onClearTelegrafEditor: reset,
onSetActiveTelegrafPlugin: setActiveTelegrafPlugin,
onSetPluginConfiguration: setPluginConfiguration,
}

View File

@ -4,9 +4,8 @@ import {
ActivePluginAction,
EditorAction,
} from 'src/dataLoaders/actions/telegrafEditor'
type TelegrafEditorPluginType =
export type TelegrafEditorPluginType =
| 'system'
| 'bundle'
| 'input'
| 'output'
| 'processor'
@ -14,24 +13,26 @@ type TelegrafEditorPluginType =
| 'display'
type TelegrafEditorPluginName = string
interface TelegrafEditorBasicPlugin {
export interface TelegrafEditorBasicPlugin {
name: TelegrafEditorPluginName
description: string
code: string
type: TelegrafEditorPluginType
}
interface TelegrafEditorBundlePlugin {
export interface TelegrafEditorBundlePlugin {
name: TelegrafEditorPluginName
description: string
type: string
type: 'bundle'
include: Array<TelegrafEditorPluginName>
}
export type TelegrafEditorPluginState = Array<
TelegrafEditorBasicPlugin | TelegrafEditorBundlePlugin
>
interface TelegrafEditorActivePlugin {
export type TelegrafEditorPlugin =
| TelegrafEditorBasicPlugin
| TelegrafEditorBundlePlugin
export type TelegrafEditorPluginState = Array<TelegrafEditorPlugin>
export interface TelegrafEditorActivePlugin {
name: string
type: TelegrafEditorPluginType
line: number
@ -40,7 +41,8 @@ interface TelegrafEditorActivePlugin {
export type TelegrafEditorActivePluginState = Array<TelegrafEditorActivePlugin>
type TelegrafEditorMode = 'adding' | 'indexing'
interface TelegrafEditorState {
export interface TelegrafEditorState {
mode: TelegrafEditorMode
bucket: Bucket | null
text: string
@ -52,7 +54,8 @@ const INITIAL_PLUGINS: TelegrafEditorPluginState = [
name: 'cpu',
type: 'input',
description: 'watch your cpu yo',
code: `[[inputs.cpu]]
code: `
[[inputs.cpu]]
## Whether to report per-cpu stats or not
percpu = true
## Whether to report total system cpu stats or not
@ -67,7 +70,8 @@ const INITIAL_PLUGINS: TelegrafEditorPluginState = [
name: 'disk',
type: 'input',
description: 'watch your disks yo',
code: `[[inputs.disk]]
code: `
[[inputs.disk]]
## By default stats will be gathered for all mount points.
## Set mount_points will restrict the stats to only the specified mount points.
# mount_points = ["/"]
@ -79,21 +83,24 @@ ignore_fs = ["tmpfs", "devtmpfs", "devfs", "overlay", "aufs", "squashfs"]
name: 'diskio',
type: 'input',
description: 'watch your diskio yo',
code: `[[inputs.diskio]]
code: `
[[inputs.diskio]]
`,
},
{
name: 'memory',
type: 'input',
description: 'watch your memory yo',
code: `[[inputs.mem]]
code: `
[[inputs.mem]]
`,
},
{
name: 'network',
type: 'input',
description: 'watch your network yo',
code: `[[inputs.net]]
code: `
[[inputs.net]]
`,
},
{
@ -106,10 +113,11 @@ ignore_fs = ["tmpfs", "devtmpfs", "devfs", "overlay", "aufs", "squashfs"]
name: 'kubernetes',
type: 'input',
description: 'watch your cluster yo',
code: `[[inputs.kubernetes]]
## URL for the kubelet
## exp: http://1.1.1.1:10255
url = "http://url"
code: `
[[inputs.kubernetes]]
## URL for the kubelet
## exp: http://1.1.1.1:10255
url = "http://url"
`,
},
{
@ -176,7 +184,8 @@ omit_hostname = false
name: 'influxdb_v2',
type: 'output',
description: 'output to the cloud',
code: `[[outputs.influxdb_v2]]
code: `
[[outputs.influxdb_v2]]
## The URLs of the InfluxDB cluster nodes.
##
## Multiple URLs can be specified for a single cluster, only ONE of the
@ -222,7 +231,7 @@ export function pluginsReducer(
}
export function activePluginsReducer(
state = [],
state: TelegrafEditorActivePluginState = [],
action: ActivePluginAction
): TelegrafEditorActivePluginState {
switch (action.type) {

View File

@ -26,12 +26,13 @@ import {
ComponentColor,
ComponentStatus,
} from '@influxdata/clockface'
import {AppState, Bucket} from 'src/types'
import {AppState, Bucket, Organization} from 'src/types'
import FilterList from 'src/shared/components/Filter'
interface StateProps {
scrapers: ScraperTargetResponse[]
buckets: Bucket[]
org: Organization
}
interface DispatchProps {
@ -39,11 +40,7 @@ interface DispatchProps {
onDeleteScraper: typeof deleteScraper
}
interface OwnProps {
orgName: string
}
type Props = OwnProps & StateProps & DispatchProps & WithRouterProps
type Props = StateProps & DispatchProps & WithRouterProps
interface State {
searchTerm: string
@ -142,14 +139,15 @@ class Scrapers extends PureComponent<Props, State> {
}
private get emptyState(): JSX.Element {
const {orgName} = this.props
const {org} = this.props
const {searchTerm} = this.state
if (_.isEmpty(searchTerm)) {
return (
<EmptyState size={ComponentSize.Large}>
<EmptyState.Text>
{`${orgName}`} does not own any <b>Scrapers</b>, why not create one?
{`${org.name}`} does not own any <b>Scrapers</b>, why not create
one?
</EmptyState.Text>
{this.createScraperButton('create-scraper-button-empty')}
</EmptyState>
@ -174,16 +172,13 @@ class Scrapers extends PureComponent<Props, State> {
}
private handleShowOverlay = () => {
const {
router,
params: {orgID},
} = this.props
const {router, org} = this.props
if (this.hasNoBuckets) {
return
}
router.push(`/orgs/${orgID}/load-data/scrapers/new`)
router.push(`/orgs/${org.id}/load-data/scrapers/new`)
}
private handleFilterChange = (searchTerm: string) => {
@ -191,9 +186,10 @@ class Scrapers extends PureComponent<Props, State> {
}
}
const mstp = ({scrapers, buckets}: AppState): StateProps => ({
const mstp = ({scrapers, buckets, orgs}: AppState): StateProps => ({
scrapers: scrapers.list,
buckets: buckets.list,
org: orgs.org,
})
const mdtp: DispatchProps = {
@ -201,7 +197,7 @@ const mdtp: DispatchProps = {
onUpdateScraper: updateScraper,
}
export default connect<StateProps, DispatchProps, OwnProps>(
export default connect<StateProps, DispatchProps>(
mstp,
mdtp
)(withRouter<OwnProps>(Scrapers))
)(withRouter(Scrapers))

View File

@ -35,7 +35,7 @@ class ScrapersIndex extends Component<StateProps> {
<GetResources
resources={[ResourceType.Scrapers, ResourceType.Buckets]}
>
<Scrapers orgName={org.name} />
<Scrapers />
</GetResources>
</LoadDataTabbedPage>
</Page>

View File

@ -6,6 +6,8 @@ import MonacoEditor from 'react-monaco-editor'
import addTomlTheme, {THEME_NAME} from 'src/external/monaco.tomlTheme'
import {addSyntax} from 'src/external/monaco.tomlSyntax'
import {OnChangeScript} from 'src/types/flux'
import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api'
import './FluxMonacoEditor.scss'
interface Position {
@ -15,31 +17,39 @@ interface Position {
interface Props {
script: string
className?: string
willMount?: (monaco: monacoEditor.editor.IStandaloneCodeEditor) => void
readOnly?: boolean
testID?: string
onChangeScript?: OnChangeScript
onSubmitScript?: () => void
onCursorChange?: (position: Position) => void
}
const TomlEditorMonaco: FC<Props> = props => {
const editorWillMount = monaco => {
const editorWillMount = (monaco: typeof monacoEditor) => {
addTomlTheme(monaco)
addSyntax(monaco)
}
const editorDidMount = editor => {
editor.onDidChangeCursorPosition(evt => {
const {position} = evt
const {onCursorChange} = props
const pos = {
line: position.lineNumber,
ch: position.column,
}
const editorDidMount = (
editor: monacoEditor.editor.IStandaloneCodeEditor
) => {
editor.onDidChangeCursorPosition(
(evt: monacoEditor.editor.ICursorPositionChangedEvent) => {
const {position} = evt
const {onCursorChange} = props
const pos = {
line: position.lineNumber,
ch: position.column,
}
if (onCursorChange) {
onCursorChange(pos)
if (onCursorChange) {
onCursorChange(pos)
}
}
})
)
editor.onKeyUp(evt => {
editor.onKeyUp((evt: monacoEditor.IKeyboardEvent) => {
const {ctrlKey, code} = evt
const {onSubmitScript} = props
if (ctrlKey && code === 'Enter') {
@ -48,11 +58,17 @@ const TomlEditorMonaco: FC<Props> = props => {
}
}
})
if (props.willMount) {
props.willMount(editor)
}
}
const {script, onChangeScript} = props
const {script, onChangeScript, readOnly} = props
const testID = props.testID || 'toml-editor'
const className = props.className || 'time-machine-editor--embedded'
return (
<div className="time-machine-editor--embedded" data-testid="toml-editor">
<div className={className} data-testid={testID}>
<MonacoEditor
language="toml"
theme={THEME_NAME}
@ -65,12 +81,11 @@ const TomlEditorMonaco: FC<Props> = props => {
lineNumbersMinChars: 4,
lineDecorationsWidth: 0,
minimap: {
enabled: false,
renderCharacters: false,
},
overviewRulerBorder: false,
automaticLayout: true,
readOnly: true,
readOnly: readOnly || false,
}}
editorWillMount={editorWillMount}
editorDidMount={editorDidMount}

View File

@ -7,6 +7,7 @@ const OSS_FLAGS = {
deleteWithPredicate: false,
monacoEditor: false,
downloadCellCSV: false,
telegrafEditor: false,
}
const CLOUD_FLAGS = {
@ -16,6 +17,7 @@ const CLOUD_FLAGS = {
monacoEditor: false,
cloudBilling: CLOUD_BILLING_VISIBLE, // should be visible in dev and acceptance, but not in cloud
downloadCellCSV: false,
telegrafEditor: false,
}
export const isFlagEnabled = (flagName: string, equals?: string | boolean) => {

View File

@ -38,6 +38,7 @@ import endpointsReducer from 'src/alerting/reducers/notifications/endpoints'
import {
pluginsReducer,
activePluginsReducer,
editorReducer,
} from 'src/dataLoaders/reducers/telegrafEditor'
// Types
@ -67,6 +68,7 @@ export const rootReducer = combineReducers<ReducerState>({
buckets: bucketsReducer,
telegrafEditorPlugins: pluginsReducer,
telegrafEditorActivePlugins: activePluginsReducer,
telegrafEditor: editorReducer,
telegrafs: telegrafsReducer,
tokens: authorizationsReducer,
scrapers: scrapersReducer,

View File

@ -1,16 +1,25 @@
// Libraries
import React, {PureComponent, Suspense} from 'react'
import React, {PureComponent} from 'react'
import Loadable from 'react-loadable'
import {connect} from 'react-redux'
import {withRouter, WithRouterProps} from 'react-router'
// Components
import {ErrorHandling} from 'src/shared/decorators/errors'
const Editor = React.lazy(() =>
import('react-codemirror2').then(module => ({default: module.Controlled}))
)
const MonacoEditor = React.lazy(() =>
import('src/shared/components/TomlMonacoEditor')
)
const spinner = <div className="time-machine-editor--loading" />
const Editor = Loadable({
loader: () =>
import('react-codemirror2').then(module => ({default: module.Controlled})),
loading() {
return spinner
},
})
const MonacoEditor = Loadable({
loader: () => import('src/shared/components/TomlMonacoEditor'),
loading() {
return spinner
},
})
import {RemoteDataState} from '@influxdata/clockface'
import {FeatureFlag} from 'src/shared/utils/featureFlag'
@ -31,8 +40,6 @@ interface StateProps {
type Props = StateProps & DispatchProps
const spinner = <div className="time-machine-editor--loading" />
@ErrorHandling
export class TelegrafConfig extends PureComponent<Props & WithRouterProps> {
public componentDidMount() {
@ -53,9 +60,9 @@ export class TelegrafConfig extends PureComponent<Props & WithRouterProps> {
private get overlayBody(): JSX.Element {
const {telegrafConfig} = this.props
return (
<Suspense fallback={spinner}>
<>
<FeatureFlag name="monacoEditor">
<MonacoEditor script={telegrafConfig} />
<MonacoEditor script={telegrafConfig} readOnly />
</FeatureFlag>
<FeatureFlag name="monacoEditor" equals={false}>
<Editor
@ -73,7 +80,7 @@ export class TelegrafConfig extends PureComponent<Props & WithRouterProps> {
onTouchStart={this.onTouchStart}
/>
</FeatureFlag>
</Suspense>
</>
)
}
}

View File

@ -1,13 +1,24 @@
// Libraries
import React, {PureComponent, Suspense} from 'react'
import React, {PureComponent} from 'react'
import Loadable from 'react-loadable'
import {connect} from 'react-redux'
import {Position} from 'codemirror'
// Components
const FluxEditor = React.lazy(() => import('src/shared/components/FluxEditor'))
const FluxMonacoEditor = React.lazy(() =>
import('src/shared/components/FluxMonacoEditor')
)
const spinner = <div className="time-machine-editor--loading" />
const FluxEditor = Loadable({
loader: () => import('src/shared/components/FluxEditor'),
loading() {
return spinner
},
})
const FluxMonacoEditor = Loadable({
loader: () => import('src/shared/components/FluxMonacoEditor'),
loading() {
return spinner
},
})
import Threesizer from 'src/shared/components/threesizer/Threesizer'
import FluxFunctionsToolbar from 'src/timeMachine/components/fluxFunctionsToolbar/FluxFunctionsToolbar'
import VariableToolbar from 'src/timeMachine/components/variableToolbar/VariableToolbar'
@ -44,8 +55,6 @@ interface State {
type Props = StateProps & DispatchProps
const spinner = <div className="time-machine-editor--loading" />
class TimeMachineFluxEditor extends PureComponent<Props, State> {
private cursorPosition: Position = {line: 0, ch: 0}
@ -63,26 +72,24 @@ class TimeMachineFluxEditor extends PureComponent<Props, State> {
render: () => {
return (
<>
<Suspense fallback={spinner}>
<FeatureFlag name="monacoEditor">
<FluxMonacoEditor
script={activeQueryText}
onChangeScript={onSetActiveQueryText}
onSubmitScript={onSubmitQueries}
onCursorChange={this.handleCursorPosition}
/>
</FeatureFlag>
<FeatureFlag name="monacoEditor" equals={false}>
<FluxEditor
script={activeQueryText}
status={{type: '', text: ''}}
onChangeScript={onSetActiveQueryText}
onSubmitScript={onSubmitQueries}
suggestions={[]}
onCursorChange={this.handleCursorPosition}
/>
</FeatureFlag>
</Suspense>
<FeatureFlag name="monacoEditor">
<FluxMonacoEditor
script={activeQueryText}
onChangeScript={onSetActiveQueryText}
onSubmitScript={onSubmitQueries}
onCursorChange={this.handleCursorPosition}
/>
</FeatureFlag>
<FeatureFlag name="monacoEditor" equals={false}>
<FluxEditor
script={activeQueryText}
status={{type: '', text: ''}}
onChangeScript={onSetActiveQueryText}
onSubmitScript={onSubmitQueries}
suggestions={[]}
onCursorChange={this.handleCursorPosition}
/>
</FeatureFlag>
</>
)
},

View File

@ -16,6 +16,7 @@ import {BucketsState} from 'src/buckets/reducers'
import {
TelegrafEditorPluginState,
TelegrafEditorActivePluginState,
TelegrafEditorState,
} from 'src/dataLoaders/reducers/telegrafEditor'
import {TelegrafsState} from 'src/telegrafs/reducers'
import {TemplatesState} from 'src/templates/reducers'
@ -60,6 +61,7 @@ export interface AppState {
tasks: TasksState
telegrafEditorPlugins: TelegrafEditorPluginState
telegrafEditorActivePlugins: TelegrafEditorActivePluginState
telegrafEditor: TelegrafEditorState
telegrafs: TelegrafsState
templates: TemplatesState
timeMachines: TimeMachinesState

View File

@ -9371,7 +9371,7 @@ prop-types@15.x, prop-types@^15.5.10, prop-types@^15.5.6, prop-types@^15.5.8, pr
loose-envify "^1.3.1"
object-assign "^4.1.1"
prop-types@^15.0.0, prop-types@^15.7.2:
prop-types@^15.0.0, prop-types@^15.5.0, prop-types@^15.7.2:
version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@ -9708,6 +9708,13 @@ react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4:
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
react-loadable@^5.5.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/react-loadable/-/react-loadable-5.5.0.tgz#582251679d3da86c32aae2c8e689c59f1196d8c4"
integrity sha512-C8Aui0ZpMd4KokxRdVAm2bQtI03k2RMRNzOB+IipV3yxFTSVICv7WoUr5L9ALB5BmKO1iHgZtWM8EvYG83otdg==
dependencies:
prop-types "^15.5.0"
react-markdown@^4.0.3:
version "4.0.4"
resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-4.0.4.tgz#bdc882bc3eb4dfac45d57bfe58d8f482c5a85a64"