github.com/minamijoyo/terraform@v0.7.8-0.20161029001309-18b3736ba44b/builtin/providers/aws/validators.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "net" 6 "regexp" 7 "time" 8 9 "github.com/aws/aws-sdk-go/service/s3" 10 "github.com/hashicorp/terraform/helper/schema" 11 ) 12 13 func validateRdsId(v interface{}, k string) (ws []string, errors []error) { 14 value := v.(string) 15 if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) { 16 errors = append(errors, fmt.Errorf( 17 "only lowercase alphanumeric characters and hyphens allowed in %q", k)) 18 } 19 if !regexp.MustCompile(`^[a-z]`).MatchString(value) { 20 errors = append(errors, fmt.Errorf( 21 "first character of %q must be a letter", k)) 22 } 23 if regexp.MustCompile(`--`).MatchString(value) { 24 errors = append(errors, fmt.Errorf( 25 "%q cannot contain two consecutive hyphens", k)) 26 } 27 if regexp.MustCompile(`-$`).MatchString(value) { 28 errors = append(errors, fmt.Errorf( 29 "%q cannot end with a hyphen", k)) 30 } 31 return 32 } 33 34 func validateElastiCacheClusterId(v interface{}, k string) (ws []string, errors []error) { 35 value := v.(string) 36 if (len(value) < 1) || (len(value) > 20) { 37 errors = append(errors, fmt.Errorf( 38 "%q must contain from 1 to 20 alphanumeric characters or hyphens", k)) 39 } 40 if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) { 41 errors = append(errors, fmt.Errorf( 42 "only lowercase alphanumeric characters and hyphens allowed in %q", k)) 43 } 44 if !regexp.MustCompile(`^[a-z]`).MatchString(value) { 45 errors = append(errors, fmt.Errorf( 46 "first character of %q must be a letter", k)) 47 } 48 if regexp.MustCompile(`--`).MatchString(value) { 49 errors = append(errors, fmt.Errorf( 50 "%q cannot contain two consecutive hyphens", k)) 51 } 52 if regexp.MustCompile(`-$`).MatchString(value) { 53 errors = append(errors, fmt.Errorf( 54 "%q cannot end with a hyphen", k)) 55 } 56 return 57 } 58 59 func validateASGScheduleTimestamp(v interface{}, k string) (ws []string, errors []error) { 60 value := v.(string) 61 _, err := time.Parse(awsAutoscalingScheduleTimeLayout, value) 62 if err != nil { 63 errors = append(errors, fmt.Errorf( 64 "%q cannot be parsed as iso8601 Timestamp Format", value)) 65 } 66 67 return 68 } 69 70 // validateTagFilters confirms the "value" component of a tag filter is one of 71 // AWS's three allowed types. 72 func validateTagFilters(v interface{}, k string) (ws []string, errors []error) { 73 value := v.(string) 74 if value != "KEY_ONLY" && value != "VALUE_ONLY" && value != "KEY_AND_VALUE" { 75 errors = append(errors, fmt.Errorf( 76 "%q must be one of \"KEY_ONLY\", \"VALUE_ONLY\", or \"KEY_AND_VALUE\"", k)) 77 } 78 return 79 } 80 81 func validateDbParamGroupName(v interface{}, k string) (ws []string, errors []error) { 82 value := v.(string) 83 if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) { 84 errors = append(errors, fmt.Errorf( 85 "only lowercase alphanumeric characters and hyphens allowed in %q", k)) 86 } 87 if !regexp.MustCompile(`^[a-z]`).MatchString(value) { 88 errors = append(errors, fmt.Errorf( 89 "first character of %q must be a letter", k)) 90 } 91 if regexp.MustCompile(`--`).MatchString(value) { 92 errors = append(errors, fmt.Errorf( 93 "%q cannot contain two consecutive hyphens", k)) 94 } 95 if regexp.MustCompile(`-$`).MatchString(value) { 96 errors = append(errors, fmt.Errorf( 97 "%q cannot end with a hyphen", k)) 98 } 99 if len(value) > 255 { 100 errors = append(errors, fmt.Errorf( 101 "%q cannot be greater than 255 characters", k)) 102 } 103 return 104 105 } 106 107 func validateStreamViewType(v interface{}, k string) (ws []string, errors []error) { 108 value := v.(string) 109 viewTypes := map[string]bool{ 110 "KEYS_ONLY": true, 111 "NEW_IMAGE": true, 112 "OLD_IMAGE": true, 113 "NEW_AND_OLD_IMAGES": true, 114 } 115 116 if !viewTypes[value] { 117 errors = append(errors, fmt.Errorf("%q be a valid DynamoDB StreamViewType", k)) 118 } 119 return 120 } 121 122 func validateElbName(v interface{}, k string) (ws []string, errors []error) { 123 value := v.(string) 124 if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) { 125 errors = append(errors, fmt.Errorf( 126 "only alphanumeric characters and hyphens allowed in %q: %q", 127 k, value)) 128 } 129 if len(value) > 32 { 130 errors = append(errors, fmt.Errorf( 131 "%q cannot be longer than 32 characters: %q", k, value)) 132 } 133 if regexp.MustCompile(`^-`).MatchString(value) { 134 errors = append(errors, fmt.Errorf( 135 "%q cannot begin with a hyphen: %q", k, value)) 136 } 137 if regexp.MustCompile(`-$`).MatchString(value) { 138 errors = append(errors, fmt.Errorf( 139 "%q cannot end with a hyphen: %q", k, value)) 140 } 141 return 142 143 } 144 145 func validateEcrRepositoryName(v interface{}, k string) (ws []string, errors []error) { 146 value := v.(string) 147 if len(value) < 2 { 148 errors = append(errors, fmt.Errorf( 149 "%q must be at least 2 characters long: %q", k, value)) 150 } 151 if len(value) > 256 { 152 errors = append(errors, fmt.Errorf( 153 "%q cannot be longer than 256 characters: %q", k, value)) 154 } 155 156 // http://docs.aws.amazon.com/AmazonECR/latest/APIReference/API_CreateRepository.html 157 pattern := `^(?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)*[a-z0-9]+(?:[._-][a-z0-9]+)*$` 158 if !regexp.MustCompile(pattern).MatchString(value) { 159 errors = append(errors, fmt.Errorf( 160 "%q doesn't comply with restrictions (%q): %q", 161 k, pattern, value)) 162 } 163 164 return 165 } 166 167 func validateCloudWatchEventRuleName(v interface{}, k string) (ws []string, errors []error) { 168 value := v.(string) 169 if len(value) > 64 { 170 errors = append(errors, fmt.Errorf( 171 "%q cannot be longer than 64 characters: %q", k, value)) 172 } 173 174 // http://docs.aws.amazon.com/AmazonCloudWatchEvents/latest/APIReference/API_PutRule.html 175 pattern := `^[\.\-_A-Za-z0-9]+$` 176 if !regexp.MustCompile(pattern).MatchString(value) { 177 errors = append(errors, fmt.Errorf( 178 "%q doesn't comply with restrictions (%q): %q", 179 k, pattern, value)) 180 } 181 182 return 183 } 184 185 func validateMaxLength(length int) schema.SchemaValidateFunc { 186 return func(v interface{}, k string) (ws []string, errors []error) { 187 value := v.(string) 188 if len(value) > length { 189 errors = append(errors, fmt.Errorf( 190 "%q cannot be longer than %d characters: %q", k, length, value)) 191 } 192 return 193 } 194 } 195 196 func validateIntegerInRange(min, max int) schema.SchemaValidateFunc { 197 return func(v interface{}, k string) (ws []string, errors []error) { 198 value := v.(int) 199 if value < min { 200 errors = append(errors, fmt.Errorf( 201 "%q cannot be lower than %d: %d", k, min, value)) 202 } 203 if value > max { 204 errors = append(errors, fmt.Errorf( 205 "%q cannot be higher than %d: %d", k, max, value)) 206 } 207 return 208 } 209 } 210 211 func validateCloudWatchEventTargetId(v interface{}, k string) (ws []string, errors []error) { 212 value := v.(string) 213 if len(value) > 64 { 214 errors = append(errors, fmt.Errorf( 215 "%q cannot be longer than 64 characters: %q", k, value)) 216 } 217 218 // http://docs.aws.amazon.com/AmazonCloudWatchEvents/latest/APIReference/API_Target.html 219 pattern := `^[\.\-_A-Za-z0-9]+$` 220 if !regexp.MustCompile(pattern).MatchString(value) { 221 errors = append(errors, fmt.Errorf( 222 "%q doesn't comply with restrictions (%q): %q", 223 k, pattern, value)) 224 } 225 226 return 227 } 228 229 func validateLambdaFunctionName(v interface{}, k string) (ws []string, errors []error) { 230 value := v.(string) 231 if len(value) > 140 { 232 errors = append(errors, fmt.Errorf( 233 "%q cannot be longer than 140 characters: %q", k, value)) 234 } 235 // http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html 236 pattern := `^(arn:aws:lambda:)?([a-z]{2}-[a-z]+-\d{1}:)?(\d{12}:)?(function:)?([a-zA-Z0-9-_]+)(:(\$LATEST|[a-zA-Z0-9-_]+))?$` 237 if !regexp.MustCompile(pattern).MatchString(value) { 238 errors = append(errors, fmt.Errorf( 239 "%q doesn't comply with restrictions (%q): %q", 240 k, pattern, value)) 241 } 242 243 return 244 } 245 246 func validateLambdaQualifier(v interface{}, k string) (ws []string, errors []error) { 247 value := v.(string) 248 if len(value) > 128 { 249 errors = append(errors, fmt.Errorf( 250 "%q cannot be longer than 128 characters: %q", k, value)) 251 } 252 // http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html 253 pattern := `^[a-zA-Z0-9$_]+$` 254 if !regexp.MustCompile(pattern).MatchString(value) { 255 errors = append(errors, fmt.Errorf( 256 "%q doesn't comply with restrictions (%q): %q", 257 k, pattern, value)) 258 } 259 260 return 261 } 262 263 func validateLambdaPermissionAction(v interface{}, k string) (ws []string, errors []error) { 264 value := v.(string) 265 266 // http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html 267 pattern := `^(lambda:[*]|lambda:[a-zA-Z]+|[*])$` 268 if !regexp.MustCompile(pattern).MatchString(value) { 269 errors = append(errors, fmt.Errorf( 270 "%q doesn't comply with restrictions (%q): %q", 271 k, pattern, value)) 272 } 273 274 return 275 } 276 277 func validateAwsAccountId(v interface{}, k string) (ws []string, errors []error) { 278 value := v.(string) 279 280 // http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html 281 pattern := `^\d{12}$` 282 if !regexp.MustCompile(pattern).MatchString(value) { 283 errors = append(errors, fmt.Errorf( 284 "%q doesn't look like AWS Account ID (exactly 12 digits): %q", 285 k, value)) 286 } 287 288 return 289 } 290 291 func validateArn(v interface{}, k string) (ws []string, errors []error) { 292 value := v.(string) 293 294 // http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html 295 pattern := `^arn:aws:([a-zA-Z0-9\-])+:([a-z]{2}-[a-z]+-\d{1})?:(\d{12})?:(.*)$` 296 if !regexp.MustCompile(pattern).MatchString(value) { 297 errors = append(errors, fmt.Errorf( 298 "%q doesn't look like a valid ARN (%q): %q", 299 k, pattern, value)) 300 } 301 302 return 303 } 304 305 func validatePolicyStatementId(v interface{}, k string) (ws []string, errors []error) { 306 value := v.(string) 307 308 if len(value) > 100 { 309 errors = append(errors, fmt.Errorf( 310 "%q cannot be longer than 100 characters: %q", k, value)) 311 } 312 313 // http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html 314 pattern := `^[a-zA-Z0-9-_]+$` 315 if !regexp.MustCompile(pattern).MatchString(value) { 316 errors = append(errors, fmt.Errorf( 317 "%q doesn't look like a valid statement ID (%q): %q", 318 k, pattern, value)) 319 } 320 321 return 322 } 323 324 // validateCIDRNetworkAddress ensures that the string value is a valid CIDR that 325 // represents a network address - it adds an error otherwise 326 func validateCIDRNetworkAddress(v interface{}, k string) (ws []string, errors []error) { 327 value := v.(string) 328 _, ipnet, err := net.ParseCIDR(value) 329 if err != nil { 330 errors = append(errors, fmt.Errorf( 331 "%q must contain a valid CIDR, got error parsing: %s", k, err)) 332 return 333 } 334 335 if ipnet == nil || value != ipnet.String() { 336 errors = append(errors, fmt.Errorf( 337 "%q must contain a valid network CIDR, expected %q, got %q", 338 k, ipnet, value)) 339 } 340 341 return 342 } 343 344 func validateHTTPMethod(v interface{}, k string) (ws []string, errors []error) { 345 value := v.(string) 346 347 validMethods := map[string]bool{ 348 "ANY": true, 349 "DELETE": true, 350 "GET": true, 351 "HEAD": true, 352 "OPTIONS": true, 353 "PATCH": true, 354 "POST": true, 355 "PUT": true, 356 } 357 358 if _, ok := validMethods[value]; !ok { 359 errors = append(errors, fmt.Errorf( 360 "%q contains an invalid method %q. Valid methods are either %q, %q, %q, %q, %q, %q, %q, or %q.", 361 k, value, "ANY", "DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT")) 362 } 363 return 364 } 365 366 func validateLogMetricFilterName(v interface{}, k string) (ws []string, errors []error) { 367 value := v.(string) 368 369 if len(value) > 512 { 370 errors = append(errors, fmt.Errorf( 371 "%q cannot be longer than 512 characters: %q", k, value)) 372 } 373 374 // http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutMetricFilter.html 375 pattern := `^[^:*]+$` 376 if !regexp.MustCompile(pattern).MatchString(value) { 377 errors = append(errors, fmt.Errorf( 378 "%q isn't a valid log metric name (must not contain colon nor asterisk): %q", 379 k, value)) 380 } 381 382 return 383 } 384 385 func validateLogMetricFilterTransformationName(v interface{}, k string) (ws []string, errors []error) { 386 value := v.(string) 387 388 if len(value) > 255 { 389 errors = append(errors, fmt.Errorf( 390 "%q cannot be longer than 255 characters: %q", k, value)) 391 } 392 393 // http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_MetricTransformation.html 394 pattern := `^[^:*$]*$` 395 if !regexp.MustCompile(pattern).MatchString(value) { 396 errors = append(errors, fmt.Errorf( 397 "%q isn't a valid log metric transformation name (must not contain"+ 398 " colon, asterisk nor dollar sign): %q", 399 k, value)) 400 } 401 402 return 403 } 404 405 func validateLogGroupName(v interface{}, k string) (ws []string, errors []error) { 406 value := v.(string) 407 408 if len(value) > 512 { 409 errors = append(errors, fmt.Errorf( 410 "%q cannot be longer than 512 characters: %q", k, value)) 411 } 412 413 // http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_CreateLogGroup.html 414 pattern := `^[\.\-_/#A-Za-z0-9]+$` 415 if !regexp.MustCompile(pattern).MatchString(value) { 416 errors = append(errors, fmt.Errorf( 417 "%q isn't a valid log group name (alphanumeric characters, underscores,"+ 418 " hyphens, slashes, hash signs and dots are allowed): %q", 419 k, value)) 420 } 421 422 return 423 } 424 425 func validateS3BucketLifecycleTimestamp(v interface{}, k string) (ws []string, errors []error) { 426 value := v.(string) 427 _, err := time.Parse(time.RFC3339, fmt.Sprintf("%sT00:00:00Z", value)) 428 if err != nil { 429 errors = append(errors, fmt.Errorf( 430 "%q cannot be parsed as RFC3339 Timestamp Format", value)) 431 } 432 433 return 434 } 435 436 func validateS3BucketLifecycleStorageClass(v interface{}, k string) (ws []string, errors []error) { 437 value := v.(string) 438 if value != s3.TransitionStorageClassStandardIa && value != s3.TransitionStorageClassGlacier { 439 errors = append(errors, fmt.Errorf( 440 "%q must be one of '%q', '%q'", k, s3.TransitionStorageClassStandardIa, s3.TransitionStorageClassGlacier)) 441 } 442 443 return 444 } 445 446 func validateS3BucketLifecycleRuleId(v interface{}, k string) (ws []string, errors []error) { 447 value := v.(string) 448 if len(value) > 255 { 449 errors = append(errors, fmt.Errorf( 450 "%q cannot exceed 255 characters", k)) 451 } 452 return 453 } 454 455 func validateDbEventSubscriptionName(v interface{}, k string) (ws []string, errors []error) { 456 value := v.(string) 457 if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) { 458 errors = append(errors, fmt.Errorf( 459 "only alphanumeric characters and hyphens allowed in %q", k)) 460 } 461 if len(value) > 255 { 462 errors = append(errors, fmt.Errorf( 463 "%q cannot be longer than 255 characters", k)) 464 } 465 return 466 } 467 468 func validateApiGatewayIntegrationPassthroughBehavior(v interface{}, k string) (ws []string, errors []error) { 469 value := v.(string) 470 if value != "WHEN_NO_MATCH" && value != "WHEN_NO_TEMPLATES" && value != "NEVER" { 471 errors = append(errors, fmt.Errorf( 472 "%q must be one of 'WHEN_NO_MATCH', 'WHEN_NO_TEMPLATES', 'NEVER'", k)) 473 } 474 return 475 } 476 477 func validateJsonString(v interface{}, k string) (ws []string, errors []error) { 478 if _, err := normalizeJsonString(v); err != nil { 479 errors = append(errors, fmt.Errorf("%q contains an invalid JSON: %s", k, err)) 480 } 481 return 482 } 483 484 func validateApiGatewayIntegrationType(v interface{}, k string) (ws []string, errors []error) { 485 value := v.(string) 486 487 validTypes := map[string]bool{ 488 "AWS": true, 489 "AWS_PROXY": true, 490 "HTTP": true, 491 "HTTP_PROXY": true, 492 "MOCK": true, 493 } 494 495 if _, ok := validTypes[value]; !ok { 496 errors = append(errors, fmt.Errorf( 497 "%q contains an invalid integration type %q. Valid types are either %q, %q, %q, %q, or %q.", 498 k, value, "AWS", "AWS_PROXY", "HTTP", "HTTP_PROXY", "MOCK")) 499 } 500 return 501 }