github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/modelupgrader/worker_test.go (about) 1 // Copyright 2017 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package modelupgrader_test 5 6 import ( 7 "errors" 8 "sync" 9 10 "github.com/juju/testing" 11 jc "github.com/juju/testing/checkers" 12 gc "gopkg.in/check.v1" 13 "gopkg.in/juju/names.v2" 14 "gopkg.in/juju/worker.v1/workertest" 15 tomb "gopkg.in/tomb.v2" 16 17 "github.com/juju/juju/apiserver/params" 18 "github.com/juju/juju/core/status" 19 "github.com/juju/juju/core/watcher" 20 "github.com/juju/juju/environs" 21 "github.com/juju/juju/environs/context" 22 coretesting "github.com/juju/juju/testing" 23 "github.com/juju/juju/worker/modelupgrader" 24 ) 25 26 type WorkerSuite struct { 27 testing.IsolationSuite 28 } 29 30 var _ = gc.Suite(&WorkerSuite{}) 31 32 func (*WorkerSuite) TestNewWorkerValidatesConfig(c *gc.C) { 33 _, err := modelupgrader.NewWorker(modelupgrader.Config{}) 34 c.Assert(err, gc.ErrorMatches, "nil Facade not valid") 35 } 36 37 func (*WorkerSuite) TestNewWorker(c *gc.C) { 38 mockFacade := mockFacade{current: 123, target: 124} 39 mockEnviron := mockEnviron{} 40 mockGateUnlocker := mockGateUnlocker{} 41 w, err := modelupgrader.NewWorker(modelupgrader.Config{ 42 Facade: &mockFacade, 43 Environ: &mockEnviron, 44 GateUnlocker: &mockGateUnlocker, 45 ControllerTag: coretesting.ControllerTag, 46 ModelTag: coretesting.ModelTag, 47 CredentialAPI: &credentialAPIForTest{}, 48 }) 49 c.Assert(err, jc.ErrorIsNil) 50 workertest.CheckKill(c, w) 51 mockFacade.CheckCalls(c, []testing.StubCall{ 52 {"ModelTargetEnvironVersion", []interface{}{coretesting.ModelTag}}, 53 {"ModelEnvironVersion", []interface{}{coretesting.ModelTag}}, 54 {"SetModelStatus", []interface{}{coretesting.ModelTag, status.Busy, "upgrading environ from version 123 to 124", nilData}}, 55 {"SetModelStatus", []interface{}{coretesting.ModelTag, status.Available, "", nilData}}, 56 }) 57 mockEnviron.CheckCallNames(c, "UpgradeOperations") 58 mockGateUnlocker.CheckCallNames(c, "Unlock") 59 } 60 61 func (*WorkerSuite) TestNonUpgradeable(c *gc.C) { 62 mockFacade := mockFacade{current: 123, target: 124} 63 mockEnviron := struct{ environs.Environ }{} // not an Upgrader 64 mockGateUnlocker := mockGateUnlocker{} 65 w, err := modelupgrader.NewWorker(modelupgrader.Config{ 66 Facade: &mockFacade, 67 Environ: &mockEnviron, 68 GateUnlocker: &mockGateUnlocker, 69 ControllerTag: coretesting.ControllerTag, 70 ModelTag: coretesting.ModelTag, 71 CredentialAPI: &credentialAPIForTest{}, 72 }) 73 c.Assert(err, jc.ErrorIsNil) 74 workertest.CheckKill(c, w) 75 mockFacade.CheckCalls(c, []testing.StubCall{ 76 {"ModelTargetEnvironVersion", []interface{}{coretesting.ModelTag}}, 77 {"ModelEnvironVersion", []interface{}{coretesting.ModelTag}}, 78 {"SetModelStatus", []interface{}{coretesting.ModelTag, status.Busy, "upgrading environ from version 123 to 124", nilData}}, 79 {"SetModelStatus", []interface{}{coretesting.ModelTag, status.Available, "", nilData}}, 80 }) 81 mockGateUnlocker.CheckCallNames(c, "Unlock") 82 } 83 84 func (*WorkerSuite) TestRunUpgradeOperations(c *gc.C) { 85 var stepsStub testing.Stub 86 mockFacade := mockFacade{current: 123, target: 125} 87 mockEnviron := mockEnviron{ 88 ops: []environs.UpgradeOperation{{ 89 TargetVersion: 123, 90 Steps: []environs.UpgradeStep{ 91 newStep(&stepsStub, "step122"), 92 }, 93 }, { 94 TargetVersion: 123, 95 Steps: []environs.UpgradeStep{ 96 newStep(&stepsStub, "step123"), 97 }, 98 }, { 99 TargetVersion: 124, 100 Steps: []environs.UpgradeStep{ 101 newStep(&stepsStub, "step124_0"), 102 newStep(&stepsStub, "step124_1"), 103 }, 104 }, { 105 TargetVersion: 125, 106 Steps: []environs.UpgradeStep{ 107 newStep(&stepsStub, "step125"), 108 }, 109 }, { 110 TargetVersion: 126, 111 Steps: []environs.UpgradeStep{ 112 newStep(&stepsStub, "step126"), 113 }, 114 }}, 115 } 116 mockGateUnlocker := mockGateUnlocker{} 117 w, err := modelupgrader.NewWorker(modelupgrader.Config{ 118 Facade: &mockFacade, 119 Environ: &mockEnviron, 120 GateUnlocker: &mockGateUnlocker, 121 ControllerTag: coretesting.ControllerTag, 122 ModelTag: coretesting.ModelTag, 123 CredentialAPI: &credentialAPIForTest{}, 124 }) 125 c.Assert(err, jc.ErrorIsNil) 126 workertest.CheckKill(c, w) 127 mockFacade.CheckCalls(c, []testing.StubCall{ 128 {"ModelTargetEnvironVersion", []interface{}{coretesting.ModelTag}}, 129 {"ModelEnvironVersion", []interface{}{coretesting.ModelTag}}, 130 {"SetModelStatus", []interface{}{coretesting.ModelTag, status.Busy, "upgrading environ from version 123 to 125", nilData}}, 131 {"SetModelEnvironVersion", []interface{}{ 132 coretesting.ModelTag, 124, 133 }}, 134 {"SetModelEnvironVersion", []interface{}{ 135 coretesting.ModelTag, 125, 136 }}, 137 {"SetModelStatus", []interface{}{coretesting.ModelTag, status.Available, "", nilData}}, 138 }) 139 mockEnviron.CheckCalls(c, []testing.StubCall{ 140 {"UpgradeOperations", []interface{}{ 141 mockEnviron.callCtxUsed, 142 environs.UpgradeOperationsParams{ 143 ControllerUUID: coretesting.ControllerTag.Id(), 144 }}, 145 }, 146 }) 147 mockGateUnlocker.CheckCallNames(c, "Unlock") 148 stepsStub.CheckCallNames(c, "step124_0", "step124_1", "step125") 149 } 150 151 func (*WorkerSuite) TestRunUpgradeOperationsStepError(c *gc.C) { 152 var stepsStub testing.Stub 153 stepsStub.SetErrors(errors.New("phooey")) 154 mockFacade := mockFacade{current: 123, target: 124} 155 mockEnviron := mockEnviron{ 156 ops: []environs.UpgradeOperation{{ 157 TargetVersion: 124, 158 Steps: []environs.UpgradeStep{ 159 newStep(&stepsStub, "step124"), 160 }, 161 }}, 162 } 163 mockGateUnlocker := mockGateUnlocker{} 164 w, err := modelupgrader.NewWorker(modelupgrader.Config{ 165 Facade: &mockFacade, 166 Environ: &mockEnviron, 167 GateUnlocker: &mockGateUnlocker, 168 ControllerTag: coretesting.ControllerTag, 169 ModelTag: coretesting.ModelTag, 170 CredentialAPI: &credentialAPIForTest{}, 171 }) 172 c.Assert(err, jc.ErrorIsNil) 173 174 err = workertest.CheckKilled(c, w) 175 c.Assert(err, gc.ErrorMatches, "upgrading environ: phooey") 176 177 mockFacade.CheckCalls(c, []testing.StubCall{ 178 {"ModelTargetEnvironVersion", []interface{}{coretesting.ModelTag}}, 179 {"ModelEnvironVersion", []interface{}{coretesting.ModelTag}}, 180 {"SetModelStatus", []interface{}{coretesting.ModelTag, status.Busy, "upgrading environ from version 123 to 124", nilData}}, 181 {"SetModelStatus", []interface{}{coretesting.ModelTag, status.Error, "failed to upgrade environ: phooey", nilData}}, 182 }) 183 mockGateUnlocker.CheckNoCalls(c) 184 } 185 186 func (*WorkerSuite) TestWaitForUpgrade(c *gc.C) { 187 ch := make(chan struct{}) 188 mockFacade := mockFacade{ 189 current: 123, 190 target: 125, 191 watcher: newMockNotifyWatcher(ch), 192 } 193 mockGateUnlocker := mockGateUnlocker{} 194 w, err := modelupgrader.NewWorker(modelupgrader.Config{ 195 Facade: &mockFacade, 196 Environ: nil, // not responsible for running upgrades 197 GateUnlocker: &mockGateUnlocker, 198 ControllerTag: coretesting.ControllerTag, 199 ModelTag: coretesting.ModelTag, 200 CredentialAPI: &credentialAPIForTest{}, 201 }) 202 c.Assert(err, jc.ErrorIsNil) 203 204 // Send the initial change event on the watcher, and 205 // wait for the worker to call "ModelEnvironVersion". 206 ch <- struct{}{} 207 for a := coretesting.LongAttempt.Start(); a.Next(); { 208 if len(mockFacade.Calls()) < 3 && a.HasNext() { 209 continue 210 } 211 mockFacade.CheckCalls(c, []testing.StubCall{ 212 {"ModelTargetEnvironVersion", []interface{}{coretesting.ModelTag}}, 213 {"WatchModelEnvironVersion", []interface{}{coretesting.ModelTag}}, 214 {"ModelEnvironVersion", []interface{}{coretesting.ModelTag}}, 215 }) 216 mockGateUnlocker.CheckNoCalls(c) 217 break 218 } 219 220 // Set the current version >= target. In practice we should 221 // only ever see that the current version <= target, as all 222 // controller agents should be running the same version at 223 // this point. We require that the environ version be strictly 224 // increasing, so we can be defensive. 225 mockFacade.setCurrent(126) 226 ch <- struct{}{} 227 228 workertest.CheckKill(c, w) 229 mockFacade.CheckCalls(c, []testing.StubCall{ 230 {"ModelTargetEnvironVersion", []interface{}{coretesting.ModelTag}}, 231 {"WatchModelEnvironVersion", []interface{}{coretesting.ModelTag}}, 232 {"ModelEnvironVersion", []interface{}{coretesting.ModelTag}}, 233 {"ModelEnvironVersion", []interface{}{coretesting.ModelTag}}, 234 }) 235 mockGateUnlocker.CheckCallNames(c, "Unlock") 236 } 237 238 func (*WorkerSuite) TestModelNotFoundWhenRunning(c *gc.C) { 239 ch := make(chan struct{}) 240 mockFacade := mockFacade{ 241 current: 123, 242 target: 125, 243 watcher: newMockNotifyWatcher(ch), 244 } 245 w, err := modelupgrader.NewWorker(modelupgrader.Config{ 246 Facade: &mockFacade, 247 Environ: nil, // not responsible for running upgrades 248 GateUnlocker: &mockGateUnlocker{}, 249 ControllerTag: coretesting.ControllerTag, 250 ModelTag: coretesting.ModelTag, 251 CredentialAPI: &credentialAPIForTest{}, 252 }) 253 c.Assert(err, jc.ErrorIsNil) 254 255 mockFacade.SetErrors(¶ms.Error{Code: params.CodeNotFound}) 256 ch <- struct{}{} 257 258 err = workertest.CheckKill(c, w) 259 // We expect NotFound to be changed to modelupgrader.ErrModelRemoved. 260 c.Check(err, gc.ErrorMatches, "model has been removed") 261 } 262 263 func newStep(stub *testing.Stub, name string) environs.UpgradeStep { 264 run := func() error { 265 stub.AddCall(name) 266 return stub.NextErr() 267 } 268 return mockUpgradeStep{name, run} 269 } 270 271 type mockUpgradeStep struct { 272 description string 273 run func() error 274 } 275 276 func (s mockUpgradeStep) Description() string { 277 return s.description 278 } 279 280 func (s mockUpgradeStep) Run(ctx context.ProviderCallContext) error { 281 return s.run() 282 } 283 284 type mockFacade struct { 285 testing.Stub 286 target int 287 watcher *mockNotifyWatcher 288 289 mu sync.Mutex 290 current int 291 } 292 293 func (f *mockFacade) setCurrent(v int) { 294 f.mu.Lock() 295 defer f.mu.Unlock() 296 f.current = v 297 } 298 299 func (f *mockFacade) ModelEnvironVersion(tag names.ModelTag) (int, error) { 300 f.mu.Lock() 301 defer f.mu.Unlock() 302 f.MethodCall(f, "ModelEnvironVersion", tag) 303 return f.current, f.NextErr() 304 } 305 306 func (f *mockFacade) ModelTargetEnvironVersion(tag names.ModelTag) (int, error) { 307 f.MethodCall(f, "ModelTargetEnvironVersion", tag) 308 return f.target, f.NextErr() 309 } 310 311 func (f *mockFacade) SetModelEnvironVersion(tag names.ModelTag, v int) error { 312 f.MethodCall(f, "SetModelEnvironVersion", tag, v) 313 return f.NextErr() 314 } 315 316 func (f *mockFacade) WatchModelEnvironVersion(tag names.ModelTag) (watcher.NotifyWatcher, error) { 317 f.MethodCall(f, "WatchModelEnvironVersion", tag) 318 if err := f.NextErr(); err != nil { 319 return nil, err 320 } 321 if f.watcher != nil { 322 return f.watcher, nil 323 } 324 return nil, errors.New("unexpected call to WatchModelEnvironVersion") 325 } 326 327 var nilData map[string]interface{} 328 329 func (f *mockFacade) SetModelStatus(tag names.ModelTag, status status.Status, info string, data map[string]interface{}) error { 330 f.MethodCall(f, "SetModelStatus", tag, status, info, data) 331 return f.NextErr() 332 } 333 334 type mockEnviron struct { 335 environs.Environ 336 testing.Stub 337 ops []environs.UpgradeOperation 338 339 callCtxUsed context.ProviderCallContext 340 } 341 342 func (e *mockEnviron) UpgradeOperations(ctx context.ProviderCallContext, args environs.UpgradeOperationsParams) []environs.UpgradeOperation { 343 e.MethodCall(e, "UpgradeOperations", ctx, args) 344 e.callCtxUsed = ctx 345 e.PopNoErr() 346 return e.ops 347 } 348 349 type mockGateUnlocker struct { 350 testing.Stub 351 } 352 353 func (g *mockGateUnlocker) Unlock() { 354 g.MethodCall(g, "Unlock") 355 g.PopNoErr() 356 } 357 358 type mockNotifyWatcher struct { 359 tomb tomb.Tomb 360 ch chan struct{} 361 } 362 363 func newMockNotifyWatcher(ch chan struct{}) *mockNotifyWatcher { 364 w := &mockNotifyWatcher{ch: ch} 365 w.tomb.Go(func() error { 366 defer close(ch) 367 <-w.tomb.Dying() 368 return tomb.ErrDying 369 }) 370 return w 371 } 372 373 func (w *mockNotifyWatcher) Changes() watcher.NotifyChannel { 374 return w.ch 375 } 376 377 func (w *mockNotifyWatcher) Kill() { 378 w.tomb.Kill(nil) 379 } 380 381 func (w *mockNotifyWatcher) Wait() error { 382 return w.tomb.Wait() 383 } 384 385 type credentialAPIForTest struct{} 386 387 func (*credentialAPIForTest) InvalidateModelCredential(reason string) error { 388 return nil 389 }