1
// Copyright 2015 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
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"
19
"github.com/juju/juju/charmstore"
22
type DeploySuite struct {
23
testing.IsolationSuite
28
var _ = gc.Suite(&DeploySuite{})
30
func (s *DeploySuite) SetUpTest(c *gc.C) {
31
s.IsolationSuite.SetUpTest(c)
33
s.stub = &testing.Stub{}
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{
42
csMac := &macaroon.Macaroon{}
43
resources := map[string]charmresource.Meta{
45
Name: "store-tarball",
46
Type: charmresource.TypeFile,
51
Type: charmresource.TypeFile,
56
ids, err := DeployResources(DeployResourcesArgs{
57
ApplicationID: "mysql",
59
CharmStoreMacaroon: csMac,
62
ResourcesMeta: resources,
64
c.Assert(err, jc.ErrorIsNil)
66
c.Check(ids, gc.DeepEquals, map[string]string{
67
"store-tarball": "id-store-tarball",
68
"store-zip": "id-store-zip",
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,
77
Meta: resources["store-zip"],
78
Origin: charmresource.OriginStore,
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{
89
csMac := &macaroon.Macaroon{}
91
applicationID: "mysql",
95
resources: map[string]charmresource.Meta{
98
Type: charmresource.TypeFile,
103
Type: charmresource.TypeFile,
111
files := map[string]string{
112
"upload": "foobar.txt",
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",
122
s.stub.CheckCallNames(c, "Stat", "AddPendingResources", "Open", "AddPendingResource")
123
expectedStore := []charmresource.Resource{
125
Meta: du.resources["store"],
126
Origin: charmresource.OriginStore,
130
s.stub.CheckCall(c, 1, "AddPendingResources", "mysql", chID, csMac, expectedStore)
131
s.stub.CheckCall(c, 2, "Open", "foobar.txt")
133
expectedUpload := charmresource.Resource{
134
Meta: du.resources["upload"],
135
Origin: charmresource.OriginUpload,
137
s.stub.CheckCall(c, 3, "AddPendingResource", "mysql", expectedUpload, "foobar.txt", deps.ReadSeekCloser)
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{
146
csMac := &macaroon.Macaroon{}
147
du := deployUploader{
148
applicationID: "mysql",
152
resources: map[string]charmresource.Meta{
155
Type: charmresource.TypeFile,
160
Type: charmresource.TypeFile,
168
files := map[string]string{}
169
revisions := map[string]int{
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",
179
s.stub.CheckCallNames(c, "AddPendingResources")
180
expectedStore := []charmresource.Resource{{
181
Meta: du.resources["store"],
182
Origin: charmresource.OriginStore,
185
Meta: du.resources["upload"],
186
Origin: charmresource.OriginStore,
189
s.stub.CheckCall(c, 0, "AddPendingResources", "mysql", chID, csMac, expectedStore)
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{
198
csMac := &macaroon.Macaroon{}
199
du := deployUploader{
200
applicationID: "mysql",
204
resources: map[string]charmresource.Meta{
207
Type: charmresource.TypeFile,
212
Type: charmresource.TypeFile,
220
files := map[string]string{
221
"upload": "foobar.txt",
223
revisions := map[string]int{
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",
233
s.stub.CheckCallNames(c, "Stat", "AddPendingResources", "Open", "AddPendingResource")
234
expectedStore := []charmresource.Resource{
236
Meta: du.resources["store"],
237
Origin: charmresource.OriginStore,
241
s.stub.CheckCall(c, 1, "AddPendingResources", "mysql", chID, csMac, expectedStore)
242
s.stub.CheckCall(c, 2, "Open", "foobar.txt")
244
expectedUpload := charmresource.Resource{
245
Meta: du.resources["upload"],
246
Origin: charmresource.OriginUpload,
248
s.stub.CheckCall(c, 3, "AddPendingResource", "mysql", expectedUpload, "foobar.txt", deps.ReadSeekCloser)
251
func (s DeploySuite) TestUploadUnexpectedResourceFile(c *gc.C) {
252
deps := uploadDeps{s.stub, rsc{&bytes.Buffer{}}}
253
du := deployUploader{
254
applicationID: "mysql",
256
resources: map[string]charmresource.Meta{
259
Type: charmresource.TypeFile,
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"`)
272
s.stub.CheckNoCalls(c)
275
func (s DeploySuite) TestUploadUnexpectedResourceRevision(c *gc.C) {
276
deps := uploadDeps{s.stub, rsc{&bytes.Buffer{}}}
277
du := deployUploader{
278
applicationID: "mysql",
280
resources: map[string]charmresource.Meta{
283
Type: charmresource.TypeFile,
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"`)
296
s.stub.CheckNoCalls(c)
299
func (s DeploySuite) TestMissingResource(c *gc.C) {
300
deps := uploadDeps{s.stub, rsc{&bytes.Buffer{}}}
301
du := deployUploader{
302
applicationID: "mysql",
304
resources: map[string]charmresource.Meta{
307
Type: charmresource.TypeFile,
315
// set the error that will be returned by os.Stat
316
s.stub.SetErrors(os.ErrNotExist)
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)
325
type uploadDeps struct {
327
ReadSeekCloser ReadSeekCloser
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 {
336
ids = make([]string, len(resources))
337
for i, res := range resources {
338
ids[i] = "id-" + res.Name
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 {
348
return "id-" + resource.Name, nil
351
func (s uploadDeps) Open(name string) (ReadSeekCloser, error) {
352
s.stub.AddCall("Open", name)
353
if err := s.stub.NextErr(); err != nil {
356
return s.ReadSeekCloser, nil
359
func (s uploadDeps) Stat(name string) error {
360
s.stub.AddCall("Stat", name)
361
return s.stub.NextErr()
368
func (rsc) Close() error {
371
func (rsc) Seek(offset int64, whence int) (int64, error) {