CreateImageWizard: Add Azure upload step
This commit is contained in:
parent
f3887a1d42
commit
398eeb8cb2
6 changed files with 216 additions and 14 deletions
|
|
@ -44,8 +44,7 @@ const WizardStepImageOutput = (props) => {
|
|||
onClick={ () => props.toggleUploadDestination('azure') }
|
||||
isSelected={ props.uploadDestinations.azure }
|
||||
isStacked
|
||||
isDisplayLarge
|
||||
isDisabled />
|
||||
isDisplayLarge />
|
||||
<Tile
|
||||
className="tile"
|
||||
data-testid="upload-google"
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@
|
|||
color: var(--pf-global--danger-color--100);
|
||||
}
|
||||
// Increasing margins for h3 for better spacing and readability
|
||||
.pf-c-content h3 {
|
||||
.textcontent-review h3 {
|
||||
margin-top: var(--pf-global--spacer--xl);
|
||||
}
|
||||
// Decreasing gap between dl items for better spacing and readability
|
||||
// Also setting a first column width to 25% instead of auto,
|
||||
// to guarantee the same width for each section.
|
||||
@media screen and (min-width: 576px) {
|
||||
.pf-c-content dl {
|
||||
.textcontent-review dl {
|
||||
grid-template: 1fr / 25% 1fr;
|
||||
grid-gap: var(--pf-global--spacer--sm);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
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/authorize?client_id=b94bb246-b02c-4985-9c22-d44e66f657f4
|
||||
&redirect_uri=https%3A%2F%2Flogin.microsoftonline.com%2Fcommon%2Foauth2%2Fnativeclient">
|
||||
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;
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
.textcontent-azure {
|
||||
margin-bottom: var(--pf-global--spacer--lg);
|
||||
h3, h4 {
|
||||
margin-top: var(--pf-global--spacer--sm);
|
||||
margin-bottom: var(--pf-global--spacer--xs);
|
||||
}
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -8,6 +8,7 @@ import { Wizard, TextContent } from '@patternfly/react-core';
|
|||
|
||||
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';
|
||||
|
|
@ -51,7 +52,9 @@ class CreateImageWizard extends Component {
|
|||
uploadAzure: {
|
||||
type: 'azure',
|
||||
options: {
|
||||
temp: ''
|
||||
tenant_id: null,
|
||||
subscription_id: null,
|
||||
resource_group: null,
|
||||
}
|
||||
},
|
||||
uploadGoogle: {
|
||||
|
|
@ -103,14 +106,21 @@ class CreateImageWizard extends Component {
|
|||
}
|
||||
|
||||
validate() {
|
||||
if (this.state.uploadDestinations.aws) {this.validateUploadAmazon();}
|
||||
else {
|
||||
this.setState({
|
||||
uploadAWSErrors: {},
|
||||
uploadAzureErrors: {},
|
||||
uploadGoogleErrors: {},
|
||||
});
|
||||
}
|
||||
/* upload */
|
||||
Object.keys(this.state.uploadDestinations).forEach(provider => {
|
||||
switch (provider) {
|
||||
case 'aws':
|
||||
this.validateUploadAmazon();
|
||||
break;
|
||||
case 'azure':
|
||||
this.validateUploadAzure();
|
||||
break;
|
||||
case 'google':
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
/* subscription */
|
||||
if (this.state.subscribeNow) {
|
||||
|
|
@ -131,6 +141,29 @@ class CreateImageWizard extends Component {
|
|||
this.setState({ uploadAWSErrors });
|
||||
}
|
||||
|
||||
validateUploadAzure() {
|
||||
let uploadAzureErrors = {};
|
||||
|
||||
let tenant_id = this.state.uploadAzure.options.tenant_id;
|
||||
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;
|
||||
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;
|
||||
if (resource_group === null || resource_group === '') {
|
||||
uploadAzureErrors['azure-resource-group'] =
|
||||
{ label: 'Azure resource group', value: 'A resource group is required' };
|
||||
}
|
||||
// TODO check oauth2 thing too here?
|
||||
}
|
||||
|
||||
validateSubscription() {
|
||||
let subscriptionErrors = {};
|
||||
if (!this.state.subscription['activation-key']) {
|
||||
|
|
@ -306,6 +339,30 @@ class CreateImageWizard extends Component {
|
|||
requests.push(request);
|
||||
}
|
||||
|
||||
if (this.state.uploadDestinations.azure) {
|
||||
let request = {
|
||||
distribution: this.state.release,
|
||||
image_requests: [
|
||||
{
|
||||
architecture: this.state.arch,
|
||||
image_type: 'vhd',
|
||||
upload_requests: [{
|
||||
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,
|
||||
},
|
||||
}],
|
||||
}],
|
||||
customizations: {
|
||||
subscription: this.state.subscription,
|
||||
},
|
||||
};
|
||||
requests.push(request);
|
||||
|
||||
}
|
||||
|
||||
const composeRequests = [];
|
||||
requests.forEach(request => {
|
||||
const composeRequest = api.composeImage(request).then(response => {
|
||||
|
|
@ -348,7 +405,11 @@ class CreateImageWizard extends Component {
|
|||
};
|
||||
|
||||
const StepUploadAzure = {
|
||||
name: 'Microsoft Azure'
|
||||
name: 'Microsoft Azure',
|
||||
component: <WizardStepUploadAzure
|
||||
uploadAzure={ this.state.uploadAzure }
|
||||
setUploadOptions={ this.setUploadOptions }
|
||||
errors={ this.state.uploadAzureErrors } />
|
||||
};
|
||||
|
||||
const StepUploadGoogle = {
|
||||
|
|
|
|||
|
|
@ -213,6 +213,60 @@ describe('Step Upload to Google', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('Step Upload to Azure', () => {
|
||||
beforeEach(() => {
|
||||
const { _component, history } = renderWithReduxRouter(<CreateImageWizard />);
|
||||
historySpy = jest.spyOn(history, 'push');
|
||||
|
||||
// select aws as upload destination
|
||||
const azureTile = screen.getByTestId('upload-azure');
|
||||
azureTile.click();
|
||||
|
||||
// left sidebar navigation
|
||||
const sidebar = screen.getByRole('navigation');
|
||||
const anchor = getByText(sidebar, 'Microsoft Azure');
|
||||
|
||||
// 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 azure upload fields are shown and required', () => {
|
||||
const tenantId = screen.getByTestId('azure-tenant-id');
|
||||
expect(tenantId).toHaveValue('');
|
||||
expect(tenantId).toBeEnabled();
|
||||
expect(tenantId).toBeRequired();
|
||||
|
||||
const subscription = screen.getByTestId('azure-subscription-id');
|
||||
expect(subscription).toHaveValue('');
|
||||
expect(subscription).toBeEnabled();
|
||||
expect(subscription).toBeRequired();
|
||||
|
||||
const resourceGroup = screen.getByTestId('azure-resource-group');
|
||||
expect(resourceGroup).toHaveValue('');
|
||||
expect(resourceGroup).toBeEnabled();
|
||||
expect(resourceGroup).toBeRequired();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Step Registration', () => {
|
||||
beforeEach(() => {
|
||||
const { _component, history } = renderWithReduxRouter(<CreateImageWizard />);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue