github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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/names" 9 "github.com/juju/schema" 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 Principal_ string `yaml:"principal,omitempty"` 29 Subordinates_ []string `yaml:"subordinates,omitempty"` 30 31 // TODO: 32 // storage constraints 33 // storage attachment count 34 35 PasswordHash_ string `yaml:"password-hash"` 36 Tools_ *agentTools `yaml:"tools"` 37 38 MeterStatusCode_ string `yaml:"meter-status-code,omitempty"` 39 MeterStatusInfo_ string `yaml:"meter-status-info,omitempty"` 40 41 Annotations_ `yaml:"annotations,omitempty"` 42 43 Constraints_ *constraints `yaml:"constraints,omitempty"` 44 } 45 46 // UnitArgs is an argument struct used to add a Unit to a Service in the Model. 47 type UnitArgs struct { 48 Tag names.UnitTag 49 Machine names.MachineTag 50 PasswordHash string 51 Principal names.UnitTag 52 Subordinates []names.UnitTag 53 54 MeterStatusCode string 55 MeterStatusInfo string 56 57 // TODO: storage attachment count 58 } 59 60 func newUnit(args UnitArgs) *unit { 61 var subordinates []string 62 for _, s := range args.Subordinates { 63 subordinates = append(subordinates, s.Id()) 64 } 65 return &unit{ 66 Name_: args.Tag.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(), 75 } 76 } 77 78 // Tag implements Unit. 79 func (u *unit) Tag() names.UnitTag { 80 return names.NewUnitTag(u.Name_) 81 } 82 83 // Name implements Unit. 84 func (u *unit) Name() string { 85 return u.Name_ 86 } 87 88 // Machine implements Unit. 89 func (u *unit) Machine() names.MachineTag { 90 return names.NewMachineTag(u.Machine_) 91 } 92 93 // PasswordHash implements Unit. 94 func (u *unit) PasswordHash() string { 95 return u.PasswordHash_ 96 } 97 98 // Principal implements Unit. 99 func (u *unit) Principal() names.UnitTag { 100 if u.Principal_ == "" { 101 return names.UnitTag{} 102 } 103 return names.NewUnitTag(u.Principal_) 104 } 105 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)) 111 } 112 return subordinates 113 } 114 115 // MeterStatusCode implements Unit. 116 func (u *unit) MeterStatusCode() string { 117 return u.MeterStatusCode_ 118 } 119 120 // MeterStatusInfo implements Unit. 121 func (u *unit) MeterStatusInfo() string { 122 return u.MeterStatusInfo_ 123 } 124 125 // Tools implements Unit. 126 func (u *unit) Tools() AgentTools { 127 // To avoid a typed nil, check before returning. 128 if u.Tools_ == nil { 129 return nil 130 } 131 return u.Tools_ 132 } 133 134 // SetTools implements Unit. 135 func (u *unit) SetTools(args AgentToolsArgs) { 136 u.Tools_ = newAgentTools(args) 137 } 138 139 // WorkloadStatus implements Unit. 140 func (u *unit) WorkloadStatus() Status { 141 // To avoid typed nils check nil here. 142 if u.WorkloadStatus_ == nil { 143 return nil 144 } 145 return u.WorkloadStatus_ 146 } 147 148 // SetWorkloadStatus implements Unit. 149 func (u *unit) SetWorkloadStatus(args StatusArgs) { 150 u.WorkloadStatus_ = newStatus(args) 151 } 152 153 // WorkloadStatusHistory implements Unit. 154 func (u *unit) WorkloadStatusHistory() []Status { 155 return u.WorkloadStatusHistory_.StatusHistory() 156 } 157 158 // SetWorkloadStatusHistory implements Unit. 159 func (u *unit) SetWorkloadStatusHistory(args []StatusArgs) { 160 u.WorkloadStatusHistory_.SetStatusHistory(args) 161 } 162 163 // AgentStatus implements Unit. 164 func (u *unit) AgentStatus() Status { 165 // To avoid typed nils check nil here. 166 if u.AgentStatus_ == nil { 167 return nil 168 } 169 return u.AgentStatus_ 170 } 171 172 // SetAgentStatus implements Unit. 173 func (u *unit) SetAgentStatus(args StatusArgs) { 174 u.AgentStatus_ = newStatus(args) 175 } 176 177 // AgentStatusHistory implements Unit. 178 func (u *unit) AgentStatusHistory() []Status { 179 return u.AgentStatusHistory_.StatusHistory() 180 } 181 182 // SetAgentStatusHistory implements Unit. 183 func (u *unit) SetAgentStatusHistory(args []StatusArgs) { 184 u.AgentStatusHistory_.SetStatusHistory(args) 185 } 186 187 // Constraints implements HasConstraints. 188 func (u *unit) Constraints() Constraints { 189 if u.Constraints_ == nil { 190 return nil 191 } 192 return u.Constraints_ 193 } 194 195 // SetConstraints implements HasConstraints. 196 func (u *unit) SetConstraints(args ConstraintsArgs) { 197 u.Constraints_ = newConstraints(args) 198 } 199 200 // Validate impelements Unit. 201 func (u *unit) Validate() error { 202 if u.Name_ == "" { 203 return errors.NotValidf("missing name") 204 } 205 if u.AgentStatus_ == nil { 206 return errors.NotValidf("unit %q missing agent status", u.Name_) 207 } 208 if u.WorkloadStatus_ == nil { 209 return errors.NotValidf("unit %q missing workload status", u.Name_) 210 } 211 if u.Tools_ == nil { 212 return errors.NotValidf("unit %q missing tools", u.Name_) 213 } 214 return nil 215 } 216 217 func importUnits(source map[string]interface{}) ([]*unit, error) { 218 checker := versionedChecker("units") 219 coerced, err := checker.Coerce(source, nil) 220 if err != nil { 221 return nil, errors.Annotatef(err, "units version schema check failed") 222 } 223 valid := coerced.(map[string]interface{}) 224 225 version := int(valid["version"].(int64)) 226 importFunc, ok := unitDeserializationFuncs[version] 227 if !ok { 228 return nil, errors.NotValidf("version %d", version) 229 } 230 sourceList := valid["units"].([]interface{}) 231 return importUnitList(sourceList, importFunc) 232 } 233 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{}) 238 if !ok { 239 return nil, errors.Errorf("unexpected value for unit %d, %T", i, value) 240 } 241 unit, err := importFunc(source) 242 if err != nil { 243 return nil, errors.Annotatef(err, "unit %d", i) 244 } 245 result = append(result, unit) 246 } 247 return result, nil 248 } 249 250 type unitDeserializationFunc func(map[string]interface{}) (*unit, error) 251 252 var unitDeserializationFuncs = map[int]unitDeserializationFunc{ 253 1: importUnitV1, 254 } 255 256 func importUnitV1(source map[string]interface{}) (*unit, error) { 257 fields := schema.Fields{ 258 "name": schema.String(), 259 "machine": schema.String(), 260 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()), 265 266 "principal": schema.String(), 267 "subordinates": schema.List(schema.String()), 268 269 "password-hash": schema.String(), 270 "tools": schema.StringMap(schema.Any()), 271 272 "meter-status-code": schema.String(), 273 "meter-status-info": schema.String(), 274 } 275 defaults := schema.Defaults{ 276 "principal": "", 277 "subordinates": schema.Omit, 278 "meter-status-code": "", 279 "meter-status-info": "", 280 } 281 addAnnotationSchema(fields, defaults) 282 addConstraintsSchema(fields, defaults) 283 checker := schema.FieldMap(fields, defaults) 284 285 coerced, err := checker.Coerce(source, nil) 286 if err != nil { 287 return nil, errors.Annotatef(err, "unit v1 schema check failed") 288 } 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. 292 293 result := &unit{ 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(), 302 } 303 result.importAnnotations(valid) 304 305 workloadHistory := valid["workload-status-history"].(map[string]interface{}) 306 if err := importStatusHistory(&result.WorkloadStatusHistory_, workloadHistory); err != nil { 307 return nil, errors.Trace(err) 308 } 309 agentHistory := valid["agent-status-history"].(map[string]interface{}) 310 if err := importStatusHistory(&result.AgentStatusHistory_, agentHistory); err != nil { 311 return nil, errors.Trace(err) 312 } 313 314 if constraintsMap, ok := valid["constraints"]; ok { 315 constraints, err := importConstraints(constraintsMap.(map[string]interface{})) 316 if err != nil { 317 return nil, errors.Trace(err) 318 } 319 result.Constraints_ = constraints 320 } 321 322 result.Subordinates_ = convertToStringSlice(valid["subordinates"]) 323 324 // Tools and status are required, so we expect them to be there. 325 tools, err := importAgentTools(valid["tools"].(map[string]interface{})) 326 if err != nil { 327 return nil, errors.Trace(err) 328 } 329 result.Tools_ = tools 330 331 agentStatus, err := importStatus(valid["agent-status"].(map[string]interface{})) 332 if err != nil { 333 return nil, errors.Trace(err) 334 } 335 result.AgentStatus_ = agentStatus 336 337 workloadStatus, err := importStatus(valid["workload-status"].(map[string]interface{})) 338 if err != nil { 339 return nil, errors.Trace(err) 340 } 341 result.WorkloadStatus_ = workloadStatus 342 343 return result, nil 344 }