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  }