Skip to content

Commit

Permalink
Added patch operations for cluster network attachments
Browse files Browse the repository at this point in the history
  • Loading branch information
ujjwal-ibm committed Dec 5, 2024
1 parent a054149 commit 35eb929
Showing 1 changed file with 256 additions and 4 deletions.
260 changes: 256 additions & 4 deletions ibm/service/vpc/resource_ibm_is_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,11 @@ func ResourceIBMISInstance() *schema.Resource {

// cluster changes
"cluster_network_attachments": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Computed: true,
Description: "The cluster network attachments for this virtual server instance.The cluster network attachments are ordered for consistent instance configuration.",
Type: schema.TypeList,
Optional: true,
Computed: true,
DiffSuppressFunc: diffSuppressClusterNetworkAttachment,
Description: "The cluster network attachments for this virtual server instance.The cluster network attachments are ordered for consistent instance configuration.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"cluster_network_interface": &schema.Schema{
Expand Down Expand Up @@ -8151,3 +8152,254 @@ func setVolumePrototypesInState(d *schema.ResourceData, instance *vpcv1.Instance

return finalList, nil
}

// diffSuppressClusterNetworkAttachment handles comparing old and new cluster network attachments
// to determine if there are actual changes that require an update
func diffSuppressClusterNetworkAttachment(k, old, new string, d *schema.ResourceData) bool {
// If values are equal, no changes needed
if old == new {
return true
}

// Get the lists of old and new attachments
oldAttachments, newAttachments := []interface{}{}, []interface{}{}
if v, ok := d.GetOk("cluster_network_attachments"); ok {
newAttachments = v.([]interface{})
}
if v, ok := d.GetOk("cluster_network_attachments"); ok {
oldAttachments = v.([]interface{})
}

// If lengths differ, there are definitely changes
if len(oldAttachments) != len(newAttachments) {
return false
}

// Compare each attachment
for i := range oldAttachments {
oldAttach := oldAttachments[i].(map[string]interface{})
newAttach := newAttachments[i].(map[string]interface{})

// Compare cluster_network_interface
oldInterface := oldAttach["cluster_network_interface"].([]interface{})[0].(map[string]interface{})
newInterface := newAttach["cluster_network_interface"].([]interface{})[0].(map[string]interface{})

// Compare key properties
if !compareInterfaces(oldInterface, newInterface) {
return false
}
}

return true
}

// compareInterfaces compares the key properties of cluster network interfaces
func compareInterfaces(old, new map[string]interface{}) bool {
// Compare auto_delete
if old["auto_delete"] != new["auto_delete"] {
return false
}

// Compare name
if old["name"] != new["name"] {
return false
}

// Compare primary_ip
oldPrimaryIP := old["primary_ip"].([]interface{})
newPrimaryIP := new["primary_ip"].([]interface{})
if len(oldPrimaryIP) != len(newPrimaryIP) {
return false
}
if len(oldPrimaryIP) > 0 {
if !comparePrimaryIP(oldPrimaryIP[0].(map[string]interface{}), newPrimaryIP[0].(map[string]interface{})) {
return false
}
}

// Compare subnet
oldSubnet := old["subnet"].([]interface{})
newSubnet := new["subnet"].([]interface{})
if len(oldSubnet) != len(newSubnet) {
return false
}
if len(oldSubnet) > 0 {
if !compareSubnet(oldSubnet[0].(map[string]interface{}), newSubnet[0].(map[string]interface{})) {
return false
}
}

return true
}

// comparePrimaryIP compares primary IP configurations
func comparePrimaryIP(old, new map[string]interface{}) bool {
return old["id"] == new["id"] &&
old["address"] == new["address"] &&
old["auto_delete"] == new["auto_delete"] &&
old["name"] == new["name"]
}

// compareSubnet compares subnet configurations
func compareSubnet(old, new map[string]interface{}) bool {
return old["id"] == new["id"]
}
func handleClusterNetworkAttachmentUpdate(d *schema.ResourceData, instanceC *vpcv1.VpcV1) error {
if d.HasChange("cluster_network_attachments") && !d.IsNewResource() {
old, new := d.GetChange("cluster_network_attachments")
oldAttachments := old.([]interface{})
newAttachments := new.([]interface{})

// Track attachments to remove, add, and update
toRemove := []string{}
toAdd := []map[string]interface{}{}
toUpdate := []map[string]interface{}{}

// Build map of old attachments by name for easier lookup
oldAttachMap := make(map[string]map[string]interface{})
for _, attachment := range oldAttachments {
attach := attachment.(map[string]interface{})
oldAttachMap[attach["name"].(string)] = attach
}

// Analyze new attachments
for _, attachment := range newAttachments {
attach := attachment.(map[string]interface{})
name := attach["name"].(string)

if oldAttach, exists := oldAttachMap[name]; exists {
// Check if update needed
if !compareInterfaces(
oldAttach["cluster_network_interface"].([]interface{})[0].(map[string]interface{}),
attach["cluster_network_interface"].([]interface{})[0].(map[string]interface{}),
) {
toUpdate = append(toUpdate, attach)
}
delete(oldAttachMap, name)
} else {
toAdd = append(toAdd, attach)
}
}

// Remaining old attachments need to be removed
for _, attach := range oldAttachMap {
if id, ok := attach["id"].(string); ok {
toRemove = append(toRemove, id)
}
}

// Process removals
instanceID := d.Id()
for _, id := range toRemove {
deleteOptions := &vpcv1.DeleteInstanceClusterNetworkAttachmentOptions{
InstanceID: &instanceID,
ID: &id,
}
_, _, err := instanceC.DeleteInstanceClusterNetworkAttachment(deleteOptions)
if err != nil {
return fmt.Errorf("error removing cluster network attachment: %v", err)
}
}

// Process additions
for _, attach := range toAdd {
createOptions := buildCreateClusterNetworkAttachmentOptions(d.Id(), attach)
_, _, err := instanceC.CreateClusterNetworkAttachment(createOptions)
if err != nil {
return fmt.Errorf("error adding cluster network attachment: %v", err)
}
}

// Process updates
for _, attach := range toUpdate {
updateOptions := buildUpdateClusterNetworkAttachmentOptions(d.Id(), attach)
_, _, err := instanceC.UpdateInstanceClusterNetworkAttachment(updateOptions)
if err != nil {
return fmt.Errorf("error updating cluster network attachment: %v", err)
}
}
}
return nil
}

func buildCreateClusterNetworkAttachmentOptions(instanceID string, attachment map[string]interface{}) *vpcv1.CreateClusterNetworkAttachmentOptions {
// Extract network interface data
networkInterface := attachment["cluster_network_interface"].([]interface{})[0].(map[string]interface{})

// Build cluster network interface
clusterNetworkInterface := &vpcv1.InstanceClusterNetworkAttachmentPrototypeClusterNetworkInterface{}

if autoDelete, ok := networkInterface["auto_delete"].(bool); ok {
clusterNetworkInterface.AutoDelete = &autoDelete
}

if name, ok := networkInterface["name"].(string); ok {
clusterNetworkInterface.Name = &name
}

// Handle primary IP if present
if primaryIPList, ok := networkInterface["primary_ip"].([]interface{}); ok && len(primaryIPList) > 0 {
primaryIP := primaryIPList[0].(map[string]interface{})
primaryIPPrototype := &vpcv1.ClusterNetworkInterfacePrimaryIPPrototype{}

if address, ok := primaryIP["address"].(string); ok {
primaryIPPrototype.Address = &address
}
if autoDelete, ok := primaryIP["auto_delete"].(bool); ok {
primaryIPPrototype.AutoDelete = &autoDelete
}
if name, ok := primaryIP["name"].(string); ok {
primaryIPPrototype.Name = &name
}

clusterNetworkInterface.PrimaryIP = primaryIPPrototype
}

// Handle subnet if present
if subnetList, ok := networkInterface["subnet"].([]interface{}); ok && len(subnetList) > 0 {
subnet := subnetList[0].(map[string]interface{})
if id, ok := subnet["id"].(string); ok {
clusterNetworkInterface.Subnet = &vpcv1.ClusterNetworkSubnetIdentity{
ID: &id,
}
}
}

// Get attachment name
attachmentName := attachment["name"].(string)

// Create the options struct
createOptions := &vpcv1.CreateClusterNetworkAttachmentOptions{
InstanceID: &instanceID,
Name: &attachmentName,
ClusterNetworkInterface: clusterNetworkInterface,
}

return createOptions
}

func buildUpdateClusterNetworkAttachmentOptions(instanceID string, attachment map[string]interface{}) *vpcv1.UpdateInstanceClusterNetworkAttachmentOptions {
// Extract network interface data
networkInterface := attachment["cluster_network_interface"].([]interface{})[0].(map[string]interface{})

// Build cluster network interface patch

clusterNetworkInterface := &vpcv1.InstanceClusterNetworkAttachmentPatch{}

if name, ok := networkInterface["name"].(string); ok {
clusterNetworkInterface.Name = &name
}
clusterNetworkInterfaceAsPatch, _ := clusterNetworkInterface.AsPatch()

// Get attachment ID and name
attachmentID := attachment["id"].(string)

// Create the options struct
updateOptions := &vpcv1.UpdateInstanceClusterNetworkAttachmentOptions{
InstanceID: &instanceID,
ID: &attachmentID,
InstanceClusterNetworkAttachmentPatch: clusterNetworkInterfaceAsPatch,
}

return updateOptions
}

0 comments on commit 35eb929

Please sign in to comment.