Merge branch 'master' of https://github.com/ddelnano/packer-plugin-xenserver into destroy-vifs

This commit is contained in:
Heinrich Kruger 2023-04-21 11:05:36 +01:00
commit 30400a41a0
No known key found for this signature in database
GPG Key ID: 05A6F0214B1C419D
20 changed files with 369 additions and 235 deletions

6
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "gomod" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "daily"

24
.github/workflows/go-test.yml vendored Normal file
View File

@ -0,0 +1,24 @@
name: go-test
on:
push:
branches:
- 'main'
pull_request:
permissions:
contents: read
jobs:
go-tests:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Unshallow
run: git fetch --prune --unshallow
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.16
- name: Run go tests
run: go test -race -count 1 ./... -timeout=3m

View File

@ -25,16 +25,16 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.17
go-version: 1.16
- name: Describe plugin
id: plugin_describe
run: echo "::set-output name=api_version::$(go run . describe | jq -r '.api_version')"
- name: Import GPG key
id: import_gpg
uses: hashicorp/ghaction-import-gpg@v2.1.0
env:
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
uses: crazy-max/ghaction-import-gpg@v5.0.0
with:
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
passphrase: ${{ secrets.GPG_PASSPHRASE }}
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
with:

View File

@ -62,7 +62,7 @@ $ cp builder-xenserver-iso ~/.packer.d/plugins/packer-builder-xenserver-iso
# Documentation
For complete documentation on configuration commands, see [the
xenserver-iso docs](docs/builders/xenserver-iso.html.markdown)
xenserver-iso docs](docs/builders/iso/xenserver-iso.html.markdown)
## Support

View File

@ -624,6 +624,17 @@ func ConnectNetwork(c *Connection, networkRef xenapi.NetworkRef, vmRef xenapi.VM
return &vif, nil
}
func AddVMTags(c *Connection, vmRef xenapi.VMRef, tags []string) error {
for _, tag := range tags {
log.Printf("Adding tag %s to VM %s\n", tag, vmRef)
err := c.GetClient().VM.AddTags(c.session, vmRef, tag)
if err != nil {
return err
}
}
return nil
}
// Setters
func (self *VM) SetIsATemplate(is_a_template bool) (err error) {

View File

@ -20,10 +20,11 @@ type CommonConfig struct {
VMName string `mapstructure:"vm_name"`
VMDescription string `mapstructure:"vm_description"`
SrName string `mapstructure:"sr_name"`
SrISOName string `mapstructure:"sr_iso_name"`
SrISOName string `mapstructure:"sr_iso_name" required:"false"`
FloppyFiles []string `mapstructure:"floppy_files"`
NetworkNames []string `mapstructure:"network_names"`
ExportNetworkNames []string `mapstructure:"export_network_names"`
VMTags []string `mapstructure:"vm_tags"`
HostPortMin uint `mapstructure:"host_port_min"`
HostPortMax uint `mapstructure:"host_port_max"`
@ -225,29 +226,11 @@ func (c CommonConfig) ShouldKeepVM(state multistep.StateBag) bool {
}
func (config CommonConfig) GetSR(c *Connection) (xenapi.SRRef, error) {
var srRef xenapi.SRRef
if config.SrName == "" {
hostRef, err := c.GetClient().Session.GetThisHost(c.session, c.session)
if err != nil {
return srRef, err
}
pools, err := c.GetClient().Pool.GetAllRecords(c.session)
if err != nil {
return srRef, err
}
for _, pool := range pools {
if pool.Master == hostRef {
return pool.DefaultSR, nil
}
}
return srRef, errors.New(fmt.Sprintf("failed to find default SR on host '%s'", hostRef))
return getDefaultSR(c)
} else {
var srRef xenapi.SRRef
// Use the provided name label to find the SR to use
srs, err := c.GetClient().SR.GetByNameLabel(c.session, config.SrName)
@ -269,11 +252,11 @@ func (config CommonConfig) GetSR(c *Connection) (xenapi.SRRef, error) {
func (config CommonConfig) GetISOSR(c *Connection) (xenapi.SRRef, error) {
var srRef xenapi.SRRef
if config.SrISOName == "" {
return srRef, errors.New("sr_iso_name must be specified in the packer configuration")
return getDefaultSR(c)
} else {
// Use the provided name label to find the SR to use
srs, err := c.GetClient().SR.GetByNameLabel(c.session, config.SrName)
srs, err := c.GetClient().SR.GetByNameLabel(c.session, config.SrISOName)
if err != nil {
return srRef, err
@ -281,11 +264,42 @@ func (config CommonConfig) GetISOSR(c *Connection) (xenapi.SRRef, error) {
switch {
case len(srs) == 0:
return srRef, fmt.Errorf("Couldn't find a SR with the specified name-label '%s'", config.SrName)
return srRef, fmt.Errorf("Couldn't find a SR with the specified name-label '%s'", config.SrISOName)
case len(srs) > 1:
return srRef, fmt.Errorf("Found more than one SR with the name '%s'. The name must be unique", config.SrName)
return srRef, fmt.Errorf("Found more than one SR with the name '%s'. The name must be unique", config.SrISOName)
}
return srs[0], nil
}
}
func getDefaultSR(c *Connection) (xenapi.SRRef, error) {
var srRef xenapi.SRRef
client := c.GetClient()
hostRef, err := client.Session.GetThisHost(c.session, c.session)
if err != nil {
return srRef, err
}
// The current version of the go-xen-api-client does not fully support XenAPI version 8.2
// In particular, some values for the pool `allowed_operations` are not recognised, resulting
// in a parse error when retrieving pool records. As a workaround, we only fetch pool refs.
pool_refs, err := client.Pool.GetAll(c.session)
if err != nil {
return srRef, err
}
for _, pool_ref := range pool_refs {
pool_master, err := client.Pool.GetMaster(c.session, pool_ref)
if err != nil {
return srRef, err
}
if pool_master == hostRef {
return client.Pool.GetDefaultSR(c.session, pool_ref)
}
}
return srRef, errors.New(fmt.Sprintf("failed to find default SR on host '%s'", hostRef))
}

View File

@ -1,4 +1,4 @@
//go:generate mapstructure-to-hcl2 -type Config
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
package common
import (
@ -20,12 +20,12 @@ type Config struct {
DiskSize uint `mapstructure:"disk_size"`
CloneTemplate string `mapstructure:"clone_template"`
VMOtherConfig map[string]string `mapstructure:"vm_other_config"`
VMTags []string `mapstructure:"vm_tags"`
ISOChecksum string `mapstructure:"iso_checksum"`
ISOChecksumType string `mapstructure:"iso_checksum_type"`
ISOUrls []string `mapstructure:"iso_urls"`
ISOUrl string `mapstructure:"iso_url"`
ISOName string `mapstructure:"iso_name"`
ISOChecksum string `mapstructure:"iso_checksum"`
ISOUrls []string `mapstructure:"iso_urls"`
ISOUrl string `mapstructure:"iso_url"`
ISOName string `mapstructure:"iso_name"`
PlatformArgs map[string]string `mapstructure:"platform_args"`

View File

@ -1,4 +1,4 @@
// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT.
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
package common
@ -12,6 +12,7 @@ import (
type FlatConfig struct {
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
@ -23,10 +24,11 @@ type FlatConfig struct {
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
VMDescription *string `mapstructure:"vm_description" cty:"vm_description" hcl:"vm_description"`
SrName *string `mapstructure:"sr_name" cty:"sr_name" hcl:"sr_name"`
SrISOName *string `mapstructure:"sr_iso_name" cty:"sr_iso_name" hcl:"sr_iso_name"`
SrISOName *string `mapstructure:"sr_iso_name" required:"false" cty:"sr_iso_name" hcl:"sr_iso_name"`
FloppyFiles []string `mapstructure:"floppy_files" cty:"floppy_files" hcl:"floppy_files"`
NetworkNames []string `mapstructure:"network_names" cty:"network_names" hcl:"network_names"`
ExportNetworkNames []string `mapstructure:"export_network_names" cty:"export_network_names" hcl:"export_network_names"`
VMTags []string `mapstructure:"vm_tags" cty:"vm_tags" hcl:"vm_tags"`
HostPortMin *uint `mapstructure:"host_port_min" cty:"host_port_min" hcl:"host_port_min"`
HostPortMax *uint `mapstructure:"host_port_max" cty:"host_port_max" hcl:"host_port_max"`
BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
@ -100,7 +102,6 @@ type FlatConfig struct {
CloneTemplate *string `mapstructure:"clone_template" cty:"clone_template" hcl:"clone_template"`
VMOtherConfig map[string]string `mapstructure:"vm_other_config" cty:"vm_other_config" hcl:"vm_other_config"`
ISOChecksum *string `mapstructure:"iso_checksum" cty:"iso_checksum" hcl:"iso_checksum"`
ISOChecksumType *string `mapstructure:"iso_checksum_type" cty:"iso_checksum_type" hcl:"iso_checksum_type"`
ISOUrls []string `mapstructure:"iso_urls" cty:"iso_urls" hcl:"iso_urls"`
ISOUrl *string `mapstructure:"iso_url" cty:"iso_url" hcl:"iso_url"`
ISOName *string `mapstructure:"iso_name" cty:"iso_name" hcl:"iso_name"`
@ -125,6 +126,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false},
"packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false},
"packer_core_version": &hcldec.AttrSpec{Name: "packer_core_version", Type: cty.String, Required: false},
"packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false},
"packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false},
"packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false},
@ -140,6 +142,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"floppy_files": &hcldec.AttrSpec{Name: "floppy_files", Type: cty.List(cty.String), Required: false},
"network_names": &hcldec.AttrSpec{Name: "network_names", Type: cty.List(cty.String), Required: false},
"export_network_names": &hcldec.AttrSpec{Name: "export_network_names", Type: cty.List(cty.String), Required: false},
"vm_tags": &hcldec.AttrSpec{Name: "vm_tags", Type: cty.List(cty.String), Required: false},
"host_port_min": &hcldec.AttrSpec{Name: "host_port_min", Type: cty.Number, Required: false},
"host_port_max": &hcldec.AttrSpec{Name: "host_port_max", Type: cty.Number, Required: false},
"boot_command": &hcldec.AttrSpec{Name: "boot_command", Type: cty.List(cty.String), Required: false},
@ -213,7 +216,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"clone_template": &hcldec.AttrSpec{Name: "clone_template", Type: cty.String, Required: false},
"vm_other_config": &hcldec.AttrSpec{Name: "vm_other_config", Type: cty.Map(cty.String), Required: false},
"iso_checksum": &hcldec.AttrSpec{Name: "iso_checksum", Type: cty.String, Required: false},
"iso_checksum_type": &hcldec.AttrSpec{Name: "iso_checksum_type", Type: cty.String, Required: false},
"iso_urls": &hcldec.AttrSpec{Name: "iso_urls", Type: cty.List(cty.String), Required: false},
"iso_url": &hcldec.AttrSpec{Name: "iso_url", Type: cty.String, Required: false},
"iso_name": &hcldec.AttrSpec{Name: "iso_name", Type: cty.String, Required: false},

View File

@ -1,4 +1,4 @@
package iso
package common
import (
"context"
@ -9,18 +9,21 @@ import (
"github.com/hashicorp/packer-plugin-sdk/packer"
xenapi "github.com/terra-farm/go-xen-api-client"
xsclient "github.com/terra-farm/go-xen-api-client"
xscommon "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common"
)
type stepCreateInstance struct {
type StepCreateInstance struct {
// The XVA builder assumes it will boot an instance with an OS installed on its disks
// while the ISO builder needs packer to create a disk for an OS to be installed on.
AssumePreInstalledOS bool
instance *xsclient.VMRef
vdi *xsclient.VDIRef
}
func (self *stepCreateInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
func (self *StepCreateInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
c := state.Get("client").(*xscommon.Connection)
config := state.Get("config").(xscommon.Config)
c := state.Get("client").(*Connection)
config := state.Get("config").(Config)
ui := state.Get("ui").(packer.Ui)
ui.Say("Step: Create Instance")
@ -101,37 +104,45 @@ func (self *stepCreateInstance) Run(ctx context.Context, state multistep.StateBa
}
}
// Create VDI for the instance
sr, err := config.GetSR(c)
if !self.AssumePreInstalledOS {
err = c.GetClient().VM.RemoveFromOtherConfig(c.GetSessionRef(), instance, "disks")
if err != nil {
ui.Error(fmt.Sprintf("Error removing disks from VM other-config: %s", err.Error()))
return multistep.ActionHalt
}
if err != nil {
ui.Error(fmt.Sprintf("Unable to get SR: %s", err.Error()))
return multistep.ActionHalt
}
// Create VDI for the instance
sr, err := config.GetSR(c)
ui.Say(fmt.Sprintf("Using the following SR for the VM: %s", sr))
if err != nil {
ui.Error(fmt.Sprintf("Unable to get SR: %s", err.Error()))
return multistep.ActionHalt
}
vdi, err := c.GetClient().VDI.Create(c.GetSessionRef(), xenapi.VDIRecord{
NameLabel: "Packer-disk",
VirtualSize: int(config.DiskSize * 1024 * 1024),
Type: "user",
Sharable: false,
ReadOnly: false,
SR: sr,
OtherConfig: map[string]string{
"temp": "temp",
},
})
if err != nil {
ui.Error(fmt.Sprintf("Unable to create packer disk VDI: %s", err.Error()))
return multistep.ActionHalt
}
self.vdi = &vdi
ui.Say(fmt.Sprintf("Using the following SR for the VM: %s", sr))
err = xscommon.ConnectVdi(c, instance, vdi, xsclient.VbdTypeDisk)
if err != nil {
ui.Error(fmt.Sprintf("Unable to connect packer disk VDI: %s", err.Error()))
return multistep.ActionHalt
vdi, err := c.GetClient().VDI.Create(c.GetSessionRef(), xenapi.VDIRecord{
NameLabel: "Packer-disk",
VirtualSize: int(config.DiskSize * 1024 * 1024),
Type: "user",
Sharable: false,
ReadOnly: false,
SR: sr,
OtherConfig: map[string]string{
"temp": "temp",
},
})
if err != nil {
ui.Error(fmt.Sprintf("Unable to create packer disk VDI: %s", err.Error()))
return multistep.ActionHalt
}
self.vdi = &vdi
err = ConnectVdi(c, instance, vdi, xsclient.VbdTypeDisk)
if err != nil {
ui.Error(fmt.Sprintf("Unable to connect packer disk VDI: %s", err.Error()))
return multistep.ActionHalt
}
}
// Connect Network
@ -168,7 +179,7 @@ func (self *stepCreateInstance) Run(ctx context.Context, state multistep.StateBa
}
log.Printf("Creating VIF on network '%s' on VM '%s'\n", network, instance)
_, err = xscommon.ConnectNetwork(c, network, instance, "0")
_, err = ConnectNetwork(c, network, instance, "0")
if err != nil {
ui.Error(fmt.Sprintf("Failed to create VIF with error: %v", err))
@ -197,7 +208,7 @@ func (self *stepCreateInstance) Run(ctx context.Context, state multistep.StateBa
//we need the VIF index string
vifIndexString := fmt.Sprintf("%d", i)
_, err = xscommon.ConnectNetwork(c, networks[0], instance, vifIndexString)
_, err = ConnectNetwork(c, networks[0], instance, vifIndexString)
if err != nil {
ui.Say(fmt.Sprintf("Failed to connect VIF with error: %v", err.Error()))
@ -205,6 +216,12 @@ func (self *stepCreateInstance) Run(ctx context.Context, state multistep.StateBa
}
}
err = AddVMTags(c, instance, config.VMTags)
if err != nil {
ui.Error(fmt.Sprintf("Failed to add tags: %s", err.Error()))
return multistep.ActionHalt
}
instanceId, err := c.GetClient().VM.GetUUID(c.GetSessionRef(), instance)
if err != nil {
ui.Error(fmt.Sprintf("Unable to get VM UUID: %s", err.Error()))
@ -217,14 +234,14 @@ func (self *stepCreateInstance) Run(ctx context.Context, state multistep.StateBa
return multistep.ActionContinue
}
func (self *stepCreateInstance) Cleanup(state multistep.StateBag) {
config := state.Get("config").(xscommon.Config)
func (self *StepCreateInstance) Cleanup(state multistep.StateBag) {
config := state.Get("config").(Config)
if config.ShouldKeepVM(state) {
return
}
ui := state.Get("ui").(packer.Ui)
c := state.Get("client").(*xscommon.Connection)
c := state.Get("client").(*Connection)
if self.instance != nil {
ui.Say("Destroying VM")

View File

@ -0,0 +1,44 @@
package common
import (
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
"github.com/hashicorp/packer-plugin-sdk/packer"
)
type StepFindOrUploadVdi struct {
StepUploadVdi
}
func (self *StepFindOrUploadVdi) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
c := state.Get("client").(*Connection)
vdiName := self.VdiNameFunc()
ui.Say(fmt.Sprintf("Attemping to find VDI '%s'", vdiName))
vdis, err := c.client.VDI.GetByNameLabel(c.session, vdiName)
if err != nil {
ui.Error(fmt.Sprintf("Failed to find VDI '%s' by name label: %s", vdiName, err.Error()))
return multistep.ActionHalt
}
if len(vdis) > 1 {
ui.Error(fmt.Sprintf("Found more than one VDI with name '%s'. Name must be unique", vdiName))
return multistep.ActionHalt
} else if len(vdis) == 1 {
vdi := vdis[0]
vdiUuid, err := c.client.VDI.GetUUID(c.session, vdi)
if err != nil {
ui.Error(fmt.Sprintf("Unable to get UUID of VDI '%s': %s", vdiName, err.Error()))
return multistep.ActionHalt
}
state.Put(self.VdiUuidKey, vdiUuid)
return multistep.ActionContinue
}
return self.uploadVdi(ctx, state)
}

View File

@ -18,7 +18,7 @@ type StepUploadVdi struct {
VdiUuidKey string
}
func (self *StepUploadVdi) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
func (self *StepUploadVdi) uploadVdi(ctx context.Context, state multistep.StateBag) multistep.StepAction {
config := state.Get("commonconfig").(CommonConfig)
ui := state.Get("ui").(packer.Ui)
c := state.Get("client").(*Connection)
@ -33,10 +33,8 @@ func (self *StepUploadVdi) Run(ctx context.Context, state multistep.StateBag) mu
ui.Say(fmt.Sprintf("Step: Upload VDI '%s'", vdiName))
// Create VDI for the image
srs, err := c.client.SR.GetAll(c.session)
ui.Say(fmt.Sprintf("Step: Found SRs '%v'", srs))
sr, err := config.GetISOSR(c)
ui.Say(fmt.Sprintf("Step: Found SR for upload '%v'", sr))
if err != nil {
ui.Error(fmt.Sprintf("Unable to get SR: %v", err))
@ -96,6 +94,10 @@ func (self *StepUploadVdi) Run(ctx context.Context, state multistep.StateBag) mu
return multistep.ActionContinue
}
func (self *StepUploadVdi) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
return self.uploadVdi(ctx, state)
}
func (self *StepUploadVdi) Cleanup(state multistep.StateBag) {
config := state.Get("commonconfig").(CommonConfig)
ui := state.Get("ui").(packer.Ui)

View File

@ -40,7 +40,7 @@ func (self *Builder) Prepare(raws ...interface{}) (params []string, warns []stri
}, raws...)
if err != nil {
packer.MultiErrorAppend(errs, err)
errs = packer.MultiErrorAppend(errs, err)
}
errs = packer.MultiErrorAppend(
@ -95,12 +95,11 @@ func (self *Builder) Prepare(raws ...interface{}) (params []string, warns []stri
// 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,
"clone_template": &self.config.CloneTemplate,
"iso_checksum": &self.config.ISOChecksum,
"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]
@ -115,23 +114,8 @@ func (self *Builder) Prepare(raws ...interface{}) (params []string, warns []stri
}
if self.config.ISOName == "" {
// If ISO name is not specified, assume a URL and checksum has been provided.
if self.config.ISOChecksumType == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("The iso_checksum_type must be specified."))
} else {
self.config.ISOChecksumType = strings.ToLower(self.config.ISOChecksumType)
if self.config.ISOChecksumType != "none" {
if self.config.ISOChecksum == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("Due to the file size being large, an iso_checksum is required."))
} else {
self.config.ISOChecksum = strings.ToLower(self.config.ISOChecksum)
}
}
}
self.config.ISOChecksum = strings.ToLower(self.config.ISOChecksum)
if len(self.config.ISOUrls) == 0 {
if self.config.ISOUrl == "" {
@ -140,10 +124,25 @@ func (self *Builder) Prepare(raws ...interface{}) (params []string, warns []stri
} 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.
@ -187,7 +186,6 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p
Url: self.config.ISOUrls,
},
}
steps := []multistep.Step{
&xscommon.StepPrepareOutputDir{
Force: self.config.PackerForce,
@ -212,20 +210,22 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p
},
VdiUuidKey: "floppy_vdi_uuid",
},
&xscommon.StepUploadVdi{
VdiNameFunc: func() string {
if len(self.config.ISOUrls) > 0 {
return path.Base(self.config.ISOUrls[0])
}
return ""
&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",
},
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,
@ -235,7 +235,9 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p
VdiName: self.config.ISOName,
VdiUuidKey: "isoname_vdi_uuid",
},
new(stepCreateInstance),
&xscommon.StepCreateInstance{
AssumePreInstalledOS: false,
},
&xscommon.StepAttachVdi{
VdiUuidKey: "floppy_vdi_uuid",
VdiType: xsclient.VbdTypeFloppy,

View File

@ -5,21 +5,22 @@ import (
"testing"
"github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/common"
)
func testConfig() map[string]interface{} {
return map[string]interface{}{
"remote_host": "localhost",
"remote_username": "admin",
"remote_password": "admin",
"vm_name": "foo",
"iso_checksum": "foo",
"iso_checksum_type": "md5",
"iso_checksum": "md5:A221725EE181A44C67E25BD6A2516742",
"iso_url": "http://www.google.com/",
"shutdown_command": "yes",
"ssh_username": "foo",
packer.BuildNameConfigKey: "foo",
common.BuildNameConfigKey: "foo",
}
}
@ -179,9 +180,20 @@ func TestBuilderPrepare_ISOChecksum(t *testing.T) {
var b Builder
config := testConfig()
// Test good
b = Builder{}
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
// Test bad
config["iso_checksum"] = ""
_, warns, err := b.Prepare(config)
_, warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
@ -189,80 +201,9 @@ func TestBuilderPrepare_ISOChecksum(t *testing.T) {
t.Fatal("should have error")
}
// Test good
config["iso_checksum"] = "FOo"
b = Builder{}
_, warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if b.config.ISOChecksum != "foo" {
t.Fatalf("should've lowercased: %s", b.config.ISOChecksum)
}
}
func TestBuilderPrepare_ISOChecksumType(t *testing.T) {
var b Builder
config := testConfig()
// Test bad
config["iso_checksum_type"] = ""
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Fatal("should have error")
}
// Test good
config["iso_checksum_type"] = "mD5"
b = Builder{}
_, warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if b.config.ISOChecksumType != "md5" {
t.Fatalf("should've lowercased: %s", b.config.ISOChecksumType)
}
// Test unknown
config["iso_checksum_type"] = "fake"
b = Builder{}
_, warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Fatal("should have error")
}
// Test none
config["iso_checksum_type"] = "none"
b = Builder{}
_, warns, err = b.Prepare(config)
// @todo: give warning in this case?
/*
if len(warns) == 0 {
t.Fatalf("bad: %#v", warns)
}
*/
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if b.config.ISOChecksumType != "none" {
t.Fatalf("should've lowercased: %s", b.config.ISOChecksumType)
}
}
func TestBuilderPrepare_ISOUrl(t *testing.T) {
var b Builder

View File

@ -3,7 +3,6 @@ package xva
import (
"context"
"errors"
"fmt"
"time"
"github.com/hashicorp/hcl/v2/hcldec"
@ -38,11 +37,12 @@ func (self *Builder) Prepare(raws ...interface{}) (params []string, warns []stri
}, raws...)
if err != nil {
packer.MultiErrorAppend(errs, err)
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.VCPUsMax == 0 {
@ -74,8 +74,12 @@ func (self *Builder) Prepare(raws ...interface{}) (params []string, warns []stri
// Validation
if self.config.SourcePath == "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("A source_path must be specified"))
if self.config.SourcePath == "" && self.config.CloneTemplate == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("Either source_path or clone_template must be specified"))
} else if self.config.SourcePath != "" && self.config.CloneTemplate != "" {
errs = packer.MultiErrorAppend(
errs, errors.New("Only one of source_path and clone_template must be specified"))
}
if len(errs.Errors) > 0 {
@ -101,7 +105,7 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p
//Share state between the other steps using a statebag
state := new(multistep.BasicStateBag)
state.Put("client", c)
// state.Put("config", self.config)
state.Put("config", self.config)
state.Put("commonconfig", self.config.CommonConfig)
state.Put("hook", hook)
state.Put("ui", ui)
@ -116,8 +120,11 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p
},
&commonsteps.StepCreateFloppy{
Files: self.config.FloppyFiles,
Label: "cidata",
},
&xscommon.StepHTTPServer{
Chan: httpReqChan,
},
new(xscommon.StepHTTPServer),
&xscommon.StepUploadVdi{
VdiNameFunc: func() string {
return "Packer-floppy-disk"
@ -134,6 +141,9 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p
VdiName: self.config.ToolsIsoName,
VdiUuidKey: "tools_vdi_uuid",
},
&xscommon.StepCreateInstance{
AssumePreInstalledOS: true,
},
new(stepImportInstance),
&xscommon.StepAttachVdi{
VdiUuidKey: "floppy_vdi_uuid",
@ -153,20 +163,28 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p
Chan: httpReqChan,
Timeout: 300 * time.Minute, /*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.CommHost,
SSHConfig: xscommon.SSHConfigFunc(self.config.CommonConfig.SSHConfig),
SSHPort: xscommon.SSHPort,
Host: xscommon.InstanceSSHIP,
SSHConfig: self.config.Comm.SSHConfigFunc(),
SSHPort: xscommon.InstanceSSHPort,
},
new(commonsteps.StepProvision),
new(xscommon.StepShutdown),
&xscommon.StepDetachVdi{
VdiUuidKey: "floppy_vdi_uuid",
},
new(xscommon.StepSetVmToTemplate),
&xscommon.StepDetachVdi{
VdiUuidKey: "tools_vdi_uuid",
},
&xscommon.StepDetachVdi{
VdiUuidKey: "floppy_vdi_uuid",
},
new(xscommon.StepDestroyVIFs),
new(xscommon.StepExport),
}

View File

@ -4,6 +4,7 @@ import (
"testing"
"github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/common"
)
func testConfig() map[string]interface{} {
@ -16,7 +17,7 @@ func testConfig() map[string]interface{} {
"ssh_username": "foo",
"source_path": ".",
packer.BuildNameConfigKey: "foo",
common.BuildNameConfigKey: "foo",
}
}

View File

@ -3,6 +3,7 @@ package xva
import (
"context"
"fmt"
"log"
"os"
"github.com/hashicorp/packer-plugin-sdk/multistep"
@ -24,6 +25,11 @@ func (self *stepImportInstance) Run(ctx context.Context, state multistep.StateBa
ui.Say("Step: Import Instance")
if config.SourcePath == "" {
log.Println("Skipping imporing instance - no `source_path` configured.")
return multistep.ActionContinue
}
// find the SR
srs, err := c.GetClient().SR.GetAll(c.GetSessionRef())
sr := srs[0]
@ -80,6 +86,12 @@ func (self *stepImportInstance) Run(ctx context.Context, state multistep.StateBa
return multistep.ActionHalt
}
err = xscommon.AddVMTags(c, instance, config.VMTags)
if err != nil {
ui.Error(fmt.Sprintf("Failed to add tags: %s", err.Error()))
return multistep.ActionHalt
}
ui.Say(fmt.Sprintf("Imported instance '%s'", instanceId))
return multistep.ActionContinue

View File

@ -29,21 +29,34 @@ each category, the available options are alphabetized and described.
* `iso_checksum` (string) - The checksum for the OS ISO file. Because ISO
files are so large, this is required and Packer will verify it prior
to booting a virtual machine with the ISO attached. The type of the
checksum is specified with `iso_checksum_type`, documented below.
to booting a virtual machine with the ISO attached. The type of
the checksum is specified within the checksum field as a prefix, ex:
"md5:{$checksum}". The type of the checksum can also be omitted and
Packer will try to infer it based on string length. Valid values are
"none", "{$checksum}", "md5:{$checksum}", "sha1:{$checksum}",
"sha256:{$checksum}", "sha512:{$checksum}" or "file:{$path}". Here is a
list of valid checksum values:
* md5:090992ba9fd140077b0661cb75f7ce13
* 090992ba9fd140077b0661cb75f7ce13
* sha1:ebfb681885ddf1234c18094a45bbeafd91467911
* ebfb681885ddf1234c18094a45bbeafd91467911
* sha256:ed363350696a726b7932db864dda019bd2017365c9e299627830f06954643f93
* ed363350696a726b7932db864dda019bd2017365c9e299627830f06954643f93
* file:http://releases.ubuntu.com/20.04/SHA256SUMS
* file:file://./local/path/file.sum
* file:./local/path/file.sum
* none
Although the checksum will not be verified when it is set to "none",
this is not recommended since these files can be very large and
corruption does happen from time to time.
* `iso_checksum_type` (string) - The type of the checksum specified in
`iso_checksum`. Valid values are "none", "md5", "sha1", "sha256", or
"sha512" currently. While "none" will skip checksumming, this is not
recommended since ISO files are generally large and corruption does happen
from time to time.
* `iso_url` (string) - A URL to the ISO containing the installation image.
This URL can be either an HTTP URL or a file URL (or path to a file).
If this is an HTTP URL, Packer will download it and cache it between
runs.
* `remote_host` (string) - The host of the Xenserver / XCP-ng pool primary. Typically these will be specified through environment variables as seen in the [examples](../../examples/centos8.json).
* `remote_host` (string) - The host of the Xenserver / XCP-ng pool primary. Typically these will be specified through environment variables as seen in the [examples](../../../examples).
* `remote_username` (string) - The XenServer username used to access the remote machine.
@ -59,7 +72,7 @@ each category, the available options are alphabetized and described.
be to type just enough to initialize the operating system installer. Special
keys can be typed as well, and are covered in the section below on the boot
command. If this is not specified, it is assumed the installer will start
itself. See the [Ubuntu](../../examples/ubuntu-2004.json) and [centos](../../examples/centos8.json) examples to see how these are used to launch autoinstall and kickstart respectively.
itself. See the [Ubuntu](../../../examples/ubuntu) and [centos](../../../examples/centos) examples to see how these are used to launch autoinstall and kickstart respectively.
* `boot_wait` (string) - The time to wait after booting the initial virtual
machine before typing the `boot_command`. The value of this should be
@ -198,6 +211,8 @@ each category, the available options are alphabetized and described.
* `vm_memory` (integer) - The size, in megabytes, of the amount of memory to
allocate for the VM. By default, this is 1024 (1 GB).
* `vm_tags` (array of strings) - A list of tags to add to the VM
## Differences with other Packer builders
Currently the XenServer builder has some quirks when compared with other Packer builders.
@ -256,4 +271,4 @@ The available variables are:
configuration parameter. If `http_directory` isn't specified, these will be
blank!
See the [examples](../../examples/) for working boot commands.
See the [examples](../../../examples) for working boot commands.

View File

@ -47,8 +47,7 @@ locals {
}
source "xenserver-iso" "centos8-local" {
iso_checksum = "aaf9d4b3071c16dbbda01dfe06085e5d0fdac76df323e3bbe87cce4318052247"
iso_checksum_type = "sha1"
iso_checksum = "sha1:aaf9d4b3071c16dbbda01dfe06085e5d0fdac76df323e3bbe87cce4318052247"
iso_url = "http://mirrors.ocf.berkeley.edu/centos/8.3.2011/isos/x86_64/CentOS-8.3.2011-x86_64-dvd1.iso"
sr_iso_name = var.sr_iso_name

View File

@ -47,8 +47,7 @@ locals {
}
source "xenserver-iso" "centos8-netinstall" {
iso_checksum = "07a8e59c42cc086ec4c49bdce4fae5a17b077dea"
iso_checksum_type = "sha1"
iso_checksum = "sha1:07a8e59c42cc086ec4c49bdce4fae5a17b077dea"
iso_url = "http://mirrors.ocf.berkeley.edu/centos/8.3.2011/isos/x86_64/CentOS-8.3.2011-x86_64-boot.iso"
sr_iso_name = var.sr_iso_name

View File

@ -1,12 +1,43 @@
packer {
required_plugins {
xenserver= {
version = ">= v0.3.2"
version = ">= v0.5.2"
source = "github.com/ddelnano/xenserver"
}
}
}
# The ubuntu_version value determines what Ubuntu iso URL and sha256 hash we lookup. Updating
# this will allow a new version to be pulled in.
data "null" "ubuntu_version" {
input = "20.04"
}
locals {
timestamp = regex_replace(timestamp(), "[- TZ:]", "")
ubuntu_version = data.null.ubuntu_version.output
# Update this map to support future releases. At this time, the Ubuntu
# jammy template is not available yet.
ubuntu_template_name = {
20.04 = "Ubuntu Focal Fossa 20.04"
}
}
# TODO(ddelnano): Update this to use a local once https://github.com/hashicorp/packer/issues/11011
# is fixed.
data "http" "ubuntu_sha_and_release" {
url = "https://releases.ubuntu.com/${data.null.ubuntu_version.output}/SHA256SUMS"
}
local "ubuntu_sha256" {
expression = regex("([A-Za-z0-9]+)[\\s\\*]+ubuntu-.*server", data.http.ubuntu_sha_and_release.body)
}
local "ubuntu_url_path" {
expression = regex("[A-Za-z0-9]+[\\s\\*]+ubuntu-${local.ubuntu_version}.(\\d+)-live-server-amd64.iso", data.http.ubuntu_sha_and_release.body)
}
variable "remote_host" {
type = string
description = "The ip or fqdn of your XenServer. This will be pulled from the env var 'PKR_VAR_XAPI_HOST'"
@ -42,15 +73,9 @@ variable "sr_name" {
description = "The name of the SR to packer will use"
}
locals {
timestamp = regex_replace(timestamp(), "[- TZ:]", "")
}
source "xenserver-iso" "ubuntu-2004" {
iso_checksum = "d1f2bf834bbe9bb43faf16f9be992a6f3935e65be0edece1dee2aa6eb1767423"
iso_checksum_type = "sha256"
iso_url = "http://releases.ubuntu.com/20.04/ubuntu-20.04.2-live-server-amd64.iso"
iso_checksum = "sha256:${local.ubuntu_sha256.0}"
iso_url = "https://releases.ubuntu.com/${local.ubuntu_version}/ubuntu-${local.ubuntu_version}.${local.ubuntu_url_path.0}-live-server-amd64.iso"
sr_iso_name = var.sr_iso_name
sr_name = var.sr_name
@ -60,10 +85,12 @@ source "xenserver-iso" "ubuntu-2004" {
remote_password = var.remote_password
remote_username = var.remote_username
vm_name = "packer-ubuntu-2004-${local.timestamp}"
# Change this to match the ISO of ubuntu you are using in the iso_url variable
clone_template = local.ubuntu_template_name[data.null.ubuntu_version.output]
vm_name = "packer-ubuntu-${data.null.ubuntu_version.output}-${local.timestamp}"
vm_description = "Build started: ${local.timestamp}"
vm_memory = 4096
disk_size = 20000
disk_size = 30720
floppy_files = [
"examples/http/ubuntu-2004/meta-data",