Skip to content

Commit

Permalink
Add support for linux VMs (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
mayeut authored Nov 4, 2022
1 parent 470048a commit 059f836
Show file tree
Hide file tree
Showing 9 changed files with 253 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ main
dist/*
packer-plugin-tart
.docs/*

example/*.iso
example/cidata/*
3 changes: 3 additions & 0 deletions builder/tart/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type Config struct {
common.PackerConfig `mapstructure:",squash"`
bootcommand.VNCConfig `mapstructure:",squash"`
FromIPSW string `mapstructure:"from_ipsw" required:"true"`
FromISO []string `mapstructure:"from_iso" required:"true"`
VMName string `mapstructure:"vm_name" required:"true"`
VMBaseName string `mapstructure:"vm_base_name" required:"true"`
Recovery bool `mapstructure:"recovery" required:"false"`
Expand Down Expand Up @@ -62,6 +63,8 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack

if b.config.FromIPSW != "" {
steps = append(steps, new(stepCreateVM))
} else if len(b.config.FromISO) > 0 {
steps = append(steps, new(stepCreateLinuxVM))
} else if b.config.VMBaseName != "" {
steps = append(steps, new(stepCloneVM))
}
Expand Down
2 changes: 2 additions & 0 deletions builder/tart/builder.hcl2spec.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

106 changes: 106 additions & 0 deletions builder/tart/step_create_linux_vm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package tart

import (
"bytes"
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"os/exec"
"strconv"
"time"
)

type stepCreateLinuxVM struct{}

func (s *stepCreateLinuxVM) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config)
ui := state.Get("ui").(packersdk.Ui)

ui.Say("Creating virtual machine...")

createArguments := []string{
"create", "--linux",
}

if config.DiskSizeGb > 0 {
createArguments = append(createArguments, "--disk-size", strconv.Itoa(int(config.DiskSizeGb)))
}

createArguments = append(createArguments, config.VMName)

if _, err := TartExec(createArguments...); err != nil {
err := fmt.Errorf("Failed to create a VM: %s", err)
state.Put("error", err)
ui.Error(err.Error())

return multistep.ActionHalt
}

if s.RunInstaller(ctx, state) != multistep.ActionContinue {
return multistep.ActionHalt
}

if config.CreateGraceTime != 0 {
message := fmt.Sprintf("Waiting %v to let the Virtualization.Framework's installation process "+
"to finish correctly...", config.CreateGraceTime)
ui.Say(message)
time.Sleep(config.CreateGraceTime)
}

return multistep.ActionContinue
}

func (s *stepCreateLinuxVM) Cleanup(state multistep.StateBag) {
// nothing to clean up
}

func (s *stepCreateLinuxVM) RunInstaller(ctx context.Context, state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config)
ui := state.Get("ui").(packersdk.Ui)

ui.Say("Starting the virtual machine for installation...")
runArgs := []string{"run", config.VMName, }
if config.Headless {
runArgs = append(runArgs, "--no-graphics")
} else {
runArgs = append(runArgs, "--graphics")
}
if !config.DisableVNC {
runArgs = append(runArgs, "--vnc-experimental")
}
for _, iso := range config.FromISO {
runArgs = append(runArgs, fmt.Sprintf("--disk=%s:ro", iso))
}
cmd := exec.Command("tart", runArgs...)
stdout := bytes.NewBufferString("")
cmd.Stdout = stdout
cmd.Stderr = uiWriter{ui: ui}

// Prevent the Tart from opening the Screen Sharing
// window connected to the VNC server we're starting
if !config.DisableVNC {
cmd.Env = cmd.Environ()
cmd.Env = append(cmd.Env, "CI=true")
}

if err := cmd.Start(); err != nil {
err = fmt.Errorf("Error starting VM: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}

defer func() {
ui.Say("Waiting for the install process to shutdown the VM...")
_, _ = cmd.Process.Wait()
}()

if !config.DisableVNC {
if !typeBootCommandOverVNC(ctx, state, config, ui, stdout) {
return multistep.ActionHalt
}
}

return multistep.ActionContinue
}
21 changes: 20 additions & 1 deletion builder/tart/step_disk_resize.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"strings"
)

type stepResize struct {
Expand All @@ -26,6 +27,24 @@ func (s *stepResize) Run(ctx context.Context, state multistep.StateBag) multiste
return multistep.ActionContinue
}

// determine OS
osCmd := packersdk.RemoteCmd{
Command: "uname -s",
}
osBuf := bytes.NewBufferString("")
osCmd.Stdout = osBuf

err := osCmd.RunWithUi(ctx, communicator, &QuietUi{BaseUi: ui})
if err != nil {
ui.Error(err.Error())
return multistep.ActionHalt
}

if strings.TrimSpace(osBuf.String()) != "Darwin" {
ui.Error("Automatic partition resizing not implemented, guest OS might not see the full disk capacity.")
return multistep.ActionContinue
}

ui.Say("Let's SSH in and claim the new space for the disk...")

// Determine the disk and a partition to act on
Expand All @@ -36,7 +55,7 @@ func (s *stepResize) Run(ctx context.Context, state multistep.StateBag) multiste
buf := bytes.NewBufferString("")
listCmd.Stdout = buf

err := listCmd.RunWithUi(ctx, communicator, &QuietUi{BaseUi: ui})
err = listCmd.RunWithUi(ctx, communicator, &QuietUi{BaseUi: ui})
if err != nil {
ui.Error(err.Error())

Expand Down
2 changes: 1 addition & 1 deletion builder/tart/step_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func (s *stepRun) Run(ctx context.Context, state multistep.StateBag) multistep.S

state.Put("tart-cmd", cmd)

if !config.DisableVNC {
if (len(config.FromISO) == 0) && !config.DisableVNC {
if !typeBootCommandOverVNC(ctx, state, config, ui, stdout) {
return multistep.ActionHalt
}
Expand Down
39 changes: 39 additions & 0 deletions example/ubuntu-22.04-hello.pkr.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
packer {
required_plugins {
tart = {
version = ">= 0.5.3"
source = "github.com/cirruslabs/tart"
}
}
}

source "tart-cli" "tart" {
vm_base_name = "ubuntu-22.04-vanilla"
vm_name = "ubuntu-22.04-hello"
cpu_count = 4
memory_gb = 8
disk_size_gb = 10
headless = true
disable_vnc = true
ssh_password = "ubuntu"
ssh_username = "ubuntu"
ssh_timeout = "120s"
}

build {
sources = ["source.tart-cli.tart"]

# resize the disk
provisioner "shell" {
inline = [
"sudo growpart /dev/vda 2",
"sudo resize2fs /dev/vda2"
]
}

provisioner "shell" {
inline = [
"hello"
]
}
}
41 changes: 41 additions & 0 deletions example/ubuntu-22.04-prepare.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/bin/zsh

set -eu

if [ ! -f ubuntu-22.04.1-live-server-arm64.iso ]; then
echo "Downloading image..."
curl -fSLO https://cdimage.ubuntu.com/releases/22.04/release/ubuntu-22.04.1-live-server-arm64.iso
fi

if [ -d cidata ]; then
rm -rf cidata*
fi

mkdir cidata
touch cidata/meta-data
cat << 'EOF' > cidata/user-data
#cloud-config
autoinstall:
version: 1
identity:
hostname: ubuntu-server
# the password is ubuntu, needs to be encoded to /etc/shadow format
password: "$6$exDY1mhS4KUYCE/2$zmn9ToZwTKLhCw.b4/b.ZRTIZM30JZ4QrOQ2aOXJ8yk96xpcCof0kxKwuX1kqLG/ygbJ1f8wxED22bTL4F46P0"
username: ubuntu
storage:
swap:
size: 0
layout:
name: direct
ssh:
install-server: true
allow-pw: true
late-commands:
- "echo 'ubuntu ALL=(ALL) NOPASSWD: ALL' > /target/etc/sudoers.d/ubuntu-nopasswd"
shutdown: "poweroff"
EOF
hdiutil makehybrid -o cidata.iso cidata -joliet -iso
38 changes: 38 additions & 0 deletions example/ubuntu-22.04-vanilla.pkr.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
packer {
required_plugins {
tart = {
version = ">= 0.5.3"
source = "github.com/cirruslabs/tart"
}
}
}

source "tart-cli" "tart" {
from_iso = ["cidata.iso", "ubuntu-22.04.1-live-server-arm64.iso"]
vm_name = "ubuntu-22.04-vanilla"
cpu_count = 4
memory_gb = 8
disk_size_gb = 8
boot_command = [
# grub
"<wait5s><enter>",
# autoinstall prompt
"<wait30s>yes<enter>"
]
ssh_password = "ubuntu"
ssh_username = "ubuntu"
ssh_timeout = "120s"
}

build {
sources = ["source.tart-cli.tart"]

provisioner "shell" {
environment_vars = ["DEBIAN_FRONTEND=noninteractive"]
execute_command = "sudo sh -c '{{ .Vars }} {{ .Path }}'"
inline = [
"apt-get update",
"apt-get install -y hello"
]
}
}

0 comments on commit 059f836

Please sign in to comment.