mirror of
https://github.com/grafana/grafana.git
synced 2025-02-16 18:34:52 -06:00
SQL: Update configuration pages (#75525)
* update psql config page to follow guidelines * make small changes to mysql config page * update shared tls config to match guidelines * revert back to text area * remove type assertion * prettier * remove unused imports * update required fields * add secure sox proxy to additional settings * make additional settings collapsible * make user permissions collapsable * make db name and password optional * mysql: move from alert to collapse
This commit is contained in:
parent
e94e283cc6
commit
f6a0f6912f
@ -7,7 +7,8 @@ import {
|
|||||||
onUpdateDatasourceSecureJsonDataOption,
|
onUpdateDatasourceSecureJsonDataOption,
|
||||||
updateDatasourcePluginResetOption,
|
updateDatasourcePluginResetOption,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { InlineField, SecretTextArea } from '@grafana/ui';
|
import { Stack } from '@grafana/experimental';
|
||||||
|
import { Field, Icon, Label, SecretTextArea, Tooltip } from '@grafana/ui';
|
||||||
|
|
||||||
export interface Props<T extends DataSourceJsonData, S> {
|
export interface Props<T extends DataSourceJsonData, S> {
|
||||||
editorProps: DataSourcePluginOptionsEditorProps<T, S>;
|
editorProps: DataSourcePluginOptionsEditorProps<T, S>;
|
||||||
@ -18,20 +19,31 @@ export interface Props<T extends DataSourceJsonData, S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const TLSSecretsConfig = <T extends DataSourceJsonData, S extends {} = {}>(props: Props<T, S>) => {
|
export const TLSSecretsConfig = <T extends DataSourceJsonData, S extends {} = {}>(props: Props<T, S>) => {
|
||||||
const { labelWidth, editorProps, showCACert, showKeyPair = true } = props;
|
const { editorProps, showCACert, showKeyPair = true } = props;
|
||||||
const { secureJsonFields } = editorProps.options;
|
const { secureJsonFields } = editorProps.options;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{showKeyPair ? (
|
{showKeyPair ? (
|
||||||
<InlineField
|
<Field
|
||||||
tooltip={
|
label={
|
||||||
<span>To authenticate with an TLS/SSL client certificate, provide the client certificate here.</span>
|
<Label>
|
||||||
|
<Stack gap={0.5}>
|
||||||
|
<span>TLS/SSL Client Certificate</span>
|
||||||
|
<Tooltip
|
||||||
|
content={
|
||||||
|
<span>
|
||||||
|
To authenticate with an TLS/SSL client certificate, provide the client certificate here.
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Icon name="info-circle" size="sm" />
|
||||||
|
</Tooltip>
|
||||||
|
</Stack>
|
||||||
|
</Label>
|
||||||
}
|
}
|
||||||
labelWidth={labelWidth}
|
|
||||||
label="TLS/SSL Client Certificate"
|
|
||||||
>
|
>
|
||||||
<SecretTextArea
|
<SecretTextArea
|
||||||
placeholder="Begins with -----BEGIN CERTIFICATE-----"
|
placeholder="-----BEGIN CERTIFICATE-----"
|
||||||
cols={45}
|
cols={45}
|
||||||
rows={7}
|
rows={7}
|
||||||
isConfigured={secureJsonFields && secureJsonFields.tlsClientCert}
|
isConfigured={secureJsonFields && secureJsonFields.tlsClientCert}
|
||||||
@ -39,17 +51,28 @@ export const TLSSecretsConfig = <T extends DataSourceJsonData, S extends {} = {}
|
|||||||
onReset={() => {
|
onReset={() => {
|
||||||
updateDatasourcePluginResetOption(editorProps, 'tlsClientCert');
|
updateDatasourcePluginResetOption(editorProps, 'tlsClientCert');
|
||||||
}}
|
}}
|
||||||
></SecretTextArea>
|
/>
|
||||||
</InlineField>
|
</Field>
|
||||||
) : null}
|
) : null}
|
||||||
{showCACert ? (
|
{showCACert ? (
|
||||||
<InlineField
|
<Field
|
||||||
tooltip={<span>If the selected TLS/SSL mode requires a server root certificate, provide it here.</span>}
|
label={
|
||||||
labelWidth={labelWidth}
|
<Label>
|
||||||
label="TLS/SSL Root Certificate"
|
<Stack gap={0.5}>
|
||||||
|
<span>TLS/SSL Root Certificate</span>
|
||||||
|
<Tooltip
|
||||||
|
content={
|
||||||
|
<span>If the selected TLS/SSL mode requires a server root certificate, provide it here.</span>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Icon name="info-circle" size="sm" />
|
||||||
|
</Tooltip>
|
||||||
|
</Stack>
|
||||||
|
</Label>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<SecretTextArea
|
<SecretTextArea
|
||||||
placeholder="Begins with -----BEGIN CERTIFICATE-----"
|
placeholder="-----BEGIN CERTIFICATE-----"
|
||||||
cols={45}
|
cols={45}
|
||||||
rows={7}
|
rows={7}
|
||||||
isConfigured={secureJsonFields && secureJsonFields.tlsCACert}
|
isConfigured={secureJsonFields && secureJsonFields.tlsCACert}
|
||||||
@ -57,17 +80,26 @@ export const TLSSecretsConfig = <T extends DataSourceJsonData, S extends {} = {}
|
|||||||
onReset={() => {
|
onReset={() => {
|
||||||
updateDatasourcePluginResetOption(editorProps, 'tlsCACert');
|
updateDatasourcePluginResetOption(editorProps, 'tlsCACert');
|
||||||
}}
|
}}
|
||||||
></SecretTextArea>
|
/>
|
||||||
</InlineField>
|
</Field>
|
||||||
) : null}
|
) : null}
|
||||||
{showKeyPair ? (
|
{showKeyPair ? (
|
||||||
<InlineField
|
<Field
|
||||||
tooltip={<span>To authenticate with a client TLS/SSL certificate, provide the key here.</span>}
|
label={
|
||||||
labelWidth={labelWidth}
|
<Label>
|
||||||
label="TLS/SSL Client Key"
|
<Stack gap={0.5}>
|
||||||
|
<span>TLS/SSL Client Key</span>
|
||||||
|
<Tooltip
|
||||||
|
content={<span>To authenticate with a client TLS/SSL certificate, provide the key here.</span>}
|
||||||
|
>
|
||||||
|
<Icon name="info-circle" size="sm" />
|
||||||
|
</Tooltip>
|
||||||
|
</Stack>
|
||||||
|
</Label>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<SecretTextArea
|
<SecretTextArea
|
||||||
placeholder="Begins with -----BEGIN RSA PRIVATE KEY-----"
|
placeholder="-----BEGIN RSA PRIVATE KEY-----"
|
||||||
cols={45}
|
cols={45}
|
||||||
rows={7}
|
rows={7}
|
||||||
isConfigured={secureJsonFields && secureJsonFields.tlsClientKey}
|
isConfigured={secureJsonFields && secureJsonFields.tlsClientKey}
|
||||||
@ -75,8 +107,8 @@ export const TLSSecretsConfig = <T extends DataSourceJsonData, S extends {} = {}
|
|||||||
onReset={() => {
|
onReset={() => {
|
||||||
updateDatasourcePluginResetOption(editorProps, 'tlsClientKey');
|
updateDatasourcePluginResetOption(editorProps, 'tlsClientKey');
|
||||||
}}
|
}}
|
||||||
></SecretTextArea>
|
/>
|
||||||
</InlineField>
|
</Field>
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { SyntheticEvent } from 'react';
|
import React, { SyntheticEvent, useState } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DataSourcePluginOptionsEditorProps,
|
DataSourcePluginOptionsEditorProps,
|
||||||
@ -7,16 +7,15 @@ import {
|
|||||||
updateDatasourcePluginJsonDataOption,
|
updateDatasourcePluginJsonDataOption,
|
||||||
updateDatasourcePluginResetOption,
|
updateDatasourcePluginResetOption,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { ConfigSection, DataSourceDescription, Stack } from '@grafana/experimental';
|
import { ConfigSection, ConfigSubSection, DataSourceDescription, Stack } from '@grafana/experimental';
|
||||||
import { config } from '@grafana/runtime';
|
import { config } from '@grafana/runtime';
|
||||||
import {
|
import {
|
||||||
Alert,
|
Collapse,
|
||||||
Divider,
|
Divider,
|
||||||
Field,
|
Field,
|
||||||
Icon,
|
Icon,
|
||||||
Input,
|
Input,
|
||||||
Label,
|
Label,
|
||||||
Link,
|
|
||||||
SecretInput,
|
SecretInput,
|
||||||
SecureSocksProxySettings,
|
SecureSocksProxySettings,
|
||||||
Switch,
|
Switch,
|
||||||
@ -29,6 +28,8 @@ import { useMigrateDatabaseFields } from 'app/features/plugins/sql/components/co
|
|||||||
import { MySQLOptions } from '../types';
|
import { MySQLOptions } from '../types';
|
||||||
|
|
||||||
export const ConfigurationEditor = (props: DataSourcePluginOptionsEditorProps<MySQLOptions>) => {
|
export const ConfigurationEditor = (props: DataSourcePluginOptionsEditorProps<MySQLOptions>) => {
|
||||||
|
const [isOpen, setIsOpen] = useState(true);
|
||||||
|
|
||||||
const { options, onOptionsChange } = props;
|
const { options, onOptionsChange } = props;
|
||||||
const jsonData = options.jsonData;
|
const jsonData = options.jsonData;
|
||||||
|
|
||||||
@ -57,11 +58,22 @@ export const ConfigurationEditor = (props: DataSourcePluginOptionsEditorProps<My
|
|||||||
<DataSourceDescription
|
<DataSourceDescription
|
||||||
dataSourceName="MySQL"
|
dataSourceName="MySQL"
|
||||||
docsLink="https://grafana.com/docs/grafana/latest/datasources/mysql/"
|
docsLink="https://grafana.com/docs/grafana/latest/datasources/mysql/"
|
||||||
hasRequiredFields={false}
|
hasRequiredFields={true}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
|
<Collapse collapsible label="User Permission" isOpen={isOpen} onToggle={() => setIsOpen((x) => !x)}>
|
||||||
|
The database user should only be granted SELECT permissions on the specified database & tables you want to
|
||||||
|
query. <br />
|
||||||
|
Grafana does not validate that queries are safe so queries can contain any SQL statement. For example,
|
||||||
|
statements like <code>USE otherdb;</code> and <code>DROP TABLE user;</code> would be executed. <br />
|
||||||
|
To protect against this we <strong>Highly</strong> recommend you create a specific MySQL user with restricted
|
||||||
|
permissions. Check out the docs for more information.
|
||||||
|
</Collapse>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
<ConfigSection title="Connection">
|
<ConfigSection title="Connection">
|
||||||
<Field label="Host URL" required>
|
<Field label="Host URL" required>
|
||||||
<Input
|
<Input
|
||||||
@ -73,11 +85,7 @@ export const ConfigurationEditor = (props: DataSourcePluginOptionsEditorProps<My
|
|||||||
onChange={onDSOptionChanged('url')}
|
onChange={onDSOptionChanged('url')}
|
||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
</ConfigSection>
|
|
||||||
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
<ConfigSection title="Authentication">
|
|
||||||
<Field label="Database name">
|
<Field label="Database name">
|
||||||
<Input
|
<Input
|
||||||
width={WIDTH_LONG}
|
width={WIDTH_LONG}
|
||||||
@ -87,8 +95,12 @@ export const ConfigurationEditor = (props: DataSourcePluginOptionsEditorProps<My
|
|||||||
onChange={onUpdateDatasourceJsonDataOption(props, 'database')}
|
onChange={onUpdateDatasourceJsonDataOption(props, 'database')}
|
||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
|
</ConfigSection>
|
||||||
|
|
||||||
<Field label="Username">
|
<Divider />
|
||||||
|
|
||||||
|
<ConfigSection title="Authentication">
|
||||||
|
<Field label="Username" required>
|
||||||
<Input
|
<Input
|
||||||
width={WIDTH_LONG}
|
width={WIDTH_LONG}
|
||||||
value={options.user || ''}
|
value={options.user || ''}
|
||||||
@ -136,13 +148,6 @@ export const ConfigurationEditor = (props: DataSourcePluginOptionsEditorProps<My
|
|||||||
</Field>
|
</Field>
|
||||||
</ConfigSection>
|
</ConfigSection>
|
||||||
|
|
||||||
{config.secureSocksDSProxyEnabled && (
|
|
||||||
<>
|
|
||||||
<Divider />
|
|
||||||
<SecureSocksProxySettings options={options} onOptionsChange={onOptionsChange} />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{jsonData.tlsAuth || jsonData.tlsAuthWithCACert ? (
|
{jsonData.tlsAuth || jsonData.tlsAuthWithCACert ? (
|
||||||
<>
|
<>
|
||||||
<Divider />
|
<Divider />
|
||||||
@ -153,7 +158,7 @@ export const ConfigurationEditor = (props: DataSourcePluginOptionsEditorProps<My
|
|||||||
showCACert={jsonData.tlsAuthWithCACert}
|
showCACert={jsonData.tlsAuthWithCACert}
|
||||||
showKeyPair={jsonData.tlsAuth}
|
showKeyPair={jsonData.tlsAuth}
|
||||||
editorProps={props}
|
editorProps={props}
|
||||||
labelWidth={25}
|
labelWidth={WIDTH_LONG}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</ConfigSection>
|
</ConfigSection>
|
||||||
@ -162,84 +167,74 @@ export const ConfigurationEditor = (props: DataSourcePluginOptionsEditorProps<My
|
|||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<ConfigSection title="Additional settings">
|
<ConfigSection title="Additional settings" isCollapsible>
|
||||||
<Field
|
<ConfigSubSection title="MySQL Options">
|
||||||
label={
|
<Field
|
||||||
<Label>
|
label={
|
||||||
<Stack gap={0.5}>
|
<Label>
|
||||||
<span>Session timezone</span>
|
<Stack gap={0.5}>
|
||||||
<Tooltip
|
<span>Session timezone</span>
|
||||||
content={
|
<Tooltip
|
||||||
<span>
|
content={
|
||||||
Specify the time zone used in the database session, e.g. <code>Europe/Berlin</code> or
|
<span>
|
||||||
<code>+02:00</code>. This is necessary, if the timezone of the database (or the host of the
|
Specify the time zone used in the database session, e.g. <code>Europe/Berlin</code> or
|
||||||
database) is set to something other than UTC. The value is set in the session with
|
<code>+02:00</code>. This is necessary, if the timezone of the database (or the host of the
|
||||||
<code>SET time_zone='...'</code>. If you leave this field empty, the timezone is not
|
database) is set to something other than UTC. The value is set in the session with
|
||||||
updated. You can find more information in the MySQL documentation.
|
<code>SET time_zone='...'</code>. If you leave this field empty, the timezone is not
|
||||||
</span>
|
updated. You can find more information in the MySQL documentation.
|
||||||
}
|
</span>
|
||||||
>
|
}
|
||||||
<Icon name="info-circle" size="sm" />
|
>
|
||||||
</Tooltip>
|
<Icon name="info-circle" size="sm" />
|
||||||
</Stack>
|
</Tooltip>
|
||||||
</Label>
|
</Stack>
|
||||||
}
|
</Label>
|
||||||
>
|
}
|
||||||
<Input
|
>
|
||||||
width={WIDTH_LONG}
|
<Input
|
||||||
value={jsonData.timezone || ''}
|
width={WIDTH_LONG}
|
||||||
onChange={onUpdateDatasourceJsonDataOption(props, 'timezone')}
|
value={jsonData.timezone || ''}
|
||||||
placeholder="Europe/Berlin or +02:00"
|
onChange={onUpdateDatasourceJsonDataOption(props, 'timezone')}
|
||||||
/>
|
placeholder="Europe/Berlin or +02:00"
|
||||||
</Field>
|
/>
|
||||||
|
</Field>
|
||||||
|
|
||||||
<Field
|
<Field
|
||||||
label={
|
label={
|
||||||
<Label>
|
<Label>
|
||||||
<Stack gap={0.5}>
|
<Stack gap={0.5}>
|
||||||
<span>Min time interval</span>
|
<span>Min time interval</span>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
content={
|
content={
|
||||||
<span>
|
<span>
|
||||||
A lower limit for the auto group by time interval. Recommended to be set to write frequency, for
|
A lower limit for the auto group by time interval. Recommended to be set to write frequency, for
|
||||||
example
|
example
|
||||||
<code>1m</code> if your data is written every minute.
|
<code>1m</code> if your data is written every minute.
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Icon name="info-circle" size="sm" />
|
<Icon name="info-circle" size="sm" />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Label>
|
</Label>
|
||||||
}
|
}
|
||||||
description="A lower limit for the auto group by time interval. Recommended to be set to write frequency, for example 1m if your data is written every minute."
|
description="A lower limit for the auto group by time interval. Recommended to be set to write frequency, for example 1m if your data is written every minute."
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
width={WIDTH_LONG}
|
width={WIDTH_LONG}
|
||||||
placeholder="1m"
|
placeholder="1m"
|
||||||
value={jsonData.timeInterval || ''}
|
value={jsonData.timeInterval || ''}
|
||||||
onChange={onUpdateDatasourceJsonDataOption(props, 'timeInterval')}
|
onChange={onUpdateDatasourceJsonDataOption(props, 'timeInterval')}
|
||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
|
</ConfigSubSection>
|
||||||
|
|
||||||
|
<ConnectionLimits options={options} onOptionsChange={onOptionsChange} />
|
||||||
|
|
||||||
|
{config.secureSocksDSProxyEnabled && (
|
||||||
|
<SecureSocksProxySettings options={options} onOptionsChange={onOptionsChange} />
|
||||||
|
)}
|
||||||
</ConfigSection>
|
</ConfigSection>
|
||||||
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
<ConnectionLimits options={options} onOptionsChange={onOptionsChange} />
|
|
||||||
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
<Alert title="User Permission" severity="info">
|
|
||||||
The database user should only be granted SELECT permissions on the specified database & tables you want to
|
|
||||||
query. Grafana does not validate that queries are safe so queries can contain any SQL statement. For example,
|
|
||||||
statements like <code>USE otherdb;</code> and <code>DROP TABLE user;</code> would be executed. To protect
|
|
||||||
against this we <strong>Highly</strong> recommend you create a specific MySQL user with restricted permissions.
|
|
||||||
Check out the{' '}
|
|
||||||
<Link rel="noreferrer" target="_blank" href="http://docs.grafana.org/features/datasources/mysql/">
|
|
||||||
MySQL Data Source Docs
|
|
||||||
</Link>{' '}
|
|
||||||
for more information.
|
|
||||||
</Alert>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -8,17 +8,20 @@ import {
|
|||||||
updateDatasourcePluginJsonDataOption,
|
updateDatasourcePluginJsonDataOption,
|
||||||
updateDatasourcePluginResetOption,
|
updateDatasourcePluginResetOption,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
|
import { ConfigSection, ConfigSubSection, DataSourceDescription, Stack } from '@grafana/experimental';
|
||||||
import { config } from '@grafana/runtime';
|
import { config } from '@grafana/runtime';
|
||||||
import {
|
import {
|
||||||
Alert,
|
Divider,
|
||||||
InlineSwitch,
|
|
||||||
FieldSet,
|
|
||||||
InlineField,
|
|
||||||
InlineFieldRow,
|
|
||||||
Input,
|
Input,
|
||||||
Select,
|
Select,
|
||||||
SecretInput,
|
SecretInput,
|
||||||
Link,
|
Field,
|
||||||
|
Tooltip,
|
||||||
|
Label,
|
||||||
|
Icon,
|
||||||
|
Switch,
|
||||||
|
SecureSocksProxySettings,
|
||||||
|
Collapse,
|
||||||
} from '@grafana/ui';
|
} from '@grafana/ui';
|
||||||
import { ConnectionLimits } from 'app/features/plugins/sql/components/configuration/ConnectionLimits';
|
import { ConnectionLimits } from 'app/features/plugins/sql/components/configuration/ConnectionLimits';
|
||||||
import { TLSSecretsConfig } from 'app/features/plugins/sql/components/configuration/TLSSecretsConfig';
|
import { TLSSecretsConfig } from 'app/features/plugins/sql/components/configuration/TLSSecretsConfig';
|
||||||
@ -46,9 +49,9 @@ export const postgresVersions: Array<SelectableValue<number>> = [
|
|||||||
|
|
||||||
export const PostgresConfigEditor = (props: DataSourcePluginOptionsEditorProps<PostgresOptions, SecureJsonData>) => {
|
export const PostgresConfigEditor = (props: DataSourcePluginOptionsEditorProps<PostgresOptions, SecureJsonData>) => {
|
||||||
const [versionOptions, setVersionOptions] = useState(postgresVersions);
|
const [versionOptions, setVersionOptions] = useState(postgresVersions);
|
||||||
|
const [isOpen, setIsOpen] = useState(true);
|
||||||
|
|
||||||
useAutoDetectFeatures({ props, setVersionOptions });
|
useAutoDetectFeatures({ props, setVersionOptions });
|
||||||
|
|
||||||
useMigrateDatabaseFields(props);
|
useMigrateDatabaseFields(props);
|
||||||
|
|
||||||
const { options, onOptionsChange } = props;
|
const { options, onOptionsChange } = props;
|
||||||
@ -86,229 +89,323 @@ export const PostgresConfigEditor = (props: DataSourcePluginOptionsEditorProps<P
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const labelWidthSSLDetails = 25;
|
const WIDTH_LONG = 40;
|
||||||
const labelWidthConnection = 20;
|
|
||||||
const labelWidthShort = 20;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FieldSet label="PostgreSQL Connection" width={400}>
|
<DataSourceDescription
|
||||||
<InlineField labelWidth={labelWidthConnection} label="Host">
|
dataSourceName="Postgres"
|
||||||
|
docsLink="https://grafana.com/docs/grafana/latest/datasources/postgres/"
|
||||||
|
hasRequiredFields={true}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<Collapse collapsible label="User Permissions" isOpen={isOpen} onToggle={() => setIsOpen((x) => !x)}>
|
||||||
|
The database user should only be granted SELECT permissions on the specified database & tables you want to
|
||||||
|
query. <br />
|
||||||
|
Grafana does not validate that queries are safe so queries can contain any SQL statement. For example,
|
||||||
|
statements like <code>DELETE FROM user;</code> and <code>DROP TABLE user;</code> would be executed. <br />
|
||||||
|
To protect against this we <strong>Highly</strong> recommend you create a specific PostgreSQL user with
|
||||||
|
restricted permissions. Check out the docs for more information.
|
||||||
|
</Collapse>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<ConfigSection title="Connection">
|
||||||
|
<Field label="Host URL" required>
|
||||||
<Input
|
<Input
|
||||||
width={40}
|
width={WIDTH_LONG}
|
||||||
name="host"
|
name="host"
|
||||||
type="text"
|
type="text"
|
||||||
value={options.url || ''}
|
value={options.url || ''}
|
||||||
placeholder="localhost:5432"
|
placeholder="localhost:5432"
|
||||||
onChange={onDSOptionChanged('url')}
|
onChange={onDSOptionChanged('url')}
|
||||||
></Input>
|
/>
|
||||||
</InlineField>
|
</Field>
|
||||||
<InlineField labelWidth={labelWidthConnection} label="Database">
|
|
||||||
|
<Field label="Database name" required>
|
||||||
<Input
|
<Input
|
||||||
width={40}
|
width={WIDTH_LONG}
|
||||||
name="database"
|
name="database"
|
||||||
value={jsonData.database || ''}
|
value={jsonData.database || ''}
|
||||||
placeholder="database name"
|
placeholder="Database"
|
||||||
onChange={onUpdateDatasourceJsonDataOption(props, 'database')}
|
onChange={onUpdateDatasourceJsonDataOption(props, 'database')}
|
||||||
></Input>
|
/>
|
||||||
</InlineField>
|
</Field>
|
||||||
<InlineFieldRow>
|
</ConfigSection>
|
||||||
<InlineField labelWidth={labelWidthConnection} label="User">
|
|
||||||
<Input value={options.user || ''} placeholder="user" onChange={onDSOptionChanged('user')}></Input>
|
<Divider />
|
||||||
</InlineField>
|
|
||||||
<InlineField label="Password">
|
<ConfigSection title="Authentication">
|
||||||
<SecretInput
|
<Field label="Username" required>
|
||||||
placeholder="Password"
|
<Input
|
||||||
isConfigured={options.secureJsonFields?.password}
|
width={WIDTH_LONG}
|
||||||
onReset={onResetPassword}
|
value={options.user || ''}
|
||||||
onBlur={onUpdateDatasourceSecureJsonDataOption(props, 'password')}
|
placeholder="Username"
|
||||||
></SecretInput>
|
onChange={onDSOptionChanged('user')}
|
||||||
</InlineField>
|
/>
|
||||||
</InlineFieldRow>
|
</Field>
|
||||||
<InlineField
|
|
||||||
labelWidth={labelWidthConnection}
|
<Field label="Password" required>
|
||||||
label="TLS/SSL Mode"
|
<SecretInput
|
||||||
htmlFor="tlsMode"
|
width={WIDTH_LONG}
|
||||||
tooltip="This option determines whether or with what priority a secure TLS/SSL TCP/IP connection will be negotiated with the server."
|
placeholder="Password"
|
||||||
|
isConfigured={options.secureJsonFields && options.secureJsonFields.password}
|
||||||
|
onReset={onResetPassword}
|
||||||
|
onBlur={onUpdateDatasourceSecureJsonDataOption(props, 'password')}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
label={
|
||||||
|
<Label>
|
||||||
|
<Stack gap={0.5}>
|
||||||
|
<span>TLS/SSL Mode</span>
|
||||||
|
<Tooltip
|
||||||
|
content={
|
||||||
|
<span>
|
||||||
|
This option determines whether or with what priority a secure TLS/SSL TCP/IP connection will be
|
||||||
|
negotiated with the server
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Icon name="info-circle" size="sm" />
|
||||||
|
</Tooltip>
|
||||||
|
</Stack>
|
||||||
|
</Label>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
options={tlsModes}
|
options={tlsModes}
|
||||||
inputId="tlsMode"
|
|
||||||
value={jsonData.sslmode || PostgresTLSModes.verifyFull}
|
value={jsonData.sslmode || PostgresTLSModes.verifyFull}
|
||||||
onChange={onJSONDataOptionSelected('sslmode')}
|
onChange={onJSONDataOptionSelected('sslmode')}
|
||||||
></Select>
|
width={WIDTH_LONG}
|
||||||
</InlineField>
|
/>
|
||||||
|
</Field>
|
||||||
|
|
||||||
{options.jsonData.sslmode !== PostgresTLSModes.disable ? (
|
{options.jsonData.sslmode !== PostgresTLSModes.disable ? (
|
||||||
<InlineField
|
<Field
|
||||||
labelWidth={labelWidthConnection}
|
label={
|
||||||
label="TLS/SSL Method"
|
<Label>
|
||||||
htmlFor="tlsMethod"
|
<Stack gap={0.5}>
|
||||||
tooltip={
|
<span>TLS/SSL Method</span>
|
||||||
<span>
|
<Tooltip
|
||||||
This option determines how TLS/SSL certifications are configured. Selecting <i>File system path</i> will
|
content={
|
||||||
allow you to configure certificates by specifying paths to existing certificates on the local file
|
<span>
|
||||||
system where Grafana is running. Be sure that the file is readable by the user executing the Grafana
|
This option determines how TLS/SSL certifications are configured. Selecting{' '}
|
||||||
process.
|
<i>File system path</i> will allow you to configure certificates by specifying paths to existing
|
||||||
<br />
|
certificates on the local file system where Grafana is running. Be sure that the file is
|
||||||
<br />
|
readable by the user executing the Grafana process.
|
||||||
Selecting <i>Certificate content</i> will allow you to configure certificates by specifying its content.
|
<br />
|
||||||
The content will be stored encrypted in Grafana's database. When connecting to the database the
|
<br />
|
||||||
certificates will be written as files to Grafana's configured data path on the local file system.
|
Selecting <i>Certificate content</i> will allow you to configure certificates by specifying its
|
||||||
</span>
|
content. The content will be stored encrypted in Grafana's database. When connecting to the
|
||||||
|
database the certificates will be written as files to Grafana's configured data path on the
|
||||||
|
local file system.
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Icon name="info-circle" size="sm" />
|
||||||
|
</Tooltip>
|
||||||
|
</Stack>
|
||||||
|
</Label>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
options={tlsMethods}
|
options={tlsMethods}
|
||||||
inputId="tlsMethod"
|
|
||||||
value={jsonData.tlsConfigurationMethod || PostgresTLSMethods.filePath}
|
value={jsonData.tlsConfigurationMethod || PostgresTLSMethods.filePath}
|
||||||
onChange={onJSONDataOptionSelected('tlsConfigurationMethod')}
|
onChange={onJSONDataOptionSelected('tlsConfigurationMethod')}
|
||||||
></Select>
|
width={WIDTH_LONG}
|
||||||
</InlineField>
|
|
||||||
) : null}
|
|
||||||
</FieldSet>
|
|
||||||
|
|
||||||
{config.secureSocksDSProxyEnabled && (
|
|
||||||
<FieldSet label="Secure Socks Proxy">
|
|
||||||
<InlineField labelWidth={26} label="Enabled" tooltip="Connect to this datasource via the secure socks proxy.">
|
|
||||||
<InlineSwitch
|
|
||||||
value={options.jsonData.enableSecureSocksProxy ?? false}
|
|
||||||
onChange={(event) =>
|
|
||||||
onOptionsChange({
|
|
||||||
...options,
|
|
||||||
jsonData: { ...options.jsonData, enableSecureSocksProxy: event!.currentTarget.checked },
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</InlineField>
|
</Field>
|
||||||
</FieldSet>
|
) : null}
|
||||||
)}
|
</ConfigSection>
|
||||||
|
|
||||||
{jsonData.sslmode !== PostgresTLSModes.disable ? (
|
{jsonData.sslmode !== PostgresTLSModes.disable ? (
|
||||||
<FieldSet label="TLS/SSL Auth Details">
|
<>
|
||||||
{jsonData.tlsConfigurationMethod === PostgresTLSMethods.fileContent ? (
|
<Divider />
|
||||||
<TLSSecretsConfig
|
<ConfigSection title="TLS/SSL Auth Details">
|
||||||
showCACert={
|
{jsonData.tlsConfigurationMethod === PostgresTLSMethods.fileContent ? (
|
||||||
jsonData.sslmode === PostgresTLSModes.verifyCA || jsonData.sslmode === PostgresTLSModes.verifyFull
|
<TLSSecretsConfig
|
||||||
}
|
showCACert={
|
||||||
editorProps={props}
|
jsonData.sslmode === PostgresTLSModes.verifyCA || jsonData.sslmode === PostgresTLSModes.verifyFull
|
||||||
labelWidth={labelWidthSSLDetails}
|
|
||||||
></TLSSecretsConfig>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<InlineField
|
|
||||||
tooltip={
|
|
||||||
<span>
|
|
||||||
If the selected TLS/SSL mode requires a server root certificate, provide the path to the file here.
|
|
||||||
</span>
|
|
||||||
}
|
}
|
||||||
labelWidth={labelWidthSSLDetails}
|
editorProps={props}
|
||||||
label="TLS/SSL Root Certificate"
|
labelWidth={WIDTH_LONG}
|
||||||
>
|
/>
|
||||||
<Input
|
) : (
|
||||||
value={jsonData.sslRootCertFile || ''}
|
<>
|
||||||
onChange={onUpdateDatasourceJsonDataOption(props, 'sslRootCertFile')}
|
<Field
|
||||||
placeholder="TLS/SSL root cert file"
|
label={
|
||||||
></Input>
|
<Label>
|
||||||
</InlineField>
|
<Stack gap={0.5}>
|
||||||
<InlineField
|
<span>TLS/SSL Root Certificate</span>
|
||||||
tooltip={
|
<Tooltip
|
||||||
<span>
|
content={
|
||||||
To authenticate with an TLS/SSL client certificate, provide the path to the file here. Be sure that
|
<span>
|
||||||
the file is readable by the user executing the grafana process.
|
If the selected TLS/SSL mode requires a server root certificate, provide the path to the
|
||||||
</span>
|
file here.
|
||||||
}
|
</span>
|
||||||
labelWidth={labelWidthSSLDetails}
|
}
|
||||||
label="TLS/SSL Client Certificate"
|
>
|
||||||
>
|
<Icon name="info-circle" size="sm" />
|
||||||
<Input
|
</Tooltip>
|
||||||
value={jsonData.sslCertFile || ''}
|
</Stack>
|
||||||
onChange={onUpdateDatasourceJsonDataOption(props, 'sslCertFile')}
|
</Label>
|
||||||
placeholder="TLS/SSL client cert file"
|
}
|
||||||
></Input>
|
>
|
||||||
</InlineField>
|
<Input
|
||||||
<InlineField
|
value={jsonData.sslRootCertFile || ''}
|
||||||
tooltip={
|
onChange={onUpdateDatasourceJsonDataOption(props, 'sslRootCertFile')}
|
||||||
<span>
|
placeholder="TLS/SSL root cert file"
|
||||||
To authenticate with a client TLS/SSL certificate, provide the path to the corresponding key file
|
width={WIDTH_LONG}
|
||||||
here. Be sure that the file is <i>only</i> readable by the user executing the grafana process.
|
/>
|
||||||
</span>
|
</Field>
|
||||||
}
|
<Field
|
||||||
labelWidth={labelWidthSSLDetails}
|
label={
|
||||||
label="TLS/SSL Client Key"
|
<Label>
|
||||||
>
|
<Stack gap={0.5}>
|
||||||
<Input
|
<span>TLS/SSL Client Certificate</span>
|
||||||
value={jsonData.sslKeyFile || ''}
|
<Tooltip
|
||||||
onChange={onUpdateDatasourceJsonDataOption(props, 'sslKeyFile')}
|
content={
|
||||||
placeholder="TLS/SSL client key file"
|
<span>
|
||||||
></Input>
|
To authenticate with an TLS/SSL client certificate, provide the path to the file here. Be
|
||||||
</InlineField>
|
sure that the file is readable by the user executing the grafana process.
|
||||||
</>
|
</span>
|
||||||
)}
|
}
|
||||||
</FieldSet>
|
>
|
||||||
|
<Icon name="info-circle" size="sm" />
|
||||||
|
</Tooltip>
|
||||||
|
</Stack>
|
||||||
|
</Label>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
value={jsonData.sslCertFile || ''}
|
||||||
|
onChange={onUpdateDatasourceJsonDataOption(props, 'sslCertFile')}
|
||||||
|
placeholder="TLS/SSL client cert file"
|
||||||
|
width={WIDTH_LONG}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
<Field
|
||||||
|
label={
|
||||||
|
<Label>
|
||||||
|
<Stack gap={0.5}>
|
||||||
|
<span>TLS/SSL Client Key</span>
|
||||||
|
<Tooltip
|
||||||
|
content={
|
||||||
|
<span>
|
||||||
|
To authenticate with a client TLS/SSL certificate, provide the path to the corresponding
|
||||||
|
key file here. Be sure that the file is <i>only</i> readable by the user executing the
|
||||||
|
grafana process.
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Icon name="info-circle" size="sm" />
|
||||||
|
</Tooltip>
|
||||||
|
</Stack>
|
||||||
|
</Label>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
value={jsonData.sslKeyFile || ''}
|
||||||
|
onChange={onUpdateDatasourceJsonDataOption(props, 'sslKeyFile')}
|
||||||
|
placeholder="TLS/SSL client key file"
|
||||||
|
width={WIDTH_LONG}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</ConfigSection>
|
||||||
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<ConnectionLimits options={options} onOptionsChange={onOptionsChange} />
|
<Divider />
|
||||||
|
|
||||||
<FieldSet label="PostgreSQL details">
|
<ConfigSection title="Additional settings" isCollapsible>
|
||||||
<InlineField
|
<ConfigSubSection title="PostgreSQL Options">
|
||||||
tooltip="This option controls what functions are available in the PostgreSQL query builder"
|
<Field
|
||||||
labelWidth={labelWidthShort}
|
label={
|
||||||
htmlFor="postgresVersion"
|
<Label>
|
||||||
label="Version"
|
<Stack gap={0.5}>
|
||||||
>
|
<span>Version</span>
|
||||||
<Select
|
<Tooltip
|
||||||
value={jsonData.postgresVersion || 903}
|
content={
|
||||||
inputId="postgresVersion"
|
<span>This option controls what functions are available in the PostgreSQL query builder</span>
|
||||||
onChange={onJSONDataOptionSelected('postgresVersion')}
|
}
|
||||||
options={versionOptions}
|
>
|
||||||
></Select>
|
<Icon name="info-circle" size="sm" />
|
||||||
</InlineField>
|
</Tooltip>
|
||||||
<InlineField
|
</Stack>
|
||||||
tooltip={
|
</Label>
|
||||||
<span>
|
}
|
||||||
TimescaleDB is a time-series database built as a PostgreSQL extension. If enabled, Grafana will use
|
>
|
||||||
<code>time_bucket</code> in the <code>$__timeGroup</code> macro and display TimescaleDB specific aggregate
|
<Select
|
||||||
functions in the query builder.
|
value={jsonData.postgresVersion || 903}
|
||||||
</span>
|
onChange={onJSONDataOptionSelected('postgresVersion')}
|
||||||
}
|
options={versionOptions}
|
||||||
labelWidth={labelWidthShort}
|
width={WIDTH_LONG}
|
||||||
label="TimescaleDB"
|
/>
|
||||||
htmlFor="timescaledb"
|
</Field>
|
||||||
>
|
<Field
|
||||||
<InlineSwitch
|
label={
|
||||||
id="timescaledb"
|
<Label>
|
||||||
value={jsonData.timescaledb || false}
|
<Stack gap={0.5}>
|
||||||
onChange={onTimeScaleDBChanged}
|
<span>Min time interval</span>
|
||||||
></InlineSwitch>
|
<Tooltip
|
||||||
</InlineField>
|
content={
|
||||||
<InlineField
|
<span>
|
||||||
tooltip={
|
A lower limit for the auto group by time interval. Recommended to be set to write frequency, for
|
||||||
<span>
|
example
|
||||||
A lower limit for the auto group by time interval. Recommended to be set to write frequency, for example
|
<code>1m</code> if your data is written every minute.
|
||||||
<code>1m</code> if your data is written every minute.
|
</span>
|
||||||
</span>
|
}
|
||||||
}
|
>
|
||||||
labelWidth={labelWidthShort}
|
<Icon name="info-circle" size="sm" />
|
||||||
label="Min time interval"
|
</Tooltip>
|
||||||
>
|
</Stack>
|
||||||
<Input
|
</Label>
|
||||||
placeholder="1m"
|
}
|
||||||
value={jsonData.timeInterval || ''}
|
>
|
||||||
onChange={onUpdateDatasourceJsonDataOption(props, 'timeInterval')}
|
<Input
|
||||||
></Input>
|
placeholder="1m"
|
||||||
</InlineField>
|
value={jsonData.timeInterval || ''}
|
||||||
</FieldSet>
|
onChange={onUpdateDatasourceJsonDataOption(props, 'timeInterval')}
|
||||||
|
width={WIDTH_LONG}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
<Field
|
||||||
|
label={
|
||||||
|
<Label>
|
||||||
|
<Stack gap={0.5}>
|
||||||
|
<span>TimescaleDB</span>
|
||||||
|
<Tooltip
|
||||||
|
content={
|
||||||
|
<span>
|
||||||
|
TimescaleDB is a time-series database built as a PostgreSQL extension. If enabled, Grafana will
|
||||||
|
use
|
||||||
|
<code>time_bucket</code> in the <code>$__timeGroup</code> macro and display TimescaleDB specific
|
||||||
|
aggregate functions in the query builder.
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Icon name="info-circle" size="sm" />
|
||||||
|
</Tooltip>
|
||||||
|
</Stack>
|
||||||
|
</Label>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Switch value={jsonData.timescaledb || false} onChange={onTimeScaleDBChanged} width={WIDTH_LONG} />
|
||||||
|
</Field>
|
||||||
|
</ConfigSubSection>
|
||||||
|
|
||||||
<Alert title="User Permission" severity="info">
|
<ConnectionLimits options={options} onOptionsChange={onOptionsChange} />
|
||||||
The database user should only be granted SELECT permissions on the specified database & tables you want to
|
|
||||||
query. Grafana does not validate that queries are safe so queries can contain any SQL statement. For example,
|
{config.secureSocksDSProxyEnabled && (
|
||||||
statements like <code>DELETE FROM user;</code> and <code>DROP TABLE user;</code> would be executed. To protect
|
<SecureSocksProxySettings options={options} onOptionsChange={() => onOptionsChange(options)} />
|
||||||
against this we <strong>Highly</strong> recommend you create a specific PostgreSQL user with restricted
|
)}
|
||||||
permissions. Check out the{' '}
|
</ConfigSection>
|
||||||
<Link rel="noreferrer" target="_blank" href="http://docs.grafana.org/features/datasources/postgres/">
|
|
||||||
PostgreSQL Data Source Docs
|
|
||||||
</Link>{' '}
|
|
||||||
for more information.
|
|
||||||
</Alert>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user