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