github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/migration_internal_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state
     5  
     6  import (
     7  	"github.com/juju/charm/v12"
     8  	"github.com/juju/collections/set"
     9  	gc "gopkg.in/check.v1"
    10  
    11  	"github.com/juju/juju/testing"
    12  )
    13  
    14  type MigrationSuite struct{}
    15  
    16  var _ = gc.Suite(&MigrationSuite{})
    17  
    18  func (s *MigrationSuite) TestKnownCollections(c *gc.C) {
    19  	completedCollections := set.NewStrings(
    20  		annotationsC,
    21  		blocksC,
    22  		cloudimagemetadataC,
    23  		constraintsC,
    24  		modelsC,
    25  		modelUsersC,
    26  		modelUserLastConnectionC,
    27  		permissionsC,
    28  		settingsC,
    29  		generationsC,
    30  		sequenceC,
    31  		sshHostKeysC,
    32  		statusesC,
    33  		statusesHistoryC,
    34  
    35  		// machine
    36  		instanceDataC,
    37  		machineUpgradeSeriesLocksC,
    38  		machinesC,
    39  		openedPortsC,
    40  
    41  		// application / unit
    42  		applicationsC,
    43  		unitsC,
    44  		meterStatusC, // red / green status for metrics of units
    45  		payloadsC,
    46  		resourcesC,
    47  
    48  		// relation
    49  		relationsC,
    50  		relationScopesC,
    51  
    52  		// networking
    53  		endpointBindingsC,
    54  		ipAddressesC,
    55  		spacesC,
    56  		linkLayerDevicesC,
    57  		subnetsC,
    58  
    59  		// storage
    60  		blockDevicesC,
    61  
    62  		// cloudimagemetadata
    63  		cloudimagemetadataC,
    64  
    65  		// actions
    66  		actionsC,
    67  		operationsC,
    68  
    69  		// storage
    70  		filesystemsC,
    71  		filesystemAttachmentsC,
    72  		storageAttachmentsC,
    73  		storageConstraintsC,
    74  		storageInstancesC,
    75  		volumesC,
    76  		volumeAttachmentsC,
    77  
    78  		// caas
    79  		podSpecsC,
    80  		cloudContainersC,
    81  		cloudServicesC,
    82  		deviceConstraintsC,
    83  
    84  		// crossmodelrelations
    85  		remoteApplicationsC,
    86  		applicationOffersC,
    87  		offerConnectionsC,
    88  		relationNetworksC,
    89  		remoteEntitiesC,
    90  		externalControllersC,
    91  
    92  		// secrets
    93  		secretMetadataC,
    94  		secretRevisionsC,
    95  		secretRotateC,
    96  		secretConsumersC,
    97  		secretRemoteConsumersC,
    98  		secretPermissionsC,
    99  	)
   100  
   101  	ignoredCollections := set.NewStrings(
   102  		// Precheck ensures that there are no cleanup docs or pending
   103  		// machine removals.
   104  		cleanupsC,
   105  		machineRemovalsC,
   106  		// The autocert cache is non-critical. After migration
   107  		// you'll just need to acquire new certificates.
   108  		autocertCacheC,
   109  		// We don't export the controller model at this stage.
   110  		controllersC,
   111  		controllerNodesC,
   112  		// Clouds aren't migrated. They must exist in the
   113  		// target controller already.
   114  		cloudsC,
   115  		// Cloud credentials aren't migrated. They must exist in the
   116  		// target controller already.
   117  		cloudCredentialsC,
   118  		// Users aren't migrated.
   119  		usersC,
   120  		userLastLoginC,
   121  		// Controller users contain extra data about users therefore
   122  		// are not migrated either.
   123  		controllerUsersC,
   124  		// userenvnameC is just to provide a unique key constraint.
   125  		usermodelnameC,
   126  		// Metrics aren't migrated.
   127  		metricsC,
   128  		// reference counts are implementation details that should be
   129  		// reconstructed on the other side.
   130  		refcountsC,
   131  		globalRefcountsC,
   132  		// upgradeInfoC is used to coordinate upgrades and schema migrations,
   133  		// and aren't needed for model migrations.
   134  		upgradeInfoC,
   135  		// Not exported, but the tools will possibly need to be either bundled
   136  		// with the representation or sent separately.
   137  		toolsmetadataC,
   138  		// Bakery storage items are non-critical. We store root keys for
   139  		// temporary credentials in there; after migration you'll just have
   140  		// to log back in.
   141  		bakeryStorageItemsC,
   142  		// Transaction stuff.
   143  		"txns",
   144  		"txns.log",
   145  		"sstxns.log",
   146  
   147  		// We don't import any of the migration collections.
   148  		migrationsC,
   149  		migrationsStatusC,
   150  		migrationsActiveC,
   151  		migrationsMinionSyncC,
   152  
   153  		// The container ref document is primarily there to keep track
   154  		// of a particular machine's containers. The migration format
   155  		// uses object containment for this purpose.
   156  		containerRefsC,
   157  		// The min units collection is only used to trigger a watcher
   158  		// in order to have the application add or remove units if the minimum
   159  		// number of units is changed. The Application doc has all we need
   160  		// for migratino.
   161  		minUnitsC,
   162  		// This is a transitory collection of units that need to be assigned
   163  		// to machines.
   164  		assignUnitC,
   165  
   166  		// The model entity references collection will be repopulated
   167  		// after importing the model. It does not need to be migrated
   168  		// separately.
   169  		modelEntityRefsC,
   170  
   171  		// This is marked as deprecated, and should probably be removed.
   172  		actionresultsC,
   173  
   174  		// These are recreated whilst migrating other network entities.
   175  		providerIDsC,
   176  
   177  		// Recreated whilst migrating actions.
   178  		actionNotificationsC,
   179  
   180  		// Global settings store controller specific configuration settings
   181  		// and are not to be migrated.
   182  		globalSettingsC,
   183  
   184  		// There is a precheck to ensure that there are no pending reboots
   185  		// for the model being migrated, and as such, there is no need to
   186  		// migrate that information.
   187  		rebootC,
   188  
   189  		// Charms are added into the migrated model during the binary transfer
   190  		// phase after the initial model migration.
   191  		charmsC,
   192  
   193  		// Metrics manager maintains controller specific state relating to
   194  		// the store and forward of charm metrics. Nothing to migrate here.
   195  		metricsManagerC,
   196  
   197  		// The global clock is not migrated; each controller has its own
   198  		// independent global clock.
   199  		globalClockC,
   200  
   201  		// Volume attachment plans are ignored if missing. A missing collection
   202  		// simply defaults to the old code path.
   203  		volumeAttachmentPlanC,
   204  
   205  		// Resources are transferred separately
   206  		"storedResources",
   207  
   208  		// Unit state entries will be automatically created when the
   209  		// operator framework code mutates the state for the charm
   210  		// running within a unit. This is a new feature that is not
   211  		// backwards compatible with older controllers.
   212  		unitStatesC,
   213  
   214  		// Secret backends are per controller.
   215  		secretBackendsC,
   216  		secretBackendsRotateC,
   217  	)
   218  
   219  	// THIS SET WILL BE REMOVED WHEN MIGRATIONS ARE COMPLETE
   220  	todoCollections := set.NewStrings(
   221  		dockerResourcesC,
   222  	)
   223  
   224  	modelCollections := set.NewStrings()
   225  	for name := range allCollections() {
   226  		modelCollections.Add(name)
   227  	}
   228  
   229  	known := completedCollections.Union(ignoredCollections)
   230  
   231  	remainder := modelCollections.Difference(known)
   232  	remainder = remainder.Difference(todoCollections)
   233  
   234  	// If this test fails, it means that a new collection has been added
   235  	// but migrations for it has not been done. This is a Bad Thing™.
   236  	// Beware, if your collection is something controller-related it might
   237  	// not need migration (such as Users or ControllerUsers) in that
   238  	// case they only need to be accounted for among the ignored collections.
   239  	c.Assert(remainder, gc.HasLen, 0)
   240  }
   241  
   242  func (s *MigrationSuite) TestModelDocFields(c *gc.C) {
   243  	fields := set.NewStrings(
   244  		// UUID and Name are constructed from the model config.
   245  		"UUID",
   246  		"Name",
   247  		// Life will always be alive, or we won't be migrating.
   248  		"Life",
   249  		// ForceDestroyed is only relevant for models that are being
   250  		// removed.
   251  		"ForceDestroyed",
   252  		// DestroyTimeout is only relevant for models that are being
   253  		// removed.
   254  		"DestroyTimeout",
   255  		// ControllerUUID is recreated when the new model is created
   256  		// in the new controller (yay name changes).
   257  		"ControllerUUID",
   258  
   259  		"Type",
   260  		"MigrationMode",
   261  		"Owner",
   262  		"Cloud",
   263  		"CloudRegion",
   264  		"CloudCredential",
   265  		"LatestAvailableTools",
   266  		"SLA",
   267  		"MeterStatus",
   268  		"EnvironVersion",
   269  		"PasswordHash",
   270  	)
   271  	s.AssertExportedFields(c, modelDoc{}, fields)
   272  }
   273  
   274  func (s *MigrationSuite) TestUserAccessDocFields(c *gc.C) {
   275  	fields := set.NewStrings(
   276  		// ID is the same as UserName (but lowercased)
   277  		"ID",
   278  		// ObjectUUID shouldn't be exported, and is inherited
   279  		// from the model definition.
   280  		"ObjectUUID",
   281  		// Tracked fields:
   282  		"UserName",
   283  		"DisplayName",
   284  		"CreatedBy",
   285  		"DateCreated",
   286  	)
   287  	s.AssertExportedFields(c, userAccessDoc{}, fields)
   288  }
   289  
   290  func (s *MigrationSuite) TestPermissionDocFields(c *gc.C) {
   291  	fields := set.NewStrings(
   292  		"ID",
   293  		"ObjectGlobalKey",
   294  		"SubjectGlobalKey",
   295  		"Access",
   296  	)
   297  	s.AssertExportedFields(c, permissionDoc{}, fields)
   298  }
   299  
   300  func (s *MigrationSuite) TestModelUserLastConnectionDocFields(c *gc.C) {
   301  	fields := set.NewStrings(
   302  		// ID is the same as UserName (but lowercased)
   303  		"ID",
   304  		// ModelUUID shouldn't be exported, and is inherited
   305  		// from the model definition.
   306  		"ModelUUID",
   307  		// UserName is captured in the migration.User.
   308  		"UserName",
   309  		"LastConnection",
   310  	)
   311  	s.AssertExportedFields(c, modelUserLastConnectionDoc{}, fields)
   312  }
   313  
   314  func (s *MigrationSuite) TestMachineDocFields(c *gc.C) {
   315  	ignored := set.NewStrings(
   316  		// DocID is the model + machine id
   317  		"DocID",
   318  		// ID is the machine id
   319  		"Id",
   320  		// ModelUUID shouldn't be exported, and is inherited
   321  		// from the model definition.
   322  		"ModelUUID",
   323  		// Life is always alive, confirmed by export precheck.
   324  		"Life",
   325  		// ForceDestroyed is only true for dying/dead machines.
   326  		"ForceDestroyed",
   327  		// Ignored; they get populated on demand when the agent restarts
   328  		"AgentStartedAt",
   329  		"Hostname",
   330  	)
   331  	migrated := set.NewStrings(
   332  		"Addresses",
   333  		"Base",
   334  		"ContainerType",
   335  		"Jobs",
   336  		"MachineAddresses",
   337  		"Nonce",
   338  		"PasswordHash",
   339  		"Clean",
   340  		"Volumes",
   341  		"Filesystems",
   342  		"Placement",
   343  		"PreferredPrivateAddress",
   344  		"PreferredPublicAddress",
   345  		"Principals",
   346  		"SupportedContainers",
   347  		"SupportedContainersKnown",
   348  		"Tools",
   349  	)
   350  	s.AssertExportedFields(c, machineDoc{}, migrated.Union(ignored))
   351  }
   352  
   353  func (s *MigrationSuite) TestInstanceDataFields(c *gc.C) {
   354  	ignored := set.NewStrings(
   355  		// KeepInstance is only set when a machine is
   356  		// dying/dead (to be removed).
   357  		"KeepInstance",
   358  	)
   359  	migrated := set.NewStrings(
   360  		// DocID is the model + machine id
   361  		"DocID",
   362  		"MachineId",
   363  		// ModelUUID shouldn't be exported, and is inherited
   364  		// from the model definition.
   365  		"ModelUUID",
   366  
   367  		"InstanceId",
   368  		"DisplayName",
   369  		"Arch",
   370  		"Mem",
   371  		"RootDisk",
   372  		"RootDiskSource",
   373  		"CpuCores",
   374  		"CpuPower",
   375  		"Tags",
   376  		"AvailZone",
   377  		"VirtType",
   378  		"CharmProfiles",
   379  	)
   380  	s.AssertExportedFields(c, instanceData{}, migrated.Union(ignored))
   381  }
   382  
   383  func (s *MigrationSuite) TestApplicationDocFields(c *gc.C) {
   384  	ignored := set.NewStrings(
   385  		// DocID is the model + name
   386  		"DocID",
   387  		// ModelUUID shouldn't be exported, and is inherited
   388  		// from the model definition.
   389  		"ModelUUID",
   390  		// Always alive, not explicitly exported.
   391  		"Life",
   392  		// TxnRevno is mgo internals and should not be migrated.
   393  		"TxnRevno",
   394  		// UnitCount is handled by the number of units for the exported application.
   395  		"UnitCount",
   396  		// RelationCount is handled by the number of times the application name
   397  		// appears in relation endpoints.
   398  		"RelationCount",
   399  	)
   400  	migrated := set.NewStrings(
   401  		"Name",
   402  		"Subordinate",
   403  		"CharmURL",
   404  		"CharmModifiedVersion",
   405  		"CharmOrigin",
   406  		"ForceCharm",
   407  		"Exposed",
   408  		"ExposedEndpoints",
   409  		"MinUnits",
   410  		"MetricCredentials",
   411  		"PasswordHash",
   412  		"Tools",
   413  		"DesiredScale",
   414  		"Placement",
   415  		"HasResources",
   416  		"ProvisioningState",
   417  	)
   418  	s.AssertExportedFields(c, applicationDoc{}, migrated.Union(ignored))
   419  }
   420  
   421  func (s *MigrationSuite) TestUnitDocFields(c *gc.C) {
   422  	ignored := set.NewStrings(
   423  		"ModelUUID",
   424  		"DocID",
   425  		"Life",
   426  		// Application is implicit in the migration structure through containment.
   427  		"Application",
   428  		// Resolved is not migrated as we check that all is good before we start.
   429  		"Resolved",
   430  		// Base and CharmURL also come from the application.
   431  		"Base",
   432  		"CharmURL",
   433  	)
   434  	migrated := set.NewStrings(
   435  		"Name",
   436  		"Principal",
   437  		"Subordinates",
   438  		"StorageAttachmentCount",
   439  		"MachineId",
   440  		"Tools",
   441  		"PasswordHash",
   442  	)
   443  	s.AssertExportedFields(c, unitDoc{}, migrated.Union(ignored))
   444  }
   445  
   446  func (s *MigrationSuite) TestMachinePortRangesDocFields(c *gc.C) {
   447  	fields := set.NewStrings(
   448  		// DocID itself isn't migrated
   449  		"DocID",
   450  		// ModelUUID shouldn't be exported, and is inherited
   451  		// from the model definition.
   452  		"ModelUUID",
   453  		// MachineID is implicit in the migration structure through containment.
   454  		"MachineID",
   455  		"UnitRanges",
   456  		// TxnRevno isn't migrated.
   457  		"TxnRevno",
   458  	)
   459  	s.AssertExportedFields(c, machinePortRangesDoc{}, fields)
   460  }
   461  
   462  func (s *MigrationSuite) TestMeterStatusDocFields(c *gc.C) {
   463  	fields := set.NewStrings(
   464  		// DocID itself isn't migrated
   465  		"DocID",
   466  		// ModelUUID shouldn't be exported, and is inherited
   467  		// from the model definition.
   468  		"ModelUUID",
   469  		"Code",
   470  		"Info",
   471  	)
   472  	s.AssertExportedFields(c, meterStatusDoc{}, fields)
   473  }
   474  
   475  func (s *MigrationSuite) TestRelationDocFields(c *gc.C) {
   476  	fields := set.NewStrings(
   477  		// DocID itself isn't migrated
   478  		"DocID",
   479  		// ModelUUID shouldn't be exported, and is inherited
   480  		// from the model definition.
   481  		"ModelUUID",
   482  		"Key",
   483  		"Id",
   484  		"Endpoints",
   485  		"Suspended",
   486  		"SuspendedReason",
   487  		// Life isn't exported, only alive.
   488  		"Life",
   489  		// UnitCount isn't explicitly exported, but defined by the stored
   490  		// unit settings data for the relation endpoint.
   491  		"UnitCount",
   492  	)
   493  	s.AssertExportedFields(c, relationDoc{}, fields)
   494  	// We also need to check the Endpoint and nested charm.Relation field.
   495  	endpointFields := set.NewStrings("ApplicationName", "Relation")
   496  	s.AssertExportedFields(c, Endpoint{}, endpointFields)
   497  	charmRelationFields := set.NewStrings(
   498  		"Name",
   499  		"Role",
   500  		"Interface",
   501  		"Optional",
   502  		"Limit",
   503  		"Scope",
   504  	)
   505  	s.AssertExportedFields(c, charm.Relation{}, charmRelationFields)
   506  }
   507  
   508  func (s *MigrationSuite) TestRelationScopeDocFields(c *gc.C) {
   509  	fields := set.NewStrings(
   510  		// DocID itself isn't migrated
   511  		"DocID",
   512  		// ModelUUID shouldn't be exported, and is inherited
   513  		// from the model definition.
   514  		"ModelUUID",
   515  		"Key",
   516  		// Departing isn't exported as we only deal with live, stable systems.
   517  		"Departing",
   518  	)
   519  	s.AssertExportedFields(c, relationScopeDoc{}, fields)
   520  }
   521  
   522  func (s *MigrationSuite) TestAnnotatorDocFields(c *gc.C) {
   523  	fields := set.NewStrings(
   524  		// ModelUUID shouldn't be exported, and is inherited
   525  		// from the model definition.
   526  		"ModelUUID",
   527  		"GlobalKey",
   528  		"Tag",
   529  		"Annotations",
   530  	)
   531  	s.AssertExportedFields(c, annotatorDoc{}, fields)
   532  }
   533  
   534  func (s *MigrationSuite) TestBlockDocFields(c *gc.C) {
   535  	ignored := set.NewStrings(
   536  		// The doc id is a sequence value that has no meaning.
   537  		// It really doesn't need to be a sequence.
   538  		"DocID",
   539  		// ModelUUID shouldn't be exported, and is inherited
   540  		// from the model definition.
   541  		"ModelUUID",
   542  		// Tag is just string representation of the model tag,
   543  		// which also contains the model-uuid.
   544  		"Tag",
   545  	)
   546  	migrated := set.NewStrings(
   547  		"Type",
   548  		"Message",
   549  	)
   550  	fields := migrated.Union(ignored)
   551  	s.AssertExportedFields(c, blockDoc{}, fields)
   552  }
   553  
   554  func (s *MigrationSuite) TestSequenceDocFields(c *gc.C) {
   555  	fields := set.NewStrings(
   556  		// ModelUUID shouldn't be exported, and is inherited
   557  		// from the model definition.
   558  		"ModelUUID",
   559  		"DocID",
   560  		"Name",
   561  		"Counter",
   562  	)
   563  	s.AssertExportedFields(c, sequenceDoc{}, fields)
   564  }
   565  
   566  func (s *MigrationSuite) TestConstraintsDocFields(c *gc.C) {
   567  	fields := set.NewStrings(
   568  		// ModelUUID shouldn't be exported, and is inherited
   569  		// from the model definition.
   570  		"ModelUUID",
   571  		"DocID",
   572  		"Arch",
   573  		"CpuCores",
   574  		"CpuPower",
   575  		"Mem",
   576  		"RootDisk",
   577  		"RootDiskSource",
   578  		"InstanceRole",
   579  		"InstanceType",
   580  		"Container",
   581  		"Tags",
   582  		"Spaces",
   583  		"VirtType",
   584  		"Zones",
   585  		"AllocatePublicIP",
   586  		"ImageID",
   587  	)
   588  	s.AssertExportedFields(c, constraintsDoc{}, fields)
   589  }
   590  
   591  func (s *MigrationSuite) TestHistoricalStatusDocFields(c *gc.C) {
   592  	fields := set.NewStrings(
   593  		// ModelUUID shouldn't be exported, and is inherited
   594  		// from the model definition.
   595  		"ModelUUID",
   596  		"GlobalKey",
   597  		"Status",
   598  		"StatusInfo",
   599  		"StatusData",
   600  		"Updated",
   601  	)
   602  	s.AssertExportedFields(c, historicalStatusDoc{}, fields)
   603  }
   604  
   605  func (s *MigrationSuite) TestSpaceDocFields(c *gc.C) {
   606  	ignored := set.NewStrings(
   607  		"DocId",
   608  		// Always alive, not explicitly exported.
   609  		"Life",
   610  	)
   611  	migrated := set.NewStrings(
   612  		"Id",
   613  		"Name",
   614  		"IsPublic",
   615  		"ProviderId",
   616  	)
   617  	s.AssertExportedFields(c, spaceDoc{}, migrated.Union(ignored))
   618  }
   619  
   620  func (s *MigrationSuite) TestBlockDeviceFields(c *gc.C) {
   621  	ignored := set.NewStrings(
   622  		"DocID",
   623  		"ModelUUID",
   624  		// We manage machine through containment.
   625  		"Machine",
   626  	)
   627  	migrated := set.NewStrings(
   628  		"BlockDevices",
   629  	)
   630  	s.AssertExportedFields(c, blockDevicesDoc{}, migrated.Union(ignored))
   631  	// The meat is in the type stored in "BlockDevices".
   632  	migrated = set.NewStrings(
   633  		"DeviceName",
   634  		"DeviceLinks",
   635  		"Label",
   636  		"UUID",
   637  		"HardwareId",
   638  		"WWN",
   639  		"BusAddress",
   640  		"Size",
   641  		"FilesystemType",
   642  		"InUse",
   643  		"MountPoint",
   644  		"SerialId",
   645  	)
   646  	s.AssertExportedFields(c, BlockDeviceInfo{}, migrated)
   647  }
   648  
   649  func (s *MigrationSuite) TestSubnetDocFields(c *gc.C) {
   650  	ignored := set.NewStrings(
   651  		// DocID is the model + name
   652  		"DocID",
   653  		// TxnRevno is mgo internals and should not be migrated.
   654  		"TxnRevno",
   655  		// ModelUUID shouldn't be exported, and is inherited
   656  		// from the model definition.
   657  		"ModelUUID",
   658  		// Always alive, not explicitly exported.
   659  		"Life",
   660  	)
   661  	migrated := set.NewStrings(
   662  		"CIDR",
   663  		"ID",
   664  		"VLANTag",
   665  		"SpaceID",
   666  		"ProviderId",
   667  		"AvailabilityZones",
   668  		"ProviderNetworkId",
   669  		"FanLocalUnderlay",
   670  		"FanOverlay",
   671  		"IsPublic",
   672  	)
   673  	s.AssertExportedFields(c, subnetDoc{}, migrated.Union(ignored))
   674  }
   675  
   676  func (s *MigrationSuite) TestIPAddressDocFields(c *gc.C) {
   677  	ignored := set.NewStrings(
   678  		"DocID",
   679  		"ModelUUID",
   680  	)
   681  	migrated := set.NewStrings(
   682  		"DeviceName",
   683  		"MachineID",
   684  		"DNSSearchDomains",
   685  		"GatewayAddress",
   686  		"IsDefaultGateway",
   687  		"ProviderID",
   688  		"ProviderNetworkID",
   689  		"ProviderSubnetID",
   690  		"DNSServers",
   691  		"SubnetCIDR",
   692  		"ConfigMethod",
   693  		"Value",
   694  		"Origin",
   695  		"IsShadow",
   696  		"IsSecondary",
   697  	)
   698  	s.AssertExportedFields(c, ipAddressDoc{}, migrated.Union(ignored))
   699  }
   700  
   701  func (s *MigrationSuite) TestLinkLayerDeviceDocFields(c *gc.C) {
   702  	ignored := set.NewStrings(
   703  		"ModelUUID",
   704  		"DocID",
   705  	)
   706  	migrated := set.NewStrings(
   707  		"MachineID",
   708  		"ProviderID",
   709  		"Name",
   710  		"MTU",
   711  		"Type",
   712  		"MACAddress",
   713  		"IsAutoStart",
   714  		"IsUp",
   715  		"ParentName",
   716  		"VirtualPortType",
   717  	)
   718  	s.AssertExportedFields(c, linkLayerDeviceDoc{}, migrated.Union(ignored))
   719  }
   720  
   721  func (s *MigrationSuite) TestSSHHostKeyDocFields(c *gc.C) {
   722  	ignored := set.NewStrings()
   723  	migrated := set.NewStrings(
   724  		"Keys",
   725  	)
   726  	s.AssertExportedFields(c, sshHostKeysDoc{}, migrated.Union(ignored))
   727  }
   728  
   729  func (s *MigrationSuite) TestActionDocFields(c *gc.C) {
   730  	ignored := set.NewStrings(
   731  		"ModelUUID",
   732  	)
   733  	migrated := set.NewStrings(
   734  		"DocId",
   735  		"Receiver",
   736  		"Name",
   737  		"Operation",
   738  		"Enqueued",
   739  		"Started",
   740  		"Completed",
   741  		"Parameters",
   742  		"Results",
   743  		"Message",
   744  		"Status",
   745  		"Logs",
   746  		"Parallel",
   747  		"ExecutionGroup",
   748  	)
   749  	s.AssertExportedFields(c, actionDoc{}, migrated.Union(ignored))
   750  }
   751  
   752  func (s *MigrationSuite) TestOperationDocFields(c *gc.C) {
   753  	ignored := set.NewStrings(
   754  		"ModelUUID",
   755  		"CompleteTaskCount",
   756  	)
   757  	migrated := set.NewStrings(
   758  		"DocId",
   759  		"Summary",
   760  		"Enqueued",
   761  		"Started",
   762  		"Completed",
   763  		"Status",
   764  		"Fail",
   765  		"SpawnedTaskCount",
   766  	)
   767  	s.AssertExportedFields(c, operationDoc{}, migrated.Union(ignored))
   768  }
   769  
   770  func (s *MigrationSuite) TestVolumeDocFields(c *gc.C) {
   771  	ignored := set.NewStrings(
   772  		"ModelUUID",
   773  		"DocID",
   774  		"Life",
   775  		"HostId",    // recreated from pool properties
   776  		"Releasing", // only when dying; can't migrate dying storage
   777  	)
   778  	migrated := set.NewStrings(
   779  		"Name",
   780  		"StorageId",
   781  		"AttachmentCount", // through count of attachment instances
   782  		"Info",
   783  		"Params",
   784  	)
   785  	s.AssertExportedFields(c, volumeDoc{}, migrated.Union(ignored))
   786  	// The info and params fields ar structs.
   787  	s.AssertExportedFields(c, VolumeInfo{}, set.NewStrings(
   788  		"HardwareId", "WWN", "Size", "Pool", "VolumeId", "Persistent"))
   789  	s.AssertExportedFields(c, VolumeParams{}, set.NewStrings(
   790  		"Size", "Pool"))
   791  }
   792  
   793  func (s *MigrationSuite) TestVolumeAttachmentDocFields(c *gc.C) {
   794  	ignored := set.NewStrings(
   795  		"ModelUUID",
   796  		"DocID",
   797  		"Life",
   798  	)
   799  	migrated := set.NewStrings(
   800  		"Volume",
   801  		"Host",
   802  		"Info",
   803  		"Params",
   804  	)
   805  	s.AssertExportedFields(c, volumeAttachmentDoc{}, migrated.Union(ignored))
   806  	// The info and params fields ar structs.
   807  	s.AssertExportedFields(c, VolumeAttachmentInfo{}, set.NewStrings(
   808  		"DeviceName", "DeviceLink", "BusAddress", "ReadOnly", "PlanInfo"))
   809  	s.AssertExportedFields(c, VolumeAttachmentParams{}, set.NewStrings(
   810  		"ReadOnly"))
   811  }
   812  
   813  func (s *MigrationSuite) TestFilesystemDocFields(c *gc.C) {
   814  	ignored := set.NewStrings(
   815  		"ModelUUID",
   816  		"DocID",
   817  		"Life",
   818  		"HostId",    // recreated from pool properties
   819  		"Releasing", // only when dying; can't migrate dying storage
   820  	)
   821  	migrated := set.NewStrings(
   822  		"FilesystemId",
   823  		"StorageId",
   824  		"VolumeId",
   825  		"AttachmentCount", // through count of attachment instances
   826  		"Info",
   827  		"Params",
   828  	)
   829  	s.AssertExportedFields(c, filesystemDoc{}, migrated.Union(ignored))
   830  	// The info and params fields ar structs.
   831  	s.AssertExportedFields(c, FilesystemInfo{}, set.NewStrings(
   832  		"Size", "Pool", "FilesystemId"))
   833  	s.AssertExportedFields(c, FilesystemParams{}, set.NewStrings(
   834  		"Size", "Pool"))
   835  }
   836  
   837  func (s *MigrationSuite) TestFilesystemAttachmentDocFields(c *gc.C) {
   838  	ignored := set.NewStrings(
   839  		"ModelUUID",
   840  		"DocID",
   841  		"Life",
   842  	)
   843  	migrated := set.NewStrings(
   844  		"Filesystem",
   845  		"Host",
   846  		"Info",
   847  		"Params",
   848  	)
   849  	s.AssertExportedFields(c, filesystemAttachmentDoc{}, migrated.Union(ignored))
   850  	// The info and params fields ar structs.
   851  	s.AssertExportedFields(c, FilesystemAttachmentInfo{}, set.NewStrings(
   852  		"MountPoint", "ReadOnly"))
   853  	s.AssertExportedFields(c, FilesystemAttachmentParams{}, set.NewStrings(
   854  		"Location", "ReadOnly"))
   855  }
   856  
   857  func (s *MigrationSuite) TestStorageInstanceDocFields(c *gc.C) {
   858  	ignored := set.NewStrings(
   859  		"ModelUUID",
   860  		"DocID",
   861  		"Life",
   862  		"Releasing", // only when dying; can't migrate dying storage
   863  	)
   864  	migrated := set.NewStrings(
   865  		"Id",
   866  		"Kind",
   867  		"Owner",
   868  		"StorageName",
   869  		"AttachmentCount", // through count of attachment instances
   870  		"Constraints",
   871  	)
   872  	s.AssertExportedFields(c, storageInstanceDoc{}, migrated.Union(ignored))
   873  }
   874  
   875  func (s *MigrationSuite) TestStorageAttachmentDocFields(c *gc.C) {
   876  	ignored := set.NewStrings(
   877  		"ModelUUID",
   878  		"DocID",
   879  		"Life",
   880  	)
   881  	migrated := set.NewStrings(
   882  		"Unit",
   883  		"StorageInstance",
   884  	)
   885  	s.AssertExportedFields(c, storageAttachmentDoc{}, migrated.Union(ignored))
   886  }
   887  
   888  func (s *MigrationSuite) TestStorageConstraintsDocFields(c *gc.C) {
   889  	ignored := set.NewStrings(
   890  		"ModelUUID",
   891  		"DocID",
   892  	)
   893  	migrated := set.NewStrings(
   894  		"Constraints",
   895  	)
   896  	s.AssertExportedFields(c, storageConstraintsDoc{}, migrated.Union(ignored))
   897  }
   898  
   899  func (s *MigrationSuite) TestPayloadDocFields(c *gc.C) {
   900  	definedThroughContainment := set.NewStrings(
   901  		"UnitID",
   902  		"MachineID",
   903  	)
   904  	migrated := set.NewStrings(
   905  		"Name",
   906  		"Type",
   907  		"RawID",
   908  		"State",
   909  		"Labels",
   910  	)
   911  	s.AssertExportedFields(c, payloadDoc{}, migrated.Union(definedThroughContainment))
   912  }
   913  
   914  func (s *MigrationSuite) TestEndpointBindingFields(c *gc.C) {
   915  	definedThroughContainment := set.NewStrings(
   916  		"DocID",
   917  	)
   918  	migrated := set.NewStrings(
   919  		"Bindings",
   920  	)
   921  	ignored := set.NewStrings(
   922  		"TxnRevno",
   923  	)
   924  	fields := definedThroughContainment.Union(migrated).Union(ignored)
   925  	s.AssertExportedFields(c, endpointBindingsDoc{}, fields)
   926  }
   927  
   928  func (s *MigrationSuite) AssertExportedFields(c *gc.C, doc interface{}, fields set.Strings) {
   929  	expected := testing.GetExportedFields(doc)
   930  	unknown := expected.Difference(fields)
   931  	removed := fields.Difference(expected)
   932  	// If this test fails, it means that extra fields have been added to the
   933  	// doc without thinking about the migration implications.
   934  	c.Check(unknown, gc.HasLen, 0)
   935  	c.Assert(removed, gc.HasLen, 0)
   936  }
   937  
   938  func (s *MigrationSuite) TestSecretMetadataDocFields(c *gc.C) {
   939  	ignored := set.NewStrings(
   940  		"DocID",
   941  
   942  		// These are not exported but instead
   943  		// calculated from the revisions.
   944  		"LatestRevision",
   945  		"LatestExpireTime",
   946  	)
   947  	migrated := set.NewStrings(
   948  		"Version",
   949  		"OwnerTag",
   950  		"Description",
   951  		"Label",
   952  		"RotatePolicy",
   953  		"AutoPrune",
   954  		"CreateTime",
   955  		"UpdateTime",
   956  	)
   957  	s.AssertExportedFields(c, secretMetadataDoc{}, migrated.Union(ignored))
   958  }
   959  
   960  func (s *MigrationSuite) TestSecretRevisionDocFields(c *gc.C) {
   961  	ignored := set.NewStrings(
   962  		"DocID",
   963  		"TxnRevno",
   964  	)
   965  	migrated := set.NewStrings(
   966  		"Revision",
   967  		"CreateTime",
   968  		"UpdateTime",
   969  		"ExpireTime",
   970  		"Obsolete",
   971  		"ValueRef",
   972  		"Data",
   973  		"OwnerTag",
   974  		"PendingDelete",
   975  	)
   976  	s.AssertExportedFields(c, secretRevisionDoc{}, migrated.Union(ignored))
   977  }
   978  
   979  func (s *MigrationSuite) TestSecretRotationDocFields(c *gc.C) {
   980  	ignored := set.NewStrings(
   981  		"DocID",
   982  		"TxnRevno",
   983  	)
   984  	migrated := set.NewStrings(
   985  		"NextRotateTime",
   986  		"OwnerTag",
   987  	)
   988  	s.AssertExportedFields(c, secretRotationDoc{}, migrated.Union(ignored))
   989  }
   990  
   991  func (s *MigrationSuite) TestSecretConsumerDocFields(c *gc.C) {
   992  	ignored := set.NewStrings(
   993  		"DocID",
   994  	)
   995  	migrated := set.NewStrings(
   996  		"ConsumerTag",
   997  		"Label",
   998  		"CurrentRevision",
   999  		"LatestRevision",
  1000  	)
  1001  	s.AssertExportedFields(c, secretConsumerDoc{}, migrated.Union(ignored))
  1002  }