From f0cf58bc4b1219aca94d5770d7dc5d4fd0af0ab6 Mon Sep 17 00:00:00 2001 From: Nino Kodabande Date: Thu, 4 Jul 2024 11:08:07 -0700 Subject: [PATCH 1/3] Add diagnostic to verify kubeConfig When WSL integration is enabled we would need to check the content of the kubeConfig to see if it includes Rancher Desktop only config or not, prior to creating the symlink. Now, we can trigger a diagnostic that prompts the user to manually create the symlink if we encounter non-rancher desktop configuration. Signed-off-by: Nino Kodabande --- .../integrations/windowsIntegrationManager.ts | 26 +++++++++++ .../main/diagnostics/diagnostics.ts | 9 ++-- .../main/diagnostics/kubeConfigSymlink.ts | 43 +++++++++++++++++++ 3 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 pkg/rancher-desktop/main/diagnostics/kubeConfigSymlink.ts diff --git a/pkg/rancher-desktop/integrations/windowsIntegrationManager.ts b/pkg/rancher-desktop/integrations/windowsIntegrationManager.ts index 6093c04a2a5..0031f27fff8 100644 --- a/pkg/rancher-desktop/integrations/windowsIntegrationManager.ts +++ b/pkg/rancher-desktop/integrations/windowsIntegrationManager.ts @@ -460,6 +460,32 @@ export default class WindowsIntegrationManager implements IntegrationManager { } } + /** + * verifyDistrosKubeConfig loops through all the available distros + * and calls the wsl-helper kubeconfig --verify per distro. + */ + async verifyDistrosKubeConfig() { + await Promise.all([ + (await (this.supportedDistros)).map(distro => this.verifyKubeConfig(distro.name)), + ]); + } + + /** + * verifyDistroKubeConfig calls the wsl-helper kubeconfig --verify per distro. + */ + protected async verifyKubeConfig(distro: string) { + try { + const wslHelper = await this.getLinuxToolPath(distro, executable('wsl-helper-linux')); + + await this.execCommand({ distro }, wslHelper, 'kubeconfig', '--verify'); + } catch (err: any) { + if ('code' in err && err.code !== 0) { + return `Error ${ err } verifying kubeConfig in distro ${ distro }`; + } + } + console.debug(`Verified kubeconfig in the following distro ${ distro }`); + } + protected async syncDistroKubeconfig(distro: string, kubeconfigPath: string, state: boolean) { try { console.debug(`Syncing ${ distro } kubeconfig`); diff --git a/pkg/rancher-desktop/main/diagnostics/diagnostics.ts b/pkg/rancher-desktop/main/diagnostics/diagnostics.ts index d544a1014c0..ef5402520bd 100644 --- a/pkg/rancher-desktop/main/diagnostics/diagnostics.ts +++ b/pkg/rancher-desktop/main/diagnostics/diagnostics.ts @@ -46,14 +46,15 @@ export class DiagnosticsManager { constructor(diagnostics?: DiagnosticsChecker[]) { this.checkers = diagnostics ? Promise.resolve(diagnostics) : (async() => { const imports = (await Promise.all([ - import('./testCheckers'), import('./connectedToInternet'), import('./dockerCliSymlinks'), - import('./rdBinInShell'), + import('./kubeConfigSymlink'), import('./kubeContext'), - import('./wslFromStore'), - import('./mockForScreenshots'), import('./limaDarwin'), + import('./mockForScreenshots'), + import('./rdBinInShell'), + import('./testCheckers'), + import('./wslFromStore'), ])).map(obj => obj.default); return (await Promise.all(imports)).flat(); diff --git a/pkg/rancher-desktop/main/diagnostics/kubeConfigSymlink.ts b/pkg/rancher-desktop/main/diagnostics/kubeConfigSymlink.ts new file mode 100644 index 00000000000..a09f99d4f44 --- /dev/null +++ b/pkg/rancher-desktop/main/diagnostics/kubeConfigSymlink.ts @@ -0,0 +1,43 @@ +import { DiagnosticsCategory, DiagnosticsChecker } from './types'; + +import WindowsIntegrationManager from '@pkg/integrations/windowsIntegrationManager'; +import Logging from '@pkg/utils/logging'; + +const console = Logging.diagnostics; +const integrationManager = new WindowsIntegrationManager(); + +async function verifyKubeConfigSymlink(): Promise { + try { + await integrationManager.verifyDistrosKubeConfig(); + + return true; + } catch (error: any) { + console.error(`Error verifying kubeconfig symlinks: ${ error }`); + + return false; + } +} + +/** + * CheckKubeConfigSymlink checks the symlinked kubeConfig in WSL integration + * enabled distro for non-rancher desktop configuration. + */ +const CheckKubeConfigSymlink: DiagnosticsChecker = { + id: 'VERIFY_WSL_INTEGRATION_KUBECONFIG', + category: DiagnosticsCategory.Kubernetes, + applicable() { + return Promise.resolve(true); + }, + async check() { + return Promise.resolve({ + description: 'Rancher Desktop cannot automatically convert the provided kubeconfig file to a symlink' + + ' due to existing configurations within that file. To resolve this issue, you will need to ' + + 'manually create the symlink to ensure existing configurations are preserved and to prevent ' + + 'any loss of configuration.', + passed: await verifyKubeConfigSymlink(), + fixes: [], + }); + }, +}; + +export default CheckKubeConfigSymlink; From 3f1c79b5a99485ccff4c924261330a9b877a5671 Mon Sep 17 00:00:00 2001 From: Nino Kodabande Date: Mon, 8 Jul 2024 09:38:28 -0700 Subject: [PATCH 2/3] Make WindowsIntegrationManager singleton This is to avoid early initialization. Signed-off-by: Nino Kodabande --- .../integrations/windowsIntegrationManager.ts | 12 ++++++++++++ .../main/diagnostics/kubeConfigSymlink.ts | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/pkg/rancher-desktop/integrations/windowsIntegrationManager.ts b/pkg/rancher-desktop/integrations/windowsIntegrationManager.ts index 0031f27fff8..2e2ea395d11 100644 --- a/pkg/rancher-desktop/integrations/windowsIntegrationManager.ts +++ b/pkg/rancher-desktop/integrations/windowsIntegrationManager.ts @@ -95,6 +95,9 @@ export default class WindowsIntegrationManager implements IntegrationManager { /** Extra debugging arguments for wsl-helper. */ protected wslHelperDebugArgs: string[] = []; + /** Singleton instance. */ + private static instance: WindowsIntegrationManager; + constructor() { mainEvents.on('settings-update', (settings) => { this.wslHelperDebugArgs = runInDebugMode(settings.application.debug) ? ['--verbose'] : []; @@ -130,6 +133,15 @@ export default class WindowsIntegrationManager implements IntegrationManager { mainEvents.emit('settings-write', {}); } + /** Static method to access the singleton instance. */ + public static getInstance(): WindowsIntegrationManager { + if (!WindowsIntegrationManager.instance) { + WindowsIntegrationManager.instance = new WindowsIntegrationManager(); + } + + return WindowsIntegrationManager.instance; + } + async enforce(): Promise { this.enforcing = true; await this.sync(); diff --git a/pkg/rancher-desktop/main/diagnostics/kubeConfigSymlink.ts b/pkg/rancher-desktop/main/diagnostics/kubeConfigSymlink.ts index a09f99d4f44..4e079375c7a 100644 --- a/pkg/rancher-desktop/main/diagnostics/kubeConfigSymlink.ts +++ b/pkg/rancher-desktop/main/diagnostics/kubeConfigSymlink.ts @@ -4,9 +4,10 @@ import WindowsIntegrationManager from '@pkg/integrations/windowsIntegrationManag import Logging from '@pkg/utils/logging'; const console = Logging.diagnostics; -const integrationManager = new WindowsIntegrationManager(); async function verifyKubeConfigSymlink(): Promise { + const integrationManager = WindowsIntegrationManager.getInstance(); + try { await integrationManager.verifyDistrosKubeConfig(); From b2f339be355a67a9ef676dbebf14796e0c82d7ba Mon Sep 17 00:00:00 2001 From: Nino Kodabande Date: Tue, 9 Jul 2024 09:20:01 -0700 Subject: [PATCH 3/3] Check the error message rather than err code Signed-off-by: Nino Kodabande --- .../integrations/integrationManager.ts | 2 +- .../integrations/windowsIntegrationManager.ts | 40 +++++++++++-------- .../main/diagnostics/kubeConfigSymlink.ts | 4 +- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/pkg/rancher-desktop/integrations/integrationManager.ts b/pkg/rancher-desktop/integrations/integrationManager.ts index 8c179653d33..c2ed4815a9d 100644 --- a/pkg/rancher-desktop/integrations/integrationManager.ts +++ b/pkg/rancher-desktop/integrations/integrationManager.ts @@ -51,7 +51,7 @@ export function getIntegrationManager(): IntegrationManager { case 'darwin': return new UnixIntegrationManager(resourcesBinDir, paths.integration, dockerCliPluginDir); case 'win32': - return new WindowsIntegrationManager(); + return WindowsIntegrationManager.getInstance(); default: throw new Error(`OS ${ platform } is not supported`); } diff --git a/pkg/rancher-desktop/integrations/windowsIntegrationManager.ts b/pkg/rancher-desktop/integrations/windowsIntegrationManager.ts index 2e2ea395d11..0c2de64e5ae 100644 --- a/pkg/rancher-desktop/integrations/windowsIntegrationManager.ts +++ b/pkg/rancher-desktop/integrations/windowsIntegrationManager.ts @@ -135,9 +135,7 @@ export default class WindowsIntegrationManager implements IntegrationManager { /** Static method to access the singleton instance. */ public static getInstance(): WindowsIntegrationManager { - if (!WindowsIntegrationManager.instance) { - WindowsIntegrationManager.instance = new WindowsIntegrationManager(); - } + WindowsIntegrationManager.instance ||= new WindowsIntegrationManager(); return WindowsIntegrationManager.instance; } @@ -473,29 +471,39 @@ export default class WindowsIntegrationManager implements IntegrationManager { } /** - * verifyDistrosKubeConfig loops through all the available distros - * and calls the wsl-helper kubeconfig --verify per distro. - */ - async verifyDistrosKubeConfig() { - await Promise.all([ - (await (this.supportedDistros)).map(distro => this.verifyKubeConfig(distro.name)), - ]); + * verifyAllDistrosKubeConfig loops through all the available distros + * and checks if the kubeconfig can be managed; if any distro fails + * the check, an exception is thrown. + */ + async verifyAllDistrosKubeConfig() { + const distros = await this.supportedDistros; + + await Promise.all(distros.map(async(distro) => { + await this.verifyDistroKubeConfig(distro.name); + })); } /** - * verifyDistroKubeConfig calls the wsl-helper kubeconfig --verify per distro. - */ - protected async verifyKubeConfig(distro: string) { + * verifyDistroKubeConfig calls the wsl-helper kubeconfig --verify per distro. + * It determines the condition of the kubeConfig from the returned error code. + */ + protected async verifyDistroKubeConfig(distro: string) { try { const wslHelper = await this.getLinuxToolPath(distro, executable('wsl-helper-linux')); await this.execCommand({ distro }, wslHelper, 'kubeconfig', '--verify'); } catch (err: any) { - if ('code' in err && err.code !== 0) { - return `Error ${ err } verifying kubeConfig in distro ${ distro }`; + // Only throw for a specific error code 1, since we control that from the + // kubeconfig --verify command. The logic here is to bubble up this error + // so that the diagnostic is very specific to this issue. Any other errors + // are captured as log messages. + if (err && 'code' in err && err.code === 1) { + throw new Error(`The kubeConfig contains non-Rancher Desktop configuration in distro ${ distro }`); + } else { + console.error(`Verifying kubeconfig in distro ${ distro } failed: ${ err }`); } } - console.debug(`Verified kubeconfig in the following distro ${ distro }`); + console.debug(`Verified kubeconfig in the following distro: ${ distro }`); } protected async syncDistroKubeconfig(distro: string, kubeconfigPath: string, state: boolean) { diff --git a/pkg/rancher-desktop/main/diagnostics/kubeConfigSymlink.ts b/pkg/rancher-desktop/main/diagnostics/kubeConfigSymlink.ts index 4e079375c7a..883897d8c51 100644 --- a/pkg/rancher-desktop/main/diagnostics/kubeConfigSymlink.ts +++ b/pkg/rancher-desktop/main/diagnostics/kubeConfigSymlink.ts @@ -9,11 +9,11 @@ async function verifyKubeConfigSymlink(): Promise { const integrationManager = WindowsIntegrationManager.getInstance(); try { - await integrationManager.verifyDistrosKubeConfig(); + await integrationManager.verifyAllDistrosKubeConfig(); return true; } catch (error: any) { - console.error(`Error verifying kubeconfig symlinks: ${ error }`); + console.error(`Error verifying kubeconfig symlinks: ${ error.message }`); return false; }