github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/state/service_leader_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state_test 5 6 import ( 7 "github.com/juju/errors" 8 jc "github.com/juju/testing/checkers" 9 "github.com/juju/utils" 10 gc "gopkg.in/check.v1" 11 "gopkg.in/mgo.v2/txn" 12 13 "github.com/juju/juju/state" 14 "github.com/juju/juju/state/testing" 15 "github.com/juju/juju/testing/factory" 16 ) 17 18 type ServiceLeaderSuite struct { 19 ConnSuite 20 service *state.Service 21 } 22 23 var _ = gc.Suite(&ServiceLeaderSuite{}) 24 25 func (s *ServiceLeaderSuite) SetUpTest(c *gc.C) { 26 s.ConnSuite.SetUpTest(c) 27 s.service = s.Factory.MakeService(c, nil) 28 } 29 30 func (s *ServiceLeaderSuite) TestReadEmpty(c *gc.C) { 31 s.checkSettings(c, map[string]string{}) 32 } 33 34 func (s *ServiceLeaderSuite) TestWrite(c *gc.C) { 35 s.writeSettings(c, map[string]string{ 36 "foo": "bar", 37 "baz.qux": "ping", 38 "pong": "", 39 "$unset": "foo", 40 }) 41 42 s.checkSettings(c, map[string]string{ 43 "foo": "bar", 44 "baz.qux": "ping", 45 // pong: "" value is ignored 46 "$unset": "foo", 47 }) 48 } 49 50 func (s *ServiceLeaderSuite) TestOverwrite(c *gc.C) { 51 s.writeSettings(c, map[string]string{ 52 "one": "foo", 53 "2.0": "bar", 54 "$three": "baz", 55 "fo-ur": "qux", 56 }) 57 58 s.writeSettings(c, map[string]string{ 59 "one": "", 60 "2.0": "ping", 61 "$three": "pong", 62 "$unset": "2.0", 63 }) 64 65 s.checkSettings(c, map[string]string{ 66 // one: "" value is cleared 67 "2.0": "ping", 68 "$three": "pong", 69 "fo-ur": "qux", 70 "$unset": "2.0", 71 }) 72 } 73 74 func (s *ServiceLeaderSuite) TestTxnRevnoChange(c *gc.C) { 75 defer state.SetBeforeHooks(c, s.State, func() { 76 s.writeSettings(c, map[string]string{ 77 "other": "values", 78 "slipped": "in", 79 "before": "we", 80 "managed": "to", 81 }) 82 }).Check() 83 84 s.writeSettings(c, map[string]string{ 85 "but": "we", 86 "overwrite": "those", 87 "before": "", 88 }) 89 90 s.checkSettings(c, map[string]string{ 91 "other": "values", 92 "slipped": "in", 93 "but": "we", 94 "managed": "to", 95 "overwrite": "those", 96 }) 97 } 98 99 func (s *ServiceLeaderSuite) TestTokenError(c *gc.C) { 100 err := s.service.UpdateLeaderSettings(&failToken{}, map[string]string{"blah": "blah"}) 101 c.Check(err, gc.ErrorMatches, "prerequisites failed: something bad happened") 102 } 103 104 func (s *ServiceLeaderSuite) TestTokenAssertFailure(c *gc.C) { 105 err := s.service.UpdateLeaderSettings(&raceToken{}, map[string]string{"blah": "blah"}) 106 c.Check(err, gc.ErrorMatches, "prerequisites failed: too late") 107 } 108 109 func (s *ServiceLeaderSuite) TestReadWriteDying(c *gc.C) { 110 s.preventRemove(c) 111 s.destroyService(c) 112 113 s.writeSettings(c, map[string]string{ 114 "this": "should", 115 "still": "work", 116 }) 117 s.checkSettings(c, map[string]string{ 118 "this": "should", 119 "still": "work", 120 }) 121 } 122 123 func (s *ServiceLeaderSuite) TestReadRemoved(c *gc.C) { 124 s.destroyService(c) 125 126 actual, err := s.service.LeaderSettings() 127 c.Check(err, gc.ErrorMatches, "service not found") 128 c.Check(err, jc.Satisfies, errors.IsNotFound) 129 c.Check(actual, gc.IsNil) 130 } 131 132 func (s *ServiceLeaderSuite) TestWriteRemoved(c *gc.C) { 133 s.destroyService(c) 134 135 err := s.service.UpdateLeaderSettings(&fakeToken{}, map[string]string{ 136 "should": "fail", 137 }) 138 c.Check(err, gc.ErrorMatches, "service not found") 139 c.Check(err, jc.Satisfies, errors.IsNotFound) 140 } 141 142 func (s *ServiceLeaderSuite) TestWatchInitialEvent(c *gc.C) { 143 w := s.service.WatchLeaderSettings() 144 defer testing.AssertStop(c, w) 145 146 wc := testing.NewNotifyWatcherC(c, s.State, w) 147 wc.AssertOneChange() 148 } 149 150 func (s *ServiceLeaderSuite) TestWatchDetectChange(c *gc.C) { 151 w := s.service.WatchLeaderSettings() 152 defer testing.AssertStop(c, w) 153 wc := testing.NewNotifyWatcherC(c, s.State, w) 154 wc.AssertOneChange() 155 156 err := s.service.UpdateLeaderSettings(&fakeToken{}, map[string]string{ 157 "something": "changed", 158 }) 159 c.Assert(err, jc.ErrorIsNil) 160 wc.AssertOneChange() 161 } 162 163 func (s *ServiceLeaderSuite) TestWatchIgnoreNullChange(c *gc.C) { 164 w := s.service.WatchLeaderSettings() 165 defer testing.AssertStop(c, w) 166 wc := testing.NewNotifyWatcherC(c, s.State, w) 167 wc.AssertOneChange() 168 err := s.service.UpdateLeaderSettings(&fakeToken{}, map[string]string{ 169 "something": "changed", 170 }) 171 c.Assert(err, jc.ErrorIsNil) 172 wc.AssertOneChange() 173 174 err = s.service.UpdateLeaderSettings(&fakeToken{}, map[string]string{ 175 "something": "changed", 176 }) 177 c.Assert(err, jc.ErrorIsNil) 178 wc.AssertNoChange() 179 } 180 181 func (s *ServiceLeaderSuite) TestWatchCoalesceChanges(c *gc.C) { 182 w := s.service.WatchLeaderSettings() 183 defer testing.AssertStop(c, w) 184 wc := testing.NewNotifyWatcherC(c, s.State, w) 185 wc.AssertOneChange() 186 187 err := s.service.UpdateLeaderSettings(&fakeToken{}, map[string]string{ 188 "something": "changed", 189 }) 190 c.Assert(err, jc.ErrorIsNil) 191 err = s.service.UpdateLeaderSettings(&fakeToken{}, map[string]string{ 192 "very": "excitingly", 193 }) 194 c.Assert(err, jc.ErrorIsNil) 195 wc.AssertOneChange() 196 } 197 198 func (s *ServiceLeaderSuite) writeSettings(c *gc.C, update map[string]string) { 199 err := s.service.UpdateLeaderSettings(&fakeToken{}, update) 200 c.Check(err, jc.ErrorIsNil) 201 } 202 203 func (s *ServiceLeaderSuite) checkSettings(c *gc.C, expect map[string]string) { 204 actual, err := s.service.LeaderSettings() 205 c.Check(err, jc.ErrorIsNil) 206 c.Check(actual, gc.DeepEquals, expect) 207 } 208 209 func (s *ServiceLeaderSuite) preventRemove(c *gc.C) { 210 s.Factory.MakeUnit(c, &factory.UnitParams{Service: s.service}) 211 } 212 213 func (s *ServiceLeaderSuite) destroyService(c *gc.C) { 214 killService, err := s.State.Service(s.service.Name()) 215 c.Assert(err, jc.ErrorIsNil) 216 err = killService.Destroy() 217 c.Assert(err, jc.ErrorIsNil) 218 } 219 220 // fakeToken implements leadership.Token. 221 type fakeToken struct{} 222 223 // Check is part of the leadership.Token interface. It always claims success, 224 // and never checks or writes the userdata. 225 func (*fakeToken) Check(interface{}) error { 226 return nil 227 } 228 229 // failToken implements leadership.Token. 230 type failToken struct{} 231 232 // Check is part of the leadership.Token interface. It always returns an error, 233 // and never checks or writes the userdata. 234 func (*failToken) Check(interface{}) error { 235 return errors.New("something bad happened") 236 } 237 238 // raceToken implements leadership.Token. 239 type raceToken struct { 240 checkedOnce bool 241 } 242 243 // Check is part of the leadership.Token interface. On the first call, it expects 244 // a *[]txn.Op, into which it will copy a failing assertion; on subsequent calls, 245 // it just returns an error. 246 func (t *raceToken) Check(out interface{}) error { 247 if t.checkedOnce { 248 return errors.New("too late") 249 } 250 t.checkedOnce = true 251 outPtr, ok := out.(*[]txn.Op) 252 if !ok { 253 return errors.Errorf("SUT passed in bad value: %#v", out) 254 } 255 wontExist := utils.MustNewUUID() 256 *outPtr = []txn.Op{{ 257 C: "units", // we have to use a collection defined in the schema 258 Id: wontExist.String(), 259 Assert: txn.DocExists, 260 }} 261 return nil 262 }