1
// Copyright 2015 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
4
package cloudimagemetadata_test
9
"github.com/juju/errors"
10
"github.com/juju/testing"
11
jc "github.com/juju/testing/checkers"
13
txntesting "github.com/juju/txn/testing"
14
gc "gopkg.in/check.v1"
17
"github.com/juju/juju/mongo"
18
"github.com/juju/juju/state/cloudimagemetadata"
21
type cloudImageMetadataSuite struct {
22
testing.IsolatedMgoSuite
25
storage cloudimagemetadata.Storage
28
var _ = gc.Suite(&cloudImageMetadataSuite{})
31
envName = "test-model"
32
collectionName = "test-collection"
35
func (s *cloudImageMetadataSuite) SetUpTest(c *gc.C) {
36
s.IsolatedMgoSuite.SetUpTest(c)
38
db := s.MgoSuite.Session.DB("juju")
40
s.access = NewTestMongo(db)
41
s.storage = cloudimagemetadata.NewStorage(envName, collectionName, s.access)
44
func (s *cloudImageMetadataSuite) TestSaveMetadata(c *gc.C) {
45
attrs1 := cloudimagemetadata.MetadataAttributes{
47
Region: "region-test",
51
VirtType: "virtType-test",
52
RootStorageType: "rootStorageType-test",
54
attrs2 := cloudimagemetadata.MetadataAttributes{
61
added := []cloudimagemetadata.Metadata{
65
s.assertRecordMetadata(c, added[0])
66
s.assertRecordMetadata(c, added[1])
67
s.assertMetadataRecorded(c, cloudimagemetadata.MetadataAttributes{}, added...)
70
func (s *cloudImageMetadataSuite) TestFindMetadataNotFound(c *gc.C) {
73
// insert something...
74
attrs := cloudimagemetadata.MetadataAttributes{
81
RootStorageType: "rootStorageType"}
82
m := cloudimagemetadata.Metadata{attrs, 0, "1"}
83
s.assertRecordMetadata(c, m)
85
// ...but look for something else.
86
none, err := s.storage.FindMetadata(cloudimagemetadata.MetadataFilter{
87
Stream: "something else",
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)
95
func buildAttributesFilter(attrs cloudimagemetadata.MetadataAttributes) cloudimagemetadata.MetadataFilter {
96
filter := cloudimagemetadata.MetadataFilter{
99
VirtType: attrs.VirtType,
100
RootStorageType: attrs.RootStorageType}
101
if attrs.Series != "" {
102
filter.Series = []string{attrs.Series}
104
if attrs.Arch != "" {
105
filter.Arches = []string{attrs.Arch}
110
func (s *cloudImageMetadataSuite) TestFindMetadata(c *gc.C) {
111
attrs := cloudimagemetadata.MetadataAttributes{
117
VirtType: "virtType",
118
RootStorageType: "rootStorageType"}
120
m := cloudimagemetadata.Metadata{attrs, 0, "1"}
122
_, err := s.storage.FindMetadata(buildAttributesFilter(attrs))
123
c.Assert(err, jc.Satisfies, errors.IsNotFound)
125
s.assertRecordMetadata(c, m)
126
expected := []cloudimagemetadata.Metadata{m}
127
s.assertMetadataRecorded(c, attrs, expected...)
129
attrs.Stream = "another_stream"
130
m = cloudimagemetadata.Metadata{attrs, 0, "2"}
131
s.assertRecordMetadata(c, m)
133
expected = append(expected, m)
135
s.assertMetadataRecorded(c, cloudimagemetadata.MetadataAttributes{Region: "region"}, expected...)
138
func (s *cloudImageMetadataSuite) TestSaveMetadataUpdateSameAttrsAndImages(c *gc.C) {
139
attrs := cloudimagemetadata.MetadataAttributes{
145
metadata0 := cloudimagemetadata.Metadata{attrs, 0, "1"}
146
metadata1 := cloudimagemetadata.Metadata{attrs, 0, "1"}
148
s.assertRecordMetadata(c, metadata0)
149
s.assertRecordMetadata(c, metadata1)
150
s.assertMetadataRecorded(c, attrs, metadata1)
153
func (s *cloudImageMetadataSuite) TestSaveMetadataUpdateSameAttrsDiffImages(c *gc.C) {
154
attrs := cloudimagemetadata.MetadataAttributes{
160
metadata0 := cloudimagemetadata.Metadata{attrs, 0, "1"}
161
metadata1 := cloudimagemetadata.Metadata{attrs, 0, "12"}
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)
170
func (s *cloudImageMetadataSuite) TestSaveDiffMetadataConcurrentlyAndOrderByDateCreated(c *gc.C) {
171
attrs := cloudimagemetadata.MetadataAttributes{
177
metadata0 := cloudimagemetadata.Metadata{attrs, 0, "0"}
178
metadata1 := cloudimagemetadata.Metadata{attrs, 0, "1"}
179
metadata1.Stream = "scream"
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
190
func (s *cloudImageMetadataSuite) TestSaveSameMetadataDiffImageConcurrently(c *gc.C) {
191
attrs := cloudimagemetadata.MetadataAttributes{
197
metadata0 := cloudimagemetadata.Metadata{attrs, 0, "0"}
198
metadata1 := cloudimagemetadata.Metadata{attrs, 0, "1"}
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
207
func (s *cloudImageMetadataSuite) TestSaveSameMetadataSameImageConcurrently(c *gc.C) {
208
attrs := cloudimagemetadata.MetadataAttributes{
214
metadata0 := cloudimagemetadata.Metadata{attrs, 0, "0"}
216
s.assertConcurrentSave(c,
217
metadata0, // add this one
218
metadata0, // add it again
219
metadata0, // varify only one is in the list
223
func (s *cloudImageMetadataSuite) TestSaveSameMetadataSameImageDiffSourceConcurrently(c *gc.C) {
224
attrs := cloudimagemetadata.MetadataAttributes{
231
metadata0 := cloudimagemetadata.Metadata{attrs, 0, "0"}
233
attrs.Source = "custom"
234
metadata1 := cloudimagemetadata.Metadata{attrs, 0, "0"}
236
s.assertConcurrentSave(c,
244
func (s *cloudImageMetadataSuite) TestSaveMetadataNoVersionPassed(c *gc.C) {
245
attrs := cloudimagemetadata.MetadataAttributes{
250
metadata0 := cloudimagemetadata.Metadata{attrs, 0, "1"}
251
s.assertRecordMetadata(c, metadata0)
254
func (s *cloudImageMetadataSuite) TestSaveMetadataNoSeriesPassed(c *gc.C) {
255
attrs := cloudimagemetadata.MetadataAttributes{
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`))
264
func (s *cloudImageMetadataSuite) TestSaveMetadataUnsupportedSeriesPassed(c *gc.C) {
265
attrs := cloudimagemetadata.MetadataAttributes{
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"`))
275
func (s *cloudImageMetadataSuite) assertConcurrentSave(c *gc.C, metadata0, metadata1 cloudimagemetadata.Metadata, expected ...cloudimagemetadata.Metadata) {
276
addMetadata := func() {
277
s.assertRecordMetadata(c, metadata0)
279
defer txntesting.SetBeforeHooks(c, s.access.runner, addMetadata).Check()
280
s.assertRecordMetadata(c, metadata1)
281
s.assertMetadataRecorded(c, cloudimagemetadata.MetadataAttributes{}, expected...)
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)
289
func (s *cloudImageMetadataSuite) assertMetadataRecorded(
291
criteria cloudimagemetadata.MetadataAttributes,
292
expected ...cloudimagemetadata.Metadata,
294
metadata, err := s.storage.FindMetadata(buildAttributesFilter(criteria))
295
c.Assert(err, jc.ErrorIsNil)
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)
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)
310
func (s *cloudImageMetadataSuite) TestSupportedArchitectures(c *gc.C) {
312
region := "region-test"
315
attrs := cloudimagemetadata.MetadataAttributes{
321
VirtType: "virtType-test",
322
RootStorageType: "rootStorageType-test"}
324
added := cloudimagemetadata.Metadata{attrs, 0, "1"}
325
s.assertRecordMetadata(c, added)
326
s.assertMetadataRecorded(c, attrs, added)
328
addedNonUnique := cloudimagemetadata.Metadata{attrs, 0, "21"}
329
s.assertRecordMetadata(c, addedNonUnique)
330
s.assertMetadataRecorded(c, attrs, addedNonUnique)
332
arch2 := "anotherArch"
334
added2 := cloudimagemetadata.Metadata{attrs, 0, "21"}
335
s.assertRecordMetadata(c, added2)
336
s.assertMetadataRecorded(c, attrs, added2)
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)
345
func (s *cloudImageMetadataSuite) TestSupportedArchitecturesUnmatchedStreams(c *gc.C) {
347
region := "region-test"
349
attrs := cloudimagemetadata.MetadataAttributes{
350
Stream: "new-stream",
355
VirtType: "virtType-test",
356
RootStorageType: "rootStorageType-test"}
358
added := cloudimagemetadata.Metadata{attrs, 0, "1"}
359
s.assertRecordMetadata(c, added)
360
s.assertMetadataRecorded(c, attrs, added)
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{})
368
func (s *cloudImageMetadataSuite) TestSupportedArchitecturesUnmatchedRegions(c *gc.C) {
370
region := "region-test"
372
attrs := cloudimagemetadata.MetadataAttributes{
374
Region: "new-region",
378
VirtType: "virtType-test",
379
RootStorageType: "rootStorageType-test"}
381
added := cloudimagemetadata.Metadata{attrs, 0, "1"}
382
s.assertRecordMetadata(c, added)
383
s.assertMetadataRecorded(c, attrs, added)
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{})
391
func (s *cloudImageMetadataSuite) TestSupportedArchitecturesUnmatchedStreamsAndRegions(c *gc.C) {
393
region := "region-test"
395
attrs := cloudimagemetadata.MetadataAttributes{
396
Stream: "new-stream",
397
Region: "new-region",
401
VirtType: "virtType-test",
402
RootStorageType: "rootStorageType-test"}
404
added := cloudimagemetadata.Metadata{attrs, 0, "1"}
405
s.assertRecordMetadata(c, added)
406
s.assertMetadataRecorded(c, attrs, added)
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{})
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)
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")
426
func (s *cloudImageMetadataSuite) TestDeleteDiffMetadataConcurrently(c *gc.C) {
427
imageId := "ok-to-delete"
428
s.addTestImageMetadata(c, imageId)
430
diffImageId := "ok-to-delete-too"
431
s.addTestImageMetadata(c, diffImageId)
433
s.assertConcurrentDelete(c, imageId, diffImageId)
436
func (s *cloudImageMetadataSuite) TestDeleteSameMetadataConcurrently(c *gc.C) {
437
imageId := "ok-to-delete"
438
s.addTestImageMetadata(c, imageId)
440
s.assertConcurrentDelete(c, imageId, imageId)
443
func (s *cloudImageMetadataSuite) assertConcurrentDelete(c *gc.C, imageId0, imageId1 string) {
444
deleteMetadata := func() {
445
s.assertDeleteMetadata(c, imageId0)
447
defer txntesting.SetBeforeHooks(c, s.access.runner, deleteMetadata).Check()
448
s.assertDeleteMetadata(c, imageId1)
449
s.assertNoMetadata(c)
452
func (s *cloudImageMetadataSuite) addTestImageMetadata(c *gc.C, imageId string) {
453
attrs := cloudimagemetadata.MetadataAttributes{
455
Region: "region-test",
459
VirtType: "virtType-test",
460
RootStorageType: "rootStorageType-test"}
462
added := cloudimagemetadata.Metadata{attrs, 0, imageId}
463
s.assertRecordMetadata(c, added)
464
s.assertMetadataRecorded(c, attrs, added)
467
func (s *cloudImageMetadataSuite) assertDeleteMetadata(c *gc.C, imageId string) {
468
err := s.storage.DeleteMetadata(imageId)
469
c.Assert(err, jc.ErrorIsNil)
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)
481
type TestMongo struct {
482
database *mgo.Database
486
func NewTestMongo(database *mgo.Database) *TestMongo {
489
runner: txn.NewRunner(txn.RunnerParams{
495
func (m *TestMongo) GetCollection(name string) (mongo.Collection, func()) {
496
return mongo.CollectionFromName(m.database, name)
499
func (m *TestMongo) RunTransaction(getTxn txn.TransactionSource) error {
500
return m.runner.Run(getTxn)