mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Prometheus: Azure authentication in configuration UI (#35860)
* Azure authentication settings * Persisting credentials * Azure settings * Prometheus-specific settings component * Azure Prometheus Resource ID configuration * DataSourceHttpSettings with extensibility for Azure * Feature toggle for Azure auth * Fix snapshot * Update format of persisted credentials * AzureSettings renamed to AzureAuthSettings
This commit is contained in:
parent
013218e075
commit
4664cba935
@ -56,7 +56,14 @@ const HttpAccessHelp = () => (
|
|||||||
);
|
);
|
||||||
|
|
||||||
export const DataSourceHttpSettings: React.FC<HttpSettingsProps> = (props) => {
|
export const DataSourceHttpSettings: React.FC<HttpSettingsProps> = (props) => {
|
||||||
const { defaultUrl, dataSourceConfig, onChange, showAccessOptions, sigV4AuthToggleEnabled } = props;
|
const {
|
||||||
|
defaultUrl,
|
||||||
|
dataSourceConfig,
|
||||||
|
onChange,
|
||||||
|
showAccessOptions,
|
||||||
|
sigV4AuthToggleEnabled,
|
||||||
|
azureAuthSettings,
|
||||||
|
} = props;
|
||||||
let urlTooltip;
|
let urlTooltip;
|
||||||
const [isAccessHelpVisible, setIsAccessHelpVisible] = useState(false);
|
const [isAccessHelpVisible, setIsAccessHelpVisible] = useState(false);
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
@ -207,6 +214,22 @@ export const DataSourceHttpSettings: React.FC<HttpSettingsProps> = (props) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{azureAuthSettings?.azureAuthEnabled && (
|
||||||
|
<div className="gf-form-inline">
|
||||||
|
<Switch
|
||||||
|
label="Azure Authentication"
|
||||||
|
labelClass="width-13"
|
||||||
|
checked={dataSourceConfig.jsonData.azureAuth || false}
|
||||||
|
onChange={(event) => {
|
||||||
|
onSettingsChange({
|
||||||
|
jsonData: { ...dataSourceConfig.jsonData, azureAuth: event!.currentTarget.checked },
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
tooltip="Use Azure authentication for Azure endpoint."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{sigV4AuthToggleEnabled && (
|
{sigV4AuthToggleEnabled && (
|
||||||
<div className="gf-form-inline">
|
<div className="gf-form-inline">
|
||||||
<Switch
|
<Switch
|
||||||
@ -238,6 +261,12 @@ export const DataSourceHttpSettings: React.FC<HttpSettingsProps> = (props) => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{azureAuthSettings?.azureAuthEnabled &&
|
||||||
|
azureAuthSettings?.azureSettingsUI &&
|
||||||
|
dataSourceConfig.jsonData.azureAuth && (
|
||||||
|
<azureAuthSettings.azureSettingsUI dataSourceConfig={dataSourceConfig} onChange={onChange} />
|
||||||
|
)}
|
||||||
|
|
||||||
{dataSourceConfig.jsonData.sigV4Auth && <SigV4AuthSettings {...props} />}
|
{dataSourceConfig.jsonData.sigV4Auth && <SigV4AuthSettings {...props} />}
|
||||||
|
|
||||||
{(dataSourceConfig.jsonData.tlsAuth || dataSourceConfig.jsonData.tlsAuthWithCACert) && (
|
{(dataSourceConfig.jsonData.tlsAuth || dataSourceConfig.jsonData.tlsAuthWithCACert) && (
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
|
import React from 'react';
|
||||||
import { DataSourceSettings } from '@grafana/data';
|
import { DataSourceSettings } from '@grafana/data';
|
||||||
|
|
||||||
|
export interface AzureAuthSettings {
|
||||||
|
azureAuthEnabled: boolean;
|
||||||
|
azureSettingsUI?: React.ComponentType<HttpSettingsBaseProps>;
|
||||||
|
}
|
||||||
|
|
||||||
export interface HttpSettingsBaseProps {
|
export interface HttpSettingsBaseProps {
|
||||||
/** The configuration object of the data source */
|
/** The configuration object of the data source */
|
||||||
dataSourceConfig: DataSourceSettings<any, any>;
|
dataSourceConfig: DataSourceSettings<any, any>;
|
||||||
@ -14,4 +20,6 @@ export interface HttpSettingsProps extends HttpSettingsBaseProps {
|
|||||||
showAccessOptions?: boolean;
|
showAccessOptions?: boolean;
|
||||||
/** Show the SigV4 auth toggle option */
|
/** Show the SigV4 auth toggle option */
|
||||||
sigV4AuthToggleEnabled?: boolean;
|
sigV4AuthToggleEnabled?: boolean;
|
||||||
|
/** Azure authentication settings **/
|
||||||
|
azureAuthSettings?: AzureAuthSettings;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,51 @@
|
|||||||
|
import React, { FunctionComponent, useMemo } from 'react';
|
||||||
|
import { InlineFormLabel, Input } from '@grafana/ui';
|
||||||
|
import { config } from '@grafana/runtime';
|
||||||
|
import { KnownAzureClouds, AzureCredentials } from './AzureCredentials';
|
||||||
|
import { getCredentials, updateCredentials } from './AzureCredentialsConfig';
|
||||||
|
import { AzureCredentialsForm } from './AzureCredentialsForm';
|
||||||
|
import { HttpSettingsBaseProps } from '@grafana/ui/src/components/DataSourceSettings/types';
|
||||||
|
|
||||||
|
export const AzureAuthSettings: FunctionComponent<HttpSettingsBaseProps> = (props: HttpSettingsBaseProps) => {
|
||||||
|
const { dataSourceConfig, onChange } = props;
|
||||||
|
|
||||||
|
const credentials = useMemo(() => getCredentials(dataSourceConfig), [dataSourceConfig]);
|
||||||
|
|
||||||
|
const onCredentialsChange = (credentials: AzureCredentials): void => {
|
||||||
|
onChange(updateCredentials(dataSourceConfig, credentials));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h6>Azure Authentication</h6>
|
||||||
|
<AzureCredentialsForm
|
||||||
|
managedIdentityEnabled={config.azure.managedIdentityEnabled}
|
||||||
|
credentials={credentials}
|
||||||
|
azureCloudOptions={KnownAzureClouds}
|
||||||
|
onCredentialsChange={onCredentialsChange}
|
||||||
|
/>
|
||||||
|
<h6>Azure Configuration</h6>
|
||||||
|
<div className="gf-form-group">
|
||||||
|
<div className="gf-form-inline">
|
||||||
|
<div className="gf-form">
|
||||||
|
<InlineFormLabel className="width-12">AAD resource ID</InlineFormLabel>
|
||||||
|
<div className="width-15">
|
||||||
|
<Input
|
||||||
|
className="width-30"
|
||||||
|
value={dataSourceConfig.jsonData.azureEndpointResourceId || ''}
|
||||||
|
onChange={(event) =>
|
||||||
|
onChange({
|
||||||
|
...dataSourceConfig,
|
||||||
|
jsonData: { ...dataSourceConfig.jsonData, azureEndpointResourceId: event.currentTarget.value },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AzureAuthSettings;
|
@ -0,0 +1,48 @@
|
|||||||
|
import { SelectableValue } from '@grafana/data';
|
||||||
|
|
||||||
|
export enum AzureCloud {
|
||||||
|
Public = 'AzureCloud',
|
||||||
|
China = 'AzureChinaCloud',
|
||||||
|
USGovernment = 'AzureUSGovernment',
|
||||||
|
Germany = 'AzureGermanCloud',
|
||||||
|
None = '',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const KnownAzureClouds = [
|
||||||
|
{ value: AzureCloud.Public, label: 'Azure' },
|
||||||
|
{ value: AzureCloud.China, label: 'Azure China' },
|
||||||
|
{ value: AzureCloud.USGovernment, label: 'Azure US Government' },
|
||||||
|
{ value: AzureCloud.Germany, label: 'Azure Germany' },
|
||||||
|
] as SelectableValue[];
|
||||||
|
|
||||||
|
export type AzureAuthType = 'msi' | 'clientsecret';
|
||||||
|
|
||||||
|
export type ConcealedSecret = symbol;
|
||||||
|
|
||||||
|
interface AzureCredentialsBase {
|
||||||
|
authType: AzureAuthType;
|
||||||
|
defaultSubscriptionId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AzureManagedIdentityCredentials extends AzureCredentialsBase {
|
||||||
|
authType: 'msi';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AzureClientSecretCredentials extends AzureCredentialsBase {
|
||||||
|
authType: 'clientsecret';
|
||||||
|
azureCloud?: string;
|
||||||
|
tenantId?: string;
|
||||||
|
clientId?: string;
|
||||||
|
clientSecret?: string | ConcealedSecret;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AzureCredentials = AzureManagedIdentityCredentials | AzureClientSecretCredentials;
|
||||||
|
|
||||||
|
export function isCredentialsComplete(credentials: AzureCredentials): boolean {
|
||||||
|
switch (credentials.authType) {
|
||||||
|
case 'msi':
|
||||||
|
return true;
|
||||||
|
case 'clientsecret':
|
||||||
|
return !!(credentials.azureCloud && credentials.tenantId && credentials.clientId && credentials.clientSecret);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,107 @@
|
|||||||
|
import { DataSourceSettings } from '@grafana/data';
|
||||||
|
import { config } from '@grafana/runtime';
|
||||||
|
import { AzureCloud, AzureCredentials, ConcealedSecret } from './AzureCredentials';
|
||||||
|
|
||||||
|
const concealed: ConcealedSecret = Symbol('Concealed client secret');
|
||||||
|
|
||||||
|
function getDefaultAzureCloud(): string {
|
||||||
|
return config.azure.cloud || AzureCloud.Public;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSecret(options: DataSourceSettings<any, any>): undefined | string | ConcealedSecret {
|
||||||
|
if (options.secureJsonFields.azureClientSecret) {
|
||||||
|
// The secret is concealed on server
|
||||||
|
return concealed;
|
||||||
|
} else {
|
||||||
|
const secret = options.secureJsonData?.azureClientSecret;
|
||||||
|
return typeof secret === 'string' && secret.length > 0 ? secret : undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCredentials(options: DataSourceSettings<any, any>): AzureCredentials {
|
||||||
|
const credentials = options.jsonData.azureCredentials as AzureCredentials | undefined;
|
||||||
|
|
||||||
|
// If no credentials saved, then return empty credentials
|
||||||
|
// of type based on whether the managed identity enabled
|
||||||
|
if (!credentials) {
|
||||||
|
return {
|
||||||
|
authType: config.azure.managedIdentityEnabled ? 'msi' : 'clientsecret',
|
||||||
|
azureCloud: getDefaultAzureCloud(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (credentials.authType) {
|
||||||
|
case 'msi':
|
||||||
|
if (config.azure.managedIdentityEnabled) {
|
||||||
|
return {
|
||||||
|
authType: 'msi',
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// If authentication type is managed identity but managed identities were disabled in Grafana config,
|
||||||
|
// then we should fallback to an empty app registration (client secret) configuration
|
||||||
|
return {
|
||||||
|
authType: 'clientsecret',
|
||||||
|
azureCloud: getDefaultAzureCloud(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case 'clientsecret':
|
||||||
|
return {
|
||||||
|
authType: 'clientsecret',
|
||||||
|
azureCloud: credentials.azureCloud || getDefaultAzureCloud(),
|
||||||
|
tenantId: credentials.tenantId,
|
||||||
|
clientId: credentials.clientId,
|
||||||
|
clientSecret: getSecret(options),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateCredentials(
|
||||||
|
options: DataSourceSettings<any, any>,
|
||||||
|
credentials: AzureCredentials
|
||||||
|
): DataSourceSettings<any, any> {
|
||||||
|
switch (credentials.authType) {
|
||||||
|
case 'msi':
|
||||||
|
if (!config.azure.managedIdentityEnabled) {
|
||||||
|
throw new Error('Managed Identity authentication is not enabled in Grafana config.');
|
||||||
|
}
|
||||||
|
|
||||||
|
options = {
|
||||||
|
...options,
|
||||||
|
jsonData: {
|
||||||
|
...options.jsonData,
|
||||||
|
azureCredentials: {
|
||||||
|
authType: 'msi',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return options;
|
||||||
|
|
||||||
|
case 'clientsecret':
|
||||||
|
options = {
|
||||||
|
...options,
|
||||||
|
jsonData: {
|
||||||
|
...options.jsonData,
|
||||||
|
azureCredentials: {
|
||||||
|
authType: 'clientsecret',
|
||||||
|
azureCloud: credentials.azureCloud || getDefaultAzureCloud(),
|
||||||
|
tenantId: credentials.tenantId,
|
||||||
|
clientId: credentials.clientId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
secureJsonData: {
|
||||||
|
...options.secureJsonData,
|
||||||
|
azureClientSecret:
|
||||||
|
typeof credentials.clientSecret === 'string' && credentials.clientSecret.length > 0
|
||||||
|
? credentials.clientSecret
|
||||||
|
: undefined,
|
||||||
|
},
|
||||||
|
secureJsonFields: {
|
||||||
|
...options.secureJsonFields,
|
||||||
|
azureClientSecret: typeof credentials.clientSecret === 'symbol',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { shallow } from 'enzyme';
|
||||||
|
import AzureCredentialsForm, { Props } from './AzureCredentialsForm';
|
||||||
|
|
||||||
|
const setup = (propsFunc?: (props: Props) => Props) => {
|
||||||
|
let props: Props = {
|
||||||
|
managedIdentityEnabled: false,
|
||||||
|
credentials: {
|
||||||
|
authType: 'clientsecret',
|
||||||
|
azureCloud: 'azuremonitor',
|
||||||
|
tenantId: 'e7f3f661-a933-3h3f-0294-31c4f962ec48',
|
||||||
|
clientId: '34509fad-c0r9-45df-9e25-f1ee34af6900',
|
||||||
|
clientSecret: undefined,
|
||||||
|
defaultSubscriptionId: '44987801-6nn6-49he-9b2d-9106972f9789',
|
||||||
|
},
|
||||||
|
azureCloudOptions: [
|
||||||
|
{ value: 'azuremonitor', label: 'Azure' },
|
||||||
|
{ value: 'govazuremonitor', label: 'Azure US Government' },
|
||||||
|
{ value: 'germanyazuremonitor', label: 'Azure Germany' },
|
||||||
|
{ value: 'chinaazuremonitor', label: 'Azure China' },
|
||||||
|
],
|
||||||
|
onCredentialsChange: jest.fn(),
|
||||||
|
getSubscriptions: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (propsFunc) {
|
||||||
|
props = propsFunc(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
return shallow(<AzureCredentialsForm {...props} />);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Render', () => {
|
||||||
|
it('should render component', () => {
|
||||||
|
const wrapper = setup();
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should disable azure monitor secret input', () => {
|
||||||
|
const wrapper = setup((props) => ({
|
||||||
|
...props,
|
||||||
|
credentials: {
|
||||||
|
authType: 'clientsecret',
|
||||||
|
azureCloud: 'azuremonitor',
|
||||||
|
tenantId: 'e7f3f661-a933-3h3f-0294-31c4f962ec48',
|
||||||
|
clientId: '34509fad-c0r9-45df-9e25-f1ee34af6900',
|
||||||
|
clientSecret: Symbol(),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should enable azure monitor load subscriptions button', () => {
|
||||||
|
const wrapper = setup((props) => ({
|
||||||
|
...props,
|
||||||
|
credentials: {
|
||||||
|
authType: 'clientsecret',
|
||||||
|
azureCloud: 'azuremonitor',
|
||||||
|
tenantId: 'e7f3f661-a933-3h3f-0294-31c4f962ec48',
|
||||||
|
clientId: '34509fad-c0r9-45df-9e25-f1ee34af6900',
|
||||||
|
clientSecret: 'e7f3f661-a933-4b3f-8176-51c4f982ec48',
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,279 @@
|
|||||||
|
import React, { ChangeEvent, FunctionComponent, useEffect, useReducer, useState } from 'react';
|
||||||
|
import { SelectableValue } from '@grafana/data';
|
||||||
|
import { InlineFormLabel, Button } from '@grafana/ui/src/components';
|
||||||
|
import { Select } from '@grafana/ui/src/components/Forms/Legacy/Select/Select';
|
||||||
|
import { Input } from '@grafana/ui/src/components/Forms/Legacy/Input/Input';
|
||||||
|
import { AzureAuthType, AzureCredentials, isCredentialsComplete } from './AzureCredentials';
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
managedIdentityEnabled: boolean;
|
||||||
|
credentials: AzureCredentials;
|
||||||
|
azureCloudOptions?: SelectableValue[];
|
||||||
|
onCredentialsChange: (updatedCredentials: AzureCredentials) => void;
|
||||||
|
getSubscriptions?: () => Promise<SelectableValue[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const authTypeOptions: Array<SelectableValue<AzureAuthType>> = [
|
||||||
|
{
|
||||||
|
value: 'msi',
|
||||||
|
label: 'Managed Identity',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'clientsecret',
|
||||||
|
label: 'App Registration',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const AzureCredentialsForm: FunctionComponent<Props> = (props: Props) => {
|
||||||
|
const { credentials, azureCloudOptions, onCredentialsChange, getSubscriptions } = props;
|
||||||
|
const hasRequiredFields = isCredentialsComplete(credentials);
|
||||||
|
|
||||||
|
const [subscriptions, setSubscriptions] = useState<Array<SelectableValue<string>>>([]);
|
||||||
|
const [loadSubscriptionsClicked, onLoadSubscriptions] = useReducer((val) => val + 1, 0);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!getSubscriptions || !hasRequiredFields) {
|
||||||
|
updateSubscriptions([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let canceled = false;
|
||||||
|
getSubscriptions().then((result) => {
|
||||||
|
if (!canceled) {
|
||||||
|
updateSubscriptions(result, loadSubscriptionsClicked);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
canceled = true;
|
||||||
|
};
|
||||||
|
// This effect is intended to be called only once initially and on Load Subscriptions click
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [loadSubscriptionsClicked]);
|
||||||
|
|
||||||
|
const updateSubscriptions = (received: Array<SelectableValue<string>>, autoSelect = false) => {
|
||||||
|
setSubscriptions(received);
|
||||||
|
if (getSubscriptions) {
|
||||||
|
if (autoSelect && !credentials.defaultSubscriptionId && received.length > 0) {
|
||||||
|
// Selecting the default subscription if subscriptions received but no default subscription selected
|
||||||
|
onSubscriptionChange(received[0]);
|
||||||
|
} else if (credentials.defaultSubscriptionId) {
|
||||||
|
const found = received.find((opt) => opt.value === credentials.defaultSubscriptionId);
|
||||||
|
if (!found) {
|
||||||
|
// Unselecting the default subscription if it isn't found among the received subscriptions
|
||||||
|
onSubscriptionChange(undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onAuthTypeChange = (selected: SelectableValue<AzureAuthType>) => {
|
||||||
|
if (onCredentialsChange) {
|
||||||
|
setSubscriptions([]);
|
||||||
|
const updated: AzureCredentials = {
|
||||||
|
...credentials,
|
||||||
|
authType: selected.value || 'msi',
|
||||||
|
defaultSubscriptionId: undefined,
|
||||||
|
};
|
||||||
|
onCredentialsChange(updated);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onAzureCloudChange = (selected: SelectableValue<string>) => {
|
||||||
|
if (onCredentialsChange && credentials.authType === 'clientsecret') {
|
||||||
|
setSubscriptions([]);
|
||||||
|
const updated: AzureCredentials = {
|
||||||
|
...credentials,
|
||||||
|
azureCloud: selected.value,
|
||||||
|
defaultSubscriptionId: undefined,
|
||||||
|
};
|
||||||
|
onCredentialsChange(updated);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onTenantIdChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
if (onCredentialsChange && credentials.authType === 'clientsecret') {
|
||||||
|
setSubscriptions([]);
|
||||||
|
const updated: AzureCredentials = {
|
||||||
|
...credentials,
|
||||||
|
tenantId: event.target.value,
|
||||||
|
defaultSubscriptionId: undefined,
|
||||||
|
};
|
||||||
|
onCredentialsChange(updated);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClientIdChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
if (onCredentialsChange && credentials.authType === 'clientsecret') {
|
||||||
|
setSubscriptions([]);
|
||||||
|
const updated: AzureCredentials = {
|
||||||
|
...credentials,
|
||||||
|
clientId: event.target.value,
|
||||||
|
defaultSubscriptionId: undefined,
|
||||||
|
};
|
||||||
|
onCredentialsChange(updated);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClientSecretChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
if (onCredentialsChange && credentials.authType === 'clientsecret') {
|
||||||
|
setSubscriptions([]);
|
||||||
|
const updated: AzureCredentials = {
|
||||||
|
...credentials,
|
||||||
|
clientSecret: event.target.value,
|
||||||
|
defaultSubscriptionId: undefined,
|
||||||
|
};
|
||||||
|
onCredentialsChange(updated);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClientSecretReset = () => {
|
||||||
|
if (onCredentialsChange && credentials.authType === 'clientsecret') {
|
||||||
|
setSubscriptions([]);
|
||||||
|
const updated: AzureCredentials = {
|
||||||
|
...credentials,
|
||||||
|
clientSecret: '',
|
||||||
|
defaultSubscriptionId: undefined,
|
||||||
|
};
|
||||||
|
onCredentialsChange(updated);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSubscriptionChange = (selected: SelectableValue<string> | undefined) => {
|
||||||
|
if (onCredentialsChange) {
|
||||||
|
const updated: AzureCredentials = {
|
||||||
|
...credentials,
|
||||||
|
defaultSubscriptionId: selected?.value,
|
||||||
|
};
|
||||||
|
onCredentialsChange(updated);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="gf-form-group">
|
||||||
|
{props.managedIdentityEnabled && (
|
||||||
|
<div className="gf-form-inline">
|
||||||
|
<div className="gf-form">
|
||||||
|
<InlineFormLabel className="width-12" tooltip="Choose the type of authentication to Azure services">
|
||||||
|
Authentication
|
||||||
|
</InlineFormLabel>
|
||||||
|
<Select
|
||||||
|
className="width-15"
|
||||||
|
value={authTypeOptions.find((opt) => opt.value === credentials.authType)}
|
||||||
|
options={authTypeOptions}
|
||||||
|
onChange={onAuthTypeChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{credentials.authType === 'clientsecret' && (
|
||||||
|
<>
|
||||||
|
{azureCloudOptions && (
|
||||||
|
<div className="gf-form-inline">
|
||||||
|
<div className="gf-form">
|
||||||
|
<InlineFormLabel className="width-12" tooltip="Choose an Azure Cloud">
|
||||||
|
Azure Cloud
|
||||||
|
</InlineFormLabel>
|
||||||
|
<Select
|
||||||
|
className="width-15"
|
||||||
|
value={azureCloudOptions.find((opt) => opt.value === credentials.azureCloud)}
|
||||||
|
options={azureCloudOptions}
|
||||||
|
onChange={onAzureCloudChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="gf-form-inline">
|
||||||
|
<div className="gf-form">
|
||||||
|
<InlineFormLabel className="width-12">Directory (tenant) ID</InlineFormLabel>
|
||||||
|
<div className="width-15">
|
||||||
|
<Input
|
||||||
|
className="width-30"
|
||||||
|
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||||
|
value={credentials.tenantId || ''}
|
||||||
|
onChange={onTenantIdChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="gf-form-inline">
|
||||||
|
<div className="gf-form">
|
||||||
|
<InlineFormLabel className="width-12">Application (client) ID</InlineFormLabel>
|
||||||
|
<div className="width-15">
|
||||||
|
<Input
|
||||||
|
className="width-30"
|
||||||
|
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||||
|
value={credentials.clientId || ''}
|
||||||
|
onChange={onClientIdChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{typeof credentials.clientSecret === 'symbol' ? (
|
||||||
|
<div className="gf-form-inline">
|
||||||
|
<div className="gf-form">
|
||||||
|
<InlineFormLabel className="width-12">Client Secret</InlineFormLabel>
|
||||||
|
<Input className="width-25" placeholder="configured" disabled={true} />
|
||||||
|
</div>
|
||||||
|
<div className="gf-form">
|
||||||
|
<div className="max-width-30 gf-form-inline">
|
||||||
|
<Button variant="secondary" type="button" onClick={onClientSecretReset}>
|
||||||
|
reset
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="gf-form-inline">
|
||||||
|
<div className="gf-form">
|
||||||
|
<InlineFormLabel className="width-12">Client Secret</InlineFormLabel>
|
||||||
|
<div className="width-15">
|
||||||
|
<Input
|
||||||
|
className="width-30"
|
||||||
|
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||||
|
value={credentials.clientSecret || ''}
|
||||||
|
onChange={onClientSecretChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{getSubscriptions && (
|
||||||
|
<>
|
||||||
|
<div className="gf-form-inline">
|
||||||
|
<div className="gf-form">
|
||||||
|
<InlineFormLabel className="width-12">Default Subscription</InlineFormLabel>
|
||||||
|
<div className="width-25">
|
||||||
|
<Select
|
||||||
|
value={
|
||||||
|
credentials.defaultSubscriptionId
|
||||||
|
? subscriptions.find((opt) => opt.value === credentials.defaultSubscriptionId)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
options={subscriptions}
|
||||||
|
onChange={onSubscriptionChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="gf-form-inline">
|
||||||
|
<div className="gf-form">
|
||||||
|
<div className="max-width-30 gf-form-inline">
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
size="sm"
|
||||||
|
type="button"
|
||||||
|
onClick={onLoadSubscriptions}
|
||||||
|
disabled={!hasRequiredFields}
|
||||||
|
>
|
||||||
|
Load Subscriptions
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AzureCredentialsForm;
|
@ -1,13 +1,20 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { AlertingSettings, DataSourceHttpSettings } from '@grafana/ui';
|
import { AlertingSettings, DataSourceHttpSettings } from '@grafana/ui';
|
||||||
import { DataSourcePluginOptionsEditorProps } from '@grafana/data';
|
import { DataSourcePluginOptionsEditorProps } from '@grafana/data';
|
||||||
import { PromSettings } from './PromSettings';
|
|
||||||
import { PromOptions } from '../types';
|
|
||||||
import { config } from 'app/core/config';
|
import { config } from 'app/core/config';
|
||||||
|
import { PromOptions } from '../types';
|
||||||
|
import { AzureAuthSettings } from './AzureAuthSettings';
|
||||||
|
import { PromSettings } from './PromSettings';
|
||||||
|
|
||||||
export type Props = DataSourcePluginOptionsEditorProps<PromOptions>;
|
export type Props = DataSourcePluginOptionsEditorProps<PromOptions>;
|
||||||
export const ConfigEditor = (props: Props) => {
|
export const ConfigEditor = (props: Props) => {
|
||||||
const { options, onOptionsChange } = props;
|
const { options, onOptionsChange } = props;
|
||||||
|
|
||||||
|
const azureAuthSettings = {
|
||||||
|
azureAuthEnabled: config.featureToggles['prometheus_azure_auth'] ?? false,
|
||||||
|
azureSettingsUI: AzureAuthSettings,
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DataSourceHttpSettings
|
<DataSourceHttpSettings
|
||||||
@ -16,6 +23,7 @@ export const ConfigEditor = (props: Props) => {
|
|||||||
showAccessOptions={true}
|
showAccessOptions={true}
|
||||||
onChange={onOptionsChange}
|
onChange={onOptionsChange}
|
||||||
sigV4AuthToggleEnabled={config.sigV4AuthEnabled}
|
sigV4AuthToggleEnabled={config.sigV4AuthEnabled}
|
||||||
|
azureAuthSettings={azureAuthSettings}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<AlertingSettings<PromOptions> options={options} onOptionsChange={onOptionsChange} />
|
<AlertingSettings<PromOptions> options={options} onOptionsChange={onOptionsChange} />
|
||||||
|
@ -0,0 +1,620 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Render should disable azure monitor secret input 1`] = `
|
||||||
|
<div
|
||||||
|
className="gf-form-group"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="gf-form-inline"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="gf-form"
|
||||||
|
>
|
||||||
|
<FormLabel
|
||||||
|
className="width-12"
|
||||||
|
tooltip="Choose an Azure Cloud"
|
||||||
|
>
|
||||||
|
Azure Cloud
|
||||||
|
</FormLabel>
|
||||||
|
<Select
|
||||||
|
allowCustomValue={false}
|
||||||
|
autoFocus={false}
|
||||||
|
backspaceRemovesValue={true}
|
||||||
|
className="width-15"
|
||||||
|
components={
|
||||||
|
Object {
|
||||||
|
"Group": [Function],
|
||||||
|
"IndicatorsContainer": [Function],
|
||||||
|
"MenuList": [Function],
|
||||||
|
"Option": [Function],
|
||||||
|
"SingleValue": [Function],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isClearable={false}
|
||||||
|
isDisabled={false}
|
||||||
|
isLoading={false}
|
||||||
|
isMulti={false}
|
||||||
|
isSearchable={true}
|
||||||
|
maxMenuHeight={300}
|
||||||
|
onChange={[Function]}
|
||||||
|
openMenuOnFocus={false}
|
||||||
|
options={
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"label": "Azure",
|
||||||
|
"value": "azuremonitor",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"label": "Azure US Government",
|
||||||
|
"value": "govazuremonitor",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"label": "Azure Germany",
|
||||||
|
"value": "germanyazuremonitor",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"label": "Azure China",
|
||||||
|
"value": "chinaazuremonitor",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
tabSelectsValue={true}
|
||||||
|
value={
|
||||||
|
Object {
|
||||||
|
"label": "Azure",
|
||||||
|
"value": "azuremonitor",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="gf-form-inline"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="gf-form"
|
||||||
|
>
|
||||||
|
<FormLabel
|
||||||
|
className="width-12"
|
||||||
|
>
|
||||||
|
Directory (tenant) ID
|
||||||
|
</FormLabel>
|
||||||
|
<div
|
||||||
|
className="width-15"
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
className="width-30"
|
||||||
|
onChange={[Function]}
|
||||||
|
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||||
|
value="e7f3f661-a933-3h3f-0294-31c4f962ec48"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="gf-form-inline"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="gf-form"
|
||||||
|
>
|
||||||
|
<FormLabel
|
||||||
|
className="width-12"
|
||||||
|
>
|
||||||
|
Application (client) ID
|
||||||
|
</FormLabel>
|
||||||
|
<div
|
||||||
|
className="width-15"
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
className="width-30"
|
||||||
|
onChange={[Function]}
|
||||||
|
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||||
|
value="34509fad-c0r9-45df-9e25-f1ee34af6900"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="gf-form-inline"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="gf-form"
|
||||||
|
>
|
||||||
|
<FormLabel
|
||||||
|
className="width-12"
|
||||||
|
>
|
||||||
|
Client Secret
|
||||||
|
</FormLabel>
|
||||||
|
<Input
|
||||||
|
className="width-25"
|
||||||
|
disabled={true}
|
||||||
|
placeholder="configured"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="gf-form"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="max-width-30 gf-form-inline"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
onClick={[Function]}
|
||||||
|
type="button"
|
||||||
|
variant="secondary"
|
||||||
|
>
|
||||||
|
reset
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="gf-form-inline"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="gf-form"
|
||||||
|
>
|
||||||
|
<FormLabel
|
||||||
|
className="width-12"
|
||||||
|
>
|
||||||
|
Default Subscription
|
||||||
|
</FormLabel>
|
||||||
|
<div
|
||||||
|
className="width-25"
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
allowCustomValue={false}
|
||||||
|
autoFocus={false}
|
||||||
|
backspaceRemovesValue={true}
|
||||||
|
className=""
|
||||||
|
components={
|
||||||
|
Object {
|
||||||
|
"Group": [Function],
|
||||||
|
"IndicatorsContainer": [Function],
|
||||||
|
"MenuList": [Function],
|
||||||
|
"Option": [Function],
|
||||||
|
"SingleValue": [Function],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isClearable={false}
|
||||||
|
isDisabled={false}
|
||||||
|
isLoading={false}
|
||||||
|
isMulti={false}
|
||||||
|
isSearchable={true}
|
||||||
|
maxMenuHeight={300}
|
||||||
|
onChange={[Function]}
|
||||||
|
openMenuOnFocus={false}
|
||||||
|
options={Array []}
|
||||||
|
tabSelectsValue={true}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="gf-form-inline"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="gf-form"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="max-width-30 gf-form-inline"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
disabled={false}
|
||||||
|
onClick={[Function]}
|
||||||
|
size="sm"
|
||||||
|
type="button"
|
||||||
|
variant="secondary"
|
||||||
|
>
|
||||||
|
Load Subscriptions
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Render should enable azure monitor load subscriptions button 1`] = `
|
||||||
|
<div
|
||||||
|
className="gf-form-group"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="gf-form-inline"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="gf-form"
|
||||||
|
>
|
||||||
|
<FormLabel
|
||||||
|
className="width-12"
|
||||||
|
tooltip="Choose an Azure Cloud"
|
||||||
|
>
|
||||||
|
Azure Cloud
|
||||||
|
</FormLabel>
|
||||||
|
<Select
|
||||||
|
allowCustomValue={false}
|
||||||
|
autoFocus={false}
|
||||||
|
backspaceRemovesValue={true}
|
||||||
|
className="width-15"
|
||||||
|
components={
|
||||||
|
Object {
|
||||||
|
"Group": [Function],
|
||||||
|
"IndicatorsContainer": [Function],
|
||||||
|
"MenuList": [Function],
|
||||||
|
"Option": [Function],
|
||||||
|
"SingleValue": [Function],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isClearable={false}
|
||||||
|
isDisabled={false}
|
||||||
|
isLoading={false}
|
||||||
|
isMulti={false}
|
||||||
|
isSearchable={true}
|
||||||
|
maxMenuHeight={300}
|
||||||
|
onChange={[Function]}
|
||||||
|
openMenuOnFocus={false}
|
||||||
|
options={
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"label": "Azure",
|
||||||
|
"value": "azuremonitor",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"label": "Azure US Government",
|
||||||
|
"value": "govazuremonitor",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"label": "Azure Germany",
|
||||||
|
"value": "germanyazuremonitor",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"label": "Azure China",
|
||||||
|
"value": "chinaazuremonitor",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
tabSelectsValue={true}
|
||||||
|
value={
|
||||||
|
Object {
|
||||||
|
"label": "Azure",
|
||||||
|
"value": "azuremonitor",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="gf-form-inline"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="gf-form"
|
||||||
|
>
|
||||||
|
<FormLabel
|
||||||
|
className="width-12"
|
||||||
|
>
|
||||||
|
Directory (tenant) ID
|
||||||
|
</FormLabel>
|
||||||
|
<div
|
||||||
|
className="width-15"
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
className="width-30"
|
||||||
|
onChange={[Function]}
|
||||||
|
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||||
|
value="e7f3f661-a933-3h3f-0294-31c4f962ec48"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="gf-form-inline"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="gf-form"
|
||||||
|
>
|
||||||
|
<FormLabel
|
||||||
|
className="width-12"
|
||||||
|
>
|
||||||
|
Application (client) ID
|
||||||
|
</FormLabel>
|
||||||
|
<div
|
||||||
|
className="width-15"
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
className="width-30"
|
||||||
|
onChange={[Function]}
|
||||||
|
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||||
|
value="34509fad-c0r9-45df-9e25-f1ee34af6900"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="gf-form-inline"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="gf-form"
|
||||||
|
>
|
||||||
|
<FormLabel
|
||||||
|
className="width-12"
|
||||||
|
>
|
||||||
|
Client Secret
|
||||||
|
</FormLabel>
|
||||||
|
<div
|
||||||
|
className="width-15"
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
className="width-30"
|
||||||
|
onChange={[Function]}
|
||||||
|
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||||
|
value="e7f3f661-a933-4b3f-8176-51c4f982ec48"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="gf-form-inline"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="gf-form"
|
||||||
|
>
|
||||||
|
<FormLabel
|
||||||
|
className="width-12"
|
||||||
|
>
|
||||||
|
Default Subscription
|
||||||
|
</FormLabel>
|
||||||
|
<div
|
||||||
|
className="width-25"
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
allowCustomValue={false}
|
||||||
|
autoFocus={false}
|
||||||
|
backspaceRemovesValue={true}
|
||||||
|
className=""
|
||||||
|
components={
|
||||||
|
Object {
|
||||||
|
"Group": [Function],
|
||||||
|
"IndicatorsContainer": [Function],
|
||||||
|
"MenuList": [Function],
|
||||||
|
"Option": [Function],
|
||||||
|
"SingleValue": [Function],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isClearable={false}
|
||||||
|
isDisabled={false}
|
||||||
|
isLoading={false}
|
||||||
|
isMulti={false}
|
||||||
|
isSearchable={true}
|
||||||
|
maxMenuHeight={300}
|
||||||
|
onChange={[Function]}
|
||||||
|
openMenuOnFocus={false}
|
||||||
|
options={Array []}
|
||||||
|
tabSelectsValue={true}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="gf-form-inline"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="gf-form"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="max-width-30 gf-form-inline"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
disabled={false}
|
||||||
|
onClick={[Function]}
|
||||||
|
size="sm"
|
||||||
|
type="button"
|
||||||
|
variant="secondary"
|
||||||
|
>
|
||||||
|
Load Subscriptions
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Render should render component 1`] = `
|
||||||
|
<div
|
||||||
|
className="gf-form-group"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="gf-form-inline"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="gf-form"
|
||||||
|
>
|
||||||
|
<FormLabel
|
||||||
|
className="width-12"
|
||||||
|
tooltip="Choose an Azure Cloud"
|
||||||
|
>
|
||||||
|
Azure Cloud
|
||||||
|
</FormLabel>
|
||||||
|
<Select
|
||||||
|
allowCustomValue={false}
|
||||||
|
autoFocus={false}
|
||||||
|
backspaceRemovesValue={true}
|
||||||
|
className="width-15"
|
||||||
|
components={
|
||||||
|
Object {
|
||||||
|
"Group": [Function],
|
||||||
|
"IndicatorsContainer": [Function],
|
||||||
|
"MenuList": [Function],
|
||||||
|
"Option": [Function],
|
||||||
|
"SingleValue": [Function],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isClearable={false}
|
||||||
|
isDisabled={false}
|
||||||
|
isLoading={false}
|
||||||
|
isMulti={false}
|
||||||
|
isSearchable={true}
|
||||||
|
maxMenuHeight={300}
|
||||||
|
onChange={[Function]}
|
||||||
|
openMenuOnFocus={false}
|
||||||
|
options={
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"label": "Azure",
|
||||||
|
"value": "azuremonitor",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"label": "Azure US Government",
|
||||||
|
"value": "govazuremonitor",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"label": "Azure Germany",
|
||||||
|
"value": "germanyazuremonitor",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"label": "Azure China",
|
||||||
|
"value": "chinaazuremonitor",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
tabSelectsValue={true}
|
||||||
|
value={
|
||||||
|
Object {
|
||||||
|
"label": "Azure",
|
||||||
|
"value": "azuremonitor",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="gf-form-inline"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="gf-form"
|
||||||
|
>
|
||||||
|
<FormLabel
|
||||||
|
className="width-12"
|
||||||
|
>
|
||||||
|
Directory (tenant) ID
|
||||||
|
</FormLabel>
|
||||||
|
<div
|
||||||
|
className="width-15"
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
className="width-30"
|
||||||
|
onChange={[Function]}
|
||||||
|
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||||
|
value="e7f3f661-a933-3h3f-0294-31c4f962ec48"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="gf-form-inline"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="gf-form"
|
||||||
|
>
|
||||||
|
<FormLabel
|
||||||
|
className="width-12"
|
||||||
|
>
|
||||||
|
Application (client) ID
|
||||||
|
</FormLabel>
|
||||||
|
<div
|
||||||
|
className="width-15"
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
className="width-30"
|
||||||
|
onChange={[Function]}
|
||||||
|
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||||
|
value="34509fad-c0r9-45df-9e25-f1ee34af6900"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="gf-form-inline"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="gf-form"
|
||||||
|
>
|
||||||
|
<FormLabel
|
||||||
|
className="width-12"
|
||||||
|
>
|
||||||
|
Client Secret
|
||||||
|
</FormLabel>
|
||||||
|
<div
|
||||||
|
className="width-15"
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
className="width-30"
|
||||||
|
onChange={[Function]}
|
||||||
|
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||||
|
value=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="gf-form-inline"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="gf-form"
|
||||||
|
>
|
||||||
|
<FormLabel
|
||||||
|
className="width-12"
|
||||||
|
>
|
||||||
|
Default Subscription
|
||||||
|
</FormLabel>
|
||||||
|
<div
|
||||||
|
className="width-25"
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
allowCustomValue={false}
|
||||||
|
autoFocus={false}
|
||||||
|
backspaceRemovesValue={true}
|
||||||
|
className=""
|
||||||
|
components={
|
||||||
|
Object {
|
||||||
|
"Group": [Function],
|
||||||
|
"IndicatorsContainer": [Function],
|
||||||
|
"MenuList": [Function],
|
||||||
|
"Option": [Function],
|
||||||
|
"SingleValue": [Function],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isClearable={false}
|
||||||
|
isDisabled={false}
|
||||||
|
isLoading={false}
|
||||||
|
isMulti={false}
|
||||||
|
isSearchable={true}
|
||||||
|
maxMenuHeight={300}
|
||||||
|
onChange={[Function]}
|
||||||
|
openMenuOnFocus={false}
|
||||||
|
options={Array []}
|
||||||
|
tabSelectsValue={true}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="gf-form-inline"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="gf-form"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="max-width-30 gf-form-inline"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
disabled={true}
|
||||||
|
onClick={[Function]}
|
||||||
|
size="sm"
|
||||||
|
type="button"
|
||||||
|
variant="secondary"
|
||||||
|
>
|
||||||
|
Load Subscriptions
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
Loading…
Reference in New Issue
Block a user