github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/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  	"fmt"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/juju/charm/v12"
    12  	"github.com/juju/collections/set"
    13  	"github.com/juju/description/v5"
    14  	"github.com/juju/errors"
    15  	"github.com/juju/featureflag"
    16  	"github.com/juju/loggo"
    17  	"github.com/juju/mgo/v3/bson"
    18  	"github.com/juju/names/v5"
    19  
    20  	"github.com/juju/juju/core/arch"
    21  	corecharm "github.com/juju/juju/core/charm"
    22  	"github.com/juju/juju/core/container"
    23  	"github.com/juju/juju/core/crossmodel"
    24  	"github.com/juju/juju/core/network"
    25  	"github.com/juju/juju/core/payloads"
    26  	"github.com/juju/juju/core/resources"
    27  	"github.com/juju/juju/core/secrets"
    28  	"github.com/juju/juju/feature"
    29  	secretsprovider "github.com/juju/juju/secrets/provider"
    30  	"github.com/juju/juju/state/migrations"
    31  	"github.com/juju/juju/storage/poolmanager"
    32  )
    33  
    34  // The following exporter type is being refactored. This is to better model the
    35  // dependencies for creating the exported yaml and to correctly provide us to
    36  // unit tests at the right level of work. Rather than create integration tests
    37  // at the "unit" level.
    38  //
    39  // All exporting migrations have been currently moved to `state/migrations`.
    40  // Each provide their own type that allows them to execute a migration step
    41  // before return if successful or not via an error. The step resembles the
    42  // visitor pattern for good reason, as it allows us to safely model what is
    43  // required at a type level and type safety level. Everything is typed all the
    44  // way down. We can then create mocks for each one independently from other
    45  // migration steps (see examples).
    46  //
    47  // As this is in its infancy, there are intermediary steps. Each export type
    48  // creates its own StateExportMigration. In the future, there will be only
    49  // one and each migration step will add itself to that and Run for completion.
    50  //
    51  // Whilst we're creating these steps, it is expected to create the unit tests
    52  // and supplement all of these tests with existing tests, to ensure that no
    53  // gaps are missing. In the future the integration tests should be replaced with
    54  // the new shell tests to ensure a full end to end test is performed.
    55  
    56  const maxStatusHistoryEntries = 20
    57  
    58  // ExportConfig allows certain aspects of the model to be skipped
    59  // during the export. The intent of this is to be able to get a partial
    60  // export to support other API calls, like status.
    61  type ExportConfig struct {
    62  	IgnoreIncompleteModel    bool
    63  	SkipActions              bool
    64  	SkipAnnotations          bool
    65  	SkipCloudImageMetadata   bool
    66  	SkipCredentials          bool
    67  	SkipIPAddresses          bool
    68  	SkipSettings             bool
    69  	SkipSSHHostKeys          bool
    70  	SkipStatusHistory        bool
    71  	SkipLinkLayerDevices     bool
    72  	SkipUnitAgentBinaries    bool
    73  	SkipMachineAgentBinaries bool
    74  	SkipRelationData         bool
    75  	SkipInstanceData         bool
    76  	SkipApplicationOffers    bool
    77  	SkipOfferConnections     bool
    78  	SkipExternalControllers  bool
    79  	SkipSecrets              bool
    80  }
    81  
    82  // ExportPartial the current model for the State optionally skipping
    83  // aspects as defined by the ExportConfig.
    84  func (st *State) ExportPartial(cfg ExportConfig) (description.Model, error) {
    85  	return st.exportImpl(cfg, map[string]string{})
    86  }
    87  
    88  // Export the current model for the State.
    89  func (st *State) Export(leaders map[string]string) (description.Model, error) {
    90  	return st.exportImpl(ExportConfig{}, leaders)
    91  }
    92  
    93  func (st *State) exportImpl(cfg ExportConfig, leaders map[string]string) (description.Model, error) {
    94  	dbModel, err := st.Model()
    95  	if err != nil {
    96  		return nil, errors.Trace(err)
    97  	}
    98  	export := exporter{
    99  		st:      st,
   100  		cfg:     cfg,
   101  		dbModel: dbModel,
   102  		logger:  loggo.GetLogger("juju.state.export-model"),
   103  	}
   104  	if err := export.readAllStatuses(); err != nil {
   105  		return nil, errors.Annotate(err, "reading statuses")
   106  	}
   107  	if err := export.readAllStatusHistory(); err != nil {
   108  		return nil, errors.Trace(err)
   109  	}
   110  	if err := export.readAllSettings(); err != nil {
   111  		return nil, errors.Trace(err)
   112  	}
   113  	if err := export.readAllStorageConstraints(); err != nil {
   114  		return nil, errors.Trace(err)
   115  	}
   116  	if err := export.readAllAnnotations(); err != nil {
   117  		return nil, errors.Trace(err)
   118  	}
   119  	if err := export.readAllConstraints(); err != nil {
   120  		return nil, errors.Trace(err)
   121  	}
   122  
   123  	modelConfig, found := export.modelSettings[modelGlobalKey]
   124  	if !found && !cfg.SkipSettings {
   125  		return nil, errors.New("missing model config")
   126  	}
   127  	delete(export.modelSettings, modelGlobalKey)
   128  
   129  	blocks, err := export.readBlocks()
   130  	if err != nil {
   131  		return nil, errors.Trace(err)
   132  	}
   133  
   134  	args := description.ModelArgs{
   135  		Type:               string(dbModel.Type()),
   136  		Cloud:              dbModel.CloudName(),
   137  		CloudRegion:        dbModel.CloudRegion(),
   138  		Owner:              dbModel.Owner(),
   139  		Config:             modelConfig.Settings,
   140  		PasswordHash:       dbModel.doc.PasswordHash,
   141  		LatestToolsVersion: dbModel.LatestToolsVersion(),
   142  		EnvironVersion:     dbModel.EnvironVersion(),
   143  		Blocks:             blocks,
   144  	}
   145  	if args.SecretBackendID, err = export.secretBackendID(); err != nil {
   146  		return nil, errors.Trace(err)
   147  	}
   148  	export.model = description.NewModel(args)
   149  	if credsTag, credsSet := dbModel.CloudCredentialTag(); credsSet && !cfg.SkipCredentials {
   150  		creds, err := st.CloudCredential(credsTag)
   151  		if err != nil {
   152  			return nil, errors.Trace(err)
   153  		}
   154  		export.model.SetCloudCredential(description.CloudCredentialArgs{
   155  			Owner:      credsTag.Owner(),
   156  			Cloud:      credsTag.Cloud(),
   157  			Name:       credsTag.Name(),
   158  			AuthType:   creds.AuthType,
   159  			Attributes: creds.Attributes,
   160  		})
   161  	}
   162  	modelKey := dbModel.globalKey()
   163  	export.model.SetAnnotations(export.getAnnotations(modelKey))
   164  	if err := export.sequences(); err != nil {
   165  		return nil, errors.Trace(err)
   166  	}
   167  	constraintsArgs, err := export.constraintsArgs(modelKey)
   168  	if err != nil {
   169  		return nil, errors.Trace(err)
   170  	}
   171  	export.model.SetConstraints(constraintsArgs)
   172  	if err := export.modelStatus(); err != nil {
   173  		return nil, errors.Trace(err)
   174  	}
   175  	if err := export.modelUsers(); err != nil {
   176  		return nil, errors.Trace(err)
   177  	}
   178  	if err := export.machines(); err != nil {
   179  		return nil, errors.Trace(err)
   180  	}
   181  	if err := export.applications(leaders); err != nil {
   182  		return nil, errors.Trace(err)
   183  	}
   184  	if err := export.remoteApplications(); err != nil {
   185  		return nil, errors.Trace(err)
   186  	}
   187  	if err := export.relations(); err != nil {
   188  		return nil, errors.Trace(err)
   189  	}
   190  	if err := export.remoteEntities(); err != nil {
   191  		return nil, errors.Trace(err)
   192  	}
   193  	if err := export.offerConnections(); err != nil {
   194  		return nil, errors.Trace(err)
   195  	}
   196  	if err := export.relationNetworks(); err != nil {
   197  		return nil, errors.Trace(err)
   198  	}
   199  	if err := export.spaces(); err != nil {
   200  		return nil, errors.Trace(err)
   201  	}
   202  	if err := export.subnets(); err != nil {
   203  		return nil, errors.Trace(err)
   204  	}
   205  	if err := export.ipAddresses(); err != nil {
   206  		return nil, errors.Trace(err)
   207  	}
   208  	if err := export.linklayerdevices(); err != nil {
   209  		return nil, errors.Trace(err)
   210  	}
   211  	if err := export.sshHostKeys(); err != nil {
   212  		return nil, errors.Trace(err)
   213  	}
   214  	if err := export.actions(); err != nil {
   215  		return nil, errors.Trace(err)
   216  	}
   217  	if err := export.operations(); err != nil {
   218  		return nil, errors.Trace(err)
   219  	}
   220  	if err := export.cloudimagemetadata(); err != nil {
   221  		return nil, errors.Trace(err)
   222  	}
   223  	if err := export.storage(); err != nil {
   224  		return nil, errors.Trace(err)
   225  	}
   226  	if err := export.externalControllers(); err != nil {
   227  		return nil, errors.Trace(err)
   228  	}
   229  	if err := export.secrets(); err != nil {
   230  		return nil, errors.Trace(err)
   231  	}
   232  	if err := export.remoteSecrets(); err != nil {
   233  		return nil, errors.Trace(err)
   234  	}
   235  
   236  	// If we are doing a partial export, it doesn't really make sense
   237  	// to validate the model.
   238  	fullExport := ExportConfig{}
   239  	if cfg == fullExport {
   240  		if err := export.model.Validate(); err != nil {
   241  			return nil, errors.Trace(err)
   242  		}
   243  	}
   244  
   245  	export.model.SetSLA(dbModel.SLALevel(), dbModel.SLAOwner(), string(dbModel.SLACredential()))
   246  	export.model.SetMeterStatus(dbModel.MeterStatus().Code.String(), dbModel.MeterStatus().Info)
   247  
   248  	if featureflag.Enabled(feature.StrictMigration) {
   249  		if err := export.checkUnexportedValues(); err != nil {
   250  			return nil, errors.Trace(err)
   251  		}
   252  	}
   253  
   254  	return export.model, nil
   255  }
   256  
   257  // ExportStateMigration defines a migration for exporting various entities into
   258  // a destination description model from the source state.
   259  // It accumulates a series of migrations to run at a later time.
   260  // Running the state migration visits all the migrations and exits upon seeing
   261  // the first error from the migration.
   262  type ExportStateMigration struct {
   263  	src        *State
   264  	dst        description.Model
   265  	exporter   *exporter
   266  	migrations []func() error
   267  }
   268  
   269  // Add adds a migration to execute at a later time
   270  // Return error from the addition will cause the Run to terminate early.
   271  func (m *ExportStateMigration) Add(f func() error) {
   272  	m.migrations = append(m.migrations, f)
   273  }
   274  
   275  // Run executes all the migrations required to be run.
   276  func (m *ExportStateMigration) Run() error {
   277  	for _, f := range m.migrations {
   278  		if err := f(); err != nil {
   279  			return errors.Trace(err)
   280  		}
   281  	}
   282  	return nil
   283  }
   284  
   285  type exporter struct {
   286  	cfg     ExportConfig
   287  	st      *State
   288  	dbModel *Model
   289  	model   description.Model
   290  	logger  loggo.Logger
   291  
   292  	annotations             map[string]annotatorDoc
   293  	constraints             map[string]bson.M
   294  	modelSettings           map[string]settingsDoc
   295  	modelStorageConstraints map[string]storageConstraintsDoc
   296  	status                  map[string]bson.M
   297  	statusHistory           map[string][]historicalStatusDoc
   298  	// Map of application name to units. Populated as part
   299  	// of the applications export.
   300  	units map[string][]*Unit
   301  }
   302  
   303  func (e *exporter) sequences() error {
   304  	sequences, err := e.st.Sequences()
   305  	if err != nil {
   306  		return errors.Trace(err)
   307  	}
   308  
   309  	for name, value := range sequences {
   310  		e.model.SetSequence(name, value)
   311  	}
   312  	return nil
   313  }
   314  
   315  func (e *exporter) readBlocks() (map[string]string, error) {
   316  	blocks, closer := e.st.db().GetCollection(blocksC)
   317  	defer closer()
   318  
   319  	var docs []blockDoc
   320  	if err := blocks.Find(nil).All(&docs); err != nil {
   321  		return nil, errors.Trace(err)
   322  	}
   323  
   324  	result := make(map[string]string)
   325  	for _, doc := range docs {
   326  		// We don't care about the id, uuid, or tag.
   327  		// The uuid and tag both refer to the model uuid, and the
   328  		// id is opaque - even though it is sequence generated.
   329  		result[doc.Type.MigrationValue()] = doc.Message
   330  	}
   331  	return result, nil
   332  }
   333  
   334  func (e *exporter) modelStatus() error {
   335  	statusArgs, err := e.statusArgs(modelGlobalKey)
   336  	if err != nil {
   337  		return errors.Annotatef(err, "status for model")
   338  	}
   339  
   340  	e.model.SetStatus(statusArgs)
   341  	e.model.SetStatusHistory(e.statusHistoryArgs(modelGlobalKey))
   342  	return nil
   343  }
   344  
   345  func (e *exporter) modelUsers() error {
   346  	users, err := e.dbModel.Users()
   347  	if err != nil {
   348  		return errors.Trace(err)
   349  	}
   350  	lastConnections, err := e.readLastConnectionTimes()
   351  	if err != nil {
   352  		return errors.Trace(err)
   353  	}
   354  	for _, user := range users {
   355  		lastConn := lastConnections[strings.ToLower(user.UserName)]
   356  		arg := description.UserArgs{
   357  			Name:           user.UserTag,
   358  			DisplayName:    user.DisplayName,
   359  			CreatedBy:      user.CreatedBy,
   360  			DateCreated:    user.DateCreated,
   361  			LastConnection: lastConn,
   362  			Access:         string(user.Access),
   363  		}
   364  		e.model.AddUser(arg)
   365  	}
   366  	return nil
   367  }
   368  
   369  func (e *exporter) machines() error {
   370  	machines, err := e.st.AllMachines()
   371  	if err != nil {
   372  		return errors.Trace(err)
   373  	}
   374  	e.logger.Debugf("found %d machines", len(machines))
   375  
   376  	instances, err := e.loadMachineInstanceData()
   377  	if err != nil {
   378  		return errors.Trace(err)
   379  	}
   380  	blockDevices, err := e.loadMachineBlockDevices()
   381  	if err != nil {
   382  		return errors.Trace(err)
   383  	}
   384  	openedPorts, err := e.loadOpenedPortRangesForMachine()
   385  	if err != nil {
   386  		return errors.Trace(err)
   387  	}
   388  
   389  	// We are iterating through a flat list of machines, but the migration
   390  	// model stores the nesting. The AllMachines method assures us that the
   391  	// machines are returned in an order so the parent will always before
   392  	// any children.
   393  	machineMap := make(map[string]description.Machine)
   394  
   395  	for _, machine := range machines {
   396  		e.logger.Debugf("export machine %s", machine.Id())
   397  
   398  		var exParent description.Machine
   399  		if parentId := container.ParentId(machine.Id()); parentId != "" {
   400  			var found bool
   401  			exParent, found = machineMap[parentId]
   402  			if !found {
   403  				return errors.Errorf("machine %s missing parent", machine.Id())
   404  			}
   405  		}
   406  
   407  		exMachine, err := e.newMachine(exParent, machine, instances, openedPorts, blockDevices)
   408  		if err != nil {
   409  			return errors.Trace(err)
   410  		}
   411  		machineMap[machine.Id()] = exMachine
   412  	}
   413  
   414  	return nil
   415  }
   416  
   417  func (e *exporter) loadOpenedPortRangesForMachine() (map[string]*machinePortRanges, error) {
   418  	mprs, err := getOpenedPortRangesForAllMachines(e.st)
   419  	if err != nil {
   420  		return nil, errors.Annotate(err, "opened port ranges")
   421  	}
   422  
   423  	openedPortsByMachine := make(map[string]*machinePortRanges)
   424  	for _, mpr := range mprs {
   425  		openedPortsByMachine[mpr.MachineID()] = mpr
   426  	}
   427  
   428  	e.logger.Debugf("found %d openedPorts docs", len(openedPortsByMachine))
   429  	return openedPortsByMachine, nil
   430  }
   431  
   432  func (e *exporter) loadOpenedPortRangesForApplication() (map[string]*applicationPortRanges, error) {
   433  	mprs, err := getOpenedApplicationPortRangesForAllApplications(e.st)
   434  	if err != nil {
   435  		return nil, errors.Annotate(err, "opened port ranges")
   436  	}
   437  
   438  	openedPortsByApplication := make(map[string]*applicationPortRanges)
   439  	for _, mpr := range mprs {
   440  		openedPortsByApplication[mpr.ApplicationName()] = mpr
   441  	}
   442  
   443  	e.logger.Debugf("found %d openedPorts docs", len(openedPortsByApplication))
   444  	return openedPortsByApplication, nil
   445  }
   446  
   447  func (e *exporter) loadMachineInstanceData() (map[string]instanceData, error) {
   448  	instanceDataCollection, closer := e.st.db().GetCollection(instanceDataC)
   449  	defer closer()
   450  
   451  	var instData []instanceData
   452  	instances := make(map[string]instanceData)
   453  	if err := instanceDataCollection.Find(nil).All(&instData); err != nil {
   454  		return nil, errors.Annotate(err, "instance data")
   455  	}
   456  	e.logger.Debugf("found %d instanceData", len(instData))
   457  	for _, data := range instData {
   458  		instances[data.MachineId] = data
   459  	}
   460  	return instances, nil
   461  }
   462  
   463  func (e *exporter) loadMachineBlockDevices() (map[string][]BlockDeviceInfo, error) {
   464  	coll, closer := e.st.db().GetCollection(blockDevicesC)
   465  	defer closer()
   466  
   467  	var deviceData []blockDevicesDoc
   468  	result := make(map[string][]BlockDeviceInfo)
   469  	if err := coll.Find(nil).All(&deviceData); err != nil {
   470  		return nil, errors.Annotate(err, "block devices")
   471  	}
   472  	e.logger.Debugf("found %d block device records", len(deviceData))
   473  	for _, data := range deviceData {
   474  		result[data.Machine] = data.BlockDevices
   475  	}
   476  	return result, nil
   477  }
   478  
   479  func (e *exporter) newMachine(exParent description.Machine, machine *Machine, instances map[string]instanceData, portsData map[string]*machinePortRanges, blockDevices map[string][]BlockDeviceInfo) (description.Machine, error) {
   480  	args := description.MachineArgs{
   481  		Id:            machine.MachineTag(),
   482  		Nonce:         machine.doc.Nonce,
   483  		PasswordHash:  machine.doc.PasswordHash,
   484  		Placement:     machine.doc.Placement,
   485  		Base:          machine.doc.Base.String(),
   486  		ContainerType: machine.doc.ContainerType,
   487  	}
   488  
   489  	if supported, ok := machine.SupportedContainers(); ok {
   490  		containers := make([]string, len(supported))
   491  		for i, containerType := range supported {
   492  			containers[i] = string(containerType)
   493  		}
   494  		args.SupportedContainers = &containers
   495  	}
   496  
   497  	for _, job := range machine.Jobs() {
   498  		args.Jobs = append(args.Jobs, job.MigrationValue())
   499  	}
   500  
   501  	// A null value means that we don't yet know which containers
   502  	// are supported. An empty slice means 'no containers are supported'.
   503  	var exMachine description.Machine
   504  	if exParent == nil {
   505  		exMachine = e.model.AddMachine(args)
   506  	} else {
   507  		exMachine = exParent.AddContainer(args)
   508  	}
   509  	exMachine.SetAddresses(
   510  		e.newAddressArgsSlice(machine.doc.MachineAddresses),
   511  		e.newAddressArgsSlice(machine.doc.Addresses))
   512  	exMachine.SetPreferredAddresses(
   513  		e.newAddressArgs(machine.doc.PreferredPublicAddress),
   514  		e.newAddressArgs(machine.doc.PreferredPrivateAddress))
   515  
   516  	// We fully expect the machine to have tools set, and that there is
   517  	// some instance data.
   518  	if !e.cfg.SkipInstanceData {
   519  		instData, found := instances[machine.doc.Id]
   520  		if !found && !e.cfg.IgnoreIncompleteModel {
   521  			return nil, errors.NotValidf("missing instance data for machine %s", machine.Id())
   522  		}
   523  		if found {
   524  			exMachine.SetInstance(e.newCloudInstanceArgs(instData))
   525  			instance := exMachine.Instance()
   526  			instanceKey := machine.globalInstanceKey()
   527  			statusArgs, err := e.statusArgs(instanceKey)
   528  			if err != nil {
   529  				return nil, errors.Annotatef(err, "status for machine instance %s", machine.Id())
   530  			}
   531  			instance.SetStatus(statusArgs)
   532  			instance.SetStatusHistory(e.statusHistoryArgs(instanceKey))
   533  			// Extract the modification status from the status dataset
   534  			modificationInstanceKey := machine.globalModificationKey()
   535  			modificationStatusArgs, err := e.statusArgs(modificationInstanceKey)
   536  			if err != nil {
   537  				return nil, errors.Annotatef(err, "modification status for machine instance %s", machine.Id())
   538  			}
   539  			instance.SetModificationStatus(modificationStatusArgs)
   540  		}
   541  	}
   542  
   543  	// We don't rely on devices being there. If they aren't, we get an empty slice,
   544  	// which is fine to iterate over with range.
   545  	for _, device := range blockDevices[machine.doc.Id] {
   546  		exMachine.AddBlockDevice(description.BlockDeviceArgs{
   547  			Name:           device.DeviceName,
   548  			Links:          device.DeviceLinks,
   549  			Label:          device.Label,
   550  			UUID:           device.UUID,
   551  			HardwareID:     device.HardwareId,
   552  			WWN:            device.WWN,
   553  			BusAddress:     device.BusAddress,
   554  			Size:           device.Size,
   555  			FilesystemType: device.FilesystemType,
   556  			InUse:          device.InUse,
   557  			MountPoint:     device.MountPoint,
   558  		})
   559  	}
   560  
   561  	// Find the current machine status.
   562  	globalKey := machine.globalKey()
   563  	statusArgs, err := e.statusArgs(globalKey)
   564  	if err != nil {
   565  		return nil, errors.Annotatef(err, "status for machine %s", machine.Id())
   566  	}
   567  	exMachine.SetStatus(statusArgs)
   568  	exMachine.SetStatusHistory(e.statusHistoryArgs(globalKey))
   569  
   570  	if !e.cfg.SkipMachineAgentBinaries {
   571  		tools, err := machine.AgentTools()
   572  		if err != nil && !e.cfg.IgnoreIncompleteModel {
   573  			// This means the tools aren't set, but they should be.
   574  			return nil, errors.Trace(err)
   575  		}
   576  		if err == nil {
   577  			exMachine.SetTools(description.AgentToolsArgs{
   578  				Version: tools.Version,
   579  				URL:     tools.URL,
   580  				SHA256:  tools.SHA256,
   581  				Size:    tools.Size,
   582  			})
   583  		}
   584  	}
   585  
   586  	for _, args := range e.openedPortRangesArgsForMachine(machine.Id(), portsData) {
   587  		exMachine.AddOpenedPortRange(args)
   588  	}
   589  
   590  	exMachine.SetAnnotations(e.getAnnotations(globalKey))
   591  
   592  	constraintsArgs, err := e.constraintsArgs(globalKey)
   593  	if err != nil {
   594  		return nil, errors.Trace(err)
   595  	}
   596  	exMachine.SetConstraints(constraintsArgs)
   597  
   598  	return exMachine, nil
   599  }
   600  
   601  func (e *exporter) openedPortRangesArgsForMachine(machineID string, portsData map[string]*machinePortRanges) []description.OpenedPortRangeArgs {
   602  	if portsData[machineID] == nil {
   603  		return nil
   604  	}
   605  
   606  	var result []description.OpenedPortRangeArgs
   607  	for unitName, unitPorts := range portsData[machineID].ByUnit() {
   608  		for endpointName, portRanges := range unitPorts.ByEndpoint() {
   609  			for _, pr := range portRanges {
   610  				result = append(result, description.OpenedPortRangeArgs{
   611  					UnitName:     unitName,
   612  					EndpointName: endpointName,
   613  					FromPort:     pr.FromPort,
   614  					ToPort:       pr.ToPort,
   615  					Protocol:     pr.Protocol,
   616  				})
   617  			}
   618  		}
   619  	}
   620  	return result
   621  }
   622  
   623  func (e *exporter) newAddressArgsSlice(a []address) []description.AddressArgs {
   624  	result := make([]description.AddressArgs, len(a))
   625  	for i, addr := range a {
   626  		result[i] = e.newAddressArgs(addr)
   627  	}
   628  	return result
   629  }
   630  
   631  func (e *exporter) newAddressArgs(a address) description.AddressArgs {
   632  	return description.AddressArgs{
   633  		Value:   a.Value,
   634  		Type:    a.AddressType,
   635  		Scope:   a.Scope,
   636  		Origin:  a.Origin,
   637  		SpaceID: a.SpaceID,
   638  	}
   639  }
   640  
   641  func (e *exporter) newCloudInstanceArgs(data instanceData) description.CloudInstanceArgs {
   642  	inst := description.CloudInstanceArgs{
   643  		InstanceId:  string(data.InstanceId),
   644  		DisplayName: data.DisplayName,
   645  	}
   646  	if data.Arch != nil {
   647  		inst.Architecture = *data.Arch
   648  	}
   649  	if data.Mem != nil {
   650  		inst.Memory = *data.Mem
   651  	}
   652  	if data.RootDisk != nil {
   653  		inst.RootDisk = *data.RootDisk
   654  	}
   655  	if data.RootDiskSource != nil {
   656  		inst.RootDiskSource = *data.RootDiskSource
   657  	}
   658  	if data.CpuCores != nil {
   659  		inst.CpuCores = *data.CpuCores
   660  	}
   661  	if data.CpuPower != nil {
   662  		inst.CpuPower = *data.CpuPower
   663  	}
   664  	if data.Tags != nil {
   665  		inst.Tags = *data.Tags
   666  	}
   667  	if data.AvailZone != nil {
   668  		inst.AvailabilityZone = *data.AvailZone
   669  	}
   670  	if data.VirtType != nil {
   671  		inst.VirtType = *data.VirtType
   672  	}
   673  	if len(data.CharmProfiles) > 0 {
   674  		inst.CharmProfiles = data.CharmProfiles
   675  	}
   676  	return inst
   677  }
   678  
   679  func (e *exporter) applications(leaders map[string]string) error {
   680  	applications, err := e.st.AllApplications()
   681  	if err != nil {
   682  		return errors.Trace(err)
   683  	}
   684  	e.logger.Debugf("found %d applications", len(applications))
   685  
   686  	e.units, err = e.readAllUnits()
   687  	if err != nil {
   688  		return errors.Trace(err)
   689  	}
   690  
   691  	meterStatus, err := e.readAllMeterStatus()
   692  	if err != nil {
   693  		return errors.Trace(err)
   694  	}
   695  
   696  	bindings, err := e.readAllEndpointBindings()
   697  	if err != nil {
   698  		return errors.Trace(err)
   699  	}
   700  
   701  	payloads, err := e.readAllPayloads()
   702  	if err != nil {
   703  		return errors.Trace(err)
   704  	}
   705  
   706  	podSpecs, err := e.readAllPodSpecs()
   707  	if err != nil {
   708  		return errors.Trace(err)
   709  	}
   710  	cloudServices, err := e.readAllCloudServices()
   711  	if err != nil {
   712  		return errors.Trace(err)
   713  	}
   714  	cloudContainers, err := e.readAllCloudContainers()
   715  	if err != nil {
   716  		return errors.Trace(err)
   717  	}
   718  
   719  	resourcesSt := e.st.Resources()
   720  
   721  	appOfferMap, err := e.groupOffersByApplicationName()
   722  	if err != nil {
   723  		return errors.Trace(err)
   724  	}
   725  
   726  	openedPorts, err := e.loadOpenedPortRangesForApplication()
   727  	if err != nil {
   728  		return errors.Trace(err)
   729  	}
   730  
   731  	for _, application := range applications {
   732  		applicationUnits := e.units[application.Name()]
   733  		resources, err := resourcesSt.ListResources(application.Name())
   734  		if err != nil {
   735  			return errors.Trace(err)
   736  		}
   737  		appCtx := addApplicationContext{
   738  			application:      application,
   739  			units:            applicationUnits,
   740  			meterStatus:      meterStatus,
   741  			podSpecs:         podSpecs,
   742  			cloudServices:    cloudServices,
   743  			cloudContainers:  cloudContainers,
   744  			payloads:         payloads,
   745  			resources:        resources,
   746  			endpoingBindings: bindings,
   747  			leader:           leaders[application.Name()],
   748  			portsData:        openedPorts,
   749  		}
   750  
   751  		if appOfferMap != nil {
   752  			appCtx.offers = appOfferMap[application.Name()]
   753  		}
   754  
   755  		if err := e.addApplication(appCtx); err != nil {
   756  			return errors.Trace(err)
   757  		}
   758  
   759  	}
   760  	return nil
   761  }
   762  
   763  func (e *exporter) readAllStorageConstraints() error {
   764  	coll, closer := e.st.db().GetCollection(storageConstraintsC)
   765  	defer closer()
   766  
   767  	storageConstraints := make(map[string]storageConstraintsDoc)
   768  	var doc storageConstraintsDoc
   769  	iter := coll.Find(nil).Iter()
   770  	defer func() { _ = iter.Close() }()
   771  	for iter.Next(&doc) {
   772  		storageConstraints[e.st.localID(doc.DocID)] = doc
   773  	}
   774  	if err := iter.Close(); err != nil {
   775  		return errors.Annotate(err, "failed to read storage constraints")
   776  	}
   777  	e.logger.Debugf("read %d storage constraint documents", len(storageConstraints))
   778  	e.modelStorageConstraints = storageConstraints
   779  	return nil
   780  }
   781  
   782  func (e *exporter) storageConstraints(doc storageConstraintsDoc) map[string]description.StorageDirectiveArgs {
   783  	result := make(map[string]description.StorageDirectiveArgs)
   784  	for key, value := range doc.Constraints {
   785  		result[key] = description.StorageDirectiveArgs{
   786  			Pool:  value.Pool,
   787  			Size:  value.Size,
   788  			Count: value.Count,
   789  		}
   790  	}
   791  	return result
   792  }
   793  
   794  func (e *exporter) readAllPayloads() (map[string][]payloads.FullPayloadInfo, error) {
   795  	result := make(map[string][]payloads.FullPayloadInfo)
   796  	all, err := ModelPayloads{db: e.st.database}.ListAll()
   797  	if err != nil {
   798  		return nil, errors.Trace(err)
   799  	}
   800  	for _, pl := range all {
   801  		result[pl.Unit] = append(result[pl.Unit], pl)
   802  	}
   803  	return result, nil
   804  }
   805  
   806  type addApplicationContext struct {
   807  	application      *Application
   808  	units            []*Unit
   809  	meterStatus      map[string]*meterStatusDoc
   810  	leader           string
   811  	payloads         map[string][]payloads.FullPayloadInfo
   812  	resources        resources.ApplicationResources
   813  	endpoingBindings map[string]bindingsMap
   814  	portsData        map[string]*applicationPortRanges
   815  
   816  	// CAAS
   817  	podSpecs        map[string]string
   818  	cloudServices   map[string]*cloudServiceDoc
   819  	cloudContainers map[string]*cloudContainerDoc
   820  
   821  	// Offers
   822  	offers []*crossmodel.ApplicationOffer
   823  }
   824  
   825  func (e *exporter) addApplication(ctx addApplicationContext) error {
   826  	application := ctx.application
   827  	appName := application.Name()
   828  	globalKey := application.globalKey()
   829  	charmConfigKey := application.charmConfigKey()
   830  	appConfigKey := application.applicationConfigKey()
   831  	leadershipKey := leadershipSettingsKey(appName)
   832  	storageConstraintsKey := application.storageConstraintsKey()
   833  
   834  	var charmConfig map[string]interface{}
   835  	applicationCharmSettingsDoc, found := e.modelSettings[charmConfigKey]
   836  	if !found && !e.cfg.SkipSettings && !e.cfg.IgnoreIncompleteModel {
   837  		return errors.Errorf("missing charm settings for application %q", appName)
   838  	}
   839  	if found {
   840  		charmConfig = applicationCharmSettingsDoc.Settings
   841  	}
   842  	delete(e.modelSettings, charmConfigKey)
   843  
   844  	var applicationConfig map[string]interface{}
   845  	applicationConfigDoc, found := e.modelSettings[appConfigKey]
   846  	if !found && !e.cfg.SkipSettings && !e.cfg.IgnoreIncompleteModel {
   847  		return errors.Errorf("missing config for application %q", appName)
   848  	}
   849  	if found {
   850  		applicationConfig = applicationConfigDoc.Settings
   851  	}
   852  	delete(e.modelSettings, appConfigKey)
   853  
   854  	var leadershipSettings map[string]interface{}
   855  	leadershipSettingsDoc, found := e.modelSettings[leadershipKey]
   856  	if !found && !e.cfg.SkipSettings && !e.cfg.IgnoreIncompleteModel {
   857  		return errors.Errorf("missing leadership settings for application %q", appName)
   858  	}
   859  	if found {
   860  		leadershipSettings = leadershipSettingsDoc.Settings
   861  	}
   862  	delete(e.modelSettings, leadershipKey)
   863  
   864  	args := description.ApplicationArgs{
   865  		Tag:                  application.ApplicationTag(),
   866  		Type:                 e.model.Type(),
   867  		Subordinate:          application.doc.Subordinate,
   868  		CharmURL:             *application.doc.CharmURL,
   869  		CharmModifiedVersion: application.doc.CharmModifiedVersion,
   870  		ForceCharm:           application.doc.ForceCharm,
   871  		Exposed:              application.doc.Exposed,
   872  		PasswordHash:         application.doc.PasswordHash,
   873  		Placement:            application.doc.Placement,
   874  		HasResources:         application.doc.HasResources,
   875  		DesiredScale:         application.doc.DesiredScale,
   876  		MinUnits:             application.doc.MinUnits,
   877  		EndpointBindings:     map[string]string(ctx.endpoingBindings[globalKey]),
   878  		ApplicationConfig:    applicationConfig,
   879  		CharmConfig:          charmConfig,
   880  		Leader:               ctx.leader,
   881  		LeadershipSettings:   leadershipSettings,
   882  		MetricsCredentials:   application.doc.MetricCredentials,
   883  		PodSpec:              ctx.podSpecs[application.globalKey()],
   884  	}
   885  
   886  	if cloudService, found := ctx.cloudServices[application.globalKey()]; found {
   887  		args.CloudService = e.cloudService(cloudService)
   888  	}
   889  	if constraints, found := e.modelStorageConstraints[storageConstraintsKey]; found {
   890  		args.StorageDirectives = e.storageConstraints(constraints)
   891  	}
   892  
   893  	if ps := application.ProvisioningState(); ps != nil {
   894  		args.ProvisioningState = &description.ProvisioningStateArgs{
   895  			Scaling:     ps.Scaling,
   896  			ScaleTarget: ps.ScaleTarget,
   897  		}
   898  	}
   899  
   900  	// Include exposed endpoint details
   901  	if len(application.doc.ExposedEndpoints) > 0 {
   902  		args.ExposedEndpoints = make(map[string]description.ExposedEndpointArgs)
   903  		for epName, details := range application.doc.ExposedEndpoints {
   904  			args.ExposedEndpoints[epName] = description.ExposedEndpointArgs{
   905  				ExposeToSpaceIDs: details.ExposeToSpaceIDs,
   906  				ExposeToCIDRs:    details.ExposeToCIDRs,
   907  			}
   908  		}
   909  	}
   910  
   911  	exApplication := e.model.AddApplication(args)
   912  
   913  	// Populate offer list
   914  	for _, offer := range ctx.offers {
   915  		endpoints := make(map[string]string, len(offer.Endpoints))
   916  		for k, ep := range offer.Endpoints {
   917  			endpoints[k] = ep.Name
   918  		}
   919  
   920  		userMap, err := e.st.GetOfferUsers(offer.OfferUUID)
   921  		if err != nil {
   922  			return errors.Annotatef(err, "ACL for offer %s", offer.OfferName)
   923  		}
   924  
   925  		var acl map[string]string
   926  		if len(userMap) != 0 {
   927  			acl = make(map[string]string, len(userMap))
   928  			for user, access := range userMap {
   929  				acl[user] = accessToString(access)
   930  			}
   931  		}
   932  
   933  		_ = exApplication.AddOffer(description.ApplicationOfferArgs{
   934  			OfferUUID:              offer.OfferUUID,
   935  			OfferName:              offer.OfferName,
   936  			Endpoints:              endpoints,
   937  			ACL:                    acl,
   938  			ApplicationName:        offer.ApplicationName,
   939  			ApplicationDescription: offer.ApplicationDescription,
   940  		})
   941  	}
   942  
   943  	// Find the current application status.
   944  	statusArgs, err := e.statusArgs(globalKey)
   945  	if err != nil {
   946  		return errors.Annotatef(err, "status for application %s", appName)
   947  	}
   948  
   949  	exApplication.SetStatus(statusArgs)
   950  	exApplication.SetStatusHistory(e.statusHistoryArgs(globalKey))
   951  	exApplication.SetAnnotations(e.getAnnotations(globalKey))
   952  
   953  	globalAppWorkloadKey := applicationGlobalOperatorKey(appName)
   954  	operatorStatusArgs, err := e.statusArgs(globalAppWorkloadKey)
   955  	if err != nil {
   956  		if !errors.IsNotFound(err) {
   957  			return errors.Annotatef(err, "application operator status for application %s", appName)
   958  		}
   959  	}
   960  	exApplication.SetOperatorStatus(operatorStatusArgs)
   961  	e.statusHistoryArgs(globalAppWorkloadKey)
   962  
   963  	constraintsArgs, err := e.constraintsArgs(globalKey)
   964  	if err != nil {
   965  		return errors.Trace(err)
   966  	}
   967  	exApplication.SetConstraints(constraintsArgs)
   968  
   969  	defaultArch := constraintsArgs.Architecture
   970  	if defaultArch == "" {
   971  		defaultArch = arch.DefaultArchitecture
   972  	}
   973  	charmOriginArgs, err := e.getCharmOrigin(application.doc, defaultArch)
   974  	if err != nil {
   975  		return errors.Annotatef(err, "charm origin")
   976  	}
   977  	exApplication.SetCharmOrigin(charmOriginArgs)
   978  
   979  	if err := e.setResources(exApplication, ctx.resources); err != nil {
   980  		return errors.Trace(err)
   981  	}
   982  
   983  	for _, args := range e.openedPortRangesArgsForApplication(appName, ctx.portsData) {
   984  		exApplication.AddOpenedPortRange(args)
   985  	}
   986  
   987  	// Set Tools for application - this is only for CAAS models.
   988  	isSidecar, err := ctx.application.IsSidecar()
   989  	if err != nil {
   990  		return errors.Trace(err)
   991  	}
   992  
   993  	for _, unit := range ctx.units {
   994  		agentKey := unit.globalAgentKey()
   995  		unitMeterStatus, found := ctx.meterStatus[agentKey]
   996  		if !found {
   997  			return errors.Errorf("missing meter status for unit %s", unit.Name())
   998  		}
   999  
  1000  		workloadVersion, err := e.unitWorkloadVersion(unit)
  1001  		if err != nil {
  1002  			return errors.Trace(err)
  1003  		}
  1004  		args := description.UnitArgs{
  1005  			Tag:             unit.UnitTag(),
  1006  			Type:            string(unit.modelType),
  1007  			Machine:         names.NewMachineTag(unit.doc.MachineId),
  1008  			WorkloadVersion: workloadVersion,
  1009  			PasswordHash:    unit.doc.PasswordHash,
  1010  			MeterStatusCode: unitMeterStatus.Code,
  1011  			MeterStatusInfo: unitMeterStatus.Info,
  1012  		}
  1013  		if principalName, isSubordinate := unit.PrincipalName(); isSubordinate {
  1014  			args.Principal = names.NewUnitTag(principalName)
  1015  		}
  1016  		if subs := unit.SubordinateNames(); len(subs) > 0 {
  1017  			for _, subName := range subs {
  1018  				args.Subordinates = append(args.Subordinates, names.NewUnitTag(subName))
  1019  			}
  1020  		}
  1021  		if cloudContainer, found := ctx.cloudContainers[unit.globalKey()]; found {
  1022  			args.CloudContainer = e.cloudContainer(cloudContainer)
  1023  		}
  1024  
  1025  		// Export charm and agent state stored to the controller.
  1026  		unitState, err := unit.State()
  1027  		if err != nil {
  1028  			return errors.Trace(err)
  1029  		}
  1030  		if charmState, found := unitState.CharmState(); found {
  1031  			args.CharmState = charmState
  1032  		}
  1033  		if relationState, found := unitState.RelationState(); found {
  1034  			args.RelationState = relationState
  1035  		}
  1036  		if uniterState, found := unitState.UniterState(); found {
  1037  			args.UniterState = uniterState
  1038  		}
  1039  		if storageState, found := unitState.StorageState(); found {
  1040  			args.StorageState = storageState
  1041  		}
  1042  		if meterStatusState, found := unitState.MeterStatusState(); found {
  1043  			args.MeterStatusState = meterStatusState
  1044  		}
  1045  		exUnit := exApplication.AddUnit(args)
  1046  
  1047  		e.setUnitResources(exUnit, ctx.resources.UnitResources)
  1048  
  1049  		if err := e.setUnitPayloads(exUnit, ctx.payloads[unit.UnitTag().Id()]); err != nil {
  1050  			return errors.Trace(err)
  1051  		}
  1052  
  1053  		// workload uses globalKey, agent uses globalAgentKey,
  1054  		// workload version uses globalWorkloadVersionKey.
  1055  		globalKey := unit.globalKey()
  1056  		statusArgs, err := e.statusArgs(globalKey)
  1057  		if err != nil {
  1058  			return errors.Annotatef(err, "workload status for unit %s", unit.Name())
  1059  		}
  1060  		exUnit.SetWorkloadStatus(statusArgs)
  1061  		exUnit.SetWorkloadStatusHistory(e.statusHistoryArgs(globalKey))
  1062  
  1063  		statusArgs, err = e.statusArgs(agentKey)
  1064  		if err != nil {
  1065  			return errors.Annotatef(err, "agent status for unit %s", unit.Name())
  1066  		}
  1067  		exUnit.SetAgentStatus(statusArgs)
  1068  		exUnit.SetAgentStatusHistory(e.statusHistoryArgs(agentKey))
  1069  
  1070  		workloadVersionKey := unit.globalWorkloadVersionKey()
  1071  		exUnit.SetWorkloadVersionHistory(e.statusHistoryArgs(workloadVersionKey))
  1072  
  1073  		if (e.dbModel.Type() != ModelTypeCAAS && !e.cfg.SkipUnitAgentBinaries) || isSidecar {
  1074  			tools, err := unit.AgentTools()
  1075  			if err != nil && !e.cfg.IgnoreIncompleteModel {
  1076  				// This means the tools aren't set, but they should be.
  1077  				return errors.Trace(err)
  1078  			}
  1079  			if err == nil {
  1080  				exUnit.SetTools(description.AgentToolsArgs{
  1081  					Version: tools.Version,
  1082  					URL:     tools.URL,
  1083  					SHA256:  tools.SHA256,
  1084  					Size:    tools.Size,
  1085  				})
  1086  			}
  1087  		}
  1088  		if e.dbModel.Type() == ModelTypeCAAS {
  1089  			// TODO(caas) - Actually use the exported cloud container details and status history.
  1090  			// Currently these are only grabbed to make the MigrationExportSuite tests happy.
  1091  			globalCCKey := unit.globalCloudContainerKey()
  1092  			_, err = e.statusArgs(globalCCKey)
  1093  			if err != nil {
  1094  				if !errors.IsNotFound(err) {
  1095  					return errors.Annotatef(err, "cloud container workload status for unit %s", unit.Name())
  1096  				}
  1097  			}
  1098  			e.statusHistoryArgs(globalCCKey)
  1099  		}
  1100  		exUnit.SetAnnotations(e.getAnnotations(globalKey))
  1101  
  1102  		constraintsArgs, err := e.constraintsArgs(agentKey)
  1103  		if err != nil {
  1104  			return errors.Trace(err)
  1105  		}
  1106  		exUnit.SetConstraints(constraintsArgs)
  1107  	}
  1108  
  1109  	if e.dbModel.Type() == ModelTypeCAAS && !isSidecar {
  1110  		tools, err := ctx.application.AgentTools()
  1111  		if err != nil {
  1112  			// This means the tools aren't set, but they should be.
  1113  			return errors.Trace(err)
  1114  		}
  1115  		exApplication.SetTools(description.AgentToolsArgs{
  1116  			Version: tools.Version,
  1117  		})
  1118  	}
  1119  
  1120  	return nil
  1121  }
  1122  
  1123  func (e *exporter) openedPortRangesArgsForApplication(appName string, portsData map[string]*applicationPortRanges) []description.OpenedPortRangeArgs {
  1124  	if portsData[appName] == nil {
  1125  		return nil
  1126  	}
  1127  
  1128  	var result []description.OpenedPortRangeArgs
  1129  	for unitName, unitPorts := range portsData[appName].ByUnit() {
  1130  		for endpointName, portRanges := range unitPorts.ByEndpoint() {
  1131  			for _, pr := range portRanges {
  1132  				result = append(result, description.OpenedPortRangeArgs{
  1133  					UnitName:     unitName,
  1134  					EndpointName: endpointName,
  1135  					FromPort:     pr.FromPort,
  1136  					ToPort:       pr.ToPort,
  1137  					Protocol:     pr.Protocol,
  1138  				})
  1139  			}
  1140  		}
  1141  	}
  1142  	return result
  1143  }
  1144  
  1145  func (e *exporter) unitWorkloadVersion(unit *Unit) (string, error) {
  1146  	// Rather than call unit.WorkloadVersion(), which does a database
  1147  	// query, we go directly to the status value that is stored.
  1148  	key := unit.globalWorkloadVersionKey()
  1149  	info, err := e.statusArgs(key)
  1150  	if err != nil {
  1151  		return "", errors.Trace(err)
  1152  	}
  1153  	return info.Message, nil
  1154  }
  1155  
  1156  func (e *exporter) setResources(exApp description.Application, resources resources.ApplicationResources) error {
  1157  	if len(resources.Resources) != len(resources.CharmStoreResources) {
  1158  		return errors.New("number of resources don't match charm store resources")
  1159  	}
  1160  
  1161  	for i, resource := range resources.Resources {
  1162  		exResource := exApp.AddResource(description.ResourceArgs{
  1163  			Name: resource.Name,
  1164  		})
  1165  		exResource.SetApplicationRevision(description.ResourceRevisionArgs{
  1166  			Revision:       resource.Revision,
  1167  			Type:           resource.Type.String(),
  1168  			Path:           resource.Path,
  1169  			Description:    resource.Description,
  1170  			Origin:         resource.Origin.String(),
  1171  			FingerprintHex: resource.Fingerprint.Hex(),
  1172  			Size:           resource.Size,
  1173  			Timestamp:      resource.Timestamp,
  1174  			Username:       resource.Username,
  1175  		})
  1176  		csResource := resources.CharmStoreResources[i]
  1177  		exResource.SetCharmStoreRevision(description.ResourceRevisionArgs{
  1178  			Revision:       csResource.Revision,
  1179  			Type:           csResource.Type.String(),
  1180  			Path:           csResource.Path,
  1181  			Description:    csResource.Description,
  1182  			Origin:         csResource.Origin.String(),
  1183  			Size:           csResource.Size,
  1184  			FingerprintHex: csResource.Fingerprint.Hex(),
  1185  		})
  1186  	}
  1187  
  1188  	return nil
  1189  }
  1190  
  1191  func (e *exporter) setUnitResources(exUnit description.Unit, allResources []resources.UnitResources) {
  1192  	for _, res := range findUnitResources(exUnit.Name(), allResources) {
  1193  		exUnit.AddResource(description.UnitResourceArgs{
  1194  			Name: res.Name,
  1195  			RevisionArgs: description.ResourceRevisionArgs{
  1196  				Revision:       res.Revision,
  1197  				Type:           res.Type.String(),
  1198  				Path:           res.Path,
  1199  				Description:    res.Description,
  1200  				Origin:         res.Origin.String(),
  1201  				FingerprintHex: res.Fingerprint.Hex(),
  1202  				Size:           res.Size,
  1203  				Timestamp:      res.Timestamp,
  1204  				Username:       res.Username,
  1205  			},
  1206  		})
  1207  	}
  1208  }
  1209  
  1210  func findUnitResources(unitName string, allResources []resources.UnitResources) []resources.Resource {
  1211  	for _, unitResources := range allResources {
  1212  		if unitResources.Tag.Id() == unitName {
  1213  			return unitResources.Resources
  1214  		}
  1215  	}
  1216  	return nil
  1217  }
  1218  
  1219  func (e *exporter) setUnitPayloads(exUnit description.Unit, payloads []payloads.FullPayloadInfo) error {
  1220  	if len(payloads) == 0 {
  1221  		return nil
  1222  	}
  1223  	unitID := exUnit.Tag().Id()
  1224  	machineID := exUnit.Machine().Id()
  1225  	for _, payload := range payloads {
  1226  		if payload.Machine != machineID {
  1227  			return errors.NotValidf("payload for unit %q reports wrong machine %q (should be %q)", unitID, payload.Machine, machineID)
  1228  		}
  1229  		args := description.PayloadArgs{
  1230  			Name:   payload.Name,
  1231  			Type:   payload.Type,
  1232  			RawID:  payload.ID,
  1233  			State:  payload.Status,
  1234  			Labels: payload.Labels,
  1235  		}
  1236  		exUnit.AddPayload(args)
  1237  	}
  1238  	return nil
  1239  }
  1240  
  1241  func (e *exporter) relations() error {
  1242  	rels, err := e.st.AllRelations()
  1243  	if err != nil {
  1244  		return errors.Trace(err)
  1245  	}
  1246  	e.logger.Debugf("read %d relations", len(rels))
  1247  
  1248  	relationScopes := set.NewStrings()
  1249  	if !e.cfg.SkipRelationData {
  1250  		relationScopes, err = e.readAllRelationScopes()
  1251  		if err != nil {
  1252  			return errors.Trace(err)
  1253  		}
  1254  	}
  1255  
  1256  	for _, relation := range rels {
  1257  		exRelation := e.model.AddRelation(description.RelationArgs{
  1258  			Id:  relation.Id(),
  1259  			Key: relation.String(),
  1260  		})
  1261  		globalKey := relation.globalScope()
  1262  		statusArgs, err := e.statusArgs(globalKey)
  1263  		if err == nil {
  1264  			exRelation.SetStatus(statusArgs)
  1265  		} else if !errors.IsNotFound(err) {
  1266  			return errors.Annotatef(err, "status for relation %v", relation.Id())
  1267  		}
  1268  
  1269  		for _, ep := range relation.Endpoints() {
  1270  			if err := e.relationEndpoint(relation, exRelation, ep, relationScopes); err != nil {
  1271  				return errors.Trace(err)
  1272  			}
  1273  		}
  1274  	}
  1275  	return nil
  1276  }
  1277  
  1278  func (e *exporter) relationEndpoint(
  1279  	relation *Relation,
  1280  	exRelation description.Relation,
  1281  	ep Endpoint,
  1282  	relationScopes set.Strings,
  1283  ) error {
  1284  	exEndPoint := exRelation.AddEndpoint(description.EndpointArgs{
  1285  		ApplicationName: ep.ApplicationName,
  1286  		Name:            ep.Name,
  1287  		Role:            string(ep.Role),
  1288  		Interface:       ep.Interface,
  1289  		Optional:        ep.Optional,
  1290  		Limit:           ep.Limit,
  1291  		Scope:           string(ep.Scope),
  1292  	})
  1293  
  1294  	key := relationApplicationSettingsKey(relation.Id(), ep.ApplicationName)
  1295  	appSettingsDoc, found := e.modelSettings[key]
  1296  	if !found && !e.cfg.SkipSettings && !e.cfg.SkipRelationData {
  1297  		return errors.Errorf("missing application settings for %q application %q", relation, ep.ApplicationName)
  1298  	}
  1299  	delete(e.modelSettings, key)
  1300  	exEndPoint.SetApplicationSettings(appSettingsDoc.Settings)
  1301  
  1302  	// We expect a relationScope and settings for each of
  1303  	// the units of the specified application.
  1304  	// We need to check both local and remote applications
  1305  	// in case we are dealing with a CMR.
  1306  	if units, ok := e.units[ep.ApplicationName]; ok {
  1307  		for _, unit := range units {
  1308  			ru, err := relation.Unit(unit)
  1309  			if err != nil {
  1310  				return errors.Trace(err)
  1311  			}
  1312  
  1313  			if err := e.relationUnit(exEndPoint, ru, unit.Name(), relationScopes); err != nil {
  1314  				return errors.Annotatef(err, "processing relation unit in %s", relation)
  1315  			}
  1316  		}
  1317  	} else {
  1318  		remotes, err := relation.AllRemoteUnits(ep.ApplicationName)
  1319  		if err != nil {
  1320  			if errors.Is(err, errors.NotFound) {
  1321  				// If there are no local or remote units for this application,
  1322  				// then there are none in scope. We are done.
  1323  				return nil
  1324  			}
  1325  			return errors.Annotatef(err, "retrieving remote units for %s", relation)
  1326  		}
  1327  
  1328  		for _, ru := range remotes {
  1329  			if err := e.relationUnit(exEndPoint, ru, ru.unitName, relationScopes); err != nil {
  1330  				return errors.Annotatef(err, "processing relation unit in %s", relation)
  1331  			}
  1332  		}
  1333  	}
  1334  
  1335  	return nil
  1336  }
  1337  
  1338  func (e *exporter) relationUnit(
  1339  	exEndPoint description.Endpoint,
  1340  	ru *RelationUnit,
  1341  	unitName string,
  1342  	relationScopes set.Strings,
  1343  ) error {
  1344  	valid, err := ru.Valid()
  1345  	if err != nil {
  1346  		return errors.Trace(err)
  1347  	}
  1348  	if !valid {
  1349  		// It doesn't make sense for this application to have a
  1350  		// relations scope for this endpoint. For example the
  1351  		// situation where we have a subordinate charm related to
  1352  		// two different principals.
  1353  		return nil
  1354  	}
  1355  
  1356  	key := ru.key()
  1357  	if !e.cfg.SkipRelationData && !relationScopes.Contains(key) && !e.cfg.IgnoreIncompleteModel {
  1358  		return errors.Errorf("missing relation scope for %s", unitName)
  1359  	}
  1360  	settingsDoc, found := e.modelSettings[key]
  1361  	if !found && !e.cfg.SkipSettings && !e.cfg.SkipRelationData && !e.cfg.IgnoreIncompleteModel {
  1362  		return errors.Errorf("missing relation settings for %s", unitName)
  1363  	}
  1364  	delete(e.modelSettings, key)
  1365  	exEndPoint.SetUnitSettings(unitName, settingsDoc.Settings)
  1366  
  1367  	return nil
  1368  }
  1369  
  1370  func (e *exporter) remoteEntities() error {
  1371  	e.logger.Debugf("reading remote entities")
  1372  	migration := &ExportStateMigration{
  1373  		src: e.st,
  1374  		dst: e.model,
  1375  	}
  1376  	migration.Add(func() error {
  1377  		m := migrations.ExportRemoteEntities{}
  1378  		return m.Execute(remoteEntitiesShim{
  1379  			st: migration.src,
  1380  		}, migration.dst)
  1381  	})
  1382  	return migration.Run()
  1383  }
  1384  
  1385  // offerConnectionsShim provides a way to model our dependencies by providing
  1386  // a shim layer to manage the covariance of the state package to the migration
  1387  // package.
  1388  type offerConnectionsShim struct {
  1389  	st *State
  1390  }
  1391  
  1392  // AllOfferConnections returns all offer connections in the model.
  1393  // The offer connection shim converts a state.OfferConnection to a
  1394  // migrations.MigrationOfferConnection.
  1395  func (s offerConnectionsShim) AllOfferConnections() ([]migrations.MigrationOfferConnection, error) {
  1396  	conns, err := s.st.AllOfferConnections()
  1397  	if err != nil {
  1398  		return nil, errors.Trace(err)
  1399  	}
  1400  	result := make([]migrations.MigrationOfferConnection, len(conns))
  1401  	for k, v := range conns {
  1402  		result[k] = v
  1403  	}
  1404  	return result, nil
  1405  }
  1406  
  1407  func (e *exporter) offerConnections() error {
  1408  	if e.cfg.SkipOfferConnections {
  1409  		return nil
  1410  	}
  1411  
  1412  	e.logger.Debugf("reading offer connections")
  1413  	migration := &ExportStateMigration{
  1414  		src: e.st,
  1415  		dst: e.model,
  1416  	}
  1417  	migration.Add(func() error {
  1418  		m := migrations.ExportOfferConnections{}
  1419  		return m.Execute(offerConnectionsShim{st: migration.src}, migration.dst)
  1420  	})
  1421  	return migration.Run()
  1422  }
  1423  
  1424  // externalControllersShim is to handle the fact that go doesn't handle
  1425  // covariance and the tight abstraction around the new migration export work
  1426  // ensures that we handle our dependencies up front.
  1427  type externalControllerShim struct {
  1428  	st *State
  1429  }
  1430  
  1431  // externalControllerInfoShim is used to align to an interface with in the
  1432  // migrations package.
  1433  type externalControllerInfoShim struct {
  1434  	info externalControllerDoc
  1435  }
  1436  
  1437  // ID holds the controller ID from the external controller
  1438  func (e externalControllerInfoShim) ID() string {
  1439  	return e.info.Id
  1440  }
  1441  
  1442  // Alias holds an alias (human friendly) name for the controller.
  1443  func (e externalControllerInfoShim) Alias() string {
  1444  	return e.info.Alias
  1445  }
  1446  
  1447  // Addrs holds the host:port values for the external
  1448  // controller's API server.
  1449  func (e externalControllerInfoShim) Addrs() []string {
  1450  	return e.info.Addrs
  1451  }
  1452  
  1453  // CACert holds the certificate to validate the external
  1454  // controller's target API server's TLS certificate.
  1455  func (e externalControllerInfoShim) CACert() string {
  1456  	return e.info.CACert
  1457  }
  1458  
  1459  // Models holds model UUIDs hosted on this controller.
  1460  func (e externalControllerInfoShim) Models() []string {
  1461  	return e.info.Models
  1462  }
  1463  
  1464  func (s externalControllerShim) ControllerForModel(uuid string) (migrations.MigrationExternalController, error) {
  1465  	entity, err := s.st.ExternalControllerForModel(uuid)
  1466  	if err != nil {
  1467  		return nil, errors.Trace(err)
  1468  	}
  1469  	return externalControllerInfoShim{
  1470  		info: entity.doc,
  1471  	}, nil
  1472  }
  1473  
  1474  // AllRemoteApplications returns all remote applications in the model.
  1475  func (s externalControllerShim) AllRemoteApplications() ([]migrations.MigrationRemoteApplication, error) {
  1476  	remoteApps, err := s.st.AllRemoteApplications()
  1477  	if err != nil {
  1478  		return nil, errors.Trace(err)
  1479  	}
  1480  	result := make([]migrations.MigrationRemoteApplication, len(remoteApps))
  1481  	for k, v := range remoteApps {
  1482  		result[k] = remoteApplicationShim{RemoteApplication: v}
  1483  	}
  1484  	return result, nil
  1485  }
  1486  
  1487  func (e *exporter) externalControllers() error {
  1488  	if e.cfg.SkipExternalControllers {
  1489  		return nil
  1490  	}
  1491  	e.logger.Debugf("reading external controllers")
  1492  	migration := &ExportStateMigration{
  1493  		src: e.st,
  1494  		dst: e.model,
  1495  	}
  1496  	migration.Add(func() error {
  1497  		m := migrations.ExportExternalControllers{}
  1498  		return m.Execute(externalControllerShim{st: migration.src}, migration.dst)
  1499  	})
  1500  	return migration.Run()
  1501  }
  1502  
  1503  // remoteEntitiesShim is to handle the fact that go doesn't handle covariance
  1504  // and the tight abstraction around the new migration export work ensures that
  1505  // we handle our dependencies up front.
  1506  type remoteEntitiesShim struct {
  1507  	st *State
  1508  }
  1509  
  1510  // AllRemoteEntities returns all remote entities in the model.
  1511  func (s remoteEntitiesShim) AllRemoteEntities() ([]migrations.MigrationRemoteEntity, error) {
  1512  	entities, err := s.st.AllRemoteEntities()
  1513  	if err != nil {
  1514  		return nil, errors.Trace(err)
  1515  	}
  1516  	result := make([]migrations.MigrationRemoteEntity, len(entities))
  1517  	for k, v := range entities {
  1518  		result[k] = v
  1519  	}
  1520  	return result, nil
  1521  }
  1522  
  1523  func (e *exporter) relationNetworks() error {
  1524  	e.logger.Debugf("reading relation networks")
  1525  	migration := &ExportStateMigration{
  1526  		src: e.st,
  1527  		dst: e.model,
  1528  	}
  1529  	migration.Add(func() error {
  1530  		m := migrations.ExportRelationNetworks{}
  1531  		return m.Execute(relationNetworksShim{st: migration.src}, migration.dst)
  1532  	})
  1533  	return migration.Run()
  1534  }
  1535  
  1536  // relationNetworksShim is to handle the fact that go doesn't handle covariance
  1537  // and the tight abstraction around the new migration export work ensures that
  1538  // we handle our dependencies up front.
  1539  type relationNetworksShim struct {
  1540  	st *State
  1541  }
  1542  
  1543  func (s relationNetworksShim) AllRelationNetworks() ([]migrations.MigrationRelationNetworks, error) {
  1544  	entities, err := NewRelationNetworks(s.st).AllRelationNetworks()
  1545  	if err != nil {
  1546  		return nil, errors.Trace(err)
  1547  	}
  1548  	result := make([]migrations.MigrationRelationNetworks, len(entities))
  1549  	for k, v := range entities {
  1550  		result[k] = v
  1551  	}
  1552  	return result, nil
  1553  }
  1554  
  1555  func (e *exporter) spaces() error {
  1556  	spaces, err := e.st.AllSpaces()
  1557  	if err != nil {
  1558  		return errors.Trace(err)
  1559  	}
  1560  	e.logger.Debugf("read %d spaces", len(spaces))
  1561  
  1562  	for _, space := range spaces {
  1563  		// We do not export the alpha space because it is created by default
  1564  		// with the new model. This is OK, because it is immutable.
  1565  		// Any subnets added to the space will still be exported.
  1566  		if space.Id() == network.AlphaSpaceId {
  1567  			continue
  1568  		}
  1569  
  1570  		e.model.AddSpace(description.SpaceArgs{
  1571  			Id:         space.Id(),
  1572  			Name:       space.Name(),
  1573  			Public:     space.IsPublic(),
  1574  			ProviderID: string(space.ProviderId()),
  1575  		})
  1576  	}
  1577  	return nil
  1578  }
  1579  
  1580  func (e *exporter) linklayerdevices() error {
  1581  	if e.cfg.SkipLinkLayerDevices {
  1582  		return nil
  1583  	}
  1584  	linklayerdevices, err := e.st.AllLinkLayerDevices()
  1585  	if err != nil {
  1586  		return errors.Trace(err)
  1587  	}
  1588  	e.logger.Debugf("read %d ip devices", len(linklayerdevices))
  1589  	for _, device := range linklayerdevices {
  1590  		e.model.AddLinkLayerDevice(description.LinkLayerDeviceArgs{
  1591  			ProviderID:      string(device.ProviderID()),
  1592  			MachineID:       device.MachineID(),
  1593  			Name:            device.Name(),
  1594  			MTU:             device.MTU(),
  1595  			Type:            string(device.Type()),
  1596  			MACAddress:      device.MACAddress(),
  1597  			IsAutoStart:     device.IsAutoStart(),
  1598  			IsUp:            device.IsUp(),
  1599  			ParentName:      device.ParentName(),
  1600  			VirtualPortType: string(device.VirtualPortType()),
  1601  		})
  1602  	}
  1603  	return nil
  1604  }
  1605  
  1606  func (e *exporter) subnets() error {
  1607  	subnets, err := e.st.AllSubnets()
  1608  	if err != nil {
  1609  		return errors.Trace(err)
  1610  	}
  1611  	e.logger.Debugf("read %d subnets", len(subnets))
  1612  
  1613  	for _, subnet := range subnets {
  1614  		args := description.SubnetArgs{
  1615  			ID:                subnet.ID(),
  1616  			CIDR:              subnet.CIDR(),
  1617  			ProviderId:        string(subnet.ProviderId()),
  1618  			ProviderNetworkId: string(subnet.ProviderNetworkId()),
  1619  			VLANTag:           subnet.VLANTag(),
  1620  			SpaceID:           subnet.SpaceID(),
  1621  			AvailabilityZones: subnet.AvailabilityZones(),
  1622  			FanLocalUnderlay:  subnet.FanLocalUnderlay(),
  1623  			FanOverlay:        subnet.FanOverlay(),
  1624  			IsPublic:          subnet.IsPublic(),
  1625  		}
  1626  		e.model.AddSubnet(args)
  1627  	}
  1628  	return nil
  1629  }
  1630  
  1631  func (e *exporter) ipAddresses() error {
  1632  	if e.cfg.SkipIPAddresses {
  1633  		return nil
  1634  	}
  1635  	ipaddresses, err := e.st.AllIPAddresses()
  1636  	if err != nil {
  1637  		return errors.Trace(err)
  1638  	}
  1639  	e.logger.Debugf("read %d ip addresses", len(ipaddresses))
  1640  	for _, addr := range ipaddresses {
  1641  		e.model.AddIPAddress(description.IPAddressArgs{
  1642  			ProviderID:        string(addr.ProviderID()),
  1643  			DeviceName:        addr.DeviceName(),
  1644  			MachineID:         addr.MachineID(),
  1645  			SubnetCIDR:        addr.SubnetCIDR(),
  1646  			ConfigMethod:      string(addr.ConfigMethod()),
  1647  			Value:             addr.Value(),
  1648  			DNSServers:        addr.DNSServers(),
  1649  			DNSSearchDomains:  addr.DNSSearchDomains(),
  1650  			GatewayAddress:    addr.GatewayAddress(),
  1651  			ProviderNetworkID: addr.ProviderNetworkID().String(),
  1652  			ProviderSubnetID:  addr.ProviderSubnetID().String(),
  1653  			Origin:            string(addr.Origin()),
  1654  		})
  1655  	}
  1656  	return nil
  1657  }
  1658  
  1659  func (e *exporter) sshHostKeys() error {
  1660  	if e.cfg.SkipSSHHostKeys {
  1661  		return nil
  1662  	}
  1663  	machines, err := e.st.AllMachines()
  1664  	if err != nil {
  1665  		return errors.Trace(err)
  1666  	}
  1667  	for _, machine := range machines {
  1668  		keys, err := e.st.GetSSHHostKeys(machine.MachineTag())
  1669  		if errors.IsNotFound(err) {
  1670  			continue
  1671  		} else if err != nil {
  1672  			return errors.Trace(err)
  1673  		}
  1674  		if len(keys) == 0 {
  1675  			continue
  1676  		}
  1677  		e.model.AddSSHHostKey(description.SSHHostKeyArgs{
  1678  			MachineID: machine.Id(),
  1679  			Keys:      keys,
  1680  		})
  1681  	}
  1682  	return nil
  1683  }
  1684  
  1685  func (e *exporter) cloudimagemetadata() error {
  1686  	if e.cfg.SkipCloudImageMetadata {
  1687  		return nil
  1688  	}
  1689  	cloudimagemetadata, err := e.st.CloudImageMetadataStorage.AllCloudImageMetadata()
  1690  	if err != nil {
  1691  		return errors.Trace(err)
  1692  	}
  1693  	e.logger.Debugf("read %d cloudimagemetadata", len(cloudimagemetadata))
  1694  	for _, metadata := range cloudimagemetadata {
  1695  		e.model.AddCloudImageMetadata(description.CloudImageMetadataArgs{
  1696  			Stream:          metadata.Stream,
  1697  			Region:          metadata.Region,
  1698  			Version:         metadata.Version,
  1699  			Arch:            metadata.Arch,
  1700  			VirtType:        metadata.VirtType,
  1701  			RootStorageType: metadata.RootStorageType,
  1702  			RootStorageSize: metadata.RootStorageSize,
  1703  			DateCreated:     metadata.DateCreated,
  1704  			Source:          metadata.Source,
  1705  			Priority:        metadata.Priority,
  1706  			ImageId:         metadata.ImageId,
  1707  		})
  1708  	}
  1709  	return nil
  1710  }
  1711  
  1712  func (e *exporter) actions() error {
  1713  	if e.cfg.SkipActions {
  1714  		return nil
  1715  	}
  1716  
  1717  	m, err := e.st.Model()
  1718  	if err != nil {
  1719  		return errors.Trace(err)
  1720  	}
  1721  
  1722  	actions, err := m.AllActions()
  1723  	if err != nil {
  1724  		return errors.Trace(err)
  1725  	}
  1726  	e.logger.Debugf("read %d actions", len(actions))
  1727  	for _, a := range actions {
  1728  		results, message := a.Results()
  1729  		arg := description.ActionArgs{
  1730  			Receiver:       a.Receiver(),
  1731  			Name:           a.Name(),
  1732  			Operation:      a.(*action).doc.Operation,
  1733  			Parameters:     a.Parameters(),
  1734  			Enqueued:       a.Enqueued(),
  1735  			Started:        a.Started(),
  1736  			Completed:      a.Completed(),
  1737  			Status:         string(a.Status()),
  1738  			Results:        results,
  1739  			Message:        message,
  1740  			Id:             a.Id(),
  1741  			Parallel:       a.Parallel(),
  1742  			ExecutionGroup: a.ExecutionGroup(),
  1743  		}
  1744  		messages := a.Messages()
  1745  		arg.Messages = make([]description.ActionMessage, len(messages))
  1746  		for i, m := range messages {
  1747  			arg.Messages[i] = m
  1748  		}
  1749  		e.model.AddAction(arg)
  1750  	}
  1751  	return nil
  1752  }
  1753  
  1754  func (e *exporter) operations() error {
  1755  	if e.cfg.SkipActions {
  1756  		return nil
  1757  	}
  1758  
  1759  	m, err := e.st.Model()
  1760  	if err != nil {
  1761  		return errors.Trace(err)
  1762  	}
  1763  
  1764  	operations, err := m.AllOperations()
  1765  	if err != nil {
  1766  		return errors.Trace(err)
  1767  	}
  1768  	e.logger.Debugf("read %d operations", len(operations))
  1769  	for _, op := range operations {
  1770  		opDetails, ok := op.(*operation)
  1771  		if !ok {
  1772  			return errors.Errorf("operation must be of type operation")
  1773  		}
  1774  		arg := description.OperationArgs{
  1775  			Summary:           op.Summary(),
  1776  			Fail:              op.Fail(),
  1777  			Enqueued:          op.Enqueued(),
  1778  			Started:           op.Started(),
  1779  			Completed:         op.Completed(),
  1780  			Status:            string(op.Status()),
  1781  			CompleteTaskCount: opDetails.doc.CompleteTaskCount,
  1782  			SpawnedTaskCount:  opDetails.doc.SpawnedTaskCount,
  1783  			Id:                op.Id(),
  1784  		}
  1785  		e.model.AddOperation(arg)
  1786  	}
  1787  	return nil
  1788  }
  1789  
  1790  func (e *exporter) secretBackendID() (string, error) {
  1791  	mCfg, err := e.dbModel.Config()
  1792  	if err != nil {
  1793  		return "", errors.Trace(err)
  1794  	}
  1795  	backendName := mCfg.SecretBackend()
  1796  	if backendName == "" || backendName == secretsprovider.Auto || backendName == secretsprovider.Internal {
  1797  		return "", nil
  1798  	}
  1799  	store := NewSecretBackends(e.st)
  1800  	backend, err := store.GetSecretBackend(backendName)
  1801  	if err != nil {
  1802  		return "", errors.Trace(err)
  1803  	}
  1804  	return backend.ID, nil
  1805  }
  1806  
  1807  func (e *exporter) secrets() error {
  1808  	if e.cfg.SkipSecrets {
  1809  		return nil
  1810  	}
  1811  	store := NewSecrets(e.st)
  1812  
  1813  	allSecrets, err := store.ListSecrets(SecretsFilter{})
  1814  	if err != nil {
  1815  		return errors.Trace(err)
  1816  	}
  1817  	e.logger.Debugf("read %d secrets", len(allSecrets))
  1818  	allRevisions, err := store.allSecretRevisions()
  1819  	if err != nil {
  1820  		return errors.Trace(err)
  1821  	}
  1822  	revisionArgsByID := make(map[string][]description.SecretRevisionArgs)
  1823  	for _, rev := range allRevisions {
  1824  		id, _ := splitSecretRevision(e.st.localID(rev.DocID))
  1825  		revArg := description.SecretRevisionArgs{
  1826  			Number:        rev.Revision,
  1827  			Created:       rev.CreateTime,
  1828  			Updated:       rev.UpdateTime,
  1829  			ExpireTime:    rev.ExpireTime,
  1830  			Obsolete:      rev.Obsolete,
  1831  			PendingDelete: rev.PendingDelete,
  1832  		}
  1833  		if len(rev.Data) > 0 {
  1834  			revArg.Content = make(secrets.SecretData)
  1835  			for k, v := range rev.Data {
  1836  				revArg.Content[k] = fmt.Sprintf("%v", v)
  1837  			}
  1838  		}
  1839  		if rev.ValueRef != nil {
  1840  			revArg.ValueRef = &description.SecretValueRefArgs{
  1841  				BackendID:  rev.ValueRef.BackendID,
  1842  				RevisionID: rev.ValueRef.RevisionID,
  1843  			}
  1844  		}
  1845  		revisionArgsByID[id] = append(revisionArgsByID[id], revArg)
  1846  	}
  1847  	allPermissions, err := store.allSecretPermissions()
  1848  	if err != nil {
  1849  		return errors.Trace(err)
  1850  	}
  1851  	accessArgsByID := make(map[string]map[string]description.SecretAccessArgs)
  1852  	for _, perm := range allPermissions {
  1853  		id := strings.Split(e.st.localID(perm.DocID), "#")[0]
  1854  		accessArg := description.SecretAccessArgs{
  1855  			Scope: perm.Scope,
  1856  			Role:  perm.Role,
  1857  		}
  1858  		access, ok := accessArgsByID[id]
  1859  		if !ok {
  1860  			access = make(map[string]description.SecretAccessArgs)
  1861  			accessArgsByID[id] = access
  1862  		}
  1863  		access[perm.Subject] = accessArg
  1864  	}
  1865  	allConsumers, err := store.allLocalSecretConsumers()
  1866  	if err != nil {
  1867  		return errors.Trace(err)
  1868  	}
  1869  	consumersByID := make(map[string][]description.SecretConsumerArgs)
  1870  	for _, info := range allConsumers {
  1871  		consumer, err := names.ParseTag(info.ConsumerTag)
  1872  		if err != nil {
  1873  			return errors.Trace(err)
  1874  		}
  1875  		id := strings.Split(e.st.localID(info.DocID), "#")[0]
  1876  		consumerArg := description.SecretConsumerArgs{
  1877  			Consumer:        consumer,
  1878  			Label:           info.Label,
  1879  			CurrentRevision: info.CurrentRevision,
  1880  		}
  1881  		consumersByID[id] = append(consumersByID[id], consumerArg)
  1882  	}
  1883  
  1884  	allRemoteConsumers, err := store.allSecretRemoteConsumers()
  1885  	if err != nil {
  1886  		return errors.Trace(err)
  1887  	}
  1888  	remoteConsumersByID := make(map[string][]description.SecretRemoteConsumerArgs)
  1889  	for _, info := range allRemoteConsumers {
  1890  		consumer, err := names.ParseTag(info.ConsumerTag)
  1891  		if err != nil {
  1892  			return errors.Trace(err)
  1893  		}
  1894  		id := strings.Split(e.st.localID(info.DocID), "#")[0]
  1895  		remoteConsumerArg := description.SecretRemoteConsumerArgs{
  1896  			Consumer:        consumer,
  1897  			CurrentRevision: info.CurrentRevision,
  1898  		}
  1899  		remoteConsumersByID[id] = append(remoteConsumersByID[id], remoteConsumerArg)
  1900  	}
  1901  
  1902  	for _, md := range allSecrets {
  1903  		owner, err := names.ParseTag(md.OwnerTag)
  1904  		if err != nil {
  1905  			return errors.Trace(err)
  1906  		}
  1907  		arg := description.SecretArgs{
  1908  			ID:              md.URI.ID,
  1909  			Version:         md.Version,
  1910  			Description:     md.Description,
  1911  			Label:           md.Label,
  1912  			RotatePolicy:    md.RotatePolicy.String(),
  1913  			AutoPrune:       md.AutoPrune,
  1914  			Owner:           owner,
  1915  			Created:         md.CreateTime,
  1916  			Updated:         md.UpdateTime,
  1917  			NextRotateTime:  md.NextRotateTime,
  1918  			Revisions:       revisionArgsByID[md.URI.ID],
  1919  			ACL:             accessArgsByID[md.URI.ID],
  1920  			Consumers:       consumersByID[md.URI.ID],
  1921  			RemoteConsumers: remoteConsumersByID[md.URI.ID],
  1922  		}
  1923  		e.model.AddSecret(arg)
  1924  	}
  1925  	return nil
  1926  }
  1927  
  1928  func (e *exporter) remoteSecrets() error {
  1929  	if e.cfg.SkipSecrets {
  1930  		return nil
  1931  	}
  1932  	store := NewSecrets(e.st)
  1933  
  1934  	allConsumers, err := store.allRemoteSecretConsumers()
  1935  	if err != nil {
  1936  		return errors.Trace(err)
  1937  	}
  1938  	e.logger.Debugf("read %d remote secret consumers", len(allConsumers))
  1939  	for _, info := range allConsumers {
  1940  		consumer, err := names.ParseTag(info.ConsumerTag)
  1941  		if err != nil {
  1942  			return errors.Trace(err)
  1943  		}
  1944  		id := strings.Split(e.st.localID(info.DocID), "#")[0]
  1945  		uri, err := secrets.ParseURI(id)
  1946  		if err != nil {
  1947  			return errors.Trace(err)
  1948  		}
  1949  		arg := description.RemoteSecretArgs{
  1950  			ID:              uri.ID,
  1951  			SourceUUID:      uri.SourceUUID,
  1952  			Consumer:        consumer,
  1953  			Label:           info.Label,
  1954  			CurrentRevision: info.CurrentRevision,
  1955  			LatestRevision:  info.LatestRevision,
  1956  		}
  1957  		e.model.AddRemoteSecret(arg)
  1958  	}
  1959  	return nil
  1960  }
  1961  
  1962  func (e *exporter) readAllRelationScopes() (set.Strings, error) {
  1963  	relationScopes, closer := e.st.db().GetCollection(relationScopesC)
  1964  	defer closer()
  1965  
  1966  	var docs []relationScopeDoc
  1967  	err := relationScopes.Find(nil).All(&docs)
  1968  	if err != nil {
  1969  		return nil, errors.Annotate(err, "cannot get all relation scopes")
  1970  	}
  1971  	e.logger.Debugf("found %d relationScope docs", len(docs))
  1972  
  1973  	result := set.NewStrings()
  1974  	for _, doc := range docs {
  1975  		result.Add(doc.Key)
  1976  	}
  1977  	return result, nil
  1978  }
  1979  
  1980  func (e *exporter) readAllUnits() (map[string][]*Unit, error) {
  1981  	unitsCollection, closer := e.st.db().GetCollection(unitsC)
  1982  	defer closer()
  1983  
  1984  	var docs []unitDoc
  1985  	err := unitsCollection.Find(nil).Sort("name").All(&docs)
  1986  	if err != nil {
  1987  		return nil, errors.Annotate(err, "cannot get all units")
  1988  	}
  1989  	e.logger.Debugf("found %d unit docs", len(docs))
  1990  	result := make(map[string][]*Unit)
  1991  	for _, doc := range docs {
  1992  		units := result[doc.Application]
  1993  		result[doc.Application] = append(units, newUnit(e.st, e.dbModel.Type(), &doc))
  1994  	}
  1995  	return result, nil
  1996  }
  1997  
  1998  func (e *exporter) readAllEndpointBindings() (map[string]bindingsMap, error) {
  1999  	bindings, closer := e.st.db().GetCollection(endpointBindingsC)
  2000  	defer closer()
  2001  
  2002  	var docs []endpointBindingsDoc
  2003  	err := bindings.Find(nil).All(&docs)
  2004  	if err != nil {
  2005  		return nil, errors.Annotate(err, "cannot get all application endpoint bindings")
  2006  	}
  2007  	e.logger.Debugf("found %d application endpoint binding docs", len(docs))
  2008  	result := make(map[string]bindingsMap)
  2009  	for _, doc := range docs {
  2010  		result[e.st.localID(doc.DocID)] = doc.Bindings
  2011  	}
  2012  	return result, nil
  2013  }
  2014  
  2015  func (e *exporter) readAllMeterStatus() (map[string]*meterStatusDoc, error) {
  2016  	meterStatuses, closer := e.st.db().GetCollection(meterStatusC)
  2017  	defer closer()
  2018  
  2019  	var docs []meterStatusDoc
  2020  	err := meterStatuses.Find(nil).All(&docs)
  2021  	if err != nil {
  2022  		return nil, errors.Annotate(err, "cannot get all meter status docs")
  2023  	}
  2024  	e.logger.Debugf("found %d meter status docs", len(docs))
  2025  	result := make(map[string]*meterStatusDoc)
  2026  	for _, v := range docs {
  2027  		doc := v
  2028  		result[e.st.localID(doc.DocID)] = &doc
  2029  	}
  2030  	return result, nil
  2031  }
  2032  
  2033  func (e *exporter) readAllPodSpecs() (map[string]string, error) {
  2034  	specs, closer := e.st.db().GetCollection(podSpecsC)
  2035  	defer closer()
  2036  
  2037  	var docs []containerSpecDoc
  2038  	err := specs.Find(nil).All(&docs)
  2039  	if err != nil {
  2040  		return nil, errors.Annotate(err, "cannot get all pod spec docs")
  2041  	}
  2042  	e.logger.Debugf("found %d pod spec docs", len(docs))
  2043  	result := make(map[string]string)
  2044  	for _, doc := range docs {
  2045  		result[e.st.localID(doc.Id)] = doc.Spec
  2046  	}
  2047  	return result, nil
  2048  }
  2049  
  2050  func (e *exporter) readAllCloudServices() (map[string]*cloudServiceDoc, error) {
  2051  	cloudServices, closer := e.st.db().GetCollection(cloudServicesC)
  2052  	defer closer()
  2053  
  2054  	var docs []cloudServiceDoc
  2055  	err := cloudServices.Find(nil).All(&docs)
  2056  	if err != nil {
  2057  		return nil, errors.Annotate(err, "cannot get all cloud service docs")
  2058  	}
  2059  	e.logger.Debugf("found %d cloud service docs", len(docs))
  2060  	result := make(map[string]*cloudServiceDoc)
  2061  	for _, v := range docs {
  2062  		doc := v
  2063  		result[e.st.localID(doc.DocID)] = &doc
  2064  	}
  2065  	return result, nil
  2066  }
  2067  
  2068  func (e *exporter) cloudService(doc *cloudServiceDoc) *description.CloudServiceArgs {
  2069  	return &description.CloudServiceArgs{
  2070  		ProviderId: doc.ProviderId,
  2071  		Addresses:  e.newAddressArgsSlice(doc.Addresses),
  2072  	}
  2073  }
  2074  
  2075  func (e *exporter) readAllCloudContainers() (map[string]*cloudContainerDoc, error) {
  2076  	cloudContainers, closer := e.st.db().GetCollection(cloudContainersC)
  2077  	defer closer()
  2078  
  2079  	var docs []cloudContainerDoc
  2080  	err := cloudContainers.Find(nil).All(&docs)
  2081  	if err != nil {
  2082  		return nil, errors.Annotate(err, "cannot get all cloud container docs")
  2083  	}
  2084  	e.logger.Debugf("found %d cloud container docs", len(docs))
  2085  	result := make(map[string]*cloudContainerDoc)
  2086  	for _, v := range docs {
  2087  		doc := v
  2088  		result[e.st.localID(doc.Id)] = &doc
  2089  	}
  2090  	return result, nil
  2091  }
  2092  
  2093  func (e *exporter) cloudContainer(doc *cloudContainerDoc) *description.CloudContainerArgs {
  2094  	result := &description.CloudContainerArgs{
  2095  		ProviderId: doc.ProviderId,
  2096  		Ports:      doc.Ports,
  2097  	}
  2098  	if doc.Address != nil {
  2099  		result.Address = e.newAddressArgs(*doc.Address)
  2100  	}
  2101  	return result
  2102  }
  2103  
  2104  func (e *exporter) readLastConnectionTimes() (map[string]time.Time, error) {
  2105  	lastConnections, closer := e.st.db().GetCollection(modelUserLastConnectionC)
  2106  	defer closer()
  2107  
  2108  	var docs []modelUserLastConnectionDoc
  2109  	if err := lastConnections.Find(nil).All(&docs); err != nil {
  2110  		return nil, errors.Trace(err)
  2111  	}
  2112  
  2113  	result := make(map[string]time.Time)
  2114  	for _, doc := range docs {
  2115  		result[doc.UserName] = doc.LastConnection.UTC()
  2116  	}
  2117  	return result, nil
  2118  }
  2119  
  2120  func (e *exporter) readAllAnnotations() error {
  2121  	e.annotations = make(map[string]annotatorDoc)
  2122  	if e.cfg.SkipAnnotations {
  2123  		return nil
  2124  	}
  2125  
  2126  	annotations, closer := e.st.db().GetCollection(annotationsC)
  2127  	defer closer()
  2128  
  2129  	var docs []annotatorDoc
  2130  	if err := annotations.Find(nil).All(&docs); err != nil {
  2131  		return errors.Trace(err)
  2132  	}
  2133  	e.logger.Debugf("read %d annotations docs", len(docs))
  2134  
  2135  	for _, doc := range docs {
  2136  		e.annotations[doc.GlobalKey] = doc
  2137  	}
  2138  	return nil
  2139  }
  2140  
  2141  func (e *exporter) readAllConstraints() error {
  2142  	constraintsCollection, closer := e.st.db().GetCollection(constraintsC)
  2143  	defer closer()
  2144  
  2145  	// Since the constraintsDoc doesn't include any global key or _id
  2146  	// fields, we can't just deserialize the entire collection into a slice
  2147  	// of docs, so we get them all out with bson maps.
  2148  	var docs []bson.M
  2149  	err := constraintsCollection.Find(nil).All(&docs)
  2150  	if err != nil {
  2151  		return errors.Annotate(err, "failed to read constraints collection")
  2152  	}
  2153  
  2154  	e.logger.Debugf("read %d constraints docs", len(docs))
  2155  	e.constraints = make(map[string]bson.M)
  2156  	for _, doc := range docs {
  2157  		docId, ok := doc["_id"].(string)
  2158  		if !ok {
  2159  			return errors.Errorf("expected string, got %s (%T)", doc["_id"], doc["_id"])
  2160  		}
  2161  		id := e.st.localID(docId)
  2162  		e.constraints[id] = doc
  2163  		e.logger.Debugf("doc[%q] = %#v", id, doc)
  2164  	}
  2165  	return nil
  2166  }
  2167  
  2168  // getAnnotations doesn't really care if there are any there or not
  2169  // for the key, but if they were there, they are removed so we can
  2170  // check at the end of the export for anything we have forgotten.
  2171  func (e *exporter) getAnnotations(key string) map[string]string {
  2172  	result, found := e.annotations[key]
  2173  	if found {
  2174  		delete(e.annotations, key)
  2175  	}
  2176  	return result.Annotations
  2177  }
  2178  
  2179  func (e *exporter) getCharmOrigin(doc applicationDoc, defaultArch string) (description.CharmOriginArgs, error) {
  2180  	// Everything should be migrated, but in the case that it's not, handle
  2181  	// that case.
  2182  	origin := doc.CharmOrigin
  2183  
  2184  	// If the channel is empty, then we fall back to the Revision.
  2185  	// Set default revision to -1. This is because a revision of 0 is
  2186  	// a valid revision for local charms which we need to be able to
  2187  	// from. On import, in the -1 case we grab the revision by parsing
  2188  	// the charm url.
  2189  	revision := -1
  2190  	if rev := origin.Revision; rev != nil {
  2191  		revision = *rev
  2192  	}
  2193  
  2194  	var channel charm.Channel
  2195  	if origin.Channel != nil {
  2196  		channel = charm.MakePermissiveChannel(origin.Channel.Track, origin.Channel.Risk, origin.Channel.Branch)
  2197  	}
  2198  	// Platform is now mandatory moving forward, so we need to ensure that
  2199  	// the architecture is set in the platform if it's not set. This
  2200  	// shouldn't happen that often, but handles clients sending bad requests
  2201  	// when deploying.
  2202  	pArch := origin.Platform.Architecture
  2203  	if pArch == "" {
  2204  		e.logger.Debugf("using default architecture (%q) for doc[%q]", defaultArch, doc.DocID)
  2205  		pArch = defaultArch
  2206  	}
  2207  	platform := corecharm.Platform{
  2208  		Architecture: pArch,
  2209  		OS:           origin.Platform.OS,
  2210  		Channel:      origin.Platform.Channel,
  2211  	}
  2212  
  2213  	return description.CharmOriginArgs{
  2214  		Source:   origin.Source,
  2215  		ID:       origin.ID,
  2216  		Hash:     origin.Hash,
  2217  		Revision: revision,
  2218  		Channel:  channel.String(),
  2219  		Platform: platform.String(),
  2220  	}, nil
  2221  }
  2222  
  2223  func (e *exporter) readAllSettings() error {
  2224  	e.modelSettings = make(map[string]settingsDoc)
  2225  	if e.cfg.SkipSettings {
  2226  		return nil
  2227  	}
  2228  
  2229  	settings, closer := e.st.db().GetCollection(settingsC)
  2230  	defer closer()
  2231  
  2232  	var docs []settingsDoc
  2233  	if err := settings.Find(nil).All(&docs); err != nil {
  2234  		return errors.Trace(err)
  2235  	}
  2236  
  2237  	for _, doc := range docs {
  2238  		key := e.st.localID(doc.DocID)
  2239  		e.modelSettings[key] = doc
  2240  	}
  2241  	return nil
  2242  }
  2243  
  2244  func (e *exporter) readAllStatuses() error {
  2245  	statuses, closer := e.st.db().GetCollection(statusesC)
  2246  	defer closer()
  2247  
  2248  	var docs []bson.M
  2249  	err := statuses.Find(nil).All(&docs)
  2250  	if err != nil {
  2251  		return errors.Annotate(err, "failed to read status collection")
  2252  	}
  2253  
  2254  	e.logger.Debugf("read %d status documents", len(docs))
  2255  	e.status = make(map[string]bson.M)
  2256  	for _, doc := range docs {
  2257  		docId, ok := doc["_id"].(string)
  2258  		if !ok {
  2259  			return errors.Errorf("expected string, got %s (%T)", doc["_id"], doc["_id"])
  2260  		}
  2261  		id := e.st.localID(docId)
  2262  		e.status[id] = doc
  2263  	}
  2264  
  2265  	return nil
  2266  }
  2267  
  2268  func (e *exporter) readAllStatusHistory() error {
  2269  	statuses, closer := e.st.db().GetCollection(statusesHistoryC)
  2270  	defer closer()
  2271  
  2272  	count := 0
  2273  	e.statusHistory = make(map[string][]historicalStatusDoc)
  2274  	if e.cfg.SkipStatusHistory {
  2275  		return nil
  2276  	}
  2277  	var doc historicalStatusDoc
  2278  	// In tests, sorting by time can leave the results
  2279  	// underconstrained - include document id for deterministic
  2280  	// ordering in those cases.
  2281  	iter := statuses.Find(nil).Sort("-updated", "-_id").Iter()
  2282  	defer func() { _ = iter.Close() }()
  2283  	for iter.Next(&doc) {
  2284  		history := e.statusHistory[doc.GlobalKey]
  2285  		e.statusHistory[doc.GlobalKey] = append(history, doc)
  2286  		count++
  2287  	}
  2288  
  2289  	if err := iter.Close(); err != nil {
  2290  		return errors.Annotate(err, "failed to read status history collection")
  2291  	}
  2292  
  2293  	e.logger.Debugf("read %d status history documents", count)
  2294  
  2295  	return nil
  2296  }
  2297  
  2298  func (e *exporter) statusArgs(globalKey string) (description.StatusArgs, error) {
  2299  	result := description.StatusArgs{}
  2300  	statusDoc, found := e.status[globalKey]
  2301  	if !found {
  2302  		return result, errors.NotFoundf("status data for %s", globalKey)
  2303  	}
  2304  	delete(e.status, globalKey)
  2305  
  2306  	status, ok := statusDoc["status"].(string)
  2307  	if !ok {
  2308  		return result, errors.Errorf("expected string for status, got %T", statusDoc["status"])
  2309  	}
  2310  	info, ok := statusDoc["statusinfo"].(string)
  2311  	if !ok {
  2312  		return result, errors.Errorf("expected string for statusinfo, got %T", statusDoc["statusinfo"])
  2313  	}
  2314  	// data is an embedded map and comes out as a bson.M
  2315  	// A bson.M is map[string]interface{}, so we can type cast it.
  2316  	data, ok := statusDoc["statusdata"].(bson.M)
  2317  	if !ok {
  2318  		return result, errors.Errorf("expected map for data, got %T", statusDoc["statusdata"])
  2319  	}
  2320  	dataMap := map[string]interface{}(data)
  2321  	updated, ok := statusDoc["updated"].(int64)
  2322  	if !ok {
  2323  		return result, errors.Errorf("expected int64 for updated, got %T", statusDoc["updated"])
  2324  	}
  2325  
  2326  	result.Value = status
  2327  	result.Message = info
  2328  	result.Data = dataMap
  2329  	result.Updated = time.Unix(0, updated)
  2330  	return result, nil
  2331  }
  2332  
  2333  func (e *exporter) statusHistoryArgs(globalKey string) []description.StatusArgs {
  2334  	history := e.statusHistory[globalKey]
  2335  	e.logger.Tracef("found %d status history docs for %s", len(history), globalKey)
  2336  	if len(history) > maxStatusHistoryEntries {
  2337  		history = history[:maxStatusHistoryEntries]
  2338  	}
  2339  	result := make([]description.StatusArgs, len(history))
  2340  	for i, doc := range history {
  2341  		result[i] = description.StatusArgs{
  2342  			Value:   string(doc.Status),
  2343  			Message: doc.StatusInfo,
  2344  			Data:    doc.StatusData,
  2345  			Updated: time.Unix(0, doc.Updated),
  2346  		}
  2347  	}
  2348  	delete(e.statusHistory, globalKey)
  2349  	return result
  2350  }
  2351  
  2352  func (e *exporter) constraintsArgs(globalKey string) (description.ConstraintsArgs, error) {
  2353  	doc, found := e.constraints[globalKey]
  2354  	if !found {
  2355  		// No constraints for this key.
  2356  		e.logger.Tracef("no constraints found for key %q", globalKey)
  2357  		return description.ConstraintsArgs{}, nil
  2358  	}
  2359  	// We capture any type error using a closure to avoid having to return
  2360  	// multiple values from the optional functions. This does mean that we will
  2361  	// only report on the last one, but that is fine as there shouldn't be any.
  2362  	var optionalErr error
  2363  	optionalString := func(name string) string {
  2364  		switch value := doc[name].(type) {
  2365  		case nil:
  2366  		case string:
  2367  			return value
  2368  		default:
  2369  			optionalErr = errors.Errorf("expected string for %s, got %T", name, value)
  2370  		}
  2371  		return ""
  2372  	}
  2373  	optionalInt := func(name string) uint64 {
  2374  		switch value := doc[name].(type) {
  2375  		case nil:
  2376  		case uint64:
  2377  			return value
  2378  		case int64:
  2379  			return uint64(value)
  2380  		default:
  2381  			optionalErr = errors.Errorf("expected uint64 for %s, got %T", name, value)
  2382  		}
  2383  		return 0
  2384  	}
  2385  	optionalStringSlice := func(name string) []string {
  2386  		switch value := doc[name].(type) {
  2387  		case nil:
  2388  		case []string:
  2389  			return value
  2390  		case []interface{}:
  2391  			var result []string
  2392  			for _, val := range value {
  2393  				sval, ok := val.(string)
  2394  				if !ok {
  2395  					optionalErr = errors.Errorf("expected string slice for %s, got %T value", name, val)
  2396  					return nil
  2397  				}
  2398  				result = append(result, sval)
  2399  			}
  2400  			return result
  2401  		default:
  2402  			optionalErr = errors.Errorf("expected []string for %s, got %T", name, value)
  2403  		}
  2404  		return nil
  2405  	}
  2406  	optionalBool := func(name string) bool {
  2407  		switch value := doc[name].(type) {
  2408  		case nil:
  2409  		case bool:
  2410  			return value
  2411  		default:
  2412  			optionalErr = errors.Errorf("expected bool for %s, got %T", name, value)
  2413  		}
  2414  		return false
  2415  	}
  2416  	result := description.ConstraintsArgs{
  2417  		AllocatePublicIP: optionalBool("allocatepublicip"),
  2418  		Architecture:     optionalString("arch"),
  2419  		Container:        optionalString("container"),
  2420  		CpuCores:         optionalInt("cpucores"),
  2421  		CpuPower:         optionalInt("cpupower"),
  2422  		ImageID:          optionalString("imageid"),
  2423  		InstanceType:     optionalString("instancetype"),
  2424  		Memory:           optionalInt("mem"),
  2425  		RootDisk:         optionalInt("rootdisk"),
  2426  		RootDiskSource:   optionalString("rootdisksource"),
  2427  		Spaces:           optionalStringSlice("spaces"),
  2428  		Tags:             optionalStringSlice("tags"),
  2429  		VirtType:         optionalString("virttype"),
  2430  		Zones:            optionalStringSlice("zones"),
  2431  	}
  2432  	if optionalErr != nil {
  2433  		return description.ConstraintsArgs{}, errors.Trace(optionalErr)
  2434  	}
  2435  	return result, nil
  2436  }
  2437  
  2438  func (e *exporter) checkUnexportedValues() error {
  2439  	if e.cfg.IgnoreIncompleteModel {
  2440  		return nil
  2441  	}
  2442  
  2443  	var missing []string
  2444  
  2445  	// As annotations are saved into the model, they are removed from the
  2446  	// exporter's map. If there are any left at the end, we are missing
  2447  	// things.
  2448  	for key, doc := range e.annotations {
  2449  		missing = append(missing, fmt.Sprintf("unexported annotations for %s, %s", doc.Tag, key))
  2450  	}
  2451  
  2452  	for key := range e.modelSettings {
  2453  		missing = append(missing, fmt.Sprintf("unexported settings for %s", key))
  2454  	}
  2455  
  2456  	for key := range e.status {
  2457  		if !e.cfg.SkipInstanceData && !strings.HasSuffix(key, "#instance") {
  2458  			missing = append(missing, fmt.Sprintf("unexported status for %s", key))
  2459  		}
  2460  	}
  2461  
  2462  	for key := range e.statusHistory {
  2463  		if !e.cfg.SkipInstanceData && !(strings.HasSuffix(key, "#instance") || strings.HasSuffix(key, "#modification")) {
  2464  			missing = append(missing, fmt.Sprintf("unexported status history for %s", key))
  2465  		}
  2466  	}
  2467  
  2468  	if len(missing) > 0 {
  2469  		content := strings.Join(missing, "\n  ")
  2470  		return errors.Errorf("migration missed some docs:\n  %s", content)
  2471  	}
  2472  	return nil
  2473  }
  2474  
  2475  func (e *exporter) remoteApplications() error {
  2476  	e.logger.Debugf("read remote applications")
  2477  	migration := &ExportStateMigration{
  2478  		src:      e.st,
  2479  		dst:      e.model,
  2480  		exporter: e,
  2481  	}
  2482  	migration.Add(func() error {
  2483  		m := migrations.ExportRemoteApplications{}
  2484  		return m.Execute(remoteApplicationsShim{
  2485  			st:       migration.src,
  2486  			exporter: e,
  2487  		}, migration.dst)
  2488  	})
  2489  	return migration.Run()
  2490  }
  2491  
  2492  // remoteApplicationsShim is to handle the fact that go doesn't handle covariance
  2493  // and the tight abstraction around the new migration export work ensures that
  2494  // we handle our dependencies up front.
  2495  type remoteApplicationsShim struct {
  2496  	st       *State
  2497  	exporter *exporter
  2498  }
  2499  
  2500  // AllRemoteApplications returns all remote applications in the model.
  2501  func (s remoteApplicationsShim) AllRemoteApplications() ([]migrations.MigrationRemoteApplication, error) {
  2502  	remoteApps, err := s.st.AllRemoteApplications()
  2503  	if err != nil {
  2504  		return nil, errors.Trace(err)
  2505  	}
  2506  	result := make([]migrations.MigrationRemoteApplication, len(remoteApps))
  2507  	for k, v := range remoteApps {
  2508  		result[k] = remoteApplicationShim{RemoteApplication: v}
  2509  	}
  2510  	return result, nil
  2511  }
  2512  
  2513  func (s remoteApplicationsShim) StatusArgs(key string) (description.StatusArgs, error) {
  2514  	return s.exporter.statusArgs(key)
  2515  }
  2516  
  2517  type remoteApplicationShim struct {
  2518  	*RemoteApplication
  2519  }
  2520  
  2521  func (s remoteApplicationShim) Endpoints() ([]migrations.MigrationRemoteEndpoint, error) {
  2522  	endpoints, err := s.RemoteApplication.Endpoints()
  2523  	if err != nil {
  2524  		return nil, errors.Trace(err)
  2525  	}
  2526  	result := make([]migrations.MigrationRemoteEndpoint, len(endpoints))
  2527  	for k, v := range endpoints {
  2528  		result[k] = migrations.MigrationRemoteEndpoint{
  2529  			Name:      v.Name,
  2530  			Role:      v.Role,
  2531  			Interface: v.Interface,
  2532  		}
  2533  	}
  2534  	return result, nil
  2535  }
  2536  
  2537  func (s remoteApplicationShim) Spaces() []migrations.MigrationRemoteSpace {
  2538  	spaces := s.RemoteApplication.Spaces()
  2539  	result := make([]migrations.MigrationRemoteSpace, len(spaces))
  2540  	for k, v := range spaces {
  2541  		subnets := make([]migrations.MigrationRemoteSubnet, len(v.Subnets))
  2542  		for k, v := range v.Subnets {
  2543  			subnets[k] = migrations.MigrationRemoteSubnet{
  2544  				CIDR:              v.CIDR,
  2545  				ProviderId:        v.ProviderId,
  2546  				VLANTag:           v.VLANTag,
  2547  				AvailabilityZones: v.AvailabilityZones,
  2548  				ProviderSpaceId:   v.ProviderSpaceId,
  2549  				ProviderNetworkId: v.ProviderNetworkId,
  2550  			}
  2551  		}
  2552  		result[k] = migrations.MigrationRemoteSpace{
  2553  			CloudType:          v.CloudType,
  2554  			Name:               v.Name,
  2555  			ProviderId:         v.ProviderId,
  2556  			ProviderAttributes: v.ProviderAttributes,
  2557  			Subnets:            subnets,
  2558  		}
  2559  	}
  2560  	return result
  2561  }
  2562  
  2563  func (s remoteApplicationShim) GlobalKey() string {
  2564  	return s.RemoteApplication.globalKey()
  2565  }
  2566  
  2567  // Macaroon returns the encoded macaroon JSON.
  2568  func (s remoteApplicationShim) Macaroon() string {
  2569  	return s.RemoteApplication.doc.Macaroon
  2570  }
  2571  
  2572  func (e *exporter) storage() error {
  2573  	if err := e.volumes(); err != nil {
  2574  		return errors.Trace(err)
  2575  	}
  2576  	if err := e.filesystems(); err != nil {
  2577  		return errors.Trace(err)
  2578  	}
  2579  	if err := e.storageInstances(); err != nil {
  2580  		return errors.Trace(err)
  2581  	}
  2582  	if err := e.storagePools(); err != nil {
  2583  		return errors.Trace(err)
  2584  	}
  2585  	return nil
  2586  }
  2587  
  2588  func (e *exporter) volumes() error {
  2589  	coll, closer := e.st.db().GetCollection(volumesC)
  2590  	defer closer()
  2591  
  2592  	attachments, err := e.readVolumeAttachments()
  2593  	if err != nil {
  2594  		return errors.Trace(err)
  2595  	}
  2596  
  2597  	attachmentPlans, err := e.readVolumeAttachmentPlans()
  2598  	if err != nil {
  2599  		return errors.Trace(err)
  2600  	}
  2601  
  2602  	var doc volumeDoc
  2603  	iter := coll.Find(nil).Sort("_id").Iter()
  2604  	defer func() { _ = iter.Close() }()
  2605  	for iter.Next(&doc) {
  2606  		vol := &volume{e.st, doc}
  2607  		plan := attachmentPlans[doc.Name]
  2608  		if err := e.addVolume(vol, attachments[doc.Name], plan); err != nil {
  2609  			return errors.Trace(err)
  2610  		}
  2611  	}
  2612  	if err := iter.Close(); err != nil {
  2613  		return errors.Annotate(err, "failed to read volumes")
  2614  	}
  2615  	return nil
  2616  }
  2617  
  2618  func (e *exporter) addVolume(vol *volume, volAttachments []volumeAttachmentDoc, attachmentPlans []volumeAttachmentPlanDoc) error {
  2619  	args := description.VolumeArgs{
  2620  		Tag: vol.VolumeTag(),
  2621  	}
  2622  	if tag, err := vol.StorageInstance(); err == nil {
  2623  		// only returns an error when no storage tag.
  2624  		args.Storage = tag
  2625  	} else {
  2626  		if !errors.IsNotAssigned(err) {
  2627  			// This is an unexpected error.
  2628  			return errors.Trace(err)
  2629  		}
  2630  	}
  2631  	logger.Debugf("addVolume: %#v", vol.doc)
  2632  	if info, err := vol.Info(); err == nil {
  2633  		logger.Debugf("  info %#v", info)
  2634  		args.Provisioned = true
  2635  		args.Size = info.Size
  2636  		args.Pool = info.Pool
  2637  		args.HardwareID = info.HardwareId
  2638  		args.WWN = info.WWN
  2639  		args.VolumeID = info.VolumeId
  2640  		args.Persistent = info.Persistent
  2641  	} else {
  2642  		params, _ := vol.Params()
  2643  		logger.Debugf("  params %#v", params)
  2644  		args.Size = params.Size
  2645  		args.Pool = params.Pool
  2646  	}
  2647  
  2648  	globalKey := vol.globalKey()
  2649  	statusArgs, err := e.statusArgs(globalKey)
  2650  	if err != nil {
  2651  		return errors.Annotatef(err, "status for volume %s", vol.doc.Name)
  2652  	}
  2653  
  2654  	exVolume := e.model.AddVolume(args)
  2655  	exVolume.SetStatus(statusArgs)
  2656  	exVolume.SetStatusHistory(e.statusHistoryArgs(globalKey))
  2657  	if count := len(volAttachments); count != vol.doc.AttachmentCount {
  2658  		return errors.Errorf("volume attachment count mismatch, have %d, expected %d",
  2659  			count, vol.doc.AttachmentCount)
  2660  	}
  2661  	for _, doc := range volAttachments {
  2662  		va := volumeAttachment{doc}
  2663  		logger.Debugf("  attachment %#v", doc)
  2664  		args := description.VolumeAttachmentArgs{
  2665  			Host: va.Host(),
  2666  		}
  2667  		if info, err := va.Info(); err == nil {
  2668  			logger.Debugf("    info %#v", info)
  2669  			args.Provisioned = true
  2670  			args.ReadOnly = info.ReadOnly
  2671  			args.DeviceName = info.DeviceName
  2672  			args.DeviceLink = info.DeviceLink
  2673  			args.BusAddress = info.BusAddress
  2674  			if info.PlanInfo != nil {
  2675  				args.DeviceType = string(info.PlanInfo.DeviceType)
  2676  				args.DeviceAttributes = info.PlanInfo.DeviceAttributes
  2677  			}
  2678  		} else {
  2679  			params, _ := va.Params()
  2680  			logger.Debugf("    params %#v", params)
  2681  			args.ReadOnly = params.ReadOnly
  2682  		}
  2683  		exVolume.AddAttachment(args)
  2684  	}
  2685  
  2686  	for _, doc := range attachmentPlans {
  2687  		va := volumeAttachmentPlan{doc}
  2688  		logger.Debugf("  attachment plan %#v", doc)
  2689  		args := description.VolumeAttachmentPlanArgs{
  2690  			Machine: va.Machine(),
  2691  		}
  2692  		if info, err := va.PlanInfo(); err == nil {
  2693  			logger.Debugf("    plan info %#v", info)
  2694  			args.DeviceType = string(info.DeviceType)
  2695  			args.DeviceAttributes = info.DeviceAttributes
  2696  		} else if !errors.IsNotFound(err) {
  2697  			return errors.Trace(err)
  2698  		}
  2699  		if info, err := va.BlockDeviceInfo(); err == nil {
  2700  			logger.Debugf("    block device info %#v", info)
  2701  			args.DeviceName = info.DeviceName
  2702  			args.DeviceLinks = info.DeviceLinks
  2703  			args.Label = info.Label
  2704  			args.UUID = info.UUID
  2705  			args.HardwareId = info.HardwareId
  2706  			args.WWN = info.WWN
  2707  			args.BusAddress = info.BusAddress
  2708  			args.Size = info.Size
  2709  			args.FilesystemType = info.FilesystemType
  2710  			args.InUse = info.InUse
  2711  			args.MountPoint = info.MountPoint
  2712  		} else if !errors.IsNotFound(err) {
  2713  			return errors.Trace(err)
  2714  		}
  2715  		exVolume.AddAttachmentPlan(args)
  2716  	}
  2717  	return nil
  2718  }
  2719  
  2720  func (e *exporter) readVolumeAttachments() (map[string][]volumeAttachmentDoc, error) {
  2721  	coll, closer := e.st.db().GetCollection(volumeAttachmentsC)
  2722  	defer closer()
  2723  
  2724  	result := make(map[string][]volumeAttachmentDoc)
  2725  	var doc volumeAttachmentDoc
  2726  	var count int
  2727  	iter := coll.Find(nil).Iter()
  2728  	defer func() { _ = iter.Close() }()
  2729  	for iter.Next(&doc) {
  2730  		result[doc.Volume] = append(result[doc.Volume], doc)
  2731  		count++
  2732  	}
  2733  	if err := iter.Close(); err != nil {
  2734  		return nil, errors.Annotate(err, "failed to read volumes attachments")
  2735  	}
  2736  	e.logger.Debugf("read %d volume attachment documents", count)
  2737  	return result, nil
  2738  }
  2739  
  2740  func (e *exporter) readVolumeAttachmentPlans() (map[string][]volumeAttachmentPlanDoc, error) {
  2741  	coll, closer := e.st.db().GetCollection(volumeAttachmentPlanC)
  2742  	defer closer()
  2743  
  2744  	result := make(map[string][]volumeAttachmentPlanDoc)
  2745  	var doc volumeAttachmentPlanDoc
  2746  	var count int
  2747  	iter := coll.Find(nil).Iter()
  2748  	defer func() { _ = iter.Close() }()
  2749  	for iter.Next(&doc) {
  2750  		result[doc.Volume] = append(result[doc.Volume], doc)
  2751  		count++
  2752  	}
  2753  	if err := iter.Close(); err != nil {
  2754  		return nil, errors.Annotate(err, "failed to read volume attachment plans")
  2755  	}
  2756  	e.logger.Debugf("read %d volume attachment plan documents", count)
  2757  	return result, nil
  2758  }
  2759  
  2760  func (e *exporter) filesystems() error {
  2761  	coll, closer := e.st.db().GetCollection(filesystemsC)
  2762  	defer closer()
  2763  
  2764  	attachments, err := e.readFilesystemAttachments()
  2765  	if err != nil {
  2766  		return errors.Trace(err)
  2767  	}
  2768  	var doc filesystemDoc
  2769  	iter := coll.Find(nil).Sort("_id").Iter()
  2770  	defer func() { _ = iter.Close() }()
  2771  	for iter.Next(&doc) {
  2772  		fs := &filesystem{e.st, doc}
  2773  		if err := e.addFilesystem(fs, attachments[doc.FilesystemId]); err != nil {
  2774  			return errors.Trace(err)
  2775  		}
  2776  	}
  2777  	if err := iter.Close(); err != nil {
  2778  		return errors.Annotate(err, "failed to read filesystems")
  2779  	}
  2780  	return nil
  2781  }
  2782  
  2783  func (e *exporter) addFilesystem(fs *filesystem, fsAttachments []filesystemAttachmentDoc) error {
  2784  	// Here we don't care about the cases where the filesystem is not assigned to storage instances
  2785  	// nor no backing volues. In both those situations we have empty tags.
  2786  	storage, _ := fs.Storage()
  2787  	volume, _ := fs.Volume()
  2788  	args := description.FilesystemArgs{
  2789  		Tag:     fs.FilesystemTag(),
  2790  		Storage: storage,
  2791  		Volume:  volume,
  2792  	}
  2793  	logger.Debugf("addFilesystem: %#v", fs.doc)
  2794  	if info, err := fs.Info(); err == nil {
  2795  		logger.Debugf("  info %#v", info)
  2796  		args.Provisioned = true
  2797  		args.Size = info.Size
  2798  		args.Pool = info.Pool
  2799  		args.FilesystemID = info.FilesystemId
  2800  	} else {
  2801  		params, _ := fs.Params()
  2802  		logger.Debugf("  params %#v", params)
  2803  		args.Size = params.Size
  2804  		args.Pool = params.Pool
  2805  	}
  2806  
  2807  	globalKey := fs.globalKey()
  2808  	statusArgs, err := e.statusArgs(globalKey)
  2809  	if err != nil {
  2810  		return errors.Annotatef(err, "status for filesystem %s", fs.doc.FilesystemId)
  2811  	}
  2812  
  2813  	exFilesystem := e.model.AddFilesystem(args)
  2814  	exFilesystem.SetStatus(statusArgs)
  2815  	exFilesystem.SetStatusHistory(e.statusHistoryArgs(globalKey))
  2816  	if count := len(fsAttachments); count != fs.doc.AttachmentCount {
  2817  		return errors.Errorf("filesystem attachment count mismatch, have %d, expected %d",
  2818  			count, fs.doc.AttachmentCount)
  2819  	}
  2820  	for _, doc := range fsAttachments {
  2821  		va := filesystemAttachment{doc}
  2822  		logger.Debugf("  attachment %#v", doc)
  2823  		args := description.FilesystemAttachmentArgs{
  2824  			Host: va.Host(),
  2825  		}
  2826  		if info, err := va.Info(); err == nil {
  2827  			logger.Debugf("    info %#v", info)
  2828  			args.Provisioned = true
  2829  			args.ReadOnly = info.ReadOnly
  2830  			args.MountPoint = info.MountPoint
  2831  		} else {
  2832  			params, _ := va.Params()
  2833  			logger.Debugf("    params %#v", params)
  2834  			args.ReadOnly = params.ReadOnly
  2835  			args.MountPoint = params.Location
  2836  		}
  2837  		exFilesystem.AddAttachment(args)
  2838  	}
  2839  	return nil
  2840  }
  2841  
  2842  func (e *exporter) readFilesystemAttachments() (map[string][]filesystemAttachmentDoc, error) {
  2843  	coll, closer := e.st.db().GetCollection(filesystemAttachmentsC)
  2844  	defer closer()
  2845  
  2846  	result := make(map[string][]filesystemAttachmentDoc)
  2847  	var doc filesystemAttachmentDoc
  2848  	var count int
  2849  	iter := coll.Find(nil).Iter()
  2850  	defer func() { _ = iter.Close() }()
  2851  	for iter.Next(&doc) {
  2852  		result[doc.Filesystem] = append(result[doc.Filesystem], doc)
  2853  		count++
  2854  	}
  2855  	if err := iter.Close(); err != nil {
  2856  		return nil, errors.Annotate(err, "failed to read filesystem attachments")
  2857  	}
  2858  	e.logger.Debugf("read %d filesystem attachment documents", count)
  2859  	return result, nil
  2860  }
  2861  
  2862  func (e *exporter) storageInstances() error {
  2863  	sb, err := NewStorageBackend(e.st)
  2864  	if err != nil {
  2865  		return errors.Trace(err)
  2866  	}
  2867  	coll, closer := e.st.db().GetCollection(storageInstancesC)
  2868  	defer closer()
  2869  
  2870  	attachments, err := e.readStorageAttachments()
  2871  	if err != nil {
  2872  		return errors.Trace(err)
  2873  	}
  2874  	var doc storageInstanceDoc
  2875  	iter := coll.Find(nil).Sort("_id").Iter()
  2876  	defer func() { _ = iter.Close() }()
  2877  	for iter.Next(&doc) {
  2878  		instance := &storageInstance{sb, doc}
  2879  		if err := e.addStorage(instance, attachments[doc.Id]); err != nil {
  2880  			return errors.Trace(err)
  2881  		}
  2882  	}
  2883  	if err := iter.Close(); err != nil {
  2884  		return errors.Annotate(err, "failed to read storage instances")
  2885  	}
  2886  	return nil
  2887  }
  2888  
  2889  func (e *exporter) addStorage(instance *storageInstance, attachments []names.UnitTag) error {
  2890  	owner, ok := instance.Owner()
  2891  	if !ok {
  2892  		owner = nil
  2893  	}
  2894  	cons := description.StorageInstanceConstraints(instance.doc.Constraints)
  2895  	args := description.StorageArgs{
  2896  		Tag:         instance.StorageTag(),
  2897  		Kind:        instance.Kind().String(),
  2898  		Owner:       owner,
  2899  		Name:        instance.StorageName(),
  2900  		Attachments: attachments,
  2901  		Constraints: &cons,
  2902  	}
  2903  	e.model.AddStorage(args)
  2904  	return nil
  2905  }
  2906  
  2907  func (e *exporter) readStorageAttachments() (map[string][]names.UnitTag, error) {
  2908  	coll, closer := e.st.db().GetCollection(storageAttachmentsC)
  2909  	defer closer()
  2910  
  2911  	result := make(map[string][]names.UnitTag)
  2912  	var doc storageAttachmentDoc
  2913  	var count int
  2914  	iter := coll.Find(nil).Iter()
  2915  	defer func() { _ = iter.Close() }()
  2916  	for iter.Next(&doc) {
  2917  		unit := names.NewUnitTag(doc.Unit)
  2918  		result[doc.StorageInstance] = append(result[doc.StorageInstance], unit)
  2919  		count++
  2920  	}
  2921  	if err := iter.Close(); err != nil {
  2922  		return nil, errors.Annotate(err, "failed to read storage attachments")
  2923  	}
  2924  	e.logger.Debugf("read %d storage attachment documents", count)
  2925  	return result, nil
  2926  }
  2927  
  2928  func (e *exporter) storagePools() error {
  2929  	registry, err := e.st.storageProviderRegistry()
  2930  	if err != nil {
  2931  		return errors.Annotate(err, "getting provider registry")
  2932  	}
  2933  	pm := poolmanager.New(storagePoolSettingsManager{e: e}, registry)
  2934  	poolConfigs, err := pm.List()
  2935  	if err != nil {
  2936  		return errors.Annotate(err, "listing pools")
  2937  	}
  2938  	for _, cfg := range poolConfigs {
  2939  		e.model.AddStoragePool(description.StoragePoolArgs{
  2940  			Name:       cfg.Name(),
  2941  			Provider:   string(cfg.Provider()),
  2942  			Attributes: cfg.Attrs(),
  2943  		})
  2944  	}
  2945  	return nil
  2946  }
  2947  
  2948  func (e *exporter) groupOffersByApplicationName() (map[string][]*crossmodel.ApplicationOffer, error) {
  2949  	if e.cfg.SkipApplicationOffers {
  2950  		return nil, nil
  2951  	}
  2952  
  2953  	offerList, err := NewApplicationOffers(e.st).AllApplicationOffers()
  2954  	if err != nil {
  2955  		return nil, errors.Annotate(err, "listing offers")
  2956  	}
  2957  
  2958  	if len(offerList) == 0 {
  2959  		return nil, nil
  2960  	}
  2961  
  2962  	appMap := make(map[string][]*crossmodel.ApplicationOffer)
  2963  	for _, offer := range offerList {
  2964  		appMap[offer.ApplicationName] = append(appMap[offer.ApplicationName], offer)
  2965  	}
  2966  	return appMap, nil
  2967  }
  2968  
  2969  type storagePoolSettingsManager struct {
  2970  	poolmanager.SettingsManager
  2971  	e *exporter
  2972  }
  2973  
  2974  func (m storagePoolSettingsManager) ListSettings(keyPrefix string) (map[string]map[string]interface{}, error) {
  2975  	result := make(map[string]map[string]interface{})
  2976  	for key, doc := range m.e.modelSettings {
  2977  		if strings.HasPrefix(key, keyPrefix) {
  2978  			result[key] = doc.Settings
  2979  			delete(m.e.modelSettings, key)
  2980  		}
  2981  	}
  2982  	return result, nil
  2983  }