mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Prometheus: Config overhaul part two, auth and DataSourceHttpSettings overhaul (#71250)
* build httpsettings overhaul with new auth component * remove test code * add connection and advanced http settings components * use tooltip with link * add correct styling and spacing * save option select for sigV4 * fix styles in Azure auth to fit new auth component * add types in overhaul folder that are not available yet in grafana * update e2e tests for new connection component * update e2e tests for new connection component * update width of azure inputs * fix non custom auth selects * add feature toggle * wrap azure style changes behind the feature flag * fix feature toggle rebase fix error * move advanced http setting and wrap everything in the config subsection component to fix font * fix input width * use cx for conditional classes * use cx for conditional class
This commit is contained in:
committed by
GitHub
parent
d39ec2428e
commit
98cb3ce3b6
@@ -128,6 +128,7 @@ Experimental features might be changed or removed without prior notice.
|
||||
| `grafanaAPIServer` | Enable Kubernetes API Server for Grafana resources |
|
||||
| `featureToggleAdminPage` | Enable admin page for managing feature toggles from the Grafana front-end |
|
||||
| `awsAsyncQueryCaching` | Enable caching for async queries for Redshift and Athena. Requires that the `useCachingService` feature toggle is enabled and the datasource has caching and async query support enabled |
|
||||
| `prometheusConfigOverhaulAuth` | Update the Prometheus configuration page with the new auth component |
|
||||
|
||||
## Development feature toggles
|
||||
|
||||
|
@@ -116,4 +116,5 @@ export interface FeatureToggles {
|
||||
awsAsyncQueryCaching?: boolean;
|
||||
splitScopes?: boolean;
|
||||
azureMonitorDataplane?: boolean;
|
||||
prometheusConfigOverhaulAuth?: boolean;
|
||||
}
|
||||
|
@@ -675,5 +675,11 @@ var (
|
||||
Owner: grafanaPartnerPluginsSquad,
|
||||
Expression: "true", // on by default
|
||||
},
|
||||
{
|
||||
Name: "prometheusConfigOverhaulAuth",
|
||||
Description: "Update the Prometheus configuration page with the new auth component",
|
||||
Stage: FeatureStageExperimental,
|
||||
Owner: grafanaObservabilityMetricsSquad,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
@@ -97,3 +97,4 @@ featureToggleAdminPage,experimental,@grafana/grafana-operator-experience-squad,f
|
||||
awsAsyncQueryCaching,experimental,@grafana/aws-datasources,false,false,false,false
|
||||
splitScopes,preview,@grafana/grafana-authnz-team,false,false,true,false
|
||||
azureMonitorDataplane,GA,@grafana/partner-datasources,false,false,false,false
|
||||
prometheusConfigOverhaulAuth,experimental,@grafana/observability-metrics,false,false,false,false
|
||||
|
|
@@ -398,4 +398,8 @@ const (
|
||||
// FlagAzureMonitorDataplane
|
||||
// Adds dataplane compliant frame metadata in the Azure Monitor datasource
|
||||
FlagAzureMonitorDataplane = "azureMonitorDataplane"
|
||||
|
||||
// FlagPrometheusConfigOverhaulAuth
|
||||
// Update the Prometheus configuration page with the new auth component
|
||||
FlagPrometheusConfigOverhaulAuth = "prometheusConfigOverhaulAuth"
|
||||
)
|
||||
|
@@ -1,6 +1,9 @@
|
||||
import { cx } from '@emotion/css';
|
||||
import React from 'react';
|
||||
|
||||
import { DataSourceJsonData, DataSourcePluginOptionsEditorProps } from '@grafana/data';
|
||||
import { ConfigSubSection } from '@grafana/experimental';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { InlineField, Switch, useTheme2 } from '@grafana/ui';
|
||||
|
||||
import { docsTip, overhaulStyles } from './ConfigEditor';
|
||||
@@ -19,9 +22,13 @@ export function AlertingSettingsOverhaul<T extends AlertingConfig>({
|
||||
const theme = useTheme2();
|
||||
const styles = overhaulStyles(theme);
|
||||
|
||||
const prometheusConfigOverhaulAuth = config.featureToggles.prometheusConfigOverhaulAuth;
|
||||
|
||||
return (
|
||||
<>
|
||||
<h3 className="page-heading">Alerting</h3>
|
||||
<ConfigSubSection
|
||||
title="Alerting"
|
||||
className={cx(styles.container, { [styles.alertingTop]: prometheusConfigOverhaulAuth })}
|
||||
>
|
||||
<div className="gf-form-group">
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
@@ -51,6 +58,6 @@ export function AlertingSettingsOverhaul<T extends AlertingConfig>({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</ConfigSubSection>
|
||||
);
|
||||
}
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { cx } from '@emotion/css';
|
||||
import React, { FormEvent, useMemo, useState } from 'react';
|
||||
|
||||
import { config } from '@grafana/runtime';
|
||||
@@ -40,6 +41,10 @@ export const AzureAuthSettings = (props: HttpSettingsBaseProps) => {
|
||||
}
|
||||
};
|
||||
|
||||
const prometheusConfigOverhaulAuth = config.featureToggles.prometheusConfigOverhaulAuth;
|
||||
|
||||
const labelWidth = prometheusConfigOverhaulAuth ? 24 : 26;
|
||||
|
||||
return (
|
||||
<>
|
||||
<h6>Azure authentication</h6>
|
||||
@@ -53,15 +58,15 @@ export const AzureAuthSettings = (props: HttpSettingsBaseProps) => {
|
||||
<h6>Azure configuration</h6>
|
||||
<div className="gf-form-group">
|
||||
<InlineFieldRow>
|
||||
<InlineField labelWidth={26} label="Override AAD audience" disabled={dataSourceConfig.readOnly}>
|
||||
<InlineField labelWidth={labelWidth} label="Override AAD audience" disabled={dataSourceConfig.readOnly}>
|
||||
<InlineSwitch value={overrideAudienceChecked} onChange={onOverrideAudienceChange} />
|
||||
</InlineField>
|
||||
</InlineFieldRow>
|
||||
{overrideAudienceChecked && (
|
||||
<InlineFieldRow>
|
||||
<InlineField labelWidth={26} label="Resource ID" disabled={dataSourceConfig.readOnly}>
|
||||
<InlineField labelWidth={labelWidth} label="Resource ID" disabled={dataSourceConfig.readOnly}>
|
||||
<Input
|
||||
className="width-30"
|
||||
className={cx(prometheusConfigOverhaulAuth ? 'width-20' : 'width-30')}
|
||||
value={dataSourceConfig.jsonData.azureEndpointResourceId || ''}
|
||||
onChange={onResourceIdChange}
|
||||
/>
|
||||
|
@@ -1,6 +1,8 @@
|
||||
import { cx } from '@emotion/css';
|
||||
import React, { ChangeEvent, useEffect, useReducer, useState } from 'react';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { InlineFormLabel, Button } from '@grafana/ui/src/components';
|
||||
import { Input } from '@grafana/ui/src/components/Forms/Legacy/Input/Input';
|
||||
import { Select } from '@grafana/ui/src/components/Forms/Legacy/Select/Select';
|
||||
@@ -148,6 +150,7 @@ export const AzureCredentialsForm = (props: Props) => {
|
||||
onCredentialsChange(updated);
|
||||
}
|
||||
};
|
||||
const prometheusConfigOverhaulAuth = config.featureToggles.prometheusConfigOverhaulAuth;
|
||||
|
||||
return (
|
||||
<div className="gf-form-group">
|
||||
@@ -190,7 +193,7 @@ export const AzureCredentialsForm = (props: Props) => {
|
||||
<InlineFormLabel className="width-12">Directory (tenant) ID</InlineFormLabel>
|
||||
<div className="width-15">
|
||||
<Input
|
||||
className="width-30"
|
||||
className={cx(prometheusConfigOverhaulAuth ? 'width-20' : 'width-30')}
|
||||
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||
value={credentials.tenantId || ''}
|
||||
onChange={onTenantIdChange}
|
||||
@@ -204,7 +207,7 @@ export const AzureCredentialsForm = (props: Props) => {
|
||||
<InlineFormLabel className="width-12">Application (client) ID</InlineFormLabel>
|
||||
<div className="width-15">
|
||||
<Input
|
||||
className="width-30"
|
||||
className={cx(prometheusConfigOverhaulAuth ? 'width-20' : 'width-30')}
|
||||
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||
value={credentials.clientId || ''}
|
||||
onChange={onClientIdChange}
|
||||
@@ -219,11 +222,20 @@ export const AzureCredentialsForm = (props: Props) => {
|
||||
<InlineFormLabel htmlFor="azure-client-secret" className="width-12">
|
||||
Client Secret
|
||||
</InlineFormLabel>
|
||||
<Input id="azure-client-secret" className="width-25" placeholder="configured" disabled />
|
||||
<Input
|
||||
id="azure-client-secret"
|
||||
className={cx(prometheusConfigOverhaulAuth ? 'width-20' : 'width-25')}
|
||||
placeholder="configured"
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
{!disabled && (
|
||||
<div className="gf-form">
|
||||
<div className="max-width-30 gf-form-inline">
|
||||
<div
|
||||
className={cx(
|
||||
prometheusConfigOverhaulAuth ? 'max-width-20 gf-form-inline' : 'max-width-30 gf-form-inline'
|
||||
)}
|
||||
>
|
||||
<Button variant="secondary" type="button" onClick={onClientSecretReset}>
|
||||
reset
|
||||
</Button>
|
||||
@@ -237,7 +249,7 @@ export const AzureCredentialsForm = (props: Props) => {
|
||||
<InlineFormLabel className="width-12">Client Secret</InlineFormLabel>
|
||||
<div className="width-15">
|
||||
<Input
|
||||
className="width-30"
|
||||
className={cx(prometheusConfigOverhaulAuth ? 'width-20' : 'width-30')}
|
||||
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||
value={credentials.clientSecret || ''}
|
||||
onChange={onClientSecretChange}
|
||||
@@ -254,7 +266,7 @@ export const AzureCredentialsForm = (props: Props) => {
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<InlineFormLabel className="width-12">Default Subscription</InlineFormLabel>
|
||||
<div className="width-25">
|
||||
<div className={cx(prometheusConfigOverhaulAuth ? 'width-20' : 'width-25')}>
|
||||
<Select
|
||||
value={
|
||||
credentials.defaultSubscriptionId
|
||||
|
@@ -3,6 +3,7 @@ import React, { useRef } from 'react';
|
||||
|
||||
import { SIGV4ConnectionConfig } from '@grafana/aws-sdk';
|
||||
import { DataSourcePluginOptionsEditorProps, DataSourceSettings, GrafanaTheme2 } from '@grafana/data';
|
||||
import { ConfigSection, DataSourceDescription } from '@grafana/experimental';
|
||||
import { Alert, DataSourceHttpSettings, FieldValidationMessage, useTheme2 } from '@grafana/ui';
|
||||
import { config } from 'app/core/config';
|
||||
|
||||
@@ -11,14 +12,19 @@ import { PromOptions } from '../types';
|
||||
import { AlertingSettingsOverhaul } from './AlertingSettingsOverhaul';
|
||||
import { AzureAuthSettings } from './AzureAuthSettings';
|
||||
import { hasCredentials, setDefaultCredentials, resetCredentials } from './AzureCredentialsConfig';
|
||||
import { DataSourcehttpSettingsOverhaul } from './DataSourceHttpSettingsOverhaul';
|
||||
import { PromSettings } from './PromSettings';
|
||||
import { AdvancedHttpSettings } from './overhaul/AdvancedHttpSettings';
|
||||
|
||||
export const PROM_CONFIG_LABEL_WIDTH = 30;
|
||||
|
||||
export type Props = DataSourcePluginOptionsEditorProps<PromOptions>;
|
||||
|
||||
export const ConfigEditor = (props: Props) => {
|
||||
const { options, onOptionsChange } = props;
|
||||
|
||||
const prometheusConfigOverhaulAuth = config.featureToggles.prometheusConfigOverhaulAuth;
|
||||
|
||||
// use ref so this is evaluated only first time it renders and the select does not disappear suddenly.
|
||||
const showAccessOptions = useRef(props.options.access === 'direct');
|
||||
|
||||
@@ -40,29 +46,67 @@ export const ConfigEditor = (props: Props) => {
|
||||
Browser access mode in the Prometheus data source is no longer available. Switch to server access mode.
|
||||
</Alert>
|
||||
)}
|
||||
<DataSourceHttpSettings
|
||||
defaultUrl="http://localhost:9090"
|
||||
dataSourceConfig={options}
|
||||
showAccessOptions={showAccessOptions.current}
|
||||
onChange={onOptionsChange}
|
||||
sigV4AuthToggleEnabled={config.sigV4AuthEnabled}
|
||||
azureAuthSettings={azureAuthSettings}
|
||||
renderSigV4Editor={<SIGV4ConnectionConfig {...props}></SIGV4ConnectionConfig>}
|
||||
secureSocksDSProxyEnabled={config.secureSocksDSProxyEnabled}
|
||||
urlLabel="Prometheus server URL"
|
||||
urlDocs={docsTip()}
|
||||
/>
|
||||
<>
|
||||
<hr className={styles.hrTopSpace} />
|
||||
<h3 className={styles.sectionHeaderPadding}>Additional settings</h3>
|
||||
<p className={`${styles.secondaryGrey} ${styles.subsectionText}`}>
|
||||
Additional settings are optional settings that can be configured for more control over your data source.
|
||||
</p>
|
||||
{/* WRAP IN FEATURE TOGGLE */}
|
||||
{prometheusConfigOverhaulAuth ? (
|
||||
<>
|
||||
<DataSourceDescription
|
||||
dataSourceName="Prometheus"
|
||||
docsLink="https://grafana.com/docs/grafana/latest/datasources/prometheus/configure-prometheus-data-source/"
|
||||
/>
|
||||
<hr className={`${styles.hrTopSpace} ${styles.hrBottomSpace}`} />
|
||||
<DataSourcehttpSettingsOverhaul
|
||||
options={options}
|
||||
onOptionsChange={onOptionsChange}
|
||||
azureAuthSettings={azureAuthSettings}
|
||||
sigV4AuthToggleEnabled={config.sigV4AuthEnabled}
|
||||
renderSigV4Editor={<SIGV4ConnectionConfig {...props}></SIGV4ConnectionConfig>}
|
||||
secureSocksDSProxyEnabled={config.secureSocksDSProxyEnabled}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<DataSourceHttpSettings
|
||||
defaultUrl="http://localhost:9090"
|
||||
dataSourceConfig={options}
|
||||
showAccessOptions={showAccessOptions.current}
|
||||
onChange={onOptionsChange}
|
||||
sigV4AuthToggleEnabled={config.sigV4AuthEnabled}
|
||||
azureAuthSettings={azureAuthSettings}
|
||||
renderSigV4Editor={<SIGV4ConnectionConfig {...props}></SIGV4ConnectionConfig>}
|
||||
secureSocksDSProxyEnabled={config.secureSocksDSProxyEnabled}
|
||||
urlLabel="Prometheus server URL"
|
||||
urlDocs={docsTip()}
|
||||
/>
|
||||
)}
|
||||
{prometheusConfigOverhaulAuth ? (
|
||||
<>
|
||||
<hr />
|
||||
<ConfigSection
|
||||
className={styles.advancedSettings}
|
||||
title="Advanced settings"
|
||||
description="Additional settings are optional settings that can be configured for more control over your data source."
|
||||
>
|
||||
<AdvancedHttpSettings
|
||||
className={styles.advancedHTTPSettingsMargin}
|
||||
config={options}
|
||||
onChange={onOptionsChange}
|
||||
/>
|
||||
<AlertingSettingsOverhaul<PromOptions> options={options} onOptionsChange={onOptionsChange} />
|
||||
<PromSettings options={options} onOptionsChange={onOptionsChange} />
|
||||
</ConfigSection>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<hr className={styles.hrTopSpace} />
|
||||
<h3 className={styles.sectionHeaderPadding}>Additional settings</h3>
|
||||
<p className={`${styles.secondaryGrey} ${styles.subsectionText}`}>
|
||||
Additional settings are optional settings that can be configured for more control over your data source.
|
||||
</p>
|
||||
|
||||
<AlertingSettingsOverhaul<PromOptions> options={options} onOptionsChange={onOptionsChange} />
|
||||
<AlertingSettingsOverhaul<PromOptions> options={options} onOptionsChange={onOptionsChange} />
|
||||
|
||||
<PromSettings options={options} onOptionsChange={onOptionsChange} />
|
||||
</>
|
||||
<PromSettings options={options} onOptionsChange={onOptionsChange} />
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -130,5 +174,20 @@ export function overhaulStyles(theme: GrafanaTheme2) {
|
||||
versionMargin: css`
|
||||
margin-bottom: 12px;
|
||||
`,
|
||||
advancedHTTPSettingsMargin: css`
|
||||
margin: 24px 0 8px 0;
|
||||
`,
|
||||
advancedSettings: css`
|
||||
padding-top: 32px;
|
||||
`,
|
||||
alertingTop: css`
|
||||
margin-top: 40px !important;
|
||||
`,
|
||||
overhaulPageHeading: css`
|
||||
font-weight: 400;
|
||||
`,
|
||||
container: css`
|
||||
maxwidth: 578;
|
||||
`,
|
||||
};
|
||||
}
|
||||
|
@@ -0,0 +1,179 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
|
||||
import { DataSourceSettings } from '@grafana/data';
|
||||
import { Auth, ConnectionSettings, convertLegacyAuthProps } from '@grafana/experimental';
|
||||
import { SecureSocksProxySettings, useTheme2 } from '@grafana/ui';
|
||||
import { AzureAuthSettings } from '@grafana/ui/src/components/DataSourceSettings/types';
|
||||
|
||||
import { PromOptions } from '../types';
|
||||
|
||||
import { docsTip, overhaulStyles } from './ConfigEditor';
|
||||
import { CustomMethod } from './overhaul/types';
|
||||
|
||||
type Props = {
|
||||
options: DataSourceSettings<PromOptions, {}>;
|
||||
onOptionsChange: (options: DataSourceSettings<PromOptions, {}>) => void;
|
||||
azureAuthSettings: AzureAuthSettings;
|
||||
sigV4AuthToggleEnabled: boolean | undefined;
|
||||
renderSigV4Editor: React.ReactNode;
|
||||
secureSocksDSProxyEnabled: boolean;
|
||||
};
|
||||
|
||||
export const DataSourcehttpSettingsOverhaul = (props: Props) => {
|
||||
const {
|
||||
options,
|
||||
onOptionsChange,
|
||||
azureAuthSettings,
|
||||
sigV4AuthToggleEnabled,
|
||||
renderSigV4Editor,
|
||||
secureSocksDSProxyEnabled,
|
||||
} = props;
|
||||
|
||||
const newAuthProps = convertLegacyAuthProps({
|
||||
config: options,
|
||||
onChange: onOptionsChange,
|
||||
});
|
||||
|
||||
const theme = useTheme2();
|
||||
const styles = overhaulStyles(theme);
|
||||
|
||||
// for custom auth methods sigV4 and azure auth
|
||||
let customMethods: CustomMethod[] = [];
|
||||
|
||||
const [sigV4Selected, setSigV4Selected] = useState<boolean>(options.jsonData.sigV4Auth || false);
|
||||
|
||||
const sigV4Id = 'custom-sigV4Id';
|
||||
|
||||
const sigV4Option: CustomMethod = {
|
||||
id: sigV4Id,
|
||||
label: 'SigV4 auth',
|
||||
description: 'This is SigV4 auth description',
|
||||
component: <>{renderSigV4Editor}</>,
|
||||
};
|
||||
|
||||
if (sigV4AuthToggleEnabled) {
|
||||
customMethods.push(sigV4Option);
|
||||
}
|
||||
|
||||
const onSettingsChange = useCallback(
|
||||
(change: Partial<DataSourceSettings<PromOptions, {}>>) => {
|
||||
onOptionsChange({
|
||||
...options,
|
||||
...change,
|
||||
});
|
||||
},
|
||||
[options, onOptionsChange]
|
||||
);
|
||||
|
||||
const azureAuthEnabled: boolean =
|
||||
(azureAuthSettings?.azureAuthSupported && azureAuthSettings.getAzureAuthEnabled(options)) || false;
|
||||
|
||||
const [azureAuthSelected, setAzureAuthSelected] = useState<boolean>(azureAuthEnabled);
|
||||
|
||||
const azureAuthId = 'custom-azureAuthId';
|
||||
|
||||
const azureAuthOption: CustomMethod = {
|
||||
id: azureAuthId,
|
||||
label: 'Azure auth',
|
||||
description: 'This is Azure auth description',
|
||||
component: (
|
||||
<>
|
||||
{azureAuthSettings.azureSettingsUI && (
|
||||
<azureAuthSettings.azureSettingsUI dataSourceConfig={options} onChange={onOptionsChange} />
|
||||
)}
|
||||
</>
|
||||
),
|
||||
};
|
||||
|
||||
// allow the option to show in the dropdown
|
||||
if (azureAuthSettings?.azureAuthSupported) {
|
||||
customMethods.push(azureAuthOption);
|
||||
}
|
||||
|
||||
function returnSelectedMethod() {
|
||||
if (sigV4Selected) {
|
||||
return sigV4Id;
|
||||
}
|
||||
|
||||
if (azureAuthSelected) {
|
||||
return azureAuthId;
|
||||
}
|
||||
|
||||
return newAuthProps.selectedMethod;
|
||||
}
|
||||
|
||||
// Do we need this switch anymore? Update the language.
|
||||
let urlTooltip;
|
||||
switch (options.access) {
|
||||
case 'direct':
|
||||
urlTooltip = (
|
||||
<>
|
||||
Your access method is <em>Browser</em>, this means the URL needs to be accessible from the browser.
|
||||
{docsTip()}
|
||||
</>
|
||||
);
|
||||
break;
|
||||
case 'proxy':
|
||||
urlTooltip = (
|
||||
<>
|
||||
Your access method is <em>Server</em>, this means the URL needs to be accessible from the grafana
|
||||
backend/server.
|
||||
{docsTip()}
|
||||
</>
|
||||
);
|
||||
break;
|
||||
default:
|
||||
urlTooltip = <>Specify a complete HTTP URL (for example http://your_server:8080) {docsTip()}</>;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ConnectionSettings
|
||||
urlPlaceholder="http://localhost:9090"
|
||||
config={options}
|
||||
onChange={onOptionsChange}
|
||||
urlLabel="Prometheus server URL"
|
||||
urlTooltip={urlTooltip}
|
||||
/>
|
||||
<hr className={`${styles.hrTopSpace} ${styles.hrBottomSpace}`} />
|
||||
<Auth
|
||||
// Reshaped legacy props
|
||||
{...newAuthProps}
|
||||
// Your custom auth methods
|
||||
customMethods={customMethods}
|
||||
// Still need to call `onAuthMethodSelect` function from
|
||||
// `newAuthProps` to store the legacy data correctly.
|
||||
// Also make sure to store the data about your component
|
||||
// being selected/unselected.
|
||||
onAuthMethodSelect={(method) => {
|
||||
// handle selecting of custom methods
|
||||
// sigV4Id
|
||||
if (sigV4AuthToggleEnabled) {
|
||||
setSigV4Selected(method === sigV4Id);
|
||||
onSettingsChange({
|
||||
jsonData: { ...options.jsonData, sigV4Auth: method === sigV4Id },
|
||||
});
|
||||
}
|
||||
|
||||
// Azure
|
||||
if (azureAuthSettings?.azureAuthSupported) {
|
||||
setAzureAuthSelected(method === azureAuthId);
|
||||
azureAuthSettings.setAzureAuthEnabled(options, method === azureAuthId);
|
||||
}
|
||||
|
||||
newAuthProps.onAuthMethodSelect(method);
|
||||
}}
|
||||
// If your method is selected pass its id to `selectedMethod`,
|
||||
// otherwise pass the id from converted legacy data
|
||||
selectedMethod={returnSelectedMethod()}
|
||||
/>
|
||||
<div className={styles.sectionBottomPadding} />
|
||||
{secureSocksDSProxyEnabled && (
|
||||
<>
|
||||
<SecureSocksProxySettings options={options} onOptionsChange={onOptionsChange} />
|
||||
<div className={styles.sectionBottomPadding} />
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
@@ -2,6 +2,7 @@ import { css } from '@emotion/css';
|
||||
import React from 'react';
|
||||
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { ConfigSubSection } from '@grafana/experimental';
|
||||
import { Button, useTheme2 } from '@grafana/ui';
|
||||
|
||||
import { ExemplarTraceIdDestination } from '../types';
|
||||
@@ -20,47 +21,47 @@ export function ExemplarsSettings({ options, onChange, disabled }: Props) {
|
||||
const styles = overhaulStyles(theme);
|
||||
return (
|
||||
<div className={styles.sectionBottomPadding}>
|
||||
<h3 className="page-heading">Exemplars</h3>
|
||||
<ConfigSubSection title="Exemplars" className={styles.container}>
|
||||
{options &&
|
||||
options.map((option, index) => {
|
||||
return (
|
||||
<ExemplarSetting
|
||||
key={index}
|
||||
value={option}
|
||||
onChange={(newField) => {
|
||||
const newOptions = [...options];
|
||||
newOptions.splice(index, 1, newField);
|
||||
onChange(newOptions);
|
||||
}}
|
||||
onDelete={() => {
|
||||
const newOptions = [...options];
|
||||
newOptions.splice(index, 1);
|
||||
onChange(newOptions);
|
||||
}}
|
||||
disabled={disabled}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
{options &&
|
||||
options.map((option, index) => {
|
||||
return (
|
||||
<ExemplarSetting
|
||||
key={index}
|
||||
value={option}
|
||||
onChange={(newField) => {
|
||||
const newOptions = [...options];
|
||||
newOptions.splice(index, 1, newField);
|
||||
onChange(newOptions);
|
||||
}}
|
||||
onDelete={() => {
|
||||
const newOptions = [...options];
|
||||
newOptions.splice(index, 1);
|
||||
onChange(newOptions);
|
||||
}}
|
||||
disabled={disabled}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
{!disabled && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
aria-label={selectors.components.DataSource.Prometheus.configPage.exemplarsAddButton}
|
||||
className={css`
|
||||
margin-bottom: 10px;
|
||||
`}
|
||||
icon="plus"
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
const newOptions = [...(options || []), { name: 'traceID' }];
|
||||
onChange(newOptions);
|
||||
}}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
)}
|
||||
{disabled && !options && <i>No exemplars configurations</i>}
|
||||
{!disabled && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
aria-label={selectors.components.DataSource.Prometheus.configPage.exemplarsAddButton}
|
||||
className={css`
|
||||
margin-bottom: 10px;
|
||||
`}
|
||||
icon="plus"
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
const newOptions = [...(options || []), { name: 'traceID' }];
|
||||
onChange(newOptions);
|
||||
}}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
)}
|
||||
{disabled && !options && <i>No exemplars configurations</i>}
|
||||
</ConfigSubSection>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@ import {
|
||||
SelectableValue,
|
||||
updateDatasourcePluginJsonDataOption,
|
||||
} from '@grafana/data';
|
||||
import { ConfigSubSection } from '@grafana/experimental';
|
||||
import { getBackendSrv } from '@grafana/runtime/src';
|
||||
import { InlineField, Input, Select, Switch, useTheme2 } from '@grafana/ui';
|
||||
|
||||
@@ -171,346 +172,353 @@ export const PromSettings = (props: Props) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<h3 className="page-heading">Interval behaviour</h3>
|
||||
<div className="gf-form-group">
|
||||
{/* Scrape interval */}
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<InlineField
|
||||
label="Scrape interval"
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={
|
||||
<ConfigSubSection title="Interval behaviour" className={styles.container}>
|
||||
<div className="gf-form-group">
|
||||
{/* Scrape interval */}
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<InlineField
|
||||
label="Scrape interval"
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={
|
||||
<>
|
||||
This interval is how frequently Prometheus scrapes targets. Set this to the typical scrape and
|
||||
evaluation interval configured in your Prometheus config file. If you set this to a greater value
|
||||
than your Prometheus config file interval, Grafana will evaluate the data according to this interval
|
||||
and you will see less data points. Defaults to 15s. {docsTip()}
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
disabled={options.readOnly}
|
||||
>
|
||||
<>
|
||||
This interval is how frequently Prometheus scrapes targets. Set this to the typical scrape and
|
||||
evaluation interval configured in your Prometheus config file. If you set this to a greater value than
|
||||
your Prometheus config file interval, Grafana will evaluate the data according to this interval and
|
||||
you will see less data points. Defaults to 15s. {docsTip()}
|
||||
<Input
|
||||
className="width-20"
|
||||
value={options.jsonData.timeInterval}
|
||||
spellCheck={false}
|
||||
placeholder="15s"
|
||||
onChange={onChangeHandler('timeInterval', options, onOptionsChange)}
|
||||
onBlur={(e) => updateValidDuration({ ...validDuration, timeInterval: e.currentTarget.value })}
|
||||
/>
|
||||
{validateInput(validDuration.timeInterval, DURATION_REGEX, durationError)}
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
disabled={options.readOnly}
|
||||
>
|
||||
<>
|
||||
<Input
|
||||
className="width-20"
|
||||
value={options.jsonData.timeInterval}
|
||||
spellCheck={false}
|
||||
placeholder="15s"
|
||||
onChange={onChangeHandler('timeInterval', options, onOptionsChange)}
|
||||
onBlur={(e) => updateValidDuration({ ...validDuration, timeInterval: e.currentTarget.value })}
|
||||
/>
|
||||
{validateInput(validDuration.timeInterval, DURATION_REGEX, durationError)}
|
||||
</>
|
||||
</InlineField>
|
||||
</InlineField>
|
||||
</div>
|
||||
</div>
|
||||
{/* Query Timeout */}
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<InlineField
|
||||
label="Query timeout"
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={<>Set the Prometheus query timeout. {docsTip()}</>}
|
||||
interactive={true}
|
||||
disabled={options.readOnly}
|
||||
>
|
||||
<>
|
||||
<Input
|
||||
className="width-20"
|
||||
value={options.jsonData.queryTimeout}
|
||||
onChange={onChangeHandler('queryTimeout', options, onOptionsChange)}
|
||||
spellCheck={false}
|
||||
placeholder="60s"
|
||||
onBlur={(e) => updateValidDuration({ ...validDuration, queryTimeout: e.currentTarget.value })}
|
||||
/>
|
||||
{validateInput(validDuration.queryTimeout, DURATION_REGEX, durationError)}
|
||||
</>
|
||||
</InlineField>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* Query Timeout */}
|
||||
<div className="gf-form-inline">
|
||||
</ConfigSubSection>
|
||||
|
||||
<ConfigSubSection title="Query editor" className={styles.container}>
|
||||
<div className="gf-form-group">
|
||||
<div className="gf-form">
|
||||
<InlineField
|
||||
label="Query timeout"
|
||||
label="Default editor"
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={<>Set the Prometheus query timeout. {docsTip()}</>}
|
||||
interactive={true}
|
||||
disabled={options.readOnly}
|
||||
>
|
||||
<>
|
||||
<Input
|
||||
className="width-20"
|
||||
value={options.jsonData.queryTimeout}
|
||||
onChange={onChangeHandler('queryTimeout', options, onOptionsChange)}
|
||||
spellCheck={false}
|
||||
placeholder="60s"
|
||||
onBlur={(e) => updateValidDuration({ ...validDuration, queryTimeout: e.currentTarget.value })}
|
||||
/>
|
||||
{validateInput(validDuration.queryTimeout, DURATION_REGEX, durationError)}
|
||||
</>
|
||||
</InlineField>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 className="page-heading">Query editor</h3>
|
||||
<div className="gf-form-group">
|
||||
<div className="gf-form">
|
||||
<InlineField
|
||||
label="Default editor"
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={<>Set default editor option for all users of this data source. {docsTip()}</>}
|
||||
interactive={true}
|
||||
disabled={options.readOnly}
|
||||
>
|
||||
<Select
|
||||
aria-label={`Default Editor (Code or Builder)`}
|
||||
options={editorOptions}
|
||||
value={
|
||||
editorOptions.find((o) => o.value === options.jsonData.defaultEditor) ??
|
||||
editorOptions.find((o) => o.value === QueryEditorMode.Builder)
|
||||
}
|
||||
onChange={onChangeHandler('defaultEditor', options, onOptionsChange)}
|
||||
width={40}
|
||||
/>
|
||||
</InlineField>
|
||||
</div>
|
||||
<div className="gf-form">
|
||||
<InlineField
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
label="Disable metrics lookup"
|
||||
tooltip={
|
||||
<>
|
||||
Checking this option will disable the metrics chooser and metric/label support in the query field's
|
||||
autocomplete. This helps if you have performance issues with bigger Prometheus instances. {docsTip()}
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
disabled={options.readOnly}
|
||||
className={styles.switchField}
|
||||
>
|
||||
<Switch
|
||||
value={options.jsonData.disableMetricsLookup ?? false}
|
||||
onChange={onUpdateDatasourceJsonDataOptionChecked(props, 'disableMetricsLookup')}
|
||||
/>
|
||||
</InlineField>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 className="page-heading">Performance</h3>
|
||||
{!options.jsonData.prometheusType && !options.jsonData.prometheusVersion && options.readOnly && (
|
||||
<div className={styles.versionMargin}>
|
||||
For more information on configuring prometheus type and version in data sources, see the{' '}
|
||||
<a
|
||||
className={styles.textUnderline}
|
||||
href="https://grafana.com/docs/grafana/latest/administration/provisioning/"
|
||||
>
|
||||
provisioning documentation
|
||||
</a>
|
||||
.
|
||||
</div>
|
||||
)}
|
||||
<div className="gf-form-group">
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<InlineField
|
||||
label="Prometheus type"
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={
|
||||
<>
|
||||
Set this to the type of your prometheus database, e.g. Prometheus, Cortex, Mimir or Thanos. Changing
|
||||
this field will save your current settings, and attempt to detect the version. Certain types of
|
||||
Prometheus support or do not support various APIs. For example, some types support regex matching for
|
||||
label queries to improve performance. Some types have an API for metadata. If you set this incorrectly
|
||||
you may experience odd behavior when querying metrics and labels. Please check your Prometheus
|
||||
documentation to ensure you enter the correct type. {docsTip()}
|
||||
</>
|
||||
}
|
||||
tooltip={<>Set default editor option for all users of this data source. {docsTip()}</>}
|
||||
interactive={true}
|
||||
disabled={options.readOnly}
|
||||
>
|
||||
<Select
|
||||
aria-label="Prometheus type"
|
||||
options={prometheusFlavorSelectItems}
|
||||
value={prometheusFlavorSelectItems.find((o) => o.value === options.jsonData.prometheusType)}
|
||||
onChange={onChangeHandler(
|
||||
'prometheusType',
|
||||
{
|
||||
...options,
|
||||
jsonData: { ...options.jsonData, prometheusVersion: undefined },
|
||||
},
|
||||
(options) => {
|
||||
// Check buildinfo api and set default version if we can
|
||||
setPrometheusVersion(options, onOptionsChange, onUpdate);
|
||||
return onOptionsChange({
|
||||
...options,
|
||||
jsonData: { ...options.jsonData, prometheusVersion: undefined },
|
||||
});
|
||||
}
|
||||
)}
|
||||
aria-label={`Default Editor (Code or Builder)`}
|
||||
options={editorOptions}
|
||||
value={
|
||||
editorOptions.find((o) => o.value === options.jsonData.defaultEditor) ??
|
||||
editorOptions.find((o) => o.value === QueryEditorMode.Builder)
|
||||
}
|
||||
onChange={onChangeHandler('defaultEditor', options, onOptionsChange)}
|
||||
width={40}
|
||||
/>
|
||||
</InlineField>
|
||||
</div>
|
||||
<div className="gf-form">
|
||||
<InlineField
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
label="Disable metrics lookup"
|
||||
tooltip={
|
||||
<>
|
||||
Checking this option will disable the metrics chooser and metric/label support in the query
|
||||
field's autocomplete. This helps if you have performance issues with bigger Prometheus instances.{' '}
|
||||
{docsTip()}
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
disabled={options.readOnly}
|
||||
className={styles.switchField}
|
||||
>
|
||||
<Switch
|
||||
value={options.jsonData.disableMetricsLookup ?? false}
|
||||
onChange={onUpdateDatasourceJsonDataOptionChecked(props, 'disableMetricsLookup')}
|
||||
/>
|
||||
</InlineField>
|
||||
</div>
|
||||
</div>
|
||||
<div className="gf-form-inline">
|
||||
{options.jsonData.prometheusType && (
|
||||
</ConfigSubSection>
|
||||
|
||||
<ConfigSubSection title="Performance" className={styles.container}>
|
||||
{!options.jsonData.prometheusType && !options.jsonData.prometheusVersion && options.readOnly && (
|
||||
<div className={styles.versionMargin}>
|
||||
For more information on configuring prometheus type and version in data sources, see the{' '}
|
||||
<a
|
||||
className={styles.textUnderline}
|
||||
href="https://grafana.com/docs/grafana/latest/administration/provisioning/"
|
||||
>
|
||||
provisioning documentation
|
||||
</a>
|
||||
.
|
||||
</div>
|
||||
)}
|
||||
<div className="gf-form-group">
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<InlineField
|
||||
label={`${options.jsonData.prometheusType} version`}
|
||||
label="Prometheus type"
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={
|
||||
<>
|
||||
Use this to set the version of your {options.jsonData.prometheusType} instance if it is not
|
||||
automatically configured. {docsTip()}
|
||||
Set this to the type of your prometheus database, e.g. Prometheus, Cortex, Mimir or Thanos. Changing
|
||||
this field will save your current settings, and attempt to detect the version. Certain types of
|
||||
Prometheus support or do not support various APIs. For example, some types support regex matching
|
||||
for label queries to improve performance. Some types have an API for metadata. If you set this
|
||||
incorrectly you may experience odd behavior when querying metrics and labels. Please check your
|
||||
Prometheus documentation to ensure you enter the correct type. {docsTip()}
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
disabled={options.readOnly}
|
||||
>
|
||||
<Select
|
||||
aria-label={`${options.jsonData.prometheusType} type`}
|
||||
options={PromFlavorVersions[options.jsonData.prometheusType]}
|
||||
value={PromFlavorVersions[options.jsonData.prometheusType]?.find(
|
||||
(o) => o.value === options.jsonData.prometheusVersion
|
||||
aria-label="Prometheus type"
|
||||
options={prometheusFlavorSelectItems}
|
||||
value={prometheusFlavorSelectItems.find((o) => o.value === options.jsonData.prometheusType)}
|
||||
onChange={onChangeHandler(
|
||||
'prometheusType',
|
||||
{
|
||||
...options,
|
||||
jsonData: { ...options.jsonData, prometheusVersion: undefined },
|
||||
},
|
||||
(options) => {
|
||||
// Check buildinfo api and set default version if we can
|
||||
setPrometheusVersion(options, onOptionsChange, onUpdate);
|
||||
return onOptionsChange({
|
||||
...options,
|
||||
jsonData: { ...options.jsonData, prometheusVersion: undefined },
|
||||
});
|
||||
}
|
||||
)}
|
||||
onChange={onChangeHandler('prometheusVersion', options, onOptionsChange)}
|
||||
width={40}
|
||||
/>
|
||||
</InlineField>
|
||||
</div>
|
||||
</div>
|
||||
<div className="gf-form-inline">
|
||||
{options.jsonData.prometheusType && (
|
||||
<div className="gf-form">
|
||||
<InlineField
|
||||
label={`${options.jsonData.prometheusType} version`}
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={
|
||||
<>
|
||||
Use this to set the version of your {options.jsonData.prometheusType} instance if it is not
|
||||
automatically configured. {docsTip()}
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
disabled={options.readOnly}
|
||||
>
|
||||
<Select
|
||||
aria-label={`${options.jsonData.prometheusType} type`}
|
||||
options={PromFlavorVersions[options.jsonData.prometheusType]}
|
||||
value={PromFlavorVersions[options.jsonData.prometheusType]?.find(
|
||||
(o) => o.value === options.jsonData.prometheusVersion
|
||||
)}
|
||||
onChange={onChangeHandler('prometheusVersion', options, onOptionsChange)}
|
||||
width={40}
|
||||
/>
|
||||
</InlineField>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{config.featureToggles.prometheusResourceBrowserCache && (
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form max-width-30">
|
||||
<InlineField
|
||||
label="Cache level"
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={
|
||||
<>
|
||||
Sets the browser caching level for editor queries. Higher cache settings are recommended for high
|
||||
cardinality data sources.
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
disabled={options.readOnly}
|
||||
>
|
||||
<Select
|
||||
width={40}
|
||||
onChange={onChangeHandler('cacheLevel', options, onOptionsChange)}
|
||||
options={cacheValueOptions}
|
||||
value={
|
||||
cacheValueOptions.find((o) => o.value === options.jsonData.cacheLevel) ?? PrometheusCacheLevel.Low
|
||||
}
|
||||
/>
|
||||
</InlineField>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{config.featureToggles.prometheusResourceBrowserCache && (
|
||||
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form max-width-30">
|
||||
<InlineField
|
||||
label="Cache level"
|
||||
label="Incremental querying (beta)"
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={
|
||||
<>
|
||||
Sets the browser caching level for editor queries. Higher cache settings are recommended for high
|
||||
cardinality data sources.
|
||||
This feature will change the default behavior of relative queries to always request fresh data from
|
||||
the prometheus instance, instead query results will be cached, and only new records are requested.
|
||||
Turn this on to decrease database and network load.
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
className={styles.switchField}
|
||||
disabled={options.readOnly}
|
||||
>
|
||||
<Switch
|
||||
value={options.jsonData.incrementalQuerying ?? false}
|
||||
onChange={onUpdateDatasourceJsonDataOptionChecked(props, 'incrementalQuerying')}
|
||||
/>
|
||||
</InlineField>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="gf-form-inline">
|
||||
{options.jsonData.incrementalQuerying && (
|
||||
<InlineField
|
||||
label="Query overlap window"
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={
|
||||
<>
|
||||
Set a duration like 10m or 120s or 0s. Default of 10 minutes. This duration will be added to the
|
||||
duration of each incremental request.
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
disabled={options.readOnly}
|
||||
>
|
||||
<Select
|
||||
width={40}
|
||||
onChange={onChangeHandler('cacheLevel', options, onOptionsChange)}
|
||||
options={cacheValueOptions}
|
||||
value={
|
||||
cacheValueOptions.find((o) => o.value === options.jsonData.cacheLevel) ?? PrometheusCacheLevel.Low
|
||||
}
|
||||
<>
|
||||
<Input
|
||||
onBlur={(e) =>
|
||||
updateValidDuration({ ...validDuration, incrementalQueryOverlapWindow: e.currentTarget.value })
|
||||
}
|
||||
className="width-20"
|
||||
value={options.jsonData.incrementalQueryOverlapWindow ?? defaultPrometheusQueryOverlapWindow}
|
||||
onChange={onChangeHandler('incrementalQueryOverlapWindow', options, onOptionsChange)}
|
||||
spellCheck={false}
|
||||
/>
|
||||
{validateInput(validDuration.incrementalQueryOverlapWindow, MULTIPLE_DURATION_REGEX, durationError)}
|
||||
</>
|
||||
</InlineField>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form max-width-30">
|
||||
<InlineField
|
||||
label="Disable recording rules (beta)"
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={<>This feature will disable recording rules Turn this on to improve dashboard performance</>}
|
||||
interactive={true}
|
||||
className={styles.switchField}
|
||||
disabled={options.readOnly}
|
||||
>
|
||||
<Switch
|
||||
value={options.jsonData.disableRecordingRules ?? false}
|
||||
onChange={onUpdateDatasourceJsonDataOptionChecked(props, 'disableRecordingRules')}
|
||||
/>
|
||||
</InlineField>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form max-width-30">
|
||||
<InlineField
|
||||
label="Incremental querying (beta)"
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={
|
||||
<>
|
||||
This feature will change the default behavior of relative queries to always request fresh data from
|
||||
the prometheus instance, instead query results will be cached, and only new records are requested.
|
||||
Turn this on to decrease database and network load.
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
className={styles.switchField}
|
||||
disabled={options.readOnly}
|
||||
>
|
||||
<Switch
|
||||
value={options.jsonData.incrementalQuerying ?? false}
|
||||
onChange={onUpdateDatasourceJsonDataOptionChecked(props, 'incrementalQuerying')}
|
||||
/>
|
||||
</InlineField>
|
||||
</div>
|
||||
</div>
|
||||
</ConfigSubSection>
|
||||
|
||||
<div className="gf-form-inline">
|
||||
{options.jsonData.incrementalQuerying && (
|
||||
<InlineField
|
||||
label="Query overlap window"
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={
|
||||
<>
|
||||
Set a duration like 10m or 120s or 0s. Default of 10 minutes. This duration will be added to the
|
||||
duration of each incremental request.
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
disabled={options.readOnly}
|
||||
>
|
||||
<>
|
||||
<ConfigSubSection title="Other" className={styles.container}>
|
||||
<div className="gf-form-group">
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form max-width-30">
|
||||
<InlineField
|
||||
label="Custom query parameters"
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={
|
||||
<>
|
||||
Add custom parameters to the Prometheus query URL. For example timeout, partial_response, dedup, or
|
||||
max_source_resolution. Multiple parameters should be concatenated together with an ‘&’. {docsTip()}
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
disabled={options.readOnly}
|
||||
>
|
||||
<Input
|
||||
onBlur={(e) =>
|
||||
updateValidDuration({ ...validDuration, incrementalQueryOverlapWindow: e.currentTarget.value })
|
||||
}
|
||||
className="width-25"
|
||||
value={options.jsonData.incrementalQueryOverlapWindow ?? defaultPrometheusQueryOverlapWindow}
|
||||
onChange={onChangeHandler('incrementalQueryOverlapWindow', options, onOptionsChange)}
|
||||
className="width-20"
|
||||
value={options.jsonData.customQueryParameters}
|
||||
onChange={onChangeHandler('customQueryParameters', options, onOptionsChange)}
|
||||
spellCheck={false}
|
||||
placeholder="Example: max_source_resolution=5m&timeout=10"
|
||||
/>
|
||||
{validateInput(validDuration.incrementalQueryOverlapWindow, MULTIPLE_DURATION_REGEX, durationError)}
|
||||
</>
|
||||
</InlineField>
|
||||
)}
|
||||
</InlineField>
|
||||
</div>
|
||||
</div>
|
||||
<div className="gf-form-inline">
|
||||
{/* HTTP Method */}
|
||||
<div className="gf-form">
|
||||
<InlineField
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={
|
||||
<>
|
||||
You can use either POST or GET HTTP method to query your Prometheus data source. POST is the
|
||||
recommended method as it allows bigger queries. Change this to GET if you have a Prometheus version
|
||||
older than 2.1 or if POST requests are restricted in your network. {docsTip()}
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
label="HTTP method"
|
||||
disabled={options.readOnly}
|
||||
>
|
||||
<Select
|
||||
width={40}
|
||||
aria-label="Select HTTP method"
|
||||
options={httpOptions}
|
||||
value={httpOptions.find((o) => o.value === options.jsonData.httpMethod)}
|
||||
onChange={onChangeHandler('httpMethod', options, onOptionsChange)}
|
||||
/>
|
||||
</InlineField>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ConfigSubSection>
|
||||
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form max-width-30">
|
||||
<InlineField
|
||||
label="Disable recording rules (beta)"
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={<>This feature will disable recording rules Turn this on to improve dashboard performance</>}
|
||||
interactive={true}
|
||||
className={styles.switchField}
|
||||
disabled={options.readOnly}
|
||||
>
|
||||
<Switch
|
||||
value={options.jsonData.disableRecordingRules ?? false}
|
||||
onChange={onUpdateDatasourceJsonDataOptionChecked(props, 'disableRecordingRules')}
|
||||
/>
|
||||
</InlineField>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h3 className="page-heading">Other</h3>
|
||||
<div className="gf-form-group">
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form max-width-30">
|
||||
<InlineField
|
||||
label="Custom query parameters"
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={
|
||||
<>
|
||||
Add custom parameters to the Prometheus query URL. For example timeout, partial_response, dedup, or
|
||||
max_source_resolution. Multiple parameters should be concatenated together with an ‘&’. {docsTip()}
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
disabled={options.readOnly}
|
||||
>
|
||||
<Input
|
||||
className="width-20"
|
||||
value={options.jsonData.customQueryParameters}
|
||||
onChange={onChangeHandler('customQueryParameters', options, onOptionsChange)}
|
||||
spellCheck={false}
|
||||
placeholder="Example: max_source_resolution=5m&timeout=10"
|
||||
/>
|
||||
</InlineField>
|
||||
</div>
|
||||
</div>
|
||||
<div className="gf-form-inline">
|
||||
{/* HTTP Method */}
|
||||
<div className="gf-form">
|
||||
<InlineField
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={
|
||||
<>
|
||||
You can use either POST or GET HTTP method to query your Prometheus data source. POST is the
|
||||
recommended method as it allows bigger queries. Change this to GET if you have a Prometheus version
|
||||
older than 2.1 or if POST requests are restricted in your network. {docsTip()}
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
label="HTTP method"
|
||||
disabled={options.readOnly}
|
||||
>
|
||||
<Select
|
||||
width={40}
|
||||
aria-label="Select HTTP method"
|
||||
options={httpOptions}
|
||||
value={httpOptions.find((o) => o.value === options.jsonData.httpMethod)}
|
||||
onChange={onChangeHandler('httpMethod', options, onOptionsChange)}
|
||||
/>
|
||||
</InlineField>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ExemplarsSettings
|
||||
options={options.jsonData.exemplarTraceIdDestinations}
|
||||
onChange={(exemplarOptions) =>
|
||||
|
@@ -0,0 +1,93 @@
|
||||
import { css, cx } from '@emotion/css';
|
||||
import React from 'react';
|
||||
|
||||
import { ConfigSubSection } from '@grafana/experimental';
|
||||
import { InlineField, Input, TagsInput } from '@grafana/ui';
|
||||
|
||||
import { PROM_CONFIG_LABEL_WIDTH } from '../ConfigEditor';
|
||||
|
||||
import { Config, OnChangeHandler } from './ConnectionSettings';
|
||||
|
||||
// THIS FILE IS COPIED FROM GRAFANA/EXPERIMENTAL
|
||||
// BECAUSE THE STYLES DO NOT MATCH THE PROM CONFIG
|
||||
// THE TYPES ARE WRITTEN THERE WHERE THEY ARE NOT AS STRICT
|
||||
// @ts-ignore
|
||||
export type Props<C extends Config = Config> = {
|
||||
config: C;
|
||||
onChange: OnChangeHandler<C>;
|
||||
className?: string;
|
||||
};
|
||||
// @ts-ignore
|
||||
export const AdvancedHttpSettings: <C extends Config = Config>(props: Props<C>) => JSX.Element = ({
|
||||
config,
|
||||
onChange,
|
||||
className,
|
||||
}) => {
|
||||
const onCookiesChange = (cookies: string[]) => {
|
||||
onChange({
|
||||
...config,
|
||||
jsonData: {
|
||||
...config.jsonData,
|
||||
keepCookies: cookies,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const onTimeoutChange = (event: React.FormEvent<HTMLInputElement>) => {
|
||||
onChange({
|
||||
...config,
|
||||
jsonData: {
|
||||
...config.jsonData,
|
||||
timeout: parseInt(event.currentTarget.value, 10),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const styles = {
|
||||
container: css({
|
||||
maxWidth: 578,
|
||||
}),
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ConfigSubSection title="Advanced HTTP settings" className={cx(styles.container, className)}>
|
||||
<InlineField
|
||||
htmlFor="advanced-http-cookies"
|
||||
label="Allowed cookies"
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip="Grafana proxy deletes forwarded cookies by default. Specify cookies by name that should be forwarded to the data source."
|
||||
disabled={config.readOnly}
|
||||
grow
|
||||
>
|
||||
<TagsInput
|
||||
className="width-20"
|
||||
id="advanced-http-cookies"
|
||||
placeholder="New cookie (hit enter to add)"
|
||||
tags={config.jsonData.keepCookies}
|
||||
onChange={onCookiesChange}
|
||||
/>
|
||||
</InlineField>
|
||||
<InlineField
|
||||
htmlFor="advanced-http-timeout"
|
||||
label="Timeout"
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip="HTTP request timeout in seconds"
|
||||
disabled={config.readOnly}
|
||||
grow
|
||||
>
|
||||
<Input
|
||||
className="width-20"
|
||||
id="advanced-http-timeout"
|
||||
type="number"
|
||||
min={0}
|
||||
placeholder="Timeout in seconds"
|
||||
aria-label="Timeout in seconds"
|
||||
value={config.jsonData.timeout}
|
||||
onChange={onTimeoutChange}
|
||||
/>
|
||||
</InlineField>
|
||||
</ConfigSubSection>
|
||||
</>
|
||||
);
|
||||
};
|
@@ -0,0 +1,90 @@
|
||||
import { css, cx } from '@emotion/css';
|
||||
import React, { ReactNode } from 'react';
|
||||
|
||||
import { DataSourceJsonData, DataSourceSettings } from '@grafana/data';
|
||||
import { ConfigSection } from '@grafana/experimental';
|
||||
import { InlineField, Input, PopoverContent } from '@grafana/ui';
|
||||
|
||||
import { PromOptions } from '../../types';
|
||||
// THIS FILE IS COPIED FROM GRAFANA/EXPERIMENTAL
|
||||
// BECAUSE IT CONTAINS TYPES THAT ARE REQUIRED IN THE ADVANCEDHTTPSETTINGS COMPONENT
|
||||
// THE TYPES ARE WRITTEN IN EXPERIMENTAL WHERE THEY ARE NOT AS STRICT
|
||||
// @ts-ignore
|
||||
export type Config<JSONData extends DataSourceJsonData, SecureJSONData> = DataSourceSettings<
|
||||
// @ts-ignore
|
||||
JSONData,
|
||||
// @ts-ignore
|
||||
SecureJSONData
|
||||
>;
|
||||
// @ts-ignore
|
||||
export type OnChangeHandler<C extends Config = Config> = (options: DataSourceSettings<PromOptions, {}>) => void;
|
||||
// @ts-ignore
|
||||
export type Props<C extends Config = Config> = {
|
||||
config: C;
|
||||
onChange: OnChangeHandler<C>;
|
||||
description?: ReactNode;
|
||||
urlPlaceholder?: string;
|
||||
urlTooltip?: PopoverContent;
|
||||
urlLabel?: string;
|
||||
className?: string;
|
||||
};
|
||||
// @ts-ignore
|
||||
export const ConnectionSettings: <C extends Config = Config>(props: Props<C>) => JSX.Element = ({
|
||||
config,
|
||||
onChange,
|
||||
description,
|
||||
urlPlaceholder,
|
||||
urlTooltip,
|
||||
urlLabel,
|
||||
className,
|
||||
}) => {
|
||||
const isValidUrl = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/.test(
|
||||
config.url
|
||||
);
|
||||
|
||||
const styles = {
|
||||
container: css({
|
||||
maxWidth: 578,
|
||||
}),
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ConfigSection title="Connection" description={description} className={cx(styles.container, className)}>
|
||||
<InlineField
|
||||
htmlFor="connection-url"
|
||||
label={urlLabel || 'URL'}
|
||||
labelWidth={24}
|
||||
tooltip={
|
||||
urlTooltip || (
|
||||
<>
|
||||
Specify a complete HTTP URL
|
||||
<br />
|
||||
(for example https://example.com:8080)
|
||||
</>
|
||||
)
|
||||
}
|
||||
grow
|
||||
disabled={config.readOnly}
|
||||
required
|
||||
invalid={!isValidUrl && !config.readOnly}
|
||||
error={isValidUrl ? '' : 'Please enter a valid URL'}
|
||||
interactive
|
||||
>
|
||||
<Input
|
||||
id="connection-url"
|
||||
aria-label="Datasource HTTP settings url"
|
||||
onChange={(event) =>
|
||||
onChange({
|
||||
...config,
|
||||
url: event.currentTarget.value,
|
||||
})
|
||||
}
|
||||
value={config.url || ''}
|
||||
placeholder={urlPlaceholder || 'URL'}
|
||||
/>
|
||||
</InlineField>
|
||||
</ConfigSection>
|
||||
</>
|
||||
);
|
||||
};
|
@@ -0,0 +1,11 @@
|
||||
import { ReactElement } from 'react';
|
||||
|
||||
// these are not available yet in grafana
|
||||
export type CustomMethodId = `custom-${string}`;
|
||||
|
||||
export type CustomMethod = {
|
||||
id: CustomMethodId;
|
||||
label: string;
|
||||
description: string;
|
||||
component: ReactElement;
|
||||
};
|
@@ -45,6 +45,7 @@ export interface PromOptions extends DataSourceJsonData {
|
||||
incrementalQuerying?: boolean;
|
||||
incrementalQueryOverlapWindow?: string;
|
||||
disableRecordingRules?: boolean;
|
||||
sigV4Auth?: boolean;
|
||||
}
|
||||
|
||||
export type ExemplarTraceIdDestination = {
|
||||
|
Reference in New Issue
Block a user