@@ -19,6 +19,7 @@ package controller
1919import (
2020 "context"
2121 "reflect"
22+ "strings"
2223 "time"
2324
2425 homerv1alpha1 "github.com/rajsinghtech/homer-operator.git/api/v1alpha1"
@@ -27,6 +28,8 @@ import (
2728 corev1 "k8s.io/api/core/v1"
2829 networkingv1 "k8s.io/api/networking/v1"
2930 apierrors "k8s.io/apimachinery/pkg/api/errors"
31+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32+ "k8s.io/apimachinery/pkg/labels"
3033 "k8s.io/apimachinery/pkg/runtime"
3134 "k8s.io/apimachinery/pkg/util/wait"
3235 ctrl "sigs.k8s.io/controller-runtime"
@@ -75,6 +78,24 @@ func (r *DashboardReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
7578 return ctrl.Result {}, err
7679 }
7780
81+ // Filter Ingresses based on dashboard selectors
82+ filteredIngresses := []networkingv1.Ingress {}
83+ for _ , ingress := range ingresses .Items {
84+ shouldInclude , err := r .shouldIncludeIngressForDashboard (ctx , & ingress , & dashboard )
85+ if err != nil {
86+ log .Error (err , "unable to determine if Ingress should be included" , "dashboard" , dashboard .Name , "ingress" , ingress .Name )
87+ return ctrl.Result {}, err
88+ }
89+ if shouldInclude {
90+ filteredIngresses = append (filteredIngresses , ingress )
91+ }
92+ }
93+
94+ // Create filtered Ingress list
95+ filteredIngressList := networkingv1.IngressList {
96+ Items : filteredIngresses ,
97+ }
98+
7899 // Validate theme configuration
79100 if err := homer .ValidateTheme (dashboard .Spec .HomerConfig .Theme ); err != nil {
80101 log .Error (err , "invalid theme configuration" , "dashboard" , req .NamespacedName )
@@ -190,9 +211,23 @@ func (r *DashboardReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
190211 log .Error (err , "unable to list HTTPRoutes" , "dashboard" , req .NamespacedName )
191212 return ctrl.Result {}, err
192213 }
193- configMap = homer .CreateConfigMapWithHTTPRoutes (homerConfig , dashboard .Name , dashboard .Namespace , * ingresses , httproutes .Items , & dashboard , dashboard .Spec .DomainFilters )
214+
215+ // Filter HTTPRoutes based on dashboard selectors
216+ filteredHTTPRoutes := []gatewayv1.HTTPRoute {}
217+ for _ , httproute := range httproutes .Items {
218+ shouldInclude , err := r .shouldIncludeHTTPRouteForDashboard (ctx , & httproute , & dashboard )
219+ if err != nil {
220+ log .Error (err , "unable to determine if HTTPRoute should be included" , "dashboard" , dashboard .Name , "httproute" , httproute .Name )
221+ return ctrl.Result {}, err
222+ }
223+ if shouldInclude {
224+ filteredHTTPRoutes = append (filteredHTTPRoutes , httproute )
225+ }
226+ }
227+
228+ configMap = homer .CreateConfigMapWithHTTPRoutes (homerConfig , dashboard .Name , dashboard .Namespace , filteredIngressList , filteredHTTPRoutes , & dashboard , dashboard .Spec .DomainFilters )
194229 } else {
195- configMap = homer .CreateConfigMap (homerConfig , dashboard .Name , dashboard .Namespace , * ingresses , & dashboard )
230+ configMap = homer .CreateConfigMap (homerConfig , dashboard .Name , dashboard .Namespace , filteredIngressList , & dashboard )
196231 }
197232
198233 // List of resources
@@ -278,3 +313,156 @@ func (r *DashboardReconciler) updateConfigMapWithRetry(ctx context.Context, conf
278313 return true , nil
279314 })
280315}
316+
317+ // shouldIncludeHTTPRouteForDashboard determines if an HTTPRoute should be included
318+ // based on the Dashboard's selectors and filters. If no selectors are specified, all HTTPRoutes are included.
319+ func (r * DashboardReconciler ) shouldIncludeHTTPRouteForDashboard (ctx context.Context , httproute * gatewayv1.HTTPRoute , dashboard * homerv1alpha1.Dashboard ) (bool , error ) {
320+ log := log .FromContext (ctx )
321+
322+ // Check HTTPRoute label selector
323+ if dashboard .Spec .HTTPRouteSelector != nil {
324+ selector , err := metav1 .LabelSelectorAsSelector (dashboard .Spec .HTTPRouteSelector )
325+ if err != nil {
326+ return false , err
327+ }
328+ if ! selector .Matches (labels .Set (httproute .Labels )) {
329+ log .V (1 ).Info ("HTTPRoute excluded by HTTPRoute label selector" , "httproute" , httproute .Name )
330+ return false , nil
331+ }
332+ }
333+
334+ // Check domain filters
335+ if len (dashboard .Spec .DomainFilters ) > 0 {
336+ if ! r .matchesDomainFilters (httproute .Spec .Hostnames , dashboard .Spec .DomainFilters ) {
337+ log .Info ("HTTPRoute excluded by domain filters" , "httproute" , httproute .Name , "hostnames" , httproute .Spec .Hostnames , "domainFilters" , dashboard .Spec .DomainFilters )
338+ return false , nil
339+ }
340+ log .V (1 ).Info ("HTTPRoute included by domain filters" , "httproute" , httproute .Name , "hostnames" , httproute .Spec .Hostnames , "domainFilters" , dashboard .Spec .DomainFilters )
341+ }
342+
343+ // Check Gateway selector
344+ if dashboard .Spec .GatewaySelector != nil {
345+ selector , err := metav1 .LabelSelectorAsSelector (dashboard .Spec .GatewaySelector )
346+ if err != nil {
347+ return false , err
348+ }
349+
350+ gatewayMatched := false
351+ // Check each parent Gateway reference in the HTTPRoute
352+ for _ , parentRef := range httproute .Spec .ParentRefs {
353+ // Default to Gateway kind if not specified
354+ kind := "Gateway"
355+ if parentRef .Kind != nil {
356+ kind = string (* parentRef .Kind )
357+ }
358+
359+ // Only check Gateway resources
360+ if kind != "Gateway" {
361+ continue
362+ }
363+
364+ // Default to same namespace as HTTPRoute if not specified
365+ namespace := httproute .Namespace
366+ if parentRef .Namespace != nil {
367+ namespace = string (* parentRef .Namespace )
368+ }
369+
370+ // Fetch the Gateway
371+ gateway := & gatewayv1.Gateway {}
372+ gatewayKey := client.ObjectKey {
373+ Name : string (parentRef .Name ),
374+ Namespace : namespace ,
375+ }
376+
377+ if err := r .Get (ctx , gatewayKey , gateway ); err != nil {
378+ if apierrors .IsNotFound (err ) {
379+ log .V (1 ).Info ("Gateway not found" , "gateway" , gatewayKey )
380+ continue
381+ }
382+ return false , err
383+ }
384+
385+ // Check if Gateway labels match the selector
386+ if selector .Matches (labels .Set (gateway .Labels )) {
387+ gatewayMatched = true
388+ break
389+ }
390+ }
391+
392+ if ! gatewayMatched {
393+ log .V (1 ).Info ("HTTPRoute excluded by Gateway selector" , "httproute" , httproute .Name )
394+ return false , nil
395+ }
396+ }
397+
398+ return true , nil
399+ }
400+
401+ // matchesDomainFilters checks if any hostname matches the domain filters
402+ func (r * DashboardReconciler ) matchesDomainFilters (hostnames []gatewayv1.Hostname , domainFilters []string ) bool {
403+ if len (domainFilters ) == 0 {
404+ return true
405+ }
406+
407+ for _ , hostname := range hostnames {
408+ hostnameStr := string (hostname )
409+ for _ , filter := range domainFilters {
410+ // Support exact match or subdomain match
411+ if hostnameStr == filter || strings .HasSuffix (hostnameStr , "." + filter ) {
412+ return true
413+ }
414+ }
415+ }
416+
417+ return false
418+ }
419+
420+ // shouldIncludeIngressForDashboard determines if an Ingress should be included
421+ // based on the Dashboard's selectors and filters. If no selectors are specified, all Ingresses are included.
422+ func (r * DashboardReconciler ) shouldIncludeIngressForDashboard (ctx context.Context , ingress * networkingv1.Ingress , dashboard * homerv1alpha1.Dashboard ) (bool , error ) {
423+ log := log .FromContext (ctx )
424+
425+ // Check Ingress label selector
426+ if dashboard .Spec .IngressSelector != nil {
427+ selector , err := metav1 .LabelSelectorAsSelector (dashboard .Spec .IngressSelector )
428+ if err != nil {
429+ return false , err
430+ }
431+ if ! selector .Matches (labels .Set (ingress .Labels )) {
432+ log .V (1 ).Info ("Ingress excluded by Ingress label selector" , "ingress" , ingress .Name )
433+ return false , nil
434+ }
435+ }
436+
437+ // Check domain filters
438+ if len (dashboard .Spec .DomainFilters ) > 0 {
439+ if ! r .matchesIngressDomainFilters (ingress , dashboard .Spec .DomainFilters ) {
440+ log .V (1 ).Info ("Ingress excluded by domain filters" , "ingress" , ingress .Name )
441+ return false , nil
442+ }
443+ }
444+
445+ return true , nil
446+ }
447+
448+ // matchesIngressDomainFilters checks if any Ingress rule host matches the domain filters
449+ func (r * DashboardReconciler ) matchesIngressDomainFilters (ingress * networkingv1.Ingress , domainFilters []string ) bool {
450+ if len (domainFilters ) == 0 {
451+ return true
452+ }
453+
454+ for _ , rule := range ingress .Spec .Rules {
455+ if rule .Host == "" {
456+ continue
457+ }
458+
459+ for _ , filter := range domainFilters {
460+ // Support exact match or subdomain match
461+ if rule .Host == filter || strings .HasSuffix (rule .Host , "." + filter ) {
462+ return true
463+ }
464+ }
465+ }
466+
467+ return false
468+ }
0 commit comments