github.com/hooklift/terraform@v0.11.0-beta1.0.20171117000744-6786c1361ffe/helper/validation/validation.go (about)

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