CreateImageWizard: add google upload step
The user can now specify their authentication settings for Google Cloud Platform. These can be either a Google account, Service account, Google group, or a domain.
This commit is contained in:
parent
6adab8bd3b
commit
22385fd5ea
5 changed files with 300 additions and 34 deletions
|
|
@ -56,8 +56,7 @@ const WizardStepImageOutput = (props) => {
|
|||
onClick={ () => props.toggleUploadDestination('google') }
|
||||
isSelected={ props.uploadDestinations.google }
|
||||
isStacked
|
||||
isDisplayLarge
|
||||
isDisabled />
|
||||
isDisplayLarge />
|
||||
</div>
|
||||
</FormGroup>
|
||||
</Form>
|
||||
|
|
|
|||
|
|
@ -32,6 +32,50 @@ const WizardStepReview = (props) => {
|
|||
</>
|
||||
);
|
||||
|
||||
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 = (<>
|
||||
|
|
@ -67,6 +111,7 @@ const WizardStepReview = (props) => {
|
|||
</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>
|
||||
|
|
@ -80,6 +125,7 @@ const WizardStepReview = (props) => {
|
|||
WizardStepReview.propTypes = {
|
||||
release: PropTypes.string,
|
||||
uploadAWS: PropTypes.object,
|
||||
uploadGoogle: PropTypes.object,
|
||||
uploadDestinations: PropTypes.object,
|
||||
subscription: PropTypes.object,
|
||||
subscribeNow: PropTypes.bool,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,148 @@
|
|||
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;
|
||||
|
|
@ -9,6 +9,7 @@ import { Wizard, TextContent } from '@patternfly/react-core';
|
|||
import WizardStepImageOutput from '../../PresentationalComponents/CreateImageWizard/WizardStepImageOutput';
|
||||
import WizardStepUploadAWS from '../../PresentationalComponents/CreateImageWizard/WizardStepUploadAWS';
|
||||
import WizardStepPackages from '../../PresentationalComponents/CreateImageWizard/WizardStepPackages';
|
||||
import WizardStepUploadGoogle from '../../PresentationalComponents/CreateImageWizard/WizardStepUploadGoogle';
|
||||
import WizardStepRegistration from '../../PresentationalComponents/CreateImageWizard/WizardStepRegistration';
|
||||
import WizardStepReview from '../../PresentationalComponents/CreateImageWizard/WizardStepReview';
|
||||
|
||||
|
|
@ -28,6 +29,7 @@ class CreateImageWizard extends Component {
|
|||
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);
|
||||
|
|
@ -53,9 +55,10 @@ class CreateImageWizard extends Component {
|
|||
}
|
||||
},
|
||||
uploadGoogle: {
|
||||
type: 'google',
|
||||
type: 'gcp',
|
||||
accountType: 'googleAccount',
|
||||
options: {
|
||||
temp: ''
|
||||
share_with_accounts: []
|
||||
}
|
||||
},
|
||||
uploadDestinations: {
|
||||
|
|
@ -173,7 +176,7 @@ class CreateImageWizard extends Component {
|
|||
case 'google':
|
||||
this.setState({
|
||||
uploadGoogle: {
|
||||
type: provider,
|
||||
...this.state.uploadGoogle,
|
||||
options: uploadOptions
|
||||
}
|
||||
});
|
||||
|
|
@ -183,6 +186,15 @@ class CreateImageWizard extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
setGoogleAccountType(_, event) {
|
||||
this.setState({
|
||||
uploadGoogle: {
|
||||
...this.state.uploadGoogle,
|
||||
accountType: event.target.value
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setSubscribeNow(subscribeNow) {
|
||||
this.setState({ subscribeNow });
|
||||
}
|
||||
|
|
@ -240,34 +252,40 @@ class CreateImageWizard extends Component {
|
|||
|
||||
onSave () {
|
||||
let requests = [];
|
||||
Object.keys(this.state.uploadDestinations).forEach(provider => {
|
||||
switch (provider) {
|
||||
case 'aws': {
|
||||
let request = {
|
||||
distribution: this.state.release,
|
||||
image_requests: [
|
||||
{
|
||||
architecture: this.state.arch,
|
||||
image_type: 'ami',
|
||||
upload_requests: [ this.state.uploadAWS ],
|
||||
}],
|
||||
customizations: {
|
||||
subscription: this.state.subscription,
|
||||
packages: this.state.packagesSelectedNames,
|
||||
},
|
||||
};
|
||||
requests.push(request);
|
||||
break;
|
||||
}
|
||||
if (this.state.uploadDestinations.aws) {
|
||||
let request = {
|
||||
distribution: this.state.release,
|
||||
image_requests: [
|
||||
{
|
||||
architecture: this.state.arch,
|
||||
image_type: 'ami',
|
||||
upload_requests: [ this.state.uploadAWS ],
|
||||
}],
|
||||
customizations: {
|
||||
subscription: this.state.subscription,
|
||||
packages: this.state.packagesSelectedNames,
|
||||
},
|
||||
};
|
||||
requests.push(request);
|
||||
}
|
||||
|
||||
case 'azure':
|
||||
break;
|
||||
case 'google':
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
if (this.state.uploadDestinations.google) {
|
||||
const upload_google = this.state.uploadGoogle;
|
||||
delete upload_google.accountType;
|
||||
let request = {
|
||||
distribution: this.state.release,
|
||||
image_requests: [
|
||||
{
|
||||
architecture: this.state.arch,
|
||||
image_type: 'gcp',
|
||||
upload_requests: [ upload_google ],
|
||||
}],
|
||||
customizations: {
|
||||
subscription: this.state.subscription,
|
||||
},
|
||||
};
|
||||
requests.push(request);
|
||||
}
|
||||
|
||||
const composeRequests = [];
|
||||
requests.forEach(request => {
|
||||
|
|
@ -315,7 +333,12 @@ class CreateImageWizard extends Component {
|
|||
};
|
||||
|
||||
const StepUploadGoogle = {
|
||||
name: 'Google Cloud Platform'
|
||||
name: 'Google Cloud Platform',
|
||||
component: <WizardStepUploadGoogle
|
||||
uploadGoogle={ this.state.uploadGoogle }
|
||||
setGoogleAccountType={ this.setGoogleAccountType }
|
||||
setUploadOptions={ this.setUploadOptions }
|
||||
errors={ this.state.uploadGoogleErrors } />
|
||||
};
|
||||
|
||||
const uploadDestinationSteps = [];
|
||||
|
|
@ -362,6 +385,7 @@ class CreateImageWizard extends Component {
|
|||
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 }
|
||||
|
|
|
|||
|
|
@ -169,6 +169,50 @@ describe('Step Upload to AWS', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('Step Upload to Google', () => {
|
||||
beforeEach(() => {
|
||||
const { _component, history } = renderWithReduxRouter(<CreateImageWizard />);
|
||||
historySpy = jest.spyOn(history, 'push');
|
||||
|
||||
// select aws as upload destination
|
||||
const awsTile = screen.getByTestId('upload-google');
|
||||
awsTile.click();
|
||||
|
||||
// left sidebar navigation
|
||||
const sidebar = screen.getByRole('navigation');
|
||||
const anchor = getByText(sidebar, 'Google Cloud Platform');
|
||||
|
||||
// load from sidebar
|
||||
anchor.click();
|
||||
});
|
||||
|
||||
test('clicking Next loads Registration', () => {
|
||||
const [ next, , ] = verifyButtons();
|
||||
next.click();
|
||||
|
||||
screen.getByText('Register the system');
|
||||
});
|
||||
|
||||
test('clicking Back loads Release', () => {
|
||||
const [ , back, ] = verifyButtons();
|
||||
back.click();
|
||||
|
||||
screen.getByTestId('release-select');
|
||||
});
|
||||
|
||||
test('clicking Cancel loads landing page', () => {
|
||||
const [ , , cancel ] = verifyButtons();
|
||||
verifyCancelButton(cancel, historySpy);
|
||||
});
|
||||
|
||||
test('the google account id field is shown and required', () => {
|
||||
const accessKeyId = screen.getByTestId('input-google-user');
|
||||
expect(accessKeyId).toHaveValue('');
|
||||
expect(accessKeyId).toBeEnabled();
|
||||
expect(accessKeyId).toBeRequired();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Step Registration', () => {
|
||||
beforeEach(() => {
|
||||
const { _component, history } = renderWithReduxRouter(<CreateImageWizard />);
|
||||
|
|
@ -344,12 +388,16 @@ describe('Click through all steps', () => {
|
|||
// select image output
|
||||
userEvent.selectOptions(screen.getByTestId('release-select'), [ 'rhel-8' ]);
|
||||
screen.getByTestId('upload-aws').click();
|
||||
screen.getByTestId('upload-google').click();
|
||||
next.click();
|
||||
|
||||
// select upload target
|
||||
userEvent.type(screen.getByTestId('aws-account-id'), '012345678901');
|
||||
next.click();
|
||||
|
||||
userEvent.type(screen.getByTestId('input-google-user'), 'test@test.com');
|
||||
next.click();
|
||||
|
||||
// registration
|
||||
screen
|
||||
.getByLabelText('Embed an activation key and register systems on first boot')
|
||||
|
|
@ -367,6 +415,7 @@ describe('Click through all steps', () => {
|
|||
findByText('Review the information and click Create image to create the image using the following criteria.');
|
||||
const main = screen.getByRole('main', { name: 'Create image' });
|
||||
within(main).getByText('Amazon Web Services');
|
||||
within(main).getByText('Google Cloud Platform');
|
||||
await screen.findByText('Register the system on first boot');
|
||||
|
||||
// mock the backend API
|
||||
|
|
@ -377,7 +426,7 @@ describe('Click through all steps', () => {
|
|||
create.click();
|
||||
|
||||
// API request sent to backend
|
||||
await expect(composeImage).toHaveBeenCalledTimes(1);
|
||||
await expect(composeImage).toHaveBeenCalledTimes(2);
|
||||
|
||||
// returns back to the landing page
|
||||
// but jsdom will not render the new page so we can't assert on that
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue