add floppy and cd to vsphere-clone

This commit is contained in:
sylviamoss 2020-09-18 17:09:01 +02:00
parent eca9b2f30d
commit 778d77e4a2
21 changed files with 409 additions and 28 deletions

View File

@ -41,6 +41,15 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
&common.StepConnect{
Config: &b.config.ConnectConfig,
},
&packerCommon.StepCreateCD{
Files: b.config.CDConfig.CDFiles,
Label: b.config.CDConfig.CDLabel,
},
&common.StepRemoteUpload{
Datastore: b.config.Datastore,
Host: b.config.Host,
SetHostForDatastoreUploads: b.config.SetHostForDatastoreUploads,
},
&StepCloneVM{
Config: &b.config.CloneConfig,
Location: &b.config.LocationConfig,
@ -49,6 +58,9 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
&common.StepConfigureHardware{
Config: &b.config.HardwareConfig,
},
&StepAddCDRom{
Config: &b.config.CDRomConfig,
},
&common.StepConfigParams{
Config: &b.config.ConfigParamsConfig,
},
@ -62,6 +74,17 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
if b.config.Comm.Type != "none" {
steps = append(steps,
&packerCommon.StepCreateFloppy{
Files: b.config.FloppyFiles,
Directories: b.config.FloppyDirectories,
Label: b.config.FloppyLabel,
},
&common.StepAddFloppy{
Config: &b.config.FloppyConfig,
Datastore: b.config.Datastore,
Host: b.config.Host,
SetHostForDatastoreUploads: b.config.SetHostForDatastoreUploads,
},
&common.StepHTTPIPDiscover{
HTTPIP: b.config.BootConfig.HTTPIP,
Network: b.config.WaitIpConfig.GetIPNet(),
@ -98,10 +121,17 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
&common.StepShutdown{
Config: &b.config.ShutdownConfig,
},
&common.StepRemoveFloppy{
Datastore: b.config.Datastore,
Host: b.config.Host,
},
)
}
steps = append(steps,
&common.StepRemoveCDRom{
Config: &b.config.RemoveCDRomConfig,
},
&common.StepCreateSnapshot{
CreateSnapshot: b.config.CreateSnapshot,
},

View File

@ -0,0 +1,17 @@
package clone
import (
"bytes"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
func basicStateBag() *multistep.BasicStateBag {
state := new(multistep.BasicStateBag)
state.Put("ui", &packer.BasicUi{
Reader: new(bytes.Buffer),
Writer: new(bytes.Buffer),
})
return state
}

View File

@ -15,6 +15,7 @@ import (
type Config struct {
packerCommon.PackerConfig `mapstructure:",squash"`
packerCommon.HTTPConfig `mapstructure:",squash"`
packerCommon.CDConfig `mapstructure:",squash"`
common.ConnectConfig `mapstructure:",squash"`
CloneConfig `mapstructure:",squash"`
@ -22,6 +23,9 @@ type Config struct {
common.HardwareConfig `mapstructure:",squash"`
common.ConfigParamsConfig `mapstructure:",squash"`
CDRomConfig `mapstructure:",squash"`
common.RemoveCDRomConfig `mapstructure:",squash"`
common.FloppyConfig `mapstructure:",squash"`
common.RunConfig `mapstructure:",squash"`
common.BootConfig `mapstructure:",squash"`
common.WaitIpConfig `mapstructure:",squash"`
@ -67,6 +71,8 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
errs = packer.MultiErrorAppend(errs, c.HardwareConfig.Prepare()...)
errs = packer.MultiErrorAppend(errs, c.HTTPConfig.Prepare(&c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.CDRomConfig.Prepare()...)
errs = packer.MultiErrorAppend(errs, c.CDConfig.Prepare(&c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.BootConfig.Prepare(&c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.WaitIpConfig.Prepare()...)
errs = packer.MultiErrorAppend(errs, c.Comm.Prepare(&c.ctx)...)

View File

@ -22,6 +22,8 @@ type FlatConfig struct {
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"`
CDFiles []string `mapstructure:"cd_files" cty:"cd_files" hcl:"cd_files"`
CDLabel *string `mapstructure:"cd_label" cty:"cd_label" hcl:"cd_label"`
VCenterServer *string `mapstructure:"vcenter_server" cty:"vcenter_server" hcl:"vcenter_server"`
Username *string `mapstructure:"username" cty:"username" hcl:"username"`
Password *string `mapstructure:"password" cty:"password" hcl:"password"`
@ -58,6 +60,12 @@ type FlatConfig struct {
ConfigParams map[string]string `mapstructure:"configuration_parameters" cty:"configuration_parameters" hcl:"configuration_parameters"`
ToolsSyncTime *bool `mapstructure:"tools_sync_time" cty:"tools_sync_time" hcl:"tools_sync_time"`
ToolsUpgradePolicy *bool `mapstructure:"tools_upgrade_policy" cty:"tools_upgrade_policy" hcl:"tools_upgrade_policy"`
CdromType *string `mapstructure:"cdrom_type" cty:"cdrom_type" hcl:"cdrom_type"`
RemoveCdrom *bool `mapstructure:"remove_cdrom" cty:"remove_cdrom" hcl:"remove_cdrom"`
FloppyIMGPath *string `mapstructure:"floppy_img_path" cty:"floppy_img_path" hcl:"floppy_img_path"`
FloppyFiles []string `mapstructure:"floppy_files" cty:"floppy_files" hcl:"floppy_files"`
FloppyDirectories []string `mapstructure:"floppy_dirs" cty:"floppy_dirs" hcl:"floppy_dirs"`
FloppyLabel *string `mapstructure:"floppy_label" cty:"floppy_label" hcl:"floppy_label"`
BootOrder *string `mapstructure:"boot_order" cty:"boot_order" hcl:"boot_order"`
BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"`
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
@ -147,6 +155,8 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
"http_interface": &hcldec.AttrSpec{Name: "http_interface", Type: cty.String, Required: false},
"cd_files": &hcldec.AttrSpec{Name: "cd_files", Type: cty.List(cty.String), Required: false},
"cd_label": &hcldec.AttrSpec{Name: "cd_label", Type: cty.String, Required: false},
"vcenter_server": &hcldec.AttrSpec{Name: "vcenter_server", Type: cty.String, Required: false},
"username": &hcldec.AttrSpec{Name: "username", Type: cty.String, Required: false},
"password": &hcldec.AttrSpec{Name: "password", Type: cty.String, Required: false},
@ -183,6 +193,12 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"configuration_parameters": &hcldec.AttrSpec{Name: "configuration_parameters", Type: cty.Map(cty.String), Required: false},
"tools_sync_time": &hcldec.AttrSpec{Name: "tools_sync_time", Type: cty.Bool, Required: false},
"tools_upgrade_policy": &hcldec.AttrSpec{Name: "tools_upgrade_policy", Type: cty.Bool, Required: false},
"cdrom_type": &hcldec.AttrSpec{Name: "cdrom_type", Type: cty.String, Required: false},
"remove_cdrom": &hcldec.AttrSpec{Name: "remove_cdrom", Type: cty.Bool, Required: false},
"floppy_img_path": &hcldec.AttrSpec{Name: "floppy_img_path", Type: cty.String, Required: false},
"floppy_files": &hcldec.AttrSpec{Name: "floppy_files", Type: cty.List(cty.String), Required: false},
"floppy_dirs": &hcldec.AttrSpec{Name: "floppy_dirs", Type: cty.List(cty.String), Required: false},
"floppy_label": &hcldec.AttrSpec{Name: "floppy_label", Type: cty.String, Required: false},
"boot_order": &hcldec.AttrSpec{Name: "boot_order", Type: cty.String, Required: false},
"boot_keygroup_interval": &hcldec.AttrSpec{Name: "boot_keygroup_interval", Type: cty.String, Required: false},
"boot_wait": &hcldec.AttrSpec{Name: "boot_wait", Type: cty.String, Required: false},

View File

@ -0,0 +1,59 @@
//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type CDRomConfig
package clone
import (
"context"
"fmt"
"github.com/hashicorp/packer/builder/vsphere/driver"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
type CDRomConfig struct {
// Which controller to use. Example: `sata`. Defaults to `ide`.
CdromType string `mapstructure:"cdrom_type"`
}
type StepAddCDRom struct {
Config *CDRomConfig
}
func (c *CDRomConfig) Prepare() []error {
var errs []error
if c.CdromType != "" && c.CdromType != "ide" && c.CdromType != "sata" {
errs = append(errs, fmt.Errorf("'cdrom_type' must be 'ide' or 'sata'"))
}
return errs
}
func (s *StepAddCDRom) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
vm := state.Get("vm").(driver.VirtualMachine)
if s.Config.CdromType == "sata" {
if _, err := vm.FindSATAController(); err == driver.ErrNoSataController {
ui.Say("Adding SATA controller...")
if err := vm.AddSATAController(); err != nil {
state.Put("error", fmt.Errorf("error adding SATA controller: %v", err))
return multistep.ActionHalt
}
}
}
// Add our custom CD, if it exists
if cd_path, _ := state.Get("cd_path").(string); cd_path != "" {
if err := vm.AddCdrom(s.Config.CdromType, cd_path); err != nil {
state.Put("error", fmt.Errorf("error mounting a CD '%v': %v", cd_path, err))
return multistep.ActionHalt
}
}
return multistep.ActionContinue
}
func (s *StepAddCDRom) Cleanup(state multistep.StateBag) {}

View File

@ -0,0 +1,30 @@
// Code generated by "mapstructure-to-hcl2 -type CDRomConfig"; DO NOT EDIT.
package clone
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/zclconf/go-cty/cty"
)
// FlatCDRomConfig is an auto-generated flat version of CDRomConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatCDRomConfig struct {
CdromType *string `mapstructure:"cdrom_type" cty:"cdrom_type" hcl:"cdrom_type"`
}
// FlatMapstructure returns a new FlatCDRomConfig.
// FlatCDRomConfig is an auto-generated flat version of CDRomConfig.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*CDRomConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatCDRomConfig)
}
// HCL2Spec returns the hcl spec of a CDRomConfig.
// This spec is used by HCL to read the fields of CDRomConfig.
// The decoded values from this spec will then be applied to a FlatCDRomConfig.
func (*FlatCDRomConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"cdrom_type": &hcldec.AttrSpec{Name: "cdrom_type", Type: cty.String, Required: false},
}
return s
}

View File

@ -0,0 +1,200 @@
package clone
import (
"context"
"fmt"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/hashicorp/packer/builder/vsphere/driver"
"github.com/hashicorp/packer/helper/multistep"
)
func TestCDRomConfig_Prepare(t *testing.T) {
// Data validation
tc := []struct {
name string
config *CDRomConfig
fail bool
expectedErrMsg string
}{
{
name: "Should not fail for empty config",
config: new(CDRomConfig),
fail: false,
expectedErrMsg: "",
},
{
name: "Valid cdroom type ide",
config: &CDRomConfig{CdromType: "ide"},
fail: false,
expectedErrMsg: "",
},
{
name: "Valid cdroom type sata",
config: &CDRomConfig{CdromType: "ide"},
fail: false,
expectedErrMsg: "",
},
{
name: "Invalid cdroom type",
config: &CDRomConfig{CdromType: "invalid"},
fail: true,
expectedErrMsg: "'cdrom_type' must be 'ide' or 'sata'",
},
}
for _, c := range tc {
errs := c.config.Prepare()
if c.fail {
if len(errs) == 0 {
t.Fatalf("Config preprare should fail")
}
if errs[0].Error() != c.expectedErrMsg {
t.Fatalf("Expected error message: %s but was '%s'", c.expectedErrMsg, errs[0].Error())
}
} else {
if len(errs) != 0 {
t.Fatalf("Config preprare should not fail")
}
}
}
}
func TestStepAddCDRom_Run(t *testing.T) {
tc := []struct {
name string
state *multistep.BasicStateBag
step *StepAddCDRom
vmMock *driver.VirtualMachineMock
expectedAction multistep.StepAction
expectedVmMock *driver.VirtualMachineMock
fail bool
errMessage string
}{
{
name: "CDRom SATA type with all cd paths set",
state: cdPathStateBag(),
step: &StepAddCDRom{
Config: &CDRomConfig{
CdromType: "sata",
},
},
vmMock: new(driver.VirtualMachineMock),
expectedAction: multistep.ActionContinue,
expectedVmMock: &driver.VirtualMachineMock{
FindSATAControllerCalled: true,
AddCdromCalled: true,
AddCdromCalledTimes: 1,
AddCdromTypes: []string{"sata"},
AddCdromPaths: []string{"cd/path"},
},
fail: false,
errMessage: "",
},
{
name: "Add SATA Controller",
state: basicStateBag(),
step: &StepAddCDRom{
Config: &CDRomConfig{
CdromType: "sata",
},
},
vmMock: &driver.VirtualMachineMock{
FindSATAControllerErr: driver.ErrNoSataController,
},
expectedAction: multistep.ActionContinue,
expectedVmMock: &driver.VirtualMachineMock{
FindSATAControllerCalled: true,
FindSATAControllerErr: driver.ErrNoSataController,
AddSATAControllerCalled: true,
},
fail: false,
errMessage: "",
},
{
name: "Fail to add SATA Controller",
state: basicStateBag(),
step: &StepAddCDRom{
Config: &CDRomConfig{
CdromType: "sata",
},
},
vmMock: &driver.VirtualMachineMock{
FindSATAControllerErr: driver.ErrNoSataController,
AddSATAControllerErr: fmt.Errorf("AddSATAController error"),
},
expectedAction: multistep.ActionHalt,
expectedVmMock: &driver.VirtualMachineMock{
FindSATAControllerCalled: true,
AddSATAControllerCalled: true,
},
fail: true,
errMessage: fmt.Sprintf("error adding SATA controller: %v", fmt.Errorf("AddSATAController error")),
},
{
name: "IDE CDRom Type and Iso Path set",
state: basicStateBag(),
step: &StepAddCDRom{
Config: &CDRomConfig{
CdromType: "ide",
},
},
vmMock: new(driver.VirtualMachineMock),
expectedAction: multistep.ActionContinue,
expectedVmMock: new(driver.VirtualMachineMock),
fail: false,
errMessage: "",
},
{
name: "Fail to add cdrom from state cd_path",
state: cdPathStateBag(),
step: &StepAddCDRom{
Config: new(CDRomConfig),
},
vmMock: &driver.VirtualMachineMock{
AddCdromErr: fmt.Errorf("AddCdrom error"),
},
expectedAction: multistep.ActionHalt,
expectedVmMock: &driver.VirtualMachineMock{
AddCdromCalled: true,
AddCdromCalledTimes: 1,
AddCdromTypes: []string{""},
AddCdromPaths: []string{"cd/path"},
},
fail: true,
errMessage: fmt.Sprintf("error mounting a CD 'cd/path': %v", fmt.Errorf("AddCdrom error")),
},
}
for _, c := range tc {
t.Run(c.name, func(t *testing.T) {
c.state.Put("vm", c.vmMock)
if action := c.step.Run(context.TODO(), c.state); action != c.expectedAction {
t.Fatalf("unexpected action %v", action)
}
err, ok := c.state.Get("error").(error)
if ok {
if err.Error() != c.errMessage {
t.Fatalf("unexpected error %s", err.Error())
}
} else {
if c.fail {
t.Fatalf("expected to fail but it didn't")
}
}
if diff := cmp.Diff(c.vmMock, c.expectedVmMock,
cmpopts.IgnoreInterfaces(struct{ error }{})); diff != "" {
t.Fatalf("unexpected VirtualMachine calls: %s", diff)
}
})
}
}
func cdPathStateBag() *multistep.BasicStateBag {
state := basicStateBag()
state.Put("cd_path", "cd/path")
return state
}

View File

@ -1,7 +1,7 @@
//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type FloppyConfig
package iso
package common
import (
"context"

View File

@ -1,5 +1,5 @@
// Code generated by "mapstructure-to-hcl2 -type FloppyConfig"; DO NOT EDIT.
package iso
package common
import (
"github.com/hashicorp/hcl/v2/hcldec"

View File

@ -1,4 +1,4 @@
package iso
package common
import (
"context"

View File

@ -1,4 +1,4 @@
package iso
package common
import (
"context"
@ -10,7 +10,7 @@ import (
)
func TestStepRemoteUpload_Run(t *testing.T) {
state := basicStateBag()
state := basicStateBag(nil)
driverMock := driver.NewDriverMock()
state.Put("driver", driverMock)
state.Put("iso_path", "[datastore] iso/path")
@ -48,7 +48,7 @@ func TestStepRemoteUpload_Run(t *testing.T) {
}
func TestStepRemoteUpload_SkipRun(t *testing.T) {
state := basicStateBag()
state := basicStateBag(nil)
driverMock := driver.NewDriverMock()
state.Put("driver", driverMock)

View File

@ -1,7 +1,7 @@
//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type RemoveCDRomConfig
package iso
package common
import (
"context"

View File

@ -1,5 +1,5 @@
// Code generated by "mapstructure-to-hcl2 -type RemoveCDRomConfig"; DO NOT EDIT.
package iso
package common
import (
"github.com/hashicorp/hcl/v2/hcldec"

View File

@ -1,4 +1,4 @@
package iso
package common
import (
"context"

View File

@ -52,7 +52,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
Files: b.config.CDConfig.CDFiles,
Label: b.config.CDConfig.CDLabel,
},
&StepRemoteUpload{
&common.StepRemoteUpload{
Datastore: b.config.Datastore,
Host: b.config.Host,
SetHostForDatastoreUploads: b.config.SetHostForDatastoreUploads,
@ -80,7 +80,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
Directories: b.config.FloppyDirectories,
Label: b.config.FloppyLabel,
},
&StepAddFloppy{
&common.StepAddFloppy{
Config: &b.config.FloppyConfig,
Datastore: b.config.Datastore,
Host: b.config.Host,
@ -117,7 +117,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
&common.StepShutdown{
Config: &b.config.ShutdownConfig,
},
&StepRemoveFloppy{
&common.StepRemoveFloppy{
Datastore: b.config.Datastore,
Host: b.config.Host,
},
@ -125,7 +125,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
}
steps = append(steps,
&StepRemoveCDRom{
&common.StepRemoveCDRom{
Config: &b.config.RemoveCDRomConfig,
},
&common.StepCreateSnapshot{

View File

@ -26,8 +26,8 @@ type Config struct {
packerCommon.ISOConfig `mapstructure:",squash"`
CDRomConfig `mapstructure:",squash"`
RemoveCDRomConfig `mapstructure:",squash"`
FloppyConfig `mapstructure:",squash"`
common.RemoveCDRomConfig `mapstructure:",squash"`
common.FloppyConfig `mapstructure:",squash"`
common.RunConfig `mapstructure:",squash"`
common.BootConfig `mapstructure:",squash"`
common.WaitIpConfig `mapstructure:",squash"`

View File

@ -168,6 +168,10 @@ can be done via environment variable:
@include 'common/HTTPConfig-not-required.mdx'
### Floppy configuration
@include 'builder/vsphere/common/FloppyConfig-not-required.mdx'
### Connection Configuration
@include 'builder/vsphere/common/ConnectConfig-not-required.mdx'
@ -192,6 +196,16 @@ can be done via environment variable:
@include 'builder/vsphere/common/WaitIpConfig-not-required.mdx'
### CDRom Configuration
@include 'common/CDConfig.mdx'
#### Optional:
@include 'common/CDConfig-not-required.mdx'
@include 'builder/vsphere/clone/CDRomConfig-not-required.mdx'
### Communicator configuration
#### Optional common fields:

View File

@ -97,7 +97,7 @@ source "vsphere-iso" "example" {
### Floppy configuration
@include 'builder/vsphere/iso/FloppyConfig-not-required.mdx'
@include 'builder/vsphere/common/FloppyConfig-not-required.mdx'
### Connection Configuration
@ -174,6 +174,12 @@ iso_paths = [
@include 'builder/vsphere/iso/CDRomConfig-not-required.mdx'
@include 'common/CDConfig.mdx'
#### Optional:
@include 'common/CDConfig-not-required.mdx'
### Create Configuration
@include 'builder/vsphere/iso/CreateConfig-not-required.mdx'

View File

@ -0,0 +1,3 @@
<!-- Code generated from the comments of the CDRomConfig struct in builder/vsphere/clone/step_add_cdrom.go; DO NOT EDIT MANUALLY -->
- `cdrom_type` (string) - Which controller to use. Example: `sata`. Defaults to `ide`.

View File

@ -1,4 +1,4 @@
<!-- Code generated from the comments of the FloppyConfig struct in builder/vsphere/iso/step_add_floppy.go; DO NOT EDIT MANUALLY -->
<!-- Code generated from the comments of the FloppyConfig struct in builder/vsphere/common/step_add_floppy.go; DO NOT EDIT MANUALLY -->
- `floppy_img_path` (string) - Datastore path to a floppy image that will be mounted to the VM.
Example: `[datastore1] ISO/pvscsi-Windows8.flp`.

View File

@ -1,3 +1,3 @@
<!-- Code generated from the comments of the RemoveCDRomConfig struct in builder/vsphere/iso/step_remove_cdrom.go; DO NOT EDIT MANUALLY -->
<!-- Code generated from the comments of the RemoveCDRomConfig struct in builder/vsphere/common/step_remove_cdrom.go; DO NOT EDIT MANUALLY -->
- `remove_cdrom` (bool) - Remove CD-ROM devices from template. Defaults to `false`.