CreateImageWizard: Move steps into separate components
This commit is contained in:
parent
c270991b85
commit
28cc248335
28 changed files with 1165 additions and 842 deletions
|
|
@ -1,74 +0,0 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { Form, FormGroup, FormSelect, FormSelectOption, Tile, Title } from '@patternfly/react-core';
|
||||
|
||||
import './WizardStepImageOutput.scss';
|
||||
|
||||
const WizardStepImageOutput = (props) => {
|
||||
const releaseOptions = [
|
||||
{ value: 'rhel-8', label: 'Red Hat Enterprise Linux (RHEL) 8.3' },
|
||||
{ value: 'centos-8', label: 'CentOS Stream 8' },
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form>
|
||||
<Title headingLevel="h2" size="xl">Image output</Title>
|
||||
<FormGroup isRequired label="Release" fieldId="release-select">
|
||||
<FormSelect value={ props.value } onChange={ value => props.setRelease(value) } isRequired
|
||||
aria-label="Select release input" id="release-select" data-testid="release-select">
|
||||
{ releaseOptions.map(option => <FormSelectOption key={ option.value } value={ option.value } label={ option.label } />) }
|
||||
</FormSelect>
|
||||
</FormGroup>
|
||||
<FormGroup isRequired label="Select target environment" data-testid="target-select">
|
||||
<div className="tiles">
|
||||
<Tile
|
||||
className="tile pf-u-mr-sm"
|
||||
data-testid="upload-aws"
|
||||
title="Amazon Web Services"
|
||||
icon={ <img
|
||||
className='provider-icon'
|
||||
src={ '/apps/frontend-assets/partners-icons/aws.svg' } /> }
|
||||
onClick={ () => props.toggleUploadDestination('aws') }
|
||||
isSelected={ props.uploadDestinations.aws }
|
||||
isStacked
|
||||
isDisplayLarge />
|
||||
<Tile
|
||||
className="tile pf-u-mr-sm"
|
||||
data-testid="upload-azure"
|
||||
title="Microsoft Azure"
|
||||
icon={ <img
|
||||
className='provider-icon'
|
||||
src={ '/apps/frontend-assets/partners-icons/microsoft-azure-short.svg' } /> }
|
||||
onClick={ () => props.toggleUploadDestination('azure') }
|
||||
isSelected={ props.uploadDestinations.azure }
|
||||
isStacked
|
||||
isDisplayLarge />
|
||||
<Tile
|
||||
className="tile"
|
||||
data-testid="upload-google"
|
||||
title="Google Cloud Platform"
|
||||
icon={ <img
|
||||
className='provider-icon'
|
||||
src={ '/apps/frontend-assets/partners-icons/google-cloud-short.svg' } /> }
|
||||
onClick={ () => props.toggleUploadDestination('google') }
|
||||
isSelected={ props.uploadDestinations.google }
|
||||
isStacked
|
||||
isDisplayLarge />
|
||||
</div>
|
||||
</FormGroup>
|
||||
</Form>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
WizardStepImageOutput.propTypes = {
|
||||
toggleUploadAWS: PropTypes.func,
|
||||
uploadDestinations: PropTypes.object,
|
||||
setRelease: PropTypes.func,
|
||||
value: PropTypes.string,
|
||||
toggleUploadDestination: PropTypes.func,
|
||||
};
|
||||
|
||||
export default WizardStepImageOutput;
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { Button, DualListSelector, Text, TextContent, Title } from '@patternfly/react-core';
|
||||
|
||||
const WizardStepPackages = (props) => {
|
||||
const availableOptionsActions = [
|
||||
<Button
|
||||
aria-label="Search button for available packages"
|
||||
key="availableSearchButton"
|
||||
onClick={ props.handlePackagesSearch }>
|
||||
Search
|
||||
</Button>
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<TextContent>
|
||||
<Title headingLevel="h2" size="xl">Additional packages</Title>
|
||||
<Text>Optionally add additional packages to your <strong>{props.release}</strong> image</Text>
|
||||
</TextContent>
|
||||
<DualListSelector
|
||||
className="pf-u-mt-sm"
|
||||
isSearchable
|
||||
availableOptionsActions={ availableOptionsActions }
|
||||
availableOptions={ props.packagesAvailableComponents }
|
||||
chosenOptions={ props.packagesFilteredComponents }
|
||||
addSelected={ props.packageListChange }
|
||||
removeSelected={ props.packageListChange }
|
||||
addAll={ props.packageListChange }
|
||||
removeAll= { props.packageListChange }
|
||||
onAvailableOptionsSearchInputChanged={ props.setPackagesSearchName }
|
||||
onChosenOptionsSearchInputChanged={ props.handlePackagesFilter }
|
||||
filterOption={ () => true }
|
||||
id="basicSelectorWithSearch" />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
WizardStepPackages.propTypes = {
|
||||
packageListChange: PropTypes.func,
|
||||
release: PropTypes.string,
|
||||
packagesAvailableComponents: PropTypes.arrayOf(PropTypes.object),
|
||||
packagesFilteredComponents: PropTypes.arrayOf(PropTypes.object),
|
||||
handlePackagesSearch: PropTypes.func,
|
||||
handlePackagesFilter: PropTypes.func,
|
||||
setPackagesSearchName: PropTypes.func,
|
||||
};
|
||||
|
||||
export default WizardStepPackages;
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { Form, FormGroup, TextInput, Radio, Title } from '@patternfly/react-core';
|
||||
|
||||
const WizardStepRegistration = (props) => {
|
||||
return (
|
||||
<Form>
|
||||
<Title headingLevel="h2" size="xl">Registration</Title>
|
||||
<FormGroup isRequired label="Register the system">
|
||||
<Radio name="subscribe-now-radio" isChecked={ props.subscribeNow } id="subscribe-now-radio"
|
||||
label="Embed an activation key and register systems on first boot"
|
||||
onChange={ () => props.setSubscribeNow(true) }
|
||||
data-testid="register-now-radio-button" />
|
||||
<Radio name="subscribe-later-radio" isChecked={ !props.subscribeNow }
|
||||
label="Register the system later" id="subscribe-later-radio"
|
||||
onChange={ () => props.setSubscribeNow(false) }
|
||||
data-testid="register-later-radio-button" />
|
||||
</FormGroup>
|
||||
{ props.subscribeNow &&
|
||||
<>
|
||||
<FormGroup label="Organization ID" fieldId="subscription-organization">
|
||||
<TextInput isDisabled value={ props.subscription.organization || '' } type="text"
|
||||
id="subscription-organization" aria-label="Subscription organization ID"
|
||||
data-testid="organization-id" />
|
||||
</FormGroup>
|
||||
<FormGroup isRequired label="Activation key" fieldId="subscription-activation"
|
||||
helperTextInvalid={ 'A value is required' }
|
||||
validated={ !props.isValidSubscription && props.subscription['activation-key'] !== null ? 'error' : 'default' }>
|
||||
<TextInput
|
||||
value={ props.subscription['activation-key'] || '' }
|
||||
type="password"
|
||||
data-testid="subscription-activation"
|
||||
id="subscription-activation"
|
||||
aria-label="Subscription activation key"
|
||||
onChange={ value => props.setSubscription(Object.assign(props.subscription, { 'activation-key': value })) }
|
||||
validated={ !props.isValidSubscription && props.subscription['activation-key'] !== null ? 'error' : 'default' }
|
||||
isRequired />
|
||||
</FormGroup>
|
||||
</> }
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
WizardStepRegistration.propTypes = {
|
||||
setSubscription: PropTypes.func,
|
||||
setSubscribeNow: PropTypes.func,
|
||||
subscription: PropTypes.object,
|
||||
subscribeNow: PropTypes.bool,
|
||||
isValidSubscription: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default WizardStepRegistration;
|
||||
|
|
@ -1,136 +0,0 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import {
|
||||
Alert,
|
||||
Text, TextVariants, TextContent, TextList, TextListVariants, TextListItem, TextListItemVariants,
|
||||
Title
|
||||
} from '@patternfly/react-core';
|
||||
import { ExclamationCircleIcon } from '@patternfly/react-icons';
|
||||
|
||||
import './WizardStepReview.scss';
|
||||
|
||||
const WizardStepReview = (props) => {
|
||||
const releaseLabels = {
|
||||
'rhel-8': 'Red Hat Enterprise Linux (RHEL) 8.3',
|
||||
'centos-8': 'CentOS Stream 8'
|
||||
};
|
||||
|
||||
const awsReview = (
|
||||
<>
|
||||
<Text id="destination-header">Amazon Web Services</Text>
|
||||
<TextList component={ TextListVariants.dl } data-testid='review-image-upload-aws'>
|
||||
<TextListItem component={ TextListItemVariants.dt }>Account ID</TextListItem>
|
||||
{props.uploadAWSErrors['aws-account-id'] ? (
|
||||
<TextListItem component={ TextListItemVariants.dd }>
|
||||
<ExclamationCircleIcon className="error" /> { props.uploadAWSErrors['aws-account-id'].value }
|
||||
</TextListItem>
|
||||
) : (
|
||||
<TextListItem component={ TextListItemVariants.dd }>{props.uploadAWS.options.share_with_accounts[0]}</TextListItem>
|
||||
)}
|
||||
</TextList>
|
||||
</>
|
||||
);
|
||||
|
||||
const googleReview = (
|
||||
<>
|
||||
<Text id="destination-header">Google Cloud Platform</Text>
|
||||
<TextList component={ TextListVariants.dl } data-testid='review-image-upload-google'>
|
||||
{props.uploadGoogle.accountType === 'googleAccount' && (
|
||||
<>
|
||||
<TextListItem component={ TextListItemVariants.dt }>Google account</TextListItem>
|
||||
<TextListItem component={ TextListItemVariants.dd }>{props.uploadGoogle.options.share_with_accounts[0] ?
|
||||
props.uploadGoogle.options.share_with_accounts[0].user || '' :
|
||||
''}
|
||||
</TextListItem>
|
||||
</>
|
||||
)}
|
||||
{props.uploadGoogle.accountType === 'serviceAccount' && (
|
||||
<>
|
||||
<TextListItem component={ TextListItemVariants.dt }>Service account</TextListItem>
|
||||
<TextListItem component={ TextListItemVariants.dd }>{props.uploadGoogle.options.share_with_accounts[0] ?
|
||||
props.uploadGoogle.options.share_with_accounts[0].serviceAccount || '' :
|
||||
''}
|
||||
</TextListItem>
|
||||
</>
|
||||
)}
|
||||
{props.uploadGoogle.accountType === 'googleGroup' && (
|
||||
<>
|
||||
<TextListItem component={ TextListItemVariants.dt }>Google group</TextListItem>
|
||||
<TextListItem component={ TextListItemVariants.dd }>{props.uploadGoogle.options.share_with_accounts[0] ?
|
||||
props.uploadGoogle.options.share_with_accounts[0].group || '' :
|
||||
''}
|
||||
</TextListItem>
|
||||
</>
|
||||
)}
|
||||
{props.uploadGoogle.accountType === 'domain' && (
|
||||
<>
|
||||
<TextListItem component={ TextListItemVariants.dt }>Domain</TextListItem>
|
||||
<TextListItem component={ TextListItemVariants.dd }>{props.uploadGoogle.options.share_with_accounts[0] ?
|
||||
props.uploadGoogle.options.share_with_accounts[0].domain || '' :
|
||||
''}
|
||||
</TextListItem>
|
||||
</>
|
||||
)}
|
||||
</TextList>
|
||||
</>
|
||||
);
|
||||
|
||||
let subscriptionReview = <TextListItem component={ TextListItemVariants.dd }>Register the system later</TextListItem>;
|
||||
if (props.subscribeNow) {
|
||||
subscriptionReview = (<>
|
||||
<TextListItem component={ TextListItemVariants.dd }>Register the system on first boot</TextListItem>
|
||||
<TextListItem component={ TextListItemVariants.dt }>Activation key</TextListItem>
|
||||
{ !props.isValidSubscription || !props.subscription['activation-key'] ? (
|
||||
<TextListItem component={ TextListItemVariants.dd }>
|
||||
<ExclamationCircleIcon className="error" /> { 'A value is required' }
|
||||
</TextListItem>
|
||||
) : (
|
||||
<TextListItem component={ TextListItemVariants.dd } type="password">
|
||||
{'*'.repeat(props.subscription['activation-key'].length)}
|
||||
</TextListItem>
|
||||
)}
|
||||
</>);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{ (Object.keys(props.uploadAWSErrors).length > 0 ||
|
||||
!props.isValidSubscription) &&
|
||||
<Alert variant="danger" className="pf-u-mb-xl" isInline title="Required information is missing" /> }
|
||||
<Title headingLevel="h2" size="xl">Review</Title>
|
||||
<TextContent>
|
||||
<Text component={ TextVariants.small }>
|
||||
Review the information and click Create image
|
||||
to create the image using the following criteria.
|
||||
</Text>
|
||||
<Text component={ TextVariants.h3 }>Image output</Text>
|
||||
<TextList component={ TextListVariants.dl } data-testid='review-image-output'>
|
||||
<TextListItem component={ TextListItemVariants.dt }>Release</TextListItem>
|
||||
<TextListItem component={ TextListItemVariants.dd }>{releaseLabels[props.release]}</TextListItem>
|
||||
</TextList>
|
||||
<Text component={ TextVariants.h3 }>Target environment</Text>
|
||||
{props.uploadDestinations.aws && awsReview }
|
||||
{props.uploadDestinations.google && googleReview }
|
||||
<Text component={ TextVariants.h3 }>Registration</Text>
|
||||
<TextList component={ TextListVariants.dl } data-testid='review-image-registration'>
|
||||
<TextListItem component={ TextListItemVariants.dt }>Subscription</TextListItem>
|
||||
{ subscriptionReview }
|
||||
</TextList>
|
||||
</TextContent>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
WizardStepReview.propTypes = {
|
||||
release: PropTypes.string,
|
||||
uploadAWS: PropTypes.object,
|
||||
uploadGoogle: PropTypes.object,
|
||||
uploadDestinations: PropTypes.object,
|
||||
subscription: PropTypes.object,
|
||||
subscribeNow: PropTypes.bool,
|
||||
uploadAWSErrors: PropTypes.object,
|
||||
isValidSubscription: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default WizardStepReview;
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { Form, FormGroup, TextInput, Title } from '@patternfly/react-core';
|
||||
|
||||
const WizardStepUploadAWS = (props) => {
|
||||
return (
|
||||
<Form>
|
||||
<Title headingLevel="h2" size="xl">Target Environment - Upload to AWS</Title>
|
||||
<p>
|
||||
Your image will be uploaded to a temporary account on Amazon Web Services. <br />
|
||||
The image will be shared with the account you provide below. <br />
|
||||
Within the next 14 days you will need to copy the shared image to your own account.
|
||||
After 14 days it will be unavailable and will have to be regenerated.
|
||||
</p>
|
||||
<FormGroup isRequired label="AWS account ID" fieldId="aws-account-id"
|
||||
helperTextInvalid={ (props.errors['aws-account-id'] && props.errors['aws-account-id'].value) || '' }
|
||||
validated={ (props.errors['aws-account-id'] && 'error') || 'default' }>
|
||||
<TextInput value={ props.uploadAWS.options.share_with_accounts || '' }
|
||||
type="text" aria-label="AWS account ID" id="aws-account-id"
|
||||
data-testid="aws-account-id" isRequired
|
||||
onChange={ value =>
|
||||
props.setUploadOptions('aws', Object.assign(props.uploadAWS.options, { share_with_accounts: [ value ]})) } />
|
||||
</FormGroup>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
WizardStepUploadAWS.propTypes = {
|
||||
setUploadOptions: PropTypes.func,
|
||||
uploadAWS: PropTypes.object,
|
||||
errors: PropTypes.object,
|
||||
};
|
||||
|
||||
export default WizardStepUploadAWS;
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { Form, FormGroup, Text, TextContent, TextInput, Title } from '@patternfly/react-core';
|
||||
import { ExternalLinkAltIcon } from '@patternfly/react-icons';
|
||||
|
||||
import './WizardStepUploadAzure.scss';
|
||||
|
||||
const WizardStepUploadAzure = (props) => {
|
||||
return (
|
||||
<>
|
||||
<TextContent className="textcontent-azure">
|
||||
<Title headingLevel="h2">Target Environment - Upload to Azure</Title>
|
||||
<Text>
|
||||
Image Builder will send an image to an authorized Azure account.
|
||||
</Text>
|
||||
<Title headingLevel="h3">OAuth permissions</Title>
|
||||
<Text>
|
||||
In order to use Image Builder to push images to Azure, Image Builder must
|
||||
be configured as an authorized application, and given the role of "Contributor" to at least one resource group.<br />
|
||||
Image Builder must be authorized by an account owner.<br />
|
||||
<a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow">
|
||||
<small>Learn more</small></a>
|
||||
</Text>
|
||||
|
||||
<a href="https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=b94bb246-b02c-4985-9c22-d44e66f657f4
|
||||
&scope=openid&response_type=code&response_mode=form_post
|
||||
&redirect_uri=https%3A%2F%2Flogin.microsoftonline.com%2Fcommon%2Foauth2%2Fnativeclient" target="_blank" rel="noopener noreferrer">
|
||||
Authorize Image Builder on Azure <ExternalLinkAltIcon />
|
||||
</a>
|
||||
</TextContent>
|
||||
|
||||
<Title headingLevel="h3">Destination</Title>
|
||||
<Text>
|
||||
Your image will be uploaded to the resource group in the subscription you specify.
|
||||
</Text>
|
||||
<Form>
|
||||
<FormGroup isRequired label="Tenant ID" fieldId="azure-tenant-id"
|
||||
helperTextInvalid={ (props.errors['azure-tenant-id'] && props.errors['azure-tenant-id'].value) || '' }
|
||||
validated={ (props.errors['azure-tenant-id'] && 'error') || 'default' }>
|
||||
<TextInput value={ props.uploadAzure.options.tenant_id || '' }
|
||||
type="text" aria-label="Azure tenant-id" id="azure-tenant-id"
|
||||
data-testid="azure-tenant-id" isRequired
|
||||
onChange={ value =>
|
||||
props.setUploadOptions('azure', Object.assign(props.uploadAzure.options, { tenant_id: value })) } />
|
||||
</FormGroup>
|
||||
<FormGroup isRequired label="Subscription ID" fieldId="azure-subscription-id"
|
||||
helperTextInvalid={ (props.errors['azure-subscription-id'] &&
|
||||
props.errors['azure-subscription-id'].value) || '' }
|
||||
validated={ (props.errors['azure-subscription-id'] && 'error') || 'default' }>
|
||||
<TextInput value={ props.uploadAzure.options.subscription_id || '' }
|
||||
type="text" aria-label="Azure subscription-id" id="azure-subscription-id"
|
||||
data-testid="azure-subscription-id" isRequired
|
||||
onChange={ value =>
|
||||
props.setUploadOptions('azure', Object.assign(props.uploadAzure.options, { subscription_id: value })) } />
|
||||
</FormGroup>
|
||||
<FormGroup isRequired label="Resource group" fieldId="azure-resource-group"
|
||||
helperTextInvalid={ (props.errors['azure-resource-group'] &&
|
||||
props.errors['azure-resource-group'].value) || '' }
|
||||
validated={ (props.errors['azure-resource-group'] && 'error') || 'default' }>
|
||||
<TextInput value={ props.uploadAzure.options.resource_group || '' }
|
||||
type="text" aria-label="Azure resource group" id="azure-resource-group"
|
||||
data-testid="azure-resource-group" isRequired
|
||||
onChange={ value =>
|
||||
props.setUploadOptions('azure', Object.assign(props.uploadAzure.options, { resource_group: value })) } />
|
||||
</FormGroup>
|
||||
</Form>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
WizardStepUploadAzure.propTypes = {
|
||||
setUploadOptions: PropTypes.func,
|
||||
uploadAzure: PropTypes.object,
|
||||
errors: PropTypes.object,
|
||||
};
|
||||
|
||||
export default WizardStepUploadAzure;
|
||||
|
|
@ -1,148 +0,0 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { Form, FormGroup, TextList, TextListItem, Popover, Radio, TextContent, Text, TextInput, Title } from '@patternfly/react-core';
|
||||
import { HelpIcon } from '@patternfly/react-icons';
|
||||
|
||||
const WizardStepUploadGoogle = (props) => {
|
||||
const accountTypePopover = (
|
||||
<Popover
|
||||
hasAutoWidth
|
||||
maxWidth='35rem'
|
||||
headerContent={ 'Valid account types' }
|
||||
bodyContent={ <TextContent>
|
||||
<Text>The following account types can have an image shared with them:</Text>
|
||||
<TextList>
|
||||
<TextListItem>
|
||||
<strong>Google account:</strong> A Google account represents a developer, an administrator,
|
||||
or any other person who interacts with Google Cloud. e.g., <em>`alice@gmail.com`</em>.
|
||||
</TextListItem>
|
||||
<TextListItem>
|
||||
<strong>Service account:</strong> A service account is an account for an application instead
|
||||
of an individual end user. e.g., <em>`myapp@appspot.gserviceaccount.com`</em>.
|
||||
</TextListItem>
|
||||
<TextListItem>
|
||||
<strong>Google group:</strong> A Google group is a named collection of Google accounts and
|
||||
and service accounts. e.g., <em>`admins@example.com`</em>.
|
||||
</TextListItem>
|
||||
<TextListItem>
|
||||
<strong>Google workspace domain/Cloud identity domain:</strong> A Google workspace or cloud identity
|
||||
domain represents a virtual group of all the Google accounts in an organization. These domains
|
||||
represent your organization's internet domain name. e.g., <em>`mycompany.com`</em>.
|
||||
</TextListItem>
|
||||
</TextList>
|
||||
</TextContent> }>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Account info"
|
||||
aria-describedby="google-account-type"
|
||||
className="pf-c-form__group-label-help">
|
||||
<HelpIcon />
|
||||
</button>
|
||||
</Popover>
|
||||
);
|
||||
|
||||
return (
|
||||
<Form>
|
||||
<Title headingLevel="h2" size="xl">Target Environment - Google Cloud Platform</Title>
|
||||
<p>
|
||||
Your image will be uploaded to an account on Google Cloud Platform. <br />
|
||||
The image will be shared with the email you provide below. <br />
|
||||
Within the next 14 days you will need to copy the shared image to your own account.
|
||||
After 14 days it will be unavailable and will have to be regenerated.
|
||||
</p>
|
||||
<FormGroup isRequired label="Type" labelIcon={ accountTypePopover } fieldId="google-account-type">
|
||||
<Radio
|
||||
onChange={ props.setGoogleAccountType }
|
||||
isChecked={ props.uploadGoogle.accountType === 'googleAccount' }
|
||||
label="Google account"
|
||||
id="radio-google-account"
|
||||
test-id
|
||||
value="googleAccount" />
|
||||
<Radio
|
||||
onChange={ props.setGoogleAccountType }
|
||||
isChecked={ props.uploadGoogle.accountType === 'serviceAccount' }
|
||||
label="Service account"
|
||||
id="radio-service-account"
|
||||
value="serviceAccount" />
|
||||
<Radio
|
||||
onChange={ props.setGoogleAccountType }
|
||||
isChecked={ props.uploadGoogle.accountType === 'googleGroup' }
|
||||
label="Google group"
|
||||
id="radio-google-group"
|
||||
value="googleGroup" />
|
||||
<Radio
|
||||
onChange={ props.setGoogleAccountType }
|
||||
isChecked={ props.uploadGoogle.accountType === 'domain' }
|
||||
label="Google Workspace Domain or Cloud Identity Domain"
|
||||
id="radio-domain"
|
||||
value="domain" />
|
||||
</FormGroup>
|
||||
{props.uploadGoogle.accountType === 'googleAccount' && (
|
||||
<FormGroup isRequired label="Email address" fieldId="user">
|
||||
<TextInput
|
||||
value={ props.uploadGoogle.options.share_with_accounts[0] ?
|
||||
props.uploadGoogle.options.share_with_accounts[0].user || '' :
|
||||
'' }
|
||||
type="text" aria-label="Google email address" id="input-google-user"
|
||||
data-testid="input-google-user" isRequired
|
||||
onChange={ value => props.setUploadOptions(
|
||||
'google',
|
||||
Object.assign(props.uploadGoogle.options, { share_with_accounts: [{ user: value }]})
|
||||
) } />
|
||||
</FormGroup>
|
||||
)}
|
||||
{props.uploadGoogle.accountType === 'serviceAccount' && (
|
||||
<FormGroup isRequired label="Email address" fieldId="service-account">
|
||||
<TextInput
|
||||
value={ props.uploadGoogle.options.share_with_accounts[0] ?
|
||||
props.uploadGoogle.options.share_with_accounts[0].serviceAccount || '' :
|
||||
'' }
|
||||
type="text" aria-label="Google email address" id="input-google-service-account"
|
||||
data-testid="input-google-service-account" isRequired
|
||||
onChange={ value => props.setUploadOptions(
|
||||
'google',
|
||||
Object.assign(props.uploadGoogle.options, { share_with_accounts: [{ serviceAccount: value }]})
|
||||
) } />
|
||||
</FormGroup>
|
||||
)}
|
||||
{props.uploadGoogle.accountType === 'googleGroup' && (
|
||||
<FormGroup isRequired label="Email address" fieldId="group">
|
||||
<TextInput
|
||||
value={ props.uploadGoogle.options.share_with_accounts[0] ?
|
||||
props.uploadGoogle.options.share_with_accounts[0].group || '' :
|
||||
'' }
|
||||
type="text" aria-label="Google email address" id="input-google-group"
|
||||
data-testid="input-google-group" isRequired
|
||||
onChange={ value => props.setUploadOptions(
|
||||
'google',
|
||||
Object.assign(props.uploadGoogle.options, { share_with_accounts: [{ group: value }]})
|
||||
) } />
|
||||
</FormGroup>
|
||||
)}
|
||||
{props.uploadGoogle.accountType === 'domain' && (
|
||||
<FormGroup isRequired label="Domain" fieldId="domain">
|
||||
<TextInput
|
||||
value={ props.uploadGoogle.options.share_with_accounts[0] ?
|
||||
props.uploadGoogle.options.share_with_accounts[0].domain || '' :
|
||||
'' }
|
||||
type="text" aria-label="Google domain" id="input-google-domain"
|
||||
data-testid="input-google-domain" isRequired
|
||||
onChange={ value => props.setUploadOptions(
|
||||
'google',
|
||||
Object.assign(props.uploadGoogle.options, { share_with_accounts: [{ domain: value }]})
|
||||
) } />
|
||||
</FormGroup>
|
||||
)}
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
WizardStepUploadGoogle.propTypes = {
|
||||
setUploadOptions: PropTypes.func,
|
||||
setGoogleAccountType: PropTypes.func,
|
||||
uploadGoogle: PropTypes.object,
|
||||
errors: PropTypes.object,
|
||||
};
|
||||
|
||||
export default WizardStepUploadGoogle;
|
||||
|
|
@ -4,17 +4,17 @@ import { withRouter } from 'react-router-dom';
|
|||
import { connect } from 'react-redux';
|
||||
import { actions } from '../../store/actions';
|
||||
|
||||
import { Wizard, TextContent } from '@patternfly/react-core';
|
||||
import { Wizard } from '@patternfly/react-core';
|
||||
import { addNotification } from '@redhat-cloud-services/frontend-components-notifications/redux';
|
||||
|
||||
import WizardStepImageOutput from '../../PresentationalComponents/CreateImageWizard/WizardStepImageOutput';
|
||||
import WizardStepUploadAWS from '../../PresentationalComponents/CreateImageWizard/WizardStepUploadAWS';
|
||||
import WizardStepUploadAzure from '../../PresentationalComponents/CreateImageWizard/WizardStepUploadAzure';
|
||||
import WizardStepPackages from '../../PresentationalComponents/CreateImageWizard/WizardStepPackages';
|
||||
import WizardStepUploadGoogle from '../../PresentationalComponents/CreateImageWizard/WizardStepUploadGoogle';
|
||||
import WizardStepRegistration from '../../PresentationalComponents/CreateImageWizard/WizardStepRegistration';
|
||||
import WizardStepReview from '../../PresentationalComponents/CreateImageWizard/WizardStepReview';
|
||||
import ImageWizardFooter from '../../PresentationalComponents/CreateImageWizard/ImageWizardFooter';
|
||||
import WizardStepImageOutput from './WizardStepImageOutput';
|
||||
import WizardStepUploadAWS from './WizardStepUploadAWS';
|
||||
import WizardStepUploadAzure from './WizardStepUploadAzure';
|
||||
import WizardStepPackages from './WizardStepPackages';
|
||||
import WizardStepUploadGoogle from './WizardStepUploadGoogle';
|
||||
import WizardStepRegistration from './WizardStepRegistration';
|
||||
import WizardStepReview from './WizardStepReview';
|
||||
import ImageWizardFooter from './ImageWizardFooter';
|
||||
|
||||
import api from './../../api.js';
|
||||
import './CreateImageWizard.scss';
|
||||
|
|
@ -23,17 +23,6 @@ class CreateImageWizard extends Component {
|
|||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.handlePackagesSearch = this.handlePackagesSearch.bind(this);
|
||||
this.handlePackagesFilter = this.handlePackagesFilter.bind(this);
|
||||
this.packageListChange = this.packageListChange.bind(this);
|
||||
this.mapPackagesToComponent = this.mapPackagesToComponent.bind(this);
|
||||
this.setRelease = this.setRelease.bind(this);
|
||||
this.setUploadOptions = this.setUploadOptions.bind(this);
|
||||
this.setSubscription = this.setSubscription.bind(this);
|
||||
this.setSubscribeNow = this.setSubscribeNow.bind(this);
|
||||
this.setPackagesSearchName = this.setPackagesSearchName.bind(this);
|
||||
this.setGoogleAccountType = this.setGoogleAccountType.bind(this);
|
||||
this.toggleUploadDestination = this.toggleUploadDestination.bind(this);
|
||||
this.onStep = this.onStep.bind(this);
|
||||
this.onSave = this.onSave.bind(this);
|
||||
this.onClose = this.onClose.bind(this);
|
||||
|
|
@ -41,52 +30,10 @@ class CreateImageWizard extends Component {
|
|||
this.validateUploadAmazon = this.validateUploadAmazon.bind(this);
|
||||
|
||||
this.state = {
|
||||
arch: 'x86_64',
|
||||
imageType: 'qcow2',
|
||||
release: 'rhel-8',
|
||||
uploadAWS: {
|
||||
type: 'aws',
|
||||
options: {
|
||||
share_with_accounts: []
|
||||
}
|
||||
},
|
||||
uploadAzure: {
|
||||
type: 'azure',
|
||||
options: {
|
||||
tenant_id: null,
|
||||
subscription_id: null,
|
||||
resource_group: null,
|
||||
}
|
||||
},
|
||||
uploadGoogle: {
|
||||
type: 'gcp',
|
||||
accountType: 'googleAccount',
|
||||
options: {
|
||||
share_with_accounts: []
|
||||
}
|
||||
},
|
||||
uploadDestinations: {
|
||||
aws: false,
|
||||
azure: false,
|
||||
google: false
|
||||
},
|
||||
subscription: {
|
||||
organization: null,
|
||||
'activation-key': null,
|
||||
'server-url': 'subscription.rhsm.redhat.com',
|
||||
'base-url': 'https://cdn.redhat.com/',
|
||||
insights: true
|
||||
},
|
||||
subscribeNow: false,
|
||||
/* errors take form of $fieldId: error */
|
||||
uploadAWSErrors: {},
|
||||
uploadAzureErrors: {},
|
||||
uploadGoogleErrors: {},
|
||||
packagesAvailableComponents: [],
|
||||
packagesSelectedComponents: [],
|
||||
packagesFilteredComponents: [],
|
||||
packagesSelectedNames: [],
|
||||
packagesSearchName: '',
|
||||
isSaveInProgress: false,
|
||||
isValidSubscription: true,
|
||||
onSaveError: null,
|
||||
|
|
@ -110,7 +57,7 @@ class CreateImageWizard extends Component {
|
|||
|
||||
validate() {
|
||||
/* upload */
|
||||
Object.keys(this.state.uploadDestinations).forEach(provider => {
|
||||
Object.keys(this.props.uploadDestinations).forEach(provider => {
|
||||
switch (provider) {
|
||||
case 'aws':
|
||||
this.validateUploadAmazon();
|
||||
|
|
@ -124,11 +71,17 @@ class CreateImageWizard extends Component {
|
|||
break;
|
||||
}
|
||||
});
|
||||
/* subscription */
|
||||
if (this.props.subscribeNow) {
|
||||
this.setState({ isValidSubscription: this.props.subscription.activationKey ? true : false });
|
||||
} else {
|
||||
this.setState({ isValidSubscription: true });
|
||||
}
|
||||
}
|
||||
|
||||
validateUploadAmazon() {
|
||||
let uploadAWSErrors = {};
|
||||
let share = this.state.uploadAWS.options.share_with_accounts;
|
||||
let share = this.props.uploadAWS.shareWithAccounts;
|
||||
if (share.length === 0 || share[0].length !== 12 || isNaN(share[0])) {
|
||||
uploadAWSErrors['aws-account-id'] =
|
||||
{ label: 'AWS account ID', value: 'A 12-digit number is required' };
|
||||
|
|
@ -140,19 +93,19 @@ class CreateImageWizard extends Component {
|
|||
validateUploadAzure() {
|
||||
let uploadAzureErrors = {};
|
||||
|
||||
let tenant_id = this.state.uploadAzure.options.tenant_id;
|
||||
let tenant_id = this.props.uploadAzure.tenantId;
|
||||
if (tenant_id === null || tenant_id === '') {
|
||||
uploadAzureErrors['azure-resource-group'] =
|
||||
{ label: 'Azure tenant ID', value: 'A tenant ID is required' };
|
||||
}
|
||||
|
||||
let subscriptionId = this.state.uploadAzure.options.subscription_id;
|
||||
let subscriptionId = this.props.uploadAzure.subscriptionId;
|
||||
if (subscriptionId === null || subscriptionId === '') {
|
||||
uploadAzureErrors['azure-subscription-id'] =
|
||||
{ label: 'Azure subscription ID', value: 'A subscription ID is required' };
|
||||
}
|
||||
|
||||
let resource_group = this.state.uploadAzure.options.resource_group;
|
||||
let resource_group = this.props.uploadAzure.resourceGroup;
|
||||
if (resource_group === null || resource_group === '') {
|
||||
uploadAzureErrors['azure-resource-group'] =
|
||||
{ label: 'Azure resource group', value: 'A resource group is required' };
|
||||
|
|
@ -160,172 +113,66 @@ class CreateImageWizard extends Component {
|
|||
// TODO check oauth2 thing too here?
|
||||
}
|
||||
|
||||
setRelease(release) {
|
||||
this.setState({ release });
|
||||
}
|
||||
|
||||
toggleUploadDestination(provider) {
|
||||
this.setState(prevState => ({
|
||||
...prevState,
|
||||
uploadDestinations: {
|
||||
...prevState.uploadDestinations,
|
||||
[provider]: !prevState.uploadDestinations[provider]
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
setUploadOptions(provider, uploadOptions) {
|
||||
switch (provider) {
|
||||
case 'aws':
|
||||
this.setState({
|
||||
uploadAWS: {
|
||||
type: provider,
|
||||
options: uploadOptions
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'azure':
|
||||
this.setState({
|
||||
uploadAzure: {
|
||||
type: provider,
|
||||
options: uploadOptions
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'google':
|
||||
this.setState({
|
||||
uploadGoogle: {
|
||||
...this.state.uploadGoogle,
|
||||
options: uploadOptions
|
||||
}
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
setGoogleAccountType(_, event) {
|
||||
this.setState({
|
||||
uploadGoogle: {
|
||||
...this.state.uploadGoogle,
|
||||
accountType: event.target.value
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setSubscribeNow(subscribeNow) {
|
||||
// if subscribe now the subscription will be invalid, else the subscription is valid since none is required
|
||||
this.setState({
|
||||
subscription: {
|
||||
...this.state.subscription,
|
||||
'activation-key': null
|
||||
},
|
||||
subscribeNow,
|
||||
isValidSubscription: !subscribeNow
|
||||
});
|
||||
}
|
||||
|
||||
setSubscription(subscription) {
|
||||
if (subscription['activation-key']) {
|
||||
this.setState({ subscription, isValidSubscription: true });
|
||||
} else {
|
||||
this.setState({ subscription, isValidSubscription: false });
|
||||
}
|
||||
}
|
||||
|
||||
setPackagesSearchName(packagesSearchName) {
|
||||
this.setState({ packagesSearchName });
|
||||
}
|
||||
|
||||
mapPackagesToComponent(packages) {
|
||||
return packages.map((pack) =>
|
||||
<TextContent key={ pack }>
|
||||
<span className="pf-c-dual-list-selector__item-text">{ pack.name }</span>
|
||||
<small>{ pack.summary }</small>
|
||||
</TextContent>
|
||||
);
|
||||
}
|
||||
|
||||
// this digs into the component properties to extract the package name
|
||||
mapComponentToPackageName(component) {
|
||||
return component.props.children[0].props.children;
|
||||
}
|
||||
|
||||
handlePackagesSearch() {
|
||||
api.getPackages(this.state.release, this.state.arch, this.state.packagesSearchName).then(response => {
|
||||
const packageComponents = this.mapPackagesToComponent(response.data);
|
||||
this.setState({
|
||||
packagesAvailableComponents: packageComponents
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
handlePackagesFilter(filter) {
|
||||
const filteredPackages = this.state.packagesSelectedComponents.filter(component => {
|
||||
const name = this.mapComponentToPackageName(component);
|
||||
return name.includes(filter);
|
||||
});
|
||||
this.setState({
|
||||
packagesFilteredComponents: filteredPackages
|
||||
});
|
||||
}
|
||||
|
||||
packageListChange(newAvailablePackages, newChosenPackages) {
|
||||
const chosenNames = newChosenPackages.map(component => this.mapComponentToPackageName(component));
|
||||
this.setState({
|
||||
packagesAvailableComponents: newAvailablePackages,
|
||||
packagesSelectedComponents: newChosenPackages,
|
||||
packagesFilteredComponents: newChosenPackages,
|
||||
packagesSelectedNames: chosenNames
|
||||
});
|
||||
}
|
||||
|
||||
onSave() {
|
||||
this.setState({
|
||||
isSaveInProgress: true,
|
||||
});
|
||||
|
||||
let customizations = {
|
||||
packages: this.props.selectedPackages,
|
||||
};
|
||||
if (this.props.subscribeNow) {
|
||||
customizations.subscription = {
|
||||
'activation-key': this.props.subscription.activationKey,
|
||||
insights: this.props.subscription.insights,
|
||||
organization: this.props.subscription.organization,
|
||||
'server-url': 'subscription.rhsm.redhat.com',
|
||||
'base-url': 'https://cdn.redhat.com/',
|
||||
};
|
||||
}
|
||||
|
||||
let requests = [];
|
||||
if (this.state.uploadDestinations.aws) {
|
||||
if (this.props.uploadDestinations.aws) {
|
||||
let request = {
|
||||
distribution: this.state.release,
|
||||
distribution: this.props.release.distro,
|
||||
image_requests: [
|
||||
{
|
||||
architecture: this.state.arch,
|
||||
architecture: this.props.release.arch,
|
||||
image_type: 'ami',
|
||||
upload_request: this.state.uploadAWS,
|
||||
upload_request: {
|
||||
type: 'aws',
|
||||
options: {
|
||||
share_with_accounts: this.props.uploadAWS.shareWithAccounts,
|
||||
},
|
||||
},
|
||||
}],
|
||||
customizations: {
|
||||
subscription: this.state.subscription,
|
||||
packages: this.state.packagesSelectedNames,
|
||||
},
|
||||
customizations,
|
||||
};
|
||||
requests.push(request);
|
||||
}
|
||||
|
||||
if (this.state.uploadDestinations.google) {
|
||||
if (this.props.uploadDestinations.google) {
|
||||
let share = '';
|
||||
switch (this.state.uploadGoogle.accountType) {
|
||||
switch (this.props.uploadGoogle.accountType) {
|
||||
case 'googleAccount':
|
||||
share = 'user:' + this.state.uploadGoogle.options.share_with_accounts[0].user;
|
||||
share = 'user:' + this.props.uploadGoogle.shareWithAccounts[0].user;
|
||||
break;
|
||||
case 'serviceAccount':
|
||||
share = 'serviceAccount:' + this.state.uploadGoogle.options.share_with_accounts[0].serviceAccount;
|
||||
share = 'serviceAccount:' + this.props.uploadGoogle.shareWithAccounts[0].serviceAccount;
|
||||
break;
|
||||
case 'googleGroup':
|
||||
share = 'group:' + this.state.uploadGoogle.options.share_with_accounts[0].group;
|
||||
share = 'group:' + this.props.uploadGoogle.shareWithAccounts[0].group;
|
||||
break;
|
||||
case 'domain':
|
||||
share = 'domain:' + this.state.uploadGoogle.options.share_with_accounts[0].domain;
|
||||
share = 'domain:' + this.props.uploadGoogle.shareWithAccounts[0].domain;
|
||||
break;
|
||||
}
|
||||
|
||||
let request = {
|
||||
distribution: this.state.release,
|
||||
distribution: this.props.release.distro,
|
||||
image_requests: [
|
||||
{
|
||||
architecture: this.state.arch,
|
||||
architecture: this.props.release.arch,
|
||||
image_type: 'vhd',
|
||||
upload_request: {
|
||||
type: 'gcp',
|
||||
|
|
@ -334,32 +181,29 @@ class CreateImageWizard extends Component {
|
|||
},
|
||||
},
|
||||
}],
|
||||
customizations: {
|
||||
subscription: this.state.subscription,
|
||||
},
|
||||
customizations,
|
||||
};
|
||||
|
||||
requests.push(request);
|
||||
}
|
||||
|
||||
if (this.state.uploadDestinations.azure) {
|
||||
if (this.props.uploadDestinations.azure) {
|
||||
let request = {
|
||||
distribution: this.state.release,
|
||||
distribution: this.props.release.distro,
|
||||
image_requests: [
|
||||
{
|
||||
architecture: this.state.arch,
|
||||
architecture: this.props.release.arch,
|
||||
image_type: 'vhd',
|
||||
upload_request: {
|
||||
type: 'azure',
|
||||
options: {
|
||||
tenant_id: this.state.uploadAzure.options.tenant_id,
|
||||
subscription_id: this.state.uploadAzure.options.subscription_id,
|
||||
resource_group: this.state.uploadAzure.options.resource_group,
|
||||
tenant_id: this.props.uploadAzure.tenantId,
|
||||
subscription_id: this.props.uploadAzure.subscriptionId,
|
||||
resource_group: this.props.uploadAzure.resourceGroup,
|
||||
},
|
||||
},
|
||||
}],
|
||||
customizations: {
|
||||
subscription: this.state.subscription,
|
||||
},
|
||||
customizations,
|
||||
};
|
||||
requests.push(request);
|
||||
|
||||
|
|
@ -392,6 +236,7 @@ class CreateImageWizard extends Component {
|
|||
this.props.history.push('/landing');
|
||||
})
|
||||
.catch(err => {
|
||||
console.log('ERR', err);
|
||||
this.setState({ isSaveInProgress: false });
|
||||
if (err.response.status === 500) {
|
||||
this.setState({ onSaveError: 'Error: Something went wrong serverside' });
|
||||
|
|
@ -404,54 +249,43 @@ class CreateImageWizard extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const isValidUploadDestination = this.state.uploadDestinations.aws ||
|
||||
this.state.uploadDestinations.azure ||
|
||||
this.state.uploadDestinations.google;
|
||||
const isValidUploadDestination = this.props.uploadDestinations.aws ||
|
||||
this.props.uploadDestinations.azure ||
|
||||
this.props.uploadDestinations.google;
|
||||
|
||||
const StepImageOutput = {
|
||||
name: 'Image output',
|
||||
component: <WizardStepImageOutput
|
||||
value={ this.state.release }
|
||||
setRelease={ this.setRelease }
|
||||
toggleUploadDestination={ this.toggleUploadDestination }
|
||||
uploadDestinations={ this.state.uploadDestinations } />
|
||||
component: <WizardStepImageOutput />
|
||||
};
|
||||
|
||||
const StepUploadAWS = {
|
||||
name: 'Amazon Web Services',
|
||||
component: <WizardStepUploadAWS
|
||||
uploadAWS={ this.state.uploadAWS }
|
||||
setUploadOptions={ this.setUploadOptions }
|
||||
errors={ this.state.uploadAWSErrors } />
|
||||
};
|
||||
|
||||
const StepUploadAzure = {
|
||||
name: 'Microsoft Azure',
|
||||
component: <WizardStepUploadAzure
|
||||
uploadAzure={ this.state.uploadAzure }
|
||||
setUploadOptions={ this.setUploadOptions }
|
||||
errors={ this.state.uploadAzureErrors } />
|
||||
};
|
||||
|
||||
const StepUploadGoogle = {
|
||||
name: 'Google Cloud Platform',
|
||||
component: <WizardStepUploadGoogle
|
||||
uploadGoogle={ this.state.uploadGoogle }
|
||||
setGoogleAccountType={ this.setGoogleAccountType }
|
||||
setUploadOptions={ this.setUploadOptions }
|
||||
errors={ this.state.uploadGoogleErrors } />
|
||||
};
|
||||
|
||||
const uploadDestinationSteps = [];
|
||||
if (this.state.uploadDestinations.aws) {
|
||||
if (this.props.uploadDestinations.aws) {
|
||||
uploadDestinationSteps.push(StepUploadAWS);
|
||||
}
|
||||
|
||||
if (this.state.uploadDestinations.azure) {
|
||||
if (this.props.uploadDestinations.azure) {
|
||||
uploadDestinationSteps.push(StepUploadAzure);
|
||||
}
|
||||
|
||||
if (this.state.uploadDestinations.google) {
|
||||
if (this.props.uploadDestinations.google) {
|
||||
uploadDestinationSteps.push(StepUploadGoogle);
|
||||
}
|
||||
|
||||
|
|
@ -466,30 +300,13 @@ class CreateImageWizard extends Component {
|
|||
{
|
||||
name: 'Registration',
|
||||
component: <WizardStepRegistration
|
||||
subscription={ this.state.subscription }
|
||||
subscribeNow={ this.state.subscribeNow }
|
||||
setSubscription={ this.setSubscription }
|
||||
setSubscribeNow={ this.setSubscribeNow }
|
||||
isValidSubscription={ this.state.isValidSubscription } /> },
|
||||
{
|
||||
name: 'Packages',
|
||||
component: <WizardStepPackages
|
||||
packageListChange={ this.packageListChange }
|
||||
release={ this.state.release }
|
||||
packagesAvailableComponents={ this.state.packagesAvailableComponents }
|
||||
packagesFilteredComponents={ this.state.packagesFilteredComponents }
|
||||
handlePackagesSearch={ this.handlePackagesSearch }
|
||||
handlePackagesFilter= { this.handlePackagesFilter }
|
||||
setPackagesSearchName={ this.setPackagesSearchName } /> },
|
||||
component: <WizardStepPackages /> },
|
||||
{
|
||||
name: 'Review',
|
||||
component: <WizardStepReview
|
||||
release={ this.state.release }
|
||||
uploadAWS={ this.state.uploadAWS }
|
||||
uploadGoogle={ this.state.uploadGoogle }
|
||||
uploadDestinations={ this.state.uploadDestinations }
|
||||
subscription={ this.state.subscription }
|
||||
subscribeNow={ this.state.subscribeNow }
|
||||
uploadAWSErrors={ this.state.uploadAWSErrors }
|
||||
isValidSubscription={ this.state.isValidSubscription } />,
|
||||
nextButtonText: 'Create',
|
||||
|
|
@ -516,6 +333,19 @@ class CreateImageWizard extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
release: state.pendingCompose.release,
|
||||
uploadDestinations: state.pendingCompose.uploadDestinations,
|
||||
uploadAWS: state.pendingCompose.uploadAWS,
|
||||
uploadAzure: state.pendingCompose.uploadAzure,
|
||||
uploadGoogle: state.pendingCompose.uploadGoogle,
|
||||
selectedPackages: state.pendingCompose.selectedPackages,
|
||||
subscription: state.pendingCompose.subscription,
|
||||
subscribeNow: state.pendingCompose.subscribeNow,
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
updateCompose: (compose) => dispatch(actions.updateCompose(compose)),
|
||||
|
|
@ -527,6 +357,14 @@ CreateImageWizard.propTypes = {
|
|||
updateCompose: PropTypes.func,
|
||||
addNotification: PropTypes.func,
|
||||
history: PropTypes.object,
|
||||
release: PropTypes.object,
|
||||
uploadDestinations: PropTypes.object,
|
||||
uploadAWS: PropTypes.object,
|
||||
uploadAzure: PropTypes.object,
|
||||
uploadGoogle: PropTypes.object,
|
||||
subscription: PropTypes.object,
|
||||
subscribeNow: PropTypes.bool,
|
||||
selectedPackages: PropTypes.array,
|
||||
};
|
||||
|
||||
export default connect(null, mapDispatchToProps)(withRouter(CreateImageWizard));
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(withRouter(CreateImageWizard));
|
||||
|
|
|
|||
109
src/SmartComponents/CreateImageWizard/WizardStepImageOutput.js
Normal file
109
src/SmartComponents/CreateImageWizard/WizardStepImageOutput.js
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { actions } from '../../store/actions';
|
||||
|
||||
import { Form, FormGroup, FormSelect, FormSelectOption, Tile, Title } from '@patternfly/react-core';
|
||||
|
||||
import './WizardStepImageOutput.scss';
|
||||
|
||||
class WizardStepImageOutput extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.setDistro = this.setDistro.bind(this);
|
||||
this.toggleUploadDestination = this.toggleUploadDestination.bind(this);
|
||||
}
|
||||
|
||||
setDistro(distro) {
|
||||
this.props.setRelease({ arch: 'x86_64', distro });
|
||||
}
|
||||
|
||||
toggleUploadDestination(provider) {
|
||||
this.props.setUploadDestinations({
|
||||
...this.props.uploadDestinations,
|
||||
[provider]: !this.props.uploadDestinations[provider]
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const releaseOptions = [
|
||||
{ value: 'rhel-8', label: 'Red Hat Enterprise Linux (RHEL) 8.3' },
|
||||
{ value: 'centos-8', label: 'CentOS Stream 8' },
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form>
|
||||
<Title headingLevel="h2" size="xl">Image output</Title>
|
||||
<FormGroup isRequired label="Release" fieldId="release-select">
|
||||
<FormSelect value={ this.props.release.distro } onChange={ value => this.setDistro(value) } isRequired
|
||||
aria-label="Select release input" id="release-select" data-testid="release-select">
|
||||
{ releaseOptions.map(option => <FormSelectOption key={ option.value } value={ option.value } label={ option.label } />) }
|
||||
</FormSelect>
|
||||
</FormGroup>
|
||||
<FormGroup isRequired label="Select target environment" data-testid="target-select">
|
||||
<div className="tiles">
|
||||
<Tile
|
||||
className="tile pf-u-mr-sm"
|
||||
data-testid="upload-aws"
|
||||
title="Amazon Web Services"
|
||||
icon={ <img
|
||||
className='provider-icon'
|
||||
src={ '/apps/frontend-assets/partners-icons/aws.svg' } /> }
|
||||
onClick={ () => this.toggleUploadDestination('aws') }
|
||||
isSelected={ this.props.uploadDestinations.aws }
|
||||
isStacked
|
||||
isDisplayLarge />
|
||||
<Tile
|
||||
className="tile pf-u-mr-sm"
|
||||
data-testid="upload-azure"
|
||||
title="Microsoft Azure"
|
||||
icon={ <img
|
||||
className='provider-icon'
|
||||
src={ '/apps/frontend-assets/partners-icons/microsoft-azure-short.svg' } /> }
|
||||
onClick={ () => this.toggleUploadDestination('azure') }
|
||||
isSelected={ this.props.uploadDestinations.azure }
|
||||
isStacked
|
||||
isDisplayLarge />
|
||||
<Tile
|
||||
className="tile"
|
||||
data-testid="upload-google"
|
||||
title="Google Cloud Platform"
|
||||
icon={ <img
|
||||
className='provider-icon'
|
||||
src={ '/apps/frontend-assets/partners-icons/google-cloud-short.svg' } /> }
|
||||
onClick={ () => this.toggleUploadDestination('google') }
|
||||
isSelected={ this.props.uploadDestinations.google }
|
||||
isStacked
|
||||
isDisplayLarge />
|
||||
</div>
|
||||
</FormGroup>
|
||||
</Form>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
release: state.pendingCompose.release,
|
||||
uploadDestinations: state.pendingCompose.uploadDestinations,
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
setRelease: i => dispatch(actions.setRelease(i)),
|
||||
setUploadDestinations: d => dispatch(actions.setUploadDestinations(d)),
|
||||
};
|
||||
}
|
||||
|
||||
WizardStepImageOutput.propTypes = {
|
||||
setRelease: PropTypes.func,
|
||||
setUploadDestinations: PropTypes.func,
|
||||
release: PropTypes.object,
|
||||
uploadDestinations: PropTypes.object,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(WizardStepImageOutput);
|
||||
129
src/SmartComponents/CreateImageWizard/WizardStepPackages.js
Normal file
129
src/SmartComponents/CreateImageWizard/WizardStepPackages.js
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { Button, DualListSelector, Text, TextContent, Title } from '@patternfly/react-core';
|
||||
|
||||
import { actions } from '../../store/actions';
|
||||
import api from '../../api.js';
|
||||
|
||||
class WizardStepPackages extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.setPackagesSearchName = this.setPackagesSearchName.bind(this);
|
||||
this.handlePackagesSearch = this.handlePackagesSearch.bind(this);
|
||||
this.handlePackagesFilter = this.handlePackagesFilter.bind(this);
|
||||
this.packageListChange = this.packageListChange.bind(this);
|
||||
this.mapPackagesToComponent = this.mapPackagesToComponent.bind(this);
|
||||
|
||||
this.state = {
|
||||
packagesAvailableComponents: [],
|
||||
packagesSelectedComponents: [],
|
||||
packagesFilteredComponents: [],
|
||||
packagesSelectedNames: [],
|
||||
packagesSearchName: '',
|
||||
};
|
||||
}
|
||||
|
||||
setPackagesSearchName(packagesSearchName) {
|
||||
this.setState({ packagesSearchName });
|
||||
}
|
||||
|
||||
mapPackagesToComponent(packages) {
|
||||
return packages.map((pack) =>
|
||||
<TextContent key={ pack }>
|
||||
<span className="pf-c-dual-list-selector__item-text">{ pack.name }</span>
|
||||
<small>{ pack.summary }</small>
|
||||
</TextContent>
|
||||
);
|
||||
}
|
||||
|
||||
// this digs into the component properties to extract the package name
|
||||
mapComponentToPackageName(component) {
|
||||
return component.props.children[0].props.children;
|
||||
}
|
||||
|
||||
handlePackagesSearch() {
|
||||
api.getPackages(this.props.release.distro, this.props.release.arch, this.state.packagesSearchName).then(response => {
|
||||
const packageComponents = this.mapPackagesToComponent(response.data);
|
||||
this.setState({
|
||||
packagesAvailableComponents: packageComponents
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
handlePackagesFilter(filter) {
|
||||
const filteredPackages = this.state.packagesSelectedComponents.filter(component => {
|
||||
const name = this.mapComponentToPackageName(component);
|
||||
return name.includes(filter);
|
||||
});
|
||||
this.setState({
|
||||
packagesFilteredComponents: filteredPackages
|
||||
});
|
||||
}
|
||||
|
||||
packageListChange(newAvailablePackages, newChosenPackages) {
|
||||
const chosenNames = newChosenPackages.map(component => this.mapComponentToPackageName(component));
|
||||
this.setState({
|
||||
packagesAvailableComponents: newAvailablePackages,
|
||||
packagesSelectedComponents: newChosenPackages,
|
||||
packagesFilteredComponents: newChosenPackages,
|
||||
});
|
||||
|
||||
this.props.setSelectedPackages(chosenNames);
|
||||
}
|
||||
|
||||
render() {
|
||||
const availableOptionsActions = [
|
||||
<Button
|
||||
aria-label="Search button for available packages"
|
||||
key="availableSearchButton"
|
||||
onClick={ this.handlePackagesSearch }>
|
||||
Search
|
||||
</Button>
|
||||
];
|
||||
return (
|
||||
<>
|
||||
<TextContent>
|
||||
<Title headingLevel="h2" size="xl">Additional packages</Title>
|
||||
<Text>Optionally add additional packages to your <strong>{this.props.release.distro}</strong> image</Text>
|
||||
</TextContent>
|
||||
<DualListSelector
|
||||
className="pf-u-mt-sm"
|
||||
isSearchable
|
||||
availableOptionsActions={ availableOptionsActions }
|
||||
availableOptions={ this.state.packagesAvailableComponents }
|
||||
chosenOptions={ this.state.packagesFilteredComponents }
|
||||
addSelected={ this.packageListChange }
|
||||
removeSelected={ this.packageListChange }
|
||||
addAll={ this.packageListChange }
|
||||
removeAll= { this.packageListChange }
|
||||
onAvailableOptionsSearchInputChanged={ this.setPackagesSearchName }
|
||||
onChosenOptionsSearchInputChanged={ this.handlePackagesFilter }
|
||||
filterOption={ () => true }
|
||||
id="basicSelectorWithSearch" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
release: state.pendingCompose.release,
|
||||
selectedPackages: state.pendingCompose.selectedPackages,
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
setSelectedPackages: (p) => dispatch(actions.setSelectedPackages(p)),
|
||||
};
|
||||
}
|
||||
|
||||
WizardStepPackages.propTypes = {
|
||||
release: PropTypes.object,
|
||||
selectedPackages: PropTypes.array,
|
||||
setSelectedPackages: PropTypes.func,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(WizardStepPackages);
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { Form, FormGroup, TextInput, Radio, Title } from '@patternfly/react-core';
|
||||
|
||||
import { actions } from '../../store/actions';
|
||||
|
||||
class WizardStepRegistration extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
let user = await insights.chrome.auth.getUser();
|
||||
this.props.setSubscription(Object.assign(this.props.subscription, { organization: user.identity.internal.org_id }));
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Form>
|
||||
<Title headingLevel="h2" size="xl">Registration</Title>
|
||||
<FormGroup isRequired label="Register the system">
|
||||
<Radio name="subscribe-now-radio" isChecked={ this.props.subscribeNow } id="subscribe-now-radio"
|
||||
label="Embed an activation key and register systems on first boot"
|
||||
onChange={ () => this.props.setSubscribeNow(true) }
|
||||
data-testid="register-now-radio-button" />
|
||||
<Radio name="subscribe-later-radio" isChecked={ !this.props.subscribeNow }
|
||||
label="Register the system later" id="subscribe-later-radio"
|
||||
onChange={ () => this.props.setSubscribeNow(false) }
|
||||
data-testid="register-later-radio-button" />
|
||||
</FormGroup>
|
||||
{ this.props.subscribeNow &&
|
||||
<>
|
||||
<FormGroup label="Organization ID" fieldId="subscription-organization">
|
||||
<TextInput isDisabled value={ this.props.subscription.organization || '' } type="text"
|
||||
id="subscription-organization" aria-label="Subscription organization ID"
|
||||
data-testid="organization-id" />
|
||||
</FormGroup>
|
||||
<FormGroup isRequired label="Activation key" fieldId="subscription-activation"
|
||||
helperTextInvalid={ 'A value is required' }
|
||||
validated={ !this.props.isValidSubscription && this.props.subscription.activationKey !== null ? 'error' : 'default' }>
|
||||
<TextInput
|
||||
value={ this.props.subscription.activationKey || '' }
|
||||
type="password"
|
||||
data-testid="subscription-activation"
|
||||
id="subscription-activation"
|
||||
aria-label="Subscription activation key"
|
||||
onChange={ activationKey => this.props.setSubscription(Object.assign(this.props.subscription, { activationKey })) }
|
||||
validated={ !this.props.isValidSubscription && this.props.subscription.activationKey !== null ? 'error' : 'default' }
|
||||
isRequired />
|
||||
</FormGroup>
|
||||
</> }
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
subscription: state.pendingCompose.subscription,
|
||||
subscribeNow: state.pendingCompose.subscribeNow,
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
setSubscription: s => dispatch(actions.setSubscription(s)),
|
||||
setSubscribeNow: s => dispatch(actions.setSubscribeNow(s)),
|
||||
};
|
||||
}
|
||||
|
||||
WizardStepRegistration.propTypes = {
|
||||
setSubscription: PropTypes.func,
|
||||
setSubscribeNow: PropTypes.func,
|
||||
subscription: PropTypes.object,
|
||||
subscribeNow: PropTypes.bool,
|
||||
isValidSubscription: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(WizardStepRegistration);
|
||||
155
src/SmartComponents/CreateImageWizard/WizardStepReview.js
Normal file
155
src/SmartComponents/CreateImageWizard/WizardStepReview.js
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import {
|
||||
Alert,
|
||||
Text, TextVariants, TextContent, TextList, TextListVariants, TextListItem, TextListItemVariants,
|
||||
Title
|
||||
} from '@patternfly/react-core';
|
||||
import { ExclamationCircleIcon } from '@patternfly/react-icons';
|
||||
|
||||
import './WizardStepReview.scss';
|
||||
|
||||
class WizardStepReview extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
const releaseLabels = {
|
||||
'rhel-8': 'Red Hat Enterprise Linux (RHEL) 8.3',
|
||||
'centos-8': 'CentOS Stream 8'
|
||||
};
|
||||
|
||||
const awsReview = (
|
||||
<>
|
||||
<Text id="destination-header">Amazon Web Services</Text>
|
||||
<TextList component={ TextListVariants.dl } data-testid='review-image-upload-aws'>
|
||||
<TextListItem component={ TextListItemVariants.dt }>Account ID</TextListItem>
|
||||
{this.props.uploadAWSErrors['aws-account-id'] ? (
|
||||
<TextListItem component={ TextListItemVariants.dd }>
|
||||
<ExclamationCircleIcon className="error" /> { this.props.uploadAWSErrors['aws-account-id'].value }
|
||||
</TextListItem>
|
||||
) : (
|
||||
<TextListItem component={ TextListItemVariants.dd }>{this.props.uploadAWS.shareWithAccounts[0]}</TextListItem>
|
||||
)}
|
||||
</TextList>
|
||||
</>
|
||||
);
|
||||
|
||||
const googleReview = (
|
||||
<>
|
||||
<Text id="destination-header">Google Cloud Platform</Text>
|
||||
<TextList component={ TextListVariants.dl } data-testid='review-image-upload-google'>
|
||||
{this.props.uploadGoogle.accountType === 'googleAccount' && (
|
||||
<>
|
||||
<TextListItem component={ TextListItemVariants.dt }>Google account</TextListItem>
|
||||
<TextListItem component={ TextListItemVariants.dd }>{this.props.uploadGoogle.shareWithAccounts[0] ?
|
||||
this.props.uploadGoogle.shareWithAccounts[0].user || '' :
|
||||
''}
|
||||
</TextListItem>
|
||||
</>
|
||||
)}
|
||||
{this.props.uploadGoogle.accountType === 'serviceAccount' && (
|
||||
<>
|
||||
<TextListItem component={ TextListItemVariants.dt }>Service account</TextListItem>
|
||||
<TextListItem component={ TextListItemVariants.dd }>{this.props.uploadGoogle.shareWithAccounts[0] ?
|
||||
this.props.uploadGoogle.shareWithAccounts[0].serviceAccount || '' :
|
||||
''}
|
||||
</TextListItem>
|
||||
</>
|
||||
)}
|
||||
{this.props.uploadGoogle.accountType === 'googleGroup' && (
|
||||
<>
|
||||
<TextListItem component={ TextListItemVariants.dt }>Google group</TextListItem>
|
||||
<TextListItem component={ TextListItemVariants.dd }>{this.props.uploadGoogle.shareWithAccounts[0] ?
|
||||
this.props.uploadGoogle.shareWithAccounts[0].group || '' :
|
||||
''}
|
||||
</TextListItem>
|
||||
</>
|
||||
)}
|
||||
{this.props.uploadGoogle.accountType === 'domain' && (
|
||||
<>
|
||||
<TextListItem component={ TextListItemVariants.dt }>Domain</TextListItem>
|
||||
<TextListItem component={ TextListItemVariants.dd }>{this.props.uploadGoogle.shareWithAccounts[0] ?
|
||||
this.props.uploadGoogle.shareWithAccounts[0].domain || '' :
|
||||
''}
|
||||
</TextListItem>
|
||||
</>
|
||||
)}
|
||||
</TextList>
|
||||
</>
|
||||
);
|
||||
|
||||
let subscriptionReview = <TextListItem component={ TextListItemVariants.dd }>Register the system later</TextListItem>;
|
||||
if (this.props.subscribeNow) {
|
||||
subscriptionReview = (<>
|
||||
<TextListItem component={ TextListItemVariants.dd }>Register the system on first boot</TextListItem>
|
||||
<TextListItem component={ TextListItemVariants.dt }>Activation key</TextListItem>
|
||||
{ !this.props.isValidSubscription || !this.props.subscription.activationKey ? (
|
||||
<TextListItem component={ TextListItemVariants.dd }>
|
||||
<ExclamationCircleIcon className="error" /> { 'A value is required' }
|
||||
</TextListItem>
|
||||
) : (
|
||||
<TextListItem component={ TextListItemVariants.dd } type="password">
|
||||
{'*'.repeat(this.props.subscription.activationKey.length)}
|
||||
</TextListItem>
|
||||
)}
|
||||
</>);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{ (Object.keys(this.props.uploadAWSErrors).length > 0 ||
|
||||
!this.props.isValidSubscription) &&
|
||||
<Alert variant="danger" className="pf-u-mb-xl" isInline title="Required information is missing" /> }
|
||||
<Title headingLevel="h2" size="xl">Review</Title>
|
||||
<TextContent>
|
||||
<Text component={ TextVariants.small }>
|
||||
Review the information and click Create image
|
||||
to create the image using the following criteria.
|
||||
</Text>
|
||||
<Text component={ TextVariants.h3 }>Image output</Text>
|
||||
<TextList component={ TextListVariants.dl } data-testid='review-image-output'>
|
||||
<TextListItem component={ TextListItemVariants.dt }>Release</TextListItem>
|
||||
<TextListItem component={ TextListItemVariants.dd }>{releaseLabels[this.props.release]}</TextListItem>
|
||||
</TextList>
|
||||
<Text component={ TextVariants.h3 }>Target environment</Text>
|
||||
{this.props.uploadDestinations.aws && awsReview }
|
||||
{this.props.uploadDestinations.google && googleReview }
|
||||
<Text component={ TextVariants.h3 }>Registration</Text>
|
||||
<TextList component={ TextListVariants.dl } data-testid='review-image-registration'>
|
||||
<TextListItem component={ TextListItemVariants.dt }>Subscription</TextListItem>
|
||||
{ subscriptionReview }
|
||||
</TextList>
|
||||
</TextContent>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
uploadDestinations: state.pendingCompose.uploadDestinations,
|
||||
uploadAWS: state.pendingCompose.uploadAWS,
|
||||
uploadAzure: state.pendingCompose.uploadAzure,
|
||||
uploadGoogle: state.pendingCompose.uploadGoogle,
|
||||
subscribeNow: state.pendingCompose.subscribeNow,
|
||||
subscription: state.pendingCompose.subscription,
|
||||
};
|
||||
}
|
||||
|
||||
WizardStepReview.propTypes = {
|
||||
release: PropTypes.string,
|
||||
uploadAWS: PropTypes.object,
|
||||
uploadGoogle: PropTypes.object,
|
||||
uploadDestinations: PropTypes.object,
|
||||
uploadAzure: PropTypes.object,
|
||||
subscription: PropTypes.object,
|
||||
subscribeNow: PropTypes.bool,
|
||||
uploadAWSErrors: PropTypes.object,
|
||||
isValidSubscription: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, null)(WizardStepReview);
|
||||
54
src/SmartComponents/CreateImageWizard/WizardStepUploadAWS.js
Normal file
54
src/SmartComponents/CreateImageWizard/WizardStepUploadAWS.js
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { actions } from '../../store/actions';
|
||||
|
||||
import { Form, FormGroup, TextInput, Title } from '@patternfly/react-core';
|
||||
|
||||
class WizardStepUploadAWS extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Form>
|
||||
<Title headingLevel="h2" size="xl">Target Environment - Upload to AWS</Title>
|
||||
<p>
|
||||
Your image will be uploaded to a temporary account on Amazon Web Services. <br />
|
||||
The image will be shared with the account you provide below. <br />
|
||||
Within the next 14 days you will need to copy the shared image to your own account.
|
||||
After 14 days it will be unavailable and will have to be regenerated.
|
||||
</p>
|
||||
<FormGroup isRequired label="AWS account ID" fieldId="aws-account-id"
|
||||
helperTextInvalid={ (this.props.errors['aws-account-id'] && this.props.errors['aws-account-id'].value) || '' }
|
||||
validated={ (this.props.errors['aws-account-id'] && 'error') || 'default' }>
|
||||
<TextInput value={ this.props.uploadAWS.shareWithAccounts || '' }
|
||||
type="text" aria-label="AWS account ID" id="aws-account-id"
|
||||
data-testid="aws-account-id" isRequired
|
||||
onChange={ value => this.props.setUploadAWS({ shareWithAccounts: [ value ]}) } />
|
||||
</FormGroup>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
uploadAWS: state.pendingCompose.uploadAWS,
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
setUploadAWS: u => dispatch(actions.setUploadAWS(u)),
|
||||
};
|
||||
}
|
||||
|
||||
WizardStepUploadAWS.propTypes = {
|
||||
setUploadAWS: PropTypes.func,
|
||||
uploadAWS: PropTypes.object,
|
||||
errors: PropTypes.object,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(WizardStepUploadAWS);
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { actions } from '../../store/actions';
|
||||
|
||||
import { Form, FormGroup, Text, TextContent, TextInput, Title } from '@patternfly/react-core';
|
||||
import { ExternalLinkAltIcon } from '@patternfly/react-icons';
|
||||
|
||||
import './WizardStepUploadAzure.scss';
|
||||
|
||||
class WizardStepUploadAzure extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<TextContent className="textcontent-azure">
|
||||
<Title headingLevel="h2">Target Environment - Upload to Azure</Title>
|
||||
<Text>
|
||||
Image Builder will send an image to an authorized Azure account.
|
||||
</Text>
|
||||
<Title headingLevel="h3">OAuth permissions</Title>
|
||||
<Text>
|
||||
In order to use Image Builder to push images to Azure, Image Builder must
|
||||
be configured as an authorized application, and given the role of "Contributor" to at least one resource group.<br />
|
||||
Image Builder must be authorized by an account owner.<br />
|
||||
<a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow">
|
||||
<small>Learn more</small></a>
|
||||
</Text>
|
||||
|
||||
<a href="https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=b94bb246-b02c-4985-9c22-d44e66f657f4
|
||||
&scope=openid&response_type=code&response_mode=form_post
|
||||
&redirect_uri=https%3A%2F%2Flogin.microsoftonline.com%2Fcommon%2Foauth2%2Fnativeclient" target="_blank" rel="noopener noreferrer">
|
||||
Authorize Image Builder on Azure <ExternalLinkAltIcon />
|
||||
</a>
|
||||
</TextContent>
|
||||
|
||||
<Title headingLevel="h3">Destination</Title>
|
||||
<Text>
|
||||
Your image will be uploaded to the resource group in the subscription you specify.
|
||||
</Text>
|
||||
<Form>
|
||||
<FormGroup isRequired label="Tenant ID" fieldId="azure-tenant-id"
|
||||
helperTextInvalid={ (this.props.errors['azure-tenant-id'] && this.props.errors['azure-tenant-id'].value) || '' }
|
||||
validated={ (this.props.errors['azure-tenant-id'] && 'error') || 'default' }>
|
||||
<TextInput value={ this.props.uploadAzure.tenantId || '' }
|
||||
type="text" aria-label="Azure tenant-id" id="azure-tenant-id"
|
||||
data-testid="azure-tenant-id" isRequired
|
||||
onChange={ value =>
|
||||
this.props.setUploadAzure(Object.assign(this.props.uploadAzure, { tenantId: value })) } />
|
||||
</FormGroup>
|
||||
<FormGroup isRequired label="Subscription ID" fieldId="azure-subscription-id"
|
||||
helperTextInvalid={ (this.props.errors['azure-subscription-id'] &&
|
||||
this.props.errors['azure-subscription-id'].value) || '' }
|
||||
validated={ (this.props.errors['azure-subscription-id'] && 'error') || 'default' }>
|
||||
<TextInput value={ this.props.uploadAzure.subscriptionId || '' }
|
||||
type="text" aria-label="Azure subscription-id" id="azure-subscription-id"
|
||||
data-testid="azure-subscription-id" isRequired
|
||||
onChange={ value =>
|
||||
this.props.setUploadAzure(Object.assign(this.props.uploadAzure, { subscriptionId: value })) } />
|
||||
</FormGroup>
|
||||
<FormGroup isRequired label="Resource group" fieldId="azure-resource-group"
|
||||
helperTextInvalid={ (this.props.errors['azure-resource-group'] &&
|
||||
this.props.errors['azure-resource-group'].value) || '' }
|
||||
validated={ (this.props.errors['azure-resource-group'] && 'error') || 'default' }>
|
||||
<TextInput value={ this.props.uploadAzure.resourceGroup || '' }
|
||||
type="text" aria-label="Azure resource group" id="azure-resource-group"
|
||||
data-testid="azure-resource-group" isRequired
|
||||
onChange={ value =>
|
||||
this.props.setUploadAzure(Object.assign(this.props.uploadAzure, { resourceGroup: value })) } />
|
||||
</FormGroup>
|
||||
</Form>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
uploadAzure: state.pendingCompose.uploadAzure,
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
setUploadAzure: u => dispatch(actions.setUploadAzure(u)),
|
||||
};
|
||||
}
|
||||
|
||||
WizardStepUploadAzure.propTypes = {
|
||||
setUploadAzure: PropTypes.func,
|
||||
uploadAzure: PropTypes.object,
|
||||
errors: PropTypes.object,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(WizardStepUploadAzure);
|
||||
162
src/SmartComponents/CreateImageWizard/WizardStepUploadGoogle.js
Normal file
162
src/SmartComponents/CreateImageWizard/WizardStepUploadGoogle.js
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { actions } from '../../store/actions';
|
||||
|
||||
import { Form, FormGroup, TextList, TextListItem, Popover, Radio, TextContent, Text, TextInput, Title } from '@patternfly/react-core';
|
||||
import { HelpIcon } from '@patternfly/react-icons';
|
||||
|
||||
const accountTypePopover = (
|
||||
<Popover
|
||||
hasAutoWidth
|
||||
maxWidth='35rem'
|
||||
headerContent={ 'Valid account types' }
|
||||
bodyContent={ <TextContent>
|
||||
<Text>The following account types can have an image shared with them:</Text>
|
||||
<TextList>
|
||||
<TextListItem>
|
||||
<strong>Google account:</strong> A Google account represents a developer, an administrator,
|
||||
or any other person who interacts with Google Cloud. e.g., <em>`alice@gmail.com`</em>.
|
||||
</TextListItem>
|
||||
<TextListItem>
|
||||
<strong>Service account:</strong> A service account is an account for an application instead
|
||||
of an individual end user. e.g., <em>`myapp@appspot.gserviceaccount.com`</em>.
|
||||
</TextListItem>
|
||||
<TextListItem>
|
||||
<strong>Google group:</strong> A Google group is a named collection of Google accounts and
|
||||
and service accounts. e.g., <em>`admins@example.com`</em>.
|
||||
</TextListItem>
|
||||
<TextListItem>
|
||||
<strong>Google workspace domain/Cloud identity domain:</strong> A Google workspace or cloud identity
|
||||
domain represents a virtual group of all the Google accounts in an organization. These domains
|
||||
represent your organization's internet domain name. e.g., <em>`mycompany.com`</em>.
|
||||
</TextListItem>
|
||||
</TextList>
|
||||
</TextContent> }>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Account info"
|
||||
aria-describedby="google-account-type"
|
||||
className="pf-c-form__group-label-help">
|
||||
<HelpIcon />
|
||||
</button>
|
||||
</Popover>
|
||||
);
|
||||
|
||||
class WizardStepUploadGoogle extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Form>
|
||||
<Title headingLevel="h2" size="xl">Target Environment - Google Cloud Platform</Title>
|
||||
<p>
|
||||
Your image will be uploaded to an account on Google Cloud Platform. <br />
|
||||
The image will be shared with the email you provide below. <br />
|
||||
Within the next 14 days you will need to copy the shared image to your own account.
|
||||
After 14 days it will be unavailable and will have to be regenerated.
|
||||
</p>
|
||||
<FormGroup isRequired label="Type" labelIcon={ accountTypePopover } fieldId="google-account-type">
|
||||
<Radio
|
||||
onChange={ () => this.props.setUploadGoogle({ accountType: 'googleAccount', shareWithAccounts: [{ user: null }]}) }
|
||||
isChecked={ this.props.uploadGoogle.accountType === 'googleAccount' }
|
||||
label="Google account"
|
||||
id="radio-google-account"
|
||||
value="googleAccount" />
|
||||
<Radio
|
||||
onChange={ () => this.props.setUploadGoogle({ accountType: 'serviceAccount', shareWithAccounts: [{ serviceAccount: null }]}) }
|
||||
isChecked={ this.props.uploadGoogle.accountType === 'serviceAccount' }
|
||||
label="Service account"
|
||||
id="radio-service-account"
|
||||
value="serviceAccount" />
|
||||
<Radio
|
||||
onChange={ () => this.props.setUploadGoogle({ accountType: 'googleGroup', shareWithAccounts: [{ group: null }]}) }
|
||||
isChecked={ this.props.uploadGoogle.accountType === 'googleGroup' }
|
||||
label="Google group"
|
||||
id="radio-google-group"
|
||||
value="googleGroup" />
|
||||
<Radio
|
||||
onChange={ () => this.props.setUploadGoogle({ accountType: 'domain', shareWithAccounts: [{ domain: null }]}) }
|
||||
isChecked={ this.props.uploadGoogle.accountType === 'domain' }
|
||||
label="Google Workspace Domain or Cloud Identity Domain"
|
||||
id="radio-domain"
|
||||
value="domain" />
|
||||
</FormGroup>
|
||||
{this.props.uploadGoogle.accountType === 'googleAccount' && (
|
||||
<FormGroup isRequired label="Email address" fieldId="user">
|
||||
<TextInput
|
||||
value={ this.props.uploadGoogle.shareWithAccounts[0] ?
|
||||
this.props.uploadGoogle.shareWithAccounts[0].user || '' :
|
||||
'' }
|
||||
type="text" aria-label="Google email address" id="input-google-user"
|
||||
data-testid="input-google-user" isRequired
|
||||
onChange={ value => this.props.setUploadGoogle(
|
||||
{ accountType: 'googleAccount', shareWithAccounts: [{ user: value }]}
|
||||
) } />
|
||||
</FormGroup>
|
||||
)}
|
||||
{this.props.uploadGoogle.accountType === 'serviceAccount' && (
|
||||
<FormGroup isRequired label="Email address" fieldId="service-account">
|
||||
<TextInput
|
||||
value={ this.props.uploadGoogle.shareWithAccounts[0] ?
|
||||
this.props.uploadGoogle.shareWithAccounts[0].serviceAccount || '' :
|
||||
'' }
|
||||
type="text" aria-label="Google email address" id="input-google-service-account"
|
||||
data-testid="input-google-service-account" isRequired
|
||||
onChange={ value => this.props.setUploadGoogle(
|
||||
{ accountType: 'serviceAccount', shareWithAccounts: [{ serviceAccount: value }]}
|
||||
) } />
|
||||
</FormGroup>
|
||||
)}
|
||||
{this.props.uploadGoogle.accountType === 'googleGroup' && (
|
||||
<FormGroup isRequired label="Email address" fieldId="group">
|
||||
<TextInput
|
||||
value={ this.props.uploadGoogle.shareWithAccounts[0] ?
|
||||
this.props.uploadGoogle.shareWithAccounts[0].group || '' :
|
||||
'' }
|
||||
type="text" aria-label="Google email address" id="input-google-group"
|
||||
data-testid="input-google-group" isRequired
|
||||
onChange={ value => this.props.setUploadGoogle(
|
||||
{ accountType: 'googleGroup', shareWithAccounts: [{ group: value }]}
|
||||
) } />
|
||||
</FormGroup>
|
||||
)}
|
||||
{this.props.uploadGoogle.accountType === 'domain' && (
|
||||
<FormGroup isRequired label="Domain" fieldId="domain">
|
||||
<TextInput
|
||||
value={ this.props.uploadGoogle.shareWithAccounts[0] ?
|
||||
this.props.uploadGoogle.shareWithAccounts[0].domain || '' :
|
||||
'' }
|
||||
type="text" aria-label="Google domain" id="input-google-domain"
|
||||
data-testid="input-google-domain" isRequired
|
||||
onChange={ value => this.props.setUploadGoogle(
|
||||
{ accountType: 'domain', shareWithAccounts: [{ domain: value }]}
|
||||
) } />
|
||||
</FormGroup>
|
||||
)}
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
uploadGoogle: state.pendingCompose.uploadGoogle,
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
setUploadGoogle: u => dispatch(actions.setUploadGoogle(u)),
|
||||
};
|
||||
}
|
||||
|
||||
WizardStepUploadGoogle.propTypes = {
|
||||
setUploadGoogle: PropTypes.func,
|
||||
uploadGoogle: PropTypes.object,
|
||||
errors: PropTypes.object,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(WizardStepUploadGoogle);
|
||||
|
|
@ -7,6 +7,90 @@ function updateCompose(compose) {
|
|||
};
|
||||
}
|
||||
|
||||
function setRelease({ arch, distro }) {
|
||||
return {
|
||||
type: types.SET_RELEASE,
|
||||
payload: {
|
||||
arch,
|
||||
distro,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function setUploadDestinations({ aws, azure, google }) {
|
||||
return {
|
||||
type: types.SET_UPLOAD_DESTINATIONS,
|
||||
payload: {
|
||||
aws,
|
||||
azure,
|
||||
google,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function setUploadAWS({ shareWithAccounts }) {
|
||||
return {
|
||||
type: types.SET_UPLOAD_AWS,
|
||||
payload: {
|
||||
shareWithAccounts,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function setUploadAzure({ tenantId, subscriptionId, resourceGroup }) {
|
||||
return {
|
||||
type: types.SET_UPLOAD_AZURE,
|
||||
payload: {
|
||||
tenantId,
|
||||
subscriptionId,
|
||||
resourceGroup,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function setUploadGoogle({ accountType, shareWithAccounts }) {
|
||||
return {
|
||||
type: types.SET_UPLOAD_GOOGLE,
|
||||
payload: {
|
||||
accountType,
|
||||
shareWithAccounts,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function setSelectedPackages(selectedPackages) {
|
||||
return {
|
||||
type: types.SET_SELECTED_PACKAGES,
|
||||
payload: selectedPackages
|
||||
};
|
||||
}
|
||||
|
||||
function setSubscription({ activationKey, insights, organization }) {
|
||||
return {
|
||||
type: types.SET_SUBSCRIPTION,
|
||||
payload: {
|
||||
activationKey,
|
||||
insights,
|
||||
organization,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function setSubscribeNow(subscribeNow) {
|
||||
return {
|
||||
type: types.SET_SUBSCRIBE_NOW,
|
||||
payload: subscribeNow
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
updateCompose,
|
||||
setRelease,
|
||||
setUploadDestinations,
|
||||
setUploadAWS,
|
||||
setUploadAzure,
|
||||
setUploadGoogle,
|
||||
setSelectedPackages,
|
||||
setSubscription,
|
||||
setSubscribeNow,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ import ReducerRegistry from '@redhat-cloud-services/frontend-components-utilitie
|
|||
import promiseMiddleware from 'redux-promise-middleware';
|
||||
import { notificationsReducer } from '@redhat-cloud-services/frontend-components-notifications/redux';
|
||||
|
||||
import { composeReducer } from './reducers/composes';
|
||||
import composes from './reducers/composes';
|
||||
import pendingCompose from './reducers/pendingCompose';
|
||||
|
||||
let registry;
|
||||
|
||||
|
|
@ -17,7 +18,8 @@ export function init (store = {}, ...middleware) {
|
|||
]);
|
||||
|
||||
registry.register({
|
||||
composes: composeReducer,
|
||||
composes,
|
||||
pendingCompose,
|
||||
notifications: notificationsReducer,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -3,14 +3,16 @@ import types from '../types';
|
|||
// Example of action.compose
|
||||
// {
|
||||
// "77e4c693-0497-4b85-936d-b2a3ad69571b": {
|
||||
// status: "uploading",
|
||||
// distribution: "fedora-31",
|
||||
// image_status: {
|
||||
// status: "uploading",
|
||||
// },
|
||||
// distribution: "rhel-8",
|
||||
// architecture: "x86_64",
|
||||
// image_type: "qcow2"
|
||||
// image_type: "ami"
|
||||
// }
|
||||
// };
|
||||
|
||||
export function composeReducer(state = { }, action) {
|
||||
export function composes(state = { }, action) {
|
||||
switch (action.type) {
|
||||
case types.UPDATE_COMPOSE:
|
||||
return Object.assign({}, state, action.compose);
|
||||
|
|
@ -18,3 +20,5 @@ export function composeReducer(state = { }, action) {
|
|||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export default composes;
|
||||
|
|
|
|||
81
src/store/reducers/pendingCompose.js
Normal file
81
src/store/reducers/pendingCompose.js
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
import types from '../types';
|
||||
|
||||
const initialPendingComposeState = {
|
||||
release: {
|
||||
arch: 'x86_64',
|
||||
distro: 'rhel-8',
|
||||
},
|
||||
uploadDestinations: {
|
||||
aws: false,
|
||||
azure: false,
|
||||
google: false,
|
||||
},
|
||||
uploadAWS: {
|
||||
shareWithAccounts: [],
|
||||
},
|
||||
uploadAzure: {
|
||||
tenantId: null,
|
||||
subscriptionId: null,
|
||||
resourceGroup: null,
|
||||
},
|
||||
uploadGoogle: {
|
||||
accountType: 'googleAccount',
|
||||
shareWithAccounts: [],
|
||||
},
|
||||
selectedPackages: [],
|
||||
subscription: {
|
||||
activationKey: null,
|
||||
insights: true,
|
||||
organization: null,
|
||||
},
|
||||
subscribeNow: false,
|
||||
};
|
||||
|
||||
export function pendingCompose(state = initialPendingComposeState, action) {
|
||||
switch (action.type) {
|
||||
case types.SET_RELEASE:
|
||||
return {
|
||||
...state,
|
||||
release: action.payload,
|
||||
};
|
||||
case types.SET_UPLOAD_DESTINATIONS:
|
||||
return {
|
||||
...state,
|
||||
uploadDestinations: action.payload,
|
||||
};
|
||||
case types.SET_UPLOAD_AWS:
|
||||
return {
|
||||
...state,
|
||||
uploadAWS: action.payload,
|
||||
};
|
||||
case types.SET_UPLOAD_AZURE:
|
||||
return {
|
||||
...state,
|
||||
uploadAzure: action.payload,
|
||||
};
|
||||
case types.SET_UPLOAD_GOOGLE:
|
||||
return {
|
||||
...state,
|
||||
uploadGoogle: action.payload,
|
||||
};
|
||||
case types.SET_SELECTED_PACKAGES:
|
||||
return {
|
||||
...state,
|
||||
selectedPackages: action.payload,
|
||||
};
|
||||
case types.SET_SUBSCRIPTION:
|
||||
return {
|
||||
...state,
|
||||
subscription: action.payload,
|
||||
};
|
||||
case types.SET_SUBSCRIBE_NOW:
|
||||
return {
|
||||
...state,
|
||||
subscribeNow: action.payload,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export default pendingCompose;
|
||||
|
|
@ -1,6 +1,21 @@
|
|||
const UPDATE_COMPOSE = 'UPDATE_COMPOSE';
|
||||
const SET_RELEASE = 'SET_RELEASE';
|
||||
const SET_UPLOAD_DESTINATIONS = 'SET_UPLOAD_DESTINATIONS';
|
||||
const SET_UPLOAD_AWS = 'SET_UPLOAD_AWS';
|
||||
const SET_UPLOAD_AZURE = 'SET_UPLOAD_AZURE';
|
||||
const SET_UPLOAD_GOOGLE = 'SET_UPLOAD_GOOGLE';
|
||||
const SET_SELECTED_PACKAGES = 'SET_SELECTED_PACKAGES';
|
||||
const SET_SUBSCRIPTION = 'SET_SUBSCRIPTION';
|
||||
const SET_SUBSCRIBE_NOW = 'SET_SUBSCRIBE_NOW';
|
||||
|
||||
export default {
|
||||
UPDATE_COMPOSE,
|
||||
SET_RELEASE,
|
||||
SET_UPLOAD_DESTINATIONS,
|
||||
SET_UPLOAD_AWS,
|
||||
SET_UPLOAD_AZURE,
|
||||
SET_UPLOAD_GOOGLE,
|
||||
SET_SELECTED_PACKAGES,
|
||||
SET_SUBSCRIPTION,
|
||||
SET_SUBSCRIBE_NOW,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@ describe('Step Image output', () => {
|
|||
// select aws as upload destination
|
||||
const awsTile = screen.getByTestId('upload-aws');
|
||||
awsTile.click();
|
||||
|
||||
// load from sidebar
|
||||
anchor.click();
|
||||
});
|
||||
|
|
@ -446,6 +447,7 @@ describe('Click through all steps', () => {
|
|||
// select image output
|
||||
userEvent.selectOptions(screen.getByTestId('release-select'), [ 'rhel-8' ]);
|
||||
screen.getByTestId('upload-aws').click();
|
||||
screen.getByTestId('upload-azure').click();
|
||||
screen.getByTestId('upload-google').click();
|
||||
next.click();
|
||||
|
||||
|
|
@ -453,6 +455,11 @@ describe('Click through all steps', () => {
|
|||
userEvent.type(screen.getByTestId('aws-account-id'), '012345678901');
|
||||
next.click();
|
||||
|
||||
userEvent.type(screen.getByTestId('azure-tenant-id'), 'testTenant');
|
||||
userEvent.type(screen.getByTestId('azure-subscription-id'), 'testSubscriptionId');
|
||||
userEvent.type(screen.getByTestId('azure-resource-group'), 'testResourceGroup');
|
||||
next.click();
|
||||
|
||||
userEvent.type(screen.getByTestId('input-google-user'), 'test@test.com');
|
||||
next.click();
|
||||
|
||||
|
|
@ -477,14 +484,93 @@ describe('Click through all steps', () => {
|
|||
await screen.findByText('Register the system on first boot');
|
||||
|
||||
// mock the backend API
|
||||
const composeImage = jest.spyOn(api, 'composeImage');
|
||||
composeImage.mockResolvedValue({ id: 'test-me' });
|
||||
const composeImage = jest
|
||||
.spyOn(api, 'composeImage')
|
||||
.mockImplementation(body => {
|
||||
|
||||
if (body.image_requests[0].upload_request.type === 'aws') {
|
||||
expect(body).toEqual({
|
||||
distribution: 'rhel-8',
|
||||
image_requests: [{
|
||||
architecture: 'x86_64',
|
||||
image_type: 'ami',
|
||||
upload_request: {
|
||||
type: 'aws',
|
||||
options: {
|
||||
share_with_accounts: [ '012345678901' ],
|
||||
}
|
||||
},
|
||||
}],
|
||||
customizations: {
|
||||
packages: [],
|
||||
subscription: {
|
||||
'activation-key': '1234567890',
|
||||
insights: true,
|
||||
organization: 5,
|
||||
'server-url': 'subscription.rhsm.redhat.com',
|
||||
'base-url': 'https://cdn.redhat.com/'
|
||||
},
|
||||
},
|
||||
});
|
||||
} else if (body.image_requests[0].upload_request.type === 'gcp') {
|
||||
expect(body).toEqual({
|
||||
distribution: 'rhel-8',
|
||||
image_requests: [{
|
||||
architecture: 'x86_64',
|
||||
image_type: 'vhd',
|
||||
upload_request: {
|
||||
type: 'gcp',
|
||||
options: {
|
||||
share_with_accounts: [ 'user:test@test.com' ],
|
||||
}
|
||||
},
|
||||
}],
|
||||
customizations: {
|
||||
packages: [],
|
||||
subscription: {
|
||||
'activation-key': '1234567890',
|
||||
insights: true,
|
||||
organization: 5,
|
||||
'server-url': 'subscription.rhsm.redhat.com',
|
||||
'base-url': 'https://cdn.redhat.com/'
|
||||
},
|
||||
},
|
||||
});
|
||||
} else if (body.image_requests[0].upload_request.type === 'azure') {
|
||||
expect(body).toEqual({
|
||||
distribution: 'rhel-8',
|
||||
image_requests: [{
|
||||
architecture: 'x86_64',
|
||||
image_type: 'vhd',
|
||||
upload_request: {
|
||||
type: 'azure',
|
||||
options: {
|
||||
tenant_id: 'testTenant',
|
||||
subscription_id: 'testSubscriptionId',
|
||||
resource_group: 'testResourceGroup',
|
||||
}
|
||||
},
|
||||
}],
|
||||
customizations: {
|
||||
packages: [],
|
||||
subscription: {
|
||||
'activation-key': '1234567890',
|
||||
insights: true,
|
||||
organization: 5,
|
||||
'server-url': 'subscription.rhsm.redhat.com',
|
||||
'base-url': 'https://cdn.redhat.com/'
|
||||
},
|
||||
},
|
||||
}); }
|
||||
|
||||
return Promise.resolve({ id: 'edbae1c2-62bc-42c1-ae0c-3110ab718f58' });
|
||||
});
|
||||
|
||||
const create = screen.getByRole('button', { name: /Create/ });
|
||||
create.click();
|
||||
|
||||
// API request sent to backend
|
||||
await expect(composeImage).toHaveBeenCalledTimes(2);
|
||||
await expect(composeImage).toHaveBeenCalledTimes(3);
|
||||
|
||||
// returns back to the landing page
|
||||
// but jsdom will not render the new page so we can't assert on that
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { composeReducer } from '../../../store/reducers/composes';
|
||||
import { composes } from '../../../store/reducers/composes';
|
||||
import types from '../../../store/types';
|
||||
|
||||
const compose = {
|
||||
|
|
@ -10,9 +10,9 @@ const compose = {
|
|||
}
|
||||
};
|
||||
|
||||
describe('composeReducer', () => {
|
||||
describe('composes', () => {
|
||||
test('returns state for unknown actions', () => {
|
||||
const result = composeReducer({}, {
|
||||
const result = composes({}, {
|
||||
type: 'THIS-IS-UNKNOWN',
|
||||
});
|
||||
|
||||
|
|
@ -23,7 +23,7 @@ describe('composeReducer', () => {
|
|||
const state = {
|
||||
testAttr: 'test-me'
|
||||
};
|
||||
const result = composeReducer(state, {
|
||||
const result = composes(state, {
|
||||
type: types.UPDATE_COMPOSE,
|
||||
compose
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import { Router } from 'react-router-dom';
|
|||
import { Provider } from 'react-redux';
|
||||
import { render } from '@testing-library/react';
|
||||
import { createMemoryHistory } from 'history';
|
||||
//import configureStore from 'redux-mock-store';
|
||||
import { init, clearStore } from '../store';
|
||||
|
||||
export const renderWithReduxRouter = (component, store = {}, route = '/') => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue