github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/state/multiwatcher/multiwatcher.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 // TODO(rogpeppe) move everything in this package to apiserver/params 5 // because all the types are part of the public API server interface. 6 // Then params would not need to import this package and we would 7 // not need to duplicate types like Life and ModelSLAInfo. 8 9 package multiwatcher 10 11 import ( 12 "bytes" 13 "encoding/json" 14 "fmt" 15 "time" 16 17 "github.com/juju/errors" 18 "gopkg.in/juju/charm.v6" 19 20 "github.com/juju/juju/core/constraints" 21 "github.com/juju/juju/core/instance" 22 "github.com/juju/juju/core/status" 23 ) 24 25 // Life describes the lifecycle state of an entity ("alive", "dying" 26 // or "dead"). 27 type Life string 28 29 // EntityInfo is implemented by all entity Info types. 30 type EntityInfo interface { 31 // EntityId returns an identifier that will uniquely 32 // identify the entity within its kind 33 EntityId() EntityId 34 } 35 36 // EntityId uniquely identifies an entity being tracked by the 37 // multiwatcherStore. 38 type EntityId struct { 39 Kind string `json:"kind"` 40 ModelUUID string `json:"model-uuid"` 41 Id string `json:"id"` 42 } 43 44 // Delta holds details of a change to the model. 45 type Delta struct { 46 // If Removed is true, the entity has been removed; 47 // otherwise it has been created or changed. 48 Removed bool `json:"removed"` 49 // Entity holds data about the entity that has changed. 50 Entity EntityInfo `json:"entity"` 51 } 52 53 // MarshalJSON implements json.Marshaler. 54 func (d *Delta) MarshalJSON() ([]byte, error) { 55 b, err := json.Marshal(d.Entity) 56 if err != nil { 57 return nil, err 58 } 59 var buf bytes.Buffer 60 buf.WriteByte('[') 61 c := "change" 62 if d.Removed { 63 c = "remove" 64 } 65 fmt.Fprintf(&buf, "%q,%q,", d.Entity.EntityId().Kind, c) 66 buf.Write(b) 67 buf.WriteByte(']') 68 return buf.Bytes(), nil 69 } 70 71 // UnmarshalJSON implements json.Unmarshaler. 72 func (d *Delta) UnmarshalJSON(data []byte) error { 73 var elements []json.RawMessage 74 if err := json.Unmarshal(data, &elements); err != nil { 75 return err 76 } 77 if len(elements) != 3 { 78 return fmt.Errorf( 79 "Expected 3 elements in top-level of JSON but got %d", 80 len(elements)) 81 } 82 var entityKind, operation string 83 if err := json.Unmarshal(elements[0], &entityKind); err != nil { 84 return err 85 } 86 if err := json.Unmarshal(elements[1], &operation); err != nil { 87 return err 88 } 89 if operation == "remove" { 90 d.Removed = true 91 } else if operation != "change" { 92 return fmt.Errorf("Unexpected operation %q", operation) 93 } 94 switch entityKind { 95 case "model": 96 d.Entity = new(ModelInfo) 97 case "machine": 98 d.Entity = new(MachineInfo) 99 case "application": 100 d.Entity = new(ApplicationInfo) 101 case "remoteApplication": 102 d.Entity = new(RemoteApplicationInfo) 103 case "unit": 104 d.Entity = new(UnitInfo) 105 case "relation": 106 d.Entity = new(RelationInfo) 107 case "annotation": 108 d.Entity = new(AnnotationInfo) 109 case "block": 110 d.Entity = new(BlockInfo) 111 case "action": 112 d.Entity = new(ActionInfo) 113 default: 114 return errors.Errorf("Unexpected entity name %q", entityKind) 115 } 116 return json.Unmarshal(elements[2], &d.Entity) 117 } 118 119 // Address describes a network address. 120 type Address struct { 121 Value string `json:"value"` 122 Type string `json:"type"` 123 Scope string `json:"scope"` 124 SpaceName string `json:"space-name,omitempty"` 125 SpaceProviderId string `json:"space-provider-id,omitempty"` 126 } 127 128 // MachineInfo holds the information about a machine 129 // that is tracked by multiwatcherStore. 130 type MachineInfo struct { 131 ModelUUID string `json:"model-uuid"` 132 Id string `json:"id"` 133 InstanceId string `json:"instance-id"` 134 AgentStatus StatusInfo `json:"agent-status"` 135 InstanceStatus StatusInfo `json:"instance-status"` 136 Life Life `json:"life"` 137 Config map[string]interface{} `json:"config,omitempty"` 138 Series string `json:"series"` 139 SupportedContainers []instance.ContainerType `json:"supported-containers"` 140 SupportedContainersKnown bool `json:"supported-containers-known"` 141 HardwareCharacteristics *instance.HardwareCharacteristics `json:"hardware-characteristics,omitempty"` 142 Jobs []MachineJob `json:"jobs"` 143 Addresses []Address `json:"addresses"` 144 HasVote bool `json:"has-vote"` 145 WantsVote bool `json:"wants-vote"` 146 } 147 148 // EntityId returns a unique identifier for a machine across 149 // models. 150 func (i *MachineInfo) EntityId() EntityId { 151 return EntityId{ 152 Kind: "machine", 153 ModelUUID: i.ModelUUID, 154 Id: i.Id, 155 } 156 } 157 158 // StatusInfo holds the unit and machine status information. It is 159 // used by ApplicationInfo and UnitInfo. 160 type StatusInfo struct { 161 Err error `json:"err,omitempty"` 162 Current status.Status `json:"current"` 163 Message string `json:"message"` 164 Since *time.Time `json:"since,omitempty"` 165 Version string `json:"version"` 166 Data map[string]interface{} `json:"data,omitempty"` 167 } 168 169 // NewStatusInfo return a new multiwatcher StatusInfo from a 170 // status StatusInfo. 171 func NewStatusInfo(s status.StatusInfo, err error) StatusInfo { 172 return StatusInfo{ 173 Err: err, 174 Current: s.Status, 175 Message: s.Message, 176 Since: s.Since, 177 Data: s.Data, 178 } 179 } 180 181 // ApplicationInfo holds the information about an application that is tracked 182 // by multiwatcherStore. 183 type ApplicationInfo struct { 184 ModelUUID string `json:"model-uuid"` 185 Name string `json:"name"` 186 Exposed bool `json:"exposed"` 187 CharmURL string `json:"charm-url"` 188 OwnerTag string `json:"owner-tag"` 189 Life Life `json:"life"` 190 MinUnits int `json:"min-units"` 191 Constraints constraints.Value `json:"constraints"` 192 Config map[string]interface{} `json:"config,omitempty"` 193 Subordinate bool `json:"subordinate"` 194 Status StatusInfo `json:"status"` 195 WorkloadVersion string `json:"workload-version"` 196 } 197 198 // EntityId returns a unique identifier for an application across 199 // models. 200 func (i *ApplicationInfo) EntityId() EntityId { 201 return EntityId{ 202 Kind: "application", 203 ModelUUID: i.ModelUUID, 204 Id: i.Name, 205 } 206 } 207 208 // RemoteApplicationInfo holds the information about a remote application that is 209 // tracked by multiwatcherStore. 210 type RemoteApplicationInfo struct { 211 ModelUUID string `json:"model-uuid"` 212 Name string `json:"name"` 213 OfferUUID string `json:"offer-uuid"` 214 OfferURL string `json:"offer-url"` 215 Life Life `json:"life"` 216 Status StatusInfo `json:"status"` 217 } 218 219 // EntityId returns a unique identifier for a remote application across models. 220 func (i *RemoteApplicationInfo) EntityId() EntityId { 221 return EntityId{ 222 Kind: "remoteApplication", 223 ModelUUID: i.ModelUUID, 224 Id: i.Name, 225 } 226 } 227 228 // ApplicationOfferInfo holds the information about an application offer that is 229 // tracked by multiwatcherStore. 230 type ApplicationOfferInfo struct { 231 ModelUUID string `json:"model-uuid"` 232 OfferName string `json:"offer-name"` 233 OfferUUID string `json:"offer-uuid"` 234 ApplicationName string `json:"application-name"` 235 CharmName string `json:"charm-name"` 236 TotalConnectedCount int `json:"total-connected-count"` 237 ActiveConnectedCount int `json:"active-connected-count"` 238 } 239 240 // EntityId returns a unique identifier for an application offer across models. 241 func (i *ApplicationOfferInfo) EntityId() EntityId { 242 return EntityId{ 243 Kind: "applicationOffer", 244 ModelUUID: i.ModelUUID, 245 Id: i.OfferName, 246 } 247 } 248 249 // Port identifies a network port number for a particular protocol. 250 type Port struct { 251 Protocol string `json:"protocol"` 252 Number int `json:"number"` 253 } 254 255 // PortRange represents a single range of ports. 256 type PortRange struct { 257 FromPort int `json:"from-port"` 258 ToPort int `json:"to-port"` 259 Protocol string `json:"protocol"` 260 } 261 262 // UnitInfo holds the information about a unit 263 // that is tracked by multiwatcherStore. 264 type UnitInfo struct { 265 ModelUUID string `json:"model-uuid"` 266 Name string `json:"name"` 267 Application string `json:"application"` 268 Series string `json:"series"` 269 CharmURL string `json:"charm-url"` 270 PublicAddress string `json:"public-address"` 271 PrivateAddress string `json:"private-address"` 272 MachineId string `json:"machine-id"` 273 Ports []Port `json:"ports"` 274 PortRanges []PortRange `json:"port-ranges"` 275 Subordinate bool `json:"subordinate"` 276 // Workload and agent state are modelled separately. 277 WorkloadStatus StatusInfo `json:"workload-status"` 278 AgentStatus StatusInfo `json:"agent-status"` 279 } 280 281 // EntityId returns a unique identifier for a unit across 282 // models. 283 func (i *UnitInfo) EntityId() EntityId { 284 return EntityId{ 285 Kind: "unit", 286 ModelUUID: i.ModelUUID, 287 Id: i.Name, 288 } 289 } 290 291 // ActionInfo holds the information about a action that is tracked by 292 // multiwatcherStore. 293 type ActionInfo struct { 294 ModelUUID string `json:"model-uuid"` 295 Id string `json:"id"` 296 Receiver string `json:"receiver"` 297 Name string `json:"name"` 298 Parameters map[string]interface{} `json:"parameters,omitempty"` 299 Status string `json:"status"` 300 Message string `json:"message"` 301 Results map[string]interface{} `json:"results,omitempty"` 302 Enqueued time.Time `json:"enqueued"` 303 Started time.Time `json:"started"` 304 Completed time.Time `json:"completed"` 305 } 306 307 // EntityId returns a unique identifier for an action across 308 // models. 309 func (i *ActionInfo) EntityId() EntityId { 310 return EntityId{ 311 Kind: "action", 312 ModelUUID: i.ModelUUID, 313 Id: i.Id, 314 } 315 } 316 317 // RelationInfo holds the information about a relation that is tracked 318 // by multiwatcherStore. 319 type RelationInfo struct { 320 ModelUUID string `json:"model-uuid"` 321 Key string `json:"key"` 322 Id int `json:"id"` 323 Endpoints []Endpoint `json:"endpoints"` 324 } 325 326 // CharmRelation is a mirror struct for charm.Relation. 327 type CharmRelation struct { 328 Name string `json:"name"` 329 Role string `json:"role"` 330 Interface string `json:"interface"` 331 Optional bool `json:"optional"` 332 Limit int `json:"limit"` 333 Scope string `json:"scope"` 334 } 335 336 // NewCharmRelation creates a new local CharmRelation structure from the 337 // charm.Relation structure. NOTE: when we update the database to not store a 338 // charm.Relation directly in the database, this method should take the state 339 // structure type. 340 func NewCharmRelation(cr charm.Relation) CharmRelation { 341 return CharmRelation{ 342 Name: cr.Name, 343 Role: string(cr.Role), 344 Interface: cr.Interface, 345 Optional: cr.Optional, 346 Limit: cr.Limit, 347 Scope: string(cr.Scope), 348 } 349 } 350 351 // Endpoint holds an application-relation pair. 352 type Endpoint struct { 353 ApplicationName string `json:"application-name"` 354 Relation CharmRelation `json:"relation"` 355 } 356 357 // EntityId returns a unique identifier for a relation across 358 // models. 359 func (i *RelationInfo) EntityId() EntityId { 360 return EntityId{ 361 Kind: "relation", 362 ModelUUID: i.ModelUUID, 363 Id: i.Key, 364 } 365 } 366 367 // AnnotationInfo holds the information about an annotation that is 368 // tracked by multiwatcherStore. 369 type AnnotationInfo struct { 370 ModelUUID string `json:"model-uuid"` 371 Tag string `json:"tag"` 372 Annotations map[string]string `json:"annotations"` 373 } 374 375 // EntityId returns a unique identifier for an annotation across 376 // models. 377 func (i *AnnotationInfo) EntityId() EntityId { 378 return EntityId{ 379 Kind: "annotation", 380 ModelUUID: i.ModelUUID, 381 Id: i.Tag, 382 } 383 } 384 385 // MachineJob values define responsibilities that machines may be 386 // expected to fulfil. 387 type MachineJob string 388 389 const ( 390 JobHostUnits MachineJob = "JobHostUnits" 391 JobManageModel MachineJob = "JobManageModel" 392 ) 393 394 // NeedsState returns true if the job requires a state connection. 395 func (job MachineJob) NeedsState() bool { 396 return job == JobManageModel 397 } 398 399 // AnyJobNeedsState returns true if any of the provided jobs 400 // require a state connection. 401 func AnyJobNeedsState(jobs ...MachineJob) bool { 402 for _, j := range jobs { 403 if j.NeedsState() { 404 return true 405 } 406 } 407 return false 408 } 409 410 // BlockInfo holds the information about a block that is tracked by 411 // multiwatcherStore. 412 type BlockInfo struct { 413 ModelUUID string `json:"model-uuid"` 414 Id string `json:"id"` 415 Type BlockType `json:"type"` 416 Message string `json:"message"` 417 Tag string `json:"tag"` 418 } 419 420 // EntityId returns a unique identifier for a block across 421 // models. 422 func (i *BlockInfo) EntityId() EntityId { 423 return EntityId{ 424 Kind: "block", 425 ModelUUID: i.ModelUUID, 426 Id: i.Id, 427 } 428 } 429 430 // BlockType values define model block type. 431 type BlockType string 432 433 const ( 434 // BlockDestroy type identifies destroy blocks. 435 BlockDestroy BlockType = "BlockDestroy" 436 437 // BlockRemove type identifies remove blocks. 438 BlockRemove BlockType = "BlockRemove" 439 440 // BlockChange type identifies change blocks. 441 BlockChange BlockType = "BlockChange" 442 ) 443 444 // ModelSLAInfo describes the SLA info for a model. 445 // Note: this replicates the type of the same name in the params package. 446 type ModelSLAInfo struct { 447 Level string `json:"level"` 448 Owner string `json:"owner"` 449 } 450 451 // ModelInfo holds the information about an model that is 452 // tracked by multiwatcherStore. 453 type ModelInfo struct { 454 ModelUUID string `json:"model-uuid"` 455 Name string `json:"name"` 456 Life Life `json:"life"` 457 Owner string `json:"owner"` 458 ControllerUUID string `json:"controller-uuid"` 459 IsController bool `json:"is-controller"` 460 Config map[string]interface{} `json:"config,omitempty"` 461 Status StatusInfo `json:"status"` 462 Constraints constraints.Value `json:"constraints"` 463 SLA ModelSLAInfo `json:"sla"` 464 } 465 466 // EntityId returns a unique identifier for an model. 467 func (i *ModelInfo) EntityId() EntityId { 468 return EntityId{ 469 Kind: "model", 470 ModelUUID: i.ModelUUID, 471 Id: i.ModelUUID, 472 } 473 }