github.com/sylr/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 }