~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/cmd/juju/application/upgradecharm_resources_test.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2013 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
 
4
package application_test
 
5
 
 
6
import (
 
7
        "bytes"
 
8
        "io/ioutil"
 
9
        "net/http/httptest"
 
10
        "path"
 
11
        "sort"
 
12
        "strings"
 
13
        "time"
 
14
 
 
15
        jc "github.com/juju/testing/checkers"
 
16
        gc "gopkg.in/check.v1"
 
17
        "gopkg.in/juju/charm.v6-unstable"
 
18
        charmresource "gopkg.in/juju/charm.v6-unstable/resource"
 
19
        "gopkg.in/juju/charmrepo.v2-unstable"
 
20
        "gopkg.in/juju/charmrepo.v2-unstable/csclient"
 
21
        "gopkg.in/juju/charmstore.v5-unstable"
 
22
 
 
23
        "github.com/juju/juju/cmd/juju/application"
 
24
        "github.com/juju/juju/component/all"
 
25
        "github.com/juju/juju/constraints"
 
26
        jujutesting "github.com/juju/juju/juju/testing"
 
27
        "github.com/juju/juju/resource"
 
28
        "github.com/juju/juju/state"
 
29
        "github.com/juju/juju/testcharms"
 
30
        "github.com/juju/juju/testing"
 
31
)
 
32
 
 
33
type UpgradeCharmResourceSuite struct {
 
34
        jujutesting.RepoSuite
 
35
}
 
36
 
 
37
var _ = gc.Suite(&UpgradeCharmResourceSuite{})
 
38
 
 
39
func (s *UpgradeCharmResourceSuite) SetUpSuite(c *gc.C) {
 
40
        s.RepoSuite.SetUpSuite(c)
 
41
        all.RegisterForServer()
 
42
}
 
43
 
 
44
func (s *UpgradeCharmResourceSuite) SetUpTest(c *gc.C) {
 
45
        s.RepoSuite.SetUpTest(c)
 
46
        chPath := testcharms.Repo.ClonedDirPath(s.CharmsPath, "riak")
 
47
 
 
48
        _, err := testing.RunCommand(c, application.NewDeployCommand(), chPath, "riak", "--series", "quantal")
 
49
        c.Assert(err, jc.ErrorIsNil)
 
50
        riak, err := s.State.Application("riak")
 
51
        c.Assert(err, jc.ErrorIsNil)
 
52
        ch, forced, err := riak.Charm()
 
53
        c.Assert(err, jc.ErrorIsNil)
 
54
        c.Assert(ch.Revision(), gc.Equals, 7)
 
55
        c.Assert(forced, jc.IsFalse)
 
56
}
 
57
 
 
58
var riakResourceMeta = []byte(`
 
59
name: riakresource
 
60
summary: "K/V storage engine"
 
61
description: "Scalable K/V Store in Erlang with Clocks :-)"
 
62
provides:
 
63
  endpoint:
 
64
    interface: http
 
65
  admin:
 
66
    interface: http
 
67
peers:
 
68
  ring:
 
69
    interface: riak
 
70
resources:
 
71
  data:
 
72
    type: file
 
73
    filename: foo.lib
 
74
    description: some comment
 
75
`)
 
76
 
 
77
func (s *UpgradeCharmResourceSuite) TestUpgradeWithResources(c *gc.C) {
 
78
        myriakPath := testcharms.Repo.ClonedDir(c.MkDir(), "riak")
 
79
        err := ioutil.WriteFile(path.Join(myriakPath.Path, "metadata.yaml"), riakResourceMeta, 0644)
 
80
        c.Assert(err, jc.ErrorIsNil)
 
81
 
 
82
        data := []byte("some-data")
 
83
        fp, err := charmresource.GenerateFingerprint(bytes.NewReader(data))
 
84
        c.Assert(err, jc.ErrorIsNil)
 
85
 
 
86
        resourceFile := path.Join(c.MkDir(), "data.lib")
 
87
        err = ioutil.WriteFile(resourceFile, data, 0644)
 
88
        c.Assert(err, jc.ErrorIsNil)
 
89
 
 
90
        _, err = testing.RunCommand(c, application.NewUpgradeCharmCommand(),
 
91
                "riak", "--path="+myriakPath.Path, "--resource", "data="+resourceFile)
 
92
        c.Assert(err, jc.ErrorIsNil)
 
93
 
 
94
        resources, err := s.State.Resources()
 
95
        c.Assert(err, jc.ErrorIsNil)
 
96
 
 
97
        sr, err := resources.ListResources("riak")
 
98
        c.Assert(err, jc.ErrorIsNil)
 
99
 
 
100
        c.Check(sr.Resources, gc.HasLen, 1)
 
101
 
 
102
        c.Check(sr.Resources[0].ApplicationID, gc.Equals, "riak")
 
103
 
 
104
        // Most of this is just a sanity check... this is all tested elsewhere.
 
105
        c.Check(sr.Resources[0].PendingID, gc.Equals, "")
 
106
        c.Check(sr.Resources[0].Username, gc.Not(gc.Equals), "")
 
107
        c.Check(sr.Resources[0].ID, gc.Not(gc.Equals), "")
 
108
        c.Check(sr.Resources[0].Timestamp.IsZero(), jc.IsFalse)
 
109
 
 
110
        // Ensure we get the data we passed in from the metadata.yaml.
 
111
        c.Check(sr.Resources[0].Resource, gc.DeepEquals, charmresource.Resource{
 
112
                Meta: charmresource.Meta{
 
113
                        Name:        "data",
 
114
                        Type:        charmresource.TypeFile,
 
115
                        Path:        "foo.lib",
 
116
                        Description: "some comment",
 
117
                },
 
118
                Origin:      charmresource.OriginUpload,
 
119
                Fingerprint: fp,
 
120
                Size:        int64(len(data)),
 
121
        })
 
122
}
 
123
 
 
124
// charmStoreSuite is a suite fixture that puts the machinery in
 
125
// place to allow testing code that calls addCharmViaAPI.
 
126
type charmStoreSuite struct {
 
127
        jujutesting.JujuConnSuite
 
128
        handler charmstore.HTTPCloseHandler
 
129
        srv     *httptest.Server
 
130
        client  *csclient.Client
 
131
}
 
132
 
 
133
func (s *charmStoreSuite) SetUpTest(c *gc.C) {
 
134
        s.JujuConnSuite.SetUpTest(c)
 
135
 
 
136
        // Set up the charm store testing server.
 
137
        db := s.Session.DB("juju-testing")
 
138
        params := charmstore.ServerParams{
 
139
                AuthUsername: "test-user",
 
140
                AuthPassword: "test-password",
 
141
        }
 
142
        handler, err := charmstore.NewServer(db, nil, "", params, charmstore.V5)
 
143
        c.Assert(err, jc.ErrorIsNil)
 
144
        s.handler = handler
 
145
        s.srv = httptest.NewServer(handler)
 
146
        s.client = csclient.New(csclient.Params{
 
147
                URL:      s.srv.URL,
 
148
                User:     params.AuthUsername,
 
149
                Password: params.AuthPassword,
 
150
        })
 
151
 
 
152
        application.PatchNewCharmStoreClient(s, s.srv.URL)
 
153
 
 
154
        // Initialize the charm cache dir.
 
155
        s.PatchValue(&charmrepo.CacheDir, c.MkDir())
 
156
 
 
157
        // Point the CLI to the charm store testing server.
 
158
 
 
159
        // Point the Juju API server to the charm store testing server.
 
160
        s.PatchValue(&csclient.ServerURL, s.srv.URL)
 
161
}
 
162
 
 
163
func (s *charmStoreSuite) TearDownTest(c *gc.C) {
 
164
        s.handler.Close()
 
165
        s.srv.Close()
 
166
        s.JujuConnSuite.TearDownTest(c)
 
167
}
 
168
 
 
169
type UpgradeCharmStoreResourceSuite struct {
 
170
        charmStoreSuite
 
171
}
 
172
 
 
173
var _ = gc.Suite(&UpgradeCharmStoreResourceSuite{})
 
174
 
 
175
func (s *UpgradeCharmStoreResourceSuite) SetUpSuite(c *gc.C) {
 
176
        s.charmStoreSuite.SetUpSuite(c)
 
177
        err := all.RegisterForServer()
 
178
        c.Assert(err, jc.ErrorIsNil)
 
179
        err = all.RegisterForClient()
 
180
        c.Assert(err, jc.ErrorIsNil)
 
181
}
 
182
 
 
183
// TODO(ericsnow) Adapt this test to check passing revisions once the
 
184
// charmstore endpoints are implemented.
 
185
 
 
186
func (s *UpgradeCharmStoreResourceSuite) TestDeployStarsaySuccess(c *gc.C) {
 
187
        testcharms.UploadCharm(c, s.client, "trusty/starsay-1", "starsay")
 
188
 
 
189
        // let's make a fake resource file to upload
 
190
        resourceContent := "some-data"
 
191
 
 
192
        resourceFile := path.Join(c.MkDir(), "data.xml")
 
193
        err := ioutil.WriteFile(resourceFile, []byte(resourceContent), 0644)
 
194
        c.Assert(err, jc.ErrorIsNil)
 
195
 
 
196
        ctx, err := testing.RunCommand(c, application.NewDeployCommand(), "trusty/starsay", "--resource", "upload-resource="+resourceFile)
 
197
        c.Assert(err, jc.ErrorIsNil)
 
198
        output := testing.Stderr(ctx)
 
199
 
 
200
        expectedOutput := `Added charm "cs:trusty/starsay-1" to the model.
 
201
Deploying charm "cs:trusty/starsay-1" with the user specified series "trusty".
 
202
`
 
203
        c.Assert(output, gc.Equals, expectedOutput)
 
204
        s.assertCharmsUploaded(c, "cs:trusty/starsay-1")
 
205
        s.assertServicesDeployed(c, map[string]serviceInfo{
 
206
                "starsay": {charm: "cs:trusty/starsay-1"},
 
207
        })
 
208
        _, err = s.State.Unit("starsay/0")
 
209
        c.Assert(err, jc.ErrorIsNil)
 
210
 
 
211
        res, err := s.State.Resources()
 
212
        c.Assert(err, jc.ErrorIsNil)
 
213
        svcres, err := res.ListResources("starsay")
 
214
        c.Assert(err, jc.ErrorIsNil)
 
215
 
 
216
        sort.Sort(byname(svcres.Resources))
 
217
 
 
218
        c.Assert(svcres.Resources, gc.HasLen, 3)
 
219
        c.Check(svcres.Resources[2].Timestamp, gc.Not(gc.Equals), time.Time{})
 
220
        svcres.Resources[2].Timestamp = time.Time{}
 
221
 
 
222
        // Note that all charm resources were uploaded by testcharms.UploadCharm
 
223
        // so that the charm could be published.
 
224
        expectedResources := []resource.Resource{{
 
225
                Resource: charmresource.Resource{
 
226
                        Meta: charmresource.Meta{
 
227
                                Name:        "install-resource",
 
228
                                Type:        charmresource.TypeFile,
 
229
                                Path:        "gotta-have-it.txt",
 
230
                                Description: "get things started",
 
231
                        },
 
232
                        Origin:      charmresource.OriginStore,
 
233
                        Revision:    0,
 
234
                        Fingerprint: resourceHash("install-resource content"),
 
235
                        Size:        int64(len("install-resource content")),
 
236
                },
 
237
                ID:            "starsay/install-resource",
 
238
                ApplicationID: "starsay",
 
239
        }, {
 
240
                Resource: charmresource.Resource{
 
241
                        Meta: charmresource.Meta{
 
242
                                Name:        "store-resource",
 
243
                                Type:        charmresource.TypeFile,
 
244
                                Path:        "filename.tgz",
 
245
                                Description: "One line that is useful when operators need to push it.",
 
246
                        },
 
247
                        Origin:      charmresource.OriginStore,
 
248
                        Revision:    0,
 
249
                        Fingerprint: resourceHash("store-resource content"),
 
250
                        Size:        int64(len("store-resource content")),
 
251
                },
 
252
                ID:            "starsay/store-resource",
 
253
                ApplicationID: "starsay",
 
254
        }, {
 
255
                Resource: charmresource.Resource{
 
256
                        Meta: charmresource.Meta{
 
257
                                Name:        "upload-resource",
 
258
                                Type:        charmresource.TypeFile,
 
259
                                Path:        "somename.xml",
 
260
                                Description: "Who uses xml anymore?",
 
261
                        },
 
262
                        Origin:      charmresource.OriginUpload,
 
263
                        Revision:    0,
 
264
                        Fingerprint: resourceHash(resourceContent),
 
265
                        Size:        int64(len(resourceContent)),
 
266
                },
 
267
                ID:            "starsay/upload-resource",
 
268
                ApplicationID: "starsay",
 
269
                Username:      "admin@local",
 
270
                // Timestamp is checked above
 
271
        }}
 
272
 
 
273
        c.Check(svcres.Resources, jc.DeepEquals, expectedResources)
 
274
 
 
275
        oldCharmStoreResources := make([]charmresource.Resource, len(svcres.CharmStoreResources))
 
276
        copy(oldCharmStoreResources, svcres.CharmStoreResources)
 
277
 
 
278
        sort.Sort(csbyname(oldCharmStoreResources))
 
279
 
 
280
        testcharms.UploadCharm(c, s.client, "trusty/starsay-2", "starsay")
 
281
 
 
282
        _, err = testing.RunCommand(c, application.NewUpgradeCharmCommand(), "starsay")
 
283
        c.Assert(err, jc.ErrorIsNil)
 
284
 
 
285
        s.assertServicesDeployed(c, map[string]serviceInfo{
 
286
                "starsay": {charm: "cs:trusty/starsay-2"},
 
287
        })
 
288
 
 
289
        res, err = s.State.Resources()
 
290
        c.Assert(err, jc.ErrorIsNil)
 
291
        svcres, err = res.ListResources("starsay")
 
292
        c.Assert(err, jc.ErrorIsNil)
 
293
 
 
294
        sort.Sort(byname(svcres.Resources))
 
295
 
 
296
        c.Assert(svcres.Resources, gc.HasLen, 3)
 
297
        c.Check(svcres.Resources[2].Timestamp, gc.Not(gc.Equals), time.Time{})
 
298
        svcres.Resources[2].Timestamp = time.Time{}
 
299
 
 
300
        // ensure that we haven't overridden the previously uploaded resource.
 
301
        c.Check(svcres.Resources, jc.DeepEquals, expectedResources)
 
302
 
 
303
        sort.Sort(csbyname(svcres.CharmStoreResources))
 
304
        c.Check(oldCharmStoreResources, gc.DeepEquals, svcres.CharmStoreResources)
 
305
}
 
306
 
 
307
func resourceHash(content string) charmresource.Fingerprint {
 
308
        fp, err := charmresource.GenerateFingerprint(strings.NewReader(content))
 
309
        if err != nil {
 
310
                panic(err)
 
311
        }
 
312
        return fp
 
313
}
 
314
 
 
315
type byname []resource.Resource
 
316
 
 
317
func (b byname) Len() int           { return len(b) }
 
318
func (b byname) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
 
319
func (b byname) Less(i, j int) bool { return b[i].Name < b[j].Name }
 
320
 
 
321
type csbyname []charmresource.Resource
 
322
 
 
323
func (b csbyname) Len() int           { return len(b) }
 
324
func (b csbyname) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
 
325
func (b csbyname) Less(i, j int) bool { return b[i].Name < b[j].Name }
 
326
 
 
327
// assertCharmsUploaded checks that the given charm ids have been uploaded.
 
328
func (s *charmStoreSuite) assertCharmsUploaded(c *gc.C, ids ...string) {
 
329
        charms, err := s.State.AllCharms()
 
330
        c.Assert(err, jc.ErrorIsNil)
 
331
        uploaded := make([]string, len(charms))
 
332
        for i, charm := range charms {
 
333
                uploaded[i] = charm.URL().String()
 
334
        }
 
335
        c.Assert(uploaded, jc.SameContents, ids)
 
336
}
 
337
 
 
338
// assertServicesDeployed checks that the given services have been deployed.
 
339
func (s *charmStoreSuite) assertServicesDeployed(c *gc.C, info map[string]serviceInfo) {
 
340
        services, err := s.State.AllApplications()
 
341
        c.Assert(err, jc.ErrorIsNil)
 
342
        deployed := make(map[string]serviceInfo, len(services))
 
343
        for _, application := range services {
 
344
                charm, _ := application.CharmURL()
 
345
                config, err := application.ConfigSettings()
 
346
                c.Assert(err, jc.ErrorIsNil)
 
347
                if len(config) == 0 {
 
348
                        config = nil
 
349
                }
 
350
                constraints, err := application.Constraints()
 
351
                c.Assert(err, jc.ErrorIsNil)
 
352
                storage, err := application.StorageConstraints()
 
353
                c.Assert(err, jc.ErrorIsNil)
 
354
                if len(storage) == 0 {
 
355
                        storage = nil
 
356
                }
 
357
                deployed[application.Name()] = serviceInfo{
 
358
                        charm:       charm.String(),
 
359
                        config:      config,
 
360
                        constraints: constraints,
 
361
                        exposed:     application.IsExposed(),
 
362
                        storage:     storage,
 
363
                }
 
364
        }
 
365
        c.Assert(deployed, jc.DeepEquals, info)
 
366
}
 
367
 
 
368
// serviceInfo holds information about a deployed application.
 
369
type serviceInfo struct {
 
370
        charm            string
 
371
        config           charm.Settings
 
372
        constraints      constraints.Value
 
373
        exposed          bool
 
374
        storage          map[string]state.StorageConstraints
 
375
        endpointBindings map[string]string
 
376
}