Skip to content

Commit

Permalink
Merge pull request #5199 from rancher-sandbox/4952-handle-invalid-k8s…
Browse files Browse the repository at this point in the history
…-version

Handle invalid k8s versions at startup
  • Loading branch information
ericpromislow authored Jul 24, 2023
2 parents 49732db + 37eb237 commit 6d6d49e
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 43 deletions.
28 changes: 28 additions & 0 deletions bats/tests/k8s/specify-invalid-k8s-version.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
load '../helpers/load'

@test 'factory reset' {
factory_reset
}

@test 'invalid k8s version' {
start_kubernetes --kubernetes.version=moose
wait_for_container_engine
# Can't use wait_for_api_server because it hard-wires a valid k8s version and we're specifying an invalid one here.
# and we're specifying an invalid one here
local timeout="$(($(date +%s) + 10 * 60))"
until kubectl get --raw /readyz &>/dev/null; do
assert [ "$(date +%s)" -lt "$timeout" ]
sleep 1
done
# No way there's a race-condition here.
# The version was checked and written to the log file before starting k8s,
# and we have to wait a few minutes before k8s is ready and we're at the next line.
assert_file_contains "$PATH_LOGS/kube.log" "Requested kubernetes version 'moose' is not a valid version. Falling back to the most recent stable version of"
}

# on macOS it still hangs without this
@test 'shutdown' {
if is_macos; then
rdctl shutdown
fi
}
68 changes: 68 additions & 0 deletions pkg/rancher-desktop/backend/backendHelper.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import Electron from 'electron';
import merge from 'lodash/merge';
import semver from 'semver';

import { BackendSettings } from '@pkg/backend/backend';
import { ContainerEngine } from '@pkg/config/settings';
import Logging from '@pkg/utils/logging';
import { showMessageBox } from '@pkg/window';

const console = Logging.kube;

export default class BackendHelper {
/**
Expand Down Expand Up @@ -116,4 +121,67 @@ export default class BackendHelper {
static requiresCRIDockerd(engineName: string, kubeVersion: string | semver.SemVer): boolean {
return engineName === ContainerEngine.MOBY && semver.gte(kubeVersion, '1.24.1') && semver.lte(kubeVersion, '1.24.3');
}

/**
* Validate the cfg.kubernetes.version string
* If it's valid and available, use it.
* Otherwise fall back to the first (recommended) available version.
*/
static async getDesiredVersion(currentConfigVersionString: string|undefined, availableVersions: semver.SemVer[], noModalDialogs: boolean, settingsWriter: (_: any) => void): Promise<semver.SemVer> {
let storedVersion: semver.SemVer|null;
let matchedVersion: semver.SemVer|undefined;
const invalidK8sVersionMainMessage = `Requested kubernetes version '${ currentConfigVersionString }' is not a valid version.`;

// If we're here either there's no existing cfg.k8s.version, or it isn't valid
if (!availableVersions.length) {
if (currentConfigVersionString) {
console.log(invalidK8sVersionMainMessage);
} else {
console.log('Internal error: no available kubernetes versions found.');
}
throw new Error('No kubernetes version available.');
}

if (currentConfigVersionString) {
storedVersion = semver.parse(currentConfigVersionString);
if (storedVersion) {
matchedVersion = availableVersions.find((v) => {
try {
return v.compare(storedVersion as semver.SemVer) === 0;
} catch (err: any) {
console.error(`Can't compare versions ${ storedVersion } and ${ v }: `, err);
if (!(err instanceof TypeError)) {
return false;
}
// We haven't seen a non-TypeError exception here, but it would be worthwhile to have it reported.
// This throw will cause the exception to appear in a non-fatal error reporting dialog box.
throw err;
}
});
if (matchedVersion) {
return matchedVersion;
}
}
const message = invalidK8sVersionMainMessage;
const detail = `Falling back to the most recent stable version of ${ availableVersions[0] }`;

if (noModalDialogs) {
console.log(`${ message } ${ detail }`);
} else {
const options: Electron.MessageBoxOptions = {
message,
detail,
type: 'warning',
buttons: ['OK'],
title: 'Invalid Kubernetes Version',
};

await showMessageBox(options, true);
}
}
// No (valid) stored version; save the default one.
settingsWriter({ kubernetes: { version: availableVersions[0].version } });

return availableVersions[0];
}
}
22 changes: 1 addition & 21 deletions pkg/rancher-desktop/backend/kube/lima.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,28 +298,8 @@ export default class LimaKubernetesBackend extends events.EventEmitter implement
protected get desiredVersion(): Promise<semver.SemVer> {
return (async() => {
const availableVersions = (await this.k3sHelper.availableVersions).map(v => v.version);
const storedVersion = semver.parse(this.cfg?.kubernetes?.version);
const version = storedVersion ?? availableVersions[0];

if (!version) {
throw new Error('No version available');
}

const matchedVersion = availableVersions.find(v => v.compare(version) === 0);

if (matchedVersion) {
if (!storedVersion) {
// No (valid) stored version; save the selected one.
this.vm.writeSetting({ kubernetes: { version: matchedVersion.version } });
}

return matchedVersion;
}

console.error(`Could not use saved version ${ version.raw }, not in ${ availableVersions }`);
this.vm.writeSetting({ kubernetes: { version: availableVersions[0].version } });

return availableVersions[0];
return await BackendHelper.getDesiredVersion(this.cfg?.kubernetes?.version, availableVersions, this.vm.noModalDialogs, this.vm.writeSetting.bind(this.vm));
})();
}

Expand Down
28 changes: 6 additions & 22 deletions pkg/rancher-desktop/backend/kube/wsl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,18 @@ import WSLBackend, { Action } from '../wsl';

import INSTALL_K3S_SCRIPT from '@pkg/assets/scripts/install-k3s';
import { BackendSettings, RestartReasons } from '@pkg/backend/backend';
import BackendHelper from '@pkg/backend/backendHelper';
import * as K8s from '@pkg/backend/k8s';
import { ContainerEngine } from '@pkg/config/settings';
import mainEvents from '@pkg/main/mainEvents';
import { checkConnectivity } from '@pkg/main/networking';
import Logging from '@pkg/utils/logging';
import paths from '@pkg/utils/paths';
import { RecursivePartial } from '@pkg/utils/typeUtils';
import { showMessageBox } from '@pkg/window';

const console = Logging.kube;

export default class WSLKubernetesBackend extends events.EventEmitter implements K8s.KubernetesBackend {
constructor(vm: WSLBackend) {
super();
Expand Down Expand Up @@ -67,31 +71,11 @@ export default class WSLKubernetesBackend extends events.EventEmitter implements
return await K3sHelper.cachedVersionsOnly();
}

get desiredVersion(): Promise<semver.SemVer> {
protected get desiredVersion(): Promise<semver.SemVer> {
return (async() => {
const availableVersions = (await this.k3sHelper.availableVersions).map(v => v.version);
const storedVersion = semver.parse(this.cfg?.kubernetes?.version);
const version = storedVersion ?? availableVersions[0];

if (!version) {
throw new Error('No version available');
}

const matchedVersion = availableVersions.find(v => v.compare(version) === 0);

if (matchedVersion) {
if (!storedVersion) {
// No (valid) stored version; save the selected one.
this.vm.writeSetting({ kubernetes: { version: matchedVersion.version } });
}

return matchedVersion;
}

console.error(`Could not use saved version ${ version.raw }, not in ${ availableVersions }`);
this.vm.writeSetting({ kubernetes: { version: availableVersions[0].version } });

return availableVersions[0];
return await BackendHelper.getDesiredVersion(this.cfg?.kubernetes?.version, availableVersions, this.vm.noModalDialogs, this.vm.writeSetting.bind(this.vm));
})();
}

Expand Down

0 comments on commit 6d6d49e

Please sign in to comment.