Skip to content
Open
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
67 changes: 67 additions & 0 deletions internal/pkg/manager/workloadannotator/workloadannotator.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,13 @@ import (
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
errors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/record"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/scheme"

profilerecordingapi "sigs.k8s.io/security-profiles-operator/api/profilerecording/v1alpha1"
seccompprofileapi "sigs.k8s.io/security-profiles-operator/api/seccompprofile/v1beta1"
selinuxprofileapi "sigs.k8s.io/security-profiles-operator/api/selinuxprofile/v1alpha2"
"sigs.k8s.io/security-profiles-operator/internal/pkg/controller"
Expand Down Expand Up @@ -98,6 +100,7 @@ func (r *PodReconciler) Reconcile(ctx context.Context, req reconcile.Request) (r
if errors.IsNotFound(err) { // this is a pod deletion, so update all seccomp/selinux profiles that were using it
seccompProfiles := &seccompprofileapi.SeccompProfileList{}
selinuxProfiles := &selinuxprofileapi.SelinuxProfileList{}
profileRecordings := &profilerecordingapi.ProfileRecordingList{}

if err = r.client.List(ctx, seccompProfiles, client.MatchingFields{linkedPodsKey: podID}); err != nil {
return reconcile.Result{}, fmt.Errorf("listing SeccompProfiles for deleted pod: %w", err)
Expand All @@ -107,6 +110,10 @@ func (r *PodReconciler) Reconcile(ctx context.Context, req reconcile.Request) (r
return reconcile.Result{}, fmt.Errorf("listing SelinuxProfiles for deleted pod: %w", err)
}

if err = r.client.List(ctx, profileRecordings, client.MatchingFields{linkedPodsKey: podID}); err != nil {
return reconcile.Result{}, fmt.Errorf("listing ProfileRecordings for deleted pod: %w", err)
}

for i := range seccompProfiles.Items {
if err = r.updatePodReferencesForSeccomp(ctx, &seccompProfiles.Items[i]); err != nil {
return reconcile.Result{}, fmt.Errorf("updating SeccompProfile for deleted pod: %w", err)
Expand All @@ -119,6 +126,12 @@ func (r *PodReconciler) Reconcile(ctx context.Context, req reconcile.Request) (r
}
}

for k := range profileRecordings.Items {
if err = r.updatePodReferencesForProfileRecording(ctx, &profileRecordings.Items[k]); err != nil {
return reconcile.Result{}, fmt.Errorf("updating ProfileRecording for deleted pod: %w", err)
}
}

return reconcile.Result{}, nil
}

Expand Down Expand Up @@ -271,6 +284,60 @@ func (r *PodReconciler) updatePodReferencesForSelinux(ctx context.Context, se *s
return nil
}

// updatePodReferencesForProfileRecording updates a ProfileRecording with the identifiers of pods using it and ensures
// it has a finalizer indicating it is in use to prevent it from being deleted.
func (r *PodReconciler) updatePodReferencesForProfileRecording(
ctx context.Context, pr *profilerecordingapi.ProfileRecording,
) error {
// we list the pods and not the workloads, because the workloads are just strings
// and we can't query for them directly
selector, err := metav1.LabelSelectorAsSelector(&pr.Spec.PodSelector)
if err != nil {
return fmt.Errorf("creating selector: %w", err)
}

linkedPods := &corev1.PodList{}
if err := r.client.List(ctx, linkedPods, client.InNamespace(pr.GetNamespace()), client.MatchingLabelsSelector{Selector: selector}); err != nil {
return fmt.Errorf("listing pods to update profileRecording: %w", err)
}

podList := make([]string, len(linkedPods.Items))
for i := range linkedPods.Items {
pod := linkedPods.Items[i]
podList[i] = pod.GetName()
}

if err := util.Retry(func() error {
pr.Status.ActiveWorkloads = podList
updateErr := r.client.Status().Update(ctx, pr)
if updateErr != nil {
if err := r.client.Get(ctx, util.NamespacedName(pr.GetName(), pr.GetNamespace()), pr); err != nil {
return fmt.Errorf("retrieving profile: %w", err)
}
return fmt.Errorf("updating profile: %w", updateErr)
}
return nil
}, util.IsNotFoundOrConflict); err != nil {
return fmt.Errorf("updating ProfileRecording status: %w", err)
}

if len(linkedPods.Items) > 0 {
if err := util.Retry(func() error {
return util.AddFinalizer(ctx, r.client, pr, util.HasActivePodsFinalizerString)
}, util.IsNotFoundOrConflict); err != nil {
return fmt.Errorf("adding finalizer: %w", err)
}
} else {
if err := util.Retry(func() error {
return util.RemoveFinalizer(ctx, r.client, pr, util.HasActivePodsFinalizerString)
}, util.IsNotFoundOrConflict); err != nil {
return fmt.Errorf("removing finalizer: %w", err)
}
}

return nil
}

// getSeccompProfilesFromPod returns a slice of strings representing seccomp profiles required by the pod.
// It looks first at the pod spec level, then in each container and init container, then in the annotations.
func getSeccompProfilesFromPod(pod *corev1.Pod) []string {
Expand Down