github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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/services/storage/mgmt/2018-07-01/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  		return nil, errors.New("global firewall mode is not supported")
   111  	}
   112  
   113  	storageAccountType := validated[configAttrStorageAccountType].(string)
   114  	if !isKnownStorageAccountType(storageAccountType) {
   115  		return nil, errors.Errorf(
   116  			"invalid storage account type %q, expected one of: %q",
   117  			storageAccountType, knownStorageAccountTypes,
   118  		)
   119  	}
   120  
   121  	azureConfig := &azureModelConfig{
   122  		newCfg,
   123  		storageAccountType,
   124  	}
   125  	return azureConfig, nil
   126  }
   127  
   128  // isKnownStorageAccountType reports whether or not the given string identifies
   129  // a known storage account type.
   130  func isKnownStorageAccountType(t string) bool {
   131  	for _, knownStorageAccountType := range knownStorageAccountTypes {
   132  		if t == knownStorageAccountType {
   133  			return true
   134  		}
   135  	}
   136  	return false
   137  }
   138  
   139  // canonicalLocation returns the canonicalized location string. This involves
   140  // stripping whitespace, and lowercasing. The ARM APIs do not support embedded
   141  // whitespace, whereas the old Service Management APIs used to; we allow the
   142  // user to provide either, and canonicalize them to one form that ARM allows.
   143  func canonicalLocation(s string) string {
   144  	s = strings.Replace(s, " ", "", -1)
   145  	return strings.ToLower(s)
   146  }