Skip to content

Commit

Permalink
Merge pull request #2398 from terraform-providers/b-mssql-validation
Browse files Browse the repository at this point in the history
Updated MS SQL resource name validation
  • Loading branch information
katbyte authored Nov 28, 2018
2 parents d31061c + 696f37f commit a8710af
Show file tree
Hide file tree
Showing 14 changed files with 335 additions and 142 deletions.
36 changes: 36 additions & 0 deletions azurerm/helpers/azure/mssql.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package azure

import (
"fmt"

"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate"
)

//Your server name can contain only lowercase letters, numbers, and '-', but can't start or end with '-' or have more than 63 characters.
func ValidateMsSqlServerName(i interface{}, k string) (_ []string, errors []error) {
if m, regexErrs := validate.RegExHelper(i, k, `^[0-9a-z]([-0-9a-z]{0,61}[0-9a-z])?$`); !m {
errors = append(regexErrs, fmt.Errorf("%q can contain only lowercase letters, numbers, and '-', but can't start or end with '-' or have more than 63 characters.", k))
}

return nil, errors
}

//Your database name can't end with '.' or ' ', can't contain '<,>,*,%,&,:,\,/,?' or control characters, and can't have more than 128 characters.
func ValidateMsSqlDatabaseName(i interface{}, k string) (_ []string, errors []error) {
if m, regexErrs := validate.RegExHelper(i, k, `^[^<>*%&:\\\/?]{0,127}[^\s.<>*%&:\\\/?]$`); !m {
errors = append(regexErrs, fmt.Errorf(`%q can't end with '.' or ' ', can't contain '<,>,*,%%,&,:,\,/,?' or control characters, and can't have more than 128 characters.`, k))
}

return nil, errors
}

//Following characters and any control characters are not allowed for resource name '%,&,\\\\,?,/'.\"
//The name can not end with characters: '. '
//TODO: unsure about length, was able to deploy one at 120
func ValidateMsSqlElasticPoolName(i interface{}, k string) (_ []string, errors []error) {
if m, regexErrs := validate.RegExHelper(i, k, `^[^&%\\\/?]{0,127}[^\s.&%\\\/?]$`); !m {
errors = append(regexErrs, fmt.Errorf(`%q can't end with '.' or ' ', can't contain '%%,&,\,/,?' or control characters, and can't have more than 128 characters.`, k))
}

return nil, errors
}
215 changes: 215 additions & 0 deletions azurerm/helpers/azure/mssql_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
package azure

import "testing"

//Your server name can contain only lowercase letters, numbers, and '-', but can't start or end with '-' or have more than 63 characters.
func TestValidateMsSqlServerName(t *testing.T) {
cases := []struct {
Value string
Errors bool
}{
{
Value: "",
Errors: true,
},
{
Value: "k",
Errors: false,
},
{
Value: "K",
Errors: true,
},
{
Value: "k-",
Errors: true,
},
{
Value: "k-t",
Errors: false,
},
{
Value: "K-T",
Errors: true,
},
{
Value: "validname",
Errors: false,
},
{
Value: "invalid_name",
Errors: true,
},
{
Value: "123456789112345678921234567893123456789412345678951234567896123",
Errors: false,
},
{
Value: "01234567891123456789212345678931234567894123456789512345678961234",
Errors: true,
},
}

for _, tc := range cases {
_, errors := ValidateMsSqlServerName(tc.Value, "name")

if len(errors) > 0 != tc.Errors {
if tc.Errors {
t.Fatalf("Expected ValidateMsSqlServerName to have errors for '%s', got %d ", tc.Value, len(errors))
} else {
t.Fatalf("Expected ValidateMsSqlServerName to not have errors for '%s', got %d ", tc.Value, len(errors))
}
}
}
}

//Your database name can't end with '.' or ' ', can't contain '<,>,*,%,&,:,\,/,?' or control characters, and can't have more than 128 characters.
func TestValidateMsSqlDatabaseName(t *testing.T) {
cases := []struct {
Value string
Errors bool
}{
{
Value: "",
Errors: true,
},
{
Value: "k",
Errors: false,
},
{
Value: "K",
Errors: false,
},
{
Value: "space ",
Errors: true,
},
{
Value: "dot.",
Errors: true,
},
{
Value: "data_base-name",
Errors: false,
},
{
Value: "ends.with.dash-",
Errors: false,
},
{
Value: "ends.with.underscore_",
Errors: false,
},
{
Value: "fail:semicolon",
Errors: true,
},
{
Value: "fail?question",
Errors: true,
},
{
Value: "fail&ampersand",
Errors: true,
},
{
Value: "fail%percent",
Errors: true,
},
{
Value: "12345678911234567892123456789312345678941234567895123456789612345678971234567898123456789912345678901234567891123456789212345678",
Errors: false,
},
{
Value: "123456789112345678921234567893123456789412345678951234567896123456789712345678981234567899123456789012345678911234567892123456789",
Errors: true,
},
}

for _, tc := range cases {
_, errors := ValidateMsSqlDatabaseName(tc.Value, "name")

if len(errors) > 0 != tc.Errors {
if tc.Errors {
t.Fatalf("Expected ValidateMsSqlDatabaseName to have errors for '%s', got %d ", tc.Value, len(errors))
} else {
t.Fatalf("Expected ValidateMsSqlDatabaseName to not have errors for '%s', got %d ", tc.Value, len(errors))
}
}
}
}

//Following characters and any control characters are not allowed for resource name '%,&,\\\\,?,/'.\"
//The name can not end with characters: '. '
func TestValidateMsSqlElasticPoolName(t *testing.T) {
cases := []struct {
Value string
Errors bool
}{
{
Value: "",
Errors: true,
},
{
Value: "k",
Errors: false,
},
{
Value: "K",
Errors: false,
},
{
Value: "space ",
Errors: true,
},
{
Value: "dot.",
Errors: true,
},
{
Value: "data_base-name",
Errors: false,
},
{
Value: "ends.with.dash-",
Errors: false,
},
{
Value: "ends.with.underscore_",
Errors: false,
},
{
Value: "fail?question",
Errors: true,
},
{
Value: "fail&ampersand",
Errors: true,
},
{
Value: "fail%percent",
Errors: true,
},
{
Value: "12345678911234567892123456789312345678941234567895123456789612345678971234567898123456789912345678901234567891123456789212345678",
Errors: false,
},
{
Value: "123456789112345678921234567893123456789412345678951234567896123456789712345678981234567899123456789012345678911234567892123456789",
Errors: true,
},
}

for _, tc := range cases {
_, errors := ValidateMsSqlElasticPoolName(tc.Value, "name")

if len(errors) > 0 != tc.Errors {
if tc.Errors {
t.Fatalf("Expected ValidateMsSqlServerName to have errors for '%s', got %d ", tc.Value, len(errors))
} else {
t.Fatalf("Expected ValidateMsSqlServerName to not have errors for '%s', got %d ", tc.Value, len(errors))
}
}
}
}
26 changes: 0 additions & 26 deletions azurerm/helpers/azure/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package azure

import (
"fmt"
"regexp"
)

func ValidateResourceID(i interface{}, k string) (warnings []string, errors []error) {
Expand Down Expand Up @@ -33,28 +32,3 @@ func ValidateResourceIDOrEmpty(i interface{}, k string) (_ []string, errors []er

return ValidateResourceID(i, k)
}

//true for a resource ID or an empty string
func ValidateMsSqlServiceName(i interface{}, k string) (_ []string, errors []error) {
v, ok := i.(string)
if !ok {
errors = append(errors, fmt.Errorf("expected type of %q to be string", k))
return nil, errors
}

//First, second, and last characters must be a letter or number with a total length between 3 to 50 lowercase characters.
r := regexp.MustCompile("^[a-z0-9]{2}[-a-z0-9]{0,47}[a-z0-9]{1}$")
if !r.MatchString(v) {
errors = append(errors, fmt.Errorf("%q must be 3 - 50 characters in length", k))
errors = append(errors, fmt.Errorf("%q first, second, and last characters must be a lowercase letter or number", k))
errors = append(errors, fmt.Errorf("%q can only contain lowercase letters, numbers and hyphens", k))
}

//No consecutive dashes.
r = regexp.MustCompile("(--)")
if r.MatchString(v) {
errors = append(errors, fmt.Errorf("%q must not contain any consecutive hyphens", k))
}

return nil, errors
}
58 changes: 0 additions & 58 deletions azurerm/helpers/azure/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,61 +90,3 @@ func TestAzureResourceIDOrEmpty(t *testing.T) {
})
}
}

func TestAzureValidateMsSqlServiceName(t *testing.T) {
cases := []struct {
ServiceName string
Errors int
}{
{
ServiceName: "as",
Errors: 3,
},
{
ServiceName: "Asd",
Errors: 3,
},
{
ServiceName: "asd",
Errors: 0,
},
{
ServiceName: "-asd",
Errors: 3,
},
{
ServiceName: "asd-",
Errors: 3,
},
{
ServiceName: "asd-1",
Errors: 0,
},
{
ServiceName: "asd--1",
Errors: 1,
},
{
ServiceName: "asd--1-",
Errors: 4,
},
{
ServiceName: "asdfghjklzasdfghjklzasdfghjklzasdfghjklzasdfghjklz",
Errors: 0,
},
{
ServiceName: "asdfghjklzasdfghjklzasdfghjklzasdfghjklzasdfghjklz1",
Errors: 3,
},
}

for _, tc := range cases {
t.Run(tc.ServiceName, func(t *testing.T) {
_, errors := ValidateMsSqlServiceName(tc.ServiceName, "name")

if len(errors) < tc.Errors {
t.Fatalf("Expected TestAzureValidateMsSqlServiceName to have %d not %d errors for %q", tc.Errors, len(errors), tc.ServiceName)
}
})
}
}
22 changes: 22 additions & 0 deletions azurerm/helpers/validate/int.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,28 @@ import (
"github.com/hashicorp/terraform/helper/schema"
)

func IntBetweenAndNot(min, max, not int) schema.SchemaValidateFunc {
return func(i interface{}, k string) (_ []string, errors []error) {
v, ok := i.(int)
if !ok {
errors = append(errors, fmt.Errorf("expected type of %q to be int", k))
return
}

if v < min || v > max {
errors = append(errors, fmt.Errorf("expected %s to be in the range (%d - %d), got %d", k, min, max, v))
return
}

if v == not {
errors = append(errors, fmt.Errorf("expected %s to not be %d, got %d", k, not, v))
return
}

return
}
}

// IntBetweenAndDivisibleBy returns a SchemaValidateFunc which tests if the provided value
// is of type int and is between min and max (inclusive) and is divisible by a given number
func IntBetweenAndDivisibleBy(min, max, divisor int) schema.SchemaValidateFunc { // nolint: unparam
Expand Down
Loading

0 comments on commit a8710af

Please sign in to comment.