packer-plugin-xenserver/builder/xenserver/iso/builder.go
somerandomqaguy 6b0b4baf2f Remove iso_checksum_type checks
This aligns the Xenserver plugin to being a bit more inline with what
Packer > 1.6.0 is expecting, since packer now simply ignores the
iso_checksum_type (it's supposed to error out but that code path isn't
working right now because we don't set PluginType in the configs. The
unit tests have been altered to reflect this reality.

Note that this isn't a comprehensive change; the config still has the
inert ISOChecksumType, and there's probably a laundry list of other
things that needs to be looked at, For now though, we have working
unit tests again.

Documentation from SDK has been aligned for iso_checksum
2023-04-06 23:12:25 -07:00

327 lines
8.4 KiB
Go

package iso
import (
"context"
"errors"
"fmt"
"path"
"strings"
"time"
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer-plugin-sdk/communicator"
"github.com/hashicorp/packer-plugin-sdk/multistep"
commonsteps "github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
"github.com/hashicorp/packer-plugin-sdk/packer"
hconfig "github.com/hashicorp/packer-plugin-sdk/template/config"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
xsclient "github.com/terra-farm/go-xen-api-client"
xscommon "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common"
)
type Builder struct {
config xscommon.Config
runner multistep.Runner
}
func (self *Builder) ConfigSpec() hcldec.ObjectSpec { return self.config.FlatMapstructure().HCL2Spec() }
func (self *Builder) Prepare(raws ...interface{}) (params []string, warns []string, retErr error) {
var errs *packer.MultiError
err := hconfig.Decode(&self.config, &hconfig.DecodeOpts{
Interpolate: true,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{
"boot_command",
},
},
}, raws...)
if err != nil {
errs = packer.MultiErrorAppend(errs, err)
}
errs = packer.MultiErrorAppend(
errs, self.config.CommonConfig.Prepare(self.config.GetInterpContext(), &self.config.PackerConfig)...)
errs = packer.MultiErrorAppend(errs, self.config.SSHConfig.Prepare(self.config.GetInterpContext())...)
// Set default values
if self.config.RawInstallTimeout == "" {
self.config.RawInstallTimeout = "200m"
}
if self.config.DiskSize == 0 {
self.config.DiskSize = 40000
}
if self.config.VCPUsMax == 0 {
self.config.VCPUsMax = 1
}
if self.config.VCPUsAtStartup == 0 {
self.config.VCPUsAtStartup = 1
}
if self.config.VCPUsAtStartup > self.config.VCPUsMax {
self.config.VCPUsAtStartup = self.config.VCPUsMax
}
if self.config.VMMemory == 0 {
self.config.VMMemory = 1024
}
if self.config.CloneTemplate == "" {
self.config.CloneTemplate = "Other install media"
}
if self.config.Firmware == "" {
self.config.Firmware = "bios"
}
if len(self.config.PlatformArgs) == 0 {
pargs := make(map[string]string)
pargs["viridian"] = "false"
pargs["nx"] = "true"
pargs["pae"] = "true"
pargs["apic"] = "true"
pargs["timeoffset"] = "0"
pargs["acpi"] = "1"
self.config.PlatformArgs = pargs
}
// Template substitution
templates := map[string]*string{
"clone_template": &self.config.CloneTemplate,
"iso_checksum": &self.config.ISOChecksum,
"iso_checksum_type": &self.config.ISOChecksumType,
"iso_url": &self.config.ISOUrl,
"iso_name": &self.config.ISOName,
"install_timeout": &self.config.RawInstallTimeout,
}
for i := range self.config.ISOUrls {
templates[fmt.Sprintf("iso_urls[%d]", i)] = &self.config.ISOUrls[i]
}
// Validation
self.config.InstallTimeout, err = time.ParseDuration(self.config.RawInstallTimeout)
if err != nil {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("Failed to parse install_timeout: %s", err))
}
if self.config.ISOName == "" {
// If ISO name is not specified, assume a URL and checksum has been provided.
self.config.ISOChecksum = strings.ToLower(self.config.ISOChecksum)
if len(self.config.ISOUrls) == 0 {
if self.config.ISOUrl == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("One of iso_url or iso_urls must be specified."))
} else {
self.config.ISOUrls = []string{self.config.ISOUrl}
}
} else if self.config.ISOUrl != "" {
errs = packer.MultiErrorAppend(
errs, errors.New("Only one of iso_url or iso_urls may be specified."))
}
//The SDK can validate the ISO checksum and other sanity checks on the url.
iso_config := commonsteps.ISOConfig {
ISOChecksum: self.config.ISOChecksum,
ISOUrls: self.config.ISOUrls,
}
_, iso_errs := iso_config.Prepare(nil)
if iso_errs != nil {
for _, this_err := range iso_errs {
errs = packer.MultiErrorAppend(errs, this_err)
}
}
} else {
// An ISO name has been provided. It should be attached from an available SR.
}
if len(errs.Errors) > 0 {
retErr = errors.New(errs.Error())
}
return nil, nil, retErr
}
func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
c, err := xscommon.NewXenAPIClient(self.config.HostIp, self.config.Username, self.config.Password)
if err != nil {
return nil, err
}
ui.Say("XAPI client session established")
c.GetClient().Host.GetAll(c.GetSessionRef())
//Share state between the other steps using a statebag
state := new(multistep.BasicStateBag)
state.Put("client", c)
state.Put("config", self.config)
state.Put("commonconfig", self.config.CommonConfig)
state.Put("hook", hook)
state.Put("ui", ui)
httpReqChan := make(chan string, 1)
//Build the steps
download_steps := []multistep.Step{
&commonsteps.StepDownload{
Checksum: self.config.ISOChecksum,
Description: "ISO",
ResultKey: "iso_path",
Url: self.config.ISOUrls,
},
}
steps := []multistep.Step{
&xscommon.StepPrepareOutputDir{
Force: self.config.PackerForce,
Path: self.config.OutputDir,
},
&commonsteps.StepCreateFloppy{
Files: self.config.FloppyFiles,
Label: "cidata",
},
&xscommon.StepHTTPServer{
Chan: httpReqChan,
},
&xscommon.StepUploadVdi{
VdiNameFunc: func() string {
return "Packer-floppy-disk"
},
ImagePathFunc: func() string {
if floppyPath, ok := state.GetOk("floppy_path"); ok {
return floppyPath.(string)
}
return ""
},
VdiUuidKey: "floppy_vdi_uuid",
},
&xscommon.StepFindOrUploadVdi{
xscommon.StepUploadVdi{
VdiNameFunc: func() string {
if len(self.config.ISOUrls) > 0 {
return path.Base(self.config.ISOUrls[0])
}
return ""
},
ImagePathFunc: func() string {
if isoPath, ok := state.GetOk("iso_path"); ok {
return isoPath.(string)
}
return ""
},
VdiUuidKey: "iso_vdi_uuid",
},
},
&xscommon.StepFindVdi{
VdiName: self.config.ToolsIsoName,
VdiUuidKey: "tools_vdi_uuid",
},
&xscommon.StepFindVdi{
VdiName: self.config.ISOName,
VdiUuidKey: "isoname_vdi_uuid",
},
new(stepCreateInstance),
&xscommon.StepAttachVdi{
VdiUuidKey: "floppy_vdi_uuid",
VdiType: xsclient.VbdTypeFloppy,
},
&xscommon.StepAttachVdi{
VdiUuidKey: "iso_vdi_uuid",
VdiType: xsclient.VbdTypeCD,
},
&xscommon.StepAttachVdi{
VdiUuidKey: "isoname_vdi_uuid",
VdiType: xsclient.VbdTypeCD,
},
&xscommon.StepAttachVdi{
VdiUuidKey: "tools_vdi_uuid",
VdiType: xsclient.VbdTypeCD,
},
new(xscommon.StepStartVmPaused),
new(xscommon.StepSetVmHostSshAddress),
// &xscommon.StepForwardPortOverSSH{
// RemotePort: xscommon.InstanceVNCPort,
// RemoteDest: xscommon.InstanceVNCIP,
// HostPortMin: self.config.HostPortMin,
// HostPortMax: self.config.HostPortMax,
// ResultKey: "local_vnc_port",
// },
new(xscommon.StepBootWait),
&xscommon.StepTypeBootCommand{
Ctx: *self.config.GetInterpContext(),
},
&xscommon.StepWaitForIP{
Chan: httpReqChan,
Timeout: self.config.InstallTimeout, // @todo change this
},
&xscommon.StepForwardPortOverSSH{
RemotePort: xscommon.InstanceSSHPort,
RemoteDest: xscommon.InstanceSSHIP,
HostPortMin: self.config.HostPortMin,
HostPortMax: self.config.HostPortMax,
ResultKey: "local_ssh_port",
},
&communicator.StepConnect{
Config: &self.config.SSHConfig.Comm,
Host: xscommon.InstanceSSHIP,
SSHConfig: self.config.Comm.SSHConfigFunc(),
SSHPort: xscommon.InstanceSSHPort,
},
new(commonsteps.StepProvision),
new(xscommon.StepShutdown),
new(xscommon.StepSetVmToTemplate),
&xscommon.StepDetachVdi{
VdiUuidKey: "iso_vdi_uuid",
},
&xscommon.StepDetachVdi{
VdiUuidKey: "isoname_vdi_uuid",
},
&xscommon.StepDetachVdi{
VdiUuidKey: "tools_vdi_uuid",
},
&xscommon.StepDetachVdi{
VdiUuidKey: "floppy_vdi_uuid",
},
new(xscommon.StepExport),
}
if self.config.ISOName == "" {
steps = append(download_steps, steps...)
}
self.runner = &multistep.BasicRunner{Steps: steps}
self.runner.Run(ctx, state)
if rawErr, ok := state.GetOk("error"); ok {
return nil, rawErr.(error)
}
// If we were interrupted or cancelled, then just exit.
if _, ok := state.GetOk(multistep.StateCancelled); ok {
return nil, errors.New("Build was cancelled.")
}
if _, ok := state.GetOk(multistep.StateHalted); ok {
return nil, errors.New("Build was halted.")
}
artifact, _ := xscommon.NewArtifact(self.config.OutputDir)
return artifact, nil
}