~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/cmd/juju/gui/upgradegui_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 2016 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
 
4
package gui_test
 
5
 
 
6
import (
 
7
        "archive/tar"
 
8
        "bytes"
 
9
        "crypto/sha256"
 
10
        "errors"
 
11
        "fmt"
 
12
        "io"
 
13
        "io/ioutil"
 
14
        "os"
 
15
        "os/exec"
 
16
        "path/filepath"
 
17
        "runtime"
 
18
        "strings"
 
19
 
 
20
        jc "github.com/juju/testing/checkers"
 
21
        "github.com/juju/version"
 
22
        gc "gopkg.in/check.v1"
 
23
 
 
24
        "github.com/juju/juju/api"
 
25
        "github.com/juju/juju/apiserver/params"
 
26
        "github.com/juju/juju/cmd/juju/gui"
 
27
        envgui "github.com/juju/juju/environs/gui"
 
28
        "github.com/juju/juju/environs/simplestreams"
 
29
        jujutesting "github.com/juju/juju/juju/testing"
 
30
        coretesting "github.com/juju/juju/testing"
 
31
)
 
32
 
 
33
type upgradeGUISuite struct {
 
34
        jujutesting.JujuConnSuite
 
35
}
 
36
 
 
37
var _ = gc.Suite(&upgradeGUISuite{})
 
38
 
 
39
// run executes the upgrade-gui command passing the given args.
 
40
func (s *upgradeGUISuite) run(c *gc.C, args ...string) (string, error) {
 
41
        ctx, err := coretesting.RunCommand(c, gui.NewUpgradeGUICommand(), args...)
 
42
        return strings.Trim(coretesting.Stderr(ctx), "\n"), err
 
43
}
 
44
 
 
45
// calledFunc is returned by the patch* methods below, and when called reports
 
46
// whether the corresponding patched function has been called.
 
47
type calledFunc func() bool
 
48
 
 
49
func (s *upgradeGUISuite) patchClientGUIArchives(c *gc.C, returnedVersions []params.GUIArchiveVersion, returnedErr error) calledFunc {
 
50
        var called bool
 
51
        f := func(client *api.Client) ([]params.GUIArchiveVersion, error) {
 
52
                called = true
 
53
                return returnedVersions, returnedErr
 
54
        }
 
55
        s.PatchValue(gui.ClientGUIArchives, f)
 
56
        return func() bool {
 
57
                return called
 
58
        }
 
59
}
 
60
 
 
61
func (s *upgradeGUISuite) patchClientSelectGUIVersion(c *gc.C, expectedVers string, returnedErr error) calledFunc {
 
62
        var called bool
 
63
        f := func(client *api.Client, vers version.Number) error {
 
64
                called = true
 
65
                c.Assert(vers.String(), gc.Equals, expectedVers)
 
66
                return returnedErr
 
67
        }
 
68
        s.PatchValue(gui.ClientSelectGUIVersion, f)
 
69
        return func() bool {
 
70
                return called
 
71
        }
 
72
}
 
73
 
 
74
func (s *upgradeGUISuite) patchClientUploadGUIArchive(c *gc.C, expectedHash string, expectedSize int64, expectedVers string, returnedIsCurrent bool, returnedErr error) calledFunc {
 
75
        var called bool
 
76
        f := func(client *api.Client, r io.ReadSeeker, hash string, size int64, vers version.Number) (bool, error) {
 
77
                called = true
 
78
                c.Assert(hash, gc.Equals, expectedHash)
 
79
                c.Assert(size, gc.Equals, expectedSize)
 
80
                c.Assert(vers.String(), gc.Equals, expectedVers)
 
81
                return returnedIsCurrent, returnedErr
 
82
        }
 
83
        s.PatchValue(gui.ClientUploadGUIArchive, f)
 
84
        return func() bool {
 
85
                return called
 
86
        }
 
87
}
 
88
 
 
89
func (s *upgradeGUISuite) patchGUIFetchMetadata(c *gc.C, returnedMetadata []*envgui.Metadata, returnedErr error) calledFunc {
 
90
        var called bool
 
91
        f := func(stream string, sources ...simplestreams.DataSource) ([]*envgui.Metadata, error) {
 
92
                called = true
 
93
                c.Assert(stream, gc.Equals, envgui.ReleasedStream)
 
94
                c.Assert(sources[0].Description(), gc.Equals, "gui simplestreams")
 
95
                return returnedMetadata, returnedErr
 
96
        }
 
97
        s.PatchValue(gui.GUIFetchMetadata, f)
 
98
        return func() bool {
 
99
                return called
 
100
        }
 
101
}
 
102
 
 
103
var upgradeGUIInputErrorsTests = []struct {
 
104
        about         string
 
105
        args          []string
 
106
        expectedError string
 
107
}{{
 
108
        about:         "too many arguments",
 
109
        args:          []string{"bad", "wolf"},
 
110
        expectedError: `unrecognized args: \["bad" "wolf"\]`,
 
111
}, {
 
112
        about:         "listing and upgrading",
 
113
        args:          []string{"bad", "--list"},
 
114
        expectedError: "cannot provide arguments if --list is provided",
 
115
}, {
 
116
        about:         "archive path not found",
 
117
        args:          []string{"no-such-file"},
 
118
        expectedError: `invalid GUI release version or local path "no-such-file"`,
 
119
}}
 
120
 
 
121
func (s *upgradeGUISuite) TestUpgradeGUIInputErrors(c *gc.C) {
 
122
        for i, test := range upgradeGUIInputErrorsTests {
 
123
                c.Logf("\n%d: %s", i, test.about)
 
124
                _, err := s.run(c, test.args...)
 
125
                c.Assert(err, gc.ErrorMatches, test.expectedError)
 
126
        }
 
127
}
 
128
 
 
129
func (s *upgradeGUISuite) TestUpgradeGUIListSuccess(c *gc.C) {
 
130
        s.patchGUIFetchMetadata(c, []*envgui.Metadata{{
 
131
                Version: version.MustParse("2.2.0"),
 
132
        }, {
 
133
                Version: version.MustParse("2.1.1"),
 
134
        }, {
 
135
                Version: version.MustParse("2.1.0"),
 
136
        }}, nil)
 
137
        uploadCalled := s.patchClientUploadGUIArchive(c, "", 0, "", false, nil)
 
138
        selectCalled := s.patchClientSelectGUIVersion(c, "", nil)
 
139
 
 
140
        // Run the command to list available Juju GUI archive versions.
 
141
        out, err := s.run(c, "--list")
 
142
        c.Assert(err, jc.ErrorIsNil)
 
143
        c.Assert(out, gc.Equals, "2.2.0\n2.1.1\n2.1.0")
 
144
 
 
145
        // No uploads or switches are preformed.
 
146
        c.Assert(uploadCalled(), jc.IsFalse)
 
147
        c.Assert(selectCalled(), jc.IsFalse)
 
148
}
 
149
 
 
150
func (s *upgradeGUISuite) TestUpgradeGUIListNoReleases(c *gc.C) {
 
151
        s.patchGUIFetchMetadata(c, nil, nil)
 
152
        out, err := s.run(c, "--list")
 
153
        c.Assert(err, gc.ErrorMatches, "cannot list Juju GUI release versions: no available Juju GUI archives found")
 
154
        c.Assert(out, gc.Equals, "")
 
155
}
 
156
 
 
157
func (s *upgradeGUISuite) TestUpgradeGUIListError(c *gc.C) {
 
158
        s.patchGUIFetchMetadata(c, nil, errors.New("bad wolf"))
 
159
        out, err := s.run(c, "--list")
 
160
        c.Assert(err, gc.ErrorMatches, "cannot list Juju GUI release versions: cannot retrieve Juju GUI archive info: bad wolf")
 
161
        c.Assert(out, gc.Equals, "")
 
162
}
 
163
 
 
164
func (s *upgradeGUISuite) TestUpgradeGUIFileError(c *gc.C) {
 
165
        path, _, _ := saveGUIArchive(c, "2.0.0")
 
166
        err := os.Chmod(path, 0000)
 
167
        c.Assert(err, jc.ErrorIsNil)
 
168
        defer os.Chmod(path, 0600)
 
169
        out, err := s.run(c, path)
 
170
        c.Assert(err, gc.ErrorMatches, "cannot open GUI archive: .*")
 
171
        c.Assert(out, gc.Equals, "")
 
172
}
 
173
 
 
174
func (s *upgradeGUISuite) TestUpgradeGUIArchiveVersionNotValid(c *gc.C) {
 
175
        path, _, _ := saveGUIArchive(c, "bad-wolf")
 
176
        out, err := s.run(c, path)
 
177
        c.Assert(err, gc.ErrorMatches, `cannot upgrade Juju GUI using ".*": invalid version "bad-wolf" in archive`)
 
178
        c.Assert(out, gc.Equals, "")
 
179
}
 
180
 
 
181
func (s *upgradeGUISuite) TestUpgradeGUIArchiveVersionNotFound(c *gc.C) {
 
182
        path, _, _ := saveGUIArchive(c, "")
 
183
        out, err := s.run(c, path)
 
184
        c.Assert(err, gc.ErrorMatches, `cannot upgrade Juju GUI using ".*": cannot find Juju GUI version in archive`)
 
185
        c.Assert(out, gc.Equals, "")
 
186
}
 
187
 
 
188
func (s *upgradeGUISuite) TestUpgradeGUIGUIArchivesError(c *gc.C) {
 
189
        path, _, _ := saveGUIArchive(c, "2.1.0")
 
190
        s.patchClientGUIArchives(c, nil, errors.New("bad wolf"))
 
191
        out, err := s.run(c, path)
 
192
        c.Assert(err, gc.ErrorMatches, "cannot retrieve GUI versions from the controller: bad wolf")
 
193
        c.Assert(out, gc.Equals, "")
 
194
}
 
195
 
 
196
func (s *upgradeGUISuite) TestUpgradeGUIUploadGUIArchiveError(c *gc.C) {
 
197
        path, hash, size := saveGUIArchive(c, "2.2.0")
 
198
        s.patchClientGUIArchives(c, nil, nil)
 
199
        s.patchClientUploadGUIArchive(c, hash, size, "2.2.0", false, errors.New("bad wolf"))
 
200
        out, err := s.run(c, path)
 
201
        c.Assert(err, gc.ErrorMatches, "cannot upload Juju GUI: bad wolf")
 
202
        c.Assert(out, gc.Equals, "using local Juju GUI archive\nuploading Juju GUI 2.2.0")
 
203
}
 
204
 
 
205
func (s *upgradeGUISuite) TestUpgradeGUISelectGUIVersionError(c *gc.C) {
 
206
        path, hash, size := saveGUIArchive(c, "2.3.0")
 
207
        s.patchClientGUIArchives(c, nil, nil)
 
208
        s.patchClientUploadGUIArchive(c, hash, size, "2.3.0", false, nil)
 
209
        s.patchClientSelectGUIVersion(c, "2.3.0", errors.New("bad wolf"))
 
210
        out, err := s.run(c, path)
 
211
        c.Assert(err, gc.ErrorMatches, "cannot switch to new Juju GUI version: bad wolf")
 
212
        c.Assert(out, gc.Equals, "using local Juju GUI archive\nuploading Juju GUI 2.3.0\nupload completed")
 
213
}
 
214
 
 
215
func (s *upgradeGUISuite) TestUpgradeGUIFromSimplestreamsReleaseErrors(c *gc.C) {
 
216
        tests := []struct {
 
217
                about            string
 
218
                arg              string
 
219
                returnedMetadata []*envgui.Metadata
 
220
                returnedErr      error
 
221
                expectedErr      string
 
222
        }{{
 
223
                about:       "last release: no releases found",
 
224
                expectedErr: "cannot upgrade to most recent release: no available Juju GUI archives found",
 
225
        }, {
 
226
                about:       "specific release: no releases found",
 
227
                arg:         "2.0.42",
 
228
                expectedErr: "cannot upgrade to release 2.0.42: no available Juju GUI archives found",
 
229
        }, {
 
230
                about:       "last release: error while fetching releases list",
 
231
                returnedErr: errors.New("bad wolf"),
 
232
                expectedErr: "cannot upgrade to most recent release: cannot retrieve Juju GUI archive info: bad wolf",
 
233
        }, {
 
234
                about:       "specific release: error while fetching releases list",
 
235
                arg:         "2.0.47",
 
236
                returnedErr: errors.New("bad wolf"),
 
237
                expectedErr: "cannot upgrade to release 2.0.47: cannot retrieve Juju GUI archive info: bad wolf",
 
238
        }, {
 
239
                about: "last release: error while opening the remote release resource",
 
240
                returnedMetadata: []*envgui.Metadata{
 
241
                        makeGUIMetadata(c, "2.2.0", "exterminate"),
 
242
                        makeGUIMetadata(c, "2.1.0", ""),
 
243
                },
 
244
                expectedErr: `cannot open Juju GUI archive at "https://1.2.3.4/path/to/gui/2.2.0": exterminate`,
 
245
        }, {
 
246
                about: "specific release: error while opening the remote release resource",
 
247
                arg:   "2.1.0",
 
248
                returnedMetadata: []*envgui.Metadata{
 
249
                        makeGUIMetadata(c, "2.2.0", ""),
 
250
                        makeGUIMetadata(c, "2.1.0", "boo"),
 
251
                        makeGUIMetadata(c, "2.0.0", ""),
 
252
                },
 
253
                expectedErr: `cannot open Juju GUI archive at "https://1.2.3.4/path/to/gui/2.1.0": boo`,
 
254
        }, {
 
255
                about: "specific release: not found in available releases",
 
256
                arg:   "2.1.0",
 
257
                returnedMetadata: []*envgui.Metadata{
 
258
                        makeGUIMetadata(c, "2.2.0", ""),
 
259
                        makeGUIMetadata(c, "2.0.0", ""),
 
260
                },
 
261
                expectedErr: "Juju GUI release version 2.1.0 not found",
 
262
        }}
 
263
 
 
264
        for i, test := range tests {
 
265
                c.Logf("\n%d: %s", i, test.about)
 
266
 
 
267
                s.patchGUIFetchMetadata(c, test.returnedMetadata, test.returnedErr)
 
268
                out, err := s.run(c, test.arg)
 
269
                c.Assert(err, gc.ErrorMatches, test.expectedErr)
 
270
                c.Assert(out, gc.Equals, "")
 
271
        }
 
272
}
 
273
 
 
274
func (s *upgradeGUISuite) TestUpgradeGUISuccess(c *gc.C) {
 
275
        tests := []struct {
 
276
                // about describes the test.
 
277
                about string
 
278
                // returnedMetadata holds metadata information returned by simplestreams.
 
279
                returnedMetadata *envgui.Metadata
 
280
                // archiveVersion is the version of the archive to be uploaded.
 
281
                archiveVersion string
 
282
                // existingVersions is a function returning a list of GUI archive versions
 
283
                // already included in the controller.
 
284
                existingVersions func(hash string) []params.GUIArchiveVersion
 
285
                // opened holds whether Juju GUI metadata information in simplestreams
 
286
                // has been opened.
 
287
                opened bool
 
288
                // uploaded holds whether the archive has been actually uploaded. If an
 
289
                // archive with the same hash and version is already present in the
 
290
                // controller, the upload is not performed again.
 
291
                uploaded bool
 
292
                // selected holds whether a new GUI version must be selected. If the upload
 
293
                // upgraded the currently served version there is no need to perform
 
294
                // the API call to switch GUI version.
 
295
                selected bool
 
296
                // expectedOutput holds the expected upgrade-gui command output.
 
297
                expectedOutput string
 
298
        }{{
 
299
                about:          "archive: first archive",
 
300
                archiveVersion: "2.0.0",
 
301
                expectedOutput: "using local Juju GUI archive\nuploading Juju GUI 2.0.0\nupload completed\nJuju GUI switched to version 2.0.0",
 
302
                uploaded:       true,
 
303
                selected:       true,
 
304
        }, {
 
305
                about:          "archive: new archive",
 
306
                archiveVersion: "2.1.0",
 
307
                existingVersions: func(hash string) []params.GUIArchiveVersion {
 
308
                        return []params.GUIArchiveVersion{{
 
309
                                Version: version.MustParse("1.0.0"),
 
310
                                SHA256:  "hash-1",
 
311
                                Current: true,
 
312
                        }}
 
313
                },
 
314
                uploaded:       true,
 
315
                selected:       true,
 
316
                expectedOutput: "using local Juju GUI archive\nuploading Juju GUI 2.1.0\nupload completed\nJuju GUI switched to version 2.1.0",
 
317
        }, {
 
318
                about:          "archive: new archive, existing non-current version",
 
319
                archiveVersion: "2.0.42",
 
320
                existingVersions: func(hash string) []params.GUIArchiveVersion {
 
321
                        return []params.GUIArchiveVersion{{
 
322
                                Version: version.MustParse("2.0.42"),
 
323
                                SHA256:  "hash-42",
 
324
                                Current: false,
 
325
                        }, {
 
326
                                Version: version.MustParse("2.0.47"),
 
327
                                SHA256:  "hash-47",
 
328
                                Current: true,
 
329
                        }}
 
330
                },
 
331
                uploaded:       true,
 
332
                selected:       true,
 
333
                expectedOutput: "using local Juju GUI archive\nuploading Juju GUI 2.0.42\nupload completed\nJuju GUI switched to version 2.0.42",
 
334
        }, {
 
335
                about:          "archive: new archive, existing current version",
 
336
                archiveVersion: "2.0.47",
 
337
                existingVersions: func(hash string) []params.GUIArchiveVersion {
 
338
                        return []params.GUIArchiveVersion{{
 
339
                                Version: version.MustParse("2.0.47"),
 
340
                                SHA256:  "hash-47",
 
341
                                Current: true,
 
342
                        }}
 
343
                },
 
344
                uploaded:       true,
 
345
                expectedOutput: "using local Juju GUI archive\nuploading Juju GUI 2.0.47\nupload completed\nJuju GUI at version 2.0.47",
 
346
        }, {
 
347
                about:          "archive: existing archive, existing non-current version",
 
348
                archiveVersion: "2.0.42",
 
349
                existingVersions: func(hash string) []params.GUIArchiveVersion {
 
350
                        return []params.GUIArchiveVersion{{
 
351
                                Version: version.MustParse("2.0.42"),
 
352
                                SHA256:  hash,
 
353
                                Current: false,
 
354
                        }, {
 
355
                                Version: version.MustParse("2.0.47"),
 
356
                                SHA256:  "hash-47",
 
357
                                Current: true,
 
358
                        }}
 
359
                },
 
360
                selected:       true,
 
361
                expectedOutput: "Juju GUI switched to version 2.0.42",
 
362
        }, {
 
363
                about:          "archive: existing archive, existing current version",
 
364
                archiveVersion: "1.47.0",
 
365
                existingVersions: func(hash string) []params.GUIArchiveVersion {
 
366
                        return []params.GUIArchiveVersion{{
 
367
                                Version: version.MustParse("1.47.0"),
 
368
                                SHA256:  hash,
 
369
                                Current: true,
 
370
                        }}
 
371
                },
 
372
                expectedOutput: "Juju GUI at version 1.47.0",
 
373
        }, {
 
374
                about:          "archive: existing archive, different existing version",
 
375
                archiveVersion: "2.0.42",
 
376
                existingVersions: func(hash string) []params.GUIArchiveVersion {
 
377
                        return []params.GUIArchiveVersion{{
 
378
                                Version: version.MustParse("2.0.42"),
 
379
                                SHA256:  "hash-42",
 
380
                                Current: false,
 
381
                        }, {
 
382
                                Version: version.MustParse("2.0.47"),
 
383
                                SHA256:  hash,
 
384
                                Current: true,
 
385
                        }}
 
386
                },
 
387
                uploaded:       true,
 
388
                selected:       true,
 
389
                expectedOutput: "using local Juju GUI archive\nuploading Juju GUI 2.0.42\nupload completed\nJuju GUI switched to version 2.0.42",
 
390
        }, {
 
391
                about:            "stream: first archive",
 
392
                archiveVersion:   "2.0.0",
 
393
                returnedMetadata: makeGUIMetadata(c, "2.0.0", ""),
 
394
                expectedOutput:   "fetching Juju GUI archive\nuploading Juju GUI 2.0.0\nupload completed\nJuju GUI switched to version 2.0.0",
 
395
                opened:           true,
 
396
                uploaded:         true,
 
397
                selected:         true,
 
398
        }, {
 
399
                about:            "stream: new archive",
 
400
                archiveVersion:   "2.1.0",
 
401
                returnedMetadata: makeGUIMetadata(c, "2.1.0", ""),
 
402
                existingVersions: func(hash string) []params.GUIArchiveVersion {
 
403
                        return []params.GUIArchiveVersion{{
 
404
                                Version: version.MustParse("1.0.0"),
 
405
                                SHA256:  "hash-1",
 
406
                                Current: true,
 
407
                        }}
 
408
                },
 
409
                opened:         true,
 
410
                uploaded:       true,
 
411
                selected:       true,
 
412
                expectedOutput: "fetching Juju GUI archive\nuploading Juju GUI 2.1.0\nupload completed\nJuju GUI switched to version 2.1.0",
 
413
        }, {
 
414
                about:            "stream: new archive, existing non-current version",
 
415
                archiveVersion:   "2.0.42",
 
416
                returnedMetadata: makeGUIMetadata(c, "2.0.42", ""),
 
417
                existingVersions: func(hash string) []params.GUIArchiveVersion {
 
418
                        return []params.GUIArchiveVersion{{
 
419
                                Version: version.MustParse("2.0.42"),
 
420
                                SHA256:  "hash-42",
 
421
                                Current: false,
 
422
                        }, {
 
423
                                Version: version.MustParse("2.0.47"),
 
424
                                SHA256:  "hash-47",
 
425
                                Current: true,
 
426
                        }}
 
427
                },
 
428
                opened:         true,
 
429
                uploaded:       true,
 
430
                selected:       true,
 
431
                expectedOutput: "fetching Juju GUI archive\nuploading Juju GUI 2.0.42\nupload completed\nJuju GUI switched to version 2.0.42",
 
432
        }, {
 
433
                about:            "stream: new archive, existing current version",
 
434
                archiveVersion:   "2.0.47",
 
435
                returnedMetadata: makeGUIMetadata(c, "2.0.47", ""),
 
436
                existingVersions: func(hash string) []params.GUIArchiveVersion {
 
437
                        return []params.GUIArchiveVersion{{
 
438
                                Version: version.MustParse("2.0.47"),
 
439
                                SHA256:  "hash-47",
 
440
                                Current: true,
 
441
                        }}
 
442
                },
 
443
                opened:         true,
 
444
                uploaded:       true,
 
445
                expectedOutput: "fetching Juju GUI archive\nuploading Juju GUI 2.0.47\nupload completed\nJuju GUI at version 2.0.47",
 
446
        }, {
 
447
                about:            "stream: existing archive, existing non-current version",
 
448
                archiveVersion:   "2.0.42",
 
449
                returnedMetadata: makeGUIMetadata(c, "2.0.42", ""),
 
450
                existingVersions: func(hash string) []params.GUIArchiveVersion {
 
451
                        return []params.GUIArchiveVersion{{
 
452
                                Version: version.MustParse("2.0.42"),
 
453
                                SHA256:  hash,
 
454
                                Current: false,
 
455
                        }, {
 
456
                                Version: version.MustParse("2.0.47"),
 
457
                                SHA256:  "hash-47",
 
458
                                Current: true,
 
459
                        }}
 
460
                },
 
461
                opened:         true,
 
462
                selected:       true,
 
463
                expectedOutput: "Juju GUI switched to version 2.0.42",
 
464
        }, {
 
465
                about:            "stream: existing archive, existing current version",
 
466
                archiveVersion:   "1.47.0",
 
467
                returnedMetadata: makeGUIMetadata(c, "1.47.0", ""),
 
468
                existingVersions: func(hash string) []params.GUIArchiveVersion {
 
469
                        return []params.GUIArchiveVersion{{
 
470
                                Version: version.MustParse("1.47.0"),
 
471
                                SHA256:  hash,
 
472
                                Current: true,
 
473
                        }}
 
474
                },
 
475
                opened:         true,
 
476
                expectedOutput: "Juju GUI at version 1.47.0",
 
477
        }, {
 
478
                about:            "stream: existing archive, different existing version",
 
479
                archiveVersion:   "2.0.42",
 
480
                returnedMetadata: makeGUIMetadata(c, "2.0.42", ""),
 
481
                existingVersions: func(hash string) []params.GUIArchiveVersion {
 
482
                        return []params.GUIArchiveVersion{{
 
483
                                Version: version.MustParse("2.0.42"),
 
484
                                SHA256:  "hash-42",
 
485
                                Current: false,
 
486
                        }, {
 
487
                                Version: version.MustParse("2.0.47"),
 
488
                                SHA256:  hash,
 
489
                                Current: true,
 
490
                        }}
 
491
                },
 
492
                opened:         true,
 
493
                uploaded:       true,
 
494
                selected:       true,
 
495
                expectedOutput: "fetching Juju GUI archive\nuploading Juju GUI 2.0.42\nupload completed\nJuju GUI switched to version 2.0.42",
 
496
        }}
 
497
 
 
498
        for i, test := range tests {
 
499
                c.Logf("\n%d: %s", i, test.about)
 
500
 
 
501
                var arg string
 
502
                var hash string
 
503
                var size int64
 
504
 
 
505
                if test.returnedMetadata == nil {
 
506
                        // Create an fake Juju GUI local archive.
 
507
                        arg, hash, size = saveGUIArchive(c, test.archiveVersion)
 
508
                } else {
 
509
                        // Use the remote metadata information.
 
510
                        arg = test.returnedMetadata.Version.String()
 
511
                        hash = test.returnedMetadata.SHA256
 
512
                        size = test.returnedMetadata.Size
 
513
                }
 
514
 
 
515
                // Patch the call to get simplestreams metadata information.
 
516
                fetchMetadataCalled := s.patchGUIFetchMetadata(c, []*envgui.Metadata{test.returnedMetadata}, nil)
 
517
 
 
518
                // Patch the call to get existing archive versions.
 
519
                var existingVersions []params.GUIArchiveVersion
 
520
                if test.existingVersions != nil {
 
521
                        existingVersions = test.existingVersions(hash)
 
522
                }
 
523
                guiArchivesCalled := s.patchClientGUIArchives(c, existingVersions, nil)
 
524
 
 
525
                // Patch the other calls to the controller.
 
526
                uploadGUIArchiveCalled := s.patchClientUploadGUIArchive(c, hash, size, test.archiveVersion, !test.selected, nil)
 
527
                selectGUIVersionCalled := s.patchClientSelectGUIVersion(c, test.archiveVersion, nil)
 
528
 
 
529
                // Run the command.
 
530
                out, err := s.run(c, arg)
 
531
                c.Assert(err, jc.ErrorIsNil)
 
532
                c.Assert(out, gc.Equals, test.expectedOutput)
 
533
                c.Assert(guiArchivesCalled(), jc.IsTrue)
 
534
                c.Assert(fetchMetadataCalled(), gc.Equals, test.opened)
 
535
                c.Assert(uploadGUIArchiveCalled(), gc.Equals, test.uploaded)
 
536
                c.Assert(selectGUIVersionCalled(), gc.Equals, test.selected)
 
537
        }
 
538
}
 
539
 
 
540
func (s *upgradeGUISuite) TestUpgradeGUIIntegration(c *gc.C) {
 
541
        // Prepare a GUI archive.
 
542
        path, hash, size := saveGUIArchive(c, "2.42.0")
 
543
 
 
544
        // Upload the archive from command line.
 
545
        out, err := s.run(c, path)
 
546
        c.Assert(err, jc.ErrorIsNil)
 
547
        c.Assert(out, gc.Equals, "using local Juju GUI archive\nuploading Juju GUI 2.42.0\nupload completed\nJuju GUI switched to version 2.42.0")
 
548
 
 
549
        // Check that the archive is present in the GUI storage server side.
 
550
        storage, err := s.State.GUIStorage()
 
551
        c.Assert(err, jc.ErrorIsNil)
 
552
        defer storage.Close()
 
553
        metadata, err := storage.Metadata("2.42.0")
 
554
        c.Assert(err, jc.ErrorIsNil)
 
555
        c.Assert(metadata.SHA256, gc.Equals, hash)
 
556
        c.Assert(metadata.Size, gc.Equals, size)
 
557
 
 
558
        // Check that the uploaded version has been set as the current one.
 
559
        vers, err := s.State.GUIVersion()
 
560
        c.Assert(err, jc.ErrorIsNil)
 
561
        c.Assert(vers.String(), gc.Equals, "2.42.0")
 
562
}
 
563
 
 
564
// makeGUIArchive creates a Juju GUI tar.bz2 archive in memory, and returns a
 
565
// reader for the archive, its SHA256 hash and size.
 
566
func makeGUIArchive(c *gc.C, vers string) (r io.Reader, hash string, size int64) {
 
567
        if runtime.GOOS == "windows" {
 
568
                c.Skip("bzip2 command not available")
 
569
        }
 
570
        cmd := exec.Command("bzip2", "--compress", "--stdout", "--fast")
 
571
 
 
572
        stdin, err := cmd.StdinPipe()
 
573
        c.Assert(err, jc.ErrorIsNil)
 
574
        stdout, err := cmd.StdoutPipe()
 
575
        c.Assert(err, jc.ErrorIsNil)
 
576
 
 
577
        err = cmd.Start()
 
578
        c.Assert(err, jc.ErrorIsNil)
 
579
 
 
580
        tw := tar.NewWriter(stdin)
 
581
        if vers != "" {
 
582
                err = tw.WriteHeader(&tar.Header{
 
583
                        Name:     filepath.Join("jujugui-"+vers, "jujugui"),
 
584
                        Mode:     0700,
 
585
                        Typeflag: tar.TypeDir,
 
586
                })
 
587
                c.Assert(err, jc.ErrorIsNil)
 
588
        }
 
589
        err = tw.Close()
 
590
        c.Assert(err, jc.ErrorIsNil)
 
591
        err = stdin.Close()
 
592
        c.Assert(err, jc.ErrorIsNil)
 
593
 
 
594
        h := sha256.New()
 
595
        r = io.TeeReader(stdout, h)
 
596
        b, err := ioutil.ReadAll(r)
 
597
        c.Assert(err, jc.ErrorIsNil)
 
598
 
 
599
        err = cmd.Wait()
 
600
        c.Assert(err, jc.ErrorIsNil)
 
601
 
 
602
        return bytes.NewReader(b), fmt.Sprintf("%x", h.Sum(nil)), int64(len(b))
 
603
}
 
604
 
 
605
// saveGUIArchive creates a Juju GUI tar.bz2 archive with the given version on
 
606
// disk, and return its path, SHA256 hash and size.
 
607
func saveGUIArchive(c *gc.C, vers string) (path, hash string, size int64) {
 
608
        r, hash, size := makeGUIArchive(c, vers)
 
609
        path = filepath.Join(c.MkDir(), "gui.tar.bz2")
 
610
        data, err := ioutil.ReadAll(r)
 
611
        c.Assert(err, jc.ErrorIsNil)
 
612
        err = ioutil.WriteFile(path, data, 0600)
 
613
        c.Assert(err, jc.ErrorIsNil)
 
614
        return path, hash, size
 
615
}
 
616
 
 
617
// makeGUIMetadata creates and return a Juju GUI archive metadata with the
 
618
// given version. If fetchError is not empty, trying to fetch the corresponding
 
619
// archive will return the given error.
 
620
func makeGUIMetadata(c *gc.C, vers, fetchError string) *envgui.Metadata {
 
621
        path, hash, size := saveGUIArchive(c, vers)
 
622
        metaPath := "/path/to/gui/" + vers
 
623
        return &envgui.Metadata{
 
624
                Version:  version.MustParse(vers),
 
625
                SHA256:   hash,
 
626
                Size:     size,
 
627
                Path:     metaPath,
 
628
                FullPath: "https://1.2.3.4" + metaPath,
 
629
                Source: &dataSource{
 
630
                        DataSource: envgui.NewDataSource("htpps://1.2.3.4"),
 
631
                        metaPath:   metaPath,
 
632
                        path:       path,
 
633
                        fetchError: fetchError,
 
634
                        c:          c,
 
635
                },
 
636
        }
 
637
}
 
638
 
 
639
// datasource implements simplestreams.DataSource and overrides the Fetch
 
640
// method for testing purposes.
 
641
type dataSource struct {
 
642
        simplestreams.DataSource
 
643
 
 
644
        metaPath   string
 
645
        path       string
 
646
        fetchError string
 
647
        c          *gc.C
 
648
}
 
649
 
 
650
// Fetch implements simplestreams.DataSource.
 
651
func (ds *dataSource) Fetch(path string) (io.ReadCloser, string, error) {
 
652
        ds.c.Assert(path, gc.Equals, ds.metaPath)
 
653
        if ds.fetchError != "" {
 
654
                return nil, "", errors.New(ds.fetchError)
 
655
        }
 
656
        f, err := os.Open(ds.path)
 
657
        ds.c.Assert(err, jc.ErrorIsNil)
 
658
        return f, "", nil
 
659
}