Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 33 additions & 9 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
_ "k8s.io/client-go/plugin/pkg/client/auth"
"sigs.k8s.io/controller-runtime/pkg/metrics/filters"

appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
Expand All @@ -27,6 +28,7 @@ import (
bsv1 "github.com/redhat-developer/rhdh-operator/api/v1alpha5"

"github.com/redhat-developer/rhdh-operator/internal/controller"
"github.com/redhat-developer/rhdh-operator/pkg/utils"

openshift "github.com/openshift/api/route/v1"
//+kubebuilder:scaffold:imports
Expand Down Expand Up @@ -137,10 +139,34 @@ func main() {
// With many resources, managedFields can consume 70%+ of the informer cache heap.
mgrOpts.Cache.DefaultTransform = cache.TransformStripManagedFields()

// Configure cache to only watch labeled Secrets and ConfigMaps if flag is enabled
// Restrict Deployment/StatefulSet cache to operator-managed objects only.
// The operator only needs Deployments/StatefulSets it creates, and those always
// carry app.kubernetes.io/name=backstage. Without this filter, the metadata
// informers cache every Deployment and StatefulSet cluster-wide, which on
// large multi-tenant clusters consumes hundreds of MB of heap.
backstageLabelSelector, err := metav1.LabelSelectorAsSelector(&metav1.LabelSelector{
MatchLabels: map[string]string{
utils.BackstageAppLabel: utils.BackstageAppName,
},
})
if err != nil {
setupLog.Error(err, "failed to create backstage label selector")
os.Exit(1)
}

mgrOpts.Cache.ByObject = map[client.Object]cache.ByObject{
&appsv1.Deployment{}: {
Label: backstageLabelSelector,
},
&appsv1.StatefulSet{}: {
Label: backstageLabelSelector,
},
}

// Optionally restrict Secret/ConfigMap cache to labeled objects.
if enableCacheLabelFilter {
setupLog.Info("Enabling cache label filter for Secrets and ConfigMaps")
labelSelector, err := metav1.LabelSelectorAsSelector(&metav1.LabelSelector{
extConfigLabelSelector, err := metav1.LabelSelectorAsSelector(&metav1.LabelSelector{
MatchLabels: map[string]string{
"rhdh.redhat.com/external-config": "true",
},
Expand All @@ -150,13 +176,11 @@ func main() {
os.Exit(1)
}

mgrOpts.Cache.ByObject = map[client.Object]cache.ByObject{
&corev1.Secret{}: {
Label: labelSelector,
},
&corev1.ConfigMap{}: {
Label: labelSelector,
},
mgrOpts.Cache.ByObject[&corev1.Secret{}] = cache.ByObject{
Label: extConfigLabelSelector,
}
mgrOpts.Cache.ByObject[&corev1.ConfigMap{}] = cache.ByObject{
Label: extConfigLabelSelector,
}
}

Expand Down
81 changes: 24 additions & 57 deletions internal/controller/watchers.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/redhat-developer/rhdh-operator/api"
"github.com/redhat-developer/rhdh-operator/pkg/model"
"github.com/redhat-developer/rhdh-operator/pkg/utils"
appsv1 "k8s.io/api/apps/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
Expand Down Expand Up @@ -74,53 +75,32 @@ func (r *BackstageReconciler) addWatchers(b *builder.Builder) error {
}))
}

// Watch Deployment for the Backstage CR status if enabled
labelPred, err := predicate.LabelSelectorPredicate(metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: utils.BackstageAppLabel,
Values: []string{utils.BackstageAppName},
Operator: metav1.LabelSelectorOpIn,
},
// Watch operator-owned Deployments and StatefulSets for status tracking.
// Owns() maps events back to the owning Backstage CR via ownerReferences.
logEvent := func(eventType string, obj client.Object) {
log.Log.V(1).Info("enqueuing reconcile on change of",
"event", eventType,
"kind", obj.GetObjectKind().GroupVersionKind().Kind,
"name", obj.GetName(),
"namespace", obj.GetNamespace())
}
commonPreds := builder.WithPredicates(predicate.Funcs{
CreateFunc: func(e event.CreateEvent) bool {
logEvent("create", e.Object)
return true
},
UpdateFunc: func(e event.UpdateEvent) bool {
logEvent("update", e.ObjectNew)
return true
},
DeleteFunc: func(e event.DeleteEvent) bool {
logEvent("delete", e.Object)
return true
},
})
if err != nil {
return fmt.Errorf("failed to construct the predicate for backstage deployment. This should not happen: %w", err)
}

commonPreds := builder.WithPredicates(labelPred, predicate.Funcs{
DeleteFunc: func(e event.DeleteEvent) bool { return true },
UpdateFunc: func(e event.UpdateEvent) bool { return true },
CreateFunc: func(e event.CreateEvent) bool { return true },
})

metaFor := func(kind string) *metav1.PartialObjectMetadata {
m := &metav1.PartialObjectMetadata{}
m.SetGroupVersionKind(schema.GroupVersionKind{
Group: "apps",
Version: "v1",
Kind: kind,
})
return m
}

// Deployment
b.WatchesMetadata(
metaFor("Deployment"),
handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, o client.Object) []reconcile.Request {
return r.requestByAppLabels(ctx, o)
}),
commonPreds,
)

// StatefulSet
b.WatchesMetadata(
metaFor("StatefulSet"),
handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, o client.Object) []reconcile.Request {
return r.requestByAppLabels(ctx, o)
}),
commonPreds,
)
b.Owns(&appsv1.Deployment{}, commonPreds).
Owns(&appsv1.StatefulSet{}, commonPreds)

return nil
}
Expand Down Expand Up @@ -183,16 +163,3 @@ func (r *BackstageReconciler) requestByExtConfigLabel(ctx context.Context, objec
return []reconcile.Request{{NamespacedName: types.NamespacedName{Name: backstage.Name, Namespace: object.GetNamespace()}}}

}

// requestByAppLabels returns a request with current Namespace and Backstage Object name taken from label
func (r *BackstageReconciler) requestByAppLabels(ctx context.Context, object client.Object) []reconcile.Request {
lg := log.FromContext(ctx)

backstageName := object.GetLabels()[utils.BackstageInstanceLabel]
if object.GetLabels()[utils.BackstageAppLabel] == "" || backstageName == "" {
return []reconcile.Request{}
}

lg.V(1).Info("enqueuing reconcile on change of ", "kind: ", object.GetObjectKind().GroupVersionKind().Kind, "name: ", object.GetName(), "namespace: ", object.GetNamespace())
return []reconcile.Request{{NamespacedName: types.NamespacedName{Name: backstageName, Namespace: object.GetNamespace()}}}
}
Loading