Skip to content

Commit

Permalink
Deal with unsupported owners for replicasets (#847)
Browse files Browse the repository at this point in the history
  • Loading branch information
mariomac authored May 21, 2024
1 parent 8cc6694 commit a22db74
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 68 deletions.
11 changes: 6 additions & 5 deletions pkg/internal/discover/watcher_kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ func (wk *watcherKubeEnricher) enrichReplicaSetEvent(rsEvent Event[*kube.Replica
switch rsEvent.Type {
case EventCreated:
wk.log.Debug("ReplicaSet added", "namespace",
rsEvent.Obj.Namespace, "name", rsEvent.Obj.Name, "deployment", rsEvent.Obj.DeploymentName)
rsEvent.Obj.Namespace, "name", rsEvent.Obj.Name, "owner", rsEvent.Obj.Owner)
out <- wk.onNewReplicaSet(rsEvent.Obj)
case EventDeleted:
wk.log.Debug("ReplicaSet deleted", "namespace", rsEvent.Obj.Namespace, "name", rsEvent.Obj.Name)
Expand Down Expand Up @@ -238,9 +238,10 @@ func (wk *watcherKubeEnricher) onNewReplicaSet(rsInfo *kube.ReplicaSetInfo) []Ev
for _, pod := range podInfos {
for _, containerID := range pod.ContainerIDs {
if procInfo, ok := wk.processByContainer[containerID]; ok {
pod.Owner = &kube.Owner{Type: kube.OwnerReplicaSet, Name: rsInfo.Name}
if rsInfo.DeploymentName != "" {
pod.Owner.Owner = &kube.Owner{Type: kube.OwnerDeployment, Name: rsInfo.DeploymentName}
pod.Owner = &kube.Owner{
LabelName: kube.OwnerReplicaSet,
Name: rsInfo.Name,
Owner: rsInfo.Owner,
}
allProcesses = append(allProcesses, Event[processAttrs]{
Type: EventCreated,
Expand Down Expand Up @@ -310,7 +311,7 @@ func withMetadata(pp processAttrs, info *kube.PodInfo) processAttrs {
owner := info.Owner
for owner != nil {
ret.metadata[services.AttrOwnerName] = owner.Name
switch owner.Type {
switch owner.LabelName {
case kube.OwnerDaemonSet:
ret.metadata[services.AttrDaemonSetName] = owner.Name
case kube.OwnerReplicaSet:
Expand Down
19 changes: 10 additions & 9 deletions pkg/internal/export/attributes/names/attrs.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,16 @@ const (
RPCGRPCStatusCode = Name(semconv.RPCGRPCStatusCodeKey)
HTTPRoute = Name(semconv.HTTPRouteKey)

K8sNamespaceName = Name("k8s.namespace.name")
K8sPodName = Name("k8s.pod.name")
K8sDeploymentName = Name("k8s.deployment.name")
K8sReplicaSetName = Name("k8s.replicaset.name")
K8sDaemonSetName = Name("k8s.daemonset.name")
K8sStatefulSetName = Name("k8s.statefulset.name")
K8sNodeName = Name("k8s.node.name")
K8sPodUID = Name("k8s.pod.uid")
K8sPodStartTime = Name("k8s.pod.start_time")
K8sNamespaceName = Name("k8s.namespace.name")
K8sPodName = Name("k8s.pod.name")
K8sDeploymentName = Name("k8s.deployment.name")
K8sReplicaSetName = Name("k8s.replicaset.name")
K8sDaemonSetName = Name("k8s.daemonset.name")
K8sStatefulSetName = Name("k8s.statefulset.name")
K8sUnknownOwnerName = Name("k8s.owner.name")
K8sNodeName = Name("k8s.node.name")
K8sPodUID = Name("k8s.pod.uid")
K8sPodStartTime = Name("k8s.pod.start_time")
)

// Beyla-specific network attributes
Expand Down
21 changes: 7 additions & 14 deletions pkg/internal/kube/informer.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ type PodInfo struct {

type ReplicaSetInfo struct {
metav1.ObjectMeta
DeploymentName string
Owner *Owner
}

func qName(namespace, name string) string {
Expand Down Expand Up @@ -147,7 +147,7 @@ func (k *Metadata) initPodInformer(informerFactory informers.SharedInformerFacto
}
}

owner := OwnerFromPodInfo(pod)
owner := OwnerFrom(pod.OwnerReferences)
startTime := pod.GetCreationTimestamp().String()
if log.Enabled(context.TODO(), slog.LevelDebug) {
log.Debug("inserting pod", "name", pod.Name, "namespace", pod.Namespace,
Expand Down Expand Up @@ -233,24 +233,17 @@ func (k *Metadata) initReplicaSetInformer(informerFactory informers.SharedInform
}
return nil, fmt.Errorf("was expecting a ReplicaSet. Got: %T", i)
}
var deployment string
for i := range rs.OwnerReferences {
or := &rs.OwnerReferences[i]
if or.APIVersion == "apps/v1" && or.Kind == "Deployment" {
deployment = or.Name
break
}
}
owner := OwnerFrom(rs.OwnerReferences)
if log.Enabled(context.TODO(), slog.LevelDebug) {
log.Debug("inserting ReplicaSet", "name", rs.Name, "namespace", rs.Namespace,
"deployment", deployment)
"owner", owner)
}
return &ReplicaSetInfo{
ObjectMeta: metav1.ObjectMeta{
Name: rs.Name,
Namespace: rs.Namespace,
},
DeploymentName: deployment,
Owner: owner,
}, nil
}); err != nil {
return fmt.Errorf("can't set pods transform: %w", err)
Expand Down Expand Up @@ -328,9 +321,9 @@ func (k *Metadata) initInformers(ctx context.Context, client kubernetes.Interfac
// usually has a Deployment as owner reference, which is the one that we'd really like
// to report as owner.
func (k *Metadata) FetchPodOwnerInfo(pod *PodInfo) {
if pod.Owner != nil && pod.Owner.Type == OwnerReplicaSet {
if pod.Owner != nil && pod.Owner.LabelName == OwnerReplicaSet {
if rsi, ok := k.GetReplicaSetInfo(pod.Namespace, pod.Owner.Name); ok {
pod.Owner.Owner = &Owner{Type: OwnerDeployment, Name: rsi.DeploymentName}
pod.Owner.Owner = rsi.Owner
}
}
}
Expand Down
69 changes: 34 additions & 35 deletions pkg/internal/kube/owner.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,63 +3,62 @@ package kube
import (
"strings"

v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

attr "github.com/grafana/beyla/pkg/internal/export/attributes/names"
)

type OwnerType int
type OwnerLabel attr.Name

const (
OwnerUnknown = OwnerType(iota)
OwnerReplicaSet
OwnerDeployment
OwnerStatefulSet
OwnerDaemonSet
OwnerReplicaSet = OwnerLabel(attr.K8sReplicaSetName)
OwnerDeployment = OwnerLabel(attr.K8sDeploymentName)
OwnerStatefulSet = OwnerLabel(attr.K8sStatefulSetName)
OwnerDaemonSet = OwnerLabel(attr.K8sDaemonSetName)
OwnerUnknown = OwnerLabel(attr.K8sUnknownOwnerName)
)

func (o OwnerType) LabelName() attr.Name {
switch o {
case OwnerReplicaSet:
return attr.K8sReplicaSetName
case OwnerDeployment:
return attr.K8sDeploymentName
case OwnerStatefulSet:
return attr.K8sStatefulSetName
case OwnerDaemonSet:
return attr.K8sDaemonSetName
default:
return "k8s.unknown.owner"
}
}

type Owner struct {
Type OwnerType
Name string
LabelName OwnerLabel
Name string
// Owner of the owner. For example, a ReplicaSet might be owned by a Deployment
Owner *Owner
}

// OwnerFromPodInfo returns the pod Owner reference. It might be
// null if the Pod does not have any owner
func OwnerFromPodInfo(pod *v1.Pod) *Owner {
for i := range pod.OwnerReferences {
or := &pod.OwnerReferences[i]
// OwnerFrom returns the most plausible Owner reference. It might be
// null if the entity does not have any owner
func OwnerFrom(orefs []metav1.OwnerReference) *Owner {
// fallback will store any found owner that is not part of the bundled
// K8s owner types (e.g. argocd rollouts).
// It will be returned if any of the standard K8s owners are found
var fallback *Owner
for i := range orefs {
or := &orefs[i]
if or.APIVersion != "apps/v1" {
fallback = unrecognizedOwner(or)
continue
}
switch or.Kind {
case "ReplicaSet":
return &Owner{Type: OwnerReplicaSet, Name: or.Name}
return &Owner{LabelName: OwnerReplicaSet, Name: or.Name}
case "Deployment":
return &Owner{Type: OwnerDeployment, Name: or.Name}
return &Owner{LabelName: OwnerDeployment, Name: or.Name}
case "StatefulSet":
return &Owner{Type: OwnerStatefulSet, Name: or.Name}
return &Owner{LabelName: OwnerStatefulSet, Name: or.Name}
case "DaemonSet":
return &Owner{Type: OwnerDaemonSet, Name: or.Name}
return &Owner{LabelName: OwnerDaemonSet, Name: or.Name}
default:
fallback = unrecognizedOwner(or)
}
}
return nil
return fallback
}

func unrecognizedOwner(or *metav1.OwnerReference) *Owner {
return &Owner{
LabelName: OwnerLabel(attr.K8sUnknownOwnerName),
Name: or.Name,
}
}

func (o *Owner) String() string {
Expand All @@ -73,7 +72,7 @@ func (o *Owner) string(sb *strings.Builder) {
o.Owner.string(sb)
sb.WriteString("->")
}
sb.WriteString(string(o.Type.LabelName()))
sb.WriteString(string(o.LabelName))
sb.WriteByte(':')
sb.WriteString(o.Name)
}
47 changes: 45 additions & 2 deletions pkg/internal/kube/owner_test.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,57 @@
package kube

import (
"fmt"
"strings"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestOwnerString(t *testing.T) {
owner := Owner{Type: OwnerReplicaSet, Name: "rs"}
owner := Owner{LabelName: OwnerReplicaSet, Name: "rs"}
assert.Equal(t, "k8s.replicaset.name:rs", owner.String())
owner.Owner = &Owner{Type: OwnerDeployment, Name: "dep"}
owner.Owner = &Owner{LabelName: OwnerDeployment, Name: "dep"}
assert.Equal(t, "k8s.deployment.name:dep->k8s.replicaset.name:rs", owner.String())
}

func TestOwnerFrom(t *testing.T) {
for _, kind := range []string{"ReplicaSet", "Deployment", "StatefulSet", "DaemonSet"} {
t.Run(kind, func(t *testing.T) {
owner := OwnerFrom([]v1.OwnerReference{
{APIVersion: "foo/bar", Kind: kind, Name: "no"},
{APIVersion: "apps/v1", Kind: "Unknown", Name: "no"},
{APIVersion: "apps/v1", Kind: kind, Name: "theowner"},
})
require.NotNil(t, owner)
assert.Equal(t, &Owner{
LabelName: OwnerLabel(fmt.Sprintf("k8s.%s.name", strings.ToLower(kind))),
Name: "theowner",
}, owner)
})
}
}

func TestOwnerFrom_Unrecognized(t *testing.T) {
owner := OwnerFrom([]v1.OwnerReference{
{APIVersion: "foo/v1", Kind: "Unknown", Name: "theowner"},
})
require.NotNil(t, owner)
assert.Equal(t, &Owner{
LabelName: OwnerUnknown,
Name: "theowner",
}, owner)
}

func TestOwnerFrom_Unrecognized_AppsV1(t *testing.T) {
owner := OwnerFrom([]v1.OwnerReference{
{APIVersion: "apps/v1", Kind: "Unknown", Name: "theowner"},
})
require.NotNil(t, owner)
assert.Equal(t, &Owner{
LabelName: OwnerUnknown,
Name: "theowner",
}, owner)
}
2 changes: 1 addition & 1 deletion pkg/transform/k8s.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ func appendMetadata(span *request.Span, info *kube.PodInfo) {
}
owner := info.Owner
for owner != nil {
span.ServiceID.Metadata[owner.Type.LabelName()] = owner.Name
span.ServiceID.Metadata[attr.Name(owner.LabelName)] = owner.Name
owner = owner.Owner
}
}
4 changes: 2 additions & 2 deletions pkg/transform/k8s_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ func TestDecoration(t *testing.T) {
},
NodeName: "the-node",
StartTimeStr: "2020-01-02 12:12:56",
Owner: &kube.Owner{Type: kube.OwnerDeployment, Name: "deployment-12"},
Owner: &kube.Owner{LabelName: kube.OwnerDeployment, Name: "deployment-12"},
},
34: &kube.PodInfo{
ObjectMeta: v1.ObjectMeta{
Name: "pod-34", Namespace: "the-ns", UID: "uid-34",
},
NodeName: "the-node",
StartTimeStr: "2020-01-02 12:34:56",
Owner: &kube.Owner{Type: kube.OwnerReplicaSet, Name: "rs-34"},
Owner: &kube.Owner{LabelName: kube.OwnerReplicaSet, Name: "rs-34"},
},
56: &kube.PodInfo{
ObjectMeta: v1.ObjectMeta{
Expand Down

0 comments on commit a22db74

Please sign in to comment.