mirror of https://github.com/grafana/grafana
CloudWatch: Consume the grafana/aws-sdk (#31807)
* consume the grafana/aws-sdk * upgrade aws-sdkpull/31870/head
parent
d2aaaeb497
commit
f135d75a22
@ -1,339 +1,63 @@ |
|||||||
import React, { PureComponent } from 'react'; |
import React, { FC, useEffect, useState } from 'react'; |
||||||
import { InlineFormLabel, LegacyForms, Button } from '@grafana/ui'; |
import { Input, InlineField } from '@grafana/ui'; |
||||||
const { Select, Input } = LegacyForms; |
import { DataSourcePluginOptionsEditorProps, onUpdateDatasourceJsonDataOption } from '@grafana/data'; |
||||||
import { |
import { ConnectionConfig } from '@grafana/aws-sdk'; |
||||||
AppEvents, |
|
||||||
SelectableValue, |
|
||||||
DataSourcePluginOptionsEditorProps, |
|
||||||
onUpdateDatasourceJsonDataOptionSelect, |
|
||||||
onUpdateDatasourceResetOption, |
|
||||||
onUpdateDatasourceJsonDataOption, |
|
||||||
onUpdateDatasourceSecureJsonDataOption, |
|
||||||
} from '@grafana/data'; |
|
||||||
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; |
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; |
||||||
import { CloudWatchDatasource } from '../datasource'; |
import { store } from 'app/store/store'; |
||||||
import { CloudWatchJsonData, CloudWatchSecureJsonData } from '../types'; |
import { notifyApp } from 'app/core/actions'; |
||||||
import { CancelablePromise, makePromiseCancelable } from 'app/core/utils/CancelablePromise'; |
import { createWarningNotification } from 'app/core/copy/appNotification'; |
||||||
import { appEvents } from 'app/core/core'; |
|
||||||
|
|
||||||
const authProviderOptions = [ |
import { CloudWatchJsonData, CloudWatchSecureJsonData } from '../types'; |
||||||
{ label: 'AWS SDK Default', value: 'default' }, |
import { CloudWatchDatasource } from '../datasource'; |
||||||
{ label: 'Access & secret key', value: 'keys' }, |
|
||||||
{ label: 'Credentials file', value: 'credentials' }, |
|
||||||
] as SelectableValue[]; |
|
||||||
|
|
||||||
export type Props = DataSourcePluginOptionsEditorProps<CloudWatchJsonData, CloudWatchSecureJsonData>; |
export type Props = DataSourcePluginOptionsEditorProps<CloudWatchJsonData, CloudWatchSecureJsonData>; |
||||||
|
|
||||||
export interface State { |
export const ConfigEditor: FC<Props> = (props: Props) => { |
||||||
regions: SelectableValue[]; |
const [datasource, setDatasource] = useState<CloudWatchDatasource>(); |
||||||
} |
|
||||||
|
|
||||||
export class ConfigEditor extends PureComponent<Props, State> { |
const addWarning = (message: string) => { |
||||||
constructor(props: Props) { |
store.dispatch(notifyApp(createWarningNotification('CloudWatch Authentication', message))); |
||||||
super(props); |
}; |
||||||
|
|
||||||
this.state = { |
useEffect(() => { |
||||||
regions: [], |
getDatasourceSrv() |
||||||
}; |
.loadDatasource(props.options.name) |
||||||
} |
.then((datasource: CloudWatchDatasource) => setDatasource(datasource)); |
||||||
|
|
||||||
loadRegionsPromise: CancelablePromise<any> | null = null; |
if (props.options.jsonData.authType === 'arn') { |
||||||
|
addWarning('Since grafana 7.3 authentication type "arn" is deprecated, falling back to default SDK provider'); |
||||||
componentDidMount() { |
|
||||||
this.loadRegionsPromise = makePromiseCancelable(this.loadRegions()); |
|
||||||
this.loadRegionsPromise.promise.catch(({ isCanceled }) => { |
|
||||||
if (isCanceled) { |
|
||||||
console.warn('Cloud Watch ConfigEditor has unmounted, initialization was canceled'); |
|
||||||
} |
|
||||||
}); |
|
||||||
|
|
||||||
if (this.props.options.jsonData.authType === 'arn') { |
|
||||||
appEvents.emit(AppEvents.alertWarning, [ |
|
||||||
'Since grafana 7.3 authentication type "arn" is deprecated, falling back to default SDK provider', |
|
||||||
]); |
|
||||||
} else if ( |
} else if ( |
||||||
this.props.options.jsonData.authType === 'credentials' && |
props.options.jsonData.authType === 'credentials' && |
||||||
!this.props.options.jsonData.profile && |
!props.options.jsonData.profile && |
||||||
!this.props.options.jsonData.database |
!props.options.jsonData.database |
||||||
) { |
) { |
||||||
appEvents.emit(AppEvents.alertWarning, [ |
addWarning( |
||||||
'As of grafana 7.3 authentication type "credentials" should be used only for shared file credentials. \ |
'As of grafana 7.3 authentication type "credentials" should be used only for shared file credentials. \ |
||||||
If you don\'t have a credentials file, switch to the default SDK provider for extracting credentials \ |
If you don\'t have a credentials file, switch to the default SDK provider for extracting credentials \ |
||||||
from environment variables or IAM roles', |
from environment variables or IAM roles' |
||||||
]); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
componentWillUnmount() { |
|
||||||
if (this.loadRegionsPromise) { |
|
||||||
this.loadRegionsPromise.cancel(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
async loadRegions() { |
|
||||||
await getDatasourceSrv() |
|
||||||
.loadDatasource(this.props.options.name) |
|
||||||
.then((ds: CloudWatchDatasource) => ds.getRegions()) |
|
||||||
.then( |
|
||||||
(regions: any) => { |
|
||||||
this.setState({ |
|
||||||
regions: regions.map((region: any) => { |
|
||||||
return { |
|
||||||
value: region.value, |
|
||||||
label: region.text, |
|
||||||
}; |
|
||||||
}), |
|
||||||
}); |
|
||||||
}, |
|
||||||
(err: any) => { |
|
||||||
const regions = [ |
|
||||||
'af-south-1', |
|
||||||
'ap-east-1', |
|
||||||
'ap-northeast-1', |
|
||||||
'ap-northeast-2', |
|
||||||
'ap-northeast-3', |
|
||||||
'ap-south-1', |
|
||||||
'ap-southeast-1', |
|
||||||
'ap-southeast-2', |
|
||||||
'ca-central-1', |
|
||||||
'cn-north-1', |
|
||||||
'cn-northwest-1', |
|
||||||
'eu-central-1', |
|
||||||
'eu-north-1', |
|
||||||
'eu-south-1', |
|
||||||
'eu-west-1', |
|
||||||
'eu-west-2', |
|
||||||
'eu-west-3', |
|
||||||
'me-south-1', |
|
||||||
'sa-east-1', |
|
||||||
'us-east-1', |
|
||||||
'us-east-2', |
|
||||||
'us-gov-east-1', |
|
||||||
'us-gov-west-1', |
|
||||||
'us-iso-east-1', |
|
||||||
'us-isob-east-1', |
|
||||||
'us-west-1', |
|
||||||
'us-west-2', |
|
||||||
]; |
|
||||||
|
|
||||||
this.setState({ |
|
||||||
regions: regions.map((region: string) => ({ |
|
||||||
value: region, |
|
||||||
label: region, |
|
||||||
})), |
|
||||||
}); |
|
||||||
|
|
||||||
// expected to fail when creating new datasource
|
|
||||||
// console.error('failed to get latest regions', err);
|
|
||||||
} |
|
||||||
); |
); |
||||||
} |
|
||||||
|
|
||||||
render() { |
|
||||||
const { regions } = this.state; |
|
||||||
const { options } = this.props; |
|
||||||
const secureJsonData = (options.secureJsonData || {}) as CloudWatchSecureJsonData; |
|
||||||
let profile = options.jsonData.profile; |
|
||||||
if (profile === undefined) { |
|
||||||
profile = options.database; |
|
||||||
} |
} |
||||||
|
}, []); |
||||||
return ( |
|
||||||
<> |
return ( |
||||||
<h3 className="page-heading">CloudWatch Details</h3> |
<> |
||||||
<div className="gf-form-group"> |
<ConnectionConfig |
||||||
<div className="gf-form-inline"> |
{...props} |
||||||
<div className="gf-form"> |
loadRegions={ |
||||||
<InlineFormLabel |
datasource && |
||||||
className="width-14" |
(() => datasource!.getRegions().then((r) => r.filter((r) => r.value !== 'default').map((v) => v.value))) |
||||||
tooltip="Specify which AWS credentials chain to use. AWS SDK Default is the recommended option for EKS, ECS, or if you've attached an IAM role to your EC2 instance." |
} |
||||||
> |
> |
||||||
Authentication Provider |
<InlineField label="Namespaces of Custom Metrics" labelWidth={28} tooltip="Namespaces of Custom Metrics."> |
||||||
</InlineFormLabel> |
<Input |
||||||
<Select |
width={60} |
||||||
className="width-30" |
placeholder="Namespace1,Namespace2" |
||||||
value={authProviderOptions.find((authProvider) => authProvider.value === options.jsonData.authType)} |
value={props.options.jsonData.customMetricsNamespaces || ''} |
||||||
options={authProviderOptions} |
onChange={onUpdateDatasourceJsonDataOption(props, 'customMetricsNamespaces')} |
||||||
defaultValue={options.jsonData.authType} |
/> |
||||||
onChange={(option) => { |
</InlineField> |
||||||
onUpdateDatasourceJsonDataOptionSelect(this.props, 'authType')(option); |
</ConnectionConfig> |
||||||
}} |
</> |
||||||
/> |
); |
||||||
</div> |
}; |
||||||
</div> |
|
||||||
{options.jsonData.authType === 'credentials' && ( |
|
||||||
<div className="gf-form-inline"> |
|
||||||
<div className="gf-form"> |
|
||||||
<InlineFormLabel |
|
||||||
className="width-14" |
|
||||||
tooltip="Credentials profile name, as specified in ~/.aws/credentials, leave blank for default." |
|
||||||
> |
|
||||||
Credentials Profile Name |
|
||||||
</InlineFormLabel> |
|
||||||
<div className="width-30"> |
|
||||||
<Input |
|
||||||
className="width-30" |
|
||||||
placeholder="default" |
|
||||||
value={profile} |
|
||||||
onChange={onUpdateDatasourceJsonDataOption(this.props, 'profile')} |
|
||||||
/> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
)} |
|
||||||
{options.jsonData.authType === 'keys' && ( |
|
||||||
<div> |
|
||||||
{options.secureJsonFields?.accessKey ? ( |
|
||||||
<div className="gf-form-inline"> |
|
||||||
<div className="gf-form"> |
|
||||||
<InlineFormLabel className="width-14">Access Key ID</InlineFormLabel> |
|
||||||
<Input className="width-25" placeholder="Configured" disabled={true} /> |
|
||||||
</div> |
|
||||||
<div className="gf-form"> |
|
||||||
<div className="max-width-30 gf-form-inline"> |
|
||||||
<Button |
|
||||||
variant="secondary" |
|
||||||
type="button" |
|
||||||
onClick={onUpdateDatasourceResetOption(this.props, 'accessKey')} |
|
||||||
> |
|
||||||
Reset |
|
||||||
</Button> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
) : ( |
|
||||||
<div className="gf-form-inline"> |
|
||||||
<div className="gf-form"> |
|
||||||
<InlineFormLabel className="width-14">Access Key ID</InlineFormLabel> |
|
||||||
<div className="width-30"> |
|
||||||
<Input |
|
||||||
className="width-30" |
|
||||||
value={secureJsonData.accessKey || ''} |
|
||||||
onChange={onUpdateDatasourceSecureJsonDataOption(this.props, 'accessKey')} |
|
||||||
/> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
)} |
|
||||||
{options.secureJsonFields?.secretKey ? ( |
|
||||||
<div className="gf-form-inline"> |
|
||||||
<div className="gf-form"> |
|
||||||
<InlineFormLabel className="width-14">Secret Access Key</InlineFormLabel> |
|
||||||
<Input className="width-25" placeholder="Configured" disabled={true} /> |
|
||||||
</div> |
|
||||||
<div className="gf-form"> |
|
||||||
<div className="max-width-30 gf-form-inline"> |
|
||||||
<Button |
|
||||||
variant="secondary" |
|
||||||
type="button" |
|
||||||
onClick={onUpdateDatasourceResetOption(this.props, 'secretKey')} |
|
||||||
> |
|
||||||
Reset |
|
||||||
</Button> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
) : ( |
|
||||||
<div className="gf-form-inline"> |
|
||||||
<div className="gf-form"> |
|
||||||
<InlineFormLabel className="width-14">Secret Access Key</InlineFormLabel> |
|
||||||
<div className="width-30"> |
|
||||||
<Input |
|
||||||
className="width-30" |
|
||||||
value={secureJsonData.secretKey || ''} |
|
||||||
onChange={onUpdateDatasourceSecureJsonDataOption(this.props, 'secretKey')} |
|
||||||
/> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
)} |
|
||||||
</div> |
|
||||||
)} |
|
||||||
<div className="gf-form-inline"> |
|
||||||
<div className="gf-form"> |
|
||||||
<InlineFormLabel |
|
||||||
className="width-14" |
|
||||||
tooltip="Optionally, specify the ARN of a role to assume. Specifying a role here will ensure that the selected authentication provider is used to assume the specified role rather than using the credentials directly. Leave blank if you don't need to assume a role at all" |
|
||||||
> |
|
||||||
Assume Role ARN |
|
||||||
</InlineFormLabel> |
|
||||||
<div className="width-30"> |
|
||||||
<Input |
|
||||||
className="width-30" |
|
||||||
placeholder="arn:aws:iam:*" |
|
||||||
value={options.jsonData.assumeRoleArn || ''} |
|
||||||
onChange={onUpdateDatasourceJsonDataOption(this.props, 'assumeRoleArn')} |
|
||||||
/> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
<div className="gf-form-inline"> |
|
||||||
<div className="gf-form"> |
|
||||||
<InlineFormLabel |
|
||||||
className="width-14" |
|
||||||
tooltip="If you are assuming a role in another account, that has been created with an external ID, specify the external ID here." |
|
||||||
> |
|
||||||
External ID |
|
||||||
</InlineFormLabel> |
|
||||||
<div className="width-30"> |
|
||||||
<Input |
|
||||||
className="width-30" |
|
||||||
placeholder="External ID" |
|
||||||
value={options.jsonData.externalId || ''} |
|
||||||
onChange={onUpdateDatasourceJsonDataOption(this.props, 'externalId')} |
|
||||||
/> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
<div className="gf-form-inline"> |
|
||||||
<div className="gf-form"> |
|
||||||
<InlineFormLabel |
|
||||||
className="width-14" |
|
||||||
tooltip="Specify the region, such as for US West (Oregon) use ` us-west-2 ` as the region." |
|
||||||
> |
|
||||||
Default Region |
|
||||||
</InlineFormLabel> |
|
||||||
<Select |
|
||||||
className="width-30" |
|
||||||
value={regions.find((region) => region.value === options.jsonData.defaultRegion)} |
|
||||||
options={regions} |
|
||||||
defaultValue={options.jsonData.defaultRegion} |
|
||||||
onChange={onUpdateDatasourceJsonDataOptionSelect(this.props, 'defaultRegion')} |
|
||||||
/> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
<div className="gf-form-inline"> |
|
||||||
<div className="gf-form"> |
|
||||||
<InlineFormLabel className="width-14" tooltip="Namespaces of Custom Metrics."> |
|
||||||
Custom Metrics |
|
||||||
</InlineFormLabel> |
|
||||||
<Input |
|
||||||
className="width-30" |
|
||||||
placeholder="Namespace1,Namespace2" |
|
||||||
value={options.jsonData.customMetricsNamespaces || ''} |
|
||||||
onChange={onUpdateDatasourceJsonDataOption(this.props, 'customMetricsNamespaces')} |
|
||||||
/> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
<div className="gf-form-inline"> |
|
||||||
<div className="gf-form"> |
|
||||||
<InlineFormLabel className="width-14" tooltip="Optionally, specify a custom endpoint for the service."> |
|
||||||
Endpoint |
|
||||||
</InlineFormLabel> |
|
||||||
<div className="width-30"> |
|
||||||
<Input |
|
||||||
className="width-30" |
|
||||||
placeholder={'https://{service}.{region}.amazonaws.com'} |
|
||||||
value={options.jsonData.endpoint || ''} |
|
||||||
onChange={onUpdateDatasourceJsonDataOption(this.props, 'endpoint')} |
|
||||||
/> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</> |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
export default ConfigEditor; |
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue