Skip to content

Commit

Permalink
clone: Add support to clone VM by ID
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastian-de authored and lbajolet-hashicorp committed Feb 24, 2023
1 parent c8743bc commit 4842a84
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 13 deletions.
29 changes: 19 additions & 10 deletions builder/proxmox/clone/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,20 +69,29 @@ func (*cloneVMCreator) Create(vmRef *proxmoxapi.VmRef, config proxmoxapi.ConfigQ
}
config.Ipconfig = IpconfigMap

sourceVmrs, err := client.GetVmRefsByName(c.CloneVM)
if err != nil {
return err
}
var sourceVmr *proxmoxapi.VmRef
if c.CloneVM != "" {
sourceVmrs, err := client.GetVmRefsByName(c.CloneVM)
if err != nil {
return err
}

// prefer source Vm located on same node
sourceVmr := sourceVmrs[0]
for _, candVmr := range sourceVmrs {
if candVmr.Node() == vmRef.Node() {
sourceVmr = candVmr
// prefer source Vm located on same node
sourceVmr = sourceVmrs[0]
for _, candVmr := range sourceVmrs {
if candVmr.Node() == vmRef.Node() {
sourceVmr = candVmr
}
}
} else if c.CloneVMID != 0 {
sourceVmr = proxmoxapi.NewVmRef(c.CloneVMID)
err := client.CheckVmRef(sourceVmr)
if err != nil {
return err
}
}

err = config.CloneVm(sourceVmr, vmRef, client)
err := config.CloneVm(sourceVmr, vmRef, client)
if err != nil {
return err
}
Expand Down
14 changes: 12 additions & 2 deletions builder/proxmox/clone/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type Config struct {
proxmoxcommon.Config `mapstructure:",squash"`

CloneVM string `mapstructure:"clone_vm" required:"true"`
CloneVMID int `mapstructure:"clone_vm_id" required:"true"`
FullClone config.Trilean `mapstructure:"full_clone" required:"false"`

Nameserver string `mapstructure:"nameserver" required:"false"`
Expand All @@ -39,8 +40,17 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, []string, error) {
errs = packersdk.MultiErrorAppend(errs, merrs)
}

if c.CloneVM == "" {
errs = packersdk.MultiErrorAppend(errs, errors.New("clone_vm must be specified"))
if c.CloneVM == "" && c.CloneVMID == 0 {
errs = packersdk.MultiErrorAppend(errs, errors.New("one of clone_vm or clone_vm_id must be specified"))
}
if c.CloneVM != "" && c.CloneVMID != 0 {
errs = packersdk.MultiErrorAppend(errs, errors.New("clone_vm and clone_vm_id cannot both be specified"))
}
// Technically Proxmox VMIDs are unsigned 32bit integers, but are limited to
// the range 100-999999999. Source:
// https://pve-devel.pve.proxmox.narkive.com/Pa6mH1OP/avoiding-vmid-reuse#post8
if c.CloneVMID != 0 && (c.CloneVMID < 100 || c.CloneVMID > 999999999) {
errs = packersdk.MultiErrorAppend(errs, errors.New("clone_vm_id must be in range 100-999999999"))
}

// Check validity of given IP addresses
Expand Down
2 changes: 2 additions & 0 deletions builder/proxmox/clone/config.hcl2spec.go

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

59 changes: 58 additions & 1 deletion builder/proxmox/clone/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func TestRequiredParameters(t *testing.T) {
t.Fatal("Expected errors to be packersdk.MultiError")
}

required := []string{"username", "token", "proxmox_url", "node", "ssh_username", "clone_vm"}
required := []string{"username", "token", "proxmox_url", "node", "ssh_username", "clone_vm", "clone_vm_id"}
for _, param := range required {
found := false
for _, err := range errs.Errors {
Expand All @@ -45,6 +45,63 @@ func TestRequiredParameters(t *testing.T) {
}
}

func TestVMNameOrID(t *testing.T) {
ipconfigTest := []struct {
name string
cloneVM string
cloneVMID int
expectFailure bool
}{
{
name: "clone_vm given, no error",
cloneVM: "myVM",
cloneVMID: 0,
expectFailure: false,
},
{
name: "valid clone_vm_id given, no error",
cloneVM: "",
cloneVMID: 100,
expectFailure: false,
},
{
name: "clone_vm_id out of range, error",
cloneVM: "",
cloneVMID: 50,
expectFailure: true,
},
{
name: "clone_vm and clone_vm_id given, error",
cloneVM: "myVM",
cloneVMID: 100,
expectFailure: true,
},
{
name: "neither clone_vm nor clone_vm_id given, error",
cloneVM: "",
cloneVMID: 0,
expectFailure: true,
},
}

for _, tt := range ipconfigTest {
t.Run(tt.name, func(t *testing.T) {
cfg := mandatoryConfig(t)
cfg["clone_vm"] = tt.cloneVM
cfg["clone_vm_id"] = tt.cloneVMID

var c Config
_, _, err := c.Prepare(&c, cfg)
if err != nil && !tt.expectFailure {
t.Fatalf("unexpected failure: %s", err)
}
if err == nil && tt.expectFailure {
t.Errorf("expected failure, but prepare succeeded")
}
})
}
}

func TestNameserver(t *testing.T) {
ipconfigTest := []struct {
name string
Expand Down
5 changes: 5 additions & 0 deletions docs/builders/clone.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ in the image's Cloud-Init settings for provisioning.
machine on during creation.

- `clone_vm` (string) - The name of the VM Packer should clone and build from.
Either `clone_vm` or `clone_vm_id` must be specifed.

- `clone_vm_id` (int) - The ID of the VM Packer should clone and build from.
Proxmox VMIDs are limited to the range 100-999999999.
Either `clone_vm` or `clone_vm_id` must be specifed.

### Optional:

Expand Down

0 comments on commit 4842a84

Please sign in to comment.