1
// Copyright 2016 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
7
"github.com/juju/errors"
8
"github.com/juju/schema"
9
"gopkg.in/juju/names.v2"
13
Version int `yaml:"version"`
14
Units_ []*unit `yaml:"units"`
18
Name_ string `yaml:"name"`
20
Machine_ string `yaml:"machine"`
22
AgentStatus_ *status `yaml:"agent-status"`
23
AgentStatusHistory_ StatusHistory_ `yaml:"agent-status-history"`
25
WorkloadStatus_ *status `yaml:"workload-status"`
26
WorkloadStatusHistory_ StatusHistory_ `yaml:"workload-status-history"`
28
WorkloadVersion_ string `yaml:"workload-version,omitempty"`
29
WorkloadVersionHistory_ StatusHistory_ `yaml:"workload-version-history"`
31
Principal_ string `yaml:"principal,omitempty"`
32
Subordinates_ []string `yaml:"subordinates,omitempty"`
35
// storage constraints
36
// storage attachment count
38
PasswordHash_ string `yaml:"password-hash"`
39
Tools_ *agentTools `yaml:"tools"`
41
MeterStatusCode_ string `yaml:"meter-status-code,omitempty"`
42
MeterStatusInfo_ string `yaml:"meter-status-info,omitempty"`
44
Annotations_ `yaml:"annotations,omitempty"`
46
Constraints_ *constraints `yaml:"constraints,omitempty"`
49
// UnitArgs is an argument struct used to add a Unit to a Application in the Model.
50
type UnitArgs struct {
52
Machine names.MachineTag
54
Principal names.UnitTag
55
Subordinates []names.UnitTag
57
WorkloadVersion string
58
MeterStatusCode string
59
MeterStatusInfo string
61
// TODO: storage attachment count
64
func newUnit(args UnitArgs) *unit {
65
var subordinates []string
66
for _, s := range args.Subordinates {
67
subordinates = append(subordinates, s.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(),
84
// Tag implements Unit.
85
func (u *unit) Tag() names.UnitTag {
86
return names.NewUnitTag(u.Name_)
89
// Name implements Unit.
90
func (u *unit) Name() string {
94
// Machine implements Unit.
95
func (u *unit) Machine() names.MachineTag {
96
return names.NewMachineTag(u.Machine_)
99
// PasswordHash implements Unit.
100
func (u *unit) PasswordHash() string {
101
return u.PasswordHash_
104
// Principal implements Unit.
105
func (u *unit) Principal() names.UnitTag {
106
if u.Principal_ == "" {
107
return names.UnitTag{}
109
return names.NewUnitTag(u.Principal_)
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))
121
// MeterStatusCode implements Unit.
122
func (u *unit) MeterStatusCode() string {
123
return u.MeterStatusCode_
126
// MeterStatusInfo implements Unit.
127
func (u *unit) MeterStatusInfo() string {
128
return u.MeterStatusInfo_
131
// Tools implements Unit.
132
func (u *unit) Tools() AgentTools {
133
// To avoid a typed nil, check before returning.
140
// SetTools implements Unit.
141
func (u *unit) SetTools(args AgentToolsArgs) {
142
u.Tools_ = newAgentTools(args)
145
// WorkloadVersion implements Unit.
146
func (u *unit) WorkloadVersion() string {
147
return u.WorkloadVersion_
150
// WorkloadStatus implements Unit.
151
func (u *unit) WorkloadStatus() Status {
152
// To avoid typed nils check nil here.
153
if u.WorkloadStatus_ == nil {
156
return u.WorkloadStatus_
159
// SetWorkloadStatus implements Unit.
160
func (u *unit) SetWorkloadStatus(args StatusArgs) {
161
u.WorkloadStatus_ = newStatus(args)
164
// WorkloadStatusHistory implements Unit.
165
func (u *unit) WorkloadStatusHistory() []Status {
166
return u.WorkloadStatusHistory_.StatusHistory()
169
// SetWorkloadStatusHistory implements Unit.
170
func (u *unit) SetWorkloadStatusHistory(args []StatusArgs) {
171
u.WorkloadStatusHistory_.SetStatusHistory(args)
174
// WorkloadVersionHistory implements Unit.
175
func (u *unit) WorkloadVersionHistory() []Status {
176
return u.WorkloadVersionHistory_.StatusHistory()
179
// SetWorkloadVersionHistory implements Unit.
180
func (u *unit) SetWorkloadVersionHistory(args []StatusArgs) {
181
u.WorkloadVersionHistory_.SetStatusHistory(args)
184
// AgentStatus implements Unit.
185
func (u *unit) AgentStatus() Status {
186
// To avoid typed nils check nil here.
187
if u.AgentStatus_ == nil {
190
return u.AgentStatus_
193
// SetAgentStatus implements Unit.
194
func (u *unit) SetAgentStatus(args StatusArgs) {
195
u.AgentStatus_ = newStatus(args)
198
// AgentStatusHistory implements Unit.
199
func (u *unit) AgentStatusHistory() []Status {
200
return u.AgentStatusHistory_.StatusHistory()
203
// SetAgentStatusHistory implements Unit.
204
func (u *unit) SetAgentStatusHistory(args []StatusArgs) {
205
u.AgentStatusHistory_.SetStatusHistory(args)
208
// Constraints implements HasConstraints.
209
func (u *unit) Constraints() Constraints {
210
if u.Constraints_ == nil {
213
return u.Constraints_
216
// SetConstraints implements HasConstraints.
217
func (u *unit) SetConstraints(args ConstraintsArgs) {
218
u.Constraints_ = newConstraints(args)
221
// Validate impelements Unit.
222
func (u *unit) Validate() error {
224
return errors.NotValidf("missing name")
226
if u.AgentStatus_ == nil {
227
return errors.NotValidf("unit %q missing agent status", u.Name_)
229
if u.WorkloadStatus_ == nil {
230
return errors.NotValidf("unit %q missing workload status", u.Name_)
233
return errors.NotValidf("unit %q missing tools", u.Name_)
238
func importUnits(source map[string]interface{}) ([]*unit, error) {
239
checker := versionedChecker("units")
240
coerced, err := checker.Coerce(source, nil)
242
return nil, errors.Annotatef(err, "units version schema check failed")
244
valid := coerced.(map[string]interface{})
246
version := int(valid["version"].(int64))
247
importFunc, ok := unitDeserializationFuncs[version]
249
return nil, errors.NotValidf("version %d", version)
251
sourceList := valid["units"].([]interface{})
252
return importUnitList(sourceList, importFunc)
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{})
260
return nil, errors.Errorf("unexpected value for unit %d, %T", i, value)
262
unit, err := importFunc(source)
264
return nil, errors.Annotatef(err, "unit %d", i)
266
result = append(result, unit)
271
type unitDeserializationFunc func(map[string]interface{}) (*unit, error)
273
var unitDeserializationFuncs = map[int]unitDeserializationFunc{
277
func importUnitV1(source map[string]interface{}) (*unit, error) {
278
fields := schema.Fields{
279
"name": schema.String(),
280
"machine": schema.String(),
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()),
289
"principal": schema.String(),
290
"subordinates": schema.List(schema.String()),
292
"password-hash": schema.String(),
293
"tools": schema.StringMap(schema.Any()),
295
"meter-status-code": schema.String(),
296
"meter-status-info": schema.String(),
298
defaults := schema.Defaults{
300
"subordinates": schema.Omit,
301
"workload-version": "",
302
"meter-status-code": "",
303
"meter-status-info": "",
305
addAnnotationSchema(fields, defaults)
306
addConstraintsSchema(fields, defaults)
307
checker := schema.FieldMap(fields, defaults)
309
coerced, err := checker.Coerce(source, nil)
311
return nil, errors.Annotatef(err, "unit v1 schema check failed")
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.
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(),
329
result.importAnnotations(valid)
331
workloadStatusHistory := valid["workload-status-history"].(map[string]interface{})
332
if err := importStatusHistory(&result.WorkloadStatusHistory_, workloadStatusHistory); err != nil {
333
return nil, errors.Trace(err)
335
workloadVersionHistory := valid["workload-version-history"].(map[string]interface{})
336
if err := importStatusHistory(&result.WorkloadVersionHistory_, workloadVersionHistory); err != nil {
337
return nil, errors.Trace(err)
339
agentHistory := valid["agent-status-history"].(map[string]interface{})
340
if err := importStatusHistory(&result.AgentStatusHistory_, agentHistory); err != nil {
341
return nil, errors.Trace(err)
344
if constraintsMap, ok := valid["constraints"]; ok {
345
constraints, err := importConstraints(constraintsMap.(map[string]interface{}))
347
return nil, errors.Trace(err)
349
result.Constraints_ = constraints
352
result.Subordinates_ = convertToStringSlice(valid["subordinates"])
354
// Tools and status are required, so we expect them to be there.
355
tools, err := importAgentTools(valid["tools"].(map[string]interface{}))
357
return nil, errors.Trace(err)
359
result.Tools_ = tools
361
agentStatus, err := importStatus(valid["agent-status"].(map[string]interface{}))
363
return nil, errors.Trace(err)
365
result.AgentStatus_ = agentStatus
367
workloadStatus, err := importStatus(valid["workload-status"].(map[string]interface{}))
369
return nil, errors.Trace(err)
371
result.WorkloadStatus_ = workloadStatus