Skip to content

Commit

Permalink
feat: additional metadata for pods
Browse files Browse the repository at this point in the history
Signed-off-by: Dario Tranchitella <[email protected]>
Co-authored-by: Giuseppe Chiesa <[email protected]>
  • Loading branch information
prometherion and gchiesa committed Nov 24, 2023
1 parent 14eb8f2 commit d944304
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 0 deletions.
30 changes: 30 additions & 0 deletions controllers/pod/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0

package pod

import "fmt"

type NonTenantObjectError struct {
objectName string
}

func NewNonTenantObject(objectName string) error {
return &NonTenantObjectError{objectName: objectName}
}

func (n NonTenantObjectError) Error() string {
return fmt.Sprintf("Skipping labels sync for %s as it doesn't belong to tenant", n.objectName)
}

type NoPodMetadataError struct {
objectName string
}

func NewNoPodMetadata(objectName string) error {
return &NoPodMetadataError{objectName: objectName}
}

func (n NoPodMetadataError) Error() string {
return fmt.Sprintf("Skipping labels sync for %s because no AdditionalLabels or AdditionalAnnotations presents in Tenant spec", n.objectName)
}
130 changes: 130 additions & 0 deletions controllers/pod/metadata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// Copyright 2020-2023 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) 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
}

func (m *MetadataReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&corev1.Pod{}, m.forOptionPerInstanceName(ctx)).
Complete(m)
}
6 changes: 6 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
capsulev1beta1 "github.com/projectcapsule/capsule/api/v1beta1"
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
configcontroller "github.com/projectcapsule/capsule/controllers/config"
podlabelscontroller "github.com/projectcapsule/capsule/controllers/pod"
"github.com/projectcapsule/capsule/controllers/pv"
rbaccontroller "github.com/projectcapsule/capsule/controllers/rbac"
"github.com/projectcapsule/capsule/controllers/resources"
Expand Down Expand Up @@ -291,6 +292,11 @@ func main() {
setupLog.Error(err, "unable to create controller", "controller", "EndpointSliceLabels")
}

if err = (&podlabelscontroller.MetadataReconciler{Client: manager.GetClient()}).SetupWithManager(ctx, manager); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "PodLabels")
os.Exit(1)
}

if err = (&pv.Controller{}).SetupWithManager(manager); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "PersistentVolume")
os.Exit(1)
Expand Down

0 comments on commit d944304

Please sign in to comment.