Skip to content

Commit

Permalink
support disk creation, deletion, error on unsupported updates
Browse files Browse the repository at this point in the history
  • Loading branch information
Said Sakuh committed Nov 30, 2023
1 parent cd5d51d commit fb84658
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 61 deletions.
6 changes: 6 additions & 0 deletions cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ func init() {
startCmd.Flags().StringVar(&startCmdArgs.MountType, "mount-type", defaultMountTypeQEMU, "volume driver for the mount ("+mounts+")")
startCmd.Flags().BoolVar(&startCmdArgs.MountINotify, "mount-inotify", true, "propagate inotify file events to the VM")

// lima disks
startCmd.Flags().StringSliceVarP(&startCmdArgs.Flags.Mounts, "lima-disk", "D", nil, "map of disk name and size e.g. --lima-disk=my-disk,10G")

// ssh agent
startCmd.Flags().BoolVarP(&startCmdArgs.ForwardAgent, "ssh-agent", "s", false, "forward SSH agent to the VM")

Expand Down Expand Up @@ -372,6 +375,9 @@ func prepareConfig(cmd *cobra.Command) {
if !cmd.Flag("mount-inotify").Changed {
startCmdArgs.MountINotify = current.MountINotify
}
if !cmd.Flag("lima-disk").Changed {
startCmdArgs.LimaDisks = current.LimaDisks
}
if !cmd.Flag("ssh-agent").Changed {
startCmdArgs.ForwardAgent = current.ForwardAgent
}
Expand Down
7 changes: 3 additions & 4 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ type Config struct {
VZRosetta bool `yaml:"rosetta,omitempty"`

// disks
Disks []Disk `yaml:"disks,omitempty"`
LimaDisks []Disk `yaml:"limaDisks,omitempty"`

// volume mounts
Mounts []Mount `yaml:"mounts,omitempty"`
Expand All @@ -109,9 +109,8 @@ type Config struct {
}

type Disk struct {
Name string `yaml:"name"`
Size string `yaml:"size"`
Format string `yaml:"format,omitempty"`
Name string `yaml:"name"`
Size string `yaml:"size"`
}

// Kubernetes is kubernetes configuration
Expand Down
2 changes: 1 addition & 1 deletion config/configmanager/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ func ValidateConfig(c config.Config) error {
if _, ok := validVMTypes[c.VMType]; !ok {
return fmt.Errorf("invalid vmType: '%s'", c.VMType)
}

return nil
}

Expand All @@ -85,6 +84,7 @@ func Load() (config.Config, error) {

// config file does not exist, check older version for backward compatibility
if _, err := os.Stat(oldCFile); err != nil {
logrus.Warn(fmt.Errorf("error loading config: %w, proceeding with defaults", err))
return config.Config{}, nil
}

Expand Down
13 changes: 7 additions & 6 deletions embedded/defaults/colima.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -175,12 +175,13 @@ mounts: []
# Configure additional disks
#
# EXAMPLE
# additionalDisks:
# - size: 10
# path: /tmp/colima/disk1.img
# - size: 20
# path: /tmp/colima/disk2.img
additionalDisks: []
# disks:
# - name: mydisk
# size: 20G
# format: raw (optional). qcow2 is not supported in current limactl. raw is default.
# - name: otherdisk
# size: 1M
limaDisks: []

# Environment variables for the virtual machine.
#
Expand Down
97 changes: 97 additions & 0 deletions environment/vm/lima/disk.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package lima

import (
"encoding/json"
"fmt"
"github.com/abiosoft/colima/config"
"github.com/docker/go-units"
"github.com/sirupsen/logrus"
"strings"
)

func (l *limaVM) updateLimaDisks(conf config.Config) ([]Disk, error) {
logrus.Trace(fmt.Errorf("updating lima disks: %s", conf.LimaDisks))
out, err := l.host.RunOutput(limactl, "disk", "list", "--json")
if err != nil {
logrus.Trace(fmt.Errorf("error listing disks: %s, %s", out, err))
return []Disk{}, err
}
logrus.Trace(fmt.Errorf("listing disks: %s", out))

var listedDisks []Disk
if out != "" {
for _, line := range strings.Split(strings.TrimSuffix(out, "\n"), "\n") {
var listedDisk Disk
err = json.Unmarshal([]byte(line), &listedDisk)
if err != nil {
logrus.Trace(fmt.Errorf("error unmarshaling listed disks: %s", err))
return []Disk{}, err
}
listedDisks = append(listedDisks, listedDisk)
}
}

var disksToCreate []config.Disk
for _, disk := range conf.LimaDisks {
diskName := config.CurrentProfile().ID + "-" + disk.Name
var found = false
for _, listedDisk := range listedDisks {
if listedDisk.Name == diskName {
found = true
break
}
}
if !found {
disksToCreate = append(disksToCreate, disk)
}
}

for _, disk := range disksToCreate {
diskName := config.CurrentProfile().ID + "-" + disk.Name
logrus.Trace(fmt.Errorf("creating disk %s", diskName))
out, err = l.host.RunOutput(limactl, "disk", "create", diskName, "--size", disk.Size, "--format", "raw")
if err != nil {
logrus.Trace(fmt.Errorf("error creating disk: %s, %s", out, err))
return []Disk{}, err
}
logrus.Trace(fmt.Errorf("disk create output: %s", out))
}

var disksToDelete []Disk
for _, listedDisk := range listedDisks {
var found = false
for _, disk := range conf.LimaDisks {
diskName := config.CurrentProfile().ID + "-" + disk.Name
if listedDisk.Name == diskName {
found = true
diskSize, err := units.RAMInBytes(disk.Size)
if err != nil {
logrus.Trace(fmt.Errorf("error parsing disk size: %s", err))
return []Disk{}, err
}
logrus.Trace(fmt.Errorf("disk size: %d", diskSize))

if diskSize == listedDisk.Size {
logrus.Trace(fmt.Errorf("disk %s is up to date", diskName))
continue
}
return []Disk{}, fmt.Errorf("%s cannot be updated: limactl does not support updating disks", diskName)
}
}
if !found {
disksToDelete = append(disksToDelete, listedDisk)
}
}

for _, disk := range disksToDelete {
logrus.Trace(fmt.Errorf("deleting disk %s", disk.Name))
out, err := l.host.RunOutput(limactl, "disk", "delete", disk.Name)
if err != nil {
logrus.Trace(fmt.Errorf("error deleting disk: %s, %s", out, err))
return []Disk{}, err
}
logrus.Trace(fmt.Errorf("disk delete output: %s", out))
}

return disksToDelete, nil
}
84 changes: 51 additions & 33 deletions environment/vm/lima/lima.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ func (l limaVM) Dependencies() []string {
}
}

type Disk struct {
Name string `json:"name"`
Size int64 `json:"size"`
Dir string `json:"dir"`
Instance string `json:"instance"`
MountPoint string `json:"mountPoint"`
}

func (l *limaVM) Start(ctx context.Context, conf config.Config) error {
a := l.Init(ctx)

Expand All @@ -90,9 +98,14 @@ func (l *limaVM) Start(ctx context.Context, conf config.Config) error {
return err
})

a.Stage("creating and starting")
configFile := filepath.Join(os.TempDir(), config.CurrentProfile().ID+".yaml")

var disksToDelete []Disk
disksToDelete, err := l.updateLimaDisks(conf)
if err != nil {
return err
}

a.Add(func() (err error) {
l.limaConf, err = newConf(ctx, conf)
if err != nil {
Expand All @@ -102,36 +115,28 @@ func (l *limaVM) Start(ctx context.Context, conf config.Config) error {
})
a.Add(l.writeNetworkFile)

if len(conf.Disks) > 0 {
for _, d := range conf.Disks {
a.Add(func() error {
return l.host.Run(limactl, "disk", "create", d.Name, "--size", d.Size, "--format", d.Format)
})
}
}

a.Add(func() error {
return l.host.Run(limactl, "start", "--tty=false", configFile)
})
a.Add(func() error {
return os.Remove(configFile)
})

// adding it to command chain to execute only after successful startup.
logrus.Trace("adding it to command chain to execute only after successful startup.")
a.Add(func() error {
l.conf = conf
return nil
})

// restart needed for docker user
logrus.Trace("restart needed for docker user")
if conf.Runtime == docker.Name {
a.Add(func() error {
ctx := context.WithValue(ctx, cli.CtxKeyQuiet, true)
return l.Restart(ctx)
})
}

l.addPostStartActions(a, conf)
l.addPostStartActions(a, conf, disksToDelete)

return a.Exec()
}
Expand All @@ -145,6 +150,12 @@ func (l *limaVM) resume(ctx context.Context, conf config.Config) error {
return nil
}

var disksToDelete []Disk
disksToDelete, err := l.updateLimaDisks(conf)
if err != nil {
return err
}

a.Add(func() (err error) {
ctx, err = l.startDaemon(ctx, conf)
return err
Expand All @@ -163,21 +174,12 @@ func (l *limaVM) resume(ctx context.Context, conf config.Config) error {

a.Add(l.writeNetworkFile)

if len(conf.Disks) > 0 {
for _, d := range conf.Disks {
log.Println("creating disk", d.Name)
a.Add(func() error {
return l.host.Run(limactl, "disk", "create", d.Name, "--size", d.Size, "--format", d.Format)
})
}
}

a.Stage("starting")
a.Add(func() error {
return l.host.Run(limactl, "start", config.CurrentProfile().ID)
})

l.addPostStartActions(a, conf)
l.addPostStartActions(a, conf, disksToDelete)

return a.Exec()
}
Expand Down Expand Up @@ -224,27 +226,31 @@ func (l limaVM) Stop(ctx context.Context, force bool) error {

func (l limaVM) Teardown(ctx context.Context) error {
a := l.Init(ctx)
conf, _ := limautil.InstanceConfig()

if util.MacOS() {
conf, _ := limautil.InstanceConfig()
a.Retry("", time.Second*1, 10, func(retryCount int) error {
return l.daemon.Stop(ctx, conf)
})
}

a.Stage("stopping")
a.Add(func() error {
return l.host.Run(limactl, "delete", "--force", config.CurrentProfile().ID)
return l.Stop(ctx, false)
})

conf, _ := limautil.InstanceConfig()

if len(conf.Disks) > 0 {
for _, d := range conf.Disks {
a.Add(func() error {
return l.host.Run(limactl, "disk", "delete", d.Name)
})
a.Add(func() error {
for _, d := range conf.LimaDisks {
diskName := config.CurrentProfile().ID + "-" + d.Name
logrus.Trace(fmt.Errorf("deleting disk %s", diskName))
return l.host.Run(limactl, "disk", "delete", diskName)
}
}
return nil
})

a.Add(func() error {
return l.host.Run(limactl, "delete", "--force", config.CurrentProfile().ID)
})

return a.Exec()
}
Expand Down Expand Up @@ -341,7 +347,19 @@ func (l *limaVM) syncDiskSize(ctx context.Context, conf config.Config) config.Co
return conf
}

func (l *limaVM) addPostStartActions(a *cli.ActiveCommandChain, conf config.Config) {
func (l *limaVM) addPostStartActions(a *cli.ActiveCommandChain, conf config.Config, disksToDelete []Disk) {
// delete unused disk mount directories
a.Add(func() error {
for _, disk := range disksToDelete {
err := l.Run("sh", "-c", "sudo rm -rf /mnt/lima-"+disk.Name)
if err != nil {
logrus.Trace(fmt.Errorf("error deleting disk mount directory: %s", err))
return err
}
}
return nil
})

// package dependencies
a.Add(func() error {
return l.installDependencies(a.Logger(), conf)
Expand Down
17 changes: 11 additions & 6 deletions environment/vm/lima/limautil/limautil.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"bytes"
"encoding/json"
"fmt"
"github.com/sirupsen/logrus"
"os"
"os/exec"
"strings"
Expand Down Expand Up @@ -52,17 +53,19 @@ func IPAddress(profileID string) string {
const fallback = "127.0.0.1"
instance, err := getInstance(profileID)
if err != nil {
logrus.Trace(fmt.Errorf("error getting instance: %s", err))
return fallback
}

logrus.Trace(fmt.Errorf("instance: %s", instance))
if len(instance.Network) > 0 {
for _, n := range instance.Network {
if n.Interface == vmnet.NetInterface {
return getIPAddress(profileID, n.Interface)
result := getIPAddress(profileID, n.Interface)
logrus.Trace(fmt.Errorf("ip address: %s", result))
return result
}
}
}

return fallback
}

Expand Down Expand Up @@ -171,14 +174,16 @@ func Instances(ids ...string) ([]InstanceInfo, error) {
}
func getIPAddress(profileID, interfaceName string) string {
var buf bytes.Buffer
logrus.Trace(fmt.Errorf("getting ip address for %s", interfaceName))
// TODO: this should be less hacky
cmd := Limactl("shell", profileID, "sh", "-c",
`ip -4 addr show `+interfaceName+` | grep inet | awk -F' ' '{print $2 }' | cut -d/ -f1`)
cmd := Limactl("shell", profileID, "sh", "-c", `ip -4 -o addr show `+interfaceName+` | grep inet | cut -d ' ' -f7 | cut -d/ -f1`)
cmd.Stderr = nil
cmd.Stdout = &buf

_ = cmd.Run()
return strings.TrimSpace(buf.String())
result := strings.TrimSpace(buf.String())
logrus.Trace(fmt.Errorf("ip address: %s", result))
return result
}

func getRuntime(conf config.Config) string {
Expand Down
Loading

0 comments on commit fb84658

Please sign in to comment.