Skip to content

Commit

Permalink
Merge pull request #449 from roots/refactor-lima-config-management
Browse files Browse the repository at this point in the history
Refactor Lima config management
  • Loading branch information
swalkinshaw authored Aug 3, 2024
2 parents 4122c50 + bca23c1 commit 57a8e99
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 59 deletions.
8 changes: 7 additions & 1 deletion cmd/vm_start.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,19 @@ func (c *VmStartCommand) Run(args []string) int {
return 1
}

// VM doesn't exist yet, create it
// VM doesn't exist yet, create and start it
if err = manager.CreateInstance(siteName); err != nil {
c.UI.Error("Error creating VM.")
c.UI.Error(err.Error())
return 1
}

if err = manager.StartInstance(siteName); err != nil {
c.UI.Error("Error starting VM.")
c.UI.Error(err.Error())
return 1
}

c.UI.Info("\nProvisioning VM...")

provisionCmd := NewProvisionCommand(c.UI, c.Trellis)
Expand Down
35 changes: 20 additions & 15 deletions pkg/lima/instance.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package lima

import (
"bytes"
_ "embed"
"errors"
"fmt"
"os"
"path/filepath"
"regexp"
"text/template"

Expand Down Expand Up @@ -40,7 +42,6 @@ type Config struct {
}

type Instance struct {
ConfigFile string
InventoryFile string
Sites map[string]*trellis.Site
Name string `json:"name"`
Expand All @@ -55,16 +56,29 @@ type Instance struct {
Username string `json:"username,omitempty"`
}

func (i *Instance) CreateConfig() error {
func (i *Instance) ConfigFile() string {
return filepath.Join(i.Dir, "lima.yaml")
}

func (i *Instance) GenerateConfig() (*bytes.Buffer, error) {
var contents bytes.Buffer

tpl := template.Must(template.New("lima").Parse(ConfigTemplate))

file, err := os.Create(i.ConfigFile)
if err != nil {
return fmt.Errorf("%v: %w", ConfigErr, err)
if err := tpl.Execute(&contents, i); err != nil {
return &contents, fmt.Errorf("%v: %w", ConfigErr, err)
}

err = tpl.Execute(file, i)
return &contents, nil
}

func (i *Instance) UpdateConfig() error {
contents, err := i.GenerateConfig()
if err != nil {
return err
}

if err := os.WriteFile(i.ConfigFile(), contents.Bytes(), 0666); err != nil {
return fmt.Errorf("%v: %w", ConfigErr, err)
}

Expand All @@ -91,15 +105,6 @@ func (i *Instance) CreateInventoryFile() error {
return nil
}

func (i *Instance) DeleteConfig() error {
err := os.Remove(i.ConfigFile)
if err != nil {
return fmt.Errorf("Could not delete config file: %v", err)
}

return nil
}

/*
Gets the IP address of the instance using the output of `ip route`:
default via 192.168.64.1 proto dhcp src 192.168.64.2 metric 100
Expand Down
81 changes: 75 additions & 6 deletions pkg/lima/instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,17 @@ import (
"github.com/roots/trellis-cli/trellis"
)

func TestCreateConfig(t *testing.T) {
func TestGenerateConfig(t *testing.T) {
defer trellis.LoadFixtureProject(t)()
trellis := trellis.NewTrellis()
if err := trellis.LoadProject(); err != nil {
t.Fatal(err)
}

dir := t.TempDir()
configFile := filepath.Join(dir, "lima.yaml")

instance := &Instance{
Dir: dir,
ConfigFile: configFile,
Dir: dir,
Config: Config{
Images: []Image{
{
Expand All @@ -40,12 +38,83 @@ func TestCreateConfig(t *testing.T) {
Sites: trellis.Environments["development"].WordPressSites,
}

err := instance.CreateConfig()
content, err := instance.GenerateConfig()
if err != nil {
t.Fatal(err)
}

content, err := os.ReadFile(configFile)
absSitePath := filepath.Join(trellis.Path, "../site")

expected := fmt.Sprintf(`vmType: "vz"
rosetta:
enabled: false
images:
- location: http://ubuntu.com/focal
arch: aarch64
mounts:
- location: %s
mountPoint: /srv/www/example.com/current
writable: true
mountType: "virtiofs"
ssh:
forwardAgent: true
networks:
- vzNAT: true
portForwards:
- guestPort: 80
hostPort: 1234
containerd:
user: false
provision:
- mode: system
script: |
#!/bin/bash
echo "127.0.0.1 $(hostname)" >> /etc/hosts
`, absSitePath)

if content.String() != expected {
t.Errorf("expected %s\ngot %s", expected, content.String())
}
}

func TestUpdateConfig(t *testing.T) {
defer trellis.LoadFixtureProject(t)()
trellis := trellis.NewTrellis()
if err := trellis.LoadProject(); err != nil {
t.Fatal(err)
}

dir := t.TempDir()

instance := &Instance{
Dir: dir,
Config: Config{
Images: []Image{
{
Location: "http://ubuntu.com/focal",
Arch: "aarch64",
},
},
PortForwards: []PortForward{
{
HostPort: 1234,
GuestPort: 80,
},
},
},
Sites: trellis.Environments["development"].WordPressSites,
}

err := instance.UpdateConfig()
if err != nil {
t.Fatal(err)
}

content, err := os.ReadFile(instance.ConfigFile())

if err != nil {
t.Fatal(err)
Expand Down
61 changes: 27 additions & 34 deletions pkg/lima/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,20 +80,18 @@ func (m *Manager) GetInstance(name string) (Instance, bool) {
func (m *Manager) CreateInstance(name string) error {
instance := m.newInstance(name)

if err := instance.CreateConfig(); err != nil {
return err
}

err := command.WithOptions(
cmd := command.WithOptions(
command.WithTermOutput(),
command.WithLogging(m.ui),
).Cmd("limactl", []string{"start", "--tty=false", "--name=" + instance.Name, instance.ConfigFile}).Run()
).Cmd("limactl", []string{"create", "--tty=false", "--name=" + instance.Name, "-"})

configContents, err := instance.GenerateConfig()
if err != nil {
return err
}

return postStart(m, instance)
cmd.Stdin = configContents
return cmd.Run()
}

func (m *Manager) DeleteInstance(name string) error {
Expand All @@ -114,10 +112,6 @@ func (m *Manager) DeleteInstance(name string) error {
return err
}

if err := instance.DeleteConfig(); err != nil {
return err
}

return nil
} else {
return fmt.Errorf("Error: VM is running. Run `trellis vm stop` to stop it.")
Expand Down Expand Up @@ -158,6 +152,10 @@ func (m *Manager) StartInstance(name string) error {
return nil
}

if err := instance.UpdateConfig(); err != nil {
return err
}

err := command.WithOptions(
command.WithTermOutput(),
command.WithLogging(m.ui),
Expand All @@ -167,7 +165,24 @@ func (m *Manager) StartInstance(name string) error {
return err
}

return postStart(m, instance)
user, err := instance.getUsername()
if err != nil {
return fmt.Errorf("Could not get username: %v", err)
}

instance.Username = string(user)

// Hydrate instance with data from limactl that is only available after starting (mainly the forwarded SSH local port)
err = m.hydrateInstance(&instance)
if err != nil {
return err
}

if err = m.addHosts(instance); err != nil {
return err
}

return nil
}

func (m *Manager) StopInstance(name string) error {
Expand Down Expand Up @@ -213,7 +228,6 @@ func (m *Manager) hydrateInstance(instance *Instance) error {
}

func (m *Manager) initInstance(instance *Instance) {
instance.ConfigFile = filepath.Join(m.ConfigPath, instance.Name+".yml")
instance.InventoryFile = m.InventoryPath()
instance.Sites = m.Sites
}
Expand Down Expand Up @@ -281,27 +295,6 @@ func (m *Manager) removeHosts(instance Instance) error {
return m.HostsResolver.RemoveHosts(instance.Name)
}

func postStart(manager *Manager, instance Instance) error {
user, err := instance.getUsername()
if err != nil {
return fmt.Errorf("Could not get username: %v", err)
}

instance.Username = string(user)

// Hydrate instance with data from limactl that is only available after starting (mainly the forwarded SSH local port)
err = manager.hydrateInstance(&instance)
if err != nil {
return err
}

if err = manager.addHosts(instance); err != nil {
return err
}

return nil
}

func getMacOSVersion() (string, error) {
cmd := command.Cmd("sw_vers", []string{"-productVersion"})
b, err := cmd.Output()
Expand Down
Loading

0 comments on commit 57a8e99

Please sign in to comment.