Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #6927 from terraform-providers/f/enhanced-location…
…-validation feature: optionally enhanced location validation
- Loading branch information
Showing
7 changed files
with
378 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package features | ||
|
||
import ( | ||
"os" | ||
"strings" | ||
) | ||
|
||
// EnhancedValidationEnabled returns whether or not the feature for Enhanced Validation is | ||
// enabled. | ||
// | ||
// This functionality calls out to the Azure MetaData Service to cache the list of supported | ||
// Azure Locations for the specified Endpoint - and then uses that to provide enhanced validation | ||
// | ||
// This can be enabled using the Environment Variable `ARM_PROVIDER_ENHANCED_VALIDATION` and | ||
// defaults to 'false' at the present time - but may change in a future release. | ||
func EnhancedValidationEnabled() bool { | ||
return strings.EqualFold(os.Getenv("ARM_PROVIDER_ENHANCED_VALIDATION"), "true") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package location | ||
|
||
import ( | ||
"context" | ||
"log" | ||
|
||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/sdk" | ||
) | ||
|
||
// supportedLocations can be (validly) nil - as such this shouldn't be relied on | ||
var supportedLocations *[]string | ||
|
||
// CacheSupportedLocations attempts to retrieve the supported locations from the Azure MetaData Service | ||
// and caches them, for used in enhanced validation | ||
func CacheSupportedLocations(ctx context.Context, endpoint string) { | ||
locs, err := sdk.AvailableAzureLocations(ctx, endpoint) | ||
if err != nil { | ||
log.Printf("[DEBUG] error retrieving locations: %s. Enhanced validation will be unavailable", err) | ||
return | ||
} | ||
|
||
supportedLocations = locs.Locations | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package location | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" | ||
) | ||
|
||
// this is only here to aid testing | ||
var enhancedEnabled = features.EnhancedValidationEnabled() | ||
|
||
// EnhancedValidate returns a validation function which attempts to validate the location | ||
// against the list of Locations supported by this Azure Location. | ||
// | ||
// NOTE: this is best-effort - if the users offline, or the API doesn't return it we'll | ||
// fall back to the original approach | ||
func EnhancedValidate(i interface{}, k string) ([]string, []error) { | ||
if !enhancedEnabled || supportedLocations == nil { | ||
return validate.NoEmptyStrings(i, k) | ||
} | ||
|
||
return enhancedValidation(i, k) | ||
} | ||
|
||
func enhancedValidation(i interface{}, k string) ([]string, []error) { | ||
v, ok := i.(string) | ||
if !ok { | ||
return nil, []error{fmt.Errorf("expected type of %q to be string", k)} | ||
} | ||
|
||
normalizedUserInput := Normalize(v) | ||
if normalizedUserInput == "" { | ||
return nil, []error{fmt.Errorf("%q must not be empty", k)} | ||
} | ||
|
||
// supportedLocations can be nil if the users offline | ||
if supportedLocations != nil { | ||
found := false | ||
for _, loc := range *supportedLocations { | ||
if normalizedUserInput == Normalize(loc) { | ||
found = true | ||
break | ||
} | ||
} | ||
|
||
if !found { | ||
locations := strings.Join(*supportedLocations, ",") | ||
return nil, []error{ | ||
fmt.Errorf("%q was not found in the list of supported Azure Locations: %q", normalizedUserInput, locations), | ||
} | ||
} | ||
} | ||
|
||
return nil, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,211 @@ | ||
package location | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" | ||
) | ||
|
||
func TestEnhancedValidationDisabled(t *testing.T) { | ||
testCases := []struct { | ||
input string | ||
valid bool | ||
}{ | ||
{ | ||
input: "", | ||
valid: false, | ||
}, | ||
{ | ||
input: "chinanorth", | ||
valid: true, | ||
}, | ||
{ | ||
input: "China North", | ||
valid: true, | ||
}, | ||
{ | ||
input: "westeurope", | ||
valid: true, | ||
}, | ||
{ | ||
input: "West Europe", | ||
valid: true, | ||
}, | ||
} | ||
enhancedEnabled = false | ||
defer func() { | ||
enhancedEnabled = features.EnhancedValidationEnabled() | ||
}() | ||
|
||
for _, testCase := range testCases { | ||
t.Logf("Testing %q..", testCase.input) | ||
|
||
warnings, errors := EnhancedValidate(testCase.input, "location") | ||
valid := len(warnings) == 0 && len(errors) == 0 | ||
if testCase.valid != valid { | ||
t.Errorf("Expected %t but got %t", testCase.valid, valid) | ||
} | ||
} | ||
} | ||
|
||
func TestEnhancedValidationEnabledButIsOffline(t *testing.T) { | ||
testCases := []struct { | ||
input string | ||
valid bool | ||
}{ | ||
{ | ||
input: "", | ||
valid: false, | ||
}, | ||
{ | ||
input: "chinanorth", | ||
valid: true, | ||
}, | ||
{ | ||
input: "China North", | ||
valid: true, | ||
}, | ||
{ | ||
input: "westeurope", | ||
valid: true, | ||
}, | ||
{ | ||
input: "West Europe", | ||
valid: true, | ||
}, | ||
} | ||
enhancedEnabled = true | ||
supportedLocations = nil | ||
defer func() { | ||
enhancedEnabled = features.EnhancedValidationEnabled() | ||
}() | ||
|
||
for _, testCase := range testCases { | ||
t.Logf("Testing %q..", testCase.input) | ||
|
||
warnings, errors := EnhancedValidate(testCase.input, "location") | ||
valid := len(warnings) == 0 && len(errors) == 0 | ||
if testCase.valid != valid { | ||
t.Logf("Expected %t but got %t", testCase.valid, valid) | ||
t.Fail() | ||
} | ||
} | ||
} | ||
|
||
func TestEnhancedValidationEnabled(t *testing.T) { | ||
testCases := []struct { | ||
availableLocations []string | ||
input string | ||
valid bool | ||
}{ | ||
{ | ||
availableLocations: publicLocations, | ||
input: "", | ||
valid: false, | ||
}, | ||
{ | ||
availableLocations: publicLocations, | ||
input: "chinanorth", | ||
valid: false, | ||
}, | ||
{ | ||
availableLocations: publicLocations, | ||
input: "China North", | ||
valid: false, | ||
}, | ||
{ | ||
availableLocations: publicLocations, | ||
input: "westeurope", | ||
valid: true, | ||
}, | ||
{ | ||
availableLocations: publicLocations, | ||
input: "West Europe", | ||
valid: true, | ||
}, | ||
{ | ||
availableLocations: chinaLocations, | ||
input: "chinanorth", | ||
valid: true, | ||
}, | ||
{ | ||
availableLocations: chinaLocations, | ||
input: "China North", | ||
valid: true, | ||
}, | ||
{ | ||
availableLocations: chinaLocations, | ||
input: "westeurope", | ||
valid: false, | ||
}, | ||
{ | ||
availableLocations: chinaLocations, | ||
input: "West Europe", | ||
valid: false, | ||
}, | ||
} | ||
enhancedEnabled = true | ||
defer func() { | ||
enhancedEnabled = features.EnhancedValidationEnabled() | ||
supportedLocations = nil | ||
}() | ||
|
||
for _, testCase := range testCases { | ||
t.Logf("Testing %q..", testCase.input) | ||
supportedLocations = &testCase.availableLocations | ||
|
||
warnings, errors := EnhancedValidate(testCase.input, "location") | ||
valid := len(warnings) == 0 && len(errors) == 0 | ||
if testCase.valid != valid { | ||
t.Logf("Expected %t but got %t", testCase.valid, valid) | ||
t.Fail() | ||
} | ||
} | ||
} | ||
|
||
var chinaLocations = []string{"chinaeast", "chinanorth", "chinanorth2", "chinaeast2"} | ||
var publicLocations = []string{ | ||
"westus", | ||
"westus2", | ||
"eastus", | ||
"centralus", | ||
"southcentralus", | ||
"northcentralus", | ||
"westcentralus", | ||
"eastus2", | ||
"brazilsouth", | ||
"brazilus", | ||
"northeurope", | ||
"westeurope", | ||
"eastasia", | ||
"southeastasia", | ||
"japanwest", | ||
"japaneast", | ||
"koreacentral", | ||
"koreasouth", | ||
"indiasouth", | ||
"indiawest", | ||
"indiacentral", | ||
"australiaeast", | ||
"australiasoutheast", | ||
"canadacentral", | ||
"canadaeast", | ||
"uknorth", | ||
"uksouth2", | ||
"uksouth", | ||
"ukwest", | ||
"francecentral", | ||
"francesouth", | ||
"australiacentral", | ||
"australiacentral2", | ||
"uaecentral", | ||
"uaenorth", | ||
"southafricanorth", | ||
"southafricawest", | ||
"switzerlandnorth", | ||
"switzerlandwest", | ||
"germanynorth", | ||
"germanywestcentral", | ||
"norwayeast", | ||
"norwaywest", | ||
} |
Oops, something went wrong.