Fix removing array type values in machine pool configs (#10741)

* fix removing array type values in machine pool configs

* update comment

* lint

* limit custom merge helper to vsphere

---------

Co-authored-by: Mo Mesgin <mmesgin@Mos-M2-MacBook-Pro.local>
This commit is contained in:
momesgin 2024-04-18 08:25:12 -07:00 committed by GitHub
parent 3bbb7b2d41
commit 4af6e261d0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 65 additions and 2 deletions

View File

@ -26,6 +26,7 @@ import {
} from '@shell/utils/object';
import { allHash } from '@shell/utils/promise';
import { sortBy } from '@shell/utils/sort';
import { vspherePoolConfigMerge } from '@shell/machine-config/vmwarevsphere-pool-config-merge';
import { compare, sortable } from '@shell/utils/version';
import { isHarvesterSatisfiesVersion } from '@shell/utils/cluster';
@ -66,6 +67,8 @@ const HARVESTER = 'harvester';
const HARVESTER_CLOUD_PROVIDER = 'harvester-cloud-provider';
const NETBIOS_TRUNCATION_LENGTH = 15;
const VMWARE_VSPHERE = 'vmwarevsphere';
/**
* Classes to be adopted by the node badges in Machine pools
*/
@ -1104,7 +1107,7 @@ export default {
},
};
if (this.provider === 'vmwarevsphere') {
if (this.provider === VMWARE_VSPHERE) {
pool.pool.machineOS = 'linux';
}
@ -1149,8 +1152,13 @@ export default {
// We don't allow the user to edit any of the fields in metadata from the UI so it's safe to override it with the
// metadata defined by the latest backend value. This is primarily used to ensure the resourceVersion is up to date.
delete clonedCurrentConfig.metadata;
if (this.provider === VMWARE_VSPHERE) {
machinePool.config = vspherePoolConfigMerge(clonedLatestConfig, clonedCurrentConfig);
} else {
machinePool.config = merge(clonedLatestConfig, clonedCurrentConfig);
}
}
},
async saveMachinePools(hookContext) {

View File

@ -0,0 +1,30 @@
import { vspherePoolConfigMerge } from '@shell/machine-config/vmwarevsphere-pool-config-merge';
describe('vspherePoolConfigMerge', () => {
const testCases: Array<[object?, object?, object?]> = [
// Some array test cases, an array from the first object should be replaced with the array from the second object
[{ a: ['one'] }, { a: [] }, { a: [] }],
[{ a: ['one', 'two'] }, { a: ['one', 'two', 'three'] }, { a: ['one', 'two', 'three'] }],
[{ a: ['one', 'two'], b: ['three', 'four'] }, { a: ['one'], b: [] }, { a: ['one'], b: [] }],
[{
a: ['one', 'two'], b: ['three', 'four'], c: 'five'
}, { a: ['one'], b: [] }, {
a: ['one'], b: [], c: 'five'
}],
// Some other test cases
[{ a: 'one' }, { b: 'two' }, { a: 'one', b: 'two' }],
[{ a: 'one' }, { a: '', b: 'two' }, { a: '', b: 'two' }],
[{ a: 'one', b: 'two' }, { a: 1, c: { d: null } }, {
a: 1, b: 'two', c: { d: null }
}],
[undefined, undefined, {}],
[{}, undefined, {}],
[undefined, {}, {}],
];
it.each(testCases)('should merge properly', (obj1, obj2, expected) => {
const result = vspherePoolConfigMerge(obj1, obj2);
expect(result).toStrictEqual(expected);
});
});

View File

@ -0,0 +1,25 @@
import mergeWith from 'lodash/mergeWith';
/**
* Helper function to alter Lodash merge function default behaviour on merging arrays while updating machine pool configuration.
*
* In rke2.vue, the syncMachineConfigWithLatest function updates machine pool configuration by
* merging the latest configuration received from the backend with the current configuration updated by the user.
* However, Lodash's merge function treats arrays like object so index values are merged and not appended to arrays
* resulting in undesired outcomes for us, Example:
*
* const lastSavedConfigFromBE = { a: ["test"] };
* const currentConfigByUser = { a: [] };
* merge(lastSavedConfigFromBE, currentConfigByUser); // returns { a: ["test"] }; but we expect { a: [] };
*
* More info: https://github.com/lodash/lodash/issues/1313
*
* This helper function addresses the issue by always replacing the old array with the new array during the merge process.
*/
export function vspherePoolConfigMerge(obj1 = {}, obj2 = {}): Object {
return mergeWith(obj1, obj2, (obj1Value, obj2Value) => {
if (Array.isArray(obj1Value) && Array.isArray(obj2Value)) {
return obj2Value;
}
});
}