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 }