Skip to content

Commit

Permalink
Merge pull request #7461 from mook-as/k3s/channel-server-dependency
Browse files Browse the repository at this point in the history
Kubernetes: Support starting with no versions
  • Loading branch information
jandubois authored Sep 12, 2024
2 parents bcac1f4 + d74a5de commit dcc9841
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 51 deletions.
11 changes: 8 additions & 3 deletions background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -827,10 +827,15 @@ ipcMainProxy.on('k8s-restart', async() => {
});

ipcMainProxy.on('k8s-versions', async() => {
const versions = await k8smanager.kubeBackend.availableVersions;
const cachedOnly = await k8smanager.kubeBackend.cachedVersionsOnly();
try {
const versions = await k8smanager.kubeBackend.availableVersions;
const cachedOnly = await k8smanager.kubeBackend.cachedVersionsOnly();

window.send('k8s-versions', versions.map(v => v.versionEntry), cachedOnly);
window.send('k8s-versions', versions.map(v => v.versionEntry), cachedOnly);
} catch (ex) {
console.error(`Error handling k8s-versions: ${ ex }`);
window.send('k8s-versions', [], true);
}
});

ipcMainProxy.on('k8s-progress', () => {
Expand Down
27 changes: 20 additions & 7 deletions pkg/rancher-desktop/backend/__tests__/k3sHelper.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,9 @@ describe(K3sHelper, () => {

return Promise.resolve(new FetchResponse(
JSON.stringify({
data: [{
resourceType: 'channels',
data: [{
type: 'channel',
name: 'stable',
latest: 'v1.99.3+k3s3',
}],
Expand Down Expand Up @@ -314,12 +316,23 @@ describe(K3sHelper, () => {

return Promise.resolve(new FetchResponse(
JSON.stringify({
data: [
{ name: 'v1.96', latest: '1.96.9+k3s1' },
{ name: 'v1.97', latest: '1.97.7+k3s1' },
{ name: 'stable', latest: '1.97.7+k3s1' },
{ name: 'latest', latest: '1.98.3+k3s1' },
{ name: 'v1.98', latest: '1.98.3+k3s1' },
resourceType: 'channels',
data: [
{
type: 'channel', name: 'v1.96', latest: '1.96.9+k3s1',
},
{
type: 'channel', name: 'v1.97', latest: '1.97.7+k3s1',
},
{
type: 'channel', name: 'stable', latest: '1.97.7+k3s1',
},
{
type: 'channel', name: 'latest', latest: '1.98.3+k3s1',
},
{
type: 'channel', name: 'v1.98', latest: '1.98.3+k3s1',
},
],
}),
));
Expand Down
46 changes: 35 additions & 11 deletions pkg/rancher-desktop/backend/k3sHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ import type Electron from 'electron';
const console = Logging.k8s;

/**
* ShortVersion is the version string without any k3s suffixes; this is the
* version we present to the user.
* ShortVersion is the version string without any k3s suffixes, nor a "v"
* prefix; this is the version we present to the user.
*/
export type ShortVersion = string;

Expand All @@ -65,7 +65,7 @@ type cacheData = {
/** List of available versions; includes build information. */
versions: string[];
/** Mapping of channel labels to current version (excluding build information). */
channels: Record<string, string>;
channels: Record<string, ShortVersion>;
};

/**
Expand Down Expand Up @@ -132,7 +132,11 @@ export default class K3sHelper extends events.EventEmitter {
protected readonly releaseApiAccept = 'application/vnd.github.v3+json';
protected readonly cachePath = path.join(paths.cache, 'k3s-versions.json');
protected readonly minimumVersion = new semver.SemVer('1.21.0');
protected versionFromChannel: Record<string, string> = {};
/**
* versionFromChannel is a mapping from the channel name to the latest (short)
* version in that channel.
*/
protected versionFromChannel: Record<string, ShortVersion> = {};

constructor(arch: Architecture) {
super();
Expand Down Expand Up @@ -348,6 +352,10 @@ export default class K3sHelper extends events.EventEmitter {
return a.localeCompare(b);
}

/**
* Fetch the list of available Kubernetes versions.
* @throws If there were issues fetching the list of versions.
*/
protected async updateCache(): Promise<void> {
try {
let wantMoreVersions = true;
Expand All @@ -360,12 +368,8 @@ export default class K3sHelper extends events.EventEmitter {
let channelResponse: Response;

try {
const headers: HeadersInit = { Accept: this.channelApiAccept };

if (process.env.GITHUB_TOKEN) {
headers.Authorization = `Bearer ${ process.env.GITHUB_TOKEN }`;
}
channelResponse = await fetch(this.channelApiUrl, { headers });
channelResponse = await fetch(this.channelApiUrl,
{ headers: { Accept: this.channelApiAccept } });
} catch (ex: any) {
console.log(`updateCache: error: ${ ex }`);
if (!(await checkConnectivity('k3s.io'))) {
Expand All @@ -376,10 +380,30 @@ export default class K3sHelper extends events.EventEmitter {
}

if (channelResponse.ok) {
const channels = (await channelResponse.json()) as { data?: { name: string, latest: string }[] };
const ResourceTypeChannels = 'channels';
const DataTypeChannel = 'channel';

type ChannelResponse = {
resourceType: typeof ResourceTypeChannels;
data?: {
type: typeof DataTypeChannel;
name: string;
latest: string;
}[];
};
const channels: ChannelResponse = await channelResponse.json();

console.debug(`Got K3s update channel data: ${ channels.data?.map(ch => ch.name) }`);
if (channels.resourceType !== ResourceTypeChannels) {
throw new Error(`Channel response does not have correct resource type: ${ channels.resourceType }`);
}

for (const channel of channels.data ?? []) {
if (channel.type !== DataTypeChannel) {
// The channel entry is invalid; ignore it.
continue;
}

const version = semver.parse(channel.latest);

if (version) {
Expand Down
21 changes: 19 additions & 2 deletions pkg/rancher-desktop/backend/kube/lima.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ export default class LimaKubernetesBackend extends events.EventEmitter implement
this.k3sHelper.on('versions-updated', () => this.emit('versions-updated'));
this.k3sHelper.initialize().catch((err) => {
console.log('k3sHelper.initialize failed: ', err);
// If we fail to initialize, we still need to continue (with no versions).
this.emit('versions-updated');
});
mainEvents.on('network-ready', () => this.k3sHelper.networkReady());
}
Expand Down Expand Up @@ -71,6 +73,13 @@ export default class LimaKubernetesBackend extends events.EventEmitter implement
try {
const persistedVersion = await K3sHelper.getInstalledK3sVersion(this.vm);
const desiredVersion = await this.desiredVersion;

if (desiredVersion === undefined) {
// If we could not determine the desired version (e.g. we have no cached
// versions and the machine is offline), bail out.
return [undefined, false];
}

const isDowngrade = (version: semver.SemVer | string) => {
return !!persistedVersion && semver.gt(persistedVersion, version);
};
Expand Down Expand Up @@ -312,9 +321,17 @@ export default class LimaKubernetesBackend extends events.EventEmitter implement
return this.cfg?.kubernetes?.port ?? 6443;
}

protected get desiredVersion(): Promise<semver.SemVer> {
protected get desiredVersion(): Promise<semver.SemVer | undefined> {
return (async() => {
const availableVersions = await this.k3sHelper.availableVersions;
let availableVersions: SemanticVersionEntry[];

try {
availableVersions = await this.k3sHelper.availableVersions;
} catch (ex) {
console.error(`Could not get desired version: ${ ex }`);

return undefined;
}

return await BackendHelper.getDesiredVersion(
this.cfg as BackendSettings,
Expand Down
22 changes: 18 additions & 4 deletions pkg/rancher-desktop/backend/kube/wsl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ export default class WSLKubernetesBackend extends events.EventEmitter implements
this.k3sHelper.on('versions-updated', () => this.emit('versions-updated'));
this.k3sHelper.initialize().catch((err) => {
console.log('k3sHelper.initialize failed: ', err);
// If we fail to initialize, we still need to continue (with no versions).
this.emit('versions-updated');
});
mainEvents.on('network-ready', () => this.k3sHelper.networkReady());
}
Expand Down Expand Up @@ -72,9 +74,17 @@ export default class WSLKubernetesBackend extends events.EventEmitter implements
return await K3sHelper.cachedVersionsOnly();
}

protected get desiredVersion(): Promise<semver.SemVer> {
protected get desiredVersion(): Promise<semver.SemVer | undefined> {
return (async() => {
const availableVersions = await this.k3sHelper.availableVersions;
let availableVersions: SemanticVersionEntry[];

try {
availableVersions = await this.k3sHelper.availableVersions;
} catch (ex) {
console.error(`Could not get desired version: ${ ex }`);

return undefined;
}

return await BackendHelper.getDesiredVersion(
this.cfg as BackendSettings,
Expand Down Expand Up @@ -105,8 +115,8 @@ export default class WSLKubernetesBackend extends events.EventEmitter implements

/**
* Download K3s images. This will also calculate the version to download.
* @returns The version of K3s images downloaded. If startup should not
* continue, INVALID_VERSION is returned instead.
* @returns The version of K3s images downloaded, and whether this is a
* downgrade.
*/
async download(cfg: BackendSettings): Promise<[semver.SemVer | undefined, boolean]> {
this.cfg = cfg;
Expand All @@ -129,6 +139,10 @@ export default class WSLKubernetesBackend extends events.EventEmitter implements
try {
const desiredVersion = await this.desiredVersion;

if (desiredVersion === undefined) {
return [undefined, false];
}

try {
await this.progressTracker.action('Checking k3s images', 100, this.k3sHelper.ensureK3sImages(desiredVersion));

Expand Down
50 changes: 26 additions & 24 deletions pkg/rancher-desktop/pages/FirstRun.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
</h2>
<rd-checkbox
label="Enable Kubernetes"
:value="settings.kubernetes.enabled"
:value="hasVersions && settings.kubernetes.enabled"
:is-locked="kubernetesLocked"
:disabled="!hasVersions"
@input="handleDisableKubernetesCheckbox"
/>
<rd-fieldset
Expand Down Expand Up @@ -98,10 +99,11 @@ import RdSelect from '@pkg/components/RdSelect.vue';
import RdCheckbox from '@pkg/components/form/RdCheckbox.vue';
import RdFieldset from '@pkg/components/form/RdFieldset.vue';
import { defaultSettings } from '@pkg/config/settings';
import type { ContainerEngine } from '@pkg/config/settings';
import type { ContainerEngine, Settings } from '@pkg/config/settings';
import { PathManagementStrategy } from '@pkg/integrations/pathManager';
import { ipcRenderer } from '@pkg/utils/ipcRenderer';
import { highestStableVersion, VersionEntry } from '@pkg/utils/kubeVersions';
import { RecursivePartial } from '@pkg/utils/typeUtils';
export default Vue.extend({
components: {
Expand Down Expand Up @@ -138,6 +140,9 @@ export default Vue.extend({
return wrappedSemver ? wrappedSemver.version : '';
},
hasVersions(): boolean {
return this.versions.length > 0;
},
/** Versions that are the tip of a channel */
recommendedVersions(): VersionEntry[] {
return this.versions.filter(v => !!v.channels);
Expand All @@ -163,6 +168,9 @@ export default Vue.extend({
this.versions = versions;
this.cachedVersionsOnly = cachedVersionsOnly;
this.settings.kubernetes.version = this.unwrappedDefaultVersion;
if (!this.hasVersions) {
ipcRenderer.invoke('settings-write', { kubernetes: { enabled: false } });
}
// Manually send the ready event here, as we do not use the normal
// "dialog/populate" event.
ipcRenderer.send('dialog/ready');
Expand All @@ -186,37 +194,31 @@ export default Vue.extend({
window.removeEventListener('beforeunload', this.close);
},
methods: {
async commitChanges(settings: RecursivePartial<Settings>) {
try {
return await ipcRenderer.invoke('settings-write', settings);
} catch (ex) {
console.log(`invoke settings-write failed: `, ex);
}
},
onChange() {
ipcRenderer.invoke(
'settings-write',
{
kubernetes: { version: this.settings.kubernetes.version },
application: { pathManagementStrategy: this.pathManagementStrategy },
});
return this.commitChanges({
application: { pathManagementStrategy: this.pathManagementStrategy },
kubernetes: {
version: this.settings.kubernetes.version,
enabled: this.settings.kubernetes.enabled && this.hasVersions,
},
});
},
close() {
this.onChange();
window.close();
},
onChangeEngine(desiredEngine: ContainerEngine) {
try {
ipcRenderer.invoke(
'settings-write',
{ containerEngine: { name: desiredEngine } },
);
} catch (err) {
console.log('invoke settings-write failed: ', err);
}
return this.commitChanges({ containerEngine: { name: desiredEngine } });
},
handleDisableKubernetesCheckbox(value: boolean) {
try {
ipcRenderer.invoke(
'settings-write',
{ kubernetes: { enabled: value } },
);
} catch (err) {
console.log('invoke settings-write failed: ', err);
}
return this.commitChanges({ kubernetes: { enabled: value } });
},
/**
* Get the display name of a given version.
Expand Down

0 comments on commit dcc9841

Please sign in to comment.