Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ func New() *schema.Provider {
"dns_mx_record_set": resourceDnsMXRecordSet(),
"dns_ns_record_set": resourceDnsNSRecordSet(),
"dns_ptr_record": resourceDnsPtrRecord(),
"dns_ptr_record_set": resourceDnsPtrRecordSet(),
"dns_srv_record_set": resourceDnsSRVRecordSet(),
"dns_txt_record_set": resourceDnsTXTRecordSet(),
},
Expand Down
137 changes: 137 additions & 0 deletions internal/provider/resource_dns_ptr_record_set.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package provider

import (
"fmt"
"sort"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/miekg/dns"
)

func resourceDnsPtrRecordSet() *schema.Resource {
return &schema.Resource{
Create: resourceDnsPtrRecordSetCreate,
Read: resourceDnsPtrRecordSetRead,
Update: resourceDnsPtrRecordSetUpdate,
Delete: resourceDnsPtrRecordSetDelete,
Importer: &schema.ResourceImporter{
State: resourceDnsImport,
},

Schema: map[string]*schema.Schema{
"zone": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateZone,
},
"name": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validateName,
},
"ptr": {
Type: schema.TypeSet,
Required: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
"ttl": {
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
Default: 3600,
},
},
}
}

func resourceDnsPtrRecordSetCreate(d *schema.ResourceData, meta interface{}) error {

d.SetId(resourceFQDN(d))

return resourceDnsPtrRecordSetUpdate(d, meta)
}

func resourceDnsPtrRecordSetRead(d *schema.ResourceData, meta interface{}) error {

answers, err := resourceDnsRead(d, meta, dns.TypePTR)
if err != nil {
return err
}

if len(answers) > 0 {

var ttl sort.IntSlice

ptr := schema.NewSet(schema.HashString, nil)
for _, record := range answers {
p, t, err := getPtrVal(record)
if err != nil {
return fmt.Errorf("Error querying DNS record: %s", err)
}
ptr.Add(p)
ttl = append(ttl, t)
}
sort.Sort(ttl)

d.Set("ptr", ptr)
d.Set("ttl", ttl[0])
} else {
d.SetId("")
}

return nil
}

func resourceDnsPtrRecordSetUpdate(d *schema.ResourceData, meta interface{}) error {

if meta != nil {

ttl := d.Get("ttl").(int)
rec_fqdn := resourceFQDN(d)

msg := new(dns.Msg)

msg.SetUpdate(d.Get("zone").(string))

if d.HasChange("ptr") {
o, n := d.GetChange("ptr")
os := o.(*schema.Set)
ns := n.(*schema.Set)
remove := os.Difference(ns).List()
add := ns.Difference(os).List()

// Loop through all the old ptr and remove them
for _, ptr := range remove {
rr_remove, _ := dns.NewRR(fmt.Sprintf("%s %d PTR %s", rec_fqdn, ttl, ptr.(string)))
msg.Remove([]dns.RR{rr_remove})
}
// Loop through all the new ptr and insert them
for _, ptr := range add {
rr_insert, _ := dns.NewRR(fmt.Sprintf("%s %d PTR %s", rec_fqdn, ttl, ptr.(string)))
msg.Insert([]dns.RR{rr_insert})
}

r, err := exchange(msg, true, meta)
if err != nil {
d.SetId("")
return fmt.Errorf("Error updating DNS record: %s", err)
}
if r.Rcode != dns.RcodeSuccess {
d.SetId("")
return fmt.Errorf("Error updating DNS record: %v (%s)", r.Rcode, dns.RcodeToString[r.Rcode])
}
}

return resourceDnsPtrRecordSetRead(d, meta)
} else {
return fmt.Errorf("update server is not set")
}
}

func resourceDnsPtrRecordSetDelete(d *schema.ResourceData, meta interface{}) error {

return resourceDnsDelete(d, meta, dns.TypePTR)
}
156 changes: 156 additions & 0 deletions internal/provider/resource_dns_ptr_record_set_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package provider

import (
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
"github.com/miekg/dns"
)

func TestAccDnsPtrRecordSet_Basic(t *testing.T) {

var rec_name, rec_zone string
resourceName := "dns_ptr_record_set.foo"
resourceRoot := "dns_ptr_record_set.root"

deletePtrRecordSet := func() {
meta := testAccProvider.Meta()

msg := new(dns.Msg)

msg.SetUpdate(rec_zone)

rec_fqdn := testResourceFQDN(rec_name, rec_zone)

rr_remove, _ := dns.NewRR(fmt.Sprintf("%s 0 PTR", rec_fqdn))
msg.RemoveRRset([]dns.RR{rr_remove})

r, err := exchange(msg, true, meta)
if err != nil {
t.Fatalf("Error deleting DNS record: %s", err)
}
if r.Rcode != dns.RcodeSuccess {
t.Fatalf("Error deleting DNS record: %v", r.Rcode)
}
}

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckDnsPtrRecordSetDestroy,
Steps: []resource.TestStep{
{
Config: testAccDnsPtrRecordSet_basic,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "ptr.#", "1"),
testAccCheckDnsPtrRecordSetExists(t, resourceName, []interface{}{"foo.example.com."}, &rec_name, &rec_zone),
),
},
{
Config: testAccDnsPtrRecordSet_update,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "ptr.#", "1"),
testAccCheckDnsPtrRecordSetExists(t, resourceName, []interface{}{"bar.example.com."}, &rec_name, &rec_zone),
),
},
{
PreConfig: deletePtrRecordSet,
Config: testAccDnsPtrRecordSet_update,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "ptr.#", "1"),
testAccCheckDnsPtrRecordSetExists(t, resourceName, []interface{}{"bar.example.com."}, &rec_name, &rec_zone),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
{
Config: testAccDnsPtrRecordSet_root,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceRoot, "ptr.#", "1"),
testAccCheckDnsPtrRecordSetExists(t, resourceRoot, []interface{}{"baz.example.com."}, &rec_name, &rec_zone),
),
},
{
ResourceName: resourceRoot,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func testAccCheckDnsPtrRecordSetDestroy(s *terraform.State) error {
return testAccCheckDnsDestroy(s, "dns_ptr_record_set", dns.TypePTR)
}

func testAccCheckDnsPtrRecordSetExists(t *testing.T, n string, addr []interface{}, rec_name, rec_zone *string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}
if rs.Primary.ID == "" {
return fmt.Errorf("No ID is set")
}

*rec_name = rs.Primary.Attributes["name"]
*rec_zone = rs.Primary.Attributes["zone"]

rec_fqdn := testResourceFQDN(*rec_name, *rec_zone)

meta := testAccProvider.Meta()

msg := new(dns.Msg)
msg.SetQuestion(rec_fqdn, dns.TypePTR)
r, err := exchange(msg, false, meta)
if err != nil {
return fmt.Errorf("Error querying DNS record: %s", err)
}
if r.Rcode != dns.RcodeSuccess {
return fmt.Errorf("Error querying DNS record")
}

ptr := schema.NewSet(schema.HashString, nil)
expected := schema.NewSet(schema.HashString, addr)
for _, record := range r.Answer {
addr, _, err := getPtrVal(record)
if err != nil {
return fmt.Errorf("Error querying DNS record: %s", err)
}
ptr.Add(addr)
}
if !ptr.Equal(expected) {
return fmt.Errorf("DNS record differs: expected %v, found %v", expected, ptr)
}
return nil
}
}

var testAccDnsPtrRecordSet_basic = fmt.Sprintf(`
resource "dns_ptr_record_set" "foo" {
zone = "1.168.192.in-addr.arpa."
name = "99"
ptr = ["foo.example.com."]
ttl = 300
}`)

var testAccDnsPtrRecordSet_update = fmt.Sprintf(`
resource "dns_ptr_record_set" "foo" {
zone = "1.168.192.in-addr.arpa."
name = "99"
ptr = ["bar.example.com."]
ttl = 300
}`)

var testAccDnsPtrRecordSet_root = fmt.Sprintf(`
resource "dns_ptr_record_set" "root" {
zone = "1.168.192.in-addr.arpa."
ptr = ["baz.example.com."]
ttl = 300
}`)
3 changes: 3 additions & 0 deletions website/dns.erb
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@
</li>
<li<%= sidebar_current("docs-dns-ptr-record") %>>
<a href="/docs/providers/dns/r/dns_ptr_record.html">dns_ptr_record</a>
</li>
<li<%= sidebar_current("docs-dns-ptr-record-set") %>>
<a href="/docs/providers/dns/r/dns_ptr_record_set.html">dns_ptr_record_set</a>
</li>
<li<%= sidebar_current("docs-dns-srv-record-set") %>>
<a href="/docs/providers/dns/r/dns_srv_record_set.html">dns_srv_record_set</a>
Expand Down
48 changes: 48 additions & 0 deletions website/docs/r/dns_ptr_record_set.html.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
layout: "dns"
page_title: "DNS: dns_ptr_record_set"
sidebar_current: "docs-dns-ptr-record-set"
description: |-
Creates a PTR type DNS record set.
---

# dns_ptr_record_set

Creates a PTR type DNS record set.

## Example Usage

```hcl
resource "dns_ptr_record" "dns-sd" {
zone = "example.com."
name = "r._dns-sd"
ptr = ["example.com."]
ttl = 300
}
```

## Argument Reference

The following arguments are supported:

* `zone` - (Required) DNS zone the record set belongs to. It must be an FQDN, that is, include the trailing dot.
* `name` - (Optional) The name of the record set. The `zone` argument will be appended to this value to create the full record path.
* `ptr` - (Required) The PTR records (usually only one makes sense) this record set will point to.
* `ttl` - (Optional) The TTL of the record set. Defaults to `3600`.

## Attributes Reference

The following attributes are exported:

* `zone` - See Argument Reference above.
* `name` - See Argument Reference above.
* `ptr` - See Argument Reference above.
* `ttl` - See Argument Reference above.

## Import

Records can be imported using the FQDN, e.g.

```
$ terraform import dns_ptr_record_set.dns-sd r._dns-sd.example.com.
```