package workloads import ( "context" "errors" "fmt" "reflect" v1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" "edge-infra.dev/pkg/k8s/runtime/objectrestarter" ) // getOwnerReference determines the workload object that owns the input object. // If a valid workload owner cant be found, an empty metav1.OwnerReference is returned. func getOwnerReference(m metav1.ObjectMeta) metav1.OwnerReference { for i, ref := range m.OwnerReferences { // either the kind must be restartable, a descendant of a kind that is // restartable (eg, ReplicaSet are owned by Deployments, which are restartable) if !objectrestarter.IsKindRestartable(ref.Kind) && ref.Kind != reflect.TypeOf(v1.ReplicaSet{}).Name() { continue } return m.OwnerReferences[i] } return metav1.OwnerReference{} } // getReplicaSetOwnerReference determines the workload object that owns the input replicaSet. // For pods owned by a ReplicaSet we need to go one level up for the real owning type. // Technically ReplicaSets could be the highest level owning type, but it would // be an antipattern since the K8s API docs say they should be managed by // higher level workload types: https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/#when-to-use-a-replicaset func getReplicaSetOwnerReference(ctx context.Context, c client.Client, pod corev1.Pod, ownerRef metav1.OwnerReference) (metav1.OwnerReference, error) { replicaSet := &v1.ReplicaSet{} if err := c.Get(ctx, types.NamespacedName{Name: ownerRef.Name, Namespace: pod.Namespace}, replicaSet); err != nil { return metav1.OwnerReference{}, fmt.Errorf("failed to get owning replicaset: %w", err) } replicaSetOwnerRef := getOwnerReference(replicaSet.ObjectMeta) if replicaSetOwnerRef == (metav1.OwnerReference{}) { return metav1.OwnerReference{}, errors.New("failed to get owner ref for replicaset") } return replicaSetOwnerRef, nil } // getWorkloadFromOwnerReference returns the workload from the given ownerReference as an unstructured object. func getWorkloadFromOwnerReference(ctx context.Context, c client.Client, pod corev1.Pod, ownerRef metav1.OwnerReference) (*unstructured.Unstructured, error) { gv, err := schema.ParseGroupVersion(ownerRef.APIVersion) if err != nil { return nil, fmt.Errorf("failed to parse apiVersion for owner ref: %w", err) } workload := &unstructured.Unstructured{} workload.SetGroupVersionKind(gv.WithKind(ownerRef.Kind)) if err := c.Get(ctx, types.NamespacedName{Name: ownerRef.Name, Namespace: pod.Namespace}, workload); err != nil { return nil, fmt.Errorf("failed to get owning workload %s/%s/%s: %w", ownerRef.Kind, pod.Namespace, ownerRef.Name, err) } return workload, nil }