From 7be2d84d1496ce0f13210128700951fc9bf441b2 Mon Sep 17 00:00:00 2001 From: Jan Dubois Date: Wed, 8 May 2024 13:01:41 -0700 Subject: [PATCH] Setup spin cli templates and plugins We only install templates and plugins when the there are non installed before. If the user has already configured spin, then we don't want to modify their setup, as we would overwrite them on each app start. Signed-off-by: Jan Dubois --- .gitattributes | 1 + .github/actions/spelling/expect.txt | 1 + .github/actions/spelling/patterns.txt | 2 +- pkg/rancher-desktop/backend/lima.ts | 8 +- pkg/rancher-desktop/backend/wsl.ts | 5 +- .../integrations/windowsIntegrationManager.ts | 21 +-- pkg/rancher-desktop/utils/resources.ts | 1 + resources/setup-spin | 128 ++++++++++++++++++ 8 files changed, 155 insertions(+), 12 deletions(-) create mode 100755 resources/setup-spin diff --git a/.gitattributes b/.gitattributes index deb74d8b075..5fa1b006569 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,5 @@ # All Linux scripts should have LF line endings # But only text files should be changed (not any binaries / images / etc.) resources/linux/** text=auto eol=lf +resources/setup-spin text=auto eol=lf pkg/rancher-desktop/assets/scripts/** text=auto eol=lf diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 88ee1f80081..0ce16050351 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -794,6 +794,7 @@ userpreference UTCTIME vcenter vcpus +vcruntime vcs vde ventura diff --git a/.github/actions/spelling/patterns.txt b/.github/actions/spelling/patterns.txt index 793bab3831e..84c0ad3187f 100644 --- a/.github/actions/spelling/patterns.txt +++ b/.github/actions/spelling/patterns.txt @@ -57,7 +57,7 @@ sha\d+:[0-9]*[a-f]{3,}[0-9a-f]* # hit-count: 6 file-count: 4 # tar arguments -\b(?:\\n|)g?tar(?:\.exe|)(?:(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+ +\b(?:\\n|)g?tar(?:\.exe|)"?(?:(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+ # hit-count: 4 file-count: 3 # base64 encoded content diff --git a/pkg/rancher-desktop/backend/lima.ts b/pkg/rancher-desktop/backend/lima.ts index 3e541f356ef..54c8d813c60 100644 --- a/pkg/rancher-desktop/backend/lima.ts +++ b/pkg/rancher-desktop/backend/lima.ts @@ -1567,7 +1567,13 @@ export default class LimaBackend extends events.EventEmitter implements VMBacken await this.writeFile('/etc/cni/net.d/10-flannel.conflist', FLANNEL_CONFLIST); } - await BackendHelper.configureContainerEngine(this, configureWASM); + const promises: Promise[] = []; + + promises.push(BackendHelper.configureContainerEngine(this, configureWASM)); + if (configureWASM) { + promises.push(this.spawnWithCapture(path.join(paths.resources, 'setup-spin'), {})); + } + await Promise.all(promises); } catch (err) { console.log(`Error trying to start/update containerd: ${ err }: `, err); } diff --git a/pkg/rancher-desktop/backend/wsl.ts b/pkg/rancher-desktop/backend/wsl.ts index 6de5fad1db4..0247f14c419 100644 --- a/pkg/rancher-desktop/backend/wsl.ts +++ b/pkg/rancher-desktop/backend/wsl.ts @@ -1510,8 +1510,11 @@ export default class WSLBackend extends events.EventEmitter implements VMBackend await this.writeFile('/usr/local/bin/wsl-exec', WSL_EXEC, 0o755); await this.runInit(); + if (configureWASM) { + await this.execCommand(await this.wslify(path.join(paths.resources, 'setup-spin'))); + } if (rdNetworking) { - // Do not await on this, as we don't want to wait until the proxy exits. + // Do not await on this, as we don't want to wait until the proxy exits. this.runWslProxy().catch(console.error); } }), diff --git a/pkg/rancher-desktop/integrations/windowsIntegrationManager.ts b/pkg/rancher-desktop/integrations/windowsIntegrationManager.ts index 76e6cbcf2b4..7a1a6ad5266 100644 --- a/pkg/rancher-desktop/integrations/windowsIntegrationManager.ts +++ b/pkg/rancher-desktop/integrations/windowsIntegrationManager.ts @@ -206,6 +206,7 @@ export default class WindowsIntegrationManager implements IntegrationManager { this.syncDistroSocketProxy(distro, state), this.syncDistroDockerPlugins(distro, state), this.syncDistroKubeconfig(distro, kubeconfigPath, state), + this.syncDistroSpinCLI(distro, state), ]); } catch (ex) { console.error(`Failed to sync integration for ${ distro }: ${ ex }`); @@ -307,15 +308,7 @@ export default class WindowsIntegrationManager implements IntegrationManager { protected async getLinuxToolPath(distro: string, tool: string): Promise { // We need to get the Linux path to our helper executable; it is easier to // just get WSL to do the transformation for us. - - const logStream = Logging[`wsl-helper.${ distro }`]; - const { stdout } = await spawnFile( - await this.wslExe, - ['--distribution', distro, '--exec', '/bin/wslpath', '-a', '-u', tool], - { stdio: ['ignore', 'pipe', logStream] }, - ); - - return stdout.trim(); + return (await this.captureCommand( { distro }, '/bin/wslpath', '-a', '-u', tool)).trim(); } protected async syncHostSocketProxy(): Promise { @@ -497,6 +490,16 @@ export default class WindowsIntegrationManager implements IntegrationManager { console.log(`kubeconfig integration for ${ distro } set to ${ state }`); } + protected async syncDistroSpinCLI(distro: string, state: boolean) { + if (state) { + const setupSpin = (await this.captureCommand( + { distro }, '/bin/wslpath', '-a', '-u', path.join(paths.resources, 'setup-spin')) + ).trim(); + + await this.execCommand( { distro }, setupSpin); + } + } + protected get nonBlacklistedDistros(): Promise { return (async() => { let wslOutput: string; diff --git a/pkg/rancher-desktop/utils/resources.ts b/pkg/rancher-desktop/utils/resources.ts index 82fa2d6f3e2..692feba6338 100644 --- a/pkg/rancher-desktop/utils/resources.ts +++ b/pkg/rancher-desktop/utils/resources.ts @@ -15,6 +15,7 @@ const executableMap: Record = { kubectl: undefined, nerdctl: undefined, rdctl: undefined, + spin: undefined, 'wsl-helper': [paths.resources, process.platform, 'internal', platformBinary('wsl-helper')], 'wsl-helper-linux': [paths.resources, 'linux', 'internal', 'wsl-helper'], }; diff --git a/resources/setup-spin b/resources/setup-spin new file mode 100755 index 00000000000..abcb2b0bb3c --- /dev/null +++ b/resources/setup-spin @@ -0,0 +1,128 @@ +#!/bin/sh +# This script uses sh instead of bash to be compatible with as many distros as possible. + +# The script is located in the Rancher Desktop resources/ directory. +resources_dir=$(dirname "$0") + +# We run setup-spin in the rancher-desktop distro to setup spin on the Win32 host. +if [ "$WSL_DISTRO_NAME" = "rancher-desktop" ]; then + app_data_dir=$(/bin/wslpath "$(powershell.exe -Command "Write-Output \${Env:LOCALAPPDATA}")" | tr -d "\r") + system_root=$(/bin/wslpath "$(powershell.exe -Command "Write-Output \${Env:SystemRoot}")" | tr -d "\r") + spin="${resources_dir}/win32/bin/spin.exe" +elif [ "$(uname)" = "Linux" ]; then + app_data_dir="${XDG_DATA_HOME:-$HOME/.local/share}" + spin="${resources_dir}/linux/bin/spin" +else + app_data_dir="${HOME}/Library/Application Support" + spin="${resources_dir}/darwin/bin/spin" +fi + +if [ ! -x "$spin" ]; then + echo "Cannot execute '${spin}' (or does not exist)" + exit 1 +fi + +spin_dir="${app_data_dir}/spin" + +# shellcheck disable=SC2012 # Using `ls` is fine +if [ -d "${spin_dir}/templates" ] && [ "$(ls -1 "${spin_dir}/templates" | wc -l)" -gt 0 ]; then + echo "'${spin_dir}/templates' already exists and is not empty" + exit 0 +fi + +# shellcheck disable=SC2012 # Using `ls` is fine +if [ -d "${spin_dir}/plugins" ] && [ "$(ls -1 "${spin_dir}/plugins" | wc -l)" -gt 0 ]; then + echo "'${spin_dir}/plugins' already exists and is not empty" + exit 0 +fi + +if [ "$WSL_DISTRO_NAME" = "rancher-desktop" ]; then + echo "Waiting for github.com to become resolvable" + for _ in $(seq 30); do + curl --head --silent http://github.com >/dev/null + rc=$?; test $rc -ne 0 && echo "curl exit status is $rc" + if [ $rc -ne 6 ]; then + break + fi + sleep 2 + done +fi + +# The reason for this complexity is to be able to run on systems without git. +# We do need either curl or wget to be on the PATH though. +install_templates() { + repo=$1 + branch=main + tmpdir="${spin_dir}/rancher-desktop.$$" + tarball="${tmpdir}/${repo}.tar.gz" + + url="https://github.com/fermyon/${repo}/archive/refs/heads/${branch}.tar.gz" + + if [ "$WSL_DISTRO_NAME" = "rancher-desktop" ]; then + # Download and extract tarball on Win32 host side to avoid 9p syncing issues + tmpdir=$(/bin/wslpath -w "$tmpdir") + tarball=$(/bin/wslpath -w "$tarball") + + "${system_root}/system32/cmd.exe" /c mkdir "$tmpdir" + + echo "Downloading '${url}' to '${tarball}' with curl.exe" + "${system_root}/system32/curl.exe" --silent --location "$url" --output "$tarball" + rc=$?; test $rc -ne 0 && echo "curl.exe exit status is $rc" + + if [ $rc -eq 0 ]; then + echo "Unpacking '${tarball}'" + "${system_root}/system32/tar.exe" xfz "$tarball" -C "$tmpdir" + rc=$?; test $rc -ne 0 && echo "tar.exe exit status is $rc" + + dir="${tmpdir}\\${repo}-${branch}" + echo "Installing templates from '${dir}'" + "$spin" templates install --update --dir "$dir" + rc=$?; test $rc -ne 0 && echo "Exit status is $rc" + else + echo "Could not download '${url}'" + fi + "${system_root}/system32/cmd.exe" /c rmdir /s /q "$tmpdir" + return + fi + + mkdir -p "$tmpdir" + if command -v curl >/dev/null; then + echo "Downloading '${url}' to '${tarball}' with curl" + curl --silent --location "$url" --output "$tarball" + rc=$?; test $rc -ne 0 && echo "curl exit status is $rc" + elif command -v wget >/dev/null; then + echo "Downloading '${url}' to '${tarball}' with wget" + wget --no-verbose "$url" -O "$tarball" + rc=$?; test $rc -ne 0 && echo "wget exit status is $rc" + fi + if [ -f "$tarball" ]; then + echo "Unpacking '${tarball}'" + tar xfz "$tarball" -C "$tmpdir" + rc=$?; test $rc -ne 0 && echo "tar exit status is $rc" + + dir="${tmpdir}/${repo}-${branch}" + echo "Installing templates from '${dir}'" + "$spin" templates install --update --dir "$dir" + rc=$?; test $rc -ne 0 && echo "Exit status is $rc" + else + echo "Could not download '${url}' (maybe no curl/wget)" + fi + rm -rf "$tmpdir" +} + +install_plugin() { + plugin=$1 + url="https://raw.githubusercontent.com/fermyon/spin-plugins/main/manifests/${plugin}/${plugin}.json" + echo "Installing plugin from '${url}'" + "$spin" plugins install --yes --url "$url" + rc=$?; test $rc -ne 0 && echo "Exit status is $rc" +} + +install_templates spin +install_templates spin-python-sdk +install_templates spin-js-sdk + +install_plugin js2wasm +install_plugin kube + +echo "'${spin}' setup complete"