github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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(c *gc.C, criteria cloudimagemetadata.MetadataAttributes, expected ...cloudimagemetadata.Metadata) {
   290  	metadata, err := s.storage.FindMetadata(buildAttributesFilter(criteria))
   291  	c.Assert(err, jc.ErrorIsNil)
   292  
   293  	groups := make(map[string][]cloudimagemetadata.Metadata)
   294  	for _, one := range expected {
   295  		groups[one.Source] = append(groups[one.Source], one)
   296  	}
   297  	c.Assert(metadata, jc.DeepEquals, groups)
   298  }
   299  
   300  func (s *cloudImageMetadataSuite) TestSupportedArchitectures(c *gc.C) {
   301  	stream := "stream"
   302  	region := "region-test"
   303  
   304  	arch1 := "arch"
   305  	attrs := cloudimagemetadata.MetadataAttributes{
   306  		Stream:          stream,
   307  		Region:          region,
   308  		Version:         "14.04",
   309  		Series:          "trusty",
   310  		Arch:            arch1,
   311  		VirtType:        "virtType-test",
   312  		RootStorageType: "rootStorageType-test"}
   313  
   314  	added := cloudimagemetadata.Metadata{attrs, 0, "1"}
   315  	s.assertRecordMetadata(c, added)
   316  	s.assertMetadataRecorded(c, attrs, added)
   317  
   318  	addedNonUnique := cloudimagemetadata.Metadata{attrs, 0, "21"}
   319  	s.assertRecordMetadata(c, addedNonUnique)
   320  	s.assertMetadataRecorded(c, attrs, addedNonUnique)
   321  
   322  	arch2 := "anotherArch"
   323  	attrs.Arch = arch2
   324  	added2 := cloudimagemetadata.Metadata{attrs, 0, "21"}
   325  	s.assertRecordMetadata(c, added2)
   326  	s.assertMetadataRecorded(c, attrs, added2)
   327  
   328  	expected := []string{arch1, arch2}
   329  	uniqueArches, err := s.storage.SupportedArchitectures(
   330  		cloudimagemetadata.MetadataFilter{Stream: stream, Region: region})
   331  	c.Assert(err, jc.ErrorIsNil)
   332  	c.Assert(uniqueArches, gc.DeepEquals, expected)
   333  }
   334  
   335  func (s *cloudImageMetadataSuite) TestSupportedArchitecturesUnmatchedStreams(c *gc.C) {
   336  	stream := "stream"
   337  	region := "region-test"
   338  
   339  	attrs := cloudimagemetadata.MetadataAttributes{
   340  		Stream:          "new-stream",
   341  		Region:          region,
   342  		Version:         "14.04",
   343  		Series:          "trusty",
   344  		Arch:            "arch",
   345  		VirtType:        "virtType-test",
   346  		RootStorageType: "rootStorageType-test"}
   347  
   348  	added := cloudimagemetadata.Metadata{attrs, 0, "1"}
   349  	s.assertRecordMetadata(c, added)
   350  	s.assertMetadataRecorded(c, attrs, added)
   351  
   352  	uniqueArches, err := s.storage.SupportedArchitectures(
   353  		cloudimagemetadata.MetadataFilter{Stream: stream, Region: region})
   354  	c.Assert(err, jc.ErrorIsNil)
   355  	c.Assert(uniqueArches, gc.DeepEquals, []string{})
   356  }
   357  
   358  func (s *cloudImageMetadataSuite) TestSupportedArchitecturesUnmatchedRegions(c *gc.C) {
   359  	stream := "stream"
   360  	region := "region-test"
   361  
   362  	attrs := cloudimagemetadata.MetadataAttributes{
   363  		Stream:          stream,
   364  		Region:          "new-region",
   365  		Version:         "14.04",
   366  		Series:          "trusty",
   367  		Arch:            "arch",
   368  		VirtType:        "virtType-test",
   369  		RootStorageType: "rootStorageType-test"}
   370  
   371  	added := cloudimagemetadata.Metadata{attrs, 0, "1"}
   372  	s.assertRecordMetadata(c, added)
   373  	s.assertMetadataRecorded(c, attrs, added)
   374  
   375  	uniqueArches, err := s.storage.SupportedArchitectures(
   376  		cloudimagemetadata.MetadataFilter{Stream: stream, Region: region})
   377  	c.Assert(err, jc.ErrorIsNil)
   378  	c.Assert(uniqueArches, gc.DeepEquals, []string{})
   379  }
   380  
   381  func (s *cloudImageMetadataSuite) TestSupportedArchitecturesUnmatchedStreamsAndRegions(c *gc.C) {
   382  	stream := "stream"
   383  	region := "region-test"
   384  
   385  	attrs := cloudimagemetadata.MetadataAttributes{
   386  		Stream:          "new-stream",
   387  		Region:          "new-region",
   388  		Version:         "14.04",
   389  		Series:          "trusty",
   390  		Arch:            "arch",
   391  		VirtType:        "virtType-test",
   392  		RootStorageType: "rootStorageType-test"}
   393  
   394  	added := cloudimagemetadata.Metadata{attrs, 0, "1"}
   395  	s.assertRecordMetadata(c, added)
   396  	s.assertMetadataRecorded(c, attrs, added)
   397  
   398  	uniqueArches, err := s.storage.SupportedArchitectures(
   399  		cloudimagemetadata.MetadataFilter{Stream: stream, Region: region})
   400  	c.Assert(err, jc.ErrorIsNil)
   401  	c.Assert(uniqueArches, gc.DeepEquals, []string{})
   402  }
   403  
   404  func (s *cloudImageMetadataSuite) TestDeleteMetadata(c *gc.C) {
   405  	imageId := "ok-to-delete"
   406  	s.addTestImageMetadata(c, imageId)
   407  	s.assertDeleteMetadata(c, imageId)
   408  	s.assertNoMetadata(c)
   409  
   410  	// calling delete on it again should be a no-op
   411  	s.assertDeleteMetadata(c, imageId)
   412  	// make sure log has "nothing to delete" message
   413  	c.Assert(c.GetTestLog(), jc.Contains, "no metadata for image ID ok-to-delete to delete")
   414  }
   415  
   416  func (s *cloudImageMetadataSuite) TestDeleteDiffMetadataConcurrently(c *gc.C) {
   417  	imageId := "ok-to-delete"
   418  	s.addTestImageMetadata(c, imageId)
   419  
   420  	diffImageId := "ok-to-delete-too"
   421  	s.addTestImageMetadata(c, diffImageId)
   422  
   423  	s.assertConcurrentDelete(c, imageId, diffImageId)
   424  }
   425  
   426  func (s *cloudImageMetadataSuite) TestDeleteSameMetadataConcurrently(c *gc.C) {
   427  	imageId := "ok-to-delete"
   428  	s.addTestImageMetadata(c, imageId)
   429  
   430  	s.assertConcurrentDelete(c, imageId, imageId)
   431  }
   432  
   433  func (s *cloudImageMetadataSuite) assertConcurrentDelete(c *gc.C, imageId0, imageId1 string) {
   434  	deleteMetadata := func() {
   435  		s.assertDeleteMetadata(c, imageId0)
   436  	}
   437  	defer txntesting.SetBeforeHooks(c, s.access.runner, deleteMetadata).Check()
   438  	s.assertDeleteMetadata(c, imageId1)
   439  	s.assertNoMetadata(c)
   440  }
   441  
   442  func (s *cloudImageMetadataSuite) addTestImageMetadata(c *gc.C, imageId string) {
   443  	attrs := cloudimagemetadata.MetadataAttributes{
   444  		Stream:          "stream",
   445  		Region:          "region-test",
   446  		Version:         "14.04",
   447  		Series:          "trusty",
   448  		Arch:            "arch",
   449  		VirtType:        "virtType-test",
   450  		RootStorageType: "rootStorageType-test"}
   451  
   452  	added := cloudimagemetadata.Metadata{attrs, 0, imageId}
   453  	s.assertRecordMetadata(c, added)
   454  	s.assertMetadataRecorded(c, attrs, added)
   455  }
   456  
   457  func (s *cloudImageMetadataSuite) assertDeleteMetadata(c *gc.C, imageId string) {
   458  	err := s.storage.DeleteMetadata(imageId)
   459  	c.Assert(err, jc.ErrorIsNil)
   460  }
   461  
   462  func (s *cloudImageMetadataSuite) assertNoMetadata(c *gc.C) {
   463  	// No metadata should be in store.
   464  	// So when looking for all and none is found, err.
   465  	found, err := s.storage.FindMetadata(cloudimagemetadata.MetadataFilter{})
   466  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   467  	c.Assert(err, gc.ErrorMatches, "matching cloud image metadata not found")
   468  	c.Assert(found, gc.HasLen, 0)
   469  }
   470  
   471  type TestMongo struct {
   472  	database *mgo.Database
   473  	runner   txn.Runner
   474  }
   475  
   476  func NewTestMongo(database *mgo.Database) *TestMongo {
   477  	return &TestMongo{
   478  		database: database,
   479  		runner: txn.NewRunner(txn.RunnerParams{
   480  			Database: database,
   481  		}),
   482  	}
   483  }
   484  
   485  func (m *TestMongo) GetCollection(name string) (mongo.Collection, func()) {
   486  	return mongo.CollectionFromName(m.database, name)
   487  }
   488  
   489  func (m *TestMongo) RunTransaction(getTxn txn.TransactionSource) error {
   490  	return m.runner.Run(getTxn)
   491  }