github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/caasbroker/broker_test.go (about) 1 // Copyright 2017 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package caasbroker_test 5 6 import ( 7 "context" 8 "time" 9 10 "github.com/juju/errors" 11 "github.com/juju/loggo" 12 jc "github.com/juju/testing/checkers" 13 "github.com/juju/utils/v3" 14 "github.com/juju/worker/v3/workertest" 15 gc "gopkg.in/check.v1" 16 17 "github.com/juju/juju/caas" 18 "github.com/juju/juju/environs" 19 environscloudspec "github.com/juju/juju/environs/cloudspec" 20 coretesting "github.com/juju/juju/testing" 21 "github.com/juju/juju/worker/caasbroker" 22 ) 23 24 type TrackerSuite struct { 25 coretesting.BaseSuite 26 } 27 28 var _ = gc.Suite(&TrackerSuite{}) 29 30 func (s *TrackerSuite) validConfig() caasbroker.Config { 31 return caasbroker.Config{ 32 ConfigAPI: &runContext{}, 33 NewContainerBrokerFunc: func(context.Context, environs.OpenParams) (caas.Broker, error) { 34 return nil, errors.NotImplementedf("test func") 35 }, 36 Logger: loggo.GetLogger("test"), 37 } 38 } 39 40 func (s *TrackerSuite) TestValidateObserver(c *gc.C) { 41 config := s.validConfig() 42 config.ConfigAPI = nil 43 s.testValidate(c, config, func(err error) { 44 c.Check(err, jc.Satisfies, errors.IsNotValid) 45 c.Check(err, gc.ErrorMatches, "nil ConfigAPI not valid") 46 }) 47 } 48 49 func (s *TrackerSuite) TestValidateNewBrokerFunc(c *gc.C) { 50 config := s.validConfig() 51 config.NewContainerBrokerFunc = nil 52 s.testValidate(c, config, func(err error) { 53 c.Check(err, jc.Satisfies, errors.IsNotValid) 54 c.Check(err, gc.ErrorMatches, "nil NewContainerBrokerFunc not valid") 55 }) 56 } 57 58 func (s *TrackerSuite) TestValidateLogger(c *gc.C) { 59 config := s.validConfig() 60 config.Logger = nil 61 s.testValidate(c, config, func(err error) { 62 c.Check(err, jc.Satisfies, errors.IsNotValid) 63 c.Check(err, gc.ErrorMatches, "nil Logger not valid") 64 }) 65 } 66 67 func (s *TrackerSuite) testValidate(c *gc.C, config caasbroker.Config, check func(err error)) { 68 err := config.Validate() 69 check(err) 70 71 tracker, err := caasbroker.NewTracker(config) 72 c.Check(tracker, gc.IsNil) 73 check(err) 74 } 75 76 func (s *TrackerSuite) TestCloudSpecFails(c *gc.C) { 77 fix := &fixture{ 78 observerErrs: []error{ 79 errors.New("no you"), 80 }, 81 } 82 fix.Run(c, func(context *runContext) { 83 tracker, err := caasbroker.NewTracker(caasbroker.Config{ 84 ConfigAPI: context, 85 NewContainerBrokerFunc: newMockBroker, 86 Logger: loggo.GetLogger("test"), 87 }) 88 c.Check(err, gc.ErrorMatches, "cannot get cloud information: no you") 89 c.Check(tracker, gc.IsNil) 90 context.CheckCallNames(c, "CloudSpec") 91 }) 92 } 93 94 func (s *TrackerSuite) validFixture() *fixture { 95 cloudSpec := environscloudspec.CloudSpec{ 96 Name: "foo", 97 Type: "bar", 98 Region: "baz", 99 } 100 cfg := coretesting.FakeConfig() 101 cfg["type"] = "kubernetes" 102 cfg["uuid"] = utils.MustNewUUID().String() 103 return &fixture{initialSpec: cloudSpec, initialConfig: cfg} 104 } 105 106 func (s *TrackerSuite) TestSuccess(c *gc.C) { 107 fix := s.validFixture() 108 fix.Run(c, func(context *runContext) { 109 tracker, err := caasbroker.NewTracker(caasbroker.Config{ 110 ConfigAPI: context, 111 NewContainerBrokerFunc: newMockBroker, 112 Logger: loggo.GetLogger("test"), 113 }) 114 c.Assert(err, jc.ErrorIsNil) 115 defer workertest.CleanKill(c, tracker) 116 117 gotBroker := tracker.Broker() 118 c.Assert(gotBroker, gc.NotNil) 119 }) 120 } 121 122 func (s *TrackerSuite) TestInitialise(c *gc.C) { 123 fix := s.validFixture() 124 fix.Run(c, func(runContext *runContext) { 125 tracker, err := caasbroker.NewTracker(caasbroker.Config{ 126 ConfigAPI: runContext, 127 NewContainerBrokerFunc: func(_ context.Context, args environs.OpenParams) (caas.Broker, error) { 128 c.Assert(args.Cloud, jc.DeepEquals, fix.initialSpec) 129 c.Assert(args.Config.Name(), jc.DeepEquals, "testmodel") 130 return nil, errors.NotValidf("cloud spec") 131 }, 132 Logger: loggo.GetLogger("test"), 133 }) 134 c.Check(err, gc.ErrorMatches, `cannot create caas broker: cloud spec not valid`) 135 c.Check(tracker, gc.IsNil) 136 runContext.CheckCallNames(c, "CloudSpec", "ModelConfig", "ControllerConfig") 137 }) 138 } 139 140 func (s *TrackerSuite) TestModelConfigFails(c *gc.C) { 141 fix := &fixture{ 142 observerErrs: []error{ 143 nil, 144 errors.New("no you"), 145 }, 146 } 147 fix.Run(c, func(context *runContext) { 148 tracker, err := caasbroker.NewTracker(caasbroker.Config{ 149 ConfigAPI: context, 150 NewContainerBrokerFunc: newMockBroker, 151 Logger: loggo.GetLogger("test"), 152 }) 153 c.Check(err, gc.ErrorMatches, "no you") 154 c.Check(tracker, gc.IsNil) 155 context.CheckCallNames(c, "CloudSpec", "ModelConfig") 156 }) 157 } 158 159 func (s *TrackerSuite) TestModelConfigInvalid(c *gc.C) { 160 fix := &fixture{} 161 fix.Run(c, func(runContext *runContext) { 162 tracker, err := caasbroker.NewTracker(caasbroker.Config{ 163 ConfigAPI: runContext, 164 NewContainerBrokerFunc: func(context.Context, environs.OpenParams) (caas.Broker, error) { 165 return nil, errors.NotValidf("config") 166 }, 167 Logger: loggo.GetLogger("test"), 168 }) 169 c.Check(err, gc.ErrorMatches, `cannot create caas broker: config not valid`) 170 c.Check(tracker, gc.IsNil) 171 runContext.CheckCallNames(c, "CloudSpec", "ModelConfig", "ControllerConfig") 172 }) 173 } 174 175 func (s *TrackerSuite) TestModelConfigValid(c *gc.C) { 176 fix := &fixture{ 177 initialConfig: coretesting.Attrs{ 178 "name": "this-particular-name", 179 }, 180 } 181 fix.Run(c, func(context *runContext) { 182 tracker, err := caasbroker.NewTracker(caasbroker.Config{ 183 ConfigAPI: context, 184 NewContainerBrokerFunc: newMockBroker, 185 Logger: loggo.GetLogger("test"), 186 }) 187 c.Assert(err, jc.ErrorIsNil) 188 defer workertest.CleanKill(c, tracker) 189 190 gotBroker := tracker.Broker() 191 c.Assert(gotBroker, gc.NotNil) 192 c.Check(gotBroker.Config().Name(), gc.Equals, "this-particular-name") 193 }) 194 } 195 196 func (s *TrackerSuite) TestCloudSpecInvalid(c *gc.C) { 197 cloudSpec := environscloudspec.CloudSpec{ 198 Name: "foo", 199 Type: "bar", 200 Region: "baz", 201 } 202 fix := &fixture{initialSpec: cloudSpec} 203 fix.Run(c, func(runContext *runContext) { 204 tracker, err := caasbroker.NewTracker(caasbroker.Config{ 205 ConfigAPI: runContext, 206 NewContainerBrokerFunc: func(_ context.Context, args environs.OpenParams) (caas.Broker, error) { 207 c.Assert(args.Cloud, jc.DeepEquals, cloudSpec) 208 return nil, errors.NotValidf("cloud spec") 209 }, 210 Logger: loggo.GetLogger("test"), 211 }) 212 c.Check(err, gc.ErrorMatches, `cannot create caas broker: cloud spec not valid`) 213 c.Check(tracker, gc.IsNil) 214 runContext.CheckCallNames(c, "CloudSpec", "ModelConfig", "ControllerConfig") 215 }) 216 } 217 218 func (s *TrackerSuite) TestWatchFails(c *gc.C) { 219 fix := &fixture{ 220 observerErrs: []error{ 221 nil, nil, nil, errors.New("grrk splat"), 222 }, 223 } 224 fix.Run(c, func(context *runContext) { 225 tracker, err := caasbroker.NewTracker(caasbroker.Config{ 226 ConfigAPI: context, 227 NewContainerBrokerFunc: newMockBroker, 228 Logger: loggo.GetLogger("test"), 229 }) 230 c.Assert(err, jc.ErrorIsNil) 231 defer workertest.DirtyKill(c, tracker) 232 233 err = workertest.CheckKilled(c, tracker) 234 c.Check(err, gc.ErrorMatches, "cannot watch model config: grrk splat") 235 context.CheckCallNames(c, "CloudSpec", "ModelConfig", "ControllerConfig", "WatchForModelConfigChanges") 236 }) 237 } 238 239 func (s *TrackerSuite) TestModelConfigWatchCloses(c *gc.C) { 240 fix := &fixture{} 241 fix.Run(c, func(context *runContext) { 242 tracker, err := caasbroker.NewTracker(caasbroker.Config{ 243 ConfigAPI: context, 244 NewContainerBrokerFunc: newMockBroker, 245 Logger: loggo.GetLogger("test"), 246 }) 247 c.Assert(err, jc.ErrorIsNil) 248 defer workertest.DirtyKill(c, tracker) 249 250 context.CloseModelConfigNotify() 251 err = workertest.CheckKilled(c, tracker) 252 c.Check(err, gc.ErrorMatches, "model config watch closed") 253 context.CheckCallNames(c, "CloudSpec", "ModelConfig", "ControllerConfig", "WatchForModelConfigChanges", "WatchCloudSpecChanges") 254 }) 255 } 256 257 func (s *TrackerSuite) TestCloudSpecWatchCloses(c *gc.C) { 258 fix := &fixture{} 259 fix.Run(c, func(context *runContext) { 260 tracker, err := caasbroker.NewTracker(caasbroker.Config{ 261 ConfigAPI: context, 262 NewContainerBrokerFunc: newMockBroker, 263 Logger: loggo.GetLogger("test"), 264 }) 265 c.Assert(err, jc.ErrorIsNil) 266 defer workertest.DirtyKill(c, tracker) 267 268 context.CloseCloudSpecNotify() 269 err = workertest.CheckKilled(c, tracker) 270 c.Check(err, gc.ErrorMatches, "cloud watch closed") 271 context.CheckCallNames(c, "CloudSpec", "ModelConfig", "ControllerConfig", "WatchForModelConfigChanges", "WatchCloudSpecChanges") 272 }) 273 } 274 275 func (s *TrackerSuite) TestWatchedModelConfigFails(c *gc.C) { 276 fix := &fixture{ 277 observerErrs: []error{ 278 nil, nil, nil, nil, nil, errors.New("blam ouch"), 279 }, 280 } 281 fix.Run(c, func(context *runContext) { 282 tracker, err := caasbroker.NewTracker(caasbroker.Config{ 283 ConfigAPI: context, 284 NewContainerBrokerFunc: newMockBroker, 285 Logger: loggo.GetLogger("test"), 286 }) 287 c.Check(err, jc.ErrorIsNil) 288 defer workertest.DirtyKill(c, tracker) 289 290 context.SendModelConfigNotify() 291 context.SendCloudSpecNotify() 292 err = workertest.CheckKilled(c, tracker) 293 c.Check(err, gc.ErrorMatches, "cannot read model config: blam ouch") 294 }) 295 } 296 297 func (s *TrackerSuite) TestWatchedModelConfigIncompatible(c *gc.C) { 298 fix := &fixture{} 299 fix.Run(c, func(runContext *runContext) { 300 tracker, err := caasbroker.NewTracker(caasbroker.Config{ 301 ConfigAPI: runContext, 302 NewContainerBrokerFunc: func(context.Context, environs.OpenParams) (caas.Broker, error) { 303 broker := &mockBroker{} 304 broker.SetErrors(errors.New("SetConfig is broken")) 305 return broker, nil 306 }, 307 Logger: loggo.GetLogger("test"), 308 }) 309 c.Check(err, jc.ErrorIsNil) 310 defer workertest.DirtyKill(c, tracker) 311 312 runContext.SendModelConfigNotify() 313 err = workertest.CheckKilled(c, tracker) 314 c.Check(err, gc.ErrorMatches, "cannot update model config: SetConfig is broken") 315 runContext.CheckCallNames(c, "CloudSpec", "ModelConfig", "ControllerConfig", "WatchForModelConfigChanges", "WatchCloudSpecChanges", "ModelConfig") 316 }) 317 } 318 319 func (s *TrackerSuite) TestWatchedModelConfigUpdates(c *gc.C) { 320 fix := &fixture{ 321 initialConfig: coretesting.Attrs{ 322 "name": "original-name", 323 }, 324 } 325 fix.Run(c, func(context *runContext) { 326 tracker, err := caasbroker.NewTracker(caasbroker.Config{ 327 ConfigAPI: context, 328 NewContainerBrokerFunc: newMockBroker, 329 Logger: loggo.GetLogger("test"), 330 }) 331 c.Check(err, jc.ErrorIsNil) 332 defer workertest.CleanKill(c, tracker) 333 334 context.SetConfig(c, coretesting.Attrs{ 335 "name": "updated-name", 336 }) 337 gotBroker := tracker.Broker() 338 c.Assert(gotBroker.Config().Name(), gc.Equals, "original-name") 339 340 timeout := time.After(coretesting.LongWait) 341 attempt := time.After(0) 342 context.SendModelConfigNotify() 343 for { 344 select { 345 case <-attempt: 346 name := gotBroker.Config().Name() 347 if name == "original-name" { 348 attempt = time.After(coretesting.ShortWait) 349 continue 350 } 351 c.Check(name, gc.Equals, "updated-name") 352 case <-timeout: 353 c.Fatalf("timed out waiting for broker to be updated") 354 } 355 break 356 } 357 }) 358 } 359 360 func (s *TrackerSuite) TestWatchedCloudSpecUpdates(c *gc.C) { 361 fix := &fixture{ 362 initialSpec: environscloudspec.CloudSpec{Name: "cloud", Type: "lxd"}, 363 } 364 fix.Run(c, func(context *runContext) { 365 tracker, err := caasbroker.NewTracker(caasbroker.Config{ 366 ConfigAPI: context, 367 NewContainerBrokerFunc: newMockBroker, 368 Logger: loggo.GetLogger("test"), 369 }) 370 c.Check(err, jc.ErrorIsNil) 371 defer workertest.CleanKill(c, tracker) 372 373 context.SetCloudSpec(c, environscloudspec.CloudSpec{Name: "lxd", Type: "lxd", Endpoint: "http://api"}) 374 gotBroker := tracker.Broker().(*mockBroker) 375 c.Assert(gotBroker.CloudSpec(), jc.DeepEquals, fix.initialSpec) 376 377 timeout := time.After(coretesting.LongWait) 378 attempt := time.After(0) 379 context.SendCloudSpecNotify() 380 for { 381 select { 382 case <-attempt: 383 ep := gotBroker.CloudSpec().Endpoint 384 if ep == "" { 385 attempt = time.After(coretesting.ShortWait) 386 continue 387 } 388 c.Check(ep, gc.Equals, "http://api") 389 case <-timeout: 390 c.Fatalf("timed out waiting for environ to be updated") 391 } 392 break 393 } 394 }) 395 }