provider/openstack: Add DNS V2 RecordSet resource (#14813)

* vendor gophercloud dns v2 recordset deps

* openstack dns v2 recordset resource

* fix type assertion panic condition

* address pr review feedback
This commit is contained in:
jrperritt 2017-05-24 23:58:44 -05:00 committed by Joe Topjian
parent acedf7228f
commit e1c8d760d3
12 changed files with 985 additions and 1 deletions

View File

@ -0,0 +1,28 @@
package openstack
import (
"testing"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccDNSV2RecordSet_importBasic(t *testing.T) {
zoneName := randomZoneName()
resourceName := "openstack_dns_recordset_v2.recordset_1"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheckDNSRecordSetV2(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckDNSV2RecordSetDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccDNSV2RecordSet_basic(zoneName),
},
resource.TestStep{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

View File

@ -150,6 +150,7 @@ func Provider() terraform.ResourceProvider {
"openstack_compute_floatingip_v2": resourceComputeFloatingIPV2(), "openstack_compute_floatingip_v2": resourceComputeFloatingIPV2(),
"openstack_compute_floatingip_associate_v2": resourceComputeFloatingIPAssociateV2(), "openstack_compute_floatingip_associate_v2": resourceComputeFloatingIPAssociateV2(),
"openstack_compute_volume_attach_v2": resourceComputeVolumeAttachV2(), "openstack_compute_volume_attach_v2": resourceComputeVolumeAttachV2(),
"openstack_dns_recordset_v2": resourceDNSRecordSetV2(),
"openstack_dns_zone_v2": resourceDNSZoneV2(), "openstack_dns_zone_v2": resourceDNSZoneV2(),
"openstack_fw_firewall_v1": resourceFWFirewallV1(), "openstack_fw_firewall_v1": resourceFWFirewallV1(),
"openstack_fw_policy_v1": resourceFWPolicyV1(), "openstack_fw_policy_v1": resourceFWPolicyV1(),

View File

@ -0,0 +1,276 @@
package openstack
import (
"fmt"
"log"
"strings"
"time"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceDNSRecordSetV2() *schema.Resource {
return &schema.Resource{
Create: resourceDNSRecordSetV2Create,
Read: resourceDNSRecordSetV2Read,
Update: resourceDNSRecordSetV2Update,
Delete: resourceDNSRecordSetV2Delete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(10 * time.Minute),
Update: schema.DefaultTimeout(10 * time.Minute),
Delete: schema.DefaultTimeout(10 * time.Minute),
},
Schema: map[string]*schema.Schema{
"region": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""),
},
"zone_id": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"description": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"records": &schema.Schema{
Type: schema.TypeList,
Optional: true,
ForceNew: false,
Elem: &schema.Schema{Type: schema.TypeString},
},
"ttl": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Computed: true,
ForceNew: false,
},
"type": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
"value_specs": &schema.Schema{
Type: schema.TypeMap,
Optional: true,
ForceNew: true,
},
},
}
}
func resourceDNSRecordSetV2Create(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
dnsClient, err := config.dnsV2Client(GetRegion(d))
if err != nil {
return fmt.Errorf("Error creating OpenStack DNS client: %s", err)
}
recordsraw := d.Get("records").([]interface{})
records := make([]string, len(recordsraw))
for i, recordraw := range recordsraw {
records[i] = recordraw.(string)
}
createOpts := RecordSetCreateOpts{
recordsets.CreateOpts{
Name: d.Get("name").(string),
Description: d.Get("description").(string),
Records: records,
TTL: d.Get("ttl").(int),
Type: d.Get("type").(string),
},
MapValueSpecs(d),
}
zoneID := d.Get("zone_id").(string)
log.Printf("[DEBUG] Create Options: %#v", createOpts)
n, err := recordsets.Create(dnsClient, zoneID, createOpts).Extract()
if err != nil {
return fmt.Errorf("Error creating OpenStack DNS record set: %s", err)
}
log.Printf("[DEBUG] Waiting for DNS record set (%s) to become available", n.ID)
stateConf := &resource.StateChangeConf{
Target: []string{"ACTIVE"},
Pending: []string{"PENDING"},
Refresh: waitForDNSRecordSet(dnsClient, zoneID, n.ID),
Timeout: d.Timeout(schema.TimeoutCreate),
Delay: 5 * time.Second,
MinTimeout: 3 * time.Second,
}
_, err = stateConf.WaitForState()
id := fmt.Sprintf("%s/%s", zoneID, n.ID)
d.SetId(id)
log.Printf("[DEBUG] Created OpenStack DNS record set %s: %#v", n.ID, n)
return resourceDNSRecordSetV2Read(d, meta)
}
func resourceDNSRecordSetV2Read(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
dnsClient, err := config.dnsV2Client(GetRegion(d))
if err != nil {
return fmt.Errorf("Error creating OpenStack DNS client: %s", err)
}
// Obtain relevant info from parsing the ID
zoneID, recordsetID, err := parseDNSV2RecordSetID(d.Id())
if err != nil {
return err
}
n, err := recordsets.Get(dnsClient, zoneID, recordsetID).Extract()
if err != nil {
return CheckDeleted(d, err, "record_set")
}
log.Printf("[DEBUG] Retrieved record set %s: %#v", recordsetID, n)
d.Set("name", n.Name)
d.Set("description", n.Description)
d.Set("ttl", n.TTL)
d.Set("type", n.Type)
d.Set("records", n.Records)
d.Set("region", GetRegion(d))
d.Set("zone_id", zoneID)
return nil
}
func resourceDNSRecordSetV2Update(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
dnsClient, err := config.dnsV2Client(GetRegion(d))
if err != nil {
return fmt.Errorf("Error creating OpenStack DNS client: %s", err)
}
var updateOpts recordsets.UpdateOpts
if d.HasChange("ttl") {
updateOpts.TTL = d.Get("ttl").(int)
}
if d.HasChange("records") {
recordsraw := d.Get("records").([]interface{})
records := make([]string, len(recordsraw))
for i, recordraw := range recordsraw {
records[i] = recordraw.(string)
}
updateOpts.Records = records
}
if d.HasChange("description") {
updateOpts.Description = d.Get("description").(string)
}
// Obtain relevant info from parsing the ID
zoneID, recordsetID, err := parseDNSV2RecordSetID(d.Id())
if err != nil {
return err
}
log.Printf("[DEBUG] Updating record set %s with options: %#v", recordsetID, updateOpts)
_, err = recordsets.Update(dnsClient, zoneID, recordsetID, updateOpts).Extract()
if err != nil {
return fmt.Errorf("Error updating OpenStack DNS record set: %s", err)
}
log.Printf("[DEBUG] Waiting for DNS record set (%s) to update", recordsetID)
stateConf := &resource.StateChangeConf{
Target: []string{"ACTIVE"},
Pending: []string{"PENDING"},
Refresh: waitForDNSRecordSet(dnsClient, zoneID, recordsetID),
Timeout: d.Timeout(schema.TimeoutUpdate),
Delay: 5 * time.Second,
MinTimeout: 3 * time.Second,
}
_, err = stateConf.WaitForState()
return resourceDNSRecordSetV2Read(d, meta)
}
func resourceDNSRecordSetV2Delete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
dnsClient, err := config.dnsV2Client(GetRegion(d))
if err != nil {
return fmt.Errorf("Error creating OpenStack DNS client: %s", err)
}
// Obtain relevant info from parsing the ID
zoneID, recordsetID, err := parseDNSV2RecordSetID(d.Id())
if err != nil {
return err
}
err = recordsets.Delete(dnsClient, zoneID, recordsetID).ExtractErr()
if err != nil {
return fmt.Errorf("Error deleting OpenStack DNS record set: %s", err)
}
log.Printf("[DEBUG] Waiting for DNS record set (%s) to be deleted", recordsetID)
stateConf := &resource.StateChangeConf{
Target: []string{"DELETED"},
Pending: []string{"ACTIVE", "PENDING"},
Refresh: waitForDNSRecordSet(dnsClient, zoneID, recordsetID),
Timeout: d.Timeout(schema.TimeoutDelete),
Delay: 5 * time.Second,
MinTimeout: 3 * time.Second,
}
_, err = stateConf.WaitForState()
d.SetId("")
return nil
}
func waitForDNSRecordSet(dnsClient *gophercloud.ServiceClient, zoneID, recordsetId string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
recordset, err := recordsets.Get(dnsClient, zoneID, recordsetId).Extract()
if err != nil {
if _, ok := err.(gophercloud.ErrDefault404); ok {
return recordset, "DELETED", nil
}
return nil, "", err
}
log.Printf("[DEBUG] OpenStack DNS record set (%s) current status: %s", recordset.ID, recordset.Status)
return recordset, recordset.Status, nil
}
}
func parseDNSV2RecordSetID(id string) (string, string, error) {
idParts := strings.Split(id, "/")
if len(idParts) != 2 {
return "", "", fmt.Errorf("Unable to determine DNS record set ID from raw ID: %s", id)
}
zoneID := idParts[0]
recordsetID := idParts[1]
return zoneID, recordsetID, nil
}

View File

@ -0,0 +1,249 @@
package openstack
import (
"fmt"
"os"
"regexp"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets"
)
func randomZoneName() string {
return fmt.Sprintf("ACPTTEST-zone-%s.com.", acctest.RandString(5))
}
func TestAccDNSV2RecordSet_basic(t *testing.T) {
var recordset recordsets.RecordSet
zoneName := randomZoneName()
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheckDNSRecordSetV2(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckDNSV2RecordSetDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccDNSV2RecordSet_basic(zoneName),
Check: resource.ComposeTestCheckFunc(
testAccCheckDNSV2RecordSetExists("openstack_dns_recordset_v2.recordset_1", &recordset),
resource.TestCheckResourceAttr(
"openstack_dns_recordset_v2.recordset_1", "description", "a record set"),
resource.TestCheckResourceAttr(
"openstack_dns_recordset_v2.recordset_1", "records.0", "10.1.0.0"),
),
},
resource.TestStep{
Config: testAccDNSV2RecordSet_update(zoneName),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("openstack_dns_recordset_v2.recordset_1", "name", zoneName),
resource.TestCheckResourceAttr("openstack_dns_recordset_v2.recordset_1", "ttl", "6000"),
resource.TestCheckResourceAttr("openstack_dns_recordset_v2.recordset_1", "type", "A"),
resource.TestCheckResourceAttr(
"openstack_dns_recordset_v2.recordset_1", "description", "an updated record set"),
resource.TestCheckResourceAttr(
"openstack_dns_recordset_v2.recordset_1", "records.0", "10.1.0.1"),
),
},
},
})
}
func TestAccDNSV2RecordSet_readTTL(t *testing.T) {
var recordset recordsets.RecordSet
zoneName := randomZoneName()
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheckDNSRecordSetV2(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckDNSV2RecordSetDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccDNSV2RecordSet_readTTL(zoneName),
Check: resource.ComposeTestCheckFunc(
testAccCheckDNSV2RecordSetExists("openstack_dns_recordset_v2.recordset_1", &recordset),
resource.TestMatchResourceAttr(
"openstack_dns_recordset_v2.recordset_1", "ttl", regexp.MustCompile("^[0-9]+$")),
),
},
},
})
}
func TestAccDNSV2RecordSet_timeout(t *testing.T) {
var recordset recordsets.RecordSet
zoneName := randomZoneName()
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheckDNSRecordSetV2(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckDNSV2RecordSetDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccDNSV2RecordSet_timeout(zoneName),
Check: resource.ComposeTestCheckFunc(
testAccCheckDNSV2RecordSetExists("openstack_dns_recordset_v2.recordset_1", &recordset),
),
},
},
})
}
func testAccCheckDNSV2RecordSetDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
dnsClient, err := config.dnsV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("Error creating OpenStack DNS client: %s", err)
}
for _, rs := range s.RootModule().Resources {
if rs.Type != "openstack_dns_recordset_v2" {
continue
}
zoneID, recordsetID, err := parseDNSV2RecordSetID(rs.Primary.ID)
if err != nil {
return err
}
_, err = recordsets.Get(dnsClient, zoneID, recordsetID).Extract()
if err == nil {
return fmt.Errorf("Record set still exists")
}
}
return nil
}
func testAccCheckDNSV2RecordSetExists(n string, recordset *recordsets.RecordSet) 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")
}
config := testAccProvider.Meta().(*Config)
dnsClient, err := config.dnsV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("Error creating OpenStack DNS client: %s", err)
}
zoneID, recordsetID, err := parseDNSV2RecordSetID(rs.Primary.ID)
if err != nil {
return err
}
found, err := recordsets.Get(dnsClient, zoneID, recordsetID).Extract()
if err != nil {
return err
}
if found.ID != recordsetID {
return fmt.Errorf("Record set not found")
}
*recordset = *found
return nil
}
}
func testAccPreCheckDNSRecordSetV2(t *testing.T) {
if os.Getenv("OS_AUTH_URL") == "" {
t.Fatal("OS_AUTH_URL must be set for acceptance tests")
}
}
func testAccDNSV2RecordSet_basic(zoneName string) string {
return fmt.Sprintf(`
resource "openstack_dns_zone_v2" "zone_1" {
name = "%s"
email = "email2@example.com"
description = "a zone"
ttl = 6000
type = "PRIMARY"
}
resource "openstack_dns_recordset_v2" "recordset_1" {
zone_id = "${openstack_dns_zone_v2.zone_1.id}"
name = "%s"
type = "A"
description = "a record set"
ttl = 3000
records = ["10.1.0.0"]
}
`, zoneName, zoneName)
}
func testAccDNSV2RecordSet_update(zoneName string) string {
return fmt.Sprintf(`
resource "openstack_dns_zone_v2" "zone_1" {
name = "%s"
email = "email2@example.com"
description = "an updated zone"
ttl = 6000
type = "PRIMARY"
}
resource "openstack_dns_recordset_v2" "recordset_1" {
zone_id = "${openstack_dns_zone_v2.zone_1.id}"
name = "%s"
type = "A"
description = "an updated record set"
ttl = 6000
records = ["10.1.0.1"]
}
`, zoneName, zoneName)
}
func testAccDNSV2RecordSet_readTTL(zoneName string) string {
return fmt.Sprintf(`
resource "openstack_dns_zone_v2" "zone_1" {
name = "%s"
email = "email2@example.com"
description = "an updated zone"
ttl = 6000
type = "PRIMARY"
}
resource "openstack_dns_recordset_v2" "recordset_1" {
zone_id = "${openstack_dns_zone_v2.zone_1.id}"
name = "%s"
type = "A"
records = ["10.1.0.2"]
}
`, zoneName, zoneName)
}
func testAccDNSV2RecordSet_timeout(zoneName string) string {
return fmt.Sprintf(`
resource "openstack_dns_zone_v2" "zone_1" {
name = "%s"
email = "email2@example.com"
description = "an updated zone"
ttl = 6000
type = "PRIMARY"
}
resource "openstack_dns_recordset_v2" "recordset_1" {
zone_id = "${openstack_dns_zone_v2.zone_1.id}"
name = "%s"
type = "A"
ttl = 3000
records = ["10.1.0.3"]
timeouts {
create = "5m"
update = "5m"
delete = "5m"
}
}
`, zoneName, zoneName)
}

View File

@ -180,7 +180,12 @@ func resourceDNSZoneV2Update(d *schema.ResourceData, meta interface{}) error {
updateOpts.TTL = d.Get("ttl").(int) updateOpts.TTL = d.Get("ttl").(int)
} }
if d.HasChange("masters") { if d.HasChange("masters") {
updateOpts.Masters = d.Get("masters").([]string) mastersraw := d.Get("masters").(*schema.Set).List()
masters := make([]string, len(mastersraw))
for i, masterraw := range mastersraw {
masters[i] = masterraw.(string)
}
updateOpts.Masters = masters
} }
if d.HasChange("description") { if d.HasChange("description") {
updateOpts.Description = d.Get("description").(string) updateOpts.Description = d.Get("description").(string)

View File

@ -12,6 +12,7 @@ import (
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups"
"github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets"
"github.com/gophercloud/gophercloud/openstack/dns/v2/zones" "github.com/gophercloud/gophercloud/openstack/dns/v2/zones"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
@ -225,6 +226,27 @@ func (opts PortCreateOpts) ToPortCreateMap() (map[string]interface{}, error) {
return BuildRequest(opts, "port") return BuildRequest(opts, "port")
} }
// RecordSetCreateOpts represents the attributes used when creating a new DNS record set.
type RecordSetCreateOpts struct {
recordsets.CreateOpts
ValueSpecs map[string]string `json:"value_specs,omitempty"`
}
// ToRecordSetCreateMap casts a CreateOpts struct to a map.
// It overrides recordsets.ToRecordSetCreateMap to add the ValueSpecs field.
func (opts RecordSetCreateOpts) ToRecordSetCreateMap() (map[string]interface{}, error) {
b, err := BuildRequest(opts, "")
if err != nil {
return nil, err
}
if m, ok := b[""].(map[string]interface{}); ok {
return m, nil
}
return nil, fmt.Errorf("Expected map but got %T", b[""])
}
// RouterCreateOpts represents the attributes used when creating a new router. // RouterCreateOpts represents the attributes used when creating a new router.
type RouterCreateOpts struct { type RouterCreateOpts struct {
routers.CreateOpts routers.CreateOpts

View File

@ -0,0 +1,6 @@
// Package recordsets provides information and interaction with the zone API
// resource for the OpenStack DNS service.
//
// For more information, see:
// http://developer.openstack.org/api-ref/dns/#recordsets
package recordsets

View File

@ -0,0 +1,157 @@
package recordsets
import (
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/pagination"
)
// ListOptsBuilder allows extensions to add additional parameters to the
// List request.
type ListOptsBuilder interface {
ToRecordSetListQuery() (string, error)
}
// ListOpts allows the filtering and sorting of paginated collections through
// the API. Filtering is achieved by passing in struct field values that map to
// the server attributes you want to see returned. Marker and Limit are used
// for pagination.
// https://developer.openstack.org/api-ref/dns/
type ListOpts struct {
// Integer value for the limit of values to return.
Limit int `q:"limit"`
// UUID of the recordset at which you want to set a marker.
Marker string `q:"marker"`
Data string `q:"data"`
Description string `q:"description"`
Name string `q:"name"`
SortDir string `q:"sort_dir"`
SortKey string `q:"sort_key"`
Status string `q:"status"`
TTL int `q:"ttl"`
Type string `q:"type"`
ZoneID string `q:"zone_id"`
}
// ToRecordSetListQuery formats a ListOpts into a query string.
func (opts ListOpts) ToRecordSetListQuery() (string, error) {
q, err := gophercloud.BuildQueryString(opts)
return q.String(), err
}
// ListByZone implements the recordset list request.
func ListByZone(client *gophercloud.ServiceClient, zoneID string, opts ListOptsBuilder) pagination.Pager {
url := baseURL(client, zoneID)
if opts != nil {
query, err := opts.ToRecordSetListQuery()
if err != nil {
return pagination.Pager{Err: err}
}
url += query
}
return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
return RecordSetPage{pagination.LinkedPageBase{PageResult: r}}
})
}
// Get implements the recordset get request.
func Get(client *gophercloud.ServiceClient, zoneID string, rrsetID string) (r GetResult) {
_, r.Err = client.Get(rrsetURL(client, zoneID, rrsetID), &r.Body, nil)
return
}
// CreateOptsBuilder allows extensions to add additional attributes to the Create request.
type CreateOptsBuilder interface {
ToRecordSetCreateMap() (map[string]interface{}, error)
}
// CreateOpts specifies the base attributes that may be used to create a RecordSet.
type CreateOpts struct {
// Name is the name of the RecordSet.
Name string `json:"name" required:"true"`
// Description is a description of the RecordSet.
Description string `json:"description,omitempty"`
// Records are the DNS records of the RecordSet.
Records []string `json:"records,omitempty"`
// TTL is the time to live of the RecordSet.
TTL int `json:"ttl,omitempty"`
// Type is the RRTYPE of the RecordSet.
Type string `json:"type,omitempty"`
}
// ToRecordSetCreateMap formats an CreateOpts structure into a request body.
func (opts CreateOpts) ToRecordSetCreateMap() (map[string]interface{}, error) {
b, err := gophercloud.BuildRequestBody(opts, "")
if err != nil {
return nil, err
}
return b, nil
}
// Create creates a recordset in a given zone.
func Create(client *gophercloud.ServiceClient, zoneID string, opts CreateOptsBuilder) (r CreateResult) {
b, err := opts.ToRecordSetCreateMap()
if err != nil {
r.Err = err
return
}
_, r.Err = client.Post(baseURL(client, zoneID), &b, &r.Body, &gophercloud.RequestOpts{
OkCodes: []int{201, 202},
})
return
}
// UpdateOptsBuilder allows extensions to add additional attributes to the Update request.
type UpdateOptsBuilder interface {
ToRecordSetUpdateMap() (map[string]interface{}, error)
}
// UpdateOpts specifies the base attributes that may be updated on an existing RecordSet.
type UpdateOpts struct {
Description string `json:"description,omitempty"`
TTL int `json:"ttl,omitempty"`
Records []string `json:"records,omitempty"`
}
// ToRecordSetUpdateMap formats an UpdateOpts structure into a request body.
func (opts UpdateOpts) ToRecordSetUpdateMap() (map[string]interface{}, error) {
b, err := gophercloud.BuildRequestBody(opts, "")
if err != nil {
return nil, err
}
if opts.TTL > 0 {
b["ttl"] = opts.TTL
} else {
b["ttl"] = nil
}
return b, nil
}
// Update updates a recordset in a given zone
func Update(client *gophercloud.ServiceClient, zoneID string, rrsetID string, opts UpdateOptsBuilder) (r UpdateResult) {
b, err := opts.ToRecordSetUpdateMap()
if err != nil {
r.Err = err
return
}
_, r.Err = client.Put(rrsetURL(client, zoneID, rrsetID), &b, &r.Body, &gophercloud.RequestOpts{
OkCodes: []int{200, 202},
})
return
}
// Delete removes an existing RecordSet.
func Delete(client *gophercloud.ServiceClient, zoneID string, rrsetID string) (r DeleteResult) {
_, r.Err = client.Delete(rrsetURL(client, zoneID, rrsetID), &gophercloud.RequestOpts{
OkCodes: []int{202},
})
return
}

View File

@ -0,0 +1,141 @@
package recordsets
import (
"encoding/json"
"time"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/pagination"
)
type commonResult struct {
gophercloud.Result
}
// Extract interprets a GetResult, CreateResult or UpdateResult as a concrete RecordSet.
// An error is returned if the original call or the extraction failed.
func (r commonResult) Extract() (*RecordSet, error) {
var s *RecordSet
err := r.ExtractInto(&s)
return s, err
}
// CreateResult is the deferred result of a Create call.
type CreateResult struct {
commonResult
}
// GetResult is the deferred result of a Get call.
type GetResult struct {
commonResult
}
// RecordSetPage is a single page of RecordSet results.
type RecordSetPage struct {
pagination.LinkedPageBase
}
// UpdateResult is the deferred result of an Update call.
type UpdateResult struct {
commonResult
}
// DeleteResult is the deferred result of an Delete call.
type DeleteResult struct {
gophercloud.ErrResult
}
// IsEmpty returns true if the page contains no results.
func (r RecordSetPage) IsEmpty() (bool, error) {
s, err := ExtractRecordSets(r)
return len(s) == 0, err
}
// ExtractRecordSets extracts a slice of RecordSets from a Collection acquired from List.
func ExtractRecordSets(r pagination.Page) ([]RecordSet, error) {
var s struct {
RecordSets []RecordSet `json:"recordsets"`
}
err := (r.(RecordSetPage)).ExtractInto(&s)
return s.RecordSets, err
}
type RecordSet struct {
// ID is the unique ID of the recordset
ID string `json:"id"`
// ZoneID is the ID of the zone the recordset belongs to.
ZoneID string `json:"zone_id"`
// ProjectID is the ID of the project that owns the recordset.
ProjectID string `json:"project_id"`
// Name is the name of the recordset.
Name string `json:"name"`
// ZoneName is the name of the zone the recordset belongs to.
ZoneName string `json:"zone_name"`
// Type is the RRTYPE of the recordset.
Type string `json:"type"`
// Records are the DNS records of the recordset.
Records []string `json:"records"`
// TTL is the time to live of the recordset.
TTL int `json:"ttl"`
// Status is the status of the recordset.
Status string `json:"status"`
// Action is the current action in progress of the recordset.
Action string `json:"action"`
// Description is the description of the recordset.
Description string `json:"description"`
// Version is the revision of the recordset.
Version int `json:version"`
// CreatedAt is the date when the recordset was created.
CreatedAt time.Time `json:"-"`
// UpdatedAt is the date when the recordset was updated.
UpdatedAt time.Time `json:"-"`
// Links includes HTTP references to the itself,
// useful for passing along to other APIs that might want a recordset reference.
Links []gophercloud.Link `json:"-"`
}
func (r *RecordSet) UnmarshalJSON(b []byte) error {
type tmp RecordSet
var s struct {
tmp
CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"`
UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"`
Links map[string]interface{} `json:"links"`
}
err := json.Unmarshal(b, &s)
if err != nil {
return err
}
*r = RecordSet(s.tmp)
r.CreatedAt = time.Time(s.CreatedAt)
r.UpdatedAt = time.Time(s.UpdatedAt)
if s.Links != nil {
for rel, href := range s.Links {
if v, ok := href.(string); ok {
link := gophercloud.Link{
Rel: rel,
Href: v,
}
r.Links = append(r.Links, link)
}
}
}
return err
}

View File

@ -0,0 +1,11 @@
package recordsets
import "github.com/gophercloud/gophercloud"
func baseURL(c *gophercloud.ServiceClient, zoneID string) string {
return c.ServiceURL("zones", zoneID, "recordsets")
}
func rrsetURL(c *gophercloud.ServiceClient, zoneID string, rrsetID string) string {
return c.ServiceURL("zones", zoneID, "recordsets", rrsetID)
}

6
vendor/vendor.json vendored
View File

@ -1714,6 +1714,12 @@
"revision": "0f64da0e36de86a0ca1a8f2fc1b0570a0d3f7504", "revision": "0f64da0e36de86a0ca1a8f2fc1b0570a0d3f7504",
"revisionTime": "2017-03-10T01:59:53Z" "revisionTime": "2017-03-10T01:59:53Z"
}, },
{
"checksumSHA1": "Mugw0ROrabHNN4nG9YlimPbyENk=",
"path": "github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets",
"revision": "5a6e13341e63bd202a0da558a2a7187720042676",
"revisionTime": "2017-05-22T01:35:44Z"
},
{ {
"checksumSHA1": "OGb3suDcMij0bnu0r/eQgWAX5Ho=", "checksumSHA1": "OGb3suDcMij0bnu0r/eQgWAX5Ho=",
"path": "github.com/gophercloud/gophercloud/openstack/dns/v2/zones", "path": "github.com/gophercloud/gophercloud/openstack/dns/v2/zones",

View File

@ -0,0 +1,82 @@
---
layout: "openstack"
page_title: "OpenStack: openstack_dns_recordset_v2"
sidebar_current: "docs-openstack-resource-dns-recordset-v2"
description: |-
Manages a DNS record set in the OpenStack DNS Service
---
# openstack\_dns\_recordset_v2
Manages a DNS record set in the OpenStack DNS Service.
## Example Usage
### Automatically detect the correct network
```hcl
resource "openstack_dns_zone_v2" "example_zone" {
name = "example.com."
email = "email2@example.com"
description = "a zone"
ttl = 6000
type = "PRIMARY"
}
resource "openstack_dns_recordset_v2" "rs_example_com" {
zone_id = "${openstack_dns_zone_v2.example_zone.id}"
name = "rs.example.com."
description = "An example record set"
ttl = 3000
type = "A"
records = ["10.0.0.1"]
}
```
## Argument Reference
The following arguments are supported:
* `region` - (Required) The region in which to obtain the V2 DNS client.
If omitted, the `OS_REGION_NAME` environment variable is used.
Changing this creates a new DNS record set.
* `zone_id` - (Required) The ID of the zone in which to create the record set.
Changing this creates a new DNS record set.
* `name` - (Required) The name of the record set. Note the `.` at the end of the name.
Changing this creates a new DNS record set.
* `type` - (Optional) The type of record set. Examples: "A", "MX".
Changing this creates a new DNS record set.
* `ttl` - (Optional) The time to live (TTL) of the record set.
* `description` - (Optional) A description of the record set.
* `records` - (Optional) An array of DNS records.
* `value_specs` - (Optional) Map of additional options. Changing this creates a
new record set.
## Attributes Reference
The following attributes are exported:
* `region` - See Argument Reference above.
* `name` - See Argument Reference above.
* `type` - See Argument Reference above.
* `ttl` - See Argument Reference above.
* `description` - See Argument Reference above.
* `records` - See Argument Reference above.
* `zone_id` - See Argument Reference above.
* `value_specs` - See Argument Reference above.
## Import
This resource can be imported by specifying the zone ID and recordset ID,
separated by a forward slash.
```
$ terraform import openstack_dns_recordset_v2.recordset_1 <zone_id>/<recordset_id>
```