From 2deb0aa254457ce987ea9ba0720fa3cd5a88158e Mon Sep 17 00:00:00 2001 From: Marcelo Fukumoto Date: Thu, 28 May 2026 22:57:38 +0200 Subject: [PATCH] feat(forklift): small fixes after Copilot Signed-off-by: Marcelo Fukumoto --- .vscode/settings.json | 11 +- .../vm-migration/ConfigureProviderStep.vue | 273 ++++++------------ .../vm-migration/ReviewMigrationStep.vue | 2 - .../components/vm-migration/SelectVmsStep.vue | 3 +- pkg/harvester/l10n/en-us.yaml | 13 +- .../pages/c/_cluster/vm-migration/index.vue | 14 +- .../_cluster/vm-migration/provider-wizard.vue | 20 +- .../vm-migration/vm-migration-wizard.vue | 6 - 8 files changed, 126 insertions(+), 216 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 65fd8024..6d970ee1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,5 +5,14 @@ ".eslintcache": true, ".nyc_output": true, "yarn-error.log": true - } + }, + "editor.formatOnSave": false, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + }, + "eslint.validate": [ + "javascript", + "typescript", + "vue" + ] } diff --git a/pkg/harvester/components/vm-migration/ConfigureProviderStep.vue b/pkg/harvester/components/vm-migration/ConfigureProviderStep.vue index ef370ca7..f174fa43 100644 --- a/pkg/harvester/components/vm-migration/ConfigureProviderStep.vue +++ b/pkg/harvester/components/vm-migration/ConfigureProviderStep.vue @@ -159,9 +159,17 @@ watch(selectedProvider, (val) => { ); if (secret?.data) { - username.value = atob(secret.data.user || ''); - password.value = atob(secret.data.password || ''); - skipTlsVerify.value = atob(secret.data.insecureSkipVerify || '') === 'true'; + const decode = (val) => { + try { + return atob(val || ''); + } catch (e) { + return ''; + } + }; + + username.value = decode(secret.data.user); + password.value = decode(secret.data.password); + skipTlsVerify.value = decode(secret.data.insecureSkipVerify) === 'true'; } } @@ -195,6 +203,74 @@ watch(testing, (val) => { emit('testing', val); }, { immediate: true }); +const pollProviderReady = async(name) => { + const inStore = store.getters['currentProduct'].inStore; + const namespace = FORKLIFT_NAMESPACE; + const maxAttempts = 15; + let attempts = 0; + + while (attempts < maxAttempts) { + await new Promise((resolve) => setTimeout(resolve, 2000)); + attempts++; + + const refreshed = await store.dispatch(`${ inStore }/find`, { + type: HCI.FORKLIFT_PROVIDER, + id: `${ namespace }/${ name }`, + opt: { force: true } + }); + + const conditions = refreshed?.status?.conditions || []; + const connectionCondition = conditions.find((c) => c.type === 'ConnectionTestSucceeded'); + const readyCondition = conditions.find((c) => c.type === 'Ready'); + + if (connectionCondition) { + if (connectionCondition.status === 'True') { + return { connected: true }; + } + + return { connected: false, errorMsg: connectionCondition.message || t('harvester.addons.vmMigration.errors.connectionFailed') }; + } + + if (readyCondition) { + if (readyCondition.status === 'True') { + return { connected: true }; + } else if (readyCondition.status === 'False') { + return { connected: false, errorMsg: readyCondition.message || t('harvester.addons.vmMigration.errors.providerNotReady') }; + } + } + } + + return { connected: false, errorMsg: '' }; +}; + +const handlePollResult = ({ connected, errorMsg }, buttonCb) => { + if (connected) { + testPassed.value = true; + testResult.value = t('harvester.addons.vmMigration.configureProvider.testSuccess'); + testing.value = false; + buttonCb(true); + } else { + testError.value = errorMsg || t('harvester.addons.vmMigration.configureProvider.testTimeout'); + testing.value = false; + buttonCb(false); + } +}; + +const cleanupCreatedResources = async() => { + if (createdProvider.value) { + try { + await createdProvider.value.remove(); + } catch (e) {} + createdProvider.value = null; + createdSecret.value = null; + } else if (createdSecret.value) { + try { + await createdSecret.value.remove(); + } catch (e) {} + createdSecret.value = null; + } +}; + const testConnection = async(buttonCb) => { testResult.value = null; testError.value = null; @@ -213,8 +289,6 @@ const testConnection = async(buttonCb) => { // Edit mode: update existing provider URL + secret, then poll if (props.editMode && createdProvider.value) { try { - const namespace = FORKLIFT_NAMESPACE; - createdProvider.value.spec.url = url.value; await createdProvider.value.save(); @@ -230,56 +304,7 @@ const testConnection = async(buttonCb) => { await createdSecret.value.save(); } - const maxAttempts = 15; - let attempts = 0; - let connected = false; - let errorMsg = ''; - - while (attempts < maxAttempts) { - await new Promise((resolve) => setTimeout(resolve, 2000)); - attempts++; - - const refreshed = await store.dispatch(`${ inStore }/find`, { - type: HCI.FORKLIFT_PROVIDER, - id: `${ namespace }/${ providerName.value }`, - opt: { force: true } - }); - - const conditions = refreshed?.status?.conditions || []; - const readyCondition = conditions.find((c) => c.type === 'Ready'); - const connectionCondition = conditions.find((c) => c.type === 'ConnectionTestSucceeded'); - - if (connectionCondition) { - if (connectionCondition.status === 'True') { - connected = true; - break; - } else { - errorMsg = connectionCondition.message || t('harvester.addons.vmMigration.errors.connectionFailed'); - break; - } - } - - if (readyCondition) { - if (readyCondition.status === 'True') { - connected = true; - break; - } else if (readyCondition.status === 'False') { - errorMsg = readyCondition.message || t('harvester.addons.vmMigration.errors.providerNotReady'); - break; - } - } - } - - if (connected) { - testPassed.value = true; - testResult.value = t('harvester.addons.vmMigration.configureProvider.testSuccess'); - testing.value = false; - buttonCb(true); - } else { - testError.value = errorMsg || t('harvester.addons.vmMigration.configureProvider.testTimeout'); - testing.value = false; - buttonCb(false); - } + handlePollResult(await pollProviderReady(providerName.value), buttonCb); } catch (err) { testError.value = err.message || t('harvester.addons.vmMigration.configureProvider.testFailed'); testing.value = false; @@ -292,57 +317,7 @@ const testConnection = async(buttonCb) => { // For existing providers, just poll for Ready/ConnectionTestSucceeded status if (isExistingProvider.value) { try { - const namespace = FORKLIFT_NAMESPACE; - const maxAttempts = 15; - let attempts = 0; - let connected = false; - let errorMsg = ''; - - while (attempts < maxAttempts) { - await new Promise((resolve) => setTimeout(resolve, 2000)); - attempts++; - - const refreshed = await store.dispatch(`${ inStore }/find`, { - type: HCI.FORKLIFT_PROVIDER, - id: `${ namespace }/${ providerName.value }`, - opt: { force: true } - }); - - const conditions = refreshed?.status?.conditions || []; - const readyCondition = conditions.find((c) => c.type === 'Ready'); - const connectionCondition = conditions.find((c) => c.type === 'ConnectionTestSucceeded'); - - if (connectionCondition) { - if (connectionCondition.status === 'True') { - connected = true; - break; - } else { - errorMsg = connectionCondition.message || t('harvester.addons.vmMigration.errors.connectionFailed'); - break; - } - } - - if (readyCondition) { - if (readyCondition.status === 'True') { - connected = true; - break; - } else if (readyCondition.status === 'False') { - errorMsg = readyCondition.message || t('harvester.addons.vmMigration.errors.providerNotReady'); - break; - } - } - } - - if (connected) { - testPassed.value = true; - testResult.value = t('harvester.addons.vmMigration.configureProvider.testSuccess'); - testing.value = false; - buttonCb(true); - } else { - testError.value = errorMsg || t('harvester.addons.vmMigration.configureProvider.testTimeout'); - testing.value = false; - buttonCb(false); - } + handlePollResult(await pollProviderReady(providerName.value), buttonCb); } catch (err) { testError.value = err.message || t('harvester.addons.vmMigration.configureProvider.testFailed'); testing.value = false; @@ -354,20 +329,11 @@ const testConnection = async(buttonCb) => { // For new providers, create provider + secret then poll try { - // Delete previous provider (cascades to secret via ownerReferences) - if (createdProvider.value) { - await createdProvider.value.remove(); - createdProvider.value = null; - createdSecret.value = null; - } else if (createdSecret.value) { - await createdSecret.value.remove(); - createdSecret.value = null; - } + await cleanupCreatedResources(); const namespace = FORKLIFT_NAMESPACE; const secretName = `${ providerName.value }-creds-${ randomStr(4).toLowerCase() }`; - // Create Provider first so we have its UID for the ownerReference on the Secret const provider = await store.dispatch(`${ inStore }/create`, { type: HCI.FORKLIFT_PROVIDER, metadata: { @@ -387,7 +353,6 @@ const testConnection = async(buttonCb) => { await provider.save(); createdProvider.value = provider; - // Create Secret with ownerReference already set (avoids an extra PUT) const newSecret = await store.dispatch(`${ inStore }/create`, { type: SECRET, metadata: { @@ -417,79 +382,15 @@ const testConnection = async(buttonCb) => { await newSecret.save(); createdSecret.value = newSecret; - const maxAttempts = 15; - let attempts = 0; - let connected = false; - let errorMsg = ''; + const result = await pollProviderReady(providerName.value); - while (attempts < maxAttempts) { - await new Promise((resolve) => setTimeout(resolve, 2000)); - attempts++; - - const refreshed = await store.dispatch(`${ inStore }/find`, { - type: HCI.FORKLIFT_PROVIDER, - id: `${ namespace }/${ providerName.value }`, - opt: { force: true } - }); - - const conditions = refreshed?.status?.conditions || []; - const readyCondition = conditions.find((c) => c.type === 'Ready'); - const connectionCondition = conditions.find((c) => c.type === 'ConnectionTestSucceeded'); - - if (connectionCondition) { - if (connectionCondition.status === 'True') { - connected = true; - break; - } else { - errorMsg = connectionCondition.message || t('harvester.addons.vmMigration.errors.connectionFailed'); - break; - } - } - - if (readyCondition) { - if (readyCondition.status === 'True') { - connected = true; - break; - } else if (readyCondition.status === 'False') { - errorMsg = readyCondition.message || t('harvester.addons.vmMigration.errors.providerNotReady'); - break; - } - } + if (!result.connected) { + await cleanupCreatedResources(); } - if (connected) { - testPassed.value = true; - testResult.value = t('harvester.addons.vmMigration.configureProvider.testSuccess'); - testing.value = false; - buttonCb(true); - } else { - if (createdProvider.value) { - await createdProvider.value.remove(); - createdProvider.value = null; - createdSecret.value = null; - } else if (createdSecret.value) { - await createdSecret.value.remove(); - createdSecret.value = null; - } - - testError.value = errorMsg || t('harvester.addons.vmMigration.configureProvider.testTimeout'); - testing.value = false; - buttonCb(false); - } + handlePollResult(result, buttonCb); } catch (err) { - if (createdProvider.value) { - try { - await createdProvider.value.remove(); - } catch (e) {} - createdProvider.value = null; - } - if (createdSecret.value) { - try { - await createdSecret.value.remove(); - } catch (e) {} - createdSecret.value = null; - } - + await cleanupCreatedResources(); testError.value = err.message || t('harvester.addons.vmMigration.configureProvider.testFailed'); testing.value = false; buttonCb(false); diff --git a/pkg/harvester/components/vm-migration/ReviewMigrationStep.vue b/pkg/harvester/components/vm-migration/ReviewMigrationStep.vue index 968edc77..92a9c229 100644 --- a/pkg/harvester/components/vm-migration/ReviewMigrationStep.vue +++ b/pkg/harvester/components/vm-migration/ReviewMigrationStep.vue @@ -15,8 +15,6 @@ const props = defineProps({ providerName: { type: String, default: '' }, provider: { type: Object, default: null }, selectedVms: { type: Array, default: () => [] }, - networkMapName: { type: String, default: '' }, - storageMapName: { type: String, default: '' }, mappingEntries: { type: Object, default: null }, stepData: { type: Object, required: true }, }); diff --git a/pkg/harvester/components/vm-migration/SelectVmsStep.vue b/pkg/harvester/components/vm-migration/SelectVmsStep.vue index ce5daf0c..3ac220f2 100644 --- a/pkg/harvester/components/vm-migration/SelectVmsStep.vue +++ b/pkg/harvester/components/vm-migration/SelectVmsStep.vue @@ -406,8 +406,7 @@ init();