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