github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/allwatcher.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state 5 6 import ( 7 "reflect" 8 "strings" 9 10 "github.com/juju/errors" 11 "gopkg.in/juju/names.v2" 12 "gopkg.in/mgo.v2" 13 14 "github.com/juju/juju/network" 15 "github.com/juju/juju/state/multiwatcher" 16 "github.com/juju/juju/state/watcher" 17 "github.com/juju/juju/state/workers" 18 "github.com/juju/juju/status" 19 ) 20 21 // allWatcherStateBacking implements Backing by fetching entities for 22 // a single model from the State. 23 type allWatcherStateBacking struct { 24 st *State 25 watcher workers.TxnLogWatcher 26 collectionByName map[string]allWatcherStateCollection 27 } 28 29 // allModelWatcherStateBacking implements Backing by fetching entities 30 // for all models from the State. 31 type allModelWatcherStateBacking struct { 32 st *State 33 watcher workers.TxnLogWatcher 34 stPool *StatePool 35 collectionByName map[string]allWatcherStateCollection 36 } 37 38 // allWatcherStateCollection holds information about a 39 // collection watched by an allWatcher and the 40 // type of value we use to store entity information 41 // for that collection. 42 type allWatcherStateCollection struct { 43 // name stores the name of the collection. 44 name string 45 46 // docType stores the type of document 47 // that we use for this collection. 48 docType reflect.Type 49 50 // subsidiary is true if the collection is used only 51 // to modify a primary entity. 52 subsidiary bool 53 } 54 55 // makeAllWatcherCollectionInfo returns a name indexed map of 56 // allWatcherStateCollection instances for the collections specified. 57 func makeAllWatcherCollectionInfo(collNames ...string) map[string]allWatcherStateCollection { 58 seenTypes := make(map[reflect.Type]struct{}) 59 collectionByName := make(map[string]allWatcherStateCollection) 60 61 for _, collName := range collNames { 62 collection := allWatcherStateCollection{name: collName} 63 switch collName { 64 case modelsC: 65 collection.docType = reflect.TypeOf(backingModel{}) 66 case machinesC: 67 collection.docType = reflect.TypeOf(backingMachine{}) 68 case unitsC: 69 collection.docType = reflect.TypeOf(backingUnit{}) 70 case applicationsC: 71 collection.docType = reflect.TypeOf(backingApplication{}) 72 case actionsC: 73 collection.docType = reflect.TypeOf(backingAction{}) 74 case relationsC: 75 collection.docType = reflect.TypeOf(backingRelation{}) 76 case annotationsC: 77 collection.docType = reflect.TypeOf(backingAnnotation{}) 78 case blocksC: 79 collection.docType = reflect.TypeOf(backingBlock{}) 80 case statusesC: 81 collection.docType = reflect.TypeOf(backingStatus{}) 82 collection.subsidiary = true 83 case constraintsC: 84 collection.docType = reflect.TypeOf(backingConstraints{}) 85 collection.subsidiary = true 86 case settingsC: 87 collection.docType = reflect.TypeOf(backingSettings{}) 88 collection.subsidiary = true 89 case openedPortsC: 90 collection.docType = reflect.TypeOf(backingOpenedPorts{}) 91 collection.subsidiary = true 92 default: 93 panic(errors.Errorf("unknown collection %q", collName)) 94 } 95 96 docType := collection.docType 97 if _, ok := seenTypes[docType]; ok { 98 panic(errors.Errorf("duplicate collection type %s", docType)) 99 } 100 seenTypes[docType] = struct{}{} 101 102 if _, ok := collectionByName[collName]; ok { 103 panic(errors.Errorf("duplicate collection name %q", collName)) 104 } 105 collectionByName[collName] = collection 106 } 107 108 return collectionByName 109 } 110 111 type backingModel modelDoc 112 113 func (e *backingModel) updated(st *State, store *multiwatcherStore, id string) error { 114 store.Update(&multiwatcher.ModelInfo{ 115 ModelUUID: e.UUID, 116 Name: e.Name, 117 Life: multiwatcher.Life(e.Life.String()), 118 Owner: e.Owner, 119 ControllerUUID: e.ControllerUUID, 120 }) 121 return nil 122 } 123 124 func (e *backingModel) removed(store *multiwatcherStore, modelUUID, _ string, _ *State) error { 125 store.Remove(multiwatcher.EntityId{ 126 Kind: "model", 127 ModelUUID: modelUUID, 128 Id: modelUUID, 129 }) 130 return nil 131 } 132 133 func (e *backingModel) mongoId() string { 134 return e.UUID 135 } 136 137 type backingMachine machineDoc 138 139 func (m *backingMachine) updated(st *State, store *multiwatcherStore, id string) error { 140 info := &multiwatcher.MachineInfo{ 141 ModelUUID: st.ModelUUID(), 142 Id: m.Id, 143 Life: multiwatcher.Life(m.Life.String()), 144 Series: m.Series, 145 Jobs: paramsJobsFromJobs(m.Jobs), 146 SupportedContainers: m.SupportedContainers, 147 SupportedContainersKnown: m.SupportedContainersKnown, 148 HasVote: m.HasVote, 149 WantsVote: wantsVote(m.Jobs, m.NoVote), 150 } 151 addresses := network.MergedAddresses(networkAddresses(m.MachineAddresses), networkAddresses(m.Addresses)) 152 for _, addr := range addresses { 153 info.Addresses = append(info.Addresses, multiwatcher.Address{ 154 Value: addr.Value, 155 Type: string(addr.Type), 156 Scope: string(addr.Scope), 157 SpaceName: string(addr.SpaceName), 158 SpaceProviderId: string(addr.SpaceProviderId), 159 }) 160 } 161 162 oldInfo := store.Get(info.EntityId()) 163 if oldInfo == nil { 164 // We're adding the entry for the first time, 165 // so fetch the associated machine status. 166 entity, err := st.FindEntity(names.NewMachineTag(m.Id)) 167 if err != nil { 168 return errors.Annotatef(err, "retrieving machine %q", m.Id) 169 } 170 machine, ok := entity.(status.StatusGetter) 171 if !ok { 172 return errors.Errorf("the given entity does not support Status %v", entity) 173 } 174 agentStatus, err := machine.Status() 175 if err != nil { 176 return errors.Annotatef(err, "retrieving agent status for machine %q", m.Id) 177 } 178 info.AgentStatus = multiwatcher.NewStatusInfo(agentStatus, nil) 179 180 inst := machine.(status.InstanceStatusGetter) 181 if !ok { 182 return errors.Errorf("the given entity does not support InstanceStatus %v", entity) 183 } 184 185 instanceStatus, err := inst.InstanceStatus() 186 if err != nil { 187 return errors.Annotatef(err, "retrieving instance status for machine %q", m.Id) 188 } 189 info.InstanceStatus = multiwatcher.NewStatusInfo(instanceStatus, nil) 190 } else { 191 // The entry already exists, so preserve the current status and 192 // instance data. 193 oldInfo := oldInfo.(*multiwatcher.MachineInfo) 194 info.AgentStatus = oldInfo.AgentStatus 195 info.InstanceStatus = oldInfo.InstanceStatus 196 info.InstanceId = oldInfo.InstanceId 197 info.HardwareCharacteristics = oldInfo.HardwareCharacteristics 198 } 199 // If the machine is been provisioned, fetch the instance id as required, 200 // and set instance id and hardware characteristics. 201 if m.Nonce != "" && info.InstanceId == "" { 202 instanceData, err := getInstanceData(st, m.Id) 203 if err == nil { 204 info.InstanceId = string(instanceData.InstanceId) 205 info.HardwareCharacteristics = hardwareCharacteristics(instanceData) 206 } else if !errors.IsNotFound(err) { 207 return err 208 } 209 } 210 store.Update(info) 211 return nil 212 } 213 214 func (m *backingMachine) removed(store *multiwatcherStore, modelUUID, id string, _ *State) error { 215 store.Remove(multiwatcher.EntityId{ 216 Kind: "machine", 217 ModelUUID: modelUUID, 218 Id: id, 219 }) 220 return nil 221 } 222 223 func (m *backingMachine) mongoId() string { 224 return m.DocID 225 } 226 227 type backingUnit unitDoc 228 229 func getUnitPortRangesAndPorts(st *State, unitName string) ([]network.PortRange, []network.Port, error) { 230 // Get opened port ranges for the unit and convert them to ports, 231 // as older clients/servers do not know about ranges). See bug 232 // http://pad.lv/1418344 for more info. 233 unit, err := st.Unit(unitName) 234 if errors.IsNotFound(err) { 235 // Empty slices ensure backwards compatibility with older clients. 236 // See Bug #1425435. 237 return []network.PortRange{}, []network.Port{}, nil 238 } else if err != nil { 239 return nil, nil, errors.Annotatef(err, "failed to get unit %q", unitName) 240 } 241 portRanges, err := unit.OpenedPorts() 242 // Since the port ranges are associated with the unit's machine, 243 // we need to check for NotAssignedError. 244 if errors.IsNotAssigned(err) { 245 // Not assigned, so there won't be any ports opened. 246 // Empty slices ensure backwards compatibility with older clients. 247 // See Bug #1425435. 248 return []network.PortRange{}, []network.Port{}, nil 249 } else if err != nil { 250 return nil, nil, errors.Annotate(err, "failed to get unit port ranges") 251 } 252 // For backward compatibility, if there are no ports opened, return an 253 // empty slice rather than a nil slice. Use a len(portRanges) capacity to 254 // avoid unnecessary allocations, since most of the times only specific 255 // ports are opened by charms. 256 compatiblePorts := make([]network.Port, 0, len(portRanges)) 257 for _, portRange := range portRanges { 258 for j := portRange.FromPort; j <= portRange.ToPort; j++ { 259 compatiblePorts = append(compatiblePorts, network.Port{ 260 Number: j, 261 Protocol: portRange.Protocol, 262 }) 263 } 264 } 265 return portRanges, compatiblePorts, nil 266 } 267 268 func unitAndAgentStatus(st *State, name string) (unitStatus, agentStatus status.StatusInfo, err error) { 269 var empty status.StatusInfo 270 unit, err := st.Unit(name) 271 if err != nil { 272 return empty, empty, errors.Trace(err) 273 } 274 unitStatusResult, err := unit.Status() 275 if err != nil { 276 return empty, empty, errors.Trace(err) 277 } 278 agentStatusResult, err := unit.AgentStatus() 279 if err != nil { 280 return empty, empty, errors.Trace(err) 281 } 282 return unitStatusResult, agentStatusResult, nil 283 } 284 285 func (u *backingUnit) updated(st *State, store *multiwatcherStore, id string) error { 286 info := &multiwatcher.UnitInfo{ 287 ModelUUID: st.ModelUUID(), 288 Name: u.Name, 289 Application: u.Application, 290 Series: u.Series, 291 MachineId: u.MachineId, 292 Subordinate: u.Principal != "", 293 } 294 if u.CharmURL != nil { 295 info.CharmURL = u.CharmURL.String() 296 } 297 oldInfo := store.Get(info.EntityId()) 298 if oldInfo == nil { 299 logger.Debugf("new unit %q added to backing state", u.Name) 300 // We're adding the entry for the first time, 301 // so fetch the associated unit status and opened ports. 302 unitStatus, agentStatus, err := unitAndAgentStatus(st, u.Name) 303 if err != nil { 304 return errors.Annotatef(err, "reading unit and agent status for %q", u.Name) 305 } 306 // Unit and workload status. 307 info.WorkloadStatus = multiwatcher.NewStatusInfo(unitStatus, nil) 308 info.AgentStatus = multiwatcher.NewStatusInfo(agentStatus, nil) 309 if u.Tools != nil { 310 info.AgentStatus.Version = u.Tools.Version.Number.String() 311 } 312 313 portRanges, compatiblePorts, err := getUnitPortRangesAndPorts(st, u.Name) 314 if err != nil { 315 return errors.Trace(err) 316 } 317 318 info.PortRanges = toMultiwatcherPortRanges(portRanges) 319 info.Ports = toMultiwatcherPorts(compatiblePorts) 320 321 } else { 322 // The entry already exists, so preserve the current status and ports. 323 oldInfo := oldInfo.(*multiwatcher.UnitInfo) 324 // Unit and workload status. 325 info.AgentStatus = oldInfo.AgentStatus 326 info.WorkloadStatus = oldInfo.WorkloadStatus 327 info.Ports = oldInfo.Ports 328 info.PortRanges = oldInfo.PortRanges 329 } 330 publicAddress, privateAddress, err := getUnitAddresses(st, u.Name) 331 if err != nil { 332 return err 333 } 334 info.PublicAddress = publicAddress 335 info.PrivateAddress = privateAddress 336 store.Update(info) 337 return nil 338 } 339 340 // getUnitAddresses returns the public and private addresses on a given unit. 341 // As of 1.18, the addresses are stored on the assigned machine but we retain 342 // this approach for backwards compatibility. 343 func getUnitAddresses(st *State, unitName string) (string, string, error) { 344 u, err := st.Unit(unitName) 345 if errors.IsNotFound(err) { 346 // Not found, so there won't be any addresses. 347 return "", "", nil 348 } else if err != nil { 349 return "", "", err 350 } 351 publicAddress, err := u.PublicAddress() 352 if err != nil { 353 logger.Errorf("getting a public address for unit %q failed: %q", u.Name(), err) 354 } 355 privateAddress, err := u.PrivateAddress() 356 if err != nil { 357 logger.Errorf("getting a private address for unit %q failed: %q", u.Name(), err) 358 } 359 return publicAddress.Value, privateAddress.Value, nil 360 } 361 362 func (u *backingUnit) removed(store *multiwatcherStore, modelUUID, id string, _ *State) error { 363 store.Remove(multiwatcher.EntityId{ 364 Kind: "unit", 365 ModelUUID: modelUUID, 366 Id: id, 367 }) 368 return nil 369 } 370 371 func (u *backingUnit) mongoId() string { 372 return u.DocID 373 } 374 375 type backingApplication applicationDoc 376 377 func (svc *backingApplication) updated(st *State, store *multiwatcherStore, id string) error { 378 if svc.CharmURL == nil { 379 return errors.Errorf("charm url is nil") 380 } 381 info := &multiwatcher.ApplicationInfo{ 382 ModelUUID: st.ModelUUID(), 383 Name: svc.Name, 384 Exposed: svc.Exposed, 385 CharmURL: svc.CharmURL.String(), 386 Life: multiwatcher.Life(svc.Life.String()), 387 MinUnits: svc.MinUnits, 388 Subordinate: svc.Subordinate, 389 } 390 oldInfo := store.Get(info.EntityId()) 391 needConfig := false 392 if oldInfo == nil { 393 logger.Debugf("new application %q added to backing state", svc.Name) 394 key := applicationGlobalKey(svc.Name) 395 // We're adding the entry for the first time, 396 // so fetch the associated child documents. 397 c, err := readConstraints(st, key) 398 if err != nil { 399 return errors.Trace(err) 400 } 401 info.Constraints = c 402 needConfig = true 403 // Fetch the status. 404 application, err := st.Application(svc.Name) 405 if err != nil { 406 return errors.Trace(err) 407 } 408 applicationStatus, err := application.Status() 409 if err != nil { 410 return errors.Annotatef(err, "reading application status for key %s", key) 411 } 412 if err == nil { 413 info.Status = multiwatcher.StatusInfo{ 414 Current: applicationStatus.Status, 415 Message: applicationStatus.Message, 416 Data: normaliseStatusData(applicationStatus.Data), 417 Since: applicationStatus.Since, 418 } 419 } else { 420 // TODO(wallyworld) - bug http://pad.lv/1451283 421 // return an error here once we figure out what's happening 422 // Not sure how status can even return NotFound as it is created 423 // with the application initially. For now, we'll log the error as per 424 // the above and return Unknown. 425 now := st.clock.Now() 426 info.Status = multiwatcher.StatusInfo{ 427 Current: status.Unknown, 428 Since: &now, 429 Data: normaliseStatusData(nil), 430 } 431 } 432 } else { 433 // The entry already exists, so preserve the current status. 434 oldInfo := oldInfo.(*multiwatcher.ApplicationInfo) 435 info.Constraints = oldInfo.Constraints 436 if info.CharmURL == oldInfo.CharmURL { 437 // The charm URL remains the same - we can continue to 438 // use the same config settings. 439 info.Config = oldInfo.Config 440 } else { 441 // The charm URL has changed - we need to fetch the 442 // settings from the new charm's settings doc. 443 needConfig = true 444 } 445 } 446 if needConfig { 447 doc, err := readSettingsDoc(st, settingsC, applicationSettingsKey(svc.Name, svc.CharmURL)) 448 if err != nil { 449 return errors.Trace(err) 450 } 451 info.Config = doc.Settings 452 } 453 store.Update(info) 454 return nil 455 } 456 457 func (svc *backingApplication) removed(store *multiwatcherStore, modelUUID, id string, _ *State) error { 458 store.Remove(multiwatcher.EntityId{ 459 Kind: "application", 460 ModelUUID: modelUUID, 461 Id: id, 462 }) 463 return nil 464 } 465 466 func (svc *backingApplication) mongoId() string { 467 return svc.DocID 468 } 469 470 type backingAction actionDoc 471 472 func (a *backingAction) mongoId() string { 473 return a.DocId 474 } 475 476 func (a *backingAction) removed(store *multiwatcherStore, modelUUID, id string, _ *State) error { 477 store.Remove(multiwatcher.EntityId{ 478 Kind: "action", 479 ModelUUID: modelUUID, 480 Id: id, 481 }) 482 return nil 483 } 484 485 func (a *backingAction) updated(st *State, store *multiwatcherStore, id string) error { 486 info := &multiwatcher.ActionInfo{ 487 ModelUUID: st.ModelUUID(), 488 Id: id, 489 Receiver: a.Receiver, 490 Name: a.Name, 491 Parameters: a.Parameters, 492 Status: string(a.Status), 493 Message: a.Message, 494 Results: a.Results, 495 Enqueued: a.Enqueued, 496 Started: a.Started, 497 Completed: a.Completed, 498 } 499 store.Update(info) 500 return nil 501 } 502 503 type backingRelation relationDoc 504 505 func (r *backingRelation) updated(st *State, store *multiwatcherStore, id string) error { 506 eps := make([]multiwatcher.Endpoint, len(r.Endpoints)) 507 for i, ep := range r.Endpoints { 508 eps[i] = multiwatcher.Endpoint{ 509 ApplicationName: ep.ApplicationName, 510 Relation: multiwatcher.NewCharmRelation(ep.Relation), 511 } 512 } 513 info := &multiwatcher.RelationInfo{ 514 ModelUUID: st.ModelUUID(), 515 Key: r.Key, 516 Id: r.Id, 517 Endpoints: eps, 518 } 519 store.Update(info) 520 return nil 521 } 522 523 func (r *backingRelation) removed(store *multiwatcherStore, modelUUID, id string, _ *State) error { 524 store.Remove(multiwatcher.EntityId{ 525 Kind: "relation", 526 ModelUUID: modelUUID, 527 Id: id, 528 }) 529 return nil 530 } 531 532 func (r *backingRelation) mongoId() string { 533 return r.DocID 534 } 535 536 type backingAnnotation annotatorDoc 537 538 func (a *backingAnnotation) updated(st *State, store *multiwatcherStore, id string) error { 539 info := &multiwatcher.AnnotationInfo{ 540 ModelUUID: st.ModelUUID(), 541 Tag: a.Tag, 542 Annotations: a.Annotations, 543 } 544 store.Update(info) 545 return nil 546 } 547 548 func (a *backingAnnotation) removed(store *multiwatcherStore, modelUUID, id string, _ *State) error { 549 tag, ok := tagForGlobalKey(id) 550 if !ok { 551 return errors.Errorf("could not parse global key: %q", id) 552 } 553 store.Remove(multiwatcher.EntityId{ 554 Kind: "annotation", 555 ModelUUID: modelUUID, 556 Id: tag, 557 }) 558 return nil 559 } 560 561 func (a *backingAnnotation) mongoId() string { 562 return a.GlobalKey 563 } 564 565 type backingBlock blockDoc 566 567 func (a *backingBlock) updated(st *State, store *multiwatcherStore, id string) error { 568 info := &multiwatcher.BlockInfo{ 569 ModelUUID: st.ModelUUID(), 570 Id: id, 571 Tag: a.Tag, 572 Type: a.Type.ToParams(), 573 Message: a.Message, 574 } 575 store.Update(info) 576 return nil 577 } 578 579 func (a *backingBlock) removed(store *multiwatcherStore, modelUUID, id string, _ *State) error { 580 store.Remove(multiwatcher.EntityId{ 581 Kind: "block", 582 ModelUUID: modelUUID, 583 Id: id, 584 }) 585 return nil 586 } 587 588 func (a *backingBlock) mongoId() string { 589 return a.DocID 590 } 591 592 type backingStatus statusDoc 593 594 func (s *backingStatus) toStatusInfo() multiwatcher.StatusInfo { 595 return multiwatcher.StatusInfo{ 596 Current: s.Status, 597 Message: s.StatusInfo, 598 Data: s.StatusData, 599 Since: unixNanoToTime(s.Updated), 600 } 601 } 602 603 func (s *backingStatus) updated(st *State, store *multiwatcherStore, id string) error { 604 parentID, ok := backingEntityIdForGlobalKey(st.ModelUUID(), id) 605 if !ok { 606 return nil 607 } 608 info0 := store.Get(parentID) 609 switch info := info0.(type) { 610 case nil: 611 // The parent info doesn't exist. Ignore the status until it does. 612 return nil 613 case *multiwatcher.UnitInfo: 614 newInfo := *info 615 // Get the unit's current recorded status from state. 616 // It's needed to reset the unit status when a unit comes off error. 617 statusInfo, err := getStatus(st, unitGlobalKey(newInfo.Name), "unit") 618 if err != nil { 619 return err 620 } 621 if err := s.updatedUnitStatus(st, store, id, statusInfo, &newInfo); err != nil { 622 return err 623 } 624 info0 = &newInfo 625 case *multiwatcher.ApplicationInfo: 626 newInfo := *info 627 newInfo.Status = s.toStatusInfo() 628 info0 = &newInfo 629 case *multiwatcher.MachineInfo: 630 newInfo := *info 631 // lets dissambiguate between juju machine agent and provider instance statuses. 632 if strings.HasSuffix(id, "#instance") { 633 newInfo.InstanceStatus = s.toStatusInfo() 634 } else { 635 newInfo.AgentStatus = s.toStatusInfo() 636 } 637 info0 = &newInfo 638 default: 639 return errors.Errorf("status for unexpected entity with id %q; type %T", id, info) 640 } 641 store.Update(info0) 642 return nil 643 } 644 645 func (s *backingStatus) updatedUnitStatus(st *State, store *multiwatcherStore, id string, unitStatus status.StatusInfo, newInfo *multiwatcher.UnitInfo) error { 646 // Unit or workload status - display the agent status or any error. 647 // NOTE: thumper 2016-06-27, this is truely horrible, and we are lying to our users. 648 // however, this is explicitly what has been asked for as much as we dislike it. 649 if strings.HasSuffix(id, "#charm") || s.Status == status.Error { 650 newInfo.WorkloadStatus = s.toStatusInfo() 651 } else { 652 newInfo.AgentStatus = s.toStatusInfo() 653 // If the unit was in error and now it's not, we need to reset its 654 // status back to what was previously recorded. 655 if newInfo.WorkloadStatus.Current == status.Error { 656 newInfo.WorkloadStatus.Current = unitStatus.Status 657 newInfo.WorkloadStatus.Message = unitStatus.Message 658 newInfo.WorkloadStatus.Data = unitStatus.Data 659 newInfo.WorkloadStatus.Since = unixNanoToTime(s.Updated) 660 } 661 } 662 663 // A change in a unit's status might also affect it's application. 664 application, err := st.Application(newInfo.Application) 665 if err != nil { 666 return errors.Trace(err) 667 } 668 applicationId, ok := backingEntityIdForGlobalKey(st.ModelUUID(), application.globalKey()) 669 if !ok { 670 return nil 671 } 672 applicationInfo := store.Get(applicationId) 673 if applicationInfo == nil { 674 return nil 675 } 676 status, err := application.Status() 677 if err != nil { 678 return errors.Trace(err) 679 } 680 newApplicationInfo := *applicationInfo.(*multiwatcher.ApplicationInfo) 681 newApplicationInfo.Status = multiwatcher.NewStatusInfo(status, nil) 682 store.Update(&newApplicationInfo) 683 return nil 684 } 685 686 func (s *backingStatus) removed(*multiwatcherStore, string, string, *State) error { 687 // If the status is removed, the parent will follow not long after, 688 // so do nothing. 689 return nil 690 } 691 692 func (s *backingStatus) mongoId() string { 693 panic("cannot find mongo id from status document") 694 } 695 696 type backingConstraints constraintsDoc 697 698 func (c *backingConstraints) updated(st *State, store *multiwatcherStore, id string) error { 699 parentID, ok := backingEntityIdForGlobalKey(st.ModelUUID(), id) 700 if !ok { 701 return nil 702 } 703 info0 := store.Get(parentID) 704 switch info := info0.(type) { 705 case nil: 706 // The parent info doesn't exist. Ignore the status until it does. 707 return nil 708 case *multiwatcher.UnitInfo, *multiwatcher.MachineInfo: 709 // We don't (yet) publish unit or machine constraints. 710 return nil 711 case *multiwatcher.ApplicationInfo: 712 newInfo := *info 713 newInfo.Constraints = constraintsDoc(*c).value() 714 info0 = &newInfo 715 default: 716 return errors.Errorf("status for unexpected entity with id %q; type %T", id, info) 717 } 718 store.Update(info0) 719 return nil 720 } 721 722 func (c *backingConstraints) removed(*multiwatcherStore, string, string, *State) error { 723 return nil 724 } 725 726 func (c *backingConstraints) mongoId() string { 727 panic("cannot find mongo id from constraints document") 728 } 729 730 type backingSettings settingsDoc 731 732 func (s *backingSettings) updated(st *State, store *multiwatcherStore, id string) error { 733 parentID, url, ok := backingEntityIdForSettingsKey(st.ModelUUID(), id) 734 if !ok { 735 return nil 736 } 737 info0 := store.Get(parentID) 738 switch info := info0.(type) { 739 case nil: 740 // The parent info doesn't exist. Ignore the status until it does. 741 return nil 742 case *multiwatcher.ApplicationInfo: 743 // If we're seeing settings for the application with a different 744 // charm URL, we ignore them - we will fetch 745 // them again when the service charm changes. 746 // By doing this we make sure that the settings in the 747 // ApplicationInfo are always consistent with the charm URL. 748 if info.CharmURL != url { 749 break 750 } 751 newInfo := *info 752 newInfo.Config = s.Settings 753 info0 = &newInfo 754 default: 755 return nil 756 } 757 store.Update(info0) 758 return nil 759 } 760 761 func (s *backingSettings) removed(store *multiwatcherStore, modelUUID, id string, _ *State) error { 762 parentID, url, ok := backingEntityIdForSettingsKey(modelUUID, id) 763 if !ok { 764 // Service is already gone along with its settings. 765 return nil 766 } 767 parent := store.Get(parentID) 768 if info, ok := parent.(*multiwatcher.ApplicationInfo); ok { 769 if info.CharmURL != url { 770 return nil 771 } 772 newInfo := *info 773 newInfo.Config = s.Settings 774 parent = &newInfo 775 store.Update(parent) 776 } 777 return nil 778 } 779 780 func (s *backingSettings) mongoId() string { 781 panic("cannot find mongo id from settings document") 782 } 783 784 // backingEntityIdForSettingsKey returns the entity id for the given 785 // settings key. Any extra information in the key is returned in 786 // extra. 787 func backingEntityIdForSettingsKey(modelUUID, key string) (eid multiwatcher.EntityId, extra string, ok bool) { 788 if !strings.HasPrefix(key, "a#") { 789 eid, ok = backingEntityIdForGlobalKey(modelUUID, key) 790 return 791 } 792 key = key[2:] 793 i := strings.Index(key, "#") 794 if i == -1 { 795 return multiwatcher.EntityId{}, "", false 796 } 797 eid = (&multiwatcher.ApplicationInfo{ 798 ModelUUID: modelUUID, 799 Name: key[0:i], 800 }).EntityId() 801 extra = key[i+1:] 802 ok = true 803 return 804 } 805 806 type backingOpenedPorts map[string]interface{} 807 808 func (p *backingOpenedPorts) updated(st *State, store *multiwatcherStore, id string) error { 809 parentID, ok := backingEntityIdForOpenedPortsKey(st.ModelUUID(), id) 810 if !ok { 811 return nil 812 } 813 switch info := store.Get(parentID).(type) { 814 case nil: 815 // The parent info doesn't exist. This is unexpected because the port 816 // always refers to a machine. Anyway, ignore the ports for now. 817 return nil 818 case *multiwatcher.MachineInfo: 819 // Retrieve the units placed in the machine. 820 units, err := st.UnitsFor(info.Id) 821 if err != nil { 822 return errors.Trace(err) 823 } 824 // Update the ports on all units assigned to the machine. 825 for _, u := range units { 826 if err := updateUnitPorts(st, store, u); err != nil { 827 return errors.Trace(err) 828 } 829 } 830 } 831 return nil 832 } 833 834 func (p *backingOpenedPorts) removed(store *multiwatcherStore, modelUUID, id string, st *State) error { 835 if st == nil { 836 return nil 837 } 838 parentID, ok := backingEntityIdForOpenedPortsKey(st.ModelUUID(), id) 839 if !ok { 840 return nil 841 } 842 switch info := store.Get(parentID).(type) { 843 case nil: 844 // The parent info doesn't exist. This is unexpected because the port 845 // always refers to a machine. Anyway, ignore the ports for now. 846 return nil 847 case *multiwatcher.MachineInfo: 848 // Retrieve the units placed in the machine. 849 units, err := st.UnitsFor(info.Id) 850 if err != nil { 851 // An error isn't returned here because the watcher is 852 // always acting a little behind reality. It is reasonable 853 // that entities have been deleted from State but we're 854 // still seeing events related to them from the watcher. 855 logger.Errorf("cannot retrieve units for %q: %v", info.Id, err) 856 return nil 857 } 858 // Update the ports on all units assigned to the machine. 859 for _, u := range units { 860 if err := updateUnitPorts(st, store, u); err != nil { 861 logger.Errorf("cannot update unit ports for %q: %v", u.Name(), err) 862 } 863 } 864 } 865 return nil 866 } 867 868 func (p *backingOpenedPorts) mongoId() string { 869 panic("cannot find mongo id from openedPorts document") 870 } 871 872 func toMultiwatcherPortRanges(portRanges []network.PortRange) []multiwatcher.PortRange { 873 result := make([]multiwatcher.PortRange, len(portRanges)) 874 for i, pr := range portRanges { 875 result[i] = multiwatcher.PortRange{ 876 FromPort: pr.FromPort, 877 ToPort: pr.ToPort, 878 Protocol: pr.Protocol, 879 } 880 } 881 return result 882 } 883 884 func toMultiwatcherPorts(ports []network.Port) []multiwatcher.Port { 885 result := make([]multiwatcher.Port, len(ports)) 886 for i, p := range ports { 887 result[i] = multiwatcher.Port{ 888 Number: p.Number, 889 Protocol: p.Protocol, 890 } 891 } 892 return result 893 } 894 895 // updateUnitPorts updates the Ports and PortRanges info of the given unit. 896 func updateUnitPorts(st *State, store *multiwatcherStore, u *Unit) error { 897 eid, ok := backingEntityIdForGlobalKey(st.ModelUUID(), u.globalKey()) 898 if !ok { 899 // This should never happen. 900 return errors.New("cannot retrieve entity id for unit") 901 } 902 switch oldInfo := store.Get(eid).(type) { 903 case nil: 904 // The unit info doesn't exist. This is unlikely to happen, but ignore 905 // the status until a unitInfo is included in the store. 906 return nil 907 case *multiwatcher.UnitInfo: 908 portRanges, compatiblePorts, err := getUnitPortRangesAndPorts(st, oldInfo.Name) 909 if err != nil { 910 return errors.Trace(err) 911 } 912 unitInfo := *oldInfo 913 unitInfo.PortRanges = toMultiwatcherPortRanges(portRanges) 914 unitInfo.Ports = toMultiwatcherPorts(compatiblePorts) 915 store.Update(&unitInfo) 916 default: 917 return nil 918 } 919 return nil 920 } 921 922 // backingEntityIdForOpenedPortsKey returns the entity id for the given 923 // openedPorts key. Any extra information in the key is discarded. 924 func backingEntityIdForOpenedPortsKey(modelUUID, key string) (multiwatcher.EntityId, bool) { 925 parts, err := extractPortsIDParts(key) 926 if err != nil { 927 logger.Debugf("cannot parse ports key %q: %v", key, err) 928 return multiwatcher.EntityId{}, false 929 } 930 return backingEntityIdForGlobalKey(modelUUID, machineGlobalKey(parts[1])) 931 } 932 933 // backingEntityIdForGlobalKey returns the entity id for the given global key. 934 // It returns false if the key is not recognized. 935 func backingEntityIdForGlobalKey(modelUUID, key string) (multiwatcher.EntityId, bool) { 936 if len(key) < 3 || key[1] != '#' { 937 return multiwatcher.EntityId{}, false 938 } 939 id := key[2:] 940 switch key[0] { 941 case 'm': 942 return (&multiwatcher.MachineInfo{ 943 ModelUUID: modelUUID, 944 Id: id, 945 }).EntityId(), true 946 case 'u': 947 id = strings.TrimSuffix(id, "#charm") 948 return (&multiwatcher.UnitInfo{ 949 ModelUUID: modelUUID, 950 Name: id, 951 }).EntityId(), true 952 case 'a': 953 return (&multiwatcher.ApplicationInfo{ 954 ModelUUID: modelUUID, 955 Name: id, 956 }).EntityId(), true 957 default: 958 return multiwatcher.EntityId{}, false 959 } 960 } 961 962 // backingEntityDoc is implemented by the documents in 963 // collections that the allWatcherStateBacking watches. 964 type backingEntityDoc interface { 965 // updated is called when the document has changed. 966 // The mongo _id value of the document is provided in id. 967 updated(st *State, store *multiwatcherStore, id string) error 968 969 // removed is called when the document has changed. 970 // The receiving instance will not contain any data. 971 // 972 // The mongo _id value of the document is provided in id. 973 // 974 // In some cases st may be nil. If the implementation requires st 975 // then it should do nothing. 976 removed(store *multiwatcherStore, modelUUID, id string, st *State) error 977 978 // mongoId returns the mongo _id field of the document. 979 // It is currently never called for subsidiary documents. 980 mongoId() string 981 } 982 983 func newAllWatcherStateBacking(st *State) Backing { 984 collections := makeAllWatcherCollectionInfo( 985 machinesC, 986 unitsC, 987 applicationsC, 988 relationsC, 989 annotationsC, 990 statusesC, 991 constraintsC, 992 settingsC, 993 openedPortsC, 994 actionsC, 995 blocksC, 996 ) 997 return &allWatcherStateBacking{ 998 st: st, 999 watcher: st.workers.TxnLogWatcher(), 1000 collectionByName: collections, 1001 } 1002 } 1003 1004 func (b *allWatcherStateBacking) filterEnv(docID interface{}) bool { 1005 _, err := b.st.strictLocalID(docID.(string)) 1006 return err == nil 1007 } 1008 1009 // Watch watches all the collections. 1010 func (b *allWatcherStateBacking) Watch(in chan<- watcher.Change) { 1011 for _, c := range b.collectionByName { 1012 b.watcher.WatchCollectionWithFilter(c.name, in, b.filterEnv) 1013 } 1014 } 1015 1016 // Unwatch unwatches all the collections. 1017 func (b *allWatcherStateBacking) Unwatch(in chan<- watcher.Change) { 1018 for _, c := range b.collectionByName { 1019 b.watcher.UnwatchCollection(c.name, in) 1020 } 1021 } 1022 1023 // GetAll fetches all items that we want to watch from the state. 1024 func (b *allWatcherStateBacking) GetAll(all *multiwatcherStore) error { 1025 err := loadAllWatcherEntities(b.st, b.collectionByName, all) 1026 return errors.Trace(err) 1027 } 1028 1029 // Changed updates the allWatcher's idea of the current state 1030 // in response to the given change. 1031 func (b *allWatcherStateBacking) Changed(all *multiwatcherStore, change watcher.Change) error { 1032 c, ok := b.collectionByName[change.C] 1033 if !ok { 1034 return errors.Errorf("unknown collection %q in fetch request", change.C) 1035 } 1036 col, closer := b.st.getCollection(c.name) 1037 defer closer() 1038 doc := reflect.New(c.docType).Interface().(backingEntityDoc) 1039 1040 id := b.st.localID(change.Id.(string)) 1041 1042 // TODO(rog) investigate ways that this can be made more efficient 1043 // than simply fetching each entity in turn. 1044 // TODO(rog) avoid fetching documents that we have no interest 1045 // in, such as settings changes to entities we don't care about. 1046 err := col.FindId(id).One(doc) 1047 if err == mgo.ErrNotFound { 1048 err := doc.removed(all, b.st.ModelUUID(), id, b.st) 1049 return errors.Trace(err) 1050 } 1051 if err != nil { 1052 return err 1053 } 1054 return doc.updated(b.st, all, id) 1055 } 1056 1057 // Release implements the Backing interface. 1058 func (b *allWatcherStateBacking) Release() error { 1059 // allWatcherStateBacking doesn't need to release anything. 1060 return nil 1061 } 1062 1063 func NewAllModelWatcherStateBacking(st *State) Backing { 1064 collections := makeAllWatcherCollectionInfo( 1065 modelsC, 1066 machinesC, 1067 unitsC, 1068 applicationsC, 1069 relationsC, 1070 annotationsC, 1071 statusesC, 1072 constraintsC, 1073 settingsC, 1074 openedPortsC, 1075 ) 1076 return &allModelWatcherStateBacking{ 1077 st: st, 1078 watcher: st.workers.TxnLogWatcher(), 1079 stPool: NewStatePool(st), 1080 collectionByName: collections, 1081 } 1082 } 1083 1084 // Watch watches all the collections. 1085 func (b *allModelWatcherStateBacking) Watch(in chan<- watcher.Change) { 1086 for _, c := range b.collectionByName { 1087 b.watcher.WatchCollection(c.name, in) 1088 } 1089 } 1090 1091 // Unwatch unwatches all the collections. 1092 func (b *allModelWatcherStateBacking) Unwatch(in chan<- watcher.Change) { 1093 for _, c := range b.collectionByName { 1094 b.watcher.UnwatchCollection(c.name, in) 1095 } 1096 } 1097 1098 // GetAll fetches all items that we want to watch from the state. 1099 func (b *allModelWatcherStateBacking) GetAll(all *multiwatcherStore) error { 1100 models, err := b.st.AllModels() 1101 if err != nil { 1102 return errors.Annotate(err, "error loading models") 1103 } 1104 for _, m := range models { 1105 if err := b.loadAllWatcherEntitiesForModel(m, all); err != nil { 1106 return errors.Trace(err) 1107 } 1108 } 1109 return nil 1110 } 1111 1112 func (b *allModelWatcherStateBacking) loadAllWatcherEntitiesForModel(m *Model, all *multiwatcherStore) error { 1113 st, err := b.st.ForModel(m.ModelTag()) 1114 if err != nil { 1115 return errors.Trace(err) 1116 } 1117 defer st.Close() 1118 1119 err = loadAllWatcherEntities(st, b.collectionByName, all) 1120 if err != nil { 1121 return errors.Annotatef(err, "error loading entities for model %v", m.UUID()) 1122 } 1123 return nil 1124 } 1125 1126 // Changed updates the allWatcher's idea of the current state 1127 // in response to the given change. 1128 func (b *allModelWatcherStateBacking) Changed(all *multiwatcherStore, change watcher.Change) error { 1129 c, ok := b.collectionByName[change.C] 1130 if !ok { 1131 return errors.Errorf("unknown collection %q in fetch request", change.C) 1132 } 1133 1134 modelUUID, id, err := b.idForChange(change) 1135 if err != nil { 1136 return errors.Trace(err) 1137 } 1138 1139 doc := reflect.New(c.docType).Interface().(backingEntityDoc) 1140 1141 st, err := b.getState(change.C, modelUUID) 1142 if err != nil { 1143 _, envErr := b.st.GetModel(names.NewModelTag(modelUUID)) 1144 if errors.IsNotFound(envErr) { 1145 // The entity's model is gone so remove the entity 1146 // from the store. 1147 doc.removed(all, modelUUID, id, nil) 1148 return nil 1149 } 1150 return errors.Trace(err) 1151 } 1152 1153 col, closer := st.getCollection(c.name) 1154 defer closer() 1155 1156 // TODO - see TODOs in allWatcherStateBacking.Changed() 1157 err = col.FindId(id).One(doc) 1158 if err == mgo.ErrNotFound { 1159 err := doc.removed(all, modelUUID, id, st) 1160 return errors.Trace(err) 1161 } 1162 if err != nil { 1163 return err 1164 } 1165 return doc.updated(st, all, id) 1166 } 1167 1168 func (b *allModelWatcherStateBacking) idForChange(change watcher.Change) (string, string, error) { 1169 if change.C == modelsC { 1170 modelUUID := change.Id.(string) 1171 return modelUUID, modelUUID, nil 1172 } 1173 1174 modelUUID, id, ok := splitDocID(change.Id.(string)) 1175 if !ok { 1176 return "", "", errors.Errorf("unknown id format: %v", change.Id.(string)) 1177 } 1178 return modelUUID, id, nil 1179 } 1180 1181 func (b *allModelWatcherStateBacking) getState(collName, modelUUID string) (*State, error) { 1182 if collName == modelsC { 1183 return b.st, nil 1184 } 1185 1186 st, err := b.stPool.Get(modelUUID) 1187 if err != nil { 1188 return nil, errors.Trace(err) 1189 } 1190 return st, nil 1191 } 1192 1193 // Release implements the Backing interface. 1194 func (b *allModelWatcherStateBacking) Release() error { 1195 err := b.stPool.Close() 1196 return errors.Trace(err) 1197 } 1198 1199 func loadAllWatcherEntities(st *State, collectionByName map[string]allWatcherStateCollection, all *multiwatcherStore) error { 1200 // Use a single new MongoDB connection for all the work here. 1201 db, closer := st.newDB() 1202 defer closer() 1203 1204 // TODO(rog) fetch collections concurrently? 1205 for _, c := range collectionByName { 1206 if c.subsidiary { 1207 continue 1208 } 1209 col, closer := db.GetCollection(c.name) 1210 defer closer() 1211 infoSlicePtr := reflect.New(reflect.SliceOf(c.docType)) 1212 if err := col.Find(nil).All(infoSlicePtr.Interface()); err != nil { 1213 return errors.Errorf("cannot get all %s: %v", c.name, err) 1214 } 1215 infos := infoSlicePtr.Elem() 1216 for i := 0; i < infos.Len(); i++ { 1217 info := infos.Index(i).Addr().Interface().(backingEntityDoc) 1218 id := info.mongoId() 1219 err := info.updated(st, all, id) 1220 if err != nil { 1221 return errors.Annotatef(err, "failed to initialise backing for %s:%v", c.name, id) 1222 } 1223 } 1224 } 1225 1226 return nil 1227 } 1228 1229 func normaliseStatusData(data map[string]interface{}) map[string]interface{} { 1230 if data == nil { 1231 return make(map[string]interface{}) 1232 } 1233 return data 1234 }