github.com/rothwerx/packer@v0.9.0/builder/virtualbox/iso/builder.go (about)

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