mirror of https://github.com/grafana/grafana
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 AzureAuthSettingspull/37119/head
parent
013218e075
commit
4664cba935
@ -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; |
@ -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