From 7c5de6ecc65b3ee246dea03ea6f12de3bf48fb15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Fri, 8 Aug 2025 10:26:53 +0200 Subject: [PATCH] Apple Container external driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A new macOS driver similar to the WSL2 driver for Windows, creating virtual machines from container images (as rootfs). Signed-off-by: Anders F Björklund --- cmd/lima-driver-ac/main_darwin.go | 16 + pkg/driver/ac/ac_driver_darwin.go | 330 +++++++++++++++++++ pkg/driver/ac/boot/02-no-cloud-init-setup.sh | 25 ++ pkg/driver/ac/errors_darwin.go | 8 + pkg/driver/ac/fs.go | 40 +++ pkg/driver/ac/lima-init.TEMPLATE | 8 + pkg/driver/ac/vm_darwin.go | 305 +++++++++++++++++ pkg/hostagent/hostagent.go | 2 +- pkg/limatype/lima_yaml.go | 3 +- templates/experimental/ac.yaml | 23 ++ website/content/en/docs/config/vmtype/ac.md | 9 + 11 files changed, 767 insertions(+), 2 deletions(-) create mode 100644 cmd/lima-driver-ac/main_darwin.go create mode 100644 pkg/driver/ac/ac_driver_darwin.go create mode 100755 pkg/driver/ac/boot/02-no-cloud-init-setup.sh create mode 100644 pkg/driver/ac/errors_darwin.go create mode 100644 pkg/driver/ac/fs.go create mode 100644 pkg/driver/ac/lima-init.TEMPLATE create mode 100644 pkg/driver/ac/vm_darwin.go create mode 100644 templates/experimental/ac.yaml create mode 100644 website/content/en/docs/config/vmtype/ac.md diff --git a/cmd/lima-driver-ac/main_darwin.go b/cmd/lima-driver-ac/main_darwin.go new file mode 100644 index 00000000000..88b391ab27b --- /dev/null +++ b/cmd/lima-driver-ac/main_darwin.go @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "context" + + "github.com/lima-vm/lima/v2/pkg/driver/ac" + "github.com/lima-vm/lima/v2/pkg/driver/external/server" +) + +// To be used as an external driver for Lima. +func main() { + server.Serve(context.Background(), ac.New()) +} diff --git a/pkg/driver/ac/ac_driver_darwin.go b/pkg/driver/ac/ac_driver_darwin.go new file mode 100644 index 00000000000..c62f9bdfe40 --- /dev/null +++ b/pkg/driver/ac/ac_driver_darwin.go @@ -0,0 +1,330 @@ +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package ac + +import ( + "context" + "embed" + "errors" + "fmt" + "net" + "regexp" + + "github.com/sirupsen/logrus" + + "github.com/lima-vm/lima/v2/pkg/driver" + "github.com/lima-vm/lima/v2/pkg/limatype" + "github.com/lima-vm/lima/v2/pkg/limayaml" + "github.com/lima-vm/lima/v2/pkg/ptr" + "github.com/lima-vm/lima/v2/pkg/reflectutil" +) + +var knownYamlProperties = []string{ + "Arch", + "Containerd", + "CopyToHost", + "CPUType", + "Disk", + "DNS", + "Env", + "HostResolver", + "Images", + "Message", + "Mounts", + "MountType", + "Param", + "Plain", + "PortForwards", + "Probes", + "PropagateProxyEnv", + "Provision", + "SSH", + "VMType", +} + +const Enabled = true + +type LimaAcDriver struct { + Instance *limatype.Instance + + SSHLocalPort int + vSockPort int + virtioPort string +} + +var _ driver.Driver = (*LimaAcDriver)(nil) + +func New() *LimaAcDriver { + return &LimaAcDriver{ + vSockPort: 0, + virtioPort: "", + } +} + +func (l *LimaAcDriver) Configure(inst *limatype.Instance) *driver.ConfiguredDriver { + l.Instance = inst + l.SSHLocalPort = inst.SSHLocalPort + + return &driver.ConfiguredDriver{ + Driver: l, + } +} + +func (l *LimaAcDriver) FillConfig(ctx context.Context, cfg *limatype.LimaYAML, _ string) error { + if cfg.VMType == nil { + cfg.VMType = ptr.Of(limatype.AC) + } + if cfg.MountType == nil { + cfg.MountType = ptr.Of(limatype.WSLMount) + } + return validateConfig(ctx, cfg) +} + +func (l *LimaAcDriver) Validate(ctx context.Context) error { + return validateConfig(ctx, l.Instance.Config) +} + +func validateConfig(_ context.Context, cfg *limatype.LimaYAML) error { + if cfg == nil { + return errors.New("configuration is nil") + } + if *cfg.MountType != limatype.VIRTIOFS && *cfg.MountType != limatype.REVSSHFS { + return fmt.Errorf("field `mountType` must be %q or %q for AC driver, got %q", limatype.VIRTIOFS, limatype.REVSSHFS, *cfg.MountType) + } + // TODO: revise this list for AC + if cfg.VMType != nil { + if unknown := reflectutil.UnknownNonEmptyFields(cfg, knownYamlProperties...); len(unknown) > 0 { + logrus.Warnf("Ignoring: vmType %s: %+v", *cfg.VMType, unknown) + } + } + + if !limayaml.IsNativeArch(*cfg.Arch) { + return fmt.Errorf("unsupported arch: %q", *cfg.Arch) + } + + if cfg.VMType != nil { + if cfg.Images != nil && cfg.Arch != nil { + // TODO: real filetype checks + tarFileRegex := regexp.MustCompile(`.*tar\.*`) + for i, image := range cfg.Images { + if unknown := reflectutil.UnknownNonEmptyFields(image, "File"); len(unknown) > 0 { + logrus.Warnf("Ignoring: vmType %s: images[%d]: %+v", *cfg.VMType, i, unknown) + } + match := tarFileRegex.MatchString(image.Location) + if image.Arch == *cfg.Arch && !match { + return fmt.Errorf("unsupported image type for vmType: %s, tarball root file system required: %q", *cfg.VMType, image.Location) + } + } + } + + if cfg.Mounts != nil { + for i, mount := range cfg.Mounts { + if unknown := reflectutil.UnknownNonEmptyFields(mount); len(unknown) > 0 { + logrus.Warnf("Ignoring: vmType %s: mounts[%d]: %+v", *cfg.VMType, i, unknown) + } + } + } + + if cfg.Networks != nil { + for i, network := range cfg.Networks { + if unknown := reflectutil.UnknownNonEmptyFields(network); len(unknown) > 0 { + logrus.Warnf("Ignoring: vmType %s: networks[%d]: %+v", *cfg.VMType, i, unknown) + } + } + } + + if cfg.Audio.Device != nil { + audioDevice := *cfg.Audio.Device + if audioDevice != "" { + logrus.Warnf("Ignoring: vmType %s: `audio.device`: %+v", *cfg.VMType, audioDevice) + } + } + } + + return nil +} + +//go:embed boot/*.sh +var bootFS embed.FS + +func (l *LimaAcDriver) BootScripts() (map[string][]byte, error) { + scripts := make(map[string][]byte) + + entries, err := bootFS.ReadDir("boot") + if err != nil { + return scripts, err + } + + for _, entry := range entries { + if entry.IsDir() { + continue + } + + content, err := bootFS.ReadFile("boot/" + entry.Name()) + if err != nil { + return nil, err + } + + scripts[entry.Name()] = content + } + + return scripts, nil +} + +func (l *LimaAcDriver) InspectStatus(ctx context.Context, inst *limatype.Instance) string { + status, err := getAcStatus(ctx, inst.Name) + if err != nil { + inst.Status = limatype.StatusBroken + inst.Errors = append(inst.Errors, err) + } else { + inst.Status = status + } + + inst.SSHLocalPort = 22 + + if inst.Status == limatype.StatusRunning { + sshAddr, err := getSSHAddress(ctx, inst.Name) + if err == nil { + inst.SSHAddress = sshAddr + } else { + inst.Errors = append(inst.Errors, err) + } + } + + return inst.Status +} + +func (l *LimaAcDriver) Delete(ctx context.Context) error { + distroName := "lima-" + l.Instance.Name + status, err := getAcStatus(ctx, l.Instance.Name) + if err != nil { + return err + } + switch status { + case limatype.StatusRunning, limatype.StatusStopped, limatype.StatusBroken, limatype.StatusInstalling: + return deleteVM(ctx, distroName) + } + + logrus.Info("AC VM is not running or does not exist, skipping deletion") + return nil +} + +func (l *LimaAcDriver) Start(ctx context.Context) (chan error, error) { + logrus.Infof("Starting AC VM") + status, err := getAcStatus(ctx, l.Instance.Name) + if err != nil { + return nil, err + } + + distroName := "lima-" + l.Instance.Name + + if status == limatype.StatusUninitialized { + if err := EnsureFs(ctx, l.Instance); err != nil { + return nil, err + } + if err := initVM(ctx, l.Instance.Dir, distroName); err != nil { + return nil, err + } + cpus := l.Instance.CPUs + memory := int(l.Instance.Memory >> 20) // MiB + if err := createVM(ctx, distroName, cpus, memory); err != nil { + return nil, err + } + } + + errCh := make(chan error) + + if err := startVM(ctx, distroName); err != nil { + return nil, err + } + + if err := provisionVM( + ctx, + l.Instance.Dir, + l.Instance.Name, + distroName, + errCh, + ); err != nil { + return nil, err + } + + return errCh, err +} + +func (l *LimaAcDriver) canRunGUI() bool { + return false +} + +func (l *LimaAcDriver) RunGUI() error { + return fmt.Errorf("RunGUI is not supported for the given driver '%s' and display '%s'", "ac", *l.Instance.Config.Video.Display) +} + +func (l *LimaAcDriver) Stop(ctx context.Context) error { + logrus.Info("Shutting down AC VM") + distroName := "lima-" + l.Instance.Name + return stopVM(ctx, distroName) +} + +// GuestAgentConn returns the guest agent connection, or nil (if forwarded by ssh). +func (l *LimaAcDriver) GuestAgentConn(_ context.Context) (net.Conn, string, error) { + return nil, "", nil +} + +func (l *LimaAcDriver) Info() driver.Info { + var info driver.Info + info.Name = "ac" + if l.Instance != nil { + info.InstanceDir = l.Instance.Dir + } + info.VirtioPort = l.virtioPort + info.VsockPort = l.vSockPort + + info.Features = driver.DriverFeatures{ + DynamicSSHAddress: true, + SkipSocketForwarding: true, + CanRunGUI: l.canRunGUI(), + } + return info +} + +func (l *LimaAcDriver) SSHAddress(_ context.Context) (string, error) { + return "127.0.0.1", nil +} + +func (l *LimaAcDriver) Create(_ context.Context) error { + return nil +} + +func (l *LimaAcDriver) CreateDisk(_ context.Context) error { + return nil +} + +func (l *LimaAcDriver) ChangeDisplayPassword(_ context.Context, _ string) error { + return nil +} + +func (l *LimaAcDriver) DisplayConnection(_ context.Context) (string, error) { + return "", nil +} + +func (l *LimaAcDriver) CreateSnapshot(_ context.Context, _ string) error { + return errUnimplemented +} + +func (l *LimaAcDriver) ApplySnapshot(_ context.Context, _ string) error { + return errUnimplemented +} + +func (l *LimaAcDriver) DeleteSnapshot(_ context.Context, _ string) error { + return errUnimplemented +} + +func (l *LimaAcDriver) ListSnapshots(_ context.Context) (string, error) { + return "", errUnimplemented +} + +func (l *LimaAcDriver) ForwardGuestAgent() bool { + // If driver is not providing, use host agent + return l.vSockPort == 0 && l.virtioPort == "" +} diff --git a/pkg/driver/ac/boot/02-no-cloud-init-setup.sh b/pkg/driver/ac/boot/02-no-cloud-init-setup.sh new file mode 100755 index 00000000000..ebfe351cd3e --- /dev/null +++ b/pkg/driver/ac/boot/02-no-cloud-init-setup.sh @@ -0,0 +1,25 @@ +#!/bin/sh + +# SPDX-FileCopyrightText: Copyright The Lima Authors +# SPDX-License-Identifier: Apache-2.0 + +# This script replaces the cloud-init functionality of creating a user and setting its SSH keys +# when cloud-init is not available +[ "$LIMA_CIDATA_NO_CLOUD_INIT" = "1" ] || exit 0 + +# create user +# shellcheck disable=SC2153 +useradd -u "${LIMA_CIDATA_UID}" "${LIMA_CIDATA_USER}" -c "${LIMA_CIDATA_COMMENT}" -d "${LIMA_CIDATA_HOME}" -m -s "${LIMA_CIDATA_SHELL}" +LIMA_CIDATA_GID=$(id -g "${LIMA_CIDATA_USER}") +mkdir "${LIMA_CIDATA_HOME}"/.ssh/ +chown "${LIMA_CIDATA_UID}:${LIMA_CIDATA_GID}" "${LIMA_CIDATA_HOME}"/.ssh/ +chmod 700 "${LIMA_CIDATA_HOME}"/.ssh/ +cp "${LIMA_CIDATA_MNT}"/ssh_authorized_keys "${LIMA_CIDATA_HOME}"/.ssh/authorized_keys +chown "${LIMA_CIDATA_UID}:${LIMA_CIDATA_GID}" "${LIMA_CIDATA_HOME}"/.ssh/authorized_keys +chmod 600 "${LIMA_CIDATA_HOME}"/.ssh/authorized_keys + +# add $LIMA_CIDATA_USER to sudoers +echo "${LIMA_CIDATA_USER} ALL=(ALL) NOPASSWD:ALL" | tee -a /etc/sudoers.d/99_lima_sudoers + +# symlink CIDATA to the hardcoded path for requirement checks (TODO: make this not hardcoded) +[ "$LIMA_CIDATA_MNT" = "/mnt/lima-cidata" ] || ln -sfFn "${LIMA_CIDATA_MNT}" /mnt/lima-cidata diff --git a/pkg/driver/ac/errors_darwin.go b/pkg/driver/ac/errors_darwin.go new file mode 100644 index 00000000000..47454705c81 --- /dev/null +++ b/pkg/driver/ac/errors_darwin.go @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package ac + +import "errors" + +var errUnimplemented = errors.New("unimplemented") diff --git a/pkg/driver/ac/fs.go b/pkg/driver/ac/fs.go new file mode 100644 index 00000000000..5a4645248ed --- /dev/null +++ b/pkg/driver/ac/fs.go @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package ac + +import ( + "context" + "errors" + "os" + "path/filepath" + + "github.com/sirupsen/logrus" + + "github.com/lima-vm/lima/v2/pkg/fileutils" + "github.com/lima-vm/lima/v2/pkg/limatype" + "github.com/lima-vm/lima/v2/pkg/limatype/filenames" +) + +// EnsureFs downloads the root fs. +func EnsureFs(ctx context.Context, inst *limatype.Instance) error { + baseDisk := filepath.Join(inst.Dir, filenames.BaseDisk) + if _, err := os.Stat(baseDisk); errors.Is(err, os.ErrNotExist) { + var ensuredBaseDisk bool + errs := make([]error, len(inst.Config.Images)) + for i, f := range inst.Config.Images { + if _, err := fileutils.DownloadFile(ctx, baseDisk, f.File, true, "the image", *inst.Config.Arch); err != nil { + errs[i] = err + continue + } + ensuredBaseDisk = true + break + } + if !ensuredBaseDisk { + return fileutils.Errors(errs) + } + } + logrus.Info("Download succeeded") + + return nil +} diff --git a/pkg/driver/ac/lima-init.TEMPLATE b/pkg/driver/ac/lima-init.TEMPLATE new file mode 100644 index 00000000000..b30c8825344 --- /dev/null +++ b/pkg/driver/ac/lima-init.TEMPLATE @@ -0,0 +1,8 @@ +#!/bin/bash +set -eu +export LOG_FILE=/var/log/lima-init.log +exec > >(tee $LOG_FILE) 2>&1 +ln -sf rtc0 /dev/rtc +chmod 666 /dev/fuse +export LIMA_CIDATA_MNT="{{.CIDataPath}}" +exec "$LIMA_CIDATA_MNT/boot.sh" diff --git a/pkg/driver/ac/vm_darwin.go b/pkg/driver/ac/vm_darwin.go new file mode 100644 index 00000000000..2e43ece4525 --- /dev/null +++ b/pkg/driver/ac/vm_darwin.go @@ -0,0 +1,305 @@ +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package ac + +import ( + "bytes" + "context" + _ "embed" + "encoding/json" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + + "github.com/sirupsen/logrus" + + "github.com/lima-vm/lima/v2/pkg/limatype" + "github.com/lima-vm/lima/v2/pkg/limatype/filenames" + "github.com/lima-vm/lima/v2/pkg/textutil" +) + +// init system (pid 1) in VM. +const initSystem = "openrc" + +// createVM calls AC to create a VM. +func createVM(ctx context.Context, distroName string, cpus, memory int) error { + imageName := distroName + entrypoint := "/sbin/init" + // /sbin/init is normally just a symlink to systemd + // eventually we might want to look inside the image + switch initSystem { + case "systemd": + entrypoint = "/lib/systemd/systemd" + case "openrc": + entrypoint = "/sbin/openrc-init" + default: + logrus.Infof("unknown init system, running only vminitd") + } + out, err := exec.CommandContext(ctx, + "container", + "create", + "--name", + distroName, + "--cpus", + fmt.Sprintf("%d", cpus), + "--memory", + fmt.Sprintf("%dM", memory), + "--entrypoint", + entrypoint, + imageName).CombinedOutput() + if err != nil { + return fmt.Errorf("failed to run `container create %s`: %w (out=%q)", + distroName, err, out) + } + return nil +} + +// startVM calls AC to start a VM. +func startVM(ctx context.Context, distroName string) error { + out, err := exec.CommandContext(ctx, + "container", + "start", + distroName).CombinedOutput() + if err != nil { + return fmt.Errorf("failed to run `container start %s`: %w (out=%q)", + distroName, err, out) + } + return nil +} + +// initVM calls AC to import a new VM specifically for Lima. +func initVM(ctx context.Context, instanceDir, distroName string) error { + imageName := distroName + dockerFile := filepath.Join(instanceDir, "Dockerfile") + fileContents := fmt.Sprintf("FROM scratch\nADD %s /\n", filenames.BaseDisk) + err := os.WriteFile(dockerFile, []byte(fileContents), 0o644) + if err != nil { + return err + } + baseDisk := filepath.Join(instanceDir, filenames.BaseDisk) + logrus.Infof("Importing distro from %q to %q", baseDisk, instanceDir) + wd, err := os.Getwd() + if err != nil { + return err + } + err = os.Chdir(instanceDir) + if err != nil { + return err + } + defer func() { _ = os.Chdir(wd) }() + out, err := exec.CommandContext(ctx, + "container", + "build", + "-t", + imageName, + instanceDir).CombinedOutput() + if err != nil { + return fmt.Errorf("failed to run `container build -t %s %s`: %w (out=%q)", + imageName, instanceDir, err, out) + } + return nil +} + +// stopVM calls AC to stop a running VM. +func stopVM(ctx context.Context, distroName string) error { + out, err := exec.CommandContext(ctx, + "container", + "stop", + distroName).CombinedOutput() + if err != nil { + return fmt.Errorf("failed to run `container stop %s`: %w (out=%q)", + distroName, err, out) + } + return nil +} + +//go:embed lima-init.TEMPLATE +var limaBoot string + +// copyDir copies a directory. +func copyDir(ctx context.Context, distroName, src, dst string) error { + cmd := exec.CommandContext(ctx, "container", "exec", distroName, "mkdir", "-p", dst) + if err := cmd.Run(); err != nil { + if exiterr, ok := err.(*exec.ExitError); ok { + logrus.Debugf("run stderr: %s", exiterr.Stderr) + } + return fmt.Errorf("failed to run %v: %w", cmd.Args, err) + } + logrus.Infof("Copying directory from %q to \"%s:%s\"", src, distroName, dst) + tar1 := exec.CommandContext(ctx, "tar", "Cc", src, ".") + tar2 := exec.CommandContext(ctx, "container", "exec", "-i", distroName, "tar", "Cx", dst) + + p, err := tar1.StdoutPipe() + if err != nil { + return err + } + tar2.Stdin = p + if err := tar2.Start(); err != nil { + return err + } + if err := tar1.Run(); err != nil { + return err + } + if err := tar2.Wait(); err != nil { + return err + } + return nil +} + +// provisionVM starts Lima's boot process inside an already imported VM. +func provisionVM(ctx context.Context, instanceDir, instanceName, distroName string, errCh chan<- error) error { + ciDataPath := filepath.Join(instanceDir, filenames.CIDataISODir) + // can't mount the cidata, due to problems with virtiofs mounts + if err := copyDir(ctx, distroName, ciDataPath, "/mnt/lima-cidata"); err != nil { + return fmt.Errorf("failed to copy cidata directory: %w", err) + } + m := map[string]string{ + "CIDataPath": "/mnt/lima-cidata", + } + limaBootB, err := textutil.ExecuteTemplate(limaBoot, m) + if err != nil { + return fmt.Errorf("failed to construct ac boot.sh script: %w", err) + } + go func() { + cmd := exec.CommandContext( + ctx, + "container", + "exec", + "-i", + distroName, + "/bin/bash", + ) + cmd.Stdin = bytes.NewReader(limaBootB) + out, err := cmd.CombinedOutput() + logrus.Debugf("%v: %q", cmd.Args, string(out)) + if err != nil { + errCh <- fmt.Errorf( + "error running command that executes boot.sh (%v): %w, "+ + "check /var/log/lima-init.log for more details (out=%q)", cmd.Args, err, string(out)) + } + + for { + <-ctx.Done() + logrus.Info("Context closed, stopping vm") + if status, err := getAcStatus(ctx, instanceName); err == nil && + status == limatype.StatusRunning { + _ = stopVM(ctx, distroName) + } + } + }() + + return err +} + +// deleteVM calls AC to delete a VM. +func deleteVM(ctx context.Context, distroName string) error { + imageName := distroName + logrus.Info("Deleting AC VM") + out, err := exec.CommandContext(ctx, + "container", + "rm", + "-f", + distroName).CombinedOutput() + if err != nil { + return fmt.Errorf("failed to run `container rm -f %s`: %w (out=%q)", + distroName, err, out) + } + out, err = exec.CommandContext(ctx, + "container", + "image", + "rm", + imageName).CombinedOutput() + if err != nil { + return fmt.Errorf("failed to run `container image rm %s`: %w (out=%q)", + distroName, err, out) + } + return nil +} + +type Network struct { + Address string `json:"address"` + Gateway string `json:"gateway"` + Hostname string `json:"hostname"` + Network string `json:"network"` +} + +type Container struct { + Status string `json:"status"` + Config map[string]any `json:"configuration"` + Networks []Network `json:"networks,omitempty"` +} + +// listContainers returns all containers in the system. +// +// but currently there is _no_ way to filter in the list. +// so we need to loop through all of them in the client. +func listContainers(ctx context.Context) ([]Container, error) { + out, err := exec.CommandContext( + ctx, + "container", + "list", + "--format=json", + ).CombinedOutput() + if err != nil { + return nil, fmt.Errorf("failed to run `container list --format=json`, err: %w (out=%q)", err, out) + } + + var list []Container + err = json.Unmarshal(out, &list) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal json output, err: %w (out=%q)", err, out) + } + return list, nil +} + +func getAcStatus(ctx context.Context, instName string) (string, error) { + distroName := "lima-" + instName + list, err := listContainers(ctx) + if err != nil { + return "", err + } + + instState := limatype.StatusUninitialized + for _, c := range list { + // container don't have real ID + // (any --name replaces the UUID) + if c.Config["id"] == distroName { + switch c.Status { + case "stopped": + instState = limatype.StatusStopped + case "running": + instState = limatype.StatusRunning + default: + instState = limatype.StatusUnknown + } + break + } + } + + return instState, nil +} + +func getSSHAddress(ctx context.Context, instName string) (string, error) { + distroName := "lima-" + instName + list, err := listContainers(ctx) + if err != nil { + return "", err + } + + instAddress := "127.0.0.1" + for _, c := range list { + // container don't have real ID + // (any --name replaces the UUID) + if c.Config["id"] == distroName { + if len(c.Networks) > 0 { + instAddress = c.Networks[0].Address + } + break + } + } + + return strings.Replace(instAddress, "/24", "", 1), nil +} diff --git a/pkg/hostagent/hostagent.go b/pkg/hostagent/hostagent.go index b53848db1ea..641501a416f 100644 --- a/pkg/hostagent/hostagent.go +++ b/pkg/hostagent/hostagent.go @@ -139,7 +139,7 @@ func New(ctx context.Context, instName string, stdout io.Writer, signalCh chan o if err != nil { return nil, err } - if *inst.Config.VMType == limatype.WSL2 { + if *inst.Config.VMType == limatype.WSL2 || *inst.Config.VMType == limatype.AC { sshLocalPort = inst.SSHLocalPort } diff --git a/pkg/limatype/lima_yaml.go b/pkg/limatype/lima_yaml.go index 367f5bc9887..f3a9543ccdb 100644 --- a/pkg/limatype/lima_yaml.go +++ b/pkg/limatype/lima_yaml.go @@ -93,13 +93,14 @@ const ( QEMU VMType = "qemu" VZ VMType = "vz" WSL2 VMType = "wsl2" + AC VMType = "ac" ) var ( OSTypes = []OS{LINUX} ArchTypes = []Arch{X8664, AARCH64, ARMV7L, PPC64LE, RISCV64, S390X} MountTypes = []MountType{REVSSHFS, NINEP, VIRTIOFS, WSLMount} - VMTypes = []VMType{QEMU, VZ, WSL2} + VMTypes = []VMType{QEMU, VZ, WSL2, AC} ) type User struct { diff --git a/templates/experimental/ac.yaml b/templates/experimental/ac.yaml new file mode 100644 index 00000000000..eb6063dda25 --- /dev/null +++ b/templates/experimental/ac.yaml @@ -0,0 +1,23 @@ +vmType: ac + +arch: aarch64 +cpus: 4 +memory: 1GiB +disk: 512GiB + +images: +- location: "debian-rootfs-arm64.tar.gz" + arch: "aarch64" + +# virtiofs is broken +mountType: reverse-sshfs + +mounts: +- location: "~" +- location: "/tmp/lima" + writable: true + +# The built-in containerd installer does not support OpenRC currently. +containerd: + system: false + user: false diff --git a/website/content/en/docs/config/vmtype/ac.md b/website/content/en/docs/config/vmtype/ac.md new file mode 100644 index 00000000000..95d905b48c0 --- /dev/null +++ b/website/content/en/docs/config/vmtype/ac.md @@ -0,0 +1,9 @@ +--- +title: AC +weight: 3 +--- + +> **Warning** +> "ac" option is experimental + +"ac" option makes use of Apple Container to run the guest OS.