Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
Signed-off-by: Jan Dubois <[email protected]>
  • Loading branch information
jandubois committed Mar 2, 2023
1 parent 37f5365 commit 4f4d8e8
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 9 deletions.
7 changes: 6 additions & 1 deletion e2e/rdctl.e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
CacheMode,
ProtocolVersion,
SecurityModel,
VMType,
} from '@pkg/config/settings';
import { PathManagementStrategy } from '@pkg/integrations/pathManager';
import { ServerState } from '@pkg/main/commandServer/httpCommandServer';
Expand Down Expand Up @@ -708,6 +709,8 @@ test.describe('Command server', () => {
['experimental.virtual-machine.mount.9p.security-model', SecurityModel.NONE],
['experimental.virtual-machine.mount.type', MountType.NINEP],
['experimental.virtual-machine.socket-vmnet', true],
['experimental.virtual-machine.use-rosetta', true],
['experimental.virtual-machine.type', VMType.VZ],
['virtual-machine.memory-in-gb', 10],
['virtual-machine.number-cpus', 10],
],
Expand All @@ -716,6 +719,8 @@ test.describe('Command server', () => {
],
linux: [
['experimental.virtual-machine.socket-vmnet', true],
['experimental.virtual-machine.use-rosetta', true],
['experimental.virtual-machine.type', VMType.VZ],
['virtual-machine.host-resolver', true],
],
};
Expand All @@ -732,7 +737,7 @@ test.describe('Command server', () => {
stdout: '',
});
expect(stderr).toContain('Usage:');
expect(stderr.split(/\n/).filter(line => /^\s+--/.test(line)).length).toBe(35 - unsupportedOptions.length);
expect(stderr.split(/\n/).filter(line => /^\s+--/.test(line)).length).toBe(37 - unsupportedOptions.length);
});

test('complains when option value missing', async() => {
Expand Down
9 changes: 8 additions & 1 deletion pkg/rancher-desktop/assets/specs/command-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ components:
properties:
type:
type: string
enum: ['reverse-sshfs', '9p']
enum: ['reverse-sshfs', '9p', 'virtiofs']
9p:
type: object
properties:
Expand All @@ -361,6 +361,13 @@ components:
networkingTunnel:
type: boolean
x-rd-platforms: ['win32']
type:
type: string
enum: ['qemu', 'vz']
x-rd-platforms: ['darwin']
useRosetta:
type: boolean
x-rd-platforms: ['darwin']
WSL:
type: object
x-rd-platforms: ['win32']
Expand Down
27 changes: 24 additions & 3 deletions pkg/rancher-desktop/backend/lima.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ export type LimaMount = {
* Lima configuration
*/
export type LimaConfiguration = {
vmType?: 'qemu' | 'vz';
rosetta?: {
enabled?: boolean;
binfmt?: boolean;
},
arch?: 'x86_64' | 'aarch64';
images: {
location: string;
Expand All @@ -96,7 +101,7 @@ export type LimaConfiguration = {
memory?: number;
disk?: number;
mounts?: LimaMount[];
mountType: string;
mountType: 'reverse-sshfs' | '9p' | 'virtiofs';
ssh: {
localPort: number;
loadDotSSHPubKeys?: boolean;
Expand Down Expand Up @@ -160,6 +165,7 @@ interface LimaListResult {
status: 'Broken' | 'Stopped' | 'Running';
dir: string;
arch: 'x86_64' | 'aarch64';
vmType?: 'qemu' | 'vz';
sshLocalPort?: number;
hostAgentPID?: number;
qemuPID?: number;
Expand Down Expand Up @@ -617,6 +623,11 @@ export default class LimaBackend extends events.EventEmitter implements VMBacken
// We use {} as the first argument because merge() modifies
// it, and it would be less safe to modify baseConfig.
const config: LimaConfiguration = merge({}, baseConfig, DEFAULT_CONFIG as LimaConfiguration, {
vmType: this.cfg?.experimental.virtualMachine.type,
rosetta: {
enabled: this.cfg?.experimental.virtualMachine.useRosetta,
binfmt: this.cfg?.experimental.virtualMachine.useRosetta,
},
images: [{
location: this.baseDiskImage,
arch: this.arch,
Expand Down Expand Up @@ -1705,9 +1716,17 @@ export default class LimaBackend extends events.EventEmitter implements VMBacken
return;
}

// Start the VM; if it's already running, this does nothing.
const isVMAlreadyRunning = (await this.status)?.status === 'Running';
const vmStatus = await this.status;
const isVMAlreadyRunning = vmStatus?.status === 'Running';

// Delete existing VM if the user modified the vmType.
if (vmStatus && vmStatus.vmType !== config.experimental.virtualMachine.type) {
await this.del(true);
// Restore this.cfg because `del` sets it to undefined.
this.cfg = config;
}

// Start the VM; if it's already running, this does nothing.
await this.startVM();

if (config.kubernetes.enabled) {
Expand Down Expand Up @@ -2019,6 +2038,8 @@ CREDFWD_URL='http://${ hostIPAddr }:${ stateInfo.port }'
'experimental.virtualMachine.mount.9p.protocolVersion': undefined,
'experimental.virtualMachine.mount.9p.securityModel': undefined,
'experimental.virtualMachine.mount.type': undefined,
'experimental.virtualMachine.useRosetta': undefined,
'experimental.virtualMachine.type': undefined,
}));
if (process.platform === 'darwin') {
Object.assign(reasons, this.kubeBackend.k3sHelper.requiresRestartReasons(this.cfg, cfg, { 'experimental.virtualMachine.socketVMNet': undefined }));
Expand Down
8 changes: 6 additions & 2 deletions pkg/rancher-desktop/config/__tests__/settings.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import fs from 'fs';

import * as settings from '../settings';
import { CacheMode, MountType, ProtocolVersion, SecurityModel } from '../settings';
import {
CacheMode, MountType, ProtocolVersion, SecurityModel, VMType,
} from '../settings';

import { TransientSettings } from '@pkg/config/transientSettings';
import { PathManagementStrategy } from '@pkg/integrations/pathManager';
Expand Down Expand Up @@ -51,8 +53,10 @@ describe('updateFromCommandLine', () => {
cacheMode: CacheMode.MMAP,
},
},
socketVMNet: true,
networkingTunnel: false,
socketVMNet: true,
useRosetta: false,
type: VMType.QEMU,
},
},
WSL: { integrations: {} },
Expand Down
8 changes: 8 additions & 0 deletions pkg/rancher-desktop/config/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ const console = Logging.settings;

export const CURRENT_SETTINGS_VERSION = 6 as const;

export enum VMType {
QEMU = 'qemu',
VZ = 'vz',
}
export enum ContainerEngine {
NONE = '',
CONTAINERD = 'containerd',
Expand Down Expand Up @@ -119,6 +123,10 @@ export const defaultSettings = {
*/
experimental: {
virtualMachine: {
/** can only be set to VMType.VZ on macOS Ventura and later */
type: VMType.QEMU,
/** can only be used when type is VMType.VZ, and only on aarch64 */
useRosetta: false,
/** macOS only: if set, use socket_vmnet instead of vde_vmnet. */
socketVMNet: false,
mount: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ describe(SettingsValidator, () => {
['experimental', 'virtualMachine', 'mount', '9p', 'protocolVersion'],
['experimental', 'virtualMachine', 'mount', '9p', 'securityModel'],
['experimental', 'virtualMachine', 'mount', 'type'],
['experimental', 'virtualMachine', 'useRosetta'],
['experimental', 'virtualMachine', 'type'],
['kubernetes', 'version'],
['version'],
['WSL', 'integrations'],
Expand Down
49 changes: 47 additions & 2 deletions pkg/rancher-desktop/main/commandServer/settingsValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import os from 'os';
import _ from 'lodash';

import {
CacheMode, defaultSettings, MountType, ProtocolVersion, SecurityModel, Settings,
CacheMode, defaultSettings, MountType, ProtocolVersion, SecurityModel, Settings, VMType,
} from '@pkg/config/settings';
import { NavItemName, navItemNames, TransientSettings } from '@pkg/config/transientSettings';
import { PathManagementStrategy } from '@pkg/integrations/pathManager';
Expand Down Expand Up @@ -97,6 +97,11 @@ export default class SettingsValidator {
},
socketVMNet: this.checkPlatform('darwin', this.checkBoolean),
networkingTunnel: this.checkPlatform('win32', this.checkBoolean),
useRosetta: this.checkPlatform('darwin', this.checkRosetta),
type: this.checkPlatform('darwin', this.checkMulti(
this.checkEnum(...Object.values(VMType)),
this.checkVMType),
),
},
},
WSL: { integrations: this.checkPlatform('win32', this.checkBooleanMapping) },
Expand Down Expand Up @@ -188,7 +193,8 @@ export default class SettingsValidator {

if (!(k in allowedSettings)) {
continue;
} else if (typeof (allowedSettings[k]) === 'object') {
}
if (typeof (allowedSettings[k]) === 'object') {
if (typeof (newSettings[k]) === 'object') {
changeNeeded = this.checkProposedSettings(mergedSettings, allowedSettings[k], currentSettings[k], newSettings[k], errors, fqname) || changeNeeded;
} else {
Expand Down Expand Up @@ -239,6 +245,33 @@ export default class SettingsValidator {
};
}

protected checkRosetta(mergedSettings: Settings, currentValue: boolean, desiredValue: boolean, errors: string[], fqname: string): boolean {
if (desiredValue) {
if (mergedSettings.experimental.virtualMachine.type !== VMType.VZ) {
errors.push(`Setting ${ fqname } can only be enabled when experimental.virtual-machine.vm-type is "${ VMType.VZ }".`);

return false;
}
if (!Electron.app.runningUnderARM64Translation && os.arch() !== 'arm64') {
errors.push(`Setting ${ fqname } can only be enabled on aarch64 systems.`);

return false;
}
}

return currentValue !== desiredValue;
}

protected checkVMType(mergedSettings: Settings, currentValue: string, desiredValue: string, errors: string[], fqname: string): boolean {
if (desiredValue === VMType.VZ && parseInt(os.release()) < 22) {
errors.push(`Setting ${ fqname } to "${ VMType.VZ }" requires macOS 13.0 (Ventura) or later.`);

return false;
}

return currentValue !== desiredValue;
}

protected checkPlatform<C, D>(platform: NodeJS.Platform, validator: ValidatorFunc<Settings, C, D>) {
return (mergedSettings: Settings, currentValue: C, desiredValue: D, errors: string[], fqname: string) => {
if (!_.isEqual(currentValue, desiredValue)) {
Expand Down Expand Up @@ -267,6 +300,18 @@ export default class SettingsValidator {
};
}

protected checkMulti<S, C, D>(...validators: ValidatorFunc<S, C, D>[]) {
return (mergedSettings: S, currentValue: C, desiredValue: D, errors: string[], fqname: string) => {
let retval = false;

for (const validator of validators) {
retval ||= validator.call(this, mergedSettings, currentValue, desiredValue, errors, fqname);
}

return retval;
};
}

/**
* checkBoolean is a generic checker for simple boolean values.
*/
Expand Down

0 comments on commit 4f4d8e8

Please sign in to comment.