mirror of
https://github.com/projectcapsule/capsule.git
synced 2026-08-22 05:57:02 +00:00
* chore * perfromance improvements Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * perfromance improvements Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat(performance): removed duplicate client calls from all admission paths Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat(performance): removed duplicate client calls from all admission paths Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat(performance): removed duplicate client calls from all admission paths Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat(performance): removed duplicate client calls from all admission paths Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat(performance): removed duplicate client calls from all admission paths Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat(performance): removed duplicate client calls from all admission paths Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat(performance): removed duplicate client calls from all admission paths Signed-off-by: Oliver Baehler <oliver@sudo-i.net> --------- Signed-off-by: Oliver Baehler <oliver@sudo-i.net>
208 lines
5.6 KiB
Go
208 lines
5.6 KiB
Go
// Copyright 2020-2026 Project Capsule Authors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package invalidator
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/go-logr/logr"
|
|
corev1 "k8s.io/api/core/v1"
|
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
"k8s.io/client-go/rest"
|
|
"k8s.io/client-go/util/workqueue"
|
|
ctrl "sigs.k8s.io/controller-runtime"
|
|
"sigs.k8s.io/controller-runtime/pkg/builder"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
"sigs.k8s.io/controller-runtime/pkg/event"
|
|
"sigs.k8s.io/controller-runtime/pkg/handler"
|
|
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
|
|
|
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
|
"github.com/projectcapsule/capsule/internal/cache"
|
|
"github.com/projectcapsule/capsule/internal/controllers/utils"
|
|
"github.com/projectcapsule/capsule/internal/metrics"
|
|
"github.com/projectcapsule/capsule/pkg/runtime/configuration"
|
|
"github.com/projectcapsule/capsule/pkg/runtime/predicates"
|
|
)
|
|
|
|
type CacheInvalidator struct {
|
|
client.Client
|
|
|
|
Rest *rest.Config
|
|
Log logr.Logger
|
|
|
|
reader client.Reader
|
|
configName string
|
|
metrics *metrics.ConfigRecorder
|
|
|
|
Configuration configuration.Configuration
|
|
|
|
RegistryCache *cache.RegistryRuleSetCache
|
|
TargetsCache *cache.CompiledTargetsCache[string]
|
|
JSONPathCache *cache.JSONPathCache
|
|
CELCache *cache.CELCache
|
|
ImpersonationCache *cache.ImpersonationCache
|
|
RegexCache *cache.RegexCache
|
|
}
|
|
|
|
func (r *CacheInvalidator) NeedLeaderElection() bool {
|
|
return false
|
|
}
|
|
|
|
// Start is the Runnable function triggered upon Manager start-up to perform cache population.
|
|
func (r *CacheInvalidator) Start(ctx context.Context) error {
|
|
if err := r.rebuildCaches(ctx, r.Log); err != nil {
|
|
r.Log.Error(err, "cache population failed")
|
|
|
|
return nil
|
|
}
|
|
|
|
<-ctx.Done()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *CacheInvalidator) SetupWithManager(
|
|
mgr ctrl.Manager,
|
|
ctrlConfig utils.ControllerOptions,
|
|
metrics *metrics.ConfigRecorder,
|
|
) (err error) {
|
|
r.configName = ctrlConfig.ConfigurationName
|
|
r.reader = mgr.GetAPIReader()
|
|
r.metrics = metrics
|
|
|
|
err = ctrl.NewControllerManagedBy(mgr).
|
|
Named("config/caches").
|
|
For(
|
|
&capsulev1beta2.CapsuleConfiguration{},
|
|
builder.WithPredicates(
|
|
predicate.GenerationChangedPredicate{},
|
|
predicates.NamesMatchingPredicate{Names: []string{ctrlConfig.ConfigurationName}},
|
|
),
|
|
).
|
|
Watches(
|
|
&capsulev1beta2.CapsuleConfiguration{},
|
|
handler.Funcs{
|
|
UpdateFunc: func(ctx context.Context, updateEvent event.TypedUpdateEvent[client.Object], limitingInterface workqueue.TypedRateLimitingInterface[reconcile.Request]) {
|
|
if err := r.rebuildImpersonationCache(ctx, r.Log); err != nil {
|
|
r.Log.Error(err, "unable to invalidate impersonation cache")
|
|
}
|
|
},
|
|
},
|
|
builder.WithPredicates(
|
|
predicates.CapsuleConfigSpecImpersonationChangedPredicate{},
|
|
predicates.NamesMatchingPredicate{Names: []string{ctrlConfig.ConfigurationName}},
|
|
),
|
|
).
|
|
WatchesMetadata(
|
|
&corev1.ServiceAccount{},
|
|
handler.Funcs{
|
|
DeleteFunc: func(
|
|
ctx context.Context,
|
|
e event.TypedDeleteEvent[client.Object],
|
|
q workqueue.TypedRateLimitingInterface[reconcile.Request],
|
|
) {
|
|
if err := r.invalidateServiceAccount(ctx, e.Object); err != nil {
|
|
r.Log.Error(err, "unable to invalidate serviceaccount cache",
|
|
"namespace", e.Object.GetNamespace(),
|
|
"name", e.Object.GetName(),
|
|
)
|
|
}
|
|
},
|
|
},
|
|
builder.WithPredicates(predicate.Funcs{
|
|
DeleteFunc: func(e event.DeleteEvent) bool {
|
|
return true
|
|
},
|
|
CreateFunc: func(e event.CreateEvent) bool {
|
|
return false
|
|
},
|
|
UpdateFunc: func(e event.UpdateEvent) bool {
|
|
return false
|
|
},
|
|
GenericFunc: func(e event.GenericEvent) bool {
|
|
return false
|
|
},
|
|
},
|
|
),
|
|
).
|
|
WithOptions(ctrlConfig.Runtime.ToControllerOptions()).
|
|
Complete(r)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// register Start(ctx) as a manager runnable.
|
|
return mgr.Add(r)
|
|
}
|
|
|
|
func (r *CacheInvalidator) Reconcile(ctx context.Context, request reconcile.Request) (res reconcile.Result, err error) {
|
|
log := r.Log.WithValues("configuration", request.Name)
|
|
|
|
log.V(5).Info("invalidating and rebuilding caches")
|
|
|
|
cfg := configuration.NewCapsuleConfiguration(ctx, r.Client, r.reader, r.Rest, request.Name)
|
|
|
|
instance := &capsulev1beta2.CapsuleConfiguration{}
|
|
if err = r.Get(ctx, request.NamespacedName, instance); err != nil {
|
|
if apierrors.IsNotFound(err) {
|
|
log.V(5).Info("requested object not found, could have been deleted after reconcile request")
|
|
|
|
return reconcile.Result{}, nil
|
|
}
|
|
|
|
log.Error(err, "error reading the object")
|
|
|
|
return res, err
|
|
}
|
|
|
|
if err := r.rebuildCaches(ctx, log); err != nil {
|
|
return res, err
|
|
}
|
|
|
|
interval := cfg.CacheInvalidation()
|
|
|
|
return reconcile.Result{
|
|
Requeue: true,
|
|
RequeueAfter: interval.Duration,
|
|
}, err
|
|
}
|
|
|
|
// invalidateCaches invokes for all caches their invalidation functions.
|
|
func (r *CacheInvalidator) rebuildCaches(
|
|
ctx context.Context,
|
|
log logr.Logger,
|
|
) error {
|
|
var errs []error
|
|
|
|
if err := r.rebuildRegexCache(ctx, log); err != nil {
|
|
errs = append(errs, fmt.Errorf("rebuild Regex cache: %w", err))
|
|
}
|
|
|
|
if err := r.rebuildJSONPathCache(ctx, log); err != nil {
|
|
errs = append(errs, fmt.Errorf("rebuild JSONPath cache: %w", err))
|
|
}
|
|
|
|
if err := r.rebuildTargetsCache(ctx, log); err != nil {
|
|
errs = append(errs, fmt.Errorf("rebuild targets cache: %w", err))
|
|
}
|
|
|
|
if err := r.rebuildRuleStatusRegistryCache(ctx, log); err != nil {
|
|
errs = append(errs, fmt.Errorf("rebuild registry cache: %w", err))
|
|
}
|
|
|
|
if err := r.rebuildImpersonationCache(ctx, log); err != nil {
|
|
errs = append(errs, fmt.Errorf("rebuild impersonation cache: %w", err))
|
|
}
|
|
|
|
if len(errs) > 0 {
|
|
return errors.Join(errs...)
|
|
}
|
|
|
|
return nil
|
|
}
|