~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/state/modelconfig.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
9
        "github.com/juju/juju/controller"
 
10
        "github.com/juju/juju/environs/config"
 
11
)
 
12
 
 
13
type attrValues map[string]interface{}
 
14
 
 
15
var disallowedModelConfigAttrs = [...]string{
 
16
        "admin-secret",
 
17
        "ca-private-key",
 
18
}
 
19
 
 
20
// ModelConfig returns the complete config for the model represented
 
21
// by this state.
 
22
func (st *State) ModelConfig() (*config.Config, error) {
 
23
        modelSettings, err := readSettings(st, settingsC, modelGlobalKey)
 
24
        if err != nil {
 
25
                return nil, errors.Trace(err)
 
26
        }
 
27
        return config.New(config.NoDefaults, modelSettings.Map())
 
28
}
 
29
 
 
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")
 
36
                }
 
37
        }
 
38
        if _, ok := cfg.AgentVersion(); !ok {
 
39
                return errors.Errorf("agent-version must always be set in state")
 
40
        }
 
41
        for attr := range allAttrs {
 
42
                if controller.ControllerOnlyAttribute(attr) {
 
43
                        return errors.Errorf("cannot set controller attribute %q on a model", attr)
 
44
                }
 
45
        }
 
46
        return nil
 
47
}
 
48
 
 
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) {
 
57
                        continue
 
58
                }
 
59
                if err != nil {
 
60
                        return nil, errors.Annotatef(err, "reading %s settings", src.name)
 
61
                }
 
62
                for attrName, value := range cfg {
 
63
                        values[attrName] = value
 
64
                }
 
65
        }
 
66
        return values, nil
 
67
}
 
68
 
 
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 {
 
74
                resultValues[k] = v
 
75
        }
 
76
 
 
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) {
 
86
                        continue
 
87
                }
 
88
                if err != nil {
 
89
                        return nil, errors.Annotatef(err, "reading %s settings", src.name)
 
90
                }
 
91
                sourceAttrs = append(sourceAttrs, cfg)
 
92
 
 
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 {
 
97
                                resultValues[k] = v
 
98
                        }
 
99
                }
 
100
        }
 
101
 
 
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]
 
115
                                break
 
116
                        }
 
117
                }
 
118
                result[attr] = config.ConfigValue{
 
119
                        Value:  val,
 
120
                        Source: source,
 
121
                }
 
122
        }
 
123
        return result, nil
 
124
}
 
125
 
 
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)
 
129
        if err != nil {
 
130
                return errors.Trace(err)
 
131
        }
 
132
 
 
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 {
 
141
                settings.Delete(r)
 
142
        }
 
143
        _, err = settings.Write()
 
144
        return err
 
145
}
 
146
 
 
147
// ModelConfigValues returns the config values for the model represented
 
148
// by this state.
 
149
func (st *State) ModelConfigValues() (config.ConfigValues, error) {
 
150
        cfg, err := st.ModelConfig()
 
151
        if err != nil {
 
152
                return nil, errors.Trace(err)
 
153
        }
 
154
        return st.modelConfigValues(cfg.AllAttrs())
 
155
}
 
156
 
 
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)
 
161
}
 
162
 
 
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)
 
169
                }
 
170
        }
 
171
        for attrName := range attrs {
 
172
                if controller.ControllerOnlyAttribute(attrName) {
 
173
                        return errors.Errorf("local cloud config cannot contain controller attribute %q", attrName)
 
174
                }
 
175
        }
 
176
        return nil
 
177
}
 
178
 
 
179
func (st *State) buildAndValidateModelConfig(updateAttrs attrValues, removeAttrs []string, oldConfig *config.Config) (*config.Config, error) {
 
180
        newConfig, err := oldConfig.Apply(updateAttrs)
 
181
        if err != nil {
 
182
                return nil, errors.Trace(err)
 
183
        }
 
184
        if len(removeAttrs) != 0 {
 
185
                newConfig, err = newConfig.Remove(removeAttrs)
 
186
                if err != nil {
 
187
                        return nil, errors.Trace(err)
 
188
                }
 
189
        }
 
190
        if err := checkModelConfig(newConfig); err != nil {
 
191
                return nil, errors.Trace(err)
 
192
        }
 
193
        return st.validate(newConfig, oldConfig)
 
194
}
 
195
 
 
196
type ValidateConfigFunc func(updateAttrs map[string]interface{}, removeAttrs []string, oldConfig *config.Config) error
 
197
 
 
198
// UpdateModelConfig adds, updates or removes attributes in the current
 
199
// configuration of the model with the provided updateAttrs and
 
200
// removeAttrs.
 
201
func (st *State) UpdateModelConfig(updateAttrs map[string]interface{}, removeAttrs []string, additionalValidation ValidateConfigFunc) error {
 
202
        if len(updateAttrs)+len(removeAttrs) == 0 {
 
203
                return nil
 
204
        }
 
205
 
 
206
        if len(removeAttrs) > 0 {
 
207
                var removed []string
 
208
                if updateAttrs == nil {
 
209
                        updateAttrs = make(map[string]interface{})
 
210
                }
 
211
                // For each removed attribute, pick up any inherited value
 
212
                // and if there's one, use that.
 
213
                inherited, err := st.inheritedConfigAttributes()
 
214
                if err != nil {
 
215
                        return errors.Trace(err)
 
216
                }
 
217
                for _, attr := range removeAttrs {
 
218
                        // We we are updating an attribute, that takes
 
219
                        // precedence over removing.
 
220
                        if _, ok := updateAttrs[attr]; ok {
 
221
                                continue
 
222
                        }
 
223
                        if val, ok := inherited[attr]; ok {
 
224
                                updateAttrs[attr] = val
 
225
                        } else {
 
226
                                removed = append(removed, attr)
 
227
                        }
 
228
                }
 
229
                removeAttrs = removed
 
230
        }
 
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.
 
237
 
 
238
        modelSettings, err := readSettings(st, settingsC, modelGlobalKey)
 
239
        if err != nil {
 
240
                return errors.Trace(err)
 
241
        }
 
242
 
 
243
        // Get the existing model config from state.
 
244
        oldConfig, err := st.ModelConfig()
 
245
        if err != nil {
 
246
                return errors.Trace(err)
 
247
        }
 
248
        if additionalValidation != nil {
 
249
                err = additionalValidation(updateAttrs, removeAttrs, oldConfig)
 
250
                if err != nil {
 
251
                        return errors.Trace(err)
 
252
                }
 
253
        }
 
254
        validCfg, err := st.buildAndValidateModelConfig(updateAttrs, removeAttrs, oldConfig)
 
255
        if err != nil {
 
256
                return errors.Trace(err)
 
257
        }
 
258
 
 
259
        validAttrs := validCfg.AllAttrs()
 
260
        for k := range oldConfig.AllAttrs() {
 
261
                if _, ok := validAttrs[k]; !ok {
 
262
                        modelSettings.Delete(k)
 
263
                }
 
264
        }
 
265
        // Some values require marshalling before storage.
 
266
        validAttrs = config.CoerceForStorage(validAttrs)
 
267
 
 
268
        modelSettings.Update(validAttrs)
 
269
        _, ops := modelSettings.settingsUpdateOps()
 
270
        return modelSettings.write(ops)
 
271
}
 
272
 
 
273
type modelConfigSourceFunc func() (attrValues, error)
 
274
 
 
275
type modelConfigSource struct {
 
276
        name       string
 
277
        sourceFunc modelConfigSourceFunc
 
278
}
 
279
 
 
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
 
289
        }
 
290
}
 
291
 
 
292
const (
 
293
        // controllerInheritedSettingsGlobalKey is the key for default settings shared across models.
 
294
        controllerInheritedSettingsGlobalKey = "controller"
 
295
)
 
296
 
 
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)
 
301
        if err != nil {
 
302
                return nil, errors.Trace(err)
 
303
        }
 
304
        return settings.Map(), nil
 
305
}
 
306
 
 
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)
 
313
 
 
314
        // Compose default settings from all known sources.
 
315
        for _, source := range configSources {
 
316
                newSettings, err := source.sourceFunc()
 
317
                if errors.IsNotFound(err) {
 
318
                        continue
 
319
                }
 
320
                if err != nil {
 
321
                        return nil, errors.Annotatef(err, "reading %s settings", source.name)
 
322
                }
 
323
                for name, val := range newSettings {
 
324
                        resultAttrs[name] = val
 
325
                }
 
326
        }
 
327
 
 
328
        // Merge in model specific settings.
 
329
        for attr, val := range modelAttr {
 
330
                resultAttrs[attr] = val
 
331
        }
 
332
 
 
333
        return resultAttrs, nil
 
334
}
 
335
 
 
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...)
 
341
}