From 5b70655b5208d0f490cb8cf7113ceae0f094bc03 Mon Sep 17 00:00:00 2001 From: Jan Dubois Date: Tue, 16 Apr 2024 16:09:35 -0700 Subject: [PATCH 1/2] Add --experimental.kubernetes.options.spinkube setting Signed-off-by: Jan Dubois --- .github/actions/spelling/expect.txt | 2 + e2e/pages/preferences/kubernetes.ts | 4 +- e2e/preferences.e2e.spec.ts | 2 +- pkg/rancher-desktop/assets/dependencies.yaml | 2 + .../assets/scripts/install-k3s | 9 +++ .../scripts/spin-operator.helm-chart.yaml | 9 +++ .../scripts/spin-operator.shim-executor.yaml | 8 +++ .../assets/specs/command-api.yaml | 9 +++ pkg/rancher-desktop/backend/backendHelper.ts | 40 +++++++++---- pkg/rancher-desktop/backend/kube/lima.ts | 14 ++++- pkg/rancher-desktop/backend/kube/wsl.ts | 11 +++- pkg/rancher-desktop/backend/lima.ts | 21 +++++-- .../components/Preferences/BodyKubernetes.vue | 13 ++++- .../components/form/RdCheckbox.vue | 56 ++++++++++++------- pkg/rancher-desktop/config/settings.ts | 4 +- .../__tests__/settingsValidator.spec.ts | 1 + .../main/commandServer/settingsValidator.ts | 13 +++++ scripts/dependencies/tools.ts | 53 ++++++++++++++++++ scripts/lib/build-utils.ts | 5 +- scripts/lib/dependencies.ts | 2 + scripts/postinstall.ts | 2 + scripts/rddepman.ts | 2 + 22 files changed, 236 insertions(+), 46 deletions(-) create mode 100644 pkg/rancher-desktop/assets/scripts/spin-operator.helm-chart.yaml create mode 100644 pkg/rancher-desktop/assets/scripts/spin-operator.shim-executor.yaml diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 6cbf3add312..1b857a0a5a4 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -707,6 +707,8 @@ softmmu someothername somepaththatshouldnevereverexist sourced +spinkube +spinoperator splatform splunk ssd diff --git a/e2e/pages/preferences/kubernetes.ts b/e2e/pages/preferences/kubernetes.ts index 59bc61e6d3b..106cd0e8bb2 100644 --- a/e2e/pages/preferences/kubernetes.ts +++ b/e2e/pages/preferences/kubernetes.ts @@ -6,7 +6,7 @@ export class KubernetesNav { readonly kubernetesToggle: Locator; readonly kubernetesVersion: Locator; readonly kubernetesPort: Locator; - readonly traefikToggle: Locator; + readonly kubernetesOptions: Locator; readonly kubernetesVersionLockedFields: Locator; constructor(page: Page) { @@ -15,7 +15,7 @@ export class KubernetesNav { this.kubernetesToggle = page.locator('[data-test="kubernetesToggle"]'); this.kubernetesVersion = page.locator('[data-test="kubernetesVersion"]'); this.kubernetesPort = page.locator('[data-test="kubernetesPort"]'); - this.traefikToggle = page.locator('[data-test="traefikToggle"]'); + this.kubernetesOptions = page.locator('[data-test="kubernetesOptions"]'); this.kubernetesVersionLockedFields = page.locator('[data-test="kubernetesVersion"] > .select-k8s-version > .icon-lock'); } } diff --git a/e2e/preferences.e2e.spec.ts b/e2e/preferences.e2e.spec.ts index 8213a2c58c8..dbed9d1581f 100644 --- a/e2e/preferences.e2e.spec.ts +++ b/e2e/preferences.e2e.spec.ts @@ -208,7 +208,7 @@ test.describe.serial('Main App Test', () => { await expect(kubernetes.kubernetesToggle).toBeVisible(); await expect(kubernetes.kubernetesVersion).toBeVisible(); await expect(kubernetes.kubernetesPort).toBeVisible(); - await expect(kubernetes.traefikToggle).toBeVisible(); + await expect(kubernetes.kubernetesOptions).toBeVisible(); }); test('should navigate to WSL and render network tab', async() => { diff --git a/pkg/rancher-desktop/assets/dependencies.yaml b/pkg/rancher-desktop/assets/dependencies.yaml index b2d19a48123..d5c99d8f650 100644 --- a/pkg/rancher-desktop/assets/dependencies.yaml +++ b/pkg/rancher-desktop/assets/dependencies.yaml @@ -21,3 +21,5 @@ wix: v3.14.1 hostSwitch: 1.2.2 moproxy: 0.5.0 wasmShims: 0.11.1 +spinOperator: 0.1.0 +certManager: 1.14.4 diff --git a/pkg/rancher-desktop/assets/scripts/install-k3s b/pkg/rancher-desktop/assets/scripts/install-k3s index 39d26240613..63e695c3183 100755 --- a/pkg/rancher-desktop/assets/scripts/install-k3s +++ b/pkg/rancher-desktop/assets/scripts/install-k3s @@ -41,3 +41,12 @@ fi ln -s -f "${K3S_DIR}/${K3S}" /usr/local/bin/k3s # The file system may be readonly (on macOS) chmod a+x "${K3S_DIR}/${K3S}" || true + +# Make sure any old manifests are removed before configuring k3s again +MANIFESTS=/var/lib/rancher/k3s/server/manifests +rm -rf "$MANIFESTS" +mkdir -p "$MANIFESTS" + +STATIC=/var/lib/rancher/k3s/server/static/rancher-desktop +rm -rf "$STATIC" +mkdir -p "$STATIC" diff --git a/pkg/rancher-desktop/assets/scripts/spin-operator.helm-chart.yaml b/pkg/rancher-desktop/assets/scripts/spin-operator.helm-chart.yaml new file mode 100644 index 00000000000..1a10fa5fd7a --- /dev/null +++ b/pkg/rancher-desktop/assets/scripts/spin-operator.helm-chart.yaml @@ -0,0 +1,9 @@ +apiVersion: helm.cattle.io/v1 +kind: HelmChart +metadata: + name: spin-operator + namespace: kube-system +spec: + chart: "https://%{KUBERNETES_API}%/static/rancher-desktop/spin-operator.tgz" + targetNamespace: spin-operator + createNamespace: true diff --git a/pkg/rancher-desktop/assets/scripts/spin-operator.shim-executor.yaml b/pkg/rancher-desktop/assets/scripts/spin-operator.shim-executor.yaml new file mode 100644 index 00000000000..357046c1f55 --- /dev/null +++ b/pkg/rancher-desktop/assets/scripts/spin-operator.shim-executor.yaml @@ -0,0 +1,8 @@ +apiVersion: core.spinoperator.dev/v1alpha1 +kind: SpinAppExecutor +metadata: + name: containerd-shim-spin +spec: + createDeployment: true + deploymentConfig: + runtimeClassName: spin diff --git a/pkg/rancher-desktop/assets/specs/command-api.yaml b/pkg/rancher-desktop/assets/specs/command-api.yaml index e0974d64df1..d2cf5ebef6c 100644 --- a/pkg/rancher-desktop/assets/specs/command-api.yaml +++ b/pkg/rancher-desktop/assets/specs/command-api.yaml @@ -652,6 +652,15 @@ components: enabled: type: boolean x-rd-usage: enable support for containerd-wasm shims + kubernetes: + type: object + properties: + options: + type: object + properties: + spinkube: + type: boolean + x-rd-usage: install spin operator virtualMachine: type: object properties: diff --git a/pkg/rancher-desktop/backend/backendHelper.ts b/pkg/rancher-desktop/backend/backendHelper.ts index fbe42543b1d..faa451b347b 100644 --- a/pkg/rancher-desktop/backend/backendHelper.ts +++ b/pkg/rancher-desktop/backend/backendHelper.ts @@ -7,6 +7,8 @@ import yaml from 'yaml'; import INSTALL_CONTAINERD_SHIMS_SCRIPT from '@pkg/assets/scripts/install-containerd-shims'; import CONTAINERD_CONFIG from '@pkg/assets/scripts/k3s-containerd-config.toml'; +import SPIN_OPERATOR_HELM_CHART from '@pkg/assets/scripts/spin-operator.helm-chart.yaml'; +import SPIN_OPERATOR_SHIM_EXECUTOR from '@pkg/assets/scripts/spin-operator.shim-executor.yaml'; import { BackendSettings, VMExecutor } from '@pkg/backend/backend'; import { LockedFieldError } from '@pkg/config/commandLineOptions'; import { ContainerEngine, Settings } from '@pkg/config/settings'; @@ -19,8 +21,18 @@ import { showMessageBox } from '@pkg/window'; const CONTAINERD_CONFIG_TOML = '/etc/containerd/config.toml'; const DOCKER_DAEMON_JSON = '/etc/docker/daemon.json'; -// Don't use `runtimes.yaml` because k3s may overwrite it. -const MANIFESTS_RUNTIMES_YAML = '/var/lib/rancher/k3s/server/manifests/rd-runtimes.yaml'; + +const MANIFEST_DIR = '/var/lib/rancher/k3s/server/manifests'; +// Manifests are applied in sort order, so use a prefix to load them last, in the required sequence. +// Also: don't use `runtimes.yaml` because k3s may overwrite it. +const MANIFEST_RUNTIMES_YAML = `${ MANIFEST_DIR }/z100-rd-runtimes.yaml`; +const MANIFEST_CERT_MANAGER = `${ MANIFEST_DIR }/z110-cert-manager.yaml`; +const MANIFEST_SPIN_OPERATOR_CRDS = `${ MANIFEST_DIR }/z120-spin-operator.crds.yaml`; +const MANIFEST_SPIN_OPERATOR_SHIM_EXECUTOR = `${ MANIFEST_DIR }/z121-spin-operator.shim-executor.yaml`; +const MANIFEST_SPIN_OPERATOR_CHART = `${ MANIFEST_DIR }/z122-spin-operator.chart.yaml`; + +const STATIC_DIR = '/var/lib/rancher/k3s/server/static/rancher-desktop'; +const STATIC_SPIN_OPERATOR_CHART = `${ STATIC_DIR }/spin-operator.tgz`; const console = Logging.kube; @@ -269,21 +281,29 @@ export default class BackendHelper { }); } - await vmx.execCommand({ root: true }, 'mkdir', '-p', path.dirname(MANIFESTS_RUNTIMES_YAML)); // Don't let k3s define runtime classes, only use the ones defined by Rancher Desktop. - await vmx.execCommand({ root: true }, 'touch', `${ path.dirname(MANIFESTS_RUNTIMES_YAML) }/runtimes.yaml.skip`); + await vmx.execCommand({ root: true }, 'touch', `${ MANIFEST_DIR }/runtimes.yaml.skip`); - if (runtimes.length === 0) { - // We delete the manifest file, but we don't actually delete old runtime classes in k3s that no longer exist. - // They won't work though, as the symlinks in /usr/local/bin have been removed. - await vmx.execCommand({ root: true }, 'rm', '-f', MANIFESTS_RUNTIMES_YAML); - } else { + if (runtimes.length > 0) { const manifest = runtimes.map(r => yaml.stringify(r)).join('---\n'); - await vmx.writeFile(MANIFESTS_RUNTIMES_YAML, manifest, 0o644); + await vmx.writeFile(MANIFEST_RUNTIMES_YAML, manifest, 0o644); } } + /** + * Write k3s manifests to install cert-manager and spinkube operator + */ + static async configureSpinOperator(vmx: VMExecutor) { + await Promise.all([ + vmx.copyFileIn(path.join(paths.resources, 'cert-manager.yaml'), MANIFEST_CERT_MANAGER), + vmx.copyFileIn(path.join(paths.resources, 'spin-operator.crds.yaml'), MANIFEST_SPIN_OPERATOR_CRDS), + vmx.copyFileIn(path.join(paths.resources, 'spin-operator.tgz'), STATIC_SPIN_OPERATOR_CHART), + vmx.writeFile(MANIFEST_SPIN_OPERATOR_SHIM_EXECUTOR, SPIN_OPERATOR_SHIM_EXECUTOR, 0o644), + vmx.writeFile(MANIFEST_SPIN_OPERATOR_CHART, SPIN_OPERATOR_HELM_CHART, 0o644), + ]); + } + /** * Install containerd-wasm shims into /usr/local/containerd-shims (and symlinks into /usr/local/bin). */ diff --git a/pkg/rancher-desktop/backend/kube/lima.ts b/pkg/rancher-desktop/backend/kube/lima.ts index ef955781740..453821398f7 100644 --- a/pkg/rancher-desktop/backend/kube/lima.ts +++ b/pkg/rancher-desktop/backend/kube/lima.ts @@ -126,9 +126,18 @@ export default class LimaKubernetesBackend extends events.EventEmitter implement */ async install(config: BackendSettings, desiredVersion: semver.SemVer, allowSudo: boolean) { await this.progressTracker.action('Installing k3s', 50, async() => { + // installK3s removes old config and makes sure the directories are recreated await this.installK3s(desiredVersion); - await this.writeServiceScript(config, desiredVersion, allowSudo); - await BackendHelper.configureRuntimeClasses(this.vm); + + const promises : Promise[] = [this.writeServiceScript(config, desiredVersion, allowSudo)]; + + if (config.experimental?.containerEngine?.webAssembly?.enabled) { + promises.push(BackendHelper.configureRuntimeClasses(this.vm)); + if (config.experimental?.kubernetes?.options?.spinkube) { + promises.push(BackendHelper.configureSpinOperator(this.vm)); + } + } + await Promise.all(promises); }); this.activeVersion = desiredVersion; @@ -388,6 +397,7 @@ export default class LimaKubernetesBackend extends events.EventEmitter implement 'containerEngine.allowedImages.enabled': undefined, 'containerEngine.name': undefined, 'experimental.containerEngine.webAssembly.enabled': undefined, + 'experimental.kubernetes.options.spinkube': undefined, 'kubernetes.port': undefined, 'kubernetes.enabled': undefined, 'kubernetes.options.traefik': undefined, diff --git a/pkg/rancher-desktop/backend/kube/wsl.ts b/pkg/rancher-desktop/backend/kube/wsl.ts index d12d1d5122e..c9777766561 100644 --- a/pkg/rancher-desktop/backend/kube/wsl.ts +++ b/pkg/rancher-desktop/backend/kube/wsl.ts @@ -170,7 +170,15 @@ export default class WSLKubernetesBackend extends events.EventEmitter implements async install(config: BackendSettings, version: semver.SemVer, allowSudo: boolean) { await this.vm.runInstallScript(INSTALL_K3S_SCRIPT, 'install-k3s', version.raw, await this.vm.wslify(path.join(paths.cache, 'k3s'))); - await BackendHelper.configureRuntimeClasses(this.vm); + + if (config.experimental?.containerEngine?.webAssembly?.enabled) { + const promises : Promise[] = [BackendHelper.configureRuntimeClasses(this.vm)]; + + if (config.experimental?.kubernetes?.options?.spinkube) { + promises.push(BackendHelper.configureSpinOperator(this.vm)); + } + await Promise.all(promises); + } } async start(config: BackendSettings, activeVersion: semver.SemVer, kubeClient?: () => KubeClient): Promise { @@ -295,6 +303,7 @@ export default class WSLKubernetesBackend extends events.EventEmitter implements 'containerEngine.allowedImages.enabled': undefined, 'containerEngine.name': undefined, 'experimental.containerEngine.webAssembly.enabled': undefined, + 'experimental.kubernetes.options.spinkube': undefined, 'kubernetes.enabled': undefined, 'kubernetes.ingress.localhostOnly': undefined, 'kubernetes.options.flannel': undefined, diff --git a/pkg/rancher-desktop/backend/lima.ts b/pkg/rancher-desktop/backend/lima.ts index b20f720be83..3927999cb2f 100644 --- a/pkg/rancher-desktop/backend/lima.ts +++ b/pkg/rancher-desktop/backend/lima.ts @@ -1557,8 +1557,6 @@ export default class LimaBackend extends events.EventEmitter implements VMBacken } protected async configureContainerEngine(): Promise { - const workdir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'rd-containerd-install-')); - try { const configureWASM = !!this.cfg?.experimental?.containerEngine?.webAssembly?.enabled; @@ -1572,8 +1570,6 @@ export default class LimaBackend extends events.EventEmitter implements VMBacken await BackendHelper.configureContainerEngine(this, configureWASM); } catch (err) { console.log(`Error trying to start/update containerd: ${ err }: `, err); - } finally { - await fs.promises.rm(workdir, { recursive: true }); } } @@ -1627,7 +1623,7 @@ export default class LimaBackend extends events.EventEmitter implements VMBacken * @param permissions The file permissions. */ async writeFile(filePath: string, fileContents: string, permissions: fs.Mode = 0o644) { - const workdir = await fs.promises.mkdtemp(path.join(os.tmpdir(), `rd-${ path.basename(filePath) }-`)); + const workdir = await fs.promises.mkdtemp(path.join(os.tmpdir(), `rd-${ path.basename(filePath) }`)); const tempPath = `/tmp/${ path.basename(workdir) }.${ path.basename(filePath) }`; try { @@ -1643,7 +1639,20 @@ export default class LimaBackend extends events.EventEmitter implements VMBacken } } - copyFileIn(hostPath: string, vmPath: string): Promise { + async copyFileIn(hostPath: string, vmPath: string): Promise { + // TODO This logic is copied from writeFile() above and should be simplified. + const workdir = await fs.promises.mkdtemp(path.join(os.tmpdir(), `rd-${ path.basename(hostPath) }`)); + const tempPath = `/tmp/${ path.basename(workdir) }.${ path.basename(hostPath) }`; + + try { + await this.lima('copy', hostPath, `${ MACHINE_NAME }:${ tempPath }`); + await this.execCommand('chmod', '644', tempPath); + await this.execCommand({ root: true }, 'mv', tempPath, vmPath); + } finally { + await fs.promises.rm(workdir, { recursive: true }); + await this.execCommand({ root: true }, 'rm', '-f', tempPath); + } + return this.lima('copy', hostPath, `${ MACHINE_NAME }:${ vmPath }`); } diff --git a/pkg/rancher-desktop/components/Preferences/BodyKubernetes.vue b/pkg/rancher-desktop/components/Preferences/BodyKubernetes.vue index caac352dcd0..9cd7773d11e 100644 --- a/pkg/rancher-desktop/components/Preferences/BodyKubernetes.vue +++ b/pkg/rancher-desktop/components/Preferences/BodyKubernetes.vue @@ -162,8 +162,8 @@ export default Vue.extend({ /> + + diff --git a/pkg/rancher-desktop/components/form/RdCheckbox.vue b/pkg/rancher-desktop/components/form/RdCheckbox.vue index 102494f651d..8c86824315f 100644 --- a/pkg/rancher-desktop/components/form/RdCheckbox.vue +++ b/pkg/rancher-desktop/components/form/RdCheckbox.vue @@ -2,11 +2,17 @@ import { Checkbox } from '@rancher/components'; import Vue from 'vue'; +import TooltipIcon from '@pkg/components/form/TooltipIcon.vue'; + export default Vue.extend({ name: 'rd-checkbox', - components: { Checkbox }, + components: { TooltipIcon, Checkbox }, inheritAttrs: false, props: { + isExperimental: { + type: Boolean, + default: false, + }, isLocked: { type: Boolean, default: false, @@ -39,25 +45,31 @@ export default Vue.extend({ v-on="$listeners" >