Skip to content
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

core: make qemu an optional dependency #1187

Merged
merged 3 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ Colima can also be utilised solely as a headless virtual machine manager by spec

### Customizing the VM

The default VM created by Colima has 2 CPUs, 2GiB memory and 60GiB storage.
The default VM created by Colima has 2 CPUs, 2GiB memory and 100GiB storage.

The VM can be customized either by passing additional flags to `colima start`.
e.g. `--cpu`, `--memory`, `--disk`, `--runtime`.
Expand Down
16 changes: 11 additions & 5 deletions cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,16 +110,18 @@ Run 'colima template' to set the default configurations or 'colima start --edit'
const (
defaultCPU = 2
defaultMemory = 2
defaultDisk = 60
defaultDisk = 100
defaultKubernetesVersion = kubernetes.DefaultVersion

defaultVMType = "qemu"
defaultMountTypeQEMU = "sshfs"
defaultMountTypeVZ = "virtiofs"
)

var defaultK3sArgs = []string{"--disable=traefik"}
var envSaveConfig = osutil.EnvVar("COLIMA_SAVE_CONFIG")
var (
defaultVMType = "qemu"
defaultK3sArgs = []string{"--disable=traefik"}
envSaveConfig = osutil.EnvVar("COLIMA_SAVE_CONFIG")
)

var startCmdArgs struct {
config.Config
Expand All @@ -140,6 +142,7 @@ var startCmdArgs struct {
func init() {
runtimes := strings.Join(environment.ContainerRuntimes(), ", ")
defaultArch := string(environment.HostArch())
defaultVMType = environment.DefaultVMType()

mounts := strings.Join([]string{defaultMountTypeQEMU, "9p", "virtiofs"}, ", ")
types := strings.Join([]string{defaultVMType, "vz"}, ", ")
Expand Down Expand Up @@ -178,7 +181,6 @@ func init() {
// nested virtualization
if util.MacOSNestedVirtualizationSupported() {
startCmd.Flags().BoolVarP(&startCmdArgs.NestedVirtualization, "nested-virtualization", "z", false, "enable nested virtualization")
startCmd.Flag("nested-virtualization").Hidden = true
}
}

Expand Down Expand Up @@ -290,6 +292,10 @@ func setConfigDefaults(conf *config.Config) {
// handle macOS virtualization.framework transition
if conf.VMType == "" {
conf.VMType = defaultVMType
// if on macOS with no qemu, use vz
if err := util.AssertQemuImg(); err != nil && util.MacOS13OrNewer() {
conf.VMType = "vz"
}
}

if conf.MountType == "" {
Expand Down
5 changes: 5 additions & 0 deletions config/configmanager/configmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ func ValidateConfig(c config.Config) error {
if _, ok := validVMTypes[c.VMType]; !ok {
return fmt.Errorf("invalid vmType: '%s'", c.VMType)
}
if c.VMType == "qemu" {
if err := util.AssertQemuImg(); err != nil {
return fmt.Errorf("cannot use vmType: '%s', error: %w", c.VMType, err)
}
}

return nil
}
Expand Down
4 changes: 2 additions & 2 deletions embedded/defaults/colima.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ cpu: 2
# Size of the disk in GiB to be allocated to the virtual machine.
# NOTE: value can only be increased after virtual machine has been created.
#
# Default: 60
disk: 60
# Default: 100
disk: 100

# Size of the memory in GiB to be allocated to the virtual machine.
# Default: 2
Expand Down
12 changes: 12 additions & 0 deletions environment/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package environment
import (
"context"
"runtime"

"github.com/abiosoft/colima/util"
)

// VM is virtual machine.
Expand Down Expand Up @@ -58,3 +60,13 @@ func (a Arch) Value() Arch {

return Arch(runtime.GOARCH).Value()
}

// DefaultVMType returns the default virtual machine type based on the operation
// system and availability of Qemu.
func DefaultVMType() string {
if util.AssertQemuImg() != nil && util.MacOS13OrNewer() {
return "vz"
}

return "qemu"
}
21 changes: 21 additions & 0 deletions environment/vm/lima/lima.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ func (l *limaVM) Start(ctx context.Context, conf config.Config) error {
return err
})

a.Add(l.assertQemu)

a.Add(func() error {
return l.downloadDiskImage(ctx, conf)
})
Expand Down Expand Up @@ -144,6 +146,8 @@ func (l *limaVM) resume(ctx context.Context, conf config.Config) error {
return err
})

a.Add(l.assertQemu)

a.Add(l.setDiskImage)

a.Add(func() error {
Expand Down Expand Up @@ -319,6 +323,11 @@ func (l *limaVM) syncDiskSize(ctx context.Context, conf config.Config) config.Co
return false
}

if err := util.AssertQemuImg(); err != nil {
log.Warnln(fmt.Errorf("unable to resize disk: %w", err))
return false
}

sizeStr := fmt.Sprintf("%dG", conf.Disk)
args := []string{"qemu-img", "resize"}
disk := limautil.ColimaDiffDisk(config.CurrentProfile().ID)
Expand Down Expand Up @@ -393,3 +402,15 @@ func (l *limaVM) addPostStartActions(a *cli.ActiveCommandChain, conf config.Conf
return nil
})
}

func (l *limaVM) assertQemu() error {
// assert qemu requirement
sameArchitecture := environment.HostArch() == l.limaConf.Arch
if err := util.AssertQemuImg(); err != nil && l.limaConf.VMType == limaconfig.QEMU {
if !sameArchitecture {
return fmt.Errorf("qemu is required to emulate %s: %w", l.limaConf.Arch, err)
}
return err
}
return nil
}
25 changes: 22 additions & 3 deletions environment/vm/lima/limautil/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/abiosoft/colima/environment"
"github.com/abiosoft/colima/environment/host"
"github.com/abiosoft/colima/environment/vm/lima/limaconfig"
"github.com/abiosoft/colima/util"
"github.com/abiosoft/colima/util/downloader"
"github.com/sirupsen/logrus"
)
Expand All @@ -32,7 +33,7 @@ func ImageCached(arch environment.Arch, runtime string) (limaconfig.File, bool)

image := diskImageFile(downloader.CacheFilename(img.Location))

img.Location = image.Raw()
img.Location = image.Location()
img.Digest = ""

return img, image.Generated()
Expand Down Expand Up @@ -65,8 +66,18 @@ func DownloadImage(arch environment.Arch, runtime string) (f limaconfig.File, er
if err != nil {
return f, err
}

diskImage := diskImageFile(qcow2)

// if qemu-img is missing, ignore raw conversion
if err := util.AssertQemuImg(); err != nil {
img.Location = diskImage.String()
img.Digest = "" // remove digest
return img, nil
}

// convert from qcow2 to raw
raw, err := qcow2ToRaw(host, diskImageFile(qcow2))
raw, err := qcow2ToRaw(host, diskImage)
if err != nil {
return f, err
}
Expand Down Expand Up @@ -160,6 +171,14 @@ type diskImageFile string
func (d diskImageFile) String() string { return strings.TrimSuffix(string(d), ".raw") }
func (d diskImageFile) Raw() string { return d.String() + ".raw" }
func (d diskImageFile) Generated() bool {
stat, err := os.Stat(d.Raw())
stat, err := os.Stat(d.Location())
return err == nil && !stat.IsDir()
}

// Location returns the expected location of the image based on availability of qemu.
func (d diskImageFile) Location() string {
if err := util.AssertQemuImg(); err == nil {
return d.Raw()
}
return d.String()
}
16 changes: 16 additions & 0 deletions util/qemu.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package util

import (
"fmt"
"os/exec"
)

// AssertQemuImg checks if qemu-img is available.
func AssertQemuImg() error {
cmd := "qemu-img"
if _, err := exec.LookPath(cmd); err != nil {
return fmt.Errorf("%s not found, run 'brew install %s' to install", cmd, "qemu")
}

return nil
}
Loading