diff --git a/builtin/providers/openstack/import_openstack_dns_zone_v2_test.go b/builtin/providers/openstack/import_openstack_dns_zone_v2_test.go index ca7ec7a37..83b7f5509 100644 --- a/builtin/providers/openstack/import_openstack_dns_zone_v2_test.go +++ b/builtin/providers/openstack/import_openstack_dns_zone_v2_test.go @@ -1,12 +1,15 @@ package openstack import ( + "fmt" "testing" + "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" ) func TestAccDNSV2Zone_importBasic(t *testing.T) { + var zoneName = fmt.Sprintf("ACPTTEST%s.com.", acctest.RandString(5)) resourceName := "openstack_dns_zone_v2.zone_1" resource.Test(t, resource.TestCase{ @@ -15,7 +18,7 @@ func TestAccDNSV2Zone_importBasic(t *testing.T) { CheckDestroy: testAccCheckDNSV2ZoneDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccDNSV2Zone_basic, + Config: testAccDNSV2Zone_basic(zoneName), }, resource.TestStep{ diff --git a/builtin/providers/openstack/resource_openstack_dns_zone_v2.go b/builtin/providers/openstack/resource_openstack_dns_zone_v2.go index 1e2a6a676..542502fc2 100644 --- a/builtin/providers/openstack/resource_openstack_dns_zone_v2.go +++ b/builtin/providers/openstack/resource_openstack_dns_zone_v2.go @@ -3,10 +3,12 @@ package openstack import ( "fmt" "log" - "strconv" "time" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/dns/v2/zones" + + "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" ) @@ -22,6 +24,7 @@ func resourceDNSZoneV2() *schema.Resource { Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(10 * time.Minute), + Update: schema.DefaultTimeout(10 * time.Minute), Delete: schema.DefaultTimeout(10 * time.Minute), }, @@ -43,9 +46,11 @@ func resourceDNSZoneV2() *schema.Resource { ForceNew: false, }, "type": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ForceNew: true, + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: resourceDNSZoneV2ValidType, }, "attributes": &schema.Schema{ Type: schema.TypeMap, @@ -55,6 +60,7 @@ func resourceDNSZoneV2() *schema.Resource { "ttl": &schema.Schema{ Type: schema.TypeInt, Optional: true, + Computed: true, ForceNew: false, }, "description": &schema.Schema{ @@ -114,10 +120,22 @@ func resourceDNSZoneV2Create(d *schema.ResourceData, meta interface{}) error { if err != nil { return fmt.Errorf("Error creating OpenStack DNS zone: %s", err) } - log.Printf("[INFO] Zone ID: %s", n.ID) + + log.Printf("[DEBUG] Waiting for DNS Zone (%s) to become available", n.ID) + stateConf := &resource.StateChangeConf{ + Target: []string{"ACTIVE"}, + Pending: []string{"PENDING"}, + Refresh: waitForDNSZone(dnsClient, n.ID), + Timeout: d.Timeout(schema.TimeoutCreate), + Delay: 5 * time.Second, + MinTimeout: 3 * time.Second, + } + + _, err = stateConf.WaitForState() d.SetId(n.ID) + log.Printf("[DEBUG] Created OpenStack DNS Zone %s: %#v", n.ID, n) return resourceDNSZoneV2Read(d, meta) } @@ -133,12 +151,12 @@ func resourceDNSZoneV2Read(d *schema.ResourceData, meta interface{}) error { return CheckDeleted(d, err, "zone") } - log.Printf("[DEBUG] Retrieved Zone %s: %+v", d.Id(), n) + log.Printf("[DEBUG] Retrieved Zone %s: %#v", d.Id(), n) d.Set("name", n.Name) d.Set("email", n.Email) d.Set("description", n.Description) - d.Set("ttl", strconv.Itoa(n.TTL)) + d.Set("ttl", n.TTL) d.Set("type", n.Type) d.Set("attributes", n.Attributes) d.Set("masters", n.Masters) @@ -168,13 +186,25 @@ func resourceDNSZoneV2Update(d *schema.ResourceData, meta interface{}) error { updateOpts.Description = d.Get("description").(string) } - log.Printf("[DEBUG] Updating Zone %s with options: %+v", d.Id(), updateOpts) + log.Printf("[DEBUG] Updating Zone %s with options: %#v", d.Id(), updateOpts) _, err = zones.Update(dnsClient, d.Id(), updateOpts).Extract() if err != nil { return fmt.Errorf("Error updating OpenStack DNS Zone: %s", err) } + log.Printf("[DEBUG] Waiting for DNS Zone (%s) to update", d.Id()) + stateConf := &resource.StateChangeConf{ + Target: []string{"ACTIVE"}, + Pending: []string{"PENDING"}, + Refresh: waitForDNSZone(dnsClient, d.Id()), + Timeout: d.Timeout(schema.TimeoutUpdate), + Delay: 5 * time.Second, + MinTimeout: 3 * time.Second, + } + + _, err = stateConf.WaitForState() + return resourceDNSZoneV2Read(d, meta) } @@ -190,6 +220,52 @@ func resourceDNSZoneV2Delete(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("Error deleting OpenStack DNS Zone: %s", err) } + log.Printf("[DEBUG] Waiting for DNS Zone (%s) to become available", d.Id()) + stateConf := &resource.StateChangeConf{ + Target: []string{"DELETED"}, + Pending: []string{"ACTIVE", "PENDING"}, + Refresh: waitForDNSZone(dnsClient, d.Id()), + Timeout: d.Timeout(schema.TimeoutDelete), + Delay: 5 * time.Second, + MinTimeout: 3 * time.Second, + } + + _, err = stateConf.WaitForState() + d.SetId("") return nil } + +func resourceDNSZoneV2ValidType(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + validTypes := []string{ + "PRIMARY", + "SECONDARY", + } + + for _, v := range validTypes { + if value == v { + return + } + } + + err := fmt.Errorf("%s must be one of %s", k, validTypes) + errors = append(errors, err) + return +} + +func waitForDNSZone(dnsClient *gophercloud.ServiceClient, zoneId string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + zone, err := zones.Get(dnsClient, zoneId).Extract() + if err != nil { + if _, ok := err.(gophercloud.ErrDefault404); ok { + return zone, "DELETED", nil + } + + return nil, "", err + } + + log.Printf("[DEBUG] OpenStack DNS Zone (%s) current status: %s", zone.ID, zone.Status) + return zone, zone.Status, nil + } +} diff --git a/builtin/providers/openstack/resource_openstack_dns_zone_v2_test.go b/builtin/providers/openstack/resource_openstack_dns_zone_v2_test.go index f656321d0..8b06d28e4 100644 --- a/builtin/providers/openstack/resource_openstack_dns_zone_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_dns_zone_v2_test.go @@ -3,8 +3,10 @@ package openstack import ( "fmt" "os" + "regexp" "testing" + "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" @@ -13,6 +15,7 @@ import ( func TestAccDNSV2Zone_basic(t *testing.T) { var zone zones.Zone + var zoneName = fmt.Sprintf("ACPTTEST%s.com.", acctest.RandString(5)) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheckDNSZoneV2(t) }, @@ -20,17 +23,44 @@ func TestAccDNSV2Zone_basic(t *testing.T) { CheckDestroy: testAccCheckDNSV2ZoneDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccDNSV2Zone_basic, + Config: testAccDNSV2Zone_basic(zoneName), Check: resource.ComposeTestCheckFunc( testAccCheckDNSV2ZoneExists("openstack_dns_zone_v2.zone_1", &zone), + resource.TestCheckResourceAttr( + "openstack_dns_zone_v2.zone_1", "description", "a zone"), ), }, resource.TestStep{ - Config: testAccDNSV2Zone_update, + Config: testAccDNSV2Zone_update(zoneName), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("openstack_dns_zone_v2.zone_1", "name", "example.com"), + resource.TestCheckResourceAttr("openstack_dns_zone_v2.zone_1", "name", zoneName), resource.TestCheckResourceAttr("openstack_dns_zone_v2.zone_1", "email", "email2@example.com"), resource.TestCheckResourceAttr("openstack_dns_zone_v2.zone_1", "ttl", "6000"), + resource.TestCheckResourceAttr("openstack_dns_zone_v2.zone_1", "type", "PRIMARY"), + resource.TestCheckResourceAttr( + "openstack_dns_zone_v2.zone_1", "description", "an updated zone"), + ), + }, + }, + }) +} + +func TestAccDNSV2Zone_readTTL(t *testing.T) { + var zone zones.Zone + var zoneName = fmt.Sprintf("ACPTTEST%s.com.", acctest.RandString(5)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheckDNSZoneV2(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDNSV2ZoneDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDNSV2Zone_readTTL(zoneName), + Check: resource.ComposeTestCheckFunc( + testAccCheckDNSV2ZoneExists("openstack_dns_zone_v2.zone_1", &zone), + resource.TestCheckResourceAttr("openstack_dns_zone_v2.zone_1", "type", "PRIMARY"), + resource.TestMatchResourceAttr( + "openstack_dns_zone_v2.zone_1", "ttl", regexp.MustCompile("^[0-9]+$")), ), }, }, @@ -39,6 +69,7 @@ func TestAccDNSV2Zone_basic(t *testing.T) { func TestAccDNSV2Zone_timeout(t *testing.T) { var zone zones.Zone + var zoneName = fmt.Sprintf("ACPTTEST%s.com.", acctest.RandString(5)) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheckDNSZoneV2(t) }, @@ -46,7 +77,7 @@ func TestAccDNSV2Zone_timeout(t *testing.T) { CheckDestroy: testAccCheckDNSV2ZoneDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccDNSV2Zone_timeout, + Config: testAccDNSV2Zone_timeout(zoneName), Check: resource.ComposeTestCheckFunc( testAccCheckDNSV2ZoneExists("openstack_dns_zone_v2.zone_1", &zone), ), @@ -115,31 +146,51 @@ func testAccPreCheckDNSZoneV2(t *testing.T) { } } -const testAccDNSV2Zone_basic = ` -resource "openstack_dns_zone_v2" "zone_1" { - name = "example.com." - email = "email1@example.com" - ttl = 3000 +func testAccDNSV2Zone_basic(zoneName string) string { + return fmt.Sprintf(` + resource "openstack_dns_zone_v2" "zone_1" { + name = "%s" + email = "email1@example.com" + description = "a zone" + ttl = 3000 + type = "PRIMARY" + } + `, zoneName) } -` -const testAccDNSV2Zone_update = ` -resource "openstack_dns_zone_v2" "zone_1" { - name = "example.com." - email = "email2@example.com" - ttl = 6000 +func testAccDNSV2Zone_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" + } + `, zoneName) } -` -const testAccDNSV2Zone_timeout = ` -resource "openstack_dns_zone_v2" "zone_1" { - name = "example.com." - email = "email@example.com" - ttl = 3000 - - timeouts { - create = "5m" - delete = "5m" - } +func testAccDNSV2Zone_readTTL(zoneName string) string { + return fmt.Sprintf(` + resource "openstack_dns_zone_v2" "zone_1" { + name = "%s" + email = "email1@example.com" + } + `, zoneName) +} + +func testAccDNSV2Zone_timeout(zoneName string) string { + return fmt.Sprintf(` + resource "openstack_dns_zone_v2" "zone_1" { + name = "%s" + email = "email@example.com" + ttl = 3000 + + timeouts { + create = "5m" + update = "5m" + delete = "5m" + } + } + `, zoneName) } -` diff --git a/builtin/providers/openstack/types.go b/builtin/providers/openstack/types.go index b586e10b0..6acd04ec0 100644 --- a/builtin/providers/openstack/types.go +++ b/builtin/providers/openstack/types.go @@ -306,6 +306,10 @@ func (opts ZoneCreateOpts) ToZoneCreateMap() (map[string]interface{}, error) { } if m, ok := b[""].(map[string]interface{}); ok { + if opts.TTL > 0 { + m["ttl"] = opts.TTL + } + return m, nil } diff --git a/website/source/docs/providers/openstack/r/dns_zone_v2.html.markdown b/website/source/docs/providers/openstack/r/dns_zone_v2.html.markdown new file mode 100644 index 000000000..bb499670c --- /dev/null +++ b/website/source/docs/providers/openstack/r/dns_zone_v2.html.markdown @@ -0,0 +1,78 @@ +--- +layout: "openstack" +page_title: "OpenStack: openstack_dns_zone_v2" +sidebar_current: "docs-openstack-resource-dns-zone-v2" +description: |- + Manages a DNS zone in the OpenStack DNS Service +--- + +# openstack\_dns\_zone_v2 + +Manages a DNS zone in the OpenStack DNS Service. + +## Example Usage + +### Automatically detect the correct network + +```hcl +resource "openstack_dns_zone_v2" "example.com" { + name = "example.com." + email = "jdoe@example.com" + description = "An example zone" + ttl = 3000 + type = "PRIMARY" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `region` - (Required) The region in which to obtain the V2 Compute client. + Keypairs are associated with accounts, but a Compute client is needed to + create one. If omitted, the `OS_REGION_NAME` environment variable is used. + Changing this creates a new DNS zone. + +* `name` - (Required) The name of the zone. Note the `.` at the end of the name. + Changing this creates a new DNS zone. + +* `email` - (Optional) The email contact for the zone record. + +* `type` - (Optional) The type of zone. Can either be `PRIMARY` or `SECONDARY`. + Changing this creates a new zone. + +* `attributes` - (Optional) Attributes for the DNS Service scheduler. + Changing this creates a new zone. + +* `ttl` - (Optional) The time to live (TTL) of the zone. + +* `description` - (Optional) A description of the zone. + +* `masters` - (Optional) An array of master DNS servers. For when `type` is + `SECONDARY`. + +* `value_specs` - (Optional) Map of additional options. Changing this creates a + new zone. + +## Attributes Reference + +The following attributes are exported: + +* `region` - See Argument Reference above. +* `name` - See Argument Reference above. +* `email` - See Argument Reference above. +* `type` - See Argument Reference above. +* `attributes` - See Argument Reference above. +* `ttl` - See Argument Reference above. +* `description` - See Argument Reference above. +* `masters` - See Argument Reference above. +* `value_specs` - See Argument Reference above. + +## Import + +This resource can be imported by specifying all three arguments, separated +by a forward slash: + +``` +$ terraform import openstack_dns_zone_v2.zone_1 +``` diff --git a/website/source/layouts/openstack.erb b/website/source/layouts/openstack.erb index 0c0c13241..30fe4a574 100644 --- a/website/source/layouts/openstack.erb +++ b/website/source/layouts/openstack.erb @@ -64,6 +64,15 @@ + > + DNS Resources + + + > Images Resources