2020-05-08 15:36:00 -04:00
//go:generate struct-markdown
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-10-31 10:49:34 -04:00
"time"
2019-02-16 08:32:22 -05:00
vboxcommon "github.com/hashicorp/packer/builder/virtualbox/common"
2020-11-12 17:44:02 -05:00
"github.com/hashicorp/packer/packer-plugin-sdk/bootcommand"
"github.com/hashicorp/packer/packer-plugin-sdk/common"
2020-11-17 19:31:03 -05:00
"github.com/hashicorp/packer/packer-plugin-sdk/multistep/commonsteps"
2020-11-19 15:07:02 -05:00
packersdk "github.com/hashicorp/packer/packer-plugin-sdk/packer"
2020-11-18 13:34:59 -05:00
"github.com/hashicorp/packer/packer-plugin-sdk/template/config"
2020-11-11 13:21:37 -05:00
"github.com/hashicorp/packer/packer-plugin-sdk/template/interpolate"
2019-02-16 08:32:22 -05:00
)
// Config is the configuration structure for the builder.
type Config struct {
2020-09-11 17:15:17 -04:00
common . PackerConfig ` mapstructure:",squash" `
2020-11-11 18:04:28 -05:00
commonsteps . HTTPConfig ` mapstructure:",squash" `
commonsteps . FloppyConfig ` mapstructure:",squash" `
commonsteps . CDConfig ` mapstructure:",squash" `
2020-09-11 17:15:17 -04:00
bootcommand . BootConfig ` mapstructure:",squash" `
vboxcommon . ExportConfig ` mapstructure:",squash" `
vboxcommon . OutputConfig ` mapstructure:",squash" `
vboxcommon . RunConfig ` mapstructure:",squash" `
vboxcommon . CommConfig ` mapstructure:",squash" `
vboxcommon . ShutdownConfig ` mapstructure:",squash" `
vboxcommon . VBoxManageConfig ` mapstructure:",squash" `
vboxcommon . VBoxVersionConfig ` mapstructure:",squash" `
vboxcommon . GuestAdditionsConfig ` mapstructure:",squash" `
2020-05-08 15:36:00 -04:00
// This is the name of the virtual machine to which the
// builder shall attach.
VMName string ` mapstructure:"vm_name" required:"true" `
// Default to `null/empty`. The name of an
// **existing** snapshot to which the builder shall attach the VM before
// starting it. If no snapshot is specified the builder will simply start the
// VM from it's current state i.e. snapshot.
AttachSnapshot string ` mapstructure:"attach_snapshot" required:"false" `
// Default to `null/empty`. The name of the
// snapshot which shall be created after all provisioners has been run by the
// builder. If no target snapshot is specified and `keep_registered` is set to
// `false` the builder will revert to the snapshot to which the VM was attached
// before the builder has been executed, which will revert all changes applied
// by the provisioners. This is handy if only an export shall be created and no
// further snapshot is required.
TargetSnapshot string ` mapstructure:"target_snapshot" required:"false" `
// Defaults to `false`. If set to `true`,
// overwrite an existing `target_snapshot`. Otherwise the builder will yield an
// error if the specified target snapshot already exists.
DeleteTargetSnapshot bool ` mapstructure:"force_delete_snapshot" required:"false" `
// Set this to `true` if you would like to keep
// the VM attached to the snapshot specified by `attach_snapshot`. Otherwise
// the builder will reset the VM to the snapshot to which the VM was attached
// before the builder started. Defaults to `false`.
KeepRegistered bool ` mapstructure:"keep_registered" required:"false" `
// Defaults to `false`. When enabled, Packer will
// not export the VM. Useful if the builder should be applied again on the created
// target snapshot.
SkipExport bool ` mapstructure:"skip_export" required:"false" `
2019-02-16 08:32:22 -05:00
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 {
2020-10-09 20:01:55 -04:00
PluginType : vboxcommon . BuilderId , // "mitchellh.virtualbox"
2019-02-16 08:32:22 -05:00
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
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
2020-11-20 13:53:16 -05:00
var errs * packersdk . MultiError
errs = packersdk . MultiErrorAppend ( errs , c . ExportConfig . Prepare ( & c . ctx ) ... )
errs = packersdk . MultiErrorAppend ( errs , c . FloppyConfig . Prepare ( & c . ctx ) ... )
errs = packersdk . MultiErrorAppend ( errs , c . CDConfig . Prepare ( & c . ctx ) ... )
errs = packersdk . MultiErrorAppend ( errs , c . HTTPConfig . Prepare ( & c . ctx ) ... )
errs = packersdk . MultiErrorAppend ( errs , c . OutputConfig . Prepare ( & c . ctx , & c . PackerConfig ) ... )
errs = packersdk . MultiErrorAppend ( errs , c . RunConfig . Prepare ( & c . ctx ) ... )
errs = packersdk . MultiErrorAppend ( errs , c . ShutdownConfig . Prepare ( & c . ctx ) ... )
errs = packersdk . MultiErrorAppend ( errs , c . CommConfig . Prepare ( & c . ctx ) ... )
errs = packersdk . MultiErrorAppend ( errs , c . VBoxManageConfig . Prepare ( & c . ctx ) ... )
errs = packersdk . MultiErrorAppend ( errs , c . VBoxVersionConfig . Prepare ( c . CommConfig . Comm . Type ) ... )
errs = packersdk . MultiErrorAppend ( errs , c . BootConfig . Prepare ( & c . ctx ) ... )
errs = packersdk . MultiErrorAppend ( errs , c . GuestAdditionsConfig . Prepare ( c . CommConfig . Comm . Type ) ... )
2020-09-11 17:15:17 -04:00
if c . GuestAdditionsInterface == "" {
c . GuestAdditionsInterface = "ide"
}
2019-02-16 08:32:22 -05:00
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 == "" {
2020-11-19 15:07:02 -05:00
errs = packersdk . MultiErrorAppend ( errs ,
2019-02-16 08:32:22 -05:00
fmt . Errorf ( "vm_name is required" ) )
}
// 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 {
2020-11-19 15:07:02 -05:00
errs = packersdk . MultiErrorAppend ( errs , fmt . Errorf ( "Failed creating VirtualBox driver: %s" , err ) )
2019-02-16 08:32:22 -05:00
} else {
2019-03-30 11:27:18 -04:00
if c . AttachSnapshot != "" && c . TargetSnapshot != "" && c . AttachSnapshot == c . TargetSnapshot {
2020-11-19 15:07:02 -05:00
errs = packersdk . MultiErrorAppend ( errs , fmt . Errorf ( "Attach snapshot %s and target snapshot %s cannot be the same" , c . AttachSnapshot , c . TargetSnapshot ) )
2019-03-30 11:27:18 -04:00
}
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 {
2020-11-19 15:07:02 -05:00
errs = packersdk . 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 {
2020-11-19 15:07:02 -05:00
errs = packersdk . 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 ) {
2020-11-19 15:07:02 -05:00
errs = packersdk . MultiErrorAppend ( errs , fmt . Errorf ( "Snapshot %s does not exist on VM %s" , c . AttachSnapshot , c . VMName ) )
2019-03-30 11:27:18 -04:00
} else if 1 < len ( snapshots ) {
2020-11-19 15:07:02 -05:00
errs = packersdk . MultiErrorAppend ( errs , fmt . Errorf ( "Multiple Snapshots with name %s exist on VM %s" , c . AttachSnapshot , c . VMName ) )
2019-03-30 11:27:18 -04:00
} 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 {
2020-11-19 15:07:02 -05:00
errs = packersdk . 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 {
2020-11-19 15:07:02 -05:00
errs = packersdk . MultiErrorAppend ( errs , fmt . Errorf ( "Target snapshot %s already exists and is not a direct child of %s" , c . TargetSnapshot , attachSnapshot . Name ) )
2019-07-26 05:36:05 -04:00
} else if ! c . DeleteTargetSnapshot {
2020-11-19 15:07:02 -05:00
errs = packersdk . 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" ,
2019-07-26 05:36:05 -04:00
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
}