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