github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/state/allwatcher.go (about)

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