github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/state/migration_export.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state
     5  
     6  import (
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/juju/errors"
    11  	"github.com/juju/loggo"
    12  	"github.com/juju/names"
    13  	"github.com/juju/utils/set"
    14  	"gopkg.in/mgo.v2/bson"
    15  
    16  	"github.com/juju/juju/core/description"
    17  )
    18  
    19  // Export the current model for the State.
    20  func (st *State) Export() (description.Model, error) {
    21  	dbModel, err := st.Model()
    22  	if err != nil {
    23  		return nil, errors.Trace(err)
    24  	}
    25  
    26  	export := exporter{
    27  		st:      st,
    28  		dbModel: dbModel,
    29  		logger:  loggo.GetLogger("juju.state.export-model"),
    30  	}
    31  	if err := export.readAllStatuses(); err != nil {
    32  		return nil, errors.Annotate(err, "reading statuses")
    33  	}
    34  	if err := export.readAllStatusHistory(); err != nil {
    35  		return nil, errors.Trace(err)
    36  	}
    37  	if err := export.readAllSettings(); err != nil {
    38  		return nil, errors.Trace(err)
    39  	}
    40  	if err := export.readAllAnnotations(); err != nil {
    41  		return nil, errors.Trace(err)
    42  	}
    43  	if err := export.readAllConstraints(); err != nil {
    44  		return nil, errors.Trace(err)
    45  	}
    46  
    47  	envConfig, found := export.settings[modelGlobalKey]
    48  	if !found {
    49  		return nil, errors.New("missing environ config")
    50  	}
    51  
    52  	blocks, err := export.readBlocks()
    53  	if err != nil {
    54  		return nil, errors.Trace(err)
    55  	}
    56  
    57  	args := description.ModelArgs{
    58  		Owner:              dbModel.Owner(),
    59  		Config:             envConfig.Settings,
    60  		LatestToolsVersion: dbModel.LatestToolsVersion(),
    61  		Blocks:             blocks,
    62  	}
    63  	export.model = description.NewModel(args)
    64  	modelKey := dbModel.globalKey()
    65  	export.model.SetAnnotations(export.getAnnotations(modelKey))
    66  	if err := export.sequences(); err != nil {
    67  		return nil, errors.Trace(err)
    68  	}
    69  	constraintsArgs, err := export.constraintsArgs(modelKey)
    70  	if err != nil {
    71  		return nil, errors.Trace(err)
    72  	}
    73  	export.model.SetConstraints(constraintsArgs)
    74  
    75  	if err := export.modelUsers(); err != nil {
    76  		return nil, errors.Trace(err)
    77  	}
    78  	if err := export.machines(); err != nil {
    79  		return nil, errors.Trace(err)
    80  	}
    81  	if err := export.services(); err != nil {
    82  		return nil, errors.Trace(err)
    83  	}
    84  	if err := export.relations(); err != nil {
    85  		return nil, errors.Trace(err)
    86  	}
    87  
    88  	if err := export.model.Validate(); err != nil {
    89  		return nil, errors.Trace(err)
    90  	}
    91  
    92  	export.logExtras()
    93  
    94  	return export.model, nil
    95  }
    96  
    97  type exporter struct {
    98  	st      *State
    99  	dbModel *Model
   100  	model   description.Model
   101  	logger  loggo.Logger
   102  
   103  	annotations   map[string]annotatorDoc
   104  	constraints   map[string]bson.M
   105  	settings      map[string]settingsDoc
   106  	status        map[string]bson.M
   107  	statusHistory map[string][]historicalStatusDoc
   108  	// Map of service name to units. Populated as part
   109  	// of the services export.
   110  	units map[string][]*Unit
   111  }
   112  
   113  func (e *exporter) sequences() error {
   114  	sequences, closer := e.st.getCollection(sequenceC)
   115  	defer closer()
   116  
   117  	var docs []sequenceDoc
   118  	if err := sequences.Find(nil).All(&docs); err != nil {
   119  		return errors.Trace(err)
   120  	}
   121  
   122  	for _, doc := range docs {
   123  		e.model.SetSequence(doc.Name, doc.Counter)
   124  	}
   125  	return nil
   126  }
   127  
   128  func (e *exporter) readBlocks() (map[string]string, error) {
   129  	blocks, closer := e.st.getCollection(blocksC)
   130  	defer closer()
   131  
   132  	var docs []blockDoc
   133  	if err := blocks.Find(nil).All(&docs); err != nil {
   134  		return nil, errors.Trace(err)
   135  	}
   136  
   137  	result := make(map[string]string)
   138  	for _, doc := range docs {
   139  		// We don't care about the id, uuid, or tag.
   140  		// The uuid and tag both refer to the model uuid, and the
   141  		// id is opaque - even though it is sequence generated.
   142  		result[doc.Type.MigrationValue()] = doc.Message
   143  	}
   144  	return result, nil
   145  }
   146  
   147  func (e *exporter) modelUsers() error {
   148  	users, err := e.dbModel.Users()
   149  	if err != nil {
   150  		return errors.Trace(err)
   151  	}
   152  	lastConnections, err := e.readLastConnectionTimes()
   153  	if err != nil {
   154  		return errors.Trace(err)
   155  	}
   156  
   157  	for _, user := range users {
   158  		lastConn := lastConnections[strings.ToLower(user.UserName())]
   159  		arg := description.UserArgs{
   160  			Name:           user.UserTag(),
   161  			DisplayName:    user.DisplayName(),
   162  			CreatedBy:      names.NewUserTag(user.CreatedBy()),
   163  			DateCreated:    user.DateCreated(),
   164  			LastConnection: lastConn,
   165  			ReadOnly:       user.ReadOnly(),
   166  		}
   167  		e.model.AddUser(arg)
   168  	}
   169  	return nil
   170  }
   171  
   172  func (e *exporter) machines() error {
   173  	machines, err := e.st.AllMachines()
   174  	if err != nil {
   175  		return errors.Trace(err)
   176  	}
   177  	e.logger.Debugf("found %d machines", len(machines))
   178  
   179  	instanceDataCollection, closer := e.st.getCollection(instanceDataC)
   180  	defer closer()
   181  
   182  	var instData []instanceData
   183  	instances := make(map[string]instanceData)
   184  	if err := instanceDataCollection.Find(nil).All(&instData); err != nil {
   185  		return errors.Annotate(err, "instance data")
   186  	}
   187  	e.logger.Debugf("found %d instanceData", len(instData))
   188  	for _, data := range instData {
   189  		instances[data.MachineId] = data
   190  	}
   191  
   192  	// Read all the open ports documents.
   193  	openedPorts, closer := e.st.getCollection(openedPortsC)
   194  	defer closer()
   195  	var portsData []portsDoc
   196  	if err := openedPorts.Find(nil).All(&portsData); err != nil {
   197  		return errors.Annotate(err, "opened ports")
   198  	}
   199  	e.logger.Debugf("found %d openedPorts docs", len(portsData))
   200  
   201  	// We are iterating through a flat list of machines, but the migration
   202  	// model stores the nesting. The AllMachines method assures us that the
   203  	// machines are returned in an order so the parent will always before
   204  	// any children.
   205  	machineMap := make(map[string]description.Machine)
   206  
   207  	for _, machine := range machines {
   208  		e.logger.Debugf("export machine %s", machine.Id())
   209  
   210  		var exParent description.Machine
   211  		if parentId := ParentId(machine.Id()); parentId != "" {
   212  			var found bool
   213  			exParent, found = machineMap[parentId]
   214  			if !found {
   215  				return errors.Errorf("machine %s missing parent", machine.Id())
   216  			}
   217  		}
   218  
   219  		exMachine, err := e.newMachine(exParent, machine, instances, portsData)
   220  		if err != nil {
   221  			return errors.Trace(err)
   222  		}
   223  		machineMap[machine.Id()] = exMachine
   224  	}
   225  
   226  	return nil
   227  }
   228  
   229  func (e *exporter) newMachine(exParent description.Machine, machine *Machine, instances map[string]instanceData, portsData []portsDoc) (description.Machine, error) {
   230  	args := description.MachineArgs{
   231  		Id:            machine.MachineTag(),
   232  		Nonce:         machine.doc.Nonce,
   233  		PasswordHash:  machine.doc.PasswordHash,
   234  		Placement:     machine.doc.Placement,
   235  		Series:        machine.doc.Series,
   236  		ContainerType: machine.doc.ContainerType,
   237  	}
   238  
   239  	if supported, ok := machine.SupportedContainers(); ok {
   240  		containers := make([]string, len(supported))
   241  		for i, containerType := range supported {
   242  			containers[i] = string(containerType)
   243  		}
   244  		args.SupportedContainers = &containers
   245  	}
   246  
   247  	for _, job := range machine.Jobs() {
   248  		args.Jobs = append(args.Jobs, job.MigrationValue())
   249  	}
   250  
   251  	// A null value means that we don't yet know which containers
   252  	// are supported. An empty slice means 'no containers are supported'.
   253  	var exMachine description.Machine
   254  	if exParent == nil {
   255  		exMachine = e.model.AddMachine(args)
   256  	} else {
   257  		exMachine = exParent.AddContainer(args)
   258  	}
   259  	exMachine.SetAddresses(
   260  		e.newAddressArgsSlice(machine.doc.MachineAddresses),
   261  		e.newAddressArgsSlice(machine.doc.Addresses))
   262  	exMachine.SetPreferredAddresses(
   263  		e.newAddressArgs(machine.doc.PreferredPublicAddress),
   264  		e.newAddressArgs(machine.doc.PreferredPrivateAddress))
   265  
   266  	// We fully expect the machine to have tools set, and that there is
   267  	// some instance data.
   268  	instData, found := instances[machine.doc.Id]
   269  	if !found {
   270  		return nil, errors.NotValidf("missing instance data for machine %s", machine.Id())
   271  	}
   272  	exMachine.SetInstance(e.newCloudInstanceArgs(instData))
   273  
   274  	// Find the current machine status.
   275  	globalKey := machine.globalKey()
   276  	statusArgs, err := e.statusArgs(globalKey)
   277  	if err != nil {
   278  		return nil, errors.Annotatef(err, "status for machine %s", machine.Id())
   279  	}
   280  	exMachine.SetStatus(statusArgs)
   281  	exMachine.SetStatusHistory(e.statusHistoryArgs(globalKey))
   282  
   283  	tools, err := machine.AgentTools()
   284  	if err != nil {
   285  		// This means the tools aren't set, but they should be.
   286  		return nil, errors.Trace(err)
   287  	}
   288  
   289  	exMachine.SetTools(description.AgentToolsArgs{
   290  		Version: tools.Version,
   291  		URL:     tools.URL,
   292  		SHA256:  tools.SHA256,
   293  		Size:    tools.Size,
   294  	})
   295  
   296  	for _, args := range e.openedPortsArgsForMachine(machine.Id(), portsData) {
   297  		exMachine.AddOpenedPorts(args)
   298  	}
   299  
   300  	exMachine.SetAnnotations(e.getAnnotations(globalKey))
   301  
   302  	constraintsArgs, err := e.constraintsArgs(globalKey)
   303  	if err != nil {
   304  		return nil, errors.Trace(err)
   305  	}
   306  	exMachine.SetConstraints(constraintsArgs)
   307  
   308  	return exMachine, nil
   309  }
   310  
   311  func (e *exporter) openedPortsArgsForMachine(machineId string, portsData []portsDoc) []description.OpenedPortsArgs {
   312  	var result []description.OpenedPortsArgs
   313  	for _, doc := range portsData {
   314  		// Don't bother including a subnet if there are no ports open on it.
   315  		if doc.MachineID == machineId && len(doc.Ports) > 0 {
   316  			args := description.OpenedPortsArgs{SubnetID: doc.SubnetID}
   317  			for _, p := range doc.Ports {
   318  				args.OpenedPorts = append(args.OpenedPorts, description.PortRangeArgs{
   319  					UnitName: p.UnitName,
   320  					FromPort: p.FromPort,
   321  					ToPort:   p.ToPort,
   322  					Protocol: p.Protocol,
   323  				})
   324  			}
   325  			result = append(result, args)
   326  		}
   327  	}
   328  	return result
   329  }
   330  
   331  func (e *exporter) newAddressArgsSlice(a []address) []description.AddressArgs {
   332  	result := []description.AddressArgs{}
   333  	for _, addr := range a {
   334  		result = append(result, e.newAddressArgs(addr))
   335  	}
   336  	return result
   337  }
   338  
   339  func (e *exporter) newAddressArgs(a address) description.AddressArgs {
   340  	return description.AddressArgs{
   341  		Value:  a.Value,
   342  		Type:   a.AddressType,
   343  		Scope:  a.Scope,
   344  		Origin: a.Origin,
   345  	}
   346  }
   347  
   348  func (e *exporter) newCloudInstanceArgs(data instanceData) description.CloudInstanceArgs {
   349  	inst := description.CloudInstanceArgs{
   350  		InstanceId: string(data.InstanceId),
   351  		Status:     data.Status,
   352  	}
   353  	if data.Arch != nil {
   354  		inst.Architecture = *data.Arch
   355  	}
   356  	if data.Mem != nil {
   357  		inst.Memory = *data.Mem
   358  	}
   359  	if data.RootDisk != nil {
   360  		inst.RootDisk = *data.RootDisk
   361  	}
   362  	if data.CpuCores != nil {
   363  		inst.CpuCores = *data.CpuCores
   364  	}
   365  	if data.CpuPower != nil {
   366  		inst.CpuPower = *data.CpuPower
   367  	}
   368  	if data.Tags != nil {
   369  		inst.Tags = *data.Tags
   370  	}
   371  	if data.AvailZone != nil {
   372  		inst.AvailabilityZone = *data.AvailZone
   373  	}
   374  	return inst
   375  }
   376  
   377  func (e *exporter) services() error {
   378  	services, err := e.st.AllServices()
   379  	if err != nil {
   380  		return errors.Trace(err)
   381  	}
   382  	e.logger.Debugf("found %d services", len(services))
   383  
   384  	refcounts, err := e.readAllSettingsRefCounts()
   385  	if err != nil {
   386  		return errors.Trace(err)
   387  	}
   388  
   389  	e.units, err = e.readAllUnits()
   390  	if err != nil {
   391  		return errors.Trace(err)
   392  	}
   393  
   394  	meterStatus, err := e.readAllMeterStatus()
   395  	if err != nil {
   396  		return errors.Trace(err)
   397  	}
   398  
   399  	leaders := e.readServiceLeaders()
   400  
   401  	for _, service := range services {
   402  		serviceUnits := e.units[service.Name()]
   403  		leader := leaders[service.Name()]
   404  		if err := e.addService(service, refcounts, serviceUnits, meterStatus, leader); err != nil {
   405  			return errors.Trace(err)
   406  		}
   407  	}
   408  	return nil
   409  }
   410  
   411  func (e *exporter) readServiceLeaders() map[string]string {
   412  	result := make(map[string]string)
   413  	for key, value := range e.st.leadershipClient.Leases() {
   414  		result[key] = value.Holder
   415  	}
   416  	return result
   417  }
   418  
   419  func (e *exporter) addService(service *Service, refcounts map[string]int, units []*Unit, meterStatus map[string]*meterStatusDoc, leader string) error {
   420  	settingsKey := service.settingsKey()
   421  	leadershipKey := leadershipSettingsKey(service.Name())
   422  
   423  	serviceSettingsDoc, found := e.settings[settingsKey]
   424  	if !found {
   425  		return errors.Errorf("missing settings for service %q", service.Name())
   426  	}
   427  	refCount, found := refcounts[settingsKey]
   428  	if !found {
   429  		return errors.Errorf("missing settings refcount for service %q", service.Name())
   430  	}
   431  	leadershipSettingsDoc, found := e.settings[leadershipKey]
   432  	if !found {
   433  		return errors.Errorf("missing leadership settings for service %q", service.Name())
   434  	}
   435  
   436  	args := description.ServiceArgs{
   437  		Tag:                  service.ServiceTag(),
   438  		Series:               service.doc.Series,
   439  		Subordinate:          service.doc.Subordinate,
   440  		CharmURL:             service.doc.CharmURL.String(),
   441  		Channel:              service.doc.Channel,
   442  		CharmModifiedVersion: service.doc.CharmModifiedVersion,
   443  		ForceCharm:           service.doc.ForceCharm,
   444  		Exposed:              service.doc.Exposed,
   445  		MinUnits:             service.doc.MinUnits,
   446  		Settings:             serviceSettingsDoc.Settings,
   447  		SettingsRefCount:     refCount,
   448  		Leader:               leader,
   449  		LeadershipSettings:   leadershipSettingsDoc.Settings,
   450  		MetricsCredentials:   service.doc.MetricCredentials,
   451  	}
   452  	exService := e.model.AddService(args)
   453  	// Find the current service status.
   454  	globalKey := service.globalKey()
   455  	statusArgs, err := e.statusArgs(globalKey)
   456  	if err != nil {
   457  		return errors.Annotatef(err, "status for service %s", service.Name())
   458  	}
   459  	exService.SetStatus(statusArgs)
   460  	exService.SetStatusHistory(e.statusHistoryArgs(globalKey))
   461  	exService.SetAnnotations(e.getAnnotations(globalKey))
   462  
   463  	constraintsArgs, err := e.constraintsArgs(globalKey)
   464  	if err != nil {
   465  		return errors.Trace(err)
   466  	}
   467  	exService.SetConstraints(constraintsArgs)
   468  
   469  	for _, unit := range units {
   470  		agentKey := unit.globalAgentKey()
   471  		unitMeterStatus, found := meterStatus[agentKey]
   472  		if !found {
   473  			return errors.Errorf("missing meter status for unit %s", unit.Name())
   474  		}
   475  
   476  		args := description.UnitArgs{
   477  			Tag:             unit.UnitTag(),
   478  			Machine:         names.NewMachineTag(unit.doc.MachineId),
   479  			PasswordHash:    unit.doc.PasswordHash,
   480  			MeterStatusCode: unitMeterStatus.Code,
   481  			MeterStatusInfo: unitMeterStatus.Info,
   482  		}
   483  		if principalName, isSubordinate := unit.PrincipalName(); isSubordinate {
   484  			args.Principal = names.NewUnitTag(principalName)
   485  		}
   486  		if subs := unit.SubordinateNames(); len(subs) > 0 {
   487  			for _, subName := range subs {
   488  				args.Subordinates = append(args.Subordinates, names.NewUnitTag(subName))
   489  			}
   490  		}
   491  		exUnit := exService.AddUnit(args)
   492  		// workload uses globalKey, agent uses globalAgentKey.
   493  		globalKey := unit.globalKey()
   494  		statusArgs, err := e.statusArgs(globalKey)
   495  		if err != nil {
   496  			return errors.Annotatef(err, "workload status for unit %s", unit.Name())
   497  		}
   498  		exUnit.SetWorkloadStatus(statusArgs)
   499  		exUnit.SetWorkloadStatusHistory(e.statusHistoryArgs(globalKey))
   500  		statusArgs, err = e.statusArgs(agentKey)
   501  		if err != nil {
   502  			return errors.Annotatef(err, "agent status for unit %s", unit.Name())
   503  		}
   504  		exUnit.SetAgentStatus(statusArgs)
   505  		exUnit.SetAgentStatusHistory(e.statusHistoryArgs(agentKey))
   506  
   507  		tools, err := unit.AgentTools()
   508  		if err != nil {
   509  			// This means the tools aren't set, but they should be.
   510  			return errors.Trace(err)
   511  		}
   512  		exUnit.SetTools(description.AgentToolsArgs{
   513  			Version: tools.Version,
   514  			URL:     tools.URL,
   515  			SHA256:  tools.SHA256,
   516  			Size:    tools.Size,
   517  		})
   518  		exUnit.SetAnnotations(e.getAnnotations(globalKey))
   519  
   520  		constraintsArgs, err := e.constraintsArgs(agentKey)
   521  		if err != nil {
   522  			return errors.Trace(err)
   523  		}
   524  		exUnit.SetConstraints(constraintsArgs)
   525  	}
   526  
   527  	return nil
   528  }
   529  
   530  func (e *exporter) relations() error {
   531  	rels, err := e.st.AllRelations()
   532  	if err != nil {
   533  		return errors.Trace(err)
   534  	}
   535  	e.logger.Debugf("read %d relations", len(rels))
   536  
   537  	relationScopes, err := e.readAllRelationScopes()
   538  	if err != nil {
   539  		return errors.Trace(err)
   540  	}
   541  
   542  	for _, relation := range rels {
   543  		exRelation := e.model.AddRelation(description.RelationArgs{
   544  			Id:  relation.Id(),
   545  			Key: relation.String(),
   546  		})
   547  		for _, ep := range relation.Endpoints() {
   548  			exEndPoint := exRelation.AddEndpoint(description.EndpointArgs{
   549  				ServiceName: ep.ServiceName,
   550  				Name:        ep.Name,
   551  				Role:        string(ep.Role),
   552  				Interface:   ep.Interface,
   553  				Optional:    ep.Optional,
   554  				Limit:       ep.Limit,
   555  				Scope:       string(ep.Scope),
   556  			})
   557  			// We expect a relationScope and settings for each of the
   558  			// units of the specified service.
   559  			units := e.units[ep.ServiceName]
   560  			for _, unit := range units {
   561  				ru, err := relation.Unit(unit)
   562  				if err != nil {
   563  					return errors.Trace(err)
   564  				}
   565  				key := ru.key()
   566  				if !relationScopes.Contains(key) {
   567  					return errors.Errorf("missing relation scope for %s and %s", relation, unit.Name())
   568  				}
   569  				settingsDoc, found := e.settings[key]
   570  				if !found {
   571  					return errors.Errorf("missing relation settings for %s and %s", relation, unit.Name())
   572  				}
   573  				exEndPoint.SetUnitSettings(unit.Name(), settingsDoc.Settings)
   574  			}
   575  		}
   576  	}
   577  	return nil
   578  }
   579  
   580  func (e *exporter) readAllRelationScopes() (set.Strings, error) {
   581  	relationScopes, closer := e.st.getCollection(relationScopesC)
   582  	defer closer()
   583  
   584  	docs := []relationScopeDoc{}
   585  	err := relationScopes.Find(nil).All(&docs)
   586  	if err != nil {
   587  		return nil, errors.Annotate(err, "cannot get all relation scopes")
   588  	}
   589  	e.logger.Debugf("found %d relationScope docs", len(docs))
   590  
   591  	result := set.NewStrings()
   592  	for _, doc := range docs {
   593  		result.Add(doc.Key)
   594  	}
   595  	return result, nil
   596  }
   597  
   598  func (e *exporter) readAllUnits() (map[string][]*Unit, error) {
   599  	unitsCollection, closer := e.st.getCollection(unitsC)
   600  	defer closer()
   601  
   602  	docs := []unitDoc{}
   603  	err := unitsCollection.Find(nil).All(&docs)
   604  	if err != nil {
   605  		return nil, errors.Annotate(err, "cannot get all units")
   606  	}
   607  	e.logger.Debugf("found %d unit docs", len(docs))
   608  	result := make(map[string][]*Unit)
   609  	for _, doc := range docs {
   610  		units := result[doc.Service]
   611  		result[doc.Service] = append(units, newUnit(e.st, &doc))
   612  	}
   613  	return result, nil
   614  }
   615  
   616  func (e *exporter) readAllMeterStatus() (map[string]*meterStatusDoc, error) {
   617  	meterStatuses, closer := e.st.getCollection(meterStatusC)
   618  	defer closer()
   619  
   620  	docs := []meterStatusDoc{}
   621  	err := meterStatuses.Find(nil).All(&docs)
   622  	if err != nil {
   623  		return nil, errors.Annotate(err, "cannot get all meter status docs")
   624  	}
   625  	e.logger.Debugf("found %d meter status docs", len(docs))
   626  	result := make(map[string]*meterStatusDoc)
   627  	for _, doc := range docs {
   628  		result[e.st.localID(doc.DocID)] = &doc
   629  	}
   630  	return result, nil
   631  }
   632  
   633  func (e *exporter) readLastConnectionTimes() (map[string]time.Time, error) {
   634  	lastConnections, closer := e.st.getCollection(modelUserLastConnectionC)
   635  	defer closer()
   636  
   637  	var docs []modelUserLastConnectionDoc
   638  	if err := lastConnections.Find(nil).All(&docs); err != nil {
   639  		return nil, errors.Trace(err)
   640  	}
   641  
   642  	result := make(map[string]time.Time)
   643  	for _, doc := range docs {
   644  		result[doc.UserName] = doc.LastConnection.UTC()
   645  	}
   646  	return result, nil
   647  }
   648  
   649  func (e *exporter) readAllAnnotations() error {
   650  	annotations, closer := e.st.getCollection(annotationsC)
   651  	defer closer()
   652  
   653  	var docs []annotatorDoc
   654  	if err := annotations.Find(nil).All(&docs); err != nil {
   655  		return errors.Trace(err)
   656  	}
   657  	e.logger.Debugf("read %d annotations docs", len(docs))
   658  
   659  	e.annotations = make(map[string]annotatorDoc)
   660  	for _, doc := range docs {
   661  		e.annotations[doc.GlobalKey] = doc
   662  	}
   663  	return nil
   664  }
   665  
   666  func (e *exporter) readAllConstraints() error {
   667  	constraintsCollection, closer := e.st.getCollection(constraintsC)
   668  	defer closer()
   669  
   670  	// Since the constraintsDoc doesn't include any global key or _id
   671  	// fields, we can't just deserialize the entire collection into a slice
   672  	// of docs, so we get them all out with bson maps.
   673  	var docs []bson.M
   674  	err := constraintsCollection.Find(nil).All(&docs)
   675  	if err != nil {
   676  		return errors.Annotate(err, "failed to read constraints collection")
   677  	}
   678  
   679  	e.logger.Debugf("read %d constraints docs", len(docs))
   680  	e.constraints = make(map[string]bson.M)
   681  	for _, doc := range docs {
   682  		docId, ok := doc["_id"].(string)
   683  		if !ok {
   684  			return errors.Errorf("expected string, got %s (%T)", doc["_id"], doc["_id"])
   685  		}
   686  		id := e.st.localID(docId)
   687  		e.constraints[id] = doc
   688  		e.logger.Debugf("doc[%q] = %#v", id, doc)
   689  	}
   690  	return nil
   691  }
   692  
   693  // getAnnotations doesn't really care if there are any there or not
   694  // for the key, but if they were there, they are removed so we can
   695  // check at the end of the export for anything we have forgotten.
   696  func (e *exporter) getAnnotations(key string) map[string]string {
   697  	result, found := e.annotations[key]
   698  	if found {
   699  		delete(e.annotations, key)
   700  	}
   701  	return result.Annotations
   702  }
   703  
   704  func (e *exporter) readAllSettings() error {
   705  	settings, closer := e.st.getCollection(settingsC)
   706  	defer closer()
   707  
   708  	var docs []settingsDoc
   709  	if err := settings.Find(nil).All(&docs); err != nil {
   710  		return errors.Trace(err)
   711  	}
   712  
   713  	e.settings = make(map[string]settingsDoc)
   714  	for _, doc := range docs {
   715  		key := e.st.localID(doc.DocID)
   716  		e.settings[key] = doc
   717  	}
   718  	return nil
   719  }
   720  
   721  func (e *exporter) readAllStatuses() error {
   722  	statuses, closer := e.st.getCollection(statusesC)
   723  	defer closer()
   724  
   725  	var docs []bson.M
   726  	err := statuses.Find(nil).All(&docs)
   727  	if err != nil {
   728  		return errors.Annotate(err, "failed to read status collection")
   729  	}
   730  
   731  	e.logger.Debugf("read %d status documents", len(docs))
   732  	e.status = make(map[string]bson.M)
   733  	for _, doc := range docs {
   734  		docId, ok := doc["_id"].(string)
   735  		if !ok {
   736  			return errors.Errorf("expected string, got %s (%T)", doc["_id"], doc["_id"])
   737  		}
   738  		id := e.st.localID(docId)
   739  		e.status[id] = doc
   740  	}
   741  
   742  	return nil
   743  }
   744  
   745  func (e *exporter) readAllStatusHistory() error {
   746  	statuses, closer := e.st.getCollection(statusesHistoryC)
   747  	defer closer()
   748  
   749  	count := 0
   750  	e.statusHistory = make(map[string][]historicalStatusDoc)
   751  	var doc historicalStatusDoc
   752  	iter := statuses.Find(nil).Sort("-updated").Iter()
   753  	defer iter.Close()
   754  	for iter.Next(&doc) {
   755  		history := e.statusHistory[doc.GlobalKey]
   756  		e.statusHistory[doc.GlobalKey] = append(history, doc)
   757  		count++
   758  	}
   759  
   760  	if err := iter.Err(); err != nil {
   761  		return errors.Annotate(err, "failed to read status history collection")
   762  	}
   763  
   764  	e.logger.Debugf("read %d status history documents", count)
   765  
   766  	return nil
   767  }
   768  
   769  func (e *exporter) statusArgs(globalKey string) (description.StatusArgs, error) {
   770  	result := description.StatusArgs{}
   771  	statusDoc, found := e.status[globalKey]
   772  	if !found {
   773  		return result, errors.NotFoundf("status data for %s", globalKey)
   774  	}
   775  
   776  	status, ok := statusDoc["status"].(string)
   777  	if !ok {
   778  		return result, errors.Errorf("expected string for status, got %T", statusDoc["status"])
   779  	}
   780  	info, ok := statusDoc["statusinfo"].(string)
   781  	if !ok {
   782  		return result, errors.Errorf("expected string for statusinfo, got %T", statusDoc["statusinfo"])
   783  	}
   784  	// data is an embedded map and comes out as a bson.M
   785  	// A bson.M is map[string]interface{}, so we can type cast it.
   786  	data, ok := statusDoc["statusdata"].(bson.M)
   787  	if !ok {
   788  		return result, errors.Errorf("expected map for data, got %T", statusDoc["statusdata"])
   789  	}
   790  	dataMap := map[string]interface{}(data)
   791  	updated, ok := statusDoc["updated"].(int64)
   792  	if !ok {
   793  		return result, errors.Errorf("expected int64 for updated, got %T", statusDoc["updated"])
   794  	}
   795  
   796  	result.Value = status
   797  	result.Message = info
   798  	result.Data = dataMap
   799  	result.Updated = time.Unix(0, updated)
   800  	return result, nil
   801  }
   802  
   803  func (e *exporter) statusHistoryArgs(globalKey string) []description.StatusArgs {
   804  	history := e.statusHistory[globalKey]
   805  	result := make([]description.StatusArgs, len(history))
   806  	e.logger.Debugf("found %d status history docs for %s", len(history), globalKey)
   807  	for i, doc := range history {
   808  		result[i] = description.StatusArgs{
   809  			Value:   string(doc.Status),
   810  			Message: doc.StatusInfo,
   811  			Data:    doc.StatusData,
   812  			Updated: time.Unix(0, doc.Updated),
   813  		}
   814  	}
   815  
   816  	return result
   817  }
   818  
   819  func (e *exporter) constraintsArgs(globalKey string) (description.ConstraintsArgs, error) {
   820  	doc, found := e.constraints[globalKey]
   821  	if !found {
   822  		// No constraints for this key.
   823  		e.logger.Debugf("no constraints found for key %q", globalKey)
   824  		return description.ConstraintsArgs{}, nil
   825  	}
   826  	// We capture any type error using a closure to avoid having to return
   827  	// multiple values from the optional functions. This does mean that we will
   828  	// only report on the last one, but that is fine as there shouldn't be any.
   829  	var optionalErr error
   830  	optionalString := func(name string) string {
   831  		switch value := doc[name].(type) {
   832  		case nil:
   833  		case string:
   834  			return value
   835  		default:
   836  			optionalErr = errors.Errorf("expected uint64 for %s, got %T", name, value)
   837  		}
   838  		return ""
   839  	}
   840  	optionalInt := func(name string) uint64 {
   841  		switch value := doc[name].(type) {
   842  		case nil:
   843  		case uint64:
   844  			return value
   845  		case int64:
   846  			return uint64(value)
   847  		default:
   848  			optionalErr = errors.Errorf("expected uint64 for %s, got %T", name, value)
   849  		}
   850  		return 0
   851  	}
   852  	optionalStringSlice := func(name string) []string {
   853  		switch value := doc[name].(type) {
   854  		case nil:
   855  		case []string:
   856  			return value
   857  		default:
   858  			optionalErr = errors.Errorf("expected []string] for %s, got %T", name, value)
   859  		}
   860  		return nil
   861  	}
   862  	result := description.ConstraintsArgs{
   863  		Architecture: optionalString("arch"),
   864  		Container:    optionalString("container"),
   865  		CpuCores:     optionalInt("cpucores"),
   866  		CpuPower:     optionalInt("cpupower"),
   867  		InstanceType: optionalString("instancetype"),
   868  		Memory:       optionalInt("mem"),
   869  		RootDisk:     optionalInt("rootdisk"),
   870  		Spaces:       optionalStringSlice("spaces"),
   871  		Tags:         optionalStringSlice("tags"),
   872  	}
   873  	if optionalErr != nil {
   874  		return description.ConstraintsArgs{}, errors.Trace(optionalErr)
   875  	}
   876  	return result, nil
   877  }
   878  
   879  func (e *exporter) readAllSettingsRefCounts() (map[string]int, error) {
   880  	refCounts, closer := e.st.getCollection(settingsrefsC)
   881  	defer closer()
   882  
   883  	var docs []bson.M
   884  	err := refCounts.Find(nil).All(&docs)
   885  	if err != nil {
   886  		return nil, errors.Annotate(err, "failed to read settings refcount collection")
   887  	}
   888  
   889  	e.logger.Debugf("read %d settings refcount documents", len(docs))
   890  	result := make(map[string]int)
   891  	for _, doc := range docs {
   892  		docId, ok := doc["_id"].(string)
   893  		if !ok {
   894  			return nil, errors.Errorf("expected string, got %s (%T)", doc["_id"], doc["_id"])
   895  		}
   896  		id := e.st.localID(docId)
   897  		count, ok := doc["refcount"].(int)
   898  		if !ok {
   899  			return nil, errors.Errorf("expected int, got %s (%T)", doc["refcount"], doc["refcount"])
   900  		}
   901  		result[id] = count
   902  	}
   903  
   904  	return result, nil
   905  }
   906  
   907  func (e *exporter) logExtras() {
   908  	// As annotations are saved into the model, they are removed from the
   909  	// exporter's map. If there are any left at the end, we are missing
   910  	// things. Not an error just now, just a warning that we have missed
   911  	// something. Could potentially be an error at a later date when
   912  	// migrations are complete (but probably not).
   913  	for key, doc := range e.annotations {
   914  		e.logger.Warningf("unexported annotation for %s, %s", doc.Tag, key)
   915  	}
   916  }