github.com/mitchellh/packer@v1.3.2/builder/virtualbox/iso/builder.go (about)

     1  package iso
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"log"
     7  	"strings"
     8  
     9  	vboxcommon "github.com/hashicorp/packer/builder/virtualbox/common"
    10  	"github.com/hashicorp/packer/common"
    11  	"github.com/hashicorp/packer/common/bootcommand"
    12  	"github.com/hashicorp/packer/helper/communicator"
    13  	"github.com/hashicorp/packer/helper/config"
    14  	"github.com/hashicorp/packer/helper/multistep"
    15  	"github.com/hashicorp/packer/packer"
    16  	"github.com/hashicorp/packer/template/interpolate"
    17  )
    18  
    19  const BuilderId = "mitchellh.virtualbox"
    20  
    21  type Builder struct {
    22  	config Config
    23  	runner multistep.Runner
    24  }
    25  
    26  type Config struct {
    27  	common.PackerConfig             `mapstructure:",squash"`
    28  	common.HTTPConfig               `mapstructure:",squash"`
    29  	common.ISOConfig                `mapstructure:",squash"`
    30  	common.FloppyConfig             `mapstructure:",squash"`
    31  	bootcommand.BootConfig          `mapstructure:",squash"`
    32  	vboxcommon.ExportConfig         `mapstructure:",squash"`
    33  	vboxcommon.ExportOpts           `mapstructure:",squash"`
    34  	vboxcommon.OutputConfig         `mapstructure:",squash"`
    35  	vboxcommon.RunConfig            `mapstructure:",squash"`
    36  	vboxcommon.ShutdownConfig       `mapstructure:",squash"`
    37  	vboxcommon.SSHConfig            `mapstructure:",squash"`
    38  	vboxcommon.VBoxManageConfig     `mapstructure:",squash"`
    39  	vboxcommon.VBoxManagePostConfig `mapstructure:",squash"`
    40  	vboxcommon.VBoxVersionConfig    `mapstructure:",squash"`
    41  
    42  	DiskSize               uint   `mapstructure:"disk_size"`
    43  	GuestAdditionsMode     string `mapstructure:"guest_additions_mode"`
    44  	GuestAdditionsPath     string `mapstructure:"guest_additions_path"`
    45  	GuestAdditionsSHA256   string `mapstructure:"guest_additions_sha256"`
    46  	GuestAdditionsURL      string `mapstructure:"guest_additions_url"`
    47  	GuestOSType            string `mapstructure:"guest_os_type"`
    48  	HardDriveDiscard       bool   `mapstructure:"hard_drive_discard"`
    49  	HardDriveInterface     string `mapstructure:"hard_drive_interface"`
    50  	SATAPortCount          int    `mapstructure:"sata_port_count"`
    51  	HardDriveNonrotational bool   `mapstructure:"hard_drive_nonrotational"`
    52  	ISOInterface           string `mapstructure:"iso_interface"`
    53  	KeepRegistered         bool   `mapstructure:"keep_registered"`
    54  	SkipExport             bool   `mapstructure:"skip_export"`
    55  	VMName                 string `mapstructure:"vm_name"`
    56  
    57  	ctx interpolate.Context
    58  }
    59  
    60  func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
    61  	err := config.Decode(&b.config, &config.DecodeOpts{
    62  		Interpolate:        true,
    63  		InterpolateContext: &b.config.ctx,
    64  		InterpolateFilter: &interpolate.RenderFilter{
    65  			Exclude: []string{
    66  				"boot_command",
    67  				"guest_additions_path",
    68  				"guest_additions_url",
    69  				"vboxmanage",
    70  				"vboxmanage_post",
    71  			},
    72  		},
    73  	}, raws...)
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  
    78  	// Accumulate any errors and warnings
    79  	var errs *packer.MultiError
    80  	warnings := make([]string, 0)
    81  
    82  	isoWarnings, isoErrs := b.config.ISOConfig.Prepare(&b.config.ctx)
    83  	warnings = append(warnings, isoWarnings...)
    84  	errs = packer.MultiErrorAppend(errs, isoErrs...)
    85  
    86  	errs = packer.MultiErrorAppend(errs, b.config.ExportConfig.Prepare(&b.config.ctx)...)
    87  	errs = packer.MultiErrorAppend(errs, b.config.ExportOpts.Prepare(&b.config.ctx)...)
    88  	errs = packer.MultiErrorAppend(errs, b.config.FloppyConfig.Prepare(&b.config.ctx)...)
    89  	errs = packer.MultiErrorAppend(
    90  		errs, b.config.OutputConfig.Prepare(&b.config.ctx, &b.config.PackerConfig)...)
    91  	errs = packer.MultiErrorAppend(errs, b.config.HTTPConfig.Prepare(&b.config.ctx)...)
    92  	errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...)
    93  	errs = packer.MultiErrorAppend(errs, b.config.ShutdownConfig.Prepare(&b.config.ctx)...)
    94  	errs = packer.MultiErrorAppend(errs, b.config.SSHConfig.Prepare(&b.config.ctx)...)
    95  	errs = packer.MultiErrorAppend(errs, b.config.VBoxManageConfig.Prepare(&b.config.ctx)...)
    96  	errs = packer.MultiErrorAppend(errs, b.config.VBoxManagePostConfig.Prepare(&b.config.ctx)...)
    97  	errs = packer.MultiErrorAppend(errs, b.config.VBoxVersionConfig.Prepare(&b.config.ctx)...)
    98  	errs = packer.MultiErrorAppend(errs, b.config.BootConfig.Prepare(&b.config.ctx)...)
    99  
   100  	if b.config.DiskSize == 0 {
   101  		b.config.DiskSize = 40000
   102  	}
   103  
   104  	if b.config.GuestAdditionsMode == "" {
   105  		b.config.GuestAdditionsMode = "upload"
   106  	}
   107  
   108  	if b.config.GuestAdditionsPath == "" {
   109  		b.config.GuestAdditionsPath = "VBoxGuestAdditions.iso"
   110  	}
   111  
   112  	if b.config.HardDriveInterface == "" {
   113  		b.config.HardDriveInterface = "ide"
   114  	}
   115  
   116  	if b.config.GuestOSType == "" {
   117  		b.config.GuestOSType = "Other"
   118  	}
   119  
   120  	if b.config.ISOInterface == "" {
   121  		b.config.ISOInterface = "ide"
   122  	}
   123  
   124  	if b.config.VMName == "" {
   125  		b.config.VMName = fmt.Sprintf(
   126  			"packer-%s-%d", b.config.PackerBuildName, interpolate.InitTime.Unix())
   127  	}
   128  
   129  	if b.config.HardDriveInterface != "ide" && b.config.HardDriveInterface != "sata" && b.config.HardDriveInterface != "scsi" {
   130  		errs = packer.MultiErrorAppend(
   131  			errs, errors.New("hard_drive_interface can only be ide, sata, or scsi"))
   132  	}
   133  
   134  	if b.config.SATAPortCount == 0 {
   135  		b.config.SATAPortCount = 1
   136  	}
   137  
   138  	if b.config.SATAPortCount > 30 {
   139  		errs = packer.MultiErrorAppend(
   140  			errs, errors.New("sata_port_count cannot be greater than 30"))
   141  	}
   142  
   143  	if b.config.ISOInterface != "ide" && b.config.ISOInterface != "sata" {
   144  		errs = packer.MultiErrorAppend(
   145  			errs, errors.New("iso_interface can only be ide or sata"))
   146  	}
   147  
   148  	validMode := false
   149  	validModes := []string{
   150  		vboxcommon.GuestAdditionsModeDisable,
   151  		vboxcommon.GuestAdditionsModeAttach,
   152  		vboxcommon.GuestAdditionsModeUpload,
   153  	}
   154  
   155  	for _, mode := range validModes {
   156  		if b.config.GuestAdditionsMode == mode {
   157  			validMode = true
   158  			break
   159  		}
   160  	}
   161  
   162  	if !validMode {
   163  		errs = packer.MultiErrorAppend(errs,
   164  			fmt.Errorf("guest_additions_mode is invalid. Must be one of: %v", validModes))
   165  	}
   166  
   167  	if b.config.GuestAdditionsSHA256 != "" {
   168  		b.config.GuestAdditionsSHA256 = strings.ToLower(b.config.GuestAdditionsSHA256)
   169  	}
   170  
   171  	// Warnings
   172  	if b.config.ShutdownCommand == "" {
   173  		warnings = append(warnings,
   174  			"A shutdown_command was not specified. Without a shutdown command, Packer\n"+
   175  				"will forcibly halt the virtual machine, which may result in data loss.")
   176  	}
   177  
   178  	if errs != nil && len(errs.Errors) > 0 {
   179  		return warnings, errs
   180  	}
   181  
   182  	return warnings, nil
   183  }
   184  
   185  func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
   186  	// Create the driver that we'll use to communicate with VirtualBox
   187  	driver, err := vboxcommon.NewDriver()
   188  	if err != nil {
   189  		return nil, fmt.Errorf("Failed creating VirtualBox driver: %s", err)
   190  	}
   191  
   192  	steps := []multistep.Step{
   193  		&vboxcommon.StepDownloadGuestAdditions{
   194  			GuestAdditionsMode:   b.config.GuestAdditionsMode,
   195  			GuestAdditionsURL:    b.config.GuestAdditionsURL,
   196  			GuestAdditionsSHA256: b.config.GuestAdditionsSHA256,
   197  			Ctx:                  b.config.ctx,
   198  		},
   199  		&common.StepDownload{
   200  			Checksum:     b.config.ISOChecksum,
   201  			ChecksumType: b.config.ISOChecksumType,
   202  			Description:  "ISO",
   203  			Extension:    b.config.TargetExtension,
   204  			ResultKey:    "iso_path",
   205  			TargetPath:   b.config.TargetPath,
   206  			Url:          b.config.ISOUrls,
   207  		},
   208  		&vboxcommon.StepOutputDir{
   209  			Force: b.config.PackerForce,
   210  			Path:  b.config.OutputDir,
   211  		},
   212  		&common.StepCreateFloppy{
   213  			Files:       b.config.FloppyConfig.FloppyFiles,
   214  			Directories: b.config.FloppyConfig.FloppyDirectories,
   215  		},
   216  		&common.StepHTTPServer{
   217  			HTTPDir:     b.config.HTTPDir,
   218  			HTTPPortMin: b.config.HTTPPortMin,
   219  			HTTPPortMax: b.config.HTTPPortMax,
   220  		},
   221  		new(vboxcommon.StepSuppressMessages),
   222  		new(stepCreateVM),
   223  		new(stepCreateDisk),
   224  		new(stepAttachISO),
   225  		&vboxcommon.StepAttachGuestAdditions{
   226  			GuestAdditionsMode: b.config.GuestAdditionsMode,
   227  		},
   228  		&vboxcommon.StepConfigureVRDP{
   229  			VRDPBindAddress: b.config.VRDPBindAddress,
   230  			VRDPPortMin:     b.config.VRDPPortMin,
   231  			VRDPPortMax:     b.config.VRDPPortMax,
   232  		},
   233  		new(vboxcommon.StepAttachFloppy),
   234  		&vboxcommon.StepForwardSSH{
   235  			CommConfig:     &b.config.SSHConfig.Comm,
   236  			HostPortMin:    b.config.SSHHostPortMin,
   237  			HostPortMax:    b.config.SSHHostPortMax,
   238  			SkipNatMapping: b.config.SSHSkipNatMapping,
   239  		},
   240  		&vboxcommon.StepVBoxManage{
   241  			Commands: b.config.VBoxManage,
   242  			Ctx:      b.config.ctx,
   243  		},
   244  		&vboxcommon.StepRun{
   245  			Headless: b.config.Headless,
   246  		},
   247  		&vboxcommon.StepTypeBootCommand{
   248  			BootWait:      b.config.BootWait,
   249  			BootCommand:   b.config.FlatBootCommand(),
   250  			VMName:        b.config.VMName,
   251  			Ctx:           b.config.ctx,
   252  			GroupInterval: b.config.BootConfig.BootGroupInterval,
   253  		},
   254  		&communicator.StepConnect{
   255  			Config:    &b.config.SSHConfig.Comm,
   256  			Host:      vboxcommon.CommHost(b.config.SSHConfig.Comm.SSHHost),
   257  			SSHConfig: b.config.SSHConfig.Comm.SSHConfigFunc(),
   258  			SSHPort:   vboxcommon.SSHPort,
   259  			WinRMPort: vboxcommon.SSHPort,
   260  		},
   261  		&vboxcommon.StepUploadVersion{
   262  			Path: *b.config.VBoxVersionFile,
   263  		},
   264  		&vboxcommon.StepUploadGuestAdditions{
   265  			GuestAdditionsMode: b.config.GuestAdditionsMode,
   266  			GuestAdditionsPath: b.config.GuestAdditionsPath,
   267  			Ctx:                b.config.ctx,
   268  		},
   269  		new(common.StepProvision),
   270  		&common.StepCleanupTempKeys{
   271  			Comm: &b.config.SSHConfig.Comm,
   272  		},
   273  		&vboxcommon.StepShutdown{
   274  			Command: b.config.ShutdownCommand,
   275  			Timeout: b.config.ShutdownTimeout,
   276  			Delay:   b.config.PostShutdownDelay,
   277  		},
   278  		new(vboxcommon.StepRemoveDevices),
   279  		&vboxcommon.StepVBoxManage{
   280  			Commands: b.config.VBoxManagePost,
   281  			Ctx:      b.config.ctx,
   282  		},
   283  		&vboxcommon.StepExport{
   284  			Format:         b.config.Format,
   285  			OutputDir:      b.config.OutputDir,
   286  			ExportOpts:     b.config.ExportOpts.ExportOpts,
   287  			SkipNatMapping: b.config.SSHSkipNatMapping,
   288  			SkipExport:     b.config.SkipExport,
   289  		},
   290  	}
   291  
   292  	// Setup the state bag
   293  	state := new(multistep.BasicStateBag)
   294  	state.Put("cache", cache)
   295  	state.Put("config", &b.config)
   296  	state.Put("debug", b.config.PackerDebug)
   297  	state.Put("driver", driver)
   298  	state.Put("hook", hook)
   299  	state.Put("ui", ui)
   300  
   301  	// Run
   302  	b.runner = common.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state)
   303  	b.runner.Run(state)
   304  
   305  	// If there was an error, return that
   306  	if rawErr, ok := state.GetOk("error"); ok {
   307  		return nil, rawErr.(error)
   308  	}
   309  
   310  	// If we were interrupted or cancelled, then just exit.
   311  	if _, ok := state.GetOk(multistep.StateCancelled); ok {
   312  		return nil, errors.New("Build was cancelled.")
   313  	}
   314  
   315  	if _, ok := state.GetOk(multistep.StateHalted); ok {
   316  		return nil, errors.New("Build was halted.")
   317  	}
   318  
   319  	return vboxcommon.NewArtifact(b.config.OutputDir)
   320  }
   321  
   322  func (b *Builder) Cancel() {
   323  	if b.runner != nil {
   324  		log.Println("Cancelling the step runner...")
   325  		b.runner.Cancel()
   326  	}
   327  }