github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/resource/cmd/deploy_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package cmd
     5  
     6  import (
     7  	"bytes"
     8  	"io"
     9  	"os"
    10  
    11  	"github.com/juju/errors"
    12  	"github.com/juju/testing"
    13  	jc "github.com/juju/testing/checkers"
    14  	gc "gopkg.in/check.v1"
    15  	"gopkg.in/juju/charm.v6-unstable"
    16  	charmresource "gopkg.in/juju/charm.v6-unstable/resource"
    17  	"gopkg.in/macaroon.v1"
    18  
    19  	"github.com/juju/juju/charmstore"
    20  )
    21  
    22  type DeploySuite struct {
    23  	testing.IsolationSuite
    24  
    25  	stub *testing.Stub
    26  }
    27  
    28  var _ = gc.Suite(&DeploySuite{})
    29  
    30  func (s *DeploySuite) SetUpTest(c *gc.C) {
    31  	s.IsolationSuite.SetUpTest(c)
    32  
    33  	s.stub = &testing.Stub{}
    34  }
    35  
    36  func (s DeploySuite) TestDeployResourcesWithoutFiles(c *gc.C) {
    37  	deps := uploadDeps{s.stub, rsc{&bytes.Buffer{}}}
    38  	cURL := charm.MustParseURL("cs:~a-user/trusty/spam-5")
    39  	chID := charmstore.CharmID{
    40  		URL: cURL,
    41  	}
    42  	csMac := &macaroon.Macaroon{}
    43  	resources := map[string]charmresource.Meta{
    44  		"store-tarball": {
    45  			Name: "store-tarball",
    46  			Type: charmresource.TypeFile,
    47  			Path: "store.tgz",
    48  		},
    49  		"store-zip": {
    50  			Name: "store-zip",
    51  			Type: charmresource.TypeFile,
    52  			Path: "store.zip",
    53  		},
    54  	}
    55  
    56  	ids, err := DeployResources(DeployResourcesArgs{
    57  		ApplicationID:      "mysql",
    58  		CharmID:            chID,
    59  		CharmStoreMacaroon: csMac,
    60  		Filenames:          nil,
    61  		Client:             deps,
    62  		ResourcesMeta:      resources,
    63  	})
    64  	c.Assert(err, jc.ErrorIsNil)
    65  
    66  	c.Check(ids, gc.DeepEquals, map[string]string{
    67  		"store-tarball": "id-store-tarball",
    68  		"store-zip":     "id-store-zip",
    69  	})
    70  
    71  	s.stub.CheckCallNames(c, "AddPendingResources")
    72  	s.stub.CheckCall(c, 0, "AddPendingResources", "mysql", chID, csMac, []charmresource.Resource{{
    73  		Meta:     resources["store-tarball"],
    74  		Origin:   charmresource.OriginStore,
    75  		Revision: -1,
    76  	}, {
    77  		Meta:     resources["store-zip"],
    78  		Origin:   charmresource.OriginStore,
    79  		Revision: -1,
    80  	}})
    81  }
    82  
    83  func (s DeploySuite) TestUploadFilesOnly(c *gc.C) {
    84  	deps := uploadDeps{s.stub, rsc{&bytes.Buffer{}}}
    85  	cURL := charm.MustParseURL("cs:~a-user/trusty/spam-5")
    86  	chID := charmstore.CharmID{
    87  		URL: cURL,
    88  	}
    89  	csMac := &macaroon.Macaroon{}
    90  	du := deployUploader{
    91  		applicationID: "mysql",
    92  		chID:          chID,
    93  		csMac:         csMac,
    94  		client:        deps,
    95  		resources: map[string]charmresource.Meta{
    96  			"upload": {
    97  				Name: "upload",
    98  				Type: charmresource.TypeFile,
    99  				Path: "upload",
   100  			},
   101  			"store": {
   102  				Name: "store",
   103  				Type: charmresource.TypeFile,
   104  				Path: "store",
   105  			},
   106  		},
   107  		osOpen: deps.Open,
   108  		osStat: deps.Stat,
   109  	}
   110  
   111  	files := map[string]string{
   112  		"upload": "foobar.txt",
   113  	}
   114  	revisions := map[string]int{}
   115  	ids, err := du.upload(files, revisions)
   116  	c.Assert(err, jc.ErrorIsNil)
   117  	c.Check(ids, gc.DeepEquals, map[string]string{
   118  		"upload": "id-upload",
   119  		"store":  "id-store",
   120  	})
   121  
   122  	s.stub.CheckCallNames(c, "Stat", "AddPendingResources", "Open", "AddPendingResource")
   123  	expectedStore := []charmresource.Resource{
   124  		{
   125  			Meta:     du.resources["store"],
   126  			Origin:   charmresource.OriginStore,
   127  			Revision: -1,
   128  		},
   129  	}
   130  	s.stub.CheckCall(c, 1, "AddPendingResources", "mysql", chID, csMac, expectedStore)
   131  	s.stub.CheckCall(c, 2, "Open", "foobar.txt")
   132  
   133  	expectedUpload := charmresource.Resource{
   134  		Meta:   du.resources["upload"],
   135  		Origin: charmresource.OriginUpload,
   136  	}
   137  	s.stub.CheckCall(c, 3, "AddPendingResource", "mysql", expectedUpload, "foobar.txt", deps.ReadSeekCloser)
   138  }
   139  
   140  func (s DeploySuite) TestUploadRevisionsOnly(c *gc.C) {
   141  	deps := uploadDeps{s.stub, rsc{&bytes.Buffer{}}}
   142  	cURL := charm.MustParseURL("cs:~a-user/trusty/spam-5")
   143  	chID := charmstore.CharmID{
   144  		URL: cURL,
   145  	}
   146  	csMac := &macaroon.Macaroon{}
   147  	du := deployUploader{
   148  		applicationID: "mysql",
   149  		chID:          chID,
   150  		csMac:         csMac,
   151  		client:        deps,
   152  		resources: map[string]charmresource.Meta{
   153  			"upload": {
   154  				Name: "upload",
   155  				Type: charmresource.TypeFile,
   156  				Path: "upload",
   157  			},
   158  			"store": {
   159  				Name: "store",
   160  				Type: charmresource.TypeFile,
   161  				Path: "store",
   162  			},
   163  		},
   164  		osOpen: deps.Open,
   165  		osStat: deps.Stat,
   166  	}
   167  
   168  	files := map[string]string{}
   169  	revisions := map[string]int{
   170  		"store": 3,
   171  	}
   172  	ids, err := du.upload(files, revisions)
   173  	c.Assert(err, jc.ErrorIsNil)
   174  	c.Check(ids, gc.DeepEquals, map[string]string{
   175  		"upload": "id-upload",
   176  		"store":  "id-store",
   177  	})
   178  
   179  	s.stub.CheckCallNames(c, "AddPendingResources")
   180  	expectedStore := []charmresource.Resource{{
   181  		Meta:     du.resources["store"],
   182  		Origin:   charmresource.OriginStore,
   183  		Revision: 3,
   184  	}, {
   185  		Meta:     du.resources["upload"],
   186  		Origin:   charmresource.OriginStore,
   187  		Revision: -1,
   188  	}}
   189  	s.stub.CheckCall(c, 0, "AddPendingResources", "mysql", chID, csMac, expectedStore)
   190  }
   191  
   192  func (s DeploySuite) TestUploadFilesAndRevisions(c *gc.C) {
   193  	deps := uploadDeps{s.stub, rsc{&bytes.Buffer{}}}
   194  	cURL := charm.MustParseURL("cs:~a-user/trusty/spam-5")
   195  	chID := charmstore.CharmID{
   196  		URL: cURL,
   197  	}
   198  	csMac := &macaroon.Macaroon{}
   199  	du := deployUploader{
   200  		applicationID: "mysql",
   201  		chID:          chID,
   202  		csMac:         csMac,
   203  		client:        deps,
   204  		resources: map[string]charmresource.Meta{
   205  			"upload": {
   206  				Name: "upload",
   207  				Type: charmresource.TypeFile,
   208  				Path: "upload",
   209  			},
   210  			"store": {
   211  				Name: "store",
   212  				Type: charmresource.TypeFile,
   213  				Path: "store",
   214  			},
   215  		},
   216  		osOpen: deps.Open,
   217  		osStat: deps.Stat,
   218  	}
   219  
   220  	files := map[string]string{
   221  		"upload": "foobar.txt",
   222  	}
   223  	revisions := map[string]int{
   224  		"store": 3,
   225  	}
   226  	ids, err := du.upload(files, revisions)
   227  	c.Assert(err, jc.ErrorIsNil)
   228  	c.Check(ids, gc.DeepEquals, map[string]string{
   229  		"upload": "id-upload",
   230  		"store":  "id-store",
   231  	})
   232  
   233  	s.stub.CheckCallNames(c, "Stat", "AddPendingResources", "Open", "AddPendingResource")
   234  	expectedStore := []charmresource.Resource{
   235  		{
   236  			Meta:     du.resources["store"],
   237  			Origin:   charmresource.OriginStore,
   238  			Revision: 3,
   239  		},
   240  	}
   241  	s.stub.CheckCall(c, 1, "AddPendingResources", "mysql", chID, csMac, expectedStore)
   242  	s.stub.CheckCall(c, 2, "Open", "foobar.txt")
   243  
   244  	expectedUpload := charmresource.Resource{
   245  		Meta:   du.resources["upload"],
   246  		Origin: charmresource.OriginUpload,
   247  	}
   248  	s.stub.CheckCall(c, 3, "AddPendingResource", "mysql", expectedUpload, "foobar.txt", deps.ReadSeekCloser)
   249  }
   250  
   251  func (s DeploySuite) TestUploadUnexpectedResourceFile(c *gc.C) {
   252  	deps := uploadDeps{s.stub, rsc{&bytes.Buffer{}}}
   253  	du := deployUploader{
   254  		applicationID: "mysql",
   255  		client:        deps,
   256  		resources: map[string]charmresource.Meta{
   257  			"res1": {
   258  				Name: "res1",
   259  				Type: charmresource.TypeFile,
   260  				Path: "path",
   261  			},
   262  		},
   263  		osOpen: deps.Open,
   264  		osStat: deps.Stat,
   265  	}
   266  
   267  	files := map[string]string{"some bad resource": "foobar.txt"}
   268  	revisions := map[string]int{}
   269  	_, err := du.upload(files, revisions)
   270  	c.Check(err, gc.ErrorMatches, `unrecognized resource "some bad resource"`)
   271  
   272  	s.stub.CheckNoCalls(c)
   273  }
   274  
   275  func (s DeploySuite) TestUploadUnexpectedResourceRevision(c *gc.C) {
   276  	deps := uploadDeps{s.stub, rsc{&bytes.Buffer{}}}
   277  	du := deployUploader{
   278  		applicationID: "mysql",
   279  		client:        deps,
   280  		resources: map[string]charmresource.Meta{
   281  			"res1": {
   282  				Name: "res1",
   283  				Type: charmresource.TypeFile,
   284  				Path: "path",
   285  			},
   286  		},
   287  		osOpen: deps.Open,
   288  		osStat: deps.Stat,
   289  	}
   290  
   291  	files := map[string]string{}
   292  	revisions := map[string]int{"some bad resource": 2}
   293  	_, err := du.upload(files, revisions)
   294  	c.Check(err, gc.ErrorMatches, `unrecognized resource "some bad resource"`)
   295  
   296  	s.stub.CheckNoCalls(c)
   297  }
   298  
   299  func (s DeploySuite) TestMissingResource(c *gc.C) {
   300  	deps := uploadDeps{s.stub, rsc{&bytes.Buffer{}}}
   301  	du := deployUploader{
   302  		applicationID: "mysql",
   303  		client:        deps,
   304  		resources: map[string]charmresource.Meta{
   305  			"res1": {
   306  				Name: "res1",
   307  				Type: charmresource.TypeFile,
   308  				Path: "path",
   309  			},
   310  		},
   311  		osOpen: deps.Open,
   312  		osStat: deps.Stat,
   313  	}
   314  
   315  	// set the error that will be returned by os.Stat
   316  	s.stub.SetErrors(os.ErrNotExist)
   317  
   318  	files := map[string]string{"res1": "foobar.txt"}
   319  	revisions := map[string]int{}
   320  	_, err := du.upload(files, revisions)
   321  	c.Check(err, gc.ErrorMatches, `file for resource "res1".*`)
   322  	c.Check(errors.Cause(err), jc.Satisfies, os.IsNotExist)
   323  }
   324  
   325  type uploadDeps struct {
   326  	stub           *testing.Stub
   327  	ReadSeekCloser ReadSeekCloser
   328  }
   329  
   330  func (s uploadDeps) AddPendingResources(applicationID string, charmID charmstore.CharmID, csMac *macaroon.Macaroon, resources []charmresource.Resource) (ids []string, err error) {
   331  	charmresource.Sort(resources)
   332  	s.stub.AddCall("AddPendingResources", applicationID, charmID, csMac, resources)
   333  	if err := s.stub.NextErr(); err != nil {
   334  		return nil, err
   335  	}
   336  	ids = make([]string, len(resources))
   337  	for i, res := range resources {
   338  		ids[i] = "id-" + res.Name
   339  	}
   340  	return ids, nil
   341  }
   342  
   343  func (s uploadDeps) AddPendingResource(applicationID string, resource charmresource.Resource, filename string, r io.ReadSeeker) (id string, err error) {
   344  	s.stub.AddCall("AddPendingResource", applicationID, resource, filename, r)
   345  	if err := s.stub.NextErr(); err != nil {
   346  		return "", err
   347  	}
   348  	return "id-" + resource.Name, nil
   349  }
   350  
   351  func (s uploadDeps) Open(name string) (ReadSeekCloser, error) {
   352  	s.stub.AddCall("Open", name)
   353  	if err := s.stub.NextErr(); err != nil {
   354  		return nil, err
   355  	}
   356  	return s.ReadSeekCloser, nil
   357  }
   358  
   359  func (s uploadDeps) Stat(name string) error {
   360  	s.stub.AddCall("Stat", name)
   361  	return s.stub.NextErr()
   362  }
   363  
   364  type rsc struct {
   365  	*bytes.Buffer
   366  }
   367  
   368  func (rsc) Close() error {
   369  	return nil
   370  }
   371  func (rsc) Seek(offset int64, whence int) (int64, error) {
   372  	return 0, nil
   373  }