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