github.com/scottwinkler/terraform@v0.11.6-0.20180329211809-05143987aea8/helper/validation/validation.go (about)

     1  package validation
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"reflect"
     7  	"regexp"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/hashicorp/terraform/helper/schema"
    12  	"github.com/hashicorp/terraform/helper/structure"
    13  )
    14  
    15  // IntBetween returns a SchemaValidateFunc which tests if the provided value
    16  // is of type int and is between min and max (inclusive)
    17  func IntBetween(min, max int) schema.SchemaValidateFunc {
    18  	return func(i interface{}, k string) (s []string, es []error) {
    19  		v, ok := i.(int)
    20  		if !ok {
    21  			es = append(es, fmt.Errorf("expected type of %s to be int", k))
    22  			return
    23  		}
    24  
    25  		if v < min || v > max {
    26  			es = append(es, fmt.Errorf("expected %s to be in the range (%d - %d), got %d", k, min, max, v))
    27  			return
    28  		}
    29  
    30  		return
    31  	}
    32  }
    33  
    34  // IntAtLeast returns a SchemaValidateFunc which tests if the provided value
    35  // is of type int and is at least min (inclusive)
    36  func IntAtLeast(min int) schema.SchemaValidateFunc {
    37  	return func(i interface{}, k string) (s []string, es []error) {
    38  		v, ok := i.(int)
    39  		if !ok {
    40  			es = append(es, fmt.Errorf("expected type of %s to be int", k))
    41  			return
    42  		}
    43  
    44  		if v < min {
    45  			es = append(es, fmt.Errorf("expected %s to be at least (%d), got %d", k, min, v))
    46  			return
    47  		}
    48  
    49  		return
    50  	}
    51  }
    52  
    53  // IntAtMost returns a SchemaValidateFunc which tests if the provided value
    54  // is of type int and is at most max (inclusive)
    55  func IntAtMost(max int) schema.SchemaValidateFunc {
    56  	return func(i interface{}, k string) (s []string, es []error) {
    57  		v, ok := i.(int)
    58  		if !ok {
    59  			es = append(es, fmt.Errorf("expected type of %s to be int", k))
    60  			return
    61  		}
    62  
    63  		if v > max {
    64  			es = append(es, fmt.Errorf("expected %s to be at most (%d), got %d", k, max, v))
    65  			return
    66  		}
    67  
    68  		return
    69  	}
    70  }
    71  
    72  // StringInSlice returns a SchemaValidateFunc which tests if the provided value
    73  // is of type string and matches the value of an element in the valid slice
    74  // will test with in lower case if ignoreCase is true
    75  func StringInSlice(valid []string, ignoreCase bool) schema.SchemaValidateFunc {
    76  	return func(i interface{}, k string) (s []string, es []error) {
    77  		v, ok := i.(string)
    78  		if !ok {
    79  			es = append(es, fmt.Errorf("expected type of %s to be string", k))
    80  			return
    81  		}
    82  
    83  		for _, str := range valid {
    84  			if v == str || (ignoreCase && strings.ToLower(v) == strings.ToLower(str)) {
    85  				return
    86  			}
    87  		}
    88  
    89  		es = append(es, fmt.Errorf("expected %s to be one of %v, got %s", k, valid, v))
    90  		return
    91  	}
    92  }
    93  
    94  // StringLenBetween returns a SchemaValidateFunc which tests if the provided value
    95  // is of type string and has length between min and max (inclusive)
    96  func StringLenBetween(min, max int) schema.SchemaValidateFunc {
    97  	return func(i interface{}, k string) (s []string, es []error) {
    98  		v, ok := i.(string)
    99  		if !ok {
   100  			es = append(es, fmt.Errorf("expected type of %s to be string", k))
   101  			return
   102  		}
   103  		if len(v) < min || len(v) > max {
   104  			es = append(es, fmt.Errorf("expected length of %s to be in the range (%d - %d), got %s", k, min, max, v))
   105  		}
   106  		return
   107  	}
   108  }
   109  
   110  // StringMatch returns a SchemaValidateFunc which tests if the provided value
   111  // matches a given regexp. Optionally an error message can be provided to
   112  // return something friendlier than "must match some globby regexp".
   113  func StringMatch(r *regexp.Regexp, message string) schema.SchemaValidateFunc {
   114  	return func(i interface{}, k string) ([]string, []error) {
   115  		v, ok := i.(string)
   116  		if !ok {
   117  			return nil, []error{fmt.Errorf("expected type of %s to be string", k)}
   118  		}
   119  
   120  		if ok := r.MatchString(v); !ok {
   121  			if message != "" {
   122  				return nil, []error{fmt.Errorf("invalid value for %s (%s)", k, message)}
   123  
   124  			}
   125  			return nil, []error{fmt.Errorf("expected value of %s to match regular expression %q", k, r)}
   126  		}
   127  		return nil, nil
   128  	}
   129  }
   130  
   131  // NoZeroValues is a SchemaValidateFunc which tests if the provided value is
   132  // not a zero value. It's useful in situations where you want to catch
   133  // explicit zero values on things like required fields during validation.
   134  func NoZeroValues(i interface{}, k string) (s []string, es []error) {
   135  	if reflect.ValueOf(i).Interface() == reflect.Zero(reflect.TypeOf(i)).Interface() {
   136  		switch reflect.TypeOf(i).Kind() {
   137  		case reflect.String:
   138  			es = append(es, fmt.Errorf("%s must not be empty", k))
   139  		case reflect.Int, reflect.Float64:
   140  			es = append(es, fmt.Errorf("%s must not be zero", k))
   141  		default:
   142  			// this validator should only ever be applied to TypeString, TypeInt and TypeFloat
   143  			panic(fmt.Errorf("can't use NoZeroValues with %T attribute %s", i, k))
   144  		}
   145  	}
   146  	return
   147  }
   148  
   149  // CIDRNetwork returns a SchemaValidateFunc which tests if the provided value
   150  // is of type string, is in valid CIDR network notation, and has significant bits between min and max (inclusive)
   151  func CIDRNetwork(min, max int) schema.SchemaValidateFunc {
   152  	return func(i interface{}, k string) (s []string, es []error) {
   153  		v, ok := i.(string)
   154  		if !ok {
   155  			es = append(es, fmt.Errorf("expected type of %s to be string", k))
   156  			return
   157  		}
   158  
   159  		_, ipnet, err := net.ParseCIDR(v)
   160  		if err != nil {
   161  			es = append(es, fmt.Errorf(
   162  				"expected %s to contain a valid CIDR, got: %s with err: %s", k, v, err))
   163  			return
   164  		}
   165  
   166  		if ipnet == nil || v != ipnet.String() {
   167  			es = append(es, fmt.Errorf(
   168  				"expected %s to contain a valid network CIDR, expected %s, got %s",
   169  				k, ipnet, v))
   170  		}
   171  
   172  		sigbits, _ := ipnet.Mask.Size()
   173  		if sigbits < min || sigbits > max {
   174  			es = append(es, fmt.Errorf(
   175  				"expected %q to contain a network CIDR with between %d and %d significant bits, got: %d",
   176  				k, min, max, sigbits))
   177  		}
   178  
   179  		return
   180  	}
   181  }
   182  
   183  // ValidateJsonString is a SchemaValidateFunc which tests to make sure the
   184  // supplied string is valid JSON.
   185  func ValidateJsonString(v interface{}, k string) (ws []string, errors []error) {
   186  	if _, err := structure.NormalizeJsonString(v); err != nil {
   187  		errors = append(errors, fmt.Errorf("%q contains an invalid JSON: %s", k, err))
   188  	}
   189  	return
   190  }
   191  
   192  // ValidateListUniqueStrings is a ValidateFunc that ensures a list has no
   193  // duplicate items in it. It's useful for when a list is needed over a set
   194  // because order matters, yet the items still need to be unique.
   195  func ValidateListUniqueStrings(v interface{}, k string) (ws []string, errors []error) {
   196  	for n1, v1 := range v.([]interface{}) {
   197  		for n2, v2 := range v.([]interface{}) {
   198  			if v1.(string) == v2.(string) && n1 != n2 {
   199  				errors = append(errors, fmt.Errorf("%q: duplicate entry - %s", k, v1.(string)))
   200  			}
   201  		}
   202  	}
   203  	return
   204  }
   205  
   206  // ValidateRegexp returns a SchemaValidateFunc which tests to make sure the
   207  // supplied string is a valid regular expression.
   208  func ValidateRegexp(v interface{}, k string) (ws []string, errors []error) {
   209  	if _, err := regexp.Compile(v.(string)); err != nil {
   210  		errors = append(errors, fmt.Errorf("%q: %s", k, err))
   211  	}
   212  	return
   213  }
   214  
   215  // ValidateRFC3339TimeString is a ValidateFunc that ensures a string parses
   216  // as time.RFC3339 format
   217  func ValidateRFC3339TimeString(v interface{}, k string) (ws []string, errors []error) {
   218  	if _, err := time.Parse(time.RFC3339, v.(string)); err != nil {
   219  		errors = append(errors, fmt.Errorf("%q: invalid RFC3339 timestamp", k))
   220  	}
   221  	return
   222  }