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