2019-10-14 10:43:59 -04:00
//go:generate mapstructure-to-hcl2 -type Config
2019-02-16 08:32:22 -05:00
package vm
import (
"fmt"
2019-03-17 15:50:13 -04:00
"log"
2019-02-16 08:32:22 -05:00
"strings"
2019-10-31 10:49:34 -04:00
"time"
2019-02-16 08:32:22 -05:00
vboxcommon "github.com/hashicorp/packer/builder/virtualbox/common"
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/common/bootcommand"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
)
// Config is the configuration structure for the builder.
type Config struct {
2019-08-21 06:28:34 -04:00
common . PackerConfig ` mapstructure:",squash" `
common . HTTPConfig ` mapstructure:",squash" `
common . FloppyConfig ` mapstructure:",squash" `
bootcommand . BootConfig ` mapstructure:",squash" `
vboxcommon . ExportConfig ` mapstructure:",squash" `
vboxcommon . OutputConfig ` mapstructure:",squash" `
vboxcommon . RunConfig ` mapstructure:",squash" `
2020-01-09 11:37:30 -05:00
vboxcommon . CommConfig ` mapstructure:",squash" `
2019-08-21 06:28:34 -04:00
vboxcommon . ShutdownConfig ` mapstructure:",squash" `
vboxcommon . VBoxManageConfig ` mapstructure:",squash" `
vboxcommon . VBoxVersionConfig ` mapstructure:",squash" `
2019-02-16 08:32:22 -05:00
GuestAdditionsMode string ` mapstructure:"guest_additions_mode" `
GuestAdditionsPath string ` mapstructure:"guest_additions_path" `
GuestAdditionsSHA256 string ` mapstructure:"guest_additions_sha256" `
GuestAdditionsURL string ` mapstructure:"guest_additions_url" `
VMName string ` mapstructure:"vm_name" `
AttachSnapshot string ` mapstructure:"attach_snapshot" `
TargetSnapshot string ` mapstructure:"target_snapshot" `
2019-07-26 05:36:05 -04:00
DeleteTargetSnapshot bool ` mapstructure:"force_delete_snapshot" `
2019-02-16 08:32:22 -05:00
KeepRegistered bool ` mapstructure:"keep_registered" `
SkipExport bool ` mapstructure:"skip_export" `
ctx interpolate . Context
}
2019-12-17 05:25:56 -05:00
func ( c * Config ) Prepare ( raws ... interface { } ) ( [ ] string , error ) {
2019-02-16 08:32:22 -05:00
err := config . Decode ( c , & config . DecodeOpts {
Interpolate : true ,
InterpolateContext : & c . ctx ,
InterpolateFilter : & interpolate . RenderFilter {
Exclude : [ ] string {
"boot_command" ,
"guest_additions_path" ,
"guest_additions_url" ,
"vboxmanage" ,
"vboxmanage_post" ,
} ,
} ,
} , raws ... )
if err != nil {
2019-12-17 05:25:56 -05:00
return nil , err
2019-02-16 08:32:22 -05:00
}
// Defaults
if c . GuestAdditionsMode == "" {
c . GuestAdditionsMode = "upload"
}
if c . GuestAdditionsPath == "" {
c . GuestAdditionsPath = "VBoxGuestAdditions.iso"
}
2019-10-31 10:49:34 -04:00
if c . PostShutdownDelay == 0 {
c . PostShutdownDelay = 2 * time . Second
2019-07-26 05:36:05 -04:00
}
2019-02-16 08:32:22 -05:00
// Prepare the errors
var errs * packer . MultiError
errs = packer . MultiErrorAppend ( errs , c . ExportConfig . Prepare ( & c . ctx ) ... )
errs = packer . MultiErrorAppend ( errs , c . FloppyConfig . Prepare ( & c . ctx ) ... )
errs = packer . MultiErrorAppend ( errs , c . HTTPConfig . Prepare ( & c . ctx ) ... )
errs = packer . MultiErrorAppend ( errs , c . OutputConfig . Prepare ( & c . ctx , & c . PackerConfig ) ... )
errs = packer . MultiErrorAppend ( errs , c . RunConfig . Prepare ( & c . ctx ) ... )
errs = packer . MultiErrorAppend ( errs , c . ShutdownConfig . Prepare ( & c . ctx ) ... )
2020-01-09 11:37:30 -05:00
errs = packer . MultiErrorAppend ( errs , c . CommConfig . Prepare ( & c . ctx ) ... )
2019-02-16 08:32:22 -05:00
errs = packer . MultiErrorAppend ( errs , c . VBoxManageConfig . Prepare ( & c . ctx ) ... )
errs = packer . MultiErrorAppend ( errs , c . VBoxVersionConfig . Prepare ( & c . ctx ) ... )
errs = packer . MultiErrorAppend ( errs , c . BootConfig . Prepare ( & c . ctx ) ... )
2019-10-31 06:31:17 -04:00
log . Printf ( "PostShutdownDelay: %s" , c . PostShutdownDelay )
2019-07-26 05:36:05 -04:00
2019-02-16 08:32:22 -05:00
if c . VMName == "" {
errs = packer . MultiErrorAppend ( errs ,
fmt . Errorf ( "vm_name is required" ) )
}
validMode := false
validModes := [ ] string {
vboxcommon . GuestAdditionsModeDisable ,
vboxcommon . GuestAdditionsModeAttach ,
vboxcommon . GuestAdditionsModeUpload ,
}
for _ , mode := range validModes {
if c . GuestAdditionsMode == mode {
validMode = true
break
}
}
if ! validMode {
errs = packer . MultiErrorAppend ( errs ,
fmt . Errorf ( "guest_additions_mode is invalid. Must be one of: %v" , validModes ) )
}
if c . GuestAdditionsSHA256 != "" {
c . GuestAdditionsSHA256 = strings . ToLower ( c . GuestAdditionsSHA256 )
}
// Warnings
var warnings [ ] string
2019-08-03 07:42:53 -04:00
if c . TargetSnapshot == "" && c . SkipExport {
warnings = append ( warnings ,
"No target snapshot is specified (target_snapshot empty) and no export will be created (skip_export=true).\n" +
"You might lose all changes applied by this run, the next time you execute packer." )
}
2019-02-16 08:32:22 -05:00
if c . ShutdownCommand == "" {
warnings = append ( warnings ,
"A shutdown_command was not specified. Without a shutdown command, Packer\n" +
"will forcibly halt the virtual machine, which may result in data loss." )
}
2019-08-03 07:42:53 -04:00
2019-02-16 08:32:22 -05:00
driver , err := vboxcommon . NewDriver ( )
if err != nil {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "Failed creating VirtualBox driver: %s" , err ) )
} else {
2019-03-30 11:27:18 -04:00
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 ) )
}
2019-03-17 15:50:13 -04:00
snapshotTree , err := driver . LoadSnapshots ( c . VMName )
2019-03-30 11:27:18 -04:00
log . Printf ( "" )
2019-03-17 15:50:13 -04:00
if err != nil {
2019-03-30 11:27:18 -04:00
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "Failed to load snapshots for VM %s: %s" , c . VMName , err ) )
2019-03-17 15:50:13 -04:00
} else {
2019-03-30 11:27:18 -04:00
log . Printf ( "Snapshots loaded from VM %s" , c . VMName )
var attachSnapshot * vboxcommon . VBoxSnapshot
if nil != snapshotTree {
attachSnapshot = snapshotTree . GetCurrentSnapshot ( )
log . Printf ( "VM %s is currently attached to snapshot: %s/%s" , c . VMName , attachSnapshot . Name , attachSnapshot . UUID )
2019-03-17 15:50:13 -04:00
}
if c . AttachSnapshot != "" {
2019-03-30 11:27:18 -04:00
log . Printf ( "Checking configuration attach_snapshot [%s]" , c . AttachSnapshot )
if nil == snapshotTree {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "No snapshots defined on VM %s. Unable to attach to %s" , c . VMName , c . AttachSnapshot ) )
2019-03-17 15:50:13 -04:00
} else {
2019-03-30 11:27:18 -04:00
snapshots := snapshotTree . GetSnapshotsByName ( c . AttachSnapshot )
if 0 >= len ( snapshots ) {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "Snapshot %s does not exist on VM %s" , c . AttachSnapshot , c . VMName ) )
} else if 1 < len ( snapshots ) {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "Multiple Snapshots with name %s exist on VM %s" , c . AttachSnapshot , c . VMName ) )
} else {
attachSnapshot = snapshots [ 0 ]
}
2019-02-16 08:32:22 -05:00
}
}
2019-03-17 15:50:13 -04:00
if c . TargetSnapshot != "" {
2019-03-30 11:27:18 -04:00
log . Printf ( "Checking configuration target_snapshot [%s]" , c . TargetSnapshot )
if nil == snapshotTree {
log . Printf ( "Currently no snapshots defined in VM %s" , c . VMName )
} else {
2019-07-26 05:36:05 -04:00
if c . TargetSnapshot == attachSnapshot . Name {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "Target snapshot %s cannot be the same as the snapshot to which the builder shall attach: %s" , c . TargetSnapshot , attachSnapshot . Name ) )
2019-03-30 11:27:18 -04:00
} else {
2019-07-26 05:36:05 -04:00
snapshots := snapshotTree . GetSnapshotsByName ( c . TargetSnapshot )
if 0 < len ( snapshots ) {
if nil == attachSnapshot {
panic ( "Internal error. Expecting a handle to a VBoxSnapshot" )
}
isChild := false
for _ , snapshot := range snapshots {
log . Printf ( "Checking if target snaphot %s/%s is child of %s/%s" , snapshot . Name , snapshot . UUID , attachSnapshot . Name , attachSnapshot . UUID )
isChild = nil != snapshot . Parent && snapshot . Parent . UUID == attachSnapshot . UUID
}
if ! isChild {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "Target snapshot %s already exists and is not a direct child of %s" , c . TargetSnapshot , attachSnapshot . Name ) )
} else if ! c . DeleteTargetSnapshot {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "Target snapshot %s already exists as direct child of %s for VM %s. Use force_delete_snapshot = true to overwrite snapshot" ,
c . TargetSnapshot ,
attachSnapshot . Name ,
c . VMName ) )
}
} else {
log . Printf ( "No snapshot with name %s defined in VM %s" , c . TargetSnapshot , c . VMName )
}
2019-03-10 10:38:38 -04:00
}
2019-02-16 08:32:22 -05:00
}
}
}
}
// Check for any errors.
if errs != nil && len ( errs . Errors ) > 0 {
2019-12-17 05:25:56 -05:00
return warnings , errs
2019-02-16 08:32:22 -05:00
}
2019-12-17 05:25:56 -05:00
return warnings , nil
2019-02-16 08:32:22 -05:00
}