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
pull/37119/head
Sergey Kostrukov 4 years ago committed by GitHub
parent 013218e075
commit 4664cba935
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 31
      packages/grafana-ui/src/components/DataSourceSettings/DataSourceHttpSettings.tsx
  2. 8
      packages/grafana-ui/src/components/DataSourceSettings/types.ts
  3. 51
      public/app/plugins/datasource/prometheus/configuration/AzureAuthSettings.tsx
  4. 48
      public/app/plugins/datasource/prometheus/configuration/AzureCredentials.ts
  5. 107
      public/app/plugins/datasource/prometheus/configuration/AzureCredentialsConfig.ts
  6. 66
      public/app/plugins/datasource/prometheus/configuration/AzureCredentialsForm.test.tsx
  7. 279
      public/app/plugins/datasource/prometheus/configuration/AzureCredentialsForm.tsx
  8. 12
      public/app/plugins/datasource/prometheus/configuration/ConfigEditor.tsx
  9. 620
      public/app/plugins/datasource/prometheus/configuration/__snapshots__/AzureCredentialsForm.test.tsx.snap

@ -56,7 +56,14 @@ const HttpAccessHelp = () => (
);
export const DataSourceHttpSettings: React.FC<HttpSettingsProps> = (props) => {
const { defaultUrl, dataSourceConfig, onChange, showAccessOptions, sigV4AuthToggleEnabled } = props;
const {
defaultUrl,
dataSourceConfig,
onChange,
showAccessOptions,
sigV4AuthToggleEnabled,
azureAuthSettings,
} = props;
let urlTooltip;
const [isAccessHelpVisible, setIsAccessHelpVisible] = useState(false);
const theme = useTheme();
@ -207,6 +214,22 @@ export const DataSourceHttpSettings: React.FC<HttpSettingsProps> = (props) => {
/>
</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 && (
<div className="gf-form-inline">
<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.tlsAuth || dataSourceConfig.jsonData.tlsAuthWithCACert) && (

@ -1,5 +1,11 @@
import React from 'react';
import { DataSourceSettings } from '@grafana/data';
export interface AzureAuthSettings {
azureAuthEnabled: boolean;
azureSettingsUI?: React.ComponentType<HttpSettingsBaseProps>;
}
export interface HttpSettingsBaseProps {
/** The configuration object of the data source */
dataSourceConfig: DataSourceSettings<any, any>;
@ -14,4 +20,6 @@ export interface HttpSettingsProps extends HttpSettingsBaseProps {
showAccessOptions?: boolean;
/** Show the SigV4 auth toggle option */
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 { AlertingSettings, DataSourceHttpSettings } from '@grafana/ui';
import { DataSourcePluginOptionsEditorProps } from '@grafana/data';
import { PromSettings } from './PromSettings';
import { PromOptions } from '../types';
import { config } from 'app/core/config';
import { PromOptions } from '../types';
import { AzureAuthSettings } from './AzureAuthSettings';
import { PromSettings } from './PromSettings';
export type Props = DataSourcePluginOptionsEditorProps<PromOptions>;
export const ConfigEditor = (props: Props) => {
const { options, onOptionsChange } = props;
const azureAuthSettings = {
azureAuthEnabled: config.featureToggles['prometheus_azure_auth'] ?? false,
azureSettingsUI: AzureAuthSettings,
};
return (
<>
<DataSourceHttpSettings
@ -16,6 +23,7 @@ export const ConfigEditor = (props: Props) => {
showAccessOptions={true}
onChange={onOptionsChange}
sigV4AuthToggleEnabled={config.sigV4AuthEnabled}
azureAuthSettings={azureAuthSettings}
/>
<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…
Cancel
Save