import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { withRouter } from 'react-router-dom'; import { connect } from 'react-redux'; import { actions } from '../../store/actions'; import { Button, Wizard } from '@patternfly/react-core'; import { ExternalLinkAltIcon } from '@patternfly/react-icons'; import { addNotification } from '@redhat-cloud-services/frontend-components-notifications/redux'; 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 './CreateImageWizard.scss'; class CreateImageWizard extends Component { constructor(props) { super(props); this.onStep = this.onStep.bind(this); this.onSave = this.onSave.bind(this); this.onClose = this.onClose.bind(this); this.validate = this.validate.bind(this); this.validateUploadAmazon = this.validateUploadAmazon.bind(this); this.state = { /* errors take form of $fieldId: error */ uploadAWSErrors: {}, uploadAzureErrors: {}, uploadGoogleErrors: {}, isSaveInProgress: false, isValidSubscription: true, }; } async componentDidMount() { let user = await insights.chrome.auth.getUser(); this.setState({ subscription: { organization: Number(user.identity.internal.org_id) } }); } onStep(step) { if (step.name === 'Review') { this.validate(); } } validate() { /* upload */ Object.keys(this.props.uploadDestinations).forEach(provider => { switch (provider) { case 'aws': this.validateUploadAmazon(); break; case 'azure': this.validateUploadAzure(); break; case 'google': break; default: 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.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' }; } this.setState({ uploadAWSErrors }); } validateUploadAzure() { let uploadAzureErrors = {}; 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.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.props.uploadAzure.resourceGroup; 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? } onSave() { this.setState({ isSaveInProgress: true }); let customizations = { packages: this.props.selectedPackages.map(p => p.name), }; if (this.props.subscribeNow) { customizations.subscription = { 'activation-key': this.props.subscription.activationKey, insights: this.props.subscription.insights, organization: Number(this.props.subscription.organization), 'server-url': 'subscription.rhsm.redhat.com', 'base-url': 'https://cdn.redhat.com/', }; } let requests = []; if (this.props.uploadDestinations.aws) { let request = { distribution: this.props.release.distro, image_requests: [ { architecture: this.props.release.arch, image_type: 'ami', upload_request: { type: 'aws', options: { share_with_accounts: this.props.uploadAWS.shareWithAccounts, }, }, }], customizations, }; requests.push(request); } if (this.props.uploadDestinations.google) { let share = ''; switch (this.props.uploadGoogle.accountType) { case 'googleAccount': share = 'user:' + this.props.uploadGoogle.shareWithAccounts[0].user; break; case 'serviceAccount': share = 'serviceAccount:' + this.props.uploadGoogle.shareWithAccounts[0].serviceAccount; break; case 'googleGroup': share = 'group:' + this.props.uploadGoogle.shareWithAccounts[0].group; break; case 'domain': share = 'domain:' + this.props.uploadGoogle.shareWithAccounts[0].domain; break; } let request = { distribution: this.props.release.distro, image_requests: [ { architecture: this.props.release.arch, image_type: 'vhd', upload_request: { type: 'gcp', options: { share_with_accounts: [ share ], }, }, }], customizations, }; requests.push(request); } if (this.props.uploadDestinations.azure) { let request = { distribution: this.props.release.distro, image_requests: [ { architecture: this.props.release.arch, image_type: 'vhd', upload_request: { type: 'azure', options: { tenant_id: this.props.uploadAzure.tenantId, subscription_id: this.props.uploadAzure.subscriptionId, resource_group: this.props.uploadAzure.resourceGroup, }, }, }], customizations, }; requests.push(request); } const composeRequests = requests.map(request => this.props.composeStart(request)); Promise.all(composeRequests) .then(() => { if (!this.props.composesError) { this.props.addNotification({ variant: 'success', title: 'Your image is being created', }); this.props.history.push('/landing'); } this.setState({ isSaveInProgress: false }); }); } onClose () { this.props.history.push('/landing'); } render() { const isValidUploadDestination = this.props.uploadDestinations.aws || this.props.uploadDestinations.azure || this.props.uploadDestinations.google; const StepImageOutput = { name: 'Image output', component: }; const StepUploadAWS = { name: 'Amazon Web Services', component: }; const StepUploadGoogle = { name: 'Google Cloud Platform', component: }; const StepUploadAzure = { name: 'Microsoft Azure', component: }; const uploadDestinationSteps = []; if (this.props.uploadDestinations.aws) { uploadDestinationSteps.push(StepUploadAWS); } if (this.props.uploadDestinations.google) { uploadDestinationSteps.push(StepUploadGoogle); } if (this.props.uploadDestinations.azure) { uploadDestinationSteps.push(StepUploadAzure); } const StepTargetEnv = { name: 'Target environment', steps: uploadDestinationSteps }; const StepImageRegistration = { name: 'Registration', component: }; const steps = [ StepImageOutput, ...(StepTargetEnv.steps.length > 0 ? [ StepTargetEnv ] : []), ...(this.props.release.distro === 'rhel-84' ? [ StepImageRegistration ] : []), { name: 'Packages', component: }, { name: 'Review', component: , nextButtonText: 'Create', } ]; return ( Create a RHEL image and push it to cloud providers. {' '} } onNext={ this.onStep } onGoToStep={ this.onStep } steps={ steps } onClose={ this.onClose } onSave={ this.onSave } footer={ } isOpen /> ); } } function mapStateToProps(state) { return { composesError: state.composes.error, 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 { composeUpdated: (compose) => dispatch(actions.composeUpdated(compose)), composeStart: (composeRequest) => dispatch(actions.composeStart(composeRequest)), addNotification: (not) => dispatch(addNotification(not)), }; } CreateImageWizard.propTypes = { composesError: PropTypes.string, composeUpdated: PropTypes.func, composeStart: 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(mapStateToProps, mapDispatchToProps)(withRouter(CreateImageWizard));