github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/api/watcher/watcher_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package watcher_test 5 6 import ( 7 "time" 8 9 "github.com/juju/names" 10 jc "github.com/juju/testing/checkers" 11 "github.com/juju/utils" 12 gc "gopkg.in/check.v1" 13 14 "github.com/juju/juju/api" 15 "github.com/juju/juju/api/migrationminion" 16 "github.com/juju/juju/api/watcher" 17 "github.com/juju/juju/apiserver/params" 18 "github.com/juju/juju/core/migration" 19 "github.com/juju/juju/juju/testing" 20 "github.com/juju/juju/state" 21 "github.com/juju/juju/storage" 22 "github.com/juju/juju/storage/provider/dummy" 23 "github.com/juju/juju/storage/provider/registry" 24 coretesting "github.com/juju/juju/testing" 25 "github.com/juju/juju/testing/factory" 26 corewatcher "github.com/juju/juju/watcher" 27 "github.com/juju/juju/watcher/watchertest" 28 "github.com/juju/juju/worker" 29 ) 30 31 type watcherSuite struct { 32 testing.JujuConnSuite 33 34 stateAPI api.Connection 35 36 // These are raw State objects. Use them for setup and assertions, but 37 // should never be touched by the API calls themselves 38 rawMachine *state.Machine 39 } 40 41 var _ = gc.Suite(&watcherSuite{}) 42 43 func (s *watcherSuite) SetUpTest(c *gc.C) { 44 s.JujuConnSuite.SetUpTest(c) 45 s.stateAPI, s.rawMachine = s.OpenAPIAsNewMachine(c, state.JobManageModel, state.JobHostUnits) 46 } 47 48 func (s *watcherSuite) TestWatchInitialEventConsumed(c *gc.C) { 49 // Machiner.Watch should send the initial event as part of the Watch 50 // call (for NotifyWatchers there is no state to be transmitted). So a 51 // call to Next() should not have anything to return. 52 var results params.NotifyWatchResults 53 args := params.Entities{Entities: []params.Entity{{Tag: s.rawMachine.Tag().String()}}} 54 err := s.stateAPI.APICall("Machiner", s.stateAPI.BestFacadeVersion("Machiner"), "", "Watch", args, &results) 55 c.Assert(err, jc.ErrorIsNil) 56 c.Assert(results.Results, gc.HasLen, 1) 57 result := results.Results[0] 58 c.Assert(result.Error, gc.IsNil) 59 60 // We expect the Call() to "Next" to block, so run it in a goroutine. 61 done := make(chan error) 62 go func() { 63 ignored := struct{}{} 64 done <- s.stateAPI.APICall("NotifyWatcher", s.stateAPI.BestFacadeVersion("NotifyWatcher"), result.NotifyWatcherId, "Next", nil, &ignored) 65 }() 66 67 select { 68 case err := <-done: 69 c.Errorf("Call(Next) did not block immediately after Watch(): err %v", err) 70 case <-time.After(coretesting.ShortWait): 71 } 72 } 73 74 func (s *watcherSuite) TestWatchMachine(c *gc.C) { 75 var results params.NotifyWatchResults 76 args := params.Entities{Entities: []params.Entity{{Tag: s.rawMachine.Tag().String()}}} 77 err := s.stateAPI.APICall("Machiner", s.stateAPI.BestFacadeVersion("Machiner"), "", "Watch", args, &results) 78 c.Assert(err, jc.ErrorIsNil) 79 c.Assert(results.Results, gc.HasLen, 1) 80 result := results.Results[0] 81 c.Assert(result.Error, gc.IsNil) 82 83 w := watcher.NewNotifyWatcher(s.stateAPI, result) 84 wc := watchertest.NewNotifyWatcherC(c, w, s.BackingState.StartSync) 85 defer wc.AssertStops() 86 wc.AssertOneChange() 87 } 88 89 func (s *watcherSuite) TestNotifyWatcherStopsWithPendingSend(c *gc.C) { 90 var results params.NotifyWatchResults 91 args := params.Entities{Entities: []params.Entity{{Tag: s.rawMachine.Tag().String()}}} 92 err := s.stateAPI.APICall("Machiner", s.stateAPI.BestFacadeVersion("Machiner"), "", "Watch", args, &results) 93 c.Assert(err, jc.ErrorIsNil) 94 c.Assert(results.Results, gc.HasLen, 1) 95 result := results.Results[0] 96 c.Assert(result.Error, gc.IsNil) 97 98 // params.NotifyWatcher conforms to the watcher.NotifyWatcher interface 99 w := watcher.NewNotifyWatcher(s.stateAPI, result) 100 wc := watchertest.NewNotifyWatcherC(c, w, s.BackingState.StartSync) 101 wc.AssertStops() 102 } 103 104 func (s *watcherSuite) TestWatchUnitsKeepsEvents(c *gc.C) { 105 // Create two services, relate them, and add one unit to each - a 106 // principal and a subordinate. 107 mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) 108 s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging")) 109 eps, err := s.State.InferEndpoints("mysql", "logging") 110 c.Assert(err, jc.ErrorIsNil) 111 rel, err := s.State.AddRelation(eps...) 112 c.Assert(err, jc.ErrorIsNil) 113 principal, err := mysql.AddUnit() 114 c.Assert(err, jc.ErrorIsNil) 115 err = principal.AssignToMachine(s.rawMachine) 116 c.Assert(err, jc.ErrorIsNil) 117 relUnit, err := rel.Unit(principal) 118 c.Assert(err, jc.ErrorIsNil) 119 err = relUnit.EnterScope(nil) 120 c.Assert(err, jc.ErrorIsNil) 121 subordinate, err := s.State.Unit("logging/0") 122 c.Assert(err, jc.ErrorIsNil) 123 124 // Call the Deployer facade's WatchUnits for machine-0. 125 var results params.StringsWatchResults 126 args := params.Entities{Entities: []params.Entity{{Tag: s.rawMachine.Tag().String()}}} 127 err = s.stateAPI.APICall("Deployer", s.stateAPI.BestFacadeVersion("Deployer"), "", "WatchUnits", args, &results) 128 c.Assert(err, jc.ErrorIsNil) 129 c.Assert(results.Results, gc.HasLen, 1) 130 result := results.Results[0] 131 c.Assert(result.Error, gc.IsNil) 132 133 // Start a StringsWatcher and check the initial event. 134 w := watcher.NewStringsWatcher(s.stateAPI, result) 135 wc := watchertest.NewStringsWatcherC(c, w, s.BackingState.StartSync) 136 defer wc.AssertStops() 137 138 wc.AssertChange("mysql/0", "logging/0") 139 wc.AssertNoChange() 140 141 // Now, without reading any changes advance the lifecycle of both 142 // units, inducing an update server-side after each two changes to 143 // ensure they're reported as separate events over the API. 144 err = subordinate.EnsureDead() 145 c.Assert(err, jc.ErrorIsNil) 146 s.BackingState.StartSync() 147 err = subordinate.Remove() 148 c.Assert(err, jc.ErrorIsNil) 149 err = principal.EnsureDead() 150 c.Assert(err, jc.ErrorIsNil) 151 s.BackingState.StartSync() 152 153 // Expect these changes as 2 separate events, so that 154 // nothing gets lost. 155 wc.AssertChange("logging/0") 156 wc.AssertChange("mysql/0") 157 wc.AssertNoChange() 158 } 159 160 func (s *watcherSuite) TestStringsWatcherStopsWithPendingSend(c *gc.C) { 161 // Call the Deployer facade's WatchUnits for machine-0. 162 var results params.StringsWatchResults 163 args := params.Entities{Entities: []params.Entity{{Tag: s.rawMachine.Tag().String()}}} 164 err := s.stateAPI.APICall("Deployer", s.stateAPI.BestFacadeVersion("Deployer"), "", "WatchUnits", args, &results) 165 c.Assert(err, jc.ErrorIsNil) 166 c.Assert(results.Results, gc.HasLen, 1) 167 result := results.Results[0] 168 c.Assert(result.Error, gc.IsNil) 169 170 // Start a StringsWatcher and check the initial event. 171 w := watcher.NewStringsWatcher(s.stateAPI, result) 172 wc := watchertest.NewStringsWatcherC(c, w, s.BackingState.StartSync) 173 defer wc.AssertStops() 174 175 // Create a service, deploy a unit of it on the machine. 176 mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) 177 principal, err := mysql.AddUnit() 178 c.Assert(err, jc.ErrorIsNil) 179 err = principal.AssignToMachine(s.rawMachine) 180 c.Assert(err, jc.ErrorIsNil) 181 } 182 183 // TODO(fwereade): 2015-11-18 lp:1517391 184 func (s *watcherSuite) TestWatchMachineStorage(c *gc.C) { 185 registry.RegisterProvider( 186 "envscoped", 187 &dummy.StorageProvider{ 188 StorageScope: storage.ScopeEnviron, 189 }, 190 ) 191 registry.RegisterEnvironStorageProviders("dummy", "envscoped") 192 defer registry.RegisterProvider("envscoped", nil) 193 194 f := factory.NewFactory(s.BackingState) 195 f.MakeMachine(c, &factory.MachineParams{ 196 Volumes: []state.MachineVolumeParams{{ 197 Volume: state.VolumeParams{ 198 Pool: "envscoped", 199 Size: 1024, 200 }, 201 }}, 202 }) 203 204 var results params.MachineStorageIdsWatchResults 205 args := params.Entities{Entities: []params.Entity{{ 206 Tag: s.State.ModelTag().String(), 207 }}} 208 err := s.stateAPI.APICall( 209 "StorageProvisioner", 210 s.stateAPI.BestFacadeVersion("StorageProvisioner"), 211 "", "WatchVolumeAttachments", args, &results) 212 c.Assert(err, jc.ErrorIsNil) 213 c.Assert(results.Results, gc.HasLen, 1) 214 result := results.Results[0] 215 c.Assert(result.Error, gc.IsNil) 216 217 w := watcher.NewVolumeAttachmentsWatcher(s.stateAPI, result) 218 defer func() { 219 220 // Check we can stop the watcher... 221 w.Kill() 222 wait := make(chan error) 223 go func() { 224 wait <- w.Wait() 225 }() 226 select { 227 case err := <-wait: 228 c.Assert(err, jc.ErrorIsNil) 229 case <-time.After(coretesting.LongWait): 230 c.Fatalf("watcher never stopped") 231 } 232 233 // ...and that its channel hasn't been closed. 234 s.BackingState.StartSync() 235 select { 236 case change, ok := <-w.Changes(): 237 c.Fatalf("watcher sent unexpected change: (%#v, %v)", change, ok) 238 default: 239 } 240 241 }() 242 243 // Check initial event; 244 s.BackingState.StartSync() 245 select { 246 case changes, ok := <-w.Changes(): 247 c.Assert(ok, jc.IsTrue) 248 c.Assert(changes, jc.SameContents, []corewatcher.MachineStorageId{{ 249 MachineTag: "machine-1", 250 AttachmentTag: "volume-0", 251 }}) 252 case <-time.After(coretesting.LongWait): 253 c.Fatalf("timed out waiting for change") 254 } 255 256 // check no subsequent event. 257 s.BackingState.StartSync() 258 select { 259 case <-w.Changes(): 260 c.Fatalf("received unexpected change") 261 case <-time.After(coretesting.ShortWait): 262 } 263 } 264 265 type migrationSuite struct { 266 testing.JujuConnSuite 267 } 268 269 var _ = gc.Suite(&migrationSuite{}) 270 271 func (s *migrationSuite) startSync(c *gc.C, st *state.State) { 272 backingSt, err := s.BackingStatePool.Get(st.ModelUUID()) 273 c.Assert(err, jc.ErrorIsNil) 274 backingSt.StartSync() 275 } 276 277 func (s *migrationSuite) TestMigrationStatusWatcher(c *gc.C) { 278 const nonce = "noncey" 279 280 // Create a model to migrate. 281 hostedState := s.Factory.MakeModel(c, &factory.ModelParams{Prepare: true}) 282 defer hostedState.Close() 283 hostedFactory := factory.NewFactory(hostedState) 284 285 // Create a machine in the hosted model to connect as. 286 m, password := hostedFactory.MakeMachineReturningPassword(c, &factory.MachineParams{ 287 Nonce: nonce, 288 }) 289 290 // Connect as the machine to watch for migration status. 291 apiInfo := s.APIInfo(c) 292 apiInfo.Tag = m.Tag() 293 apiInfo.Password = password 294 apiInfo.ModelTag = hostedState.ModelTag() 295 apiInfo.Nonce = nonce 296 297 apiConn, err := api.Open(apiInfo, api.DialOpts{}) 298 c.Assert(err, jc.ErrorIsNil) 299 defer apiConn.Close() 300 301 // Start watching for a migration. 302 client := migrationminion.NewClient(apiConn) 303 w, err := client.Watch() 304 c.Assert(err, jc.ErrorIsNil) 305 defer func() { 306 c.Assert(worker.Stop(w), jc.ErrorIsNil) 307 }() 308 309 assertNoChange := func() { 310 s.startSync(c, hostedState) 311 select { 312 case _, ok := <-w.Changes(): 313 c.Fatalf("watcher sent unexpected change: (_, %v)", ok) 314 case <-time.After(coretesting.ShortWait): 315 } 316 } 317 318 assertChange := func(phase migration.Phase) { 319 s.startSync(c, hostedState) 320 select { 321 case status, ok := <-w.Changes(): 322 c.Assert(ok, jc.IsTrue) 323 c.Assert(status.Phase, gc.Equals, phase) 324 case <-time.After(coretesting.LongWait): 325 c.Fatalf("watcher didn't emit an event") 326 } 327 assertNoChange() 328 } 329 330 // Initial event with no migration in progress. 331 assertChange(migration.NONE) 332 333 // Now create a migration, should trigger watcher. 334 spec := state.ModelMigrationSpec{ 335 InitiatedBy: names.NewUserTag("someone"), 336 TargetInfo: migration.TargetInfo{ 337 ControllerTag: names.NewModelTag(utils.MustNewUUID().String()), 338 Addrs: []string{"1.2.3.4:5"}, 339 CACert: "cert", 340 AuthTag: names.NewUserTag("dog"), 341 Password: "sekret", 342 }, 343 } 344 mig, err := hostedState.CreateModelMigration(spec) 345 c.Assert(err, jc.ErrorIsNil) 346 assertChange(migration.QUIESCE) 347 348 // Now abort the migration, this should be reported too. 349 c.Assert(mig.SetPhase(migration.ABORT), jc.ErrorIsNil) 350 assertChange(migration.ABORT) 351 c.Assert(mig.SetPhase(migration.ABORTDONE), jc.ErrorIsNil) 352 assertChange(migration.ABORTDONE) 353 354 // Start a new migration, this should also trigger. 355 _, err = hostedState.CreateModelMigration(spec) 356 c.Assert(err, jc.ErrorIsNil) 357 assertChange(migration.QUIESCE) 358 }