github.phpd.cn/cilium/cilium@v1.6.12/pkg/k8s/service_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 "reflect" 22 "testing" 23 24 "github.com/cilium/cilium/pkg/checker" 25 "github.com/cilium/cilium/pkg/k8s/types" 26 "github.com/cilium/cilium/pkg/loadbalancer" 27 "github.com/cilium/cilium/pkg/service" 28 29 "gopkg.in/check.v1" 30 "k8s.io/api/core/v1" 31 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 32 ) 33 34 func (s *K8sSuite) TestGetAnnotationIncludeExternal(c *check.C) { 35 svc := &types.Service{Service: &v1.Service{ObjectMeta: metav1.ObjectMeta{ 36 Name: "foo", 37 }}} 38 c.Assert(getAnnotationIncludeExternal(svc), check.Equals, false) 39 40 svc = &types.Service{Service: &v1.Service{ObjectMeta: metav1.ObjectMeta{ 41 Annotations: map[string]string{"io.cilium/global-service": "True"}, 42 }}} 43 c.Assert(getAnnotationIncludeExternal(svc), check.Equals, true) 44 45 svc = &types.Service{Service: &v1.Service{ObjectMeta: metav1.ObjectMeta{ 46 Annotations: map[string]string{"io.cilium/global-service": "false"}, 47 }}} 48 c.Assert(getAnnotationIncludeExternal(svc), check.Equals, false) 49 50 svc = &types.Service{Service: &v1.Service{ObjectMeta: metav1.ObjectMeta{ 51 Annotations: map[string]string{"io.cilium/global-service": ""}, 52 }}} 53 c.Assert(getAnnotationIncludeExternal(svc), check.Equals, false) 54 } 55 56 func (s *K8sSuite) TestGetAnnotationShared(c *check.C) { 57 svc := &types.Service{Service: &v1.Service{ObjectMeta: metav1.ObjectMeta{ 58 Name: "foo", 59 }}} 60 c.Assert(getAnnotationShared(svc), check.Equals, false) 61 svc = &types.Service{Service: &v1.Service{ObjectMeta: metav1.ObjectMeta{ 62 Annotations: map[string]string{"io.cilium/global-service": "true"}, 63 }}} 64 c.Assert(getAnnotationShared(svc), check.Equals, true) 65 66 svc = &types.Service{Service: &v1.Service{ObjectMeta: metav1.ObjectMeta{ 67 Annotations: map[string]string{"io.cilium/shared-service": "True"}, 68 }}} 69 c.Assert(getAnnotationShared(svc), check.Equals, true) 70 71 svc = &types.Service{Service: &v1.Service{ObjectMeta: metav1.ObjectMeta{ 72 Annotations: map[string]string{"io.cilium/global-service": "true", "io.cilium/shared-service": "false"}, 73 }}} 74 c.Assert(getAnnotationShared(svc), check.Equals, false) 75 } 76 77 func (s *K8sSuite) TestParseServiceID(c *check.C) { 78 svc := &types.Service{ 79 Service: &v1.Service{ 80 ObjectMeta: metav1.ObjectMeta{ 81 Name: "foo", 82 Namespace: "bar", 83 }, 84 }, 85 } 86 87 c.Assert(ParseServiceID(svc), checker.DeepEquals, ServiceID{Namespace: "bar", Name: "foo"}) 88 } 89 90 func (s *K8sSuite) TestParseService(c *check.C) { 91 k8sSvc := &types.Service{ 92 Service: &v1.Service{ 93 ObjectMeta: metav1.ObjectMeta{ 94 Name: "foo", 95 Namespace: "bar", 96 Labels: map[string]string{ 97 "foo": "bar", 98 }, 99 }, 100 Spec: v1.ServiceSpec{ 101 ClusterIP: "127.0.0.1", 102 Selector: map[string]string{ 103 "foo": "bar", 104 }, 105 Type: v1.ServiceTypeClusterIP, 106 }, 107 }, 108 } 109 110 id, svc := ParseService(k8sSvc) 111 c.Assert(id, checker.DeepEquals, ServiceID{Namespace: "bar", Name: "foo"}) 112 c.Assert(svc, checker.DeepEquals, &Service{ 113 FrontendIP: net.ParseIP("127.0.0.1"), 114 Selector: map[string]string{"foo": "bar"}, 115 Labels: map[string]string{"foo": "bar"}, 116 Ports: map[loadbalancer.FEPortName]*loadbalancer.FEPort{}, 117 NodePorts: map[loadbalancer.FEPortName]map[string]*loadbalancer.L3n4AddrID{}, 118 }) 119 120 k8sSvc = &types.Service{ 121 Service: &v1.Service{ 122 ObjectMeta: metav1.ObjectMeta{ 123 Name: "foo", 124 Namespace: "bar", 125 Labels: map[string]string{ 126 "foo": "bar", 127 }, 128 }, 129 Spec: v1.ServiceSpec{ 130 ClusterIP: "none", 131 Type: v1.ServiceTypeClusterIP, 132 }, 133 }, 134 } 135 136 id, svc = ParseService(k8sSvc) 137 c.Assert(id, checker.DeepEquals, ServiceID{Namespace: "bar", Name: "foo"}) 138 c.Assert(svc, checker.DeepEquals, &Service{ 139 IsHeadless: true, 140 Labels: map[string]string{"foo": "bar"}, 141 Ports: map[loadbalancer.FEPortName]*loadbalancer.FEPort{}, 142 NodePorts: map[loadbalancer.FEPortName]map[string]*loadbalancer.L3n4AddrID{}, 143 }) 144 } 145 146 func (s *K8sSuite) TestIsK8ServiceExternal(c *check.C) { 147 si := Service{} 148 149 c.Assert(si.IsExternal(), check.Equals, true) 150 151 si.Selector = map[string]string{"l": "v"} 152 c.Assert(si.IsExternal(), check.Equals, false) 153 } 154 155 func (s *K8sSuite) TestServiceUniquePorts(c *check.C) { 156 type testMatrix struct { 157 input Service 158 expected map[uint16]bool 159 } 160 161 matrix := []testMatrix{ 162 { 163 input: Service{}, 164 expected: map[uint16]bool{}, 165 }, 166 { 167 input: Service{ 168 Ports: map[loadbalancer.FEPortName]*loadbalancer.FEPort{ 169 loadbalancer.FEPortName("foo"): { 170 L4Addr: &loadbalancer.L4Addr{ 171 Protocol: loadbalancer.NONE, 172 Port: 1, 173 }, 174 }, 175 loadbalancer.FEPortName("bar"): { 176 L4Addr: &loadbalancer.L4Addr{ 177 Protocol: loadbalancer.NONE, 178 Port: 2, 179 }, 180 }, 181 }, 182 }, 183 expected: map[uint16]bool{ 184 1: true, 185 2: true, 186 }}, 187 } 188 189 for _, m := range matrix { 190 c.Assert(m.input.UniquePorts(), checker.DeepEquals, m.expected) 191 } 192 } 193 194 func TestService_Equals(t *testing.T) { 195 type args struct { 196 o *Service 197 } 198 tests := []struct { 199 name string 200 fields *Service 201 args args 202 want bool 203 }{ 204 { 205 name: "both equal", 206 fields: &Service{ 207 FrontendIP: net.ParseIP("1.1.1.1"), 208 IsHeadless: true, 209 Ports: map[loadbalancer.FEPortName]*loadbalancer.FEPort{ 210 loadbalancer.FEPortName("foo"): { 211 L4Addr: &loadbalancer.L4Addr{ 212 Protocol: loadbalancer.NONE, 213 Port: 1, 214 }, 215 ID: 1, 216 }, 217 }, 218 NodePorts: map[loadbalancer.FEPortName]map[string]*loadbalancer.L3n4AddrID{ 219 loadbalancer.FEPortName("foo"): { 220 "0.0.0.0:31000": { 221 L3n4Addr: loadbalancer.L3n4Addr{ 222 L4Addr: loadbalancer.L4Addr{ 223 Protocol: loadbalancer.NONE, 224 Port: 31000, 225 }, 226 IP: net.IPv4(0, 0, 0, 0), 227 }, 228 ID: 1, 229 }, 230 }, 231 }, 232 233 Labels: map[string]string{ 234 "foo": "bar", 235 }, 236 Selector: map[string]string{ 237 "baz": "foz", 238 }, 239 }, 240 args: args{ 241 o: &Service{ 242 FrontendIP: net.ParseIP("1.1.1.1"), 243 IsHeadless: true, 244 Ports: map[loadbalancer.FEPortName]*loadbalancer.FEPort{ 245 loadbalancer.FEPortName("foo"): { 246 L4Addr: &loadbalancer.L4Addr{ 247 Protocol: loadbalancer.NONE, 248 Port: 1, 249 }, 250 ID: 1, 251 }, 252 }, 253 NodePorts: map[loadbalancer.FEPortName]map[string]*loadbalancer.L3n4AddrID{ 254 loadbalancer.FEPortName("foo"): { 255 "0.0.0.0:31000": { 256 L3n4Addr: loadbalancer.L3n4Addr{ 257 L4Addr: loadbalancer.L4Addr{ 258 Protocol: loadbalancer.NONE, 259 Port: 31000, 260 }, 261 IP: net.IPv4(0, 0, 0, 0), 262 }, 263 ID: 1, 264 }, 265 }, 266 }, 267 268 Labels: map[string]string{ 269 "foo": "bar", 270 }, 271 Selector: map[string]string{ 272 "baz": "foz", 273 }, 274 }, 275 }, 276 want: true, 277 }, 278 { 279 name: "different labels", 280 fields: &Service{ 281 FrontendIP: net.ParseIP("1.1.1.1"), 282 IsHeadless: true, 283 Ports: map[loadbalancer.FEPortName]*loadbalancer.FEPort{ 284 loadbalancer.FEPortName("foo"): { 285 L4Addr: &loadbalancer.L4Addr{ 286 Protocol: loadbalancer.NONE, 287 Port: 1, 288 }, 289 ID: 1, 290 }, 291 }, 292 Labels: map[string]string{}, 293 Selector: map[string]string{ 294 "baz": "foz", 295 }, 296 }, 297 args: args{ 298 o: &Service{ 299 FrontendIP: net.ParseIP("1.1.1.1"), 300 IsHeadless: true, 301 Ports: map[loadbalancer.FEPortName]*loadbalancer.FEPort{ 302 loadbalancer.FEPortName("foo"): { 303 L4Addr: &loadbalancer.L4Addr{ 304 Protocol: loadbalancer.NONE, 305 Port: 1, 306 }, 307 ID: 1, 308 }, 309 }, 310 Labels: map[string]string{ 311 "foo": "bar", 312 }, 313 Selector: map[string]string{ 314 "baz": "foz", 315 }, 316 }, 317 }, 318 want: false, 319 }, 320 { 321 name: "different selector", 322 fields: &Service{ 323 FrontendIP: net.ParseIP("1.1.1.1"), 324 IsHeadless: true, 325 Ports: map[loadbalancer.FEPortName]*loadbalancer.FEPort{ 326 loadbalancer.FEPortName("foo"): { 327 L4Addr: &loadbalancer.L4Addr{ 328 Protocol: loadbalancer.NONE, 329 Port: 1, 330 }, 331 ID: 1, 332 }, 333 }, 334 Labels: map[string]string{}, 335 Selector: map[string]string{}, 336 }, 337 args: args{ 338 o: &Service{ 339 FrontendIP: net.ParseIP("1.1.1.1"), 340 IsHeadless: true, 341 Ports: map[loadbalancer.FEPortName]*loadbalancer.FEPort{ 342 loadbalancer.FEPortName("foo"): { 343 L4Addr: &loadbalancer.L4Addr{ 344 Protocol: loadbalancer.NONE, 345 Port: 1, 346 }, 347 ID: 1, 348 }, 349 }, 350 Labels: map[string]string{}, 351 Selector: map[string]string{ 352 "baz": "foz", 353 }, 354 }, 355 }, 356 want: false, 357 }, 358 { 359 name: "ports different name", 360 fields: &Service{ 361 FrontendIP: net.ParseIP("1.1.1.1"), 362 IsHeadless: true, 363 Ports: map[loadbalancer.FEPortName]*loadbalancer.FEPort{ 364 loadbalancer.FEPortName("foz"): { 365 L4Addr: &loadbalancer.L4Addr{ 366 Protocol: loadbalancer.NONE, 367 Port: 1, 368 }, 369 ID: 1, 370 }, 371 }, 372 Labels: map[string]string{}, 373 Selector: map[string]string{}, 374 }, 375 args: args{ 376 o: &Service{ 377 FrontendIP: net.ParseIP("1.1.1.1"), 378 IsHeadless: true, 379 Ports: map[loadbalancer.FEPortName]*loadbalancer.FEPort{ 380 loadbalancer.FEPortName("foo"): { 381 L4Addr: &loadbalancer.L4Addr{ 382 Protocol: loadbalancer.NONE, 383 Port: 1, 384 }, 385 ID: 1, 386 }, 387 }, 388 Labels: map[string]string{}, 389 Selector: map[string]string{}, 390 }, 391 }, 392 want: false, 393 }, 394 { 395 name: "ports different content", 396 fields: &Service{ 397 FrontendIP: net.ParseIP("1.1.1.1"), 398 IsHeadless: true, 399 Ports: map[loadbalancer.FEPortName]*loadbalancer.FEPort{ 400 loadbalancer.FEPortName("foo"): { 401 L4Addr: &loadbalancer.L4Addr{ 402 Protocol: loadbalancer.NONE, 403 Port: 1, 404 }, 405 ID: 1, 406 }, 407 }, 408 Labels: map[string]string{}, 409 Selector: map[string]string{}, 410 }, 411 args: args{ 412 o: &Service{ 413 FrontendIP: net.ParseIP("1.1.1.1"), 414 IsHeadless: true, 415 Ports: map[loadbalancer.FEPortName]*loadbalancer.FEPort{ 416 loadbalancer.FEPortName("foo"): { 417 L4Addr: &loadbalancer.L4Addr{ 418 Protocol: loadbalancer.NONE, 419 Port: 2, 420 }, 421 ID: 1, 422 }, 423 }, 424 Labels: map[string]string{}, 425 Selector: map[string]string{}, 426 }, 427 }, 428 want: false, 429 }, 430 { 431 name: "ports different one is bigger", 432 fields: &Service{ 433 FrontendIP: net.ParseIP("1.1.1.1"), 434 IsHeadless: true, 435 Ports: map[loadbalancer.FEPortName]*loadbalancer.FEPort{ 436 loadbalancer.FEPortName("foo"): { 437 L4Addr: &loadbalancer.L4Addr{ 438 Protocol: loadbalancer.NONE, 439 Port: 1, 440 }, 441 ID: 1, 442 }, 443 }, 444 Labels: map[string]string{}, 445 Selector: map[string]string{}, 446 }, 447 args: args{ 448 o: &Service{ 449 FrontendIP: net.ParseIP("1.1.1.1"), 450 IsHeadless: true, 451 Ports: map[loadbalancer.FEPortName]*loadbalancer.FEPort{ 452 loadbalancer.FEPortName("foo"): { 453 L4Addr: &loadbalancer.L4Addr{ 454 Protocol: loadbalancer.NONE, 455 Port: 1, 456 }, 457 ID: 1, 458 }, 459 loadbalancer.FEPortName("baz"): { 460 L4Addr: &loadbalancer.L4Addr{ 461 Protocol: loadbalancer.NONE, 462 Port: 2, 463 }, 464 ID: 2, 465 }, 466 }, 467 Labels: map[string]string{}, 468 Selector: map[string]string{}, 469 }, 470 }, 471 want: false, 472 }, 473 { 474 name: "ports different one is nil", 475 fields: &Service{ 476 FrontendIP: net.ParseIP("1.1.1.1"), 477 IsHeadless: true, 478 Labels: map[string]string{}, 479 Selector: map[string]string{}, 480 }, 481 args: args{ 482 o: &Service{ 483 FrontendIP: net.ParseIP("1.1.1.1"), 484 IsHeadless: true, 485 Ports: map[loadbalancer.FEPortName]*loadbalancer.FEPort{ 486 loadbalancer.FEPortName("foo"): { 487 L4Addr: &loadbalancer.L4Addr{ 488 Protocol: loadbalancer.NONE, 489 Port: 1, 490 }, 491 ID: 1, 492 }, 493 }, 494 Labels: map[string]string{}, 495 Selector: map[string]string{}, 496 }, 497 }, 498 want: false, 499 }, 500 { 501 name: "nodeports different", 502 fields: &Service{ 503 FrontendIP: net.ParseIP("1.1.1.1"), 504 IsHeadless: true, 505 Ports: map[loadbalancer.FEPortName]*loadbalancer.FEPort{ 506 loadbalancer.FEPortName("foo"): { 507 L4Addr: &loadbalancer.L4Addr{ 508 Protocol: loadbalancer.NONE, 509 Port: 1, 510 }, 511 ID: 1, 512 }, 513 }, 514 NodePorts: map[loadbalancer.FEPortName]map[string]*loadbalancer.L3n4AddrID{ 515 loadbalancer.FEPortName("foo"): { 516 "1.1.1.1:31000": { 517 L3n4Addr: loadbalancer.L3n4Addr{ 518 L4Addr: loadbalancer.L4Addr{ 519 Protocol: loadbalancer.NONE, 520 Port: 31000, 521 }, 522 IP: net.IPv4(1, 1, 1, 1), 523 }, 524 ID: 1, 525 }, 526 }, 527 }, 528 529 Labels: map[string]string{ 530 "foo": "bar", 531 }, 532 Selector: map[string]string{ 533 "baz": "foz", 534 }, 535 }, 536 args: args{ 537 o: &Service{ 538 FrontendIP: net.ParseIP("1.1.1.1"), 539 IsHeadless: true, 540 Ports: map[loadbalancer.FEPortName]*loadbalancer.FEPort{ 541 loadbalancer.FEPortName("foo"): { 542 L4Addr: &loadbalancer.L4Addr{ 543 Protocol: loadbalancer.NONE, 544 Port: 1, 545 }, 546 ID: 1, 547 }, 548 }, 549 NodePorts: map[loadbalancer.FEPortName]map[string]*loadbalancer.L3n4AddrID{ 550 loadbalancer.FEPortName("foo"): { 551 "0.0.0.0:31000": { 552 L3n4Addr: loadbalancer.L3n4Addr{ 553 L4Addr: loadbalancer.L4Addr{ 554 Protocol: loadbalancer.NONE, 555 Port: 31000, 556 }, 557 IP: net.IPv4(0, 0, 0, 0), 558 }, 559 ID: 1, 560 }, 561 }, 562 }, 563 564 Labels: map[string]string{ 565 "foo": "bar", 566 }, 567 Selector: map[string]string{ 568 "baz": "foz", 569 }, 570 }, 571 }, 572 want: false, 573 }, 574 { 575 name: "both nil", 576 args: args{}, 577 want: true, 578 }, 579 } 580 for _, tt := range tests { 581 t.Run(tt.name, func(t *testing.T) { 582 si := tt.fields 583 if got := si.DeepEquals(tt.args.o); got != tt.want { 584 t.Errorf("Service.Equals() = %v, want %v", got, tt.want) 585 } 586 }) 587 } 588 } 589 590 func (s *K8sSuite) TestServiceString(c *check.C) { 591 k8sSvc := &types.Service{ 592 Service: &v1.Service{ 593 ObjectMeta: metav1.ObjectMeta{ 594 Name: "foo", 595 Namespace: "bar", 596 Labels: map[string]string{ 597 "foo": "bar", 598 }, 599 }, 600 Spec: v1.ServiceSpec{ 601 ClusterIP: "127.0.0.1", 602 Selector: map[string]string{ 603 "foo": "bar", 604 }, 605 Type: v1.ServiceTypeClusterIP, 606 }, 607 }, 608 } 609 610 _, svc := ParseService(k8sSvc) 611 c.Assert(svc.String(), check.Equals, "frontend:127.0.0.1/ports=[]/selector=map[foo:bar]") 612 } 613 614 func (s *K8sSuite) TestNewClusterService(c *check.C) { 615 id, svc := ParseService(&types.Service{ 616 Service: &v1.Service{ 617 ObjectMeta: metav1.ObjectMeta{ 618 Name: "foo", 619 Namespace: "bar", 620 Labels: map[string]string{ 621 "foo": "bar", 622 }, 623 }, 624 Spec: v1.ServiceSpec{ 625 ClusterIP: "127.0.0.1", 626 Selector: map[string]string{ 627 "foo": "bar", 628 }, 629 Type: v1.ServiceTypeClusterIP, 630 }, 631 }, 632 }) 633 634 _, endpoints := ParseEndpoints(&types.Endpoints{ 635 Endpoints: &v1.Endpoints{ 636 ObjectMeta: metav1.ObjectMeta{ 637 Name: "foo", 638 Namespace: "bar", 639 }, 640 Subsets: []v1.EndpointSubset{ 641 { 642 Addresses: []v1.EndpointAddress{{IP: "2.2.2.2"}}, 643 Ports: []v1.EndpointPort{ 644 { 645 Name: "http-test-svc", 646 Port: 8080, 647 Protocol: v1.ProtocolTCP, 648 }, 649 }, 650 }, 651 }, 652 }, 653 }) 654 655 clusterService := NewClusterService(id, svc, endpoints) 656 c.Assert(clusterService, check.DeepEquals, service.ClusterService{ 657 Name: "foo", 658 Namespace: "bar", 659 Labels: map[string]string{"foo": "bar"}, 660 Selector: map[string]string{"foo": "bar"}, 661 Frontends: map[string]service.PortConfiguration{ 662 "127.0.0.1": {}, 663 }, 664 Backends: map[string]service.PortConfiguration{ 665 "2.2.2.2": { 666 "http-test-svc": {Protocol: loadbalancer.TCP, Port: 8080}, 667 }, 668 }, 669 }) 670 } 671 672 func TestParseServiceIDFrom(t *testing.T) { 673 type args struct { 674 dn string 675 } 676 tests := []struct { 677 args args 678 want *ServiceID 679 }{ 680 {args: args{dn: "cilium-etcd-client.kube-system.svc"}, want: &ServiceID{Name: "cilium-etcd-client", Namespace: "kube-system"}}, 681 {args: args{dn: "1.kube-system"}, want: &ServiceID{Name: "1", Namespace: "kube-system"}}, 682 {args: args{dn: ".kube-system"}, want: &ServiceID{Name: "", Namespace: "kube-system"}}, 683 {args: args{dn: "..kube-system"}, want: &ServiceID{Name: "", Namespace: ""}}, 684 {args: args{dn: "2-..kube-system"}, want: &ServiceID{Name: "2-", Namespace: ""}}, 685 {args: args{dn: ""}, want: nil}, 686 {args: args{dn: "cilium-etcd-client.kube-system"}, want: &ServiceID{Name: "cilium-etcd-client", Namespace: "kube-system"}}, 687 {args: args{dn: "cilium-etcd-client"}, want: nil}, 688 } 689 for _, tt := range tests { 690 t.Run(tt.args.dn, func(t *testing.T) { 691 if got := ParseServiceIDFrom(tt.args.dn); !reflect.DeepEqual(got, tt.want) { 692 t.Errorf("ParseServiceIDFrom() = %v, want %v", got, tt.want) 693 } 694 }) 695 } 696 }