Skip to content

Commit e50b298

Browse files
authored
fix(vpc): dns update fix on resolver system to delegated (IBM-Cloud#5937)
* fix(vpc): dns update fix on resolver system to delegated * Update resource_ibm_is_vpc.go * Update resource_ibm_is_vpc_test.go
1 parent 24d8a4b commit e50b298

File tree

3 files changed

+342
-7
lines changed

3 files changed

+342
-7
lines changed

ibm/service/vpc/resource_ibm_is_vpc.go

Lines changed: 80 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -207,10 +207,11 @@ func ResourceIBMISVPC() *schema.Resource {
207207
Description: "The VPC dns binding id whose DNS resolver provides the DNS server addresses for this VPC.",
208208
},
209209
"dns_binding_name": &schema.Schema{
210-
Type: schema.TypeString,
211-
Optional: true,
212-
Computed: true,
213-
Description: "The VPC dns binding name whose DNS resolver provides the DNS server addresses for this VPC.",
210+
Type: schema.TypeString,
211+
Optional: true,
212+
DiffSuppressFunc: suppressNullDnsBindingName,
213+
Computed: true,
214+
Description: "The VPC dns binding name whose DNS resolver provides the DNS server addresses for this VPC.",
214215
},
215216
"vpc_id": &schema.Schema{
216217
Type: schema.TypeString,
@@ -1349,6 +1350,8 @@ func vpcUpdate(d *schema.ResourceData, meta interface{}, id, name string, hasCha
13491350
isDnsResolverVPCCrnNull := false
13501351
isDnsResolverManualServerChange := false
13511352
isDnsResolverManualServerEtag := ""
1353+
deleteBinding := false
1354+
deleteDnsBindings := &vpcv1.DeleteVPCDnsResolutionBindingOptions{}
13521355
var dnsPatch *vpcv1.VpcdnsPatch
13531356
if d.HasChange(isVPCDns) {
13541357
dnsPatch = &vpcv1.VpcdnsPatch{}
@@ -1357,6 +1360,7 @@ func vpcUpdate(d *schema.ResourceData, meta interface{}, id, name string, hasCha
13571360
dnsPatch.EnableHub = core.BoolPtr(newEH.(bool))
13581361
}
13591362
if d.HasChange("dns.0.resolver") {
1363+
13601364
_, newResolver := d.GetChange("dns.0.resolver")
13611365

13621366
if newResolver != nil && len(newResolver.([]interface{})) > 0 {
@@ -1390,10 +1394,67 @@ func vpcUpdate(d *schema.ResourceData, meta interface{}, id, name string, hasCha
13901394

13911395
}
13921396
if d.HasChange("dns.0.resolver.0.type") {
1393-
_, newResolverType := d.GetChange("dns.0.resolver.0.type")
1397+
oldResolverType, newResolverType := d.GetChange("dns.0.resolver.0.type")
13941398
if newResolverType != nil && newResolverType.(string) != "" {
13951399
ResolverModel.Type = core.StringPtr(newResolverType.(string))
13961400
}
1401+
if oldResolverType != nil && newResolverType != nil && oldResolverType.(string) != "" && newResolverType.(string) != "" {
1402+
if oldResolverType.(string) == "system" && newResolverType.(string) == "delegated" {
1403+
vpcId := d.Get("dns.0.resolver.0.vpc_id").(string)
1404+
createDnsBindings := &vpcv1.CreateVPCDnsResolutionBindingOptions{
1405+
VPCID: core.StringPtr(d.Id()),
1406+
VPC: &vpcv1.VPCIdentity{
1407+
ID: &vpcId,
1408+
},
1409+
}
1410+
if bindingNameOk, ok := d.GetOk("dns.0.resolver.0.dns_binding_name"); ok {
1411+
bindingName := bindingNameOk.(string)
1412+
createDnsBindings.Name = &bindingName
1413+
}
1414+
_, response, err := sess.CreateVPCDnsResolutionBinding(createDnsBindings)
1415+
if err != nil {
1416+
exitError := false
1417+
log.Printf("[DEBUG] CreateVPCDnsResolutionBindingWithContext failed %s\n%s", err, response)
1418+
if response.StatusCode == 400 && strings.Contains(err.Error(), "This VPC already contains DNS Resolution Bindings") {
1419+
listVPCDnsResolutionBindingOptions := &vpcv1.ListVPCDnsResolutionBindingsOptions{
1420+
VPCID: createDnsBindings.VPCID,
1421+
}
1422+
1423+
pager, err := sess.NewVPCDnsResolutionBindingsPager(listVPCDnsResolutionBindingOptions)
1424+
if err != nil {
1425+
return fmt.Errorf("[ERROR] Error getting VPC dns bindings in CreateVPCDnsResolutionBindingWithContext: %s", err)
1426+
}
1427+
var allResults []vpcv1.VpcdnsResolutionBinding
1428+
for pager.HasNext() {
1429+
nextPage, err := pager.GetNext()
1430+
if err != nil {
1431+
return fmt.Errorf("[ERROR] Error getting VPC dns bindings pager next in CreateVPCDnsResolutionBindingWithContext: %s", err)
1432+
}
1433+
allResults = append(allResults, nextPage...)
1434+
}
1435+
for _, binding := range allResults {
1436+
if *binding.VPC.ID == vpcId {
1437+
log.Printf("[DEBUG] CreateVPCDnsResolutionBindingWithContext failed but binding to same vpc exists %s\n%s", err, response)
1438+
exitError = true
1439+
}
1440+
}
1441+
}
1442+
if !exitError {
1443+
return fmt.Errorf("[ERROR] CreateVPCDnsResolutionBinding failed in vpc update resource %s\n%s", err, response)
1444+
}
1445+
}
1446+
}
1447+
if _, ok := d.GetOk("dns.0.resolver.0.dns_binding_name"); ok {
1448+
}
1449+
if d.HasChange("dns.0.resolver.0.dns_binding_name") && d.Get("dns.0.resolver.0.dns_binding_name").(string) == "null" {
1450+
dnsid := d.Get("dns.0.resolver.0.dns_binding_id").(string)
1451+
deleteBinding = true
1452+
deleteDnsBindings = &vpcv1.DeleteVPCDnsResolutionBindingOptions{
1453+
VPCID: core.StringPtr(d.Id()),
1454+
ID: &dnsid,
1455+
}
1456+
}
1457+
}
13971458
}
13981459
if d.HasChange("dns.0.resolver.0.vpc_id") {
13991460
_, newResolverVpc := d.GetChange("dns.0.resolver.0.vpc_id")
@@ -1475,6 +1536,13 @@ func vpcUpdate(d *schema.ResourceData, meta interface{}, id, name string, hasCha
14751536
return fmt.Errorf("[ERROR] Error Updating VPC : %s\n%s", err, response)
14761537
}
14771538
}
1539+
if deleteBinding && *deleteDnsBindings.VPCID != "" {
1540+
_, response, err := sess.DeleteVPCDnsResolutionBinding(deleteDnsBindings)
1541+
if err != nil {
1542+
log.Printf("[DEBUG] DeleteVPCDnsResolutionBindingWithContext failed %s\n%s", err, response)
1543+
return fmt.Errorf("[ERROR] DeleteVPCDnsResolutionBinding failed in vpc update resource %s\n%s", err, response)
1544+
}
1545+
}
14781546
if isDnsResolverVPCCrnNull || isDnsResolverVPCIDNull {
14791547

14801548
dnsList := make([]map[string]interface{}, 0)
@@ -1670,6 +1738,13 @@ func suppressNullVPC(k, old, new string, d *schema.ResourceData) bool {
16701738
return false
16711739
}
16721740

1741+
func suppressNullDnsBindingName(k, old, new string, d *schema.ResourceData) bool {
1742+
if new != old && new == "null" && old == "" && d.Id() != "" {
1743+
return true
1744+
}
1745+
return false
1746+
}
1747+
16731748
func hashManualServersList(v interface{}) int {
16741749
var buf bytes.Buffer
16751750
a := v.(map[string]interface{})

ibm/service/vpc/resource_ibm_is_vpc_test.go

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,3 +1011,248 @@ func TestAccIBMISVPC_basicAddressPrefix(t *testing.T) {
10111011
},
10121012
})
10131013
}
1014+
1015+
// VPC DNS fix
1016+
// TestAccIBMISVPC_ResolverTypeTransition tests the transition of resolver types in a VPC.
1017+
func TestAccIBMISVPC_ResolverTypeTransition(t *testing.T) {
1018+
var vpc string
1019+
vpcname1 := fmt.Sprintf("tf-vpc-hub-true-%d", acctest.RandIntRange(10, 100))
1020+
vpcname2 := fmt.Sprintf("tf-vpc-hub-false-%d", acctest.RandIntRange(10, 100))
1021+
1022+
resource.Test(t, resource.TestCase{
1023+
PreCheck: func() { acc.TestAccPreCheck(t) },
1024+
Providers: acc.TestAccProviders,
1025+
CheckDestroy: testAccCheckIBMISVPCDestroy,
1026+
Steps: []resource.TestStep{
1027+
// Step 1: Initial setup with system resolver
1028+
{
1029+
Config: testAccCheckIBMISVPCResolverSystemConfig(vpcname1, vpcname2),
1030+
Check: resource.ComposeTestCheckFunc(
1031+
testAccCheckIBMISVPCExists("ibm_is_vpc.hub_false_delegated", vpc),
1032+
resource.TestCheckResourceAttr(
1033+
"ibm_is_vpc.hub_false_delegated", "name", vpcname2),
1034+
resource.TestCheckResourceAttr(
1035+
"ibm_is_vpc.hub_false_delegated", "dns.0.enable_hub", "false"),
1036+
resource.TestCheckResourceAttr(
1037+
"ibm_is_vpc.hub_false_delegated", "dns.0.resolver.0.type", "system"),
1038+
resource.TestCheckResourceAttr(
1039+
"ibm_is_vpc.hub_false_delegated", "dns.0.resolver.0.vpc_id", ""),
1040+
resource.TestCheckResourceAttr(
1041+
"ibm_is_vpc.hub_false_delegated", "dns.0.resolver.0.dns_binding_name", ""),
1042+
),
1043+
},
1044+
// Step 2: Change to delegated resolver
1045+
{
1046+
Config: testAccCheckIBMISVPCCustomResolverDelegatedConfig(vpcname1, vpcname2),
1047+
Check: resource.ComposeTestCheckFunc(
1048+
testAccCheckIBMISVPCExists("ibm_is_vpc.hub_false_delegated", vpc),
1049+
resource.TestCheckResourceAttr(
1050+
"ibm_is_vpc.hub_false_delegated", "dns.0.resolver.0.type", "delegated"),
1051+
resource.TestCheckResourceAttrSet(
1052+
"ibm_is_vpc.hub_false_delegated", "dns.0.resolver.0.vpc_id"),
1053+
resource.TestCheckResourceAttr(
1054+
"ibm_is_vpc.hub_false_delegated", "dns.0.resolver.0.dns_binding_name", "test-delegation"),
1055+
),
1056+
},
1057+
// Step 3: Change back to system resolver
1058+
{
1059+
Config: testAccCheckIBMISVPCCustomResolverDelegatedToSystemConfig(vpcname1, vpcname2),
1060+
Check: resource.ComposeTestCheckFunc(
1061+
testAccCheckIBMISVPCExists("ibm_is_vpc.hub_false_delegated", vpc),
1062+
resource.TestCheckResourceAttr(
1063+
"ibm_is_vpc.hub_false_delegated", "dns.0.resolver.0.type", "system"),
1064+
resource.TestCheckResourceAttr(
1065+
"ibm_is_vpc.hub_false_delegated", "dns.0.resolver.0.vpc_id", ""),
1066+
resource.TestCheckResourceAttr(
1067+
"ibm_is_vpc.hub_false_delegated", "dns.0.resolver.0.dns_binding_name", ""),
1068+
),
1069+
},
1070+
},
1071+
})
1072+
}
1073+
1074+
// Helper function to generate config for resolver type transitions
1075+
func testAccCheckIBMISVPCResolverSystemConfig(vpcname1, vpcname2 string) string {
1076+
return fmt.Sprintf(`
1077+
resource "ibm_is_vpc" "hub_true" {
1078+
name = "%s"
1079+
dns {
1080+
enable_hub = true
1081+
}
1082+
}
1083+
resource "ibm_is_subnet" "hub_true_sub1" {
1084+
name = "hub-true-subnet1"
1085+
vpc = ibm_is_vpc.hub_true.id
1086+
zone = "%s"
1087+
total_ipv4_address_count = 16
1088+
}
1089+
resource "ibm_is_subnet" "hub_true_sub2" {
1090+
name = "hub-true-subnet2"
1091+
vpc = ibm_is_vpc.hub_true.id
1092+
zone = "%s"
1093+
total_ipv4_address_count = 16
1094+
}
1095+
resource "ibm_resource_instance" "dns-cr-instance" {
1096+
name = "dns-cr-instance"
1097+
resource_group_id = data.ibm_resource_group.rg.id
1098+
location = "global"
1099+
service = "dns-svcs"
1100+
plan = "standard-dns"
1101+
}
1102+
resource "ibm_dns_custom_resolver" "test_hub_true" {
1103+
name = "test-hub-true-customresolver"
1104+
instance_id = ibm_resource_instance.dns-cr-instance.guid
1105+
description = "new test CR - TF"
1106+
high_availability = true
1107+
enabled = true
1108+
locations {
1109+
subnet_crn = ibm_is_subnet.hub_true_sub1.crn
1110+
enabled = true
1111+
}
1112+
locations {
1113+
subnet_crn = ibm_is_subnet.hub_true_sub2.crn
1114+
enabled = true
1115+
}
1116+
}
1117+
// delegated vpc
1118+
resource "ibm_is_vpc" "hub_false_delegated" {
1119+
depends_on = [ibm_dns_custom_resolver.test_hub_true]
1120+
name = "%s"
1121+
dns {
1122+
enable_hub = false
1123+
resolver {
1124+
type = "system"
1125+
}
1126+
}
1127+
}
1128+
1129+
data "ibm_resource_group" "rg" {
1130+
is_default = true
1131+
}
1132+
`, vpcname1, acc.ISZoneName, acc.ISZoneName, vpcname2)
1133+
}
1134+
1135+
// Helper function to generate config for custom resolver with hub VPC
1136+
func testAccCheckIBMISVPCCustomResolverDelegatedConfig(vpcname1, vpcname2 string) string {
1137+
return fmt.Sprintf(`
1138+
resource "ibm_is_vpc" "hub_true" {
1139+
name = "%s"
1140+
dns {
1141+
enable_hub = true
1142+
}
1143+
}
1144+
resource "ibm_is_subnet" "hub_true_sub1" {
1145+
name = "hub-true-subnet1"
1146+
vpc = ibm_is_vpc.hub_true.id
1147+
zone = "%s"
1148+
total_ipv4_address_count = 16
1149+
}
1150+
resource "ibm_is_subnet" "hub_true_sub2" {
1151+
name = "hub-true-subnet2"
1152+
vpc = ibm_is_vpc.hub_true.id
1153+
zone = "%s"
1154+
total_ipv4_address_count = 16
1155+
}
1156+
resource "ibm_resource_instance" "dns-cr-instance" {
1157+
name = "dns-cr-instance"
1158+
resource_group_id = data.ibm_resource_group.rg.id
1159+
location = "global"
1160+
service = "dns-svcs"
1161+
plan = "standard-dns"
1162+
}
1163+
resource "ibm_dns_custom_resolver" "test_hub_true" {
1164+
name = "test-hub-true-customresolver"
1165+
instance_id = ibm_resource_instance.dns-cr-instance.guid
1166+
description = "new test CR - TF"
1167+
high_availability = true
1168+
enabled = true
1169+
locations {
1170+
subnet_crn = ibm_is_subnet.hub_true_sub1.crn
1171+
enabled = true
1172+
}
1173+
locations {
1174+
subnet_crn = ibm_is_subnet.hub_true_sub2.crn
1175+
enabled = true
1176+
}
1177+
}
1178+
// delegated vpc
1179+
resource "ibm_is_vpc" "hub_false_delegated" {
1180+
depends_on = [ibm_dns_custom_resolver.test_hub_true]
1181+
name = "%s"
1182+
dns {
1183+
enable_hub = false
1184+
resolver {
1185+
type = "delegated"
1186+
dns_binding_name = "test-delegation"
1187+
vpc_id = ibm_is_vpc.hub_true.id
1188+
}
1189+
}
1190+
}
1191+
1192+
data "ibm_resource_group" "rg" {
1193+
is_default = true
1194+
}
1195+
`, vpcname1, acc.ISZoneName, acc.ISZoneName, vpcname2)
1196+
}
1197+
1198+
func testAccCheckIBMISVPCCustomResolverDelegatedToSystemConfig(vpcname1, vpcname2 string) string {
1199+
return fmt.Sprintf(`
1200+
resource "ibm_is_vpc" "hub_true" {
1201+
name = "%s"
1202+
dns {
1203+
enable_hub = true
1204+
}
1205+
}
1206+
resource "ibm_is_subnet" "hub_true_sub1" {
1207+
name = "hub-true-subnet1"
1208+
vpc = ibm_is_vpc.hub_true.id
1209+
zone = "%s"
1210+
total_ipv4_address_count = 16
1211+
}
1212+
resource "ibm_is_subnet" "hub_true_sub2" {
1213+
name = "hub-true-subnet2"
1214+
vpc = ibm_is_vpc.hub_true.id
1215+
zone = "%s"
1216+
total_ipv4_address_count = 16
1217+
}
1218+
resource "ibm_resource_instance" "dns-cr-instance" {
1219+
name = "dns-cr-instance"
1220+
resource_group_id = data.ibm_resource_group.rg.id
1221+
location = "global"
1222+
service = "dns-svcs"
1223+
plan = "standard-dns"
1224+
}
1225+
resource "ibm_dns_custom_resolver" "test_hub_true" {
1226+
name = "test-hub-true-customresolver"
1227+
instance_id = ibm_resource_instance.dns-cr-instance.guid
1228+
description = "new test CR - TF"
1229+
high_availability = true
1230+
enabled = true
1231+
locations {
1232+
subnet_crn = ibm_is_subnet.hub_true_sub1.crn
1233+
enabled = true
1234+
}
1235+
locations {
1236+
subnet_crn = ibm_is_subnet.hub_true_sub2.crn
1237+
enabled = true
1238+
}
1239+
}
1240+
// delegated vpc
1241+
resource "ibm_is_vpc" "hub_false_delegated" {
1242+
depends_on = [ibm_dns_custom_resolver.test_hub_true]
1243+
name = "%s"
1244+
dns {
1245+
enable_hub = false
1246+
resolver {
1247+
type = "system"
1248+
dns_binding_name = "null"
1249+
vpc_id = "null"
1250+
}
1251+
}
1252+
}
1253+
1254+
data "ibm_resource_group" "rg" {
1255+
is_default = true
1256+
}
1257+
`, vpcname1, acc.ISZoneName, acc.ISZoneName, vpcname2)
1258+
}

0 commit comments

Comments
 (0)