github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/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 (%q) must contain from 1 to 20 alphanumeric characters or hyphens", k, value))
    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 (%q)", k, value))
    44  	}
    45  	if !regexp.MustCompile(`^[a-z]`).MatchString(value) {
    46  		errors = append(errors, fmt.Errorf(
    47  			"first character of %q (%q) must be a letter", k, value))
    48  	}
    49  	if regexp.MustCompile(`--`).MatchString(value) {
    50  		errors = append(errors, fmt.Errorf(
    51  			"%q (%q) cannot contain two consecutive hyphens", k, value))
    52  	}
    53  	if regexp.MustCompile(`-$`).MatchString(value) {
    54  		errors = append(errors, fmt.Errorf(
    55  			"%q (%q) cannot end with a hyphen", k, value))
    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  	validTimeFormatConsolidated := "^(" + validTimeFormat + "-" + validTimeFormat + "|)$"
   645  
   646  	value := strings.ToLower(v.(string))
   647  	if !regexp.MustCompile(validTimeFormatConsolidated).MatchString(value) {
   648  		errors = append(errors, fmt.Errorf(
   649  			"%q must satisfy the format of \"ddd:hh24:mi-ddd:hh24:mi\".", k))
   650  	}
   651  	return
   652  }
   653  
   654  func validateOnceADayWindowFormat(v interface{}, k string) (ws []string, errors []error) {
   655  	// valid time format is "hh24:mi"
   656  	validTimeFormat := "([0-1][0-9]|2[0-3]):([0-5][0-9])"
   657  	validTimeFormatConsolidated := "^(" + validTimeFormat + "-" + validTimeFormat + "|)$"
   658  
   659  	value := v.(string)
   660  	if !regexp.MustCompile(validTimeFormatConsolidated).MatchString(value) {
   661  		errors = append(errors, fmt.Errorf(
   662  			"%q must satisfy the format of \"hh24:mi-hh24:mi\".", k))
   663  	}
   664  	return
   665  }
   666  
   667  func validateRoute53RecordType(v interface{}, k string) (ws []string, errors []error) {
   668  	// Valid Record types
   669  	// SOA, A, TXT, NS, CNAME, MX, NAPTR, PTR, SRV, SPF, AAAA
   670  	validTypes := map[string]struct{}{
   671  		"SOA":   {},
   672  		"A":     {},
   673  		"TXT":   {},
   674  		"NS":    {},
   675  		"CNAME": {},
   676  		"MX":    {},
   677  		"NAPTR": {},
   678  		"PTR":   {},
   679  		"SRV":   {},
   680  		"SPF":   {},
   681  		"AAAA":  {},
   682  	}
   683  
   684  	value := v.(string)
   685  	if _, ok := validTypes[value]; !ok {
   686  		errors = append(errors, fmt.Errorf(
   687  			"%q must be one of [SOA, A, TXT, NS, CNAME, MX, NAPTR, PTR, SRV, SPF, AAAA]", k))
   688  	}
   689  	return
   690  }
   691  
   692  // Validates that ECS Placement Constraints are set correctly
   693  // Takes type, and expression as strings
   694  func validateAwsEcsPlacementConstraint(constType, constExpr string) error {
   695  	switch constType {
   696  	case "distinctInstance":
   697  		// Expression can be nil for distinctInstance
   698  		return nil
   699  	case "memberOf":
   700  		if constExpr == "" {
   701  			return fmt.Errorf("Expression cannot be nil for 'memberOf' type")
   702  		}
   703  	default:
   704  		return fmt.Errorf("Unknown type provided: %q", constType)
   705  	}
   706  	return nil
   707  }
   708  
   709  // Validates that an Ecs placement strategy is set correctly
   710  // Takes type, and field as strings
   711  func validateAwsEcsPlacementStrategy(stratType, stratField string) error {
   712  	switch stratType {
   713  	case "random":
   714  		// random does not need the field attribute set, could error, but it isn't read at the API level
   715  		return nil
   716  	case "spread":
   717  		//  For the spread placement strategy, valid values are instanceId
   718  		// (or host, which has the same effect), or any platform or custom attribute
   719  		// that is applied to a container instance
   720  		// stratField is already cased to a string
   721  		return nil
   722  	case "binpack":
   723  		if stratField != "cpu" && stratField != "memory" {
   724  			return fmt.Errorf("Binpack type requires the field attribute to be either 'cpu' or 'memory'. Got: %s",
   725  				stratField)
   726  		}
   727  	default:
   728  		return fmt.Errorf("Unknown type %s. Must be one of 'random', 'spread', or 'binpack'.", stratType)
   729  	}
   730  	return nil
   731  }
   732  
   733  func validateAwsEmrEbsVolumeType(v interface{}, k string) (ws []string, errors []error) {
   734  	validTypes := map[string]struct{}{
   735  		"gp2":      {},
   736  		"io1":      {},
   737  		"standard": {},
   738  	}
   739  
   740  	value := v.(string)
   741  
   742  	if _, ok := validTypes[value]; !ok {
   743  		errors = append(errors, fmt.Errorf(
   744  			"%q must be one of ['gp2', 'io1', 'standard']", k))
   745  	}
   746  	return
   747  }
   748  
   749  func validateSfnActivityName(v interface{}, k string) (ws []string, errors []error) {
   750  	value := v.(string)
   751  	if len(value) > 80 {
   752  		errors = append(errors, fmt.Errorf("%q cannot be longer than 80 characters", k))
   753  	}
   754  
   755  	return
   756  }
   757  
   758  func validateSfnStateMachineDefinition(v interface{}, k string) (ws []string, errors []error) {
   759  	value := v.(string)
   760  	if len(value) > 1048576 {
   761  		errors = append(errors, fmt.Errorf("%q cannot be longer than 1048576 characters", k))
   762  	}
   763  	return
   764  }
   765  
   766  func validateSfnStateMachineName(v interface{}, k string) (ws []string, errors []error) {
   767  	value := v.(string)
   768  	if len(value) > 80 {
   769  		errors = append(errors, fmt.Errorf("%q cannot be longer than 80 characters", k))
   770  	}
   771  
   772  	if !regexp.MustCompile(`^[a-zA-Z0-9-_]+$`).MatchString(value) {
   773  		errors = append(errors, fmt.Errorf(
   774  			"%q must be composed with only these characters [a-zA-Z0-9-_]: %v", k, value))
   775  	}
   776  	return
   777  }
   778  
   779  func validateDmsCertificateId(v interface{}, k string) (ws []string, es []error) {
   780  	val := v.(string)
   781  
   782  	if len(val) > 255 {
   783  		es = append(es, fmt.Errorf("%q must not be longer than 255 characters", k))
   784  	}
   785  	if !regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9-]+$").MatchString(val) {
   786  		es = append(es, fmt.Errorf("%q must start with a letter, only contain alphanumeric characters and hyphens", k))
   787  	}
   788  	if strings.Contains(val, "--") {
   789  		es = append(es, fmt.Errorf("%q must not contain consecutive hyphens", k))
   790  	}
   791  	if strings.HasSuffix(val, "-") {
   792  		es = append(es, fmt.Errorf("%q must not end in a hyphen", k))
   793  	}
   794  
   795  	return
   796  }
   797  
   798  func validateDmsEndpointId(v interface{}, k string) (ws []string, es []error) {
   799  	val := v.(string)
   800  
   801  	if len(val) > 255 {
   802  		es = append(es, fmt.Errorf("%q must not be longer than 255 characters", k))
   803  	}
   804  	if !regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9-]+$").MatchString(val) {
   805  		es = append(es, fmt.Errorf("%q must start with a letter, only contain alphanumeric characters and hyphens", k))
   806  	}
   807  	if strings.Contains(val, "--") {
   808  		es = append(es, fmt.Errorf("%q must not contain consecutive hyphens", k))
   809  	}
   810  	if strings.HasSuffix(val, "-") {
   811  		es = append(es, fmt.Errorf("%q must not end in a hyphen", k))
   812  	}
   813  
   814  	return
   815  }
   816  
   817  func validateDmsReplicationInstanceId(v interface{}, k string) (ws []string, es []error) {
   818  	val := v.(string)
   819  
   820  	if len(val) > 63 {
   821  		es = append(es, fmt.Errorf("%q must not be longer than 63 characters", k))
   822  	}
   823  	if !regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9-]+$").MatchString(val) {
   824  		es = append(es, fmt.Errorf("%q must start with a letter, only contain alphanumeric characters and hyphens", k))
   825  	}
   826  	if strings.Contains(val, "--") {
   827  		es = append(es, fmt.Errorf("%q must not contain consecutive hyphens", k))
   828  	}
   829  	if strings.HasSuffix(val, "-") {
   830  		es = append(es, fmt.Errorf("%q must not end in a hyphen", k))
   831  	}
   832  
   833  	return
   834  }
   835  
   836  func validateDmsReplicationSubnetGroupId(v interface{}, k string) (ws []string, es []error) {
   837  	val := v.(string)
   838  
   839  	if val == "default" {
   840  		es = append(es, fmt.Errorf("%q must not be default", k))
   841  	}
   842  	if len(val) > 255 {
   843  		es = append(es, fmt.Errorf("%q must not be longer than 255 characters", k))
   844  	}
   845  	if !regexp.MustCompile(`^[a-zA-Z0-9. _-]+$`).MatchString(val) {
   846  		es = append(es, fmt.Errorf("%q must only contain alphanumeric characters, periods, spaces, underscores and hyphens", k))
   847  	}
   848  
   849  	return
   850  }
   851  
   852  func validateDmsReplicationTaskId(v interface{}, k string) (ws []string, es []error) {
   853  	val := v.(string)
   854  
   855  	if len(val) > 255 {
   856  		es = append(es, fmt.Errorf("%q must not be longer than 255 characters", k))
   857  	}
   858  	if !regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9-]+$").MatchString(val) {
   859  		es = append(es, fmt.Errorf("%q must start with a letter, only contain alphanumeric characters and hyphens", k))
   860  	}
   861  	if strings.Contains(val, "--") {
   862  		es = append(es, fmt.Errorf("%q must not contain consecutive hyphens", k))
   863  	}
   864  	if strings.HasSuffix(val, "-") {
   865  		es = append(es, fmt.Errorf("%q must not end in a hyphen", k))
   866  	}
   867  
   868  	return
   869  }
   870  
   871  func validateAppautoscalingScalableDimension(v interface{}, k string) (ws []string, errors []error) {
   872  	value := v.(string)
   873  	dimensions := map[string]bool{
   874  		"ecs:service:DesiredCount":              true,
   875  		"ec2:spot-fleet-request:TargetCapacity": true,
   876  	}
   877  
   878  	if !dimensions[value] {
   879  		errors = append(errors, fmt.Errorf("%q must be a valid scalable dimension value: %q", k, value))
   880  	}
   881  	return
   882  }
   883  
   884  func validateAppautoscalingServiceNamespace(v interface{}, k string) (ws []string, errors []error) {
   885  	value := v.(string)
   886  	namespaces := map[string]bool{
   887  		"ecs": true,
   888  		"ec2": true,
   889  	}
   890  
   891  	if !namespaces[value] {
   892  		errors = append(errors, fmt.Errorf("%q must be a valid service namespace value: %q", k, value))
   893  	}
   894  	return
   895  }
   896  
   897  func validateConfigRuleSourceOwner(v interface{}, k string) (ws []string, errors []error) {
   898  	validOwners := []string{
   899  		"CUSTOM_LAMBDA",
   900  		"AWS",
   901  	}
   902  	owner := v.(string)
   903  	for _, o := range validOwners {
   904  		if owner == o {
   905  			return
   906  		}
   907  	}
   908  	errors = append(errors, fmt.Errorf(
   909  		"%q contains an invalid owner %q. Valid owners are %q.",
   910  		k, owner, validOwners))
   911  	return
   912  }
   913  
   914  func validateConfigExecutionFrequency(v interface{}, k string) (ws []string, errors []error) {
   915  	validFrequencies := []string{
   916  		"One_Hour",
   917  		"Three_Hours",
   918  		"Six_Hours",
   919  		"Twelve_Hours",
   920  		"TwentyFour_Hours",
   921  	}
   922  	frequency := v.(string)
   923  	for _, f := range validFrequencies {
   924  		if frequency == f {
   925  			return
   926  		}
   927  	}
   928  	errors = append(errors, fmt.Errorf(
   929  		"%q contains an invalid freqency %q. Valid frequencies are %q.",
   930  		k, frequency, validFrequencies))
   931  	return
   932  }