From 99cb9fa61d8f4c573be19241e3678b4057951cb6 Mon Sep 17 00:00:00 2001 From: Fabio Silva Date: Tue, 2 Jul 2024 23:49:50 +0100 Subject: [PATCH 1/7] chore: rename "dbVersions" to "dbEngineData" --- .../steps/first/first-step.tsx | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/first-step.tsx b/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/first-step.tsx index 637d1aa1b..b503f53d8 100644 --- a/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/first-step.tsx +++ b/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/first-step.tsx @@ -57,7 +57,7 @@ export const FirstStep = ({ loadingDefaultsForEdition }: StepProps) => { useDbEngines(dbNamespace); const dbEngine = dbTypeToDbEngine(dbType); - const [dbVersions, setDbVersions] = useState( + const [dbEngineData, setDbEngineData] = useState( dbEngines.find((engine) => engine.type === dbEngine) ); @@ -70,23 +70,23 @@ export const FirstStep = ({ loadingDefaultsForEdition }: StepProps) => { [setValue] ); - const setDbVersionsForEngine = useCallback(() => { - const newVersions = dbEngines.find((engine) => engine.type === dbEngine); + const setDbEngineDataForEngineType = useCallback(() => { + const newEngineData = dbEngines.find((engine) => engine.type === dbEngine); - setDbVersions(newVersions); + setDbEngineData(newEngineData); }, [dbEngine, dbEngines]); const updateDbVersions = useCallback(() => { const { isDirty: dbVersionDirty } = getFieldState( DbWizardFormFields.dbVersion ); - setDbVersionsForEngine(); + setDbEngineDataForEngineType(); // Safety check if ( dbVersionDirty || - !dbVersions || - !dbVersions.availableVersions.engine.length + !dbEngineData || + !dbEngineData.availableVersions.engine.length ) { return; } @@ -95,7 +95,7 @@ export const FirstStep = ({ loadingDefaultsForEdition }: StepProps) => { ((mode === 'edit' || mode === 'restoreFromBackup') && !dbVersion) || mode === 'new' ) { - const recommendedVersion = dbVersions.availableVersions.engine + const recommendedVersion = dbEngineData.availableVersions.engine .slice() .reverse() .find((version) => version.status === DbEngineToolStatus.RECOMMENDED); @@ -104,15 +104,15 @@ export const FirstStep = ({ loadingDefaultsForEdition }: StepProps) => { DbWizardFormFields.dbVersion, recommendedVersion ? recommendedVersion.version - : dbVersions.availableVersions.engine[0].version + : dbEngineData.availableVersions.engine[0].version ); } }, [ dbVersion, - dbVersions, + dbEngineData, getFieldState, mode, - setDbVersionsForEngine, + setDbEngineDataForEngineType, setValue, ]); @@ -200,8 +200,8 @@ export const FirstStep = ({ loadingDefaultsForEdition }: StepProps) => { }, [clusterInfo]); useEffect(() => { - setDbVersionsForEngine(); - }, [setDbVersionsForEngine]); + setDbEngineDataForEngineType(); + }, [setDbEngineDataForEngineType]); useEffect(() => { const { isTouched: k8sNamespaceTouched } = getFieldState( @@ -285,7 +285,7 @@ export const FirstStep = ({ loadingDefaultsForEdition }: StepProps) => { disabled: mode === 'restoreFromBackup', }} > - {dbVersions?.availableVersions.engine.map((version) => ( + {dbEngineData?.availableVersions.engine.map((version) => ( {version.version} From e8e77e0ca8af3a1f23ae5e6922dee56f0592c28d Mon Sep 17 00:00:00 2001 From: Fabio Silva Date: Wed, 3 Jul 2024 01:31:34 +0100 Subject: [PATCH 2/7] feat: filter out invalid engine up/downgrades --- .../steps/first/first-step.tsx | 20 ++++++++++-- .../database-form-body/steps/first/utils.ts | 32 +++++++++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/first-step.tsx b/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/first-step.tsx index b503f53d8..a1293409a 100644 --- a/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/first-step.tsx +++ b/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/first-step.tsx @@ -37,11 +37,17 @@ import { useDatabasePageMode } from '../../../useDatabasePageMode.ts'; import { StepHeader } from '../step-header/step-header.tsx'; import { DEFAULT_NODES } from './first-step.constants.ts'; import { Messages } from './first-step.messages.ts'; -import { generateShortUID } from './utils.ts'; +import { + changeAvailableDbVersionsForDbEngine, + generateShortUID, +} from './utils.ts'; +import { useDatabasePageDefaultValues } from '../../../useDatabaseFormDefaultValues.ts'; export const FirstStep = ({ loadingDefaultsForEdition }: StepProps) => { const mode = useDatabasePageMode(); - + const { + defaultValues: { [DbWizardFormFields.dbVersion]: defaultDbVersion }, + } = useDatabasePageDefaultValues(mode); const { watch, setValue, getFieldState, resetField, getValues, trigger } = useFormContext(); @@ -73,8 +79,16 @@ export const FirstStep = ({ loadingDefaultsForEdition }: StepProps) => { const setDbEngineDataForEngineType = useCallback(() => { const newEngineData = dbEngines.find((engine) => engine.type === dbEngine); + if (newEngineData && mode === 'edit') { + const validVersions = changeAvailableDbVersionsForDbEngine( + newEngineData, + defaultDbVersion + ); + newEngineData.availableVersions.engine = validVersions; + } + setDbEngineData(newEngineData); - }, [dbEngine, dbEngines]); + }, [dbEngine, dbEngines, defaultDbVersion]); const updateDbVersions = useCallback(() => { const { isDirty: dbVersionDirty } = getFieldState( diff --git a/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/utils.ts b/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/utils.ts index 85321a7d0..1d818f26c 100644 --- a/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/utils.ts +++ b/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/utils.ts @@ -11,6 +11,9 @@ // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and +import { gte, coerce } from 'semver'; +import { DbEngine, DbEngineType } from 'shared-types/dbEngines.types'; + // limitations under the License. export const generateShortUID = (): string => { const firstPart = `000${((Math.random() * 46656) | 0).toString(36)}`.slice( @@ -22,3 +25,32 @@ export const generateShortUID = (): string => { return `${firstPart}${secondPart}`.slice(0, 3); }; + +export const changeAvailableDbVersionsForDbEngine = ( + dbEngine: DbEngine, + currentVersion: string +) => { + const versions = dbEngine.availableVersions.engine || []; + const currentSemverVersion = coerce(currentVersion); + const dbType = dbEngine.type; + + if (!currentSemverVersion) { + return versions; + } + + const currentMajor = currentSemverVersion.major; + + // If the engine is PSMDB or PG, major versions are ruled out + if ([DbEngineType.PSMDB, DbEngineType.POSTGRESQL].includes(dbType)) { + return versions.filter(({ version }) => { + const semverVersion = coerce(version); + return semverVersion ? semverVersion.major === currentMajor : true; + }); + } + + // If not, we just filter out downgrades + return versions.filter(({ version }) => { + const semverVersion = coerce(version); + return semverVersion ? gte(semverVersion, currentSemverVersion) : true; + }); +}; From fe1cdef3a0dd393ca6e7436a56acc284c817d9c0 Mon Sep 17 00:00:00 2001 From: Fabio Silva Date: Wed, 3 Jul 2024 01:33:53 +0100 Subject: [PATCH 3/7] fix: missing semver coerce --- .../namespaces/namespace-details/cluster-status-table.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/apps/everest/src/pages/settings/namespaces/namespace-details/cluster-status-table.tsx b/ui/apps/everest/src/pages/settings/namespaces/namespace-details/cluster-status-table.tsx index 08e709fe0..55c1d7ba4 100644 --- a/ui/apps/everest/src/pages/settings/namespaces/namespace-details/cluster-status-table.tsx +++ b/ui/apps/everest/src/pages/settings/namespaces/namespace-details/cluster-status-table.tsx @@ -3,7 +3,6 @@ import { MRT_ColumnDef } from 'material-react-table'; import { Button } from '@mui/material'; import { Table } from '@percona/ui-lib'; import semverCoerce from 'semver/functions/coerce'; -import semverMajor from 'semver/functions/major'; import { DbEngineToolStatus, OperatorUpgradeDb, @@ -73,9 +72,10 @@ const ClusterStatusTable = ({ const currentMajor = currenEngineVersion.major; const availableVersions = dbEngine.availableVersions.engine .filter(({ version, status }) => { + const semver = semverCoerce(version); return ( status === DbEngineToolStatus.RECOMMENDED && - semverMajor(version) === currentMajor + semver?.major === currentMajor ); }) .map(({ version }) => version); From 00a25cd1ecc4a2853d8a092d7e09706ae0eff9a0 Mon Sep 17 00:00:00 2001 From: Fabio Silva Date: Wed, 3 Jul 2024 01:35:35 +0100 Subject: [PATCH 4/7] chore: change comment --- .../pages/database-form/database-form-body/steps/first/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/utils.ts b/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/utils.ts index 1d818f26c..a768d44e2 100644 --- a/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/utils.ts +++ b/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/utils.ts @@ -40,7 +40,7 @@ export const changeAvailableDbVersionsForDbEngine = ( const currentMajor = currentSemverVersion.major; - // If the engine is PSMDB or PG, major versions are ruled out + // If the engine is PSMDB or PG, major version up/downgrades are ruled out if ([DbEngineType.PSMDB, DbEngineType.POSTGRESQL].includes(dbType)) { return versions.filter(({ version }) => { const semverVersion = coerce(version); From ea4b9d06567e6d5afe242af3b69264b80da2151e Mon Sep 17 00:00:00 2001 From: Fabio Silva Date: Wed, 3 Jul 2024 11:40:18 +0100 Subject: [PATCH 5/7] chore: add test to changeAvailableDbVersionsForDbEngine --- .../steps/first/utils.test.ts | 90 +++++++++++++++++++ .../database-form-body/steps/first/utils.ts | 18 ++-- 2 files changed, 100 insertions(+), 8 deletions(-) create mode 100644 ui/apps/everest/src/pages/database-form/database-form-body/steps/first/utils.test.ts diff --git a/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/utils.test.ts b/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/utils.test.ts new file mode 100644 index 000000000..3dec31cca --- /dev/null +++ b/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/utils.test.ts @@ -0,0 +1,90 @@ +import { DbEngineType } from 'shared-types/dbEngines.types'; +import { changeAvailableDbVersionsForDbEngine } from './utils'; + +describe('Wizard first step::utils', () => { + describe('changeAvailableDbVersionsForDbEngine', () => { + it('should rule out downgrades', () => { + expect( + changeAvailableDbVersionsForDbEngine( + { + availableVersions: { + engine: [ + { version: '5.0.0' }, + { version: '4.0.0' }, + { version: '4.3.9' }, + { version: '4.4.1' }, + ], + }, + type: DbEngineType.PXC, + }, + '4.4.0' + ) + ).toEqual([{ version: '5.0.0' }, { version: '4.4.1' }]); + }); + + it('should coerce PG versions to semver', () => { + expect( + changeAvailableDbVersionsForDbEngine( + { + availableVersions: { + engine: [ + { version: '13.0' }, + { version: '14.0' }, + { version: '14.1' }, + { version: '15.0' }, + ], + }, + type: DbEngineType.POSTGRESQL, + }, + '14.1' + ) + ).toEqual([{ version: '14.1' }]); + }); + + it('should allow major upgrades for PXC', () => { + expect( + changeAvailableDbVersionsForDbEngine( + { + availableVersions: { + engine: [ + { version: '5.0.0' }, + { version: '4.0.0' }, + { version: '4.3.9' }, + { version: '4.4.1' }, + ], + }, + type: DbEngineType.PXC, + }, + '4.4.0' + ) + ).toEqual([{ version: '5.0.0' }, { version: '4.4.1' }]); + }); + + it('should rule out major upgrades/downgrades for PSMDB/PG', () => { + expect( + changeAvailableDbVersionsForDbEngine( + { + availableVersions: { + engine: [ + { version: '1.0.0' }, + { version: '1.2.0' }, + { version: '2.3.3' }, + { version: '2.5.0' }, + { version: '2.9.0' }, + { version: '3.1.0' }, + { version: '4.3.0' }, + { version: '5.4.1' }, + ], + }, + type: DbEngineType.PSMDB, + }, + '2.3.0' + ) + ).toEqual([ + { version: '2.3.3' }, + { version: '2.5.0' }, + { version: '2.9.0' }, + ]); + }); + }); +}); diff --git a/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/utils.ts b/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/utils.ts index a768d44e2..325b71571 100644 --- a/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/utils.ts +++ b/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/utils.ts @@ -30,7 +30,7 @@ export const changeAvailableDbVersionsForDbEngine = ( dbEngine: DbEngine, currentVersion: string ) => { - const versions = dbEngine.availableVersions.engine || []; + let versions = dbEngine.availableVersions.engine || []; const currentSemverVersion = coerce(currentVersion); const dbType = dbEngine.type; @@ -40,17 +40,19 @@ export const changeAvailableDbVersionsForDbEngine = ( const currentMajor = currentSemverVersion.major; - // If the engine is PSMDB or PG, major version up/downgrades are ruled out + // Filter out downgrades + versions = versions.filter(({ version }) => { + const semverVersion = coerce(version); + return semverVersion ? gte(semverVersion, currentSemverVersion) : true; + }); + + // If the engine is PSMDB or PG, major version upgrades are also ruled out if ([DbEngineType.PSMDB, DbEngineType.POSTGRESQL].includes(dbType)) { - return versions.filter(({ version }) => { + versions = versions.filter(({ version }) => { const semverVersion = coerce(version); return semverVersion ? semverVersion.major === currentMajor : true; }); } - // If not, we just filter out downgrades - return versions.filter(({ version }) => { - const semverVersion = coerce(version); - return semverVersion ? gte(semverVersion, currentSemverVersion) : true; - }); + return versions; }; From c5bc4a4409bbae8b107564490ed7526ce5e23274 Mon Sep 17 00:00:00 2001 From: Fabio Silva Date: Wed, 3 Jul 2024 11:41:34 +0100 Subject: [PATCH 6/7] chore: rename utils function --- .../database-form-body/steps/first/first-step.tsx | 4 ++-- .../database-form-body/steps/first/utils.test.ts | 12 ++++++------ .../database-form-body/steps/first/utils.ts | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/first-step.tsx b/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/first-step.tsx index a1293409a..d50b341c2 100644 --- a/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/first-step.tsx +++ b/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/first-step.tsx @@ -38,7 +38,7 @@ import { StepHeader } from '../step-header/step-header.tsx'; import { DEFAULT_NODES } from './first-step.constants.ts'; import { Messages } from './first-step.messages.ts'; import { - changeAvailableDbVersionsForDbEngine, + filterAvailableDbVersionsForDbEngineEdition, generateShortUID, } from './utils.ts'; import { useDatabasePageDefaultValues } from '../../../useDatabaseFormDefaultValues.ts'; @@ -80,7 +80,7 @@ export const FirstStep = ({ loadingDefaultsForEdition }: StepProps) => { const newEngineData = dbEngines.find((engine) => engine.type === dbEngine); if (newEngineData && mode === 'edit') { - const validVersions = changeAvailableDbVersionsForDbEngine( + const validVersions = filterAvailableDbVersionsForDbEngineEdition( newEngineData, defaultDbVersion ); diff --git a/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/utils.test.ts b/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/utils.test.ts index 3dec31cca..4f05b7057 100644 --- a/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/utils.test.ts +++ b/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/utils.test.ts @@ -1,11 +1,11 @@ import { DbEngineType } from 'shared-types/dbEngines.types'; -import { changeAvailableDbVersionsForDbEngine } from './utils'; +import { filterAvailableDbVersionsForDbEngineEdition } from './utils'; describe('Wizard first step::utils', () => { - describe('changeAvailableDbVersionsForDbEngine', () => { + describe('filterAvailableDbVersionsForDbEngineEdition', () => { it('should rule out downgrades', () => { expect( - changeAvailableDbVersionsForDbEngine( + filterAvailableDbVersionsForDbEngineEdition( { availableVersions: { engine: [ @@ -24,7 +24,7 @@ describe('Wizard first step::utils', () => { it('should coerce PG versions to semver', () => { expect( - changeAvailableDbVersionsForDbEngine( + filterAvailableDbVersionsForDbEngineEdition( { availableVersions: { engine: [ @@ -43,7 +43,7 @@ describe('Wizard first step::utils', () => { it('should allow major upgrades for PXC', () => { expect( - changeAvailableDbVersionsForDbEngine( + filterAvailableDbVersionsForDbEngineEdition( { availableVersions: { engine: [ @@ -62,7 +62,7 @@ describe('Wizard first step::utils', () => { it('should rule out major upgrades/downgrades for PSMDB/PG', () => { expect( - changeAvailableDbVersionsForDbEngine( + filterAvailableDbVersionsForDbEngineEdition( { availableVersions: { engine: [ diff --git a/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/utils.ts b/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/utils.ts index 325b71571..445167017 100644 --- a/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/utils.ts +++ b/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/utils.ts @@ -26,7 +26,7 @@ export const generateShortUID = (): string => { return `${firstPart}${secondPart}`.slice(0, 3); }; -export const changeAvailableDbVersionsForDbEngine = ( +export const filterAvailableDbVersionsForDbEngineEdition = ( dbEngine: DbEngine, currentVersion: string ) => { From cd0e33a89faef3edd7f7ebe5a81daa7c48de6443 Mon Sep 17 00:00:00 2001 From: Fabio Silva Date: Wed, 3 Jul 2024 16:02:42 +0100 Subject: [PATCH 7/7] fix: test typings --- .../steps/first/utils.test.ts | 106 ++++++++---------- 1 file changed, 45 insertions(+), 61 deletions(-) diff --git a/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/utils.test.ts b/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/utils.test.ts index 4f05b7057..d81101db0 100644 --- a/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/utils.test.ts +++ b/ui/apps/everest/src/pages/database-form/database-form-body/steps/first/utils.test.ts @@ -1,90 +1,74 @@ -import { DbEngineType } from 'shared-types/dbEngines.types'; +import { DbEngine, DbEngineType } from 'shared-types/dbEngines.types'; import { filterAvailableDbVersionsForDbEngineEdition } from './utils'; +const generateDbEngineWithVersions = ( + versions: string[], + type: DbEngineType +): DbEngine => + ({ + availableVersions: { + engine: versions.map((version) => ({ version })), + }, + type, + }) as unknown as DbEngine; + describe('Wizard first step::utils', () => { describe('filterAvailableDbVersionsForDbEngineEdition', () => { it('should rule out downgrades', () => { expect( filterAvailableDbVersionsForDbEngineEdition( - { - availableVersions: { - engine: [ - { version: '5.0.0' }, - { version: '4.0.0' }, - { version: '4.3.9' }, - { version: '4.4.1' }, - ], - }, - type: DbEngineType.PXC, - }, + generateDbEngineWithVersions( + ['5.0.0', '4.0.0', '4.3.9', '4.4.1'], + DbEngineType.PXC + ), '4.4.0' - ) - ).toEqual([{ version: '5.0.0' }, { version: '4.4.1' }]); + ).map(({ version }) => version) + ).toEqual(['5.0.0', '4.4.1']); }); it('should coerce PG versions to semver', () => { expect( filterAvailableDbVersionsForDbEngineEdition( - { - availableVersions: { - engine: [ - { version: '13.0' }, - { version: '14.0' }, - { version: '14.1' }, - { version: '15.0' }, - ], - }, - type: DbEngineType.POSTGRESQL, - }, + generateDbEngineWithVersions( + ['13.0', '14.0', '14.1', '15.0'], + DbEngineType.POSTGRESQL + ), '14.1' - ) - ).toEqual([{ version: '14.1' }]); + ).map(({ version }) => version) + ).toEqual(['14.1']); }); it('should allow major upgrades for PXC', () => { expect( filterAvailableDbVersionsForDbEngineEdition( - { - availableVersions: { - engine: [ - { version: '5.0.0' }, - { version: '4.0.0' }, - { version: '4.3.9' }, - { version: '4.4.1' }, - ], - }, - type: DbEngineType.PXC, - }, + generateDbEngineWithVersions( + ['5.0.0', '4.0.0', '4.3.9', '4.4.1'], + DbEngineType.PXC + ), '4.4.0' - ) - ).toEqual([{ version: '5.0.0' }, { version: '4.4.1' }]); + ).map(({ version }) => version) + ).toEqual(['5.0.0', '4.4.1']); }); it('should rule out major upgrades/downgrades for PSMDB/PG', () => { expect( filterAvailableDbVersionsForDbEngineEdition( - { - availableVersions: { - engine: [ - { version: '1.0.0' }, - { version: '1.2.0' }, - { version: '2.3.3' }, - { version: '2.5.0' }, - { version: '2.9.0' }, - { version: '3.1.0' }, - { version: '4.3.0' }, - { version: '5.4.1' }, - ], - }, - type: DbEngineType.PSMDB, - }, + generateDbEngineWithVersions( + [ + '1.0.0', + '1.2.0', + '2.3.3', + '2.5.0', + '2.9.0', + '3.1.0', + '4.3.0', + '5.4.1', + ], + DbEngineType.PSMDB + ), '2.3.0' - ) - ).toEqual([ - { version: '2.3.3' }, - { version: '2.5.0' }, - { version: '2.9.0' }, - ]); + ).map(({ version }) => version) + ).toEqual(['2.3.3', '2.5.0', '2.9.0']); }); }); });