github.com/dougneal/terraform@v0.6.15-0.20170330092735-b6a3840768a4/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/apigateway"
    11  	"github.com/aws/aws-sdk-go/service/s3"
    12  	"github.com/hashicorp/terraform/helper/schema"
    13  )
    14  
    15  func validateRdsId(v interface{}, k string) (ws []string, errors []error) {
    16  	value := v.(string)
    17  	if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) {
    18  		errors = append(errors, fmt.Errorf(
    19  			"only lowercase alphanumeric characters and hyphens allowed in %q", k))
    20  	}
    21  	if !regexp.MustCompile(`^[a-z]`).MatchString(value) {
    22  		errors = append(errors, fmt.Errorf(
    23  			"first character of %q must be a letter", k))
    24  	}
    25  	if regexp.MustCompile(`--`).MatchString(value) {
    26  		errors = append(errors, fmt.Errorf(
    27  			"%q cannot contain two consecutive hyphens", k))
    28  	}
    29  	if regexp.MustCompile(`-$`).MatchString(value) {
    30  		errors = append(errors, fmt.Errorf(
    31  			"%q cannot end with a hyphen", k))
    32  	}
    33  	return
    34  }
    35  
    36  func validateElastiCacheClusterId(v interface{}, k string) (ws []string, errors []error) {
    37  	value := v.(string)
    38  	if (len(value) < 1) || (len(value) > 20) {
    39  		errors = append(errors, fmt.Errorf(
    40  			"%q (%q) must contain from 1 to 20 alphanumeric characters or hyphens", k, value))
    41  	}
    42  	if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) {
    43  		errors = append(errors, fmt.Errorf(
    44  			"only lowercase alphanumeric characters and hyphens allowed in %q (%q)", k, value))
    45  	}
    46  	if !regexp.MustCompile(`^[a-z]`).MatchString(value) {
    47  		errors = append(errors, fmt.Errorf(
    48  			"first character of %q (%q) must be a letter", k, value))
    49  	}
    50  	if regexp.MustCompile(`--`).MatchString(value) {
    51  		errors = append(errors, fmt.Errorf(
    52  			"%q (%q) cannot contain two consecutive hyphens", k, value))
    53  	}
    54  	if regexp.MustCompile(`-$`).MatchString(value) {
    55  		errors = append(errors, fmt.Errorf(
    56  			"%q (%q) cannot end with a hyphen", k, value))
    57  	}
    58  	return
    59  }
    60  
    61  func validateASGScheduleTimestamp(v interface{}, k string) (ws []string, errors []error) {
    62  	value := v.(string)
    63  	_, err := time.Parse(awsAutoscalingScheduleTimeLayout, value)
    64  	if err != nil {
    65  		errors = append(errors, fmt.Errorf(
    66  			"%q cannot be parsed as iso8601 Timestamp Format", value))
    67  	}
    68  
    69  	return
    70  }
    71  
    72  // validateTagFilters confirms the "value" component of a tag filter is one of
    73  // AWS's three allowed types.
    74  func validateTagFilters(v interface{}, k string) (ws []string, errors []error) {
    75  	value := v.(string)
    76  	if value != "KEY_ONLY" && value != "VALUE_ONLY" && value != "KEY_AND_VALUE" {
    77  		errors = append(errors, fmt.Errorf(
    78  			"%q must be one of \"KEY_ONLY\", \"VALUE_ONLY\", or \"KEY_AND_VALUE\"", k))
    79  	}
    80  	return
    81  }
    82  
    83  func validateDbParamGroupName(v interface{}, k string) (ws []string, errors []error) {
    84  	value := v.(string)
    85  	if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) {
    86  		errors = append(errors, fmt.Errorf(
    87  			"only lowercase alphanumeric characters and hyphens allowed in %q", k))
    88  	}
    89  	if !regexp.MustCompile(`^[a-z]`).MatchString(value) {
    90  		errors = append(errors, fmt.Errorf(
    91  			"first character of %q must be a letter", k))
    92  	}
    93  	if regexp.MustCompile(`--`).MatchString(value) {
    94  		errors = append(errors, fmt.Errorf(
    95  			"%q cannot contain two consecutive hyphens", k))
    96  	}
    97  	if regexp.MustCompile(`-$`).MatchString(value) {
    98  		errors = append(errors, fmt.Errorf(
    99  			"%q cannot end with a hyphen", k))
   100  	}
   101  	if len(value) > 255 {
   102  		errors = append(errors, fmt.Errorf(
   103  			"%q cannot be greater than 255 characters", k))
   104  	}
   105  	return
   106  
   107  }
   108  
   109  func validateStreamViewType(v interface{}, k string) (ws []string, errors []error) {
   110  	value := v.(string)
   111  	viewTypes := map[string]bool{
   112  		"KEYS_ONLY":          true,
   113  		"NEW_IMAGE":          true,
   114  		"OLD_IMAGE":          true,
   115  		"NEW_AND_OLD_IMAGES": true,
   116  	}
   117  
   118  	if !viewTypes[value] {
   119  		errors = append(errors, fmt.Errorf("%q be a valid DynamoDB StreamViewType", k))
   120  	}
   121  	return
   122  }
   123  
   124  func validateElbName(v interface{}, k string) (ws []string, errors []error) {
   125  	value := v.(string)
   126  	if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) {
   127  		errors = append(errors, fmt.Errorf(
   128  			"only alphanumeric characters and hyphens allowed in %q: %q",
   129  			k, value))
   130  	}
   131  	if len(value) > 32 {
   132  		errors = append(errors, fmt.Errorf(
   133  			"%q cannot be longer than 32 characters: %q", k, value))
   134  	}
   135  	if regexp.MustCompile(`^-`).MatchString(value) {
   136  		errors = append(errors, fmt.Errorf(
   137  			"%q cannot begin with a hyphen: %q", k, value))
   138  	}
   139  	if regexp.MustCompile(`-$`).MatchString(value) {
   140  		errors = append(errors, fmt.Errorf(
   141  			"%q cannot end with a hyphen: %q", k, value))
   142  	}
   143  	return
   144  }
   145  
   146  func validateElbNamePrefix(v interface{}, k string) (ws []string, errors []error) {
   147  	value := v.(string)
   148  	if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) {
   149  		errors = append(errors, fmt.Errorf(
   150  			"only alphanumeric characters and hyphens allowed in %q: %q",
   151  			k, value))
   152  	}
   153  	if len(value) > 6 {
   154  		errors = append(errors, fmt.Errorf(
   155  			"%q cannot be longer than 6 characters: %q", k, value))
   156  	}
   157  	if regexp.MustCompile(`^-`).MatchString(value) {
   158  		errors = append(errors, fmt.Errorf(
   159  			"%q cannot begin with a hyphen: %q", k, value))
   160  	}
   161  	return
   162  }
   163  
   164  func validateEcrRepositoryName(v interface{}, k string) (ws []string, errors []error) {
   165  	value := v.(string)
   166  	if len(value) < 2 {
   167  		errors = append(errors, fmt.Errorf(
   168  			"%q must be at least 2 characters long: %q", k, value))
   169  	}
   170  	if len(value) > 256 {
   171  		errors = append(errors, fmt.Errorf(
   172  			"%q cannot be longer than 256 characters: %q", k, value))
   173  	}
   174  
   175  	// http://docs.aws.amazon.com/AmazonECR/latest/APIReference/API_CreateRepository.html
   176  	pattern := `^(?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)*[a-z0-9]+(?:[._-][a-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 validateCloudWatchEventRuleName(v interface{}, k string) (ws []string, errors []error) {
   187  	value := v.(string)
   188  	if len(value) > 64 {
   189  		errors = append(errors, fmt.Errorf(
   190  			"%q cannot be longer than 64 characters: %q", k, value))
   191  	}
   192  
   193  	// http://docs.aws.amazon.com/AmazonCloudWatchEvents/latest/APIReference/API_PutRule.html
   194  	pattern := `^[\.\-_A-Za-z0-9]+$`
   195  	if !regexp.MustCompile(pattern).MatchString(value) {
   196  		errors = append(errors, fmt.Errorf(
   197  			"%q doesn't comply with restrictions (%q): %q",
   198  			k, pattern, value))
   199  	}
   200  
   201  	return
   202  }
   203  
   204  func validateMaxLength(length int) schema.SchemaValidateFunc {
   205  	return func(v interface{}, k string) (ws []string, errors []error) {
   206  		value := v.(string)
   207  		if len(value) > length {
   208  			errors = append(errors, fmt.Errorf(
   209  				"%q cannot be longer than %d characters: %q", k, length, value))
   210  		}
   211  		return
   212  	}
   213  }
   214  
   215  func validateIntegerInRange(min, max int) schema.SchemaValidateFunc {
   216  	return func(v interface{}, k string) (ws []string, errors []error) {
   217  		value := v.(int)
   218  		if value < min {
   219  			errors = append(errors, fmt.Errorf(
   220  				"%q cannot be lower than %d: %d", k, min, value))
   221  		}
   222  		if value > max {
   223  			errors = append(errors, fmt.Errorf(
   224  				"%q cannot be higher than %d: %d", k, max, value))
   225  		}
   226  		return
   227  	}
   228  }
   229  
   230  func validateCloudWatchEventTargetId(v interface{}, k string) (ws []string, errors []error) {
   231  	value := v.(string)
   232  	if len(value) > 64 {
   233  		errors = append(errors, fmt.Errorf(
   234  			"%q cannot be longer than 64 characters: %q", k, value))
   235  	}
   236  
   237  	// http://docs.aws.amazon.com/AmazonCloudWatchEvents/latest/APIReference/API_Target.html
   238  	pattern := `^[\.\-_A-Za-z0-9]+$`
   239  	if !regexp.MustCompile(pattern).MatchString(value) {
   240  		errors = append(errors, fmt.Errorf(
   241  			"%q doesn't comply with restrictions (%q): %q",
   242  			k, pattern, value))
   243  	}
   244  
   245  	return
   246  }
   247  
   248  func validateLambdaFunctionName(v interface{}, k string) (ws []string, errors []error) {
   249  	value := v.(string)
   250  	if len(value) > 140 {
   251  		errors = append(errors, fmt.Errorf(
   252  			"%q cannot be longer than 140 characters: %q", k, value))
   253  	}
   254  	// http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html
   255  	pattern := `^(arn:[\w-]+:lambda:)?([a-z]{2}-[a-z]+-\d{1}:)?(\d{12}:)?(function:)?([a-zA-Z0-9-_]+)(:(\$LATEST|[a-zA-Z0-9-_]+))?$`
   256  	if !regexp.MustCompile(pattern).MatchString(value) {
   257  		errors = append(errors, fmt.Errorf(
   258  			"%q doesn't comply with restrictions (%q): %q",
   259  			k, pattern, value))
   260  	}
   261  
   262  	return
   263  }
   264  
   265  func validateLambdaQualifier(v interface{}, k string) (ws []string, errors []error) {
   266  	value := v.(string)
   267  	if len(value) > 128 {
   268  		errors = append(errors, fmt.Errorf(
   269  			"%q cannot be longer than 128 characters: %q", k, value))
   270  	}
   271  	// http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html
   272  	pattern := `^[a-zA-Z0-9$_-]+$`
   273  	if !regexp.MustCompile(pattern).MatchString(value) {
   274  		errors = append(errors, fmt.Errorf(
   275  			"%q doesn't comply with restrictions (%q): %q",
   276  			k, pattern, value))
   277  	}
   278  
   279  	return
   280  }
   281  
   282  func validateLambdaPermissionAction(v interface{}, k string) (ws []string, errors []error) {
   283  	value := v.(string)
   284  
   285  	// http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html
   286  	pattern := `^(lambda:[*]|lambda:[a-zA-Z]+|[*])$`
   287  	if !regexp.MustCompile(pattern).MatchString(value) {
   288  		errors = append(errors, fmt.Errorf(
   289  			"%q doesn't comply with restrictions (%q): %q",
   290  			k, pattern, value))
   291  	}
   292  
   293  	return
   294  }
   295  
   296  func validateAwsAccountId(v interface{}, k string) (ws []string, errors []error) {
   297  	value := v.(string)
   298  
   299  	// http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html
   300  	pattern := `^\d{12}$`
   301  	if !regexp.MustCompile(pattern).MatchString(value) {
   302  		errors = append(errors, fmt.Errorf(
   303  			"%q doesn't look like AWS Account ID (exactly 12 digits): %q",
   304  			k, value))
   305  	}
   306  
   307  	return
   308  }
   309  
   310  func validateArn(v interface{}, k string) (ws []string, errors []error) {
   311  	value := v.(string)
   312  
   313  	if value == "" {
   314  		return
   315  	}
   316  
   317  	// http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html
   318  	pattern := `^arn:[\w-]+:([a-zA-Z0-9\-])+:([a-z]{2}-[a-z]+-\d{1})?:(\d{12})?:(.*)$`
   319  	if !regexp.MustCompile(pattern).MatchString(value) {
   320  		errors = append(errors, fmt.Errorf(
   321  			"%q doesn't look like a valid ARN (%q): %q",
   322  			k, pattern, value))
   323  	}
   324  
   325  	return
   326  }
   327  
   328  func validatePolicyStatementId(v interface{}, k string) (ws []string, errors []error) {
   329  	value := v.(string)
   330  
   331  	if len(value) > 100 {
   332  		errors = append(errors, fmt.Errorf(
   333  			"%q cannot be longer than 100 characters: %q", k, value))
   334  	}
   335  
   336  	// http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html
   337  	pattern := `^[a-zA-Z0-9-_]+$`
   338  	if !regexp.MustCompile(pattern).MatchString(value) {
   339  		errors = append(errors, fmt.Errorf(
   340  			"%q doesn't look like a valid statement ID (%q): %q",
   341  			k, pattern, value))
   342  	}
   343  
   344  	return
   345  }
   346  
   347  // validateCIDRNetworkAddress ensures that the string value is a valid CIDR that
   348  // represents a network address - it adds an error otherwise
   349  func validateCIDRNetworkAddress(v interface{}, k string) (ws []string, errors []error) {
   350  	value := v.(string)
   351  	_, ipnet, err := net.ParseCIDR(value)
   352  	if err != nil {
   353  		errors = append(errors, fmt.Errorf(
   354  			"%q must contain a valid CIDR, got error parsing: %s", k, err))
   355  		return
   356  	}
   357  
   358  	if ipnet == nil || value != ipnet.String() {
   359  		errors = append(errors, fmt.Errorf(
   360  			"%q must contain a valid network CIDR, expected %q, got %q",
   361  			k, ipnet, value))
   362  	}
   363  
   364  	return
   365  }
   366  
   367  func validateHTTPMethod(v interface{}, k string) (ws []string, errors []error) {
   368  	value := v.(string)
   369  
   370  	validMethods := map[string]bool{
   371  		"ANY":     true,
   372  		"DELETE":  true,
   373  		"GET":     true,
   374  		"HEAD":    true,
   375  		"OPTIONS": true,
   376  		"PATCH":   true,
   377  		"POST":    true,
   378  		"PUT":     true,
   379  	}
   380  
   381  	if _, ok := validMethods[value]; !ok {
   382  		errors = append(errors, fmt.Errorf(
   383  			"%q contains an invalid method %q. Valid methods are either %q, %q, %q, %q, %q, %q, %q, or %q.",
   384  			k, value, "ANY", "DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"))
   385  	}
   386  	return
   387  }
   388  
   389  func validateLogMetricFilterName(v interface{}, k string) (ws []string, errors []error) {
   390  	value := v.(string)
   391  
   392  	if len(value) > 512 {
   393  		errors = append(errors, fmt.Errorf(
   394  			"%q cannot be longer than 512 characters: %q", k, value))
   395  	}
   396  
   397  	// http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutMetricFilter.html
   398  	pattern := `^[^:*]+$`
   399  	if !regexp.MustCompile(pattern).MatchString(value) {
   400  		errors = append(errors, fmt.Errorf(
   401  			"%q isn't a valid log metric name (must not contain colon nor asterisk): %q",
   402  			k, value))
   403  	}
   404  
   405  	return
   406  }
   407  
   408  func validateLogMetricFilterTransformationName(v interface{}, k string) (ws []string, errors []error) {
   409  	value := v.(string)
   410  
   411  	if len(value) > 255 {
   412  		errors = append(errors, fmt.Errorf(
   413  			"%q cannot be longer than 255 characters: %q", k, value))
   414  	}
   415  
   416  	// http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_MetricTransformation.html
   417  	pattern := `^[^:*$]*$`
   418  	if !regexp.MustCompile(pattern).MatchString(value) {
   419  		errors = append(errors, fmt.Errorf(
   420  			"%q isn't a valid log metric transformation name (must not contain"+
   421  				" colon, asterisk nor dollar sign): %q",
   422  			k, value))
   423  	}
   424  
   425  	return
   426  }
   427  
   428  func validateLogGroupName(v interface{}, k string) (ws []string, errors []error) {
   429  	value := v.(string)
   430  
   431  	if len(value) > 512 {
   432  		errors = append(errors, fmt.Errorf(
   433  			"%q cannot be longer than 512 characters: %q", k, value))
   434  	}
   435  
   436  	// http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_CreateLogGroup.html
   437  	pattern := `^[\.\-_/#A-Za-z0-9]+$`
   438  	if !regexp.MustCompile(pattern).MatchString(value) {
   439  		errors = append(errors, fmt.Errorf(
   440  			"%q isn't a valid log group name (alphanumeric characters, underscores,"+
   441  				" hyphens, slashes, hash signs and dots are allowed): %q",
   442  			k, value))
   443  	}
   444  
   445  	return
   446  }
   447  
   448  func validateS3BucketLifecycleTimestamp(v interface{}, k string) (ws []string, errors []error) {
   449  	value := v.(string)
   450  	_, err := time.Parse(time.RFC3339, fmt.Sprintf("%sT00:00:00Z", value))
   451  	if err != nil {
   452  		errors = append(errors, fmt.Errorf(
   453  			"%q cannot be parsed as RFC3339 Timestamp Format", value))
   454  	}
   455  
   456  	return
   457  }
   458  
   459  func validateS3BucketLifecycleStorageClass(v interface{}, k string) (ws []string, errors []error) {
   460  	value := v.(string)
   461  	if value != s3.TransitionStorageClassStandardIa && value != s3.TransitionStorageClassGlacier {
   462  		errors = append(errors, fmt.Errorf(
   463  			"%q must be one of '%q', '%q'", k, s3.TransitionStorageClassStandardIa, s3.TransitionStorageClassGlacier))
   464  	}
   465  
   466  	return
   467  }
   468  
   469  func validateS3BucketReplicationRuleId(v interface{}, k string) (ws []string, errors []error) {
   470  	value := v.(string)
   471  	if len(value) > 255 {
   472  		errors = append(errors, fmt.Errorf(
   473  			"%q cannot be longer than 255 characters: %q", k, value))
   474  	}
   475  
   476  	return
   477  }
   478  
   479  func validateS3BucketReplicationRulePrefix(v interface{}, k string) (ws []string, errors []error) {
   480  	value := v.(string)
   481  	if len(value) > 1024 {
   482  		errors = append(errors, fmt.Errorf(
   483  			"%q cannot be longer than 1024 characters: %q", k, value))
   484  	}
   485  
   486  	return
   487  }
   488  
   489  func validateS3BucketReplicationDestinationStorageClass(v interface{}, k string) (ws []string, errors []error) {
   490  	value := v.(string)
   491  	if value != s3.StorageClassStandard && value != s3.StorageClassStandardIa && value != s3.StorageClassReducedRedundancy {
   492  		errors = append(errors, fmt.Errorf(
   493  			"%q must be one of '%q', '%q' or '%q'", k, s3.StorageClassStandard, s3.StorageClassStandardIa, s3.StorageClassReducedRedundancy))
   494  	}
   495  
   496  	return
   497  }
   498  
   499  func validateS3BucketReplicationRuleStatus(v interface{}, k string) (ws []string, errors []error) {
   500  	value := v.(string)
   501  	if value != s3.ReplicationRuleStatusEnabled && value != s3.ReplicationRuleStatusDisabled {
   502  		errors = append(errors, fmt.Errorf(
   503  			"%q must be one of '%q' or '%q'", k, s3.ReplicationRuleStatusEnabled, s3.ReplicationRuleStatusDisabled))
   504  	}
   505  
   506  	return
   507  }
   508  
   509  func validateS3BucketLifecycleRuleId(v interface{}, k string) (ws []string, errors []error) {
   510  	value := v.(string)
   511  	if len(value) > 255 {
   512  		errors = append(errors, fmt.Errorf(
   513  			"%q cannot exceed 255 characters", k))
   514  	}
   515  	return
   516  }
   517  
   518  func validateDbEventSubscriptionName(v interface{}, k string) (ws []string, errors []error) {
   519  	value := v.(string)
   520  	if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) {
   521  		errors = append(errors, fmt.Errorf(
   522  			"only alphanumeric characters and hyphens allowed in %q", k))
   523  	}
   524  	if len(value) > 255 {
   525  		errors = append(errors, fmt.Errorf(
   526  			"%q cannot be longer than 255 characters", k))
   527  	}
   528  	return
   529  }
   530  
   531  func validateApiGatewayIntegrationPassthroughBehavior(v interface{}, k string) (ws []string, errors []error) {
   532  	value := v.(string)
   533  	if value != "WHEN_NO_MATCH" && value != "WHEN_NO_TEMPLATES" && value != "NEVER" {
   534  		errors = append(errors, fmt.Errorf(
   535  			"%q must be one of 'WHEN_NO_MATCH', 'WHEN_NO_TEMPLATES', 'NEVER'", k))
   536  	}
   537  	return
   538  }
   539  
   540  func validateJsonString(v interface{}, k string) (ws []string, errors []error) {
   541  	if _, err := normalizeJsonString(v); err != nil {
   542  		errors = append(errors, fmt.Errorf("%q contains an invalid JSON: %s", k, err))
   543  	}
   544  	return
   545  }
   546  
   547  func validateCloudFormationTemplate(v interface{}, k string) (ws []string, errors []error) {
   548  	if looksLikeJsonString(v) {
   549  		if _, err := normalizeJsonString(v); err != nil {
   550  			errors = append(errors, fmt.Errorf("%q contains an invalid JSON: %s", k, err))
   551  		}
   552  	} else {
   553  		if _, err := checkYamlString(v); err != nil {
   554  			errors = append(errors, fmt.Errorf("%q contains an invalid YAML: %s", k, err))
   555  		}
   556  	}
   557  	return
   558  }
   559  
   560  func validateApiGatewayIntegrationType(v interface{}, k string) (ws []string, errors []error) {
   561  	value := v.(string)
   562  
   563  	validTypes := map[string]bool{
   564  		"AWS":        true,
   565  		"AWS_PROXY":  true,
   566  		"HTTP":       true,
   567  		"HTTP_PROXY": true,
   568  		"MOCK":       true,
   569  	}
   570  
   571  	if _, ok := validTypes[value]; !ok {
   572  		errors = append(errors, fmt.Errorf(
   573  			"%q contains an invalid integration type %q. Valid types are either %q, %q, %q, %q, or %q.",
   574  			k, value, "AWS", "AWS_PROXY", "HTTP", "HTTP_PROXY", "MOCK"))
   575  	}
   576  	return
   577  }
   578  
   579  func validateApiGatewayIntegrationContentHandling(v interface{}, k string) (ws []string, errors []error) {
   580  	value := v.(string)
   581  
   582  	validTypes := map[string]bool{
   583  		"CONVERT_TO_BINARY": true,
   584  		"CONVERT_TO_TEXT":   true,
   585  	}
   586  
   587  	if _, ok := validTypes[value]; !ok {
   588  		errors = append(errors, fmt.Errorf(
   589  			"%q contains an invalid integration type %q. Valid types are either %q or %q.",
   590  			k, value, "CONVERT_TO_BINARY", "CONVERT_TO_TEXT"))
   591  	}
   592  	return
   593  }
   594  
   595  func validateSQSQueueName(v interface{}, k string) (errors []error) {
   596  	value := v.(string)
   597  	if len(value) > 80 {
   598  		errors = append(errors, fmt.Errorf("%q cannot be longer than 80 characters", k))
   599  	}
   600  
   601  	if !regexp.MustCompile(`^[0-9A-Za-z-_]+$`).MatchString(value) {
   602  		errors = append(errors, fmt.Errorf("only alphanumeric characters and hyphens allowed in %q", k))
   603  	}
   604  	return
   605  }
   606  
   607  func validateSQSFifoQueueName(v interface{}, k string) (errors []error) {
   608  	value := v.(string)
   609  
   610  	if len(value) > 80 {
   611  		errors = append(errors, fmt.Errorf("%q cannot be longer than 80 characters", k))
   612  	}
   613  
   614  	if !regexp.MustCompile(`^[0-9A-Za-z-_.]+$`).MatchString(value) {
   615  		errors = append(errors, fmt.Errorf("only alphanumeric characters and hyphens allowed in %q", k))
   616  	}
   617  
   618  	if regexp.MustCompile(`^[^a-zA-Z0-9-_]`).MatchString(value) {
   619  		errors = append(errors, fmt.Errorf("FIFO queue name must start with one of these characters [a-zA-Z0-9-_]: %v", value))
   620  	}
   621  
   622  	if !regexp.MustCompile(`\.fifo$`).MatchString(value) {
   623  		errors = append(errors, fmt.Errorf("FIFO queue name should ends with \".fifo\": %v", value))
   624  	}
   625  
   626  	return
   627  }
   628  
   629  func validateSNSSubscriptionProtocol(v interface{}, k string) (ws []string, errors []error) {
   630  	value := strings.ToLower(v.(string))
   631  	forbidden := []string{"email", "sms"}
   632  	for _, f := range forbidden {
   633  		if strings.Contains(value, f) {
   634  			errors = append(
   635  				errors,
   636  				fmt.Errorf("Unsupported protocol (%s) for SNS Topic", value),
   637  			)
   638  		}
   639  	}
   640  	return
   641  }
   642  
   643  func validateSecurityRuleType(v interface{}, k string) (ws []string, errors []error) {
   644  	value := strings.ToLower(v.(string))
   645  
   646  	validTypes := map[string]bool{
   647  		"ingress": true,
   648  		"egress":  true,
   649  	}
   650  
   651  	if _, ok := validTypes[value]; !ok {
   652  		errors = append(errors, fmt.Errorf(
   653  			"%q contains an invalid Security Group Rule type %q. Valid types are either %q or %q.",
   654  			k, value, "ingress", "egress"))
   655  	}
   656  	return
   657  }
   658  
   659  func validateOnceAWeekWindowFormat(v interface{}, k string) (ws []string, errors []error) {
   660  	// valid time format is "ddd:hh24:mi"
   661  	validTimeFormat := "(sun|mon|tue|wed|thu|fri|sat):([0-1][0-9]|2[0-3]):([0-5][0-9])"
   662  	validTimeFormatConsolidated := "^(" + validTimeFormat + "-" + validTimeFormat + "|)$"
   663  
   664  	value := strings.ToLower(v.(string))
   665  	if !regexp.MustCompile(validTimeFormatConsolidated).MatchString(value) {
   666  		errors = append(errors, fmt.Errorf(
   667  			"%q must satisfy the format of \"ddd:hh24:mi-ddd:hh24:mi\".", k))
   668  	}
   669  	return
   670  }
   671  
   672  func validateOnceADayWindowFormat(v interface{}, k string) (ws []string, errors []error) {
   673  	// valid time format is "hh24:mi"
   674  	validTimeFormat := "([0-1][0-9]|2[0-3]):([0-5][0-9])"
   675  	validTimeFormatConsolidated := "^(" + validTimeFormat + "-" + validTimeFormat + "|)$"
   676  
   677  	value := v.(string)
   678  	if !regexp.MustCompile(validTimeFormatConsolidated).MatchString(value) {
   679  		errors = append(errors, fmt.Errorf(
   680  			"%q must satisfy the format of \"hh24:mi-hh24:mi\".", k))
   681  	}
   682  	return
   683  }
   684  
   685  func validateRoute53RecordType(v interface{}, k string) (ws []string, errors []error) {
   686  	// Valid Record types
   687  	// SOA, A, TXT, NS, CNAME, MX, NAPTR, PTR, SRV, SPF, AAAA
   688  	validTypes := map[string]struct{}{
   689  		"SOA":   {},
   690  		"A":     {},
   691  		"TXT":   {},
   692  		"NS":    {},
   693  		"CNAME": {},
   694  		"MX":    {},
   695  		"NAPTR": {},
   696  		"PTR":   {},
   697  		"SRV":   {},
   698  		"SPF":   {},
   699  		"AAAA":  {},
   700  	}
   701  
   702  	value := v.(string)
   703  	if _, ok := validTypes[value]; !ok {
   704  		errors = append(errors, fmt.Errorf(
   705  			"%q must be one of [SOA, A, TXT, NS, CNAME, MX, NAPTR, PTR, SRV, SPF, AAAA]", k))
   706  	}
   707  	return
   708  }
   709  
   710  // Validates that ECS Placement Constraints are set correctly
   711  // Takes type, and expression as strings
   712  func validateAwsEcsPlacementConstraint(constType, constExpr string) error {
   713  	switch constType {
   714  	case "distinctInstance":
   715  		// Expression can be nil for distinctInstance
   716  		return nil
   717  	case "memberOf":
   718  		if constExpr == "" {
   719  			return fmt.Errorf("Expression cannot be nil for 'memberOf' type")
   720  		}
   721  	default:
   722  		return fmt.Errorf("Unknown type provided: %q", constType)
   723  	}
   724  	return nil
   725  }
   726  
   727  // Validates that an Ecs placement strategy is set correctly
   728  // Takes type, and field as strings
   729  func validateAwsEcsPlacementStrategy(stratType, stratField string) error {
   730  	switch stratType {
   731  	case "random":
   732  		// random does not need the field attribute set, could error, but it isn't read at the API level
   733  		return nil
   734  	case "spread":
   735  		//  For the spread placement strategy, valid values are instanceId
   736  		// (or host, which has the same effect), or any platform or custom attribute
   737  		// that is applied to a container instance
   738  		// stratField is already cased to a string
   739  		return nil
   740  	case "binpack":
   741  		if stratField != "cpu" && stratField != "memory" {
   742  			return fmt.Errorf("Binpack type requires the field attribute to be either 'cpu' or 'memory'. Got: %s",
   743  				stratField)
   744  		}
   745  	default:
   746  		return fmt.Errorf("Unknown type %s. Must be one of 'random', 'spread', or 'binpack'.", stratType)
   747  	}
   748  	return nil
   749  }
   750  
   751  func validateAwsEmrEbsVolumeType(v interface{}, k string) (ws []string, errors []error) {
   752  	validTypes := map[string]struct{}{
   753  		"gp2":      {},
   754  		"io1":      {},
   755  		"standard": {},
   756  	}
   757  
   758  	value := v.(string)
   759  
   760  	if _, ok := validTypes[value]; !ok {
   761  		errors = append(errors, fmt.Errorf(
   762  			"%q must be one of ['gp2', 'io1', 'standard']", k))
   763  	}
   764  	return
   765  }
   766  
   767  func validateSfnActivityName(v interface{}, k string) (ws []string, errors []error) {
   768  	value := v.(string)
   769  	if len(value) > 80 {
   770  		errors = append(errors, fmt.Errorf("%q cannot be longer than 80 characters", k))
   771  	}
   772  
   773  	return
   774  }
   775  
   776  func validateSfnStateMachineDefinition(v interface{}, k string) (ws []string, errors []error) {
   777  	value := v.(string)
   778  	if len(value) > 1048576 {
   779  		errors = append(errors, fmt.Errorf("%q cannot be longer than 1048576 characters", k))
   780  	}
   781  	return
   782  }
   783  
   784  func validateSfnStateMachineName(v interface{}, k string) (ws []string, errors []error) {
   785  	value := v.(string)
   786  	if len(value) > 80 {
   787  		errors = append(errors, fmt.Errorf("%q cannot be longer than 80 characters", k))
   788  	}
   789  
   790  	if !regexp.MustCompile(`^[a-zA-Z0-9-_]+$`).MatchString(value) {
   791  		errors = append(errors, fmt.Errorf(
   792  			"%q must be composed with only these characters [a-zA-Z0-9-_]: %v", k, value))
   793  	}
   794  	return
   795  }
   796  
   797  func validateDmsCertificateId(v interface{}, k string) (ws []string, es []error) {
   798  	val := v.(string)
   799  
   800  	if len(val) > 255 {
   801  		es = append(es, fmt.Errorf("%q must not be longer than 255 characters", k))
   802  	}
   803  	if !regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9-]+$").MatchString(val) {
   804  		es = append(es, fmt.Errorf("%q must start with a letter, only contain alphanumeric characters and hyphens", k))
   805  	}
   806  	if strings.Contains(val, "--") {
   807  		es = append(es, fmt.Errorf("%q must not contain consecutive hyphens", k))
   808  	}
   809  	if strings.HasSuffix(val, "-") {
   810  		es = append(es, fmt.Errorf("%q must not end in a hyphen", k))
   811  	}
   812  
   813  	return
   814  }
   815  
   816  func validateDmsEndpointId(v interface{}, k string) (ws []string, es []error) {
   817  	val := v.(string)
   818  
   819  	if len(val) > 255 {
   820  		es = append(es, fmt.Errorf("%q must not be longer than 255 characters", k))
   821  	}
   822  	if !regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9-]+$").MatchString(val) {
   823  		es = append(es, fmt.Errorf("%q must start with a letter, only contain alphanumeric characters and hyphens", k))
   824  	}
   825  	if strings.Contains(val, "--") {
   826  		es = append(es, fmt.Errorf("%q must not contain consecutive hyphens", k))
   827  	}
   828  	if strings.HasSuffix(val, "-") {
   829  		es = append(es, fmt.Errorf("%q must not end in a hyphen", k))
   830  	}
   831  
   832  	return
   833  }
   834  
   835  func validateDmsReplicationInstanceId(v interface{}, k string) (ws []string, es []error) {
   836  	val := v.(string)
   837  
   838  	if len(val) > 63 {
   839  		es = append(es, fmt.Errorf("%q must not be longer than 63 characters", k))
   840  	}
   841  	if !regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9-]+$").MatchString(val) {
   842  		es = append(es, fmt.Errorf("%q must start with a letter, only contain alphanumeric characters and hyphens", k))
   843  	}
   844  	if strings.Contains(val, "--") {
   845  		es = append(es, fmt.Errorf("%q must not contain consecutive hyphens", k))
   846  	}
   847  	if strings.HasSuffix(val, "-") {
   848  		es = append(es, fmt.Errorf("%q must not end in a hyphen", k))
   849  	}
   850  
   851  	return
   852  }
   853  
   854  func validateDmsReplicationSubnetGroupId(v interface{}, k string) (ws []string, es []error) {
   855  	val := v.(string)
   856  
   857  	if val == "default" {
   858  		es = append(es, fmt.Errorf("%q must not be default", k))
   859  	}
   860  	if len(val) > 255 {
   861  		es = append(es, fmt.Errorf("%q must not be longer than 255 characters", k))
   862  	}
   863  	if !regexp.MustCompile(`^[a-zA-Z0-9. _-]+$`).MatchString(val) {
   864  		es = append(es, fmt.Errorf("%q must only contain alphanumeric characters, periods, spaces, underscores and hyphens", k))
   865  	}
   866  
   867  	return
   868  }
   869  
   870  func validateDmsReplicationTaskId(v interface{}, k string) (ws []string, es []error) {
   871  	val := v.(string)
   872  
   873  	if len(val) > 255 {
   874  		es = append(es, fmt.Errorf("%q must not be longer than 255 characters", k))
   875  	}
   876  	if !regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9-]+$").MatchString(val) {
   877  		es = append(es, fmt.Errorf("%q must start with a letter, only contain alphanumeric characters and hyphens", k))
   878  	}
   879  	if strings.Contains(val, "--") {
   880  		es = append(es, fmt.Errorf("%q must not contain consecutive hyphens", k))
   881  	}
   882  	if strings.HasSuffix(val, "-") {
   883  		es = append(es, fmt.Errorf("%q must not end in a hyphen", k))
   884  	}
   885  
   886  	return
   887  }
   888  
   889  func validateAppautoscalingScalableDimension(v interface{}, k string) (ws []string, errors []error) {
   890  	value := v.(string)
   891  	dimensions := map[string]bool{
   892  		"ecs:service:DesiredCount":              true,
   893  		"ec2:spot-fleet-request:TargetCapacity": true,
   894  	}
   895  
   896  	if !dimensions[value] {
   897  		errors = append(errors, fmt.Errorf("%q must be a valid scalable dimension value: %q", k, value))
   898  	}
   899  	return
   900  }
   901  
   902  func validateAppautoscalingServiceNamespace(v interface{}, k string) (ws []string, errors []error) {
   903  	value := v.(string)
   904  	namespaces := map[string]bool{
   905  		"ecs": true,
   906  		"ec2": true,
   907  	}
   908  
   909  	if !namespaces[value] {
   910  		errors = append(errors, fmt.Errorf("%q must be a valid service namespace value: %q", k, value))
   911  	}
   912  	return
   913  }
   914  
   915  func validateConfigRuleSourceOwner(v interface{}, k string) (ws []string, errors []error) {
   916  	validOwners := []string{
   917  		"CUSTOM_LAMBDA",
   918  		"AWS",
   919  	}
   920  	owner := v.(string)
   921  	for _, o := range validOwners {
   922  		if owner == o {
   923  			return
   924  		}
   925  	}
   926  	errors = append(errors, fmt.Errorf(
   927  		"%q contains an invalid owner %q. Valid owners are %q.",
   928  		k, owner, validOwners))
   929  	return
   930  }
   931  
   932  func validateConfigExecutionFrequency(v interface{}, k string) (ws []string, errors []error) {
   933  	validFrequencies := []string{
   934  		"One_Hour",
   935  		"Three_Hours",
   936  		"Six_Hours",
   937  		"Twelve_Hours",
   938  		"TwentyFour_Hours",
   939  	}
   940  	frequency := v.(string)
   941  	for _, f := range validFrequencies {
   942  		if frequency == f {
   943  			return
   944  		}
   945  	}
   946  	errors = append(errors, fmt.Errorf(
   947  		"%q contains an invalid frequency %q. Valid frequencies are %q.",
   948  		k, frequency, validFrequencies))
   949  	return
   950  }
   951  
   952  func validateAccountAlias(v interface{}, k string) (ws []string, es []error) {
   953  	val := v.(string)
   954  
   955  	if (len(val) < 3) || (len(val) > 63) {
   956  		es = append(es, fmt.Errorf("%q must contain from 3 to 63 alphanumeric characters or hyphens", k))
   957  	}
   958  	if !regexp.MustCompile("^[a-z0-9][a-z0-9-]+$").MatchString(val) {
   959  		es = append(es, fmt.Errorf("%q must start with an alphanumeric character and only contain lowercase alphanumeric characters and hyphens", k))
   960  	}
   961  	if strings.Contains(val, "--") {
   962  		es = append(es, fmt.Errorf("%q must not contain consecutive hyphens", k))
   963  	}
   964  	if strings.HasSuffix(val, "-") {
   965  		es = append(es, fmt.Errorf("%q must not end in a hyphen", k))
   966  	}
   967  	return
   968  }
   969  
   970  func validateApiGatewayApiKeyValue(v interface{}, k string) (ws []string, errors []error) {
   971  	value := v.(string)
   972  	if len(value) < 30 {
   973  		errors = append(errors, fmt.Errorf(
   974  			"%q must be at least 30 characters long", k))
   975  	}
   976  	if len(value) > 128 {
   977  		errors = append(errors, fmt.Errorf(
   978  			"%q cannot be longer than 128 characters", k))
   979  	}
   980  	return
   981  }
   982  
   983  func validateIamRolePolicyName(v interface{}, k string) (ws []string, errors []error) {
   984  	// https://github.com/boto/botocore/blob/2485f5c/botocore/data/iam/2010-05-08/service-2.json#L8291-L8296
   985  	value := v.(string)
   986  	if len(value) > 128 {
   987  		errors = append(errors, fmt.Errorf(
   988  			"%q cannot be longer than 128 characters", k))
   989  	}
   990  	if !regexp.MustCompile("^[\\w+=,.@-]+$").MatchString(value) {
   991  		errors = append(errors, fmt.Errorf("%q must match [\\w+=,.@-]", k))
   992  	}
   993  	return
   994  }
   995  
   996  func validateIamRolePolicyNamePrefix(v interface{}, k string) (ws []string, errors []error) {
   997  	value := v.(string)
   998  	if len(value) > 100 {
   999  		errors = append(errors, fmt.Errorf(
  1000  			"%q cannot be longer than 100 characters", k))
  1001  	}
  1002  	if !regexp.MustCompile("^[\\w+=,.@-]+$").MatchString(value) {
  1003  		errors = append(errors, fmt.Errorf("%q must match [\\w+=,.@-]", k))
  1004  	}
  1005  	return
  1006  }
  1007  
  1008  func validateApiGatewayUsagePlanQuotaSettingsPeriod(v interface{}, k string) (ws []string, errors []error) {
  1009  	validPeriods := []string{
  1010  		apigateway.QuotaPeriodTypeDay,
  1011  		apigateway.QuotaPeriodTypeWeek,
  1012  		apigateway.QuotaPeriodTypeMonth,
  1013  	}
  1014  	period := v.(string)
  1015  	for _, f := range validPeriods {
  1016  		if period == f {
  1017  			return
  1018  		}
  1019  	}
  1020  	errors = append(errors, fmt.Errorf(
  1021  		"%q contains an invalid period %q. Valid period are %q.",
  1022  		k, period, validPeriods))
  1023  	return
  1024  }
  1025  
  1026  func validateApiGatewayUsagePlanQuotaSettings(v map[string]interface{}) (errors []error) {
  1027  	period := v["period"].(string)
  1028  	offset := v["offset"].(int)
  1029  
  1030  	if period == apigateway.QuotaPeriodTypeDay && offset != 0 {
  1031  		errors = append(errors, fmt.Errorf("Usage Plan quota offset must be zero in the DAY period"))
  1032  	}
  1033  
  1034  	if period == apigateway.QuotaPeriodTypeWeek && (offset < 0 || offset > 6) {
  1035  		errors = append(errors, fmt.Errorf("Usage Plan quota offset must be between 0 and 6 inclusive in the WEEK period"))
  1036  	}
  1037  
  1038  	if period == apigateway.QuotaPeriodTypeMonth && (offset < 0 || offset > 27) {
  1039  		errors = append(errors, fmt.Errorf("Usage Plan quota offset must be between 0 and 27 inclusive in the MONTH period"))
  1040  	}
  1041  
  1042  	return
  1043  }