github.com/kimor79/packer@v0.8.7-0.20151221212622-d507b18eb4cf/builder/parallels/iso/builder.go (about)

     1  package iso
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"log"
     7  
     8  	"github.com/mitchellh/multistep"
     9  	parallelscommon "github.com/mitchellh/packer/builder/parallels/common"
    10  	"github.com/mitchellh/packer/common"
    11  	"github.com/mitchellh/packer/helper/communicator"
    12  	"github.com/mitchellh/packer/helper/config"
    13  	"github.com/mitchellh/packer/packer"
    14  	"github.com/mitchellh/packer/template/interpolate"
    15  )
    16  
    17  const BuilderId = "rickard-von-essen.parallels"
    18  
    19  type Builder struct {
    20  	config Config
    21  	runner multistep.Runner
    22  }
    23  
    24  type Config struct {
    25  	common.PackerConfig                 `mapstructure:",squash"`
    26  	common.ISOConfig                    `mapstructure:",squash"`
    27  	parallelscommon.FloppyConfig        `mapstructure:",squash"`
    28  	parallelscommon.OutputConfig        `mapstructure:",squash"`
    29  	parallelscommon.PrlctlConfig        `mapstructure:",squash"`
    30  	parallelscommon.PrlctlPostConfig    `mapstructure:",squash"`
    31  	parallelscommon.PrlctlVersionConfig `mapstructure:",squash"`
    32  	parallelscommon.RunConfig           `mapstructure:",squash"`
    33  	parallelscommon.ShutdownConfig      `mapstructure:",squash"`
    34  	parallelscommon.SSHConfig           `mapstructure:",squash"`
    35  	parallelscommon.ToolsConfig         `mapstructure:",squash"`
    36  
    37  	BootCommand        []string `mapstructure:"boot_command"`
    38  	DiskSize           uint     `mapstructure:"disk_size"`
    39  	GuestOSType        string   `mapstructure:"guest_os_type"`
    40  	HardDriveInterface string   `mapstructure:"hard_drive_interface"`
    41  	HostInterfaces     []string `mapstructure:"host_interfaces"`
    42  	HTTPDir            string   `mapstructure:"http_directory"`
    43  	HTTPPortMin        uint     `mapstructure:"http_port_min"`
    44  	HTTPPortMax        uint     `mapstructure:"http_port_max"`
    45  	SkipCompaction     bool     `mapstructure:"skip_compaction"`
    46  	VMName             string   `mapstructure:"vm_name"`
    47  
    48  	// Deprecated parameters
    49  	GuestOSDistribution    string `mapstructure:"guest_os_distribution"`
    50  	ParallelsToolsHostPath string `mapstructure:"parallels_tools_host_path"`
    51  
    52  	ctx interpolate.Context
    53  }
    54  
    55  func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
    56  	err := config.Decode(&b.config, &config.DecodeOpts{
    57  		Interpolate:        true,
    58  		InterpolateContext: &b.config.ctx,
    59  		InterpolateFilter: &interpolate.RenderFilter{
    60  			Exclude: []string{
    61  				"boot_command",
    62  				"prlctl",
    63  				"prlctl_post",
    64  				"parallels_tools_guest_path",
    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.FloppyConfig.Prepare(&b.config.ctx)...)
    81  	errs = packer.MultiErrorAppend(
    82  		errs, b.config.OutputConfig.Prepare(&b.config.ctx, &b.config.PackerConfig)...)
    83  	errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...)
    84  	errs = packer.MultiErrorAppend(errs, b.config.PrlctlConfig.Prepare(&b.config.ctx)...)
    85  	errs = packer.MultiErrorAppend(errs, b.config.PrlctlPostConfig.Prepare(&b.config.ctx)...)
    86  	errs = packer.MultiErrorAppend(errs, b.config.PrlctlVersionConfig.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.ToolsConfig.Prepare(&b.config.ctx)...)
    90  
    91  	if b.config.DiskSize == 0 {
    92  		b.config.DiskSize = 40000
    93  	}
    94  
    95  	if b.config.HardDriveInterface == "" {
    96  		b.config.HardDriveInterface = "sata"
    97  	}
    98  
    99  	if b.config.GuestOSType == "" {
   100  		b.config.GuestOSType = "other"
   101  	}
   102  
   103  	if b.config.GuestOSDistribution != "" {
   104  		// Compatibility with older templates:
   105  		// Use value of 'guest_os_distribution' if it is defined.
   106  		b.config.GuestOSType = b.config.GuestOSDistribution
   107  		warnings = append(warnings,
   108  			"A 'guest_os_distribution' has been completely replaced with 'guest_os_type'\n"+
   109  				"It is recommended to remove it and assign the previous value to 'guest_os_type'.\n"+
   110  				"Run it to see all available values: `prlctl create x -d list` ")
   111  	}
   112  
   113  	if b.config.HTTPPortMin == 0 {
   114  		b.config.HTTPPortMin = 8000
   115  	}
   116  
   117  	if b.config.HTTPPortMax == 0 {
   118  		b.config.HTTPPortMax = 9000
   119  	}
   120  
   121  	if len(b.config.HostInterfaces) == 0 {
   122  		b.config.HostInterfaces = []string{"en0", "en1", "en2", "en3", "en4", "en5", "en6", "en7",
   123  			"en8", "en9", "ppp0", "ppp1", "ppp2"}
   124  	}
   125  
   126  	if b.config.VMName == "" {
   127  		b.config.VMName = fmt.Sprintf("packer-%s", b.config.PackerBuildName)
   128  	}
   129  
   130  	if b.config.HardDriveInterface != "ide" && b.config.HardDriveInterface != "sata" && b.config.HardDriveInterface != "scsi" {
   131  		errs = packer.MultiErrorAppend(
   132  			errs, errors.New("hard_drive_interface can only be ide, sata, or scsi"))
   133  	}
   134  
   135  	if b.config.HTTPPortMin > b.config.HTTPPortMax {
   136  		errs = packer.MultiErrorAppend(
   137  			errs, errors.New("http_port_min must be less than http_port_max"))
   138  	}
   139  
   140  	// Warnings
   141  	if b.config.ShutdownCommand == "" {
   142  		warnings = append(warnings,
   143  			"A shutdown_command was not specified. Without a shutdown command, Packer\n"+
   144  				"will forcibly halt the virtual machine, which may result in data loss.")
   145  	}
   146  
   147  	if b.config.ParallelsToolsHostPath != "" {
   148  		warnings = append(warnings,
   149  			"A 'parallels_tools_host_path' has been deprecated and not in use anymore\n"+
   150  				"You can remove it from your Packer template.")
   151  	}
   152  
   153  	if errs != nil && len(errs.Errors) > 0 {
   154  		return warnings, errs
   155  	}
   156  
   157  	return warnings, nil
   158  }
   159  
   160  func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
   161  	// Create the driver that we'll use to communicate with Parallels
   162  	driver, err := parallelscommon.NewDriver()
   163  	if err != nil {
   164  		return nil, fmt.Errorf("Failed creating Parallels driver: %s", err)
   165  	}
   166  
   167  	steps := []multistep.Step{
   168  		&parallelscommon.StepPrepareParallelsTools{
   169  			ParallelsToolsFlavor: b.config.ParallelsToolsFlavor,
   170  			ParallelsToolsMode:   b.config.ParallelsToolsMode,
   171  		},
   172  		&common.StepDownload{
   173  			Checksum:     b.config.ISOChecksum,
   174  			ChecksumType: b.config.ISOChecksumType,
   175  			Description:  "ISO",
   176  			Extension:    "iso",
   177  			ResultKey:    "iso_path",
   178  			TargetPath:   b.config.TargetPath,
   179  			Url:          b.config.ISOUrls,
   180  		},
   181  		&parallelscommon.StepOutputDir{
   182  			Force: b.config.PackerForce,
   183  			Path:  b.config.OutputDir,
   184  		},
   185  		&common.StepCreateFloppy{
   186  			Files: b.config.FloppyFiles,
   187  		},
   188  		new(stepHTTPServer),
   189  		new(stepCreateVM),
   190  		new(stepCreateDisk),
   191  		new(stepSetBootOrder),
   192  		new(stepAttachISO),
   193  		&parallelscommon.StepAttachParallelsTools{
   194  			ParallelsToolsMode: b.config.ParallelsToolsMode,
   195  		},
   196  		new(parallelscommon.StepAttachFloppy),
   197  		&parallelscommon.StepPrlctl{
   198  			Commands: b.config.Prlctl,
   199  			Ctx:      b.config.ctx,
   200  		},
   201  		&parallelscommon.StepRun{
   202  			BootWait: b.config.BootWait,
   203  		},
   204  		&parallelscommon.StepTypeBootCommand{
   205  			BootCommand:    b.config.BootCommand,
   206  			HostInterfaces: b.config.HostInterfaces,
   207  			VMName:         b.config.VMName,
   208  			Ctx:            b.config.ctx,
   209  		},
   210  		&communicator.StepConnect{
   211  			Config:    &b.config.SSHConfig.Comm,
   212  			Host:      parallelscommon.CommHost,
   213  			SSHConfig: parallelscommon.SSHConfigFunc(b.config.SSHConfig),
   214  		},
   215  		&parallelscommon.StepUploadVersion{
   216  			Path: b.config.PrlctlVersionFile,
   217  		},
   218  		&parallelscommon.StepUploadParallelsTools{
   219  			ParallelsToolsFlavor:    b.config.ParallelsToolsFlavor,
   220  			ParallelsToolsGuestPath: b.config.ParallelsToolsGuestPath,
   221  			ParallelsToolsMode:      b.config.ParallelsToolsMode,
   222  			Ctx:                     b.config.ctx,
   223  		},
   224  		new(common.StepProvision),
   225  		&parallelscommon.StepShutdown{
   226  			Command: b.config.ShutdownCommand,
   227  			Timeout: b.config.ShutdownTimeout,
   228  		},
   229  		&parallelscommon.StepPrlctl{
   230  			Commands: b.config.PrlctlPost,
   231  			Ctx:      b.config.ctx,
   232  		},
   233  		&parallelscommon.StepCompactDisk{
   234  			Skip: b.config.SkipCompaction,
   235  		},
   236  	}
   237  
   238  	// Setup the state bag
   239  	state := new(multistep.BasicStateBag)
   240  	state.Put("cache", cache)
   241  	state.Put("config", &b.config)
   242  	state.Put("driver", driver)
   243  	state.Put("hook", hook)
   244  	state.Put("ui", ui)
   245  
   246  	// Run
   247  	if b.config.PackerDebug {
   248  		b.runner = &multistep.DebugRunner{
   249  			Steps:   steps,
   250  			PauseFn: common.MultistepDebugFn(ui),
   251  		}
   252  	} else {
   253  		b.runner = &multistep.BasicRunner{Steps: steps}
   254  	}
   255  
   256  	b.runner.Run(state)
   257  
   258  	// If there was an error, return that
   259  	if rawErr, ok := state.GetOk("error"); ok {
   260  		return nil, rawErr.(error)
   261  	}
   262  
   263  	// If we were interrupted or cancelled, then just exit.
   264  	if _, ok := state.GetOk(multistep.StateCancelled); ok {
   265  		return nil, errors.New("Build was cancelled.")
   266  	}
   267  
   268  	if _, ok := state.GetOk(multistep.StateHalted); ok {
   269  		return nil, errors.New("Build was halted.")
   270  	}
   271  
   272  	return parallelscommon.NewArtifact(b.config.OutputDir)
   273  }
   274  
   275  func (b *Builder) Cancel() {
   276  	if b.runner != nil {
   277  		log.Println("Cancelling the step runner...")
   278  		b.runner.Cancel()
   279  	}
   280  }