mirror of
https://github.com/lukaszraczylo/kubemirror.git
synced 2026-07-04 16:46:32 +00:00
improvements jan2025 (#6)
* feat(controller): add lazy watcher, improve resource usage and add pattern validation - [x] Add cache sync health check for readiness probe verification - [x] Create namespace lister with API reader support for fresh label queries - [x] Add pattern validation with warning logs for invalid glob patterns - [x] Implement lazy watcher initialization mode to scan for active resources - [x] Add requeue delay to namespace reconciler for cache settlement - [x] Replace custom containsString with slices.Contains from stdlib - [x] Add structured logging context to reconcilers (kind, group, version) - [x] Improve error variable naming for clarity in nested conditions - [x] Add nil-safe label access in namespace reconciler setup - [x] Add APIReader to namespace and source reconcilers for fresh data - [x] Improve type assertions with proper error handling in mirror operations - [x] Reorder struct fields for consistency and readability - [x] Add comprehensive pattern validation tests and validation API * feat(controller): add lazy watcher, improve resource usage and add pattern validation - [x] Add circuit breaker for reconciliation failure tracking and prevention - [x] Implement granular registration state tracking (not-registered, source-only, fully-registered) - [x] Add lazy controller initialization for active resource types only - [x] Consolidate namespace listing into single API call for efficiency - [x] Add mirror creation verification to catch webhook rejections - [x] Implement high-cardinality resource detection and warnings - [x] Add source deletion check in mirror reconciler to prevent races - [x] Preserve transformation annotations on errors in mirror reconciliation - [x] Expand constants documentation with labels vs annotations design rationale - [x] Add comprehensive test coverage for circuit breaker and registration states - [x] Add mutation-safety tests for hash computation * fixup! feat(controller): add lazy watcher, improve resource usage and add pattern validation
This commit is contained in:
@@ -4,6 +4,8 @@ package controller
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
@@ -20,21 +22,31 @@ import (
|
||||
"github.com/lukaszraczylo/kubemirror/pkg/filter"
|
||||
)
|
||||
|
||||
const (
|
||||
// cacheSettleDelay is the time to wait after namespace label changes
|
||||
// to allow informer caches to sync. This addresses the race condition
|
||||
// where namespace watch events fire before the cache is updated.
|
||||
cacheSettleDelay = 3 * time.Second
|
||||
)
|
||||
|
||||
// NamespaceReconciler watches for namespace CREATE and UPDATE events
|
||||
// and triggers reconciliation of source resources that match the new namespace.
|
||||
type NamespaceReconciler struct {
|
||||
client.Client
|
||||
NamespaceLister NamespaceLister
|
||||
APIReader client.Reader
|
||||
Scheme *runtime.Scheme
|
||||
Config *config.Config
|
||||
Filter *filter.NamespaceFilter
|
||||
NamespaceLister NamespaceLister
|
||||
// ResourceTypes contains all discovered resource types to reconcile
|
||||
ResourceTypes []config.ResourceType
|
||||
ResourceTypes []config.ResourceType
|
||||
}
|
||||
|
||||
// Reconcile processes namespace events and creates mirrors for matching sources.
|
||||
func (r *NamespaceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
logger := log.FromContext(ctx).WithValues("namespace", req.Name)
|
||||
logger := log.FromContext(ctx).WithValues(
|
||||
"namespace", req.Name,
|
||||
"reconciler", "namespace",
|
||||
)
|
||||
|
||||
// Fetch the namespace
|
||||
namespace := &corev1.Namespace{}
|
||||
@@ -76,7 +88,11 @@ func (r *NamespaceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
|
||||
return ctrl.Result{}, fmt.Errorf("failed to reconcile %d source resources", totalErrors)
|
||||
}
|
||||
|
||||
return ctrl.Result{}, nil
|
||||
// Requeue with delay to catch any updates missed due to cache staleness.
|
||||
// This is particularly important for namespace label changes where the
|
||||
// informer cache may not yet reflect the new label state. The delay allows
|
||||
// the cache to settle and ensures all relevant source resources are reconciled.
|
||||
return ctrl.Result{RequeueAfter: cacheSettleDelay}, nil
|
||||
}
|
||||
|
||||
// reconcileResourceType finds and reconciles all sources of a specific resource type
|
||||
@@ -125,13 +141,7 @@ func (r *NamespaceReconciler) reconcileResourceType(ctx context.Context, rt conf
|
||||
}
|
||||
|
||||
// Check if the new namespace matches this source's targets
|
||||
var isTarget bool
|
||||
for _, target := range targetNamespaces {
|
||||
if target == namespaceName {
|
||||
isTarget = true
|
||||
break
|
||||
}
|
||||
}
|
||||
isTarget := slices.Contains(targetNamespaces, namespaceName)
|
||||
|
||||
if isTarget {
|
||||
// Create or update mirror in the namespace
|
||||
@@ -222,30 +232,47 @@ func (r *NamespaceReconciler) resolveTargetNamespaces(ctx context.Context, sourc
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Get all namespaces
|
||||
allNamespaces, err := r.NamespaceLister.ListNamespaces(ctx)
|
||||
// Validate patterns and log warnings for invalid ones
|
||||
validationResults, allValid := filter.ValidatePatterns(patterns)
|
||||
if !allValid {
|
||||
logger := log.FromContext(ctx)
|
||||
invalidPatterns := filter.InvalidPatterns(validationResults)
|
||||
for _, invalid := range invalidPatterns {
|
||||
logger.Info("invalid glob pattern in target-namespaces annotation, pattern will be skipped",
|
||||
"pattern", invalid.Pattern,
|
||||
"error", invalid.Error.Error(),
|
||||
"source", source.GetName(),
|
||||
"namespace", source.GetNamespace(),
|
||||
)
|
||||
}
|
||||
|
||||
// Filter to only valid patterns
|
||||
var validPatterns []string
|
||||
for _, result := range validationResults {
|
||||
if result.Valid {
|
||||
validPatterns = append(validPatterns, result.Pattern)
|
||||
}
|
||||
}
|
||||
patterns = validPatterns
|
||||
|
||||
// If no valid patterns remain, return empty
|
||||
if len(patterns) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Get all namespace info in a single API call (more efficient than 3 separate calls)
|
||||
nsInfo, err := r.NamespaceLister.ListNamespacesWithLabels(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list namespaces: %w", err)
|
||||
}
|
||||
|
||||
// Get namespaces with allow-mirrors label
|
||||
allowMirrorsNamespaces, err := r.NamespaceLister.ListAllowMirrorsNamespaces(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list allow-mirrors namespaces: %w", err)
|
||||
}
|
||||
|
||||
// Get namespaces that have explicitly opted out (allow-mirrors="false")
|
||||
optOutNamespaces, err := r.NamespaceLister.ListOptOutNamespaces(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list opt-out namespaces: %w", err)
|
||||
}
|
||||
|
||||
// Resolve target namespaces
|
||||
// Resolve target namespaces using the pre-categorized namespace info
|
||||
targetNamespaces := filter.ResolveTargetNamespaces(
|
||||
patterns,
|
||||
allNamespaces,
|
||||
allowMirrorsNamespaces,
|
||||
optOutNamespaces,
|
||||
nsInfo.All,
|
||||
nsInfo.AllowMirrors,
|
||||
nsInfo.OptOut,
|
||||
source.GetNamespace(),
|
||||
r.Filter,
|
||||
)
|
||||
@@ -292,8 +319,18 @@ func (r *NamespaceReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
}
|
||||
|
||||
// Check if allow-mirrors label changed
|
||||
oldLabel := oldNs.Labels[constants.LabelAllowMirrors]
|
||||
newLabel := newNs.Labels[constants.LabelAllowMirrors]
|
||||
// Use GetLabels() to safely handle nil labels map
|
||||
oldLabels := oldNs.GetLabels()
|
||||
newLabels := newNs.GetLabels()
|
||||
|
||||
// Get label values with nil-safe access
|
||||
var oldLabel, newLabel string
|
||||
if oldLabels != nil {
|
||||
oldLabel = oldLabels[constants.LabelAllowMirrors]
|
||||
}
|
||||
if newLabels != nil {
|
||||
newLabel = newLabels[constants.LabelAllowMirrors]
|
||||
}
|
||||
|
||||
return oldLabel != newLabel
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user