From 8abec085ec0af0319d561e5328fd0ac285c2244f Mon Sep 17 00:00:00 2001 From: Peter McAtominey Date: Wed, 10 Aug 2016 13:14:25 +0100 Subject: [PATCH 1/2] helper: create validation package to provide common validation functions --- helper/validation/validation.go | 49 +++++++++++++++ helper/validation/validation_test.go | 91 ++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 helper/validation/validation.go create mode 100644 helper/validation/validation_test.go diff --git a/helper/validation/validation.go b/helper/validation/validation.go new file mode 100644 index 000000000..484f7d7da --- /dev/null +++ b/helper/validation/validation.go @@ -0,0 +1,49 @@ +package validation + +import ( + "fmt" + "strings" + + "github.com/hashicorp/terraform/helper/schema" +) + +// IntBetween returns a SchemaValidateFunc which tests if the provided value +// is of type int and is between min and max (inclusive) +func IntBetween(min, max int) schema.SchemaValidateFunc { + return func(i interface{}, k string) (s []string, es []error) { + v, ok := i.(int) + if !ok { + es = append(es, fmt.Errorf("expected type of %s to be int", k)) + return + } + + if v < min || v > max { + es = append(es, fmt.Errorf("expected %s to be in the range (%d - %d), got %d", k, min, max, v)) + return + } + + return + } +} + +// StringInSlice returns a SchemaValidateFunc which tests if the provided value +// is of type string and matches the value of an element in the valid slice +// will test with in lower case if ignoreCase is true +func StringInSlice(valid []string, ignoreCase bool) schema.SchemaValidateFunc { + return func(i interface{}, k string) (s []string, es []error) { + v, ok := i.(string) + if !ok { + es = append(es, fmt.Errorf("expected type of %s to be string", k)) + return + } + + for _, str := range valid { + if v == str || (ignoreCase && strings.ToLower(v) == strings.ToLower(str)) { + return + } + } + + es = append(es, fmt.Errorf("expected %s to be one of %v, got %s", k, valid, v)) + return + } +} diff --git a/helper/validation/validation_test.go b/helper/validation/validation_test.go new file mode 100644 index 000000000..b6ff5e5d9 --- /dev/null +++ b/helper/validation/validation_test.go @@ -0,0 +1,91 @@ +package validation + +import ( + "regexp" + "testing" + + "github.com/hashicorp/terraform/helper/schema" +) + +type testCase struct { + val interface{} + f schema.SchemaValidateFunc + expectedErr *regexp.Regexp +} + +func TestValidationIntBetween(t *testing.T) { + runTestCases(t, []testCase{ + { + val: 1, + f: IntBetween(1, 1), + }, + { + val: 1, + f: IntBetween(0, 2), + }, + { + val: 1, + f: IntBetween(2, 3), + expectedErr: regexp.MustCompile("expected [\\w]+ to be in the range \\(2 - 3\\), got 1"), + }, + { + val: "1", + f: IntBetween(2, 3), + expectedErr: regexp.MustCompile("expected type of [\\w]+ to be int"), + }, + }) +} + +func TestValidationSringInSlice(t *testing.T) { + runTestCases(t, []testCase{ + { + val: "ValidValue", + f: StringInSlice([]string{"ValidValue", "AnotherValidValue"}, false), + }, + // ignore case + { + val: "VALIDVALUE", + f: StringInSlice([]string{"ValidValue", "AnotherValidValue"}, true), + }, + { + val: "VALIDVALUE", + f: StringInSlice([]string{"ValidValue", "AnotherValidValue"}, false), + expectedErr: regexp.MustCompile("expected [\\w]+ to be one of \\[ValidValue AnotherValidValue\\], got VALIDVALUE"), + }, + { + val: "InvalidValue", + f: StringInSlice([]string{"ValidValue", "AnotherValidValue"}, false), + expectedErr: regexp.MustCompile("expected [\\w]+ to be one of \\[ValidValue AnotherValidValue\\], got InvalidValue"), + }, + { + val: 1, + f: StringInSlice([]string{"ValidValue", "AnotherValidValue"}, false), + expectedErr: regexp.MustCompile("expected type of [\\w]+ to be string"), + }, + }) +} + +func runTestCases(t *testing.T, cases []testCase) { + matchErr := func(errs []error, r *regexp.Regexp) bool { + // err must match one provided + for _, err := range errs { + if r.MatchString(err.Error()) { + return true + } + } + + return false + } + + for i, tc := range cases { + _, errs := tc.f(tc.val, "test_property") + + if len(errs) == 0 && tc.expectedErr == nil { + continue + } + + if !matchErr(errs, tc.expectedErr) { + t.Fatalf("expected test case %d to produce error matching \"%s\", got %v", i, tc.expectedErr, errs) + } + } +} From 012fd9825f65b39b2459c2894b351f32f19db068 Mon Sep 17 00:00:00 2001 From: Peter McAtominey Date: Wed, 10 Aug 2016 13:32:35 +0100 Subject: [PATCH 2/2] provider/azurerm: refactor traffic_manager_ resources to use validation helpers Updated: traffic_manager_profile traffic_manager_endpoint --- .../resource_arm_traffic_manager_endpoint.go | 36 ++---------- .../resource_arm_traffic_manager_profile.go | 56 ++----------------- 2 files changed, 10 insertions(+), 82 deletions(-) diff --git a/builtin/providers/azurerm/resource_arm_traffic_manager_endpoint.go b/builtin/providers/azurerm/resource_arm_traffic_manager_endpoint.go index 922db271f..071cd8bc9 100644 --- a/builtin/providers/azurerm/resource_arm_traffic_manager_endpoint.go +++ b/builtin/providers/azurerm/resource_arm_traffic_manager_endpoint.go @@ -7,6 +7,7 @@ import ( "github.com/Azure/azure-sdk-for-go/arm/trafficmanager" "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" ) func resourceArmTrafficManagerEndpoint() *schema.Resource { @@ -30,7 +31,7 @@ func resourceArmTrafficManagerEndpoint() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: validateAzureRMTrafficManagerEndpointType, + ValidateFunc: validation.StringInSlice([]string{"azureEndpoints", "nestedEndpoints", "externalEndpoints"}, false), }, "profile_name": { @@ -61,14 +62,14 @@ func resourceArmTrafficManagerEndpoint() *schema.Resource { Type: schema.TypeInt, Optional: true, Computed: true, - ValidateFunc: validateAzureRMTrafficManagerEndpointWeight, + ValidateFunc: validation.IntBetween(1, 1000), }, "priority": { Type: schema.TypeInt, Optional: true, Computed: true, - ValidateFunc: validateAzureRMTrafficManagerEndpointPriority, + ValidateFunc: validation.IntBetween(1, 1000), }, "endpoint_location": { @@ -220,32 +221,3 @@ func getArmTrafficManagerEndpointProperties(d *schema.ResourceData) *trafficmana return &endpointProps } - -func validateAzureRMTrafficManagerEndpointType(i interface{}, k string) (s []string, errors []error) { - valid := map[string]struct{}{ - "azureEndpoints": struct{}{}, - "externalEndpoints": struct{}{}, - "nestedEndpoints": struct{}{}, - } - - if _, ok := valid[i.(string)]; !ok { - errors = append(errors, fmt.Errorf("endpoint type invalid, got %s", i.(string))) - } - return -} - -func validateAzureRMTrafficManagerEndpointWeight(i interface{}, k string) (s []string, errors []error) { - w := i.(int) - if w < 1 || w > 1000 { - errors = append(errors, fmt.Errorf("endpoint weight must be between 1-1000 inclusive")) - } - return -} - -func validateAzureRMTrafficManagerEndpointPriority(i interface{}, k string) (s []string, errors []error) { - p := i.(int) - if p < 1 || p > 1000 { - errors = append(errors, fmt.Errorf("endpoint priority must be between 1-1000 inclusive")) - } - return -} diff --git a/builtin/providers/azurerm/resource_arm_traffic_manager_profile.go b/builtin/providers/azurerm/resource_arm_traffic_manager_profile.go index 53013631f..21438f9d2 100644 --- a/builtin/providers/azurerm/resource_arm_traffic_manager_profile.go +++ b/builtin/providers/azurerm/resource_arm_traffic_manager_profile.go @@ -10,6 +10,7 @@ import ( "github.com/Azure/azure-sdk-for-go/arm/trafficmanager" "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" ) func resourceArmTrafficManagerProfile() *schema.Resource { @@ -33,13 +34,13 @@ func resourceArmTrafficManagerProfile() *schema.Resource { Type: schema.TypeString, Optional: true, Computed: true, - ValidateFunc: validateAzureRMTrafficManagerStatus, + ValidateFunc: validation.StringInSlice([]string{"Enabled", "Disabled"}, true), }, "traffic_routing_method": { Type: schema.TypeString, Required: true, - ValidateFunc: validateAzureRMTrafficManagerRoutingMethod, + ValidateFunc: validation.StringInSlice([]string{"Performance", "Weighted", "Priority"}, false), }, "dns_config": { @@ -55,7 +56,7 @@ func resourceArmTrafficManagerProfile() *schema.Resource { "ttl": { Type: schema.TypeInt, Required: true, - ValidateFunc: validateAzureRMTrafficManagerTTL, + ValidateFunc: validation.IntBetween(30, 999999), }, }, }, @@ -76,12 +77,12 @@ func resourceArmTrafficManagerProfile() *schema.Resource { "protocol": { Type: schema.TypeString, Required: true, - ValidateFunc: validateAzureRMTrafficManagerMonitorProtocol, + ValidateFunc: validation.StringInSlice([]string{"http", "https"}, false), }, "port": { Type: schema.TypeInt, Required: true, - ValidateFunc: validateAzureRMTrafficManagerMonitorPort, + ValidateFunc: validation.IntBetween(1, 65535), }, "path": { Type: schema.TypeString, @@ -276,48 +277,3 @@ func resourceAzureRMTrafficManagerMonitorConfigHash(v interface{}) int { return hashcode.String(buf.String()) } - -func validateAzureRMTrafficManagerStatus(i interface{}, k string) (s []string, errors []error) { - status := strings.ToLower(i.(string)) - if status != "enabled" && status != "disabled" { - errors = append(errors, fmt.Errorf("%s must be one of: Enabled, Disabled", k)) - } - return -} - -func validateAzureRMTrafficManagerRoutingMethod(i interface{}, k string) (s []string, errors []error) { - valid := map[string]struct{}{ - "Performance": struct{}{}, - "Weighted": struct{}{}, - "Priority": struct{}{}, - } - - if _, ok := valid[i.(string)]; !ok { - errors = append(errors, fmt.Errorf("traffic_routing_method must be one of (Performance, Weighted, Priority), got %s", i.(string))) - } - return -} - -func validateAzureRMTrafficManagerTTL(i interface{}, k string) (s []string, errors []error) { - ttl := i.(int) - if ttl < 30 || ttl > 999999 { - errors = append(errors, fmt.Errorf("ttl must be between 30 and 999,999 inclusive")) - } - return -} - -func validateAzureRMTrafficManagerMonitorProtocol(i interface{}, k string) (s []string, errors []error) { - p := i.(string) - if p != "http" && p != "https" { - errors = append(errors, fmt.Errorf("monitor_config.protocol must be one of: http, https")) - } - return -} - -func validateAzureRMTrafficManagerMonitorPort(i interface{}, k string) (s []string, errors []error) { - p := i.(int) - if p < 1 || p > 65535 { - errors = append(errors, fmt.Errorf("monitor_config.port must be between 1 - 65535 inclusive")) - } - return -}