sigs.k8s.io/external-dns@v0.14.1/source/traefik_proxy.go (about) 1 /* 2 Copyright 2022 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 "fmt" 22 "regexp" 23 "sort" 24 "strings" 25 26 "github.com/pkg/errors" 27 log "github.com/sirupsen/logrus" 28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 30 "k8s.io/apimachinery/pkg/labels" 31 "k8s.io/apimachinery/pkg/runtime" 32 "k8s.io/apimachinery/pkg/runtime/schema" 33 "k8s.io/client-go/dynamic" 34 "k8s.io/client-go/dynamic/dynamicinformer" 35 "k8s.io/client-go/informers" 36 "k8s.io/client-go/kubernetes" 37 "k8s.io/client-go/kubernetes/scheme" 38 "k8s.io/client-go/tools/cache" 39 40 "sigs.k8s.io/external-dns/endpoint" 41 ) 42 43 var ( 44 ingressrouteGVR = schema.GroupVersionResource{ 45 Group: "traefik.io", 46 Version: "v1alpha1", 47 Resource: "ingressroutes", 48 } 49 ingressrouteTCPGVR = schema.GroupVersionResource{ 50 Group: "traefik.io", 51 Version: "v1alpha1", 52 Resource: "ingressroutetcps", 53 } 54 ingressrouteUDPGVR = schema.GroupVersionResource{ 55 Group: "traefik.io", 56 Version: "v1alpha1", 57 Resource: "ingressrouteudps", 58 } 59 oldIngressrouteGVR = schema.GroupVersionResource{ 60 Group: "traefik.containo.us", 61 Version: "v1alpha1", 62 Resource: "ingressroutes", 63 } 64 oldIngressrouteTCPGVR = schema.GroupVersionResource{ 65 Group: "traefik.containo.us", 66 Version: "v1alpha1", 67 Resource: "ingressroutetcps", 68 } 69 oldIngressrouteUDPGVR = schema.GroupVersionResource{ 70 Group: "traefik.containo.us", 71 Version: "v1alpha1", 72 Resource: "ingressrouteudps", 73 } 74 ) 75 76 var ( 77 traefikHostExtractor = regexp.MustCompile(`(?:HostSNI|HostHeader|Host)\s*\(\s*(\x60.*?\x60)\s*\)`) 78 traefikValueProcessor = regexp.MustCompile(`\x60([^,\x60]+)\x60`) 79 ) 80 81 type traefikSource struct { 82 annotationFilter string 83 ignoreHostnameAnnotation bool 84 dynamicKubeClient dynamic.Interface 85 ingressRouteInformer informers.GenericInformer 86 ingressRouteTcpInformer informers.GenericInformer 87 ingressRouteUdpInformer informers.GenericInformer 88 oldIngressRouteInformer informers.GenericInformer 89 oldIngressRouteTcpInformer informers.GenericInformer 90 oldIngressRouteUdpInformer informers.GenericInformer 91 kubeClient kubernetes.Interface 92 namespace string 93 unstructuredConverter *unstructuredConverter 94 } 95 96 func NewTraefikSource(ctx context.Context, dynamicKubeClient dynamic.Interface, kubeClient kubernetes.Interface, namespace string, annotationFilter string, ignoreHostnameAnnotation bool, disableLegacy bool, disableNew bool) (Source, error) { 97 // Use shared informer to listen for add/update/delete of Host in the specified namespace. 98 // Set resync period to 0, to prevent processing when nothing has changed. 99 informerFactory := dynamicinformer.NewFilteredDynamicSharedInformerFactory(dynamicKubeClient, 0, namespace, nil) 100 var ingressRouteInformer, ingressRouteTcpInformer, ingressRouteUdpInformer informers.GenericInformer 101 var oldIngressRouteInformer, oldIngressRouteTcpInformer, oldIngressRouteUdpInformer informers.GenericInformer 102 103 // Add default resource event handlers to properly initialize informers. 104 if !disableNew { 105 ingressRouteInformer = informerFactory.ForResource(ingressrouteGVR) 106 ingressRouteTcpInformer = informerFactory.ForResource(ingressrouteTCPGVR) 107 ingressRouteUdpInformer = informerFactory.ForResource(ingressrouteUDPGVR) 108 ingressRouteInformer.Informer().AddEventHandler( 109 cache.ResourceEventHandlerFuncs{ 110 AddFunc: func(obj interface{}) {}, 111 }, 112 ) 113 ingressRouteTcpInformer.Informer().AddEventHandler( 114 cache.ResourceEventHandlerFuncs{ 115 AddFunc: func(obj interface{}) {}, 116 }, 117 ) 118 ingressRouteUdpInformer.Informer().AddEventHandler( 119 cache.ResourceEventHandlerFuncs{ 120 AddFunc: func(obj interface{}) {}, 121 }, 122 ) 123 } 124 if !disableLegacy { 125 oldIngressRouteInformer = informerFactory.ForResource(oldIngressrouteGVR) 126 oldIngressRouteTcpInformer = informerFactory.ForResource(oldIngressrouteTCPGVR) 127 oldIngressRouteUdpInformer = informerFactory.ForResource(oldIngressrouteUDPGVR) 128 oldIngressRouteInformer.Informer().AddEventHandler( 129 cache.ResourceEventHandlerFuncs{ 130 AddFunc: func(obj interface{}) {}, 131 }, 132 ) 133 oldIngressRouteTcpInformer.Informer().AddEventHandler( 134 cache.ResourceEventHandlerFuncs{ 135 AddFunc: func(obj interface{}) {}, 136 }, 137 ) 138 oldIngressRouteUdpInformer.Informer().AddEventHandler( 139 cache.ResourceEventHandlerFuncs{ 140 AddFunc: func(obj interface{}) {}, 141 }, 142 ) 143 } 144 145 informerFactory.Start((ctx.Done())) 146 147 // wait for the local cache to be populated. 148 if err := waitForDynamicCacheSync(context.Background(), informerFactory); err != nil { 149 return nil, err 150 } 151 152 uc, err := newTraefikUnstructuredConverter() 153 if err != nil { 154 return nil, errors.Wrapf(err, "failed to setup Unstructured Converter") 155 } 156 157 return &traefikSource{ 158 annotationFilter: annotationFilter, 159 ignoreHostnameAnnotation: ignoreHostnameAnnotation, 160 dynamicKubeClient: dynamicKubeClient, 161 ingressRouteInformer: ingressRouteInformer, 162 ingressRouteTcpInformer: ingressRouteTcpInformer, 163 ingressRouteUdpInformer: ingressRouteUdpInformer, 164 oldIngressRouteInformer: oldIngressRouteInformer, 165 oldIngressRouteTcpInformer: oldIngressRouteTcpInformer, 166 oldIngressRouteUdpInformer: oldIngressRouteUdpInformer, 167 kubeClient: kubeClient, 168 namespace: namespace, 169 unstructuredConverter: uc, 170 }, nil 171 } 172 173 func (ts *traefikSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) { 174 var endpoints []*endpoint.Endpoint 175 176 if ts.ingressRouteInformer != nil { 177 ingressRouteEndpoints, err := ts.ingressRouteEndpoints() 178 if err != nil { 179 return nil, err 180 } 181 endpoints = append(endpoints, ingressRouteEndpoints...) 182 } 183 if ts.oldIngressRouteInformer != nil { 184 oldIngressRouteEndpoints, err := ts.oldIngressRouteEndpoints() 185 if err != nil { 186 return nil, err 187 } 188 endpoints = append(endpoints, oldIngressRouteEndpoints...) 189 } 190 if ts.ingressRouteTcpInformer != nil { 191 ingressRouteTcpEndpoints, err := ts.ingressRouteTCPEndpoints() 192 if err != nil { 193 return nil, err 194 } 195 endpoints = append(endpoints, ingressRouteTcpEndpoints...) 196 } 197 if ts.oldIngressRouteTcpInformer != nil { 198 oldIngressRouteTcpEndpoints, err := ts.oldIngressRouteTCPEndpoints() 199 if err != nil { 200 return nil, err 201 } 202 endpoints = append(endpoints, oldIngressRouteTcpEndpoints...) 203 } 204 if ts.ingressRouteUdpInformer != nil { 205 ingressRouteUdpEndpoints, err := ts.ingressRouteUDPEndpoints() 206 if err != nil { 207 return nil, err 208 } 209 endpoints = append(endpoints, ingressRouteUdpEndpoints...) 210 } 211 if ts.oldIngressRouteUdpInformer != nil { 212 oldIngressRouteUdpEndpoints, err := ts.oldIngressRouteUDPEndpoints() 213 if err != nil { 214 return nil, err 215 } 216 endpoints = append(endpoints, oldIngressRouteUdpEndpoints...) 217 } 218 219 for _, ep := range endpoints { 220 sort.Sort(ep.Targets) 221 } 222 223 return endpoints, nil 224 } 225 226 // ingressRouteEndpoints extracts endpoints from all IngressRoute objects 227 func (ts *traefikSource) ingressRouteEndpoints() ([]*endpoint.Endpoint, error) { 228 var endpoints []*endpoint.Endpoint 229 230 irs, err := ts.ingressRouteInformer.Lister().ByNamespace(ts.namespace).List(labels.Everything()) 231 if err != nil { 232 return nil, err 233 } 234 235 var ingressRoutes []*IngressRoute 236 for _, ingressRouteObj := range irs { 237 unstructuredHost, ok := ingressRouteObj.(*unstructured.Unstructured) 238 if !ok { 239 return nil, errors.New("could not convert IngressRoute object to unstructured") 240 } 241 242 ingressRoute := &IngressRoute{} 243 err := ts.unstructuredConverter.scheme.Convert(unstructuredHost, ingressRoute, nil) 244 if err != nil { 245 return nil, err 246 } 247 ingressRoutes = append(ingressRoutes, ingressRoute) 248 } 249 250 ingressRoutes, err = ts.filterIngressRouteByAnnotation(ingressRoutes) 251 if err != nil { 252 return nil, errors.Wrap(err, "failed to filter IngressRoute") 253 } 254 255 for _, ingressRoute := range ingressRoutes { 256 var targets endpoint.Targets 257 258 targets = append(targets, getTargetsFromTargetAnnotation(ingressRoute.Annotations)...) 259 260 fullname := fmt.Sprintf("%s/%s", ingressRoute.Namespace, ingressRoute.Name) 261 262 ingressEndpoints, err := ts.endpointsFromIngressRoute(ingressRoute, targets) 263 if err != nil { 264 return nil, err 265 } 266 if len(ingressEndpoints) == 0 { 267 log.Debugf("No endpoints could be generated from Host %s", fullname) 268 continue 269 } 270 271 log.Debugf("Endpoints generated from IngressRoute: %s: %v", fullname, ingressEndpoints) 272 ts.setDualstackLabelIngressRoute(ingressRoute, ingressEndpoints) 273 endpoints = append(endpoints, ingressEndpoints...) 274 } 275 276 return endpoints, nil 277 } 278 279 // ingressRouteTCPEndpoints extracts endpoints from all IngressRouteTCP objects 280 func (ts *traefikSource) ingressRouteTCPEndpoints() ([]*endpoint.Endpoint, error) { 281 var endpoints []*endpoint.Endpoint 282 283 irs, err := ts.ingressRouteTcpInformer.Lister().ByNamespace(ts.namespace).List(labels.Everything()) 284 if err != nil { 285 return nil, err 286 } 287 288 var ingressRouteTCPs []*IngressRouteTCP 289 for _, ingressRouteTCPObj := range irs { 290 unstructuredHost, ok := ingressRouteTCPObj.(*unstructured.Unstructured) 291 if !ok { 292 return nil, errors.New("could not convert IngressRouteTCP object to unstructured") 293 } 294 295 ingressRouteTCP := &IngressRouteTCP{} 296 err := ts.unstructuredConverter.scheme.Convert(unstructuredHost, ingressRouteTCP, nil) 297 if err != nil { 298 return nil, err 299 } 300 ingressRouteTCPs = append(ingressRouteTCPs, ingressRouteTCP) 301 } 302 303 ingressRouteTCPs, err = ts.filterIngressRouteTcpByAnnotations(ingressRouteTCPs) 304 if err != nil { 305 return nil, errors.Wrap(err, "failed to filter IngressRouteTCP") 306 } 307 308 for _, ingressRouteTCP := range ingressRouteTCPs { 309 var targets endpoint.Targets 310 311 targets = append(targets, getTargetsFromTargetAnnotation(ingressRouteTCP.Annotations)...) 312 313 fullname := fmt.Sprintf("%s/%s", ingressRouteTCP.Namespace, ingressRouteTCP.Name) 314 315 ingressEndpoints, err := ts.endpointsFromIngressRouteTCP(ingressRouteTCP, targets) 316 if err != nil { 317 return nil, err 318 } 319 if len(ingressEndpoints) == 0 { 320 log.Debugf("No endpoints could be generated from Host %s", fullname) 321 continue 322 } 323 324 log.Debugf("Endpoints generated from IngressRouteTCP: %s: %v", fullname, ingressEndpoints) 325 ts.setDualstackLabelIngressRouteTCP(ingressRouteTCP, ingressEndpoints) 326 endpoints = append(endpoints, ingressEndpoints...) 327 } 328 329 return endpoints, nil 330 } 331 332 // ingressRouteUDPEndpoints extracts endpoints from all IngressRouteUDP objects 333 func (ts *traefikSource) ingressRouteUDPEndpoints() ([]*endpoint.Endpoint, error) { 334 var endpoints []*endpoint.Endpoint 335 336 irs, err := ts.ingressRouteUdpInformer.Lister().ByNamespace(ts.namespace).List(labels.Everything()) 337 if err != nil { 338 return nil, err 339 } 340 341 var ingressRouteUDPs []*IngressRouteUDP 342 for _, ingressRouteUDPObj := range irs { 343 unstructuredHost, ok := ingressRouteUDPObj.(*unstructured.Unstructured) 344 if !ok { 345 return nil, errors.New("could not convert IngressRouteUDP object to unstructured") 346 } 347 348 ingressRoute := &IngressRouteUDP{} 349 err := ts.unstructuredConverter.scheme.Convert(unstructuredHost, ingressRoute, nil) 350 if err != nil { 351 return nil, err 352 } 353 ingressRouteUDPs = append(ingressRouteUDPs, ingressRoute) 354 } 355 356 ingressRouteUDPs, err = ts.filterIngressRouteUdpByAnnotations(ingressRouteUDPs) 357 if err != nil { 358 return nil, errors.Wrap(err, "failed to filter IngressRouteUDP") 359 } 360 361 for _, ingressRouteUDP := range ingressRouteUDPs { 362 var targets endpoint.Targets 363 364 targets = append(targets, getTargetsFromTargetAnnotation(ingressRouteUDP.Annotations)...) 365 366 fullname := fmt.Sprintf("%s/%s", ingressRouteUDP.Namespace, ingressRouteUDP.Name) 367 368 ingressEndpoints, err := ts.endpointsFromIngressRouteUDP(ingressRouteUDP, targets) 369 if err != nil { 370 return nil, err 371 } 372 if len(ingressEndpoints) == 0 { 373 log.Debugf("No endpoints could be generated from Host %s", fullname) 374 continue 375 } 376 377 log.Debugf("Endpoints generated from IngressRouteUDP: %s: %v", fullname, ingressEndpoints) 378 ts.setDualstackLabelIngressRouteUDP(ingressRouteUDP, ingressEndpoints) 379 endpoints = append(endpoints, ingressEndpoints...) 380 } 381 382 return endpoints, nil 383 } 384 385 // oldIngressRouteEndpoints extracts endpoints from all IngressRoute objects 386 func (ts *traefikSource) oldIngressRouteEndpoints() ([]*endpoint.Endpoint, error) { 387 var endpoints []*endpoint.Endpoint 388 389 irs, err := ts.oldIngressRouteInformer.Lister().ByNamespace(ts.namespace).List(labels.Everything()) 390 if err != nil { 391 return nil, err 392 } 393 394 var ingressRoutes []*IngressRoute 395 for _, ingressRouteObj := range irs { 396 unstructuredHost, ok := ingressRouteObj.(*unstructured.Unstructured) 397 if !ok { 398 return nil, errors.New("could not convert IngressRoute object to unstructured") 399 } 400 401 ingressRoute := &IngressRoute{} 402 err := ts.unstructuredConverter.scheme.Convert(unstructuredHost, ingressRoute, nil) 403 if err != nil { 404 return nil, err 405 } 406 ingressRoutes = append(ingressRoutes, ingressRoute) 407 } 408 409 ingressRoutes, err = ts.filterIngressRouteByAnnotation(ingressRoutes) 410 if err != nil { 411 return nil, errors.Wrap(err, "failed to filter IngressRoute") 412 } 413 414 for _, ingressRoute := range ingressRoutes { 415 var targets endpoint.Targets 416 417 targets = append(targets, getTargetsFromTargetAnnotation(ingressRoute.Annotations)...) 418 419 fullname := fmt.Sprintf("%s/%s", ingressRoute.Namespace, ingressRoute.Name) 420 421 ingressEndpoints, err := ts.endpointsFromIngressRoute(ingressRoute, targets) 422 if err != nil { 423 return nil, err 424 } 425 if len(ingressEndpoints) == 0 { 426 log.Debugf("No endpoints could be generated from Host %s", fullname) 427 continue 428 } 429 430 log.Debugf("Endpoints generated from IngressRoute: %s: %v", fullname, ingressEndpoints) 431 ts.setDualstackLabelIngressRoute(ingressRoute, ingressEndpoints) 432 endpoints = append(endpoints, ingressEndpoints...) 433 } 434 435 return endpoints, nil 436 } 437 438 // oldIngressRouteTCPEndpoints extracts endpoints from all IngressRouteTCP objects 439 func (ts *traefikSource) oldIngressRouteTCPEndpoints() ([]*endpoint.Endpoint, error) { 440 var endpoints []*endpoint.Endpoint 441 442 irs, err := ts.oldIngressRouteTcpInformer.Lister().ByNamespace(ts.namespace).List(labels.Everything()) 443 if err != nil { 444 return nil, err 445 } 446 447 var ingressRouteTCPs []*IngressRouteTCP 448 for _, ingressRouteTCPObj := range irs { 449 unstructuredHost, ok := ingressRouteTCPObj.(*unstructured.Unstructured) 450 if !ok { 451 return nil, errors.New("could not convert IngressRouteTCP object to unstructured") 452 } 453 454 ingressRouteTCP := &IngressRouteTCP{} 455 err := ts.unstructuredConverter.scheme.Convert(unstructuredHost, ingressRouteTCP, nil) 456 if err != nil { 457 return nil, err 458 } 459 ingressRouteTCPs = append(ingressRouteTCPs, ingressRouteTCP) 460 } 461 462 ingressRouteTCPs, err = ts.filterIngressRouteTcpByAnnotations(ingressRouteTCPs) 463 if err != nil { 464 return nil, errors.Wrap(err, "failed to filter IngressRouteTCP") 465 } 466 467 for _, ingressRouteTCP := range ingressRouteTCPs { 468 var targets endpoint.Targets 469 470 targets = append(targets, getTargetsFromTargetAnnotation(ingressRouteTCP.Annotations)...) 471 472 fullname := fmt.Sprintf("%s/%s", ingressRouteTCP.Namespace, ingressRouteTCP.Name) 473 474 ingressEndpoints, err := ts.endpointsFromIngressRouteTCP(ingressRouteTCP, targets) 475 if err != nil { 476 return nil, err 477 } 478 if len(ingressEndpoints) == 0 { 479 log.Debugf("No endpoints could be generated from Host %s", fullname) 480 continue 481 } 482 483 log.Debugf("Endpoints generated from IngressRouteTCP: %s: %v", fullname, ingressEndpoints) 484 ts.setDualstackLabelIngressRouteTCP(ingressRouteTCP, ingressEndpoints) 485 endpoints = append(endpoints, ingressEndpoints...) 486 } 487 488 return endpoints, nil 489 } 490 491 // oldIngressRouteUDPEndpoints extracts endpoints from all IngressRouteUDP objects 492 func (ts *traefikSource) oldIngressRouteUDPEndpoints() ([]*endpoint.Endpoint, error) { 493 var endpoints []*endpoint.Endpoint 494 495 irs, err := ts.oldIngressRouteUdpInformer.Lister().ByNamespace(ts.namespace).List(labels.Everything()) 496 if err != nil { 497 return nil, err 498 } 499 500 var ingressRouteUDPs []*IngressRouteUDP 501 for _, ingressRouteUDPObj := range irs { 502 unstructuredHost, ok := ingressRouteUDPObj.(*unstructured.Unstructured) 503 if !ok { 504 return nil, errors.New("could not convert IngressRouteUDP object to unstructured") 505 } 506 507 ingressRoute := &IngressRouteUDP{} 508 err := ts.unstructuredConverter.scheme.Convert(unstructuredHost, ingressRoute, nil) 509 if err != nil { 510 return nil, err 511 } 512 ingressRouteUDPs = append(ingressRouteUDPs, ingressRoute) 513 } 514 515 ingressRouteUDPs, err = ts.filterIngressRouteUdpByAnnotations(ingressRouteUDPs) 516 if err != nil { 517 return nil, errors.Wrap(err, "failed to filter IngressRouteUDP") 518 } 519 520 for _, ingressRouteUDP := range ingressRouteUDPs { 521 var targets endpoint.Targets 522 523 targets = append(targets, getTargetsFromTargetAnnotation(ingressRouteUDP.Annotations)...) 524 525 fullname := fmt.Sprintf("%s/%s", ingressRouteUDP.Namespace, ingressRouteUDP.Name) 526 527 ingressEndpoints, err := ts.endpointsFromIngressRouteUDP(ingressRouteUDP, targets) 528 if err != nil { 529 return nil, err 530 } 531 if len(ingressEndpoints) == 0 { 532 log.Debugf("No endpoints could be generated from Host %s", fullname) 533 continue 534 } 535 536 log.Debugf("Endpoints generated from IngressRouteUDP: %s: %v", fullname, ingressEndpoints) 537 ts.setDualstackLabelIngressRouteUDP(ingressRouteUDP, ingressEndpoints) 538 endpoints = append(endpoints, ingressEndpoints...) 539 } 540 541 return endpoints, nil 542 } 543 544 // filterIngressRouteByAnnotation filters a list of IngressRoute by a given annotation selector. 545 func (ts *traefikSource) filterIngressRouteByAnnotation(ingressRoutes []*IngressRoute) ([]*IngressRoute, error) { 546 labelSelector, err := metav1.ParseToLabelSelector(ts.annotationFilter) 547 if err != nil { 548 return nil, err 549 } 550 selector, err := metav1.LabelSelectorAsSelector(labelSelector) 551 if err != nil { 552 return nil, err 553 } 554 555 // empty filter returns original list 556 if selector.Empty() { 557 return ingressRoutes, nil 558 } 559 560 filteredList := []*IngressRoute{} 561 562 for _, ingressRoute := range ingressRoutes { 563 // convert the IngressRoute's annotations to an equivalent label selector 564 annotations := labels.Set(ingressRoute.Annotations) 565 566 // include IngressRoute if its annotations match the selector 567 if selector.Matches(annotations) { 568 filteredList = append(filteredList, ingressRoute) 569 } 570 } 571 572 return filteredList, nil 573 } 574 575 // filterIngressRouteTcpByAnnotations filters a list of IngressRouteTCP by a given annotation selector. 576 func (ts *traefikSource) filterIngressRouteTcpByAnnotations(ingressRoutes []*IngressRouteTCP) ([]*IngressRouteTCP, error) { 577 labelSelector, err := metav1.ParseToLabelSelector(ts.annotationFilter) 578 if err != nil { 579 return nil, err 580 } 581 selector, err := metav1.LabelSelectorAsSelector(labelSelector) 582 if err != nil { 583 return nil, err 584 } 585 586 // empty filter returns original list 587 if selector.Empty() { 588 return ingressRoutes, nil 589 } 590 591 filteredList := []*IngressRouteTCP{} 592 593 for _, ingressRoute := range ingressRoutes { 594 // convert the IngressRoute's annotations to an equivalent label selector 595 annotations := labels.Set(ingressRoute.Annotations) 596 597 // include IngressRoute if its annotations match the selector 598 if selector.Matches(annotations) { 599 filteredList = append(filteredList, ingressRoute) 600 } 601 } 602 603 return filteredList, nil 604 } 605 606 // filterIngressRouteUdpByAnnotations filters a list of IngressRoute by a given annotation selector. 607 func (ts *traefikSource) filterIngressRouteUdpByAnnotations(ingressRoutes []*IngressRouteUDP) ([]*IngressRouteUDP, error) { 608 labelSelector, err := metav1.ParseToLabelSelector(ts.annotationFilter) 609 if err != nil { 610 return nil, err 611 } 612 selector, err := metav1.LabelSelectorAsSelector(labelSelector) 613 if err != nil { 614 return nil, err 615 } 616 617 // empty filter returns original list 618 if selector.Empty() { 619 return ingressRoutes, nil 620 } 621 622 filteredList := []*IngressRouteUDP{} 623 624 for _, ingressRoute := range ingressRoutes { 625 // convert the IngressRoute's annotations to an equivalent label selector 626 annotations := labels.Set(ingressRoute.Annotations) 627 628 // include IngressRoute if its annotations match the selector 629 if selector.Matches(annotations) { 630 filteredList = append(filteredList, ingressRoute) 631 } 632 } 633 634 return filteredList, nil 635 } 636 637 func (ts *traefikSource) setDualstackLabelIngressRoute(ingressRoute *IngressRoute, endpoints []*endpoint.Endpoint) { 638 val, ok := ingressRoute.Annotations[ALBDualstackAnnotationKey] 639 if ok && val == ALBDualstackAnnotationValue { 640 log.Debugf("Adding dualstack label to IngressRoute %s/%s.", ingressRoute.Namespace, ingressRoute.Name) 641 for _, ep := range endpoints { 642 ep.Labels[endpoint.DualstackLabelKey] = "true" 643 } 644 } 645 } 646 func (ts *traefikSource) setDualstackLabelIngressRouteTCP(ingressRoute *IngressRouteTCP, endpoints []*endpoint.Endpoint) { 647 val, ok := ingressRoute.Annotations[ALBDualstackAnnotationKey] 648 if ok && val == ALBDualstackAnnotationValue { 649 log.Debugf("Adding dualstack label to IngressRouteTCP %s/%s.", ingressRoute.Namespace, ingressRoute.Name) 650 for _, ep := range endpoints { 651 ep.Labels[endpoint.DualstackLabelKey] = "true" 652 } 653 } 654 } 655 func (ts *traefikSource) setDualstackLabelIngressRouteUDP(ingressRoute *IngressRouteUDP, endpoints []*endpoint.Endpoint) { 656 val, ok := ingressRoute.Annotations[ALBDualstackAnnotationKey] 657 if ok && val == ALBDualstackAnnotationValue { 658 log.Debugf("Adding dualstack label to IngressRouteUDP %s/%s.", ingressRoute.Namespace, ingressRoute.Name) 659 for _, ep := range endpoints { 660 ep.Labels[endpoint.DualstackLabelKey] = "true" 661 } 662 } 663 } 664 665 // endpointsFromIngressRoute extracts the endpoints from a IngressRoute object 666 func (ts *traefikSource) endpointsFromIngressRoute(ingressRoute *IngressRoute, targets endpoint.Targets) ([]*endpoint.Endpoint, error) { 667 var endpoints []*endpoint.Endpoint 668 669 resource := fmt.Sprintf("ingressroute/%s/%s", ingressRoute.Namespace, ingressRoute.Name) 670 671 ttl := getTTLFromAnnotations(ingressRoute.Annotations, resource) 672 673 providerSpecific, setIdentifier := getProviderSpecificAnnotations(ingressRoute.Annotations) 674 675 if !ts.ignoreHostnameAnnotation { 676 hostnameList := getHostnamesFromAnnotations(ingressRoute.Annotations) 677 for _, hostname := range hostnameList { 678 endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific, setIdentifier, resource)...) 679 } 680 } 681 682 for _, route := range ingressRoute.Spec.Routes { 683 match := route.Match 684 685 for _, hostEntry := range traefikHostExtractor.FindAllString(match, -1) { 686 for _, host := range traefikValueProcessor.FindAllString(hostEntry, -1) { 687 host = strings.TrimPrefix(host, "`") 688 host = strings.TrimSuffix(host, "`") 689 690 // Checking for host = * is required, as Host(`*`) can be set 691 if host != "*" && host != "" { 692 endpoints = append(endpoints, endpointsForHostname(host, targets, ttl, providerSpecific, setIdentifier, resource)...) 693 } 694 } 695 } 696 } 697 698 return endpoints, nil 699 } 700 701 // endpointsFromIngressRouteTCP extracts the endpoints from a IngressRouteTCP object 702 func (ts *traefikSource) endpointsFromIngressRouteTCP(ingressRoute *IngressRouteTCP, targets endpoint.Targets) ([]*endpoint.Endpoint, error) { 703 var endpoints []*endpoint.Endpoint 704 705 resource := fmt.Sprintf("ingressroutetcp/%s/%s", ingressRoute.Namespace, ingressRoute.Name) 706 707 ttl := getTTLFromAnnotations(ingressRoute.Annotations, resource) 708 709 providerSpecific, setIdentifier := getProviderSpecificAnnotations(ingressRoute.Annotations) 710 711 if !ts.ignoreHostnameAnnotation { 712 hostnameList := getHostnamesFromAnnotations(ingressRoute.Annotations) 713 for _, hostname := range hostnameList { 714 endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific, setIdentifier, resource)...) 715 } 716 } 717 718 for _, route := range ingressRoute.Spec.Routes { 719 match := route.Match 720 721 for _, hostEntry := range traefikHostExtractor.FindAllString(match, -1) { 722 for _, host := range traefikValueProcessor.FindAllString(hostEntry, -1) { 723 host = strings.TrimPrefix(host, "`") 724 host = strings.TrimSuffix(host, "`") 725 726 // Checking for host = * is required, as HostSNI(`*`) can be set 727 // in the case of TLS passthrough 728 if host != "*" && host != "" { 729 endpoints = append(endpoints, endpointsForHostname(host, targets, ttl, providerSpecific, setIdentifier, resource)...) 730 } 731 } 732 } 733 } 734 735 return endpoints, nil 736 } 737 738 // endpointsFromIngressRouteUDP extracts the endpoints from a IngressRouteUDP object 739 func (ts *traefikSource) endpointsFromIngressRouteUDP(ingressRoute *IngressRouteUDP, targets endpoint.Targets) ([]*endpoint.Endpoint, error) { 740 var endpoints []*endpoint.Endpoint 741 742 resource := fmt.Sprintf("ingressrouteudp/%s/%s", ingressRoute.Namespace, ingressRoute.Name) 743 744 ttl := getTTLFromAnnotations(ingressRoute.Annotations, resource) 745 746 providerSpecific, setIdentifier := getProviderSpecificAnnotations(ingressRoute.Annotations) 747 748 if !ts.ignoreHostnameAnnotation { 749 hostnameList := getHostnamesFromAnnotations(ingressRoute.Annotations) 750 for _, hostname := range hostnameList { 751 endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific, setIdentifier, resource)...) 752 } 753 } 754 755 return endpoints, nil 756 } 757 758 func (ts *traefikSource) AddEventHandler(ctx context.Context, handler func()) { 759 // Right now there is no way to remove event handler from informer, see: 760 // https://github.com/kubernetes/kubernetes/issues/79610 761 log.Debug("Adding event handler for IngressRoute") 762 ts.ingressRouteInformer.Informer().AddEventHandler(eventHandlerFunc(handler)) 763 ts.oldIngressRouteInformer.Informer().AddEventHandler(eventHandlerFunc(handler)) 764 log.Debug("Adding event handler for IngressRouteTCP") 765 ts.ingressRouteTcpInformer.Informer().AddEventHandler(eventHandlerFunc(handler)) 766 ts.oldIngressRouteTcpInformer.Informer().AddEventHandler(eventHandlerFunc(handler)) 767 log.Debug("Adding event handler for IngressRouteUDP") 768 ts.ingressRouteUdpInformer.Informer().AddEventHandler(eventHandlerFunc(handler)) 769 ts.oldIngressRouteUdpInformer.Informer().AddEventHandler(eventHandlerFunc(handler)) 770 } 771 772 // newTraefikUnstructuredConverter returns a new unstructuredConverter initialized 773 func newTraefikUnstructuredConverter() (*unstructuredConverter, error) { 774 uc := &unstructuredConverter{ 775 scheme: runtime.NewScheme(), 776 } 777 778 // Add the core types we need 779 uc.scheme.AddKnownTypes(ingressrouteGVR.GroupVersion(), &IngressRoute{}, &IngressRouteList{}) 780 uc.scheme.AddKnownTypes(oldIngressrouteGVR.GroupVersion(), &IngressRoute{}, &IngressRouteList{}) 781 uc.scheme.AddKnownTypes(ingressrouteTCPGVR.GroupVersion(), &IngressRouteTCP{}, &IngressRouteTCPList{}) 782 uc.scheme.AddKnownTypes(oldIngressrouteTCPGVR.GroupVersion(), &IngressRouteTCP{}, &IngressRouteTCPList{}) 783 uc.scheme.AddKnownTypes(ingressrouteUDPGVR.GroupVersion(), &IngressRouteUDP{}, &IngressRouteUDPList{}) 784 uc.scheme.AddKnownTypes(oldIngressrouteUDPGVR.GroupVersion(), &IngressRouteUDP{}, &IngressRouteUDPList{}) 785 if err := scheme.AddToScheme(uc.scheme); err != nil { 786 return nil, err 787 } 788 789 return uc, nil 790 } 791 792 // Basic redefinition of Traefik 2's CRD: https://github.com/traefik/traefik/tree/v2.8.7/pkg/provider/kubernetes/crd/traefik/v1alpha1 793 794 // traefikIngressRouteSpec defines the desired state of IngressRoute. 795 type traefikIngressRouteSpec struct { 796 // Routes defines the list of routes. 797 Routes []traefikRoute `json:"routes"` 798 } 799 800 // traefikRoute holds the HTTP route configuration. 801 type traefikRoute struct { 802 // Match defines the router's rule. 803 // More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#rule 804 Match string `json:"match"` 805 } 806 807 // IngressRoute is the CRD implementation of a Traefik HTTP Router. 808 type IngressRoute struct { 809 metav1.TypeMeta `json:",inline"` 810 // Standard object's metadata. 811 // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata 812 metav1.ObjectMeta `json:"metadata"` 813 814 Spec traefikIngressRouteSpec `json:"spec"` 815 } 816 817 // IngressRouteList is a collection of IngressRoute. 818 type IngressRouteList struct { 819 metav1.TypeMeta `json:",inline"` 820 // Standard object's metadata. 821 // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata 822 metav1.ListMeta `json:"metadata"` 823 824 // Items is the list of IngressRoute. 825 Items []IngressRoute `json:"items"` 826 } 827 828 // traefikIngressRouteTCPSpec defines the desired state of IngressRouteTCP. 829 type traefikIngressRouteTCPSpec struct { 830 Routes []traefikRouteTCP `json:"routes"` 831 } 832 833 // traefikRouteTCP holds the TCP route configuration. 834 type traefikRouteTCP struct { 835 // Match defines the router's rule. 836 // More info: https://doc.traefik.io/traefik/v2.9/routing/routers/#rule_1 837 Match string `json:"match"` 838 } 839 840 // IngressRouteTCP is the CRD implementation of a Traefik TCP Router. 841 type IngressRouteTCP struct { 842 metav1.TypeMeta `json:",inline"` 843 // Standard object's metadata. 844 // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata 845 metav1.ObjectMeta `json:"metadata"` 846 847 Spec traefikIngressRouteTCPSpec `json:"spec"` 848 } 849 850 // IngressRouteTCPList is a collection of IngressRouteTCP. 851 type IngressRouteTCPList struct { 852 metav1.TypeMeta `json:",inline"` 853 // Standard object's metadata. 854 // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata 855 metav1.ListMeta `json:"metadata"` 856 857 // Items is the list of IngressRouteTCP. 858 Items []IngressRouteTCP `json:"items"` 859 } 860 861 // IngressRouteUDP is a CRD implementation of a Traefik UDP Router. 862 type IngressRouteUDP struct { 863 metav1.TypeMeta `json:",inline"` 864 // Standard object's metadata. 865 // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata 866 metav1.ObjectMeta `json:"metadata"` 867 } 868 869 // IngressRouteUDPList is a collection of IngressRouteUDP. 870 type IngressRouteUDPList struct { 871 metav1.TypeMeta `json:",inline"` 872 // Standard object's metadata. 873 // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata 874 metav1.ListMeta `json:"metadata"` 875 876 // Items is the list of IngressRouteUDP. 877 Items []IngressRouteUDP `json:"items"` 878 } 879 880 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 881 func (in *IngressRoute) DeepCopyInto(out *IngressRoute) { 882 *out = *in 883 out.TypeMeta = in.TypeMeta 884 in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 885 in.Spec.DeepCopyInto(&out.Spec) 886 } 887 888 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressRoute. 889 func (in *IngressRoute) DeepCopy() *IngressRoute { 890 if in == nil { 891 return nil 892 } 893 out := new(IngressRoute) 894 in.DeepCopyInto(out) 895 return out 896 } 897 898 // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 899 func (in *IngressRoute) DeepCopyObject() runtime.Object { 900 if c := in.DeepCopy(); c != nil { 901 return c 902 } 903 return nil 904 } 905 906 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 907 func (in *IngressRouteList) DeepCopyInto(out *IngressRouteList) { 908 *out = *in 909 out.TypeMeta = in.TypeMeta 910 in.ListMeta.DeepCopyInto(&out.ListMeta) 911 if in.Items != nil { 912 in, out := &in.Items, &out.Items 913 *out = make([]IngressRoute, len(*in)) 914 for i := range *in { 915 (*in)[i].DeepCopyInto(&(*out)[i]) 916 } 917 } 918 } 919 920 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressRouteList. 921 func (in *IngressRouteList) DeepCopy() *IngressRouteList { 922 if in == nil { 923 return nil 924 } 925 out := new(IngressRouteList) 926 in.DeepCopyInto(out) 927 return out 928 } 929 930 // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 931 func (in *IngressRouteList) DeepCopyObject() runtime.Object { 932 if c := in.DeepCopy(); c != nil { 933 return c 934 } 935 return nil 936 } 937 938 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 939 func (in *traefikIngressRouteSpec) DeepCopyInto(out *traefikIngressRouteSpec) { 940 *out = *in 941 if in.Routes != nil { 942 in, out := &in.Routes, &out.Routes 943 *out = make([]traefikRoute, len(*in)) 944 for i := range *in { 945 (*in)[i].DeepCopyInto(&(*out)[i]) 946 } 947 } 948 } 949 950 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressRouteSpec. 951 func (in *traefikIngressRouteSpec) DeepCopy() *traefikIngressRouteSpec { 952 if in == nil { 953 return nil 954 } 955 out := new(traefikIngressRouteSpec) 956 in.DeepCopyInto(out) 957 return out 958 } 959 960 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 961 func (in *traefikRoute) DeepCopyInto(out *traefikRoute) { 962 *out = *in 963 } 964 965 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Route. 966 func (in *traefikRoute) DeepCopy() *traefikRoute { 967 if in == nil { 968 return nil 969 } 970 out := new(traefikRoute) 971 in.DeepCopyInto(out) 972 return out 973 } 974 975 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 976 func (in *IngressRouteTCP) DeepCopyInto(out *IngressRouteTCP) { 977 *out = *in 978 out.TypeMeta = in.TypeMeta 979 in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 980 in.Spec.DeepCopyInto(&out.Spec) 981 } 982 983 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressRouteTCP. 984 func (in *IngressRouteTCP) DeepCopy() *IngressRouteTCP { 985 if in == nil { 986 return nil 987 } 988 out := new(IngressRouteTCP) 989 in.DeepCopyInto(out) 990 return out 991 } 992 993 // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 994 func (in *IngressRouteTCP) DeepCopyObject() runtime.Object { 995 if c := in.DeepCopy(); c != nil { 996 return c 997 } 998 return nil 999 } 1000 1001 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 1002 func (in *IngressRouteTCPList) DeepCopyInto(out *IngressRouteTCPList) { 1003 *out = *in 1004 out.TypeMeta = in.TypeMeta 1005 in.ListMeta.DeepCopyInto(&out.ListMeta) 1006 if in.Items != nil { 1007 in, out := &in.Items, &out.Items 1008 *out = make([]IngressRouteTCP, len(*in)) 1009 for i := range *in { 1010 (*in)[i].DeepCopyInto(&(*out)[i]) 1011 } 1012 } 1013 } 1014 1015 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressRouteTCPList. 1016 func (in *IngressRouteTCPList) DeepCopy() *IngressRouteTCPList { 1017 if in == nil { 1018 return nil 1019 } 1020 out := new(IngressRouteTCPList) 1021 in.DeepCopyInto(out) 1022 return out 1023 } 1024 1025 // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 1026 func (in *IngressRouteTCPList) DeepCopyObject() runtime.Object { 1027 if c := in.DeepCopy(); c != nil { 1028 return c 1029 } 1030 return nil 1031 } 1032 1033 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 1034 func (in *traefikIngressRouteTCPSpec) DeepCopyInto(out *traefikIngressRouteTCPSpec) { 1035 *out = *in 1036 if in.Routes != nil { 1037 in, out := &in.Routes, &out.Routes 1038 *out = make([]traefikRouteTCP, len(*in)) 1039 for i := range *in { 1040 (*in)[i].DeepCopyInto(&(*out)[i]) 1041 } 1042 } 1043 } 1044 1045 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressRouteTCPSpec. 1046 func (in *traefikIngressRouteTCPSpec) DeepCopy() *traefikIngressRouteTCPSpec { 1047 if in == nil { 1048 return nil 1049 } 1050 out := new(traefikIngressRouteTCPSpec) 1051 in.DeepCopyInto(out) 1052 return out 1053 } 1054 1055 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 1056 func (in *traefikRouteTCP) DeepCopyInto(out *traefikRouteTCP) { 1057 *out = *in 1058 } 1059 1060 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteTCP. 1061 func (in *traefikRouteTCP) DeepCopy() *traefikRouteTCP { 1062 if in == nil { 1063 return nil 1064 } 1065 out := new(traefikRouteTCP) 1066 in.DeepCopyInto(out) 1067 return out 1068 } 1069 1070 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 1071 func (in *IngressRouteUDP) DeepCopyInto(out *IngressRouteUDP) { 1072 *out = *in 1073 out.TypeMeta = in.TypeMeta 1074 in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 1075 } 1076 1077 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressRouteUDP. 1078 func (in *IngressRouteUDP) DeepCopy() *IngressRouteUDP { 1079 if in == nil { 1080 return nil 1081 } 1082 out := new(IngressRouteUDP) 1083 in.DeepCopyInto(out) 1084 return out 1085 } 1086 1087 // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 1088 func (in *IngressRouteUDP) DeepCopyObject() runtime.Object { 1089 if c := in.DeepCopy(); c != nil { 1090 return c 1091 } 1092 return nil 1093 } 1094 1095 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 1096 func (in *IngressRouteUDPList) DeepCopyInto(out *IngressRouteUDPList) { 1097 *out = *in 1098 out.TypeMeta = in.TypeMeta 1099 in.ListMeta.DeepCopyInto(&out.ListMeta) 1100 if in.Items != nil { 1101 in, out := &in.Items, &out.Items 1102 *out = make([]IngressRouteUDP, len(*in)) 1103 for i := range *in { 1104 (*in)[i].DeepCopyInto(&(*out)[i]) 1105 } 1106 } 1107 } 1108 1109 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressRouteUDPList. 1110 func (in *IngressRouteUDPList) DeepCopy() *IngressRouteUDPList { 1111 if in == nil { 1112 return nil 1113 } 1114 out := new(IngressRouteUDPList) 1115 in.DeepCopyInto(out) 1116 return out 1117 } 1118 1119 // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 1120 func (in *IngressRouteUDPList) DeepCopyObject() runtime.Object { 1121 if c := in.DeepCopy(); c != nil { 1122 return c 1123 } 1124 return nil 1125 }