github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/caasmodelconfigmanager/worker_test.go (about) 1 // Copyright 2021 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package caasmodelconfigmanager_test 5 6 import ( 7 "time" 8 9 "github.com/juju/clock/testclock" 10 "github.com/juju/loggo" 11 "github.com/juju/names/v5" 12 "github.com/juju/testing" 13 jc "github.com/juju/testing/checkers" 14 "github.com/juju/worker/v3" 15 "github.com/juju/worker/v3/workertest" 16 "go.uber.org/mock/gomock" 17 gc "gopkg.in/check.v1" 18 19 "github.com/juju/juju/controller" 20 "github.com/juju/juju/core/watcher" 21 "github.com/juju/juju/core/watcher/watchertest" 22 "github.com/juju/juju/docker" 23 "github.com/juju/juju/docker/registry" 24 registrymocks "github.com/juju/juju/docker/registry/mocks" 25 coretesting "github.com/juju/juju/testing" 26 "github.com/juju/juju/worker/caasmodelconfigmanager" 27 "github.com/juju/juju/worker/caasmodelconfigmanager/mocks" 28 ) 29 30 var _ = gc.Suite(&workerSuite{}) 31 32 type workerSuite struct { 33 testing.IsolationSuite 34 35 modelTag names.ModelTag 36 logger loggo.Logger 37 38 facade *mocks.MockFacade 39 broker *mocks.MockCAASBroker 40 reg *registrymocks.MockRegistry 41 clock testclock.AdvanceableClock 42 controllerConfig controller.Config 43 } 44 45 func (s *workerSuite) SetUpTest(c *gc.C) { 46 s.IsolationSuite.SetUpTest(c) 47 s.modelTag = names.NewModelTag("ffffffff-ffff-ffff-ffff-ffffffffffff") 48 s.logger = loggo.GetLogger("test") 49 s.controllerConfig = coretesting.FakeControllerConfig() 50 s.clock = testclock.NewDilatedWallClock(testing.ShortWait) 51 } 52 53 func (s *workerSuite) TearDownTest(c *gc.C) { 54 s.IsolationSuite.TearDownTest(c) 55 s.facade = nil 56 } 57 58 func (s *workerSuite) TestConfigValidate(c *gc.C) { 59 ctrl := gomock.NewController(c) 60 defer ctrl.Finish() 61 62 cfg := caasmodelconfigmanager.Config{} 63 c.Check(cfg.Validate(), gc.ErrorMatches, `ModelTag is missing not valid`) 64 65 cfg = caasmodelconfigmanager.Config{ 66 ModelTag: s.modelTag, 67 } 68 c.Check(cfg.Validate(), gc.ErrorMatches, `Facade is missing not valid`) 69 70 cfg = caasmodelconfigmanager.Config{ 71 ModelTag: s.modelTag, 72 Facade: mocks.NewMockFacade(ctrl), 73 } 74 c.Check(cfg.Validate(), gc.ErrorMatches, `Broker is missing not valid`) 75 76 cfg = caasmodelconfigmanager.Config{ 77 ModelTag: s.modelTag, 78 Facade: mocks.NewMockFacade(ctrl), 79 Broker: mocks.NewMockCAASBroker(ctrl), 80 } 81 c.Check(cfg.Validate(), gc.ErrorMatches, `Logger is missing not valid`) 82 83 cfg = caasmodelconfigmanager.Config{ 84 ModelTag: s.modelTag, 85 Facade: mocks.NewMockFacade(ctrl), 86 Broker: mocks.NewMockCAASBroker(ctrl), 87 Logger: s.logger, 88 } 89 c.Check(cfg.Validate(), gc.ErrorMatches, `Clock is missing not valid`) 90 91 cfg = caasmodelconfigmanager.Config{ 92 ModelTag: s.modelTag, 93 Facade: mocks.NewMockFacade(ctrl), 94 Broker: mocks.NewMockCAASBroker(ctrl), 95 Logger: s.logger, 96 Clock: s.clock, 97 } 98 c.Check(cfg.Validate(), gc.ErrorMatches, `RegistryFunc is missing not valid`) 99 100 cfg = caasmodelconfigmanager.Config{ 101 ModelTag: s.modelTag, 102 Facade: mocks.NewMockFacade(ctrl), 103 Broker: mocks.NewMockCAASBroker(ctrl), 104 Logger: s.logger, 105 Clock: s.clock, 106 RegistryFunc: func(i docker.ImageRepoDetails) (registry.Registry, error) { return nil, nil }, 107 } 108 c.Check(cfg.Validate(), jc.ErrorIsNil) 109 } 110 111 func (s *workerSuite) getWorkerStarter(c *gc.C) (func(...any) worker.Worker, *gomock.Controller) { 112 ctrl := gomock.NewController(c) 113 114 s.facade = mocks.NewMockFacade(ctrl) 115 s.broker = mocks.NewMockCAASBroker(ctrl) 116 s.reg = registrymocks.NewMockRegistry(ctrl) 117 118 cfg := caasmodelconfigmanager.Config{ 119 ModelTag: s.modelTag, 120 Logger: s.logger, 121 Facade: s.facade, 122 Broker: s.broker, 123 Clock: s.clock, 124 RegistryFunc: func(i docker.ImageRepoDetails) (registry.Registry, error) { 125 c.Check(i, gc.DeepEquals, s.CAASImageRepo(c)) 126 return s.reg, nil 127 }, 128 } 129 return func(calls ...any) worker.Worker { 130 gomock.InOrder(calls...) 131 w, err := caasmodelconfigmanager.NewWorker(cfg) 132 c.Assert(err, jc.ErrorIsNil) 133 return w 134 }, ctrl 135 } 136 137 func (s *workerSuite) TestWorkerTokenRefreshRequired(c *gc.C) { 138 s.controllerConfig[controller.CAASImageRepo] = ` 139 { 140 "serveraddress": "66668888.dkr.ecr.eu-west-1.amazonaws.com", 141 "username": "aws_access_key_id", 142 "repository": "66668888.dkr.ecr.eu-west-1.amazonaws.com", 143 "password": "aws_secret_access_key", 144 "region": "ap-southeast-2" 145 }`[1:] 146 147 refreshed := s.CAASImageRepo(c) 148 refreshed.Auth = docker.NewToken(`refreshed===`) 149 150 done := make(chan struct{}, 1) 151 startWorker, ctrl := s.getWorkerStarter(c) 152 defer ctrl.Finish() 153 154 controllerConfigChangedChan := make(chan struct{}, 1) 155 w := startWorker( 156 s.facade.EXPECT().WatchControllerConfig().DoAndReturn(func() (watcher.NotifyWatcher, error) { 157 controllerConfigChangedChan <- struct{}{} 158 return watchertest.NewMockNotifyWatcher(controllerConfigChangedChan), nil 159 }), 160 // 1st round. 161 s.facade.EXPECT().ControllerConfig().Return(s.controllerConfig, nil), 162 s.reg.EXPECT().Ping().Return(nil), 163 s.reg.EXPECT().ShouldRefreshAuth().Return(true, time.Duration(0)), 164 s.reg.EXPECT().RefreshAuth().Return(nil), 165 s.reg.EXPECT().ImageRepoDetails().DoAndReturn(func() docker.ImageRepoDetails { 166 o := s.CAASImageRepo(c) 167 c.Check(o, gc.DeepEquals, docker.ImageRepoDetails{ 168 ServerAddress: "66668888.dkr.ecr.eu-west-1.amazonaws.com", 169 Repository: "66668888.dkr.ecr.eu-west-1.amazonaws.com", 170 Region: "ap-southeast-2", 171 BasicAuthConfig: docker.BasicAuthConfig{ 172 Username: "aws_access_key_id", 173 Password: "aws_secret_access_key", 174 }, 175 }) 176 return o 177 }), 178 s.broker.EXPECT().EnsureImageRepoSecret(gomock.Any()).DoAndReturn(func(i docker.ImageRepoDetails) error { 179 c.Check(i, gc.DeepEquals, s.CAASImageRepo(c)) 180 return nil 181 }), 182 // 2nd round. 183 s.reg.EXPECT().ShouldRefreshAuth().Return(true, time.Duration(0)), 184 s.reg.EXPECT().RefreshAuth().Return(nil), 185 s.reg.EXPECT().ImageRepoDetails().Return(refreshed), 186 s.broker.EXPECT().EnsureImageRepoSecret(gomock.Any()).DoAndReturn(func(i docker.ImageRepoDetails) error { 187 c.Check(i, gc.DeepEquals, refreshed) 188 close(done) 189 return nil 190 }), 191 s.reg.EXPECT().Close().Return(nil), 192 ) 193 194 select { 195 case <-done: 196 case <-time.After(coretesting.LongWait): 197 c.Fatalf("timed out waiting for worker to start") 198 } 199 200 workertest.CleanKill(c, w) 201 } 202 203 func (s *workerSuite) TestWorkerTokenRefreshNotRequiredThenRetry(c *gc.C) { 204 s.controllerConfig[controller.CAASImageRepo] = ` 205 { 206 "serveraddress": "66668888.dkr.ecr.eu-west-1.amazonaws.com", 207 "username": "aws_access_key_id", 208 "repository": "66668888.dkr.ecr.eu-west-1.amazonaws.com", 209 "password": "aws_secret_access_key", 210 "region": "ap-southeast-2" 211 }`[1:] 212 213 done := make(chan struct{}, 1) 214 startWorker, ctrl := s.getWorkerStarter(c) 215 defer ctrl.Finish() 216 217 controllerConfigChangedChan := make(chan struct{}, 1) 218 w := startWorker( 219 s.facade.EXPECT().WatchControllerConfig().DoAndReturn(func() (watcher.NotifyWatcher, error) { 220 controllerConfigChangedChan <- struct{}{} 221 return watchertest.NewMockNotifyWatcher(controllerConfigChangedChan), nil 222 }), 223 // 1st round. 224 s.facade.EXPECT().ControllerConfig().Return(s.controllerConfig, nil), 225 s.reg.EXPECT().Ping().Return(nil), 226 s.reg.EXPECT().ShouldRefreshAuth().Return(true, time.Duration(0)), 227 s.reg.EXPECT().RefreshAuth().Return(nil), 228 s.reg.EXPECT().ImageRepoDetails().DoAndReturn(func() docker.ImageRepoDetails { 229 o := s.CAASImageRepo(c) 230 c.Check(o, gc.DeepEquals, docker.ImageRepoDetails{ 231 ServerAddress: "66668888.dkr.ecr.eu-west-1.amazonaws.com", 232 Repository: "66668888.dkr.ecr.eu-west-1.amazonaws.com", 233 Region: "ap-southeast-2", 234 BasicAuthConfig: docker.BasicAuthConfig{ 235 Username: "aws_access_key_id", 236 Password: "aws_secret_access_key", 237 }, 238 }) 239 return o 240 }), 241 s.broker.EXPECT().EnsureImageRepoSecret(gomock.Any()).DoAndReturn(func(i docker.ImageRepoDetails) error { 242 c.Check(i, gc.DeepEquals, s.CAASImageRepo(c)) 243 return nil 244 }), 245 // 2nd round. 246 s.reg.EXPECT().ShouldRefreshAuth().DoAndReturn(func() (bool, time.Duration) { 247 return false, 1 * time.Second 248 }), 249 // 3rd round. 250 s.reg.EXPECT().ShouldRefreshAuth().DoAndReturn(func() (bool, time.Duration) { 251 return true, time.Duration(0) 252 }), 253 s.reg.EXPECT().RefreshAuth().Return(nil), 254 s.reg.EXPECT().ImageRepoDetails().Return(s.CAASImageRepo(c)), 255 s.broker.EXPECT().EnsureImageRepoSecret(gomock.Any()).DoAndReturn(func(i docker.ImageRepoDetails) error { 256 c.Check(i, gc.DeepEquals, s.CAASImageRepo(c)) 257 close(done) 258 return nil 259 }), 260 s.reg.EXPECT().Close().Return(nil), 261 ) 262 263 select { 264 case <-done: 265 case <-time.After(coretesting.LongWait): 266 c.Fatalf("timed out waiting for worker to start") 267 } 268 269 workertest.CleanKill(c, w) 270 } 271 272 func (s *workerSuite) TestWorkerNoOpsForPublicRepo(c *gc.C) { 273 s.controllerConfig[controller.CAASImageRepo] = ` 274 { 275 "serveraddress": "66668888.dkr.ecr.eu-west-1.amazonaws.com", 276 "repository": "66668888.dkr.ecr.eu-west-1.amazonaws.com", 277 "region": "ap-southeast-2", 278 }`[1:] 279 280 done := make(chan struct{}, 1) 281 startWorker, ctrl := s.getWorkerStarter(c) 282 defer ctrl.Finish() 283 284 controllerConfigChangedChan := make(chan struct{}, 1) 285 w := startWorker( 286 s.facade.EXPECT().WatchControllerConfig().DoAndReturn(func() (watcher.NotifyWatcher, error) { 287 controllerConfigChangedChan <- struct{}{} 288 return watchertest.NewMockNotifyWatcher(controllerConfigChangedChan), nil 289 }), 290 s.facade.EXPECT().ControllerConfig().DoAndReturn(func() (controller.Config, error) { 291 close(done) 292 return s.controllerConfig, nil 293 }), 294 ) 295 296 select { 297 case <-done: 298 case <-time.After(coretesting.LongWait): 299 c.Fatalf("timed out waiting for worker to start") 300 } 301 302 workertest.CleanKill(c, w) 303 } 304 305 func (s *workerSuite) CAASImageRepo(c *gc.C) docker.ImageRepoDetails { 306 r, err := docker.NewImageRepoDetails(s.controllerConfig.CAASImageRepo()) 307 c.Assert(err, jc.ErrorIsNil) 308 return r 309 }