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  }