Skip to content

Commit ced9595

Browse files
committed
Remove deprecated sample dashboard configurations and enhance the Dashboard controller with filtering capabilities for Ingress and HTTPRoute resources. Implement domain filtering logic and update tests to validate the new filtering features, ensuring only relevant resources are included in the dashboard configuration.
1 parent 7782462 commit ced9595

File tree

5 files changed

+1283
-145
lines changed

5 files changed

+1283
-145
lines changed

config/samples/dashboard-with-domain-filters.yaml

Lines changed: 0 additions & 60 deletions
This file was deleted.

config/samples/dashboard-with-gateway-selector.yaml

Lines changed: 0 additions & 64 deletions
This file was deleted.

internal/controller/dashboard_controller.go

Lines changed: 190 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package controller
1919
import (
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

Comments
 (0)