diff --git a/managed/src/main/java/com/yugabyte/yw/controllers/handlers/UniverseCRUDHandler.java b/managed/src/main/java/com/yugabyte/yw/controllers/handlers/UniverseCRUDHandler.java index d84ef88ec3d8..3641933b345e 100644 --- a/managed/src/main/java/com/yugabyte/yw/controllers/handlers/UniverseCRUDHandler.java +++ b/managed/src/main/java/com/yugabyte/yw/controllers/handlers/UniverseCRUDHandler.java @@ -358,7 +358,10 @@ public void configure(Customer customer, UniverseConfigureTaskParams taskParams) userIntent.masterGFlags = trimFlags(userIntent.masterGFlags); userIntent.tserverGFlags = trimFlags(userIntent.tserverGFlags); - if (StringUtils.isEmpty(userIntent.accessKeyCode)) { + Provider provider = Provider.getOrBadRequest(UUID.fromString(userIntent.provider)); + if (StringUtils.isEmpty(userIntent.accessKeyCode) + && !(userIntent.providerType.equals(Common.CloudType.onprem) + && provider.getDetails().skipProvisioning)) { userIntent.accessKeyCode = appConfig.getString("yb.security.default.access.key"); } try { diff --git a/managed/ui/src/components/configRedesign/providerRedesign/forms/onPrem/OnPremProviderCreateForm.tsx b/managed/ui/src/components/configRedesign/providerRedesign/forms/onPrem/OnPremProviderCreateForm.tsx index ffd8ec404fbc..6299ecd54b7f 100644 --- a/managed/ui/src/components/configRedesign/providerRedesign/forms/onPrem/OnPremProviderCreateForm.tsx +++ b/managed/ui/src/components/configRedesign/providerRedesign/forms/onPrem/OnPremProviderCreateForm.tsx @@ -75,8 +75,14 @@ const VALIDATION_SCHEMA = object().shape({ ACCEPTABLE_CHARS, 'Provider name cannot contain special characters other than "-", and "_"' ), - sshUser: string().required('SSH user is required.'), - sshPrivateKeyContent: mixed().required('SSH private key is required.'), + sshUser: string().when('skipProvisioning', { + is: false, + then: string().required('SSH user is required.') + }), + sshPrivateKeyContent: mixed().when('skipProvisioning', { + is: false, + then: mixed().required('SSH private key is required.') + }), ntpServers: array().when('ntpSetupType', { is: NTPSetupType.SPECIFIED, then: array().of( diff --git a/managed/ui/src/components/configRedesign/providerRedesign/forms/onPrem/OnPremProviderEditForm.tsx b/managed/ui/src/components/configRedesign/providerRedesign/forms/onPrem/OnPremProviderEditForm.tsx index a5e22318a0e4..2bd31adb9ce9 100644 --- a/managed/ui/src/components/configRedesign/providerRedesign/forms/onPrem/OnPremProviderEditForm.tsx +++ b/managed/ui/src/components/configRedesign/providerRedesign/forms/onPrem/OnPremProviderEditForm.tsx @@ -99,9 +99,12 @@ const VALIDATION_SCHEMA = object().shape({ ACCEPTABLE_CHARS, 'Provider name cannot contain special characters other than "-", and "_"' ), - sshUser: string().required('SSH user is required.'), - sshPrivateKeyContent: mixed().when('editSSHKeypair', { - is: true, + sshUser: string().when('skipProvisioning', { + is: false, + then: string().required('SSH user is required.') + }), + sshPrivateKeyContent: mixed().when(['editSSHKeypair', 'skipProvisioning'], { + is: (editSSHKeypair, skipProvisioning) => editSSHKeypair && !skipProvisioning, then: mixed().required('SSH private key is required.') }), ntpServers: array().when('ntpSetupType', { diff --git a/managed/ui/src/components/universes/NodeDetails/NodeAction.js b/managed/ui/src/components/universes/NodeDetails/NodeAction.js index 18f68f012e47..01cef59df4f4 100644 --- a/managed/ui/src/components/universes/NodeDetails/NodeAction.js +++ b/managed/ui/src/components/universes/NodeDetails/NodeAction.js @@ -199,7 +199,9 @@ export default class NodeAction extends Component { disabled, clusterType, isKubernetes, - isOnPremManuallyProvisioned + isOnPremManuallyProvisioned, + cluster, + accessKeys } = this.props; const allowedActions = @@ -363,6 +365,10 @@ export default class NodeAction extends Component { ); } + const accessKeyCode = cluster.userIntent.accessKeyCode; + const accessKey = accessKeys.data.find( + (key) => key.idKey.providerUUID === providerUUID && key.idKey.keyCode === accessKeyCode + ); return ( )} {isNonEmptyArray(nodeAllowedActions) ? ( diff --git a/managed/ui/src/components/universes/NodeDetails/NodeConnectModal.js b/managed/ui/src/components/universes/NodeDetails/NodeConnectModal.js index b07878fdae96..1abc5400c991 100644 --- a/managed/ui/src/components/universes/NodeDetails/NodeConnectModal.js +++ b/managed/ui/src/components/universes/NodeDetails/NodeConnectModal.js @@ -47,7 +47,8 @@ class NodeConnectModal extends Component { currentUniverse, clusterType, universeUUID, - providers + providers, + disabled } = this.props; const nodeIPs = { privateIP: currentRow.privateIP, publicIP: currentRow.publicIP }; let accessCommand = null; @@ -69,7 +70,9 @@ class NodeConnectModal extends Component { } const providerUsed = _.find(providers?.data, { uuid: providerUUID }); - const imageBundleUsed = _.find(providerUsed?.imageBundles, { uuid: cluster?.userIntent?.imageBundleUUID }); + const imageBundleUsed = _.find(providerUsed?.imageBundles, { + uuid: cluster?.userIntent?.imageBundleUUID + }); const tectiaSSH = runtimeConfigs?.data?.configEntries?.find( (c) => c.key === 'yb.security.ssh2_enabled' @@ -107,11 +110,13 @@ class NodeConnectModal extends Component { const accessKeyInfo = accessKey?.keyInfo; const sshPort = imageBundleUsed?.details?.sshPort ?? (accessKeyInfo?.sshPort || 22); if (!isTectiaSSHEnabled) { - accessCommand = `sudo ssh -i ${accessKeyInfo?.privateKey ?? '' - } -ostricthostkeychecking=no -p ${sshPort} yugabyte@${nodeIPs.privateIP}`; + accessCommand = `sudo ssh -i ${ + accessKeyInfo?.privateKey ?? '' + } -ostricthostkeychecking=no -p ${sshPort} yugabyte@${nodeIPs.privateIP}`; } else { - accessCommand = `sshg3 -K ${accessKeyInfo?.privateKey ?? '' - } -ostricthostkeychecking=no -p ${sshPort} yugabyte@${nodeIPs.privateIP}`; + accessCommand = `sshg3 -K ${ + accessKeyInfo?.privateKey ?? '' + } -ostricthostkeychecking=no -p ${sshPort} yugabyte@${nodeIPs.privateIP}`; } } @@ -128,7 +133,8 @@ class NodeConnectModal extends Component { this.toggleConnectModal(true)} + onSelect={() => this.toggleConnectModal(true)} + disabled={disabled} > {label} diff --git a/managed/ui/src/components/universes/NodeDetails/NodeDetails.js b/managed/ui/src/components/universes/NodeDetails/NodeDetails.js index 8c592f00d546..5d80deb6e320 100644 --- a/managed/ui/src/components/universes/NodeDetails/NodeDetails.js +++ b/managed/ui/src/components/universes/NodeDetails/NodeDetails.js @@ -67,7 +67,8 @@ export default class NodeDetails extends Component { universeMasterInfo }, customer, - providers + providers, + accessKeys } = this.props; const universeDetails = currentUniverse.data.universeDetails; const nodeDetails = universeDetails.nodeDetailsSet; @@ -220,6 +221,7 @@ export default class NodeDetails extends Component { customer={customer} currentUniverse={currentUniverse} providers={providers} + accessKeys={accessKeys} /> {readOnlyCluster && ( )} diff --git a/managed/ui/src/components/universes/NodeDetails/NodeDetailsContainer.js b/managed/ui/src/components/universes/NodeDetails/NodeDetailsContainer.js index fb72aced0abf..6f76399ec7e6 100644 --- a/managed/ui/src/components/universes/NodeDetails/NodeDetailsContainer.js +++ b/managed/ui/src/components/universes/NodeDetails/NodeDetailsContainer.js @@ -21,6 +21,7 @@ import { function mapStateToProps(state) { return { + accessKeys: state.cloud.accessKeys, universe: state.universe, customer: state.customer, providers: state.cloud.providers diff --git a/managed/ui/src/components/universes/NodeDetails/NodeDetailsTable.js b/managed/ui/src/components/universes/NodeDetails/NodeDetailsTable.js index 80c9e684defb..a5752d3613a0 100644 --- a/managed/ui/src/components/universes/NodeDetails/NodeDetailsTable.js +++ b/managed/ui/src/components/universes/NodeDetails/NodeDetailsTable.js @@ -61,7 +61,8 @@ export default class NodeDetailsTable extends Component { currentUniverse, providers, isDedicatedNodes, - isKubernetesCluster + isKubernetesCluster, + accessKeys } = this.props; const successIcon = ; const warningIcon = ; @@ -307,6 +308,8 @@ export default class NodeDetailsTable extends Component { clusterType={clusterType} isKubernetes={isKubernetes} isOnPremManuallyProvisioned={isOnPremManuallyProvisioned} + cluster={cluster} + accessKeys={accessKeys} /> ); }; diff --git a/managed/ui/src/redesign/features/universe/universe-form/EditRR.tsx b/managed/ui/src/redesign/features/universe/universe-form/EditRR.tsx index 6a1b7f7ce5e2..36fdd4e3f93a 100644 --- a/managed/ui/src/redesign/features/universe/universe-form/EditRR.tsx +++ b/managed/ui/src/redesign/features/universe/universe-form/EditRR.tsx @@ -34,6 +34,7 @@ import { UniverseFormData } from './utils/dto'; import { PROVIDER_FIELD, TOAST_AUTO_DISMISS_INTERVAL } from './utils/constants'; +import { providerQueryKey, api as helperApi } from '../../../helpers/api'; interface EditReadReplicaProps { uuid: string; @@ -80,6 +81,15 @@ export const EditReadReplica: FC = ({ uuid, isViewMode }) } ); + const asyncProviderUuid = universe?.universeDetails + ? getAsyncCluster(universe?.universeDetails)?.userIntent?.provider ?? '' + : ''; + const providerConfigQuery = useQuery( + providerQueryKey.detail(asyncProviderUuid), + () => helperApi.fetchProvider(asyncProviderUuid), + { enabled: !!asyncProviderUuid } + ); + const onCancel = () => browserHistory.push(`/universes/${uuid}`); const onSubmit = async (formData: UniverseFormData) => { @@ -127,12 +137,14 @@ export const EditReadReplica: FC = ({ uuid, isViewMode }) } else setK8Modal(true); }; - if (isLoading || contextState.isLoading) return ; + if (isLoading || contextState.isLoading || providerConfigQuery.isLoading) { + return ; + } if (!universe?.universeDetails) return null; //get async form data and intitalize the form - const initialFormData = getAsyncFormData(universe.universeDetails); + const initialFormData = getAsyncFormData(universe.universeDetails, providerConfigQuery.data); return ( <> diff --git a/managed/ui/src/redesign/features/universe/universe-form/EditUniverse.tsx b/managed/ui/src/redesign/features/universe/universe-form/EditUniverse.tsx index 3214a4a2bc93..e39212162bd1 100644 --- a/managed/ui/src/redesign/features/universe/universe-form/EditUniverse.tsx +++ b/managed/ui/src/redesign/features/universe/universe-form/EditUniverse.tsx @@ -17,6 +17,7 @@ import { api, QUERY_KEY } from './utils/api'; import { getPlacements } from './form/fields/PlacementsField/PlacementsFieldHelper'; import { createErrorMessage, + getPrimaryCluster, getPrimaryFormData, transformTagsArrayToObject, transitToUniverse @@ -48,6 +49,7 @@ import { USER_TAGS_FIELD, SPOT_INSTANCE_FIELD } from './utils/constants'; +import { providerQueryKey, api as helperApi } from '../../../helpers/api'; interface EditUniverseProps { uuid: string; @@ -101,6 +103,15 @@ export const EditUniverse: FC = ({ uuid, isViewMode }) => { } ); + const primaryProviderUuid = originalData?.universeDetails + ? getPrimaryCluster(originalData?.universeDetails)?.userIntent?.provider ?? '' + : ''; + const providerConfigQuery = useQuery( + providerQueryKey.detail(primaryProviderUuid), + () => helperApi.fetchProvider(primaryProviderUuid), + { enabled: !!primaryProviderUuid } + ); + const onCancel = () => browserHistory.push(`/universes/${uuid}`); const submitEditUniverse = async (finalPayload: UniverseConfigure) => { @@ -113,9 +124,18 @@ export const EditUniverse: FC = ({ uuid, isViewMode }) => { } }; - if (isUniverseLoading || isLoading || !originalData?.universeDetails) return ; + if ( + isUniverseLoading || + isLoading || + !originalData?.universeDetails || + providerConfigQuery.isLoading + ) + return ; - const initialFormData = getPrimaryFormData(originalData.universeDetails); + const initialFormData = getPrimaryFormData( + originalData.universeDetails, + providerConfigQuery.data + ); const onSubmit = async (formData: UniverseFormData) => { if (!_.isEqual(formData, initialFormData)) { diff --git a/managed/ui/src/redesign/features/universe/universe-form/form/fields/AccessKeysField/AccessKeysField.tsx b/managed/ui/src/redesign/features/universe/universe-form/form/fields/AccessKeysField/AccessKeysField.tsx index f1f3094f086d..00c116528a1f 100644 --- a/managed/ui/src/redesign/features/universe/universe-form/form/fields/AccessKeysField/AccessKeysField.tsx +++ b/managed/ui/src/redesign/features/universe/universe-form/form/fields/AccessKeysField/AccessKeysField.tsx @@ -8,6 +8,8 @@ import { YBLabel, YBSelectField } from '../../../../../../components'; import { AccessKey, UniverseFormData } from '../../../utils/dto'; import { ACCESS_KEY_FIELD, PROVIDER_FIELD } from '../../../utils/constants'; import { useFormFieldStyles } from '../../../universeMainStyle'; +import { YBProvider } from '../../../../../../../components/configRedesign/providerRedesign/types'; +import { ProviderCode } from '../../../../../../../components/configRedesign/providerRedesign/constants'; const useStyles = makeStyles((theme) => ({ overrideMuiSelectMenu: { @@ -43,14 +45,20 @@ export const AccessKeysField = ({ disabled }: AccessKeysFieldProps): ReactElemen const accessKeys = allAccessKeys.data.filter( (item: AccessKey) => item?.idKey?.providerUUID === provider?.uuid ); - if (accessKeys?.length) + if (accessKeys?.length) { setValue(ACCESS_KEY_FIELD, accessKeys[0]?.idKey.keyCode, { shouldValidate: true }); + } else { + setValue(ACCESS_KEY_FIELD, null, { shouldValidate: true }); + } }, [provider]); //only first time useEffectOnce(() => { - if (accessKeysList?.length && provider?.uuid) + if (accessKeysList?.length && provider?.uuid) { setValue(ACCESS_KEY_FIELD, accessKeysList[0]?.idKey.keyCode, { shouldValidate: true }); + } else { + setValue(ACCESS_KEY_FIELD, null, { shouldValidate: true }); + } }); return ( @@ -62,11 +70,12 @@ export const AccessKeysField = ({ disabled }: AccessKeysFieldProps): ReactElemen ; +export type ProviderMin = Pick & { + isOnPremManuallyProvisioned: boolean; +}; const getOptionLabel = (option: Record): string => option.name; export const ProvidersField = ({ @@ -32,9 +37,12 @@ export const ProvidersField = ({ onSuccess: (providers) => { // Pre-select provider by default if (_.isEmpty(getValues(PROVIDER_FIELD)) && providers.length >= 1) { + const provider = providers[0]; + const isOnPremManuallyProvisioned = + provider?.code === ProviderCode.ON_PREM && provider?.details?.skipProvisioning; setValue( PROVIDER_FIELD, - { code: providers[0]?.code, uuid: providers[0]?.uuid }, + { code: provider?.code, uuid: provider?.uuid, isOnPremManuallyProvisioned }, { shouldValidate: true } ); } @@ -54,7 +62,13 @@ export const ProvidersField = ({ const handleChange = (e: ChangeEvent<{}>, option: any) => { if (option) { const { code, uuid } = option; - setValue(PROVIDER_FIELD, { code, uuid }, { shouldValidate: true }); + const isOnPremManuallyProvisioned = + option?.code === ProviderCode.ON_PREM && option?.details?.skipProvisioning; + setValue( + PROVIDER_FIELD, + { code, uuid, isOnPremManuallyProvisioned }, + { shouldValidate: true } + ); } else { setValue(PROVIDER_FIELD, DEFAULT_CLOUD_CONFIG.provider, { shouldValidate: true }); } diff --git a/managed/ui/src/redesign/features/universe/universe-form/form/sections/cloud/CloudConfiguration.tsx b/managed/ui/src/redesign/features/universe/universe-form/form/sections/cloud/CloudConfiguration.tsx index eff965f313c7..0ec7fd7ad577 100644 --- a/managed/ui/src/redesign/features/universe/universe-form/form/sections/cloud/CloudConfiguration.tsx +++ b/managed/ui/src/redesign/features/universe/universe-form/form/sections/cloud/CloudConfiguration.tsx @@ -36,7 +36,7 @@ export const CloudConfiguration = ({ runtimeConfigs }: UniverseFormConfiguration const { t } = useTranslation(); const isLargeDevice = useMediaQuery('(min-width:1400px)'); - const provider: YBProvider = useWatch({ name: PROVIDER_FIELD }); + const provider = useWatch({ name: PROVIDER_FIELD }); const providerRuntimeConfigQuery = useQuery( runtimeConfigQueryKey.providerScope(provider?.uuid), @@ -111,11 +111,7 @@ export const CloudConfiguration = ({ runtimeConfigs }: UniverseFormConfiguration - + {isPrimary && isGeoPartitionEnabled && ( diff --git a/managed/ui/src/redesign/features/universe/universe-form/utils/helpers.ts b/managed/ui/src/redesign/features/universe/universe-form/utils/helpers.ts index 96fbc12cd524..b6960ea18f3b 100644 --- a/managed/ui/src/redesign/features/universe/universe-form/utils/helpers.ts +++ b/managed/ui/src/redesign/features/universe/universe-form/utils/helpers.ts @@ -35,6 +35,8 @@ import { MIN_PG_SUPPORTED_PREVIEW_VERSION, MIN_PG_SUPPORTED_STABLE_VERSION } from '../../../../helpers/constants'; +import { YBProvider } from '../../../../../components/configRedesign/providerRedesign/types'; +import { ProviderCode } from '../../../../../components/configRedesign/providerRedesign/constants'; export const transitToUniverse = (universeUUID?: string) => universeUUID @@ -60,11 +62,11 @@ export const getCurrentVersion = (universeData: UniverseDetails) => { export const getUniverseName = (universeData: UniverseDetails) => _.get(getClusterByType(universeData, ClusterType.PRIMARY), 'userIntent.universeName'); -export const getPrimaryFormData = (universeData: UniverseDetails) => - getFormData(universeData, ClusterType.PRIMARY); +export const getPrimaryFormData = (universeData: UniverseDetails, providerConfig?: YBProvider) => + getFormData(universeData, ClusterType.PRIMARY, providerConfig); -export const getAsyncFormData = (universeData: UniverseDetails) => - getFormData(universeData, ClusterType.ASYNC); +export const getAsyncFormData = (universeData: UniverseDetails, providerConfig?: YBProvider) => + getFormData(universeData, ClusterType.ASYNC, providerConfig); //returns fields needs to be copied from Primary to Async in Create+RR flow export const getPrimaryInheritedValues = (formData: UniverseFormData) => @@ -171,7 +173,11 @@ export const isPGEnabledFromIntent = (intent: UserIntent) => { }; //Transform universe data to form data -export const getFormData = (universeData: UniverseDetails, clusterType: ClusterType) => { +export const getFormData = ( + universeData: UniverseDetails, + clusterType: ClusterType, + providerConfig?: YBProvider +) => { const { communicationPorts, encryptionAtRestConfig, rootCA } = universeData; const cluster = getClusterByType(universeData, clusterType); @@ -184,7 +190,11 @@ export const getFormData = (universeData: UniverseDetails, clusterType: ClusterT universeName: userIntent.universeName, provider: { code: userIntent.providerType, - uuid: userIntent.provider + uuid: userIntent.provider, + isOnPremManuallyProvisioned: + (providerConfig?.code === ProviderCode.ON_PREM && + providerConfig.details?.skipProvisioning) ?? + false }, regionList: userIntent.regionList, numNodes: userIntent.numNodes,