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