github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/modelconfig.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/schema"
     9  
    10  	"github.com/juju/juju/controller"
    11  	"github.com/juju/juju/environs"
    12  	"github.com/juju/juju/environs/config"
    13  )
    14  
    15  type attrValues map[string]interface{}
    16  
    17  var disallowedModelConfigAttrs = [...]string{
    18  	"admin-secret",
    19  	"ca-private-key",
    20  }
    21  
    22  // ModelConfig returns the complete config for the model represented
    23  // by this state.
    24  func (st *State) ModelConfig() (*config.Config, error) {
    25  	modelSettings, err := readSettings(st, settingsC, modelGlobalKey)
    26  	if err != nil {
    27  		return nil, errors.Trace(err)
    28  	}
    29  	return config.New(config.NoDefaults, modelSettings.Map())
    30  }
    31  
    32  // checkModelConfig returns an error if the config is definitely invalid.
    33  func checkModelConfig(cfg *config.Config) error {
    34  	allAttrs := cfg.AllAttrs()
    35  	for _, attr := range disallowedModelConfigAttrs {
    36  		if _, ok := allAttrs[attr]; ok {
    37  			return errors.Errorf(attr + " should never be written to the state")
    38  		}
    39  	}
    40  	if _, ok := cfg.AgentVersion(); !ok {
    41  		return errors.Errorf("agent-version must always be set in state")
    42  	}
    43  	for attr := range allAttrs {
    44  		if controller.ControllerOnlyAttribute(attr) {
    45  			return errors.Errorf("cannot set controller attribute %q on a model", attr)
    46  		}
    47  	}
    48  	return nil
    49  }
    50  
    51  // inheritedConfigAttributes returns the merged collection of inherited config
    52  // values used as model defaults when adding models or unsetting values.
    53  func (st *State) inheritedConfigAttributes() (map[string]interface{}, error) {
    54  	rspec, err := st.regionSpec()
    55  	if err != nil {
    56  		return nil, errors.Trace(err)
    57  	}
    58  	configSources := modelConfigSources(st, rspec)
    59  	values := make(attrValues)
    60  	for _, src := range configSources {
    61  		cfg, err := src.sourceFunc()
    62  		if errors.IsNotFound(err) {
    63  			continue
    64  		}
    65  		if err != nil {
    66  			return nil, errors.Annotatef(err, "reading %s settings", src.name)
    67  		}
    68  		for attrName, value := range cfg {
    69  			values[attrName] = value
    70  		}
    71  	}
    72  	return values, nil
    73  }
    74  
    75  // modelConfigValues returns the values and source for the supplied model config
    76  // when combined with controller and Juju defaults.
    77  func (st *State) modelConfigValues(modelCfg attrValues) (config.ConfigValues, error) {
    78  	resultValues := make(attrValues)
    79  	for k, v := range modelCfg {
    80  		resultValues[k] = v
    81  	}
    82  
    83  	// Read all of the current inherited config values so
    84  	// we can dynamically reflect the origin of the model config.
    85  	rspec, err := st.regionSpec()
    86  	if err != nil {
    87  		return nil, errors.Trace(err)
    88  	}
    89  	configSources := modelConfigSources(st, rspec)
    90  	sourceNames := make([]string, 0, len(configSources))
    91  	sourceAttrs := make([]attrValues, 0, len(configSources))
    92  	for _, src := range configSources {
    93  		sourceNames = append(sourceNames, src.name)
    94  		cfg, err := src.sourceFunc()
    95  		if errors.IsNotFound(err) {
    96  			continue
    97  		}
    98  		if err != nil {
    99  			return nil, errors.Annotatef(err, "reading %s settings", src.name)
   100  		}
   101  		sourceAttrs = append(sourceAttrs, cfg)
   102  
   103  		// If no modelCfg was passed in, we'll accumulate data
   104  		// for the inherited values instead.
   105  		if len(modelCfg) == 0 {
   106  			for k, v := range cfg {
   107  				resultValues[k] = v
   108  			}
   109  		}
   110  	}
   111  
   112  	// Figure out the source of each config attribute based
   113  	// on the current model values and the inherited values.
   114  	result := make(config.ConfigValues)
   115  	for attr, val := range resultValues {
   116  		// Find the source of config for which the model
   117  		// value matches. If there's a match, the last match
   118  		// in the search order will be the source of config.
   119  		// If there's no match, the source is the model.
   120  		source := config.JujuModelConfigSource
   121  		n := len(sourceAttrs)
   122  		for i := range sourceAttrs {
   123  			if sourceAttrs[n-i-1][attr] == val {
   124  				source = sourceNames[n-i-1]
   125  				break
   126  			}
   127  		}
   128  		result[attr] = config.ConfigValue{
   129  			Value:  val,
   130  			Source: source,
   131  		}
   132  	}
   133  	return result, nil
   134  }
   135  
   136  // UpdateModelConfigDefaultValues updates the inherited settings used when creating a new model.
   137  func (st *State) UpdateModelConfigDefaultValues(attrs map[string]interface{}, removed []string, regionSpec *environs.RegionSpec) error {
   138  	var key string
   139  	if regionSpec != nil {
   140  		key = regionSettingsGlobalKey(regionSpec.Cloud, regionSpec.Region)
   141  	} else {
   142  		key = controllerInheritedSettingsGlobalKey
   143  	}
   144  	settings, err := readSettings(st, globalSettingsC, key)
   145  	if err != nil {
   146  		return errors.Trace(err)
   147  	}
   148  
   149  	// TODO(axw) 2013-12-6 #1167616
   150  	// Ensure that the settings on disk have not changed
   151  	// underneath us. The settings changes are actually
   152  	// applied as a delta to what's on disk; if there has
   153  	// been a concurrent update, the change may not be what
   154  	// the user asked for.
   155  	settings.Update(attrs)
   156  	for _, r := range removed {
   157  		settings.Delete(r)
   158  	}
   159  	_, err = settings.Write()
   160  	return err
   161  }
   162  
   163  // ModelConfigValues returns the config values for the model represented
   164  // by this state.
   165  func (st *State) ModelConfigValues() (config.ConfigValues, error) {
   166  	cfg, err := st.ModelConfig()
   167  	if err != nil {
   168  		return nil, errors.Trace(err)
   169  	}
   170  	return st.modelConfigValues(cfg.AllAttrs())
   171  }
   172  
   173  // ModelConfigDefaultValues returns the default config values to be used
   174  // when creating a new model, and the origin of those values.
   175  func (st *State) ModelConfigDefaultValues() (config.ModelDefaultAttributes, error) {
   176  	model, err := st.Model()
   177  	if err != nil {
   178  		return nil, errors.Trace(err)
   179  	}
   180  	cloudName := model.Cloud()
   181  	cloud, err := st.Cloud(cloudName)
   182  	if err != nil {
   183  		return nil, errors.Trace(err)
   184  	}
   185  
   186  	result := make(config.ModelDefaultAttributes)
   187  	// Juju defaults
   188  	defaultAttrs, err := st.defaultInheritedConfig()
   189  	if err != nil {
   190  		return nil, errors.Trace(err)
   191  	}
   192  	for k, v := range defaultAttrs {
   193  		result[k] = config.AttributeDefaultValues{Default: v}
   194  	}
   195  	// Controller config
   196  	ciCfg, err := st.controllerInheritedConfig()
   197  	if err != nil && !errors.IsNotFound(err) {
   198  		return nil, errors.Trace(err)
   199  
   200  	}
   201  	for k, v := range ciCfg {
   202  		if ds, ok := result[k]; ok {
   203  			ds.Controller = v
   204  			result[k] = ds
   205  		} else {
   206  			result[k] = config.AttributeDefaultValues{Controller: v}
   207  		}
   208  	}
   209  	// Region config
   210  	for _, region := range cloud.Regions {
   211  		rspec := &environs.RegionSpec{Cloud: cloudName, Region: region.Name}
   212  		riCfg, err := st.regionInheritedConfig(rspec)()
   213  		if err != nil {
   214  			if errors.IsNotFound(err) {
   215  				continue
   216  			}
   217  			return nil, errors.Trace(err)
   218  		}
   219  		for k, v := range riCfg {
   220  			regCfg := config.RegionDefaultValue{Name: region.Name, Value: v}
   221  			if ds, ok := result[k]; ok {
   222  				ds.Regions = append(result[k].Regions, regCfg)
   223  				result[k] = ds
   224  			} else {
   225  				result[k] = config.AttributeDefaultValues{Regions: []config.RegionDefaultValue{regCfg}}
   226  			}
   227  		}
   228  	}
   229  	return result, nil
   230  }
   231  
   232  // checkControllerInheritedConfig returns an error if the shared local cloud config is definitely invalid.
   233  func checkControllerInheritedConfig(attrs attrValues) error {
   234  	disallowedCloudConfigAttrs := append(disallowedModelConfigAttrs[:], config.AgentVersionKey)
   235  	for _, attr := range disallowedCloudConfigAttrs {
   236  		if _, ok := attrs[attr]; ok {
   237  			return errors.Errorf("local cloud config cannot contain " + attr)
   238  		}
   239  	}
   240  	for attrName := range attrs {
   241  		if controller.ControllerOnlyAttribute(attrName) {
   242  			return errors.Errorf("local cloud config cannot contain controller attribute %q", attrName)
   243  		}
   244  	}
   245  	return nil
   246  }
   247  
   248  func (st *State) buildAndValidateModelConfig(updateAttrs attrValues, removeAttrs []string, oldConfig *config.Config) (*config.Config, error) {
   249  	newConfig, err := oldConfig.Apply(updateAttrs)
   250  	if err != nil {
   251  		return nil, errors.Trace(err)
   252  	}
   253  	if len(removeAttrs) != 0 {
   254  		newConfig, err = newConfig.Remove(removeAttrs)
   255  		if err != nil {
   256  			return nil, errors.Trace(err)
   257  		}
   258  	}
   259  	if err := checkModelConfig(newConfig); err != nil {
   260  		return nil, errors.Trace(err)
   261  	}
   262  	return st.validate(newConfig, oldConfig)
   263  }
   264  
   265  type ValidateConfigFunc func(updateAttrs map[string]interface{}, removeAttrs []string, oldConfig *config.Config) error
   266  
   267  // UpdateModelConfig adds, updates or removes attributes in the current
   268  // configuration of the model with the provided updateAttrs and
   269  // removeAttrs.
   270  func (st *State) UpdateModelConfig(updateAttrs map[string]interface{}, removeAttrs []string, additionalValidation ValidateConfigFunc) error {
   271  	if len(updateAttrs)+len(removeAttrs) == 0 {
   272  		return nil
   273  	}
   274  
   275  	if len(removeAttrs) > 0 {
   276  		var removed []string
   277  		if updateAttrs == nil {
   278  			updateAttrs = make(map[string]interface{})
   279  		}
   280  		// For each removed attribute, pick up any inherited value
   281  		// and if there's one, use that.
   282  		inherited, err := st.inheritedConfigAttributes()
   283  		if err != nil {
   284  			return errors.Trace(err)
   285  		}
   286  		for _, attr := range removeAttrs {
   287  			// We we are updating an attribute, that takes
   288  			// precedence over removing.
   289  			if _, ok := updateAttrs[attr]; ok {
   290  				continue
   291  			}
   292  			if val, ok := inherited[attr]; ok {
   293  				updateAttrs[attr] = val
   294  			} else {
   295  				removed = append(removed, attr)
   296  			}
   297  		}
   298  		removeAttrs = removed
   299  	}
   300  	// TODO(axw) 2013-12-6 #1167616
   301  	// Ensure that the settings on disk have not changed
   302  	// underneath us. The settings changes are actually
   303  	// applied as a delta to what's on disk; if there has
   304  	// been a concurrent update, the change may not be what
   305  	// the user asked for.
   306  
   307  	modelSettings, err := readSettings(st, settingsC, modelGlobalKey)
   308  	if err != nil {
   309  		return errors.Trace(err)
   310  	}
   311  
   312  	// Get the existing model config from state.
   313  	oldConfig, err := st.ModelConfig()
   314  	if err != nil {
   315  		return errors.Trace(err)
   316  	}
   317  	if additionalValidation != nil {
   318  		err = additionalValidation(updateAttrs, removeAttrs, oldConfig)
   319  		if err != nil {
   320  			return errors.Trace(err)
   321  		}
   322  	}
   323  	validCfg, err := st.buildAndValidateModelConfig(updateAttrs, removeAttrs, oldConfig)
   324  	if err != nil {
   325  		return errors.Trace(err)
   326  	}
   327  
   328  	validAttrs := validCfg.AllAttrs()
   329  	for k := range oldConfig.AllAttrs() {
   330  		if _, ok := validAttrs[k]; !ok {
   331  			modelSettings.Delete(k)
   332  		}
   333  	}
   334  	// Some values require marshalling before storage.
   335  	validAttrs = config.CoerceForStorage(validAttrs)
   336  
   337  	modelSettings.Update(validAttrs)
   338  	_, ops := modelSettings.settingsUpdateOps()
   339  	return modelSettings.write(ops)
   340  }
   341  
   342  type modelConfigSourceFunc func() (attrValues, error)
   343  
   344  type modelConfigSource struct {
   345  	name       string
   346  	sourceFunc modelConfigSourceFunc
   347  }
   348  
   349  // modelConfigSources returns a slice of named model config
   350  // sources, in hierarchical order. Starting from the first source,
   351  // config is retrieved and each subsequent source adds to the
   352  // overall config values, later values override earlier ones.
   353  func modelConfigSources(st *State, regionSpec *environs.RegionSpec) []modelConfigSource {
   354  	return []modelConfigSource{
   355  		{config.JujuDefaultSource, st.defaultInheritedConfig},
   356  		{config.JujuControllerSource, st.controllerInheritedConfig},
   357  		{config.JujuRegionSource, st.regionInheritedConfig(regionSpec)},
   358  	}
   359  }
   360  
   361  const (
   362  	// controllerInheritedSettingsGlobalKey is the key for default settings shared across models.
   363  	controllerInheritedSettingsGlobalKey = "controller"
   364  )
   365  
   366  // defaultInheritedConfig returns config values which are defined
   367  // as defaults in either Juju or the state's environ provider.
   368  func (st *State) defaultInheritedConfig() (attrValues, error) {
   369  	var defaults = make(map[string]interface{})
   370  	for k, v := range config.ConfigDefaults() {
   371  		defaults[k] = v
   372  	}
   373  	providerDefaults, err := st.environsProviderConfigSchemaSource()
   374  	if errors.IsNotImplemented(err) {
   375  		return defaults, nil
   376  	} else if err != nil {
   377  		return nil, errors.Trace(err)
   378  	}
   379  	fields := schema.FieldMap(providerDefaults.ConfigSchema(), providerDefaults.ConfigDefaults())
   380  	if coercedAttrs, err := fields.Coerce(defaults, nil); err != nil {
   381  		return nil, errors.Trace(err)
   382  	} else {
   383  		for k, v := range coercedAttrs.(map[string]interface{}) {
   384  			defaults[k] = v
   385  		}
   386  	}
   387  	return defaults, nil
   388  }
   389  
   390  // controllerInheritedConfig returns the inherited config values
   391  // sourced from the local cloud config.
   392  func (st *State) controllerInheritedConfig() (attrValues, error) {
   393  	settings, err := readSettings(st, globalSettingsC, controllerInheritedSettingsGlobalKey)
   394  	if err != nil {
   395  		return nil, errors.Trace(err)
   396  	}
   397  	return settings.Map(), nil
   398  }
   399  
   400  // regionInheritedConfig returns the configuration attributes for the region in
   401  // the cloud where the model is targeted.
   402  func (st *State) regionInheritedConfig(regionSpec *environs.RegionSpec) func() (attrValues, error) {
   403  	if regionSpec == nil {
   404  		return func() (attrValues, error) {
   405  			return nil, errors.New(
   406  				"no environs.RegionSpec provided")
   407  		}
   408  	}
   409  	if regionSpec.Region == "" {
   410  		// It is expected that not all clouds have regions. So return not found
   411  		// if there is not a region here.
   412  		return func() (attrValues, error) {
   413  			return nil, errors.NotFoundf("region")
   414  		}
   415  	}
   416  	return func() (attrValues, error) {
   417  		settings, err := readSettings(st,
   418  			globalSettingsC,
   419  			regionSettingsGlobalKey(regionSpec.Cloud, regionSpec.Region),
   420  		)
   421  		if err != nil {
   422  			return nil, errors.Trace(err)
   423  		}
   424  		return settings.Map(), nil
   425  	}
   426  }
   427  
   428  // regionSpec returns a suitable environs.RegionSpec for use in
   429  // regionInheritedConfig.
   430  func (st *State) regionSpec() (*environs.RegionSpec, error) {
   431  	model, err := st.Model()
   432  	if err != nil {
   433  		return nil, errors.Trace(err)
   434  	}
   435  	rspec := &environs.RegionSpec{
   436  		Cloud:  model.Cloud(),
   437  		Region: model.CloudRegion(),
   438  	}
   439  	return rspec, nil
   440  }
   441  
   442  // composeModelConfigAttributes returns a set of model config settings composed from known
   443  // sources of default values overridden by model specific attributes.
   444  func composeModelConfigAttributes(
   445  	modelAttr attrValues, configSources ...modelConfigSource,
   446  ) (attrValues, error) {
   447  	resultAttrs := make(attrValues)
   448  
   449  	// Compose default settings from all known sources.
   450  	for _, source := range configSources {
   451  		newSettings, err := source.sourceFunc()
   452  		if errors.IsNotFound(err) {
   453  			continue
   454  		}
   455  		if err != nil {
   456  			return nil, errors.Annotatef(err, "reading %s settings", source.name)
   457  		}
   458  		for name, val := range newSettings {
   459  			resultAttrs[name] = val
   460  		}
   461  	}
   462  
   463  	// Merge in model specific settings.
   464  	for attr, val := range modelAttr {
   465  		resultAttrs[attr] = val
   466  	}
   467  
   468  	return resultAttrs, nil
   469  }
   470  
   471  // ComposeNewModelConfig returns a complete map of config attributes suitable for
   472  // creating a new model, by combining user specified values with system defaults.
   473  func (st *State) ComposeNewModelConfig(modelAttr map[string]interface{}, regionSpec *environs.RegionSpec) (map[string]interface{}, error) {
   474  	configSources := modelConfigSources(st, regionSpec)
   475  	return composeModelConfigAttributes(modelAttr, configSources...)
   476  }