~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/cmd/juju/commands/upgradejuju_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 2012, 2013 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
 
4
package commands
 
5
 
 
6
import (
 
7
        "archive/tar"
 
8
        "bytes"
 
9
        "compress/gzip"
 
10
        "io"
 
11
        "io/ioutil"
 
12
        "strings"
 
13
 
 
14
        jc "github.com/juju/testing/checkers"
 
15
        "github.com/juju/utils/arch"
 
16
        "github.com/juju/utils/series"
 
17
        "github.com/juju/version"
 
18
        gc "gopkg.in/check.v1"
 
19
 
 
20
        "github.com/juju/juju/apiserver/common"
 
21
        "github.com/juju/juju/apiserver/params"
 
22
        apiservertesting "github.com/juju/juju/apiserver/testing"
 
23
        cmdcommon "github.com/juju/juju/cmd/juju/common"
 
24
        "github.com/juju/juju/cmd/modelcmd"
 
25
        "github.com/juju/juju/environs/filestorage"
 
26
        "github.com/juju/juju/environs/sync"
 
27
        envtesting "github.com/juju/juju/environs/testing"
 
28
        "github.com/juju/juju/environs/tools"
 
29
        toolstesting "github.com/juju/juju/environs/tools/testing"
 
30
        jujutesting "github.com/juju/juju/juju/testing"
 
31
        "github.com/juju/juju/network"
 
32
        "github.com/juju/juju/provider/dummy"
 
33
        "github.com/juju/juju/state"
 
34
        coretesting "github.com/juju/juju/testing"
 
35
        coretools "github.com/juju/juju/tools"
 
36
        jujuversion "github.com/juju/juju/version"
 
37
)
 
38
 
 
39
type UpgradeJujuSuite struct {
 
40
        jujutesting.JujuConnSuite
 
41
 
 
42
        resources  *common.Resources
 
43
        authoriser apiservertesting.FakeAuthorizer
 
44
 
 
45
        toolsDir string
 
46
        cmdcommon.CmdBlockHelper
 
47
}
 
48
 
 
49
func (s *UpgradeJujuSuite) SetUpTest(c *gc.C) {
 
50
        s.JujuConnSuite.SetUpTest(c)
 
51
        s.resources = common.NewResources()
 
52
        s.authoriser = apiservertesting.FakeAuthorizer{
 
53
                Tag: s.AdminUserTag(c),
 
54
        }
 
55
 
 
56
        s.CmdBlockHelper = cmdcommon.NewCmdBlockHelper(s.APIState)
 
57
        c.Assert(s.CmdBlockHelper, gc.NotNil)
 
58
        s.AddCleanup(func(*gc.C) { s.CmdBlockHelper.Close() })
 
59
}
 
60
 
 
61
var _ = gc.Suite(&UpgradeJujuSuite{})
 
62
 
 
63
var upgradeJujuTests = []struct {
 
64
        about          string
 
65
        tools          []string
 
66
        currentVersion string
 
67
        agentVersion   string
 
68
 
 
69
        args           []string
 
70
        expectInitErr  string
 
71
        expectErr      string
 
72
        expectVersion  string
 
73
        expectUploaded []string
 
74
        upgradeMap     map[int]version.Number
 
75
}{{
 
76
        about:          "unwanted extra argument",
 
77
        currentVersion: "1.0.0-quantal-amd64",
 
78
        args:           []string{"foo"},
 
79
        expectInitErr:  "unrecognized args:.*",
 
80
}, {
 
81
        about:          "removed arg --dev specified",
 
82
        currentVersion: "1.0.0-quantal-amd64",
 
83
        args:           []string{"--dev"},
 
84
        expectInitErr:  "flag provided but not defined: --dev",
 
85
}, {
 
86
        about:          "invalid --version value",
 
87
        currentVersion: "1.0.0-quantal-amd64",
 
88
        args:           []string{"--version", "invalid-version"},
 
89
        expectInitErr:  "invalid version .*",
 
90
}, {
 
91
        about:          "just major version, no minor specified",
 
92
        currentVersion: "4.2.0-quantal-amd64",
 
93
        args:           []string{"--version", "4"},
 
94
        expectInitErr:  `invalid version "4"`,
 
95
}, {
 
96
        about:          "major version upgrade to incompatible version",
 
97
        currentVersion: "2.0.0-quantal-amd64",
 
98
        agentVersion:   "2.0.0",
 
99
        args:           []string{"--version", "5.2.0"},
 
100
        expectErr:      `unknown version "5.2.0"`,
 
101
}, {
 
102
        about:          "major version downgrade to incompatible version",
 
103
        currentVersion: "4.2.0-quantal-amd64",
 
104
        agentVersion:   "4.2.0",
 
105
        args:           []string{"--version", "3.2.0"},
 
106
        expectErr:      "cannot change version from 4.2.0 to 3.2.0",
 
107
}, {
 
108
        about:          "--upload-tools with inappropriate version 1",
 
109
        currentVersion: "4.2.0-quantal-amd64",
 
110
        agentVersion:   "4.2.0",
 
111
        args:           []string{"--upload-tools", "--version", "3.1.0"},
 
112
        expectErr:      "cannot change version from 4.2.0 to 3.1.0",
 
113
}, {
 
114
        about:          "--upload-tools with inappropriate version 2",
 
115
        currentVersion: "3.2.7-quantal-amd64",
 
116
        args:           []string{"--upload-tools", "--version", "3.2.8.4"},
 
117
        expectInitErr:  "cannot specify build number when uploading tools",
 
118
}, {
 
119
        about:          "latest supported stable release",
 
120
        tools:          []string{"2.1.0-quantal-amd64", "2.1.2-quantal-i386", "2.1.3-quantal-amd64", "2.1-dev1-quantal-amd64"},
 
121
        currentVersion: "2.0.0-quantal-amd64",
 
122
        agentVersion:   "2.0.0",
 
123
        expectVersion:  "2.1.3",
 
124
}, {
 
125
        about:          "latest current release",
 
126
        tools:          []string{"2.0.5-quantal-amd64", "2.0.1-quantal-i386", "2.3.3-quantal-amd64"},
 
127
        currentVersion: "2.0.0-quantal-amd64",
 
128
        agentVersion:   "2.0.0",
 
129
        expectVersion:  "2.0.5",
 
130
}, {
 
131
        about:          "latest current release matching CLI, major version, no matching major tools",
 
132
        tools:          []string{"2.8.2-quantal-amd64"},
 
133
        currentVersion: "3.0.2-quantal-amd64",
 
134
        agentVersion:   "2.8.2",
 
135
        expectVersion:  "2.8.2",
 
136
}, {
 
137
        about:          "latest current release matching CLI, major version, no matching tools",
 
138
        tools:          []string{"3.3.0-quantal-amd64"},
 
139
        currentVersion: "3.0.2-quantal-amd64",
 
140
        agentVersion:   "2.8.2",
 
141
        expectVersion:  "2.8.2",
 
142
}, {
 
143
        about:          "no next supported available",
 
144
        tools:          []string{"2.2.0-quantal-amd64", "2.2.5-quantal-i386", "2.3.3-quantal-amd64", "2.1-dev1-quantal-amd64"},
 
145
        currentVersion: "2.0.0-quantal-amd64",
 
146
        agentVersion:   "2.0.0",
 
147
        expectErr:      "no more recent supported versions available",
 
148
}, {
 
149
        about:          "latest supported stable, when client is dev",
 
150
        tools:          []string{"2.1-dev1-quantal-amd64", "2.1.0-quantal-amd64", "2.3-dev0-quantal-amd64", "3.0.1-quantal-amd64"},
 
151
        currentVersion: "2.1-dev0-quantal-amd64",
 
152
        agentVersion:   "2.0.0",
 
153
        expectVersion:  "2.1.0",
 
154
}, {
 
155
        about:          "latest current, when agent is dev",
 
156
        tools:          []string{"2.1-dev1-quantal-amd64", "2.2.0-quantal-amd64", "2.3-dev0-quantal-amd64", "3.0.1-quantal-amd64"},
 
157
        currentVersion: "2.0.0-quantal-amd64",
 
158
        agentVersion:   "2.1-dev0",
 
159
        expectVersion:  "2.2.0",
 
160
}, {
 
161
        about:          "specified version",
 
162
        tools:          []string{"2.3-dev0-quantal-amd64"},
 
163
        currentVersion: "2.0.0-quantal-amd64",
 
164
        agentVersion:   "2.0.0",
 
165
        args:           []string{"--version", "2.3-dev0"},
 
166
        expectVersion:  "2.3-dev0",
 
167
}, {
 
168
        about:          "specified major version",
 
169
        tools:          []string{"3.0.2-quantal-amd64"},
 
170
        currentVersion: "3.0.2-quantal-amd64",
 
171
        agentVersion:   "2.8.2",
 
172
        args:           []string{"--version", "3.0.2"},
 
173
        expectVersion:  "3.0.2",
 
174
        upgradeMap:     map[int]version.Number{3: version.MustParse("2.8.2")},
 
175
}, {
 
176
        about:          "specified version missing, but already set",
 
177
        currentVersion: "3.0.0-quantal-amd64",
 
178
        agentVersion:   "3.0.0",
 
179
        args:           []string{"--version", "3.0.0"},
 
180
        expectVersion:  "3.0.0",
 
181
}, {
 
182
        about:          "specified version, no tools",
 
183
        currentVersion: "3.0.0-quantal-amd64",
 
184
        agentVersion:   "3.0.0",
 
185
        args:           []string{"--version", "3.2.0"},
 
186
        expectErr:      "no tools available",
 
187
}, {
 
188
        about:          "specified version, no matching major version",
 
189
        tools:          []string{"4.2.0-quantal-amd64"},
 
190
        currentVersion: "3.0.0-quantal-amd64",
 
191
        agentVersion:   "3.0.0",
 
192
        args:           []string{"--version", "3.2.0"},
 
193
        expectErr:      "no matching tools available",
 
194
}, {
 
195
        about:          "specified version, no matching minor version",
 
196
        tools:          []string{"3.4.0-quantal-amd64"},
 
197
        currentVersion: "3.0.0-quantal-amd64",
 
198
        agentVersion:   "3.0.0",
 
199
        args:           []string{"--version", "3.2.0"},
 
200
        expectErr:      "no matching tools available",
 
201
}, {
 
202
        about:          "specified version, no matching patch version",
 
203
        tools:          []string{"3.2.5-quantal-amd64"},
 
204
        currentVersion: "3.0.0-quantal-amd64",
 
205
        agentVersion:   "3.0.0",
 
206
        args:           []string{"--version", "3.2.0"},
 
207
        expectErr:      "no matching tools available",
 
208
}, {
 
209
        about:          "specified version, no matching build version",
 
210
        tools:          []string{"3.2.0.2-quantal-amd64"},
 
211
        currentVersion: "3.0.0-quantal-amd64",
 
212
        agentVersion:   "3.0.0",
 
213
        args:           []string{"--version", "3.2.0"},
 
214
        expectErr:      "no matching tools available",
 
215
}, {
 
216
        about:          "incompatible version (minor != 0)",
 
217
        tools:          []string{"3.2.0-quantal-amd64"},
 
218
        currentVersion: "4.2.0-quantal-amd64",
 
219
        agentVersion:   "3.2.0",
 
220
        args:           []string{"--version", "3.2.0"},
 
221
        expectErr:      "cannot upgrade a 3.2.0 model with a 4.2.0 client",
 
222
}, {
 
223
        about:          "incompatible version (model major > client major)",
 
224
        tools:          []string{"3.2.0-quantal-amd64"},
 
225
        currentVersion: "3.2.0-quantal-amd64",
 
226
        agentVersion:   "4.2.0",
 
227
        args:           []string{"--version", "3.2.0"},
 
228
        expectErr:      "cannot upgrade a 4.2.0 model with a 3.2.0 client",
 
229
}, {
 
230
        about:          "incompatible version (model major < client major - 1)",
 
231
        tools:          []string{"3.2.0-quantal-amd64"},
 
232
        currentVersion: "4.0.2-quantal-amd64",
 
233
        agentVersion:   "2.0.0",
 
234
        args:           []string{"--version", "3.2.0"},
 
235
        expectErr:      "cannot upgrade a 2.0.0 model with a 4.0.2 client",
 
236
}, {
 
237
        about:          "minor version downgrade to incompatible version",
 
238
        tools:          []string{"3.2.0-quantal-amd64"},
 
239
        currentVersion: "3.2.0-quantal-amd64",
 
240
        agentVersion:   "3.3-dev0",
 
241
        args:           []string{"--version", "3.2.0"},
 
242
        expectErr:      "cannot change version from 3.3-dev0 to 3.2.0",
 
243
}, {
 
244
        about:          "nothing available",
 
245
        currentVersion: "2.0.0-quantal-amd64",
 
246
        agentVersion:   "2.0.0",
 
247
        expectVersion:  "2.0.0",
 
248
}, {
 
249
        about:          "nothing available 2",
 
250
        currentVersion: "2.0.0-quantal-amd64",
 
251
        tools:          []string{"3.2.0-quantal-amd64"},
 
252
        agentVersion:   "2.0.0",
 
253
        expectVersion:  "2.0.0",
 
254
}, {
 
255
        about:          "upload with default series",
 
256
        currentVersion: "2.2.0-quantal-amd64",
 
257
        agentVersion:   "2.0.0",
 
258
        args:           []string{"--upload-tools"},
 
259
        expectVersion:  "2.2.0.1",
 
260
        expectUploaded: []string{"2.2.0.1-quantal-amd64", "2.2.0.1-%LTS%-amd64", "2.2.0.1-raring-amd64"},
 
261
}, {
 
262
        about:          "upload with explicit version",
 
263
        currentVersion: "2.2.0-quantal-amd64",
 
264
        agentVersion:   "2.0.0",
 
265
        args:           []string{"--upload-tools", "--version", "2.7.3"},
 
266
        expectVersion:  "2.7.3.1",
 
267
        expectUploaded: []string{"2.7.3.1-quantal-amd64", "2.7.3.1-%LTS%-amd64", "2.7.3.1-raring-amd64"},
 
268
}, {
 
269
        about:          "upload dev version, currently on release version",
 
270
        currentVersion: "2.1.0-quantal-amd64",
 
271
        agentVersion:   "2.0.0",
 
272
        args:           []string{"--upload-tools"},
 
273
        expectVersion:  "2.1.0.1",
 
274
        expectUploaded: []string{"2.1.0.1-quantal-amd64", "2.1.0.1-%LTS%-amd64", "2.1.0.1-raring-amd64"},
 
275
}, {
 
276
        about:          "upload bumps version when necessary",
 
277
        tools:          []string{"2.4.6-quantal-amd64", "2.4.8-quantal-amd64"},
 
278
        currentVersion: "2.4.6-quantal-amd64",
 
279
        agentVersion:   "2.4.0",
 
280
        args:           []string{"--upload-tools"},
 
281
        expectVersion:  "2.4.6.1",
 
282
        expectUploaded: []string{"2.4.6.1-quantal-amd64", "2.4.6.1-%LTS%-amd64", "2.4.6.1-raring-amd64"},
 
283
}, {
 
284
        about:          "upload re-bumps version when necessary",
 
285
        tools:          []string{"2.4.6-quantal-amd64", "2.4.6.2-saucy-i386", "2.4.8-quantal-amd64"},
 
286
        currentVersion: "2.4.6-quantal-amd64",
 
287
        agentVersion:   "2.4.6.2",
 
288
        args:           []string{"--upload-tools"},
 
289
        expectVersion:  "2.4.6.3",
 
290
        expectUploaded: []string{"2.4.6.3-quantal-amd64", "2.4.6.3-%LTS%-amd64", "2.4.6.3-raring-amd64"},
 
291
}, {
 
292
        about:          "upload with explicit version bumps when necessary",
 
293
        currentVersion: "2.2.0-quantal-amd64",
 
294
        tools:          []string{"2.7.3.1-quantal-amd64"},
 
295
        agentVersion:   "2.0.0",
 
296
        args:           []string{"--upload-tools", "--version", "2.7.3"},
 
297
        expectVersion:  "2.7.3.2",
 
298
        expectUploaded: []string{"2.7.3.2-quantal-amd64", "2.7.3.2-%LTS%-amd64", "2.7.3.2-raring-amd64"},
 
299
}, {
 
300
        about:          "latest supported stable release",
 
301
        tools:          []string{"1.21.3-quantal-amd64", "1.22.1-quantal-amd64"},
 
302
        currentVersion: "1.22.1-quantal-amd64",
 
303
        agentVersion:   "1.20.14",
 
304
        expectVersion:  "1.21.3",
 
305
}}
 
306
 
 
307
func (s *UpgradeJujuSuite) TestUpgradeJuju(c *gc.C) {
 
308
        for i, test := range upgradeJujuTests {
 
309
                c.Logf("\ntest %d: %s", i, test.about)
 
310
                s.Reset(c)
 
311
                tools.DefaultBaseURL = ""
 
312
 
 
313
                // Set up apparent CLI version and initialize the command.
 
314
                current := version.MustParseBinary(test.currentVersion)
 
315
                s.PatchValue(&jujuversion.Current, current.Number)
 
316
                s.PatchValue(&arch.HostArch, func() string { return current.Arch })
 
317
                s.PatchValue(&series.HostSeries, func() string { return current.Series })
 
318
                com := newUpgradeJujuCommand(test.upgradeMap)
 
319
                if err := coretesting.InitCommand(com, test.args); err != nil {
 
320
                        if test.expectInitErr != "" {
 
321
                                c.Check(err, gc.ErrorMatches, test.expectInitErr)
 
322
                        } else {
 
323
                                c.Check(err, jc.ErrorIsNil)
 
324
                        }
 
325
                        continue
 
326
                }
 
327
 
 
328
                // Set up state and environ, and run the command.
 
329
                toolsDir := c.MkDir()
 
330
                updateAttrs := map[string]interface{}{
 
331
                        "agent-version":      test.agentVersion,
 
332
                        "agent-metadata-url": "file://" + toolsDir + "/tools",
 
333
                }
 
334
                err := s.State.UpdateModelConfig(updateAttrs, nil, nil)
 
335
                c.Assert(err, jc.ErrorIsNil)
 
336
                versions := make([]version.Binary, len(test.tools))
 
337
                for i, v := range test.tools {
 
338
                        versions[i] = version.MustParseBinary(v)
 
339
                }
 
340
                if len(versions) > 0 {
 
341
                        stor, err := filestorage.NewFileStorageWriter(toolsDir)
 
342
                        c.Assert(err, jc.ErrorIsNil)
 
343
                        envtesting.MustUploadFakeToolsVersions(stor, s.Environ.Config().AgentStream(), versions...)
 
344
                }
 
345
 
 
346
                err = com.Run(coretesting.Context(c))
 
347
                if test.expectErr != "" {
 
348
                        c.Check(err, gc.ErrorMatches, test.expectErr)
 
349
                        continue
 
350
                } else if !c.Check(err, jc.ErrorIsNil) {
 
351
                        continue
 
352
                }
 
353
 
 
354
                // Check expected changes to environ/state.
 
355
                cfg, err := s.State.ModelConfig()
 
356
                c.Check(err, jc.ErrorIsNil)
 
357
                agentVersion, ok := cfg.AgentVersion()
 
358
                c.Check(ok, jc.IsTrue)
 
359
                c.Check(agentVersion, gc.Equals, version.MustParse(test.expectVersion))
 
360
 
 
361
                for _, uploaded := range test.expectUploaded {
 
362
                        // Substitute latest LTS for placeholder in expected series for uploaded tools
 
363
                        uploaded = strings.Replace(uploaded, "%LTS%", series.LatestLts(), 1)
 
364
                        vers := version.MustParseBinary(uploaded)
 
365
                        s.checkToolsUploaded(c, vers, agentVersion)
 
366
                }
 
367
        }
 
368
}
 
369
 
 
370
func (s *UpgradeJujuSuite) checkToolsUploaded(c *gc.C, vers version.Binary, agentVersion version.Number) {
 
371
        storage, err := s.State.ToolsStorage()
 
372
        c.Assert(err, jc.ErrorIsNil)
 
373
        defer storage.Close()
 
374
        _, r, err := storage.Open(vers.String())
 
375
        if !c.Check(err, jc.ErrorIsNil) {
 
376
                return
 
377
        }
 
378
        data, err := ioutil.ReadAll(r)
 
379
        r.Close()
 
380
        c.Check(err, jc.ErrorIsNil)
 
381
        expectContent := version.Binary{
 
382
                Number: agentVersion,
 
383
                Arch:   arch.HostArch(),
 
384
                Series: series.HostSeries(),
 
385
        }
 
386
        checkToolsContent(c, data, "jujud contents "+expectContent.String())
 
387
}
 
388
 
 
389
func checkToolsContent(c *gc.C, data []byte, uploaded string) {
 
390
        zr, err := gzip.NewReader(bytes.NewReader(data))
 
391
        c.Check(err, jc.ErrorIsNil)
 
392
        defer zr.Close()
 
393
        tr := tar.NewReader(zr)
 
394
        found := false
 
395
        for {
 
396
                hdr, err := tr.Next()
 
397
                if err == io.EOF {
 
398
                        break
 
399
                }
 
400
                c.Check(err, jc.ErrorIsNil)
 
401
                if strings.ContainsAny(hdr.Name, "/\\") {
 
402
                        c.Fail()
 
403
                }
 
404
                if hdr.Typeflag != tar.TypeReg {
 
405
                        c.Fail()
 
406
                }
 
407
                content, err := ioutil.ReadAll(tr)
 
408
                c.Check(err, jc.ErrorIsNil)
 
409
                c.Check(string(content), gc.Equals, uploaded)
 
410
                found = true
 
411
        }
 
412
        c.Check(found, jc.IsTrue)
 
413
}
 
414
 
 
415
// JujuConnSuite very helpfully uploads some default
 
416
// tools to the environment's storage. We don't want
 
417
// 'em there; but we do want a consistent default-series
 
418
// in the environment state.
 
419
func (s *UpgradeJujuSuite) Reset(c *gc.C) {
 
420
        s.JujuConnSuite.Reset(c)
 
421
        envtesting.RemoveTools(c, s.DefaultToolsStorage, s.Environ.Config().AgentStream())
 
422
        updateAttrs := map[string]interface{}{
 
423
                "default-series": "raring",
 
424
                "agent-version":  "1.2.3",
 
425
        }
 
426
        err := s.State.UpdateModelConfig(updateAttrs, nil, nil)
 
427
        c.Assert(err, jc.ErrorIsNil)
 
428
        s.PatchValue(&sync.BuildToolsTarball, toolstesting.GetMockBuildTools(c))
 
429
 
 
430
        // Set API host ports so FindTools works.
 
431
        hostPorts := [][]network.HostPort{
 
432
                network.NewHostPorts(1234, "0.1.2.3"),
 
433
        }
 
434
        err = s.State.SetAPIHostPorts(hostPorts)
 
435
        c.Assert(err, jc.ErrorIsNil)
 
436
 
 
437
        s.CmdBlockHelper = cmdcommon.NewCmdBlockHelper(s.APIState)
 
438
        c.Assert(s.CmdBlockHelper, gc.NotNil)
 
439
        s.AddCleanup(func(*gc.C) { s.CmdBlockHelper.Close() })
 
440
}
 
441
 
 
442
func (s *UpgradeJujuSuite) TestUpgradeJujuWithRealUpload(c *gc.C) {
 
443
        s.Reset(c)
 
444
        s.PatchValue(&jujuversion.Current, version.MustParse("1.99.99"))
 
445
        cmd := newUpgradeJujuCommand(map[int]version.Number{2: version.MustParse("1.99.99")})
 
446
        _, err := coretesting.RunCommand(c, cmd, "--upload-tools")
 
447
        c.Assert(err, jc.ErrorIsNil)
 
448
        vers := version.Binary{
 
449
                Number: jujuversion.Current,
 
450
                Arch:   arch.HostArch(),
 
451
                Series: series.HostSeries(),
 
452
        }
 
453
        vers.Build = 1
 
454
        s.checkToolsUploaded(c, vers, vers.Number)
 
455
}
 
456
 
 
457
func (s *UpgradeJujuSuite) TestBlockUpgradeJujuWithRealUpload(c *gc.C) {
 
458
        s.Reset(c)
 
459
        s.PatchValue(&jujuversion.Current, version.MustParse("1.99.99"))
 
460
        cmd := newUpgradeJujuCommand(map[int]version.Number{2: version.MustParse("1.99.99")})
 
461
        // Block operation
 
462
        s.BlockAllChanges(c, "TestBlockUpgradeJujuWithRealUpload")
 
463
        _, err := coretesting.RunCommand(c, cmd, "--upload-tools")
 
464
        s.AssertBlocked(c, err, ".*TestBlockUpgradeJujuWithRealUpload.*")
 
465
}
 
466
 
 
467
func (s *UpgradeJujuSuite) TestFailUploadOnNonController(c *gc.C) {
 
468
        fakeAPI := &fakeUpgradeJujuAPINoState{
 
469
                name:           "dummy-model",
 
470
                uuid:           "deadbeef-0000-400d-8000-4b1d0d06f00d",
 
471
                controllerUUID: "deadbeef-0bad-400d-8000-4b1d0d06f00d",
 
472
        }
 
473
        s.PatchValue(&getUpgradeJujuAPI, func(*upgradeJujuCommand) (upgradeJujuAPI, error) {
 
474
                return fakeAPI, nil
 
475
        })
 
476
        s.PatchValue(&getModelConfigAPI, func(*upgradeJujuCommand) (modelConfigAPI, error) {
 
477
                return fakeAPI, nil
 
478
        })
 
479
        cmd := newUpgradeJujuCommand(nil)
 
480
        _, err := coretesting.RunCommand(c, cmd, "--upload-tools", "-m", "dummy-model")
 
481
        c.Assert(err, gc.ErrorMatches, "--upload-tools can only be used with the controller model")
 
482
}
 
483
 
 
484
type DryRunTest struct {
 
485
        about             string
 
486
        cmdArgs           []string
 
487
        tools             []string
 
488
        currentVersion    string
 
489
        agentVersion      string
 
490
        expectedCmdOutput string
 
491
}
 
492
 
 
493
func (s *UpgradeJujuSuite) TestUpgradeDryRun(c *gc.C) {
 
494
        tests := []DryRunTest{
 
495
                {
 
496
                        about:          "dry run outputs and doesn't change anything when uploading tools",
 
497
                        cmdArgs:        []string{"--upload-tools", "--dry-run"},
 
498
                        tools:          []string{"2.1.0-quantal-amd64", "2.1.2-quantal-i386", "2.1.3-quantal-amd64", "2.1-dev1-quantal-amd64", "2.2.3-quantal-amd64"},
 
499
                        currentVersion: "2.1.3-quantal-amd64",
 
500
                        agentVersion:   "2.0.0",
 
501
                        expectedCmdOutput: `available tools:
 
502
    2.1.3-quantal-amd64
 
503
best version:
 
504
    2.1.3
 
505
upgrade to this version by running
 
506
    juju upgrade-juju --version="2.1.3"
 
507
`,
 
508
                },
 
509
                {
 
510
                        about:          "dry run outputs and doesn't change anything",
 
511
                        cmdArgs:        []string{"--dry-run"},
 
512
                        tools:          []string{"2.1.0-quantal-amd64", "2.1.2-quantal-i386", "2.1.3-quantal-amd64", "2.1-dev1-quantal-amd64", "2.2.3-quantal-amd64"},
 
513
                        currentVersion: "2.0.0-quantal-amd64",
 
514
                        agentVersion:   "2.0.0",
 
515
                        expectedCmdOutput: `available tools:
 
516
    2.1-dev1-quantal-amd64
 
517
    2.1.0-quantal-amd64
 
518
    2.1.2-quantal-i386
 
519
    2.1.3-quantal-amd64
 
520
    2.2.3-quantal-amd64
 
521
best version:
 
522
    2.1.3
 
523
upgrade to this version by running
 
524
    juju upgrade-juju --version="2.1.3"
 
525
`,
 
526
                },
 
527
                {
 
528
                        about:          "dry run ignores unknown series",
 
529
                        cmdArgs:        []string{"--dry-run"},
 
530
                        tools:          []string{"2.1.0-quantal-amd64", "2.1.2-quantal-i386", "2.1.3-quantal-amd64", "1.2.3-myawesomeseries-amd64"},
 
531
                        currentVersion: "2.0.0-quantal-amd64",
 
532
                        agentVersion:   "2.0.0",
 
533
                        expectedCmdOutput: `available tools:
 
534
    2.1.0-quantal-amd64
 
535
    2.1.2-quantal-i386
 
536
    2.1.3-quantal-amd64
 
537
best version:
 
538
    2.1.3
 
539
upgrade to this version by running
 
540
    juju upgrade-juju --version="2.1.3"
 
541
`,
 
542
                },
 
543
        }
 
544
 
 
545
        for i, test := range tests {
 
546
                c.Logf("\ntest %d: %s", i, test.about)
 
547
                s.Reset(c)
 
548
                tools.DefaultBaseURL = ""
 
549
 
 
550
                s.setUpEnvAndTools(c, test.currentVersion, test.agentVersion, test.tools)
 
551
 
 
552
                com := newUpgradeJujuCommand(nil)
 
553
                err := coretesting.InitCommand(com, test.cmdArgs)
 
554
                c.Assert(err, jc.ErrorIsNil)
 
555
 
 
556
                ctx := coretesting.Context(c)
 
557
                err = com.Run(ctx)
 
558
                c.Assert(err, jc.ErrorIsNil)
 
559
 
 
560
                // Check agent version doesn't change
 
561
                cfg, err := s.State.ModelConfig()
 
562
                c.Assert(err, jc.ErrorIsNil)
 
563
                agentVer, ok := cfg.AgentVersion()
 
564
                c.Assert(ok, jc.IsTrue)
 
565
                c.Assert(agentVer, gc.Equals, version.MustParse(test.agentVersion))
 
566
                output := coretesting.Stderr(ctx)
 
567
                c.Assert(output, gc.Equals, test.expectedCmdOutput)
 
568
        }
 
569
}
 
570
 
 
571
func (s *UpgradeJujuSuite) setUpEnvAndTools(c *gc.C, currentVersion string, agentVersion string, tools []string) {
 
572
        current := version.MustParseBinary(currentVersion)
 
573
        s.PatchValue(&jujuversion.Current, current.Number)
 
574
        s.PatchValue(&arch.HostArch, func() string { return current.Arch })
 
575
        s.PatchValue(&series.HostSeries, func() string { return current.Series })
 
576
 
 
577
        toolsDir := c.MkDir()
 
578
        updateAttrs := map[string]interface{}{
 
579
                "agent-version":      agentVersion,
 
580
                "agent-metadata-url": "file://" + toolsDir + "/tools",
 
581
        }
 
582
 
 
583
        err := s.State.UpdateModelConfig(updateAttrs, nil, nil)
 
584
        c.Assert(err, jc.ErrorIsNil)
 
585
        versions := make([]version.Binary, len(tools))
 
586
        for i, v := range tools {
 
587
                versions[i], err = version.ParseBinary(v)
 
588
                if err != nil {
 
589
                        c.Assert(err, jc.Satisfies, series.IsUnknownOSForSeriesError)
 
590
                }
 
591
        }
 
592
        if len(versions) > 0 {
 
593
                stor, err := filestorage.NewFileStorageWriter(toolsDir)
 
594
                c.Assert(err, jc.ErrorIsNil)
 
595
                envtesting.MustUploadFakeToolsVersions(stor, s.Environ.Config().AgentStream(), versions...)
 
596
        }
 
597
}
 
598
 
 
599
func (s *UpgradeJujuSuite) TestUpgradesDifferentMajor(c *gc.C) {
 
600
        toolsList49Only := `available tools:
 
601
    4.9.0-trusty-amd64
 
602
best version:
 
603
    4.9.0
 
604
`
 
605
        tests := []struct {
 
606
                about             string
 
607
                cmdArgs           []string
 
608
                tools             []string
 
609
                currentVersion    string
 
610
                agentVersion      string
 
611
                expectedVersion   string
 
612
                expectedCmdOutput string
 
613
                expectedLogOutput string
 
614
                excludedLogOutput string
 
615
                expectedErr       string
 
616
                upgradeMap        map[int]version.Number
 
617
        }{{
 
618
                about:             "upgrade previous major to latest previous major",
 
619
                tools:             []string{"5.0.1-trusty-amd64", "4.9.0-trusty-amd64"},
 
620
                currentVersion:    "5.0.0-trusty-amd64",
 
621
                agentVersion:      "4.8.5",
 
622
                expectedVersion:   "4.9.0",
 
623
                expectedCmdOutput: toolsList49Only,
 
624
                expectedLogOutput: `.*version 4.9.0 incompatible with this client \(5.0.0\).*started upgrade to 4.9.0.*`,
 
625
        }, {
 
626
                about:             "upgrade previous major to latest previous major --dry-run still warns",
 
627
                tools:             []string{"5.0.1-trusty-amd64", "4.9.0-trusty-amd64"},
 
628
                currentVersion:    "5.0.1-trusty-amd64",
 
629
                agentVersion:      "4.8.5",
 
630
                expectedVersion:   "4.9.0",
 
631
                expectedCmdOutput: toolsList49Only,
 
632
                expectedLogOutput: `.*version 4.9.0 incompatible with this client \(5.0.1\).*started upgrade to 4.9.0.*`,
 
633
        }, {
 
634
                about:             "upgrade previous major to latest previous major with --version",
 
635
                cmdArgs:           []string{"--version=4.9.0"},
 
636
                tools:             []string{"5.0.2-trusty-amd64", "4.9.0-trusty-amd64", "4.8.0-trusty-amd64"},
 
637
                currentVersion:    "5.0.2-trusty-amd64",
 
638
                agentVersion:      "4.7.5",
 
639
                expectedVersion:   "4.9.0",
 
640
                expectedCmdOutput: toolsList49Only,
 
641
                expectedLogOutput: `.*version 4.9.0 incompatible with this client \(5.0.2\).*started upgrade to 4.9.0.*`,
 
642
        }, {
 
643
                about:             "can upgrade lower major version to current major version at minimum level",
 
644
                cmdArgs:           []string{"--version=6.0.5"},
 
645
                tools:             []string{"6.0.5-trusty-amd64", "5.9.9-trusty-amd64"},
 
646
                currentVersion:    "6.0.0-trusty-amd64",
 
647
                agentVersion:      "5.9.8",
 
648
                expectedVersion:   "6.0.5",
 
649
                excludedLogOutput: `incompatible with this client (6.0.0)`,
 
650
                upgradeMap:        map[int]version.Number{6: version.MustParse("5.9.8")},
 
651
        }, {
 
652
                about:             "can upgrade lower major version to current major version above minimum level",
 
653
                cmdArgs:           []string{"--version=6.0.5"},
 
654
                tools:             []string{"6.0.5-trusty-amd64", "5.11.0-trusty-amd64"},
 
655
                currentVersion:    "6.0.1-trusty-amd64",
 
656
                agentVersion:      "5.10.8",
 
657
                expectedVersion:   "6.0.5",
 
658
                excludedLogOutput: `incompatible with this client (6.0.1)`,
 
659
                upgradeMap:        map[int]version.Number{6: version.MustParse("5.9.8")},
 
660
        }, {
 
661
                about:           "can upgrade current to next major version",
 
662
                cmdArgs:         []string{"--version=6.0.5"},
 
663
                tools:           []string{"6.0.5-trusty-amd64", "5.11.0-trusty-amd64"},
 
664
                currentVersion:  "5.10.8-trusty-amd64",
 
665
                agentVersion:    "5.10.8",
 
666
                expectedVersion: "6.0.5",
 
667
                upgradeMap:      map[int]version.Number{6: version.MustParse("5.9.8")},
 
668
        }, {
 
669
                about:             "upgrade fails if not at minimum version",
 
670
                cmdArgs:           []string{"--version=7.0.1"},
 
671
                tools:             []string{"7.0.1-trusty-amd64"},
 
672
                currentVersion:    "7.0.1-trusty-amd64",
 
673
                agentVersion:      "6.0.0",
 
674
                expectedVersion:   "6.0.0",
 
675
                expectedCmdOutput: "upgrades to a new major version must first go through 6.7.8\n",
 
676
                expectedErr:       "unable to upgrade to requested version",
 
677
                upgradeMap:        map[int]version.Number{7: version.MustParse("6.7.8")},
 
678
        }, {
 
679
                about:             "upgrade fails if not a minor of 0",
 
680
                cmdArgs:           []string{"--version=7.1.1"},
 
681
                tools:             []string{"7.0.1-trusty-amd64", "7.1.1-trusty-amd64"},
 
682
                currentVersion:    "7.0.1-trusty-amd64",
 
683
                agentVersion:      "6.7.8",
 
684
                expectedVersion:   "6.7.8",
 
685
                expectedCmdOutput: "upgrades to 7.1.1 must first go through juju 7.0\n",
 
686
                expectedErr:       "unable to upgrade to requested version",
 
687
                upgradeMap:        map[int]version.Number{7: version.MustParse("6.7.8")},
 
688
        }, {
 
689
                about:           "upgrade fails if not at minimum version and not a minor of 0",
 
690
                cmdArgs:         []string{"--version=7.1.1"},
 
691
                tools:           []string{"7.0.1-trusty-amd64", "7.1.1-trusty-amd64"},
 
692
                currentVersion:  "7.0.1-trusty-amd64",
 
693
                agentVersion:    "6.0.0",
 
694
                expectedVersion: "6.0.0",
 
695
                expectedCmdOutput: "upgrades to 7.1.1 must first go through juju 7.0\n" +
 
696
                        "upgrades to a new major version must first go through 6.7.8\n",
 
697
                expectedErr: "unable to upgrade to requested version",
 
698
                upgradeMap:  map[int]version.Number{7: version.MustParse("6.7.8")},
 
699
        }}
 
700
        for i, test := range tests {
 
701
                c.Logf("\ntest %d: %s", i, test.about)
 
702
                s.Reset(c)
 
703
                tools.DefaultBaseURL = ""
 
704
 
 
705
                s.setUpEnvAndTools(c, test.currentVersion, test.agentVersion, test.tools)
 
706
 
 
707
                com := newUpgradeJujuCommand(test.upgradeMap)
 
708
                err := coretesting.InitCommand(com, test.cmdArgs)
 
709
                c.Assert(err, jc.ErrorIsNil)
 
710
 
 
711
                ctx := coretesting.Context(c)
 
712
                err = com.Run(ctx)
 
713
                if test.expectedErr != "" {
 
714
                        c.Check(err, gc.ErrorMatches, test.expectedErr)
 
715
                } else if !c.Check(err, jc.ErrorIsNil) {
 
716
                        continue
 
717
                }
 
718
 
 
719
                // Check agent version doesn't change
 
720
                cfg, err := s.State.ModelConfig()
 
721
                c.Assert(err, jc.ErrorIsNil)
 
722
                agentVer, ok := cfg.AgentVersion()
 
723
                c.Assert(ok, jc.IsTrue)
 
724
                c.Check(agentVer, gc.Equals, version.MustParse(test.expectedVersion))
 
725
                output := coretesting.Stderr(ctx)
 
726
                if test.expectedCmdOutput != "" {
 
727
                        c.Check(output, gc.Equals, test.expectedCmdOutput)
 
728
                }
 
729
                if test.expectedLogOutput != "" {
 
730
                        c.Check(strings.Replace(c.GetTestLog(), "\n", " ", -1), gc.Matches, test.expectedLogOutput)
 
731
                }
 
732
                if test.excludedLogOutput != "" {
 
733
                        c.Check(c.GetTestLog(), gc.Not(jc.Contains), test.excludedLogOutput)
 
734
                }
 
735
        }
 
736
}
 
737
 
 
738
func (s *UpgradeJujuSuite) TestUpgradeUnknownSeriesInStreams(c *gc.C) {
 
739
        fakeAPI := NewFakeUpgradeJujuAPI(c, s.State)
 
740
        fakeAPI.addTools("2.1.0-weird-amd64")
 
741
        fakeAPI.patch(s)
 
742
 
 
743
        cmd := &upgradeJujuCommand{}
 
744
        err := coretesting.InitCommand(modelcmd.Wrap(cmd), []string{})
 
745
        c.Assert(err, jc.ErrorIsNil)
 
746
 
 
747
        err = modelcmd.Wrap(cmd).Run(coretesting.Context(c))
 
748
        c.Assert(err, gc.IsNil)
 
749
 
 
750
        // ensure find tools was called
 
751
        c.Assert(fakeAPI.findToolsCalled, jc.IsTrue)
 
752
        c.Assert(fakeAPI.tools, gc.DeepEquals, []string{"2.1.0-weird-amd64", fakeAPI.nextVersion.String()})
 
753
}
 
754
 
 
755
func (s *UpgradeJujuSuite) TestUpgradeInProgress(c *gc.C) {
 
756
        fakeAPI := NewFakeUpgradeJujuAPI(c, s.State)
 
757
        fakeAPI.setVersionErr = &params.Error{
 
758
                Message: "a message from the server about the problem",
 
759
                Code:    params.CodeUpgradeInProgress,
 
760
        }
 
761
        fakeAPI.patch(s)
 
762
        cmd := &upgradeJujuCommand{}
 
763
        err := coretesting.InitCommand(modelcmd.Wrap(cmd), []string{})
 
764
        c.Assert(err, jc.ErrorIsNil)
 
765
 
 
766
        err = modelcmd.Wrap(cmd).Run(coretesting.Context(c))
 
767
        c.Assert(err, gc.ErrorMatches, "a message from the server about the problem\n"+
 
768
                "\n"+
 
769
                "Please wait for the upgrade to complete or if there was a problem with\n"+
 
770
                "the last upgrade that has been resolved, consider running the\n"+
 
771
                "upgrade-juju command with the --reset-previous-upgrade flag.",
 
772
        )
 
773
}
 
774
 
 
775
func (s *UpgradeJujuSuite) TestBlockUpgradeInProgress(c *gc.C) {
 
776
        fakeAPI := NewFakeUpgradeJujuAPI(c, s.State)
 
777
        fakeAPI.setVersionErr = common.OperationBlockedError("the operation has been blocked")
 
778
        fakeAPI.patch(s)
 
779
        cmd := &upgradeJujuCommand{}
 
780
        err := coretesting.InitCommand(modelcmd.Wrap(cmd), []string{})
 
781
        c.Assert(err, jc.ErrorIsNil)
 
782
 
 
783
        // Block operation
 
784
        s.BlockAllChanges(c, "TestBlockUpgradeInProgress")
 
785
        err = modelcmd.Wrap(cmd).Run(coretesting.Context(c))
 
786
        s.AssertBlocked(c, err, ".*To unblock changes.*")
 
787
}
 
788
 
 
789
func (s *UpgradeJujuSuite) TestResetPreviousUpgrade(c *gc.C) {
 
790
        fakeAPI := NewFakeUpgradeJujuAPI(c, s.State)
 
791
        fakeAPI.patch(s)
 
792
 
 
793
        ctx := coretesting.Context(c)
 
794
        var stdin bytes.Buffer
 
795
        ctx.Stdin = &stdin
 
796
 
 
797
        run := func(answer string, expect bool, args ...string) {
 
798
                stdin.Reset()
 
799
                if answer != "" {
 
800
                        stdin.WriteString(answer)
 
801
                }
 
802
 
 
803
                fakeAPI.reset()
 
804
 
 
805
                cmd := &upgradeJujuCommand{}
 
806
                err := coretesting.InitCommand(modelcmd.Wrap(cmd),
 
807
                        append([]string{"--reset-previous-upgrade"}, args...))
 
808
                c.Assert(err, jc.ErrorIsNil)
 
809
                err = modelcmd.Wrap(cmd).Run(ctx)
 
810
                if expect {
 
811
                        c.Assert(err, jc.ErrorIsNil)
 
812
                } else {
 
813
                        c.Assert(err, gc.ErrorMatches, "previous upgrade not reset and no new upgrade triggered")
 
814
                }
 
815
 
 
816
                c.Assert(fakeAPI.abortCurrentUpgradeCalled, gc.Equals, expect)
 
817
                expectedVersion := version.Number{}
 
818
                if expect {
 
819
                        expectedVersion = fakeAPI.nextVersion.Number
 
820
                }
 
821
                c.Assert(fakeAPI.setVersionCalledWith, gc.Equals, expectedVersion)
 
822
        }
 
823
 
 
824
        const expectUpgrade = true
 
825
        const expectNoUpgrade = false
 
826
 
 
827
        // EOF on stdin - equivalent to answering no.
 
828
        run("", expectNoUpgrade)
 
829
 
 
830
        // -y on command line - no confirmation required
 
831
        run("", expectUpgrade, "-y")
 
832
 
 
833
        // --yes on command line - no confirmation required
 
834
        run("", expectUpgrade, "--yes")
 
835
 
 
836
        // various ways of saying "yes" to the prompt
 
837
        for _, answer := range []string{"y", "Y", "yes", "YES"} {
 
838
                run(answer, expectUpgrade)
 
839
        }
 
840
 
 
841
        // various ways of saying "no" to the prompt
 
842
        for _, answer := range []string{"n", "N", "no", "foo"} {
 
843
                run(answer, expectNoUpgrade)
 
844
        }
 
845
}
 
846
 
 
847
func NewFakeUpgradeJujuAPI(c *gc.C, st *state.State) *fakeUpgradeJujuAPI {
 
848
        nextVersion := version.Binary{
 
849
                Number: jujuversion.Current,
 
850
                Arch:   arch.HostArch(),
 
851
                Series: series.HostSeries(),
 
852
        }
 
853
        nextVersion.Minor++
 
854
        return &fakeUpgradeJujuAPI{
 
855
                c:           c,
 
856
                st:          st,
 
857
                nextVersion: nextVersion,
 
858
        }
 
859
}
 
860
 
 
861
type fakeUpgradeJujuAPI struct {
 
862
        c                         *gc.C
 
863
        st                        *state.State
 
864
        nextVersion               version.Binary
 
865
        setVersionErr             error
 
866
        abortCurrentUpgradeCalled bool
 
867
        setVersionCalledWith      version.Number
 
868
        tools                     []string
 
869
        findToolsCalled           bool
 
870
}
 
871
 
 
872
func (a *fakeUpgradeJujuAPI) reset() {
 
873
        a.setVersionErr = nil
 
874
        a.abortCurrentUpgradeCalled = false
 
875
        a.setVersionCalledWith = version.Number{}
 
876
        a.tools = []string{}
 
877
        a.findToolsCalled = false
 
878
}
 
879
 
 
880
func (a *fakeUpgradeJujuAPI) patch(s *UpgradeJujuSuite) {
 
881
        s.PatchValue(&getUpgradeJujuAPI, func(*upgradeJujuCommand) (upgradeJujuAPI, error) {
 
882
                return a, nil
 
883
        })
 
884
        s.PatchValue(&getModelConfigAPI, func(*upgradeJujuCommand) (modelConfigAPI, error) {
 
885
                return a, nil
 
886
        })
 
887
}
 
888
 
 
889
func (a *fakeUpgradeJujuAPI) addTools(tools ...string) {
 
890
        for _, tool := range tools {
 
891
                a.tools = append(a.tools, tool)
 
892
        }
 
893
}
 
894
 
 
895
func (a *fakeUpgradeJujuAPI) ModelGet() (map[string]interface{}, error) {
 
896
        config, err := a.st.ModelConfig()
 
897
        if err != nil {
 
898
                return make(map[string]interface{}), err
 
899
        }
 
900
        return config.AllAttrs(), nil
 
901
}
 
902
 
 
903
func (a *fakeUpgradeJujuAPI) FindTools(majorVersion, minorVersion int, series, arch string) (
 
904
        result params.FindToolsResult, err error,
 
905
) {
 
906
        a.findToolsCalled = true
 
907
        a.tools = append(a.tools, a.nextVersion.String())
 
908
        tools := toolstesting.MakeTools(a.c, a.c.MkDir(), "released", a.tools)
 
909
        return params.FindToolsResult{
 
910
                List:  tools,
 
911
                Error: nil,
 
912
        }, nil
 
913
}
 
914
 
 
915
func (a *fakeUpgradeJujuAPI) UploadTools(r io.ReadSeeker, vers version.Binary, additionalSeries ...string) (coretools.List, error) {
 
916
        panic("not implemented")
 
917
}
 
918
 
 
919
func (a *fakeUpgradeJujuAPI) AbortCurrentUpgrade() error {
 
920
        a.abortCurrentUpgradeCalled = true
 
921
        return nil
 
922
}
 
923
 
 
924
func (a *fakeUpgradeJujuAPI) SetModelAgentVersion(v version.Number) error {
 
925
        a.setVersionCalledWith = v
 
926
        return a.setVersionErr
 
927
}
 
928
 
 
929
func (a *fakeUpgradeJujuAPI) Close() error {
 
930
        return nil
 
931
}
 
932
 
 
933
// Mock an API with no state
 
934
type fakeUpgradeJujuAPINoState struct {
 
935
        upgradeJujuAPI
 
936
        name           string
 
937
        uuid           string
 
938
        controllerUUID string
 
939
}
 
940
 
 
941
func (a *fakeUpgradeJujuAPINoState) Close() error {
 
942
        return nil
 
943
}
 
944
 
 
945
func (a *fakeUpgradeJujuAPINoState) ModelGet() (map[string]interface{}, error) {
 
946
        return dummy.SampleConfig().Merge(map[string]interface{}{
 
947
                "name":            a.name,
 
948
                "uuid":            a.uuid,
 
949
                "controller-uuid": a.controllerUUID,
 
950
        }), nil
 
951
}