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