CloudWatch: Make it possible to specify custom api endpoint (#31402)

* wip: add endpoint field

* add endpoint to config and make sure it's part of the cache key

* update docs

* improve endpoint paragraph

* Update docs/sources/datasources/cloudwatch.md

Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com>

* Update public/app/plugins/datasource/cloudwatch/components/ConfigEditor.tsx

Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com>

* goimports

* Update docs/sources/datasources/cloudwatch.md

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* update snapshot

* Update docs/sources/datasources/cloudwatch.md

Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com>

Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com>
Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com>
This commit is contained in:
Erik Sundell 2021-02-24 16:05:14 +01:00 committed by GitHub
parent 8f7e26cd01
commit 055590890c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 148 additions and 1 deletions

View File

@ -105,6 +105,10 @@ Here is a minimal policy example:
The `Assume Role ARN` field allows you to specify which IAM role to assume, if any. When left blank, the provided credentials are used directly and the associated role or user should have the required permissions. If this field is non-blank, on the other hand, the provided credentials are used to perform an [sts:AssumeRole](https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html) call.
### Endpoint
The `Endpoint` field allows you to specify a custom endpoint URL that overrides the default generated endpoint for the CloudWatch API. Leave this field blank if you want to use the default generated endpoint. For more information on why and how to use Service endpoints, refer to the [AWS service endpoints documentation](https://docs.aws.amazon.com/general/latest/gr/rande.html).
### EKS IAM roles for service accounts
The Grafana process in the container runs as user 472 (called "grafana"). When Kubernetes mounts your projected credentials, they will by default only be available to the root user. In order to allow user 472 to access the credentials (and avoid it falling back to the IAM role attached to the EC2 instance), you will need to provide a [security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/) for your pod.

View File

@ -38,6 +38,7 @@ type datasourceInfo struct {
AssumeRoleARN string
ExternalID string
Namespace string
Endpoint string
AccessKey string
SecretKey string
@ -96,7 +97,7 @@ func (e *cloudWatchExecutor) newSession(region string) (*session.Session, error)
bldr := strings.Builder{}
for i, s := range []string{
dsInfo.AuthType.String(), dsInfo.AccessKey, dsInfo.Profile, dsInfo.AssumeRoleARN, region,
dsInfo.AuthType.String(), dsInfo.AccessKey, dsInfo.Profile, dsInfo.AssumeRoleARN, region, dsInfo.Endpoint,
} {
if i != 0 {
bldr.WriteString(":")
@ -130,6 +131,10 @@ func (e *cloudWatchExecutor) newSession(region string) (*session.Session, error)
cfgs = append(cfgs, regionCfg)
}
if dsInfo.Endpoint != "" {
cfgs = append(cfgs, &aws.Config{Endpoint: aws.String(dsInfo.Endpoint)})
}
switch dsInfo.AuthType {
case authTypeSharedCreds:
plog.Debug("Authenticating towards AWS with shared credentials", "profile", dsInfo.Profile,
@ -413,6 +418,7 @@ func (e *cloudWatchExecutor) getDSInfo(region string) *datasourceInfo {
atStr := e.DataSource.JsonData.Get("authType").MustString()
assumeRoleARN := e.DataSource.JsonData.Get("assumeRoleArn").MustString()
externalID := e.DataSource.JsonData.Get("externalId").MustString()
endpoint := e.DataSource.JsonData.Get("endpoint").MustString()
decrypted := e.DataSource.DecryptedValues()
accessKey := decrypted["accessKey"]
secretKey := decrypted["secretKey"]
@ -445,6 +451,7 @@ func (e *cloudWatchExecutor) getDSInfo(region string) *datasourceInfo {
ExternalID: externalID,
AccessKey: accessKey,
SecretKey: secretKey,
Endpoint: endpoint,
}
}

View File

@ -315,6 +315,21 @@ export class ConfigEditor extends PureComponent<Props, State> {
/>
</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>
</>
);

View File

@ -225,6 +225,30 @@ exports[`Render should disable access key id field 1`] = `
/>
</div>
</div>
<div
className="gf-form-inline"
>
<div
className="gf-form"
>
<FormLabel
className="width-14"
tooltip="Optionally, specify a custom endpoint for the service."
>
Endpoint
</FormLabel>
<div
className="width-30"
>
<Input
className="width-30"
onChange={[Function]}
placeholder="https://{service}.{region}.amazonaws.com"
value=""
/>
</div>
</div>
</div>
</div>
</Fragment>
`;
@ -454,6 +478,30 @@ exports[`Render should render component 1`] = `
/>
</div>
</div>
<div
className="gf-form-inline"
>
<div
className="gf-form"
>
<FormLabel
className="width-14"
tooltip="Optionally, specify a custom endpoint for the service."
>
Endpoint
</FormLabel>
<div
className="width-30"
>
<Input
className="width-30"
onChange={[Function]}
placeholder="https://{service}.{region}.amazonaws.com"
value=""
/>
</div>
</div>
</div>
</div>
</Fragment>
`;
@ -683,6 +731,30 @@ exports[`Render should show access key and secret access key fields 1`] = `
/>
</div>
</div>
<div
className="gf-form-inline"
>
<div
className="gf-form"
>
<FormLabel
className="width-14"
tooltip="Optionally, specify a custom endpoint for the service."
>
Endpoint
</FormLabel>
<div
className="width-30"
>
<Input
className="width-30"
onChange={[Function]}
placeholder="https://{service}.{region}.amazonaws.com"
value=""
/>
</div>
</div>
</div>
</div>
</Fragment>
`;
@ -912,6 +984,30 @@ exports[`Render should show arn role field 1`] = `
/>
</div>
</div>
<div
className="gf-form-inline"
>
<div
className="gf-form"
>
<FormLabel
className="width-14"
tooltip="Optionally, specify a custom endpoint for the service."
>
Endpoint
</FormLabel>
<div
className="width-30"
>
<Input
className="width-30"
onChange={[Function]}
placeholder="https://{service}.{region}.amazonaws.com"
value=""
/>
</div>
</div>
</div>
</div>
</Fragment>
`;
@ -1141,6 +1237,30 @@ exports[`Render should show credentials profile name field 1`] = `
/>
</div>
</div>
<div
className="gf-form-inline"
>
<div
className="gf-form"
>
<FormLabel
className="width-14"
tooltip="Optionally, specify a custom endpoint for the service."
>
Endpoint
</FormLabel>
<div
className="width-30"
>
<Input
className="width-30"
onChange={[Function]}
placeholder="https://{service}.{region}.amazonaws.com"
value=""
/>
</div>
</div>
</div>
</div>
</Fragment>
`;

View File

@ -62,6 +62,7 @@ export interface CloudWatchJsonData extends DataSourceJsonData {
externalId?: string;
database?: string;
customMetricsNamespaces?: string;
endpoint?: string;
}
export interface CloudWatchSecureJsonData {