github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/resource/state/resource_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state
     5  
     6  import (
     7  	"bytes"
     8  	"io"
     9  	"io/ioutil"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/juju/errors"
    14  	"github.com/juju/names"
    15  	"github.com/juju/testing"
    16  	jc "github.com/juju/testing/checkers"
    17  	gc "gopkg.in/check.v1"
    18  	charmresource "gopkg.in/juju/charm.v6-unstable/resource"
    19  	"gopkg.in/mgo.v2/txn"
    20  
    21  	"github.com/juju/juju/resource"
    22  	"github.com/juju/juju/resource/resourcetesting"
    23  )
    24  
    25  var _ = gc.Suite(&ResourceSuite{})
    26  
    27  type ResourceSuite struct {
    28  	testing.IsolationSuite
    29  
    30  	stub      *testing.Stub
    31  	raw       *stubRawState
    32  	persist   *stubPersistence
    33  	storage   *stubStorage
    34  	timestamp time.Time
    35  	pendingID string
    36  }
    37  
    38  func (s *ResourceSuite) SetUpTest(c *gc.C) {
    39  	s.IsolationSuite.SetUpTest(c)
    40  
    41  	s.stub = &testing.Stub{}
    42  	s.raw = &stubRawState{stub: s.stub}
    43  	s.persist = &stubPersistence{stub: s.stub}
    44  	s.persist.ReturnStageResource = &stubStagedResource{stub: s.stub}
    45  	s.storage = &stubStorage{stub: s.stub}
    46  	s.raw.ReturnPersistence = s.persist
    47  	s.raw.ReturnStorage = s.storage
    48  	s.timestamp = time.Now().UTC()
    49  	s.pendingID = ""
    50  }
    51  
    52  func (s *ResourceSuite) now() time.Time {
    53  	s.stub.AddCall("currentTimestamp")
    54  	s.stub.NextErr() // Pop one off.
    55  
    56  	return s.timestamp
    57  }
    58  
    59  func (s *ResourceSuite) newPendingID() (string, error) {
    60  	s.stub.AddCall("newPendingID")
    61  	if err := s.stub.NextErr(); err != nil {
    62  		return "", errors.Trace(err)
    63  	}
    64  
    65  	return s.pendingID, nil
    66  }
    67  
    68  func (s *ResourceSuite) TestListResourcesOkay(c *gc.C) {
    69  	expected := newUploadResources(c, "spam", "eggs")
    70  	s.persist.ReturnListResources = resource.ServiceResources{Resources: expected}
    71  	tag := names.NewUnitTag("a-service/0")
    72  	s.raw.ReturnUnits = []names.UnitTag{tag}
    73  	st := NewState(s.raw)
    74  	s.stub.ResetCalls()
    75  
    76  	resources, err := st.ListResources("a-service")
    77  	c.Assert(err, jc.ErrorIsNil)
    78  
    79  	c.Check(resources.Resources, jc.DeepEquals, expected)
    80  	c.Check(resources.UnitResources, jc.DeepEquals, []resource.UnitResources{{
    81  		Tag: tag,
    82  	}})
    83  	s.stub.CheckCallNames(c, "ListResources", "Units")
    84  	s.stub.CheckCall(c, 0, "ListResources", "a-service")
    85  }
    86  
    87  func (s *ResourceSuite) TestListResourcesNoUnits(c *gc.C) {
    88  	expected := newUploadResources(c, "spam", "eggs")
    89  	s.persist.ReturnListResources = resource.ServiceResources{Resources: expected}
    90  	st := NewState(s.raw)
    91  	s.stub.ResetCalls()
    92  
    93  	resources, err := st.ListResources("a-service")
    94  	c.Assert(err, jc.ErrorIsNil)
    95  
    96  	c.Check(resources.Resources, jc.DeepEquals, expected)
    97  	c.Check(resources.UnitResources, gc.HasLen, 0)
    98  	s.stub.CheckCallNames(c, "ListResources", "Units")
    99  	s.stub.CheckCall(c, 0, "ListResources", "a-service")
   100  }
   101  
   102  func (s *ResourceSuite) TestListResourcesEmpty(c *gc.C) {
   103  	s.raw.ReturnUnits = []names.UnitTag{
   104  		names.NewUnitTag("a-service/0"),
   105  	}
   106  	st := NewState(s.raw)
   107  	s.stub.ResetCalls()
   108  
   109  	resources, err := st.ListResources("a-service")
   110  	c.Assert(err, jc.ErrorIsNil)
   111  
   112  	c.Check(resources.Resources, gc.HasLen, 0)
   113  	c.Check(resources.UnitResources, gc.HasLen, 1)
   114  	s.stub.CheckCallNames(c, "ListResources", "Units")
   115  }
   116  
   117  func (s *ResourceSuite) TestListResourcesError(c *gc.C) {
   118  	expected := newUploadResources(c, "spam", "eggs")
   119  	s.persist.ReturnListResources = resource.ServiceResources{Resources: expected}
   120  	st := NewState(s.raw)
   121  	s.stub.ResetCalls()
   122  	failure := errors.New("<failure>")
   123  	s.stub.SetErrors(failure)
   124  
   125  	_, err := st.ListResources("a-service")
   126  
   127  	c.Check(errors.Cause(err), gc.Equals, failure)
   128  	s.stub.CheckCallNames(c, "ListResources", "VerifyService")
   129  }
   130  
   131  func (s *ResourceSuite) TestGetPendingResource(c *gc.C) {
   132  	resources := newUploadResources(c, "spam", "eggs")
   133  	resources[0].PendingID = "some-unique-id"
   134  	resources[1].PendingID = "other-unique-id"
   135  	s.persist.ReturnListPendingResources = resources
   136  	st := NewState(s.raw)
   137  	s.stub.ResetCalls()
   138  
   139  	res, err := st.GetPendingResource("a-service", "eggs", "other-unique-id")
   140  	c.Assert(err, jc.ErrorIsNil)
   141  
   142  	s.stub.CheckCallNames(c, "ListPendingResources")
   143  	s.stub.CheckCall(c, 0, "ListPendingResources", "a-service")
   144  	c.Check(res, jc.DeepEquals, resources[1])
   145  }
   146  
   147  func (s *ResourceSuite) TestSetResourceOkay(c *gc.C) {
   148  	expected := newUploadResource(c, "spam", "spamspamspam")
   149  	expected.Timestamp = s.timestamp
   150  	chRes := expected.Resource
   151  	hash := chRes.Fingerprint.String()
   152  	path := "service-a-service/resources/spam"
   153  	file := &stubReader{stub: s.stub}
   154  	st := NewState(s.raw)
   155  	st.currentTimestamp = s.now
   156  	s.stub.ResetCalls()
   157  
   158  	res, err := st.SetResource("a-service", "a-user", chRes, file)
   159  	c.Assert(err, jc.ErrorIsNil)
   160  
   161  	s.stub.CheckCallNames(c,
   162  		"currentTimestamp",
   163  		"StageResource",
   164  		"PutAndCheckHash",
   165  		"Activate",
   166  	)
   167  	s.stub.CheckCall(c, 1, "StageResource", expected, path)
   168  	s.stub.CheckCall(c, 2, "PutAndCheckHash", path, file, res.Size, hash)
   169  	c.Check(res, jc.DeepEquals, resource.Resource{
   170  		Resource:  chRes,
   171  		ID:        "a-service/" + res.Name,
   172  		ServiceID: "a-service",
   173  		Username:  "a-user",
   174  		Timestamp: s.timestamp,
   175  	})
   176  }
   177  
   178  func (s *ResourceSuite) TestSetResourceInfoOnly(c *gc.C) {
   179  	expected := newUploadResource(c, "spam", "spamspamspam")
   180  	expected.Timestamp = time.Time{}
   181  	expected.Username = ""
   182  	chRes := expected.Resource
   183  	st := NewState(s.raw)
   184  	st.currentTimestamp = s.now
   185  	s.stub.ResetCalls()
   186  
   187  	res, err := st.SetResource("a-service", "a-user", chRes, nil)
   188  	c.Assert(err, jc.ErrorIsNil)
   189  
   190  	s.stub.CheckCallNames(c,
   191  		"SetResource",
   192  	)
   193  	s.stub.CheckCall(c, 0, "SetResource", expected)
   194  	c.Check(res, jc.DeepEquals, resource.Resource{
   195  		Resource:  chRes,
   196  		ID:        "a-service/" + res.Name,
   197  		ServiceID: "a-service",
   198  	})
   199  }
   200  
   201  func (s *ResourceSuite) TestSetResourceBadResource(c *gc.C) {
   202  	res := newUploadResource(c, "spam", "spamspamspam")
   203  	res.Fingerprint = charmresource.Fingerprint{}
   204  	file := &stubReader{stub: s.stub}
   205  	st := NewState(s.raw)
   206  	st.currentTimestamp = s.now
   207  	s.stub.ResetCalls()
   208  
   209  	_, err := st.SetResource("a-service", "a-user", res.Resource, file)
   210  
   211  	c.Check(err, jc.Satisfies, errors.IsNotValid)
   212  	c.Check(err, gc.ErrorMatches, `bad resource metadata.*`)
   213  	s.stub.CheckCallNames(c, "currentTimestamp")
   214  }
   215  
   216  func (s *ResourceSuite) TestSetResourceStagingFailure(c *gc.C) {
   217  	expected := newUploadResource(c, "spam", "spamspamspam")
   218  	expected.Timestamp = s.timestamp
   219  	path := "service-a-service/resources/spam"
   220  	file := &stubReader{stub: s.stub}
   221  	st := NewState(s.raw)
   222  	st.currentTimestamp = s.now
   223  	s.stub.ResetCalls()
   224  	failure := errors.New("<failure>")
   225  	ignoredErr := errors.New("<never reached>")
   226  	s.stub.SetErrors(nil, failure, ignoredErr)
   227  
   228  	_, err := st.SetResource("a-service", "a-user", expected.Resource, file)
   229  
   230  	c.Check(errors.Cause(err), gc.Equals, failure)
   231  	s.stub.CheckCallNames(c, "currentTimestamp", "StageResource")
   232  	s.stub.CheckCall(c, 1, "StageResource", expected, path)
   233  }
   234  
   235  func (s *ResourceSuite) TestSetResourcePutFailureBasic(c *gc.C) {
   236  	expected := newUploadResource(c, "spam", "spamspamspam")
   237  	expected.Timestamp = s.timestamp
   238  	hash := expected.Fingerprint.String()
   239  	path := "service-a-service/resources/spam"
   240  	file := &stubReader{stub: s.stub}
   241  	st := NewState(s.raw)
   242  	st.currentTimestamp = s.now
   243  	s.stub.ResetCalls()
   244  	failure := errors.New("<failure>")
   245  	ignoredErr := errors.New("<never reached>")
   246  	s.stub.SetErrors(nil, nil, failure, nil, ignoredErr)
   247  
   248  	_, err := st.SetResource("a-service", "a-user", expected.Resource, file)
   249  
   250  	c.Check(errors.Cause(err), gc.Equals, failure)
   251  	s.stub.CheckCallNames(c,
   252  		"currentTimestamp",
   253  		"StageResource",
   254  		"PutAndCheckHash",
   255  		"Unstage",
   256  	)
   257  	s.stub.CheckCall(c, 1, "StageResource", expected, path)
   258  	s.stub.CheckCall(c, 2, "PutAndCheckHash", path, file, expected.Size, hash)
   259  }
   260  
   261  func (s *ResourceSuite) TestSetResourcePutFailureExtra(c *gc.C) {
   262  	expected := newUploadResource(c, "spam", "spamspamspam")
   263  	expected.Timestamp = s.timestamp
   264  	hash := expected.Fingerprint.String()
   265  	path := "service-a-service/resources/spam"
   266  	file := &stubReader{stub: s.stub}
   267  	st := NewState(s.raw)
   268  	st.currentTimestamp = s.now
   269  	s.stub.ResetCalls()
   270  	failure := errors.New("<failure>")
   271  	extraErr := errors.New("<just not your day>")
   272  	ignoredErr := errors.New("<never reached>")
   273  	s.stub.SetErrors(nil, nil, failure, extraErr, ignoredErr)
   274  
   275  	_, err := st.SetResource("a-service", "a-user", expected.Resource, file)
   276  
   277  	c.Check(errors.Cause(err), gc.Equals, failure)
   278  	s.stub.CheckCallNames(c,
   279  		"currentTimestamp",
   280  		"StageResource",
   281  		"PutAndCheckHash",
   282  		"Unstage",
   283  	)
   284  	s.stub.CheckCall(c, 1, "StageResource", expected, path)
   285  	s.stub.CheckCall(c, 2, "PutAndCheckHash", path, file, expected.Size, hash)
   286  }
   287  
   288  func (s *ResourceSuite) TestSetResourceSetFailureBasic(c *gc.C) {
   289  	expected := newUploadResource(c, "spam", "spamspamspam")
   290  	expected.Timestamp = s.timestamp
   291  	hash := expected.Fingerprint.String()
   292  	path := "service-a-service/resources/spam"
   293  	file := &stubReader{stub: s.stub}
   294  	st := NewState(s.raw)
   295  	st.currentTimestamp = s.now
   296  	s.stub.ResetCalls()
   297  	failure := errors.New("<failure>")
   298  	ignoredErr := errors.New("<never reached>")
   299  	s.stub.SetErrors(nil, nil, nil, failure, nil, nil, ignoredErr)
   300  
   301  	_, err := st.SetResource("a-service", "a-user", expected.Resource, file)
   302  
   303  	c.Check(errors.Cause(err), gc.Equals, failure)
   304  	s.stub.CheckCallNames(c,
   305  		"currentTimestamp",
   306  		"StageResource",
   307  		"PutAndCheckHash",
   308  		"Activate",
   309  		"Remove",
   310  		"Unstage",
   311  	)
   312  	s.stub.CheckCall(c, 1, "StageResource", expected, path)
   313  	s.stub.CheckCall(c, 2, "PutAndCheckHash", path, file, expected.Size, hash)
   314  	s.stub.CheckCall(c, 4, "Remove", path)
   315  }
   316  
   317  func (s *ResourceSuite) TestSetResourceSetFailureExtra(c *gc.C) {
   318  	expected := newUploadResource(c, "spam", "spamspamspam")
   319  	expected.Timestamp = s.timestamp
   320  	hash := expected.Fingerprint.String()
   321  	path := "service-a-service/resources/spam"
   322  	file := &stubReader{stub: s.stub}
   323  	st := NewState(s.raw)
   324  	st.currentTimestamp = s.now
   325  	s.stub.ResetCalls()
   326  	failure := errors.New("<failure>")
   327  	extraErr1 := errors.New("<just not your day>")
   328  	extraErr2 := errors.New("<wow...just wow>")
   329  	ignoredErr := errors.New("<never reached>")
   330  	s.stub.SetErrors(nil, nil, nil, failure, extraErr1, extraErr2, ignoredErr)
   331  
   332  	_, err := st.SetResource("a-service", "a-user", expected.Resource, file)
   333  
   334  	c.Check(errors.Cause(err), gc.Equals, failure)
   335  	s.stub.CheckCallNames(c,
   336  		"currentTimestamp",
   337  		"StageResource",
   338  		"PutAndCheckHash",
   339  		"Activate",
   340  		"Remove",
   341  		"Unstage",
   342  	)
   343  	s.stub.CheckCall(c, 1, "StageResource", expected, path)
   344  	s.stub.CheckCall(c, 2, "PutAndCheckHash", path, file, expected.Size, hash)
   345  	s.stub.CheckCall(c, 4, "Remove", path)
   346  }
   347  
   348  func (s *ResourceSuite) TestUpdatePendingResourceOkay(c *gc.C) {
   349  	expected := newUploadResource(c, "spam", "spamspamspam")
   350  	expected.PendingID = "some-unique-id"
   351  	expected.Timestamp = s.timestamp
   352  	chRes := expected.Resource
   353  	hash := chRes.Fingerprint.String()
   354  	path := "service-a-service/resources/spam-some-unique-id"
   355  	file := &stubReader{stub: s.stub}
   356  	st := NewState(s.raw)
   357  	st.currentTimestamp = s.now
   358  	s.stub.ResetCalls()
   359  
   360  	res, err := st.UpdatePendingResource("a-service", "some-unique-id", "a-user", chRes, file)
   361  	c.Assert(err, jc.ErrorIsNil)
   362  
   363  	s.stub.CheckCallNames(c,
   364  		"currentTimestamp",
   365  		"StageResource",
   366  		"PutAndCheckHash",
   367  		"Activate",
   368  	)
   369  	s.stub.CheckCall(c, 1, "StageResource", expected, path)
   370  	s.stub.CheckCall(c, 2, "PutAndCheckHash", path, file, res.Size, hash)
   371  	c.Check(res, jc.DeepEquals, resource.Resource{
   372  		Resource:  chRes,
   373  		ID:        "a-service/" + res.Name,
   374  		ServiceID: "a-service",
   375  		PendingID: "some-unique-id",
   376  		Username:  "a-user",
   377  		Timestamp: s.timestamp,
   378  	})
   379  }
   380  
   381  func (s *ResourceSuite) TestAddPendingResourceOkay(c *gc.C) {
   382  	s.pendingID = "some-unique-ID-001"
   383  	expected := newUploadResource(c, "spam", "spamspamspam")
   384  	expected.PendingID = s.pendingID
   385  	expected.Timestamp = s.timestamp
   386  	chRes := expected.Resource
   387  	hash := chRes.Fingerprint.String()
   388  	path := "service-a-service/resources/spam-some-unique-ID-001"
   389  	file := &stubReader{stub: s.stub}
   390  	st := NewState(s.raw)
   391  	st.currentTimestamp = s.now
   392  	st.newPendingID = s.newPendingID
   393  	s.stub.ResetCalls()
   394  
   395  	pendingID, err := st.AddPendingResource("a-service", "a-user", chRes, file)
   396  	c.Assert(err, jc.ErrorIsNil)
   397  
   398  	s.stub.CheckCallNames(c,
   399  		"newPendingID",
   400  		"currentTimestamp",
   401  		"StageResource",
   402  		"PutAndCheckHash",
   403  		"Activate",
   404  	)
   405  	s.stub.CheckCall(c, 2, "StageResource", expected, path)
   406  	s.stub.CheckCall(c, 3, "PutAndCheckHash", path, file, expected.Size, hash)
   407  	c.Check(pendingID, gc.Equals, s.pendingID)
   408  }
   409  
   410  func (s *ResourceSuite) TestOpenResourceOkay(c *gc.C) {
   411  	data := "some data"
   412  	opened := resourcetesting.NewResource(c, s.stub, "spam", "a-service", data)
   413  	s.persist.ReturnGetResource = opened.Resource
   414  	s.persist.ReturnGetResourcePath = "service-a-service/resources/spam"
   415  	s.storage.ReturnGet = opened.Content()
   416  	st := NewState(s.raw)
   417  	s.stub.ResetCalls()
   418  
   419  	info, reader, err := st.OpenResource("a-service", "spam")
   420  	c.Assert(err, jc.ErrorIsNil)
   421  
   422  	s.stub.CheckCallNames(c, "GetResource", "Get")
   423  	s.stub.CheckCall(c, 1, "Get", "service-a-service/resources/spam")
   424  	c.Check(info, jc.DeepEquals, opened.Resource)
   425  	c.Check(reader, gc.Equals, opened.ReadCloser)
   426  }
   427  
   428  func (s *ResourceSuite) TestOpenResourceNotFound(c *gc.C) {
   429  	st := NewState(s.raw)
   430  	s.stub.ResetCalls()
   431  	errNotFound := errors.NotFoundf("resource")
   432  	s.stub.SetErrors(errNotFound)
   433  
   434  	_, _, err := st.OpenResource("a-service", "spam")
   435  
   436  	s.stub.CheckCallNames(c, "GetResource", "VerifyService")
   437  	c.Check(err, jc.Satisfies, errors.IsNotFound)
   438  }
   439  
   440  func (s *ResourceSuite) TestOpenResourcePlaceholder(c *gc.C) {
   441  	res := resourcetesting.NewPlaceholderResource(c, "spam", "a-service")
   442  	s.persist.ReturnGetResource = res
   443  	s.persist.ReturnGetResourcePath = "service-a-service/resources/spam"
   444  	st := NewState(s.raw)
   445  	s.stub.ResetCalls()
   446  
   447  	_, _, err := st.OpenResource("a-service", "spam")
   448  
   449  	s.stub.CheckCallNames(c, "GetResource")
   450  	c.Check(err, jc.Satisfies, errors.IsNotFound)
   451  }
   452  
   453  func (s *ResourceSuite) TestOpenResourceSizeMismatch(c *gc.C) {
   454  	opened := resourcetesting.NewResource(c, s.stub, "spam", "a-service", "some data")
   455  	s.persist.ReturnGetResource = opened.Resource
   456  	s.persist.ReturnGetResourcePath = "service-a-service/resources/spam"
   457  	content := opened.Content()
   458  	content.Size += 1
   459  	s.storage.ReturnGet = content
   460  	st := NewState(s.raw)
   461  	s.stub.ResetCalls()
   462  
   463  	_, _, err := st.OpenResource("a-service", "spam")
   464  
   465  	s.stub.CheckCallNames(c, "GetResource", "Get")
   466  	c.Check(err, gc.ErrorMatches, `storage returned a size \(10\) which doesn't match resource metadata \(9\)`)
   467  }
   468  
   469  func (s *ResourceSuite) TestOpenResourceForUniterOkay(c *gc.C) {
   470  	data := "some data"
   471  	opened := resourcetesting.NewResource(c, s.stub, "spam", "a-service", data)
   472  	s.persist.ReturnGetResource = opened.Resource
   473  	s.persist.ReturnGetResourcePath = "service-a-service/resources/spam"
   474  	s.storage.ReturnGet = opened.Content()
   475  	unit := newUnit(s.stub, "a-service/0")
   476  	st := NewState(s.raw)
   477  	s.stub.ResetCalls()
   478  
   479  	info, reader, err := st.OpenResourceForUniter(unit, "spam")
   480  	c.Assert(err, jc.ErrorIsNil)
   481  
   482  	s.stub.CheckCallNames(c, "ServiceName", "GetResource", "Get", "Name", "SetUnitResourceProgress")
   483  	s.stub.CheckCall(c, 2, "Get", "service-a-service/resources/spam")
   484  	c.Check(info, jc.DeepEquals, opened.Resource)
   485  
   486  	b, err := ioutil.ReadAll(reader)
   487  	// note ioutil.ReadAll converts EOF to nil
   488  	c.Check(err, jc.ErrorIsNil)
   489  	c.Check(b, gc.DeepEquals, []byte(data))
   490  }
   491  
   492  func (s *ResourceSuite) TestOpenResourceForUniterNotFound(c *gc.C) {
   493  	unit := newUnit(s.stub, "a-service/0")
   494  	st := NewState(s.raw)
   495  	s.stub.ResetCalls()
   496  	errNotFound := errors.NotFoundf("resource")
   497  	s.stub.SetErrors(nil, errNotFound)
   498  
   499  	_, _, err := st.OpenResourceForUniter(unit, "spam")
   500  
   501  	s.stub.CheckCallNames(c, "ServiceName", "GetResource", "VerifyService")
   502  	c.Check(err, jc.Satisfies, errors.IsNotFound)
   503  }
   504  
   505  func (s *ResourceSuite) TestOpenResourceForUniterPlaceholder(c *gc.C) {
   506  	res := resourcetesting.NewPlaceholderResource(c, "spam", "a-service")
   507  	s.persist.ReturnGetResource = res
   508  	s.persist.ReturnGetResourcePath = "service-a-service/resources/spam"
   509  	unit := newUnit(s.stub, "a-service/0")
   510  	st := NewState(s.raw)
   511  	s.stub.ResetCalls()
   512  
   513  	_, _, err := st.OpenResourceForUniter(unit, "spam")
   514  
   515  	s.stub.CheckCallNames(c, "ServiceName", "GetResource")
   516  	c.Check(err, jc.Satisfies, errors.IsNotFound)
   517  }
   518  
   519  func (s *ResourceSuite) TestOpenResourceForUniterSizeMismatch(c *gc.C) {
   520  	opened := resourcetesting.NewResource(c, s.stub, "spam", "a-service", "some data")
   521  	s.persist.ReturnGetResource = opened.Resource
   522  	s.persist.ReturnGetResourcePath = "service-a-service/resources/spam"
   523  	content := opened.Content()
   524  	content.Size += 1
   525  	s.storage.ReturnGet = content
   526  	unit := newUnit(s.stub, "a-service/0")
   527  	st := NewState(s.raw)
   528  	s.stub.ResetCalls()
   529  
   530  	_, _, err := st.OpenResourceForUniter(unit, "spam")
   531  
   532  	s.stub.CheckCallNames(c, "ServiceName", "GetResource", "Get")
   533  	c.Check(err, gc.ErrorMatches, `storage returned a size \(10\) which doesn't match resource metadata \(9\)`)
   534  }
   535  
   536  func (s *ResourceSuite) TestSetCharmStoreResources(c *gc.C) {
   537  	lastPolled := time.Now().UTC()
   538  	resources := newStoreResources(c, "spam", "eggs")
   539  	var info []charmresource.Resource
   540  	for _, res := range resources {
   541  		chRes := res.Resource
   542  		info = append(info, chRes)
   543  	}
   544  	st := NewState(s.raw)
   545  	s.stub.ResetCalls()
   546  
   547  	err := st.SetCharmStoreResources("a-service", info, lastPolled)
   548  	c.Assert(err, jc.ErrorIsNil)
   549  
   550  	s.stub.CheckCallNames(c,
   551  		"SetCharmStoreResource",
   552  		"SetCharmStoreResource",
   553  	)
   554  	s.stub.CheckCall(c, 0, "SetCharmStoreResource", "a-service/spam", "a-service", info[0], lastPolled)
   555  	s.stub.CheckCall(c, 1, "SetCharmStoreResource", "a-service/eggs", "a-service", info[1], lastPolled)
   556  }
   557  
   558  func (s *ResourceSuite) TestNewResourcePendingResourcesOps(c *gc.C) {
   559  	doc1 := map[string]string{"a": "1"}
   560  	doc2 := map[string]string{"b": "2"}
   561  	expected := []txn.Op{{
   562  		C:      "resources",
   563  		Id:     "resource#a-service/spam#pending-some-unique-ID-001",
   564  		Assert: txn.DocExists,
   565  		Remove: true,
   566  	}, {
   567  		C:      "resources",
   568  		Id:     "resource#a-service/spam",
   569  		Assert: txn.DocMissing,
   570  		Insert: &doc1,
   571  	}, {
   572  		C:      "resources",
   573  		Id:     "resource#a-service/spam#pending-some-unique-ID-001",
   574  		Assert: txn.DocExists,
   575  		Remove: true,
   576  	}, {
   577  		C:      "resources",
   578  		Id:     "resource#a-service/spam",
   579  		Assert: txn.DocMissing,
   580  		Insert: &doc2,
   581  	}}
   582  	s.persist.ReturnNewResolvePendingResourceOps = [][]txn.Op{
   583  		expected[:2],
   584  		expected[2:],
   585  	}
   586  	serviceID := "a-service"
   587  	st := NewState(s.raw)
   588  	s.stub.ResetCalls()
   589  	pendingIDs := map[string]string{
   590  		"spam": "some-unique-id",
   591  		"eggs": "other-unique-id",
   592  	}
   593  
   594  	ops, err := st.NewResolvePendingResourcesOps(serviceID, pendingIDs)
   595  	c.Assert(err, jc.ErrorIsNil)
   596  
   597  	s.stub.CheckCallNames(c,
   598  		"NewResolvePendingResourceOps",
   599  		"NewResolvePendingResourceOps",
   600  	)
   601  	c.Check(s.persist.CallsForNewResolvePendingResourceOps, jc.DeepEquals, map[string]string{
   602  		"a-service/spam": "some-unique-id",
   603  		"a-service/eggs": "other-unique-id",
   604  	})
   605  	c.Check(ops, jc.DeepEquals, expected)
   606  }
   607  
   608  func (s *ResourceSuite) TestUnitSetterEOF(c *gc.C) {
   609  	r := unitSetter{
   610  		ReadCloser: ioutil.NopCloser(&bytes.Buffer{}),
   611  		persist:    &stubPersistence{stub: s.stub},
   612  		unit:       newUnit(s.stub, "some-service/0"),
   613  		resource:   newUploadResource(c, "res", "res"),
   614  	}
   615  	// have to try to read non-zero data, or bytes.buffer will happily return
   616  	// nil.
   617  	p := make([]byte, 5)
   618  	n, err := r.Read(p)
   619  	c.Assert(n, gc.Equals, 0)
   620  	c.Assert(err, gc.Equals, io.EOF)
   621  
   622  	s.stub.CheckCallNames(c, "Name", "SetUnitResource")
   623  	s.stub.CheckCall(c, 1, "SetUnitResource", "some-service/0", r.resource)
   624  }
   625  
   626  func (s *ResourceSuite) TestUnitSetterNoEOF(c *gc.C) {
   627  	r := unitSetter{
   628  		ReadCloser: ioutil.NopCloser(bytes.NewBufferString("foobar")),
   629  		persist:    &stubPersistence{stub: s.stub},
   630  		unit:       newUnit(s.stub, "some-service/0"),
   631  		resource:   newUploadResource(c, "res", "res"),
   632  	}
   633  	// read less than the full buffer
   634  	p := make([]byte, 3)
   635  	n, err := r.Read(p)
   636  	c.Assert(n, gc.Equals, 3)
   637  	c.Assert(err, gc.Equals, nil)
   638  
   639  	// Assert that we don't call SetUnitResource if we read but don't reach the
   640  	// end of the buffer.
   641  	s.stub.CheckCallNames(c, "Name", "SetUnitResourceProgress")
   642  }
   643  
   644  func (s *ResourceSuite) TestUnitSetterSetUnitErr(c *gc.C) {
   645  	r := unitSetter{
   646  		ReadCloser: ioutil.NopCloser(&bytes.Buffer{}),
   647  		persist:    &stubPersistence{stub: s.stub},
   648  		unit:       newUnit(s.stub, "some-service/0"),
   649  		resource:   newUploadResource(c, "res", "res"),
   650  	}
   651  
   652  	s.stub.SetErrors(errors.Errorf("oops!"))
   653  	// have to try to read non-zero data, or bytes.buffer will happily return
   654  	// nil.
   655  	p := make([]byte, 5)
   656  	n, err := r.Read(p)
   657  	c.Assert(n, gc.Equals, 0)
   658  
   659  	// ensure that we return the EOF from bytes.buffer and not the error from SetUnitResource.
   660  	c.Assert(err, gc.Equals, io.EOF)
   661  
   662  	s.stub.CheckCallNames(c, "Name", "SetUnitResource")
   663  	s.stub.CheckCall(c, 1, "SetUnitResource", "some-service/0", r.resource)
   664  }
   665  
   666  func (s *ResourceSuite) TestUnitSetterErr(c *gc.C) {
   667  	r := unitSetter{
   668  		ReadCloser: ioutil.NopCloser(&stubReader{stub: s.stub}),
   669  		persist:    &stubPersistence{stub: s.stub},
   670  		unit:       newUnit(s.stub, "some-service/0"),
   671  		resource:   newUploadResource(c, "res", "res"),
   672  	}
   673  	expected := errors.Errorf("some-err")
   674  	s.stub.SetErrors(expected)
   675  	// have to try to read non-zero data, or bytes.buffer will happily return
   676  	// nil.
   677  	p := make([]byte, 5)
   678  	n, err := r.Read(p)
   679  	c.Assert(n, gc.Equals, 0)
   680  	c.Assert(err, gc.Equals, expected)
   681  
   682  	s.stub.CheckCall(c, 0, "Read", p)
   683  }
   684  
   685  func newUploadResources(c *gc.C, names ...string) []resource.Resource {
   686  	var resources []resource.Resource
   687  	for _, name := range names {
   688  		res := newUploadResource(c, name, name)
   689  		resources = append(resources, res)
   690  	}
   691  	return resources
   692  }
   693  
   694  func newUploadResource(c *gc.C, name, data string) resource.Resource {
   695  	opened := resourcetesting.NewResource(c, nil, name, "a-service", data)
   696  	return opened.Resource
   697  }
   698  
   699  func newStoreResources(c *gc.C, names ...string) []resource.Resource {
   700  	var resources []resource.Resource
   701  	for _, name := range names {
   702  		res := newStoreResource(c, name, name)
   703  		resources = append(resources, res)
   704  	}
   705  	return resources
   706  }
   707  
   708  func newStoreResource(c *gc.C, name, data string) resource.Resource {
   709  	opened := resourcetesting.NewResource(c, nil, name, "a-service", data)
   710  	res := opened.Resource
   711  	res.Origin = charmresource.OriginStore
   712  	res.Revision = 1
   713  	res.Username = ""
   714  	res.Timestamp = time.Time{}
   715  	return res
   716  }
   717  
   718  func newCharmResource(c *gc.C, name, data string, rev int) charmresource.Resource {
   719  	opened := resourcetesting.NewResource(c, nil, name, "a-service", data)
   720  	chRes := opened.Resource.Resource
   721  	chRes.Origin = charmresource.OriginStore
   722  	chRes.Revision = rev
   723  	return chRes
   724  }
   725  
   726  func newUnit(stub *testing.Stub, name string) *resourcetesting.StubUnit {
   727  	return &resourcetesting.StubUnit{
   728  		Stub:              stub,
   729  		ReturnName:        name,
   730  		ReturnServiceName: strings.Split(name, "/")[0],
   731  	}
   732  }