Skip to content

Commit

Permalink
use force instead of replace_existing, unit tests for force
Browse files Browse the repository at this point in the history
  • Loading branch information
joeyberkovitz committed Aug 13, 2022
1 parent 3b5e5f2 commit 6515c8b
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 13 deletions.
2 changes: 0 additions & 2 deletions builder/proxmox/clone/config.hcl2spec.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions builder/proxmox/common/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,6 @@ type Config struct {
AdditionalISOFiles []additionalISOsConfig `mapstructure:"additional_iso_files"`
VMInterface string `mapstructure:"vm_interface"`

ReplaceExisting bool `mapstructure:"replace_existing"`

Ctx interpolate.Context `mapstructure-to-hcl2:",skip"`
}

Expand Down
2 changes: 0 additions & 2 deletions builder/proxmox/common/config.hcl2spec.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 17 additions & 3 deletions builder/proxmox/common/step_start_vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type vmStarter interface {
GetNextID(int) (int, error)
StartVm(*proxmox.VmRef) (string, error)
SetVmConfig(*proxmox.VmRef, map[string]interface{}) (interface{}, error)
CheckVmRef(vmr *proxmox.VmRef) (err error)
GetVmRefsByName(vmName string) (vmrs []*proxmox.VmRef, err error)
GetVmState(vmr *proxmox.VmRef) (vmState map[string]interface{}, err error)
StopVm(vmr *proxmox.VmRef) (exitStatus string, err error)
Expand All @@ -37,6 +38,19 @@ var (
maxDuplicateIDRetries = 3
)

func getExistingVms(c *Config, client vmStarter) ([]*proxmox.VmRef, error) {
if c.VMID > 0 {
vmRef := proxmox.NewVmRef(c.VMID)
err := client.CheckVmRef(vmRef)
if err != nil {
return []*proxmox.VmRef{}, err
}
return []*proxmox.VmRef{vmRef}, nil
} else {
return client.GetVmRefsByName(c.TemplateName)
}
}

func (s *stepStartVM) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
client := state.Get("proxmoxClient").(vmStarter)
Expand Down Expand Up @@ -74,9 +88,9 @@ func (s *stepStartVM) Run(ctx context.Context, state multistep.StateBag) multist
Onboot: c.Onboot,
}

if c.ReplaceExisting {
ui.Say("Replace existing set, checking for existing VM or template by name")
vmrs, err := client.GetVmRefsByName(c.TemplateName)
if c.PackerForce {
ui.Say("Force set, checking for existing VM or template")
vmrs, err := getExistingVms(c, client)
if err != nil {
// This can happen if no VMs found, so don't consider it to be an error
ui.Say(fmt.Sprintf("Couldn't find existing VMs, continuing: %s", err.Error()))
Expand Down
76 changes: 76 additions & 0 deletions builder/proxmox/common/step_start_vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"testing"

"github.com/Telmate/proxmox-api-go/proxmox"
"github.com/hashicorp/packer-plugin-sdk/common"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
Expand Down Expand Up @@ -113,6 +114,11 @@ type startVMMock struct {
startVm func(*proxmox.VmRef) (string, error)
setVmConfig func(*proxmox.VmRef, map[string]interface{}) (interface{}, error)
getNextID func(id int) (int, error)
checkVmRef func(vmr *proxmox.VmRef) (err error)
getVmByName func(vmName string) (vmrs []*proxmox.VmRef, err error)
getVmState func(vmr *proxmox.VmRef) (vmState map[string]interface{}, err error)
stopVm func(vmr *proxmox.VmRef) (exitStatus string, err error)
deleteVm func(vmr *proxmox.VmRef) (exitStatus string, err error)
}

func (m *startVMMock) Create(vmRef *proxmox.VmRef, config proxmox.ConfigQemu, state multistep.StateBag) error {
Expand All @@ -128,6 +134,23 @@ func (m *startVMMock) GetNextID(id int) (int, error) {
return m.getNextID(id)
}

func (m *startVMMock) CheckVmRef(vmr *proxmox.VmRef) (err error) {
return m.checkVmRef(vmr)
}

func (m *startVMMock) GetVmRefsByName(vmName string) (vmrs []*proxmox.VmRef, err error) {
return m.getVmByName(vmName)
}
func (m *startVMMock) GetVmState(vmr *proxmox.VmRef) (vmState map[string]interface{}, err error) {
return m.getVmState(vmr)
}
func (m *startVMMock) StopVm(vmr *proxmox.VmRef) (exitStatus string, err error) {
return m.stopVm(vmr)
}
func (m *startVMMock) DeleteVm(vmr *proxmox.VmRef) (exitStatus string, err error) {
return m.deleteVm(vmr)
}

func TestStartVM(t *testing.T) {
// TODO: proxmox-api-go does a lot of manipulation on the input and does not
// give any way to access the actual data it sends to the Proxmox server,
Expand Down Expand Up @@ -198,6 +221,7 @@ func TestStartVMRetryOnDuplicateID(t *testing.T) {
config *Config
createErrorGenerator func(id int) error
expectedCallsToCreate int
expectedCallsToDelete int
expectedAction multistep.StepAction
}{
{
Expand Down Expand Up @@ -242,11 +266,38 @@ func TestStartVMRetryOnDuplicateID(t *testing.T) {
expectedCallsToCreate: maxDuplicateIDRetries,
expectedAction: multistep.ActionHalt,
},
{
name: "Delete existing VM by VMID only when VMID and force are enabled",
config: &Config{
PackerConfig: common.PackerConfig{
PackerForce: true,
},
VMID: 1,
},
createErrorGenerator: func(id int) error { return nil },
expectedCallsToCreate: 1,
expectedCallsToDelete: 1,
expectedAction: multistep.ActionContinue,
},
{
name: "Delete existing VMs by template name when VMID not specified and force are enabled",
config: &Config{
PackerConfig: common.PackerConfig{
PackerForce: true,
},
TemplateName: "test_vm",
},
createErrorGenerator: func(id int) error { return nil },
expectedCallsToCreate: 1,
expectedCallsToDelete: 3,
expectedAction: multistep.ActionContinue,
},
}

for _, c := range cs {
t.Run(c.name, func(t *testing.T) {
createCalls := 0
deleteCalls := 0
mock := &startVMMock{
create: func(vmRef *proxmox.VmRef, config proxmox.ConfigQemu, state multistep.StateBag) error {
createCalls++
Expand All @@ -261,6 +312,28 @@ func TestStartVMRetryOnDuplicateID(t *testing.T) {
getNextID: func(id int) (int, error) {
return createCalls + 1, nil
},
checkVmRef: func(vmr *proxmox.VmRef) (err error) {
return nil
},
getVmByName: func(vmName string) (vmrs []*proxmox.VmRef, err error) {
return []*proxmox.VmRef{
proxmox.NewVmRef(100),
proxmox.NewVmRef(101),
proxmox.NewVmRef(102),
}, nil
},
getVmState: func(vmr *proxmox.VmRef) (vmState map[string]interface{}, err error) {
return map[string]interface{}{ // only used expecting shutdown
"status": "stopped",
}, nil
},
stopVm: func(vmr *proxmox.VmRef) (exitStatus string, err error) {
return "", nil
},
deleteVm: func(vmr *proxmox.VmRef) (exitStatus string, err error) {
deleteCalls++
return "", nil
},
}
state := new(multistep.BasicStateBag)
state.Put("ui", packersdk.TestUi(t))
Expand All @@ -275,6 +348,9 @@ func TestStartVMRetryOnDuplicateID(t *testing.T) {
if createCalls != c.expectedCallsToCreate {
t.Errorf("Expected %d calls to create, got %d", c.expectedCallsToCreate, createCalls)
}
if deleteCalls != c.expectedCallsToDelete {
t.Errorf("Expected %d calls to delete, got %d", c.expectedCallsToDelete, deleteCalls)
}
})
}
}
2 changes: 0 additions & 2 deletions builder/proxmox/iso/config.hcl2spec.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions docs-partials/builder/proxmox/common/Config-not-required.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,4 @@

- `vm_interface` (string) - VM Interface

- `replace_existing` (bool) - Replace Existing

<!-- End of code generated from the comments of the Config struct in builder/proxmox/common/config.go; -->

0 comments on commit 6515c8b

Please sign in to comment.