Skip to content

Commit

Permalink
fix: add vm to existing vm group (hashicorp#2260)
Browse files Browse the repository at this point in the history
Updates `r/sphere_compute_cluster_vm_group` to allow for additional virtual machines to be adding or removed from a VM Group.

Ref: hashicorp#1878

Signed-off-by: Jared Burns <[email protected]>
  • Loading branch information
burnsjared0415 authored Sep 16, 2024
1 parent 46c4ce8 commit 6b14937
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 2 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# <!-- markdownlint-disable first-line-h1 no-inline-html -->

## 2.9.2 (nNot Released)

BUG FIX:
* `resource/vsphere_compute_cluster_vm_group`: Updates resource to allow for additional virtual
machines to be adding or removed from a VM Group. Must be ran in conjunction with and import.
([#2260]https://github.com/hashicorp/terraform-provider-vsphere/pull/2260)

## 2.9.1 (September 9, 2024)

BUG FIX:
Expand Down
119 changes: 117 additions & 2 deletions vsphere/resource_vsphere_compute_cluster_vm_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package vsphere

import (
"context"
"encoding/json"
"errors"
"fmt"
Expand Down Expand Up @@ -63,6 +64,17 @@ func resourceVSphereComputeClusterVMGroupCreate(d *schema.ResourceData, meta int
return err
}

// Check if the VM group already exists
exists, err := resourceVSphereComputeClusterVMGroupFindEntry(cluster, name)
if err != nil {
return err
}

if exists != nil {
log.Printf("[DEBUG] %s: VM group already exists, calling update", exists.Name)
return resourceVSphereComputeClusterVMGroupUpdate(d, meta)
}

info, err := expandClusterVMGroup(d, meta, name)
if err != nil {
return err
Expand Down Expand Up @@ -140,17 +152,82 @@ func resourceVSphereComputeClusterVMGroupUpdate(d *schema.ResourceData, meta int
return err
}

info, err := expandClusterVMGroup(d, meta, name)
// Retrieve the existing VM group information.
existingGroup, err := getCurrentVMsInGroup(cluster, name)
if err != nil {
return err
}

// Check if existingGroup is nil.
if existingGroup == nil {
return fmt.Errorf("VM group %s not found", name)
}

// Expand the new VM group information.
newInfo, err := expandClusterVMGroup(d, meta, name)
if err != nil {
return err
}

// Convert existing and new virtual machines to string slices for diffVmGroup.
existingVMs := make([]string, len(existingGroup.Vm))
for i, vm := range existingGroup.Vm {
existingVMs[i] = vm.Value
}

newVMs := make([]string, len(newInfo.Vm))
for i, vm := range newInfo.Vm {
newVMs[i] = vm.Value
}

// Use diffVmGroup to find added and removed virtual machines from virtual machine group.
addedVMs, removedVMs := diffVmGroup(existingVMs, newVMs)

// Convert addedVMs and removedVMs back to ManagedObjectReference slices.
addedVMRefs := make([]types.ManagedObjectReference, len(addedVMs))
for i, vm := range addedVMs {
addedVMRefs[i] = types.ManagedObjectReference{
Type: "VirtualMachine",
Value: vm,
}
}

removedVMRefs := make([]types.ManagedObjectReference, len(removedVMs))
for i, vm := range removedVMs {
removedVMRefs[i] = types.ManagedObjectReference{
Type: "VirtualMachine",
Value: vm,
}
}

// Merge existing virtual machines with added virtual machines and remove duplicates.
mergedVMs := append(existingGroup.Vm, addedVMRefs...)
vmMap := make(map[types.ManagedObjectReference]bool)
for _, vm := range mergedVMs {
vmMap[vm] = true
}
for _, vm := range removedVMRefs {
delete(vmMap, vm)
}
uniqueVMs := make([]types.ManagedObjectReference, 0, len(vmMap))
for vm := range vmMap {
uniqueVMs = append(uniqueVMs, vm)
}

if len(uniqueVMs) == 0 {
return fmt.Errorf("the resultant set of virtual machines in the vm group cannot be empty")
}

// Update the VM group information with the merged list.
newInfo.Vm = uniqueVMs

spec := &types.ClusterConfigSpecEx{
GroupSpec: []types.ClusterGroupSpec{
{
ArrayUpdateSpec: types.ArrayUpdateSpec{
Operation: types.ArrayUpdateOperationEdit,
},
Info: info,
Info: newInfo,
},
},
}
Expand Down Expand Up @@ -397,3 +474,41 @@ func resourceVSphereComputeClusterVMGroupClient(meta interface{}) (*govmomi.Clie
}
return client, nil
}

func diffVmGroup(oldVMs, newVMs []string) ([]string, []string) {
oldVMMap := make(map[string]bool)
for _, vm := range oldVMs {
oldVMMap[vm] = true
}

var addedVMs, removedVMs []string
for _, vm := range newVMs {
if !oldVMMap[vm] {
addedVMs = append(addedVMs, vm)
}
delete(oldVMMap, vm)
}

for vm := range oldVMMap {
removedVMs = append(removedVMs, vm)
}

return addedVMs, removedVMs
}

// getCurrentVMsInGroup retrieves the current VMs in the specified VM group from the vSphere cluster.
func getCurrentVMsInGroup(cluster *object.ClusterComputeResource, groupName string) (*types.ClusterVmGroup, error) {
ctx := context.TODO()
groups, err := cluster.Configuration(ctx)
if err != nil {
return nil, err
}

for _, group := range groups.Group {
if vmGroup, ok := group.(*types.ClusterVmGroup); ok && vmGroup.Name == groupName {
return vmGroup, nil
}
}

return nil, fmt.Errorf("VM group %s not found", groupName)
}
5 changes: 5 additions & 0 deletions website/docs/r/compute_cluster_vm_group.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ resource. Make sure your names are unique across both resources.

[tf-vsphere-cluster-host-group-resource]: /docs/providers/vsphere/r/compute_cluster_host_group.html

~> **NOTE:** To update a existing VM group, you must first import the group with `import` command in
[Importing](#importing) section. When importing a VM group, validate that all virtual machines that
need to be in the group are included in the `virtual_machine_ids`; otherwise, any virtual machines
that are not in `virtual_machine_ids` the included will be removed from the group.

## Attribute Reference

The only attribute this resource exports is the `id` of the resource, which is
Expand Down

0 comments on commit 6b14937

Please sign in to comment.