Skip to content

Commit

Permalink
Merge pull request #7515 from mook-as/k3s-no-versions-fallback
Browse files Browse the repository at this point in the history
Fix fallback when Kubernetes versions list is unavailable
  • Loading branch information
mook-as authored Sep 23, 2024
2 parents 3358166 + 3766dc2 commit f70fcb4
Show file tree
Hide file tree
Showing 10 changed files with 121 additions and 35 deletions.
9 changes: 8 additions & 1 deletion pkg/rancher-desktop/backend/k3sHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,12 @@ export default class K3sHelper extends events.EventEmitter {

return;
}
await this.updateCache();
try {
await this.updateCache();
} catch (ex) {
console.log(`Ignoring failure to get initial versions list: ${ ex }`);
// At this point this.versions is still empty.
}
})();
}
this.versionFromChannel = {};
Expand Down Expand Up @@ -567,6 +572,8 @@ export default class K3sHelper extends events.EventEmitter {

/**
* The versions that are available to install.
* @note The list will be empty if the machine is offline and we have no
* cached versions.
*/
get availableVersions(): Promise<SemanticVersionEntry[]> {
return (async() => {
Expand Down
18 changes: 11 additions & 7 deletions pkg/rancher-desktop/backend/kube/lima.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export default class LimaKubernetesBackend extends events.EventEmitter implement
const result = await showMessageBox(options, true);

if (result.response !== 0) {
return [undefined, false];
return [undefined, true];
}
}
console.log(`Going with alternative version ${ newVersion.raw }`);
Expand Down Expand Up @@ -324,20 +324,24 @@ export default class LimaKubernetesBackend extends events.EventEmitter implement
protected get desiredVersion(): Promise<semver.SemVer | undefined> {
return (async() => {
let availableVersions: SemanticVersionEntry[];
let available = true;

try {
availableVersions = await this.k3sHelper.availableVersions;

return await BackendHelper.getDesiredVersion(
this.cfg as BackendSettings,
availableVersions,
this.vm.noModalDialogs,
this.vm.writeSetting.bind(this.vm));
} catch (ex) {
console.error(`Could not get desired version: ${ ex }`);
available = false;

return undefined;
} finally {
mainEvents.emit('diagnostics-event', { id: 'kube-versions-available', available });
}

return await BackendHelper.getDesiredVersion(
this.cfg as BackendSettings,
availableVersions,
this.vm.noModalDialogs,
this.vm.writeSetting.bind(this.vm));
})();
}

Expand Down
16 changes: 10 additions & 6 deletions pkg/rancher-desktop/backend/kube/wsl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,20 +77,24 @@ export default class WSLKubernetesBackend extends events.EventEmitter implements
protected get desiredVersion(): Promise<semver.SemVer | undefined> {
return (async() => {
let availableVersions: SemanticVersionEntry[];
let available = true;

try {
availableVersions = await this.k3sHelper.availableVersions;

return await BackendHelper.getDesiredVersion(
this.cfg as BackendSettings,
availableVersions,
this.vm.noModalDialogs,
this.vm.writeSetting.bind(this.vm));
} catch (ex) {
console.error(`Could not get desired version: ${ ex }`);
available = false;

return undefined;
} finally {
mainEvents.emit('diagnostics-event', { id: 'kube-versions-available', available });
}

return await BackendHelper.getDesiredVersion(
this.cfg as BackendSettings,
availableVersions,
this.vm.noModalDialogs,
this.vm.writeSetting.bind(this.vm));
})();
}

Expand Down
17 changes: 13 additions & 4 deletions pkg/rancher-desktop/backend/lima.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1813,14 +1813,23 @@ export default class LimaBackend extends events.EventEmitter implements VMBacken
// Start the VM; if it's already running, this does nothing.
await this.startVM();

// Clear the diagnostic about not having Kubernetes versions
mainEvents.emit('diagnostics-event', { id: 'kube-versions-available', available: true });

if (config.kubernetes.enabled) {
[kubernetesVersion, isDowngrade] = await this.kubeBackend.download(config);

if (typeof (kubernetesVersion) === 'undefined') {
// The desired version was unavailable, and the user declined a downgrade.
await this.setState(State.ERROR);
if (kubernetesVersion === undefined) {
if (isDowngrade) {
// The desired version was unavailable, and the user declined a downgrade.
await this.setState(State.ERROR);

return;
return;
}
// The desired version was unavailable, and we couldn't find a fallback.
// Notify the user, and turn off Kubernetes.
mainEvents.emit('diagnostics-event', { id: 'kube-versions-available', available: false });
this.writeSetting({ kubernetes: { enabled: false } });
}
}

Expand Down
20 changes: 15 additions & 5 deletions pkg/rancher-desktop/backend/wsl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1189,6 +1189,7 @@ export default class WSLBackend extends events.EventEmitter implements VMBackend
const config = this.cfg = _.defaultsDeep(clone(config_),
{ containerEngine: { name: ContainerEngine.NONE } });
let kubernetesVersion: semver.SemVer | undefined;
let isDowngrade = false;

await this.setState(State.STARTING);
this.currentAction = Action.STARTING;
Expand All @@ -1203,16 +1204,25 @@ export default class WSLBackend extends events.EventEmitter implements VMBackend

if (config.kubernetes.enabled) {
prepActions.push((async() => {
[kubernetesVersion] = await this.kubeBackend.download(config);
[kubernetesVersion, isDowngrade] = await this.kubeBackend.download(config);
})());
}

// Clear the diagnostic about not having Kubernetes versions
mainEvents.emit('diagnostics-event', { id: 'kube-versions-available', available: true });

await this.progressTracker.action('Preparing to start', 0, Promise.all(prepActions));
if (config.kubernetes.enabled && typeof (kubernetesVersion) === 'undefined') {
// The desired version was unavailable, and the user declined a downgrade.
this.setState(State.ERROR);
if (config.kubernetes.enabled && kubernetesVersion === undefined) {
if (isDowngrade) {
// The desired version was unavailable, and the user declined a downgrade.
this.setState(State.ERROR);

return;
return;
}
// The desired version was unavailable, and we couldn't find a fallback.
// Notify the user, and turn off Kubernetes.
mainEvents.emit('diagnostics-event', { id: 'kube-versions-available', available: false });
this.writeSetting({ kubernetes: { enabled: false } });
}
if (this.currentAction !== Action.STARTING) {
// User aborted before we finished
Expand Down
16 changes: 12 additions & 4 deletions pkg/rancher-desktop/integrations/pathManagerImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,13 @@ export class RcFilePathManager implements PathManager {
protected async manageLinesInFile(fileName: string, filePath: string, lines: string[], desiredPresent: boolean) {
try {
await manageLinesInFile(filePath, lines, desiredPresent);
mainEvents.emit('diagnostics-event', 'path-management', { fileName, error: undefined });
mainEvents.emit('diagnostics-event', {
id: 'path-management', fileName, error: undefined,
});
} catch (error: any) {
mainEvents.emit('diagnostics-event', 'path-management', { fileName, error });
mainEvents.emit('diagnostics-event', {
id: 'path-management', fileName, error,
});
throw error;
}
}
Expand Down Expand Up @@ -96,10 +100,14 @@ export class RcFilePathManager implements PathManager {
} catch (error: any) {
if (error.code === 'ENOENT') {
// If the file does not exist, it is not an error.
mainEvents.emit('diagnostics-event', 'path-management', { fileName, error: undefined });
mainEvents.emit('diagnostics-event', {
id: 'path-management', fileName, error: undefined,
});
continue;
}
mainEvents.emit('diagnostics-event', 'path-management', { fileName, error });
mainEvents.emit('diagnostics-event', {
id: 'path-management', fileName, error,
});
throw error;
}
await this.manageLinesInFile(fileName, filePath, [pathLine], !linesAdded);
Expand Down
1 change: 1 addition & 0 deletions pkg/rancher-desktop/main/diagnostics/diagnostics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export class DiagnosticsManager {
import('./dockerCliSymlinks'),
import('./kubeConfigSymlink'),
import('./kubeContext'),
import('./kubeVersionsAvailable'),
import('./limaDarwin'),
import('./mockForScreenshots'),
import('./pathManagement'),
Expand Down
44 changes: 44 additions & 0 deletions pkg/rancher-desktop/main/diagnostics/kubeVersionsAvailable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import mainEvents from '../mainEvents';
import { DiagnosticsCategory, DiagnosticsChecker, DiagnosticsCheckerResult } from './types';

let kubeVersionsAvailable = true;

mainEvents.on('diagnostics-event', (payload) => {
if (payload.id !== 'kube-versions-available') {
return;
}
kubeVersionsAvailable = payload.available;
mainEvents.invoke('diagnostics-trigger', instance.id);
});

/**
* KubeVersionsAvailable is a diagnostic that will be emitted when all of the
* following are met:
* - Kubernetes was configured to be enabled
* - The selected Kubernetes version is unavailable (e.g. user is offline)
* Once the diagnostic is triggered, it stays on until the backend is restarted.
*/
class KubeVersionsAvailable implements DiagnosticsChecker {
readonly id = 'KUBE_VERSIONS_AVAILABLE';
readonly category = DiagnosticsCategory.Kubernetes;
applicable(): Promise<boolean> {
return Promise.resolve(true);
}

check(): Promise<DiagnosticsCheckerResult> {
const description = [
'There are no issues with Kubernetes versions',
'Kubernetes has been disabled due to issues with fetching Kubernetes versions',
][kubeVersionsAvailable ? 0 : 1];

return Promise.resolve({
passed: kubeVersionsAvailable,
description,
fixes: [{ description: 'Check your network connection to update.k3s.io' }],
});
}
}

const instance = new KubeVersionsAvailable();

export default instance;
7 changes: 3 additions & 4 deletions pkg/rancher-desktop/main/diagnostics/pathManagement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,11 @@ const CheckPathManagement: DiagnosticsChecker = {
},
};

mainEvents.on('diagnostics-event', (id, state) => {
if (id !== 'path-management') {
mainEvents.on('diagnostics-event', (payload) => {
if (payload.id !== 'path-management') {
return;
}
const typedState: { fileName: string, error: Error | undefined } = state;
const { fileName, error } = typedState;
const { fileName, error } = payload;

cachedResults[fileName] = {
description: error?.message ?? error?.toString() ?? `Unknown error managing ${ fileName }`,
Expand Down
8 changes: 4 additions & 4 deletions pkg/rancher-desktop/main/mainEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ interface MainEventNames {
* @param id The diagnostic identifier.
* @param state The new state for the diagnostic.
*/
'diagnostics-event'<K extends keyof DiagnosticsEventPayload>(id: K, state: DiagnosticsEventPayload[K]): void;
'diagnostics-event'(payload: DiagnosticsEventPayload): void;

/**
* Emitted when an extension is uninstalled via the extension manager.
Expand Down Expand Up @@ -154,9 +154,9 @@ interface MainEventNames {
* DiagnosticsEventPayload defines the data that will be passed on a
* 'diagnostics-event' event.
*/
type DiagnosticsEventPayload = {
'path-management': { fileName: string; error: Error | undefined };
};
type DiagnosticsEventPayload =
{ id: 'kube-versions-available', available: boolean } |
{ id: 'path-management', fileName: string; error: Error | undefined };

/**
* Helper type definition to check if the given event name is a handler (i.e.
Expand Down

0 comments on commit f70fcb4

Please sign in to comment.