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  }