github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/endpoint_bindings_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/charm/v12" 8 "github.com/juju/errors" 9 "github.com/juju/testing" 10 jc "github.com/juju/testing/checkers" 11 "go.uber.org/mock/gomock" 12 gc "gopkg.in/check.v1" 13 14 "github.com/juju/juju/core/network" 15 "github.com/juju/juju/state" 16 "github.com/juju/juju/state/mocks" 17 ) 18 19 type bindingsSuite struct { 20 ConnSuite 21 22 oldMeta *charm.Meta 23 oldDefaults map[string]string 24 newMeta *charm.Meta 25 newDefaults map[string]string 26 27 clientSpace *state.Space 28 appsSpace *state.Space 29 barbSpace *state.Space 30 dbSpace *state.Space 31 } 32 33 var _ = gc.Suite(&bindingsSuite{}) 34 35 func (s *bindingsSuite) SetUpTest(c *gc.C) { 36 s.ConnSuite.SetUpTest(c) 37 38 const dummyCharmWithOneOfEachRelationTypeAndExtraBindings = ` 39 name: dummy 40 summary: "That's a dummy charm with one relation of each type and extra-bindings." 41 description: "This is a longer description." 42 provides: 43 foo1: 44 interface: phony 45 requires: 46 bar1: 47 interface: fake 48 peers: 49 self: 50 interface: dummy 51 extra-bindings: 52 one-extra: 53 ` 54 oldCharm := s.AddMetaCharm(c, "dummy", dummyCharmWithOneOfEachRelationTypeAndExtraBindings, 1) 55 s.oldMeta = oldCharm.Meta() 56 s.oldDefaults = map[string]string{ 57 "": network.AlphaSpaceId, 58 "foo1": network.AlphaSpaceId, 59 "bar1": network.AlphaSpaceId, 60 "self": network.AlphaSpaceId, 61 "one-extra": network.AlphaSpaceId, 62 } 63 64 const dummyCharmWithTwoOfEachRelationTypeAndNoExtraBindings = ` 65 name: dummy 66 summary: "That's a dummy charm with 2 relations for each type." 67 description: "This is a longer description." 68 provides: 69 foo1: 70 interface: phony 71 foo2: 72 interface: secret 73 requires: 74 bar2: real 75 bar3: 76 interface: cool 77 peers: 78 self: 79 interface: dummy 80 me: peer 81 ` 82 newCharm := s.AddMetaCharm(c, "dummy", dummyCharmWithTwoOfEachRelationTypeAndNoExtraBindings, 2) 83 s.newMeta = newCharm.Meta() 84 s.newDefaults = map[string]string{ 85 "foo1": network.AlphaSpaceId, 86 "foo2": network.AlphaSpaceId, 87 "bar2": network.AlphaSpaceId, 88 "bar3": network.AlphaSpaceId, 89 "self": network.AlphaSpaceId, 90 "me": network.AlphaSpaceId, 91 } 92 93 // Add some spaces to use in bindings, but notably NOT the default space, as 94 // it should be always allowed. 95 96 var err error 97 s.clientSpace, err = s.State.AddSpace("client", "", nil, true) 98 c.Assert(err, jc.ErrorIsNil) 99 s.appsSpace, err = s.State.AddSpace("apps", "", nil, true) 100 c.Assert(err, jc.ErrorIsNil) 101 s.dbSpace, err = s.State.AddSpace("db", "", nil, true) 102 c.Assert(err, jc.ErrorIsNil) 103 s.barbSpace, err = s.State.AddSpace("barb3", "", nil, true) 104 c.Assert(err, jc.ErrorIsNil) 105 } 106 107 func (s *bindingsSuite) TestMergeBindings(c *gc.C) { 108 // The test cases below are not exhaustive, but just check basic 109 // functionality. Most of the logic is tested by calling application.SetCharm() 110 // in various ways. 111 112 for i, test := range []struct { 113 about string 114 mergeWithMap, currentMap map[string]string 115 meta *charm.Meta 116 updated map[string]string 117 modified bool 118 }{{ 119 about: "defaults used when both mergeWithMap and currentMap are nil", 120 mergeWithMap: nil, 121 currentMap: nil, 122 meta: s.oldMeta, 123 updated: s.copyMap(s.oldDefaults), 124 modified: true, 125 }, { 126 about: "currentMap overrides defaults, mergeWithMap is nil", 127 mergeWithMap: nil, 128 currentMap: map[string]string{ 129 "foo1": s.clientSpace.Id(), 130 "self": s.dbSpace.Id(), 131 }, 132 meta: s.oldMeta, 133 updated: map[string]string{ 134 "": network.AlphaSpaceId, 135 "foo1": s.clientSpace.Id(), 136 "bar1": network.AlphaSpaceId, 137 "self": s.dbSpace.Id(), 138 "one-extra": network.AlphaSpaceId, 139 }, 140 modified: true, 141 }, { 142 about: "currentMap overrides defaults, mergeWithMap overrides currentMap", 143 mergeWithMap: map[string]string{ 144 "": network.AlphaSpaceId, 145 "foo1": network.AlphaSpaceId, 146 "self": s.dbSpace.Id(), 147 "bar1": s.clientSpace.Id(), 148 "one-extra": s.appsSpace.Id(), 149 }, 150 currentMap: map[string]string{ 151 "foo1": s.clientSpace.Id(), 152 "bar1": s.dbSpace.Id(), 153 }, 154 meta: s.oldMeta, 155 updated: map[string]string{ 156 "": network.AlphaSpaceId, 157 "foo1": network.AlphaSpaceId, 158 "bar1": s.clientSpace.Id(), 159 "self": s.dbSpace.Id(), 160 "one-extra": s.appsSpace.Id(), 161 }, 162 modified: true, 163 }, { 164 about: "mergeWithMap overrides defaults, currentMap is nil", 165 mergeWithMap: map[string]string{ 166 "self": s.dbSpace.Id(), 167 }, 168 currentMap: nil, 169 meta: s.oldMeta, 170 updated: map[string]string{ 171 "": network.AlphaSpaceId, 172 "foo1": network.AlphaSpaceId, 173 "bar1": network.AlphaSpaceId, 174 "self": s.dbSpace.Id(), 175 "one-extra": network.AlphaSpaceId, 176 }, 177 modified: true, 178 }, { 179 about: "obsolete entries in currentMap missing in defaults are removed", 180 mergeWithMap: nil, 181 currentMap: map[string]string{ 182 "any-old-thing": s.dbSpace.Id(), 183 "self": s.dbSpace.Id(), 184 "one-extra": s.appsSpace.Id(), 185 }, 186 meta: s.oldMeta, 187 updated: map[string]string{ 188 "": network.AlphaSpaceId, 189 "foo1": network.AlphaSpaceId, 190 "bar1": network.AlphaSpaceId, 191 "self": s.dbSpace.Id(), 192 "one-extra": s.appsSpace.Id(), 193 }, 194 modified: true, 195 }, { 196 about: "new endpoints use defaults unless specified in mergeWithMap, existing ones are kept", 197 mergeWithMap: map[string]string{ 198 "foo2": s.dbSpace.Id(), 199 "me": s.clientSpace.Id(), 200 "bar3": s.dbSpace.Id(), 201 }, 202 currentMap: s.copyMap(s.oldDefaults), 203 meta: s.newMeta, 204 updated: map[string]string{ 205 "": network.AlphaSpaceId, 206 "foo1": network.AlphaSpaceId, 207 "foo2": s.dbSpace.Id(), 208 "bar2": network.AlphaSpaceId, 209 "bar3": s.dbSpace.Id(), 210 "self": network.AlphaSpaceId, 211 "me": s.clientSpace.Id(), 212 }, 213 modified: true, 214 }, { 215 about: "new default supersedes old default", 216 mergeWithMap: map[string]string{ 217 "": s.clientSpace.Name(), 218 "bar3": s.barbSpace.Name(), 219 }, 220 currentMap: map[string]string{ 221 "": s.appsSpace.Id(), 222 "foo1": s.appsSpace.Id(), 223 "bar1": s.dbSpace.Id(), 224 "self": network.AlphaSpaceId, 225 "one-extra": s.barbSpace.Id(), 226 }, 227 meta: s.newMeta, 228 updated: map[string]string{ 229 "": s.clientSpace.Id(), 230 "foo1": s.appsSpace.Id(), 231 "foo2": s.clientSpace.Id(), 232 "bar2": s.clientSpace.Id(), 233 "bar3": s.barbSpace.Id(), 234 "self": network.AlphaSpaceId, 235 "me": s.clientSpace.Id(), 236 }, 237 modified: true, 238 }, { 239 about: "new map one change", 240 mergeWithMap: map[string]string{ 241 "self": s.barbSpace.Name(), 242 }, 243 currentMap: map[string]string{ 244 "": s.appsSpace.Id(), 245 "foo1": s.appsSpace.Id(), 246 "bar1": s.dbSpace.Id(), 247 "self": network.AlphaSpaceId, 248 "one-extra": s.clientSpace.Id(), 249 }, 250 meta: s.oldMeta, 251 updated: map[string]string{ 252 "": s.appsSpace.Id(), 253 "foo1": s.appsSpace.Id(), 254 "bar1": s.dbSpace.Id(), 255 "self": s.barbSpace.Id(), 256 "one-extra": s.clientSpace.Id(), 257 }, 258 modified: true, 259 }, { 260 about: "old unchanged but different key", 261 mergeWithMap: nil, 262 currentMap: map[string]string{ 263 "": s.appsSpace.Id(), 264 "bar1": s.dbSpace.Id(), 265 "self": network.AlphaSpaceId, 266 "lost": s.clientSpace.Id(), 267 "one-extra": s.clientSpace.Id(), 268 }, 269 meta: s.oldMeta, 270 updated: map[string]string{ 271 "": s.appsSpace.Id(), 272 "foo1": s.appsSpace.Id(), 273 "bar1": s.dbSpace.Id(), 274 "self": network.AlphaSpaceId, 275 "one-extra": s.clientSpace.Id(), 276 }, 277 modified: true, 278 }} { 279 c.Logf("test #%d: %s", i, test.about) 280 b, err := state.NewBindings(s.State, test.currentMap) 281 c.Assert(err, jc.ErrorIsNil) 282 isModified, err := b.Merge(test.mergeWithMap, test.meta) 283 c.Check(err, jc.ErrorIsNil) 284 c.Check(b.Map(), jc.DeepEquals, test.updated) 285 c.Check(isModified, gc.Equals, test.modified) 286 } 287 } 288 289 func (s *bindingsSuite) TestMergeWithModelConfigNonDefaultSpace(c *gc.C) { 290 err := s.Model.UpdateModelConfig(map[string]interface{}{"default-space": s.appsSpace.Name()}, nil) 291 c.Assert(err, jc.ErrorIsNil) 292 293 currentMap := map[string]string{ 294 "foo1": s.clientSpace.Id(), 295 "self": s.dbSpace.Id(), 296 } 297 updated := map[string]string{ 298 "": s.appsSpace.Id(), 299 "foo1": s.clientSpace.Id(), 300 "bar1": s.appsSpace.Id(), 301 "self": s.dbSpace.Id(), 302 "one-extra": s.appsSpace.Id(), 303 } 304 305 b, err := state.NewBindings(s.State, currentMap) 306 c.Assert(err, jc.ErrorIsNil) 307 isModified, err := b.Merge(nil, s.oldMeta) 308 c.Check(err, jc.ErrorIsNil) 309 c.Check(b.Map(), jc.DeepEquals, updated) 310 c.Check(isModified, gc.Equals, true) 311 } 312 313 func (s *bindingsSuite) TestDefaultEndpointBindingSpaceNotDefault(c *gc.C) { 314 err := s.Model.UpdateModelConfig(map[string]interface{}{"default-space": s.clientSpace.Name()}, nil) 315 c.Assert(err, jc.ErrorIsNil) 316 id, err := s.State.DefaultEndpointBindingSpace() 317 c.Assert(err, jc.ErrorIsNil) 318 c.Assert(id, gc.Equals, s.clientSpace.Id()) 319 } 320 321 func (s *bindingsSuite) TestDefaultEndpointBindingSpaceDefault(c *gc.C) { 322 id, err := s.State.DefaultEndpointBindingSpace() 323 c.Assert(err, jc.ErrorIsNil) 324 c.Assert(id, gc.Equals, network.AlphaSpaceId) 325 } 326 327 func (s *bindingsSuite) copyMap(input map[string]string) map[string]string { 328 output := make(map[string]string, len(input)) 329 for key, value := range input { 330 output[key] = value 331 } 332 return output 333 } 334 335 var _ = gc.Suite(&bindingsMockSuite{}) 336 337 type bindingsMockSuite struct { 338 testing.IsolationSuite 339 340 endpointBinding *mocks.MockEndpointBinding 341 } 342 343 func (s *bindingsMockSuite) TestNewBindingsNilMap(c *gc.C) { 344 defer s.setup(c).Finish() 345 s.expectAllSpaceInfos(s.expectedSpaceInfos()) 346 347 binding, err := state.NewBindings(s.endpointBinding, nil) 348 c.Assert(err, jc.ErrorIsNil) 349 c.Assert(binding, gc.NotNil) 350 c.Assert(binding.Map(), gc.DeepEquals, map[string]string{}) 351 } 352 353 func (s *bindingsMockSuite) TestNewBindingsByID(c *gc.C) { 354 defer s.setup(c).Finish() 355 s.expectAllSpaceInfos(s.expectedSpaceInfos()) 356 357 initial := map[string]string{ 358 "db": "2", 359 "testing": "5", 360 "empty": network.AlphaSpaceId, 361 } 362 363 binding, err := state.NewBindings(s.endpointBinding, initial) 364 c.Assert(err, jc.ErrorIsNil) 365 c.Assert(binding, gc.NotNil) 366 367 c.Assert(binding.Map(), jc.DeepEquals, initial) 368 } 369 370 func (s *bindingsMockSuite) TestNewBindingsByName(c *gc.C) { 371 defer s.setup(c).Finish() 372 s.expectAllSpaceInfos(s.expectedSpaceInfos()) 373 374 initial := map[string]string{ 375 "db": "two", 376 "testing": "42", 377 "empty": network.AlphaSpaceName, 378 } 379 380 binding, err := state.NewBindings(s.endpointBinding, initial) 381 c.Assert(err, jc.ErrorIsNil) 382 c.Assert(binding, gc.NotNil) 383 384 expected := map[string]string{ 385 "db": "2", 386 "testing": "5", 387 "empty": network.AlphaSpaceId, 388 } 389 c.Logf("%+v", binding.Map()) 390 c.Assert(binding.Map(), jc.DeepEquals, expected) 391 } 392 393 func (s *bindingsMockSuite) TestNewBindingsNotFound(c *gc.C) { 394 defer s.setup(c).Finish() 395 s.expectAllSpaceInfos(s.expectedSpaceInfos()) 396 initial := map[string]string{ 397 "db": "2", 398 "testing": "three", 399 "empty": network.AlphaSpaceId, 400 } 401 402 binding, err := state.NewBindings(s.endpointBinding, initial) 403 c.Assert(err, jc.Satisfies, errors.IsNotFound) 404 c.Assert(binding, gc.IsNil) 405 } 406 407 func (s *bindingsMockSuite) TestMapWithSpaceNames(c *gc.C) { 408 defer s.setup(c).Finish() 409 410 infos := s.expectedSpaceInfos() 411 s.expectAllSpaceInfos(infos) 412 413 initial := map[string]string{ 414 "db": "2", 415 "testing": "3", 416 "empty": network.AlphaSpaceId, 417 } 418 419 binding, err := state.NewBindings(s.endpointBinding, initial) 420 c.Assert(err, jc.ErrorIsNil) 421 c.Assert(binding, gc.NotNil) 422 withSpaceNames, err := binding.MapWithSpaceNames(infos) 423 c.Assert(err, jc.ErrorIsNil) 424 425 expected := map[string]string{ 426 "db": "two", 427 "testing": "three", 428 "empty": network.AlphaSpaceName, 429 } 430 c.Assert(withSpaceNames, jc.DeepEquals, expected) 431 } 432 433 func (s *bindingsMockSuite) TestMapWithSpaceNamesWithNoLookup(c *gc.C) { 434 defer s.setup(c).Finish() 435 436 s.expectAllSpaceInfos(s.expectedSpaceInfos()) 437 438 initial := map[string]string{ 439 "db": "2", 440 "testing": "3", 441 "empty": network.AlphaSpaceId, 442 } 443 444 binding, err := state.NewBindings(s.endpointBinding, initial) 445 c.Assert(err, jc.ErrorIsNil) 446 c.Assert(binding, gc.NotNil) 447 _, err = binding.MapWithSpaceNames(nil) 448 c.Assert(err, gc.ErrorMatches, "*not valid*") 449 } 450 451 func (s *bindingsMockSuite) TestMapWithSpaceNamesWithNoBindings(c *gc.C) { 452 defer s.setup(c).Finish() 453 454 s.expectAllSpaceInfos(s.expectedSpaceInfos()) 455 456 initial := map[string]string{} 457 458 binding, err := state.NewBindings(s.endpointBinding, initial) 459 c.Assert(err, jc.ErrorIsNil) 460 c.Assert(binding, gc.NotNil) 461 withSpaceNames, err := binding.MapWithSpaceNames(make(network.SpaceInfos, 0)) 462 c.Assert(err, jc.ErrorIsNil) 463 c.Assert(withSpaceNames, gc.HasLen, 0) 464 } 465 466 func (s *bindingsMockSuite) TestMapWithSpaceNamesWithEmptyBindings(c *gc.C) { 467 defer s.setup(c).Finish() 468 469 s.expectAllSpaceInfos(s.expectedSpaceInfos()) 470 471 initial := map[string]string{ 472 "db": "2", 473 "testing": "3", 474 "empty": network.AlphaSpaceId, 475 } 476 477 binding, err := state.NewBindings(s.endpointBinding, initial) 478 c.Assert(err, jc.ErrorIsNil) 479 c.Assert(binding, gc.NotNil) 480 _, err = binding.MapWithSpaceNames(make(network.SpaceInfos, 0)) 481 c.Assert(err, gc.ErrorMatches, "*not valid*") 482 } 483 484 func (s *bindingsMockSuite) expectedSpaceInfos() network.SpaceInfos { 485 return network.SpaceInfos{ 486 {ID: network.AlphaSpaceId, Name: network.AlphaSpaceName}, 487 {ID: "1", Name: "one"}, 488 {ID: "2", Name: "two"}, 489 {ID: "3", Name: "three"}, 490 {ID: "4", Name: "four"}, 491 {ID: "5", Name: "42"}, 492 } 493 } 494 495 func (s *bindingsMockSuite) expectAllSpaceInfos(infos network.SpaceInfos) { 496 s.endpointBinding.EXPECT().AllSpaceInfos().Return(infos, nil) 497 } 498 499 func (s *bindingsMockSuite) setup(c *gc.C) *gomock.Controller { 500 ctrl := gomock.NewController(c) 501 s.endpointBinding = mocks.NewMockEndpointBinding(ctrl) 502 return ctrl 503 }