Skip to content

Commit

Permalink
fix(vpc): dns update fix on resolver system to delegated
Browse files Browse the repository at this point in the history
  • Loading branch information
uibm committed Jan 30, 2025
1 parent 710bb51 commit 48f4893
Show file tree
Hide file tree
Showing 3 changed files with 318 additions and 7 deletions.
58 changes: 53 additions & 5 deletions ibm/service/vpc/resource_ibm_is_vpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,10 +198,11 @@ func ResourceIBMISVPC() *schema.Resource {
Description: "The VPC dns binding id whose DNS resolver provides the DNS server addresses for this VPC.",
},
"dns_binding_name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
Description: "The VPC dns binding name whose DNS resolver provides the DNS server addresses for this VPC.",
Type: schema.TypeString,
Optional: true,
DiffSuppressFunc: suppressNullDnsBindingName,
Computed: true,
Description: "The VPC dns binding name whose DNS resolver provides the DNS server addresses for this VPC.",
},
"vpc_id": &schema.Schema{
Type: schema.TypeString,
Expand Down Expand Up @@ -1295,6 +1296,8 @@ func vpcUpdate(d *schema.ResourceData, meta interface{}, id, name string, hasCha
isDnsResolverVPCCrnNull := false
isDnsResolverManualServerChange := false
isDnsResolverManualServerEtag := ""
deleteBinding := false
deleteDnsBindings := &vpcv1.DeleteVPCDnsResolutionBindingOptions{}
var dnsPatch *vpcv1.VpcdnsPatch
if d.HasChange(isVPCDns) {
dnsPatch = &vpcv1.VpcdnsPatch{}
Expand All @@ -1303,6 +1306,7 @@ func vpcUpdate(d *schema.ResourceData, meta interface{}, id, name string, hasCha
dnsPatch.EnableHub = core.BoolPtr(newEH.(bool))
}
if d.HasChange("dns.0.resolver") {

_, newResolver := d.GetChange("dns.0.resolver")

if newResolver != nil && len(newResolver.([]interface{})) > 0 {
Expand Down Expand Up @@ -1336,10 +1340,40 @@ func vpcUpdate(d *schema.ResourceData, meta interface{}, id, name string, hasCha

}
if d.HasChange("dns.0.resolver.0.type") {
_, newResolverType := d.GetChange("dns.0.resolver.0.type")
oldResolverType, newResolverType := d.GetChange("dns.0.resolver.0.type")
if newResolverType != nil && newResolverType.(string) != "" {
ResolverModel.Type = core.StringPtr(newResolverType.(string))
}
if oldResolverType != nil && newResolverType != nil && oldResolverType.(string) != "" && newResolverType.(string) != "" {
if oldResolverType.(string) == "system" && newResolverType.(string) == "delegated" {
vpcId := d.Get("dns.0.resolver.0.vpc_id").(string)
createDnsBindings := &vpcv1.CreateVPCDnsResolutionBindingOptions{
VPCID: core.StringPtr(d.Id()),
VPC: &vpcv1.VPCIdentity{
ID: &vpcId,
},
}
if bindingNameOk, ok := d.GetOk("dns.0.resolver.0.dns_binding_name"); ok {
bindingName := bindingNameOk.(string)
createDnsBindings.Name = &bindingName
}
_, response, err := sess.CreateVPCDnsResolutionBinding(createDnsBindings)
if err != nil {
log.Printf("[DEBUG] CreateVPCDnsResolutionBindingWithContext failed %s\n%s", err, response)
return fmt.Errorf("[ERROR] CreateVPCDnsResolutionBinding failed in vpc update resource %s\n%s", err, response)
}
}
if _, ok := d.GetOk("dns.0.resolver.0.dns_binding_name"); ok {
}
if d.HasChange("dns.0.resolver.0.dns_binding_name") && d.Get("dns.0.resolver.0.dns_binding_name").(string) == "null" {
dnsid := d.Get("dns.0.resolver.0.dns_binding_id").(string)
deleteBinding = true
deleteDnsBindings = &vpcv1.DeleteVPCDnsResolutionBindingOptions{
VPCID: core.StringPtr(d.Id()),
ID: &dnsid,
}
}
}
}
if d.HasChange("dns.0.resolver.0.vpc_id") {
_, newResolverVpc := d.GetChange("dns.0.resolver.0.vpc_id")
Expand Down Expand Up @@ -1421,6 +1455,13 @@ func vpcUpdate(d *schema.ResourceData, meta interface{}, id, name string, hasCha
return fmt.Errorf("[ERROR] Error Updating VPC : %s\n%s", err, response)
}
}
if deleteBinding && *deleteDnsBindings.VPCID != "" {
_, response, err := sess.DeleteVPCDnsResolutionBinding(deleteDnsBindings)
if err != nil {
log.Printf("[DEBUG] DeleteVPCDnsResolutionBindingWithContext failed %s\n%s", err, response)
return fmt.Errorf("[ERROR] DeleteVPCDnsResolutionBinding failed in vpc update resource %s\n%s", err, response)
}
}
if isDnsResolverVPCCrnNull || isDnsResolverVPCIDNull {

dnsList := make([]map[string]interface{}, 0)
Expand Down Expand Up @@ -1616,6 +1657,13 @@ func suppressNullVPC(k, old, new string, d *schema.ResourceData) bool {
return false
}

func suppressNullDnsBindingName(k, old, new string, d *schema.ResourceData) bool {
if new != old && new == "null" && old == "" && d.Id() != "" {
return true
}
return false
}

func hashManualServersList(v interface{}) int {
var buf bytes.Buffer
a := v.(map[string]interface{})
Expand Down
248 changes: 248 additions & 0 deletions ibm/service/vpc/resource_ibm_is_vpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -962,3 +962,251 @@ func testAccCheckIBMISVPCNoSgAclRulesConfig(vpcname string) string {
`, vpcname)

}

// VPC DNS fix
// TestAccIBMISVPC_ResolverTypeTransition tests the transition of resolver types in a VPC.
func TestAccIBMISVPC_ResolverTypeTransition(t *testing.T) {
var vpc string
vpcname1 := fmt.Sprintf("tf-vpc-hub-true-%d", acctest.RandIntRange(10, 100))
vpcname2 := fmt.Sprintf("tf-vpc-hub-false-%d", acctest.RandIntRange(10, 100))

resource.Test(t, resource.TestCase{
PreCheck: func() { acc.TestAccPreCheck(t) },
Providers: acc.TestAccProviders,
CheckDestroy: testAccCheckIBMISVPCDestroy,
Steps: []resource.TestStep{
// Step 1: Initial setup with system resolver
{
Config: testAccCheckIBMISVPCResolverSystemConfig(vpcname1, vpcname2),
Check: resource.ComposeTestCheckFunc(
testAccCheckIBMISVPCExists("ibm_is_vpc.hub_false_delegated", vpc),
resource.TestCheckResourceAttr(
"ibm_is_vpc.hub_false_delegated", "name", vpcname2),
resource.TestCheckResourceAttr(
"ibm_is_vpc.hub_false_delegated", "dns.0.enable_hub", "false"),
resource.TestCheckResourceAttr(
"ibm_is_vpc.hub_false_delegated", "dns.0.resolver.0.type", "system"),
resource.TestCheckResourceAttr(
"ibm_is_vpc.hub_false_delegated", "dns.0.resolver.0.vpc_id", ""),
resource.TestCheckResourceAttr(
"ibm_is_vpc.hub_false_delegated", "dns.0.resolver.0.dns_binding_name", ""),
),
},
// Step 2: Change to delegated resolver
{
Config: testAccCheckIBMISVPCCustomResolverDelegatedConfig(vpcname1, vpcname2),
Check: resource.ComposeTestCheckFunc(
testAccCheckIBMISVPCExists("ibm_is_vpc.hub_false_delegated", vpc),
resource.TestCheckResourceAttr(
"ibm_is_vpc.hub_false_delegated", "dns.0.resolver.0.type", "delegated"),
resource.TestCheckResourceAttrSet(
"ibm_is_vpc.hub_false_delegated", "dns.0.resolver.0.vpc_id"),
resource.TestCheckResourceAttr(
"ibm_is_vpc.hub_false_delegated", "dns.0.resolver.0.dns_binding_name", "test-delegation"),
),
},
// Step 3: Change back to system resolver
{
Config: testAccCheckIBMISVPCCustomResolverDelegatedToSystemConfig(vpcname1, vpcname2),
Check: resource.ComposeTestCheckFunc(
testAccCheckIBMISVPCExists("ibm_is_vpc.hub_false_delegated", vpc),
resource.TestCheckResourceAttr(
"ibm_is_vpc.hub_false_delegated", "dns.0.resolver.0.type", "system"),
resource.TestCheckResourceAttr(
"ibm_is_vpc.hub_false_delegated", "dns.0.resolver.0.vpc_id", ""),
resource.TestCheckResourceAttr(
"ibm_is_vpc.hub_false_delegated", "dns.0.resolver.0.dns_binding_name", ""),
),
},
},
})
}

// Helper function to generate config for resolver type transitions
func testAccCheckIBMISVPCResolverSystemConfig(vpcname1, vpcname2 string) string {
return fmt.Sprintf(`
resource "ibm_is_vpc" "hub_true" {
name = "%s"
dns {
enable_hub = true
}
}
resource "ibm_is_subnet" "hub_true_sub1" {
name = "hub-true-subnet1"
vpc = ibm_is_vpc.hub_true.id
zone = "%s"
total_ipv4_address_count = 16
}
resource "ibm_is_subnet" "hub_true_sub2" {
name = "hub-true-subnet2"
vpc = ibm_is_vpc.hub_true.id
zone = "%s"
total_ipv4_address_count = 16
}
resource "ibm_resource_instance" "dns-cr-instance" {
name = "dns-cr-instance"
resource_group_id = data.ibm_resource_group.rg.id
location = "global"
service = "dns-svcs"
plan = "standard-dns"
}
resource "ibm_dns_custom_resolver" "test_hub_true" {
name = "test-hub-true-customresolver"
instance_id = ibm_resource_instance.dns-cr-instance.guid
description = "new test CR - TF"
high_availability = true
enabled = true
locations {
subnet_crn = ibm_is_subnet.hub_true_sub1.crn
enabled = true
}
locations {
subnet_crn = ibm_is_subnet.hub_true_sub2.crn
enabled = true
}
}
// delegated vpc
resource "ibm_is_vpc" "hub_false_delegated" {
depends_on = [ibm_dns_custom_resolver.test_hub_true]
name = "%s"
dns {
enable_hub = false
resolver {
type = "system"
}
}
}
data "ibm_resource_group" "rg" {
is_default = true
}
`, vpcname1, acc.ISZoneName, acc.ISZoneName, vpcname2)
}

// Helper function to generate config for custom resolver with hub VPC
func testAccCheckIBMISVPCCustomResolverDelegatedConfig(vpcname1, vpcname2 string) string {
return fmt.Sprintf(`
resource "ibm_is_vpc" "hub_true" {
name = "%s"
dns {
enable_hub = true
}
}
resource "ibm_is_subnet" "hub_true_sub1" {
name = "hub-true-subnet1"
vpc = ibm_is_vpc.hub_true.id
zone = "%s"
total_ipv4_address_count = 16
}
resource "ibm_is_subnet" "hub_true_sub2" {
name = "hub-true-subnet2"
vpc = ibm_is_vpc.hub_true.id
zone = "%s"
total_ipv4_address_count = 16
}
resource "ibm_resource_instance" "dns-cr-instance" {
name = "dns-cr-instance"
resource_group_id = data.ibm_resource_group.rg.id
location = "global"
service = "dns-svcs"
plan = "standard-dns"
}
resource "ibm_dns_custom_resolver" "test_hub_true" {
name = "test-hub-true-customresolver"
instance_id = ibm_resource_instance.dns-cr-instance.guid
description = "new test CR - TF"
high_availability = true
enabled = true
locations {
subnet_crn = ibm_is_subnet.hub_true_sub1.crn
enabled = true
}
locations {
subnet_crn = ibm_is_subnet.hub_true_sub2.crn
enabled = true
}
}
// delegated vpc
resource "ibm_is_vpc" "hub_false_delegated" {
depends_on = [ibm_dns_custom_resolver.test_hub_true]
name = "%s"
dns {
enable_hub = false
resolver {
type = "delegated"
dns_binding_name = "test-delegation"
vpc_id = ibm_is_vpc.hub_true.id
}
}
}
data "ibm_resource_group" "rg" {
is_default = true
}
`, vpcname1, acc.ISZoneName, acc.ISZoneName, vpcname2)
}

func testAccCheckIBMISVPCCustomResolverDelegatedToSystemConfig(vpcname1, vpcname2 string) string {
return fmt.Sprintf(`
resource "ibm_is_vpc" "hub_true" {
name = "%s"
dns {
enable_hub = true
}
}
resource "ibm_is_subnet" "hub_true_sub1" {
name = "hub-true-subnet1"
vpc = ibm_is_vpc.hub_true.id
zone = "%s"
total_ipv4_address_count = 16
}
resource "ibm_is_subnet" "hub_true_sub2" {
name = "hub-true-subnet2"
vpc = ibm_is_vpc.hub_true.id
zone = "%s"
total_ipv4_address_count = 16
}
resource "ibm_resource_instance" "dns-cr-instance" {
name = "dns-cr-instance"
resource_group_id = data.ibm_resource_group.rg.id
location = "global"
service = "dns-svcs"
plan = "standard-dns"
}
resource "ibm_dns_custom_resolver" "test_hub_true" {
name = "test-hub-true-customresolver"
instance_id = ibm_resource_instance.dns-cr-instance.guid
description = "new test CR - TF"
high_availability = true
enabled = true
locations {
subnet_crn = ibm_is_subnet.hub_true_sub1.crn
enabled = true
}
locations {
subnet_crn = ibm_is_subnet.hub_true_sub2.crn
enabled = true
}
}
// delegated vpc
resource "ibm_is_vpc" "hub_false_delegated" {
depends_on = [ibm_dns_custom_resolver.test_hub_true]
name = "%s"
dns {
enable_hub = false
resolver {
type = "system"
dns_binding_name = "null"
vpc_id = "null"
}
}
}
data "ibm_resource_group" "rg" {
is_default = true
}
`, vpcname1, acc.ISZoneName, acc.ISZoneName, vpcname2)
}
19 changes: 17 additions & 2 deletions website/docs/r/is_vpc.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ resource "ibm_is_vpc" "example-system" {
name = "example-system-vpc"
dns {
enable_hub = false
type = "system"
// uncommenting/patching vpc with below code would make the resolver type delegated
# resolver {
# type = "delegated"
Expand All @@ -99,6 +99,21 @@ resource "ibm_is_vpc" "example-delegated" {
}
}
// to change from delegated to system (this removes the binding)
resource "ibm_is_vpc" "example-delegated-to-system" {
// required : add a dependency on ibm dns custom resolver of the hub vpc
depends_on = [ ibm_dns_custom_resolver.example-hub ]
name = "example-hub-false-delegated"
dns {
enable_hub = false
resolver {
type = "system"
vpc_id = "null"
dns_binding_name = "null"
}
}
}
```

## Timeouts
Expand Down Expand Up @@ -135,7 +150,7 @@ Review the argument references that you can specify for your resource.
Nested scheme for `resolver`:

- `dns_binding_id` - (String) The VPC dns binding id whose DNS resolver provides the DNS server addresses for this VPC. (If any)
- `dns_binding_name` - (Optional, String) The VPC dns binding name whose DNS resolver provides the DNS server addresses for this VPC. Only applicable for `delegated`, providing value would create binding with this name.
- `dns_binding_name` - (Optional, String) The VPC dns binding name whose DNS resolver provides the DNS server addresses for this VPC. Only applicable for `delegated`, providing value would create binding with this name. Providing "null" as name, would remove the binding.

~> **Note:**
`manual_servers` must be set if and only if `dns.resolver.type` is manual.
Expand Down

0 comments on commit 48f4893

Please sign in to comment.