Compare commits

..
Author SHA1 Message Date
mergify[bot]andAndy Lee f2e8cce29f fix unable to detach PCI & USB device in VM edit page (#247) (#249)
* fix unable to detach PCI & USB device in VM edit page

Signed-off-by: Andy Lee <andy.lee@suse.com>

* refactor based on comment

Signed-off-by: Andy Lee <andy.lee@suse.com>

* using set from shell/utils/object

Signed-off-by: Andy Lee <andy.lee@suse.com>

---------

Signed-off-by: Andy Lee <andy.lee@suse.com>
(cherry picked from commit 2993ddb82c)

Co-authored-by: Andy Lee <andy.lee@suse.com>
2025-04-16 16:13:12 +08:00
mergify[bot]andAndy Lee 31682dd165 bump to 1.5.0-rc5 (#245) (#246)
(cherry picked from commit a5d4604dce)
Signed-off-by: Andy Lee <andy.lee@suse.com>

Co-authored-by: Andy Lee <andy.lee@suse.com>
2025-04-16 14:44:54 +08:00
mergify[bot]andAndy Lee 9fac43d5e5 pass showYaml prop to prevent double generate secret (#243) (#244)
Signed-off-by: Andy Lee <andy.lee@suse.com>
(cherry picked from commit daa6d6942b)

Co-authored-by: Andy Lee <andy.lee@suse.com>
2025-04-15 16:08:09 +08:00
mergify[bot]andAndy Lee cd6ea6e402 Add logic to block import harvester version lower than 1.3.0 (#241) (#242)
* add logic to block import harvester version lower than 1.3.0

Signed-off-by: Andy Lee <andy.lee@suse.com>

* refactor console.error message

Signed-off-by: Andy Lee <andy.lee@suse.com>

---------

Signed-off-by: Andy Lee <andy.lee@suse.com>
(cherry picked from commit eb92642b3b)

Co-authored-by: Andy Lee <andy.lee@suse.com>
2025-04-10 16:24:22 +08:00
mergify[bot]andAndy Lee 3bfe57dc54 filter golden image volume in hot plug volume modal (#239) (#240)
Signed-off-by: Andy Lee <andy.lee@suse.com>
(cherry picked from commit 78234b9e1e)

Co-authored-by: Andy Lee <andy.lee@suse.com>
2025-04-10 13:27:23 +08:00
11 changed files with 39 additions and 16 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "harvester-ui-extension", "name": "harvester-ui-extension",
"version": "1.5.0-rc4", "version": "1.5.0-rc5",
"private": false, "private": false,
"engines": { "engines": {
"node": ">=20.0.0" "node": ">=20.0.0"
@@ -57,6 +57,10 @@ export default {
if (!!pvc.metadata?.annotations?.[HCI_ANNOTATIONS.IMAGE_ID]) { if (!!pvc.metadata?.annotations?.[HCI_ANNOTATIONS.IMAGE_ID]) {
return false; return false;
} }
// we won't show golden image volume in the hot plug volume modal
if (pvc.isGoldenImageVolume) {
return false;
}
return !pvc.attachVM; return !pvc.attachVM;
}) })
@@ -101,9 +101,7 @@ export default {
...formatted, ...formatted,
]; ];
if (devices.length > 0) { set(this.value.domain.devices, 'hostDevices', devices);
set(this.value.domain.devices, 'hostDevices', devices);
}
} }
}, },
@@ -84,9 +84,7 @@ export default {
...formatted, ...formatted,
]; ];
if (devices.length > 0) { set(this.value.domain.devices, 'hostDevices', devices);
set(this.value.domain.devices, 'hostDevices', devices);
}
} }
}, },
@@ -111,7 +111,7 @@ export default {
return false; return false;
} }
if (pvc.metadata?.annotations?.[HCI_ANNOTATIONS.GOLDEN_IMAGE] === 'true') { if (pvc.isGoldenImageVolume) {
return false; return false;
} }
@@ -2,7 +2,7 @@
import { isEqual } from 'lodash'; import { isEqual } from 'lodash';
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import Tabbed from '@shell/components/Tabbed'; import Tabbed from '@shell/components/Tabbed';
import { clone } from '@shell/utils/object'; import { clone, set } from '@shell/utils/object';
import Tab from '@shell/components/Tabbed/Tab'; import Tab from '@shell/components/Tabbed/Tab';
import { Checkbox } from '@components/Form/Checkbox'; import { Checkbox } from '@components/Form/Checkbox';
import CruResource from '@shell/components/CruResource'; import CruResource from '@shell/components/CruResource';
@@ -16,7 +16,6 @@ import PodAffinity from '@shell/components/form/PodAffinity';
import VGpuDevices from './VirtualMachineVGpuDevices/index'; import VGpuDevices from './VirtualMachineVGpuDevices/index';
import UsbDevices from './VirtualMachineUSBDevices/index'; import UsbDevices from './VirtualMachineUSBDevices/index';
import KeyValue from '@shell/components/form/KeyValue'; import KeyValue from '@shell/components/form/KeyValue';
import { clear } from '@shell/utils/array'; import { clear } from '@shell/utils/array';
import { saferDump } from '@shell/utils/create-yaml'; import { saferDump } from '@shell/utils/create-yaml';
import { exceptionToErrorsArray } from '@shell/utils/error'; import { exceptionToErrorsArray } from '@shell/utils/error';
@@ -412,12 +411,21 @@ export default {
} }
const cloneDeepNewVM = clone(this.value); const cloneDeepNewVM = clone(this.value);
// new VM
delete cloneDeepNewVM?.metadata; delete cloneDeepNewVM?.metadata;
delete cloneDeepNewVM?.__clone; delete cloneDeepNewVM?.__clone;
// old VM
delete this.cloneVM?.metadata; delete this.cloneVM?.metadata;
delete this.cloneVM?.__clone; delete this.cloneVM?.__clone;
// add empty hostDevices to old VM as CRD does not have it.
const devicesObj = this.cloneVM?.spec?.template?.spec?.domain?.devices;
if (devicesObj && devicesObj.hostDevices === undefined) {
set(devicesObj, 'hostDevices', []);
}
const oldVM = JSON.parse(JSON.stringify(this.cloneVM)); const oldVM = JSON.parse(JSON.stringify(this.cloneVM));
const newVM = JSON.parse(JSON.stringify(cloneDeepNewVM)); const newVM = JSON.parse(JSON.stringify(cloneDeepNewVM));
@@ -485,6 +493,7 @@ export default {
}, },
generateYaml() { generateYaml() {
this.showYaml = this?.$refs?.vmCruResource?.showYaml || false;
this.parseVM(); this.parseVM();
const out = saferDump(this.value); const out = saferDump(this.value);
@@ -498,6 +507,7 @@ export default {
<CruResource <CruResource
v-if="spec" v-if="spec"
id="vm" id="vm"
ref="vmCruResource"
:done-route="doneRoute" :done-route="doneRoute"
:resource="value" :resource="value"
:cancel-event="true" :cancel-event="true"
@@ -6,10 +6,8 @@ import {
} from '@shell/config/types'; } from '@shell/config/types';
import { STATE, AGE, NAME, NAMESPACE } from '@shell/config/table-headers'; import { STATE, AGE, NAME, NAMESPACE } from '@shell/config/table-headers';
import HarvesterVolumeState from '../formatters/HarvesterVolumeState'; import HarvesterVolumeState from '../formatters/HarvesterVolumeState';
import { allSettled } from '../utils/promise'; import { allSettled } from '../utils/promise';
import { HCI, VOLUME_SNAPSHOT } from '../types'; import { HCI, VOLUME_SNAPSHOT } from '../types';
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
const schema = { const schema = {
id: HCI.VOLUME, id: HCI.VOLUME,
@@ -72,7 +70,7 @@ export default {
}, },
filterRows() { filterRows() {
// we only show the non golden image PVCs in the list // we only show the non golden image PVCs in the list
return this.rows.filter((pvc) => pvc?.metadata?.annotations?.[HCI_ANNOTATIONS.GOLDEN_IMAGE] !== 'true'); return this.rows.filter((pvc) => !pvc?.isGoldenImageVolume);
}, },
headers() { headers() {
return [ return [
+3 -2
View File
@@ -140,6 +140,7 @@ export default {
return { return {
OS, OS,
isClone, isClone,
showYaml: false,
spec: null, spec: null,
osType: 'linux', osType: 'linux',
sshKey: [], sshKey: [],
@@ -269,7 +270,7 @@ export default {
needNewSecret() { needNewSecret() {
// When creating a template it is always necessary to create a new secret. // When creating a template it is always necessary to create a new secret.
return this.resourceType === HCI.VM_VERSION || this.isCreate; return this.showYaml ? false : this.resourceType === HCI.VM_VERSION || this.isCreate;
}, },
defaultTerminationSetting() { defaultTerminationSetting() {
@@ -669,7 +670,7 @@ export default {
} }
}); });
if (!this.secretName) { if (this.needNewSecret || !this.secretName) {
this.secretName = this.generateSecretName(this.secretNamePrefix); this.secretName = this.generateSecretName(this.secretNamePrefix);
} }
@@ -344,6 +344,10 @@ export default class HciPv extends HarvesterResource {
return this.dataEngine === DATA_ENGINE_V2; return this.dataEngine === DATA_ENGINE_V2;
} }
get isGoldenImageVolume() {
return this?.metadata?.annotations?.[HCI_ANNOTATIONS.GOLDEN_IMAGE] === 'true';
}
get thirdPartyStorageFeatureEnabled() { get thirdPartyStorageFeatureEnabled() {
return this.$rootGetters['harvester-common/getFeatureEnabled']('thirdPartyStorage'); return this.$rootGetters['harvester-common/getFeatureEnabled']('thirdPartyStorage');
} }
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "harvester", "name": "harvester",
"description": "Rancher UI Extension for Harvester", "description": "Rancher UI Extension for Harvester",
"version": "1.5.0-rc4", "version": "1.5.0-rc5",
"private": false, "private": false,
"rancher": { "rancher": {
"annotations": { "annotations": {
+10
View File
@@ -35,7 +35,17 @@ function latestMinorVersion(v) {
} }
export const featureEnabled = (featureKey, serverVersion) => { export const featureEnabled = (featureKey, serverVersion) => {
const minSupportedVersion = '1.3.0';
const version = getVersion(serverVersion); const version = getVersion(serverVersion);
if (semver.lt(version.replace('v', ''), minSupportedVersion)) {
// eslint-disable-next-line no-console
console.error(`Harvester UI extension only supports Harvester cluster version >= ${ minSupportedVersion }. Current version: ${ version }`);
return false;
}
let releasedFeatures = RELEASE_FEATURES[version]; let releasedFeatures = RELEASE_FEATURES[version];
if (!releasedFeatures) { if (!releasedFeatures) {