Skip to content
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

[![Go](https://github.com/abiosoft/colima/actions/workflows/go.yml/badge.svg)](https://github.com/abiosoft/colima/actions/workflows/go.yml)
[![Integration](https://github.com/abiosoft/colima/actions/workflows/integration.yml/badge.svg)](https://github.com/abiosoft/colima/actions/workflows/integration.yml)
[![Go Report Card](https://goreportcard.com/badge/github.com/abiosoft/colima)](https://goreportcard.com/report/github.com/abiosoft/colima)

![Demonstration](colima.gif)

Expand Down
10 changes: 7 additions & 3 deletions cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -508,14 +508,18 @@ func start(app app.App, conf config.Config) error {
}

func awaitForInterruption(app app.App) error {
signalChannel := make(chan os.Signal, 1)
signal.Notify(signalChannel, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)

log.Println("keeping Colima in the foreground, press ctrl+c to exit...")
sig := <-signalChannel

sig := <-c
log.Infof("interrupted by: %v", sig)

if err := app.Stop(false); err != nil {
log.Errorf("error stopping: %v", err)
return err
}

return nil
}
10 changes: 5 additions & 5 deletions config/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ type Profile struct {
configDir *requiredDir
}

// ConfigDir implements ProfileInfo.
// ConfigDir returns the configuration directory.
func (p *Profile) ConfigDir() string {
if p.configDir == nil {
p.configDir = &requiredDir{
Expand All @@ -61,22 +61,22 @@ func (p *Profile) ConfigDir() string {
return p.configDir.Dir()
}

// LimaInstanceDir implements ProfileInfo.
// LimaInstanceDir returns the directory for the Lima instance.
func (p *Profile) LimaInstanceDir() string {
return filepath.Join(limaDir.Dir(), p.ID)
}

// File implements ProfileInfo.
// File returns the path to the config file.
func (p *Profile) File() string {
return filepath.Join(p.ConfigDir(), configFileName)
}

// LimaFile implements ProfileInfo.
// LimaFile returns the path to the lima config file.
func (p *Profile) LimaFile() string {
return filepath.Join(p.LimaInstanceDir(), "lima.yaml")
}

// StateFile implements ProfileInfo.
// StateFile returns the path to the state file.
func (p *Profile) StateFile() string {
return filepath.Join(p.LimaInstanceDir(), configFileName)
}
Expand Down
5 changes: 2 additions & 3 deletions daemon/process/vmnet/vmnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@ const Name = "vmnet"
const (
SubProcessEnvVar = "COLIMA_VMNET"

NetGateway = "192.168.106.1"
NetDHCPEnd = "192.168.106.254"
NetInterface = "col0"
NetGateway = "192.168.106.1"
NetDHCPEnd = "192.168.106.254"
)

var _ process.Process = (*vmnetProcess)(nil)
Expand Down
49 changes: 49 additions & 0 deletions environment/container/incus/incus.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/abiosoft/colima/cli"
"github.com/abiosoft/colima/config"
"github.com/abiosoft/colima/environment"
"github.com/abiosoft/colima/environment/vm/lima/limautil"
"github.com/abiosoft/colima/util"
)

Expand Down Expand Up @@ -116,6 +117,13 @@ func (c *incusRuntime) Start(ctx context.Context) error {
return nil
})

a.Add(func() error {
if err := c.registerNetworks(); err != nil {
return cli.ErrNonFatal(err)
}
return nil
})

return a.Exec()
}

Expand Down Expand Up @@ -223,9 +231,50 @@ func (c incusRuntime) addDockerRemote() error {
return c.host.RunQuiet("incus", "remote", "add", "docker", "https://docker.io", "--protocol=oci")
}

func (c incusRuntime) registerNetworks() error {
b, err := c.guest.RunOutput("sudo", "incus", "network", "list", "--format", "json")
if err != nil {
return fmt.Errorf("error listing networks: %w", err)
}

networks := map[string]networkInfo{}
{ // decode and flatten for easy lookup
var resp []networkInfo
if err := json.NewDecoder(strings.NewReader(b)).Decode(&resp); err != nil {
return fmt.Errorf("error decoding networks into struct: %w", err)
}
for _, n := range resp {
networks[n.Name] = n
}
}

for i := 0; i < limautil.VZNetworksMaxNo; i++ {
name := limautil.NetInterfaceName(i)
network, ok := networks[name]

// must be an unmanaged physical network
if !ok || network.Managed || network.Type != "physical" {
continue
}

err := c.guest.RunQuiet("sudo", "incus", "network", "create", name, "--type", "physical", "parent="+name)
if err != nil {
return fmt.Errorf("error creating managed network '%s': %w", name, err)
}
}

return nil
}

//go:embed config.yaml
var configYaml string

type remoteInfo map[string]struct {
Addr string `json:"Addr"`
}

type networkInfo struct {
Name string `json:"name"`
Managed bool `json:"managed"`
Type string `json:"type"`
}
3 changes: 1 addition & 2 deletions environment/container/kubernetes/k3s.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (

"github.com/abiosoft/colima/cli"
"github.com/abiosoft/colima/config"
"github.com/abiosoft/colima/daemon/process/vmnet"
"github.com/abiosoft/colima/environment"
"github.com/abiosoft/colima/environment/container/containerd"
"github.com/abiosoft/colima/environment/container/docker"
Expand Down Expand Up @@ -148,7 +147,7 @@ func installK3sCluster(
args = append(args, "--flannel-iface", "eth0")
} else {
args = append(args, "--advertise-address", ipAddress)
args = append(args, "--flannel-iface", vmnet.NetInterface)
args = append(args, "--flannel-iface", limautil.NetInterface)
}

switch containerRuntime {
Expand Down
8 changes: 8 additions & 0 deletions environment/vm/lima/limaconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type Config struct {
CPUs *int `yaml:"cpus,omitempty"`
Memory string `yaml:"memory,omitempty"`
Disk string `yaml:"disk,omitempty"`
AdditionalDisks []Disk `yaml:"additionalDisks,omitempty" json:"additionalDisks,omitempty"`
Mounts []Mount `yaml:"mounts,omitempty"`
MountType MountType `yaml:"mountType,omitempty" json:"mountType,omitempty"`
SSH SSH `yaml:"ssh"`
Expand Down Expand Up @@ -45,6 +46,13 @@ type Mount struct {
NineP NineP `yaml:"9p,omitempty" json:"9p,omitempty"`
}

type Disk struct {
Name string `yaml:"name" json:"name"` // REQUIRED
Format *bool `yaml:"format,omitempty" json:"format,omitempty"`
FSType *string `yaml:"fsType,omitempty" json:"fsType,omitempty"`
FSArgs []string `yaml:"fsArgs,omitempty" json:"fsArgs,omitempty"`
}

type SSH struct {
LocalPort int `yaml:"localPort,omitempty"`
LoadDotSSHPubKeys bool `yaml:"loadDotSSHPubKeys"`
Expand Down
56 changes: 56 additions & 0 deletions environment/vm/lima/limautil/disk.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package limautil

import (
"bytes"
"encoding/json"
"fmt"

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

// HasDisk checks if a lima disk exists for the current instance.
func HasDisk() bool {
name := config.CurrentProfile().ID

var resp struct {
Name string `json:"name"`
}

cmd := Limactl("disk", "list", "--json", name)
var buf bytes.Buffer
cmd.Stdout = &buf

if err := cmd.Run(); err != nil {
return false
}

if err := json.NewDecoder(&buf).Decode(&resp); err != nil {
return false
}

return resp.Name == name
}

// CreateDisk creates a lima disk with size in GiB.
func CreateDisk(size int) error {
name := config.CurrentProfile().ID
cmd := Limactl("disk", "create", name, "--size", fmt.Sprintf("%dGiB", size))

if err := cmd.Run(); err != nil {
return fmt.Errorf("error creating lima disk: %w", err)
}

return nil
}

// DeleteDisk deletes lima disk for the current instance.
func DeleteDisk() error {
name := config.CurrentProfile().ID
cmd := Limactl("disk", "delete", name)

if err := cmd.Run(); err != nil {
return fmt.Errorf("error deleting lima disk: %w", err)
}

return nil
}
148 changes: 148 additions & 0 deletions environment/vm/lima/limautil/instance.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package limautil

import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"strings"

"github.com/abiosoft/colima/config"
"github.com/abiosoft/colima/config/configmanager"
)

// Instance returns current instance.
func Instance() (InstanceInfo, error) {
return getInstance(config.CurrentProfile().ID)
}

// InstanceConfig returns the current instance config.
func InstanceConfig() (config.Config, error) {
i, err := Instance()
if err != nil {
return config.Config{}, err
}
return i.Config()
}

// InstanceInfo is the information about a Lima instance
type InstanceInfo struct {
Name string `json:"name,omitempty"`
Status string `json:"status,omitempty"`
Arch string `json:"arch,omitempty"`
CPU int `json:"cpus,omitempty"`
Memory int64 `json:"memory,omitempty"`
Disk int64 `json:"disk,omitempty"`
Dir string `json:"dir,omitempty"`
Network []struct {
VNL string `json:"vnl,omitempty"`
Interface string `json:"interface,omitempty"`
} `json:"network,omitempty"`
IPAddress string `json:"address,omitempty"`
Runtime string `json:"runtime,omitempty"`
}

// Running checks if the instance is running.
func (i InstanceInfo) Running() bool { return i.Status == limaStatusRunning }

// Config returns the current Colima config
func (i InstanceInfo) Config() (config.Config, error) {
return configmanager.LoadFrom(config.ProfileFromName(i.Name).StateFile())
}

// Lima statuses
const (
limaStatusRunning = "Running"
)

func getInstance(profileID string) (InstanceInfo, error) {
var i InstanceInfo
var buf bytes.Buffer
cmd := Limactl("list", profileID, "--json")
cmd.Stderr = nil
cmd.Stdout = &buf

if err := cmd.Run(); err != nil {
return i, fmt.Errorf("error retrieving instance: %w", err)
}

if buf.Len() == 0 {
return i, fmt.Errorf("instance '%s' does not exist", config.ProfileFromName(profileID).DisplayName)
}

if err := json.Unmarshal(buf.Bytes(), &i); err != nil {
return i, fmt.Errorf("error retrieving instance: %w", err)
}
return i, nil
}

// Instances returns Lima instances created by colima.
func Instances(ids ...string) ([]InstanceInfo, error) {
limaIDs := make([]string, len(ids))
for i := range ids {
limaIDs[i] = config.ProfileFromName(ids[i]).ID
}
args := append([]string{"list", "--json"}, limaIDs...)

var buf bytes.Buffer
cmd := Limactl(args...)
cmd.Stderr = nil
cmd.Stdout = &buf

if err := cmd.Run(); err != nil {
return nil, fmt.Errorf("error retrieving instances: %w", err)
}

var instances []InstanceInfo
scanner := bufio.NewScanner(&buf)
for scanner.Scan() {
var i InstanceInfo
line := scanner.Bytes()
if err := json.Unmarshal(line, &i); err != nil {
return nil, fmt.Errorf("error retrieving instances: %w", err)
}

// limit to colima instances
if !strings.HasPrefix(i.Name, "colima") {
continue
}

if i.Running() {
for _, n := range i.Network {
if n.Interface == NetInterface {
i.IPAddress = getIPAddress(i.Name, NetInterface)
}
}
conf, _ := i.Config()
i.Runtime = getRuntime(conf)
}

// rename to local friendly names
i.Name = config.ProfileFromName(i.Name).ShortName

// network is low level, remove
i.Network = nil

instances = append(instances, i)
}

return instances, nil
}

func getRuntime(conf config.Config) string {
var runtime string

switch conf.Runtime {
case "docker", "containerd", "incus":
runtime = conf.Runtime
case "none":
return "none"
default:
return ""
}

if conf.Kubernetes.Enabled {
runtime += "+k3s"
}
return runtime
}
Loading