From ade1d49117f5390e5ee58ddeadef0adf02143d33 Mon Sep 17 00:00:00 2001 From: Pavel Boldyrev <627562+bpg@users.noreply.github.com> Date: Fri, 25 Nov 2022 23:23:50 -0500 Subject: [PATCH] feat: add support for VM tags (#169) --- docs/resources/virtual_environment_vm.md | 2 + example/resource_virtual_environment_vm.tf | 1 + proxmoxtf/resource_virtual_environment_vm.go | 54 ++++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/docs/resources/virtual_environment_vm.md b/docs/resources/virtual_environment_vm.md index c220b150..7f00fb16 100644 --- a/docs/resources/virtual_environment_vm.md +++ b/docs/resources/virtual_environment_vm.md @@ -17,6 +17,7 @@ Manages a virtual machine. resource "proxmox_virtual_environment_vm" "ubuntu_vm" { name = "terraform-provider-proxmox-ubuntu-vm" description = "Managed by Terraform" + tags = ["terraform", "ubuntu"] node_name = "first-node" vm_id = 4321 @@ -283,6 +284,7 @@ output "ubuntu_vm_public_key" { * `socket` - A unix socket. * `started` - (Optional) Whether to start the virtual machine (defaults to `true`). * `tablet_device` - (Optional) Whether to enable the USB tablet device (defaults to `true`). +* `tags` - (Optional) A list of tags of the VM. This is only meta information (defaults to `[]`). Note: Proxmox always sorts the VM tags. If the list in template is not sorted, then Proxmox will always report a difference on the resource. You may use the `ignore_changes` lifecycle meta-argument to ignore changes to this attribute. * `template` - (Optional) Whether to create a template (defaults to `false`). * `timeout_clone` - (Optional) Timeout for cloning a VM in seconds (defaults to 1800). * `timeout_move_disk` - (Optional) Timeout for moving the disk of a VM in seconds (defaults to 1800). diff --git a/example/resource_virtual_environment_vm.tf b/example/resource_virtual_environment_vm.tf index 01cab3ab..d1ed7b3e 100644 --- a/example/resource_virtual_environment_vm.tf +++ b/example/resource_virtual_environment_vm.tf @@ -60,6 +60,7 @@ resource "proxmox_virtual_environment_vm" "example" { node_name = data.proxmox_virtual_environment_nodes.example.names[0] pool_id = proxmox_virtual_environment_pool.example.id vm_id = 2041 + tags = ["terraform", "ubuntu"] clone { vm_id = proxmox_virtual_environment_vm.example_template.id diff --git a/proxmoxtf/resource_virtual_environment_vm.go b/proxmoxtf/resource_virtual_environment_vm.go index 1c682393..48ac31eb 100644 --- a/proxmoxtf/resource_virtual_environment_vm.go +++ b/proxmoxtf/resource_virtual_environment_vm.go @@ -187,6 +187,7 @@ const ( mkResourceVirtualEnvironmentVMSerialDeviceDevice = "device" mkResourceVirtualEnvironmentVMStarted = "started" mkResourceVirtualEnvironmentVMTabletDevice = "tablet_device" + mkResourceVirtualEnvironmentVMTags = "tags" mkResourceVirtualEnvironmentVMTemplate = "template" mkResourceVirtualEnvironmentVMTimeoutClone = "timeout_clone" mkResourceVirtualEnvironmentVMTimeoutMoveDisk = "timeout_move_disk" @@ -981,6 +982,12 @@ func resourceVirtualEnvironmentVM() *schema.Resource { Optional: true, Default: dvResourceVirtualEnvironmentVMTabletDevice, }, + mkResourceVirtualEnvironmentVMTags: { + Type: schema.TypeList, + Description: "Tags of the virtual machine. This is only meta information.", + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, mkResourceVirtualEnvironmentVMTemplate: { Type: schema.TypeBool, Description: "Whether to create a template", @@ -1118,6 +1125,7 @@ func resourceVirtualEnvironmentVMCreateClone(ctx context.Context, d *schema.Reso description := d.Get(mkResourceVirtualEnvironmentVMDescription).(string) name := d.Get(mkResourceVirtualEnvironmentVMName).(string) + tags := d.Get(mkResourceVirtualEnvironmentVMTags).([]interface{}) nodeName := d.Get(mkResourceVirtualEnvironmentVMNodeName).(string) poolID := d.Get(mkResourceVirtualEnvironmentVMPoolID).(string) vmID := d.Get(mkResourceVirtualEnvironmentVMVMID).(int) @@ -1394,6 +1402,11 @@ func resourceVirtualEnvironmentVMCreateClone(ctx context.Context, d *schema.Reso updateBody.TabletDeviceEnabled = &tabletDevice } + if len(tags) > 0 { + tagString := resourceVirtualEnvironmentVMGetTagsString(d) + updateBody.Tags = &tagString + } + //nolint:gosimple if template != dvResourceVirtualEnvironmentVMTemplate { updateBody.Template = &template @@ -1616,6 +1629,7 @@ func resourceVirtualEnvironmentVMCreateCustom(ctx context.Context, d *schema.Res memoryShared := memoryBlock[mkResourceVirtualEnvironmentVMMemoryShared].(int) name := d.Get(mkResourceVirtualEnvironmentVMName).(string) + tags := d.Get(mkResourceVirtualEnvironmentVMTags).([]interface{}) networkDeviceObjects, err := resourceVirtualEnvironmentVMGetNetworkDeviceObjects(d) if err != nil { @@ -1757,6 +1771,11 @@ func resourceVirtualEnvironmentVMCreateCustom(ctx context.Context, d *schema.Res createBody.Description = &description } + if len(tags) > 0 { + tagsString := resourceVirtualEnvironmentVMGetTagsString(d) + createBody.Tags = &tagsString + } + if name != "" { createBody.Name = &name } @@ -2282,6 +2301,19 @@ func resourceVirtualEnvironmentVMGetSerialDeviceList(d *schema.ResourceData) (pr return list, nil } +func resourceVirtualEnvironmentVMGetTagsString(d *schema.ResourceData) string { + tags := d.Get(mkResourceVirtualEnvironmentVMTags).([]interface{}) + var sanitizedTags []string + for i := 0; i < len(tags); i++ { + tag := strings.TrimSpace(tags[i].(string)) + if len(tag) > 0 { + sanitizedTags = append(sanitizedTags, tag) + } + } + sort.Strings(sanitizedTags) + return strings.Join(sanitizedTags, ";") +} + func resourceVirtualEnvironmentVMGetSerialDeviceValidator() schema.SchemaValidateDiagFunc { return validation.ToDiagFunc(func(i interface{}, k string) (s []string, es []error) { v, ok := i.(string) @@ -3220,6 +3252,23 @@ func resourceVirtualEnvironmentVMReadPrimitiveValues(d *schema.ResourceData, vmC diags = append(diags, diag.FromErr(err)...) } + currentTags := d.Get(mkResourceVirtualEnvironmentVMTags).([]interface{}) + + if len(clone) == 0 || len(currentTags) > 0 { + var tags []string + if vmConfig.Tags != nil { + for _, tag := range strings.Split(*vmConfig.Tags, ";") { + t := strings.TrimSpace(tag) + if len(t) > 0 { + tags = append(tags, t) + } + } + sort.Strings(tags) + } + err = d.Set(mkResourceVirtualEnvironmentVMTags, tags) + diags = append(diags, diag.FromErr(err)...) + } + currentKeyboardLayout := d.Get(mkResourceVirtualEnvironmentVMKeyboardLayout).(string) if len(clone) == 0 || currentKeyboardLayout != dvResourceVirtualEnvironmentVMKeyboardLayout { @@ -3337,6 +3386,11 @@ func resourceVirtualEnvironmentVMUpdate(ctx context.Context, d *schema.ResourceD updateBody.Description = &description } + if d.HasChange(mkResourceVirtualEnvironmentVMTags) { + tagString := resourceVirtualEnvironmentVMGetTagsString(d) + updateBody.Tags = &tagString + } + if d.HasChange(mkResourceVirtualEnvironmentVMKeyboardLayout) { keyboardLayout := d.Get(mkResourceVirtualEnvironmentVMKeyboardLayout).(string) updateBody.KeyboardLayout = &keyboardLayout