1
// Copyright 2016 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
7
"github.com/juju/errors"
9
"github.com/juju/juju/controller"
10
"github.com/juju/juju/environs/config"
13
type attrValues map[string]interface{}
15
var disallowedModelConfigAttrs = [...]string{
20
// ModelConfig returns the complete config for the model represented
22
func (st *State) ModelConfig() (*config.Config, error) {
23
modelSettings, err := readSettings(st, settingsC, modelGlobalKey)
25
return nil, errors.Trace(err)
27
return config.New(config.NoDefaults, modelSettings.Map())
30
// checkModelConfig returns an error if the config is definitely invalid.
31
func checkModelConfig(cfg *config.Config) error {
32
allAttrs := cfg.AllAttrs()
33
for _, attr := range disallowedModelConfigAttrs {
34
if _, ok := allAttrs[attr]; ok {
35
return errors.Errorf(attr + " should never be written to the state")
38
if _, ok := cfg.AgentVersion(); !ok {
39
return errors.Errorf("agent-version must always be set in state")
41
for attr := range allAttrs {
42
if controller.ControllerOnlyAttribute(attr) {
43
return errors.Errorf("cannot set controller attribute %q on a model", attr)
49
// inheritedConfigAttributes returns the merged collection of inherited config
50
// values used as model defaults when adding models or unsetting values.
51
func (st *State) inheritedConfigAttributes() (map[string]interface{}, error) {
52
configSources := modelConfigSources(st)
53
values := make(attrValues)
54
for _, src := range configSources {
55
cfg, err := src.sourceFunc()
56
if errors.IsNotFound(err) {
60
return nil, errors.Annotatef(err, "reading %s settings", src.name)
62
for attrName, value := range cfg {
63
values[attrName] = value
69
// modelConfigValues returns the values and source for the supplied model config
70
// when combined with controller and Juju defaults.
71
func (st *State) modelConfigValues(modelCfg attrValues) (config.ConfigValues, error) {
72
resultValues := make(attrValues)
73
for k, v := range modelCfg {
77
// Read all of the current inherited config values so
78
// we can dynamically reflect the origin of the model config.
79
configSources := modelConfigSources(st)
80
sourceNames := make([]string, 0, len(configSources))
81
sourceAttrs := make([]attrValues, 0, len(configSources))
82
for _, src := range configSources {
83
sourceNames = append(sourceNames, src.name)
84
cfg, err := src.sourceFunc()
85
if errors.IsNotFound(err) {
89
return nil, errors.Annotatef(err, "reading %s settings", src.name)
91
sourceAttrs = append(sourceAttrs, cfg)
93
// If no modelCfg was passed in, we'll accumulate data
94
// for the inherited values instead.
95
if len(modelCfg) == 0 {
96
for k, v := range cfg {
102
// Figure out the source of each config attribute based
103
// on the current model values and the inherited values.
104
result := make(config.ConfigValues)
105
for attr, val := range resultValues {
106
// Find the source of config for which the model
107
// value matches. If there's a match, the last match
108
// in the search order will be the source of config.
109
// If there's no match, the source is the model.
110
source := config.JujuModelConfigSource
111
n := len(sourceAttrs)
112
for i := range sourceAttrs {
113
if sourceAttrs[n-i-1][attr] == val {
114
source = sourceNames[n-i-1]
118
result[attr] = config.ConfigValue{
126
// UpdateModelConfigDefaultValues updates the inherited settings used when creating a new model.
127
func (st *State) UpdateModelConfigDefaultValues(attrs map[string]interface{}, removed []string) error {
128
settings, err := readSettings(st, globalSettingsC, controllerInheritedSettingsGlobalKey)
130
return errors.Trace(err)
133
// TODO(axw) 2013-12-6 #1167616
134
// Ensure that the settings on disk have not changed
135
// underneath us. The settings changes are actually
136
// applied as a delta to what's on disk; if there has
137
// been a concurrent update, the change may not be what
138
// the user asked for.
139
settings.Update(attrs)
140
for _, r := range removed {
143
_, err = settings.Write()
147
// ModelConfigValues returns the config values for the model represented
149
func (st *State) ModelConfigValues() (config.ConfigValues, error) {
150
cfg, err := st.ModelConfig()
152
return nil, errors.Trace(err)
154
return st.modelConfigValues(cfg.AllAttrs())
157
// ModelConfigDefaultValues returns the default config values to be used
158
// when creating a new model, and the origin of those values.
159
func (st *State) ModelConfigDefaultValues() (config.ConfigValues, error) {
160
return st.modelConfigValues(nil)
163
// checkControllerInheritedConfig returns an error if the shared local cloud config is definitely invalid.
164
func checkControllerInheritedConfig(attrs attrValues) error {
165
disallowedCloudConfigAttrs := append(disallowedModelConfigAttrs[:], config.AgentVersionKey)
166
for _, attr := range disallowedCloudConfigAttrs {
167
if _, ok := attrs[attr]; ok {
168
return errors.Errorf("local cloud config cannot contain " + attr)
171
for attrName := range attrs {
172
if controller.ControllerOnlyAttribute(attrName) {
173
return errors.Errorf("local cloud config cannot contain controller attribute %q", attrName)
179
func (st *State) buildAndValidateModelConfig(updateAttrs attrValues, removeAttrs []string, oldConfig *config.Config) (*config.Config, error) {
180
newConfig, err := oldConfig.Apply(updateAttrs)
182
return nil, errors.Trace(err)
184
if len(removeAttrs) != 0 {
185
newConfig, err = newConfig.Remove(removeAttrs)
187
return nil, errors.Trace(err)
190
if err := checkModelConfig(newConfig); err != nil {
191
return nil, errors.Trace(err)
193
return st.validate(newConfig, oldConfig)
196
type ValidateConfigFunc func(updateAttrs map[string]interface{}, removeAttrs []string, oldConfig *config.Config) error
198
// UpdateModelConfig adds, updates or removes attributes in the current
199
// configuration of the model with the provided updateAttrs and
201
func (st *State) UpdateModelConfig(updateAttrs map[string]interface{}, removeAttrs []string, additionalValidation ValidateConfigFunc) error {
202
if len(updateAttrs)+len(removeAttrs) == 0 {
206
if len(removeAttrs) > 0 {
208
if updateAttrs == nil {
209
updateAttrs = make(map[string]interface{})
211
// For each removed attribute, pick up any inherited value
212
// and if there's one, use that.
213
inherited, err := st.inheritedConfigAttributes()
215
return errors.Trace(err)
217
for _, attr := range removeAttrs {
218
// We we are updating an attribute, that takes
219
// precedence over removing.
220
if _, ok := updateAttrs[attr]; ok {
223
if val, ok := inherited[attr]; ok {
224
updateAttrs[attr] = val
226
removed = append(removed, attr)
229
removeAttrs = removed
231
// TODO(axw) 2013-12-6 #1167616
232
// Ensure that the settings on disk have not changed
233
// underneath us. The settings changes are actually
234
// applied as a delta to what's on disk; if there has
235
// been a concurrent update, the change may not be what
236
// the user asked for.
238
modelSettings, err := readSettings(st, settingsC, modelGlobalKey)
240
return errors.Trace(err)
243
// Get the existing model config from state.
244
oldConfig, err := st.ModelConfig()
246
return errors.Trace(err)
248
if additionalValidation != nil {
249
err = additionalValidation(updateAttrs, removeAttrs, oldConfig)
251
return errors.Trace(err)
254
validCfg, err := st.buildAndValidateModelConfig(updateAttrs, removeAttrs, oldConfig)
256
return errors.Trace(err)
259
validAttrs := validCfg.AllAttrs()
260
for k := range oldConfig.AllAttrs() {
261
if _, ok := validAttrs[k]; !ok {
262
modelSettings.Delete(k)
265
// Some values require marshalling before storage.
266
validAttrs = config.CoerceForStorage(validAttrs)
268
modelSettings.Update(validAttrs)
269
_, ops := modelSettings.settingsUpdateOps()
270
return modelSettings.write(ops)
273
type modelConfigSourceFunc func() (attrValues, error)
275
type modelConfigSource struct {
277
sourceFunc modelConfigSourceFunc
280
// modelConfigSources returns a slice of named model config
281
// sources, in hierarchical order. Starting from the first source,
282
// config is retrieved and each subsequent source adds to the
283
// overall config values, later values override earlier ones.
284
func modelConfigSources(st *State) []modelConfigSource {
285
return []modelConfigSource{
286
{config.JujuDefaultSource, func() (attrValues, error) { return config.ConfigDefaults(), nil }},
287
{config.JujuControllerSource, st.controllerInheritedConfig},
288
// We will also support local cloud region, tenant, user etc
293
// controllerInheritedSettingsGlobalKey is the key for default settings shared across models.
294
controllerInheritedSettingsGlobalKey = "controller"
297
// controllerInheritedConfig returns the inherited config values
298
// sourced from the local cloud config.
299
func (st *State) controllerInheritedConfig() (attrValues, error) {
300
settings, err := readSettings(st, globalSettingsC, controllerInheritedSettingsGlobalKey)
302
return nil, errors.Trace(err)
304
return settings.Map(), nil
307
// composeModelConfigAttributes returns a set of model config settings composed from known
308
// sources of default values overridden by model specific attributes.
309
func composeModelConfigAttributes(
310
modelAttr attrValues, configSources ...modelConfigSource,
311
) (attrValues, error) {
312
resultAttrs := make(attrValues)
314
// Compose default settings from all known sources.
315
for _, source := range configSources {
316
newSettings, err := source.sourceFunc()
317
if errors.IsNotFound(err) {
321
return nil, errors.Annotatef(err, "reading %s settings", source.name)
323
for name, val := range newSettings {
324
resultAttrs[name] = val
328
// Merge in model specific settings.
329
for attr, val := range modelAttr {
330
resultAttrs[attr] = val
333
return resultAttrs, nil
336
// ComposeNewModelConfig returns a complete map of config attributes suitable for
337
// creating a new model, by combining user specified values with system defaults.
338
func (st *State) ComposeNewModelConfig(modelAttr map[string]interface{}) (map[string]interface{}, error) {
339
configSources := modelConfigSources(st)
340
return composeModelConfigAttributes(modelAttr, configSources...)