mirror of
https://github.com/projectcapsule/capsule.git
synced 2026-02-14 18:09:58 +00:00
feat(controller): administration persona (#1739)
* chore(refactor): project and api refactoring Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore(refactor): project and api refactoring Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> --------- Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
This commit is contained in:
130
internal/controllers/pod/metadata.go
Normal file
130
internal/controllers/pod/metadata.go
Normal file
@@ -0,0 +1,130 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package pod
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierr "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"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/log"
|
||||
"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/pkg/utils"
|
||||
)
|
||||
|
||||
type MetadataReconciler struct {
|
||||
Client client.Client
|
||||
}
|
||||
|
||||
func (m *MetadataReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error {
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&corev1.Pod{}, m.forOptionPerInstanceName(ctx)).
|
||||
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 := &NonTenantObjectError{}
|
||||
|
||||
noPodMetaError := &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) 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, 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, 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.NewPredicateFuncs(func(object client.Object) bool {
|
||||
return m.isNamespaceInTenant(ctx, object.GetNamespace())
|
||||
}))
|
||||
}
|
||||
|
||||
func (m *MetadataReconciler) isNamespaceInTenant(ctx context.Context, namespace string) bool {
|
||||
tl := &capsulev1beta2.TenantList{}
|
||||
if err := m.Client.List(ctx, tl, client.MatchingFieldsSelector{
|
||||
Selector: fields.OneTermEqualSelector(".status.namespaces", namespace),
|
||||
}); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return len(tl.Items) > 0
|
||||
}
|
||||
Reference in New Issue
Block a user