github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/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 ) 20 21 type cloudImageMetadataSuite struct { 22 testing.IsolatedMgoSuite 23 24 access *TestMongo 25 storage cloudimagemetadata.Storage 26 } 27 28 var _ = gc.Suite(&cloudImageMetadataSuite{}) 29 30 const ( 31 envName = "test-model" 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(envName, 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 } 54 attrs2 := cloudimagemetadata.MetadataAttributes{ 55 Stream: "chalk", 56 Region: "nether", 57 Version: "12.04", 58 Series: "precise", 59 Arch: "amd64", 60 } 61 added := []cloudimagemetadata.Metadata{ 62 {attrs1, 0, "1"}, 63 {attrs2, 0, "2"}, 64 } 65 s.assertRecordMetadata(c, added[0]) 66 s.assertRecordMetadata(c, added[1]) 67 s.assertMetadataRecorded(c, cloudimagemetadata.MetadataAttributes{}, added...) 68 } 69 70 func (s *cloudImageMetadataSuite) TestFindMetadataNotFound(c *gc.C) { 71 s.assertNoMetadata(c) 72 73 // insert something... 74 attrs := cloudimagemetadata.MetadataAttributes{ 75 Stream: "stream", 76 Region: "region", 77 Version: "14.04", 78 Series: "trusty", 79 Arch: "arch", 80 VirtType: "virtType", 81 RootStorageType: "rootStorageType"} 82 m := cloudimagemetadata.Metadata{attrs, 0, "1"} 83 s.assertRecordMetadata(c, m) 84 85 // ...but look for something else. 86 none, err := s.storage.FindMetadata(cloudimagemetadata.MetadataFilter{ 87 Stream: "something else", 88 }) 89 // Make sure that we are explicit that we could not find what we wanted. 90 c.Assert(err, jc.Satisfies, errors.IsNotFound) 91 c.Assert(err, gc.ErrorMatches, "matching cloud image metadata not found") 92 c.Assert(none, gc.HasLen, 0) 93 } 94 95 func buildAttributesFilter(attrs cloudimagemetadata.MetadataAttributes) cloudimagemetadata.MetadataFilter { 96 filter := cloudimagemetadata.MetadataFilter{ 97 Stream: attrs.Stream, 98 Region: attrs.Region, 99 VirtType: attrs.VirtType, 100 RootStorageType: attrs.RootStorageType} 101 if attrs.Series != "" { 102 filter.Series = []string{attrs.Series} 103 } 104 if attrs.Arch != "" { 105 filter.Arches = []string{attrs.Arch} 106 } 107 return filter 108 } 109 110 func (s *cloudImageMetadataSuite) TestFindMetadata(c *gc.C) { 111 attrs := cloudimagemetadata.MetadataAttributes{ 112 Stream: "stream", 113 Region: "region", 114 Version: "14.04", 115 Series: "trusty", 116 Arch: "arch", 117 VirtType: "virtType", 118 RootStorageType: "rootStorageType"} 119 120 m := cloudimagemetadata.Metadata{attrs, 0, "1"} 121 122 _, err := s.storage.FindMetadata(buildAttributesFilter(attrs)) 123 c.Assert(err, jc.Satisfies, errors.IsNotFound) 124 125 s.assertRecordMetadata(c, m) 126 expected := []cloudimagemetadata.Metadata{m} 127 s.assertMetadataRecorded(c, attrs, expected...) 128 129 attrs.Stream = "another_stream" 130 m = cloudimagemetadata.Metadata{attrs, 0, "2"} 131 s.assertRecordMetadata(c, m) 132 133 expected = append(expected, m) 134 // Should find both 135 s.assertMetadataRecorded(c, cloudimagemetadata.MetadataAttributes{Region: "region"}, expected...) 136 } 137 138 func (s *cloudImageMetadataSuite) TestSaveMetadataUpdateSameAttrsAndImages(c *gc.C) { 139 attrs := cloudimagemetadata.MetadataAttributes{ 140 Stream: "stream", 141 Version: "14.04", 142 Series: "trusty", 143 Arch: "arch", 144 } 145 metadata0 := cloudimagemetadata.Metadata{attrs, 0, "1"} 146 metadata1 := cloudimagemetadata.Metadata{attrs, 0, "1"} 147 148 s.assertRecordMetadata(c, metadata0) 149 s.assertRecordMetadata(c, metadata1) 150 s.assertMetadataRecorded(c, attrs, metadata1) 151 } 152 153 func (s *cloudImageMetadataSuite) TestSaveMetadataUpdateSameAttrsDiffImages(c *gc.C) { 154 attrs := cloudimagemetadata.MetadataAttributes{ 155 Stream: "stream", 156 Version: "14.04", 157 Series: "trusty", 158 Arch: "arch", 159 } 160 metadata0 := cloudimagemetadata.Metadata{attrs, 0, "1"} 161 metadata1 := cloudimagemetadata.Metadata{attrs, 0, "12"} 162 163 s.assertRecordMetadata(c, metadata0) 164 s.assertMetadataRecorded(c, attrs, metadata0) 165 s.assertRecordMetadata(c, metadata1) 166 s.assertMetadataRecorded(c, attrs, metadata1) 167 s.assertMetadataRecorded(c, cloudimagemetadata.MetadataAttributes{}, metadata1) 168 } 169 170 func (s *cloudImageMetadataSuite) TestSaveDiffMetadataConcurrentlyAndOrderByDateCreated(c *gc.C) { 171 attrs := cloudimagemetadata.MetadataAttributes{ 172 Stream: "stream", 173 Version: "14.04", 174 Series: "trusty", 175 Arch: "arch", 176 } 177 metadata0 := cloudimagemetadata.Metadata{attrs, 0, "0"} 178 metadata1 := cloudimagemetadata.Metadata{attrs, 0, "1"} 179 metadata1.Stream = "scream" 180 181 s.assertConcurrentSave(c, 182 metadata0, // add this one 183 metadata1, // add this one 184 // last added should be first as order is by date created 185 metadata1, // verify it's in the list 186 metadata0, // verify it's in the list 187 ) 188 } 189 190 func (s *cloudImageMetadataSuite) TestSaveSameMetadataDiffImageConcurrently(c *gc.C) { 191 attrs := cloudimagemetadata.MetadataAttributes{ 192 Stream: "stream", 193 Version: "14.04", 194 Series: "trusty", 195 Arch: "arch", 196 } 197 metadata0 := cloudimagemetadata.Metadata{attrs, 0, "0"} 198 metadata1 := cloudimagemetadata.Metadata{attrs, 0, "1"} 199 200 s.assertConcurrentSave(c, 201 metadata0, // add this one 202 metadata1, // overwrite it with this one 203 metadata1, // verify only the last one is in the list 204 ) 205 } 206 207 func (s *cloudImageMetadataSuite) TestSaveSameMetadataSameImageConcurrently(c *gc.C) { 208 attrs := cloudimagemetadata.MetadataAttributes{ 209 Stream: "stream", 210 Version: "14.04", 211 Series: "trusty", 212 Arch: "arch", 213 } 214 metadata0 := cloudimagemetadata.Metadata{attrs, 0, "0"} 215 216 s.assertConcurrentSave(c, 217 metadata0, // add this one 218 metadata0, // add it again 219 metadata0, // varify only one is in the list 220 ) 221 } 222 223 func (s *cloudImageMetadataSuite) TestSaveSameMetadataSameImageDiffSourceConcurrently(c *gc.C) { 224 attrs := cloudimagemetadata.MetadataAttributes{ 225 Stream: "stream", 226 Version: "14.04", 227 Series: "trusty", 228 Arch: "arch", 229 Source: "public", 230 } 231 metadata0 := cloudimagemetadata.Metadata{attrs, 0, "0"} 232 233 attrs.Source = "custom" 234 metadata1 := cloudimagemetadata.Metadata{attrs, 0, "0"} 235 236 s.assertConcurrentSave(c, 237 metadata0, 238 metadata1, 239 metadata0, 240 metadata1, 241 ) 242 } 243 244 func (s *cloudImageMetadataSuite) TestSaveMetadataNoVersionPassed(c *gc.C) { 245 attrs := cloudimagemetadata.MetadataAttributes{ 246 Stream: "stream", 247 Series: "trusty", 248 Arch: "arch", 249 } 250 metadata0 := cloudimagemetadata.Metadata{attrs, 0, "1"} 251 s.assertRecordMetadata(c, metadata0) 252 } 253 254 func (s *cloudImageMetadataSuite) TestSaveMetadataNoSeriesPassed(c *gc.C) { 255 attrs := cloudimagemetadata.MetadataAttributes{ 256 Stream: "stream", 257 Arch: "arch", 258 } 259 metadata0 := cloudimagemetadata.Metadata{attrs, 0, "1"} 260 err := s.storage.SaveMetadata([]cloudimagemetadata.Metadata{metadata0}) 261 c.Assert(err, gc.ErrorMatches, regexp.QuoteMeta(`missing series: metadata for image 1 not valid`)) 262 } 263 264 func (s *cloudImageMetadataSuite) TestSaveMetadataUnsupportedSeriesPassed(c *gc.C) { 265 attrs := cloudimagemetadata.MetadataAttributes{ 266 Stream: "stream", 267 Series: "blah", 268 Arch: "arch", 269 } 270 metadata0 := cloudimagemetadata.Metadata{attrs, 0, "1"} 271 err := s.storage.SaveMetadata([]cloudimagemetadata.Metadata{metadata0}) 272 c.Assert(err, gc.ErrorMatches, regexp.QuoteMeta(`unknown version for series: "blah"`)) 273 } 274 275 func (s *cloudImageMetadataSuite) assertConcurrentSave(c *gc.C, metadata0, metadata1 cloudimagemetadata.Metadata, expected ...cloudimagemetadata.Metadata) { 276 addMetadata := func() { 277 s.assertRecordMetadata(c, metadata0) 278 } 279 defer txntesting.SetBeforeHooks(c, s.access.runner, addMetadata).Check() 280 s.assertRecordMetadata(c, metadata1) 281 s.assertMetadataRecorded(c, cloudimagemetadata.MetadataAttributes{}, expected...) 282 } 283 284 func (s *cloudImageMetadataSuite) assertRecordMetadata(c *gc.C, m cloudimagemetadata.Metadata) { 285 err := s.storage.SaveMetadata([]cloudimagemetadata.Metadata{m}) 286 c.Assert(err, jc.ErrorIsNil) 287 } 288 289 func (s *cloudImageMetadataSuite) assertMetadataRecorded( 290 c *gc.C, 291 criteria cloudimagemetadata.MetadataAttributes, 292 expected ...cloudimagemetadata.Metadata, 293 ) { 294 metadata, err := s.storage.FindMetadata(buildAttributesFilter(criteria)) 295 c.Assert(err, jc.ErrorIsNil) 296 297 // Collate expected into a map 298 groups := make(map[string][]cloudimagemetadata.Metadata) 299 for _, expectedMetadata := range expected { 300 groups[expectedMetadata.Source] = append(groups[expectedMetadata.Source], expectedMetadata) 301 } 302 303 // Compare maps by key; order of slices does not matter 304 c.Assert(groups, gc.HasLen, len(metadata)) 305 for source, expectedMetadata := range groups { 306 c.Assert(metadata[source], jc.SameContents, expectedMetadata) 307 } 308 } 309 310 func (s *cloudImageMetadataSuite) TestSupportedArchitectures(c *gc.C) { 311 stream := "stream" 312 region := "region-test" 313 314 arch1 := "arch" 315 attrs := cloudimagemetadata.MetadataAttributes{ 316 Stream: stream, 317 Region: region, 318 Version: "14.04", 319 Series: "trusty", 320 Arch: arch1, 321 VirtType: "virtType-test", 322 RootStorageType: "rootStorageType-test"} 323 324 added := cloudimagemetadata.Metadata{attrs, 0, "1"} 325 s.assertRecordMetadata(c, added) 326 s.assertMetadataRecorded(c, attrs, added) 327 328 addedNonUnique := cloudimagemetadata.Metadata{attrs, 0, "21"} 329 s.assertRecordMetadata(c, addedNonUnique) 330 s.assertMetadataRecorded(c, attrs, addedNonUnique) 331 332 arch2 := "anotherArch" 333 attrs.Arch = arch2 334 added2 := cloudimagemetadata.Metadata{attrs, 0, "21"} 335 s.assertRecordMetadata(c, added2) 336 s.assertMetadataRecorded(c, attrs, added2) 337 338 expected := []string{arch1, arch2} 339 uniqueArches, err := s.storage.SupportedArchitectures( 340 cloudimagemetadata.MetadataFilter{Stream: stream, Region: region}) 341 c.Assert(err, jc.ErrorIsNil) 342 c.Assert(uniqueArches, gc.DeepEquals, expected) 343 } 344 345 func (s *cloudImageMetadataSuite) TestSupportedArchitecturesUnmatchedStreams(c *gc.C) { 346 stream := "stream" 347 region := "region-test" 348 349 attrs := cloudimagemetadata.MetadataAttributes{ 350 Stream: "new-stream", 351 Region: region, 352 Version: "14.04", 353 Series: "trusty", 354 Arch: "arch", 355 VirtType: "virtType-test", 356 RootStorageType: "rootStorageType-test"} 357 358 added := cloudimagemetadata.Metadata{attrs, 0, "1"} 359 s.assertRecordMetadata(c, added) 360 s.assertMetadataRecorded(c, attrs, added) 361 362 uniqueArches, err := s.storage.SupportedArchitectures( 363 cloudimagemetadata.MetadataFilter{Stream: stream, Region: region}) 364 c.Assert(err, jc.ErrorIsNil) 365 c.Assert(uniqueArches, gc.DeepEquals, []string{}) 366 } 367 368 func (s *cloudImageMetadataSuite) TestSupportedArchitecturesUnmatchedRegions(c *gc.C) { 369 stream := "stream" 370 region := "region-test" 371 372 attrs := cloudimagemetadata.MetadataAttributes{ 373 Stream: stream, 374 Region: "new-region", 375 Version: "14.04", 376 Series: "trusty", 377 Arch: "arch", 378 VirtType: "virtType-test", 379 RootStorageType: "rootStorageType-test"} 380 381 added := cloudimagemetadata.Metadata{attrs, 0, "1"} 382 s.assertRecordMetadata(c, added) 383 s.assertMetadataRecorded(c, attrs, added) 384 385 uniqueArches, err := s.storage.SupportedArchitectures( 386 cloudimagemetadata.MetadataFilter{Stream: stream, Region: region}) 387 c.Assert(err, jc.ErrorIsNil) 388 c.Assert(uniqueArches, gc.DeepEquals, []string{}) 389 } 390 391 func (s *cloudImageMetadataSuite) TestSupportedArchitecturesUnmatchedStreamsAndRegions(c *gc.C) { 392 stream := "stream" 393 region := "region-test" 394 395 attrs := cloudimagemetadata.MetadataAttributes{ 396 Stream: "new-stream", 397 Region: "new-region", 398 Version: "14.04", 399 Series: "trusty", 400 Arch: "arch", 401 VirtType: "virtType-test", 402 RootStorageType: "rootStorageType-test"} 403 404 added := cloudimagemetadata.Metadata{attrs, 0, "1"} 405 s.assertRecordMetadata(c, added) 406 s.assertMetadataRecorded(c, attrs, added) 407 408 uniqueArches, err := s.storage.SupportedArchitectures( 409 cloudimagemetadata.MetadataFilter{Stream: stream, Region: region}) 410 c.Assert(err, jc.ErrorIsNil) 411 c.Assert(uniqueArches, gc.DeepEquals, []string{}) 412 } 413 414 func (s *cloudImageMetadataSuite) TestDeleteMetadata(c *gc.C) { 415 imageId := "ok-to-delete" 416 s.addTestImageMetadata(c, imageId) 417 s.assertDeleteMetadata(c, imageId) 418 s.assertNoMetadata(c) 419 420 // calling delete on it again should be a no-op 421 s.assertDeleteMetadata(c, imageId) 422 // make sure log has "nothing to delete" message 423 c.Assert(c.GetTestLog(), jc.Contains, "no metadata for image ID ok-to-delete to delete") 424 } 425 426 func (s *cloudImageMetadataSuite) TestDeleteDiffMetadataConcurrently(c *gc.C) { 427 imageId := "ok-to-delete" 428 s.addTestImageMetadata(c, imageId) 429 430 diffImageId := "ok-to-delete-too" 431 s.addTestImageMetadata(c, diffImageId) 432 433 s.assertConcurrentDelete(c, imageId, diffImageId) 434 } 435 436 func (s *cloudImageMetadataSuite) TestDeleteSameMetadataConcurrently(c *gc.C) { 437 imageId := "ok-to-delete" 438 s.addTestImageMetadata(c, imageId) 439 440 s.assertConcurrentDelete(c, imageId, imageId) 441 } 442 443 func (s *cloudImageMetadataSuite) assertConcurrentDelete(c *gc.C, imageId0, imageId1 string) { 444 deleteMetadata := func() { 445 s.assertDeleteMetadata(c, imageId0) 446 } 447 defer txntesting.SetBeforeHooks(c, s.access.runner, deleteMetadata).Check() 448 s.assertDeleteMetadata(c, imageId1) 449 s.assertNoMetadata(c) 450 } 451 452 func (s *cloudImageMetadataSuite) addTestImageMetadata(c *gc.C, imageId string) { 453 attrs := cloudimagemetadata.MetadataAttributes{ 454 Stream: "stream", 455 Region: "region-test", 456 Version: "14.04", 457 Series: "trusty", 458 Arch: "arch", 459 VirtType: "virtType-test", 460 RootStorageType: "rootStorageType-test"} 461 462 added := cloudimagemetadata.Metadata{attrs, 0, imageId} 463 s.assertRecordMetadata(c, added) 464 s.assertMetadataRecorded(c, attrs, added) 465 } 466 467 func (s *cloudImageMetadataSuite) assertDeleteMetadata(c *gc.C, imageId string) { 468 err := s.storage.DeleteMetadata(imageId) 469 c.Assert(err, jc.ErrorIsNil) 470 } 471 472 func (s *cloudImageMetadataSuite) assertNoMetadata(c *gc.C) { 473 // No metadata should be in store. 474 // So when looking for all and none is found, err. 475 found, err := s.storage.FindMetadata(cloudimagemetadata.MetadataFilter{}) 476 c.Assert(err, jc.Satisfies, errors.IsNotFound) 477 c.Assert(err, gc.ErrorMatches, "matching cloud image metadata not found") 478 c.Assert(found, gc.HasLen, 0) 479 } 480 481 type TestMongo struct { 482 database *mgo.Database 483 runner txn.Runner 484 } 485 486 func NewTestMongo(database *mgo.Database) *TestMongo { 487 return &TestMongo{ 488 database: database, 489 runner: txn.NewRunner(txn.RunnerParams{ 490 Database: database, 491 }), 492 } 493 } 494 495 func (m *TestMongo) GetCollection(name string) (mongo.Collection, func()) { 496 return mongo.CollectionFromName(m.database, name) 497 } 498 499 func (m *TestMongo) RunTransaction(getTxn txn.TransactionSource) error { 500 return m.runner.Run(getTxn) 501 }