Skip to content

Commit

Permalink
Correctly match RRs in the prefetch cache (#84)
Browse files Browse the repository at this point in the history
It was found that the prefetch feature. Which pulls the entire zone and creates a local cache for the provider to use and determine if a record already exists as a performance improvement and a way to reduce the number of API requests. The lookup algorithm did a naive lookup only based on the name of the Resource Record (RR), however, that can yield false positives and also result in incorrect records being created/updated.

Implementation notes
Unfortunately, the current version of the TF SDK does not support testing of for_each resources. The algorithm was updated to match all attributes that make a resource unique which are name, type, and content. There is also the potential of using the record ids to match existing records but the current implementation allows for some flexibility when using the provider especially since we do not support duplicate records.

Belongs to #80
  • Loading branch information
DXTimer authored Mar 6, 2023
1 parent bbf9823 commit b233c0c
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 3 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## master

BUG FIXES:

* Prefetch cache lookups are deterministic (dnsimple/terraform-provider-dnsimple#84)

## 0.16.1

IMPROVEMENTS:
Expand Down
2 changes: 1 addition & 1 deletion dnsimple/resource_dnsimple_zone_record.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ func resourceDNSimpleZoneRecordRead(ctx context.Context, data *schema.ResourceDa
}

for index := range records {
if records[index].Name == data.Get("name").(string) {
if records[index].Name == data.Get("name").(string) && records[index].Type == data.Get("type").(string) && records[index].Content == data.Get("value").(string) {
record = &records[index]
break
}
Expand Down
83 changes: 81 additions & 2 deletions dnsimple/resource_dnsimple_zone_record_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,14 +183,74 @@ func TestAccDNSimpleZoneRecord_Prefetch(t *testing.T) {
{
Config: fmt.Sprintf(testAccCheckDnsimpleZoneRecordConfigBasic, domain),
Check: resource.ComposeTestCheckFunc(
testAccCheckDnsimpleZoneRecordPrefetch("dnsimple_zone_record.foobar", &record),
testAccCheckDNSimpleZoneRecordExists("dnsimple_zone_record.foobar", &record),
testAccCheckDnsimpleZoneRecordPrefetch("dnsimple_zone_record.foobar"),
),
},
},
})
}

func testAccCheckDnsimpleZoneRecordPrefetch(n string, record *dnsimple.ZoneRecord) resource.TestCheckFunc {
func TestAccDNSimpleZoneRecord_Prefetch_ForEach(t *testing.T) {
// Issue: https://github.com/dnsimple/terraform-provider-dnsimple/issues/80
// This test is to ensure that the prefetch behaviour is deterministic
var recordSet1 dnsimple.ZoneRecord
var recordSet2 dnsimple.ZoneRecord
domain := os.Getenv("DNSIMPLE_DOMAIN")
resourceName1 := "dnsimple_zone_record.for_each_example_1"
resourceName2 := "dnsimple_zone_record.for_each_example_2"

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
os.Setenv("PREFETCH", "1")
},
ProviderFactories: testAccProviderFactories,
CheckDestroy: testAccCheckDNSimpleZoneRecordDestroy,
Steps: []resource.TestStep{
{
Config: fmt.Sprintf(testAccExampleDnsimpleZoneRecordsForEach, domain),
Check: resource.ComposeTestCheckFunc(
testAccCheckDNSimpleZoneRecordExists(resourceName1, &recordSet1),
testAccCheckDNSimpleZoneRecordExists(resourceName2, &recordSet2),
),
},
{
Config: fmt.Sprintf(testAccExampleDnsimpleZoneRecordsForEach, domain),
Check: resource.ComposeTestCheckFunc(
func(s *terraform.State) error {
resourceName := resourceName1
expectedID := fmt.Sprintf("%d", recordSet1.ID)
rs, ok := s.RootModule().Resources[resourceName]
if !ok {
return fmt.Errorf("Not found: %s", resourceName)
}

if rs.Primary.ID != expectedID {
return fmt.Errorf("Expected the ID of the resource (%s) to be the same as the record ID, but got %s and %s", resourceName, rs.Primary.ID, expectedID)
}
return nil
},
func(s *terraform.State) error {
resourceName := resourceName2
expectedID := fmt.Sprintf("%d", recordSet2.ID)
rs, ok := s.RootModule().Resources[resourceName]
if !ok {
return fmt.Errorf("Not found: %s", resourceName)
}

if rs.Primary.ID != expectedID {
return fmt.Errorf("Expected the ID of the resource (%s) to be the same as the record ID, but got %s and %s", resourceName, rs.Primary.ID, expectedID)
}
return nil
},
),
},
},
})
}

func testAccCheckDnsimpleZoneRecordPrefetch(n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
provider := testAccProvider.Meta().(*Client)

Expand Down Expand Up @@ -318,6 +378,25 @@ resource "dnsimple_zone_record" "foobar" {
ttl = 3600
}`

const testAccExampleDnsimpleZoneRecordsForEach = `
resource "dnsimple_zone_record" "for_each_example_1" {
zone_name = %[1]q
name = ""
value = "1.1.1.1"
type = "A"
ttl = 600
}
resource "dnsimple_zone_record" "for_each_example_2" {
zone_name = %[1]q
name = "1a"
value = "1.1.1.1"
type = "A"
ttl = 600
}`

const testAccCheckDnsimpleZoneRecordConfigNewValue = `
resource "dnsimple_zone_record" "foobar" {
zone_name = "%s"
Expand Down

0 comments on commit b233c0c

Please sign in to comment.