github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/core/description/unit.go (about) 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 PasswordHash_ string `yaml:"password-hash"` 35 Tools_ *agentTools `yaml:"tools"` 36 37 MeterStatusCode_ string `yaml:"meter-status-code,omitempty"` 38 MeterStatusInfo_ string `yaml:"meter-status-info,omitempty"` 39 40 Annotations_ `yaml:"annotations,omitempty"` 41 42 Constraints_ *constraints `yaml:"constraints,omitempty"` 43 44 Payloads_ payloads `yaml:"payloads"` 45 } 46 47 // UnitArgs is an argument struct used to add a Unit to a Application in the Model. 48 type UnitArgs struct { 49 Tag names.UnitTag 50 Machine names.MachineTag 51 PasswordHash string 52 Principal names.UnitTag 53 Subordinates []names.UnitTag 54 55 WorkloadVersion string 56 MeterStatusCode string 57 MeterStatusInfo string 58 59 // TODO: storage attachment count 60 } 61 62 func newUnit(args UnitArgs) *unit { 63 var subordinates []string 64 for _, s := range args.Subordinates { 65 subordinates = append(subordinates, s.Id()) 66 } 67 u := &unit{ 68 Name_: args.Tag.Id(), 69 Machine_: args.Machine.Id(), 70 PasswordHash_: args.PasswordHash, 71 Principal_: args.Principal.Id(), 72 Subordinates_: subordinates, 73 WorkloadVersion_: args.WorkloadVersion, 74 MeterStatusCode_: args.MeterStatusCode, 75 MeterStatusInfo_: args.MeterStatusInfo, 76 WorkloadStatusHistory_: newStatusHistory(), 77 WorkloadVersionHistory_: newStatusHistory(), 78 AgentStatusHistory_: newStatusHistory(), 79 } 80 u.setPayloads(nil) 81 return u 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 // AddPayload implements Unit. 222 func (u *unit) AddPayload(args PayloadArgs) Payload { 223 payload := newPayload(args) 224 u.Payloads_.Payloads_ = append(u.Payloads_.Payloads_, payload) 225 return payload 226 } 227 228 // Payloads implements Unit. 229 func (u *unit) Payloads() []Payload { 230 var result []Payload 231 for _, payload := range u.Payloads_.Payloads_ { 232 result = append(result, payload) 233 } 234 return result 235 } 236 237 func (u *unit) setPayloads(payloadList []*payload) { 238 u.Payloads_ = payloads{ 239 Version: 1, 240 Payloads_: payloadList, 241 } 242 } 243 244 // Validate impelements Unit. 245 func (u *unit) Validate() error { 246 if u.Name_ == "" { 247 return errors.NotValidf("missing name") 248 } 249 if u.AgentStatus_ == nil { 250 return errors.NotValidf("unit %q missing agent status", u.Name_) 251 } 252 if u.WorkloadStatus_ == nil { 253 return errors.NotValidf("unit %q missing workload status", u.Name_) 254 } 255 if u.Tools_ == nil { 256 return errors.NotValidf("unit %q missing tools", u.Name_) 257 } 258 return nil 259 } 260 261 func importUnits(source map[string]interface{}) ([]*unit, error) { 262 checker := versionedChecker("units") 263 coerced, err := checker.Coerce(source, nil) 264 if err != nil { 265 return nil, errors.Annotatef(err, "units version schema check failed") 266 } 267 valid := coerced.(map[string]interface{}) 268 269 version := int(valid["version"].(int64)) 270 importFunc, ok := unitDeserializationFuncs[version] 271 if !ok { 272 return nil, errors.NotValidf("version %d", version) 273 } 274 sourceList := valid["units"].([]interface{}) 275 return importUnitList(sourceList, importFunc) 276 } 277 278 func importUnitList(sourceList []interface{}, importFunc unitDeserializationFunc) ([]*unit, error) { 279 result := make([]*unit, 0, len(sourceList)) 280 for i, value := range sourceList { 281 source, ok := value.(map[string]interface{}) 282 if !ok { 283 return nil, errors.Errorf("unexpected value for unit %d, %T", i, value) 284 } 285 unit, err := importFunc(source) 286 if err != nil { 287 return nil, errors.Annotatef(err, "unit %d", i) 288 } 289 result = append(result, unit) 290 } 291 return result, nil 292 } 293 294 type unitDeserializationFunc func(map[string]interface{}) (*unit, error) 295 296 var unitDeserializationFuncs = map[int]unitDeserializationFunc{ 297 1: importUnitV1, 298 } 299 300 func importUnitV1(source map[string]interface{}) (*unit, error) { 301 fields := schema.Fields{ 302 "name": schema.String(), 303 "machine": schema.String(), 304 305 "agent-status": schema.StringMap(schema.Any()), 306 "agent-status-history": schema.StringMap(schema.Any()), 307 "workload-status": schema.StringMap(schema.Any()), 308 "workload-status-history": schema.StringMap(schema.Any()), 309 "workload-version": schema.String(), 310 "workload-version-history": schema.StringMap(schema.Any()), 311 312 "principal": schema.String(), 313 "subordinates": schema.List(schema.String()), 314 315 "password-hash": schema.String(), 316 "tools": schema.StringMap(schema.Any()), 317 318 "meter-status-code": schema.String(), 319 "meter-status-info": schema.String(), 320 321 "payloads": schema.StringMap(schema.Any()), 322 } 323 defaults := schema.Defaults{ 324 "principal": "", 325 "subordinates": schema.Omit, 326 "workload-version": "", 327 "meter-status-code": "", 328 "meter-status-info": "", 329 } 330 addAnnotationSchema(fields, defaults) 331 addConstraintsSchema(fields, defaults) 332 checker := schema.FieldMap(fields, defaults) 333 334 coerced, err := checker.Coerce(source, nil) 335 if err != nil { 336 return nil, errors.Annotatef(err, "unit v1 schema check failed") 337 } 338 valid := coerced.(map[string]interface{}) 339 // From here we know that the map returned from the schema coercion 340 // contains fields of the right type. 341 342 result := &unit{ 343 Name_: valid["name"].(string), 344 Machine_: valid["machine"].(string), 345 Principal_: valid["principal"].(string), 346 PasswordHash_: valid["password-hash"].(string), 347 WorkloadVersion_: valid["workload-version"].(string), 348 MeterStatusCode_: valid["meter-status-code"].(string), 349 MeterStatusInfo_: valid["meter-status-info"].(string), 350 WorkloadStatusHistory_: newStatusHistory(), 351 WorkloadVersionHistory_: newStatusHistory(), 352 AgentStatusHistory_: newStatusHistory(), 353 } 354 result.importAnnotations(valid) 355 356 workloadStatusHistory := valid["workload-status-history"].(map[string]interface{}) 357 if err := importStatusHistory(&result.WorkloadStatusHistory_, workloadStatusHistory); err != nil { 358 return nil, errors.Trace(err) 359 } 360 workloadVersionHistory := valid["workload-version-history"].(map[string]interface{}) 361 if err := importStatusHistory(&result.WorkloadVersionHistory_, workloadVersionHistory); err != nil { 362 return nil, errors.Trace(err) 363 } 364 agentHistory := valid["agent-status-history"].(map[string]interface{}) 365 if err := importStatusHistory(&result.AgentStatusHistory_, agentHistory); err != nil { 366 return nil, errors.Trace(err) 367 } 368 369 if constraintsMap, ok := valid["constraints"]; ok { 370 constraints, err := importConstraints(constraintsMap.(map[string]interface{})) 371 if err != nil { 372 return nil, errors.Trace(err) 373 } 374 result.Constraints_ = constraints 375 } 376 377 result.Subordinates_ = convertToStringSlice(valid["subordinates"]) 378 379 // Tools and status are required, so we expect them to be there. 380 tools, err := importAgentTools(valid["tools"].(map[string]interface{})) 381 if err != nil { 382 return nil, errors.Trace(err) 383 } 384 result.Tools_ = tools 385 386 agentStatus, err := importStatus(valid["agent-status"].(map[string]interface{})) 387 if err != nil { 388 return nil, errors.Trace(err) 389 } 390 result.AgentStatus_ = agentStatus 391 392 workloadStatus, err := importStatus(valid["workload-status"].(map[string]interface{})) 393 if err != nil { 394 return nil, errors.Trace(err) 395 } 396 result.WorkloadStatus_ = workloadStatus 397 398 payloadMap := valid["payloads"].(map[string]interface{}) 399 payloads, err := importPayloads(payloadMap) 400 if err != nil { 401 return nil, errors.Annotate(err, "payloads") 402 } 403 result.setPayloads(payloads) 404 405 return result, nil 406 }