github.com/tompao/terraform@v0.6.10-0.20180215233341-e41b29d0961b/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 // StringMatch returns a SchemaValidateFunc which tests if the provided value 110 // matches a given regexp. Optionally an error message can be provided to 111 // return something friendlier than "must match some globby regexp". 112 func StringMatch(r *regexp.Regexp, message string) schema.SchemaValidateFunc { 113 return func(i interface{}, k string) ([]string, []error) { 114 v, ok := i.(string) 115 if !ok { 116 return nil, []error{fmt.Errorf("expected type of %s to be string", k)} 117 } 118 119 if ok := r.MatchString(v); !ok { 120 if message != "" { 121 return nil, []error{fmt.Errorf("invalid value for %s (%s)", k, message)} 122 123 } 124 return nil, []error{fmt.Errorf("expected value of %s to match regular expression %q", k, r)} 125 } 126 return nil, nil 127 } 128 } 129 130 // NoZeroValues is a SchemaValidateFunc which tests if the provided value is 131 // not a zero value. It's useful in situations where you want to catch 132 // explicit zero values on things like required fields during validation. 133 func NoZeroValues(i interface{}, k string) (s []string, es []error) { 134 if reflect.ValueOf(i).Interface() == reflect.Zero(reflect.TypeOf(i)).Interface() { 135 switch reflect.TypeOf(i).Kind() { 136 case reflect.String: 137 es = append(es, fmt.Errorf("%s must not be empty", k)) 138 case reflect.Int, reflect.Float64: 139 es = append(es, fmt.Errorf("%s must not be zero", k)) 140 default: 141 // this validator should only ever be applied to TypeString, TypeInt and TypeFloat 142 panic(fmt.Errorf("can't use NoZeroValues with %T attribute %s", i, k)) 143 } 144 } 145 return 146 } 147 148 // CIDRNetwork returns a SchemaValidateFunc which tests if the provided value 149 // is of type string, is in valid CIDR network notation, and has significant bits between min and max (inclusive) 150 func CIDRNetwork(min, max int) schema.SchemaValidateFunc { 151 return func(i interface{}, k string) (s []string, es []error) { 152 v, ok := i.(string) 153 if !ok { 154 es = append(es, fmt.Errorf("expected type of %s to be string", k)) 155 return 156 } 157 158 _, ipnet, err := net.ParseCIDR(v) 159 if err != nil { 160 es = append(es, fmt.Errorf( 161 "expected %s to contain a valid CIDR, got: %s with err: %s", k, v, err)) 162 return 163 } 164 165 if ipnet == nil || v != ipnet.String() { 166 es = append(es, fmt.Errorf( 167 "expected %s to contain a valid network CIDR, expected %s, got %s", 168 k, ipnet, v)) 169 } 170 171 sigbits, _ := ipnet.Mask.Size() 172 if sigbits < min || sigbits > max { 173 es = append(es, fmt.Errorf( 174 "expected %q to contain a network CIDR with between %d and %d significant bits, got: %d", 175 k, min, max, sigbits)) 176 } 177 178 return 179 } 180 } 181 182 // ValidateJsonString is a SchemaValidateFunc which tests to make sure the 183 // supplied string is valid JSON. 184 func ValidateJsonString(v interface{}, k string) (ws []string, errors []error) { 185 if _, err := structure.NormalizeJsonString(v); err != nil { 186 errors = append(errors, fmt.Errorf("%q contains an invalid JSON: %s", k, err)) 187 } 188 return 189 } 190 191 // ValidateListUniqueStrings is a ValidateFunc that ensures a list has no 192 // duplicate items in it. It's useful for when a list is needed over a set 193 // because order matters, yet the items still need to be unique. 194 func ValidateListUniqueStrings(v interface{}, k string) (ws []string, errors []error) { 195 for n1, v1 := range v.([]interface{}) { 196 for n2, v2 := range v.([]interface{}) { 197 if v1.(string) == v2.(string) && n1 != n2 { 198 errors = append(errors, fmt.Errorf("%q: duplicate entry - %s", k, v1.(string))) 199 } 200 } 201 } 202 return 203 } 204 205 // ValidateRegexp returns a SchemaValidateFunc which tests to make sure the 206 // supplied string is a valid regular expression. 207 func ValidateRegexp(v interface{}, k string) (ws []string, errors []error) { 208 if _, err := regexp.Compile(v.(string)); err != nil { 209 errors = append(errors, fmt.Errorf("%q: %s", k, err)) 210 } 211 return 212 }