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