First working version of virtualbox/vm builder
This commit is contained in:
parent
2a531f8ad6
commit
d3202497ae
|
@ -69,6 +69,9 @@ type Driver interface {
|
|||
|
||||
//
|
||||
SnapshotExists(string, string) (bool, error)
|
||||
|
||||
//
|
||||
GetParentSnapshot(string, string) (string, error)
|
||||
}
|
||||
|
||||
func NewDriver() (Driver, error) {
|
||||
|
|
|
@ -12,7 +12,8 @@ import (
|
|||
"time"
|
||||
|
||||
versionUtil "github.com/hashicorp/go-version"
|
||||
"github.com/hashicorp/packer/common/retry"
|
||||
|
||||
packer "github.com/hashicorp/packer/common"
|
||||
)
|
||||
|
||||
type VBox42Driver struct {
|
||||
|
@ -241,10 +242,14 @@ func (d *VBox42Driver) Version() (string, error) {
|
|||
}
|
||||
|
||||
func (d *VBox42Driver) CreateSnapshot(vmname string, snapshotName string) error {
|
||||
log.Printf("Executing CreateSnapshot: VM: %s, SnapshotName %s", vmname, snapshotName)
|
||||
|
||||
return d.VBoxManage("snapshot", vmname, "take", snapshotName)
|
||||
}
|
||||
|
||||
func (d *VBox42Driver) HasSnapshots(vmname string) (bool, error) {
|
||||
log.Printf("Executing HasSnapshots: VM: %s", vmname)
|
||||
|
||||
var stdout, stderr bytes.Buffer
|
||||
var hasSnapshots = false
|
||||
|
||||
|
@ -268,6 +273,8 @@ func (d *VBox42Driver) HasSnapshots(vmname string) (bool, error) {
|
|||
}
|
||||
|
||||
func (d *VBox42Driver) GetCurrentSnapshot(vmname string) (string, error) {
|
||||
log.Printf("Executing GetCurrentSnapshot: VM: %s", vmname)
|
||||
|
||||
var stdout, stderr bytes.Buffer
|
||||
|
||||
cmd := exec.Command(d.VBoxManagePath, "snapshot", vmname, "list", "--machinereadable")
|
||||
|
@ -299,6 +306,8 @@ func (d *VBox42Driver) GetCurrentSnapshot(vmname string) (string, error) {
|
|||
}
|
||||
|
||||
func (d *VBox42Driver) SetSnapshot(vmname string, snapshotName string) error {
|
||||
log.Printf("Executing SetSnapshot: VM: %s, SnapshotName %s", vmname, snapshotName)
|
||||
|
||||
var err error
|
||||
if snapshotName == "" {
|
||||
err = d.VBoxManage("snapshot", vmname, "restorecurrent")
|
||||
|
@ -313,12 +322,18 @@ func (d *VBox42Driver) DeleteSnapshot(vmname string, snapshotName string) error
|
|||
}
|
||||
|
||||
func (d *VBox42Driver) SnapshotExists(vmname string, snapshotName string) (bool, error) {
|
||||
var stdout bytes.Buffer
|
||||
log.Printf("Executing SnapshotExists: VM %s, SnapshotName %s", vmname, snapshotName)
|
||||
|
||||
var stdout, stderr bytes.Buffer
|
||||
|
||||
cmd := exec.Command(d.VBoxManagePath, "snapshot", vmname, "list", "--machinereadable")
|
||||
cmd.Stdout = &stdout
|
||||
if err := cmd.Run(); err != nil {
|
||||
return false, err
|
||||
cmd.Stderr = &stderr
|
||||
err := cmd.Run()
|
||||
|
||||
if _, ok := err.(*exec.ExitError); ok {
|
||||
stderrString := strings.TrimSpace(stderr.String())
|
||||
return false, (fmt.Errorf("VBoxManage error: %s", stderrString))
|
||||
}
|
||||
|
||||
SnapshotNameRe := regexp.MustCompile(fmt.Sprintf("SnapshotName[^=]*=[^\"]*\"%s\"", snapshotName))
|
||||
|
@ -331,3 +346,64 @@ func (d *VBox42Driver) SnapshotExists(vmname string, snapshotName string) (bool,
|
|||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (d *VBox42Driver) GetParentSnapshot(vmname string, snapshotName string) (string, error) {
|
||||
log.Printf("Executing GetParentSnapshot: VM %s, SnapshotName %s", vmname, snapshotName)
|
||||
|
||||
var stdout, stderr bytes.Buffer
|
||||
|
||||
cmd := exec.Command(d.VBoxManagePath, "snapshot", vmname, "list", "--machinereadable")
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
err := cmd.Run()
|
||||
|
||||
if _, ok := err.(*exec.ExitError); ok {
|
||||
stderrString := strings.TrimSpace(stderr.String())
|
||||
return "", (fmt.Errorf("VBoxManage error: %s", stderrString))
|
||||
}
|
||||
|
||||
SnapshotNameRe := regexp.MustCompile(fmt.Sprintf("SnapshotName[^=]*=[^\"]*\"%s\"", snapshotName))
|
||||
|
||||
var snapshot string
|
||||
for _, line := range strings.Split(stdout.String(), "\n") {
|
||||
if SnapshotNameRe.MatchString(line) {
|
||||
snapshot = line
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if snapshot == "" {
|
||||
return "", (fmt.Errorf("Snapshot %s does not exist", snapshotName))
|
||||
}
|
||||
|
||||
SnapshotNamePartsRe := regexp.MustCompile("SnapshotName(?P<Path>(-[1-9]+)*)")
|
||||
matches := SnapshotNamePartsRe.FindStringSubmatch(snapshot)
|
||||
log.Printf("************ Snapshot %s name parts", snapshot)
|
||||
log.Printf("Matches %#v\n", matches)
|
||||
log.Printf("Node %s\n", matches[0])
|
||||
log.Printf("Path %s\n", matches[1])
|
||||
log.Printf("Leaf %s\n", matches[2])
|
||||
leaf := matches[2]
|
||||
node := matches[0]
|
||||
if node == "" {
|
||||
return "", (fmt.Errorf("Unsupported format for snapshot %s", snapshot))
|
||||
}
|
||||
if leaf != "" && node != "" {
|
||||
SnapshotNodeRe := regexp.MustCompile("^(?P<Node>SnapshotName[^=]*)=[^\"]*\"(?P<Name>[^\"]+)\"")
|
||||
parentNode := node[:len(node)-len(leaf)]
|
||||
log.Printf("Parent node %s\n", parentNode)
|
||||
var parentName string
|
||||
for _, line := range strings.Split(stdout.String(), "\n") {
|
||||
if matches := SnapshotNodeRe.FindStringSubmatch(line); len(matches) > 1 && parentNode == matches[1] {
|
||||
parentName = matches[2]
|
||||
log.Printf("Parent Snapshot name %s\n", parentName)
|
||||
break
|
||||
}
|
||||
}
|
||||
if parentName == "" {
|
||||
return "", (fmt.Errorf("Internal error: Unable to find name for snapshot node %s", parentNode))
|
||||
}
|
||||
return parentName, nil
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
|
|
@ -70,12 +70,28 @@ func (s *StepForwardSSH) Run(ctx context.Context, state multistep.StateBag) mult
|
|||
"--natpf1",
|
||||
fmt.Sprintf("packercomm,tcp,127.0.0.1,%d,,%d", sshHostPort, guestPort),
|
||||
}
|
||||
retried := false
|
||||
retry:
|
||||
if err := driver.VBoxManage(command...); err != nil {
|
||||
if !strings.Contains(err.Error(), "A NAT rule of this name already exists") {
|
||||
if !strings.Contains(err.Error(), "A NAT rule of this name already exists") || retried {
|
||||
err := fmt.Errorf("Error creating port forwarding rule: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
} else {
|
||||
log.Printf("A packer NAT rule already exists. Trying to delete ...")
|
||||
delcommand := []string{
|
||||
"modifyvm", vmName,
|
||||
"--natpf1",
|
||||
"delete", "packercomm",
|
||||
}
|
||||
if err := driver.VBoxManage(delcommand...); err != nil {
|
||||
err := fmt.Errorf("Error deleting packer NAT forwarding rule: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
goto retry
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,10 +50,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
|
||||
// Build the steps.
|
||||
steps := []multistep.Step{
|
||||
&common.StepOutputDir{
|
||||
Force: b.config.PackerForce,
|
||||
Path: b.config.OutputDir,
|
||||
},
|
||||
new(vboxcommon.StepSuppressMessages),
|
||||
&common.StepCreateFloppy{
|
||||
Files: b.config.FloppyConfig.FloppyFiles,
|
||||
|
@ -62,6 +58,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
&StepSetSnapshot{
|
||||
Name: b.config.VMName,
|
||||
AttachSnapshot: b.config.AttachSnapshot,
|
||||
KeepRegistered: b.config.KeepRegistered,
|
||||
},
|
||||
&common.StepHTTPServer{
|
||||
HTTPDir: b.config.HTTPDir,
|
||||
|
@ -105,6 +102,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
VMName: b.config.VMName,
|
||||
Ctx: b.config.ctx,
|
||||
GroupInterval: b.config.BootConfig.BootGroupInterval,
|
||||
Comm: &b.config.Comm,
|
||||
},
|
||||
&communicator.StepConnect{
|
||||
Config: &b.config.SSHConfig.Comm,
|
||||
|
@ -147,6 +145,14 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
},
|
||||
}
|
||||
|
||||
if !b.config.SkipExport {
|
||||
steps = append(steps, nil)
|
||||
copy(steps[1:], steps)
|
||||
steps[0] = &common.StepOutputDir{
|
||||
Force: b.config.PackerForce,
|
||||
Path: b.config.OutputDir,
|
||||
}
|
||||
}
|
||||
// Run the steps.
|
||||
b.runner = common.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state)
|
||||
b.runner.Run(state)
|
||||
|
@ -165,7 +171,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
return nil, errors.New("Build was halted.")
|
||||
}
|
||||
|
||||
return vboxcommon.NewArtifact(b.config.OutputDir)
|
||||
if b.config.SkipExport {
|
||||
return nil, nil
|
||||
} else {
|
||||
return vboxcommon.NewArtifact(b.config.OutputDir)
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel.
|
||||
|
|
|
@ -131,10 +131,10 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
|||
if c.AttachSnapshot != "" {
|
||||
snapshotExists, err := driver.SnapshotExists(c.VMName, c.AttachSnapshot)
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Failed to check for snapshot: %s", err))
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Failed to check for snapshot: %s with VM %s ; Error: %s", c.AttachSnapshot, c.VMName, err))
|
||||
} else {
|
||||
if !snapshotExists {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Snapshot does not exist: %s", c.AttachSnapshot))
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Snapshot %s does not exist on with VM %s", c.AttachSnapshot, c.VMName))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -144,10 +144,35 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
|||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Failed to check for snapshot: %s", err))
|
||||
} else {
|
||||
if snapshotExists {
|
||||
warnings = append(warnings, fmt.Sprintf("Target snapshot already exists: %s.", c.TargetSnapshot))
|
||||
parent, err := driver.GetParentSnapshot(c.VMName, c.TargetSnapshot)
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Failed to get parent for snapshot %s: %s", c.TargetSnapshot, err))
|
||||
return nil, warnings, errs
|
||||
} else {
|
||||
var selfSnapshotName string
|
||||
if "" != c.AttachSnapshot {
|
||||
selfSnapshotName = c.AttachSnapshot
|
||||
} else {
|
||||
currentSnapshot, err := driver.GetCurrentSnapshot(c.VMName)
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Failed to get current snapshot for VM %s: %s", c.VMName, err))
|
||||
return nil, warnings, errs
|
||||
}
|
||||
selfSnapshotName = currentSnapshot
|
||||
}
|
||||
selfSnapshotParent, err := driver.GetParentSnapshot(c.VMName, selfSnapshotName)
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Failed to get parent for snapshot %s: %s", selfSnapshotName, err))
|
||||
} else if parent != selfSnapshotName {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Target snapshot %s already exists and is not a direct child of %s", c.TargetSnapshot, selfSnapshotParent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if c.AttachSnapshot != "" && c.TargetSnapshot != "" && c.AttachSnapshot == c.TargetSnapshot {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Attach snapshot %s and target snapshot %s cannot be the same", c.AttachSnapshot, c.TargetSnapshot))
|
||||
}
|
||||
}
|
||||
// Check for any errors.
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
|
|
|
@ -3,6 +3,7 @@ package vm
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
vboxcommon "github.com/hashicorp/packer/builder/virtualbox/common"
|
||||
|
@ -21,7 +22,23 @@ func (s *StepCreateSnapshot) Run(_ context.Context, state multistep.StateBag) mu
|
|||
if s.TargetSnapshot != "" {
|
||||
time.Sleep(10 * time.Second) // Wait after the Vm has been shutdown, otherwise creating the snapshot might make the VM unstartable
|
||||
ui.Say(fmt.Sprintf("Creating snapshot %s on virtual machine %s", s.TargetSnapshot, s.Name))
|
||||
err := driver.CreateSnapshot(s.Name, s.TargetSnapshot)
|
||||
snapshotExists, err := driver.SnapshotExists(s.Name, s.TargetSnapshot)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Failed to check for snapshot: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
} else if snapshotExists {
|
||||
log.Printf("Deleting existing target snapshot %s", s.TargetSnapshot)
|
||||
err = driver.DeleteSnapshot(s.Name, s.TargetSnapshot)
|
||||
if nil != err {
|
||||
err = fmt.Errorf("Unable to delete snapshot %s from VM %s: %s", s.TargetSnapshot, s.Name, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
err = driver.CreateSnapshot(s.Name, s.TargetSnapshot)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating snaphot VM: %s", err)
|
||||
state.Put("error", err)
|
||||
|
|
|
@ -12,26 +12,20 @@ import (
|
|||
type StepSetSnapshot struct {
|
||||
Name string
|
||||
AttachSnapshot string
|
||||
KeepRegistered bool
|
||||
revertToSnapshot string
|
||||
}
|
||||
|
||||
func (s *StepSetSnapshot) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
driver := state.Get("driver").(vboxcommon.Driver)
|
||||
if s.AttachSnapshot != "" {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
hasSnapshots, err := driver.HasSnapshots(s.Name)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error checking for snapshots VM: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
if !hasSnapshots {
|
||||
err := fmt.Errorf("Unable to attach snapshot on VM %s when no snapshots exist", s.Name)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
hasSnapshots, err := driver.HasSnapshots(s.Name)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error checking for snapshots VM: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
} else if hasSnapshots {
|
||||
currentSnapshot, err := driver.GetCurrentSnapshot(s.Name)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Unable to get current snapshot for VM: %s", err)
|
||||
|
@ -39,6 +33,15 @@ func (s *StepSetSnapshot) Run(_ context.Context, state multistep.StateBag) multi
|
|||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
s.revertToSnapshot = currentSnapshot
|
||||
}
|
||||
if s.AttachSnapshot != "" {
|
||||
if !hasSnapshots {
|
||||
err := fmt.Errorf("Unable to attach snapshot on VM %s when no snapshots exist", s.Name)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
ui.Say(fmt.Sprintf("Attaching snapshot %s on virtual machine %s", s.AttachSnapshot, s.Name))
|
||||
err = driver.SetSnapshot(s.Name, s.AttachSnapshot)
|
||||
if err != nil {
|
||||
|
@ -47,7 +50,6 @@ func (s *StepSetSnapshot) Run(_ context.Context, state multistep.StateBag) multi
|
|||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
s.revertToSnapshot = currentSnapshot
|
||||
}
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
@ -56,13 +58,18 @@ func (s *StepSetSnapshot) Cleanup(state multistep.StateBag) {
|
|||
driver := state.Get("driver").(vboxcommon.Driver)
|
||||
if s.revertToSnapshot != "" {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
ui.Say(fmt.Sprintf("Reverting to snapshot %s on virtual machine %s", s.revertToSnapshot, s.Name))
|
||||
err := driver.SetSnapshot(s.Name, s.revertToSnapshot)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Unable to set snapshot for VM: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
if s.KeepRegistered {
|
||||
ui.Say("Keeping virtual machine state (keep_registered = true)")
|
||||
return
|
||||
} else {
|
||||
ui.Say(fmt.Sprintf("Reverting to snapshot %s on virtual machine %s", s.revertToSnapshot, s.Name))
|
||||
err := driver.SetSnapshot(s.Name, s.revertToSnapshot)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Unable to set snapshot for VM: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -241,9 +241,11 @@ func (c *BuildCommand) RunContext(buildCtx context.Context, args []string) int {
|
|||
errors.Unlock()
|
||||
} else {
|
||||
ui.Say(fmt.Sprintf("Build '%s' finished.", name))
|
||||
artifacts.Lock()
|
||||
artifacts.m[name] = runArtifacts
|
||||
artifacts.Unlock()
|
||||
if nil != runArtifacts {
|
||||
artifacts.Lock()
|
||||
artifacts.m[name] = runArtifacts
|
||||
artifacts.Unlock()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
|
|
Loading…
Reference in New Issue