github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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  	"reflect"
     8  
     9  	"github.com/juju/utils/set"
    10  	gc "gopkg.in/check.v1"
    11  	"gopkg.in/juju/charm.v6-unstable"
    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  		constraintsC,
    23  		modelsC,
    24  		modelUsersC,
    25  		modelUserLastConnectionC,
    26  		settingsC,
    27  		sequenceC,
    28  		statusesC,
    29  		statusesHistoryC,
    30  
    31  		// machine
    32  		instanceDataC,
    33  		machinesC,
    34  		openedPortsC,
    35  
    36  		// service / unit
    37  		leasesC,
    38  		servicesC,
    39  		unitsC,
    40  		meterStatusC, // red / green status for metrics of units
    41  
    42  		// settings reference counts are only used for services
    43  		settingsrefsC,
    44  
    45  		// relation
    46  		relationsC,
    47  		relationScopesC,
    48  	)
    49  
    50  	ignoredCollections := set.NewStrings(
    51  		// Precheck ensures that there are no cleanup docs.
    52  		cleanupsC,
    53  		// We don't export the controller model at this stage.
    54  		controllersC,
    55  		// This is controller global, and related to the system state of the
    56  		// embedded GUI.
    57  		guimetadataC,
    58  		// This is controller global, not migrated.
    59  		guisettingsC,
    60  		// Users aren't migrated.
    61  		usersC,
    62  		userLastLoginC,
    63  		// userenvnameC is just to provide a unique key constraint.
    64  		usermodelnameC,
    65  		// Metrics aren't migrated.
    66  		metricsC,
    67  		// leaseC is deprecated in favour of leasesC.
    68  		leaseC,
    69  		// Backup and restore information is not migrated.
    70  		restoreInfoC,
    71  		// upgradeInfoC is used to coordinate upgrades and schema migrations,
    72  		// and aren't needed for model migrations.
    73  		upgradeInfoC,
    74  		// Not exported, but the tools will possibly need to be either bundled
    75  		// with the representation or sent separately.
    76  		toolsmetadataC,
    77  		// Bakery storage items are non-critical. We store root keys for
    78  		// temporary credentials in there; after migration you'll just have
    79  		// to log back in.
    80  		bakeryStorageItemsC,
    81  		// Transaction stuff.
    82  		"txns",
    83  		"txns.log",
    84  
    85  		// We don't import any of the migration collections.
    86  		migrationsC,
    87  		migrationsStatusC,
    88  		migrationsActiveC,
    89  
    90  		// The container ref document is primarily there to keep track
    91  		// of a particular machine's containers. The migration format
    92  		// uses object containment for this purpose.
    93  		containerRefsC,
    94  		// The min units collection is only used to trigger a watcher
    95  		// in order to have the service add or remove units if the minimum
    96  		// number of units is changed. The Service doc has all we need
    97  		// for migratino.
    98  		minUnitsC,
    99  		// This is a transitory collection of units that need to be assigned
   100  		// to machines.
   101  		assignUnitC,
   102  
   103  		// The model entity references collection will be repopulated
   104  		// after importing the model. It does not need to be migrated
   105  		// separately.
   106  		modelEntityRefsC,
   107  
   108  		// This has been deprecated in 2.0, and should not contain any data
   109  		// we actually care about migrating.
   110  		legacyipaddressesC,
   111  
   112  		// The SSH host keys for each machine will be reported as each
   113  		// machine agent starts up.
   114  		sshHostKeysC,
   115  	)
   116  
   117  	// THIS SET WILL BE REMOVED WHEN MIGRATIONS ARE COMPLETE
   118  	todoCollections := set.NewStrings(
   119  		// model
   120  		cloudimagemetadataC,
   121  
   122  		// machine
   123  		rebootC,
   124  
   125  		// service / unit
   126  		charmsC,
   127  		"payloads",
   128  		"resources",
   129  		endpointBindingsC,
   130  
   131  		// storage
   132  		blockDevicesC,
   133  		filesystemsC,
   134  		filesystemAttachmentsC,
   135  		storageInstancesC,
   136  		storageAttachmentsC,
   137  		storageConstraintsC,
   138  		volumesC,
   139  		volumeAttachmentsC,
   140  
   141  		// network
   142  		ipAddressesC,
   143  		linkLayerDevicesC,
   144  		linkLayerDevicesRefsC,
   145  		subnetsC,
   146  		spacesC,
   147  
   148  		// actions
   149  		actionsC,
   150  		actionNotificationsC,
   151  		actionresultsC,
   152  
   153  		// uncategorised
   154  		metricsManagerC, // should really be copied across
   155  	)
   156  
   157  	envCollections := set.NewStrings()
   158  	for name := range allCollections() {
   159  		envCollections.Add(name)
   160  	}
   161  
   162  	known := completedCollections.Union(ignoredCollections)
   163  
   164  	remainder := envCollections.Difference(known)
   165  	remainder = remainder.Difference(todoCollections)
   166  
   167  	// If this test fails, it means that a new collection has been added
   168  	// but migrations for it has not been done. This is a Bad Thing™.
   169  	c.Assert(remainder, gc.HasLen, 0)
   170  }
   171  
   172  func (s *MigrationSuite) TestModelDocFields(c *gc.C) {
   173  	fields := set.NewStrings(
   174  		// UUID and Name are constructed from the model config.
   175  		"UUID",
   176  		"Name",
   177  		// Life will always be alive, or we won't be migrating.
   178  		"Life",
   179  		// ServerUUID is recreated when the new model is created in the
   180  		// new controller (yay name changes).
   181  		"ServerUUID",
   182  
   183  		"MigrationMode",
   184  		"Owner",
   185  		"LatestAvailableTools",
   186  	)
   187  	s.AssertExportedFields(c, modelDoc{}, fields)
   188  }
   189  
   190  func (s *MigrationSuite) TestEnvUserDocFields(c *gc.C) {
   191  	fields := set.NewStrings(
   192  		// ID is the same as UserName (but lowercased)
   193  		"ID",
   194  		// ModelUUID shouldn't be exported, and is inherited
   195  		// from the model definition.
   196  		"ModelUUID",
   197  		// Tracked fields:
   198  		"UserName",
   199  		"DisplayName",
   200  		"CreatedBy",
   201  		"DateCreated",
   202  		"Access",
   203  	)
   204  	s.AssertExportedFields(c, modelUserDoc{}, fields)
   205  }
   206  
   207  func (s *MigrationSuite) TestEnvUserLastConnectionDocFields(c *gc.C) {
   208  	fields := set.NewStrings(
   209  		// ID is the same as UserName (but lowercased)
   210  		"ID",
   211  		// ModelUUID shouldn't be exported, and is inherited
   212  		// from the model definition.
   213  		"ModelUUID",
   214  		// UserName is captured in the migration.User.
   215  		"UserName",
   216  		"LastConnection",
   217  	)
   218  	s.AssertExportedFields(c, modelUserLastConnectionDoc{}, fields)
   219  }
   220  
   221  func (s *MigrationSuite) TestMachineDocFields(c *gc.C) {
   222  	fields := set.NewStrings(
   223  		// DocID is the env + machine id
   224  		"DocID",
   225  		// ID is the machine id
   226  		"Id",
   227  		// ModelUUID shouldn't be exported, and is inherited
   228  		// from the model definition.
   229  		"ModelUUID",
   230  		// Life is always alive, confirmed by export precheck.
   231  		"Life",
   232  
   233  		"Addresses",
   234  		"ContainerType",
   235  		"Jobs",
   236  		"MachineAddresses",
   237  		"Nonce",
   238  		"PasswordHash",
   239  		"Placement",
   240  		"PreferredPrivateAddress",
   241  		"PreferredPublicAddress",
   242  		"Principals",
   243  		"Series",
   244  		"SupportedContainers",
   245  		"SupportedContainersKnown",
   246  		"Tools",
   247  
   248  		// Ignored at this stage, could be an issue if mongo 3.0 isn't
   249  		// available.
   250  		"StopMongoUntilVersion",
   251  	)
   252  	todo := set.NewStrings(
   253  		"Volumes",
   254  		"NoVote",
   255  		"Clean",
   256  		"Filesystems",
   257  		"HasVote",
   258  	)
   259  	s.AssertExportedFields(c, machineDoc{}, fields.Union(todo))
   260  }
   261  
   262  func (s *MigrationSuite) TestInstanceDataFields(c *gc.C) {
   263  	fields := set.NewStrings(
   264  		// DocID is the env + machine id
   265  		"DocID",
   266  		"MachineId",
   267  		// ModelUUID shouldn't be exported, and is inherited
   268  		// from the model definition.
   269  		"ModelUUID",
   270  
   271  		"InstanceId",
   272  		"Status",
   273  		"Arch",
   274  		"Mem",
   275  		"RootDisk",
   276  		"CpuCores",
   277  		"CpuPower",
   278  		"Tags",
   279  		"AvailZone",
   280  	)
   281  	s.AssertExportedFields(c, instanceData{}, fields)
   282  }
   283  
   284  func (s *MigrationSuite) TestServiceDocFields(c *gc.C) {
   285  	ignored := set.NewStrings(
   286  		// DocID is the env + name
   287  		"DocID",
   288  		// ModelUUID shouldn't be exported, and is inherited
   289  		// from the model definition.
   290  		"ModelUUID",
   291  		// Always alive, not explicitly exported.
   292  		"Life",
   293  		// OwnerTag is deprecated and should be deleted.
   294  		"OwnerTag",
   295  		// TxnRevno is mgo internals and should not be migrated.
   296  		"TxnRevno",
   297  		// UnitCount is handled by the number of units for the exported service.
   298  		"UnitCount",
   299  		// RelationCount is handled by the number of times the service name
   300  		// appears in relation endpoints.
   301  		"RelationCount",
   302  	)
   303  	migrated := set.NewStrings(
   304  		"Name",
   305  		"Series",
   306  		"Subordinate",
   307  		"CharmURL",
   308  		"Channel",
   309  		"CharmModifiedVersion",
   310  		"ForceCharm",
   311  		"Exposed",
   312  		"MinUnits",
   313  		"MetricCredentials",
   314  	)
   315  	s.AssertExportedFields(c, serviceDoc{}, migrated.Union(ignored))
   316  }
   317  
   318  func (s *MigrationSuite) TestSettingsRefsDocFields(c *gc.C) {
   319  	fields := set.NewStrings(
   320  		// ModelUUID shouldn't be exported, and is inherited
   321  		// from the model definition.
   322  		"ModelUUID",
   323  
   324  		"RefCount",
   325  	)
   326  	s.AssertExportedFields(c, settingsRefsDoc{}, fields)
   327  }
   328  
   329  func (s *MigrationSuite) TestUnitDocFields(c *gc.C) {
   330  	fields := set.NewStrings(
   331  		// DocID itself isn't migrated
   332  		"DocID",
   333  		"Name",
   334  		// ModelUUID shouldn't be exported, and is inherited
   335  		// from the model definition.
   336  		"ModelUUID",
   337  		// Service is implicit in the migration structure through containment.
   338  		"Service",
   339  		// Series, CharmURL, and Channel also come from the service.
   340  		"Series",
   341  		"CharmURL",
   342  		"Principal",
   343  		"Subordinates",
   344  		"MachineId",
   345  		// Resolved is not migrated as we check that all is good before we start.
   346  		"Resolved",
   347  		"Tools",
   348  		// Life isn't migrated as we only migrate live things.
   349  		"Life",
   350  		// TxnRevno isn't migrated.
   351  		"TxnRevno",
   352  		"PasswordHash",
   353  		// Obsolete and not migrated.
   354  		"Ports",
   355  		"PublicAddress",
   356  		"PrivateAddress",
   357  	)
   358  	todo := set.NewStrings(
   359  		"StorageAttachmentCount",
   360  	)
   361  
   362  	s.AssertExportedFields(c, unitDoc{}, fields.Union(todo))
   363  }
   364  
   365  func (s *MigrationSuite) TestPortsDocFields(c *gc.C) {
   366  	fields := set.NewStrings(
   367  		// DocID itself isn't migrated
   368  		"DocID",
   369  		// ModelUUID shouldn't be exported, and is inherited
   370  		// from the model definition.
   371  		"ModelUUID",
   372  		// MachineID is implicit in the migration structure through containment.
   373  		"MachineID",
   374  		"SubnetID",
   375  		"Ports",
   376  		// TxnRevno isn't migrated.
   377  		"TxnRevno",
   378  	)
   379  	s.AssertExportedFields(c, portsDoc{}, fields)
   380  }
   381  
   382  func (s *MigrationSuite) TestMeterStatusDocFields(c *gc.C) {
   383  	fields := set.NewStrings(
   384  		// DocID itself isn't migrated
   385  		"DocID",
   386  		// ModelUUID shouldn't be exported, and is inherited
   387  		// from the model definition.
   388  		"ModelUUID",
   389  		"Code",
   390  		"Info",
   391  	)
   392  	s.AssertExportedFields(c, meterStatusDoc{}, fields)
   393  }
   394  
   395  func (s *MigrationSuite) TestRelationDocFields(c *gc.C) {
   396  	fields := set.NewStrings(
   397  		// DocID itself isn't migrated
   398  		"DocID",
   399  		// ModelUUID shouldn't be exported, and is inherited
   400  		// from the model definition.
   401  		"ModelUUID",
   402  		"Key",
   403  		"Id",
   404  		"Endpoints",
   405  		// Life isn't exported, only alive.
   406  		"Life",
   407  		// UnitCount isn't explicitly exported, but defined by the stored
   408  		// unit settings data for the relation endpoint.
   409  		"UnitCount",
   410  	)
   411  	s.AssertExportedFields(c, relationDoc{}, fields)
   412  	// We also need to check the Endpoint and nested charm.Relation field.
   413  	endpointFields := set.NewStrings("ServiceName", "Relation")
   414  	s.AssertExportedFields(c, Endpoint{}, endpointFields)
   415  	charmRelationFields := set.NewStrings(
   416  		"Name",
   417  		"Role",
   418  		"Interface",
   419  		"Optional",
   420  		"Limit",
   421  		"Scope",
   422  	)
   423  	s.AssertExportedFields(c, charm.Relation{}, charmRelationFields)
   424  }
   425  
   426  func (s *MigrationSuite) TestRelationScopeDocFields(c *gc.C) {
   427  	fields := set.NewStrings(
   428  		// DocID itself isn't migrated
   429  		"DocID",
   430  		// ModelUUID shouldn't be exported, and is inherited
   431  		// from the model definition.
   432  		"ModelUUID",
   433  		"Key",
   434  		// Departing isn't exported as we only deal with live, stable systems.
   435  		"Departing",
   436  	)
   437  	s.AssertExportedFields(c, relationScopeDoc{}, fields)
   438  }
   439  
   440  func (s *MigrationSuite) TestAnnatatorDocFields(c *gc.C) {
   441  	fields := set.NewStrings(
   442  		// ModelUUID shouldn't be exported, and is inherited
   443  		// from the model definition.
   444  		"ModelUUID",
   445  		"GlobalKey",
   446  		"Tag",
   447  		"Annotations",
   448  	)
   449  	s.AssertExportedFields(c, annotatorDoc{}, fields)
   450  }
   451  
   452  func (s *MigrationSuite) TestBlockDocFields(c *gc.C) {
   453  	ignored := set.NewStrings(
   454  		// The doc id is a sequence value that has no meaning.
   455  		// It really doesn't need to be a sequence.
   456  		"DocID",
   457  		// ModelUUID shouldn't be exported, and is inherited
   458  		// from the model definition.
   459  		"ModelUUID",
   460  		// Tag is just string representation of the model tag,
   461  		// which also contains the model-uuid.
   462  		"Tag",
   463  	)
   464  	migrated := set.NewStrings(
   465  		"Type",
   466  		"Message",
   467  	)
   468  	fields := migrated.Union(ignored)
   469  	s.AssertExportedFields(c, blockDoc{}, fields)
   470  }
   471  
   472  func (s *MigrationSuite) TestSequenceDocFields(c *gc.C) {
   473  	fields := set.NewStrings(
   474  		// ModelUUID shouldn't be exported, and is inherited
   475  		// from the model definition.
   476  		"ModelUUID",
   477  		"DocID",
   478  		"Name",
   479  		"Counter",
   480  	)
   481  	s.AssertExportedFields(c, sequenceDoc{}, fields)
   482  }
   483  
   484  func (s *MigrationSuite) TestConstraintsDocFields(c *gc.C) {
   485  	fields := set.NewStrings(
   486  		// ModelUUID shouldn't be exported, and is inherited
   487  		// from the model definition.
   488  		"ModelUUID",
   489  		"Arch",
   490  		"CpuCores",
   491  		"CpuPower",
   492  		"Mem",
   493  		"RootDisk",
   494  		"InstanceType",
   495  		"Container",
   496  		"Tags",
   497  		"Spaces",
   498  	)
   499  	s.AssertExportedFields(c, constraintsDoc{}, fields)
   500  }
   501  
   502  func (s *MigrationSuite) TestHistoricalStatusDocFields(c *gc.C) {
   503  	fields := set.NewStrings(
   504  		// ModelUUID shouldn't be exported, and is inherited
   505  		// from the model definition.
   506  		"ModelUUID",
   507  		"GlobalKey",
   508  		"Status",
   509  		"StatusInfo",
   510  		"StatusData",
   511  		"Updated",
   512  	)
   513  	s.AssertExportedFields(c, historicalStatusDoc{}, fields)
   514  }
   515  
   516  func (s *MigrationSuite) AssertExportedFields(c *gc.C, doc interface{}, fields set.Strings) {
   517  	expected := getExportedFields(doc)
   518  	unknown := expected.Difference(fields)
   519  	removed := fields.Difference(expected)
   520  	// If this test fails, it means that extra fields have been added to the
   521  	// doc without thinking about the migration implications.
   522  	c.Check(unknown, gc.HasLen, 0)
   523  	c.Assert(removed, gc.HasLen, 0)
   524  }
   525  
   526  func getExportedFields(arg interface{}) set.Strings {
   527  	t := reflect.TypeOf(arg)
   528  	result := set.NewStrings()
   529  
   530  	count := t.NumField()
   531  	for i := 0; i < count; i++ {
   532  		f := t.Field(i)
   533  		// empty PkgPath means exported field.
   534  		// see https://golang.org/pkg/reflect/#StructField
   535  		if f.PkgPath == "" {
   536  			result.Add(f.Name)
   537  		}
   538  	}
   539  
   540  	return result
   541  }