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