sigs.k8s.io/external-dns@v0.14.1/source/pod_test.go (about) 1 /* 2 Copyright 2021 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package source 18 19 import ( 20 "context" 21 "testing" 22 23 "github.com/stretchr/testify/require" 24 corev1 "k8s.io/api/core/v1" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/client-go/kubernetes/fake" 27 "sigs.k8s.io/external-dns/endpoint" 28 ) 29 30 // testPodSource tests that various services generate the correct endpoints. 31 func TestPodSource(t *testing.T) { 32 t.Parallel() 33 34 for _, tc := range []struct { 35 title string 36 targetNamespace string 37 compatibility string 38 expected []*endpoint.Endpoint 39 expectError bool 40 nodes []*corev1.Node 41 pods []*corev1.Pod 42 }{ 43 { 44 "create IPv4 records based on pod's external and internal IPs", 45 "", 46 "", 47 []*endpoint.Endpoint{ 48 {DNSName: "a.foo.example.org", Targets: endpoint.Targets{"54.10.11.1", "54.10.11.2"}, RecordType: endpoint.RecordTypeA}, 49 {DNSName: "internal.a.foo.example.org", Targets: endpoint.Targets{"10.0.1.1", "10.0.1.2"}, RecordType: endpoint.RecordTypeA}, 50 }, 51 false, 52 []*corev1.Node{ 53 { 54 ObjectMeta: metav1.ObjectMeta{ 55 Name: "my-node1", 56 }, 57 Status: corev1.NodeStatus{ 58 Addresses: []corev1.NodeAddress{ 59 {Type: corev1.NodeExternalIP, Address: "54.10.11.1"}, 60 {Type: corev1.NodeInternalIP, Address: "10.0.1.1"}, 61 }, 62 }, 63 }, 64 { 65 ObjectMeta: metav1.ObjectMeta{ 66 Name: "my-node2", 67 }, 68 Status: corev1.NodeStatus{ 69 Addresses: []corev1.NodeAddress{ 70 {Type: corev1.NodeExternalIP, Address: "54.10.11.2"}, 71 {Type: corev1.NodeInternalIP, Address: "10.0.1.2"}, 72 }, 73 }, 74 }, 75 }, 76 []*corev1.Pod{ 77 { 78 ObjectMeta: metav1.ObjectMeta{ 79 Name: "my-pod1", 80 Namespace: "kube-system", 81 Annotations: map[string]string{ 82 internalHostnameAnnotationKey: "internal.a.foo.example.org", 83 hostnameAnnotationKey: "a.foo.example.org", 84 }, 85 }, 86 Spec: corev1.PodSpec{ 87 HostNetwork: true, 88 NodeName: "my-node1", 89 }, 90 Status: corev1.PodStatus{ 91 PodIP: "10.0.1.1", 92 }, 93 }, 94 { 95 ObjectMeta: metav1.ObjectMeta{ 96 Name: "my-pod2", 97 Namespace: "kube-system", 98 Annotations: map[string]string{ 99 internalHostnameAnnotationKey: "internal.a.foo.example.org", 100 hostnameAnnotationKey: "a.foo.example.org", 101 }, 102 }, 103 Spec: corev1.PodSpec{ 104 HostNetwork: true, 105 NodeName: "my-node2", 106 }, 107 Status: corev1.PodStatus{ 108 PodIP: "10.0.1.2", 109 }, 110 }, 111 }, 112 }, 113 { 114 "create IPv4 records based on pod's external and internal IPs using DNS Controller annotations", 115 "", 116 "kops-dns-controller", 117 []*endpoint.Endpoint{ 118 {DNSName: "a.foo.example.org", Targets: endpoint.Targets{"54.10.11.1", "54.10.11.2"}, RecordType: endpoint.RecordTypeA}, 119 {DNSName: "internal.a.foo.example.org", Targets: endpoint.Targets{"10.0.1.1", "10.0.1.2"}, RecordType: endpoint.RecordTypeA}, 120 }, 121 false, 122 []*corev1.Node{ 123 { 124 ObjectMeta: metav1.ObjectMeta{ 125 Name: "my-node1", 126 }, 127 Status: corev1.NodeStatus{ 128 Addresses: []corev1.NodeAddress{ 129 {Type: corev1.NodeExternalIP, Address: "54.10.11.1"}, 130 {Type: corev1.NodeInternalIP, Address: "10.0.1.1"}, 131 }, 132 }, 133 }, 134 { 135 ObjectMeta: metav1.ObjectMeta{ 136 Name: "my-node2", 137 }, 138 Status: corev1.NodeStatus{ 139 Addresses: []corev1.NodeAddress{ 140 {Type: corev1.NodeExternalIP, Address: "54.10.11.2"}, 141 {Type: corev1.NodeInternalIP, Address: "10.0.1.2"}, 142 }, 143 }, 144 }, 145 }, 146 []*corev1.Pod{ 147 { 148 ObjectMeta: metav1.ObjectMeta{ 149 Name: "my-pod1", 150 Namespace: "kube-system", 151 Annotations: map[string]string{ 152 kopsDNSControllerInternalHostnameAnnotationKey: "internal.a.foo.example.org", 153 kopsDNSControllerHostnameAnnotationKey: "a.foo.example.org", 154 }, 155 }, 156 Spec: corev1.PodSpec{ 157 HostNetwork: true, 158 NodeName: "my-node1", 159 }, 160 Status: corev1.PodStatus{ 161 PodIP: "10.0.1.1", 162 }, 163 }, 164 { 165 ObjectMeta: metav1.ObjectMeta{ 166 Name: "my-pod2", 167 Namespace: "kube-system", 168 Annotations: map[string]string{ 169 kopsDNSControllerInternalHostnameAnnotationKey: "internal.a.foo.example.org", 170 kopsDNSControllerHostnameAnnotationKey: "a.foo.example.org", 171 }, 172 }, 173 Spec: corev1.PodSpec{ 174 HostNetwork: true, 175 NodeName: "my-node2", 176 }, 177 Status: corev1.PodStatus{ 178 PodIP: "10.0.1.2", 179 }, 180 }, 181 }, 182 }, 183 { 184 "create IPv6 records based on pod's external and internal IPs", 185 "", 186 "", 187 []*endpoint.Endpoint{ 188 {DNSName: "a.foo.example.org", Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}, RecordType: endpoint.RecordTypeAAAA}, 189 {DNSName: "internal.a.foo.example.org", Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}, RecordType: endpoint.RecordTypeAAAA}, 190 }, 191 false, 192 []*corev1.Node{ 193 { 194 ObjectMeta: metav1.ObjectMeta{ 195 Name: "my-node1", 196 }, 197 Status: corev1.NodeStatus{ 198 Addresses: []corev1.NodeAddress{ 199 {Type: corev1.NodeInternalIP, Address: "2001:DB8::1"}, 200 }, 201 }, 202 }, 203 { 204 ObjectMeta: metav1.ObjectMeta{ 205 Name: "my-node2", 206 }, 207 Status: corev1.NodeStatus{ 208 Addresses: []corev1.NodeAddress{ 209 {Type: corev1.NodeInternalIP, Address: "2001:DB8::2"}, 210 }, 211 }, 212 }, 213 }, 214 []*corev1.Pod{ 215 { 216 ObjectMeta: metav1.ObjectMeta{ 217 Name: "my-pod1", 218 Namespace: "kube-system", 219 Annotations: map[string]string{ 220 internalHostnameAnnotationKey: "internal.a.foo.example.org", 221 hostnameAnnotationKey: "a.foo.example.org", 222 }, 223 }, 224 Spec: corev1.PodSpec{ 225 HostNetwork: true, 226 NodeName: "my-node1", 227 }, 228 Status: corev1.PodStatus{ 229 PodIP: "2001:DB8::1", 230 }, 231 }, 232 { 233 ObjectMeta: metav1.ObjectMeta{ 234 Name: "my-pod2", 235 Namespace: "kube-system", 236 Annotations: map[string]string{ 237 internalHostnameAnnotationKey: "internal.a.foo.example.org", 238 hostnameAnnotationKey: "a.foo.example.org", 239 }, 240 }, 241 Spec: corev1.PodSpec{ 242 HostNetwork: true, 243 NodeName: "my-node2", 244 }, 245 Status: corev1.PodStatus{ 246 PodIP: "2001:DB8::2", 247 }, 248 }, 249 }, 250 }, 251 { 252 "create IPv6 records based on pod's external and internal IPs using DNS Controller annotations", 253 "", 254 "kops-dns-controller", 255 []*endpoint.Endpoint{ 256 {DNSName: "a.foo.example.org", Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}, RecordType: endpoint.RecordTypeAAAA}, 257 {DNSName: "internal.a.foo.example.org", Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}, RecordType: endpoint.RecordTypeAAAA}, 258 }, 259 false, 260 []*corev1.Node{ 261 { 262 ObjectMeta: metav1.ObjectMeta{ 263 Name: "my-node1", 264 }, 265 Status: corev1.NodeStatus{ 266 Addresses: []corev1.NodeAddress{ 267 {Type: corev1.NodeInternalIP, Address: "2001:DB8::1"}, 268 }, 269 }, 270 }, 271 { 272 ObjectMeta: metav1.ObjectMeta{ 273 Name: "my-node2", 274 }, 275 Status: corev1.NodeStatus{ 276 Addresses: []corev1.NodeAddress{ 277 {Type: corev1.NodeInternalIP, Address: "2001:DB8::2"}, 278 }, 279 }, 280 }, 281 }, 282 []*corev1.Pod{ 283 { 284 ObjectMeta: metav1.ObjectMeta{ 285 Name: "my-pod1", 286 Namespace: "kube-system", 287 Annotations: map[string]string{ 288 kopsDNSControllerInternalHostnameAnnotationKey: "internal.a.foo.example.org", 289 kopsDNSControllerHostnameAnnotationKey: "a.foo.example.org", 290 }, 291 }, 292 Spec: corev1.PodSpec{ 293 HostNetwork: true, 294 NodeName: "my-node1", 295 }, 296 Status: corev1.PodStatus{ 297 PodIP: "2001:DB8::1", 298 }, 299 }, 300 { 301 ObjectMeta: metav1.ObjectMeta{ 302 Name: "my-pod2", 303 Namespace: "kube-system", 304 Annotations: map[string]string{ 305 kopsDNSControllerInternalHostnameAnnotationKey: "internal.a.foo.example.org", 306 kopsDNSControllerHostnameAnnotationKey: "a.foo.example.org", 307 }, 308 }, 309 Spec: corev1.PodSpec{ 310 HostNetwork: true, 311 NodeName: "my-node2", 312 }, 313 Status: corev1.PodStatus{ 314 PodIP: "2001:DB8::2", 315 }, 316 }, 317 }, 318 }, 319 { 320 "create records based on pod's target annotation", 321 "", 322 "", 323 []*endpoint.Endpoint{ 324 {DNSName: "a.foo.example.org", Targets: endpoint.Targets{"208.1.2.1", "208.1.2.2"}, RecordType: endpoint.RecordTypeA}, 325 {DNSName: "internal.a.foo.example.org", Targets: endpoint.Targets{"208.1.2.1", "208.1.2.2"}, RecordType: endpoint.RecordTypeA}, 326 }, 327 false, 328 []*corev1.Node{ 329 { 330 ObjectMeta: metav1.ObjectMeta{ 331 Name: "my-node1", 332 }, 333 Status: corev1.NodeStatus{ 334 Addresses: []corev1.NodeAddress{ 335 {Type: corev1.NodeExternalIP, Address: "54.10.11.1"}, 336 {Type: corev1.NodeInternalIP, Address: "10.0.1.1"}, 337 }, 338 }, 339 }, 340 { 341 ObjectMeta: metav1.ObjectMeta{ 342 Name: "my-node2", 343 }, 344 Status: corev1.NodeStatus{ 345 Addresses: []corev1.NodeAddress{ 346 {Type: corev1.NodeExternalIP, Address: "54.10.11.2"}, 347 {Type: corev1.NodeInternalIP, Address: "10.0.1.2"}, 348 }, 349 }, 350 }, 351 }, 352 []*corev1.Pod{ 353 { 354 ObjectMeta: metav1.ObjectMeta{ 355 Name: "my-pod1", 356 Namespace: "kube-system", 357 Annotations: map[string]string{ 358 internalHostnameAnnotationKey: "internal.a.foo.example.org", 359 hostnameAnnotationKey: "a.foo.example.org", 360 targetAnnotationKey: "208.1.2.1", 361 }, 362 }, 363 Spec: corev1.PodSpec{ 364 HostNetwork: true, 365 NodeName: "my-node1", 366 }, 367 Status: corev1.PodStatus{ 368 PodIP: "10.0.1.1", 369 }, 370 }, 371 { 372 ObjectMeta: metav1.ObjectMeta{ 373 Name: "my-pod2", 374 Namespace: "kube-system", 375 Annotations: map[string]string{ 376 internalHostnameAnnotationKey: "internal.a.foo.example.org", 377 hostnameAnnotationKey: "a.foo.example.org", 378 targetAnnotationKey: "208.1.2.2", 379 }, 380 }, 381 Spec: corev1.PodSpec{ 382 HostNetwork: true, 383 NodeName: "my-node2", 384 }, 385 Status: corev1.PodStatus{ 386 PodIP: "10.0.1.2", 387 }, 388 }, 389 }, 390 }, 391 { 392 "create multiple records", 393 "", 394 "", 395 []*endpoint.Endpoint{ 396 {DNSName: "a.foo.example.org", Targets: endpoint.Targets{"54.10.11.1"}, RecordType: endpoint.RecordTypeA}, 397 {DNSName: "a.foo.example.org", Targets: endpoint.Targets{"2001:DB8::1"}, RecordType: endpoint.RecordTypeAAAA}, 398 {DNSName: "b.foo.example.org", Targets: endpoint.Targets{"54.10.11.2"}, RecordType: endpoint.RecordTypeA}, 399 }, 400 false, 401 []*corev1.Node{ 402 { 403 ObjectMeta: metav1.ObjectMeta{ 404 Name: "my-node1", 405 }, 406 Status: corev1.NodeStatus{ 407 Addresses: []corev1.NodeAddress{ 408 {Type: corev1.NodeExternalIP, Address: "54.10.11.1"}, 409 {Type: corev1.NodeInternalIP, Address: "2001:DB8::1"}, 410 {Type: corev1.NodeInternalIP, Address: "10.0.1.1"}, 411 }, 412 }, 413 }, 414 { 415 ObjectMeta: metav1.ObjectMeta{ 416 Name: "my-node2", 417 }, 418 Status: corev1.NodeStatus{ 419 Addresses: []corev1.NodeAddress{ 420 {Type: corev1.NodeExternalIP, Address: "54.10.11.2"}, 421 {Type: corev1.NodeInternalIP, Address: "10.0.1.2"}, 422 }, 423 }, 424 }, 425 }, 426 []*corev1.Pod{ 427 { 428 ObjectMeta: metav1.ObjectMeta{ 429 Name: "my-pod1", 430 Namespace: "kube-system", 431 Annotations: map[string]string{ 432 hostnameAnnotationKey: "a.foo.example.org", 433 }, 434 }, 435 Spec: corev1.PodSpec{ 436 HostNetwork: true, 437 NodeName: "my-node1", 438 }, 439 Status: corev1.PodStatus{ 440 PodIP: "10.0.1.1", 441 }, 442 }, 443 { 444 ObjectMeta: metav1.ObjectMeta{ 445 Name: "my-pod2", 446 Namespace: "kube-system", 447 Annotations: map[string]string{ 448 hostnameAnnotationKey: "b.foo.example.org", 449 }, 450 }, 451 Spec: corev1.PodSpec{ 452 HostNetwork: true, 453 NodeName: "my-node2", 454 }, 455 Status: corev1.PodStatus{ 456 PodIP: "10.0.1.2", 457 }, 458 }, 459 }, 460 }, 461 { 462 "pods with hostNetwore=false should be ignored", 463 "", 464 "", 465 []*endpoint.Endpoint{ 466 {DNSName: "a.foo.example.org", Targets: endpoint.Targets{"54.10.11.1"}, RecordType: endpoint.RecordTypeA}, 467 {DNSName: "internal.a.foo.example.org", Targets: endpoint.Targets{"10.0.1.1"}, RecordType: endpoint.RecordTypeA}, 468 }, 469 false, 470 []*corev1.Node{ 471 { 472 ObjectMeta: metav1.ObjectMeta{ 473 Name: "my-node1", 474 }, 475 Status: corev1.NodeStatus{ 476 Addresses: []corev1.NodeAddress{ 477 {Type: corev1.NodeExternalIP, Address: "54.10.11.1"}, 478 {Type: corev1.NodeInternalIP, Address: "10.0.1.1"}, 479 }, 480 }, 481 }, 482 { 483 ObjectMeta: metav1.ObjectMeta{ 484 Name: "my-node2", 485 }, 486 Status: corev1.NodeStatus{ 487 Addresses: []corev1.NodeAddress{ 488 {Type: corev1.NodeExternalIP, Address: "54.10.11.2"}, 489 {Type: corev1.NodeInternalIP, Address: "10.0.1.2"}, 490 }, 491 }, 492 }, 493 }, 494 []*corev1.Pod{ 495 { 496 ObjectMeta: metav1.ObjectMeta{ 497 Name: "my-pod1", 498 Namespace: "kube-system", 499 Annotations: map[string]string{ 500 internalHostnameAnnotationKey: "internal.a.foo.example.org", 501 hostnameAnnotationKey: "a.foo.example.org", 502 }, 503 }, 504 Spec: corev1.PodSpec{ 505 HostNetwork: true, 506 NodeName: "my-node1", 507 }, 508 Status: corev1.PodStatus{ 509 PodIP: "10.0.1.1", 510 }, 511 }, 512 { 513 ObjectMeta: metav1.ObjectMeta{ 514 Name: "my-pod2", 515 Namespace: "kube-system", 516 Annotations: map[string]string{ 517 internalHostnameAnnotationKey: "internal.a.foo.example.org", 518 hostnameAnnotationKey: "a.foo.example.org", 519 }, 520 }, 521 Spec: corev1.PodSpec{ 522 HostNetwork: false, 523 NodeName: "my-node2", 524 }, 525 Status: corev1.PodStatus{ 526 PodIP: "100.0.1.2", 527 }, 528 }, 529 }, 530 }, 531 { 532 "only watch a given namespace", 533 "kube-system", 534 "", 535 []*endpoint.Endpoint{ 536 {DNSName: "a.foo.example.org", Targets: endpoint.Targets{"54.10.11.1"}, RecordType: endpoint.RecordTypeA}, 537 {DNSName: "internal.a.foo.example.org", Targets: endpoint.Targets{"10.0.1.1"}, RecordType: endpoint.RecordTypeA}, 538 }, 539 false, 540 []*corev1.Node{ 541 { 542 ObjectMeta: metav1.ObjectMeta{ 543 Name: "my-node1", 544 }, 545 Status: corev1.NodeStatus{ 546 Addresses: []corev1.NodeAddress{ 547 {Type: corev1.NodeExternalIP, Address: "54.10.11.1"}, 548 {Type: corev1.NodeInternalIP, Address: "10.0.1.1"}, 549 }, 550 }, 551 }, 552 { 553 ObjectMeta: metav1.ObjectMeta{ 554 Name: "my-node2", 555 }, 556 Status: corev1.NodeStatus{ 557 Addresses: []corev1.NodeAddress{ 558 {Type: corev1.NodeExternalIP, Address: "54.10.11.2"}, 559 {Type: corev1.NodeInternalIP, Address: "10.0.1.2"}, 560 }, 561 }, 562 }, 563 }, 564 []*corev1.Pod{ 565 { 566 ObjectMeta: metav1.ObjectMeta{ 567 Name: "my-pod1", 568 Namespace: "kube-system", 569 Annotations: map[string]string{ 570 internalHostnameAnnotationKey: "internal.a.foo.example.org", 571 hostnameAnnotationKey: "a.foo.example.org", 572 }, 573 }, 574 Spec: corev1.PodSpec{ 575 HostNetwork: true, 576 NodeName: "my-node1", 577 }, 578 Status: corev1.PodStatus{ 579 PodIP: "10.0.1.1", 580 }, 581 }, 582 { 583 ObjectMeta: metav1.ObjectMeta{ 584 Name: "my-pod2", 585 Namespace: "default", 586 Annotations: map[string]string{ 587 internalHostnameAnnotationKey: "internal.a.foo.example.org", 588 hostnameAnnotationKey: "a.foo.example.org", 589 }, 590 }, 591 Spec: corev1.PodSpec{ 592 HostNetwork: true, 593 NodeName: "my-node2", 594 }, 595 Status: corev1.PodStatus{ 596 PodIP: "100.0.1.2", 597 }, 598 }, 599 }, 600 }, 601 { 602 "split record for internal hostname annotation", 603 "", 604 "", 605 []*endpoint.Endpoint{ 606 {DNSName: "internal.a.foo.example.org", Targets: endpoint.Targets{"10.0.1.1"}, RecordType: endpoint.RecordTypeA}, 607 {DNSName: "internal.b.foo.example.org", Targets: endpoint.Targets{"10.0.1.1"}, RecordType: endpoint.RecordTypeA}, 608 }, 609 false, 610 []*corev1.Node{ 611 { 612 ObjectMeta: metav1.ObjectMeta{ 613 Name: "my-node1", 614 }, 615 Status: corev1.NodeStatus{ 616 Addresses: []corev1.NodeAddress{ 617 {Type: corev1.NodeInternalIP, Address: "10.0.1.1"}, 618 }, 619 }, 620 }, 621 }, 622 []*corev1.Pod{ 623 { 624 ObjectMeta: metav1.ObjectMeta{ 625 Name: "my-pod1", 626 Namespace: "kube-system", 627 Annotations: map[string]string{ 628 internalHostnameAnnotationKey: "internal.a.foo.example.org,internal.b.foo.example.org", 629 }, 630 }, 631 Spec: corev1.PodSpec{ 632 HostNetwork: true, 633 NodeName: "my-node1", 634 }, 635 Status: corev1.PodStatus{ 636 PodIP: "10.0.1.1", 637 }, 638 }, 639 }, 640 }, 641 } { 642 tc := tc 643 t.Run(tc.title, func(t *testing.T) { 644 t.Parallel() 645 646 // Create a Kubernetes testing client 647 kubernetes := fake.NewSimpleClientset() 648 ctx := context.Background() 649 650 // Create the nodes 651 for _, node := range tc.nodes { 652 if _, err := kubernetes.CoreV1().Nodes().Create(ctx, node, metav1.CreateOptions{}); err != nil { 653 t.Fatal(err) 654 } 655 } 656 657 for _, pod := range tc.pods { 658 pods := kubernetes.CoreV1().Pods(pod.Namespace) 659 660 if _, err := pods.Create(ctx, pod, metav1.CreateOptions{}); err != nil { 661 t.Fatal(err) 662 } 663 } 664 665 client, err := NewPodSource(context.TODO(), kubernetes, tc.targetNamespace, tc.compatibility) 666 require.NoError(t, err) 667 668 endpoints, err := client.Endpoints(ctx) 669 if tc.expectError { 670 require.Error(t, err) 671 } else { 672 require.NoError(t, err) 673 } 674 675 // Validate returned endpoints against desired endpoints. 676 validateEndpoints(t, endpoints, tc.expected) 677 }) 678 679 } 680 }