Skip to content

Commit

Permalink
Merge pull request #1302 from mook-as/wsl-dnsmasq
Browse files Browse the repository at this point in the history
WSL: Use dnsmasq
  • Loading branch information
mook-as authored Feb 8, 2022
2 parents ae8bd71 + c6ac2d8 commit d34cc89
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 3 deletions.
2 changes: 1 addition & 1 deletion scripts/download/wsl.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import path from 'path';
import { download } from '../lib/download.mjs';

export default async function main() {
const v = '0.12.1';
const v = '0.14';

await download(
`https://github.com/rancher-sandbox/rancher-desktop-wsl-distro/releases/download/v${ v }/distro-${ v }.tar`,
Expand Down
14 changes: 14 additions & 0 deletions src/assets/scripts/wsl-data.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# This is the /etc/wsl.conf for use with rancher-desktop-data
# As we do not have an actual data distribution, this file is included as part
# of the application and written out at runtime.

[automount]
# Prevent processing /etc/fstab, since it doesn't exist.
mountFsTab = false
# Prevent running ldconfig, since that doesn't exist.
ldconfig = false
# Needed for compatibility with some `npm install` scenarios.
options = metadata

# We _do_ want to generate `/etc/hosts` here, so that it can be used by the main
# distribution.
2 changes: 2 additions & 0 deletions src/k8s-engine/progressTracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ export default class ProgressTracker {

/**
* Register an action.
* @param description Descriptive text for the action, to be shown to the user.
* @param priority Only the action with the largest priority will be shown among concurrent actions.
* @returns A promise that will be resolved when the passed-in promise resolves.
*/
action<T>(description: string, priority: number, promise: Promise<T>): Promise<T>;
Expand Down
95 changes: 93 additions & 2 deletions src/k8s-engine/wsl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import LOGROTATE_K3S_SCRIPT from '@/assets/scripts/logrotate-k3s';
import SERVICE_BUILDKITD_INIT from '@/assets/scripts/buildkit.initd';
import SERVICE_BUILDKITD_CONF from '@/assets/scripts/buildkit.confd';
import INSTALL_WSL_HELPERS_SCRIPT from '@/assets/scripts/install-wsl-helpers';
import SCRIPT_DATA_WSL_CONF from '@/assets/scripts/wsl-data.conf';
import mainEvents from '@/main/mainEvents';
import * as childProcess from '@/utils/childProcess';
import Logging from '@/utils/logging';
Expand Down Expand Up @@ -59,7 +60,7 @@ const DISTRO_BLACKLIST = [
];

/** The version of the WSL distro we expect. */
const DISTRO_VERSION = '0.12.1';
const DISTRO_VERSION = '0.14';

/**
* The list of directories that are in the data distribution (persisted across
Expand Down Expand Up @@ -441,12 +442,12 @@ export default class WSLBackend extends events.EventEmitter implements K8s.Kuber
try {
// Create a distro archive from the main distro.
// WSL seems to require a working /bin/sh for initialization.
const OVERRIDE_FILES = { 'etc/wsl.conf': SCRIPT_DATA_WSL_CONF };
const REQUIRED_FILES = [
'/bin/busybox', // Base tools
'/bin/mount', // Required for WSL startup
'/bin/sh', // WSL requires a working shell to initialize
'/lib', // Dependencies for busybox
'/etc/wsl.conf', // WSL configuration for minimal startup
'/etc/passwd', // So WSL can spawn programs as a user
];
const archivePath = path.join(workdir, 'distro.tar');
Expand All @@ -471,6 +472,20 @@ export default class WSLBackend extends events.EventEmitter implements K8s.Kuber

await this.execCommand('tar', '-cf', await this.wslify(archivePath),
'-C', '/', ...extraFiles, ...DISTRO_DATA_DIRS);

// The tar-stream package doesn't handle appends well (needs to
// stream to a temporary file), and busybox tar doesn't support
// append either. Luckily Windows ships with a bsdtar that
// supports it, though it only supports short options.
for (const [relPath, contents] of Object.entries(OVERRIDE_FILES)) {
const absPath = path.join(workdir, 'tar', relPath);

await fs.promises.mkdir(path.dirname(absPath), { recursive: true });
await fs.promises.writeFile(absPath, contents);
}
await childProcess.spawnFile('tar.exe',
['-r', '-f', archivePath, '-C', path.join(workdir, 'tar'), ...Object.keys(OVERRIDE_FILES)]);
await this.execCommand('tar', '-tvf', await this.wslify(archivePath));
await this.execWSL('--import', DATA_INSTANCE_NAME, paths.wslDistroData, archivePath, '--version', '2');
} catch (ex) {
console.log(`Error registering data distribution: ${ ex }`);
Expand Down Expand Up @@ -512,6 +527,55 @@ export default class WSLBackend extends events.EventEmitter implements K8s.Kuber
}
}

/**
* Write out /etc/hosts in the main distribution, copying the bulk of the
* contents from the data distribution.
*/
protected async writeHostsFile() {
await this.progressTracker.action('Updating /etc/hosts', 50, async() => {
const contents = await fs.promises.readFile(`\\\\wsl$\\${ DATA_INSTANCE_NAME }\\etc\\hosts`);
const hosts = ['host.rancher-desktop.internal', 'host.docker.internal'];
const extra = [
'# BEGIN Rancher Desktop configuration.',
`${ this.hostIPAddress } ${ hosts.join(' ') }`,
'# END Rancher Desktop configuration.',
].map(l => `${ l }\n`).join('');

await fs.promises.writeFile(`\\\\wsl$\\${ INSTANCE_NAME }\\etc\\hosts`,
Buffer.concat([contents, Buffer.from(extra, 'utf-8')]));
});
}

/**
* Write configuration for dnsmasq / and /etc/resolv.conf; required before [runInit].
*/
protected async writeResolvConf() {
await this.progressTracker.action('Updating DNS configuration', 50,
// Tell dnsmasq to use the resolv.conf from the data distro as the
// upstream configuration.
Promise.all([
(async() => {
try {
const contents = await this.readFile(
'/run/resolvconf/resolv.conf', { distro: DATA_INSTANCE_NAME });

await this.writeFile('/etc/dnsmasq.d/data-resolv-conf', contents);
} catch (ex) {
console.error('Failed to copy existing resolv.conf');
throw ex;
}
})(),
this.writeFile(
'/etc/dnsmasq.d/rancher-desktop.conf',
Object.entries({
'resolv-file': '/etc/dnsmasq.d/data-resolv-conf',
'listen-address': await this.ipAddress,
}).map(([k, v]) => `${ k }=${ v }\n`).join('')),
this.writeFile('/etc/resolv.conf', `nameserver ${ await this.ipAddress }`),
this.writeConf('dnsmasq', { DNSMASQ_OPTS: '--user=dnsmasq --group=dnsmasq' }),
]));
}

/**
* Mount the data distribution over.
*/
Expand Down Expand Up @@ -655,6 +719,24 @@ export default class WSLBackend extends events.EventEmitter implements K8s.Kuber
}
}

/**
* Read the given file in a WSL distribution
* @param [filePath] the path of the file to read.
* @param [options] Optional configuratino for reading the file.
* @param [options.distro=INSTANCE_NAME] The distribution to read from.
* @param [options.encoding='utf-8'] The encoding to use for the result.
*/
protected async readFile(filePath: string, options?: Partial<{ distro: typeof INSTANCE_NAME | typeof DATA_INSTANCE_NAME, encoding : BufferEncoding}>) {
const distro = options?.distro ?? INSTANCE_NAME;
const encoding = options?.encoding ?? 'utf-8';
// Run wslpath here, to ensure that WSL generates any files we need.
const windowsPath = (await this.execWSL({ capture: true, encoding },
'--distribution', distro,
'/bin/wslpath', '-w', filePath)).trim();

return await fs.promises.readFile(windowsPath, options?.encoding ?? 'utf-8');
}

/**
* Write the given contents to a given file name in the RD WSL distribution.
* @param filePath The destination file path, in the WSL distribution.
Expand Down Expand Up @@ -851,6 +933,13 @@ export default class WSLBackend extends events.EventEmitter implements K8s.Kuber
})();
}

/** Get the IPv4 address of the WSL (VM) host interface, assuming it's already up. */
get hostIPAddress(): string | undefined {
const iface = os.networkInterfaces()['vEthernet (WSL)'];

return (iface ?? []).find(addr => addr.family === 'IPv4')?.address;
}

async getBackendInvalidReason(): Promise<K8s.KubernetesError | null> {
// Check if wsl.exe is available
try {
Expand Down Expand Up @@ -1022,6 +1111,8 @@ export default class WSLBackend extends events.EventEmitter implements K8s.Kuber
await this.upgradeDistroAsNeeded();
await this.ensureDistroRegistered();
await this.initDataDistribution();
await this.writeHostsFile();
await this.writeResolvConf();
})(),
this.progressTracker.action(
'Checking k3s images',
Expand Down

0 comments on commit d34cc89

Please sign in to comment.