From dc2813c8e2002b7e37baff4649050b336a5bf713 Mon Sep 17 00:00:00 2001 From: Gerhard Lausser Date: Fri, 10 Apr 2020 23:19:33 +0200 Subject: [PATCH] feat(proxmox): add ability to add a cloud-init drive --- builder/proxmox/config.go | 5 +++ builder/proxmox/config.hcl2spec.go | 4 ++ builder/proxmox/config_test.go | 3 ++ .../proxmox/step_finalize_template_config.go | 37 +++++++++++++++ .../step_finalize_template_config_test.go | 45 +++++++++++++++++++ website/pages/docs/builders/proxmox.mdx | 5 +++ 6 files changed, 99 insertions(+) diff --git a/builder/proxmox/config.go b/builder/proxmox/config.go index 8a3aad139..92b6b9cef 100644 --- a/builder/proxmox/config.go +++ b/builder/proxmox/config.go @@ -58,6 +58,9 @@ type Config struct { TemplateDescription string `mapstructure:"template_description"` UnmountISO bool `mapstructure:"unmount_iso"` + CloudInit bool `mapstructure:"cloud_init"` + CloudInitStoragePool string `mapstructure:"cloud_init_storage_pool"` + shouldUploadISO bool ctx interpolate.Context @@ -85,6 +88,8 @@ type vgaConfig struct { func (c *Config) Prepare(raws ...interface{}) ([]string, error) { // Agent defaults to true c.Agent = true + // Do not add a cloud-init cdrom by default + c.CloudInit = false var md mapstructure.Metadata err := config.Decode(c, &config.DecodeOpts{ diff --git a/builder/proxmox/config.hcl2spec.go b/builder/proxmox/config.hcl2spec.go index 9864ebcac..73d86d749 100644 --- a/builder/proxmox/config.hcl2spec.go +++ b/builder/proxmox/config.hcl2spec.go @@ -95,6 +95,8 @@ type FlatConfig struct { TemplateName *string `mapstructure:"template_name" cty:"template_name"` TemplateDescription *string `mapstructure:"template_description" cty:"template_description"` UnmountISO *bool `mapstructure:"unmount_iso" cty:"unmount_iso"` + CloudInit *bool `mapstructure:"cloud_init" cty:"cloud_init"` + CloudInitStoragePool *string `mapstructure:"cloud_init_storage_pool" cty:"cloud_init_storage_pool"` } // FlatMapstructure returns a new FlatConfig. @@ -195,6 +197,8 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "template_name": &hcldec.AttrSpec{Name: "template_name", Type: cty.String, Required: false}, "template_description": &hcldec.AttrSpec{Name: "template_description", Type: cty.String, Required: false}, "unmount_iso": &hcldec.AttrSpec{Name: "unmount_iso", Type: cty.Bool, Required: false}, + "cloud_init": &hcldec.AttrSpec{Name: "cloud_init", Type: cty.Bool, Required: false}, + "cloud_init_storage_pool": &hcldec.AttrSpec{Name: "cloud_init_storage_pool", Type: cty.String, Required: false}, } return s } diff --git a/builder/proxmox/config_test.go b/builder/proxmox/config_test.go index f925a1adb..2c6eac5cf 100644 --- a/builder/proxmox/config_test.go +++ b/builder/proxmox/config_test.go @@ -125,6 +125,9 @@ func TestBasicExampleFromDocsIsValid(t *testing.T) { if b.config.SCSIController != "lsi" { t.Errorf("Expected SCSI controller to be 'lsi', got %s", b.config.SCSIController) } + if b.config.CloudInit != false { + t.Errorf("Expected CloudInit to be false, got %t", b.config.CloudInit) + } } func TestAgentSetToFalse(t *testing.T) { diff --git a/builder/proxmox/step_finalize_template_config.go b/builder/proxmox/step_finalize_template_config.go index df90fc9f8..1e2deba69 100644 --- a/builder/proxmox/step_finalize_template_config.go +++ b/builder/proxmox/step_finalize_template_config.go @@ -56,6 +56,43 @@ func (s *stepFinalizeTemplateConfig) Run(ctx context.Context, state multistep.St changes["ide2"] = "none,media=cdrom" } + if c.CloudInit { + vmParams, err := client.GetVmConfig(vmRef) + if err != nil { + err := fmt.Errorf("Error fetching template config: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + + cloudInitStoragePool := c.CloudInitStoragePool + if cloudInitStoragePool == "" { + if vmParams["bootdisk"] != nil && vmParams[vmParams["bootdisk"].(string)] != nil { + bootDisk := vmParams[vmParams["bootdisk"].(string)].(string) + cloudInitStoragePool = strings.Split(bootDisk, ":")[0] + } + } + if cloudInitStoragePool != "" { + ideControllers := []string{"ide3", "ide2", "ide1", "ide0"} + cloudInitAttached := false + // find a free ide controller + for _, controller := range ideControllers { + if vmParams[controller] == nil { + ui.Say("Adding a cloud-init cdrom in storage pool " + cloudInitStoragePool) + changes[controller] = cloudInitStoragePool + ":cloudinit" + cloudInitAttached = true + break + } + } + if cloudInitAttached == false { + err := fmt.Errorf("Found no free ide controller for a cloud-init cdrom") + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + } + } + if len(changes) > 0 { _, err := client.SetVmConfig(vmRef, changes) if err != nil { diff --git a/builder/proxmox/step_finalize_template_config_test.go b/builder/proxmox/step_finalize_template_config_test.go index 501dddef2..e045cb0ce 100644 --- a/builder/proxmox/step_finalize_template_config_test.go +++ b/builder/proxmox/step_finalize_template_config_test.go @@ -71,6 +71,51 @@ func TestTemplateFinalize(t *testing.T) { }, expectedAction: multistep.ActionContinue, }, + { + name: "all options with cloud-init", + builderConfig: &Config{ + TemplateName: "my-template", + TemplateDescription: "some-description", + UnmountISO: true, + CloudInit: true, + }, + initialVMConfig: map[string]interface{}{ + "name": "dummy", + "description": "Packer ephemeral build VM", + "ide2": "local:iso/Fedora-Server-dvd-x86_64-29-1.2.iso,media=cdrom", + "bootdisk": "virtio0", + "virtio0": "ceph01:base-223-disk-0,cache=unsafe,media=disk,size=32G", + }, + expectCallSetConfig: true, + expectedVMConfig: map[string]interface{}{ + "name": "my-template", + "description": "some-description", + "ide2": "none,media=cdrom", + "ide3": "ceph01:cloudinit", + }, + expectedAction: multistep.ActionContinue, + }, + { + name: "no available controller for cloud-init drive", + builderConfig: &Config{ + TemplateName: "my-template", + TemplateDescription: "some-description", + UnmountISO: false, + CloudInit: true, + }, + initialVMConfig: map[string]interface{}{ + "name": "dummy", + "description": "Packer ephemeral build VM", + "ide0": "local:iso/Fedora-Server-dvd-x86_64-29-1.2.iso,media=cdrom", + "ide1": "local:iso/Fedora-Server-dvd-x86_64-29-1.2.iso,media=cdrom", + "ide2": "local:iso/Fedora-Server-dvd-x86_64-29-1.2.iso,media=cdrom", + "ide3": "local:iso/Fedora-Server-dvd-x86_64-29-1.2.iso,media=cdrom", + "bootdisk": "virtio0", + "virtio0": "ceph01:base-223-disk-0,cache=unsafe,media=disk,size=32G", + }, + expectCallSetConfig: false, + expectedAction: multistep.ActionHalt, + }, { name: "no cd-drive with unmount=true should returns halt", builderConfig: &Config{ diff --git a/website/pages/docs/builders/proxmox.mdx b/website/pages/docs/builders/proxmox.mdx index e0d98eed5..944d5007f 100644 --- a/website/pages/docs/builders/proxmox.mdx +++ b/website/pages/docs/builders/proxmox.mdx @@ -193,6 +193,11 @@ builder. `lsi53c810`, `virtio-scsi-pci`, `virtio-scsi-single`, `megasas`, or `pvscsi`. Defaults to `lsi`. +- `cloud_init` (bool) - If true, add a Cloud-Init CDROM drive after the virtual machine has been converted to a template. + +- `cloud_init_storage_pool` - (string) - Name of the Proxmox storage pool + to store the Cloud-Init CDROM on. If not given, the storage pool of the boot device will be used. + ## Example: Fedora with kickstart Here is a basic example creating a Fedora 29 server image with a Kickstart