provider/cloudflare: Add validation for record types and record content
Adds a validation function for cloudflare record types. Also adds an apply-time validation for the record's content based on record type. Currently only validating `A` and `AAAA` records, can be expanded to verify record content for every possible record type in the future. ``` $ make test TEST=./builtin/providers/cloudflare ==> Checking that code complies with gofmt requirements... ==> Checking AWS provider for unchecked errors... ==> NOTE: at this time we only look for uncheck errors in the AWS package ==> Installing errcheck... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/01/13 14:41:37 Generated command/internal_plugin_list.go TF_ACC= go test ./builtin/providers/cloudflare -timeout=30s -parallel=4 ok github.com/hashicorp/terraform/builtin/providers/cloudflare 0.018s ``` Fixes: #11173
This commit is contained in:
parent
d1b34d322c
commit
8ac7f53c33
|
@ -34,9 +34,10 @@ func resourceCloudFlareRecord() *schema.Resource {
|
||||||
},
|
},
|
||||||
|
|
||||||
"type": &schema.Schema{
|
"type": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
|
ValidateFunc: validateRecordType,
|
||||||
},
|
},
|
||||||
|
|
||||||
"value": &schema.Schema{
|
"value": &schema.Schema{
|
||||||
|
@ -88,6 +89,11 @@ func resourceCloudFlareRecordCreate(d *schema.ResourceData, meta interface{}) er
|
||||||
newRecord.TTL = ttl.(int)
|
newRecord.TTL = ttl.(int)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate value based on type
|
||||||
|
if err := validateRecordName(newRecord.Type, newRecord.Content); err != nil {
|
||||||
|
return fmt.Errorf("Error validating record name %q: %s", newRecord.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
zoneId, err := client.ZoneIDByName(newRecord.ZoneName)
|
zoneId, err := client.ZoneIDByName(newRecord.ZoneName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error finding zone %q: %s", newRecord.ZoneName, err)
|
return fmt.Errorf("Error finding zone %q: %s", newRecord.ZoneName, err)
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// validateRecordType ensures that the cloudflare record type is valid
|
||||||
|
func validateRecordType(v interface{}, k string) (ws []string, errors []error) {
|
||||||
|
value := v.(string)
|
||||||
|
|
||||||
|
validTypes := map[string]struct{}{
|
||||||
|
"A": {},
|
||||||
|
"AAAA": {},
|
||||||
|
"CNAME": {},
|
||||||
|
"TXT": {},
|
||||||
|
"SRV": {},
|
||||||
|
"LOC": {},
|
||||||
|
"MX": {},
|
||||||
|
"NS": {},
|
||||||
|
"SPF": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := validTypes[value]; !ok {
|
||||||
|
errors = append(errors, fmt.Errorf(
|
||||||
|
"%q contains an invalid type %q. Valid types are: %q, %q, %q, %q, %q, %q, %q, %q, or %q",
|
||||||
|
k, value, "A", "AAAA", "CNAME", "TXT", "SRV", "LOC", "MX", "NS", "SPF"))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateRecordName ensures that based on supplied record type, the name content matches
|
||||||
|
// Currently only validates A and AAAA types
|
||||||
|
func validateRecordName(t string, value string) error {
|
||||||
|
switch t {
|
||||||
|
case "A":
|
||||||
|
// Must be ipv4 addr
|
||||||
|
addr := net.ParseIP(value)
|
||||||
|
if addr == nil || !strings.Contains(value, ".") {
|
||||||
|
return fmt.Errorf("A record must be a valid IPv4 address, got: %q", value)
|
||||||
|
}
|
||||||
|
case "AAAA":
|
||||||
|
// Must be ipv6 addr
|
||||||
|
addr := net.ParseIP(value)
|
||||||
|
if addr == nil || !strings.Contains(value, ":") {
|
||||||
|
return fmt.Errorf("AAAA record must be a valid IPv6 address, got: %q", value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestValidateRecordType(t *testing.T) {
|
||||||
|
validTypes := []string{
|
||||||
|
"A",
|
||||||
|
"AAAA",
|
||||||
|
"CNAME",
|
||||||
|
"TXT",
|
||||||
|
"SRV",
|
||||||
|
"LOC",
|
||||||
|
"MX",
|
||||||
|
"NS",
|
||||||
|
"SPF",
|
||||||
|
}
|
||||||
|
for _, v := range validTypes {
|
||||||
|
_, errors := validateRecordType(v, "type")
|
||||||
|
if len(errors) != 0 {
|
||||||
|
t.Fatalf("%q should be a valid record type: %q", v, errors)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidTypes := []string{
|
||||||
|
"a",
|
||||||
|
"cName",
|
||||||
|
"txt",
|
||||||
|
"SRv",
|
||||||
|
"foo",
|
||||||
|
"bar",
|
||||||
|
}
|
||||||
|
for _, v := range invalidTypes {
|
||||||
|
_, errors := validateRecordType(v, "type")
|
||||||
|
if len(errors) == 0 {
|
||||||
|
t.Fatalf("%q should be an invalid record type", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateRecordName(t *testing.T) {
|
||||||
|
validNames := map[string]string{
|
||||||
|
"A": "192.168.0.1",
|
||||||
|
"AAAA": "2001:0db8:0000:0042:0000:8a2e:0370:7334",
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range validNames {
|
||||||
|
if err := validateRecordName(k, v); err != nil {
|
||||||
|
t.Fatalf("%q should be a valid name for type %q: %v", v, k, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidNames := map[string]string{
|
||||||
|
"A": "terraform.io",
|
||||||
|
"AAAA": "192.168.0.1",
|
||||||
|
}
|
||||||
|
for k, v := range invalidNames {
|
||||||
|
if err := validateRecordName(k, v); err == nil {
|
||||||
|
t.Fatalf("%q should be an invalid name for type %q", v, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue