diff --git a/README.md b/README.md index eb5ad1c9..6926b2cf 100644 --- a/README.md +++ b/README.md @@ -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`. diff --git a/cmd/start.go b/cmd/start.go index d9b55c74..0f2a6de9 100644 --- a/cmd/start.go +++ b/cmd/start.go @@ -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 @@ -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"}, ", ") @@ -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 } } @@ -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 == "" { diff --git a/config/configmanager/configmanager.go b/config/configmanager/configmanager.go index 02edccbc..adc5c41f 100644 --- a/config/configmanager/configmanager.go +++ b/config/configmanager/configmanager.go @@ -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 } diff --git a/embedded/defaults/colima.yaml b/embedded/defaults/colima.yaml index 4b9db906..fec14980 100644 --- a/embedded/defaults/colima.yaml +++ b/embedded/defaults/colima.yaml @@ -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 diff --git a/environment/vm.go b/environment/vm.go index 1fccffff..73492209 100644 --- a/environment/vm.go +++ b/environment/vm.go @@ -3,6 +3,8 @@ package environment import ( "context" "runtime" + + "github.com/abiosoft/colima/util" ) // VM is virtual machine. @@ -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" +} diff --git a/environment/vm/lima/lima.go b/environment/vm/lima/lima.go index ab982321..645113e5 100644 --- a/environment/vm/lima/lima.go +++ b/environment/vm/lima/lima.go @@ -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) }) @@ -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 { @@ -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) @@ -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 +} diff --git a/environment/vm/lima/limautil/image.go b/environment/vm/lima/limautil/image.go index 3c6dedb8..a877073f 100644 --- a/environment/vm/lima/limautil/image.go +++ b/environment/vm/lima/limautil/image.go @@ -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" ) @@ -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() @@ -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 } @@ -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() +} diff --git a/util/qemu.go b/util/qemu.go new file mode 100644 index 00000000..1669bc89 --- /dev/null +++ b/util/qemu.go @@ -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 +}