github.com/noironetworks/cilium-net@v1.6.12/pkg/k8s/service_cache_test.go (about) 1 // Copyright 2018-2019 Authors of Cilium 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // +build !privileged_tests 16 17 package k8s 18 19 import ( 20 "net" 21 "time" 22 23 "github.com/cilium/cilium/pkg/checker" 24 "github.com/cilium/cilium/pkg/k8s/types" 25 "github.com/cilium/cilium/pkg/loadbalancer" 26 "github.com/cilium/cilium/pkg/option" 27 "github.com/cilium/cilium/pkg/service" 28 "github.com/cilium/cilium/pkg/testutils" 29 30 "gopkg.in/check.v1" 31 "k8s.io/api/core/v1" 32 "k8s.io/api/extensions/v1beta1" 33 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 34 "k8s.io/apimachinery/pkg/util/intstr" 35 ) 36 37 func (s *K8sSuite) TestGetUniqueServiceFrontends(c *check.C) { 38 svcID1 := ServiceID{Name: "svc1", Namespace: "default"} 39 svcID2 := ServiceID{Name: "svc2", Namespace: "default"} 40 41 endpoints := Endpoints{ 42 Backends: map[string]service.PortConfiguration{ 43 "3.3.3.3": map[string]*loadbalancer.L4Addr{ 44 "port": { 45 Protocol: loadbalancer.TCP, 46 Port: 80, 47 }, 48 }, 49 }, 50 } 51 52 cache := NewServiceCache() 53 cache.services = map[ServiceID]*Service{ 54 svcID1: { 55 FrontendIP: net.ParseIP("1.1.1.1"), 56 Ports: map[loadbalancer.FEPortName]*loadbalancer.FEPort{ 57 loadbalancer.FEPortName("foo"): { 58 L4Addr: &loadbalancer.L4Addr{ 59 Protocol: loadbalancer.TCP, 60 Port: 10, 61 }, 62 }, 63 loadbalancer.FEPortName("bar"): { 64 L4Addr: &loadbalancer.L4Addr{ 65 Protocol: loadbalancer.TCP, 66 Port: 20, 67 }, 68 }, 69 }, 70 }, 71 svcID2: { 72 FrontendIP: net.ParseIP("2.2.2.2"), 73 Ports: map[loadbalancer.FEPortName]*loadbalancer.FEPort{ 74 loadbalancer.FEPortName("bar"): { 75 L4Addr: &loadbalancer.L4Addr{ 76 Protocol: loadbalancer.UDP, 77 Port: 20, 78 }, 79 }, 80 }, 81 }, 82 } 83 cache.endpoints = map[ServiceID]*Endpoints{ 84 svcID1: &endpoints, 85 svcID2: &endpoints, 86 } 87 88 frontends := cache.UniqueServiceFrontends() 89 c.Assert(frontends, checker.DeepEquals, FrontendList{ 90 "1.1.1.1:10/TCP": {}, 91 "1.1.1.1:20/TCP": {}, 92 "2.2.2.2:20/UDP": {}, 93 }) 94 95 // Validate all frontends as exact matches 96 frontend := loadbalancer.NewL3n4Addr(loadbalancer.TCP, net.ParseIP("1.1.1.1"), 10) 97 c.Assert(frontends.LooseMatch(*frontend), check.Equals, true) 98 frontend = loadbalancer.NewL3n4Addr(loadbalancer.TCP, net.ParseIP("1.1.1.1"), 20) 99 c.Assert(frontends.LooseMatch(*frontend), check.Equals, true) 100 frontend = loadbalancer.NewL3n4Addr(loadbalancer.UDP, net.ParseIP("2.2.2.2"), 20) 101 c.Assert(frontends.LooseMatch(*frontend), check.Equals, true) 102 103 // Validate protocol mismatch on exact match 104 frontend = loadbalancer.NewL3n4Addr(loadbalancer.TCP, net.ParseIP("2.2.2.2"), 20) 105 c.Assert(frontends.LooseMatch(*frontend), check.Equals, false) 106 107 // Validate protocol wildcard matching 108 frontend = loadbalancer.NewL3n4Addr(loadbalancer.NONE, net.ParseIP("2.2.2.2"), 20) 109 c.Assert(frontends.LooseMatch(*frontend), check.Equals, true) 110 frontend = loadbalancer.NewL3n4Addr(loadbalancer.NONE, net.ParseIP("1.1.1.1"), 10) 111 c.Assert(frontends.LooseMatch(*frontend), check.Equals, true) 112 frontend = loadbalancer.NewL3n4Addr(loadbalancer.NONE, net.ParseIP("1.1.1.1"), 20) 113 c.Assert(frontends.LooseMatch(*frontend), check.Equals, true) 114 } 115 116 func (s *K8sSuite) TestServiceCache(c *check.C) { 117 svcCache := NewServiceCache() 118 119 k8sSvc := &types.Service{ 120 Service: &v1.Service{ 121 ObjectMeta: metav1.ObjectMeta{ 122 Name: "foo", 123 Namespace: "bar", 124 Labels: map[string]string{ 125 "foo": "bar", 126 }, 127 }, 128 Spec: v1.ServiceSpec{ 129 ClusterIP: "127.0.0.1", 130 Selector: map[string]string{ 131 "foo": "bar", 132 }, 133 Type: v1.ServiceTypeClusterIP, 134 }, 135 }, 136 } 137 138 svcID := svcCache.UpdateService(k8sSvc) 139 140 time.Sleep(100 * time.Millisecond) 141 142 select { 143 case <-svcCache.Events: 144 c.Error("Unexpected service event received before endpoints have been imported") 145 default: 146 } 147 148 k8sEndpoints := &types.Endpoints{ 149 Endpoints: &v1.Endpoints{ 150 ObjectMeta: metav1.ObjectMeta{ 151 Name: "foo", 152 Namespace: "bar", 153 }, 154 Subsets: []v1.EndpointSubset{ 155 { 156 Addresses: []v1.EndpointAddress{{IP: "2.2.2.2"}}, 157 Ports: []v1.EndpointPort{ 158 { 159 Name: "http-test-svc", 160 Port: 8080, 161 Protocol: v1.ProtocolTCP, 162 }, 163 }, 164 }, 165 }, 166 }, 167 } 168 169 svcCache.UpdateEndpoints(k8sEndpoints) 170 171 // The service should be ready as both service and endpoints have been 172 // imported 173 c.Assert(testutils.WaitUntil(func() bool { 174 event := <-svcCache.Events 175 c.Assert(event.Action, check.Equals, UpdateService) 176 c.Assert(event.ID, check.Equals, svcID) 177 return true 178 }, 2*time.Second), check.IsNil) 179 180 endpoints, ready := svcCache.correlateEndpoints(svcID) 181 c.Assert(ready, check.Equals, true) 182 c.Assert(endpoints.String(), check.Equals, "2.2.2.2:8080/TCP") 183 184 // Updating the service without chaning it should not result in an event 185 svcCache.UpdateService(k8sSvc) 186 time.Sleep(100 * time.Millisecond) 187 select { 188 case <-svcCache.Events: 189 c.Error("Unexpected service event received for unchanged service object") 190 default: 191 } 192 193 // Deleting the service will result in a service delete event 194 svcCache.DeleteService(k8sSvc) 195 c.Assert(testutils.WaitUntil(func() bool { 196 event := <-svcCache.Events 197 c.Assert(event.Action, check.Equals, DeleteService) 198 c.Assert(event.ID, check.Equals, svcID) 199 return true 200 }, 2*time.Second), check.IsNil) 201 202 // Reinserting the service should re-match with the still existing endpoints 203 svcCache.UpdateService(k8sSvc) 204 c.Assert(testutils.WaitUntil(func() bool { 205 event := <-svcCache.Events 206 c.Assert(event.Action, check.Equals, UpdateService) 207 c.Assert(event.ID, check.Equals, svcID) 208 return true 209 }, 2*time.Second), check.IsNil) 210 211 // Deleting the endpoints will result in a service update event 212 svcCache.DeleteEndpoints(k8sEndpoints) 213 c.Assert(testutils.WaitUntil(func() bool { 214 event := <-svcCache.Events 215 c.Assert(event.Action, check.Equals, UpdateService) 216 c.Assert(event.ID, check.Equals, svcID) 217 return true 218 }, 2*time.Second), check.IsNil) 219 220 endpoints, serviceReady := svcCache.correlateEndpoints(svcID) 221 c.Assert(serviceReady, check.Equals, false) 222 c.Assert(endpoints.String(), check.Equals, "") 223 224 // Reinserting the endpoints should re-match with the still existing service 225 svcCache.UpdateEndpoints(k8sEndpoints) 226 c.Assert(testutils.WaitUntil(func() bool { 227 event := <-svcCache.Events 228 c.Assert(event.Action, check.Equals, UpdateService) 229 c.Assert(event.ID, check.Equals, svcID) 230 return true 231 }, 2*time.Second), check.IsNil) 232 233 endpoints, serviceReady = svcCache.correlateEndpoints(svcID) 234 c.Assert(serviceReady, check.Equals, true) 235 c.Assert(endpoints.String(), check.Equals, "2.2.2.2:8080/TCP") 236 237 // Deleting the service will result in a service delete event 238 svcCache.DeleteService(k8sSvc) 239 c.Assert(testutils.WaitUntil(func() bool { 240 event := <-svcCache.Events 241 c.Assert(event.Action, check.Equals, DeleteService) 242 c.Assert(event.ID, check.Equals, svcID) 243 return true 244 }, 2*time.Second), check.IsNil) 245 246 // Deleting the endpoints will not emit an event as the notification 247 // was sent out when the service was deleted. 248 svcCache.DeleteEndpoints(k8sEndpoints) 249 time.Sleep(100 * time.Millisecond) 250 select { 251 case <-svcCache.Events: 252 c.Error("Unexpected service delete event received") 253 default: 254 } 255 256 k8sIngress := &types.Ingress{ 257 Ingress: &v1beta1.Ingress{ 258 ObjectMeta: metav1.ObjectMeta{ 259 Namespace: "bar", 260 }, 261 Spec: v1beta1.IngressSpec{ 262 Backend: &v1beta1.IngressBackend{ 263 ServiceName: "svc1", 264 ServicePort: intstr.IntOrString{ 265 IntVal: 8080, 266 StrVal: "foo", 267 Type: intstr.Int, 268 }, 269 }, 270 }, 271 }, 272 } 273 ingressID, err := svcCache.UpdateIngress(k8sIngress, net.ParseIP("1.1.1.1")) 274 c.Assert(err, check.IsNil) 275 276 c.Assert(testutils.WaitUntil(func() bool { 277 event := <-svcCache.Events 278 c.Assert(event.Action, check.Equals, UpdateIngress) 279 c.Assert(event.ID, check.Equals, ingressID) 280 return true 281 }, 2*time.Second), check.IsNil) 282 283 // Updating the ingress without changes should not result in an event 284 _, err = svcCache.UpdateIngress(k8sIngress, net.ParseIP("1.1.1.1")) 285 c.Assert(err, check.IsNil) 286 time.Sleep(100 * time.Millisecond) 287 select { 288 case <-svcCache.Events: 289 c.Error("Unexpected ingress event received for unchanged ingress object") 290 default: 291 } 292 293 // Deleting the ingress resource will emit a delete event 294 svcCache.DeleteIngress(k8sIngress) 295 c.Assert(testutils.WaitUntil(func() bool { 296 event := <-svcCache.Events 297 c.Assert(event.Action, check.Equals, DeleteIngress) 298 c.Assert(event.ID, check.Equals, ingressID) 299 return true 300 }, 2*time.Second), check.IsNil) 301 } 302 303 func (s *K8sSuite) TestCacheActionString(c *check.C) { 304 c.Assert(UpdateService.String(), check.Equals, "service-updated") 305 c.Assert(DeleteService.String(), check.Equals, "service-deleted") 306 c.Assert(UpdateIngress.String(), check.Equals, "ingress-updated") 307 c.Assert(DeleteIngress.String(), check.Equals, "ingress-deleted") 308 } 309 310 func (s *K8sSuite) TestServiceMerging(c *check.C) { 311 svcCache := NewServiceCache() 312 313 k8sSvc := &types.Service{ 314 Service: &v1.Service{ 315 ObjectMeta: metav1.ObjectMeta{ 316 Name: "foo", 317 Namespace: "bar", 318 Annotations: map[string]string{ 319 "io.cilium/global-service": "true", 320 }, 321 }, 322 Spec: v1.ServiceSpec{ 323 ClusterIP: "127.0.0.1", 324 Type: v1.ServiceTypeClusterIP, 325 Ports: []v1.ServicePort{ 326 { 327 Name: "foo", 328 Protocol: v1.ProtocolTCP, 329 Port: 80, 330 }, 331 }, 332 }, 333 }, 334 } 335 336 svcID := svcCache.UpdateService(k8sSvc) 337 338 k8sEndpoints := &types.Endpoints{ 339 Endpoints: &v1.Endpoints{ 340 ObjectMeta: metav1.ObjectMeta{ 341 Name: "foo", 342 Namespace: "bar", 343 }, 344 Subsets: []v1.EndpointSubset{ 345 { 346 Addresses: []v1.EndpointAddress{{IP: "2.2.2.2"}}, 347 Ports: []v1.EndpointPort{ 348 { 349 Name: "http-test-svc", 350 Port: 8080, 351 Protocol: v1.ProtocolTCP, 352 }, 353 }, 354 }, 355 }, 356 }, 357 } 358 359 svcCache.UpdateEndpoints(k8sEndpoints) 360 361 // The service should be ready as both service and endpoints have been 362 // imported 363 c.Assert(testutils.WaitUntil(func() bool { 364 event := <-svcCache.Events 365 c.Assert(event.Action, check.Equals, UpdateService) 366 c.Assert(event.ID, check.Equals, svcID) 367 return true 368 }, 2*time.Second), check.IsNil) 369 370 // Merging a service update with own cluster name must not result in update 371 svcCache.MergeExternalServiceUpdate(&service.ClusterService{ 372 Cluster: option.Config.ClusterName, 373 Namespace: "bar", 374 Name: "foo", 375 Frontends: map[string]service.PortConfiguration{ 376 "1.1.1.1": {}, 377 }, 378 Backends: map[string]service.PortConfiguration{ 379 "3.3.3.3": map[string]*loadbalancer.L4Addr{ 380 "port": {Protocol: loadbalancer.TCP, Port: 80}, 381 }, 382 }, 383 }) 384 385 time.Sleep(100 * time.Millisecond) 386 387 select { 388 case <-svcCache.Events: 389 c.Error("Unexpected service event received") 390 default: 391 } 392 393 svcCache.MergeExternalServiceUpdate(&service.ClusterService{ 394 Cluster: "cluster1", 395 Namespace: "bar", 396 Name: "foo", 397 Frontends: map[string]service.PortConfiguration{ 398 "1.1.1.1": {}, 399 }, 400 Backends: map[string]service.PortConfiguration{ 401 "3.3.3.3": map[string]*loadbalancer.L4Addr{ 402 "port": {Protocol: loadbalancer.TCP, Port: 80}, 403 }, 404 }, 405 }) 406 407 // Adding remote endpoints will trigger a service update 408 c.Assert(testutils.WaitUntil(func() bool { 409 event := <-svcCache.Events 410 c.Assert(event.Action, check.Equals, UpdateService) 411 c.Assert(event.ID, check.Equals, svcID) 412 413 c.Assert(event.Endpoints.Backends["2.2.2.2"], checker.DeepEquals, service.PortConfiguration{ 414 "http-test-svc": {Protocol: loadbalancer.TCP, Port: 8080}, 415 }) 416 417 c.Assert(event.Endpoints.Backends["3.3.3.3"], checker.DeepEquals, service.PortConfiguration{ 418 "port": {Protocol: loadbalancer.TCP, Port: 80}, 419 }) 420 421 return true 422 }, 2*time.Second), check.IsNil) 423 424 // Merging a service for another name should not trigger any updates 425 svcCache.MergeExternalServiceUpdate(&service.ClusterService{ 426 Cluster: "cluster", 427 Namespace: "bar", 428 Name: "foo2", 429 Frontends: map[string]service.PortConfiguration{ 430 "1.1.1.1": {}, 431 }, 432 Backends: map[string]service.PortConfiguration{ 433 "3.3.3.3": map[string]*loadbalancer.L4Addr{ 434 "port": {Protocol: loadbalancer.TCP, Port: 80}, 435 }, 436 }, 437 }) 438 439 time.Sleep(100 * time.Millisecond) 440 441 select { 442 case <-svcCache.Events: 443 c.Error("Unexpected service event received") 444 default: 445 } 446 447 // Adding the service later must trigger an update 448 svcID2 := svcCache.UpdateService(&types.Service{ 449 Service: &v1.Service{ 450 ObjectMeta: metav1.ObjectMeta{ 451 Name: "foo2", 452 Namespace: "bar", 453 Labels: map[string]string{ 454 "foo": "bar", 455 }, 456 Annotations: map[string]string{ 457 "io.cilium/global-service": "true", 458 }, 459 }, 460 Spec: v1.ServiceSpec{ 461 ClusterIP: "127.0.0.2", 462 Selector: map[string]string{ 463 "foo": "bar", 464 }, 465 Type: v1.ServiceTypeClusterIP, 466 }, 467 }, 468 }) 469 470 c.Assert(testutils.WaitUntil(func() bool { 471 event := <-svcCache.Events 472 c.Assert(event.Action, check.Equals, UpdateService) 473 c.Assert(event.ID, check.Equals, svcID2) 474 return true 475 }, 2*time.Second), check.IsNil) 476 477 cluster2svc := &service.ClusterService{ 478 Cluster: "cluster2", 479 Namespace: "bar", 480 Name: "foo", 481 Frontends: map[string]service.PortConfiguration{ 482 "1.1.1.1": {}, 483 }, 484 Backends: map[string]service.PortConfiguration{ 485 "4.4.4.4": map[string]*loadbalancer.L4Addr{ 486 "port": {Protocol: loadbalancer.TCP, Port: 80}, 487 }, 488 }, 489 } 490 491 // Adding another cluster to the first service will triger an event 492 svcCache.MergeExternalServiceUpdate(cluster2svc) 493 c.Assert(testutils.WaitUntil(func() bool { 494 event := <-svcCache.Events 495 c.Assert(event.Action, check.Equals, UpdateService) 496 497 c.Assert(event.Endpoints.Backends["4.4.4.4"], checker.DeepEquals, service.PortConfiguration{ 498 "port": {Protocol: loadbalancer.TCP, Port: 80}, 499 }) 500 501 return true 502 }, 2*time.Second), check.IsNil) 503 504 svcCache.MergeExternalServiceDelete(cluster2svc) 505 c.Assert(testutils.WaitUntil(func() bool { 506 event := <-svcCache.Events 507 c.Assert(event.Action, check.Equals, UpdateService) 508 c.Assert(event.Endpoints.Backends["4.4.4.4"], check.IsNil) 509 return true 510 }, 2*time.Second), check.IsNil) 511 512 // Deletion of the service frontend will trigger the delete notification 513 svcCache.DeleteService(k8sSvc) 514 c.Assert(testutils.WaitUntil(func() bool { 515 event := <-svcCache.Events 516 c.Assert(event.Action, check.Equals, DeleteService) 517 c.Assert(event.ID, check.Equals, svcID) 518 return true 519 }, 2*time.Second), check.IsNil) 520 521 // When readding the service, the remote endpoints of cluster1 must still be present 522 svcCache.UpdateService(k8sSvc) 523 c.Assert(testutils.WaitUntil(func() bool { 524 event := <-svcCache.Events 525 c.Assert(event.Action, check.Equals, UpdateService) 526 c.Assert(event.ID, check.Equals, svcID) 527 c.Assert(event.Endpoints.Backends["3.3.3.3"], checker.DeepEquals, service.PortConfiguration{ 528 "port": {Protocol: loadbalancer.TCP, Port: 80}, 529 }) 530 return true 531 }, 2*time.Second), check.IsNil) 532 533 k8sSvcID, _ := ParseService(k8sSvc) 534 addresses := svcCache.GetServiceIP(k8sSvcID) 535 c.Assert(addresses, checker.DeepEquals, loadbalancer.NewL3n4Addr(loadbalancer.TCP, net.ParseIP("127.0.0.1"), 80)) 536 } 537 538 func (s *K8sSuite) TestNonSharedServie(c *check.C) { 539 svcCache := NewServiceCache() 540 541 k8sSvc := &types.Service{ 542 Service: &v1.Service{ 543 ObjectMeta: metav1.ObjectMeta{ 544 Name: "foo", 545 Namespace: "bar", 546 Annotations: map[string]string{ 547 "io.cilium/global-service": "false", 548 }, 549 }, 550 Spec: v1.ServiceSpec{ 551 ClusterIP: "127.0.0.1", 552 Type: v1.ServiceTypeClusterIP, 553 }, 554 }, 555 } 556 557 svcCache.UpdateService(k8sSvc) 558 559 svcCache.MergeExternalServiceUpdate(&service.ClusterService{ 560 Cluster: "cluster1", 561 Namespace: "bar", 562 Name: "foo", 563 Backends: map[string]service.PortConfiguration{ 564 "3.3.3.3": map[string]*loadbalancer.L4Addr{ 565 "port": {Protocol: loadbalancer.TCP, Port: 80}, 566 }, 567 }, 568 }) 569 570 // The service is unshared, it should not trigger an update 571 time.Sleep(100 * time.Millisecond) 572 573 select { 574 case <-svcCache.Events: 575 c.Error("Unexpected service event received") 576 default: 577 } 578 }