github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/worker/upgrader/upgrader_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package upgrader_test 5 6 import ( 7 "fmt" 8 "os" 9 "path/filepath" 10 stdtesting "testing" 11 "time" 12 13 "github.com/juju/errors" 14 jc "github.com/juju/testing/checkers" 15 "github.com/juju/utils" 16 "github.com/juju/utils/arch" 17 "github.com/juju/utils/series" 18 "github.com/juju/utils/symlink" 19 "github.com/juju/version" 20 gc "gopkg.in/check.v1" 21 "gopkg.in/juju/names.v2" 22 23 "github.com/juju/juju/agent" 24 agenttools "github.com/juju/juju/agent/tools" 25 "github.com/juju/juju/api" 26 envtesting "github.com/juju/juju/environs/testing" 27 envtools "github.com/juju/juju/environs/tools" 28 jujutesting "github.com/juju/juju/juju/testing" 29 "github.com/juju/juju/state" 30 statetesting "github.com/juju/juju/state/testing" 31 coretesting "github.com/juju/juju/testing" 32 coretools "github.com/juju/juju/tools" 33 jujuversion "github.com/juju/juju/version" 34 "github.com/juju/juju/worker/gate" 35 "github.com/juju/juju/worker/upgrader" 36 ) 37 38 func TestPackage(t *stdtesting.T) { 39 coretesting.MgoTestPackage(t) 40 } 41 42 type UpgraderSuite struct { 43 jujutesting.JujuConnSuite 44 45 machine *state.Machine 46 state api.Connection 47 oldRetryAfter func() <-chan time.Time 48 confVersion version.Number 49 upgradeStepsComplete gate.Lock 50 initialCheckComplete gate.Lock 51 } 52 53 type AllowedTargetVersionSuite struct{} 54 55 var _ = gc.Suite(&UpgraderSuite{}) 56 var _ = gc.Suite(&AllowedTargetVersionSuite{}) 57 58 func (s *UpgraderSuite) SetUpTest(c *gc.C) { 59 s.JujuConnSuite.SetUpTest(c) 60 // s.machine needs to have IsManager() so that it can get the actual 61 // current revision to upgrade to. 62 s.state, s.machine = s.OpenAPIAsNewMachine(c, state.JobManageModel) 63 // Capture the value of RetryAfter, and use that captured 64 // value in the cleanup lambda. 65 oldRetryAfter := *upgrader.RetryAfter 66 s.AddCleanup(func(*gc.C) { 67 *upgrader.RetryAfter = oldRetryAfter 68 }) 69 s.upgradeStepsComplete = gate.NewLock() 70 s.initialCheckComplete = gate.NewLock() 71 } 72 73 func (s *UpgraderSuite) patchVersion(v version.Binary) { 74 s.PatchValue(&arch.HostArch, func() string { return v.Arch }) 75 s.PatchValue(&series.HostSeries, func() string { return v.Series }) 76 s.PatchValue(&jujuversion.Current, v.Number) 77 } 78 79 type mockConfig struct { 80 agent.Config 81 tag names.Tag 82 datadir string 83 version version.Number 84 } 85 86 func (mock *mockConfig) Tag() names.Tag { 87 return mock.tag 88 } 89 90 func (mock *mockConfig) DataDir() string { 91 return mock.datadir 92 } 93 94 func agentConfig(tag names.Tag, datadir string) agent.Config { 95 return &mockConfig{ 96 tag: tag, 97 datadir: datadir, 98 } 99 } 100 101 func (s *UpgraderSuite) makeUpgrader(c *gc.C) *upgrader.Upgrader { 102 w, err := upgrader.NewAgentUpgrader( 103 s.state.Upgrader(), 104 agentConfig(s.machine.Tag(), s.DataDir()), 105 s.confVersion, 106 s.upgradeStepsComplete, 107 s.initialCheckComplete, 108 ) 109 c.Assert(err, jc.ErrorIsNil) 110 return w 111 } 112 113 func (s *UpgraderSuite) TestUpgraderSetsTools(c *gc.C) { 114 vers := version.MustParseBinary("5.4.3-precise-amd64") 115 err := statetesting.SetAgentVersion(s.State, vers.Number) 116 c.Assert(err, jc.ErrorIsNil) 117 stor := s.DefaultToolsStorage 118 agentTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(), vers) 119 s.patchVersion(agentTools.Version) 120 err = envtools.MergeAndWriteMetadata(stor, "released", "released", coretools.List{agentTools}, envtools.DoNotWriteMirrors) 121 _, err = s.machine.AgentTools() 122 c.Assert(err, jc.Satisfies, errors.IsNotFound) 123 124 u := s.makeUpgrader(c) 125 statetesting.AssertStop(c, u) 126 s.expectInitialUpgradeCheckDone(c) 127 s.machine.Refresh() 128 gotTools, err := s.machine.AgentTools() 129 c.Assert(err, jc.ErrorIsNil) 130 envtesting.CheckTools(c, gotTools, agentTools) 131 } 132 133 func (s *UpgraderSuite) TestUpgraderSetVersion(c *gc.C) { 134 vers := version.MustParseBinary("5.4.3-precise-amd64") 135 agentTools := envtesting.PrimeTools(c, s.DefaultToolsStorage, s.DataDir(), s.Environ.Config().AgentStream(), vers) 136 s.patchVersion(agentTools.Version) 137 err := os.RemoveAll(filepath.Join(s.DataDir(), "tools")) 138 c.Assert(err, jc.ErrorIsNil) 139 140 _, err = s.machine.AgentTools() 141 c.Assert(err, jc.Satisfies, errors.IsNotFound) 142 err = statetesting.SetAgentVersion(s.State, vers.Number) 143 c.Assert(err, jc.ErrorIsNil) 144 145 u := s.makeUpgrader(c) 146 statetesting.AssertStop(c, u) 147 s.expectInitialUpgradeCheckDone(c) 148 s.machine.Refresh() 149 gotTools, err := s.machine.AgentTools() 150 c.Assert(err, jc.ErrorIsNil) 151 c.Assert(gotTools, gc.DeepEquals, &coretools.Tools{Version: vers}) 152 } 153 154 func (s *UpgraderSuite) expectInitialUpgradeCheckDone(c *gc.C) { 155 c.Assert(s.initialCheckComplete.IsUnlocked(), jc.IsTrue) 156 } 157 158 func (s *UpgraderSuite) expectInitialUpgradeCheckNotDone(c *gc.C) { 159 c.Assert(s.initialCheckComplete.IsUnlocked(), jc.IsFalse) 160 } 161 162 func (s *UpgraderSuite) TestUpgraderUpgradesImmediately(c *gc.C) { 163 stor := s.DefaultToolsStorage 164 oldTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.3-precise-amd64")) 165 s.patchVersion(oldTools.Version) 166 newTools := envtesting.AssertUploadFakeToolsVersions( 167 c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.5-precise-amd64"))[0] 168 err := statetesting.SetAgentVersion(s.State, newTools.Version.Number) 169 c.Assert(err, jc.ErrorIsNil) 170 171 u := s.makeUpgrader(c) 172 err = u.Stop() 173 s.expectInitialUpgradeCheckNotDone(c) 174 envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{ 175 AgentName: s.machine.Tag().String(), 176 OldTools: oldTools.Version, 177 NewTools: newTools.Version, 178 DataDir: s.DataDir(), 179 }) 180 foundTools, err := agenttools.ReadTools(s.DataDir(), newTools.Version) 181 c.Assert(err, jc.ErrorIsNil) 182 newTools.URL = fmt.Sprintf("https://%s/model/%s/tools/5.4.5-precise-amd64", 183 s.APIState.Addr(), coretesting.ModelTag.Id()) 184 envtesting.CheckTools(c, foundTools, newTools) 185 } 186 187 func (s *UpgraderSuite) TestUpgraderRetryAndChanged(c *gc.C) { 188 stor := s.DefaultToolsStorage 189 oldTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.3-precise-amd64")) 190 s.patchVersion(oldTools.Version) 191 newTools := envtesting.AssertUploadFakeToolsVersions( 192 c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.5-precise-amd64"))[0] 193 err := statetesting.SetAgentVersion(s.State, newTools.Version.Number) 194 c.Assert(err, jc.ErrorIsNil) 195 196 retryc := make(chan time.Time) 197 *upgrader.RetryAfter = func() <-chan time.Time { 198 c.Logf("replacement retry after") 199 return retryc 200 } 201 err = stor.Remove(envtools.StorageName(newTools.Version, "released")) 202 c.Assert(err, jc.ErrorIsNil) 203 u := s.makeUpgrader(c) 204 defer u.Stop() 205 s.expectInitialUpgradeCheckNotDone(c) 206 207 for i := 0; i < 3; i++ { 208 select { 209 case retryc <- time.Now(): 210 case <-time.After(coretesting.LongWait): 211 c.Fatalf("upgrader did not retry (attempt %d)", i) 212 } 213 } 214 215 // Make it upgrade to some newer tools that can be 216 // downloaded ok; it should stop retrying, download 217 // the newer tools and exit. 218 newerTools := envtesting.AssertUploadFakeToolsVersions( 219 c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.6-precise-amd64"))[0] 220 221 err = statetesting.SetAgentVersion(s.State, newerTools.Version.Number) 222 c.Assert(err, jc.ErrorIsNil) 223 224 s.BackingState.StartSync() 225 done := make(chan error) 226 go func() { 227 done <- u.Wait() 228 }() 229 select { 230 case err := <-done: 231 envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{ 232 AgentName: s.machine.Tag().String(), 233 OldTools: oldTools.Version, 234 NewTools: newerTools.Version, 235 DataDir: s.DataDir(), 236 }) 237 case <-time.After(coretesting.LongWait): 238 c.Fatalf("upgrader did not quit after upgrading") 239 } 240 } 241 242 func (s *UpgraderSuite) TestChangeAgentTools(c *gc.C) { 243 oldTools := &coretools.Tools{ 244 Version: version.MustParseBinary("1.2.3-quantal-amd64"), 245 } 246 stor := s.DefaultToolsStorage 247 newToolsBinary := "5.4.3-precise-amd64" 248 newTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(), version.MustParseBinary(newToolsBinary)) 249 s.patchVersion(newTools.Version) 250 err := envtools.MergeAndWriteMetadata(stor, "released", "released", coretools.List{newTools}, envtools.DoNotWriteMirrors) 251 c.Assert(err, jc.ErrorIsNil) 252 ugErr := &upgrader.UpgradeReadyError{ 253 AgentName: "anAgent", 254 OldTools: oldTools.Version, 255 NewTools: newTools.Version, 256 DataDir: s.DataDir(), 257 } 258 err = ugErr.ChangeAgentTools() 259 c.Assert(err, jc.ErrorIsNil) 260 target := agenttools.ToolsDir(s.DataDir(), newToolsBinary) 261 link, err := symlink.Read(agenttools.ToolsDir(s.DataDir(), "anAgent")) 262 c.Assert(err, jc.ErrorIsNil) 263 c.Assert(link, jc.SamePath, target) 264 } 265 266 func (s *UpgraderSuite) TestUsesAlreadyDownloadedToolsIfAvailable(c *gc.C) { 267 oldVersion := version.MustParseBinary("1.2.3-quantal-amd64") 268 s.patchVersion(oldVersion) 269 270 newVersion := version.MustParseBinary("5.4.3-quantal-amd64") 271 err := statetesting.SetAgentVersion(s.State, newVersion.Number) 272 c.Assert(err, jc.ErrorIsNil) 273 274 // Install tools matching the new version in the data directory 275 // but *not* in environment storage. The upgrader should find the 276 // downloaded tools without looking in environment storage. 277 envtesting.InstallFakeDownloadedTools(c, s.DataDir(), newVersion) 278 279 u := s.makeUpgrader(c) 280 err = u.Stop() 281 s.expectInitialUpgradeCheckNotDone(c) 282 283 envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{ 284 AgentName: s.machine.Tag().String(), 285 OldTools: oldVersion, 286 NewTools: newVersion, 287 DataDir: s.DataDir(), 288 }) 289 } 290 291 func (s *UpgraderSuite) TestUpgraderRefusesToDowngradeMinorVersions(c *gc.C) { 292 stor := s.DefaultToolsStorage 293 origTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.3-precise-amd64")) 294 s.patchVersion(origTools.Version) 295 downgradeTools := envtesting.AssertUploadFakeToolsVersions( 296 c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.3.3-precise-amd64"))[0] 297 err := statetesting.SetAgentVersion(s.State, downgradeTools.Version.Number) 298 c.Assert(err, jc.ErrorIsNil) 299 300 u := s.makeUpgrader(c) 301 err = u.Stop() 302 s.expectInitialUpgradeCheckDone(c) 303 // If the upgrade would have triggered, we would have gotten an 304 // UpgradeReadyError, since it was skipped, we get no error 305 c.Check(err, jc.ErrorIsNil) 306 _, err = agenttools.ReadTools(s.DataDir(), downgradeTools.Version) 307 // TODO: ReadTools *should* be returning some form of errors.NotFound, 308 // however, it just passes back a fmt.Errorf so we live with it 309 // c.Assert(err, jc.Satisfies, errors.IsNotFound) 310 c.Check(err, gc.ErrorMatches, "cannot read tools metadata in tools directory.*"+utils.NoSuchFileErrRegexp) 311 } 312 313 func (s *UpgraderSuite) TestUpgraderAllowsDowngradingPatchVersions(c *gc.C) { 314 stor := s.DefaultToolsStorage 315 origTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.3-precise-amd64")) 316 s.patchVersion(origTools.Version) 317 downgradeTools := envtesting.AssertUploadFakeToolsVersions( 318 c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.2-precise-amd64"))[0] 319 err := statetesting.SetAgentVersion(s.State, downgradeTools.Version.Number) 320 c.Assert(err, jc.ErrorIsNil) 321 322 u := s.makeUpgrader(c) 323 err = u.Stop() 324 s.expectInitialUpgradeCheckNotDone(c) 325 envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{ 326 AgentName: s.machine.Tag().String(), 327 OldTools: origTools.Version, 328 NewTools: downgradeTools.Version, 329 DataDir: s.DataDir(), 330 }) 331 foundTools, err := agenttools.ReadTools(s.DataDir(), downgradeTools.Version) 332 c.Assert(err, jc.ErrorIsNil) 333 downgradeTools.URL = fmt.Sprintf("https://%s/model/%s/tools/5.4.2-precise-amd64", 334 s.APIState.Addr(), coretesting.ModelTag.Id()) 335 envtesting.CheckTools(c, foundTools, downgradeTools) 336 } 337 338 func (s *UpgraderSuite) TestUpgraderAllowsDowngradeToOrigVersionIfUpgradeInProgress(c *gc.C) { 339 // note: otherwise illegal version jump 340 downgradeVersion := version.MustParseBinary("5.3.0-precise-amd64") 341 s.confVersion = downgradeVersion.Number 342 343 stor := s.DefaultToolsStorage 344 origTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.3-precise-amd64")) 345 s.patchVersion(origTools.Version) 346 downgradeTools := envtesting.AssertUploadFakeToolsVersions( 347 c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(), downgradeVersion)[0] 348 err := statetesting.SetAgentVersion(s.State, downgradeVersion.Number) 349 c.Assert(err, jc.ErrorIsNil) 350 351 u := s.makeUpgrader(c) 352 err = u.Stop() 353 s.expectInitialUpgradeCheckNotDone(c) 354 envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{ 355 AgentName: s.machine.Tag().String(), 356 OldTools: origTools.Version, 357 NewTools: downgradeVersion, 358 DataDir: s.DataDir(), 359 }) 360 foundTools, err := agenttools.ReadTools(s.DataDir(), downgradeTools.Version) 361 c.Assert(err, jc.ErrorIsNil) 362 downgradeTools.URL = fmt.Sprintf("https://%s/model/%s/tools/5.3.0-precise-amd64", 363 s.APIState.Addr(), coretesting.ModelTag.Id()) 364 envtesting.CheckTools(c, foundTools, downgradeTools) 365 } 366 367 func (s *UpgraderSuite) TestUpgraderRefusesDowngradeToOrigVersionIfUpgradeNotInProgress(c *gc.C) { 368 downgradeVersion := version.MustParseBinary("5.3.0-precise-amd64") 369 s.confVersion = downgradeVersion.Number 370 s.upgradeStepsComplete.Unlock() 371 372 stor := s.DefaultToolsStorage 373 origTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(), version.MustParseBinary("5.4.3-precise-amd64")) 374 s.patchVersion(origTools.Version) 375 envtesting.AssertUploadFakeToolsVersions( 376 c, stor, s.Environ.Config().AgentStream(), s.Environ.Config().AgentStream(), downgradeVersion) 377 err := statetesting.SetAgentVersion(s.State, downgradeVersion.Number) 378 c.Assert(err, jc.ErrorIsNil) 379 380 u := s.makeUpgrader(c) 381 err = u.Stop() 382 s.expectInitialUpgradeCheckDone(c) 383 384 // If the upgrade would have triggered, we would have gotten an 385 // UpgradeReadyError, since it was skipped, we get no error 386 c.Check(err, jc.ErrorIsNil) 387 } 388 389 type allowedTest struct { 390 original string 391 current string 392 target string 393 upgradeRunning bool 394 allowed bool 395 } 396 397 func (s *AllowedTargetVersionSuite) TestAllowedTargetVersionSuite(c *gc.C) { 398 cases := []allowedTest{ 399 {original: "1.2.3", current: "1.2.3", upgradeRunning: false, target: "1.3.3", allowed: true}, 400 {original: "1.2.3", current: "1.2.3", upgradeRunning: false, target: "1.2.3", allowed: true}, 401 {original: "1.2.3", current: "1.2.3", upgradeRunning: false, target: "2.2.3", allowed: true}, 402 {original: "1.2.3", current: "1.2.3", upgradeRunning: false, target: "1.1.3", allowed: false}, 403 {original: "1.2.3", current: "1.2.3", upgradeRunning: false, target: "1.2.2", allowed: true}, // downgrade between builds 404 {original: "1.2.3", current: "1.2.3", upgradeRunning: false, target: "0.2.3", allowed: false}, 405 {original: "0.2.3", current: "1.2.3", upgradeRunning: false, target: "0.2.3", allowed: false}, 406 {original: "0.2.3", current: "1.2.3", upgradeRunning: true, target: "0.2.3", allowed: true}, // downgrade during upgrade 407 } 408 for i, test := range cases { 409 c.Logf("test case %d, %#v", i, test) 410 original := version.MustParse(test.original) 411 current := version.MustParse(test.current) 412 target := version.MustParse(test.target) 413 result := upgrader.AllowedTargetVersion(original, current, test.upgradeRunning, target) 414 c.Check(result, gc.Equals, test.allowed) 415 } 416 }