From 0a04d5661f842f361d2acc97d67d13598a5860d4 Mon Sep 17 00:00:00 2001
From: CrazyMax <1951866+crazy-max@users.noreply.github.com>
Date: Sun, 26 Nov 2023 22:15:16 +0100
Subject: [PATCH] docker(install): switch to lima
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
---
__tests__/docker/install.test.itg.ts | 3 +-
package.json | 1 -
src/docker/assets.ts | 284 +++++++++++----------------
src/docker/install.ts | 88 ++++-----
yarn.lock | 1 -
5 files changed, 158 insertions(+), 219 deletions(-)
diff --git a/__tests__/docker/install.test.itg.ts b/__tests__/docker/install.test.itg.ts
index b4a24263..479e4531 100644
--- a/__tests__/docker/install.test.itg.ts
+++ b/__tests__/docker/install.test.itg.ts
@@ -29,8 +29,7 @@ describe('install', () => {
jest.resetModules();
process.env = {
...originalEnv,
- SIGN_QEMU_BINARY: '1',
- COLIMA_START_ARGS: '--cpu 4 --memory 8 --disk 32'
+ LIMA_START_ARGS: '--cpus 4 --memory 8'
};
});
afterEach(() => {
diff --git a/package.json b/package.json
index 843f84de..8e36a59b 100644
--- a/package.json
+++ b/package.json
@@ -57,7 +57,6 @@
"async-retry": "^1.3.3",
"csv-parse": "^5.5.2",
"handlebars": "^4.7.8",
- "js-yaml": "^4.1.0",
"jwt-decode": "^4.0.0",
"semver": "^7.5.4",
"tmp": "^0.2.1"
diff --git a/src/docker/assets.ts b/src/docker/assets.ts
index 30fe7699..a99f7de7 100644
--- a/src/docker/assets.ts
+++ b/src/docker/assets.ts
@@ -25,8 +25,8 @@ export const dockerServiceLogsPs1 = (): string => {
return get('docker-service-logs.ps1', dockerServiceLogsPs1Data);
};
-export const colimaYaml = (): string => {
- return get('colima.yaml', colimaYamlData);
+export const limaYaml = (): string => {
+ return get('lima.yaml', limaYamlData);
};
const get = (filename: string, data: string, mode?: string): string => {
@@ -128,174 +128,122 @@ Get-WinEvent -ea SilentlyContinue \`
ForEach-Object {"$($_.TimeCreated.ToUniversalTime().ToString("o")) [$($_.LevelDisplayName)] $($_.Message)"}
`;
-export const colimaYamlData = `
-# Number of CPUs to be allocated to the virtual machine.
-# Default: 2
-cpu: 2
-
-# Size of the disk in GiB to be allocated to the virtual machine.
-# NOTE: changing this has no effect after the virtual machine has been created.
-# Default: 60
-disk: 60
-
-# Size of the memory in GiB to be allocated to the virtual machine.
-# Default: 2
-memory: 2
-
-# Architecture of the virtual machine (x86_64, aarch64, host).
-# Default: host
-arch: host
-
-# Container runtime to be used (docker, containerd).
-# Default: docker
-runtime: docker
-
-# Kubernetes configuration for the virtual machine.
-kubernetes:
- enabled: false
-
-# Auto-activate on the Host for client access.
-# Setting to true does the following on startup
-# - sets as active Docker context (for Docker runtime).
-# - sets as active Kubernetes context (if Kubernetes is enabled).
-# Default: true
-autoActivate: false
-
-# Network configurations for the virtual machine.
-network:
- # Assign reachable IP address to the virtual machine.
- # NOTE: this is currently macOS only and ignored on Linux.
- # Default: false
- address: false
-
- # Custom DNS resolvers for the virtual machine.
- #
- # EXAMPLE
- # dns: [8.8.8.8, 1.1.1.1]
- #
- # Default: []
- dns: []
-
- # DNS hostnames to resolve to custom targets using the internal resolver.
- # This setting has no effect if a custom DNS resolver list is supplied above.
- # It does not configure the /etc/hosts files of any machine or container.
- # The value can be an IP address or another host.
- #
- # EXAMPLE
- # dnsHosts:
- # example.com: 1.2.3.4
- dnsHosts:
- host.docker.internal: host.lima.internal
-
-# Forward the host's SSH agent to the virtual machine.
-# Default: false
-forwardAgent: false
-
-# Docker daemon configuration that maps directly to daemon.json.
-# https://docs.docker.com/engine/reference/commandline/dockerd/#daemon-configuration-file.
-# NOTE: some settings may affect Colima's ability to start docker. e.g. \`hosts\`.
-#
-# EXAMPLE - disable buildkit
-# docker:
-# features:
-# buildkit: false
-#
-# EXAMPLE - add insecure registries
-# docker:
-# insecure-registries:
-# - myregistry.com:5000
-# - host.docker.internal:5000
-#
-# Colima default behaviour: buildkit enabled
-# Default: {}
-{{daemonConfig}}
-
-# Virtual Machine type (qemu, vz)
-# NOTE: this is macOS 13 only. For Linux and macOS <13.0, qemu is always used.
-#
-# vz is macOS virtualization framework and requires macOS 13
-#
-# Default: qemu
+export const limaYamlData = `
+# VM type: "qemu" or "vz" (on macOS 13 and later).
+# The vmType can be specified only on creating the instance.
+# The vmType of existing instances cannot be changed.
+# Builtin default: "qemu"
vmType: qemu
-# Volume mount driver for the virtual machine (virtiofs, 9p, sshfs).
-#
-# virtiofs is limited to macOS and vmType \`vz\`. It is the fastest of the options.
-#
-# 9p is the recommended and the most stable option for vmType \`qemu\`.
-#
-# sshfs is faster than 9p but the least reliable of the options (when there are lots
-# of concurrent reads or writes).
-#
-# Default: virtiofs (for vz), sshfs (for qemu)
-mountType: 9p
-
-# The CPU type for the virtual machine (requires vmType \`qemu\`).
-# Options available for host emulation can be checked with: \`qemu-system-$(arch) -cpu help\`.
-# Instructions are also supported by appending to the cpu type e.g. "qemu64,+ssse3".
-# Default: host
-cpuType: host
-
-# Custom provision scripts for the virtual machine.
-# Provisioning scripts are executed on startup and therefore needs to be idempotent.
-#
-# EXAMPLE - script exected as root
-# provision:
-# - mode: system
-# script: apk add htop vim
-#
-# EXAMPLE - script exected as user
-# provision:
-# - mode: user
-# script: |
-# [ -f ~/.provision ] && exit 0;
-# echo provisioning as $USER...
-# touch ~/.provision
-#
-# Default: []
+# OS: "Linux".
+# Builtin default: "Linux"
+os: null
+
+# Arch: "default", "x86_64", "aarch64".
+# Builtin default: "default" (corresponds to the host architecture)
+arch: null
+
+images:
+- location: "https://cloud-images.ubuntu.com/releases/22.04/release-20231026/ubuntu-22.04-server-cloudimg-amd64.img"
+ arch: "x86_64"
+ digest: "sha256:054db2d88c454bb0ad8dfd8883955e3946b57d2b0bf0d023f3ade3c93cdd14e5"
+- location: "https://cloud-images.ubuntu.com/releases/22.04/release-20231026/ubuntu-22.04-server-cloudimg-arm64.img"
+ arch: "aarch64"
+ digest: "sha256:eafa7742ce5ff109222ea313d31ea366d587b4e89b900b11d8285ae775dfe8c3"
+
+# CPUs
+# Builtin default: min(4, host CPU cores)
+cpus: null
+
+# Memory size
+# Builtin default: min("4GiB", half of host memory)
+memory: null
+
+# Disk size
+# Builtin default: "100GiB"
+disk: 60GiB
+
+# Expose host directories to the guest, the mount point might be accessible from all UIDs in the guest
+# Builtin default: null (Mount nothing)
+# This file: Mount the home as read-only, /tmp/lima as writable
+mounts:
+- location: "~"
+- location: "/tmp/lima"
+ writable: true
+
+# Mount type for above mounts, such as "reverse-sshfs" (from sshocker), "9p" (EXPERIMENTAL, from QEMU’s virtio-9p-pci, aka virtfs),
+# or "virtiofs" (EXPERIMENTAL, needs \`vmType: vz\`)
+# Builtin default: "reverse-sshfs" (for QEMU), "virtiofs" (for vz)
+mountType: null
+
+containerd:
+ system: false
+ user: false
+
provision:
- - mode: system
- script: |
- wget -qO- "https://download.docker.com/linux/static/{{dockerBinChannel}}/{{dockerBinArch}}/docker-{{dockerBinVersion}}.tgz" | tar xvz --strip 1 -C /usr/bin/
-
-# Modify ~/.ssh/config automatically to include a SSH config for the virtual machine.
-# SSH config will still be generated in ~/.colima/ssh_config regardless.
-# Default: true
-sshConfig: false
-
-# Configure volume mounts for the virtual machine.
-# Colima mounts user's home directory by default to provide a familiar
-# user experience.
-#
-# EXAMPLE
-# mounts:
-# - location: ~/secrets
-# writable: false
-# - location: ~/projects
-# writable: true
-#
-# Colima default behaviour: $HOME and /tmp/colima are mounted as writable.
-# Default: []
-mounts: []
-
-# Environment variables for the virtual machine.
-#
-# EXAMPLE
-# env:
-# KEY: value
-# ANOTHER_KEY: another value
-#
-# Default: {}
-env: {}
-`;
+- mode: system
+ # This script defines the host.docker.internal hostname when hostResolver is disabled.
+ # It is also needed for lima 0.8.2 and earlier, which does not support hostResolver.hosts.
+ # Names defined in /etc/hosts inside the VM are not resolved inside containers when
+ # using the hostResolver; use hostResolver.hosts instead (requires lima 0.8.3 or later).
+ script: |
+ #!/bin/sh
+ sed -i 's/host.lima.internal.*/host.lima.internal host.docker.internal/' /etc/hosts
+- mode: system
+ script: |
+ #!/bin/sh
+ apt-get install -f -y iptables
+- mode: system
+ script: |
+ #!/bin/bash
+ set -eux -o pipefail
+ command -v docker >/dev/null 2>&1 && exit 0
+ if [ ! -e /etc/systemd/system/docker.socket.d/override.conf ]; then
+ mkdir -p /etc/systemd/system/docker.socket.d
+ # Alternatively we could just add the user to the "docker" group, but that requires restarting the user session
+ cat <<-EOF >/etc/systemd/system/docker.socket.d/override.conf
+ [Socket]
+ SocketUser=\${LIMA_CIDATA_USER}
+ EOF
+ fi
+ if [ ! -e /etc/docker/daemon.json ]; then
+ mkdir -p /etc/docker
+ cat <<-EOF >/etc/docker/daemon.json
+ {{stringify daemonConfig}}
+ EOF
+ fi
+ export DEBIAN_FRONTEND=noninteractive
+ curl -fsSL https://get.docker.com | sh -s -- --channel {{dockerBinChannel}} --version {{dockerBinVersion}}
+
+probes:
+- script: |
+ #!/bin/bash
+ set -eux -o pipefail
+ if ! timeout 30s bash -c "until command -v docker >/dev/null 2>&1; do sleep 3; done"; then
+ echo >&2 "docker is not installed yet"
+ exit 1
+ fi
+ if ! timeout 30s bash -c "until pgrep dockerd; do sleep 3; done"; then
+ echo >&2 "dockerd is not running"
+ exit 1
+ fi
+ hint: See "/var/log/cloud-init-output.log". in the guest
+
+hostResolver:
+ # hostResolver.hosts requires lima 0.8.3 or later. Names defined here will also
+ # resolve inside containers, and not just inside the VM itself.
+ hosts:
+ host.docker.internal: host.lima.internal
-export const qemuEntitlements = `
-
-
-
-
- com.apple.security.hypervisor
-
-
-
+portForwards:
+- guestSocket: "/var/run/docker.sock"
+ hostSocket: "{{dockerSock}}"
+
+audio:
+ # EXPERIMENTAL
+ # QEMU audiodev, e.g., "none", "coreaudio", "pa", "alsa", "oss".
+ # VZ driver, use "vz" as device name
+ # Choosing "none" will mute the audio output, and not play any sound.
+ # Builtin default: ""
+ device: none
`;
diff --git a/src/docker/install.ts b/src/docker/install.ts
index 91e83c4b..bca2c25b 100644
--- a/src/docker/install.ts
+++ b/src/docker/install.ts
@@ -20,7 +20,6 @@ import fsp from 'fs/promises';
import os from 'os';
import path from 'path';
import retry from 'async-retry';
-import yaml from 'js-yaml';
import * as handlebars from 'handlebars';
import * as util from 'util';
import * as core from '@actions/core';
@@ -31,7 +30,7 @@ import * as tc from '@actions/tool-cache';
import {Context} from '../context';
import {Exec} from '../exec';
import {Util} from '../util';
-import {colimaYamlData, dockerServiceLogsPs1, qemuEntitlements, setupDockerWinPs1} from './assets';
+import {limaYamlData, dockerServiceLogsPs1, setupDockerWinPs1} from './assets';
import {GitHubRelease} from '../types/github';
export interface InstallOpts {
@@ -51,6 +50,8 @@ export class Install {
private _version: string | undefined;
private _toolDir: string | undefined;
+ private readonly limaInstanceName = 'docker-actions-toolkit';
+
constructor(opts: InstallOpts) {
this.runDir = opts.runDir;
this.version = opts.version || 'latest';
@@ -131,30 +132,33 @@ export class Install {
}
private async installDarwin(): Promise {
- const colimaDir = path.join(os.homedir(), '.colima', 'default'); // TODO: create a custom colima profile to avoid overlap with other actions
- await io.mkdirP(colimaDir);
- const dockerHost = `unix://${colimaDir}/docker.sock`;
+ const limaDir = path.join(os.homedir(), '.lima', this.limaInstanceName);
+ await io.mkdirP(limaDir);
+ const dockerHost = `unix://${limaDir}/docker.sock`;
- if (!(await Install.colimaInstalled())) {
- await core.group('Installing colima', async () => {
- await Exec.exec('brew', ['install', 'colima']);
+ if (!(await Install.limaInstalled())) {
+ await core.group('Installing lima', async () => {
+ await Exec.exec('brew', ['install', 'lima']);
});
}
- await core.group('Creating colima config', async () => {
- let colimaDaemonConfig = {};
+ await core.group('Creating lima config', async () => {
+ let limaDaemonConfig = {};
if (this.daemonConfig) {
- colimaDaemonConfig = JSON.parse(this.daemonConfig);
+ limaDaemonConfig = JSON.parse(this.daemonConfig);
}
- const colimaCfg = handlebars.compile(colimaYamlData)({
- daemonConfig: yaml.dump(yaml.load(JSON.stringify({docker: colimaDaemonConfig}))),
+ handlebars.registerHelper('stringify', function (obj) {
+ return new handlebars.SafeString(JSON.stringify(obj));
+ });
+ const limaCfg = handlebars.compile(limaYamlData)({
+ daemonConfig: limaDaemonConfig,
+ dockerSock: `${limaDir}/docker.sock`,
dockerBinVersion: this._version,
- dockerBinChannel: this.channel,
- dockerBinArch: Install.platformArch()
+ dockerBinChannel: this.channel
});
- core.info(`Writing colima config to ${path.join(colimaDir, 'colima.yaml')}`);
- fs.writeFileSync(path.join(colimaDir, 'colima.yaml'), colimaCfg);
- core.info(colimaCfg);
+ core.info(`Writing lima config to ${path.join(limaDir, 'lima.yaml')}`);
+ fs.writeFileSync(path.join(limaDir, 'lima.yaml'), limaCfg);
+ core.info(limaCfg);
});
const qemuArch = await Install.qemuArch();
@@ -162,17 +166,7 @@ export class Install {
await Exec.exec(`qemu-system-${qemuArch} --version`);
});
- // https://github.com/abiosoft/colima/issues/786#issuecomment-1693629650
- if (process.env.SIGN_QEMU_BINARY === '1') {
- await core.group('Signing QEMU binary with entitlements', async () => {
- const qemuEntitlementsFile = path.join(Context.tmpDir(), 'qemu-entitlements.xml');
- core.info(`Writing entitlements to ${qemuEntitlementsFile}`);
- fs.writeFileSync(qemuEntitlementsFile, qemuEntitlements);
- await Exec.exec(`codesign --sign - --entitlements ${qemuEntitlementsFile} --force /usr/local/bin/qemu-system-${qemuArch}`);
- });
- }
-
- // colima is already started on the runner so env var added in download
+ // lima might already be started on the runner so env var added in download
// method is not expanded to the running process.
const envs = Object.assign({}, process.env, {
PATH: `${this.toolDir}:${process.env.PATH}`
@@ -180,22 +174,21 @@ export class Install {
[key: string]: string;
};
- await core.group('Starting colima', async () => {
- const colimaStartArgs = ['start', '--very-verbose'];
- if (process.env.COLIMA_START_ARGS) {
- colimaStartArgs.push(process.env.COLIMA_START_ARGS);
+ await core.group('Starting lima instance', async () => {
+ const limaStartArgs = ['start', `--name=${this.limaInstanceName}`, '--tty=false'];
+ if (process.env.LIMA_START_ARGS) {
+ limaStartArgs.push(process.env.LIMA_START_ARGS);
}
try {
- await Exec.exec(`colima ${colimaStartArgs.join(' ')}`, [], {env: envs});
+ await Exec.exec(`limactl ${limaStartArgs.join(' ')}`, [], {env: envs});
} catch (e) {
- const limaColimaDir = path.join(os.homedir(), '.lima', 'colima');
fsp
- .readdir(limaColimaDir)
+ .readdir(limaDir)
.then(files => {
files
.filter(f => path.extname(f) === '.log')
.forEach(f => {
- const logfile = path.join(limaColimaDir, f);
+ const logfile = path.join(limaDir, f);
const logcontent = fs.readFileSync(logfile, {encoding: 'utf8'}).trim();
if (logcontent.length > 0) {
core.info(`### ${logfile}:\n${logcontent}`);
@@ -362,14 +355,15 @@ EOF`,
private async tearDownDarwin(): Promise {
await core.group('Docker daemon logs', async () => {
- await Exec.exec('colima', ['exec', '--', 'cat', '/var/log/docker.log']).catch(async () => {
- await Exec.exec('colima', ['exec', '--', 'sudo', 'journalctl', '-u', 'docker.service', '-l', '--no-pager']).catch(() => {
- core.warning(`Failed to get Docker daemon logs`);
- });
+ await Exec.exec('limactl', ['shell', '--tty=false', this.limaInstanceName, 'sudo', 'journalctl', '-u', 'docker.service', '-l', '--no-pager']).catch(() => {
+ core.warning(`Failed to get Docker daemon logs`);
});
});
- await core.group('Stopping colima', async () => {
- await Exec.exec('colima', ['stop', '--very-verbose']);
+ await core.group('Stopping lima instance', async () => {
+ await Exec.exec('limactl', ['stop', '--tty=false', this.limaInstanceName, '--force']);
+ });
+ await core.group('Removing lima instance', async () => {
+ await Exec.exec('limactl', ['delete', '--tty=false', this.limaInstanceName, '--force']);
});
await core.group('Removing Docker context', async () => {
await Exec.exec('docker', ['context', 'rm', '-f', this.contextName]);
@@ -464,15 +458,15 @@ EOF`,
}
}
- private static async colimaInstalled(): Promise {
+ private static async limaInstalled(): Promise {
return await io
- .which('colima', true)
+ .which('lima', true)
.then(res => {
- core.debug(`docker.Install.colimaAvailable ok: ${res}`);
+ core.debug(`docker.Install.limaAvailable ok: ${res}`);
return true;
})
.catch(error => {
- core.debug(`docker.Install.colimaAvailable error: ${error}`);
+ core.debug(`docker.Install.limaAvailable error: ${error}`);
return false;
});
}
diff --git a/yarn.lock b/yarn.lock
index e917206b..061d3736 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1065,7 +1065,6 @@ __metadata:
eslint-plugin-prettier: ^5.0.0
handlebars: ^4.7.8
jest: ^29.6.4
- js-yaml: ^4.1.0
jwt-decode: ^4.0.0
prettier: ^3.0.3
rimraf: ^5.0.1