launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/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.1.0.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: "no next supported available", 109 tools: []string{"2.1.0-quantal-amd64", "2.1.5-quantal-i386", "2.3.3-quantal-amd64"}, 110 currentVersion: "2.0.0-quantal-amd64", 111 agentVersion: "2.0.0", 112 expectErr: "no more recent supported versions available", 113 }, { 114 about: "latest supported stable, when client is dev", 115 tools: []string{"2.1.1-quantal-amd64", "2.2.0-quantal-amd64", "2.3.0-quantal-amd64", "3.0.1-quantal-amd64"}, 116 currentVersion: "2.1.0-quantal-amd64", 117 agentVersion: "2.0.0", 118 expectVersion: "2.2.0", 119 }, { 120 about: "latest current, when agent is dev", 121 tools: []string{"2.1.1-quantal-amd64", "2.2.0-quantal-amd64", "2.3.0-quantal-amd64", "3.0.1-quantal-amd64"}, 122 currentVersion: "2.0.0-quantal-amd64", 123 agentVersion: "2.1.0", 124 expectVersion: "2.2.0", 125 }, { 126 about: "specified version", 127 tools: []string{"2.3.0-quantal-amd64"}, 128 currentVersion: "2.0.0-quantal-amd64", 129 agentVersion: "2.0.0", 130 args: []string{"--version", "2.3.0"}, 131 expectVersion: "2.3.0", 132 }, { 133 about: "specified version missing, but already set", 134 currentVersion: "3.0.0-quantal-amd64", 135 agentVersion: "3.0.0", 136 args: []string{"--version", "3.0.0"}, 137 expectVersion: "3.0.0", 138 }, { 139 about: "specified version, no tools", 140 currentVersion: "3.0.0-quantal-amd64", 141 agentVersion: "3.0.0", 142 args: []string{"--version", "3.2.0"}, 143 expectErr: "no matching tools available", 144 }, { 145 about: "specified version, no matching major version", 146 tools: []string{"4.2.0-quantal-amd64"}, 147 currentVersion: "3.0.0-quantal-amd64", 148 agentVersion: "3.0.0", 149 args: []string{"--version", "3.2.0"}, 150 expectErr: "no matching tools available", 151 }, { 152 about: "specified version, no matching minor version", 153 tools: []string{"3.4.0-quantal-amd64"}, 154 currentVersion: "3.0.0-quantal-amd64", 155 agentVersion: "3.0.0", 156 args: []string{"--version", "3.2.0"}, 157 expectErr: "no matching tools available", 158 }, { 159 about: "specified version, no matching patch version", 160 tools: []string{"3.2.5-quantal-amd64"}, 161 currentVersion: "3.0.0-quantal-amd64", 162 agentVersion: "3.0.0", 163 args: []string{"--version", "3.2.0"}, 164 expectErr: "no matching tools available", 165 }, { 166 about: "specified version, no matching build version", 167 tools: []string{"3.2.0.2-quantal-amd64"}, 168 currentVersion: "3.0.0-quantal-amd64", 169 agentVersion: "3.0.0", 170 args: []string{"--version", "3.2.0"}, 171 expectErr: "no matching tools available", 172 }, { 173 about: "major version downgrade to incompatible version", 174 tools: []string{"3.2.0-quantal-amd64"}, 175 currentVersion: "3.2.0-quantal-amd64", 176 agentVersion: "4.2.0", 177 args: []string{"--version", "3.2.0"}, 178 expectErr: "cannot change major version from 4 to 3", 179 }, { 180 about: "major version upgrade to compatible version", 181 tools: []string{"3.2.0-quantal-amd64"}, 182 currentVersion: "3.2.0-quantal-amd64", 183 agentVersion: "2.8.2", 184 args: []string{"--version", "3.2.0"}, 185 expectErr: "major version upgrades are not supported yet", 186 }, { 187 about: "nothing available", 188 currentVersion: "2.0.0-quantal-amd64", 189 agentVersion: "2.0.0", 190 expectVersion: "2.0.0", 191 }, { 192 about: "nothing available 2", 193 currentVersion: "2.0.0-quantal-amd64", 194 tools: []string{"3.2.0-quantal-amd64"}, 195 agentVersion: "2.0.0", 196 expectVersion: "2.0.0", 197 }, { 198 about: "upload with default series", 199 currentVersion: "2.2.0-quantal-amd64", 200 agentVersion: "2.0.0", 201 args: []string{"--upload-tools"}, 202 expectVersion: "2.2.0.1", 203 expectUploaded: []string{"2.2.0.1-quantal-amd64", "2.2.0.1-precise-amd64", "2.2.0.1-raring-amd64"}, 204 }, { 205 about: "upload with explicit version", 206 currentVersion: "2.2.0-quantal-amd64", 207 agentVersion: "2.0.0", 208 args: []string{"--upload-tools", "--version", "2.7.3"}, 209 expectVersion: "2.7.3.1", 210 expectUploaded: []string{"2.7.3.1-quantal-amd64", "2.7.3.1-precise-amd64", "2.7.3.1-raring-amd64"}, 211 }, { 212 about: "upload with explicit series", 213 currentVersion: "2.2.0-quantal-amd64", 214 agentVersion: "2.0.0", 215 args: []string{"--upload-tools", "--series", "raring"}, 216 expectVersion: "2.2.0.1", 217 expectUploaded: []string{"2.2.0.1-quantal-amd64", "2.2.0.1-raring-amd64"}, 218 }, { 219 about: "upload dev version, currently on release version", 220 currentVersion: "2.1.0-quantal-amd64", 221 agentVersion: "2.0.0", 222 args: []string{"--upload-tools"}, 223 expectVersion: "2.1.0.1", 224 expectUploaded: []string{"2.1.0.1-quantal-amd64", "2.1.0.1-precise-amd64", "2.1.0.1-raring-amd64"}, 225 }, { 226 about: "upload bumps version when necessary", 227 tools: []string{"2.4.6-quantal-amd64", "2.4.8-quantal-amd64"}, 228 currentVersion: "2.4.6-quantal-amd64", 229 agentVersion: "2.4.0", 230 args: []string{"--upload-tools"}, 231 expectVersion: "2.4.6.1", 232 expectUploaded: []string{"2.4.6.1-quantal-amd64", "2.4.6.1-precise-amd64", "2.4.6.1-raring-amd64"}, 233 }, { 234 about: "upload re-bumps version when necessary", 235 tools: []string{"2.4.6-quantal-amd64", "2.4.6.2-saucy-i386", "2.4.8-quantal-amd64"}, 236 currentVersion: "2.4.6-quantal-amd64", 237 agentVersion: "2.4.6.2", 238 args: []string{"--upload-tools"}, 239 expectVersion: "2.4.6.3", 240 expectUploaded: []string{"2.4.6.3-quantal-amd64", "2.4.6.3-precise-amd64", "2.4.6.3-raring-amd64"}, 241 }, { 242 about: "upload with explicit version bumps when necessary", 243 currentVersion: "2.2.0-quantal-amd64", 244 tools: []string{"2.7.3.1-quantal-amd64"}, 245 agentVersion: "2.0.0", 246 args: []string{"--upload-tools", "--version", "2.7.3"}, 247 expectVersion: "2.7.3.2", 248 expectUploaded: []string{"2.7.3.2-quantal-amd64", "2.7.3.2-precise-amd64", "2.7.3.2-raring-amd64"}, 249 }} 250 251 // mockUploadTools simulates the effect of tools.Upload, but skips the time- 252 // consuming build from source. 253 // TODO(fwereade) better factor agent/tools such that build logic is 254 // exposed and can itself be neatly mocked? 255 func mockUploadTools(stor storage.Storage, forceVersion *version.Number, series ...string) (*coretools.Tools, error) { 256 vers := version.Current 257 if forceVersion != nil { 258 vers.Number = *forceVersion 259 } 260 versions := []version.Binary{vers} 261 for _, series := range series { 262 if series != version.Current.Series { 263 newVers := vers 264 newVers.Series = series 265 versions = append(versions, newVers) 266 } 267 } 268 agentTools, err := envtesting.UploadFakeToolsVersions(stor, versions...) 269 if err != nil { 270 return nil, err 271 } 272 return agentTools[0], nil 273 } 274 275 func (s *UpgradeJujuSuite) TestUpgradeJuju(c *gc.C) { 276 s.PatchValue(&sync.Upload, mockUploadTools) 277 oldVersion := version.Current 278 defer func() { 279 version.Current = oldVersion 280 }() 281 282 for i, test := range upgradeJujuTests { 283 c.Logf("\ntest %d: %s", i, test.about) 284 s.Reset(c) 285 286 // Set up apparent CLI version and initialize the command. 287 version.Current = version.MustParseBinary(test.currentVersion) 288 com := &UpgradeJujuCommand{} 289 if err := coretesting.InitCommand(com, test.args); err != nil { 290 if test.expectInitErr != "" { 291 c.Check(err, gc.ErrorMatches, test.expectInitErr) 292 } else { 293 c.Check(err, gc.IsNil) 294 } 295 continue 296 } 297 298 // Set up state and environ, and run the command. 299 oldcfg, err := s.State.EnvironConfig() 300 c.Assert(err, gc.IsNil) 301 toolsDir := c.MkDir() 302 cfg, err := oldcfg.Apply(map[string]interface{}{ 303 "agent-version": test.agentVersion, 304 "tools-metadata-url": "file://" + toolsDir, 305 }) 306 c.Assert(err, gc.IsNil) 307 err = s.State.SetEnvironConfig(cfg, oldcfg) 308 c.Assert(err, gc.IsNil) 309 versions := make([]version.Binary, len(test.tools)) 310 for i, v := range test.tools { 311 versions[i] = version.MustParseBinary(v) 312 313 } 314 envtesting.MustUploadFakeToolsVersions(s.Conn.Environ.Storage(), versions...) 315 stor, err := filestorage.NewFileStorageWriter(toolsDir, "") 316 c.Assert(err, gc.IsNil) 317 envtesting.MustUploadFakeToolsVersions(stor, versions...) 318 err = com.Run(coretesting.Context(c)) 319 if test.expectErr != "" { 320 c.Check(err, gc.ErrorMatches, test.expectErr) 321 continue 322 } else if !c.Check(err, gc.IsNil) { 323 continue 324 } 325 326 // Check expected changes to environ/state. 327 cfg, err = s.State.EnvironConfig() 328 c.Check(err, gc.IsNil) 329 agentVersion, ok := cfg.AgentVersion() 330 c.Check(ok, gc.Equals, true) 331 c.Check(agentVersion, gc.Equals, version.MustParse(test.expectVersion)) 332 333 for _, uploaded := range test.expectUploaded { 334 vers := version.MustParseBinary(uploaded) 335 r, err := storage.Get(s.Conn.Environ.Storage(), envtools.StorageName(vers)) 336 if !c.Check(err, gc.IsNil) { 337 continue 338 } 339 data, err := ioutil.ReadAll(r) 340 r.Close() 341 c.Check(err, gc.IsNil) 342 checkToolsContent(c, data, "jujud contents "+uploaded) 343 } 344 } 345 } 346 347 func checkToolsContent(c *gc.C, data []byte, uploaded string) { 348 zr, err := gzip.NewReader(bytes.NewReader(data)) 349 c.Check(err, gc.IsNil) 350 defer zr.Close() 351 tr := tar.NewReader(zr) 352 found := false 353 for { 354 hdr, err := tr.Next() 355 if err == io.EOF { 356 break 357 } 358 c.Check(err, gc.IsNil) 359 if strings.ContainsAny(hdr.Name, "/\\") { 360 c.Fail() 361 } 362 if hdr.Typeflag != tar.TypeReg { 363 c.Fail() 364 } 365 content, err := ioutil.ReadAll(tr) 366 c.Check(err, gc.IsNil) 367 c.Check(string(content), gc.Equals, uploaded) 368 found = true 369 } 370 c.Check(found, jc.IsTrue) 371 } 372 373 // JujuConnSuite very helpfully uploads some default 374 // tools to the environment's storage. We don't want 375 // 'em there; but we do want a consistent default-series 376 // in the environment state. 377 func (s *UpgradeJujuSuite) Reset(c *gc.C) { 378 s.JujuConnSuite.Reset(c) 379 envtesting.RemoveTools(c, s.Conn.Environ.Storage()) 380 oldcfg, err := s.State.EnvironConfig() 381 c.Assert(err, gc.IsNil) 382 cfg, err := oldcfg.Apply(map[string]interface{}{ 383 "default-series": "raring", 384 "agent-version": "1.2.3", 385 }) 386 c.Assert(err, gc.IsNil) 387 err = s.State.SetEnvironConfig(cfg, oldcfg) 388 c.Assert(err, gc.IsNil) 389 } 390 391 func (s *UpgradeJujuSuite) TestUpgradeJujuWithRealUpload(c *gc.C) { 392 s.Reset(c) 393 _, err := coretesting.RunCommand(c, &UpgradeJujuCommand{}, []string{"--upload-tools"}) 394 c.Assert(err, gc.IsNil) 395 vers := version.Current 396 vers.Build = 1 397 tools, err := envtools.FindInstanceTools(s.Conn.Environ, vers.Number, vers.Series, &vers.Arch) 398 c.Assert(err, gc.IsNil) 399 c.Assert(len(tools), gc.Equals, 1) 400 }