github.com/ttysteale/packer@v0.8.2-0.20150708160520-e5f8ea386ed8/builder/parallels/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  	parallelscommon "github.com/mitchellh/packer/builder/parallels/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 = "rickard-von-essen.parallels"
    19  
    20  type Builder struct {
    21  	config Config
    22  	runner multistep.Runner
    23  }
    24  
    25  type Config struct {
    26  	common.PackerConfig                 `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  	ISOChecksum        string   `mapstructure:"iso_checksum"`
    46  	ISOChecksumType    string   `mapstructure:"iso_checksum_type"`
    47  	ISOUrls            []string `mapstructure:"iso_urls"`
    48  	VMName             string   `mapstructure:"vm_name"`
    49  
    50  	RawSingleISOUrl string `mapstructure:"iso_url"`
    51  
    52  	// Deprecated parameters
    53  	GuestOSDistribution    string `mapstructure:"guest_os_distribution"`
    54  	ParallelsToolsHostPath string `mapstructure:"parallels_tools_host_path"`
    55  
    56  	ctx interpolate.Context
    57  }
    58  
    59  func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
    60  	err := config.Decode(&b.config, &config.DecodeOpts{
    61  		Interpolate:        true,
    62  		InterpolateContext: &b.config.ctx,
    63  		InterpolateFilter: &interpolate.RenderFilter{
    64  			Exclude: []string{
    65  				"boot_command",
    66  				"prlctl",
    67  				"parallel_tools_guest_path",
    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  	errs = packer.MultiErrorAppend(errs, b.config.FloppyConfig.Prepare(&b.config.ctx)...)
    78  	errs = packer.MultiErrorAppend(
    79  		errs, b.config.OutputConfig.Prepare(&b.config.ctx, &b.config.PackerConfig)...)
    80  	errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...)
    81  	errs = packer.MultiErrorAppend(errs, b.config.PrlctlConfig.Prepare(&b.config.ctx)...)
    82  	errs = packer.MultiErrorAppend(errs, b.config.PrlctlPostConfig.Prepare(&b.config.ctx)...)
    83  	errs = packer.MultiErrorAppend(errs, b.config.PrlctlVersionConfig.Prepare(&b.config.ctx)...)
    84  	errs = packer.MultiErrorAppend(errs, b.config.ShutdownConfig.Prepare(&b.config.ctx)...)
    85  	errs = packer.MultiErrorAppend(errs, b.config.SSHConfig.Prepare(&b.config.ctx)...)
    86  	errs = packer.MultiErrorAppend(errs, b.config.ToolsConfig.Prepare(&b.config.ctx)...)
    87  	warnings := make([]string, 0)
    88  
    89  	if b.config.DiskSize == 0 {
    90  		b.config.DiskSize = 40000
    91  	}
    92  
    93  	if b.config.HardDriveInterface == "" {
    94  		b.config.HardDriveInterface = "sata"
    95  	}
    96  
    97  	if b.config.GuestOSType == "" {
    98  		b.config.GuestOSType = "other"
    99  	}
   100  
   101  	if b.config.GuestOSDistribution != "" {
   102  		// Compatibility with older templates:
   103  		// Use value of 'guest_os_distribution' if it is defined.
   104  		b.config.GuestOSType = b.config.GuestOSDistribution
   105  		warnings = append(warnings,
   106  			"A 'guest_os_distribution' has been completely replaced with 'guest_os_type'\n"+
   107  				"It is recommended to remove it and assign the previous value to 'guest_os_type'.\n"+
   108  				"Run it to see all available values: `prlctl create x -d list` ")
   109  	}
   110  
   111  	if b.config.HTTPPortMin == 0 {
   112  		b.config.HTTPPortMin = 8000
   113  	}
   114  
   115  	if b.config.HTTPPortMax == 0 {
   116  		b.config.HTTPPortMax = 9000
   117  	}
   118  
   119  	if len(b.config.HostInterfaces) == 0 {
   120  		b.config.HostInterfaces = []string{"en0", "en1", "en2", "en3", "en4", "en5", "en6", "en7",
   121  			"en8", "en9", "ppp0", "ppp1", "ppp2"}
   122  	}
   123  
   124  	if b.config.VMName == "" {
   125  		b.config.VMName = fmt.Sprintf("packer-%s", b.config.PackerBuildName)
   126  	}
   127  
   128  	if b.config.HardDriveInterface != "ide" && b.config.HardDriveInterface != "sata" && b.config.HardDriveInterface != "scsi" {
   129  		errs = packer.MultiErrorAppend(
   130  			errs, errors.New("hard_drive_interface can only be ide, sata, or scsi"))
   131  	}
   132  
   133  	if b.config.HTTPPortMin > b.config.HTTPPortMax {
   134  		errs = packer.MultiErrorAppend(
   135  			errs, errors.New("http_port_min must be less than http_port_max"))
   136  	}
   137  
   138  	if b.config.ISOChecksumType == "" {
   139  		errs = packer.MultiErrorAppend(
   140  			errs, errors.New("The iso_checksum_type must be specified."))
   141  	} else {
   142  		b.config.ISOChecksumType = strings.ToLower(b.config.ISOChecksumType)
   143  		if b.config.ISOChecksumType != "none" {
   144  			if b.config.ISOChecksum == "" {
   145  				errs = packer.MultiErrorAppend(
   146  					errs, errors.New("Due to large file sizes, an iso_checksum is required"))
   147  			} else {
   148  				b.config.ISOChecksum = strings.ToLower(b.config.ISOChecksum)
   149  			}
   150  
   151  			if h := common.HashForType(b.config.ISOChecksumType); h == nil {
   152  				errs = packer.MultiErrorAppend(
   153  					errs,
   154  					fmt.Errorf("Unsupported checksum type: %s", b.config.ISOChecksumType))
   155  			}
   156  		}
   157  	}
   158  
   159  	if b.config.RawSingleISOUrl == "" && len(b.config.ISOUrls) == 0 {
   160  		errs = packer.MultiErrorAppend(
   161  			errs, errors.New("One of iso_url or iso_urls must be specified."))
   162  	} else if b.config.RawSingleISOUrl != "" && len(b.config.ISOUrls) > 0 {
   163  		errs = packer.MultiErrorAppend(
   164  			errs, errors.New("Only one of iso_url or iso_urls may be specified."))
   165  	} else if b.config.RawSingleISOUrl != "" {
   166  		b.config.ISOUrls = []string{b.config.RawSingleISOUrl}
   167  	}
   168  
   169  	for i, url := range b.config.ISOUrls {
   170  		b.config.ISOUrls[i], err = common.DownloadableURL(url)
   171  		if err != nil {
   172  			errs = packer.MultiErrorAppend(
   173  				errs, fmt.Errorf("Failed to parse iso_url %d: %s", i+1, err))
   174  		}
   175  	}
   176  
   177  	// Warnings
   178  	if b.config.ISOChecksumType == "none" {
   179  		warnings = append(warnings,
   180  			"A checksum type of 'none' was specified. Since ISO files are so big,\n"+
   181  				"a checksum is highly recommended.")
   182  	}
   183  
   184  	if b.config.ShutdownCommand == "" {
   185  		warnings = append(warnings,
   186  			"A shutdown_command was not specified. Without a shutdown command, Packer\n"+
   187  				"will forcibly halt the virtual machine, which may result in data loss.")
   188  	}
   189  
   190  	if b.config.ParallelsToolsHostPath != "" {
   191  		warnings = append(warnings,
   192  			"A 'parallels_tools_host_path' has been deprecated and not in use anymore\n"+
   193  				"You can remove it from your Packer template.")
   194  	}
   195  
   196  	if errs != nil && len(errs.Errors) > 0 {
   197  		return warnings, errs
   198  	}
   199  
   200  	return warnings, nil
   201  }
   202  
   203  func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
   204  	// Create the driver that we'll use to communicate with Parallels
   205  	driver, err := parallelscommon.NewDriver()
   206  	if err != nil {
   207  		return nil, fmt.Errorf("Failed creating Parallels driver: %s", err)
   208  	}
   209  
   210  	steps := []multistep.Step{
   211  		&parallelscommon.StepPrepareParallelsTools{
   212  			ParallelsToolsFlavor: b.config.ParallelsToolsFlavor,
   213  			ParallelsToolsMode:   b.config.ParallelsToolsMode,
   214  		},
   215  		&common.StepDownload{
   216  			Checksum:     b.config.ISOChecksum,
   217  			ChecksumType: b.config.ISOChecksumType,
   218  			Description:  "ISO",
   219  			ResultKey:    "iso_path",
   220  			Url:          b.config.ISOUrls,
   221  		},
   222  		&parallelscommon.StepOutputDir{
   223  			Force: b.config.PackerForce,
   224  			Path:  b.config.OutputDir,
   225  		},
   226  		&common.StepCreateFloppy{
   227  			Files: b.config.FloppyFiles,
   228  		},
   229  		new(stepHTTPServer),
   230  		new(stepCreateVM),
   231  		new(stepCreateDisk),
   232  		new(stepSetBootOrder),
   233  		new(stepAttachISO),
   234  		&parallelscommon.StepAttachParallelsTools{
   235  			ParallelsToolsMode: b.config.ParallelsToolsMode,
   236  		},
   237  		new(parallelscommon.StepAttachFloppy),
   238  		&parallelscommon.StepPrlctl{
   239  			Commands: b.config.Prlctl,
   240  			Ctx:      b.config.ctx,
   241  		},
   242  		&parallelscommon.StepRun{
   243  			BootWait: b.config.BootWait,
   244  			Headless: b.config.Headless, // TODO: migth work on Enterprise Ed.
   245  		},
   246  		&parallelscommon.StepTypeBootCommand{
   247  			BootCommand:    b.config.BootCommand,
   248  			HostInterfaces: b.config.HostInterfaces,
   249  			VMName:         b.config.VMName,
   250  			Ctx:            b.config.ctx,
   251  		},
   252  		&communicator.StepConnect{
   253  			Config:    &b.config.SSHConfig.Comm,
   254  			Host:      parallelscommon.CommHost,
   255  			SSHConfig: parallelscommon.SSHConfigFunc(b.config.SSHConfig),
   256  		},
   257  		&parallelscommon.StepUploadVersion{
   258  			Path: b.config.PrlctlVersionFile,
   259  		},
   260  		&parallelscommon.StepUploadParallelsTools{
   261  			ParallelsToolsFlavor:    b.config.ParallelsToolsFlavor,
   262  			ParallelsToolsGuestPath: b.config.ParallelsToolsGuestPath,
   263  			ParallelsToolsMode:      b.config.ParallelsToolsMode,
   264  			Ctx:                     b.config.ctx,
   265  		},
   266  		new(common.StepProvision),
   267  		&parallelscommon.StepShutdown{
   268  			Command: b.config.ShutdownCommand,
   269  			Timeout: b.config.ShutdownTimeout,
   270  		},
   271  		&parallelscommon.StepPrlctl{
   272  			Commands: b.config.PrlctlPost,
   273  			Ctx:      b.config.ctx,
   274  		},
   275  	}
   276  
   277  	// Setup the state bag
   278  	state := new(multistep.BasicStateBag)
   279  	state.Put("cache", cache)
   280  	state.Put("config", &b.config)
   281  	state.Put("driver", driver)
   282  	state.Put("hook", hook)
   283  	state.Put("ui", ui)
   284  
   285  	// Run
   286  	if b.config.PackerDebug {
   287  		b.runner = &multistep.DebugRunner{
   288  			Steps:   steps,
   289  			PauseFn: common.MultistepDebugFn(ui),
   290  		}
   291  	} else {
   292  		b.runner = &multistep.BasicRunner{Steps: steps}
   293  	}
   294  
   295  	b.runner.Run(state)
   296  
   297  	// If there was an error, return that
   298  	if rawErr, ok := state.GetOk("error"); ok {
   299  		return nil, rawErr.(error)
   300  	}
   301  
   302  	// If we were interrupted or cancelled, then just exit.
   303  	if _, ok := state.GetOk(multistep.StateCancelled); ok {
   304  		return nil, errors.New("Build was cancelled.")
   305  	}
   306  
   307  	if _, ok := state.GetOk(multistep.StateHalted); ok {
   308  		return nil, errors.New("Build was halted.")
   309  	}
   310  
   311  	return parallelscommon.NewArtifact(b.config.OutputDir)
   312  }
   313  
   314  func (b *Builder) Cancel() {
   315  	if b.runner != nil {
   316  		log.Println("Cancelling the step runner...")
   317  		b.runner.Cancel()
   318  	}
   319  }