github.com/magodo/terraform@v0.11.12-beta1/helper/validation/validation.go (about)

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