github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/application_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 gc "gopkg.in/check.v1" 10 11 "github.com/juju/juju/state" 12 "github.com/juju/juju/state/testing" 13 "github.com/juju/juju/testing/factory" 14 ) 15 16 type ApplicationLeaderSuite struct { 17 ConnSuite 18 application *state.Application 19 } 20 21 var _ = gc.Suite(&ApplicationLeaderSuite{}) 22 23 func (s *ApplicationLeaderSuite) SetUpTest(c *gc.C) { 24 s.ConnSuite.SetUpTest(c) 25 s.application = s.Factory.MakeApplication(c, nil) 26 // Before we get into the tests, ensure that all the creation events have flowed through the system. 27 s.WaitForModelWatchersIdle(c, s.Model.UUID()) 28 } 29 30 func (s *ApplicationLeaderSuite) TestReadEmpty(c *gc.C) { 31 s.checkSettings(c, map[string]string{}) 32 } 33 34 func (s *ApplicationLeaderSuite) 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 *ApplicationLeaderSuite) 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 *ApplicationLeaderSuite) 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 *ApplicationLeaderSuite) TestTokenError(c *gc.C) { 100 err := s.application.UpdateLeaderSettings(&failToken{}, map[string]string{"blah": "blah"}) 101 c.Check(err, gc.ErrorMatches, `application "mysql": checking leadership continuity: something bad happened`) 102 } 103 104 func (s *ApplicationLeaderSuite) TestReadWriteDying(c *gc.C) { 105 s.preventRemove(c) 106 s.destroyApplication(c) 107 108 s.writeSettings(c, map[string]string{ 109 "this": "should", 110 "still": "work", 111 }) 112 s.checkSettings(c, map[string]string{ 113 "this": "should", 114 "still": "work", 115 }) 116 } 117 118 func (s *ApplicationLeaderSuite) TestReadRemoved(c *gc.C) { 119 s.destroyApplication(c) 120 121 actual, err := s.application.LeaderSettings() 122 c.Check(err, gc.ErrorMatches, `application "mysql" not found`) 123 c.Check(err, jc.Satisfies, errors.IsNotFound) 124 c.Check(actual, gc.IsNil) 125 } 126 127 func (s *ApplicationLeaderSuite) TestWriteRemoved(c *gc.C) { 128 s.destroyApplication(c) 129 130 err := s.application.UpdateLeaderSettings(&fakeToken{}, map[string]string{ 131 "should": "fail", 132 }) 133 c.Check(err, gc.ErrorMatches, `application "mysql" not found`) 134 c.Check(err, jc.Satisfies, errors.IsNotFound) 135 } 136 137 func (s *ApplicationLeaderSuite) TestWatchInitialEvent(c *gc.C) { 138 w := s.application.WatchLeaderSettings() 139 defer testing.AssertStop(c, w) 140 141 wc := testing.NewNotifyWatcherC(c, w) 142 wc.AssertOneChange() 143 } 144 145 func (s *ApplicationLeaderSuite) TestWatchDetectChange(c *gc.C) { 146 w := s.application.WatchLeaderSettings() 147 defer testing.AssertStop(c, w) 148 wc := testing.NewNotifyWatcherC(c, w) 149 wc.AssertOneChange() 150 151 err := s.application.UpdateLeaderSettings(&fakeToken{}, map[string]string{ 152 "something": "changed", 153 }) 154 c.Assert(err, jc.ErrorIsNil) 155 wc.AssertOneChange() 156 } 157 158 func (s *ApplicationLeaderSuite) TestWatchIgnoreNullChange(c *gc.C) { 159 w := s.application.WatchLeaderSettings() 160 defer testing.AssertStop(c, w) 161 wc := testing.NewNotifyWatcherC(c, w) 162 wc.AssertOneChange() 163 err := s.application.UpdateLeaderSettings(&fakeToken{}, map[string]string{ 164 "something": "changed", 165 }) 166 c.Assert(err, jc.ErrorIsNil) 167 wc.AssertOneChange() 168 169 err = s.application.UpdateLeaderSettings(&fakeToken{}, map[string]string{ 170 "something": "changed", 171 }) 172 c.Assert(err, jc.ErrorIsNil) 173 wc.AssertNoChange() 174 } 175 176 func (s *ApplicationLeaderSuite) TestWatchCoalesceChanges(c *gc.C) { 177 w := s.application.WatchLeaderSettings() 178 defer testing.AssertStop(c, w) 179 wc := testing.NewNotifyWatcherC(c, w) 180 wc.AssertOneChange() 181 182 err := s.application.UpdateLeaderSettings(&fakeToken{}, map[string]string{ 183 "something": "changed", 184 }) 185 c.Assert(err, jc.ErrorIsNil) 186 // TODO(quiescence): these two changes should be one event. 187 wc.AssertOneChange() 188 err = s.application.UpdateLeaderSettings(&fakeToken{}, map[string]string{ 189 "very": "excitingly", 190 }) 191 c.Assert(err, jc.ErrorIsNil) 192 wc.AssertOneChange() 193 } 194 195 func (s *ApplicationLeaderSuite) writeSettings(c *gc.C, update map[string]string) { 196 err := s.application.UpdateLeaderSettings(&fakeToken{}, update) 197 c.Check(err, jc.ErrorIsNil) 198 } 199 200 func (s *ApplicationLeaderSuite) checkSettings(c *gc.C, expect map[string]string) { 201 actual, err := s.application.LeaderSettings() 202 c.Check(err, jc.ErrorIsNil) 203 c.Check(actual, gc.DeepEquals, expect) 204 } 205 206 func (s *ApplicationLeaderSuite) preventRemove(c *gc.C) { 207 s.Factory.MakeUnit(c, &factory.UnitParams{Application: s.application}) 208 } 209 210 func (s *ApplicationLeaderSuite) destroyApplication(c *gc.C) { 211 killApplication, err := s.State.Application(s.application.Name()) 212 c.Assert(err, jc.ErrorIsNil) 213 err = killApplication.Destroy() 214 c.Assert(err, jc.ErrorIsNil) 215 } 216 217 // fakeToken implements leadership.Token. 218 type fakeToken struct { 219 err error 220 } 221 222 // Check is part of the leadership.Token interface. It returns its 223 // contained error (which defaults to nil), and never checks or writes 224 // the userdata. 225 func (t *fakeToken) Check() error { 226 return t.err 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 func (*failToken) Check() error { 234 return errors.New("something bad happened") 235 }