~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/core/description/unit.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 description
 
5
 
 
6
import (
 
7
        "github.com/juju/errors"
 
8
        "github.com/juju/schema"
 
9
        "gopkg.in/juju/names.v2"
 
10
)
 
11
 
 
12
type units struct {
 
13
        Version int     `yaml:"version"`
 
14
        Units_  []*unit `yaml:"units"`
 
15
}
 
16
 
 
17
type unit struct {
 
18
        Name_ string `yaml:"name"`
 
19
 
 
20
        Machine_ string `yaml:"machine"`
 
21
 
 
22
        AgentStatus_        *status        `yaml:"agent-status"`
 
23
        AgentStatusHistory_ StatusHistory_ `yaml:"agent-status-history"`
 
24
 
 
25
        WorkloadStatus_        *status        `yaml:"workload-status"`
 
26
        WorkloadStatusHistory_ StatusHistory_ `yaml:"workload-status-history"`
 
27
 
 
28
        WorkloadVersion_        string         `yaml:"workload-version,omitempty"`
 
29
        WorkloadVersionHistory_ StatusHistory_ `yaml:"workload-version-history"`
 
30
 
 
31
        Principal_    string   `yaml:"principal,omitempty"`
 
32
        Subordinates_ []string `yaml:"subordinates,omitempty"`
 
33
 
 
34
        // TODO:
 
35
        //  storage constraints
 
36
        //  storage attachment count
 
37
 
 
38
        PasswordHash_ string      `yaml:"password-hash"`
 
39
        Tools_        *agentTools `yaml:"tools"`
 
40
 
 
41
        MeterStatusCode_ string `yaml:"meter-status-code,omitempty"`
 
42
        MeterStatusInfo_ string `yaml:"meter-status-info,omitempty"`
 
43
 
 
44
        Annotations_ `yaml:"annotations,omitempty"`
 
45
 
 
46
        Constraints_ *constraints `yaml:"constraints,omitempty"`
 
47
}
 
48
 
 
49
// UnitArgs is an argument struct used to add a Unit to a Application in the Model.
 
50
type UnitArgs struct {
 
51
        Tag          names.UnitTag
 
52
        Machine      names.MachineTag
 
53
        PasswordHash string
 
54
        Principal    names.UnitTag
 
55
        Subordinates []names.UnitTag
 
56
 
 
57
        WorkloadVersion string
 
58
        MeterStatusCode string
 
59
        MeterStatusInfo string
 
60
 
 
61
        // TODO: storage attachment count
 
62
}
 
63
 
 
64
func newUnit(args UnitArgs) *unit {
 
65
        var subordinates []string
 
66
        for _, s := range args.Subordinates {
 
67
                subordinates = append(subordinates, s.Id())
 
68
        }
 
69
        return &unit{
 
70
                Name_:                   args.Tag.Id(),
 
71
                Machine_:                args.Machine.Id(),
 
72
                PasswordHash_:           args.PasswordHash,
 
73
                Principal_:              args.Principal.Id(),
 
74
                Subordinates_:           subordinates,
 
75
                WorkloadVersion_:        args.WorkloadVersion,
 
76
                MeterStatusCode_:        args.MeterStatusCode,
 
77
                MeterStatusInfo_:        args.MeterStatusInfo,
 
78
                WorkloadStatusHistory_:  newStatusHistory(),
 
79
                WorkloadVersionHistory_: newStatusHistory(),
 
80
                AgentStatusHistory_:     newStatusHistory(),
 
81
        }
 
82
}
 
83
 
 
84
// Tag implements Unit.
 
85
func (u *unit) Tag() names.UnitTag {
 
86
        return names.NewUnitTag(u.Name_)
 
87
}
 
88
 
 
89
// Name implements Unit.
 
90
func (u *unit) Name() string {
 
91
        return u.Name_
 
92
}
 
93
 
 
94
// Machine implements Unit.
 
95
func (u *unit) Machine() names.MachineTag {
 
96
        return names.NewMachineTag(u.Machine_)
 
97
}
 
98
 
 
99
// PasswordHash implements Unit.
 
100
func (u *unit) PasswordHash() string {
 
101
        return u.PasswordHash_
 
102
}
 
103
 
 
104
// Principal implements Unit.
 
105
func (u *unit) Principal() names.UnitTag {
 
106
        if u.Principal_ == "" {
 
107
                return names.UnitTag{}
 
108
        }
 
109
        return names.NewUnitTag(u.Principal_)
 
110
}
 
111
 
 
112
// Subordinates implements Unit.
 
113
func (u *unit) Subordinates() []names.UnitTag {
 
114
        var subordinates []names.UnitTag
 
115
        for _, s := range u.Subordinates_ {
 
116
                subordinates = append(subordinates, names.NewUnitTag(s))
 
117
        }
 
118
        return subordinates
 
119
}
 
120
 
 
121
// MeterStatusCode implements Unit.
 
122
func (u *unit) MeterStatusCode() string {
 
123
        return u.MeterStatusCode_
 
124
}
 
125
 
 
126
// MeterStatusInfo implements Unit.
 
127
func (u *unit) MeterStatusInfo() string {
 
128
        return u.MeterStatusInfo_
 
129
}
 
130
 
 
131
// Tools implements Unit.
 
132
func (u *unit) Tools() AgentTools {
 
133
        // To avoid a typed nil, check before returning.
 
134
        if u.Tools_ == nil {
 
135
                return nil
 
136
        }
 
137
        return u.Tools_
 
138
}
 
139
 
 
140
// SetTools implements Unit.
 
141
func (u *unit) SetTools(args AgentToolsArgs) {
 
142
        u.Tools_ = newAgentTools(args)
 
143
}
 
144
 
 
145
// WorkloadVersion implements Unit.
 
146
func (u *unit) WorkloadVersion() string {
 
147
        return u.WorkloadVersion_
 
148
}
 
149
 
 
150
// WorkloadStatus implements Unit.
 
151
func (u *unit) WorkloadStatus() Status {
 
152
        // To avoid typed nils check nil here.
 
153
        if u.WorkloadStatus_ == nil {
 
154
                return nil
 
155
        }
 
156
        return u.WorkloadStatus_
 
157
}
 
158
 
 
159
// SetWorkloadStatus implements Unit.
 
160
func (u *unit) SetWorkloadStatus(args StatusArgs) {
 
161
        u.WorkloadStatus_ = newStatus(args)
 
162
}
 
163
 
 
164
// WorkloadStatusHistory implements Unit.
 
165
func (u *unit) WorkloadStatusHistory() []Status {
 
166
        return u.WorkloadStatusHistory_.StatusHistory()
 
167
}
 
168
 
 
169
// SetWorkloadStatusHistory implements Unit.
 
170
func (u *unit) SetWorkloadStatusHistory(args []StatusArgs) {
 
171
        u.WorkloadStatusHistory_.SetStatusHistory(args)
 
172
}
 
173
 
 
174
// WorkloadVersionHistory implements Unit.
 
175
func (u *unit) WorkloadVersionHistory() []Status {
 
176
        return u.WorkloadVersionHistory_.StatusHistory()
 
177
}
 
178
 
 
179
// SetWorkloadVersionHistory implements Unit.
 
180
func (u *unit) SetWorkloadVersionHistory(args []StatusArgs) {
 
181
        u.WorkloadVersionHistory_.SetStatusHistory(args)
 
182
}
 
183
 
 
184
// AgentStatus implements Unit.
 
185
func (u *unit) AgentStatus() Status {
 
186
        // To avoid typed nils check nil here.
 
187
        if u.AgentStatus_ == nil {
 
188
                return nil
 
189
        }
 
190
        return u.AgentStatus_
 
191
}
 
192
 
 
193
// SetAgentStatus implements Unit.
 
194
func (u *unit) SetAgentStatus(args StatusArgs) {
 
195
        u.AgentStatus_ = newStatus(args)
 
196
}
 
197
 
 
198
// AgentStatusHistory implements Unit.
 
199
func (u *unit) AgentStatusHistory() []Status {
 
200
        return u.AgentStatusHistory_.StatusHistory()
 
201
}
 
202
 
 
203
// SetAgentStatusHistory implements Unit.
 
204
func (u *unit) SetAgentStatusHistory(args []StatusArgs) {
 
205
        u.AgentStatusHistory_.SetStatusHistory(args)
 
206
}
 
207
 
 
208
// Constraints implements HasConstraints.
 
209
func (u *unit) Constraints() Constraints {
 
210
        if u.Constraints_ == nil {
 
211
                return nil
 
212
        }
 
213
        return u.Constraints_
 
214
}
 
215
 
 
216
// SetConstraints implements HasConstraints.
 
217
func (u *unit) SetConstraints(args ConstraintsArgs) {
 
218
        u.Constraints_ = newConstraints(args)
 
219
}
 
220
 
 
221
// Validate impelements Unit.
 
222
func (u *unit) Validate() error {
 
223
        if u.Name_ == "" {
 
224
                return errors.NotValidf("missing name")
 
225
        }
 
226
        if u.AgentStatus_ == nil {
 
227
                return errors.NotValidf("unit %q missing agent status", u.Name_)
 
228
        }
 
229
        if u.WorkloadStatus_ == nil {
 
230
                return errors.NotValidf("unit %q missing workload status", u.Name_)
 
231
        }
 
232
        if u.Tools_ == nil {
 
233
                return errors.NotValidf("unit %q missing tools", u.Name_)
 
234
        }
 
235
        return nil
 
236
}
 
237
 
 
238
func importUnits(source map[string]interface{}) ([]*unit, error) {
 
239
        checker := versionedChecker("units")
 
240
        coerced, err := checker.Coerce(source, nil)
 
241
        if err != nil {
 
242
                return nil, errors.Annotatef(err, "units version schema check failed")
 
243
        }
 
244
        valid := coerced.(map[string]interface{})
 
245
 
 
246
        version := int(valid["version"].(int64))
 
247
        importFunc, ok := unitDeserializationFuncs[version]
 
248
        if !ok {
 
249
                return nil, errors.NotValidf("version %d", version)
 
250
        }
 
251
        sourceList := valid["units"].([]interface{})
 
252
        return importUnitList(sourceList, importFunc)
 
253
}
 
254
 
 
255
func importUnitList(sourceList []interface{}, importFunc unitDeserializationFunc) ([]*unit, error) {
 
256
        result := make([]*unit, 0, len(sourceList))
 
257
        for i, value := range sourceList {
 
258
                source, ok := value.(map[string]interface{})
 
259
                if !ok {
 
260
                        return nil, errors.Errorf("unexpected value for unit %d, %T", i, value)
 
261
                }
 
262
                unit, err := importFunc(source)
 
263
                if err != nil {
 
264
                        return nil, errors.Annotatef(err, "unit %d", i)
 
265
                }
 
266
                result = append(result, unit)
 
267
        }
 
268
        return result, nil
 
269
}
 
270
 
 
271
type unitDeserializationFunc func(map[string]interface{}) (*unit, error)
 
272
 
 
273
var unitDeserializationFuncs = map[int]unitDeserializationFunc{
 
274
        1: importUnitV1,
 
275
}
 
276
 
 
277
func importUnitV1(source map[string]interface{}) (*unit, error) {
 
278
        fields := schema.Fields{
 
279
                "name":    schema.String(),
 
280
                "machine": schema.String(),
 
281
 
 
282
                "agent-status":             schema.StringMap(schema.Any()),
 
283
                "agent-status-history":     schema.StringMap(schema.Any()),
 
284
                "workload-status":          schema.StringMap(schema.Any()),
 
285
                "workload-status-history":  schema.StringMap(schema.Any()),
 
286
                "workload-version":         schema.String(),
 
287
                "workload-version-history": schema.StringMap(schema.Any()),
 
288
 
 
289
                "principal":    schema.String(),
 
290
                "subordinates": schema.List(schema.String()),
 
291
 
 
292
                "password-hash": schema.String(),
 
293
                "tools":         schema.StringMap(schema.Any()),
 
294
 
 
295
                "meter-status-code": schema.String(),
 
296
                "meter-status-info": schema.String(),
 
297
        }
 
298
        defaults := schema.Defaults{
 
299
                "principal":         "",
 
300
                "subordinates":      schema.Omit,
 
301
                "workload-version":  "",
 
302
                "meter-status-code": "",
 
303
                "meter-status-info": "",
 
304
        }
 
305
        addAnnotationSchema(fields, defaults)
 
306
        addConstraintsSchema(fields, defaults)
 
307
        checker := schema.FieldMap(fields, defaults)
 
308
 
 
309
        coerced, err := checker.Coerce(source, nil)
 
310
        if err != nil {
 
311
                return nil, errors.Annotatef(err, "unit v1 schema check failed")
 
312
        }
 
313
        valid := coerced.(map[string]interface{})
 
314
        // From here we know that the map returned from the schema coercion
 
315
        // contains fields of the right type.
 
316
 
 
317
        result := &unit{
 
318
                Name_:                   valid["name"].(string),
 
319
                Machine_:                valid["machine"].(string),
 
320
                Principal_:              valid["principal"].(string),
 
321
                PasswordHash_:           valid["password-hash"].(string),
 
322
                WorkloadVersion_:        valid["workload-version"].(string),
 
323
                MeterStatusCode_:        valid["meter-status-code"].(string),
 
324
                MeterStatusInfo_:        valid["meter-status-info"].(string),
 
325
                WorkloadStatusHistory_:  newStatusHistory(),
 
326
                WorkloadVersionHistory_: newStatusHistory(),
 
327
                AgentStatusHistory_:     newStatusHistory(),
 
328
        }
 
329
        result.importAnnotations(valid)
 
330
 
 
331
        workloadStatusHistory := valid["workload-status-history"].(map[string]interface{})
 
332
        if err := importStatusHistory(&result.WorkloadStatusHistory_, workloadStatusHistory); err != nil {
 
333
                return nil, errors.Trace(err)
 
334
        }
 
335
        workloadVersionHistory := valid["workload-version-history"].(map[string]interface{})
 
336
        if err := importStatusHistory(&result.WorkloadVersionHistory_, workloadVersionHistory); err != nil {
 
337
                return nil, errors.Trace(err)
 
338
        }
 
339
        agentHistory := valid["agent-status-history"].(map[string]interface{})
 
340
        if err := importStatusHistory(&result.AgentStatusHistory_, agentHistory); err != nil {
 
341
                return nil, errors.Trace(err)
 
342
        }
 
343
 
 
344
        if constraintsMap, ok := valid["constraints"]; ok {
 
345
                constraints, err := importConstraints(constraintsMap.(map[string]interface{}))
 
346
                if err != nil {
 
347
                        return nil, errors.Trace(err)
 
348
                }
 
349
                result.Constraints_ = constraints
 
350
        }
 
351
 
 
352
        result.Subordinates_ = convertToStringSlice(valid["subordinates"])
 
353
 
 
354
        // Tools and status are required, so we expect them to be there.
 
355
        tools, err := importAgentTools(valid["tools"].(map[string]interface{}))
 
356
        if err != nil {
 
357
                return nil, errors.Trace(err)
 
358
        }
 
359
        result.Tools_ = tools
 
360
 
 
361
        agentStatus, err := importStatus(valid["agent-status"].(map[string]interface{}))
 
362
        if err != nil {
 
363
                return nil, errors.Trace(err)
 
364
        }
 
365
        result.AgentStatus_ = agentStatus
 
366
 
 
367
        workloadStatus, err := importStatus(valid["workload-status"].(map[string]interface{}))
 
368
        if err != nil {
 
369
                return nil, errors.Trace(err)
 
370
        }
 
371
        result.WorkloadStatus_ = workloadStatus
 
372
 
 
373
        return result, nil
 
374
}