1
// Copyright 2016 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
7
"github.com/juju/errors"
8
"github.com/juju/names"
9
"github.com/juju/schema"
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
Principal_ string `yaml:"principal,omitempty"`
29
Subordinates_ []string `yaml:"subordinates,omitempty"`
32
// storage constraints
33
// storage attachment count
35
PasswordHash_ string `yaml:"password-hash"`
36
Tools_ *agentTools `yaml:"tools"`
38
MeterStatusCode_ string `yaml:"meter-status-code,omitempty"`
39
MeterStatusInfo_ string `yaml:"meter-status-info,omitempty"`
41
Annotations_ `yaml:"annotations,omitempty"`
43
Constraints_ *constraints `yaml:"constraints,omitempty"`
46
// UnitArgs is an argument struct used to add a Unit to a Service in the Model.
47
type UnitArgs struct {
49
Machine names.MachineTag
51
Principal names.UnitTag
52
Subordinates []names.UnitTag
54
MeterStatusCode string
55
MeterStatusInfo string
57
// TODO: storage attachment count
60
func newUnit(args UnitArgs) *unit {
61
var subordinates []string
62
for _, s := range args.Subordinates {
63
subordinates = append(subordinates, s.Id())
67
Machine_: args.Machine.Id(),
68
PasswordHash_: args.PasswordHash,
69
Principal_: args.Principal.Id(),
70
Subordinates_: subordinates,
71
MeterStatusCode_: args.MeterStatusCode,
72
MeterStatusInfo_: args.MeterStatusInfo,
73
WorkloadStatusHistory_: newStatusHistory(),
74
AgentStatusHistory_: newStatusHistory(),
78
// Tag implements Unit.
79
func (u *unit) Tag() names.UnitTag {
80
return names.NewUnitTag(u.Name_)
83
// Name implements Unit.
84
func (u *unit) Name() string {
88
// Machine implements Unit.
89
func (u *unit) Machine() names.MachineTag {
90
return names.NewMachineTag(u.Machine_)
93
// PasswordHash implements Unit.
94
func (u *unit) PasswordHash() string {
95
return u.PasswordHash_
98
// Principal implements Unit.
99
func (u *unit) Principal() names.UnitTag {
100
if u.Principal_ == "" {
101
return names.UnitTag{}
103
return names.NewUnitTag(u.Principal_)
106
// Subordinates implements Unit.
107
func (u *unit) Subordinates() []names.UnitTag {
108
var subordinates []names.UnitTag
109
for _, s := range u.Subordinates_ {
110
subordinates = append(subordinates, names.NewUnitTag(s))
115
// MeterStatusCode implements Unit.
116
func (u *unit) MeterStatusCode() string {
117
return u.MeterStatusCode_
120
// MeterStatusInfo implements Unit.
121
func (u *unit) MeterStatusInfo() string {
122
return u.MeterStatusInfo_
125
// Tools implements Unit.
126
func (u *unit) Tools() AgentTools {
127
// To avoid a typed nil, check before returning.
134
// SetTools implements Unit.
135
func (u *unit) SetTools(args AgentToolsArgs) {
136
u.Tools_ = newAgentTools(args)
139
// WorkloadStatus implements Unit.
140
func (u *unit) WorkloadStatus() Status {
141
// To avoid typed nils check nil here.
142
if u.WorkloadStatus_ == nil {
145
return u.WorkloadStatus_
148
// SetWorkloadStatus implements Unit.
149
func (u *unit) SetWorkloadStatus(args StatusArgs) {
150
u.WorkloadStatus_ = newStatus(args)
153
// WorkloadStatusHistory implements Unit.
154
func (u *unit) WorkloadStatusHistory() []Status {
155
return u.WorkloadStatusHistory_.StatusHistory()
158
// SetWorkloadStatusHistory implements Unit.
159
func (u *unit) SetWorkloadStatusHistory(args []StatusArgs) {
160
u.WorkloadStatusHistory_.SetStatusHistory(args)
163
// AgentStatus implements Unit.
164
func (u *unit) AgentStatus() Status {
165
// To avoid typed nils check nil here.
166
if u.AgentStatus_ == nil {
169
return u.AgentStatus_
172
// SetAgentStatus implements Unit.
173
func (u *unit) SetAgentStatus(args StatusArgs) {
174
u.AgentStatus_ = newStatus(args)
177
// AgentStatusHistory implements Unit.
178
func (u *unit) AgentStatusHistory() []Status {
179
return u.AgentStatusHistory_.StatusHistory()
182
// SetAgentStatusHistory implements Unit.
183
func (u *unit) SetAgentStatusHistory(args []StatusArgs) {
184
u.AgentStatusHistory_.SetStatusHistory(args)
187
// Constraints implements HasConstraints.
188
func (u *unit) Constraints() Constraints {
189
if u.Constraints_ == nil {
192
return u.Constraints_
195
// SetConstraints implements HasConstraints.
196
func (u *unit) SetConstraints(args ConstraintsArgs) {
197
u.Constraints_ = newConstraints(args)
200
// Validate impelements Unit.
201
func (u *unit) Validate() error {
203
return errors.NotValidf("missing name")
205
if u.AgentStatus_ == nil {
206
return errors.NotValidf("unit %q missing agent status", u.Name_)
208
if u.WorkloadStatus_ == nil {
209
return errors.NotValidf("unit %q missing workload status", u.Name_)
212
return errors.NotValidf("unit %q missing tools", u.Name_)
217
func importUnits(source map[string]interface{}) ([]*unit, error) {
218
checker := versionedChecker("units")
219
coerced, err := checker.Coerce(source, nil)
221
return nil, errors.Annotatef(err, "units version schema check failed")
223
valid := coerced.(map[string]interface{})
225
version := int(valid["version"].(int64))
226
importFunc, ok := unitDeserializationFuncs[version]
228
return nil, errors.NotValidf("version %d", version)
230
sourceList := valid["units"].([]interface{})
231
return importUnitList(sourceList, importFunc)
234
func importUnitList(sourceList []interface{}, importFunc unitDeserializationFunc) ([]*unit, error) {
235
result := make([]*unit, 0, len(sourceList))
236
for i, value := range sourceList {
237
source, ok := value.(map[string]interface{})
239
return nil, errors.Errorf("unexpected value for unit %d, %T", i, value)
241
unit, err := importFunc(source)
243
return nil, errors.Annotatef(err, "unit %d", i)
245
result = append(result, unit)
250
type unitDeserializationFunc func(map[string]interface{}) (*unit, error)
252
var unitDeserializationFuncs = map[int]unitDeserializationFunc{
256
func importUnitV1(source map[string]interface{}) (*unit, error) {
257
fields := schema.Fields{
258
"name": schema.String(),
259
"machine": schema.String(),
261
"agent-status": schema.StringMap(schema.Any()),
262
"agent-status-history": schema.StringMap(schema.Any()),
263
"workload-status": schema.StringMap(schema.Any()),
264
"workload-status-history": schema.StringMap(schema.Any()),
266
"principal": schema.String(),
267
"subordinates": schema.List(schema.String()),
269
"password-hash": schema.String(),
270
"tools": schema.StringMap(schema.Any()),
272
"meter-status-code": schema.String(),
273
"meter-status-info": schema.String(),
275
defaults := schema.Defaults{
277
"subordinates": schema.Omit,
278
"meter-status-code": "",
279
"meter-status-info": "",
281
addAnnotationSchema(fields, defaults)
282
addConstraintsSchema(fields, defaults)
283
checker := schema.FieldMap(fields, defaults)
285
coerced, err := checker.Coerce(source, nil)
287
return nil, errors.Annotatef(err, "unit v1 schema check failed")
289
valid := coerced.(map[string]interface{})
290
// From here we know that the map returned from the schema coercion
291
// contains fields of the right type.
294
Name_: valid["name"].(string),
295
Machine_: valid["machine"].(string),
296
Principal_: valid["principal"].(string),
297
PasswordHash_: valid["password-hash"].(string),
298
MeterStatusCode_: valid["meter-status-code"].(string),
299
MeterStatusInfo_: valid["meter-status-info"].(string),
300
WorkloadStatusHistory_: newStatusHistory(),
301
AgentStatusHistory_: newStatusHistory(),
303
result.importAnnotations(valid)
305
workloadHistory := valid["workload-status-history"].(map[string]interface{})
306
if err := importStatusHistory(&result.WorkloadStatusHistory_, workloadHistory); err != nil {
307
return nil, errors.Trace(err)
309
agentHistory := valid["agent-status-history"].(map[string]interface{})
310
if err := importStatusHistory(&result.AgentStatusHistory_, agentHistory); err != nil {
311
return nil, errors.Trace(err)
314
if constraintsMap, ok := valid["constraints"]; ok {
315
constraints, err := importConstraints(constraintsMap.(map[string]interface{}))
317
return nil, errors.Trace(err)
319
result.Constraints_ = constraints
322
result.Subordinates_ = convertToStringSlice(valid["subordinates"])
324
// Tools and status are required, so we expect them to be there.
325
tools, err := importAgentTools(valid["tools"].(map[string]interface{}))
327
return nil, errors.Trace(err)
329
result.Tools_ = tools
331
agentStatus, err := importStatus(valid["agent-status"].(map[string]interface{}))
333
return nil, errors.Trace(err)
335
result.AgentStatus_ = agentStatus
337
workloadStatus, err := importStatus(valid["workload-status"].(map[string]interface{}))
339
return nil, errors.Trace(err)
341
result.WorkloadStatus_ = workloadStatus