From 986b9fcc5319b27e6f50c05f65b521e02a3f0350 Mon Sep 17 00:00:00 2001 From: jrperritt Date: Thu, 18 May 2017 16:24:26 -0500 Subject: [PATCH 1/3] vendor: Updating Gophercloud for OpenStack Provider --- .../gophercloud/gophercloud/README.md | 2 +- .../gophercloud/gophercloud/auth_options.go | 20 +-- .../github.com/gophercloud/gophercloud/doc.go | 8 +- .../gophercloud/openstack/dns/v2/zones/doc.go | 6 + .../openstack/dns/v2/zones/requests.go | 162 ++++++++++++++++++ .../openstack/dns/v2/zones/results.go | 159 +++++++++++++++++ .../openstack/dns/v2/zones/urls.go | 11 ++ vendor/vendor.json | 12 +- 8 files changed, 361 insertions(+), 19 deletions(-) create mode 100644 vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/doc.go create mode 100644 vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/requests.go create mode 100644 vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/results.go create mode 100644 vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/urls.go diff --git a/vendor/github.com/gophercloud/gophercloud/README.md b/vendor/github.com/gophercloud/gophercloud/README.md index 8d8d0a5c6..60ca479de 100644 --- a/vendor/github.com/gophercloud/gophercloud/README.md +++ b/vendor/github.com/gophercloud/gophercloud/README.md @@ -74,7 +74,7 @@ import ( // Option 1: Pass in the values yourself opts := gophercloud.AuthOptions{ - IdentityEndpoint: "https://my-openstack.com:5000/v2.0", + IdentityEndpoint: "https://openstack.example.com:5000/v2.0", Username: "{username}", Password: "{password}", } diff --git a/vendor/github.com/gophercloud/gophercloud/auth_options.go b/vendor/github.com/gophercloud/gophercloud/auth_options.go index 3ee97dfb3..eabf18207 100644 --- a/vendor/github.com/gophercloud/gophercloud/auth_options.go +++ b/vendor/github.com/gophercloud/gophercloud/auth_options.go @@ -1,7 +1,7 @@ package gophercloud /* -AuthOptions stores information needed to authenticate to an OpenStack cluster. +AuthOptions stores information needed to authenticate to an OpenStack Cloud. You can populate one manually, or use a provider's AuthOptionsFromEnv() function to read relevant information from the standard environment variables. Pass one to a provider's AuthenticatedClient function to authenticate and obtain a @@ -31,9 +31,16 @@ type AuthOptions struct { DomainName string `json:"name,omitempty"` // The TenantID and TenantName fields are optional for the Identity V2 API. + // The same fields are known as project_id and project_name in the Identity + // V3 API, but are collected as TenantID and TenantName here in both cases. // Some providers allow you to specify a TenantName instead of the TenantId. // Some require both. Your provider's authentication policies will determine // how these fields influence authentication. + // If DomainID or DomainName are provided, they will also apply to TenantName. + // It is not currently possible to authenticate with Username and a Domain + // and scope to a Project in a different Domain by using TenantName. To + // accomplish that, the ProjectID will need to be provided to the TenantID + // option. TenantID string `json:"tenantId,omitempty"` TenantName string `json:"tenantName,omitempty"` @@ -132,14 +139,6 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s // if insufficient or incompatible information is present. var req request - // Test first for unrecognized arguments. - if opts.TenantID != "" { - return nil, ErrTenantIDProvided{} - } - if opts.TenantName != "" { - return nil, ErrTenantNameProvided{} - } - if opts.Password == "" { if opts.TokenID != "" { // Because we aren't using password authentication, it's an error to also provide any of the user-based authentication @@ -252,15 +251,12 @@ func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { if opts.TenantID != "" { scope.ProjectID = opts.TenantID - opts.TenantID = "" - opts.TenantName = "" } else { if opts.TenantName != "" { scope.ProjectName = opts.TenantName scope.DomainID = opts.DomainID scope.DomainName = opts.DomainName } - opts.TenantName = "" } if scope.ProjectName != "" { diff --git a/vendor/github.com/gophercloud/gophercloud/doc.go b/vendor/github.com/gophercloud/gophercloud/doc.go index fb81a9d8f..b559516f9 100644 --- a/vendor/github.com/gophercloud/gophercloud/doc.go +++ b/vendor/github.com/gophercloud/gophercloud/doc.go @@ -4,11 +4,13 @@ clouds. The library has a three-level hierarchy: providers, services, and resources. Provider structs represent the service providers that offer and manage a -collection of services. Examples of providers include: OpenStack, Rackspace, -HP. These are defined like so: +collection of services. The IdentityEndpoint is typically refered to as +"auth_url" in information provided by the cloud operator. Additionally, +the cloud may refer to TenantID or TenantName as project_id and project_name. +These are defined like so: opts := gophercloud.AuthOptions{ - IdentityEndpoint: "https://my-openstack.com:5000/v2.0", + IdentityEndpoint: "https://openstack.example.com:5000/v2.0", Username: "{username}", Password: "{password}", TenantID: "{tenant_id}", diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/doc.go b/vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/doc.go new file mode 100644 index 000000000..1302cb93d --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/doc.go @@ -0,0 +1,6 @@ +// Package tokens 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/#zone +package zones diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/requests.go new file mode 100644 index 000000000..1c818a38a --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/requests.go @@ -0,0 +1,162 @@ +package zones + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +type ListOptsBuilder interface { + ToZoneListQuery() (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 zone at which you want to set a marker. + Marker string `q:"marker"` + + Description string `q:"description"` + Email string `q:"email"` + 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"` +} + +func (opts ListOpts) ToZoneListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := baseURL(client) + if opts != nil { + query, err := opts.ToZoneListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return ZonePage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Get returns additional information about a zone, given its ID. +func Get(client *gophercloud.ServiceClient, zoneID string) (r GetResult) { + _, r.Err = client.Get(zoneURL(client, zoneID), &r.Body, nil) + return +} + +// CreateOptsBuilder allows extensions to add additional attributes to the Update request. +type CreateOptsBuilder interface { + ToZoneCreateMap() (map[string]interface{}, error) +} + +// CreateOpts specifies the base attributes used to create a zone. +type CreateOpts struct { + // Attributes are settings that supply hints and filters for the zone. + Attributes map[string]string `json:"attributes,omitempty"` + + // Email contact of the zone. + Email string `json:"email,omitempty"` + + // Description of the zone. + Description string `json:"description,omitempty"` + + // Name of the zone. + Name string `json:"name,required"` + + // Masters specifies zone masters if this is a secondary zone. + Masters []string `json:"masters,omitempty"` + + // TTL is the time to live of the zone. + TTL int `json:"-"` + + // Type specifies if this is a primary or secondary zone. + Type string `json:"type,omitempty"` +} + +// ToZoneCreateMap formats an CreateOpts structure into a request body. +func (opts CreateOpts) ToZoneCreateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + + if opts.TTL > 0 { + b["ttl"] = opts.TTL + } + + return b, nil +} + +// Create a zone +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToZoneCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201, 202}, + }) + return +} + +// UpdateOptsBuilder allows extensions to add additional attributes to the Update request. +type UpdateOptsBuilder interface { + ToZoneUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts specifies the base attributes to update a zone. +type UpdateOpts struct { + Email string `json:"email,omitempty"` + TTL int `json:"-"` + Masters []string `json:"masters,omitempty"` + Description string `json:"description,omitempty"` +} + +// ToZoneUpdateMap formats an UpdateOpts structure into a request body. +func (opts UpdateOpts) ToZoneUpdateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + + if opts.TTL > 0 { + b["ttl"] = opts.TTL + } + + return b, nil +} + +// Update a zone. +func Update(client *gophercloud.ServiceClient, zoneID string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToZoneUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Patch(zoneURL(client, zoneID), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 202}, + }) + return +} + +// Delete a zone. +func Delete(client *gophercloud.ServiceClient, zoneID string) (r DeleteResult) { + _, r.Err = client.Delete(zoneURL(client, zoneID), &gophercloud.RequestOpts{ + OkCodes: []int{202}, + JSONResponse: &r.Body, + }) + return +} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/results.go new file mode 100644 index 000000000..de8069567 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/results.go @@ -0,0 +1,159 @@ +package zones + +import ( + "encoding/json" + "strconv" + "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 Zone. +// An error is returned if the original call or the extraction failed. +func (r commonResult) Extract() (*Zone, error) { + var s *Zone + 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 +} + +// 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 { + commonResult +} + +// ZonePage is a single page of Zone results. +type ZonePage struct { + pagination.LinkedPageBase +} + +// IsEmpty returns true if the page contains no results. +func (r ZonePage) IsEmpty() (bool, error) { + s, err := ExtractZones(r) + return len(s) == 0, err +} + +// ExtractZones extracts a slice of Services from a Collection acquired from List. +func ExtractZones(r pagination.Page) ([]Zone, error) { + var s struct { + Zones []Zone `json:"zones"` + } + err := (r.(ZonePage)).ExtractInto(&s) + return s.Zones, err +} + +// Zone represents a DNS zone. +type Zone struct { + // ID uniquely identifies this zone amongst all other zones, including those not accessible to the current tenant. + ID string `json:"id"` + + // PoolID is the ID for the pool hosting this zone. + PoolID string `json:"pool_id"` + + // ProjectID identifies the project/tenant owning this resource. + ProjectID string `json:"project_id"` + + // Name is the DNS Name for the zone. + Name string `json:"name"` + + // Email for the zone. Used in SOA records for the zone. + Email string `json:"email"` + + // Description for this zone. + Description string `json:"description"` + + // TTL is the Time to Live for the zone. + TTL int `json:"ttl"` + + // Serial is the current serial number for the zone. + Serial int `json:"-"` + + // Status is the status of the resource. + Status string `json:"status"` + + // Action is the current action in progress on the resource. + Action string `json:"action"` + + // Version of the resource. + Version int `json:"version"` + + // Attributes for the zone. + Attributes map[string]string `json:"attributes"` + + // Type of zone. Primary is controlled by Designate. + // Secondary zones are slaved from another DNS Server. + // Defaults to Primary. + Type string `json:"type"` + + // Masters is the servers for slave servers to get DNS information from. + Masters []string `json:"masters"` + + // CreatedAt is the date when the zone was created. + CreatedAt time.Time `json:"-"` + + // UpdatedAt is the date when the last change was made to the zone. + UpdatedAt time.Time `json:"-"` + + // TransferredAt is the last time an update was retrieved from the master servers. + TransferredAt time.Time `json:"-"` + + // Links includes HTTP references to the itself, useful for passing along to other APIs that might want a server reference. + Links map[string]interface{} `json:"links"` +} + +func (r *Zone) UnmarshalJSON(b []byte) error { + type tmp Zone + var s struct { + tmp + CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` + TransferredAt gophercloud.JSONRFC3339MilliNoZ `json:"transferred_at"` + Serial interface{} `json:"serial"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Zone(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + r.TransferredAt = time.Time(s.TransferredAt) + + switch t := s.Serial.(type) { + case float64: + r.Serial = int(t) + case string: + switch t { + case "": + r.Serial = 0 + default: + serial, err := strconv.ParseFloat(t, 64) + if err != nil { + return err + } + r.Serial = int(serial) + } + } + + return err +} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/urls.go b/vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/urls.go new file mode 100644 index 000000000..9bef70580 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/urls.go @@ -0,0 +1,11 @@ +package zones + +import "github.com/gophercloud/gophercloud" + +func baseURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("zones") +} + +func zoneURL(c *gophercloud.ServiceClient, zoneID string) string { + return c.ServiceURL("zones", zoneID) +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 09a55c3d8..72d6a5005 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1605,10 +1605,10 @@ "revisionTime": "2016-11-07T00:24:06Z" }, { - "checksumSHA1": "xKB/9qxVhWxAERkjZLYfuUBR4P8=", + "checksumSHA1": "cuTQkSEiIoMhDhB7xCbYDwDhrgw=", "path": "github.com/gophercloud/gophercloud", - "revision": "0f64da0e36de86a0ca1a8f2fc1b0570a0d3f7504", - "revisionTime": "2017-03-10T01:59:53Z" + "revision": "c5dfe1bc7b150a98b1578bb32f7c138ff9568f1d", + "revisionTime": "2017-05-15T02:07:51Z" }, { "checksumSHA1": "0KdIjTH5IO8hlIl8kdfI6313GiY=", @@ -1712,6 +1712,12 @@ "revision": "0f64da0e36de86a0ca1a8f2fc1b0570a0d3f7504", "revisionTime": "2017-03-10T01:59:53Z" }, + { + "checksumSHA1": "OGb3suDcMij0bnu0r/eQgWAX5Ho=", + "path": "github.com/gophercloud/gophercloud/openstack/dns/v2/zones", + "revision": "c5dfe1bc7b150a98b1578bb32f7c138ff9568f1d", + "revisionTime": "2017-05-15T02:07:51Z" + }, { "checksumSHA1": "1sVqsZBZBNhDXLY9XzjMkcOkcbg=", "path": "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants", From 5792b9fe765572f1c826a8b1c805e8e1e5e5c575 Mon Sep 17 00:00:00 2001 From: jrperritt Date: Thu, 18 May 2017 16:25:28 -0500 Subject: [PATCH 2/3] provider/openstack: Add openstack_dns_zone_v2 resource --- builtin/providers/openstack/config.go | 7 + .../import_openstack_dns_zone_v2_test.go | 28 +++ builtin/providers/openstack/provider.go | 1 + .../resource_openstack_dns_zone_v2.go | 195 ++++++++++++++++++ .../resource_openstack_dns_zone_v2_test.go | 145 +++++++++++++ builtin/providers/openstack/types.go | 23 +++ 6 files changed, 399 insertions(+) create mode 100644 builtin/providers/openstack/import_openstack_dns_zone_v2_test.go create mode 100644 builtin/providers/openstack/resource_openstack_dns_zone_v2.go create mode 100644 builtin/providers/openstack/resource_openstack_dns_zone_v2_test.go diff --git a/builtin/providers/openstack/config.go b/builtin/providers/openstack/config.go index 0c6cb8995..142672af5 100644 --- a/builtin/providers/openstack/config.go +++ b/builtin/providers/openstack/config.go @@ -152,6 +152,13 @@ func (c *Config) computeV2Client(region string) (*gophercloud.ServiceClient, err }) } +func (c *Config) dnsV2Client(region string) (*gophercloud.ServiceClient, error) { + return openstack.NewDNSV2(c.osClient, gophercloud.EndpointOpts{ + Region: region, + Availability: c.getEndpointType(), + }) +} + func (c *Config) imageV2Client(region string) (*gophercloud.ServiceClient, error) { return openstack.NewImageServiceV2(c.osClient, gophercloud.EndpointOpts{ Region: region, diff --git a/builtin/providers/openstack/import_openstack_dns_zone_v2_test.go b/builtin/providers/openstack/import_openstack_dns_zone_v2_test.go new file mode 100644 index 000000000..ca7ec7a37 --- /dev/null +++ b/builtin/providers/openstack/import_openstack_dns_zone_v2_test.go @@ -0,0 +1,28 @@ +package openstack + +import ( + "testing" + + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccDNSV2Zone_importBasic(t *testing.T) { + resourceName := "openstack_dns_zone_v2.zone_1" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheckDNSZoneV2(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDNSV2ZoneDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDNSV2Zone_basic, + }, + + resource.TestStep{ + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} diff --git a/builtin/providers/openstack/provider.go b/builtin/providers/openstack/provider.go index 294f207e9..1290ee266 100644 --- a/builtin/providers/openstack/provider.go +++ b/builtin/providers/openstack/provider.go @@ -150,6 +150,7 @@ func Provider() terraform.ResourceProvider { "openstack_compute_floatingip_v2": resourceComputeFloatingIPV2(), "openstack_compute_floatingip_associate_v2": resourceComputeFloatingIPAssociateV2(), "openstack_compute_volume_attach_v2": resourceComputeVolumeAttachV2(), + "openstack_dns_zone_v2": resourceDNSZoneV2(), "openstack_fw_firewall_v1": resourceFWFirewallV1(), "openstack_fw_policy_v1": resourceFWPolicyV1(), "openstack_fw_rule_v1": resourceFWRuleV1(), diff --git a/builtin/providers/openstack/resource_openstack_dns_zone_v2.go b/builtin/providers/openstack/resource_openstack_dns_zone_v2.go new file mode 100644 index 000000000..1e2a6a676 --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_dns_zone_v2.go @@ -0,0 +1,195 @@ +package openstack + +import ( + "fmt" + "log" + "strconv" + "time" + + "github.com/gophercloud/gophercloud/openstack/dns/v2/zones" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceDNSZoneV2() *schema.Resource { + return &schema.Resource{ + Create: resourceDNSZoneV2Create, + Read: resourceDNSZoneV2Read, + Update: resourceDNSZoneV2Update, + Delete: resourceDNSZoneV2Delete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: 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", ""), + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "email": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + "type": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "attributes": &schema.Schema{ + Type: schema.TypeMap, + Optional: true, + ForceNew: true, + }, + "ttl": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + ForceNew: false, + }, + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + "masters": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + ForceNew: false, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "value_specs": &schema.Schema{ + Type: schema.TypeMap, + Optional: true, + ForceNew: true, + }, + }, + } +} + +func resourceDNSZoneV2Create(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) + } + + mastersraw := d.Get("masters").(*schema.Set).List() + masters := make([]string, len(mastersraw)) + for i, masterraw := range mastersraw { + masters[i] = masterraw.(string) + } + + attrsraw := d.Get("attributes").(map[string]interface{}) + attrs := make(map[string]string, len(attrsraw)) + for k, v := range attrsraw { + attrs[k] = v.(string) + } + + createOpts := ZoneCreateOpts{ + zones.CreateOpts{ + Name: d.Get("name").(string), + Type: d.Get("type").(string), + Attributes: attrs, + TTL: d.Get("ttl").(int), + Email: d.Get("email").(string), + Description: d.Get("description").(string), + Masters: masters, + }, + MapValueSpecs(d), + } + + log.Printf("[DEBUG] Create Options: %#v", createOpts) + n, err := zones.Create(dnsClient, createOpts).Extract() + if err != nil { + return fmt.Errorf("Error creating OpenStack DNS zone: %s", err) + } + log.Printf("[INFO] Zone ID: %s", n.ID) + + d.SetId(n.ID) + + return resourceDNSZoneV2Read(d, meta) +} + +func resourceDNSZoneV2Read(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) + } + + n, err := zones.Get(dnsClient, d.Id()).Extract() + if err != nil { + return CheckDeleted(d, err, "zone") + } + + 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("type", n.Type) + d.Set("attributes", n.Attributes) + d.Set("masters", n.Masters) + d.Set("region", GetRegion(d)) + + return nil +} + +func resourceDNSZoneV2Update(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 zones.UpdateOpts + if d.HasChange("email") { + updateOpts.Email = d.Get("email").(string) + } + if d.HasChange("ttl") { + updateOpts.TTL = d.Get("ttl").(int) + } + if d.HasChange("masters") { + updateOpts.Masters = d.Get("masters").([]string) + } + if d.HasChange("description") { + updateOpts.Description = d.Get("description").(string) + } + + 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) + } + + return resourceDNSZoneV2Read(d, meta) +} + +func resourceDNSZoneV2Delete(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) + } + + _, err = zones.Delete(dnsClient, d.Id()).Extract() + if err != nil { + return fmt.Errorf("Error deleting OpenStack DNS Zone: %s", err) + } + + d.SetId("") + return 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 new file mode 100644 index 000000000..f656321d0 --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_dns_zone_v2_test.go @@ -0,0 +1,145 @@ +package openstack + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + + "github.com/gophercloud/gophercloud/openstack/dns/v2/zones" +) + +func TestAccDNSV2Zone_basic(t *testing.T) { + var zone zones.Zone + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheckDNSZoneV2(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDNSV2ZoneDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDNSV2Zone_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckDNSV2ZoneExists("openstack_dns_zone_v2.zone_1", &zone), + ), + }, + resource.TestStep{ + Config: testAccDNSV2Zone_update, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("openstack_dns_zone_v2.zone_1", "name", "example.com"), + resource.TestCheckResourceAttr("openstack_dns_zone_v2.zone_1", "email", "email2@example.com"), + resource.TestCheckResourceAttr("openstack_dns_zone_v2.zone_1", "ttl", "6000"), + ), + }, + }, + }) +} + +func TestAccDNSV2Zone_timeout(t *testing.T) { + var zone zones.Zone + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheckDNSZoneV2(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDNSV2ZoneDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDNSV2Zone_timeout, + Check: resource.ComposeTestCheckFunc( + testAccCheckDNSV2ZoneExists("openstack_dns_zone_v2.zone_1", &zone), + ), + }, + }, + }) +} + +func testAccCheckDNSV2ZoneDestroy(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_zone_v2" { + continue + } + + _, err := zones.Get(dnsClient, rs.Primary.ID).Extract() + if err == nil { + return fmt.Errorf("Zone still exists") + } + } + + return nil +} + +func testAccCheckDNSV2ZoneExists(n string, zone *zones.Zone) 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) + } + + found, err := zones.Get(dnsClient, rs.Primary.ID).Extract() + if err != nil { + return err + } + + if found.ID != rs.Primary.ID { + return fmt.Errorf("Zone not found") + } + + *zone = *found + + return nil + } +} + +func testAccPreCheckDNSZoneV2(t *testing.T) { + v := os.Getenv("OS_AUTH_URL") + if v == "" { + t.Fatal("OS_AUTH_URL must be set for acceptance tests") + } +} + +const testAccDNSV2Zone_basic = ` +resource "openstack_dns_zone_v2" "zone_1" { + name = "example.com." + email = "email1@example.com" + ttl = 3000 +} +` + +const testAccDNSV2Zone_update = ` +resource "openstack_dns_zone_v2" "zone_1" { + name = "example.com." + email = "email2@example.com" + ttl = 6000 +} +` + +const testAccDNSV2Zone_timeout = ` +resource "openstack_dns_zone_v2" "zone_1" { + name = "example.com." + email = "email@example.com" + ttl = 3000 + + timeouts { + create = "5m" + delete = "5m" + } +} +` diff --git a/builtin/providers/openstack/types.go b/builtin/providers/openstack/types.go index c6c6a268a..b586e10b0 100644 --- a/builtin/providers/openstack/types.go +++ b/builtin/providers/openstack/types.go @@ -3,6 +3,7 @@ package openstack import ( "bytes" "encoding/json" + "fmt" "io" "io/ioutil" "log" @@ -11,6 +12,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups" + "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/policies" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/rules" @@ -288,3 +290,24 @@ func (opts SubnetCreateOpts) ToSubnetCreateMap() (map[string]interface{}, error) return b, nil } + +// ZoneCreateOpts represents the attributes used when creating a new DNS zone. +type ZoneCreateOpts struct { + zones.CreateOpts + ValueSpecs map[string]string `json:"value_specs,omitempty"` +} + +// ToZoneCreateMap casts a CreateOpts struct to a map. +// It overrides zones.ToZoneCreateMap to add the ValueSpecs field. +func (opts ZoneCreateOpts) ToZoneCreateMap() (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[""]) +} From e0b5f4f83387895c423005018e6357dabd578f5a Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 21 May 2017 22:39:52 +0000 Subject: [PATCH 3/3] provider/openstack: dns zone v2 updates and docs --- .../import_openstack_dns_zone_v2_test.go | 5 +- .../resource_openstack_dns_zone_v2.go | 92 +++++++++++++-- .../resource_openstack_dns_zone_v2_test.go | 105 +++++++++++++----- builtin/providers/openstack/types.go | 4 + .../openstack/r/dns_zone_v2.html.markdown | 78 +++++++++++++ website/source/layouts/openstack.erb | 9 ++ 6 files changed, 257 insertions(+), 36 deletions(-) create mode 100644 website/source/docs/providers/openstack/r/dns_zone_v2.html.markdown 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