From 42ad3818ca1e6563a3b1b70e901a20382dc87dac Mon Sep 17 00:00:00 2001
From: Ariel Salem
Date: Thu, 19 Mar 2020 20:01:42 -0700
Subject: [PATCH] fix(ui): resolved telegraf bucket dropdown bug and undefined
token issue (#17363)
---
CHANGELOG.md | 2 +
ui/src/buckets/selectors/index.ts | 14 ++-
ui/src/dataLoaders/actions/dataLoaders.ts | 100 +++++++++++-------
.../collectorsWizard/CollectorsWizard.tsx | 4 +-
.../verifyStep/TelegrafInstructions.tsx | 11 +-
ui/src/resources/selectors/index.ts | 3 +
ui/src/shared/components/CodeSnippet.scss | 4 +
ui/src/shared/components/TokenCodeSnippet.tsx | 92 ++++++++++++++++
ui/src/shared/copy/notifications.ts | 5 +
.../TelegrafInstructionsOverlay.tsx | 64 ++++-------
10 files changed, 216 insertions(+), 83 deletions(-)
create mode 100644 ui/src/shared/components/TokenCodeSnippet.tsx
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1051d4ec033..1455c34311d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,10 +5,12 @@
1. [17232](https://github.com/influxdata/influxdb/pull/17232): Allow dashboards to optionally be displayed in light mode
1. [17273](https://github.com/influxdata/influxdb/pull/17273): Add shell completions command for the influx cli
1. [17353](https://github.com/influxdata/influxdb/pull/17353): Make all pkg resources unique by metadata.name field
+1. [17363](https://github.com/influxdata/influxdb/pull/17363): Telegraf config tokens can no longer be retrieved after creation, but new tokens can be created after a telegraf has been setup
### Bug Fixes
1. [17240](https://github.com/influxdata/influxdb/pull/17240): NodeJS logo displays properly in Firefox
+1. [17363](https://github.com/influxdata/influxdb/pull/17363): Fixed telegraf configuration bugs where system buckets were appearing in the buckets dropdown
### UI Improvements
diff --git a/ui/src/buckets/selectors/index.ts b/ui/src/buckets/selectors/index.ts
index 030c40c98d2..7189dd17702 100644
--- a/ui/src/buckets/selectors/index.ts
+++ b/ui/src/buckets/selectors/index.ts
@@ -1,8 +1,20 @@
// Types
-import {Bucket} from 'src/types'
+import {AppState, Bucket, ResourceType} from 'src/types'
+
+// Selectors
+import {getAll} from 'src/resources/selectors'
export const SYSTEM = 'system'
+export const getBucketByName = (
+ state: AppState,
+ bucketName: string
+): Bucket => {
+ const buckets = getAll(state, ResourceType.Buckets)
+ const bucket = buckets.find(b => b.name === bucketName)
+ return bucket
+}
+
export const isSystemBucket = (type: string): boolean => type === SYSTEM
const sortFunc = (a: Bucket, b: Bucket) => {
diff --git a/ui/src/dataLoaders/actions/dataLoaders.ts b/ui/src/dataLoaders/actions/dataLoaders.ts
index 17f5c1cac25..021e479fcf9 100644
--- a/ui/src/dataLoaders/actions/dataLoaders.ts
+++ b/ui/src/dataLoaders/actions/dataLoaders.ts
@@ -6,7 +6,7 @@ import {normalize} from 'normalizr'
import {client} from 'src/utils/api'
import {ScraperTargetRequest, PermissionResource} from '@influxdata/influx'
import {createAuthorization} from 'src/authorizations/apis'
-import {postWrite as apiPostWrite, postLabel as apiPostLabel} from 'src/client'
+import {postWrite as apiPostWrite} from 'src/client'
// Schemas
import {authSchema} from 'src/schemas'
@@ -14,8 +14,9 @@ import {telegrafSchema} from 'src/schemas/telegrafs'
// Utils
import {createNewPlugin} from 'src/dataLoaders/utils/pluginConfigs'
-import {addLabelDefaults} from 'src/labels/utils'
import {getDataLoaders, getSteps} from 'src/dataLoaders/selectors'
+import {getBucketByName} from 'src/buckets/selectors'
+import {getByID} from 'src/resources/selectors'
import {getOrg} from 'src/organizations/selectors'
// Constants
@@ -37,13 +38,12 @@ import {
import {
GetState,
RemoteDataState,
- LabelProperties,
Authorization,
AuthEntities,
+ ResourceType,
TelegrafEntities,
Telegraf,
} from 'src/types'
-import {ILabel} from '@influxdata/influx'
import {
WritePrecision,
TelegrafRequest,
@@ -57,9 +57,10 @@ import {addTelegraf, editTelegraf} from 'src/telegrafs/actions/creators'
import {addAuthorization} from 'src/authorizations/actions/creators'
import {notify} from 'src/shared/actions/notifications'
import {
+ readWriteCardinalityLimitReached,
TelegrafConfigCreationError,
TelegrafConfigCreationSuccess,
- readWriteCardinalityLimitReached,
+ TokenCreationError,
} from 'src/shared/copy/notifications'
const DEFAULT_COLLECTION_INTERVAL = 10000
@@ -401,6 +402,61 @@ export const createOrUpdateTelegrafConfigAsync = () => async (
createTelegraf(dispatch, getState, plugins)
}
+export const generateTelegrafToken = (configID: string) => async (
+ dispatch,
+ getState: GetState
+) => {
+ try {
+ const state = getState()
+ const orgID = getOrg(state).id
+ const telegraf = getByID(state, ResourceType.Telegrafs, configID)
+ const bucketName = get(telegraf, 'metadata.buckets[0]', '')
+ if (bucketName === '') {
+ throw new Error(
+ 'A token cannot be generated without a corresponding bucket'
+ )
+ }
+ const bucket = getBucketByName(state, bucketName)
+
+ const permissions = [
+ {
+ action: Permission.ActionEnum.Write,
+ resource: {
+ type: PermissionResource.TypeEnum.Buckets,
+ id: bucket.id,
+ orgID,
+ },
+ },
+ {
+ action: Permission.ActionEnum.Read,
+ resource: {
+ type: PermissionResource.TypeEnum.Telegrafs,
+ id: configID,
+ orgID,
+ },
+ },
+ ]
+
+ const token = {
+ name: `${telegraf.name} token`,
+ orgID,
+ description: `WRITE ${bucketName} bucket / READ ${
+ telegraf.name
+ } telegraf config`,
+ permissions,
+ }
+
+ // create token
+ const createdToken = await createAuthorization(token)
+
+ // add token to data loader state
+ dispatch(setToken(createdToken.token))
+ } catch (error) {
+ console.error(error)
+ dispatch(notify(TokenCreationError))
+ }
+}
+
const createTelegraf = async (dispatch, getState: GetState, plugins) => {
try {
const state = getState()
@@ -461,40 +517,8 @@ const createTelegraf = async (dispatch, getState: GetState, plugins) => {
// add token to authorizations state
dispatch(addAuthorization(normAuth))
- // create token label
- const properties = {
- color: '#FFFFFF',
- description: `token for telegraf config: ${telegrafConfigName}`,
- tokenID: createdToken.id,
- } as LabelProperties // hack to make compiler work
-
- const resp = await apiPostLabel({
- data: {
- orgID: org.id,
- name: `@influxdata.token-${new Date().getTime()}`, // fix for https://github.com/influxdata/influxdb/issues/15730
- properties,
- },
- })
-
- if (resp.status !== 201) {
- throw new Error(resp.data.message)
- }
-
- const createdLabel = addLabelDefaults(resp.data.label)
-
- // add label to telegraf config
- const label = await client.telegrafConfigs.addLabel(
- tc.id,
- createdLabel as ILabel
- )
-
- const config = {
- ...tc,
- labels: [label],
- }
-
const normTelegraf = normalize(
- config,
+ tc,
telegrafSchema
)
diff --git a/ui/src/dataLoaders/components/collectorsWizard/CollectorsWizard.tsx b/ui/src/dataLoaders/components/collectorsWizard/CollectorsWizard.tsx
index 71f933bac42..50267b2a7b7 100644
--- a/ui/src/dataLoaders/components/collectorsWizard/CollectorsWizard.tsx
+++ b/ui/src/dataLoaders/components/collectorsWizard/CollectorsWizard.tsx
@@ -50,7 +50,9 @@ import {AppState, Bucket, Organization, ResourceType} from 'src/types'
// Selectors
import {getAll} from 'src/resources/selectors'
import {getOrg} from 'src/organizations/selectors'
-import {isSystemBucket} from 'src/buckets/selectors'
+
+// Utils
+import {isSystemBucket} from 'src/buckets/constants'
export interface CollectorsStepProps {
currentStepIndex: number
diff --git a/ui/src/dataLoaders/components/verifyStep/TelegrafInstructions.tsx b/ui/src/dataLoaders/components/verifyStep/TelegrafInstructions.tsx
index 762b4324da2..c29dccba98b 100644
--- a/ui/src/dataLoaders/components/verifyStep/TelegrafInstructions.tsx
+++ b/ui/src/dataLoaders/components/verifyStep/TelegrafInstructions.tsx
@@ -1,5 +1,6 @@
// Libraries
import React, {PureComponent} from 'react'
+import {Alert, ComponentColor, IconFont} from '@influxdata/clockface'
import _ from 'lodash'
// Decorator
@@ -7,6 +8,7 @@ import {ErrorHandling} from 'src/shared/decorators/errors'
// Components
import CodeSnippet from 'src/shared/components/CodeSnippet'
+import TokenCodeSnippet from 'src/shared/components/TokenCodeSnippet'
export interface Props {
token: string
@@ -17,7 +19,6 @@ export interface Props {
class TelegrafInstructions extends PureComponent {
public render() {
const {token, configID} = this.props
- const exportToken = `export INFLUX_TOKEN=${token || ''}`
const configScript = `telegraf --config ${
this.origin
}/api/v2/telegrafs/${configID || ''}`
@@ -42,7 +43,13 @@ class TelegrafInstructions extends PureComponent {
copy the following command to your terminal window to set an
environment variable with your token.
-
+ {token && (
+
+ Make sure to copy your new personal access token now. You won’t be
+ able to see it again!
+
+ )}
+
3. Start Telegraf
Finally, you can run the following command to start the Telegraf agent
diff --git a/ui/src/resources/selectors/index.ts b/ui/src/resources/selectors/index.ts
index 41d0fd7dc2c..3ac9430a0ce 100644
--- a/ui/src/resources/selectors/index.ts
+++ b/ui/src/resources/selectors/index.ts
@@ -20,6 +20,9 @@ export const getAll = (
return allIDs.map(id => byID[id])
}
+export const getToken = (state: AppState): string =>
+ get(state, 'dataLoading.dataLoaders.token', '') || ''
+
export const getByID = (
{resources}: AppState,
type: ResourceType,
diff --git a/ui/src/shared/components/CodeSnippet.scss b/ui/src/shared/components/CodeSnippet.scss
index fc800f82366..b4ac2eb9d1f 100644
--- a/ui/src/shared/components/CodeSnippet.scss
+++ b/ui/src/shared/components/CodeSnippet.scss
@@ -34,6 +34,10 @@
}
}
+.new-token--btn {
+ margin: $ix-marg-a $ix-marg-c;
+}
+
.code-snippet--footer {
background-color: rgba($g4-onyx, 0.5);
display: flex;
diff --git a/ui/src/shared/components/TokenCodeSnippet.tsx b/ui/src/shared/components/TokenCodeSnippet.tsx
new file mode 100644
index 00000000000..65805c62912
--- /dev/null
+++ b/ui/src/shared/components/TokenCodeSnippet.tsx
@@ -0,0 +1,92 @@
+// Libraries
+import React, {FC} from 'react'
+import {connect} from 'react-redux'
+import {
+ Button,
+ ComponentColor,
+ ComponentSize,
+ ComponentStatus,
+ IconFont,
+} from '@influxdata/clockface'
+
+// Decorator
+import {Notification} from 'src/types'
+
+// Components
+import FancyScrollbar from 'src/shared/components/fancy_scrollbar/FancyScrollbar'
+import CopyButton from 'src/shared/components/CopyButton'
+
+// Actions
+import {generateTelegrafToken} from 'src/dataLoaders/actions/dataLoaders'
+
+export interface Props {
+ configID: string
+ token: string
+ onCopyText?: (text: string, status: boolean) => Notification
+ testID?: string
+ label: string
+}
+
+interface DispatchProps {
+ onGenerateTelegrafToken: typeof generateTelegrafToken
+}
+
+const TokenCodeSnippet: FC = ({
+ token,
+ onCopyText,
+ testID,
+ label = 'Code Snippet',
+}) => {
+ const handleRefreshClick = () => {
+ const {configID, onGenerateTelegrafToken} = this.props
+ onGenerateTelegrafToken(configID)
+ }
+
+ return (
+
+
+
+
+ export INFLUX_TOKEN={token || ''}
+
+
+
+
+
+ )
+}
+
+const mdtp: DispatchProps = {
+ onGenerateTelegrafToken: generateTelegrafToken,
+}
+
+export default connect<{}, DispatchProps>(
+ null,
+ mdtp
+)(TokenCodeSnippet)
diff --git a/ui/src/shared/copy/notifications.ts b/ui/src/shared/copy/notifications.ts
index 94fe6c40b9b..5974828523a 100644
--- a/ui/src/shared/copy/notifications.ts
+++ b/ui/src/shared/copy/notifications.ts
@@ -121,6 +121,11 @@ export const TelegrafConfigCreationError: Notification = {
message: `Failed to save configurations`,
}
+export const TokenCreationError: Notification = {
+ ...defaultErrorNotification,
+ message: `Failed to create a new Telegraf Token`,
+}
+
// Task Notifications
// ----------------------------------------------------------------------------
export const addTaskLabelFailed = (): Notification => ({
diff --git a/ui/src/telegrafs/components/TelegrafInstructionsOverlay.tsx b/ui/src/telegrafs/components/TelegrafInstructionsOverlay.tsx
index 0ac3a9c15c2..b66b9ee7753 100644
--- a/ui/src/telegrafs/components/TelegrafInstructionsOverlay.tsx
+++ b/ui/src/telegrafs/components/TelegrafInstructionsOverlay.tsx
@@ -10,29 +10,29 @@ import {Overlay} from '@influxdata/clockface'
import TelegrafInstructions from 'src/dataLoaders/components/verifyStep/TelegrafInstructions'
import GetResources from 'src/resources/components/GetResources'
-// Constants
-import {TOKEN_LABEL} from 'src/labels/constants'
-
// Types
-import {Telegraf, AppState, ResourceType, Authorization} from 'src/types'
+import {Telegraf, AppState, ResourceType} from 'src/types'
// Selectors
-import {getAll} from 'src/resources/selectors'
-
-const {Authorizations} = ResourceType
+import {getAll, getToken} from 'src/resources/selectors'
+import {clearDataLoaders} from 'src/dataLoaders/actions/dataLoaders'
interface StateProps {
username: string
- telegrafs: Telegraf[]
- tokens: Authorization[]
+ token: string
collectors: Telegraf[]
}
+interface DispatchProps {
+ onClearDataLoaders: typeof clearDataLoaders
+}
+
+type Props = StateProps & DispatchProps & WithRouterProps
+
@ErrorHandling
-export class TelegrafInstructionsOverlay extends PureComponent<
- StateProps & WithRouterProps
-> {
+export class TelegrafInstructionsOverlay extends PureComponent {
public render() {
+ const {token} = this.props
return (
@@ -43,7 +43,7 @@ export class TelegrafInstructionsOverlay extends PureComponent<
@@ -53,28 +53,6 @@ export class TelegrafInstructionsOverlay extends PureComponent<
)
}
- private get token(): string {
- const {telegrafs, tokens} = this.props
- const config =
- telegrafs.find(t => get(this.collector, 'id', '') === t.id) ||
- this.collector
-
- if (!config) {
- return 'no config found'
- }
-
- const labels = get(config, 'labels', [])
-
- const label = labels.find(l => l.name === TOKEN_LABEL)
- const auth = tokens.find(t => t.id === get(label, 'properties.tokenID'))
-
- if (!label || !auth) {
- return 'unknown token'
- }
-
- return auth.token
- }
-
private get collector() {
const {
params: {id},
@@ -87,11 +65,12 @@ export class TelegrafInstructionsOverlay extends PureComponent<
const {
router,
params: {orgID},
+ onClearDataLoaders,
} = this.props
this.setState({
collectorID: null,
})
-
+ onClearDataLoaders()
router.push(`/orgs/${orgID}/load-data/telegrafs/`)
}
}
@@ -101,18 +80,21 @@ const mstp = (state: AppState): StateProps => {
me: {name},
} = state
- const tokens = getAll(state, Authorizations)
+ const token = getToken(state)
const telegrafs = getAll(state, ResourceType.Telegrafs)
return {
username: name,
- tokens,
+ token,
collectors: telegrafs,
- telegrafs: telegrafs,
}
}
-export default connect(
+const mdtp: DispatchProps = {
+ onClearDataLoaders: clearDataLoaders,
+}
+
+export default connect(
mstp,
- null
+ mdtp
)(withRouter(TelegrafInstructionsOverlay))