diff --git a/helper/validation/validation.go b/helper/validation/validation.go index 1fc3a6c09..e9edcd333 100644 --- a/helper/validation/validation.go +++ b/helper/validation/validation.go @@ -1,6 +1,7 @@ package validation import ( + "bytes" "fmt" "net" "reflect" @@ -180,6 +181,51 @@ func CIDRNetwork(min, max int) schema.SchemaValidateFunc { } } +// SingleIP returns a SchemaValidateFunc which tests if the provided value +// is of type string, and in valid single IP notation +func SingleIP() schema.SchemaValidateFunc { + return func(i interface{}, k string) (s []string, es []error) { + v, ok := i.(string) + if !ok { + es = append(es, fmt.Errorf("expected type of %s to be string", k)) + return + } + + ip := net.ParseIP(v) + if ip == nil { + es = append(es, fmt.Errorf( + "expected %s to contain a valid IP, got: %s", k, v)) + } + return + } +} + +// IPRange returns a SchemaValidateFunc which tests if the provided value +// is of type string, and in valid IP range notation +func IPRange() schema.SchemaValidateFunc { + return func(i interface{}, k string) (s []string, es []error) { + v, ok := i.(string) + if !ok { + es = append(es, fmt.Errorf("expected type of %s to be string", k)) + return + } + + ips := strings.Split(v, "-") + if len(ips) != 2 { + es = append(es, fmt.Errorf( + "expected %s to contain a valid IP range, got: %s", k, v)) + return + } + ip1 := net.ParseIP(ips[0]) + ip2 := net.ParseIP(ips[1]) + if ip1 == nil || ip2 == nil || bytes.Compare(ip1, ip2) > 0 { + es = append(es, fmt.Errorf( + "expected %s to contain a valid IP range, got: %s", k, v)) + } + return + } +} + // ValidateJsonString is a SchemaValidateFunc which tests to make sure the // supplied string is valid JSON. func ValidateJsonString(v interface{}, k string) (ws []string, errors []error) { diff --git a/helper/validation/validation_test.go b/helper/validation/validation_test.go index 0e77006d4..ea3b703bc 100644 --- a/helper/validation/validation_test.go +++ b/helper/validation/validation_test.go @@ -144,6 +144,49 @@ func TestValidationRegexp(t *testing.T) { }) } +func TestValidationSingleIP(t *testing.T) { + runTestCases(t, []testCase{ + { + val: "172.10.10.10", + f: SingleIP(), + }, + { + val: "1.1.1", + f: SingleIP(), + expectedErr: regexp.MustCompile(regexp.QuoteMeta("expected test_property to contain a valid IP, got:")), + }, + { + val: "1.1.1.0/20", + f: SingleIP(), + expectedErr: regexp.MustCompile(regexp.QuoteMeta("expected test_property to contain a valid IP, got:")), + }, + { + val: "256.1.1.1", + f: SingleIP(), + expectedErr: regexp.MustCompile(regexp.QuoteMeta("expected test_property to contain a valid IP, got:")), + }, + }) +} + +func TestValidationIPRange(t *testing.T) { + runTestCases(t, []testCase{ + { + val: "172.10.10.10-172.10.10.12", + f: IPRange(), + }, + { + val: "172.10.10.20", + f: IPRange(), + expectedErr: regexp.MustCompile(regexp.QuoteMeta("expected test_property to contain a valid IP range, got:")), + }, + { + val: "172.10.10.20-172.10.10.12", + f: IPRange(), + expectedErr: regexp.MustCompile(regexp.QuoteMeta("expected test_property to contain a valid IP range, got:")), + }, + }) +} + func TestValidateRFC3339TimeString(t *testing.T) { runTestCases(t, []testCase{ {