Skip to content

Commit f383f84

Browse files
committed
added test case and update scenarios
1 parent 778cab2 commit f383f84

File tree

2 files changed

+303
-84
lines changed

2 files changed

+303
-84
lines changed

ibm/service/vpc/resource_ibm_is_virtual_network_interface.go

Lines changed: 201 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,20 @@
44
package vpc
55

66
import (
7+
"bytes"
78
"context"
9+
"encoding/json"
810
"fmt"
911
"log"
12+
"time"
1013

14+
"github.com/IBM-Cloud/terraform-provider-ibm/ibm/conns"
1115
"github.com/IBM-Cloud/terraform-provider-ibm/ibm/flex"
1216
"github.com/IBM-Cloud/terraform-provider-ibm/ibm/validate"
1317
"github.com/IBM/go-sdk-core/v5/core"
1418
"github.com/IBM/vpc-go-sdk/vpcv1"
1519
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
20+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
1621
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
1722
)
1823

@@ -44,9 +49,10 @@ func ResourceIBMIsVirtualNetworkInterface() *schema.Resource {
4449
Description: "If `true`:- The VPC infrastructure performs any needed NAT operations.- `floating_ips` must not have more than one floating IP.If `false`:- Packets are passed unchanged to/from the network interface, allowing the workload to perform any needed NAT operations.- `allow_ip_spoofing` must be `false`.- If the virtual network interface is attached: - The target `resource_type` must be `bare_metal_server_network_attachment`. - The target `interface_type` must not be `hipersocket`.",
4550
},
4651
"ips": &schema.Schema{
47-
Type: schema.TypeList,
52+
Type: schema.TypeSet,
4853
Optional: true,
4954
Computed: true,
55+
Set: hashIpsList,
5056
Description: "The reserved IPs bound to this virtual network interface.May be empty when `lifecycle_state` is `pending`.",
5157
Elem: &schema.Resource{
5258
Schema: map[string]*schema.Schema{
@@ -84,6 +90,7 @@ func ResourceIBMIsVirtualNetworkInterface() *schema.Resource {
8490
"reserved_ip": &schema.Schema{
8591
Type: schema.TypeString,
8692
Optional: true,
93+
Computed: true,
8794
Description: "The unique identifier for this reserved IP.",
8895
},
8996
"name": &schema.Schema{
@@ -170,7 +177,6 @@ func ResourceIBMIsVirtualNetworkInterface() *schema.Resource {
170177
Type: schema.TypeSet,
171178
Optional: true,
172179
Computed: true,
173-
ForceNew: true,
174180
Elem: &schema.Schema{Type: schema.TypeString},
175181
Set: schema.HashString,
176182
Description: "The security groups for this virtual network interface.",
@@ -347,7 +353,7 @@ func resourceIBMIsVirtualNetworkInterfaceCreate(context context.Context, d *sche
347353
}
348354
if _, ok := d.GetOk("ips"); ok {
349355
var ips []vpcv1.VirtualNetworkInterfaceIPPrototypeIntf
350-
for _, v := range d.Get("ips").([]interface{}) {
356+
for _, v := range d.Get("ips").(*schema.Set).List() {
351357
value := v.(map[string]interface{})
352358
ipsItem, err := resourceIBMIsVirtualNetworkInterfaceMapToVirtualNetworkInterfaceIPsReservedIPPrototype(value)
353359
if err != nil {
@@ -427,35 +433,37 @@ func resourceIBMIsVirtualNetworkInterfaceRead(context context.Context, d *schema
427433

428434
if !core.IsNil(virtualNetworkInterface.AllowIPSpoofing) {
429435
if err = d.Set("allow_ip_spoofing", virtualNetworkInterface.AllowIPSpoofing); err != nil {
430-
return diag.FromErr(fmt.Errorf("Error setting allow_ip_spoofing: %s", err))
436+
return diag.FromErr(fmt.Errorf("[ERROR] Error setting allow_ip_spoofing: %s", err))
431437
}
432438
}
433439
if !core.IsNil(virtualNetworkInterface.AutoDelete) {
434440
if err = d.Set("auto_delete", virtualNetworkInterface.AutoDelete); err != nil {
435-
return diag.FromErr(fmt.Errorf("Error setting auto_delete: %s", err))
441+
return diag.FromErr(fmt.Errorf("[ERROR] Error setting auto_delete: %s", err))
436442
}
437443
}
438444
if !core.IsNil(virtualNetworkInterface.EnableInfrastructureNat) {
439445
if err = d.Set("enable_infrastructure_nat", virtualNetworkInterface.EnableInfrastructureNat); err != nil {
440-
return diag.FromErr(fmt.Errorf("Error setting enable_infrastructure_nat: %s", err))
446+
return diag.FromErr(fmt.Errorf("[ERROR] Error setting enable_infrastructure_nat: %s", err))
441447
}
442448
}
443449
if !core.IsNil(virtualNetworkInterface.Ips) {
444450
ips := []map[string]interface{}{}
445451
for _, ipsItem := range virtualNetworkInterface.Ips {
446-
ipsItemMap, err := resourceIBMIsVirtualNetworkInterfaceReservedIPReferenceToMap(&ipsItem)
447-
if err != nil {
448-
return diag.FromErr(err)
452+
if *virtualNetworkInterface.PrimaryIP.ID != *ipsItem.ID {
453+
ipsItemMap, err := resourceIBMIsVirtualNetworkInterfaceReservedIPReferenceToMap(&ipsItem)
454+
if err != nil {
455+
return diag.FromErr(err)
456+
}
457+
ips = append(ips, ipsItemMap)
449458
}
450-
ips = append(ips, ipsItemMap)
451459
}
452460
if err = d.Set("ips", ips); err != nil {
453-
return diag.FromErr(fmt.Errorf("Error setting ips: %s", err))
461+
return diag.FromErr(fmt.Errorf("[ERROR] Error setting ips: %s", err))
454462
}
455463
}
456464
if !core.IsNil(virtualNetworkInterface.Name) {
457465
if err = d.Set("name", virtualNetworkInterface.Name); err != nil {
458-
return diag.FromErr(fmt.Errorf("Error setting name: %s", err))
466+
return diag.FromErr(fmt.Errorf("[ERROR] Error setting name: %s", err))
459467
}
460468
}
461469
if !core.IsNil(virtualNetworkInterface.PrimaryIP) {
@@ -464,7 +472,7 @@ func resourceIBMIsVirtualNetworkInterfaceRead(context context.Context, d *schema
464472
return diag.FromErr(err)
465473
}
466474
if err = d.Set("primary_ip", []map[string]interface{}{primaryIPMap}); err != nil {
467-
return diag.FromErr(fmt.Errorf("Error setting primary_ip: %s", err))
475+
return diag.FromErr(fmt.Errorf("[ERROR] Error setting primary_ip: %s", err))
468476
}
469477
}
470478
if !core.IsNil(virtualNetworkInterface.ResourceGroup) {
@@ -485,32 +493,32 @@ func resourceIBMIsVirtualNetworkInterfaceRead(context context.Context, d *schema
485493
d.Set("subnet", virtualNetworkInterface.Subnet.ID)
486494
}
487495
if err = d.Set("created_at", flex.DateTimeToString(virtualNetworkInterface.CreatedAt)); err != nil {
488-
return diag.FromErr(fmt.Errorf("Error setting created_at: %s", err))
496+
return diag.FromErr(fmt.Errorf("[ERROR] Error setting created_at: %s", err))
489497
}
490498
if err = d.Set("crn", virtualNetworkInterface.CRN); err != nil {
491-
return diag.FromErr(fmt.Errorf("Error setting crn: %s", err))
499+
return diag.FromErr(fmt.Errorf("[ERROR] Error setting crn: %s", err))
492500
}
493501
if err = d.Set("href", virtualNetworkInterface.Href); err != nil {
494-
return diag.FromErr(fmt.Errorf("Error setting href: %s", err))
502+
return diag.FromErr(fmt.Errorf("[ERROR] Error setting href: %s", err))
495503
}
496504
if err = d.Set("lifecycle_state", virtualNetworkInterface.LifecycleState); err != nil {
497-
return diag.FromErr(fmt.Errorf("Error setting lifecycle_state: %s", err))
505+
return diag.FromErr(fmt.Errorf("[ERROR] Error setting lifecycle_state: %s", err))
498506
}
499507
if !core.IsNil(virtualNetworkInterface.MacAddress) {
500508
if err = d.Set("mac_address", virtualNetworkInterface.MacAddress); err != nil {
501-
return diag.FromErr(fmt.Errorf("Error setting mac_address: %s", err))
509+
return diag.FromErr(fmt.Errorf("[ERROR] Error setting mac_address: %s", err))
502510
}
503511
}
504512
if err = d.Set("resource_type", virtualNetworkInterface.ResourceType); err != nil {
505-
return diag.FromErr(fmt.Errorf("Error setting resource_type: %s", err))
513+
return diag.FromErr(fmt.Errorf("[ERROR] Error setting resource_type: %s", err))
506514
}
507515
if !core.IsNil(virtualNetworkInterface.Target) {
508516
targetMap, err := resourceIBMIsVirtualNetworkInterfaceVirtualNetworkInterfaceTargetToMap(virtualNetworkInterface.Target)
509517
if err != nil {
510518
return diag.FromErr(err)
511519
}
512520
if err = d.Set("target", []map[string]interface{}{targetMap}); err != nil {
513-
return diag.FromErr(fmt.Errorf("Error setting target: %s", err))
521+
return diag.FromErr(fmt.Errorf("[ERROR] Error setting target: %s", err))
514522
}
515523
} else {
516524
d.Set("target", nil)
@@ -520,7 +528,7 @@ func resourceIBMIsVirtualNetworkInterfaceRead(context context.Context, d *schema
520528
return diag.FromErr(err)
521529
}
522530
if err = d.Set("vpc", []map[string]interface{}{vpcMap}); err != nil {
523-
return diag.FromErr(fmt.Errorf("Error setting vpc: %s", err))
531+
return diag.FromErr(fmt.Errorf("[ERROR] Error setting vpc: %s", err))
524532
}
525533

526534
if virtualNetworkInterface.Zone != nil {
@@ -535,10 +543,10 @@ func resourceIBMIsVirtualNetworkInterfaceUpdate(context context.Context, d *sche
535543
if err != nil {
536544
return diag.FromErr(err)
537545
}
538-
546+
id := d.Id()
539547
updateVirtualNetworkInterfaceOptions := &vpcv1.UpdateVirtualNetworkInterfaceOptions{}
540548

541-
updateVirtualNetworkInterfaceOptions.SetID(d.Id())
549+
updateVirtualNetworkInterfaceOptions.SetID(id)
542550

543551
hasChange := false
544552

@@ -563,6 +571,130 @@ func resourceIBMIsVirtualNetworkInterfaceUpdate(context context.Context, d *sche
563571
patchVals.Name = &newName
564572
hasChange = true
565573
}
574+
if d.HasChange("ips") {
575+
oldips, newIPs := d.GetChange("ips")
576+
log.Printf("[INFO] vnip2 ipsoldmap old map %s", output(oldips))
577+
log.Printf("[INFO] vnip2 ipsnewmap new map %s", output(newIPs))
578+
579+
ov := oldips.(*schema.Set)
580+
nv := newIPs.(*schema.Set)
581+
remove := (ov.Difference(nv).List())
582+
add := (nv.Difference(ov).List())
583+
log.Printf("[INFO] vnip2 add map %s", output(add))
584+
log.Printf("[INFO] vnip2 remove map %s", output(remove))
585+
586+
if add != nil && len(add) > 0 {
587+
for _, ipItem := range add {
588+
value := ipItem.(map[string]interface{})
589+
if value["reserved_ip"] != nil && value["reserved_ip"].(string) != "" {
590+
reservedipid := value["reserved_ip"].(string)
591+
addVirtualNetworkInterfaceIPOptions := &vpcv1.AddVirtualNetworkInterfaceIPOptions{}
592+
addVirtualNetworkInterfaceIPOptions.SetVirtualNetworkInterfaceID(id)
593+
addVirtualNetworkInterfaceIPOptions.SetID(reservedipid)
594+
_, response, err := sess.AddVirtualNetworkInterfaceIPWithContext(context, addVirtualNetworkInterfaceIPOptions)
595+
if err != nil {
596+
log.Printf("[DEBUG] AddVirtualNetworkInterfaceIPWithContext failed in VirtualNetworkInterface patch %s\n%s", err, response)
597+
return diag.FromErr(fmt.Errorf("AddVirtualNetworkInterfaceIPWithContext failed in VirtualNetworkInterface patch %s\n%s", err, response))
598+
}
599+
}
600+
}
601+
}
602+
if remove != nil && len(remove) > 0 {
603+
subnetId := d.Get("subnet").(string)
604+
for _, ipItem := range remove {
605+
value := ipItem.(map[string]interface{})
606+
if value["reserved_ip"] != nil && value["reserved_ip"].(string) != "" {
607+
reservedipid := value["reserved_ip"].(string)
608+
removeVirtualNetworkInterfaceIPOptions := &vpcv1.RemoveVirtualNetworkInterfaceIPOptions{}
609+
removeVirtualNetworkInterfaceIPOptions.SetVirtualNetworkInterfaceID(id)
610+
removeVirtualNetworkInterfaceIPOptions.SetID(reservedipid)
611+
response, err := sess.RemoveVirtualNetworkInterfaceIPWithContext(context, removeVirtualNetworkInterfaceIPOptions)
612+
if err != nil {
613+
log.Printf("[DEBUG] RemoveVirtualNetworkInterfaceIPWithContext failed in VirtualNetworkInterface patch %s\n%s", err, response)
614+
return diag.FromErr(fmt.Errorf("RemoveVirtualNetworkInterfaceIPWithContext failed in VirtualNetworkInterface patch %s\n%s", err, response))
615+
}
616+
}
617+
if value["address"] != nil && value["address"].(string) != "" {
618+
reservedipid := value["reserved_ip"].(string)
619+
removeSubnetReservedIPOptions := &vpcv1.DeleteSubnetReservedIPOptions{}
620+
removeSubnetReservedIPOptions.SetSubnetID(subnetId)
621+
removeSubnetReservedIPOptions.SetID(reservedipid)
622+
response, err := sess.DeleteSubnetReservedIPWithContext(context, removeSubnetReservedIPOptions)
623+
if err != nil {
624+
log.Printf("[DEBUG] DeleteSubnetReservedIPWithContext failed in VirtualNetworkInterface patch %s\n%s", err, response)
625+
return diag.FromErr(fmt.Errorf("DeleteSubnetReservedIPWithContext failed in VirtualNetworkInterface patch %s\n%s", err, response))
626+
}
627+
}
628+
}
629+
}
630+
}
631+
if !d.IsNewResource() && d.HasChange("primary_ip") {
632+
subnetId := d.Get("subnet").(string)
633+
ripId := d.Get("primary_ip.0.reserved_ip").(string)
634+
updateripoptions := &vpcv1.UpdateSubnetReservedIPOptions{
635+
SubnetID: &subnetId,
636+
ID: &ripId,
637+
}
638+
reservedIpPath := &vpcv1.ReservedIPPatch{}
639+
if d.HasChange("primary_ip.0.name") {
640+
name := d.Get("primary_ip.0.name").(string)
641+
reservedIpPath.Name = &name
642+
}
643+
if d.HasChange("primary_ip.0.auto_delete") {
644+
auto := d.Get("primary_ip.0.auto_delete").(bool)
645+
reservedIpPath.AutoDelete = &auto
646+
}
647+
reservedIpPathAsPatch, err := reservedIpPath.AsPatch()
648+
if err != nil {
649+
return diag.FromErr(fmt.Errorf("[ERROR] Error calling reserved ip as patch on vni patch \n%s", err))
650+
}
651+
updateripoptions.ReservedIPPatch = reservedIpPathAsPatch
652+
_, response, err := sess.UpdateSubnetReservedIP(updateripoptions)
653+
if err != nil {
654+
return diag.FromErr(fmt.Errorf("[ERROR] Error updating vni reserved ip(%s): %s\n%s", ripId, err, response))
655+
}
656+
}
657+
if d.HasChange("security_groups") && !d.IsNewResource() {
658+
ovs, nvs := d.GetChange("security_groups")
659+
vniId := d.Id()
660+
ov := ovs.(*schema.Set)
661+
nv := nvs.(*schema.Set)
662+
remove := flex.ExpandStringList(ov.Difference(nv).List())
663+
add := flex.ExpandStringList(nv.Difference(ov).List())
664+
if len(add) > 0 {
665+
for i := range add {
666+
createsgnicoptions := &vpcv1.CreateSecurityGroupTargetBindingOptions{
667+
SecurityGroupID: &add[i],
668+
ID: &vniId,
669+
}
670+
_, response, err := sess.CreateSecurityGroupTargetBinding(createsgnicoptions)
671+
if err != nil {
672+
return diag.FromErr(fmt.Errorf("[ERROR] Error while creating security group %q for virtual network interface %s\n%s: %q", add[i], d.Id(), err, response))
673+
}
674+
_, err = isWaitForVirtualNetworkInterfaceAvailable(sess, vniId, d.Timeout(schema.TimeoutUpdate))
675+
if err != nil {
676+
return diag.FromErr(err)
677+
}
678+
}
679+
680+
}
681+
if len(remove) > 0 {
682+
for i := range remove {
683+
deletesgnicoptions := &vpcv1.DeleteSecurityGroupTargetBindingOptions{
684+
SecurityGroupID: &remove[i],
685+
ID: &vniId,
686+
}
687+
response, err := sess.DeleteSecurityGroupTargetBinding(deletesgnicoptions)
688+
if err != nil {
689+
return diag.FromErr(fmt.Errorf("[ERROR] Error while removing security group %q for virtual network interface %s\n%s: %q", remove[i], d.Id(), err, response))
690+
}
691+
_, err = isWaitForVirtualNetworkInterfaceAvailable(sess, vniId, d.Timeout(schema.TimeoutUpdate))
692+
if err != nil {
693+
return diag.FromErr(err)
694+
}
695+
}
696+
}
697+
}
566698

567699
if hasChange {
568700
updateVirtualNetworkInterfaceOptions.VirtualNetworkInterfacePatch, _ = patchVals.AsPatch()
@@ -747,6 +879,37 @@ func resourceIBMIsVirtualNetworkInterfaceVirtualNetworkInterfaceTargetShareMount
747879
return modelMap, nil
748880
}
749881

882+
func isWaitForVirtualNetworkInterfaceAvailable(client *vpcv1.VpcV1, id string, timeout time.Duration) (interface{}, error) {
883+
log.Printf("Waiting for VirtualNetworkInterface (%s) to be available.", id)
884+
885+
stateConf := &resource.StateChangeConf{
886+
Pending: []string{"", "pending"},
887+
Target: []string{"done", "failed", "stable"},
888+
Refresh: isVirtualNetworkInterfaceRefreshFunc(client, id),
889+
Timeout: timeout,
890+
Delay: 10 * time.Second,
891+
MinTimeout: 10 * time.Second,
892+
}
893+
894+
return stateConf.WaitForState()
895+
}
896+
897+
func isVirtualNetworkInterfaceRefreshFunc(client *vpcv1.VpcV1, id string) resource.StateRefreshFunc {
898+
return func() (interface{}, string, error) {
899+
vnigetoptions := &vpcv1.GetVirtualNetworkInterfaceOptions{
900+
ID: &id,
901+
}
902+
vni, response, err := client.GetVirtualNetworkInterface(vnigetoptions)
903+
if err != nil {
904+
return nil, "failed", fmt.Errorf("[ERROR] Error getting vni: %s\n%s", err, response)
905+
}
906+
if *vni.LifecycleState == "failed" || *vni.LifecycleState == "suspended" {
907+
return vni, *vni.LifecycleState, fmt.Errorf("[ERROR] Error VirtualNetworkInterface in : %s state", *vni.LifecycleState)
908+
}
909+
return vni, *vni.LifecycleState, nil
910+
}
911+
}
912+
750913
func resourceIBMIsVirtualNetworkInterfaceVirtualNetworkInterfaceTargetInstanceNetworkAttachmentReferenceVirtualNetworkInterfaceContextToMap(model *vpcv1.VirtualNetworkInterfaceTargetInstanceNetworkAttachmentReferenceVirtualNetworkInterfaceContext) (map[string]interface{}, error) {
751914
modelMap := make(map[string]interface{})
752915
// if model.Deleted != nil {
@@ -813,3 +976,18 @@ func resourceIBMIsVirtualNetworkInterfaceVPCReferenceDeletedToMap(model *vpcv1.V
813976
modelMap["more_info"] = model.MoreInfo
814977
return modelMap, nil
815978
}
979+
980+
func hashIpsList(v interface{}) int {
981+
var buf bytes.Buffer
982+
a := v.(map[string]interface{})
983+
buf.WriteString(fmt.Sprintf("%s-", a["address"].(string)))
984+
return conns.String(buf.String())
985+
}
986+
func output(vnimap interface{}) string {
987+
output, err := json.MarshalIndent(vnimap, "", " ")
988+
if err == nil {
989+
return fmt.Sprintf("block %+v\n", string(output))
990+
} else {
991+
return fmt.Sprintf("block : %#v", vnimap)
992+
}
993+
}

0 commit comments

Comments
 (0)