github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/state/imagestorage/image_test.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package imagestorage_test
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"io"
    10  	"io/ioutil"
    11  	"strings"
    12  	stdtesting "testing"
    13  	"time" // Only used for time types.
    14  
    15  	"github.com/juju/errors"
    16  	gitjujutesting "github.com/juju/testing"
    17  	jc "github.com/juju/testing/checkers"
    18  	"github.com/juju/txn"
    19  	jujutxn "github.com/juju/txn"
    20  	txntesting "github.com/juju/txn/testing"
    21  	gc "gopkg.in/check.v1"
    22  	"gopkg.in/mgo.v2"
    23  
    24  	"github.com/juju/juju/state/imagestorage"
    25  	"github.com/juju/juju/testing"
    26  )
    27  
    28  var _ = gc.Suite(&ImageSuite{})
    29  
    30  func TestPackage(t *stdtesting.T) {
    31  	gc.TestingT(t)
    32  }
    33  
    34  type ImageSuite struct {
    35  	testing.BaseSuite
    36  	mongo              *gitjujutesting.MgoInstance
    37  	session            *mgo.Session
    38  	storage            imagestorage.Storage
    39  	metadataCollection *mgo.Collection
    40  	txnRunner          jujutxn.Runner
    41  }
    42  
    43  func (s *ImageSuite) SetUpTest(c *gc.C) {
    44  	s.BaseSuite.SetUpTest(c)
    45  	s.mongo = &gitjujutesting.MgoInstance{}
    46  	s.mongo.Start(nil)
    47  
    48  	var err error
    49  	s.session, err = s.mongo.Dial()
    50  	c.Assert(err, gc.IsNil)
    51  	s.storage = imagestorage.NewStorage(s.session, "my-uuid")
    52  	s.metadataCollection = imagestorage.MetadataCollection(s.storage)
    53  	s.txnRunner = jujutxn.NewRunner(jujutxn.RunnerParams{Database: s.metadataCollection.Database})
    54  	s.patchTransactionRunner()
    55  }
    56  
    57  func (s *ImageSuite) TearDownTest(c *gc.C) {
    58  	s.session.Close()
    59  	s.mongo.DestroyWithLog()
    60  	s.BaseSuite.TearDownTest(c)
    61  }
    62  func (s *ImageSuite) patchTransactionRunner() {
    63  	s.PatchValue(imagestorage.TxnRunner, func(db *mgo.Database) txn.Runner {
    64  		return s.txnRunner
    65  	})
    66  }
    67  
    68  func (s *ImageSuite) TestAddImage(c *gc.C) {
    69  	s.testAddImage(c, "some-image")
    70  }
    71  
    72  func (s *ImageSuite) TestAddImageReplaces(c *gc.C) {
    73  	s.testAddImage(c, "abc")
    74  	s.testAddImage(c, "defghi")
    75  }
    76  
    77  func checkMetadata(c *gc.C, fromDb, metadata *imagestorage.Metadata) {
    78  	c.Assert(fromDb.Created.IsZero(), jc.IsFalse)
    79  	// We don't want Now() here, we want NonZeroTime().Add(...). Before
    80  	// that can happen, we need to look at AddImage for its Created
    81  	// timestamp.
    82  	c.Assert(fromDb.Created.Before(time.Now()), jc.IsTrue)
    83  	fromDb.Created = testing.ZeroTime()
    84  	c.Assert(metadata, gc.DeepEquals, fromDb)
    85  }
    86  
    87  func checkAllMetadata(c *gc.C, fromDb []*imagestorage.Metadata, metadata ...*imagestorage.Metadata) {
    88  	c.Assert(len(metadata), gc.Equals, len(fromDb))
    89  	for i, m := range metadata {
    90  		checkMetadata(c, fromDb[i], m)
    91  	}
    92  }
    93  
    94  func (s *ImageSuite) testAddImage(c *gc.C, content string) {
    95  	var r io.Reader = bytes.NewReader([]byte(content))
    96  	addedMetadata := &imagestorage.Metadata{
    97  		ModelUUID: "my-uuid",
    98  		Kind:      "lxc",
    99  		Series:    "trusty",
   100  		Arch:      "amd64",
   101  		Size:      int64(len(content)),
   102  		SHA256:    "hash(" + content + ")",
   103  		SourceURL: "http://path",
   104  	}
   105  	err := s.storage.AddImage(r, addedMetadata)
   106  	c.Assert(err, gc.IsNil)
   107  
   108  	metadata, rc, err := s.storage.Image("lxc", "trusty", "amd64")
   109  	c.Assert(err, gc.IsNil)
   110  	c.Assert(r, gc.NotNil)
   111  	defer rc.Close()
   112  	checkMetadata(c, metadata, addedMetadata)
   113  
   114  	data, err := ioutil.ReadAll(rc)
   115  	c.Assert(err, gc.IsNil)
   116  	c.Assert(string(data), gc.Equals, content)
   117  }
   118  
   119  func (s *ImageSuite) TestImage(c *gc.C) {
   120  	_, _, err := s.storage.Image("lxc", "trusty", "amd64")
   121  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   122  	c.Assert(err, gc.ErrorMatches, `.* image metadata not found`)
   123  
   124  	s.addMetadataDoc(c, "lxc", "trusty", "amd64", 3, "hash(abc)", "path", "http://path")
   125  	_, _, err = s.storage.Image("lxc", "trusty", "amd64")
   126  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   127  	c.Assert(err, gc.ErrorMatches, `resource at path "buckets/my-uuid/path" not found`)
   128  
   129  	managedStorage := imagestorage.ManagedStorage(s.storage, s.session)
   130  	err = managedStorage.PutForBucket("my-uuid", "path", strings.NewReader("blah"), 4)
   131  	c.Assert(err, gc.IsNil)
   132  
   133  	metadata, r, err := s.storage.Image("lxc", "trusty", "amd64")
   134  	c.Assert(err, gc.IsNil)
   135  	defer r.Close()
   136  	checkMetadata(c, metadata, &imagestorage.Metadata{
   137  		ModelUUID: "my-uuid",
   138  		Kind:      "lxc",
   139  		Series:    "trusty",
   140  		Arch:      "amd64",
   141  		Size:      3,
   142  		SHA256:    "hash(abc)",
   143  		SourceURL: "http://path",
   144  	})
   145  
   146  	data, err := ioutil.ReadAll(r)
   147  	c.Assert(err, gc.IsNil)
   148  	c.Assert(string(data), gc.Equals, "blah")
   149  }
   150  
   151  func (s *ImageSuite) TestAddImageRemovesExisting(c *gc.C) {
   152  	// Add a metadata doc and a blob at a known path, then
   153  	// call AddImage and ensure the original blob is removed.
   154  	s.addMetadataDoc(c, "lxc", "trusty", "amd64", 3, "hash(abc)", "path", "http://path")
   155  	managedStorage := imagestorage.ManagedStorage(s.storage, s.session)
   156  	err := managedStorage.PutForBucket("my-uuid", "path", strings.NewReader("blah"), 4)
   157  	c.Assert(err, gc.IsNil)
   158  
   159  	addedMetadata := &imagestorage.Metadata{
   160  		ModelUUID: "my-uuid",
   161  		Kind:      "lxc",
   162  		Series:    "trusty",
   163  		Arch:      "amd64",
   164  		Size:      6,
   165  		SHA256:    "hash(xyzzzz)",
   166  		SourceURL: "http://path",
   167  	}
   168  	err = s.storage.AddImage(strings.NewReader("xyzzzz"), addedMetadata)
   169  	c.Assert(err, gc.IsNil)
   170  
   171  	// old blob should be gone
   172  	_, _, err = managedStorage.GetForBucket("my-uuid", "path")
   173  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   174  
   175  	s.assertImage(c, addedMetadata, "xyzzzz")
   176  }
   177  
   178  func (s *ImageSuite) TestAddImageRemovesExistingRemoveFails(c *gc.C) {
   179  	// Add a metadata doc and a blob at a known path, then
   180  	// call AddImage and ensure that AddImage attempts to remove
   181  	// the original blob, but does not return an error if it
   182  	// fails.
   183  	s.addMetadataDoc(c, "lxc", "trusty", "amd64", 3, "hash(abc)", "path", "http://path")
   184  	managedStorage := imagestorage.ManagedStorage(s.storage, s.session)
   185  	err := managedStorage.PutForBucket("my-uuid", "path", strings.NewReader("blah"), 4)
   186  	c.Assert(err, gc.IsNil)
   187  
   188  	storage := imagestorage.NewStorage(s.session, "my-uuid")
   189  	s.PatchValue(imagestorage.GetManagedStorage, imagestorage.RemoveFailsManagedStorage)
   190  	addedMetadata := &imagestorage.Metadata{
   191  		ModelUUID: "my-uuid",
   192  		Kind:      "lxc",
   193  		Series:    "trusty",
   194  		Arch:      "amd64",
   195  		Size:      6,
   196  		SHA256:    "hash(xyzzzz)",
   197  		SourceURL: "http://path",
   198  	}
   199  	err = storage.AddImage(strings.NewReader("xyzzzz"), addedMetadata)
   200  	c.Assert(err, gc.IsNil)
   201  
   202  	// old blob should still be there
   203  	r, _, err := managedStorage.GetForBucket("my-uuid", "path")
   204  	c.Assert(err, gc.IsNil)
   205  	r.Close()
   206  
   207  	s.assertImage(c, addedMetadata, "xyzzzz")
   208  }
   209  
   210  type errorTransactionRunner struct {
   211  	txn.Runner
   212  }
   213  
   214  func (errorTransactionRunner) Run(transactions txn.TransactionSource) error {
   215  	return errors.New("Run fails")
   216  }
   217  
   218  func (s *ImageSuite) TestAddImageRemovesBlobOnFailure(c *gc.C) {
   219  	storage := imagestorage.NewStorage(s.session, "my-uuid")
   220  	s.txnRunner = errorTransactionRunner{s.txnRunner}
   221  	addedMetadata := &imagestorage.Metadata{
   222  		ModelUUID: "my-uuid",
   223  		Kind:      "lxc",
   224  		Series:    "trusty",
   225  		Arch:      "amd64",
   226  		Size:      6,
   227  		SHA256:    "hash",
   228  	}
   229  	err := storage.AddImage(strings.NewReader("xyzzzz"), addedMetadata)
   230  	c.Assert(err, gc.ErrorMatches, "cannot store image metadata: Run fails")
   231  
   232  	path := fmt.Sprintf(
   233  		"images/%s-%s-%s:%s", addedMetadata.Kind, addedMetadata.Series, addedMetadata.Arch, addedMetadata.SHA256)
   234  	managedStorage := imagestorage.ManagedStorage(s.storage, s.session)
   235  	_, _, err = managedStorage.GetForBucket("my-uuid", path)
   236  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   237  }
   238  
   239  func (s *ImageSuite) TestAddImageRemovesBlobOnFailureRemoveFails(c *gc.C) {
   240  	storage := imagestorage.NewStorage(s.session, "my-uuid")
   241  	s.PatchValue(imagestorage.GetManagedStorage, imagestorage.RemoveFailsManagedStorage)
   242  	s.txnRunner = errorTransactionRunner{s.txnRunner}
   243  	addedMetadata := &imagestorage.Metadata{
   244  		ModelUUID: "my-uuid",
   245  		Kind:      "lxc",
   246  		Series:    "trusty",
   247  		Arch:      "amd64",
   248  		Size:      6,
   249  		SHA256:    "hash",
   250  	}
   251  	err := storage.AddImage(strings.NewReader("xyzzzz"), addedMetadata)
   252  	c.Assert(err, gc.ErrorMatches, "cannot store image metadata: Run fails")
   253  
   254  	// blob should still be there, because the removal failed.
   255  	path := fmt.Sprintf(
   256  		"images/%s-%s-%s:%s", addedMetadata.Kind, addedMetadata.Series, addedMetadata.Arch, addedMetadata.SHA256)
   257  	managedStorage := imagestorage.ManagedStorage(s.storage, s.session)
   258  	r, _, err := managedStorage.GetForBucket("my-uuid", path)
   259  	c.Assert(err, gc.IsNil)
   260  	r.Close()
   261  }
   262  
   263  func (s *ImageSuite) TestAddImageSame(c *gc.C) {
   264  	metadata := &imagestorage.Metadata{
   265  		ModelUUID: "my-uuid", Kind: "lxc", Series: "trusty", Arch: "amd64", Size: 1, SHA256: "0", SourceURL: "http://path",
   266  	}
   267  	for i := 0; i < 2; i++ {
   268  		err := s.storage.AddImage(strings.NewReader("0"), metadata)
   269  		c.Assert(err, gc.IsNil)
   270  		s.assertImage(c, metadata, "0")
   271  	}
   272  }
   273  
   274  func (s *ImageSuite) TestAddImageAndJustMetadataExists(c *gc.C) {
   275  	s.addMetadataDoc(c, "lxc", "trusty", "amd64", 3, "hash(abc)", "images/lxc-trusty-amd64:hash(abc)", "http://path")
   276  	n, err := s.metadataCollection.Count()
   277  	c.Assert(err, jc.ErrorIsNil)
   278  	c.Assert(n, gc.Equals, 1)
   279  	s.testAddImage(c, "abc")
   280  	n, err = s.metadataCollection.Count()
   281  	c.Assert(err, jc.ErrorIsNil)
   282  	c.Assert(n, gc.Equals, 1)
   283  }
   284  
   285  func (s *ImageSuite) TestJustMetadataFails(c *gc.C) {
   286  	s.addMetadataDoc(c, "lxc", "trusty", "amd64", 3, "hash(abc)", "images/lxc-trusty-amd64:hash(abc)", "http://path")
   287  	_, rc, err := s.storage.Image("lxc", "trusty", "amd64")
   288  	c.Assert(rc, gc.IsNil)
   289  	c.Assert(err, gc.NotNil)
   290  }
   291  
   292  func (s *ImageSuite) TestAddImageConcurrent(c *gc.C) {
   293  	metadata0 := &imagestorage.Metadata{
   294  		ModelUUID: "my-uuid", Kind: "lxc", Series: "trusty", Arch: "amd64", Size: 1, SHA256: "0", SourceURL: "http://path",
   295  	}
   296  	metadata1 := &imagestorage.Metadata{
   297  		ModelUUID: "my-uuid", Kind: "lxc", Series: "trusty", Arch: "amd64", Size: 1, SHA256: "1", SourceURL: "http://path",
   298  	}
   299  
   300  	addMetadata := func() {
   301  		err := s.storage.AddImage(strings.NewReader("0"), metadata0)
   302  		c.Assert(err, gc.IsNil)
   303  		managedStorage := imagestorage.ManagedStorage(s.storage, s.session)
   304  		r, _, err := managedStorage.GetForBucket("my-uuid", "images/lxc-trusty-amd64:0")
   305  		c.Assert(err, gc.IsNil)
   306  		r.Close()
   307  	}
   308  	defer txntesting.SetBeforeHooks(c, s.txnRunner, addMetadata).Check()
   309  
   310  	err := s.storage.AddImage(strings.NewReader("1"), metadata1)
   311  	c.Assert(err, gc.IsNil)
   312  
   313  	// Blob added in before-hook should be removed.
   314  	managedStorage := imagestorage.ManagedStorage(s.storage, s.session)
   315  	_, _, err = managedStorage.GetForBucket("my-uuid", "images/lxc-trusty-amd64:0")
   316  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   317  
   318  	s.assertImage(c, metadata1, "1")
   319  }
   320  
   321  func (s *ImageSuite) TestAddImageExcessiveContention(c *gc.C) {
   322  	metadata := []*imagestorage.Metadata{
   323  		{ModelUUID: "my-uuid", Kind: "lxc", Series: "trusty", Arch: "amd64", Size: 1, SHA256: "0", SourceURL: "http://path"},
   324  		{ModelUUID: "my-uuid", Kind: "lxc", Series: "trusty", Arch: "amd64", Size: 1, SHA256: "1", SourceURL: "http://path"},
   325  		{ModelUUID: "my-uuid", Kind: "lxc", Series: "trusty", Arch: "amd64", Size: 1, SHA256: "2", SourceURL: "http://path"},
   326  		{ModelUUID: "my-uuid", Kind: "lxc", Series: "trusty", Arch: "amd64", Size: 1, SHA256: "3", SourceURL: "http://path"},
   327  	}
   328  
   329  	i := 1
   330  	addMetadata := func() {
   331  		err := s.storage.AddImage(strings.NewReader(metadata[i].SHA256), metadata[i])
   332  		c.Assert(err, gc.IsNil)
   333  		i++
   334  	}
   335  	defer txntesting.SetBeforeHooks(c, s.txnRunner, addMetadata, addMetadata, addMetadata).Check()
   336  
   337  	err := s.storage.AddImage(strings.NewReader(metadata[0].SHA256), metadata[0])
   338  	c.Assert(err, gc.ErrorMatches, "cannot store image metadata: state changing too quickly; try again soon")
   339  
   340  	// There should be no blobs apart from the last one added by the before-hook.
   341  	for _, metadata := range metadata[:3] {
   342  		path := fmt.Sprintf("images/%s-%s-%s:%s", metadata.Kind, metadata.Series, metadata.Arch, metadata.SHA256)
   343  		managedStorage := imagestorage.ManagedStorage(s.storage, s.session)
   344  		_, _, err = managedStorage.GetForBucket("my-uuid", path)
   345  		c.Assert(err, jc.Satisfies, errors.IsNotFound)
   346  	}
   347  
   348  	s.assertImage(c, metadata[3], "3")
   349  }
   350  
   351  func (s *ImageSuite) TestDeleteImage(c *gc.C) {
   352  	s.addMetadataDoc(c, "lxc", "trusty", "amd64", 3, "hash(abc)", "images/lxc-trusty-amd64:sha256", "http://lxc-trusty-amd64")
   353  	managedStorage := imagestorage.ManagedStorage(s.storage, s.session)
   354  	err := managedStorage.PutForBucket("my-uuid", "images/lxc-trusty-amd64:sha256", strings.NewReader("blah"), 4)
   355  	c.Assert(err, gc.IsNil)
   356  
   357  	_, rc, err := s.storage.Image("lxc", "trusty", "amd64")
   358  	c.Assert(err, gc.IsNil)
   359  	c.Assert(rc, gc.NotNil)
   360  	rc.Close()
   361  
   362  	metadata := &imagestorage.Metadata{
   363  		ModelUUID: "my-uuid",
   364  		Kind:      "lxc",
   365  		Series:    "trusty",
   366  		Arch:      "amd64",
   367  		SHA256:    "sha256",
   368  	}
   369  	err = s.storage.DeleteImage(metadata)
   370  	c.Assert(err, gc.IsNil)
   371  
   372  	_, _, err = managedStorage.GetForBucket("my-uuid", "images/lxc-trusty-amd64:sha256")
   373  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   374  
   375  	_, _, err = s.storage.Image("lxc", "trusty", "amd64")
   376  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   377  }
   378  
   379  func (s *ImageSuite) TestDeleteNotExistentImage(c *gc.C) {
   380  	metadata := &imagestorage.Metadata{
   381  		ModelUUID: "my-uuid",
   382  		Kind:      "lxc",
   383  		Series:    "trusty",
   384  		Arch:      "amd64",
   385  		SHA256:    "sha256",
   386  	}
   387  	err := s.storage.DeleteImage(metadata)
   388  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   389  }
   390  
   391  func (s *ImageSuite) addMetadataDoc(c *gc.C, kind, series, arch string, size int64, checksum, path, sourceURL string) {
   392  	doc := struct {
   393  		Id        string    `bson:"_id"`
   394  		ModelUUID string    `bson:"modelUUID"`
   395  		Kind      string    `bson:"kind"`
   396  		Series    string    `bson:"series"`
   397  		Arch      string    `bson:"arch"`
   398  		Size      int64     `bson:"size"`
   399  		SHA256    string    `bson:"sha256,omitempty"`
   400  		Path      string    `bson:"path"`
   401  		Created   time.Time `bson:"created"`
   402  		SourceURL string    `bson:"sourceurl"`
   403  	}{
   404  		Id:        fmt.Sprintf("my-uuid-%s-%s-%s", kind, series, arch),
   405  		ModelUUID: "my-uuid",
   406  		Kind:      kind,
   407  		Series:    series,
   408  		Arch:      arch,
   409  		Size:      size,
   410  		SHA256:    checksum,
   411  		Path:      path,
   412  		Created:   testing.NonZeroTime(),
   413  		SourceURL: sourceURL,
   414  	}
   415  	err := s.metadataCollection.Insert(&doc)
   416  	c.Assert(err, gc.IsNil)
   417  }
   418  
   419  func (s *ImageSuite) assertImage(c *gc.C, expected *imagestorage.Metadata, content string) {
   420  	metadata, r, err := s.storage.Image(expected.Kind, expected.Series, expected.Arch)
   421  	c.Assert(err, gc.IsNil)
   422  	defer r.Close()
   423  	checkMetadata(c, metadata, expected)
   424  
   425  	data, err := ioutil.ReadAll(r)
   426  	c.Assert(err, gc.IsNil)
   427  	c.Assert(string(data), gc.Equals, content)
   428  }
   429  
   430  func (s *ImageSuite) createListImageMetadata(c *gc.C) []*imagestorage.Metadata {
   431  	s.addMetadataDoc(c, "lxc", "trusty", "amd64", 3, "hash(abc)", "images/lxc-trusty-amd64:sha256", "http://lxc-trusty-amd64")
   432  	metadataLxc := &imagestorage.Metadata{
   433  		ModelUUID: "my-uuid",
   434  		Kind:      "lxc",
   435  		Series:    "trusty",
   436  		Arch:      "amd64",
   437  		SHA256:    "hash(abc)",
   438  		Size:      3,
   439  		SourceURL: "http://lxc-trusty-amd64",
   440  	}
   441  	s.addMetadataDoc(c, "kvm", "precise", "amd64", 4, "hash(abcd)", "images/kvm-precise-amd64:sha256", "http://kvm-precise-amd64")
   442  	metadataKvm := &imagestorage.Metadata{
   443  		ModelUUID: "my-uuid",
   444  		Kind:      "kvm",
   445  		Series:    "precise",
   446  		Arch:      "amd64",
   447  		SHA256:    "hash(abcd)",
   448  		Size:      4,
   449  		SourceURL: "http://kvm-precise-amd64",
   450  	}
   451  	return []*imagestorage.Metadata{metadataLxc, metadataKvm}
   452  }
   453  
   454  func (s *ImageSuite) TestListAllImages(c *gc.C) {
   455  	testMetadata := s.createListImageMetadata(c)
   456  	metadata, err := s.storage.ListImages(imagestorage.ImageFilter{})
   457  	c.Assert(err, gc.IsNil)
   458  	checkAllMetadata(c, metadata, testMetadata...)
   459  }
   460  
   461  func (s *ImageSuite) TestListImagesByKind(c *gc.C) {
   462  	testMetadata := s.createListImageMetadata(c)
   463  	metadata, err := s.storage.ListImages(imagestorage.ImageFilter{Kind: "lxc"})
   464  	c.Assert(err, gc.IsNil)
   465  	checkAllMetadata(c, metadata, testMetadata[0])
   466  }
   467  
   468  func (s *ImageSuite) TestListImagesBySeries(c *gc.C) {
   469  	testMetadata := s.createListImageMetadata(c)
   470  	metadata, err := s.storage.ListImages(imagestorage.ImageFilter{Series: "precise"})
   471  	c.Assert(err, gc.IsNil)
   472  	checkAllMetadata(c, metadata, testMetadata[1])
   473  }
   474  
   475  func (s *ImageSuite) TestListImagesByArch(c *gc.C) {
   476  	testMetadata := s.createListImageMetadata(c)
   477  	metadata, err := s.storage.ListImages(imagestorage.ImageFilter{Arch: "amd64"})
   478  	c.Assert(err, gc.IsNil)
   479  	checkAllMetadata(c, metadata, testMetadata...)
   480  }
   481  
   482  func (s *ImageSuite) TestListImagesNoMatch(c *gc.C) {
   483  	metadata, err := s.storage.ListImages(imagestorage.ImageFilter{Series: "utopic"})
   484  	c.Assert(err, gc.IsNil)
   485  	checkAllMetadata(c, metadata)
   486  }
   487  
   488  func (s *ImageSuite) TestListImagesMultiFilter(c *gc.C) {
   489  	testMetadata := s.createListImageMetadata(c)
   490  	metadata, err := s.storage.ListImages(imagestorage.ImageFilter{Series: "trusty", Arch: "amd64"})
   491  	c.Assert(err, gc.IsNil)
   492  	checkAllMetadata(c, metadata, testMetadata[0])
   493  }