github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/cloudimagemetadata/image_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package cloudimagemetadata_test 5 6 import ( 7 "regexp" 8 9 "github.com/juju/errors" 10 "github.com/juju/testing" 11 jc "github.com/juju/testing/checkers" 12 "github.com/juju/txn" 13 txntesting "github.com/juju/txn/testing" 14 gc "gopkg.in/check.v1" 15 "gopkg.in/mgo.v2" 16 17 "github.com/juju/juju/mongo" 18 "github.com/juju/juju/state/cloudimagemetadata" 19 coretesting "github.com/juju/juju/testing" 20 ) 21 22 type cloudImageMetadataSuite struct { 23 testing.IsolatedMgoSuite 24 25 access *TestMongo 26 storage cloudimagemetadata.Storage 27 } 28 29 var _ = gc.Suite(&cloudImageMetadataSuite{}) 30 31 const ( 32 collectionName = "test-collection" 33 ) 34 35 func (s *cloudImageMetadataSuite) SetUpTest(c *gc.C) { 36 s.IsolatedMgoSuite.SetUpTest(c) 37 38 db := s.MgoSuite.Session.DB("juju") 39 40 s.access = NewTestMongo(db) 41 s.storage = cloudimagemetadata.NewStorage(collectionName, s.access) 42 } 43 44 func (s *cloudImageMetadataSuite) TestSaveMetadata(c *gc.C) { 45 attrs1 := cloudimagemetadata.MetadataAttributes{ 46 Stream: "stream", 47 Region: "region-test", 48 Version: "14.04", 49 Series: "trusty", 50 Arch: "arch", 51 VirtType: "virtType-test", 52 RootStorageType: "rootStorageType-test", 53 Source: "test", 54 } 55 attrs2 := cloudimagemetadata.MetadataAttributes{ 56 Stream: "chalk", 57 Region: "nether", 58 Version: "12.04", 59 Series: "precise", 60 Arch: "amd64", 61 Source: "test", 62 } 63 added := []cloudimagemetadata.Metadata{ 64 {attrs1, 0, "1", 0}, 65 {attrs2, 0, "2", 0}, 66 } 67 s.assertRecordMetadata(c, added[0]) 68 s.assertRecordMetadata(c, added[1]) 69 s.assertMetadataRecorded(c, cloudimagemetadata.MetadataAttributes{}, added...) 70 } 71 72 func (s *cloudImageMetadataSuite) TestSaveMetadataWithDateCreated(c *gc.C) { 73 attrs := cloudimagemetadata.MetadataAttributes{ 74 Stream: "stream", 75 Region: "region-test", 76 Version: "14.04", 77 Series: "trusty", 78 Arch: "arch", 79 VirtType: "virtType-test", 80 RootStorageType: "rootStorageType-test", 81 Source: "test", 82 } 83 now := coretesting.NonZeroTime().UnixNano() 84 metadata := cloudimagemetadata.Metadata{attrs, 0, "1", now} 85 s.assertRecordMetadata(c, metadata) 86 s.assertMetadataRecorded(c, cloudimagemetadata.MetadataAttributes{}, metadata) 87 } 88 89 func (s *cloudImageMetadataSuite) TestFindMetadataNotFound(c *gc.C) { 90 s.assertNoMetadata(c) 91 92 // insert something... 93 attrs := cloudimagemetadata.MetadataAttributes{ 94 Stream: "stream", 95 Region: "region", 96 Version: "14.04", 97 Series: "trusty", 98 Arch: "arch", 99 VirtType: "virtType", 100 Source: "test", 101 RootStorageType: "rootStorageType"} 102 m := cloudimagemetadata.Metadata{attrs, 0, "1", 0} 103 s.assertRecordMetadata(c, m) 104 105 // ...but look for something else. 106 none, err := s.storage.FindMetadata(cloudimagemetadata.MetadataFilter{ 107 Stream: "something else", 108 }) 109 // Make sure that we are explicit that we could not find what we wanted. 110 c.Assert(err, jc.Satisfies, errors.IsNotFound) 111 c.Assert(err, gc.ErrorMatches, "matching cloud image metadata not found") 112 c.Assert(none, gc.HasLen, 0) 113 } 114 115 func buildAttributesFilter(attrs cloudimagemetadata.MetadataAttributes) cloudimagemetadata.MetadataFilter { 116 filter := cloudimagemetadata.MetadataFilter{ 117 Stream: attrs.Stream, 118 Region: attrs.Region, 119 VirtType: attrs.VirtType, 120 RootStorageType: attrs.RootStorageType} 121 if attrs.Series != "" { 122 filter.Series = []string{attrs.Series} 123 } 124 if attrs.Arch != "" { 125 filter.Arches = []string{attrs.Arch} 126 } 127 return filter 128 } 129 130 func (s *cloudImageMetadataSuite) TestFindMetadata(c *gc.C) { 131 attrs := cloudimagemetadata.MetadataAttributes{ 132 Stream: "stream", 133 Region: "region", 134 Version: "14.04", 135 Series: "trusty", 136 Arch: "arch", 137 VirtType: "virtType", 138 Source: "test", 139 RootStorageType: "rootStorageType"} 140 141 m := cloudimagemetadata.Metadata{attrs, 0, "1", 0} 142 143 _, err := s.storage.FindMetadata(buildAttributesFilter(attrs)) 144 c.Assert(err, jc.Satisfies, errors.IsNotFound) 145 146 s.assertRecordMetadata(c, m) 147 expected := []cloudimagemetadata.Metadata{m} 148 s.assertMetadataRecorded(c, attrs, expected...) 149 150 attrs.Stream = "another_stream" 151 m = cloudimagemetadata.Metadata{attrs, 0, "2", 0} 152 s.assertRecordMetadata(c, m) 153 154 expected = append(expected, m) 155 // Should find both 156 s.assertMetadataRecorded(c, cloudimagemetadata.MetadataAttributes{Region: "region"}, expected...) 157 } 158 159 func (s *cloudImageMetadataSuite) TestSaveMetadataUpdateSameAttrsAndImages(c *gc.C) { 160 attrs := cloudimagemetadata.MetadataAttributes{ 161 Stream: "stream", 162 Version: "14.04", 163 Series: "trusty", 164 Arch: "arch", 165 Source: "test", 166 Region: "wonder", 167 } 168 metadata0 := cloudimagemetadata.Metadata{attrs, 0, "1", 0} 169 metadata1 := cloudimagemetadata.Metadata{attrs, 0, "1", 0} 170 171 s.assertRecordMetadata(c, metadata0) 172 s.assertRecordMetadata(c, metadata1) 173 s.assertMetadataRecorded(c, attrs, metadata1) 174 } 175 176 func (s *cloudImageMetadataSuite) TestSaveMetadataUpdateSameAttrsDiffImages(c *gc.C) { 177 attrs := cloudimagemetadata.MetadataAttributes{ 178 Stream: "stream", 179 Version: "14.04", 180 Series: "trusty", 181 Arch: "arch", 182 Source: "test", 183 Region: "wonder", 184 } 185 metadata0 := cloudimagemetadata.Metadata{attrs, 0, "1", 0} 186 metadata1 := cloudimagemetadata.Metadata{attrs, 0, "12", 0} 187 188 s.assertRecordMetadata(c, metadata0) 189 s.assertMetadataRecorded(c, attrs, metadata0) 190 s.assertRecordMetadata(c, metadata1) 191 s.assertMetadataRecorded(c, attrs, metadata1) 192 s.assertMetadataRecorded(c, cloudimagemetadata.MetadataAttributes{}, metadata1) 193 } 194 195 func (s *cloudImageMetadataSuite) TestSaveDiffMetadataConcurrentlyAndOrderByDateCreated(c *gc.C) { 196 attrs := cloudimagemetadata.MetadataAttributes{ 197 Stream: "stream", 198 Version: "14.04", 199 Series: "trusty", 200 Arch: "arch", 201 Region: "wonder", 202 Source: "test", 203 } 204 metadata0 := cloudimagemetadata.Metadata{attrs, 0, "0", 0} 205 metadata1 := cloudimagemetadata.Metadata{attrs, 0, "1", 0} 206 metadata1.Stream = "scream" 207 208 s.assertConcurrentSave(c, 209 metadata0, // add this one 210 metadata1, // add this one 211 // last added should be first as order is by date created 212 metadata1, // verify it's in the list 213 metadata0, // verify it's in the list 214 ) 215 } 216 217 func (s *cloudImageMetadataSuite) TestSaveSameMetadataDiffImageConcurrently(c *gc.C) { 218 attrs := cloudimagemetadata.MetadataAttributes{ 219 Stream: "stream", 220 Version: "14.04", 221 Series: "trusty", 222 Arch: "arch", 223 Source: "test", 224 Region: "wonder", 225 } 226 metadata0 := cloudimagemetadata.Metadata{attrs, 0, "0", 0} 227 metadata1 := cloudimagemetadata.Metadata{attrs, 0, "1", 0} 228 229 s.assertConcurrentSave(c, 230 metadata0, // add this one 231 metadata1, // overwrite it with this one 232 metadata1, // verify only the last one is in the list 233 ) 234 } 235 236 func (s *cloudImageMetadataSuite) TestSaveSameMetadataSameImageConcurrently(c *gc.C) { 237 attrs := cloudimagemetadata.MetadataAttributes{ 238 Stream: "stream", 239 Version: "14.04", 240 Series: "trusty", 241 Arch: "arch", 242 Source: "test", 243 Region: "wonder", 244 } 245 metadata0 := cloudimagemetadata.Metadata{attrs, 0, "0", 0} 246 247 s.assertConcurrentSave(c, 248 metadata0, // add this one 249 metadata0, // add it again 250 metadata0, // varify only one is in the list 251 ) 252 } 253 254 func (s *cloudImageMetadataSuite) TestSaveSameMetadataSameImageDiffSourceConcurrently(c *gc.C) { 255 attrs := cloudimagemetadata.MetadataAttributes{ 256 Stream: "stream", 257 Version: "14.04", 258 Series: "trusty", 259 Arch: "arch", 260 Source: "public", 261 Region: "wonder", 262 } 263 metadata0 := cloudimagemetadata.Metadata{attrs, 0, "0", 0} 264 265 attrs.Source = "custom" 266 metadata1 := cloudimagemetadata.Metadata{attrs, 0, "0", 0} 267 268 s.assertConcurrentSave(c, 269 metadata0, 270 metadata1, 271 metadata0, 272 metadata1, 273 ) 274 } 275 276 func (s *cloudImageMetadataSuite) TestSaveMetadataNoVersionPassed(c *gc.C) { 277 attrs := cloudimagemetadata.MetadataAttributes{ 278 Stream: "stream", 279 Series: "trusty", 280 Arch: "arch", 281 Source: "test", 282 Region: "wonder", 283 } 284 metadata0 := cloudimagemetadata.Metadata{attrs, 0, "1", 0} 285 s.assertRecordMetadata(c, metadata0) 286 } 287 288 func (s *cloudImageMetadataSuite) TestSaveMetadataNoSeriesPassed(c *gc.C) { 289 attrs := cloudimagemetadata.MetadataAttributes{ 290 Stream: "stream", 291 Arch: "arch", 292 Source: "test", 293 Region: "wonder", 294 } 295 metadata0 := cloudimagemetadata.Metadata{attrs, 0, "1", 0} 296 err := s.storage.SaveMetadata([]cloudimagemetadata.Metadata{metadata0}) 297 c.Assert(err, gc.ErrorMatches, regexp.QuoteMeta(`missing series: metadata for image 1 not valid`)) 298 } 299 300 func (s *cloudImageMetadataSuite) TestSaveMetadataUnsupportedSeriesPassed(c *gc.C) { 301 attrs := cloudimagemetadata.MetadataAttributes{ 302 Stream: "stream", 303 Series: "blah", 304 Arch: "arch", 305 Source: "test", 306 } 307 metadata0 := cloudimagemetadata.Metadata{attrs, 0, "1", 0} 308 err := s.storage.SaveMetadata([]cloudimagemetadata.Metadata{metadata0}) 309 c.Assert(err, gc.ErrorMatches, regexp.QuoteMeta(`unknown version for series: "blah"`)) 310 } 311 312 func (s *cloudImageMetadataSuite) TestSaveMetadataNoStreamPassed(c *gc.C) { 313 attrs := cloudimagemetadata.MetadataAttributes{ 314 Arch: "arch", 315 Source: "test", 316 Series: "trusty", 317 Region: "wonder", 318 } 319 metadata0 := cloudimagemetadata.Metadata{attrs, 0, "1", 0} 320 err := s.storage.SaveMetadata([]cloudimagemetadata.Metadata{metadata0}) 321 c.Assert(err, gc.ErrorMatches, regexp.QuoteMeta(`missing stream: metadata for image 1 not valid`)) 322 } 323 324 func (s *cloudImageMetadataSuite) TestSaveMetadataNoSourcePassed(c *gc.C) { 325 attrs := cloudimagemetadata.MetadataAttributes{ 326 Stream: "stream", 327 Arch: "arch", 328 Series: "trusty", 329 Region: "wonder", 330 } 331 metadata0 := cloudimagemetadata.Metadata{attrs, 0, "1", 0} 332 err := s.storage.SaveMetadata([]cloudimagemetadata.Metadata{metadata0}) 333 c.Assert(err, gc.ErrorMatches, regexp.QuoteMeta(`missing source: metadata for image 1 not valid`)) 334 } 335 336 func (s *cloudImageMetadataSuite) TestSaveMetadataNoArchitecturePassed(c *gc.C) { 337 attrs := cloudimagemetadata.MetadataAttributes{ 338 Stream: "stream", 339 Source: "test", 340 Series: "trusty", 341 Region: "wonder", 342 } 343 metadata0 := cloudimagemetadata.Metadata{attrs, 0, "1", 0} 344 err := s.storage.SaveMetadata([]cloudimagemetadata.Metadata{metadata0}) 345 c.Assert(err, gc.ErrorMatches, regexp.QuoteMeta(`missing architecture: metadata for image 1 not valid`)) 346 } 347 348 func (s *cloudImageMetadataSuite) TestSaveMetadataNoRegionPassed(c *gc.C) { 349 attrs := cloudimagemetadata.MetadataAttributes{ 350 Stream: "stream", 351 Arch: "arch", 352 Source: "test", 353 Series: "trusty", 354 } 355 metadata0 := cloudimagemetadata.Metadata{attrs, 0, "1", 0} 356 err := s.storage.SaveMetadata([]cloudimagemetadata.Metadata{metadata0}) 357 c.Assert(err, gc.ErrorMatches, regexp.QuoteMeta(`missing region: metadata for image 1 not valid`)) 358 } 359 360 func (s *cloudImageMetadataSuite) assertConcurrentSave(c *gc.C, metadata0, metadata1 cloudimagemetadata.Metadata, expected ...cloudimagemetadata.Metadata) { 361 addMetadata := func() { 362 s.assertRecordMetadata(c, metadata0) 363 } 364 defer txntesting.SetBeforeHooks(c, s.access.runner, addMetadata).Check() 365 s.assertRecordMetadata(c, metadata1) 366 s.assertMetadataRecorded(c, cloudimagemetadata.MetadataAttributes{}, expected...) 367 } 368 369 func (s *cloudImageMetadataSuite) assertRecordMetadata(c *gc.C, m cloudimagemetadata.Metadata) { 370 err := s.storage.SaveMetadata([]cloudimagemetadata.Metadata{m}) 371 c.Assert(err, jc.ErrorIsNil) 372 } 373 374 func (s *cloudImageMetadataSuite) assertMetadataRecorded( 375 c *gc.C, 376 criteria cloudimagemetadata.MetadataAttributes, 377 expected ...cloudimagemetadata.Metadata, 378 ) { 379 metadata, err := s.storage.FindMetadata(buildAttributesFilter(criteria)) 380 c.Assert(err, jc.ErrorIsNil) 381 382 // Collate expected into a map 383 groups := make(map[string][]cloudimagemetadata.Metadata) 384 for _, expectedMetadata := range expected { 385 groups[expectedMetadata.Source] = append(groups[expectedMetadata.Source], expectedMetadata) 386 } 387 388 // Compare maps by key; order of slices does not matter 389 c.Assert(groups, gc.HasLen, len(metadata)) 390 for source, expectedMetadata := range groups { 391 actual := metadata[source] 392 if len(actual) == len(expectedMetadata) { 393 for i, image := range actual { 394 if expectedMetadata[i].DateCreated == 0 { 395 // Copy the creation date across as this will have been 396 // generated. 397 expectedMetadata[i].DateCreated = image.DateCreated 398 } 399 } 400 } 401 c.Assert(actual, jc.SameContents, expectedMetadata) 402 } 403 } 404 405 func (s *cloudImageMetadataSuite) TestSupportedArchitectures(c *gc.C) { 406 stream := "stream" 407 region := "region-test" 408 409 arch1 := "arch" 410 attrs := cloudimagemetadata.MetadataAttributes{ 411 Stream: stream, 412 Region: region, 413 Version: "14.04", 414 Series: "trusty", 415 Arch: arch1, 416 VirtType: "virtType-test", 417 Source: "test", 418 RootStorageType: "rootStorageType-test"} 419 420 added := cloudimagemetadata.Metadata{attrs, 0, "1", 0} 421 s.assertRecordMetadata(c, added) 422 s.assertMetadataRecorded(c, attrs, added) 423 424 addedNonUnique := cloudimagemetadata.Metadata{attrs, 0, "21", 0} 425 s.assertRecordMetadata(c, addedNonUnique) 426 s.assertMetadataRecorded(c, attrs, addedNonUnique) 427 428 arch2 := "anotherArch" 429 attrs.Arch = arch2 430 added2 := cloudimagemetadata.Metadata{attrs, 0, "21", 0} 431 s.assertRecordMetadata(c, added2) 432 s.assertMetadataRecorded(c, attrs, added2) 433 434 expected := []string{arch1, arch2} 435 uniqueArches, err := s.storage.SupportedArchitectures( 436 cloudimagemetadata.MetadataFilter{Stream: stream, Region: region}) 437 c.Assert(err, jc.ErrorIsNil) 438 c.Assert(uniqueArches, gc.DeepEquals, expected) 439 } 440 441 func (s *cloudImageMetadataSuite) TestSupportedArchitecturesUnmatchedStreams(c *gc.C) { 442 stream := "stream" 443 region := "region-test" 444 445 attrs := cloudimagemetadata.MetadataAttributes{ 446 Stream: "new-stream", 447 Region: region, 448 Version: "14.04", 449 Series: "trusty", 450 Arch: "arch", 451 VirtType: "virtType-test", 452 Source: "test", 453 RootStorageType: "rootStorageType-test"} 454 455 added := cloudimagemetadata.Metadata{attrs, 0, "1", 0} 456 s.assertRecordMetadata(c, added) 457 s.assertMetadataRecorded(c, attrs, added) 458 459 uniqueArches, err := s.storage.SupportedArchitectures( 460 cloudimagemetadata.MetadataFilter{Stream: stream, Region: region}) 461 c.Assert(err, jc.ErrorIsNil) 462 c.Assert(uniqueArches, gc.DeepEquals, []string{}) 463 } 464 465 func (s *cloudImageMetadataSuite) TestSupportedArchitecturesUnmatchedRegions(c *gc.C) { 466 stream := "stream" 467 region := "region-test" 468 469 attrs := cloudimagemetadata.MetadataAttributes{ 470 Stream: stream, 471 Region: "new-region", 472 Version: "14.04", 473 Series: "trusty", 474 Arch: "arch", 475 VirtType: "virtType-test", 476 Source: "test", 477 RootStorageType: "rootStorageType-test"} 478 479 added := cloudimagemetadata.Metadata{attrs, 0, "1", 0} 480 s.assertRecordMetadata(c, added) 481 s.assertMetadataRecorded(c, attrs, added) 482 483 uniqueArches, err := s.storage.SupportedArchitectures( 484 cloudimagemetadata.MetadataFilter{Stream: stream, Region: region}) 485 c.Assert(err, jc.ErrorIsNil) 486 c.Assert(uniqueArches, gc.DeepEquals, []string{}) 487 } 488 489 func (s *cloudImageMetadataSuite) TestSupportedArchitecturesUnmatchedStreamsAndRegions(c *gc.C) { 490 stream := "stream" 491 region := "region-test" 492 493 attrs := cloudimagemetadata.MetadataAttributes{ 494 Stream: "new-stream", 495 Region: "new-region", 496 Version: "14.04", 497 Series: "trusty", 498 Arch: "arch", 499 VirtType: "virtType-test", 500 Source: "test", 501 RootStorageType: "rootStorageType-test"} 502 503 added := cloudimagemetadata.Metadata{attrs, 0, "1", 0} 504 s.assertRecordMetadata(c, added) 505 s.assertMetadataRecorded(c, attrs, added) 506 507 uniqueArches, err := s.storage.SupportedArchitectures( 508 cloudimagemetadata.MetadataFilter{Stream: stream, Region: region}) 509 c.Assert(err, jc.ErrorIsNil) 510 c.Assert(uniqueArches, gc.DeepEquals, []string{}) 511 } 512 513 func (s *cloudImageMetadataSuite) TestDeleteMetadata(c *gc.C) { 514 imageId := "ok-to-delete" 515 s.addTestImageMetadata(c, imageId) 516 s.assertDeleteMetadata(c, imageId) 517 s.assertNoMetadata(c) 518 519 // calling delete on it again should be a no-op 520 s.assertDeleteMetadata(c, imageId) 521 // make sure log has "nothing to delete" message 522 c.Assert(c.GetTestLog(), jc.Contains, "no metadata for image ID ok-to-delete to delete") 523 } 524 525 func (s *cloudImageMetadataSuite) TestDeleteDiffMetadataConcurrently(c *gc.C) { 526 imageId := "ok-to-delete" 527 s.addTestImageMetadata(c, imageId) 528 529 diffImageId := "ok-to-delete-too" 530 s.addTestImageMetadata(c, diffImageId) 531 532 s.assertConcurrentDelete(c, imageId, diffImageId) 533 } 534 535 func (s *cloudImageMetadataSuite) TestDeleteSameMetadataConcurrently(c *gc.C) { 536 imageId := "ok-to-delete" 537 s.addTestImageMetadata(c, imageId) 538 539 s.assertConcurrentDelete(c, imageId, imageId) 540 } 541 542 func (s *cloudImageMetadataSuite) assertConcurrentDelete(c *gc.C, imageId0, imageId1 string) { 543 deleteMetadata := func() { 544 s.assertDeleteMetadata(c, imageId0) 545 } 546 defer txntesting.SetBeforeHooks(c, s.access.runner, deleteMetadata).Check() 547 s.assertDeleteMetadata(c, imageId1) 548 s.assertNoMetadata(c) 549 } 550 551 func (s *cloudImageMetadataSuite) addTestImageMetadata(c *gc.C, imageId string) { 552 attrs := cloudimagemetadata.MetadataAttributes{ 553 Stream: "stream", 554 Region: "region-test", 555 Version: "14.04", 556 Series: "trusty", 557 Arch: "arch", 558 VirtType: "virtType-test", 559 Source: "test", 560 RootStorageType: "rootStorageType-test"} 561 562 added := cloudimagemetadata.Metadata{attrs, 0, imageId, 0} 563 s.assertRecordMetadata(c, added) 564 s.assertMetadataRecorded(c, attrs, added) 565 } 566 567 func (s *cloudImageMetadataSuite) assertDeleteMetadata(c *gc.C, imageId string) { 568 err := s.storage.DeleteMetadata(imageId) 569 c.Assert(err, jc.ErrorIsNil) 570 } 571 572 func (s *cloudImageMetadataSuite) assertNoMetadata(c *gc.C) { 573 // No metadata should be in store. 574 // So when looking for all and none is found, err. 575 found, err := s.storage.FindMetadata(cloudimagemetadata.MetadataFilter{}) 576 c.Assert(err, jc.Satisfies, errors.IsNotFound) 577 c.Assert(err, gc.ErrorMatches, "matching cloud image metadata not found") 578 c.Assert(found, gc.HasLen, 0) 579 } 580 581 type TestMongo struct { 582 database *mgo.Database 583 runner txn.Runner 584 } 585 586 func NewTestMongo(database *mgo.Database) *TestMongo { 587 return &TestMongo{ 588 database: database, 589 runner: txn.NewRunner(txn.RunnerParams{ 590 Database: database, 591 }), 592 } 593 } 594 595 func (m *TestMongo) GetCollection(name string) (mongo.Collection, func()) { 596 return mongo.CollectionFromName(m.database, name) 597 } 598 599 func (m *TestMongo) RunTransaction(getTxn txn.TransactionSource) error { 600 return m.runner.Run(getTxn) 601 }