mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-20 11:48:24 -06:00
This changeset fixes how some digitalocean dns records were getting parsed. In particular, it allows for understanding "@" as shorthand for the domain itself, preventing terraform from suggesting changes that wouldn't have any actual effect. This changeset also adds a trailing "." to certain record types which are required to be submitted with a trailing dot, but which digitalocean does not return with a trailing dot, again preventing changes that wouldn't have an effect. Tests have been added for the above, and with just adding the tests, the current code is failing, as it is handling some records(e.g. MX) incorrectly
212 lines
4.8 KiB
Go
212 lines
4.8 KiB
Go
package digitalocean
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/digitalocean/godo"
|
|
"github.com/hashicorp/terraform/helper/schema"
|
|
)
|
|
|
|
func resourceDigitalOceanRecord() *schema.Resource {
|
|
return &schema.Resource{
|
|
Create: resourceDigitalOceanRecordCreate,
|
|
Read: resourceDigitalOceanRecordRead,
|
|
Update: resourceDigitalOceanRecordUpdate,
|
|
Delete: resourceDigitalOceanRecordDelete,
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
"type": {
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
ForceNew: true,
|
|
},
|
|
|
|
"domain": {
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
ForceNew: true,
|
|
},
|
|
|
|
"name": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
},
|
|
|
|
"port": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
|
|
"priority": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
|
|
"weight": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
|
|
"value": {
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
|
|
"fqdn": {
|
|
Type: schema.TypeString,
|
|
Computed: true,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func resourceDigitalOceanRecordCreate(d *schema.ResourceData, meta interface{}) error {
|
|
client := meta.(*godo.Client)
|
|
|
|
newRecord := godo.DomainRecordEditRequest{
|
|
Type: d.Get("type").(string),
|
|
Name: d.Get("name").(string),
|
|
Data: d.Get("value").(string),
|
|
}
|
|
|
|
var err error
|
|
if priority := d.Get("priority").(string); priority != "" {
|
|
newRecord.Priority, err = strconv.Atoi(priority)
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to parse priority as an integer: %v", err)
|
|
}
|
|
}
|
|
if port := d.Get("port").(string); port != "" {
|
|
newRecord.Port, err = strconv.Atoi(port)
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to parse port as an integer: %v", err)
|
|
}
|
|
}
|
|
if weight := d.Get("weight").(string); weight != "" {
|
|
newRecord.Weight, err = strconv.Atoi(weight)
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to parse weight as an integer: %v", err)
|
|
}
|
|
}
|
|
|
|
log.Printf("[DEBUG] record create configuration: %#v", newRecord)
|
|
rec, _, err := client.Domains.CreateRecord(d.Get("domain").(string), &newRecord)
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to create record: %s", err)
|
|
}
|
|
|
|
d.SetId(strconv.Itoa(rec.ID))
|
|
log.Printf("[INFO] Record ID: %s", d.Id())
|
|
|
|
return resourceDigitalOceanRecordRead(d, meta)
|
|
}
|
|
|
|
func resourceDigitalOceanRecordRead(d *schema.ResourceData, meta interface{}) error {
|
|
client := meta.(*godo.Client)
|
|
domain := d.Get("domain").(string)
|
|
id, err := strconv.Atoi(d.Id())
|
|
if err != nil {
|
|
return fmt.Errorf("invalid record ID: %v", err)
|
|
}
|
|
|
|
rec, resp, err := client.Domains.Record(domain, id)
|
|
if err != nil {
|
|
// If the record is somehow already destroyed, mark as
|
|
// successfully gone
|
|
if resp.StatusCode == 404 {
|
|
d.SetId("")
|
|
return nil
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
if t := rec.Type; t == "CNAME" || t == "MX" || t == "NS" || t == "SRV" {
|
|
if rec.Data == "@" {
|
|
rec.Data = domain
|
|
}
|
|
rec.Data += "."
|
|
}
|
|
|
|
d.Set("name", rec.Name)
|
|
d.Set("type", rec.Type)
|
|
d.Set("value", rec.Data)
|
|
d.Set("weight", strconv.Itoa(rec.Weight))
|
|
d.Set("priority", strconv.Itoa(rec.Priority))
|
|
d.Set("port", strconv.Itoa(rec.Port))
|
|
|
|
en := constructFqdn(rec.Name, d.Get("domain").(string))
|
|
log.Printf("[DEBUG] Constructed FQDN: %s", en)
|
|
d.Set("fqdn", en)
|
|
|
|
return nil
|
|
}
|
|
|
|
func resourceDigitalOceanRecordUpdate(d *schema.ResourceData, meta interface{}) error {
|
|
client := meta.(*godo.Client)
|
|
|
|
domain := d.Get("domain").(string)
|
|
id, err := strconv.Atoi(d.Id())
|
|
if err != nil {
|
|
return fmt.Errorf("invalid record ID: %v", err)
|
|
}
|
|
|
|
var editRecord godo.DomainRecordEditRequest
|
|
if v, ok := d.GetOk("name"); ok {
|
|
editRecord.Name = v.(string)
|
|
}
|
|
|
|
log.Printf("[DEBUG] record update configuration: %#v", editRecord)
|
|
_, _, err = client.Domains.EditRecord(domain, id, &editRecord)
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to update record: %s", err)
|
|
}
|
|
|
|
return resourceDigitalOceanRecordRead(d, meta)
|
|
}
|
|
|
|
func resourceDigitalOceanRecordDelete(d *schema.ResourceData, meta interface{}) error {
|
|
client := meta.(*godo.Client)
|
|
|
|
domain := d.Get("domain").(string)
|
|
id, err := strconv.Atoi(d.Id())
|
|
if err != nil {
|
|
return fmt.Errorf("invalid record ID: %v", err)
|
|
}
|
|
|
|
log.Printf("[INFO] Deleting record: %s, %d", domain, id)
|
|
|
|
resp, delErr := client.Domains.DeleteRecord(domain, id)
|
|
if delErr != nil {
|
|
// If the record is somehow already destroyed, mark as
|
|
// successfully gone
|
|
if resp.StatusCode == 404 {
|
|
return nil
|
|
}
|
|
|
|
return fmt.Errorf("Error deleting record: %s", delErr)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func constructFqdn(name, domain string) string {
|
|
rn := strings.ToLower(strings.TrimSuffix(name, "."))
|
|
domain = strings.TrimSuffix(domain, ".")
|
|
if !strings.HasSuffix(rn, domain) {
|
|
rn = strings.Join([]string{name, domain}, ".")
|
|
}
|
|
return rn
|
|
}
|