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 }