github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/externalcontrollerupdater/externalcontrollerupdater_test.go (about) 1 // Copyright 2017 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package externalcontrollerupdater_test 5 6 import ( 7 "time" 8 9 "github.com/juju/clock/testclock" 10 "github.com/juju/errors" 11 "github.com/juju/names/v5" 12 "github.com/juju/testing" 13 jc "github.com/juju/testing/checkers" 14 "github.com/juju/worker/v3/workertest" 15 gc "gopkg.in/check.v1" 16 17 "github.com/juju/juju/api" 18 "github.com/juju/juju/api/controller/crosscontroller" 19 "github.com/juju/juju/core/crossmodel" 20 coretesting "github.com/juju/juju/testing" 21 "github.com/juju/juju/worker/externalcontrollerupdater" 22 ) 23 24 var _ = gc.Suite(&ExternalControllerUpdaterSuite{}) 25 26 type ExternalControllerUpdaterSuite struct { 27 coretesting.BaseSuite 28 29 updater mockExternalControllerUpdaterClient 30 watcher mockExternalControllerWatcherClient 31 32 clock *testclock.Clock 33 34 stub testing.Stub 35 newWatcher externalcontrollerupdater.NewExternalControllerWatcherClientFunc 36 } 37 38 func (s *ExternalControllerUpdaterSuite) SetUpTest(c *gc.C) { 39 s.BaseSuite.SetUpTest(c) 40 41 s.updater = mockExternalControllerUpdaterClient{ 42 watcher: newMockStringsWatcher(), 43 info: crossmodel.ControllerInfo{ 44 ControllerTag: coretesting.ControllerTag, 45 Alias: "foo", 46 Addrs: []string{"bar"}, 47 CACert: "baz", 48 }, 49 } 50 s.AddCleanup(func(*gc.C) { s.updater.watcher.Stop() }) 51 52 s.watcher = mockExternalControllerWatcherClient{ 53 watcher: newMockNotifyWatcher(), 54 info: crosscontroller.ControllerInfo{ 55 Addrs: []string{"foo"}, 56 CACert: "bar", 57 }, 58 } 59 s.AddCleanup(func(*gc.C) { s.watcher.watcher.Stop() }) 60 61 s.clock = testclock.NewClock(time.Time{}) 62 63 s.stub.ResetCalls() 64 s.newWatcher = func(apiInfo *api.Info) (externalcontrollerupdater.ExternalControllerWatcherClientCloser, error) { 65 s.stub.AddCall("NextExternalControllerWatcherClient", apiInfo) 66 if err := s.stub.NextErr(); err != nil { 67 return nil, err 68 } 69 return &s.watcher, nil 70 } 71 } 72 73 func (s *ExternalControllerUpdaterSuite) TestStartStop(c *gc.C) { 74 w, err := externalcontrollerupdater.New(&s.updater, s.newWatcher, s.clock) 75 c.Assert(err, jc.ErrorIsNil) 76 workertest.CleanKill(c, w) 77 } 78 79 func (s *ExternalControllerUpdaterSuite) TestWatchExternalControllersCalled(c *gc.C) { 80 s.updater.watcher.changes = make(chan []string) 81 82 w, err := externalcontrollerupdater.New(&s.updater, s.newWatcher, s.clock) 83 c.Assert(err, jc.ErrorIsNil) 84 defer workertest.CleanKill(c, w) 85 86 select { 87 case s.updater.watcher.changes <- []string{}: 88 case <-time.After(coretesting.LongWait): 89 c.Fatal("timed out waiting to send changes") 90 } 91 92 workertest.CleanKill(c, w) 93 s.updater.Stub.CheckCallNames(c, "WatchExternalControllers") 94 } 95 96 func (s *ExternalControllerUpdaterSuite) assertWatchExternalControllersStart(c *gc.C) { 97 s.updater.watcher.changes <- []string{coretesting.ControllerTag.Id()} 98 99 // Cause three notifications. Only the first notification is 100 // accompanied by API address changes, so there should be only 101 // one API reconnection, and one local controller update. 102 for i := 0; i < 3; i++ { 103 select { 104 case s.watcher.watcher.changes <- struct{}{}: 105 case <-time.After(coretesting.LongWait): 106 c.Fatal("timed out waiting to send changes") 107 } 108 } 109 110 s.stub.CheckCalls(c, []testing.StubCall{{ 111 "NextExternalControllerWatcherClient", 112 []interface{}{&api.Info{ 113 Addrs: s.updater.info.Addrs, 114 CACert: s.updater.info.CACert, 115 Tag: names.NewUserTag("jujuanonymous"), 116 }}, 117 }, { 118 "NextExternalControllerWatcherClient", 119 []interface{}{&api.Info{ 120 Addrs: s.watcher.info.Addrs, 121 CACert: s.updater.info.CACert, // only addresses are updated 122 Tag: names.NewUserTag("jujuanonymous"), 123 }}, 124 }}) 125 s.updater.Stub.CheckCalls(c, []testing.StubCall{{ 126 "WatchExternalControllers", 127 []interface{}{}, 128 }, { 129 "ExternalControllerInfo", 130 []interface{}{coretesting.ControllerTag.Id()}, 131 }, { 132 "SetExternalControllerInfo", 133 []interface{}{crossmodel.ControllerInfo{ 134 ControllerTag: s.updater.info.ControllerTag, 135 Alias: s.updater.info.Alias, 136 Addrs: s.watcher.info.Addrs, // new addrs 137 CACert: s.updater.info.CACert, 138 }}, 139 }}) 140 for attempt := coretesting.LongAttempt.Start(); attempt.Next(); { 141 if len(s.watcher.Stub.Calls()) < 6 { 142 continue 143 } 144 s.watcher.Stub.CheckCallNames(c, 145 "WatchControllerInfo", 146 "ControllerInfo", 147 "Close", // close watcher and restart when a change arrives 148 "WatchControllerInfo", 149 "ControllerInfo", // no change 150 "ControllerInfo", // no change 151 ) 152 return 153 } 154 c.Fatal("time out waiting for worker api calls") 155 156 s.updater.Stub.CheckNoCalls(c) 157 } 158 159 func (s *ExternalControllerUpdaterSuite) TestWatchExternalControllers(c *gc.C) { 160 w, err := externalcontrollerupdater.New(&s.updater, s.newWatcher, s.clock) 161 c.Assert(err, jc.ErrorIsNil) 162 defer workertest.CleanKill(c, w) 163 164 s.assertWatchExternalControllersStart(c) 165 } 166 167 func (s *ExternalControllerUpdaterSuite) TestWatchExternalControllersStop(c *gc.C) { 168 w, err := externalcontrollerupdater.New(&s.updater, s.newWatcher, s.clock) 169 c.Assert(err, jc.ErrorIsNil) 170 defer workertest.CleanKill(c, w) 171 172 s.assertWatchExternalControllersStart(c) 173 174 s.updater.Stub.ResetCalls() 175 s.watcher.Stub.ResetCalls() 176 177 s.updater.watcher.changes <- []string{coretesting.ControllerTag.Id()} 178 179 for attempt := coretesting.LongAttempt.Start(); attempt.Next(); { 180 if len(s.watcher.Stub.Calls()) < 1 { 181 continue 182 } 183 s.watcher.Stub.CheckCallNames(c, 184 "Close", 185 ) 186 return 187 } 188 for attempt := coretesting.LongAttempt.Start(); attempt.Next(); { 189 if len(s.updater.Stub.Calls()) < 1 { 190 continue 191 } 192 s.updater.Stub.CheckCallNames(c, 193 "Close", 194 ) 195 return 196 } 197 198 c.Fatal("time out waiting for worker api calls") 199 } 200 201 func (s *ExternalControllerUpdaterSuite) TestWatchExternalControllersErrorsContained(c *gc.C) { 202 // The first time we attempt to connect to the external controller, 203 // the dial should fail. The runner will reschedule the worker to 204 // try again. 205 s.stub.SetErrors(errors.New("no API connection for you")) 206 207 s.updater.watcher.changes <- []string{coretesting.ControllerTag.Id()} 208 s.watcher.watcher.changes = make(chan struct{}) 209 s.watcher.info.Addrs = s.updater.info.Addrs // no change 210 211 w, err := externalcontrollerupdater.New(&s.updater, s.newWatcher, s.clock) 212 c.Assert(err, jc.ErrorIsNil) 213 defer workertest.CleanKill(c, w) 214 215 // The first run of the controller worker should fail to 216 // connect to the API, and should abort. The runner should 217 // then be waiting for a minute to restart the controller 218 // worker. 219 s.clock.WaitAdvance(time.Second, coretesting.LongWait, 1) 220 s.clock.WaitAdvance(59*time.Second, coretesting.LongWait, 1) 221 222 // The controller worker should have been restarted now. 223 select { 224 case s.watcher.watcher.changes <- struct{}{}: 225 case <-time.After(coretesting.LongWait): 226 c.Fatal("timed out waiting to send changes") 227 } 228 229 workertest.CleanKill(c, w) 230 s.stub.CheckCalls(c, []testing.StubCall{{ 231 "NextExternalControllerWatcherClient", 232 []interface{}{&api.Info{ 233 Addrs: s.updater.info.Addrs, 234 CACert: s.updater.info.CACert, 235 Tag: names.NewUserTag("jujuanonymous"), 236 }}, 237 }, { 238 "NextExternalControllerWatcherClient", 239 []interface{}{&api.Info{ 240 Addrs: s.updater.info.Addrs, 241 CACert: s.updater.info.CACert, 242 Tag: names.NewUserTag("jujuanonymous"), 243 }}, 244 }}) 245 s.updater.Stub.CheckCallNames(c, 246 "WatchExternalControllers", 247 "ExternalControllerInfo", 248 "ExternalControllerInfo", 249 ) 250 s.watcher.Stub.CheckCallNames(c, 251 "WatchControllerInfo", 252 "ControllerInfo", 253 "Close", 254 ) 255 }