V2Wizard: add all necessary components
This commit is contained in:
parent
fa016c3228
commit
f77cc39135
9 changed files with 445 additions and 150 deletions
|
|
@ -17,9 +17,13 @@ import RepositoriesStep from './steps/Repositories';
|
|||
import ReviewStep from './steps/Review';
|
||||
import ReviewWizardFooter from './steps/Review/Footer';
|
||||
import Aws from './steps/TargetEnvironment/Aws';
|
||||
import Azure from './steps/TargetEnvironment/Azure';
|
||||
import Gcp from './steps/TargetEnvironment/Gcp';
|
||||
import {
|
||||
isAwsAccountIdValid,
|
||||
isAzureTenantGUIDValid,
|
||||
isAzureSubscriptionIdValid,
|
||||
isAzureResourceGroupValid,
|
||||
isBlueprintDescriptionValid,
|
||||
isBlueprintNameValid,
|
||||
isGcpEmailValid,
|
||||
|
|
@ -33,6 +37,11 @@ import {
|
|||
selectAwsAccountId,
|
||||
selectAwsShareMethod,
|
||||
selectAwsSource,
|
||||
selectAzureResourceGroup,
|
||||
selectAzureShareMethod,
|
||||
selectAzureSource,
|
||||
selectAzureSubscriptionId,
|
||||
selectAzureTenantId,
|
||||
selectBlueprintDescription,
|
||||
selectBlueprintName,
|
||||
selectGcpEmail,
|
||||
|
|
@ -100,6 +109,18 @@ const CreateImageWizard = () => {
|
|||
// GCP
|
||||
const gcpShareMethod = useAppSelector((state) => selectGcpShareMethod(state));
|
||||
const gcpEmail = useAppSelector((state) => selectGcpEmail(state));
|
||||
// AZURE
|
||||
const azureShareMethod = useAppSelector((state) =>
|
||||
selectAzureShareMethod(state)
|
||||
);
|
||||
const azureTenantId = useAppSelector((state) => selectAzureTenantId(state));
|
||||
const azureSubscriptionId = useAppSelector((state) =>
|
||||
selectAzureSubscriptionId(state)
|
||||
);
|
||||
const azureResourceGroup = useAppSelector((state) =>
|
||||
selectAzureResourceGroup(state)
|
||||
);
|
||||
const azureSource = useAppSelector((state) => selectAzureSource(state));
|
||||
|
||||
const registrationType = useAppSelector((state) =>
|
||||
selectRegistrationType(state)
|
||||
|
|
@ -169,6 +190,29 @@ const CreateImageWizard = () => {
|
|||
>
|
||||
<Gcp />
|
||||
</WizardStep>,
|
||||
<WizardStep
|
||||
name="Azure"
|
||||
id="wizard-target-azure"
|
||||
key="wizard-target-azure"
|
||||
footer={
|
||||
<CustomWizardFooter
|
||||
disableNext={
|
||||
azureShareMethod === 'manual'
|
||||
? !isAzureTenantGUIDValid(azureTenantId) ||
|
||||
!isAzureSubscriptionIdValid(azureSubscriptionId) ||
|
||||
!isAzureResourceGroupValid(azureResourceGroup)
|
||||
: azureShareMethod === 'sources'
|
||||
? !isAzureTenantGUIDValid(azureTenantId) ||
|
||||
!isAzureSubscriptionIdValid(azureSubscriptionId) ||
|
||||
!isAzureResourceGroupValid(azureResourceGroup)
|
||||
: azureSource === undefined
|
||||
}
|
||||
/>
|
||||
}
|
||||
isHidden={!targetEnvironments.includes('azure')}
|
||||
>
|
||||
<Azure />
|
||||
</WizardStep>,
|
||||
]}
|
||||
/>
|
||||
<WizardStep
|
||||
|
|
|
|||
|
|
@ -31,14 +31,6 @@ import { isAwsAccountIdValid } from '../../../validators';
|
|||
|
||||
export type AwsShareMethod = 'manual' | 'sources';
|
||||
|
||||
// The Sources API only defines a V1ListSourceResponseItem[] type
|
||||
export type V1ListSourceResponseItem = {
|
||||
id?: string;
|
||||
name?: string;
|
||||
source_type_id?: string;
|
||||
uid?: string;
|
||||
};
|
||||
|
||||
const SourcesButton = () => {
|
||||
return (
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import React from 'react';
|
||||
|
||||
import useFormApi from '@data-driven-forms/react-form-renderer/use-form-api';
|
||||
import { Button, FormGroup } from '@patternfly/react-core';
|
||||
|
||||
const AzureAuthButton = () => {
|
||||
const { getState } = useFormApi();
|
||||
import { useAppSelector } from '../../../../../store/hooks';
|
||||
import { selectAzureTenantId } from '../../../../../store/wizardSlice';
|
||||
|
||||
const tenantId = getState()?.values?.['azure-tenant-id'];
|
||||
export const AzureAuthButton = () => {
|
||||
const tenantId = useAppSelector((state) => selectAzureTenantId(state));
|
||||
const guidRegex = new RegExp(
|
||||
'^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$',
|
||||
'i'
|
||||
|
|
@ -31,5 +31,3 @@ const AzureAuthButton = () => {
|
|||
</FormGroup>
|
||||
);
|
||||
};
|
||||
|
||||
export default AzureAuthButton;
|
||||
|
|
|
|||
|
|
@ -1,75 +1,83 @@
|
|||
import React, { useState } from 'react';
|
||||
|
||||
import FormSpy from '@data-driven-forms/react-form-renderer/form-spy';
|
||||
import useFieldApi from '@data-driven-forms/react-form-renderer/use-field-api';
|
||||
import useFormApi from '@data-driven-forms/react-form-renderer/use-form-api';
|
||||
import { FormGroup, Spinner } from '@patternfly/react-core';
|
||||
import {
|
||||
Select,
|
||||
SelectOption,
|
||||
SelectVariant,
|
||||
} from '@patternfly/react-core/deprecated';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { useGetSourceUploadInfoQuery } from '../../../store/provisioningApi';
|
||||
import { useAppDispatch, useAppSelector } from '../../../../../store/hooks';
|
||||
import { useGetSourceUploadInfoQuery } from '../../../../../store/provisioningApi';
|
||||
import {
|
||||
changeAzureResourceGroup,
|
||||
selectAzureResourceGroup,
|
||||
selectAzureSource,
|
||||
} from '../../../../../store/wizardSlice';
|
||||
|
||||
const AzureResourceGroups = ({ label, isRequired, className, ...props }) => {
|
||||
const { change, getState } = useFormApi();
|
||||
const { input } = useFieldApi(props);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [sourceId, setSourceId] = useState(
|
||||
getState()?.values?.['azure-sources-select']
|
||||
export const AzureResourceGroups = () => {
|
||||
const azureSource = useAppSelector((state) => selectAzureSource(state));
|
||||
const azureResourceGroup = useAppSelector((state) =>
|
||||
selectAzureResourceGroup(state)
|
||||
);
|
||||
const onFormChange = ({ values }) => {
|
||||
setSourceId(values['azure-sources-select']);
|
||||
};
|
||||
const dispatch = useAppDispatch();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const { data: sourceDetails, isFetching } = useGetSourceUploadInfoQuery(
|
||||
{ id: sourceId },
|
||||
// @ts-ignore
|
||||
{ id: azureSource },
|
||||
{
|
||||
skip: !sourceId,
|
||||
skip: !azureSource,
|
||||
}
|
||||
);
|
||||
const resourceGroups =
|
||||
(sourceId && sourceDetails?.azure?.resource_groups) || [];
|
||||
|
||||
const setResourceGroup = (_, selection) => {
|
||||
const resourceGroups =
|
||||
(azureSource && sourceDetails?.azure?.resource_groups) || [];
|
||||
|
||||
const setResourceGroup = (
|
||||
_event: React.MouseEvent<Element, MouseEvent>,
|
||||
selection: string
|
||||
) => {
|
||||
const resource =
|
||||
resourceGroups?.find((resource) => resource === selection) || '';
|
||||
setIsOpen(false);
|
||||
change(input.name, selection);
|
||||
dispatch(changeAzureResourceGroup(resource));
|
||||
};
|
||||
|
||||
const handleClear = () => {
|
||||
change(input.name, undefined);
|
||||
dispatch(changeAzureResourceGroup(''));
|
||||
};
|
||||
const options: JSX.Element[] = [];
|
||||
|
||||
if (isFetching) {
|
||||
options.push(
|
||||
<SelectOption
|
||||
isNoResultsOption={true}
|
||||
data-testid="azure-resource-groups-loading"
|
||||
>
|
||||
<Spinner size="lg" />
|
||||
</SelectOption>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<FormGroup
|
||||
isRequired={isRequired}
|
||||
label={label}
|
||||
isRequired
|
||||
label={'Resource group'}
|
||||
data-testid="azure-resource-groups"
|
||||
>
|
||||
<FormSpy subscription={{ values: true }} onChange={onFormChange} />
|
||||
<Select
|
||||
ouiaId="resource_group_select"
|
||||
variant={SelectVariant.typeahead}
|
||||
className={className}
|
||||
onToggle={() => setIsOpen(!isOpen)}
|
||||
onSelect={setResourceGroup}
|
||||
onClear={handleClear}
|
||||
selections={input.value}
|
||||
selections={azureResourceGroup}
|
||||
isOpen={isOpen}
|
||||
placeholderText="Select resource group"
|
||||
typeAheadAriaLabel="Select resource group"
|
||||
>
|
||||
{isFetching && (
|
||||
<SelectOption
|
||||
isNoResultsOption={true}
|
||||
data-testid="azure-resource-groups-loading"
|
||||
>
|
||||
<Spinner size="lg" />
|
||||
</SelectOption>
|
||||
)}
|
||||
{resourceGroups.map((name, index) => (
|
||||
{resourceGroups.map((name: string, index: number) => (
|
||||
<SelectOption
|
||||
key={index}
|
||||
value={name}
|
||||
|
|
@ -80,17 +88,3 @@ const AzureResourceGroups = ({ label, isRequired, className, ...props }) => {
|
|||
</FormGroup>
|
||||
);
|
||||
};
|
||||
|
||||
AzureResourceGroups.propTypes = {
|
||||
label: PropTypes.node,
|
||||
isRequired: PropTypes.bool,
|
||||
className: PropTypes.string,
|
||||
};
|
||||
|
||||
AzureResourceGroups.defaultProps = {
|
||||
label: '',
|
||||
isRequired: false,
|
||||
className: '',
|
||||
};
|
||||
|
||||
export default AzureResourceGroups;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
import FormSpy from '@data-driven-forms/react-form-renderer/form-spy';
|
||||
import useFieldApi from '@data-driven-forms/react-form-renderer/use-field-api';
|
||||
import useFormApi from '@data-driven-forms/react-form-renderer/use-form-api';
|
||||
import { Alert } from '@patternfly/react-core';
|
||||
import { FormGroup, Spinner } from '@patternfly/react-core';
|
||||
import {
|
||||
|
|
@ -10,19 +7,24 @@ import {
|
|||
SelectOption,
|
||||
SelectVariant,
|
||||
} from '@patternfly/react-core/deprecated';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { extractProvisioningList } from '../../../store/helpers';
|
||||
import { useAppDispatch, useAppSelector } from '../../../../../store/hooks';
|
||||
import {
|
||||
useGetSourceListQuery,
|
||||
useGetSourceUploadInfoQuery,
|
||||
} from '../../../store/provisioningApi';
|
||||
} from '../../../../../store/provisioningApi';
|
||||
import {
|
||||
changeAzureResourceGroup,
|
||||
changeAzureSource,
|
||||
changeAzureSubscriptionId,
|
||||
changeAzureTenantId,
|
||||
selectAzureSource,
|
||||
} from '../../../../../store/wizardSlice';
|
||||
|
||||
const AzureSourcesSelect = ({ label, isRequired, className, ...props }) => {
|
||||
const { change } = useFormApi();
|
||||
const { input } = useFieldApi(props);
|
||||
export const AzureSourcesSelect = () => {
|
||||
const azureSource = useAppSelector((state) => selectAzureSource(state));
|
||||
const dispatch = useAppDispatch();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const selectedSourceId = input.value;
|
||||
|
||||
const {
|
||||
data: rawSources,
|
||||
|
|
@ -31,7 +33,6 @@ const AzureSourcesSelect = ({ label, isRequired, className, ...props }) => {
|
|||
isError,
|
||||
refetch,
|
||||
} = useGetSourceListQuery({ provider: 'azure' });
|
||||
const sources = extractProvisioningList(rawSources);
|
||||
|
||||
const {
|
||||
data: sourceDetails,
|
||||
|
|
@ -39,43 +40,42 @@ const AzureSourcesSelect = ({ label, isRequired, className, ...props }) => {
|
|||
isSuccess: isSuccessDetails,
|
||||
isError: isErrorDetails,
|
||||
} = useGetSourceUploadInfoQuery(
|
||||
{ id: selectedSourceId },
|
||||
{ id: parseInt(azureSource as string) },
|
||||
{
|
||||
skip: !selectedSourceId,
|
||||
skip: !azureSource,
|
||||
}
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (isFetchingDetails || !isSuccessDetails) return;
|
||||
change('azure-tenant-id', sourceDetails?.azure?.tenant_id);
|
||||
change('azure-subscription-id', sourceDetails?.azure?.subscription_id);
|
||||
dispatch(changeAzureTenantId(sourceDetails?.azure?.tenant_id || ''));
|
||||
dispatch(
|
||||
changeAzureSubscriptionId(sourceDetails?.azure?.subscription_id || '')
|
||||
);
|
||||
}, [
|
||||
isFetchingDetails,
|
||||
isSuccessDetails,
|
||||
sourceDetails?.azure?.subscription_id,
|
||||
sourceDetails?.azure?.tenant_id,
|
||||
change,
|
||||
sourceDetails?.azure?.subscription_id,
|
||||
dispatch,
|
||||
]);
|
||||
|
||||
const onFormChange = ({ values }) => {
|
||||
if (
|
||||
values['azure-type'] !== 'azure-type-source' ||
|
||||
values[input.name] === undefined
|
||||
) {
|
||||
change(input.name, undefined);
|
||||
change('azure-tenant-id', undefined);
|
||||
change('azure-subscription-id', undefined);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelect = (_, sourceName) => {
|
||||
const sourceId = sources.find((source) => source.name === sourceName).id;
|
||||
change(input.name, sourceId);
|
||||
const handleSelect = (
|
||||
_event: React.MouseEvent<Element, MouseEvent>,
|
||||
sourceName: string
|
||||
) => {
|
||||
const sourceId = rawSources?.data?.find(
|
||||
(source) => source?.name === sourceName
|
||||
)?.id;
|
||||
dispatch(changeAzureSource(sourceId || ''));
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
const handleClear = () => {
|
||||
change(input.name, undefined);
|
||||
dispatch(changeAzureSource(''));
|
||||
dispatch(changeAzureTenantId(''));
|
||||
dispatch(changeAzureSubscriptionId(''));
|
||||
dispatch(changeAzureResourceGroup(''));
|
||||
};
|
||||
|
||||
const handleToggle = () => {
|
||||
|
|
@ -86,25 +86,33 @@ const AzureSourcesSelect = ({ label, isRequired, className, ...props }) => {
|
|||
|
||||
setIsOpen(!isOpen);
|
||||
};
|
||||
const selectOptions = rawSources?.data?.map((source) => (
|
||||
<SelectOption key={source.id} value={source.name} />
|
||||
));
|
||||
|
||||
if (isSuccess) {
|
||||
if (isFetching) {
|
||||
selectOptions?.push(
|
||||
<SelectOption key="loading" isNoResultsOption={true}>
|
||||
<Spinner size="lg" />
|
||||
</SelectOption>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormSpy subscription={{ values: true }} onChange={onFormChange} />
|
||||
<FormGroup
|
||||
isRequired={isRequired}
|
||||
label={label}
|
||||
data-testid="azure-sources"
|
||||
>
|
||||
<FormGroup isRequired label={'Source Name'} data-testid="azure-sources">
|
||||
<Select
|
||||
ouiaId="source_select"
|
||||
variant={SelectVariant.typeahead}
|
||||
className={className}
|
||||
onToggle={handleToggle}
|
||||
onSelect={handleSelect}
|
||||
onClear={handleClear}
|
||||
selections={
|
||||
selectedSourceId
|
||||
? sources.find((source) => source.id === selectedSourceId)?.name
|
||||
azureSource
|
||||
? rawSources?.data?.find((source) => source.id === azureSource)
|
||||
?.name
|
||||
: undefined
|
||||
}
|
||||
isOpen={isOpen}
|
||||
|
|
@ -114,50 +122,33 @@ const AzureSourcesSelect = ({ label, isRequired, className, ...props }) => {
|
|||
maxHeight="25rem"
|
||||
isDisabled={!isSuccess}
|
||||
>
|
||||
{isSuccess &&
|
||||
sources.map((source) => (
|
||||
<SelectOption key={source.id} value={source.name} />
|
||||
))}
|
||||
{isFetching && (
|
||||
<SelectOption isNoResultsOption={true}>
|
||||
<Spinner size="lg" />
|
||||
</SelectOption>
|
||||
)}
|
||||
{selectOptions}
|
||||
</Select>
|
||||
</FormGroup>
|
||||
<>
|
||||
{isError && (
|
||||
<Alert
|
||||
variant={'danger'}
|
||||
isPlain
|
||||
isInline
|
||||
title={'Sources unavailable'}
|
||||
>
|
||||
Sources cannot be reached, try again later or enter an account info
|
||||
for upload manually.
|
||||
</Alert>
|
||||
)}
|
||||
{!isError && isErrorDetails && (
|
||||
<Alert
|
||||
variant={'danger'}
|
||||
isPlain
|
||||
isInline
|
||||
title={'Azure details unavailable'}
|
||||
>
|
||||
Could not fetch Tenant id and Subscription id from Azure for given
|
||||
Source. Check Sources page for the source availability or select a
|
||||
different Source.
|
||||
</Alert>
|
||||
)}
|
||||
</>
|
||||
{isError && (
|
||||
<Alert
|
||||
variant={'danger'}
|
||||
isPlain
|
||||
isInline
|
||||
title={'Sources unavailable'}
|
||||
>
|
||||
Sources cannot be reached, try again later or enter an account info
|
||||
for upload manually.
|
||||
</Alert>
|
||||
)}
|
||||
{!isError && isErrorDetails && (
|
||||
<Alert
|
||||
variant={'danger'}
|
||||
isPlain
|
||||
isInline
|
||||
title={'Azure details unavailable'}
|
||||
>
|
||||
Could not fetch Tenant id and Subscription id from Azure for given
|
||||
Source. Check Sources page for the source availability or select a
|
||||
different Source.
|
||||
</Alert>
|
||||
)}
|
||||
<></>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
AzureSourcesSelect.propTypes = {
|
||||
className: PropTypes.string,
|
||||
label: PropTypes.node,
|
||||
isRequired: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default AzureSourcesSelect;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,197 @@
|
|||
import React from 'react';
|
||||
|
||||
import {
|
||||
Radio,
|
||||
Text,
|
||||
Form,
|
||||
Title,
|
||||
FormGroup,
|
||||
TextInput,
|
||||
Gallery,
|
||||
GalleryItem,
|
||||
Button,
|
||||
} from '@patternfly/react-core';
|
||||
import { ExternalLinkAltIcon } from '@patternfly/react-icons';
|
||||
|
||||
import { AzureAuthButton } from './AzureAuthButton';
|
||||
import { AzureResourceGroups } from './AzureResourceGroups';
|
||||
import { AzureSourcesSelect } from './AzureSourcesSelect';
|
||||
|
||||
import { useAppDispatch, useAppSelector } from '../../../../../store/hooks';
|
||||
import {
|
||||
changeAzureResourceGroup,
|
||||
changeAzureShareMethod,
|
||||
changeAzureSource,
|
||||
changeAzureSubscriptionId,
|
||||
changeAzureTenantId,
|
||||
selectAzureResourceGroup,
|
||||
selectAzureShareMethod,
|
||||
selectAzureSubscriptionId,
|
||||
selectAzureTenantId,
|
||||
} from '../../../../../store/wizardSlice';
|
||||
import { ValidatedTextInput } from '../../../ValidatedTextInput';
|
||||
import {
|
||||
isAzureResourceGroupValid,
|
||||
isAzureSubscriptionIdValid,
|
||||
isAzureTenantGUIDValid,
|
||||
} from '../../../validators';
|
||||
|
||||
export type AzureShareMethod = 'manual' | 'sources';
|
||||
|
||||
const SourcesButton = () => {
|
||||
return (
|
||||
<Button
|
||||
component="a"
|
||||
target="_blank"
|
||||
variant="link"
|
||||
icon={<ExternalLinkAltIcon />}
|
||||
iconPosition="right"
|
||||
isInline
|
||||
href={'settings/sources'}
|
||||
>
|
||||
Create and manage sources here
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
const Azure = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const shareMethod = useAppSelector((state) => selectAzureShareMethod(state));
|
||||
const tenantId = useAppSelector((state) => selectAzureTenantId(state));
|
||||
const subscriptionId = useAppSelector((state) =>
|
||||
selectAzureSubscriptionId(state)
|
||||
);
|
||||
const resourceGroup = useAppSelector((state) =>
|
||||
selectAzureResourceGroup(state)
|
||||
);
|
||||
|
||||
return (
|
||||
<Form>
|
||||
<Title headingLevel="h2">Target environment - Microsoft Azure</Title>
|
||||
<Text>
|
||||
Upon build, Image Builder sends the image to the selected authorized
|
||||
Azure account. The image will be uploaded to the resource group in the
|
||||
subscription you specify.
|
||||
</Text>
|
||||
<Text>
|
||||
To authorize Image Builder to push images to Microsoft Azure, the
|
||||
account owner must configure Image Builder as an authorized application
|
||||
for a specific tenant ID and give it the role of "Contributor"
|
||||
for the resource group you want to upload to. This applies even when
|
||||
defining target by Source selection.
|
||||
<br />
|
||||
<Button
|
||||
component="a"
|
||||
target="_blank"
|
||||
variant="link"
|
||||
icon={<ExternalLinkAltIcon />}
|
||||
iconPosition="right"
|
||||
isInline
|
||||
href="https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow"
|
||||
>
|
||||
Learn more about OAuth 2.0
|
||||
</Button>
|
||||
</Text>
|
||||
<FormGroup label="Share method:">
|
||||
<Radio
|
||||
id="radio-with-description"
|
||||
label="Use an account configured from Sources."
|
||||
name="radio-7"
|
||||
description="Use a configured sources to launch environments directly from the console."
|
||||
isChecked={shareMethod === 'sources'}
|
||||
onChange={() => {
|
||||
dispatch(changeAzureSource(''));
|
||||
dispatch(changeAzureTenantId(''));
|
||||
dispatch(changeAzureSubscriptionId(''));
|
||||
dispatch(changeAzureShareMethod('sources'));
|
||||
dispatch(changeAzureResourceGroup(''));
|
||||
}}
|
||||
/>
|
||||
<Radio
|
||||
id="radio"
|
||||
label="Manually enter the account information."
|
||||
name="radio-8"
|
||||
isChecked={shareMethod === 'manual'}
|
||||
onChange={() => {
|
||||
dispatch(changeAzureSource(''));
|
||||
dispatch(changeAzureTenantId(''));
|
||||
dispatch(changeAzureSubscriptionId(''));
|
||||
dispatch(changeAzureShareMethod('manual'));
|
||||
dispatch(changeAzureResourceGroup(''));
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
{shareMethod === 'sources' && (
|
||||
<>
|
||||
<AzureSourcesSelect />
|
||||
<SourcesButton />
|
||||
<Gallery hasGutter>
|
||||
<GalleryItem>
|
||||
<FormGroup label="Azure Tenant GUID" isRequired>
|
||||
<TextInput
|
||||
aria-label="Azure Tenant GUID"
|
||||
readOnlyVariant="default"
|
||||
isRequired
|
||||
id="tenant id"
|
||||
value={tenantId}
|
||||
/>
|
||||
</FormGroup>
|
||||
</GalleryItem>
|
||||
<GalleryItem>
|
||||
<FormGroup label="Subscription ID" isRequired>
|
||||
<TextInput
|
||||
aria-label="Subscription ID"
|
||||
label="Subscription ID"
|
||||
readOnlyVariant="default"
|
||||
isRequired
|
||||
id="subscription id"
|
||||
value={subscriptionId}
|
||||
/>
|
||||
</FormGroup>
|
||||
</GalleryItem>
|
||||
</Gallery>
|
||||
<AzureAuthButton />
|
||||
<AzureResourceGroups />
|
||||
</>
|
||||
)}
|
||||
{shareMethod === 'manual' && (
|
||||
<>
|
||||
<FormGroup label="Azure Tenant GUID" isRequired>
|
||||
<ValidatedTextInput
|
||||
ariaLabel="Azure Tenant GUID"
|
||||
value={tenantId || ''}
|
||||
validator={isAzureTenantGUIDValid}
|
||||
onChange={(_event, value) => dispatch(changeAzureTenantId(value))}
|
||||
helperText="Please enter a valid tenant ID"
|
||||
/>
|
||||
</FormGroup>
|
||||
<AzureAuthButton />
|
||||
<FormGroup label="Subscription ID" isRequired>
|
||||
<ValidatedTextInput
|
||||
ariaLabel="subscription id"
|
||||
value={subscriptionId}
|
||||
validator={isAzureSubscriptionIdValid}
|
||||
onChange={(_event, value) =>
|
||||
dispatch(changeAzureSubscriptionId(value))
|
||||
}
|
||||
helperText="Please enter a valid subscription ID"
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup label="Resource group" isRequired>
|
||||
<ValidatedTextInput
|
||||
ariaLabel="resource group"
|
||||
value={resourceGroup}
|
||||
validator={isAzureResourceGroupValid}
|
||||
onChange={(_event, value) =>
|
||||
dispatch(changeAzureResourceGroup(value))
|
||||
}
|
||||
helperText="Resource group names only allow alphanumeric characters, periods, underscores, hyphens, and parenthesis and cannot end in a period"
|
||||
/>
|
||||
</FormGroup>
|
||||
</>
|
||||
)}
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
export default Azure;
|
||||
7
src/Components/CreateImageWizardV2/types.tsx
Normal file
7
src/Components/CreateImageWizardV2/types.tsx
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
// The Sources API only defines a V1ListSourceResponseItem[] type
|
||||
export type V1ListSourceResponseItem = {
|
||||
id?: string;
|
||||
name?: string;
|
||||
source_type_id?: string;
|
||||
uid?: string;
|
||||
};
|
||||
|
|
@ -6,6 +6,22 @@ export const isAwsAccountIdValid = (awsAccountId: string | undefined) => {
|
|||
);
|
||||
};
|
||||
|
||||
export const isAzureTenantGUIDValid = (azureTenantGUID: string) => {
|
||||
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(
|
||||
azureTenantGUID
|
||||
);
|
||||
};
|
||||
|
||||
export const isAzureSubscriptionIdValid = (azureSubscriptionId: string) => {
|
||||
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(
|
||||
azureSubscriptionId
|
||||
);
|
||||
};
|
||||
|
||||
export const isAzureResourceGroupValid = (azureResourceGroup: string) => {
|
||||
return /^[-\w._()]+[-\w_()]$/.test(azureResourceGroup);
|
||||
};
|
||||
|
||||
export const isGcpEmailValid = (gcpShareWithAccount: string | undefined) => {
|
||||
return (
|
||||
gcpShareWithAccount !== undefined &&
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue