From afce6ad2f397e4e40e25945dc237903d1ece5ce8 Mon Sep 17 00:00:00 2001 From: Jethro Mak <88681329+Jethro-M@users.noreply.github.com> Date: Tue, 10 Sep 2024 16:25:33 -0400 Subject: [PATCH] [PLAT-14158] Support multiple transactional xCluster configs per universe on YBA UI Summary: YBDB support for multiple transactional xCluster configs per universe was added in: D33634 YBA backend support for multiple transactional xCluster configs per universe was added in: D37088 This diff adds YBA UI support for multiple transactional xCluster configs per universe. In this diff, we remove the logic for disabling xCluster config creation when either participant universe is already part of a transactional xCluster config. This diff also adds a DR config list page since we can now have more than one DR config per universe. Test Plan: Verify user can create more than one transactional xCluster config. - Test both transactional xCluster config and DR Verify the DR config list shows all DR configs that the current universe is participating in. {F280727} {F280730} {F280731} {F283536} Reviewers: rmadhavan, vbansal, hzare, cwang, mkazerooni Reviewed By: rmadhavan Subscribers: yugaware Differential Revision: https://phorge.dev.yugabyte.com/D37701 --- .../UniverseDetail/UniverseDetail.js | 4 +- .../components/xcluster/ReplicationUtils.tsx | 41 +- .../xcluster/XClusterConfigList.tsx | 2 +- .../xcluster/XClusterReplication.tsx | 40 +- .../ui/src/components/xcluster/constants.ts | 4 + .../createConfig/SelectTargetUniverseStep.tsx | 4 +- .../disasterRecovery/DrConfigCard.module.scss | 91 ++ .../disasterRecovery/DrConfigCard.tsx | 91 ++ .../disasterRecovery/DrConfigList.module.scss | 50 ++ .../disasterRecovery/DrConfigList.tsx | 203 +++++ .../xcluster/disasterRecovery/DrPanel.tsx | 807 +++++++++--------- .../createConfig/ConfigurePitrStep.tsx | 10 +- .../createConfig/CreateConfigModal.tsx | 4 +- .../createConfig/SelectTargetUniverseStep.tsx | 118 ++- .../drConfig/DrParticipantCard.tsx | 9 +- managed/ui/src/components/xcluster/styles.ts | 4 + managed/ui/src/pages/DrPanel.tsx | 16 + managed/ui/src/routes.js | 2 + managed/ui/src/translations/en.json | 12 +- 19 files changed, 996 insertions(+), 516 deletions(-) create mode 100644 managed/ui/src/components/xcluster/disasterRecovery/DrConfigCard.module.scss create mode 100644 managed/ui/src/components/xcluster/disasterRecovery/DrConfigCard.tsx create mode 100644 managed/ui/src/components/xcluster/disasterRecovery/DrConfigList.module.scss create mode 100644 managed/ui/src/components/xcluster/disasterRecovery/DrConfigList.tsx create mode 100644 managed/ui/src/pages/DrPanel.tsx diff --git a/managed/ui/src/components/universes/UniverseDetail/UniverseDetail.js b/managed/ui/src/components/universes/UniverseDetail/UniverseDetail.js index c162e21626f8..28f3f7d53dc8 100644 --- a/managed/ui/src/components/universes/UniverseDetail/UniverseDetail.js +++ b/managed/ui/src/components/universes/UniverseDetail/UniverseDetail.js @@ -65,12 +65,12 @@ import { UniverseState, getUniverseStatus, SoftwareUpgradeState } from '../helpe import { TaskDetailBanner } from '../../../redesign/features/tasks/components/TaskDetailBanner'; import { RbacValidator } from '../../../redesign/features/rbac/common/RbacApiPermValidator'; import { ApiPermissionMap } from '../../../redesign/features/rbac/ApiAndUserPermMapping'; -import { DrPanel } from '../../xcluster/disasterRecovery/DrPanel'; import { TroubleshootRegistrationDetails } from '../TroubleshootUniverse/TroubleshootRegistrationDetails'; import { VM_PATCHING_RUNTIME_CONFIG, isImgBundleSupportedByProvider } from '../../configRedesign/providerRedesign/components/linuxVersionCatalog/LinuxVersionUtils'; +import { DrConfigList } from '../../xcluster/disasterRecovery/DrConfigList'; import { AppName } from '../../../redesign/features/Troubleshooting/TroubleshootingDashboard'; import { RuntimeConfigKey, UNIVERSE_TASKS } from '../../../redesign/helpers/constants'; @@ -639,7 +639,7 @@ class UniverseDetail extends Component { mountOnEnter={true} unmountOnExit={true} > - + ), isNotHidden(currentCustomer.data.features, 'universes.details.replication') && ( diff --git a/managed/ui/src/components/xcluster/ReplicationUtils.tsx b/managed/ui/src/components/xcluster/ReplicationUtils.tsx index f7b50ec5ddb2..1098c7ad7902 100644 --- a/managed/ui/src/components/xcluster/ReplicationUtils.tsx +++ b/managed/ui/src/components/xcluster/ReplicationUtils.tsx @@ -517,6 +517,14 @@ export const getXClusterConfigUuids = (universe: Universe | undefined) => ({ targetXClusterConfigUuids: universe?.universeDetails?.xclusterInfo?.targetXClusterConfigs ?? [] }); +/* + * Returns the UUIDs for all xCluster DR configs associated with the provided universe. + */ +export const getDrConfigUuids = (universe: Universe | undefined) => ({ + sourceDrConfigUuids: universe?.drConfigUuidsAsSource ?? [], + targetDrConfigUuids: universe?.drConfigUuidsAsTarget ?? [] +}); + export const hasLinkedXClusterConfig = (universes: Universe[]) => universes.some((universe) => { const { sourceXClusterConfigUuids, targetXClusterConfigUuids } = getXClusterConfigUuids( @@ -647,26 +655,39 @@ export const isTableToggleable = ( export const shouldAutoIncludeIndexTables = (xClusterConfig: XClusterConfig | undefined) => xClusterConfig ? xClusterConfig.type === XClusterConfigType.TXN : true; -export const getIsTransactionalAtomicityEnabled = ( +/** + * If targetUniverse is undefined, then we just consider whether the source universe supports + * txn atomicity. If both source and target universes are defined, then we will consider both. + */ +export const getIsTransactionalAtomicitySupported = ( sourceUniverse: Universe, targetUniverse?: Universe ) => { - const ybSoftwareVersion = getPrimaryCluster(sourceUniverse.universeDetails.clusters)?.userIntent - .ybSoftwareVersion; - const participatingUniverses = targetUniverse - ? [sourceUniverse, targetUniverse] - : [sourceUniverse]; - const participantsHaveLinkedXClusterConfig = hasLinkedXClusterConfig(participatingUniverses); + const sourceYbSoftwareVersion = getPrimaryCluster(sourceUniverse.universeDetails.clusters) + ?.userIntent.ybSoftwareVersion; + const targetYbSoftwareVersion = targetUniverse + ? getPrimaryCluster(targetUniverse.universeDetails.clusters)?.userIntent.ybSoftwareVersion + : ''; + return ( - !!ybSoftwareVersion && + !!sourceYbSoftwareVersion && compareYBSoftwareVersions({ versionA: TRANSACTIONAL_ATOMICITY_YB_SOFTWARE_VERSION_THRESHOLD, - versionB: ybSoftwareVersion, + versionB: sourceYbSoftwareVersion, options: { suppressFormatError: true } }) < 0 && - !participantsHaveLinkedXClusterConfig + (!targetUniverse || + (targetUniverse && + !!targetYbSoftwareVersion && + compareYBSoftwareVersions({ + versionA: TRANSACTIONAL_ATOMICITY_YB_SOFTWARE_VERSION_THRESHOLD, + versionB: targetYbSoftwareVersion, + options: { + suppressFormatError: true + } + }) < 0)) ); }; diff --git a/managed/ui/src/components/xcluster/XClusterConfigList.tsx b/managed/ui/src/components/xcluster/XClusterConfigList.tsx index c7d463b4bcdc..4f0a5434baf0 100644 --- a/managed/ui/src/components/xcluster/XClusterConfigList.tsx +++ b/managed/ui/src/components/xcluster/XClusterConfigList.tsx @@ -63,7 +63,6 @@ export function XClusterConfigList({ currentUniverseUUID }: Props) { useInterval(() => { xClusterConfigQueries.forEach((xClusterConfig) => { if ( - !xClusterConfig.data?.usedForDr && xClusterConfig.data?.status && _.includes(TRANSITORY_XCLUSTER_CONFIG_STATUSES, xClusterConfig.data.status) ) { @@ -103,6 +102,7 @@ export function XClusterConfigList({ currentUniverseUUID }: Props) { const shownXClusterConfigQueries = shouldShowDrXClusterConfigs ? xClusterConfigQueries : xClusterConfigQueries.filter((xClusterConfigQuery) => !xClusterConfigQuery.data?.usedForDr); + return ( <>