Files
capsule/internal/controllers/pod/metadata.go
T
Oliver BählerandGitHub 99e05220ab feat: implement performance optimizations (#2045)
* feat: implement performance optimizations

---------

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>
2026-07-21 16:27:29 +02:00

165 lines
4.9 KiB
Go

// Copyright 2020-2026 Project Capsule Authors
// SPDX-License-Identifier: Apache-2.0
package pod
import (
"context"
"fmt"
"github.com/go-logr/logr"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
apierr "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
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/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
controllerutils "github.com/projectcapsule/capsule/internal/controllers/utils"
caperrors "github.com/projectcapsule/capsule/pkg/api/errors"
"github.com/projectcapsule/capsule/pkg/runtime/predicates"
"github.com/projectcapsule/capsule/pkg/tenant"
"github.com/projectcapsule/capsule/pkg/utils"
)
type MetadataReconciler struct {
Client client.Client
Log logr.Logger
}
func (m *MetadataReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, ctrlConfig controllerutils.ControllerOptions) error {
return ctrl.NewControllerManagedBy(mgr).
Named("capsule/pod").
For(&corev1.Pod{}, m.forOptionPerInstanceName(ctx)).
Watches(
&capsulev1beta2.Tenant{},
handler.EnqueueRequestsFromMapFunc(m.podsForTenant),
builder.WithPredicates(predicates.TenantPodOptionsChangedPredicate{}),
).
WithOptions(ctrlConfig.Runtime.ToControllerOptions()).
Complete(m)
}
func (m *MetadataReconciler) Reconcile(ctx context.Context, request ctrl.Request) (ctrl.Result, error) {
var pod corev1.Pod
logger := log.FromContext(ctx)
tenant, err := m.getTenant(ctx, request.NamespacedName, m.Client)
if err != nil {
noTenantObjError := &caperrors.NonTenantObjectError{}
noPodMetaError := &caperrors.NoPodMetadataError{}
if errors.As(err, &noTenantObjError) || errors.As(err, &noPodMetaError) {
return reconcile.Result{}, nil
}
logger.Error(err, fmt.Sprintf("Cannot get tenant corev1.Pod %s/%s", request.Namespace, request.Name))
return reconcile.Result{}, err
}
err = m.Client.Get(ctx, request.NamespacedName, &pod)
if err != nil {
if apierr.IsNotFound(err) {
return reconcile.Result{}, nil
}
return reconcile.Result{}, err
}
_, err = controllerutil.CreateOrUpdate(ctx, m.Client, &pod, func() (err error) {
pod.SetLabels(m.sync(pod.GetLabels(), tenant.Spec.PodOptions.AdditionalMetadata.Labels))
pod.SetAnnotations(m.sync(pod.GetAnnotations(), tenant.Spec.PodOptions.AdditionalMetadata.Annotations))
return nil
})
return reconcile.Result{}, err
}
func (m *MetadataReconciler) podsForTenant(ctx context.Context, obj client.Object) []reconcile.Request {
tnt, ok := obj.(*capsulev1beta2.Tenant)
if !ok {
return nil
}
requests := make([]reconcile.Request, 0)
for _, namespace := range tnt.Status.Namespaces {
pods := &corev1.PodList{}
if err := m.Client.List(ctx, pods, client.InNamespace(namespace)); err != nil {
m.Log.Error(err, "failed listing Pods for Tenant metadata update", "tenant", tnt.Name, "namespace", namespace)
continue
}
for i := range pods.Items {
requests = append(requests, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(&pods.Items[i])})
}
}
return requests
}
func (m *MetadataReconciler) getTenant(ctx context.Context, namespacedName types.NamespacedName, client client.Client) (*capsulev1beta2.Tenant, error) {
ns := &corev1.Namespace{}
tenant := &capsulev1beta2.Tenant{}
if err := client.Get(ctx, types.NamespacedName{Name: namespacedName.Namespace}, ns); err != nil {
return nil, err
}
capsuleLabel, _ := utils.GetTypeLabel(&capsulev1beta2.Tenant{})
if _, ok := ns.GetLabels()[capsuleLabel]; !ok {
return nil, caperrors.NewNonTenantObject(namespacedName.Name)
}
if err := client.Get(ctx, types.NamespacedName{Name: ns.Labels[capsuleLabel]}, tenant); err != nil {
return nil, err
}
if tenant.Spec.PodOptions == nil || tenant.Spec.PodOptions.AdditionalMetadata == nil {
return nil, caperrors.NewNoPodMetadata(namespacedName.Name)
}
return tenant, nil
}
func (m *MetadataReconciler) sync(available map[string]string, tenantSpec map[string]string) map[string]string {
if tenantSpec != nil {
if available == nil {
return tenantSpec
}
for key, value := range tenantSpec {
if available[key] != value {
available[key] = value
}
}
}
return available
}
func (m *MetadataReconciler) forOptionPerInstanceName(ctx context.Context) builder.ForOption {
return builder.WithPredicates(predicate.And(
predicates.ObjectMetadataChangedPredicate{},
predicate.NewPredicateFuncs(func(object client.Object) bool {
status, err := tenant.IsNamespaceInTenant(ctx, m.Client, object.GetNamespace())
if err != nil {
m.Log.Error(err, "failed resolving instances")
}
return status
}),
))
}