This repository has been archived by the owner on Mar 8, 2022. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 149
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #410 from alexkappa/pr/squarebracket/228-1
Support for verifying custom domains
- Loading branch information
Showing
11 changed files
with
555 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package digitalocean | ||
|
||
import ( | ||
"github.com/digitalocean/godo" | ||
"github.com/hashicorp/terraform-plugin-sdk/helper/schema" | ||
) | ||
|
||
// Provider returns a schema.Provider for a minimal version of the DigitalOcean | ||
// provider used for testing. | ||
func Provider() *schema.Provider { | ||
return &schema.Provider{ | ||
Schema: map[string]*schema.Schema{ | ||
"token": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
DefaultFunc: schema.MultiEnvDefaultFunc([]string{ | ||
"DIGITALOCEAN_TOKEN", | ||
"DIGITALOCEAN_ACCESS_TOKEN", | ||
}, nil), | ||
Description: "The token key for API operations.", | ||
}, | ||
}, | ||
ResourcesMap: map[string]*schema.Resource{ | ||
"digitalocean_record": resourceDigitalOceanRecord(), | ||
}, | ||
ConfigureFunc: func(d *schema.ResourceData) (interface{}, error) { | ||
return godo.NewFromToken(d.Get("token").(string)), nil | ||
}, | ||
} | ||
} |
323 changes: 323 additions & 0 deletions
323
auth0/internal/digitalocean/resource_digitalocean_record.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,323 @@ | ||
package digitalocean | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"log" | ||
"strconv" | ||
"strings" | ||
|
||
"github.com/digitalocean/godo" | ||
"github.com/hashicorp/terraform-plugin-sdk/helper/schema" | ||
"github.com/hashicorp/terraform-plugin-sdk/helper/validation" | ||
) | ||
|
||
func resourceDigitalOceanRecord() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceDigitalOceanRecordCreate, | ||
Read: resourceDigitalOceanRecordRead, | ||
Update: resourceDigitalOceanRecordUpdate, | ||
Delete: resourceDigitalOceanRecordDelete, | ||
Importer: &schema.ResourceImporter{ | ||
State: resourceDigitalOceanRecordImport, | ||
}, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"type": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validation.StringInSlice([]string{ | ||
"A", | ||
"AAAA", | ||
"CAA", | ||
"CNAME", | ||
"MX", | ||
"NS", | ||
"TXT", | ||
"SRV", | ||
}, false), | ||
}, | ||
|
||
"domain": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validation.NoZeroValues, | ||
}, | ||
|
||
"name": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ValidateFunc: validation.NoZeroValues, | ||
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { | ||
domain := d.Get("domain").(string) + "." | ||
|
||
return old+"."+domain == new | ||
}, | ||
}, | ||
|
||
"port": { | ||
Type: schema.TypeInt, | ||
Optional: true, | ||
ValidateFunc: validation.IntBetween(0, 65535), | ||
}, | ||
|
||
"priority": { | ||
Type: schema.TypeInt, | ||
Optional: true, | ||
ValidateFunc: validation.IntBetween(0, 65535), | ||
}, | ||
|
||
"weight": { | ||
Type: schema.TypeInt, | ||
Optional: true, | ||
ValidateFunc: validation.IntBetween(0, 65535), | ||
}, | ||
|
||
"ttl": { | ||
Type: schema.TypeInt, | ||
Optional: true, | ||
Computed: true, | ||
ValidateFunc: validation.IntAtLeast(1), | ||
}, | ||
|
||
"value": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { | ||
domain := d.Get("domain").(string) + "." | ||
|
||
return (old == "@" && new == domain) || (old == new+"."+domain) | ||
}, | ||
}, | ||
|
||
"fqdn": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
|
||
"flags": { | ||
Type: schema.TypeInt, | ||
Optional: true, | ||
ValidateFunc: validation.IntBetween(0, 255), | ||
}, | ||
|
||
"tag": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ValidateFunc: validation.StringInSlice([]string{ | ||
"issue", | ||
"issuewild", | ||
"iodef", | ||
}, false), | ||
}, | ||
}, | ||
|
||
CustomizeDiff: func(diff *schema.ResourceDiff, v interface{}) error { | ||
recordType := diff.Get("type").(string) | ||
|
||
_, hasPriority := diff.GetOkExists("priority") | ||
if recordType == "MX" { | ||
if !hasPriority { | ||
return fmt.Errorf("`priority` is required for when type is `MX`") | ||
} | ||
} | ||
|
||
_, hasPort := diff.GetOkExists("port") | ||
_, hasWeight := diff.GetOkExists("weight") | ||
if recordType == "SRV" { | ||
if !hasPriority { | ||
return fmt.Errorf("`priority` is required for when type is `SRV`") | ||
} | ||
if !hasPort { | ||
return fmt.Errorf("`port` is required for when type is `SRV`") | ||
} | ||
if !hasWeight { | ||
return fmt.Errorf("`weight` is required for when type is `SRV`") | ||
} | ||
} | ||
|
||
_, hasFlags := diff.GetOkExists("flags") | ||
_, hasTag := diff.GetOk("tag") | ||
if recordType == "CAA" { | ||
if !hasFlags { | ||
return fmt.Errorf("`flags` is required for when type is `CAA`") | ||
} | ||
if !hasTag { | ||
return fmt.Errorf("`tag` is required for when type is `CAA`") | ||
} | ||
} | ||
|
||
return nil | ||
}, | ||
} | ||
} | ||
|
||
func resourceDigitalOceanRecordCreate(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*godo.Client) | ||
|
||
newRecord, err := expandDigitalOceanRecordResource(d) | ||
if err != nil { | ||
return fmt.Errorf("Error in constructing record request: %s", err) | ||
} | ||
|
||
newRecord.Type = d.Get("type").(string) | ||
|
||
log.Printf("[DEBUG] record create configuration: %#v", newRecord) | ||
rec, _, err := client.Domains.CreateRecord(context.Background(), 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(context.Background(), domain, id) | ||
if err != nil { | ||
// If the record is somehow already destroyed, mark as | ||
// successfully gone | ||
if resp != nil && resp.StatusCode == 404 { | ||
d.SetId("") | ||
return nil | ||
} | ||
|
||
return err | ||
} | ||
|
||
if t := rec.Type; t == "CNAME" || t == "MX" || t == "NS" || t == "SRV" || t == "CAA" { | ||
if rec.Data != "@" && rec.Tag != "iodef" { | ||
rec.Data += "." | ||
} | ||
} | ||
|
||
d.Set("name", rec.Name) | ||
d.Set("type", rec.Type) | ||
d.Set("value", rec.Data) | ||
d.Set("port", rec.Port) | ||
d.Set("priority", rec.Priority) | ||
d.Set("ttl", rec.TTL) | ||
d.Set("weight", rec.Weight) | ||
d.Set("flags", rec.Flags) | ||
d.Set("tag", rec.Tag) | ||
|
||
en := constructFqdn(rec.Name, d.Get("domain").(string)) | ||
log.Printf("[DEBUG] Constructed FQDN: %s", en) | ||
d.Set("fqdn", en) | ||
|
||
return nil | ||
} | ||
|
||
func resourceDigitalOceanRecordImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { | ||
if strings.Contains(d.Id(), ",") { | ||
s := strings.Split(d.Id(), ",") | ||
// Validate that this is an ID by making sure it can be converted into an int | ||
_, err := strconv.Atoi(s[1]) | ||
if err != nil { | ||
return nil, fmt.Errorf("invalid record ID: %v", err) | ||
} | ||
|
||
d.SetId(s[1]) | ||
d.Set("domain", s[0]) | ||
} | ||
|
||
return []*schema.ResourceData{d}, 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) | ||
} | ||
|
||
editRecord, err := expandDigitalOceanRecordResource(d) | ||
if err != nil { | ||
return fmt.Errorf("Error in constructing record request: %s", err) | ||
} | ||
|
||
log.Printf("[DEBUG] record update configuration: %#v", editRecord) | ||
_, _, err = client.Domains.EditRecord(context.Background(), 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(context.Background(), domain, id) | ||
if delErr != nil { | ||
// If the record is somehow already destroyed, mark as | ||
// successfully gone | ||
if resp != nil && resp.StatusCode == 404 { | ||
return nil | ||
} | ||
|
||
return fmt.Errorf("Error deleting record: %s", delErr) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func expandDigitalOceanRecordResource(d *schema.ResourceData) (*godo.DomainRecordEditRequest, error) { | ||
record := &godo.DomainRecordEditRequest{ | ||
Name: d.Get("name").(string), | ||
Data: d.Get("value").(string), | ||
} | ||
|
||
if v, ok := d.GetOkExists("port"); ok { | ||
record.Port = v.(int) | ||
} | ||
if v, ok := d.GetOkExists("priority"); ok { | ||
record.Priority = v.(int) | ||
} | ||
if v, ok := d.GetOk("ttl"); ok { | ||
record.TTL = v.(int) | ||
} | ||
if v, ok := d.GetOkExists("weight"); ok { | ||
record.Weight = v.(int) | ||
} | ||
if v, ok := d.GetOkExists("flags"); ok { | ||
record.Flags = v.(int) | ||
} | ||
if v, ok := d.GetOk("tag"); ok { | ||
record.Tag = v.(string) | ||
} | ||
|
||
return record, nil | ||
} | ||
|
||
func constructFqdn(name, domain string) string { | ||
rn := strings.ToLower(name) | ||
domainSuffix := domain + "." | ||
if strings.HasSuffix(rn, domainSuffix) { | ||
rn = strings.TrimSuffix(rn, ".") | ||
} else { | ||
rn = strings.Join([]string{name, domain}, ".") | ||
} | ||
return rn | ||
} |
Oops, something went wrong.