github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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 stdtesting "testing" 8 "time" 9 10 jc "github.com/juju/testing/checkers" 11 gc "gopkg.in/check.v1" 12 13 "github.com/juju/juju/api" 14 "github.com/juju/juju/api/watcher" 15 "github.com/juju/juju/apiserver/params" 16 "github.com/juju/juju/juju/testing" 17 "github.com/juju/juju/state" 18 statetesting "github.com/juju/juju/state/testing" 19 "github.com/juju/juju/storage" 20 "github.com/juju/juju/storage/provider/dummy" 21 "github.com/juju/juju/storage/provider/registry" 22 coretesting "github.com/juju/juju/testing" 23 "github.com/juju/juju/testing/factory" 24 ) 25 26 func TestAll(t *stdtesting.T) { 27 coretesting.MgoTestPackage(t) 28 } 29 30 type watcherSuite struct { 31 testing.JujuConnSuite 32 33 stateAPI api.Connection 34 35 // These are raw State objects. Use them for setup and assertions, but 36 // should never be touched by the API calls themselves 37 rawMachine *state.Machine 38 } 39 40 var _ = gc.Suite(&watcherSuite{}) 41 42 func (s *watcherSuite) SetUpTest(c *gc.C) { 43 s.JujuConnSuite.SetUpTest(c) 44 s.stateAPI, s.rawMachine = s.OpenAPIAsNewMachine(c, state.JobManageEnviron, state.JobHostUnits) 45 } 46 47 func (s *watcherSuite) TestWatchInitialEventConsumed(c *gc.C) { 48 // Machiner.Watch should send the initial event as part of the Watch 49 // call (for NotifyWatchers there is no state to be transmitted). So a 50 // call to Next() should not have anything to return. 51 var results params.NotifyWatchResults 52 args := params.Entities{Entities: []params.Entity{{Tag: s.rawMachine.Tag().String()}}} 53 err := s.stateAPI.APICall("Machiner", s.stateAPI.BestFacadeVersion("Machiner"), "", "Watch", args, &results) 54 c.Assert(err, jc.ErrorIsNil) 55 c.Assert(results.Results, gc.HasLen, 1) 56 result := results.Results[0] 57 c.Assert(result.Error, gc.IsNil) 58 59 // We expect the Call() to "Next" to block, so run it in a goroutine. 60 done := make(chan error) 61 go func() { 62 ignored := struct{}{} 63 done <- s.stateAPI.APICall("NotifyWatcher", s.stateAPI.BestFacadeVersion("NotifyWatcher"), result.NotifyWatcherId, "Next", nil, &ignored) 64 }() 65 66 select { 67 case err := <-done: 68 c.Errorf("Call(Next) did not block immediately after Watch(): err %v", err) 69 case <-time.After(coretesting.ShortWait): 70 } 71 } 72 73 func (s *watcherSuite) TestWatchMachine(c *gc.C) { 74 var results params.NotifyWatchResults 75 args := params.Entities{Entities: []params.Entity{{Tag: s.rawMachine.Tag().String()}}} 76 err := s.stateAPI.APICall("Machiner", s.stateAPI.BestFacadeVersion("Machiner"), "", "Watch", args, &results) 77 c.Assert(err, jc.ErrorIsNil) 78 c.Assert(results.Results, gc.HasLen, 1) 79 result := results.Results[0] 80 c.Assert(result.Error, gc.IsNil) 81 82 // params.NotifyWatcher conforms to the state.NotifyWatcher interface 83 w := watcher.NewNotifyWatcher(s.stateAPI, result) 84 wc := statetesting.NewNotifyWatcherC(c, s.State, w) 85 wc.AssertOneChange() 86 statetesting.AssertStop(c, w) 87 wc.AssertClosed() 88 } 89 90 func (s *watcherSuite) TestNotifyWatcherStopsWithPendingSend(c *gc.C) { 91 var results params.NotifyWatchResults 92 args := params.Entities{Entities: []params.Entity{{Tag: s.rawMachine.Tag().String()}}} 93 err := s.stateAPI.APICall("Machiner", s.stateAPI.BestFacadeVersion("Machiner"), "", "Watch", args, &results) 94 c.Assert(err, jc.ErrorIsNil) 95 c.Assert(results.Results, gc.HasLen, 1) 96 result := results.Results[0] 97 c.Assert(result.Error, gc.IsNil) 98 99 // params.NotifyWatcher conforms to the state.NotifyWatcher interface 100 w := watcher.NewNotifyWatcher(s.stateAPI, result) 101 wc := statetesting.NewNotifyWatcherC(c, s.State, w) 102 103 // Now, without reading any changes try stopping the watcher. 104 statetesting.AssertCanStopWhenSending(c, w) 105 wc.AssertClosed() 106 } 107 108 func (s *watcherSuite) TestWatchUnitsKeepsEvents(c *gc.C) { 109 // Create two services, relate them, and add one unit to each - a 110 // principal and a subordinate. 111 mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) 112 s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging")) 113 eps, err := s.State.InferEndpoints("mysql", "logging") 114 c.Assert(err, jc.ErrorIsNil) 115 rel, err := s.State.AddRelation(eps...) 116 c.Assert(err, jc.ErrorIsNil) 117 principal, err := mysql.AddUnit() 118 c.Assert(err, jc.ErrorIsNil) 119 err = principal.AssignToMachine(s.rawMachine) 120 c.Assert(err, jc.ErrorIsNil) 121 relUnit, err := rel.Unit(principal) 122 c.Assert(err, jc.ErrorIsNil) 123 err = relUnit.EnterScope(nil) 124 c.Assert(err, jc.ErrorIsNil) 125 subordinate, err := s.State.Unit("logging/0") 126 c.Assert(err, jc.ErrorIsNil) 127 128 // Call the Deployer facade's WatchUnits for machine-0. 129 var results params.StringsWatchResults 130 args := params.Entities{Entities: []params.Entity{{Tag: s.rawMachine.Tag().String()}}} 131 err = s.stateAPI.APICall("Deployer", s.stateAPI.BestFacadeVersion("Deployer"), "", "WatchUnits", args, &results) 132 c.Assert(err, jc.ErrorIsNil) 133 c.Assert(results.Results, gc.HasLen, 1) 134 result := results.Results[0] 135 c.Assert(result.Error, gc.IsNil) 136 137 // Start a StringsWatcher and check the initial event. 138 w := watcher.NewStringsWatcher(s.stateAPI, result) 139 wc := statetesting.NewStringsWatcherC(c, s.State, w) 140 wc.AssertChange("mysql/0", "logging/0") 141 wc.AssertNoChange() 142 143 // Now, without reading any changes advance the lifecycle of both 144 // units, inducing an update server-side after each two changes to 145 // ensure they're reported as separate events over the API. 146 err = subordinate.EnsureDead() 147 c.Assert(err, jc.ErrorIsNil) 148 s.BackingState.StartSync() 149 err = subordinate.Remove() 150 c.Assert(err, jc.ErrorIsNil) 151 err = principal.EnsureDead() 152 c.Assert(err, jc.ErrorIsNil) 153 s.BackingState.StartSync() 154 155 // Expect these changes as 2 separate events, so that 156 // nothing gets lost. 157 wc.AssertChange("logging/0") 158 wc.AssertChange("mysql/0") 159 wc.AssertNoChange() 160 161 statetesting.AssertStop(c, w) 162 wc.AssertClosed() 163 } 164 165 func (s *watcherSuite) TestStringsWatcherStopsWithPendingSend(c *gc.C) { 166 // Call the Deployer facade's WatchUnits for machine-0. 167 var results params.StringsWatchResults 168 args := params.Entities{Entities: []params.Entity{{Tag: s.rawMachine.Tag().String()}}} 169 err := s.stateAPI.APICall("Deployer", s.stateAPI.BestFacadeVersion("Deployer"), "", "WatchUnits", args, &results) 170 c.Assert(err, jc.ErrorIsNil) 171 c.Assert(results.Results, gc.HasLen, 1) 172 result := results.Results[0] 173 c.Assert(result.Error, gc.IsNil) 174 175 // Start a StringsWatcher and check the initial event. 176 w := watcher.NewStringsWatcher(s.stateAPI, result) 177 wc := statetesting.NewStringsWatcherC(c, s.State, w) 178 179 // Create a service, deploy a unit of it on the machine. 180 mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) 181 principal, err := mysql.AddUnit() 182 c.Assert(err, jc.ErrorIsNil) 183 err = principal.AssignToMachine(s.rawMachine) 184 c.Assert(err, jc.ErrorIsNil) 185 186 // Ensure the initial event is delivered. Then test the watcher 187 // can be stopped cleanly without reading the pending change. 188 s.BackingState.StartSync() 189 statetesting.AssertCanStopWhenSending(c, w) 190 wc.AssertClosed() 191 } 192 193 func (s *watcherSuite) TestWatchMachineStorage(c *gc.C) { 194 registry.RegisterProvider( 195 "envscoped", 196 &dummy.StorageProvider{ 197 StorageScope: storage.ScopeEnviron, 198 }, 199 ) 200 registry.RegisterEnvironStorageProviders("dummy", "envscoped") 201 defer registry.RegisterProvider("envscoped", nil) 202 203 f := factory.NewFactory(s.BackingState) 204 f.MakeMachine(c, &factory.MachineParams{ 205 Volumes: []state.MachineVolumeParams{{ 206 Volume: state.VolumeParams{ 207 Pool: "envscoped", 208 Size: 1024, 209 }, 210 }}, 211 }) 212 213 var results params.MachineStorageIdsWatchResults 214 args := params.Entities{Entities: []params.Entity{{ 215 Tag: s.State.EnvironTag().String(), 216 }}} 217 err := s.stateAPI.APICall( 218 "StorageProvisioner", 219 s.stateAPI.BestFacadeVersion("StorageProvisioner"), 220 "", "WatchVolumeAttachments", args, &results) 221 c.Assert(err, jc.ErrorIsNil) 222 c.Assert(results.Results, gc.HasLen, 1) 223 result := results.Results[0] 224 c.Assert(result.Error, gc.IsNil) 225 226 w := watcher.NewVolumeAttachmentsWatcher(s.stateAPI, result) 227 select { 228 case changes, ok := <-w.Changes(): 229 c.Assert(ok, jc.IsTrue) 230 c.Assert(changes, jc.SameContents, []params.MachineStorageId{{ 231 MachineTag: "machine-1", 232 AttachmentTag: "volume-0", 233 }}) 234 case <-time.After(coretesting.LongWait): 235 c.Fatalf("timed out waiting for change") 236 } 237 select { 238 case <-w.Changes(): 239 c.Fatalf("received unexpected change") 240 case <-time.After(coretesting.ShortWait): 241 } 242 243 statetesting.AssertStop(c, w) 244 select { 245 case _, ok := <-w.Changes(): 246 c.Assert(ok, jc.IsFalse) 247 case <-time.After(coretesting.LongWait): 248 c.Fatalf("timed out waiting for watcher channel to be closed") 249 } 250 }