github.com/pbthorste/terraform@v0.8.6-0.20170127005045-deb56bd93da2/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:[\w-]+: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 if value == "" { 296 return 297 } 298 299 // http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html 300 pattern := `^arn:[\w-]+:([a-zA-Z0-9\-])+:([a-z]{2}-[a-z]+-\d{1})?:(\d{12})?:(.*)$` 301 if !regexp.MustCompile(pattern).MatchString(value) { 302 errors = append(errors, fmt.Errorf( 303 "%q doesn't look like a valid ARN (%q): %q", 304 k, pattern, value)) 305 } 306 307 return 308 } 309 310 func validatePolicyStatementId(v interface{}, k string) (ws []string, errors []error) { 311 value := v.(string) 312 313 if len(value) > 100 { 314 errors = append(errors, fmt.Errorf( 315 "%q cannot be longer than 100 characters: %q", k, value)) 316 } 317 318 // http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html 319 pattern := `^[a-zA-Z0-9-_]+$` 320 if !regexp.MustCompile(pattern).MatchString(value) { 321 errors = append(errors, fmt.Errorf( 322 "%q doesn't look like a valid statement ID (%q): %q", 323 k, pattern, value)) 324 } 325 326 return 327 } 328 329 // validateCIDRNetworkAddress ensures that the string value is a valid CIDR that 330 // represents a network address - it adds an error otherwise 331 func validateCIDRNetworkAddress(v interface{}, k string) (ws []string, errors []error) { 332 value := v.(string) 333 _, ipnet, err := net.ParseCIDR(value) 334 if err != nil { 335 errors = append(errors, fmt.Errorf( 336 "%q must contain a valid CIDR, got error parsing: %s", k, err)) 337 return 338 } 339 340 if ipnet == nil || value != ipnet.String() { 341 errors = append(errors, fmt.Errorf( 342 "%q must contain a valid network CIDR, expected %q, got %q", 343 k, ipnet, value)) 344 } 345 346 return 347 } 348 349 func validateHTTPMethod(v interface{}, k string) (ws []string, errors []error) { 350 value := v.(string) 351 352 validMethods := map[string]bool{ 353 "ANY": true, 354 "DELETE": true, 355 "GET": true, 356 "HEAD": true, 357 "OPTIONS": true, 358 "PATCH": true, 359 "POST": true, 360 "PUT": true, 361 } 362 363 if _, ok := validMethods[value]; !ok { 364 errors = append(errors, fmt.Errorf( 365 "%q contains an invalid method %q. Valid methods are either %q, %q, %q, %q, %q, %q, %q, or %q.", 366 k, value, "ANY", "DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT")) 367 } 368 return 369 } 370 371 func validateLogMetricFilterName(v interface{}, k string) (ws []string, errors []error) { 372 value := v.(string) 373 374 if len(value) > 512 { 375 errors = append(errors, fmt.Errorf( 376 "%q cannot be longer than 512 characters: %q", k, value)) 377 } 378 379 // http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutMetricFilter.html 380 pattern := `^[^:*]+$` 381 if !regexp.MustCompile(pattern).MatchString(value) { 382 errors = append(errors, fmt.Errorf( 383 "%q isn't a valid log metric name (must not contain colon nor asterisk): %q", 384 k, value)) 385 } 386 387 return 388 } 389 390 func validateLogMetricFilterTransformationName(v interface{}, k string) (ws []string, errors []error) { 391 value := v.(string) 392 393 if len(value) > 255 { 394 errors = append(errors, fmt.Errorf( 395 "%q cannot be longer than 255 characters: %q", k, value)) 396 } 397 398 // http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_MetricTransformation.html 399 pattern := `^[^:*$]*$` 400 if !regexp.MustCompile(pattern).MatchString(value) { 401 errors = append(errors, fmt.Errorf( 402 "%q isn't a valid log metric transformation name (must not contain"+ 403 " colon, asterisk nor dollar sign): %q", 404 k, value)) 405 } 406 407 return 408 } 409 410 func validateLogGroupName(v interface{}, k string) (ws []string, errors []error) { 411 value := v.(string) 412 413 if len(value) > 512 { 414 errors = append(errors, fmt.Errorf( 415 "%q cannot be longer than 512 characters: %q", k, value)) 416 } 417 418 // http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_CreateLogGroup.html 419 pattern := `^[\.\-_/#A-Za-z0-9]+$` 420 if !regexp.MustCompile(pattern).MatchString(value) { 421 errors = append(errors, fmt.Errorf( 422 "%q isn't a valid log group name (alphanumeric characters, underscores,"+ 423 " hyphens, slashes, hash signs and dots are allowed): %q", 424 k, value)) 425 } 426 427 return 428 } 429 430 func validateS3BucketLifecycleTimestamp(v interface{}, k string) (ws []string, errors []error) { 431 value := v.(string) 432 _, err := time.Parse(time.RFC3339, fmt.Sprintf("%sT00:00:00Z", value)) 433 if err != nil { 434 errors = append(errors, fmt.Errorf( 435 "%q cannot be parsed as RFC3339 Timestamp Format", value)) 436 } 437 438 return 439 } 440 441 func validateS3BucketLifecycleStorageClass(v interface{}, k string) (ws []string, errors []error) { 442 value := v.(string) 443 if value != s3.TransitionStorageClassStandardIa && value != s3.TransitionStorageClassGlacier { 444 errors = append(errors, fmt.Errorf( 445 "%q must be one of '%q', '%q'", k, s3.TransitionStorageClassStandardIa, s3.TransitionStorageClassGlacier)) 446 } 447 448 return 449 } 450 451 func validateS3BucketReplicationRuleId(v interface{}, k string) (ws []string, errors []error) { 452 value := v.(string) 453 if len(value) > 255 { 454 errors = append(errors, fmt.Errorf( 455 "%q cannot be longer than 255 characters: %q", k, value)) 456 } 457 458 return 459 } 460 461 func validateS3BucketReplicationRulePrefix(v interface{}, k string) (ws []string, errors []error) { 462 value := v.(string) 463 if len(value) > 1024 { 464 errors = append(errors, fmt.Errorf( 465 "%q cannot be longer than 1024 characters: %q", k, value)) 466 } 467 468 return 469 } 470 471 func validateS3BucketReplicationDestinationStorageClass(v interface{}, k string) (ws []string, errors []error) { 472 value := v.(string) 473 if value != s3.StorageClassStandard && value != s3.StorageClassStandardIa && value != s3.StorageClassReducedRedundancy { 474 errors = append(errors, fmt.Errorf( 475 "%q must be one of '%q', '%q' or '%q'", k, s3.StorageClassStandard, s3.StorageClassStandardIa, s3.StorageClassReducedRedundancy)) 476 } 477 478 return 479 } 480 481 func validateS3BucketReplicationRuleStatus(v interface{}, k string) (ws []string, errors []error) { 482 value := v.(string) 483 if value != s3.ReplicationRuleStatusEnabled && value != s3.ReplicationRuleStatusDisabled { 484 errors = append(errors, fmt.Errorf( 485 "%q must be one of '%q' or '%q'", k, s3.ReplicationRuleStatusEnabled, s3.ReplicationRuleStatusDisabled)) 486 } 487 488 return 489 } 490 491 func validateS3BucketLifecycleRuleId(v interface{}, k string) (ws []string, errors []error) { 492 value := v.(string) 493 if len(value) > 255 { 494 errors = append(errors, fmt.Errorf( 495 "%q cannot exceed 255 characters", k)) 496 } 497 return 498 } 499 500 func validateDbEventSubscriptionName(v interface{}, k string) (ws []string, errors []error) { 501 value := v.(string) 502 if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) { 503 errors = append(errors, fmt.Errorf( 504 "only alphanumeric characters and hyphens allowed in %q", k)) 505 } 506 if len(value) > 255 { 507 errors = append(errors, fmt.Errorf( 508 "%q cannot be longer than 255 characters", k)) 509 } 510 return 511 } 512 513 func validateApiGatewayIntegrationPassthroughBehavior(v interface{}, k string) (ws []string, errors []error) { 514 value := v.(string) 515 if value != "WHEN_NO_MATCH" && value != "WHEN_NO_TEMPLATES" && value != "NEVER" { 516 errors = append(errors, fmt.Errorf( 517 "%q must be one of 'WHEN_NO_MATCH', 'WHEN_NO_TEMPLATES', 'NEVER'", k)) 518 } 519 return 520 } 521 522 func validateJsonString(v interface{}, k string) (ws []string, errors []error) { 523 if _, err := normalizeJsonString(v); err != nil { 524 errors = append(errors, fmt.Errorf("%q contains an invalid JSON: %s", k, err)) 525 } 526 return 527 } 528 529 func validateCloudFormationTemplate(v interface{}, k string) (ws []string, errors []error) { 530 if looksLikeJsonString(v) { 531 if _, err := normalizeJsonString(v); err != nil { 532 errors = append(errors, fmt.Errorf("%q contains an invalid JSON: %s", k, err)) 533 } 534 } else { 535 if _, err := checkYamlString(v); err != nil { 536 errors = append(errors, fmt.Errorf("%q contains an invalid YAML: %s", k, err)) 537 } 538 } 539 return 540 } 541 542 func validateApiGatewayIntegrationType(v interface{}, k string) (ws []string, errors []error) { 543 value := v.(string) 544 545 validTypes := map[string]bool{ 546 "AWS": true, 547 "AWS_PROXY": true, 548 "HTTP": true, 549 "HTTP_PROXY": true, 550 "MOCK": true, 551 } 552 553 if _, ok := validTypes[value]; !ok { 554 errors = append(errors, fmt.Errorf( 555 "%q contains an invalid integration type %q. Valid types are either %q, %q, %q, %q, or %q.", 556 k, value, "AWS", "AWS_PROXY", "HTTP", "HTTP_PROXY", "MOCK")) 557 } 558 return 559 } 560 561 func validateApiGatewayIntegrationContentHandling(v interface{}, k string) (ws []string, errors []error) { 562 value := v.(string) 563 564 validTypes := map[string]bool{ 565 "CONVERT_TO_BINARY": true, 566 "CONVERT_TO_TEXT": true, 567 } 568 569 if _, ok := validTypes[value]; !ok { 570 errors = append(errors, fmt.Errorf( 571 "%q contains an invalid integration type %q. Valid types are either %q or %q.", 572 k, value, "CONVERT_TO_BINARY", "CONVERT_TO_TEXT")) 573 } 574 return 575 } 576 577 func validateSQSQueueName(v interface{}, k string) (errors []error) { 578 value := v.(string) 579 if len(value) > 80 { 580 errors = append(errors, fmt.Errorf("%q cannot be longer than 80 characters", k)) 581 } 582 583 if !regexp.MustCompile(`^[0-9A-Za-z-_]+$`).MatchString(value) { 584 errors = append(errors, fmt.Errorf("only alphanumeric characters and hyphens allowed in %q", k)) 585 } 586 return 587 } 588 589 func validateSQSFifoQueueName(v interface{}, k string) (errors []error) { 590 value := v.(string) 591 592 if len(value) > 80 { 593 errors = append(errors, fmt.Errorf("%q cannot be longer than 80 characters", k)) 594 } 595 596 if !regexp.MustCompile(`^[0-9A-Za-z-_.]+$`).MatchString(value) { 597 errors = append(errors, fmt.Errorf("only alphanumeric characters and hyphens allowed in %q", k)) 598 } 599 600 if regexp.MustCompile(`^[^a-zA-Z0-9-_]`).MatchString(value) { 601 errors = append(errors, fmt.Errorf("FIFO queue name must start with one of these characters [a-zA-Z0-9-_]: %v", value)) 602 } 603 604 if !regexp.MustCompile(`\.fifo$`).MatchString(value) { 605 errors = append(errors, fmt.Errorf("FIFO queue name should ends with \".fifo\": %v", value)) 606 } 607 608 return 609 } 610 611 func validateSNSSubscriptionProtocol(v interface{}, k string) (ws []string, errors []error) { 612 value := strings.ToLower(v.(string)) 613 forbidden := []string{"email", "sms"} 614 for _, f := range forbidden { 615 if strings.Contains(value, f) { 616 errors = append( 617 errors, 618 fmt.Errorf("Unsupported protocol (%s) for SNS Topic", value), 619 ) 620 } 621 } 622 return 623 } 624 625 func validateSecurityRuleType(v interface{}, k string) (ws []string, errors []error) { 626 value := strings.ToLower(v.(string)) 627 628 validTypes := map[string]bool{ 629 "ingress": true, 630 "egress": true, 631 } 632 633 if _, ok := validTypes[value]; !ok { 634 errors = append(errors, fmt.Errorf( 635 "%q contains an invalid Security Group Rule type %q. Valid types are either %q or %q.", 636 k, value, "ingress", "egress")) 637 } 638 return 639 } 640 641 func validateOnceAWeekWindowFormat(v interface{}, k string) (ws []string, errors []error) { 642 // valid time format is "ddd:hh24:mi" 643 validTimeFormat := "(sun|mon|tue|wed|thu|fri|sat):([0-1][0-9]|2[0-3]):([0-5][0-9])" 644 645 value := strings.ToLower(v.(string)) 646 if !regexp.MustCompile(validTimeFormat + "-" + validTimeFormat).MatchString(value) { 647 errors = append(errors, fmt.Errorf( 648 "%q must satisfy the format of \"ddd:hh24:mi-ddd:hh24:mi\".", k)) 649 } 650 return 651 } 652 653 func validateOnceADayWindowFormat(v interface{}, k string) (ws []string, errors []error) { 654 // valid time format is "hh24:mi" 655 validTimeFormat := "([0-1][0-9]|2[0-3]):([0-5][0-9])" 656 657 value := v.(string) 658 if !regexp.MustCompile(validTimeFormat + "-" + validTimeFormat).MatchString(value) { 659 errors = append(errors, fmt.Errorf( 660 "%q must satisfy the format of \"hh24:mi-hh24:mi\".", k)) 661 } 662 return 663 } 664 665 func validateRoute53RecordType(v interface{}, k string) (ws []string, errors []error) { 666 // Valid Record types 667 // SOA, A, TXT, NS, CNAME, MX, NAPTR, PTR, SRV, SPF, AAAA 668 validTypes := map[string]struct{}{ 669 "SOA": {}, 670 "A": {}, 671 "TXT": {}, 672 "NS": {}, 673 "CNAME": {}, 674 "MX": {}, 675 "NAPTR": {}, 676 "PTR": {}, 677 "SRV": {}, 678 "SPF": {}, 679 "AAAA": {}, 680 } 681 682 value := v.(string) 683 if _, ok := validTypes[value]; !ok { 684 errors = append(errors, fmt.Errorf( 685 "%q must be one of [SOA, A, TXT, NS, CNAME, MX, NAPTR, PTR, SRV, SPF, AAAA]", k)) 686 } 687 return 688 } 689 690 // Validates that ECS Placement Constraints are set correctly 691 // Takes type, and expression as strings 692 func validateAwsEcsPlacementConstraint(constType, constExpr string) error { 693 switch constType { 694 case "distinctInstance": 695 // Expression can be nil for distinctInstance 696 return nil 697 case "memberOf": 698 if constExpr == "" { 699 return fmt.Errorf("Expression cannot be nil for 'memberOf' type") 700 } 701 default: 702 return fmt.Errorf("Unknown type provided: %q", constType) 703 } 704 return nil 705 } 706 707 // Validates that an Ecs placement strategy is set correctly 708 // Takes type, and field as strings 709 func validateAwsEcsPlacementStrategy(stratType, stratField string) error { 710 switch stratType { 711 case "random": 712 // random does not need the field attribute set, could error, but it isn't read at the API level 713 return nil 714 case "spread": 715 // For the spread placement strategy, valid values are instanceId 716 // (or host, which has the same effect), or any platform or custom attribute 717 // that is applied to a container instance 718 // stratField is already cased to a string 719 return nil 720 case "binpack": 721 if stratField != "cpu" && stratField != "memory" { 722 return fmt.Errorf("Binpack type requires the field attribute to be either 'cpu' or 'memory'. Got: %s", 723 stratField) 724 } 725 default: 726 return fmt.Errorf("Unknown type %s. Must be one of 'random', 'spread', or 'binpack'.", stratType) 727 } 728 return nil 729 }