mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2026-08-16 04:39:15 +00:00
* feat(vm): allow Cloud Config for Windows guests Currently the Cloud Config editor is completely hidden when a VM's OS Type is set to Windows, and no `cloudinitdisk` volume or secret is written to the VM spec even if the user has entered content. This blocks a common Windows-on-Harvester workflow: using Cloudbase-Init to run first-boot configuration (install VMDP, enable OpenSSH server, add authorized_keys, join a domain, run `runcmd` steps, etc.). Users on Windows today have to hand-edit the VM YAML after creation to attach a cloudinitdisk, which is fragile and undiscoverable. Changes: - Remove the `v-if="!isWindows"` on the CloudConfig component so the editor is available for every OS type. The Sysprep editor continues to render only for Windows, so Windows now gets both panels; Linux is unchanged. - Drop the `if (!this.isWindows)` guard around the cloudinitdisk volume serialization so the disk is attached whenever the user provided user-data or network-data, regardless of OS. - Drop the `|| this.isWindows` early-return in `saveSecret` so the cloud-init secret is persisted for Windows VMs too. - `getInitUserData` now returns a bare `#cloud-config\n` header for Windows instead of the Linux qemu-guest-agent runcmd template (which would fail on Cloudbase-Init). VMDP installs the QGA on Windows as a native service, so the runcmd path is not needed. The "Install guest agent" checkbox stays disabled for Windows because it specifically drives the Linux QGA-via-runcmd recipe. Fixes point 2 of harvester/harvester#11124. Signed-off-by: Alejandro Bonilla <abonilla@suse.com> * fix(vm): do not clear cloud-config on isWindows watcher fire The isWindows watcher runs on every change of the isWindows computed property, including on mount when editing an existing Windows VM. Setting this['userScript'] and this['networkScript'] to undefined at that point wipes the cloud-config data that was just loaded from the existing VM's cloudinit secret, so the Advanced -> Cloud Config editor shows empty even though the VM has data on the wire. Remove the two clears - Cloud Config is now supported for Windows guests by this PR, so there is no reason to unset the user/network scripts when the OS type is Windows. sshKey and installAgent remain gated (SSH keys are injected via cloud-init on Linux only, and the qemu-guest-agent package install checkbox does not apply to Windows). Signed-off-by: Alejandro Bonilla <abonilla@suse.com> Co-authored-by: Volker Theile <vtheile@suse.com> * refactor(vm): Get CloudInit working for VM templates - Remove unnecessary osType watcher in cloud config. The watcher (see `pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachineCloudConfig/DataTemplate.vue`) that cleared the cloud config template for Windows VMs is no longer necessary. - Automatically strip the CloudInit `User Data` properties `package_update` and `packages` for OS type `Windows`. Signed-off-by: Volker Theile <vtheile@suse.com> --------- Signed-off-by: Alejandro Bonilla <abonilla@suse.com> Signed-off-by: Volker Theile <vtheile@suse.com> Co-authored-by: Volker Theile <vtheile@suse.com>
166 lines
3.1 KiB
Vue
166 lines
3.1 KiB
Vue
<script>
|
|
import { mapGetters } from 'vuex';
|
|
|
|
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
|
import YamlEditor, { EDITOR_MODES } from '@shell/components/YamlEditor';
|
|
|
|
import { _VIEW } from '@shell/config/query-params';
|
|
import { CONFIG_MAP } from '@shell/config/types';
|
|
|
|
const _NEW = '_NEW';
|
|
const _NONE = '_NONE';
|
|
|
|
export default {
|
|
components: { YamlEditor, LabeledSelect },
|
|
|
|
emits: ['updateTemplateId', 'show', 'update'],
|
|
|
|
props: {
|
|
mode: {
|
|
type: String,
|
|
default: 'create'
|
|
},
|
|
osType: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
value: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
type: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
options: {
|
|
type: Array,
|
|
default: () => {
|
|
return [];
|
|
}
|
|
},
|
|
configId: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
viewCode: {
|
|
type: Boolean,
|
|
default: false
|
|
}
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
id: '',
|
|
yamlScript: this.value,
|
|
};
|
|
},
|
|
|
|
computed: {
|
|
...mapGetters({ t: 'i18n/t' }),
|
|
|
|
editorMode() {
|
|
return this.isView || this.viewCode ? EDITOR_MODES.VIEW_CODE : EDITOR_MODES.EDIT_CODE;
|
|
},
|
|
|
|
isView() {
|
|
return this.mode === _VIEW;
|
|
},
|
|
},
|
|
|
|
watch: {
|
|
value(neu) {
|
|
this.yamlScript = neu;
|
|
},
|
|
|
|
configId(neu) {
|
|
this.id = this.configId;
|
|
},
|
|
|
|
id(neu, old) {
|
|
const cloudInit = this.$store.getters['harvester/byId'](CONFIG_MAP, neu)?.data?.cloudInit || '';
|
|
|
|
this.$emit('updateTemplateId', this.type, neu);
|
|
if (!neu) {
|
|
// should not reset yaml when nothing is selected
|
|
return;
|
|
} else if (neu === _NEW) {
|
|
this.$emit('show', this.type);
|
|
this.id = old;
|
|
|
|
return;
|
|
} else if (neu === _NONE ) {
|
|
this.yamlScript = '';
|
|
} else {
|
|
this.yamlScript = cloudInit;
|
|
}
|
|
|
|
this.$refs['yaml'].updateValue(cloudInit);
|
|
},
|
|
|
|
yamlScript(neu) {
|
|
this.$emit('update', neu, this.type);
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
refresh() {
|
|
this.$refs.yaml.refresh();
|
|
},
|
|
|
|
updateValue() {
|
|
this.$refs['yaml'].updateValue(this.value);
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div class="mb-20">
|
|
<h3>{{ t(`harvester.virtualMachine.cloudConfig.${type}.title`) }}</h3>
|
|
<p class="text-muted mb-20">
|
|
<t
|
|
:k="`harvester.virtualMachine.cloudConfig.${type}.tip`"
|
|
:raw="true"
|
|
/>
|
|
</p>
|
|
|
|
<LabeledSelect
|
|
v-if="!isView"
|
|
v-model:value="id"
|
|
class="mb-20"
|
|
:options="options"
|
|
:disabled="viewCode"
|
|
:label-key="`harvester.virtualMachine.cloudConfig.${type}.label`"
|
|
/>
|
|
|
|
<div class="resource-yaml">
|
|
<YamlEditor
|
|
ref="yaml"
|
|
v-model:value="yamlScript"
|
|
:mode="mode"
|
|
class="yaml-editor"
|
|
:editor-mode="editorMode"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
$yaml-height: 200px;
|
|
|
|
:deep() .resource-yaml {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
|
|
& .yaml-editor{
|
|
flex: 1;
|
|
min-height: $yaml-height;
|
|
|
|
& .code-mirror .CodeMirror {
|
|
min-height: $yaml-height;
|
|
}
|
|
}
|
|
}
|
|
</style>
|