github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/cmd/juju/upgradejuju_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package main 5 6 import ( 7 "archive/tar" 8 "bytes" 9 "compress/gzip" 10 "io" 11 "io/ioutil" 12 "strings" 13 14 gc "launchpad.net/gocheck" 15 16 "launchpad.net/juju-core/environs/filestorage" 17 "launchpad.net/juju-core/environs/storage" 18 "launchpad.net/juju-core/environs/sync" 19 envtesting "launchpad.net/juju-core/environs/testing" 20 envtools "launchpad.net/juju-core/environs/tools" 21 "launchpad.net/juju-core/juju/testing" 22 coretesting "launchpad.net/juju-core/testing" 23 jc "launchpad.net/juju-core/testing/checkers" 24 coretools "launchpad.net/juju-core/tools" 25 "launchpad.net/juju-core/version" 26 ) 27 28 type UpgradeJujuSuite struct { 29 testing.JujuConnSuite 30 } 31 32 var _ = gc.Suite(&UpgradeJujuSuite{}) 33 34 var upgradeJujuTests = []struct { 35 about string 36 tools []string 37 currentVersion string 38 agentVersion string 39 40 args []string 41 expectInitErr string 42 expectErr string 43 expectVersion string 44 expectUploaded []string 45 }{{ 46 about: "unwanted extra argument", 47 currentVersion: "1.0.0-quantal-amd64", 48 args: []string{"foo"}, 49 expectInitErr: "unrecognized args:.*", 50 }, { 51 about: "removed arg --dev specified", 52 currentVersion: "1.0.0-quantal-amd64", 53 args: []string{"--dev"}, 54 expectInitErr: "flag provided but not defined: --dev", 55 }, { 56 about: "invalid --version value", 57 currentVersion: "1.0.0-quantal-amd64", 58 args: []string{"--version", "invalid-version"}, 59 expectInitErr: "invalid version .*", 60 }, { 61 about: "just major version, no minor specified", 62 currentVersion: "4.2.0-quantal-amd64", 63 args: []string{"--version", "4"}, 64 expectInitErr: `invalid version "4"`, 65 }, { 66 about: "major version upgrade to incompatible version", 67 currentVersion: "2.0.0-quantal-amd64", 68 args: []string{"--version", "5.2.0"}, 69 expectInitErr: "cannot upgrade to version incompatible with CLI", 70 }, { 71 about: "major version downgrade to incompatible version", 72 currentVersion: "4.2.0-quantal-amd64", 73 args: []string{"--version", "3.2.0"}, 74 expectInitErr: "cannot upgrade to version incompatible with CLI", 75 }, { 76 about: "invalid --series", 77 currentVersion: "4.2.0-quantal-amd64", 78 args: []string{"--series", "precise&quantal"}, 79 expectInitErr: `invalid value "precise&quantal" for flag --series: .*`, 80 }, { 81 about: "--series without --upload-tools", 82 currentVersion: "4.2.0-quantal-amd64", 83 args: []string{"--series", "precise,quantal"}, 84 expectInitErr: "--series requires --upload-tools", 85 }, { 86 about: "--upload-tools with inappropriate version 1", 87 currentVersion: "4.2.0-quantal-amd64", 88 args: []string{"--upload-tools", "--version", "3.1.0"}, 89 expectInitErr: "cannot upgrade to version incompatible with CLI", 90 }, { 91 about: "--upload-tools with inappropriate version 2", 92 currentVersion: "3.2.7-quantal-amd64", 93 args: []string{"--upload-tools", "--version", "3.2.8.4"}, 94 expectInitErr: "cannot specify build number when uploading tools", 95 }, { 96 about: "latest supported stable release", 97 tools: []string{"2.2.0-quantal-amd64", "2.2.2-quantal-i386", "2.2.3-quantal-amd64"}, 98 currentVersion: "2.0.0-quantal-amd64", 99 agentVersion: "2.0.0", 100 expectVersion: "2.2.3", 101 }, { 102 about: "latest current release", 103 tools: []string{"2.0.5-quantal-amd64", "2.0.1-quantal-i386", "2.3.3-quantal-amd64"}, 104 currentVersion: "2.0.0-quantal-amd64", 105 agentVersion: "2.0.0", 106 expectVersion: "2.0.5", 107 }, { 108 about: "latest current release matching CLI, major version", 109 tools: []string{"3.2.0-quantal-amd64"}, 110 currentVersion: "3.2.0-quantal-amd64", 111 agentVersion: "2.8.2", 112 expectVersion: "3.2.0", 113 }, { 114 about: "latest current release matching CLI, major version, no matching major tools", 115 tools: []string{"2.8.2-quantal-amd64"}, 116 currentVersion: "3.2.0-quantal-amd64", 117 agentVersion: "2.8.2", 118 expectErr: "no matching tools available", 119 }, { 120 about: "latest current release matching CLI, major version, no matching tools", 121 tools: []string{"3.3.0-quantal-amd64"}, 122 currentVersion: "3.2.0-quantal-amd64", 123 agentVersion: "2.8.2", 124 expectErr: "no compatible tools available", 125 }, { 126 about: "no next supported available", 127 tools: []string{"2.1.0-quantal-amd64", "2.1.5-quantal-i386", "2.3.3-quantal-amd64"}, 128 currentVersion: "2.0.0-quantal-amd64", 129 agentVersion: "2.0.0", 130 expectErr: "no more recent supported versions available", 131 }, { 132 about: "latest supported stable, when client is dev", 133 tools: []string{"2.1.1-quantal-amd64", "2.2.0-quantal-amd64", "2.3.0-quantal-amd64", "3.0.1-quantal-amd64"}, 134 currentVersion: "2.1.0-quantal-amd64", 135 agentVersion: "2.0.0", 136 expectVersion: "2.2.0", 137 }, { 138 about: "latest current, when agent is dev", 139 tools: []string{"2.1.1-quantal-amd64", "2.2.0-quantal-amd64", "2.3.0-quantal-amd64", "3.0.1-quantal-amd64"}, 140 currentVersion: "2.0.0-quantal-amd64", 141 agentVersion: "2.1.0", 142 expectVersion: "2.2.0", 143 }, { 144 about: "specified version", 145 tools: []string{"2.3.0-quantal-amd64"}, 146 currentVersion: "2.0.0-quantal-amd64", 147 agentVersion: "2.0.0", 148 args: []string{"--version", "2.3.0"}, 149 expectVersion: "2.3.0", 150 }, { 151 about: "specified major version", 152 tools: []string{"3.2.0-quantal-amd64"}, 153 currentVersion: "3.2.0-quantal-amd64", 154 agentVersion: "2.8.2", 155 args: []string{"--version", "3.2.0"}, 156 expectVersion: "3.2.0", 157 }, { 158 about: "specified version missing, but already set", 159 currentVersion: "3.0.0-quantal-amd64", 160 agentVersion: "3.0.0", 161 args: []string{"--version", "3.0.0"}, 162 expectVersion: "3.0.0", 163 }, { 164 about: "specified version, no tools", 165 currentVersion: "3.0.0-quantal-amd64", 166 agentVersion: "3.0.0", 167 args: []string{"--version", "3.2.0"}, 168 expectErr: "no matching tools available", 169 }, { 170 about: "specified version, no matching major version", 171 tools: []string{"4.2.0-quantal-amd64"}, 172 currentVersion: "3.0.0-quantal-amd64", 173 agentVersion: "3.0.0", 174 args: []string{"--version", "3.2.0"}, 175 expectErr: "no matching tools available", 176 }, { 177 about: "specified version, no matching minor version", 178 tools: []string{"3.4.0-quantal-amd64"}, 179 currentVersion: "3.0.0-quantal-amd64", 180 agentVersion: "3.0.0", 181 args: []string{"--version", "3.2.0"}, 182 expectErr: "no matching tools available", 183 }, { 184 about: "specified version, no matching patch version", 185 tools: []string{"3.2.5-quantal-amd64"}, 186 currentVersion: "3.0.0-quantal-amd64", 187 agentVersion: "3.0.0", 188 args: []string{"--version", "3.2.0"}, 189 expectErr: "no matching tools available", 190 }, { 191 about: "specified version, no matching build version", 192 tools: []string{"3.2.0.2-quantal-amd64"}, 193 currentVersion: "3.0.0-quantal-amd64", 194 agentVersion: "3.0.0", 195 args: []string{"--version", "3.2.0"}, 196 expectErr: "no matching tools available", 197 }, { 198 about: "major version downgrade to incompatible version", 199 tools: []string{"3.2.0-quantal-amd64"}, 200 currentVersion: "3.2.0-quantal-amd64", 201 agentVersion: "4.2.0", 202 args: []string{"--version", "3.2.0"}, 203 expectErr: "cannot change version from 4.2.0 to 3.2.0", 204 }, { 205 about: "minor version downgrade to incompatible version", 206 tools: []string{"3.2.0-quantal-amd64"}, 207 currentVersion: "3.2.0-quantal-amd64", 208 agentVersion: "3.3.0", 209 args: []string{"--version", "3.2.0"}, 210 expectErr: "cannot change version from 3.3.0 to 3.2.0", 211 }, { 212 about: "nothing available", 213 currentVersion: "2.0.0-quantal-amd64", 214 agentVersion: "2.0.0", 215 expectVersion: "2.0.0", 216 }, { 217 about: "nothing available 2", 218 currentVersion: "2.0.0-quantal-amd64", 219 tools: []string{"3.2.0-quantal-amd64"}, 220 agentVersion: "2.0.0", 221 expectVersion: "2.0.0", 222 }, { 223 about: "upload with default series", 224 currentVersion: "2.2.0-quantal-amd64", 225 agentVersion: "2.0.0", 226 args: []string{"--upload-tools"}, 227 expectVersion: "2.2.0.1", 228 expectUploaded: []string{"2.2.0.1-quantal-amd64", "2.2.0.1-precise-amd64", "2.2.0.1-raring-amd64"}, 229 }, { 230 about: "upload with explicit version", 231 currentVersion: "2.2.0-quantal-amd64", 232 agentVersion: "2.0.0", 233 args: []string{"--upload-tools", "--version", "2.7.3"}, 234 expectVersion: "2.7.3.1", 235 expectUploaded: []string{"2.7.3.1-quantal-amd64", "2.7.3.1-precise-amd64", "2.7.3.1-raring-amd64"}, 236 }, { 237 about: "upload with explicit series", 238 currentVersion: "2.2.0-quantal-amd64", 239 agentVersion: "2.0.0", 240 args: []string{"--upload-tools", "--series", "raring"}, 241 expectVersion: "2.2.0.1", 242 expectUploaded: []string{"2.2.0.1-quantal-amd64", "2.2.0.1-raring-amd64"}, 243 }, { 244 about: "upload dev version, currently on release version", 245 currentVersion: "2.1.0-quantal-amd64", 246 agentVersion: "2.0.0", 247 args: []string{"--upload-tools"}, 248 expectVersion: "2.1.0.1", 249 expectUploaded: []string{"2.1.0.1-quantal-amd64", "2.1.0.1-precise-amd64", "2.1.0.1-raring-amd64"}, 250 }, { 251 about: "upload bumps version when necessary", 252 tools: []string{"2.4.6-quantal-amd64", "2.4.8-quantal-amd64"}, 253 currentVersion: "2.4.6-quantal-amd64", 254 agentVersion: "2.4.0", 255 args: []string{"--upload-tools"}, 256 expectVersion: "2.4.6.1", 257 expectUploaded: []string{"2.4.6.1-quantal-amd64", "2.4.6.1-precise-amd64", "2.4.6.1-raring-amd64"}, 258 }, { 259 about: "upload re-bumps version when necessary", 260 tools: []string{"2.4.6-quantal-amd64", "2.4.6.2-saucy-i386", "2.4.8-quantal-amd64"}, 261 currentVersion: "2.4.6-quantal-amd64", 262 agentVersion: "2.4.6.2", 263 args: []string{"--upload-tools"}, 264 expectVersion: "2.4.6.3", 265 expectUploaded: []string{"2.4.6.3-quantal-amd64", "2.4.6.3-precise-amd64", "2.4.6.3-raring-amd64"}, 266 }, { 267 about: "upload with explicit version bumps when necessary", 268 currentVersion: "2.2.0-quantal-amd64", 269 tools: []string{"2.7.3.1-quantal-amd64"}, 270 agentVersion: "2.0.0", 271 args: []string{"--upload-tools", "--version", "2.7.3"}, 272 expectVersion: "2.7.3.2", 273 expectUploaded: []string{"2.7.3.2-quantal-amd64", "2.7.3.2-precise-amd64", "2.7.3.2-raring-amd64"}, 274 }} 275 276 // mockUploadTools simulates the effect of tools.Upload, but skips the time- 277 // consuming build from source. 278 // TODO(fwereade) better factor agent/tools such that build logic is 279 // exposed and can itself be neatly mocked? 280 func mockUploadTools(stor storage.Storage, forceVersion *version.Number, series ...string) (*coretools.Tools, error) { 281 vers := version.Current 282 if forceVersion != nil { 283 vers.Number = *forceVersion 284 } 285 versions := []version.Binary{vers} 286 for _, series := range series { 287 if series != version.Current.Series { 288 newVers := vers 289 newVers.Series = series 290 versions = append(versions, newVers) 291 } 292 } 293 agentTools, err := envtesting.UploadFakeToolsVersions(stor, versions...) 294 if err != nil { 295 return nil, err 296 } 297 return agentTools[0], nil 298 } 299 300 func (s *UpgradeJujuSuite) TestUpgradeJuju(c *gc.C) { 301 s.PatchValue(&sync.Upload, mockUploadTools) 302 oldVersion := version.Current 303 defer func() { 304 version.Current = oldVersion 305 }() 306 307 for i, test := range upgradeJujuTests { 308 c.Logf("\ntest %d: %s", i, test.about) 309 s.Reset(c) 310 311 // Set up apparent CLI version and initialize the command. 312 version.Current = version.MustParseBinary(test.currentVersion) 313 com := &UpgradeJujuCommand{} 314 if err := coretesting.InitCommand(com, test.args); err != nil { 315 if test.expectInitErr != "" { 316 c.Check(err, gc.ErrorMatches, test.expectInitErr) 317 } else { 318 c.Check(err, gc.IsNil) 319 } 320 continue 321 } 322 323 // Set up state and environ, and run the command. 324 oldcfg, err := s.State.EnvironConfig() 325 c.Assert(err, gc.IsNil) 326 toolsDir := c.MkDir() 327 cfg, err := oldcfg.Apply(map[string]interface{}{ 328 "agent-version": test.agentVersion, 329 "tools-metadata-url": "file://" + toolsDir, 330 }) 331 c.Assert(err, gc.IsNil) 332 err = s.State.SetEnvironConfig(cfg, oldcfg) 333 c.Assert(err, gc.IsNil) 334 versions := make([]version.Binary, len(test.tools)) 335 for i, v := range test.tools { 336 versions[i] = version.MustParseBinary(v) 337 338 } 339 envtesting.MustUploadFakeToolsVersions(s.Conn.Environ.Storage(), versions...) 340 stor, err := filestorage.NewFileStorageWriter(toolsDir) 341 c.Assert(err, gc.IsNil) 342 envtesting.MustUploadFakeToolsVersions(stor, versions...) 343 err = com.Run(coretesting.Context(c)) 344 if test.expectErr != "" { 345 c.Check(err, gc.ErrorMatches, test.expectErr) 346 continue 347 } else if !c.Check(err, gc.IsNil) { 348 continue 349 } 350 351 // Check expected changes to environ/state. 352 cfg, err = s.State.EnvironConfig() 353 c.Check(err, gc.IsNil) 354 agentVersion, ok := cfg.AgentVersion() 355 c.Check(ok, gc.Equals, true) 356 c.Check(agentVersion, gc.Equals, version.MustParse(test.expectVersion)) 357 358 for _, uploaded := range test.expectUploaded { 359 vers := version.MustParseBinary(uploaded) 360 r, err := storage.Get(s.Conn.Environ.Storage(), envtools.StorageName(vers)) 361 if !c.Check(err, gc.IsNil) { 362 continue 363 } 364 data, err := ioutil.ReadAll(r) 365 r.Close() 366 c.Check(err, gc.IsNil) 367 checkToolsContent(c, data, "jujud contents "+uploaded) 368 } 369 } 370 } 371 372 func checkToolsContent(c *gc.C, data []byte, uploaded string) { 373 zr, err := gzip.NewReader(bytes.NewReader(data)) 374 c.Check(err, gc.IsNil) 375 defer zr.Close() 376 tr := tar.NewReader(zr) 377 found := false 378 for { 379 hdr, err := tr.Next() 380 if err == io.EOF { 381 break 382 } 383 c.Check(err, gc.IsNil) 384 if strings.ContainsAny(hdr.Name, "/\\") { 385 c.Fail() 386 } 387 if hdr.Typeflag != tar.TypeReg { 388 c.Fail() 389 } 390 content, err := ioutil.ReadAll(tr) 391 c.Check(err, gc.IsNil) 392 c.Check(string(content), gc.Equals, uploaded) 393 found = true 394 } 395 c.Check(found, jc.IsTrue) 396 } 397 398 // JujuConnSuite very helpfully uploads some default 399 // tools to the environment's storage. We don't want 400 // 'em there; but we do want a consistent default-series 401 // in the environment state. 402 func (s *UpgradeJujuSuite) Reset(c *gc.C) { 403 s.JujuConnSuite.Reset(c) 404 envtesting.RemoveTools(c, s.Conn.Environ.Storage()) 405 oldcfg, err := s.State.EnvironConfig() 406 c.Assert(err, gc.IsNil) 407 cfg, err := oldcfg.Apply(map[string]interface{}{ 408 "default-series": "raring", 409 "agent-version": "1.2.3", 410 }) 411 c.Assert(err, gc.IsNil) 412 err = s.State.SetEnvironConfig(cfg, oldcfg) 413 c.Assert(err, gc.IsNil) 414 } 415 416 func (s *UpgradeJujuSuite) TestUpgradeJujuWithRealUpload(c *gc.C) { 417 s.Reset(c) 418 _, err := coretesting.RunCommand(c, &UpgradeJujuCommand{}, []string{"--upload-tools"}) 419 c.Assert(err, gc.IsNil) 420 vers := version.Current 421 vers.Build = 1 422 tools, err := envtools.FindInstanceTools(s.Conn.Environ, vers.Number, vers.Series, &vers.Arch) 423 c.Assert(err, gc.IsNil) 424 c.Assert(len(tools), gc.Equals, 1) 425 }