-
Notifications
You must be signed in to change notification settings - Fork 696
Apple Container external driver for macOS #3839
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// SPDX-FileCopyrightText: Copyright The Lima Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package main | ||
|
||
import ( | ||
"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(ac.New()) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,243 @@ | ||
// SPDX-FileCopyrightText: Copyright The Lima Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package ac | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"net" | ||
"regexp" | ||
|
||
"github.com/sirupsen/logrus" | ||
|
||
"github.com/lima-vm/lima/v2/pkg/driver" | ||
"github.com/lima-vm/lima/v2/pkg/limayaml" | ||
"github.com/lima-vm/lima/v2/pkg/reflectutil" | ||
"github.com/lima-vm/lima/v2/pkg/store" | ||
) | ||
|
||
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 *store.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 *store.Instance) *driver.ConfiguredDriver { | ||
l.Instance = inst | ||
l.SSHLocalPort = inst.SSHLocalPort | ||
|
||
return &driver.ConfiguredDriver{ | ||
Driver: l, | ||
} | ||
} | ||
|
||
func (l *LimaAcDriver) Validate() error { | ||
if *l.Instance.Config.MountType != limayaml.VIRTIOFS && *l.Instance.Config.MountType != limayaml.REVSSHFS { | ||
return fmt.Errorf("field `mountType` must be %q or %q for AC driver, got %q", limayaml.VIRTIOFS, limayaml.REVSSHFS, *l.Instance.Config.MountType) | ||
} | ||
// TODO: revise this list for AC | ||
if unknown := reflectutil.UnknownNonEmptyFields(l.Instance.Config, knownYamlProperties...); len(unknown) > 0 { | ||
logrus.Warnf("Ignoring: vmType %s: %+v", *l.Instance.Config.VMType, unknown) | ||
} | ||
|
||
if !limayaml.IsNativeArch(*l.Instance.Config.Arch) { | ||
return fmt.Errorf("unsupported arch: %q", *l.Instance.Config.Arch) | ||
} | ||
|
||
// TODO: real filetype checks | ||
tarFileRegex := regexp.MustCompile(`.*tar\.*`) | ||
for i, image := range l.Instance.Config.Images { | ||
if unknown := reflectutil.UnknownNonEmptyFields(image, "File"); len(unknown) > 0 { | ||
logrus.Warnf("Ignoring: vmType %s: images[%d]: %+v", *l.Instance.Config.VMType, i, unknown) | ||
} | ||
match := tarFileRegex.MatchString(image.Location) | ||
if image.Arch == *l.Instance.Config.Arch && !match { | ||
return fmt.Errorf("unsupported image type for vmType: %s, tarball root file system required: %q", *l.Instance.Config.VMType, image.Location) | ||
} | ||
} | ||
|
||
for i, mount := range l.Instance.Config.Mounts { | ||
if unknown := reflectutil.UnknownNonEmptyFields(mount); len(unknown) > 0 { | ||
logrus.Warnf("Ignoring: vmType %s: mounts[%d]: %+v", *l.Instance.Config.VMType, i, unknown) | ||
} | ||
} | ||
|
||
for i, network := range l.Instance.Config.Networks { | ||
if unknown := reflectutil.UnknownNonEmptyFields(network); len(unknown) > 0 { | ||
logrus.Warnf("Ignoring: vmType %s: networks[%d]: %+v", *l.Instance.Config.VMType, i, unknown) | ||
} | ||
} | ||
|
||
audioDevice := *l.Instance.Config.Audio.Device | ||
if audioDevice != "" { | ||
logrus.Warnf("Ignoring: vmType %s: `audio.device`: %+v", *l.Instance.Config.VMType, audioDevice) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (l *LimaAcDriver) Start(ctx context.Context) (chan error, error) { | ||
logrus.Infof("Starting AC VM") | ||
status, err := store.GetAcStatus(l.Instance.Name) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
distroName := "lima-" + l.Instance.Name | ||
|
||
if status == store.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 := registerVM(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) | ||
} | ||
|
||
func (l *LimaAcDriver) Unregister(ctx context.Context) error { | ||
distroName := "lima-" + l.Instance.Name | ||
status, err := store.GetAcStatus(l.Instance.Name) | ||
if err != nil { | ||
return err | ||
} | ||
switch status { | ||
case store.StatusRunning, store.StatusStopped, store.StatusBroken, store.StatusInstalling: | ||
return unregisterVM(ctx, distroName) | ||
} | ||
|
||
logrus.Info("VM not registered, skipping unregistration") | ||
return nil | ||
} | ||
|
||
// 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 | ||
if l.Instance != nil { | ||
info.InstanceDir = l.Instance.Dir | ||
} | ||
info.DriverName = "ac" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "ac" isn't a descriptive name. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are breaking @afbjorklund's pun about AC/DC (Apple Container/Debian Container). 🤣 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we will abandon those before merging, also they didn't rhyme with "WSL2" anyway (WC? 🚽) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We use "vz" for Virtualization.framework, so I thought we could use "ac" for Apple Container There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using "cz" is a bit non-obvious, and there is no Containerization.framework - only a Swift library... /usr/local/bin/container: /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 2000.62.0) /usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.12) /usr/lib/libbz2.1.0.dylib (compatibility version 1.0.0, current version 1.0.8) /usr/lib/liblzma.5.dylib (compatibility version 6.0.0, current version 6.3.0) /usr/lib/libarchive.2.dylib (compatibility version 9.0.0, current version 9.2.0) /usr/lib/libiconv.2.dylib (compatibility version 7.0.0, current version 7.0.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1356.0.0) /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 4037.0.0) /System/Library/Frameworks/CryptoKit.framework/Versions/A/CryptoKit (compatibility version 1.0.0, current version 1.0.0) /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 4037.0.0) /System/Library/Frameworks/Network.framework/Versions/A/Network (compatibility version 1.0.0, current version 5569.0.219) /System/Library/Frameworks/Security.framework/Versions/A/Security (compatibility version 1.0.0, current version 61901.0.56) /System/Library/Frameworks/Virtualization.framework/Versions/A/Virtualization (compatibility version 1.0.0, current version 259.0.0) /System/Library/Frameworks/vmnet.framework/Versions/A/vmnet (compatibility version 1.0.0, current version 1.0.0, weak) /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0) /usr/lib/swift/libswiftCore.dylib (compatibility version 0.0.0, current version 0.0.0) /usr/lib/swift/libswiftCoreFoundation.dylib (compatibility version 1.0.0, current version 120.100.0, weak) /usr/lib/swift/libswiftCoreImage.dylib (compatibility version 1.0.0, current version 2.2.0, weak) /usr/lib/swift/libswiftDarwin.dylib (compatibility version 1.0.0, current version 347.0.12) /usr/lib/swift/libswiftDispatch.dylib (compatibility version 1.0.0, current version 56.0.0) /usr/lib/swift/libswiftIOKit.dylib (compatibility version 1.0.0, current version 1.0.0, weak) /usr/lib/swift/libswiftMetal.dylib (compatibility version 1.0.0, current version 370.57.0, weak) /usr/lib/swift/libswiftOSLog.dylib (compatibility version 1.0.0, current version 8.0.0, weak) /usr/lib/swift/libswiftObjectiveC.dylib (compatibility version 1.0.0, current version 948.0.0, weak) /usr/lib/swift/libswiftQuartzCore.dylib (compatibility version 1.0.0, current version 5.0.0, weak) /usr/lib/swift/libswiftSynchronization.dylib (compatibility version 1.0.0, current version 0.0.0) /usr/lib/swift/libswiftUniformTypeIdentifiers.dylib (compatibility version 1.0.0, current version 874.0.0, weak) /usr/lib/swift/libswiftXPC.dylib (compatibility version 1.0.0, current version 105.0.13) /usr/lib/swift/libswift_Builtin_float.dylib (compatibility version 1.0.0, current version 0.0.0, weak) /usr/lib/swift/libswift_Concurrency.dylib (compatibility version 1.0.0, current version 0.0.0) /usr/lib/swift/libswift_StringProcessing.dylib (compatibility version 1.0.0, current version 0.0.0) /usr/lib/swift/libswiftos.dylib (compatibility version 1.0.0, current version 1076.0.0) /usr/lib/swift/libswiftsimd.dylib (compatibility version 1.0.0, current version 23.0.0, weak) /usr/lib/swift/libswift_errno.dylib (compatibility version 1.0.0, current version 347.0.12) /usr/lib/swift/libswift_stdio.dylib (compatibility version 1.0.0, current version 347.0.12) /usr/lib/swift/libswift_signal.dylib (compatibility version 1.0.0, current version 347.0.12) The use of "hvf" for Hypervisor.framework and "vz" for Virtualization.framework is confusing enough |
||
info.CanRunGUI = l.canRunGUI() | ||
info.VirtioPort = l.virtioPort | ||
info.VsockPort = l.vSockPort | ||
return info | ||
} | ||
|
||
func (l *LimaAcDriver) Initialize(_ context.Context) error { | ||
return nil | ||
} | ||
|
||
func (l *LimaAcDriver) CreateDisk(_ context.Context) error { | ||
return nil | ||
} | ||
|
||
func (l *LimaAcDriver) Register(_ 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 == "" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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") |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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/store" | ||
"github.com/lima-vm/lima/v2/pkg/store/filenames" | ||
) | ||
|
||
// EnsureFs downloads the root fs. | ||
func EnsureFs(ctx context.Context, inst *store.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 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The provision scripts should not be aware of the VM driver names
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is currently necessary because drivers don't yet have the capability to provide their own boot scripts. We briefly touched on this on Slack in the #lima-gsoc channel1. @unsuman is aware.
Footnotes
https://cloud-native.slack.com/archives/C08KKELMQ92/p1755330101367019?thread_ts=1755325617.281999&cid=C08KKELMQ92 ↩
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another option is to introduce a common feature variable ("container" ?), that all three drivers can set.