github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/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/charm/v12" 11 "github.com/juju/errors" 12 "github.com/juju/mgo/v3" 13 "github.com/juju/mgo/v3/bson" 14 15 corebase "github.com/juju/juju/core/base" 16 "github.com/juju/juju/core/constraints" 17 "github.com/juju/juju/core/life" 18 "github.com/juju/juju/core/model" 19 "github.com/juju/juju/core/multiwatcher" 20 "github.com/juju/juju/core/network" 21 "github.com/juju/juju/core/permission" 22 "github.com/juju/juju/core/status" 23 "github.com/juju/juju/environs/config" 24 "github.com/juju/juju/state/watcher" 25 ) 26 27 var allWatcherLogger = logger.Child("allwatcher") 28 29 // allWatcherBacking implements AllWatcherBacking by fetching entities 30 // for all models from the State. 31 type allWatcherBacking struct { 32 watcher watcher.BaseWatcher 33 stPool *StatePool 34 collections []string 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 instanceDataC: 69 collection.docType = reflect.TypeOf(backingInstanceData{}) 70 collection.subsidiary = true 71 case unitsC: 72 collection.docType = reflect.TypeOf(backingUnit{}) 73 case applicationsC: 74 collection.docType = reflect.TypeOf(backingApplication{}) 75 case charmsC: 76 collection.docType = reflect.TypeOf(backingCharm{}) 77 case actionsC: 78 collection.docType = reflect.TypeOf(backingAction{}) 79 case relationsC: 80 collection.docType = reflect.TypeOf(backingRelation{}) 81 case annotationsC: 82 collection.docType = reflect.TypeOf(backingAnnotation{}) 83 // TODO: this should be a subsidiary too. 84 case blocksC: 85 collection.docType = reflect.TypeOf(backingBlock{}) 86 case statusesC: 87 collection.docType = reflect.TypeOf(backingStatus{}) 88 collection.subsidiary = true 89 case constraintsC: 90 collection.docType = reflect.TypeOf(backingConstraints{}) 91 collection.subsidiary = true 92 case settingsC: 93 collection.docType = reflect.TypeOf(backingSettings{}) 94 collection.subsidiary = true 95 case openedPortsC: 96 collection.docType = reflect.TypeOf(backingOpenedPorts{}) 97 collection.subsidiary = true 98 case remoteApplicationsC: 99 collection.docType = reflect.TypeOf(backingRemoteApplication{}) 100 case applicationOffersC: 101 collection.docType = reflect.TypeOf(backingApplicationOffer{}) 102 case generationsC: 103 collection.docType = reflect.TypeOf(backingGeneration{}) 104 case permissionsC: 105 // Permissions are attached to the Model that they are for. 106 collection.docType = reflect.TypeOf(backingPermission{}) 107 collection.subsidiary = true 108 case podSpecsC: 109 collection.docType = reflect.TypeOf(backingPodSpec{}) 110 collection.subsidiary = true 111 default: 112 allWatcherLogger.Criticalf("programming error: unknown collection %q", collName) 113 } 114 115 docType := collection.docType 116 if _, ok := seenTypes[docType]; ok { 117 allWatcherLogger.Criticalf("programming error: duplicate collection type %s", docType) 118 } else { 119 seenTypes[docType] = struct{}{} 120 } 121 122 if _, ok := collectionByName[collName]; ok { 123 allWatcherLogger.Criticalf("programming error: duplicate collection name %q", collName) 124 } else { 125 collectionByName[collName] = collection 126 } 127 } 128 129 return collectionByName 130 } 131 132 type backingModel modelDoc 133 134 func (e *backingModel) isNotFoundAndModelDead(err error) bool { 135 // Return true if the error is not found and the model is dead. 136 // This will be the case if the model has been marked dead, pending cleanup. 137 return errors.IsNotFound(err) && e.Life == Dead 138 } 139 140 func (e *backingModel) updated(ctx *allWatcherContext) error { 141 allWatcherLogger.Tracef(`model "%s" updated`, ctx.id) 142 143 // Update the context with the model type. 144 ctx.modelType_ = e.Type 145 info := &multiwatcher.ModelInfo{ 146 ModelUUID: e.UUID, 147 Type: model.ModelType(e.Type), 148 Name: e.Name, 149 Life: life.Value(e.Life.String()), 150 Owner: e.Owner, 151 ControllerUUID: e.ControllerUUID, 152 IsController: ctx.state.IsController(), 153 Cloud: e.Cloud, 154 CloudRegion: e.CloudRegion, 155 CloudCredential: e.CloudCredential, 156 SLA: multiwatcher.ModelSLAInfo{ 157 Level: e.SLA.Level.String(), 158 Owner: e.SLA.Owner, 159 }, 160 } 161 162 oldInfo := ctx.store.Get(info.EntityID()) 163 if oldInfo == nil { 164 settings, err := ctx.getSettings(modelGlobalKey) 165 if e.isNotFoundAndModelDead(err) { 166 // Since we know this isn't in the store, stop looking for new 167 // things. 168 return nil 169 } 170 if err != nil { 171 return errors.Trace(err) 172 } 173 cfg, err := config.New(config.NoDefaults, settings) 174 if err != nil { 175 return errors.Trace(err) 176 } 177 178 info.Config = cfg.AllAttrs() 179 180 // Annotations are optional, so may not be there. 181 info.Annotations = ctx.getAnnotations(modelGlobalKey) 182 183 c, err := ctx.readConstraints(modelGlobalKey) 184 if e.isNotFoundAndModelDead(err) { 185 // Since we know this isn't in the store, stop looking for new 186 // things. 187 return nil 188 } 189 if err != nil { 190 return errors.Trace(err) 191 } 192 info.Constraints = c 193 194 info.Status, err = ctx.getStatus(modelGlobalKey, "model") 195 if e.isNotFoundAndModelDead(err) { 196 // Since we know this isn't in the store, stop looking for new 197 // things. 198 return nil 199 } 200 if err != nil { 201 return errors.Trace(err) 202 } 203 204 permissions, err := ctx.permissionsForModel(e.UUID) 205 if err != nil { 206 return errors.Trace(err) 207 } 208 209 info.UserPermissions = permissions 210 } else { 211 oldInfo := oldInfo.(*multiwatcher.ModelInfo) 212 info.Annotations = oldInfo.Annotations 213 info.Config = oldInfo.Config 214 info.Constraints = oldInfo.Constraints 215 info.Status = oldInfo.Status 216 info.UserPermissions = oldInfo.UserPermissions 217 } 218 219 ctx.store.Update(info) 220 return nil 221 } 222 223 func (e *backingModel) removed(ctx *allWatcherContext) error { 224 allWatcherLogger.Tracef(`model "%s" removed`, ctx.id) 225 ctx.removeFromStore(multiwatcher.ModelKind) 226 return nil 227 } 228 229 func (e *backingModel) mongoID() string { 230 return e.UUID 231 } 232 233 type backingPermission permissionDoc 234 235 func (e *backingPermission) modelAndUser(id string) (string, string, bool) { 236 parts := strings.Split(id, "#") 237 238 if len(parts) < 4 { 239 // Not valid for as far as we care about. 240 return "", "", false 241 } 242 243 // At this stage, we are only dealing with model user permissions. 244 if parts[0] != modelGlobalKey || parts[2] != userGlobalKeyPrefix { 245 return "", "", false 246 } 247 return parts[1], parts[3], true 248 } 249 250 func (e *backingPermission) updated(ctx *allWatcherContext) error { 251 allWatcherLogger.Tracef(`permission "%s" updated`, ctx.id) 252 253 modelUUID, user, ok := e.modelAndUser(ctx.id) 254 if !ok { 255 // Not valid for as far as we care about. 256 return nil 257 } 258 259 info := e.getModelInfo(ctx, modelUUID) 260 if info == nil { 261 return nil 262 } 263 264 // Set the access for the user in the permission map of the model. 265 info.UserPermissions[user] = permission.Access(e.Access) 266 267 ctx.store.Update(info) 268 return nil 269 } 270 271 func (e *backingPermission) removed(ctx *allWatcherContext) error { 272 allWatcherLogger.Tracef(`permission "%s" removed`, ctx.id) 273 274 modelUUID, user, ok := e.modelAndUser(ctx.id) 275 if !ok { 276 // Not valid for as far as we care about. 277 return nil 278 } 279 280 info := e.getModelInfo(ctx, modelUUID) 281 if info == nil { 282 return nil 283 } 284 285 delete(info.UserPermissions, user) 286 287 ctx.store.Update(info) 288 return nil 289 } 290 291 func (e *backingPermission) getModelInfo(ctx *allWatcherContext, modelUUID string) *multiwatcher.ModelInfo { 292 // NOTE: we can't use the modelUUID from the ctx here because it is the 293 // modelUUID of the system state. 294 storeKey := &multiwatcher.ModelInfo{ 295 ModelUUID: modelUUID, 296 } 297 info0 := ctx.store.Get(storeKey.EntityID()) 298 switch info := info0.(type) { 299 case *multiwatcher.ModelInfo: 300 return info 301 } 302 // In all other cases, which really should be never, return nil. 303 return nil 304 } 305 306 func (e *backingPermission) mongoID() string { 307 allWatcherLogger.Criticalf("programming error: attempting to get mongoID from permissions document") 308 return "" 309 } 310 311 type backingMachine machineDoc 312 313 func (m *backingMachine) updateAgentVersion(info *multiwatcher.MachineInfo) { 314 if m.Tools != nil { 315 info.AgentStatus.Version = m.Tools.Version.Number.String() 316 } 317 } 318 319 func (m *backingMachine) updated(ctx *allWatcherContext) error { 320 allWatcherLogger.Tracef(`machine "%s:%s" updated`, ctx.modelUUID, ctx.id) 321 wantsVote := false 322 hasVote := false 323 if ctx.state.IsController() { 324 // We can handle an extra query here as long as it is only for controller 325 // machines. Could potentially optimize further if necessary for initial load. 326 node, err := ctx.state.ControllerNode(m.Id) 327 if err != nil && !errors.IsNotFound(err) { 328 return errors.Trace(err) 329 } 330 wantsVote = err == nil && node.WantsVote() 331 hasVote = err == nil && node.HasVote() 332 } 333 modelID, _, _ := ctx.entityIDForGlobalKey(modelGlobalKey) 334 modelEntity := ctx.store.Get(modelID) 335 var providerType string 336 if modelEntity != nil { 337 modelInfo := modelEntity.(*multiwatcher.ModelInfo) 338 providerType, _ = modelInfo.Config["type"].(string) 339 340 } 341 isManual := isManualMachine(m.Id, m.Nonce, providerType) 342 base, err := corebase.ParseBase(m.Base.OS, m.Base.Channel) 343 if err != nil { 344 return errors.Trace(err) 345 } 346 info := &multiwatcher.MachineInfo{ 347 ModelUUID: m.ModelUUID, 348 ID: m.Id, 349 Life: life.Value(m.Life.String()), 350 Base: base.DisplayString(), 351 ContainerType: m.ContainerType, 352 IsManual: isManual, 353 Jobs: paramsJobsFromJobs(m.Jobs), 354 SupportedContainers: m.SupportedContainers, 355 SupportedContainersKnown: m.SupportedContainersKnown, 356 HasVote: hasVote, 357 WantsVote: wantsVote, 358 PreferredPublicAddress: m.PreferredPublicAddress.networkAddress(), 359 PreferredPrivateAddress: m.PreferredPrivateAddress.networkAddress(), 360 Hostname: m.Hostname, 361 } 362 addresses := network.MergedAddresses(networkAddresses(m.MachineAddresses), networkAddresses(m.Addresses)) 363 for _, addr := range addresses { 364 mAddr := network.ProviderAddress{ 365 MachineAddress: addr.MachineAddress, 366 } 367 368 spaceID := addr.SpaceID 369 if spaceID != network.AlphaSpaceId && spaceID != "" { 370 // TODO: cache spaces 371 space, err := ctx.state.Space(spaceID) 372 if err != nil { 373 return errors.Annotatef(err, "retrieving space for ID %q", spaceID) 374 } 375 mAddr.SpaceName = network.SpaceName(space.Name()) 376 mAddr.ProviderSpaceID = space.ProviderId() 377 } 378 379 info.Addresses = append(info.Addresses, mAddr) 380 } 381 382 oldInfo := ctx.store.Get(info.EntityID()) 383 if oldInfo == nil { 384 key := machineGlobalKey(m.Id) 385 agentStatus, err := ctx.getStatus(key, "machine agent") 386 if err != nil { 387 return errors.Annotatef(err, "reading machine agent for key %s", key) 388 } 389 info.AgentStatus = agentStatus 390 391 key = machineGlobalInstanceKey(m.Id) 392 instanceStatus, err := ctx.getStatus(key, "machine instance") 393 if err != nil { 394 return errors.Annotatef(err, "reading machine instance for key %s", key) 395 } 396 info.InstanceStatus = instanceStatus 397 398 // Annotations are optional, so may not be there. 399 info.Annotations = ctx.getAnnotations(key) 400 } else { 401 // The entry already exists, so preserve the current status and 402 // instance data. These will be updated as necessary as the status and instance data 403 // updates come through. 404 oldInfo := oldInfo.(*multiwatcher.MachineInfo) 405 info.AgentStatus = oldInfo.AgentStatus 406 info.Annotations = oldInfo.Annotations 407 info.InstanceStatus = oldInfo.InstanceStatus 408 info.InstanceID = oldInfo.InstanceID 409 info.HardwareCharacteristics = oldInfo.HardwareCharacteristics 410 } 411 m.updateAgentVersion(info) 412 413 // If the machine is been provisioned, fetch the instance id as required, 414 // and set instance id and hardware characteristics. 415 instanceData, err := ctx.getInstanceData(m.Id) 416 if err == nil { 417 if m.Nonce != "" && info.InstanceID == "" { 418 info.InstanceID = string(instanceData.InstanceId) 419 info.HardwareCharacteristics = hardwareCharacteristics(instanceData) 420 } 421 // InstanceMutater needs the liveliness of the instanceData.CharmProfiles 422 // as this changes with charm-upgrades 423 info.CharmProfiles = instanceData.CharmProfiles 424 } else if !errors.IsNotFound(err) { 425 return err 426 } 427 428 ctx.store.Update(info) 429 return nil 430 } 431 432 func (m *backingMachine) removed(ctx *allWatcherContext) error { 433 allWatcherLogger.Tracef(`machine "%s:%s" removed`, ctx.modelUUID, ctx.id) 434 ctx.removeFromStore(multiwatcher.MachineKind) 435 return nil 436 } 437 438 func (m *backingMachine) mongoID() string { 439 return m.Id 440 } 441 442 type backingInstanceData instanceData 443 444 func (i *backingInstanceData) updated(ctx *allWatcherContext) error { 445 allWatcherLogger.Tracef(`instance data "%s:%s" updated`, ctx.modelUUID, ctx.id) 446 parentID, _, ok := ctx.entityIDForGlobalKey(machineGlobalKey(ctx.id)) 447 if !ok { 448 return nil 449 } 450 451 info0 := ctx.store.Get(parentID) 452 switch info := info0.(type) { 453 case nil: 454 // The parent info doesn't exist. Ignore the status until it does. 455 return nil 456 case *multiwatcher.MachineInfo: 457 newInfo := *info 458 var instanceData *instanceData = (*instanceData)(i) 459 newInfo.HardwareCharacteristics = hardwareCharacteristics(*instanceData) 460 newInfo.CharmProfiles = instanceData.CharmProfiles 461 info0 = &newInfo 462 default: 463 return errors.Errorf("instanceData for unexpected entity with id %q; type %T", ctx.id, info) 464 } 465 ctx.store.Update(info0) 466 return nil 467 } 468 469 func (i *backingInstanceData) removed(ctx *allWatcherContext) error { 470 // If the instanceData is removed, the machine will follow not long 471 // after so do nothing. 472 return nil 473 } 474 475 func (i *backingInstanceData) mongoID() string { 476 // This is a subsidiary collection, we shouldn't be calling mongoID. 477 return i.MachineId 478 } 479 480 type backingUnit unitDoc 481 482 func (u *backingUnit) unitAndAgentStatus(ctx *allWatcherContext, info *multiwatcher.UnitInfo) error { 483 unitStatusResult, err := ctx.getStatus(unitGlobalKey(u.Name), "unit") 484 if err != nil { 485 return errors.Trace(err) 486 } 487 488 agentStatusResult, err := ctx.getStatus(unitAgentGlobalKey(u.Name), "unit") 489 if err != nil { 490 return errors.Trace(err) 491 } 492 493 // NOTE: c.f. *Unit.Status(), we need to deal with the error state. 494 if agentStatusResult.Current == status.Error { 495 since := agentStatusResult.Since 496 unitStatusResult = agentStatusResult 497 agentStatusResult = multiwatcher.StatusInfo{ 498 Current: status.Idle, 499 Data: normaliseStatusData(nil), 500 Since: since, 501 } 502 } 503 504 // Unit and workload status. 505 info.WorkloadStatus = unitStatusResult 506 info.AgentStatus = agentStatusResult 507 return nil 508 } 509 510 func (u *backingUnit) updateAgentVersion(info *multiwatcher.UnitInfo) { 511 if u.Tools != nil { 512 info.AgentStatus.Version = u.Tools.Version.Number.String() 513 } 514 } 515 516 func (u *backingUnit) updated(ctx *allWatcherContext) error { 517 allWatcherLogger.Tracef(`unit "%s:%s" updated`, ctx.modelUUID, ctx.id) 518 base, err := corebase.ParseBase(u.Base.OS, u.Base.Channel) 519 if err != nil { 520 return errors.Trace(err) 521 } 522 info := &multiwatcher.UnitInfo{ 523 ModelUUID: u.ModelUUID, 524 Name: u.Name, 525 Application: u.Application, 526 Base: base.DisplayString(), 527 Life: life.Value(u.Life.String()), 528 MachineID: u.MachineId, 529 Principal: u.Principal, 530 Subordinate: u.Principal != "", 531 OpenPortRangesByEndpoint: make(network.GroupedPortRanges), 532 } 533 if u.CharmURL != nil { 534 info.CharmURL = *u.CharmURL 535 } 536 537 // Construct a unit for the purpose of retrieving other fields as necessary. 538 modelType, err := ctx.modelType() 539 if err != nil { 540 return errors.Annotatef(err, "get model type for %q", ctx.modelUUID) 541 } 542 var unitDoc unitDoc = unitDoc(*u) 543 unit := newUnit(ctx.state, modelType, &unitDoc) 544 545 oldInfo := ctx.store.Get(info.EntityID()) 546 if oldInfo == nil { 547 allWatcherLogger.Debugf("new unit %q added to backing state", u.Name) 548 549 // Annotations are optional, so may not be there. 550 info.Annotations = ctx.getAnnotations(unitGlobalKey(u.Name)) 551 552 // We're adding the entry for the first time, 553 // so fetch the associated unit status and opened ports. 554 err := u.unitAndAgentStatus(ctx, info) 555 if err != nil { 556 return errors.Annotatef(err, "retrieve unit and agent status for %q", u.Name) 557 } 558 unitPortRangesByEndpoint, err := ctx.getUnitPortRangesByEndpoint(unit) 559 if err != nil { 560 return errors.Trace(err) 561 } 562 if len(unitPortRangesByEndpoint) > 0 { 563 info.OpenPortRangesByEndpoint = unitPortRangesByEndpoint.Clone() 564 } 565 if modelType == ModelTypeCAAS { 566 containerStatus, err := ctx.getStatus(globalCloudContainerKey(u.Name), "cloud container") 567 if err == nil { 568 info.ContainerStatus = containerStatus 569 } 570 } 571 } else { 572 // The entry already exists, so preserve the current status and ports. 573 oldInfo := oldInfo.(*multiwatcher.UnitInfo) 574 info.Annotations = oldInfo.Annotations 575 // Unit and workload status. 576 info.AgentStatus = oldInfo.AgentStatus 577 info.WorkloadStatus = oldInfo.WorkloadStatus 578 info.ContainerStatus = oldInfo.ContainerStatus 579 info.OpenPortRangesByEndpoint = oldInfo.OpenPortRangesByEndpoint 580 } 581 582 u.updateAgentVersion(info) 583 584 // This is horrible as we are loading the machine twice for every unit. 585 // Can't optimize this yet. 586 // TODO: deprecate this ASAP and remove ASAP. It is only there for backwards 587 // compatibility to 1.18. 588 publicAddress, privateAddress, err := ctx.getUnitAddresses(unit) 589 if err != nil { 590 return errors.Annotatef(err, "get addresses for %q", u.Name) 591 } 592 info.PublicAddress = publicAddress 593 info.PrivateAddress = privateAddress 594 ctx.store.Update(info) 595 return nil 596 } 597 598 // getUnitAddresses returns the public and private addresses on a given unit. 599 // As of 1.18, the addresses are stored on the assigned machine but we retain 600 // this approach for backwards compatibility. 601 func (ctx *allWatcherContext) getUnitAddresses(u *Unit) (string, string, error) { 602 // If we are dealing with a CAAS unit, use the unit methods, they 603 // are complicated and not yet mirrored in the allwatcher. Also there 604 // are entities in CAAS models that should probably be exposed up to the 605 // model cache, but haven't yet. 606 modelType, err := ctx.modelType() 607 if err != nil { 608 return "", "", errors.Annotatef(err, "get model type for %q", ctx.modelUUID) 609 } 610 if modelType == ModelTypeCAAS { 611 publicAddress, err := u.PublicAddress() 612 if err != nil { 613 allWatcherLogger.Tracef("getting a public address for unit %q failed: %q", u.Name(), err) 614 } 615 privateAddress, err := u.PrivateAddress() 616 if err != nil { 617 allWatcherLogger.Tracef("getting a private address for unit %q failed: %q", u.Name(), err) 618 } 619 return publicAddress.Value, privateAddress.Value, nil 620 } 621 622 machineID, _ := u.AssignedMachineId() 623 if machineID == "" { 624 return "", "", nil 625 } 626 // Get the machine out of the store and use the preferred public and 627 // preferred private addresses out of that. 628 machineInfo := ctx.getMachineInfo(machineID) 629 if machineInfo == nil { 630 // We know that the machines are processed before the units, so they 631 // will always be there when we are looking. Except for the case where 632 // we are in the process of deleting the machine or units as they are 633 // being destroyed. If this is the case, we don't really care about 634 // the addresses, so returning empty values is fine. 635 return "", "", nil 636 } 637 return machineInfo.PreferredPublicAddress.Value, machineInfo.PreferredPrivateAddress.Value, nil 638 } 639 640 func (u *backingUnit) removed(ctx *allWatcherContext) error { 641 allWatcherLogger.Tracef(`unit "%s:%s" removed`, ctx.modelUUID, ctx.id) 642 ctx.removeFromStore(multiwatcher.UnitKind) 643 return nil 644 } 645 646 func (u *backingUnit) mongoID() string { 647 return u.Name 648 } 649 650 type backingApplication applicationDoc 651 652 func (app *backingApplication) updated(ctx *allWatcherContext) error { 653 allWatcherLogger.Tracef(`application "%s:%s" updated`, ctx.modelUUID, ctx.id) 654 if app.CharmURL == nil { 655 return errors.Errorf("charm url is nil") 656 } 657 info := &multiwatcher.ApplicationInfo{ 658 ModelUUID: app.ModelUUID, 659 Name: app.Name, 660 Exposed: app.Exposed, 661 CharmURL: *app.CharmURL, 662 Life: life.Value(app.Life.String()), 663 MinUnits: app.MinUnits, 664 Subordinate: app.Subordinate, 665 } 666 oldInfo := ctx.store.Get(info.EntityID()) 667 needConfig := false 668 if oldInfo == nil { 669 allWatcherLogger.Debugf("new application %q added to backing state", app.Name) 670 key := applicationGlobalKey(app.Name) 671 // Annotations are optional, so may not be there. 672 info.Annotations = ctx.getAnnotations(key) 673 // We're adding the entry for the first time, 674 // so fetch the associated child documents. 675 c, err := ctx.readConstraints(key) 676 if err != nil { 677 return errors.Trace(err) 678 } 679 info.Constraints = c 680 needConfig = true 681 applicationStatus, err := ctx.getStatus(key, "application") 682 if err != nil { 683 return errors.Annotatef(err, "reading application status for key %s", key) 684 } 685 info.Status = applicationStatus 686 // OperatorStatus is only available for CAAS applications. 687 // So if we don't find it, don't worry. 688 modelType, err := ctx.modelType() 689 if err != nil { 690 return errors.Annotatef(err, "get model type for %q", ctx.modelUUID) 691 } 692 if modelType == ModelTypeCAAS { 693 // Look for the PodSpec for this application. 694 var doc backingPodSpec 695 if err := readPodInfo(ctx.state.db(), app.Name, &doc); err != nil { 696 if errors.IsNotFound(err) { 697 // This is expected in some situations, there just hasn't 698 // been a call to set the pod spec. 699 } else { 700 return errors.Annotatef(err, "get podSpec for %s", app.Name) 701 } 702 } else { 703 info.PodSpec = doc.asPodSpec() 704 } 705 key = applicationGlobalOperatorKey(app.Name) 706 operatorStatus, err := ctx.getStatus(key, "application operator") 707 if err == nil { 708 info.OperatorStatus = operatorStatus 709 } 710 } 711 } else { 712 // The entry already exists, so preserve the current status. 713 appInfo := oldInfo.(*multiwatcher.ApplicationInfo) 714 info.Annotations = appInfo.Annotations 715 info.Constraints = appInfo.Constraints 716 info.WorkloadVersion = appInfo.WorkloadVersion 717 if info.CharmURL == appInfo.CharmURL { 718 // The charm URL remains the same - we can continue to 719 // use the same config settings. 720 info.Config = appInfo.Config 721 } else { 722 // The charm URL has changed - we need to fetch the 723 // settings from the new charm's settings doc. 724 needConfig = true 725 } 726 info.Status = appInfo.Status 727 info.OperatorStatus = appInfo.OperatorStatus 728 info.PodSpec = appInfo.PodSpec 729 } 730 if needConfig { 731 config, err := ctx.getSettings(applicationCharmConfigKey(app.Name, app.CharmURL)) 732 if err != nil { 733 return errors.Annotatef(err, "application %q", app.Name) 734 } 735 info.Config = config 736 } 737 ctx.store.Update(info) 738 return nil 739 } 740 741 func (app *backingApplication) removed(ctx *allWatcherContext) error { 742 allWatcherLogger.Tracef(`application "%s:%s" removed`, ctx.modelUUID, ctx.id) 743 ctx.removeFromStore(multiwatcher.ApplicationKind) 744 return nil 745 } 746 747 func (app *backingApplication) mongoID() string { 748 return app.Name 749 } 750 751 type backingPodSpec containerSpecDoc 752 753 func (ps *backingPodSpec) updated(ctx *allWatcherContext) error { 754 allWatcherLogger.Tracef(`podspec "%s:%s" updated`, ctx.modelUUID, ctx.id) 755 756 // The id of the podspec is the application global key. 757 parentID, _, ok := ctx.entityIDForGlobalKey(ctx.id) 758 if !ok { 759 return nil 760 } 761 info0 := ctx.store.Get(parentID) 762 switch info := info0.(type) { 763 case nil: 764 // The parent info doesn't exist. Ignore until it does. 765 return nil 766 case *multiwatcher.ApplicationInfo: 767 newInfo := *info 768 newInfo.PodSpec = ps.asPodSpec() 769 info0 = &newInfo 770 default: 771 allWatcherLogger.Warningf("unexpected podspec type: %T", info) 772 return nil 773 } 774 ctx.store.Update(info0) 775 return nil 776 } 777 778 func (ps *backingPodSpec) asPodSpec() *multiwatcher.PodSpec { 779 podSpec := &multiwatcher.PodSpec{ 780 Spec: ps.Spec, 781 Counter: ps.UpgradeCounter, 782 } 783 if len(podSpec.Spec) == 0 && len(ps.RawSpec) > 0 { 784 podSpec.Spec = ps.RawSpec 785 podSpec.Raw = true 786 } 787 return podSpec 788 } 789 790 func (ps *backingPodSpec) removed(ctx *allWatcherContext) error { 791 allWatcherLogger.Tracef(`podspec "%s:%s" removed`, ctx.modelUUID, ctx.id) 792 // The podSpec is only removed when the application is removed, so we don't care. 793 return nil 794 } 795 796 func (ps *backingPodSpec) mongoID() string { 797 allWatcherLogger.Criticalf("programming error: attempting to get mongoID from podspec document") 798 return "" 799 } 800 801 type backingCharm charmDoc 802 803 func (ch *backingCharm) updated(ctx *allWatcherContext) error { 804 allWatcherLogger.Tracef(`charm "%s:%s" updated`, ctx.modelUUID, ctx.id) 805 info := &multiwatcher.CharmInfo{ 806 ModelUUID: ch.ModelUUID, 807 CharmURL: *ch.URL, 808 CharmVersion: ch.CharmVersion, 809 Life: life.Value(ch.Life.String()), 810 } 811 812 if ch.LXDProfile != nil && !ch.LXDProfile.Empty() { 813 info.LXDProfile = toMultiwatcherProfile(ch.LXDProfile) 814 } 815 816 if ch.Config != nil { 817 if ds := ch.Config.DefaultSettings(); len(ds) > 0 { 818 info.DefaultConfig = ds 819 } 820 } 821 822 ctx.store.Update(info) 823 return nil 824 } 825 826 func (ch *backingCharm) removed(ctx *allWatcherContext) error { 827 allWatcherLogger.Tracef(`charm "%s:%s" removed`, ctx.modelUUID, ctx.id) 828 ctx.removeFromStore(multiwatcher.CharmKind) 829 return nil 830 } 831 832 func (ch *backingCharm) mongoID() string { 833 _, id, ok := splitDocID(ch.DocID) 834 if !ok { 835 allWatcherLogger.Criticalf("charm ID not valid: %v", ch.DocID) 836 } 837 return id 838 } 839 840 func toMultiwatcherProfile(profile *LXDProfile) *multiwatcher.Profile { 841 unescapedProfile := unescapeLXDProfile(profile) 842 return &multiwatcher.Profile{ 843 Config: unescapedProfile.Config, 844 Description: unescapedProfile.Description, 845 Devices: unescapedProfile.Devices, 846 } 847 } 848 849 type backingRemoteApplication remoteApplicationDoc 850 851 func (app *backingRemoteApplication) updated(ctx *allWatcherContext) error { 852 allWatcherLogger.Tracef(`remote application "%s:%s" updated`, ctx.modelUUID, ctx.id) 853 if app.Name == "" { 854 return errors.Errorf("saas application name is not set") 855 } 856 if app.IsConsumerProxy { 857 // Since this is a consumer proxy, we update the offer 858 // info in this (the offering) model. 859 return app.updateOfferInfo(ctx) 860 } 861 info := &multiwatcher.RemoteApplicationUpdate{ 862 ModelUUID: ctx.modelUUID, // ModelUUID not part of the remoteApplicationDoc 863 Name: app.Name, 864 OfferURL: app.URL, 865 Life: life.Value(app.Life.String()), 866 } 867 oldInfo := ctx.store.Get(info.EntityID()) 868 if oldInfo == nil { 869 allWatcherLogger.Debugf("new remote application %q added to backing state", app.Name) 870 // Fetch the status. 871 key := remoteApplicationGlobalKey(app.Name) 872 appStatus, err := ctx.getStatus(key, "saas application") 873 if err != nil { 874 return errors.Annotatef(err, "reading remote application status for key %s", key) 875 } 876 info.Status = appStatus 877 allWatcherLogger.Debugf("saas application status %#v", info.Status) 878 } else { 879 allWatcherLogger.Debugf("use status from existing app") 880 switch t := oldInfo.(type) { 881 case *multiwatcher.RemoteApplicationUpdate: 882 info.Status = t.Status 883 default: 884 allWatcherLogger.Debugf("unexpected type %t", t) 885 } 886 } 887 ctx.store.Update(info) 888 return nil 889 } 890 891 func (app *backingRemoteApplication) updateOfferInfo(ctx *allWatcherContext) error { 892 entities := ctx.store.All() 893 for _, e := range entities { 894 var ( 895 offerInfo *multiwatcher.ApplicationOfferInfo 896 ok bool 897 ) 898 if offerInfo, ok = e.(*multiwatcher.ApplicationOfferInfo); !ok { 899 continue 900 } 901 if offerInfo.ModelUUID != ctx.modelUUID { 902 continue 903 } 904 // TODO: be smarter about reading status. 905 // We should only read offers relevant to the remote app that has been updated, 906 // but there would be more db reads to do the filtering than just reading the 907 // connection info for each offer, even if it might not have changed. 908 remoteConnection, err := ctx.state.RemoteConnectionStatus(offerInfo.OfferUUID) 909 if err != nil { 910 return errors.Trace(err) 911 } 912 offerInfo.TotalConnectedCount = remoteConnection.TotalConnectionCount() 913 offerInfo.ActiveConnectedCount = remoteConnection.ActiveConnectionCount() 914 ctx.store.Update(offerInfo) 915 } 916 return nil 917 } 918 919 func (app *backingRemoteApplication) removed(ctx *allWatcherContext) (err error) { 920 allWatcherLogger.Tracef(`remote application "%s:%s" removed`, ctx.modelUUID, ctx.id) 921 // TODO: see if we need the check of consumer proxy like in the change 922 if app.IsConsumerProxy { 923 err = app.updateOfferInfo(ctx) 924 } 925 if err != nil { 926 // We log the error but don't prevent the remote app removal. 927 allWatcherLogger.Errorf("updating application offer info: %v", err) 928 } 929 ctx.removeFromStore(multiwatcher.RemoteApplicationKind) 930 return err 931 } 932 933 func (app *backingRemoteApplication) mongoID() string { 934 return app.Name 935 } 936 937 type backingApplicationOffer applicationOfferDoc 938 939 func (b *backingApplicationOffer) updated(ctx *allWatcherContext) error { 940 allWatcherLogger.Tracef(`application offer "%s:%s" updated`, ctx.modelUUID, ctx.id) 941 info := &multiwatcher.ApplicationOfferInfo{ 942 ModelUUID: ctx.modelUUID, // ModelUUID not on applicationOfferDoc 943 OfferName: b.OfferName, 944 OfferUUID: b.OfferUUID, 945 ApplicationName: b.ApplicationName, 946 } 947 948 // UGH, this abstraction means we are likely doing needless queries. 949 offers := NewApplicationOffers(ctx.state) 950 offer, err := offers.ApplicationOfferForUUID(info.OfferUUID) 951 if err != nil { 952 return errors.Trace(err) 953 } 954 localApp, err := ctx.state.Application(offer.ApplicationName) 955 if err != nil { 956 return errors.Trace(err) 957 } 958 info.ApplicationName = offer.ApplicationName 959 cURL, _ := localApp.CharmURL() 960 curl, err := charm.ParseURL(*cURL) 961 if err != nil { 962 return errors.Trace(err) 963 } 964 info.CharmName = curl.Name 965 966 remoteConnection, err := ctx.state.RemoteConnectionStatus(info.OfferUUID) 967 if err != nil { 968 return errors.Trace(err) 969 } 970 info.TotalConnectedCount = remoteConnection.TotalConnectionCount() 971 info.ActiveConnectedCount = remoteConnection.ActiveConnectionCount() 972 973 ctx.store.Update(info) 974 return nil 975 } 976 977 func (b *backingApplicationOffer) removed(ctx *allWatcherContext) error { 978 allWatcherLogger.Tracef(`application offer "%s:%s" removed`, ctx.modelUUID, ctx.id) 979 ctx.removeFromStore(multiwatcher.ApplicationOfferKind) 980 return nil 981 } 982 983 func (b *backingApplicationOffer) mongoID() string { 984 return b.OfferName 985 } 986 987 type backingAction actionDoc 988 989 func (a *backingAction) mongoID() string { 990 _, id, ok := splitDocID(a.DocId) 991 if !ok { 992 allWatcherLogger.Criticalf("action ID not valid: %v", a.DocId) 993 } 994 return id 995 } 996 997 func (a *backingAction) removed(ctx *allWatcherContext) error { 998 allWatcherLogger.Tracef(`action "%s:%s" removed`, ctx.modelUUID, ctx.id) 999 ctx.removeFromStore(multiwatcher.ActionKind) 1000 return nil 1001 } 1002 1003 func (a *backingAction) updated(ctx *allWatcherContext) error { 1004 allWatcherLogger.Tracef(`action "%s:%s" updated`, ctx.modelUUID, ctx.id) 1005 info := &multiwatcher.ActionInfo{ 1006 ModelUUID: a.ModelUUID, 1007 ID: ctx.id, // local ID isn't available on the action doc 1008 Receiver: a.Receiver, 1009 Name: a.Name, 1010 Parameters: a.Parameters, 1011 Parallel: a.Parallel, 1012 ExecutionGroup: a.ExecutionGroup, 1013 Status: string(a.Status), 1014 Message: a.Message, 1015 Results: a.Results, 1016 Enqueued: a.Enqueued, 1017 Started: a.Started, 1018 Completed: a.Completed, 1019 } 1020 ctx.store.Update(info) 1021 return nil 1022 } 1023 1024 type backingRelation relationDoc 1025 1026 func (r *backingRelation) updated(ctx *allWatcherContext) error { 1027 allWatcherLogger.Tracef(`relation "%s:%s" updated`, ctx.modelUUID, ctx.id) 1028 eps := make([]multiwatcher.Endpoint, len(r.Endpoints)) 1029 for i, ep := range r.Endpoints { 1030 eps[i] = multiwatcher.Endpoint{ 1031 ApplicationName: ep.ApplicationName, 1032 Relation: newCharmRelation(ep.Relation), 1033 } 1034 } 1035 info := &multiwatcher.RelationInfo{ 1036 ModelUUID: r.ModelUUID, 1037 Key: r.Key, 1038 ID: r.Id, 1039 Endpoints: eps, 1040 } 1041 ctx.store.Update(info) 1042 return nil 1043 } 1044 1045 // newCharmRelation creates a new local CharmRelation structure from the 1046 // charm.Relation structure. NOTE: when we update the database to not store a 1047 // charm.Relation directly in the database, this method should take the state 1048 // structure type. 1049 func newCharmRelation(cr charm.Relation) multiwatcher.CharmRelation { 1050 return multiwatcher.CharmRelation{ 1051 Name: cr.Name, 1052 Role: string(cr.Role), 1053 Interface: cr.Interface, 1054 Optional: cr.Optional, 1055 Limit: cr.Limit, 1056 Scope: string(cr.Scope), 1057 } 1058 } 1059 1060 func (r *backingRelation) removed(ctx *allWatcherContext) error { 1061 allWatcherLogger.Tracef(`relation "%s:%s" removed`, ctx.modelUUID, ctx.id) 1062 ctx.removeFromStore(multiwatcher.RelationKind) 1063 return nil 1064 } 1065 1066 func (r *backingRelation) mongoID() string { 1067 return r.Key 1068 } 1069 1070 type backingAnnotation annotatorDoc 1071 1072 func (a *backingAnnotation) updated(ctx *allWatcherContext) error { 1073 allWatcherLogger.Tracef(`annotation "%s:%s" updated`, ctx.modelUUID, ctx.id) 1074 info := &multiwatcher.AnnotationInfo{ 1075 ModelUUID: a.ModelUUID, 1076 Tag: a.Tag, 1077 Annotations: a.Annotations, 1078 } 1079 ctx.store.Update(info) 1080 // Also update the annotations on the associated type. 1081 // When we can kill the old Watch API where annotations are separate 1082 // entries, we'd only update the associated type. 1083 parentID, _, ok := ctx.entityIDForGlobalKey(ctx.id) 1084 if !ok { 1085 return nil 1086 } 1087 info0 := ctx.store.Get(parentID) 1088 switch info := info0.(type) { 1089 case nil: 1090 // The parent info doesn't exist. Ignore the annotation until it does. 1091 return nil 1092 case *multiwatcher.UnitInfo: 1093 newInfo := *info 1094 newInfo.Annotations = a.Annotations 1095 info0 = &newInfo 1096 case *multiwatcher.ModelInfo: 1097 newInfo := *info 1098 newInfo.Annotations = a.Annotations 1099 info0 = &newInfo 1100 case *multiwatcher.ApplicationInfo: 1101 newInfo := *info 1102 newInfo.Annotations = a.Annotations 1103 info0 = &newInfo 1104 case *multiwatcher.MachineInfo: 1105 newInfo := *info 1106 newInfo.Annotations = a.Annotations 1107 info0 = &newInfo 1108 default: 1109 // We really don't care about this type yet. 1110 return nil 1111 } 1112 ctx.store.Update(info0) 1113 return nil 1114 } 1115 1116 func (a *backingAnnotation) removed(ctx *allWatcherContext) error { 1117 // Annotations are only removed when the entity is removed. 1118 // So no work is needed for the assocated entity type. 1119 allWatcherLogger.Tracef(`annotation "%s:%s" removed`, ctx.modelUUID, ctx.id) 1120 // UGH, TODO, use the global key as the entity id. 1121 tag, ok := tagForGlobalKey(ctx.id) 1122 if !ok { 1123 return errors.Errorf("could not parse global key: %q", ctx.id) 1124 } 1125 ctx.store.Remove(multiwatcher.EntityID{ 1126 Kind: multiwatcher.AnnotationKind, 1127 ModelUUID: ctx.modelUUID, 1128 ID: tag, 1129 }) 1130 return nil 1131 } 1132 1133 func (a *backingAnnotation) mongoID() string { 1134 return a.GlobalKey 1135 } 1136 1137 type backingBlock blockDoc 1138 1139 func (a *backingBlock) updated(ctx *allWatcherContext) error { 1140 allWatcherLogger.Tracef(`block "%s:%s" updated`, ctx.modelUUID, ctx.id) 1141 info := &multiwatcher.BlockInfo{ 1142 ModelUUID: a.ModelUUID, 1143 ID: ctx.id, // ID not in the blockDoc 1144 Tag: a.Tag, 1145 Type: a.Type.ToParams(), 1146 Message: a.Message, 1147 } 1148 ctx.store.Update(info) 1149 return nil 1150 } 1151 1152 func (a *backingBlock) removed(ctx *allWatcherContext) error { 1153 allWatcherLogger.Tracef(`block "%s:%s" removed`, ctx.modelUUID, ctx.id) 1154 ctx.removeFromStore(multiwatcher.BlockKind) 1155 return nil 1156 } 1157 1158 func (a *backingBlock) mongoID() string { 1159 _, id, ok := splitDocID(a.DocID) 1160 if !ok { 1161 allWatcherLogger.Criticalf("block ID not valid: %v", a.DocID) 1162 } 1163 return id 1164 } 1165 1166 type backingStatus statusDoc 1167 1168 func (s *backingStatus) toStatusInfo() multiwatcher.StatusInfo { 1169 return multiwatcher.StatusInfo{ 1170 Current: s.Status, 1171 Message: s.StatusInfo, 1172 Data: s.StatusData, 1173 Since: unixNanoToTime(s.Updated), 1174 } 1175 } 1176 1177 func (s *backingStatus) updated(ctx *allWatcherContext) error { 1178 allWatcherLogger.Tracef(`status "%s:%s" updated`, ctx.modelUUID, ctx.id) 1179 parentID, suffix, ok := ctx.entityIDForGlobalKey(ctx.id) 1180 if !ok { 1181 return nil 1182 } 1183 info0 := ctx.store.Get(parentID) 1184 // NOTE: for both the machine and the unit, where the version 1185 // is set in the agent status, we need to copy across the version from 1186 // the existing info. 1187 switch info := info0.(type) { 1188 case nil: 1189 // The parent info doesn't exist. Ignore the status until it does. 1190 return nil 1191 case *multiwatcher.UnitInfo: 1192 newInfo := *info 1193 switch suffix { 1194 case "": 1195 if err := s.updatedUnitAgentStatus(ctx, &newInfo); err != nil { 1196 return err 1197 } 1198 case "#charm": 1199 s.updatedUnitWorkloadStatus(ctx, &newInfo) 1200 case "#charm#container": 1201 newInfo.ContainerStatus = s.toStatusInfo() 1202 // I have no idea what the original author was thinking about when 1203 // they added "sat" as part of the key. 1204 case "#charm#sat#workload-version": 1205 // Unit workload status is only shown currently implemented on the 1206 // application, and the last set unit workload version is considered 1207 // the one that matters. 1208 s.updateApplicationWorkload(ctx, info) 1209 // No need to touch the unit for now, so we can exit the function here. 1210 return nil 1211 default: 1212 allWatcherLogger.Tracef("charm status suffix %q unhandled", suffix) 1213 return nil 1214 } 1215 info0 = &newInfo 1216 case *multiwatcher.ModelInfo: 1217 newInfo := *info 1218 newInfo.Status = s.toStatusInfo() 1219 info0 = &newInfo 1220 case *multiwatcher.ApplicationInfo: 1221 newInfo := *info 1222 switch suffix { 1223 case "#operator": 1224 newInfo.OperatorStatus = s.toStatusInfo() 1225 case "": 1226 newInfo.Status = s.toStatusInfo() 1227 default: 1228 allWatcherLogger.Tracef("application status suffix %q unhandled", suffix) 1229 return nil 1230 } 1231 info0 = &newInfo 1232 case *multiwatcher.RemoteApplicationUpdate: 1233 newInfo := *info 1234 newInfo.Status = s.toStatusInfo() 1235 info0 = &newInfo 1236 case *multiwatcher.MachineInfo: 1237 newInfo := *info 1238 switch suffix { 1239 case "#instance": 1240 newInfo.InstanceStatus = s.toStatusInfo() 1241 case "": 1242 // Preserve the agent version that is set on the agent status. 1243 agentVersion := newInfo.AgentStatus.Version 1244 newInfo.AgentStatus = s.toStatusInfo() 1245 newInfo.AgentStatus.Version = agentVersion 1246 default: 1247 allWatcherLogger.Tracef("machine status suffix %q unhandled", suffix) 1248 return nil 1249 } 1250 info0 = &newInfo 1251 default: 1252 return errors.Errorf("status for unexpected entity with id %q; type %T", ctx.id, info) 1253 } 1254 ctx.store.Update(info0) 1255 return nil 1256 } 1257 1258 func (s *backingStatus) updateApplicationWorkload(ctx *allWatcherContext, unit *multiwatcher.UnitInfo) { 1259 // If the workload version is blank, do nothing. 1260 if s.StatusInfo == "" { 1261 return 1262 } 1263 // Lookup the application for the unit. 1264 appInfo := &multiwatcher.ApplicationInfo{ 1265 ModelUUID: ctx.modelUUID, 1266 Name: unit.Application, 1267 } 1268 info0 := ctx.store.Get(appInfo.EntityID()) 1269 if info0 == nil { 1270 // The parent info doesn't exist. Ignore the workload until it does. 1271 return 1272 } 1273 appInfo = info0.(*multiwatcher.ApplicationInfo) 1274 updated := *appInfo 1275 updated.WorkloadVersion = s.StatusInfo 1276 ctx.store.Update(&updated) 1277 } 1278 1279 func (s *backingStatus) updatedUnitAgentStatus(ctx *allWatcherContext, unitInfo *multiwatcher.UnitInfo) error { 1280 // Preserve the agent version that is set on the agent status. 1281 agentVersion := unitInfo.AgentStatus.Version 1282 1283 // If the agent status used to be "error", then the units workload status will be error. 1284 if unitInfo.WorkloadStatus.Current == status.Error { 1285 // If we were in an error state, and continue in an error state, just update the workload 1286 // status with the new error info. 1287 if s.Status == status.Error { 1288 unitInfo.WorkloadStatus = s.toStatusInfo() 1289 return nil 1290 } 1291 // Agent is coming out of error status, so we need to read the workload status. 1292 workloadStatus, err := ctx.getStatus(unitGlobalKey(unitInfo.Name), "unit") 1293 if err != nil { 1294 return err 1295 } 1296 unitInfo.WorkloadStatus = workloadStatus 1297 unitInfo.AgentStatus = s.toStatusInfo() 1298 unitInfo.AgentStatus.Version = agentVersion 1299 return nil 1300 } 1301 // We weren't in an error state before. 1302 if s.Status != status.Error { 1303 // We aren't entering an error state, so just update the value. 1304 unitInfo.AgentStatus = s.toStatusInfo() 1305 unitInfo.AgentStatus.Version = agentVersion 1306 return nil 1307 } 1308 // Otherwise we need to do the annoying dance with errors. 1309 errorState := s.toStatusInfo() 1310 unitInfo.WorkloadStatus = errorState 1311 unitInfo.AgentStatus = multiwatcher.StatusInfo{ 1312 Current: status.Idle, 1313 Data: normaliseStatusData(nil), 1314 Since: errorState.Since, 1315 Version: agentVersion, 1316 } 1317 return nil 1318 } 1319 1320 func (s *backingStatus) updatedUnitWorkloadStatus(ctx *allWatcherContext, unitInfo *multiwatcher.UnitInfo) { 1321 // If we aren't in an error state, update the workload. 1322 if unitInfo.WorkloadStatus.Current != status.Error { 1323 unitInfo.WorkloadStatus = s.toStatusInfo() 1324 } 1325 // We don't need to look up the current value of the agent status 1326 // because any change to that will cause the unit to be updated again, 1327 // and it will read the workload status if needed. 1328 } 1329 1330 func (s *backingStatus) removed(ctx *allWatcherContext) error { 1331 // If the status is removed, the parent will follow not long after, 1332 // so do nothing. 1333 return nil 1334 } 1335 1336 func (s *backingStatus) mongoID() string { 1337 allWatcherLogger.Criticalf("programming error: attempting to get mongoID from status document") 1338 return "" 1339 } 1340 1341 type backingConstraints constraintsDoc 1342 1343 func (c *backingConstraints) updated(ctx *allWatcherContext) error { 1344 allWatcherLogger.Tracef(`constraints "%s:%s" updated`, ctx.modelUUID, ctx.id) 1345 parentID, _, ok := ctx.entityIDForGlobalKey(ctx.id) 1346 if !ok { 1347 return nil 1348 } 1349 info0 := ctx.store.Get(parentID) 1350 switch info := info0.(type) { 1351 case nil: 1352 // The parent info doesn't exist. Ignore the constraints until it does. 1353 return nil 1354 case *multiwatcher.UnitInfo, *multiwatcher.MachineInfo: 1355 // We don't (yet) publish unit or machine constraints. 1356 return nil 1357 case *multiwatcher.ModelInfo: 1358 newInfo := *info 1359 newInfo.Constraints = constraintsDoc(*c).value() 1360 info0 = &newInfo 1361 case *multiwatcher.ApplicationInfo: 1362 newInfo := *info 1363 newInfo.Constraints = constraintsDoc(*c).value() 1364 info0 = &newInfo 1365 default: 1366 return errors.Errorf("constraints for unexpected entity with id %q; type %T", ctx.id, info) 1367 } 1368 ctx.store.Update(info0) 1369 return nil 1370 } 1371 1372 func (c *backingConstraints) removed(ctx *allWatcherContext) error { 1373 return nil 1374 } 1375 1376 func (c *backingConstraints) mongoID() string { 1377 allWatcherLogger.Criticalf("programming error: attempting to get mongoID from constraints document") 1378 return "" 1379 } 1380 1381 type backingSettings settingsDoc 1382 1383 func (s *backingSettings) updated(ctx *allWatcherContext) error { 1384 allWatcherLogger.Tracef(`settings "%s:%s" updated`, ctx.modelUUID, ctx.id) 1385 parentID, url, ok := ctx.entityIDForSettingsKey(ctx.id) 1386 if !ok { 1387 return nil 1388 } 1389 info0 := ctx.store.Get(parentID) 1390 switch info := info0.(type) { 1391 case nil: 1392 // The parent info doesn't exist. Ignore the status until it does. 1393 return nil 1394 case *multiwatcher.ModelInfo: 1395 // We need to construct a model config so that coercion 1396 // of raw settings values occurs. 1397 cfg, err := config.New(config.NoDefaults, s.Settings) 1398 if err != nil { 1399 return errors.Trace(err) 1400 } 1401 newInfo := *info 1402 newInfo.Config = cfg.AllAttrs() 1403 info0 = &newInfo 1404 case *multiwatcher.ApplicationInfo: 1405 // If we're seeing settings for the application with a different 1406 // charm URL, we ignore them - we will fetch 1407 // them again when the application charm changes. 1408 // By doing this we make sure that the settings in the 1409 // ApplicationInfo are always consistent with the charm URL. 1410 if info.CharmURL != url { 1411 break 1412 } 1413 newInfo := *info 1414 newInfo.Config = s.Settings 1415 info0 = &newInfo 1416 default: 1417 return nil 1418 } 1419 ctx.store.Update(info0) 1420 return nil 1421 } 1422 1423 func (s *backingSettings) removed(ctx *allWatcherContext) error { 1424 // Settings docs are only removed when the principal doc is removed. Nothing to do here. 1425 return nil 1426 } 1427 1428 func (s *backingSettings) mongoID() string { 1429 allWatcherLogger.Criticalf("programming error: attempting to get mongoID from settings document") 1430 return "" 1431 } 1432 1433 type backingOpenedPorts map[string]interface{} 1434 1435 func (p *backingOpenedPorts) updated(ctx *allWatcherContext) error { 1436 allWatcherLogger.Tracef(`opened ports "%s:%s" updated`, ctx.modelUUID, ctx.id) 1437 parentID, ok := ctx.entityIDForOpenedPortsKey(ctx.id) 1438 if !ok { 1439 return nil 1440 } 1441 switch info := ctx.store.Get(parentID).(type) { 1442 case nil: 1443 // The parent info doesn't exist. This is unexpected because the port 1444 // always refers to a machine. Anyway, ignore the ports for now. 1445 return nil 1446 case *multiwatcher.MachineInfo: 1447 // Retrieve the units placed in the machine. 1448 units, err := ctx.state.UnitsFor(info.ID) 1449 if err != nil { 1450 return errors.Trace(err) 1451 } 1452 // Update the ports on all units assigned to the machine. 1453 for _, u := range units { 1454 if err := updateUnitPorts(ctx, u); err != nil { 1455 return errors.Trace(err) 1456 } 1457 } 1458 } 1459 return nil 1460 } 1461 1462 func (p *backingOpenedPorts) removed(ctx *allWatcherContext) error { 1463 allWatcherLogger.Tracef(`opened ports "%s:%s" removed`, ctx.modelUUID, ctx.id) 1464 // This magic is needed as an open ports doc may be removed if all 1465 // open ports on the subnet are removed. 1466 parentID, ok := ctx.entityIDForOpenedPortsKey(ctx.id) 1467 if !ok { 1468 return nil 1469 } 1470 switch info := ctx.store.Get(parentID).(type) { 1471 case nil: 1472 // The parent info doesn't exist. This is unexpected because the port 1473 // always refers to a machine. Anyway, ignore the ports for now. 1474 return nil 1475 case *multiwatcher.MachineInfo: 1476 // Retrieve the units placed in the machine. 1477 units, err := ctx.state.UnitsFor(info.ID) 1478 if err != nil { 1479 // An error isn't returned here because the watcher is 1480 // always acting a little behind reality. It is reasonable 1481 // that entities have been deleted from State but we're 1482 // still seeing events related to them from the watcher. 1483 allWatcherLogger.Errorf("cannot retrieve units for %q: %v", info.ID, err) 1484 return nil 1485 } 1486 // Update the ports on all units assigned to the machine. 1487 for _, u := range units { 1488 if err := updateUnitPorts(ctx, u); err != nil { 1489 allWatcherLogger.Errorf("cannot update unit ports for %q: %v", u.Name(), err) 1490 } 1491 } 1492 } 1493 return nil 1494 } 1495 1496 func (p *backingOpenedPorts) mongoID() string { 1497 allWatcherLogger.Criticalf("programming error: attempting to get mongoID from openedPorts document") 1498 return "" 1499 } 1500 1501 // updateUnitPorts updates the PortRanges info of the given unit. 1502 func updateUnitPorts(ctx *allWatcherContext, u *Unit) error { 1503 eid, _, ok := ctx.entityIDForGlobalKey(u.globalKey()) 1504 if !ok { 1505 // This should never happen. 1506 return errors.New("cannot retrieve entity id for unit") 1507 } 1508 switch oldInfo := ctx.store.Get(eid).(type) { 1509 case nil: 1510 // The unit info doesn't exist. This is unlikely to happen, but ignore 1511 // the status until a unitInfo is included in the store. 1512 return nil 1513 case *multiwatcher.UnitInfo: 1514 unitPortRangesByEndpoint, err := ctx.getUnitPortRangesByEndpoint(u) 1515 if err != nil { 1516 return errors.Trace(err) 1517 } 1518 unitInfo := *oldInfo 1519 unitInfo.OpenPortRangesByEndpoint = unitPortRangesByEndpoint.Clone() 1520 ctx.store.Update(&unitInfo) 1521 default: 1522 return nil 1523 } 1524 return nil 1525 } 1526 1527 type backingGeneration generationDoc 1528 1529 func (g *backingGeneration) updated(ctx *allWatcherContext) error { 1530 allWatcherLogger.Tracef(`generation "%s:%s" updated`, ctx.modelUUID, ctx.id) 1531 // Convert the state representation of config deltas 1532 // to the multiwatcher representation. 1533 var cfg map[string][]multiwatcher.ItemChange 1534 if len(g.Config) > 0 { 1535 cfg = make(map[string][]multiwatcher.ItemChange, len(g.Config)) 1536 for app, deltas := range g.Config { 1537 d := make([]multiwatcher.ItemChange, len(deltas)) 1538 for i, delta := range deltas { 1539 d[i] = multiwatcher.ItemChange{ 1540 Type: delta.Type, 1541 Key: delta.Key, 1542 OldValue: delta.OldValue, 1543 NewValue: delta.NewValue, 1544 } 1545 } 1546 cfg[app] = d 1547 } 1548 } 1549 1550 // Make a copy of the AssignedUnits map. 1551 assigned := make(map[string][]string, len(g.AssignedUnits)) 1552 for k, v := range g.AssignedUnits { 1553 units := make([]string, len(v)) 1554 copy(units, v) 1555 assigned[k] = units 1556 } 1557 1558 info := &multiwatcher.BranchInfo{ 1559 ModelUUID: g.ModelUUID, 1560 ID: ctx.id, // Id not stored on the doc. 1561 Name: g.Name, 1562 AssignedUnits: assigned, 1563 Config: cfg, 1564 Created: g.Created, 1565 CreatedBy: g.CreatedBy, 1566 Completed: g.Completed, 1567 CompletedBy: g.CompletedBy, 1568 GenerationID: g.GenerationId, 1569 } 1570 ctx.store.Update(info) 1571 return nil 1572 1573 } 1574 1575 func (g *backingGeneration) removed(ctx *allWatcherContext) error { 1576 allWatcherLogger.Tracef(`branch "%s:%s" removed`, ctx.modelUUID, ctx.id) 1577 ctx.removeFromStore(multiwatcher.BranchKind) 1578 return nil 1579 } 1580 1581 func (g *backingGeneration) mongoID() string { 1582 _, id, ok := splitDocID(g.DocId) 1583 if !ok { 1584 allWatcherLogger.Criticalf("charm ID not valid: %v", g.DocId) 1585 } 1586 return id 1587 } 1588 1589 // backingEntityDoc is implemented by the documents in 1590 // collections that the allWatcherStateBacking watches. 1591 type backingEntityDoc interface { 1592 // updated is called when the document has changed. 1593 // The mongo _id value of the document is provided in id. 1594 updated(ctx *allWatcherContext) error 1595 1596 // removed is called when the document has changed. 1597 // The receiving instance will not contain any data. 1598 // 1599 // The mongo _id value of the document is provided in id. 1600 // 1601 // In some cases st may be nil. If the implementation requires st 1602 // then it should do nothing. 1603 removed(ctx *allWatcherContext) error 1604 1605 // mongoID returns the localID of the document. 1606 // It is currently never called for subsidiary documents. 1607 mongoID() string 1608 } 1609 1610 // AllWatcherBacking is the interface required by the multiwatcher to access the 1611 // underlying state. 1612 type AllWatcherBacking interface { 1613 // GetAll retrieves information about all information 1614 // known to the Backing and stashes it in the Store. 1615 GetAll(multiwatcher.Store) error 1616 1617 // Changed informs the backing about a change received 1618 // from a watcher channel. The backing is responsible for 1619 // updating the Store to reflect the change. 1620 Changed(multiwatcher.Store, watcher.Change) error 1621 1622 // Watch watches for any changes and sends them 1623 // on the given channel. 1624 Watch(chan<- watcher.Change) 1625 1626 // Unwatch stops watching for changes on the 1627 // given channel. 1628 Unwatch(chan<- watcher.Change) 1629 } 1630 1631 // NewAllWatcherBacking creates a backing object that watches 1632 // all the models in the controller for changes that are fed through 1633 // the multiwatcher infrastructure. 1634 func NewAllWatcherBacking(pool *StatePool) (AllWatcherBacking, error) { 1635 collectionNames := []string{ 1636 // The ordering here matters. We want to load machines, then 1637 // applications, then units. The others don't matter so much. 1638 modelsC, 1639 machinesC, 1640 applicationsC, 1641 unitsC, 1642 // The rest don't really matter. 1643 actionsC, 1644 annotationsC, 1645 applicationOffersC, 1646 blocksC, 1647 charmsC, 1648 constraintsC, 1649 generationsC, 1650 instanceDataC, 1651 openedPortsC, 1652 permissionsC, 1653 relationsC, 1654 remoteApplicationsC, 1655 statusesC, 1656 settingsC, 1657 // And for CAAS we need to watch these... 1658 podSpecsC, 1659 } 1660 collectionMap := makeAllWatcherCollectionInfo(collectionNames) 1661 controllerState, err := pool.SystemState() 1662 if err != nil { 1663 return nil, errors.Trace(err) 1664 } 1665 return &allWatcherBacking{ 1666 watcher: controllerState.workers.txnLogWatcher(), 1667 stPool: pool, 1668 collections: collectionNames, 1669 collectionByName: collectionMap, 1670 }, nil 1671 } 1672 1673 // Watch watches all the collections. 1674 func (b *allWatcherBacking) Watch(in chan<- watcher.Change) { 1675 for _, c := range b.collectionByName { 1676 b.watcher.WatchCollection(c.name, in) 1677 } 1678 } 1679 1680 // Unwatch unwatches all the collections. 1681 func (b *allWatcherBacking) Unwatch(in chan<- watcher.Change) { 1682 for _, c := range b.collectionByName { 1683 b.watcher.UnwatchCollection(c.name, in) 1684 } 1685 } 1686 1687 // GetAll fetches all items that we want to watch from the state. 1688 func (b *allWatcherBacking) GetAll(store multiwatcher.Store) error { 1689 systemState, err := b.stPool.SystemState() 1690 if err != nil { 1691 return errors.Trace(err) 1692 } 1693 modelUUIDs, err := systemState.AllModelUUIDs() 1694 if err != nil { 1695 return errors.Annotate(err, "error loading models") 1696 } 1697 for _, modelUUID := range modelUUIDs { 1698 if err := b.loadAllWatcherEntitiesForModel(modelUUID, store); err != nil { 1699 return errors.Trace(err) 1700 } 1701 } 1702 return nil 1703 } 1704 1705 func (b *allWatcherBacking) loadAllWatcherEntitiesForModel(modelUUID string, store multiwatcher.Store) error { 1706 st, err := b.stPool.Get(modelUUID) 1707 if err != nil { 1708 if errors.IsNotFound(err) { 1709 // This can occur if the model has been destroyed since 1710 // the moment when model uuid has been retrieved. 1711 // If we cannot find the model in the above call, 1712 // we do not want to err out and we do not want to proceed 1713 // with this call - just leave. 1714 return nil 1715 } 1716 return errors.Trace(err) 1717 } 1718 defer st.Release() 1719 1720 err = loadAllWatcherEntities(st.State, b.collections, b.collectionByName, store) 1721 if err != nil { 1722 return errors.Annotatef(err, "error loading entities for model %v", modelUUID) 1723 } 1724 return nil 1725 } 1726 1727 // Changed updates the allWatcher's idea of the current state 1728 // in response to the given change. 1729 func (b *allWatcherBacking) Changed(store multiwatcher.Store, change watcher.Change) error { 1730 c, ok := b.collectionByName[change.C] 1731 if !ok { 1732 return errors.Errorf("unknown collection %q in fetch request", change.C) 1733 } 1734 1735 modelUUID, id, err := b.idForChange(change) 1736 if err != nil { 1737 return errors.Trace(err) 1738 } 1739 1740 doc := reflect.New(c.docType).Interface().(backingEntityDoc) 1741 1742 systemState, err := b.stPool.SystemState() 1743 if err != nil { 1744 return errors.Trace(err) 1745 } 1746 ctx := &allWatcherContext{ 1747 // In order to have a valid state instance, use the controller model initially. 1748 state: systemState, 1749 store: store, 1750 modelUUID: modelUUID, 1751 id: id, 1752 } 1753 1754 st, err := b.getState(modelUUID) 1755 if err != nil { 1756 // The state pool will return a not found error if the model is 1757 // in the process of being removed. 1758 if errors.IsNotFound(err) { 1759 // The entity's model is gone so remove the entity from the store. 1760 _ = doc.removed(ctx) 1761 return nil 1762 } 1763 return errors.Trace(err) // prioritise getState error 1764 } 1765 defer st.Release() 1766 // Update the state in the context to be the valid one from the state pool. 1767 ctx.state = st.State 1768 1769 col, closer := st.db().GetCollection(c.name) 1770 defer closer() 1771 1772 err = col.FindId(id).One(doc) 1773 if err == mgo.ErrNotFound { 1774 err := doc.removed(ctx) 1775 return errors.Trace(err) 1776 } 1777 if err != nil { 1778 return err 1779 } 1780 return doc.updated(ctx) 1781 } 1782 1783 func (b *allWatcherBacking) idForChange(change watcher.Change) (string, string, error) { 1784 if change.C == modelsC { 1785 modelUUID := change.Id.(string) 1786 return modelUUID, modelUUID, nil 1787 } else if change.C == permissionsC { 1788 // All permissions can just load using the system state. 1789 systemState, err := b.stPool.SystemState() 1790 if err != nil { 1791 return "", "", errors.Trace(err) 1792 } 1793 modelUUID := systemState.ModelUUID() 1794 return modelUUID, change.Id.(string), nil 1795 } 1796 1797 modelUUID, id, ok := splitDocID(change.Id.(string)) 1798 if !ok { 1799 return "", "", errors.Errorf("unknown id format: %v", change.Id.(string)) 1800 } 1801 return modelUUID, id, nil 1802 } 1803 1804 func (b *allWatcherBacking) getState(modelUUID string) (*PooledState, error) { 1805 st, err := b.stPool.Get(modelUUID) 1806 if err != nil { 1807 return nil, errors.Trace(err) 1808 } 1809 return st, nil 1810 } 1811 1812 func loadAllWatcherEntities(st *State, loadOrder []string, collectionByName map[string]allWatcherStateCollection, store multiwatcher.Store) error { 1813 // Use a single new MongoDB connection for all the work here. 1814 db, closer := st.newDB() 1815 defer closer() 1816 start := st.clock().Now() 1817 defer func() { 1818 elapsed := st.clock().Now().Sub(start) 1819 allWatcherLogger.Infof("allwatcher loaded for model %q in %s", st.ModelUUID(), elapsed) 1820 }() 1821 1822 ctx := &allWatcherContext{ 1823 state: st, 1824 store: store, 1825 modelUUID: st.ModelUUID(), 1826 } 1827 // TODO(thumper): make it multimodel aware 1828 if err := ctx.loadSubsidiaryCollections(); err != nil { 1829 return errors.Annotate(err, "loading subsidiary collections") 1830 } 1831 1832 for _, name := range loadOrder { 1833 c, found := collectionByName[name] 1834 if !found { 1835 allWatcherLogger.Criticalf("programming error, collection %q not found in map", name) 1836 continue 1837 } 1838 if c.subsidiary { 1839 continue 1840 } 1841 if err := loadOneWatcherEntity(ctx, db, st.ModelUUID(), c.docType, c.name); err != nil { 1842 return errors.Trace(err) 1843 } 1844 } 1845 1846 return nil 1847 } 1848 1849 func loadOneWatcherEntity(ctx *allWatcherContext, db Database, modelUUID string, docType reflect.Type, name string) error { 1850 col, closer := db.GetCollection(name) 1851 defer closer() 1852 infoSlicePtr := reflect.New(reflect.SliceOf(docType)) 1853 1854 // models is a global collection so need to filter on UUID. 1855 var filter bson.M 1856 if name == modelsC { 1857 filter = bson.M{"_id": modelUUID} 1858 } 1859 query := col.Find(filter) 1860 // Units are ordered so we load the subordinates first. 1861 if name == unitsC { 1862 // Subordinates have a principal, so will sort after the 1863 // empty string, which is what principal units have. 1864 query = query.Sort("principal") 1865 } 1866 if err := query.All(infoSlicePtr.Interface()); err != nil { 1867 return errors.Errorf("cannot get all %s: %v", name, err) 1868 } 1869 infos := infoSlicePtr.Elem() 1870 for i := 0; i < infos.Len(); i++ { 1871 info := infos.Index(i).Addr().Interface().(backingEntityDoc) 1872 ctx.id = info.mongoID() 1873 err := info.updated(ctx) 1874 if err != nil { 1875 return errors.Annotatef(err, "failed to initialise backing for %s:%v", name, ctx.id) 1876 } 1877 } 1878 return nil 1879 } 1880 1881 func normaliseStatusData(data map[string]interface{}) map[string]interface{} { 1882 if data == nil { 1883 return make(map[string]interface{}) 1884 } 1885 return data 1886 } 1887 1888 type allWatcherContext struct { 1889 state *State 1890 store multiwatcher.Store 1891 modelUUID string 1892 id string 1893 1894 modelType_ ModelType 1895 1896 settings map[string]*settingsDoc 1897 annotations map[string]map[string]string 1898 constraints map[string]constraints.Value 1899 statuses map[string]status.StatusInfo 1900 instances map[string]instanceData 1901 // A map of the existing MachinePortRanges where the keys are machine IDs. 1902 openPortRangesForMachine map[string]MachinePortRanges 1903 // A map of the existing ApplicationPortRanges where the keys are application names. 1904 openPortRangesForApplication map[string]ApplicationPortRanges 1905 userAccess map[string]map[string]permission.Access 1906 } 1907 1908 func (ctx *allWatcherContext) loadSubsidiaryCollections() error { 1909 if err := ctx.loadSettings(); err != nil { 1910 return errors.Annotatef(err, "cache settings") 1911 } 1912 if err := ctx.loadAnnotations(); err != nil { 1913 return errors.Annotatef(err, "cache annotations") 1914 } 1915 if err := ctx.loadConstraints(); err != nil { 1916 return errors.Annotatef(err, "cache constraints") 1917 } 1918 if err := ctx.loadStatuses(); err != nil { 1919 return errors.Annotatef(err, "cache statuses") 1920 } 1921 if err := ctx.loadInstanceData(); err != nil { 1922 return errors.Annotatef(err, "cache instance data") 1923 } 1924 if err := ctx.loadOpenedPortRanges(); err != nil { 1925 return errors.Annotatef(err, "cache opened ports") 1926 } 1927 if err := ctx.loadPermissions(); err != nil { 1928 return errors.Annotatef(err, "permissions") 1929 } 1930 return nil 1931 } 1932 1933 func (ctx *allWatcherContext) loadSettings() error { 1934 col, closer := ctx.state.db().GetCollection(settingsC) 1935 defer closer() 1936 1937 var docs []settingsDoc 1938 if err := col.Find(nil).All(&docs); err != nil { 1939 return errors.Annotate(err, "cannot read all settings") 1940 } 1941 1942 ctx.settings = make(map[string]*settingsDoc) 1943 for _, doc := range docs { 1944 docCopy := doc 1945 ctx.settings[doc.DocID] = &docCopy 1946 } 1947 1948 return nil 1949 } 1950 1951 func (ctx *allWatcherContext) loadAnnotations() error { 1952 col, closer := ctx.state.db().GetCollection(annotationsC) 1953 defer closer() 1954 1955 var docs []annotatorDoc 1956 if err := col.Find(nil).All(&docs); err != nil { 1957 return errors.Annotate(err, "cannot read all annotations") 1958 } 1959 1960 ctx.annotations = make(map[string]map[string]string) 1961 for _, doc := range docs { 1962 key := ensureModelUUID(doc.ModelUUID, doc.GlobalKey) 1963 ctx.annotations[key] = doc.Annotations 1964 } 1965 1966 return nil 1967 } 1968 1969 func (ctx *allWatcherContext) loadStatuses() error { 1970 col, closer := ctx.state.db().GetCollection(statusesC) 1971 defer closer() 1972 1973 var docs []statusDocWithID 1974 if err := col.Find(nil).All(&docs); err != nil { 1975 return errors.Annotate(err, "cannot read all statuses") 1976 } 1977 1978 ctx.statuses = make(map[string]status.StatusInfo) 1979 for _, doc := range docs { 1980 ctx.statuses[doc.ID] = doc.asStatusInfo() 1981 } 1982 1983 return nil 1984 } 1985 1986 func (ctx *allWatcherContext) loadInstanceData() error { 1987 col, closer := ctx.state.db().GetCollection(instanceDataC) 1988 defer closer() 1989 1990 var docs []instanceData 1991 if err := col.Find(nil).All(&docs); err != nil { 1992 return errors.Annotate(err, "cannot read all instance data") 1993 } 1994 1995 ctx.instances = make(map[string]instanceData) 1996 for _, doc := range docs { 1997 docCopy := doc 1998 ctx.instances[doc.DocID] = docCopy 1999 } 2000 2001 return nil 2002 } 2003 2004 func (ctx *allWatcherContext) loadOpenedPortRanges() error { 2005 openedMachineRanges, err := getOpenedPortRangesForAllMachines(ctx.state) 2006 if err != nil { 2007 return errors.Annotate(err, "cannot read all opened port ranges") 2008 } 2009 ctx.openPortRangesForMachine = make(map[string]MachinePortRanges) 2010 for _, mpr := range openedMachineRanges { 2011 ctx.openPortRangesForMachine[mpr.MachineID()] = mpr 2012 } 2013 2014 openedApplicationRanges, err := getOpenedApplicationPortRangesForAllApplications(ctx.state) 2015 if err != nil { 2016 return errors.Annotate(err, "cannot read all opened port ranges") 2017 } 2018 ctx.openPortRangesForApplication = make(map[string]ApplicationPortRanges) 2019 for _, mpr := range openedApplicationRanges { 2020 ctx.openPortRangesForApplication[mpr.ApplicationName()] = mpr 2021 } 2022 2023 return nil 2024 } 2025 2026 func (ctx *allWatcherContext) loadPermissions() error { 2027 col, closer := ctx.state.db().GetCollection(permissionsC) 2028 defer closer() 2029 2030 var docs []backingPermission 2031 if err := col.Find(nil).All(&docs); err != nil { 2032 return errors.Annotate(err, "cannot read all permissions") 2033 } 2034 2035 ctx.userAccess = make(map[string]map[string]permission.Access) 2036 for _, doc := range docs { 2037 modelUUID, user, ok := doc.modelAndUser(doc.ID) 2038 if !ok { 2039 continue 2040 } 2041 modelPermissions := ctx.userAccess[modelUUID] 2042 if modelPermissions == nil { 2043 modelPermissions = make(map[string]permission.Access) 2044 ctx.userAccess[modelUUID] = modelPermissions 2045 } 2046 modelPermissions[user] = permission.Access(doc.Access) 2047 } 2048 2049 return nil 2050 } 2051 2052 func (ctx *allWatcherContext) loadConstraints() error { 2053 col, closer := ctx.state.db().GetCollection(constraintsC) 2054 defer closer() 2055 2056 var docs []constraintsDoc 2057 if err := col.Find(nil).All(&docs); err != nil { 2058 return errors.Errorf("cannot read all constraints") 2059 } 2060 2061 ctx.constraints = make(map[string]constraints.Value) 2062 for _, doc := range docs { 2063 ctx.constraints[doc.DocID] = doc.value() 2064 } 2065 2066 return nil 2067 } 2068 2069 func (ctx *allWatcherContext) removeFromStore(kind string) { 2070 ctx.store.Remove(multiwatcher.EntityID{ 2071 Kind: kind, 2072 ModelUUID: ctx.modelUUID, 2073 ID: ctx.id, 2074 }) 2075 } 2076 2077 func (ctx *allWatcherContext) getAnnotations(key string) map[string]string { 2078 gKey := ensureModelUUID(ctx.modelUUID, key) 2079 if ctx.annotations != nil { 2080 // It is entirely possible and fine for there to be no annotations. 2081 return ctx.annotations[gKey] 2082 } 2083 2084 annotations, closer := ctx.state.db().GetCollection(annotationsC) 2085 defer closer() 2086 2087 var doc annotatorDoc 2088 err := annotations.FindId(gKey).One(&doc) 2089 if err != nil { 2090 // We really don't care what the error is. Anything substantial 2091 // will be caught by other queries. 2092 return nil 2093 } 2094 return doc.Annotations 2095 } 2096 2097 func (ctx *allWatcherContext) getSettings(key string) (map[string]interface{}, error) { 2098 var doc *settingsDoc 2099 var err error 2100 if ctx.settings != nil { 2101 gKey := ensureModelUUID(ctx.modelUUID, key) 2102 cDoc, found := ctx.settings[gKey] 2103 if !found { 2104 return nil, errors.NotFoundf("settings doc %q", gKey) 2105 } 2106 doc = cDoc 2107 } else { 2108 doc, err = readSettingsDoc(ctx.state.db(), settingsC, key) 2109 if err != nil { 2110 return nil, errors.Trace(err) 2111 } 2112 } 2113 // The copyMap does the key translation for dots and dollars. 2114 settings := copyMap(doc.Settings, nil) 2115 return settings, nil 2116 } 2117 2118 func (ctx *allWatcherContext) readConstraints(key string) (constraints.Value, error) { 2119 if ctx.constraints != nil { 2120 gKey := ensureModelUUID(ctx.modelUUID, key) 2121 value, found := ctx.constraints[gKey] 2122 if !found { 2123 return constraints.Value{}, errors.NotFoundf("constraints %q", gKey) 2124 } 2125 return value, nil 2126 } 2127 value, err := readConstraints(ctx.state, key) 2128 return value, err 2129 } 2130 2131 func (ctx *allWatcherContext) getStatus(key, badge string) (multiwatcher.StatusInfo, error) { 2132 var modelStatus status.StatusInfo 2133 var err error 2134 if ctx.statuses != nil { 2135 gKey := ensureModelUUID(ctx.modelUUID, key) 2136 cached, found := ctx.statuses[gKey] 2137 if found { 2138 modelStatus = cached 2139 } else { 2140 err = errors.NotFoundf("status doc %q", gKey) 2141 } 2142 } else { 2143 modelStatus, err = getStatus(ctx.state.db(), key, badge) 2144 } 2145 if err != nil { 2146 return multiwatcher.StatusInfo{}, errors.Trace(err) 2147 } 2148 return multiwatcher.StatusInfo{ 2149 Current: modelStatus.Status, 2150 Message: modelStatus.Message, 2151 Data: normaliseStatusData(modelStatus.Data), 2152 Since: modelStatus.Since, 2153 }, nil 2154 } 2155 2156 func (ctx *allWatcherContext) getInstanceData(id string) (instanceData, error) { 2157 if ctx.instances != nil { 2158 gKey := ensureModelUUID(ctx.modelUUID, id) 2159 cached, found := ctx.instances[gKey] 2160 if found { 2161 return cached, nil 2162 } else { 2163 return instanceData{}, errors.NotFoundf("instance data for machine %v", id) 2164 } 2165 } 2166 return getInstanceData(ctx.state, id) 2167 } 2168 2169 func (ctx *allWatcherContext) permissionsForModel(uuid string) (map[string]permission.Access, error) { 2170 if ctx.userAccess != nil { 2171 return ctx.userAccess[uuid], nil 2172 } 2173 permissions, err := ctx.state.usersPermissions(modelKey(uuid)) 2174 if err != nil { 2175 return nil, errors.Trace(err) 2176 } 2177 result := make(map[string]permission.Access) 2178 for _, perm := range permissions { 2179 user := userIDFromGlobalKey(perm.doc.SubjectGlobalKey) 2180 if user == perm.doc.SubjectGlobalKey { 2181 // Not a user subject 2182 continue 2183 } 2184 result[user] = perm.access() 2185 } 2186 return result, nil 2187 } 2188 2189 func (ctx *allWatcherContext) getUnitPortRangesByEndpoint(unit *Unit) (network.GroupedPortRanges, error) { 2190 if unit.ShouldBeAssigned() { 2191 machineID, err := unit.AssignedMachineId() 2192 if err != nil { 2193 if errors.IsNotAssigned(err) { 2194 // Not assigned, so there won't be any ports opened. 2195 // Return an empty port map (see Bug #1425435). 2196 return make(network.GroupedPortRanges), nil 2197 } 2198 return nil, errors.Trace(err) 2199 } 2200 // No cached port ranges available; make a direct DB lookup instead. 2201 if ctx.openPortRangesForMachine == nil || ctx.openPortRangesForMachine[machineID] == nil { 2202 unitPortRanges, err := unit.OpenedPortRanges() 2203 return unitPortRanges.ByEndpoint(), err 2204 } 2205 return ctx.openPortRangesForMachine[machineID].ForUnit(unit.Name()).ByEndpoint(), nil 2206 } 2207 2208 appName := unit.ApplicationName() 2209 if ctx.openPortRangesForApplication == nil || ctx.openPortRangesForApplication[appName] == nil { 2210 unitPortRanges, err := unit.OpenedPortRanges() 2211 if err != nil { 2212 if errors.Is(err, errors.NotSupported) { 2213 // We support open/close port for CAAS sidecar applications but not operator applications. 2214 return make(network.GroupedPortRanges), nil 2215 } 2216 return nil, errors.Trace(err) 2217 } 2218 return unitPortRanges.ByEndpoint(), nil 2219 } 2220 return ctx.openPortRangesForApplication[appName].ForUnit(unit.Name()).ByEndpoint(), nil 2221 } 2222 2223 // entityIDForGlobalKey returns the entity id for the given global key. 2224 // It returns false if the key is not recognized. 2225 func (ctx *allWatcherContext) entityIDForGlobalKey(key string) (multiwatcher.EntityID, string, bool) { 2226 var result multiwatcher.EntityInfo 2227 if key == modelGlobalKey { 2228 result = &multiwatcher.ModelInfo{ 2229 ModelUUID: ctx.modelUUID, 2230 } 2231 return result.EntityID(), "", true 2232 } else if len(key) < 3 || key[1] != '#' { 2233 return multiwatcher.EntityID{}, "", false 2234 } 2235 // NOTE: we should probably have a single place where we have all the 2236 // global key functions, so we can check coverage both ways. 2237 parts := strings.Split(key, "#") 2238 id := parts[1] 2239 suffix := strings.Join(parts[2:], "#") 2240 if len(suffix) > 0 { 2241 suffix = "#" + suffix 2242 } 2243 switch parts[0] { 2244 case "m": 2245 result = &multiwatcher.MachineInfo{ 2246 ModelUUID: ctx.modelUUID, 2247 ID: id, 2248 } 2249 case "u": 2250 result = &multiwatcher.UnitInfo{ 2251 ModelUUID: ctx.modelUUID, 2252 Name: id, 2253 } 2254 case "a": 2255 result = &multiwatcher.ApplicationInfo{ 2256 ModelUUID: ctx.modelUUID, 2257 Name: id, 2258 } 2259 case "c": 2260 result = &multiwatcher.RemoteApplicationUpdate{ 2261 ModelUUID: ctx.modelUUID, 2262 Name: id, 2263 } 2264 default: 2265 return multiwatcher.EntityID{}, "", false 2266 } 2267 return result.EntityID(), suffix, true 2268 } 2269 2270 func (ctx *allWatcherContext) modelType() (ModelType, error) { 2271 if ctx.modelType_ != modelTypeNone { 2272 return ctx.modelType_, nil 2273 } 2274 model, err := ctx.state.Model() 2275 if err != nil { 2276 return modelTypeNone, errors.Trace(err) 2277 } 2278 ctx.modelType_ = model.Type() 2279 return ctx.modelType_, nil 2280 } 2281 2282 // entityIDForSettingsKey returns the entity id for the given 2283 // settings key. Any extra information in the key is returned in 2284 // extra. 2285 func (ctx *allWatcherContext) entityIDForSettingsKey(key string) (multiwatcher.EntityID, string, bool) { 2286 if !strings.HasPrefix(key, "a#") { 2287 eid, _, ok := ctx.entityIDForGlobalKey(key) 2288 return eid, "", ok 2289 } 2290 key = key[2:] 2291 i := strings.Index(key, "#") 2292 if i == -1 { 2293 return multiwatcher.EntityID{}, "", false 2294 } 2295 info := &multiwatcher.ApplicationInfo{ 2296 ModelUUID: ctx.modelUUID, 2297 Name: key[0:i], 2298 } 2299 extra := key[i+1:] 2300 return info.EntityID(), extra, true 2301 } 2302 2303 // entityIDForOpenedPortsKey returns the entity id for the given 2304 // openedPorts key. Any extra information in the key is discarded. 2305 func (ctx *allWatcherContext) entityIDForOpenedPortsKey(key string) (multiwatcher.EntityID, bool) { 2306 id, _, known := ctx.entityIDForGlobalKey(machineGlobalKey(key)) 2307 return id, known 2308 } 2309 2310 func (ctx *allWatcherContext) getMachineInfo(machineID string) *multiwatcher.MachineInfo { 2311 storeKey := &multiwatcher.MachineInfo{ 2312 ModelUUID: ctx.modelUUID, 2313 ID: machineID, 2314 } 2315 info0 := ctx.store.Get(storeKey.EntityID()) 2316 switch info := info0.(type) { 2317 case *multiwatcher.MachineInfo: 2318 return info 2319 } 2320 // In all other cases, which really should be never, return nil. 2321 return nil 2322 }