2022-02-14 00:43:48 -06:00
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2022, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import gettext from 'sources/gettext' ;
import url _for from 'sources/url_for' ;
import React from 'react' ;
2022-04-26 06:11:10 -05:00
import { Box , Paper } from '@material-ui/core' ;
2022-02-14 00:43:48 -06:00
import { makeStyles } from '@material-ui/core/styles' ;
import Wizard from '../../../../static/js/helpers/wizard/Wizard' ;
import WizardStep from '../../../../static/js/helpers/wizard/WizardStep' ;
2022-04-26 06:11:10 -05:00
import { FormFooterMessage , MESSAGE _TYPE } from '../../../../static/js/components/FormComponents' ;
2022-02-14 00:43:48 -06:00
import getApiInstance from '../../../../static/js/api_instance' ;
2022-07-27 23:44:04 -05:00
import Notifier from '../../../../static/js/helpers/Notifier' ;
2022-02-14 00:43:48 -06:00
import PropTypes from 'prop-types' ;
import pgAdmin from 'sources/pgadmin' ;
2022-04-26 06:11:10 -05:00
import { ToggleButtons , FinalSummary } from './cloud_components' ;
import { PrimaryButton } from '../../../../static/js/components/Buttons' ;
2022-06-15 00:52:42 -05:00
import { AwsCredentials , AwsInstanceDetails , AwsDatabaseDetails , validateCloudStep1 , validateCloudStep2 , validateCloudStep3 } from './aws' ;
import { BigAnimalInstance , BigAnimalDatabase , validateBigAnimal , validateBigAnimalStep2 , validateBigAnimalStep3 } from './biganimal' ;
2022-05-24 01:04:23 -05:00
import { isEmptyString } from 'sources/validators' ;
2022-06-15 00:52:42 -05:00
import { AWSIcon , BigAnimalIcon , AzureIcon } from '../../../../static/js/components/ExternalIcon' ;
import { AzureCredentials , AzureInstanceDetails , AzureDatabaseDetails , checkClusternameAvailbility , validateAzureStep2 , validateAzureStep3 } from './azure' ;
import EventBus from '../../../../static/js/helpers/EventBus' ;
2022-02-14 00:43:48 -06:00
const useStyles = makeStyles ( ( ) =>
( {
messageBox : {
marginBottom : '1em' ,
display : 'flex' ,
} ,
messagePadding : {
2022-04-26 06:11:10 -05:00
paddingTop : '10px' ,
flex : 2.5 ,
} ,
buttonMarginEDB : {
position : 'relative' ,
top : '20%' ,
2022-02-14 00:43:48 -06:00
} ,
toggleButton : {
height : '100px' ,
} ,
2022-04-26 06:11:10 -05:00
summaryContainer : {
flexGrow : 1 ,
minHeight : 0 ,
overflow : 'auto' ,
2022-02-14 00:43:48 -06:00
} ,
2022-04-26 06:11:10 -05:00
boxText : {
paddingBottom : '5px'
2022-02-14 00:43:48 -06:00
} ,
2022-06-15 00:52:42 -05:00
authButton : {
marginLeft : '12em'
}
2022-02-14 00:43:48 -06:00
} ) ,
) ;
2022-06-15 00:52:42 -05:00
export const CloudWizardEventsContext = React . createContext ( ) ;
2022-07-27 23:44:04 -05:00
export default function CloudWizard ( { nodeInfo , nodeData , onClose } ) {
2022-02-14 00:43:48 -06:00
const classes = useStyles ( ) ;
2022-06-15 00:52:42 -05:00
const eventBus = React . useRef ( new EventBus ( ) ) ;
2022-04-29 05:48:28 -05:00
var steps = [ gettext ( 'Cloud Provider' ) , gettext ( 'Credentials' ) ,
gettext ( 'Instance Specification' ) , gettext ( 'Database Details' ) , gettext ( 'Review' ) ] ;
2022-02-14 00:43:48 -06:00
const [ currentStep , setCurrentStep ] = React . useState ( '' ) ;
const [ selectionVal , setCloudSelection ] = React . useState ( '' ) ;
const [ errMsg , setErrMsg ] = React . useState ( '' ) ;
const [ cloudInstanceDetails , setCloudInstanceDetails ] = React . useState ( { } ) ;
const [ cloudDBCred , setCloudDBCred ] = React . useState ( { } ) ;
const [ cloudDBDetails , setCloudDBDetails ] = React . useState ( { } ) ;
const [ callRDSAPI , setCallRDSAPI ] = React . useState ( { } ) ;
2022-02-28 06:49:18 -06:00
const [ hostIP , setHostIP ] = React . useState ( '127.0.0.1/32' ) ;
2022-04-26 06:11:10 -05:00
const [ cloudProvider , setCloudProvider ] = React . useState ( '' ) ;
const [ verificationIntiated , setVerificationIntiated ] = React . useState ( false ) ;
const [ bigAnimalInstanceData , setBigAnimalInstanceData ] = React . useState ( { } ) ;
const [ bigAnimalDatabaseData , setBigAnimalDatabaseData ] = React . useState ( { } ) ;
2022-06-15 00:52:42 -05:00
const [ azureCredData , setAzureCredData ] = React . useState ( { } ) ;
const [ azureInstanceData , setAzureInstanceData ] = React . useState ( { } ) ;
const [ azureDatabaseData , setAzureDatabaseData ] = React . useState ( { } ) ;
2022-05-24 01:04:23 -05:00
2022-02-14 00:43:48 -06:00
const axiosApi = getApiInstance ( ) ;
2022-04-26 06:11:10 -05:00
const [ verificationURI , setVerificationURI ] = React . useState ( '' ) ;
const [ verificationCode , setVerificationCode ] = React . useState ( '' ) ;
2022-06-15 00:52:42 -05:00
React . useEffect ( ( ) => {
eventBus . current . registerListener ( 'SET_ERROR_MESSAGE_FOR_CLOUD_WIZARD' , ( msg ) => {
setErrMsg ( msg ) ;
} ) ;
} , [ ] ) ;
React . useEffect ( ( ) => {
eventBus . current . registerListener ( 'SET_CRED_VERIFICATION_INITIATED' , ( initiated ) => {
setVerificationIntiated ( initiated ) ;
} ) ;
} , [ ] ) ;
2022-02-28 06:49:18 -06:00
React . useEffect ( ( ) => {
let _url = url _for ( 'cloud.get_host_ip' ) ;
axiosApi . get ( _url )
. then ( ( res ) => {
if ( res . data . data ) {
setHostIP ( res . data . data ) ;
}
} )
. catch ( ( error ) => {
2022-07-27 23:44:04 -05:00
Notifier . error ( gettext ( ` Error while getting the host ip: ${ error . response . data . errormsg } ` ) ) ;
2022-02-28 06:49:18 -06:00
} ) ;
2022-04-26 06:11:10 -05:00
} , [ cloudProvider ] ) ;
2022-02-14 00:43:48 -06:00
const wizardStepChange = ( data ) => {
setCurrentStep ( data . currentStep ) ;
} ;
2022-04-26 06:11:10 -05:00
const onSave = ( ) => {
var _url = url _for ( 'cloud.deploy_on_cloud' ) ,
post _data = { } ;
if ( cloudProvider == 'rds' ) {
post _data = {
gid : nodeInfo . server _group . _id ,
cloud : cloudProvider ,
secret : cloudDBCred ,
instance _details : cloudInstanceDetails ,
db _details : cloudDBDetails
} ;
2022-06-15 00:52:42 -05:00
} else if ( cloudProvider == 'azure' ) {
post _data = {
gid : nodeInfo . server _group . _id ,
secret : azureCredData ,
cloud : cloudProvider ,
instance _details : azureInstanceData ,
db _details : azureDatabaseData
} ;
} else {
2022-04-26 06:11:10 -05:00
post _data = {
gid : nodeInfo . server _group . _id ,
cloud : cloudProvider ,
instance _details : bigAnimalInstanceData ,
db _details : bigAnimalDatabaseData
} ;
2022-02-14 00:43:48 -06:00
}
axiosApi . post ( _url , post _data )
. then ( ( res ) => {
pgAdmin . Browser . Events . trigger ( 'pgadmin:browser:tree:add' , res . data . data . node , { 'server_group' : nodeInfo [ 'server_group' ] } ) ;
2022-08-11 00:19:45 -05:00
pgAdmin . Browser . BgProcessManager . startProcess ( res . data . data . job _id , res . data . data . desc ) ;
2022-07-27 23:44:04 -05:00
onClose ( ) ;
2022-02-14 00:43:48 -06:00
} )
. catch ( ( error ) => {
2022-07-27 23:44:04 -05:00
Notifier . error ( gettext ( ` Error while saving cloud wizard data: ${ error . response . data . errormsg } ` ) ) ;
2022-02-14 00:43:48 -06:00
} ) ;
} ;
const disableNextCheck = ( ) => {
setCallRDSAPI ( currentStep ) ;
2022-04-26 06:11:10 -05:00
let isError = ( cloudProvider == '' ) ;
switch ( cloudProvider ) {
case 'rds' :
switch ( currentStep ) {
case 0 :
setCloudSelection ( 'rds' ) ;
break ;
case 1 :
isError = validateCloudStep1 ( cloudDBCred ) ;
break ;
case 2 :
isError = validateCloudStep2 ( cloudInstanceDetails , hostIP ) ;
break ;
case 3 :
isError = validateCloudStep3 ( cloudDBDetails , nodeInfo ) ;
break ;
default :
break ;
}
2022-02-14 00:43:48 -06:00
break ;
2022-04-26 06:11:10 -05:00
case 'biganimal' :
switch ( currentStep ) {
case 0 :
setCloudSelection ( 'biganimal' ) ;
break ;
case 1 :
isError = ! verificationIntiated ;
break ;
case 2 :
isError = validateBigAnimalStep2 ( bigAnimalInstanceData ) ;
break ;
case 3 :
isError = validateBigAnimalStep3 ( bigAnimalDatabaseData , nodeInfo ) ;
break ;
default :
break ;
}
2022-02-14 00:43:48 -06:00
break ;
2022-06-15 00:52:42 -05:00
case 'azure' :
switch ( currentStep ) {
case 0 :
setCloudSelection ( 'azure' ) ;
break ;
case 1 :
isError = ! verificationIntiated ;
break ;
case 2 :
isError = validateAzureStep2 ( azureInstanceData ) ;
break ;
case 3 :
isError = validateAzureStep3 ( azureDatabaseData , nodeInfo ) ;
break ;
default :
break ;
}
break ;
2022-02-14 00:43:48 -06:00
}
return isError ;
} ;
const onBeforeNext = ( activeStep ) => {
return new Promise ( ( resolve , reject ) => {
2022-04-26 06:11:10 -05:00
if ( activeStep == 1 && cloudProvider == 'rds' ) {
2022-02-14 00:43:48 -06:00
setErrMsg ( [ MESSAGE _TYPE . INFO , 'Validating credentials...' ] ) ;
2022-04-26 06:11:10 -05:00
var _url = url _for ( 'rds.verify_credentials' ) ;
2022-02-14 00:43:48 -06:00
const post _data = {
cloud : selectionVal ,
secret : cloudDBCred ,
} ;
axiosApi . post ( _url , post _data )
. then ( ( res ) => {
if ( ! res . data . success ) {
setErrMsg ( [ MESSAGE _TYPE . ERROR , res . data . info ] ) ;
reject ( ) ;
} else {
setErrMsg ( [ '' , '' ] ) ;
resolve ( ) ;
}
} )
. catch ( ( ) => {
setErrMsg ( [ MESSAGE _TYPE . ERROR , 'Error while checking cloud credentials' ] ) ;
reject ( ) ;
} ) ;
2022-04-26 06:11:10 -05:00
} else if ( activeStep == 0 && cloudProvider == 'biganimal' ) {
2022-05-24 01:04:23 -05:00
if ( ! isEmptyString ( verificationURI ) ) { resolve ( ) ; return ; }
2022-04-26 06:11:10 -05:00
setErrMsg ( [ MESSAGE _TYPE . INFO , 'Getting EDB BigAnimal verification URL...' ] ) ;
validateBigAnimal ( )
. then ( ( res ) => {
setVerificationURI ( res ) ;
setVerificationCode ( res . substring ( res . indexOf ( '=' ) + 1 ) ) ;
setErrMsg ( [ '' , '' ] ) ;
resolve ( ) ;
} )
. catch ( ( error ) => {
setErrMsg ( [ MESSAGE _TYPE . ERROR , gettext ( error ) ] ) ;
reject ( ) ;
} ) ;
2022-06-15 00:52:42 -05:00
} else if ( activeStep == 2 && cloudProvider == 'azure' ) {
setErrMsg ( [ MESSAGE _TYPE . INFO , 'Checking cluster name availability...' ] ) ;
checkClusternameAvailbility ( azureInstanceData . name )
. then ( ( res ) => {
if ( res . data && res . data . success == 0 ) {
setErrMsg ( [ MESSAGE _TYPE . ERROR , gettext ( 'Specified cluster name is already used.' ) ] ) ;
} else {
setErrMsg ( [ '' , '' ] ) ;
}
resolve ( ) ;
} ) . catch ( ( error ) => {
setErrMsg ( [ MESSAGE _TYPE . ERROR , gettext ( error ) ] ) ;
reject ( ) ;
} ) ;
2022-04-26 06:11:10 -05:00
}
else {
2022-06-15 00:52:42 -05:00
setErrMsg ( [ '' , '' ] ) ;
2022-02-14 00:43:48 -06:00
resolve ( ) ;
}
} ) ;
} ;
2022-04-26 06:11:10 -05:00
const authenticateBigAnimal = ( ) => {
var loading _icon _url = url _for (
'static' , { 'filename' : 'img/loading.gif' }
) ;
2022-02-14 00:43:48 -06:00
2022-04-26 06:11:10 -05:00
setErrMsg ( [ MESSAGE _TYPE . INFO , 'EDB BigAnimal authentication process is in progress...<img src="' + loading _icon _url + '" alt="' + gettext ( 'Loading...' ) + '">' ] ) ;
window . open ( verificationURI , 'edb_biganimal_authentication' ) ;
let _url = url _for ( 'biganimal.verification_ack' ) ;
const myInterval = setInterval ( ( ) => {
axiosApi . get ( _url )
. then ( ( res ) => {
if ( res . data && res . data . success == 1 ) {
2022-06-09 03:37:05 -05:00
setErrMsg ( [ MESSAGE _TYPE . SUCCESS , gettext ( 'Authentication completed successfully. Click the Next button to proceed.' ) ] ) ;
2022-04-26 06:11:10 -05:00
setVerificationIntiated ( true ) ;
clearInterval ( myInterval ) ;
}
else if ( res . data && res . data . success == 0 && res . data . errormsg == 'access_denied' ) {
2022-06-09 03:37:05 -05:00
setErrMsg ( [ MESSAGE _TYPE . INFO , gettext ( 'Verification failed. Access Denied...' ) ] ) ;
2022-04-26 06:11:10 -05:00
setVerificationIntiated ( false ) ;
clearInterval ( myInterval ) ;
}
else if ( res . data && res . data . success == 0 && res . data . errormsg == 'forbidden' ) {
2022-06-09 03:37:05 -05:00
setErrMsg ( [ MESSAGE _TYPE . INFO , gettext ( 'Authentication completed successfully but you do not have permission to create the cluster.' ) ] ) ;
2022-04-26 06:11:10 -05:00
setVerificationIntiated ( false ) ;
clearInterval ( myInterval ) ;
}
} )
. catch ( ( error ) => {
setErrMsg ( [ MESSAGE _TYPE . ERROR , gettext ( ` Error while verification EDB BigAnimal: ${ error . response . data . errormsg } ` ) ] ) ;
} ) ;
} , 1000 ) ;
2022-02-14 00:43:48 -06:00
2022-04-26 06:11:10 -05:00
} ;
2022-02-14 00:43:48 -06:00
2022-04-26 06:11:10 -05:00
const onDialogHelp = ( ) => {
window . open ( url _for ( 'help.static' , { 'filename' : 'cloud_deployment.html' } ) , 'pgadmin_help' ) ;
} ;
2022-02-14 00:43:48 -06:00
const onErrClose = React . useCallback ( ( ) => {
setErrMsg ( [ ] ) ;
} ) ;
2022-07-06 01:13:49 -05:00
let cloud _providers = [ { label : 'Amazon RDS' , value : 'rds' , icon : < AWSIcon className = { classes . icon } / > } ,
{ label : 'EDB BigAnimal' , value : 'biganimal' , icon : < BigAnimalIcon className = { classes . icon } / > } ,
{ 'label' : 'Azure PostgreSQL' , value : 'azure' , icon : < AzureIcon className = { classes . icon } / > } ] ;
2022-06-28 08:18:41 -05:00
2022-02-14 00:43:48 -06:00
return (
2022-06-15 00:52:42 -05:00
< CloudWizardEventsContext.Provider value = { eventBus . current } >
< >
< Wizard
title = { gettext ( 'Deploy Cloud Instance' ) }
stepList = { steps }
disableNextStep = { disableNextCheck }
onStepChange = { wizardStepChange }
onSave = { onSave }
onHelp = { onDialogHelp }
beforeNext = { onBeforeNext } >
< WizardStep stepId = { 0 } >
< Box className = { classes . messageBox } >
< Box className = { classes . messagePadding } > { gettext ( 'Select a cloud provider.' ) } < / Box >
< / Box >
< Box className = { classes . messageBox } >
< ToggleButtons cloudProvider = { cloudProvider } setCloudProvider = { setCloudProvider }
2022-06-28 08:18:41 -05:00
options = { cloud _providers }
2022-06-15 00:52:42 -05:00
> < / ToggleButtons >
< / Box >
< FormFooterMessage type = { errMsg [ 0 ] } message = { errMsg [ 1 ] } onClose = { onErrClose } / >
< / WizardStep >
< WizardStep stepId = { 1 } >
< Box className = { classes . buttonMarginEDB } >
{ cloudProvider == 'biganimal' && < Box className = { classes . messageBox } >
< Box > { gettext ( 'The verification code to authenticate the pgAdmin to EDB BigAnimal is: ' ) } < strong > { verificationCode } < / strong >
< br / > { gettext ( 'By clicking the below button, you will be redirected to the EDB BigAnimal authentication page in a new tab.' ) }
< / Box >
< / Box > }
{ cloudProvider == 'biganimal' && < PrimaryButton onClick = { authenticateBigAnimal } disabled = { verificationIntiated ? true : false } >
{ gettext ( 'Click here to authenticate yourself to EDB BigAnimal' ) }
< / PrimaryButton > }
{ cloudProvider == 'biganimal' && < Box className = { classes . messageBox } >
< Box > < / Box >
< / Box > }
< / Box >
{ cloudProvider == 'rds' && < AwsCredentials cloudProvider = { cloudProvider } nodeInfo = { nodeInfo } nodeData = { nodeData } setCloudDBCred = { setCloudDBCred } / > }
2022-07-06 01:13:49 -05:00
< Box flexGrow = { 1 } >
2022-06-15 00:52:42 -05:00
{ cloudProvider == 'azure' && < AzureCredentials cloudProvider = { cloudProvider } nodeInfo = { nodeInfo } nodeData = { nodeData } setAzureCredData = { setAzureCredData } / > }
< / Box >
< FormFooterMessage type = { errMsg [ 0 ] } message = { errMsg [ 1 ] } onClose = { onErrClose } / >
< / WizardStep >
< WizardStep stepId = { 2 } >
{ cloudProvider == 'rds' && callRDSAPI == 2 && < AwsInstanceDetails
cloudProvider = { cloudProvider }
nodeInfo = { nodeInfo }
nodeData = { nodeData }
setCloudInstanceDetails = { setCloudInstanceDetails }
hostIP = { hostIP } / > }
{ cloudProvider == 'biganimal' && callRDSAPI == 2 && < BigAnimalInstance
cloudProvider = { cloudProvider }
nodeInfo = { nodeInfo }
nodeData = { nodeData }
setBigAnimalInstanceData = { setBigAnimalInstanceData }
hostIP = { hostIP }
/ > }
{ cloudProvider == 'azure' && callRDSAPI == 2 && < AzureInstanceDetails
cloudProvider = { cloudProvider }
nodeInfo = { nodeInfo }
nodeData = { nodeData }
setAzureInstanceData = { setAzureInstanceData }
hostIP = { hostIP }
azureInstanceData = { azureInstanceData }
/ > }
< FormFooterMessage type = { errMsg [ 0 ] } message = { errMsg [ 1 ] } onClose = { onErrClose } / >
< / WizardStep >
< WizardStep stepId = { 3 } >
{ cloudProvider == 'rds' && < AwsDatabaseDetails
2022-04-26 06:11:10 -05:00
cloudProvider = { cloudProvider }
2022-06-15 00:52:42 -05:00
nodeInfo = { nodeInfo }
nodeData = { nodeData }
setCloudDBDetails = { setCloudDBDetails }
2022-04-26 06:11:10 -05:00
/ >
}
2022-06-15 00:52:42 -05:00
{ cloudProvider == 'biganimal' && callRDSAPI == 3 && < BigAnimalDatabase
2022-04-26 06:11:10 -05:00
cloudProvider = { cloudProvider }
2022-06-15 00:52:42 -05:00
nodeInfo = { nodeInfo }
nodeData = { nodeData }
setBigAnimalDatabaseData = { setBigAnimalDatabaseData }
2022-04-26 06:11:10 -05:00
/ >
}
2022-06-15 00:52:42 -05:00
{ cloudProvider == 'azure' && < AzureDatabaseDetails
cloudProvider = { cloudProvider }
nodeInfo = { nodeInfo }
nodeData = { nodeData }
setAzureDatabaseData = { setAzureDatabaseData }
/ >
}
< / WizardStep >
< WizardStep stepId = { 4 } >
< Box className = { classes . boxText } > { gettext ( 'Please review the details before creating the cloud instance.' ) } < / Box >
< Paper variant = "outlined" elevation = { 0 } className = { classes . summaryContainer } >
{ cloudProvider == 'rds' && callRDSAPI == 4 && < FinalSummary
cloudProvider = { cloudProvider }
instanceData = { cloudInstanceDetails }
databaseData = { cloudDBDetails }
/ >
}
{ cloudProvider == 'biganimal' && callRDSAPI == 4 && < FinalSummary
cloudProvider = { cloudProvider }
instanceData = { bigAnimalInstanceData }
databaseData = { bigAnimalDatabaseData }
/ >
}
{ cloudProvider == 'azure' && callRDSAPI == 4 && < FinalSummary
cloudProvider = { cloudProvider }
instanceData = { azureInstanceData }
databaseData = { azureDatabaseData }
/ >
}
< / Paper >
< / WizardStep >
< / Wizard >
< / >
< / CloudWizardEventsContext.Provider >
2022-02-14 00:43:48 -06:00
) ;
}
CloudWizard . propTypes = {
nodeInfo : PropTypes . object ,
nodeData : PropTypes . object ,
2022-07-27 23:44:04 -05:00
onClose : PropTypes . func
2022-02-14 00:43:48 -06:00
} ;