mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-07-03 08:42:39 +00:00
![Paddy](/assets/img/avatar_default.png)
Cloud DNS requires every managed zone to have an NS record at all times. This means if people want to manage their own NS records, we need to add their new record and remove the old one in the same call. It also means we can't delete NS records, as we wouldn't know what to replace it with. So deleting of NS records short-circuits. For the case of `terraform destroy`, this prevents the error. It does mean if the user explicitly tries to remove their NS zone from their project, it silently does nothing, but that's unavoidable unless we want to A) restore a default value (and it looks like the default values change from zone to zone? And that is arguably just as unexpected?) or B) let the (arguably more reasonable) `terraform destroy` case be impossible.
276 lines
6.3 KiB
Go
276 lines
6.3 KiB
Go
package google
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
|
|
"github.com/hashicorp/terraform/helper/schema"
|
|
"google.golang.org/api/dns/v1"
|
|
)
|
|
|
|
func resourceDnsRecordSet() *schema.Resource {
|
|
return &schema.Resource{
|
|
Create: resourceDnsRecordSetCreate,
|
|
Read: resourceDnsRecordSetRead,
|
|
Delete: resourceDnsRecordSetDelete,
|
|
Update: resourceDnsRecordSetUpdate,
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
"managed_zone": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
ForceNew: true,
|
|
},
|
|
|
|
"name": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
ForceNew: true,
|
|
},
|
|
|
|
"rrdatas": &schema.Schema{
|
|
Type: schema.TypeList,
|
|
Required: true,
|
|
Elem: &schema.Schema{
|
|
Type: schema.TypeString,
|
|
},
|
|
},
|
|
|
|
"ttl": &schema.Schema{
|
|
Type: schema.TypeInt,
|
|
Required: true,
|
|
},
|
|
|
|
"type": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
},
|
|
|
|
"project": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func resourceDnsRecordSetCreate(d *schema.ResourceData, meta interface{}) error {
|
|
config := meta.(*Config)
|
|
|
|
project, err := getProject(d, config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
zone := d.Get("managed_zone").(string)
|
|
|
|
// Build the change
|
|
chg := &dns.Change{
|
|
Additions: []*dns.ResourceRecordSet{
|
|
&dns.ResourceRecordSet{
|
|
Name: d.Get("name").(string),
|
|
Type: d.Get("type").(string),
|
|
Ttl: int64(d.Get("ttl").(int)),
|
|
Rrdatas: rrdata(d),
|
|
},
|
|
},
|
|
}
|
|
|
|
if d.Get("type").(string) == "NS" {
|
|
log.Printf("{DEBUG] DNS record list request for %q", zone)
|
|
res, err := config.clientDns.ResourceRecordSets.List(project, zone).Do()
|
|
if err != nil {
|
|
return fmt.Errorf("Error retrieving record sets for %q: %s", zone, err)
|
|
}
|
|
var deletions []*dns.ResourceRecordSet
|
|
|
|
for _, record := range res.Rrsets {
|
|
if record.Type != "NS" {
|
|
continue
|
|
}
|
|
deletions = append(deletions, record)
|
|
}
|
|
if len(deletions) > 0 {
|
|
chg.Deletions = deletions
|
|
}
|
|
}
|
|
|
|
log.Printf("[DEBUG] DNS Record create request: %#v", chg)
|
|
chg, err = config.clientDns.Changes.Create(project, zone, chg).Do()
|
|
if err != nil {
|
|
return fmt.Errorf("Error creating DNS RecordSet: %s", err)
|
|
}
|
|
|
|
d.SetId(chg.Id)
|
|
|
|
w := &DnsChangeWaiter{
|
|
Service: config.clientDns,
|
|
Change: chg,
|
|
Project: project,
|
|
ManagedZone: zone,
|
|
}
|
|
_, err = w.Conf().WaitForState()
|
|
if err != nil {
|
|
return fmt.Errorf("Error waiting for Google DNS change: %s", err)
|
|
}
|
|
|
|
return resourceDnsRecordSetRead(d, meta)
|
|
}
|
|
|
|
func resourceDnsRecordSetRead(d *schema.ResourceData, meta interface{}) error {
|
|
config := meta.(*Config)
|
|
|
|
project, err := getProject(d, config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
zone := d.Get("managed_zone").(string)
|
|
|
|
// name and type are effectively the 'key'
|
|
name := d.Get("name").(string)
|
|
dnsType := d.Get("type").(string)
|
|
|
|
resp, err := config.clientDns.ResourceRecordSets.List(
|
|
project, zone).Name(name).Type(dnsType).Do()
|
|
if err != nil {
|
|
return handleNotFoundError(err, d, fmt.Sprintf("DNS Record Set %q", d.Get("name").(string)))
|
|
}
|
|
if len(resp.Rrsets) == 0 {
|
|
// The resource doesn't exist anymore
|
|
d.SetId("")
|
|
return nil
|
|
}
|
|
|
|
if len(resp.Rrsets) > 1 {
|
|
return fmt.Errorf("Only expected 1 record set, got %d", len(resp.Rrsets))
|
|
}
|
|
|
|
d.Set("ttl", resp.Rrsets[0].Ttl)
|
|
d.Set("rrdatas", resp.Rrsets[0].Rrdatas)
|
|
|
|
return nil
|
|
}
|
|
|
|
func resourceDnsRecordSetDelete(d *schema.ResourceData, meta interface{}) error {
|
|
|
|
// NS records must always have a value, so we short-circuit delete
|
|
// this allows terraform delete to work, but may have unexpected
|
|
// side-effects when deleting just that record set.
|
|
if d.Get("type").(string) == "NS" {
|
|
return nil
|
|
}
|
|
config := meta.(*Config)
|
|
|
|
project, err := getProject(d, config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
zone := d.Get("managed_zone").(string)
|
|
|
|
// Build the change
|
|
chg := &dns.Change{
|
|
Deletions: []*dns.ResourceRecordSet{
|
|
&dns.ResourceRecordSet{
|
|
Name: d.Get("name").(string),
|
|
Type: d.Get("type").(string),
|
|
Ttl: int64(d.Get("ttl").(int)),
|
|
Rrdatas: rrdata(d),
|
|
},
|
|
},
|
|
}
|
|
|
|
log.Printf("[DEBUG] DNS Record delete request: %#v", chg)
|
|
chg, err = config.clientDns.Changes.Create(project, zone, chg).Do()
|
|
if err != nil {
|
|
return fmt.Errorf("Error deleting DNS RecordSet: %s", err)
|
|
}
|
|
|
|
w := &DnsChangeWaiter{
|
|
Service: config.clientDns,
|
|
Change: chg,
|
|
Project: project,
|
|
ManagedZone: zone,
|
|
}
|
|
_, err = w.Conf().WaitForState()
|
|
if err != nil {
|
|
return fmt.Errorf("Error waiting for Google DNS change: %s", err)
|
|
}
|
|
|
|
d.SetId("")
|
|
return nil
|
|
}
|
|
|
|
func resourceDnsRecordSetUpdate(d *schema.ResourceData, meta interface{}) error {
|
|
config := meta.(*Config)
|
|
|
|
project, err := getProject(d, config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
zone := d.Get("managed_zone").(string)
|
|
recordName := d.Get("name").(string)
|
|
|
|
oldTtl, newTtl := d.GetChange("ttl")
|
|
oldType, newType := d.GetChange("type")
|
|
|
|
oldCountRaw, _ := d.GetChange("rrdatas.#")
|
|
oldCount := oldCountRaw.(int)
|
|
|
|
chg := &dns.Change{
|
|
Deletions: []*dns.ResourceRecordSet{
|
|
&dns.ResourceRecordSet{
|
|
Name: recordName,
|
|
Type: oldType.(string),
|
|
Ttl: int64(oldTtl.(int)),
|
|
Rrdatas: make([]string, oldCount),
|
|
},
|
|
},
|
|
Additions: []*dns.ResourceRecordSet{
|
|
&dns.ResourceRecordSet{
|
|
Name: recordName,
|
|
Type: newType.(string),
|
|
Ttl: int64(newTtl.(int)),
|
|
Rrdatas: rrdata(d),
|
|
},
|
|
},
|
|
}
|
|
|
|
for i := 0; i < oldCount; i++ {
|
|
rrKey := fmt.Sprintf("rrdatas.%d", i)
|
|
oldRR, _ := d.GetChange(rrKey)
|
|
chg.Deletions[0].Rrdatas[i] = oldRR.(string)
|
|
}
|
|
log.Printf("[DEBUG] DNS Record change request: %#v old: %#v new: %#v", chg, chg.Deletions[0], chg.Additions[0])
|
|
chg, err = config.clientDns.Changes.Create(project, zone, chg).Do()
|
|
if err != nil {
|
|
return fmt.Errorf("Error changing DNS RecordSet: %s", err)
|
|
}
|
|
|
|
w := &DnsChangeWaiter{
|
|
Service: config.clientDns,
|
|
Change: chg,
|
|
Project: project,
|
|
ManagedZone: zone,
|
|
}
|
|
if _, err = w.Conf().WaitForState(); err != nil {
|
|
return fmt.Errorf("Error waiting for Google DNS change: %s", err)
|
|
}
|
|
|
|
return resourceDnsRecordSetRead(d, meta)
|
|
}
|
|
|
|
func rrdata(
|
|
d *schema.ResourceData,
|
|
) []string {
|
|
rrdatasCount := d.Get("rrdatas.#").(int)
|
|
data := make([]string, rrdatasCount)
|
|
for i := 0; i < rrdatasCount; i++ {
|
|
data[i] = d.Get(fmt.Sprintf("rrdatas.%d", i)).(string)
|
|
}
|
|
return data
|
|
}
|