~juju-qa/ubuntu/xenial/juju/xenial-2.0-beta3

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/core/description/model.go

  • Committer: Martin Packman
  • Date: 2016-03-30 19:31:08 UTC
  • mfrom: (1.1.41)
  • Revision ID: martin.packman@canonical.com-20160330193108-h9iz3ak334uk0z5r
Merge new upstream source 2.0~beta3

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 description
 
5
 
 
6
import (
 
7
        "sort"
 
8
 
 
9
        "github.com/juju/errors"
 
10
        "github.com/juju/loggo"
 
11
        "github.com/juju/names"
 
12
        "github.com/juju/schema"
 
13
        "github.com/juju/utils/set"
 
14
        "github.com/juju/version"
 
15
        "gopkg.in/yaml.v2"
 
16
)
 
17
 
 
18
var logger = loggo.GetLogger("juju.state.migration")
 
19
 
 
20
// ModelArgs represent the bare minimum information that is needed
 
21
// to represent a model.
 
22
type ModelArgs struct {
 
23
        Owner              names.UserTag
 
24
        Config             map[string]interface{}
 
25
        LatestToolsVersion version.Number
 
26
        Blocks             map[string]string
 
27
}
 
28
 
 
29
// NewModel returns a Model based on the args specified.
 
30
func NewModel(args ModelArgs) Model {
 
31
        m := &model{
 
32
                Version:             1,
 
33
                Owner_:              args.Owner.Id(),
 
34
                Config_:             args.Config,
 
35
                LatestToolsVersion_: args.LatestToolsVersion,
 
36
                Sequences_:          make(map[string]int),
 
37
                Blocks_:             args.Blocks,
 
38
        }
 
39
        m.setUsers(nil)
 
40
        m.setMachines(nil)
 
41
        m.setServices(nil)
 
42
        m.setRelations(nil)
 
43
        return m
 
44
}
 
45
 
 
46
// Serialize mirrors the Deserialize method, and makes sure that
 
47
// the same serialization method is used.
 
48
func Serialize(model Model) ([]byte, error) {
 
49
        return yaml.Marshal(model)
 
50
}
 
51
 
 
52
// Deserialize constructs a Model from a serialized YAML byte stream. The
 
53
// normal use for this is to construct the Model representation after getting
 
54
// the byte stream from an API connection or read from a file.
 
55
func Deserialize(bytes []byte) (Model, error) {
 
56
        var source map[string]interface{}
 
57
        err := yaml.Unmarshal(bytes, &source)
 
58
        if err != nil {
 
59
                return nil, errors.Trace(err)
 
60
        }
 
61
 
 
62
        model, err := importModel(source)
 
63
        if err != nil {
 
64
                return nil, errors.Trace(err)
 
65
        }
 
66
        return model, nil
 
67
}
 
68
 
 
69
type model struct {
 
70
        Version int `yaml:"version"`
 
71
 
 
72
        Owner_  string                 `yaml:"owner"`
 
73
        Config_ map[string]interface{} `yaml:"config"`
 
74
        Blocks_ map[string]string      `yaml:"blocks,omitempty"`
 
75
 
 
76
        LatestToolsVersion_ version.Number `yaml:"latest-tools,omitempty"`
 
77
 
 
78
        Users_     users     `yaml:"users"`
 
79
        Machines_  machines  `yaml:"machines"`
 
80
        Services_  services  `yaml:"services"`
 
81
        Relations_ relations `yaml:"relations"`
 
82
 
 
83
        Sequences_ map[string]int `yaml:"sequences"`
 
84
 
 
85
        Annotations_ `yaml:"annotations,omitempty"`
 
86
 
 
87
        Constraints_ *constraints `yaml:"constraints,omitempty"`
 
88
 
 
89
        // TODO:
 
90
        // Spaces
 
91
        // Storage
 
92
}
 
93
 
 
94
func (m *model) Tag() names.ModelTag {
 
95
        // Here we make the assumption that the environment UUID is set
 
96
        // correctly in the Config.
 
97
        value := m.Config_["uuid"]
 
98
        // Explicitly ignore the 'ok' aspect of the cast. If we don't have it
 
99
        // and it is wrong, we panic. Here we fully expect it to exist, but
 
100
        // paranoia says 'never panic', so worst case is we have an empty string.
 
101
        uuid, _ := value.(string)
 
102
        return names.NewModelTag(uuid)
 
103
}
 
104
 
 
105
// Owner implements Model.
 
106
func (m *model) Owner() names.UserTag {
 
107
        return names.NewUserTag(m.Owner_)
 
108
}
 
109
 
 
110
// Config implements Model.
 
111
func (m *model) Config() map[string]interface{} {
 
112
        // TODO: consider returning a deep copy.
 
113
        return m.Config_
 
114
}
 
115
 
 
116
// UpdateConfig implements Model.
 
117
func (m *model) UpdateConfig(config map[string]interface{}) {
 
118
        for key, value := range config {
 
119
                m.Config_[key] = value
 
120
        }
 
121
}
 
122
 
 
123
// LatestToolsVersion implements Model.
 
124
func (m *model) LatestToolsVersion() version.Number {
 
125
        return m.LatestToolsVersion_
 
126
}
 
127
 
 
128
// Blocks implements Model.
 
129
func (m *model) Blocks() map[string]string {
 
130
        return m.Blocks_
 
131
}
 
132
 
 
133
// Implement length-based sort with ByLen type.
 
134
type ByName []User
 
135
 
 
136
func (a ByName) Len() int           { return len(a) }
 
137
func (a ByName) Less(i, j int) bool { return a[i].Name().Canonical() < a[j].Name().Canonical() }
 
138
func (a ByName) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
 
139
 
 
140
// Users implements Model.
 
141
func (m *model) Users() []User {
 
142
        var result []User
 
143
        for _, user := range m.Users_.Users_ {
 
144
                result = append(result, user)
 
145
        }
 
146
        sort.Sort(ByName(result))
 
147
        return result
 
148
}
 
149
 
 
150
// AddUser implements Model.
 
151
func (m *model) AddUser(args UserArgs) {
 
152
        m.Users_.Users_ = append(m.Users_.Users_, newUser(args))
 
153
}
 
154
 
 
155
func (m *model) setUsers(userList []*user) {
 
156
        m.Users_ = users{
 
157
                Version: 1,
 
158
                Users_:  userList,
 
159
        }
 
160
}
 
161
 
 
162
// Machines implements Model.
 
163
func (m *model) Machines() []Machine {
 
164
        var result []Machine
 
165
        for _, machine := range m.Machines_.Machines_ {
 
166
                result = append(result, machine)
 
167
        }
 
168
        return result
 
169
}
 
170
 
 
171
// AddMachine implements Model.
 
172
func (m *model) AddMachine(args MachineArgs) Machine {
 
173
        machine := newMachine(args)
 
174
        m.Machines_.Machines_ = append(m.Machines_.Machines_, machine)
 
175
        return machine
 
176
}
 
177
 
 
178
func (m *model) setMachines(machineList []*machine) {
 
179
        m.Machines_ = machines{
 
180
                Version:   1,
 
181
                Machines_: machineList,
 
182
        }
 
183
}
 
184
 
 
185
// Services implements Model.
 
186
func (m *model) Services() []Service {
 
187
        var result []Service
 
188
        for _, service := range m.Services_.Services_ {
 
189
                result = append(result, service)
 
190
        }
 
191
        return result
 
192
}
 
193
 
 
194
func (m *model) service(name string) *service {
 
195
        for _, service := range m.Services_.Services_ {
 
196
                if service.Name() == name {
 
197
                        return service
 
198
                }
 
199
        }
 
200
        return nil
 
201
}
 
202
 
 
203
// AddService implements Model.
 
204
func (m *model) AddService(args ServiceArgs) Service {
 
205
        service := newService(args)
 
206
        m.Services_.Services_ = append(m.Services_.Services_, service)
 
207
        return service
 
208
}
 
209
 
 
210
func (m *model) setServices(serviceList []*service) {
 
211
        m.Services_ = services{
 
212
                Version:   1,
 
213
                Services_: serviceList,
 
214
        }
 
215
}
 
216
 
 
217
// Relations implements Model.
 
218
func (m *model) Relations() []Relation {
 
219
        var result []Relation
 
220
        for _, relation := range m.Relations_.Relations_ {
 
221
                result = append(result, relation)
 
222
        }
 
223
        return result
 
224
}
 
225
 
 
226
// AddRelation implements Model.
 
227
func (m *model) AddRelation(args RelationArgs) Relation {
 
228
        relation := newRelation(args)
 
229
        m.Relations_.Relations_ = append(m.Relations_.Relations_, relation)
 
230
        return relation
 
231
}
 
232
 
 
233
func (m *model) setRelations(relationList []*relation) {
 
234
        m.Relations_ = relations{
 
235
                Version:    1,
 
236
                Relations_: relationList,
 
237
        }
 
238
}
 
239
 
 
240
// Sequences implements Model.
 
241
func (m *model) Sequences() map[string]int {
 
242
        return m.Sequences_
 
243
}
 
244
 
 
245
// SetSequence implements Model.
 
246
func (m *model) SetSequence(name string, value int) {
 
247
        m.Sequences_[name] = value
 
248
}
 
249
 
 
250
// Constraints implements HasConstraints.
 
251
func (m *model) Constraints() Constraints {
 
252
        if m.Constraints_ == nil {
 
253
                return nil
 
254
        }
 
255
        return m.Constraints_
 
256
}
 
257
 
 
258
// SetConstraints implements HasConstraints.
 
259
func (m *model) SetConstraints(args ConstraintsArgs) {
 
260
        m.Constraints_ = newConstraints(args)
 
261
}
 
262
 
 
263
// Validate implements Model.
 
264
func (m *model) Validate() error {
 
265
        // A model needs an owner.
 
266
        if m.Owner_ == "" {
 
267
                return errors.NotValidf("missing model owner")
 
268
        }
 
269
 
 
270
        unitsWithOpenPorts := set.NewStrings()
 
271
        for _, machine := range m.Machines_.Machines_ {
 
272
                if err := machine.Validate(); err != nil {
 
273
                        return errors.Trace(err)
 
274
                }
 
275
                for _, np := range machine.NetworkPorts() {
 
276
                        for _, pr := range np.OpenPorts() {
 
277
                                unitsWithOpenPorts.Add(pr.UnitName())
 
278
                        }
 
279
                }
 
280
        }
 
281
        allUnits := set.NewStrings()
 
282
        for _, service := range m.Services_.Services_ {
 
283
                if err := service.Validate(); err != nil {
 
284
                        return errors.Trace(err)
 
285
                }
 
286
                allUnits = allUnits.Union(service.unitNames())
 
287
        }
 
288
        // Make sure that all the unit names specified in machine opened ports
 
289
        // exist as units of services.
 
290
        unknownUnitsWithPorts := unitsWithOpenPorts.Difference(allUnits)
 
291
        if len(unknownUnitsWithPorts) > 0 {
 
292
                return errors.Errorf("unknown unit names in open ports: %s", unknownUnitsWithPorts.SortedValues())
 
293
        }
 
294
 
 
295
        return m.validateRelations()
 
296
}
 
297
 
 
298
// validateRelations makes sure that for each endpoint in each relation there
 
299
// are settings for all units of that service for that endpoint.
 
300
func (m *model) validateRelations() error {
 
301
        for _, relation := range m.Relations_.Relations_ {
 
302
                for _, ep := range relation.Endpoints_.Endpoints_ {
 
303
                        // Check service exists.
 
304
                        service := m.service(ep.ServiceName())
 
305
                        if service == nil {
 
306
                                return errors.Errorf("unknown service %q for relation id %d", ep.ServiceName(), relation.Id())
 
307
                        }
 
308
                        // Check that all units have settings.
 
309
                        serviceUnits := service.unitNames()
 
310
                        epUnits := ep.unitNames()
 
311
                        if missingSettings := serviceUnits.Difference(epUnits); len(missingSettings) > 0 {
 
312
                                return errors.Errorf("missing relation settings for units %s in relation %d", missingSettings.SortedValues(), relation.Id())
 
313
                        }
 
314
                        if extraSettings := epUnits.Difference(serviceUnits); len(extraSettings) > 0 {
 
315
                                return errors.Errorf("settings for unknown units %s in relation %d", extraSettings.SortedValues(), relation.Id())
 
316
                        }
 
317
                }
 
318
        }
 
319
        return nil
 
320
}
 
321
 
 
322
// importModel constructs a new Model from a map that in normal usage situations
 
323
// will be the result of interpreting a large YAML document.
 
324
//
 
325
// This method is a package internal serialisation method.
 
326
func importModel(source map[string]interface{}) (*model, error) {
 
327
        version, err := getVersion(source)
 
328
        if err != nil {
 
329
                return nil, errors.Trace(err)
 
330
        }
 
331
 
 
332
        importFunc, ok := modelDeserializationFuncs[version]
 
333
        if !ok {
 
334
                return nil, errors.NotValidf("version %d", version)
 
335
        }
 
336
 
 
337
        return importFunc(source)
 
338
}
 
339
 
 
340
type modelDeserializationFunc func(map[string]interface{}) (*model, error)
 
341
 
 
342
var modelDeserializationFuncs = map[int]modelDeserializationFunc{
 
343
        1: importModelV1,
 
344
}
 
345
 
 
346
func importModelV1(source map[string]interface{}) (*model, error) {
 
347
        fields := schema.Fields{
 
348
                "owner":        schema.String(),
 
349
                "config":       schema.StringMap(schema.Any()),
 
350
                "latest-tools": schema.String(),
 
351
                "blocks":       schema.StringMap(schema.String()),
 
352
                "users":        schema.StringMap(schema.Any()),
 
353
                "machines":     schema.StringMap(schema.Any()),
 
354
                "services":     schema.StringMap(schema.Any()),
 
355
                "relations":    schema.StringMap(schema.Any()),
 
356
                "sequences":    schema.StringMap(schema.Int()),
 
357
        }
 
358
        // Some values don't have to be there.
 
359
        defaults := schema.Defaults{
 
360
                "latest-tools": schema.Omit,
 
361
                "blocks":       schema.Omit,
 
362
        }
 
363
        addAnnotationSchema(fields, defaults)
 
364
        addConstraintsSchema(fields, defaults)
 
365
        checker := schema.FieldMap(fields, defaults)
 
366
 
 
367
        coerced, err := checker.Coerce(source, nil)
 
368
        if err != nil {
 
369
                return nil, errors.Annotatef(err, "model v1 schema check failed")
 
370
        }
 
371
        valid := coerced.(map[string]interface{})
 
372
        // From here we know that the map returned from the schema coercion
 
373
        // contains fields of the right type.
 
374
 
 
375
        result := &model{
 
376
                Version:    1,
 
377
                Owner_:     valid["owner"].(string),
 
378
                Config_:    valid["config"].(map[string]interface{}),
 
379
                Sequences_: make(map[string]int),
 
380
                Blocks_:    convertToStringMap(valid["blocks"]),
 
381
        }
 
382
        result.importAnnotations(valid)
 
383
        sequences := valid["sequences"].(map[string]interface{})
 
384
        for key, value := range sequences {
 
385
                result.SetSequence(key, int(value.(int64)))
 
386
        }
 
387
 
 
388
        if constraintsMap, ok := valid["constraints"]; ok {
 
389
                constraints, err := importConstraints(constraintsMap.(map[string]interface{}))
 
390
                if err != nil {
 
391
                        return nil, errors.Trace(err)
 
392
                }
 
393
                result.Constraints_ = constraints
 
394
        }
 
395
 
 
396
        if availableTools, ok := valid["latest-tools"]; ok {
 
397
                num, err := version.Parse(availableTools.(string))
 
398
                if err != nil {
 
399
                        return nil, errors.Trace(err)
 
400
                }
 
401
                result.LatestToolsVersion_ = num
 
402
        }
 
403
 
 
404
        userMap := valid["users"].(map[string]interface{})
 
405
        users, err := importUsers(userMap)
 
406
        if err != nil {
 
407
                return nil, errors.Annotate(err, "users")
 
408
        }
 
409
        result.setUsers(users)
 
410
 
 
411
        machineMap := valid["machines"].(map[string]interface{})
 
412
        machines, err := importMachines(machineMap)
 
413
        if err != nil {
 
414
                return nil, errors.Annotate(err, "machines")
 
415
        }
 
416
        result.setMachines(machines)
 
417
 
 
418
        serviceMap := valid["services"].(map[string]interface{})
 
419
        services, err := importServices(serviceMap)
 
420
        if err != nil {
 
421
                return nil, errors.Annotate(err, "services")
 
422
        }
 
423
        result.setServices(services)
 
424
 
 
425
        relationMap := valid["relations"].(map[string]interface{})
 
426
        relations, err := importRelations(relationMap)
 
427
        if err != nil {
 
428
                return nil, errors.Annotate(err, "relations")
 
429
        }
 
430
        result.setRelations(relations)
 
431
 
 
432
        return result, nil
 
433
}