github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/provider/azure/config.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package azure 5 6 import ( 7 "strings" 8 9 "github.com/Azure/azure-sdk-for-go/arm/storage" 10 "github.com/juju/errors" 11 "github.com/juju/schema" 12 "gopkg.in/juju/names.v2" 13 14 "github.com/juju/juju/environs/config" 15 ) 16 17 const ( 18 configAttrStorageAccountType = "storage-account-type" 19 20 // The below bits are internal book-keeping things, rather than 21 // configuration. Config is just what we have to work with. 22 23 // resourceNameLengthMax is the maximum length of resource 24 // names in Azure. 25 resourceNameLengthMax = 80 26 ) 27 28 var configFields = schema.Fields{ 29 configAttrStorageAccountType: schema.String(), 30 } 31 32 var configDefaults = schema.Defaults{ 33 configAttrStorageAccountType: string(storage.StandardLRS), 34 } 35 36 var immutableConfigAttributes = []string{ 37 configAttrStorageAccountType, 38 } 39 40 type azureModelConfig struct { 41 *config.Config 42 storageAccountType string 43 } 44 45 var knownStorageAccountTypes = []string{ 46 "Standard_LRS", "Standard_GRS", "Standard_RAGRS", "Standard_ZRS", "Premium_LRS", 47 } 48 49 // Validate ensures that the provided configuration is valid for this 50 // provider, and that changes between the old (if provided) and new 51 // configurations are valid. 52 func (*azureEnvironProvider) Validate(newCfg, oldCfg *config.Config) (*config.Config, error) { 53 _, err := validateConfig(newCfg, oldCfg) 54 if err != nil { 55 return nil, errors.Trace(err) 56 } 57 return newCfg, nil 58 } 59 60 func validateConfig(newCfg, oldCfg *config.Config) (*azureModelConfig, error) { 61 err := config.Validate(newCfg, oldCfg) 62 if err != nil { 63 return nil, err 64 } 65 66 validated, err := newCfg.ValidateUnknownAttrs(configFields, configDefaults) 67 if err != nil { 68 return nil, err 69 } 70 71 if oldCfg != nil { 72 // Ensure immutable configuration isn't changed. 73 oldUnknownAttrs := oldCfg.UnknownAttrs() 74 for _, key := range immutableConfigAttributes { 75 oldValue, hadValue := oldUnknownAttrs[key].(string) 76 if hadValue { 77 newValue, haveValue := validated[key].(string) 78 if !haveValue { 79 return nil, errors.Errorf( 80 "cannot remove immutable %q config", key, 81 ) 82 } 83 if newValue != oldValue { 84 return nil, errors.Errorf( 85 "cannot change immutable %q config (%v -> %v)", 86 key, oldValue, newValue, 87 ) 88 } 89 } 90 // It's valid to go from not having to having. 91 } 92 } 93 94 // Resource group names must not exceed 80 characters. Resource group 95 // names are based on the model UUID and model name, the latter of 96 // which the model creator controls. 97 modelTag := names.NewModelTag(newCfg.UUID()) 98 resourceGroup := resourceGroupName(modelTag, newCfg.Name()) 99 if n := len(resourceGroup); n > resourceNameLengthMax { 100 smallestResourceGroup := resourceGroupName(modelTag, "") 101 return nil, errors.Errorf(`resource group name %q is too long 102 103 Please choose a model name of no more than %d characters.`, 104 resourceGroup, 105 resourceNameLengthMax-len(smallestResourceGroup), 106 ) 107 } 108 109 if newCfg.FirewallMode() == config.FwGlobal { 110 // We do not currently support the "global" firewall mode. 111 return nil, errNoFwGlobal 112 } 113 114 storageAccountType := validated[configAttrStorageAccountType].(string) 115 if !isKnownStorageAccountType(storageAccountType) { 116 return nil, errors.Errorf( 117 "invalid storage account type %q, expected one of: %q", 118 storageAccountType, knownStorageAccountTypes, 119 ) 120 } 121 122 azureConfig := &azureModelConfig{ 123 newCfg, 124 storageAccountType, 125 } 126 return azureConfig, nil 127 } 128 129 // isKnownStorageAccountType reports whether or not the given string identifies 130 // a known storage account type. 131 func isKnownStorageAccountType(t string) bool { 132 for _, knownStorageAccountType := range knownStorageAccountTypes { 133 if t == knownStorageAccountType { 134 return true 135 } 136 } 137 return false 138 } 139 140 // canonicalLocation returns the canonicalized location string. This involves 141 // stripping whitespace, and lowercasing. The ARM APIs do not support embedded 142 // whitespace, whereas the old Service Management APIs used to; we allow the 143 // user to provide either, and canonicalize them to one form that ARM allows. 144 func canonicalLocation(s string) string { 145 s = strings.Replace(s, " ", "", -1) 146 return strings.ToLower(s) 147 }