github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/cmd/juju/commands/upgradejuju_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 jc "github.com/juju/testing/checkers" 15 gc "gopkg.in/check.v1" 16 17 "github.com/juju/juju/apiserver/common" 18 "github.com/juju/juju/apiserver/params" 19 apiservertesting "github.com/juju/juju/apiserver/testing" 20 "github.com/juju/juju/cmd/envcmd" 21 "github.com/juju/juju/environs/config" 22 "github.com/juju/juju/environs/filestorage" 23 "github.com/juju/juju/environs/sync" 24 envtesting "github.com/juju/juju/environs/testing" 25 "github.com/juju/juju/environs/tools" 26 toolstesting "github.com/juju/juju/environs/tools/testing" 27 jujutesting "github.com/juju/juju/juju/testing" 28 "github.com/juju/juju/network" 29 _ "github.com/juju/juju/provider/dummy" 30 "github.com/juju/juju/state" 31 coretesting "github.com/juju/juju/testing" 32 coretools "github.com/juju/juju/tools" 33 "github.com/juju/juju/version" 34 ) 35 36 type UpgradeJujuSuite struct { 37 jujutesting.JujuConnSuite 38 39 resources *common.Resources 40 authoriser apiservertesting.FakeAuthorizer 41 42 toolsDir string 43 CmdBlockHelper 44 } 45 46 func (s *UpgradeJujuSuite) SetUpTest(c *gc.C) { 47 s.JujuConnSuite.SetUpTest(c) 48 s.resources = common.NewResources() 49 s.authoriser = apiservertesting.FakeAuthorizer{ 50 Tag: s.AdminUserTag(c), 51 } 52 53 s.CmdBlockHelper = NewCmdBlockHelper(s.APIState) 54 c.Assert(s.CmdBlockHelper, gc.NotNil) 55 s.AddCleanup(func(*gc.C) { s.CmdBlockHelper.Close() }) 56 } 57 58 var _ = gc.Suite(&UpgradeJujuSuite{}) 59 60 var upgradeJujuTests = []struct { 61 about string 62 tools []string 63 currentVersion string 64 agentVersion string 65 66 args []string 67 expectInitErr string 68 expectErr string 69 expectVersion string 70 expectUploaded []string 71 }{{ 72 about: "unwanted extra argument", 73 currentVersion: "1.0.0-quantal-amd64", 74 args: []string{"foo"}, 75 expectInitErr: "unrecognized args:.*", 76 }, { 77 about: "removed arg --dev specified", 78 currentVersion: "1.0.0-quantal-amd64", 79 args: []string{"--dev"}, 80 expectInitErr: "flag provided but not defined: --dev", 81 }, { 82 about: "invalid --version value", 83 currentVersion: "1.0.0-quantal-amd64", 84 args: []string{"--version", "invalid-version"}, 85 expectInitErr: "invalid version .*", 86 }, { 87 about: "just major version, no minor specified", 88 currentVersion: "4.2.0-quantal-amd64", 89 args: []string{"--version", "4"}, 90 expectInitErr: `invalid version "4"`, 91 }, { 92 about: "major version upgrade to incompatible version", 93 currentVersion: "2.0.0-quantal-amd64", 94 args: []string{"--version", "5.2.0"}, 95 expectInitErr: "cannot upgrade to version incompatible with CLI", 96 }, { 97 about: "major version downgrade to incompatible version", 98 currentVersion: "4.2.0-quantal-amd64", 99 args: []string{"--version", "3.2.0"}, 100 expectInitErr: "cannot upgrade to version incompatible with CLI", 101 }, { 102 about: "invalid --series", 103 currentVersion: "4.2.0-quantal-amd64", 104 args: []string{"--series", "precise&quantal"}, 105 expectInitErr: `invalid value "precise&quantal" for flag --series: .*`, 106 }, { 107 about: "--series without --upload-tools", 108 currentVersion: "4.2.0-quantal-amd64", 109 args: []string{"--series", "precise,quantal"}, 110 expectInitErr: "--series requires --upload-tools", 111 }, { 112 about: "--upload-tools with inappropriate version 1", 113 currentVersion: "4.2.0-quantal-amd64", 114 args: []string{"--upload-tools", "--version", "3.1.0"}, 115 expectInitErr: "cannot upgrade to version incompatible with CLI", 116 }, { 117 about: "--upload-tools with inappropriate version 2", 118 currentVersion: "3.2.7-quantal-amd64", 119 args: []string{"--upload-tools", "--version", "3.2.8.4"}, 120 expectInitErr: "cannot specify build number when uploading tools", 121 }, { 122 about: "latest supported stable release", 123 tools: []string{"2.1.0-quantal-amd64", "2.1.2-quantal-i386", "2.1.3-quantal-amd64", "2.1-dev1-quantal-amd64"}, 124 currentVersion: "2.0.0-quantal-amd64", 125 agentVersion: "2.0.0", 126 expectVersion: "2.1.3", 127 }, { 128 about: "latest current release", 129 tools: []string{"2.0.5-quantal-amd64", "2.0.1-quantal-i386", "2.3.3-quantal-amd64"}, 130 currentVersion: "2.0.0-quantal-amd64", 131 agentVersion: "2.0.0", 132 expectVersion: "2.0.5", 133 }, { 134 about: "latest current release matching CLI, major version", 135 tools: []string{"3.2.0-quantal-amd64"}, 136 currentVersion: "3.2.0-quantal-amd64", 137 agentVersion: "2.8.2", 138 expectVersion: "3.2.0", 139 }, { 140 about: "latest current release matching CLI, major version, no matching major tools", 141 tools: []string{"2.8.2-quantal-amd64"}, 142 currentVersion: "3.2.0-quantal-amd64", 143 agentVersion: "2.8.2", 144 expectErr: "no matching tools available", 145 }, { 146 about: "latest current release matching CLI, major version, no matching tools", 147 tools: []string{"3.3.0-quantal-amd64"}, 148 currentVersion: "3.2.0-quantal-amd64", 149 agentVersion: "2.8.2", 150 expectErr: "no compatible tools available", 151 }, { 152 about: "no next supported available", 153 tools: []string{"2.2.0-quantal-amd64", "2.2.5-quantal-i386", "2.3.3-quantal-amd64", "2.1-dev1-quantal-amd64"}, 154 currentVersion: "2.0.0-quantal-amd64", 155 agentVersion: "2.0.0", 156 expectErr: "no more recent supported versions available", 157 }, { 158 about: "latest supported stable, when client is dev", 159 tools: []string{"2.1-dev1-quantal-amd64", "2.1.0-quantal-amd64", "2.3-dev0-quantal-amd64", "3.0.1-quantal-amd64"}, 160 currentVersion: "2.1-dev0-quantal-amd64", 161 agentVersion: "2.0.0", 162 expectVersion: "2.1.0", 163 }, { 164 about: "latest current, when agent is dev", 165 tools: []string{"2.1-dev1-quantal-amd64", "2.2.0-quantal-amd64", "2.3-dev0-quantal-amd64", "3.0.1-quantal-amd64"}, 166 currentVersion: "2.0.0-quantal-amd64", 167 agentVersion: "2.1-dev0", 168 expectVersion: "2.2.0", 169 }, { 170 about: "specified version", 171 tools: []string{"2.3-dev0-quantal-amd64"}, 172 currentVersion: "2.0.0-quantal-amd64", 173 agentVersion: "2.0.0", 174 args: []string{"--version", "2.3-dev0"}, 175 expectVersion: "2.3-dev0", 176 }, { 177 about: "specified major version", 178 tools: []string{"3.2.0-quantal-amd64"}, 179 currentVersion: "3.2.0-quantal-amd64", 180 agentVersion: "2.8.2", 181 args: []string{"--version", "3.2.0"}, 182 expectVersion: "3.2.0", 183 }, { 184 about: "specified version missing, but already set", 185 currentVersion: "3.0.0-quantal-amd64", 186 agentVersion: "3.0.0", 187 args: []string{"--version", "3.0.0"}, 188 expectVersion: "3.0.0", 189 }, { 190 about: "specified version, no tools", 191 currentVersion: "3.0.0-quantal-amd64", 192 agentVersion: "3.0.0", 193 args: []string{"--version", "3.2.0"}, 194 expectErr: "no tools available", 195 }, { 196 about: "specified version, no matching major version", 197 tools: []string{"4.2.0-quantal-amd64"}, 198 currentVersion: "3.0.0-quantal-amd64", 199 agentVersion: "3.0.0", 200 args: []string{"--version", "3.2.0"}, 201 expectErr: "no matching tools available", 202 }, { 203 about: "specified version, no matching minor version", 204 tools: []string{"3.4.0-quantal-amd64"}, 205 currentVersion: "3.0.0-quantal-amd64", 206 agentVersion: "3.0.0", 207 args: []string{"--version", "3.2.0"}, 208 expectErr: "no matching tools available", 209 }, { 210 about: "specified version, no matching patch version", 211 tools: []string{"3.2.5-quantal-amd64"}, 212 currentVersion: "3.0.0-quantal-amd64", 213 agentVersion: "3.0.0", 214 args: []string{"--version", "3.2.0"}, 215 expectErr: "no matching tools available", 216 }, { 217 about: "specified version, no matching build version", 218 tools: []string{"3.2.0.2-quantal-amd64"}, 219 currentVersion: "3.0.0-quantal-amd64", 220 agentVersion: "3.0.0", 221 args: []string{"--version", "3.2.0"}, 222 expectErr: "no matching tools available", 223 }, { 224 about: "major version downgrade to incompatible version", 225 tools: []string{"3.2.0-quantal-amd64"}, 226 currentVersion: "3.2.0-quantal-amd64", 227 agentVersion: "4.2.0", 228 args: []string{"--version", "3.2.0"}, 229 expectErr: "cannot change version from 4.2.0 to 3.2.0", 230 }, { 231 about: "minor version downgrade to incompatible version", 232 tools: []string{"3.2.0-quantal-amd64"}, 233 currentVersion: "3.2.0-quantal-amd64", 234 agentVersion: "3.3-dev0", 235 args: []string{"--version", "3.2.0"}, 236 expectErr: "cannot change version from 3.3-dev0 to 3.2.0", 237 }, { 238 about: "nothing available", 239 currentVersion: "2.0.0-quantal-amd64", 240 agentVersion: "2.0.0", 241 expectVersion: "2.0.0", 242 }, { 243 about: "nothing available 2", 244 currentVersion: "2.0.0-quantal-amd64", 245 tools: []string{"3.2.0-quantal-amd64"}, 246 agentVersion: "2.0.0", 247 expectVersion: "2.0.0", 248 }, { 249 about: "upload with default series", 250 currentVersion: "2.2.0-quantal-amd64", 251 agentVersion: "2.0.0", 252 args: []string{"--upload-tools"}, 253 expectVersion: "2.2.0.1", 254 expectUploaded: []string{"2.2.0.1-quantal-amd64", "2.2.0.1-%LTS%-amd64", "2.2.0.1-raring-amd64"}, 255 }, { 256 about: "upload with explicit version", 257 currentVersion: "2.2.0-quantal-amd64", 258 agentVersion: "2.0.0", 259 args: []string{"--upload-tools", "--version", "2.7.3"}, 260 expectVersion: "2.7.3.1", 261 expectUploaded: []string{"2.7.3.1-quantal-amd64", "2.7.3.1-%LTS%-amd64", "2.7.3.1-raring-amd64"}, 262 }, { 263 about: "upload with explicit series", 264 currentVersion: "2.2.0-quantal-amd64", 265 agentVersion: "2.0.0", 266 args: []string{"--upload-tools", "--series", "raring"}, 267 expectVersion: "2.2.0.1", 268 expectUploaded: []string{"2.2.0.1-quantal-amd64", "2.2.0.1-raring-amd64"}, 269 }, { 270 about: "upload dev version, currently on release version", 271 currentVersion: "2.1.0-quantal-amd64", 272 agentVersion: "2.0.0", 273 args: []string{"--upload-tools"}, 274 expectVersion: "2.1.0.1", 275 expectUploaded: []string{"2.1.0.1-quantal-amd64", "2.1.0.1-%LTS%-amd64", "2.1.0.1-raring-amd64"}, 276 }, { 277 about: "upload bumps version when necessary", 278 tools: []string{"2.4.6-quantal-amd64", "2.4.8-quantal-amd64"}, 279 currentVersion: "2.4.6-quantal-amd64", 280 agentVersion: "2.4.0", 281 args: []string{"--upload-tools"}, 282 expectVersion: "2.4.6.1", 283 expectUploaded: []string{"2.4.6.1-quantal-amd64", "2.4.6.1-%LTS%-amd64", "2.4.6.1-raring-amd64"}, 284 }, { 285 about: "upload re-bumps version when necessary", 286 tools: []string{"2.4.6-quantal-amd64", "2.4.6.2-saucy-i386", "2.4.8-quantal-amd64"}, 287 currentVersion: "2.4.6-quantal-amd64", 288 agentVersion: "2.4.6.2", 289 args: []string{"--upload-tools"}, 290 expectVersion: "2.4.6.3", 291 expectUploaded: []string{"2.4.6.3-quantal-amd64", "2.4.6.3-%LTS%-amd64", "2.4.6.3-raring-amd64"}, 292 }, { 293 about: "upload with explicit version bumps when necessary", 294 currentVersion: "2.2.0-quantal-amd64", 295 tools: []string{"2.7.3.1-quantal-amd64"}, 296 agentVersion: "2.0.0", 297 args: []string{"--upload-tools", "--version", "2.7.3"}, 298 expectVersion: "2.7.3.2", 299 expectUploaded: []string{"2.7.3.2-quantal-amd64", "2.7.3.2-%LTS%-amd64", "2.7.3.2-raring-amd64"}, 300 }, { 301 about: "latest supported stable release", 302 tools: []string{"1.21.3-quantal-amd64", "1.22.1-quantal-amd64"}, 303 currentVersion: "1.22.1-quantal-amd64", 304 agentVersion: "1.20.14", 305 expectVersion: "1.21.3", 306 }} 307 308 func (s *UpgradeJujuSuite) TestUpgradeJuju(c *gc.C) { 309 oldVersion := version.Current 310 defer func() { 311 version.Current = oldVersion 312 }() 313 314 for i, test := range upgradeJujuTests { 315 c.Logf("\ntest %d: %s", i, test.about) 316 s.Reset(c) 317 tools.DefaultBaseURL = "" 318 319 // Set up apparent CLI version and initialize the command. 320 version.Current = version.MustParseBinary(test.currentVersion) 321 com := &UpgradeJujuCommand{} 322 if err := coretesting.InitCommand(envcmd.Wrap(com), test.args); err != nil { 323 if test.expectInitErr != "" { 324 c.Check(err, gc.ErrorMatches, test.expectInitErr) 325 } else { 326 c.Check(err, jc.ErrorIsNil) 327 } 328 continue 329 } 330 331 // Set up state and environ, and run the command. 332 toolsDir := c.MkDir() 333 updateAttrs := map[string]interface{}{ 334 "agent-version": test.agentVersion, 335 "agent-metadata-url": "file://" + toolsDir + "/tools", 336 } 337 err := s.State.UpdateEnvironConfig(updateAttrs, nil, nil) 338 c.Assert(err, jc.ErrorIsNil) 339 versions := make([]version.Binary, len(test.tools)) 340 for i, v := range test.tools { 341 versions[i] = version.MustParseBinary(v) 342 } 343 if len(versions) > 0 { 344 stor, err := filestorage.NewFileStorageWriter(toolsDir) 345 c.Assert(err, jc.ErrorIsNil) 346 envtesting.MustUploadFakeToolsVersions(stor, s.Environ.Config().AgentStream(), versions...) 347 } 348 349 err = com.Run(coretesting.Context(c)) 350 if test.expectErr != "" { 351 c.Check(err, gc.ErrorMatches, test.expectErr) 352 continue 353 } else if !c.Check(err, jc.ErrorIsNil) { 354 continue 355 } 356 357 // Check expected changes to environ/state. 358 cfg, err := s.State.EnvironConfig() 359 c.Check(err, jc.ErrorIsNil) 360 agentVersion, ok := cfg.AgentVersion() 361 c.Check(ok, jc.IsTrue) 362 c.Check(agentVersion, gc.Equals, version.MustParse(test.expectVersion)) 363 364 for _, uploaded := range test.expectUploaded { 365 // Substitute latest LTS for placeholder in expected series for uploaded tools 366 uploaded = strings.Replace(uploaded, "%LTS%", config.LatestLtsSeries(), 1) 367 vers := version.MustParseBinary(uploaded) 368 s.checkToolsUploaded(c, vers, agentVersion) 369 } 370 } 371 } 372 373 func (s *UpgradeJujuSuite) checkToolsUploaded(c *gc.C, vers version.Binary, agentVersion version.Number) { 374 storage, err := s.State.ToolsStorage() 375 c.Assert(err, jc.ErrorIsNil) 376 defer storage.Close() 377 _, r, err := storage.Tools(vers) 378 if !c.Check(err, jc.ErrorIsNil) { 379 return 380 } 381 data, err := ioutil.ReadAll(r) 382 r.Close() 383 c.Check(err, jc.ErrorIsNil) 384 expectContent := version.Current 385 expectContent.Number = agentVersion 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.UpdateEnvironConfig(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 = 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 cmd := envcmd.Wrap(&UpgradeJujuCommand{}) 445 _, err := coretesting.RunCommand(c, cmd, "--upload-tools") 446 c.Assert(err, jc.ErrorIsNil) 447 vers := version.Current 448 vers.Build = 1 449 s.checkToolsUploaded(c, vers, vers.Number) 450 } 451 452 func (s *UpgradeJujuSuite) TestBlockUpgradeJujuWithRealUpload(c *gc.C) { 453 s.Reset(c) 454 cmd := envcmd.Wrap(&UpgradeJujuCommand{}) 455 // Block operation 456 s.BlockAllChanges(c, "TestBlockUpgradeJujuWithRealUpload") 457 _, err := coretesting.RunCommand(c, cmd, "--upload-tools") 458 s.AssertBlocked(c, err, ".*TestBlockUpgradeJujuWithRealUpload.*") 459 } 460 461 type DryRunTest struct { 462 about string 463 cmdArgs []string 464 tools []string 465 currentVersion string 466 agentVersion string 467 expectedCmdOutput string 468 } 469 470 func (s *UpgradeJujuSuite) TestUpgradeDryRun(c *gc.C) { 471 tests := []DryRunTest{ 472 { 473 about: "dry run outputs and doesn't change anything when uploading tools", 474 cmdArgs: []string{"--upload-tools", "--dry-run"}, 475 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"}, 476 currentVersion: "2.0.0-quantal-amd64", 477 agentVersion: "2.0.0", 478 expectedCmdOutput: `available tools: 479 2.1-dev1-quantal-amd64 480 2.1.0-quantal-amd64 481 2.1.2-quantal-i386 482 2.1.3-quantal-amd64 483 2.2.3-quantal-amd64 484 best version: 485 2.1.3 486 upgrade to this version by running 487 juju upgrade-juju --version="2.1.3" 488 `, 489 }, 490 { 491 about: "dry run outputs and doesn't change anything", 492 cmdArgs: []string{"--dry-run"}, 493 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"}, 494 currentVersion: "2.0.0-quantal-amd64", 495 agentVersion: "2.0.0", 496 expectedCmdOutput: `available tools: 497 2.1-dev1-quantal-amd64 498 2.1.0-quantal-amd64 499 2.1.2-quantal-i386 500 2.1.3-quantal-amd64 501 2.2.3-quantal-amd64 502 best version: 503 2.1.3 504 upgrade to this version by running 505 juju upgrade-juju --version="2.1.3" 506 `, 507 }, 508 { 509 about: "dry run ignores unknown series", 510 cmdArgs: []string{"--dry-run"}, 511 tools: []string{"2.1.0-quantal-amd64", "2.1.2-quantal-i386", "2.1.3-quantal-amd64", "1.2.3-myawesomeseries-amd64"}, 512 currentVersion: "2.0.0-quantal-amd64", 513 agentVersion: "2.0.0", 514 expectedCmdOutput: `available tools: 515 2.1.0-quantal-amd64 516 2.1.2-quantal-i386 517 2.1.3-quantal-amd64 518 best version: 519 2.1.3 520 upgrade to this version by running 521 juju upgrade-juju --version="2.1.3" 522 `, 523 }, 524 } 525 526 for i, test := range tests { 527 c.Logf("\ntest %d: %s", i, test.about) 528 s.Reset(c) 529 tools.DefaultBaseURL = "" 530 531 s.PatchValue(&version.Current, version.MustParseBinary(test.currentVersion)) 532 com := &UpgradeJujuCommand{} 533 err := coretesting.InitCommand(envcmd.Wrap(com), test.cmdArgs) 534 c.Assert(err, jc.ErrorIsNil) 535 toolsDir := c.MkDir() 536 updateAttrs := map[string]interface{}{ 537 "agent-version": test.agentVersion, 538 "agent-metadata-url": "file://" + toolsDir + "/tools", 539 } 540 541 err = s.State.UpdateEnvironConfig(updateAttrs, nil, nil) 542 c.Assert(err, jc.ErrorIsNil) 543 versions := make([]version.Binary, len(test.tools)) 544 for i, v := range test.tools { 545 versions[i], err = version.ParseBinary(v) 546 if err != nil { 547 c.Assert(err, jc.Satisfies, version.IsUnknownOSForSeriesError) 548 } 549 } 550 if len(versions) > 0 { 551 stor, err := filestorage.NewFileStorageWriter(toolsDir) 552 c.Assert(err, jc.ErrorIsNil) 553 envtesting.MustUploadFakeToolsVersions(stor, s.Environ.Config().AgentStream(), versions...) 554 } 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.EnvironConfig() 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) TestUpgradeUnknownSeriesInStreams(c *gc.C) { 572 fakeAPI := NewFakeUpgradeJujuAPI(c, s.State) 573 fakeAPI.addTools("2.1.0-weird-amd64") 574 fakeAPI.patch(s) 575 576 cmd := &UpgradeJujuCommand{} 577 err := coretesting.InitCommand(envcmd.Wrap(cmd), []string{}) 578 c.Assert(err, jc.ErrorIsNil) 579 580 err = cmd.Run(coretesting.Context(c)) 581 c.Assert(err, gc.IsNil) 582 583 // ensure find tools was called 584 c.Assert(fakeAPI.findToolsCalled, jc.IsTrue) 585 c.Assert(fakeAPI.tools, gc.DeepEquals, []string{"2.1.0-weird-amd64", fakeAPI.nextVersion.String()}) 586 } 587 588 func (s *UpgradeJujuSuite) TestUpgradeInProgress(c *gc.C) { 589 fakeAPI := NewFakeUpgradeJujuAPI(c, s.State) 590 fakeAPI.setVersionErr = ¶ms.Error{ 591 Message: "a message from the server about the problem", 592 Code: params.CodeUpgradeInProgress, 593 } 594 fakeAPI.patch(s) 595 cmd := &UpgradeJujuCommand{} 596 err := coretesting.InitCommand(envcmd.Wrap(cmd), []string{}) 597 c.Assert(err, jc.ErrorIsNil) 598 599 err = cmd.Run(coretesting.Context(c)) 600 c.Assert(err, gc.ErrorMatches, "a message from the server about the problem\n"+ 601 "\n"+ 602 "Please wait for the upgrade to complete or if there was a problem with\n"+ 603 "the last upgrade that has been resolved, consider running the\n"+ 604 "upgrade-juju command with the --reset-previous-upgrade flag.", 605 ) 606 } 607 608 func (s *UpgradeJujuSuite) TestBlockUpgradeInProgress(c *gc.C) { 609 fakeAPI := NewFakeUpgradeJujuAPI(c, s.State) 610 fakeAPI.setVersionErr = common.ErrOperationBlocked("The operation has been blocked.") 611 fakeAPI.patch(s) 612 cmd := &UpgradeJujuCommand{} 613 err := coretesting.InitCommand(envcmd.Wrap(cmd), []string{}) 614 c.Assert(err, jc.ErrorIsNil) 615 616 // Block operation 617 s.BlockAllChanges(c, "TestBlockUpgradeInProgress") 618 err = cmd.Run(coretesting.Context(c)) 619 s.AssertBlocked(c, err, ".*To unblock changes.*") 620 } 621 622 func (s *UpgradeJujuSuite) TestResetPreviousUpgrade(c *gc.C) { 623 fakeAPI := NewFakeUpgradeJujuAPI(c, s.State) 624 fakeAPI.patch(s) 625 626 ctx := coretesting.Context(c) 627 var stdin bytes.Buffer 628 ctx.Stdin = &stdin 629 630 run := func(answer string, expect bool, args ...string) { 631 stdin.Reset() 632 if answer != "" { 633 stdin.WriteString(answer) 634 } 635 636 fakeAPI.reset() 637 638 cmd := &UpgradeJujuCommand{} 639 err := coretesting.InitCommand(envcmd.Wrap(cmd), 640 append([]string{"--reset-previous-upgrade"}, args...)) 641 c.Assert(err, jc.ErrorIsNil) 642 err = cmd.Run(ctx) 643 if expect { 644 c.Assert(err, jc.ErrorIsNil) 645 } else { 646 c.Assert(err, gc.ErrorMatches, "previous upgrade not reset and no new upgrade triggered") 647 } 648 649 c.Assert(fakeAPI.abortCurrentUpgradeCalled, gc.Equals, expect) 650 expectedVersion := version.Number{} 651 if expect { 652 expectedVersion = fakeAPI.nextVersion.Number 653 } 654 c.Assert(fakeAPI.setVersionCalledWith, gc.Equals, expectedVersion) 655 } 656 657 const expectUpgrade = true 658 const expectNoUpgrade = false 659 660 // EOF on stdin - equivalent to answering no. 661 run("", expectNoUpgrade) 662 663 // -y on command line - no confirmation required 664 run("", expectUpgrade, "-y") 665 666 // --yes on command line - no confirmation required 667 run("", expectUpgrade, "--yes") 668 669 // various ways of saying "yes" to the prompt 670 for _, answer := range []string{"y", "Y", "yes", "YES"} { 671 run(answer, expectUpgrade) 672 } 673 674 // various ways of saying "no" to the prompt 675 for _, answer := range []string{"n", "N", "no", "foo"} { 676 run(answer, expectNoUpgrade) 677 } 678 } 679 680 func NewFakeUpgradeJujuAPI(c *gc.C, st *state.State) *fakeUpgradeJujuAPI { 681 nextVersion := version.Current 682 nextVersion.Minor++ 683 return &fakeUpgradeJujuAPI{ 684 c: c, 685 st: st, 686 nextVersion: nextVersion, 687 } 688 } 689 690 type fakeUpgradeJujuAPI struct { 691 c *gc.C 692 st *state.State 693 nextVersion version.Binary 694 setVersionErr error 695 abortCurrentUpgradeCalled bool 696 setVersionCalledWith version.Number 697 tools []string 698 findToolsCalled bool 699 } 700 701 func (a *fakeUpgradeJujuAPI) reset() { 702 a.setVersionErr = nil 703 a.abortCurrentUpgradeCalled = false 704 a.setVersionCalledWith = version.Number{} 705 a.tools = []string{} 706 a.findToolsCalled = false 707 } 708 709 func (a *fakeUpgradeJujuAPI) patch(s *UpgradeJujuSuite) { 710 s.PatchValue(&getUpgradeJujuAPI, func(*UpgradeJujuCommand) (upgradeJujuAPI, error) { 711 return a, nil 712 }) 713 } 714 715 func (a *fakeUpgradeJujuAPI) addTools(tools ...string) { 716 for _, tool := range tools { 717 a.tools = append(a.tools, tool) 718 } 719 } 720 721 func (a *fakeUpgradeJujuAPI) EnvironmentGet() (map[string]interface{}, error) { 722 config, err := a.st.EnvironConfig() 723 if err != nil { 724 return make(map[string]interface{}), err 725 } 726 return config.AllAttrs(), nil 727 } 728 729 func (a *fakeUpgradeJujuAPI) FindTools(majorVersion, minorVersion int, series, arch string) ( 730 result params.FindToolsResult, err error, 731 ) { 732 a.findToolsCalled = true 733 a.tools = append(a.tools, a.nextVersion.String()) 734 tools := toolstesting.MakeTools(a.c, a.c.MkDir(), "released", a.tools) 735 return params.FindToolsResult{ 736 List: tools, 737 Error: nil, 738 }, nil 739 } 740 741 func (a *fakeUpgradeJujuAPI) UploadTools(r io.Reader, vers version.Binary, additionalSeries ...string) ( 742 *coretools.Tools, error, 743 ) { 744 panic("not implemented") 745 } 746 747 func (a *fakeUpgradeJujuAPI) AbortCurrentUpgrade() error { 748 a.abortCurrentUpgradeCalled = true 749 return nil 750 } 751 752 func (a *fakeUpgradeJujuAPI) SetEnvironAgentVersion(v version.Number) error { 753 a.setVersionCalledWith = v 754 return a.setVersionErr 755 } 756 757 func (a *fakeUpgradeJujuAPI) Close() error { 758 return nil 759 }