diff --git a/pkg/harvester/edit/kubevirt.io.virtualmachine/index.vue b/pkg/harvester/edit/kubevirt.io.virtualmachine/index.vue index b675a824..e064d274 100644 --- a/pkg/harvester/edit/kubevirt.io.virtualmachine/index.vue +++ b/pkg/harvester/edit/kubevirt.io.virtualmachine/index.vue @@ -484,6 +484,20 @@ export default { await value.save(); await this.applyHooks(AFTER_SAVE_HOOKS); } catch (e) { + // Reset the generated secret name(s) so a retried create (e.g. after correcting an + // invalid/duplicate VM name) always regenerates them from the current name instead of + // reusing names derived from this failed attempt. Only do this for create: in edit mode + // secretName/sysprep.secretName may reference an existing secret loaded from the VM, + // which must not be discarded. See harvester/harvester#11174. + if (this.isCreate) { + this.secretName = ''; + this.secretNamePrefixUsed = ''; + if (this.sysprepSecretNamePrefixUsed !== '') { + this.sysprep.secretName = ''; + this.sysprepSecretNamePrefixUsed = ''; + } + } + this.errors.push(...exceptionToErrorsArray(e)); buttonCb(false); } diff --git a/pkg/harvester/mixins/harvester-vm/index.js b/pkg/harvester/mixins/harvester-vm/index.js index 87f240bd..fce3fc50 100644 --- a/pkg/harvester/mixins/harvester-vm/index.js +++ b/pkg/harvester/mixins/harvester-vm/index.js @@ -162,6 +162,12 @@ export default { machineType: '', machineTypes: [], secretName: '', + // Tracks the name prefix that `secretName` was generated from, so it can be + // regenerated if the VM name changes (e.g. after a failed create attempt is retried + // with a different name). See harvester/harvester#11174. + secretNamePrefixUsed: '', + // Same purpose as secretNamePrefixUsed, but for the Windows sysprep secret name. + sysprepSecretNamePrefixUsed: '', secretRef: null, showAdvanced: false, deleteAgent: true, @@ -866,18 +872,30 @@ export default { } }); - if (this.needNewSecret || !this.secretName) { + // Regenerate the cloud-init secret name whenever it hasn't been generated yet, or the VM + // name has changed since it was last generated (e.g. a previous create attempt failed with + // an invalid/duplicate name and the user corrected it). Without this, a stale secret name + // derived from a rejected attempt gets reused for the VM that's actually created. + // See harvester/harvester#11174. + if (this.needNewSecret || !this.secretName || (this.isCreate && this.secretNamePrefixUsed !== this.secretNamePrefix)) { this.secretName = this.generateSecretName(this.secretNamePrefix); + this.secretNamePrefixUsed = this.secretNamePrefix; } if (!disks.find((D) => D.name === 'sysprep') && this.isWindows) { const hasSysprepContent = !!this.sysprep.xmlContent?.trim?.(); - // If we have content but no secret name, it's a new secret that needs a name. - if (hasSysprepContent && !this.sysprep.secretName?.trim?.()) { + // If we have content but no secret name, it's a new secret that needs a name. Also + // regenerate when creating a VM if the name prefix has changed since it was last + // generated. + const sysprepNeedsNewSecretName = !this.sysprep.secretName?.trim?.() || + (this.isCreate && this.sysprepSecretNamePrefixUsed !== '' && this.sysprepSecretNamePrefixUsed !== this.secretNamePrefix); + + if (hasSysprepContent && sysprepNeedsNewSecretName) { const prefix = this.secretNamePrefix ? `${ this.secretNamePrefix }-windows-sysprep` : 'windows-sysprep'; this.sysprep.secretName = `${ this.value.metadata.namespace }/${ this.generateSecretName(prefix) }`; + this.sysprepSecretNamePrefixUsed = this.secretNamePrefix; } // Preserve/attach sysprep whenever a secret is selected/known.